@easyteam/auto-scheduler-modal-ui 0.1.3 → 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 +880 -314
- 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 +873 -312
- 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
|
}
|
|
@@ -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);
|
|
@@ -1716,6 +1792,7 @@ function ViolatedConstraintsPanel({
|
|
|
1716
1792
|
return;
|
|
1717
1793
|
}
|
|
1718
1794
|
if (recommendations !== null) {
|
|
1795
|
+
setShowRecommendations(true);
|
|
1719
1796
|
return;
|
|
1720
1797
|
}
|
|
1721
1798
|
if (!canFetchRecommendations) {
|
|
@@ -1836,13 +1913,78 @@ function ViolatedConstraintsPanel({
|
|
|
1836
1913
|
] });
|
|
1837
1914
|
}
|
|
1838
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
|
+
|
|
1839
1971
|
// src/AutoSchedulerModal.tsx
|
|
1840
|
-
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" };
|
|
1841
1980
|
function AutoSchedulerModal({
|
|
1981
|
+
displayMode,
|
|
1982
|
+
drawerSize = "lg",
|
|
1842
1983
|
baseURL,
|
|
1843
1984
|
isOpen,
|
|
1844
1985
|
title = "Auto-scheduling",
|
|
1845
|
-
|
|
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.",
|
|
1846
1988
|
jurisdictions,
|
|
1847
1989
|
openShifts,
|
|
1848
1990
|
employees,
|
|
@@ -1861,22 +2003,78 @@ function AutoSchedulerModal({
|
|
|
1861
2003
|
onSolution,
|
|
1862
2004
|
getStoredConfigForLocations,
|
|
1863
2005
|
persistConfigForLocations,
|
|
1864
|
-
errorToast
|
|
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
|
|
1865
2026
|
}) {
|
|
1866
2027
|
const baseTheme = useTheme();
|
|
1867
|
-
const { chakraOverride, styleOverrides } = useMemo2(
|
|
1868
|
-
() => resolveThemeParts(theme),
|
|
1869
|
-
[theme]
|
|
1870
|
-
);
|
|
2028
|
+
const { chakraOverride, styleOverrides } = useMemo2(() => resolveThemeParts(theme), [theme]);
|
|
1871
2029
|
const mergedTheme = useMemo2(
|
|
1872
2030
|
() => mergeThemeWithBase(baseTheme, chakraOverride),
|
|
1873
2031
|
[baseTheme, chakraOverride]
|
|
1874
2032
|
);
|
|
1875
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([]);
|
|
1876
2073
|
const [selectedConstraintIds, setSelectedConstraintIds] = useState2([]);
|
|
1877
2074
|
const [constraintValues, setConstraintValues] = useState2({});
|
|
1878
2075
|
const [selectedOptimizationIds, setSelectedOptimizationIds] = useState2([]);
|
|
1879
2076
|
const [optimizationValues, setOptimizationValues] = useState2({});
|
|
2077
|
+
const [selectedScheduleDates, setSelectedScheduleDates] = useState2([]);
|
|
1880
2078
|
const [screen, setScreen] = useState2("configure");
|
|
1881
2079
|
const [activeStepIndex, setActiveStepIndex] = useState2(0);
|
|
1882
2080
|
const [completedStepIndex, setCompletedStepIndex] = useState2(-1);
|
|
@@ -1885,12 +2083,19 @@ function AutoSchedulerModal({
|
|
|
1885
2083
|
const timersRef = useRef([]);
|
|
1886
2084
|
const pollIntervalRef = useRef(null);
|
|
1887
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
|
+
};
|
|
1888
2095
|
const selectedByJurisdiction = useMemo2(
|
|
1889
2096
|
() => jurisdictions.map((jurisdiction) => ({
|
|
1890
2097
|
...jurisdiction,
|
|
1891
|
-
selectedLocations: jurisdiction.locations.filter(
|
|
1892
|
-
(location) => selectedLocationIds.includes(location.id)
|
|
1893
|
-
)
|
|
2098
|
+
selectedLocations: jurisdiction.locations.filter((location) => selectedLocationIds.includes(location.id))
|
|
1894
2099
|
})).filter((jurisdiction) => jurisdiction.selectedLocations.length > 0),
|
|
1895
2100
|
[jurisdictions, selectedLocationIds]
|
|
1896
2101
|
);
|
|
@@ -1899,43 +2104,84 @@ function AutoSchedulerModal({
|
|
|
1899
2104
|
0
|
|
1900
2105
|
);
|
|
1901
2106
|
const locationIdsWithOpenShifts = useMemo2(() => {
|
|
1902
|
-
return new Set(
|
|
1903
|
-
openShifts.filter((shift) => !shift.employeeId).map((shift) => shift.location.id)
|
|
1904
|
-
);
|
|
2107
|
+
return new Set(openShifts.filter((shift) => !shift.employeeId).map((shift) => shift.location.id));
|
|
1905
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]);
|
|
1906
2128
|
const selectedConstraints = useMemo2(
|
|
1907
|
-
() =>
|
|
1908
|
-
[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]
|
|
1909
2137
|
);
|
|
1910
2138
|
const optimizationOptions = useMemo2(
|
|
1911
|
-
() =>
|
|
2139
|
+
() => effectiveSoftConstraintGroups.flatMap(
|
|
1912
2140
|
(group) => group.options.map((option) => ({
|
|
1913
2141
|
...option,
|
|
1914
2142
|
groupId: group.id,
|
|
1915
2143
|
groupName: group.name
|
|
1916
2144
|
}))
|
|
1917
2145
|
),
|
|
1918
|
-
[]
|
|
2146
|
+
[effectiveSoftConstraintGroups]
|
|
1919
2147
|
);
|
|
1920
2148
|
const selectedOptimizationOptions = useMemo2(
|
|
2149
|
+
() => optimizationOptions.filter((option) => selectedOptimizationIds.includes(option.id)),
|
|
2150
|
+
[optimizationOptions, selectedOptimizationIds]
|
|
2151
|
+
);
|
|
2152
|
+
const selectedOptimizationDisplayOptions = useMemo2(
|
|
1921
2153
|
() => optimizationOptions.filter(
|
|
1922
|
-
(option) => selectedOptimizationIds.includes(option.id)
|
|
2154
|
+
(option) => selectedOptimizationIds.includes(option.id) || option.readOnly && option.defaultSelected
|
|
1923
2155
|
),
|
|
1924
2156
|
[optimizationOptions, selectedOptimizationIds]
|
|
1925
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
|
+
);
|
|
1926
2176
|
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
|
-
);
|
|
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
|
+
});
|
|
1939
2185
|
return hasEmptyConstraint || hasEmptyWeight;
|
|
1940
2186
|
}, [selectedConstraints, constraintValues, selectedOptimizationOptions, optimizationValues]);
|
|
1941
2187
|
const allocationSummary = useMemo2(() => {
|
|
@@ -1969,13 +2215,25 @@ function AutoSchedulerModal({
|
|
|
1969
2215
|
setScreen("configure");
|
|
1970
2216
|
setLastFailure(null);
|
|
1971
2217
|
setSelectedLocationIds([]);
|
|
2218
|
+
setSelectedSubLocationIds([]);
|
|
2219
|
+
setSelectedTeamIds([]);
|
|
1972
2220
|
setSelectedOptimizationIds([]);
|
|
1973
2221
|
setSelectedConstraintIds([]);
|
|
1974
2222
|
setConstraintValues({});
|
|
1975
2223
|
setOptimizationValues({});
|
|
2224
|
+
setSelectedScheduleDates([]);
|
|
1976
2225
|
resetProgress();
|
|
1977
2226
|
}
|
|
1978
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]);
|
|
1979
2237
|
useEffect(() => {
|
|
1980
2238
|
if (isOpen && initialConfig) {
|
|
1981
2239
|
setSelectedLocationIds(initialConfig.selectedLocationIds);
|
|
@@ -2008,11 +2266,12 @@ function AutoSchedulerModal({
|
|
|
2008
2266
|
}, [selectedLocationIds, initialConfig, getStoredConfigForLocations]);
|
|
2009
2267
|
useEffect(() => () => clearTimers(), []);
|
|
2010
2268
|
useEffect(() => {
|
|
2269
|
+
if (onGenerate) return;
|
|
2011
2270
|
setSelectedLocationIds((previous) => {
|
|
2012
2271
|
const filtered = previous.filter((id) => locationIdsWithOpenShifts.has(id));
|
|
2013
2272
|
return filtered.length === previous.length ? previous : filtered;
|
|
2014
2273
|
});
|
|
2015
|
-
}, [locationIdsWithOpenShifts]);
|
|
2274
|
+
}, [locationIdsWithOpenShifts, onGenerate]);
|
|
2016
2275
|
const stepStatusForIndex = (index) => {
|
|
2017
2276
|
if (index <= completedStepIndex) {
|
|
2018
2277
|
return "done";
|
|
@@ -2040,144 +2299,249 @@ function AutoSchedulerModal({
|
|
|
2040
2299
|
timersRef.current.push(timerId);
|
|
2041
2300
|
};
|
|
2042
2301
|
const afterSuccessSteps = (runId, response) => {
|
|
2043
|
-
schedule(
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
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);
|
|
2058
2316
|
setActiveStepIndex(4);
|
|
2059
|
-
}
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
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
|
+
);
|
|
2075
2346
|
};
|
|
2076
2347
|
const handleCreateSchedule = () => {
|
|
2077
2348
|
if (selectedCount === 0) {
|
|
2078
2349
|
return;
|
|
2079
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
|
+
}
|
|
2080
2425
|
const payload = buildSchedulePayload({
|
|
2081
2426
|
employees,
|
|
2082
2427
|
openShifts,
|
|
2083
2428
|
timeOffs,
|
|
2084
2429
|
selectedLocationIds,
|
|
2085
2430
|
jurisdictions,
|
|
2086
|
-
selectedConstraintIds,
|
|
2431
|
+
selectedConstraintIds: effectiveSelectedConstraintIds,
|
|
2087
2432
|
constraintValues,
|
|
2088
|
-
selectedOptimizationIds,
|
|
2089
|
-
optimizationValues
|
|
2433
|
+
selectedOptimizationIds: effectiveSelectedOptimizationIds,
|
|
2434
|
+
optimizationValues,
|
|
2435
|
+
trafficForecast,
|
|
2436
|
+
constraintDefinitionIds,
|
|
2437
|
+
constraintDefinitions,
|
|
2438
|
+
organizationId
|
|
2090
2439
|
});
|
|
2091
2440
|
persistConfigForLocations == null ? void 0 : persistConfigForLocations(selectedLocationIds, {
|
|
2092
2441
|
selectedLocationIds,
|
|
2093
|
-
selectedConstraintIds,
|
|
2442
|
+
selectedConstraintIds: effectiveSelectedConstraintIds,
|
|
2094
2443
|
constraintValues,
|
|
2095
|
-
selectedOptimizationIds,
|
|
2096
|
-
optimizationValues
|
|
2444
|
+
selectedOptimizationIds: effectiveSelectedOptimizationIds,
|
|
2445
|
+
optimizationValues,
|
|
2446
|
+
selectedBuiltinIds: defaultSelectedBuiltinIds
|
|
2097
2447
|
});
|
|
2098
2448
|
const runId = resetBeforeSubmit();
|
|
2099
|
-
schedule(
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
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 () => {
|
|
2118
2472
|
if (runIdRef.current !== runId) return;
|
|
2119
|
-
|
|
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;
|
|
2120
2494
|
if (pollIntervalRef.current) {
|
|
2121
2495
|
clearInterval(pollIntervalRef.current);
|
|
2122
2496
|
pollIntervalRef.current = null;
|
|
2123
2497
|
}
|
|
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;
|
|
2498
|
+
console.error(err);
|
|
2499
|
+
setLastFailure(NETWORK_ERROR_FAILURE);
|
|
2500
|
+
setScreen("failed");
|
|
2140
2501
|
}
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2502
|
+
};
|
|
2503
|
+
pollForResult();
|
|
2504
|
+
pollIntervalRef.current = setInterval(pollForResult, POLL_INTERVAL_MS);
|
|
2505
|
+
} catch (err) {
|
|
2506
|
+
if (runIdRef.current !== runId) {
|
|
2507
|
+
return;
|
|
2144
2508
|
}
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
} catch (err) {
|
|
2149
|
-
if (runIdRef.current !== runId) {
|
|
2150
|
-
return;
|
|
2509
|
+
console.error(err);
|
|
2510
|
+
setLastFailure(NETWORK_ERROR_FAILURE);
|
|
2511
|
+
setScreen("failed");
|
|
2151
2512
|
}
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
}
|
|
2156
|
-
}, runId);
|
|
2513
|
+
},
|
|
2514
|
+
runId
|
|
2515
|
+
);
|
|
2157
2516
|
};
|
|
2158
2517
|
const handleSolveResult = (runId, response) => {
|
|
2159
|
-
var _a
|
|
2518
|
+
var _a;
|
|
2160
2519
|
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
|
-
);
|
|
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));
|
|
2168
2522
|
const recommendationPayload = generateRecommendationsURLAndHeaders ? {
|
|
2169
2523
|
context: {
|
|
2170
2524
|
selectedLocationIds,
|
|
2171
|
-
selectedConstraintIds,
|
|
2525
|
+
selectedConstraintIds: effectiveSelectedConstraintIds,
|
|
2172
2526
|
constraintValues,
|
|
2173
|
-
selectedOptimizationIds,
|
|
2527
|
+
selectedOptimizationIds: effectiveSelectedOptimizationIds,
|
|
2174
2528
|
optimizationValues,
|
|
2175
|
-
timezone:
|
|
2529
|
+
timezone: selectedLocationTimezone
|
|
2176
2530
|
},
|
|
2177
2531
|
locations: selectedLocations,
|
|
2178
2532
|
employees,
|
|
2179
|
-
openShifts: openShifts.filter(
|
|
2180
|
-
|
|
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
|
+
),
|
|
2181
2545
|
timeOffs: timeOffs != null ? timeOffs : [],
|
|
2182
2546
|
explanation: response.explanation
|
|
2183
2547
|
} : void 0;
|
|
@@ -2196,17 +2560,34 @@ function AutoSchedulerModal({
|
|
|
2196
2560
|
setScreen("configure");
|
|
2197
2561
|
};
|
|
2198
2562
|
const handleToggleLocation = (locationId) => {
|
|
2199
|
-
if (!locationIdsWithOpenShifts.has(locationId)) {
|
|
2563
|
+
if (!onGenerate && !locationIdsWithOpenShifts.has(locationId)) {
|
|
2200
2564
|
return;
|
|
2201
2565
|
}
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
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);
|
|
2205
2574
|
}
|
|
2206
|
-
|
|
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]);
|
|
2207
2587
|
};
|
|
2208
2588
|
const handleToggleConstraint = (constraintId) => {
|
|
2209
2589
|
setSelectedConstraintIds((prev) => {
|
|
2590
|
+
var _a;
|
|
2210
2591
|
if (prev.includes(constraintId)) {
|
|
2211
2592
|
setConstraintValues((values) => {
|
|
2212
2593
|
const next = { ...values };
|
|
@@ -2215,10 +2596,11 @@ function AutoSchedulerModal({
|
|
|
2215
2596
|
});
|
|
2216
2597
|
return prev.filter((id) => id !== constraintId);
|
|
2217
2598
|
}
|
|
2218
|
-
|
|
2599
|
+
const defaultVal = (_a = effectiveHardConstraintOptions.find((o) => o.id === constraintId)) == null ? void 0 : _a.defaultValue;
|
|
2600
|
+
if (defaultVal) {
|
|
2219
2601
|
setConstraintValues((values) => ({
|
|
2220
2602
|
...values,
|
|
2221
|
-
[constraintId]:
|
|
2603
|
+
[constraintId]: defaultVal
|
|
2222
2604
|
}));
|
|
2223
2605
|
}
|
|
2224
2606
|
return [...prev, constraintId];
|
|
@@ -2270,12 +2652,12 @@ function AutoSchedulerModal({
|
|
|
2270
2652
|
const configurationContent = /* @__PURE__ */ jsxs3(Stack2, { spacing: "20px", children: [
|
|
2271
2653
|
/* @__PURE__ */ jsxs3(Box2, { sx: styles.banner, children: [
|
|
2272
2654
|
/* @__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:
|
|
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 })
|
|
2276
2658
|
] })
|
|
2277
2659
|
] }),
|
|
2278
|
-
openShifts.length === 0 ? /* @__PURE__ */ jsxs3(
|
|
2660
|
+
!onGenerate && openShifts.length === 0 ? /* @__PURE__ */ jsxs3(
|
|
2279
2661
|
Box2,
|
|
2280
2662
|
{
|
|
2281
2663
|
sx: {
|
|
@@ -2318,30 +2700,33 @@ function AutoSchedulerModal({
|
|
|
2318
2700
|
] }) }) }),
|
|
2319
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: [
|
|
2320
2702
|
/* @__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
|
-
|
|
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
|
+
]
|
|
2329
2726
|
},
|
|
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
|
-
)) })
|
|
2727
|
+
location.id
|
|
2728
|
+
);
|
|
2729
|
+
}) })
|
|
2345
2730
|
] }, jurisdiction.id)) }) }) })
|
|
2346
2731
|
] }),
|
|
2347
2732
|
/* @__PURE__ */ jsx3(Stack2, { spacing: "8px", children: selectedByJurisdiction.map((jurisdiction) => /* @__PURE__ */ jsxs3(Box2, { sx: styles.selectedBanner, children: [
|
|
@@ -2355,11 +2740,128 @@ function AutoSchedulerModal({
|
|
|
2355
2740
|
] })
|
|
2356
2741
|
] }, jurisdiction.id)) })
|
|
2357
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,
|
|
2358
2860
|
/* @__PURE__ */ jsxs3(Stack2, { spacing: "16px", sx: styles.hardConstraintsSection, children: [
|
|
2359
2861
|
/* @__PURE__ */ jsxs3(Stack2, { spacing: "4px", children: [
|
|
2360
2862
|
/* @__PURE__ */ jsxs3(HStack2, { sx: styles.hardConstraintsLabelRow, children: [
|
|
2361
2863
|
/* @__PURE__ */ jsx3(Text2, { sx: styles.hardConstraintsLabel, children: "Required rules" }),
|
|
2362
|
-
|
|
2864
|
+
selectedConstraintDisplayOptions.length > 0 ? /* @__PURE__ */ jsx3(Badge, { sx: styles.hardConstraintsBadge, children: selectedConstraintDisplayOptions.length }) : null
|
|
2363
2865
|
] }),
|
|
2364
2866
|
/* @__PURE__ */ jsx3(Text2, { sx: styles.hardConstraintsSubtitle, children: "Assigned shifts will be required to meet the following rules:" })
|
|
2365
2867
|
] }),
|
|
@@ -2371,35 +2873,30 @@ function AutoSchedulerModal({
|
|
|
2371
2873
|
{
|
|
2372
2874
|
sx: {
|
|
2373
2875
|
...styles.hardConstraintsTriggerText,
|
|
2374
|
-
...
|
|
2876
|
+
...selectedConstraintDisplayOptions.length > 0 ? { color: "#303030" } : {}
|
|
2375
2877
|
},
|
|
2376
2878
|
noOfLines: 1,
|
|
2377
|
-
children:
|
|
2879
|
+
children: selectedConstraintDisplayOptions.length > 0 ? selectedConstraintDisplayOptions.map((o) => o.name).join(", ") : "Select rules"
|
|
2378
2880
|
}
|
|
2379
2881
|
),
|
|
2380
2882
|
/* @__PURE__ */ jsx3(Box2, { color: "#5c5c5c", children: /* @__PURE__ */ jsx3(ChevronDownIcon, {}) })
|
|
2381
2883
|
] }) }) }),
|
|
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
|
-
)) }) }) })
|
|
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)) }) }) })
|
|
2401
2898
|
] }),
|
|
2402
|
-
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) => {
|
|
2403
2900
|
var _a;
|
|
2404
2901
|
return /* @__PURE__ */ jsxs3(Stack2, { sx: styles.hardConstraintsInputItem, children: [
|
|
2405
2902
|
/* @__PURE__ */ jsxs3(Text2, { sx: styles.hardConstraintsInputLabel, children: [
|
|
@@ -2417,13 +2914,13 @@ function AutoSchedulerModal({
|
|
|
2417
2914
|
...prev,
|
|
2418
2915
|
[option.id]: event.target.value
|
|
2419
2916
|
})),
|
|
2420
|
-
min: option.
|
|
2421
|
-
max: option.
|
|
2422
|
-
step: option.
|
|
2917
|
+
min: option.min,
|
|
2918
|
+
max: option.max,
|
|
2919
|
+
step: option.max != null ? void 0 : "any",
|
|
2423
2920
|
sx: styles.hardConstraintsInputField
|
|
2424
2921
|
}
|
|
2425
2922
|
),
|
|
2426
|
-
/* @__PURE__ */ jsx3(InputRightAddon, { sx: styles.optimizationSuffix, children: option.
|
|
2923
|
+
/* @__PURE__ */ jsx3(InputRightAddon, { sx: styles.optimizationSuffix, children: option.suffix })
|
|
2427
2924
|
] })
|
|
2428
2925
|
] }, option.id);
|
|
2429
2926
|
}) }) }) : null
|
|
@@ -2432,89 +2929,100 @@ function AutoSchedulerModal({
|
|
|
2432
2929
|
/* @__PURE__ */ jsxs3(Stack2, { spacing: "4px", children: [
|
|
2433
2930
|
/* @__PURE__ */ jsxs3(HStack2, { sx: styles.optimizationLabelRow, children: [
|
|
2434
2931
|
/* @__PURE__ */ jsx3(Text2, { sx: styles.optimizationLabel, children: "Optimization" }),
|
|
2435
|
-
|
|
2932
|
+
selectedOptimizationDisplayOptions.length > 0 ? /* @__PURE__ */ jsx3(Badge, { sx: styles.optimizationBadge, children: selectedOptimizationDisplayOptions.length }) : null
|
|
2436
2933
|
] }),
|
|
2437
2934
|
/* @__PURE__ */ jsx3(Text2, { sx: styles.optimizationSubtitle, children: "Your schedule will be optimized based on the following priorities:" })
|
|
2438
2935
|
] }),
|
|
2439
|
-
/* @__PURE__ */ jsxs3(Popover, { placement: "
|
|
2936
|
+
/* @__PURE__ */ jsxs3(Popover, { placement: "top-start", strategy: "fixed", matchWidth: true, isLazy: true, gutter: 4, children: [
|
|
2440
2937
|
/* @__PURE__ */ jsx3(PopoverTrigger, { children: /* @__PURE__ */ jsx3(Button2, { variant: "unstyled", sx: styles.optimizationTrigger, children: /* @__PURE__ */ jsxs3(HStack2, { justify: "space-between", w: "full", children: [
|
|
2441
2938
|
/* @__PURE__ */ jsx3(
|
|
2442
2939
|
Text2,
|
|
2443
2940
|
{
|
|
2444
2941
|
sx: {
|
|
2445
2942
|
...styles.optimizationTriggerText,
|
|
2446
|
-
...
|
|
2943
|
+
...selectedOptimizationDisplayOptions.length > 0 ? { color: "#303030" } : {}
|
|
2447
2944
|
},
|
|
2448
2945
|
noOfLines: 1,
|
|
2449
|
-
children:
|
|
2946
|
+
children: selectedOptimizationDisplayOptions.length > 0 ? selectedOptimizationDisplayOptions.map((o) => o.title).join(", ") : "Select optimization priorities"
|
|
2450
2947
|
}
|
|
2451
2948
|
),
|
|
2452
2949
|
/* @__PURE__ */ jsx3(Box2, { color: "#5c5c5c", children: /* @__PURE__ */ jsx3(ChevronDownIcon, {}) })
|
|
2453
2950
|
] }) }) }),
|
|
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
|
-
]
|
|
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" }
|
|
2477
2962
|
},
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
|
|
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
|
+
) })
|
|
2481
2996
|
] }),
|
|
2482
2997
|
selectedOptimizationOptions.length >= 2 ? /* @__PURE__ */ jsxs3(Box2, { sx: styles.optimizationInputsContainer, children: [
|
|
2483
2998
|
/* @__PURE__ */ jsx3(Stack2, { spacing: "16px", children: selectedOptimizationOptions.map((option, index) => {
|
|
2484
2999
|
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
|
-
);
|
|
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);
|
|
2518
3026
|
}) }),
|
|
2519
3027
|
/* @__PURE__ */ jsx3(Divider, { sx: { ...styles.optimizationDivider, my: "16px" } }),
|
|
2520
3028
|
/* @__PURE__ */ jsx3(Text2, { sx: styles.optimizationSummary, children: allocationSummary })
|
|
@@ -2523,7 +3031,9 @@ function AutoSchedulerModal({
|
|
|
2523
3031
|
] });
|
|
2524
3032
|
const stepsContent = /* @__PURE__ */ jsxs3(Stack2, { spacing: "16px", sx: styles.stepsContainer, children: [
|
|
2525
3033
|
showFailed ? violationPanel : null,
|
|
2526
|
-
/* @__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;
|
|
2527
3037
|
const status = stepStatusForIndex(index);
|
|
2528
3038
|
const isFailedStep = showFailed && index === activeStepIndex;
|
|
2529
3039
|
const iconStyles = {
|
|
@@ -2541,26 +3051,26 @@ function AutoSchedulerModal({
|
|
|
2541
3051
|
...isFailedStep ? styles.stepTitleFailed : null
|
|
2542
3052
|
};
|
|
2543
3053
|
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:
|
|
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 }),
|
|
2545
3055
|
/* @__PURE__ */ jsxs3(Box2, { children: [
|
|
2546
3056
|
/* @__PURE__ */ jsx3(Text2, { sx: titleStyles, children: step.title }),
|
|
2547
3057
|
status === "active" && step.subtitle ? /* @__PURE__ */ jsx3(Text2, { sx: styles.stepSubtitle, children: step.subtitle }) : null
|
|
2548
3058
|
] })
|
|
2549
|
-
] },
|
|
3059
|
+
] }, rawStep.title);
|
|
2550
3060
|
}) })
|
|
2551
3061
|
] });
|
|
2552
|
-
const
|
|
3062
|
+
const footerButtons = showConfiguration ? /* @__PURE__ */ jsxs3(Fragment, { children: [
|
|
2553
3063
|
/* @__PURE__ */ jsx3(Button2, { variant: "outline", onClick: onClose, sx: styles.cancelButton, children: cancelLabel }),
|
|
2554
3064
|
/* @__PURE__ */ jsx3(
|
|
2555
3065
|
Button2,
|
|
2556
3066
|
{
|
|
2557
3067
|
onClick: handleCreateSchedule,
|
|
2558
|
-
isDisabled: selectedCount === 0 || openShifts.length === 0 || hasEmptyInputs,
|
|
3068
|
+
isDisabled: selectedCount === 0 || !onGenerate && openShifts.length === 0 || hasEmptyInputs || Boolean(scheduleWeekStartYmd) && selectedScheduleDates.length === 0,
|
|
2559
3069
|
sx: styles.primaryButton,
|
|
2560
3070
|
children: primaryActionLabel
|
|
2561
3071
|
}
|
|
2562
3072
|
)
|
|
2563
|
-
] }) : showFailed ? /* @__PURE__ */ jsxs3(
|
|
3073
|
+
] }) : showFailed ? /* @__PURE__ */ jsxs3(Fragment, { children: [
|
|
2564
3074
|
/* @__PURE__ */ jsx3(
|
|
2565
3075
|
Button2,
|
|
2566
3076
|
{
|
|
@@ -2569,10 +3079,11 @@ function AutoSchedulerModal({
|
|
|
2569
3079
|
if (lastFailure) {
|
|
2570
3080
|
onFixManually == null ? void 0 : onFixManually(lastFailure, {
|
|
2571
3081
|
selectedLocationIds,
|
|
2572
|
-
selectedConstraintIds,
|
|
3082
|
+
selectedConstraintIds: effectiveSelectedConstraintIds,
|
|
2573
3083
|
constraintValues,
|
|
2574
|
-
selectedOptimizationIds,
|
|
2575
|
-
optimizationValues
|
|
3084
|
+
selectedOptimizationIds: effectiveSelectedOptimizationIds,
|
|
3085
|
+
optimizationValues,
|
|
3086
|
+
selectedBuiltinIds: defaultSelectedBuiltinIds
|
|
2576
3087
|
});
|
|
2577
3088
|
}
|
|
2578
3089
|
onClose();
|
|
@@ -2583,31 +3094,75 @@ function AutoSchedulerModal({
|
|
|
2583
3094
|
),
|
|
2584
3095
|
/* @__PURE__ */ jsx3(Button2, { onClick: handleAdjustConstraints, sx: styles.failedPrimaryButton, children: "Adjust constraints" })
|
|
2585
3096
|
] }) : null;
|
|
2586
|
-
const
|
|
2587
|
-
|
|
2588
|
-
|
|
2589
|
-
|
|
2590
|
-
|
|
2591
|
-
|
|
2592
|
-
|
|
2593
|
-
|
|
2594
|
-
|
|
2595
|
-
|
|
2596
|
-
|
|
2597
|
-
|
|
2598
|
-
|
|
2599
|
-
|
|
2600
|
-
|
|
2601
|
-
|
|
2602
|
-
|
|
2603
|
-
|
|
2604
|
-
|
|
2605
|
-
|
|
2606
|
-
|
|
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;
|
|
2607
3162
|
if (theme || cssVarsRoot) {
|
|
2608
|
-
return /* @__PURE__ */ jsx3(ChakraProvider, { theme: mergedTheme, cssVarsRoot, children:
|
|
3163
|
+
return /* @__PURE__ */ jsx3(ChakraProvider, { theme: mergedTheme, cssVarsRoot, children: content });
|
|
2609
3164
|
}
|
|
2610
|
-
return
|
|
3165
|
+
return content;
|
|
2611
3166
|
}
|
|
2612
3167
|
|
|
2613
3168
|
// src/AutoSchedulerModalWithProvider.tsx
|
|
@@ -2683,11 +3238,17 @@ var RequestError = class extends Error {
|
|
|
2683
3238
|
export {
|
|
2684
3239
|
AutoSchedulerModal,
|
|
2685
3240
|
AutoSchedulerModalWithProvider,
|
|
3241
|
+
InfeasibleScheduleError,
|
|
3242
|
+
MAXIMIZE_CONVERSION_RATE_RULE_ID,
|
|
2686
3243
|
RequestError,
|
|
3244
|
+
TRAFFIC_FORECAST_INCENTIVE_RULE_ID,
|
|
2687
3245
|
ViolatedConstraintsPanel,
|
|
2688
3246
|
ViolatedConstraintsPanelWithProvider,
|
|
2689
3247
|
buildRulesPayload,
|
|
2690
3248
|
buildSchedulePayload,
|
|
2691
|
-
|
|
3249
|
+
defaultSelectedScheduleDates,
|
|
3250
|
+
getFriendlyRuleName,
|
|
3251
|
+
getSequentialWeekDatesFromWeekStart,
|
|
3252
|
+
weekdayTwoLetterFromYmd
|
|
2692
3253
|
};
|
|
2693
3254
|
//# sourceMappingURL=index.js.map
|