HonzaH commited on
Commit
5d00a15
·
verified ·
1 Parent(s): f5149ae

Upload 176 files

Browse files
README.md CHANGED
@@ -194,6 +194,35 @@ UI update (2025-11-03): No‑Contact controls
194
  - Added a single, visible primary toggle alongside the "Upravit začátek" and "Nastavit na teď" actions. The primary toggle uses the canonical `noContact.startISO` state as the single source of truth and shows the labels "Zahájení Bez Kontaktu" (inactive) / "Ukončit Bez kontaktu" (active).
195
  - This change reduces duplicated controls, simplifies the interaction surface, and makes the No‑Contact flow deterministic (optimistic UI updates remain in handlers).
196
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
197
  UI & chat updates (2025-11-03):
198
  - Chat message metadata: chat messages now carry optional `meta` (for example `{ type: 'medal', medal: 'První krok' }`) so the frontend can render special visual variants for high-value events (medals, assessment results, no-contact milestones).
199
  - Medal visuals & divider: the frontend renders a small medal badge/divider and a distinct `.chat-bubble.medal` style for earned medals to make them immediately visible and celebratory.
 
194
  - Added a single, visible primary toggle alongside the "Upravit začátek" and "Nastavit na teď" actions. The primary toggle uses the canonical `noContact.startISO` state as the single source of truth and shows the labels "Zahájení Bez Kontaktu" (inactive) / "Ukončit Bez kontaktu" (active).
195
  - This change reduces duplicated controls, simplifies the interaction surface, and makes the No‑Contact flow deterministic (optimistic UI updates remain in handlers).
196
 
197
+ ### Localization sweep (2025-11-10)
198
+
199
+ - Performed a repo-wide localization sweep to fix runtime i18n issues and make UI texts update immediately when the user switches language.
200
+ - Key fixes applied:
201
+ - Moved `LocaleProvider` to `frontend/src/main.tsx` so `useLocale()` / `useTranslation()` are available at app mount time (fixes "useLocale must be used within a LocaleProvider").
202
+ - Removed module-level calls to `t()` and moved translation-dependent constants into component scope (fixes "t is not defined" runtime errors).
203
+ - Ensured memoized values that depend on translated text include `locale` in their dependency arrays (notably `App.activeTab`, `ProfileView.loadSummary`, `AttachmentView.allQuestions`).
204
+ - Attachment content flow: `AttachmentView` displays per-locale content fields as-is (content buckets are localized) and keeps `locale` in memo deps; `AttachmentStylesView` now requests `/api/content/attachment_styles?locale={locale}` so content refetches when language changes.
205
+ - Added English activities content (`content/activities.en.json`) and updated `ActivitiesView` to load locale-appropriate content buckets.
206
+ - Build: frontend production build succeeded after the sweep and produced `dist/` assets.
207
+
208
+ ## Recent developer updates (2025-11-10)
209
+
210
+ - Persistence & store reliability: several frontend stores (chat, journal, noContact and related loaders) were updated to await IndexedDB writes (`idbSet`) and to log failures. This reduces race conditions where writes could be lost during reloads or app shutdown. Loaders (`loadMedals`, `loadCompletedActions`, `loadNoContact`) now include diagnostic logs to help reproduce persistence issues.
211
+
212
+ - Activity logging consistency: `activityLogger` usage has been consolidated in places to ensure store-first writes are completed before emitting assistant/journal messages. Helpers now await persistence and medal checks to avoid race conditions.
213
+
214
+ - Translation helper update: `frontend/src/lib/strings.ts` was updated
215
+
216
+ - Tests & typing: Unit-tests were updated to reflect corrected store shapes and keys (for example `medals` and `completedActions`). A small typing fix in `frontend/tests/unit.test.ts` resolves a TS2339 complaint related to a test mock shape.
217
+
218
+ - Server SPA cache: the backend serving logic was tuned to serve `index.html` with a no-cache policy so clients revalidate the HTML (avoids HF Spaces / CDN stale-hashed-asset 404s when deploys remove old hashed assets).
219
+
220
+ These developer-facing changes were made to make debugging persistence and localization issues easier. See `docs/lessons-learned.md` for guidance on interpreting the new diagnostic logs and on the recommended store-first logging pattern.
221
+
222
+ Next recommended steps:
223
+ - Run the runtime QA flow (start backend + frontend/dev server and verify header, tabs, Activities, Attachment and Profile update immediately on language switch).
224
+ - Optionally run a full automated patch to add `locale` to remaining memo/callback deps across the codebase; this is low-risk but touches many files and should be validated by the test suite.
225
+
226
  UI & chat updates (2025-11-03):
