@things-factory/work-shift 10.1.248 → 10.1.250
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/dist-client/bootstrap.d.ts +1 -0
- package/dist-client/bootstrap.js +6 -0
- package/dist-client/bootstrap.js.map +1 -0
- package/dist-client/index.d.ts +0 -0
- package/dist-client/index.js +2 -0
- package/dist-client/index.js.map +1 -0
- package/dist-client/pages/work-shift.d.ts +105 -0
- package/dist-client/pages/work-shift.js +1144 -0
- package/dist-client/pages/work-shift.js.map +1 -0
- package/dist-client/route.d.ts +1 -0
- package/dist-client/route.js +8 -0
- package/dist-client/route.js.map +1 -0
- package/dist-client/shift-coverage.d.ts +102 -0
- package/dist-client/shift-coverage.js +196 -0
- package/dist-client/shift-coverage.js.map +1 -0
- package/dist-client/shift-editing.d.ts +80 -0
- package/dist-client/shift-editing.js +212 -0
- package/dist-client/shift-editing.js.map +1 -0
- package/dist-client/shift-revisions.d.ts +73 -0
- package/dist-client/shift-revisions.js +64 -0
- package/dist-client/shift-revisions.js.map +1 -0
- package/dist-client/tsconfig.tsbuildinfo +1 -0
- package/dist-server/controllers/index.d.ts +2 -0
- package/dist-server/controllers/index.js +6 -0
- package/dist-server/controllers/index.js.map +1 -0
- package/dist-server/controllers/work-shift-range.d.ts +28 -0
- package/dist-server/controllers/work-shift-range.js +65 -0
- package/dist-server/controllers/work-shift-range.js.map +1 -0
- package/dist-server/controllers/work-shift-schedule.d.ts +5 -0
- package/dist-server/controllers/work-shift-schedule.js +60 -0
- package/dist-server/controllers/work-shift-schedule.js.map +1 -0
- package/dist-server/index.d.ts +2 -0
- package/dist-server/index.js +19 -0
- package/dist-server/index.js.map +1 -0
- package/dist-server/service/index.d.ts +13 -0
- package/dist-server/service/index.js +33 -0
- package/dist-server/service/index.js.map +1 -0
- package/dist-server/service/work-shift/days-of-week.d.ts +30 -0
- package/dist-server/service/work-shift/days-of-week.js +74 -0
- package/dist-server/service/work-shift/days-of-week.js.map +1 -0
- package/dist-server/service/work-shift/index.d.ts +9 -0
- package/dist-server/service/work-shift/index.js +20 -0
- package/dist-server/service/work-shift/index.js.map +1 -0
- package/dist-server/service/work-shift/shift-occurrence.d.ts +19 -0
- package/dist-server/service/work-shift/shift-occurrence.js +34 -0
- package/dist-server/service/work-shift/shift-occurrence.js.map +1 -0
- package/dist-server/service/work-shift/work-shift-arrangement.d.ts +10 -0
- package/dist-server/service/work-shift/work-shift-arrangement.js +40 -0
- package/dist-server/service/work-shift/work-shift-arrangement.js.map +1 -0
- package/dist-server/service/work-shift/work-shift-baseline.d.ts +23 -0
- package/dist-server/service/work-shift/work-shift-baseline.js +55 -0
- package/dist-server/service/work-shift/work-shift-baseline.js.map +1 -0
- package/dist-server/service/work-shift/work-shift-change-approval.d.ts +14 -0
- package/dist-server/service/work-shift/work-shift-change-approval.js +64 -0
- package/dist-server/service/work-shift/work-shift-change-approval.js.map +1 -0
- package/dist-server/service/work-shift/work-shift-content.d.ts +18 -0
- package/dist-server/service/work-shift/work-shift-content.js +62 -0
- package/dist-server/service/work-shift/work-shift-content.js.map +1 -0
- package/dist-server/service/work-shift/work-shift-lifecycle-resolver.d.ts +68 -0
- package/dist-server/service/work-shift/work-shift-lifecycle-resolver.js +89 -0
- package/dist-server/service/work-shift/work-shift-lifecycle-resolver.js.map +1 -0
- package/dist-server/service/work-shift/work-shift-lifecycle.d.ts +229 -0
- package/dist-server/service/work-shift/work-shift-lifecycle.js +672 -0
- package/dist-server/service/work-shift/work-shift-lifecycle.js.map +1 -0
- package/dist-server/service/work-shift/work-shift-mutation.d.ts +16 -0
- package/dist-server/service/work-shift/work-shift-mutation.js +77 -0
- package/dist-server/service/work-shift/work-shift-mutation.js.map +1 -0
- package/dist-server/service/work-shift/work-shift-query.d.ts +14 -0
- package/dist-server/service/work-shift/work-shift-query.js +116 -0
- package/dist-server/service/work-shift/work-shift-query.js.map +1 -0
- package/dist-server/service/work-shift/work-shift-revision-service.d.ts +32 -0
- package/dist-server/service/work-shift/work-shift-revision-service.js +89 -0
- package/dist-server/service/work-shift/work-shift-revision-service.js.map +1 -0
- package/dist-server/service/work-shift/work-shift-revision.d.ts +85 -0
- package/dist-server/service/work-shift/work-shift-revision.js +337 -0
- package/dist-server/service/work-shift/work-shift-revision.js.map +1 -0
- package/dist-server/service/work-shift/work-shift-selection.d.ts +50 -0
- package/dist-server/service/work-shift/work-shift-selection.js +65 -0
- package/dist-server/service/work-shift/work-shift-selection.js.map +1 -0
- package/dist-server/service/work-shift/work-shift-type.d.ts +26 -0
- package/dist-server/service/work-shift/work-shift-type.js +77 -0
- package/dist-server/service/work-shift/work-shift-type.js.map +1 -0
- package/dist-server/service/work-shift/work-shift.d.ts +56 -0
- package/dist-server/service/work-shift/work-shift.js +109 -0
- package/dist-server/service/work-shift/work-shift.js.map +1 -0
- package/dist-server/tsconfig.tsbuildinfo +1 -0
- package/package.json +4 -4
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"work-shift.js","sourceRoot":"","sources":["../../client/pages/work-shift.ts"],"names":[],"mappings":";AAAA,OAAO,2CAA2C,CAAA;AAClD,OAAO,sCAAsC,CAAA;AAC7C,OAAO,sCAAsC,CAAA;AAE7C,OAAO,GAAG,MAAM,aAAa,CAAA;AAC7B,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,KAAK,CAAA;AACxC,OAAO,EAAE,aAAa,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAA;AACxD,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAA;AACzC,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AACjD,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAA;AACxC,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AACzC,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AACzC,OAAO,EAAE,YAAY,EAAE,kBAAkB,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AACpG,OAAO,EAAE,YAAY,IAAI,EAAE,EAAE,YAAY,EAAE,MAAM,4EAA4E,CAAA;AAC7H,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,sEAAsE,CAAA;AAElH,OAAO,EAAE,OAAO,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAA;AAEzE,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,WAAW,EAAE,aAAa,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,EAAE,aAAa,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAA;AAEzJ,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAA;AAGpF,MAAM,WAAW,GAAG,EAAE,GAAG,EAAE,CAAA;AAe3B,MAAM,CAAC,GAAG,CAAC,GAAW,EAAE,SAAc,EAAE,EAAE,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC,CAAA;AAE3E;;;;;;;;;GASG;AAEI,IAAM,SAAS,GAAf,MAAM,SAAU,SAAQ,QAAQ,CAAC,OAAO,CAAC,CAAC,QAAQ,CAAC;IAAnD;;QAsWY,YAAO,GAAG,KAAK,CAAA;QACf,UAAK,GAAY,IAAI,CAAA;QACrB,cAAS,GAAwB,EAAE,CAAA;QAEnC,UAAK,GAAG,KAAK,CAAA;QACb,SAAI,GAAG,KAAK,CAAA;QACZ,cAAS,GAAG,EAAE,CAAA;QAC/B,0GAA0G;QACzF,UAAK,GAAG,KAAK,CAAA;QA8P9B,0EAA0E;QAElE,YAAO,GAAG,CAAC,MAAc,EAAE,EAAE,CAAC,EAAE,CAAC,aAAa,GAAG,MAAM,CAAC,CAAA;IAwflE,CAAC;aArmCQ,WAAM,GAAG;QACd,eAAe;QACf,YAAY;QACZ,kBAAkB;QAClB,eAAe;QACf,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KA4VF;KACF,AAlWY,CAkWZ;IAcD,IAAI,OAAO;QACT,OAAO;YACL,KAAK,EAAE,CAAC,CAAC,kBAAkB,CAAC;YAC5B,KAAK,EAAE,UAAU;YACjB,IAAI,EAAE,iBAAiB;YACvB,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE;SACxB,CAAA;IACH,CAAC;IAEO,OAAO;QACb,IAAI,CAAC,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,KAAK;YAAE,OAAO,EAAE,CAAA;QAC3C,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC;YAChB,OAAO,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,sBAAsB,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,KAAK,EAAE,EAAE,CAAC,CAAA;QACrH,CAAC;QACD,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,gBAAgB,CAAA;QAC/C,OAAO;YACL,EAAE,KAAK,EAAE,CAAC,CAAC,eAAe,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,KAAK,EAAE,EAAE;YACzD,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,CAAC,YAAY,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;YAClF;gBACE,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC,kBAAkB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC;gBAC3D,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM;gBAChC,QAAQ,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE;gBAC1B,MAAM,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,MAAM,EAAE;aAC5B;SACF,CAAA;IACH,CAAC;IAED,KAAK,CAAC,WAAW,CAAC,OAAY;QAC5B,IAAI,IAAI,CAAC,MAAM,IAAI,OAAO,CAAC,MAAM,IAAI,CAAC,IAAI,CAAC,KAAK;YAAE,MAAM,IAAI,CAAC,IAAI,EAAE,CAAA;IACrE,CAAC;IAED,0EAA0E;IAElE,KAAK,CAAC,IAAI;QAChB,IAAI,CAAC,OAAO,GAAG,IAAI,CAAA;QACnB,IAAI,CAAC,KAAK,GAAG,IAAI,CAAA;QACjB,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,KAAK,CAAC;gBAChC,KAAK,EAAE,GAAG,CAAA;;;;SAIT;gBACD,WAAW,EAAE,UAAU;aACxB,CAAC,CAAA;YACF,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM;gBAAE,MAAM,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAA;YACjD,IAAI,CAAC,QAAQ,GAAG,MAAM,CAAC,IAAI,CAAC,yBAAyB,CAAA;YACrD,IAAI,CAAC,SAAS,GAAG,MAAM,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAA;QACpE,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,IAAI,CAAC,KAAK,GAAG,CAAC,CAAA;QAChB,CAAC;gBAAS,CAAC;YACT,IAAI,CAAC,OAAO,GAAG,KAAK,CAAA;YACpB,IAAI,CAAC,aAAa,EAAE,CAAA;QACtB,CAAC;IACH,CAAC;IAED,oFAAoF;IAC5E,KAAK,CAAC,aAAa,CAAC,SAAwB;QAClD,MAAM,SAAS,GAAwB,EAAE,CAAA;QACzC,KAAK,MAAM,GAAG,IAAI,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,kBAAkB,CAAC,EAAE,CAAC;YAC9D,IAAI,OAAiC,CAAA;YACrC,QAAQ,CAAC,aAAa,CACpB,IAAI,WAAW,CAAC,sBAAsB,EAAE;gBACtC,MAAM,EAAE,EAAE,kBAAkB,EAAE,GAAG,CAAC,kBAAkB,EAAE,WAAW,EAAE,CAAC,CAAe,EAAE,EAAE,CAAC,CAAC,OAAO,GAAG,CAAC,CAAC,EAAE;aACxG,CAAC,CACH,CAAA;YACD,IAAI,OAAO;gBAAE,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,OAAO,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,CAAC,CAAA;QACpF,CAAC;QACD,OAAO,SAAS,CAAA;IAClB,CAAC;IAED,IAAY,MAAM;QAChB,OAAO,cAAc,CAAC,IAAI,CAAC,QAAQ,IAAI,EAAE,SAAS,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,EAAE,IAAI,CAAC,SAAS,CAAC,CAAA;IACzF,CAAC;IAED,0EAA0E;IAElE,KAAK,CAAC,IAAkB;QAC9B,MAAM,IAAI,GAAG,IAAI,IAAI,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,GAAG,CAAA;QAC7C,MAAM,MAAM,GAAoB,IAAI,EAAE,MAAM,IAAI,IAAI,CAAC,QAAQ,CAAC,YAAY,IAAI,EAAE,CAAA;QAChF,IAAI,CAAC,KAAK,GAAG;YACX,WAAW,EAAE,IAAI,EAAE,WAAW,IAAI,EAAE;YACpC,QAAQ,EAAE,IAAI,EAAE,QAAQ,IAAI,IAAI,CAAC,QAAQ,CAAC,QAAQ;YAClD,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,MAAM,CAAC,EAAE,GAAG,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;YACnF,aAAa,EAAE,KAAK;YACpB,aAAa,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;YACvC,MAAM,EAAE,EAAE;SACX,CAAA;QACD,IAAI,CAAC,WAAW,EAAE,CAAA;IACpB,CAAC;IAEO,MAAM,CAAC,GAAgB;QAC7B,IAAI,CAAC,KAAK,GAAG;YACX,EAAE,EAAE,GAAG,CAAC,EAAE;YACV,cAAc,EAAE,GAAG,CAAC,cAAc;YAClC,cAAc,EAAE,GAAG,CAAC,cAAc;YAClC,WAAW,EAAE,GAAG,CAAC,WAAW,IAAI,EAAE;YAClC,QAAQ,EAAE,GAAG,CAAC,QAAQ,IAAI,IAAI,CAAC,QAAQ,CAAC,QAAQ;YAChD,MAAM,EAAE,CAAC,GAAG,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,MAAM,CAAC,EAAE,GAAG,KAAK,EAAE,CAAC,CAAC;YAC7D,aAAa,EAAE,IAAI;YACnB,aAAa,EAAE,GAAG,CAAC,aAAa,IAAI,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;YAC5D,MAAM,EAAE,GAAG,CAAC,MAAM,IAAI,EAAE;SACzB,CAAA;QACD,IAAI,CAAC,WAAW,EAAE,CAAA;IACpB,CAAC;IAEO,WAAW;QACjB,IAAI,CAAC,KAAK,GAAG,KAAK,CAAA;QAClB,IAAI,CAAC,KAAK,GAAG,KAAK,CAAA;QAClB,IAAI,CAAC,SAAS,GAAG,EAAE,CAAA;QACnB,IAAI,CAAC,aAAa,EAAE,CAAA;QACpB,yDAAyD;QACzD,IAAI,CAAC,QAAQ,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC,CAAA;IAC3B,CAAC;IAEO,KAAK,CAAC,KAAK;QACjB,IAAI,IAAI,CAAC,IAAI;YAAE,OAAM;QACrB,IACE,IAAI,CAAC,KAAK;YACV,CAAC,CAAC,MAAM,QAAQ,CAAC,IAAI,CAAC;gBACpB,IAAI,EAAE,SAAS;gBACf,KAAK,EAAE,EAAE,CAAC,kBAAkB,CAAC;gBAC7B,aAAa,EAAE,EAAE,IAAI,EAAE,EAAE,CAAC,SAAS,CAAC,EAAE,WAAW,EAAE,IAAI,EAAE;gBACzD,YAAY,EAAE,EAAE,IAAI,EAAE,EAAE,CAAC,cAAc,CAAC,EAAE;aAC3C,CAAC,CAAC;YAEH,OAAM;QACR,IAAI,CAAC,KAAK,GAAG,SAAS,CAAA;QACtB,IAAI,CAAC,KAAK,GAAG,KAAK,CAAA;QAClB,IAAI,CAAC,aAAa,EAAE,CAAA;QACpB,MAAM,IAAI,CAAC,IAAI,EAAE,CAAA;IACnB,CAAC;IAEO,MAAM,CAAC,KAAqB;QAClC,IAAI,CAAC,KAAK,GAAG,EAAE,GAAG,IAAI,CAAC,KAAM,EAAE,GAAG,KAAK,EAAE,CAAA;QACzC,IAAI,CAAC,KAAK,GAAG,IAAI,CAAA;IACnB,CAAC;IAEO,WAAW,CAAC,KAAa,EAAE,KAA6B;QAC9D,IAAI,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,KAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,GAAG,CAAC,EAAE,GAAG,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAA;IAC3G,CAAC;IAEO,OAAO;QACb,MAAM,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC,KAAM,CAAA;QAC3F,OAAO;YACL,WAAW;YACX,QAAQ;YACR,MAAM,EAAE,aAAa,CAAC,MAAM,CAAC;YAC7B,aAAa,EAAE,aAAa,KAAK,KAAK,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,aAAa;YACjF,MAAM,EAAE,MAAM,CAAC,IAAI,EAAE;SACtB,CAAA;IACH,CAAC;IAEO,KAAK,CAAC,GAAG,CAAI,IAAsB;QACzC,IAAI,IAAI,CAAC,IAAI;YAAE,OAAO,SAAS,CAAA;QAC/B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAA;QAChB,IAAI,CAAC,SAAS,GAAG,EAAE,CAAA;QACnB,IAAI,CAAC;YACH,OAAO,MAAM,IAAI,EAAE,CAAA;QACrB,CAAC;QAAC,OAAO,CAAM,EAAE,CAAC;YAChB,2GAA2G;YAC3G,IAAI,CAAC,CAAC,CAAC,EAAE,QAAQ,IAAI,CAAC,EAAE,aAAa,EAAE,MAAM,IAAI,CAAC,EAAE,YAAY,CAAC;gBAAE,IAAI,CAAC,SAAS,GAAG,CAAC,EAAE,OAAO,IAAI,MAAM,CAAC,CAAC,CAAC,CAAA;YAC3G,OAAO,SAAS,CAAA;QAClB,CAAC;gBAAS,CAAC;YACT,IAAI,CAAC,IAAI,GAAG,KAAK,CAAA;QACnB,CAAC;IACH,CAAC;IAEO,KAAK,CAAC,MAAM,CAAC,QAAa,EAAE,SAAc,EAAE,KAAa;QAC/D,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC,CAAA;QAC7D,IAAI,QAAQ,CAAC,MAAM,EAAE,MAAM;YAAE,MAAM,MAAM,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAA;QAC3G,OAAO,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;IAC7B,CAAC;IAED,yFAAyF;IACjF,KAAK,CAAC,SAAS,CAAC,KAAK,GAAG,KAAK;QACnC,IAAI,CAAC,KAAK,GAAG,IAAI,CAAA;QACjB,IAAI,WAAW,CAAC,IAAI,CAAC,KAAM,CAAC,CAAC,QAAQ;YAAE,OAAO,KAAK,CAAA;QACnD,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,KAAK,IAAI,EAAE;YACtC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAM,CAAA;YACzB,MAAM,GAAG,GAAG,KAAK,CAAC,EAAE;gBAClB,CAAC,CAAC,MAAM,IAAI,CAAC,MAAM,CACf,GAAG,CAAA;;;;aAIF,EACD,EAAE,UAAU,EAAE,KAAK,CAAC,EAAE,EAAE,cAAc,EAAE,KAAK,CAAC,cAAc,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,EAAE,EACvF,oBAAoB,CACrB;gBACH,CAAC,CAAC,MAAM,IAAI,CAAC,MAAM,CACf,GAAG,CAAA;;;;aAIF,EACD,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,EAAE,EAC3B,sBAAsB,CACvB,CAAA;YACL,IAAI,CAAC,KAAK,GAAG,EAAE,GAAG,KAAK,EAAE,EAAE,EAAE,GAAG,CAAC,EAAE,EAAE,cAAc,EAAE,GAAG,CAAC,cAAc,EAAE,cAAc,EAAE,GAAG,CAAC,cAAc,EAAE,CAAA;YAC7G,IAAI,CAAC,KAAK,GAAG,KAAK,CAAA;YAClB,OAAO,IAAI,CAAA;QACb,CAAC,CAAC,CAAA;QACF,IAAI,KAAK,IAAI,CAAC,KAAK;YAAE,MAAM,CAAC,EAAE,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC,CAAA;QACrD,OAAO,CAAC,CAAC,KAAK,CAAA;IAChB,CAAC;IAED,oFAAoF;IAC5E,KAAK,CAAC,MAAM;QAClB,IAAI,CAAC,KAAK,GAAG,IAAI,CAAA;QACjB,IAAI,WAAW,CAAC,IAAI,CAAC,KAAM,CAAC,CAAC,QAAQ;YAAE,OAAM;QAC7C,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,gBAAgB,CAAA;QAC/C,IAAI,QAAQ,IAAI,CAAC,IAAI,CAAC,KAAK,IAAI,CAAC,IAAI,CAAC,KAAM,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,MAAM,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;YAAE,OAAM;QAExF,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,KAAK,IAAI,EAAE;YACrC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAM,CAAA;YACzB,IAAI,QAAQ,EAAE,CAAC;gBACb,IAAI,OAAiC,CAAA;gBACrC,QAAQ,CAAC,aAAa,CACpB,IAAI,WAAW,CAAC,wBAAwB,EAAE;oBACxC,MAAM,EAAE;wBACN,IAAI,EAAE,WAAW;wBACjB,UAAU,EAAE,KAAK,CAAC,EAAE;wBACpB,cAAc,EAAE,KAAK,CAAC,cAAc;wBACpC,WAAW,EAAE,CAAC,CAAe,EAAE,EAAE,CAAC,CAAC,OAAO,GAAG,CAAC,CAAC;qBAChD;iBACF,CAAC,CACH,CAAA;gBACD,IAAI,CAAC,OAAO;oBAAE,MAAM,IAAI,KAAK,CAAC,EAAE,CAAC,sBAAsB,CAAC,CAAC,CAAA;gBACzD,MAAM,OAAO,CAAA;YACf,CAAC;iBAAM,CAAC;gBACN,MAAM,IAAI,CAAC,MAAM,CACf,GAAG,CAAA;;;;WAIF,EACD,EAAE,UAAU,EAAE,KAAK,CAAC,EAAE,EAAE,cAAc,EAAE,KAAK,CAAC,cAAc,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,EAAE,EACvF,yBAAyB,CAC1B,CAAA;YACH,CAAC;YACD,OAAO,IAAI,CAAA;QACb,CAAC,CAAC,CAAA;QACF,IAAI,CAAC,IAAI;YAAE,OAAM;QACjB,MAAM,CAAC,EAAE,OAAO,EAAE,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,WAAW,CAAC,EAAE,CAAC,CAAA;QAC7D,IAAI,CAAC,KAAK,GAAG,SAAS,CAAA;QACtB,IAAI,CAAC,KAAK,GAAG,KAAK,CAAA;QAClB,IAAI,CAAC,aAAa,EAAE,CAAA;QACpB,MAAM,IAAI,CAAC,IAAI,EAAE,CAAA;IACnB,CAAC;IAMO,KAAK,CAAC,MAAc;QAC1B,OAAO,MAAM,IAAI,WAAW;YAC1B,CAAC,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,OAAO,CAAC,MAAM,GAAG,WAAW,CAAC,EAAE;YACvD,CAAC,CAAC,MAAM,GAAG,CAAC;gBACV,CAAC,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,OAAO,CAAC,MAAM,GAAG,WAAW,CAAC,EAAE;gBACxD,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,CAAA;IACvB,CAAC;IAEO,IAAI,CAAC,IAAY,EAAE,EAAU;QACnC,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,EAAE,CAAA;IAClD,CAAC;IAEO,KAAK,CAAC,OAAgB;QAC5B,IAAI,OAAO,KAAK,SAAS;YAAE,OAAO,EAAE,CAAA;QACpC,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,EAAE,CAAC,CAAA;QAClC,MAAM,CAAC,GAAG,OAAO,GAAG,EAAE,CAAA;QACtB,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,oBAAoB,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,YAAY,EAAE,EAAE,CAAC,EAAE,CAAC,CAAA;IACvE,CAAC;IAEO,QAAQ,CAAC,IAAuB;QACtC,OAAO,SAAS,CAAC,IAAI,EAAE,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC,UAAU,GAAG,GAAG,CAAC,EAAE,EAAE,CAAC,WAAW,CAAC,CAAC,CAAA;IACtE,CAAC;IAEO,GAAG,CAAC,MAAmB;QAC7B,MAAM,IAAI,GAAG,UAAU,CAAC,MAAM,CAAC,CAAA;QAC/B,IAAI,CAAC,IAAI;YAAE,OAAO,IAAI,CAAA,yBAAyB,CAAC,CAAC,6BAA6B,CAAC,QAAQ,CAAA;QACvF,MAAM,KAAK,GAAG,CAAC,KAAmC,EAAE,EAAE,CACpD,KAAK,CAAC,IAAI,KAAK,OAAO;YACpB,CAAC,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,QAAQ,CAAC,EAAE;YACpE,CAAC,CAAC,KAAK,CAAC,IAAI,KAAK,KAAK;gBACpB,CAAC,CAAC,CAAC,CAAC,qBAAqB,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,QAAQ,CAAC,EAAE,CAAC;gBACjF,CAAC,CAAC,CAAC,CAAC,yBAAyB,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,QAAQ,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAA;QAE1I,OAAO,IAAI,CAAA;+CACgC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;UACtE,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAA,qCAAqC,IAAI,WAAW,CAAC;UACpF,IAAI,CAAC,MAAM,CAAC,GAAG,CACf,KAAK,CAAC,EAAE,CACN,IAAI,CAAA,gCAAgC,KAAK,CAAC,IAAI,gBAAgB,KAAK,CAAC,IAAI,WAAW,KAAK,CAAC,KAAK,YAAY,KAAK,CAAC,KAAK,CAAC;gBAClH,KAAK,CAAC,IAAI,KAAK,OAAO;YACtB,CAAC,CAAC,IAAI,CAAA,WAAW,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC;0CACL,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,UAAU,GAAG,WAAW,CAAC,GAAG,WAAW,CAAC,GAAG,WAAW,CAAC,IAAI,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,GAAG,WAAW,CAAC,GAAG,WAAW,CAAC,GAAG,WAAW,CAAC,SAAS;YACnL,CAAC,CAAC,OAAO;mBACN,CACV;;;UAGC,IAAI,CAAC,KAAK,CAAC,GAAG,CACd,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CACZ,IAAI,CAAA,qBAAqB,CAAC,GAAG,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,MAAM,GAAG,WAAW,CAAC,GAAG,WAAW,CAAC,GAAG,WAAW,CAAC,SAAS,CAC9G;;WAEE,CAAA;IACT,CAAC;IAED;;;;OAIG;IACK,QAAQ,CAAC,MAAmB,EAAE,YAA6B;QACjE,MAAM,MAAM,GAAG,UAAU,CAAC,MAAM,CAAC,CAAA;QACjC,MAAM,QAAQ,GAAG,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CACtC,gBAAgB,CAAC,KAAK,CAAC,MAAM,CAAC;aAC3B,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,OAAO,CAAC,IAAI,KAAK,KAAK,IAAI,CAAC,OAAO,CAAC,IAAI,KAAK,SAAS,IAAI,CAAC,YAAY,CAAC,CAAC;aAC1F,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC,CACxC,CAAA;QACD,MAAM,QAAQ,GAAG,QAAQ,CAAC,SAAS,CAAC,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,KAAK,KAAK,CAAC,CAAA;QAC5E,MAAM,MAAM,GAAG,CAAC,IAAc,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAA;QACzF,OAAO,IAAI,CAAA;;UAEL,MAAM,CAAC,GAAG,CACV,KAAK,CAAC,EAAE,CACN,IAAI,CAAA,0BAA0B,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC;gBACnD,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAA,yBAAyB,CAAC,CAAC,6BAA6B,CAAC,QAAQ,EAAE,CAC7H;;QAED,QAAQ,CAAC,MAAM,IAAI,YAAY,EAAE,MAAM;YACvC,CAAC,CAAC,IAAI,CAAA;cACA,QAAQ,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,KAAK,EAAE,EAAE,CAC3C,OAAO,CAAC,IAAI,KAAK,KAAK;gBACpB,CAAC,CAAC,IAAI,CAAA;;yBAEG,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,qBAAqB,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,UAAW,EAAE,OAAO,CAAC,QAAS,CAAC,EAAE,CAAC;wBAC3G,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAA,qBAAqB,CAAC,CAAC,oBAAoB,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO;;wBAExF;gBACR,CAAC,CAAC,IAAI,CAAA;;yBAEG,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,yBAAyB,EAAE;oBACnD,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,UAAW,EAAE,OAAO,CAAC,QAAS,CAAC;oBACvD,KAAK,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC;oBACvB,MAAM,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC;iBACzB,CAAC;;wBAEA,CACX;cACC,CAAC,YAAY,IAAI,EAAE,CAAC,CAAC,GAAG,CACxB,OAAO,CAAC,EAAE,CACR,IAAI,CAAA;;uBAEG,CAAC,CAAC,4BAA4B,EAAE;gBACjC,IAAI,EAAE,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC;gBACjC,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,OAAO,CAAC,QAAQ,CAAC;gBACrD,KAAK,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC;gBACvB,MAAM,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC;aACzB,CAAC;;sBAEA,CACT;gBACG;YACR,CAAC,CAAC,OAAO;KACZ,CAAA;IACH,CAAC;IAEO,UAAU,CAAC,MAAgD;QACjE,OAAO,IAAI,CAAA;;;;kBAIG,CAAC,CAAC,aAAa,CAAC;kBAChB,CAAC,CAAC,mBAAmB,CAAC;kBACtB,EAAE,CAAC,YAAY,CAAC;;;;YAItB,MAAM,CAAC,GAAG,CACV,KAAK,CAAC,EAAE,CACN,IAAI,CAAA;sBACI,KAAK,CAAC,IAAI,GAAG,KAAK,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,CAAA,sBAAsB,KAAK,CAAC,WAAW,QAAQ,CAAC,CAAC,CAAC,OAAO;qCAC/E,SAAS,CAAC,KAAK,EAAE,IAAI,CAAC,OAAO,CAAC,0BAA0B,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;sBAClG,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;oBACzC,CACT;;;WAGA,CAAA;IACT,CAAC;IAEO,UAAU,CAAC,SAAsB,EAAE;QACzC,OAAO,IAAI,CAAA;QACP,MAAM,CAAC,GAAG,CACV,KAAK,CAAC,EAAE,CACN,IAAI,CAAA;cACA,KAAK,CAAC,IAAI,yBAAyB,SAAS,CAAC,KAAK,EAAE,IAAI,CAAC,OAAO,CAAC;cACjE,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAA,uBAAuB,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO;iBAClI,CACV;WACI,CAAA;IACT,CAAC;IAEO,QAAQ,CAAC,KAAa;QAC5B,OAAO,IAAI,CAAA,kCAAkC,KAAK,IAAI,EAAE,CAAC,KAAK,CAAC,SAAS,CAAA;IAC1E,CAAC;IAEO,MAAM,CAAC,IAAoB;QACjC,IAAI,CAAC,IAAI,CAAC,MAAM;YAAE,OAAO,IAAI,CAAA,uBAAuB,CAAC,CAAC,gBAAgB,EAAE,EAAE,IAAI,EAAE,YAAY,CAAC,IAAI,CAAC,GAAG,CAAC,aAAa,CAAC,EAAE,CAAC,SAAS,CAAA;QAChI,OAAO,IAAI,CAAC,MAAM,CAAC,EAAE;YACnB,CAAC,CAAC,GAAG,YAAY,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,YAAY,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE;YACvE,CAAC,CAAC,CAAC,CAAC,gBAAgB,EAAE,EAAE,IAAI,EAAE,YAAY,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;IACnE,CAAC;IAED,0EAA0E;IAElE,WAAW,CAAC,MAAsB;QACxC,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAA;QAC9B,MAAM,MAAM,GAAgB,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,YAAY,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAA;QAC5E,OAAO,IAAI,CAAA;gDACiC,CAAC,CAAC,qBAAqB,CAAC;QAChE,OAAO;YACP,CAAC,CAAC,IAAI,CAAA;cACA,CAAC,CAAC,gBAAgB,EAAE,EAAE,IAAI,EAAE,YAAY,CAAC,OAAO,CAAC,MAAO,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,OAAO,CAAC,GAAG,CAAC,QAAQ;eAC1F;YACP,CAAC,CAAC,OAAO;QACT,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,IAAI,MAAM,CAAC,MAAM;YAC5C,CAAC,CAAC,IAAI,CAAA;cACA,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,GAAG,CAAC,MAAM,IAAI,MAAM,CAAC,IAAI,IAAI,CAAC,UAAU,CAAC,OAAO,EAAE,GAAG,CAAC,MAAM,IAAI,MAAM,CAAC;iBAC3F;YACT,CAAC,CAAC,IAAI,CAAA;qBACO,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE;yBACnB,EAAE,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,CAAC,CAAC,yBAAyB,CAAC,EAAE;4BACtD;eACb,CAAA;IACb,CAAC;IAEO,aAAa,CAAC,MAAsB;QAC1C,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,MAAM;YAAE,OAAO,OAAO,CAAA;QAC5C,OAAO,IAAI,CAAA;gDACiC,CAAC,CAAC,2BAA2B,CAAC;QACtE,MAAM,CAAC,SAAS,CAAC,GAAG;QACpB,oFAAoF;QACpF,IAAI,CAAC,EAAE,CAAC,IAAI,CAAA;cACN,CAAC,CAAC,gBAAgB,EAAE,EAAE,IAAI,EAAE,YAAY,CAAC,IAAI,CAAC,MAAO,CAAC,IAAI,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE;;qCAExF,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,IAAI,EAAE,CAAC,QAAQ,CAC1E;eACQ,CAAA;IACb,CAAC;IAED,4GAA4G;IACpG,UAAU,CAAC,GAAgB,EAAE,QAA+B;QAClE,OAAO,IAAI,CAAA;QACP,QAAQ,CAAC,GAAG,EAAE,CAAC,CAAC,qBAAqB,CAAC,CAAC;QACvC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAA,sBAAsB,CAAC,CAAC,yBAAyB,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO;QAC1F,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC,IAAI,CAAA,sBAAsB,QAAQ,CAAC,OAAO,QAAQ,CAAC,CAAC,CAAC,OAAO;UAC9E,CAAA;IACR,CAAC;IAEO,QAAQ,CAAC,MAAsB;QACrC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM;YAAE,OAAO,OAAO,CAAA;QACvC,OAAO,IAAI,CAAA;gDACiC,CAAC,CAAC,0BAA0B,CAAC;;;;;oBAKzD,EAAE,CAAC,OAAO,CAAC;oBACX,EAAE,CAAC,eAAe,CAAC;oBACnB,CAAC,CAAC,aAAa,CAAC;oBAChB,EAAE,CAAC,QAAQ,CAAC;;;;;cAKlB,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE;YACvB,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;YAC5C,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAA;YACxC,OAAO,IAAI,CAAA;wBACD,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC;uCACV,YAAY,CAAC,IAAI,CAAC,GAAG,CAAC,aAAa,CAAC;wBACnD,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC;oBACpC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,GAAG,EAAE,QAAQ,CAAC;;;wBAG/B,IAAI,CAAC,KAAK,KAAK,OAAO;gBACtB,CAAC,CAAC,IAAI,CAAA,wBAAwB,GAAG,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,yBAAyB,CAAC,WAAW;gBACpG,CAAC,CAAC,IAAI,CAAA,8BAA8B,GAAG,EAAE,CAAC,CAAC,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;8BAClF,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,SAAS,CAAC;oCAClC;wBACZ,QAAQ,EAAE,IAAI,CAAC,CAAC,CAAC,IAAI,CAAA,wBAAwB,GAAG,EAAE,CAAC,QAAQ,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,iBAAiB,CAAC,WAAW,CAAC,CAAC,CAAC,OAAO;;;;kBAItH,IAAI;gBACJ,CAAC,CAAC,IAAI,CAAA;;;;oCAIY,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAc,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,IAAI,CAAC,GAAG,CAAC,cAAc,CAAC;mCACnF,IAAI,CAAC,GAAG;;;0BAGjB;gBACR,CAAC,CAAC,OAAO,EAAE,CAAA;QACjB,CAAC,CAAC;;;;eAIC,CAAA;IACb,CAAC;IAEO,WAAW,CAAC,MAAsB;QACxC,OAAO,IAAI,CAAA;gDACiC,CAAC,CAAC,qBAAqB,CAAC;QAChE,MAAM,CAAC,OAAO,CAAC,MAAM;YACrB,CAAC,CAAC,IAAI,CAAA;;;;wBAIU,CAAC,CAAC,uBAAuB,CAAC;wBAC1B,EAAE,CAAC,OAAO,CAAC;wBACX,CAAC,CAAC,oBAAoB,CAAC;wBACvB,CAAC,CAAC,aAAa,CAAC;wBAChB,EAAE,CAAC,QAAQ,CAAC;;;;;kBAKlB,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE;gBAC1B,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;gBAC5C,OAAO,IAAI,CAAA;yCACY,IAAI,CAAC,GAAG,CAAC,cAAc;0BACtC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC;yCACV,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC;0BAChC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC;sBACpC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,GAAG,EAAE,QAAQ,CAAC;;;0BAG/B,QAAQ,EAAE,IAAI,CAAC,CAAC,CAAC,IAAI,CAAA,8BAA8B,GAAG,EAAE,CAAC,QAAQ,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,iBAAiB,CAAC,WAAW,CAAC,CAAC,CAAC,OAAO;+CACjG,GAAG,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,yBAAyB,CAAC;;;wBAGjF,CAAA;YACR,CAAC,CAAC;;;iBAGD;YACT,CAAC,CAAC,IAAI,CAAA,yBAAyB,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,eAAe,EAAE,KAAK,EAAE,CAAC,CAAC,uBAAuB,CAAC,EAAE,mBAAmB;eACtH,CAAA;IACb,CAAC;IAED,0EAA0E;IAElE,SAAS,CAAC,KAAe;QAC/B,OAAO,CAAC,CAAC,mBAAmB,GAAG,KAAK,CAAC,CAAA;IACvC,CAAC;IAEO,QAAQ,CAAC,KAAoB,EAAE,KAAa,EAAE,MAAkB;QACtE,MAAM,KAAK,GAAG,IAAI,CAAC,KAAM,CAAA;QACzB,MAAM,OAAO,GAAG,MAAM,CAAC,QAAQ,CAAC,cAAc,CAAC,IAAI,MAAM,CAAC,QAAQ,CAAC,gBAAgB,CAAC,CAAA;QACpF,MAAM,OAAO,GAAG,MAAM,CAAC,QAAQ,CAAC,cAAc,CAAC,IAAI,MAAM,CAAC,QAAQ,CAAC,eAAe,CAAC,CAAA;QACnF,MAAM,MAAM,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAA;QAC9B,OAAO,IAAI,CAAA;;mCAEoB,EAAE,CAAC,MAAM,CAAC;;mBAE1B,KAAK,CAAC,IAAI,IAAI,EAAE;uBACZ,EAAE,CAAC,MAAM,CAAC;yBACR,OAAO,IAAI,CAAC,IAAI,CAAC,KAAK,IAAI,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO;sBAC7D,IAAI,CAAC,IAAI;mBACZ,CAAC,CAAM,EAAE,EAAE,CAAC,IAAI,CAAC,WAAW,CAAC,KAAK,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;;;;mCAI7C,CAAC,CAAC,mBAAmB,CAAC;;;yBAGhC,EAAE,CAAC,UAAU,CAAC;wBACf,IAAI,CAAC,IAAI;sBACX,CAAC,CAAM,EAAE,EAAE,CAAC,IAAI,CAAC,WAAW,CAAC,KAAK,EAAE,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;;cAEjF,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,CAAA,iBAAiB,GAAG,cAAc,MAAM,CAAC,KAAK,CAAC,QAAQ,IAAI,CAAC,CAAC,KAAK,GAAG,IAAI,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC;;;;qBAIzH,MAAM,CAAC,KAAK,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC;yBACpC,EAAE,CAAC,UAAU,CAAC;2BACZ,OAAO,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO;wBAC3C,IAAI,CAAC,IAAI;qBACZ,CAAC,CAAM,EAAE,EAAE,CAAC,IAAI,CAAC,WAAW,CAAC,KAAK,EAAE,EAAE,QAAQ,EAAE,CAAC,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;;;;;mCAKnD,CAAC,CAAC,iBAAiB,CAAC;;;;qBAIlC,MAAM,CAAC,KAAK,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC;yBAClC,EAAE,CAAC,QAAQ,CAAC;2BACV,OAAO,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO;wBAC3C,IAAI,CAAC,IAAI;qBACZ,CAAC,CAAM,EAAE,EAAE,CAAC,IAAI,CAAC,WAAW,CAAC,KAAK,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;;;eAGrE,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC;;;;;mCAKvD,EAAE,CAAC,YAAY,CAAC;;;uBAG5B,EAAE,CAAC,YAAY,CAAC;qBAClB,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,EAAE,CAAC,UAAU,GAAG,GAAG,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC;mBAClE,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC;sBACrB,IAAI,CAAC,IAAI;oBACX,CAAC,CAAM,EAAE,EAAE;YACnB,MAAM,KAAK,GAAG,SAAS,CAAC,CAAC,CAAC,MAAM,IAAI,EAAE,CAAC,CAAA;YACvC,iEAAiE;YACjE,IAAI,KAAK,KAAK,SAAS;gBAAE,CAAC,CAAC,MAAM,CAAC,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,CAAA;;gBAC7D,IAAI,CAAC,WAAW,CAAC,KAAK,EAAE,EAAE,UAAU,EAAE,KAAK,EAAE,CAAC,CAAA;QACrD,CAAC;;;;mCAIwB,EAAE,CAAC,aAAa,CAAC;;mBAEjC,KAAK,CAAC,WAAW,IAAI,EAAE;uBACnB,EAAE,CAAC,aAAa,CAAC;sBAClB,IAAI,CAAC,IAAI;mBACZ,CAAC,CAAM,EAAE,EAAE,CAAC,IAAI,CAAC,WAAW,CAAC,KAAK,EAAE,EAAE,WAAW,EAAE,CAAC,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;;;;;;;;sBAQjE,IAAI,CAAC,IAAI,IAAI,KAAK,CAAC,MAAM,CAAC,MAAM,KAAK,CAAC;uBACrC,GAAG,EAAE,CAAC,QAAQ,CAAC,IAAI,KAAK,CAAC,IAAI,IAAI,KAAK,GAAG,CAAC,EAAE;mBAChD,GAAG,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,KAAK,CAAC,EAAE,CAAC;;YAEhF,EAAE,CAAC,QAAQ,CAAC;;;QAGhB,MAAM,CAAC,MAAM,IAAI,CAAC,IAAI,CAAC,KAAK,IAAI,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,KAAK,gBAAgB,IAAI,KAAK,KAAK,eAAe,CAAC,CAAC;YAC9G,CAAC,CAAC,IAAI,CAAA,yBAAyB,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM;YAC3F,CAAC,CAAC,OAAO;WACN,CAAA;IACT,CAAC;IAEO,MAAM;QACZ,MAAM,KAAK,GAAG,IAAI,CAAC,KAAM,CAAA;QACzB,MAAM,MAAM,GAAG,WAAW,CAAC,KAAK,CAAC,CAAA;QACjC,MAAM,SAAS,GAAc,IAAY,CAAC,iBAAiB,EAAE,CAAC,UAAU,CAAC,IAAI,EAAE,CAAA;QAC/E,MAAM,KAAK,GAAG,SAAS,CAAC,QAAQ,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,EAAE,GAAG,SAAS,CAAC,CAAA;QAC7F,OAAO,IAAI,CAAA;QACP,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAA,iCAAiC,IAAI,CAAC,SAAS,MAAM,CAAC,CAAC,CAAC,OAAO;;;gBAG5E,KAAK,CAAC,cAAc,CAAC,CAAC,CAAC,EAAE,CAAC,gBAAgB,EAAE,EAAE,CAAC,EAAE,KAAK,CAAC,cAAc,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,6BAA6B,CAAC;;gCAE3F,IAAI,CAAC,IAAI,WAAW,GAAG,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,SAAS,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC;gBAC7G,EAAE,CAAC,WAAW,CAAC;;;;;;oBAMX,EAAE,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAC,mBAAmB,CAAC,gBAAgB,CAAC,CAAC,iBAAiB,CAAC;qBACnF,EAAE,CAAC,YAAY,CAAC,gBAAgB,EAAE,CAAC,aAAa,CAAC;;YAE1D,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE,KAAK,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;;;YAG7F,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,MAAM,EAAE,aAAa,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;;;;;kDAKrB,CAAC,CAAC,mBAAmB,CAAC;;;oBAGpD,EAAE,CAAC,eAAe,CAAC;;;2BAGZ;YACT,EAAE,OAAO,EAAE,CAAC,CAAC,gBAAgB,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE;YAC9C,EAAE,OAAO,EAAE,CAAC,CAAC,kBAAkB,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE;SAChD;yBACQ,KAAK,CAAC,aAAa;4BAChB,IAAI,CAAC,IAAI;0BACX,CAAC,CAAM,EAAE,EAAE;YACnB,IAAI,CAAC,CAAC,CAAC,MAAM;gBAAE,CAAC,CAAC,MAAM,CAAC,KAAK,GAAG,KAAK,CAAC,aAAa,CAAA;;gBAC9C,IAAI,CAAC,MAAM,CAAC,EAAE,aAAa,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,CAAA;QAC/C,CAAC;;gBAED,KAAK,CAAC,aAAa,KAAK,IAAI;YAC5B,CAAC,CAAC,IAAI,CAAA;;iCAEW,EAAE,CAAC,eAAe,CAAC;6BACvB,aAAa,CAAC,KAAK,CAAC,aAAa,CAAC;gCAC/B,IAAI,CAAC,IAAI;8BACX,CAAC,CAAM,EAAE,EAAE;gBACnB,IAAI,CAAC;oBACH,IAAI,CAAC,MAAM,CAAC,EAAE,aAAa,EAAE,YAAY,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAA;gBAC9D,CAAC;gBAAC,MAAM,CAAC;oBACP,CAAC,CAAC,MAAM,CAAC,KAAK,GAAG,aAAa,CAAC,KAAK,CAAC,aAAa,CAAC,CAAA;gBACrD,CAAC;YACH,CAAC;qBACA;YACL,CAAC,CAAC,OAAO;;;;oBAIL,EAAE,CAAC,QAAQ,CAAC;;uBAET,KAAK,CAAC,MAAM;6BACN,MAAM,CAAC,aAAa,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO;0BACxD,IAAI,CAAC,IAAI;uBACZ,CAAC,CAAM,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;;cAE5D,MAAM,CAAC,aAAa,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAA,uBAAuB,CAAC,CAAC,iCAAiC,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO;;;oBAGjH,EAAE,CAAC,UAAU,CAAC;gCACF,IAAI,CAAC,IAAI,YAAY,CAAC,CAAM,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;gBAC1F,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAA,iBAAiB,IAAI,cAAc,IAAI,KAAK,KAAK,CAAC,QAAQ,IAAI,IAAI,WAAW,CAAC;;;;oBAIhG,EAAE,CAAC,aAAa,CAAC;4BACT,KAAK,CAAC,WAAW,IAAI,EAAE,cAAc,IAAI,CAAC,IAAI,WAAW,CAAC,CAAM,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,WAAW,EAAE,CAAC,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;;;;KAIxI,CAAA;IACH,CAAC;IAED,MAAM;QACJ,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAA;QACtD,OAAO,IAAI,CAAA,2BAA2B,IAAI,CAAC,OAAO,IAAI,CAAC,IAAI,CAAC,QAAQ,WAAW,IAAI,CAAC,KAAK,WAAW,GAAG,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE;;UAE/G,MAAM;YACN,CAAC,CAAC,IAAI,CAAC,KAAK;gBACV,CAAC,CAAC,IAAI,CAAC,MAAM,EAAE;gBACf,CAAC,CAAC,IAAI,CAAA,GAAG,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,EAAE;YACxH,CAAC,CAAC,OAAO;;qBAEE,CAAA;IACnB,CAAC;;AAhwBgB;IAAhB,KAAK,EAAE;;2CAAsB;AACb;IAAhB,KAAK,EAAE;;0CAAwB;AACf;IAAhB,KAAK,EAAE;;wCAA8B;AACrB;IAAhB,KAAK,EAAE;;4CAA4C;AACnC;IAAhB,KAAK,EAAE;;wCAAsB;AACb;IAAhB,KAAK,EAAE;;wCAAsB;AACb;IAAhB,KAAK,EAAE;;uCAAqB;AACZ;IAAhB,KAAK,EAAE;;4CAAuB;AAEd;IAAhB,KAAK,EAAE;;wCAAsB;AACb;IAAhB,KAAK,EAAE;;yCAAwB;AA/WrB,SAAS;IADrB,aAAa,CAAC,YAAY,CAAC;GACf,SAAS,CAsmCrB","sourcesContent":["import '@operato/input/ox-input-select-buttons.js'\nimport '@operato/list-state/ox-list-state.js'\nimport '@operato/list-state/ox-page-state.js'\n\nimport gql from 'graphql-tag'\nimport { css, html, nothing } from 'lit'\nimport { customElement, state } from 'lit/decorators.js'\nimport { client } from '@operato/graphql'\nimport { i18next, localize } from '@operato/i18n'\nimport { notify } from '@operato/layout'\nimport { OxPrompt } from '@operato/popup'\nimport { PageView } from '@operato/shell'\nimport { ButtonStyles, DocumentPageStyles, FormFieldStyles, ScrollbarStyles } from '@operato/styles'\nimport { revisionText as rt, revisionDate } from '@things-factory/ops-master/dist-client/pages/master/revision-comparison.js'\nimport { localDateTime, isoFromLocal } from '@things-factory/ops-master/dist-client/pages/master/revision-view.js'\n\nimport { clockOf, coverageProblems, timelineOf } from '../shift-coverage'\nimport type { ShiftLike } from '../shift-coverage'\nimport { daysLabel, daysOf, daysValue, draftIssues, groupOverlaps, lengthOf, nextShift, settle, shiftsContent, weekGroups, WEEK } from '../shift-editing'\nimport type { EditableShift, OverlapGroup, RowIssue } from '../shift-editing'\nimport { isBaseline, placeRevisions, reasonOf, timeRange } from '../shift-revisions'\nimport type { PlacedOverview, PlacedRevision, RevisionRow } from '../shift-revisions'\n\nconst DAY_MINUTES = 24 * 60\n\ninterface Draft {\n id?: string\n editGeneration?: number\n revisionNumber?: number\n description: string\n timezone: string\n shifts: EditableShift[]\n /** \"now\" is read when the change is saved, not when the editor opened. */\n effectiveMode: 'now' | 'at'\n effectiveFrom: string\n reason: string\n}\n\nconst t = (key: string, params: any = {}) => String(i18next.t(key, params))\n\n/**\n * 근무 교대 — the shift arrangement in force, what is about to change, and what it was before.\n *\n * One arrangement is in force at a time. Changing it means writing a whole new arrangement with a reason and\n * the moment it takes effect; where the site requires approval, the change goes through ops-master's approval\n * and takes effect once approved. Work already recorded keeps the shift it was recorded under.\n *\n * The rules this screen applies - weekday groups, the end day of a shift, what blocks a save, where each\n * revision belongs - live in shift-editing.ts, shift-revisions.ts and shift-coverage.ts, with tests.\n */\n@customElement('work-shift')\nexport class WorkShift extends localize(i18next)(PageView) {\n static styles = [\n FormFieldStyles,\n ButtonStyles,\n DocumentPageStyles,\n ScrollbarStyles,\n css`\n .arrangement {\n display: flex;\n flex-direction: column;\n gap: var(--spacing-large);\n }\n\n /* ── the day bar ─────────────────────────────────────────────── */\n .day-groups {\n display: grid;\n grid-template-columns: max-content minmax(0, 1fr);\n gap: var(--spacing-large) var(--spacing-large);\n align-items: start;\n }\n .day-label {\n padding-top: 7px;\n color: var(--ui-muted);\n font-size: var(--ui-t-small);\n font-weight: 600;\n white-space: nowrap;\n }\n .day-none {\n padding-top: 7px;\n color: var(--ui-muted);\n }\n .bar {\n position: relative;\n height: 32px;\n border-radius: var(--md-sys-shape-corner-small, 4px);\n background: var(--md-sys-color-surface-container-low);\n overflow: hidden;\n }\n .piece {\n position: absolute;\n inset-block: 0;\n box-sizing: border-box;\n display: flex;\n align-items: center;\n gap: var(--spacing-small);\n min-width: 0;\n padding-inline: var(--spacing-medium);\n overflow: hidden;\n white-space: nowrap;\n font-size: var(--ui-t-small);\n }\n .piece[data-kind='shift'] {\n border-inline-start: 3px solid var(--ui-accent, var(--md-sys-color-primary));\n border-inline-end: 1px solid var(--md-sys-color-surface-container-lowest);\n background: color-mix(in srgb, var(--ui-accent, var(--md-sys-color-primary)) 18%, var(--md-sys-color-surface-container-lowest));\n color: var(--ui-ink);\n }\n .piece[data-kind='shift'] strong {\n font-weight: 600;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n .piece .time {\n color: var(--ui-muted);\n font-variant-numeric: tabular-nums;\n }\n .piece[data-kind='gap'] {\n background: repeating-linear-gradient(\n -45deg,\n transparent 0 5px,\n color-mix(in srgb, var(--ui-warn) 35%, transparent) 5px 6px\n );\n }\n .piece[data-kind='overlap'] {\n background: repeating-linear-gradient(\n -45deg,\n color-mix(in srgb, var(--ui-crit) 30%, transparent) 0 4px,\n transparent 4px 7px\n );\n }\n .midnight {\n position: absolute;\n inset-block: 0;\n border-inline-start: 1px dashed var(--ui-muted);\n }\n .axis {\n position: relative;\n height: 16px;\n margin-top: 2px;\n color: var(--ui-muted);\n font-size: var(--ui-t-micro);\n font-variant-numeric: tabular-nums;\n }\n .axis span {\n position: absolute;\n white-space: nowrap;\n transform: translateX(-50%);\n }\n .axis span:first-child {\n transform: none;\n }\n .axis span:last-child {\n transform: translateX(-100%);\n }\n\n .findings {\n display: grid;\n gap: var(--spacing-small);\n margin: 0;\n padding: 0;\n list-style: none;\n }\n .findings li {\n display: flex;\n gap: var(--spacing-medium);\n font-variant-numeric: tabular-nums;\n }\n .findings li::before {\n content: '';\n flex: none;\n width: 3px;\n border-radius: 2px;\n background: var(--ui-warn);\n }\n .findings li[data-kind='overlap']::before,\n .findings li[data-kind='error']::before {\n background: var(--ui-crit);\n }\n .findings .why {\n display: block;\n color: var(--ui-muted);\n font-size: var(--ui-t-small);\n }\n\n /* ── tables: the grid's look (page-standard §2-1) ───────────────── */\n .table-scroll {\n overflow-x: auto;\n }\n table {\n width: 100%;\n border-collapse: collapse;\n font-size: var(--md-sys-typescale-label-large-size, 14px);\n }\n th {\n padding: var(--spacing-small) var(--spacing-medium);\n border-top: 2px solid var(--md-sys-color-primary);\n border-bottom: 1px solid var(--md-sys-color-outline-variant);\n background: var(--md-sys-color-surface-container-low);\n color: var(--md-sys-color-primary);\n font-weight: 500;\n text-align: start;\n white-space: nowrap;\n }\n td {\n padding: var(--spacing-medium);\n border-bottom: 1px solid var(--md-sys-color-outline-variant);\n vertical-align: top;\n }\n td.figure,\n .figure {\n font-variant-numeric: tabular-nums;\n white-space: nowrap;\n }\n td.reason {\n min-width: 16em;\n }\n td.actions {\n width: 1%;\n white-space: nowrap;\n }\n td.actions > div {\n display: flex;\n justify-content: flex-end;\n gap: var(--spacing-small);\n }\n .muted {\n color: var(--ui-muted);\n }\n .shift-lines {\n display: grid;\n gap: 2px;\n }\n .shift-lines > div {\n white-space: nowrap;\n }\n .shift-lines .figure {\n color: var(--ui-muted);\n }\n .state {\n display: inline-block;\n padding: 1px var(--spacing-medium);\n border-radius: var(--md-sys-shape-corner-small, 4px);\n background: var(--md-sys-color-surface-container-high);\n font-size: var(--ui-t-micro);\n font-weight: 600;\n white-space: nowrap;\n }\n .state[data-state='current'] {\n background: color-mix(in srgb, var(--ui-good) 18%, transparent);\n }\n .state[data-state='scheduled'],\n .state[data-state='pending'],\n .state[data-state='approved'] {\n background: color-mix(in srgb, var(--ui-info, var(--md-sys-color-primary)) 18%, transparent);\n }\n .state[data-state='rejected'] {\n background: color-mix(in srgb, var(--ui-crit) 16%, transparent);\n }\n tr.detail > td {\n background: var(--md-sys-color-surface-container-lowest);\n }\n\n /* ── the editor ──────────────────────────────────────────────── */\n .shift-rows {\n container-type: inline-size;\n display: grid;\n margin-bottom: var(--spacing-large);\n }\n .shift-row {\n display: grid;\n grid-template-columns:\n minmax(110px, 1fr) minmax(184px, 1.1fr) minmax(190px, 1.1fr) minmax(392px, max-content)\n minmax(110px, 1fr) max-content;\n gap: var(--spacing-medium) var(--spacing-large);\n align-items: start;\n padding: var(--spacing-medium) 0;\n border-bottom: 1px solid var(--md-sys-color-outline-variant);\n }\n .shift-row.head {\n padding-block: var(--spacing-small);\n border-top: 2px solid var(--md-sys-color-primary);\n color: var(--md-sys-color-primary);\n font-weight: 500;\n }\n .shift-row input:not([type='checkbox']),\n .shift-row select {\n width: 100%;\n min-width: 0;\n }\n .cell {\n display: grid;\n gap: var(--spacing-small);\n min-width: 0;\n }\n .cell-label {\n display: none;\n color: var(--ui-muted);\n font-size: var(--ui-t-micro);\n }\n .clock {\n display: flex;\n align-items: center;\n gap: var(--spacing-small);\n }\n .clock select {\n flex: none;\n width: 72px !important;\n }\n .clock input {\n flex: 1;\n }\n .derived {\n color: var(--ui-muted);\n font-size: var(--ui-t-small);\n font-variant-numeric: tabular-nums;\n white-space: nowrap;\n }\n .row-issues {\n grid-column: 1 / -1;\n margin: 0;\n color: var(--ui-crit);\n font-size: var(--ui-t-small);\n }\n .shift-row input[aria-invalid='true'] {\n border-color: var(--ui-crit);\n }\n ox-input-select-buttons {\n --md-chip-set-gap: 4px;\n }\n @container (max-width: 1080px) {\n .shift-row {\n grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);\n }\n .shift-row.head {\n display: none;\n }\n .cell-label {\n display: block;\n }\n .cell.name,\n .cell.days,\n .cell.note {\n grid-column: 1 / -1;\n }\n .cell.remove {\n grid-column: 1 / -1;\n justify-items: start;\n }\n }\n @container (max-width: 480px) {\n .shift-row {\n grid-template-columns: minmax(0, 1fr);\n }\n }\n\n .form-grid {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(min(100%, 280px), 1fr));\n gap: var(--spacing-large);\n }\n .field {\n display: grid;\n align-content: start;\n gap: var(--spacing-small);\n min-width: 0;\n }\n .field > span:first-child {\n color: var(--ui-muted);\n font-size: var(--ui-t-micro);\n }\n .field input,\n .field select {\n width: 100%;\n }\n .field .error {\n color: var(--ui-crit);\n font-size: var(--ui-t-small);\n }\n .when {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--spacing-medium);\n }\n .when input {\n flex: 1;\n min-width: 200px;\n }\n .alert {\n margin: 0;\n padding: var(--spacing-medium) var(--spacing-large);\n border-inline-start: 3px solid var(--ui-crit);\n background: color-mix(in srgb, var(--ui-crit) 8%, transparent);\n }\n\n @media (max-width: 600px) {\n .day-groups {\n grid-template-columns: minmax(0, 1fr);\n gap: var(--spacing-small);\n }\n .piece .time {\n display: none;\n }\n }\n `\n ]\n\n @state() private overview: any\n @state() private loading = false\n @state() private error: unknown = null\n @state() private approvals: Record<string, any> = {}\n @state() private draft?: Draft\n @state() private dirty = false\n @state() private busy = false\n @state() private saveError = ''\n /** A save was tried: from then on every blocking problem is shown, not only the ones a person touched. */\n @state() private tried = false\n @state() private opened?: string\n\n get context() {\n return {\n title: t('title.work-shift'),\n frame: 'document',\n help: 'page/work-shift',\n actions: this.actions()\n }\n }\n\n private actions() {\n if (!this.overview || this.error) return []\n if (!this.draft) {\n return [{ title: t('button.change-shifts'), icon: 'edit', emphasis: { raised: true }, action: () => this.begin() }]\n }\n const approval = this.overview.approvalRequired\n return [\n { title: t('button.cancel'), action: () => this.close() },\n ...(approval ? [{ title: rt('save-draft'), action: () => this.saveDraft() }] : []),\n {\n title: approval ? rt('request-approval') : t('button.save'),\n icon: approval ? 'send' : 'save',\n emphasis: { raised: true },\n action: () => this.commit()\n }\n ]\n }\n\n async pageUpdated(changes: any) {\n if (this.active && changes.active && !this.draft) await this.load()\n }\n\n /* ── data ──────────────────────────────────────────────────────────── */\n\n private async load() {\n this.loading = true\n this.error = null\n try {\n const result = await client.query({\n query: gql`\n query {\n workShiftRevisionOverview\n }\n `,\n fetchPolicy: 'no-cache'\n })\n if (result.errors?.length) throw result.errors[0]\n this.overview = result.data.workShiftRevisionOverview\n this.approvals = await this.readApprovals(this.overview.revisions)\n } catch (e) {\n this.error = e\n } finally {\n this.loading = false\n this.updateContext()\n }\n }\n\n /** Approval states come from ops-master's approval flow, one revision at a time. */\n private async readApprovals(revisions: RevisionRow[]) {\n const approvals: Record<string, any> = {}\n for (const row of revisions.filter(r => r.activityInstanceId)) {\n let promise: Promise<any> | undefined\n document.dispatchEvent(\n new CustomEvent('master-change-status', {\n detail: { activityInstanceId: row.activityInstanceId, respondWith: (p: Promise<any>) => (promise = p) }\n })\n )\n if (promise) approvals[row.id] = await promise.catch(() => ({ state: 'unknown' }))\n }\n return approvals\n }\n\n private get placed(): PlacedOverview {\n return placeRevisions(this.overview ?? { revisions: [], schedule: [] }, this.approvals)\n }\n\n /* ── editing ───────────────────────────────────────────────────────── */\n\n private begin(from?: RevisionRow) {\n const base = from ?? this.placed.current?.row\n const shifts: EditableShift[] = base?.shifts ?? this.overview.legacyShifts ?? []\n this.draft = {\n description: base?.description ?? '',\n timezone: base?.timezone ?? this.overview.timezone,\n shifts: shifts.length ? shifts.map(shift => settle({ ...shift })) : [nextShift([])],\n effectiveMode: 'now',\n effectiveFrom: new Date().toISOString(),\n reason: ''\n }\n this.enterEditor()\n }\n\n private resume(row: RevisionRow) {\n this.draft = {\n id: row.id,\n editGeneration: row.editGeneration,\n revisionNumber: row.revisionNumber,\n description: row.description ?? '',\n timezone: row.timezone ?? this.overview.timezone,\n shifts: (row.shifts ?? []).map(shift => settle({ ...shift })),\n effectiveMode: 'at',\n effectiveFrom: row.effectiveFrom ?? new Date().toISOString(),\n reason: row.reason ?? ''\n }\n this.enterEditor()\n }\n\n private enterEditor() {\n this.dirty = false\n this.tried = false\n this.saveError = ''\n this.updateContext()\n /* The document frame scrolls the page element itself. */\n this.scrollTo({ top: 0 })\n }\n\n private async close() {\n if (this.busy) return\n if (\n this.dirty &&\n !(await OxPrompt.open({\n type: 'warning',\n title: rt('discard-question'),\n confirmButton: { text: rt('discard'), destructive: true },\n cancelButton: { text: rt('keep-editing') }\n }))\n )\n return\n this.draft = undefined\n this.dirty = false\n this.updateContext()\n await this.load()\n }\n\n private change(patch: Partial<Draft>) {\n this.draft = { ...this.draft!, ...patch }\n this.dirty = true\n }\n\n private changeShift(index: number, patch: Partial<EditableShift>) {\n this.change({ shifts: this.draft!.shifts.map((s, i) => (i === index ? settle({ ...s, ...patch }) : s)) })\n }\n\n private content() {\n const { description, timezone, shifts, effectiveMode, effectiveFrom, reason } = this.draft!\n return {\n description,\n timezone,\n shifts: shiftsContent(shifts),\n effectiveFrom: effectiveMode === 'now' ? new Date().toISOString() : effectiveFrom,\n reason: reason.trim()\n }\n }\n\n private async run<T>(work: () => Promise<T>): Promise<T | undefined> {\n if (this.busy) return undefined\n this.busy = true\n this.saveError = ''\n try {\n return await work()\n } catch (e: any) {\n /* The GraphQL client already announces server refusals and network failures; say only what it does not. */\n if (!(e?.reported || e?.graphQLErrors?.length || e?.networkError)) this.saveError = e?.message ?? String(e)\n return undefined\n } finally {\n this.busy = false\n }\n }\n\n private async mutate(mutation: any, variables: any, field: string) {\n const response = await client.mutate({ mutation, variables })\n if (response.errors?.length) throw Object.assign(new Error(response.errors[0].message), { reported: true })\n return response.data[field]\n }\n\n /** Saves the editor as a draft and keeps it open. Returns false when it did not save. */\n private async saveDraft(quiet = false): Promise<boolean> {\n this.tried = true\n if (draftIssues(this.draft!).blocking) return false\n const saved = await this.run(async () => {\n const draft = this.draft!\n const row = draft.id\n ? await this.mutate(\n gql`\n mutation ($revisionId: String!, $editGeneration: Int!, $content: Object!) {\n editWorkShiftDraft(revisionId: $revisionId, editGeneration: $editGeneration, content: $content)\n }\n `,\n { revisionId: draft.id, editGeneration: draft.editGeneration, content: this.content() },\n 'editWorkShiftDraft'\n )\n : await this.mutate(\n gql`\n mutation ($content: Object!) {\n createWorkShiftDraft(content: $content)\n }\n `,\n { content: this.content() },\n 'createWorkShiftDraft'\n )\n this.draft = { ...draft, id: row.id, editGeneration: row.editGeneration, revisionNumber: row.revisionNumber }\n this.dirty = false\n return true\n })\n if (saved && !quiet) notify({ message: rt('saved') })\n return !!saved\n }\n\n /** Puts the change into effect, or asks for approval where the site requires it. */\n private async commit() {\n this.tried = true\n if (draftIssues(this.draft!).blocking) return\n const approval = this.overview.approvalRequired\n if (approval && (this.dirty || !this.draft!.id) && !(await this.saveDraft(true))) return\n\n const done = await this.run(async () => {\n const draft = this.draft!\n if (approval) {\n let promise: Promise<any> | undefined\n document.dispatchEvent(\n new CustomEvent('master-change-approval', {\n detail: {\n axis: 'workShift',\n revisionId: draft.id,\n editGeneration: draft.editGeneration,\n respondWith: (p: Promise<any>) => (promise = p)\n }\n })\n )\n if (!promise) throw new Error(rt('approval-unavailable'))\n await promise\n } else {\n await this.mutate(\n gql`\n mutation ($revisionId: String, $editGeneration: Int, $content: Object!) {\n saveAndPublishWorkShift(revisionId: $revisionId, editGeneration: $editGeneration, content: $content)\n }\n `,\n { revisionId: draft.id, editGeneration: draft.editGeneration, content: this.content() },\n 'saveAndPublishWorkShift'\n )\n }\n return true\n })\n if (!done) return\n notify({ message: rt(approval ? 'submitted' : 'published') })\n this.draft = undefined\n this.dirty = false\n this.updateContext()\n await this.load()\n }\n\n /* ── pieces ────────────────────────────────────────────────────────── */\n\n private dayWord = (offset: number) => rt('day-offset-' + offset)\n\n private clock(minute: number) {\n return minute >= DAY_MINUTES\n ? `${this.dayWord(1)} ${clockOf(minute - DAY_MINUTES)}`\n : minute < 0\n ? `${this.dayWord(-1)} ${clockOf(minute + DAY_MINUTES)}`\n : clockOf(minute)\n }\n\n private span(from: number, to: number) {\n return `${this.clock(from)} – ${this.clock(to)}`\n }\n\n private hours(minutes?: number) {\n if (minutes === undefined) return ''\n const h = Math.floor(minutes / 60)\n const m = minutes % 60\n return m ? t('text.hours-minutes', { h, m }) : t('text.hours', { h })\n }\n\n private daysText(days: readonly string[]) {\n return daysLabel(days, day => rt('weekday-' + day), rt('every-day'))\n }\n\n private bar(shifts: ShiftLike[]) {\n const line = timelineOf(shifts)\n if (!line) return html`<div class=\"day-none\">${t('text.no-shift-on-these-days')}</div>`\n const label = (piece: (typeof line.pieces)[number]) =>\n piece.kind === 'shift'\n ? `${piece.names[0]} ${this.span(piece.fromMinute, piece.toMinute)}`\n : piece.kind === 'gap'\n ? t('text.shift-gap-span', { span: this.span(piece.fromMinute, piece.toMinute) })\n : t('text.shift-overlap-span', { span: this.span(piece.fromMinute, piece.toMinute), first: piece.names[0], second: piece.names[1] })\n\n return html`<div>\n <div class=\"bar\" role=\"img\" aria-label=${line.pieces.map(label).join(', ')}>\n ${line.midnights.map(left => html`<div class=\"midnight\" style=\"left:${left}%\"></div>`)}\n ${line.pieces.map(\n piece =>\n html`<div class=\"piece\" data-kind=${piece.kind} style=\"left:${piece.left}%;width:${piece.width}%\" title=${label(piece)}>\n ${piece.kind === 'shift'\n ? html`<strong>${piece.names[0]}</strong\n ><span class=\"time\">${clockOf(((piece.fromMinute % DAY_MINUTES) + DAY_MINUTES) % DAY_MINUTES)}–${clockOf(((piece.toMinute % DAY_MINUTES) + DAY_MINUTES) % DAY_MINUTES)}</span>`\n : nothing}\n </div>`\n )}\n </div>\n <div class=\"axis\" aria-hidden=\"true\">\n ${line.ticks.map(\n (minute, i) =>\n html`<span style=\"left:${i * 25}%\">${clockOf(((minute % DAY_MINUTES) + DAY_MINUTES) % DAY_MINUTES)}</span>`\n )}\n </div>\n </div>`\n }\n\n /**\n * The day bars of an arrangement, one per group of weekdays that run the same shifts, and what is wrong with them.\n * The editor passes the week's overlaps: they include a Friday night running into Saturday, which no single\n * day's bar shows, and the server refuses every one of them.\n */\n private timeline(shifts: ShiftLike[], weekOverlaps?: OverlapGroup[]) {\n const groups = weekGroups(shifts)\n const findings = groups.flatMap(group =>\n coverageProblems(group.shifts)\n .filter(problem => problem.kind === 'gap' || (problem.kind === 'overlap' && !weekOverlaps))\n .map(problem => ({ group, problem }))\n )\n const firstGap = findings.findIndex(({ problem }) => problem.kind === 'gap')\n const prefix = (days: string[]) => (groups.length > 1 ? `${this.daysText(days)} · ` : '')\n return html`\n <div class=\"day-groups\">\n ${groups.map(\n group =>\n html`<div class=\"day-label\">${this.daysText(group.days)}</div>\n ${group.shifts.length ? this.bar(group.shifts) : html`<div class=\"day-none\">${t('text.no-shift-on-these-days')}</div>`}`\n )}\n </div>\n ${findings.length || weekOverlaps?.length\n ? html`<ul class=\"findings\">\n ${findings.map(({ group, problem }, index) =>\n problem.kind === 'gap'\n ? html`<li data-kind=\"gap\">\n <span\n >${prefix(group.days)}${t('text.shift-gap-span', { span: this.span(problem.fromMinute!, problem.toMinute!) })}\n ${index === firstGap ? html`<span class=\"why\">${t('text.shift-gap-why')}</span>` : nothing}</span\n >\n </li>`\n : html`<li data-kind=\"overlap\">\n <span\n >${prefix(group.days)}${t('text.shift-overlap-span', {\n span: this.span(problem.fromMinute!, problem.toMinute!),\n first: problem.names[0],\n second: problem.names[1]\n })}</span\n >\n </li>`\n )}\n ${(weekOverlaps ?? []).map(\n overlap =>\n html`<li data-kind=\"error\">\n <span\n >${t('text.shift-overlap-on-days', {\n days: this.daysText(overlap.days),\n span: this.span(overlap.fromMinute, overlap.toMinute),\n first: overlap.names[0],\n second: overlap.names[1]\n })}</span\n >\n </li>`\n )}\n </ul>`\n : nothing}\n `\n }\n\n private shiftTable(shifts: (ShiftLike & { description?: string })[]) {\n return html`<div class=\"table-scroll\">\n <table>\n <thead>\n <tr>\n <th>${t('field.shift')}</th>\n <th>${t('field.shift-hours')}</th>\n <th>${rt('daysOfWeek')}</th>\n </tr>\n </thead>\n <tbody>\n ${shifts.map(\n shift =>\n html`<tr>\n <td>${shift.name}${shift.description ? html`<div class=\"muted\">${shift.description}</div>` : nothing}</td>\n <td class=\"figure\">${timeRange(shift, this.dayWord)} <span class=\"muted\">· ${this.hours(lengthOf(shift))}</span></td>\n <td>${this.daysText(daysOf(shift.daysOfWeek))}</td>\n </tr>`\n )}\n </tbody>\n </table>\n </div>`\n }\n\n private shiftLines(shifts: ShiftLike[] = []) {\n return html`<div class=\"shift-lines\">\n ${shifts.map(\n shift =>\n html`<div>\n ${shift.name} <span class=\"figure\">${timeRange(shift, this.dayWord)}</span>\n ${daysOf(shift.daysOfWeek).length < WEEK.length ? html`<span class=\"muted\">${this.daysText(daysOf(shift.daysOfWeek))}</span>` : nothing}\n </div>`\n )}\n </div>`\n }\n\n private stateTag(state: string) {\n return html`<span class=\"state\" data-state=${state}>${rt(state)}</span>`\n }\n\n private period(item: PlacedRevision) {\n if (!item.period) return html`<span class=\"muted\">${t('text.from-time', { time: revisionDate(item.row.effectiveFrom) })}</span>`\n return item.period.to\n ? `${revisionDate(item.period.from)} – ${revisionDate(item.period.to)}`\n : t('text.from-time', { time: revisionDate(item.period.from) })\n }\n\n /* ── view ──────────────────────────────────────────────────────────── */\n\n private viewCurrent(placed: PlacedOverview) {\n const current = placed.current\n const legacy: ShiftLike[] = !current ? this.overview.legacyShifts ?? [] : []\n return html`<section class=\"page-section\">\n <header class=\"page-section-header\"><h3>${t('title.shift-current')}</h3></header>\n ${current\n ? html`<p class=\"page-section-note\">\n ${t('text.from-time', { time: revisionDate(current.period!.from) })} · ${current.row.timezone}\n </p>`\n : nothing}\n ${current?.row.shifts?.length || legacy.length\n ? html`<div class=\"arrangement\">\n ${this.timeline(current?.row.shifts ?? legacy)} ${this.shiftTable(current?.row.shifts ?? legacy)}\n </div>`\n : html`<ox-list-state\n .scale=${{ shown: 0, loaded: 0 }}\n .emptyNote=${{ icon: 'schedule', title: t('text.no-shifts-declared') }}\n ></ox-list-state>`}\n </section>`\n }\n\n private viewScheduled(placed: PlacedOverview) {\n if (!placed.scheduled.length) return nothing\n return html`<section class=\"page-section\">\n <header class=\"page-section-header\"><h3>${t('title.shift-announcements')}</h3></header>\n ${placed.scheduled.map(\n /* The bar only: the shifts themselves are listed in the history row right below. */\n item => html`<p class=\"page-section-note\">\n ${t('text.from-time', { time: revisionDate(item.period!.from) })}${item.row.reason ? ` · ${item.row.reason}` : ''}\n </p>\n <div class=\"arrangement\">${this.timeline(item.row.shifts ?? [])}</div>`\n )}\n </section>`\n }\n\n /** A person's reason as written; the recorded first arrangement in plain words, with where it came from. */\n private reasonCell(row: RevisionRow, approval?: { comment?: string }) {\n return html`<td class=\"reason\">\n ${reasonOf(row, t('text.shift-baseline'))}\n ${isBaseline(row) ? html`<div class=\"muted\">${t('text.shift-baseline-why')}</div>` : nothing}\n ${approval?.comment ? html`<div class=\"muted\">${approval.comment}</div>` : nothing}\n </td>`\n }\n\n private viewOpen(placed: PlacedOverview) {\n if (!placed.open.length) return nothing\n return html`<section class=\"page-section\">\n <header class=\"page-section-header\"><h3>${t('title.shift-open-changes')}</h3></header>\n <div class=\"table-scroll\">\n <table>\n <thead>\n <tr>\n <th>${rt('state')}</th>\n <th>${rt('effectiveFrom')}</th>\n <th>${t('field.shift')}</th>\n <th>${rt('reason')}</th>\n <th></th>\n </tr>\n </thead>\n <tbody>\n ${placed.open.map(item => {\n const approval = this.approvals[item.row.id]\n const open = this.opened === item.row.id\n return html`<tr>\n <td>${this.stateTag(item.state)}</td>\n <td class=\"figure\">${revisionDate(item.row.effectiveFrom)}</td>\n <td>${this.shiftLines(item.row.shifts)}</td>\n ${this.reasonCell(item.row, approval)}\n <td class=\"actions\">\n <div>\n ${item.state === 'draft'\n ? html`<button small @click=${() => this.resume(item.row)}>${t('button.continue-editing')}</button>`\n : html`<button small quiet @click=${() => (this.opened = open ? undefined : item.row.id)}>\n ${open ? t('button.close') : rt('changes')}\n </button>`}\n ${approval?.open ? html`<button small @click=${() => approval.open()}>${rt('approval-detail')}</button>` : nothing}\n </div>\n </td>\n </tr>\n ${open\n ? html`<tr class=\"detail\">\n <td colspan=\"5\">\n <ops-master-revision-comparison\n axis=\"workShift\"\n .before=${this.overview.revisions.find((r: RevisionRow) => r.id === item.row.baseRevisionId)}\n .after=${item.row}\n ></ops-master-revision-comparison>\n </td>\n </tr>`\n : nothing}`\n })}\n </tbody>\n </table>\n </div>\n </section>`\n }\n\n private viewHistory(placed: PlacedOverview) {\n return html`<section class=\"page-section\">\n <header class=\"page-section-header\"><h3>${t('title.shift-history')}</h3></header>\n ${placed.history.length\n ? html`<div class=\"table-scroll\">\n <table>\n <thead>\n <tr>\n <th>${t('field.revision-number')}</th>\n <th>${rt('state')}</th>\n <th>${t('field.shift-period')}</th>\n <th>${t('field.shift')}</th>\n <th>${rt('reason')}</th>\n <th></th>\n </tr>\n </thead>\n <tbody>\n ${placed.history.map(item => {\n const approval = this.approvals[item.row.id]\n return html`<tr>\n <td class=\"figure\">${item.row.revisionNumber}</td>\n <td>${this.stateTag(item.state)}</td>\n <td class=\"figure\">${this.period(item)}</td>\n <td>${this.shiftLines(item.row.shifts)}</td>\n ${this.reasonCell(item.row, approval)}\n <td class=\"actions\">\n <div>\n ${approval?.open ? html`<button small quiet @click=${() => approval.open()}>${rt('approval-detail')}</button>` : nothing}\n <button small @click=${() => this.begin(item.row)}>${t('button.change-from-this')}</button>\n </div>\n </td>\n </tr>`\n })}\n </tbody>\n </table>\n </div>`\n : html`<ox-list-state .scale=${{ shown: 0, loaded: 0 }} .emptyNote=${{ title: t('text.no-shift-history') }}></ox-list-state>`}\n </section>`\n }\n\n /* ── editor ────────────────────────────────────────────────────────── */\n\n private issueText(issue: RowIssue) {\n return t('text.shift-issue-' + issue)\n }\n\n private shiftRow(shift: EditableShift, index: number, issues: RowIssue[]) {\n const draft = this.draft!\n const nameBad = issues.includes('name-missing') || issues.includes('name-duplicate')\n const timeBad = issues.includes('time-missing') || issues.includes('ends-too-late')\n const length = lengthOf(shift)\n return html`<div class=\"shift-row\">\n <label class=\"cell name\">\n <span class=\"cell-label\">${rt('name')}</span>\n <input\n .value=${shift.name ?? ''}\n aria-label=${rt('name')}\n aria-invalid=${nameBad && (this.tried || !!shift.name) ? 'true' : 'false'}\n ?disabled=${this.busy}\n @input=${(e: any) => this.changeShift(index, { name: e.target.value })}\n />\n </label>\n <div class=\"cell\">\n <span class=\"cell-label\">${t('field.shift-start')}</span>\n <div class=\"clock\">\n <select\n aria-label=${rt('fromDate')}\n ?disabled=${this.busy}\n @change=${(e: any) => this.changeShift(index, { fromDate: Number(e.target.value) })}\n >\n ${[-1, 0, 1].map(day => html`<option value=${day} ?selected=${Number(shift.fromDate ?? 0) === day}>${this.dayWord(day)}</option>`)}\n </select>\n <input\n type=\"time\"\n .value=${String(shift.fromTime ?? '').slice(0, 5)}\n aria-label=${rt('fromTime')}\n aria-invalid=${timeBad && this.tried ? 'true' : 'false'}\n ?disabled=${this.busy}\n @input=${(e: any) => this.changeShift(index, { fromTime: e.target.value })}\n />\n </div>\n </div>\n <div class=\"cell\">\n <span class=\"cell-label\">${t('field.shift-end')}</span>\n <div class=\"clock\">\n <input\n type=\"time\"\n .value=${String(shift.toTime ?? '').slice(0, 5)}\n aria-label=${rt('toTime')}\n aria-invalid=${timeBad && this.tried ? 'true' : 'false'}\n ?disabled=${this.busy}\n @input=${(e: any) => this.changeShift(index, { toTime: e.target.value })}\n />\n <span class=\"derived\"\n >${shift.toDate ? `${this.dayWord(shift.toDate)} · ` : ''}${this.hours(length)}</span\n >\n </div>\n </div>\n <div class=\"cell days\">\n <span class=\"cell-label\">${rt('daysOfWeek')}</span>\n <ox-input-select-buttons\n multiple\n aria-label=${rt('daysOfWeek')}\n .options=${WEEK.map(day => ({ display: rt('weekday-' + day), value: day }))}\n .value=${daysOf(shift.daysOfWeek)}\n ?disabled=${this.busy}\n @change=${(e: any) => {\n const value = daysValue(e.detail ?? [])\n /* A shift on no day is not a shift: keep the last day chosen. */\n if (value === undefined) e.target.value = daysOf(shift.daysOfWeek)\n else this.changeShift(index, { daysOfWeek: value })\n }}\n ></ox-input-select-buttons>\n </div>\n <label class=\"cell note\">\n <span class=\"cell-label\">${rt('description')}</span>\n <input\n .value=${shift.description ?? ''}\n aria-label=${rt('description')}\n ?disabled=${this.busy}\n @input=${(e: any) => this.changeShift(index, { description: e.target.value })}\n />\n </label>\n <div class=\"cell remove\">\n <button\n small\n quiet\n danger\n ?disabled=${this.busy || draft.shifts.length === 1}\n aria-label=${`${rt('remove')} ${shift.name || index + 1}`}\n @click=${() => this.change({ shifts: draft.shifts.filter((_, i) => i !== index) })}\n >\n ${rt('remove')}\n </button>\n </div>\n ${issues.length && (this.tried || issues.some(issue => issue === 'name-duplicate' || issue === 'ends-too-late'))\n ? html`<p class=\"row-issues\">${issues.map(issue => this.issueText(issue)).join(' · ')}</p>`\n : nothing}\n </div>`\n }\n\n private editor() {\n const draft = this.draft!\n const issues = draftIssues(draft)\n const timezones: string[] = (Intl as any).supportedValuesOf?.('timeZone') ?? []\n const zones = timezones.includes(draft.timezone) ? timezones : [draft.timezone, ...timezones]\n return html`\n ${this.saveError ? html`<p class=\"alert\" role=\"alert\">${this.saveError}</p>` : nothing}\n <section class=\"page-section\">\n <header class=\"page-section-header\">\n <h3>${draft.revisionNumber ? rt('revision-value', { n: draft.revisionNumber }) : t('title.shift-new-arrangement')}</h3>\n <div class=\"page-section-actions\">\n <button ?disabled=${this.busy} @click=${() => this.change({ shifts: [...draft.shifts, nextShift(draft.shifts)] })}>\n ${rt('add-shift')}\n </button>\n </div>\n </header>\n <div class=\"shift-rows\">\n <div class=\"shift-row head\" aria-hidden=\"true\">\n <span>${rt('name')}</span><span>${t('field.shift-start')}</span><span>${t('field.shift-end')}</span\n ><span>${rt('daysOfWeek')}</span><span>${rt('description')}</span><span></span>\n </div>\n ${draft.shifts.map((shift, index) => this.shiftRow(shift, index, issues.rows.get(index) ?? []))}\n </div>\n <div class=\"arrangement\">\n ${this.timeline(draft.shifts, groupOverlaps(issues.overlaps))}\n </div>\n </section>\n\n <section class=\"page-section\">\n <header class=\"page-section-header\"><h3>${t('title.shift-apply')}</h3></header>\n <div class=\"form-grid\">\n <div class=\"field\">\n <span>${rt('effectiveFrom')}</span>\n <div class=\"when\">\n <ox-input-select-buttons\n .options=${[\n { display: t('text.apply-now'), value: 'now' },\n { display: t('text.apply-later'), value: 'at' }\n ]}\n .value=${draft.effectiveMode}\n ?disabled=${this.busy}\n @change=${(e: any) => {\n if (!e.detail) e.target.value = draft.effectiveMode\n else this.change({ effectiveMode: e.detail })\n }}\n ></ox-input-select-buttons>\n ${draft.effectiveMode === 'at'\n ? html`<input\n type=\"datetime-local\"\n aria-label=${rt('effectiveFrom')}\n .value=${localDateTime(draft.effectiveFrom)}\n ?disabled=${this.busy}\n @change=${(e: any) => {\n try {\n this.change({ effectiveFrom: isoFromLocal(e.target.value) })\n } catch {\n e.target.value = localDateTime(draft.effectiveFrom)\n }\n }}\n />`\n : nothing}\n </div>\n </div>\n <label class=\"field\">\n <span>${rt('reason')}</span>\n <input\n .value=${draft.reason}\n aria-invalid=${issues.reasonMissing && this.tried ? 'true' : 'false'}\n ?disabled=${this.busy}\n @input=${(e: any) => this.change({ reason: e.target.value })}\n />\n ${issues.reasonMissing && this.tried ? html`<span class=\"error\">${t('text.shift-issue-reason-missing')}</span>` : nothing}\n </label>\n <label class=\"field\">\n <span>${rt('timezone')}</span>\n <select ?disabled=${this.busy} @change=${(e: any) => this.change({ timezone: e.target.value })}>\n ${zones.map(zone => html`<option value=${zone} ?selected=${zone === draft.timezone}>${zone}</option>`)}\n </select>\n </label>\n <label class=\"field\">\n <span>${rt('description')}</span>\n <input .value=${draft.description ?? ''} ?disabled=${this.busy} @input=${(e: any) => this.change({ description: e.target.value })} />\n </label>\n </div>\n </section>\n `\n }\n\n render() {\n const placed = this.overview ? this.placed : undefined\n return html`<ox-page-state .loading=${this.loading && !this.overview} .error=${this.error} @retry=${() => this.load()}>\n <div class=\"page\">\n ${placed\n ? this.draft\n ? this.editor()\n : html`${this.viewCurrent(placed)} ${this.viewScheduled(placed)} ${this.viewOpen(placed)} ${this.viewHistory(placed)}`\n : nothing}\n </div>\n </ox-page-state>`\n }\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export default function route(page: any): any;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"route.js","sourceRoot":"","sources":["../client/route.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,OAAO,UAAU,KAAK,CAAC,IAAI;IAChC,QAAQ,IAAI,EAAE,CAAC;QACb,KAAK,YAAY;YACf,MAAM,CAAC,oBAAoB,CAAC,CAAA;YAC5B,OAAO,IAAI,CAAA;IACf,CAAC;AACH,CAAC","sourcesContent":["export default function route(page) {\n switch (page) {\n case 'work-shift':\n import('./pages/work-shift')\n return page\n }\n}\n"]}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 편성이 하루를 빈틈없이 덮나 — **순수 계산.**
|
|
3
|
+
*
|
|
4
|
+
* ── 왜 이걸 보여야 하나 ────────────────────────────────────────────────────
|
|
5
|
+
* 교대 사이에 틈이 있으면 그 시간에 일어난 일은 **어느 교대에도 속하지 않는다.** 그러면 그 시각을 물은
|
|
6
|
+
* 쪽은 조업일만 받고 교대 이름은 못 받는다. 화면에 편성을 줄로만 보여 주면 그 틈이 안 보인다 —
|
|
7
|
+
* 22:00 에 끝나고 23:00 에 시작하는 것을 사람이 눈으로 세어야 한다.
|
|
8
|
+
*
|
|
9
|
+
* 겹침도 마찬가지다. 두 교대가 겹치면 그 시간의 일이 **먼저 걸린 교대**로 간다. 편성을 적은 사람은
|
|
10
|
+
* 그렇게 되는 줄 모른다.
|
|
11
|
+
*
|
|
12
|
+
* ── 자정이 아니라 첫 교대에서 시작한다 ─────────────────────────────────────
|
|
13
|
+
* 하루를 00:00~24:00 으로 재면 야간 교대가 있는 공장에서 **없는 틈이 보이고 있는 틈이 안 보인다.**
|
|
14
|
+
*
|
|
15
|
+
* ```
|
|
16
|
+
* 주간 08:00-17:00 · 야간 18:00-익일 02:00
|
|
17
|
+
*
|
|
18
|
+
* 00:00 기준 00:00~08:00 이 빈 것으로 보인다 ← 어제 야간이 덮고 있다
|
|
19
|
+
* 02:00~08:00 의 진짜 틈은 안 보인다
|
|
20
|
+
* 첫 교대 기준 17:00~18:00 · 익일 02:00~08:00 ← 둘 다 맞다
|
|
21
|
+
* ```
|
|
22
|
+
*
|
|
23
|
+
* 그래서 **첫 교대가 시작하는 시각부터 24시간**을 한 바퀴로 본다. 그것이 이 공장의 하루다.
|
|
24
|
+
*/
|
|
25
|
+
export interface ShiftLike {
|
|
26
|
+
name: string;
|
|
27
|
+
/** Work weekdays are the operating date of the shift; blank means every day. */
|
|
28
|
+
daysOfWeek?: string;
|
|
29
|
+
/** 시작이 그날인가 전날인가 다음 날인가 — `-1` · `0` · `1`. */
|
|
30
|
+
fromDate: number;
|
|
31
|
+
/** `HH:mm` 또는 `HH:mm:ss`. */
|
|
32
|
+
fromTime: string;
|
|
33
|
+
toDate: number;
|
|
34
|
+
toTime: string;
|
|
35
|
+
}
|
|
36
|
+
export interface ShiftSpan {
|
|
37
|
+
name: string;
|
|
38
|
+
/** 조업일 시작(0분)부터 몇 분 뒤인가. 전날이면 음수다. */
|
|
39
|
+
fromMinute: number;
|
|
40
|
+
toMinute: number;
|
|
41
|
+
}
|
|
42
|
+
export interface CoverageProblem {
|
|
43
|
+
kind: 'gap' | 'overlap' | 'not-a-span';
|
|
44
|
+
/** 걸린 교대 이름들. `gap` 은 그 틈의 앞뒤 둘. */
|
|
45
|
+
names: string[];
|
|
46
|
+
fromMinute?: number;
|
|
47
|
+
toMinute?: number;
|
|
48
|
+
}
|
|
49
|
+
/** 이 편성의 한 바퀴 — 첫 교대가 시작하는 시각부터 24시간. */
|
|
50
|
+
export interface ShiftCycle {
|
|
51
|
+
startMinute: number;
|
|
52
|
+
endMinute: number;
|
|
53
|
+
spans: ShiftSpan[];
|
|
54
|
+
}
|
|
55
|
+
/** `HH:mm[:ss]` 를 분으로. 읽을 수 없으면 없다고 답한다 — 0 으로 두면 자정으로 읽힌다. */
|
|
56
|
+
export declare function minutesOf(time: string): number | undefined;
|
|
57
|
+
/** 교대 하나를 조업일 기준 분 구간으로. 읽을 수 없으면 없다. */
|
|
58
|
+
export declare function spanOf(shift: ShiftLike): ShiftSpan | undefined;
|
|
59
|
+
/**
|
|
60
|
+
* 이 편성의 한 바퀴 — **첫 교대가 시작하는 시각부터 24시간.**
|
|
61
|
+
*
|
|
62
|
+
* 읽을 수 없는 교대는 여기 들지 않는다. 그것은 `coverageProblems` 가 이름과 함께 낸다.
|
|
63
|
+
*/
|
|
64
|
+
export declare function cycleOf(shifts: ShiftLike[]): ShiftCycle | undefined;
|
|
65
|
+
/**
|
|
66
|
+
* 편성을 훑어 틈과 겹침을 찾는다.
|
|
67
|
+
*
|
|
68
|
+
* 빈 편성은 문제가 아니다 — 아직 안 정한 것이지 잘못 정한 것이 아니다. 그 사실은 부르는 쪽이 목록의
|
|
69
|
+
* 길이로 안다.
|
|
70
|
+
*
|
|
71
|
+
* **마지막 교대와 다음 바퀴의 첫 교대 사이도 본다.** 야간 교대가 새벽에 끝나는 공장에서 그 틈이 가장
|
|
72
|
+
* 크고, 자정 기준으로 재면 그것이 보이지 않는다.
|
|
73
|
+
*/
|
|
74
|
+
export declare function coverageProblems(shifts: ShiftLike[]): CoverageProblem[];
|
|
75
|
+
/** 편성이 덮는 전체 길이(분). 화면이 「24시간을 덮는가」를 말하는 근거. */
|
|
76
|
+
export declare function coveredMinutes(shifts: ShiftLike[]): number;
|
|
77
|
+
/** 분을 `HH:mm` 으로. 조업일을 넘어간 것은 넘어간 대로 적는다 — 24:30 은 다음 날 00:30 이다. */
|
|
78
|
+
export declare function clockOf(minute: number): string;
|
|
79
|
+
export interface TimelinePiece {
|
|
80
|
+
kind: 'shift' | 'gap' | 'overlap';
|
|
81
|
+
/** Shift names: one for a shift, the two either side for a gap, the two that collide for an overlap. */
|
|
82
|
+
names: string[];
|
|
83
|
+
fromMinute: number;
|
|
84
|
+
toMinute: number;
|
|
85
|
+
/** Position on the bar, in percent of the day. */
|
|
86
|
+
left: number;
|
|
87
|
+
width: number;
|
|
88
|
+
}
|
|
89
|
+
export interface Timeline {
|
|
90
|
+
startMinute: number;
|
|
91
|
+
pieces: TimelinePiece[];
|
|
92
|
+
/** Where each midnight inside the day falls, in percent - the bar starts at the first shift, not at 00:00. */
|
|
93
|
+
midnights: number[];
|
|
94
|
+
/** Six-hour marks from the start of the day, in minutes, both ends included. */
|
|
95
|
+
ticks: number[];
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* The bar the screen draws for one day of an arrangement: shifts, the gaps between them and the overlaps,
|
|
99
|
+
* laid out over the 24 hours that start with the first shift. Shift pieces are clipped to that day; a gap or
|
|
100
|
+
* an overlap is drawn on top of the shifts so it is never hidden behind one.
|
|
101
|
+
*/
|
|
102
|
+
export declare function timelineOf(shifts: ShiftLike[]): Timeline | undefined;
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 편성이 하루를 빈틈없이 덮나 — **순수 계산.**
|
|
3
|
+
*
|
|
4
|
+
* ── 왜 이걸 보여야 하나 ────────────────────────────────────────────────────
|
|
5
|
+
* 교대 사이에 틈이 있으면 그 시간에 일어난 일은 **어느 교대에도 속하지 않는다.** 그러면 그 시각을 물은
|
|
6
|
+
* 쪽은 조업일만 받고 교대 이름은 못 받는다. 화면에 편성을 줄로만 보여 주면 그 틈이 안 보인다 —
|
|
7
|
+
* 22:00 에 끝나고 23:00 에 시작하는 것을 사람이 눈으로 세어야 한다.
|
|
8
|
+
*
|
|
9
|
+
* 겹침도 마찬가지다. 두 교대가 겹치면 그 시간의 일이 **먼저 걸린 교대**로 간다. 편성을 적은 사람은
|
|
10
|
+
* 그렇게 되는 줄 모른다.
|
|
11
|
+
*
|
|
12
|
+
* ── 자정이 아니라 첫 교대에서 시작한다 ─────────────────────────────────────
|
|
13
|
+
* 하루를 00:00~24:00 으로 재면 야간 교대가 있는 공장에서 **없는 틈이 보이고 있는 틈이 안 보인다.**
|
|
14
|
+
*
|
|
15
|
+
* ```
|
|
16
|
+
* 주간 08:00-17:00 · 야간 18:00-익일 02:00
|
|
17
|
+
*
|
|
18
|
+
* 00:00 기준 00:00~08:00 이 빈 것으로 보인다 ← 어제 야간이 덮고 있다
|
|
19
|
+
* 02:00~08:00 의 진짜 틈은 안 보인다
|
|
20
|
+
* 첫 교대 기준 17:00~18:00 · 익일 02:00~08:00 ← 둘 다 맞다
|
|
21
|
+
* ```
|
|
22
|
+
*
|
|
23
|
+
* 그래서 **첫 교대가 시작하는 시각부터 24시간**을 한 바퀴로 본다. 그것이 이 공장의 하루다.
|
|
24
|
+
*/
|
|
25
|
+
const DAY = 24 * 60;
|
|
26
|
+
/** `HH:mm[:ss]` 를 분으로. 읽을 수 없으면 없다고 답한다 — 0 으로 두면 자정으로 읽힌다. */
|
|
27
|
+
export function minutesOf(time) {
|
|
28
|
+
const parts = String(time ?? '').split(':');
|
|
29
|
+
if (parts.length < 2) {
|
|
30
|
+
return undefined;
|
|
31
|
+
}
|
|
32
|
+
const hours = Number(parts[0]);
|
|
33
|
+
const minutes = Number(parts[1]);
|
|
34
|
+
if (!Number.isFinite(hours) || !Number.isFinite(minutes)) {
|
|
35
|
+
return undefined;
|
|
36
|
+
}
|
|
37
|
+
return hours * 60 + minutes;
|
|
38
|
+
}
|
|
39
|
+
/** 교대 하나를 조업일 기준 분 구간으로. 읽을 수 없으면 없다. */
|
|
40
|
+
export function spanOf(shift) {
|
|
41
|
+
const from = minutesOf(shift.fromTime);
|
|
42
|
+
const to = minutesOf(shift.toTime);
|
|
43
|
+
if (from === undefined || to === undefined) {
|
|
44
|
+
return undefined;
|
|
45
|
+
}
|
|
46
|
+
return {
|
|
47
|
+
name: shift.name,
|
|
48
|
+
fromMinute: Number(shift.fromDate ?? 0) * DAY + from,
|
|
49
|
+
toMinute: Number(shift.toDate ?? 0) * DAY + to
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* 이 편성의 한 바퀴 — **첫 교대가 시작하는 시각부터 24시간.**
|
|
54
|
+
*
|
|
55
|
+
* 읽을 수 없는 교대는 여기 들지 않는다. 그것은 `coverageProblems` 가 이름과 함께 낸다.
|
|
56
|
+
*/
|
|
57
|
+
export function cycleOf(shifts) {
|
|
58
|
+
const spans = shifts
|
|
59
|
+
.map(spanOf)
|
|
60
|
+
.filter((span) => Boolean(span) && span.toMinute > span.fromMinute)
|
|
61
|
+
.sort((a, b) => a.fromMinute - b.fromMinute);
|
|
62
|
+
if (!spans.length) {
|
|
63
|
+
return undefined;
|
|
64
|
+
}
|
|
65
|
+
const startMinute = spans[0].fromMinute;
|
|
66
|
+
return { startMinute, endMinute: startMinute + DAY, spans };
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* 편성을 훑어 틈과 겹침을 찾는다.
|
|
70
|
+
*
|
|
71
|
+
* 빈 편성은 문제가 아니다 — 아직 안 정한 것이지 잘못 정한 것이 아니다. 그 사실은 부르는 쪽이 목록의
|
|
72
|
+
* 길이로 안다.
|
|
73
|
+
*
|
|
74
|
+
* **마지막 교대와 다음 바퀴의 첫 교대 사이도 본다.** 야간 교대가 새벽에 끝나는 공장에서 그 틈이 가장
|
|
75
|
+
* 크고, 자정 기준으로 재면 그것이 보이지 않는다.
|
|
76
|
+
*/
|
|
77
|
+
export function coverageProblems(shifts) {
|
|
78
|
+
const problems = [];
|
|
79
|
+
const spans = [];
|
|
80
|
+
for (const shift of shifts) {
|
|
81
|
+
const span = spanOf(shift);
|
|
82
|
+
if (!span || span.toMinute <= span.fromMinute) {
|
|
83
|
+
/* 시각을 읽을 수 없거나 구간이 되지 않는다. 빼고 넘어가면 그 교대가 화면에서 사라진다. */
|
|
84
|
+
problems.push({ kind: 'not-a-span', names: [shift.name] });
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
spans.push(span);
|
|
88
|
+
}
|
|
89
|
+
const sorted = [...spans].sort((a, b) => a.fromMinute - b.fromMinute);
|
|
90
|
+
for (let i = 1; i < sorted.length; i += 1) {
|
|
91
|
+
const before = sorted[i - 1];
|
|
92
|
+
const after = sorted[i];
|
|
93
|
+
if (after.fromMinute > before.toMinute) {
|
|
94
|
+
problems.push({
|
|
95
|
+
kind: 'gap',
|
|
96
|
+
names: [before.name, after.name],
|
|
97
|
+
fromMinute: before.toMinute,
|
|
98
|
+
toMinute: after.fromMinute
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
else if (after.fromMinute < before.toMinute) {
|
|
102
|
+
problems.push({
|
|
103
|
+
kind: 'overlap',
|
|
104
|
+
names: [before.name, after.name],
|
|
105
|
+
fromMinute: after.fromMinute,
|
|
106
|
+
toMinute: Math.min(before.toMinute, after.toMinute)
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
/* 한 바퀴가 닫히나 — 마지막 교대가 끝나고 다음 바퀴 첫 교대가 시작할 때까지. */
|
|
111
|
+
if (sorted.length) {
|
|
112
|
+
const first = sorted[0];
|
|
113
|
+
const lastEnd = Math.max(...sorted.map(span => span.toMinute));
|
|
114
|
+
const nextStart = first.fromMinute + DAY;
|
|
115
|
+
const last = sorted.find(span => span.toMinute === lastEnd) ?? sorted[sorted.length - 1];
|
|
116
|
+
if (lastEnd < nextStart) {
|
|
117
|
+
problems.push({ kind: 'gap', names: [last.name, first.name], fromMinute: lastEnd, toMinute: nextStart });
|
|
118
|
+
}
|
|
119
|
+
else if (lastEnd > nextStart && sorted.length > 1) {
|
|
120
|
+
/* The last shift runs into the next day's first one - a night shift ending after the morning shift starts. */
|
|
121
|
+
problems.push({ kind: 'overlap', names: [last.name, first.name], fromMinute: nextStart, toMinute: lastEnd });
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
return problems;
|
|
125
|
+
}
|
|
126
|
+
/** 편성이 덮는 전체 길이(분). 화면이 「24시간을 덮는가」를 말하는 근거. */
|
|
127
|
+
export function coveredMinutes(shifts) {
|
|
128
|
+
const spans = shifts.map(spanOf).filter((span) => Boolean(span));
|
|
129
|
+
if (!spans.length) {
|
|
130
|
+
return 0;
|
|
131
|
+
}
|
|
132
|
+
const sorted = [...spans].sort((a, b) => a.fromMinute - b.fromMinute);
|
|
133
|
+
let covered = 0;
|
|
134
|
+
let cursorEnd = sorted[0].fromMinute;
|
|
135
|
+
for (const span of sorted) {
|
|
136
|
+
const start = Math.max(span.fromMinute, cursorEnd);
|
|
137
|
+
if (span.toMinute > start) {
|
|
138
|
+
covered += span.toMinute - start;
|
|
139
|
+
cursorEnd = span.toMinute;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
return covered;
|
|
143
|
+
}
|
|
144
|
+
/** 분을 `HH:mm` 으로. 조업일을 넘어간 것은 넘어간 대로 적는다 — 24:30 은 다음 날 00:30 이다. */
|
|
145
|
+
export function clockOf(minute) {
|
|
146
|
+
const sign = minute < 0 ? '-' : '';
|
|
147
|
+
const value = Math.abs(minute);
|
|
148
|
+
return `${sign}${String(Math.floor(value / 60)).padStart(2, '0')}:${String(value % 60).padStart(2, '0')}`;
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* The bar the screen draws for one day of an arrangement: shifts, the gaps between them and the overlaps,
|
|
152
|
+
* laid out over the 24 hours that start with the first shift. Shift pieces are clipped to that day; a gap or
|
|
153
|
+
* an overlap is drawn on top of the shifts so it is never hidden behind one.
|
|
154
|
+
*/
|
|
155
|
+
export function timelineOf(shifts) {
|
|
156
|
+
const cycle = cycleOf(shifts);
|
|
157
|
+
if (!cycle)
|
|
158
|
+
return undefined;
|
|
159
|
+
const place = (kind, names, from, to) => {
|
|
160
|
+
/* A problem at the turn of the day (the next day's first shift) is drawn where that shift starts: at the left. */
|
|
161
|
+
if (kind !== 'shift' && from >= cycle.endMinute) {
|
|
162
|
+
from -= DAY;
|
|
163
|
+
to -= DAY;
|
|
164
|
+
}
|
|
165
|
+
const left = Math.max(cycle.startMinute, from);
|
|
166
|
+
const right = Math.min(cycle.endMinute, to);
|
|
167
|
+
if (right <= left)
|
|
168
|
+
return undefined;
|
|
169
|
+
return {
|
|
170
|
+
kind,
|
|
171
|
+
names,
|
|
172
|
+
fromMinute: from,
|
|
173
|
+
toMinute: to,
|
|
174
|
+
left: ((left - cycle.startMinute) / DAY) * 100,
|
|
175
|
+
width: ((right - left) / DAY) * 100
|
|
176
|
+
};
|
|
177
|
+
};
|
|
178
|
+
const pieces = [
|
|
179
|
+
...cycle.spans.map(span => place('shift', [span.name], span.fromMinute, span.toMinute)),
|
|
180
|
+
...coverageProblems(shifts)
|
|
181
|
+
.filter(problem => problem.kind !== 'not-a-span')
|
|
182
|
+
.map(problem => place(problem.kind, problem.names, problem.fromMinute, problem.toMinute))
|
|
183
|
+
].filter((piece) => Boolean(piece));
|
|
184
|
+
const midnights = [];
|
|
185
|
+
for (let m = Math.ceil(cycle.startMinute / DAY) * DAY; m < cycle.endMinute; m += DAY) {
|
|
186
|
+
if (m > cycle.startMinute)
|
|
187
|
+
midnights.push(((m - cycle.startMinute) / DAY) * 100);
|
|
188
|
+
}
|
|
189
|
+
return {
|
|
190
|
+
startMinute: cycle.startMinute,
|
|
191
|
+
pieces,
|
|
192
|
+
midnights,
|
|
193
|
+
ticks: [0, 6, 12, 18, 24].map(hour => cycle.startMinute + hour * 60)
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
//# sourceMappingURL=shift-coverage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shift-coverage.js","sourceRoot":"","sources":["../client/shift-coverage.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * 편성이 하루를 빈틈없이 덮나 — **순수 계산.**\n *\n * ── 왜 이걸 보여야 하나 ────────────────────────────────────────────────────\n * 교대 사이에 틈이 있으면 그 시간에 일어난 일은 **어느 교대에도 속하지 않는다.** 그러면 그 시각을 물은\n * 쪽은 조업일만 받고 교대 이름은 못 받는다. 화면에 편성을 줄로만 보여 주면 그 틈이 안 보인다 —\n * 22:00 에 끝나고 23:00 에 시작하는 것을 사람이 눈으로 세어야 한다.\n *\n * 겹침도 마찬가지다. 두 교대가 겹치면 그 시간의 일이 **먼저 걸린 교대**로 간다. 편성을 적은 사람은\n * 그렇게 되는 줄 모른다.\n *\n * ── 자정이 아니라 첫 교대에서 시작한다 ─────────────────────────────────────\n * 하루를 00:00~24:00 으로 재면 야간 교대가 있는 공장에서 **없는 틈이 보이고 있는 틈이 안 보인다.**\n *\n * ```\n * 주간 08:00-17:00 · 야간 18:00-익일 02:00\n *\n * 00:00 기준 00:00~08:00 이 빈 것으로 보인다 ← 어제 야간이 덮고 있다\n * 02:00~08:00 의 진짜 틈은 안 보인다\n * 첫 교대 기준 17:00~18:00 · 익일 02:00~08:00 ← 둘 다 맞다\n * ```\n *\n * 그래서 **첫 교대가 시작하는 시각부터 24시간**을 한 바퀴로 본다. 그것이 이 공장의 하루다.\n */\n\nexport interface ShiftLike {\n name: string\n /** Work weekdays are the operating date of the shift; blank means every day. */\n daysOfWeek?: string\n /** 시작이 그날인가 전날인가 다음 날인가 — `-1` · `0` · `1`. */\n fromDate: number\n /** `HH:mm` 또는 `HH:mm:ss`. */\n fromTime: string\n toDate: number\n toTime: string\n}\n\nexport interface ShiftSpan {\n name: string\n /** 조업일 시작(0분)부터 몇 분 뒤인가. 전날이면 음수다. */\n fromMinute: number\n toMinute: number\n}\n\nexport interface CoverageProblem {\n kind: 'gap' | 'overlap' | 'not-a-span'\n /** 걸린 교대 이름들. `gap` 은 그 틈의 앞뒤 둘. */\n names: string[]\n fromMinute?: number\n toMinute?: number\n}\n\n/** 이 편성의 한 바퀴 — 첫 교대가 시작하는 시각부터 24시간. */\nexport interface ShiftCycle {\n startMinute: number\n endMinute: number\n spans: ShiftSpan[]\n}\n\nconst DAY = 24 * 60\n\n/** `HH:mm[:ss]` 를 분으로. 읽을 수 없으면 없다고 답한다 — 0 으로 두면 자정으로 읽힌다. */\nexport function minutesOf(time: string): number | undefined {\n const parts = String(time ?? '').split(':')\n\n if (parts.length < 2) {\n return undefined\n }\n\n const hours = Number(parts[0])\n const minutes = Number(parts[1])\n\n if (!Number.isFinite(hours) || !Number.isFinite(minutes)) {\n return undefined\n }\n\n return hours * 60 + minutes\n}\n\n/** 교대 하나를 조업일 기준 분 구간으로. 읽을 수 없으면 없다. */\nexport function spanOf(shift: ShiftLike): ShiftSpan | undefined {\n const from = minutesOf(shift.fromTime)\n const to = minutesOf(shift.toTime)\n\n if (from === undefined || to === undefined) {\n return undefined\n }\n\n return {\n name: shift.name,\n fromMinute: Number(shift.fromDate ?? 0) * DAY + from,\n toMinute: Number(shift.toDate ?? 0) * DAY + to\n }\n}\n\n/**\n * 이 편성의 한 바퀴 — **첫 교대가 시작하는 시각부터 24시간.**\n *\n * 읽을 수 없는 교대는 여기 들지 않는다. 그것은 `coverageProblems` 가 이름과 함께 낸다.\n */\nexport function cycleOf(shifts: ShiftLike[]): ShiftCycle | undefined {\n const spans = shifts\n .map(spanOf)\n .filter((span): span is ShiftSpan => Boolean(span) && span!.toMinute > span!.fromMinute)\n .sort((a, b) => a.fromMinute - b.fromMinute)\n\n if (!spans.length) {\n return undefined\n }\n\n const startMinute = spans[0].fromMinute\n\n return { startMinute, endMinute: startMinute + DAY, spans }\n}\n\n/**\n * 편성을 훑어 틈과 겹침을 찾는다.\n *\n * 빈 편성은 문제가 아니다 — 아직 안 정한 것이지 잘못 정한 것이 아니다. 그 사실은 부르는 쪽이 목록의\n * 길이로 안다.\n *\n * **마지막 교대와 다음 바퀴의 첫 교대 사이도 본다.** 야간 교대가 새벽에 끝나는 공장에서 그 틈이 가장\n * 크고, 자정 기준으로 재면 그것이 보이지 않는다.\n */\nexport function coverageProblems(shifts: ShiftLike[]): CoverageProblem[] {\n const problems: CoverageProblem[] = []\n const spans: ShiftSpan[] = []\n\n for (const shift of shifts) {\n const span = spanOf(shift)\n\n if (!span || span.toMinute <= span.fromMinute) {\n /* 시각을 읽을 수 없거나 구간이 되지 않는다. 빼고 넘어가면 그 교대가 화면에서 사라진다. */\n problems.push({ kind: 'not-a-span', names: [shift.name] })\n continue\n }\n\n spans.push(span)\n }\n\n const sorted = [...spans].sort((a, b) => a.fromMinute - b.fromMinute)\n\n for (let i = 1; i < sorted.length; i += 1) {\n const before = sorted[i - 1]\n const after = sorted[i]\n\n if (after.fromMinute > before.toMinute) {\n problems.push({\n kind: 'gap',\n names: [before.name, after.name],\n fromMinute: before.toMinute,\n toMinute: after.fromMinute\n })\n } else if (after.fromMinute < before.toMinute) {\n problems.push({\n kind: 'overlap',\n names: [before.name, after.name],\n fromMinute: after.fromMinute,\n toMinute: Math.min(before.toMinute, after.toMinute)\n })\n }\n }\n\n /* 한 바퀴가 닫히나 — 마지막 교대가 끝나고 다음 바퀴 첫 교대가 시작할 때까지. */\n if (sorted.length) {\n const first = sorted[0]\n const lastEnd = Math.max(...sorted.map(span => span.toMinute))\n const nextStart = first.fromMinute + DAY\n\n const last = sorted.find(span => span.toMinute === lastEnd) ?? sorted[sorted.length - 1]\n\n if (lastEnd < nextStart) {\n problems.push({ kind: 'gap', names: [last.name, first.name], fromMinute: lastEnd, toMinute: nextStart })\n } else if (lastEnd > nextStart && sorted.length > 1) {\n /* The last shift runs into the next day's first one - a night shift ending after the morning shift starts. */\n problems.push({ kind: 'overlap', names: [last.name, first.name], fromMinute: nextStart, toMinute: lastEnd })\n }\n }\n\n return problems\n}\n\n/** 편성이 덮는 전체 길이(분). 화면이 「24시간을 덮는가」를 말하는 근거. */\nexport function coveredMinutes(shifts: ShiftLike[]): number {\n const spans = shifts.map(spanOf).filter((span): span is ShiftSpan => Boolean(span))\n\n if (!spans.length) {\n return 0\n }\n\n const sorted = [...spans].sort((a, b) => a.fromMinute - b.fromMinute)\n let covered = 0\n let cursorEnd = sorted[0].fromMinute\n\n for (const span of sorted) {\n const start = Math.max(span.fromMinute, cursorEnd)\n\n if (span.toMinute > start) {\n covered += span.toMinute - start\n cursorEnd = span.toMinute\n }\n }\n\n return covered\n}\n\n/** 분을 `HH:mm` 으로. 조업일을 넘어간 것은 넘어간 대로 적는다 — 24:30 은 다음 날 00:30 이다. */\nexport function clockOf(minute: number): string {\n const sign = minute < 0 ? '-' : ''\n const value = Math.abs(minute)\n\n return `${sign}${String(Math.floor(value / 60)).padStart(2, '0')}:${String(value % 60).padStart(2, '0')}`\n}\n\nexport interface TimelinePiece {\n kind: 'shift' | 'gap' | 'overlap'\n /** Shift names: one for a shift, the two either side for a gap, the two that collide for an overlap. */\n names: string[]\n fromMinute: number\n toMinute: number\n /** Position on the bar, in percent of the day. */\n left: number\n width: number\n}\n\nexport interface Timeline {\n startMinute: number\n pieces: TimelinePiece[]\n /** Where each midnight inside the day falls, in percent - the bar starts at the first shift, not at 00:00. */\n midnights: number[]\n /** Six-hour marks from the start of the day, in minutes, both ends included. */\n ticks: number[]\n}\n\n/**\n * The bar the screen draws for one day of an arrangement: shifts, the gaps between them and the overlaps,\n * laid out over the 24 hours that start with the first shift. Shift pieces are clipped to that day; a gap or\n * an overlap is drawn on top of the shifts so it is never hidden behind one.\n */\nexport function timelineOf(shifts: ShiftLike[]): Timeline | undefined {\n const cycle = cycleOf(shifts)\n if (!cycle) return undefined\n\n const place = (kind: TimelinePiece['kind'], names: string[], from: number, to: number): TimelinePiece | undefined => {\n /* A problem at the turn of the day (the next day's first shift) is drawn where that shift starts: at the left. */\n if (kind !== 'shift' && from >= cycle.endMinute) {\n from -= DAY\n to -= DAY\n }\n const left = Math.max(cycle.startMinute, from)\n const right = Math.min(cycle.endMinute, to)\n if (right <= left) return undefined\n return {\n kind,\n names,\n fromMinute: from,\n toMinute: to,\n left: ((left - cycle.startMinute) / DAY) * 100,\n width: ((right - left) / DAY) * 100\n }\n }\n\n const pieces = [\n ...cycle.spans.map(span => place('shift', [span.name], span.fromMinute, span.toMinute)),\n ...coverageProblems(shifts)\n .filter(problem => problem.kind !== 'not-a-span')\n .map(problem => place(problem.kind as 'gap' | 'overlap', problem.names, problem.fromMinute!, problem.toMinute!))\n ].filter((piece): piece is TimelinePiece => Boolean(piece))\n\n const midnights: number[] = []\n for (let m = Math.ceil(cycle.startMinute / DAY) * DAY; m < cycle.endMinute; m += DAY) {\n if (m > cycle.startMinute) midnights.push(((m - cycle.startMinute) / DAY) * 100)\n }\n\n return {\n startMinute: cycle.startMinute,\n pieces,\n midnights,\n ticks: [0, 6, 12, 18, 24].map(hour => cycle.startMinute + hour * 60)\n }\n}\n"]}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import type { ShiftLike } from './shift-coverage';
|
|
2
|
+
export interface EditableShift extends ShiftLike {
|
|
3
|
+
description?: string;
|
|
4
|
+
}
|
|
5
|
+
/** Display order of the week: Monday first. */
|
|
6
|
+
export declare const WEEK: readonly ["1", "2", "3", "4", "5", "6", "0"];
|
|
7
|
+
/** The weekdays a stored value names, in display order. Empty or unreadable means every day. */
|
|
8
|
+
export declare function daysOf(daysOfWeek?: string | null): string[];
|
|
9
|
+
/**
|
|
10
|
+
* The stored value for a chosen set of weekdays: '' for all seven, a sorted CSV otherwise.
|
|
11
|
+
* `undefined` when nothing is chosen - a shift that runs on no day is not a shift, and '' would read as every day.
|
|
12
|
+
*/
|
|
13
|
+
export declare function daysValue(chosen: readonly string[]): string | undefined;
|
|
14
|
+
/**
|
|
15
|
+
* A short label for a set of weekdays: runs of three or more consecutive days (Monday first) collapse to a
|
|
16
|
+
* range, the rest are listed. `월–금`, `월·수·금`, `토·일`, and the every-day word for all seven.
|
|
17
|
+
*/
|
|
18
|
+
export declare function daysLabel(days: readonly string[], name: (day: string) => string, everyDay: string): string;
|
|
19
|
+
/** The end day that follows from the start: an end at or before the start clock is on the next day. */
|
|
20
|
+
export declare function endDateOf(shift: Pick<ShiftLike, 'fromDate' | 'fromTime' | 'toTime'>): number;
|
|
21
|
+
/** The shift with its end day put right. Call after every change to a start day or a clock. */
|
|
22
|
+
export declare function settle<T extends EditableShift>(shift: T): T;
|
|
23
|
+
/** Length of a shift in minutes, or undefined when its clocks cannot be read. */
|
|
24
|
+
export declare function lengthOf(shift: ShiftLike): number | undefined;
|
|
25
|
+
/**
|
|
26
|
+
* The row "add shift" puts in: it starts where the last shift ends and lasts eight hours, on the same days.
|
|
27
|
+
* The work day is usually split into back-to-back shifts, so this is the next one more often than not.
|
|
28
|
+
*/
|
|
29
|
+
export declare function nextShift(shifts: readonly EditableShift[]): EditableShift;
|
|
30
|
+
/**
|
|
31
|
+
* Weekdays grouped by the shifts that run on them, Monday first: one group per distinct set.
|
|
32
|
+
* A plant that runs the same shifts every day gets one group; days with no shift form a group with none.
|
|
33
|
+
*/
|
|
34
|
+
export declare function weekGroups<T extends ShiftLike>(shifts: readonly T[]): {
|
|
35
|
+
days: string[];
|
|
36
|
+
shifts: T[];
|
|
37
|
+
}[];
|
|
38
|
+
export type RowIssue = 'name-missing' | 'name-duplicate' | 'time-missing' | 'ends-too-late';
|
|
39
|
+
export interface WeekOverlap {
|
|
40
|
+
names: [string, string];
|
|
41
|
+
/** Work weekday of the later shift - the day the overlap is read on. */
|
|
42
|
+
day: string;
|
|
43
|
+
/** Minutes from that work date's midnight. */
|
|
44
|
+
fromMinute: number;
|
|
45
|
+
toMinute: number;
|
|
46
|
+
}
|
|
47
|
+
export interface DraftIssues {
|
|
48
|
+
/** Problems of one row, by its index. Rows without problems are absent. */
|
|
49
|
+
rows: Map<number, RowIssue[]>;
|
|
50
|
+
/** Shifts that run at the same time somewhere in the week - the server refuses these. */
|
|
51
|
+
overlaps: WeekOverlap[];
|
|
52
|
+
reasonMissing: boolean;
|
|
53
|
+
/** True when saving would be refused. Gaps are not here: an arrangement with gaps is allowed. */
|
|
54
|
+
blocking: boolean;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* What stops a draft from saving, found before the server refuses it.
|
|
58
|
+
*
|
|
59
|
+
* The same rules as the server's content check: a non-empty unique name, readable clocks, a start day the
|
|
60
|
+
* end can follow (a shift that starts on the next day must end on it), a reason, and no two shifts running
|
|
61
|
+
* at the same time anywhere in the week - including a Friday night shift running into Saturday's first one.
|
|
62
|
+
*/
|
|
63
|
+
export declare function draftIssues(draft: {
|
|
64
|
+
shifts: readonly EditableShift[];
|
|
65
|
+
reason?: string;
|
|
66
|
+
}): DraftIssues;
|
|
67
|
+
/** The content the server takes: trimmed names, end days put right, nothing the editor added. */
|
|
68
|
+
export declare function shiftsContent(shifts: readonly EditableShift[]): EditableShift[];
|
|
69
|
+
export interface OverlapGroup {
|
|
70
|
+
names: [string, string];
|
|
71
|
+
/** Work weekdays it happens on, in display order. */
|
|
72
|
+
days: string[];
|
|
73
|
+
fromMinute: number;
|
|
74
|
+
toMinute: number;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Week overlaps folded into one line per pair and clock range: two every-day shifts that collide make one
|
|
78
|
+
* finding on seven days, not seven findings.
|
|
79
|
+
*/
|
|
80
|
+
export declare function groupOverlaps(overlaps: readonly WeekOverlap[]): OverlapGroup[];
|