Files
backend/src/business-settings/business-settings.types.ts
T
Alireza HassaniandCursor 3558c441b9 Add opt-in roll_call business module gate.
Keeps Roll Call off by default and blocks the API until super-admin enables it.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-07 14:15:43 +03:30

430 lines
12 KiB
TypeScript

import type { BusinessPrimaryColorId } from './business-primary-colors';
import { DEFAULT_BUSINESS_PRIMARY_COLOR_ID } from './business-primary-colors';
export type DashboardLocale = 'en' | 'fa';
export const DEFAULT_BUSINESS_DASHBOARD_LOCALE: DashboardLocale = 'fa';
export type DashboardThemeMode = 'light' | 'dark';
export const DEFAULT_BUSINESS_THEME_MODE: DashboardThemeMode = 'light';
export type BrandingSettings = {
primaryColor: BusinessPrimaryColorId;
/** Default UI language for business + customer dashboards. */
defaultLocale: DashboardLocale;
/** Dashboard surface theme (neutral light/dark). Independent of primary brand color. */
themeMode: DashboardThemeMode;
};
export type DashboardCommentsSettings = {
autoApprove: boolean;
};
export type DashboardExpertReviewsSettings = {
autoApprove: boolean;
};
/** Per-business dashboard settings. Add new sections here as the CMS grows. */
export type DashboardSettings = {
comments: DashboardCommentsSettings;
expertReviews: DashboardExpertReviewsSettings;
};
export type OrderProcessStep = {
id: string;
label: string;
labelFa: string;
color: string;
};
/** Supported online payment gateways (extend when adding adapters). */
export const PAYMENT_GATEWAY_IDS = [
'mellat',
'sep',
'snappay',
'digipay',
'zarinpal',
] as const;
export type PaymentGatewayId = (typeof PAYMENT_GATEWAY_IDS)[number];
/** Behpardakht Mellat credentials. */
export type MellatGatewaySettings = {
enabled: boolean;
terminalId: string;
username: string;
/** Stored secret — never returned in API responses (always empty string). */
password: string;
};
/** ZarinPal merchant credentials. */
export type ZarinpalGatewaySettings = {
enabled: boolean;
/** Merchant code from the ZarinPal panel. */
merchantId: string;
/** When true, use sandbox.zarinpal.com endpoints. */
sandbox: boolean;
};
/**
* Placeholder for gateways not yet wired (SEP, Snapp Pay, DigiPay).
* Credential fields are added when each adapter ships.
*/
export type StubGatewaySettings = {
enabled: boolean;
};
export type PaymentGatewaysSettings = {
mellat: MellatGatewaySettings;
sep: StubGatewaySettings;
snappay: StubGatewaySettings;
digipay: StubGatewaySettings;
zarinpal: ZarinpalGatewaySettings;
};
/** Online e-payment (website checkout). */
export type EPaymentSettings = {
/** Master switch — when false, websites must not offer e-payment. */
enabled: boolean;
/** Preferred gateway when several are enabled (null = first configured). */
defaultGateway: PaymentGatewayId | null;
gateways: PaymentGatewaysSettings;
};
/** Per-province override for the static flat shipping fee. */
export type StaticShippingProvinceException = {
provinceId: string;
amount: number;
};
/** Per-business store / sales settings. */
export type StoreSettings = {
onlineSellEnabled: boolean;
/** When true, Torob Product API v3 is available for this tenant (store module still required). */
torobEnabled: boolean;
orderProcessSteps: OrderProcessStep[];
ePayment: EPaymentSettings;
/**
* When true, every order uses `staticShippingAmount` and shipping cost groups are ignored.
*/
staticShippingEnabled: boolean;
/** Flat shipping fee in IRT applied to all orders when static shipping is enabled. */
staticShippingAmount: number;
/** Province-specific overrides of the static flat fee. */
staticShippingProvinceExceptions: StaticShippingProvinceException[];
};
/** Where website special-product carousels read from. */
export const SPECIAL_PRODUCTS_SOURCE_IDS = ['product', 'store_item'] as const;
export type SpecialProductsSource = (typeof SPECIAL_PRODUCTS_SOURCE_IDS)[number];
/** Per-business website CMS settings. */
export type WebsiteSettings = {
specialProductsSource: SpecialProductsSource;
/** Imported from GET https://{domain}/meshkee/sitemap-config.json */
sitemapConfig?: WebsiteSitemapConfig | null;
/** Site-wide Schema.org Organization / LocalBusiness settings (Phase A). */
schema: WebsiteSchemaSettings;
/** Custom <meta> tags + eNamad / Samandehi trust badges. */
tagsAndBadges: WebsiteTagsAndBadgesSettings;
};
/**
* Custom head meta tag.
* `name` = admin label only; `content` = full `<meta …>` HTML to inject.
*/
export type WebsiteMetaTag = {
id: string;
name: string;
content: string;
};
/** Fixed trust integrations (eNamad / Samandehi) — footer HTML only. */
export type WebsiteTrustBadgeSettings = {
/** Footer widget HTML; empty = not shown. */
embedHtml: string;
};
export type WebsiteTagsAndBadgesSettings = {
metaTags: WebsiteMetaTag[];
enamad: WebsiteTrustBadgeSettings;
samandehi: WebsiteTrustBadgeSettings;
};
export const DEFAULT_WEBSITE_TRUST_BADGE: WebsiteTrustBadgeSettings = {
embedHtml: '',
};
export const DEFAULT_WEBSITE_TAGS_AND_BADGES: WebsiteTagsAndBadgesSettings = {
metaTags: [],
enamad: { ...DEFAULT_WEBSITE_TRUST_BADGE },
samandehi: { ...DEFAULT_WEBSITE_TRUST_BADGE },
};
/** Schema.org @type for the site-wide entity JSON-LD. */
export const WEBSITE_SCHEMA_TYPE_IDS = ['Organization', 'LocalBusiness'] as const;
export type WebsiteSchemaType = (typeof WEBSITE_SCHEMA_TYPE_IDS)[number];
/**
* Business-admin overrides for site-wide structured data.
* Empty name/description fall back to business profile fields on the public API.
*/
export type WebsiteSchemaSettings = {
/** When false, websites should not emit Organization/LocalBusiness JSON-LD. */
enabled: boolean;
type: WebsiteSchemaType;
/** Optional override; empty → business `name`. */
name: string | null;
/** Optional override; empty → business `nameFa`. */
nameFa: string | null;
/** Optional override; empty → business `about`. */
description: string | null;
/** Optional LocalBusiness priceRange (e.g. "$$" or "۱۰۰٬۰۰۰–۵٬۰۰۰٬۰۰۰ تومان"). */
priceRange: string | null;
/** Extra sameAs profile URLs (merged with social links from business profile). */
sameAs: string[];
};
export const DEFAULT_WEBSITE_SCHEMA_SETTINGS: WebsiteSchemaSettings = {
enabled: true,
type: 'Organization',
name: null,
nameFa: null,
description: null,
priceRange: null,
sameAs: [],
};
export type SitemapChangefreq =
| 'always'
| 'hourly'
| 'daily'
| 'weekly'
| 'monthly'
| 'yearly'
| 'never';
export type SitemapStaticPageConfig = {
path: string;
changefreq?: SitemapChangefreq;
priority?: number;
};
export type SitemapPathTemplatesConfig = {
product?: string;
productCategory?: string;
blog?: string;
portfolio?: string;
storeItemByProduct?: string;
video?: string;
instruction?: string;
workshop?: string;
userProduct?: string;
};
export type WebsiteSitemapConfig = {
baseUrl?: string | null;
staticPages?: SitemapStaticPageConfig[];
templates?: SitemapPathTemplatesConfig;
syncedAt?: string | null;
};
/** Optional business-dashboard CMS modules. Always-on areas (customers, website, etc.) are not listed. */
export const BUSINESS_DASHBOARD_MODULE_IDS = [
'products',
'store',
'store_installments',
'portfolio',
'blog',
'warehouse',
'videos',
'instructions',
'workshops',
'finance',
'multilanguage_data',
'roll_call',
] as const;
/** Optional customer-dashboard modules. Always-on: home, profile, addresses, orders, favorites. */
export const CUSTOMER_MODULE_IDS = ['customer_products'] as const;
/** All optional modules stored in `settings.modules.enabled`. */
export const BUSINESS_MODULE_IDS = [
...BUSINESS_DASHBOARD_MODULE_IDS,
...CUSTOMER_MODULE_IDS,
] as const;
export type BusinessDashboardModuleId =
(typeof BUSINESS_DASHBOARD_MODULE_IDS)[number];
export type CustomerModuleId = (typeof CUSTOMER_MODULE_IDS)[number];
export type BusinessModuleId = (typeof BUSINESS_MODULE_IDS)[number];
/** Home dashboard chart types (super-admin selectable). */
export const HOME_CHART_IDS = [
'none',
'orders_30d',
'customers_joined_1y',
'blog_views_30d',
'products_added_1y',
'product_views_30d',
'website_views_30d',
'portfolio_views_30d',
] as const;
export type HomeChartId = (typeof HOME_CHART_IDS)[number];
export type ModulesSettings = {
enabled: BusinessModuleId[];
/** Two home-page chart slots (left, right). */
charts: [HomeChartId, HomeChartId];
};
/** Per-business SMS sender (Gama shortcode). Empty → Meshkee advertising number. */
export type SmsSettings = {
/** Outbound shortcode for customer SMS, e.g. "5000123456". */
senderNumber: string | null;
};
/** Top-level business settings stored in `businesses.settings` JSONB. */
export type BusinessSettings = {
branding: BrandingSettings;
dashboard: DashboardSettings;
store: StoreSettings;
modules: ModulesSettings;
website: WebsiteSettings;
sms: SmsSettings;
};
export const DEFAULT_ORDER_PROCESS_STEPS: OrderProcessStep[] = [
{
id: 'awaiting-payment',
label: 'Waiting for payment',
labelFa: 'در انتظار پرداخت',
color: '#64748B',
},
{
id: 'payment-successful',
label: 'Payment successful',
labelFa: 'پرداخت موفق',
color: '#10B981',
},
{
id: 'processing',
label: 'Under processing',
labelFa: 'در حال پردازش',
color: '#3B82F6',
},
{
id: 'ready-for-shipping',
label: 'Ready for shipping',
labelFa: 'آماده ارسال',
color: '#F59E0B',
},
{
id: 'shipped',
label: 'Shipped',
labelFa: 'ارسال‌شده',
color: '#8B5CF6',
},
{
id: 'delivered',
label: 'Delivered',
labelFa: 'تحویل‌شده',
color: '#22C55E',
},
];
/** Modules enabled when a new business is created (super-admin). */
export const DEFAULT_NEW_BUSINESS_MODULES: BusinessModuleId[] = [
'products',
'blog',
'finance',
];
/**
* Existing tenants without `settings.modules` keep every business-dashboard module
* enabled except opt-in modules. Customer modules stay opt-in.
*/
export const DEFAULT_ENABLED_BUSINESS_MODULES: BusinessModuleId[] =
BUSINESS_DASHBOARD_MODULE_IDS.filter(
(id) =>
id !== 'workshops' &&
id !== 'multilanguage_data' &&
id !== 'store_installments' &&
id !== 'roll_call',
);
export const DEFAULT_HOME_CHARTS: [HomeChartId, HomeChartId] = [
'orders_30d',
'customers_joined_1y',
];
export const DEFAULT_MELLAT_GATEWAY_SETTINGS: MellatGatewaySettings = {
enabled: false,
terminalId: '',
username: '',
password: '',
};
export const DEFAULT_STUB_GATEWAY_SETTINGS: StubGatewaySettings = {
enabled: false,
};
export const DEFAULT_ZARINPAL_GATEWAY_SETTINGS: ZarinpalGatewaySettings = {
enabled: false,
merchantId: '',
sandbox: false,
};
export const DEFAULT_EPAYMENT_SETTINGS: EPaymentSettings = {
enabled: false,
defaultGateway: null,
gateways: {
mellat: { ...DEFAULT_MELLAT_GATEWAY_SETTINGS },
sep: { ...DEFAULT_STUB_GATEWAY_SETTINGS },
snappay: { ...DEFAULT_STUB_GATEWAY_SETTINGS },
digipay: { ...DEFAULT_STUB_GATEWAY_SETTINGS },
zarinpal: { ...DEFAULT_ZARINPAL_GATEWAY_SETTINGS },
},
};
export const DEFAULT_BUSINESS_SETTINGS: BusinessSettings = {
branding: {
primaryColor: DEFAULT_BUSINESS_PRIMARY_COLOR_ID,
defaultLocale: DEFAULT_BUSINESS_DASHBOARD_LOCALE,
themeMode: DEFAULT_BUSINESS_THEME_MODE,
},
dashboard: {
comments: { autoApprove: false },
expertReviews: { autoApprove: false },
},
store: {
onlineSellEnabled: true,
torobEnabled: false,
orderProcessSteps: DEFAULT_ORDER_PROCESS_STEPS,
ePayment: { ...DEFAULT_EPAYMENT_SETTINGS },
staticShippingEnabled: true,
staticShippingAmount: 0,
staticShippingProvinceExceptions: [],
},
modules: {
enabled: DEFAULT_ENABLED_BUSINESS_MODULES,
charts: DEFAULT_HOME_CHARTS,
},
website: {
specialProductsSource: 'store_item',
schema: { ...DEFAULT_WEBSITE_SCHEMA_SETTINGS },
tagsAndBadges: {
metaTags: [],
enamad: { ...DEFAULT_WEBSITE_TRUST_BADGE },
samandehi: { ...DEFAULT_WEBSITE_TRUST_BADGE },
},
},
sms: {
senderNumber: null,
},
};