TorbatYar/frontend/modules/beauty/configs/owner-resources.tsx
Mortezakoohjani d579d0b142 feat(loyalty): add Loyalty Platform Frontend module
Add frontend/modules/loyalty with types, API client, design system, feature pages and thin App Router routes under app/loyalty/. BFF proxy at app/api/loyalty/. Include loyalty frontend docs.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-27 10:50:55 +03:30

708 lines
31 KiB
TypeScript

"use client";
import { formatMoney } from "@/lib/utils";
import { beautyBusinessApi } from "@/modules/beauty/services/beauty-business-api";
import type { ResourceConfig } from "@/modules/beauty/components/crud/BeautyResourceWorkspace";
import {
businessFormatOptions,
dayOfWeekOptions,
lifecycleStatusOptions,
resourceStatusOptions,
staffKindOptions,
staffKindLabel,
dayOfWeekLabel,
} from "@/modules/beauty/constants/labels";
import { BeautyStatusChip } from "@/modules/beauty/design-system";
import { JalaliDateText } from "@/components/ds";
const auditList = (tenantId: string, entityType: string, entityId: string) =>
beautyBusinessApi.audit.list(tenantId, entityType, entityId);
const stripEmpty = (values: Record<string, string>) => {
const body: Record<string, unknown> = {};
Object.entries(values).forEach(([k, v]) => {
if (v?.trim()) body[k] = v.trim();
});
return body;
};
export const organizationResource: ResourceConfig = {
storageKey: "beauty-organizations",
title: "سازمان‌ها",
description: "مدیریت سازمان‌های سالن و زیبایی.",
viewPermission: "beauty_business.organizations.view",
managePermission: "beauty_business.organizations.manage",
queryKey: ["beauty", "organizations"],
listFn: (tenantId) => beautyBusinessApi.organizations.list(tenantId),
createFn: (tenantId, body) => beautyBusinessApi.organizations.create(tenantId, body as never),
updateFn: (tenantId, id, body) => beautyBusinessApi.organizations.update(tenantId, id, body as never),
deleteFn: (tenantId, id) => beautyBusinessApi.organizations.delete(tenantId, id),
auditEntityType: "organization",
auditFn: auditList,
createLabel: "سازمان جدید",
fields: [
{ name: "code", label: "کد", required: true, form: (m) => m === "create" },
{ name: "name", label: "نام", required: true },
{ name: "description", label: "توضیحات" },
{
name: "business_format",
label: "نوع کسب‌وکار",
type: "select",
options: businessFormatOptions,
table: false,
form: (m) => m === "create",
},
{
name: "status",
label: "وضعیت",
type: "select",
options: lifecycleStatusOptions,
form: (m) => m === "edit",
},
],
};
export const branchResource: ResourceConfig = {
storageKey: "beauty-branches",
title: "شعب",
description: "مدیریت شعب سالن زیبایی.",
viewPermission: "beauty_business.branches.view",
managePermission: "beauty_business.branches.manage",
queryKey: ["beauty", "branches"],
listFn: (tenantId) => beautyBusinessApi.branches.list(tenantId),
createFn: (tenantId, body) => beautyBusinessApi.branches.create(tenantId, body as never),
updateFn: (tenantId, id, body) => beautyBusinessApi.branches.update(tenantId, id, body as never),
deleteFn: (tenantId, id) => beautyBusinessApi.branches.delete(tenantId, id),
auditEntityType: "branch",
auditFn: auditList,
createLabel: "شعبه جدید",
fields: [
{ name: "organization_id", label: "سازمان", type: "organization", required: true, form: (m) => m === "create", table: false },
{ name: "code", label: "کد", required: true, form: (m) => m === "create" },
{ name: "name", label: "نام", required: true },
{ name: "description", label: "توضیحات" },
{
name: "status",
label: "وضعیت",
type: "select",
options: lifecycleStatusOptions,
form: (m) => m === "edit",
},
],
};
export const customerResource: ResourceConfig = {
storageKey: "beauty-customers",
title: "مشتریان",
description: "پروفایل مشتریان سالن زیبایی.",
viewPermission: "beauty_business.customers.view",
managePermission: "beauty_business.customers.manage",
queryKey: ["beauty", "customers"],
listFn: (tenantId) => beautyBusinessApi.customers.list(tenantId),
createFn: (tenantId, body) => beautyBusinessApi.customers.create(tenantId, body as never),
createLabel: "مشتری جدید",
cardTitleKey: "display_name",
fields: [
{ name: "organization_id", label: "سازمان", type: "organization", required: true, form: (m) => m === "create", table: false },
{ name: "code", label: "کد", required: true, form: (m) => m === "create" },
{ name: "display_name", label: "نام", required: true },
{ name: "mobile", label: "موبایل" },
{ name: "email", label: "ایمیل" },
{ name: "status", label: "وضعیت" },
],
mapCreateBody: stripEmpty,
};
export const staffResource: ResourceConfig = {
storageKey: "beauty-staff",
title: "پرسنل",
description: "مدیریت کارکنان سالن.",
viewPermission: "beauty_business.staff.view",
managePermission: "beauty_business.staff.manage",
queryKey: ["beauty", "staff"],
listFn: (tenantId) => beautyBusinessApi.staff.list(tenantId),
createFn: (tenantId, body) => beautyBusinessApi.staff.create(tenantId, body as never),
createLabel: "پرسنل جدید",
cardTitleKey: "display_name",
fields: [
{ name: "organization_id", label: "سازمان", type: "organization", required: true, form: (m) => m === "create", table: false },
{ name: "branch_id", label: "شعبه", type: "branch", form: (m) => m === "create", table: false },
{ name: "code", label: "کد", required: true, form: (m) => m === "create" },
{ name: "display_name", label: "نام", required: true },
{
name: "kind",
label: "نقش",
type: "select",
options: staffKindOptions,
render: (r) => staffKindLabel[String(r.kind)] ?? String(r.kind),
},
{ name: "mobile", label: "موبایل" },
{ name: "email", label: "ایمیل" },
{ name: "status", label: "وضعیت" },
],
mapCreateBody: (values) => ({
organization_id: values.organization_id,
branch_id: values.branch_id || undefined,
code: values.code,
display_name: values.display_name,
kind: values.kind || "stylist",
mobile: values.mobile || undefined,
email: values.email || undefined,
}),
};
export const serviceCategoryResource: ResourceConfig = {
storageKey: "beauty-service-categories",
title: "دسته‌بندی خدمات",
description: "دسته‌های کاتالوگ خدمات.",
viewPermission: "beauty_business.service_categories.view",
managePermission: "beauty_business.service_categories.manage",
queryKey: ["beauty", "service-categories"],
listFn: (tenantId) => beautyBusinessApi.serviceCategories.list(tenantId),
createFn: (tenantId, body) => beautyBusinessApi.serviceCategories.create(tenantId, body as never),
createLabel: "دسته جدید",
fields: [
{ name: "organization_id", label: "سازمان", type: "organization", required: true, form: (m) => m === "create", table: false },
{ name: "code", label: "کد", required: true, form: (m) => m === "create" },
{ name: "name", label: "نام", required: true },
{ name: "description", label: "توضیحات" },
{ name: "sort_order", label: "ترتیب", type: "number" },
{ name: "status", label: "وضعیت" },
],
mapCreateBody: (values) => ({
organization_id: values.organization_id,
code: values.code,
name: values.name,
description: values.description || undefined,
sort_order: values.sort_order ? Number(values.sort_order) : undefined,
}),
};
export const serviceResource: ResourceConfig = {
storageKey: "beauty-services",
title: "خدمات",
description: "کاتالوگ خدمات زیبایی.",
viewPermission: "beauty_business.services.view",
managePermission: "beauty_business.services.manage",
queryKey: ["beauty", "services"],
listFn: (tenantId) => beautyBusinessApi.services.list(tenantId),
createFn: (tenantId, body) => beautyBusinessApi.services.create(tenantId, body as never),
createLabel: "خدمت جدید",
fields: [
{ name: "organization_id", label: "سازمان", type: "organization", required: true, form: (m) => m === "create", table: false },
{ name: "category_id", label: "دسته", type: "category", form: (m) => m === "create", table: false },
{ name: "code", label: "کد", required: true, form: (m) => m === "create" },
{ name: "name", label: "نام", required: true },
{ name: "description", label: "توضیحات" },
{ name: "base_duration_minutes", label: "مدت (دقیقه)", type: "number" },
{
name: "base_price",
label: "قیمت پایه",
type: "number",
render: (r) => (r.base_price != null ? formatMoney(String(r.base_price)) : "—"),
},
{ name: "status", label: "وضعیت" },
],
mapCreateBody: (values) => ({
organization_id: values.organization_id,
category_id: values.category_id || undefined,
code: values.code,
name: values.name,
description: values.description || undefined,
base_duration_minutes: values.base_duration_minutes ? Number(values.base_duration_minutes) : 30,
base_price: values.base_price ? Number(values.base_price) : undefined,
}),
};
export const staffScheduleResource: ResourceConfig = {
storageKey: "beauty-staff-schedules",
title: "برنامه کاری",
description: "برنامه هفتگی پرسنل.",
viewPermission: "beauty_business.schedules.view",
managePermission: "beauty_business.schedules.manage",
queryKey: ["beauty", "staff-schedules"],
listFn: (tenantId) => beautyBusinessApi.staffSchedules.list(tenantId),
createFn: (tenantId, body) => beautyBusinessApi.staffSchedules.create(tenantId, body as never),
createLabel: "برنامه جدید",
fields: [
{ name: "organization_id", label: "سازمان", type: "organization", required: true, form: (m) => m === "create", table: false },
{ name: "branch_id", label: "شعبه", type: "branch", required: true, form: (m) => m === "create", table: false },
{ name: "staff_ref", label: "پرسنل", type: "staff", required: true, form: (m) => m === "create" },
{ name: "code", label: "کد", required: true, form: (m) => m === "create" },
{
name: "day_of_week",
label: "روز",
type: "select",
options: dayOfWeekOptions,
required: true,
render: (r) => dayOfWeekLabel[String(r.day_of_week)] ?? String(r.day_of_week),
},
{ name: "start_time", label: "شروع", required: true, placeholder: "09:00" },
{ name: "end_time", label: "پایان", required: true, placeholder: "18:00" },
{
name: "is_active",
label: "فعال",
render: (r) => (r.is_active ? "بله" : "خیر"),
},
],
mapCreateBody: (values) => ({
organization_id: values.organization_id,
branch_id: values.branch_id,
staff_ref: values.staff_ref,
code: values.code,
day_of_week: values.day_of_week,
start_time: values.start_time,
end_time: values.end_time,
is_active: true,
}),
};
export const waitingListResource: ResourceConfig = {
storageKey: "beauty-waiting-list",
title: "لیست انتظار",
description: "درخواست‌های انتظار نوبت.",
viewPermission: "beauty_business.waiting_list.view",
managePermission: "beauty_business.waiting_list.manage",
queryKey: ["beauty", "waiting-list"],
listFn: (tenantId) => beautyBusinessApi.waitingList.list(tenantId),
createFn: (tenantId, body) => beautyBusinessApi.waitingList.create(tenantId, body as never),
createLabel: "ثبت در لیست انتظار",
fields: [
{ name: "organization_id", label: "سازمان", type: "organization", required: true, form: (m) => m === "create", table: false },
{ name: "branch_id", label: "شعبه", type: "branch", required: true, form: (m) => m === "create", table: false },
{ name: "code", label: "کد", required: true, form: (m) => m === "create" },
{ name: "customer_ref", label: "مشتری", type: "customer" },
{ name: "service_ref", label: "خدمت", type: "service" },
{ name: "preferred_staff_ref", label: "پرسنل ترجیحی", type: "staff", table: false },
{ name: "requested_date", label: "تاریخ درخواست", type: "datetime-local" },
{ name: "priority", label: "اولویت", type: "number" },
{ name: "notes", label: "یادداشت", type: "textarea" },
],
mapCreateBody: (values) => ({
organization_id: values.organization_id,
branch_id: values.branch_id,
code: values.code,
customer_ref: values.customer_ref || undefined,
service_ref: values.service_ref || undefined,
preferred_staff_ref: values.preferred_staff_ref || undefined,
requested_date: values.requested_date ? new Date(values.requested_date).toISOString().slice(0, 10) : undefined,
priority: values.priority ? Number(values.priority) : 0,
notes: values.notes || undefined,
}),
};
export const treatmentRoomResource: ResourceConfig = {
storageKey: "beauty-treatment-rooms",
title: "اتاق‌های درمان",
description: "اتاق‌ها و فضاهای خدمات.",
viewPermission: "beauty_business.rooms.view",
managePermission: "beauty_business.rooms.manage",
queryKey: ["beauty", "treatment-rooms"],
listFn: (tenantId) => beautyBusinessApi.treatmentRooms.list(tenantId),
createFn: (tenantId, body) => beautyBusinessApi.treatmentRooms.create(tenantId, body as never),
createLabel: "اتاق جدید",
fields: [
{ name: "organization_id", label: "سازمان", type: "organization", required: true, form: (m) => m === "create", table: false },
{ name: "branch_id", label: "شعبه", type: "branch", required: true, form: (m) => m === "create", table: false },
{ name: "code", label: "کد", required: true, form: (m) => m === "create" },
{ name: "name", label: "نام", required: true },
{ name: "description", label: "توضیحات" },
{
name: "status",
label: "وضعیت",
type: "select",
options: resourceStatusOptions,
render: (r) => <BeautyStatusChip status={String(r.status)} domain="resource" />,
},
{ name: "capacity", label: "ظرفیت", type: "number" },
],
mapCreateBody: (values) => ({
organization_id: values.organization_id,
branch_id: values.branch_id,
code: values.code,
name: values.name,
description: values.description || undefined,
status: values.status || "available",
capacity: values.capacity ? Number(values.capacity) : 1,
}),
};
export const stationResource: ResourceConfig = {
storageKey: "beauty-stations",
title: "ایستگاه‌ها",
description: "ایستگاه‌های کاری سالن.",
viewPermission: "beauty_business.stations.view",
managePermission: "beauty_business.stations.manage",
queryKey: ["beauty", "stations"],
listFn: (tenantId) => beautyBusinessApi.stations.list(tenantId),
createFn: (tenantId, body) => beautyBusinessApi.stations.create(tenantId, body as never),
createLabel: "ایستگاه جدید",
fields: [
{ name: "organization_id", label: "سازمان", type: "organization", required: true, form: (m) => m === "create", table: false },
{ name: "branch_id", label: "شعبه", type: "branch", required: true, form: (m) => m === "create", table: false },
{ name: "room_id", label: "اتاق", type: "room", form: (m) => m === "create", table: false },
{ name: "code", label: "کد", required: true, form: (m) => m === "create" },
{ name: "name", label: "نام", required: true },
{
name: "status",
label: "وضعیت",
type: "select",
options: resourceStatusOptions,
render: (r) => <BeautyStatusChip status={String(r.status)} domain="resource" />,
},
],
mapCreateBody: (values) => ({
organization_id: values.organization_id,
branch_id: values.branch_id,
room_id: values.room_id || undefined,
code: values.code,
name: values.name,
status: values.status || "available",
}),
};
export const branchPolicyResource: ResourceConfig = {
storageKey: "beauty-branch-policies",
title: "سیاست‌های شعبه",
description: "قوانین و سیاست‌های عملیاتی.",
viewPermission: "beauty_business.branch_policies.view",
managePermission: "beauty_business.branch_policies.manage",
queryKey: ["beauty", "branch-policies"],
listFn: (tenantId) => beautyBusinessApi.branchPolicies.list(tenantId),
createFn: (tenantId, body) => beautyBusinessApi.branchPolicies.create(tenantId, body as never),
createLabel: "سیاست جدید",
fields: [
{ name: "organization_id", label: "سازمان", type: "organization", required: true, form: (m) => m === "create", table: false },
{ name: "branch_id", label: "شعبه", type: "branch", required: true, form: (m) => m === "create", table: false },
{ name: "code", label: "کد", required: true, form: (m) => m === "create" },
{ name: "name", label: "نام", required: true },
{ name: "policy_type", label: "نوع سیاست", required: true },
{ name: "status", label: "وضعیت" },
],
mapCreateBody: (values) => ({
organization_id: values.organization_id,
branch_id: values.branch_id,
code: values.code,
name: values.name,
policy_type: values.policy_type,
status: values.status || "active",
}),
};
export const packageResource: ResourceConfig = {
storageKey: "beauty-packages",
title: "پکیج‌ها",
description: "تعریف پکیج‌های خدماتی.",
viewPermission: "beauty_business.packages.view",
managePermission: "beauty_business.packages.manage",
queryKey: ["beauty", "package-definitions"],
listFn: (tenantId) => beautyBusinessApi.packageDefinitions.list(tenantId),
createFn: (tenantId, body) => beautyBusinessApi.packageDefinitions.create(tenantId, body as never),
createLabel: "پکیج جدید",
fields: [
{ name: "organization_id", label: "سازمان", type: "organization", required: true, form: (m) => m === "create", table: false },
{ name: "code", label: "کد", required: true, form: (m) => m === "create" },
{ name: "name", label: "نام", required: true },
{ name: "description", label: "توضیحات" },
{ name: "total_sessions", label: "تعداد جلسات", type: "number" },
{
name: "price",
label: "قیمت",
type: "number",
render: (r) => (r.price != null ? formatMoney(String(r.price)) : "—"),
},
{ name: "valid_days", label: "اعتبار (روز)", type: "number" },
{ name: "status", label: "وضعیت" },
],
mapCreateBody: (values) => ({
organization_id: values.organization_id,
code: values.code,
name: values.name,
description: values.description || undefined,
total_sessions: values.total_sessions ? Number(values.total_sessions) : 1,
price: values.price ? Number(values.price) : undefined,
valid_days: values.valid_days ? Number(values.valid_days) : undefined,
}),
};
export const membershipResource: ResourceConfig = {
storageKey: "beauty-memberships",
title: "طرح‌های عضویت",
description: "عضویت و اشتراک مشتریان.",
viewPermission: "beauty_business.memberships.view",
managePermission: "beauty_business.memberships.manage",
queryKey: ["beauty", "membership-plans"],
listFn: (tenantId) => beautyBusinessApi.membershipPlans.list(tenantId),
createFn: (tenantId, body) => beautyBusinessApi.membershipPlans.create(tenantId, body as never),
createLabel: "طرح جدید",
fields: [
{ name: "organization_id", label: "سازمان", type: "organization", required: true, form: (m) => m === "create", table: false },
{ name: "code", label: "کد", required: true, form: (m) => m === "create" },
{ name: "name", label: "نام", required: true },
{ name: "description", label: "توضیحات" },
{ name: "billing_interval_days", label: "دوره صورتحساب (روز)", type: "number" },
{
name: "price",
label: "قیمت",
type: "number",
render: (r) => (r.price != null ? formatMoney(String(r.price)) : "—"),
},
{ name: "status", label: "وضعیت" },
],
mapCreateBody: (values) => ({
organization_id: values.organization_id,
code: values.code,
name: values.name,
description: values.description || undefined,
billing_interval_days: values.billing_interval_days ? Number(values.billing_interval_days) : 30,
price: values.price ? Number(values.price) : undefined,
}),
};
export const commissionResource: ResourceConfig = {
storageKey: "beauty-commissions",
title: "قوانین پورسانت",
description: "قوانین محاسبه پورسانت پرسنل.",
viewPermission: "beauty_business.commissions.view",
managePermission: "beauty_business.commissions.manage",
queryKey: ["beauty", "commission-rules"],
listFn: (tenantId) => beautyBusinessApi.commissionRules.list(tenantId),
createFn: (tenantId, body) => beautyBusinessApi.commissionRules.create(tenantId, body as never),
createLabel: "قانون جدید",
fields: [
{ name: "organization_id", label: "سازمان", type: "organization", required: true, form: (m) => m === "create", table: false },
{ name: "code", label: "کد", required: true, form: (m) => m === "create" },
{ name: "name", label: "نام", required: true },
{ name: "rule_type", label: "نوع قانون", required: true },
{ name: "rate_percent", label: "درصد", type: "number" },
{ name: "flat_amount", label: "مبلغ ثابت", type: "number" },
{ name: "applies_to_service_id", label: "خدمت", type: "service", table: false },
{ name: "status", label: "وضعیت" },
],
mapCreateBody: (values) => ({
organization_id: values.organization_id,
code: values.code,
name: values.name,
rule_type: values.rule_type,
rate_percent: values.rate_percent ? Number(values.rate_percent) : undefined,
flat_amount: values.flat_amount ? Number(values.flat_amount) : undefined,
applies_to_service_id: values.applies_to_service_id || undefined,
}),
};
export const marketingCampaignResource: ResourceConfig = {
storageKey: "beauty-marketing-campaigns",
title: "کمپین‌های بازاریابی",
description: "مدیریت کمپین‌ها و کانال‌های ارتباطی.",
viewPermission: "beauty_business.marketing.view",
managePermission: "beauty_business.marketing.manage",
queryKey: ["beauty", "marketing-campaigns"],
listFn: (tenantId) => beautyBusinessApi.marketingCampaigns.list(tenantId),
createFn: (tenantId, body) => beautyBusinessApi.marketingCampaigns.create(tenantId, body as never),
createLabel: "کمپین جدید",
fields: [
{ name: "organization_id", label: "سازمان", type: "organization", form: (m) => m === "create", table: false },
{ name: "code", label: "کد", required: true, form: (m) => m === "create" },
{ name: "name", label: "نام", required: true },
{ name: "channel", label: "کانال", placeholder: "sms, email, push" },
{ name: "status", label: "وضعیت" },
],
mapCreateBody: (values) => ({
organization_id: values.organization_id || undefined,
code: values.code,
name: values.name,
channel: values.channel || undefined,
}),
};
export const appointmentResource: ResourceConfig = {
storageKey: "beauty-appointments",
title: "نوبت‌ها",
description: "مدیریت نوبت‌های سالن زیبایی.",
viewPermission: "beauty_business.appointments.view",
managePermission: "beauty_business.appointments.manage",
queryKey: ["beauty", "appointments"],
listFn: (tenantId) => beautyBusinessApi.appointments.list(tenantId),
createFn: (tenantId, body) => beautyBusinessApi.appointments.create(tenantId, body as never),
updateFn: (tenantId, id, body) => beautyBusinessApi.appointments.update(tenantId, id, body as never),
auditEntityType: "appointment",
auditFn: auditList,
createLabel: "نوبت جدید",
fields: [
{ name: "organization_id", label: "سازمان", type: "organization", required: true, form: (m) => m === "create", table: false },
{ name: "branch_id", label: "شعبه", type: "branch", required: true, form: (m) => m === "create", table: false },
{ name: "code", label: "کد", required: true, form: (m) => m === "create" },
{
name: "scheduled_start",
label: "شروع",
type: "datetime-local",
required: true,
render: (r) => <JalaliDateText value={String(r.scheduled_start ?? "")} />,
},
{
name: "scheduled_end",
label: "پایان",
type: "datetime-local",
required: true,
form: (m) => m === "create",
table: false,
},
{ name: "customer_ref", label: "مشتری", type: "customer", form: (m) => m === "create" },
{ name: "staff_ref", label: "پرسنل", type: "staff" },
{ name: "service_ref", label: "خدمت", type: "service" },
{ name: "room_ref", label: "اتاق", type: "room", table: false },
{ name: "notes", label: "یادداشت", type: "textarea" },
{
name: "status",
label: "وضعیت",
render: (r) => (
<BeautyStatusChip status={String(r.status)} domain="appointment" />
),
},
],
mapCreateBody: (values) => ({
organization_id: values.organization_id,
branch_id: values.branch_id,
code: values.code,
scheduled_start: new Date(values.scheduled_start).toISOString(),
scheduled_end: new Date(values.scheduled_end).toISOString(),
customer_ref: values.customer_ref || undefined,
notes: values.notes || undefined,
}),
mapUpdateBody: (values, row) => ({
staff_ref: values.staff_ref || undefined,
room_ref: values.room_ref || undefined,
service_ref: values.service_ref || undefined,
scheduled_start: values.scheduled_start ? new Date(values.scheduled_start).toISOString() : undefined,
scheduled_end: values.scheduled_end ? new Date(values.scheduled_end).toISOString() : undefined,
notes: values.notes || undefined,
version: row.version as number,
}),
workflowActions: [
{
id: "confirm",
label: "تأیید",
visible: (r) => r.status === "requested",
run: (r, tenantId) =>
beautyBusinessApi.appointments.confirm(tenantId, String(r.id), {
version: r.version as number,
}),
},
{
id: "check-in",
label: "پذیرش",
visible: (r) => r.status === "confirmed",
run: (r, tenantId) =>
beautyBusinessApi.appointments.checkIn(tenantId, String(r.id), {
version: r.version as number,
}),
},
{
id: "complete",
label: "تکمیل",
visible: (r) => r.status === "checked_in" || r.status === "in_progress",
run: (r, tenantId) =>
beautyBusinessApi.appointments.complete(tenantId, String(r.id), {
version: r.version as number,
}),
},
{
id: "cancel",
label: "لغو",
variant: "danger",
confirmTitle: "لغو نوبت",
confirmDescription: "نوبت لغو می‌شود.",
visible: (r) => !["completed", "cancelled", "no_show"].includes(String(r.status)),
run: (r, tenantId) =>
beautyBusinessApi.appointments.cancel(tenantId, String(r.id), {
version: r.version as number,
}),
},
{
id: "no-show",
label: "عدم حضور",
variant: "outline",
confirmTitle: "ثبت عدم حضور",
visible: (r) => r.status === "confirmed" || r.status === "checked_in",
run: (r, tenantId) =>
beautyBusinessApi.appointments.noShow(tenantId, String(r.id), {
version: r.version as number,
}),
},
],
};
export const loyaltyIntegrationResource: ResourceConfig = {
storageKey: "beauty-loyalty-integrations",
title: "یکپارچه‌سازی وفاداری",
description: "اتصال به برنامه وفاداری.",
viewPermission: "beauty_business.integrations.view",
managePermission: "beauty_business.integrations.manage",
queryKey: ["beauty", "loyalty-integrations"],
listFn: (tenantId) => beautyBusinessApi.loyaltyIntegrations.list(tenantId),
createFn: (tenantId, body) => beautyBusinessApi.loyaltyIntegrations.create(tenantId, body as never),
createLabel: "یکپارچه‌سازی جدید",
fields: [
{ name: "organization_id", label: "سازمان", type: "organization", form: (m) => m === "create", table: false },
{ name: "code", label: "کد", required: true, form: (m) => m === "create" },
{ name: "name", label: "نام", required: true },
{ name: "external_program_ref", label: "مرجع خارجی" },
{ name: "status", label: "وضعیت" },
],
mapCreateBody: (values) => ({
organization_id: values.organization_id || undefined,
code: values.code,
name: values.name,
external_program_ref: values.external_program_ref || undefined,
}),
};
export const communicationIntegrationResource: ResourceConfig = {
storageKey: "beauty-communication-integrations",
title: "یکپارچه‌سازی ارتباطات",
description: "اتصال به کانال‌های پیام‌رسانی.",
viewPermission: "beauty_business.integrations.view",
managePermission: "beauty_business.integrations.manage",
queryKey: ["beauty", "communication-integrations"],
listFn: (tenantId) => beautyBusinessApi.communicationIntegrations.list(tenantId),
createFn: (tenantId, body) => beautyBusinessApi.communicationIntegrations.create(tenantId, body as never),
createLabel: "یکپارچه‌سازی جدید",
fields: [
{ name: "organization_id", label: "سازمان", type: "organization", form: (m) => m === "create", table: false },
{ name: "code", label: "کد", required: true, form: (m) => m === "create" },
{ name: "name", label: "نام", required: true },
{ name: "status", label: "وضعیت" },
],
mapCreateBody: (values) => ({
organization_id: values.organization_id || undefined,
code: values.code,
name: values.name,
}),
};
export const integrationDispatchResource: ResourceConfig = {
storageKey: "beauty-integration-dispatches",
title: "Dispatch رویدادها",
description: "ارسال رویداد به سیستم‌های خارجی.",
viewPermission: "beauty_business.integrations.view",
managePermission: "beauty_business.integrations.manage",
queryKey: ["beauty", "integration-dispatches"],
listFn: (tenantId) => beautyBusinessApi.integrationDispatches.list(tenantId),
createFn: (tenantId, body) => beautyBusinessApi.integrationDispatches.create(tenantId, body as never),
createLabel: "Dispatch جدید",
cardTitleKey: "event_type",
fields: [
{ name: "organization_id", label: "سازمان", type: "organization", form: (m) => m === "create", table: false },
{ name: "integration_kind", label: "نوع", required: true },
{ name: "event_type", label: "رویداد", required: true },
{ name: "payload_ref", label: "مرجع payload" },
{ name: "status", label: "وضعیت" },
],
mapCreateBody: (values) => ({
organization_id: values.organization_id || undefined,
integration_kind: values.integration_kind,
event_type: values.event_type,
payload_ref: values.payload_ref || undefined,
}),
};