![]() Server : Apache/2 System : Linux server-15-235-50-60 5.15.0-164-generic #174-Ubuntu SMP Fri Nov 14 20:25:16 UTC 2025 x86_64 User : gositeme ( 1004) PHP Version : 8.2.29 Disable Function : exec,system,passthru,shell_exec,proc_close,proc_open,dl,popen,show_source,posix_kill,posix_mkfifo,posix_getpwuid,posix_setpgid,posix_setsid,posix_setuid,posix_setgid,posix_seteuid,posix_setegid,posix_uname Directory : /home/gositeme/domains/lavocat.ca/public_html/src/lib/ |
import { prisma } from './prisma';
import { AIAssignmentEngine } from './ai-assignment-engine';
import { logger } from './logger';
interface WorkflowRule {
id: string;
name: string;
trigger: 'case_created' | 'status_changed' | 'document_uploaded' | 'deadline_approaching';
conditions: WorkflowCondition[];
actions: WorkflowAction[];
isActive: boolean;
priority: number;
}
interface WorkflowCondition {
field: string;
operator: 'equals' | 'contains' | 'greater_than' | 'less_than' | 'in_list';
value: any;
}
interface WorkflowAction {
type: 'assign_case' | 'send_notification' | 'update_status' | 'create_task' | 'escalate';
parameters: any;
}
export class WorkflowAutomation {
private static workflows: WorkflowRule[] = [
{
id: 'urgent_case_auto_assign',
name: 'Auto-assign urgent cases',
trigger: 'case_created',
conditions: [
{ field: 'urgentNeeds', operator: 'contains', value: ['urgent', 'court date', 'hearing'] }
],
actions: [
{ type: 'assign_case', parameters: { priority: 'high', autoAssign: true } },
{ type: 'send_notification', parameters: { recipients: 'admins', template: 'urgent_case' } }
],
isActive: true,
priority: 1
},
{
id: 'complex_case_team_assignment',
name: 'Complex cases need full team',
trigger: 'case_created',
conditions: [
{ field: 'documentCount', operator: 'greater_than', value: 5 },
{ field: 'message', operator: 'contains', value: ['appeal', 'wrongful conviction', 'class action'] }
],
actions: [
{ type: 'assign_case', parameters: { teamSize: 'full', includeSecretary: true } },
{ type: 'create_task', parameters: { type: 'document_review', assignTo: 'secretary' } }
],
isActive: true,
priority: 2
},
{
id: 'deadline_reminder_system',
name: 'Deadline reminder automation',
trigger: 'deadline_approaching',
conditions: [
{ field: 'daysUntilDeadline', operator: 'less_than', value: 7 }
],
actions: [
{ type: 'send_notification', parameters: { recipients: 'assigned_team', template: 'deadline_reminder' } },
{ type: 'escalate', parameters: { to: 'primary_lawyer', urgency: 'high' } }
],
isActive: true,
priority: 3
},
{
id: 'status_update_notifications',
name: 'Status change notifications',
trigger: 'status_changed',
conditions: [
{ field: 'newStatus', operator: 'in_list', value: ['APPROVED', 'REJECTED', 'UNDER_REVIEW'] }
],
actions: [
{ type: 'send_notification', parameters: { recipients: 'client_family', template: 'status_update' } }
],
isActive: true,
priority: 4
},
{
id: 'workload_balancing',
name: 'Auto-balance team workload',
trigger: 'case_created',
conditions: [
{ field: 'assignedLawyerUtilization', operator: 'greater_than', value: 85 }
],
actions: [
{ type: 'assign_case', parameters: { strategy: 'load_balance', skipOverloaded: true } }
],
isActive: true,
priority: 5
}
];
/**
* Process workflow triggers for new cases
*/
static async processNewCase(registrationId: string): Promise<void> {
try {
const registration = await prisma.registration.findUnique({
where: { id: registrationId },
include: { documents: true, detaineeInfo: true }
});
if (!registration) return;
const triggerWorkflows = this.workflows.filter(
w => w.trigger === 'case_created' && w.isActive
).sort((a, b) => a.priority - b.priority);
for (const workflow of triggerWorkflows) {
if (await this.evaluateConditions(workflow.conditions, registration)) {
await this.executeActions(workflow.actions, registration);
logger.info('Workflow executed', {
workflowId: workflow.id,
registrationId,
trigger: 'case_created'
});
}
}
} catch (error) {
logger.error('Error processing workflow for new case', { error, registrationId });
}
}
/**
* Process status change workflows
*/
static async processStatusChange(
registrationId: string,
oldStatus: string,
newStatus: string
): Promise<void> {
try {
const registration = await prisma.registration.findUnique({
where: { id: registrationId },
include: { documents: true, detaineeInfo: true }
});
if (!registration) return;
const triggerWorkflows = this.workflows.filter(
w => w.trigger === 'status_changed' && w.isActive
).sort((a, b) => a.priority - b.priority);
for (const workflow of triggerWorkflows) {
const contextData = { ...registration, oldStatus, newStatus };
if (await this.evaluateConditions(workflow.conditions, contextData)) {
await this.executeActions(workflow.actions, contextData);
logger.info('Status change workflow executed', {
workflowId: workflow.id,
registrationId,
oldStatus,
newStatus
});
}
}
} catch (error) {
logger.error('Error processing status change workflow', { error, registrationId });
}
}
/**
* Evaluate workflow conditions
*/
private static async evaluateConditions(
conditions: WorkflowCondition[],
data: any
): Promise<boolean> {
for (const condition of conditions) {
const fieldValue = this.getFieldValue(data, condition.field);
if (!this.evaluateCondition(fieldValue, condition.operator, condition.value)) {
return false;
}
}
return true;
}
/**
* Get field value from data object
*/
private static getFieldValue(data: any, fieldPath: string): any {
const keys = fieldPath.split('.');
let value = data;
for (const key of keys) {
if (value && typeof value === 'object') {
value = value[key];
} else {
return undefined;
}
}
// Special computed fields
if (fieldPath === 'documentCount') {
return data.documents?.length || 0;
}
return value;
}
/**
* Evaluate single condition
*/
private static evaluateCondition(fieldValue: any, operator: string, expectedValue: any): boolean {
switch (operator) {
case 'equals':
return fieldValue === expectedValue;
case 'contains':
if (typeof fieldValue === 'string') {
if (Array.isArray(expectedValue)) {
return expectedValue.some(val => fieldValue.toLowerCase().includes(val.toLowerCase()));
}
return fieldValue.toLowerCase().includes(expectedValue.toLowerCase());
}
return false;
case 'greater_than':
return Number(fieldValue) > Number(expectedValue);
case 'less_than':
return Number(fieldValue) < Number(expectedValue);
case 'in_list':
return Array.isArray(expectedValue) && expectedValue.includes(fieldValue);
default:
return false;
}
}
/**
* Execute workflow actions
*/
private static async executeActions(actions: WorkflowAction[], data: any): Promise<void> {
for (const action of actions) {
try {
await this.executeAction(action, data);
} catch (error) {
logger.error('Error executing workflow action', { error, action, registrationId: data.id });
}
}
}
/**
* Execute single workflow action
*/
private static async executeAction(action: WorkflowAction, data: any): Promise<void> {
switch (action.type) {
case 'assign_case':
await this.handleCaseAssignment(action.parameters, data);
break;
case 'send_notification':
await this.handleNotification(action.parameters, data);
break;
case 'update_status':
await this.handleStatusUpdate(action.parameters, data);
break;
case 'create_task':
await this.handleTaskCreation(action.parameters, data);
break;
case 'escalate':
await this.handleEscalation(action.parameters, data);
break;
}
}
/**
* Handle automatic case assignment
*/
private static async handleCaseAssignment(parameters: any, data: any): Promise<void> {
if (parameters.autoAssign) {
// Use AI engine for optimal assignment
const adminUser = await prisma.user.findFirst({
where: { role: { in: ['SUPERADMIN', 'SUPERADMIN'] } }
});
if (adminUser) {
await AIAssignmentEngine.autoAssignOptimalTeam(data.id, adminUser.id);
}
} else if (parameters.strategy === 'load_balance') {
// Implement load balancing logic
const availableLawyers = await prisma.user.findMany({
where: {
role: { in: ['LAWYER', 'ADMIN'] },
},
include: {
caseAssignments: {
where: { isActive: true }
}
}
});
// Find lawyer with lowest caseload
const optimalLawyer = availableLawyers.reduce((prev, current) =>
(prev.caseAssignments.length < current.caseAssignments.length) ? prev : current
);
if (optimalLawyer) {
await prisma.caseAssignment.create({
data: {
registrationId: data.id,
userId: optimalLawyer.id,
role: 'primary_lawyer',
assignedBy: 'SYSTEM_AUTO'
}
});
}
}
}
/**
* Handle notification sending
*/
private static async handleNotification(parameters: any, data: any): Promise<void> {
// This would integrate with your notification system
logger.info('Sending automated notification', {
recipients: parameters.recipients,
template: parameters.template,
registrationId: data.id
});
// Example: Send to assigned team
if (parameters.recipients === 'assigned_team') {
const assignments = await prisma.caseAssignment.findMany({
where: { registrationId: data.id, isActive: true },
include: { user: true }
});
// Send notifications to each team member
for (const assignment of assignments) {
// Integrate with your notification service here
console.log(`Notification sent to ${assignment.user.email}: ${parameters.template}`);
}
}
}
/**
* Handle status updates
*/
private static async handleStatusUpdate(parameters: any, data: any): Promise<void> {
if (parameters.newStatus) {
await prisma.registration.update({
where: { id: data.id },
data: { status: parameters.newStatus }
});
}
}
/**
* Handle task creation
*/
private static async handleTaskCreation(parameters: any, data: any): Promise<void> {
// This would create tasks in your task management system
logger.info('Creating automated task', {
type: parameters.type,
assignTo: parameters.assignTo,
registrationId: data.id
});
}
/**
* Handle escalation
*/
private static async handleEscalation(parameters: any, data: any): Promise<void> {
// Find the person to escalate to
let escalationTarget;
if (parameters.to === 'primary_lawyer') {
const primaryAssignment = await prisma.caseAssignment.findFirst({
where: {
registrationId: data.id,
role: 'primary_lawyer',
isActive: true
},
include: { user: true }
});
escalationTarget = primaryAssignment?.user;
} else {
escalationTarget = await prisma.user.findFirst({
where: { role: { in: ['SUPERADMIN', 'SUPERADMIN'] } }
});
}
if (escalationTarget) {
logger.info('Case escalated', {
escalatedTo: escalationTarget.email,
urgency: parameters.urgency,
registrationId: data.id
});
}
}
/**
* Run daily automation checks
*/
static async runDailyAutomation(): Promise<void> {
try {
// Check for approaching deadlines
const approachingDeadlines = await prisma.registration.findMany({
where: {
status: { in: ['PENDING', 'DOCUMENTS_UNDER_REVIEW', 'UNDER_REVIEW'] },
// Add deadline field to your schema if needed
},
include: { documents: true }
});
for (const registration of approachingDeadlines) {
// Process deadline workflows
const triggerWorkflows = this.workflows.filter(
w => w.trigger === 'deadline_approaching' && w.isActive
);
for (const workflow of triggerWorkflows) {
if (await this.evaluateConditions(workflow.conditions, registration)) {
await this.executeActions(workflow.actions, registration);
}
}
}
// Check for overloaded team members
await this.checkWorkloadBalance();
logger.info('Daily automation completed successfully');
} catch (error) {
logger.error('Error in daily automation', { error });
}
}
/**
* Check and rebalance workloads
*/
private static async checkWorkloadBalance(): Promise<void> {
const teamMembers = await prisma.user.findMany({
where: { role: { in: ['LAWYER', 'ADMIN'] } },
include: {
caseAssignments: {
where: { isActive: true },
include: { registration: true }
}
}
});
for (const member of teamMembers) {
const utilization = (member.caseAssignments.length / 5) * 100; // Assuming 5 cases = 100%
if (utilization > 90) {
logger.warn('Team member overloaded', {
userId: member.id,
name: member.name,
utilization,
activeCases: member.caseAssignments.length
});
// Could trigger reassignment or hiring recommendations
}
}
}
/**
* Get workflow statistics
*/
static async getWorkflowStats(): Promise<any> {
return {
totalWorkflows: this.workflows.length,
activeWorkflows: this.workflows.filter(w => w.isActive).length,
executionsToday: 0, // Would track in database
averageExecutionTime: 0 // Would track performance
};
}
}