"use client"; import { zodResolver } from "@hookform/resolvers/zod"; import { pageComponentImpact } from "@openstatus/db/src/schema/page_components/constants"; import { statusReportStatus } from "@openstatus/db/src/schema/status_reports/constants"; import { Checkbox } from "@openstatus/ui/components/ui/checkbox"; import { Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage, } from "@openstatus/ui/components/ui/form"; import { Label } from "@openstatus/ui/components/ui/label"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@openstatus/ui/components/ui/select"; import { TabsContent } from "@openstatus/ui/components/ui/tabs"; import { TabsList, TabsTrigger } from "@openstatus/ui/components/ui/tabs"; import { Tabs } from "@openstatus/ui/components/ui/tabs"; import { Textarea } from "@openstatus/ui/components/ui/textarea"; import { cn } from "@openstatus/ui/lib/utils"; import { useQuery } from "@tanstack/react-query"; import { isTRPCClientError } from "@trpc/client"; import React, { useTransition } from "react"; import { useForm } from "react-hook-form"; import { toast } from "sonner"; import { z } from "zod"; import { DateTimePicker } from "@/components/common/date-time-picker"; import { ProcessMessage } from "@/components/content/process-message"; import { FormCardContent, FormCardSeparator, } from "@/components/forms/form-card"; import { useFormSheetDirty } from "@/components/forms/form-sheet"; import { ComponentImpactList } from "@/components/forms/status-report/component-impact-field"; import { colors } from "@/data/status-report-updates.client"; import { useTRPC } from "@/lib/trpc/client"; const schema = z.object({ status: z.enum(statusReportStatus), message: z.string(), date: z.date(), componentImpacts: z .array( z.object({ pageComponentId: z.number(), impact: z.enum(pageComponentImpact), }), ) .optional(), notifySubscribers: z.boolean().optional(), }); export type FormValues = z.infer; export function FormStatusReportUpdate({ defaultValues, onSubmit, className, components, allowUnsetImpacts = false, ...props }: Omit, "onSubmit"> & { defaultValues?: Partial; onSubmit: (values: FormValues) => Promise; /** The report's affected components; renders the per-component impact picker. */ components?: { id: number; name: string }[]; /** Edit mode: components without an entry show "No change". */ allowUnsetImpacts?: boolean; }) { const trpc = useTRPC(); const { data: workspace } = useQuery(trpc.workspace.get.queryOptions()); const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone; const form = useForm({ resolver: zodResolver(schema), defaultValues: { status: defaultValues?.status ?? "identified", message: defaultValues?.message ?? "", date: defaultValues?.date ?? new Date(), componentImpacts: defaultValues?.componentImpacts, notifySubscribers: defaultValues?.notifySubscribers ?? !!workspace?.limits["status-subscribers"], }, }); const watchMessage = form.watch("message"); const [isPending, startTransition] = useTransition(); const { setIsDirty } = useFormSheetDirty(); const formIsDirty = form.formState.isDirty; React.useEffect(() => { setIsDirty(formIsDirty); }, [formIsDirty, setIsDirty]); function submitAction(values: FormValues) { if (isPending) return; startTransition(async () => { try { const promise = onSubmit(values); toast.promise(promise, { loading: "Saving...", success: () => "Saved", error: (error) => { if (isTRPCClientError(error)) { return error.message; } return "Failed to save"; }, }); await promise; } catch (error) { console.error(error); } }); } return (
( Status )} /> ( Date When the status report was created. Shown in your timezone ( {timezone} ) and saved as Unix time ( UTC ). )} /> {components && components.length > 0 ? ( <> ( Component Impact How badly is each affected component impacted? )} /> ) : null} Writing Preview ( Message