Ship the delivery platform UI under modules/delivery with thin app routes, BFF proxy, CRUD for phase 10.0-10.1 APIs, and capability-gated future screens. Co-authored-by: Cursor <cursoragent@cursor.com>
74 lines
3.1 KiB
TypeScript
74 lines
3.1 KiB
TypeScript
"use client";
|
||
|
||
import { useState } from "react";
|
||
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
|
||
import { toast } from "sonner";
|
||
import { useTenantId } from "@/hooks/useTenantId";
|
||
import { deliveryApi } from "@/modules/delivery/services/delivery-api";
|
||
import { DeliveryPageLoader, DeliveryPageError } from "@/modules/delivery/pages/shared";
|
||
import { PageHeader, Card, CardContent, Button, Input, FormField } from "@/components/ds";
|
||
import { DeliveryBreadcrumbs } from "@/modules/delivery/components/DeliveryBreadcrumbs";
|
||
import { useDeliveryLookups } from "@/modules/delivery/hooks/useDeliveryCapabilities";
|
||
|
||
export const SettingsPage = function DeliverySettingsPage() {
|
||
const { tenantId } = useTenantId();
|
||
const qc = useQueryClient();
|
||
const lookups = useDeliveryLookups(tenantId);
|
||
const [key, setKey] = useState("");
|
||
const [value, setValue] = useState("");
|
||
const [orgId, setOrgId] = useState("");
|
||
|
||
const listQ = useQuery({
|
||
queryKey: ["delivery", tenantId, "settings"],
|
||
queryFn: () => deliveryApi.settings.list(tenantId!),
|
||
enabled: !!tenantId,
|
||
});
|
||
|
||
const saveM = useMutation({
|
||
mutationFn: () => deliveryApi.settings.upsert(tenantId!, {
|
||
organization_id: orgId || null,
|
||
key,
|
||
value: JSON.parse(value || "{}"),
|
||
}),
|
||
onSuccess: () => {
|
||
toast.success("تنظیمات ذخیره شد");
|
||
qc.invalidateQueries({ queryKey: ["delivery", tenantId, "settings"] });
|
||
},
|
||
onError: (e: Error) => toast.error(e.message),
|
||
});
|
||
|
||
if (!tenantId || listQ.isLoading || lookups.isLoading) return <DeliveryPageLoader />;
|
||
if (listQ.error) return <DeliveryPageError error={listQ.error} onRetry={() => listQ.refetch()} />;
|
||
|
||
return (
|
||
<div className="space-y-6">
|
||
<DeliveryBreadcrumbs current="تنظیمات" />
|
||
<PageHeader title="تنظیمات" description="کلید-مقدار tenant-scoped." />
|
||
<Card>
|
||
<CardContent className="space-y-4 p-4">
|
||
<FormField label="سازمان (اختیاری)">
|
||
<select className="w-full rounded-xl border px-3 py-2" value={orgId} onChange={(e) => setOrgId(e.target.value)}>
|
||
<option value="">—</option>
|
||
{lookups.data?.organizations.map((o) => (
|
||
<option key={String(o.id)} value={String(o.id)}>{String(o.name)}</option>
|
||
))}
|
||
</select>
|
||
</FormField>
|
||
<FormField label="کلید"><Input value={key} onChange={(e) => setKey(e.target.value)} /></FormField>
|
||
<FormField label="مقدار (JSON)"><Input value={value} onChange={(e) => setValue(e.target.value)} placeholder='{"enabled":true}' /></FormField>
|
||
<Button onClick={() => saveM.mutate()} loading={saveM.isPending}>ذخیره</Button>
|
||
</CardContent>
|
||
</Card>
|
||
<div className="grid gap-2">
|
||
{(listQ.data ?? []).map((s) => (
|
||
<Card key={String(s.id)}><CardContent className="p-3 text-sm">
|
||
<strong>{String(s.key)}</strong>
|
||
<pre className="mt-1 overflow-x-auto text-xs">{JSON.stringify(s.value, null, 2)}</pre>
|
||
</CardContent></Card>
|
||
))}
|
||
</div>
|
||
</div>
|
||
);
|
||
};
|
||
export default SettingsPage;
|