@things-factory/work-shift 10.1.33 → 10.1.35

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 (51) hide show
  1. package/dist-client/pages/work-shift.d.ts +18 -102
  2. package/dist-client/pages/work-shift.js +165 -781
  3. package/dist-client/pages/work-shift.js.map +1 -1
  4. package/dist-client/tsconfig.tsbuildinfo +1 -1
  5. package/dist-server/controllers/work-shift-range.d.ts +25 -9
  6. package/dist-server/controllers/work-shift-range.js +49 -196
  7. package/dist-server/controllers/work-shift-range.js.map +1 -1
  8. package/dist-server/controllers/work-shift-schedule.d.ts +2 -0
  9. package/dist-server/controllers/work-shift-schedule.js +9 -18
  10. package/dist-server/controllers/work-shift-schedule.js.map +1 -1
  11. package/dist-server/service/index.d.ts +5 -2
  12. package/dist-server/service/index.js +3 -0
  13. package/dist-server/service/index.js.map +1 -1
  14. package/dist-server/service/work-shift/index.d.ts +4 -4
  15. package/dist-server/service/work-shift/index.js +4 -3
  16. package/dist-server/service/work-shift/index.js.map +1 -1
  17. package/dist-server/service/work-shift/shift-occurrence.d.ts +19 -0
  18. package/dist-server/service/work-shift/shift-occurrence.js +34 -0
  19. package/dist-server/service/work-shift/shift-occurrence.js.map +1 -0
  20. package/dist-server/service/work-shift/work-shift-arrangement.d.ts +10 -0
  21. package/dist-server/service/work-shift/work-shift-arrangement.js +40 -0
  22. package/dist-server/service/work-shift/work-shift-arrangement.js.map +1 -0
  23. package/dist-server/service/work-shift/work-shift-content.d.ts +15 -0
  24. package/dist-server/service/work-shift/work-shift-content.js +55 -0
  25. package/dist-server/service/work-shift/work-shift-content.js.map +1 -0
  26. package/dist-server/service/work-shift/work-shift-lifecycle-resolver.d.ts +60 -0
  27. package/dist-server/service/work-shift/work-shift-lifecycle-resolver.js +77 -0
  28. package/dist-server/service/work-shift/work-shift-lifecycle-resolver.js.map +1 -0
  29. package/dist-server/service/work-shift/work-shift-lifecycle.d.ts +130 -0
  30. package/dist-server/service/work-shift/work-shift-lifecycle.js +508 -0
  31. package/dist-server/service/work-shift/work-shift-lifecycle.js.map +1 -0
  32. package/dist-server/service/work-shift/work-shift-mutation.js +9 -42
  33. package/dist-server/service/work-shift/work-shift-mutation.js.map +1 -1
  34. package/dist-server/service/work-shift/work-shift-query.js +30 -15
  35. package/dist-server/service/work-shift/work-shift-query.js.map +1 -1
  36. package/dist-server/service/work-shift/work-shift-revision-service.d.ts +29 -65
  37. package/dist-server/service/work-shift/work-shift-revision-service.js +67 -167
  38. package/dist-server/service/work-shift/work-shift-revision-service.js.map +1 -1
  39. package/dist-server/service/work-shift/work-shift-revision.d.ts +78 -62
  40. package/dist-server/service/work-shift/work-shift-revision.js +303 -82
  41. package/dist-server/service/work-shift/work-shift-revision.js.map +1 -1
  42. package/dist-server/service/work-shift/work-shift-type.d.ts +0 -5
  43. package/dist-server/service/work-shift/work-shift-type.js +1 -16
  44. package/dist-server/service/work-shift/work-shift-type.js.map +1 -1
  45. package/dist-server/tsconfig.tsbuildinfo +1 -1
  46. package/helps/page/work-shift.ko.md +19 -3
  47. package/helps/page/work-shift.md +17 -9
  48. package/package.json +8 -5
  49. package/dist-server/service/work-shift/work-shift-revision-query.d.ts +0 -20
  50. package/dist-server/service/work-shift/work-shift-revision-query.js +0 -74
  51. package/dist-server/service/work-shift/work-shift-revision-query.js.map +0 -1
