Scaffold frontend/modules/loyalty with types, API client, design system, feature pages, and thin App Router routes. Wire all screens to backend Loyalty service via BFF proxy. Add loyalty docs and update progress. Co-authored-by: Cursor <cursoragent@cursor.com>
1301 lines
47 KiB
TypeScript
1301 lines
47 KiB
TypeScript
"use client";
|
|
|
|
import type { HospitalityListConfig } from "@/modules/hospitality/components/HospitalityListCrudPage";
|
|
import { hospitalityApi } from "@/modules/hospitality/services/hospitality-api";
|
|
import {
|
|
auditColumns,
|
|
codeNameCreateFields,
|
|
codeNameStatusColumns,
|
|
lifecycleStatusOptions,
|
|
venueIdField,
|
|
} from "@/modules/hospitality/constants/fieldHelpers";
|
|
import { venueFormatLabels } from "@/modules/hospitality/design-system/tokens";
|
|
|
|
const venueFormatOptions = Object.entries(venueFormatLabels).map(([value, label]) => ({
|
|
value,
|
|
label,
|
|
}));
|
|
|
|
const menuStatusOptions = [
|
|
{ value: "draft", label: "پیشنویس" },
|
|
{ value: "published", label: "منتشر شده" },
|
|
{ value: "archived", label: "آرشیو" },
|
|
];
|
|
|
|
const tableStatusOptions = [
|
|
{ value: "available", label: "آزاد" },
|
|
{ value: "occupied", label: "اشغال" },
|
|
{ value: "reserved", label: "رزرو" },
|
|
{ value: "cleaning", label: "نظافت" },
|
|
{ value: "out_of_service", label: "خارج از سرویس" },
|
|
];
|
|
|
|
const posPaymentMethodOptions = [
|
|
{ value: "cash", label: "نقد" },
|
|
{ value: "card", label: "کارت" },
|
|
{ value: "other", label: "سایر" },
|
|
];
|
|
|
|
const posDiscountKindOptions = [
|
|
{ value: "percent", label: "درصدی" },
|
|
{ value: "fixed", label: "مبلغ ثابت" },
|
|
];
|
|
|
|
const connectorKindFilter = (kind: string) => (row: Record<string, unknown>) =>
|
|
String(row.kind) === kind;
|
|
|
|
const connectorColumns = [
|
|
{ key: "code", header: "کد" },
|
|
{ key: "name", header: "نام" },
|
|
{ key: "kind", header: "نوع" },
|
|
{ key: "external_ref", header: "ارجاع خارجی" },
|
|
{ key: "status", header: "وضعیت", type: "status" as const },
|
|
...auditColumns(),
|
|
];
|
|
|
|
const connectorCreateFields = (kind: string, kindLabel: string) =>
|
|
codeNameCreateFields([
|
|
{
|
|
name: "kind",
|
|
label: "نوع کانکتور",
|
|
type: "select",
|
|
required: true,
|
|
options: [{ value: kind, label: kindLabel }],
|
|
},
|
|
venueIdField(false),
|
|
]);
|
|
|
|
export const hospitalityResources = {
|
|
venues: {
|
|
title: "مکانها (Venues)",
|
|
description: "مدیریت مکانهای مهماننوازی — کافه، رستوران، فستفود و …",
|
|
breadcrumb: "مکانها",
|
|
resourceKey: "venues",
|
|
bundleFeature: null,
|
|
permission: "hospitality.venues.view",
|
|
api: hospitalityApi.venues,
|
|
columns: [
|
|
{ key: "code", header: "کد" },
|
|
{ key: "name", header: "نام" },
|
|
{
|
|
key: "venue_format",
|
|
header: "نوع",
|
|
render: (r) => venueFormatLabels[String(r.venue_format)] ?? String(r.venue_format),
|
|
},
|
|
{ key: "status", header: "وضعیت", type: "status" },
|
|
{ key: "currency_code", header: "ارز" },
|
|
...auditColumns(),
|
|
],
|
|
createFields: [
|
|
{ name: "code", label: "کد", required: true },
|
|
{ name: "name", label: "نام", required: true },
|
|
{
|
|
name: "venue_format",
|
|
label: "نوع کسبوکار",
|
|
type: "select",
|
|
required: true,
|
|
options: venueFormatOptions,
|
|
},
|
|
],
|
|
editFields: [
|
|
{ name: "name", label: "نام", required: true },
|
|
{
|
|
name: "status",
|
|
label: "وضعیت",
|
|
type: "select",
|
|
options: lifecycleStatusOptions,
|
|
},
|
|
],
|
|
filterDeleted: true,
|
|
} satisfies HospitalityListConfig,
|
|
|
|
branches: {
|
|
title: "شعب",
|
|
breadcrumb: "شعب",
|
|
resourceKey: "branches",
|
|
bundleFeature: null,
|
|
permission: "hospitality.view",
|
|
api: hospitalityApi.branches,
|
|
columns: codeNameStatusColumns([{ key: "venue_id", header: "مکان" }]),
|
|
createFields: codeNameCreateFields([venueIdField(), { name: "timezone", label: "منطقه زمانی" }]),
|
|
editFields: [
|
|
{ name: "name", label: "نام", required: true },
|
|
{ name: "status", label: "وضعیت", type: "select", options: lifecycleStatusOptions },
|
|
],
|
|
filterDeleted: true,
|
|
} satisfies HospitalityListConfig,
|
|
|
|
diningAreas: {
|
|
title: "سالنها",
|
|
breadcrumb: "سالنها",
|
|
resourceKey: "diningAreas",
|
|
bundleFeature: null,
|
|
permission: "hospitality.view",
|
|
api: hospitalityApi.diningAreas,
|
|
columns: codeNameStatusColumns([
|
|
{ key: "venue_id", header: "مکان" },
|
|
{ key: "sort_order", header: "ترتیب", type: "text" },
|
|
]),
|
|
createFields: codeNameCreateFields([venueIdField(), { name: "sort_order", label: "ترتیب", type: "number" }]),
|
|
filterDeleted: true,
|
|
} satisfies HospitalityListConfig,
|
|
|
|
tables: {
|
|
title: "میزها",
|
|
breadcrumb: "میزها",
|
|
resourceKey: "tables",
|
|
bundleFeature: null,
|
|
permission: "hospitality.view",
|
|
api: hospitalityApi.tables,
|
|
columns: [
|
|
{ key: "code", header: "کد" },
|
|
{ key: "name", header: "نام" },
|
|
{ key: "capacity", header: "ظرفیت" },
|
|
{ key: "status", header: "وضعیت", type: "status" },
|
|
{ key: "dining_area_id", header: "سالن" },
|
|
{ key: "qr_token", header: "توکن QR", defaultVisible: false },
|
|
...auditColumns(),
|
|
],
|
|
createFields: codeNameCreateFields([
|
|
venueIdField(),
|
|
{ name: "capacity", label: "ظرفیت", type: "number", required: true },
|
|
{ name: "dining_area_id", label: "شناسه سالن" },
|
|
{
|
|
name: "status",
|
|
label: "وضعیت",
|
|
type: "select",
|
|
options: tableStatusOptions,
|
|
},
|
|
]),
|
|
filterDeleted: true,
|
|
} satisfies HospitalityListConfig,
|
|
|
|
posFloorPlans: {
|
|
title: "نقشه سالن",
|
|
breadcrumb: "نقشه سالن",
|
|
resourceKey: "posFloorPlans",
|
|
bundleFeature: "pos_pro",
|
|
permission: "hospitality.view",
|
|
api: hospitalityApi.posFloorPlans,
|
|
columns: codeNameStatusColumns([
|
|
{ key: "venue_id", header: "مکان" },
|
|
{ key: "dining_area_id", header: "سالن" },
|
|
]),
|
|
createFields: codeNameCreateFields([
|
|
venueIdField(),
|
|
{ name: "dining_area_id", label: "شناسه سالن" },
|
|
]),
|
|
filterDeleted: true,
|
|
} satisfies HospitalityListConfig,
|
|
|
|
reservations: {
|
|
title: "رزروها",
|
|
breadcrumb: "رزروها",
|
|
resourceKey: "reservations",
|
|
bundleFeature: "reservation",
|
|
permission: "hospitality.view",
|
|
api: hospitalityApi.reservations,
|
|
columns: [
|
|
{ key: "code", header: "کد" },
|
|
{ key: "guest_name", header: "مهمان", searchable: true },
|
|
{ key: "guest_phone", header: "تلفن", defaultVisible: false },
|
|
{ key: "party_size", header: "تعداد نفر" },
|
|
{ key: "reserved_from", header: "از", type: "datetime" },
|
|
{ key: "reserved_to", header: "تا", type: "datetime" },
|
|
{ key: "status", header: "وضعیت", type: "status" },
|
|
{ key: "table_id", header: "میز", defaultVisible: false },
|
|
...auditColumns(),
|
|
],
|
|
createFields: [
|
|
venueIdField(),
|
|
{ name: "code", label: "کد", required: true },
|
|
{ name: "guest_name", label: "نام مهمان", required: true },
|
|
{ name: "guest_phone", label: "تلفن", type: "tel" },
|
|
{ name: "party_size", label: "تعداد نفر", type: "number", required: true },
|
|
{ name: "reserved_from", label: "شروع رزرو", type: "datetime-local", required: true },
|
|
{ name: "reserved_to", label: "پایان رزرو", type: "datetime-local", required: true },
|
|
{ name: "notes", label: "یادداشت", type: "textarea" },
|
|
],
|
|
rowActions: [
|
|
{
|
|
label: "تأیید",
|
|
onClick: async (tenantId, row) => {
|
|
await hospitalityApi.reservations.updateStatus(tenantId, String(row.id), { status: "confirmed" });
|
|
},
|
|
},
|
|
],
|
|
filterDeleted: true,
|
|
} satisfies HospitalityListConfig,
|
|
|
|
waitlist: {
|
|
title: "صف انتظار",
|
|
breadcrumb: "صف انتظار",
|
|
resourceKey: "waitlist",
|
|
bundleFeature: "table_service",
|
|
permission: "hospitality.view",
|
|
api: hospitalityApi.waitlist,
|
|
columns: [
|
|
{ key: "code", header: "کد" },
|
|
{ key: "guest_name", header: "مهمان", searchable: true },
|
|
{ key: "party_size", header: "تعداد نفر" },
|
|
{ key: "phone", header: "تلفن" },
|
|
{ key: "status", header: "وضعیت", type: "status" },
|
|
...auditColumns(),
|
|
],
|
|
createFields: [
|
|
venueIdField(),
|
|
{ name: "code", label: "کد", required: true },
|
|
{ name: "guest_name", label: "نام مهمان", required: true },
|
|
{ name: "party_size", label: "تعداد نفر", type: "number", required: true },
|
|
{ name: "phone", label: "تلفن", type: "tel" },
|
|
{ name: "notes", label: "یادداشت", type: "textarea" },
|
|
],
|
|
rowActions: [
|
|
{
|
|
label: "نشست",
|
|
onClick: async (tenantId, row) => {
|
|
await hospitalityApi.waitlist.updateStatus(tenantId, String(row.id), { status: "seated" });
|
|
},
|
|
},
|
|
],
|
|
filterDeleted: true,
|
|
} satisfies HospitalityListConfig,
|
|
|
|
menus: {
|
|
title: "منوی دیجیتال",
|
|
breadcrumb: "منوی دیجیتال",
|
|
resourceKey: "menus",
|
|
bundleFeature: "digital_menu",
|
|
permission: "hospitality.view",
|
|
api: hospitalityApi.menus,
|
|
columns: [
|
|
{ key: "code", header: "کد" },
|
|
{ key: "name", header: "نام" },
|
|
{ key: "status", header: "وضعیت", type: "status" },
|
|
{ key: "currency_code", header: "ارز" },
|
|
{ key: "is_default", header: "پیشفرض", render: (r) => (r.is_default ? "بله" : "خیر") },
|
|
...auditColumns(),
|
|
],
|
|
createFields: codeNameCreateFields([
|
|
venueIdField(),
|
|
{ name: "currency_code", label: "ارز", required: true },
|
|
{ name: "is_default", label: "پیشفرض (true/false)" },
|
|
]),
|
|
editFields: [
|
|
{ name: "name", label: "نام", required: true },
|
|
{ name: "status", label: "وضعیت", type: "select", options: menuStatusOptions },
|
|
],
|
|
filterDeleted: true,
|
|
} satisfies HospitalityListConfig,
|
|
|
|
menuCategories: {
|
|
title: "دستهبندی منو",
|
|
breadcrumb: "دستهبندی منو",
|
|
resourceKey: "menuCategories",
|
|
bundleFeature: "digital_menu",
|
|
permission: "hospitality.view",
|
|
api: hospitalityApi.menuCategories,
|
|
columns: [
|
|
{ key: "code", header: "کد" },
|
|
{ key: "name", header: "نام" },
|
|
{ key: "menu_id", header: "منو" },
|
|
{ key: "sort_order", header: "ترتیب" },
|
|
{ key: "status", header: "وضعیت", type: "status" },
|
|
...auditColumns(),
|
|
],
|
|
createFields: codeNameCreateFields([
|
|
venueIdField(),
|
|
{ name: "menu_id", label: "شناسه منو", required: true },
|
|
{ name: "sort_order", label: "ترتیب", type: "number" },
|
|
]),
|
|
filterDeleted: true,
|
|
} satisfies HospitalityListConfig,
|
|
|
|
menuItems: {
|
|
title: "آیتمهای منو",
|
|
breadcrumb: "آیتمهای منو",
|
|
resourceKey: "menuItems",
|
|
bundleFeature: "digital_menu",
|
|
permission: "hospitality.view",
|
|
api: hospitalityApi.menuItems,
|
|
columns: [
|
|
{ key: "code", header: "کد" },
|
|
{ key: "name", header: "نام", searchable: true },
|
|
{ key: "base_price", header: "قیمت پایه", type: "money" },
|
|
{ key: "is_available", header: "موجود", render: (r) => (r.is_available ? "بله" : "خیر") },
|
|
{ key: "status", header: "وضعیت", type: "status" },
|
|
{ key: "menu_id", header: "منو", defaultVisible: false },
|
|
{ key: "category_id", header: "دسته", defaultVisible: false },
|
|
...auditColumns(),
|
|
],
|
|
createFields: codeNameCreateFields([
|
|
venueIdField(),
|
|
{ name: "menu_id", label: "شناسه منو", required: true },
|
|
{ name: "category_id", label: "شناسه دسته" },
|
|
{ name: "base_price", label: "قیمت پایه", type: "number", required: true },
|
|
{ name: "is_available", label: "موجود (true/false)" },
|
|
]),
|
|
filterDeleted: true,
|
|
} satisfies HospitalityListConfig,
|
|
|
|
modifierGroups: {
|
|
title: "گروه افزودنی",
|
|
breadcrumb: "گروه افزودنی",
|
|
resourceKey: "modifierGroups",
|
|
bundleFeature: "digital_menu",
|
|
permission: "hospitality.view",
|
|
api: hospitalityApi.modifierGroups,
|
|
columns: codeNameStatusColumns([
|
|
{ key: "selection_mode", header: "حالت انتخاب" },
|
|
{ key: "min_select", header: "حداقل" },
|
|
{ key: "max_select", header: "حداکثر" },
|
|
{ key: "is_required", header: "اجباری", render: (r) => (r.is_required ? "بله" : "خیر") },
|
|
]),
|
|
createFields: codeNameCreateFields([
|
|
venueIdField(),
|
|
{ name: "selection_mode", label: "حالت انتخاب", required: true },
|
|
{ name: "min_select", label: "حداقل انتخاب", type: "number" },
|
|
{ name: "max_select", label: "حداکثر انتخاب", type: "number" },
|
|
{ name: "is_required", label: "اجباری (true/false)" },
|
|
]),
|
|
filterDeleted: true,
|
|
} satisfies HospitalityListConfig,
|
|
|
|
modifierOptions: {
|
|
title: "گزینه افزودنی",
|
|
breadcrumb: "گزینه افزودنی",
|
|
resourceKey: "modifierOptions",
|
|
bundleFeature: "digital_menu",
|
|
permission: "hospitality.view",
|
|
api: hospitalityApi.modifierOptions,
|
|
columns: [
|
|
{ key: "code", header: "کد" },
|
|
{ key: "name", header: "نام" },
|
|
{ key: "modifier_group_id", header: "گروه" },
|
|
{ key: "price_delta", header: "تغییر قیمت", type: "money" },
|
|
{ key: "is_default", header: "پیشفرض", render: (r) => (r.is_default ? "بله" : "خیر") },
|
|
{ key: "status", header: "وضعیت", type: "status" },
|
|
...auditColumns(),
|
|
],
|
|
createFields: [
|
|
venueIdField(),
|
|
{ name: "modifier_group_id", label: "شناسه گروه", required: true },
|
|
{ name: "code", label: "کد", required: true },
|
|
{ name: "name", label: "نام", required: true },
|
|
{ name: "price_delta", label: "تغییر قیمت", type: "number" },
|
|
{ name: "currency_code", label: "ارز" },
|
|
],
|
|
filterDeleted: true,
|
|
} satisfies HospitalityListConfig,
|
|
|
|
availabilityWindows: {
|
|
title: "قوانین دسترسی",
|
|
breadcrumb: "قوانین دسترسی",
|
|
resourceKey: "availabilityWindows",
|
|
bundleFeature: "digital_menu",
|
|
permission: "hospitality.view",
|
|
api: hospitalityApi.availabilityWindows,
|
|
columns: [
|
|
{ key: "code", header: "کد" },
|
|
{ key: "name", header: "نام" },
|
|
{ key: "scope", header: "دامنه" },
|
|
{ key: "target_id", header: "هدف" },
|
|
{ key: "weekday", header: "روز هفته" },
|
|
{ key: "start_time", header: "شروع" },
|
|
{ key: "end_time", header: "پایان" },
|
|
{ key: "status", header: "وضعیت", type: "status" },
|
|
...auditColumns(),
|
|
],
|
|
createFields: codeNameCreateFields([
|
|
venueIdField(),
|
|
{ name: "scope", label: "دامنه", required: true },
|
|
{ name: "target_id", label: "شناسه هدف", required: true },
|
|
{ name: "weekday", label: "روز هفته (0-6)", type: "number" },
|
|
{ name: "start_time", label: "ساعت شروع", required: true },
|
|
{ name: "end_time", label: "ساعت پایان", required: true },
|
|
]),
|
|
filterDeleted: true,
|
|
} satisfies HospitalityListConfig,
|
|
|
|
menuMediaRefs: {
|
|
title: "کتابخانه رسانه",
|
|
breadcrumb: "کتابخانه رسانه",
|
|
resourceKey: "menuMediaRefs",
|
|
bundleFeature: "digital_menu",
|
|
permission: "hospitality.view",
|
|
api: hospitalityApi.menuMediaRefs,
|
|
columns: [
|
|
{ key: "kind", header: "نوع رسانه" },
|
|
{ key: "file_ref", header: "فایل" },
|
|
{ key: "alt_text", header: "متن جایگزین" },
|
|
{ key: "menu_id", header: "منو", defaultVisible: false },
|
|
{ key: "menu_item_id", header: "آیتم", defaultVisible: false },
|
|
{ key: "sort_order", header: "ترتیب" },
|
|
{ key: "status", header: "وضعیت", type: "status" },
|
|
...auditColumns(),
|
|
],
|
|
createFields: [
|
|
venueIdField(),
|
|
{ name: "kind", label: "نوع رسانه", required: true },
|
|
{ name: "file_ref", label: "مرجع فایل", required: true },
|
|
{ name: "alt_text", label: "متن جایگزین" },
|
|
{ name: "menu_id", label: "شناسه منو" },
|
|
{ name: "menu_item_id", label: "شناسه آیتم" },
|
|
{ name: "sort_order", label: "ترتیب", type: "number" },
|
|
],
|
|
filterDeleted: true,
|
|
} satisfies HospitalityListConfig,
|
|
|
|
qrCodes: {
|
|
title: "منوی QR",
|
|
breadcrumb: "منوی QR",
|
|
resourceKey: "qrCodes",
|
|
bundleFeature: "qr_menu",
|
|
permission: "hospitality.view",
|
|
api: hospitalityApi.qrCodes,
|
|
columns: [
|
|
{ key: "code", header: "کد" },
|
|
{ key: "name", header: "نام" },
|
|
{ key: "token", header: "توکن" },
|
|
{ key: "target_type", header: "نوع هدف" },
|
|
{ key: "ordering_enabled", header: "سفارش", render: (r) => (r.ordering_enabled ? "فعال" : "غیرفعال") },
|
|
{ key: "status", header: "وضعیت", type: "status" },
|
|
...auditColumns(),
|
|
],
|
|
createFields: codeNameCreateFields([
|
|
venueIdField(),
|
|
{ name: "target_type", label: "نوع هدف", required: true },
|
|
{ name: "menu_id", label: "شناسه منو" },
|
|
{ name: "table_id", label: "شناسه میز" },
|
|
{ name: "ordering_enabled", label: "سفارش فعال (true/false)" },
|
|
]),
|
|
filterDeleted: true,
|
|
} satisfies HospitalityListConfig,
|
|
|
|
qrMenuSessions: {
|
|
title: "پروفایل مهمان",
|
|
breadcrumb: "پروفایل مهمان",
|
|
resourceKey: "qrMenuSessions",
|
|
bundleFeature: "qr_menu",
|
|
permission: "hospitality.view",
|
|
api: hospitalityApi.qrMenuSessions,
|
|
columns: [
|
|
{ key: "session_token", header: "توکن نشست" },
|
|
{ key: "guest_label", header: "مهمان" },
|
|
{ key: "locale", header: "زبان" },
|
|
{ key: "status", header: "وضعیت", type: "status" },
|
|
{ key: "qr_code_id", header: "QR", defaultVisible: false },
|
|
{ key: "table_id", header: "میز", defaultVisible: false },
|
|
...auditColumns(),
|
|
],
|
|
createFields: [
|
|
venueIdField(),
|
|
{ name: "qr_code_id", label: "شناسه QR", required: true },
|
|
{ name: "guest_label", label: "برچسب مهمان" },
|
|
{ name: "locale", label: "زبان" },
|
|
{ name: "menu_id", label: "شناسه منو" },
|
|
{ name: "table_id", label: "شناسه میز" },
|
|
],
|
|
filterDeleted: true,
|
|
} satisfies HospitalityListConfig,
|
|
|
|
qrOrderingSessions: {
|
|
title: "سفارش QR",
|
|
breadcrumb: "سفارش QR",
|
|
resourceKey: "qrOrderingSessions",
|
|
bundleFeature: "qr_ordering",
|
|
permission: "hospitality.view",
|
|
api: hospitalityApi.qrOrderingSessions,
|
|
columns: [
|
|
{ key: "session_token", header: "توکن نشست" },
|
|
{ key: "guest_label", header: "مهمان" },
|
|
{ key: "status", header: "وضعیت", type: "status" },
|
|
{ key: "qr_code_id", header: "QR", defaultVisible: false },
|
|
{ key: "table_id", header: "میز" },
|
|
...auditColumns(),
|
|
],
|
|
createFields: [
|
|
venueIdField(),
|
|
{ name: "qr_code_id", label: "شناسه QR", required: true },
|
|
{ name: "guest_label", label: "برچسب مهمان" },
|
|
{ name: "notes", label: "یادداشت", type: "textarea" },
|
|
],
|
|
rowActions: [
|
|
{
|
|
label: "ثبت سفارش",
|
|
onClick: async (tenantId, row) => {
|
|
await hospitalityApi.qrOrderingSessions.submit(tenantId, String(row.id));
|
|
},
|
|
},
|
|
],
|
|
filterDeleted: true,
|
|
} satisfies HospitalityListConfig,
|
|
|
|
carts: {
|
|
title: "سبد خرید",
|
|
breadcrumb: "سبد خرید",
|
|
resourceKey: "carts",
|
|
bundleFeature: "qr_ordering",
|
|
permission: "hospitality.view",
|
|
api: hospitalityApi.carts,
|
|
columns: [
|
|
{ key: "ordering_session_id", header: "نشست سفارش" },
|
|
{ key: "status", header: "وضعیت", type: "status" },
|
|
{ key: "currency_code", header: "ارز" },
|
|
{ key: "notes", header: "یادداشت", defaultVisible: false },
|
|
...auditColumns(),
|
|
],
|
|
createFields: [
|
|
venueIdField(),
|
|
{ name: "ordering_session_id", label: "شناسه نشست سفارش", required: true },
|
|
{ name: "currency_code", label: "ارز" },
|
|
{ name: "notes", label: "یادداشت", type: "textarea" },
|
|
],
|
|
filterDeleted: true,
|
|
} satisfies HospitalityListConfig,
|
|
|
|
cartLines: {
|
|
title: "تسویه",
|
|
breadcrumb: "تسویه",
|
|
resourceKey: "cartLines",
|
|
bundleFeature: "qr_ordering",
|
|
permission: "hospitality.view",
|
|
api: hospitalityApi.cartLines,
|
|
columns: [
|
|
{ key: "cart_id", header: "سبد" },
|
|
{ key: "menu_item_id", header: "آیتم" },
|
|
{ key: "quantity", header: "تعداد" },
|
|
{ key: "unit_price", header: "قیمت واحد", type: "money" },
|
|
{ key: "line_notes", header: "یادداشت", defaultVisible: false },
|
|
...auditColumns(),
|
|
],
|
|
createFields: [
|
|
venueIdField(),
|
|
{ name: "cart_id", label: "شناسه سبد", required: true },
|
|
{ name: "menu_item_id", label: "شناسه آیتم", required: true },
|
|
{ name: "quantity", label: "تعداد", type: "number", required: true },
|
|
{ name: "line_notes", label: "یادداشت خط", type: "textarea" },
|
|
],
|
|
filterDeleted: true,
|
|
} satisfies HospitalityListConfig,
|
|
|
|
kitchenTickets: {
|
|
title: "نمایشگر آشپزخانه",
|
|
breadcrumb: "نمایشگر آشپزخانه",
|
|
resourceKey: "kitchenTickets",
|
|
bundleFeature: "kitchen",
|
|
permission: "hospitality.view",
|
|
api: hospitalityApi.kitchenTickets,
|
|
refreshIntervalMs: 5000,
|
|
columns: [
|
|
{ key: "code", header: "کد" },
|
|
{ key: "source_type", header: "منبع" },
|
|
{ key: "source_id", header: "شناسه منبع", defaultVisible: false },
|
|
{ key: "status", header: "وضعیت", type: "status" },
|
|
{ key: "station_id", header: "ایستگاه", defaultVisible: false },
|
|
...auditColumns(),
|
|
],
|
|
createFields: [
|
|
venueIdField(),
|
|
{ name: "code", label: "کد", required: true },
|
|
{ name: "source_type", label: "نوع منبع", required: true },
|
|
{ name: "source_id", label: "شناسه منبع", required: true },
|
|
{ name: "station_id", label: "شناسه ایستگاه" },
|
|
{ name: "notes", label: "یادداشت", type: "textarea" },
|
|
],
|
|
rowActions: [
|
|
{
|
|
label: "آماده",
|
|
onClick: async (tenantId, row) => {
|
|
await hospitalityApi.kitchenTickets.updateStatus(tenantId, String(row.id), { status: "ready" });
|
|
},
|
|
},
|
|
{
|
|
label: "تکمیل",
|
|
onClick: async (tenantId, row) => {
|
|
await hospitalityApi.kitchenTickets.updateStatus(tenantId, String(row.id), { status: "bumped" });
|
|
},
|
|
},
|
|
],
|
|
filterDeleted: true,
|
|
} satisfies HospitalityListConfig,
|
|
|
|
kitchenTicketItems: {
|
|
title: "صف آشپزخانه",
|
|
breadcrumb: "صف آشپزخانه",
|
|
resourceKey: "kitchenTicketItems",
|
|
bundleFeature: "kitchen",
|
|
permission: "hospitality.view",
|
|
api: hospitalityApi.kitchenTicketItems,
|
|
refreshIntervalMs: 5000,
|
|
columns: [
|
|
{ key: "kitchen_ticket_id", header: "تیکت" },
|
|
{ key: "menu_item_id", header: "آیتم" },
|
|
{ key: "quantity", header: "تعداد" },
|
|
{ key: "status", header: "وضعیت", type: "status" },
|
|
{ key: "sort_order", header: "ترتیب", defaultVisible: false },
|
|
...auditColumns(),
|
|
],
|
|
createFields: [
|
|
venueIdField(),
|
|
{ name: "kitchen_ticket_id", label: "شناسه تیکت", required: true },
|
|
{ name: "menu_item_id", label: "شناسه آیتم", required: true },
|
|
{ name: "quantity", label: "تعداد", type: "number", required: true },
|
|
],
|
|
rowActions: [
|
|
{
|
|
label: "آماده",
|
|
onClick: async (tenantId, row) => {
|
|
await hospitalityApi.kitchenTicketItems.updateStatus(tenantId, String(row.id), { status: "ready" });
|
|
},
|
|
},
|
|
],
|
|
filterDeleted: true,
|
|
} satisfies HospitalityListConfig,
|
|
|
|
kitchenTicketsPrep: {
|
|
title: "وضعیت آمادهسازی",
|
|
breadcrumb: "وضعیت آمادهسازی",
|
|
resourceKey: "kitchenTicketsPrep",
|
|
bundleFeature: "kitchen",
|
|
permission: "hospitality.view",
|
|
api: hospitalityApi.kitchenTickets,
|
|
refreshIntervalMs: 5000,
|
|
listFilter: (row) => String(row.status) === "preparing",
|
|
columns: [
|
|
{ key: "code", header: "کد" },
|
|
{ key: "source_type", header: "منبع" },
|
|
{ key: "status", header: "وضعیت", type: "status" },
|
|
{ key: "station_id", header: "ایستگاه" },
|
|
...auditColumns(),
|
|
],
|
|
rowActions: [
|
|
{
|
|
label: "آماده",
|
|
onClick: async (tenantId, row) => {
|
|
await hospitalityApi.kitchenTickets.updateStatus(tenantId, String(row.id), { status: "ready" });
|
|
},
|
|
},
|
|
],
|
|
readOnly: true,
|
|
} satisfies HospitalityListConfig,
|
|
|
|
posTickets: {
|
|
title: "POS Lite",
|
|
breadcrumb: "POS Lite",
|
|
resourceKey: "posTickets",
|
|
bundleFeature: "pos_lite",
|
|
permission: "hospitality.view",
|
|
api: hospitalityApi.posTickets,
|
|
columns: [
|
|
{ key: "code", header: "کد" },
|
|
{ key: "register_id", header: "صندوق" },
|
|
{ key: "status", header: "وضعیت", type: "status" },
|
|
{ key: "currency_code", header: "ارز" },
|
|
{ key: "table_id", header: "میز", defaultVisible: false },
|
|
...auditColumns(),
|
|
],
|
|
createFields: [
|
|
venueIdField(),
|
|
{ name: "code", label: "کد", required: true },
|
|
{ name: "register_id", label: "شناسه صندوق", required: true },
|
|
{ name: "currency_code", label: "ارز" },
|
|
{ name: "table_id", label: "شناسه میز" },
|
|
],
|
|
filterDeleted: true,
|
|
} satisfies HospitalityListConfig,
|
|
|
|
posRegisters: {
|
|
title: "صندوق",
|
|
breadcrumb: "صندوق",
|
|
resourceKey: "posRegisters",
|
|
bundleFeature: "pos_lite",
|
|
permission: "hospitality.view",
|
|
api: hospitalityApi.posRegisters,
|
|
columns: codeNameStatusColumns([{ key: "venue_id", header: "مکان" }]),
|
|
createFields: codeNameCreateFields([venueIdField()]),
|
|
filterDeleted: true,
|
|
} satisfies HospitalityListConfig,
|
|
|
|
posPayments: {
|
|
title: "POS Pro",
|
|
breadcrumb: "POS Pro",
|
|
resourceKey: "posPayments",
|
|
bundleFeature: "pos_pro",
|
|
permission: "hospitality.view",
|
|
api: hospitalityApi.posPayments,
|
|
columns: [
|
|
{ key: "ticket_id", header: "تیکت" },
|
|
{ key: "method", header: "روش پرداخت" },
|
|
{ key: "amount", header: "مبلغ", type: "money" },
|
|
{ key: "currency_code", header: "ارز" },
|
|
{ key: "status", header: "وضعیت", type: "status" },
|
|
{ key: "external_payment_ref", header: "ارجاع خارجی", defaultVisible: false },
|
|
...auditColumns(),
|
|
],
|
|
createFields: [
|
|
venueIdField(),
|
|
{ name: "ticket_id", label: "شناسه تیکت", required: true },
|
|
{
|
|
name: "method",
|
|
label: "روش پرداخت",
|
|
type: "select",
|
|
required: true,
|
|
options: posPaymentMethodOptions,
|
|
},
|
|
{ name: "amount", label: "مبلغ", type: "number", required: true },
|
|
{ name: "currency_code", label: "ارز" },
|
|
],
|
|
filterDeleted: true,
|
|
} satisfies HospitalityListConfig,
|
|
|
|
posPaymentsList: {
|
|
title: "پرداختها",
|
|
breadcrumb: "پرداختها",
|
|
resourceKey: "posPaymentsList",
|
|
bundleFeature: "pos_pro",
|
|
permission: "hospitality.view",
|
|
api: hospitalityApi.posPayments,
|
|
columns: [
|
|
{ key: "ticket_id", header: "تیکت" },
|
|
{ key: "method", header: "روش" },
|
|
{ key: "amount", header: "مبلغ", type: "money" },
|
|
{ key: "currency_code", header: "ارز" },
|
|
{ key: "status", header: "وضعیت", type: "status" },
|
|
{ key: "created_at", header: "زمان", type: "datetime" },
|
|
],
|
|
createFields: [
|
|
venueIdField(),
|
|
{ name: "ticket_id", label: "شناسه تیکت", required: true },
|
|
{
|
|
name: "method",
|
|
label: "روش پرداخت",
|
|
type: "select",
|
|
required: true,
|
|
options: posPaymentMethodOptions,
|
|
},
|
|
{ name: "amount", label: "مبلغ", type: "number", required: true },
|
|
],
|
|
filterDeleted: true,
|
|
} satisfies HospitalityListConfig,
|
|
|
|
posDiscounts: {
|
|
title: "تخفیفها",
|
|
breadcrumb: "تخفیفها",
|
|
resourceKey: "posDiscounts",
|
|
bundleFeature: "pos_pro",
|
|
permission: "hospitality.view",
|
|
api: hospitalityApi.posDiscounts,
|
|
listFilter: (row) => !String(row.code ?? "").toUpperCase().startsWith("CPN"),
|
|
columns: [
|
|
{ key: "code", header: "کد" },
|
|
{ key: "name", header: "نام" },
|
|
{ key: "ticket_id", header: "تیکت" },
|
|
{ key: "kind", header: "نوع" },
|
|
{ key: "value", header: "مقدار" },
|
|
...auditColumns(),
|
|
],
|
|
createFields: [
|
|
venueIdField(),
|
|
{ name: "ticket_id", label: "شناسه تیکت", required: true },
|
|
{ name: "code", label: "کد", required: true },
|
|
{
|
|
name: "kind",
|
|
label: "نوع",
|
|
type: "select",
|
|
required: true,
|
|
options: posDiscountKindOptions,
|
|
},
|
|
{ name: "value", label: "مقدار", type: "number", required: true },
|
|
{ name: "name", label: "نام" },
|
|
],
|
|
filterDeleted: true,
|
|
} satisfies HospitalityListConfig,
|
|
|
|
posDiscountsCoupons: {
|
|
title: "کوپنها",
|
|
breadcrumb: "کوپنها",
|
|
resourceKey: "posDiscountsCoupons",
|
|
bundleFeature: "pos_pro",
|
|
permission: "hospitality.view",
|
|
api: hospitalityApi.posDiscounts,
|
|
listFilter: (row) => String(row.code ?? "").toUpperCase().startsWith("CPN"),
|
|
columns: [
|
|
{ key: "code", header: "کد کوپن" },
|
|
{ key: "name", header: "نام" },
|
|
{ key: "kind", header: "نوع" },
|
|
{ key: "value", header: "مقدار" },
|
|
{ key: "ticket_id", header: "تیکت", defaultVisible: false },
|
|
...auditColumns(),
|
|
],
|
|
createFields: [
|
|
venueIdField(),
|
|
{ name: "ticket_id", label: "شناسه تیکت", required: true },
|
|
{ name: "code", label: "کد کوپن (CPN-…)", required: true },
|
|
{
|
|
name: "kind",
|
|
label: "نوع",
|
|
type: "select",
|
|
required: true,
|
|
options: posDiscountKindOptions,
|
|
},
|
|
{ name: "value", label: "مقدار", type: "number", required: true },
|
|
{ name: "name", label: "نام", required: true },
|
|
],
|
|
filterDeleted: true,
|
|
} satisfies HospitalityListConfig,
|
|
|
|
featureTogglesPromo: {
|
|
title: "پروموشنها",
|
|
breadcrumb: "پروموشنها",
|
|
resourceKey: "featureTogglesPromo",
|
|
bundleFeature: "pos_pro",
|
|
permission: "hospitality.view",
|
|
api: hospitalityApi.featureToggles,
|
|
useUpsert: true,
|
|
listFilter: (row) => String(row.feature_key ?? "").toLowerCase().includes("promo"),
|
|
columns: [
|
|
{ key: "feature_key", header: "کلید قابلیت" },
|
|
{ key: "enabled", header: "فعال", render: (r) => (r.enabled ? "بله" : "خیر") },
|
|
{ key: "bundle_key", header: "بسته", defaultVisible: false },
|
|
{ key: "venue_id", header: "مکان", defaultVisible: false },
|
|
...auditColumns(),
|
|
],
|
|
createFields: [
|
|
{ name: "feature_key", label: "کلید قابلیت", required: true, placeholder: "hospitality.promo.*" },
|
|
{
|
|
name: "enabled",
|
|
label: "فعال",
|
|
type: "select",
|
|
required: true,
|
|
options: [
|
|
{ value: "true", label: "بله" },
|
|
{ value: "false", label: "خیر" },
|
|
],
|
|
},
|
|
venueIdField(false),
|
|
],
|
|
} satisfies HospitalityListConfig,
|
|
|
|
connectorRegistrationsCrm: {
|
|
title: "مشتریان",
|
|
breadcrumb: "مشتریان",
|
|
resourceKey: "connectorRegistrationsCrm",
|
|
bundleFeature: "crm_connector",
|
|
permission: "hospitality.view",
|
|
api: hospitalityApi.connectorRegistrations,
|
|
listFilter: connectorKindFilter("crm"),
|
|
columns: connectorColumns,
|
|
createFields: connectorCreateFields("crm", "CRM"),
|
|
filterDeleted: true,
|
|
} satisfies HospitalityListConfig,
|
|
|
|
connectorCrm: {
|
|
title: "CRM",
|
|
breadcrumb: "CRM",
|
|
resourceKey: "connectorCrm",
|
|
bundleFeature: "crm_connector",
|
|
permission: "hospitality.view",
|
|
api: hospitalityApi.connectorRegistrations,
|
|
listFilter: connectorKindFilter("crm"),
|
|
columns: connectorColumns,
|
|
createFields: connectorCreateFields("crm", "CRM"),
|
|
filterDeleted: true,
|
|
} satisfies HospitalityListConfig,
|
|
|
|
connectorLoyalty: {
|
|
title: "وفاداری",
|
|
breadcrumb: "وفاداری",
|
|
resourceKey: "connectorLoyalty",
|
|
bundleFeature: "loyalty_connector",
|
|
permission: "hospitality.view",
|
|
api: hospitalityApi.connectorRegistrations,
|
|
listFilter: connectorKindFilter("loyalty"),
|
|
columns: connectorColumns,
|
|
createFields: connectorCreateFields("loyalty", "وفاداری"),
|
|
filterDeleted: true,
|
|
} satisfies HospitalityListConfig,
|
|
|
|
connectorCommunication: {
|
|
title: "ارتباطات",
|
|
breadcrumb: "ارتباطات",
|
|
resourceKey: "connectorCommunication",
|
|
bundleFeature: "communication_connector",
|
|
permission: "hospitality.view",
|
|
api: hospitalityApi.connectorRegistrations,
|
|
listFilter: connectorKindFilter("communication"),
|
|
columns: connectorColumns,
|
|
createFields: connectorCreateFields("communication", "ارتباطات"),
|
|
filterDeleted: true,
|
|
} satisfies HospitalityListConfig,
|
|
|
|
connectorWebsite: {
|
|
title: "وبسایت",
|
|
breadcrumb: "وبسایت",
|
|
resourceKey: "connectorWebsite",
|
|
bundleFeature: "website_connector",
|
|
permission: "hospitality.view",
|
|
api: hospitalityApi.connectorRegistrations,
|
|
listFilter: connectorKindFilter("website"),
|
|
columns: connectorColumns,
|
|
createFields: connectorCreateFields("website", "وبسایت"),
|
|
filterDeleted: true,
|
|
} satisfies HospitalityListConfig,
|
|
|
|
connectorDelivery: {
|
|
title: "پیک",
|
|
breadcrumb: "پیک",
|
|
resourceKey: "connectorDelivery",
|
|
bundleFeature: "delivery_connector",
|
|
permission: "hospitality.view",
|
|
api: hospitalityApi.connectorRegistrations,
|
|
listFilter: connectorKindFilter("delivery"),
|
|
columns: connectorColumns,
|
|
createFields: connectorCreateFields("delivery", "پیک"),
|
|
filterDeleted: true,
|
|
} satisfies HospitalityListConfig,
|
|
|
|
connectorDispatches: {
|
|
title: "مارکتپلیس",
|
|
breadcrumb: "مارکتپلیس",
|
|
resourceKey: "connectorDispatches",
|
|
bundleFeature: null,
|
|
permission: "hospitality.view",
|
|
api: hospitalityApi.connectorDispatches,
|
|
columns: [
|
|
{ key: "event_type", header: "نوع رویداد" },
|
|
{ key: "direction", header: "جهت" },
|
|
{ key: "status", header: "وضعیت", type: "status" },
|
|
{ key: "registration_id", header: "ثبتنام" },
|
|
{ key: "payload_ref", header: "Payload", defaultVisible: false },
|
|
...auditColumns(),
|
|
],
|
|
createFields: [
|
|
venueIdField(false),
|
|
{ name: "registration_id", label: "شناسه ثبتنام", required: true },
|
|
{ name: "event_type", label: "نوع رویداد", required: true },
|
|
{ name: "direction", label: "جهت", required: true },
|
|
{ name: "payload_ref", label: "مرجع Payload" },
|
|
],
|
|
filterDeleted: true,
|
|
} satisfies HospitalityListConfig,
|
|
|
|
pickupOrders: {
|
|
title: "سفارش بیرونبر",
|
|
breadcrumb: "سفارش بیرونبر",
|
|
resourceKey: "pickupOrders",
|
|
bundleFeature: "qr_ordering",
|
|
permission: "hospitality.view",
|
|
api: hospitalityApi.qrOrderingSessions,
|
|
listFilter: (row) => row.table_id == null,
|
|
columns: [
|
|
{ key: "session_token", header: "توکن" },
|
|
{ key: "guest_label", header: "مهمان" },
|
|
{ key: "status", header: "وضعیت", type: "status" },
|
|
{ key: "notes", header: "یادداشت", defaultVisible: false },
|
|
...auditColumns(),
|
|
],
|
|
readOnly: true,
|
|
} satisfies HospitalityListConfig,
|
|
|
|
onlineOrders: {
|
|
title: "سفارش آنلاین",
|
|
breadcrumb: "سفارش آنلاین",
|
|
resourceKey: "onlineOrders",
|
|
bundleFeature: "qr_ordering",
|
|
permission: "hospitality.view",
|
|
api: hospitalityApi.qrOrderingSessions,
|
|
listFilter: (row) => row.table_id != null,
|
|
columns: [
|
|
{ key: "session_token", header: "توکن" },
|
|
{ key: "guest_label", header: "مهمان" },
|
|
{ key: "table_id", header: "میز" },
|
|
{ key: "status", header: "وضعیت", type: "status" },
|
|
...auditColumns(),
|
|
],
|
|
readOnly: true,
|
|
} satisfies HospitalityListConfig,
|
|
|
|
orderTimeline: {
|
|
title: "خط زمانی سفارش",
|
|
breadcrumb: "خط زمانی سفارش",
|
|
resourceKey: "orderTimeline",
|
|
bundleFeature: "kitchen",
|
|
permission: "hospitality.view",
|
|
api: hospitalityApi.kitchenTickets,
|
|
refreshIntervalMs: 10000,
|
|
columns: [
|
|
{ key: "code", header: "کد" },
|
|
{ key: "source_type", header: "منبع" },
|
|
{ key: "status", header: "وضعیت", type: "status" },
|
|
{ key: "created_at", header: "ایجاد", type: "datetime" },
|
|
{ key: "updated_at", header: "بهروزرسانی", type: "datetime" },
|
|
],
|
|
readOnly: true,
|
|
} satisfies HospitalityListConfig,
|
|
|
|
invoices: {
|
|
title: "فاکتورها",
|
|
breadcrumb: "فاکتورها",
|
|
resourceKey: "invoices",
|
|
bundleFeature: "pos_lite",
|
|
permission: "hospitality.view",
|
|
api: hospitalityApi.posTickets,
|
|
listFilter: (row) => !["void", "cancelled"].includes(String(row.status)),
|
|
columns: [
|
|
{ key: "code", header: "کد فاکتور" },
|
|
{ key: "register_id", header: "صندوق" },
|
|
{ key: "status", header: "وضعیت", type: "status" },
|
|
{ key: "currency_code", header: "ارز" },
|
|
{ key: "created_at", header: "تاریخ", type: "datetime" },
|
|
],
|
|
readOnly: true,
|
|
} satisfies HospitalityListConfig,
|
|
|
|
receipts: {
|
|
title: "رسیدها",
|
|
breadcrumb: "رسیدها",
|
|
resourceKey: "receipts",
|
|
bundleFeature: "pos_lite",
|
|
permission: "hospitality.view",
|
|
api: hospitalityApi.posTickets,
|
|
listFilter: (row) => String(row.status) === "paid",
|
|
columns: [
|
|
{ key: "code", header: "کد رسید" },
|
|
{ key: "register_id", header: "صندوق" },
|
|
{ key: "currency_code", header: "ارز" },
|
|
{ key: "created_at", header: "تاریخ پرداخت", type: "datetime" },
|
|
],
|
|
readOnly: true,
|
|
} satisfies HospitalityListConfig,
|
|
|
|
analyticsReports: {
|
|
title: "گزارشها",
|
|
breadcrumb: "گزارشها",
|
|
resourceKey: "analyticsReports",
|
|
bundleFeature: "analytics",
|
|
permission: "hospitality.view",
|
|
api: hospitalityApi.analyticsReports,
|
|
columns: [
|
|
{ key: "code", header: "کد" },
|
|
{ key: "name", header: "نام" },
|
|
{
|
|
key: "metric_keys",
|
|
header: "متریکها",
|
|
render: (r) => (Array.isArray(r.metric_keys) ? (r.metric_keys as string[]).join(", ") : "—"),
|
|
},
|
|
{ key: "status", header: "وضعیت", type: "status" },
|
|
...auditColumns(),
|
|
],
|
|
createFields: codeNameCreateFields([
|
|
venueIdField(false),
|
|
{ name: "metric_keys", label: "متریکها (جدا با کاما)", required: true },
|
|
]),
|
|
filterDeleted: true,
|
|
} satisfies HospitalityListConfig,
|
|
|
|
analyticsSnapshots: {
|
|
title: "آنالیتیکس",
|
|
breadcrumb: "آنالیتیکس",
|
|
resourceKey: "analyticsSnapshots",
|
|
bundleFeature: "analytics",
|
|
permission: "hospitality.view",
|
|
api: hospitalityApi.analyticsSnapshots,
|
|
columns: [
|
|
{ key: "report_id", header: "گزارش" },
|
|
{ key: "period_start", header: "شروع دوره" },
|
|
{ key: "period_end", header: "پایان دوره" },
|
|
{ key: "status", header: "وضعیت", type: "status" },
|
|
{
|
|
key: "metrics",
|
|
header: "متریکها",
|
|
render: (r) =>
|
|
r.metrics && typeof r.metrics === "object"
|
|
? String(Object.keys(r.metrics as object).length)
|
|
: "0",
|
|
},
|
|
...auditColumns(),
|
|
],
|
|
readOnly: true,
|
|
} satisfies HospitalityListConfig,
|
|
|
|
inventoryOverview: {
|
|
title: "موجودی",
|
|
breadcrumb: "موجودی",
|
|
resourceKey: "inventoryOverview",
|
|
bundleFeature: "analytics",
|
|
permission: "hospitality.view",
|
|
api: hospitalityApi.analyticsSnapshots,
|
|
listFilter: (row) => {
|
|
const metrics = row.metrics as Record<string, unknown> | null | undefined;
|
|
if (!metrics) return false;
|
|
return Object.keys(metrics).some((k) => k.toLowerCase().includes("inventory") || k.toLowerCase().includes("stock"));
|
|
},
|
|
columns: [
|
|
{ key: "period_start", header: "شروع" },
|
|
{ key: "period_end", header: "پایان" },
|
|
{
|
|
key: "metrics",
|
|
header: "موجودی",
|
|
render: (r) => {
|
|
const metrics = r.metrics as Record<string, unknown> | undefined;
|
|
if (!metrics) return "—";
|
|
const inv = Object.entries(metrics).find(([k]) =>
|
|
k.toLowerCase().includes("inventory") || k.toLowerCase().includes("stock")
|
|
);
|
|
return inv ? String(inv[1]) : "—";
|
|
},
|
|
},
|
|
{ key: "status", header: "وضعیت", type: "status" },
|
|
],
|
|
readOnly: true,
|
|
} satisfies HospitalityListConfig,
|
|
|
|
settings: {
|
|
title: "تنظیمات",
|
|
breadcrumb: "تنظیمات",
|
|
resourceKey: "settings",
|
|
bundleFeature: null,
|
|
permission: "hospitality.view",
|
|
api: hospitalityApi.settings,
|
|
useUpsert: true,
|
|
columns: [
|
|
{ key: "key", header: "کلید", searchable: true },
|
|
{
|
|
key: "value",
|
|
header: "مقدار",
|
|
render: (r) => (r.value != null ? JSON.stringify(r.value) : "—"),
|
|
},
|
|
{ key: "venue_id", header: "مکان", defaultVisible: false },
|
|
...auditColumns(),
|
|
],
|
|
createFields: [
|
|
{ name: "key", label: "کلید", required: true },
|
|
{ name: "value", label: "مقدار (JSON)", type: "textarea", placeholder: '{"enabled": true}' },
|
|
venueIdField(false),
|
|
],
|
|
} satisfies HospitalityListConfig,
|
|
|
|
permissions: {
|
|
title: "مجوزها",
|
|
breadcrumb: "مجوزها",
|
|
resourceKey: "permissions",
|
|
bundleFeature: null,
|
|
permission: "hospitality.view",
|
|
api: hospitalityApi.permissions,
|
|
columns: [
|
|
{ key: "code", header: "کد" },
|
|
{ key: "name", header: "نام" },
|
|
{ key: "bundle_key", header: "بسته", defaultVisible: false },
|
|
{ key: "status", header: "وضعیت", type: "status" },
|
|
...auditColumns(),
|
|
],
|
|
createFields: [
|
|
{ name: "code", label: "کد", required: true },
|
|
{ name: "name", label: "نام", required: true },
|
|
{ name: "description", label: "توضیح", type: "textarea" },
|
|
{ name: "bundle_key", label: "کلید بسته" },
|
|
],
|
|
filterDeleted: true,
|
|
} satisfies HospitalityListConfig,
|
|
|
|
roles: {
|
|
title: "نقشها",
|
|
breadcrumb: "نقشها",
|
|
resourceKey: "roles",
|
|
bundleFeature: null,
|
|
permission: "hospitality.view",
|
|
api: hospitalityApi.roles,
|
|
columns: codeNameStatusColumns([
|
|
{ key: "venue_id", header: "مکان", defaultVisible: false },
|
|
{
|
|
key: "permission_keys",
|
|
header: "مجوزها",
|
|
render: (r) => (Array.isArray(r.permission_keys) ? (r.permission_keys as string[]).join(", ") : "—"),
|
|
defaultVisible: false,
|
|
},
|
|
]),
|
|
createFields: codeNameCreateFields([
|
|
venueIdField(false),
|
|
{ name: "description", label: "توضیح", type: "textarea" },
|
|
{ name: "permission_keys", label: "مجوزها (جدا با کاما)" },
|
|
]),
|
|
filterDeleted: true,
|
|
} satisfies HospitalityListConfig,
|
|
|
|
events: {
|
|
title: "لاگ ممیزی",
|
|
breadcrumb: "لاگ ممیزی",
|
|
resourceKey: "events",
|
|
bundleFeature: null,
|
|
permission: "hospitality.view",
|
|
api: hospitalityApi.events,
|
|
columns: [
|
|
{ key: "event_type", header: "نوع رویداد", searchable: true },
|
|
{ key: "title", header: "عنوان", searchable: true },
|
|
{ key: "status", header: "وضعیت", type: "status" },
|
|
{ key: "venue_id", header: "مکان", defaultVisible: false },
|
|
...auditColumns(),
|
|
],
|
|
createFields: [
|
|
venueIdField(),
|
|
{ name: "event_type", label: "نوع رویداد", required: true },
|
|
{ name: "title", label: "عنوان", required: true },
|
|
{ name: "payload", label: "Payload (JSON)", type: "textarea" },
|
|
],
|
|
filterDeleted: true,
|
|
} satisfies HospitalityListConfig,
|
|
|
|
notifications: {
|
|
title: "اعلانها",
|
|
breadcrumb: "اعلانها",
|
|
resourceKey: "notifications",
|
|
bundleFeature: null,
|
|
permission: "hospitality.view",
|
|
api: hospitalityApi.events,
|
|
listFilter: (row) => {
|
|
const t = String(row.event_type ?? "");
|
|
return (
|
|
t.includes("status_changed") ||
|
|
t.includes("submitted") ||
|
|
t.includes("payment") ||
|
|
t.includes("waitlist")
|
|
);
|
|
},
|
|
columns: [
|
|
{ key: "event_type", header: "نوع" },
|
|
{ key: "title", header: "اعلان" },
|
|
{ key: "created_at", header: "زمان", type: "datetime" },
|
|
{ key: "status", header: "وضعیت", type: "status", defaultVisible: false },
|
|
],
|
|
readOnly: true,
|
|
} satisfies HospitalityListConfig,
|
|
|
|
tenantBundles: {
|
|
title: "بستههای فعال",
|
|
breadcrumb: "بستههای فعال",
|
|
resourceKey: "tenantBundles",
|
|
bundleFeature: null,
|
|
permission: "hospitality.view",
|
|
api: hospitalityApi.tenantBundles,
|
|
readOnly: true,
|
|
columns: [
|
|
{ key: "bundle_key", header: "بسته" },
|
|
{ key: "status", header: "وضعیت", type: "status" },
|
|
{ key: "venue_id", header: "مکان", defaultVisible: false },
|
|
{ key: "activated_by", header: "فعالساز", defaultVisible: false },
|
|
{ key: "bundle_definition_id", header: "تعریف بسته", defaultVisible: false },
|
|
...auditColumns(),
|
|
],
|
|
rowActions: [
|
|
{
|
|
label: "غیرفعال",
|
|
variant: "danger",
|
|
onClick: async (tenantId, row) => {
|
|
await hospitalityApi.tenantBundles.deactivate(tenantId, String(row.id));
|
|
},
|
|
},
|
|
],
|
|
} satisfies HospitalityListConfig,
|
|
} as const satisfies Record<string, HospitalityListConfig>;
|