import { Button, Dialog, Notification, toast } from '@/components/ui' import { MenuDto } from '@/proxy/menus/models' import { MenuItem } from '@/proxy/menus/menu' import { MenuService } from '@/services/menu.service' import navigationIcon from '@/proxy/menus/navigation-icon.config' import { useEffect, useRef, useState } from 'react' import { FaChevronDown, FaPlus } from 'react-icons/fa' import { useLocalization } from '@/utils/hooks/useLocalization' const menuService = new MenuService() // ─── IconPickerField ────────────────────────────────────────────────────────── interface IconPickerFieldProps { value: string onChange: (iconKey: string) => void invalid?: boolean } const ALL_ICON_ENTRIES = Object.entries(navigationIcon) const ICON_PAGE_SIZE = 100 export function IconPickerField({ value, onChange, invalid }: IconPickerFieldProps) { const [open, setOpen] = useState(false) const [search, setSearch] = useState('') const [limit, setLimit] = useState(ICON_PAGE_SIZE) const wrapperRef = useRef(null) const SelectedIcon = value ? navigationIcon[value] : null const filtered = search.trim() ? ALL_ICON_ENTRIES.filter(([key]) => key.toLowerCase().includes(search.toLowerCase())) : ALL_ICON_ENTRIES const displayed = filtered.slice(0, limit) const hasMore = displayed.length < filtered.length const { translate } = useLocalization() useEffect(() => { setLimit(ICON_PAGE_SIZE) }, [search]) useEffect(() => { function handleClickOutside(e: MouseEvent) { if (wrapperRef.current && !wrapperRef.current.contains(e.target as Node)) setOpen(false) } if (open) document.addEventListener('mousedown', handleClickOutside) return () => document.removeEventListener('mousedown', handleClickOutside) }, [open]) return (
{open && (
setSearch(e.target.value)} placeholder="Search icons… (FaHome, FcSettings)" className="flex-1 px-2 py-1.5 text-sm rounded border border-gray-200 dark:border-gray-600 dark:bg-gray-700 dark:text-gray-100 outline-none focus:border-indigo-400" /> {filtered.length} icons
{displayed.map(([key, Icon]) => ( ))}
{hasMore && (
{displayed.length} / {filtered.length}
)}
)}
) } // ─── MenuAddDialog ──────────────────────────────────────────────────────────── export interface MenuAddDialogProps { isOpen: boolean onClose: () => void initialParentCode: string initialOrder: number rawItems: (MenuItem & { id?: string })[] onSaved: () => void } export function MenuAddDialog({ isOpen, onClose, initialParentCode, initialOrder, rawItems, onSaved, }: MenuAddDialogProps) { const [form, setForm] = useState({ name: '', code: '', menuTextEn: '', menuTextTr: '', parentCode: initialParentCode, icon: '', shortName: '', order: initialOrder, }) const [saving, setSaving] = useState(false) const { translate } = useLocalization() useEffect(() => { if (isOpen) setForm({ name: '', code: '', menuTextEn: '', menuTextTr: '', parentCode: initialParentCode, icon: '', shortName: '', order: initialOrder, }) }, [isOpen, initialParentCode, initialOrder]) const shortNameRequired = !form.parentCode.trim() console.log(form.parentCode.length) const handleSave = async () => { if (!form.code.trim() || !form.menuTextEn.trim()) return if (shortNameRequired && !form.shortName.trim()) return setSaving(true) try { await menuService.createWithLanguageKeyText({ code: form.code.trim(), displayName: form.code.trim(), parentCode: form.parentCode.trim() || undefined, icon: form.icon || undefined, shortName: form.shortName.trim() || undefined, order: form.order, isDisabled: false, menuTextTr: form.menuTextTr.trim(), menuTextEn: form.menuTextEn.trim(), } as MenuDto) onSaved() onClose() } catch (e: any) { toast.push(, { placement: 'top-end' }) } finally { setSaving(false) } } // suppress unused warning — rawItems kept for future use void rawItems const fieldCls = 'h-11 px-3 py-2 rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-800 text-sm text-gray-700 dark:text-gray-200 outline-none focus:border-indigo-400 w-full' const disabledCls = 'h-11 px-3 py-2 rounded-lg border border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-700 text-sm text-gray-400 dark:text-gray-500 cursor-not-allowed w-full' const labelCls = 'text-xs font-medium text-gray-500 dark:text-gray-400 mb-1' const toCodeToken = (value: string) => value.replace(/\s+/g, '') const toSpacedLabel = (value: string) => value .replace(/([a-z0-9])([A-Z])/g, '$1 $2') .replace(/([A-Z])([A-Z][a-z])/g, '$1 $2') .trim() return (
{/* Header */}
{translate('::ListForms.Wizard.Step1.AddNewMenu')}
{/* Row 1 — Name | Code */}
{ const codeToken = toCodeToken(e.target.value) const spacedLabel = toSpacedLabel(codeToken) setForm((p) => ({ ...p, name: codeToken, code: `App.Wizard.${codeToken}`, menuTextEn: spacedLabel, menuTextTr: spacedLabel, shortName: codeToken.substring(0, 3), })) }} placeholder="MyMenu" className={fieldCls} />
{/* Row 2 — Icon (full width) */}
setForm((p) => ({ ...p, icon: key }))} />
{/* Row 3 — Display Name EN | Display Name TR */}
setForm((p) => ({ ...p, menuTextEn: e.target.value }))} placeholder="My Menu" className={fieldCls} />
setForm((p) => ({ ...p, menuTextTr: e.target.value }))} placeholder="Menüm" className={fieldCls} />
{/* Row 4 — Menu Parent | Order */}
setForm((p) => ({ ...p, order: Number(e.target.value) }))} className={fieldCls} />
{/* Row 5 — Short Name (full width) */}
setForm((p) => ({ ...p, shortName: e.target.value }))} placeholder="Sas, Finance, Hr…" className={`${fieldCls}`} /> {shortNameRequired && (

{translate('::ListForms.Wizard.Step1.ShortNameHint') || 'Tablo prefix olarak kullanılır (örn. "Sas" → Sas_D_TableName)'}

)}
{/* Footer */}
) }