"use client"; import { useState } from "react"; import { useTranslations } from "next-intl"; import { useSettingsStore, KEYWORD_PALETTE, DEFAULT_KEYWORDS, type KeywordDefinition } from "@/stores/settings-store"; import { useAuthStore } from "@/stores/auth-store"; import { useEmailStore } from "@/stores/email-store"; import { SettingsSection } from "./settings-section"; import { Plus, Pencil, Trash2, GripVertical, Check, X, RotateCcw, Loader2 } from "lucide-react"; import { cn } from "@/lib/utils"; const PALETTE_KEYS = Object.keys(KEYWORD_PALETTE); function KeywordColorPicker({ value, onChange, }: { value: string; onChange: (color: string) => void; }) { return (
{PALETTE_KEYS.map((colorKey) => (
); } function KeywordRow({ keyword, onEdit, onDelete, }: { keyword: KeywordDefinition; onEdit: () => void; onDelete: () => void; }) { const t = useTranslations("settings.keywords"); const palette = KEYWORD_PALETTE[keyword.color]; return (
{keyword.label} {"$label:" + keyword.id}
); } function KeywordEditForm({ initial, existingIds, onSave, onCancel, }: { initial?: KeywordDefinition; existingIds: string[]; onSave: (keyword: KeywordDefinition) => void; onCancel: () => void; }) { const t = useTranslations("settings.keywords"); const [label, setLabel] = useState(initial?.label || ""); const [color, setColor] = useState(initial?.color || "blue"); const isEditing = !!initial; const normalizedId = label .trim() .toLowerCase() .replace(/[^a-z0-9_-]/g, "-") .replace(/-+/g, "-") .replace(/^-|-$/g, ""); const isDuplicate = normalizedId.length > 0 && existingIds.includes(normalizedId); const isValid = normalizedId.length > 0 && label.trim().length > 0 && !isDuplicate; const handleSave = () => { if (!isValid) return; onSave({ id: normalizedId, label: label.trim(), color }); }; return (
setLabel(e.target.value)} className="w-full px-2.5 py-1.5 text-sm rounded-md border border-border bg-background focus:outline-none focus:ring-2 focus:ring-ring" placeholder={t("label_placeholder")} autoFocus maxLength={30} onKeyDown={(e) => e.key === "Enter" && handleSave()} /> {isDuplicate && (

{t("id_exists")}

)}
); } export function KeywordSettings() { const t = useTranslations("settings.keywords"); const { emailKeywords, addKeyword, updateKeyword, renameKeyword, removeKeyword, reorderKeywords } = useSettingsStore(); const { client } = useAuthStore(); const { fetchTagCounts } = useEmailStore(); const [editingId, setEditingId] = useState(null); const [isAdding, setIsAdding] = useState(false); const [isMigrating, setIsMigrating] = useState(false); const existingIds = emailKeywords.map((k) => k.id); const handleAdd = (keyword: KeywordDefinition) => { addKeyword(keyword); setIsAdding(false); }; const handleEdit = async (keyword: KeywordDefinition) => { const oldId = editingId; if (!oldId) return; const idChanged = oldId !== keyword.id; if (idChanged && client) { setIsMigrating(true); try { const oldJmapKeyword = `$label:${oldId}`; const newJmapKeyword = `$label:${keyword.id}`; await client.migrateKeyword(oldJmapKeyword, newJmapKeyword); renameKeyword(oldId, keyword); fetchTagCounts(client); } catch (error) { console.error("Failed to migrate keyword:", error); const toastModule = await import('sonner'); toastModule.toast.error(t("migration_error")); setIsMigrating(false); return; } setIsMigrating(false); } else { updateKeyword(oldId, { label: keyword.label, color: keyword.color }); } setEditingId(null); }; const handleDelete = (id: string) => { removeKeyword(id); }; const handleResetDefaults = () => { reorderKeywords(DEFAULT_KEYWORDS); }; return (
{isMigrating && (
{t("migrating")}
)} {emailKeywords.map((keyword) => editingId === keyword.id ? ( id !== keyword.id)} onSave={handleEdit} onCancel={() => setEditingId(null)} /> ) : ( { setEditingId(keyword.id); setIsAdding(false); }} onDelete={() => handleDelete(keyword.id)} /> ) )} {isAdding ? ( setIsAdding(false)} /> ) : (
)}
); }