forked from Yara724/api
1169 lines
40 KiB
TypeScript
1169 lines
40 KiB
TypeScript
import { Injectable, NotFoundException, BadRequestException, ConflictException } 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 {
|
||
constructor(
|
||
private readonly workflowStepDbService: WorkflowStepDbService,
|
||
) {}
|
||
|
||
/**
|
||
* 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);
|
||
}
|
||
}
|