forked from Yara724/api
1250 lines
42 KiB
TypeScript
1250 lines
42 KiB
TypeScript
import {
|
||
Injectable,
|
||
NotFoundException,
|
||
BadRequestException,
|
||
ConflictException,
|
||
Logger,
|
||
} from "@nestjs/common";
|
||
import { WorkflowStepDbService } from "./entities/db-service/workflow-step.db.service";
|
||
import { CreateWorkflowStepDto } from "./dto/create-workflow-step.dto";
|
||
import { UpdateWorkflowStepDto } from "./dto/update-workflow-step.dto";
|
||
import { WorkflowStep } from "src/Types&Enums/blame-request-management/blameWorkflow-steps.enum";
|
||
import { ClaimWorkflowStep } from "src/Types&Enums/claim-request-management/claim-workflow-steps.enum";
|
||
import { WorkflowStepModel } from "./entities/schema/workflow-step.schema";
|
||
|
||
// Type alias for combined workflow steps
|
||
type AnyWorkflowStep = WorkflowStep | ClaimWorkflowStep;
|
||
|
||
@Injectable()
|
||
export class WorkflowStepManagementService {
|
||
private readonly logger = new Logger(WorkflowStepManagementService.name);
|
||
|
||
constructor(private readonly workflowStepDbService: WorkflowStepDbService) {}
|
||
|
||
async onModuleInit() {
|
||
try {
|
||
this.logger.log("Seeding workflow steps...");
|
||
const result = await this.initializeDefaultSteps();
|
||
this.logger.log(
|
||
`Workflow steps seeded — upserted: ${result?.upsertedCount ?? 0}, modified: ${result?.modifiedCount ?? 0}, matched: ${result?.matchedCount ?? 0}`,
|
||
);
|
||
} catch (err) {
|
||
this.logger.error(
|
||
"Failed to seed workflow steps",
|
||
err instanceof Error ? err.stack : String(err),
|
||
);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Create a new workflow step
|
||
*/
|
||
async create(createDto: CreateWorkflowStepDto): Promise<WorkflowStepModel> {
|
||
// Check if step already exists
|
||
const exists = await this.workflowStepDbService.exists(createDto.stepKey);
|
||
if (exists) {
|
||
throw new ConflictException(
|
||
`Workflow step with key ${createDto.stepKey} already exists`,
|
||
);
|
||
}
|
||
|
||
return await this.workflowStepDbService.create(createDto);
|
||
}
|
||
|
||
/**
|
||
* Get all active workflow steps
|
||
*/
|
||
async findAll(): Promise<WorkflowStepModel[]> {
|
||
return await this.workflowStepDbService.findAll();
|
||
}
|
||
|
||
/**
|
||
* Get all workflow steps including inactive ones
|
||
*/
|
||
async findAllIncludingInactive(): Promise<WorkflowStepModel[]> {
|
||
return await this.workflowStepDbService.findAllIncludingInactive();
|
||
}
|
||
|
||
/**
|
||
* Find a workflow step by ID
|
||
*/
|
||
async findById(id: string): Promise<WorkflowStepModel> {
|
||
const step = await this.workflowStepDbService.findById(id);
|
||
if (!step) {
|
||
throw new NotFoundException(`Workflow step with ID ${id} not found`);
|
||
}
|
||
return step;
|
||
}
|
||
|
||
/**
|
||
* Find a workflow step by step key (supports both blame and claim workflows)
|
||
*/
|
||
async findByStepKey(stepKey: AnyWorkflowStep): Promise<WorkflowStepModel> {
|
||
const step = await this.workflowStepDbService.findByStepKey(stepKey as any);
|
||
if (!step) {
|
||
throw new NotFoundException(
|
||
`Workflow step with key ${stepKey} not found`,
|
||
);
|
||
}
|
||
return step;
|
||
}
|
||
|
||
/**
|
||
* Find a workflow step by stepKey or stepNumber (intelligent lookup)
|
||
*/
|
||
async findByStepKeyOrNumber(
|
||
identifier: string | number,
|
||
): Promise<WorkflowStepModel> {
|
||
// If identifier is a number or can be parsed as a number, search by stepNumber
|
||
const stepNumber =
|
||
typeof identifier === "number" ? identifier : Number(identifier);
|
||
|
||
if (!isNaN(stepNumber)) {
|
||
// Search by step number
|
||
const step =
|
||
await this.workflowStepDbService.findByStepNumber(stepNumber);
|
||
if (step) {
|
||
return step;
|
||
}
|
||
}
|
||
|
||
// Otherwise, search by stepKey (supports both blame and claim)
|
||
const step = await this.workflowStepDbService.findByStepKey(
|
||
identifier as any,
|
||
);
|
||
if (!step) {
|
||
throw new NotFoundException(
|
||
`Workflow step with identifier ${identifier} not found`,
|
||
);
|
||
}
|
||
|
||
return step;
|
||
}
|
||
|
||
/**
|
||
* Find steps by category
|
||
*/
|
||
async findByCategory(category: string): Promise<WorkflowStepModel[]> {
|
||
return await this.workflowStepDbService.findByCategory(category);
|
||
}
|
||
|
||
/**
|
||
* Update a workflow step by ID
|
||
*/
|
||
async update(
|
||
id: string,
|
||
updateDto: UpdateWorkflowStepDto,
|
||
): Promise<WorkflowStepModel> {
|
||
const updated = await this.workflowStepDbService.update(id, updateDto);
|
||
if (!updated) {
|
||
throw new NotFoundException(`Workflow step with ID ${id} not found`);
|
||
}
|
||
return updated;
|
||
}
|
||
|
||
/**
|
||
* Update a workflow step by step key (supports both blame and claim)
|
||
*/
|
||
async updateByStepKey(
|
||
stepKey: AnyWorkflowStep,
|
||
updateDto: UpdateWorkflowStepDto,
|
||
): Promise<WorkflowStepModel> {
|
||
const updated = await this.workflowStepDbService.updateByStepKey(
|
||
stepKey as any,
|
||
updateDto,
|
||
);
|
||
if (!updated) {
|
||
throw new NotFoundException(
|
||
`Workflow step with key ${stepKey} not found`,
|
||
);
|
||
}
|
||
return updated;
|
||
}
|
||
|
||
/**
|
||
* Soft delete a workflow step
|
||
*/
|
||
async remove(id: string): Promise<WorkflowStepModel> {
|
||
const removed = await this.workflowStepDbService.remove(id);
|
||
if (!removed) {
|
||
throw new NotFoundException(`Workflow step with ID ${id} not found`);
|
||
}
|
||
return removed;
|
||
}
|
||
|
||
/**
|
||
* Get the next possible steps from a given step (supports both blame and claim)
|
||
*/
|
||
async getNextSteps(stepKey: AnyWorkflowStep): Promise<WorkflowStepModel[]> {
|
||
const currentStep = await this.findByStepKey(stepKey);
|
||
|
||
if (
|
||
!currentStep.nextPossibleSteps ||
|
||
currentStep.nextPossibleSteps.length === 0
|
||
) {
|
||
return [];
|
||
}
|
||
|
||
const nextSteps: WorkflowStepModel[] = [];
|
||
for (const nextStepKey of currentStep.nextPossibleSteps) {
|
||
const step = await this.workflowStepDbService.findByStepKey(
|
||
nextStepKey as any,
|
||
);
|
||
if (step && step.isActive) {
|
||
nextSteps.push(step);
|
||
}
|
||
}
|
||
|
||
return nextSteps;
|
||
}
|
||
|
||
/**
|
||
* Validate if a step can be accessed based on completed steps (supports both blame and claim)
|
||
*/
|
||
async validateStepAccess(
|
||
stepKey: AnyWorkflowStep,
|
||
completedSteps: AnyWorkflowStep[],
|
||
): Promise<{ canAccess: boolean; missingSteps: AnyWorkflowStep[] }> {
|
||
const step = await this.findByStepKey(stepKey);
|
||
|
||
if (
|
||
!step.requiredPreviousSteps ||
|
||
step.requiredPreviousSteps.length === 0
|
||
) {
|
||
return { canAccess: true, missingSteps: [] };
|
||
}
|
||
|
||
const missingSteps = step.requiredPreviousSteps.filter(
|
||
(requiredStep) => !completedSteps.includes(requiredStep as any),
|
||
);
|
||
|
||
return {
|
||
canAccess: missingSteps.length === 0,
|
||
missingSteps,
|
||
};
|
||
}
|
||
|
||
/**
|
||
* Get the fields for a specific step (for dynamic form generation)
|
||
*/
|
||
async getStepFields(stepKey: AnyWorkflowStep): Promise<any> {
|
||
const step = await this.findByStepKey(stepKey);
|
||
|
||
return {
|
||
stepKey: step.stepKey,
|
||
stepName_fa: step.stepName_fa,
|
||
stepName_en: step.stepName_en,
|
||
description_fa: step.description_fa,
|
||
description_en: step.description_en,
|
||
fields: step.fields || [],
|
||
};
|
||
}
|
||
|
||
/**
|
||
* Bulk upsert workflow steps
|
||
*/
|
||
async bulkUpsert(steps: CreateWorkflowStepDto[]): Promise<any> {
|
||
return await this.workflowStepDbService.bulkUpsert(steps);
|
||
}
|
||
|
||
/**
|
||
* Initialize default workflow steps
|
||
*/
|
||
async initializeDefaultSteps(): Promise<any> {
|
||
const defaultSteps: CreateWorkflowStepDto[] = [
|
||
{
|
||
stepKey: WorkflowStep.CREATED,
|
||
type: "THIRD_PARTY",
|
||
stepNumber: 1,
|
||
isActive: true,
|
||
stepName_fa: "ایجاد نوع درخواست",
|
||
stepName_en: "request_type",
|
||
description_fa: "انتخاب نوع درخواست توسط کاربر (شخص ثالث یا بدنه)",
|
||
description_en: "Select request type by user (THIRD_PARTY or CAR_BODY)",
|
||
category: "INITIAL",
|
||
requiredPreviousSteps: [],
|
||
nextPossibleSteps: [WorkflowStep.FIRST_BLAME_CONFESSION],
|
||
allowedRoles: ["user"],
|
||
estimatedDuration: 1,
|
||
fields: [
|
||
{
|
||
id: "507f1f77bcf86cd799439011" as any,
|
||
name_fa: "نوع درخواست",
|
||
name_en: "requestType",
|
||
label_fa: "نوع درخواست",
|
||
label_en: "Request Type",
|
||
placeholder_fa: "نوع درخواست خود را انتخاب کنید",
|
||
placeholder_en: "Select your request type",
|
||
value: [
|
||
{
|
||
label_fa: "شخص ثالث",
|
||
label_en: "Third Party",
|
||
value: "THIRD_PARTY",
|
||
},
|
||
{ label_fa: "بدنه", label_en: "Car Body", value: "CAR_BODY" },
|
||
],
|
||
dataType: "select",
|
||
required: true,
|
||
order: 1,
|
||
isActive: true,
|
||
validation: {
|
||
enum: ["THIRD_PARTY", "CAR_BODY"],
|
||
},
|
||
},
|
||
],
|
||
metadata: {
|
||
icon: "clipboard-list",
|
||
color: "#8B5CF6",
|
||
},
|
||
},
|
||
{
|
||
stepKey: WorkflowStep.FIRST_BLAME_CONFESSION,
|
||
type: "THIRD_PARTY",
|
||
stepNumber: 2,
|
||
isActive: true,
|
||
stepName_fa: "اعتراف طرف اول",
|
||
stepName_en: "First Party Blame Confession",
|
||
description_fa: "طرف اول وضعیت تقصیر و اعتراف خود را مشخص میکند",
|
||
description_en: "First party declares blame status and confession",
|
||
category: "FIRST_PARTY",
|
||
requiredPreviousSteps: [WorkflowStep.CREATED],
|
||
nextPossibleSteps: [WorkflowStep.FIRST_VIDEO],
|
||
allowedRoles: ["user"],
|
||
estimatedDuration: 2,
|
||
fields: [
|
||
{
|
||
id: "507f1f77bcf86cd799439012" as any,
|
||
name_fa: "وضعیت تقصیر",
|
||
name_en: "blameStatus",
|
||
label_fa: "وضعیت تقصیر",
|
||
label_en: "Blame Status",
|
||
placeholder_fa: "وضعیت تقصیر را انتخاب کنید",
|
||
placeholder_en: "Select blame status",
|
||
value: [
|
||
{ label_fa: "موافق", label_en: "Agreed", value: "AGREED" },
|
||
{
|
||
label_fa: "مخالف",
|
||
label_en: "Disagreement",
|
||
value: "DISAGREEMENT",
|
||
},
|
||
],
|
||
dataType: "select",
|
||
required: true,
|
||
order: 1,
|
||
isActive: true,
|
||
validation: {
|
||
enum: ["AGREED", "DISAGREEMENT"],
|
||
},
|
||
},
|
||
{
|
||
id: "507f1f77bcf86cd799439013" as any,
|
||
name_fa: "اعتراف",
|
||
name_en: "confess",
|
||
label_fa: "اعتراف",
|
||
label_en: "Confession",
|
||
placeholder_fa: "نوع اعتراف خود را انتخاب کنید",
|
||
placeholder_en: "Select your confession type",
|
||
value: [
|
||
{ label_fa: "مقصر", label_en: "Guilty", value: "guilty" },
|
||
{ label_fa: "آسیب دیده", label_en: "Damaged", value: "damaged" },
|
||
],
|
||
dataType: "select",
|
||
required: true,
|
||
order: 2,
|
||
isActive: true,
|
||
validation: {
|
||
enum: ["guilty", "damaged"],
|
||
},
|
||
},
|
||
],
|
||
metadata: {
|
||
icon: "balance-scale",
|
||
color: "#F59E0B",
|
||
requiresAuth: true,
|
||
},
|
||
},
|
||
{
|
||
stepKey: WorkflowStep.FIRST_VIDEO,
|
||
type: "THIRD_PARTY",
|
||
stepNumber: 3,
|
||
isActive: true,
|
||
stepName_fa: "ضبط ویدیو طرف اول",
|
||
stepName_en: "First Party Video Capture",
|
||
description_fa:
|
||
"ضبط ویدیو مستقیم از دوربین توسط طرف اول (گالری مجاز نیست)",
|
||
description_en:
|
||
"Live video capture from camera by first party (gallery not allowed)",
|
||
category: "FIRST_PARTY",
|
||
requiredPreviousSteps: [WorkflowStep.FIRST_BLAME_CONFESSION],
|
||
nextPossibleSteps: [WorkflowStep.FIRST_INITIAL_FORM],
|
||
allowedRoles: ["user"],
|
||
estimatedDuration: 3,
|
||
fields: [
|
||
{
|
||
id: "507f1f77bcf86cd799439014" as any,
|
||
name_fa: "فایل ویدیو",
|
||
name_en: "file",
|
||
label_fa: "ضبط ویدیو",
|
||
label_en: "Video Recording",
|
||
placeholder_fa: "ویدیو خود را از دوربین ضبط کنید",
|
||
placeholder_en: "Record your video from camera",
|
||
value: null,
|
||
dataType: "video-capture",
|
||
required: true,
|
||
order: 1,
|
||
isActive: true,
|
||
validation: {
|
||
maxSize: 52428800,
|
||
maxDuration: 180,
|
||
allowedTypes: ["video/mp4", "video/webm"],
|
||
captureMode: "camera-only",
|
||
allowGallery: false,
|
||
},
|
||
},
|
||
],
|
||
metadata: {
|
||
icon: "video-camera",
|
||
color: "#EF4444",
|
||
captureType: "live-camera",
|
||
allowGallerySelection: false,
|
||
requiresCamera: true,
|
||
},
|
||
},
|
||
{
|
||
stepKey: WorkflowStep.FIRST_INITIAL_FORM,
|
||
type: "THIRD_PARTY",
|
||
stepNumber: 4,
|
||
isActive: true,
|
||
stepName_fa: "فرم اصلی اطلاعات راننده و بیمهگذار",
|
||
stepName_en: "Main Form - Driver & Insurer Information",
|
||
description_fa: "وارد کردن اطلاعات کامل راننده، بیمهگذار و خودرو",
|
||
description_en:
|
||
"Enter complete driver, insurer and vehicle information",
|
||
category: "FIRST_PARTY",
|
||
requiredPreviousSteps: [WorkflowStep.FIRST_VIDEO],
|
||
nextPossibleSteps: [WorkflowStep.FIRST_LOCATION],
|
||
allowedRoles: ["user"],
|
||
estimatedDuration: 5,
|
||
fields: [
|
||
{
|
||
id: "507f1f77bcf86cd799439015" as any,
|
||
name_fa: "کد ملی مالک",
|
||
name_en: "nationalCodeOfOwner",
|
||
label_fa: "کد ملی مالک",
|
||
label_en: "National Code of Owner",
|
||
placeholder_fa: "کد ملی ۱۰ رقمی مالک را وارد کنید",
|
||
placeholder_en: "Enter 10-digit national code of insurer",
|
||
value: null,
|
||
dataType: "string",
|
||
required: true,
|
||
order: 1,
|
||
isActive: true,
|
||
validation: {
|
||
pattern: "^[0-9]{10}$",
|
||
minLength: 10,
|
||
maxLength: 10,
|
||
},
|
||
},
|
||
{
|
||
id: "507f1f77bcf86cd799439016" as any,
|
||
name_fa: "کد ملی راننده",
|
||
name_en: "nationalCodeOfDriver",
|
||
label_fa: "کد ملی راننده",
|
||
label_en: "National Code of Driver",
|
||
placeholder_fa: "کد ملی ۱۰ رقمی راننده را وارد کنید",
|
||
placeholder_en: "Enter 10-digit national code of driver",
|
||
value: null,
|
||
dataType: "string",
|
||
required: true,
|
||
order: 2,
|
||
isActive: true,
|
||
validation: {
|
||
pattern: "^[0-9]{10}$",
|
||
minLength: 10,
|
||
maxLength: 10,
|
||
},
|
||
},
|
||
{
|
||
id: "507f1f77bcf86cd799439017" as any,
|
||
name_fa: "شماره گواهینامه بیمهگذار",
|
||
name_en: "insurerLicense",
|
||
label_fa: "شماره گواهینامه بیمهگذار",
|
||
label_en: "Insurer License Number",
|
||
placeholder_fa: "شماره گواهینامه بیمهگذار را وارد کنید",
|
||
placeholder_en: "Enter insurer license number",
|
||
value: null,
|
||
dataType: "string",
|
||
required: true,
|
||
order: 3,
|
||
isActive: true,
|
||
validation: {
|
||
minLength: 5,
|
||
maxLength: 20,
|
||
},
|
||
},
|
||
{
|
||
id: "507f1f77bcf86cd799439018" as any,
|
||
name_fa: "شماره گواهینامه راننده",
|
||
name_en: "driverLicense",
|
||
label_fa: "شماره گواهینامه راننده",
|
||
label_en: "Driver License Number",
|
||
placeholder_fa: "شماره گواهینامه راننده را وارد کنید",
|
||
placeholder_en: "Enter driver license number",
|
||
value: null,
|
||
dataType: "string",
|
||
required: true,
|
||
order: 4,
|
||
isActive: true,
|
||
validation: {
|
||
minLength: 5,
|
||
maxLength: 20,
|
||
},
|
||
},
|
||
{
|
||
id: "507f1f77bcf86cd799439019" as any,
|
||
name_fa: "پلاک خودرو",
|
||
name_en: "plate",
|
||
label_fa: "پلاک خودرو",
|
||
label_en: "Vehicle Plate",
|
||
placeholder_fa: "اطلاعات پلاک خودرو را وارد کنید",
|
||
placeholder_en: "Enter vehicle plate information",
|
||
value: null,
|
||
dataType: "object",
|
||
required: true,
|
||
order: 5,
|
||
isActive: true,
|
||
validation: {
|
||
type: "object",
|
||
properties: {
|
||
leftDigits: { type: "number", min: 10, max: 99 },
|
||
centerAlphabet: { type: "string", pattern: "^[آ-ی]{1}$" },
|
||
centerDigits: { type: "number", min: 100, max: 999 },
|
||
ir: { type: "number", min: 10, max: 99 },
|
||
},
|
||
},
|
||
},
|
||
{
|
||
id: "507f1f77bcf86cd79943901a" as any,
|
||
name_fa: "راننده همان بیمهگذار است",
|
||
name_en: "driverIsInsurer",
|
||
label_fa: "راننده همان بیمهگذار است",
|
||
label_en: "Driver is Insurer",
|
||
placeholder_fa: "",
|
||
placeholder_en: "",
|
||
value: null,
|
||
dataType: "boolean",
|
||
required: true,
|
||
order: 6,
|
||
isActive: true,
|
||
validation: {
|
||
type: "boolean",
|
||
},
|
||
},
|
||
{
|
||
id: "507f1f77bcf86cd79943901b" as any,
|
||
name_fa: "خودرو صفر است",
|
||
name_en: "isNewCar",
|
||
label_fa: "خودرو صفر است",
|
||
label_en: "Is New Car",
|
||
placeholder_fa: "",
|
||
placeholder_en: "",
|
||
value: null,
|
||
dataType: "boolean",
|
||
required: true,
|
||
order: 7,
|
||
isActive: true,
|
||
validation: {
|
||
type: "boolean",
|
||
},
|
||
},
|
||
{
|
||
id: "507f1f77bcf86cd79943901c" as any,
|
||
name_fa: "بدون گواهی",
|
||
name_en: "userNoCertificate",
|
||
label_fa: "کاربر گواهی ندارد",
|
||
label_en: "User Has No Certificate",
|
||
placeholder_fa: "",
|
||
placeholder_en: "",
|
||
value: null,
|
||
dataType: "boolean",
|
||
required: true,
|
||
order: 8,
|
||
isActive: true,
|
||
validation: {
|
||
type: "boolean",
|
||
},
|
||
},
|
||
{
|
||
id: "507f1f77bcf86cd79943901d" as any,
|
||
name_fa: "تاریخ تولد بیمهگذار",
|
||
name_en: "insurerBirthday",
|
||
label_fa: "تاریخ تولد بیمهگذار",
|
||
label_en: "Insurer Birthday",
|
||
placeholder_fa: "تاریخ تولد بیمهگذار (timestamp)",
|
||
placeholder_en: "Insurer birthday (timestamp)",
|
||
value: null,
|
||
dataType: "number",
|
||
required: true,
|
||
order: 9,
|
||
isActive: true,
|
||
validation: {
|
||
type: "number",
|
||
min: 0,
|
||
},
|
||
},
|
||
{
|
||
id: "507f1f77bcf86cd79943901e" as any,
|
||
name_fa: "تاریخ تولد راننده",
|
||
name_en: "driverBirthday",
|
||
label_fa: "تاریخ تولد راننده",
|
||
label_en: "Driver Birthday",
|
||
placeholder_fa: "تاریخ تولد راننده را وارد کنید",
|
||
placeholder_en: "Enter driver birthday",
|
||
value: null,
|
||
dataType: "string",
|
||
required: true,
|
||
order: 10,
|
||
isActive: true,
|
||
validation: {
|
||
format: "date",
|
||
},
|
||
},
|
||
],
|
||
metadata: {
|
||
icon: "clipboard-list",
|
||
color: "#8B5CF6",
|
||
formType: "detailed",
|
||
hasComplexValidation: true,
|
||
},
|
||
},
|
||
{
|
||
stepKey: WorkflowStep.FIRST_LOCATION,
|
||
type: "THIRD_PARTY",
|
||
stepNumber: 5,
|
||
isActive: true,
|
||
stepName_fa: "موقعیت مکانی حادثه",
|
||
stepName_en: "Accident Location",
|
||
description_fa: "ثبت موقعیت جغرافیایی محل وقوع حادثه",
|
||
description_en: "Record geographical location of accident scene",
|
||
category: "FIRST_PARTY",
|
||
requiredPreviousSteps: [WorkflowStep.FIRST_INITIAL_FORM],
|
||
nextPossibleSteps: [WorkflowStep.FIRST_DESCRIPTION],
|
||
allowedRoles: ["user"],
|
||
estimatedDuration: 2,
|
||
fields: [
|
||
{
|
||
id: "507f1f77bcf86cd79943901f" as any,
|
||
name_fa: "عرض جغرافیایی",
|
||
name_en: "lat",
|
||
label_fa: "عرض جغرافیایی",
|
||
label_en: "Latitude",
|
||
placeholder_fa: "عرض جغرافیایی را وارد کنید",
|
||
placeholder_en: "Enter latitude",
|
||
value: null,
|
||
dataType: "number",
|
||
required: true,
|
||
order: 1,
|
||
isActive: true,
|
||
validation: {
|
||
type: "number",
|
||
min: -90,
|
||
max: 90,
|
||
},
|
||
},
|
||
{
|
||
id: "507f1f77bcf86cd799439020" as any,
|
||
name_fa: "طول جغرافیایی",
|
||
name_en: "lon",
|
||
label_fa: "طول جغرافیایی",
|
||
label_en: "Longitude",
|
||
placeholder_fa: "طول جغرافیایی را وارد کنید",
|
||
placeholder_en: "Enter longitude",
|
||
value: null,
|
||
dataType: "number",
|
||
required: true,
|
||
order: 2,
|
||
isActive: true,
|
||
validation: {
|
||
type: "number",
|
||
min: -180,
|
||
max: 180,
|
||
},
|
||
},
|
||
],
|
||
metadata: {
|
||
icon: "map-pin",
|
||
color: "#10B981",
|
||
requiresGPS: true,
|
||
autoDetectLocation: true,
|
||
},
|
||
},
|
||
{
|
||
stepKey: WorkflowStep.FIRST_VOICE,
|
||
type: "THIRD_PARTY",
|
||
stepNumber: 6,
|
||
isActive: true,
|
||
stepName_fa: "ضبط صدای طرف اول",
|
||
stepName_en: "First Party Voice Recording",
|
||
description_fa: "ضبط توضیحات صوتی توسط طرف اول",
|
||
description_en: "Voice recording of explanations by first party",
|
||
category: "FIRST_PARTY",
|
||
requiredPreviousSteps: [WorkflowStep.FIRST_LOCATION],
|
||
nextPossibleSteps: [WorkflowStep.FIRST_DESCRIPTION],
|
||
allowedRoles: ["user"],
|
||
estimatedDuration: 3,
|
||
fields: [
|
||
{
|
||
id: "507f1f77bcf86cd799439021" as any,
|
||
name_fa: "فایل صوتی",
|
||
name_en: "file",
|
||
label_fa: "ضبط صدا",
|
||
label_en: "Voice Recording",
|
||
placeholder_fa: "توضیحات صوتی خود را ضبط کنید",
|
||
placeholder_en: "Record your voice explanation",
|
||
value: null,
|
||
dataType: "audio-file",
|
||
required: true,
|
||
order: 1,
|
||
isActive: true,
|
||
validation: {
|
||
maxSize: 10485760,
|
||
maxDuration: 300,
|
||
allowedTypes: [
|
||
"audio/mp3",
|
||
"audio/mpeg",
|
||
"audio/wav",
|
||
"audio/m4a",
|
||
"audio/webm",
|
||
],
|
||
},
|
||
},
|
||
],
|
||
metadata: {
|
||
icon: "microphone",
|
||
color: "#F59E0B",
|
||
recordingType: "voice",
|
||
requiresMicrophone: true,
|
||
},
|
||
},
|
||
{
|
||
stepKey: WorkflowStep.FIRST_DESCRIPTION,
|
||
type: "THIRD_PARTY",
|
||
stepNumber: 7,
|
||
isActive: true,
|
||
stepName_fa: "توضیحات طرف اول",
|
||
stepName_en: "First Party Description",
|
||
description_fa: "ثبت توضیحات و جزئیات حادثه توسط طرف اول",
|
||
description_en:
|
||
"Record accident description and details by first party",
|
||
category: "FIRST_PARTY",
|
||
requiredPreviousSteps: [WorkflowStep.FIRST_VOICE],
|
||
nextPossibleSteps: [WorkflowStep.FIRST_INVITE_SECOND],
|
||
allowedRoles: ["user"],
|
||
estimatedDuration: 4,
|
||
fields: [
|
||
{
|
||
id: "507f1f77bcf86cd799439022" as any,
|
||
name_fa: "توضیحات",
|
||
name_en: "desc",
|
||
label_fa: "توضیحات حادثه",
|
||
label_en: "Accident Description",
|
||
placeholder_fa: "توضیحات کامل حادثه را وارد کنید",
|
||
placeholder_en: "Enter complete accident description",
|
||
value: null,
|
||
dataType: "textarea",
|
||
required: true,
|
||
order: 1,
|
||
isActive: true,
|
||
validation: {
|
||
minLength: 10,
|
||
maxLength: 2000,
|
||
},
|
||
},
|
||
{
|
||
id: "507f1f77bcf86cd799439023" as any,
|
||
name_fa: "تاریخ حادثه",
|
||
name_en: "accidentDate",
|
||
label_fa: "تاریخ حادثه",
|
||
label_en: "Accident Date",
|
||
placeholder_fa: "تاریخ وقوع حادثه را وارد کنید",
|
||
placeholder_en: "Enter accident date",
|
||
value: null,
|
||
dataType: "date",
|
||
required: true,
|
||
order: 2,
|
||
isActive: true,
|
||
validation: {
|
||
format: "YYYY-MM-DD",
|
||
},
|
||
},
|
||
{
|
||
id: "507f1f77bcf86cd799439024" as any,
|
||
name_fa: "زمان حادثه",
|
||
name_en: "accidentTime",
|
||
label_fa: "زمان حادثه",
|
||
label_en: "Accident Time",
|
||
placeholder_fa: "زمان وقوع حادثه را وارد کنید",
|
||
placeholder_en: "Enter accident time",
|
||
value: null,
|
||
dataType: "time",
|
||
required: true,
|
||
order: 3,
|
||
isActive: true,
|
||
validation: {
|
||
format: "HH:mm",
|
||
},
|
||
},
|
||
],
|
||
metadata: {
|
||
icon: "file-text",
|
||
color: "#6366F1",
|
||
},
|
||
},
|
||
{
|
||
stepKey: WorkflowStep.FIRST_INVITE_SECOND,
|
||
type: "THIRD_PARTY",
|
||
stepNumber: 8,
|
||
isActive: true,
|
||
stepName_fa: "دعوت طرف دوم",
|
||
stepName_en: "Invite Second Party",
|
||
description_fa: "ارسال دعوتنامه به طرف دوم برای ثبت اطلاعات",
|
||
description_en:
|
||
"Send invitation to second party to register information",
|
||
category: "FIRST_PARTY",
|
||
requiredPreviousSteps: [WorkflowStep.FIRST_DESCRIPTION],
|
||
nextPossibleSteps: [WorkflowStep.SECOND_INITIAL_FORM],
|
||
allowedRoles: ["user"],
|
||
estimatedDuration: 1,
|
||
fields: [],
|
||
metadata: {
|
||
icon: "user-plus",
|
||
color: "#8B5CF6",
|
||
requiresPhoneNumber: true,
|
||
actionType: "invitation",
|
||
},
|
||
},
|
||
{
|
||
stepKey: WorkflowStep.SECOND_INITIAL_FORM,
|
||
type: "THIRD_PARTY",
|
||
stepNumber: 9,
|
||
isActive: true,
|
||
stepName_fa: "فرم اصلی اطلاعات طرف دوم",
|
||
stepName_en: "Second Party Main Form",
|
||
description_fa:
|
||
"وارد کردن اطلاعات کامل راننده، بیمهگذار و خودرو طرف دوم",
|
||
description_en:
|
||
"Enter complete driver, insurer and vehicle information for second party",
|
||
category: "SECOND_PARTY",
|
||
requiredPreviousSteps: [WorkflowStep.FIRST_INVITE_SECOND],
|
||
nextPossibleSteps: [WorkflowStep.SECOND_LOCATION],
|
||
allowedRoles: ["user"],
|
||
estimatedDuration: 5,
|
||
fields: [
|
||
{
|
||
id: "507f1f77bcf86cd799439025" as any,
|
||
name_fa: "کد ملی مالک",
|
||
name_en: "nationalCodeOfOwner",
|
||
label_fa: "کد ملی مالک",
|
||
label_en: "National Code of Owner",
|
||
placeholder_fa: "کد ملی ۱۰ رقمی مالک را وارد کنید",
|
||
placeholder_en: "Enter 10-digit national code of insurer",
|
||
value: null,
|
||
dataType: "string",
|
||
required: true,
|
||
order: 1,
|
||
isActive: true,
|
||
validation: {
|
||
pattern: "^[0-9]{10}$",
|
||
minLength: 10,
|
||
maxLength: 10,
|
||
},
|
||
},
|
||
{
|
||
id: "507f1f77bcf86cd799439026" as any,
|
||
name_fa: "کد ملی راننده",
|
||
name_en: "nationalCodeOfDriver",
|
||
label_fa: "کد ملی راننده",
|
||
label_en: "National Code of Driver",
|
||
placeholder_fa: "کد ملی ۱۰ رقمی راننده را وارد کنید",
|
||
placeholder_en: "Enter 10-digit national code of driver",
|
||
value: null,
|
||
dataType: "string",
|
||
required: true,
|
||
order: 2,
|
||
isActive: true,
|
||
validation: {
|
||
pattern: "^[0-9]{10}$",
|
||
minLength: 10,
|
||
maxLength: 10,
|
||
},
|
||
},
|
||
{
|
||
id: "507f1f77bcf86cd799439027" as any,
|
||
name_fa: "شماره گواهینامه بیمهگذار",
|
||
name_en: "insurerLicense",
|
||
label_fa: "شماره گواهینامه بیمهگذار",
|
||
label_en: "Insurer License Number",
|
||
placeholder_fa: "شماره گواهینامه بیمهگذار را وارد کنید",
|
||
placeholder_en: "Enter insurer license number",
|
||
value: null,
|
||
dataType: "string",
|
||
required: true,
|
||
order: 3,
|
||
isActive: true,
|
||
validation: {
|
||
minLength: 5,
|
||
maxLength: 20,
|
||
},
|
||
},
|
||
{
|
||
id: "507f1f77bcf86cd799439028" as any,
|
||
name_fa: "شماره گواهینامه راننده",
|
||
name_en: "driverLicense",
|
||
label_fa: "شماره گواهینامه راننده",
|
||
label_en: "Driver License Number",
|
||
placeholder_fa: "شماره گواهینامه راننده را وارد کنید",
|
||
placeholder_en: "Enter driver license number",
|
||
value: null,
|
||
dataType: "string",
|
||
required: true,
|
||
order: 4,
|
||
isActive: true,
|
||
validation: {
|
||
minLength: 5,
|
||
maxLength: 20,
|
||
},
|
||
},
|
||
{
|
||
id: "507f1f77bcf86cd799439029" as any,
|
||
name_fa: "پلاک خودرو",
|
||
name_en: "plate",
|
||
label_fa: "پلاک خودرو",
|
||
label_en: "Vehicle Plate",
|
||
placeholder_fa: "اطلاعات پلاک خودرو را وارد کنید",
|
||
placeholder_en: "Enter vehicle plate information",
|
||
value: null,
|
||
dataType: "object",
|
||
required: true,
|
||
order: 5,
|
||
isActive: true,
|
||
validation: {
|
||
type: "object",
|
||
properties: {
|
||
leftDigits: { type: "number", min: 10, max: 99 },
|
||
centerAlphabet: { type: "string", pattern: "^[آ-ی]{1}$" },
|
||
centerDigits: { type: "number", min: 100, max: 999 },
|
||
ir: { type: "number", min: 10, max: 99 },
|
||
},
|
||
},
|
||
},
|
||
{
|
||
id: "507f1f77bcf86cd79943902a" as any,
|
||
name_fa: "راننده همان بیمهگذار است",
|
||
name_en: "driverIsInsurer",
|
||
label_fa: "راننده همان بیمهگذار است",
|
||
label_en: "Driver is Insurer",
|
||
value: null,
|
||
dataType: "boolean",
|
||
required: true,
|
||
order: 6,
|
||
isActive: true,
|
||
},
|
||
{
|
||
id: "507f1f77bcf86cd79943902b" as any,
|
||
name_fa: "خودرو صفر است",
|
||
name_en: "isNewCar",
|
||
label_fa: "خودرو صفر است",
|
||
label_en: "Is New Car",
|
||
value: null,
|
||
dataType: "boolean",
|
||
required: true,
|
||
order: 7,
|
||
isActive: true,
|
||
},
|
||
{
|
||
id: "507f1f77bcf86cd79943902c" as any,
|
||
name_fa: "بدون گواهی",
|
||
name_en: "userNoCertificate",
|
||
label_fa: "کاربر گواهی ندارد",
|
||
label_en: "User Has No Certificate",
|
||
value: null,
|
||
dataType: "boolean",
|
||
required: true,
|
||
order: 8,
|
||
isActive: true,
|
||
},
|
||
{
|
||
id: "507f1f77bcf86cd79943902d" as any,
|
||
name_fa: "تاریخ تولد بیمهگذار",
|
||
name_en: "insurerBirthday",
|
||
label_fa: "تاریخ تولد بیمهگذار",
|
||
label_en: "Insurer Birthday",
|
||
placeholder_fa: "تاریخ تولد بیمهگذار (timestamp)",
|
||
placeholder_en: "Insurer birthday (timestamp)",
|
||
value: null,
|
||
dataType: "number",
|
||
required: true,
|
||
order: 9,
|
||
isActive: true,
|
||
validation: {
|
||
type: "number",
|
||
min: 0,
|
||
},
|
||
},
|
||
{
|
||
id: "507f1f77bcf86cd79943902e" as any,
|
||
name_fa: "تاریخ تولد راننده",
|
||
name_en: "driverBirthday",
|
||
label_fa: "تاریخ تولد راننده",
|
||
label_en: "Driver Birthday",
|
||
placeholder_fa: "تاریخ تولد راننده را وارد کنید",
|
||
placeholder_en: "Enter driver birthday",
|
||
value: null,
|
||
dataType: "string",
|
||
required: true,
|
||
order: 10,
|
||
isActive: true,
|
||
validation: {
|
||
format: "date",
|
||
},
|
||
},
|
||
],
|
||
metadata: {
|
||
icon: "clipboard-list",
|
||
color: "#8B5CF6",
|
||
formType: "detailed",
|
||
hasComplexValidation: true,
|
||
party: "second",
|
||
},
|
||
},
|
||
{
|
||
stepKey: WorkflowStep.SECOND_LOCATION,
|
||
type: "THIRD_PARTY",
|
||
stepNumber: 10,
|
||
isActive: true,
|
||
stepName_fa: "موقعیت مکانی طرف دوم",
|
||
stepName_en: "Second Party Location",
|
||
description_fa: "ثبت موقعیت جغرافیایی طرف دوم",
|
||
description_en: "Record second party geographical location",
|
||
category: "SECOND_PARTY",
|
||
requiredPreviousSteps: [WorkflowStep.SECOND_INITIAL_FORM],
|
||
nextPossibleSteps: [WorkflowStep.SECOND_VOICE],
|
||
allowedRoles: ["user"],
|
||
estimatedDuration: 2,
|
||
fields: [
|
||
{
|
||
id: "507f1f77bcf86cd79943902f" as any,
|
||
name_fa: "عرض جغرافیایی",
|
||
name_en: "lat",
|
||
label_fa: "عرض جغرافیایی",
|
||
label_en: "Latitude",
|
||
placeholder_fa: "عرض جغرافیایی را وارد کنید",
|
||
placeholder_en: "Enter latitude",
|
||
value: null,
|
||
dataType: "number",
|
||
required: true,
|
||
order: 1,
|
||
isActive: true,
|
||
validation: {
|
||
type: "number",
|
||
min: -90,
|
||
max: 90,
|
||
},
|
||
},
|
||
{
|
||
id: "507f1f77bcf86cd799439030" as any,
|
||
name_fa: "طول جغرافیایی",
|
||
name_en: "lon",
|
||
label_fa: "طول جغرافیایی",
|
||
label_en: "Longitude",
|
||
placeholder_fa: "طول جغرافیایی را وارد کنید",
|
||
placeholder_en: "Enter longitude",
|
||
value: null,
|
||
dataType: "number",
|
||
required: true,
|
||
order: 2,
|
||
isActive: true,
|
||
validation: {
|
||
type: "number",
|
||
min: -180,
|
||
max: 180,
|
||
},
|
||
},
|
||
],
|
||
metadata: {
|
||
icon: "map-pin",
|
||
color: "#10B981",
|
||
requiresGPS: true,
|
||
autoDetectLocation: true,
|
||
party: "second",
|
||
},
|
||
},
|
||
{
|
||
stepKey: WorkflowStep.SECOND_VOICE,
|
||
type: "THIRD_PARTY",
|
||
stepNumber: 11,
|
||
isActive: true,
|
||
stepName_fa: "ضبط صدای طرف دوم",
|
||
stepName_en: "Second Party Voice Recording",
|
||
description_fa: "ضبط توضیحات صوتی توسط طرف دوم",
|
||
description_en: "Voice recording of explanations by second party",
|
||
category: "SECOND_PARTY",
|
||
requiredPreviousSteps: [WorkflowStep.SECOND_LOCATION],
|
||
nextPossibleSteps: [WorkflowStep.SECOND_DESCRIPTION],
|
||
allowedRoles: ["user"],
|
||
estimatedDuration: 3,
|
||
fields: [
|
||
{
|
||
id: "507f1f77bcf86cd799439031" as any,
|
||
name_fa: "فایل صوتی",
|
||
name_en: "file",
|
||
label_fa: "ضبط صدا",
|
||
label_en: "Voice Recording",
|
||
placeholder_fa: "توضیحات صوتی خود را ضبط کنید",
|
||
placeholder_en: "Record your voice explanation",
|
||
value: null,
|
||
dataType: "audio-file",
|
||
required: true,
|
||
order: 1,
|
||
isActive: true,
|
||
validation: {
|
||
maxSize: 10485760,
|
||
maxDuration: 300,
|
||
allowedTypes: [
|
||
"audio/mp3",
|
||
"audio/mpeg",
|
||
"audio/wav",
|
||
"audio/m4a",
|
||
"audio/webm",
|
||
],
|
||
},
|
||
},
|
||
],
|
||
metadata: {
|
||
icon: "microphone",
|
||
color: "#F59E0B",
|
||
recordingType: "voice",
|
||
requiresMicrophone: true,
|
||
party: "second",
|
||
},
|
||
},
|
||
{
|
||
stepKey: WorkflowStep.SECOND_DESCRIPTION,
|
||
type: "THIRD_PARTY",
|
||
stepNumber: 12,
|
||
isActive: true,
|
||
stepName_fa: "توضیحات طرف دوم",
|
||
stepName_en: "Second Party Description",
|
||
description_fa: "ثبت توضیحات و جزئیات حادثه توسط طرف دوم",
|
||
description_en:
|
||
"Record accident description and details by second party",
|
||
category: "SECOND_PARTY",
|
||
requiredPreviousSteps: [WorkflowStep.SECOND_VOICE],
|
||
nextPossibleSteps: [WorkflowStep.WAITING_FOR_GUILT_DECISION],
|
||
allowedRoles: ["user"],
|
||
estimatedDuration: 4,
|
||
fields: [
|
||
{
|
||
id: "507f1f77bcf86cd799439032" as any,
|
||
name_fa: "توضیحات",
|
||
name_en: "desc",
|
||
label_fa: "توضیحات حادثه",
|
||
label_en: "Accident Description",
|
||
placeholder_fa: "توضیحات کامل حادثه را وارد کنید",
|
||
placeholder_en: "Enter complete accident description",
|
||
value: null,
|
||
dataType: "textarea",
|
||
required: true,
|
||
order: 1,
|
||
isActive: true,
|
||
validation: {
|
||
minLength: 10,
|
||
maxLength: 2000,
|
||
},
|
||
},
|
||
{
|
||
id: "507f1f77bcf86cd799439033" as any,
|
||
name_fa: "تاریخ حادثه",
|
||
name_en: "accidentDate",
|
||
label_fa: "تاریخ حادثه",
|
||
label_en: "Accident Date",
|
||
placeholder_fa: "تاریخ وقوع حادثه را وارد کنید",
|
||
placeholder_en: "Enter accident date",
|
||
value: null,
|
||
dataType: "date",
|
||
required: true,
|
||
order: 2,
|
||
isActive: true,
|
||
validation: {
|
||
format: "YYYY-MM-DD",
|
||
},
|
||
},
|
||
{
|
||
id: "507f1f77bcf86cd799439034" as any,
|
||
name_fa: "زمان حادثه",
|
||
name_en: "accidentTime",
|
||
label_fa: "زمان حادثه",
|
||
label_en: "Accident Time",
|
||
placeholder_fa: "زمان وقوع حادثه را وارد کنید",
|
||
placeholder_en: "Enter accident time",
|
||
value: null,
|
||
dataType: "time",
|
||
required: true,
|
||
order: 3,
|
||
isActive: true,
|
||
validation: {
|
||
format: "HH:mm",
|
||
},
|
||
},
|
||
],
|
||
metadata: {
|
||
icon: "file-text",
|
||
color: "#6366F1",
|
||
party: "second",
|
||
},
|
||
},
|
||
// CAR_BODY workflow: accident type form (replaces confession for CAR_BODY)
|
||
{
|
||
stepKey: WorkflowStep.CAR_BODY_ACCIDENT_TYPE,
|
||
type: "CAR_BODY",
|
||
stepNumber: 2,
|
||
isActive: true,
|
||
stepName_fa: "نوع حادثه بدنه",
|
||
stepName_en: "Car Body Accident Type",
|
||
description_fa: "انتخاب اینکه حادثه با خودرو بوده یا با شیء",
|
||
description_en:
|
||
"Select whether accident was with another car or with an object",
|
||
category: "FIRST_PARTY",
|
||
requiredPreviousSteps: [WorkflowStep.CREATED],
|
||
nextPossibleSteps: [WorkflowStep.FIRST_VIDEO],
|
||
allowedRoles: ["user"],
|
||
estimatedDuration: 1,
|
||
fields: [
|
||
{
|
||
id: "507f1f77bcf86cd7994390cb" as any,
|
||
name_fa: "نوع حادثه",
|
||
name_en: "accidentType",
|
||
label_fa: "نوع حادثه",
|
||
label_en: "Accident Type",
|
||
placeholder_fa: "حادثه با خودرو یا شیء؟",
|
||
placeholder_en: "Accident with car or object?",
|
||
value: [
|
||
{ label_fa: "با خودرو", label_en: "With Car", value: "car" },
|
||
{ label_fa: "با شیء", label_en: "With Object", value: "object" },
|
||
],
|
||
dataType: "select",
|
||
required: true,
|
||
order: 1,
|
||
isActive: true,
|
||
validation: { enum: ["car", "object"] },
|
||
},
|
||
],
|
||
metadata: { icon: "car", color: "#10B981" },
|
||
},
|
||
// Add more default steps as needed...
|
||
];
|
||
|
||
return await this.bulkUpsert(defaultSteps);
|
||
}
|
||
}
|