"use client"; import { Dispatch, SetStateAction, useEffect, useMemo, useState, useTransition, } from "react"; import { zodResolver } from "@hookform/resolvers/zod"; import { User } from "@prisma/client"; import { Sparkles } from "lucide-react"; import { useTranslations } from "next-intl"; import { useForm } from "react-hook-form"; import { toast } from "sonner"; import useSWR from "swr"; import { ShortUrlFormData } from "@/lib/dto/short-urls"; import { EXPIRATION_ENUMS } from "@/lib/enums"; import { fetcher, generateUrlSuffix } from "@/lib/utils"; import { createUrlSchema } from "@/lib/validations/url"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Icons } from "@/components/shared/icons"; import { FormSectionColumns } from "../dashboard/form-section-columns"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "../ui/select"; import { Skeleton } from "../ui/skeleton"; export type FormData = ShortUrlFormData; export type FormType = "add" | "edit"; export interface RecordFormProps { user: Pick; isShowForm: boolean; setShowForm: Dispatch>; type: FormType; initData?: ShortUrlFormData | null; action: string; onRefresh: (id?: string) => void; } export function UrlForm({ setShowForm, type, initData, action, onRefresh, }: RecordFormProps) { const [isPending, startTransition] = useTransition(); const [isDeleting, startDeleteTransition] = useTransition(); const [currentPrefix, setCurrentPrefix] = useState(initData?.prefix || ""); const [limitLen, setLimitLen] = useState(3); const t = useTranslations("List"); const { handleSubmit, register, formState: { errors }, setValue, } = useForm({ resolver: zodResolver(createUrlSchema), defaultValues: { id: initData?.id || "", target: initData?.target || "", url: initData?.url || "", active: initData?.active || 1, prefix: initData?.prefix || "", visible: initData?.visible || 0, expiration: initData?.expiration || "-1", password: initData?.password || "", }, }); const { data: shortDomains, isLoading } = useSWR< { domain_name: string; min_url_length: number }[] >("/api/domain?feature=short", fetcher, { revalidateOnFocus: false, dedupingInterval: 10000, }); const validDefaultDomain = useMemo(() => { if (!shortDomains?.length) return undefined; if ( initData?.prefix && shortDomains.some((d) => d.domain_name === initData.prefix) ) { return initData.prefix; } return shortDomains[0].domain_name; }, [shortDomains, initData?.prefix]); useEffect(() => { if (validDefaultDomain) { setValue("prefix", validDefaultDomain); setCurrentPrefix(validDefaultDomain); } }, [validDefaultDomain]); useEffect(() => { setLimitLen( shortDomains?.find((d) => d.domain_name === currentPrefix) ?.min_url_length || 3, ); }, [currentPrefix]); const onSubmit = handleSubmit((data) => { if (type === "add") { handleCreateUrl(data); } else if (type === "edit") { handleUpdateUrl(data); } }); const handleCreateUrl = async (data: ShortUrlFormData) => { if (data.password !== "" && data.password.length !== 6) { toast.error("Password must be 6 characters!"); return; } startTransition(async () => { const response = await fetch(`${action}/add`, { method: "POST", body: JSON.stringify({ data, }), }); if (!response.ok || response.status !== 200) { toast.error("Created Failed!", { description: await response.text(), }); } else { const res = await response.json(); toast.success(`Created successfully!`); setShowForm(false); onRefresh(res.id); } }); }; const handleUpdateUrl = async (data: ShortUrlFormData) => { if (data.password !== "" && data.password.length !== 6) { toast.error("Password must be 6 characters!"); return; } startTransition(async () => { if (type === "edit") { const response = await fetch(`${action}/update`, { method: "POST", body: JSON.stringify({ data, userId: initData?.userId }), }); if (!response.ok || response.status !== 200) { toast.error("Update Failed", { description: await response.text(), }); } else { const res = await response.json(); toast.success(`Update successfully!`); setShowForm(false); onRefresh(); } } }); }; const handleDeleteUrl = async () => { if (type === "edit") { startDeleteTransition(async () => { const response = await fetch(`${action}/delete`, { method: "POST", body: JSON.stringify({ url_id: initData?.id, userId: initData?.userId, }), }); if (!response.ok || response.status !== 200) { toast.error("Delete Failed", { description: await response.text(), }); } else { await response.json(); toast.success(`Success`); setShowForm(false); onRefresh(); } }); } }; return (
{type === "add" ? t("Create short link") : t("Edit short link")}
{errors?.target ? (

{errors.target.message}

) : (

{t("Required")}. https://your-origin-url

)}
{isLoading ? ( ) : ( )}
{errors?.url ? (

{errors.url.message}

) : (

{t("A random url suffix")}. {t("Final url like")} 「wr.do/s/suffix」

)}
{errors?.password ? (

{errors.password.message}

) : (

{t("Optional")}. {t("If you want to protect your link")}.

)}

{t("Expiration time, default for never")}.

{/* Action buttons */}
{type === "edit" && ( )}
); }