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:
@@ -185,7 +185,7 @@ export function HomeClient({ initialNotes, initialSettings }: HomeClientProps) {
|
||||
handleNoteCreated(newNote)
|
||||
setEditingNote({ note: newNote, readOnly: false })
|
||||
} catch {
|
||||
toast.error(t('notes.createFailed') || 'Failed to create note')
|
||||
toast.error(t('notes.createFailed'))
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -429,10 +429,10 @@ export function HomeClient({ initialNotes, initialSettings }: HomeClientProps) {
|
||||
}, [sortedNotes])
|
||||
|
||||
const sortLabels: Record<SortOrder, string> = {
|
||||
newest: t('sidebar.sortNewest') || 'Plus récentes',
|
||||
oldest: t('sidebar.sortOldest') || 'Plus anciennes',
|
||||
alpha: t('sidebar.sortAlpha') || 'A → Z',
|
||||
manual: t('sidebar.sortManual') || 'Libre',
|
||||
newest: t('sidebar.sortNewest'),
|
||||
oldest: t('sidebar.sortOldest'),
|
||||
alpha: t('sidebar.sortAlpha'),
|
||||
manual: t('sidebar.sortManual'),
|
||||
}
|
||||
|
||||
|
||||
@@ -495,9 +495,9 @@ export function HomeClient({ initialNotes, initialSettings }: HomeClientProps) {
|
||||
{currentNotebook
|
||||
? currentNotebook.name
|
||||
: searchParams.get('shared') === '1'
|
||||
? (t('sidebar.sharedWithMe') || 'Partagées avec moi')
|
||||
? t('sidebar.sharedWithMe')
|
||||
: searchParams.get('reminders') === '1'
|
||||
? (t('sidebar.reminders') || 'Rappels')
|
||||
? t('sidebar.reminders')
|
||||
: t('notes.title')}
|
||||
</h1>
|
||||
</div>
|
||||
@@ -511,7 +511,7 @@ export function HomeClient({ initialNotes, initialSettings }: HomeClientProps) {
|
||||
className="flex items-center gap-2 text-[13px] text-foreground font-medium hover:opacity-70 transition-opacity"
|
||||
>
|
||||
<Plus size={16} />
|
||||
<span>{t('notes.newNote') || 'Add Note'}</span>
|
||||
<span>{t('notes.newNote')}</span>
|
||||
</button>
|
||||
|
||||
{currentNotebook && (
|
||||
@@ -520,7 +520,7 @@ export function HomeClient({ initialNotes, initialSettings }: HomeClientProps) {
|
||||
className="flex items-center gap-2 text-[13px] text-foreground font-medium hover:opacity-70 transition-opacity"
|
||||
>
|
||||
<FolderOpen size={16} />
|
||||
<span>{t('notebook.createSubNotebook') || 'Nouveau sous-carnet'}</span>
|
||||
<span>{t('notebook.createSubNotebook')}</span>
|
||||
</button>
|
||||
)}
|
||||
|
||||
@@ -558,7 +558,7 @@ export function HomeClient({ initialNotes, initialSettings }: HomeClientProps) {
|
||||
router.push(`/home?${params.toString()}`)
|
||||
}
|
||||
}}
|
||||
placeholder={t('search.placeholder') || 'Rechercher...'}
|
||||
placeholder={t('search.placeholder')}
|
||||
className="w-48 bg-transparent border-b border-foreground/20 focus:border-foreground outline-none text-[13px] text-foreground placeholder:text-muted-foreground/50 py-0.5 transition-colors"
|
||||
/>
|
||||
{inlineSearchQuery && (
|
||||
@@ -585,7 +585,7 @@ export function HomeClient({ initialNotes, initialSettings }: HomeClientProps) {
|
||||
className="flex items-center gap-2 text-[13px] text-foreground font-medium hover:opacity-70 transition-opacity"
|
||||
>
|
||||
<Search size={16} />
|
||||
<span>{t('notes.search') || 'Search'}</span>
|
||||
<span>{t('notes.search')}</span>
|
||||
</button>
|
||||
)}
|
||||
|
||||
@@ -595,7 +595,7 @@ export function HomeClient({ initialNotes, initialSettings }: HomeClientProps) {
|
||||
className="flex items-center gap-2 text-[13px] text-foreground font-medium hover:opacity-70 transition-opacity"
|
||||
>
|
||||
<Sparkles size={16} />
|
||||
<span>{t('notes.reorganize') || 'Réorganiser les notes'}</span>
|
||||
<span>{t('notes.reorganize')}</span>
|
||||
</button>
|
||||
)}
|
||||
|
||||
@@ -622,7 +622,7 @@ export function HomeClient({ initialNotes, initialSettings }: HomeClientProps) {
|
||||
title={initialSettings.aiAssistantEnabled ? t('notebook.summary') : t('notebook.assistantRequiredForSummarize')}
|
||||
>
|
||||
<FileText size={16} />
|
||||
<span>{t('notebook.summary') || 'Summarize'}</span>
|
||||
<span>{t('notebook.summary')}</span>
|
||||
</button>
|
||||
)}
|
||||
<button
|
||||
@@ -640,7 +640,7 @@ export function HomeClient({ initialNotes, initialSettings }: HomeClientProps) {
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-3 text-[10px] font-bold uppercase tracking-[0.2em] text-muted-foreground">
|
||||
<TagIcon size={12} />
|
||||
<span>{t('labels.filterByTags') || 'Filter by Tags'}</span>
|
||||
<span>{t('labels.filterByTags')}</span>
|
||||
{selectedTagIds.length > 0 && (
|
||||
<span className="bg-brand-accent/10 text-brand-accent px-2 py-0.5 rounded-full text-[9px] lowercase tracking-normal">
|
||||
{selectedTagIds.length} active
|
||||
@@ -650,7 +650,7 @@ export function HomeClient({ initialNotes, initialSettings }: HomeClientProps) {
|
||||
{availableTags.length > 10 && (
|
||||
<input
|
||||
type="text"
|
||||
placeholder={t('labels.searchTags') || 'Search tags...'}
|
||||
placeholder={t('labels.searchTags')}
|
||||
className="bg-transparent border-b border-foreground/10 text-[10px] outline-none focus:border-brand-accent/40 py-1 px-2 w-32 transition-all focus:w-48 placeholder:text-muted-foreground/40"
|
||||
value={tagSearchQuery}
|
||||
onChange={e => setTagSearchQuery(e.target.value)}
|
||||
@@ -696,7 +696,7 @@ export function HomeClient({ initialNotes, initialSettings }: HomeClientProps) {
|
||||
className="px-3 py-1.5 text-[10px] font-bold uppercase tracking-wider text-muted-foreground/60 hover:text-foreground transition-colors border border-dashed border-border rounded-full"
|
||||
>
|
||||
{isTagsExpanded
|
||||
? (t('labels.showLess') || 'Show less')
|
||||
? t('labels.showLess')
|
||||
: `+ ${availableTags.length - 10} more`}
|
||||
</button>
|
||||
)}
|
||||
@@ -706,7 +706,7 @@ export function HomeClient({ initialNotes, initialSettings }: HomeClientProps) {
|
||||
onClick={() => setSelectedTagIds([])}
|
||||
className="px-3 py-1.5 text-[10px] font-bold uppercase tracking-wider text-red-500 hover:underline ms-auto"
|
||||
>
|
||||
{t('labels.clearAll') || 'Clear all'}
|
||||
{t('labels.clearAll')}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
@@ -745,14 +745,14 @@ export function HomeClient({ initialNotes, initialSettings }: HomeClientProps) {
|
||||
{notes.length === 0 && (
|
||||
<div className="h-64 flex flex-col items-center justify-center text-center space-y-4">
|
||||
<p className="font-memento-serif text-xl italic text-muted-foreground">
|
||||
{t('notes.emptyState') || 'This notebook is waiting for its first vision.'}
|
||||
{t('notes.emptyState')}
|
||||
</p>
|
||||
<button
|
||||
onClick={handleAddNote}
|
||||
disabled={isCreating}
|
||||
className="px-6 py-2 border border-foreground text-[13px] uppercase tracking-[0.2em] hover:bg-foreground hover:text-background transition-all"
|
||||
>
|
||||
{t('notes.createFirst') || 'Begin Drawing'}
|
||||
{t('notes.createFirst')}
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user