Commit completed Torbat Pages admin frontend module, BFF proxy, routes, and validation artifacts for official platform deploy. Co-authored-by: Cursor <cursoragent@cursor.com>
90 lines
3.5 KiB
TypeScript
90 lines
3.5 KiB
TypeScript
"use client";
|
||
|
||
import { useParams } from "next/navigation";
|
||
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
||
import { toast } from "sonner";
|
||
import { useTenantId } from "@/hooks/useTenantId";
|
||
import { experienceApi } from "@/modules/experience/services/experience-api";
|
||
import { ExperiencePageLoader, ExperiencePageError } from "@/modules/experience/pages/shared";
|
||
import { ExperienceDetailBreadcrumbs } from "@/modules/experience/components/ExperienceBreadcrumbs";
|
||
import { PageHeader, Button, Card, CardContent, Input, FormField } from "@/components/ds";
|
||
|
||
export const FormBuilderPage = function ExperienceFormBuilder() {
|
||
const params = useParams();
|
||
const formId = String(params.id);
|
||
const { tenantId } = useTenantId();
|
||
const qc = useQueryClient();
|
||
|
||
const formQ = useQuery({
|
||
queryKey: ["experience", tenantId, "form", formId],
|
||
queryFn: () => experienceApi.forms.get(tenantId!, formId),
|
||
enabled: !!tenantId && !!formId,
|
||
});
|
||
|
||
const saveM = useMutation({
|
||
mutationFn: (schema: string) =>
|
||
experienceApi.forms.update(tenantId!, formId, {
|
||
form_schema_json: JSON.parse(schema),
|
||
version: formQ.data?.version,
|
||
}),
|
||
onSuccess: () => {
|
||
toast.success("اسکیمای فرم ذخیره شد");
|
||
qc.invalidateQueries({ queryKey: ["experience", tenantId, "form", formId] });
|
||
},
|
||
onError: (e: Error) => toast.error(e.message),
|
||
});
|
||
|
||
if (!tenantId || formQ.isLoading) return <ExperiencePageLoader />;
|
||
if (formQ.error) return <ExperiencePageError error={formQ.error} onRetry={() => formQ.refetch()} />;
|
||
|
||
const form = formQ.data!;
|
||
const schemaStr = JSON.stringify(form.form_schema_json ?? { fields: [] }, null, 2);
|
||
|
||
return (
|
||
<div className="space-y-4">
|
||
<ExperienceDetailBreadcrumbs parent="فرمها" parentHref="/experience/forms" current={String(form.name ?? form.code)} />
|
||
<PageHeader title={`سازنده فرم: ${String(form.name ?? form.code)}`} description="ویرایش form_schema_json" />
|
||
<Card>
|
||
<CardContent className="space-y-4 p-4">
|
||
<FormField label="اسکیمای JSON">
|
||
<textarea
|
||
id="form-schema"
|
||
className="min-h-[320px] w-full rounded-xl border p-3 font-mono text-xs"
|
||
defaultValue={schemaStr}
|
||
/>
|
||
</FormField>
|
||
<p className="text-xs text-[var(--muted)]">
|
||
اعتبارسنجی: هر فیلد باید name و type داشته باشد. required اختیاری است.
|
||
</p>
|
||
<Button
|
||
onClick={() => {
|
||
const el = document.getElementById("form-schema") as HTMLTextAreaElement;
|
||
try {
|
||
const parsed = JSON.parse(el.value);
|
||
if (!parsed.fields || !Array.isArray(parsed.fields)) {
|
||
toast.error("fields باید آرایه باشد");
|
||
return;
|
||
}
|
||
for (const f of parsed.fields) {
|
||
if (!f.name || !f.type) {
|
||
toast.error("هر فیلد name و type لازم دارد");
|
||
return;
|
||
}
|
||
}
|
||
saveM.mutate(el.value);
|
||
} catch {
|
||
toast.error("JSON نامعتبر");
|
||
}
|
||
}}
|
||
loading={saveM.isPending}
|
||
>
|
||
ذخیره اسکیما
|
||
</Button>
|
||
</CardContent>
|
||
</Card>
|
||
</div>
|
||
);
|
||
};
|
||
|
||
export default FormBuilderPage;
|