@acorex-platform/module-workplacesafety-management-client 0.0.38 → 0.0.46
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/fesm2022/{acorex-platform-module-workplacesafety-management-client-acorex-platform-module-workplacesafety-management-client-Ds0Fkspp.mjs → acorex-platform-module-workplacesafety-management-client-acorex-platform-module-workplacesafety-management-client-C3a08xjg.mjs} +79 -32
- package/fesm2022/acorex-platform-module-workplacesafety-management-client-acorex-platform-module-workplacesafety-management-client-C3a08xjg.mjs.map +1 -0
- package/fesm2022/acorex-platform-module-workplacesafety-management-client-contracts-entities-control-hierarchy-levels.mjs +19 -5
- package/fesm2022/acorex-platform-module-workplacesafety-management-client-contracts-entities-control-hierarchy-levels.mjs.map +1 -1
- package/fesm2022/acorex-platform-module-workplacesafety-management-client-contracts-entities-control-measure.mjs +19 -5
- package/fesm2022/acorex-platform-module-workplacesafety-management-client-contracts-entities-control-measure.mjs.map +1 -1
- package/fesm2022/acorex-platform-module-workplacesafety-management-client-contracts-entities-hazard-ppe-rule.mjs +19 -5
- package/fesm2022/acorex-platform-module-workplacesafety-management-client-contracts-entities-hazard-ppe-rule.mjs.map +1 -1
- package/fesm2022/acorex-platform-module-workplacesafety-management-client-contracts-entities-hazard-register.mjs +19 -5
- package/fesm2022/acorex-platform-module-workplacesafety-management-client-contracts-entities-hazard-register.mjs.map +1 -1
- package/fesm2022/acorex-platform-module-workplacesafety-management-client-contracts-entities-hazard-risk.mjs +22 -72
- package/fesm2022/acorex-platform-module-workplacesafety-management-client-contracts-entities-hazard-risk.mjs.map +1 -1
- package/fesm2022/acorex-platform-module-workplacesafety-management-client-contracts-entities-hazard.mjs +20 -6
- package/fesm2022/acorex-platform-module-workplacesafety-management-client-contracts-entities-hazard.mjs.map +1 -1
- package/fesm2022/acorex-platform-module-workplacesafety-management-client-contracts-entities-ppe-item.mjs +19 -5
- package/fesm2022/acorex-platform-module-workplacesafety-management-client-contracts-entities-ppe-item.mjs.map +1 -1
- package/fesm2022/acorex-platform-module-workplacesafety-management-client-contracts-entities-safety-plan-activity.mjs +1516 -0
- package/fesm2022/acorex-platform-module-workplacesafety-management-client-contracts-entities-safety-plan-activity.mjs.map +1 -0
- package/fesm2022/acorex-platform-module-workplacesafety-management-client-contracts-entities-safety-plan-section.mjs +82 -12
- package/fesm2022/acorex-platform-module-workplacesafety-management-client-contracts-entities-safety-plan-section.mjs.map +1 -1
- package/fesm2022/acorex-platform-module-workplacesafety-management-client-contracts-entities-safety-plan-type-section.mjs +82 -12
- package/fesm2022/acorex-platform-module-workplacesafety-management-client-contracts-entities-safety-plan-type-section.mjs.map +1 -1
- package/fesm2022/acorex-platform-module-workplacesafety-management-client-contracts-entities-safety-plan-type.mjs +19 -5
- package/fesm2022/acorex-platform-module-workplacesafety-management-client-contracts-entities-safety-plan-type.mjs.map +1 -1
- package/fesm2022/acorex-platform-module-workplacesafety-management-client-contracts-entities-safety-plan.mjs +39 -6
- package/fesm2022/acorex-platform-module-workplacesafety-management-client-contracts-entities-safety-plan.mjs.map +1 -1
- package/fesm2022/acorex-platform-module-workplacesafety-management-client-contracts-entities-safety-template.mjs +20 -5
- package/fesm2022/acorex-platform-module-workplacesafety-management-client-contracts-entities-safety-template.mjs.map +1 -1
- package/fesm2022/acorex-platform-module-workplacesafety-management-client-contracts-entities-section-type.mjs +19 -5
- package/fesm2022/acorex-platform-module-workplacesafety-management-client-contracts-entities-section-type.mjs.map +1 -1
- package/fesm2022/acorex-platform-module-workplacesafety-management-client-contracts-entities-work-step-type.mjs +82 -12
- package/fesm2022/acorex-platform-module-workplacesafety-management-client-contracts-entities-work-step-type.mjs.map +1 -1
- package/fesm2022/{acorex-platform-module-workplacesafety-management-client-contracts-entity-catalog-acorex-platform-module-workplacesafety-management-client-contracts-entity-catalog-BI4TLvSE.mjs → acorex-platform-module-workplacesafety-management-client-contracts-entity-catalog-acorex-platform-module-workplacesafety-management-client-contracts-entity-catalog-C46YEBVd.mjs} +21 -21
- package/fesm2022/acorex-platform-module-workplacesafety-management-client-contracts-entity-catalog-acorex-platform-module-workplacesafety-management-client-contracts-entity-catalog-C46YEBVd.mjs.map +1 -0
- package/fesm2022/{acorex-platform-module-workplacesafety-management-client-contracts-entity-catalog-control-hierarchy-levels.entity-COlCNnyK.mjs → acorex-platform-module-workplacesafety-management-client-contracts-entity-catalog-control-hierarchy-levels.entity-oylKTsgJ.mjs} +16 -2
- package/fesm2022/acorex-platform-module-workplacesafety-management-client-contracts-entity-catalog-control-hierarchy-levels.entity-oylKTsgJ.mjs.map +1 -0
- package/fesm2022/{acorex-platform-module-workplacesafety-management-client-contracts-entity-catalog-control-measure.entity-BrXGzqZU.mjs → acorex-platform-module-workplacesafety-management-client-contracts-entity-catalog-control-measure.entity-DHm1nRwW.mjs} +16 -2
- package/fesm2022/acorex-platform-module-workplacesafety-management-client-contracts-entity-catalog-control-measure.entity-DHm1nRwW.mjs.map +1 -0
- package/fesm2022/{acorex-platform-module-workplacesafety-management-client-contracts-entity-catalog-hazard-ppe-rule.entity-DHJus7OP.mjs → acorex-platform-module-workplacesafety-management-client-contracts-entity-catalog-hazard-ppe-rule.entity-DH-hPuAD.mjs} +16 -2
- package/fesm2022/acorex-platform-module-workplacesafety-management-client-contracts-entity-catalog-hazard-ppe-rule.entity-DH-hPuAD.mjs.map +1 -0
- package/fesm2022/{acorex-platform-module-workplacesafety-management-client-contracts-entity-catalog-hazard-register.entity-CogjoDox.mjs → acorex-platform-module-workplacesafety-management-client-contracts-entity-catalog-hazard-register.entity-g4AT_IiT.mjs} +16 -2
- package/fesm2022/acorex-platform-module-workplacesafety-management-client-contracts-entity-catalog-hazard-register.entity-g4AT_IiT.mjs.map +1 -0
- package/fesm2022/{acorex-platform-module-workplacesafety-management-client-contracts-entity-catalog-hazard-risk.entity-9UWHmIXV.mjs → acorex-platform-module-workplacesafety-management-client-contracts-entity-catalog-hazard-risk.entity-BwNlWbnC.mjs} +18 -72
- package/fesm2022/acorex-platform-module-workplacesafety-management-client-contracts-entity-catalog-hazard-risk.entity-BwNlWbnC.mjs.map +1 -0
- package/fesm2022/{acorex-platform-module-workplacesafety-management-client-contracts-entity-catalog-hazard.entity-CNPbxULL.mjs → acorex-platform-module-workplacesafety-management-client-contracts-entity-catalog-hazard.entity-Cogwbd1P.mjs} +17 -3
- package/fesm2022/acorex-platform-module-workplacesafety-management-client-contracts-entity-catalog-hazard.entity-Cogwbd1P.mjs.map +1 -0
- package/fesm2022/{acorex-platform-module-workplacesafety-management-client-contracts-entity-catalog-ppe-item.entity-D5iOBl3P.mjs → acorex-platform-module-workplacesafety-management-client-contracts-entity-catalog-ppe-item.entity-0xqGaPnk.mjs} +16 -2
- package/fesm2022/acorex-platform-module-workplacesafety-management-client-contracts-entity-catalog-ppe-item.entity-0xqGaPnk.mjs.map +1 -0
- package/fesm2022/acorex-platform-module-workplacesafety-management-client-contracts-entity-catalog-risk-management.integration-CUZIl8zt.mjs +7 -0
- package/fesm2022/acorex-platform-module-workplacesafety-management-client-contracts-entity-catalog-risk-management.integration-CUZIl8zt.mjs.map +1 -0
- package/fesm2022/acorex-platform-module-workplacesafety-management-client-contracts-entity-catalog-safety-plan-activity.entity-CZCYupQT.mjs +324 -0
- package/fesm2022/acorex-platform-module-workplacesafety-management-client-contracts-entity-catalog-safety-plan-activity.entity-CZCYupQT.mjs.map +1 -0
- package/fesm2022/{acorex-platform-module-workplacesafety-management-client-contracts-entity-catalog-safety-plan-section-dependency-TdIMIkM7.mjs → acorex-platform-module-workplacesafety-management-client-contracts-entity-catalog-safety-plan-section-dependency-D0fzHV_L.mjs} +62 -10
- package/fesm2022/acorex-platform-module-workplacesafety-management-client-contracts-entity-catalog-safety-plan-section-dependency-D0fzHV_L.mjs.map +1 -0
- package/fesm2022/{acorex-platform-module-workplacesafety-management-client-contracts-entity-catalog-safety-plan-section.entity-DJAI-7lB.mjs → acorex-platform-module-workplacesafety-management-client-contracts-entity-catalog-safety-plan-section.entity-BpziBgGf.mjs} +17 -3
- package/fesm2022/{acorex-platform-module-workplacesafety-management-client-contracts-entity-catalog-safety-plan-section.entity-DJAI-7lB.mjs.map → acorex-platform-module-workplacesafety-management-client-contracts-entity-catalog-safety-plan-section.entity-BpziBgGf.mjs.map} +1 -1
- package/fesm2022/{acorex-platform-module-workplacesafety-management-client-contracts-entity-catalog-safety-plan-type-section.entity-BGZ1VAW3.mjs → acorex-platform-module-workplacesafety-management-client-contracts-entity-catalog-safety-plan-type-section.entity-D8wL0ned.mjs} +17 -3
- package/fesm2022/acorex-platform-module-workplacesafety-management-client-contracts-entity-catalog-safety-plan-type-section.entity-D8wL0ned.mjs.map +1 -0
- package/fesm2022/{acorex-platform-module-workplacesafety-management-client-contracts-entity-catalog-safety-plan-type.entity-B6NYL4xi.mjs → acorex-platform-module-workplacesafety-management-client-contracts-entity-catalog-safety-plan-type.entity-DrqQDYn9.mjs} +16 -2
- package/fesm2022/acorex-platform-module-workplacesafety-management-client-contracts-entity-catalog-safety-plan-type.entity-DrqQDYn9.mjs.map +1 -0
- package/fesm2022/{acorex-platform-module-workplacesafety-management-client-contracts-entity-catalog-safety-plan.entity-BSUR9jqs.mjs → acorex-platform-module-workplacesafety-management-client-contracts-entity-catalog-safety-plan.entity-DaRnWCin.mjs} +36 -3
- package/fesm2022/acorex-platform-module-workplacesafety-management-client-contracts-entity-catalog-safety-plan.entity-DaRnWCin.mjs.map +1 -0
- package/fesm2022/{acorex-platform-module-workplacesafety-management-client-contracts-entity-catalog-safety-template.entity-CIHy_hLh.mjs → acorex-platform-module-workplacesafety-management-client-contracts-entity-catalog-safety-template.entity-Coj5Rzry.mjs} +17 -2
- package/fesm2022/acorex-platform-module-workplacesafety-management-client-contracts-entity-catalog-safety-template.entity-Coj5Rzry.mjs.map +1 -0
- package/fesm2022/{acorex-platform-module-workplacesafety-management-client-contracts-entity-catalog-section-type.entity-Cs4nIHou.mjs → acorex-platform-module-workplacesafety-management-client-contracts-entity-catalog-section-type.entity-BU5roQ5-.mjs} +16 -2
- package/fesm2022/acorex-platform-module-workplacesafety-management-client-contracts-entity-catalog-section-type.entity-BU5roQ5-.mjs.map +1 -0
- package/fesm2022/{acorex-platform-module-workplacesafety-management-client-contracts-entity-catalog-work-step-type.entity-CJFWDOoh.mjs → acorex-platform-module-workplacesafety-management-client-contracts-entity-catalog-work-step-type.entity-DijGV30H.mjs} +17 -3
- package/fesm2022/acorex-platform-module-workplacesafety-management-client-contracts-entity-catalog-work-step-type.entity-DijGV30H.mjs.map +1 -0
- package/fesm2022/acorex-platform-module-workplacesafety-management-client-contracts-entity-catalog.mjs +1 -1
- package/fesm2022/acorex-platform-module-workplacesafety-management-client-contracts.mjs +144 -168
- package/fesm2022/acorex-platform-module-workplacesafety-management-client-contracts.mjs.map +1 -1
- package/fesm2022/acorex-platform-module-workplacesafety-management-client-fill-safety-plan.command-lJl0qpvT.mjs +165 -0
- package/fesm2022/acorex-platform-module-workplacesafety-management-client-fill-safety-plan.command-lJl0qpvT.mjs.map +1 -0
- package/fesm2022/acorex-platform-module-workplacesafety-management-client-hydrate-safety-plan-activity-sections-TrVl3-T6.mjs +240 -0
- package/fesm2022/acorex-platform-module-workplacesafety-management-client-hydrate-safety-plan-activity-sections-TrVl3-T6.mjs.map +1 -0
- package/fesm2022/{acorex-platform-module-workplacesafety-management-client-index-gbDgxqrV.mjs → acorex-platform-module-workplacesafety-management-client-index-CInaiEjw.mjs} +130 -398
- package/fesm2022/acorex-platform-module-workplacesafety-management-client-index-CInaiEjw.mjs.map +1 -0
- package/fesm2022/acorex-platform-module-workplacesafety-management-client-load-safety-plan-preview-structure-Ce67NSIZ.mjs +136 -0
- package/fesm2022/acorex-platform-module-workplacesafety-management-client-load-safety-plan-preview-structure-Ce67NSIZ.mjs.map +1 -0
- package/fesm2022/acorex-platform-module-workplacesafety-management-client-local-entities.mjs +1 -1
- package/fesm2022/acorex-platform-module-workplacesafety-management-client-local-entities.mjs.map +1 -1
- package/fesm2022/acorex-platform-module-workplacesafety-management-client-preview-safety-plan.command-DJqqiYlD.mjs +102 -0
- package/fesm2022/acorex-platform-module-workplacesafety-management-client-preview-safety-plan.command-DJqqiYlD.mjs.map +1 -0
- package/fesm2022/acorex-platform-module-workplacesafety-management-client-safety-plan-fill-values.util-RLOdySf7.mjs +33 -0
- package/fesm2022/acorex-platform-module-workplacesafety-management-client-safety-plan-fill-values.util-RLOdySf7.mjs.map +1 -0
- package/fesm2022/{acorex-platform-module-workplacesafety-management-client-safety-plan-preview-modal.component-CQ2xB7Va.mjs → acorex-platform-module-workplacesafety-management-client-safety-plan-preview-modal.component-B9lElhxc.mjs} +252 -72
- package/fesm2022/acorex-platform-module-workplacesafety-management-client-safety-plan-preview-modal.component-B9lElhxc.mjs.map +1 -0
- package/fesm2022/acorex-platform-module-workplacesafety-management-client-safety-plan-section-builder.types-C0SEJsVM.mjs +185 -0
- package/fesm2022/acorex-platform-module-workplacesafety-management-client-safety-plan-section-builder.types-C0SEJsVM.mjs.map +1 -0
- package/fesm2022/{acorex-platform-module-workplacesafety-management-client-save-safety-plan-sections.command-CLj3y_JD.mjs → acorex-platform-module-workplacesafety-management-client-save-safety-plan-sections.command-D0aRQHWk.mjs} +30 -94
- package/fesm2022/acorex-platform-module-workplacesafety-management-client-save-safety-plan-sections.command-D0aRQHWk.mjs.map +1 -0
- package/fesm2022/acorex-platform-module-workplacesafety-management-client.mjs +1 -1
- package/package.json +6 -6
- package/types/acorex-platform-module-workplacesafety-management-client-contracts-entities-safety-plan-activity.d.ts +5 -0
- package/types/acorex-platform-module-workplacesafety-management-client-contracts.d.ts +133 -157
- package/fesm2022/acorex-platform-module-workplacesafety-management-client-acorex-platform-module-workplacesafety-management-client-Ds0Fkspp.mjs.map +0 -1
- package/fesm2022/acorex-platform-module-workplacesafety-management-client-contracts-entities-safety-plan-work-step.mjs +0 -542
- package/fesm2022/acorex-platform-module-workplacesafety-management-client-contracts-entities-safety-plan-work-step.mjs.map +0 -1
- package/fesm2022/acorex-platform-module-workplacesafety-management-client-contracts-entity-catalog-acorex-platform-module-workplacesafety-management-client-contracts-entity-catalog-BI4TLvSE.mjs.map +0 -1
- package/fesm2022/acorex-platform-module-workplacesafety-management-client-contracts-entity-catalog-control-hierarchy-levels.entity-COlCNnyK.mjs.map +0 -1
- package/fesm2022/acorex-platform-module-workplacesafety-management-client-contracts-entity-catalog-control-measure.entity-BrXGzqZU.mjs.map +0 -1
- package/fesm2022/acorex-platform-module-workplacesafety-management-client-contracts-entity-catalog-hazard-ppe-rule.entity-DHJus7OP.mjs.map +0 -1
- package/fesm2022/acorex-platform-module-workplacesafety-management-client-contracts-entity-catalog-hazard-register.entity-CogjoDox.mjs.map +0 -1
- package/fesm2022/acorex-platform-module-workplacesafety-management-client-contracts-entity-catalog-hazard-risk.entity-9UWHmIXV.mjs.map +0 -1
- package/fesm2022/acorex-platform-module-workplacesafety-management-client-contracts-entity-catalog-hazard.entity-CNPbxULL.mjs.map +0 -1
- package/fesm2022/acorex-platform-module-workplacesafety-management-client-contracts-entity-catalog-ppe-item.entity-D5iOBl3P.mjs.map +0 -1
- package/fesm2022/acorex-platform-module-workplacesafety-management-client-contracts-entity-catalog-safety-plan-section-dependency-TdIMIkM7.mjs.map +0 -1
- package/fesm2022/acorex-platform-module-workplacesafety-management-client-contracts-entity-catalog-safety-plan-type-section.entity-BGZ1VAW3.mjs.map +0 -1
- package/fesm2022/acorex-platform-module-workplacesafety-management-client-contracts-entity-catalog-safety-plan-type.entity-B6NYL4xi.mjs.map +0 -1
- package/fesm2022/acorex-platform-module-workplacesafety-management-client-contracts-entity-catalog-safety-plan-work-step.entity-BlCbf5qP.mjs +0 -405
- package/fesm2022/acorex-platform-module-workplacesafety-management-client-contracts-entity-catalog-safety-plan-work-step.entity-BlCbf5qP.mjs.map +0 -1
- package/fesm2022/acorex-platform-module-workplacesafety-management-client-contracts-entity-catalog-safety-plan.entity-BSUR9jqs.mjs.map +0 -1
- package/fesm2022/acorex-platform-module-workplacesafety-management-client-contracts-entity-catalog-safety-template.entity-CIHy_hLh.mjs.map +0 -1
- package/fesm2022/acorex-platform-module-workplacesafety-management-client-contracts-entity-catalog-section-type.entity-Cs4nIHou.mjs.map +0 -1
- package/fesm2022/acorex-platform-module-workplacesafety-management-client-contracts-entity-catalog-work-step-type.entity-CJFWDOoh.mjs.map +0 -1
- package/fesm2022/acorex-platform-module-workplacesafety-management-client-index-gbDgxqrV.mjs.map +0 -1
- package/fesm2022/acorex-platform-module-workplacesafety-management-client-preview-safety-plan.command-BU95c4xb.mjs +0 -254
- package/fesm2022/acorex-platform-module-workplacesafety-management-client-preview-safety-plan.command-BU95c4xb.mjs.map +0 -1
- package/fesm2022/acorex-platform-module-workplacesafety-management-client-safety-plan-preview-modal.component-CQ2xB7Va.mjs.map +0 -1
- package/fesm2022/acorex-platform-module-workplacesafety-management-client-safety-plan-preview.service-DJP41MuL.mjs +0 -36
- package/fesm2022/acorex-platform-module-workplacesafety-management-client-safety-plan-preview.service-DJP41MuL.mjs.map +0 -1
- package/fesm2022/acorex-platform-module-workplacesafety-management-client-safety-plan-section-builder.types-LaN1NIgc.mjs +0 -204
- package/fesm2022/acorex-platform-module-workplacesafety-management-client-safety-plan-section-builder.types-LaN1NIgc.mjs.map +0 -1
- package/fesm2022/acorex-platform-module-workplacesafety-management-client-save-safety-plan-sections.command-CLj3y_JD.mjs.map +0 -1
- package/types/acorex-platform-module-workplacesafety-management-client-contracts-entities-safety-plan-work-step.d.ts +0 -5
|
@@ -0,0 +1,1516 @@
|
|
|
1
|
+
import { createRandomUuid, AXPWidgetsList } from '@acorex-platform/framework-shared/core';
|
|
2
|
+
import { createAllQueryView, entityMasterCrudActions, entityMasterRecordActions } from '@acorex-platform/framework-shared/entity';
|
|
3
|
+
|
|
4
|
+
const config = {
|
|
5
|
+
i18n: 'workplacesafety-management',
|
|
6
|
+
module: 'WorkplaceSafetyManagement',
|
|
7
|
+
};
|
|
8
|
+
const RootConfig = {
|
|
9
|
+
config,
|
|
10
|
+
module: {
|
|
11
|
+
name: config.module,
|
|
12
|
+
title: `@${config.i18n}:module.title`,
|
|
13
|
+
icon: 'fa-light fa-building-shield',
|
|
14
|
+
},
|
|
15
|
+
entities: {
|
|
16
|
+
hazard: {
|
|
17
|
+
name: 'Hazard',
|
|
18
|
+
source: `${config.module}.Hazard`,
|
|
19
|
+
title: `@${config.i18n}:hazards.entities.hazard.title`,
|
|
20
|
+
titlePlural: `@${config.i18n}:hazards.entities.hazard.plural`,
|
|
21
|
+
icon: 'fa-light fa-triangle-exclamation',
|
|
22
|
+
},
|
|
23
|
+
hazardRegister: {
|
|
24
|
+
name: 'HazardRegister',
|
|
25
|
+
source: `${config.module}.HazardRegister`,
|
|
26
|
+
title: `@${config.i18n}:hazard-registers.entities.hazard-register.title`,
|
|
27
|
+
titlePlural: `@${config.i18n}:hazard-registers.entities.hazard-register.plural`,
|
|
28
|
+
icon: 'fa-light fa-clipboard-list',
|
|
29
|
+
},
|
|
30
|
+
ppeItem: {
|
|
31
|
+
name: 'PpeItem',
|
|
32
|
+
source: `${config.module}.PpeItem`,
|
|
33
|
+
title: `@${config.i18n}:ppe-items.entities.ppe-item.title`,
|
|
34
|
+
titlePlural: `@${config.i18n}:ppe-items.entities.ppe-item.plural`,
|
|
35
|
+
icon: 'fa-light fa-helmet-safety',
|
|
36
|
+
},
|
|
37
|
+
hazardPpeRule: {
|
|
38
|
+
name: 'HazardPpeRule',
|
|
39
|
+
source: `${config.module}.HazardPpeRule`,
|
|
40
|
+
title: `@${config.i18n}:hazard-ppe-rules.entities.hazard-ppe-rule.title`,
|
|
41
|
+
titlePlural: `@${config.i18n}:hazard-ppe-rules.entities.hazard-ppe-rule.plural`,
|
|
42
|
+
icon: 'fa-light fa-user-helmet-safety',
|
|
43
|
+
},
|
|
44
|
+
hazardRisk: {
|
|
45
|
+
name: 'HazardRisk',
|
|
46
|
+
source: `${config.module}.HazardRisk`,
|
|
47
|
+
title: `@${config.i18n}:hazard-risks.entities.hazard-risk.title`,
|
|
48
|
+
titlePlural: `@${config.i18n}:hazard-risks.entities.hazard-risk.plural`,
|
|
49
|
+
icon: 'fa-light fa-shield-exclamation',
|
|
50
|
+
},
|
|
51
|
+
controlHierarchyLevels: {
|
|
52
|
+
name: 'ControlHierarchyLevels',
|
|
53
|
+
source: `${config.module}.ControlHierarchyLevels`,
|
|
54
|
+
title: `@${config.i18n}:control-measure-types.entities.control-measure-type.title`,
|
|
55
|
+
titlePlural: `@${config.i18n}:control-measure-types.entities.control-measure-type.plural`,
|
|
56
|
+
icon: 'fa-light fa-layer-group',
|
|
57
|
+
},
|
|
58
|
+
controlMeasure: {
|
|
59
|
+
name: 'ControlMeasure',
|
|
60
|
+
source: `${config.module}.ControlMeasure`,
|
|
61
|
+
title: `@${config.i18n}:control-measures.entities.control-measure.title`,
|
|
62
|
+
titlePlural: `@${config.i18n}:control-measures.entities.control-measure.plural`,
|
|
63
|
+
icon: 'fa-light fa-shield-check',
|
|
64
|
+
},
|
|
65
|
+
safetyPlan: {
|
|
66
|
+
name: 'SafetyPlan',
|
|
67
|
+
source: `${config.module}.SafetyPlan`,
|
|
68
|
+
title: `@${config.i18n}:safety-plans.entities.safety-plan.title`,
|
|
69
|
+
titlePlural: `@${config.i18n}:safety-plans.entities.safety-plan.plural`,
|
|
70
|
+
icon: 'fa-light fa-file-shield',
|
|
71
|
+
},
|
|
72
|
+
safetyPlanType: {
|
|
73
|
+
name: 'SafetyPlanType',
|
|
74
|
+
source: `${config.module}.SafetyPlanType`,
|
|
75
|
+
title: `@${config.i18n}:safety-plan-types.entities.safety-plan-type.title`,
|
|
76
|
+
titlePlural: `@${config.i18n}:safety-plan-types.entities.safety-plan-type.plural`,
|
|
77
|
+
icon: 'fa-light fa-file-lines',
|
|
78
|
+
},
|
|
79
|
+
safetyPlanTypeSection: {
|
|
80
|
+
name: 'SafetyPlanTypeSection',
|
|
81
|
+
source: `${config.module}.SafetyPlanTypeSection`,
|
|
82
|
+
title: `@${config.i18n}:safety-plan-type-sections.entities.safety-plan-type-section.title`,
|
|
83
|
+
titlePlural: `@${config.i18n}:safety-plan-type-sections.entities.safety-plan-type-section.plural`,
|
|
84
|
+
icon: 'fa-light fa-list-ol',
|
|
85
|
+
},
|
|
86
|
+
sectionType: {
|
|
87
|
+
name: 'SectionType',
|
|
88
|
+
source: `${config.module}.SectionType`,
|
|
89
|
+
title: `@${config.i18n}:section-types.entities.section-type.title`,
|
|
90
|
+
titlePlural: `@${config.i18n}:section-types.entities.section-type.plural`,
|
|
91
|
+
icon: 'fa-light fa-list-tree',
|
|
92
|
+
},
|
|
93
|
+
safetyTemplate: {
|
|
94
|
+
name: 'SafetyTemplate',
|
|
95
|
+
source: `${config.module}.SafetyTemplate`,
|
|
96
|
+
title: `@${config.i18n}:safety-templates.entities.safety-template.title`,
|
|
97
|
+
titlePlural: `@${config.i18n}:safety-templates.entities.safety-template.plural`,
|
|
98
|
+
icon: 'fa-light fa-file-lines',
|
|
99
|
+
},
|
|
100
|
+
safetyPlanSection: {
|
|
101
|
+
name: 'SafetyPlanSection',
|
|
102
|
+
source: `${config.module}.SafetyPlanSection`,
|
|
103
|
+
title: `@${config.i18n}:safety-plan-sections.entities.safety-plan-section.title`,
|
|
104
|
+
titlePlural: `@${config.i18n}:safety-plan-sections.entities.safety-plan-section.plural`,
|
|
105
|
+
icon: 'fa-light fa-list-ol',
|
|
106
|
+
},
|
|
107
|
+
workStepType: {
|
|
108
|
+
name: 'WorkStepType',
|
|
109
|
+
source: `${config.module}.WorkStepType`,
|
|
110
|
+
title: `@${config.i18n}:work-step-types.entities.work-step-type.title`,
|
|
111
|
+
titlePlural: `@${config.i18n}:work-step-types.entities.work-step-type.plural`,
|
|
112
|
+
icon: 'fa-light fa-list-tree',
|
|
113
|
+
},
|
|
114
|
+
safetyPlanActivity: {
|
|
115
|
+
name: 'SafetyPlanActivity',
|
|
116
|
+
source: `${config.module}.SafetyPlanActivity`,
|
|
117
|
+
title: `@${config.i18n}:safety-plan-activities.entities.safety-plan-activity.title`,
|
|
118
|
+
titlePlural: `@${config.i18n}:safety-plan-activities.entities.safety-plan-activity.plural`,
|
|
119
|
+
icon: 'fa-light fa-list-check',
|
|
120
|
+
},
|
|
121
|
+
},
|
|
122
|
+
queries: {
|
|
123
|
+
dashboard: {
|
|
124
|
+
openHazardsKpi: `${config.module}:Dashboard:OpenHazardsKpi`,
|
|
125
|
+
highRisksKpi: `${config.module}:Dashboard:HighRisksKpi`,
|
|
126
|
+
activeSafetyPlansKpi: `${config.module}:Dashboard:ActiveSafetyPlansKpi`,
|
|
127
|
+
take5ChecklistsKpi: `${config.module}:Dashboard:Take5ChecklistsKpi`,
|
|
128
|
+
take5CompletedKpi: `${config.module}:Dashboard:Take5CompletedKpi`,
|
|
129
|
+
hazardsByCategoryDonut: `${config.module}:Dashboard:HazardsByCategoryDonut`,
|
|
130
|
+
risksByRatingBar: `${config.module}:Dashboard:RisksByRatingBar`,
|
|
131
|
+
plansByTypeBar: `${config.module}:Dashboard:PlansByTypeBar`,
|
|
132
|
+
hazardsByStatusBar: `${config.module}:Dashboard:HazardsByStatusBar`,
|
|
133
|
+
ppeCoverageDonut: `${config.module}:Dashboard:PpeCoverageDonut`,
|
|
134
|
+
},
|
|
135
|
+
},
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
/** Risk matrix entity key in the risk-management module. */
|
|
139
|
+
const RISK_MANAGEMENT_RISK_MATRIX_ENTITY = 'RiskManagement.RiskMatrix';
|
|
140
|
+
/** Hazard catalog scope for risk matrix lookup filtering. */
|
|
141
|
+
const RISK_MANAGEMENT_HAZARD_CATALOG_SCOPE = 'risk-management.hazard';
|
|
142
|
+
|
|
143
|
+
//#region ---- Imports ----
|
|
144
|
+
//#endregion
|
|
145
|
+
//#region ---- Built-in section type IDs ----
|
|
146
|
+
/** Stable IDs for seeded built-in section types (shared by server mocks and client lookups). */
|
|
147
|
+
const WORKPLACE_SAFETY_BUILT_IN_SECTION_TYPE_IDS = {
|
|
148
|
+
hazard: 'c1d2e3f4-a5b6-4789-c001-000000000001',
|
|
149
|
+
hazardRisk: 'c1d2e3f4-a5b6-4789-c001-000000000002',
|
|
150
|
+
ppe: 'c1d2e3f4-a5b6-4789-c001-000000000003',
|
|
151
|
+
control: 'c1d2e3f4-a5b6-4789-c001-000000000004',
|
|
152
|
+
hierarchyLevel: 'c1d2e3f4-a5b6-4789-c001-000000000005',
|
|
153
|
+
custom: 'c1d2e3f4-a5b6-4789-c001-000000000006',
|
|
154
|
+
ppeHazards: 'c1d2e3f4-a5b6-4789-c001-000000000007',
|
|
155
|
+
riskMatrix: 'c1d2e3f4-a5b6-4789-c001-000000000008',
|
|
156
|
+
hazardRiskPpe: 'c1d2e3f4-a5b6-4789-c001-000000000009',
|
|
157
|
+
activity: 'c1d2e3f4-a5b6-4789-c001-00000000000a',
|
|
158
|
+
};
|
|
159
|
+
const moduleName = RootConfig.module.name;
|
|
160
|
+
/** Maps section type codes to the entity that supplies selectable items. */
|
|
161
|
+
const WORKPLACE_SAFETY_SECTION_TYPE_ENTITY_BY_CODE = {
|
|
162
|
+
Hazard: `${moduleName}.${RootConfig.entities.hazard.name}`,
|
|
163
|
+
HazardRisk: `${moduleName}.${RootConfig.entities.hazardRisk.name}`,
|
|
164
|
+
Ppe: `${moduleName}.${RootConfig.entities.ppeItem.name}`,
|
|
165
|
+
PpeHazards: `${moduleName}.${RootConfig.entities.hazardPpeRule.name}`,
|
|
166
|
+
HazardRiskPpe: undefined,
|
|
167
|
+
Control: `${moduleName}.${RootConfig.entities.controlMeasure.name}`,
|
|
168
|
+
HierarchyLevel: `${moduleName}.${RootConfig.entities.controlHierarchyLevels.name}`,
|
|
169
|
+
RiskMatrix: RISK_MANAGEMENT_RISK_MATRIX_ENTITY,
|
|
170
|
+
Custom: undefined,
|
|
171
|
+
/** Host section for Activity rows (managed by the section builder, not a catalog picker). */
|
|
172
|
+
Activity: undefined,
|
|
173
|
+
};
|
|
174
|
+
/** Built-in section types resolved by stable section type id. */
|
|
175
|
+
const WORKPLACE_SAFETY_BUILT_IN_SECTION_TYPE_BY_ID = {
|
|
176
|
+
[WORKPLACE_SAFETY_BUILT_IN_SECTION_TYPE_IDS.hazard]: {
|
|
177
|
+
sectionTypeKey: 'Hazard',
|
|
178
|
+
itemEntityName: WORKPLACE_SAFETY_SECTION_TYPE_ENTITY_BY_CODE.Hazard,
|
|
179
|
+
},
|
|
180
|
+
[WORKPLACE_SAFETY_BUILT_IN_SECTION_TYPE_IDS.hazardRisk]: {
|
|
181
|
+
sectionTypeKey: 'HazardRisk',
|
|
182
|
+
itemEntityName: WORKPLACE_SAFETY_SECTION_TYPE_ENTITY_BY_CODE.HazardRisk,
|
|
183
|
+
},
|
|
184
|
+
[WORKPLACE_SAFETY_BUILT_IN_SECTION_TYPE_IDS.ppe]: {
|
|
185
|
+
sectionTypeKey: 'Ppe',
|
|
186
|
+
itemEntityName: WORKPLACE_SAFETY_SECTION_TYPE_ENTITY_BY_CODE.Ppe,
|
|
187
|
+
},
|
|
188
|
+
[WORKPLACE_SAFETY_BUILT_IN_SECTION_TYPE_IDS.ppeHazards]: {
|
|
189
|
+
sectionTypeKey: 'PpeHazards',
|
|
190
|
+
itemEntityName: WORKPLACE_SAFETY_SECTION_TYPE_ENTITY_BY_CODE.PpeHazards,
|
|
191
|
+
},
|
|
192
|
+
[WORKPLACE_SAFETY_BUILT_IN_SECTION_TYPE_IDS.hazardRiskPpe]: {
|
|
193
|
+
sectionTypeKey: 'HazardRiskPpe',
|
|
194
|
+
itemEntityName: WORKPLACE_SAFETY_SECTION_TYPE_ENTITY_BY_CODE.HazardRiskPpe,
|
|
195
|
+
},
|
|
196
|
+
[WORKPLACE_SAFETY_BUILT_IN_SECTION_TYPE_IDS.control]: {
|
|
197
|
+
sectionTypeKey: 'Control',
|
|
198
|
+
itemEntityName: WORKPLACE_SAFETY_SECTION_TYPE_ENTITY_BY_CODE.Control,
|
|
199
|
+
},
|
|
200
|
+
[WORKPLACE_SAFETY_BUILT_IN_SECTION_TYPE_IDS.hierarchyLevel]: {
|
|
201
|
+
sectionTypeKey: 'HierarchyLevel',
|
|
202
|
+
itemEntityName: WORKPLACE_SAFETY_SECTION_TYPE_ENTITY_BY_CODE.HierarchyLevel,
|
|
203
|
+
},
|
|
204
|
+
[WORKPLACE_SAFETY_BUILT_IN_SECTION_TYPE_IDS.riskMatrix]: {
|
|
205
|
+
sectionTypeKey: 'RiskMatrix',
|
|
206
|
+
itemEntityName: WORKPLACE_SAFETY_SECTION_TYPE_ENTITY_BY_CODE.RiskMatrix,
|
|
207
|
+
},
|
|
208
|
+
[WORKPLACE_SAFETY_BUILT_IN_SECTION_TYPE_IDS.custom]: {
|
|
209
|
+
sectionTypeKey: 'Custom',
|
|
210
|
+
itemEntityName: WORKPLACE_SAFETY_SECTION_TYPE_ENTITY_BY_CODE.Custom,
|
|
211
|
+
isRepeatable: true,
|
|
212
|
+
},
|
|
213
|
+
[WORKPLACE_SAFETY_BUILT_IN_SECTION_TYPE_IDS.activity]: {
|
|
214
|
+
sectionTypeKey: 'Activity',
|
|
215
|
+
itemEntityName: WORKPLACE_SAFETY_SECTION_TYPE_ENTITY_BY_CODE.Activity,
|
|
216
|
+
isRepeatable: true,
|
|
217
|
+
},
|
|
218
|
+
};
|
|
219
|
+
/** Default entity so the lookup widget mounts before a section type is chosen. */
|
|
220
|
+
const WORKPLACE_SAFETY_SECTION_TYPE_ENTITY_FALLBACK = `${moduleName}.${RootConfig.entities.hazard.name}`;
|
|
221
|
+
//#endregion
|
|
222
|
+
//#region ---- Snapshot helpers ----
|
|
223
|
+
function resolveSectionTypeKey(sectionTypeId, snapshot) {
|
|
224
|
+
const fromSnapshot = snapshot?.sectionTypeKey;
|
|
225
|
+
if (typeof fromSnapshot === 'string' && fromSnapshot.length > 0) {
|
|
226
|
+
return fromSnapshot;
|
|
227
|
+
}
|
|
228
|
+
if (!sectionTypeId) {
|
|
229
|
+
return undefined;
|
|
230
|
+
}
|
|
231
|
+
return WORKPLACE_SAFETY_BUILT_IN_SECTION_TYPE_BY_ID[sectionTypeId]
|
|
232
|
+
?.sectionTypeKey;
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Human-readable titles for built-in section type keys (matches seeded SectionType titles).
|
|
236
|
+
* Used when snapshots lack `title` (common after id-only enrichment).
|
|
237
|
+
*/
|
|
238
|
+
const SECTION_TYPE_KEY_DISPLAY_TITLES = {
|
|
239
|
+
Hazard: 'Hazard',
|
|
240
|
+
HazardRisk: 'Hazard Risk',
|
|
241
|
+
Ppe: 'PPE',
|
|
242
|
+
PpeHazards: 'Hazard PPE Items',
|
|
243
|
+
HazardRiskPpe: 'Hazard Risk PPE',
|
|
244
|
+
Control: 'Control',
|
|
245
|
+
HierarchyLevel: 'Hierarchy Level',
|
|
246
|
+
RiskMatrix: 'Risk Matrix',
|
|
247
|
+
Custom: 'Custom',
|
|
248
|
+
Activity: 'Activity',
|
|
249
|
+
};
|
|
250
|
+
/** Formats a section type key for display (e.g. `HierarchyLevel` → `Hierarchy Level`). */
|
|
251
|
+
function formatSectionTypeKeyAsTitle(key) {
|
|
252
|
+
if (!key) {
|
|
253
|
+
return '';
|
|
254
|
+
}
|
|
255
|
+
const known = SECTION_TYPE_KEY_DISPLAY_TITLES[key];
|
|
256
|
+
if (known) {
|
|
257
|
+
return known;
|
|
258
|
+
}
|
|
259
|
+
return key.replace(/([a-z\d])([A-Z])/g, '$1 $2').trim();
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* Resolves a section type label for UI/preview: snapshot title → key display title → spaced code.
|
|
263
|
+
*/
|
|
264
|
+
function resolveSectionTypeDisplayTitle(sectionTypeId, snapshot) {
|
|
265
|
+
if (typeof snapshot?.title === 'string' && snapshot.title.trim()) {
|
|
266
|
+
return snapshot.title.trim();
|
|
267
|
+
}
|
|
268
|
+
const key = resolveSectionTypeKey(sectionTypeId, snapshot ?? undefined);
|
|
269
|
+
const fromKey = formatSectionTypeKeyAsTitle(key);
|
|
270
|
+
if (fromKey) {
|
|
271
|
+
return fromKey;
|
|
272
|
+
}
|
|
273
|
+
if (typeof snapshot?.code === 'string' && snapshot.code.trim()) {
|
|
274
|
+
return formatSectionTypeKeyAsTitle(snapshot.code.trim()) || snapshot.code.trim();
|
|
275
|
+
}
|
|
276
|
+
return '';
|
|
277
|
+
}
|
|
278
|
+
function resolveSectionItemEntityName(sectionTypeId, snapshot) {
|
|
279
|
+
const fromSnapshot = snapshot?.itemEntityName;
|
|
280
|
+
if (typeof fromSnapshot === 'string' && fromSnapshot.length > 0) {
|
|
281
|
+
return fromSnapshot;
|
|
282
|
+
}
|
|
283
|
+
if (!sectionTypeId) {
|
|
284
|
+
return undefined;
|
|
285
|
+
}
|
|
286
|
+
return WORKPLACE_SAFETY_BUILT_IN_SECTION_TYPE_BY_ID[sectionTypeId]
|
|
287
|
+
?.itemEntityName;
|
|
288
|
+
}
|
|
289
|
+
/** Adds runtime lookup metadata (`sectionTypeKey`, `itemEntityName`) from built-in id map. */
|
|
290
|
+
function enrichSectionTypeSnapshot(sectionTypeId, snapshot) {
|
|
291
|
+
if (!sectionTypeId) {
|
|
292
|
+
return snapshot;
|
|
293
|
+
}
|
|
294
|
+
return {
|
|
295
|
+
...snapshot,
|
|
296
|
+
id: sectionTypeId,
|
|
297
|
+
sectionTypeKey: resolveSectionTypeKey(sectionTypeId, snapshot),
|
|
298
|
+
itemEntityName: resolveSectionItemEntityName(sectionTypeId, snapshot),
|
|
299
|
+
isRepeatable: snapshot?.isRepeatable ??
|
|
300
|
+
WORKPLACE_SAFETY_BUILT_IN_SECTION_TYPE_BY_ID[sectionTypeId]?.isRepeatable,
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
//#endregion
|
|
304
|
+
//#region ---- Dynamic entity expressions ----
|
|
305
|
+
function buildSectionTypeEntityExpressionById(sectionTypeIdPath) {
|
|
306
|
+
const parts = Object.entries(WORKPLACE_SAFETY_BUILT_IN_SECTION_TYPE_BY_ID)
|
|
307
|
+
.filter(([, meta]) => meta.itemEntityName)
|
|
308
|
+
.map(([id, meta]) => `context.eval('${sectionTypeIdPath}') === '${id}' ? '${meta.itemEntityName}'`);
|
|
309
|
+
return `{{ ${parts.join(' : ')} : '${WORKPLACE_SAFETY_SECTION_TYPE_ENTITY_FALLBACK}' }}`;
|
|
310
|
+
}
|
|
311
|
+
function buildSectionTypeEntityExpressionWithSnapshotFallback(sectionTypeIdPath, itemEntityNamePath) {
|
|
312
|
+
const byId = buildSectionTypeEntityExpressionById(sectionTypeIdPath).slice(3, -2);
|
|
313
|
+
return `{{ context.eval('${itemEntityNamePath}') || (${byId}) }}`;
|
|
314
|
+
}
|
|
315
|
+
/**
|
|
316
|
+
* For repeater / nested FormField widgets — `>` walks up to the row where `sectionType` is exposed.
|
|
317
|
+
*/
|
|
318
|
+
const WORKPLACE_SAFETY_SECTION_TYPE_ENTITY_EXPRESSION = buildSectionTypeEntityExpressionWithSnapshotFallback('>sectionType.id', '>sectionType.itemEntityName');
|
|
319
|
+
/**
|
|
320
|
+
* Entity expression keyed by the bound `sectionTypeId` field (repeater-safe).
|
|
321
|
+
* Prefer this when expose snapshots may not live on the same path the lookup evaluates.
|
|
322
|
+
*/
|
|
323
|
+
function buildWorkplaceSafetySectionTypeEntityExpression(sectionTypeIdPath) {
|
|
324
|
+
return buildSectionTypeEntityExpressionById(sectionTypeIdPath);
|
|
325
|
+
}
|
|
326
|
+
/**
|
|
327
|
+
* For flat dialog / dynamic forms — sibling fields share the same context (no parent path).
|
|
328
|
+
*/
|
|
329
|
+
const WORKPLACE_SAFETY_SECTION_TYPE_ENTITY_EXPRESSION_SIBLING = buildSectionTypeEntityExpressionWithSnapshotFallback('sectionType.id', 'sectionType.itemEntityName');
|
|
330
|
+
//#endregion
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* Shared helpers for Safety Plan Section type identity (repeatable vs one-off).
|
|
334
|
+
* Leaf module — imports only other contract leaves (no client/server layers).
|
|
335
|
+
*/
|
|
336
|
+
//#region ---- Helpers ----
|
|
337
|
+
function resolveRefId(raw) {
|
|
338
|
+
if (typeof raw === 'string' && raw.trim().length > 0) {
|
|
339
|
+
return raw.trim();
|
|
340
|
+
}
|
|
341
|
+
if (typeof raw === 'number' && Number.isFinite(raw)) {
|
|
342
|
+
return String(raw);
|
|
343
|
+
}
|
|
344
|
+
if (raw && typeof raw === 'object') {
|
|
345
|
+
const id = raw.id;
|
|
346
|
+
if (typeof id === 'string' && id.trim().length > 0) {
|
|
347
|
+
return id.trim();
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
return '';
|
|
351
|
+
}
|
|
352
|
+
/**
|
|
353
|
+
* True when this section type may appear more than once in a Safety Plan.
|
|
354
|
+
* Checks (in order): snapshot flag, explicit allow-list, built-in section type meta.
|
|
355
|
+
*/
|
|
356
|
+
function isSafetyPlanSectionTypeRepeatable(sectionTypeId, options) {
|
|
357
|
+
const id = resolveRefId(sectionTypeId);
|
|
358
|
+
if (!id) {
|
|
359
|
+
return false;
|
|
360
|
+
}
|
|
361
|
+
if (options?.sectionType?.isRepeatable === true) {
|
|
362
|
+
return true;
|
|
363
|
+
}
|
|
364
|
+
const allowList = new Set((options?.repeatableSectionTypeIds ?? []).map((entry) => resolveRefId(entry)).filter(Boolean));
|
|
365
|
+
if (allowList.has(id)) {
|
|
366
|
+
return true;
|
|
367
|
+
}
|
|
368
|
+
return WORKPLACE_SAFETY_BUILT_IN_SECTION_TYPE_BY_ID[id]?.isRepeatable === true;
|
|
369
|
+
}
|
|
370
|
+
/** Built-in section type ids that are always treated as repeatable. */
|
|
371
|
+
function collectBuiltInRepeatableSectionTypeIds() {
|
|
372
|
+
return Object.entries(WORKPLACE_SAFETY_BUILT_IN_SECTION_TYPE_BY_ID)
|
|
373
|
+
.filter(([, meta]) => meta.isRepeatable === true)
|
|
374
|
+
.map(([id]) => id);
|
|
375
|
+
}
|
|
376
|
+
//#endregion
|
|
377
|
+
|
|
378
|
+
//#region ---- Imports ----
|
|
379
|
+
//#endregion
|
|
380
|
+
//#region ---- Display helpers ----
|
|
381
|
+
/** Coerces a stored string or locale map into a single display string. */
|
|
382
|
+
function asSafetyPlanDisplayText(value) {
|
|
383
|
+
if (typeof value === 'string') {
|
|
384
|
+
return value.trim();
|
|
385
|
+
}
|
|
386
|
+
if (value && typeof value === 'object' && !Array.isArray(value)) {
|
|
387
|
+
const rec = value;
|
|
388
|
+
for (const locale of ['en-US', 'en-AU', 'en', 'fa-IR']) {
|
|
389
|
+
const part = rec[locale];
|
|
390
|
+
if (typeof part === 'string' && part.trim()) {
|
|
391
|
+
return part.trim();
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
for (const part of Object.values(rec)) {
|
|
395
|
+
if (typeof part === 'string' && part.trim()) {
|
|
396
|
+
return part.trim();
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
return '';
|
|
401
|
+
}
|
|
402
|
+
function buildSectionTypeRelationSnapshot(sectionTypeId, snapshot) {
|
|
403
|
+
const title = asSafetyPlanDisplayText(snapshot?.title);
|
|
404
|
+
const code = asSafetyPlanDisplayText(snapshot?.code);
|
|
405
|
+
const description = asSafetyPlanDisplayText(snapshot?.description);
|
|
406
|
+
return {
|
|
407
|
+
id: sectionTypeId,
|
|
408
|
+
...(title ? { title } : {}),
|
|
409
|
+
...(code ? { code } : {}),
|
|
410
|
+
...(description ? { description } : {}),
|
|
411
|
+
...(snapshot?.isRepeatable === true ? { isRepeatable: true } : {}),
|
|
412
|
+
};
|
|
413
|
+
}
|
|
414
|
+
function resolveSectionTypeTitle(sectionTypeId, sectionType) {
|
|
415
|
+
const title = asSafetyPlanDisplayText(sectionType?.title);
|
|
416
|
+
if (title) {
|
|
417
|
+
return title;
|
|
418
|
+
}
|
|
419
|
+
const code = asSafetyPlanDisplayText(sectionType?.code);
|
|
420
|
+
if (code) {
|
|
421
|
+
return code;
|
|
422
|
+
}
|
|
423
|
+
const builtIn = WORKPLACE_SAFETY_BUILT_IN_SECTION_TYPE_BY_ID[sectionTypeId]?.sectionTypeKey;
|
|
424
|
+
if (builtIn) {
|
|
425
|
+
return builtIn;
|
|
426
|
+
}
|
|
427
|
+
return sectionTypeId;
|
|
428
|
+
}
|
|
429
|
+
/**
|
|
430
|
+
* Title persisted on SafetyPlanSection. Never stores a bare UUID — the UI translate
|
|
431
|
+
* pipe treats unknown keys as empty, which made seeded section headers blank.
|
|
432
|
+
*/
|
|
433
|
+
function resolveSafetyPlanSectionPersistedTitle(sectionTitle, sectionTypeId, snapshot) {
|
|
434
|
+
const explicit = asSafetyPlanDisplayText(sectionTitle);
|
|
435
|
+
if (explicit && explicit !== sectionTypeId) {
|
|
436
|
+
return explicit;
|
|
437
|
+
}
|
|
438
|
+
const fromType = resolveSectionTypeTitle(sectionTypeId, snapshot);
|
|
439
|
+
if (fromType && fromType !== sectionTypeId) {
|
|
440
|
+
return fromType;
|
|
441
|
+
}
|
|
442
|
+
return 'Section';
|
|
443
|
+
}
|
|
444
|
+
//#endregion
|
|
445
|
+
//#region ---- Ref helpers ----
|
|
446
|
+
/** True when a type-section row is marked as the plan-type default template. */
|
|
447
|
+
function isSafetyPlanTypeSectionDefault(row) {
|
|
448
|
+
const value = row?.isDefault;
|
|
449
|
+
return value === true || value === 1 || value === 'true';
|
|
450
|
+
}
|
|
451
|
+
/**
|
|
452
|
+
* Returns the default Safety Plan Type Section (structure) for a plan type.
|
|
453
|
+
* Prefers `isDefault: true`; otherwise the first active row by sortOrder/title.
|
|
454
|
+
*/
|
|
455
|
+
function resolveDefaultSafetyPlanTypeSection(typeSections) {
|
|
456
|
+
const active = typeSections
|
|
457
|
+
.filter((row) => row && row.isArchived !== true)
|
|
458
|
+
.slice()
|
|
459
|
+
.sort((a, b) => (a.sortOrder ?? 0) - (b.sortOrder ?? 0) || String(a.title ?? '').localeCompare(String(b.title ?? '')));
|
|
460
|
+
if (!active.length) {
|
|
461
|
+
return null;
|
|
462
|
+
}
|
|
463
|
+
return active.find((row) => isSafetyPlanTypeSectionDefault(row)) ?? active[0] ?? null;
|
|
464
|
+
}
|
|
465
|
+
/**
|
|
466
|
+
* Narrows type-section rows to the selected structure when `structureId` is set.
|
|
467
|
+
* Falls back to all active rows (caller / build still applies isDefault rules).
|
|
468
|
+
*/
|
|
469
|
+
function pickSafetyPlanTypeSectionsForStructure(typeSections, structureId) {
|
|
470
|
+
const active = typeSections.filter((row) => row && row.isArchived !== true);
|
|
471
|
+
const id = typeof structureId === 'string' ? structureId.trim() : '';
|
|
472
|
+
if (!id) {
|
|
473
|
+
return active;
|
|
474
|
+
}
|
|
475
|
+
const selected = active.filter((row) => resolveEntityRefId(row.id) === id);
|
|
476
|
+
return selected.length ? selected : active;
|
|
477
|
+
}
|
|
478
|
+
/** Extracts a stable entity id from a raw lookup / context value. */
|
|
479
|
+
function resolveEntityRefId(raw) {
|
|
480
|
+
if (typeof raw === 'string' && raw.trim().length > 0) {
|
|
481
|
+
return raw.trim();
|
|
482
|
+
}
|
|
483
|
+
if (typeof raw === 'number' && Number.isFinite(raw)) {
|
|
484
|
+
return String(raw);
|
|
485
|
+
}
|
|
486
|
+
if (raw && typeof raw === 'object') {
|
|
487
|
+
const id = raw.id;
|
|
488
|
+
if (typeof id === 'string' && id.trim().length > 0) {
|
|
489
|
+
return id.trim();
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
return null;
|
|
493
|
+
}
|
|
494
|
+
function resolveSectionTypeId(typeRow) {
|
|
495
|
+
if (!typeRow) {
|
|
496
|
+
return '';
|
|
497
|
+
}
|
|
498
|
+
return (resolveEntityRefId(typeRow.sectionTypeId) ??
|
|
499
|
+
resolveEntityRefId(typeRow.sectionType) ??
|
|
500
|
+
resolveEntityRefId(typeRow.sectionType?.id) ??
|
|
501
|
+
'');
|
|
502
|
+
}
|
|
503
|
+
//#endregion
|
|
504
|
+
//#region ---- Build from type sections ----
|
|
505
|
+
let structureIdSeq = 0;
|
|
506
|
+
function nextStructureId(prefix) {
|
|
507
|
+
structureIdSeq += 1;
|
|
508
|
+
const rand = createRandomUuid();
|
|
509
|
+
return prefix ? `${prefix}-${rand}` : rand;
|
|
510
|
+
}
|
|
511
|
+
/**
|
|
512
|
+
* Builds an empty editable section structure from SafetyPlanTypeSection rows for one plan type.
|
|
513
|
+
*
|
|
514
|
+
* Uses `isDefault` rows when present; otherwise falls back to all active rows for that type
|
|
515
|
+
* (so a mis-flagged template still produces a structure). One plan section per `sectionTypeIds`
|
|
516
|
+
* entry, titled from the Section Type, ordered by type-section `sortOrder` then repeater order.
|
|
517
|
+
*/
|
|
518
|
+
function buildSectionStructureFromTypeSections(typeSections, options) {
|
|
519
|
+
const createId = options?.createId ?? (() => nextStructureId(''));
|
|
520
|
+
const active = typeSections
|
|
521
|
+
.filter((row) => row && row.isArchived !== true)
|
|
522
|
+
.slice()
|
|
523
|
+
.sort((a, b) => (a.sortOrder ?? 0) - (b.sortOrder ?? 0) || String(a.title ?? '').localeCompare(String(b.title ?? '')));
|
|
524
|
+
const defaults = active.filter((row) => isSafetyPlanTypeSectionDefault(row));
|
|
525
|
+
const sourceRows = defaults.length > 0 ? defaults : active;
|
|
526
|
+
const sections = [];
|
|
527
|
+
const seenSourceTypePairs = new Set();
|
|
528
|
+
let sortOrder = 1;
|
|
529
|
+
for (const typeSection of sourceRows) {
|
|
530
|
+
const typeRows = Array.isArray(typeSection.sectionTypeIds) ? typeSection.sectionTypeIds : [];
|
|
531
|
+
if (!typeRows.length) {
|
|
532
|
+
continue;
|
|
533
|
+
}
|
|
534
|
+
for (const typeRow of typeRows) {
|
|
535
|
+
const sectionTypeId = resolveSectionTypeId(typeRow);
|
|
536
|
+
if (!sectionTypeId) {
|
|
537
|
+
continue;
|
|
538
|
+
}
|
|
539
|
+
// One plan section per (type-section source, section type) — prevents
|
|
540
|
+
// accidental double expansion when the same type is listed twice.
|
|
541
|
+
const sourceId = typeof typeSection.id === 'string' ? typeSection.id : '';
|
|
542
|
+
const pairKey = sourceId ? `${sourceId}|${sectionTypeId}` : sectionTypeId;
|
|
543
|
+
if (seenSourceTypePairs.has(pairKey)) {
|
|
544
|
+
continue;
|
|
545
|
+
}
|
|
546
|
+
seenSourceTypePairs.add(pairKey);
|
|
547
|
+
const sectionType = buildSectionTypeRelationSnapshot(sectionTypeId, typeRow.sectionType);
|
|
548
|
+
const sectionId = createId();
|
|
549
|
+
sections.push({
|
|
550
|
+
id: sectionId,
|
|
551
|
+
sectionTypeId,
|
|
552
|
+
sectionType,
|
|
553
|
+
title: resolveSafetyPlanSectionPersistedTitle(undefined, sectionTypeId, sectionType),
|
|
554
|
+
description: asSafetyPlanDisplayText(sectionType.description),
|
|
555
|
+
sourceSafetyPlanTypeSectionId: typeSection.id,
|
|
556
|
+
sortOrder: sortOrder++,
|
|
557
|
+
required: isSafetyPlanTypeSectionDefault(typeSection) || defaults.length === 0,
|
|
558
|
+
items: [
|
|
559
|
+
{
|
|
560
|
+
id: `${sectionId}__item_1`,
|
|
561
|
+
itemIds: [],
|
|
562
|
+
items: [],
|
|
563
|
+
},
|
|
564
|
+
],
|
|
565
|
+
});
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
return { sections };
|
|
569
|
+
}
|
|
570
|
+
/**
|
|
571
|
+
* Removes accidental ghost rows from double materialization of the same type-section
|
|
572
|
+
* source. Distinct sections of the same type are kept. Reassigns contiguous 1-based
|
|
573
|
+
* `sortOrder` to preserve display order.
|
|
574
|
+
*/
|
|
575
|
+
function dedupeSafetyPlanSectionStructure(structure, _options) {
|
|
576
|
+
const sections = Array.isArray(structure?.sections)
|
|
577
|
+
? structure.sections
|
|
578
|
+
: [];
|
|
579
|
+
const seenSourceTypePairs = new Set();
|
|
580
|
+
const deduped = [];
|
|
581
|
+
for (const section of sections) {
|
|
582
|
+
const sectionTypeId = resolveEntityRefId(section.sectionTypeId) ?? String(section.sectionTypeId ?? '').trim();
|
|
583
|
+
if (!sectionTypeId) {
|
|
584
|
+
continue;
|
|
585
|
+
}
|
|
586
|
+
const sourceId = String(section.sourceSafetyPlanTypeSectionId ?? '').trim();
|
|
587
|
+
const sourcePairKey = sourceId ? `${sourceId}|${sectionTypeId}` : '';
|
|
588
|
+
// Same type-section source + section type must never appear twice (double materialization).
|
|
589
|
+
if (sourcePairKey && seenSourceTypePairs.has(sourcePairKey)) {
|
|
590
|
+
continue;
|
|
591
|
+
}
|
|
592
|
+
if (sourcePairKey) {
|
|
593
|
+
seenSourceTypePairs.add(sourcePairKey);
|
|
594
|
+
}
|
|
595
|
+
deduped.push(section);
|
|
596
|
+
}
|
|
597
|
+
return {
|
|
598
|
+
...structure,
|
|
599
|
+
sections: deduped.map((section, index) => ({
|
|
600
|
+
...section,
|
|
601
|
+
sortOrder: index + 1,
|
|
602
|
+
})),
|
|
603
|
+
};
|
|
604
|
+
}
|
|
605
|
+
function collectAllowedSectionTypeIds(typeSections) {
|
|
606
|
+
const ids = new Set();
|
|
607
|
+
for (const typeSection of typeSections) {
|
|
608
|
+
if (typeSection.isArchived === true) {
|
|
609
|
+
continue;
|
|
610
|
+
}
|
|
611
|
+
for (const row of typeSection.sectionTypeIds ?? []) {
|
|
612
|
+
const id = resolveSectionTypeId(row);
|
|
613
|
+
if (id) {
|
|
614
|
+
ids.add(id);
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
return [...ids];
|
|
619
|
+
}
|
|
620
|
+
function collectRequiredSourceTypeSectionIds(typeSections, options) {
|
|
621
|
+
const structureId = typeof options?.structureId === 'string' && options.structureId.trim()
|
|
622
|
+
? options.structureId.trim()
|
|
623
|
+
: '';
|
|
624
|
+
if (structureId) {
|
|
625
|
+
const selected = typeSections.find((row) => row.isArchived !== true && resolveEntityRefId(row.id) === structureId);
|
|
626
|
+
return selected?.id && typeof selected.id === 'string' ? [selected.id] : [structureId];
|
|
627
|
+
}
|
|
628
|
+
return typeSections
|
|
629
|
+
.filter((row) => row.isArchived !== true && isSafetyPlanTypeSectionDefault(row))
|
|
630
|
+
.map((row) => row.id)
|
|
631
|
+
.filter((id) => typeof id === 'string' && id.length > 0);
|
|
632
|
+
}
|
|
633
|
+
/**
|
|
634
|
+
* Validates that a plan-type section template does not list the same
|
|
635
|
+
* non-repeatable section type more than once (across rows and within a single row's repeater).
|
|
636
|
+
*/
|
|
637
|
+
function validateSafetyPlanTypeSectionTemplate(typeSections, repeatableSectionTypeIds = []) {
|
|
638
|
+
const seen = new Map();
|
|
639
|
+
const issues = [];
|
|
640
|
+
for (const typeSection of typeSections) {
|
|
641
|
+
if (typeSection.isArchived === true) {
|
|
642
|
+
continue;
|
|
643
|
+
}
|
|
644
|
+
for (const row of typeSection.sectionTypeIds ?? []) {
|
|
645
|
+
const sectionTypeId = resolveSectionTypeId(row);
|
|
646
|
+
if (!sectionTypeId ||
|
|
647
|
+
isSafetyPlanSectionTypeRepeatable(sectionTypeId, {
|
|
648
|
+
repeatableSectionTypeIds,
|
|
649
|
+
sectionType: row.sectionType,
|
|
650
|
+
})) {
|
|
651
|
+
continue;
|
|
652
|
+
}
|
|
653
|
+
const sectionTypeTitle = resolveSafetyPlanSectionPersistedTitle(undefined, sectionTypeId, row.sectionType);
|
|
654
|
+
if (seen.has(sectionTypeId)) {
|
|
655
|
+
issues.push({
|
|
656
|
+
code: 'INVALID_TYPE_SECTION_TEMPLATE',
|
|
657
|
+
messageKey: '@workplacesafety-management:safety-plans.messages.section-structure.invalid-type-template-duplicate',
|
|
658
|
+
details: {
|
|
659
|
+
sectionTypeId,
|
|
660
|
+
sectionTypeTitle,
|
|
661
|
+
sourceSafetyPlanTypeSectionId: typeSection.id,
|
|
662
|
+
previousSourceSafetyPlanTypeSectionId: seen.get(sectionTypeId)?.sourceSafetyPlanTypeSectionId,
|
|
663
|
+
},
|
|
664
|
+
});
|
|
665
|
+
continue;
|
|
666
|
+
}
|
|
667
|
+
seen.set(sectionTypeId, {
|
|
668
|
+
title: sectionTypeTitle,
|
|
669
|
+
sourceSafetyPlanTypeSectionId: typeSection.id,
|
|
670
|
+
});
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
return issues;
|
|
674
|
+
}
|
|
675
|
+
//#endregion
|
|
676
|
+
//#region ---- Validation ----
|
|
677
|
+
function validateSafetyPlanSectionStructure(structure, context) {
|
|
678
|
+
const sections = Array.isArray(structure?.sections) ? structure.sections : [];
|
|
679
|
+
const issues = [];
|
|
680
|
+
const presentSourceIds = new Set(sections
|
|
681
|
+
.map((s) => s.sourceSafetyPlanTypeSectionId)
|
|
682
|
+
.filter((id) => typeof id === 'string' && id.length > 0));
|
|
683
|
+
for (const requiredSourceId of context.requiredSourceTypeSectionIds) {
|
|
684
|
+
if (!presentSourceIds.has(requiredSourceId)) {
|
|
685
|
+
issues.push({
|
|
686
|
+
code: 'REQUIRED_SECTION_MISSING',
|
|
687
|
+
messageKey: '@workplacesafety-management:safety-plans.messages.section-structure.required-section-missing',
|
|
688
|
+
details: { sourceSafetyPlanTypeSectionId: requiredSourceId },
|
|
689
|
+
});
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
const allowed = new Set(context.allowedSectionTypeIds);
|
|
693
|
+
const sortOrders = [];
|
|
694
|
+
for (const section of sections) {
|
|
695
|
+
const sectionTypeId = resolveEntityRefId(section.sectionTypeId) ??
|
|
696
|
+
String(section.sectionTypeId ?? '').trim();
|
|
697
|
+
if (!sectionTypeId) {
|
|
698
|
+
continue;
|
|
699
|
+
}
|
|
700
|
+
const sectionType = section
|
|
701
|
+
.sectionType;
|
|
702
|
+
const sectionTypeTitle = resolveSafetyPlanSectionPersistedTitle(undefined, sectionTypeId, sectionType);
|
|
703
|
+
if (allowed.size > 0 && !allowed.has(sectionTypeId)) {
|
|
704
|
+
issues.push({
|
|
705
|
+
code: 'SECTION_TYPE_NOT_ALLOWED',
|
|
706
|
+
messageKey: '@workplacesafety-management:safety-plans.messages.section-structure.section-type-not-allowed',
|
|
707
|
+
details: { sectionTypeId, sectionTypeTitle, sectionId: section.id },
|
|
708
|
+
});
|
|
709
|
+
}
|
|
710
|
+
const sortOrder = section.sortOrder;
|
|
711
|
+
if (typeof sortOrder === 'number' && Number.isFinite(sortOrder)) {
|
|
712
|
+
sortOrders.push(sortOrder);
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
if (sortOrders.length !== sections.length) {
|
|
716
|
+
issues.push({
|
|
717
|
+
code: 'INVALID_SORT_ORDER',
|
|
718
|
+
messageKey: '@workplacesafety-management:safety-plans.messages.section-structure.invalid-sort-order',
|
|
719
|
+
});
|
|
720
|
+
}
|
|
721
|
+
else {
|
|
722
|
+
const unique = new Set(sortOrders);
|
|
723
|
+
if (unique.size !== sortOrders.length) {
|
|
724
|
+
issues.push({
|
|
725
|
+
code: 'INVALID_SORT_ORDER',
|
|
726
|
+
messageKey: '@workplacesafety-management:safety-plans.messages.section-structure.invalid-sort-order',
|
|
727
|
+
details: { reason: 'duplicate' },
|
|
728
|
+
});
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
return issues;
|
|
732
|
+
}
|
|
733
|
+
function toBuilderValueFromStructure(structure) {
|
|
734
|
+
return {
|
|
735
|
+
sections: structure.sections.map((section) => ({
|
|
736
|
+
id: section.id,
|
|
737
|
+
sectionTypeId: section.sectionTypeId,
|
|
738
|
+
sectionType: section.sectionType,
|
|
739
|
+
title: section.title,
|
|
740
|
+
description: section.description ?? '',
|
|
741
|
+
safetyPlanId: section.safetyPlanId,
|
|
742
|
+
code: section.code,
|
|
743
|
+
sourceSafetyPlanTypeSectionId: section.sourceSafetyPlanTypeSectionId,
|
|
744
|
+
sortOrder: section.sortOrder,
|
|
745
|
+
required: section.required,
|
|
746
|
+
items: section.items.map((item) => ({
|
|
747
|
+
id: item.id,
|
|
748
|
+
itemIds: [...(item.itemIds ?? [])],
|
|
749
|
+
items: item.items ? [...item.items] : [],
|
|
750
|
+
})),
|
|
751
|
+
})),
|
|
752
|
+
};
|
|
753
|
+
}
|
|
754
|
+
//#endregion
|
|
755
|
+
|
|
756
|
+
//#region ---- Imports ----
|
|
757
|
+
/** Declarative section-type dependencies resolved from prior sections in plan order. */
|
|
758
|
+
const WORKPLACE_SAFETY_SECTION_TYPE_DEPENDENCIES = {
|
|
759
|
+
HazardRisk: {
|
|
760
|
+
kind: 'parentFilter',
|
|
761
|
+
parentSectionTypeKey: 'Hazard',
|
|
762
|
+
filterField: 'hazardId',
|
|
763
|
+
},
|
|
764
|
+
PpeHazards: {
|
|
765
|
+
kind: 'parentFilter',
|
|
766
|
+
parentSectionTypeKey: 'Hazard',
|
|
767
|
+
filterField: 'hazardId',
|
|
768
|
+
},
|
|
769
|
+
HazardRiskPpe: {
|
|
770
|
+
kind: 'parentFilter',
|
|
771
|
+
parentSectionTypeKey: 'HazardRisk',
|
|
772
|
+
filterField: 'hazardRiskId',
|
|
773
|
+
},
|
|
774
|
+
RiskMatrix: {
|
|
775
|
+
kind: 'derivedFromParent',
|
|
776
|
+
parentSectionTypeKey: 'HazardRisk',
|
|
777
|
+
resolveField: 'riskMatrixId',
|
|
778
|
+
filterField: 'id',
|
|
779
|
+
},
|
|
780
|
+
};
|
|
781
|
+
/** Section types cleared when upstream hazard selections change. */
|
|
782
|
+
const WORKPLACE_SAFETY_DOWNSTREAM_FROM_HAZARD = [
|
|
783
|
+
'HazardRisk',
|
|
784
|
+
'PpeHazards',
|
|
785
|
+
'HazardRiskPpe',
|
|
786
|
+
'RiskMatrix',
|
|
787
|
+
];
|
|
788
|
+
/** Section types cleared when upstream hazard-risk selections change. */
|
|
789
|
+
const WORKPLACE_SAFETY_DOWNSTREAM_FROM_HAZARD_RISK = [
|
|
790
|
+
'HazardRiskPpe',
|
|
791
|
+
'RiskMatrix',
|
|
792
|
+
];
|
|
793
|
+
//#endregion
|
|
794
|
+
//#region ---- Lookup helpers ----
|
|
795
|
+
const PARENT_HAZARD_IDS_CONTEXT_KEY = 'parentHazardIds';
|
|
796
|
+
const PARENT_HAZARD_RISK_IDS_CONTEXT_KEY = 'parentHazardRiskIds';
|
|
797
|
+
/** Expose paths when a section type is selected (shared by entity repeater and builder dialog). */
|
|
798
|
+
const WORKPLACE_SAFETY_SECTION_TYPE_LOOKUP_EXPOSE = [
|
|
799
|
+
{ source: 'id', target: 'sectionType.id' },
|
|
800
|
+
{ source: 'title', target: 'sectionType.title' },
|
|
801
|
+
{ source: 'code', target: 'sectionType.code' },
|
|
802
|
+
{ source: 'description', target: 'sectionType.description' },
|
|
803
|
+
{ source: 'isRepeatable', target: 'sectionType.isRepeatable' },
|
|
804
|
+
];
|
|
805
|
+
/** Auto-fill section description when section type is selected. */
|
|
806
|
+
const WORKPLACE_SAFETY_SECTION_TYPE_DESCRIPTION_SYNC_TRIGGERS = [
|
|
807
|
+
{
|
|
808
|
+
event: "{{ events.context('sectionTypeId') }}",
|
|
809
|
+
action: "{{ const items = widget.output('selectedItems'); const selectedDescription = Array.isArray(items) ? items[0]?.description : undefined; const d = selectedDescription || context.eval('sectionType.description') || ''; context.set('sectionTypeDescription', d); const descriptionWidget = widget.find('sectionTypeDescription'); if (descriptionWidget) { descriptionWidget.setValue(d); } }}",
|
|
810
|
+
},
|
|
811
|
+
{
|
|
812
|
+
event: "{{ events.context('sectionType.description') }}",
|
|
813
|
+
action: "{{ const d = context.eval('sectionType.description') || ''; context.set('sectionTypeDescription', d); const descriptionWidget = widget.find('sectionTypeDescription'); if (descriptionWidget) { descriptionWidget.setValue(d); } }}",
|
|
814
|
+
},
|
|
815
|
+
];
|
|
816
|
+
/** Auto-fill section title when section type is selected. */
|
|
817
|
+
const WORKPLACE_SAFETY_SECTION_TYPE_TITLE_SYNC_TRIGGERS = [
|
|
818
|
+
{
|
|
819
|
+
event: "{{ events.context('sectionTypeId') }}",
|
|
820
|
+
action: "{{ const items = widget.output('selectedItems'); const selectedTitle = Array.isArray(items) ? items[0]?.title : undefined; const t = selectedTitle || context.eval('sectionType.title') || ''; if (t) { context.set('title', t); const titleWidget = widget.find('title'); if (titleWidget) { titleWidget.setValue(t); } } }}",
|
|
821
|
+
},
|
|
822
|
+
{
|
|
823
|
+
event: "{{ events.context('sectionType.title') }}",
|
|
824
|
+
action: "{{ const t = context.eval('sectionType.title') || ''; if (t) { context.set('title', t); const titleWidget = widget.find('title'); if (titleWidget) { titleWidget.setValue(t); } } }}",
|
|
825
|
+
},
|
|
826
|
+
];
|
|
827
|
+
/** Shared lookup widget options for section type pickers (builder dialog and entity forms). */
|
|
828
|
+
const WORKPLACE_SAFETY_SECTION_TYPE_LOOKUP_WIDGET_OPTIONS = {
|
|
829
|
+
look: 'select',
|
|
830
|
+
allowSearch: true,
|
|
831
|
+
textField: 'code',
|
|
832
|
+
textTemplate: '{code} — {description}',
|
|
833
|
+
valueField: 'id',
|
|
834
|
+
columns: ['code', 'title', 'description'],
|
|
835
|
+
expose: WORKPLACE_SAFETY_SECTION_TYPE_LOOKUP_EXPOSE,
|
|
836
|
+
triggers: [
|
|
837
|
+
...WORKPLACE_SAFETY_SECTION_TYPE_TITLE_SYNC_TRIGGERS,
|
|
838
|
+
...WORKPLACE_SAFETY_SECTION_TYPE_DESCRIPTION_SYNC_TRIGGERS,
|
|
839
|
+
],
|
|
840
|
+
};
|
|
841
|
+
//#endregion
|
|
842
|
+
//#region ---- Resolver API ----
|
|
843
|
+
function getSectionTypeDependency(sectionTypeKey) {
|
|
844
|
+
if (!sectionTypeKey) {
|
|
845
|
+
return undefined;
|
|
846
|
+
}
|
|
847
|
+
return WORKPLACE_SAFETY_SECTION_TYPE_DEPENDENCIES[sectionTypeKey];
|
|
848
|
+
}
|
|
849
|
+
function sectionTypeKeyFromSection(section) {
|
|
850
|
+
const sectionTypeId = section.sectionTypeId || section.sectionType?.id;
|
|
851
|
+
const key = resolveSectionTypeKey(sectionTypeId, section.sectionType);
|
|
852
|
+
return key;
|
|
853
|
+
}
|
|
854
|
+
function collectSectionItemIds(section, sectionTypeKey) {
|
|
855
|
+
if (!section?.items?.length || sectionTypeKeyFromSection(section) !== sectionTypeKey) {
|
|
856
|
+
return [];
|
|
857
|
+
}
|
|
858
|
+
const ids = new Set();
|
|
859
|
+
for (const item of section.items) {
|
|
860
|
+
for (const id of item.itemIds ?? []) {
|
|
861
|
+
if (typeof id === 'string' && id.length > 0) {
|
|
862
|
+
ids.add(id);
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
}
|
|
866
|
+
return [...ids];
|
|
867
|
+
}
|
|
868
|
+
/** Collects item IDs from sections up to `sectionIndex` whose type matches `parentSectionTypeKey`. */
|
|
869
|
+
function resolveParentItemIds(sections, sectionIndex, parentSectionTypeKey) {
|
|
870
|
+
const ids = new Set();
|
|
871
|
+
const lastIndex = Math.min(sectionIndex, sections.length - 1);
|
|
872
|
+
for (let index = 0; index <= lastIndex; index += 1) {
|
|
873
|
+
collectSectionItemIds(sections[index], parentSectionTypeKey).forEach((id) => ids.add(id));
|
|
874
|
+
}
|
|
875
|
+
return [...ids];
|
|
876
|
+
}
|
|
877
|
+
/** Collects hazard IDs from earlier sections and Hazard rows in the current section. */
|
|
878
|
+
function resolveParentHazardIds(sections, sectionIndex, parentSectionTypeKey = 'Hazard') {
|
|
879
|
+
return resolveParentItemIds(sections, sectionIndex, parentSectionTypeKey);
|
|
880
|
+
}
|
|
881
|
+
/** Collects hazard-risk IDs from prior HazardRisk sections up to `sectionIndex`. */
|
|
882
|
+
function resolveParentHazardRiskIds(sections, sectionIndex) {
|
|
883
|
+
return resolveParentItemIds(sections, sectionIndex, 'HazardRisk');
|
|
884
|
+
}
|
|
885
|
+
function parentItemSignature(sections, sectionIndex, parentSectionTypeKey) {
|
|
886
|
+
return resolveParentItemIds(sections, sectionIndex, parentSectionTypeKey).sort().join('|');
|
|
887
|
+
}
|
|
888
|
+
function shouldClearSectionOnParentChange(section, sectionIndex, changeIndex, parentTypeKey, downstreamTypes) {
|
|
889
|
+
if (sectionIndex < changeIndex) {
|
|
890
|
+
return false;
|
|
891
|
+
}
|
|
892
|
+
const sectionKey = sectionTypeKeyFromSection(section);
|
|
893
|
+
if (!sectionKey || !downstreamTypes.includes(sectionKey)) {
|
|
894
|
+
return false;
|
|
895
|
+
}
|
|
896
|
+
if (sectionIndex === changeIndex && sectionKey === parentTypeKey) {
|
|
897
|
+
return false;
|
|
898
|
+
}
|
|
899
|
+
return section.items.some((item) => (item.itemIds?.length ?? 0) > 0);
|
|
900
|
+
}
|
|
901
|
+
function clearSectionItems(section) {
|
|
902
|
+
return {
|
|
903
|
+
...section,
|
|
904
|
+
items: section.items.map((item) => ({ ...item, itemIds: [], items: [] })),
|
|
905
|
+
};
|
|
906
|
+
}
|
|
907
|
+
/**
|
|
908
|
+
* When upstream hazard or hazard-risk selections change, clears item rows in later dependent sections.
|
|
909
|
+
*/
|
|
910
|
+
function clearDependentItemsOnParentChange(previous, next) {
|
|
911
|
+
if (!next.sections.length) {
|
|
912
|
+
return next;
|
|
913
|
+
}
|
|
914
|
+
let changeIndex = next.sections.length;
|
|
915
|
+
let hazardChanged = false;
|
|
916
|
+
let hazardRiskChanged = false;
|
|
917
|
+
for (let index = 0; index < next.sections.length; index += 1) {
|
|
918
|
+
const prevHazardSig = parentItemSignature(previous.sections, index, 'Hazard');
|
|
919
|
+
const nextHazardSig = parentItemSignature(next.sections, index, 'Hazard');
|
|
920
|
+
if (prevHazardSig !== nextHazardSig) {
|
|
921
|
+
changeIndex = Math.min(changeIndex, index);
|
|
922
|
+
hazardChanged = true;
|
|
923
|
+
}
|
|
924
|
+
const prevRiskSig = parentItemSignature(previous.sections, index, 'HazardRisk');
|
|
925
|
+
const nextRiskSig = parentItemSignature(next.sections, index, 'HazardRisk');
|
|
926
|
+
if (prevRiskSig !== nextRiskSig) {
|
|
927
|
+
changeIndex = Math.min(changeIndex, index);
|
|
928
|
+
hazardRiskChanged = true;
|
|
929
|
+
}
|
|
930
|
+
}
|
|
931
|
+
if (changeIndex === next.sections.length) {
|
|
932
|
+
return next;
|
|
933
|
+
}
|
|
934
|
+
return {
|
|
935
|
+
sections: next.sections.map((section, sectionIndex) => {
|
|
936
|
+
const clearFromHazard = hazardChanged &&
|
|
937
|
+
shouldClearSectionOnParentChange(section, sectionIndex, changeIndex, 'Hazard', WORKPLACE_SAFETY_DOWNSTREAM_FROM_HAZARD);
|
|
938
|
+
const clearFromHazardRisk = hazardRiskChanged &&
|
|
939
|
+
shouldClearSectionOnParentChange(section, sectionIndex, changeIndex, 'HazardRisk', WORKPLACE_SAFETY_DOWNSTREAM_FROM_HAZARD_RISK);
|
|
940
|
+
if (!clearFromHazard && !clearFromHazardRisk) {
|
|
941
|
+
return section;
|
|
942
|
+
}
|
|
943
|
+
return clearSectionItems(section);
|
|
944
|
+
}),
|
|
945
|
+
};
|
|
946
|
+
}
|
|
947
|
+
function extractRiskMatrixIdsFromHazardRisks(records) {
|
|
948
|
+
const ids = new Set();
|
|
949
|
+
for (const record of records) {
|
|
950
|
+
const id = record['riskMatrixId'];
|
|
951
|
+
if (typeof id === 'string' && id.length > 0) {
|
|
952
|
+
ids.add(id);
|
|
953
|
+
}
|
|
954
|
+
}
|
|
955
|
+
return [...ids];
|
|
956
|
+
}
|
|
957
|
+
function buildInFilter(field, values) {
|
|
958
|
+
const unique = [...new Set(values.filter((id) => id.length > 0))];
|
|
959
|
+
if (!unique.length) {
|
|
960
|
+
return null;
|
|
961
|
+
}
|
|
962
|
+
return {
|
|
963
|
+
field,
|
|
964
|
+
operator: { type: 'in' },
|
|
965
|
+
value: unique,
|
|
966
|
+
};
|
|
967
|
+
}
|
|
968
|
+
/** Filter that matches no records when parent context exists but derived IDs are empty. */
|
|
969
|
+
function buildEmptyResultFilter(field) {
|
|
970
|
+
return {
|
|
971
|
+
field,
|
|
972
|
+
operator: { type: 'in' },
|
|
973
|
+
value: ['00000000-0000-0000-0000-000000000000'],
|
|
974
|
+
};
|
|
975
|
+
}
|
|
976
|
+
function resolveSafetyPlanSectionItemFilter(sectionTypeId, parentHazardIds, parentHazardRiskIds = []) {
|
|
977
|
+
const sectionTypeKey = resolveSectionTypeKey(sectionTypeId);
|
|
978
|
+
const dependency = getSectionTypeDependency(sectionTypeKey);
|
|
979
|
+
if (!dependency || dependency.kind !== 'parentFilter') {
|
|
980
|
+
return null;
|
|
981
|
+
}
|
|
982
|
+
const parentIds = dependency.parentSectionTypeKey === 'Hazard' ? parentHazardIds : parentHazardRiskIds;
|
|
983
|
+
if (!parentIds.length) {
|
|
984
|
+
return null;
|
|
985
|
+
}
|
|
986
|
+
return buildInFilter(dependency.filterField, parentIds);
|
|
987
|
+
}
|
|
988
|
+
async function resolveSafetyPlanSectionItemFilterAsync(sectionTypeId, sections, sectionIndex, loadHazardRisks) {
|
|
989
|
+
const sectionTypeKey = resolveSectionTypeKey(sectionTypeId);
|
|
990
|
+
const dependency = getSectionTypeDependency(sectionTypeKey);
|
|
991
|
+
if (!dependency) {
|
|
992
|
+
return null;
|
|
993
|
+
}
|
|
994
|
+
if (dependency.kind === 'parentFilter') {
|
|
995
|
+
const parentIds = resolveParentItemIds(sections, sectionIndex, dependency.parentSectionTypeKey);
|
|
996
|
+
if (!parentIds.length) {
|
|
997
|
+
return null;
|
|
998
|
+
}
|
|
999
|
+
return buildInFilter(dependency.filterField, parentIds);
|
|
1000
|
+
}
|
|
1001
|
+
const parentIds = resolveParentItemIds(sections, sectionIndex, dependency.parentSectionTypeKey);
|
|
1002
|
+
if (!parentIds.length) {
|
|
1003
|
+
return null;
|
|
1004
|
+
}
|
|
1005
|
+
const records = await loadHazardRisks(parentIds);
|
|
1006
|
+
const derivedIds = extractRiskMatrixIdsFromHazardRisks(records);
|
|
1007
|
+
if (!derivedIds.length) {
|
|
1008
|
+
return buildEmptyResultFilter(dependency.filterField);
|
|
1009
|
+
}
|
|
1010
|
+
return buildInFilter(dependency.filterField, derivedIds);
|
|
1011
|
+
}
|
|
1012
|
+
const defaultItemIdsPlaceholderKey = '@workplacesafety-management:safety-plan-sections.entities.safety-plan-section.fields.item-ids.placeholder';
|
|
1013
|
+
/** Clears item selection when section type changes in item dialog context. */
|
|
1014
|
+
const WORKPLACE_SAFETY_SECTION_ITEM_LOOKUP_CLEAR_ON_SECTION_TYPE_TRIGGERS = [
|
|
1015
|
+
{
|
|
1016
|
+
event: "{{ events.context('sectionType.id') }}",
|
|
1017
|
+
action: "{{ widget.setValue([]); context.set('items', []); }}",
|
|
1018
|
+
},
|
|
1019
|
+
{
|
|
1020
|
+
event: "{{ events.context('sectionTypeId') }}",
|
|
1021
|
+
action: "{{ widget.setValue([]); context.set('items', []); }}",
|
|
1022
|
+
},
|
|
1023
|
+
];
|
|
1024
|
+
/**
|
|
1025
|
+
* Reloads lookup data after section type is resolved (`sectionType.id`, `itemEntityName`).
|
|
1026
|
+
*/
|
|
1027
|
+
const WORKPLACE_SAFETY_SECTION_ITEM_LOOKUP_REFRESH_ON_SECTION_TYPE_EXPOSE_TRIGGERS = [
|
|
1028
|
+
{
|
|
1029
|
+
event: "{{ events.context('sectionType.id') }}",
|
|
1030
|
+
action: '{{ widget.refresh(); }}',
|
|
1031
|
+
},
|
|
1032
|
+
{
|
|
1033
|
+
event: "{{ events.context('sectionType.itemEntityName') }}",
|
|
1034
|
+
action: '{{ widget.refresh(); }}',
|
|
1035
|
+
},
|
|
1036
|
+
{
|
|
1037
|
+
event: "{{ events.context('sectionTypeId') }}",
|
|
1038
|
+
action: '{{ widget.refresh(); }}',
|
|
1039
|
+
},
|
|
1040
|
+
];
|
|
1041
|
+
const WORKPLACE_SAFETY_SECTION_ITEM_LOOKUP_SECTION_TYPE_TRIGGERS = [
|
|
1042
|
+
...WORKPLACE_SAFETY_SECTION_ITEM_LOOKUP_CLEAR_ON_SECTION_TYPE_TRIGGERS,
|
|
1043
|
+
...WORKPLACE_SAFETY_SECTION_ITEM_LOOKUP_REFRESH_ON_SECTION_TYPE_EXPOSE_TRIGGERS,
|
|
1044
|
+
];
|
|
1045
|
+
function isPpeRuleSectionType(sectionTypeId) {
|
|
1046
|
+
return (sectionTypeId === WORKPLACE_SAFETY_BUILT_IN_SECTION_TYPE_IDS.ppeHazards ||
|
|
1047
|
+
sectionTypeId === WORKPLACE_SAFETY_BUILT_IN_SECTION_TYPE_IDS.hazardRiskPpe);
|
|
1048
|
+
}
|
|
1049
|
+
function buildSectionTypeItemTextFieldExpression(sectionTypeIdPath) {
|
|
1050
|
+
const ppeHazardsId = WORKPLACE_SAFETY_BUILT_IN_SECTION_TYPE_IDS.ppeHazards;
|
|
1051
|
+
const hazardRiskPpeId = WORKPLACE_SAFETY_BUILT_IN_SECTION_TYPE_IDS.hazardRiskPpe;
|
|
1052
|
+
return `{{ [ '${ppeHazardsId}', '${hazardRiskPpeId}' ].includes(context.eval('${sectionTypeIdPath}')) ? 'ppeItem.title' : 'title' }}`;
|
|
1053
|
+
}
|
|
1054
|
+
function buildSectionTypeItemColumnsExpression(sectionTypeIdPath) {
|
|
1055
|
+
const ppeHazardsId = WORKPLACE_SAFETY_BUILT_IN_SECTION_TYPE_IDS.ppeHazards;
|
|
1056
|
+
const hazardRiskPpeId = WORKPLACE_SAFETY_BUILT_IN_SECTION_TYPE_IDS.hazardRiskPpe;
|
|
1057
|
+
const riskMatrixId = WORKPLACE_SAFETY_BUILT_IN_SECTION_TYPE_IDS.riskMatrix;
|
|
1058
|
+
return `{{ context.eval('${sectionTypeIdPath}') === '${riskMatrixId}' ? ['code', 'title', 'matrix'] : [ '${ppeHazardsId}', '${hazardRiskPpeId}' ].includes(context.eval('${sectionTypeIdPath}')) ? ['code', 'ppeItem.title', 'requirementType'] : ['code', 'title'] }}`;
|
|
1059
|
+
}
|
|
1060
|
+
function buildSectionTypeDependentParentFilter(sectionTypeIdPath, parentIdsContextKey, parentSectionTypeKey) {
|
|
1061
|
+
const dependentSectionTypeIds = Object.entries(WORKPLACE_SAFETY_SECTION_TYPE_DEPENDENCIES)
|
|
1062
|
+
.filter(([, config]) => config.kind === 'parentFilter' && config.parentSectionTypeKey === parentSectionTypeKey)
|
|
1063
|
+
.map(([key]) => {
|
|
1064
|
+
const id = Object.entries(WORKPLACE_SAFETY_BUILT_IN_SECTION_TYPE_BY_ID).find(([, meta]) => meta.sectionTypeKey === key)?.[0];
|
|
1065
|
+
return id ? `'${id}'` : undefined;
|
|
1066
|
+
})
|
|
1067
|
+
.filter((id) => Boolean(id))
|
|
1068
|
+
.join(',');
|
|
1069
|
+
return {
|
|
1070
|
+
field: parentSectionTypeKey === 'Hazard' ? 'hazardId' : 'hazardRiskId',
|
|
1071
|
+
operator: { type: 'in' },
|
|
1072
|
+
value: `{{ [${dependentSectionTypeIds}].includes(context.eval('${sectionTypeIdPath}')) ? ((context.eval('${parentIdsContextKey}') || []).length ? context.eval('${parentIdsContextKey}') : undefined) : undefined }}`,
|
|
1073
|
+
};
|
|
1074
|
+
}
|
|
1075
|
+
function buildSectionTypeDependentHazardFilter(sectionTypeIdPath, parentHazardIdsContextKey) {
|
|
1076
|
+
return buildSectionTypeDependentParentFilter(sectionTypeIdPath, parentHazardIdsContextKey, 'Hazard');
|
|
1077
|
+
}
|
|
1078
|
+
const sectionItemLookupExpose = [
|
|
1079
|
+
{ source: 'id', target: 'items.{id}' },
|
|
1080
|
+
{ source: 'title', target: 'items.{title}' },
|
|
1081
|
+
{ source: 'code', target: 'items.{code}' },
|
|
1082
|
+
{ source: 'ppeItem.id', target: 'items.{id}' },
|
|
1083
|
+
{ source: 'ppeItem.title', target: 'items.{title}' },
|
|
1084
|
+
{ source: 'ppeItem.code', target: 'items.{code}' },
|
|
1085
|
+
];
|
|
1086
|
+
/** Shared item lookup options for builder item dialog (`sectionType.*` in dialog context). */
|
|
1087
|
+
function buildSafetyPlanSectionItemLookupOptions(entityExpression, options) {
|
|
1088
|
+
const sectionTypeIdPath = options?.sectionTypeIdPath ?? 'sectionType.id';
|
|
1089
|
+
const lookup = {
|
|
1090
|
+
entity: entityExpression,
|
|
1091
|
+
look: 'lookup',
|
|
1092
|
+
multiple: true,
|
|
1093
|
+
allowSearch: true,
|
|
1094
|
+
textField: buildSectionTypeItemTextFieldExpression(sectionTypeIdPath),
|
|
1095
|
+
columns: buildSectionTypeItemColumnsExpression(sectionTypeIdPath),
|
|
1096
|
+
placeholder: options?.defaultPlaceholderKey ?? defaultItemIdsPlaceholderKey,
|
|
1097
|
+
expose: sectionItemLookupExpose,
|
|
1098
|
+
triggers: WORKPLACE_SAFETY_SECTION_ITEM_LOOKUP_SECTION_TYPE_TRIGGERS,
|
|
1099
|
+
};
|
|
1100
|
+
const filters = [];
|
|
1101
|
+
if (options?.parentHazardIdsContextKey) {
|
|
1102
|
+
filters.push(buildSectionTypeDependentHazardFilter(sectionTypeIdPath, options.parentHazardIdsContextKey));
|
|
1103
|
+
}
|
|
1104
|
+
if (options?.parentHazardRiskIdsContextKey) {
|
|
1105
|
+
filters.push(buildSectionTypeDependentParentFilter(sectionTypeIdPath, options.parentHazardRiskIdsContextKey, 'HazardRisk'));
|
|
1106
|
+
}
|
|
1107
|
+
if (filters.length === 1) {
|
|
1108
|
+
lookup['filter'] = filters[0];
|
|
1109
|
+
}
|
|
1110
|
+
else if (filters.length > 1) {
|
|
1111
|
+
lookup['filter'] = { logic: 'and', filters };
|
|
1112
|
+
}
|
|
1113
|
+
return lookup;
|
|
1114
|
+
}
|
|
1115
|
+
/** Lookup options with entity data-selector popup UI (`look: 'lookup'`). */
|
|
1116
|
+
function buildSafetyPlanSectionItemSelectorOptions(entityExpression, options) {
|
|
1117
|
+
return {
|
|
1118
|
+
...buildSafetyPlanSectionItemLookupOptions(entityExpression, options),
|
|
1119
|
+
look: 'lookup',
|
|
1120
|
+
};
|
|
1121
|
+
}
|
|
1122
|
+
function resolveSafetyPlanSectionItemColumns(sectionTypeId) {
|
|
1123
|
+
if (sectionTypeId === WORKPLACE_SAFETY_BUILT_IN_SECTION_TYPE_IDS.ppeHazards) {
|
|
1124
|
+
return ['code', 'ppeItem.title', 'requirementType'];
|
|
1125
|
+
}
|
|
1126
|
+
if (sectionTypeId === WORKPLACE_SAFETY_BUILT_IN_SECTION_TYPE_IDS.hazardRiskPpe) {
|
|
1127
|
+
return ['code', 'hazardRisk.title', 'ppeItem.title', 'requirementType'];
|
|
1128
|
+
}
|
|
1129
|
+
if (sectionTypeId === WORKPLACE_SAFETY_BUILT_IN_SECTION_TYPE_IDS.riskMatrix) {
|
|
1130
|
+
return [
|
|
1131
|
+
'code',
|
|
1132
|
+
'title',
|
|
1133
|
+
'matrix',
|
|
1134
|
+
{ name: 'likelihoodIds', visible: false },
|
|
1135
|
+
{ name: 'severityIds', visible: false },
|
|
1136
|
+
{ name: 'likelihoodId', visible: false },
|
|
1137
|
+
{ name: 'severityId', visible: false },
|
|
1138
|
+
{ name: 'matrixOrientation', visible: false },
|
|
1139
|
+
];
|
|
1140
|
+
}
|
|
1141
|
+
return ['code', 'title'];
|
|
1142
|
+
}
|
|
1143
|
+
function resolveSafetyPlanSectionItemEntityKey(sectionTypeId, sectionType) {
|
|
1144
|
+
return resolveSectionItemEntityName(sectionTypeId, sectionType);
|
|
1145
|
+
}
|
|
1146
|
+
function mapSafetyPlanSectionSelectedItems(sectionTypeId, selectedItems) {
|
|
1147
|
+
const isPpeHazards = sectionTypeId === WORKPLACE_SAFETY_BUILT_IN_SECTION_TYPE_IDS.ppeHazards;
|
|
1148
|
+
const isHazardRiskPpe = sectionTypeId === WORKPLACE_SAFETY_BUILT_IN_SECTION_TYPE_IDS.hazardRiskPpe;
|
|
1149
|
+
return selectedItems
|
|
1150
|
+
.map((item) => {
|
|
1151
|
+
if (isPpeHazards || isHazardRiskPpe) {
|
|
1152
|
+
const ppeItem = item['ppeItem'];
|
|
1153
|
+
const id = String(ppeItem?.['id'] ?? item['id'] ?? '');
|
|
1154
|
+
return {
|
|
1155
|
+
id,
|
|
1156
|
+
title: asSafetyPlanDisplayText(ppeItem?.['title']) || undefined,
|
|
1157
|
+
code: asSafetyPlanDisplayText(ppeItem?.['code']) || undefined,
|
|
1158
|
+
};
|
|
1159
|
+
}
|
|
1160
|
+
return {
|
|
1161
|
+
id: String(item['id'] ?? ''),
|
|
1162
|
+
title: asSafetyPlanDisplayText(item['title']) || undefined,
|
|
1163
|
+
code: asSafetyPlanDisplayText(item['code']) || undefined,
|
|
1164
|
+
};
|
|
1165
|
+
})
|
|
1166
|
+
.filter((item) => item.id.length > 0);
|
|
1167
|
+
}
|
|
1168
|
+
function dedupeSafetyPlanSectionItemSelection(item) {
|
|
1169
|
+
const seen = new Set();
|
|
1170
|
+
const itemIds = [];
|
|
1171
|
+
const items = [];
|
|
1172
|
+
for (let index = 0; index < (item.itemIds?.length ?? 0); index += 1) {
|
|
1173
|
+
const id = item.itemIds?.[index];
|
|
1174
|
+
if (typeof id !== 'string' || !id || seen.has(id)) {
|
|
1175
|
+
continue;
|
|
1176
|
+
}
|
|
1177
|
+
seen.add(id);
|
|
1178
|
+
itemIds.push(id);
|
|
1179
|
+
const snapshot = item.items?.find((entry) => entry?.id === id) ??
|
|
1180
|
+
(item.items?.[index]?.id === id || !item.items?.[index]?.id ? item.items?.[index] : undefined);
|
|
1181
|
+
items.push({
|
|
1182
|
+
id,
|
|
1183
|
+
title: asSafetyPlanDisplayText(snapshot?.title) || undefined,
|
|
1184
|
+
code: asSafetyPlanDisplayText(snapshot?.code) || undefined,
|
|
1185
|
+
});
|
|
1186
|
+
}
|
|
1187
|
+
return { ...item, itemIds, items };
|
|
1188
|
+
}
|
|
1189
|
+
function isPpeRuleSectionTypeId(sectionTypeId) {
|
|
1190
|
+
return isPpeRuleSectionType(sectionTypeId);
|
|
1191
|
+
}
|
|
1192
|
+
//#endregion
|
|
1193
|
+
|
|
1194
|
+
//#region ---- Imports ----
|
|
1195
|
+
//#endregion
|
|
1196
|
+
//#region ---- Constants ----
|
|
1197
|
+
const listSortAndFilterOptions = {
|
|
1198
|
+
sort: { enabled: true },
|
|
1199
|
+
filter: { advance: { enabled: true }, inline: { enabled: true } },
|
|
1200
|
+
};
|
|
1201
|
+
/**
|
|
1202
|
+
* Section-type picker for activity item rows — no title/description sync (those would
|
|
1203
|
+
* write onto the parent SafetyPlanActivity fields), and Activity itself is excluded.
|
|
1204
|
+
* Keep `expose` so `sectionType.id` updates and itemIds cascade can react.
|
|
1205
|
+
*/
|
|
1206
|
+
const ACTIVITY_ITEMS_SECTION_TYPE_LOOKUP_OPTIONS = {
|
|
1207
|
+
...WORKPLACE_SAFETY_SECTION_TYPE_LOOKUP_WIDGET_OPTIONS,
|
|
1208
|
+
triggers: [],
|
|
1209
|
+
filter: {
|
|
1210
|
+
logic: 'and',
|
|
1211
|
+
filters: [
|
|
1212
|
+
{
|
|
1213
|
+
field: 'id',
|
|
1214
|
+
operator: { type: 'notEqual' },
|
|
1215
|
+
value: WORKPLACE_SAFETY_BUILT_IN_SECTION_TYPE_IDS.activity,
|
|
1216
|
+
},
|
|
1217
|
+
],
|
|
1218
|
+
},
|
|
1219
|
+
};
|
|
1220
|
+
/**
|
|
1221
|
+
* Bound sectionTypeId on the repeater row (parent of the itemIds lookup).
|
|
1222
|
+
* `>` is required at eval time for the correct row value.
|
|
1223
|
+
*/
|
|
1224
|
+
const ACTIVITY_ITEM_SECTION_TYPE_ID_PATH = '>sectionTypeId';
|
|
1225
|
+
/** Maps row sectionTypeId → catalog entity (Hazard, Ppe, Control, …). */
|
|
1226
|
+
const ACTIVITY_ITEM_ENTITY_EXPRESSION = buildWorkplaceSafetySectionTypeEntityExpression(ACTIVITY_ITEM_SECTION_TYPE_ID_PATH);
|
|
1227
|
+
/**
|
|
1228
|
+
* Enable itemIds once the row has a section type.
|
|
1229
|
+
*
|
|
1230
|
+
* `>sectionTypeId` alone does not re-trigger option updates for nested paths like
|
|
1231
|
+
* `items.[n].sectionTypeId` (renderer dependency matching ignores the `>` prefix).
|
|
1232
|
+
* Including bare `sectionTypeId` makes `isSegmentSuffix` treat the nested change as
|
|
1233
|
+
* relevant so disabled/entity expressions re-evaluate after selection.
|
|
1234
|
+
*/
|
|
1235
|
+
const ACTIVITY_ITEM_IDS_DISABLED_EXPRESSION = "{{ !(context.eval('>sectionTypeId') || context.eval('sectionTypeId')) }}";
|
|
1236
|
+
/**
|
|
1237
|
+
* Activity-only cascade: clear selection + refresh entity/data when section type changes.
|
|
1238
|
+
* Do not use the shared section triggers — they call `context.set('items', [])`, which
|
|
1239
|
+
* would wipe this entity's `items` repeater (same key as the lookup expose parent).
|
|
1240
|
+
*/
|
|
1241
|
+
const ACTIVITY_ITEM_IDS_SECTION_TYPE_TRIGGERS = [
|
|
1242
|
+
{
|
|
1243
|
+
event: "{{ events.context('sectionType.id') }}",
|
|
1244
|
+
action: '{{ widget.setValue([]); widget.refresh(); }}',
|
|
1245
|
+
},
|
|
1246
|
+
{
|
|
1247
|
+
event: "{{ events.context('sectionTypeId') }}",
|
|
1248
|
+
action: '{{ widget.setValue([]); widget.refresh(); }}',
|
|
1249
|
+
},
|
|
1250
|
+
];
|
|
1251
|
+
/** Same repeater widget tree as {@link safetyPlanSectionEntityFactory} `items`. */
|
|
1252
|
+
const ITEMS_REPEATER_CHILDREN = [
|
|
1253
|
+
{
|
|
1254
|
+
type: AXPWidgetsList.Layouts.GridLayout,
|
|
1255
|
+
mode: 'edit',
|
|
1256
|
+
name: 'itemRow',
|
|
1257
|
+
options: {
|
|
1258
|
+
grid: {
|
|
1259
|
+
default: {
|
|
1260
|
+
columns: 12,
|
|
1261
|
+
gap: '1rem',
|
|
1262
|
+
},
|
|
1263
|
+
},
|
|
1264
|
+
},
|
|
1265
|
+
children: [
|
|
1266
|
+
{
|
|
1267
|
+
type: AXPWidgetsList.Layouts.GridItemLayout,
|
|
1268
|
+
mode: 'edit',
|
|
1269
|
+
options: { colSpan: 4 },
|
|
1270
|
+
children: [
|
|
1271
|
+
{
|
|
1272
|
+
type: AXPWidgetsList.Layouts.FormField,
|
|
1273
|
+
mode: 'edit',
|
|
1274
|
+
options: {
|
|
1275
|
+
label: '@workplacesafety-management:safety-plan-sections.entities.safety-plan-section.fields.section-type.title',
|
|
1276
|
+
showLabel: true,
|
|
1277
|
+
},
|
|
1278
|
+
children: [
|
|
1279
|
+
{
|
|
1280
|
+
type: AXPWidgetsList.Editors.LookupBox,
|
|
1281
|
+
mode: 'edit',
|
|
1282
|
+
path: 'sectionTypeId',
|
|
1283
|
+
name: 'sectionTypeId',
|
|
1284
|
+
options: {
|
|
1285
|
+
entity: `${RootConfig.module.name}.${RootConfig.entities.sectionType.name}`,
|
|
1286
|
+
...ACTIVITY_ITEMS_SECTION_TYPE_LOOKUP_OPTIONS,
|
|
1287
|
+
},
|
|
1288
|
+
},
|
|
1289
|
+
],
|
|
1290
|
+
},
|
|
1291
|
+
],
|
|
1292
|
+
},
|
|
1293
|
+
{
|
|
1294
|
+
type: AXPWidgetsList.Layouts.GridItemLayout,
|
|
1295
|
+
mode: 'edit',
|
|
1296
|
+
options: { colSpan: 8 },
|
|
1297
|
+
children: [
|
|
1298
|
+
{
|
|
1299
|
+
type: AXPWidgetsList.Layouts.FormField,
|
|
1300
|
+
mode: 'edit',
|
|
1301
|
+
options: {
|
|
1302
|
+
label: '@workplacesafety-management:safety-plan-sections.entities.safety-plan-section.fields.item-ids.title',
|
|
1303
|
+
showLabel: true,
|
|
1304
|
+
},
|
|
1305
|
+
children: [
|
|
1306
|
+
{
|
|
1307
|
+
type: AXPWidgetsList.Editors.LookupBox,
|
|
1308
|
+
mode: 'edit',
|
|
1309
|
+
path: 'itemIds',
|
|
1310
|
+
name: 'itemIds',
|
|
1311
|
+
triggers: ACTIVITY_ITEM_IDS_SECTION_TYPE_TRIGGERS,
|
|
1312
|
+
options: {
|
|
1313
|
+
...buildSafetyPlanSectionItemSelectorOptions(ACTIVITY_ITEM_ENTITY_EXPRESSION, {
|
|
1314
|
+
sectionTypeIdPath: ACTIVITY_ITEM_SECTION_TYPE_ID_PATH,
|
|
1315
|
+
}),
|
|
1316
|
+
// Shared options expose into `items.*` — that key is this entity's repeater.
|
|
1317
|
+
expose: [],
|
|
1318
|
+
triggers: ACTIVITY_ITEM_IDS_SECTION_TYPE_TRIGGERS,
|
|
1319
|
+
valueField: 'id',
|
|
1320
|
+
disabled: ACTIVITY_ITEM_IDS_DISABLED_EXPRESSION,
|
|
1321
|
+
},
|
|
1322
|
+
},
|
|
1323
|
+
],
|
|
1324
|
+
},
|
|
1325
|
+
],
|
|
1326
|
+
},
|
|
1327
|
+
],
|
|
1328
|
+
},
|
|
1329
|
+
];
|
|
1330
|
+
const itemsPropertyLayout = {
|
|
1331
|
+
name: 'items',
|
|
1332
|
+
layout: { label: { visible: false }, order: 1, positions: { lg: { colSpan: 12 } } },
|
|
1333
|
+
};
|
|
1334
|
+
//#endregion
|
|
1335
|
+
//#region ---- Entity factory ----
|
|
1336
|
+
async function safetyPlanActivityEntityFactory() {
|
|
1337
|
+
const entityDef = {
|
|
1338
|
+
module: RootConfig.module.name,
|
|
1339
|
+
name: RootConfig.entities.safetyPlanActivity.name,
|
|
1340
|
+
title: RootConfig.entities.safetyPlanActivity.title,
|
|
1341
|
+
icon: RootConfig.entities.safetyPlanActivity.icon,
|
|
1342
|
+
searchTags: [
|
|
1343
|
+
'safety-plan-activity',
|
|
1344
|
+
'safety plan activity',
|
|
1345
|
+
'activity',
|
|
1346
|
+
'work activity',
|
|
1347
|
+
'plan activity',
|
|
1348
|
+
'فعالیت برنامه ایمنی',
|
|
1349
|
+
'فعالیت',
|
|
1350
|
+
'برنامه ایمنی',
|
|
1351
|
+
'ایمنی محیط کار',
|
|
1352
|
+
],
|
|
1353
|
+
formats: {
|
|
1354
|
+
individual: RootConfig.entities.safetyPlanActivity.title,
|
|
1355
|
+
plural: RootConfig.entities.safetyPlanActivity.titlePlural,
|
|
1356
|
+
searchResult: {
|
|
1357
|
+
title: '{{ code }}',
|
|
1358
|
+
description: '{{ description }}',
|
|
1359
|
+
},
|
|
1360
|
+
},
|
|
1361
|
+
plugins: [
|
|
1362
|
+
{ name: 'identifier', options: { field: 'code', rule: 'safety-plan-activity-code' } },
|
|
1363
|
+
{ name: 'unique-fields', options: { fields: 'code', scope: 'global' } },
|
|
1364
|
+
{ name: 'archive' },
|
|
1365
|
+
{
|
|
1366
|
+
name: 'attachments',
|
|
1367
|
+
options: {
|
|
1368
|
+
accept: '.jpg,.jpeg,.png,.pdf,.doc,.docx',
|
|
1369
|
+
fileEditable: true,
|
|
1370
|
+
display: 'page',
|
|
1371
|
+
},
|
|
1372
|
+
},
|
|
1373
|
+
],
|
|
1374
|
+
relatedEntities: [],
|
|
1375
|
+
groups: [
|
|
1376
|
+
{
|
|
1377
|
+
id: 'basic-info',
|
|
1378
|
+
title: '@workplacesafety-management:safety-plan-activities.entities.safety-plan-activity.groups.basic-info',
|
|
1379
|
+
},
|
|
1380
|
+
{
|
|
1381
|
+
id: 'items',
|
|
1382
|
+
title: '@workplacesafety-management:safety-plan-activities.entities.safety-plan-activity.groups.items',
|
|
1383
|
+
},
|
|
1384
|
+
],
|
|
1385
|
+
properties: [
|
|
1386
|
+
{
|
|
1387
|
+
name: 'id',
|
|
1388
|
+
title: '@general:terms.common.id',
|
|
1389
|
+
groupId: 'basic-info',
|
|
1390
|
+
schema: { dataType: 'uuid', visible: false, nullable: false, readonly: true, unique: { enabled: true } },
|
|
1391
|
+
},
|
|
1392
|
+
{
|
|
1393
|
+
name: 'code',
|
|
1394
|
+
title: '@workplacesafety-management:safety-plan-activities.entities.safety-plan-activity.fields.code.title',
|
|
1395
|
+
groupId: 'basic-info',
|
|
1396
|
+
options: listSortAndFilterOptions,
|
|
1397
|
+
schema: {
|
|
1398
|
+
dataType: 'string',
|
|
1399
|
+
unique: { enabled: true },
|
|
1400
|
+
interface: {
|
|
1401
|
+
type: AXPWidgetsList.Editors.TextBox,
|
|
1402
|
+
options: {
|
|
1403
|
+
disabled: true,
|
|
1404
|
+
placeholder: '@workplacesafety-management:safety-plan-activities.entities.safety-plan-activity.fields.code.placeholder',
|
|
1405
|
+
},
|
|
1406
|
+
},
|
|
1407
|
+
},
|
|
1408
|
+
validations: [{ rule: 'required' }],
|
|
1409
|
+
},
|
|
1410
|
+
{
|
|
1411
|
+
name: 'title',
|
|
1412
|
+
title: '@workplacesafety-management:safety-plan-activities.entities.safety-plan-activity.fields.title.title',
|
|
1413
|
+
groupId: 'basic-info',
|
|
1414
|
+
options: listSortAndFilterOptions,
|
|
1415
|
+
schema: {
|
|
1416
|
+
dataType: 'string',
|
|
1417
|
+
interface: {
|
|
1418
|
+
type: AXPWidgetsList.Editors.TextBox,
|
|
1419
|
+
options: {
|
|
1420
|
+
placeholder: '@workplacesafety-management:safety-plan-activities.entities.safety-plan-activity.fields.title.placeholder',
|
|
1421
|
+
},
|
|
1422
|
+
},
|
|
1423
|
+
},
|
|
1424
|
+
validations: [{ rule: 'required' }],
|
|
1425
|
+
},
|
|
1426
|
+
{
|
|
1427
|
+
name: 'description',
|
|
1428
|
+
title: '@workplacesafety-management:safety-plan-activities.entities.safety-plan-activity.fields.description.title',
|
|
1429
|
+
groupId: 'basic-info',
|
|
1430
|
+
options: {
|
|
1431
|
+
sort: { enabled: false },
|
|
1432
|
+
filter: { advance: { enabled: true }, inline: { enabled: false } },
|
|
1433
|
+
},
|
|
1434
|
+
schema: {
|
|
1435
|
+
dataType: 'string',
|
|
1436
|
+
interface: {
|
|
1437
|
+
type: AXPWidgetsList.Editors.LargeTextBox,
|
|
1438
|
+
options: {
|
|
1439
|
+
rows: 2,
|
|
1440
|
+
placeholder: '@workplacesafety-management:safety-plan-activities.entities.safety-plan-activity.fields.description.placeholder',
|
|
1441
|
+
},
|
|
1442
|
+
},
|
|
1443
|
+
},
|
|
1444
|
+
},
|
|
1445
|
+
{
|
|
1446
|
+
name: 'items',
|
|
1447
|
+
title: '',
|
|
1448
|
+
groupId: 'items',
|
|
1449
|
+
schema: {
|
|
1450
|
+
defaultValue: [],
|
|
1451
|
+
dataType: 'array',
|
|
1452
|
+
nullable: true,
|
|
1453
|
+
interface: {
|
|
1454
|
+
type: AXPWidgetsList.Layouts.RepeaterLayout,
|
|
1455
|
+
options: { hasControls: true, allowReorder: true },
|
|
1456
|
+
children: [...ITEMS_REPEATER_CHILDREN],
|
|
1457
|
+
},
|
|
1458
|
+
},
|
|
1459
|
+
},
|
|
1460
|
+
],
|
|
1461
|
+
columns: [
|
|
1462
|
+
{ name: 'code', options: { width: '120px', order: 1 } },
|
|
1463
|
+
{ name: 'title', options: { width: '200px', order: 2 } },
|
|
1464
|
+
{ name: 'description', options: { width: '280px', order: 3 } },
|
|
1465
|
+
],
|
|
1466
|
+
interfaces: {
|
|
1467
|
+
master: {
|
|
1468
|
+
create: {
|
|
1469
|
+
sections: [{ id: 'basic-info' }, { id: 'items' }],
|
|
1470
|
+
properties: [
|
|
1471
|
+
{ name: 'code', layout: { positions: { lg: { colSpan: 6, order: 1 } } } },
|
|
1472
|
+
{ name: 'title', layout: { positions: { lg: { colSpan: 12, order: 2 } } } },
|
|
1473
|
+
{ name: 'description', layout: { positions: { lg: { colSpan: 12, order: 3 } } } },
|
|
1474
|
+
itemsPropertyLayout,
|
|
1475
|
+
],
|
|
1476
|
+
},
|
|
1477
|
+
update: {
|
|
1478
|
+
sections: [{ id: 'basic-info' }, { id: 'items' }],
|
|
1479
|
+
properties: [
|
|
1480
|
+
{ name: 'code', layout: { positions: { lg: { colSpan: 6, order: 1 } } } },
|
|
1481
|
+
{ name: 'title', layout: { positions: { lg: { colSpan: 12, order: 2 } } } },
|
|
1482
|
+
{ name: 'description', layout: { positions: { lg: { colSpan: 12, order: 3 } } } },
|
|
1483
|
+
itemsPropertyLayout,
|
|
1484
|
+
],
|
|
1485
|
+
},
|
|
1486
|
+
single: {
|
|
1487
|
+
title: '{{ title }}',
|
|
1488
|
+
sections: [
|
|
1489
|
+
{ id: 'basic-info', layout: { positions: { lg: { colSpan: 12 } } } },
|
|
1490
|
+
{ id: 'items', layout: { positions: { lg: { colSpan: 12 } } } },
|
|
1491
|
+
],
|
|
1492
|
+
properties: [
|
|
1493
|
+
{ name: 'code', layout: { positions: { lg: { colSpan: 6, order: 1 } } } },
|
|
1494
|
+
{ name: 'title', layout: { positions: { lg: { colSpan: 12, order: 2 } } } },
|
|
1495
|
+
{ name: 'description', layout: { positions: { lg: { colSpan: 12, order: 3 } } } },
|
|
1496
|
+
itemsPropertyLayout,
|
|
1497
|
+
],
|
|
1498
|
+
actions: [...entityMasterRecordActions()],
|
|
1499
|
+
},
|
|
1500
|
+
list: {
|
|
1501
|
+
actions: [...entityMasterCrudActions()],
|
|
1502
|
+
views: [createAllQueryView({ sorts: [{ name: 'code', dir: 'asc' }] })],
|
|
1503
|
+
},
|
|
1504
|
+
},
|
|
1505
|
+
},
|
|
1506
|
+
};
|
|
1507
|
+
return entityDef;
|
|
1508
|
+
}
|
|
1509
|
+
//#endregion
|
|
1510
|
+
|
|
1511
|
+
/**
|
|
1512
|
+
* Generated bundle index. Do not edit.
|
|
1513
|
+
*/
|
|
1514
|
+
|
|
1515
|
+
export { safetyPlanActivityEntityFactory };
|
|
1516
|
+
//# sourceMappingURL=acorex-platform-module-workplacesafety-management-client-contracts-entities-safety-plan-activity.mjs.map
|