@@ -0,0 +1,34 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.shiftOccurrence = shiftOccurrence;
4
+ exports.shiftInfoAt = shiftInfoAt;
5
+ const tslib_1 = require("tslib");
6
+ const moment_timezone_1 = tslib_1.__importDefault(require("moment-timezone"));
7
+ /** Wall-clock rules use the signed zone. DST follows moment's earlier-offset/forward-gap rule. */
8
+ function shiftOccurrence(shift, workDate, timezone) {
9
+ const day = moment_timezone_1.default.tz(workDate, 'YYYY-MM-DD', true, timezone);
10
+ if (!day.isValid())
11
+ throw new Error('Invalid work date');
12
+ if (shift.daysOfWeek && !shift.daysOfWeek.split(',').includes(String(day.day())))
13
+ return undefined;
14
+ const clock = (offset, time) => moment_timezone_1.default.tz(day.clone().add(offset, 'day').format('YYYY-MM-DD') + ' ' + time, 'YYYY-MM-DD HH:mm', timezone).toDate();
15
+ const from = clock(shift.fromDate, shift.fromTime), to = clock(shift.toDate, shift.toTime);
16
+ return to > from ? { name: shift.name, workDate, from, to } : undefined;
17
+ }
18
+ function shiftInfoAt(shifts, timezone, at, validFrom, validTo) {
19
+ const today = (0, moment_timezone_1.default)(at).tz(timezone).startOf('day');
20
+ for (const offset of [-1, 0, 1]) {
21
+ const workDate = today.clone().add(offset, 'day').format('YYYY-MM-DD');
22
+ const windows = shifts.map(s => shiftOccurrence(s, workDate, timezone)).filter(Boolean);
23
+ const hit = windows.find(w => w.from <= at && w.to > at);
24
+ if (hit) {
25
+ const from = new Date(Math.max(hit.from.getTime(), validFrom?.getTime() ?? -Infinity));
26
+ const to = new Date(Math.min(hit.to.getTime(), validTo?.getTime() ?? Infinity));
27
+ return { workDate, workShift: hit.name, shiftRange: [from, to],
28
+ dateRange: [new Date(Math.max(Math.min(...windows.map(w => w.from.getTime())), validFrom?.getTime() ?? -Infinity)),
29
+ new Date(Math.min(Math.max(...windows.map(w => w.to.getTime())), validTo?.getTime() ?? Infinity))] };
30
+ }
31
+ }
32
+ return { workDate: today.format('YYYY-MM-DD'), workShift: '', dateRange: [today.toDate(), today.clone().add(1, 'day').toDate()] };
33
+ }
34
+ //# sourceMappingURL=shift-occurrence.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shift-occurrence.js","sourceRoot":"","sources":["../../../server/service/work-shift/shift-occurrence.ts"],"names":[],"mappings":"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","sourcesContent":["import moment from 'moment-timezone'\nimport type { ShiftDefinition } from './work-shift-content'\n\n/** Wall-clock rules use the signed zone. DST follows moment's earlier-offset/forward-gap rule. */\nexport function shiftOccurrence(shift: ShiftDefinition, workDate: string, timezone: string) {\n const day = moment.tz(workDate, 'YYYY-MM-DD', true, timezone)\n if (!day.isValid()) throw new Error('Invalid work date')\n if (shift.daysOfWeek && !shift.daysOfWeek.split(',').includes(String(day.day()))) return undefined\n const clock = (offset: number, time: string) => moment.tz(day.clone().add(offset, 'day').format('YYYY-MM-DD') + ' ' + time, 'YYYY-MM-DD HH:mm', timezone).toDate()\n const from = clock(shift.fromDate, shift.fromTime), to = clock(shift.toDate, shift.toTime)\n return to > from ? { name: shift.name, workDate, from, to } : undefined\n}\n\nexport function shiftInfoAt(shifts: ShiftDefinition[], timezone: string, at: Date, validFrom?: Date, validTo?: Date | null) {\n const today = moment(at).tz(timezone).startOf('day')\n for (const offset of [-1, 0, 1]) {\n const workDate = today.clone().add(offset, 'day').format('YYYY-MM-DD')\n const windows = shifts.map(s => shiftOccurrence(s, workDate, timezone)).filter(Boolean)\n const hit = windows.find(w => w.from <= at && w.to > at)\n if (hit) {\n const from = new Date(Math.max(hit.from.getTime(), validFrom?.getTime() ?? -Infinity))\n const to = new Date(Math.min(hit.to.getTime(), validTo?.getTime() ?? Infinity))\n return { workDate, workShift: hit.name, shiftRange: [from, to],\n dateRange: [new Date(Math.max(Math.min(...windows.map(w => w.from.getTime())), validFrom?.getTime() ?? -Infinity)),\n new Date(Math.min(Math.max(...windows.map(w => w.to.getTime())), validTo?.getTime() ?? Infinity))] }\n }\n }\n return { workDate: today.format('YYYY-MM-DD'), workShift: '', dateRange: [today.toDate(), today.clone().add(1,'day').toDate()] }\n}\n"]}
@@ -0,0 +1,10 @@
1
+ import { Domain } from '@things-factory/shell';
2
+ /** One identity per domain; revision content is the entire shift arrangement. */
3
+ export declare class WorkShiftArrangement {
4
+ id: string;
5
+ domain: Domain;
6
+ domainId: string;
7
+ name: string;
8
+ planGeneration: number;
9
+ revisionLock: number;
10
+ }
@@ -0,0 +1,40 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.WorkShiftArrangement = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const typeorm_1 = require("typeorm");
6
+ const shell_1 = require("@things-factory/shell");
7
+ /** One identity per domain; revision content is the entire shift arrangement. */
8
+ let WorkShiftArrangement = class WorkShiftArrangement {
9
+ };
10
+ exports.WorkShiftArrangement = WorkShiftArrangement;
11
+ tslib_1.__decorate([
12
+ (0, typeorm_1.PrimaryGeneratedColumn)('uuid'),
13
+ tslib_1.__metadata("design:type", String)
14
+ ], WorkShiftArrangement.prototype, "id", void 0);
15
+ tslib_1.__decorate([
16
+ (0, typeorm_1.ManyToOne)(() => shell_1.Domain, { nullable: false }),
17
+ tslib_1.__metadata("design:type", shell_1.Domain)
18
+ ], WorkShiftArrangement.prototype, "domain", void 0);
19
+ tslib_1.__decorate([
20
+ (0, typeorm_1.RelationId)((row) => row.domain),
21
+ tslib_1.__metadata("design:type", String)
22
+ ], WorkShiftArrangement.prototype, "domainId", void 0);
23
+ tslib_1.__decorate([
24
+ (0, typeorm_1.Column)({ default: 'Work shifts' }),
25
+ tslib_1.__metadata("design:type", String)
26
+ ], WorkShiftArrangement.prototype, "name", void 0);
27
+ tslib_1.__decorate([
28
+ (0, typeorm_1.Column)('int', { default: 0 }),
29
+ tslib_1.__metadata("design:type", Number)
30
+ ], WorkShiftArrangement.prototype, "planGeneration", void 0);
31
+ tslib_1.__decorate([
32
+ (0, typeorm_1.Column)('int', { default: 0 }),
33
+ tslib_1.__metadata("design:type", Number)
34
+ ], WorkShiftArrangement.prototype, "revisionLock", void 0);
35
+ exports.WorkShiftArrangement = WorkShiftArrangement = tslib_1.__decorate([
36
+ (0, typeorm_1.Entity)(),
37
+ (0, typeorm_1.Index)('ix_shift_arrangement_domain', ['domain'], { unique: true }),
38
+ (0, typeorm_1.Index)('ix_shift_arrangement_identity', ['domain', 'id'], { unique: true })
39
+ ], WorkShiftArrangement);
40
+ //# sourceMappingURL=work-shift-arrangement.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"work-shift-arrangement.js","sourceRoot":"","sources":["../../../server/service/work-shift/work-shift-arrangement.ts"],"names":[],"mappings":";;;;AAAA,qCAA8F;AAC9F,iDAA8C;AAE9C,iFAAiF;AAI1E,IAAM,oBAAoB,GAA1B,MAAM,oBAAoB;CAOhC,CAAA;AAPY,oDAAoB;AACC;IAA/B,IAAA,gCAAsB,EAAC,MAAM,CAAC;;gDAAW;AACI;IAA7C,IAAA,mBAAS,EAAC,GAAG,EAAE,CAAC,cAAM,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC;sCAAS,cAAM;oDAAA;AACL;IAAtD,IAAA,oBAAU,EAAC,CAAC,GAAyB,EAAE,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC;;sDAAiB;AACnC;IAAnC,IAAA,gBAAM,EAAC,EAAE,OAAO,EAAE,aAAa,EAAE,CAAC;;kDAAa;AACjB;IAA9B,IAAA,gBAAM,EAAC,KAAK,EAAE,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC;;4DAAuB;AACtB;IAA9B,IAAA,gBAAM,EAAC,KAAK,EAAE,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC;;0DAAqB;+BANxC,oBAAoB;IAHhC,IAAA,gBAAM,GAAE;IACR,IAAA,eAAK,EAAC,6BAA6B,EAAE,CAAC,QAAQ,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;IAClE,IAAA,eAAK,EAAC,+BAA+B,EAAE,CAAC,QAAQ,EAAE,IAAI,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;GAC9D,oBAAoB,CAOhC","sourcesContent":["import { Column, Entity, Index, ManyToOne, PrimaryGeneratedColumn, RelationId } from 'typeorm'\nimport { Domain } from '@things-factory/shell'\n\n/** One identity per domain; revision content is the entire shift arrangement. */\n@Entity()\n@Index('ix_shift_arrangement_domain', ['domain'], { unique: true })\n@Index('ix_shift_arrangement_identity', ['domain', 'id'], { unique: true })\nexport class WorkShiftArrangement {\n @PrimaryGeneratedColumn('uuid') id: string\n @ManyToOne(() => Domain, { nullable: false }) domain: Domain\n @RelationId((row: WorkShiftArrangement) => row.domain) domainId: string\n @Column({ default: 'Work shifts' }) name: string\n @Column('int', { default: 0 }) planGeneration: number\n @Column('int', { default: 0 }) revisionLock: number\n}\n"]}
@@ -0,0 +1,15 @@
1
+ export interface ShiftDefinition {
2
+ name: string;
3
+ description?: string;
4
+ fromDate: number;
5
+ fromTime: string;
6
+ toDate: number;
7
+ toTime: string;
8
+ daysOfWeek: string;
9
+ }
10
+ /** Weekdays refer to the work date, including shifts starting the previous/next day. */
11
+ export declare function shiftDraftContent(input: any, stored?: boolean): {
12
+ description: any;
13
+ timezone: string;
14
+ shifts: ShiftDefinition[];
15
+ };
@@ -0,0 +1,55 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.shiftDraftContent = shiftDraftContent;
4
+ const tslib_1 = require("tslib");
5
+ const moment_timezone_1 = tslib_1.__importDefault(require("moment-timezone"));
6
+ const shell_1 = require("@things-factory/shell");
7
+ function invalid(detail) { throw new shell_1.Refusal('INVALID_SHIFT_CONTENT', {}, detail); }
8
+ const clock = (value) => {
9
+ if (typeof value !== 'string' || !/^([01]\d|2[0-3]):[0-5]\d(:00)?$/.test(value))
10
+ invalid('Use HH:mm with minute precision.');
11
+ return String(value).slice(0, 5);
12
+ };
13
+ const minutes = (value) => Number(value.slice(0, 2)) * 60 + Number(value.slice(3));
14
+ /** Weekdays refer to the work date, including shifts starting the previous/next day. */
15
+ function shiftDraftContent(input, stored = false) {
16
+ if (!input || typeof input !== 'object' || (!stored && Object.keys(input).some(k => !['description', 'timezone', 'shifts', 'effectiveFrom', 'reason'].includes(k))))
17
+ invalid('Unexpected shift arrangement fields.');
18
+ if (typeof input.timezone !== 'string' || !moment_timezone_1.default.tz.zone(input.timezone))
19
+ invalid('Choose an IANA time zone.');
20
+ if (!Array.isArray(input.shifts) || !input.shifts.length || input.shifts.length > 64)
21
+ invalid('Provide between 1 and 64 shifts.');
22
+ const names = new Set(), windows = [];
23
+ const shifts = input.shifts.map((row) => {
24
+ if (!row || Object.keys(row).some(k => !['name', 'description', 'fromDate', 'fromTime', 'toDate', 'toTime', 'daysOfWeek'].includes(k)))
25
+ invalid('Unexpected shift fields.');
26
+ const name = typeof row.name === 'string' ? row.name.trim() : '';
27
+ if (!name || name.length > 128 || names.has(name))
28
+ invalid('Shift names must be nonempty and unique within the arrangement.');
29
+ names.add(name);
30
+ if (![-1, 0, 1].includes(row.fromDate) || ![-1, 0, 1].includes(row.toDate))
31
+ invalid('Date offsets must be -1, 0 or 1.');
32
+ const fromTime = clock(row.fromTime), toTime = clock(row.toTime);
33
+ const from = row.fromDate * 1440 + minutes(fromTime), to = row.toDate * 1440 + minutes(toTime);
34
+ if (to <= from || to - from > 1440)
35
+ invalid('A shift must end after it starts and last at most 24 hours.');
36
+ const said = row.daysOfWeek ?? '';
37
+ if (typeof said !== 'string' || (said !== '' && !/^[0-6](,[0-6])*$/.test(said)))
38
+ invalid('Weekdays must be a list of 0 (Sunday) through 6 (Saturday).');
39
+ const days = said === '' ? [0, 1, 2, 3, 4, 5, 6] : [...new Set(said.split(',').map(Number))].sort();
40
+ for (const day of days)
41
+ for (const week of [-1, 0, 1])
42
+ windows.push({ from: from + day * 1440 + week * 10080, to: to + day * 1440 + week * 10080, name });
43
+ if (row.description != null && typeof row.description !== 'string')
44
+ invalid('Shift description must be text.');
45
+ return { name, description: row.description ?? '', fromDate: row.fromDate, fromTime, toDate: row.toDate, toTime, daysOfWeek: days.length === 7 ? '' : days.join(',') };
46
+ }).sort((a, b) => a.fromDate - b.fromDate || a.fromTime.localeCompare(b.fromTime) || a.name.localeCompare(b.name));
47
+ windows.sort((a, b) => a.from - b.from);
48
+ for (let i = 1; i < windows.length; i++)
49
+ if (windows[i].from < windows[i - 1].to)
50
+ invalid(`Shifts overlap: ${windows[i - 1].name}, ${windows[i].name}.`);
51
+ if (input.description != null && typeof input.description !== 'string')
52
+ invalid('Arrangement description must be text.');
53
+ return { description: input.description ?? null, timezone: input.timezone, shifts };
54
+ }
55
+ //# sourceMappingURL=work-shift-content.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"work-shift-content.js","sourceRoot":"","sources":["../../../server/service/work-shift/work-shift-content.ts"],"names":[],"mappings":"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","sourcesContent":["import moment from 'moment-timezone'\nimport { Refusal } from '@things-factory/shell'\n\nexport interface ShiftDefinition {\n name: string\n description?: string\n fromDate: number\n fromTime: string\n toDate: number\n toTime: string\n daysOfWeek: string\n}\nfunction invalid(detail: string): never { throw new Refusal('INVALID_SHIFT_CONTENT', {}, detail) }\nconst clock = (value: unknown) => {\n if (typeof value !== 'string' || !/^([01]\\d|2[0-3]):[0-5]\\d(:00)?$/.test(value)) invalid('Use HH:mm with minute precision.')\n return String(value).slice(0, 5)\n}\nconst minutes = (value: string) => Number(value.slice(0, 2)) * 60 + Number(value.slice(3))\n\n/** Weekdays refer to the work date, including shifts starting the previous/next day. */\nexport function shiftDraftContent(input: any, stored = false) {\n if (!input || typeof input !== 'object' || (!stored && Object.keys(input).some(k => !['description','timezone','shifts','effectiveFrom','reason'].includes(k)))) invalid('Unexpected shift arrangement fields.')\n if (typeof input.timezone !== 'string' || !moment.tz.zone(input.timezone)) invalid('Choose an IANA time zone.')\n if (!Array.isArray(input.shifts) || !input.shifts.length || input.shifts.length > 64) invalid('Provide between 1 and 64 shifts.')\n const names = new Set<string>(), windows: { from: number; to: number; name: string }[] = []\n const shifts: ShiftDefinition[] = input.shifts.map((row: any) => {\n if (!row || Object.keys(row).some(k => !['name','description','fromDate','fromTime','toDate','toTime','daysOfWeek'].includes(k))) invalid('Unexpected shift fields.')\n const name = typeof row.name === 'string' ? row.name.trim() : ''\n if (!name || name.length > 128 || names.has(name)) invalid('Shift names must be nonempty and unique within the arrangement.')\n names.add(name)\n if (![-1,0,1].includes(row.fromDate) || ![-1,0,1].includes(row.toDate)) invalid('Date offsets must be -1, 0 or 1.')\n const fromTime = clock(row.fromTime), toTime = clock(row.toTime)\n const from = row.fromDate * 1440 + minutes(fromTime), to = row.toDate * 1440 + minutes(toTime)\n if (to <= from || to - from > 1440) invalid('A shift must end after it starts and last at most 24 hours.')\n const said = row.daysOfWeek ?? ''\n if (typeof said !== 'string' || (said !== '' && !/^[0-6](,[0-6])*$/.test(said))) invalid('Weekdays must be a list of 0 (Sunday) through 6 (Saturday).')\n const days = said === '' ? [0,1,2,3,4,5,6] : [...new Set(said.split(',').map(Number))].sort()\n for (const day of days) for (const week of [-1,0,1]) windows.push({ from: from + day * 1440 + week * 10080, to: to + day * 1440 + week * 10080, name })\n if (row.description != null && typeof row.description !== 'string') invalid('Shift description must be text.')\n return { name, description: row.description ?? '', fromDate: row.fromDate, fromTime, toDate: row.toDate, toTime, daysOfWeek: days.length === 7 ? '' : days.join(',') }\n }).sort((a: ShiftDefinition,b: ShiftDefinition) => a.fromDate - b.fromDate || a.fromTime.localeCompare(b.fromTime) || a.name.localeCompare(b.name))\n windows.sort((a,b) => a.from - b.from)\n for (let i = 1; i < windows.length; i++) if (windows[i].from < windows[i-1].to) invalid(`Shifts overlap: ${windows[i-1].name}, ${windows[i].name}.`)\n if (input.description != null && typeof input.description !== 'string') invalid('Arrangement description must be text.')\n return { description: input.description ?? null, timezone: input.timezone as string, shifts }\n}\n"]}
@@ -0,0 +1,60 @@
1
+ import { WorkShiftArrangement } from './work-shift-arrangement';
2
+ export declare class WorkShiftLifecycleResolver {
3
+ workShiftRevisionOverview(context: ResolverContext): Promise<{
4
+ approvalRequired: boolean;
5
+ timezone: string;
6
+ arrangementId: string;
7
+ name: string;
8
+ generation: number;
9
+ at: string;
10
+ schedule: {
11
+ state: string;
12
+ id: string;
13
+ domain: import("@things-factory/shell").Domain;
14
+ domainId: string;
15
+ arrangement: WorkShiftArrangement;
16
+ arrangementId: string;
17
+ plan: import("./work-shift-revision").WorkShiftPlan;
18
+ planId: string;
19
+ revision: import("./work-shift-revision").WorkShiftRevision;
20
+ revisionId: string;
21
+ validFrom: Date;
22
+ validTo: Date | null;
23
+ }[];
24
+ revisions: {
25
+ state: string;
26
+ revisionNumber: number;
27
+ policy: import("@things-factory/ops-master").MasterChangePolicy;
28
+ policyId: string;
29
+ id: string;
30
+ domain: import("@things-factory/shell").Domain;
31
+ domainId: string;
32
+ arrangement: WorkShiftArrangement;
33
+ arrangementId: string;
34
+ baseRevision: import("./work-shift-revision").WorkShiftRevision | null;
35
+ baseRevisionId: string | null;
36
+ baseGeneration: number;
37
+ editGeneration: number;
38
+ description: string | null;
39
+ timezone: string;
40
+ shifts: import("./work-shift-content").ShiftDefinition[];
41
+ effectiveFrom: Date;
42
+ reason: string;
43
+ creator: import("@things-factory/auth-base").User;
44
+ creatorId: string;
45
+ createdAt: Date;
46
+ frozenAt: Date | null;
47
+ contentHash: string | null;
48
+ activityInstanceId: string | null;
49
+ }[];
50
+ } | {
51
+ approvalRequired: boolean;
52
+ timezone: string;
53
+ revisions: any[];
54
+ schedule: any[];
55
+ generation: number;
56
+ }>;
57
+ createWorkShiftDraft(content: any, context: ResolverContext): Promise<import("./work-shift-revision").WorkShiftRevision>;
58
+ editWorkShiftDraft(id: string, generation: number, content: any, context: ResolverContext): Promise<import("./work-shift-revision").WorkShiftRevision>;
59
+ publishWorkShiftDraft(id: string, generation: number, context: ResolverContext): Promise<import("./work-shift-revision").WorkShiftPlan>;
60
+ }
@@ -0,0 +1,77 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.WorkShiftLifecycleResolver = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const type_graphql_1 = require("type-graphql");
6
+ const shell_1 = require("@things-factory/shell");
7
+ const ops_master_1 = require("@things-factory/ops-master");
8
+ const work_shift_arrangement_1 = require("./work-shift-arrangement");
9
+ const work_shift_lifecycle_1 = require("./work-shift-lifecycle");
10
+ let WorkShiftLifecycleResolver = class WorkShiftLifecycleResolver {
11
+ async workShiftRevisionOverview(context) {
12
+ const { domain } = context.state, manager = (0, shell_1.getRepository)(work_shift_arrangement_1.WorkShiftArrangement).manager;
13
+ const policy = await (0, ops_master_1.masterChangePolicy)(manager, domain.id, 'workShift');
14
+ const root = await manager.findOneBy(work_shift_arrangement_1.WorkShiftArrangement, { domain: { id: domain.id } });
15
+ return { ...(root ? await (0, work_shift_lifecycle_1.workShiftRevisionOverview)(manager, domain.id, root.id) : { revisions: [], schedule: [], generation: 0 }),
16
+ approvalRequired: policy.approvalRequired, timezone: domain.timezone || 'UTC' };
17
+ }
18
+ async createWorkShiftDraft(content, context) {
19
+ const { tx, domain, user } = context.state;
20
+ return (0, work_shift_lifecycle_1.createWorkShiftDraft)(tx, domain.id, user.id, { content });
21
+ }
22
+ async editWorkShiftDraft(id, generation, content, context) {
23
+ const { tx, domain, user } = context.state;
24
+ return (0, work_shift_lifecycle_1.editWorkShiftDraft)(tx, domain.id, user.id, id, generation, content);
25
+ }
26
+ async publishWorkShiftDraft(id, generation, context) {
27
+ const { tx, domain, user } = context.state;
28
+ return (0, work_shift_lifecycle_1.publishUnregulatedWorkShift)(tx, domain.id, user.id, id, generation);
29
+ }
30
+ };
31
+ exports.WorkShiftLifecycleResolver = WorkShiftLifecycleResolver;
32
+ tslib_1.__decorate([
33
+ (0, type_graphql_1.Directive)('@privilege(category: "work-shift", privilege: "query", domainOwnerGranted: true)'),
34
+ (0, type_graphql_1.Query)(() => shell_1.ScalarObject),
35
+ tslib_1.__param(0, (0, type_graphql_1.Ctx)()),
36
+ tslib_1.__metadata("design:type", Function),
37
+ tslib_1.__metadata("design:paramtypes", [Object]),
38
+ tslib_1.__metadata("design:returntype", Promise)
39
+ ], WorkShiftLifecycleResolver.prototype, "workShiftRevisionOverview", null);
40
+ tslib_1.__decorate([
41
+ (0, type_graphql_1.Directive)('@privilege(category: "work-shift", privilege: "mutation", domainOwnerGranted: true)'),
42
+ (0, type_graphql_1.Directive)('@transaction'),
43
+ (0, type_graphql_1.Mutation)(() => shell_1.ScalarObject),
44
+ tslib_1.__param(0, (0, type_graphql_1.Arg)('content', () => shell_1.ScalarObject)),
45
+ tslib_1.__param(1, (0, type_graphql_1.Ctx)()),
46
+ tslib_1.__metadata("design:type", Function),
47
+ tslib_1.__metadata("design:paramtypes", [Object, Object]),
48
+ tslib_1.__metadata("design:returntype", Promise)
49
+ ], WorkShiftLifecycleResolver.prototype, "createWorkShiftDraft", null);
50
+ tslib_1.__decorate([
51
+ (0, type_graphql_1.Directive)('@privilege(category: "work-shift", privilege: "mutation", domainOwnerGranted: true)'),
52
+ (0, type_graphql_1.Directive)('@transaction'),
53
+ (0, type_graphql_1.Mutation)(() => shell_1.ScalarObject),
54
+ tslib_1.__param(0, (0, type_graphql_1.Arg)('revisionId')),
55
+ tslib_1.__param(1, (0, type_graphql_1.Arg)('editGeneration', () => type_graphql_1.Int)),
56
+ tslib_1.__param(2, (0, type_graphql_1.Arg)('content', () => shell_1.ScalarObject)),
57
+ tslib_1.__param(3, (0, type_graphql_1.Ctx)()),
58
+ tslib_1.__metadata("design:type", Function),
59
+ tslib_1.__metadata("design:paramtypes", [String, Number, Object, Object]),
60
+ tslib_1.__metadata("design:returntype", Promise)
61
+ ], WorkShiftLifecycleResolver.prototype, "editWorkShiftDraft", null);
62
+ tslib_1.__decorate([
63
+ (0, type_graphql_1.Directive)('@privilege(category: "work-shift", privilege: "mutation", domainOwnerGranted: true)'),
64
+ (0, type_graphql_1.Directive)('@transaction'),
65
+ (0, type_graphql_1.Mutation)(() => shell_1.ScalarObject),
66
+ tslib_1.__param(0, (0, type_graphql_1.Arg)('revisionId')),
67
+ tslib_1.__param(1, (0, type_graphql_1.Arg)('editGeneration', () => type_graphql_1.Int)),
68
+ tslib_1.__param(2, (0, type_graphql_1.Ctx)()),
69
+ tslib_1.__metadata("design:type", Function),
70
+ tslib_1.__metadata("design:paramtypes", [String, Number, Object]),
71
+ tslib_1.__metadata("design:returntype", Promise)
72
+ ], WorkShiftLifecycleResolver.prototype, "publishWorkShiftDraft", null);
73
+ exports.WorkShiftLifecycleResolver = WorkShiftLifecycleResolver = tslib_1.__decorate([
74
+ (0, type_graphql_1.Resolver)(),
75
+ (0, type_graphql_1.UseMiddleware)(shell_1.SpeakRefusals)
76
+ ], WorkShiftLifecycleResolver);
77
+ //# sourceMappingURL=work-shift-lifecycle-resolver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"work-shift-lifecycle-resolver.js","sourceRoot":"","sources":["../../../server/service/work-shift/work-shift-lifecycle-resolver.ts"],"names":[],"mappings":"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","sourcesContent":["import { Arg, Ctx, Directive, Int, Mutation, Query, Resolver, UseMiddleware } from 'type-graphql'\nimport { getRepository, ScalarObject, SpeakRefusals } from '@things-factory/shell'\nimport { masterChangePolicy } from '@things-factory/ops-master'\nimport { WorkShiftArrangement } from './work-shift-arrangement'\nimport { createWorkShiftDraft, editWorkShiftDraft, publishUnregulatedWorkShift, workShiftRevisionOverview } from './work-shift-lifecycle'\n\n@Resolver()\n@UseMiddleware(SpeakRefusals)\nexport class WorkShiftLifecycleResolver {\n @Directive('@privilege(category: \"work-shift\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(() => ScalarObject)\n async workShiftRevisionOverview(@Ctx() context: ResolverContext) {\n const { domain } = context.state, manager = getRepository(WorkShiftArrangement).manager\n const policy = await masterChangePolicy(manager, domain.id, 'workShift')\n const root = await manager.findOneBy(WorkShiftArrangement, { domain: { id: domain.id } })\n return { ...(root ? await workShiftRevisionOverview(manager, domain.id, root.id) : { revisions: [], schedule: [], generation: 0 }),\n approvalRequired: policy.approvalRequired, timezone: domain.timezone || 'UTC' }\n }\n @Directive('@privilege(category: \"work-shift\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Directive('@transaction')\n @Mutation(() => ScalarObject)\n async createWorkShiftDraft(@Arg('content', () => ScalarObject) content: any, @Ctx() context: ResolverContext) {\n const { tx, domain, user } = context.state\n return createWorkShiftDraft(tx, domain.id, user.id, { content })\n }\n @Directive('@privilege(category: \"work-shift\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Directive('@transaction')\n @Mutation(() => ScalarObject)\n async editWorkShiftDraft(@Arg('revisionId') id: string, @Arg('editGeneration', () => Int) generation: number,\n @Arg('content', () => ScalarObject) content: any, @Ctx() context: ResolverContext) {\n const { tx, domain, user } = context.state\n return editWorkShiftDraft(tx, domain.id, user.id, id, generation, content)\n }\n @Directive('@privilege(category: \"work-shift\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Directive('@transaction')\n @Mutation(() => ScalarObject)\n async publishWorkShiftDraft(@Arg('revisionId') id: string, @Arg('editGeneration', () => Int) generation: number, @Ctx() context: ResolverContext) {\n const { tx, domain, user } = context.state\n return publishUnregulatedWorkShift(tx, domain.id, user.id, id, generation)\n }\n}\n"]}
@@ -0,0 +1,130 @@
1
+ import { EntityManager } from 'typeorm';
2
+ import { Refusal } from '@things-factory/shell';
3
+ import { WorkShiftArrangement } from './work-shift-arrangement';
4
+ import { WorkShiftBasis, WorkShiftPlan, WorkShiftRevision, WorkShiftSegment } from './work-shift-revision';
5
+ export declare class WorkShiftRefusal extends Refusal {
6
+ constructor(code: string, detail?: string);
7
+ }
8
+ export declare function workShiftSubmissionHash(revision: WorkShiftRevision): string;
9
+ /** Display values are a checked projection of the frozen revision, never a second editable source. */
10
+ export declare function workShiftApprovalInput(manager: EntityManager, domainId: string, revisionId: string): Promise<{
11
+ headline: string;
12
+ reason: string;
13
+ effectiveFrom: string;
14
+ before: {
15
+ revisionNumber: number;
16
+ detail: {
17
+ validFrom?: string;
18
+ validTo?: string;
19
+ revisionId: string;
20
+ createdAt: string;
21
+ frozenAt: string;
22
+ effectiveFrom: string;
23
+ reason: string;
24
+ };
25
+ description: string;
26
+ timezone: string;
27
+ shifts: import("./work-shift-content").ShiftDefinition[];
28
+ };
29
+ after: {
30
+ revisionNumber: number;
31
+ detail: {
32
+ validFrom?: string;
33
+ validTo?: string;
34
+ revisionId: string;
35
+ createdAt: string;
36
+ frozenAt: string;
37
+ effectiveFrom: string;
38
+ reason: string;
39
+ };
40
+ description: string;
41
+ timezone: string;
42
+ shifts: import("./work-shift-content").ShiftDefinition[];
43
+ };
44
+ revision: {
45
+ revisionId: string;
46
+ hash: string;
47
+ arrangementId: string;
48
+ baseGeneration: number;
49
+ };
50
+ }>;
51
+ export declare function assertWorkShiftApprovalInput(manager: EntityManager, domainId: string, input: Record<string, unknown>): Promise<void>;
52
+ export interface WorkShiftDraftContent {
53
+ description?: string;
54
+ timezone: string;
55
+ shifts: import('./work-shift-content').ShiftDefinition[];
56
+ effectiveFrom: Date | string;
57
+ reason: string;
58
+ }
59
+ export declare function createWorkShiftDraft(manager: EntityManager, domainId: string, creatorId: string, asked: {
60
+ arrangementId?: string;
61
+ name?: string;
62
+ baseRevisionId?: string;
63
+ content: WorkShiftDraftContent;
64
+ }): Promise<WorkShiftRevision>;
65
+ export declare function editWorkShiftDraft(manager: EntityManager, domainId: string, actorId: string, revisionId: string, expectedEditGeneration: number, input: WorkShiftDraftContent): Promise<WorkShiftRevision>;
66
+ /** Product calls this in the same transaction as issuing its worklist approval. */
67
+ export declare function freezeWorkShiftDraft(manager: EntityManager, domainId: string, actorId: string, revisionId: string, expectedEditGeneration: number): Promise<WorkShiftRevision>;
68
+ export declare function bindWorkShiftApproval(manager: EntityManager, domainId: string, revisionId: string, activityInstanceId: string): Promise<void>;
69
+ /** Internal completion callback only. Never expose this as an approved=true mutation. */
70
+ export declare function publishWorkShiftRevision(manager: EntityManager, domainId: string, revisionId: string, decision: {
71
+ activityInstanceId: string;
72
+ approverId: string;
73
+ submissionHash: string;
74
+ }): Promise<WorkShiftPlan>;
75
+ export declare function publishUnregulatedWorkShift(manager: EntityManager, domainId: string, actorId: string, revisionId: string, editGeneration: number): Promise<WorkShiftPlan>;
76
+ export declare function resolveWorkShift(manager: EntityManager, domainId: string, arrangementId: string, businessTime: Date | string, knownAt?: Date | string): Promise<{
77
+ revision: WorkShiftRevision;
78
+ plan: WorkShiftPlan;
79
+ segment: WorkShiftSegment;
80
+ }>;
81
+ /** Replay reads verify the captured digest too, not only the currently effective schedule. */
82
+ export declare function workShiftRevisionForBasis(manager: EntityManager, domainId: string, basis: WorkShiftBasis): Promise<WorkShiftRevision>;
83
+ /** Must run inside the transaction that writes the business record. */
84
+ export declare function captureWorkShiftBasis(manager: EntityManager, domainId: string, arrangementId: string, recordKey: string, businessTime: Date | string): Promise<WorkShiftBasis>;
85
+ export declare function workShiftRevisionOverview(manager: EntityManager, domainId: string, arrangementId: string, at?: Date): Promise<{
86
+ arrangementId: string;
87
+ name: string;
88
+ generation: number;
89
+ at: string;
90
+ schedule: {
91
+ state: string;
92
+ id: string;
93
+ domain: import("@things-factory/shell").Domain;
94
+ domainId: string;
95
+ arrangement: WorkShiftArrangement;
96
+ arrangementId: string;
97
+ plan: WorkShiftPlan;
98
+ planId: string;
99
+ revision: WorkShiftRevision;
100
+ revisionId: string;
101
+ validFrom: Date;
102
+ validTo: Date | null;
103
+ }[];
104
+ revisions: {
105
+ state: string;
106
+ revisionNumber: number;
107
+ policy: import("@things-factory/ops-master").MasterChangePolicy;
108
+ policyId: string;
109
+ id: string;
110
+ domain: import("@things-factory/shell").Domain;
111
+ domainId: string;
112
+ arrangement: WorkShiftArrangement;
113
+ arrangementId: string;
114
+ baseRevision: WorkShiftRevision | null;
115
+ baseRevisionId: string | null;
116
+ baseGeneration: number;
117
+ editGeneration: number;
118
+ description: string | null;
119
+ timezone: string;
120
+ shifts: import("./work-shift-content").ShiftDefinition[];
121
+ effectiveFrom: Date;
122
+ reason: string;
123
+ creator: import("@things-factory/auth-base").User;
124
+ creatorId: string;
125
+ createdAt: Date;
126
+ frozenAt: Date | null;
127
+ contentHash: string | null;
128
+ activityInstanceId: string | null;
129
+ }[];
130
+ }>;