import React, { useState } from "react"; import { Button } from "@/src/components/ui/button"; import { useHasProjectAccess } from "@/src/features/rbac/utils/checkProjectAccess"; import { Dialog, DialogBody, DialogContent, DialogFooter, DialogHeader, DialogTitle, DialogTrigger, } from "@/src/components/ui/dialog"; import { PlusIcon, Trash } from "lucide-react"; import { type UseFormReturn, useFieldArray, useForm } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage, } from "@/src/components/ui/form"; import { Input } from "@/src/components/ui/input"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/src/components/ui/select"; import { api } from "@/src/utils/api"; import { Textarea } from "@/src/components/ui/textarea"; import { isBooleanDataType, isCategoricalDataType, isNumericDataType, } from "@/src/features/scores/lib/helpers"; import DocPopup from "@/src/components/layouts/doc-popup"; import { usePostHogClientCapture } from "@/src/features/posthog-analytics/usePostHogClientCapture"; import { createConfigSchema, updateConfigSchema, type CreateConfig, type UpdateConfig, } from "@/src/features/score-configs/lib/upsertFormTypes"; import { validateScoreConfigUpsertFormInput } from "@/src/features/score-configs/lib/validateScoreConfigUpsertFormInput"; import { ScoreConfigDataType } from "@langfuse/shared"; export function UpsertScoreConfigDialog({ projectId, id, open, onOpenChange, defaultValues, }: { projectId: string; id?: string | null; open: boolean; onOpenChange: (open: boolean) => void; defaultValues?: CreateConfig | UpdateConfig; }) { const [formError, setFormError] = useState(null); const capture = usePostHogClientCapture(); const hasAccess = useHasProjectAccess({ projectId: projectId, scope: "scoreConfigs:CUD", }); const utils = api.useUtils(); const createScoreConfig = api.scoreConfigs.create.useMutation({ onSuccess: () => utils.scoreConfigs.invalidate(), onError: (error) => setFormError(error.message ?? "An error occurred while creating config."), }); const updateScoreConfig = api.scoreConfigs.update.useMutation({ onSuccess: () => utils.scoreConfigs.invalidate(), onError: (error) => setFormError(error.message ?? "An error occurred while updating config."), }); const form = useForm({ resolver: zodResolver(id ? updateConfigSchema : createConfigSchema), defaultValues: defaultValues ?? { dataType: ScoreConfigDataType.NUMERIC, minValue: undefined, maxValue: undefined, name: "", }, }) as UseFormReturn; const { fields, append, remove, replace } = useFieldArray({ control: form.control, name: "categories", }); if (!hasAccess) return null; async function onSubmit(values: CreateConfig | UpdateConfig) { const error = validateScoreConfigUpsertFormInput(values); setFormError(error); const isValid = await form.trigger(); if (!isValid || error) return; if (!!id) { return updateScoreConfig .mutateAsync({ ...values, projectId, id: id as string, description: values.description ?? null, categories: values.categories?.length ? values.categories : undefined, }) .then(() => { capture("score_configs:update_form_submit", { dataType: values.dataType, }); form.reset(); onOpenChange(false); }); } else { return createScoreConfig .mutateAsync({ projectId, ...values, description: values.description ?? null, categories: values.categories?.length ? values.categories : undefined, }) .then(() => { capture("score_configs:create_form_submit", { dataType: values.dataType, }); form.reset(); onOpenChange(false); }); } } return ( <> { form.reset(); setFormError(null); onOpenChange(v); }} > {id ? "Update score config" : "Add new score config"}
( Name field.onChange(e.target.value.trimEnd()) } /> )} /> ( Data type )} /> {isNumericDataType(form.getValues("dataType")) ? ( <> ( Minimum (optional) { const value = e.target.value; field.onChange( value === "" ? undefined : Number(value), ); }} type="number" /> )} /> ( Maximum (optional) { const value = e.target.value; field.onChange( value === "" ? undefined : Number(value), ); }} type="number" /> )} /> ) : (
( <> {fields.length > 0 && (
Value Label
)} {fields.map((category, index) => (
( )} />
( field.onChange( e.target.value.trimEnd(), ) } readOnly={isBooleanDataType( form.getValues("dataType"), )} /> )} /> {isCategoricalDataType( form.getValues("dataType"), ) && ( )}
))} {isCategoricalDataType( form.getValues("dataType"), ) && (
)} )} />
)} ( <> Description (optional)