import { Popover } from "@/comps/custom/Popover"; import { Input } from "@/comps/ui/input"; import { Skeleton } from "@/comps/ui/skeleton"; import { useLocal } from "@/utils/use-local"; import { ChevronDown, Loader2 } from "lucide-react"; import { FC, useEffect } from "react"; import { FieldListItem, FieldOptions } from "../type"; import { FormHook } from "../utils/utils"; type RelationProps = { value: string; relation: { table: string; fields: string[]; query: () => Promise; }; form?: FormHook; name: string; }; export const Relation: FC = ({ relation, value, form, name, }) => { const local = useLocal({ status: "init" as "init" | "loading" | "ready", open: false, ref: { input: null as null | HTMLInputElement }, list: [] as FieldListItem[], input: "", label: "", filter: "", pk_field: "", timeout: null as any, }); useEffect(() => { clearTimeout(local.timeout); local.timeout = setTimeout(async () => { if (form) { local.status = "loading"; local.render(); if (form.cache[name]) { local.pk_field = form.cache[name].pk_field; local.list = form.cache[name].list; } else { const table_fn = (db as any)[relation.table]; const select = {} as any; local.pk_field = ""; for (const f of relation.fields) { if (typeof f === "string") { if (f.startsWith("::")) { select[f.substring(2)] = true; local.pk_field = f.substring(2); } else { select[f] = true; } } } let q = {}; if (typeof relation.query === "function") { q = await relation.query(); } const list = await table_fn.findMany({ select, ...q }); if (Array.isArray(list)) { local.list = list.map((item: any) => { let label = []; for (const [k, v] of Object.entries(item)) { if (k !== local.pk_field) label.push(v); } return { value: item[local.pk_field], label: label.join(" - ") }; }); } form.cache[name] = { list: local.list, pk_field: local.pk_field }; } const found = local.list.find((e) => { if (typeof value === "object") { if (value["connect"]) { return e.value === value["connect"][local.pk_field]; } return e.value === value[local.pk_field]; } else { return e.value === value; } }); if (found) { local.label = found.label; } local.status = "ready"; local.render(); } }, 100); }, [relation, location.hash, location.pathname, value]); let filtered = local.list; if (local.filter) { filtered = local.list.filter((e) => { if (e.label.toLowerCase().includes(local.filter)) return true; return false; }); } return ( { local.open = false; local.render(); }} arrow={false} className={cx("c-rounded-sm c-bg-white")} content={
{local.status === "loading" && ( <>
)} {local.status === "ready" && (
{filtered.map((item, idx) => { let is_active = false; if (typeof value === "object") { const c = (value as any)?.connect; if (c) { is_active = item.value === c[local.pk_field]; } } else { is_active = item.value === value; } return (
0 && "c-border-t", idx === 0 && "c-rounded-t-sm", idx === local.list.length - 1 && "c-rounded-b-sm" )} onClick={() => { if (form) { local.open = false; form.hook.setValue(name, { connect: { [local.pk_field]: item.value }, }); form.render(); } }} > {item.label || "-"}
); })}
)}
} >
{ local.open = true; local.input = local.label; local.filter = ""; local.render(); setTimeout(() => { local.ref.input?.focus(); }); }} >
{ local.input = e.currentTarget.value; local.filter = local.input.toLowerCase(); local.render(); }} ref={(el) => { local.ref.input = el; }} type="text" /> {!local.open && (
{local.status !== "ready" ? ( ) : ( <> {local.label || "-"} )}
)}
); };