3206 lines
72 KiB
TypeScript
3206 lines
72 KiB
TypeScript
export type UserRole = "owner" | "admin" | "sales" | "support";
|
|
|
|
export const USER_ROLES: UserRole[] = ["owner", "admin", "sales", "support"];
|
|
|
|
export const USER_ROLE_LABELS: Record<UserRole, string> = {
|
|
owner: "مالک",
|
|
admin: "مدیر",
|
|
sales: "فروش",
|
|
support: "پشتیبانی",
|
|
};
|
|
|
|
/** تنظیمات سازمانی و مدیریت کاربران */
|
|
export function canManageSettings(role: UserRole | string | null | undefined): boolean {
|
|
return role === "owner" || role === "admin";
|
|
}
|
|
|
|
export function canManageUsers(role: UserRole | string | null | undefined): boolean {
|
|
return role === "owner" || role === "admin";
|
|
}
|
|
|
|
/** مشاهده همه پروندههای سازمان (نه فقط مالکیت خود) */
|
|
export function canViewAllRecords(role: UserRole | string | null | undefined): boolean {
|
|
return role === "owner" || role === "admin";
|
|
}
|
|
|
|
/** تغییر مالک پرونده */
|
|
export function canReassignRecords(role: UserRole | string | null | undefined): boolean {
|
|
return role === "owner" || role === "admin";
|
|
}
|
|
|
|
export interface AuthUserDto {
|
|
id: string;
|
|
email: string;
|
|
mobile?: string;
|
|
fullName: string;
|
|
role: UserRole;
|
|
organizationId: string;
|
|
organizationName?: string;
|
|
hasAvatar?: boolean;
|
|
/** مالک محیط فعلی — اشتراک متعلق به مالک است نه عضو زیرمجموعه */
|
|
isOrgOwner?: boolean;
|
|
enabledModules?: OrgModuleKey[];
|
|
/** خلاصه اشتراک مالک محیط (برای اعضا فقط جهت نمایش/گیت ورود) */
|
|
subscription?: SubscriptionSummaryDto | null;
|
|
}
|
|
|
|
export type BillingPlanCode =
|
|
| "trial"
|
|
| "monthly"
|
|
| "quarterly"
|
|
| "semiannual"
|
|
| "yearly"
|
|
| "custom";
|
|
|
|
export type BillingAccountStatus = "active" | "expired" | "suspended";
|
|
|
|
export const BILLING_PLAN_CODES: BillingPlanCode[] = [
|
|
"trial",
|
|
"monthly",
|
|
"quarterly",
|
|
"semiannual",
|
|
"yearly",
|
|
"custom",
|
|
];
|
|
|
|
export const BILLING_PLAN_LABELS: Record<BillingPlanCode, string> = {
|
|
trial: "دمو ۱۵ روزه",
|
|
monthly: "ماهانه",
|
|
quarterly: "سهماهه",
|
|
semiannual: "ششماهه",
|
|
yearly: "یکساله",
|
|
custom: "سفارشی",
|
|
};
|
|
|
|
/** مدت پیشفرض پلنها (روز) — custom ندارد */
|
|
export const BILLING_PLAN_DAYS: Record<Exclude<BillingPlanCode, "custom">, number> = {
|
|
trial: 15,
|
|
monthly: 30,
|
|
quarterly: 90,
|
|
semiannual: 180,
|
|
yearly: 365,
|
|
};
|
|
|
|
export const BILLING_PLAN_DEFAULT_MAX_ORGS: Record<
|
|
Exclude<BillingPlanCode, "custom">,
|
|
number
|
|
> = {
|
|
trial: 1,
|
|
monthly: 3,
|
|
quarterly: 5,
|
|
semiannual: 10,
|
|
yearly: 20,
|
|
};
|
|
|
|
export type OrgModuleKey =
|
|
| "customers"
|
|
| "pipeline"
|
|
| "products"
|
|
| "invoices"
|
|
| "quotes"
|
|
| "commissions"
|
|
| "sales_forecast"
|
|
| "activities"
|
|
| "tickets"
|
|
| "workflow_automations"
|
|
| "message_automations"
|
|
| "lead_forms"
|
|
| "project_boards"
|
|
| "inventory"
|
|
| "cheques"
|
|
| "attachments"
|
|
| "loyalty"
|
|
| "reports"
|
|
| "notes"
|
|
| "tahesab"
|
|
| "custom_fields"
|
|
| "currencies"
|
|
| "team_settings"
|
|
| "faq"
|
|
| "audit_logs"
|
|
| "saved_views"
|
|
| "public_api"
|
|
| "sms_campaigns"
|
|
| "teams"
|
|
| "woocommerce";
|
|
|
|
export const ORG_MODULE_KEYS: OrgModuleKey[] = [
|
|
"customers",
|
|
"pipeline",
|
|
"products",
|
|
"invoices",
|
|
"quotes",
|
|
"commissions",
|
|
"sales_forecast",
|
|
"activities",
|
|
"tickets",
|
|
"workflow_automations",
|
|
"message_automations",
|
|
"lead_forms",
|
|
"project_boards",
|
|
"inventory",
|
|
"cheques",
|
|
"attachments",
|
|
"loyalty",
|
|
"reports",
|
|
"notes",
|
|
"tahesab",
|
|
"custom_fields",
|
|
"currencies",
|
|
"team_settings",
|
|
"faq",
|
|
"audit_logs",
|
|
"saved_views",
|
|
"public_api",
|
|
"sms_campaigns",
|
|
"teams",
|
|
"woocommerce",
|
|
];
|
|
|
|
export const ORG_MODULE_LABELS: Record<OrgModuleKey, string> = {
|
|
customers: "مشتریان",
|
|
pipeline: "کاریز فروش",
|
|
products: "محصولات",
|
|
invoices: "فاکتورها",
|
|
quotes: "پیشفاکتورها",
|
|
commissions: "پورسانت",
|
|
sales_forecast: "هدف و پیشبینی",
|
|
activities: "فعالیتها",
|
|
tickets: "تیکتها",
|
|
workflow_automations: "اتوماسیون گردش کار",
|
|
message_automations: "اتوماسیون پیامک",
|
|
lead_forms: "فرمساز سرنخ",
|
|
project_boards: "کاریز پروژه",
|
|
inventory: "انبار",
|
|
cheques: "چکها",
|
|
attachments: "پیوستها",
|
|
loyalty: "باشگاه مشتریان",
|
|
reports: "گزارشها",
|
|
notes: "یادداشتها",
|
|
tahesab: "تهحساب",
|
|
custom_fields: "فیلدهای سفارشی",
|
|
currencies: "ارزها",
|
|
team_settings: "تیم و تنظیمات سازمان",
|
|
faq: "FAQ",
|
|
audit_logs: "لاگ فعالیت کاربران",
|
|
saved_views: "فیلترهای ذخیرهشده",
|
|
public_api: "API و وبهوک",
|
|
sms_campaigns: "پیامک گروهی",
|
|
teams: "تیمها و دیدپذیری",
|
|
woocommerce: "ووکامرس",
|
|
};
|
|
|
|
export const ORG_MODULE_GROUPS: Array<{
|
|
title: string;
|
|
keys: OrgModuleKey[];
|
|
}> = [
|
|
{ title: "مشتریان", keys: ["customers"] },
|
|
{
|
|
title: "فروش",
|
|
keys: [
|
|
"pipeline",
|
|
"products",
|
|
"invoices",
|
|
"quotes",
|
|
"commissions",
|
|
"sales_forecast",
|
|
],
|
|
},
|
|
{ title: "فعالیتها", keys: ["activities", "notes"] },
|
|
{ title: "پشتیبانی", keys: ["tickets", "faq"] },
|
|
{
|
|
title: "اتوماسیون",
|
|
keys: [
|
|
"workflow_automations",
|
|
"message_automations",
|
|
"sms_campaigns",
|
|
],
|
|
},
|
|
{
|
|
title: "ابزارها",
|
|
keys: [
|
|
"lead_forms",
|
|
"project_boards",
|
|
"inventory",
|
|
"cheques",
|
|
"attachments",
|
|
"loyalty",
|
|
"reports",
|
|
"saved_views",
|
|
"woocommerce",
|
|
],
|
|
},
|
|
{
|
|
title: "تنظیمات",
|
|
keys: [
|
|
"team_settings",
|
|
"teams",
|
|
"custom_fields",
|
|
"currencies",
|
|
"tahesab",
|
|
"audit_logs",
|
|
"public_api",
|
|
],
|
|
},
|
|
];
|
|
|
|
export function isOrgModuleKey(value: string): value is OrgModuleKey {
|
|
return (ORG_MODULE_KEYS as readonly string[]).includes(value);
|
|
}
|
|
|
|
export function defaultEnabledModules(): OrgModuleKey[] {
|
|
return [...ORG_MODULE_KEYS];
|
|
}
|
|
|
|
export interface SubscriptionSummaryDto {
|
|
planCode: BillingPlanCode;
|
|
status: BillingAccountStatus;
|
|
startsAt: string;
|
|
expiresAt: string;
|
|
maxOrganizations: number;
|
|
daysRemaining: number;
|
|
warn: boolean;
|
|
}
|
|
|
|
export interface BillingAccountDto {
|
|
id: string;
|
|
ownerUserId: string;
|
|
planCode: BillingPlanCode;
|
|
status: BillingAccountStatus;
|
|
startsAt: string;
|
|
expiresAt: string;
|
|
maxOrganizations: number;
|
|
externalPaymentRef?: string | null;
|
|
notes?: string | null;
|
|
daysRemaining: number;
|
|
warn: boolean;
|
|
}
|
|
|
|
export interface ApplyBillingPlanRequest {
|
|
planCode: BillingPlanCode;
|
|
maxOrganizations?: number;
|
|
expiresAt?: string;
|
|
notes?: string | null;
|
|
status?: BillingAccountStatus;
|
|
}
|
|
|
|
export interface UpdateOrgModulesRequest {
|
|
enabledModules: OrgModuleKey[];
|
|
}
|
|
|
|
export interface OrgUserDto {
|
|
id: string;
|
|
email: string;
|
|
mobile?: string | null;
|
|
fullName: string;
|
|
role: UserRole;
|
|
orgRoleId?: string | null;
|
|
orgRoleName?: string;
|
|
orgRoleColor?: string;
|
|
isActive?: boolean;
|
|
lastLoginAt?: string | null;
|
|
hasAvatar?: boolean;
|
|
commissionPercent?: number;
|
|
/** own = فقط خود | team = همتیمیها | org = کل سازمان */
|
|
dataVisibility?: DataVisibility;
|
|
createdAt: string;
|
|
}
|
|
|
|
export type DataVisibility = "own" | "team" | "org";
|
|
|
|
export const DATA_VISIBILITIES: DataVisibility[] = ["own", "team", "org"];
|
|
|
|
export const DATA_VISIBILITY_LABELS: Record<DataVisibility, string> = {
|
|
own: "فقط پروندههای خود",
|
|
team: "تیمهای عضو",
|
|
org: "کل سازمان",
|
|
};
|
|
|
|
export interface UpdateUserVisibilityRequest {
|
|
dataVisibility: DataVisibility;
|
|
}
|
|
|
|
export type RolePermissionKey =
|
|
| "customers"
|
|
| "sales"
|
|
| "billing"
|
|
| "products"
|
|
| "activities"
|
|
| "notes"
|
|
| "reports"
|
|
| "audit";
|
|
|
|
export const ROLE_PERMISSION_ITEMS: Array<{
|
|
key: RolePermissionKey;
|
|
label: string;
|
|
}> = [
|
|
{ key: "customers", label: "مشتریان" },
|
|
{ key: "sales", label: "فروش و معاملات" },
|
|
{ key: "billing", label: "پیشفاکتورها و فاکتورها" },
|
|
{ key: "products", label: "محصولات و خدمات" },
|
|
{ key: "activities", label: "فعالیتها" },
|
|
{ key: "notes", label: "یادداشتها" },
|
|
{ key: "reports", label: "گزارشها" },
|
|
{ key: "audit", label: "لاگ فعالیت کاربران" },
|
|
];
|
|
|
|
export interface OrgRoleMemberDto {
|
|
id: string;
|
|
fullName: string;
|
|
hasAvatar: boolean;
|
|
}
|
|
|
|
export interface OrgRoleDto {
|
|
id: string;
|
|
name: string;
|
|
description: string;
|
|
color: string;
|
|
permissions: RolePermissionKey[];
|
|
systemKey: UserRole | null;
|
|
userCount: number;
|
|
members: OrgRoleMemberDto[];
|
|
}
|
|
|
|
export interface CreateOrgRoleRequest {
|
|
name: string;
|
|
description?: string;
|
|
color?: string;
|
|
permissions?: RolePermissionKey[];
|
|
}
|
|
|
|
export interface UpdateOrgRoleRequest {
|
|
name?: string;
|
|
description?: string;
|
|
color?: string;
|
|
permissions?: RolePermissionKey[];
|
|
}
|
|
|
|
export interface InviteUserRequest {
|
|
mobile: string;
|
|
fullName: string;
|
|
password?: string;
|
|
role?: Exclude<UserRole, "owner">;
|
|
orgRoleId?: string;
|
|
}
|
|
|
|
export interface UpdateUserRoleRequest {
|
|
role?: UserRole;
|
|
orgRoleId?: string;
|
|
}
|
|
|
|
export interface UpdateUserStatusRequest {
|
|
isActive: boolean;
|
|
}
|
|
|
|
export interface UpdateUserCommissionRequest {
|
|
commissionPercent: number;
|
|
}
|
|
|
|
export interface UpdateProfileRequest {
|
|
fullName: string;
|
|
email: string;
|
|
mobile: string;
|
|
}
|
|
|
|
export type DealStatus = "open" | "won" | "lost";
|
|
|
|
export interface ApiErrorBody {
|
|
statusCode: number;
|
|
message: string | string[];
|
|
error?: string;
|
|
}
|
|
|
|
export interface LoginRequest {
|
|
/** ایمیل یا شماره موبایل */
|
|
identifier: string;
|
|
password: string;
|
|
}
|
|
|
|
export interface RegisterOrganizationRequest {
|
|
organizationName: string;
|
|
fullName: string;
|
|
email: string;
|
|
mobile: string;
|
|
password: string;
|
|
passwordConfirm: string;
|
|
businessField: BusinessField;
|
|
referralCode?: string;
|
|
acceptedTerms?: boolean;
|
|
}
|
|
|
|
export interface LoginChallengeResponse {
|
|
challengeId: string;
|
|
channel: 'email' | 'mobile';
|
|
expiresInSeconds: number;
|
|
maskedDestination?: string;
|
|
/** فقط در محیط توسعه / AUTH_RETURN_OTP */
|
|
devCode?: string;
|
|
}
|
|
|
|
export interface RegisterChallengeResponse {
|
|
challengeId: string;
|
|
expiresInSeconds: number;
|
|
maskedEmail?: string;
|
|
maskedMobile?: string;
|
|
/** وقتی سرویس ایمیل خاموش است false — فقط OTP موبایل لازم است */
|
|
emailOtpRequired?: boolean;
|
|
devEmailCode?: string;
|
|
devMobileCode?: string;
|
|
}
|
|
|
|
export interface LoginVerifyRequest {
|
|
challengeId: string;
|
|
code: string;
|
|
}
|
|
|
|
export interface RegisterVerifyRequest {
|
|
challengeId: string;
|
|
emailCode?: string;
|
|
mobileCode: string;
|
|
}
|
|
|
|
export interface LoginResponse {
|
|
accessToken: string;
|
|
user: AuthUserDto;
|
|
}
|
|
|
|
export interface OrganizationMembershipDto {
|
|
id: string;
|
|
name: string;
|
|
role: UserRole;
|
|
orgRoleName?: string;
|
|
}
|
|
|
|
export interface LoginOrganizationChoiceResponse {
|
|
requiresOrganizationChoice: true;
|
|
selectionToken: string;
|
|
organizations: OrganizationMembershipDto[];
|
|
}
|
|
|
|
export type LoginResult = LoginResponse | LoginOrganizationChoiceResponse;
|
|
|
|
export function isLoginOrganizationChoice(
|
|
data: LoginResult,
|
|
): data is LoginOrganizationChoiceResponse {
|
|
return (
|
|
typeof data === "object" &&
|
|
data !== null &&
|
|
"requiresOrganizationChoice" in data &&
|
|
data.requiresOrganizationChoice === true
|
|
);
|
|
}
|
|
|
|
export interface SelectOrganizationRequest {
|
|
selectionToken: string;
|
|
organizationId: string;
|
|
}
|
|
|
|
export interface SwitchOrganizationRequest {
|
|
organizationId: string;
|
|
}
|
|
|
|
export interface CreateOrganizationRequest {
|
|
name: string;
|
|
businessField?: BusinessField;
|
|
}
|
|
|
|
export interface OrganizationDto {
|
|
id: string;
|
|
name: string;
|
|
slug: string;
|
|
businessField: BusinessField | null;
|
|
baseCurrency: string;
|
|
enabledCurrencies: string[];
|
|
exchangeRates: ExchangeRateDto[];
|
|
enabledModules: OrgModuleKey[];
|
|
}
|
|
|
|
export interface UpdateOrganizationRequest {
|
|
name?: string;
|
|
businessField?: BusinessField;
|
|
}
|
|
|
|
/** زمینه فعالیت سازمان — همتراز صنایع دیدار */
|
|
export const BUSINESS_FIELDS = [
|
|
"education",
|
|
"beauty",
|
|
"real_estate",
|
|
"construction",
|
|
"advertising",
|
|
"insurance",
|
|
"consulting",
|
|
"startup",
|
|
"medical_devices",
|
|
"travel",
|
|
"contractor",
|
|
"trading",
|
|
"manufacturing",
|
|
"personal",
|
|
"tech",
|
|
"legal",
|
|
"repair",
|
|
"retail",
|
|
"jewelry",
|
|
"other",
|
|
] as const;
|
|
|
|
export type BusinessField = (typeof BUSINESS_FIELDS)[number];
|
|
|
|
export const BUSINESS_FIELD_LABELS: Record<BusinessField, string> = {
|
|
education: "آموزشگاه و مؤسسات آموزشی",
|
|
beauty: "کلینیک زیبایی و آرایشگاه",
|
|
real_estate: "املاک و مشاورین مسکن",
|
|
construction: "صنعت ساختمان",
|
|
advertising: "تبلیغات و مشاوره کسبوکار",
|
|
insurance: "بیمه",
|
|
consulting: "شرکتهای مشاور",
|
|
startup: "استارتاپ و کسبوکار آنلاین",
|
|
medical_devices: "تجهیزات پزشکی",
|
|
travel: "گردشگری و مهاجرت",
|
|
contractor: "پیمانکاری و پروژهای",
|
|
trading: "بازرگانی",
|
|
manufacturing: "تولیدی و صنعتی",
|
|
personal: "کسبوکار شخصی و خانگی",
|
|
tech: "نرمافزار و سختافزار",
|
|
legal: "وکلا و مشاورین حقوقی",
|
|
repair: "خدمات و تعمیرات",
|
|
retail: "خردهفروشی",
|
|
jewelry: "طلا و جواهر",
|
|
other: "سایر",
|
|
};
|
|
|
|
export function isBusinessField(value: string): value is BusinessField {
|
|
return (BUSINESS_FIELDS as readonly string[]).includes(value);
|
|
}
|
|
|
|
export interface ExchangeRateDto {
|
|
currency: string;
|
|
rateToBase: number;
|
|
updatedAt: string;
|
|
}
|
|
|
|
export interface UpdateOrgCurrenciesRequest {
|
|
baseCurrency?: string;
|
|
enabledCurrencies?: string[];
|
|
/** نرخها نسبت به ارز پایه؛ ارز پایه نیازی به نرخ ندارد */
|
|
rates?: Array<{ currency: string; rateToBase: number }>;
|
|
}
|
|
|
|
export const CURRENCY_CATALOG: Array<{
|
|
code: string;
|
|
labelFa: string;
|
|
suffix: string;
|
|
}> = [
|
|
{ code: "IRR", labelFa: "ریال ایران", suffix: "ریال" },
|
|
{ code: "USD", labelFa: "دلار آمریکا", suffix: "دلار" },
|
|
{ code: "EUR", labelFa: "یورو", suffix: "یورو" },
|
|
{ code: "AED", labelFa: "درهم امارات", suffix: "درهم" },
|
|
{ code: "TRY", labelFa: "لیر ترکیه", suffix: "لیر" },
|
|
{ code: "GBP", labelFa: "پوند انگلیس", suffix: "پوند" },
|
|
];
|
|
|
|
export function currencySuffix(code: string): string {
|
|
return CURRENCY_CATALOG.find((c) => c.code === code)?.suffix ?? code;
|
|
}
|
|
|
|
export function formatMoneyAmount(amount: number, currency = "IRR"): string {
|
|
try {
|
|
const n = new Intl.NumberFormat("fa-IR").format(amount);
|
|
return `${n} ${currencySuffix(currency)}`;
|
|
} catch {
|
|
return `${amount} ${currency}`;
|
|
}
|
|
}
|
|
|
|
export interface HealthResponse {
|
|
status: "ok";
|
|
service: string;
|
|
timestamp: string;
|
|
}
|
|
|
|
export interface CompanySummaryDto {
|
|
id: string;
|
|
name: string;
|
|
}
|
|
|
|
export interface CompanyDto {
|
|
id: string;
|
|
name: string;
|
|
nationalId: string | null;
|
|
phone: string | null;
|
|
email: string | null;
|
|
website: string | null;
|
|
address: string | null;
|
|
notes: string | null;
|
|
tags: string[];
|
|
customFields: Record<string, unknown>;
|
|
peopleCount: number;
|
|
owner: UserSummaryDto | null;
|
|
createdAt: string;
|
|
updatedAt: string;
|
|
}
|
|
|
|
export interface CreateCompanyRequest {
|
|
name: string;
|
|
nationalId?: string;
|
|
phone?: string;
|
|
email?: string;
|
|
website?: string;
|
|
address?: string;
|
|
notes?: string;
|
|
tags?: string[];
|
|
customFields?: Record<string, unknown>;
|
|
ownerId?: string | null;
|
|
}
|
|
|
|
export type UpdateCompanyRequest = Partial<CreateCompanyRequest>;
|
|
|
|
export interface LoyaltyTierSummaryDto {
|
|
id: string;
|
|
name: string;
|
|
minPoints: number;
|
|
color: string | null;
|
|
}
|
|
|
|
export const PERSON_MOODS = ["newcomer", "happy", "unhappy", "special"] as const;
|
|
export type PersonMood = (typeof PERSON_MOODS)[number];
|
|
|
|
export const PERSON_MOOD_LABELS: Record<PersonMood, string> = {
|
|
newcomer: "مشتری تازه وارد",
|
|
happy: "مشتری خوشحال",
|
|
unhappy: "مشتری ناراضی",
|
|
special: "مشتری خاص",
|
|
};
|
|
|
|
export interface PersonDto {
|
|
id: string;
|
|
firstName: string;
|
|
lastName: string;
|
|
fullName: string;
|
|
email: string | null;
|
|
phone: string | null;
|
|
mobile: string | null;
|
|
position: string | null;
|
|
website: string | null;
|
|
referrer: string | null;
|
|
notes: string | null;
|
|
mood: PersonMood;
|
|
tags: string[];
|
|
customFields: Record<string, unknown>;
|
|
loyaltyPoints: number;
|
|
loyaltyTier: LoyaltyTierSummaryDto | null;
|
|
company: CompanySummaryDto | null;
|
|
owner: UserSummaryDto | null;
|
|
createdAt: string;
|
|
updatedAt: string;
|
|
}
|
|
|
|
export interface CreatePersonRequest {
|
|
firstName?: string;
|
|
lastName?: string;
|
|
email?: string;
|
|
phone?: string;
|
|
mobile?: string;
|
|
position?: string;
|
|
website?: string;
|
|
referrer?: string;
|
|
notes?: string;
|
|
mood?: PersonMood;
|
|
tags?: string[];
|
|
companyId?: string | null;
|
|
customFields?: Record<string, unknown>;
|
|
ownerId?: string | null;
|
|
}
|
|
|
|
export type UpdatePersonRequest = Partial<CreatePersonRequest>;
|
|
|
|
export interface PaginatedResponse<T> {
|
|
items: T[];
|
|
total: number;
|
|
page: number;
|
|
limit: number;
|
|
}
|
|
|
|
export interface PersonSummaryDto {
|
|
id: string;
|
|
fullName: string;
|
|
}
|
|
|
|
export interface UserSummaryDto {
|
|
id: string;
|
|
fullName: string;
|
|
}
|
|
|
|
export interface PipelineStageDto {
|
|
id: string;
|
|
name: string;
|
|
position: number;
|
|
isWon: boolean;
|
|
isLost: boolean;
|
|
}
|
|
|
|
export interface PipelineDto {
|
|
id: string;
|
|
name: string;
|
|
isDefault: boolean;
|
|
stages: PipelineStageDto[];
|
|
}
|
|
|
|
export interface DealDto {
|
|
id: string;
|
|
title: string;
|
|
amount: number | null;
|
|
currency: string;
|
|
status: DealStatus;
|
|
source: string | null;
|
|
lostReason: string | null;
|
|
notes: string | null;
|
|
tags: string[];
|
|
isStale: boolean;
|
|
isRotten: boolean;
|
|
customFields: Record<string, unknown>;
|
|
expectedCloseAt: string | null;
|
|
position: number;
|
|
pipelineId: string;
|
|
stageId: string;
|
|
person: PersonSummaryDto | null;
|
|
company: CompanySummaryDto | null;
|
|
owner: UserSummaryDto | null;
|
|
lineItems: DealLineItemDto[];
|
|
createdAt: string;
|
|
updatedAt: string;
|
|
}
|
|
|
|
export interface PipelineBoardStageDto extends PipelineStageDto {
|
|
deals: DealDto[];
|
|
totalAmount: number;
|
|
}
|
|
|
|
export interface PipelineBoardDto {
|
|
pipeline: PipelineDto;
|
|
stages: PipelineBoardStageDto[];
|
|
}
|
|
|
|
export interface CreatePipelineRequest {
|
|
name: string;
|
|
}
|
|
|
|
export interface CreatePipelineStageRequest {
|
|
name: string;
|
|
}
|
|
|
|
export interface UpdatePipelineStageRequest {
|
|
name?: string;
|
|
}
|
|
|
|
export interface ReorderPipelineStagesRequest {
|
|
stageIds: string[];
|
|
}
|
|
|
|
export interface CreateDealRequest {
|
|
title: string;
|
|
pipelineId?: string;
|
|
stageId?: string;
|
|
amount?: number | null;
|
|
currency?: string;
|
|
source?: string;
|
|
notes?: string;
|
|
tags?: string[];
|
|
isStale?: boolean;
|
|
isRotten?: boolean;
|
|
expectedCloseAt?: string | null;
|
|
personId?: string | null;
|
|
companyId?: string | null;
|
|
ownerId?: string | null;
|
|
customFields?: Record<string, unknown>;
|
|
lineItems?: DealLineItemInput[];
|
|
}
|
|
|
|
export interface UpdateDealRequest {
|
|
title?: string;
|
|
amount?: number | null;
|
|
currency?: string;
|
|
source?: string;
|
|
notes?: string;
|
|
tags?: string[];
|
|
isStale?: boolean;
|
|
isRotten?: boolean;
|
|
lostReason?: string | null;
|
|
expectedCloseAt?: string | null;
|
|
personId?: string | null;
|
|
companyId?: string | null;
|
|
ownerId?: string | null;
|
|
customFields?: Record<string, unknown>;
|
|
lineItems?: DealLineItemInput[];
|
|
}
|
|
|
|
export interface MoveDealRequest {
|
|
stageId: string;
|
|
position?: number;
|
|
}
|
|
|
|
export type ActivityType = "call" | "meeting" | "task" | "note";
|
|
export type ActivityStatus = "open" | "done" | "canceled";
|
|
export type ActivityPriority = "very_low" | "low" | "medium" | "high" | "very_high";
|
|
export type ActivityFilter = "today" | "overdue" | "upcoming" | "done" | "all";
|
|
|
|
export type CustomFieldEntity = "person" | "company" | "deal";
|
|
export type CustomFieldType = "text" | "number" | "date" | "boolean" | "select";
|
|
|
|
export interface CustomFieldDefinitionDto {
|
|
id: string;
|
|
entityType: CustomFieldEntity;
|
|
key: string;
|
|
label: string;
|
|
fieldType: CustomFieldType;
|
|
options: string[];
|
|
required: boolean;
|
|
position: number;
|
|
isActive: boolean;
|
|
createdAt: string;
|
|
updatedAt: string;
|
|
}
|
|
|
|
export interface CreateCustomFieldRequest {
|
|
entityType: CustomFieldEntity;
|
|
label: string;
|
|
key?: string;
|
|
fieldType?: CustomFieldType;
|
|
options?: string[];
|
|
required?: boolean;
|
|
position?: number;
|
|
isActive?: boolean;
|
|
}
|
|
|
|
export interface UpdateCustomFieldRequest {
|
|
label?: string;
|
|
fieldType?: CustomFieldType;
|
|
options?: string[];
|
|
required?: boolean;
|
|
position?: number;
|
|
isActive?: boolean;
|
|
}
|
|
|
|
export interface DealSummaryDto {
|
|
id: string;
|
|
title: string;
|
|
}
|
|
|
|
export interface ActivityDto {
|
|
id: string;
|
|
type: ActivityType;
|
|
status: ActivityStatus;
|
|
priority: ActivityPriority;
|
|
title: string;
|
|
description: string | null;
|
|
dueAt: string | null;
|
|
durationMinutes: number | null;
|
|
reminderAt: string | null;
|
|
reminderNotifyApp: boolean;
|
|
reminderNotifySms: boolean;
|
|
completedAt: string | null;
|
|
tags: string[];
|
|
person: PersonSummaryDto | null;
|
|
company: CompanySummaryDto | null;
|
|
deal: DealSummaryDto | null;
|
|
assignee: UserSummaryDto | null;
|
|
createdBy: UserSummaryDto | null;
|
|
createdAt: string;
|
|
updatedAt: string;
|
|
}
|
|
|
|
export interface CreateActivityRequest {
|
|
title: string;
|
|
type?: ActivityType;
|
|
priority?: ActivityPriority;
|
|
description?: string;
|
|
dueAt?: string | null;
|
|
durationMinutes?: number | null;
|
|
reminderAt?: string | null;
|
|
reminderNotifyApp?: boolean;
|
|
reminderNotifySms?: boolean;
|
|
tags?: string[];
|
|
personId?: string | null;
|
|
companyId?: string | null;
|
|
dealId?: string | null;
|
|
assigneeId?: string | null;
|
|
}
|
|
|
|
export interface UpdateActivityRequest {
|
|
title?: string;
|
|
type?: ActivityType;
|
|
status?: ActivityStatus;
|
|
priority?: ActivityPriority;
|
|
description?: string | null;
|
|
dueAt?: string | null;
|
|
durationMinutes?: number | null;
|
|
reminderAt?: string | null;
|
|
reminderNotifyApp?: boolean;
|
|
reminderNotifySms?: boolean;
|
|
tags?: string[];
|
|
personId?: string | null;
|
|
companyId?: string | null;
|
|
dealId?: string | null;
|
|
assigneeId?: string | null;
|
|
}
|
|
|
|
export interface ActivitySummaryDto {
|
|
today: ActivityDto[];
|
|
overdue: ActivityDto[];
|
|
upcoming: ActivityDto[];
|
|
todayCount: number;
|
|
overdueCount: number;
|
|
}
|
|
|
|
export type NoteType =
|
|
| "general"
|
|
| "call_result"
|
|
| "meeting"
|
|
| "important"
|
|
| "warning"
|
|
| "next_action"
|
|
| "follow_up"
|
|
| "idea"
|
|
| "suggestion"
|
|
| "personal";
|
|
|
|
export interface NoteAttachmentDto {
|
|
id: string;
|
|
fileName: string;
|
|
mimeType: string;
|
|
sizeBytes: number;
|
|
createdAt: string;
|
|
}
|
|
|
|
export interface NoteChecklistItemDto {
|
|
id: string;
|
|
text: string;
|
|
done: boolean;
|
|
}
|
|
|
|
export interface NoteDto {
|
|
id: string;
|
|
title: string | null;
|
|
body: string;
|
|
noteType: NoteType | string;
|
|
pinned: boolean;
|
|
color: string | null;
|
|
tags: string[];
|
|
reminderAt: string | null;
|
|
checklist: NoteChecklistItemDto[];
|
|
locationText: string | null;
|
|
analyzeEnabled: boolean;
|
|
person: PersonSummaryDto | null;
|
|
company: CompanySummaryDto | null;
|
|
deal: DealSummaryDto | null;
|
|
activity: { id: string; title: string } | null;
|
|
author: UserSummaryDto | null;
|
|
attachments: NoteAttachmentDto[];
|
|
createdAt: string;
|
|
updatedAt: string;
|
|
}
|
|
|
|
export interface CreateNoteRequest {
|
|
title?: string;
|
|
body: string;
|
|
noteType?: NoteType | string;
|
|
pinned?: boolean;
|
|
color?: string;
|
|
tags?: string[];
|
|
reminderAt?: string | null;
|
|
checklist?: NoteChecklistItemDto[];
|
|
locationText?: string | null;
|
|
analyzeEnabled?: boolean;
|
|
personId?: string | null;
|
|
companyId?: string | null;
|
|
dealId?: string | null;
|
|
activityId?: string | null;
|
|
}
|
|
|
|
export interface UpdateNoteRequest {
|
|
title?: string | null;
|
|
body?: string;
|
|
noteType?: NoteType | string;
|
|
pinned?: boolean;
|
|
color?: string | null;
|
|
tags?: string[];
|
|
reminderAt?: string | null;
|
|
checklist?: NoteChecklistItemDto[];
|
|
locationText?: string | null;
|
|
analyzeEnabled?: boolean;
|
|
personId?: string | null;
|
|
companyId?: string | null;
|
|
dealId?: string | null;
|
|
activityId?: string | null;
|
|
}
|
|
|
|
export type NoteAiSuggestionStatus =
|
|
| 'pending'
|
|
| 'applied'
|
|
| 'dismissed'
|
|
| 'failed';
|
|
|
|
export type MemorySourceType = 'note' | 'manual';
|
|
|
|
export interface NoteAiNextActionDto {
|
|
title: string;
|
|
description?: string;
|
|
dueAt?: string | null;
|
|
}
|
|
|
|
export interface NoteAiMemoryExtractDto {
|
|
content: string;
|
|
category?: string;
|
|
}
|
|
|
|
export interface NoteAiPayloadDto {
|
|
nextAction: NoteAiNextActionDto | null;
|
|
memoryItems: NoteAiMemoryExtractDto[];
|
|
summary?: string;
|
|
}
|
|
|
|
export interface NoteAiSuggestionDto {
|
|
id: string;
|
|
noteId: string;
|
|
status: NoteAiSuggestionStatus;
|
|
payload: NoteAiPayloadDto | null;
|
|
errorMessage: string | null;
|
|
analyzedAt: string | null;
|
|
createdAt: string;
|
|
updatedAt: string;
|
|
}
|
|
|
|
export interface ApplyNoteAiSuggestionRequest {
|
|
applyNextAction?: boolean;
|
|
memoryIndices?: number[];
|
|
nextAction?: NoteAiNextActionDto;
|
|
memoryItems?: Array<{ index: number; content: string }>;
|
|
}
|
|
|
|
export type AskMentoSourceType = 'person' | 'note' | 'activity' | 'deal' | 'memory';
|
|
|
|
export interface AskMentoSourceDto {
|
|
type: AskMentoSourceType;
|
|
id: string;
|
|
label: string;
|
|
}
|
|
|
|
export interface AskMentoRequest {
|
|
question: string;
|
|
personId?: string;
|
|
}
|
|
|
|
export interface AskMentoResponse {
|
|
answer: string;
|
|
sources: AskMentoSourceDto[];
|
|
}
|
|
|
|
export type VoiceCommandIntent = 'create_person' | 'create_activity' | 'none';
|
|
|
|
export interface VoicePersonDraft {
|
|
firstName: string;
|
|
lastName: string;
|
|
mobile: string;
|
|
phone: string;
|
|
email: string;
|
|
website: string;
|
|
referrer: string;
|
|
position: string;
|
|
notes: string;
|
|
}
|
|
|
|
export interface VoiceActivityDraft {
|
|
title: string;
|
|
type: ActivityType;
|
|
description: string;
|
|
dueAt: string | null;
|
|
personName: string;
|
|
personId: string | null;
|
|
}
|
|
|
|
export interface VoiceCommandDraft {
|
|
intent: VoiceCommandIntent;
|
|
summary: string;
|
|
missing: string[];
|
|
canConfirm: boolean;
|
|
person: VoicePersonDraft | null;
|
|
activity: VoiceActivityDraft | null;
|
|
}
|
|
|
|
export interface PersonAiSummaryDto {
|
|
text: string;
|
|
cta: string;
|
|
ctaKind: 'call' | 'activity';
|
|
}
|
|
|
|
export interface MemoryItemDto {
|
|
id: string;
|
|
personId: string;
|
|
content: string;
|
|
sourceType: MemorySourceType;
|
|
sourceId: string | null;
|
|
confirmedBy: UserSummaryDto | null;
|
|
createdAt: string;
|
|
updatedAt: string;
|
|
}
|
|
|
|
export interface CreateMemoryItemRequest {
|
|
personId: string;
|
|
content: string;
|
|
}
|
|
|
|
export interface UpdateMemoryItemRequest {
|
|
content: string;
|
|
}
|
|
|
|
export interface ImportContactsResult {
|
|
created: number;
|
|
skipped: number;
|
|
errors: Array<{ row: number; message: string }>;
|
|
boardId?: string;
|
|
boards?: string[];
|
|
}
|
|
|
|
export interface ExcelFieldOption {
|
|
key: string;
|
|
label: string;
|
|
required?: boolean;
|
|
}
|
|
|
|
export interface ExcelColumnPreview {
|
|
header: string;
|
|
index: number;
|
|
samples: string[];
|
|
}
|
|
|
|
export interface ExcelPreviewResult {
|
|
columns: ExcelColumnPreview[];
|
|
fields: ExcelFieldOption[];
|
|
suggested: Record<string, string>;
|
|
needsMapping: boolean;
|
|
}
|
|
|
|
export interface ProductDto {
|
|
id: string;
|
|
kind: "product" | "service";
|
|
name: string;
|
|
sku: string | null;
|
|
description: string | null;
|
|
category: string | null;
|
|
unit: string | null;
|
|
stockQty: number | null;
|
|
brand: string | null;
|
|
model: string | null;
|
|
country: string | null;
|
|
tags: string[];
|
|
unitPrice: number | null;
|
|
currency: string;
|
|
isActive: boolean;
|
|
createdAt: string;
|
|
updatedAt: string;
|
|
}
|
|
|
|
export interface CreateProductRequest {
|
|
kind?: "product" | "service";
|
|
name: string;
|
|
sku?: string;
|
|
description?: string;
|
|
category?: string;
|
|
unit?: string;
|
|
stockQty?: number | null;
|
|
brand?: string;
|
|
model?: string;
|
|
country?: string;
|
|
tags?: string[];
|
|
unitPrice?: number | null;
|
|
currency?: string;
|
|
isActive?: boolean;
|
|
}
|
|
|
|
export interface UpdateProductRequest {
|
|
kind?: "product" | "service";
|
|
name?: string;
|
|
sku?: string | null;
|
|
description?: string | null;
|
|
category?: string | null;
|
|
unit?: string | null;
|
|
stockQty?: number | null;
|
|
brand?: string | null;
|
|
model?: string | null;
|
|
country?: string | null;
|
|
tags?: string[];
|
|
unitPrice?: number | null;
|
|
currency?: string;
|
|
isActive?: boolean;
|
|
}
|
|
|
|
export type StockMovementType =
|
|
| "opening"
|
|
| "purchase"
|
|
| "sale"
|
|
| "credit"
|
|
| "adjustment";
|
|
|
|
export interface InventoryStockRowDto {
|
|
productId: string;
|
|
name: string;
|
|
sku: string | null;
|
|
unit: string | null;
|
|
kind: "product" | "service";
|
|
stockQty: number;
|
|
currency: string;
|
|
unitPrice: number | null;
|
|
}
|
|
|
|
export interface StockMovementDto {
|
|
id: string;
|
|
productId: string;
|
|
productName: string;
|
|
type: StockMovementType;
|
|
quantity: number;
|
|
note: string | null;
|
|
invoiceId: string | null;
|
|
occurredAt: string;
|
|
}
|
|
|
|
export interface InventorySummaryDto {
|
|
enabled: boolean;
|
|
warehouseName: string | null;
|
|
products: InventoryStockRowDto[];
|
|
cashTotal: number;
|
|
receivablesTotal: number;
|
|
currency: string;
|
|
}
|
|
|
|
export interface CreateStockMovementRequest {
|
|
productId: string;
|
|
type: "purchase" | "adjustment";
|
|
quantity: number;
|
|
note?: string;
|
|
}
|
|
|
|
export interface PartyBalanceDto {
|
|
enabled: boolean;
|
|
personId: string;
|
|
openingAmount: number;
|
|
invoicedAmount: number;
|
|
paidAmount: number;
|
|
balance: number;
|
|
currency: string;
|
|
}
|
|
|
|
export interface UpdatePartyOpeningRequest {
|
|
amount: number;
|
|
note?: string;
|
|
}
|
|
|
|
export interface DealLineItemDto {
|
|
id: string;
|
|
productId: string | null;
|
|
name: string;
|
|
quantity: number;
|
|
unitPrice: number;
|
|
currency: string;
|
|
position: number;
|
|
lineTotal: number;
|
|
}
|
|
|
|
export interface DealLineItemInput {
|
|
productId?: string | null;
|
|
name: string;
|
|
quantity: number;
|
|
unitPrice: number;
|
|
currency?: string;
|
|
}
|
|
|
|
export type NotificationType =
|
|
| "activity_assigned"
|
|
| "activity_overdue"
|
|
| "activity_reminder"
|
|
| "deal_moved"
|
|
| "note_reminder"
|
|
| "cheque_due"
|
|
| "ticket_assigned"
|
|
| "automation"
|
|
| "system";
|
|
|
|
export interface NotificationDto {
|
|
id: string;
|
|
type: NotificationType | string;
|
|
title: string;
|
|
body: string | null;
|
|
link: string | null;
|
|
entityType: string | null;
|
|
entityId: string | null;
|
|
readAt: string | null;
|
|
createdAt: string;
|
|
}
|
|
|
|
export interface NotificationsListDto {
|
|
items: NotificationDto[];
|
|
unreadCount: number;
|
|
}
|
|
|
|
export const LEAD_FORM_FIELD_TYPES = [
|
|
"text",
|
|
"textarea",
|
|
"email",
|
|
"phone",
|
|
"select",
|
|
"radio",
|
|
"checkbox",
|
|
"date",
|
|
"number",
|
|
"file",
|
|
"rating",
|
|
"heading",
|
|
] as const;
|
|
export type LeadFormFieldType = (typeof LEAD_FORM_FIELD_TYPES)[number];
|
|
|
|
export type LeadFormMapTo =
|
|
| "person.firstName"
|
|
| "person.lastName"
|
|
| "person.fullName"
|
|
| "person.email"
|
|
| "person.mobile"
|
|
| "person.phone"
|
|
| "person.position"
|
|
| "deal.title"
|
|
| "deal.notes"
|
|
| "notes"
|
|
| "custom";
|
|
|
|
export interface LeadFormFieldDto {
|
|
id: string;
|
|
key: string;
|
|
label: string;
|
|
fieldType: LeadFormFieldType | string;
|
|
required: boolean;
|
|
mapTo: LeadFormMapTo | string;
|
|
position: number;
|
|
placeholder: string | null;
|
|
options: string[];
|
|
}
|
|
|
|
export interface LeadFormDto {
|
|
id: string;
|
|
name: string;
|
|
description: string | null;
|
|
publicToken: string;
|
|
isActive: boolean;
|
|
createPerson: boolean;
|
|
createDeal: boolean;
|
|
pipelineId: string | null;
|
|
source: string | null;
|
|
thankYouMessage: string | null;
|
|
redirectUrl: string | null;
|
|
assigneeIds: string[];
|
|
submitLabel: string;
|
|
submitColor: string;
|
|
hasCover: boolean;
|
|
fields: LeadFormFieldDto[];
|
|
submissionCount?: number;
|
|
publicPath: string;
|
|
createdAt: string;
|
|
updatedAt: string;
|
|
}
|
|
|
|
export interface LeadFormFieldInput {
|
|
key?: string;
|
|
label: string;
|
|
fieldType: LeadFormFieldType | string;
|
|
required?: boolean;
|
|
mapTo: LeadFormMapTo | string;
|
|
position?: number;
|
|
placeholder?: string | null;
|
|
options?: string[];
|
|
}
|
|
|
|
export interface CreateLeadFormRequest {
|
|
name: string;
|
|
description?: string | null;
|
|
createPerson?: boolean;
|
|
createDeal?: boolean;
|
|
pipelineId?: string | null;
|
|
source?: string | null;
|
|
thankYouMessage?: string | null;
|
|
redirectUrl?: string | null;
|
|
assigneeIds?: string[];
|
|
submitLabel?: string;
|
|
submitColor?: string;
|
|
fields?: LeadFormFieldInput[];
|
|
}
|
|
|
|
export interface UpdateLeadFormRequest {
|
|
name?: string;
|
|
description?: string | null;
|
|
isActive?: boolean;
|
|
createPerson?: boolean;
|
|
createDeal?: boolean;
|
|
pipelineId?: string | null;
|
|
source?: string | null;
|
|
thankYouMessage?: string | null;
|
|
redirectUrl?: string | null;
|
|
assigneeIds?: string[];
|
|
submitLabel?: string;
|
|
submitColor?: string;
|
|
fields?: LeadFormFieldInput[];
|
|
}
|
|
|
|
export interface PublicLeadFormDto {
|
|
name: string;
|
|
description: string | null;
|
|
thankYouMessage: string | null;
|
|
submitLabel: string;
|
|
submitColor: string;
|
|
hasCover: boolean;
|
|
coverPath: string | null;
|
|
fields: Array<{
|
|
key: string;
|
|
label: string;
|
|
fieldType: LeadFormFieldType | string;
|
|
required: boolean;
|
|
placeholder: string | null;
|
|
options: string[];
|
|
}>;
|
|
}
|
|
|
|
export interface LeadFormSubmissionDto {
|
|
id: string;
|
|
values: Record<string, string>;
|
|
personId: string | null;
|
|
createdAt: string;
|
|
}
|
|
|
|
export interface SubmitLeadFormRequest {
|
|
values: Record<string, string>;
|
|
}
|
|
|
|
export interface SubmitLeadFormResponse {
|
|
ok: true;
|
|
thankYouMessage: string | null;
|
|
redirectUrl: string | null;
|
|
}
|
|
|
|
export type QuoteStatus =
|
|
| "draft"
|
|
| "sent"
|
|
| "accepted"
|
|
| "rejected"
|
|
| "expired";
|
|
|
|
export interface QuoteLineItemDto {
|
|
id: string;
|
|
productId: string | null;
|
|
name: string;
|
|
quantity: number;
|
|
unitPrice: number;
|
|
currency: string;
|
|
position: number;
|
|
lineTotal: number;
|
|
}
|
|
|
|
export interface QuoteDto {
|
|
id: string;
|
|
number: string;
|
|
title: string;
|
|
status: QuoteStatus | string;
|
|
currency: string;
|
|
notes: string | null;
|
|
discountAmount?: number;
|
|
taxPercent?: number;
|
|
issuedAt?: string | null;
|
|
validUntil: string | null;
|
|
totalAmount: number;
|
|
subtotal: number;
|
|
taxAmount: number;
|
|
payableAmount: number;
|
|
person: PersonSummaryDto | null;
|
|
company: CompanySummaryDto | null;
|
|
deal: DealSummaryDto | null;
|
|
owner: UserSummaryDto | null;
|
|
lineItems: QuoteLineItemDto[];
|
|
createdAt: string;
|
|
updatedAt: string;
|
|
}
|
|
|
|
export interface CreateQuoteRequest {
|
|
title: string;
|
|
status?: QuoteStatus | string;
|
|
currency?: string;
|
|
notes?: string;
|
|
discountAmount?: number;
|
|
taxPercent?: number;
|
|
issuedAt?: string | null;
|
|
validityDays?: number;
|
|
validUntil?: string | null;
|
|
personId?: string | null;
|
|
companyId?: string | null;
|
|
dealId?: string | null;
|
|
fromDealId?: string | null;
|
|
lineItems?: DealLineItemInput[];
|
|
}
|
|
|
|
export interface UpdateQuoteRequest {
|
|
title?: string;
|
|
status?: QuoteStatus | string;
|
|
currency?: string;
|
|
notes?: string | null;
|
|
discountAmount?: number;
|
|
taxPercent?: number;
|
|
issuedAt?: string | null;
|
|
validUntil?: string | null;
|
|
personId?: string | null;
|
|
companyId?: string | null;
|
|
dealId?: string | null;
|
|
lineItems?: DealLineItemInput[];
|
|
}
|
|
|
|
export type InvoiceStatus =
|
|
| "draft"
|
|
| "issued"
|
|
| "partially_paid"
|
|
| "paid"
|
|
| "canceled";
|
|
|
|
export type PaymentMethod =
|
|
| "cash"
|
|
| "transfer"
|
|
| "card"
|
|
| "check"
|
|
| "other";
|
|
|
|
export type InstallmentStatus = "pending" | "paid" | "canceled";
|
|
|
|
export interface InvoiceLineItemDto {
|
|
id: string;
|
|
productId: string | null;
|
|
name: string;
|
|
quantity: number;
|
|
unitPrice: number;
|
|
currency: string;
|
|
position: number;
|
|
lineTotal: number;
|
|
}
|
|
|
|
export type ChequeStatus = "pending" | "cleared" | "bounced";
|
|
export type ChequeDirection = "in" | "out";
|
|
|
|
export interface ChequeDto {
|
|
id: string;
|
|
paymentId: string | null;
|
|
invoiceId: string | null;
|
|
invoiceNumber: string | null;
|
|
invoiceTitle: string | null;
|
|
personId: string | null;
|
|
personName: string | null;
|
|
amount: number;
|
|
currency: string;
|
|
number: string;
|
|
bank: string;
|
|
dueDate: string;
|
|
status: ChequeStatus | string;
|
|
direction: ChequeDirection | string;
|
|
notes: string | null;
|
|
paidAt: string;
|
|
createdAt: string;
|
|
updatedAt: string;
|
|
}
|
|
|
|
export interface ChequeInput {
|
|
number: string;
|
|
bank: string;
|
|
dueDate: string;
|
|
status?: ChequeStatus | string;
|
|
notes?: string;
|
|
}
|
|
|
|
export interface CreateChequeRequest {
|
|
personId: string;
|
|
amount: number;
|
|
currency?: string;
|
|
number: string;
|
|
bank: string;
|
|
dueDate: string;
|
|
status?: ChequeStatus | string;
|
|
direction?: ChequeDirection | string;
|
|
notes?: string;
|
|
}
|
|
|
|
export interface UpdateChequeRequest {
|
|
number?: string;
|
|
bank?: string;
|
|
dueDate?: string;
|
|
status?: ChequeStatus | string;
|
|
notes?: string | null;
|
|
}
|
|
|
|
export interface PaymentDto {
|
|
id: string;
|
|
amount: number;
|
|
currency: string;
|
|
method: PaymentMethod | string;
|
|
paidAt: string;
|
|
notes: string | null;
|
|
installmentId: string | null;
|
|
cheque?: ChequeDto | null;
|
|
createdAt: string;
|
|
}
|
|
|
|
export interface CommissionEntryDto {
|
|
id: string;
|
|
userId: string;
|
|
userName: string;
|
|
invoiceId: string;
|
|
invoiceNumber: string;
|
|
invoiceTitle: string;
|
|
percent: number;
|
|
amount: number;
|
|
currency: string;
|
|
status: "posted" | "void" | string;
|
|
createdAt: string;
|
|
}
|
|
|
|
export interface InvoiceInstallmentDto {
|
|
id: string;
|
|
position: number;
|
|
dueDate: string;
|
|
amount: number;
|
|
currency: string;
|
|
status: InstallmentStatus | string;
|
|
paidAt: string | null;
|
|
paymentId: string | null;
|
|
}
|
|
|
|
export interface InvoiceDto {
|
|
id: string;
|
|
number: string;
|
|
title: string;
|
|
status: InvoiceStatus | string;
|
|
kind?: "invoice" | "credit";
|
|
currency: string;
|
|
notes: string | null;
|
|
discountAmount?: number;
|
|
taxPercent?: number;
|
|
issuedAt: string | null;
|
|
dueDate: string | null;
|
|
totalAmount: number;
|
|
subtotal: number;
|
|
taxAmount: number;
|
|
payableAmount: number;
|
|
paidAmount: number;
|
|
remainingAmount: number;
|
|
person: PersonSummaryDto | null;
|
|
company: CompanySummaryDto | null;
|
|
deal: DealSummaryDto | null;
|
|
quote: { id: string; number: string; title: string } | null;
|
|
owner: UserSummaryDto | null;
|
|
lineItems: InvoiceLineItemDto[];
|
|
payments: PaymentDto[];
|
|
installments: InvoiceInstallmentDto[];
|
|
commissionPercent?: number | null;
|
|
commissionFixedAmount?: number | null;
|
|
commission?: {
|
|
percent: number;
|
|
amount: number;
|
|
status: string;
|
|
} | null;
|
|
createdAt: string;
|
|
updatedAt: string;
|
|
}
|
|
|
|
export interface InvoiceInstallmentInput {
|
|
dueDate: string;
|
|
amount: number;
|
|
currency?: string;
|
|
}
|
|
|
|
export interface CreateInvoiceRequest {
|
|
title: string;
|
|
status?: InvoiceStatus | string;
|
|
kind?: "invoice" | "credit";
|
|
currency?: string;
|
|
notes?: string;
|
|
discountAmount?: number;
|
|
taxPercent?: number;
|
|
issuedAt?: string | null;
|
|
dueDate?: string | null;
|
|
personId?: string | null;
|
|
companyId?: string | null;
|
|
dealId?: string | null;
|
|
quoteId?: string | null;
|
|
fromDealId?: string | null;
|
|
fromQuoteId?: string | null;
|
|
lineItems?: DealLineItemInput[];
|
|
installments?: InvoiceInstallmentInput[];
|
|
commissionPercent?: number | null;
|
|
commissionFixedAmount?: number | null;
|
|
}
|
|
|
|
export interface UpdateInvoiceRequest {
|
|
title?: string;
|
|
status?: InvoiceStatus | string;
|
|
kind?: "invoice" | "credit";
|
|
currency?: string;
|
|
notes?: string | null;
|
|
discountAmount?: number;
|
|
taxPercent?: number;
|
|
issuedAt?: string | null;
|
|
dueDate?: string | null;
|
|
personId?: string | null;
|
|
companyId?: string | null;
|
|
dealId?: string | null;
|
|
quoteId?: string | null;
|
|
lineItems?: DealLineItemInput[];
|
|
installments?: InvoiceInstallmentInput[];
|
|
commissionPercent?: number | null;
|
|
commissionFixedAmount?: number | null;
|
|
}
|
|
|
|
export interface CreatePaymentRequest {
|
|
amount: number;
|
|
currency?: string;
|
|
method?: PaymentMethod | string;
|
|
paidAt?: string;
|
|
notes?: string;
|
|
installmentId?: string | null;
|
|
cheque?: ChequeInput;
|
|
}
|
|
|
|
export type SalesGoalType =
|
|
| "sales"
|
|
| "customer"
|
|
| "revenue"
|
|
| "deal"
|
|
| "activity"
|
|
| "quality";
|
|
|
|
export type SalesGoalStatus = "in_progress" | "completed" | "needs_attention";
|
|
|
|
export interface SalesGoalDto {
|
|
id: string;
|
|
title: string;
|
|
type: SalesGoalType | string;
|
|
periodStart: string;
|
|
periodEnd: string;
|
|
targetAmount: number;
|
|
forecastAmount: number | null;
|
|
probability: number | null;
|
|
currency: string;
|
|
notes: string | null;
|
|
user: UserSummaryDto | null;
|
|
currentAmount: number;
|
|
progressPercent: number;
|
|
status: SalesGoalStatus;
|
|
createdAt: string;
|
|
updatedAt: string;
|
|
}
|
|
|
|
export interface CreateSalesGoalRequest {
|
|
title: string;
|
|
periodStart: string;
|
|
periodEnd: string;
|
|
targetAmount: number;
|
|
type?: SalesGoalType | string;
|
|
forecastAmount?: number | null;
|
|
/** 0-100 */
|
|
probability?: number | null;
|
|
currency?: string;
|
|
notes?: string;
|
|
/** فقط مالک/مدیر؛ در غیر این صورت هدف برای خود کاربر */
|
|
userId?: string | null;
|
|
}
|
|
|
|
export interface UpdateSalesGoalRequest {
|
|
title?: string;
|
|
periodStart?: string;
|
|
periodEnd?: string;
|
|
targetAmount?: number;
|
|
type?: SalesGoalType | string;
|
|
forecastAmount?: number | null;
|
|
/** 0-100 */
|
|
probability?: number | null;
|
|
currency?: string;
|
|
notes?: string | null;
|
|
userId?: string | null;
|
|
}
|
|
|
|
export interface SalesForecastKpiDto {
|
|
key: "sales" | "customer" | "deal" | "forecast";
|
|
label: string;
|
|
current: number;
|
|
target: number;
|
|
percent: number;
|
|
unit: "money" | "count";
|
|
}
|
|
|
|
export interface SalesForecastDto {
|
|
periodStart: string;
|
|
periodEnd: string;
|
|
currency: string;
|
|
/** مجموع اهداف همپوشان با بازه */
|
|
targetAmount: number;
|
|
/** معاملات برندهشده در بازه */
|
|
wonAmount: number;
|
|
/** معاملات باز فعلی */
|
|
openPipelineAmount: number;
|
|
/** پیشبینی: تحقق + کاریز باز × میانگین احتمال */
|
|
forecastAmount: number;
|
|
progressPercent: number | null;
|
|
openDealsCount: number;
|
|
wonDealsCount: number;
|
|
goals: SalesGoalDto[];
|
|
kpis: SalesForecastKpiDto[];
|
|
newCustomersCount: number;
|
|
/** معاملات ایجادشده در بازه */
|
|
newDealsCount: number;
|
|
}
|
|
|
|
export type TicketStatus =
|
|
| "open"
|
|
| "in_progress"
|
|
| "waiting"
|
|
| "resolved"
|
|
| "closed";
|
|
|
|
export type TicketPriority = "low" | "normal" | "high" | "urgent";
|
|
|
|
export interface TicketMessageDto {
|
|
id: string;
|
|
body: string;
|
|
isInternal: boolean;
|
|
author: UserSummaryDto | null;
|
|
createdAt: string;
|
|
}
|
|
|
|
export interface TicketDto {
|
|
id: string;
|
|
number: string;
|
|
subject: string;
|
|
description: string | null;
|
|
status: TicketStatus | string;
|
|
priority: TicketPriority | string;
|
|
person: PersonSummaryDto | null;
|
|
company: CompanySummaryDto | null;
|
|
deal: DealSummaryDto | null;
|
|
assignee: UserSummaryDto | null;
|
|
reporter: UserSummaryDto | null;
|
|
resolvedAt: string | null;
|
|
slaDueAt: string | null;
|
|
firstRespondedAt: string | null;
|
|
slaBreached: boolean;
|
|
csatScore: number | null;
|
|
csatComment: string | null;
|
|
csatAt: string | null;
|
|
messages: TicketMessageDto[];
|
|
createdAt: string;
|
|
updatedAt: string;
|
|
}
|
|
|
|
export interface CreateTicketRequest {
|
|
subject: string;
|
|
description?: string;
|
|
status?: TicketStatus | string;
|
|
priority?: TicketPriority | string;
|
|
personId?: string | null;
|
|
companyId?: string | null;
|
|
dealId?: string | null;
|
|
assigneeId?: string | null;
|
|
}
|
|
|
|
export interface UpdateTicketRequest {
|
|
subject?: string;
|
|
description?: string | null;
|
|
status?: TicketStatus | string;
|
|
priority?: TicketPriority | string;
|
|
personId?: string | null;
|
|
companyId?: string | null;
|
|
dealId?: string | null;
|
|
assigneeId?: string | null;
|
|
csatScore?: number | null;
|
|
csatComment?: string | null;
|
|
}
|
|
|
|
export interface CreateTicketMessageRequest {
|
|
body: string;
|
|
isInternal?: boolean;
|
|
}
|
|
|
|
export interface FaqArticleDto {
|
|
id: string;
|
|
question: string;
|
|
answer: string;
|
|
category: string | null;
|
|
isPublished: boolean;
|
|
position: number;
|
|
createdAt: string;
|
|
updatedAt: string;
|
|
}
|
|
|
|
export interface CreateFaqArticleRequest {
|
|
question: string;
|
|
answer: string;
|
|
category?: string;
|
|
isPublished?: boolean;
|
|
position?: number;
|
|
}
|
|
|
|
export interface UpdateFaqArticleRequest {
|
|
question?: string;
|
|
answer?: string;
|
|
category?: string | null;
|
|
isPublished?: boolean;
|
|
position?: number;
|
|
}
|
|
|
|
/** ساعات مهلت پاسخ اول بر اساس اولویت */
|
|
export const TICKET_SLA_HOURS: Record<TicketPriority, number> = {
|
|
urgent: 4,
|
|
high: 8,
|
|
normal: 24,
|
|
low: 48,
|
|
};
|
|
|
|
/* ——— پرتال مشتری (OTP) ——— */
|
|
|
|
export interface PortalOrgInfoDto {
|
|
name: string;
|
|
slug: string;
|
|
}
|
|
|
|
export interface PortalRequestOtpRequest {
|
|
mobile: string;
|
|
}
|
|
|
|
export interface PortalRequestOtpResponse {
|
|
ok: true;
|
|
/** فقط در حالت توسعه / دمو وقتی SMS واقعی وصل نیست */
|
|
devCode?: string;
|
|
expiresInSeconds: number;
|
|
}
|
|
|
|
export interface PortalVerifyOtpRequest {
|
|
mobile: string;
|
|
code: string;
|
|
}
|
|
|
|
export interface PortalPersonDto {
|
|
id: string;
|
|
fullName: string;
|
|
mobile: string | null;
|
|
}
|
|
|
|
export interface PortalLoginResponse {
|
|
accessToken: string;
|
|
person: PortalPersonDto;
|
|
organization: PortalOrgInfoDto;
|
|
}
|
|
|
|
export interface PortalCreateTicketRequest {
|
|
subject: string;
|
|
description?: string;
|
|
priority?: TicketPriority | string;
|
|
}
|
|
|
|
export interface PortalCreateMessageRequest {
|
|
body: string;
|
|
}
|
|
|
|
export interface PortalCsatRequest {
|
|
csatScore: number;
|
|
csatComment?: string | null;
|
|
}
|
|
|
|
export interface PortalTicketDto {
|
|
id: string;
|
|
number: string;
|
|
subject: string;
|
|
description: string | null;
|
|
status: TicketStatus | string;
|
|
priority: TicketPriority | string;
|
|
resolvedAt: string | null;
|
|
csatScore: number | null;
|
|
csatComment: string | null;
|
|
messages: Array<{
|
|
id: string;
|
|
body: string;
|
|
createdAt: string;
|
|
fromStaff: boolean;
|
|
}>;
|
|
createdAt: string;
|
|
updatedAt: string;
|
|
}
|
|
|
|
/* ——— گزارشات و داشبورد ——— */
|
|
|
|
export interface ReportsKpiDto {
|
|
key: string;
|
|
label: string;
|
|
value: number;
|
|
format: "money" | "number";
|
|
changePercent: number | null;
|
|
changeLabel: string;
|
|
}
|
|
|
|
export interface ReportsSeriesPointDto {
|
|
key: string;
|
|
label: string;
|
|
value: number;
|
|
}
|
|
|
|
export interface ReportsSliceDto {
|
|
label: string;
|
|
count: number;
|
|
percent: number;
|
|
amount?: number;
|
|
}
|
|
|
|
export interface ReportsTopProductDto {
|
|
name: string;
|
|
quantity: number;
|
|
amount: number;
|
|
}
|
|
|
|
export interface ReportsTeamRowDto {
|
|
userId: string;
|
|
fullName: string;
|
|
wonAmount: number;
|
|
wonCount: number;
|
|
activitiesDone: number;
|
|
}
|
|
|
|
export interface ReportsDashboardDto {
|
|
from: string;
|
|
to: string;
|
|
currency: string;
|
|
kpis: ReportsKpiDto[];
|
|
salesTrend: ReportsSeriesPointDto[];
|
|
monthlyComparison: ReportsSeriesPointDto[];
|
|
customerGrowth: ReportsSeriesPointDto[];
|
|
sources: ReportsSliceDto[];
|
|
lostReasons: ReportsSliceDto[];
|
|
salesByCategory: ReportsSliceDto[];
|
|
topProducts: ReportsTopProductDto[];
|
|
customerStatus: ReportsSliceDto[];
|
|
activities: {
|
|
open: number;
|
|
done: number;
|
|
overdue: number;
|
|
byType: Array<{ type: string; label: string; count: number }>;
|
|
};
|
|
team: ReportsTeamRowDto[];
|
|
insights: string[];
|
|
sales: {
|
|
openCount: number;
|
|
wonCount: number;
|
|
lostCount: number;
|
|
openAmount: number;
|
|
wonAmount: number;
|
|
lostAmount: number;
|
|
winRate: number;
|
|
};
|
|
funnel: Array<{
|
|
stageId: string;
|
|
stageName: string;
|
|
pipelineName: string;
|
|
count: number;
|
|
amount: number;
|
|
}>;
|
|
tickets: {
|
|
open: number;
|
|
resolved: number;
|
|
closed: number;
|
|
slaBreached: number;
|
|
avgCsat: number | null;
|
|
csatCount: number;
|
|
};
|
|
peopleCreated: number;
|
|
invoiceCount: number;
|
|
invoiceAmount: number;
|
|
}
|
|
|
|
export interface HomeDashboardKpiDto {
|
|
key: string;
|
|
label: string;
|
|
value: number;
|
|
changePercent: number | null;
|
|
changeLabel: string;
|
|
}
|
|
|
|
export interface HomeDashboardAlertDto {
|
|
key: string;
|
|
tone: 'urgent' | 'warning' | 'success' | 'info';
|
|
text: string;
|
|
link?: string;
|
|
}
|
|
|
|
export interface HomeDashboardTrendPointDto {
|
|
monthKey: string;
|
|
monthLabel: string;
|
|
amount: number;
|
|
}
|
|
|
|
export interface HomeDashboardSourceDto {
|
|
label: string;
|
|
count: number;
|
|
percent: number;
|
|
}
|
|
|
|
export interface HomeDashboardTopCustomerDto {
|
|
id: string;
|
|
name: string;
|
|
dealCount: number;
|
|
rank: number;
|
|
}
|
|
|
|
export interface HomeDashboardDto {
|
|
currency: string;
|
|
kpis: HomeDashboardKpiDto[];
|
|
alerts: HomeDashboardAlertDto[];
|
|
salesTrend: HomeDashboardTrendPointDto[];
|
|
sources: HomeDashboardSourceDto[];
|
|
topCustomers: HomeDashboardTopCustomerDto[];
|
|
}
|
|
|
|
/** برد پروژه / کاریز کارت */
|
|
export interface ProjectBoardStageDto {
|
|
id: string;
|
|
name: string;
|
|
position: number;
|
|
}
|
|
|
|
export interface ProjectBoardDto {
|
|
id: string;
|
|
name: string;
|
|
isDefault: boolean;
|
|
stages: ProjectBoardStageDto[];
|
|
}
|
|
|
|
export interface ProjectCardDto {
|
|
id: string;
|
|
title: string;
|
|
notes: string | null;
|
|
position: number;
|
|
boardId: string;
|
|
stageId: string;
|
|
tags: string[];
|
|
person: PersonSummaryDto | null;
|
|
company: CompanySummaryDto | null;
|
|
deal: { id: string; title: string } | null;
|
|
owner: UserSummaryDto | null;
|
|
assignees: UserSummaryDto[];
|
|
createdAt: string;
|
|
updatedAt: string;
|
|
}
|
|
|
|
export interface ProjectBoardColumnDto extends ProjectBoardStageDto {
|
|
cards: ProjectCardDto[];
|
|
}
|
|
|
|
export interface ProjectBoardBoardDto {
|
|
board: ProjectBoardDto;
|
|
stages: ProjectBoardColumnDto[];
|
|
}
|
|
|
|
export interface CreateProjectBoardRequest {
|
|
name: string;
|
|
}
|
|
|
|
export interface CreateProjectCardRequest {
|
|
title: string;
|
|
boardId?: string;
|
|
stageId?: string;
|
|
notes?: string;
|
|
tags?: string[];
|
|
personId?: string | null;
|
|
companyId?: string | null;
|
|
dealId?: string | null;
|
|
ownerId?: string | null;
|
|
assigneeIds?: string[];
|
|
}
|
|
|
|
export type UpdateProjectCardRequest = Partial<CreateProjectCardRequest>;
|
|
|
|
export interface MoveProjectCardRequest {
|
|
stageId: string;
|
|
position?: number;
|
|
}
|
|
|
|
/** باشگاه مشتریان */
|
|
export interface LoyaltyTierDto {
|
|
id: string;
|
|
name: string;
|
|
minPoints: number;
|
|
position: number;
|
|
color: string | null;
|
|
memberCount?: number;
|
|
}
|
|
|
|
export interface CreateLoyaltyTierRequest {
|
|
name: string;
|
|
minPoints: number;
|
|
color?: string | null;
|
|
}
|
|
|
|
export type UpdateLoyaltyTierRequest = Partial<CreateLoyaltyTierRequest>;
|
|
|
|
export interface AdjustLoyaltyPointsRequest {
|
|
delta: number;
|
|
reason?: string;
|
|
}
|
|
|
|
export interface LoyaltyMemberDto {
|
|
id: string;
|
|
fullName: string;
|
|
mobile: string | null;
|
|
email: string | null;
|
|
loyaltyPoints: number;
|
|
loyaltyTier: LoyaltyTierSummaryDto | null;
|
|
company: CompanySummaryDto | null;
|
|
lastActivityAt: string | null;
|
|
}
|
|
|
|
export interface LoyaltyStatsDto {
|
|
memberCount: number;
|
|
memberDeltaPct: number | null;
|
|
pointsIssued: number;
|
|
pointsIssuedDeltaPct: number | null;
|
|
rewardsUsed: number;
|
|
rewardsUsedDeltaPct: number | null;
|
|
retentionRate: number;
|
|
retentionDeltaPct: number | null;
|
|
}
|
|
|
|
export interface LoyaltySettingsDto {
|
|
enabled: boolean;
|
|
welcomePoints: number;
|
|
notifyOnAdjust: boolean;
|
|
}
|
|
|
|
export type UpdateLoyaltySettingsRequest = Partial<LoyaltySettingsDto>;
|
|
|
|
export interface LoyaltyRewardDto {
|
|
id: string;
|
|
name: string;
|
|
description: string | null;
|
|
pointsCost: number;
|
|
isActive: boolean;
|
|
position: number;
|
|
color: string | null;
|
|
redemptionCount?: number;
|
|
}
|
|
|
|
export interface CreateLoyaltyRewardRequest {
|
|
name: string;
|
|
description?: string | null;
|
|
pointsCost: number;
|
|
isActive?: boolean;
|
|
color?: string | null;
|
|
}
|
|
|
|
export type UpdateLoyaltyRewardRequest = Partial<CreateLoyaltyRewardRequest>;
|
|
|
|
export interface LoyaltyLedgerDto {
|
|
id: string;
|
|
delta: number;
|
|
reason: string | null;
|
|
source: string;
|
|
createdAt: string;
|
|
}
|
|
|
|
export interface BulkAdjustLoyaltyRequest {
|
|
personIds: string[];
|
|
delta: number;
|
|
reason?: string;
|
|
}
|
|
|
|
export interface LoyaltyMessageRequest {
|
|
personIds: string[];
|
|
text: string;
|
|
}
|
|
|
|
export interface LoyaltyMessageResult {
|
|
sent: number;
|
|
skipped: number;
|
|
failed: number;
|
|
}
|
|
|
|
export interface RedeemLoyaltyRewardRequest {
|
|
personId: string;
|
|
}
|
|
|
|
/** اتوماسیون اگر/آنگاه */
|
|
export type AutomationTrigger =
|
|
| "person_created"
|
|
| "lead_submitted"
|
|
| "deal_created"
|
|
| "deal_stage_changed"
|
|
| "deal_won"
|
|
| "deal_lost"
|
|
| "ticket_created"
|
|
| "ticket_resolved"
|
|
| "invoice_issued"
|
|
| "invoice_paid"
|
|
| "activity_done";
|
|
export type AutomationAction =
|
|
| "create_activity"
|
|
| "create_notification"
|
|
| "add_loyalty_points";
|
|
|
|
export const AUTOMATION_TRIGGER_LABELS: Record<AutomationTrigger, string> = {
|
|
person_created: "مخاطب جدید ساخته شد",
|
|
lead_submitted: "فرم سرنخ ارسال شد",
|
|
deal_created: "معامله جدید ساخته شد",
|
|
deal_stage_changed: "مرحله معامله عوض شد",
|
|
deal_won: "معامله برنده شد",
|
|
deal_lost: "معامله باخت",
|
|
ticket_created: "تیکت جدید ساخته شد",
|
|
ticket_resolved: "تیکت حل شد",
|
|
invoice_issued: "فاکتور صادر شد",
|
|
invoice_paid: "پرداخت روی فاکتور ثبت شد",
|
|
activity_done: "فعالیت تمام شد",
|
|
};
|
|
|
|
export const AUTOMATION_ACTION_LABELS: Record<AutomationAction, string> = {
|
|
create_activity: "ساخت فعالیت",
|
|
create_notification: "ارسال اعلان",
|
|
add_loyalty_points: "افزودن امتیاز باشگاه",
|
|
};
|
|
|
|
export const MAX_AUTOMATION_STEPS = 8;
|
|
|
|
export const AUTOMATION_ACTIONS = [
|
|
"create_activity",
|
|
"create_notification",
|
|
"add_loyalty_points",
|
|
] as const satisfies readonly AutomationAction[];
|
|
|
|
export const AUTOMATION_TRIGGERS = [
|
|
"person_created",
|
|
"lead_submitted",
|
|
"deal_created",
|
|
"deal_stage_changed",
|
|
"deal_won",
|
|
"deal_lost",
|
|
"ticket_created",
|
|
"ticket_resolved",
|
|
"invoice_issued",
|
|
"invoice_paid",
|
|
"activity_done",
|
|
] as const satisfies readonly AutomationTrigger[];
|
|
|
|
export const AUTOMATION_TRIGGER_GROUPS: {
|
|
label: string;
|
|
items: AutomationTrigger[];
|
|
}[] = [
|
|
{ label: "مخاطب", items: ["person_created", "lead_submitted"] },
|
|
{
|
|
label: "معامله",
|
|
items: ["deal_created", "deal_stage_changed", "deal_won", "deal_lost"],
|
|
},
|
|
{ label: "تیکت", items: ["ticket_created", "ticket_resolved"] },
|
|
{ label: "فعالیت", items: ["activity_done"] },
|
|
{ label: "مالی", items: ["invoice_issued", "invoice_paid"] },
|
|
];
|
|
|
|
export type AutomationStepKind = "action" | "condition";
|
|
|
|
export type AutomationConditionField =
|
|
| "deal_amount"
|
|
| "has_person"
|
|
| "has_company"
|
|
| "ticket_priority";
|
|
|
|
export type AutomationConditionOp =
|
|
| "gte"
|
|
| "lte"
|
|
| "eq"
|
|
| "neq"
|
|
| "is_set"
|
|
| "is_empty";
|
|
|
|
export type AutomationNotifyChannel = "app" | "sms" | "both";
|
|
export type AutomationNotifyTarget = "staff" | "person";
|
|
|
|
export const AUTOMATION_CONDITION_FIELD_LABELS: Record<
|
|
AutomationConditionField,
|
|
string
|
|
> = {
|
|
deal_amount: "مبلغ معامله",
|
|
has_person: "مخاطب دارد",
|
|
has_company: "شرکت دارد",
|
|
ticket_priority: "اولویت تیکت",
|
|
};
|
|
|
|
export const AUTOMATION_CONDITION_OP_LABELS: Record<
|
|
AutomationConditionOp,
|
|
string
|
|
> = {
|
|
gte: "بزرگتر یا مساوی",
|
|
lte: "کوچکتر یا مساوی",
|
|
eq: "مساوی",
|
|
neq: "نامساوی",
|
|
is_set: "دارد",
|
|
is_empty: "ندارد",
|
|
};
|
|
|
|
export const AUTOMATION_NOTIFY_CHANNEL_LABELS: Record<
|
|
AutomationNotifyChannel,
|
|
string
|
|
> = {
|
|
app: "ناتیفیکیشن داخلی",
|
|
sms: "پیامک",
|
|
both: "هر دو",
|
|
};
|
|
|
|
export const AUTOMATION_NOTIFY_CHANNELS = [
|
|
"app",
|
|
"sms",
|
|
"both",
|
|
] as const satisfies readonly AutomationNotifyChannel[];
|
|
|
|
export function isAutomationCondition(step: Pick<AutomationStep, "kind">): boolean {
|
|
return step.kind === "condition";
|
|
}
|
|
|
|
export function firstAutomationAction(
|
|
steps: AutomationStep[],
|
|
): AutomationAction | null {
|
|
const step = steps.find((s) => !isAutomationCondition(s) && s.action);
|
|
return step?.action ?? null;
|
|
}
|
|
|
|
export function conditionFieldsForTrigger(
|
|
trigger: AutomationTrigger,
|
|
): AutomationConditionField[] {
|
|
if (
|
|
trigger === "deal_won" ||
|
|
trigger === "deal_lost" ||
|
|
trigger === "deal_created" ||
|
|
trigger === "deal_stage_changed"
|
|
) {
|
|
return ["deal_amount", "has_person", "has_company"];
|
|
}
|
|
if (trigger === "ticket_resolved" || trigger === "ticket_created") {
|
|
return ["ticket_priority", "has_person", "has_company"];
|
|
}
|
|
return ["has_person", "has_company"];
|
|
}
|
|
|
|
export interface AutomationStep {
|
|
id: string;
|
|
kind?: AutomationStepKind;
|
|
action?: AutomationAction;
|
|
title?: string;
|
|
body?: string;
|
|
assigneeId?: string | null;
|
|
notifyUserId?: string | null;
|
|
notifyChannel?: AutomationNotifyChannel;
|
|
notifyTarget?: AutomationNotifyTarget;
|
|
activityType?: "call" | "meeting" | "task" | "note";
|
|
points?: number;
|
|
conditionField?: AutomationConditionField;
|
|
conditionOp?: AutomationConditionOp;
|
|
conditionValue?: string | number;
|
|
}
|
|
|
|
export interface AutomationRuleConfig {
|
|
title?: string;
|
|
body?: string;
|
|
assigneeId?: string | null;
|
|
notifyUserId?: string | null;
|
|
notifyChannel?: AutomationNotifyChannel;
|
|
notifyTarget?: AutomationNotifyTarget;
|
|
activityType?: "call" | "meeting" | "task" | "note";
|
|
points?: number;
|
|
/** زنجیرهٔ اعمال؛ اگر خالی باشد همان action تکی استفاده میشود */
|
|
steps?: AutomationStep[];
|
|
}
|
|
|
|
export interface AutomationRuleDto {
|
|
id: string;
|
|
name: string;
|
|
isActive: boolean;
|
|
trigger: AutomationTrigger;
|
|
action: AutomationAction;
|
|
config: AutomationRuleConfig;
|
|
createdAt: string;
|
|
updatedAt: string;
|
|
}
|
|
|
|
export interface CreateAutomationRuleRequest {
|
|
name: string;
|
|
trigger: AutomationTrigger;
|
|
action: AutomationAction;
|
|
isActive?: boolean;
|
|
config?: AutomationRuleConfig;
|
|
}
|
|
|
|
export type UpdateAutomationRuleRequest = Partial<CreateAutomationRuleRequest>;
|
|
|
|
export function normalizeAutomationSteps(
|
|
action: AutomationAction,
|
|
config?: AutomationRuleConfig | null,
|
|
): AutomationStep[] {
|
|
const cfg = config ?? {};
|
|
if (Array.isArray(cfg.steps) && cfg.steps.length) {
|
|
return cfg.steps
|
|
.filter((s) => s && (s.kind === "condition" || s.action))
|
|
.map((s, i) => ({
|
|
id: s.id || `step-${i}`,
|
|
kind: s.kind === "condition" ? "condition" : "action",
|
|
action: s.action,
|
|
title: s.title,
|
|
body: s.body,
|
|
assigneeId: s.assigneeId,
|
|
notifyUserId: s.notifyUserId,
|
|
notifyChannel: s.notifyChannel,
|
|
notifyTarget: s.notifyTarget,
|
|
activityType: s.activityType,
|
|
points: s.points,
|
|
conditionField: s.conditionField,
|
|
conditionOp: s.conditionOp,
|
|
conditionValue: s.conditionValue,
|
|
}));
|
|
}
|
|
return [
|
|
{
|
|
id: "step-0",
|
|
kind: "action",
|
|
action,
|
|
title: cfg.title,
|
|
body: cfg.body,
|
|
assigneeId: cfg.assigneeId,
|
|
notifyUserId: cfg.notifyUserId,
|
|
notifyChannel: cfg.notifyChannel,
|
|
notifyTarget: cfg.notifyTarget,
|
|
activityType: cfg.activityType,
|
|
points: cfg.points,
|
|
},
|
|
];
|
|
}
|
|
|
|
export function packAutomationConfig(steps: AutomationStep[]): AutomationRuleConfig {
|
|
const first =
|
|
steps.find((s) => (s.kind ?? "action") !== "condition") ?? steps[0];
|
|
return {
|
|
steps,
|
|
title: first?.title,
|
|
body: first?.body,
|
|
assigneeId: first?.assigneeId,
|
|
notifyUserId: first?.notifyUserId,
|
|
notifyChannel: first?.notifyChannel,
|
|
notifyTarget: first?.notifyTarget,
|
|
activityType: first?.activityType,
|
|
points: first?.points,
|
|
};
|
|
}
|
|
|
|
export function automationStepToConfig(step: AutomationStep): AutomationRuleConfig {
|
|
return {
|
|
title: step.title,
|
|
body: step.body,
|
|
assigneeId: step.assigneeId,
|
|
notifyUserId: step.notifyUserId,
|
|
notifyChannel: step.notifyChannel,
|
|
notifyTarget: step.notifyTarget,
|
|
activityType: step.activityType,
|
|
points: step.points,
|
|
};
|
|
}
|
|
|
|
// —— تهحساب / حسابداری ——
|
|
|
|
export const TAHSAB_SANAD_TYPES = [
|
|
"buy_sale_gold",
|
|
"buy_sale_sekeh",
|
|
"talab_bedehi",
|
|
"vkh_gold",
|
|
"vkh_bank",
|
|
] as const;
|
|
|
|
export type TahesabSanadType = (typeof TAHSAB_SANAD_TYPES)[number];
|
|
|
|
export const TAHSAB_SANAD_LABELS: Record<TahesabSanadType, string> = {
|
|
buy_sale_gold: "خرید/فروش آبشده",
|
|
buy_sale_sekeh: "خرید/فروش سکه",
|
|
talab_bedehi: "طلب/بدهی",
|
|
vkh_gold: "واریز/خروج طلا",
|
|
vkh_bank: "واریز/خروج بانکی",
|
|
};
|
|
|
|
export interface TahesabConnectionDto {
|
|
id: string;
|
|
organizationId: string;
|
|
provider: string;
|
|
label: string;
|
|
baseUrl: string;
|
|
dbName: string;
|
|
tokenMasked: string;
|
|
enabled: boolean;
|
|
isDefault: boolean;
|
|
allowInsecureTls: boolean;
|
|
lastHealthAt: string | null;
|
|
lastHealthOk: boolean | null;
|
|
lastError: string | null;
|
|
createdAt: string;
|
|
updatedAt: string;
|
|
}
|
|
|
|
export interface CreateTahesabConnectionRequest {
|
|
label: string;
|
|
baseUrl: string;
|
|
dbName: string;
|
|
token: string;
|
|
enabled?: boolean;
|
|
isDefault?: boolean;
|
|
allowInsecureTls?: boolean;
|
|
}
|
|
|
|
export type UpdateTahesabConnectionRequest = Partial<CreateTahesabConnectionRequest>;
|
|
|
|
export interface PersonAccountingLinkDto {
|
|
id: string;
|
|
personId: string;
|
|
connectionId: string;
|
|
moshtariCode: number;
|
|
matchMethod: string;
|
|
linkedAt: string;
|
|
lastBalanceSyncedAt: string | null;
|
|
connectionLabel?: string;
|
|
dbName?: string;
|
|
isDefaultConnection?: boolean;
|
|
}
|
|
|
|
export interface MandehValueDto {
|
|
amount: string | number;
|
|
unit: string;
|
|
status: "بس" | "بد";
|
|
}
|
|
|
|
export interface MandehDetailItemDto {
|
|
Name: string;
|
|
values: MandehValueDto[];
|
|
}
|
|
|
|
export interface PersonAssetsDto {
|
|
connectionId: string;
|
|
connectionLabel: string;
|
|
dbName: string;
|
|
moshtariCode: number;
|
|
goldBalance: number | null;
|
|
rialBalance: number | null;
|
|
currencyBalance: number | null;
|
|
totalGoldEquiv: number | null;
|
|
mandehDetail: MandehDetailItemDto[];
|
|
fetchedAt: string;
|
|
}
|
|
|
|
// —— پیام خودکار (جدا از اتوماسیون گردش کار) ——
|
|
|
|
export type MessageAutomationTrigger =
|
|
| "person_created"
|
|
| "lead_submitted"
|
|
| "deal_created"
|
|
| "deal_stage_changed"
|
|
| "deal_won"
|
|
| "deal_lost"
|
|
| "ticket_created"
|
|
| "ticket_resolved"
|
|
| "invoice_issued"
|
|
| "invoice_paid"
|
|
| "activity_created"
|
|
| "activity_done";
|
|
|
|
export type MessageAutomationDelayUnit =
|
|
| "minutes"
|
|
| "hours"
|
|
| "days"
|
|
| "weeks"
|
|
| "months";
|
|
export type MessageAutomationChannel = "app" | "sms" | "both";
|
|
export type MessageAutomationTarget = "staff" | "person" | "both";
|
|
|
|
export const MESSAGE_AUTOMATION_TRIGGERS = [
|
|
"person_created",
|
|
"lead_submitted",
|
|
"deal_created",
|
|
"deal_stage_changed",
|
|
"deal_won",
|
|
"deal_lost",
|
|
"ticket_created",
|
|
"ticket_resolved",
|
|
"invoice_issued",
|
|
"invoice_paid",
|
|
"activity_created",
|
|
"activity_done",
|
|
] as const satisfies readonly MessageAutomationTrigger[];
|
|
|
|
export const MESSAGE_AUTOMATION_TRIGGER_LABELS: Record<
|
|
MessageAutomationTrigger,
|
|
string
|
|
> = {
|
|
person_created: "مخاطب جدید ساخته شد",
|
|
lead_submitted: "فرم سرنخ ارسال شد",
|
|
deal_created: "معامله جدید ساخته شد",
|
|
deal_stage_changed: "مرحله معامله عوض شد",
|
|
deal_won: "معامله برنده شد",
|
|
deal_lost: "معامله باخت",
|
|
ticket_created: "تیکت جدید ساخته شد",
|
|
ticket_resolved: "تیکت حل شد",
|
|
invoice_issued: "فاکتور صادر شد",
|
|
invoice_paid: "پرداخت روی فاکتور ثبت شد",
|
|
activity_created: "فعالیت ثبت شد",
|
|
activity_done: "فعالیت تمام شد",
|
|
};
|
|
|
|
export const MESSAGE_AUTOMATION_UNIT_LABELS: Record<
|
|
MessageAutomationDelayUnit,
|
|
string
|
|
> = {
|
|
minutes: "دقیقه",
|
|
hours: "ساعت",
|
|
days: "روز",
|
|
weeks: "هفته",
|
|
months: "ماه",
|
|
};
|
|
|
|
export const MESSAGE_AUTOMATION_FIELDS = [
|
|
{ token: "{نام}", label: "نام مشتری" },
|
|
{ token: "{شماره}", label: "شماره موبایل" },
|
|
{ token: "{تاریخ}", label: "تاریخ ارسال" },
|
|
{ token: "{عنوان}", label: "عنوان رکورد" },
|
|
{ token: "{شرکت}", label: "نام شرکت" },
|
|
] as const;
|
|
|
|
export const MESSAGE_AUTOMATION_SMS_SEGMENT = 70;
|
|
|
|
export const MESSAGE_AUTOMATION_CHANNEL_LABELS: Record<
|
|
MessageAutomationChannel,
|
|
string
|
|
> = {
|
|
app: "ناتیف داخل برنامه",
|
|
sms: "پیامک",
|
|
both: "پیامک و ناتیف",
|
|
};
|
|
|
|
export const MESSAGE_AUTOMATION_TARGET_LABELS: Record<
|
|
MessageAutomationTarget,
|
|
string
|
|
> = {
|
|
staff: "مسئول پرونده",
|
|
person: "مشتری",
|
|
both: "مشتری و مسئول",
|
|
};
|
|
|
|
export interface MessageAutomationRuleDto {
|
|
id: string;
|
|
name: string;
|
|
isActive: boolean;
|
|
trigger: MessageAutomationTrigger;
|
|
startDelayValue: number;
|
|
startDelayUnit: MessageAutomationDelayUnit;
|
|
repeatCount: number;
|
|
intervalValue: number;
|
|
intervalUnit: MessageAutomationDelayUnit;
|
|
notifyTarget: MessageAutomationTarget;
|
|
channel: MessageAutomationChannel;
|
|
title: string;
|
|
description: string | null;
|
|
body: string;
|
|
createdAt: string;
|
|
updatedAt: string;
|
|
}
|
|
|
|
export interface CreateMessageAutomationRuleRequest {
|
|
name: string;
|
|
isActive?: boolean;
|
|
trigger: MessageAutomationTrigger;
|
|
startDelayValue?: number;
|
|
startDelayUnit?: MessageAutomationDelayUnit;
|
|
repeatCount?: number;
|
|
intervalValue?: number;
|
|
intervalUnit?: MessageAutomationDelayUnit;
|
|
notifyTarget?: MessageAutomationTarget;
|
|
channel?: MessageAutomationChannel;
|
|
title: string;
|
|
description?: string | null;
|
|
body: string;
|
|
}
|
|
|
|
export type UpdateMessageAutomationRuleRequest =
|
|
Partial<CreateMessageAutomationRuleRequest>;
|
|
|
|
export interface PlatformAdminDto {
|
|
id: string;
|
|
mobile?: string | null;
|
|
email?: string | null;
|
|
fullName: string;
|
|
isActive?: boolean;
|
|
lastLoginAt?: string | null;
|
|
createdAt?: string;
|
|
}
|
|
|
|
export interface PlatformLoginRequest {
|
|
mobile: string;
|
|
password: string;
|
|
}
|
|
|
|
export interface PlatformLoginResponse {
|
|
accessToken: string;
|
|
admin: PlatformAdminDto;
|
|
}
|
|
|
|
export interface CreatePlatformAdminRequest {
|
|
fullName: string;
|
|
mobile: string;
|
|
password: string;
|
|
email?: string;
|
|
}
|
|
|
|
export interface ResetPlatformAdminPasswordRequest {
|
|
password: string;
|
|
}
|
|
|
|
export interface PlatformStatsDto {
|
|
organizationCount: number;
|
|
activeOrganizationCount: number;
|
|
userCount: number;
|
|
activeUserCount: number;
|
|
membershipCount: number;
|
|
expiringSoonCount?: number;
|
|
expiredBillingCount?: number;
|
|
}
|
|
|
|
export interface PlatformOrgMemberDto {
|
|
membershipId: string;
|
|
userId: string;
|
|
fullName: string;
|
|
email: string;
|
|
mobile?: string | null;
|
|
role: UserRole;
|
|
isActive: boolean;
|
|
userIsActive: boolean;
|
|
isBillingOwner?: boolean;
|
|
billing?: BillingAccountDto | null;
|
|
}
|
|
|
|
export interface PlatformOrganizationDto {
|
|
id: string;
|
|
name: string;
|
|
slug: string;
|
|
businessField?: string | null;
|
|
isActive: boolean;
|
|
createdAt: string;
|
|
memberCount: number;
|
|
personCount: number;
|
|
dealCount: number;
|
|
enabledModules?: OrgModuleKey[];
|
|
members?: PlatformOrgMemberDto[];
|
|
}
|
|
|
|
export interface PlatformUserMembershipDto {
|
|
membershipId: string;
|
|
organizationId: string;
|
|
organizationName: string;
|
|
organizationIsActive: boolean;
|
|
role: UserRole;
|
|
isActive: boolean;
|
|
}
|
|
|
|
export interface PlatformUserDto {
|
|
id: string;
|
|
email: string;
|
|
mobile?: string | null;
|
|
fullName: string;
|
|
isActive: boolean;
|
|
lastLoginAt?: string | null;
|
|
createdAt: string;
|
|
memberships: PlatformUserMembershipDto[];
|
|
/** فقط اگر حداقل یک عضویت مالک فعال دارد */
|
|
isBillingOwner?: boolean;
|
|
billing?: BillingAccountDto | null;
|
|
}
|
|
|
|
export interface UpdatePlatformStatusRequest {
|
|
isActive: boolean;
|
|
}
|
|
|
|
export interface CreatePlatformOrganizationRequest {
|
|
name: string;
|
|
businessField?: string;
|
|
ownerFullName: string;
|
|
ownerEmail: string;
|
|
ownerPassword: string;
|
|
ownerMobile?: string;
|
|
}
|
|
|
|
export interface UpdatePlatformOrganizationRequest {
|
|
name?: string;
|
|
slug?: string;
|
|
businessField?: string | null;
|
|
isActive?: boolean;
|
|
}
|
|
|
|
export interface UpdatePlatformMembershipRoleRequest {
|
|
role: UserRole;
|
|
}
|
|
|
|
export interface ResetPlatformUserPasswordRequest {
|
|
password: string;
|
|
}
|
|
|
|
export { brand, APP_NAME, APP_NAME_FA, brandSuggestionFa } from "./brand";
|
|
export type { Brand } from "./brand";
|
|
export const API_PREFIX = "api";
|
|
|
|
/* ——— لاگ فعالیت کاربران ——— */
|
|
|
|
export interface AuditLogDto {
|
|
id: string;
|
|
action: string;
|
|
method: string;
|
|
path: string;
|
|
entityType: string | null;
|
|
entityId: string | null;
|
|
summary: string;
|
|
actor: UserSummaryDto | null;
|
|
ip: string | null;
|
|
createdAt: string;
|
|
}
|
|
|
|
export interface AuditLogListResponse {
|
|
items: AuditLogDto[];
|
|
total: number;
|
|
page: number;
|
|
limit: number;
|
|
}
|
|
|
|
/* ——— فیلترهای ذخیرهشده ——— */
|
|
|
|
export type SavedViewScope =
|
|
| "deals"
|
|
| "people"
|
|
| "companies"
|
|
| "activities"
|
|
| "project_cards";
|
|
|
|
export interface SavedViewDto {
|
|
id: string;
|
|
name: string;
|
|
scope: SavedViewScope;
|
|
filters: Record<string, unknown>;
|
|
isShared: boolean;
|
|
owner: UserSummaryDto | null;
|
|
createdAt: string;
|
|
updatedAt: string;
|
|
}
|
|
|
|
export interface CreateSavedViewRequest {
|
|
name: string;
|
|
scope: SavedViewScope;
|
|
filters: Record<string, unknown>;
|
|
isShared?: boolean;
|
|
}
|
|
|
|
export type UpdateSavedViewRequest = Partial<CreateSavedViewRequest>;
|
|
|
|
/* ——— API عمومی و وبهوک ——— */
|
|
|
|
export const WEBHOOK_EVENTS = [
|
|
"person.created",
|
|
"person.updated",
|
|
"deal.created",
|
|
"deal.updated",
|
|
"deal.won",
|
|
"deal.lost",
|
|
"invoice.paid",
|
|
"activity.created",
|
|
] as const;
|
|
|
|
export type WebhookEvent = (typeof WEBHOOK_EVENTS)[number];
|
|
|
|
export interface OrganizationApiKeyDto {
|
|
id: string;
|
|
name: string;
|
|
prefix: string;
|
|
lastUsedAt: string | null;
|
|
revokedAt: string | null;
|
|
createdAt: string;
|
|
}
|
|
|
|
export interface CreateOrganizationApiKeyRequest {
|
|
name: string;
|
|
}
|
|
|
|
export interface CreateOrganizationApiKeyResponse extends OrganizationApiKeyDto {
|
|
/** فقط یکبار هنگام ساخت برگردانده میشود */
|
|
rawKey: string;
|
|
}
|
|
|
|
export interface WebhookEndpointDto {
|
|
id: string;
|
|
name: string;
|
|
url: string;
|
|
events: string[];
|
|
isActive: boolean;
|
|
createdAt: string;
|
|
updatedAt: string;
|
|
}
|
|
|
|
export interface CreateWebhookEndpointRequest {
|
|
name: string;
|
|
url: string;
|
|
events: string[];
|
|
secret?: string;
|
|
isActive?: boolean;
|
|
}
|
|
|
|
/** فقط هنگام ساخت؛ secret دوباره برنمیگردد */
|
|
export interface CreateWebhookEndpointResponse extends WebhookEndpointDto {
|
|
secret: string;
|
|
}
|
|
|
|
export type UpdateWebhookEndpointRequest = Partial<CreateWebhookEndpointRequest>;
|
|
|
|
export interface WebhookDeliveryDto {
|
|
id: string;
|
|
event: string;
|
|
status: string;
|
|
statusCode: number | null;
|
|
attempts: number;
|
|
lastError: string | null;
|
|
createdAt: string;
|
|
deliveredAt: string | null;
|
|
}
|
|
|
|
/* ——— پیامک گروهی ——— */
|
|
|
|
export type SmsCampaignStatus =
|
|
| "draft"
|
|
| "queued"
|
|
| "sending"
|
|
| "done"
|
|
| "cancelled";
|
|
|
|
export interface SmsCampaignDto {
|
|
id: string;
|
|
name: string;
|
|
body: string;
|
|
status: SmsCampaignStatus;
|
|
scheduledAt: string | null;
|
|
totalCount: number;
|
|
sentCount: number;
|
|
failedCount: number;
|
|
skippedCount: number;
|
|
createdAt: string;
|
|
updatedAt: string;
|
|
}
|
|
|
|
export interface CreateSmsCampaignRequest {
|
|
name: string;
|
|
body: string;
|
|
personIds: string[];
|
|
scheduledAt?: string | null;
|
|
/** اگر true باشد بلافاصله صف میشود */
|
|
queueNow?: boolean;
|
|
}
|
|
|
|
export interface SmsCampaignListResponse {
|
|
items: SmsCampaignDto[];
|
|
total: number;
|
|
}
|
|
|
|
/* ——— تیمها و دیدپذیری ——— */
|
|
|
|
export type TeamMemberRole = "member" | "manager";
|
|
|
|
export interface TeamMemberDto {
|
|
userId: string;
|
|
fullName: string;
|
|
role: TeamMemberRole;
|
|
hasAvatar?: boolean;
|
|
}
|
|
|
|
export interface TeamDto {
|
|
id: string;
|
|
name: string;
|
|
description: string;
|
|
memberCount: number;
|
|
members: TeamMemberDto[];
|
|
createdAt: string;
|
|
updatedAt: string;
|
|
}
|
|
|
|
export interface CreateTeamRequest {
|
|
name: string;
|
|
description?: string;
|
|
memberIds?: string[];
|
|
managerIds?: string[];
|
|
}
|
|
|
|
export interface UpdateTeamRequest {
|
|
name?: string;
|
|
description?: string;
|
|
}
|
|
|
|
export interface SetTeamMembersRequest {
|
|
members: Array<{ userId: string; role: TeamMemberRole }>;
|
|
}
|
|
|
|
/* ——— ووکامرس ——— */
|
|
|
|
export interface WooCommerceConnectionDto {
|
|
id: string;
|
|
label: string;
|
|
storeUrl: string;
|
|
consumerKeyMasked: string;
|
|
webhookSecretSet: boolean;
|
|
enabled: boolean;
|
|
lastSyncAt: string | null;
|
|
lastHealthAt: string | null;
|
|
lastHealthOk: boolean | null;
|
|
lastError: string | null;
|
|
importedOrders: number;
|
|
createdAt: string;
|
|
updatedAt: string;
|
|
}
|
|
|
|
export interface CreateWooCommerceConnectionRequest {
|
|
label: string;
|
|
storeUrl: string;
|
|
consumerKey: string;
|
|
consumerSecret: string;
|
|
webhookSecret?: string;
|
|
enabled?: boolean;
|
|
}
|
|
|
|
export interface UpdateWooCommerceConnectionRequest {
|
|
label?: string;
|
|
storeUrl?: string;
|
|
consumerKey?: string;
|
|
consumerSecret?: string;
|
|
webhookSecret?: string | null;
|
|
enabled?: boolean;
|
|
}
|
|
|
|
export interface WooCommerceSyncResultDto {
|
|
fetched: number;
|
|
created: number;
|
|
updated: number;
|
|
skipped: number;
|
|
errors: string[];
|
|
}
|
|
|
|
export interface WooCommerceOrderLinkDto {
|
|
id: string;
|
|
wooOrderId: string;
|
|
wooOrderNumber: string | null;
|
|
invoiceId: string;
|
|
invoiceNumber: string;
|
|
status: string;
|
|
total: number;
|
|
currency: string;
|
|
syncedAt: string;
|
|
}
|