Keep/keep-notes/components/notebook-summary-dialog.tsx
sepehr 7fb486c9a4 feat: Complete internationalization and code cleanup
## Translation Files
- Add 11 new language files (es, de, pt, ru, zh, ja, ko, ar, hi, nl, pl)
- Add 100+ missing translation keys across all 15 languages
- New sections: notebook, pagination, ai.batchOrganization, ai.autoLabels
- Update nav section with workspace, quickAccess, myLibrary keys

## Component Updates
- Update 15+ components to use translation keys instead of hardcoded text
- Components: notebook dialogs, sidebar, header, note-input, ghost-tags, etc.
- Replace 80+ hardcoded English/French strings with t() calls
- Ensure consistent UI across all supported languages

## Code Quality
- Remove 77+ console.log statements from codebase
- Clean up API routes, components, hooks, and services
- Keep only essential error handling (no debugging logs)

## UI/UX Improvements
- Update Keep logo to yellow post-it style (from-yellow-400 to-amber-500)
- Change selection colors to #FEF3C6 (notebooks) and #EFB162 (nav items)
- Make "+" button permanently visible in notebooks section
- Fix grammar and syntax errors in multiple components

## Bug Fixes
- Fix JSON syntax errors in it.json, nl.json, pl.json, zh.json
- Fix syntax errors in notebook-suggestion-toast.tsx
- Fix syntax errors in use-auto-tagging.ts
- Fix syntax errors in paragraph-refactor.service.ts
- Fix duplicate "fusion" section in nl.json

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

Ou une version plus courte si vous préférez :

feat(i18n): Add 15 languages, remove logs, update UI components

- Create 11 new translation files (es, de, pt, ru, zh, ja, ko, ar, hi, nl, pl)
- Add 100+ translation keys: notebook, pagination, AI features
- Update 15+ components to use translations (80+ strings)
- Remove 77+ console.log statements from codebase
- Fix JSON syntax errors in 4 translation files
- Fix component syntax errors (toast, hooks, services)
- Update logo to yellow post-it style
- Change selection colors (#FEF3C6, #EFB162)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-11 22:26:13 +01:00

157 lines
4.6 KiB
TypeScript

'use client'
import { useState, useEffect } from 'react'
import { Button } from './ui/button'
import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
} from './ui/dialog'
import { Loader2, FileText, RefreshCw } from 'lucide-react'
import { toast } from 'sonner'
import { useLanguage } from '@/lib/i18n'
import type { NotebookSummary } from '@/lib/ai/services'
import ReactMarkdown from 'react-markdown'
interface NotebookSummaryDialogProps {
open: boolean
onOpenChange: (open: boolean) => void
notebookId: string | null
notebookName?: string
}
export function NotebookSummaryDialog({
open,
onOpenChange,
notebookId,
notebookName,
}: NotebookSummaryDialogProps) {
const { t } = useLanguage()
const [summary, setSummary] = useState<NotebookSummary | null>(null)
const [loading, setLoading] = useState(false)
const [regenerating, setRegenerating] = useState(false)
// Fetch summary when dialog opens with a notebook
useEffect(() => {
if (open && notebookId) {
fetchSummary()
} else {
// Reset state when closing
setSummary(null)
}
}, [open, notebookId])
const fetchSummary = async () => {
if (!notebookId) return
setLoading(true)
try {
const response = await fetch('/api/ai/notebook-summary', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
credentials: 'include',
body: JSON.stringify({ notebookId }),
})
const data = await response.json()
if (data.success && data.data) {
setSummary(data.data)
} else {
toast.error(data.error || t('notebook.summaryError'))
onOpenChange(false)
}
} catch (error) {
toast.error(t('notebook.summaryError'))
onOpenChange(false)
} finally {
setLoading(false)
}
}
const handleRegenerate = async () => {
if (!notebookId) return
setRegenerating(true)
await fetchSummary()
setRegenerating(false)
}
if (loading) {
return (
<Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent className="max-w-2xl max-h-[80vh] overflow-y-auto">
<div className="flex flex-col items-center justify-center py-12">
<Loader2 className="h-8 w-8 animate-spin text-primary" />
<p className="mt-4 text-sm text-muted-foreground">
{t('notebook.generating')}
</p>
</div>
</DialogContent>
</Dialog>
)
}
if (!summary) {
return null
}
return (
<Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent className="max-w-3xl max-h-[80vh] overflow-y-auto">
<DialogHeader>
<DialogTitle className="flex items-center justify-between gap-4">
<div className="flex items-center gap-2">
<FileText className="h-5 w-5" />
{t('notebook.summary')}
</div>
<Button
variant="ghost"
size="sm"
onClick={handleRegenerate}
disabled={regenerating}
className="gap-2"
>
<RefreshCw className={`h-4 w-4 ${regenerating ? 'animate-spin' : ''}`} />
{regenerating
? (t('ai.notebookSummary.regenerating') || 'Regenerating...')
: (t('ai.notebookSummary.regenerate') || 'Regenerate')}
</Button>
</DialogTitle>
<DialogDescription>
{t('notebook.summaryDescription', {
notebook: summary.notebookName,
count: summary.stats.totalNotes,
})}
</DialogDescription>
</DialogHeader>
{/* Stats */}
<div className="flex flex-wrap gap-4 p-4 bg-muted rounded-lg">
<div className="flex items-center gap-2">
<FileText className="h-4 w-4 text-muted-foreground" />
<span className="text-sm">
{summary.stats.totalNotes} {summary.stats.totalNotes === 1 ? 'note' : 'notes'}
</span>
</div>
{summary.stats.labelsUsed.length > 0 && (
<div className="flex items-center gap-2">
<span className="text-sm text-muted-foreground">Labels:</span>
<span className="text-sm">{summary.stats.labelsUsed.join(', ')}</span>
</div>
)}
<div className="ml-auto text-xs text-muted-foreground">
{new Date(summary.generatedAt).toLocaleString()}
</div>
</div>
{/* Summary Content */}
<div className="prose prose-sm dark:prose-invert max-w-none">
<ReactMarkdown>{summary.summary}</ReactMarkdown>
</div>
</DialogContent>
</Dialog>
)
}