feat: Runtime configuration and Docker deployment improvements

Frontend:
- Add runtime configuration service for deployment-time API URL injection
- Create docker-entrypoint.sh to generate config.json from environment variables
- Update ApiService, ApprovalService, and DocumentViewer to use RuntimeConfigService
- Add APP_INITIALIZER to load runtime config before app starts

Backend:
- Fix init-blockchain.js to properly quote mnemonic phrases in .env file
- Improve docker-entrypoint.sh with health checks and better error handling

Docker:
- Add API_BASE_URL environment variable to frontend container
- Update docker-compose.yml with clear documentation for remote deployment
- Reorganize .env.example with clear categories (REQUIRED FOR REMOTE, PRODUCTION, AUTO-GENERATED)

Workflow fixes:
- Fix DepartmentApproval interface to match backend schema
- Fix stage transformation for 0-indexed stageOrder
- Fix workflow list to show correct stage count from definition.stages

Cleanup:
- Move development artifacts to .trash directory
- Remove root-level package.json (was only for utility scripts)
- Add .trash/ to .gitignore
This commit is contained in:
Mahi
2026-02-08 18:44:05 -04:00
parent 2c10cd5662
commit d9de183e51
171 changed files with 10236 additions and 8386 deletions

View File

@@ -304,7 +304,7 @@ export class WorkflowFormComponent implements OnInit {
private loadDepartments(): void {
this.departmentService.getDepartments(1, 100).subscribe({
next: (response) => {
this.departments.set(response.data);
this.departments.set(response?.data ?? []);
},
});
}
@@ -322,14 +322,19 @@ export class WorkflowFormComponent implements OnInit {
});
this.stagesArray.clear();
workflow.stages.forEach((stage) => {
const stages = workflow.definition?.stages || [];
stages.forEach((stage) => {
// Find department by code to get departmentId
const dept = this.departments().find(d =>
d.code === stage.requiredApprovals?.[0]?.departmentCode
);
this.stagesArray.push(
this.fb.group({
id: [stage.id],
name: [stage.name, Validators.required],
departmentId: [stage.departmentId, Validators.required],
order: [stage.order],
isRequired: [stage.isRequired],
id: [stage.stageId],
name: [stage.stageName, Validators.required],
departmentId: [dept?.id || '', Validators.required],
order: [stage.stageOrder],
isRequired: [stage.metadata?.['isRequired'] ?? true],
})
);
});
@@ -414,18 +419,30 @@ export class WorkflowFormComponent implements OnInit {
const departmentId = currentUser?.departmentId ||
(values.stages[0]?.departmentId) || '';
const dto = {
// Transform to backend DTO format
const dto: any = {
name: normalizeWhitespace(values.name),
description: normalizeWhitespace(values.description) || undefined,
workflowType: values.workflowType!,
departmentId: departmentId,
stages: values.stages.map((s, i) => ({
id: s.id || `stage-${i + 1}`,
name: normalizeWhitespace(s.name) || `Stage ${i + 1}`,
departmentId: s.departmentId || '',
isRequired: s.isRequired ?? true,
order: i + 1,
})),
stages: values.stages.map((s, i) => {
const department = this.departments().find(d => d.id === s.departmentId);
return {
stageId: s.id || `stage-${i + 1}`,
stageName: normalizeWhitespace(s.name) || `Stage ${i + 1}`,
stageOrder: i, // Backend expects 0-indexed
executionType: 'SEQUENTIAL' as const,
requiredApprovals: [{
departmentCode: department?.code || '',
departmentName: department?.name || 'Unknown',
canDelegate: false,
}],
completionCriteria: 'ALL' as const,
rejectionHandling: 'FAIL_REQUEST' as const,
metadata: {
isRequired: s.isRequired ?? true,
},
};
}),
};
const action$ = this.isEditMode()