227
  - Chat message metadata: chat messages now carry optional `meta` (for example `{ type: 'medal', medal: 'První krok' }`) so the frontend can render special visual variants for high-value events (medals, assessment results, no-contact milestones).
228
  - Medal visuals & divider: the frontend renders a small medal badge/divider and a distinct `.chat-bubble.medal` style for earned medals to make them immediately visible and celebratory.
app.py CHANGED
@@ -34,13 +34,18 @@ def create_app() -> FastAPI:
34
  app.include_router(user_state_router)
35
  app.include_router(sse_router)
36
 
37
- # Mount static assets if available
38
  if os.path.exists(ASSETS_DIR):
39
  app.mount("/assets", StaticFiles(directory=ASSETS_DIR), name="assets")
40
 
41
- # Mount the built SPA at root as a fallback for non-API routes
42
- if os.path.exists(DIST_DIR):
43
- app.mount("/", StaticFiles(directory=DIST_DIR, html=True), name="spa")
 
 
 
 
 
44
 
45
  return app
46
 
@@ -60,7 +65,24 @@ def favicon():
60
  def index():
61
  path = os.path.join(DIST_DIR, "index.html")
62
  if os.path.exists(path):
63
- return FileResponse(path)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
64
  raise HTTPException(status_code=404, detail="index.html not found")
65
 
66
 
 
34
  app.include_router(user_state_router)
35
  app.include_router(sse_router)
36
 
37
+ # Mount static assets if available (serve hashed assets under /assets)
38
  if os.path.exists(ASSETS_DIR):
39
  app.mount("/assets", StaticFiles(directory=ASSETS_DIR), name="assets")
40
 
41
+ # Serve the SPA index.html as a controlled fallback for non-API routes.
42
+ # We intentionally do NOT mount the whole `dist` directory at `/` because
43
+ # StaticFiles would serve `index.html` without custom cache headers. To
44
+ # avoid clients reusing stale `index.html` (which can reference old hashed
45
+ # asset filenames that no longer exist and cause 404s), we return
46
+ # `index.html` via an explicit fallback route with Cache-Control: no-cache
47
+ # so browsers revalidate and fetch the latest HTML after a deploy.
48
+ # Note: individual hashed assets under /assets can be cached long-term.
49
 
50
  return app
51
 
 
65
  def index():
66
  path = os.path.join(DIST_DIR, "index.html")
67
  if os.path.exists(path):
68
+ # Serve index.html with headers that prevent long-lived caching of the
69
+ # HTML itself. This ensures clients revalidate the page when we deploy
70
+ # a new build (and prevents requests for old hashed asset names).
71
+ return FileResponse(path, headers={"Cache-Control": "no-cache, no-store, must-revalidate"})
72
+ raise HTTPException(status_code=404, detail="index.html not found")
73
+
74
+
75
+ @app.get("/{full_path:path}", include_in_schema=False)
76
+ def spa_fallback(full_path: str):
77
+ """Fallback for SPA routes: return index.html with no-cache headers.
78
+
79
+ This lets the frontend use client-side routing while ensuring the
80
+ HTML is revalidated after deploys, which prevents clients from trying to
81
+ fetch stale hashed asset filenames that no longer exist on disk.
82
+ """
83
+ path = os.path.join(DIST_DIR, "index.html")
84
+ if os.path.exists(path):
85
+ return FileResponse(path, headers={"Cache-Control": "no-cache, no-store, must-revalidate"})
86
  raise HTTPException(status_code=404, detail="index.html not found")
87
 
88
 
