feat: routines module; super admin subscription badge, 7-day trial and lifetime plan
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -50,6 +50,7 @@ export type BillingPlanCode =
|
||||
| "quarterly"
|
||||
| "semiannual"
|
||||
| "yearly"
|
||||
| "lifetime"
|
||||
| "custom";
|
||||
|
||||
export type BillingAccountStatus = "active" | "expired" | "suspended";
|
||||
@@ -60,27 +61,36 @@ export const BILLING_PLAN_CODES: BillingPlanCode[] = [
|
||||
"quarterly",
|
||||
"semiannual",
|
||||
"yearly",
|
||||
"lifetime",
|
||||
"custom",
|
||||
];
|
||||
|
||||
export const BILLING_PLAN_LABELS: Record<BillingPlanCode, string> = {
|
||||
trial: "دمو ۱۵ روزه",
|
||||
trial: "دمو ۷ روزه",
|
||||
monthly: "ماهانه",
|
||||
quarterly: "سهماهه",
|
||||
semiannual: "ششماهه",
|
||||
yearly: "یکساله",
|
||||
lifetime: "دائمی",
|
||||
custom: "سفارشی",
|
||||
};
|
||||
|
||||
/** مدت پیشفرض پلنها (روز) — custom ندارد */
|
||||
export const BILLING_TRIAL_DAYS = 7;
|
||||
|
||||
/** مدت پیشفرض پلنها (روز) — custom ندارد؛ lifetime عملاً بدون انقضا (۱۰۰ سال) */
|
||||
export const BILLING_PLAN_DAYS: Record<Exclude<BillingPlanCode, "custom">, number> = {
|
||||
trial: 15,
|
||||
trial: BILLING_TRIAL_DAYS,
|
||||
monthly: 30,
|
||||
quarterly: 90,
|
||||
semiannual: 180,
|
||||
yearly: 365,
|
||||
lifetime: 36500,
|
||||
};
|
||||
|
||||
export function isPermanentBillingPlan(code: string | null | undefined): boolean {
|
||||
return code === "lifetime";
|
||||
}
|
||||
|
||||
export const BILLING_PLAN_DEFAULT_MAX_ORGS: Record<
|
||||
Exclude<BillingPlanCode, "custom">,
|
||||
number
|
||||
@@ -90,6 +100,7 @@ export const BILLING_PLAN_DEFAULT_MAX_ORGS: Record<
|
||||
quarterly: 5,
|
||||
semiannual: 10,
|
||||
yearly: 20,
|
||||
lifetime: 20,
|
||||
};
|
||||
|
||||
export type OrgModuleKey =
|
||||
@@ -126,7 +137,8 @@ export type OrgModuleKey =
|
||||
| "voip"
|
||||
| "reservations"
|
||||
| "leads"
|
||||
| "conversations";
|
||||
| "conversations"
|
||||
| "routines";
|
||||
|
||||
export const ORG_MODULE_KEYS: OrgModuleKey[] = [
|
||||
"customers",
|
||||
@@ -163,6 +175,7 @@ export const ORG_MODULE_KEYS: OrgModuleKey[] = [
|
||||
"voip",
|
||||
"reservations",
|
||||
"conversations",
|
||||
"routines",
|
||||
];
|
||||
|
||||
export const ORG_MODULE_LABELS: Record<OrgModuleKey, string> = {
|
||||
@@ -200,6 +213,7 @@ export const ORG_MODULE_LABELS: Record<OrgModuleKey, string> = {
|
||||
voip: "تلفن سازمانی",
|
||||
reservations: "رزرو و نوبتدهی",
|
||||
conversations: "گفتگو",
|
||||
routines: "کارهای روتین",
|
||||
};
|
||||
|
||||
export const ORG_MODULE_GROUPS: Array<{
|
||||
@@ -219,7 +233,7 @@ export const ORG_MODULE_GROUPS: Array<{
|
||||
"sales_forecast",
|
||||
],
|
||||
},
|
||||
{ title: "فعالیتها", keys: ["activities", "notes"] },
|
||||
{ title: "فعالیتها", keys: ["activities", "routines", "notes"] },
|
||||
{ title: "پشتیبانی", keys: ["tickets", "faq", "conversations"] },
|
||||
{
|
||||
title: "اتوماسیون",
|
||||
@@ -341,7 +355,8 @@ export type DataScope =
|
||||
| "finance"
|
||||
| "tickets"
|
||||
| "notes"
|
||||
| "projects";
|
||||
| "projects"
|
||||
| "routines";
|
||||
|
||||
export const DATA_SCOPES: DataScope[] = [
|
||||
"contacts",
|
||||
@@ -352,6 +367,7 @@ export const DATA_SCOPES: DataScope[] = [
|
||||
"tickets",
|
||||
"notes",
|
||||
"projects",
|
||||
"routines",
|
||||
];
|
||||
|
||||
export const DATA_SCOPE_LABELS: Record<DataScope, string> = {
|
||||
@@ -363,6 +379,7 @@ export const DATA_SCOPE_LABELS: Record<DataScope, string> = {
|
||||
tickets: "تیکتها",
|
||||
notes: "یادداشتها",
|
||||
projects: "پروژهها",
|
||||
routines: "کارهای روتین",
|
||||
};
|
||||
|
||||
export type DataVisibilityScopes = Partial<Record<DataScope, DataVisibility>>;
|
||||
@@ -1658,6 +1675,10 @@ export type NotificationType =
|
||||
| "cheque_due"
|
||||
| "ticket_assigned"
|
||||
| "automation"
|
||||
| "routine_task_reminder"
|
||||
| "routine_task_overdue"
|
||||
| "routine_day_summary"
|
||||
| "routine_task_assigned"
|
||||
| "system";
|
||||
|
||||
export interface NotificationDto {
|
||||
@@ -3381,6 +3402,8 @@ export interface PlatformOrganizationDto {
|
||||
personCount: number;
|
||||
dealCount: number;
|
||||
enabledModules?: OrgModuleKey[];
|
||||
/** اشتراک مالک اصلی محیط */
|
||||
subscription?: SubscriptionSummaryDto | null;
|
||||
members?: PlatformOrgMemberDto[];
|
||||
}
|
||||
|
||||
@@ -4340,3 +4363,458 @@ export interface PlatformConversationStatsDto {
|
||||
totalConfiguredOrgs: number;
|
||||
totalRoomsTracked: number;
|
||||
}
|
||||
|
||||
/* ───────────── کارهای روتین ───────────── */
|
||||
|
||||
export type RoutineTaskStatus =
|
||||
| "todo"
|
||||
| "in_progress"
|
||||
| "completed"
|
||||
| "overdue"
|
||||
| "skipped"
|
||||
| "cancelled";
|
||||
|
||||
export const ROUTINE_TASK_STATUSES: RoutineTaskStatus[] = [
|
||||
"todo",
|
||||
"in_progress",
|
||||
"completed",
|
||||
"overdue",
|
||||
"skipped",
|
||||
"cancelled",
|
||||
];
|
||||
|
||||
export const ROUTINE_TASK_STATUS_LABELS: Record<RoutineTaskStatus, string> = {
|
||||
todo: "در انتظار",
|
||||
in_progress: "در حال انجام",
|
||||
completed: "انجام شد",
|
||||
overdue: "عقبافتاده",
|
||||
skipped: "رد شده",
|
||||
cancelled: "لغو شده",
|
||||
};
|
||||
|
||||
export type RoutineRecurrence =
|
||||
| "daily"
|
||||
| "weekdays"
|
||||
| "weekly"
|
||||
| "biweekly"
|
||||
| "monthly"
|
||||
| "every_n_days"
|
||||
| "specific_dates";
|
||||
|
||||
export const ROUTINE_RECURRENCES: RoutineRecurrence[] = [
|
||||
"daily",
|
||||
"weekdays",
|
||||
"weekly",
|
||||
"biweekly",
|
||||
"monthly",
|
||||
"every_n_days",
|
||||
"specific_dates",
|
||||
];
|
||||
|
||||
export const ROUTINE_RECURRENCE_LABELS: Record<RoutineRecurrence, string> = {
|
||||
daily: "هر روز",
|
||||
weekdays: "روزهای مشخص هفته",
|
||||
weekly: "هر هفته",
|
||||
biweekly: "هر دو هفته",
|
||||
monthly: "هر ماه",
|
||||
every_n_days: "هر چند روز یکبار",
|
||||
specific_dates: "تاریخهای مشخص",
|
||||
};
|
||||
|
||||
/** ۰ = شنبه … ۶ = جمعه */
|
||||
export const ROUTINE_WEEKDAY_LABELS = [
|
||||
"شنبه",
|
||||
"یکشنبه",
|
||||
"دوشنبه",
|
||||
"سهشنبه",
|
||||
"چهارشنبه",
|
||||
"پنجشنبه",
|
||||
"جمعه",
|
||||
];
|
||||
|
||||
export type RoutineTaskSource =
|
||||
| "routine"
|
||||
| "manual"
|
||||
| "follow_up"
|
||||
| "lead"
|
||||
| "customer"
|
||||
| "deal";
|
||||
|
||||
export const ROUTINE_TASK_SOURCE_LABELS: Record<RoutineTaskSource, string> = {
|
||||
routine: "روتین",
|
||||
manual: "دستی",
|
||||
follow_up: "پیگیری",
|
||||
lead: "لید",
|
||||
customer: "مشتری",
|
||||
deal: "معامله",
|
||||
};
|
||||
|
||||
/** اتصال Task به پرونده: none = ندارد | optional = اختیاری | lead/customer = الزامی */
|
||||
export type RoutineEntityLink = "none" | "optional" | "lead" | "customer";
|
||||
|
||||
export const ROUTINE_ENTITY_LINK_LABELS: Record<RoutineEntityLink, string> = {
|
||||
none: "بدون اتصال",
|
||||
optional: "اختیاری",
|
||||
lead: "الزامی به لید",
|
||||
customer: "الزامی به مشتری",
|
||||
};
|
||||
|
||||
/** وضعیت یک روز: کامل | ناقص | از دست رفته | بدون Task */
|
||||
export type RoutineDayState = "complete" | "partial" | "missed" | "empty";
|
||||
|
||||
export interface RoutineCategoryDto {
|
||||
id: string;
|
||||
name: string;
|
||||
color: string;
|
||||
sortOrder: number;
|
||||
}
|
||||
|
||||
export interface SaveRoutineCategoryRequest {
|
||||
name: string;
|
||||
color?: string;
|
||||
sortOrder?: number;
|
||||
}
|
||||
|
||||
export type RoutineEntityType =
|
||||
| "lead"
|
||||
| "person"
|
||||
| "company"
|
||||
| "deal"
|
||||
| "invoice"
|
||||
| "activity";
|
||||
|
||||
export interface RoutineEntityRefDto {
|
||||
type: RoutineEntityType;
|
||||
id: string;
|
||||
label: string;
|
||||
}
|
||||
|
||||
export interface RoutineTaskRulesDto {
|
||||
allowSkip: boolean;
|
||||
skipReasonRequired: boolean;
|
||||
reportRequired: boolean;
|
||||
allowAfterDeadline: boolean;
|
||||
entityLink: RoutineEntityLink;
|
||||
}
|
||||
|
||||
export interface RoutineTaskDto {
|
||||
id: string;
|
||||
routineId: string | null;
|
||||
title: string;
|
||||
description: string | null;
|
||||
status: RoutineTaskStatus;
|
||||
priority: ActivityPriority;
|
||||
category: RoutineCategoryDto | null;
|
||||
/** روز برنامه (YYYY-MM-DD به وقت تهران) */
|
||||
scheduledFor: string;
|
||||
dueAt: string | null;
|
||||
startedAt: string | null;
|
||||
completedAt: string | null;
|
||||
resultNote: string | null;
|
||||
skipReason: string | null;
|
||||
source: RoutineTaskSource;
|
||||
isMandatory: boolean;
|
||||
rules: RoutineTaskRulesDto;
|
||||
assignee: UserSummaryDto | null;
|
||||
createdBy: UserSummaryDto | null;
|
||||
entities: RoutineEntityRefDto[];
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
}
|
||||
|
||||
export interface RoutineTaskEventDto {
|
||||
id: string;
|
||||
type: "created" | "status_changed" | "rescheduled" | "note" | "reassigned" | "updated";
|
||||
fromStatus: RoutineTaskStatus | null;
|
||||
toStatus: RoutineTaskStatus | null;
|
||||
note: string | null;
|
||||
actor: UserSummaryDto | null;
|
||||
createdAt: string;
|
||||
}
|
||||
|
||||
export interface RoutineTaskDetailDto extends RoutineTaskDto {
|
||||
events: RoutineTaskEventDto[];
|
||||
}
|
||||
|
||||
export interface RoutineDayStatsDto {
|
||||
total: number;
|
||||
completed: number;
|
||||
remaining: number;
|
||||
overdue: number;
|
||||
/** null وقتی Task ندارد */
|
||||
percent: number | null;
|
||||
}
|
||||
|
||||
export interface RoutineScoreDto {
|
||||
today: number | null;
|
||||
yesterday: number | null;
|
||||
week: number | null;
|
||||
month: number | null;
|
||||
/** روزهای متوالی با انجام همه Taskهای اجباری */
|
||||
streak: number;
|
||||
}
|
||||
|
||||
export interface RoutineTodayDto {
|
||||
date: string;
|
||||
tasks: RoutineTaskDto[];
|
||||
overdue: RoutineTaskDto[];
|
||||
stats: RoutineDayStatsDto;
|
||||
score: RoutineScoreDto;
|
||||
}
|
||||
|
||||
export interface CreateRoutineTaskRequest {
|
||||
title: string;
|
||||
description?: string | null;
|
||||
/** ISO؛ اگر خالی باشد scheduledFor یا امروز */
|
||||
dueAt?: string | null;
|
||||
scheduledFor?: string | null;
|
||||
priority?: ActivityPriority;
|
||||
categoryId?: string | null;
|
||||
assigneeId?: string | null;
|
||||
isMandatory?: boolean;
|
||||
source?: RoutineTaskSource;
|
||||
leadId?: string | null;
|
||||
personId?: string | null;
|
||||
companyId?: string | null;
|
||||
dealId?: string | null;
|
||||
invoiceId?: string | null;
|
||||
activityId?: string | null;
|
||||
}
|
||||
|
||||
export type UpdateRoutineTaskRequest = Partial<
|
||||
Omit<CreateRoutineTaskRequest, "source" | "activityId">
|
||||
>;
|
||||
|
||||
export interface CompleteRoutineTaskRequest {
|
||||
note?: string | null;
|
||||
}
|
||||
|
||||
export interface SkipRoutineTaskRequest {
|
||||
reason?: string | null;
|
||||
}
|
||||
|
||||
export interface RescheduleRoutineTaskRequest {
|
||||
dueAt: string;
|
||||
}
|
||||
|
||||
export interface RoutineDto {
|
||||
id: string;
|
||||
title: string;
|
||||
description: string | null;
|
||||
assignee: UserSummaryDto | null;
|
||||
team: { id: string; name: string } | null;
|
||||
category: RoutineCategoryDto | null;
|
||||
priority: ActivityPriority;
|
||||
/** HH:mm */
|
||||
timeOfDay: string;
|
||||
durationMinutes: number | null;
|
||||
startDate: string;
|
||||
endDate: string | null;
|
||||
recurrence: RoutineRecurrence;
|
||||
weekdays: number[];
|
||||
intervalDays: number | null;
|
||||
monthDay: number | null;
|
||||
specificDates: string[];
|
||||
isMandatory: boolean;
|
||||
allowSkip: boolean;
|
||||
skipReasonRequired: boolean;
|
||||
reportRequired: boolean;
|
||||
allowAfterDeadline: boolean;
|
||||
carryOverOverdue: boolean;
|
||||
entityLink: RoutineEntityLink;
|
||||
reminderMinutesBefore: number | null;
|
||||
isActive: boolean;
|
||||
recurrenceLabel: string;
|
||||
/** درصد اجرا در ۳۰ روز اخیر */
|
||||
executionRate: number | null;
|
||||
executionTotal: number;
|
||||
executionDone: number;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
}
|
||||
|
||||
export interface SaveRoutineRequest {
|
||||
title: string;
|
||||
description?: string | null;
|
||||
assigneeId?: string | null;
|
||||
teamId?: string | null;
|
||||
categoryId?: string | null;
|
||||
priority?: ActivityPriority;
|
||||
timeOfDay: string;
|
||||
durationMinutes?: number | null;
|
||||
startDate: string;
|
||||
endDate?: string | null;
|
||||
recurrence: RoutineRecurrence;
|
||||
weekdays?: number[];
|
||||
intervalDays?: number | null;
|
||||
monthDay?: number | null;
|
||||
specificDates?: string[];
|
||||
isMandatory?: boolean;
|
||||
allowSkip?: boolean;
|
||||
skipReasonRequired?: boolean;
|
||||
reportRequired?: boolean;
|
||||
allowAfterDeadline?: boolean;
|
||||
carryOverOverdue?: boolean;
|
||||
entityLink?: RoutineEntityLink;
|
||||
reminderMinutesBefore?: number | null;
|
||||
isActive?: boolean;
|
||||
}
|
||||
|
||||
export interface RoutineContextDto {
|
||||
isManager: boolean;
|
||||
users: UserSummaryDto[];
|
||||
teams: Array<{ id: string; name: string }>;
|
||||
categories: RoutineCategoryDto[];
|
||||
}
|
||||
|
||||
export interface RoutineCalendarDayDto {
|
||||
date: string;
|
||||
total: number;
|
||||
completed: number;
|
||||
overdue: number;
|
||||
percent: number | null;
|
||||
state: RoutineDayState;
|
||||
}
|
||||
|
||||
export interface RoutineCalendarDto {
|
||||
days: RoutineCalendarDayDto[];
|
||||
tasks: RoutineTaskDto[];
|
||||
}
|
||||
|
||||
export interface RoutineTrendPointDto {
|
||||
date: string;
|
||||
total: number;
|
||||
completed: number;
|
||||
percent: number | null;
|
||||
}
|
||||
|
||||
export interface RoutineCategoryStatDto {
|
||||
categoryId: string | null;
|
||||
name: string;
|
||||
color: string;
|
||||
total: number;
|
||||
completed: number;
|
||||
percent: number | null;
|
||||
}
|
||||
|
||||
export interface RoutineStatusBreakdownDto {
|
||||
completed: number;
|
||||
notDone: number;
|
||||
overdue: number;
|
||||
skipped: number;
|
||||
cancelled: number;
|
||||
}
|
||||
|
||||
export interface RoutinePerformanceDto {
|
||||
from: string;
|
||||
to: string;
|
||||
total: number;
|
||||
completed: number;
|
||||
notDone: number;
|
||||
overdue: number;
|
||||
percent: number | null;
|
||||
avgCompletionMinutes: number | null;
|
||||
completeDays: number;
|
||||
partialDays: number;
|
||||
missedDays: number;
|
||||
streak: number;
|
||||
status: RoutineStatusBreakdownDto;
|
||||
trend: RoutineTrendPointDto[];
|
||||
byCategory: RoutineCategoryStatDto[];
|
||||
}
|
||||
|
||||
export interface RoutineMemberStatDto {
|
||||
user: UserSummaryDto;
|
||||
total: number;
|
||||
completed: number;
|
||||
overdue: number;
|
||||
skipped: number;
|
||||
percent: number | null;
|
||||
streak: number;
|
||||
avgCompletionMinutes: number | null;
|
||||
}
|
||||
|
||||
export interface RoutineTeamPerformanceDto {
|
||||
from: string;
|
||||
to: string;
|
||||
total: number;
|
||||
completed: number;
|
||||
overdue: number;
|
||||
percent: number | null;
|
||||
status: RoutineStatusBreakdownDto;
|
||||
trend: RoutineTrendPointDto[];
|
||||
byCategory: RoutineCategoryStatDto[];
|
||||
members: RoutineMemberStatDto[];
|
||||
}
|
||||
|
||||
export interface RoutinePeriodTotalsDto {
|
||||
total: number;
|
||||
completed: number;
|
||||
notDone: number;
|
||||
overdue: number;
|
||||
percent: number | null;
|
||||
completeDays: number;
|
||||
partialDays: number;
|
||||
missedDays: number;
|
||||
avgPercent: number | null;
|
||||
}
|
||||
|
||||
export interface RoutineWeeklyReportDto {
|
||||
from: string;
|
||||
to: string;
|
||||
days: RoutineCalendarDayDto[];
|
||||
totals: RoutinePeriodTotalsDto;
|
||||
previous: RoutinePeriodTotalsDto;
|
||||
}
|
||||
|
||||
export interface RoutineMonthlyReportDto {
|
||||
from: string;
|
||||
to: string;
|
||||
totals: RoutinePeriodTotalsDto;
|
||||
previous: RoutinePeriodTotalsDto;
|
||||
status: RoutineStatusBreakdownDto;
|
||||
days: RoutineCalendarDayDto[];
|
||||
byUser: RoutineMemberStatDto[];
|
||||
}
|
||||
|
||||
export interface RoutineExecutionRowDto {
|
||||
routineId: string;
|
||||
title: string;
|
||||
assigneeLabel: string;
|
||||
recurrenceLabel: string;
|
||||
occurrences: number;
|
||||
completed: number;
|
||||
notDone: number;
|
||||
skipped: number;
|
||||
percent: number | null;
|
||||
}
|
||||
|
||||
export interface RoutineHistoryDayDto {
|
||||
date: string;
|
||||
total: number;
|
||||
completed: number;
|
||||
overdue: number;
|
||||
skipped: number;
|
||||
cancelled: number;
|
||||
percent: number | null;
|
||||
state: RoutineDayState;
|
||||
}
|
||||
|
||||
export interface RoutineStatsQuery {
|
||||
from?: string;
|
||||
to?: string;
|
||||
userId?: string;
|
||||
teamId?: string;
|
||||
categoryId?: string;
|
||||
source?: RoutineTaskSource;
|
||||
status?: RoutineTaskStatus;
|
||||
}
|
||||
|
||||
export interface RoutineUserSettingsDto {
|
||||
/** چند دقیقه قبل از Deadline یادآوری شود؛ null = خاموش */
|
||||
remindBeforeMinutes: number | null;
|
||||
endOfDaySummary: boolean;
|
||||
overdueAlert: boolean;
|
||||
/** ساعت ارسال خلاصه پایان روز (۰ تا ۲۳) */
|
||||
summaryHour: number;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user