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