clarion-shared-types 1.0.118 → 1.0.120

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.
Files changed (56) hide show
  1. package/dist/cjs/index.js +1 -0
  2. package/dist/cjs/index.js.map +1 -1
  3. package/dist/cjs/modules/dispatch/index.js +1 -0
  4. package/dist/cjs/modules/dispatch/index.js.map +1 -1
  5. package/dist/cjs/modules/dispatch/interfaces/dispatch-analytics.js +3 -0
  6. package/dist/cjs/modules/dispatch/interfaces/dispatch-analytics.js.map +1 -0
  7. package/dist/cjs/modules/permission/enums/permission-actions.enum.js +10 -0
  8. package/dist/cjs/modules/permission/enums/permission-actions.enum.js.map +1 -1
  9. package/dist/cjs/modules/permission/enums/permissions-moudule.enum.js +1 -0
  10. package/dist/cjs/modules/permission/enums/permissions-moudule.enum.js.map +1 -1
  11. package/dist/cjs/modules/responder/enums/responder-enums.js +83 -0
  12. package/dist/cjs/modules/responder/enums/responder-enums.js.map +1 -0
  13. package/dist/cjs/modules/responder/index.js +23 -0
  14. package/dist/cjs/modules/responder/index.js.map +1 -0
  15. package/dist/cjs/modules/responder/interfaces/responder.dto.js +3 -0
  16. package/dist/cjs/modules/responder/interfaces/responder.dto.js.map +1 -0
  17. package/dist/cjs/modules/responder/utils/responder-display.util.js +102 -0
  18. package/dist/cjs/modules/responder/utils/responder-display.util.js.map +1 -0
  19. package/dist/cjs/modules/tracking/index.js +1 -0
  20. package/dist/cjs/modules/tracking/index.js.map +1 -1
  21. package/dist/cjs/modules/tracking/interfaces/tracking-analytics.js +14 -0
  22. package/dist/cjs/modules/tracking/interfaces/tracking-analytics.js.map +1 -0
  23. package/dist/esm/index.js +1 -0
  24. package/dist/esm/index.js.map +1 -1
  25. package/dist/esm/modules/dispatch/index.js +1 -0
  26. package/dist/esm/modules/dispatch/index.js.map +1 -1
  27. package/dist/esm/modules/dispatch/interfaces/dispatch-analytics.js +2 -0
  28. package/dist/esm/modules/dispatch/interfaces/dispatch-analytics.js.map +1 -0
  29. package/dist/esm/modules/permission/enums/permission-actions.enum.js +10 -0
  30. package/dist/esm/modules/permission/enums/permission-actions.enum.js.map +1 -1
  31. package/dist/esm/modules/permission/enums/permissions-moudule.enum.js +1 -0
  32. package/dist/esm/modules/permission/enums/permissions-moudule.enum.js.map +1 -1
  33. package/dist/esm/modules/responder/enums/responder-enums.js +80 -0
  34. package/dist/esm/modules/responder/enums/responder-enums.js.map +1 -0
  35. package/dist/esm/modules/responder/index.js +7 -0
  36. package/dist/esm/modules/responder/index.js.map +1 -0
  37. package/dist/esm/modules/responder/interfaces/responder.dto.js +2 -0
  38. package/dist/esm/modules/responder/interfaces/responder.dto.js.map +1 -0
  39. package/dist/esm/modules/responder/utils/responder-display.util.js +92 -0
  40. package/dist/esm/modules/responder/utils/responder-display.util.js.map +1 -0
  41. package/dist/esm/modules/tracking/index.js +1 -0
  42. package/dist/esm/modules/tracking/index.js.map +1 -1
  43. package/dist/esm/modules/tracking/interfaces/tracking-analytics.js +13 -0
  44. package/dist/esm/modules/tracking/interfaces/tracking-analytics.js.map +1 -0
  45. package/dist/types/index.d.ts +1 -0
  46. package/dist/types/modules/dispatch/index.d.ts +1 -0
  47. package/dist/types/modules/dispatch/interfaces/dispatch-analytics.d.ts +158 -0
  48. package/dist/types/modules/permission/enums/permission-actions.enum.d.ts +4 -0
  49. package/dist/types/modules/permission/enums/permissions-moudule.enum.d.ts +2 -1
  50. package/dist/types/modules/responder/enums/responder-enums.d.ts +75 -0
  51. package/dist/types/modules/responder/index.d.ts +3 -0
  52. package/dist/types/modules/responder/interfaces/responder.dto.d.ts +87 -0
  53. package/dist/types/modules/responder/utils/responder-display.util.d.ts +26 -0
  54. package/dist/types/modules/tracking/index.d.ts +1 -0
  55. package/dist/types/modules/tracking/interfaces/tracking-analytics.d.ts +205 -0
  56. package/package.json +1 -1
