export type UserRole = "owner" | "admin" | "sales" | "support"; export const USER_ROLES: UserRole[] = ["owner", "admin", "sales", "support"]; export const USER_ROLE_LABELS: Record = { 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; } 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 = { 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; 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; ownerId?: string | null; } export type UpdateCompanyRequest = Partial; 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 = { 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; 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; ownerId?: string | null; } export type UpdatePersonRequest = Partial; export interface PaginatedResponse { 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; 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; 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; 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"; export interface NoteAttachmentDto { id: string; fileName: string; mimeType: string; sizeBytes: number; createdAt: string; } export interface NoteDto { id: string; title: string | null; body: string; noteType: NoteType | string; pinned: boolean; color: string | null; tags: string[]; reminderAt: string | null; person: PersonSummaryDto | null; company: CompanySummaryDto | null; deal: DealSummaryDto | 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; personId?: string | null; companyId?: string | null; dealId?: string | null; } export interface UpdateNoteRequest { title?: string | null; body?: string; noteType?: NoteType | string; pinned?: boolean; color?: string | null; tags?: string[]; reminderAt?: string | null; personId?: string | null; companyId?: string | null; dealId?: 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; } 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 interface SalesGoalDto { id: string; title: string; periodStart: string; periodEnd: string; targetAmount: number; currency: string; notes: string | null; user: UserSummaryDto | null; createdAt: string; updatedAt: string; } export interface CreateSalesGoalRequest { title: string; periodStart: string; periodEnd: string; targetAmount: number; currency?: string; notes?: string; /** فقط مالک/مدیر؛ در غیر این صورت هدف برای خود کاربر */ userId?: string | null; } export interface UpdateSalesGoalRequest { title?: string; periodStart?: string; periodEnd?: string; targetAmount?: number; currency?: string; notes?: string | null; userId?: string | null; } 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[]; } 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 = { 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; 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; 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 = { deal_won: "معامله برنده شد", ticket_resolved: "تیکت حل شد", }; export const AUTOMATION_ACTION_LABELS: Record = { 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; // —— ته‌حساب / حسابداری —— 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 = { 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; 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";