TorbatYar/frontend/modules/commercial/presentation.ts

193 lines
7.8 KiB
TypeScript

const TOKEN_LABELS: Record<string, string> = {
restaurant: "رستوران",
hospitality: "رستوران و پذیرایی",
platform: "پلتفرم",
starter: "Starter",
growth: "رشد",
professional: "حرفه‌ای",
pro: "حرفه‌ای",
enterprise: "سازمانی",
loyalty: "باشگاه مشتریان",
delivery: "ارسال و پیک",
booking: "رزرو",
payment: "پرداخت",
accounting: "حسابداری",
website: "وب‌سایت",
crm: "مدیریت ارتباط با مشتری",
inventory: "انبارداری",
pos: "صندوق فروش",
monthly: "ماهانه",
yearly: "سالانه",
annual: "سالانه",
trial: "آزمایشی",
active: "فعال",
inactive: "غیرفعال",
draft: "پیش‌نویس",
published: "منتشرشده",
pending: "در انتظار",
verified: "تأییدشده",
failed: "ناموفق",
expired: "منقضی‌شده",
trialing: "دوره آزمایشی",
public: "عمومی",
private: "خصوصی",
hidden: "پنهان",
route: "داخل سامانه",
external: "پیوند بیرونی",
disabled: "غیرفعال",
deny: "غیرفعال",
allow: "فعال",
enterprise_quote: "استعلام قیمت",
irr: "ریال",
recommended: "پیشنهاد‌شده",
popular: "محبوب",
most_popular: "محبوب‌ترین",
};
export const FIELD_LABELS: Record<string, string> = {
display_name: "نام نمایشی",
title: "عنوان",
description: "توضیح کوتاه",
status: "وضعیت",
role: "نوع اشتراک",
pricing_model: "مدل پرداخت",
amount_minor: "قیمت",
currency: "واحد پول",
interval: "دوره پرداخت",
trial_days: "مدت دوره آزمایشی",
duration_days: "مدت دوره آزمایشی",
grace_period_days: "مهلت تمدید",
product_codes: "محصولات",
capability_codes: "قابلیت‌ها",
feature_refs: "امکانات",
pricing_refs: "روش‌های پرداخت",
discount_refs: "تخفیف‌ها",
coupon_code: "کد تخفیف",
tax_class_code: "مالیات",
trial_eligible: "دوره آزمایشی",
visibility: "نمایش",
display_order: "ترتیب نمایش",
category: "دسته‌بندی",
is_verified: "وضعیت تأیید",
domain_type: "نوع دامنه",
current_period_end: "تاریخ تمدید",
trial_ends_at: "پایان دوره آزمایشی",
created_at: "تاریخ ایجاد",
updated_at: "آخرین تغییر",
metadata: "اطلاعات تکمیلی",
attributes: "ویژگی‌ها",
parameters: "تنظیمات",
usage_limits: "سقف استفاده",
feature_limits: "محدودیت امکانات",
auto_disable: "غیرفعال‌سازی خودکار",
expiration_behavior: "رفتار پس از پایان",
ssl_ready: "وضعیت SSL",
dns_instructions: "راهنمای DNS",
custom_domain_allowed: "دامنه اختصاصی",
subdomain_only: "فقط زیردامنه",
reason: "دلیل پیشنهاد",
};
const TECHNICAL_FIELD = /(^id$|_id$|_code$|^code$|^source$|^version_code$|^schema_version$|^rule_trace$|^href$|_route$|launch_strategy)/i;
export function isTechnicalField(key: string): boolean {
return TECHNICAL_FIELD.test(key);
}
export function fieldLabel(key: string): string {
if (FIELD_LABELS[key]) return FIELD_LABELS[key];
const translated = key
.split(/[_\-.]+/)
.map((token) => TOKEN_LABELS[token.toLowerCase()] || "")
.filter(Boolean)
.join(" ");
return translated || "اطلاعات تکمیلی";
}
export function enumLabel(value: unknown): string {
if (typeof value === "boolean") return value ? "بله" : "خیر";
if (value == null || value === "") return "ثبت نشده";
const raw = String(value).trim();
return TOKEN_LABELS[raw.toLowerCase()] || humanizeCommercialCode(raw);
}
export function humanizeCommercialCode(code: unknown, explicitName?: string | null): string {
if (explicitName?.trim() && !looksTechnical(explicitName)) return explicitName.trim();
if (code == null) return "مورد تجاری";
const raw = String(code).trim();
if (!raw) return "مورد تجاری";
if (TOKEN_LABELS[raw.toLowerCase()]) return TOKEN_LABELS[raw.toLowerCase()];
if (!looksTechnical(raw)) return raw;
const tokens = raw.toLowerCase().split(/[._:/-]+/).filter(Boolean);
const kind = tokens[0];
const translated = tokens
.slice(1)
.map((token) => TOKEN_LABELS[token])
.filter(Boolean);
const subject = translated.join(" ");
if (kind === "price" || kind === "plan") {
const planSubject = translated.filter((token) => token !== "ماهانه" && token !== "سالانه" && token !== "آزمایشی").join(" ");
return `پلن ${planSubject || "اشتراک"}`.trim();
}
if (kind === "bundle") return subject ? `بسته شروع ${subject.replace("Starter", "").trim()}`.trim() : "بسته پیشنهادی";
if (kind === "product") return subject ? `سیستم ${subject}` : "سیستم کسب‌وکار";
if (kind === "capability" || kind === "feature") return subject || "قابلیت کاربردی";
if (kind === "tax") return subject || "مالیات";
if (kind === "coupon" || kind === "discount") return subject || "تخفیف";
return subject || "گزینه تجاری";
}
export function looksTechnical(value: string): boolean {
return (
/[a-z0-9]+[._:/][a-z0-9_.:/-]+/i.test(value) ||
/^[a-z][a-z0-9_]+$/i.test(value) ||
/\b(payload|json|registry|runtime|frontend|backend|metadata|heuristic|fallback|trace|api)\b/i.test(value)
);
}
export function formatMoney(amount?: number | null, currency?: string | null): string {
if (amount == null) return "استعلام قیمت";
const unit = !currency || /irr|rial/i.test(currency) ? "ریال" : enumLabel(currency);
return `${new Intl.NumberFormat("fa-IR").format(amount)} ${unit}`;
}
export function formatDate(value?: string | null): string {
if (!value) return "ثبت نشده";
const date = new Date(value);
return Number.isNaN(date.getTime())
? "ثبت نشده"
: new Intl.DateTimeFormat("fa-IR", { dateStyle: "medium" }).format(date);
}
export function friendlyMessage(message?: string | null, fallback = "در حال حاضر اطلاعاتی برای نمایش نداریم."): string {
if (!message || looksTechnical(message) || /\b(4\d\d|5\d\d)\b/.test(message)) return fallback;
return message.replace(/\[object Object\]/g, "اطلاعات تکمیلی");
}
export function recommendationReason(reason?: string | null): string {
if (!reason || looksTechnical(reason)) return "بر اساس پاسخ‌های شما";
const normalized = reason.toLowerCase();
if (/branch|شعب/.test(normalized)) return "چون چند شعبه دارید";
if (/delivery|ارسال/.test(normalized)) return "چون ارسال کالا دارید";
if (/online|website|وب/.test(normalized)) return "چون فروش آنلاین دارید";
if (/pos|in.person|حضوری|فروش/.test(normalized)) return "چون فروش حضوری دارید";
if (/loyal|باشگاه/.test(normalized)) return "چون باشگاه مشتریان برای شما مهم است";
if (/booking|reservation|رزرو/.test(normalized)) return "چون به مدیریت رزرو نیاز دارید";
return reason;
}
export function presentValue(value: unknown): string | string[] | Record<string, unknown> {
if (Array.isArray(value)) {
return value.map((item) =>
typeof item === "string" ? humanizeCommercialCode(item) : enumLabel(item)
);
}
if (value && typeof value === "object") return value as Record<string, unknown>;
if (typeof value === "string") return enumLabel(value);
if (typeof value === "boolean") return value ? "بله" : "خیر";
if (typeof value === "number") return new Intl.NumberFormat("fa-IR").format(value);
return "ثبت نشده";
}