@easyteam/auto-scheduler-modal-ui 0.1.3 → 0.1.5
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.cjs +1105 -370
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +202 -7
- package/dist/index.d.ts +202 -7
- package/dist/index.js +1098 -368
- package/dist/index.js.map +1 -1
- package/package.json +27 -25
package/dist/index.cjs
CHANGED
|
@@ -29,22 +29,38 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
29
29
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
30
30
|
|
|
31
31
|
// src/index.ts
|
|
32
|
-
var
|
|
33
|
-
__export(
|
|
32
|
+
var index_exports = {};
|
|
33
|
+
__export(index_exports, {
|
|
34
34
|
AutoSchedulerModal: () => AutoSchedulerModal,
|
|
35
35
|
AutoSchedulerModalWithProvider: () => AutoSchedulerModalWithProvider,
|
|
36
|
+
InfeasibleScheduleError: () => InfeasibleScheduleError,
|
|
37
|
+
MAXIMIZE_CONVERSION_RATE_RULE_ID: () => MAXIMIZE_CONVERSION_RATE_RULE_ID,
|
|
36
38
|
RequestError: () => RequestError,
|
|
39
|
+
TRAFFIC_FORECAST_INCENTIVE_RULE_ID: () => TRAFFIC_FORECAST_INCENTIVE_RULE_ID,
|
|
37
40
|
ViolatedConstraintsPanel: () => ViolatedConstraintsPanel,
|
|
38
41
|
ViolatedConstraintsPanelWithProvider: () => ViolatedConstraintsPanelWithProvider,
|
|
39
42
|
buildRulesPayload: () => buildRulesPayload,
|
|
40
43
|
buildSchedulePayload: () => buildSchedulePayload,
|
|
41
|
-
|
|
44
|
+
defaultSelectedScheduleDates: () => defaultSelectedScheduleDates,
|
|
45
|
+
getFriendlyRuleName: () => getFriendlyRuleName,
|
|
46
|
+
getSequentialWeekDatesFromWeekStart: () => getSequentialWeekDatesFromWeekStart,
|
|
47
|
+
weekdayTwoLetterFromYmd: () => weekdayTwoLetterFromYmd
|
|
42
48
|
});
|
|
43
|
-
module.exports = __toCommonJS(
|
|
49
|
+
module.exports = __toCommonJS(index_exports);
|
|
44
50
|
|
|
45
51
|
// src/AutoSchedulerModal.tsx
|
|
46
52
|
var import_react3 = require("react");
|
|
47
53
|
var import_react4 = require("@chakra-ui/react");
|
|
54
|
+
var import_web_ui = require("@easyteam/web-ui");
|
|
55
|
+
|
|
56
|
+
// src/types.ts
|
|
57
|
+
var InfeasibleScheduleError = class extends Error {
|
|
58
|
+
constructor(failure) {
|
|
59
|
+
super("Schedule could not be created due to constraint violations");
|
|
60
|
+
this.name = "InfeasibleScheduleError";
|
|
61
|
+
this.failure = failure;
|
|
62
|
+
}
|
|
63
|
+
};
|
|
48
64
|
|
|
49
65
|
// src/icons/AutoSchedulerIcons.tsx
|
|
50
66
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
@@ -181,19 +197,15 @@ function CheckIcon(props) {
|
|
|
181
197
|
"svg",
|
|
182
198
|
{
|
|
183
199
|
"aria-hidden": "true",
|
|
184
|
-
viewBox: "0 0
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
height: "20",
|
|
200
|
+
viewBox: "0 0 24 24",
|
|
201
|
+
width: "24",
|
|
202
|
+
height: "24",
|
|
188
203
|
...props,
|
|
189
204
|
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
190
205
|
"path",
|
|
191
206
|
{
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
strokeWidth: "1.6",
|
|
195
|
-
strokeLinecap: "round",
|
|
196
|
-
strokeLinejoin: "round"
|
|
207
|
+
fill: "currentColor",
|
|
208
|
+
d: "M20.285 2l-11.285 11.567-5.286-5.011-3.714 3.716 9 8.728 15-15.285z"
|
|
197
209
|
}
|
|
198
210
|
)
|
|
199
211
|
}
|
|
@@ -715,7 +727,7 @@ var defaultStyles = {
|
|
|
715
727
|
},
|
|
716
728
|
stepIconDone: {
|
|
717
729
|
border: "none",
|
|
718
|
-
bg: "#
|
|
730
|
+
bg: "#5338ff",
|
|
719
731
|
color: "white",
|
|
720
732
|
borderRadius: "10px"
|
|
721
733
|
},
|
|
@@ -1239,7 +1251,7 @@ var buildStyles = (defaultStyles2, overrides) => ({
|
|
|
1239
1251
|
}
|
|
1240
1252
|
});
|
|
1241
1253
|
|
|
1242
|
-
// src/
|
|
1254
|
+
// src/utils/ruleIdMapping.ts
|
|
1243
1255
|
var UI_TO_BACKEND_RULE_ID = {
|
|
1244
1256
|
max_weekly_hours: "max-weekly-hours",
|
|
1245
1257
|
max_daily_hours: "max-daily-hours",
|
|
@@ -1257,6 +1269,11 @@ var BACKEND_TO_UI_RULE_ID = Object.fromEntries(
|
|
|
1257
1269
|
function toBackendRuleId(uiOptionId) {
|
|
1258
1270
|
return UI_TO_BACKEND_RULE_ID[uiOptionId];
|
|
1259
1271
|
}
|
|
1272
|
+
|
|
1273
|
+
// src/buildSchedulePayload.ts
|
|
1274
|
+
var MAXIMIZE_CONVERSION_RATE_RULE_ID = "maximize-conversion-rate";
|
|
1275
|
+
var TRAFFIC_FORECAST_INCENTIVE_RULE_ID = "traffic-forecast-incentive";
|
|
1276
|
+
var SALES_OPTIMIZATION_UI_IDS = /* @__PURE__ */ new Set(["maximize_sales", "cost_to_sales_ratio"]);
|
|
1260
1277
|
function buildParametersForRule(backendRuleId, constraintValues, _optimizationValues) {
|
|
1261
1278
|
var _a, _b, _c, _d;
|
|
1262
1279
|
const params = {};
|
|
@@ -1327,11 +1344,24 @@ function hasAnyParameterOverrides(selectedConstraintIds, constraintValues, selec
|
|
|
1327
1344
|
}
|
|
1328
1345
|
return false;
|
|
1329
1346
|
}
|
|
1330
|
-
function
|
|
1347
|
+
function appendSiblingRuleId(ruleIds, siblingId) {
|
|
1348
|
+
if (!ruleIds.includes(siblingId)) {
|
|
1349
|
+
ruleIds.push(siblingId);
|
|
1350
|
+
}
|
|
1351
|
+
}
|
|
1352
|
+
function buildRulesPayload(selectedConstraintIds, constraintValues, selectedOptimizationIds, optimizationValues, siblings = {}) {
|
|
1331
1353
|
const allSelectedRuleIds = [
|
|
1332
1354
|
...selectedConstraintIds.map(toBackendRuleId),
|
|
1333
1355
|
...selectedOptimizationIds.map(toBackendRuleId)
|
|
1334
1356
|
].filter((id) => Boolean(id));
|
|
1357
|
+
const salesOptSelected = selectedOptimizationIds.some((id) => SALES_OPTIMIZATION_UI_IDS.has(id));
|
|
1358
|
+
const maximizeSalesSelected = selectedOptimizationIds.includes("maximize_sales");
|
|
1359
|
+
if (siblings.includeConversionRate && salesOptSelected) {
|
|
1360
|
+
appendSiblingRuleId(allSelectedRuleIds, MAXIMIZE_CONVERSION_RATE_RULE_ID);
|
|
1361
|
+
}
|
|
1362
|
+
if (siblings.includeTrafficIncentive && maximizeSalesSelected) {
|
|
1363
|
+
appendSiblingRuleId(allSelectedRuleIds, TRAFFIC_FORECAST_INCENTIVE_RULE_ID);
|
|
1364
|
+
}
|
|
1335
1365
|
if (allSelectedRuleIds.length === 0) {
|
|
1336
1366
|
return {};
|
|
1337
1367
|
}
|
|
@@ -1400,9 +1430,25 @@ function toApiLocation(location) {
|
|
|
1400
1430
|
id: String(location.id),
|
|
1401
1431
|
timezone: (_a = location.timezone) != null ? _a : void 0,
|
|
1402
1432
|
jurisdiction: (_b = location.jurisdiction) != null ? _b : void 0,
|
|
1403
|
-
tags: "tags" in location ? location.tags : void 0
|
|
1433
|
+
tags: "tags" in location ? location.tags : void 0,
|
|
1434
|
+
properties: location.properties ? location.properties : void 0
|
|
1404
1435
|
};
|
|
1405
1436
|
}
|
|
1437
|
+
function employeeHasConversionRate(employee) {
|
|
1438
|
+
var _a;
|
|
1439
|
+
const value = (_a = employee.properties) == null ? void 0 : _a.conversionRate;
|
|
1440
|
+
return typeof value === "number" && Number.isFinite(value) && value > 0;
|
|
1441
|
+
}
|
|
1442
|
+
function employeeTouchesSelectedLocations(employee, selectedLocationIds) {
|
|
1443
|
+
var _a;
|
|
1444
|
+
const ids = (_a = employee.locationIds) != null ? _a : [];
|
|
1445
|
+
return ids.some((id) => selectedLocationIds.includes(String(id)));
|
|
1446
|
+
}
|
|
1447
|
+
function hasConversionRateInSelectedLocations(employees, selectedLocationIds) {
|
|
1448
|
+
return employees.some(
|
|
1449
|
+
(employee) => employeeTouchesSelectedLocations(employee, selectedLocationIds) && employeeHasConversionRate(employee)
|
|
1450
|
+
);
|
|
1451
|
+
}
|
|
1406
1452
|
function buildSchedulePayload(input) {
|
|
1407
1453
|
const {
|
|
1408
1454
|
employees,
|
|
@@ -1413,7 +1459,11 @@ function buildSchedulePayload(input) {
|
|
|
1413
1459
|
selectedConstraintIds,
|
|
1414
1460
|
constraintValues,
|
|
1415
1461
|
selectedOptimizationIds,
|
|
1416
|
-
optimizationValues
|
|
1462
|
+
optimizationValues,
|
|
1463
|
+
trafficForecast,
|
|
1464
|
+
constraintDefinitionIds,
|
|
1465
|
+
constraintDefinitions,
|
|
1466
|
+
organizationId
|
|
1417
1467
|
} = input;
|
|
1418
1468
|
const locationsMap = /* @__PURE__ */ new Map();
|
|
1419
1469
|
for (const j of jurisdictions) {
|
|
@@ -1422,19 +1472,45 @@ function buildSchedulePayload(input) {
|
|
|
1422
1472
|
}
|
|
1423
1473
|
}
|
|
1424
1474
|
const selectedLocations = selectedLocationIds.map((id) => locationsMap.get(id)).filter((loc) => Boolean(loc));
|
|
1475
|
+
const scopedOpenShifts = openShifts.filter(
|
|
1476
|
+
(shift) => {
|
|
1477
|
+
var _a;
|
|
1478
|
+
return selectedLocationIds.includes(String((_a = shift.location) == null ? void 0 : _a.id));
|
|
1479
|
+
}
|
|
1480
|
+
);
|
|
1481
|
+
const includeConversionRate = hasConversionRateInSelectedLocations(
|
|
1482
|
+
employees,
|
|
1483
|
+
selectedLocationIds
|
|
1484
|
+
);
|
|
1485
|
+
const scopedTrafficForecast = (trafficForecast != null ? trafficForecast : []).filter(
|
|
1486
|
+
(entry) => selectedLocationIds.includes(String(entry.locationId))
|
|
1487
|
+
);
|
|
1488
|
+
const includeTrafficIncentive = scopedTrafficForecast.length > 0;
|
|
1425
1489
|
const rules = buildRulesPayload(
|
|
1426
1490
|
selectedConstraintIds,
|
|
1427
1491
|
constraintValues,
|
|
1428
1492
|
selectedOptimizationIds,
|
|
1429
|
-
optimizationValues
|
|
1493
|
+
optimizationValues,
|
|
1494
|
+
{ includeConversionRate, includeTrafficIncentive }
|
|
1430
1495
|
);
|
|
1431
1496
|
const includeEmployeePreferences = selectedOptimizationIds.includes("staff_preferences");
|
|
1497
|
+
const hasIds = Boolean(constraintDefinitionIds && constraintDefinitionIds.length > 0);
|
|
1498
|
+
const hasDefs = Boolean(constraintDefinitions && constraintDefinitions.length > 0);
|
|
1499
|
+
const hasRules = hasIds || hasDefs;
|
|
1500
|
+
if (hasIds && !organizationId) {
|
|
1501
|
+
throw new Error("organizationId is required when constraintDefinitionIds are provided");
|
|
1502
|
+
}
|
|
1503
|
+
const trafficRuleActive = includeTrafficIncentive && selectedOptimizationIds.includes("maximize_sales");
|
|
1432
1504
|
return {
|
|
1433
1505
|
employees: employees.map(toApiEmployee),
|
|
1434
|
-
shifts:
|
|
1506
|
+
shifts: scopedOpenShifts.map((shift) => toApiShift(shift, includeEmployeePreferences)),
|
|
1435
1507
|
locations: selectedLocations.map(toApiLocation),
|
|
1436
1508
|
...timeOffs && timeOffs.length > 0 ? { timeOffs } : {},
|
|
1437
|
-
...rules
|
|
1509
|
+
...rules,
|
|
1510
|
+
...trafficRuleActive ? { trafficForecast: scopedTrafficForecast } : {},
|
|
1511
|
+
...hasIds ? { constraintDefinitionIds } : {},
|
|
1512
|
+
...hasDefs ? { constraintDefinitions } : {},
|
|
1513
|
+
...hasRules && organizationId ? { organizationId } : {}
|
|
1438
1514
|
};
|
|
1439
1515
|
}
|
|
1440
1516
|
|
|
@@ -1457,7 +1533,7 @@ async function submitSolveJob(baseURL, payload, options) {
|
|
|
1457
1533
|
headers.Authorization = options.authorization;
|
|
1458
1534
|
}
|
|
1459
1535
|
const response = await axiosInstance.post(
|
|
1460
|
-
`${cleanBaseUrl}/api/schedule/solve-async`,
|
|
1536
|
+
withHorizonQuery(`${cleanBaseUrl}/api/schedule/solve-async`, options),
|
|
1461
1537
|
payload,
|
|
1462
1538
|
{ headers: Object.keys(headers).length > 0 ? headers : void 0 }
|
|
1463
1539
|
);
|
|
@@ -1499,8 +1575,34 @@ async function getJobSolution(baseURL, jobId, options) {
|
|
|
1499
1575
|
);
|
|
1500
1576
|
return response.data;
|
|
1501
1577
|
}
|
|
1578
|
+
function withHorizonQuery(url, options) {
|
|
1579
|
+
if (!(options == null ? void 0 : options.horizonStartDate) || !(options == null ? void 0 : options.horizonEndDate)) {
|
|
1580
|
+
return url;
|
|
1581
|
+
}
|
|
1582
|
+
const params = new URLSearchParams({
|
|
1583
|
+
dateRangeStart: options.horizonStartDate,
|
|
1584
|
+
dateRangeEnd: options.horizonEndDate
|
|
1585
|
+
});
|
|
1586
|
+
return `${url}?${params.toString()}`;
|
|
1587
|
+
}
|
|
1502
1588
|
|
|
1503
1589
|
// src/utils/violationMessages.ts
|
|
1590
|
+
var INTERNAL_VIOLATION_PATTERNS = [
|
|
1591
|
+
/\w+Rule\{/,
|
|
1592
|
+
/\[I@/,
|
|
1593
|
+
/Employee\{id=/,
|
|
1594
|
+
/Accumulator violation \(Rule:/,
|
|
1595
|
+
/^Schedule has \d+ hard, \d+ medium, and \d+ soft constraint violation/,
|
|
1596
|
+
/ConstraintMatch\{/,
|
|
1597
|
+
/MatchAnalysis\{/,
|
|
1598
|
+
/Indictment\{/,
|
|
1599
|
+
/Hard constraint violation \(count:/
|
|
1600
|
+
];
|
|
1601
|
+
function isInternalViolationText(text) {
|
|
1602
|
+
const trimmed = text == null ? void 0 : text.trim();
|
|
1603
|
+
if (!trimmed) return false;
|
|
1604
|
+
return INTERNAL_VIOLATION_PATTERNS.some((pattern) => pattern.test(trimmed));
|
|
1605
|
+
}
|
|
1504
1606
|
var SYSTEM_CONSTRAINT_MESSAGES = {
|
|
1505
1607
|
"system-unassigned-Hard": {
|
|
1506
1608
|
title: "Not enough eligible staff to cover all shifts",
|
|
@@ -1520,15 +1622,19 @@ var SYSTEM_CONSTRAINT_MESSAGES = {
|
|
|
1520
1622
|
},
|
|
1521
1623
|
"system-employee-unavailable-time-Hard": {
|
|
1522
1624
|
title: "Unavailable time",
|
|
1523
|
-
detail: "
|
|
1625
|
+
detail: "Some open shifts fall outside the available time windows for the remaining eligible team members."
|
|
1524
1626
|
},
|
|
1525
1627
|
"system-employee-available-time-Hard": {
|
|
1526
1628
|
title: "Outside available time",
|
|
1527
|
-
detail: "
|
|
1629
|
+
detail: "Some open shifts fall outside the available time windows for the remaining eligible team members."
|
|
1528
1630
|
},
|
|
1529
1631
|
"system-employee-time-off-Hard": {
|
|
1530
1632
|
title: "Time off prevents assignment",
|
|
1531
1633
|
detail: "Some open shifts overlap with approved time off for the remaining eligible team members."
|
|
1634
|
+
},
|
|
1635
|
+
"system-no-overlapping-shifts-Hard": {
|
|
1636
|
+
title: "Overlapping shifts",
|
|
1637
|
+
detail: "Some team members would be assigned to overlapping shifts if all open shifts are assigned."
|
|
1532
1638
|
}
|
|
1533
1639
|
};
|
|
1534
1640
|
var RULE_ID_MESSAGES = {
|
|
@@ -1544,119 +1650,259 @@ var RULE_ID_MESSAGES = {
|
|
|
1544
1650
|
title: "Minimum rest between shifts",
|
|
1545
1651
|
detail: "Some team members would not receive the required rest time if all open shifts are assigned."
|
|
1546
1652
|
},
|
|
1547
|
-
"
|
|
1653
|
+
"max-work-days-per-week": {
|
|
1548
1654
|
title: "Maximum work days per week exceeded",
|
|
1549
1655
|
detail: "Some team members would exceed the allowed work days this week if all open shifts are assigned."
|
|
1550
1656
|
},
|
|
1657
|
+
"min-days-off-per-week": {
|
|
1658
|
+
title: "Minimum days off not met",
|
|
1659
|
+
detail: "Some team members would not receive the required days off this week if all open shifts are assigned."
|
|
1660
|
+
},
|
|
1551
1661
|
"fair-hour-distribution": {
|
|
1552
1662
|
title: "Work distribution not balanced",
|
|
1553
|
-
detail: "
|
|
1663
|
+
detail: "Some team members would receive an uneven share of shifts if all open shifts are assigned."
|
|
1554
1664
|
},
|
|
1555
1665
|
"overtime-balance": {
|
|
1556
1666
|
title: "Overtime balance rule violated",
|
|
1557
|
-
detail: "
|
|
1667
|
+
detail: "Some team members would exceed overtime limits if all open shifts are assigned."
|
|
1668
|
+
},
|
|
1669
|
+
"labor-cost-optimization": {
|
|
1670
|
+
title: "Labor cost optimization conflict",
|
|
1671
|
+
detail: "Some open shifts cannot be assigned while meeting labor cost optimization priorities."
|
|
1672
|
+
},
|
|
1673
|
+
"maximize-sales-performance": {
|
|
1674
|
+
title: "Sales optimization conflict",
|
|
1675
|
+
detail: "Some open shifts cannot be assigned while meeting sales optimization priorities."
|
|
1676
|
+
},
|
|
1677
|
+
"staff-open-shift-preferences": {
|
|
1678
|
+
title: "Staff preferences conflict",
|
|
1679
|
+
detail: "Some open shifts cannot be assigned while respecting all staff shift preferences."
|
|
1680
|
+
},
|
|
1681
|
+
"split-shift-penalty": {
|
|
1682
|
+
title: "Split shift rule violated",
|
|
1683
|
+
detail: "Some team members would receive split shifts that violate scheduling preferences."
|
|
1684
|
+
},
|
|
1685
|
+
"shift-continuity-penalty": {
|
|
1686
|
+
title: "Shift continuity rule violated",
|
|
1687
|
+
detail: "Some team members would receive shift patterns that violate continuity preferences."
|
|
1558
1688
|
},
|
|
1559
1689
|
"manager-cashier-ratio": {
|
|
1560
1690
|
title: "Staffing ratio violated",
|
|
1561
|
-
detail: "
|
|
1691
|
+
detail: "Some open shifts cannot be assigned while meeting required staffing ratios."
|
|
1562
1692
|
},
|
|
1563
1693
|
"min-staffing": {
|
|
1564
1694
|
title: "Minimum staffing not met",
|
|
1565
|
-
detail: "
|
|
1695
|
+
detail: "Some time periods would not meet minimum staffing requirements if all open shifts are assigned."
|
|
1566
1696
|
},
|
|
1567
1697
|
"shift-duration-limits": {
|
|
1568
1698
|
title: "Shift duration outside allowed range",
|
|
1569
|
-
detail: "
|
|
1699
|
+
detail: "Some open shifts fall outside the allowed shift duration range."
|
|
1570
1700
|
},
|
|
1571
1701
|
"max-consecutive-days": {
|
|
1572
1702
|
title: "Shift sequence rule violated",
|
|
1573
|
-
detail: "
|
|
1703
|
+
detail: "Some team members would violate consecutive shift rules if all open shifts are assigned."
|
|
1574
1704
|
},
|
|
1575
1705
|
"some-transactional-rule": {
|
|
1576
1706
|
title: "Policy rule violated",
|
|
1577
|
-
detail: "
|
|
1707
|
+
detail: "Some open shifts cannot be assigned while meeting all policy requirements."
|
|
1708
|
+
}
|
|
1709
|
+
};
|
|
1710
|
+
var CONSTRAINT_NAME_MESSAGES = {
|
|
1711
|
+
"AccumulatorDays-Hard": {
|
|
1712
|
+
title: "Maximum work days per week exceeded",
|
|
1713
|
+
detail: "Some team members would exceed the allowed work days this week if all open shifts are assigned."
|
|
1714
|
+
},
|
|
1715
|
+
"Accumulator-Hard": {
|
|
1716
|
+
title: "Hours limit exceeded",
|
|
1717
|
+
detail: "Some team members would exceed hour limits if all open shifts are assigned."
|
|
1718
|
+
},
|
|
1719
|
+
"Spacer-Hard": {
|
|
1720
|
+
title: "Rest period not met",
|
|
1721
|
+
detail: "Some team members would not receive the required rest time if all open shifts are assigned."
|
|
1722
|
+
},
|
|
1723
|
+
"SpacerPeriodic-Hard": {
|
|
1724
|
+
title: "Required rest period not met",
|
|
1725
|
+
detail: "Some team members would not receive the required rest period if all open shifts are assigned."
|
|
1726
|
+
},
|
|
1727
|
+
"Composition-Hard": {
|
|
1728
|
+
title: "Staffing ratio violated",
|
|
1729
|
+
detail: "Some open shifts cannot be assigned while meeting required staffing ratios."
|
|
1730
|
+
},
|
|
1731
|
+
"Transactional-Hard": {
|
|
1732
|
+
title: "Policy rule violated",
|
|
1733
|
+
detail: "Some open shifts cannot be assigned while meeting all policy requirements."
|
|
1734
|
+
},
|
|
1735
|
+
"Balancer-Hard": {
|
|
1736
|
+
title: "Work distribution not balanced",
|
|
1737
|
+
detail: "Some team members would receive an uneven share of shifts if all open shifts are assigned."
|
|
1738
|
+
},
|
|
1739
|
+
"Coverage-Hard": {
|
|
1740
|
+
title: "Minimum staffing not met",
|
|
1741
|
+
detail: "Some time periods would not meet minimum staffing requirements if all open shifts are assigned."
|
|
1742
|
+
},
|
|
1743
|
+
"Sizing-Hard": {
|
|
1744
|
+
title: "Shift duration outside allowed range",
|
|
1745
|
+
detail: "Some open shifts fall outside the allowed shift duration range."
|
|
1746
|
+
},
|
|
1747
|
+
"Sequencer-Hard": {
|
|
1748
|
+
title: "Shift sequence rule violated",
|
|
1749
|
+
detail: "Some team members would violate consecutive shift rules if all open shifts are assigned."
|
|
1750
|
+
},
|
|
1751
|
+
"OptimizerMinimize-Hard": {
|
|
1752
|
+
title: "Optimization rule violated",
|
|
1753
|
+
detail: "Some open shifts cannot be assigned while meeting all optimization priorities."
|
|
1754
|
+
},
|
|
1755
|
+
"OptimizerMaximize-Hard": {
|
|
1756
|
+
title: "Optimization rule violated",
|
|
1757
|
+
detail: "Some open shifts cannot be assigned while meeting all optimization priorities."
|
|
1578
1758
|
}
|
|
1579
1759
|
};
|
|
1580
1760
|
var ARCHETYPE_FALLBACK_MESSAGES = {
|
|
1581
|
-
|
|
1761
|
+
AccumulatorDays: {
|
|
1762
|
+
title: "Maximum work days per week exceeded",
|
|
1763
|
+
detail: "Some team members would exceed the allowed work days this week if all open shifts are assigned."
|
|
1764
|
+
},
|
|
1765
|
+
Accumulator: {
|
|
1582
1766
|
title: "Hours limit exceeded",
|
|
1583
|
-
detail: "
|
|
1767
|
+
detail: "Some team members would exceed hour limits if all open shifts are assigned."
|
|
1584
1768
|
},
|
|
1585
|
-
|
|
1769
|
+
Spacer: {
|
|
1586
1770
|
title: "Rest period not met",
|
|
1587
|
-
detail: "
|
|
1771
|
+
detail: "Some team members would not receive the required rest time if all open shifts are assigned."
|
|
1588
1772
|
},
|
|
1589
|
-
|
|
1773
|
+
SpacerPeriodic: {
|
|
1590
1774
|
title: "Required rest period not met",
|
|
1591
|
-
detail: "
|
|
1775
|
+
detail: "Some team members would not receive the required rest period if all open shifts are assigned."
|
|
1592
1776
|
},
|
|
1593
|
-
|
|
1777
|
+
Composition: {
|
|
1594
1778
|
title: "Staffing ratio violated",
|
|
1595
|
-
detail: "
|
|
1779
|
+
detail: "Some open shifts cannot be assigned while meeting required staffing ratios."
|
|
1596
1780
|
},
|
|
1597
|
-
|
|
1781
|
+
Transactional: {
|
|
1598
1782
|
title: "Policy rule violated",
|
|
1599
|
-
detail: "
|
|
1783
|
+
detail: "Some open shifts cannot be assigned while meeting all policy requirements."
|
|
1600
1784
|
},
|
|
1601
|
-
|
|
1785
|
+
Balancer: {
|
|
1602
1786
|
title: "Work distribution not balanced",
|
|
1603
|
-
detail: "
|
|
1787
|
+
detail: "Some team members would receive an uneven share of shifts if all open shifts are assigned."
|
|
1604
1788
|
},
|
|
1605
|
-
|
|
1789
|
+
Coverage: {
|
|
1606
1790
|
title: "Minimum staffing not met",
|
|
1607
|
-
detail: "
|
|
1791
|
+
detail: "Some time periods would not meet minimum staffing requirements if all open shifts are assigned."
|
|
1608
1792
|
},
|
|
1609
|
-
|
|
1793
|
+
Sizing: {
|
|
1610
1794
|
title: "Shift duration outside allowed range",
|
|
1611
|
-
detail: "
|
|
1795
|
+
detail: "Some open shifts fall outside the allowed shift duration range."
|
|
1612
1796
|
},
|
|
1613
|
-
|
|
1797
|
+
Sequencer: {
|
|
1614
1798
|
title: "Shift sequence rule violated",
|
|
1615
|
-
detail: "
|
|
1799
|
+
detail: "Some team members would violate consecutive shift rules if all open shifts are assigned."
|
|
1616
1800
|
},
|
|
1617
|
-
|
|
1618
|
-
title: "
|
|
1619
|
-
detail: "
|
|
1801
|
+
OptimizerMinimize: {
|
|
1802
|
+
title: "Optimization rule violated",
|
|
1803
|
+
detail: "Some open shifts cannot be assigned while meeting all optimization priorities."
|
|
1804
|
+
},
|
|
1805
|
+
OptimizerMaximize: {
|
|
1806
|
+
title: "Optimization rule violated",
|
|
1807
|
+
detail: "Some open shifts cannot be assigned while meeting all optimization priorities."
|
|
1620
1808
|
}
|
|
1621
1809
|
};
|
|
1622
1810
|
var FALLBACK_MESSAGE = {
|
|
1623
1811
|
title: "Rule violated",
|
|
1624
|
-
detail: "
|
|
1812
|
+
detail: "Please adjust constraints and try again."
|
|
1625
1813
|
};
|
|
1626
|
-
function
|
|
1627
|
-
if (constraintName
|
|
1628
|
-
|
|
1814
|
+
function parseBackendConstraintName(constraintName) {
|
|
1815
|
+
if (!(constraintName == null ? void 0 : constraintName.trim())) return {};
|
|
1816
|
+
let name = constraintName.trim();
|
|
1817
|
+
for (const suffix of ["Hard", "Medium", "Soft"]) {
|
|
1818
|
+
if (name.endsWith(`-${suffix}`)) {
|
|
1819
|
+
name = name.slice(0, -(suffix.length + 1));
|
|
1820
|
+
break;
|
|
1821
|
+
}
|
|
1822
|
+
}
|
|
1823
|
+
if (name.startsWith("system-")) {
|
|
1824
|
+
return {};
|
|
1825
|
+
}
|
|
1826
|
+
const lastDash = name.lastIndexOf("-");
|
|
1827
|
+
if (lastDash > 0) {
|
|
1828
|
+
return {
|
|
1829
|
+
ruleId: name.slice(0, lastDash),
|
|
1830
|
+
archetype: name.slice(lastDash + 1)
|
|
1831
|
+
};
|
|
1832
|
+
}
|
|
1833
|
+
return { archetype: name };
|
|
1834
|
+
}
|
|
1835
|
+
function resolveRuleIdMessage(ruleId) {
|
|
1836
|
+
if (!(ruleId == null ? void 0 : ruleId.trim())) return void 0;
|
|
1837
|
+
return RULE_ID_MESSAGES[ruleId.trim()];
|
|
1838
|
+
}
|
|
1839
|
+
function stripSeveritySuffix(name) {
|
|
1840
|
+
return name.replace(/-(Hard|Medium|Soft)$/, "");
|
|
1841
|
+
}
|
|
1842
|
+
function resolveConstraintNameMessage(constraintName) {
|
|
1843
|
+
if (!(constraintName == null ? void 0 : constraintName.trim())) return void 0;
|
|
1844
|
+
const trimmed = constraintName.trim();
|
|
1845
|
+
const exact = CONSTRAINT_NAME_MESSAGES[trimmed];
|
|
1846
|
+
if (exact) return exact;
|
|
1847
|
+
const severityStripped = stripSeveritySuffix(trimmed);
|
|
1848
|
+
const directRuleId = resolveRuleIdMessage(severityStripped);
|
|
1849
|
+
if (directRuleId) return directRuleId;
|
|
1850
|
+
const parsed = parseBackendConstraintName(trimmed);
|
|
1851
|
+
if (parsed.ruleId) {
|
|
1852
|
+
const fromEmbeddedRuleId = resolveRuleIdMessage(parsed.ruleId);
|
|
1853
|
+
if (fromEmbeddedRuleId) return fromEmbeddedRuleId;
|
|
1629
1854
|
}
|
|
1630
|
-
if (
|
|
1631
|
-
return
|
|
1855
|
+
if (parsed.archetype && parsed.archetype in ARCHETYPE_FALLBACK_MESSAGES) {
|
|
1856
|
+
return ARCHETYPE_FALLBACK_MESSAGES[parsed.archetype];
|
|
1632
1857
|
}
|
|
1633
|
-
return
|
|
1858
|
+
return void 0;
|
|
1634
1859
|
}
|
|
1635
|
-
function
|
|
1636
|
-
if (
|
|
1637
|
-
return
|
|
1860
|
+
function safeDescriptionMessage(description) {
|
|
1861
|
+
if (!(description == null ? void 0 : description.trim()) || isInternalViolationText(description)) {
|
|
1862
|
+
return void 0;
|
|
1638
1863
|
}
|
|
1639
|
-
|
|
1640
|
-
|
|
1864
|
+
return { title: description.trim(), detail: description.trim() };
|
|
1865
|
+
}
|
|
1866
|
+
function getSystemConstraintMessage(constraintName, description) {
|
|
1867
|
+
var _a;
|
|
1868
|
+
if (constraintName && constraintName in SYSTEM_CONSTRAINT_MESSAGES) {
|
|
1869
|
+
return SYSTEM_CONSTRAINT_MESSAGES[constraintName];
|
|
1641
1870
|
}
|
|
1642
|
-
|
|
1643
|
-
|
|
1871
|
+
const fromConstraintName = resolveConstraintNameMessage(constraintName);
|
|
1872
|
+
if (fromConstraintName) return fromConstraintName;
|
|
1873
|
+
return (_a = safeDescriptionMessage(description)) != null ? _a : FALLBACK_MESSAGE;
|
|
1874
|
+
}
|
|
1875
|
+
function getRuleBasedViolationMessage(ruleId, constraintName, description, archetype) {
|
|
1876
|
+
var _a;
|
|
1877
|
+
const fromRuleId = resolveRuleIdMessage(ruleId);
|
|
1878
|
+
if (fromRuleId) return fromRuleId;
|
|
1879
|
+
const fromConstraintName = resolveConstraintNameMessage(constraintName);
|
|
1880
|
+
if (fromConstraintName) return fromConstraintName;
|
|
1881
|
+
if ((archetype == null ? void 0 : archetype.trim()) && archetype.trim() in ARCHETYPE_FALLBACK_MESSAGES) {
|
|
1882
|
+
return ARCHETYPE_FALLBACK_MESSAGES[archetype.trim()];
|
|
1644
1883
|
}
|
|
1645
|
-
return FALLBACK_MESSAGE;
|
|
1884
|
+
return (_a = safeDescriptionMessage(description)) != null ? _a : FALLBACK_MESSAGE;
|
|
1646
1885
|
}
|
|
1647
1886
|
|
|
1648
1887
|
// src/utils/mapViolationsToResult.ts
|
|
1649
|
-
function
|
|
1888
|
+
function resolveCustomViolationMessage(ruleId, customMessages) {
|
|
1889
|
+
if (!customMessages) return void 0;
|
|
1890
|
+
const direct = customMessages[ruleId];
|
|
1891
|
+
if (direct) return direct;
|
|
1892
|
+
const uiRuleId = BACKEND_TO_UI_RULE_ID[ruleId];
|
|
1893
|
+
if (uiRuleId) {
|
|
1894
|
+
return customMessages[uiRuleId];
|
|
1895
|
+
}
|
|
1896
|
+
return void 0;
|
|
1897
|
+
}
|
|
1898
|
+
function mapViolationsToResult(violations, summary, customMessages) {
|
|
1899
|
+
var _a;
|
|
1650
1900
|
const hardViolations = violations.filter((v) => v.isHardViolation === true);
|
|
1651
1901
|
const seenTitles = /* @__PURE__ */ new Set();
|
|
1652
1902
|
const violatedConstraints = [];
|
|
1653
1903
|
for (const v of hardViolations) {
|
|
1654
1904
|
const hasRuleId = v.ruleId != null && v.ruleId !== void 0 && String(v.ruleId).trim() !== "";
|
|
1655
|
-
const msg = hasRuleId ? getRuleBasedViolationMessage(
|
|
1656
|
-
v.ruleId,
|
|
1657
|
-
v.constraintName,
|
|
1658
|
-
v.description
|
|
1659
|
-
) : getSystemConstraintMessage(v.constraintName, v.description);
|
|
1905
|
+
const msg = hasRuleId ? (_a = resolveCustomViolationMessage(v.ruleId, customMessages)) != null ? _a : getRuleBasedViolationMessage(v.ruleId, v.constraintName, v.description, v.archetype) : getSystemConstraintMessage(v.constraintName, v.description);
|
|
1660
1906
|
if (!seenTitles.has(msg.title)) {
|
|
1661
1907
|
seenTitles.add(msg.title);
|
|
1662
1908
|
violatedConstraints.push(msg);
|
|
@@ -1666,18 +1912,12 @@ function mapViolationsToResult(violations, summary) {
|
|
|
1666
1912
|
violatedConstraints,
|
|
1667
1913
|
recommendedFixes: []
|
|
1668
1914
|
};
|
|
1669
|
-
if (violatedConstraints.length === 0
|
|
1670
|
-
|
|
1671
|
-
{
|
|
1672
|
-
title: "Schedule could not be created",
|
|
1673
|
-
detail: summary
|
|
1674
|
-
}
|
|
1675
|
-
];
|
|
1676
|
-
} else if (violatedConstraints.length === 0) {
|
|
1915
|
+
if (violatedConstraints.length === 0) {
|
|
1916
|
+
const safeSummary = (summary == null ? void 0 : summary.trim()) && !isInternalViolationText(summary) ? summary.trim() : void 0;
|
|
1677
1917
|
result.violatedConstraints = [
|
|
1678
1918
|
{
|
|
1679
1919
|
title: "Schedule could not be created",
|
|
1680
|
-
detail: "Please adjust constraints and try again."
|
|
1920
|
+
detail: safeSummary != null ? safeSummary : "Please adjust constraints and try again."
|
|
1681
1921
|
}
|
|
1682
1922
|
];
|
|
1683
1923
|
}
|
|
@@ -1724,6 +1964,7 @@ function ViolatedConstraintsPanel({
|
|
|
1724
1964
|
return;
|
|
1725
1965
|
}
|
|
1726
1966
|
if (recommendations !== null) {
|
|
1967
|
+
setShowRecommendations(true);
|
|
1727
1968
|
return;
|
|
1728
1969
|
}
|
|
1729
1970
|
if (!canFetchRecommendations) {
|
|
@@ -1844,13 +2085,78 @@ function ViolatedConstraintsPanel({
|
|
|
1844
2085
|
] });
|
|
1845
2086
|
}
|
|
1846
2087
|
|
|
2088
|
+
// src/weekDates.ts
|
|
2089
|
+
function parseCalendarYmd(ymd) {
|
|
2090
|
+
const m = /^(\d{4})-(\d{2})-(\d{2})$/.exec(String(ymd).trim());
|
|
2091
|
+
if (!m) {
|
|
2092
|
+
return /* @__PURE__ */ new Date(NaN);
|
|
2093
|
+
}
|
|
2094
|
+
return new Date(Number(m[1]), Number(m[2]) - 1, Number(m[3]));
|
|
2095
|
+
}
|
|
2096
|
+
function formatCalendarYmd(d) {
|
|
2097
|
+
const y = d.getFullYear();
|
|
2098
|
+
const mo = String(d.getMonth() + 1).padStart(2, "0");
|
|
2099
|
+
const da = String(d.getDate()).padStart(2, "0");
|
|
2100
|
+
return `${y}-${mo}-${da}`;
|
|
2101
|
+
}
|
|
2102
|
+
function getSequentialWeekDatesFromWeekStart(weekStartYmd) {
|
|
2103
|
+
const base = parseCalendarYmd(weekStartYmd);
|
|
2104
|
+
if (Number.isNaN(base.getTime())) return [];
|
|
2105
|
+
const out = [];
|
|
2106
|
+
for (let i = 0; i < 7; i++) {
|
|
2107
|
+
const d = new Date(base.getFullYear(), base.getMonth(), base.getDate() + i);
|
|
2108
|
+
out.push(formatCalendarYmd(d));
|
|
2109
|
+
}
|
|
2110
|
+
return out;
|
|
2111
|
+
}
|
|
2112
|
+
function weekdayTwoLetterFromYmd(ymd) {
|
|
2113
|
+
var _a;
|
|
2114
|
+
const labels = ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"];
|
|
2115
|
+
const d = parseCalendarYmd(ymd);
|
|
2116
|
+
if (Number.isNaN(d.getTime())) return "?";
|
|
2117
|
+
return (_a = labels[d.getDay()]) != null ? _a : "?";
|
|
2118
|
+
}
|
|
2119
|
+
function defaultSelectedScheduleDates(orderedWeekYmds, todayYmd) {
|
|
2120
|
+
return orderedWeekYmds.filter((d) => d >= todayYmd);
|
|
2121
|
+
}
|
|
2122
|
+
function todayCalendarYmdInTimezone(timezone) {
|
|
2123
|
+
const fallback = formatCalendarYmd(/* @__PURE__ */ new Date());
|
|
2124
|
+
const tz = timezone == null ? void 0 : timezone.trim();
|
|
2125
|
+
if (!tz) return fallback;
|
|
2126
|
+
try {
|
|
2127
|
+
const parts = new Intl.DateTimeFormat("en-CA", {
|
|
2128
|
+
timeZone: tz,
|
|
2129
|
+
year: "numeric",
|
|
2130
|
+
month: "2-digit",
|
|
2131
|
+
day: "2-digit"
|
|
2132
|
+
}).formatToParts(/* @__PURE__ */ new Date());
|
|
2133
|
+
const valueByType = new Map(parts.map((part) => [part.type, part.value]));
|
|
2134
|
+
const year = valueByType.get("year");
|
|
2135
|
+
const month = valueByType.get("month");
|
|
2136
|
+
const day = valueByType.get("day");
|
|
2137
|
+
return year && month && day ? `${year}-${month}-${day}` : fallback;
|
|
2138
|
+
} catch {
|
|
2139
|
+
return fallback;
|
|
2140
|
+
}
|
|
2141
|
+
}
|
|
2142
|
+
|
|
1847
2143
|
// src/AutoSchedulerModal.tsx
|
|
1848
2144
|
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
2145
|
+
function resolveOptimizationFooter(footer, key) {
|
|
2146
|
+
if (!footer) return null;
|
|
2147
|
+
const node = typeof footer === "function" ? footer() : footer;
|
|
2148
|
+
if ((0, import_react3.isValidElement)(node)) return (0, import_react3.cloneElement)(node, { key });
|
|
2149
|
+
return node;
|
|
2150
|
+
}
|
|
2151
|
+
var DRAWER_WIDTH_MAP = { sm: "448px", md: "578px", lg: "600px" };
|
|
1849
2152
|
function AutoSchedulerModal({
|
|
2153
|
+
displayMode,
|
|
2154
|
+
drawerSize = "lg",
|
|
1850
2155
|
baseURL,
|
|
1851
2156
|
isOpen,
|
|
1852
2157
|
title = "Auto-scheduling",
|
|
1853
|
-
|
|
2158
|
+
bannerTitle = "Create a smarter retail schedule in seconds.",
|
|
2159
|
+
bannerText = "AI assigns open shifts using your rules, availability, and optimization priorities while keeping existing\n assigned shifts in place.",
|
|
1854
2160
|
jurisdictions,
|
|
1855
2161
|
openShifts,
|
|
1856
2162
|
employees,
|
|
@@ -1869,22 +2175,88 @@ function AutoSchedulerModal({
|
|
|
1869
2175
|
onSolution,
|
|
1870
2176
|
getStoredConfigForLocations,
|
|
1871
2177
|
persistConfigForLocations,
|
|
1872
|
-
errorToast
|
|
2178
|
+
errorToast,
|
|
2179
|
+
organizationId,
|
|
2180
|
+
constraintDefinitionIds,
|
|
2181
|
+
constraintDefinitions,
|
|
2182
|
+
hardConstraintOptions,
|
|
2183
|
+
softConstraintGroups,
|
|
2184
|
+
optimizationFooter,
|
|
2185
|
+
trafficForecast,
|
|
2186
|
+
builtinConstraintOptions,
|
|
2187
|
+
subLocations,
|
|
2188
|
+
showSubLocations,
|
|
2189
|
+
teams,
|
|
2190
|
+
showTeams,
|
|
2191
|
+
onSelectionChange,
|
|
2192
|
+
onGenerate,
|
|
2193
|
+
onGenerateSolution,
|
|
2194
|
+
solveStatus,
|
|
2195
|
+
scheduleWeekStartYmd,
|
|
2196
|
+
todayCalendarYmd,
|
|
2197
|
+
evaluationTimezone
|
|
1873
2198
|
}) {
|
|
1874
2199
|
const baseTheme = (0, import_react4.useTheme)();
|
|
1875
|
-
const { chakraOverride, styleOverrides } = (0, import_react3.useMemo)(
|
|
1876
|
-
() => resolveThemeParts(theme),
|
|
1877
|
-
[theme]
|
|
1878
|
-
);
|
|
2200
|
+
const { chakraOverride, styleOverrides } = (0, import_react3.useMemo)(() => resolveThemeParts(theme), [theme]);
|
|
1879
2201
|
const mergedTheme = (0, import_react3.useMemo)(
|
|
1880
2202
|
() => mergeThemeWithBase(baseTheme, chakraOverride),
|
|
1881
2203
|
[baseTheme, chakraOverride]
|
|
1882
2204
|
);
|
|
1883
2205
|
const styles = (0, import_react3.useMemo)(() => buildStyles(defaultStyles, styleOverrides), [styleOverrides]);
|
|
2206
|
+
const effectiveHardConstraintOptions = (0, import_react3.useMemo)(() => {
|
|
2207
|
+
const base = hardConstraintOptions != null ? hardConstraintOptions : HARD_CONSTRAINT_OPTIONS;
|
|
2208
|
+
return base.map((opt) => {
|
|
2209
|
+
var _a, _b, _c, _d;
|
|
2210
|
+
return {
|
|
2211
|
+
...opt,
|
|
2212
|
+
defaultValue: (_a = opt.defaultValue) != null ? _a : HARD_CONSTRAINT_DEFAULTS[opt.id],
|
|
2213
|
+
suffix: (_b = opt.suffix) != null ? _b : opt.id === "max_work_days_per_week" ? "days" : "hours",
|
|
2214
|
+
min: (_c = opt.min) != null ? _c : opt.id === "max_work_days_per_week" ? 1 : void 0,
|
|
2215
|
+
max: (_d = opt.max) != null ? _d : opt.id === "max_work_days_per_week" ? 6 : void 0
|
|
2216
|
+
};
|
|
2217
|
+
});
|
|
2218
|
+
}, [hardConstraintOptions]);
|
|
2219
|
+
const effectiveSoftConstraintGroups = (0, import_react3.useMemo)(
|
|
2220
|
+
() => softConstraintGroups != null ? softConstraintGroups : OPTIMIZATION_OPTION_GROUPS,
|
|
2221
|
+
[softConstraintGroups]
|
|
2222
|
+
);
|
|
2223
|
+
const effectiveBuiltinOptions = (0, import_react3.useMemo)(
|
|
2224
|
+
() => builtinConstraintOptions != null ? builtinConstraintOptions : [],
|
|
2225
|
+
[builtinConstraintOptions]
|
|
2226
|
+
);
|
|
2227
|
+
const defaultSelectedBuiltinIds = (0, import_react3.useMemo)(
|
|
2228
|
+
() => effectiveBuiltinOptions.filter((option) => option.defaultSelected).map((option) => option.id),
|
|
2229
|
+
[effectiveBuiltinOptions]
|
|
2230
|
+
);
|
|
2231
|
+
const dynamicViolationMessages = (0, import_react3.useMemo)(() => {
|
|
2232
|
+
const map = {};
|
|
2233
|
+
for (const opt of effectiveHardConstraintOptions) {
|
|
2234
|
+
const msg = { title: opt.name, detail: opt.description || opt.name };
|
|
2235
|
+
map[opt.id] = msg;
|
|
2236
|
+
const backendRuleId = toBackendRuleId(opt.id);
|
|
2237
|
+
if (backendRuleId) {
|
|
2238
|
+
map[backendRuleId] = msg;
|
|
2239
|
+
}
|
|
2240
|
+
}
|
|
2241
|
+
for (const group of effectiveSoftConstraintGroups) {
|
|
2242
|
+
for (const opt of group.options) {
|
|
2243
|
+
const msg = { title: opt.title, detail: opt.subtitle || opt.title };
|
|
2244
|
+
map[opt.id] = msg;
|
|
2245
|
+
const backendRuleId = toBackendRuleId(opt.id);
|
|
2246
|
+
if (backendRuleId) {
|
|
2247
|
+
map[backendRuleId] = msg;
|
|
2248
|
+
}
|
|
2249
|
+
}
|
|
2250
|
+
}
|
|
2251
|
+
return map;
|
|
2252
|
+
}, [effectiveHardConstraintOptions, effectiveSoftConstraintGroups]);
|
|
2253
|
+
const [selectedSubLocationIds, setSelectedSubLocationIds] = (0, import_react3.useState)([]);
|
|
2254
|
+
const [selectedTeamIds, setSelectedTeamIds] = (0, import_react3.useState)([]);
|
|
1884
2255
|
const [selectedConstraintIds, setSelectedConstraintIds] = (0, import_react3.useState)([]);
|
|
1885
2256
|
const [constraintValues, setConstraintValues] = (0, import_react3.useState)({});
|
|
1886
2257
|
const [selectedOptimizationIds, setSelectedOptimizationIds] = (0, import_react3.useState)([]);
|
|
1887
2258
|
const [optimizationValues, setOptimizationValues] = (0, import_react3.useState)({});
|
|
2259
|
+
const [selectedScheduleDates, setSelectedScheduleDates] = (0, import_react3.useState)([]);
|
|
1888
2260
|
const [screen, setScreen] = (0, import_react3.useState)("configure");
|
|
1889
2261
|
const [activeStepIndex, setActiveStepIndex] = (0, import_react3.useState)(0);
|
|
1890
2262
|
const [completedStepIndex, setCompletedStepIndex] = (0, import_react3.useState)(-1);
|
|
@@ -1893,12 +2265,19 @@ function AutoSchedulerModal({
|
|
|
1893
2265
|
const timersRef = (0, import_react3.useRef)([]);
|
|
1894
2266
|
const pollIntervalRef = (0, import_react3.useRef)(null);
|
|
1895
2267
|
const runIdRef = (0, import_react3.useRef)(0);
|
|
2268
|
+
const weekDatesOrdered = (0, import_react3.useMemo)(() => {
|
|
2269
|
+
if (!scheduleWeekStartYmd) return [];
|
|
2270
|
+
return getSequentialWeekDatesFromWeekStart(scheduleWeekStartYmd);
|
|
2271
|
+
}, [scheduleWeekStartYmd]);
|
|
2272
|
+
const toggleScheduleDate = (ymd) => {
|
|
2273
|
+
setSelectedScheduleDates(
|
|
2274
|
+
(prev) => prev.includes(ymd) ? prev.filter((d) => d !== ymd) : [...prev, ymd].sort()
|
|
2275
|
+
);
|
|
2276
|
+
};
|
|
1896
2277
|
const selectedByJurisdiction = (0, import_react3.useMemo)(
|
|
1897
2278
|
() => jurisdictions.map((jurisdiction) => ({
|
|
1898
2279
|
...jurisdiction,
|
|
1899
|
-
selectedLocations: jurisdiction.locations.filter(
|
|
1900
|
-
(location) => selectedLocationIds.includes(location.id)
|
|
1901
|
-
)
|
|
2280
|
+
selectedLocations: jurisdiction.locations.filter((location) => selectedLocationIds.includes(location.id))
|
|
1902
2281
|
})).filter((jurisdiction) => jurisdiction.selectedLocations.length > 0),
|
|
1903
2282
|
[jurisdictions, selectedLocationIds]
|
|
1904
2283
|
);
|
|
@@ -1907,43 +2286,84 @@ function AutoSchedulerModal({
|
|
|
1907
2286
|
0
|
|
1908
2287
|
);
|
|
1909
2288
|
const locationIdsWithOpenShifts = (0, import_react3.useMemo)(() => {
|
|
1910
|
-
return new Set(
|
|
1911
|
-
openShifts.filter((shift) => !shift.employeeId).map((shift) => shift.location.id)
|
|
1912
|
-
);
|
|
2289
|
+
return new Set(openShifts.filter((shift) => !shift.employeeId).map((shift) => shift.location.id));
|
|
1913
2290
|
}, [openShifts]);
|
|
2291
|
+
const filteredSubLocations = (0, import_react3.useMemo)(
|
|
2292
|
+
() => {
|
|
2293
|
+
var _a;
|
|
2294
|
+
return (_a = subLocations == null ? void 0 : subLocations.filter((sl) => selectedLocationIds.includes(sl.parentId))) != null ? _a : [];
|
|
2295
|
+
},
|
|
2296
|
+
[subLocations, selectedLocationIds]
|
|
2297
|
+
);
|
|
2298
|
+
const selectedLocationTimezone = (0, import_react3.useMemo)(() => {
|
|
2299
|
+
var _a, _b;
|
|
2300
|
+
const allLocations = jurisdictions.flatMap((jurisdiction) => jurisdiction.locations);
|
|
2301
|
+
for (const id of selectedLocationIds) {
|
|
2302
|
+
const timezone = (_b = (_a = allLocations.find((location) => location.id === id)) == null ? void 0 : _a.timezone) == null ? void 0 : _b.trim();
|
|
2303
|
+
if (timezone) return timezone;
|
|
2304
|
+
}
|
|
2305
|
+
return (evaluationTimezone == null ? void 0 : evaluationTimezone.trim()) || "UTC";
|
|
2306
|
+
}, [evaluationTimezone, jurisdictions, selectedLocationIds]);
|
|
2307
|
+
const effectiveTodayCalendarYmd = (0, import_react3.useMemo)(() => {
|
|
2308
|
+
return todayCalendarYmd != null ? todayCalendarYmd : todayCalendarYmdInTimezone(selectedLocationTimezone);
|
|
2309
|
+
}, [selectedLocationTimezone, todayCalendarYmd]);
|
|
1914
2310
|
const selectedConstraints = (0, import_react3.useMemo)(
|
|
1915
|
-
() =>
|
|
1916
|
-
[selectedConstraintIds]
|
|
2311
|
+
() => effectiveHardConstraintOptions.filter((option) => selectedConstraintIds.includes(option.id)),
|
|
2312
|
+
[effectiveHardConstraintOptions, selectedConstraintIds]
|
|
2313
|
+
);
|
|
2314
|
+
const selectedConstraintDisplayOptions = (0, import_react3.useMemo)(
|
|
2315
|
+
() => effectiveHardConstraintOptions.filter(
|
|
2316
|
+
(option) => selectedConstraintIds.includes(option.id) || option.readOnly && option.defaultSelected
|
|
2317
|
+
),
|
|
2318
|
+
[effectiveHardConstraintOptions, selectedConstraintIds]
|
|
1917
2319
|
);
|
|
1918
2320
|
const optimizationOptions = (0, import_react3.useMemo)(
|
|
1919
|
-
() =>
|
|
2321
|
+
() => effectiveSoftConstraintGroups.flatMap(
|
|
1920
2322
|
(group) => group.options.map((option) => ({
|
|
1921
2323
|
...option,
|
|
1922
2324
|
groupId: group.id,
|
|
1923
2325
|
groupName: group.name
|
|
1924
2326
|
}))
|
|
1925
2327
|
),
|
|
1926
|
-
[]
|
|
2328
|
+
[effectiveSoftConstraintGroups]
|
|
1927
2329
|
);
|
|
1928
2330
|
const selectedOptimizationOptions = (0, import_react3.useMemo)(
|
|
2331
|
+
() => optimizationOptions.filter((option) => selectedOptimizationIds.includes(option.id)),
|
|
2332
|
+
[optimizationOptions, selectedOptimizationIds]
|
|
2333
|
+
);
|
|
2334
|
+
const selectedOptimizationDisplayOptions = (0, import_react3.useMemo)(
|
|
1929
2335
|
() => optimizationOptions.filter(
|
|
1930
|
-
(option) => selectedOptimizationIds.includes(option.id)
|
|
2336
|
+
(option) => selectedOptimizationIds.includes(option.id) || option.readOnly && option.defaultSelected
|
|
1931
2337
|
),
|
|
1932
2338
|
[optimizationOptions, selectedOptimizationIds]
|
|
1933
2339
|
);
|
|
2340
|
+
const effectiveSelectedConstraintIds = (0, import_react3.useMemo)(
|
|
2341
|
+
() => Array.from(
|
|
2342
|
+
/* @__PURE__ */ new Set([
|
|
2343
|
+
...selectedConstraintIds,
|
|
2344
|
+
...effectiveHardConstraintOptions.filter((option) => option.readOnly && option.defaultSelected).map((option) => option.id)
|
|
2345
|
+
])
|
|
2346
|
+
),
|
|
2347
|
+
[selectedConstraintIds, effectiveHardConstraintOptions]
|
|
2348
|
+
);
|
|
2349
|
+
const effectiveSelectedOptimizationIds = (0, import_react3.useMemo)(
|
|
2350
|
+
() => Array.from(
|
|
2351
|
+
/* @__PURE__ */ new Set([
|
|
2352
|
+
...selectedOptimizationIds,
|
|
2353
|
+
...optimizationOptions.filter((option) => option.readOnly && option.defaultSelected).map((option) => option.id)
|
|
2354
|
+
])
|
|
2355
|
+
),
|
|
2356
|
+
[selectedOptimizationIds, optimizationOptions]
|
|
2357
|
+
);
|
|
1934
2358
|
const hasEmptyInputs = (0, import_react3.useMemo)(() => {
|
|
1935
|
-
const hasEmptyConstraint = selectedConstraints.some(
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
var _a;
|
|
1944
|
-
return !((_a = optimizationValues[`${opt.id}_weightPoints`]) == null ? void 0 : _a.trim());
|
|
1945
|
-
}
|
|
1946
|
-
);
|
|
2359
|
+
const hasEmptyConstraint = selectedConstraints.filter((opt) => opt.defaultValue != null).some((opt) => {
|
|
2360
|
+
var _a;
|
|
2361
|
+
return !((_a = constraintValues[opt.id]) == null ? void 0 : _a.trim());
|
|
2362
|
+
});
|
|
2363
|
+
const hasEmptyWeight = selectedOptimizationOptions.length >= 2 && selectedOptimizationOptions.some((opt) => {
|
|
2364
|
+
var _a;
|
|
2365
|
+
return !((_a = optimizationValues[`${opt.id}_weightPoints`]) == null ? void 0 : _a.trim());
|
|
2366
|
+
});
|
|
1947
2367
|
return hasEmptyConstraint || hasEmptyWeight;
|
|
1948
2368
|
}, [selectedConstraints, constraintValues, selectedOptimizationOptions, optimizationValues]);
|
|
1949
2369
|
const allocationSummary = (0, import_react3.useMemo)(() => {
|
|
@@ -1977,13 +2397,25 @@ function AutoSchedulerModal({
|
|
|
1977
2397
|
setScreen("configure");
|
|
1978
2398
|
setLastFailure(null);
|
|
1979
2399
|
setSelectedLocationIds([]);
|
|
2400
|
+
setSelectedSubLocationIds([]);
|
|
2401
|
+
setSelectedTeamIds([]);
|
|
1980
2402
|
setSelectedOptimizationIds([]);
|
|
1981
2403
|
setSelectedConstraintIds([]);
|
|
1982
2404
|
setConstraintValues({});
|
|
1983
2405
|
setOptimizationValues({});
|
|
2406
|
+
setSelectedScheduleDates([]);
|
|
1984
2407
|
resetProgress();
|
|
1985
2408
|
}
|
|
1986
2409
|
}, [isOpen]);
|
|
2410
|
+
(0, import_react3.useEffect)(() => {
|
|
2411
|
+
if (!isOpen || !scheduleWeekStartYmd) return;
|
|
2412
|
+
if (weekDatesOrdered.length === 0) {
|
|
2413
|
+
setSelectedScheduleDates([]);
|
|
2414
|
+
return;
|
|
2415
|
+
}
|
|
2416
|
+
const today = effectiveTodayCalendarYmd != null ? effectiveTodayCalendarYmd : weekDatesOrdered[0];
|
|
2417
|
+
setSelectedScheduleDates(defaultSelectedScheduleDates(weekDatesOrdered, today));
|
|
2418
|
+
}, [isOpen, scheduleWeekStartYmd, weekDatesOrdered, effectiveTodayCalendarYmd]);
|
|
1987
2419
|
(0, import_react3.useEffect)(() => {
|
|
1988
2420
|
if (isOpen && initialConfig) {
|
|
1989
2421
|
setSelectedLocationIds(initialConfig.selectedLocationIds);
|
|
@@ -2016,11 +2448,12 @@ function AutoSchedulerModal({
|
|
|
2016
2448
|
}, [selectedLocationIds, initialConfig, getStoredConfigForLocations]);
|
|
2017
2449
|
(0, import_react3.useEffect)(() => () => clearTimers(), []);
|
|
2018
2450
|
(0, import_react3.useEffect)(() => {
|
|
2451
|
+
if (onGenerate) return;
|
|
2019
2452
|
setSelectedLocationIds((previous) => {
|
|
2020
2453
|
const filtered = previous.filter((id) => locationIdsWithOpenShifts.has(id));
|
|
2021
2454
|
return filtered.length === previous.length ? previous : filtered;
|
|
2022
2455
|
});
|
|
2023
|
-
}, [locationIdsWithOpenShifts]);
|
|
2456
|
+
}, [locationIdsWithOpenShifts, onGenerate]);
|
|
2024
2457
|
const stepStatusForIndex = (index) => {
|
|
2025
2458
|
if (index <= completedStepIndex) {
|
|
2026
2459
|
return "done";
|
|
@@ -2048,144 +2481,249 @@ function AutoSchedulerModal({
|
|
|
2048
2481
|
timersRef.current.push(timerId);
|
|
2049
2482
|
};
|
|
2050
2483
|
const afterSuccessSteps = (runId, response) => {
|
|
2051
|
-
schedule(
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
setCompletedStepIndex(4);
|
|
2484
|
+
schedule(
|
|
2485
|
+
STEP_INTERVAL_MS,
|
|
2486
|
+
() => {
|
|
2487
|
+
if (runIdRef.current !== runId) return;
|
|
2488
|
+
setCompletedStepIndex(2);
|
|
2489
|
+
setActiveStepIndex(3);
|
|
2490
|
+
},
|
|
2491
|
+
runId
|
|
2492
|
+
);
|
|
2493
|
+
schedule(
|
|
2494
|
+
STEP_INTERVAL_MS * 2,
|
|
2495
|
+
() => {
|
|
2496
|
+
if (runIdRef.current !== runId) return;
|
|
2497
|
+
setCompletedStepIndex(3);
|
|
2066
2498
|
setActiveStepIndex(4);
|
|
2067
|
-
}
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2499
|
+
},
|
|
2500
|
+
runId
|
|
2501
|
+
);
|
|
2502
|
+
schedule(
|
|
2503
|
+
STEP_INTERVAL_MS * 3,
|
|
2504
|
+
async () => {
|
|
2505
|
+
if (runIdRef.current !== runId) return;
|
|
2506
|
+
try {
|
|
2507
|
+
await (onSolution == null ? void 0 : onSolution(response.solution));
|
|
2508
|
+
setCompletedStepIndex(4);
|
|
2509
|
+
setActiveStepIndex(4);
|
|
2510
|
+
} catch (err) {
|
|
2511
|
+
console.error(err);
|
|
2512
|
+
const defaultErrorMessage = "Failed to create schedule shifts";
|
|
2513
|
+
const detailedErrorMessage = err instanceof Error ? err.message || defaultErrorMessage : defaultErrorMessage;
|
|
2514
|
+
setLastFailure({
|
|
2515
|
+
violatedConstraints: [
|
|
2516
|
+
{
|
|
2517
|
+
title: "Something went wrong",
|
|
2518
|
+
detail: detailedErrorMessage
|
|
2519
|
+
}
|
|
2520
|
+
],
|
|
2521
|
+
recommendedFixes: []
|
|
2522
|
+
});
|
|
2523
|
+
setScreen("failed");
|
|
2524
|
+
}
|
|
2525
|
+
},
|
|
2526
|
+
runId
|
|
2527
|
+
);
|
|
2083
2528
|
};
|
|
2084
2529
|
const handleCreateSchedule = () => {
|
|
2085
2530
|
if (selectedCount === 0) {
|
|
2086
2531
|
return;
|
|
2087
2532
|
}
|
|
2533
|
+
if (scheduleWeekStartYmd && selectedScheduleDates.length === 0) {
|
|
2534
|
+
return;
|
|
2535
|
+
}
|
|
2536
|
+
if (onGenerate) {
|
|
2537
|
+
const runId2 = resetBeforeSubmit();
|
|
2538
|
+
schedule(
|
|
2539
|
+
STEP_INTERVAL_MS,
|
|
2540
|
+
async () => {
|
|
2541
|
+
if (runIdRef.current !== runId2) return;
|
|
2542
|
+
setCompletedStepIndex(0);
|
|
2543
|
+
setActiveStepIndex(1);
|
|
2544
|
+
try {
|
|
2545
|
+
const generateResult = await onGenerate({
|
|
2546
|
+
selectedLocationIds,
|
|
2547
|
+
selectedSubLocationIds,
|
|
2548
|
+
selectedTeamIds,
|
|
2549
|
+
schedulerTimezone: selectedLocationTimezone,
|
|
2550
|
+
selectedScheduleDates,
|
|
2551
|
+
selectedConstraintIds: effectiveSelectedConstraintIds,
|
|
2552
|
+
constraintValues,
|
|
2553
|
+
selectedOptimizationIds: effectiveSelectedOptimizationIds,
|
|
2554
|
+
optimizationValues,
|
|
2555
|
+
selectedBuiltinIds: defaultSelectedBuiltinIds
|
|
2556
|
+
});
|
|
2557
|
+
if (runIdRef.current !== runId2) return;
|
|
2558
|
+
setCompletedStepIndex(1);
|
|
2559
|
+
setActiveStepIndex(2);
|
|
2560
|
+
schedule(
|
|
2561
|
+
STEP_INTERVAL_MS,
|
|
2562
|
+
() => {
|
|
2563
|
+
if (runIdRef.current !== runId2) return;
|
|
2564
|
+
setCompletedStepIndex(2);
|
|
2565
|
+
setActiveStepIndex(3);
|
|
2566
|
+
},
|
|
2567
|
+
runId2
|
|
2568
|
+
);
|
|
2569
|
+
schedule(
|
|
2570
|
+
STEP_INTERVAL_MS * 2,
|
|
2571
|
+
() => {
|
|
2572
|
+
if (runIdRef.current !== runId2) return;
|
|
2573
|
+
setCompletedStepIndex(3);
|
|
2574
|
+
setActiveStepIndex(4);
|
|
2575
|
+
},
|
|
2576
|
+
runId2
|
|
2577
|
+
);
|
|
2578
|
+
schedule(
|
|
2579
|
+
STEP_INTERVAL_MS * 3,
|
|
2580
|
+
() => {
|
|
2581
|
+
if (runIdRef.current !== runId2) return;
|
|
2582
|
+
setCompletedStepIndex(4);
|
|
2583
|
+
setActiveStepIndex(4);
|
|
2584
|
+
onGenerateSolution == null ? void 0 : onGenerateSolution(generateResult);
|
|
2585
|
+
onClose();
|
|
2586
|
+
},
|
|
2587
|
+
runId2
|
|
2588
|
+
);
|
|
2589
|
+
} catch (err) {
|
|
2590
|
+
if (runIdRef.current !== runId2) return;
|
|
2591
|
+
if (err instanceof InfeasibleScheduleError) {
|
|
2592
|
+
setLastFailure(err.failure);
|
|
2593
|
+
} else {
|
|
2594
|
+
const msg = err instanceof Error ? err.message : "Unknown error";
|
|
2595
|
+
setLastFailure({
|
|
2596
|
+
violatedConstraints: [{ title: "Error", detail: msg }],
|
|
2597
|
+
recommendedFixes: []
|
|
2598
|
+
});
|
|
2599
|
+
}
|
|
2600
|
+
setScreen("failed");
|
|
2601
|
+
}
|
|
2602
|
+
},
|
|
2603
|
+
runId2
|
|
2604
|
+
);
|
|
2605
|
+
return;
|
|
2606
|
+
}
|
|
2088
2607
|
const payload = buildSchedulePayload({
|
|
2089
2608
|
employees,
|
|
2090
2609
|
openShifts,
|
|
2091
2610
|
timeOffs,
|
|
2092
2611
|
selectedLocationIds,
|
|
2093
2612
|
jurisdictions,
|
|
2094
|
-
selectedConstraintIds,
|
|
2613
|
+
selectedConstraintIds: effectiveSelectedConstraintIds,
|
|
2095
2614
|
constraintValues,
|
|
2096
|
-
selectedOptimizationIds,
|
|
2097
|
-
optimizationValues
|
|
2615
|
+
selectedOptimizationIds: effectiveSelectedOptimizationIds,
|
|
2616
|
+
optimizationValues,
|
|
2617
|
+
trafficForecast,
|
|
2618
|
+
constraintDefinitionIds,
|
|
2619
|
+
constraintDefinitions,
|
|
2620
|
+
organizationId
|
|
2098
2621
|
});
|
|
2099
2622
|
persistConfigForLocations == null ? void 0 : persistConfigForLocations(selectedLocationIds, {
|
|
2100
2623
|
selectedLocationIds,
|
|
2101
|
-
selectedConstraintIds,
|
|
2624
|
+
selectedConstraintIds: effectiveSelectedConstraintIds,
|
|
2102
2625
|
constraintValues,
|
|
2103
|
-
selectedOptimizationIds,
|
|
2104
|
-
optimizationValues
|
|
2626
|
+
selectedOptimizationIds: effectiveSelectedOptimizationIds,
|
|
2627
|
+
optimizationValues,
|
|
2628
|
+
selectedBuiltinIds: defaultSelectedBuiltinIds
|
|
2105
2629
|
});
|
|
2106
2630
|
const runId = resetBeforeSubmit();
|
|
2107
|
-
schedule(
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2631
|
+
schedule(
|
|
2632
|
+
STEP_INTERVAL_MS,
|
|
2633
|
+
async () => {
|
|
2634
|
+
setCompletedStepIndex(0);
|
|
2635
|
+
setActiveStepIndex(1);
|
|
2636
|
+
try {
|
|
2637
|
+
const token = await getToken(getTokenURLAndHeaders.url, getTokenURLAndHeaders.headers);
|
|
2638
|
+
if (!token) {
|
|
2639
|
+
throw new Error("Failed to get auto-scheduler token");
|
|
2640
|
+
}
|
|
2641
|
+
const selectedDates = [...selectedScheduleDates].sort();
|
|
2642
|
+
const horizonOptions = selectedDates.length > 0 ? {
|
|
2643
|
+
horizonStartDate: selectedDates[0],
|
|
2644
|
+
horizonEndDate: selectedDates[selectedDates.length - 1]
|
|
2645
|
+
} : {};
|
|
2646
|
+
const auth = { authorization: `Bearer ${token}`, ...horizonOptions };
|
|
2647
|
+
const { jobId } = await submitSolveJob(baseURL, payload, auth);
|
|
2648
|
+
if (runIdRef.current !== runId) {
|
|
2649
|
+
return;
|
|
2650
|
+
}
|
|
2651
|
+
setCompletedStepIndex(1);
|
|
2652
|
+
setActiveStepIndex(OPTIMIZING_STEP_INDEX);
|
|
2653
|
+
const pollForResult = async () => {
|
|
2126
2654
|
if (runIdRef.current !== runId) return;
|
|
2127
|
-
|
|
2655
|
+
try {
|
|
2656
|
+
const statusRes = await getJobStatus(baseURL, jobId, auth);
|
|
2657
|
+
if (runIdRef.current !== runId) return;
|
|
2658
|
+
if (statusRes.status === "NOT_SOLVING" || statusRes.status === "TERMINATED_EARLY") {
|
|
2659
|
+
if (pollIntervalRef.current) {
|
|
2660
|
+
clearInterval(pollIntervalRef.current);
|
|
2661
|
+
pollIntervalRef.current = null;
|
|
2662
|
+
}
|
|
2663
|
+
const response = await getJobResult(baseURL, jobId, auth);
|
|
2664
|
+
if (runIdRef.current !== runId) return;
|
|
2665
|
+
let responseForHandling = response;
|
|
2666
|
+
if (response.explanation.isFeasible) {
|
|
2667
|
+
const solution = await getJobSolution(baseURL, jobId, auth);
|
|
2668
|
+
if (runIdRef.current !== runId) return;
|
|
2669
|
+
responseForHandling = { ...response, solution };
|
|
2670
|
+
}
|
|
2671
|
+
handleSolveResult(runId, responseForHandling);
|
|
2672
|
+
return;
|
|
2673
|
+
}
|
|
2674
|
+
} catch (err) {
|
|
2675
|
+
if (runIdRef.current !== runId) return;
|
|
2128
2676
|
if (pollIntervalRef.current) {
|
|
2129
2677
|
clearInterval(pollIntervalRef.current);
|
|
2130
2678
|
pollIntervalRef.current = null;
|
|
2131
2679
|
}
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
if (response.explanation.isFeasible) {
|
|
2136
|
-
const solution = await getJobSolution(baseURL, jobId, auth);
|
|
2137
|
-
if (runIdRef.current !== runId) return;
|
|
2138
|
-
responseForHandling = { ...response, solution };
|
|
2139
|
-
}
|
|
2140
|
-
handleSolveResult(runId, responseForHandling);
|
|
2141
|
-
return;
|
|
2142
|
-
}
|
|
2143
|
-
} catch (err) {
|
|
2144
|
-
if (runIdRef.current !== runId) return;
|
|
2145
|
-
if (pollIntervalRef.current) {
|
|
2146
|
-
clearInterval(pollIntervalRef.current);
|
|
2147
|
-
pollIntervalRef.current = null;
|
|
2680
|
+
console.error(err);
|
|
2681
|
+
setLastFailure(NETWORK_ERROR_FAILURE);
|
|
2682
|
+
setScreen("failed");
|
|
2148
2683
|
}
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2684
|
+
};
|
|
2685
|
+
pollForResult();
|
|
2686
|
+
pollIntervalRef.current = setInterval(pollForResult, POLL_INTERVAL_MS);
|
|
2687
|
+
} catch (err) {
|
|
2688
|
+
if (runIdRef.current !== runId) {
|
|
2689
|
+
return;
|
|
2152
2690
|
}
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
} catch (err) {
|
|
2157
|
-
if (runIdRef.current !== runId) {
|
|
2158
|
-
return;
|
|
2691
|
+
console.error(err);
|
|
2692
|
+
setLastFailure(NETWORK_ERROR_FAILURE);
|
|
2693
|
+
setScreen("failed");
|
|
2159
2694
|
}
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
}
|
|
2164
|
-
}, runId);
|
|
2695
|
+
},
|
|
2696
|
+
runId
|
|
2697
|
+
);
|
|
2165
2698
|
};
|
|
2166
2699
|
const handleSolveResult = (runId, response) => {
|
|
2167
|
-
var _a
|
|
2700
|
+
var _a;
|
|
2168
2701
|
if (!response.explanation.isFeasible) {
|
|
2169
|
-
const result = mapViolationsToResult(
|
|
2170
|
-
|
|
2171
|
-
response.explanation.summary
|
|
2172
|
-
);
|
|
2173
|
-
const selectedLocations = jurisdictions.flatMap((j) => j.locations).filter(
|
|
2174
|
-
(l) => selectedLocationIds.includes(l.id)
|
|
2175
|
-
);
|
|
2702
|
+
const result = mapViolationsToResult((_a = response.explanation.violations) != null ? _a : [], response.explanation.summary, dynamicViolationMessages);
|
|
2703
|
+
const selectedLocations = jurisdictions.flatMap((j) => j.locations).filter((l) => selectedLocationIds.includes(l.id));
|
|
2176
2704
|
const recommendationPayload = generateRecommendationsURLAndHeaders ? {
|
|
2177
2705
|
context: {
|
|
2178
2706
|
selectedLocationIds,
|
|
2179
|
-
selectedConstraintIds,
|
|
2707
|
+
selectedConstraintIds: effectiveSelectedConstraintIds,
|
|
2180
2708
|
constraintValues,
|
|
2181
|
-
selectedOptimizationIds,
|
|
2709
|
+
selectedOptimizationIds: effectiveSelectedOptimizationIds,
|
|
2182
2710
|
optimizationValues,
|
|
2183
|
-
timezone:
|
|
2711
|
+
timezone: selectedLocationTimezone
|
|
2184
2712
|
},
|
|
2185
2713
|
locations: selectedLocations,
|
|
2186
2714
|
employees,
|
|
2187
|
-
openShifts: openShifts.filter(
|
|
2188
|
-
|
|
2715
|
+
openShifts: openShifts.filter(
|
|
2716
|
+
(s) => {
|
|
2717
|
+
var _a2;
|
|
2718
|
+
return !s.employeeId && selectedLocationIds.includes(String((_a2 = s.location) == null ? void 0 : _a2.id));
|
|
2719
|
+
}
|
|
2720
|
+
),
|
|
2721
|
+
assignedShifts: openShifts.filter(
|
|
2722
|
+
(s) => {
|
|
2723
|
+
var _a2;
|
|
2724
|
+
return Boolean(s.employeeId) && selectedLocationIds.includes(String((_a2 = s.location) == null ? void 0 : _a2.id));
|
|
2725
|
+
}
|
|
2726
|
+
),
|
|
2189
2727
|
timeOffs: timeOffs != null ? timeOffs : [],
|
|
2190
2728
|
explanation: response.explanation
|
|
2191
2729
|
} : void 0;
|
|
@@ -2204,17 +2742,34 @@ function AutoSchedulerModal({
|
|
|
2204
2742
|
setScreen("configure");
|
|
2205
2743
|
};
|
|
2206
2744
|
const handleToggleLocation = (locationId) => {
|
|
2207
|
-
if (!locationIdsWithOpenShifts.has(locationId)) {
|
|
2745
|
+
if (!onGenerate && !locationIdsWithOpenShifts.has(locationId)) {
|
|
2208
2746
|
return;
|
|
2209
2747
|
}
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2748
|
+
const newLocationIds = selectedLocationIds.includes(locationId) ? selectedLocationIds.filter((id) => id !== locationId) : [...selectedLocationIds, locationId];
|
|
2749
|
+
setSelectedLocationIds(newLocationIds);
|
|
2750
|
+
const filteredSubs = selectedSubLocationIds.filter((subId) => {
|
|
2751
|
+
const sub = subLocations == null ? void 0 : subLocations.find((s) => s.id === subId);
|
|
2752
|
+
return sub ? newLocationIds.includes(sub.parentId) : false;
|
|
2753
|
+
});
|
|
2754
|
+
if (filteredSubs.length !== selectedSubLocationIds.length) {
|
|
2755
|
+
setSelectedSubLocationIds(filteredSubs);
|
|
2213
2756
|
}
|
|
2214
|
-
|
|
2757
|
+
onSelectionChange == null ? void 0 : onSelectionChange(
|
|
2758
|
+
newLocationIds,
|
|
2759
|
+
filteredSubs.length !== selectedSubLocationIds.length ? filteredSubs : selectedSubLocationIds
|
|
2760
|
+
);
|
|
2761
|
+
};
|
|
2762
|
+
const handleToggleSubLocation = (subLocationId) => {
|
|
2763
|
+
const next = selectedSubLocationIds.includes(subLocationId) ? selectedSubLocationIds.filter((id) => id !== subLocationId) : [...selectedSubLocationIds, subLocationId];
|
|
2764
|
+
setSelectedSubLocationIds(next);
|
|
2765
|
+
onSelectionChange == null ? void 0 : onSelectionChange(selectedLocationIds, next);
|
|
2766
|
+
};
|
|
2767
|
+
const handleToggleTeam = (teamId) => {
|
|
2768
|
+
setSelectedTeamIds((prev) => prev.includes(teamId) ? prev.filter((id) => id !== teamId) : [...prev, teamId]);
|
|
2215
2769
|
};
|
|
2216
2770
|
const handleToggleConstraint = (constraintId) => {
|
|
2217
2771
|
setSelectedConstraintIds((prev) => {
|
|
2772
|
+
var _a;
|
|
2218
2773
|
if (prev.includes(constraintId)) {
|
|
2219
2774
|
setConstraintValues((values) => {
|
|
2220
2775
|
const next = { ...values };
|
|
@@ -2223,10 +2778,11 @@ function AutoSchedulerModal({
|
|
|
2223
2778
|
});
|
|
2224
2779
|
return prev.filter((id) => id !== constraintId);
|
|
2225
2780
|
}
|
|
2226
|
-
|
|
2781
|
+
const defaultVal = (_a = effectiveHardConstraintOptions.find((o) => o.id === constraintId)) == null ? void 0 : _a.defaultValue;
|
|
2782
|
+
if (defaultVal) {
|
|
2227
2783
|
setConstraintValues((values) => ({
|
|
2228
2784
|
...values,
|
|
2229
|
-
[constraintId]:
|
|
2785
|
+
[constraintId]: defaultVal
|
|
2230
2786
|
}));
|
|
2231
2787
|
}
|
|
2232
2788
|
return [...prev, constraintId];
|
|
@@ -2278,12 +2834,12 @@ function AutoSchedulerModal({
|
|
|
2278
2834
|
const configurationContent = /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react4.Stack, { spacing: "20px", children: [
|
|
2279
2835
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react4.Box, { sx: styles.banner, children: [
|
|
2280
2836
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Box, { sx: styles.bannerIcon, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(MagicIcon, {}) }),
|
|
2281
|
-
|
|
2282
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Text, { sx: styles.bannerTitle, children:
|
|
2283
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Text, { sx: styles.bannerText, children:
|
|
2837
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react4.Stack, { spacing: "4px", flex: 1, children: [
|
|
2838
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Text, { sx: styles.bannerTitle, children: bannerTitle }),
|
|
2839
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Text, { sx: styles.bannerText, children: bannerText })
|
|
2284
2840
|
] })
|
|
2285
2841
|
] }),
|
|
2286
|
-
openShifts.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
2842
|
+
!onGenerate && openShifts.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
2287
2843
|
import_react4.Box,
|
|
2288
2844
|
{
|
|
2289
2845
|
sx: {
|
|
@@ -2326,30 +2882,33 @@ function AutoSchedulerModal({
|
|
|
2326
2882
|
] }) }) }),
|
|
2327
2883
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.PopoverContent, { sx: styles.popoverContent, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.PopoverBody, { p: 0, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Stack, { spacing: "4px", children: jurisdictions.map((jurisdiction) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react4.Box, { children: [
|
|
2328
2884
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Text, { sx: styles.groupHeader, children: jurisdiction.title }),
|
|
2329
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Stack, { spacing: "2px", children: jurisdiction.locations.map((location) =>
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2885
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Stack, { spacing: "2px", children: jurisdiction.locations.map((location) => {
|
|
2886
|
+
const isEnabled = onGenerate || locationIdsWithOpenShifts.has(location.id);
|
|
2887
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
2888
|
+
import_react4.HStack,
|
|
2889
|
+
{
|
|
2890
|
+
as: "label",
|
|
2891
|
+
sx: {
|
|
2892
|
+
...styles.optionRow,
|
|
2893
|
+
cursor: isEnabled ? "pointer" : "not-allowed",
|
|
2894
|
+
opacity: isEnabled ? 1 : 0.55
|
|
2895
|
+
},
|
|
2896
|
+
children: [
|
|
2897
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
2898
|
+
import_react4.Checkbox,
|
|
2899
|
+
{
|
|
2900
|
+
id: `location-${location.id}`,
|
|
2901
|
+
isChecked: selectedLocationIds.includes(location.id),
|
|
2902
|
+
isDisabled: !isEnabled,
|
|
2903
|
+
onChange: () => handleToggleLocation(location.id)
|
|
2904
|
+
}
|
|
2905
|
+
),
|
|
2906
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Text, { sx: styles.optionLabel, children: location.name })
|
|
2907
|
+
]
|
|
2337
2908
|
},
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
{
|
|
2342
|
-
id: `location-${location.id}`,
|
|
2343
|
-
isChecked: selectedLocationIds.includes(location.id),
|
|
2344
|
-
isDisabled: !locationIdsWithOpenShifts.has(location.id),
|
|
2345
|
-
onChange: () => handleToggleLocation(location.id)
|
|
2346
|
-
}
|
|
2347
|
-
),
|
|
2348
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Text, { sx: styles.optionLabel, children: location.name })
|
|
2349
|
-
]
|
|
2350
|
-
},
|
|
2351
|
-
location.id
|
|
2352
|
-
)) })
|
|
2909
|
+
location.id
|
|
2910
|
+
);
|
|
2911
|
+
}) })
|
|
2353
2912
|
] }, jurisdiction.id)) }) }) })
|
|
2354
2913
|
] }),
|
|
2355
2914
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Stack, { spacing: "8px", children: selectedByJurisdiction.map((jurisdiction) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react4.Box, { sx: styles.selectedBanner, children: [
|
|
@@ -2363,11 +2922,128 @@ function AutoSchedulerModal({
|
|
|
2363
2922
|
] })
|
|
2364
2923
|
] }, jurisdiction.id)) })
|
|
2365
2924
|
] }),
|
|
2925
|
+
showSubLocations && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react4.Stack, { spacing: "8px", sx: styles.locationSection, children: [
|
|
2926
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Stack, { spacing: "4px", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react4.HStack, { sx: styles.locationLabelRow, children: [
|
|
2927
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Text, { sx: styles.locationLabel, children: "Sub-location" }),
|
|
2928
|
+
selectedSubLocationIds.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Badge, { sx: styles.locationBadge, children: selectedSubLocationIds.length })
|
|
2929
|
+
] }) }),
|
|
2930
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react4.Popover, { placement: "bottom-start", matchWidth: true, isLazy: true, children: [
|
|
2931
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.PopoverTrigger, { children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
2932
|
+
import_react4.Button,
|
|
2933
|
+
{
|
|
2934
|
+
variant: "unstyled",
|
|
2935
|
+
isDisabled: selectedLocationIds.length === 0 || filteredSubLocations.length === 0,
|
|
2936
|
+
sx: {
|
|
2937
|
+
...styles.locationTrigger,
|
|
2938
|
+
...selectedLocationIds.length === 0 || filteredSubLocations.length === 0 ? { opacity: 0.5, cursor: "not-allowed" } : {}
|
|
2939
|
+
},
|
|
2940
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react4.HStack, { justify: "space-between", w: "full", children: [
|
|
2941
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
2942
|
+
import_react4.Text,
|
|
2943
|
+
{
|
|
2944
|
+
sx: {
|
|
2945
|
+
...styles.locationTriggerText,
|
|
2946
|
+
...selectedSubLocationIds.length > 0 ? { color: "#303030" } : {}
|
|
2947
|
+
},
|
|
2948
|
+
noOfLines: 1,
|
|
2949
|
+
children: selectedSubLocationIds.length > 0 ? filteredSubLocations.filter((s) => selectedSubLocationIds.includes(s.id)).map((s) => s.name).join(", ") : "Select sub-locations"
|
|
2950
|
+
}
|
|
2951
|
+
),
|
|
2952
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Box, { color: "#5c5c5c", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ChevronDownIcon, {}) })
|
|
2953
|
+
] })
|
|
2954
|
+
}
|
|
2955
|
+
) }),
|
|
2956
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.PopoverContent, { sx: styles.popoverContent, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.PopoverBody, { p: 0, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Stack, { spacing: "2px", children: filteredSubLocations.map((sub) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react4.HStack, { as: "label", sx: { ...styles.optionRow, cursor: "pointer" }, children: [
|
|
2957
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
2958
|
+
import_react4.Checkbox,
|
|
2959
|
+
{
|
|
2960
|
+
id: `sublocation-${sub.id}`,
|
|
2961
|
+
isChecked: selectedSubLocationIds.includes(sub.id),
|
|
2962
|
+
onChange: () => handleToggleSubLocation(sub.id)
|
|
2963
|
+
}
|
|
2964
|
+
),
|
|
2965
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Text, { sx: styles.optionLabel, children: sub.name })
|
|
2966
|
+
] }, sub.id)) }) }) })
|
|
2967
|
+
] })
|
|
2968
|
+
] }),
|
|
2969
|
+
showTeams && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react4.Stack, { spacing: "8px", sx: styles.locationSection, children: [
|
|
2970
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Stack, { spacing: "4px", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react4.HStack, { sx: styles.locationLabelRow, children: [
|
|
2971
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Text, { sx: styles.locationLabel, children: "Teams" }),
|
|
2972
|
+
selectedTeamIds.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Badge, { sx: styles.locationBadge, children: selectedTeamIds.length })
|
|
2973
|
+
] }) }),
|
|
2974
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react4.Popover, { placement: "bottom-start", matchWidth: true, isLazy: true, children: [
|
|
2975
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.PopoverTrigger, { children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
2976
|
+
import_react4.Button,
|
|
2977
|
+
{
|
|
2978
|
+
variant: "unstyled",
|
|
2979
|
+
isDisabled: !teams || teams.length === 0,
|
|
2980
|
+
sx: {
|
|
2981
|
+
...styles.locationTrigger,
|
|
2982
|
+
...!teams || teams.length === 0 ? { opacity: 0.5, cursor: "not-allowed" } : {}
|
|
2983
|
+
},
|
|
2984
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react4.HStack, { justify: "space-between", w: "full", children: [
|
|
2985
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
2986
|
+
import_react4.Text,
|
|
2987
|
+
{
|
|
2988
|
+
sx: {
|
|
2989
|
+
...styles.locationTriggerText,
|
|
2990
|
+
...selectedTeamIds.length > 0 ? { color: "#303030" } : {}
|
|
2991
|
+
},
|
|
2992
|
+
noOfLines: 1,
|
|
2993
|
+
children: selectedTeamIds.length > 0 ? (teams != null ? teams : []).filter((t) => selectedTeamIds.includes(t.id)).map((t) => t.name).join(", ") : "Select teams"
|
|
2994
|
+
}
|
|
2995
|
+
),
|
|
2996
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Box, { color: "#5c5c5c", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ChevronDownIcon, {}) })
|
|
2997
|
+
] })
|
|
2998
|
+
}
|
|
2999
|
+
) }),
|
|
3000
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.PopoverContent, { sx: styles.popoverContent, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.PopoverBody, { p: 0, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Stack, { spacing: "2px", children: (teams != null ? teams : []).map((team) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react4.HStack, { as: "label", sx: { ...styles.optionRow, cursor: "pointer" }, children: [
|
|
3001
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
3002
|
+
import_react4.Checkbox,
|
|
3003
|
+
{
|
|
3004
|
+
id: `team-${team.id}`,
|
|
3005
|
+
isChecked: selectedTeamIds.includes(team.id),
|
|
3006
|
+
onChange: () => handleToggleTeam(team.id)
|
|
3007
|
+
}
|
|
3008
|
+
),
|
|
3009
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Text, { sx: styles.optionLabel, children: team.name })
|
|
3010
|
+
] }, team.id)) }) }) })
|
|
3011
|
+
] })
|
|
3012
|
+
] }),
|
|
3013
|
+
scheduleWeekStartYmd ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react4.Stack, { spacing: "8px", sx: styles.locationSection, children: [
|
|
3014
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Text, { sx: { ...styles.locationLabel, fontWeight: 600 }, children: "Generate schedule for:" }),
|
|
3015
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.HStack, { spacing: 2, flexWrap: "wrap", children: weekDatesOrdered.map((ymd) => {
|
|
3016
|
+
const selected = selectedScheduleDates.includes(ymd);
|
|
3017
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
3018
|
+
import_react4.Button,
|
|
3019
|
+
{
|
|
3020
|
+
type: "button",
|
|
3021
|
+
variant: "unstyled",
|
|
3022
|
+
onClick: () => toggleScheduleDate(ymd),
|
|
3023
|
+
borderRadius: "full",
|
|
3024
|
+
minW: "40px",
|
|
3025
|
+
h: "40px",
|
|
3026
|
+
px: 0,
|
|
3027
|
+
fontSize: "sm",
|
|
3028
|
+
fontWeight: 600,
|
|
3029
|
+
borderWidth: selected ? "2px" : "1px",
|
|
3030
|
+
borderStyle: "solid",
|
|
3031
|
+
borderColor: selected ? "#5b44ff" : "#e2e8f0",
|
|
3032
|
+
bg: selected ? "#ede9fe" : "#ffffff",
|
|
3033
|
+
color: selected ? "#5b44ff" : "#303030",
|
|
3034
|
+
"aria-pressed": selected,
|
|
3035
|
+
"aria-label": `${weekdayTwoLetterFromYmd(ymd)} ${ymd}`,
|
|
3036
|
+
children: weekdayTwoLetterFromYmd(ymd)
|
|
3037
|
+
},
|
|
3038
|
+
ymd
|
|
3039
|
+
);
|
|
3040
|
+
}) })
|
|
3041
|
+
] }) : null,
|
|
2366
3042
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react4.Stack, { spacing: "16px", sx: styles.hardConstraintsSection, children: [
|
|
2367
3043
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react4.Stack, { spacing: "4px", children: [
|
|
2368
3044
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react4.HStack, { sx: styles.hardConstraintsLabelRow, children: [
|
|
2369
3045
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Text, { sx: styles.hardConstraintsLabel, children: "Required rules" }),
|
|
2370
|
-
|
|
3046
|
+
selectedConstraintDisplayOptions.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Badge, { sx: styles.hardConstraintsBadge, children: selectedConstraintDisplayOptions.length }) : null
|
|
2371
3047
|
] }),
|
|
2372
3048
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Text, { sx: styles.hardConstraintsSubtitle, children: "Assigned shifts will be required to meet the following rules:" })
|
|
2373
3049
|
] }),
|
|
@@ -2379,35 +3055,30 @@ function AutoSchedulerModal({
|
|
|
2379
3055
|
{
|
|
2380
3056
|
sx: {
|
|
2381
3057
|
...styles.hardConstraintsTriggerText,
|
|
2382
|
-
...
|
|
3058
|
+
...selectedConstraintDisplayOptions.length > 0 ? { color: "#303030" } : {}
|
|
2383
3059
|
},
|
|
2384
3060
|
noOfLines: 1,
|
|
2385
|
-
children:
|
|
3061
|
+
children: selectedConstraintDisplayOptions.length > 0 ? selectedConstraintDisplayOptions.map((o) => o.name).join(", ") : "Select rules"
|
|
2386
3062
|
}
|
|
2387
3063
|
),
|
|
2388
3064
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Box, { color: "#5c5c5c", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ChevronDownIcon, {}) })
|
|
2389
3065
|
] }) }) }),
|
|
2390
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.PopoverContent, { sx: styles.hardConstraintsPopoverContent, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.PopoverBody, { p: 0, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Stack, { spacing: "4px", children:
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Text, { sx: styles.hardConstraintsOptionLabel, children: option.name })
|
|
2405
|
-
]
|
|
2406
|
-
},
|
|
2407
|
-
option.id
|
|
2408
|
-
)) }) }) })
|
|
3066
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.PopoverContent, { sx: styles.hardConstraintsPopoverContent, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.PopoverBody, { p: 0, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Stack, { spacing: "4px", children: effectiveHardConstraintOptions.map((option) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react4.HStack, { as: "label", sx: { ...styles.hardConstraintsOptionRow, cursor: "pointer" }, children: [
|
|
3067
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
3068
|
+
import_react4.Checkbox,
|
|
3069
|
+
{
|
|
3070
|
+
id: `constraint-${option.id}`,
|
|
3071
|
+
isChecked: selectedConstraintIds.includes(option.id) || Boolean(option.readOnly && option.defaultSelected),
|
|
3072
|
+
isDisabled: option.readOnly,
|
|
3073
|
+
onChange: () => {
|
|
3074
|
+
if (!option.readOnly) handleToggleConstraint(option.id);
|
|
3075
|
+
}
|
|
3076
|
+
}
|
|
3077
|
+
),
|
|
3078
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Text, { sx: styles.hardConstraintsOptionLabel, children: option.name })
|
|
3079
|
+
] }, option.id)) }) }) })
|
|
2409
3080
|
] }),
|
|
2410
|
-
selectedConstraints.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Box, { sx: styles.hardConstraintsInputsContainer, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Stack, { spacing: "16px", children: selectedConstraints.map((option, index) => {
|
|
3081
|
+
selectedConstraints.filter((o) => o.defaultValue != null).length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Box, { sx: styles.hardConstraintsInputsContainer, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Stack, { spacing: "16px", children: selectedConstraints.filter((o) => o.defaultValue != null).map((option, index) => {
|
|
2411
3082
|
var _a;
|
|
2412
3083
|
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react4.Stack, { sx: styles.hardConstraintsInputItem, children: [
|
|
2413
3084
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react4.Text, { sx: styles.hardConstraintsInputLabel, children: [
|
|
@@ -2425,13 +3096,13 @@ function AutoSchedulerModal({
|
|
|
2425
3096
|
...prev,
|
|
2426
3097
|
[option.id]: event.target.value
|
|
2427
3098
|
})),
|
|
2428
|
-
min: option.
|
|
2429
|
-
max: option.
|
|
2430
|
-
step: option.
|
|
3099
|
+
min: option.min,
|
|
3100
|
+
max: option.max,
|
|
3101
|
+
step: option.max != null ? void 0 : "any",
|
|
2431
3102
|
sx: styles.hardConstraintsInputField
|
|
2432
3103
|
}
|
|
2433
3104
|
),
|
|
2434
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.InputRightAddon, { sx: styles.optimizationSuffix, children: option.
|
|
3105
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.InputRightAddon, { sx: styles.optimizationSuffix, children: option.suffix })
|
|
2435
3106
|
] })
|
|
2436
3107
|
] }, option.id);
|
|
2437
3108
|
}) }) }) : null
|
|
@@ -2440,89 +3111,100 @@ function AutoSchedulerModal({
|
|
|
2440
3111
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react4.Stack, { spacing: "4px", children: [
|
|
2441
3112
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react4.HStack, { sx: styles.optimizationLabelRow, children: [
|
|
2442
3113
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Text, { sx: styles.optimizationLabel, children: "Optimization" }),
|
|
2443
|
-
|
|
3114
|
+
selectedOptimizationDisplayOptions.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Badge, { sx: styles.optimizationBadge, children: selectedOptimizationDisplayOptions.length }) : null
|
|
2444
3115
|
] }),
|
|
2445
3116
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Text, { sx: styles.optimizationSubtitle, children: "Your schedule will be optimized based on the following priorities:" })
|
|
2446
3117
|
] }),
|
|
2447
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react4.Popover, { placement: "
|
|
3118
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react4.Popover, { placement: "top-start", strategy: "fixed", matchWidth: true, isLazy: true, gutter: 4, children: [
|
|
2448
3119
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.PopoverTrigger, { children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Button, { variant: "unstyled", sx: styles.optimizationTrigger, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react4.HStack, { justify: "space-between", w: "full", children: [
|
|
2449
3120
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
2450
3121
|
import_react4.Text,
|
|
2451
3122
|
{
|
|
2452
3123
|
sx: {
|
|
2453
3124
|
...styles.optimizationTriggerText,
|
|
2454
|
-
...
|
|
3125
|
+
...selectedOptimizationDisplayOptions.length > 0 ? { color: "#303030" } : {}
|
|
2455
3126
|
},
|
|
2456
3127
|
noOfLines: 1,
|
|
2457
|
-
children:
|
|
3128
|
+
children: selectedOptimizationDisplayOptions.length > 0 ? selectedOptimizationDisplayOptions.map((o) => o.title).join(", ") : "Select optimization priorities"
|
|
2458
3129
|
}
|
|
2459
3130
|
),
|
|
2460
3131
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Box, { color: "#5c5c5c", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ChevronDownIcon, {}) })
|
|
2461
3132
|
] }) }) }),
|
|
2462
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
|
|
2471
|
-
|
|
2472
|
-
|
|
2473
|
-
import_react4.Checkbox,
|
|
2474
|
-
{
|
|
2475
|
-
id: `optimization-${option.id}`,
|
|
2476
|
-
isChecked: selectedOptimizationIds.includes(option.id),
|
|
2477
|
-
isDisabled: !selectedOptimizationIds.includes(option.id) && selectedOptimizationIds.length >= 3,
|
|
2478
|
-
onChange: () => handleToggleOptimization(option.id)
|
|
2479
|
-
}
|
|
2480
|
-
),
|
|
2481
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Text, { sx: styles.optimizationOptionLabel, children: option.title })
|
|
2482
|
-
] }),
|
|
2483
|
-
option.subtitle ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Text, { sx: styles.optimizationOptionHelp, children: option.subtitle }) : null
|
|
2484
|
-
]
|
|
3133
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Portal, { appendToParentPortal: false, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
3134
|
+
import_react4.PopoverContent,
|
|
3135
|
+
{
|
|
3136
|
+
sx: {
|
|
3137
|
+
...styles.optimizationPopoverContent,
|
|
3138
|
+
maxH: "min(560px, calc(100vh - 96px))",
|
|
3139
|
+
overflowY: "auto",
|
|
3140
|
+
zIndex: 1500,
|
|
3141
|
+
scrollbarWidth: "none",
|
|
3142
|
+
msOverflowStyle: "none",
|
|
3143
|
+
"&::-webkit-scrollbar": { display: "none" }
|
|
2485
3144
|
},
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
3145
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.PopoverBody, { p: 0, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Stack, { spacing: "4px", children: effectiveSoftConstraintGroups.map((group) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react4.Box, { children: [
|
|
3146
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Text, { sx: styles.optimizationGroupHeader, children: group.name }),
|
|
3147
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Stack, { spacing: "2px", children: group.options.map((option) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react4.Box, { children: [
|
|
3148
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
3149
|
+
import_react4.Stack,
|
|
3150
|
+
{
|
|
3151
|
+
as: "label",
|
|
3152
|
+
spacing: "2px",
|
|
3153
|
+
sx: { ...styles.optimizationOptionRow, cursor: "pointer" },
|
|
3154
|
+
children: [
|
|
3155
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react4.HStack, { spacing: "8px", children: [
|
|
3156
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
3157
|
+
import_react4.Checkbox,
|
|
3158
|
+
{
|
|
3159
|
+
id: `optimization-${option.id}`,
|
|
3160
|
+
isChecked: selectedOptimizationIds.includes(option.id) || Boolean(option.readOnly && option.defaultSelected),
|
|
3161
|
+
isDisabled: option.readOnly || !selectedOptimizationIds.includes(option.id) && selectedOptimizationIds.length >= 3,
|
|
3162
|
+
onChange: () => {
|
|
3163
|
+
if (!option.readOnly) handleToggleOptimization(option.id);
|
|
3164
|
+
}
|
|
3165
|
+
}
|
|
3166
|
+
),
|
|
3167
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Text, { sx: styles.optimizationOptionLabel, children: option.title })
|
|
3168
|
+
] }),
|
|
3169
|
+
option.subtitle ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Text, { sx: styles.optimizationOptionHelp, children: option.subtitle }) : null
|
|
3170
|
+
]
|
|
3171
|
+
}
|
|
3172
|
+
),
|
|
3173
|
+
optimizationFooter && (option.id === "cost_to_sales_ratio" || option.id === "maximize_sales") ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Box, { px: "12px", pt: "4px", pb: "6px", children: resolveOptimizationFooter(optimizationFooter, option.id) }) : null
|
|
3174
|
+
] }, option.id)) })
|
|
3175
|
+
] }, group.id)) }) })
|
|
3176
|
+
}
|
|
3177
|
+
) })
|
|
2489
3178
|
] }),
|
|
2490
3179
|
selectedOptimizationOptions.length >= 2 ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react4.Box, { sx: styles.optimizationInputsContainer, children: [
|
|
2491
3180
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Stack, { spacing: "16px", children: selectedOptimizationOptions.map((option, index) => {
|
|
2492
3181
|
var _a;
|
|
2493
|
-
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
2494
|
-
import_react4.
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
|
|
2518
|
-
|
|
2519
|
-
),
|
|
2520
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.InputRightAddon, { sx: styles.optimizationSuffix, children: "points" })
|
|
2521
|
-
] })
|
|
2522
|
-
] })
|
|
2523
|
-
},
|
|
2524
|
-
option.id
|
|
2525
|
-
);
|
|
3182
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Stack, { sx: styles.optimizationInputItem, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react4.Stack, { sx: styles.hardConstraintsInputItem, children: [
|
|
3183
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react4.Text, { sx: styles.hardConstraintsInputLabel, children: [
|
|
3184
|
+
index + 1,
|
|
3185
|
+
". ",
|
|
3186
|
+
option.title
|
|
3187
|
+
] }),
|
|
3188
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react4.InputGroup, { children: [
|
|
3189
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
3190
|
+
import_react4.Input,
|
|
3191
|
+
{
|
|
3192
|
+
type: "text",
|
|
3193
|
+
value: (_a = optimizationValues[`${option.id}_weightPoints`]) != null ? _a : "1",
|
|
3194
|
+
onChange: (e) => {
|
|
3195
|
+
const val = e.target.value;
|
|
3196
|
+
if (val !== "" && (!/^\d+$/.test(val) || Number(val) < 1)) return;
|
|
3197
|
+
setOptimizationValues((prev) => ({
|
|
3198
|
+
...prev,
|
|
3199
|
+
[`${option.id}_weightPoints`]: val
|
|
3200
|
+
}));
|
|
3201
|
+
},
|
|
3202
|
+
sx: styles.hardConstraintsInputField
|
|
3203
|
+
}
|
|
3204
|
+
),
|
|
3205
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.InputRightAddon, { sx: styles.optimizationSuffix, children: "points" })
|
|
3206
|
+
] })
|
|
3207
|
+
] }) }, option.id);
|
|
2526
3208
|
}) }),
|
|
2527
3209
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Divider, { sx: { ...styles.optimizationDivider, my: "16px" } }),
|
|
2528
3210
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Text, { sx: styles.optimizationSummary, children: allocationSummary })
|
|
@@ -2531,7 +3213,9 @@ function AutoSchedulerModal({
|
|
|
2531
3213
|
] });
|
|
2532
3214
|
const stepsContent = /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react4.Stack, { spacing: "16px", sx: styles.stepsContainer, children: [
|
|
2533
3215
|
showFailed ? violationPanel : null,
|
|
2534
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Stack, { spacing: "14px", sx: styles.stepsList, children: SCHEDULE_STEPS.map((
|
|
3216
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Stack, { spacing: "14px", sx: styles.stepsList, children: SCHEDULE_STEPS.map((rawStep, index) => {
|
|
3217
|
+
const isQueueWait = index === 1 && solveStatus === "QUEUED";
|
|
3218
|
+
const step = isQueueWait ? { title: "Waiting in queue\u2026", subtitle: void 0 } : rawStep;
|
|
2535
3219
|
const status = stepStatusForIndex(index);
|
|
2536
3220
|
const isFailedStep = showFailed && index === activeStepIndex;
|
|
2537
3221
|
const iconStyles = {
|
|
@@ -2549,26 +3233,26 @@ function AutoSchedulerModal({
|
|
|
2549
3233
|
...isFailedStep ? styles.stepTitleFailed : null
|
|
2550
3234
|
};
|
|
2551
3235
|
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react4.HStack, { sx: styles.stepRow, children: [
|
|
2552
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Box, { sx: iconStyles, children: isFailedStep ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(FailedIcon, { width: 12, height: 12 }) : status === "active" ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Spinner, { size: "sm", thickness: "2px", speed: "0.7s" }) : status === "done" ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(CheckIcon, { width:
|
|
3236
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Box, { sx: iconStyles, children: isFailedStep ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(FailedIcon, { width: 12, height: 12 }) : status === "active" ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Spinner, { size: "sm", thickness: "2px", speed: "0.7s" }) : status === "done" ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(CheckIcon, { width: 10, height: 10 }) : null }),
|
|
2553
3237
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react4.Box, { children: [
|
|
2554
3238
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Text, { sx: titleStyles, children: step.title }),
|
|
2555
3239
|
status === "active" && step.subtitle ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Text, { sx: styles.stepSubtitle, children: step.subtitle }) : null
|
|
2556
3240
|
] })
|
|
2557
|
-
] },
|
|
3241
|
+
] }, rawStep.title);
|
|
2558
3242
|
}) })
|
|
2559
3243
|
] });
|
|
2560
|
-
const
|
|
3244
|
+
const footerButtons = showConfiguration ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
|
|
2561
3245
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Button, { variant: "outline", onClick: onClose, sx: styles.cancelButton, children: cancelLabel }),
|
|
2562
3246
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
2563
3247
|
import_react4.Button,
|
|
2564
3248
|
{
|
|
2565
3249
|
onClick: handleCreateSchedule,
|
|
2566
|
-
isDisabled: selectedCount === 0 || openShifts.length === 0 || hasEmptyInputs,
|
|
3250
|
+
isDisabled: selectedCount === 0 || !onGenerate && openShifts.length === 0 || hasEmptyInputs || Boolean(scheduleWeekStartYmd) && selectedScheduleDates.length === 0,
|
|
2567
3251
|
sx: styles.primaryButton,
|
|
2568
3252
|
children: primaryActionLabel
|
|
2569
3253
|
}
|
|
2570
3254
|
)
|
|
2571
|
-
] }) : showFailed ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
3255
|
+
] }) : showFailed ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
|
|
2572
3256
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
2573
3257
|
import_react4.Button,
|
|
2574
3258
|
{
|
|
@@ -2577,10 +3261,11 @@ function AutoSchedulerModal({
|
|
|
2577
3261
|
if (lastFailure) {
|
|
2578
3262
|
onFixManually == null ? void 0 : onFixManually(lastFailure, {
|
|
2579
3263
|
selectedLocationIds,
|
|
2580
|
-
selectedConstraintIds,
|
|
3264
|
+
selectedConstraintIds: effectiveSelectedConstraintIds,
|
|
2581
3265
|
constraintValues,
|
|
2582
|
-
selectedOptimizationIds,
|
|
2583
|
-
optimizationValues
|
|
3266
|
+
selectedOptimizationIds: effectiveSelectedOptimizationIds,
|
|
3267
|
+
optimizationValues,
|
|
3268
|
+
selectedBuiltinIds: defaultSelectedBuiltinIds
|
|
2584
3269
|
});
|
|
2585
3270
|
}
|
|
2586
3271
|
onClose();
|
|
@@ -2591,31 +3276,75 @@ function AutoSchedulerModal({
|
|
|
2591
3276
|
),
|
|
2592
3277
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Button, { onClick: handleAdjustConstraints, sx: styles.failedPrimaryButton, children: "Adjust constraints" })
|
|
2593
3278
|
] }) : null;
|
|
2594
|
-
const
|
|
2595
|
-
|
|
2596
|
-
|
|
2597
|
-
|
|
2598
|
-
|
|
2599
|
-
|
|
2600
|
-
|
|
2601
|
-
|
|
2602
|
-
|
|
2603
|
-
|
|
2604
|
-
|
|
2605
|
-
|
|
2606
|
-
|
|
2607
|
-
|
|
2608
|
-
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
|
|
2612
|
-
|
|
2613
|
-
|
|
2614
|
-
|
|
3279
|
+
const bodyStyles = showConfiguration ? styles.body : { ...styles.body, ...styles.creatingBody };
|
|
3280
|
+
const bodyContent = showConfiguration ? configurationContent : stepsContent;
|
|
3281
|
+
const modalContent = /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
3282
|
+
import_react4.Modal,
|
|
3283
|
+
{
|
|
3284
|
+
isOpen,
|
|
3285
|
+
onClose,
|
|
3286
|
+
isCentered: true,
|
|
3287
|
+
motionPreset: "scale",
|
|
3288
|
+
closeOnOverlayClick: false,
|
|
3289
|
+
closeOnEsc: false,
|
|
3290
|
+
trapFocus: false,
|
|
3291
|
+
children: [
|
|
3292
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.ModalOverlay, { sx: styles.overlay }),
|
|
3293
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react4.ModalContent, { sx: styles.content, children: [
|
|
3294
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.ModalHeader, { sx: styles.header, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react4.HStack, { justify: "space-between", w: "full", children: [
|
|
3295
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Text, { sx: styles.title, children: modalTitle }),
|
|
3296
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
3297
|
+
import_react4.IconButton,
|
|
3298
|
+
{
|
|
3299
|
+
"aria-label": "Close modal",
|
|
3300
|
+
variant: "ghost",
|
|
3301
|
+
size: "sm",
|
|
3302
|
+
icon: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(CloseIcon, {}),
|
|
3303
|
+
onClick: onClose,
|
|
3304
|
+
sx: styles.closeButton
|
|
3305
|
+
}
|
|
3306
|
+
)
|
|
3307
|
+
] }) }),
|
|
3308
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.ModalBody, { sx: bodyStyles, children: bodyContent }),
|
|
3309
|
+
footerButtons && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.ModalFooter, { sx: showFailed ? styles.failedFooter : styles.footer, children: footerButtons })
|
|
3310
|
+
] })
|
|
3311
|
+
]
|
|
3312
|
+
}
|
|
3313
|
+
);
|
|
3314
|
+
const drawerFooterNode = footerButtons ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
3315
|
+
import_react4.HStack,
|
|
3316
|
+
{
|
|
3317
|
+
align: "center",
|
|
3318
|
+
justify: "flex-end",
|
|
3319
|
+
px: 6,
|
|
3320
|
+
h: "68px",
|
|
3321
|
+
bg: "gray.50",
|
|
3322
|
+
borderTop: "1px solid",
|
|
3323
|
+
borderColor: "gray.200",
|
|
3324
|
+
gap: 2,
|
|
3325
|
+
flexShrink: 0,
|
|
3326
|
+
w: "full",
|
|
3327
|
+
children: footerButtons
|
|
3328
|
+
}
|
|
3329
|
+
) : void 0;
|
|
3330
|
+
const drawerContent = /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
3331
|
+
import_web_ui.Drawer,
|
|
3332
|
+
{
|
|
3333
|
+
isOpen,
|
|
3334
|
+
onClose,
|
|
3335
|
+
title: modalTitle,
|
|
3336
|
+
closeOnOverlayClick: false,
|
|
3337
|
+
maxW: DRAWER_WIDTH_MAP[drawerSize],
|
|
3338
|
+
bodySx: bodyStyles,
|
|
3339
|
+
footer: drawerFooterNode,
|
|
3340
|
+
children: bodyContent
|
|
3341
|
+
}
|
|
3342
|
+
);
|
|
3343
|
+
const content = displayMode === "drawer" ? drawerContent : modalContent;
|
|
2615
3344
|
if (theme || cssVarsRoot) {
|
|
2616
|
-
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.ChakraProvider, { theme: mergedTheme, cssVarsRoot, children:
|
|
3345
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.ChakraProvider, { theme: mergedTheme, cssVarsRoot, children: content });
|
|
2617
3346
|
}
|
|
2618
|
-
return
|
|
3347
|
+
return content;
|
|
2619
3348
|
}
|
|
2620
3349
|
|
|
2621
3350
|
// src/AutoSchedulerModalWithProvider.tsx
|
|
@@ -2692,11 +3421,17 @@ var RequestError = class extends Error {
|
|
|
2692
3421
|
0 && (module.exports = {
|
|
2693
3422
|
AutoSchedulerModal,
|
|
2694
3423
|
AutoSchedulerModalWithProvider,
|
|
3424
|
+
InfeasibleScheduleError,
|
|
3425
|
+
MAXIMIZE_CONVERSION_RATE_RULE_ID,
|
|
2695
3426
|
RequestError,
|
|
3427
|
+
TRAFFIC_FORECAST_INCENTIVE_RULE_ID,
|
|
2696
3428
|
ViolatedConstraintsPanel,
|
|
2697
3429
|
ViolatedConstraintsPanelWithProvider,
|
|
2698
3430
|
buildRulesPayload,
|
|
2699
3431
|
buildSchedulePayload,
|
|
2700
|
-
|
|
3432
|
+
defaultSelectedScheduleDates,
|
|
3433
|
+
getFriendlyRuleName,
|
|
3434
|
+
getSequentialWeekDatesFromWeekStart,
|
|
3435
|
+
weekdayTwoLetterFromYmd
|
|
2701
3436
|
});
|
|
2702
3437
|
//# sourceMappingURL=index.cjs.map
|