2026-08-29 21:06:30 +03:30
|
|
|
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;
|
|
|
|
|
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 {
|
|
|
|
|
email: string;
|
|
|
|
|
password: string;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export interface LoginResponse {
|
|
|
|
|
accessToken: string;
|
|
|
|
|
user: AuthUserDto;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export interface OrganizationDto {
|
|
|
|
|
id: string;
|
|
|
|
|
name: string;
|
|
|
|
|
slug: string;
|
|
|
|
|
baseCurrency: string;
|
|
|
|
|
enabledCurrencies: string[];
|
|
|
|
|
exchangeRates: ExchangeRateDto[];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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>;
|
|
|
|
|
|
2026-08-30 09:42:07 +03:30
|
|
|
export interface LoyaltyTierSummaryDto {
|
|
|
|
|
id: string;
|
|
|
|
|
name: string;
|
|
|
|
|
minPoints: number;
|
|
|
|
|
color: string | null;
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-29 21:06:30 +03:30
|
|
|
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;
|
|
|
|
|
customFields: Record<string, unknown>;
|
2026-08-30 09:42:07 +03:30
|
|
|
loyaltyPoints: number;
|
|
|
|
|
loyaltyTier: LoyaltyTierSummaryDto | null;
|
2026-08-29 21:06:30 +03:30
|
|
|
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;
|
|
|
|
|
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 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;
|
|
|
|
|
title: string;
|
|
|
|
|
description: string | null;
|
|
|
|
|
dueAt: string | null;
|
|
|
|
|
completedAt: string | null;
|
|
|
|
|
person: PersonSummaryDto | null;
|
|
|
|
|
company: CompanySummaryDto | null;
|
|
|
|
|
deal: DealSummaryDto | null;
|
|
|
|
|
assignee: UserSummaryDto | null;
|
|
|
|
|
createdAt: string;
|
|
|
|
|
updatedAt: string;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export interface CreateActivityRequest {
|
|
|
|
|
title: string;
|
|
|
|
|
type?: ActivityType;
|
|
|
|
|
description?: string;
|
|
|
|
|
dueAt?: string | null;
|
|
|
|
|
personId?: string | null;
|
|
|
|
|
companyId?: string | null;
|
|
|
|
|
dealId?: string | null;
|
|
|
|
|
assigneeId?: string | null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export interface UpdateActivityRequest {
|
|
|
|
|
title?: string;
|
|
|
|
|
type?: ActivityType;
|
|
|
|
|
status?: ActivityStatus;
|
|
|
|
|
description?: string | null;
|
|
|
|
|
dueAt?: string | null;
|
|
|
|
|
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 interface ImportContactsResult {
|
|
|
|
|
created: number;
|
|
|
|
|
skipped: number;
|
|
|
|
|
errors: Array<{ row: number; message: string }>;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export interface ProductDto {
|
|
|
|
|
id: string;
|
|
|
|
|
name: string;
|
|
|
|
|
sku: string | null;
|
|
|
|
|
description: string | null;
|
|
|
|
|
unitPrice: number | null;
|
|
|
|
|
currency: string;
|
|
|
|
|
isActive: boolean;
|
|
|
|
|
createdAt: string;
|
|
|
|
|
updatedAt: string;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export interface CreateProductRequest {
|
|
|
|
|
name: string;
|
|
|
|
|
sku?: string;
|
|
|
|
|
description?: string;
|
|
|
|
|
unitPrice?: number | null;
|
|
|
|
|
currency?: string;
|
|
|
|
|
isActive?: boolean;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export interface UpdateProductRequest {
|
|
|
|
|
name?: string;
|
|
|
|
|
sku?: string | null;
|
|
|
|
|
description?: string | null;
|
|
|
|
|
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"
|
|
|
|
|
| "deal_moved"
|
|
|
|
|
| "note_reminder"
|
2026-08-30 09:42:07 +03:30
|
|
|
| "ticket_assigned"
|
|
|
|
|
| "automation"
|
2026-08-29 21:06:30 +03:30
|
|
|
| "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;
|
|
|
|
|
validUntil: string | null;
|
|
|
|
|
totalAmount: 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;
|
|
|
|
|
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;
|
|
|
|
|
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;
|
|
|
|
|
currency: string;
|
|
|
|
|
notes: string | null;
|
|
|
|
|
issuedAt: string | null;
|
|
|
|
|
dueDate: string | null;
|
|
|
|
|
totalAmount: 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;
|
|
|
|
|
currency?: string;
|
|
|
|
|
notes?: string;
|
|
|
|
|
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;
|
|
|
|
|
currency?: string;
|
|
|
|
|
notes?: string | null;
|
|
|
|
|
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<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 ReportsDashboardDto {
|
|
|
|
|
from: string;
|
|
|
|
|
to: string;
|
|
|
|
|
currency: 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;
|
|
|
|
|
}>;
|
|
|
|
|
activities: {
|
|
|
|
|
open: number;
|
|
|
|
|
done: number;
|
|
|
|
|
overdue: number;
|
|
|
|
|
byType: Array<{ type: string; count: number }>;
|
|
|
|
|
};
|
|
|
|
|
team: Array<{
|
|
|
|
|
userId: string;
|
|
|
|
|
fullName: string;
|
|
|
|
|
wonAmount: number;
|
|
|
|
|
wonCount: number;
|
|
|
|
|
activitiesDone: number;
|
|
|
|
|
}>;
|
|
|
|
|
tickets: {
|
|
|
|
|
open: number;
|
|
|
|
|
resolved: number;
|
|
|
|
|
closed: number;
|
|
|
|
|
slaBreached: number;
|
|
|
|
|
avgCsat: number | null;
|
|
|
|
|
csatCount: number;
|
|
|
|
|
};
|
|
|
|
|
peopleCreated: number;
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-30 09:42:07 +03:30
|
|
|
/** برد پروژه / کاریز کارت */
|
|
|
|
|
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>;
|
|
|
|
|
|
2026-08-29 21:06:30 +03:30
|
|
|
export const APP_NAME = "Mento CRM";
|
|
|
|
|
export const APP_NAME_FA = "منتو";
|
|
|
|
|
export const API_PREFIX = "api";
|