@@ -0,0 +1,92 @@
1
+ import { ResponderDutyEndReason, ResponderDutySessionStatus, ResponderReportOutcome } from '../enums/responder-enums';
2
+ /*
3
+ * How the responder app's records read on the control room's screens - in the
4
+ * shared package, as for dispatches, so the unit screen, the incident screen
5
+ * and any future duty board name the same thing the same way.
6
+ */
7
+ /** The backend's limit on a duty-end reason (grm-shared-library RESPONDER_FIELD_LIMITS.dutyEndReason). */
8
+ export const RESPONDER_DUTY_END_REASON_MAX_LENGTH = 300;
9
+ export const RESPONDER_REPORT_OUTCOME_LABELS = {
10
+ [ResponderReportOutcome.RESOLVED_ON_SCENE]: 'Resolved on scene',
11
+ [ResponderReportOutcome.HANDED_OVER]: 'Handed over',
12
+ [ResponderReportOutcome.REFERRED]: 'Referred',
13
+ [ResponderReportOutcome.NO_ACTION_REQUIRED]: 'No action required',
14
+ [ResponderReportOutcome.FALSE_ALARM]: 'False alarm',
15
+ [ResponderReportOutcome.UNABLE_TO_LOCATE]: 'Unable to locate',
16
+ [ResponderReportOutcome.OTHER]: 'Other',
17
+ };
18
+ export const RESPONDER_DUTY_END_REASON_LABELS = {
19
+ [ResponderDutyEndReason.RESPONDER]: 'Ended by the responder',
20
+ [ResponderDutyEndReason.CONTROL_CENTRE]: 'Ended by the control room',
21
+ [ResponderDutyEndReason.MEMBERSHIP_REMOVED]: 'Removed from the unit',
22
+ [ResponderDutyEndReason.UNIT_DEACTIVATED]: 'Unit deactivated',
23
+ };
24
+ export const RESPONDER_DUTY_SESSION_STATUS_LABELS = {
25
+ [ResponderDutySessionStatus.ACTIVE]: 'On duty',
26
+ [ResponderDutySessionStatus.ENDED]: 'Ended',
27
+ };
28
+ export function responderReportOutcomeTone(outcome) {
29
+ switch (outcome) {
30
+ case ResponderReportOutcome.RESOLVED_ON_SCENE:
31
+ return 'success';
32
+ case ResponderReportOutcome.HANDED_OVER:
33
+ case ResponderReportOutcome.REFERRED:
34
+ return 'info';
35
+ case ResponderReportOutcome.UNABLE_TO_LOCATE:
36
+ return 'warning';
37
+ default:
38
+ return 'grey';
39
+ }
40
+ }
41
+ /** The outcome's label, or the raw value when this package does not know it yet. */
42
+ export function responderReportOutcomeLabel(outcome) {
43
+ var _a;
44
+ if (!outcome)
45
+ return '';
46
+ return (_a = RESPONDER_REPORT_OUTCOME_LABELS[outcome]) !== null && _a !== void 0 ? _a : String(outcome);
47
+ }
48
+ /** The end reason's label, or the raw value when this package does not know it yet. */
49
+ export function responderDutyEndReasonLabel(reason) {
50
+ var _a;
51
+ if (!reason)
52
+ return '';
53
+ return (_a = RESPONDER_DUTY_END_REASON_LABELS[reason]) !== null && _a !== void 0 ? _a : String(reason);
54
+ }
55
+ /**
56
+ * A checklist item id as words, for a screen that has the answers but not the
57
+ * organization's policy (where the labels live): "scene-made-safe",
58
+ * "scene_made_safe" and "sceneMadeSafe" all read "Scene made safe".
59
+ */
60
+ export function humanizeResponderChecklistItem(itemId) {
61
+ if (!itemId)
62
+ return '';
63
+ const words = itemId
64
+ .replace(/([a-z0-9])([A-Z])/g, '$1 $2')
65
+ .replace(/[-_.]+/g, ' ')
66
+ .trim()
67
+ .toLowerCase()
68
+ .replace(/\s+/g, ' ');
69
+ return words ? words.charAt(0).toUpperCase() + words.slice(1) : '';
70
+ }
71
+ /** "3 of 4 confirmed"; '' for an empty checklist. */
72
+ export function describeResponderChecklist(checklist) {
73
+ const items = checklist !== null && checklist !== void 0 ? checklist : [];
74
+ if (items.length === 0)
75
+ return '';
76
+ const checked = items.filter((item) => item.checked).length;
77
+ return `${checked} of ${items.length} confirmed`;
78
+ }
79
+ /** The responder's name - or, when their account could not be read, its number. */
80
+ export function responderDisplayName(responder) {
81
+ var _a, _b;
82
+ const name = (_b = (_a = responder.user) === null || _a === void 0 ? void 0 : _a.name) === null || _b === void 0 ? void 0 : _b.trim();
83
+ return name ? name : `Account #${responder.userId}`;
84
+ }
85
+ /** "jane@example.com · +254 712 345678", whichever of the two the account has. */
86
+ export function responderContactLine(responder) {
87
+ const user = responder.user;
88
+ if (!user)
89
+ return '';
90
+ return [user.email, user.phoneNumber].filter((value) => !!value && value.trim().length > 0).join(' · ');
91
+ }
92
+ //# sourceMappingURL=responder-display.util.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"responder-display.util.js","sourceRoot":"","sources":["../../../../../src/modules/responder/utils/responder-display.util.ts"],"names":[],"mappings":"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"}
@@ -11,4 +11,5 @@ export * from './interfaces/tracking-position.dto';
11
11
  export * from './interfaces/tracking-socket-events';