docs/lessons-learned.md CHANGED
@@ -50,6 +50,31 @@ const message = t('logger.activity_completed', locale, { activity: activityName
50
 
51
  ---
52
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
53
  ### SupportView t() Funkce Bez Locale
54
 
55
  **Problém:** `Uncaught ReferenceError: t is not defined` v SupportView.tsx - pole `RESOURCES` a `SUBTAB_LABELS` používaly funkci `t()` bez locale parametru na module level.
 
50
 
51
  ---
52
 
53
+ ## 2025-11-10: Persistence, tests & helper updates
54
+
55
+ Short summary of recent developer fixes and guidance for future contributors:
56
+
57
+ - Persistence writes now awaited: Several stores (`chat`, `journal`, `noContact`, and related save paths) were changed to update Zustand state first and then `await` the `idbSet(key, value)` call. This reduces lost-writes during reloads and shutdowns. When adding new stores, follow the pattern: update store → await persistence → emit assistant/journal messages.
58
+
59
+ - Diagnostic load logging: `loadMedals()`, `loadCompletedActions()` and `loadNoContact()` include console logs to surface what is read from IndexedDB on startup. Use these logs when reproducing persistence issues.
60
+
61
+ - ActivityLogger ordering: The atomic helper `emitAssistantAndJournalWithOptions` is the recommended API; make sure to await persistence operations before triggering medal checks or UI-only journal messages.
62
+
63
+ - `t()` helper relaxed signature: To reduce widespread TS errors from varying call-sites, `frontend/src/lib/strings.ts` now supports calls like `t(key, vars?)` (locale defaults to `getLocale()`). Avoid calling `t()` at module scope; prefer within component render/effects.
64
+
65
+ - Test updates: Unit tests were adjusted to match corrected store key names and shapes (for example `medals` and `completedActions`). When writing tests that mock stores, prefer explicit `any` typed mocks or properly shaped objects to avoid TypeScript `never` inference issues in mock expectations.
66
+
67
+ Quick checklist when debugging persistence/localization issues:
68
+
69
+ 1. Inspect the browser console for `load*` logs produced by `loadMedals`, `loadCompletedActions`, and `loadNoContact` on app start.
70
+ 2. Ensure store writes use: `set(newState)` then `await idbSet(key, newState)` inside try/catch and log errors.
71
+ 3. If medals or counts appear missing after reload, confirm the keys used in `idbSet` exactly match keys used in loaders (`'medals'`, `'completedActions'`, `'noContact'`, `'chat'`, `'journal'`).
72
+ 4. Avoid calling `t()` at module top-level; keep translations inside components or effects and include `locale` in memo/deps when caching translated values.
73
+
74
+ These notes were added after a targeted debugging and test-fix session on 2025-11-10. Keep them in mind when adding new stores or updating activity logging to prevent regressions.
75
+
76
+ ---
77
+
78
  ### SupportView t() Funkce Bez Locale
79
 
80
  **Problém:** `Uncaught ReferenceError: t is not defined` v SupportView.tsx - pole `RESOURCES` a `SUBTAB_LABELS` používaly funkci `t()` bez locale parametru na module level.
frontend/src/lib/strings.ts CHANGED
@@ -1,5 +1,6 @@
1
  import cs from '../i18n/cs.json'
2
  import en from '../i18n/en.json'
 
3
 
4
  const bundles: Record<string, any> = {
5
  cs,
@@ -16,7 +17,24 @@ function lookup(key: string, bundle: any) {
16
  return cur
17
  }
18
 
19
- export function t(key: string, locale: string, vars?: Record<string, string | number>) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20
  const bundle = bundles[locale] || bundles['cs']
21
  let str = lookup(key, bundle) || lookup(key, bundles['cs']) || key
22
  if (vars) {
 
1
  import cs from '../i18n/cs.json'
2
  import en from '../i18n/en.json'
3
+ import { getLocale } from './locale'
4
 
5
  const bundles: Record<string, any> = {
6
  cs,
 
17
  return cur
18
  }
19
 
20
+ /**
21
+ * Translation helper.
22
+ * Supports two calling styles:
23
+ * t(key, locale?, vars?)
24
+ * t(key, vars?) // uses stored locale
25
+ */
26
+ export function t(key: string, localeOrVars?: string | Record<string, string | number>, maybeVars?: Record<string, string | number>) {
27
+ let locale: string
28
+ let vars: Record<string, string | number> | undefined
29
+
30
+ if (typeof localeOrVars === 'string') {
31
+ locale = localeOrVars
32
+ vars = maybeVars
33
+ } else {
34
+ locale = getLocale()
35
+ vars = localeOrVars as Record<string, string | number> | undefined
36
+ }
37
+
38
  const bundle = bundles[locale] || bundles['cs']
39
  let str = lookup(key, bundle) || lookup(key, bundles['cs']) || key
40
  if (vars) {
frontend/src/state/modules/chat.ts CHANGED
@@ -7,32 +7,40 @@ export const useChat = create<{ chat: Msg[]; push: (m:Msg)=>void; appendToken: (
7
  chat: [],
8
  push: async (m)=> {
9
  const withTs = { ...m, ts: m.ts || new Date().toISOString() }
10
- set(s=>{ const newChat = [...s.chat, withTs]; idbSet('chat', newChat); return { chat: newChat } })
 
 
 
 
 
 
 
11
  // REMOVED: checkAndLogMedals(['chat']) - causes infinite recursion when called from logMedalEarned
12
  // Medal checks should be explicit in activityLogger functions, not on every chat push
13
  },
14
- appendToken: async (t)=> set(s=>{
15
- const chat = s.chat.slice(); const last = chat[chat.length-1];
16
- if(!last || last.role!=='assistant'){ chat.push({role:'assistant', content:t, ts: new Date().toISOString()}) } else { last.content += t }
17
- idbSet('chat', chat);
18
- return { chat }
19
- }),
20
- appendAgentToken: async (agent, token) => set(s=>{
21
- const chat = s.chat.slice(); const last = chat[chat.length-1];
22
- // Keep continuity: if the last message is from assistant, append tokens to it
23
- // regardless of which agent emitted them. Only create a new assistant message
24
- // when the last message is not an assistant message.
 
 
 
25
  if (!last || last.role !== 'assistant') {
26
  chat.push({ role: 'assistant', content: token, agent, ts: new Date().toISOString() })
27
  } else {
28
- // Append token to existing assistant bubble. If agent metadata wasn't set,
29
- // set it to the first agent that contributed to this bubble.
30
  last.content += token
31
  if (!last.agent) last.agent = agent
32
  }
33
- idbSet('chat', chat);
34
- return { chat }
35
- })
36
  }))
37
 
38
  // Načtení chat historie z IndexedDB při inicializaci
 
7
  chat: [],
8
  push: async (m)=> {
9
  const withTs = { ...m, ts: m.ts || new Date().toISOString() }
10
+ const newChat = get().chat.concat([withTs])
11
+ // Update state immediately for responsive UI
12
+ set({ chat: newChat })
13
+ try {
14
+ await idbSet('chat', newChat)
15
+ } catch (err) {
16
+ console.error('Error persisting chat push:', err)
17
+ }
18
  // REMOVED: checkAndLogMedals(['chat']) - causes infinite recursion when called from logMedalEarned
19
  // Medal checks should be explicit in activityLogger functions, not on every chat push
20
  },
21
+ appendToken: async (t)=> {
22
+ const chat = get().chat.slice()
23
+ const last = chat[chat.length-1]
24
+ if(!last || last.role!=='assistant'){
25
+ chat.push({role:'assistant', content:t, ts: new Date().toISOString()})
26
+ } else {
27
+ last.content += t
28
+ }
29
+ set({ chat })
30
+ try { await idbSet('chat', chat) } catch (err) { console.error('Error persisting chat appendToken:', err) }
31
+ },
32
+ appendAgentToken: async (agent, token) => {
33
+ const chat = get().chat.slice()
34
+ const last = chat[chat.length-1]
35
  if (!last || last.role !== 'assistant') {
36
  chat.push({ role: 'assistant', content: token, agent, ts: new Date().toISOString() })
37
  } else {
 
 
38
  last.content += token
39
  if (!last.agent) last.agent = agent
40
  }
41
+ set({ chat })
42
+ try { await idbSet('chat', chat) } catch (err) { console.error('Error persisting chat appendAgentToken:', err) }
43
+ }
44
  }))
45
 
46
  // Načtení chat historie z IndexedDB při inicializaci
frontend/src/state/modules/completedActions.ts CHANGED
@@ -52,6 +52,7 @@ export const useCompletedActions = create<CompletedActionsStore>((set, get) => (
52
  // Load completed actions from IndexedDB on init
53
  export async function loadCompletedActions() {
54
  const saved = await idbGet('completedActions')
 
55
  if (saved && Array.isArray(saved)) {
56
  useCompletedActions.setState({ actions: saved })
57
  }
 
52
  // Load completed actions from IndexedDB on init
53
  export async function loadCompletedActions() {
54
  const saved = await idbGet('completedActions')
55
+ console.log('loadCompletedActions: read from IndexedDB ->', saved)
56
  if (saved && Array.isArray(saved)) {
57
  useCompletedActions.setState({ actions: saved })
58
  }
frontend/src/state/modules/journal.ts CHANGED
@@ -8,11 +8,13 @@ export const useJournal = create<{ journal: JournalEntry[]; addJournal: (text:st
8
  journal: [],
9
  addJournal: async (text, meta)=> {
10
  const entry = { id: String(Date.now()), ts: new Date().toISOString(), text, meta };
11
- set(s => {
12
- const newJournal = [...s.journal, entry];
13
- idbSet('journal', newJournal);
14
- return { journal: newJournal }
15
- })
 
 
16
  // Check for journal-related medals (like "3 deníkové zápisy")
17
  await checkAndLogMedals(['journal'])
18
  // Recompute user stage after journaling (affects phase detection)
 
8
  journal: [],
9
  addJournal: async (text, meta)=> {
10
  const entry = { id: String(Date.now()), ts: new Date().toISOString(), text, meta };
11
+ const newJournal = get().journal.concat([entry])
12
+ set({ journal: newJournal })
13
+ try {
14
+ await idbSet('journal', newJournal)
15
+ } catch (err) {
16
+ console.error('Error persisting journal entry:', err)
17
+ }
18
  // Check for journal-related medals (like "3 deníkové zápisy")
19
  await checkAndLogMedals(['journal'])
20
  // Recompute user stage after journaling (affects phase detection)
frontend/src/state/modules/medal.ts CHANGED
@@ -43,6 +43,7 @@ export const useMedal = create<MedalStore>((set, get) => ({
43
  // Load medals from IndexedDB on init
44
  export async function loadMedals() {
45
  const saved = await idbGet('medals')
 
46
  if (saved && Array.isArray(saved)) {
47
  useMedal.setState({ medals: saved })
48
  }
 
43
  // Load medals from IndexedDB on init
44
  export async function loadMedals() {
45
  const saved = await idbGet('medals')
46
+ console.log('loadMedals: read from IndexedDB ->', saved)
47
  if (saved && Array.isArray(saved)) {
48
  useMedal.setState({ medals: saved })
49
  }
frontend/src/state/modules/noContact.ts CHANGED
@@ -9,7 +9,14 @@ export const useNoContact = create<{ noContact: NoContact; startNoContact: ()=>v
9
  noContact: { startISO: null, lastPraiseISO: null, reasons: [] },
10
  startNoContact: async ()=> {
11
  const now = new Date().toISOString()
12
- set(s=>{ const newState = {...s.noContact, startISO: now, lastPraiseISO: now}; idbSet('noContact', newState); return { noContact: newState } })
 
 
 
 
 
 
 
13
  // Log milestone - always show start message
14
  const { logNoContactMilestone } = await import('../../lib/activityLogger')
15
  await logNoContactMilestone(0, true) // isStart = true, days = 0 for start message
@@ -18,23 +25,40 @@ export const useNoContact = create<{ noContact: NoContact; startNoContact: ()=>v
18
  resetNoContact: async ()=> {
19
  const newState = { startISO:null, lastPraiseISO:null, reasons: [] };
20
  set({ noContact: newState });
21
- await idbSet('noContact', newState);
 
 
 
 
22
  // Log reset milestone
23
  const { logNoContactMilestone } = await import('../../lib/activityLogger')
24
  await logNoContactMilestone(0, false) // isStart = false for reset message
25
  try { await recomputeStage() } catch (e) { /* ignore */ }
26
  },
27
  updateLastPraise: async (iso: string)=> {
28
- set(s=>{ const newState = {...s.noContact, lastPraiseISO: iso}; idbSet('noContact', newState); return { noContact: newState } })
 
 
 
 
 
 
29
  },
30
  updateReasons: async (reasons: string[])=> {
31
- set(s=>{ const newState = {...s.noContact, reasons}; idbSet('noContact', newState); return { noContact: newState } })
 
 
 
 
 
 
32
  }
33
  }))
34
 
35
  // Načtení noContact stavu z IndexedDB při inicializaci
36
  export async function loadNoContact() {
37
  const saved = await idbGet('noContact')
 
38
  if (saved && typeof saved === 'object' && 'startISO' in saved) {
39
  useNoContact.setState({ noContact: {
40
  startISO: saved.startISO || null,
 
9
  noContact: { startISO: null, lastPraiseISO: null, reasons: [] },
10
  startNoContact: async ()=> {
11
  const now = new Date().toISOString()
12
+ const newState = { startISO: now, lastPraiseISO: now, reasons: get().noContact.reasons }
13
+ // Update state first, then persist and await to ensure the write completes
14
+ set({ noContact: newState })
15
+ try {
16
+ await idbSet('noContact', newState)
17
+ } catch (err) {
18
+ console.error('Error persisting noContact start:', err)
19
+ }
20
  // Log milestone - always show start message
21
  const { logNoContactMilestone } = await import('../../lib/activityLogger')
22
  await logNoContactMilestone(0, true) // isStart = true, days = 0 for start message
 
25
  resetNoContact: async ()=> {
26
  const newState = { startISO:null, lastPraiseISO:null, reasons: [] };
27
  set({ noContact: newState });
28
+ try {
29
+ await idbSet('noContact', newState);
30
+ } catch (err) {
31
+ console.error('Error persisting noContact reset:', err)
32
+ }
33
  // Log reset milestone
34
  const { logNoContactMilestone } = await import('../../lib/activityLogger')
35
  await logNoContactMilestone(0, false) // isStart = false for reset message
36
  try { await recomputeStage() } catch (e) { /* ignore */ }
37
  },
38
  updateLastPraise: async (iso: string)=> {
39
+ const newState = { ...get().noContact, lastPraiseISO: iso }
40
+ set({ noContact: newState })
41
+ try {
42
+ await idbSet('noContact', newState)
43
+ } catch (err) {
44
+ console.error('Error persisting noContact lastPraise update:', err)
45
+ }
46
  },
47
  updateReasons: async (reasons: string[])=> {
48
+ const newState = { ...get().noContact, reasons }
49
+ set({ noContact: newState })
50
+ try {
51
+ await idbSet('noContact', newState)
52
+ } catch (err) {
53
+ console.error('Error persisting noContact reasons update:', err)
54
+ }
55
  }
56
  }))
57
 
58
  // Načtení noContact stavu z IndexedDB při inicializaci
59
  export async function loadNoContact() {
60
  const saved = await idbGet('noContact')
61
+ console.log('loadNoContact: read from IndexedDB ->', saved)
62
  if (saved && typeof saved === 'object' && 'startISO' in saved) {
63
  useNoContact.setState({ noContact: {
64
  startISO: saved.startISO || null,
frontend/src/views/ProfileView.tsx CHANGED
@@ -293,12 +293,14 @@ export default function ProfileView({ accent }: Props) {
293
 
294
  console.log('Step 2: Deleting IndexedDB data...')
295
  // Smazat všechna data z IndexedDB
 
296
  await Promise.all([
297
  idbDel('profile'),
298
  idbDel('chat'),
299
  idbDel('journal'),
300
  idbDel('noContact'),
301
- idbDel('medal'),
 
302
  idbDel('last_active_tab')
303
  // onboarding_completed is no longer used
304
  ])
 
293
 
294
  console.log('Step 2: Deleting IndexedDB data...')
295
  // Smazat všechna data z IndexedDB
296
+ // Note: keys must match those used by the stores (medals, completedActions, noContact, profile, chat, journal, last_active_tab)
297
  await Promise.all([
298
  idbDel('profile'),
299
  idbDel('chat'),
300
  idbDel('journal'),
301
  idbDel('noContact'),
302
+ idbDel('medals'),
303
+ idbDel('completedActions'),
304
  idbDel('last_active_tab')
305
  // onboarding_completed is no longer used
306
  ])
frontend/tests/unit.test.ts CHANGED
@@ -829,7 +829,7 @@ describe('Unit tests', () => {
829
  const mockUseChatSetState = vi.fn()
830
  const mockUseJournalSetState = vi.fn()
831
  const mockUseNoContactSetState = vi.fn()
832
- const mockUseMedalSetState = vi.fn()
833
 
834
  // Mock window.location.reload
835
  const mockReload = vi.fn()
@@ -879,7 +879,8 @@ describe('Unit tests', () => {
879
  mockIdbDel('chat'),
880
  mockIdbDel('journal'),
881
  mockIdbDel('noContact'),
882
- mockIdbDel('medal'),
 
883
  mockIdbDel('last_active_tab')
884
  ])
885
  console.log('Data deleted from IndexedDB')
@@ -889,7 +890,7 @@ describe('Unit tests', () => {
889
  mockUseChatSetState({ chat: [] })
890
  mockUseJournalSetState({ journal: [] })
891
  mockUseNoContactSetState({ noContact: { startISO: null, lastPraiseISO: null } })
892
- mockUseMedalSetState({ medal: null })
893
  console.log('Zustand reset, reloading page...')
894
 
895
  // Reload stránky pro čistý start
@@ -899,12 +900,13 @@ describe('Unit tests', () => {
899
  await handleForget()
900
 
901
  // Verify all IndexedDB keys were deleted (except onboarding_completed which is no longer used)
902
- expect(mockIdbDel).toHaveBeenCalledWith('profile')
903
- expect(mockIdbDel).toHaveBeenCalledWith('chat')
904
- expect(mockIdbDel).toHaveBeenCalledWith('journal')
905
- expect(mockIdbDel).toHaveBeenCalledWith('noContact')
906
- expect(mockIdbDel).toHaveBeenCalledWith('medal')
907
- expect(mockIdbDel).toHaveBeenCalledWith('last_active_tab')
 
908
  // onboarding_completed is no longer used, so should not be called
909
  expect(mockIdbDel).not.toHaveBeenCalledWith('onboarding_completed')
910
 
@@ -913,7 +915,7 @@ describe('Unit tests', () => {
913
  expect(mockUseChatSetState).toHaveBeenCalledWith({ chat: [] })
914
  expect(mockUseJournalSetState).toHaveBeenCalledWith({ journal: [] })
915
  expect(mockUseNoContactSetState).toHaveBeenCalledWith({ noContact: { startISO: null, lastPraiseISO: null } })
916
- expect(mockUseMedalSetState).toHaveBeenCalledWith({ medal: null })
917
 
918
  // Verify page reload was called
919
  expect(mockReload).toHaveBeenCalled()
 
829
  const mockUseChatSetState = vi.fn()
830
  const mockUseJournalSetState = vi.fn()
831
  const mockUseNoContactSetState = vi.fn()
832
+ const mockUseMedalSetState: any = vi.fn()
833
 
834
  // Mock window.location.reload
835
  const mockReload = vi.fn()
 
879
  mockIdbDel('chat'),
880
  mockIdbDel('journal'),
881
  mockIdbDel('noContact'),
882
+ mockIdbDel('medals'),
883
+ mockIdbDel('completedActions'),
884
  mockIdbDel('last_active_tab')
885
  ])
886
  console.log('Data deleted from IndexedDB')
 
890
  mockUseChatSetState({ chat: [] })
891
  mockUseJournalSetState({ journal: [] })
892
  mockUseNoContactSetState({ noContact: { startISO: null, lastPraiseISO: null } })
893
+ mockUseMedalSetState({ medals: [] })
894
  console.log('Zustand reset, reloading page...')
895
 
896
  // Reload stránky pro čistý start
 
900
  await handleForget()
901
 
902
  // Verify all IndexedDB keys were deleted (except onboarding_completed which is no longer used)
903
+ expect(mockIdbDel).toHaveBeenCalledWith('profile')
904
+ expect(mockIdbDel).toHaveBeenCalledWith('chat')
905
+ expect(mockIdbDel).toHaveBeenCalledWith('journal')
906
+ expect(mockIdbDel).toHaveBeenCalledWith('noContact')
907
+ expect(mockIdbDel).toHaveBeenCalledWith('medals')
908
+ expect(mockIdbDel).toHaveBeenCalledWith('completedActions')
909
+ expect(mockIdbDel).toHaveBeenCalledWith('last_active_tab')
910
  // onboarding_completed is no longer used, so should not be called
911
  expect(mockIdbDel).not.toHaveBeenCalledWith('onboarding_completed')
912
 
 
915
  expect(mockUseChatSetState).toHaveBeenCalledWith({ chat: [] })
916
  expect(mockUseJournalSetState).toHaveBeenCalledWith({ journal: [] })
917
  expect(mockUseNoContactSetState).toHaveBeenCalledWith({ noContact: { startISO: null, lastPraiseISO: null } })
918
+ expect(mockUseMedalSetState).toHaveBeenCalledWith({ medals: [] } as any)
919
 
920
  // Verify page reload was called
921
  expect(mockReload).toHaveBeenCalled()