feat: Tier 1 & 2 — Daily Note, Voice, Flashcard quota, Readwise, Calendar, Agent Gallery
Some checks failed
CI / Lint, Unit Tests & Build (push) Failing after 1m7s
CI / Deploy production (on server) (push) Has been skipped

Tier 1:
- BASIC tier: chat (10/mo) + reformulate (10/mo) désormais accessibles
- Nouveaux quotas: ai_flashcard + voice_transcribe dans tous les tiers
- /api/notes/daily : note du jour auto-créée (find or create)
- Bouton Note du Jour dans la sidebar (CalendarDays)
- Voice-to-Text dans l'éditeur (Web Speech API, bouton Mic toolbar)
- Flashcard generation → quota ai_flashcard (au lieu de reformulate)

Tier 2:
- Intégration Readwise: GET/POST/DELETE /api/integrations/readwise
- Intégration Google Calendar: OAuth flow + today's events + meeting notes
- /api/integrations/calendar + /callback
- Page /settings/integrations avec cards Calendar + Readwise
- SettingsNav: onglet Intégrations
- AgentTemplates: catégories + 4 nouveaux templates (Digest/Recap/AutoTagger/Synthesis)

Schema:
- UserAISettings.integrationTokens Json? (migration 20260529160000)
- prisma generate + migrate deploy appliqués

Fix:
- SpeechRecognition types (triple-slash @types/dom-speech-recognition)
- Notebook.create: suppression champ 'description' inexistant

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
Antigravity
2026-05-29 15:14:01 +00:00
parent 79fd6553b7
commit c415d93945
20 changed files with 1271 additions and 63 deletions

View File

@@ -12,6 +12,7 @@ import {
BookMarked,
Bot,
Inbox,
CalendarDays,
FlaskConical,
ArrowUpDown,
Archive,
@@ -764,6 +765,21 @@ export function Sidebar({ className, user }: { className?: string; user?: any })
router.push('/home?forceList=1')
}
const handleDailyNoteClick = async () => {
try {
const res = await fetch('/api/notes/daily')
const data = await res.json()
if (data.success && data.note) {
const params = new URLSearchParams()
if (data.note.notebookId) params.set('notebook', data.note.notebookId)
params.set('openNote', data.note.id)
router.push(`/home?${params.toString()}`)
}
} catch {
toast.error(t('sidebar.dailyNoteError') || 'Impossible d\'ouvrir la note du jour')
}
}
const handleNoteClick = (noteId: string, notebookId: string) => {
const params = new URLSearchParams(searchParams.toString())
params.set('notebook', notebookId)
@@ -1379,6 +1395,19 @@ export function Sidebar({ className, user }: { className?: string; user?: any })
</span>
</button>
<button
type="button"
onClick={handleDailyNoteClick}
className="sidebar-inbox-item"
>
<div className="w-8 h-8 rounded-full flex items-center justify-center text-sm font-medium border shrink-0 bg-paper dark:bg-white/5 text-muted-ink border-border">
<CalendarDays size={14} />
</div>
<span className="text-[13px] font-medium truncate text-muted-ink">
{t('sidebar.dailyNote') || 'Note du jour'}
</span>
</button>
<div className="my-3 h-px bg-border/40" />
<div