Files
CRM_Montzar/packages/shared/src/index.ts
T

1747 lines
39 KiB
TypeScript
Raw Normal View History

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;
}
export interface OrgUserDto {
id: string;
email: string;
fullName: string;
role: UserRole;
createdAt: string;
}
export interface InviteUserRequest {
email: string;
fullName: string;
password: string;
role: Exclude<UserRole, "owner">;
}
export interface UpdateUserRoleRequest {
role: UserRole;
}
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 OrganizationDto {
id: string;
name: string;
slug: string;
businessField: BusinessField | null;
baseCurrency: string;
enabledCurrencies: string[];
exchangeRates: ExchangeRateDto[];
}
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;
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;
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;
notes: string | null;
mood: PersonMood;
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;
notes?: string;
mood?: PersonMood;
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 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;
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;
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;
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 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 }>;
}
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 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"
| "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 type LeadFormFieldType = "text" | "email" | "phone" | "textarea";
export type LeadFormMapTo =
| "person.firstName"
| "person.lastName"
| "person.email"
| "person.mobile"
| "person.phone"
| "person.position"
| "deal.title"
| "deal.notes"
| "notes";
export interface LeadFormFieldDto {
id: string;
key: string;
label: string;
fieldType: LeadFormFieldType | string;
required: boolean;
mapTo: LeadFormMapTo | string;
position: number;
}
export interface LeadFormDto {
id: string;
name: string;
publicToken: string;
isActive: boolean;
createPerson: boolean;
createDeal: boolean;
pipelineId: string | null;
source: string | null;
thankYouMessage: string | null;
redirectUrl: string | null;
assigneeIds: string[];
fields: LeadFormFieldDto[];
publicPath: string;
createdAt: string;
updatedAt: string;
}
export interface LeadFormFieldInput {
key?: string;
label: string;
fieldType: LeadFormFieldType | string;
required?: boolean;
mapTo: LeadFormMapTo | string;
position?: number;
}
export interface CreateLeadFormRequest {
name: string;
createPerson?: boolean;
createDeal?: boolean;
pipelineId?: string | null;
source?: string | null;
thankYouMessage?: string | null;
redirectUrl?: string | null;
assigneeIds?: string[];
fields?: LeadFormFieldInput[];
}
export interface UpdateLeadFormRequest {
name?: string;
isActive?: boolean;
createPerson?: boolean;
createDeal?: boolean;
pipelineId?: string | null;
source?: string | null;
thankYouMessage?: string | null;
redirectUrl?: string | null;
assigneeIds?: string[];
fields?: LeadFormFieldInput[];
}
export interface PublicLeadFormDto {
name: string;
thankYouMessage: string | null;
fields: Array<{
key: string;
label: string;
fieldType: LeadFormFieldType | string;
required: boolean;
}>;
}
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 interface PaymentDto {
id: string;
amount: number;
currency: string;
method: PaymentMethod | string;
paidAt: string;
notes: string | null;
installmentId: string | null;
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[];
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[];
}
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[];
}
export interface CreatePaymentRequest {
amount: number;
currency?: string;
method?: PaymentMethod | string;
paidAt?: string;
notes?: string;
installmentId?: string | null;
}
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[];
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;
person: PersonSummaryDto | null;
company: CompanySummaryDto | null;
deal: { id: string; title: string } | null;
owner: UserSummaryDto | null;
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;
personId?: string | null;
companyId?: string | null;
dealId?: string | null;
ownerId?: string | null;
}
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;
}
/** اتوماسیون اگر/آنگاه */
export type AutomationTrigger = "deal_won" | "ticket_resolved";
export type AutomationAction =
| "create_activity"
| "create_notification"
| "add_loyalty_points";
export const AUTOMATION_TRIGGER_LABELS: Record<AutomationTrigger, string> = {
deal_won: "معامله برنده شد",
ticket_resolved: "تیکت حل شد",
};
export const AUTOMATION_ACTION_LABELS: Record<AutomationAction, string> = {
create_activity: "ساخت فعالیت",
create_notification: "ارسال اعلان",
add_loyalty_points: "افزودن امتیاز باشگاه",
};
export interface AutomationRuleConfig {
title?: string;
body?: string;
assigneeId?: string | null;
notifyUserId?: string | null;
activityType?: "call" | "meeting" | "task" | "note";
points?: number;
}
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 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 const APP_NAME = "Mento CRM";
export const APP_NAME_FA = "منتو";
export const API_PREFIX = "api";