'use client'; import { useState, useCallback } from 'react'; import { useTranslations } from 'next-intl'; import { X, Plus, Pencil, Trash2, GripVertical, ExternalLink, PanelRight } from 'lucide-react'; import { icons as lucideIcons, type LucideIcon } from 'lucide-react'; import { cn } from '@/lib/utils'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { IconPicker } from './icon-picker'; import { useSettingsStore, type SidebarApp } from '@/stores/settings-store'; import { useFocusTrap } from '@/hooks/use-focus-trap'; import { useConfirmDialog } from '@/hooks/use-confirm-dialog'; import { ConfirmDialog } from '@/components/ui/confirm-dialog'; interface SidebarAppFormData { name: string; url: string; icon: string; openMode: 'tab' | 'inline'; } function SidebarAppForm({ app, onSave, onCancel, }: { app?: SidebarApp; onSave: (data: SidebarAppFormData) => void; onCancel: () => void; }) { const t = useTranslations('sidebar_apps'); const isEditing = !!app; const [formData, setFormData] = useState({ name: app?.name || '', url: app?.url || '', icon: app?.icon || 'Globe', openMode: app?.openMode || 'tab', }); const [errors, setErrors] = useState>({}); const validate = (): boolean => { const newErrors: Record = {}; if (!formData.name.trim()) { newErrors.name = t('name_required'); } if (!formData.url.trim()) { newErrors.url = t('url_required'); } else { try { const parsed = new URL(formData.url); if (!['http:', 'https:'].includes(parsed.protocol)) { newErrors.url = t('url_invalid'); } } catch { newErrors.url = t('url_invalid'); } } if (!formData.icon) { newErrors.icon = t('icon_required'); } setErrors(newErrors); return Object.keys(newErrors).length === 0; }; const handleSubmit = (e: React.FormEvent) => { e.preventDefault(); if (!validate()) return; onSave(formData); }; const SelectedIcon = formData.icon ? (lucideIcons[formData.icon as keyof typeof lucideIcons] as LucideIcon | undefined) : null; return (
{/* Name */}
setFormData({ ...formData, name: e.target.value })} placeholder={t('name_placeholder')} className={errors.name ? 'border-destructive' : ''} /> {errors.name && (

{errors.name}

)}
{/* URL */}
setFormData({ ...formData, url: e.target.value })} placeholder="https://example.com" className={errors.url ? 'border-destructive' : ''} /> {errors.url && (

{errors.url}

)}
{/* Open Mode */}
{/* Icon Picker */}
setFormData({ ...formData, icon })} /> {errors.icon && (

{errors.icon}

)}
{/* Actions */}
); } interface SidebarAppsModalProps { isOpen: boolean; onClose: () => void; } export function SidebarAppsModal({ isOpen, onClose }: SidebarAppsModalProps) { const t = useTranslations('sidebar_apps'); const { sidebarApps, addSidebarApp, updateSidebarApp, removeSidebarApp } = useSettingsStore(); const [editingId, setEditingId] = useState(null); const [isCreating, setIsCreating] = useState(false); const { dialogProps: confirmDialogProps, confirm: confirmDialog } = useConfirmDialog(); const modalRef = useFocusTrap({ isActive: isOpen, onEscape: () => { if (isCreating || editingId) { setIsCreating(false); setEditingId(null); } else { onClose(); } }, restoreFocus: true, }); const handleCreate = useCallback((data: SidebarAppFormData) => { const id = `app-${Date.now()}-${Math.random().toString(36).slice(2, 7)}`; addSidebarApp({ id, ...data }); setIsCreating(false); }, [addSidebarApp]); const handleUpdate = useCallback((id: string, data: SidebarAppFormData) => { updateSidebarApp(id, data); setEditingId(null); }, [updateSidebarApp]); const handleDelete = useCallback(async (app: SidebarApp) => { const confirmed = await confirmDialog({ title: t('delete_confirm_title'), message: t('delete_confirm', { name: app.name }), confirmText: t('delete'), variant: 'destructive', }); if (!confirmed) return; removeSidebarApp(app.id); }, [removeSidebarApp, confirmDialog, t]); if (!isOpen) return null; return (
{/* Header */}
{/* Content */}
{/* Create form */} {isCreating && (

{t('add_new')}

setIsCreating(false)} />
)} {/* Add button */} {!isCreating && !editingId && ( )} {/* Apps list */}
{sidebarApps.map((app) => { const AppIcon = lucideIcons[app.icon as keyof typeof lucideIcons] as LucideIcon | undefined; if (editingId === app.id) { return (

{t('edit_app')}

handleUpdate(app.id, data)} onCancel={() => setEditingId(null)} />
); } return (
{AppIcon ? : null}

{app.name}

{app.url}

{app.openMode === 'inline' ? t('inline_badge') : t('tab_badge')}
); })} {sidebarApps.length === 0 && !isCreating && (

{t('no_apps')}

{t('no_apps_hint')}

)}
); }