docs: add comprehensive Stripe billing guide
All checks were successful
Deploy to Production / Build and Deploy (push) Successful in 4s
All checks were successful
Deploy to Production / Build and Deploy (push) Successful in 4s
Covers architecture, configuration steps, user flows, API routes, webhooks, pricing, testing with Stripe CLI, production checklist, and troubleshooting.
This commit is contained in:
@@ -298,21 +298,21 @@ function SidebarCarnetItem({
|
||||
<button
|
||||
onClick={(e) => { e.stopPropagation(); onAddSubNotebook() }}
|
||||
className="p-1 hover:bg-ink/10 rounded-md transition-all text-concrete hover:text-ink"
|
||||
title={t('notebook.createSubNotebook') || 'Add sub-carnet'}
|
||||
title={t('notebook.createSubNotebook')}
|
||||
>
|
||||
<Plus size={10} />
|
||||
</button>
|
||||
<button
|
||||
onClick={(e) => { e.stopPropagation(); onRename() }}
|
||||
className="p-1 hover:bg-ink/10 rounded-md transition-all text-concrete hover:text-ink"
|
||||
title={t('notebook.rename') || 'Rename'}
|
||||
title={t('notebook.rename')}
|
||||
>
|
||||
<Pencil size={10} />
|
||||
</button>
|
||||
<button
|
||||
onClick={(e) => { e.stopPropagation(); onDelete() }}
|
||||
className="p-1 hover:bg-rose-50 rounded-md transition-all text-concrete hover:text-rose-500"
|
||||
title={t('notebook.delete') || 'Delete'}
|
||||
title={t('notebook.delete')}
|
||||
>
|
||||
<Trash2 size={10} />
|
||||
</button>
|
||||
@@ -399,7 +399,7 @@ function SidebarCarnetItem({
|
||||
))}
|
||||
{isActive && notes.length === 0 && !hasChildren && (
|
||||
<p className="ps-8 py-2 text-[10px] italic text-muted-foreground/40 font-light">
|
||||
{t('common.noResults') || 'No notes found'}
|
||||
{t('common.noResults')}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
@@ -583,7 +583,7 @@ export function Sidebar({ className, user }: { className?: string; user?: any })
|
||||
try {
|
||||
await moveNotebookToParent(dragId, targetId)
|
||||
} catch (err) {
|
||||
toast.error(err instanceof Error ? err.message : t('sidebar.moveFailed') || 'Failed to move notebook')
|
||||
toast.error(err instanceof Error ? err.message : t('sidebar.moveFailed'))
|
||||
setOrderedNotebooks(sortedNotebooks)
|
||||
}
|
||||
}
|
||||
@@ -602,7 +602,7 @@ export function Sidebar({ className, user }: { className?: string; user?: any })
|
||||
try {
|
||||
await moveNotebookToParent(dragId, null)
|
||||
} catch (err) {
|
||||
toast.error(err instanceof Error ? err.message : t('sidebar.moveFailed') || 'Failed to move notebook')
|
||||
toast.error(err instanceof Error ? err.message : t('sidebar.moveFailed'))
|
||||
setOrderedNotebooks(sortedNotebooks)
|
||||
}
|
||||
}
|
||||
@@ -835,14 +835,14 @@ export function Sidebar({ className, user }: { className?: string; user?: any })
|
||||
<DropdownMenuItem asChild>
|
||||
<Link href="/settings/profile" className="flex items-center gap-2 cursor-pointer">
|
||||
<User className="h-4 w-4" />
|
||||
{t('sidebar.profile') || 'Profil'}
|
||||
{t('sidebar.profile')}
|
||||
</Link>
|
||||
</DropdownMenuItem>
|
||||
{(user as { role?: string } | undefined)?.role === 'ADMIN' && (
|
||||
<DropdownMenuItem asChild>
|
||||
<a href="/admin" className="flex items-center gap-2 cursor-pointer">
|
||||
<Shield className="h-4 w-4" />
|
||||
{t('nav.adminDashboard') || 'Administration'}
|
||||
{t('nav.adminDashboard')}
|
||||
</a>
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
@@ -852,7 +852,7 @@ export function Sidebar({ className, user }: { className?: string; user?: any })
|
||||
onClick={() => signOut({ callbackUrl: '/login' })}
|
||||
>
|
||||
<LogOut className="h-4 w-4 me-2" />
|
||||
{t('sidebar.signOut') || 'Se déconnecter'}
|
||||
{t('sidebar.signOut')}
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
@@ -873,7 +873,7 @@ export function Sidebar({ className, user }: { className?: string; user?: any })
|
||||
<button
|
||||
onClick={() => { router.push('/home') }}
|
||||
className="p-1.5 text-muted-ink hover:text-ink transition-all hover:bg-white/50 dark:hover:bg-white/10 rounded-lg border border-transparent hover:border-border"
|
||||
title={t('nav.home') || 'Accueil'}
|
||||
title={t('nav.home')}
|
||||
>
|
||||
<Home size={14} />
|
||||
</button>
|
||||
@@ -1016,7 +1016,7 @@ export function Sidebar({ className, user }: { className?: string; user?: any })
|
||||
<div
|
||||
className="h-10 rounded-lg border-2 border-dashed border-brand-accent/20 flex items-center justify-center text-[11px] text-brand-accent/50"
|
||||
>
|
||||
{t('sidebar.dropToRoot') || 'Drop here to move to root'}
|
||||
{t('sidebar.dropToRoot')}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
@@ -1034,7 +1034,7 @@ export function Sidebar({ className, user }: { className?: string; user?: any })
|
||||
</p>
|
||||
<div className="px-4 py-8 text-center border border-dashed border-border rounded-2xl bg-paper/30">
|
||||
<Clock size={24} className="mx-auto text-concrete/40 mb-3" />
|
||||
<p className="text-[11px] text-concrete italic">{t('sidebar.noReminders') || 'No active reminders.'}</p>
|
||||
<p className="text-[11px] text-concrete italic">{t('sidebar.noReminders')}</p>
|
||||
</div>
|
||||
</motion.div>
|
||||
) : activeView === 'agents' ? (
|
||||
@@ -1118,7 +1118,7 @@ export function Sidebar({ className, user }: { className?: string; user?: any })
|
||||
)}
|
||||
>
|
||||
<Users size={14} className={searchParams.get('shared') === '1' && pathname === '/home' ? 'text-accent' : 'text-muted-ink group-hover:text-ink'} />
|
||||
<span>{t('sidebar.sharedWithMe') || 'Shared'}</span>
|
||||
<span>{t('sidebar.sharedWithMe')}</span>
|
||||
</Link>
|
||||
|
||||
<Link
|
||||
@@ -1175,7 +1175,7 @@ export function Sidebar({ className, user }: { className?: string; user?: any })
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<h3 className="text-sm font-semibold mb-4 font-memento-serif">
|
||||
{t('notebook.rename') || 'Rename notebook'}
|
||||
{t('notebook.rename')}
|
||||
</h3>
|
||||
<input
|
||||
autoFocus
|
||||
@@ -1183,7 +1183,7 @@ export function Sidebar({ className, user }: { className?: string; user?: any })
|
||||
onChange={(e) => setRenameValue(e.target.value)}
|
||||
onKeyDown={(e) => { if (e.key === 'Enter') handleConfirmRename(); if (e.key === 'Escape') setRenamingNotebook(null) }}
|
||||
className="w-full px-3 py-2 text-sm border border-border rounded-lg bg-transparent focus:outline-none focus:ring-2 focus:ring-foreground/20"
|
||||
placeholder={t('notebook.namePlaceholder') || 'Notebook name'}
|
||||
placeholder={t('notebook.namePlaceholder')}
|
||||
/>
|
||||
<div className="flex justify-end gap-2 mt-4">
|
||||
<button
|
||||
@@ -1191,14 +1191,14 @@ export function Sidebar({ className, user }: { className?: string; user?: any })
|
||||
disabled={isRenaming}
|
||||
className="px-4 py-1.5 text-xs font-medium text-muted-foreground hover:text-foreground transition-colors rounded-lg"
|
||||
>
|
||||
{t('common.cancel') || 'Cancel'}
|
||||
{t('common.cancel')}
|
||||
</button>
|
||||
<button
|
||||
onClick={handleConfirmRename}
|
||||
disabled={isRenaming || !renameValue.trim()}
|
||||
className="px-4 py-1.5 text-xs font-medium bg-foreground text-background rounded-lg hover:opacity-90 transition-opacity disabled:opacity-50"
|
||||
>
|
||||
{isRenaming ? '...' : (t('common.confirm') || 'Rename')}
|
||||
{isRenaming ? '...' : t('notebook.confirmRename')}
|
||||
</button>
|
||||
</div>
|
||||
</motion.div>
|
||||
@@ -1228,7 +1228,7 @@ export function Sidebar({ className, user }: { className?: string; user?: any })
|
||||
<Trash2 size={16} className="text-red-500" />
|
||||
</div>
|
||||
<h3 className="text-sm font-semibold mb-1 font-memento-serif">
|
||||
{t('notebook.trashTitle') || 'Move to trash'}
|
||||
{t('notebook.trashTitle')}
|
||||
</h3>
|
||||
<p className="text-xs text-muted-foreground mb-4">
|
||||
{t('notebook.trashConfirm', { name: deletingNotebook.name }) || `Move "${deletingNotebook.name}" to trash? You can restore it within 30 days.`}
|
||||
@@ -1244,14 +1244,14 @@ export function Sidebar({ className, user }: { className?: string; user?: any })
|
||||
disabled={isDeleting}
|
||||
className="px-4 py-1.5 text-xs font-medium text-muted-foreground hover:text-foreground transition-colors rounded-lg"
|
||||
>
|
||||
{t('common.cancel') || 'Cancel'}
|
||||
{t('common.cancel')}
|
||||
</button>
|
||||
<button
|
||||
onClick={handleConfirmDelete}
|
||||
disabled={isDeleting}
|
||||
className="px-4 py-1.5 text-xs font-medium bg-red-500 text-white rounded-lg hover:bg-red-600 transition-colors disabled:opacity-50"
|
||||
>
|
||||
{isDeleting ? '...' : (t('notebook.moveToTrash') || 'Move to trash')}
|
||||
{isDeleting ? '...' : t('notebook.moveToTrash')}
|
||||
</button>
|
||||
</div>
|
||||
</motion.div>
|
||||
|
||||
Reference in New Issue
Block a user