'use client' import { useMemo, useState } from 'react' import { useForm, Controller } from 'react-hook-form' import { zodResolver } from '@hookform/resolvers/zod' import { z } from 'zod' import { useTranslations } from 'next-intl' import { Button } from '@/components/ui/button' import { Input } from '@/components/ui/input' import { Label } from '@/components/ui/label' import { Switch } from '@/components/ui/switch' import { DestructiveConfirmDialog } from '@/components/ui/destructive-confirm-dialog' import { Loader2, Trash2 } from 'lucide-react' import { DIMENSION_CODE_PATTERN, PROJECT_DIM_NO, type DimensionDto, type DimensionValueDto, } from '@/components/dimensions/types' export interface DimensionValueFormInput { code: string name: string is_active: boolean start_date: string | null end_date: string | null } interface DimensionValueFormProps { /** The dimension the value belongs to (drives the Projekt date fields). */ dimension: DimensionDto /** When set, the form edits this value (code becomes immutable). */ value?: DimensionValueDto | null isSaving?: boolean onSubmit: (input: DimensionValueFormInput) => void | Promise /** Rendered only when editing. The caller performs the DELETE and surfaces * the retention-trigger error ("…arkivera det istället") as a toast. */ onDelete?: () => void | Promise } /** * Create/edit form for a dimension value (#OBJEKT), hosted in the * DimensionsManager dialog. Code is immutable in v1: the field is disabled * when editing. Start/end dates appear only for Projekt (dim 6), matching the * SIE model where projects span a date range while cost centres do not. */ export default function DimensionValueForm({ dimension, value, isSaving = false, onSubmit, onDelete, }: DimensionValueFormProps) { const t = useTranslations('dimensions') const isEditing = Boolean(value) const isProject = dimension.sie_dim_no === PROJECT_DIM_NO const [confirmDeleteOpen, setConfirmDeleteOpen] = useState(false) const schema = useMemo( () => z .object({ // Legacy/backfilled codes can be looser than the strict format, so // the pattern is only enforced when the code is user-typed (create). code: isEditing ? z.string() : z.string().regex(DIMENSION_CODE_PATTERN, t('form_code_invalid')), name: z.string().min(1, t('form_name_invalid')).max(120, t('form_name_invalid')), is_active: z.boolean(), start_date: z.string().optional(), end_date: z.string().optional(), }) .refine( (data) => !data.start_date || !data.end_date || data.end_date >= data.start_date, { message: t('form_date_order_invalid'), path: ['end_date'] }, ), [isEditing, t], ) type FormData = z.infer const { register, handleSubmit, control, formState: { errors }, } = useForm({ resolver: zodResolver(schema), defaultValues: { code: value?.code ?? '', name: value?.name ?? '', is_active: value?.is_active ?? true, start_date: value?.start_date ?? '', end_date: value?.end_date ?? '', }, }) function submit(data: FormData) { return onSubmit({ code: data.code.trim(), name: data.name.trim(), is_active: data.is_active, start_date: isProject && data.start_date ? data.start_date : null, end_date: isProject && data.end_date ? data.end_date : null, }) } return (

{isEditing ? t('form_code_immutable_help') : t('form_code_help')}

{errors.code && (

{errors.code.message}

)}
{errors.name && (

{errors.name.message}

)}
{isProject && (
{errors.end_date && (

{errors.end_date.message}

)}
)}

{t('form_active_help')}

( )} />
{isEditing && onDelete ? ( ) : ( )}
{onDelete && ( )} ) }