12
12
  export * from './interfaces/tracking-operations.dto';
13
13
  export * from './interfaces/tracking-playback.dto';
14
+ export * from './interfaces/tracking-analytics';
14
15
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/modules/tracking/index.ts"],"names":[],"mappings":"AAAA,QAAQ;AACR,cAAc,qCAAqC,CAAC;AACpD,cAAc,sCAAsC,CAAC;AACrD,cAAc,yCAAyC,CAAC;AACxD,cAAc,0CAA0C,CAAC;AAEzD,oBAAoB;AACpB,cAAc,+CAA+C,CAAC;AAC9D,cAAc,2CAA2C,CAAC;AAC1D,cAAc,0CAA0C,CAAC;AACzD,cAAc,oCAAoC,CAAC;AACnD,cAAc,qCAAqC,CAAC;AACpD,cAAc,sCAAsC,CAAC;AACrD,cAAc,oCAAoC,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/modules/tracking/index.ts"],"names":[],"mappings":"AAAA,QAAQ;AACR,cAAc,qCAAqC,CAAC;AACpD,cAAc,sCAAsC,CAAC;AACrD,cAAc,yCAAyC,CAAC;AACxD,cAAc,0CAA0C,CAAC;AAEzD,oBAAoB;AACpB,cAAc,+CAA+C,CAAC;AAC9D,cAAc,2CAA2C,CAAC;AAC1D,cAAc,0CAA0C,CAAC;AACzD,cAAc,oCAAoC,CAAC;AACnD,cAAc,qCAAqC,CAAC;AACpD,cAAc,sCAAsC,CAAC;AACrD,cAAc,oCAAoC,CAAC;AACnD,cAAc,iCAAiC,CAAC"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Units & Tracking analytics, the tracking half (the admin's Units & Tracking
3
+ * Analytics): the response units in scope and how they are tracked - computed by
4
+ * clarion-response-tracking. The other half, what was dispatched and how it went, is
5
+ * DispatchAnalytics (clarion-incident); both answer the same query.
6
+ *
7
+ * The fleet and its live status are as they stand NOW. Everything else is the period's,
8
+ * counted by the hour a position was recorded (`eventTime`): the service keeps an hourly
9
+ * rollup of the position history, so a year costs no more to read than a day. Counts,
10
+ * rates and durations only - never a position, a route or a device identifier.
11
+ */
12
+ export {};
13
+ //# sourceMappingURL=tracking-analytics.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tracking-analytics.js","sourceRoot":"","sources":["../../../../../src/modules/tracking/interfaces/tracking-analytics.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG"}
@@ -27,5 +27,6 @@ export * from './modules/billing/index';
27
27
  export * from './modules/newsfeed/index';
28
28
  export * from './modules/dispatch/index';
29
29
  export * from './modules/incident-sla/index';
30
+ export * from './modules/responder/index';
30
31
  export * from './modules/common/interfaces/server-message';
31
32
  export * from './modules/common/interfaces/pagination';
