@easyteam/auto-scheduler-modal-ui 0.1.2 → 0.1.4
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 +917 -316
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +207 -8
- package/dist/index.d.ts +207 -8
- package/dist/index.js +910 -314
- 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
|
},
|
|
@@ -1235,6 +1242,9 @@ var UI_TO_BACKEND_RULE_ID = {
|
|
|
1235
1242
|
maximize_sales: "maximize-sales-performance",
|
|
1236
1243
|
staff_preferences: "staff-open-shift-preferences"
|
|
1237
1244
|
};
|
|
1245
|
+
var MAXIMIZE_CONVERSION_RATE_RULE_ID = "maximize-conversion-rate";
|
|
1246
|
+
var TRAFFIC_FORECAST_INCENTIVE_RULE_ID = "traffic-forecast-incentive";
|
|
1247
|
+
var SALES_OPTIMIZATION_UI_IDS = /* @__PURE__ */ new Set(["maximize_sales", "cost_to_sales_ratio"]);
|
|
1238
1248
|
var BACKEND_TO_UI_RULE_ID = Object.fromEntries(
|
|
1239
1249
|
Object.entries(UI_TO_BACKEND_RULE_ID).map(([ui, backend]) => [backend, ui])
|
|
1240
1250
|
);
|
|
@@ -1311,11 +1321,24 @@ function hasAnyParameterOverrides(selectedConstraintIds, constraintValues, selec
|
|
|
1311
1321
|
}
|
|
1312
1322
|
return false;
|
|
1313
1323
|
}
|
|
1314
|
-
function
|
|
1324
|
+
function appendSiblingRuleId(ruleIds, siblingId) {
|
|
1325
|
+
if (!ruleIds.includes(siblingId)) {
|
|
1326
|
+
ruleIds.push(siblingId);
|
|
1327
|
+
}
|
|
1328
|
+
}
|
|
1329
|
+
function buildRulesPayload(selectedConstraintIds, constraintValues, selectedOptimizationIds, optimizationValues, siblings = {}) {
|
|
1315
1330
|
const allSelectedRuleIds = [
|
|
1316
1331
|
...selectedConstraintIds.map(toBackendRuleId),
|
|
1317
1332
|
...selectedOptimizationIds.map(toBackendRuleId)
|
|
1318
1333
|
].filter((id) => Boolean(id));
|
|
1334
|
+
const salesOptSelected = selectedOptimizationIds.some((id) => SALES_OPTIMIZATION_UI_IDS.has(id));
|
|
1335
|
+
const maximizeSalesSelected = selectedOptimizationIds.includes("maximize_sales");
|
|
1336
|
+
if (siblings.includeConversionRate && salesOptSelected) {
|
|
1337
|
+
appendSiblingRuleId(allSelectedRuleIds, MAXIMIZE_CONVERSION_RATE_RULE_ID);
|
|
1338
|
+
}
|
|
1339
|
+
if (siblings.includeTrafficIncentive && maximizeSalesSelected) {
|
|
1340
|
+
appendSiblingRuleId(allSelectedRuleIds, TRAFFIC_FORECAST_INCENTIVE_RULE_ID);
|
|
1341
|
+
}
|
|
1319
1342
|
if (allSelectedRuleIds.length === 0) {
|
|
1320
1343
|
return {};
|
|
1321
1344
|
}
|
|
@@ -1345,7 +1368,7 @@ function buildRulesPayload(selectedConstraintIds, constraintValues, selectedOpti
|
|
|
1345
1368
|
return { ruleIds: allSelectedRuleIds, ruleOverrides };
|
|
1346
1369
|
}
|
|
1347
1370
|
function toApiShift(shift, includeEmployeePreferences) {
|
|
1348
|
-
var _a
|
|
1371
|
+
var _a;
|
|
1349
1372
|
const startDateTime = "startDateTime" in shift ? shift.startDateTime : shift.startTime;
|
|
1350
1373
|
const endDateTime = "endDateTime" in shift ? shift.endDateTime : shift.endTime;
|
|
1351
1374
|
return {
|
|
@@ -1353,8 +1376,8 @@ function toApiShift(shift, includeEmployeePreferences) {
|
|
|
1353
1376
|
startDateTime,
|
|
1354
1377
|
endDateTime,
|
|
1355
1378
|
...shift.employeeId ? { employeeId: String(shift.employeeId) } : {},
|
|
1356
|
-
requiredPosition:
|
|
1357
|
-
requiredCount: (
|
|
1379
|
+
requiredPosition: shift.requiredPosition || void 0,
|
|
1380
|
+
requiredCount: (_a = shift.requiredCount) != null ? _a : 1,
|
|
1358
1381
|
location: shift.location ? {
|
|
1359
1382
|
id: String(shift.location.id),
|
|
1360
1383
|
timezone: "timezone" in shift.location ? shift.location.timezone : void 0,
|
|
@@ -1384,9 +1407,25 @@ function toApiLocation(location) {
|
|
|
1384
1407
|
id: String(location.id),
|
|
1385
1408
|
timezone: (_a = location.timezone) != null ? _a : void 0,
|
|
1386
1409
|
jurisdiction: (_b = location.jurisdiction) != null ? _b : void 0,
|
|
1387
|
-
tags: "tags" in location ? location.tags : void 0
|
|
1410
|
+
tags: "tags" in location ? location.tags : void 0,
|
|
1411
|
+
properties: location.properties ? location.properties : void 0
|
|
1388
1412
|
};
|
|
1389
1413
|
}
|
|
1414
|
+
function employeeHasConversionRate(employee) {
|
|
1415
|
+
var _a;
|
|
1416
|
+
const value = (_a = employee.properties) == null ? void 0 : _a.conversionRate;
|
|
1417
|
+
return typeof value === "number" && Number.isFinite(value) && value > 0;
|
|
1418
|
+
}
|
|
1419
|
+
function employeeTouchesSelectedLocations(employee, selectedLocationIds) {
|
|
1420
|
+
var _a;
|
|
1421
|
+
const ids = (_a = employee.locationIds) != null ? _a : [];
|
|
1422
|
+
return ids.some((id) => selectedLocationIds.includes(String(id)));
|
|
1423
|
+
}
|
|
1424
|
+
function hasConversionRateInSelectedLocations(employees, selectedLocationIds) {
|
|
1425
|
+
return employees.some(
|
|
1426
|
+
(employee) => employeeTouchesSelectedLocations(employee, selectedLocationIds) && employeeHasConversionRate(employee)
|
|
1427
|
+
);
|
|
1428
|
+
}
|
|
1390
1429
|
function buildSchedulePayload(input) {
|
|
1391
1430
|
const {
|
|
1392
1431
|
employees,
|
|
@@ -1397,7 +1436,11 @@ function buildSchedulePayload(input) {
|
|
|
1397
1436
|
selectedConstraintIds,
|
|
1398
1437
|
constraintValues,
|
|
1399
1438
|
selectedOptimizationIds,
|
|
1400
|
-
optimizationValues
|
|
1439
|
+
optimizationValues,
|
|
1440
|
+
trafficForecast,
|
|
1441
|
+
constraintDefinitionIds,
|
|
1442
|
+
constraintDefinitions,
|
|
1443
|
+
organizationId
|
|
1401
1444
|
} = input;
|
|
1402
1445
|
const locationsMap = /* @__PURE__ */ new Map();
|
|
1403
1446
|
for (const j of jurisdictions) {
|
|
@@ -1406,19 +1449,45 @@ function buildSchedulePayload(input) {
|
|
|
1406
1449
|
}
|
|
1407
1450
|
}
|
|
1408
1451
|
const selectedLocations = selectedLocationIds.map((id) => locationsMap.get(id)).filter((loc) => Boolean(loc));
|
|
1452
|
+
const scopedOpenShifts = openShifts.filter(
|
|
1453
|
+
(shift) => {
|
|
1454
|
+
var _a;
|
|
1455
|
+
return selectedLocationIds.includes(String((_a = shift.location) == null ? void 0 : _a.id));
|
|
1456
|
+
}
|
|
1457
|
+
);
|
|
1458
|
+
const includeConversionRate = hasConversionRateInSelectedLocations(
|
|
1459
|
+
employees,
|
|
1460
|
+
selectedLocationIds
|
|
1461
|
+
);
|
|
1462
|
+
const scopedTrafficForecast = (trafficForecast != null ? trafficForecast : []).filter(
|
|
1463
|
+
(entry) => selectedLocationIds.includes(String(entry.locationId))
|
|
1464
|
+
);
|
|
1465
|
+
const includeTrafficIncentive = scopedTrafficForecast.length > 0;
|
|
1409
1466
|
const rules = buildRulesPayload(
|
|
1410
1467
|
selectedConstraintIds,
|
|
1411
1468
|
constraintValues,
|
|
1412
1469
|
selectedOptimizationIds,
|
|
1413
|
-
optimizationValues
|
|
1470
|
+
optimizationValues,
|
|
1471
|
+
{ includeConversionRate, includeTrafficIncentive }
|
|
1414
1472
|
);
|
|
1415
1473
|
const includeEmployeePreferences = selectedOptimizationIds.includes("staff_preferences");
|
|
1474
|
+
const hasIds = Boolean(constraintDefinitionIds && constraintDefinitionIds.length > 0);
|
|
1475
|
+
const hasDefs = Boolean(constraintDefinitions && constraintDefinitions.length > 0);
|
|
1476
|
+
const hasRules = hasIds || hasDefs;
|
|
1477
|
+
if (hasIds && !organizationId) {
|
|
1478
|
+
throw new Error("organizationId is required when constraintDefinitionIds are provided");
|
|
1479
|
+
}
|
|
1480
|
+
const trafficRuleActive = includeTrafficIncentive && selectedOptimizationIds.includes("maximize_sales");
|
|
1416
1481
|
return {
|
|
1417
1482
|
employees: employees.map(toApiEmployee),
|
|
1418
|
-
shifts:
|
|
1483
|
+
shifts: scopedOpenShifts.map((shift) => toApiShift(shift, includeEmployeePreferences)),
|
|
1419
1484
|
locations: selectedLocations.map(toApiLocation),
|
|
1420
1485
|
...timeOffs && timeOffs.length > 0 ? { timeOffs } : {},
|
|
1421
|
-
...rules
|
|
1486
|
+
...rules,
|
|
1487
|
+
...trafficRuleActive ? { trafficForecast: scopedTrafficForecast } : {},
|
|
1488
|
+
...hasIds ? { constraintDefinitionIds } : {},
|
|
1489
|
+
...hasDefs ? { constraintDefinitions } : {},
|
|
1490
|
+
...hasRules && organizationId ? { organizationId } : {}
|
|
1422
1491
|
};
|
|
1423
1492
|
}
|
|
1424
1493
|
|
|
@@ -1441,7 +1510,7 @@ async function submitSolveJob(baseURL, payload, options) {
|
|
|
1441
1510
|
headers.Authorization = options.authorization;
|
|
1442
1511
|
}
|
|
1443
1512
|
const response = await axiosInstance.post(
|
|
1444
|
-
`${cleanBaseUrl}/api/schedule/solve-async`,
|
|
1513
|
+
withHorizonQuery(`${cleanBaseUrl}/api/schedule/solve-async`, options),
|
|
1445
1514
|
payload,
|
|
1446
1515
|
{ headers: Object.keys(headers).length > 0 ? headers : void 0 }
|
|
1447
1516
|
);
|
|
@@ -1483,6 +1552,16 @@ async function getJobSolution(baseURL, jobId, options) {
|
|
|
1483
1552
|
);
|
|
1484
1553
|
return response.data;
|
|
1485
1554
|
}
|
|
1555
|
+
function withHorizonQuery(url, options) {
|
|
1556
|
+
if (!(options == null ? void 0 : options.horizonStartDate) || !(options == null ? void 0 : options.horizonEndDate)) {
|
|
1557
|
+
return url;
|
|
1558
|
+
}
|
|
1559
|
+
const params = new URLSearchParams({
|
|
1560
|
+
dateRangeStart: options.horizonStartDate,
|
|
1561
|
+
dateRangeEnd: options.horizonEndDate
|
|
1562
|
+
});
|
|
1563
|
+
return `${url}?${params.toString()}`;
|
|
1564
|
+
}
|
|
1486
1565
|
|
|
1487
1566
|
// src/utils/violationMessages.ts
|
|
1488
1567
|
var SYSTEM_CONSTRAINT_MESSAGES = {
|
|
@@ -1630,17 +1709,14 @@ function getRuleBasedViolationMessage(ruleId, constraintName, description) {
|
|
|
1630
1709
|
}
|
|
1631
1710
|
|
|
1632
1711
|
// src/utils/mapViolationsToResult.ts
|
|
1633
|
-
function mapViolationsToResult(violations, summary) {
|
|
1712
|
+
function mapViolationsToResult(violations, summary, customMessages) {
|
|
1713
|
+
var _a;
|
|
1634
1714
|
const hardViolations = violations.filter((v) => v.isHardViolation === true);
|
|
1635
1715
|
const seenTitles = /* @__PURE__ */ new Set();
|
|
1636
1716
|
const violatedConstraints = [];
|
|
1637
1717
|
for (const v of hardViolations) {
|
|
1638
1718
|
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);
|
|
1719
|
+
const msg = hasRuleId ? (_a = customMessages == null ? void 0 : customMessages[v.ruleId]) != null ? _a : getRuleBasedViolationMessage(v.ruleId, v.constraintName, v.description) : getSystemConstraintMessage(v.constraintName, v.description);
|
|
1644
1720
|
if (!seenTitles.has(msg.title)) {
|
|
1645
1721
|
seenTitles.add(msg.title);
|
|
1646
1722
|
violatedConstraints.push(msg);
|
|
@@ -1692,12 +1768,15 @@ function ViolatedConstraintsPanel({
|
|
|
1692
1768
|
onSecondaryButtonClick,
|
|
1693
1769
|
generateRecommendationsURLAndHeaders,
|
|
1694
1770
|
styles: stylesProp,
|
|
1695
|
-
styleOverrides
|
|
1771
|
+
styleOverrides,
|
|
1772
|
+
errorToast
|
|
1696
1773
|
}) {
|
|
1697
1774
|
const [showRecommendations, setShowRecommendations] = useState(false);
|
|
1698
1775
|
const [isExpanded, setIsExpanded] = useState(true);
|
|
1699
1776
|
const [recommendations, setRecommendations] = useState(null);
|
|
1700
1777
|
const [isLoadingRecommendations, setIsLoadingRecommendations] = useState(false);
|
|
1778
|
+
const [isDisabledRecommendations, setIsDisabledRecommendations] = useState(false);
|
|
1779
|
+
const [recommendationsButtonText, setRecommendationsButtonText] = useState("Show recommendations");
|
|
1701
1780
|
const styles = useMemo(() => {
|
|
1702
1781
|
if (stylesProp) return stylesProp;
|
|
1703
1782
|
return buildStyles(defaultStyles, styleOverrides);
|
|
@@ -1708,13 +1787,12 @@ function ViolatedConstraintsPanel({
|
|
|
1708
1787
|
const displayRecommendations = recommendations !== null ? recommendations : violatedConstraints.recommendedFixes;
|
|
1709
1788
|
const hasRecommendations = displayRecommendations.length > 0;
|
|
1710
1789
|
const handleToggleRecommendations = useCallback(async () => {
|
|
1711
|
-
var _a, _b;
|
|
1712
1790
|
if (showRecommendations) {
|
|
1713
1791
|
setShowRecommendations(false);
|
|
1714
1792
|
return;
|
|
1715
1793
|
}
|
|
1716
|
-
setShowRecommendations(true);
|
|
1717
1794
|
if (recommendations !== null) {
|
|
1795
|
+
setShowRecommendations(true);
|
|
1718
1796
|
return;
|
|
1719
1797
|
}
|
|
1720
1798
|
if (!canFetchRecommendations) {
|
|
@@ -1733,9 +1811,21 @@ function ViolatedConstraintsPanel({
|
|
|
1733
1811
|
}
|
|
1734
1812
|
}
|
|
1735
1813
|
);
|
|
1736
|
-
const
|
|
1737
|
-
|
|
1738
|
-
|
|
1814
|
+
const { success, data, msg } = recResponse.data;
|
|
1815
|
+
const { isExpired, recommendations: recommendations2 } = data != null ? data : {};
|
|
1816
|
+
if (!success) {
|
|
1817
|
+
const errorMsg = msg || "Failed to fetch recommendations";
|
|
1818
|
+
errorToast == null ? void 0 : errorToast(errorMsg);
|
|
1819
|
+
if (isExpired) {
|
|
1820
|
+
setIsDisabledRecommendations(true);
|
|
1821
|
+
setRecommendationsButtonText("Daily limit reached");
|
|
1822
|
+
}
|
|
1823
|
+
return;
|
|
1824
|
+
}
|
|
1825
|
+
setShowRecommendations(true);
|
|
1826
|
+
const fetchedRecommendations = recommendations2;
|
|
1827
|
+
if (fetchedRecommendations) {
|
|
1828
|
+
setRecommendations(fetchedRecommendations.map((r) => r.recommendation));
|
|
1739
1829
|
} else {
|
|
1740
1830
|
setRecommendations([]);
|
|
1741
1831
|
}
|
|
@@ -1750,7 +1840,8 @@ function ViolatedConstraintsPanel({
|
|
|
1750
1840
|
recommendations,
|
|
1751
1841
|
canFetchRecommendations,
|
|
1752
1842
|
violatedConstraints.recommendationPayload,
|
|
1753
|
-
generateRecommendationsURLAndHeaders
|
|
1843
|
+
generateRecommendationsURLAndHeaders,
|
|
1844
|
+
errorToast
|
|
1754
1845
|
]);
|
|
1755
1846
|
return /* @__PURE__ */ jsxs2(Box, { sx: styles.violationPanel, children: [
|
|
1756
1847
|
/* @__PURE__ */ jsxs2(HStack, { sx: styles.violationHeaderRow, children: [
|
|
@@ -1802,9 +1893,9 @@ function ViolatedConstraintsPanel({
|
|
|
1802
1893
|
variant: "outline",
|
|
1803
1894
|
size: "xs",
|
|
1804
1895
|
onClick: handleToggleRecommendations,
|
|
1805
|
-
isDisabled: isLoadingRecommendations,
|
|
1896
|
+
isDisabled: isLoadingRecommendations || isDisabledRecommendations,
|
|
1806
1897
|
sx: { ...styles.recommendationsToggle, minWidth: "140px" },
|
|
1807
|
-
children: isLoadingRecommendations ? /* @__PURE__ */ jsx2(Spinner, { size: "sm", thickness: "2px" }) : showRecommendations ? "Hide recommendations" :
|
|
1898
|
+
children: isLoadingRecommendations ? /* @__PURE__ */ jsx2(Spinner, { size: "sm", thickness: "2px" }) : showRecommendations ? "Hide recommendations" : recommendationsButtonText
|
|
1808
1899
|
}
|
|
1809
1900
|
) : null,
|
|
1810
1901
|
showSecondaryButton && secondaryButtonTitle && onSecondaryButtonClick ? /* @__PURE__ */ jsx2(
|
|
@@ -1822,13 +1913,78 @@ function ViolatedConstraintsPanel({
|
|
|
1822
1913
|
] });
|
|
1823
1914
|
}
|
|
1824
1915
|
|
|
1916
|
+
// src/weekDates.ts
|
|
1917
|
+
function parseCalendarYmd(ymd) {
|
|
1918
|
+
const m = /^(\d{4})-(\d{2})-(\d{2})$/.exec(String(ymd).trim());
|
|
1919
|
+
if (!m) {
|
|
1920
|
+
return /* @__PURE__ */ new Date(NaN);
|
|
1921
|
+
}
|
|
1922
|
+
return new Date(Number(m[1]), Number(m[2]) - 1, Number(m[3]));
|
|
1923
|
+
}
|
|
1924
|
+
function formatCalendarYmd(d) {
|
|
1925
|
+
const y = d.getFullYear();
|
|
1926
|
+
const mo = String(d.getMonth() + 1).padStart(2, "0");
|
|
1927
|
+
const da = String(d.getDate()).padStart(2, "0");
|
|
1928
|
+
return `${y}-${mo}-${da}`;
|
|
1929
|
+
}
|
|
1930
|
+
function getSequentialWeekDatesFromWeekStart(weekStartYmd) {
|
|
1931
|
+
const base = parseCalendarYmd(weekStartYmd);
|
|
1932
|
+
if (Number.isNaN(base.getTime())) return [];
|
|
1933
|
+
const out = [];
|
|
1934
|
+
for (let i = 0; i < 7; i++) {
|
|
1935
|
+
const d = new Date(base.getFullYear(), base.getMonth(), base.getDate() + i);
|
|
1936
|
+
out.push(formatCalendarYmd(d));
|
|
1937
|
+
}
|
|
1938
|
+
return out;
|
|
1939
|
+
}
|
|
1940
|
+
function weekdayTwoLetterFromYmd(ymd) {
|
|
1941
|
+
var _a;
|
|
1942
|
+
const labels = ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"];
|
|
1943
|
+
const d = parseCalendarYmd(ymd);
|
|
1944
|
+
if (Number.isNaN(d.getTime())) return "?";
|
|
1945
|
+
return (_a = labels[d.getDay()]) != null ? _a : "?";
|
|
1946
|
+
}
|
|
1947
|
+
function defaultSelectedScheduleDates(orderedWeekYmds, todayYmd) {
|
|
1948
|
+
return orderedWeekYmds.filter((d) => d >= todayYmd);
|
|
1949
|
+
}
|
|
1950
|
+
function todayCalendarYmdInTimezone(timezone) {
|
|
1951
|
+
const fallback = formatCalendarYmd(/* @__PURE__ */ new Date());
|
|
1952
|
+
const tz = timezone == null ? void 0 : timezone.trim();
|
|
1953
|
+
if (!tz) return fallback;
|
|
1954
|
+
try {
|
|
1955
|
+
const parts = new Intl.DateTimeFormat("en-CA", {
|
|
1956
|
+
timeZone: tz,
|
|
1957
|
+
year: "numeric",
|
|
1958
|
+
month: "2-digit",
|
|
1959
|
+
day: "2-digit"
|
|
1960
|
+
}).formatToParts(/* @__PURE__ */ new Date());
|
|
1961
|
+
const valueByType = new Map(parts.map((part) => [part.type, part.value]));
|
|
1962
|
+
const year = valueByType.get("year");
|
|
1963
|
+
const month = valueByType.get("month");
|
|
1964
|
+
const day = valueByType.get("day");
|
|
1965
|
+
return year && month && day ? `${year}-${month}-${day}` : fallback;
|
|
1966
|
+
} catch {
|
|
1967
|
+
return fallback;
|
|
1968
|
+
}
|
|
1969
|
+
}
|
|
1970
|
+
|
|
1825
1971
|
// src/AutoSchedulerModal.tsx
|
|
1826
|
-
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
1972
|
+
import { Fragment, jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
1973
|
+
function resolveOptimizationFooter(footer, key) {
|
|
1974
|
+
if (!footer) return null;
|
|
1975
|
+
const node = typeof footer === "function" ? footer() : footer;
|
|
1976
|
+
if (isValidElement(node)) return cloneElement(node, { key });
|
|
1977
|
+
return node;
|
|
1978
|
+
}
|
|
1979
|
+
var DRAWER_WIDTH_MAP = { sm: "448px", md: "578px", lg: "600px" };
|
|
1827
1980
|
function AutoSchedulerModal({
|
|
1981
|
+
displayMode,
|
|
1982
|
+
drawerSize = "lg",
|
|
1828
1983
|
baseURL,
|
|
1829
1984
|
isOpen,
|
|
1830
1985
|
title = "Auto-scheduling",
|
|
1831
|
-
|
|
1986
|
+
bannerTitle = "Create a smarter retail schedule in seconds.",
|
|
1987
|
+
bannerText = "AI assigns open shifts using your rules, availability, and optimization priorities while keeping existing\n assigned shifts in place.",
|
|
1832
1988
|
jurisdictions,
|
|
1833
1989
|
openShifts,
|
|
1834
1990
|
employees,
|
|
@@ -1846,22 +2002,79 @@ function AutoSchedulerModal({
|
|
|
1846
2002
|
getTokenURLAndHeaders,
|
|
1847
2003
|
onSolution,
|
|
1848
2004
|
getStoredConfigForLocations,
|
|
1849
|
-
persistConfigForLocations
|
|
2005
|
+
persistConfigForLocations,
|
|
2006
|
+
errorToast,
|
|
2007
|
+
organizationId,
|
|
2008
|
+
constraintDefinitionIds,
|
|
2009
|
+
constraintDefinitions,
|
|
2010
|
+
hardConstraintOptions,
|
|
2011
|
+
softConstraintGroups,
|
|
2012
|
+
optimizationFooter,
|
|
2013
|
+
trafficForecast,
|
|
2014
|
+
builtinConstraintOptions,
|
|
2015
|
+
subLocations,
|
|
2016
|
+
showSubLocations,
|
|
2017
|
+
teams,
|
|
2018
|
+
showTeams,
|
|
2019
|
+
onSelectionChange,
|
|
2020
|
+
onGenerate,
|
|
2021
|
+
onGenerateSolution,
|
|
2022
|
+
solveStatus,
|
|
2023
|
+
scheduleWeekStartYmd,
|
|
2024
|
+
todayCalendarYmd,
|
|
2025
|
+
evaluationTimezone
|
|
1850
2026
|
}) {
|
|
1851
2027
|
const baseTheme = useTheme();
|
|
1852
|
-
const { chakraOverride, styleOverrides } = useMemo2(
|
|
1853
|
-
() => resolveThemeParts(theme),
|
|
1854
|
-
[theme]
|
|
1855
|
-
);
|
|
2028
|
+
const { chakraOverride, styleOverrides } = useMemo2(() => resolveThemeParts(theme), [theme]);
|
|
1856
2029
|
const mergedTheme = useMemo2(
|
|
1857
2030
|
() => mergeThemeWithBase(baseTheme, chakraOverride),
|
|
1858
2031
|
[baseTheme, chakraOverride]
|
|
1859
2032
|
);
|
|
1860
2033
|
const styles = useMemo2(() => buildStyles(defaultStyles, styleOverrides), [styleOverrides]);
|
|
2034
|
+
const effectiveHardConstraintOptions = useMemo2(() => {
|
|
2035
|
+
const base = hardConstraintOptions != null ? hardConstraintOptions : HARD_CONSTRAINT_OPTIONS;
|
|
2036
|
+
return base.map((opt) => {
|
|
2037
|
+
var _a, _b, _c, _d;
|
|
2038
|
+
return {
|
|
2039
|
+
...opt,
|
|
2040
|
+
defaultValue: (_a = opt.defaultValue) != null ? _a : HARD_CONSTRAINT_DEFAULTS[opt.id],
|
|
2041
|
+
suffix: (_b = opt.suffix) != null ? _b : opt.id === "max_work_days_per_week" ? "days" : "hours",
|
|
2042
|
+
min: (_c = opt.min) != null ? _c : opt.id === "max_work_days_per_week" ? 1 : void 0,
|
|
2043
|
+
max: (_d = opt.max) != null ? _d : opt.id === "max_work_days_per_week" ? 6 : void 0
|
|
2044
|
+
};
|
|
2045
|
+
});
|
|
2046
|
+
}, [hardConstraintOptions]);
|
|
2047
|
+
const effectiveSoftConstraintGroups = useMemo2(
|
|
2048
|
+
() => softConstraintGroups != null ? softConstraintGroups : OPTIMIZATION_OPTION_GROUPS,
|
|
2049
|
+
[softConstraintGroups]
|
|
2050
|
+
);
|
|
2051
|
+
const effectiveBuiltinOptions = useMemo2(
|
|
2052
|
+
() => builtinConstraintOptions != null ? builtinConstraintOptions : [],
|
|
2053
|
+
[builtinConstraintOptions]
|
|
2054
|
+
);
|
|
2055
|
+
const defaultSelectedBuiltinIds = useMemo2(
|
|
2056
|
+
() => effectiveBuiltinOptions.filter((option) => option.defaultSelected).map((option) => option.id),
|
|
2057
|
+
[effectiveBuiltinOptions]
|
|
2058
|
+
);
|
|
2059
|
+
const dynamicViolationMessages = useMemo2(() => {
|
|
2060
|
+
const map = {};
|
|
2061
|
+
for (const opt of effectiveHardConstraintOptions) {
|
|
2062
|
+
map[opt.id] = { title: opt.name, detail: opt.description || opt.name };
|
|
2063
|
+
}
|
|
2064
|
+
for (const group of effectiveSoftConstraintGroups) {
|
|
2065
|
+
for (const opt of group.options) {
|
|
2066
|
+
map[opt.id] = { title: opt.title, detail: opt.subtitle || opt.title };
|
|
2067
|
+
}
|
|
2068
|
+
}
|
|
2069
|
+
return map;
|
|
2070
|
+
}, [effectiveHardConstraintOptions, effectiveSoftConstraintGroups]);
|
|
2071
|
+
const [selectedSubLocationIds, setSelectedSubLocationIds] = useState2([]);
|
|
2072
|
+
const [selectedTeamIds, setSelectedTeamIds] = useState2([]);
|
|
1861
2073
|
const [selectedConstraintIds, setSelectedConstraintIds] = useState2([]);
|
|
1862
2074
|
const [constraintValues, setConstraintValues] = useState2({});
|
|
1863
2075
|
const [selectedOptimizationIds, setSelectedOptimizationIds] = useState2([]);
|
|
1864
2076
|
const [optimizationValues, setOptimizationValues] = useState2({});
|
|
2077
|
+
const [selectedScheduleDates, setSelectedScheduleDates] = useState2([]);
|
|
1865
2078
|
const [screen, setScreen] = useState2("configure");
|
|
1866
2079
|
const [activeStepIndex, setActiveStepIndex] = useState2(0);
|
|
1867
2080
|
const [completedStepIndex, setCompletedStepIndex] = useState2(-1);
|
|
@@ -1870,12 +2083,19 @@ function AutoSchedulerModal({
|
|
|
1870
2083
|
const timersRef = useRef([]);
|
|
1871
2084
|
const pollIntervalRef = useRef(null);
|
|
1872
2085
|
const runIdRef = useRef(0);
|
|
2086
|
+
const weekDatesOrdered = useMemo2(() => {
|
|
2087
|
+
if (!scheduleWeekStartYmd) return [];
|
|
2088
|
+
return getSequentialWeekDatesFromWeekStart(scheduleWeekStartYmd);
|
|
2089
|
+
}, [scheduleWeekStartYmd]);
|
|
2090
|
+
const toggleScheduleDate = (ymd) => {
|
|
2091
|
+
setSelectedScheduleDates(
|
|
2092
|
+
(prev) => prev.includes(ymd) ? prev.filter((d) => d !== ymd) : [...prev, ymd].sort()
|
|
2093
|
+
);
|
|
2094
|
+
};
|
|
1873
2095
|
const selectedByJurisdiction = useMemo2(
|
|
1874
2096
|
() => jurisdictions.map((jurisdiction) => ({
|
|
1875
2097
|
...jurisdiction,
|
|
1876
|
-
selectedLocations: jurisdiction.locations.filter(
|
|
1877
|
-
(location) => selectedLocationIds.includes(location.id)
|
|
1878
|
-
)
|
|
2098
|
+
selectedLocations: jurisdiction.locations.filter((location) => selectedLocationIds.includes(location.id))
|
|
1879
2099
|
})).filter((jurisdiction) => jurisdiction.selectedLocations.length > 0),
|
|
1880
2100
|
[jurisdictions, selectedLocationIds]
|
|
1881
2101
|
);
|
|
@@ -1883,39 +2103,85 @@ function AutoSchedulerModal({
|
|
|
1883
2103
|
(total, jurisdiction) => total + jurisdiction.selectedLocations.length,
|
|
1884
2104
|
0
|
|
1885
2105
|
);
|
|
2106
|
+
const locationIdsWithOpenShifts = useMemo2(() => {
|
|
2107
|
+
return new Set(openShifts.filter((shift) => !shift.employeeId).map((shift) => shift.location.id));
|
|
2108
|
+
}, [openShifts]);
|
|
2109
|
+
const filteredSubLocations = useMemo2(
|
|
2110
|
+
() => {
|
|
2111
|
+
var _a;
|
|
2112
|
+
return (_a = subLocations == null ? void 0 : subLocations.filter((sl) => selectedLocationIds.includes(sl.parentId))) != null ? _a : [];
|
|
2113
|
+
},
|
|
2114
|
+
[subLocations, selectedLocationIds]
|
|
2115
|
+
);
|
|
2116
|
+
const selectedLocationTimezone = useMemo2(() => {
|
|
2117
|
+
var _a, _b;
|
|
2118
|
+
const allLocations = jurisdictions.flatMap((jurisdiction) => jurisdiction.locations);
|
|
2119
|
+
for (const id of selectedLocationIds) {
|
|
2120
|
+
const timezone = (_b = (_a = allLocations.find((location) => location.id === id)) == null ? void 0 : _a.timezone) == null ? void 0 : _b.trim();
|
|
2121
|
+
if (timezone) return timezone;
|
|
2122
|
+
}
|
|
2123
|
+
return (evaluationTimezone == null ? void 0 : evaluationTimezone.trim()) || "UTC";
|
|
2124
|
+
}, [evaluationTimezone, jurisdictions, selectedLocationIds]);
|
|
2125
|
+
const effectiveTodayCalendarYmd = useMemo2(() => {
|
|
2126
|
+
return todayCalendarYmd != null ? todayCalendarYmd : todayCalendarYmdInTimezone(selectedLocationTimezone);
|
|
2127
|
+
}, [selectedLocationTimezone, todayCalendarYmd]);
|
|
1886
2128
|
const selectedConstraints = useMemo2(
|
|
1887
|
-
() =>
|
|
1888
|
-
[selectedConstraintIds]
|
|
2129
|
+
() => effectiveHardConstraintOptions.filter((option) => selectedConstraintIds.includes(option.id)),
|
|
2130
|
+
[effectiveHardConstraintOptions, selectedConstraintIds]
|
|
2131
|
+
);
|
|
2132
|
+
const selectedConstraintDisplayOptions = useMemo2(
|
|
2133
|
+
() => effectiveHardConstraintOptions.filter(
|
|
2134
|
+
(option) => selectedConstraintIds.includes(option.id) || option.readOnly && option.defaultSelected
|
|
2135
|
+
),
|
|
2136
|
+
[effectiveHardConstraintOptions, selectedConstraintIds]
|
|
1889
2137
|
);
|
|
1890
2138
|
const optimizationOptions = useMemo2(
|
|
1891
|
-
() =>
|
|
2139
|
+
() => effectiveSoftConstraintGroups.flatMap(
|
|
1892
2140
|
(group) => group.options.map((option) => ({
|
|
1893
2141
|
...option,
|
|
1894
2142
|
groupId: group.id,
|
|
1895
2143
|
groupName: group.name
|
|
1896
2144
|
}))
|
|
1897
2145
|
),
|
|
1898
|
-
[]
|
|
2146
|
+
[effectiveSoftConstraintGroups]
|
|
1899
2147
|
);
|
|
1900
2148
|
const selectedOptimizationOptions = useMemo2(
|
|
2149
|
+
() => optimizationOptions.filter((option) => selectedOptimizationIds.includes(option.id)),
|
|
2150
|
+
[optimizationOptions, selectedOptimizationIds]
|
|
2151
|
+
);
|
|
2152
|
+
const selectedOptimizationDisplayOptions = useMemo2(
|
|
1901
2153
|
() => optimizationOptions.filter(
|
|
1902
|
-
(option) => selectedOptimizationIds.includes(option.id)
|
|
2154
|
+
(option) => selectedOptimizationIds.includes(option.id) || option.readOnly && option.defaultSelected
|
|
1903
2155
|
),
|
|
1904
2156
|
[optimizationOptions, selectedOptimizationIds]
|
|
1905
2157
|
);
|
|
2158
|
+
const effectiveSelectedConstraintIds = useMemo2(
|
|
2159
|
+
() => Array.from(
|
|
2160
|
+
/* @__PURE__ */ new Set([
|
|
2161
|
+
...selectedConstraintIds,
|
|
2162
|
+
...effectiveHardConstraintOptions.filter((option) => option.readOnly && option.defaultSelected).map((option) => option.id)
|
|
2163
|
+
])
|
|
2164
|
+
),
|
|
2165
|
+
[selectedConstraintIds, effectiveHardConstraintOptions]
|
|
2166
|
+
);
|
|
2167
|
+
const effectiveSelectedOptimizationIds = useMemo2(
|
|
2168
|
+
() => Array.from(
|
|
2169
|
+
/* @__PURE__ */ new Set([
|
|
2170
|
+
...selectedOptimizationIds,
|
|
2171
|
+
...optimizationOptions.filter((option) => option.readOnly && option.defaultSelected).map((option) => option.id)
|
|
2172
|
+
])
|
|
2173
|
+
),
|
|
2174
|
+
[selectedOptimizationIds, optimizationOptions]
|
|
2175
|
+
);
|
|
1906
2176
|
const hasEmptyInputs = useMemo2(() => {
|
|
1907
|
-
const hasEmptyConstraint = selectedConstraints.some(
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
var _a;
|
|
1916
|
-
return !((_a = optimizationValues[`${opt.id}_weightPoints`]) == null ? void 0 : _a.trim());
|
|
1917
|
-
}
|
|
1918
|
-
);
|
|
2177
|
+
const hasEmptyConstraint = selectedConstraints.filter((opt) => opt.defaultValue != null).some((opt) => {
|
|
2178
|
+
var _a;
|
|
2179
|
+
return !((_a = constraintValues[opt.id]) == null ? void 0 : _a.trim());
|
|
2180
|
+
});
|
|
2181
|
+
const hasEmptyWeight = selectedOptimizationOptions.length >= 2 && selectedOptimizationOptions.some((opt) => {
|
|
2182
|
+
var _a;
|
|
2183
|
+
return !((_a = optimizationValues[`${opt.id}_weightPoints`]) == null ? void 0 : _a.trim());
|
|
2184
|
+
});
|
|
1919
2185
|
return hasEmptyConstraint || hasEmptyWeight;
|
|
1920
2186
|
}, [selectedConstraints, constraintValues, selectedOptimizationOptions, optimizationValues]);
|
|
1921
2187
|
const allocationSummary = useMemo2(() => {
|
|
@@ -1949,13 +2215,25 @@ function AutoSchedulerModal({
|
|
|
1949
2215
|
setScreen("configure");
|
|
1950
2216
|
setLastFailure(null);
|
|
1951
2217
|
setSelectedLocationIds([]);
|
|
2218
|
+
setSelectedSubLocationIds([]);
|
|
2219
|
+
setSelectedTeamIds([]);
|
|
1952
2220
|
setSelectedOptimizationIds([]);
|
|
1953
2221
|
setSelectedConstraintIds([]);
|
|
1954
2222
|
setConstraintValues({});
|
|
1955
2223
|
setOptimizationValues({});
|
|
2224
|
+
setSelectedScheduleDates([]);
|
|
1956
2225
|
resetProgress();
|
|
1957
2226
|
}
|
|
1958
2227
|
}, [isOpen]);
|
|
2228
|
+
useEffect(() => {
|
|
2229
|
+
if (!isOpen || !scheduleWeekStartYmd) return;
|
|
2230
|
+
if (weekDatesOrdered.length === 0) {
|
|
2231
|
+
setSelectedScheduleDates([]);
|
|
2232
|
+
return;
|
|
2233
|
+
}
|
|
2234
|
+
const today = effectiveTodayCalendarYmd != null ? effectiveTodayCalendarYmd : weekDatesOrdered[0];
|
|
2235
|
+
setSelectedScheduleDates(defaultSelectedScheduleDates(weekDatesOrdered, today));
|
|
2236
|
+
}, [isOpen, scheduleWeekStartYmd, weekDatesOrdered, effectiveTodayCalendarYmd]);
|
|
1959
2237
|
useEffect(() => {
|
|
1960
2238
|
if (isOpen && initialConfig) {
|
|
1961
2239
|
setSelectedLocationIds(initialConfig.selectedLocationIds);
|
|
@@ -1987,6 +2265,13 @@ function AutoSchedulerModal({
|
|
|
1987
2265
|
}
|
|
1988
2266
|
}, [selectedLocationIds, initialConfig, getStoredConfigForLocations]);
|
|
1989
2267
|
useEffect(() => () => clearTimers(), []);
|
|
2268
|
+
useEffect(() => {
|
|
2269
|
+
if (onGenerate) return;
|
|
2270
|
+
setSelectedLocationIds((previous) => {
|
|
2271
|
+
const filtered = previous.filter((id) => locationIdsWithOpenShifts.has(id));
|
|
2272
|
+
return filtered.length === previous.length ? previous : filtered;
|
|
2273
|
+
});
|
|
2274
|
+
}, [locationIdsWithOpenShifts, onGenerate]);
|
|
1990
2275
|
const stepStatusForIndex = (index) => {
|
|
1991
2276
|
if (index <= completedStepIndex) {
|
|
1992
2277
|
return "done";
|
|
@@ -2014,144 +2299,249 @@ function AutoSchedulerModal({
|
|
|
2014
2299
|
timersRef.current.push(timerId);
|
|
2015
2300
|
};
|
|
2016
2301
|
const afterSuccessSteps = (runId, response) => {
|
|
2017
|
-
schedule(
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
setCompletedStepIndex(4);
|
|
2302
|
+
schedule(
|
|
2303
|
+
STEP_INTERVAL_MS,
|
|
2304
|
+
() => {
|
|
2305
|
+
if (runIdRef.current !== runId) return;
|
|
2306
|
+
setCompletedStepIndex(2);
|
|
2307
|
+
setActiveStepIndex(3);
|
|
2308
|
+
},
|
|
2309
|
+
runId
|
|
2310
|
+
);
|
|
2311
|
+
schedule(
|
|
2312
|
+
STEP_INTERVAL_MS * 2,
|
|
2313
|
+
() => {
|
|
2314
|
+
if (runIdRef.current !== runId) return;
|
|
2315
|
+
setCompletedStepIndex(3);
|
|
2032
2316
|
setActiveStepIndex(4);
|
|
2033
|
-
}
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2317
|
+
},
|
|
2318
|
+
runId
|
|
2319
|
+
);
|
|
2320
|
+
schedule(
|
|
2321
|
+
STEP_INTERVAL_MS * 3,
|
|
2322
|
+
async () => {
|
|
2323
|
+
if (runIdRef.current !== runId) return;
|
|
2324
|
+
try {
|
|
2325
|
+
await (onSolution == null ? void 0 : onSolution(response.solution));
|
|
2326
|
+
setCompletedStepIndex(4);
|
|
2327
|
+
setActiveStepIndex(4);
|
|
2328
|
+
} catch (err) {
|
|
2329
|
+
console.error(err);
|
|
2330
|
+
const defaultErrorMessage = "Failed to create schedule shifts";
|
|
2331
|
+
const detailedErrorMessage = err instanceof Error ? err.message || defaultErrorMessage : defaultErrorMessage;
|
|
2332
|
+
setLastFailure({
|
|
2333
|
+
violatedConstraints: [
|
|
2334
|
+
{
|
|
2335
|
+
title: "Something went wrong",
|
|
2336
|
+
detail: detailedErrorMessage
|
|
2337
|
+
}
|
|
2338
|
+
],
|
|
2339
|
+
recommendedFixes: []
|
|
2340
|
+
});
|
|
2341
|
+
setScreen("failed");
|
|
2342
|
+
}
|
|
2343
|
+
},
|
|
2344
|
+
runId
|
|
2345
|
+
);
|
|
2049
2346
|
};
|
|
2050
2347
|
const handleCreateSchedule = () => {
|
|
2051
2348
|
if (selectedCount === 0) {
|
|
2052
2349
|
return;
|
|
2053
2350
|
}
|
|
2351
|
+
if (scheduleWeekStartYmd && selectedScheduleDates.length === 0) {
|
|
2352
|
+
return;
|
|
2353
|
+
}
|
|
2354
|
+
if (onGenerate) {
|
|
2355
|
+
const runId2 = resetBeforeSubmit();
|
|
2356
|
+
schedule(
|
|
2357
|
+
STEP_INTERVAL_MS,
|
|
2358
|
+
async () => {
|
|
2359
|
+
if (runIdRef.current !== runId2) return;
|
|
2360
|
+
setCompletedStepIndex(0);
|
|
2361
|
+
setActiveStepIndex(1);
|
|
2362
|
+
try {
|
|
2363
|
+
const generateResult = await onGenerate({
|
|
2364
|
+
selectedLocationIds,
|
|
2365
|
+
selectedSubLocationIds,
|
|
2366
|
+
selectedTeamIds,
|
|
2367
|
+
schedulerTimezone: selectedLocationTimezone,
|
|
2368
|
+
selectedScheduleDates,
|
|
2369
|
+
selectedConstraintIds: effectiveSelectedConstraintIds,
|
|
2370
|
+
constraintValues,
|
|
2371
|
+
selectedOptimizationIds: effectiveSelectedOptimizationIds,
|
|
2372
|
+
optimizationValues,
|
|
2373
|
+
selectedBuiltinIds: defaultSelectedBuiltinIds
|
|
2374
|
+
});
|
|
2375
|
+
if (runIdRef.current !== runId2) return;
|
|
2376
|
+
setCompletedStepIndex(1);
|
|
2377
|
+
setActiveStepIndex(2);
|
|
2378
|
+
schedule(
|
|
2379
|
+
STEP_INTERVAL_MS,
|
|
2380
|
+
() => {
|
|
2381
|
+
if (runIdRef.current !== runId2) return;
|
|
2382
|
+
setCompletedStepIndex(2);
|
|
2383
|
+
setActiveStepIndex(3);
|
|
2384
|
+
},
|
|
2385
|
+
runId2
|
|
2386
|
+
);
|
|
2387
|
+
schedule(
|
|
2388
|
+
STEP_INTERVAL_MS * 2,
|
|
2389
|
+
() => {
|
|
2390
|
+
if (runIdRef.current !== runId2) return;
|
|
2391
|
+
setCompletedStepIndex(3);
|
|
2392
|
+
setActiveStepIndex(4);
|
|
2393
|
+
},
|
|
2394
|
+
runId2
|
|
2395
|
+
);
|
|
2396
|
+
schedule(
|
|
2397
|
+
STEP_INTERVAL_MS * 3,
|
|
2398
|
+
() => {
|
|
2399
|
+
if (runIdRef.current !== runId2) return;
|
|
2400
|
+
setCompletedStepIndex(4);
|
|
2401
|
+
setActiveStepIndex(4);
|
|
2402
|
+
onGenerateSolution == null ? void 0 : onGenerateSolution(generateResult);
|
|
2403
|
+
onClose();
|
|
2404
|
+
},
|
|
2405
|
+
runId2
|
|
2406
|
+
);
|
|
2407
|
+
} catch (err) {
|
|
2408
|
+
if (runIdRef.current !== runId2) return;
|
|
2409
|
+
if (err instanceof InfeasibleScheduleError) {
|
|
2410
|
+
setLastFailure(err.failure);
|
|
2411
|
+
} else {
|
|
2412
|
+
const msg = err instanceof Error ? err.message : "Unknown error";
|
|
2413
|
+
setLastFailure({
|
|
2414
|
+
violatedConstraints: [{ title: "Error", detail: msg }],
|
|
2415
|
+
recommendedFixes: []
|
|
2416
|
+
});
|
|
2417
|
+
}
|
|
2418
|
+
setScreen("failed");
|
|
2419
|
+
}
|
|
2420
|
+
},
|
|
2421
|
+
runId2
|
|
2422
|
+
);
|
|
2423
|
+
return;
|
|
2424
|
+
}
|
|
2054
2425
|
const payload = buildSchedulePayload({
|
|
2055
2426
|
employees,
|
|
2056
2427
|
openShifts,
|
|
2057
2428
|
timeOffs,
|
|
2058
2429
|
selectedLocationIds,
|
|
2059
2430
|
jurisdictions,
|
|
2060
|
-
selectedConstraintIds,
|
|
2431
|
+
selectedConstraintIds: effectiveSelectedConstraintIds,
|
|
2061
2432
|
constraintValues,
|
|
2062
|
-
selectedOptimizationIds,
|
|
2063
|
-
optimizationValues
|
|
2433
|
+
selectedOptimizationIds: effectiveSelectedOptimizationIds,
|
|
2434
|
+
optimizationValues,
|
|
2435
|
+
trafficForecast,
|
|
2436
|
+
constraintDefinitionIds,
|
|
2437
|
+
constraintDefinitions,
|
|
2438
|
+
organizationId
|
|
2064
2439
|
});
|
|
2065
2440
|
persistConfigForLocations == null ? void 0 : persistConfigForLocations(selectedLocationIds, {
|
|
2066
2441
|
selectedLocationIds,
|
|
2067
|
-
selectedConstraintIds,
|
|
2442
|
+
selectedConstraintIds: effectiveSelectedConstraintIds,
|
|
2068
2443
|
constraintValues,
|
|
2069
|
-
selectedOptimizationIds,
|
|
2070
|
-
optimizationValues
|
|
2444
|
+
selectedOptimizationIds: effectiveSelectedOptimizationIds,
|
|
2445
|
+
optimizationValues,
|
|
2446
|
+
selectedBuiltinIds: defaultSelectedBuiltinIds
|
|
2071
2447
|
});
|
|
2072
2448
|
const runId = resetBeforeSubmit();
|
|
2073
|
-
schedule(
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2449
|
+
schedule(
|
|
2450
|
+
STEP_INTERVAL_MS,
|
|
2451
|
+
async () => {
|
|
2452
|
+
setCompletedStepIndex(0);
|
|
2453
|
+
setActiveStepIndex(1);
|
|
2454
|
+
try {
|
|
2455
|
+
const token = await getToken(getTokenURLAndHeaders.url, getTokenURLAndHeaders.headers);
|
|
2456
|
+
if (!token) {
|
|
2457
|
+
throw new Error("Failed to get auto-scheduler token");
|
|
2458
|
+
}
|
|
2459
|
+
const selectedDates = [...selectedScheduleDates].sort();
|
|
2460
|
+
const horizonOptions = selectedDates.length > 0 ? {
|
|
2461
|
+
horizonStartDate: selectedDates[0],
|
|
2462
|
+
horizonEndDate: selectedDates[selectedDates.length - 1]
|
|
2463
|
+
} : {};
|
|
2464
|
+
const auth = { authorization: `Bearer ${token}`, ...horizonOptions };
|
|
2465
|
+
const { jobId } = await submitSolveJob(baseURL, payload, auth);
|
|
2466
|
+
if (runIdRef.current !== runId) {
|
|
2467
|
+
return;
|
|
2468
|
+
}
|
|
2469
|
+
setCompletedStepIndex(1);
|
|
2470
|
+
setActiveStepIndex(OPTIMIZING_STEP_INDEX);
|
|
2471
|
+
const pollForResult = async () => {
|
|
2092
2472
|
if (runIdRef.current !== runId) return;
|
|
2093
|
-
|
|
2473
|
+
try {
|
|
2474
|
+
const statusRes = await getJobStatus(baseURL, jobId, auth);
|
|
2475
|
+
if (runIdRef.current !== runId) return;
|
|
2476
|
+
if (statusRes.status === "NOT_SOLVING" || statusRes.status === "TERMINATED_EARLY") {
|
|
2477
|
+
if (pollIntervalRef.current) {
|
|
2478
|
+
clearInterval(pollIntervalRef.current);
|
|
2479
|
+
pollIntervalRef.current = null;
|
|
2480
|
+
}
|
|
2481
|
+
const response = await getJobResult(baseURL, jobId, auth);
|
|
2482
|
+
if (runIdRef.current !== runId) return;
|
|
2483
|
+
let responseForHandling = response;
|
|
2484
|
+
if (response.explanation.isFeasible) {
|
|
2485
|
+
const solution = await getJobSolution(baseURL, jobId, auth);
|
|
2486
|
+
if (runIdRef.current !== runId) return;
|
|
2487
|
+
responseForHandling = { ...response, solution };
|
|
2488
|
+
}
|
|
2489
|
+
handleSolveResult(runId, responseForHandling);
|
|
2490
|
+
return;
|
|
2491
|
+
}
|
|
2492
|
+
} catch (err) {
|
|
2493
|
+
if (runIdRef.current !== runId) return;
|
|
2094
2494
|
if (pollIntervalRef.current) {
|
|
2095
2495
|
clearInterval(pollIntervalRef.current);
|
|
2096
2496
|
pollIntervalRef.current = null;
|
|
2097
2497
|
}
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
if (response.explanation.isFeasible) {
|
|
2102
|
-
const solution = await getJobSolution(baseURL, jobId, auth);
|
|
2103
|
-
if (runIdRef.current !== runId) return;
|
|
2104
|
-
responseForHandling = { ...response, solution };
|
|
2105
|
-
}
|
|
2106
|
-
handleSolveResult(runId, responseForHandling);
|
|
2107
|
-
return;
|
|
2108
|
-
}
|
|
2109
|
-
} catch (err) {
|
|
2110
|
-
if (runIdRef.current !== runId) return;
|
|
2111
|
-
if (pollIntervalRef.current) {
|
|
2112
|
-
clearInterval(pollIntervalRef.current);
|
|
2113
|
-
pollIntervalRef.current = null;
|
|
2498
|
+
console.error(err);
|
|
2499
|
+
setLastFailure(NETWORK_ERROR_FAILURE);
|
|
2500
|
+
setScreen("failed");
|
|
2114
2501
|
}
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2502
|
+
};
|
|
2503
|
+
pollForResult();
|
|
2504
|
+
pollIntervalRef.current = setInterval(pollForResult, POLL_INTERVAL_MS);
|
|
2505
|
+
} catch (err) {
|
|
2506
|
+
if (runIdRef.current !== runId) {
|
|
2507
|
+
return;
|
|
2118
2508
|
}
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
} catch (err) {
|
|
2123
|
-
if (runIdRef.current !== runId) {
|
|
2124
|
-
return;
|
|
2509
|
+
console.error(err);
|
|
2510
|
+
setLastFailure(NETWORK_ERROR_FAILURE);
|
|
2511
|
+
setScreen("failed");
|
|
2125
2512
|
}
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
}
|
|
2130
|
-
}, runId);
|
|
2513
|
+
},
|
|
2514
|
+
runId
|
|
2515
|
+
);
|
|
2131
2516
|
};
|
|
2132
2517
|
const handleSolveResult = (runId, response) => {
|
|
2133
|
-
var _a
|
|
2518
|
+
var _a;
|
|
2134
2519
|
if (!response.explanation.isFeasible) {
|
|
2135
|
-
const result = mapViolationsToResult(
|
|
2136
|
-
|
|
2137
|
-
response.explanation.summary
|
|
2138
|
-
);
|
|
2139
|
-
const selectedLocations = jurisdictions.flatMap((j) => j.locations).filter(
|
|
2140
|
-
(l) => selectedLocationIds.includes(l.id)
|
|
2141
|
-
);
|
|
2520
|
+
const result = mapViolationsToResult((_a = response.explanation.violations) != null ? _a : [], response.explanation.summary, dynamicViolationMessages);
|
|
2521
|
+
const selectedLocations = jurisdictions.flatMap((j) => j.locations).filter((l) => selectedLocationIds.includes(l.id));
|
|
2142
2522
|
const recommendationPayload = generateRecommendationsURLAndHeaders ? {
|
|
2143
2523
|
context: {
|
|
2144
2524
|
selectedLocationIds,
|
|
2145
|
-
selectedConstraintIds,
|
|
2525
|
+
selectedConstraintIds: effectiveSelectedConstraintIds,
|
|
2146
2526
|
constraintValues,
|
|
2147
|
-
selectedOptimizationIds,
|
|
2527
|
+
selectedOptimizationIds: effectiveSelectedOptimizationIds,
|
|
2148
2528
|
optimizationValues,
|
|
2149
|
-
timezone:
|
|
2529
|
+
timezone: selectedLocationTimezone
|
|
2150
2530
|
},
|
|
2151
2531
|
locations: selectedLocations,
|
|
2152
2532
|
employees,
|
|
2153
|
-
openShifts: openShifts.filter(
|
|
2154
|
-
|
|
2533
|
+
openShifts: openShifts.filter(
|
|
2534
|
+
(s) => {
|
|
2535
|
+
var _a2;
|
|
2536
|
+
return !s.employeeId && selectedLocationIds.includes(String((_a2 = s.location) == null ? void 0 : _a2.id));
|
|
2537
|
+
}
|
|
2538
|
+
),
|
|
2539
|
+
assignedShifts: openShifts.filter(
|
|
2540
|
+
(s) => {
|
|
2541
|
+
var _a2;
|
|
2542
|
+
return Boolean(s.employeeId) && selectedLocationIds.includes(String((_a2 = s.location) == null ? void 0 : _a2.id));
|
|
2543
|
+
}
|
|
2544
|
+
),
|
|
2155
2545
|
timeOffs: timeOffs != null ? timeOffs : [],
|
|
2156
2546
|
explanation: response.explanation
|
|
2157
2547
|
} : void 0;
|
|
@@ -2170,14 +2560,34 @@ function AutoSchedulerModal({
|
|
|
2170
2560
|
setScreen("configure");
|
|
2171
2561
|
};
|
|
2172
2562
|
const handleToggleLocation = (locationId) => {
|
|
2173
|
-
if (
|
|
2174
|
-
setSelectedLocationIds(selectedLocationIds.filter((id) => id !== locationId));
|
|
2563
|
+
if (!onGenerate && !locationIdsWithOpenShifts.has(locationId)) {
|
|
2175
2564
|
return;
|
|
2176
2565
|
}
|
|
2177
|
-
|
|
2566
|
+
const newLocationIds = selectedLocationIds.includes(locationId) ? selectedLocationIds.filter((id) => id !== locationId) : [...selectedLocationIds, locationId];
|
|
2567
|
+
setSelectedLocationIds(newLocationIds);
|
|
2568
|
+
const filteredSubs = selectedSubLocationIds.filter((subId) => {
|
|
2569
|
+
const sub = subLocations == null ? void 0 : subLocations.find((s) => s.id === subId);
|
|
2570
|
+
return sub ? newLocationIds.includes(sub.parentId) : false;
|
|
2571
|
+
});
|
|
2572
|
+
if (filteredSubs.length !== selectedSubLocationIds.length) {
|
|
2573
|
+
setSelectedSubLocationIds(filteredSubs);
|
|
2574
|
+
}
|
|
2575
|
+
onSelectionChange == null ? void 0 : onSelectionChange(
|
|
2576
|
+
newLocationIds,
|
|
2577
|
+
filteredSubs.length !== selectedSubLocationIds.length ? filteredSubs : selectedSubLocationIds
|
|
2578
|
+
);
|
|
2579
|
+
};
|
|
2580
|
+
const handleToggleSubLocation = (subLocationId) => {
|
|
2581
|
+
const next = selectedSubLocationIds.includes(subLocationId) ? selectedSubLocationIds.filter((id) => id !== subLocationId) : [...selectedSubLocationIds, subLocationId];
|
|
2582
|
+
setSelectedSubLocationIds(next);
|
|
2583
|
+
onSelectionChange == null ? void 0 : onSelectionChange(selectedLocationIds, next);
|
|
2584
|
+
};
|
|
2585
|
+
const handleToggleTeam = (teamId) => {
|
|
2586
|
+
setSelectedTeamIds((prev) => prev.includes(teamId) ? prev.filter((id) => id !== teamId) : [...prev, teamId]);
|
|
2178
2587
|
};
|
|
2179
2588
|
const handleToggleConstraint = (constraintId) => {
|
|
2180
2589
|
setSelectedConstraintIds((prev) => {
|
|
2590
|
+
var _a;
|
|
2181
2591
|
if (prev.includes(constraintId)) {
|
|
2182
2592
|
setConstraintValues((values) => {
|
|
2183
2593
|
const next = { ...values };
|
|
@@ -2186,10 +2596,11 @@ function AutoSchedulerModal({
|
|
|
2186
2596
|
});
|
|
2187
2597
|
return prev.filter((id) => id !== constraintId);
|
|
2188
2598
|
}
|
|
2189
|
-
|
|
2599
|
+
const defaultVal = (_a = effectiveHardConstraintOptions.find((o) => o.id === constraintId)) == null ? void 0 : _a.defaultValue;
|
|
2600
|
+
if (defaultVal) {
|
|
2190
2601
|
setConstraintValues((values) => ({
|
|
2191
2602
|
...values,
|
|
2192
|
-
[constraintId]:
|
|
2603
|
+
[constraintId]: defaultVal
|
|
2193
2604
|
}));
|
|
2194
2605
|
}
|
|
2195
2606
|
return [...prev, constraintId];
|
|
@@ -2233,6 +2644,7 @@ function AutoSchedulerModal({
|
|
|
2233
2644
|
violatedConstraints: lastFailure,
|
|
2234
2645
|
title: violationTitle,
|
|
2235
2646
|
subtitle: "",
|
|
2647
|
+
errorToast,
|
|
2236
2648
|
generateRecommendationsURLAndHeaders,
|
|
2237
2649
|
styles
|
|
2238
2650
|
}
|
|
@@ -2240,12 +2652,12 @@ function AutoSchedulerModal({
|
|
|
2240
2652
|
const configurationContent = /* @__PURE__ */ jsxs3(Stack2, { spacing: "20px", children: [
|
|
2241
2653
|
/* @__PURE__ */ jsxs3(Box2, { sx: styles.banner, children: [
|
|
2242
2654
|
/* @__PURE__ */ jsx3(Box2, { sx: styles.bannerIcon, children: /* @__PURE__ */ jsx3(MagicIcon, {}) }),
|
|
2243
|
-
|
|
2244
|
-
/* @__PURE__ */ jsx3(Text2, { sx: styles.bannerTitle, children:
|
|
2245
|
-
/* @__PURE__ */ jsx3(Text2, { sx: styles.bannerText, children:
|
|
2655
|
+
/* @__PURE__ */ jsxs3(Stack2, { spacing: "4px", flex: 1, children: [
|
|
2656
|
+
/* @__PURE__ */ jsx3(Text2, { sx: styles.bannerTitle, children: bannerTitle }),
|
|
2657
|
+
/* @__PURE__ */ jsx3(Text2, { sx: styles.bannerText, children: bannerText })
|
|
2246
2658
|
] })
|
|
2247
2659
|
] }),
|
|
2248
|
-
openShifts.length === 0 ? /* @__PURE__ */ jsxs3(
|
|
2660
|
+
!onGenerate && openShifts.length === 0 ? /* @__PURE__ */ jsxs3(
|
|
2249
2661
|
Box2,
|
|
2250
2662
|
{
|
|
2251
2663
|
sx: {
|
|
@@ -2288,25 +2700,33 @@ function AutoSchedulerModal({
|
|
|
2288
2700
|
] }) }) }),
|
|
2289
2701
|
/* @__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: [
|
|
2290
2702
|
/* @__PURE__ */ jsx3(Text2, { sx: styles.groupHeader, children: jurisdiction.title }),
|
|
2291
|
-
/* @__PURE__ */ jsx3(Stack2, { spacing: "2px", children: jurisdiction.locations.map((location) =>
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2703
|
+
/* @__PURE__ */ jsx3(Stack2, { spacing: "2px", children: jurisdiction.locations.map((location) => {
|
|
2704
|
+
const isEnabled = onGenerate || locationIdsWithOpenShifts.has(location.id);
|
|
2705
|
+
return /* @__PURE__ */ jsxs3(
|
|
2706
|
+
HStack2,
|
|
2707
|
+
{
|
|
2708
|
+
as: "label",
|
|
2709
|
+
sx: {
|
|
2710
|
+
...styles.optionRow,
|
|
2711
|
+
cursor: isEnabled ? "pointer" : "not-allowed",
|
|
2712
|
+
opacity: isEnabled ? 1 : 0.55
|
|
2713
|
+
},
|
|
2714
|
+
children: [
|
|
2715
|
+
/* @__PURE__ */ jsx3(
|
|
2716
|
+
Checkbox,
|
|
2717
|
+
{
|
|
2718
|
+
id: `location-${location.id}`,
|
|
2719
|
+
isChecked: selectedLocationIds.includes(location.id),
|
|
2720
|
+
isDisabled: !isEnabled,
|
|
2721
|
+
onChange: () => handleToggleLocation(location.id)
|
|
2722
|
+
}
|
|
2723
|
+
),
|
|
2724
|
+
/* @__PURE__ */ jsx3(Text2, { sx: styles.optionLabel, children: location.name })
|
|
2725
|
+
]
|
|
2726
|
+
},
|
|
2727
|
+
location.id
|
|
2728
|
+
);
|
|
2729
|
+
}) })
|
|
2310
2730
|
] }, jurisdiction.id)) }) }) })
|
|
2311
2731
|
] }),
|
|
2312
2732
|
/* @__PURE__ */ jsx3(Stack2, { spacing: "8px", children: selectedByJurisdiction.map((jurisdiction) => /* @__PURE__ */ jsxs3(Box2, { sx: styles.selectedBanner, children: [
|
|
@@ -2320,11 +2740,128 @@ function AutoSchedulerModal({
|
|
|
2320
2740
|
] })
|
|
2321
2741
|
] }, jurisdiction.id)) })
|
|
2322
2742
|
] }),
|
|
2743
|
+
showSubLocations && /* @__PURE__ */ jsxs3(Stack2, { spacing: "8px", sx: styles.locationSection, children: [
|
|
2744
|
+
/* @__PURE__ */ jsx3(Stack2, { spacing: "4px", children: /* @__PURE__ */ jsxs3(HStack2, { sx: styles.locationLabelRow, children: [
|
|
2745
|
+
/* @__PURE__ */ jsx3(Text2, { sx: styles.locationLabel, children: "Sub-location" }),
|
|
2746
|
+
selectedSubLocationIds.length > 0 && /* @__PURE__ */ jsx3(Badge, { sx: styles.locationBadge, children: selectedSubLocationIds.length })
|
|
2747
|
+
] }) }),
|
|
2748
|
+
/* @__PURE__ */ jsxs3(Popover, { placement: "bottom-start", matchWidth: true, isLazy: true, children: [
|
|
2749
|
+
/* @__PURE__ */ jsx3(PopoverTrigger, { children: /* @__PURE__ */ jsx3(
|
|
2750
|
+
Button2,
|
|
2751
|
+
{
|
|
2752
|
+
variant: "unstyled",
|
|
2753
|
+
isDisabled: selectedLocationIds.length === 0 || filteredSubLocations.length === 0,
|
|
2754
|
+
sx: {
|
|
2755
|
+
...styles.locationTrigger,
|
|
2756
|
+
...selectedLocationIds.length === 0 || filteredSubLocations.length === 0 ? { opacity: 0.5, cursor: "not-allowed" } : {}
|
|
2757
|
+
},
|
|
2758
|
+
children: /* @__PURE__ */ jsxs3(HStack2, { justify: "space-between", w: "full", children: [
|
|
2759
|
+
/* @__PURE__ */ jsx3(
|
|
2760
|
+
Text2,
|
|
2761
|
+
{
|
|
2762
|
+
sx: {
|
|
2763
|
+
...styles.locationTriggerText,
|
|
2764
|
+
...selectedSubLocationIds.length > 0 ? { color: "#303030" } : {}
|
|
2765
|
+
},
|
|
2766
|
+
noOfLines: 1,
|
|
2767
|
+
children: selectedSubLocationIds.length > 0 ? filteredSubLocations.filter((s) => selectedSubLocationIds.includes(s.id)).map((s) => s.name).join(", ") : "Select sub-locations"
|
|
2768
|
+
}
|
|
2769
|
+
),
|
|
2770
|
+
/* @__PURE__ */ jsx3(Box2, { color: "#5c5c5c", children: /* @__PURE__ */ jsx3(ChevronDownIcon, {}) })
|
|
2771
|
+
] })
|
|
2772
|
+
}
|
|
2773
|
+
) }),
|
|
2774
|
+
/* @__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: [
|
|
2775
|
+
/* @__PURE__ */ jsx3(
|
|
2776
|
+
Checkbox,
|
|
2777
|
+
{
|
|
2778
|
+
id: `sublocation-${sub.id}`,
|
|
2779
|
+
isChecked: selectedSubLocationIds.includes(sub.id),
|
|
2780
|
+
onChange: () => handleToggleSubLocation(sub.id)
|
|
2781
|
+
}
|
|
2782
|
+
),
|
|
2783
|
+
/* @__PURE__ */ jsx3(Text2, { sx: styles.optionLabel, children: sub.name })
|
|
2784
|
+
] }, sub.id)) }) }) })
|
|
2785
|
+
] })
|
|
2786
|
+
] }),
|
|
2787
|
+
showTeams && /* @__PURE__ */ jsxs3(Stack2, { spacing: "8px", sx: styles.locationSection, children: [
|
|
2788
|
+
/* @__PURE__ */ jsx3(Stack2, { spacing: "4px", children: /* @__PURE__ */ jsxs3(HStack2, { sx: styles.locationLabelRow, children: [
|
|
2789
|
+
/* @__PURE__ */ jsx3(Text2, { sx: styles.locationLabel, children: "Teams" }),
|
|
2790
|
+
selectedTeamIds.length > 0 && /* @__PURE__ */ jsx3(Badge, { sx: styles.locationBadge, children: selectedTeamIds.length })
|
|
2791
|
+
] }) }),
|
|
2792
|
+
/* @__PURE__ */ jsxs3(Popover, { placement: "bottom-start", matchWidth: true, isLazy: true, children: [
|
|
2793
|
+
/* @__PURE__ */ jsx3(PopoverTrigger, { children: /* @__PURE__ */ jsx3(
|
|
2794
|
+
Button2,
|
|
2795
|
+
{
|
|
2796
|
+
variant: "unstyled",
|
|
2797
|
+
isDisabled: !teams || teams.length === 0,
|
|
2798
|
+
sx: {
|
|
2799
|
+
...styles.locationTrigger,
|
|
2800
|
+
...!teams || teams.length === 0 ? { opacity: 0.5, cursor: "not-allowed" } : {}
|
|
2801
|
+
},
|
|
2802
|
+
children: /* @__PURE__ */ jsxs3(HStack2, { justify: "space-between", w: "full", children: [
|
|
2803
|
+
/* @__PURE__ */ jsx3(
|
|
2804
|
+
Text2,
|
|
2805
|
+
{
|
|
2806
|
+
sx: {
|
|
2807
|
+
...styles.locationTriggerText,
|
|
2808
|
+
...selectedTeamIds.length > 0 ? { color: "#303030" } : {}
|
|
2809
|
+
},
|
|
2810
|
+
noOfLines: 1,
|
|
2811
|
+
children: selectedTeamIds.length > 0 ? (teams != null ? teams : []).filter((t) => selectedTeamIds.includes(t.id)).map((t) => t.name).join(", ") : "Select teams"
|
|
2812
|
+
}
|
|
2813
|
+
),
|
|
2814
|
+
/* @__PURE__ */ jsx3(Box2, { color: "#5c5c5c", children: /* @__PURE__ */ jsx3(ChevronDownIcon, {}) })
|
|
2815
|
+
] })
|
|
2816
|
+
}
|
|
2817
|
+
) }),
|
|
2818
|
+
/* @__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: [
|
|
2819
|
+
/* @__PURE__ */ jsx3(
|
|
2820
|
+
Checkbox,
|
|
2821
|
+
{
|
|
2822
|
+
id: `team-${team.id}`,
|
|
2823
|
+
isChecked: selectedTeamIds.includes(team.id),
|
|
2824
|
+
onChange: () => handleToggleTeam(team.id)
|
|
2825
|
+
}
|
|
2826
|
+
),
|
|
2827
|
+
/* @__PURE__ */ jsx3(Text2, { sx: styles.optionLabel, children: team.name })
|
|
2828
|
+
] }, team.id)) }) }) })
|
|
2829
|
+
] })
|
|
2830
|
+
] }),
|
|
2831
|
+
scheduleWeekStartYmd ? /* @__PURE__ */ jsxs3(Stack2, { spacing: "8px", sx: styles.locationSection, children: [
|
|
2832
|
+
/* @__PURE__ */ jsx3(Text2, { sx: { ...styles.locationLabel, fontWeight: 600 }, children: "Generate schedule for:" }),
|
|
2833
|
+
/* @__PURE__ */ jsx3(HStack2, { spacing: 2, flexWrap: "wrap", children: weekDatesOrdered.map((ymd) => {
|
|
2834
|
+
const selected = selectedScheduleDates.includes(ymd);
|
|
2835
|
+
return /* @__PURE__ */ jsx3(
|
|
2836
|
+
Button2,
|
|
2837
|
+
{
|
|
2838
|
+
type: "button",
|
|
2839
|
+
variant: "unstyled",
|
|
2840
|
+
onClick: () => toggleScheduleDate(ymd),
|
|
2841
|
+
borderRadius: "full",
|
|
2842
|
+
minW: "40px",
|
|
2843
|
+
h: "40px",
|
|
2844
|
+
px: 0,
|
|
2845
|
+
fontSize: "sm",
|
|
2846
|
+
fontWeight: 600,
|
|
2847
|
+
borderWidth: selected ? "2px" : "1px",
|
|
2848
|
+
borderStyle: "solid",
|
|
2849
|
+
borderColor: selected ? "#5b44ff" : "#e2e8f0",
|
|
2850
|
+
bg: selected ? "#ede9fe" : "#ffffff",
|
|
2851
|
+
color: selected ? "#5b44ff" : "#303030",
|
|
2852
|
+
"aria-pressed": selected,
|
|
2853
|
+
"aria-label": `${weekdayTwoLetterFromYmd(ymd)} ${ymd}`,
|
|
2854
|
+
children: weekdayTwoLetterFromYmd(ymd)
|
|
2855
|
+
},
|
|
2856
|
+
ymd
|
|
2857
|
+
);
|
|
2858
|
+
}) })
|
|
2859
|
+
] }) : null,
|
|
2323
2860
|
/* @__PURE__ */ jsxs3(Stack2, { spacing: "16px", sx: styles.hardConstraintsSection, children: [
|
|
2324
2861
|
/* @__PURE__ */ jsxs3(Stack2, { spacing: "4px", children: [
|
|
2325
2862
|
/* @__PURE__ */ jsxs3(HStack2, { sx: styles.hardConstraintsLabelRow, children: [
|
|
2326
2863
|
/* @__PURE__ */ jsx3(Text2, { sx: styles.hardConstraintsLabel, children: "Required rules" }),
|
|
2327
|
-
|
|
2864
|
+
selectedConstraintDisplayOptions.length > 0 ? /* @__PURE__ */ jsx3(Badge, { sx: styles.hardConstraintsBadge, children: selectedConstraintDisplayOptions.length }) : null
|
|
2328
2865
|
] }),
|
|
2329
2866
|
/* @__PURE__ */ jsx3(Text2, { sx: styles.hardConstraintsSubtitle, children: "Assigned shifts will be required to meet the following rules:" })
|
|
2330
2867
|
] }),
|
|
@@ -2336,35 +2873,30 @@ function AutoSchedulerModal({
|
|
|
2336
2873
|
{
|
|
2337
2874
|
sx: {
|
|
2338
2875
|
...styles.hardConstraintsTriggerText,
|
|
2339
|
-
...
|
|
2876
|
+
...selectedConstraintDisplayOptions.length > 0 ? { color: "#303030" } : {}
|
|
2340
2877
|
},
|
|
2341
2878
|
noOfLines: 1,
|
|
2342
|
-
children:
|
|
2879
|
+
children: selectedConstraintDisplayOptions.length > 0 ? selectedConstraintDisplayOptions.map((o) => o.name).join(", ") : "Select rules"
|
|
2343
2880
|
}
|
|
2344
2881
|
),
|
|
2345
2882
|
/* @__PURE__ */ jsx3(Box2, { color: "#5c5c5c", children: /* @__PURE__ */ jsx3(ChevronDownIcon, {}) })
|
|
2346
2883
|
] }) }) }),
|
|
2347
|
-
/* @__PURE__ */ jsx3(PopoverContent, { sx: styles.hardConstraintsPopoverContent, children: /* @__PURE__ */ jsx3(PopoverBody, { p: 0, children: /* @__PURE__ */ jsx3(Stack2, { spacing: "4px", children:
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2361
|
-
/* @__PURE__ */ jsx3(Text2, { sx: styles.hardConstraintsOptionLabel, children: option.name })
|
|
2362
|
-
]
|
|
2363
|
-
},
|
|
2364
|
-
option.id
|
|
2365
|
-
)) }) }) })
|
|
2884
|
+
/* @__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: [
|
|
2885
|
+
/* @__PURE__ */ jsx3(
|
|
2886
|
+
Checkbox,
|
|
2887
|
+
{
|
|
2888
|
+
id: `constraint-${option.id}`,
|
|
2889
|
+
isChecked: selectedConstraintIds.includes(option.id) || Boolean(option.readOnly && option.defaultSelected),
|
|
2890
|
+
isDisabled: option.readOnly,
|
|
2891
|
+
onChange: () => {
|
|
2892
|
+
if (!option.readOnly) handleToggleConstraint(option.id);
|
|
2893
|
+
}
|
|
2894
|
+
}
|
|
2895
|
+
),
|
|
2896
|
+
/* @__PURE__ */ jsx3(Text2, { sx: styles.hardConstraintsOptionLabel, children: option.name })
|
|
2897
|
+
] }, option.id)) }) }) })
|
|
2366
2898
|
] }),
|
|
2367
|
-
selectedConstraints.length > 0 ? /* @__PURE__ */ jsx3(Box2, { sx: styles.hardConstraintsInputsContainer, children: /* @__PURE__ */ jsx3(Stack2, { spacing: "16px", children: selectedConstraints.map((option, index) => {
|
|
2899
|
+
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) => {
|
|
2368
2900
|
var _a;
|
|
2369
2901
|
return /* @__PURE__ */ jsxs3(Stack2, { sx: styles.hardConstraintsInputItem, children: [
|
|
2370
2902
|
/* @__PURE__ */ jsxs3(Text2, { sx: styles.hardConstraintsInputLabel, children: [
|
|
@@ -2382,13 +2914,13 @@ function AutoSchedulerModal({
|
|
|
2382
2914
|
...prev,
|
|
2383
2915
|
[option.id]: event.target.value
|
|
2384
2916
|
})),
|
|
2385
|
-
min: option.
|
|
2386
|
-
max: option.
|
|
2387
|
-
step: option.
|
|
2917
|
+
min: option.min,
|
|
2918
|
+
max: option.max,
|
|
2919
|
+
step: option.max != null ? void 0 : "any",
|
|
2388
2920
|
sx: styles.hardConstraintsInputField
|
|
2389
2921
|
}
|
|
2390
2922
|
),
|
|
2391
|
-
/* @__PURE__ */ jsx3(InputRightAddon, { sx: styles.optimizationSuffix, children: option.
|
|
2923
|
+
/* @__PURE__ */ jsx3(InputRightAddon, { sx: styles.optimizationSuffix, children: option.suffix })
|
|
2392
2924
|
] })
|
|
2393
2925
|
] }, option.id);
|
|
2394
2926
|
}) }) }) : null
|
|
@@ -2397,89 +2929,100 @@ function AutoSchedulerModal({
|
|
|
2397
2929
|
/* @__PURE__ */ jsxs3(Stack2, { spacing: "4px", children: [
|
|
2398
2930
|
/* @__PURE__ */ jsxs3(HStack2, { sx: styles.optimizationLabelRow, children: [
|
|
2399
2931
|
/* @__PURE__ */ jsx3(Text2, { sx: styles.optimizationLabel, children: "Optimization" }),
|
|
2400
|
-
|
|
2932
|
+
selectedOptimizationDisplayOptions.length > 0 ? /* @__PURE__ */ jsx3(Badge, { sx: styles.optimizationBadge, children: selectedOptimizationDisplayOptions.length }) : null
|
|
2401
2933
|
] }),
|
|
2402
2934
|
/* @__PURE__ */ jsx3(Text2, { sx: styles.optimizationSubtitle, children: "Your schedule will be optimized based on the following priorities:" })
|
|
2403
2935
|
] }),
|
|
2404
|
-
/* @__PURE__ */ jsxs3(Popover, { placement: "
|
|
2936
|
+
/* @__PURE__ */ jsxs3(Popover, { placement: "top-start", strategy: "fixed", matchWidth: true, isLazy: true, gutter: 4, children: [
|
|
2405
2937
|
/* @__PURE__ */ jsx3(PopoverTrigger, { children: /* @__PURE__ */ jsx3(Button2, { variant: "unstyled", sx: styles.optimizationTrigger, children: /* @__PURE__ */ jsxs3(HStack2, { justify: "space-between", w: "full", children: [
|
|
2406
2938
|
/* @__PURE__ */ jsx3(
|
|
2407
2939
|
Text2,
|
|
2408
2940
|
{
|
|
2409
2941
|
sx: {
|
|
2410
2942
|
...styles.optimizationTriggerText,
|
|
2411
|
-
...
|
|
2943
|
+
...selectedOptimizationDisplayOptions.length > 0 ? { color: "#303030" } : {}
|
|
2412
2944
|
},
|
|
2413
2945
|
noOfLines: 1,
|
|
2414
|
-
children:
|
|
2946
|
+
children: selectedOptimizationDisplayOptions.length > 0 ? selectedOptimizationDisplayOptions.map((o) => o.title).join(", ") : "Select optimization priorities"
|
|
2415
2947
|
}
|
|
2416
2948
|
),
|
|
2417
2949
|
/* @__PURE__ */ jsx3(Box2, { color: "#5c5c5c", children: /* @__PURE__ */ jsx3(ChevronDownIcon, {}) })
|
|
2418
2950
|
] }) }) }),
|
|
2419
|
-
/* @__PURE__ */ jsx3(
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
Checkbox,
|
|
2431
|
-
{
|
|
2432
|
-
id: `optimization-${option.id}`,
|
|
2433
|
-
isChecked: selectedOptimizationIds.includes(option.id),
|
|
2434
|
-
isDisabled: !selectedOptimizationIds.includes(option.id) && selectedOptimizationIds.length >= 3,
|
|
2435
|
-
onChange: () => handleToggleOptimization(option.id)
|
|
2436
|
-
}
|
|
2437
|
-
),
|
|
2438
|
-
/* @__PURE__ */ jsx3(Text2, { sx: styles.optimizationOptionLabel, children: option.title })
|
|
2439
|
-
] }),
|
|
2440
|
-
option.subtitle ? /* @__PURE__ */ jsx3(Text2, { sx: styles.optimizationOptionHelp, children: option.subtitle }) : null
|
|
2441
|
-
]
|
|
2951
|
+
/* @__PURE__ */ jsx3(Portal, { appendToParentPortal: false, children: /* @__PURE__ */ jsx3(
|
|
2952
|
+
PopoverContent,
|
|
2953
|
+
{
|
|
2954
|
+
sx: {
|
|
2955
|
+
...styles.optimizationPopoverContent,
|
|
2956
|
+
maxH: "min(560px, calc(100vh - 96px))",
|
|
2957
|
+
overflowY: "auto",
|
|
2958
|
+
zIndex: 1500,
|
|
2959
|
+
scrollbarWidth: "none",
|
|
2960
|
+
msOverflowStyle: "none",
|
|
2961
|
+
"&::-webkit-scrollbar": { display: "none" }
|
|
2442
2962
|
},
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
2963
|
+
children: /* @__PURE__ */ jsx3(PopoverBody, { p: 0, children: /* @__PURE__ */ jsx3(Stack2, { spacing: "4px", children: effectiveSoftConstraintGroups.map((group) => /* @__PURE__ */ jsxs3(Box2, { children: [
|
|
2964
|
+
/* @__PURE__ */ jsx3(Text2, { sx: styles.optimizationGroupHeader, children: group.name }),
|
|
2965
|
+
/* @__PURE__ */ jsx3(Stack2, { spacing: "2px", children: group.options.map((option) => /* @__PURE__ */ jsxs3(Box2, { children: [
|
|
2966
|
+
/* @__PURE__ */ jsxs3(
|
|
2967
|
+
Stack2,
|
|
2968
|
+
{
|
|
2969
|
+
as: "label",
|
|
2970
|
+
spacing: "2px",
|
|
2971
|
+
sx: { ...styles.optimizationOptionRow, cursor: "pointer" },
|
|
2972
|
+
children: [
|
|
2973
|
+
/* @__PURE__ */ jsxs3(HStack2, { spacing: "8px", children: [
|
|
2974
|
+
/* @__PURE__ */ jsx3(
|
|
2975
|
+
Checkbox,
|
|
2976
|
+
{
|
|
2977
|
+
id: `optimization-${option.id}`,
|
|
2978
|
+
isChecked: selectedOptimizationIds.includes(option.id) || Boolean(option.readOnly && option.defaultSelected),
|
|
2979
|
+
isDisabled: option.readOnly || !selectedOptimizationIds.includes(option.id) && selectedOptimizationIds.length >= 3,
|
|
2980
|
+
onChange: () => {
|
|
2981
|
+
if (!option.readOnly) handleToggleOptimization(option.id);
|
|
2982
|
+
}
|
|
2983
|
+
}
|
|
2984
|
+
),
|
|
2985
|
+
/* @__PURE__ */ jsx3(Text2, { sx: styles.optimizationOptionLabel, children: option.title })
|
|
2986
|
+
] }),
|
|
2987
|
+
option.subtitle ? /* @__PURE__ */ jsx3(Text2, { sx: styles.optimizationOptionHelp, children: option.subtitle }) : null
|
|
2988
|
+
]
|
|
2989
|
+
}
|
|
2990
|
+
),
|
|
2991
|
+
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
|
|
2992
|
+
] }, option.id)) })
|
|
2993
|
+
] }, group.id)) }) })
|
|
2994
|
+
}
|
|
2995
|
+
) })
|
|
2446
2996
|
] }),
|
|
2447
2997
|
selectedOptimizationOptions.length >= 2 ? /* @__PURE__ */ jsxs3(Box2, { sx: styles.optimizationInputsContainer, children: [
|
|
2448
2998
|
/* @__PURE__ */ jsx3(Stack2, { spacing: "16px", children: selectedOptimizationOptions.map((option, index) => {
|
|
2449
2999
|
var _a;
|
|
2450
|
-
return /* @__PURE__ */ jsx3(
|
|
2451
|
-
|
|
2452
|
-
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
|
|
2456
|
-
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
|
|
2471
|
-
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
),
|
|
2477
|
-
/* @__PURE__ */ jsx3(InputRightAddon, { sx: styles.optimizationSuffix, children: "points" })
|
|
2478
|
-
] })
|
|
2479
|
-
] })
|
|
2480
|
-
},
|
|
2481
|
-
option.id
|
|
2482
|
-
);
|
|
3000
|
+
return /* @__PURE__ */ jsx3(Stack2, { sx: styles.optimizationInputItem, children: /* @__PURE__ */ jsxs3(Stack2, { sx: styles.hardConstraintsInputItem, children: [
|
|
3001
|
+
/* @__PURE__ */ jsxs3(Text2, { sx: styles.hardConstraintsInputLabel, children: [
|
|
3002
|
+
index + 1,
|
|
3003
|
+
". ",
|
|
3004
|
+
option.title
|
|
3005
|
+
] }),
|
|
3006
|
+
/* @__PURE__ */ jsxs3(InputGroup, { children: [
|
|
3007
|
+
/* @__PURE__ */ jsx3(
|
|
3008
|
+
Input,
|
|
3009
|
+
{
|
|
3010
|
+
type: "text",
|
|
3011
|
+
value: (_a = optimizationValues[`${option.id}_weightPoints`]) != null ? _a : "1",
|
|
3012
|
+
onChange: (e) => {
|
|
3013
|
+
const val = e.target.value;
|
|
3014
|
+
if (val !== "" && (!/^\d+$/.test(val) || Number(val) < 1)) return;
|
|
3015
|
+
setOptimizationValues((prev) => ({
|
|
3016
|
+
...prev,
|
|
3017
|
+
[`${option.id}_weightPoints`]: val
|
|
3018
|
+
}));
|
|
3019
|
+
},
|
|
3020
|
+
sx: styles.hardConstraintsInputField
|
|
3021
|
+
}
|
|
3022
|
+
),
|
|
3023
|
+
/* @__PURE__ */ jsx3(InputRightAddon, { sx: styles.optimizationSuffix, children: "points" })
|
|
3024
|
+
] })
|
|
3025
|
+
] }) }, option.id);
|
|
2483
3026
|
}) }),
|
|
2484
3027
|
/* @__PURE__ */ jsx3(Divider, { sx: { ...styles.optimizationDivider, my: "16px" } }),
|
|
2485
3028
|
/* @__PURE__ */ jsx3(Text2, { sx: styles.optimizationSummary, children: allocationSummary })
|
|
@@ -2488,7 +3031,9 @@ function AutoSchedulerModal({
|
|
|
2488
3031
|
] });
|
|
2489
3032
|
const stepsContent = /* @__PURE__ */ jsxs3(Stack2, { spacing: "16px", sx: styles.stepsContainer, children: [
|
|
2490
3033
|
showFailed ? violationPanel : null,
|
|
2491
|
-
/* @__PURE__ */ jsx3(Stack2, { spacing: "14px", sx: styles.stepsList, children: SCHEDULE_STEPS.map((
|
|
3034
|
+
/* @__PURE__ */ jsx3(Stack2, { spacing: "14px", sx: styles.stepsList, children: SCHEDULE_STEPS.map((rawStep, index) => {
|
|
3035
|
+
const isQueueWait = index === 1 && solveStatus === "QUEUED";
|
|
3036
|
+
const step = isQueueWait ? { title: "Waiting in queue\u2026", subtitle: void 0 } : rawStep;
|
|
2492
3037
|
const status = stepStatusForIndex(index);
|
|
2493
3038
|
const isFailedStep = showFailed && index === activeStepIndex;
|
|
2494
3039
|
const iconStyles = {
|
|
@@ -2506,26 +3051,26 @@ function AutoSchedulerModal({
|
|
|
2506
3051
|
...isFailedStep ? styles.stepTitleFailed : null
|
|
2507
3052
|
};
|
|
2508
3053
|
return /* @__PURE__ */ jsxs3(HStack2, { sx: styles.stepRow, children: [
|
|
2509
|
-
/* @__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:
|
|
3054
|
+
/* @__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 }),
|
|
2510
3055
|
/* @__PURE__ */ jsxs3(Box2, { children: [
|
|
2511
3056
|
/* @__PURE__ */ jsx3(Text2, { sx: titleStyles, children: step.title }),
|
|
2512
3057
|
status === "active" && step.subtitle ? /* @__PURE__ */ jsx3(Text2, { sx: styles.stepSubtitle, children: step.subtitle }) : null
|
|
2513
3058
|
] })
|
|
2514
|
-
] },
|
|
3059
|
+
] }, rawStep.title);
|
|
2515
3060
|
}) })
|
|
2516
3061
|
] });
|
|
2517
|
-
const
|
|
3062
|
+
const footerButtons = showConfiguration ? /* @__PURE__ */ jsxs3(Fragment, { children: [
|
|
2518
3063
|
/* @__PURE__ */ jsx3(Button2, { variant: "outline", onClick: onClose, sx: styles.cancelButton, children: cancelLabel }),
|
|
2519
3064
|
/* @__PURE__ */ jsx3(
|
|
2520
3065
|
Button2,
|
|
2521
3066
|
{
|
|
2522
3067
|
onClick: handleCreateSchedule,
|
|
2523
|
-
isDisabled: selectedCount === 0 || openShifts.length === 0 || hasEmptyInputs,
|
|
3068
|
+
isDisabled: selectedCount === 0 || !onGenerate && openShifts.length === 0 || hasEmptyInputs || Boolean(scheduleWeekStartYmd) && selectedScheduleDates.length === 0,
|
|
2524
3069
|
sx: styles.primaryButton,
|
|
2525
3070
|
children: primaryActionLabel
|
|
2526
3071
|
}
|
|
2527
3072
|
)
|
|
2528
|
-
] }) : showFailed ? /* @__PURE__ */ jsxs3(
|
|
3073
|
+
] }) : showFailed ? /* @__PURE__ */ jsxs3(Fragment, { children: [
|
|
2529
3074
|
/* @__PURE__ */ jsx3(
|
|
2530
3075
|
Button2,
|
|
2531
3076
|
{
|
|
@@ -2534,10 +3079,11 @@ function AutoSchedulerModal({
|
|
|
2534
3079
|
if (lastFailure) {
|
|
2535
3080
|
onFixManually == null ? void 0 : onFixManually(lastFailure, {
|
|
2536
3081
|
selectedLocationIds,
|
|
2537
|
-
selectedConstraintIds,
|
|
3082
|
+
selectedConstraintIds: effectiveSelectedConstraintIds,
|
|
2538
3083
|
constraintValues,
|
|
2539
|
-
selectedOptimizationIds,
|
|
2540
|
-
optimizationValues
|
|
3084
|
+
selectedOptimizationIds: effectiveSelectedOptimizationIds,
|
|
3085
|
+
optimizationValues,
|
|
3086
|
+
selectedBuiltinIds: defaultSelectedBuiltinIds
|
|
2541
3087
|
});
|
|
2542
3088
|
}
|
|
2543
3089
|
onClose();
|
|
@@ -2548,31 +3094,75 @@ function AutoSchedulerModal({
|
|
|
2548
3094
|
),
|
|
2549
3095
|
/* @__PURE__ */ jsx3(Button2, { onClick: handleAdjustConstraints, sx: styles.failedPrimaryButton, children: "Adjust constraints" })
|
|
2550
3096
|
] }) : null;
|
|
2551
|
-
const
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
|
|
3097
|
+
const bodyStyles = showConfiguration ? styles.body : { ...styles.body, ...styles.creatingBody };
|
|
3098
|
+
const bodyContent = showConfiguration ? configurationContent : stepsContent;
|
|
3099
|
+
const modalContent = /* @__PURE__ */ jsxs3(
|
|
3100
|
+
Modal,
|
|
3101
|
+
{
|
|
3102
|
+
isOpen,
|
|
3103
|
+
onClose,
|
|
3104
|
+
isCentered: true,
|
|
3105
|
+
motionPreset: "scale",
|
|
3106
|
+
closeOnOverlayClick: false,
|
|
3107
|
+
closeOnEsc: false,
|
|
3108
|
+
trapFocus: false,
|
|
3109
|
+
children: [
|
|
3110
|
+
/* @__PURE__ */ jsx3(ModalOverlay, { sx: styles.overlay }),
|
|
3111
|
+
/* @__PURE__ */ jsxs3(ModalContent, { sx: styles.content, children: [
|
|
3112
|
+
/* @__PURE__ */ jsx3(ModalHeader, { sx: styles.header, children: /* @__PURE__ */ jsxs3(HStack2, { justify: "space-between", w: "full", children: [
|
|
3113
|
+
/* @__PURE__ */ jsx3(Text2, { sx: styles.title, children: modalTitle }),
|
|
3114
|
+
/* @__PURE__ */ jsx3(
|
|
3115
|
+
IconButton2,
|
|
3116
|
+
{
|
|
3117
|
+
"aria-label": "Close modal",
|
|
3118
|
+
variant: "ghost",
|
|
3119
|
+
size: "sm",
|
|
3120
|
+
icon: /* @__PURE__ */ jsx3(CloseIcon, {}),
|
|
3121
|
+
onClick: onClose,
|
|
3122
|
+
sx: styles.closeButton
|
|
3123
|
+
}
|
|
3124
|
+
)
|
|
3125
|
+
] }) }),
|
|
3126
|
+
/* @__PURE__ */ jsx3(ModalBody, { sx: bodyStyles, children: bodyContent }),
|
|
3127
|
+
footerButtons && /* @__PURE__ */ jsx3(ModalFooter, { sx: showFailed ? styles.failedFooter : styles.footer, children: footerButtons })
|
|
3128
|
+
] })
|
|
3129
|
+
]
|
|
3130
|
+
}
|
|
3131
|
+
);
|
|
3132
|
+
const drawerFooterNode = footerButtons ? /* @__PURE__ */ jsx3(
|
|
3133
|
+
HStack2,
|
|
3134
|
+
{
|
|
3135
|
+
align: "center",
|
|
3136
|
+
justify: "flex-end",
|
|
3137
|
+
px: 6,
|
|
3138
|
+
h: "68px",
|
|
3139
|
+
bg: "gray.50",
|
|
3140
|
+
borderTop: "1px solid",
|
|
3141
|
+
borderColor: "gray.200",
|
|
3142
|
+
gap: 2,
|
|
3143
|
+
flexShrink: 0,
|
|
3144
|
+
w: "full",
|
|
3145
|
+
children: footerButtons
|
|
3146
|
+
}
|
|
3147
|
+
) : void 0;
|
|
3148
|
+
const drawerContent = /* @__PURE__ */ jsx3(
|
|
3149
|
+
EtDrawer,
|
|
3150
|
+
{
|
|
3151
|
+
isOpen,
|
|
3152
|
+
onClose,
|
|
3153
|
+
title: modalTitle,
|
|
3154
|
+
closeOnOverlayClick: false,
|
|
3155
|
+
maxW: DRAWER_WIDTH_MAP[drawerSize],
|
|
3156
|
+
bodySx: bodyStyles,
|
|
3157
|
+
footer: drawerFooterNode,
|
|
3158
|
+
children: bodyContent
|
|
3159
|
+
}
|
|
3160
|
+
);
|
|
3161
|
+
const content = displayMode === "drawer" ? drawerContent : modalContent;
|
|
2572
3162
|
if (theme || cssVarsRoot) {
|
|
2573
|
-
return /* @__PURE__ */ jsx3(ChakraProvider, { theme: mergedTheme, cssVarsRoot, children:
|
|
3163
|
+
return /* @__PURE__ */ jsx3(ChakraProvider, { theme: mergedTheme, cssVarsRoot, children: content });
|
|
2574
3164
|
}
|
|
2575
|
-
return
|
|
3165
|
+
return content;
|
|
2576
3166
|
}
|
|
2577
3167
|
|
|
2578
3168
|
// src/AutoSchedulerModalWithProvider.tsx
|
|
@@ -2648,11 +3238,17 @@ var RequestError = class extends Error {
|
|
|
2648
3238
|
export {
|
|
2649
3239
|
AutoSchedulerModal,
|
|
2650
3240
|
AutoSchedulerModalWithProvider,
|
|
3241
|
+
InfeasibleScheduleError,
|
|
3242
|
+
MAXIMIZE_CONVERSION_RATE_RULE_ID,
|
|
2651
3243
|
RequestError,
|
|
3244
|
+
TRAFFIC_FORECAST_INCENTIVE_RULE_ID,
|
|
2652
3245
|
ViolatedConstraintsPanel,
|
|
2653
3246
|
ViolatedConstraintsPanelWithProvider,
|
|
2654
3247
|
buildRulesPayload,
|
|
2655
3248
|
buildSchedulePayload,
|
|
2656
|
-
|
|
3249
|
+
defaultSelectedScheduleDates,
|
|
3250
|
+
getFriendlyRuleName,
|
|
3251
|
+
getSequentialWeekDatesFromWeekStart,
|
|
3252
|
+
weekdayTwoLetterFromYmd
|
|
2657
3253
|
};
|
|
2658
3254
|
//# sourceMappingURL=index.js.map
|