226 lines
8.1 KiB
TypeScript
226 lines
8.1 KiB
TypeScript
'use client'
|
|
|
|
import { useState } from 'react'
|
|
import { useRouter } from 'next/navigation'
|
|
import { Plus, X, Folder, Briefcase, FileText, Zap, BarChart3, Globe, Sparkles, Book, Heart, Crown, Music, Building2 } from 'lucide-react'
|
|
import { Button } from '@/components/ui/button'
|
|
import {
|
|
Dialog,
|
|
DialogContent,
|
|
DialogDescription,
|
|
DialogHeader,
|
|
DialogTitle,
|
|
} from '@/components/ui/dialog'
|
|
import { Input } from '@/components/ui/input'
|
|
import { cn } from '@/lib/utils'
|
|
import { useLanguage } from '@/lib/i18n'
|
|
|
|
const NOTEBOOK_ICONS = [
|
|
{ icon: Folder, name: 'folder' },
|
|
{ icon: Briefcase, name: 'briefcase' },
|
|
{ icon: FileText, name: 'document' },
|
|
{ icon: Zap, name: 'lightning' },
|
|
{ icon: BarChart3, name: 'chart' },
|
|
{ icon: Globe, name: 'globe' },
|
|
{ icon: Sparkles, name: 'sparkle' },
|
|
{ icon: Book, name: 'book' },
|
|
{ icon: Heart, name: 'heart' },
|
|
{ icon: Crown, name: 'crown' },
|
|
{ icon: Music, name: 'music' },
|
|
{ icon: Building2, name: 'building' },
|
|
]
|
|
|
|
const NOTEBOOK_COLORS = [
|
|
{ name: 'Slate', value: '#64748B', bg: 'bg-slate-500' },
|
|
{ name: 'Purple', value: '#8B5CF6', bg: 'bg-purple-500' },
|
|
{ name: 'Red', value: '#EF4444', bg: 'bg-red-500' },
|
|
{ name: 'Orange', value: '#F59E0B', bg: 'bg-orange-500' },
|
|
{ name: 'Green', value: '#10B981', bg: 'bg-green-500' },
|
|
{ name: 'Teal', value: '#14B8A6', bg: 'bg-teal-500' },
|
|
{ name: 'Gray', value: '#6B7280', bg: 'bg-gray-500' },
|
|
]
|
|
|
|
interface CreateNotebookDialogProps {
|
|
open?: boolean
|
|
onOpenChange?: (open: boolean) => void
|
|
}
|
|
|
|
export function CreateNotebookDialog({ open, onOpenChange }: CreateNotebookDialogProps) {
|
|
const router = useRouter()
|
|
const { t } = useLanguage()
|
|
const [name, setName] = useState('')
|
|
const [selectedIcon, setSelectedIcon] = useState('folder')
|
|
const [selectedColor, setSelectedColor] = useState('#3B82F6')
|
|
const [isSubmitting, setIsSubmitting] = useState(false)
|
|
|
|
const handleSubmit = async (e: React.FormEvent) => {
|
|
e.preventDefault()
|
|
|
|
if (!name.trim()) return
|
|
|
|
setIsSubmitting(true)
|
|
|
|
try {
|
|
const response = await fetch('/api/notebooks', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({
|
|
name: name.trim(),
|
|
icon: selectedIcon,
|
|
color: selectedColor,
|
|
}),
|
|
})
|
|
|
|
if (response.ok) {
|
|
// Close dialog and reload
|
|
onOpenChange?.(false)
|
|
window.location.reload()
|
|
} else {
|
|
const error = await response.json()
|
|
console.error('Failed to create notebook:', error)
|
|
}
|
|
} catch (error) {
|
|
console.error('Failed to create notebook:', error)
|
|
} finally {
|
|
setIsSubmitting(false)
|
|
}
|
|
}
|
|
|
|
const handleReset = () => {
|
|
setName('')
|
|
setSelectedIcon('folder')
|
|
setSelectedColor('#3B82F6')
|
|
}
|
|
|
|
const SelectedIconComponent = NOTEBOOK_ICONS.find(i => i.name === selectedIcon)?.icon || Folder
|
|
|
|
return (
|
|
<Dialog open={open} onOpenChange={(val) => {
|
|
onOpenChange?.(val)
|
|
if (!val) handleReset()
|
|
}}>
|
|
<DialogContent className="sm:max-w-[500px] p-0">
|
|
<button
|
|
onClick={() => onOpenChange?.(false)}
|
|
className="absolute right-4 top-4 text-gray-400 hover:text-gray-600 dark:hover:text-gray-200 transition-colors z-10"
|
|
>
|
|
<X className="h-5 w-5" />
|
|
</button>
|
|
<DialogHeader className="px-8 pt-8 pb-4">
|
|
<DialogTitle className="text-2xl font-bold text-gray-900 dark:text-white mb-2">
|
|
{t('notebook.createNew')}
|
|
</DialogTitle>
|
|
<DialogDescription className="text-sm text-gray-500 dark:text-gray-400">
|
|
{t('notebook.createDescription')}
|
|
</DialogDescription>
|
|
</DialogHeader>
|
|
|
|
<form onSubmit={handleSubmit} className="px-8 pb-8">
|
|
<div className="space-y-6">
|
|
{/* Notebook Name */}
|
|
<div>
|
|
<label className="text-[11px] font-bold text-gray-500 dark:text-gray-400 uppercase tracking-wider mb-2 block">
|
|
{t('notebook.name')}
|
|
</label>
|
|
<Input
|
|
value={name}
|
|
onChange={(e) => setName(e.target.value)}
|
|
placeholder="e.g. Q4 Marketing Strategy"
|
|
className="w-full"
|
|
autoFocus
|
|
/>
|
|
</div>
|
|
|
|
{/* Icon Selection */}
|
|
<div>
|
|
<label className="text-[11px] font-bold text-gray-500 dark:text-gray-400 uppercase tracking-wider mb-3 block">
|
|
{t('notebook.selectIcon')}
|
|
</label>
|
|
<div className="grid grid-cols-6 gap-3">
|
|
{NOTEBOOK_ICONS.map((item) => {
|
|
const IconComponent = item.icon
|
|
const isSelected = selectedIcon === item.name
|
|
return (
|
|
<button
|
|
key={item.name}
|
|
type="button"
|
|
onClick={() => setSelectedIcon(item.name)}
|
|
className={cn(
|
|
"h-14 w-full rounded-xl border-2 flex items-center justify-center transition-all duration-200",
|
|
isSelected
|
|
? 'border-indigo-600 bg-indigo-50 dark:bg-indigo-900/20 text-indigo-600'
|
|
: 'border-gray-200 dark:border-gray-700 text-gray-400 hover:border-gray-300 dark:hover:border-gray-600'
|
|
)}
|
|
>
|
|
<IconComponent className="h-5 w-5" />
|
|
</button>
|
|
)
|
|
})}
|
|
</div>
|
|
</div>
|
|
|
|
{/* Color Selection */}
|
|
<div>
|
|
<label className="text-[11px] font-bold text-gray-500 dark:text-gray-400 uppercase tracking-wider mb-3 block">
|
|
{t('notebook.selectColor')}
|
|
</label>
|
|
<div className="flex items-center gap-3">
|
|
{NOTEBOOK_COLORS.map((color) => {
|
|
const isSelected = selectedColor === color.value
|
|
return (
|
|
<button
|
|
key={color.value}
|
|
type="button"
|
|
onClick={() => setSelectedColor(color.value)}
|
|
className={cn(
|
|
"h-10 w-10 rounded-full border-2 transition-all duration-200",
|
|
isSelected
|
|
? 'border-white scale-110 shadow-lg'
|
|
: 'border-gray-200 dark:border-gray-700 hover:scale-105'
|
|
)}
|
|
style={{ backgroundColor: color.value }}
|
|
title={color.name}
|
|
/>
|
|
)
|
|
})}
|
|
</div>
|
|
</div>
|
|
|
|
{/* Preview */}
|
|
{name.trim() && (
|
|
<div className="flex items-center gap-3 p-4 rounded-xl bg-gray-50 dark:bg-gray-900/50 border border-gray-200 dark:border-gray-700">
|
|
<div
|
|
className="w-10 h-10 rounded-xl flex items-center justify-center text-white shadow-md"
|
|
style={{ backgroundColor: selectedColor }}
|
|
>
|
|
<SelectedIconComponent className="h-5 w-5" />
|
|
</div>
|
|
<span className="font-semibold text-gray-900 dark:text-white">{name.trim()}</span>
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
{/* Action Buttons */}
|
|
<div className="flex items-center justify-between mt-8 pt-6 border-t border-gray-200 dark:border-gray-700">
|
|
<Button
|
|
type="button"
|
|
variant="ghost"
|
|
onClick={() => onOpenChange?.(false)}
|
|
className="text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-200"
|
|
>
|
|
{t('notebook.cancel')}
|
|
</Button>
|
|
<Button
|
|
type="submit"
|
|
disabled={!name.trim() || isSubmitting}
|
|
className="bg-indigo-600 hover:bg-indigo-700 text-white px-6"
|
|
>
|
|
{isSubmitting ? t('notebook.creating') : t('notebook.create')}
|
|
</Button>
|
|
</div>
|
|
</form>
|
|
</DialogContent>
|
|
</Dialog>
|
|
)
|
|
}
|