@@ -1,3 +1,4 @@
1
1
  export * from './enums/response-unit-dispatch-enums';
2
2
  export * from './interfaces/response-unit-dispatch.dto';
3
+ export * from './interfaces/dispatch-analytics';
3
4
  export * from './utils/response-unit-dispatch-display.util';
@@ -0,0 +1,158 @@
1
+ import { UnitsAnalyticsCount, UnitsAnalyticsDuration, UnitsAnalyticsRange, UnitsAnalyticsScope } from '../../tracking/interfaces/tracking-analytics';
2
+ /**
3
+ * Units & Tracking analytics, the dispatch half: the response units sent to emergencies
4
+ * and what became of each - computed by clarion-incident, for the same query as
5
+ * TrackingAnalytics.
6
+ *
7
+ * Dispatches are counted by when they were made (`dispatchedAt`) and shown as they stand
8
+ * now: a dispatch made in the period and completed after it counts as completed. Times
9
+ * are measured between the timestamps a dispatch records. Emergencies are the period's
10
+ * incidents (not grievances) that reached the control room. Counts, rates and durations
11
+ * only - never a dispatcher, a crew's phone number, a reason someone typed or a note.
12
+ */
13
+ export interface DispatchAnalyticsTotals {
14
+ dispatches: number;
15
+ /** The previous period of the same length; null for "forever". */
16
+ previousDispatches: number | null;
17
+ /** Emergencies a unit was sent to in the period. */
18
+ incidentsDispatched: number;
19
+ /** Emergencies raised in the period (reaching the control room), and those a unit was ever sent to. */
20
+ emergencies: number;
21
+ emergenciesDispatched: number;
22
+ /** emergenciesDispatched / emergencies, as a percentage. */
23
+ dispatchRate: number | null;
24
+ unitsDispatched: number;
25
+ dispatchers: number;
26
+ /** Dispatches per emergency dispatched in the period. */
27
+ unitsPerIncident: number | null;
28
+ }
29
+ export interface DispatchAnalyticsOutcomes {
30
+ /** Still out: dispatched, acknowledged, en route or on scene. */
31
+ active: number;
32
+ completed: number;
33
+ /** Stood down (CANCELLED) - and of those, refused by the responder. */
34
+ cancelled: number;
35
+ rejected: number;
36
+ /** Reached the scene. */
37
+ reachedScene: number;
38
+ /** Completed, as a percentage of the dispatches no longer out. */
39
+ completionRate: number | null;
40
+ /** Every status, in the order a dispatch lives through them, zero counts included. */
41
+ byStatus: UnitsAnalyticsCount[];
42
+ /** How the arrival was recorded (responder, control centre, inferred from tracking). */
43
+ arrivalBySource: UnitsAnalyticsCount[];
44
+ /** Who recorded each dispatch's latest status. */
45
+ statusBySource: UnitsAnalyticsCount[];
46
+ }
47
+ export interface DispatchAnalyticsTimes {
48
+ /** From an emergency being raised to its first unit being sent (the period's emergencies). */
49
+ toFirstDispatch: UnitsAnalyticsDuration;
50
+ /** From the unit being sent to it acknowledging, setting off, arriving and being done. */
51
+ toAcknowledge: UnitsAnalyticsDuration;
52
+ toEnRoute: UnitsAnalyticsDuration;
53
+ toOnScene: UnitsAnalyticsDuration;
54
+ /** From arriving to being done. */
55
+ onScene: UnitsAnalyticsDuration;
56
+ toComplete: UnitsAnalyticsDuration;
57
+ }
58
+ export interface DispatchAnalyticsRecommendation {
59
+ /** Dispatches made with a recommendation on screen. */
60
+ recommended: number;
61
+ /** ...that sent the recommended unit. */
62
+ followed: number;
63
+ /** ...that sent another unit, and of those, with a reason given. */
64
+ overridden: number;
65
+ overriddenWithReason: number;
66
+ /** Dispatches made when no unit was recommended. */
67
+ withoutRecommendation: number;
68
+ /** followed / recommended, as a percentage. */
69
+ followRate: number | null;
70
+ /** The sent unit as the recommendation saw it: how its capabilities matched, its availability, how fresh its position was. */
71
+ capabilityMatch: UnitsAnalyticsCount[];
72
+ availability: UnitsAnalyticsCount[];
73
+ locationFreshness: UnitsAnalyticsCount[];
74
+ /** Straight-line distance from the unit to the emergency when it was sent. */
75
+ distance: {
76
+ samples: number;
77
+ medianMetres: number | null;
78
+ p90Metres: number | null;
79
+ };
80
+ }
81
+ /** The dispatch message (SMS) to the crew. */
82
+ export interface DispatchAnalyticsNotification {
83
+ sent: number;
84
+ failed: number;
85
+ /** Could not be sent at all: no phone number, not configured, ... */
86
+ notPossible: number;
87
+ pending: number;
88
+ failures: UnitsAnalyticsCount[];
89
+ /** sent / (sent + failed), as a percentage. */
90
+ deliveryRate: number | null;
91
+ /** From the message being requested to the provider's answer. */
92
+ toResult: UnitsAnalyticsDuration;
93
+ /** Dispatches whose message was sent more than once. */
94
+ resent: number;
95
+ }
96
+ export interface DispatchAnalyticsTimelinePoint {
97
+ /** Start of the bucket, ISO 8601. */
98
+ at: string;
99
+ dispatches: number;
100
+ completed: number;
101
+ cancelled: number;
102
+ }
103
+ export interface DispatchAnalyticsUnitRow {
104
+ responseUnitId: number;
105
+ organizationId: number;
106
+ /** As it was named when last sent. */
107
+ name: string;
108
+ responseUnitType: string | null;
109
+ dispatches: number;
110
+ completed: number;
111
+ cancelled: number;
112
+ rejected: number;
113
+ medianSecondsToAcknowledge: number | null;
114
+ medianSecondsToOnScene: number | null;
115
+ lastDispatchedAt: string;
116
+ }
117
+ export interface DispatchAnalyticsTypeRow {
118
+ responseUnitType: string;
119
+ dispatches: number;
120
+ completed: number;
121
+ medianSecondsToOnScene: number | null;
122
+ }
123
+ export interface DispatchAnalyticsHourOfWeek {
124
+ /** 0 = Monday ... 6 = Sunday, in the period's time zone. */
125
+ day: number;
126
+ hour: number;
127
+ dispatches: number;
128
+ }
129
+ /** Platform view only: one row per organization. */
130
+ export interface DispatchAnalyticsOrganizationRow {
131
+ organizationId: number;
132
+ dispatches: number;
133
+ emergencies: number;
134
+ emergenciesDispatched: number;
135
+ completed: number;
136
+ medianSecondsToOnScene: number | null;
137
+ }
138
+ export interface DispatchAnalytics {
139
+ generatedAt: string;
140
+ range: UnitsAnalyticsRange;
141
+ scope: UnitsAnalyticsScope;
142
+ totals: DispatchAnalyticsTotals;
143
+ outcomes: DispatchAnalyticsOutcomes;
144
+ times: DispatchAnalyticsTimes;
145
+ recommendation: DispatchAnalyticsRecommendation;
146
+ notification: DispatchAnalyticsNotification;
147
+ timeline: DispatchAnalyticsTimelinePoint[];
148
+ byUnitType: DispatchAnalyticsTypeRow[];
149
+ /** The dispatched emergencies by their emergency type. */
150
+ byEmergencyType: UnitsAnalyticsCount[];
151
+ /** Dispatches by weekday and hour; only cells with dispatches. */
152
+ hourOfWeek: DispatchAnalyticsHourOfWeek[];
153
+ /** Units sent in the period, most dispatches first; capped (see `unitsTruncated`). */
154
+ units: DispatchAnalyticsUnitRow[];
155
+ unitsTruncated: boolean;
156
+ /** Platform view only; null otherwise. */
157
+ byOrganization: DispatchAnalyticsOrganizationRow[] | null;
158
+ }
@@ -52,6 +52,7 @@ export declare enum PermissionActions {
52
52
  READ_TRACKING_POSITION = "read:tracking-position",
53
53
  READ_TRACKING_HISTORY = "read:tracking-history",
54
54
  READ_TRACKING_OPERATIONS = "read:tracking-operations",
55
+ READ_TRACKING_ANALYTICS = "read:tracking-analytics",
55
56
  MANAGE_TRACKING_OPERATIONS = "manage:tracking-operations",
56
57
  READ_RESPONSE_UNIT_DISPATCH = "read:response-unit-dispatch",
57
58
  CREATE_RESPONSE_UNIT_DISPATCH = "create:response-unit-dispatch",
@@ -199,6 +200,9 @@ export declare enum PermissionActions {
199
200
  READ_INCIDENT_SLA_POLICY = "read:incident-sla-policy",
200
201
  MANAGE_INCIDENT_SLA_POLICY = "manage:incident-sla-policy",
201
202
  READ_INCIDENT_SLA_ANALYTICS = "read:incident-sla-analytics",
203
+ USE_RESPONDER_APP = "use:responder-app",
204
+ RESPOND_RESPONSE_UNIT_DISPATCH = "respond:response-unit-dispatch",
205
+ REPORT_RESPONDER_POSITION = "report:responder-position",
202
206
  /** @deprecated Retiring: split into the connection / message / screened-message / analytics actions. */
203
207
  READ_ALERT_INTAKE = "read:alert-intake",
204
208
  /** @deprecated Retiring: split into the connection / message / screened-message / analytics actions. */
@@ -16,5 +16,6 @@ export declare enum PermissionsModule {
16
16
  ALERT_INTAKE = "Alert Intake",
17
17
  SAFETY = "Safety",
18
18
  PLACES = "Places",
19
- INCIDENT_SLA = "Incident SLA"
19
+ INCIDENT_SLA = "Incident SLA",
20
+ RESPONDER = "Responder"
20
21
  }
@@ -0,0 +1,75 @@
1
+ /**
2
+ * The responder app, as the control room sees it: field personnel (role
3
+ * `Responder`) linked to the response units they may staff, going on duty with
4
+ * one of them and reporting how each dispatch ended.
5
+ *
6
+ * The admin's mirror of grm-shared-library `src/modules/responder` - the string
7
+ * values MUST stay identical to the backend's (pinned by responder-display.spec.ts).
8
+ */
9
+ /** A duty session's own lifecycle. One session is ACTIVE per responder at most. */
10
+ export declare enum ResponderDutySessionStatus {
11
+ ACTIVE = "ACTIVE",
12
+ ENDED = "ENDED"
13
+ }
14
+ /** Why a duty session ended. */
15
+ export declare enum ResponderDutyEndReason {
16
+ /** The responder went off duty in the app. */
17
+ RESPONDER = "RESPONDER",
18
+ /** The control room ended it (update:response-unit). */
19
+ CONTROL_CENTRE = "CONTROL_CENTRE",
20
+ /** The responder was removed from the unit while on duty with it. */
21
+ MEMBERSHIP_REMOVED = "MEMBERSHIP_REMOVED",
22
+ /** The unit was deactivated or deleted while the responder was on duty with it. */
23
+ UNIT_DEACTIVATED = "UNIT_DEACTIVATED"
24
+ }
25
+ /** How a response ended, as the responder reports it. */
26
+ export declare enum ResponderReportOutcome {
27
+ RESOLVED_ON_SCENE = "RESOLVED_ON_SCENE",
28
+ HANDED_OVER = "HANDED_OVER",
29
+ REFERRED = "REFERRED",
30
+ NO_ACTION_REQUIRED = "NO_ACTION_REQUIRED",
31
+ FALSE_ALARM = "FALSE_ALARM",
32
+ UNABLE_TO_LOCATE = "UNABLE_TO_LOCATE",
33
+ OTHER = "OTHER"
34
+ }
35
+ /**
36
+ * Machine-readable refusals of the responder endpoints, in the error body's
37
+ * `code` (or `details.code` behind the gateway) - the same
38
+ * `{statusCode, code, message, ...details}` envelope the dispatch endpoints use.
39
+ */
40
+ export declare enum ResponderErrorCode {
41
+ /** The caller does not hold the Responder role. */
42
+ NOT_A_RESPONDER = "RESPONDER_NOT_A_RESPONDER",
43
+ /** The responder account has no control centre, so nothing can be scoped. */
44
+ NO_CONTROL_CENTRE = "RESPONDER_NO_CONTROL_CENTRE",
45
+ UNIT_NOT_FOUND = "RESPONDER_UNIT_NOT_FOUND",
46
+ /** The caller is not a member of that unit. */
47
+ NOT_A_UNIT_MEMBER = "RESPONDER_NOT_A_UNIT_MEMBER",
48
+ UNIT_INACTIVE = "RESPONDER_UNIT_INACTIVE",
49
+ /** Already on duty with ANOTHER unit; end that duty first. `details.activeDuty` names it. */
50
+ ALREADY_ON_DUTY = "RESPONDER_ALREADY_ON_DUTY",
51
+ NOT_ON_DUTY = "RESPONDER_NOT_ON_DUTY",
52
+ /** Membership: the user is not a Responder of the unit's organization and control centre. */
53
+ USER_NOT_ELIGIBLE = "RESPONDER_USER_NOT_ELIGIBLE",
54
+ ALREADY_A_MEMBER = "RESPONDER_ALREADY_A_MEMBER",
55
+ MEMBERSHIP_NOT_FOUND = "RESPONDER_MEMBERSHIP_NOT_FOUND",
56
+ /** The dispatch does not exist or is not for one of the caller's units (never says which). */
57
+ DISPATCH_NOT_FOUND = "RESPONDER_DISPATCH_NOT_FOUND",
58
+ /** The move is not one a responder may make from the current status. `details.currentStatus`. */
59
+ INVALID_STATUS_TRANSITION = "RESPONDER_INVALID_STATUS_TRANSITION",
60
+ REASON_REQUIRED = "RESPONDER_REASON_REQUIRED",
61
+ DECLINE_NOT_ALLOWED = "RESPONDER_DECLINE_NOT_ALLOWED",
62
+ MANUAL_ARRIVAL_NOT_ALLOWED = "RESPONDER_MANUAL_ARRIVAL_NOT_ALLOWED",
63
+ /** Completing needs the response report first (organization policy). */
64
+ REPORT_REQUIRED = "RESPONDER_REPORT_REQUIRED",
65
+ /** A report can only be written once the unit reached the scene. */
66
+ REPORT_NOT_ALLOWED = "RESPONDER_REPORT_NOT_ALLOWED",
67
+ /** The report changed since the version the client edited. `details.currentVersion`. */
68
+ REPORT_VERSION_CONFLICT = "RESPONDER_REPORT_VERSION_CONFLICT",
69
+ /** The incident is closed or archived, so its reports can no longer be amended. */
70
+ REPORT_LOCKED = "RESPONDER_REPORT_LOCKED",
71
+ /** A required report field or checklist item is missing. `details.missing`. */
72
+ REPORT_INCOMPLETE = "RESPONDER_REPORT_INCOMPLETE",
73
+ /** An attachment id is not a file the caller may link. */
74
+ ATTACHMENT_NOT_READABLE = "RESPONDER_ATTACHMENT_NOT_READABLE"
75
+ }
@@ -0,0 +1,3 @@
1
+ export * from './enums/responder-enums';
2
+ export * from './interfaces/responder.dto';
3
+ export * from './utils/responder-display.util';
@@ -0,0 +1,87 @@
1
+ import { ResponderDutyEndReason, ResponderDutySessionStatus, ResponderReportOutcome } from '../enums/responder-enums';
2
+ /**
3
+ * A responder linked to a unit, as the unit screen lists them.
4
+ * `GET|POST /api/response-units/:id/responders`.
5
+ */
6
+ export interface ResponseUnitResponder {
7
+ id: number;
8
+ responseUnitId: number;
9
+ userId: number;
10
+ organizationId: number;
11
+ controlCentreId: number | null;
12
+ createdByUserId: number | null;
13
+ createdAt: string;
14
+ /** The account, when clarion-authentication could be read; null otherwise. */
15
+ user: {
16
+ id: number;
17
+ name: string;
18
+ email: string | null;
19
+ phoneNumber: string | null;
20
+ } | null;
21
+ /** Set while this responder is on duty with THIS unit. */
22
+ activeDuty: {
23
+ sessionId: number;
24
+ startedAt: string;
25
+ } | null;
26
+ }
27
+ /** `POST /api/response-units/:id/responders` - link a Responder account to a unit. */
28
+ export interface AddResponseUnitResponderRequest {
29
+ userId: number;
30
+ }
31
+ /** `POST /api/response-units/:id/responders/:userId/end-duty` - the control room ends someone's duty. */
32
+ export interface EndResponderDutyRequest {
33
+ /** At most RESPONDER_DUTY_END_REASON_MAX_LENGTH characters. */
34
+ reason?: string;
35
+ }
36
+ /** One stretch of duty: a responder staffing one unit. */
37
+ export interface ResponderDutySession {
38
+ id: number;
39
+ userId: number;
40
+ responseUnitId: number;
41
+ /** Copied at start so the record reads right after a rename. */
42
+ responseUnitName: string;
43
+ organizationId: number;
44
+ controlCentreId: number | null;
45
+ status: ResponderDutySessionStatus;
46
+ startedAt: string;
47
+ endedAt: string | null;
48
+ endReason: ResponderDutyEndReason | null;
49
+ endReasonText: string | null;
50
+ /** Who ended it - the responder, or the control-room user. */
51
+ endedByUserId: number | null;
52
+ }
53
+ /** One answer on a responder report's closure checklist. */
54
+ export interface ResponderReportChecklistAnswer {
55
+ /** The organization policy's stable item id (the label lives in the policy). */
56
+ itemId: string;
57
+ checked: boolean;
58
+ }
59
+ /**
60
+ * What the responder reported about one dispatch. One per dispatch, amended in
61
+ * versions. `GET /api/incidents/:id/responder-reports` lists an incident's.
62
+ */
63
+ export interface ResponderDispatchReport {
64
+ id: number;
65
+ reference: string;
66
+ dispatchId: number;
67
+ incidentId: number;
68
+ responseUnitId: number;
69
+ submittedByUserId: number;
70
+ /** Starts at 1; every amendment adds one. */
71
+ version: number;
72
+ summary: string;
73
+ actionsTaken: string | null;
74
+ outcome: ResponderReportOutcome;
75
+ personsContacted: string | null;
76
+ handoverTo: string | null;
77
+ followUpRequired: boolean;
78
+ followUpNotes: string | null;
79
+ remarks: string | null;
80
+ checklist: ResponderReportChecklistAnswer[];
81
+ /** dms file ids; the files are also appended to the incident's attachments. */
82
+ attachmentIds: number[];
83
+ submittedAt: string;
84
+ updatedAt: string;
85
+ /** Whether the CALLER may still amend it (submitter, incident not closed) - always false for the control room. */
86
+ amendable: boolean;
87
+ }
@@ -0,0 +1,26 @@
1
+ import { ResponderDutyEndReason, ResponderDutySessionStatus, ResponderReportOutcome } from '../enums/responder-enums';
2
+ import { ResponderReportChecklistAnswer, ResponseUnitResponder } from '../interfaces/responder.dto';
3
+ /** The backend's limit on a duty-end reason (grm-shared-library RESPONDER_FIELD_LIMITS.dutyEndReason). */
4
+ export declare const RESPONDER_DUTY_END_REASON_MAX_LENGTH = 300;
5
+ export declare const RESPONDER_REPORT_OUTCOME_LABELS: Readonly<Record<ResponderReportOutcome, string>>;
6
+ export declare const RESPONDER_DUTY_END_REASON_LABELS: Readonly<Record<ResponderDutyEndReason, string>>;
7
+ export declare const RESPONDER_DUTY_SESSION_STATUS_LABELS: Readonly<Record<ResponderDutySessionStatus, string>>;
8
+ /** The colour an outcome is shown in: settled is green, passed on is blue, not found is amber, the rest neutral. */
9
+ export type ResponderReportOutcomeTone = 'success' | 'info' | 'warning' | 'grey';
10
+ export declare function responderReportOutcomeTone(outcome: ResponderReportOutcome | string | null | undefined): ResponderReportOutcomeTone;
11
+ /** The outcome's label, or the raw value when this package does not know it yet. */
12
+ export declare function responderReportOutcomeLabel(outcome: ResponderReportOutcome | string | null | undefined): string;
13
+ /** The end reason's label, or the raw value when this package does not know it yet. */
14
+ export declare function responderDutyEndReasonLabel(reason: ResponderDutyEndReason | string | null | undefined): string;
15
+ /**
16
+ * A checklist item id as words, for a screen that has the answers but not the
17
+ * organization's policy (where the labels live): "scene-made-safe",
18
+ * "scene_made_safe" and "sceneMadeSafe" all read "Scene made safe".
19
+ */
20
+ export declare function humanizeResponderChecklistItem(itemId: string | null | undefined): string;
21
+ /** "3 of 4 confirmed"; '' for an empty checklist. */
22
+ export declare function describeResponderChecklist(checklist: readonly ResponderReportChecklistAnswer[] | null | undefined): string;
23
+ /** The responder's name - or, when their account could not be read, its number. */
24
+ export declare function responderDisplayName(responder: Pick<ResponseUnitResponder, 'userId' | 'user'>): string;
25
+ /** "jane@example.com · +254 712 345678", whichever of the two the account has. */
26
+ export declare function responderContactLine(responder: Pick<ResponseUnitResponder, 'user'>): string;
@@ -9,3 +9,4 @@ export * from './interfaces/tracking-position.dto';
9
9
  export * from './interfaces/tracking-socket-events';
10
10
  export * from './interfaces/tracking-operations.dto';
11
11
  export * from './interfaces/tracking-playback.dto';
12
+ export * from './interfaces/tracking-analytics';