@easyteam/auto-scheduler-modal-ui 0.1.0

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/index.js ADDED
@@ -0,0 +1,2534 @@
1
+ 'use client'
2
+
3
+ // src/AutoSchedulerModal.tsx
4
+ import { useEffect, useMemo as useMemo2, useRef, useState as useState2 } from "react";
5
+ import {
6
+ Badge,
7
+ Box as Box2,
8
+ Button as Button2,
9
+ ChakraProvider,
10
+ Checkbox,
11
+ Divider,
12
+ HStack as HStack2,
13
+ IconButton as IconButton2,
14
+ Input,
15
+ InputGroup,
16
+ InputRightAddon,
17
+ Modal,
18
+ ModalBody,
19
+ ModalContent,
20
+ ModalFooter,
21
+ ModalHeader,
22
+ ModalOverlay,
23
+ Popover,
24
+ PopoverBody,
25
+ PopoverContent,
26
+ PopoverTrigger,
27
+ Spinner as Spinner2,
28
+ Stack as Stack2,
29
+ Text as Text2,
30
+ useTheme
31
+ } from "@chakra-ui/react";
32
+
33
+ // src/icons/AutoSchedulerIcons.tsx
34
+ import { jsx, jsxs } from "react/jsx-runtime";
35
+ function DotIcon({ size = 6, fill, stroke, ...props }) {
36
+ const useStroke = stroke != null;
37
+ return /* @__PURE__ */ jsx(
38
+ "svg",
39
+ {
40
+ "aria-hidden": "true",
41
+ viewBox: "0 0 20 20",
42
+ fill: useStroke ? "none" : fill != null ? fill : "currentColor",
43
+ stroke,
44
+ width: size,
45
+ height: size,
46
+ ...props,
47
+ children: /* @__PURE__ */ jsx("circle", { cx: "10", cy: "10", r: "10" })
48
+ }
49
+ );
50
+ }
51
+ function MagicIcon(props) {
52
+ return /* @__PURE__ */ jsxs(
53
+ "svg",
54
+ {
55
+ "aria-hidden": "true",
56
+ viewBox: "0 0 20 20",
57
+ fill: "currentColor",
58
+ width: "20",
59
+ height: "20",
60
+ ...props,
61
+ children: [
62
+ /* @__PURE__ */ jsx("path", { d: "M10 2.5l1.2 3.3 3.3 1.2-3.3 1.2-1.2 3.3-1.2-3.3-3.3-1.2 3.3-1.2L10 2.5z" }),
63
+ /* @__PURE__ */ jsx("path", { d: "M4.5 11.2l.7 1.9 1.9.7-1.9.7-.7 1.9-.7-1.9-1.9-.7 1.9-.7.7-1.9z" }),
64
+ /* @__PURE__ */ jsx("path", { d: "M15.5 12.5l.6 1.7 1.7.6-1.7.6-.6 1.7-.6-1.7-1.7-.6 1.7-.6.6-1.7z" })
65
+ ]
66
+ }
67
+ );
68
+ }
69
+ function InfoIcon(props) {
70
+ return /* @__PURE__ */ jsx(
71
+ "svg",
72
+ {
73
+ "aria-hidden": "true",
74
+ viewBox: "0 0 20 20",
75
+ fill: "currentColor",
76
+ width: "20",
77
+ height: "20",
78
+ ...props,
79
+ children: /* @__PURE__ */ jsx("path", { d: "M10 1.75a8.25 8.25 0 1 0 0 16.5 8.25 8.25 0 0 0 0-16.5zm0 3.75a1 1 0 1 1 0 2 1 1 0 0 1 0-2zm-1 4.25c0-.41.34-.75.75-.75h.5c.41 0 .75.34.75.75v5.25a.75.75 0 0 1-1.5 0v-4.5h-.25a.75.75 0 0 1-.75-.75z" })
80
+ }
81
+ );
82
+ }
83
+ function WarningIcon(props) {
84
+ return /* @__PURE__ */ jsxs(
85
+ "svg",
86
+ {
87
+ "aria-hidden": "true",
88
+ viewBox: "0 0 20 20",
89
+ fill: "none",
90
+ width: "20",
91
+ height: "20",
92
+ ...props,
93
+ children: [
94
+ /* @__PURE__ */ jsx(
95
+ "path",
96
+ {
97
+ d: "M9.25 3.6a.86.86 0 0 1 1.5 0l7.2 12.1a.86.86 0 0 1-.75 1.3H2.8a.86.86 0 0 1-.75-1.3l7.2-12.1z",
98
+ stroke: "currentColor",
99
+ strokeWidth: "1.4",
100
+ strokeLinejoin: "round"
101
+ }
102
+ ),
103
+ /* @__PURE__ */ jsx(
104
+ "path",
105
+ {
106
+ d: "M10 7v4.7",
107
+ stroke: "currentColor",
108
+ strokeWidth: "1.4",
109
+ strokeLinecap: "round"
110
+ }
111
+ ),
112
+ /* @__PURE__ */ jsx("circle", { cx: "10", cy: "13.5", r: "0.9", fill: "currentColor" })
113
+ ]
114
+ }
115
+ );
116
+ }
117
+ function ChevronDownIcon(props) {
118
+ return /* @__PURE__ */ jsx(
119
+ "svg",
120
+ {
121
+ "aria-hidden": "true",
122
+ viewBox: "0 0 20 20",
123
+ fill: "none",
124
+ width: "20",
125
+ height: "20",
126
+ ...props,
127
+ children: /* @__PURE__ */ jsx(
128
+ "path",
129
+ {
130
+ d: "M5 7.5l5 5 5-5",
131
+ stroke: "currentColor",
132
+ strokeWidth: "1.6",
133
+ strokeLinecap: "round",
134
+ strokeLinejoin: "round"
135
+ }
136
+ )
137
+ }
138
+ );
139
+ }
140
+ function CloseIcon(props) {
141
+ return /* @__PURE__ */ jsx(
142
+ "svg",
143
+ {
144
+ "aria-hidden": "true",
145
+ viewBox: "0 0 20 20",
146
+ fill: "none",
147
+ width: "20",
148
+ height: "20",
149
+ ...props,
150
+ children: /* @__PURE__ */ jsx(
151
+ "path",
152
+ {
153
+ d: "M5.5 5.5l9 9m0-9l-9 9",
154
+ stroke: "currentColor",
155
+ strokeWidth: "1.6",
156
+ strokeLinecap: "round",
157
+ strokeLinejoin: "round"
158
+ }
159
+ )
160
+ }
161
+ );
162
+ }
163
+ function CheckIcon(props) {
164
+ return /* @__PURE__ */ jsx(
165
+ "svg",
166
+ {
167
+ "aria-hidden": "true",
168
+ viewBox: "0 0 20 20",
169
+ fill: "none",
170
+ width: "20",
171
+ height: "20",
172
+ ...props,
173
+ children: /* @__PURE__ */ jsx(
174
+ "path",
175
+ {
176
+ d: "M5.5 10.5l3 3 6-6",
177
+ stroke: "currentColor",
178
+ strokeWidth: "1.6",
179
+ strokeLinecap: "round",
180
+ strokeLinejoin: "round"
181
+ }
182
+ )
183
+ }
184
+ );
185
+ }
186
+ function FailedIcon(props) {
187
+ return /* @__PURE__ */ jsx(
188
+ "svg",
189
+ {
190
+ "aria-hidden": "true",
191
+ viewBox: "0 0 20 20",
192
+ fill: "none",
193
+ width: "20",
194
+ height: "20",
195
+ ...props,
196
+ children: /* @__PURE__ */ jsx(
197
+ "path",
198
+ {
199
+ d: "M6.5 6.5l7 7m0-7l-7 7",
200
+ stroke: "currentColor",
201
+ strokeWidth: "1.8",
202
+ strokeLinecap: "round",
203
+ strokeLinejoin: "round"
204
+ }
205
+ )
206
+ }
207
+ );
208
+ }
209
+
210
+ // src/theme/mergeTheme.ts
211
+ var componentNameMap = {
212
+ button: "Button",
213
+ input: "Input",
214
+ tooltip: "Tooltip",
215
+ modal: "Modal",
216
+ switch: "Switch",
217
+ drawer: "Drawer",
218
+ checkbox: "Checkbox",
219
+ radio: "Radio",
220
+ menu: "Menu",
221
+ tabs: "Tabs",
222
+ select: "Select",
223
+ numberinput: "NumberInput",
224
+ textarea: "Textarea",
225
+ popover: "Popover",
226
+ badge: "Badge",
227
+ text: "Text"
228
+ };
229
+ var isRecord = (value) => Boolean(value) && typeof value === "object" && !Array.isArray(value);
230
+ var mapComponentOverrides = (overrides) => {
231
+ const result = {};
232
+ Object.entries(overrides).forEach(([key, value]) => {
233
+ var _a, _b;
234
+ if (key === "components" && isRecord(value)) {
235
+ Object.assign(result, value);
236
+ return;
237
+ }
238
+ const normalizedKey = (_b = (_a = componentNameMap[key]) != null ? _a : componentNameMap[key.toLowerCase()]) != null ? _b : key;
239
+ result[normalizedKey] = value;
240
+ });
241
+ return result;
242
+ };
243
+ var deepMerge = (target, source) => {
244
+ const result = { ...target };
245
+ Object.entries(source).forEach(([key, value]) => {
246
+ if (isRecord(value) && isRecord(result[key])) {
247
+ result[key] = deepMerge(result[key], value);
248
+ return;
249
+ }
250
+ result[key] = value;
251
+ });
252
+ return result;
253
+ };
254
+ var resolveThemeParts = (theme) => {
255
+ if (!theme || !isRecord(theme)) {
256
+ return {};
257
+ }
258
+ const autoSchedulerModal = isRecord(theme.autoSchedulerModal) ? theme.autoSchedulerModal : void 0;
259
+ if ("tokens" in theme) {
260
+ const easyTheme = theme;
261
+ const { tokens, autoSchedulerModal: _ignored2, ...rest } = easyTheme;
262
+ const chakraOverride = {
263
+ ...isRecord(tokens) ? tokens : {},
264
+ components: mapComponentOverrides(rest)
265
+ };
266
+ return { chakraOverride, styleOverrides: autoSchedulerModal };
267
+ }
268
+ const { autoSchedulerModal: _ignored, ...override } = theme;
269
+ return { chakraOverride: override, styleOverrides: autoSchedulerModal };
270
+ };
271
+ var mergeThemeWithBase = (baseTheme, override) => {
272
+ if (!override) {
273
+ return baseTheme;
274
+ }
275
+ return deepMerge(baseTheme, override);
276
+ };
277
+
278
+ // src/constants.ts
279
+ var HARD_CONSTRAINT_DEFAULTS = {
280
+ max_weekly_hours: "40",
281
+ max_daily_hours: "8",
282
+ min_rest_between_shifts: "24",
283
+ max_work_days_per_week: "6"
284
+ };
285
+ var HARD_CONSTRAINT_OPTIONS = [
286
+ { id: "max_weekly_hours", name: "Maximum weekly hours" },
287
+ { id: "max_daily_hours", name: "Maximum daily hours" },
288
+ { id: "min_rest_between_shifts", name: "Minimum rest between shifts" },
289
+ { id: "max_work_days_per_week", name: "Maximum work days per week" }
290
+ // { id: "minor_max_daily_hours", name: "Minor maximum daily hours" },
291
+ // { id: "minor_late_hours_restriction", name: "Minor late hours restriction" },
292
+ // { id: "part_time_weekly_limit", name: "Part-time weekly limit" },
293
+ // { id: "min_staffing", name: "Minimum staffing" },
294
+ ];
295
+ var OPTIMIZATION_OPTION_GROUPS = [
296
+ {
297
+ id: "employee_happiness",
298
+ name: "Employee happiness",
299
+ options: [
300
+ {
301
+ id: "fair_hour_distribution",
302
+ title: "Fair hour distribution",
303
+ subtitle: "Distribute shifts evenly across all staff members"
304
+ },
305
+ {
306
+ id: "staff_preferences",
307
+ title: "Respect open shift preferences",
308
+ subtitle: "Consider staff requests and preferred shifts when assigning"
309
+ }
310
+ ]
311
+ },
312
+ {
313
+ id: "labor_cost_optimization",
314
+ name: "Labor cost optimization",
315
+ options: [
316
+ {
317
+ id: "minimize_overtime",
318
+ title: "Minimize overtime",
319
+ subtitle: "Reduce hours beyond standard work time"
320
+ },
321
+ {
322
+ id: "cost_to_sales_ratio",
323
+ title: "Cost-to-sales ratio",
324
+ subtitle: "Assign shifts based on total compensation (wages + commissions) compared to sales performance"
325
+ }
326
+ ]
327
+ },
328
+ {
329
+ id: "sales_optimization",
330
+ name: "Sales optimization",
331
+ options: [
332
+ {
333
+ id: "maximize_sales",
334
+ title: "Maximize sales",
335
+ subtitle: "Prioritize top-performing staff during peak hours"
336
+ }
337
+ ]
338
+ }
339
+ ];
340
+ var defaultStyles = {
341
+ overlay: {
342
+ bg: "rgba(0, 0, 0, 0.45)"
343
+ },
344
+ content: {
345
+ maxW: "600px",
346
+ bg: "white",
347
+ borderRadius: "20px",
348
+ boxShadow: "0px 20px 20px -8px rgba(26, 26, 26, 0.28), inset 0px 1px 0px rgba(204, 204, 204, 0.5), inset 0px -1px 0px rgba(0, 0, 0, 0.17), inset 1px 0px 0px rgba(0, 0, 0, 0.13), inset -1px 0px 0px rgba(0, 0, 0, 0.17)",
349
+ overflow: "hidden",
350
+ maxH: "calc(100vh - 32px)",
351
+ display: "flex",
352
+ flexDirection: "column"
353
+ },
354
+ header: {
355
+ bg: "#f3f3f3",
356
+ borderBottom: "1px solid #e3e3e3",
357
+ px: "16px",
358
+ py: "12px"
359
+ },
360
+ title: {
361
+ fontSize: "14px",
362
+ fontWeight: 600,
363
+ color: "#303030"
364
+ },
365
+ closeButton: {
366
+ color: "#5c5c5c",
367
+ borderRadius: "8px"
368
+ },
369
+ body: {
370
+ px: "16px",
371
+ py: "16px",
372
+ overflowY: "auto",
373
+ flex: "1 1 auto"
374
+ },
375
+ banner: {
376
+ backgroundColor: "#F5EFFF !important",
377
+ borderRadius: "8px",
378
+ px: "8px",
379
+ py: "8px",
380
+ display: "flex",
381
+ gap: "8px",
382
+ alignItems: "flex-start"
383
+ },
384
+ bannerIcon: {
385
+ color: "#5c2dbf"
386
+ },
387
+ bannerTitle: {
388
+ fontSize: "13px",
389
+ fontWeight: 700,
390
+ color: "#370e88",
391
+ lineHeight: "20px"
392
+ },
393
+ bannerText: {
394
+ fontSize: "13px",
395
+ color: "#370e88",
396
+ lineHeight: "20px"
397
+ },
398
+ locationSection: {},
399
+ locationLabelRow: {
400
+ alignItems: "center",
401
+ gap: "8px"
402
+ },
403
+ locationLabel: {
404
+ fontSize: "14px",
405
+ fontWeight: 600,
406
+ color: "#303030"
407
+ },
408
+ locationBadge: {
409
+ bg: "#e1e3e5",
410
+ color: "#303030",
411
+ borderRadius: "999px",
412
+ px: "6px",
413
+ py: "2px",
414
+ fontSize: "11px",
415
+ fontWeight: 600
416
+ },
417
+ locationSubtitle: {
418
+ fontSize: "12px",
419
+ color: "#616161"
420
+ },
421
+ locationTrigger: {
422
+ w: "100%",
423
+ justifyContent: "space-between",
424
+ px: "12px",
425
+ h: "32px",
426
+ borderRadius: "8px",
427
+ border: "1px solid rgb(185, 184, 184)"
428
+ },
429
+ locationTriggerText: {
430
+ fontSize: "13px",
431
+ fontWeight: 450,
432
+ color: "#616161"
433
+ },
434
+ popoverContent: {
435
+ w: "100%",
436
+ bg: "white",
437
+ borderRadius: "12px",
438
+ px: "6px",
439
+ py: "6px",
440
+ border: "none",
441
+ maxH: "260px",
442
+ overflowY: "auto",
443
+ boxShadow: "0px 4px 6px -2px rgba(26, 26, 26, 0.2), inset 0px 1px 0px rgba(204, 204, 204, 0.5), inset 0px -1px 0px rgba(0, 0, 0, 0.17), inset 1px 0px 0px rgba(0, 0, 0, 0.13), inset -1px 0px 0px rgba(0, 0, 0, 0.13)"
444
+ },
445
+ groupHeader: {
446
+ px: "6px",
447
+ pt: "2px",
448
+ pb: "4px",
449
+ fontSize: "13px",
450
+ fontWeight: 600,
451
+ color: "#303030"
452
+ },
453
+ optionRow: {
454
+ px: "6px",
455
+ py: "6px",
456
+ borderRadius: "8px",
457
+ alignItems: "center",
458
+ gap: "8px"
459
+ },
460
+ optionLabel: {
461
+ fontSize: "13px",
462
+ color: "#303030"
463
+ },
464
+ selectedBanner: {
465
+ bg: "#eaf4ff",
466
+ borderRadius: "8px",
467
+ px: "8px",
468
+ py: "8px",
469
+ display: "flex",
470
+ gap: "8px",
471
+ alignItems: "flex-start",
472
+ color: "#00527c"
473
+ },
474
+ selectedBannerIcon: {
475
+ color: "#00527c"
476
+ },
477
+ selectedBannerText: {
478
+ fontSize: "13px",
479
+ lineHeight: "20px",
480
+ color: "#00527c"
481
+ },
482
+ hardConstraintsSection: {},
483
+ hardConstraintsLabelRow: {
484
+ alignItems: "center",
485
+ gap: "8px"
486
+ },
487
+ hardConstraintsLabel: {
488
+ fontSize: "14px",
489
+ fontWeight: 600,
490
+ color: "#303030"
491
+ },
492
+ hardConstraintsBadge: {
493
+ bg: "rgba(0, 0, 0, 0.06)",
494
+ color: "#616161",
495
+ borderRadius: "8px",
496
+ px: "8px",
497
+ py: "2px",
498
+ fontSize: "12px",
499
+ fontWeight: 500
500
+ },
501
+ hardConstraintsSubtitle: {
502
+ fontSize: "13px",
503
+ color: "#616161"
504
+ },
505
+ hardConstraintsTrigger: {
506
+ w: "100%",
507
+ justifyContent: "space-between",
508
+ px: "12px",
509
+ h: "32px",
510
+ borderRadius: "8px",
511
+ border: "1px solid rgb(185, 184, 184)"
512
+ },
513
+ hardConstraintsTriggerText: {
514
+ fontSize: "13px",
515
+ fontWeight: 450,
516
+ color: "#616161"
517
+ },
518
+ hardConstraintsPopoverContent: {
519
+ w: "100%",
520
+ bg: "white",
521
+ borderRadius: "12px",
522
+ px: "6px",
523
+ py: "6px",
524
+ border: "none",
525
+ boxShadow: "0px 4px 6px -2px rgba(26, 26, 26, 0.2), inset 0px 1px 0px rgba(204, 204, 204, 0.5), inset 0px -1px 0px rgba(0, 0, 0, 0.17), inset 1px 0px 0px rgba(0, 0, 0, 0.13), inset -1px 0px 0px rgba(0, 0, 0, 0.13)"
526
+ },
527
+ hardConstraintsOptionRow: {
528
+ px: "6px",
529
+ py: "6px",
530
+ borderRadius: "8px",
531
+ alignItems: "center",
532
+ gap: "8px"
533
+ },
534
+ hardConstraintsOptionLabel: {
535
+ fontSize: "13px",
536
+ color: "#303030"
537
+ },
538
+ hardConstraintsInputsContainer: {
539
+ bg: "#f7f7f7",
540
+ border: "1px solid #ebebeb",
541
+ borderRadius: "8px",
542
+ px: "16px",
543
+ py: "16px"
544
+ },
545
+ hardConstraintsInputItem: {
546
+ gap: "4px",
547
+ maxW: "320px"
548
+ },
549
+ hardConstraintsInputLabel: {
550
+ fontSize: "13px",
551
+ color: "#303030"
552
+ },
553
+ hardConstraintsInputField: {
554
+ bg: "#fdfdfd",
555
+ border: "1px solid #8a8a8a",
556
+ borderTopLeftRadius: "8px",
557
+ borderBottomLeftRadius: "8px",
558
+ borderTopRightRadius: 0,
559
+ borderBottomRightRadius: 0,
560
+ h: "28px",
561
+ px: "12px"
562
+ },
563
+ optimizationSection: {},
564
+ optimizationLabelRow: {
565
+ alignItems: "center",
566
+ gap: "8px"
567
+ },
568
+ optimizationLabel: {
569
+ fontSize: "14px",
570
+ fontWeight: 600,
571
+ color: "#303030"
572
+ },
573
+ optimizationBadge: {
574
+ bg: "rgba(0, 0, 0, 0.06)",
575
+ color: "#616161",
576
+ borderRadius: "8px",
577
+ px: "8px",
578
+ py: "2px",
579
+ fontSize: "12px",
580
+ fontWeight: 500
581
+ },
582
+ optimizationSubtitle: {
583
+ fontSize: "13px",
584
+ color: "#616161"
585
+ },
586
+ optimizationTrigger: {
587
+ w: "100%",
588
+ justifyContent: "space-between",
589
+ px: "12px",
590
+ h: "32px",
591
+ borderRadius: "8px",
592
+ border: "1px solid rgb(185, 184, 184)"
593
+ },
594
+ optimizationTriggerText: {
595
+ fontSize: "13px",
596
+ fontWeight: 450,
597
+ color: "#616161"
598
+ },
599
+ optimizationPopoverContent: {
600
+ w: "100%",
601
+ bg: "white",
602
+ borderRadius: "12px",
603
+ px: "6px",
604
+ py: "6px",
605
+ border: "none",
606
+ boxShadow: "0px 4px 6px -2px rgba(26, 26, 26, 0.2), inset 0px 1px 0px rgba(204, 204, 204, 0.5), inset 0px -1px 0px rgba(0, 0, 0, 0.17), inset 1px 0px 0px rgba(0, 0, 0, 0.13), inset -1px 0px 0px rgba(0, 0, 0, 0.13)"
607
+ },
608
+ optimizationGroupHeader: {
609
+ px: "6px",
610
+ pt: "2px",
611
+ pb: "4px",
612
+ fontSize: "13px",
613
+ fontWeight: 600,
614
+ color: "#303030"
615
+ },
616
+ optimizationOptionRow: {
617
+ px: "6px",
618
+ py: "6px",
619
+ borderRadius: "8px",
620
+ alignItems: "flex-start",
621
+ gap: "8px"
622
+ },
623
+ optimizationOptionLabel: {
624
+ fontSize: "13px",
625
+ color: "#303030"
626
+ },
627
+ optimizationOptionHelp: {
628
+ fontSize: "13px",
629
+ color: "#616161",
630
+ ml: "1.5rem"
631
+ },
632
+ optimizationInputsContainer: {
633
+ bg: "#f7f7f7",
634
+ border: "1px solid #ebebeb",
635
+ borderRadius: "8px",
636
+ px: "16px",
637
+ py: "16px"
638
+ },
639
+ optimizationInputItem: {
640
+ gap: "4px",
641
+ maxW: "320px"
642
+ },
643
+ optimizationInputLabel: {
644
+ fontSize: "13px",
645
+ color: "#303030"
646
+ },
647
+ optimizationInputField: {
648
+ bg: "#fdfdfd",
649
+ border: "1px solid #8a8a8a",
650
+ borderTopLeftRadius: "8px",
651
+ borderBottomLeftRadius: "8px",
652
+ borderTopRightRadius: 0,
653
+ borderBottomRightRadius: 0,
654
+ h: "28px",
655
+ px: "12px"
656
+ },
657
+ optimizationSuffix: {
658
+ fontSize: "13px",
659
+ color: "#616161",
660
+ h: "28px",
661
+ bg: "#fdfdfd",
662
+ border: "1px solid #8a8a8a",
663
+ borderLeft: "none",
664
+ borderTopLeftRadius: 0,
665
+ borderBottomLeftRadius: 0,
666
+ borderTopRightRadius: "8px",
667
+ borderBottomRightRadius: "8px"
668
+ },
669
+ optimizationDivider: {
670
+ borderColor: "#ebebeb"
671
+ },
672
+ optimizationSummary: {
673
+ fontSize: "13px",
674
+ color: "#616161"
675
+ },
676
+ creatingBody: {
677
+ py: "20px"
678
+ },
679
+ stepsContainer: {},
680
+ stepsList: {},
681
+ stepRow: {
682
+ alignItems: "flex-start",
683
+ gap: "12px"
684
+ },
685
+ stepIcon: {
686
+ width: "20px",
687
+ height: "20px",
688
+ borderRadius: "999px",
689
+ border: "2px solid #d1d1d1",
690
+ display: "flex",
691
+ alignItems: "center",
692
+ justifyContent: "center",
693
+ color: "#616161",
694
+ flexShrink: 0
695
+ },
696
+ stepIconActive: {
697
+ border: "none",
698
+ color: "#303030"
699
+ },
700
+ stepIconDone: {
701
+ border: "none",
702
+ bg: "#008060",
703
+ color: "white",
704
+ borderRadius: "10px"
705
+ },
706
+ stepIconPending: {
707
+ borderColor: "#d1d1d1",
708
+ color: "#d1d1d1"
709
+ },
710
+ stepIconFailed: {
711
+ border: "none",
712
+ bg: "#d72c0d",
713
+ color: "white",
714
+ borderRadius: "10px"
715
+ },
716
+ stepTitle: {
717
+ fontSize: "13px",
718
+ fontWeight: 600,
719
+ color: "#303030"
720
+ },
721
+ stepTitleActive: {},
722
+ stepTitleDone: {
723
+ color: "#9b9b9b",
724
+ fontWeight: 500
725
+ },
726
+ stepTitlePending: {
727
+ color: "#9b9b9b",
728
+ fontWeight: 500
729
+ },
730
+ stepTitleFailed: {
731
+ color: "#d72c0d",
732
+ fontWeight: 600
733
+ },
734
+ stepSubtitle: {
735
+ fontSize: "12px",
736
+ color: "#616161"
737
+ },
738
+ violationPanel: {
739
+ border: "1px solid #e7e7e7",
740
+ borderRadius: "12px",
741
+ px: "12px",
742
+ py: "12px",
743
+ bg: "white"
744
+ },
745
+ violationHeaderRow: {
746
+ alignItems: "center",
747
+ justifyContent: "space-between"
748
+ },
749
+ violationHeader: {
750
+ alignItems: "center",
751
+ gap: "8px"
752
+ },
753
+ violationIcon: {
754
+ color: "#8E1F0B"
755
+ },
756
+ violationTitle: {
757
+ fontSize: "13px",
758
+ fontWeight: 600,
759
+ color: "#8E1F0B"
760
+ },
761
+ violationSubtitle: {
762
+ fontSize: "12px",
763
+ color: "#616161"
764
+ },
765
+ violationToggleButton: {
766
+ borderRadius: "8px",
767
+ color: "#5c5c5c"
768
+ },
769
+ violationToggleIcon: {
770
+ color: "#5c5c5c"
771
+ },
772
+ violationContent: {
773
+ pt: "8px"
774
+ },
775
+ violationItem: {
776
+ bg: "#fceaea",
777
+ borderRadius: "8px",
778
+ px: "10px",
779
+ py: "8px"
780
+ },
781
+ violationItemTitle: {
782
+ fontSize: "12px",
783
+ fontWeight: 600,
784
+ color: "#8E1F0B"
785
+ },
786
+ violationItemDetailRow: {
787
+ alignItems: "center"
788
+ },
789
+ violationItemDetail: {
790
+ fontSize: "12px",
791
+ color: "#b42318"
792
+ },
793
+ violationBullet: {
794
+ color: "#b42318",
795
+ flexShrink: 0
796
+ },
797
+ recommendationsBox: {
798
+ bg: "#eaf2ff",
799
+ borderRadius: "8px",
800
+ px: "10px",
801
+ py: "8px"
802
+ },
803
+ recommendationsTitle: {
804
+ fontSize: "12px",
805
+ fontWeight: 600,
806
+ color: "#1f4eb0",
807
+ mb: "4px"
808
+ },
809
+ recommendationsList: {},
810
+ recommendationsItem: {
811
+ fontSize: "12px",
812
+ color: "#1f4eb0"
813
+ },
814
+ recommendationsBullet: {
815
+ color: "#1f4eb0",
816
+ flexShrink: 0
817
+ },
818
+ recommendationsToggle: {
819
+ alignSelf: "flex-start",
820
+ borderRadius: "8px",
821
+ fontSize: "12px",
822
+ height: "28px",
823
+ px: "12px"
824
+ },
825
+ failedFooter: {
826
+ borderTop: "1px solid #e3e3e3",
827
+ px: "16px",
828
+ py: "16px",
829
+ justifyContent: "flex-end",
830
+ gap: "8px"
831
+ },
832
+ failedPrimaryButton: {
833
+ fontSize: "12px",
834
+ fontWeight: 600,
835
+ borderRadius: "8px",
836
+ bg: "#303030",
837
+ color: "white",
838
+ height: "30px"
839
+ },
840
+ failedSecondaryButton: {
841
+ fontSize: "12px",
842
+ fontWeight: 500,
843
+ borderRadius: "8px",
844
+ borderColor: "#e3e3e3",
845
+ color: "#303030",
846
+ height: "30px"
847
+ },
848
+ footer: {
849
+ borderTop: "1px solid #e3e3e3",
850
+ px: "16px",
851
+ py: "16px",
852
+ justifyContent: "flex-end",
853
+ gap: "8px"
854
+ },
855
+ cancelButton: {
856
+ fontSize: "12px",
857
+ fontWeight: 500,
858
+ borderRadius: "8px",
859
+ borderColor: "#e3e3e3",
860
+ color: "#303030",
861
+ height: "30px"
862
+ },
863
+ primaryButton: {
864
+ fontSize: "12px",
865
+ fontWeight: 600,
866
+ borderRadius: "8px",
867
+ bg: "#303030",
868
+ color: "white",
869
+ height: "30px"
870
+ },
871
+ violatedConstraintsSecondaryButton: {
872
+ fontWeight: 600
873
+ }
874
+ };
875
+ var SCHEDULE_STEPS = [
876
+ {
877
+ title: "Analyzing requirements",
878
+ subtitle: "Reviewing your required rules and optimization priorities."
879
+ },
880
+ {
881
+ title: "Validating required rules",
882
+ subtitle: "Ensuring all assigned shifts meet your required rules."
883
+ },
884
+ {
885
+ title: "Optimizing assigned shifts",
886
+ subtitle: "Applying optimization priorities to improve coverage and performance."
887
+ },
888
+ { title: "Checking schedule feasibility", subtitle: "Confirming the schedule is feasible and respects all required rules." },
889
+ { title: "Finalizing schedule" }
890
+ ];
891
+ var STEP_INTERVAL_MS = 1200;
892
+ var OPTIMIZING_STEP_INDEX = 2;
893
+ var NETWORK_ERROR_FAILURE = {
894
+ violatedConstraints: [
895
+ { title: "Something went wrong", detail: "Please try again or check your connection." }
896
+ ],
897
+ recommendedFixes: []
898
+ };
899
+
900
+ // src/utils/theme.ts
901
+ var buildStyles = (defaultStyles2, overrides) => ({
902
+ overlay: { ...defaultStyles2.overlay, ...overrides == null ? void 0 : overrides.overlay },
903
+ content: { ...defaultStyles2.content, ...overrides == null ? void 0 : overrides.content },
904
+ header: { ...defaultStyles2.header, ...overrides == null ? void 0 : overrides.header },
905
+ title: { ...defaultStyles2.title, ...overrides == null ? void 0 : overrides.title },
906
+ closeButton: { ...defaultStyles2.closeButton, ...overrides == null ? void 0 : overrides.closeButton },
907
+ body: { ...defaultStyles2.body, ...overrides == null ? void 0 : overrides.body },
908
+ banner: { ...defaultStyles2.banner, ...overrides == null ? void 0 : overrides.banner },
909
+ bannerIcon: { ...defaultStyles2.bannerIcon, ...overrides == null ? void 0 : overrides.bannerIcon },
910
+ bannerTitle: { ...defaultStyles2.bannerTitle, ...overrides == null ? void 0 : overrides.bannerTitle },
911
+ bannerText: { ...defaultStyles2.bannerText, ...overrides == null ? void 0 : overrides.bannerText },
912
+ locationSection: { ...defaultStyles2.locationSection, ...overrides == null ? void 0 : overrides.locationSection },
913
+ locationLabelRow: { ...defaultStyles2.locationLabelRow, ...overrides == null ? void 0 : overrides.locationLabelRow },
914
+ locationLabel: { ...defaultStyles2.locationLabel, ...overrides == null ? void 0 : overrides.locationLabel },
915
+ locationBadge: { ...defaultStyles2.locationBadge, ...overrides == null ? void 0 : overrides.locationBadge },
916
+ locationSubtitle: { ...defaultStyles2.locationSubtitle, ...overrides == null ? void 0 : overrides.locationSubtitle },
917
+ locationTrigger: { ...defaultStyles2.locationTrigger, ...overrides == null ? void 0 : overrides.locationTrigger },
918
+ locationTriggerText: {
919
+ ...defaultStyles2.locationTriggerText,
920
+ ...overrides == null ? void 0 : overrides.locationTriggerText
921
+ },
922
+ popoverContent: { ...defaultStyles2.popoverContent, ...overrides == null ? void 0 : overrides.popoverContent },
923
+ groupHeader: { ...defaultStyles2.groupHeader, ...overrides == null ? void 0 : overrides.groupHeader },
924
+ optionRow: { ...defaultStyles2.optionRow, ...overrides == null ? void 0 : overrides.optionRow },
925
+ optionLabel: { ...defaultStyles2.optionLabel, ...overrides == null ? void 0 : overrides.optionLabel },
926
+ selectedBanner: { ...defaultStyles2.selectedBanner, ...overrides == null ? void 0 : overrides.selectedBanner },
927
+ selectedBannerIcon: { ...defaultStyles2.selectedBannerIcon, ...overrides == null ? void 0 : overrides.selectedBannerIcon },
928
+ selectedBannerText: {
929
+ ...defaultStyles2.selectedBannerText,
930
+ ...overrides == null ? void 0 : overrides.selectedBannerText
931
+ },
932
+ hardConstraintsSection: {
933
+ ...defaultStyles2.hardConstraintsSection,
934
+ ...overrides == null ? void 0 : overrides.hardConstraintsSection
935
+ },
936
+ hardConstraintsLabelRow: {
937
+ ...defaultStyles2.hardConstraintsLabelRow,
938
+ ...overrides == null ? void 0 : overrides.hardConstraintsLabelRow
939
+ },
940
+ hardConstraintsLabel: {
941
+ ...defaultStyles2.hardConstraintsLabel,
942
+ ...overrides == null ? void 0 : overrides.hardConstraintsLabel
943
+ },
944
+ hardConstraintsBadge: {
945
+ ...defaultStyles2.hardConstraintsBadge,
946
+ ...overrides == null ? void 0 : overrides.hardConstraintsBadge
947
+ },
948
+ hardConstraintsSubtitle: {
949
+ ...defaultStyles2.hardConstraintsSubtitle,
950
+ ...overrides == null ? void 0 : overrides.hardConstraintsSubtitle
951
+ },
952
+ hardConstraintsTrigger: {
953
+ ...defaultStyles2.hardConstraintsTrigger,
954
+ ...overrides == null ? void 0 : overrides.hardConstraintsTrigger
955
+ },
956
+ hardConstraintsTriggerText: {
957
+ ...defaultStyles2.hardConstraintsTriggerText,
958
+ ...overrides == null ? void 0 : overrides.hardConstraintsTriggerText
959
+ },
960
+ hardConstraintsPopoverContent: {
961
+ ...defaultStyles2.hardConstraintsPopoverContent,
962
+ ...overrides == null ? void 0 : overrides.hardConstraintsPopoverContent
963
+ },
964
+ hardConstraintsOptionRow: {
965
+ ...defaultStyles2.hardConstraintsOptionRow,
966
+ ...overrides == null ? void 0 : overrides.hardConstraintsOptionRow
967
+ },
968
+ hardConstraintsOptionLabel: {
969
+ ...defaultStyles2.hardConstraintsOptionLabel,
970
+ ...overrides == null ? void 0 : overrides.hardConstraintsOptionLabel
971
+ },
972
+ hardConstraintsInputsContainer: {
973
+ ...defaultStyles2.hardConstraintsInputsContainer,
974
+ ...overrides == null ? void 0 : overrides.hardConstraintsInputsContainer
975
+ },
976
+ hardConstraintsInputItem: {
977
+ ...defaultStyles2.hardConstraintsInputItem,
978
+ ...overrides == null ? void 0 : overrides.hardConstraintsInputItem
979
+ },
980
+ hardConstraintsInputLabel: {
981
+ ...defaultStyles2.hardConstraintsInputLabel,
982
+ ...overrides == null ? void 0 : overrides.hardConstraintsInputLabel
983
+ },
984
+ hardConstraintsInputField: {
985
+ ...defaultStyles2.hardConstraintsInputField,
986
+ ...overrides == null ? void 0 : overrides.hardConstraintsInputField
987
+ },
988
+ optimizationSection: {
989
+ ...defaultStyles2.optimizationSection,
990
+ ...overrides == null ? void 0 : overrides.optimizationSection
991
+ },
992
+ optimizationLabelRow: {
993
+ ...defaultStyles2.optimizationLabelRow,
994
+ ...overrides == null ? void 0 : overrides.optimizationLabelRow
995
+ },
996
+ optimizationLabel: {
997
+ ...defaultStyles2.optimizationLabel,
998
+ ...overrides == null ? void 0 : overrides.optimizationLabel
999
+ },
1000
+ optimizationBadge: {
1001
+ ...defaultStyles2.optimizationBadge,
1002
+ ...overrides == null ? void 0 : overrides.optimizationBadge
1003
+ },
1004
+ optimizationSubtitle: {
1005
+ ...defaultStyles2.optimizationSubtitle,
1006
+ ...overrides == null ? void 0 : overrides.optimizationSubtitle
1007
+ },
1008
+ optimizationTrigger: {
1009
+ ...defaultStyles2.optimizationTrigger,
1010
+ ...overrides == null ? void 0 : overrides.optimizationTrigger
1011
+ },
1012
+ optimizationTriggerText: {
1013
+ ...defaultStyles2.optimizationTriggerText,
1014
+ ...overrides == null ? void 0 : overrides.optimizationTriggerText
1015
+ },
1016
+ optimizationPopoverContent: {
1017
+ ...defaultStyles2.optimizationPopoverContent,
1018
+ ...overrides == null ? void 0 : overrides.optimizationPopoverContent
1019
+ },
1020
+ optimizationGroupHeader: {
1021
+ ...defaultStyles2.optimizationGroupHeader,
1022
+ ...overrides == null ? void 0 : overrides.optimizationGroupHeader
1023
+ },
1024
+ optimizationOptionRow: {
1025
+ ...defaultStyles2.optimizationOptionRow,
1026
+ ...overrides == null ? void 0 : overrides.optimizationOptionRow
1027
+ },
1028
+ optimizationOptionLabel: {
1029
+ ...defaultStyles2.optimizationOptionLabel,
1030
+ ...overrides == null ? void 0 : overrides.optimizationOptionLabel
1031
+ },
1032
+ optimizationOptionHelp: {
1033
+ ...defaultStyles2.optimizationOptionHelp,
1034
+ ...overrides == null ? void 0 : overrides.optimizationOptionHelp
1035
+ },
1036
+ optimizationInputsContainer: {
1037
+ ...defaultStyles2.optimizationInputsContainer,
1038
+ ...overrides == null ? void 0 : overrides.optimizationInputsContainer
1039
+ },
1040
+ optimizationInputItem: {
1041
+ ...defaultStyles2.optimizationInputItem,
1042
+ ...overrides == null ? void 0 : overrides.optimizationInputItem
1043
+ },
1044
+ optimizationInputLabel: {
1045
+ ...defaultStyles2.optimizationInputLabel,
1046
+ ...overrides == null ? void 0 : overrides.optimizationInputLabel
1047
+ },
1048
+ optimizationInputField: {
1049
+ ...defaultStyles2.optimizationInputField,
1050
+ ...overrides == null ? void 0 : overrides.optimizationInputField
1051
+ },
1052
+ optimizationSuffix: {
1053
+ ...defaultStyles2.optimizationSuffix,
1054
+ ...overrides == null ? void 0 : overrides.optimizationSuffix
1055
+ },
1056
+ optimizationDivider: {
1057
+ ...defaultStyles2.optimizationDivider,
1058
+ ...overrides == null ? void 0 : overrides.optimizationDivider
1059
+ },
1060
+ optimizationSummary: {
1061
+ ...defaultStyles2.optimizationSummary,
1062
+ ...overrides == null ? void 0 : overrides.optimizationSummary
1063
+ },
1064
+ creatingBody: {
1065
+ ...defaultStyles2.creatingBody,
1066
+ ...overrides == null ? void 0 : overrides.creatingBody
1067
+ },
1068
+ stepsContainer: {
1069
+ ...defaultStyles2.stepsContainer,
1070
+ ...overrides == null ? void 0 : overrides.stepsContainer
1071
+ },
1072
+ stepsList: {
1073
+ ...defaultStyles2.stepsList,
1074
+ ...overrides == null ? void 0 : overrides.stepsList
1075
+ },
1076
+ stepRow: {
1077
+ ...defaultStyles2.stepRow,
1078
+ ...overrides == null ? void 0 : overrides.stepRow
1079
+ },
1080
+ stepIcon: {
1081
+ ...defaultStyles2.stepIcon,
1082
+ ...overrides == null ? void 0 : overrides.stepIcon
1083
+ },
1084
+ stepIconActive: {
1085
+ ...defaultStyles2.stepIconActive,
1086
+ ...overrides == null ? void 0 : overrides.stepIconActive
1087
+ },
1088
+ stepIconDone: {
1089
+ ...defaultStyles2.stepIconDone,
1090
+ ...overrides == null ? void 0 : overrides.stepIconDone
1091
+ },
1092
+ stepIconPending: {
1093
+ ...defaultStyles2.stepIconPending,
1094
+ ...overrides == null ? void 0 : overrides.stepIconPending
1095
+ },
1096
+ stepIconFailed: {
1097
+ ...defaultStyles2.stepIconFailed,
1098
+ ...overrides == null ? void 0 : overrides.stepIconFailed
1099
+ },
1100
+ stepTitle: {
1101
+ ...defaultStyles2.stepTitle,
1102
+ ...overrides == null ? void 0 : overrides.stepTitle
1103
+ },
1104
+ stepTitleActive: {
1105
+ ...defaultStyles2.stepTitleActive,
1106
+ ...overrides == null ? void 0 : overrides.stepTitleActive
1107
+ },
1108
+ stepTitleDone: {
1109
+ ...defaultStyles2.stepTitleDone,
1110
+ ...overrides == null ? void 0 : overrides.stepTitleDone
1111
+ },
1112
+ stepTitlePending: {
1113
+ ...defaultStyles2.stepTitlePending,
1114
+ ...overrides == null ? void 0 : overrides.stepTitlePending
1115
+ },
1116
+ stepTitleFailed: {
1117
+ ...defaultStyles2.stepTitleFailed,
1118
+ ...overrides == null ? void 0 : overrides.stepTitleFailed
1119
+ },
1120
+ stepSubtitle: {
1121
+ ...defaultStyles2.stepSubtitle,
1122
+ ...overrides == null ? void 0 : overrides.stepSubtitle
1123
+ },
1124
+ violationPanel: {
1125
+ ...defaultStyles2.violationPanel,
1126
+ ...overrides == null ? void 0 : overrides.violationPanel
1127
+ },
1128
+ violationHeaderRow: {
1129
+ ...defaultStyles2.violationHeaderRow,
1130
+ ...overrides == null ? void 0 : overrides.violationHeaderRow
1131
+ },
1132
+ violationHeader: {
1133
+ ...defaultStyles2.violationHeader,
1134
+ ...overrides == null ? void 0 : overrides.violationHeader
1135
+ },
1136
+ violationIcon: {
1137
+ ...defaultStyles2.violationIcon,
1138
+ ...overrides == null ? void 0 : overrides.violationIcon
1139
+ },
1140
+ violationTitle: {
1141
+ ...defaultStyles2.violationTitle,
1142
+ ...overrides == null ? void 0 : overrides.violationTitle
1143
+ },
1144
+ violationSubtitle: {
1145
+ ...defaultStyles2.violationSubtitle,
1146
+ ...overrides == null ? void 0 : overrides.violationSubtitle
1147
+ },
1148
+ violationToggleButton: {
1149
+ ...defaultStyles2.violationToggleButton,
1150
+ ...overrides == null ? void 0 : overrides.violationToggleButton
1151
+ },
1152
+ violationToggleIcon: {
1153
+ ...defaultStyles2.violationToggleIcon,
1154
+ ...overrides == null ? void 0 : overrides.violationToggleIcon
1155
+ },
1156
+ violationContent: {
1157
+ ...defaultStyles2.violationContent,
1158
+ ...overrides == null ? void 0 : overrides.violationContent
1159
+ },
1160
+ violationItem: {
1161
+ ...defaultStyles2.violationItem,
1162
+ ...overrides == null ? void 0 : overrides.violationItem
1163
+ },
1164
+ violationItemTitle: {
1165
+ ...defaultStyles2.violationItemTitle,
1166
+ ...overrides == null ? void 0 : overrides.violationItemTitle
1167
+ },
1168
+ violationItemDetailRow: {
1169
+ ...defaultStyles2.violationItemDetailRow,
1170
+ ...overrides == null ? void 0 : overrides.violationItemDetailRow
1171
+ },
1172
+ violationItemDetail: {
1173
+ ...defaultStyles2.violationItemDetail,
1174
+ ...overrides == null ? void 0 : overrides.violationItemDetail
1175
+ },
1176
+ violationBullet: {
1177
+ ...defaultStyles2.violationBullet,
1178
+ ...overrides == null ? void 0 : overrides.violationBullet
1179
+ },
1180
+ recommendationsBox: {
1181
+ ...defaultStyles2.recommendationsBox,
1182
+ ...overrides == null ? void 0 : overrides.recommendationsBox
1183
+ },
1184
+ recommendationsTitle: {
1185
+ ...defaultStyles2.recommendationsTitle,
1186
+ ...overrides == null ? void 0 : overrides.recommendationsTitle
1187
+ },
1188
+ recommendationsList: {
1189
+ ...defaultStyles2.recommendationsList,
1190
+ ...overrides == null ? void 0 : overrides.recommendationsList
1191
+ },
1192
+ recommendationsItem: {
1193
+ ...defaultStyles2.recommendationsItem,
1194
+ ...overrides == null ? void 0 : overrides.recommendationsItem
1195
+ },
1196
+ recommendationsBullet: {
1197
+ ...defaultStyles2.recommendationsBullet,
1198
+ ...overrides == null ? void 0 : overrides.recommendationsBullet
1199
+ },
1200
+ recommendationsToggle: {
1201
+ ...defaultStyles2.recommendationsToggle,
1202
+ ...overrides == null ? void 0 : overrides.recommendationsToggle
1203
+ },
1204
+ failedFooter: {
1205
+ ...defaultStyles2.failedFooter,
1206
+ ...overrides == null ? void 0 : overrides.failedFooter
1207
+ },
1208
+ failedPrimaryButton: {
1209
+ ...defaultStyles2.failedPrimaryButton,
1210
+ ...overrides == null ? void 0 : overrides.failedPrimaryButton
1211
+ },
1212
+ failedSecondaryButton: {
1213
+ ...defaultStyles2.failedSecondaryButton,
1214
+ ...overrides == null ? void 0 : overrides.failedSecondaryButton
1215
+ },
1216
+ footer: { ...defaultStyles2.footer, ...overrides == null ? void 0 : overrides.footer },
1217
+ cancelButton: { ...defaultStyles2.cancelButton, ...overrides == null ? void 0 : overrides.cancelButton },
1218
+ primaryButton: { ...defaultStyles2.primaryButton, ...overrides == null ? void 0 : overrides.primaryButton },
1219
+ violatedConstraintsSecondaryButton: {
1220
+ ...defaultStyles2.violatedConstraintsSecondaryButton,
1221
+ ...overrides == null ? void 0 : overrides.violatedConstraintsSecondaryButton
1222
+ }
1223
+ });
1224
+
1225
+ // src/buildSchedulePayload.ts
1226
+ var UI_TO_BACKEND_RULE_ID = {
1227
+ max_weekly_hours: "max-weekly-hours",
1228
+ max_daily_hours: "max-daily-hours",
1229
+ min_rest_between_shifts: "min-rest-between-shifts",
1230
+ max_work_days_per_week: "max-work-days-per-week",
1231
+ fair_hour_distribution: "fair-hour-distribution",
1232
+ minimize_overtime: "overtime-balance",
1233
+ cost_to_sales_ratio: "labor-cost-optimization",
1234
+ maximize_sales: "maximize-sales-performance",
1235
+ staff_preferences: "staff-open-shift-preferences"
1236
+ };
1237
+ var BACKEND_TO_UI_RULE_ID = Object.fromEntries(
1238
+ Object.entries(UI_TO_BACKEND_RULE_ID).map(([ui, backend]) => [backend, ui])
1239
+ );
1240
+ function toBackendRuleId(uiOptionId) {
1241
+ return UI_TO_BACKEND_RULE_ID[uiOptionId];
1242
+ }
1243
+ function buildParametersForRule(backendRuleId, constraintValues, _optimizationValues) {
1244
+ var _a, _b, _c, _d;
1245
+ const params = {};
1246
+ switch (backendRuleId) {
1247
+ case "max-weekly-hours": {
1248
+ const value = (_a = constraintValues["max_weekly_hours"]) == null ? void 0 : _a.trim();
1249
+ if (value) {
1250
+ const limitMinutes = Number(value) * 60;
1251
+ if (Number.isFinite(limitMinutes) && limitMinutes >= 1) {
1252
+ params.limitMinutes = limitMinutes;
1253
+ }
1254
+ }
1255
+ break;
1256
+ }
1257
+ case "max-daily-hours": {
1258
+ const value = (_b = constraintValues["max_daily_hours"]) == null ? void 0 : _b.trim();
1259
+ if (value) {
1260
+ const limitMinutes = Number(value) * 60;
1261
+ if (Number.isFinite(limitMinutes) && limitMinutes >= 1) {
1262
+ params.limitMinutes = limitMinutes;
1263
+ }
1264
+ }
1265
+ break;
1266
+ }
1267
+ case "min-rest-between-shifts": {
1268
+ const value = (_c = constraintValues["min_rest_between_shifts"]) == null ? void 0 : _c.trim();
1269
+ if (value) {
1270
+ const minGapMinutes = Number(value) * 60;
1271
+ if (Number.isFinite(minGapMinutes) && minGapMinutes >= 1) {
1272
+ params.minGapMinutes = minGapMinutes;
1273
+ }
1274
+ }
1275
+ break;
1276
+ }
1277
+ case "max-work-days-per-week": {
1278
+ const value = (_d = constraintValues["max_work_days_per_week"]) == null ? void 0 : _d.trim();
1279
+ if (value) {
1280
+ const maxWorkDays = Number(value);
1281
+ if (Number.isFinite(maxWorkDays) && maxWorkDays >= 1 && maxWorkDays <= 6) {
1282
+ params.limitDays = maxWorkDays;
1283
+ }
1284
+ }
1285
+ break;
1286
+ }
1287
+ default:
1288
+ break;
1289
+ }
1290
+ return params;
1291
+ }
1292
+ function hasAnyParameterOverrides(selectedConstraintIds, constraintValues, selectedOptimizationIds, optimizationValues) {
1293
+ var _a, _b, _c, _d, _e, _f;
1294
+ for (const id of selectedConstraintIds) {
1295
+ const value = (_a = constraintValues[id]) == null ? void 0 : _a.trim();
1296
+ if (value && Number.isFinite(Number(value))) return true;
1297
+ }
1298
+ for (const id of selectedOptimizationIds) {
1299
+ const weight = (_b = optimizationValues[`${id}_weightPoints`]) == null ? void 0 : _b.trim();
1300
+ if (weight && Number.isFinite(Number(weight))) return true;
1301
+ if (id === "fair_hour_distribution") {
1302
+ if (((_c = optimizationValues["fair_hour_distribution_balanceTarget"]) == null ? void 0 : _c.trim()) || ((_d = optimizationValues["fair_hour_distribution_period"]) == null ? void 0 : _d.trim())) {
1303
+ return true;
1304
+ }
1305
+ } else if (id === "minimize_overtime") {
1306
+ if (((_e = optimizationValues["minimize_overtime_thresholdMinutes"]) == null ? void 0 : _e.trim()) || ((_f = optimizationValues["minimize_overtime_period"]) == null ? void 0 : _f.trim())) return true;
1307
+ } else if (id === "cost_to_sales_ratio" || id === "maximize_sales") {
1308
+ return true;
1309
+ }
1310
+ }
1311
+ return false;
1312
+ }
1313
+ function buildRulesPayload(selectedConstraintIds, constraintValues, selectedOptimizationIds, optimizationValues) {
1314
+ const allSelectedRuleIds = [
1315
+ ...selectedConstraintIds.map(toBackendRuleId),
1316
+ ...selectedOptimizationIds.map(toBackendRuleId)
1317
+ ].filter((id) => Boolean(id));
1318
+ if (allSelectedRuleIds.length === 0) {
1319
+ return {};
1320
+ }
1321
+ const hasOverrides = hasAnyParameterOverrides(
1322
+ selectedConstraintIds,
1323
+ constraintValues,
1324
+ selectedOptimizationIds,
1325
+ optimizationValues
1326
+ );
1327
+ if (!hasOverrides) {
1328
+ return { ruleIds: allSelectedRuleIds };
1329
+ }
1330
+ const ruleOverrides = allSelectedRuleIds.map((ruleId) => {
1331
+ var _a;
1332
+ const params = buildParametersForRule(ruleId, constraintValues, optimizationValues);
1333
+ const uiId = BACKEND_TO_UI_RULE_ID[ruleId];
1334
+ const weightRaw = uiId ? (_a = optimizationValues[`${uiId}_weightPoints`]) == null ? void 0 : _a.trim() : void 0;
1335
+ const weightPoints = weightRaw ? Number(weightRaw) : void 0;
1336
+ if (weightPoints && Number.isFinite(weightPoints) && weightPoints >= 1) {
1337
+ params.weightPoints = weightPoints;
1338
+ }
1339
+ return {
1340
+ ruleId,
1341
+ ...Object.keys(params).length > 0 ? { parameters: params } : {}
1342
+ };
1343
+ });
1344
+ return { ruleIds: allSelectedRuleIds, ruleOverrides };
1345
+ }
1346
+ function toApiShift(shift, includeEmployeePreferences) {
1347
+ var _a, _b;
1348
+ const startDateTime = "startDateTime" in shift ? shift.startDateTime : shift.startTime;
1349
+ const endDateTime = "endDateTime" in shift ? shift.endDateTime : shift.endTime;
1350
+ return {
1351
+ id: shift.id,
1352
+ startDateTime,
1353
+ endDateTime,
1354
+ ...shift.employeeId ? { employeeId: String(shift.employeeId) } : {},
1355
+ requiredPosition: (_a = shift.requiredPosition) != null ? _a : void 0,
1356
+ requiredCount: (_b = shift.requiredCount) != null ? _b : 1,
1357
+ location: shift.location ? {
1358
+ id: String(shift.location.id),
1359
+ timezone: "timezone" in shift.location ? shift.location.timezone : void 0,
1360
+ tags: "tags" in shift.location ? shift.location.tags : void 0
1361
+ } : void 0,
1362
+ ...includeEmployeePreferences && shift.employeePreferences ? {
1363
+ employeePreferences: shift.employeePreferences.map((p) => ({
1364
+ employeeId: p.employeeId,
1365
+ level: p.level
1366
+ }))
1367
+ } : {}
1368
+ };
1369
+ }
1370
+ function toApiEmployee(employee) {
1371
+ var _a, _b, _c;
1372
+ return {
1373
+ id: String(employee.id),
1374
+ name: (_a = employee.name) != null ? _a : void 0,
1375
+ position: (_b = employee.position) != null ? _b : void 0,
1376
+ locationIds: (_c = employee.locationIds) != null ? _c : void 0,
1377
+ properties: employee.properties ? employee.properties : void 0
1378
+ };
1379
+ }
1380
+ function toApiLocation(location) {
1381
+ var _a, _b;
1382
+ return {
1383
+ id: String(location.id),
1384
+ timezone: (_a = location.timezone) != null ? _a : void 0,
1385
+ jurisdiction: (_b = location.jurisdiction) != null ? _b : void 0,
1386
+ tags: "tags" in location ? location.tags : void 0
1387
+ };
1388
+ }
1389
+ function buildSchedulePayload(input) {
1390
+ const {
1391
+ employees,
1392
+ openShifts,
1393
+ timeOffs,
1394
+ selectedLocationIds,
1395
+ jurisdictions,
1396
+ selectedConstraintIds,
1397
+ constraintValues,
1398
+ selectedOptimizationIds,
1399
+ optimizationValues
1400
+ } = input;
1401
+ const locationsMap = /* @__PURE__ */ new Map();
1402
+ for (const j of jurisdictions) {
1403
+ for (const loc of j.locations) {
1404
+ locationsMap.set(loc.id, loc);
1405
+ }
1406
+ }
1407
+ const selectedLocations = selectedLocationIds.map((id) => locationsMap.get(id)).filter((loc) => Boolean(loc));
1408
+ const rules = buildRulesPayload(
1409
+ selectedConstraintIds,
1410
+ constraintValues,
1411
+ selectedOptimizationIds,
1412
+ optimizationValues
1413
+ );
1414
+ const includeEmployeePreferences = selectedOptimizationIds.includes("staff_preferences");
1415
+ return {
1416
+ employees: employees.map(toApiEmployee),
1417
+ shifts: openShifts.map((shift) => toApiShift(shift, includeEmployeePreferences)),
1418
+ locations: selectedLocations.map(toApiLocation),
1419
+ ...timeOffs && timeOffs.length > 0 ? { timeOffs } : {},
1420
+ ...rules
1421
+ };
1422
+ }
1423
+
1424
+ // src/api/solveSchedule.ts
1425
+ import axios from "axios";
1426
+ var axiosInstance = axios.create({
1427
+ headers: { "Content-Type": "application/json" }
1428
+ });
1429
+ async function solveSchedule(baseURL, payload) {
1430
+ const cleanBaseUrl = baseURL.replace(/\/$/, "");
1431
+ const response = await axiosInstance.post(`${cleanBaseUrl}/api/schedule/solve`, payload);
1432
+ return response.data;
1433
+ }
1434
+
1435
+ // src/utils/violationMessages.ts
1436
+ var SYSTEM_CONSTRAINT_MESSAGES = {
1437
+ "system-unassigned-Hard": {
1438
+ title: "Not enough eligible staff to cover all shifts",
1439
+ detail: "Some open shifts require more team members with the required position than are currently available."
1440
+ },
1441
+ "system-position-Hard": {
1442
+ title: "Required position not matched",
1443
+ detail: "Some open shifts require a position that isn't assigned to the available team members."
1444
+ },
1445
+ "system-location-Hard": {
1446
+ title: "Location assignment mismatch",
1447
+ detail: "Some open shifts are set for a location that isn't assigned to the available team members."
1448
+ },
1449
+ "system-employee-unavailable-Hard": {
1450
+ title: "No eligible staff available for shift",
1451
+ detail: "Some open shifts don't have any remaining eligible team members available at that time."
1452
+ },
1453
+ "system-employee-unavailable-time-Hard": {
1454
+ title: "Unavailable time",
1455
+ detail: "Unavailable time"
1456
+ },
1457
+ "system-employee-available-time-Hard": {
1458
+ title: "Outside available time",
1459
+ detail: "Outside available time"
1460
+ },
1461
+ "system-employee-time-off-Hard": {
1462
+ title: "Time off prevents assignment",
1463
+ detail: "Some open shifts overlap with approved time off for the remaining eligible team members."
1464
+ }
1465
+ };
1466
+ var RULE_ID_MESSAGES = {
1467
+ "max-weekly-hours": {
1468
+ title: "Maximum weekly hours exceeded",
1469
+ detail: "Some team members would exceed weekly hour limits if all open shifts are assigned."
1470
+ },
1471
+ "max-daily-hours": {
1472
+ title: "Maximum daily hours exceeded",
1473
+ detail: "Some team members would exceed daily hour limits if all open shifts are assigned."
1474
+ },
1475
+ "min-rest-between-shifts": {
1476
+ title: "Minimum rest between shifts",
1477
+ detail: "Some team members would not receive the required rest time if all open shifts are assigned."
1478
+ },
1479
+ "min-days-off-per-week": {
1480
+ title: "Maximum work days per week exceeded",
1481
+ detail: "Some team members would exceed the allowed work days this week if all open shifts are assigned."
1482
+ },
1483
+ "fair-hour-distribution": {
1484
+ title: "Work distribution not balanced",
1485
+ detail: "Work distribution not balanced"
1486
+ },
1487
+ "overtime-balance": {
1488
+ title: "Overtime balance rule violated",
1489
+ detail: "Overtime balance rule violated"
1490
+ },
1491
+ "manager-cashier-ratio": {
1492
+ title: "Staffing ratio violated",
1493
+ detail: "Staffing ratio violated"
1494
+ },
1495
+ "min-staffing": {
1496
+ title: "Minimum staffing not met",
1497
+ detail: "Minimum staffing not met"
1498
+ },
1499
+ "shift-duration-limits": {
1500
+ title: "Shift duration outside allowed range",
1501
+ detail: "Shift duration outside allowed range"
1502
+ },
1503
+ "max-consecutive-days": {
1504
+ title: "Shift sequence rule violated",
1505
+ detail: "Shift sequence rule violated"
1506
+ },
1507
+ "some-transactional-rule": {
1508
+ title: "Policy rule violated",
1509
+ detail: "Policy rule violated"
1510
+ }
1511
+ };
1512
+ var ARCHETYPE_FALLBACK_MESSAGES = {
1513
+ "Accumulator violation (HARD)": {
1514
+ title: "Hours limit exceeded",
1515
+ detail: "Hours limit exceeded"
1516
+ },
1517
+ "Spacer violation (HARD)": {
1518
+ title: "Rest period not met",
1519
+ detail: "Rest period not met"
1520
+ },
1521
+ "Spacer periodic violation (HARD)": {
1522
+ title: "Required rest period not met",
1523
+ detail: "Required rest period not met"
1524
+ },
1525
+ "Composition violation (HARD)": {
1526
+ title: "Staffing ratio violated",
1527
+ detail: "Staffing ratio violated"
1528
+ },
1529
+ "Transactional violation (HARD)": {
1530
+ title: "Policy rule violated",
1531
+ detail: "Policy rule violated"
1532
+ },
1533
+ "Balancer violation (HARD)": {
1534
+ title: "Work distribution not balanced",
1535
+ detail: "Work distribution not balanced"
1536
+ },
1537
+ "Coverage violation (HARD)": {
1538
+ title: "Minimum staffing not met",
1539
+ detail: "Minimum staffing not met"
1540
+ },
1541
+ "Sizing violation (HARD)": {
1542
+ title: "Shift duration outside allowed range",
1543
+ detail: "Shift duration outside allowed range"
1544
+ },
1545
+ "Sequencer violation (HARD)": {
1546
+ title: "Shift sequence rule violated",
1547
+ detail: "Shift sequence rule violated"
1548
+ },
1549
+ "Overtime balance violation (HARD)": {
1550
+ title: "Overtime balance rule violated",
1551
+ detail: "Overtime balance rule violated"
1552
+ }
1553
+ };
1554
+ var FALLBACK_MESSAGE = {
1555
+ title: "Rule violated",
1556
+ detail: "Rule violated"
1557
+ };
1558
+ function getSystemConstraintMessage(constraintName, description) {
1559
+ if (constraintName && constraintName in SYSTEM_CONSTRAINT_MESSAGES) {
1560
+ return SYSTEM_CONSTRAINT_MESSAGES[constraintName];
1561
+ }
1562
+ if (description == null ? void 0 : description.trim()) {
1563
+ return { title: description, detail: description };
1564
+ }
1565
+ return FALLBACK_MESSAGE;
1566
+ }
1567
+ function getRuleBasedViolationMessage(ruleId, constraintName, description) {
1568
+ if (ruleId && ruleId in RULE_ID_MESSAGES) {
1569
+ return RULE_ID_MESSAGES[ruleId];
1570
+ }
1571
+ if (constraintName && constraintName in ARCHETYPE_FALLBACK_MESSAGES) {
1572
+ return ARCHETYPE_FALLBACK_MESSAGES[constraintName];
1573
+ }
1574
+ if (description == null ? void 0 : description.trim()) {
1575
+ return { title: description, detail: description };
1576
+ }
1577
+ return FALLBACK_MESSAGE;
1578
+ }
1579
+
1580
+ // src/utils/mapViolationsToResult.ts
1581
+ function mapViolationsToResult(violations, summary) {
1582
+ const hardViolations = violations.filter((v) => v.isHardViolation === true);
1583
+ const seenTitles = /* @__PURE__ */ new Set();
1584
+ const violatedConstraints = [];
1585
+ for (const v of hardViolations) {
1586
+ const hasRuleId = v.ruleId != null && v.ruleId !== void 0 && String(v.ruleId).trim() !== "";
1587
+ const msg = hasRuleId ? getRuleBasedViolationMessage(
1588
+ v.ruleId,
1589
+ v.constraintName,
1590
+ v.description
1591
+ ) : getSystemConstraintMessage(v.constraintName, v.description);
1592
+ if (!seenTitles.has(msg.title)) {
1593
+ seenTitles.add(msg.title);
1594
+ violatedConstraints.push(msg);
1595
+ }
1596
+ }
1597
+ const result = {
1598
+ violatedConstraints,
1599
+ recommendedFixes: []
1600
+ };
1601
+ if (violatedConstraints.length === 0 && (summary == null ? void 0 : summary.trim())) {
1602
+ result.violatedConstraints = [
1603
+ {
1604
+ title: "Schedule could not be created",
1605
+ detail: summary
1606
+ }
1607
+ ];
1608
+ } else if (violatedConstraints.length === 0) {
1609
+ result.violatedConstraints = [
1610
+ {
1611
+ title: "Schedule could not be created",
1612
+ detail: "Please adjust constraints and try again."
1613
+ }
1614
+ ];
1615
+ }
1616
+ return result;
1617
+ }
1618
+
1619
+ // src/ViolatedConstraintsPanel.tsx
1620
+ import { useCallback, useMemo, useState } from "react";
1621
+ import {
1622
+ Box,
1623
+ Button,
1624
+ HStack,
1625
+ IconButton,
1626
+ Spinner,
1627
+ Stack,
1628
+ Text
1629
+ } from "@chakra-ui/react";
1630
+ import axios2 from "axios";
1631
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
1632
+ var DEFAULT_TITLE = "Schedule can't be created";
1633
+ var DEFAULT_SUBTITLE = "Some required rules aren't being met. Review the issues below to continue.";
1634
+ function ViolatedConstraintsPanel({
1635
+ violatedConstraints,
1636
+ title = DEFAULT_TITLE,
1637
+ subtitle = DEFAULT_SUBTITLE,
1638
+ showSecondaryButton = false,
1639
+ secondaryButtonTitle,
1640
+ onSecondaryButtonClick,
1641
+ generateRecommendationsURLAndHeaders,
1642
+ styles: stylesProp,
1643
+ styleOverrides
1644
+ }) {
1645
+ const [showRecommendations, setShowRecommendations] = useState(false);
1646
+ const [isExpanded, setIsExpanded] = useState(true);
1647
+ const [recommendations, setRecommendations] = useState(null);
1648
+ const [isLoadingRecommendations, setIsLoadingRecommendations] = useState(false);
1649
+ const styles = useMemo(() => {
1650
+ if (stylesProp) return stylesProp;
1651
+ return buildStyles(defaultStyles, styleOverrides);
1652
+ }, [stylesProp, styleOverrides]);
1653
+ const canFetchRecommendations = Boolean(generateRecommendationsURLAndHeaders) && Boolean(violatedConstraints.recommendationPayload);
1654
+ const hasPreFetchedFixes = violatedConstraints.recommendedFixes.length > 0;
1655
+ const showRecommendationsButton = canFetchRecommendations || hasPreFetchedFixes;
1656
+ const displayRecommendations = recommendations !== null ? recommendations : violatedConstraints.recommendedFixes;
1657
+ const hasRecommendations = displayRecommendations.length > 0;
1658
+ const handleToggleRecommendations = useCallback(async () => {
1659
+ var _a, _b;
1660
+ if (showRecommendations) {
1661
+ setShowRecommendations(false);
1662
+ return;
1663
+ }
1664
+ setShowRecommendations(true);
1665
+ if (recommendations !== null) {
1666
+ return;
1667
+ }
1668
+ if (!canFetchRecommendations) {
1669
+ return;
1670
+ }
1671
+ const payload = violatedConstraints.recommendationPayload;
1672
+ setIsLoadingRecommendations(true);
1673
+ try {
1674
+ const recResponse = await axios2.post(
1675
+ generateRecommendationsURLAndHeaders.url,
1676
+ payload,
1677
+ {
1678
+ headers: {
1679
+ "Content-Type": "application/json",
1680
+ ...generateRecommendationsURLAndHeaders.headers
1681
+ }
1682
+ }
1683
+ );
1684
+ const data = (_b = (_a = recResponse.data) == null ? void 0 : _a.data) != null ? _b : recResponse.data;
1685
+ if (data) {
1686
+ setRecommendations(data.map((r) => r.recommendation));
1687
+ } else {
1688
+ setRecommendations([]);
1689
+ }
1690
+ } catch (recErr) {
1691
+ console.error("[ViolatedConstraintsPanel] failed to fetch recommendations:", recErr);
1692
+ setRecommendations([]);
1693
+ } finally {
1694
+ setIsLoadingRecommendations(false);
1695
+ }
1696
+ }, [
1697
+ showRecommendations,
1698
+ recommendations,
1699
+ canFetchRecommendations,
1700
+ violatedConstraints.recommendationPayload,
1701
+ generateRecommendationsURLAndHeaders
1702
+ ]);
1703
+ return /* @__PURE__ */ jsxs2(Box, { sx: styles.violationPanel, children: [
1704
+ /* @__PURE__ */ jsxs2(HStack, { sx: styles.violationHeaderRow, children: [
1705
+ /* @__PURE__ */ jsxs2(HStack, { sx: styles.violationHeader, children: [
1706
+ /* @__PURE__ */ jsx2(Box, { sx: styles.violationIcon, children: /* @__PURE__ */ jsx2(WarningIcon, {}) }),
1707
+ /* @__PURE__ */ jsxs2(Box, { children: [
1708
+ /* @__PURE__ */ jsx2(Text, { sx: styles.violationTitle, children: title }),
1709
+ /* @__PURE__ */ jsx2(Text, { sx: styles.violationSubtitle, children: subtitle })
1710
+ ] })
1711
+ ] }),
1712
+ /* @__PURE__ */ jsx2(
1713
+ IconButton,
1714
+ {
1715
+ "aria-label": isExpanded ? "Collapse" : "Expand",
1716
+ variant: "ghost",
1717
+ size: "sm",
1718
+ icon: /* @__PURE__ */ jsx2(
1719
+ Box,
1720
+ {
1721
+ sx: styles.violationToggleIcon,
1722
+ transform: isExpanded ? "rotate(180deg)" : "rotate(0deg)",
1723
+ children: /* @__PURE__ */ jsx2(ChevronDownIcon, {})
1724
+ }
1725
+ ),
1726
+ onClick: () => setIsExpanded((prev) => !prev),
1727
+ sx: styles.violationToggleButton
1728
+ }
1729
+ )
1730
+ ] }),
1731
+ isExpanded ? /* @__PURE__ */ jsxs2(Stack, { spacing: "8px", sx: styles.violationContent, children: [
1732
+ /* @__PURE__ */ jsx2(Stack, { spacing: "8px", children: violatedConstraints.violatedConstraints.map((constraint, index) => /* @__PURE__ */ jsxs2(Box, { sx: styles.violationItem, children: [
1733
+ /* @__PURE__ */ jsx2(Text, { sx: styles.violationItemTitle, children: constraint.title }),
1734
+ /* @__PURE__ */ jsxs2(HStack, { spacing: "6px", sx: styles.violationItemDetailRow, children: [
1735
+ /* @__PURE__ */ jsx2(Box, { sx: styles.violationBullet, children: /* @__PURE__ */ jsx2(DotIcon, { size: 6 }) }),
1736
+ /* @__PURE__ */ jsx2(Text, { sx: styles.violationItemDetail, children: constraint.detail })
1737
+ ] })
1738
+ ] }, `${constraint.title}-${index}`)) }),
1739
+ showRecommendations && hasRecommendations ? /* @__PURE__ */ jsxs2(Box, { sx: styles.recommendationsBox, children: [
1740
+ /* @__PURE__ */ jsx2(Text, { sx: styles.recommendationsTitle, children: "Recommended fixes" }),
1741
+ /* @__PURE__ */ jsx2(Stack, { spacing: "4px", sx: styles.recommendationsList, children: displayRecommendations.map((fix, index) => /* @__PURE__ */ jsxs2(HStack, { spacing: "6px", children: [
1742
+ /* @__PURE__ */ jsx2(Box, { sx: styles.recommendationsBullet, children: /* @__PURE__ */ jsx2(DotIcon, { size: 6 }) }),
1743
+ /* @__PURE__ */ jsx2(Text, { sx: styles.recommendationsItem, children: fix })
1744
+ ] }, `${fix}-${index}`)) })
1745
+ ] }) : null,
1746
+ /* @__PURE__ */ jsxs2(HStack, { spacing: "8px", flexWrap: "wrap", children: [
1747
+ showRecommendationsButton ? /* @__PURE__ */ jsx2(
1748
+ Button,
1749
+ {
1750
+ variant: "outline",
1751
+ size: "xs",
1752
+ onClick: handleToggleRecommendations,
1753
+ isDisabled: isLoadingRecommendations,
1754
+ sx: { ...styles.recommendationsToggle, minWidth: "140px" },
1755
+ children: isLoadingRecommendations ? /* @__PURE__ */ jsx2(Spinner, { size: "sm", thickness: "2px" }) : showRecommendations ? "Hide recommendations" : "Show recommendations"
1756
+ }
1757
+ ) : null,
1758
+ showSecondaryButton && secondaryButtonTitle && onSecondaryButtonClick ? /* @__PURE__ */ jsx2(
1759
+ Button,
1760
+ {
1761
+ variant: "outline",
1762
+ size: "xs",
1763
+ onClick: onSecondaryButtonClick,
1764
+ sx: { ...styles.failedSecondaryButton, ...styles.violatedConstraintsSecondaryButton },
1765
+ children: secondaryButtonTitle
1766
+ }
1767
+ ) : null
1768
+ ] })
1769
+ ] }) : null
1770
+ ] });
1771
+ }
1772
+
1773
+ // src/AutoSchedulerModal.tsx
1774
+ import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
1775
+ function AutoSchedulerModal({
1776
+ baseURL,
1777
+ isOpen,
1778
+ title = "Auto-scheduling",
1779
+ bannerText,
1780
+ jurisdictions,
1781
+ openShifts,
1782
+ employees,
1783
+ timeOffs,
1784
+ locationPlaceholder = "Select locations",
1785
+ onClose,
1786
+ cancelLabel = "Cancel",
1787
+ primaryActionLabel = "Create schedule",
1788
+ onPrimaryAction: _onPrimaryAction,
1789
+ onFixManually,
1790
+ initialConfig,
1791
+ theme,
1792
+ cssVarsRoot,
1793
+ generateRecommendationsURLAndHeaders,
1794
+ onSolution
1795
+ }) {
1796
+ const baseTheme = useTheme();
1797
+ const { chakraOverride, styleOverrides } = useMemo2(
1798
+ () => resolveThemeParts(theme),
1799
+ [theme]
1800
+ );
1801
+ const mergedTheme = useMemo2(
1802
+ () => mergeThemeWithBase(baseTheme, chakraOverride),
1803
+ [baseTheme, chakraOverride]
1804
+ );
1805
+ const styles = useMemo2(() => buildStyles(defaultStyles, styleOverrides), [styleOverrides]);
1806
+ const [selectedConstraintIds, setSelectedConstraintIds] = useState2([]);
1807
+ const [constraintValues, setConstraintValues] = useState2({});
1808
+ const [selectedOptimizationIds, setSelectedOptimizationIds] = useState2([]);
1809
+ const [optimizationValues, setOptimizationValues] = useState2({});
1810
+ const [screen, setScreen] = useState2("configure");
1811
+ const [activeStepIndex, setActiveStepIndex] = useState2(0);
1812
+ const [completedStepIndex, setCompletedStepIndex] = useState2(-1);
1813
+ const [lastFailure, setLastFailure] = useState2(null);
1814
+ const [selectedLocationIds, setSelectedLocationIds] = useState2([]);
1815
+ const timersRef = useRef([]);
1816
+ const runIdRef = useRef(0);
1817
+ const selectedByJurisdiction = useMemo2(
1818
+ () => jurisdictions.map((jurisdiction) => ({
1819
+ ...jurisdiction,
1820
+ selectedLocations: jurisdiction.locations.filter(
1821
+ (location) => selectedLocationIds.includes(location.id)
1822
+ )
1823
+ })).filter((jurisdiction) => jurisdiction.selectedLocations.length > 0),
1824
+ [jurisdictions, selectedLocationIds]
1825
+ );
1826
+ const selectedCount = selectedByJurisdiction.reduce(
1827
+ (total, jurisdiction) => total + jurisdiction.selectedLocations.length,
1828
+ 0
1829
+ );
1830
+ const selectedConstraints = useMemo2(
1831
+ () => HARD_CONSTRAINT_OPTIONS.filter((option) => selectedConstraintIds.includes(option.id)),
1832
+ [selectedConstraintIds]
1833
+ );
1834
+ const optimizationOptions = useMemo2(
1835
+ () => OPTIMIZATION_OPTION_GROUPS.flatMap(
1836
+ (group) => group.options.map((option) => ({
1837
+ ...option,
1838
+ groupId: group.id,
1839
+ groupName: group.name
1840
+ }))
1841
+ ),
1842
+ []
1843
+ );
1844
+ const selectedOptimizationOptions = useMemo2(
1845
+ () => optimizationOptions.filter(
1846
+ (option) => selectedOptimizationIds.includes(option.id)
1847
+ ),
1848
+ [optimizationOptions, selectedOptimizationIds]
1849
+ );
1850
+ const hasEmptyInputs = useMemo2(() => {
1851
+ const hasEmptyConstraint = selectedConstraints.some(
1852
+ (opt) => {
1853
+ var _a;
1854
+ return !((_a = constraintValues[opt.id]) == null ? void 0 : _a.trim());
1855
+ }
1856
+ );
1857
+ const hasEmptyWeight = selectedOptimizationOptions.length >= 2 && selectedOptimizationOptions.some(
1858
+ (opt) => {
1859
+ var _a;
1860
+ return !((_a = optimizationValues[`${opt.id}_weightPoints`]) == null ? void 0 : _a.trim());
1861
+ }
1862
+ );
1863
+ return hasEmptyConstraint || hasEmptyWeight;
1864
+ }, [selectedConstraints, constraintValues, selectedOptimizationOptions, optimizationValues]);
1865
+ const allocationSummary = useMemo2(() => {
1866
+ if (selectedOptimizationOptions.length === 0) return "";
1867
+ const totalWeight = selectedOptimizationOptions.reduce(
1868
+ (sum, opt) => sum + (Number(optimizationValues[`${opt.id}_weightPoints`]) || 1),
1869
+ 0
1870
+ );
1871
+ const parts = selectedOptimizationOptions.map((opt) => {
1872
+ const weight = Number(optimizationValues[`${opt.id}_weightPoints`]) || 1;
1873
+ const pct = Math.round(weight / totalWeight * 100);
1874
+ return `${opt.title} ${pct}%`;
1875
+ });
1876
+ return `Scheduling priorities: ${parts.join(", ")}.`;
1877
+ }, [selectedOptimizationOptions, optimizationValues]);
1878
+ const clearTimers = () => {
1879
+ timersRef.current.forEach((timerId) => clearTimeout(timerId));
1880
+ timersRef.current = [];
1881
+ };
1882
+ const resetProgress = () => {
1883
+ setActiveStepIndex(0);
1884
+ setCompletedStepIndex(-1);
1885
+ };
1886
+ useEffect(() => {
1887
+ if (!isOpen) {
1888
+ clearTimers();
1889
+ setScreen("configure");
1890
+ setLastFailure(null);
1891
+ setSelectedLocationIds([]);
1892
+ setSelectedOptimizationIds([]);
1893
+ setSelectedConstraintIds([]);
1894
+ setConstraintValues({});
1895
+ setOptimizationValues({});
1896
+ resetProgress();
1897
+ }
1898
+ }, [isOpen]);
1899
+ useEffect(() => {
1900
+ if (isOpen && initialConfig) {
1901
+ setSelectedLocationIds(initialConfig.selectedLocationIds);
1902
+ setSelectedConstraintIds(initialConfig.selectedConstraintIds);
1903
+ setConstraintValues(initialConfig.constraintValues);
1904
+ setSelectedOptimizationIds(initialConfig.selectedOptimizationIds);
1905
+ setOptimizationValues(initialConfig.optimizationValues);
1906
+ if (initialConfig.failure) {
1907
+ setLastFailure(initialConfig.failure);
1908
+ }
1909
+ setScreen("configure");
1910
+ }
1911
+ }, [isOpen, initialConfig]);
1912
+ useEffect(() => () => clearTimers(), []);
1913
+ const stepStatusForIndex = (index) => {
1914
+ if (index <= completedStepIndex) {
1915
+ return "done";
1916
+ }
1917
+ if (index === activeStepIndex) {
1918
+ return "active";
1919
+ }
1920
+ return "pending";
1921
+ };
1922
+ const resetBeforeSubmit = () => {
1923
+ clearTimers();
1924
+ setScreen("creating");
1925
+ setLastFailure(null);
1926
+ resetProgress();
1927
+ runIdRef.current += 1;
1928
+ return runIdRef.current;
1929
+ };
1930
+ const schedule = (delayMs, action, runId) => {
1931
+ const timerId = setTimeout(() => {
1932
+ if (runIdRef.current !== runId) {
1933
+ return;
1934
+ }
1935
+ action();
1936
+ }, delayMs);
1937
+ timersRef.current.push(timerId);
1938
+ };
1939
+ const afterSuccessSteps = (runId, response) => {
1940
+ schedule(STEP_INTERVAL_MS, () => {
1941
+ if (runIdRef.current !== runId) return;
1942
+ setCompletedStepIndex(2);
1943
+ setActiveStepIndex(3);
1944
+ }, runId);
1945
+ schedule(STEP_INTERVAL_MS * 2, () => {
1946
+ if (runIdRef.current !== runId) return;
1947
+ setCompletedStepIndex(3);
1948
+ setActiveStepIndex(4);
1949
+ }, runId);
1950
+ schedule(STEP_INTERVAL_MS * 3, async () => {
1951
+ if (runIdRef.current !== runId) return;
1952
+ try {
1953
+ await onSolution(response.solution);
1954
+ setCompletedStepIndex(4);
1955
+ setActiveStepIndex(4);
1956
+ } catch (err) {
1957
+ console.error(err);
1958
+ const defaultErrorMessage = "Failed to create schedule shifts";
1959
+ const detailedErrorMessage = err instanceof Error ? err.message || defaultErrorMessage : defaultErrorMessage;
1960
+ setLastFailure({
1961
+ violatedConstraints: [
1962
+ {
1963
+ title: "Something went wrong",
1964
+ detail: detailedErrorMessage
1965
+ }
1966
+ ],
1967
+ recommendedFixes: []
1968
+ });
1969
+ setScreen("failed");
1970
+ }
1971
+ }, runId);
1972
+ };
1973
+ const handleCreateSchedule = () => {
1974
+ if (selectedCount === 0) {
1975
+ return;
1976
+ }
1977
+ const payload = buildSchedulePayload({
1978
+ employees,
1979
+ openShifts,
1980
+ timeOffs,
1981
+ selectedLocationIds,
1982
+ jurisdictions,
1983
+ selectedConstraintIds,
1984
+ constraintValues,
1985
+ selectedOptimizationIds,
1986
+ optimizationValues
1987
+ });
1988
+ const solvePromise = solveSchedule(baseURL, payload);
1989
+ const runId = resetBeforeSubmit();
1990
+ schedule(STEP_INTERVAL_MS, async () => {
1991
+ var _a, _b;
1992
+ setCompletedStepIndex(0);
1993
+ setActiveStepIndex(1);
1994
+ try {
1995
+ const response = await solvePromise;
1996
+ if (runIdRef.current !== runId) {
1997
+ return;
1998
+ }
1999
+ if (!response.explanation.isFeasible) {
2000
+ const result = mapViolationsToResult(
2001
+ (_a = response.explanation.violations) != null ? _a : [],
2002
+ response.explanation.summary
2003
+ );
2004
+ const selectedLocations = jurisdictions.flatMap((j) => j.locations).filter(
2005
+ (l) => selectedLocationIds.includes(l.id)
2006
+ );
2007
+ const recommendationPayload = generateRecommendationsURLAndHeaders ? {
2008
+ context: {
2009
+ selectedLocationIds,
2010
+ selectedConstraintIds,
2011
+ constraintValues,
2012
+ selectedOptimizationIds,
2013
+ optimizationValues,
2014
+ timezone: (_b = selectedLocations[0]) == null ? void 0 : _b.timezone
2015
+ },
2016
+ locations: selectedLocations,
2017
+ employees,
2018
+ openShifts: openShifts.filter((s) => !s.employeeId),
2019
+ assignedShifts: openShifts.filter((s) => Boolean(s.employeeId)),
2020
+ timeOffs: timeOffs != null ? timeOffs : [],
2021
+ explanation: response.explanation
2022
+ } : void 0;
2023
+ setLastFailure({
2024
+ ...result,
2025
+ recommendedFixes: [],
2026
+ recommendationPayload
2027
+ });
2028
+ setScreen("failed");
2029
+ } else {
2030
+ setCompletedStepIndex(1);
2031
+ setActiveStepIndex(OPTIMIZING_STEP_INDEX);
2032
+ afterSuccessSteps(runId, response);
2033
+ }
2034
+ } catch (err) {
2035
+ if (runIdRef.current !== runId) {
2036
+ return;
2037
+ }
2038
+ console.error(err);
2039
+ setLastFailure(NETWORK_ERROR_FAILURE);
2040
+ setScreen("failed");
2041
+ }
2042
+ }, runId);
2043
+ };
2044
+ const handleAdjustConstraints = () => {
2045
+ clearTimers();
2046
+ setScreen("configure");
2047
+ };
2048
+ const handleToggleLocation = (locationId) => {
2049
+ if (selectedLocationIds.includes(locationId)) {
2050
+ setSelectedLocationIds(selectedLocationIds.filter((id) => id !== locationId));
2051
+ return;
2052
+ }
2053
+ setSelectedLocationIds([...selectedLocationIds, locationId]);
2054
+ };
2055
+ const handleToggleConstraint = (constraintId) => {
2056
+ setSelectedConstraintIds((prev) => {
2057
+ if (prev.includes(constraintId)) {
2058
+ setConstraintValues((values) => {
2059
+ const next = { ...values };
2060
+ delete next[constraintId];
2061
+ return next;
2062
+ });
2063
+ return prev.filter((id) => id !== constraintId);
2064
+ }
2065
+ if (HARD_CONSTRAINT_DEFAULTS[constraintId]) {
2066
+ setConstraintValues((values) => ({
2067
+ ...values,
2068
+ [constraintId]: HARD_CONSTRAINT_DEFAULTS[constraintId]
2069
+ }));
2070
+ }
2071
+ return [...prev, constraintId];
2072
+ });
2073
+ };
2074
+ const handleToggleOptimization = (optionId) => {
2075
+ setSelectedOptimizationIds((prev) => {
2076
+ if (prev.includes(optionId)) {
2077
+ setOptimizationValues((values) => {
2078
+ const next = { ...values };
2079
+ delete next[`${optionId}_weightPoints`];
2080
+ if (optionId === "fair_hour_distribution") {
2081
+ delete next["fair_hour_distribution_balanceTarget"];
2082
+ delete next["fair_hour_distribution_period"];
2083
+ } else if (optionId === "minimize_overtime") {
2084
+ delete next["minimize_overtime_thresholdMinutes"];
2085
+ delete next["minimize_overtime_period"];
2086
+ }
2087
+ return next;
2088
+ });
2089
+ return prev.filter((id) => id !== optionId);
2090
+ }
2091
+ if (prev.length >= 3) return prev;
2092
+ setOptimizationValues((values) => ({
2093
+ ...values,
2094
+ [`${optionId}_weightPoints`]: "1"
2095
+ }));
2096
+ return [...prev, optionId];
2097
+ });
2098
+ };
2099
+ const modalTitle = screen === "configure" ? title : "Creating schedule";
2100
+ const showConfiguration = screen === "configure";
2101
+ const showFailed = screen === "failed";
2102
+ const violationTitle = useMemo2(
2103
+ () => showFailed ? "Schedule can\u2019t be created" : "Validating compliance rules",
2104
+ [showFailed]
2105
+ );
2106
+ const violationPanel = lastFailure ? /* @__PURE__ */ jsx3(
2107
+ ViolatedConstraintsPanel,
2108
+ {
2109
+ violatedConstraints: lastFailure,
2110
+ title: violationTitle,
2111
+ subtitle: "",
2112
+ generateRecommendationsURLAndHeaders,
2113
+ styles
2114
+ }
2115
+ ) : null;
2116
+ const configurationContent = /* @__PURE__ */ jsxs3(Stack2, { spacing: "20px", children: [
2117
+ /* @__PURE__ */ jsxs3(Box2, { sx: styles.banner, children: [
2118
+ /* @__PURE__ */ jsx3(Box2, { sx: styles.bannerIcon, children: /* @__PURE__ */ jsx3(MagicIcon, {}) }),
2119
+ bannerText ? /* @__PURE__ */ jsx3(Text2, { sx: styles.bannerText, children: bannerText }) : /* @__PURE__ */ jsxs3(Stack2, { spacing: "4px", flex: 1, children: [
2120
+ /* @__PURE__ */ jsx3(Text2, { sx: styles.bannerTitle, children: "Create a smarter retail schedule in seconds." }),
2121
+ /* @__PURE__ */ jsx3(Text2, { sx: styles.bannerText, children: "AI assigns open shifts using your rules, availability, and optimization priorities while keeping existing assigned shifts in place." })
2122
+ ] })
2123
+ ] }),
2124
+ openShifts.length === 0 ? /* @__PURE__ */ jsxs3(
2125
+ Box2,
2126
+ {
2127
+ sx: {
2128
+ bg: "#FFF4E5",
2129
+ borderRadius: "8px",
2130
+ px: "8px",
2131
+ py: "8px",
2132
+ display: "flex",
2133
+ gap: "8px",
2134
+ alignItems: "flex-start"
2135
+ },
2136
+ children: [
2137
+ /* @__PURE__ */ jsx3(Box2, { sx: { color: "#B76E00", flexShrink: 0, mt: "2px" }, children: /* @__PURE__ */ jsx3(WarningIcon, {}) }),
2138
+ /* @__PURE__ */ jsx3(Text2, { sx: { fontSize: "13px", color: "#7A4100", lineHeight: "20px" }, children: "There are no open shifts to schedule. Add open shifts before creating a schedule." })
2139
+ ]
2140
+ }
2141
+ ) : null,
2142
+ /* @__PURE__ */ jsxs3(Stack2, { spacing: "8px", sx: styles.locationSection, children: [
2143
+ /* @__PURE__ */ jsxs3(Stack2, { spacing: "4px", children: [
2144
+ /* @__PURE__ */ jsxs3(HStack2, { sx: styles.locationLabelRow, children: [
2145
+ /* @__PURE__ */ jsx3(Text2, { sx: styles.locationLabel, children: "Location" }),
2146
+ selectedCount > 0 ? /* @__PURE__ */ jsx3(Badge, { sx: styles.locationBadge, children: selectedCount }) : null
2147
+ ] }),
2148
+ /* @__PURE__ */ jsx3(Text2, { sx: styles.locationSubtitle, children: "Select locations within the same labor jurisdiction. Locations from different jurisdictions cannot be combined." })
2149
+ ] }),
2150
+ /* @__PURE__ */ jsxs3(Popover, { placement: "bottom-start", matchWidth: true, isLazy: true, children: [
2151
+ /* @__PURE__ */ jsx3(PopoverTrigger, { children: /* @__PURE__ */ jsx3(Button2, { variant: "unstyled", sx: styles.locationTrigger, children: /* @__PURE__ */ jsxs3(HStack2, { justify: "space-between", w: "full", children: [
2152
+ /* @__PURE__ */ jsx3(
2153
+ Text2,
2154
+ {
2155
+ sx: {
2156
+ ...styles.locationTriggerText,
2157
+ ...selectedLocationIds.length > 0 ? { color: "#303030" } : {}
2158
+ },
2159
+ noOfLines: 1,
2160
+ children: selectedLocationIds.length > 0 ? jurisdictions.flatMap((j) => j.locations).filter((l) => selectedLocationIds.includes(l.id)).map((l) => l.name).join(", ") : locationPlaceholder
2161
+ }
2162
+ ),
2163
+ /* @__PURE__ */ jsx3(Box2, { color: "#5c5c5c", children: /* @__PURE__ */ jsx3(ChevronDownIcon, {}) })
2164
+ ] }) }) }),
2165
+ /* @__PURE__ */ jsx3(PopoverContent, { sx: styles.popoverContent, children: /* @__PURE__ */ jsx3(PopoverBody, { p: 0, children: /* @__PURE__ */ jsx3(Stack2, { spacing: "4px", children: jurisdictions.map((jurisdiction) => /* @__PURE__ */ jsxs3(Box2, { children: [
2166
+ /* @__PURE__ */ jsx3(Text2, { sx: styles.groupHeader, children: jurisdiction.title }),
2167
+ /* @__PURE__ */ jsx3(Stack2, { spacing: "2px", children: jurisdiction.locations.map((location) => /* @__PURE__ */ jsxs3(
2168
+ HStack2,
2169
+ {
2170
+ as: "label",
2171
+ sx: { ...styles.optionRow, cursor: "pointer" },
2172
+ children: [
2173
+ /* @__PURE__ */ jsx3(
2174
+ Checkbox,
2175
+ {
2176
+ id: `location-${location.id}`,
2177
+ isChecked: selectedLocationIds.includes(location.id),
2178
+ onChange: () => handleToggleLocation(location.id)
2179
+ }
2180
+ ),
2181
+ /* @__PURE__ */ jsx3(Text2, { sx: styles.optionLabel, children: location.name })
2182
+ ]
2183
+ },
2184
+ location.id
2185
+ )) })
2186
+ ] }, jurisdiction.id)) }) }) })
2187
+ ] }),
2188
+ /* @__PURE__ */ jsx3(Stack2, { spacing: "8px", children: selectedByJurisdiction.map((jurisdiction) => /* @__PURE__ */ jsxs3(Box2, { sx: styles.selectedBanner, children: [
2189
+ /* @__PURE__ */ jsx3(Box2, { sx: styles.selectedBannerIcon, children: /* @__PURE__ */ jsx3(InfoIcon, {}) }),
2190
+ /* @__PURE__ */ jsxs3(Box2, { sx: styles.selectedBannerText, children: [
2191
+ /* @__PURE__ */ jsxs3(Text2, { fontWeight: 600, children: [
2192
+ "Compliance jurisdiction: ",
2193
+ jurisdiction.title
2194
+ ] }),
2195
+ /* @__PURE__ */ jsx3(Text2, { children: jurisdiction.selectedLocations.map((location) => location.name).join(", ") })
2196
+ ] })
2197
+ ] }, jurisdiction.id)) })
2198
+ ] }),
2199
+ /* @__PURE__ */ jsxs3(Stack2, { spacing: "16px", sx: styles.hardConstraintsSection, children: [
2200
+ /* @__PURE__ */ jsxs3(Stack2, { spacing: "4px", children: [
2201
+ /* @__PURE__ */ jsxs3(HStack2, { sx: styles.hardConstraintsLabelRow, children: [
2202
+ /* @__PURE__ */ jsx3(Text2, { sx: styles.hardConstraintsLabel, children: "Required rules" }),
2203
+ selectedConstraintIds.length > 0 ? /* @__PURE__ */ jsx3(Badge, { sx: styles.hardConstraintsBadge, children: selectedConstraintIds.length }) : null
2204
+ ] }),
2205
+ /* @__PURE__ */ jsx3(Text2, { sx: styles.hardConstraintsSubtitle, children: "Assigned shifts will be required to meet the following rules:" })
2206
+ ] }),
2207
+ showConfiguration && lastFailure ? violationPanel : null,
2208
+ /* @__PURE__ */ jsxs3(Popover, { placement: "bottom-start", matchWidth: true, isLazy: true, children: [
2209
+ /* @__PURE__ */ jsx3(PopoverTrigger, { children: /* @__PURE__ */ jsx3(Button2, { variant: "unstyled", sx: styles.hardConstraintsTrigger, children: /* @__PURE__ */ jsxs3(HStack2, { justify: "space-between", w: "full", children: [
2210
+ /* @__PURE__ */ jsx3(
2211
+ Text2,
2212
+ {
2213
+ sx: {
2214
+ ...styles.hardConstraintsTriggerText,
2215
+ ...selectedConstraintIds.length > 0 ? { color: "#303030" } : {}
2216
+ },
2217
+ noOfLines: 1,
2218
+ children: selectedConstraintIds.length > 0 ? HARD_CONSTRAINT_OPTIONS.filter((o) => selectedConstraintIds.includes(o.id)).map((o) => o.name).join(", ") : "Select rules"
2219
+ }
2220
+ ),
2221
+ /* @__PURE__ */ jsx3(Box2, { color: "#5c5c5c", children: /* @__PURE__ */ jsx3(ChevronDownIcon, {}) })
2222
+ ] }) }) }),
2223
+ /* @__PURE__ */ jsx3(PopoverContent, { sx: styles.hardConstraintsPopoverContent, children: /* @__PURE__ */ jsx3(PopoverBody, { p: 0, children: /* @__PURE__ */ jsx3(Stack2, { spacing: "4px", children: HARD_CONSTRAINT_OPTIONS.map((option) => /* @__PURE__ */ jsxs3(
2224
+ HStack2,
2225
+ {
2226
+ as: "label",
2227
+ sx: { ...styles.hardConstraintsOptionRow, cursor: "pointer" },
2228
+ children: [
2229
+ /* @__PURE__ */ jsx3(
2230
+ Checkbox,
2231
+ {
2232
+ id: `constraint-${option.id}`,
2233
+ isChecked: selectedConstraintIds.includes(option.id),
2234
+ onChange: () => handleToggleConstraint(option.id)
2235
+ }
2236
+ ),
2237
+ /* @__PURE__ */ jsx3(Text2, { sx: styles.hardConstraintsOptionLabel, children: option.name })
2238
+ ]
2239
+ },
2240
+ option.id
2241
+ )) }) }) })
2242
+ ] }),
2243
+ selectedConstraints.length > 0 ? /* @__PURE__ */ jsx3(Box2, { sx: styles.hardConstraintsInputsContainer, children: /* @__PURE__ */ jsx3(Stack2, { spacing: "16px", children: selectedConstraints.map((option, index) => {
2244
+ var _a;
2245
+ return /* @__PURE__ */ jsxs3(Stack2, { sx: styles.hardConstraintsInputItem, children: [
2246
+ /* @__PURE__ */ jsxs3(Text2, { sx: styles.hardConstraintsInputLabel, children: [
2247
+ index + 1,
2248
+ ". ",
2249
+ option.name
2250
+ ] }),
2251
+ /* @__PURE__ */ jsxs3(InputGroup, { children: [
2252
+ /* @__PURE__ */ jsx3(
2253
+ Input,
2254
+ {
2255
+ type: "number",
2256
+ value: (_a = constraintValues[option.id]) != null ? _a : "",
2257
+ onChange: (event) => setConstraintValues((prev) => ({
2258
+ ...prev,
2259
+ [option.id]: event.target.value
2260
+ })),
2261
+ min: option.id === "max_work_days_per_week" ? 1 : void 0,
2262
+ max: option.id === "max_work_days_per_week" ? 6 : void 0,
2263
+ step: option.id === "max_work_days_per_week" ? void 0 : "any",
2264
+ sx: styles.hardConstraintsInputField
2265
+ }
2266
+ ),
2267
+ /* @__PURE__ */ jsx3(InputRightAddon, { sx: styles.optimizationSuffix, children: option.id === "max_work_days_per_week" ? "days" : "hours" })
2268
+ ] })
2269
+ ] }, option.id);
2270
+ }) }) }) : null
2271
+ ] }),
2272
+ /* @__PURE__ */ jsxs3(Stack2, { spacing: "16px", sx: styles.optimizationSection, children: [
2273
+ /* @__PURE__ */ jsxs3(Stack2, { spacing: "4px", children: [
2274
+ /* @__PURE__ */ jsxs3(HStack2, { sx: styles.optimizationLabelRow, children: [
2275
+ /* @__PURE__ */ jsx3(Text2, { sx: styles.optimizationLabel, children: "Optimization" }),
2276
+ selectedOptimizationIds.length > 0 ? /* @__PURE__ */ jsx3(Badge, { sx: styles.optimizationBadge, children: selectedOptimizationIds.length }) : null
2277
+ ] }),
2278
+ /* @__PURE__ */ jsx3(Text2, { sx: styles.optimizationSubtitle, children: "Your schedule will be optimized based on the following priorities:" })
2279
+ ] }),
2280
+ /* @__PURE__ */ jsxs3(Popover, { placement: "bottom-start", matchWidth: true, isLazy: true, children: [
2281
+ /* @__PURE__ */ jsx3(PopoverTrigger, { children: /* @__PURE__ */ jsx3(Button2, { variant: "unstyled", sx: styles.optimizationTrigger, children: /* @__PURE__ */ jsxs3(HStack2, { justify: "space-between", w: "full", children: [
2282
+ /* @__PURE__ */ jsx3(
2283
+ Text2,
2284
+ {
2285
+ sx: {
2286
+ ...styles.optimizationTriggerText,
2287
+ ...selectedOptimizationIds.length > 0 ? { color: "#303030" } : {}
2288
+ },
2289
+ noOfLines: 1,
2290
+ children: selectedOptimizationIds.length > 0 ? optimizationOptions.filter((o) => selectedOptimizationIds.includes(o.id)).map((o) => o.title).join(", ") : "Select optimization priorities"
2291
+ }
2292
+ ),
2293
+ /* @__PURE__ */ jsx3(Box2, { color: "#5c5c5c", children: /* @__PURE__ */ jsx3(ChevronDownIcon, {}) })
2294
+ ] }) }) }),
2295
+ /* @__PURE__ */ jsx3(PopoverContent, { sx: styles.optimizationPopoverContent, children: /* @__PURE__ */ jsx3(PopoverBody, { p: 0, children: /* @__PURE__ */ jsx3(Stack2, { spacing: "4px", children: OPTIMIZATION_OPTION_GROUPS.map((group) => /* @__PURE__ */ jsxs3(Box2, { children: [
2296
+ /* @__PURE__ */ jsx3(Text2, { sx: styles.optimizationGroupHeader, children: group.name }),
2297
+ /* @__PURE__ */ jsx3(Stack2, { spacing: "2px", children: group.options.map((option) => /* @__PURE__ */ jsxs3(
2298
+ Stack2,
2299
+ {
2300
+ as: "label",
2301
+ spacing: "2px",
2302
+ sx: { ...styles.optimizationOptionRow, cursor: "pointer" },
2303
+ children: [
2304
+ /* @__PURE__ */ jsxs3(HStack2, { spacing: "8px", children: [
2305
+ /* @__PURE__ */ jsx3(
2306
+ Checkbox,
2307
+ {
2308
+ id: `optimization-${option.id}`,
2309
+ isChecked: selectedOptimizationIds.includes(option.id),
2310
+ isDisabled: !selectedOptimizationIds.includes(option.id) && selectedOptimizationIds.length >= 3,
2311
+ onChange: () => handleToggleOptimization(option.id)
2312
+ }
2313
+ ),
2314
+ /* @__PURE__ */ jsx3(Text2, { sx: styles.optimizationOptionLabel, children: option.title })
2315
+ ] }),
2316
+ option.subtitle ? /* @__PURE__ */ jsx3(Text2, { sx: styles.optimizationOptionHelp, children: option.subtitle }) : null
2317
+ ]
2318
+ },
2319
+ option.id
2320
+ )) })
2321
+ ] }, group.id)) }) }) })
2322
+ ] }),
2323
+ selectedOptimizationOptions.length >= 2 ? /* @__PURE__ */ jsxs3(Box2, { sx: styles.optimizationInputsContainer, children: [
2324
+ /* @__PURE__ */ jsx3(Stack2, { spacing: "16px", children: selectedOptimizationOptions.map((option, index) => {
2325
+ var _a;
2326
+ return /* @__PURE__ */ jsx3(
2327
+ Stack2,
2328
+ {
2329
+ sx: styles.optimizationInputItem,
2330
+ children: /* @__PURE__ */ jsxs3(Stack2, { sx: styles.hardConstraintsInputItem, children: [
2331
+ /* @__PURE__ */ jsxs3(Text2, { sx: styles.hardConstraintsInputLabel, children: [
2332
+ index + 1,
2333
+ ". ",
2334
+ option.title
2335
+ ] }),
2336
+ /* @__PURE__ */ jsxs3(InputGroup, { children: [
2337
+ /* @__PURE__ */ jsx3(
2338
+ Input,
2339
+ {
2340
+ type: "text",
2341
+ value: (_a = optimizationValues[`${option.id}_weightPoints`]) != null ? _a : "1",
2342
+ onChange: (e) => {
2343
+ const val = e.target.value;
2344
+ if (val !== "" && (!/^\d+$/.test(val) || Number(val) < 1)) return;
2345
+ setOptimizationValues((prev) => ({
2346
+ ...prev,
2347
+ [`${option.id}_weightPoints`]: val
2348
+ }));
2349
+ },
2350
+ sx: styles.hardConstraintsInputField
2351
+ }
2352
+ ),
2353
+ /* @__PURE__ */ jsx3(InputRightAddon, { sx: styles.optimizationSuffix, children: "points" })
2354
+ ] })
2355
+ ] })
2356
+ },
2357
+ option.id
2358
+ );
2359
+ }) }),
2360
+ /* @__PURE__ */ jsx3(Divider, { sx: { ...styles.optimizationDivider, my: "16px" } }),
2361
+ /* @__PURE__ */ jsx3(Text2, { sx: styles.optimizationSummary, children: allocationSummary })
2362
+ ] }) : null
2363
+ ] })
2364
+ ] });
2365
+ const stepsContent = /* @__PURE__ */ jsxs3(Stack2, { spacing: "16px", sx: styles.stepsContainer, children: [
2366
+ showFailed ? violationPanel : null,
2367
+ /* @__PURE__ */ jsx3(Stack2, { spacing: "14px", sx: styles.stepsList, children: SCHEDULE_STEPS.map((step, index) => {
2368
+ const status = stepStatusForIndex(index);
2369
+ const isFailedStep = showFailed && index === activeStepIndex;
2370
+ const iconStyles = {
2371
+ ...styles.stepIcon,
2372
+ ...status === "active" ? styles.stepIconActive : null,
2373
+ ...status === "done" ? styles.stepIconDone : null,
2374
+ ...status === "pending" ? styles.stepIconPending : null,
2375
+ ...isFailedStep ? styles.stepIconFailed : null
2376
+ };
2377
+ const titleStyles = {
2378
+ ...styles.stepTitle,
2379
+ ...status === "active" ? styles.stepTitleActive : null,
2380
+ ...status === "done" ? styles.stepTitleDone : null,
2381
+ ...status === "pending" ? styles.stepTitlePending : null,
2382
+ ...isFailedStep ? styles.stepTitleFailed : null
2383
+ };
2384
+ return /* @__PURE__ */ jsxs3(HStack2, { sx: styles.stepRow, children: [
2385
+ /* @__PURE__ */ jsx3(Box2, { sx: iconStyles, children: isFailedStep ? /* @__PURE__ */ jsx3(FailedIcon, { width: 12, height: 12 }) : status === "active" ? /* @__PURE__ */ jsx3(Spinner2, { size: "sm", thickness: "2px", speed: "0.7s" }) : status === "done" ? /* @__PURE__ */ jsx3(CheckIcon, { width: 12, height: 12 }) : null }),
2386
+ /* @__PURE__ */ jsxs3(Box2, { children: [
2387
+ /* @__PURE__ */ jsx3(Text2, { sx: titleStyles, children: step.title }),
2388
+ status === "active" && step.subtitle ? /* @__PURE__ */ jsx3(Text2, { sx: styles.stepSubtitle, children: step.subtitle }) : null
2389
+ ] })
2390
+ ] }, step.title);
2391
+ }) })
2392
+ ] });
2393
+ const modalFooter = showConfiguration ? /* @__PURE__ */ jsxs3(ModalFooter, { sx: styles.footer, children: [
2394
+ /* @__PURE__ */ jsx3(Button2, { variant: "outline", onClick: onClose, sx: styles.cancelButton, children: cancelLabel }),
2395
+ /* @__PURE__ */ jsx3(
2396
+ Button2,
2397
+ {
2398
+ onClick: handleCreateSchedule,
2399
+ isDisabled: selectedCount === 0 || openShifts.length === 0 || hasEmptyInputs,
2400
+ sx: styles.primaryButton,
2401
+ children: primaryActionLabel
2402
+ }
2403
+ )
2404
+ ] }) : showFailed ? /* @__PURE__ */ jsxs3(ModalFooter, { sx: styles.failedFooter, children: [
2405
+ /* @__PURE__ */ jsx3(
2406
+ Button2,
2407
+ {
2408
+ variant: "outline",
2409
+ onClick: () => {
2410
+ if (lastFailure) {
2411
+ onFixManually == null ? void 0 : onFixManually(lastFailure, {
2412
+ selectedLocationIds,
2413
+ selectedConstraintIds,
2414
+ constraintValues,
2415
+ selectedOptimizationIds,
2416
+ optimizationValues
2417
+ });
2418
+ }
2419
+ onClose();
2420
+ },
2421
+ sx: styles.failedSecondaryButton,
2422
+ children: "Fix manually on schedule"
2423
+ }
2424
+ ),
2425
+ /* @__PURE__ */ jsx3(Button2, { onClick: handleAdjustConstraints, sx: styles.failedPrimaryButton, children: "Adjust constraints" })
2426
+ ] }) : null;
2427
+ const modalContent = /* @__PURE__ */ jsxs3(Modal, { isOpen, onClose, isCentered: true, motionPreset: "scale", closeOnOverlayClick: false, closeOnEsc: false, children: [
2428
+ /* @__PURE__ */ jsx3(ModalOverlay, { sx: styles.overlay }),
2429
+ /* @__PURE__ */ jsxs3(ModalContent, { sx: styles.content, children: [
2430
+ /* @__PURE__ */ jsx3(ModalHeader, { sx: styles.header, children: /* @__PURE__ */ jsxs3(HStack2, { justify: "space-between", w: "full", children: [
2431
+ /* @__PURE__ */ jsx3(Text2, { sx: styles.title, children: modalTitle }),
2432
+ /* @__PURE__ */ jsx3(
2433
+ IconButton2,
2434
+ {
2435
+ "aria-label": "Close modal",
2436
+ variant: "ghost",
2437
+ size: "sm",
2438
+ icon: /* @__PURE__ */ jsx3(CloseIcon, {}),
2439
+ onClick: onClose,
2440
+ sx: styles.closeButton
2441
+ }
2442
+ )
2443
+ ] }) }),
2444
+ /* @__PURE__ */ jsx3(ModalBody, { sx: showConfiguration ? styles.body : { ...styles.body, ...styles.creatingBody }, children: showConfiguration ? configurationContent : stepsContent }),
2445
+ modalFooter
2446
+ ] })
2447
+ ] });
2448
+ if (theme || cssVarsRoot) {
2449
+ return /* @__PURE__ */ jsx3(ChakraProvider, { theme: mergedTheme, cssVarsRoot, children: modalContent });
2450
+ }
2451
+ return modalContent;
2452
+ }
2453
+
2454
+ // src/AutoSchedulerModalWithProvider.tsx
2455
+ import { ChakraProvider as ChakraProvider2 } from "@chakra-ui/react";
2456
+ import { jsx as jsx4 } from "react/jsx-runtime";
2457
+ function AutoSchedulerModalWithProvider(props) {
2458
+ return /* @__PURE__ */ jsx4(ChakraProvider2, { children: /* @__PURE__ */ jsx4(AutoSchedulerModal, { ...props }) });
2459
+ }
2460
+
2461
+ // src/ViolatedConstraintsPanelWithProvider.tsx
2462
+ import { ChakraProvider as ChakraProvider3 } from "@chakra-ui/react";
2463
+ import { jsx as jsx5 } from "react/jsx-runtime";
2464
+ function ViolatedConstraintsPanelWithProvider(props) {
2465
+ return /* @__PURE__ */ jsx5(ChakraProvider3, { children: /* @__PURE__ */ jsx5(ViolatedConstraintsPanel, { ...props }) });
2466
+ }
2467
+
2468
+ // src/utils/ruleIdFriendlyNames.ts
2469
+ var RULE_ID_FRIENDLY_NAMES = {
2470
+ // System soft constraints (format: system-{type}-soft)
2471
+ "system-employee-preference-soft": "Staff preferences",
2472
+ "system-employee-prefer-not-time-soft": "Prefer-not availability",
2473
+ "system-no-overlapping-shifts-soft": "No overlapping shifts",
2474
+ "system-overtime-soft": "Expected overtime",
2475
+ // Archetype soft constraints (format: {archetype}-soft)
2476
+ "accumulator-soft": "Hours limit",
2477
+ "accumulatordays-soft": "Work days limit",
2478
+ "spacer-soft": "Rest between shifts",
2479
+ "spacerperiodic-soft": "Required rest period",
2480
+ "composition-soft": "Staffing ratio",
2481
+ "transactional-soft": "Policy rule",
2482
+ "balancer-medium": "Fair work distribution",
2483
+ "balancerovertime-medium": "Minimize overtime",
2484
+ "coverage-soft": "Minimum staffing",
2485
+ "sizing-soft": "Shift duration limit",
2486
+ "sequencer-soft": "Shift sequence",
2487
+ "optimizerminimize-soft": "Minimize target",
2488
+ "optimizermaximize-soft": "Maximize target",
2489
+ // Rule IDs (from rules JSON)
2490
+ "max-weekly-hours": "Max weekly hours",
2491
+ "max-daily-hours": "Max daily hours",
2492
+ "min-rest-between-shifts": "Min rest between shifts",
2493
+ "min-days-off-per-week": "Min days off per week",
2494
+ "max-work-days-per-week": "Max work days per week",
2495
+ "fair-hour-distribution": "Fair hour distribution",
2496
+ "overtime-balance": "Overtime balance",
2497
+ "labor-cost-optimization": "Labor cost optimization",
2498
+ "maximize-sales-performance": "Maximize sales performance",
2499
+ "split-shift-penalty": "Split shift penalty",
2500
+ "shift-continuity-penalty": "Shift continuity penalty",
2501
+ "manager-cashier-ratio": "Staffing ratio",
2502
+ "min-staffing": "Minimum staffing",
2503
+ "shift-duration-limits": "Shift duration limit",
2504
+ "max-consecutive-days": "Shift sequence",
2505
+ "some-transactional-rule": "Policy rule"
2506
+ };
2507
+ function getFriendlyRuleName(ruleIdOrConstraintName) {
2508
+ const trimmed = ruleIdOrConstraintName == null ? void 0 : ruleIdOrConstraintName.trim();
2509
+ if (!trimmed) return "Unknown";
2510
+ const mapped = RULE_ID_FRIENDLY_NAMES[trimmed.toLocaleLowerCase()];
2511
+ if (mapped) return mapped;
2512
+ return trimmed.split("-").map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join(" ");
2513
+ }
2514
+
2515
+ // src/api/types.ts
2516
+ var RequestError = class extends Error {
2517
+ constructor(message, status, payload) {
2518
+ super(message);
2519
+ this.name = "RequestError";
2520
+ this.status = status;
2521
+ this.payload = payload;
2522
+ }
2523
+ };
2524
+ export {
2525
+ AutoSchedulerModal,
2526
+ AutoSchedulerModalWithProvider,
2527
+ RequestError,
2528
+ ViolatedConstraintsPanel,
2529
+ ViolatedConstraintsPanelWithProvider,
2530
+ buildRulesPayload,
2531
+ buildSchedulePayload,
2532
+ getFriendlyRuleName
2533
+ };
2534
+ //# sourceMappingURL=index.js.map