213 lines
9.6 KiB
Markdown
213 lines
9.6 KiB
Markdown
# Sonner Global Notification System — Ralph Loop Prompt
|
|
|
|
You are implementing a global toast notification system in the adiuvAI Electron app using shadcn's sonner component.
|
|
|
|
**Full plan:** Read `docs/plan-sonner-notifications.md` for the complete architecture, file list, i18n keys, and categorization of every mutation.
|
|
|
|
## Rules
|
|
|
|
- **Always read the plan first** at `docs/plan-sonner-notifications.md` before doing any work.
|
|
- **Always read a file before editing it.** Never edit blind.
|
|
- **One phase per iteration.** Complete one phase fully, verify it compiles, then move on.
|
|
- **Run `cd adiuvAI && npx tsc --noEmit` after each phase** to catch type errors early.
|
|
- **Run `cd adiuvAI && npm run lint` after Phase 3 and Phase 4** to catch lint issues.
|
|
- **Commit after each phase** with a descriptive message.
|
|
- **i18n: add keys to ALL 5 language files** (`en`, `it`, `es`, `fr`, `de`). The plan has complete translations for each.
|
|
- **Do NOT touch silent mutations** (note auto-save, kanban drag, sidebar toggle, AI chat/streaming). For these, add `onError` only if missing.
|
|
- **When removing `saved`/`setSaved` state patterns:** also remove the `setTimeout`, the button text ternary, and any `setSaved(false)` in `onChange` handlers. Replace button text with `{t('common.save')}`.
|
|
- **Import path for useNotify:** `import { useNotify } from '@/hooks/useNotify';`
|
|
- **Import path for toast (direct):** `import { toast } from 'sonner';` (only in useNotify.ts itself)
|
|
|
|
## Progress Tracking
|
|
|
|
Check the state of the codebase to determine which phase to work on:
|
|
|
|
1. **If `src/renderer/components/ui/sonner.tsx` does NOT exist** → Start Phase 1
|
|
2. **If `sonner.tsx` exists but settings components still have `setSaved`** → Do Phase 2
|
|
3. **If settings are done but CRUD components lack `useNotify`** → Do Phase 3
|
|
4. **If CRUD is done but auth/onboarding lack `useNotify`** → Do Phase 4
|
|
5. **If all phases are done and `npx tsc --noEmit` + `npm run lint` pass** → Do Phase 5 (verification)
|
|
|
|
---
|
|
|
|
## Phase 1: Foundation
|
|
|
|
### Step 1: Install sonner
|
|
|
|
```bash
|
|
cd adiuvAI && npx shadcn@latest add sonner --yes
|
|
```
|
|
|
|
### Step 2: Fix theme import in generated `sonner.tsx`
|
|
|
|
The generated file imports `useTheme` from `next-themes`. This app does NOT use next-themes. Fix the import:
|
|
|
|
```tsx
|
|
// WRONG (generated):
|
|
import { useTheme } from "next-themes"
|
|
|
|
// CORRECT:
|
|
import { useTheme } from "@/components/theme-provider"
|
|
```
|
|
|
|
Also set `position="bottom-right"` and add `richColors` on the `<Sonner>` component.
|
|
|
|
### Step 3: Add `<Toaster />` to `src/renderer/index.tsx`
|
|
|
|
Import `Toaster` from `@/components/ui/sonner` and render it as the last child inside `<QueryClientProvider>`, AFTER `<RouterProvider />`. This ensures toasts work during login, onboarding, AND normal app usage.
|
|
|
|
### Step 4: Create `src/renderer/hooks/useNotify.ts`
|
|
|
|
Create the hook exactly as specified in the plan (Section 1.4). The hook exports `{ notify, notifyError, notifyPromise }`.
|
|
|
|
### Step 5: Add i18n keys to all 5 translation files
|
|
|
|
Add the `"toast"` top-level key with all sub-keys to:
|
|
- `src/renderer/locales/en/translation.json` (English — from plan)
|
|
- `src/renderer/locales/it/translation.json` (Italian — from plan)
|
|
- `src/renderer/locales/es/translation.json` (Spanish — from plan)
|
|
- `src/renderer/locales/fr/translation.json` (French — from plan)
|
|
- `src/renderer/locales/de/translation.json` (German — from plan)
|
|
|
|
### Step 6: Verify
|
|
|
|
```bash
|
|
cd adiuvAI && npx tsc --noEmit
|
|
```
|
|
|
|
### Step 7: Commit
|
|
|
|
```bash
|
|
cd adiuvAI && git add -A && git commit -m "feat(notifications): add sonner toast foundation with useNotify hook and i18n keys"
|
|
```
|
|
|
|
---
|
|
|
|
## Phase 2: Settings Components
|
|
|
|
For each of these 5 files, apply the pattern: add `useNotify()`, remove `saved`/`setSaved` state, remove `setTimeout`, replace button text ternary, add `notify()` in `onSuccess`, add `notifyError()` in `onError`.
|
|
|
|
### Files (in order):
|
|
|
|
1. **`src/renderer/components/settings/GeneralSection.tsx`**
|
|
- Remove: `saved`, `setSaved`, `error`, `setError`, `setTimeout`, inline `<p>` error, `setSaved(false)` in onChange
|
|
- Add: `notify('success', 'toast.profile.updated')` in handleSave onSuccess
|
|
- Add: `notifyError('toast.profile.updateError', err)` in handleSave onError
|
|
- Add: `notify('info', 'toast.settings.languageChanged')` in handleLanguageChange
|
|
- Button text: `{t('common.save')}`
|
|
|
|
2. **`src/renderer/components/settings/ProfileSection.tsx`**
|
|
- Remove: `profileSaved`, `displaySaved` states and their `setTimeout`s
|
|
- Profile save → `notify('success', 'toast.settings.memorySaved')`
|
|
- Display save → `notify('success', 'toast.settings.formatPrefsSaved')`
|
|
- Reset onboarding → `notify('info', 'toast.onboarding.reset')`
|
|
|
|
3. **`src/renderer/components/settings/AccountSection.tsx`**
|
|
- Remove: `urlSaved`, `setUrlSaved` state and `setTimeout`
|
|
- Backend URL save → `notify('success', 'toast.settings.backendUrlSaved')`
|
|
- Add onError → `notifyError('toast.settings.backendUrlError', err)`
|
|
- Logout → `notify('info', 'toast.auth.loggedOut')`
|
|
|
|
4. **`src/renderer/components/settings/LocalAgentConfigPanel.tsx`**
|
|
- Remove: `saved` state and `setTimeout`
|
|
- Save → `notify('success', 'toast.agent.updated')`
|
|
- Add onError → `notifyError('toast.agent.updateError', err)`
|
|
|
|
5. **`src/renderer/components/settings/CloudAgentConfigPanel.tsx`**
|
|
- Same as LocalAgentConfigPanel
|
|
|
|
### Verify and Commit:
|
|
|
|
```bash
|
|
cd adiuvAI && npx tsc --noEmit && npm run lint
|
|
cd adiuvAI && git add -A && git commit -m "feat(notifications): replace settings saved-state patterns with sonner toasts"
|
|
```
|
|
|
|
---
|
|
|
|
## Phase 3: CRUD Operations
|
|
|
|
Add `useNotify()` to each component and wire `notify` / `notifyError` into existing `onSuccess` / `onError` callbacks. If `onError` doesn't exist, add it.
|
|
|
|
### Files and mutations:
|
|
|
|
**Tasks:**
|
|
- `src/renderer/components/tasks/NewTaskDialog.tsx` → `tasks.create`: success `toast.task.created` + inline `clients.create`: success `toast.client.created`
|
|
- `src/renderer/components/tasks/EditTaskDialog.tsx` → `tasks.update`: success `toast.task.updated`
|
|
- `src/renderer/components/tasks/TaskDetailDialog.tsx` → `taskComments.create`: success `toast.comment.created`, `taskComments.delete`: warning `toast.comment.deleted`
|
|
- `src/renderer/routes/tasks.tsx` → `tasks.delete`: warning `toast.task.deleted`, `tasks.update` status toggle: **onError only**
|
|
- `src/renderer/components/projects/KanbanBoard.tsx` → `tasks.update` drag: **onError only**, `tasks.delete`: warning `toast.task.deleted`
|
|
- `src/renderer/components/projects/ProjectDetail.tsx` → `tasks.delete`: warning `toast.task.deleted`, `tasks.update` toggle: **onError only**, `notes.create`: success `toast.note.created`
|
|
- `src/renderer/components/ai/blocks/ChatEntityBlock.tsx` → `tasks.delete`: warning `toast.task.deleted`, `tasks.update` toggle: **onError only**
|
|
|
|
**Projects & Clients:**
|
|
- `src/renderer/components/projects/ProjectSidebar.tsx` → `projects.create`: success, `projects.update`: success, `projects.delete`: warning, `projects.archiveByClient`: warning (check if archiving or unarchiving), `clients.create`: success, `clients.update`: success, `clients.deleteWithCascade`: warning
|
|
|
|
**Notes:**
|
|
- `src/renderer/routes/notes.$noteId.tsx` → `notes.delete`: warning `toast.note.deleted`, `notes.update` auto-save: **onError only** (SILENT)
|
|
|
|
**Timeline:**
|
|
- `src/renderer/components/timeline/AddEventDialog.tsx` → `timelineEvents.create`: success
|
|
- `src/renderer/components/timeline/EditEventDialog.tsx` → `timelineEvents.update`: success
|
|
- `src/renderer/routes/timeline.tsx` → `timelineEvents.delete`: warning, `timelineEvents.update`: success
|
|
|
|
**Agents:**
|
|
- `src/renderer/components/settings/AgentsSection.tsx` → `agent.*.delete`: warning, `agent.*.update` toggle: **onError only**, `agent.runNow`: use `notifyPromise`
|
|
- `src/renderer/components/settings/InlineAgentCreationStepper.tsx` → `agent.*.create`: success
|
|
|
|
### Verify and Commit:
|
|
|
|
```bash
|
|
cd adiuvAI && npx tsc --noEmit && npm run lint
|
|
cd adiuvAI && git add -A && git commit -m "feat(notifications): add sonner toasts to all CRUD operations"
|
|
```
|
|
|
|
---
|
|
|
|
## Phase 4: Auth + Onboarding
|
|
|
|
### Files:
|
|
|
|
1. **`src/renderer/components/auth/LoginForm.tsx`**
|
|
- `auth.login` onError → `notifyError('toast.auth.loginError', err)` (KEEP inline error too)
|
|
- `auth.register` onError → `notifyError('toast.auth.registerError', err)` (KEEP inline error too)
|
|
- `auth.loginWithOAuth` onError → `notifyError('toast.auth.oauthError', err)`
|
|
|
|
2. **`src/renderer/components/layout/AppShell.tsx`**
|
|
- `auth.logout` onSuccess → `notify('info', 'toast.auth.loggedOut')` (add before `utils.auth.status.invalidate()`)
|
|
|
|
3. **`src/renderer/components/onboarding/OnboardingFlow.tsx`**
|
|
- Final save onSuccess → `notify('success', 'toast.onboarding.completed', { descriptionKey: 'toast.onboarding.completedDescription' })`
|
|
- Final save onError → `notifyError('toast.onboarding.error', err)`
|
|
- Normalize call → use `notifyPromise` with loading/success/error keys
|
|
|
|
### Verify and Commit:
|
|
|
|
```bash
|
|
cd adiuvAI && npx tsc --noEmit && npm run lint
|
|
cd adiuvAI && git add -A && git commit -m "feat(notifications): add sonner toasts to auth and onboarding flows"
|
|
```
|
|
|
|
---
|
|
|
|
## Phase 5: Final Verification
|
|
|
|
Run these checks:
|
|
|
|
```bash
|
|
cd adiuvAI && npx tsc --noEmit
|
|
cd adiuvAI && npm run lint
|
|
cd adiuvAI && npm run knip
|
|
```
|
|
|
|
Verify:
|
|
- No remaining `setSaved` or `setTimeout.*setSaved` patterns in `src/renderer/components/settings/`
|
|
- All 5 translation files have the `toast` key with matching sub-keys
|
|
- `sonner.tsx` imports from `@/components/theme-provider` (NOT `next-themes`)
|
|
- `<Toaster />` renders in `index.tsx` inside `<ThemeProvider>`
|
|
- `useNotify.ts` exists in `src/renderer/hooks/`
|
|
|
|
If everything passes:
|
|
|
|
<promise>SONNER NOTIFICATIONS COMPLETE</promise>
|