@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.cjs
CHANGED
|
@@ -29,22 +29,38 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
29
29
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
30
30
|
|
|
31
31
|
// src/index.ts
|
|
32
|
-
var
|
|
33
|
-
__export(
|
|
32
|
+
var index_exports = {};
|
|
33
|
+
__export(index_exports, {
|
|
34
34
|
AutoSchedulerModal: () => AutoSchedulerModal,
|
|
35
35
|
AutoSchedulerModalWithProvider: () => AutoSchedulerModalWithProvider,
|
|
36
|
+
InfeasibleScheduleError: () => InfeasibleScheduleError,
|
|
37
|
+
MAXIMIZE_CONVERSION_RATE_RULE_ID: () => MAXIMIZE_CONVERSION_RATE_RULE_ID,
|
|
36
38
|
RequestError: () => RequestError,
|
|
39
|
+
TRAFFIC_FORECAST_INCENTIVE_RULE_ID: () => TRAFFIC_FORECAST_INCENTIVE_RULE_ID,
|
|
37
40
|
ViolatedConstraintsPanel: () => ViolatedConstraintsPanel,
|
|
38
41
|
ViolatedConstraintsPanelWithProvider: () => ViolatedConstraintsPanelWithProvider,
|
|
39
42
|
buildRulesPayload: () => buildRulesPayload,
|
|
40
43
|
buildSchedulePayload: () => buildSchedulePayload,
|
|
41
|
-
|
|
44
|
+
defaultSelectedScheduleDates: () => defaultSelectedScheduleDates,
|
|
45
|
+
getFriendlyRuleName: () => getFriendlyRuleName,
|
|
46
|
+
getSequentialWeekDatesFromWeekStart: () => getSequentialWeekDatesFromWeekStart,
|
|
47
|
+
weekdayTwoLetterFromYmd: () => weekdayTwoLetterFromYmd
|
|
42
48
|
});
|
|
43
|
-
module.exports = __toCommonJS(
|
|
49
|
+
module.exports = __toCommonJS(index_exports);
|
|
44
50
|
|
|
45
51
|
// src/AutoSchedulerModal.tsx
|
|
46
52
|
var import_react3 = require("react");
|
|
47
53
|
var import_react4 = require("@chakra-ui/react");
|
|
54
|
+
var import_web_ui = require("@easyteam/web-ui");
|
|
55
|
+
|
|
56
|
+
// src/types.ts
|
|
57
|
+
var InfeasibleScheduleError = class extends Error {
|
|
58
|
+
constructor(failure) {
|
|
59
|
+
super("Schedule could not be created due to constraint violations");
|
|
60
|
+
this.name = "InfeasibleScheduleError";
|
|
61
|
+
this.failure = failure;
|
|
62
|
+
}
|
|
63
|
+
};
|
|
48
64
|
|
|
49
65
|
// src/icons/AutoSchedulerIcons.tsx
|
|
50
66
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
@@ -181,19 +197,15 @@ function CheckIcon(props) {
|
|
|
181
197
|
"svg",
|
|
182
198
|
{
|
|
183
199
|
"aria-hidden": "true",
|
|
184
|
-
viewBox: "0 0
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
height: "20",
|
|
200
|
+
viewBox: "0 0 24 24",
|
|
201
|
+
width: "24",
|
|
202
|
+
height: "24",
|
|
188
203
|
...props,
|
|
189
204
|
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
190
205
|
"path",
|
|
191
206
|
{
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
strokeWidth: "1.6",
|
|
195
|
-
strokeLinecap: "round",
|
|
196
|
-
strokeLinejoin: "round"
|
|
207
|
+
fill: "currentColor",
|
|
208
|
+
d: "M20.285 2l-11.285 11.567-5.286-5.011-3.714 3.716 9 8.728 15-15.285z"
|
|
197
209
|
}
|
|
198
210
|
)
|
|
199
211
|
}
|
|
@@ -715,7 +727,7 @@ var defaultStyles = {
|
|
|
715
727
|
},
|
|
716
728
|
stepIconDone: {
|
|
717
729
|
border: "none",
|
|
718
|
-
bg: "#
|
|
730
|
+
bg: "#5338ff",
|
|
719
731
|
color: "white",
|
|
720
732
|
borderRadius: "10px"
|
|
721
733
|
},
|
|
@@ -1251,6 +1263,9 @@ var UI_TO_BACKEND_RULE_ID = {
|
|
|
1251
1263
|
maximize_sales: "maximize-sales-performance",
|
|
1252
1264
|
staff_preferences: "staff-open-shift-preferences"
|
|
1253
1265
|
};
|
|
1266
|
+
var MAXIMIZE_CONVERSION_RATE_RULE_ID = "maximize-conversion-rate";
|
|
1267
|
+
var TRAFFIC_FORECAST_INCENTIVE_RULE_ID = "traffic-forecast-incentive";
|
|
1268
|
+
var SALES_OPTIMIZATION_UI_IDS = /* @__PURE__ */ new Set(["maximize_sales", "cost_to_sales_ratio"]);
|
|
1254
1269
|
var BACKEND_TO_UI_RULE_ID = Object.fromEntries(
|
|
1255
1270
|
Object.entries(UI_TO_BACKEND_RULE_ID).map(([ui, backend]) => [backend, ui])
|
|
1256
1271
|
);
|
|
@@ -1327,11 +1342,24 @@ function hasAnyParameterOverrides(selectedConstraintIds, constraintValues, selec
|
|
|
1327
1342
|
}
|
|
1328
1343
|
return false;
|
|
1329
1344
|
}
|
|
1330
|
-
function
|
|
1345
|
+
function appendSiblingRuleId(ruleIds, siblingId) {
|
|
1346
|
+
if (!ruleIds.includes(siblingId)) {
|
|
1347
|
+
ruleIds.push(siblingId);
|
|
1348
|
+
}
|
|
1349
|
+
}
|
|
1350
|
+
function buildRulesPayload(selectedConstraintIds, constraintValues, selectedOptimizationIds, optimizationValues, siblings = {}) {
|
|
1331
1351
|
const allSelectedRuleIds = [
|
|
1332
1352
|
...selectedConstraintIds.map(toBackendRuleId),
|
|
1333
1353
|
...selectedOptimizationIds.map(toBackendRuleId)
|
|
1334
1354
|
].filter((id) => Boolean(id));
|
|
1355
|
+
const salesOptSelected = selectedOptimizationIds.some((id) => SALES_OPTIMIZATION_UI_IDS.has(id));
|
|
1356
|
+
const maximizeSalesSelected = selectedOptimizationIds.includes("maximize_sales");
|
|
1357
|
+
if (siblings.includeConversionRate && salesOptSelected) {
|
|
1358
|
+
appendSiblingRuleId(allSelectedRuleIds, MAXIMIZE_CONVERSION_RATE_RULE_ID);
|
|
1359
|
+
}
|
|
1360
|
+
if (siblings.includeTrafficIncentive && maximizeSalesSelected) {
|
|
1361
|
+
appendSiblingRuleId(allSelectedRuleIds, TRAFFIC_FORECAST_INCENTIVE_RULE_ID);
|
|
1362
|
+
}
|
|
1335
1363
|
if (allSelectedRuleIds.length === 0) {
|
|
1336
1364
|
return {};
|
|
1337
1365
|
}
|
|
@@ -1400,9 +1428,25 @@ function toApiLocation(location) {
|
|
|
1400
1428
|
id: String(location.id),
|
|
1401
1429
|
timezone: (_a = location.timezone) != null ? _a : void 0,
|
|
1402
1430
|
jurisdiction: (_b = location.jurisdiction) != null ? _b : void 0,
|
|
1403
|
-
tags: "tags" in location ? location.tags : void 0
|
|
1431
|
+
tags: "tags" in location ? location.tags : void 0,
|
|
1432
|
+
properties: location.properties ? location.properties : void 0
|
|
1404
1433
|
};
|
|
1405
1434
|
}
|
|
1435
|
+
function employeeHasConversionRate(employee) {
|
|
1436
|
+
var _a;
|
|
1437
|
+
const value = (_a = employee.properties) == null ? void 0 : _a.conversionRate;
|
|
1438
|
+
return typeof value === "number" && Number.isFinite(value) && value > 0;
|
|
1439
|
+
}
|
|
1440
|
+
function employeeTouchesSelectedLocations(employee, selectedLocationIds) {
|
|
1441
|
+
var _a;
|
|
1442
|
+
const ids = (_a = employee.locationIds) != null ? _a : [];
|
|
1443
|
+
return ids.some((id) => selectedLocationIds.includes(String(id)));
|
|
1444
|
+
}
|
|
1445
|
+
function hasConversionRateInSelectedLocations(employees, selectedLocationIds) {
|
|
1446
|
+
return employees.some(
|
|
1447
|
+
(employee) => employeeTouchesSelectedLocations(employee, selectedLocationIds) && employeeHasConversionRate(employee)
|
|
1448
|
+
);
|
|
1449
|
+
}
|
|
1406
1450
|
function buildSchedulePayload(input) {
|
|
1407
1451
|
const {
|
|
1408
1452
|
employees,
|
|
@@ -1413,7 +1457,11 @@ function buildSchedulePayload(input) {
|
|
|
1413
1457
|
selectedConstraintIds,
|
|
1414
1458
|
constraintValues,
|
|
1415
1459
|
selectedOptimizationIds,
|
|
1416
|
-
optimizationValues
|
|
1460
|
+
optimizationValues,
|
|
1461
|
+
trafficForecast,
|
|
1462
|
+
constraintDefinitionIds,
|
|
1463
|
+
constraintDefinitions,
|
|
1464
|
+
organizationId
|
|
1417
1465
|
} = input;
|
|
1418
1466
|
const locationsMap = /* @__PURE__ */ new Map();
|
|
1419
1467
|
for (const j of jurisdictions) {
|
|
@@ -1422,19 +1470,45 @@ function buildSchedulePayload(input) {
|
|
|
1422
1470
|
}
|
|
1423
1471
|
}
|
|
1424
1472
|
const selectedLocations = selectedLocationIds.map((id) => locationsMap.get(id)).filter((loc) => Boolean(loc));
|
|
1473
|
+
const scopedOpenShifts = openShifts.filter(
|
|
1474
|
+
(shift) => {
|
|
1475
|
+
var _a;
|
|
1476
|
+
return selectedLocationIds.includes(String((_a = shift.location) == null ? void 0 : _a.id));
|
|
1477
|
+
}
|
|
1478
|
+
);
|
|
1479
|
+
const includeConversionRate = hasConversionRateInSelectedLocations(
|
|
1480
|
+
employees,
|
|
1481
|
+
selectedLocationIds
|
|
1482
|
+
);
|
|
1483
|
+
const scopedTrafficForecast = (trafficForecast != null ? trafficForecast : []).filter(
|
|
1484
|
+
(entry) => selectedLocationIds.includes(String(entry.locationId))
|
|
1485
|
+
);
|
|
1486
|
+
const includeTrafficIncentive = scopedTrafficForecast.length > 0;
|
|
1425
1487
|
const rules = buildRulesPayload(
|
|
1426
1488
|
selectedConstraintIds,
|
|
1427
1489
|
constraintValues,
|
|
1428
1490
|
selectedOptimizationIds,
|
|
1429
|
-
optimizationValues
|
|
1491
|
+
optimizationValues,
|
|
1492
|
+
{ includeConversionRate, includeTrafficIncentive }
|
|
1430
1493
|
);
|
|
1431
1494
|
const includeEmployeePreferences = selectedOptimizationIds.includes("staff_preferences");
|
|
1495
|
+
const hasIds = Boolean(constraintDefinitionIds && constraintDefinitionIds.length > 0);
|
|
1496
|
+
const hasDefs = Boolean(constraintDefinitions && constraintDefinitions.length > 0);
|
|
1497
|
+
const hasRules = hasIds || hasDefs;
|
|
1498
|
+
if (hasIds && !organizationId) {
|
|
1499
|
+
throw new Error("organizationId is required when constraintDefinitionIds are provided");
|
|
1500
|
+
}
|
|
1501
|
+
const trafficRuleActive = includeTrafficIncentive && selectedOptimizationIds.includes("maximize_sales");
|
|
1432
1502
|
return {
|
|
1433
1503
|
employees: employees.map(toApiEmployee),
|
|
1434
|
-
shifts:
|
|
1504
|
+
shifts: scopedOpenShifts.map((shift) => toApiShift(shift, includeEmployeePreferences)),
|
|
1435
1505
|
locations: selectedLocations.map(toApiLocation),
|
|
1436
1506
|
...timeOffs && timeOffs.length > 0 ? { timeOffs } : {},
|
|
1437
|
-
...rules
|
|
1507
|
+
...rules,
|
|
1508
|
+
...trafficRuleActive ? { trafficForecast: scopedTrafficForecast } : {},
|
|
1509
|
+
...hasIds ? { constraintDefinitionIds } : {},
|
|
1510
|
+
...hasDefs ? { constraintDefinitions } : {},
|
|
1511
|
+
...hasRules && organizationId ? { organizationId } : {}
|
|
1438
1512
|
};
|
|
1439
1513
|
}
|
|
1440
1514
|
|
|
@@ -1457,7 +1531,7 @@ async function submitSolveJob(baseURL, payload, options) {
|
|
|
1457
1531
|
headers.Authorization = options.authorization;
|
|
1458
1532
|
}
|
|
1459
1533
|
const response = await axiosInstance.post(
|
|
1460
|
-
`${cleanBaseUrl}/api/schedule/solve-async`,
|
|
1534
|
+
withHorizonQuery(`${cleanBaseUrl}/api/schedule/solve-async`, options),
|
|
1461
1535
|
payload,
|
|
1462
1536
|
{ headers: Object.keys(headers).length > 0 ? headers : void 0 }
|
|
1463
1537
|
);
|
|
@@ -1499,6 +1573,16 @@ async function getJobSolution(baseURL, jobId, options) {
|
|
|
1499
1573
|
);
|
|
1500
1574
|
return response.data;
|
|
1501
1575
|
}
|
|
1576
|
+
function withHorizonQuery(url, options) {
|
|
1577
|
+
if (!(options == null ? void 0 : options.horizonStartDate) || !(options == null ? void 0 : options.horizonEndDate)) {
|
|
1578
|
+
return url;
|
|
1579
|
+
}
|
|
1580
|
+
const params = new URLSearchParams({
|
|
1581
|
+
dateRangeStart: options.horizonStartDate,
|
|
1582
|
+
dateRangeEnd: options.horizonEndDate
|
|
1583
|
+
});
|
|
1584
|
+
return `${url}?${params.toString()}`;
|
|
1585
|
+
}
|
|
1502
1586
|
|
|
1503
1587
|
// src/utils/violationMessages.ts
|
|
1504
1588
|
var SYSTEM_CONSTRAINT_MESSAGES = {
|
|
@@ -1646,17 +1730,14 @@ function getRuleBasedViolationMessage(ruleId, constraintName, description) {
|
|
|
1646
1730
|
}
|
|
1647
1731
|
|
|
1648
1732
|
// src/utils/mapViolationsToResult.ts
|
|
1649
|
-
function mapViolationsToResult(violations, summary) {
|
|
1733
|
+
function mapViolationsToResult(violations, summary, customMessages) {
|
|
1734
|
+
var _a;
|
|
1650
1735
|
const hardViolations = violations.filter((v) => v.isHardViolation === true);
|
|
1651
1736
|
const seenTitles = /* @__PURE__ */ new Set();
|
|
1652
1737
|
const violatedConstraints = [];
|
|
1653
1738
|
for (const v of hardViolations) {
|
|
1654
1739
|
const hasRuleId = v.ruleId != null && v.ruleId !== void 0 && String(v.ruleId).trim() !== "";
|
|
1655
|
-
const msg = hasRuleId ? getRuleBasedViolationMessage(
|
|
1656
|
-
v.ruleId,
|
|
1657
|
-
v.constraintName,
|
|
1658
|
-
v.description
|
|
1659
|
-
) : getSystemConstraintMessage(v.constraintName, v.description);
|
|
1740
|
+
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);
|
|
1660
1741
|
if (!seenTitles.has(msg.title)) {
|
|
1661
1742
|
seenTitles.add(msg.title);
|
|
1662
1743
|
violatedConstraints.push(msg);
|
|
@@ -1724,6 +1805,7 @@ function ViolatedConstraintsPanel({
|
|
|
1724
1805
|
return;
|
|
1725
1806
|
}
|
|
1726
1807
|
if (recommendations !== null) {
|
|
1808
|
+
setShowRecommendations(true);
|
|
1727
1809
|
return;
|
|
1728
1810
|
}
|
|
1729
1811
|
if (!canFetchRecommendations) {
|
|
@@ -1844,13 +1926,78 @@ function ViolatedConstraintsPanel({
|
|
|
1844
1926
|
] });
|
|
1845
1927
|
}
|
|
1846
1928
|
|
|
1929
|
+
// src/weekDates.ts
|
|
1930
|
+
function parseCalendarYmd(ymd) {
|
|
1931
|
+
const m = /^(\d{4})-(\d{2})-(\d{2})$/.exec(String(ymd).trim());
|
|
1932
|
+
if (!m) {
|
|
1933
|
+
return /* @__PURE__ */ new Date(NaN);
|
|
1934
|
+
}
|
|
1935
|
+
return new Date(Number(m[1]), Number(m[2]) - 1, Number(m[3]));
|
|
1936
|
+
}
|
|
1937
|
+
function formatCalendarYmd(d) {
|
|
1938
|
+
const y = d.getFullYear();
|
|
1939
|
+
const mo = String(d.getMonth() + 1).padStart(2, "0");
|
|
1940
|
+
const da = String(d.getDate()).padStart(2, "0");
|
|
1941
|
+
return `${y}-${mo}-${da}`;
|
|
1942
|
+
}
|
|
1943
|
+
function getSequentialWeekDatesFromWeekStart(weekStartYmd) {
|
|
1944
|
+
const base = parseCalendarYmd(weekStartYmd);
|
|
1945
|
+
if (Number.isNaN(base.getTime())) return [];
|
|
1946
|
+
const out = [];
|
|
1947
|
+
for (let i = 0; i < 7; i++) {
|
|
1948
|
+
const d = new Date(base.getFullYear(), base.getMonth(), base.getDate() + i);
|
|
1949
|
+
out.push(formatCalendarYmd(d));
|
|
1950
|
+
}
|
|
1951
|
+
return out;
|
|
1952
|
+
}
|
|
1953
|
+
function weekdayTwoLetterFromYmd(ymd) {
|
|
1954
|
+
var _a;
|
|
1955
|
+
const labels = ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"];
|
|
1956
|
+
const d = parseCalendarYmd(ymd);
|
|
1957
|
+
if (Number.isNaN(d.getTime())) return "?";
|
|
1958
|
+
return (_a = labels[d.getDay()]) != null ? _a : "?";
|
|
1959
|
+
}
|
|
1960
|
+
function defaultSelectedScheduleDates(orderedWeekYmds, todayYmd) {
|
|
1961
|
+
return orderedWeekYmds.filter((d) => d >= todayYmd);
|
|
1962
|
+
}
|
|
1963
|
+
function todayCalendarYmdInTimezone(timezone) {
|
|
1964
|
+
const fallback = formatCalendarYmd(/* @__PURE__ */ new Date());
|
|
1965
|
+
const tz = timezone == null ? void 0 : timezone.trim();
|
|
1966
|
+
if (!tz) return fallback;
|
|
1967
|
+
try {
|
|
1968
|
+
const parts = new Intl.DateTimeFormat("en-CA", {
|
|
1969
|
+
timeZone: tz,
|
|
1970
|
+
year: "numeric",
|
|
1971
|
+
month: "2-digit",
|
|
1972
|
+
day: "2-digit"
|
|
1973
|
+
}).formatToParts(/* @__PURE__ */ new Date());
|
|
1974
|
+
const valueByType = new Map(parts.map((part) => [part.type, part.value]));
|
|
1975
|
+
const year = valueByType.get("year");
|
|
1976
|
+
const month = valueByType.get("month");
|
|
1977
|
+
const day = valueByType.get("day");
|
|
1978
|
+
return year && month && day ? `${year}-${month}-${day}` : fallback;
|
|
1979
|
+
} catch {
|
|
1980
|
+
return fallback;
|
|
1981
|
+
}
|
|
1982
|
+
}
|
|
1983
|
+
|
|
1847
1984
|
// src/AutoSchedulerModal.tsx
|
|
1848
1985
|
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
1986
|
+
function resolveOptimizationFooter(footer, key) {
|
|
1987
|
+
if (!footer) return null;
|
|
1988
|
+
const node = typeof footer === "function" ? footer() : footer;
|
|
1989
|
+
if ((0, import_react3.isValidElement)(node)) return (0, import_react3.cloneElement)(node, { key });
|
|
1990
|
+
return node;
|
|
1991
|
+
}
|
|
1992
|
+
var DRAWER_WIDTH_MAP = { sm: "448px", md: "578px", lg: "600px" };
|
|
1849
1993
|
function AutoSchedulerModal({
|
|
1994
|
+
displayMode,
|
|
1995
|
+
drawerSize = "lg",
|
|
1850
1996
|
baseURL,
|
|
1851
1997
|
isOpen,
|
|
1852
1998
|
title = "Auto-scheduling",
|
|
1853
|
-
|
|
1999
|
+
bannerTitle = "Create a smarter retail schedule in seconds.",
|
|
2000
|
+
bannerText = "AI assigns open shifts using your rules, availability, and optimization priorities while keeping existing\n assigned shifts in place.",
|
|
1854
2001
|
jurisdictions,
|
|
1855
2002
|
openShifts,
|
|
1856
2003
|
employees,
|
|
@@ -1869,22 +2016,78 @@ function AutoSchedulerModal({
|
|
|
1869
2016
|
onSolution,
|
|
1870
2017
|
getStoredConfigForLocations,
|
|
1871
2018
|
persistConfigForLocations,
|
|
1872
|
-
errorToast
|
|
2019
|
+
errorToast,
|
|
2020
|
+
organizationId,
|
|
2021
|
+
constraintDefinitionIds,
|
|
2022
|
+
constraintDefinitions,
|
|
2023
|
+
hardConstraintOptions,
|
|
2024
|
+
softConstraintGroups,
|
|
2025
|
+
optimizationFooter,
|
|
2026
|
+
trafficForecast,
|
|
2027
|
+
builtinConstraintOptions,
|
|
2028
|
+
subLocations,
|
|
2029
|
+
showSubLocations,
|
|
2030
|
+
teams,
|
|
2031
|
+
showTeams,
|
|
2032
|
+
onSelectionChange,
|
|
2033
|
+
onGenerate,
|
|
2034
|
+
onGenerateSolution,
|
|
2035
|
+
solveStatus,
|
|
2036
|
+
scheduleWeekStartYmd,
|
|
2037
|
+
todayCalendarYmd,
|
|
2038
|
+
evaluationTimezone
|
|
1873
2039
|
}) {
|
|
1874
2040
|
const baseTheme = (0, import_react4.useTheme)();
|
|
1875
|
-
const { chakraOverride, styleOverrides } = (0, import_react3.useMemo)(
|
|
1876
|
-
() => resolveThemeParts(theme),
|
|
1877
|
-
[theme]
|
|
1878
|
-
);
|
|
2041
|
+
const { chakraOverride, styleOverrides } = (0, import_react3.useMemo)(() => resolveThemeParts(theme), [theme]);
|
|
1879
2042
|
const mergedTheme = (0, import_react3.useMemo)(
|
|
1880
2043
|
() => mergeThemeWithBase(baseTheme, chakraOverride),
|
|
1881
2044
|
[baseTheme, chakraOverride]
|
|
1882
2045
|
);
|
|
1883
2046
|
const styles = (0, import_react3.useMemo)(() => buildStyles(defaultStyles, styleOverrides), [styleOverrides]);
|
|
2047
|
+
const effectiveHardConstraintOptions = (0, import_react3.useMemo)(() => {
|
|
2048
|
+
const base = hardConstraintOptions != null ? hardConstraintOptions : HARD_CONSTRAINT_OPTIONS;
|
|
2049
|
+
return base.map((opt) => {
|
|
2050
|
+
var _a, _b, _c, _d;
|
|
2051
|
+
return {
|
|
2052
|
+
...opt,
|
|
2053
|
+
defaultValue: (_a = opt.defaultValue) != null ? _a : HARD_CONSTRAINT_DEFAULTS[opt.id],
|
|
2054
|
+
suffix: (_b = opt.suffix) != null ? _b : opt.id === "max_work_days_per_week" ? "days" : "hours",
|
|
2055
|
+
min: (_c = opt.min) != null ? _c : opt.id === "max_work_days_per_week" ? 1 : void 0,
|
|
2056
|
+
max: (_d = opt.max) != null ? _d : opt.id === "max_work_days_per_week" ? 6 : void 0
|
|
2057
|
+
};
|
|
2058
|
+
});
|
|
2059
|
+
}, [hardConstraintOptions]);
|
|
2060
|
+
const effectiveSoftConstraintGroups = (0, import_react3.useMemo)(
|
|
2061
|
+
() => softConstraintGroups != null ? softConstraintGroups : OPTIMIZATION_OPTION_GROUPS,
|
|
2062
|
+
[softConstraintGroups]
|
|
2063
|
+
);
|
|
2064
|
+
const effectiveBuiltinOptions = (0, import_react3.useMemo)(
|
|
2065
|
+
() => builtinConstraintOptions != null ? builtinConstraintOptions : [],
|
|
2066
|
+
[builtinConstraintOptions]
|
|
2067
|
+
);
|
|
2068
|
+
const defaultSelectedBuiltinIds = (0, import_react3.useMemo)(
|
|
2069
|
+
() => effectiveBuiltinOptions.filter((option) => option.defaultSelected).map((option) => option.id),
|
|
2070
|
+
[effectiveBuiltinOptions]
|
|
2071
|
+
);
|
|
2072
|
+
const dynamicViolationMessages = (0, import_react3.useMemo)(() => {
|
|
2073
|
+
const map = {};
|
|
2074
|
+
for (const opt of effectiveHardConstraintOptions) {
|
|
2075
|
+
map[opt.id] = { title: opt.name, detail: opt.description || opt.name };
|
|
2076
|
+
}
|
|
2077
|
+
for (const group of effectiveSoftConstraintGroups) {
|
|
2078
|
+
for (const opt of group.options) {
|
|
2079
|
+
map[opt.id] = { title: opt.title, detail: opt.subtitle || opt.title };
|
|
2080
|
+
}
|
|
2081
|
+
}
|
|
2082
|
+
return map;
|
|
2083
|
+
}, [effectiveHardConstraintOptions, effectiveSoftConstraintGroups]);
|
|
2084
|
+
const [selectedSubLocationIds, setSelectedSubLocationIds] = (0, import_react3.useState)([]);
|
|
2085
|
+
const [selectedTeamIds, setSelectedTeamIds] = (0, import_react3.useState)([]);
|
|
1884
2086
|
const [selectedConstraintIds, setSelectedConstraintIds] = (0, import_react3.useState)([]);
|
|
1885
2087
|
const [constraintValues, setConstraintValues] = (0, import_react3.useState)({});
|
|
1886
2088
|
const [selectedOptimizationIds, setSelectedOptimizationIds] = (0, import_react3.useState)([]);
|
|
1887
2089
|
const [optimizationValues, setOptimizationValues] = (0, import_react3.useState)({});
|
|
2090
|
+
const [selectedScheduleDates, setSelectedScheduleDates] = (0, import_react3.useState)([]);
|
|
1888
2091
|
const [screen, setScreen] = (0, import_react3.useState)("configure");
|
|
1889
2092
|
const [activeStepIndex, setActiveStepIndex] = (0, import_react3.useState)(0);
|
|
1890
2093
|
const [completedStepIndex, setCompletedStepIndex] = (0, import_react3.useState)(-1);
|
|
@@ -1893,12 +2096,19 @@ function AutoSchedulerModal({
|
|
|
1893
2096
|
const timersRef = (0, import_react3.useRef)([]);
|
|
1894
2097
|
const pollIntervalRef = (0, import_react3.useRef)(null);
|
|
1895
2098
|
const runIdRef = (0, import_react3.useRef)(0);
|
|
2099
|
+
const weekDatesOrdered = (0, import_react3.useMemo)(() => {
|
|
2100
|
+
if (!scheduleWeekStartYmd) return [];
|
|
2101
|
+
return getSequentialWeekDatesFromWeekStart(scheduleWeekStartYmd);
|
|
2102
|
+
}, [scheduleWeekStartYmd]);
|
|
2103
|
+
const toggleScheduleDate = (ymd) => {
|
|
2104
|
+
setSelectedScheduleDates(
|
|
2105
|
+
(prev) => prev.includes(ymd) ? prev.filter((d) => d !== ymd) : [...prev, ymd].sort()
|
|
2106
|
+
);
|
|
2107
|
+
};
|
|
1896
2108
|
const selectedByJurisdiction = (0, import_react3.useMemo)(
|
|
1897
2109
|
() => jurisdictions.map((jurisdiction) => ({
|
|
1898
2110
|
...jurisdiction,
|
|
1899
|
-
selectedLocations: jurisdiction.locations.filter(
|
|
1900
|
-
(location) => selectedLocationIds.includes(location.id)
|
|
1901
|
-
)
|
|
2111
|
+
selectedLocations: jurisdiction.locations.filter((location) => selectedLocationIds.includes(location.id))
|
|
1902
2112
|
})).filter((jurisdiction) => jurisdiction.selectedLocations.length > 0),
|
|
1903
2113
|
[jurisdictions, selectedLocationIds]
|
|
1904
2114
|
);
|
|
@@ -1907,43 +2117,84 @@ function AutoSchedulerModal({
|
|
|
1907
2117
|
0
|
|
1908
2118
|
);
|
|
1909
2119
|
const locationIdsWithOpenShifts = (0, import_react3.useMemo)(() => {
|
|
1910
|
-
return new Set(
|
|
1911
|
-
openShifts.filter((shift) => !shift.employeeId).map((shift) => shift.location.id)
|
|
1912
|
-
);
|
|
2120
|
+
return new Set(openShifts.filter((shift) => !shift.employeeId).map((shift) => shift.location.id));
|
|
1913
2121
|
}, [openShifts]);
|
|
2122
|
+
const filteredSubLocations = (0, import_react3.useMemo)(
|
|
2123
|
+
() => {
|
|
2124
|
+
var _a;
|
|
2125
|
+
return (_a = subLocations == null ? void 0 : subLocations.filter((sl) => selectedLocationIds.includes(sl.parentId))) != null ? _a : [];
|
|
2126
|
+
},
|
|
2127
|
+
[subLocations, selectedLocationIds]
|
|
2128
|
+
);
|
|
2129
|
+
const selectedLocationTimezone = (0, import_react3.useMemo)(() => {
|
|
2130
|
+
var _a, _b;
|
|
2131
|
+
const allLocations = jurisdictions.flatMap((jurisdiction) => jurisdiction.locations);
|
|
2132
|
+
for (const id of selectedLocationIds) {
|
|
2133
|
+
const timezone = (_b = (_a = allLocations.find((location) => location.id === id)) == null ? void 0 : _a.timezone) == null ? void 0 : _b.trim();
|
|
2134
|
+
if (timezone) return timezone;
|
|
2135
|
+
}
|
|
2136
|
+
return (evaluationTimezone == null ? void 0 : evaluationTimezone.trim()) || "UTC";
|
|
2137
|
+
}, [evaluationTimezone, jurisdictions, selectedLocationIds]);
|
|
2138
|
+
const effectiveTodayCalendarYmd = (0, import_react3.useMemo)(() => {
|
|
2139
|
+
return todayCalendarYmd != null ? todayCalendarYmd : todayCalendarYmdInTimezone(selectedLocationTimezone);
|
|
2140
|
+
}, [selectedLocationTimezone, todayCalendarYmd]);
|
|
1914
2141
|
const selectedConstraints = (0, import_react3.useMemo)(
|
|
1915
|
-
() =>
|
|
1916
|
-
[selectedConstraintIds]
|
|
2142
|
+
() => effectiveHardConstraintOptions.filter((option) => selectedConstraintIds.includes(option.id)),
|
|
2143
|
+
[effectiveHardConstraintOptions, selectedConstraintIds]
|
|
2144
|
+
);
|
|
2145
|
+
const selectedConstraintDisplayOptions = (0, import_react3.useMemo)(
|
|
2146
|
+
() => effectiveHardConstraintOptions.filter(
|
|
2147
|
+
(option) => selectedConstraintIds.includes(option.id) || option.readOnly && option.defaultSelected
|
|
2148
|
+
),
|
|
2149
|
+
[effectiveHardConstraintOptions, selectedConstraintIds]
|
|
1917
2150
|
);
|
|
1918
2151
|
const optimizationOptions = (0, import_react3.useMemo)(
|
|
1919
|
-
() =>
|
|
2152
|
+
() => effectiveSoftConstraintGroups.flatMap(
|
|
1920
2153
|
(group) => group.options.map((option) => ({
|
|
1921
2154
|
...option,
|
|
1922
2155
|
groupId: group.id,
|
|
1923
2156
|
groupName: group.name
|
|
1924
2157
|
}))
|
|
1925
2158
|
),
|
|
1926
|
-
[]
|
|
2159
|
+
[effectiveSoftConstraintGroups]
|
|
1927
2160
|
);
|
|
1928
2161
|
const selectedOptimizationOptions = (0, import_react3.useMemo)(
|
|
2162
|
+
() => optimizationOptions.filter((option) => selectedOptimizationIds.includes(option.id)),
|
|
2163
|
+
[optimizationOptions, selectedOptimizationIds]
|
|
2164
|
+
);
|
|
2165
|
+
const selectedOptimizationDisplayOptions = (0, import_react3.useMemo)(
|
|
1929
2166
|
() => optimizationOptions.filter(
|
|
1930
|
-
(option) => selectedOptimizationIds.includes(option.id)
|
|
2167
|
+
(option) => selectedOptimizationIds.includes(option.id) || option.readOnly && option.defaultSelected
|
|
1931
2168
|
),
|
|
1932
2169
|
[optimizationOptions, selectedOptimizationIds]
|
|
1933
2170
|
);
|
|
2171
|
+
const effectiveSelectedConstraintIds = (0, import_react3.useMemo)(
|
|
2172
|
+
() => Array.from(
|
|
2173
|
+
/* @__PURE__ */ new Set([
|
|
2174
|
+
...selectedConstraintIds,
|
|
2175
|
+
...effectiveHardConstraintOptions.filter((option) => option.readOnly && option.defaultSelected).map((option) => option.id)
|
|
2176
|
+
])
|
|
2177
|
+
),
|
|
2178
|
+
[selectedConstraintIds, effectiveHardConstraintOptions]
|
|
2179
|
+
);
|
|
2180
|
+
const effectiveSelectedOptimizationIds = (0, import_react3.useMemo)(
|
|
2181
|
+
() => Array.from(
|
|
2182
|
+
/* @__PURE__ */ new Set([
|
|
2183
|
+
...selectedOptimizationIds,
|
|
2184
|
+
...optimizationOptions.filter((option) => option.readOnly && option.defaultSelected).map((option) => option.id)
|
|
2185
|
+
])
|
|
2186
|
+
),
|
|
2187
|
+
[selectedOptimizationIds, optimizationOptions]
|
|
2188
|
+
);
|
|
1934
2189
|
const hasEmptyInputs = (0, import_react3.useMemo)(() => {
|
|
1935
|
-
const hasEmptyConstraint = selectedConstraints.some(
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
var _a;
|
|
1944
|
-
return !((_a = optimizationValues[`${opt.id}_weightPoints`]) == null ? void 0 : _a.trim());
|
|
1945
|
-
}
|
|
1946
|
-
);
|
|
2190
|
+
const hasEmptyConstraint = selectedConstraints.filter((opt) => opt.defaultValue != null).some((opt) => {
|
|
2191
|
+
var _a;
|
|
2192
|
+
return !((_a = constraintValues[opt.id]) == null ? void 0 : _a.trim());
|
|
2193
|
+
});
|
|
2194
|
+
const hasEmptyWeight = selectedOptimizationOptions.length >= 2 && selectedOptimizationOptions.some((opt) => {
|
|
2195
|
+
var _a;
|
|
2196
|
+
return !((_a = optimizationValues[`${opt.id}_weightPoints`]) == null ? void 0 : _a.trim());
|
|
2197
|
+
});
|
|
1947
2198
|
return hasEmptyConstraint || hasEmptyWeight;
|
|
1948
2199
|
}, [selectedConstraints, constraintValues, selectedOptimizationOptions, optimizationValues]);
|
|
1949
2200
|
const allocationSummary = (0, import_react3.useMemo)(() => {
|
|
@@ -1977,13 +2228,25 @@ function AutoSchedulerModal({
|
|
|
1977
2228
|
setScreen("configure");
|
|
1978
2229
|
setLastFailure(null);
|
|
1979
2230
|
setSelectedLocationIds([]);
|
|
2231
|
+
setSelectedSubLocationIds([]);
|
|
2232
|
+
setSelectedTeamIds([]);
|
|
1980
2233
|
setSelectedOptimizationIds([]);
|
|
1981
2234
|
setSelectedConstraintIds([]);
|
|
1982
2235
|
setConstraintValues({});
|
|
1983
2236
|
setOptimizationValues({});
|
|
2237
|
+
setSelectedScheduleDates([]);
|
|
1984
2238
|
resetProgress();
|
|
1985
2239
|
}
|
|
1986
2240
|
}, [isOpen]);
|
|
2241
|
+
(0, import_react3.useEffect)(() => {
|
|
2242
|
+
if (!isOpen || !scheduleWeekStartYmd) return;
|
|
2243
|
+
if (weekDatesOrdered.length === 0) {
|
|
2244
|
+
setSelectedScheduleDates([]);
|
|
2245
|
+
return;
|
|
2246
|
+
}
|
|
2247
|
+
const today = effectiveTodayCalendarYmd != null ? effectiveTodayCalendarYmd : weekDatesOrdered[0];
|
|
2248
|
+
setSelectedScheduleDates(defaultSelectedScheduleDates(weekDatesOrdered, today));
|
|
2249
|
+
}, [isOpen, scheduleWeekStartYmd, weekDatesOrdered, effectiveTodayCalendarYmd]);
|
|
1987
2250
|
(0, import_react3.useEffect)(() => {
|
|
1988
2251
|
if (isOpen && initialConfig) {
|
|
1989
2252
|
setSelectedLocationIds(initialConfig.selectedLocationIds);
|
|
@@ -2016,11 +2279,12 @@ function AutoSchedulerModal({
|
|
|
2016
2279
|
}, [selectedLocationIds, initialConfig, getStoredConfigForLocations]);
|
|
2017
2280
|
(0, import_react3.useEffect)(() => () => clearTimers(), []);
|
|
2018
2281
|
(0, import_react3.useEffect)(() => {
|
|
2282
|
+
if (onGenerate) return;
|
|
2019
2283
|
setSelectedLocationIds((previous) => {
|
|
2020
2284
|
const filtered = previous.filter((id) => locationIdsWithOpenShifts.has(id));
|
|
2021
2285
|
return filtered.length === previous.length ? previous : filtered;
|
|
2022
2286
|
});
|
|
2023
|
-
}, [locationIdsWithOpenShifts]);
|
|
2287
|
+
}, [locationIdsWithOpenShifts, onGenerate]);
|
|
2024
2288
|
const stepStatusForIndex = (index) => {
|
|
2025
2289
|
if (index <= completedStepIndex) {
|
|
2026
2290
|
return "done";
|
|
@@ -2048,144 +2312,249 @@ function AutoSchedulerModal({
|
|
|
2048
2312
|
timersRef.current.push(timerId);
|
|
2049
2313
|
};
|
|
2050
2314
|
const afterSuccessSteps = (runId, response) => {
|
|
2051
|
-
schedule(
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
setCompletedStepIndex(4);
|
|
2315
|
+
schedule(
|
|
2316
|
+
STEP_INTERVAL_MS,
|
|
2317
|
+
() => {
|
|
2318
|
+
if (runIdRef.current !== runId) return;
|
|
2319
|
+
setCompletedStepIndex(2);
|
|
2320
|
+
setActiveStepIndex(3);
|
|
2321
|
+
},
|
|
2322
|
+
runId
|
|
2323
|
+
);
|
|
2324
|
+
schedule(
|
|
2325
|
+
STEP_INTERVAL_MS * 2,
|
|
2326
|
+
() => {
|
|
2327
|
+
if (runIdRef.current !== runId) return;
|
|
2328
|
+
setCompletedStepIndex(3);
|
|
2066
2329
|
setActiveStepIndex(4);
|
|
2067
|
-
}
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2330
|
+
},
|
|
2331
|
+
runId
|
|
2332
|
+
);
|
|
2333
|
+
schedule(
|
|
2334
|
+
STEP_INTERVAL_MS * 3,
|
|
2335
|
+
async () => {
|
|
2336
|
+
if (runIdRef.current !== runId) return;
|
|
2337
|
+
try {
|
|
2338
|
+
await (onSolution == null ? void 0 : onSolution(response.solution));
|
|
2339
|
+
setCompletedStepIndex(4);
|
|
2340
|
+
setActiveStepIndex(4);
|
|
2341
|
+
} catch (err) {
|
|
2342
|
+
console.error(err);
|
|
2343
|
+
const defaultErrorMessage = "Failed to create schedule shifts";
|
|
2344
|
+
const detailedErrorMessage = err instanceof Error ? err.message || defaultErrorMessage : defaultErrorMessage;
|
|
2345
|
+
setLastFailure({
|
|
2346
|
+
violatedConstraints: [
|
|
2347
|
+
{
|
|
2348
|
+
title: "Something went wrong",
|
|
2349
|
+
detail: detailedErrorMessage
|
|
2350
|
+
}
|
|
2351
|
+
],
|
|
2352
|
+
recommendedFixes: []
|
|
2353
|
+
});
|
|
2354
|
+
setScreen("failed");
|
|
2355
|
+
}
|
|
2356
|
+
},
|
|
2357
|
+
runId
|
|
2358
|
+
);
|
|
2083
2359
|
};
|
|
2084
2360
|
const handleCreateSchedule = () => {
|
|
2085
2361
|
if (selectedCount === 0) {
|
|
2086
2362
|
return;
|
|
2087
2363
|
}
|
|
2364
|
+
if (scheduleWeekStartYmd && selectedScheduleDates.length === 0) {
|
|
2365
|
+
return;
|
|
2366
|
+
}
|
|
2367
|
+
if (onGenerate) {
|
|
2368
|
+
const runId2 = resetBeforeSubmit();
|
|
2369
|
+
schedule(
|
|
2370
|
+
STEP_INTERVAL_MS,
|
|
2371
|
+
async () => {
|
|
2372
|
+
if (runIdRef.current !== runId2) return;
|
|
2373
|
+
setCompletedStepIndex(0);
|
|
2374
|
+
setActiveStepIndex(1);
|
|
2375
|
+
try {
|
|
2376
|
+
const generateResult = await onGenerate({
|
|
2377
|
+
selectedLocationIds,
|
|
2378
|
+
selectedSubLocationIds,
|
|
2379
|
+
selectedTeamIds,
|
|
2380
|
+
schedulerTimezone: selectedLocationTimezone,
|
|
2381
|
+
selectedScheduleDates,
|
|
2382
|
+
selectedConstraintIds: effectiveSelectedConstraintIds,
|
|
2383
|
+
constraintValues,
|
|
2384
|
+
selectedOptimizationIds: effectiveSelectedOptimizationIds,
|
|
2385
|
+
optimizationValues,
|
|
2386
|
+
selectedBuiltinIds: defaultSelectedBuiltinIds
|
|
2387
|
+
});
|
|
2388
|
+
if (runIdRef.current !== runId2) return;
|
|
2389
|
+
setCompletedStepIndex(1);
|
|
2390
|
+
setActiveStepIndex(2);
|
|
2391
|
+
schedule(
|
|
2392
|
+
STEP_INTERVAL_MS,
|
|
2393
|
+
() => {
|
|
2394
|
+
if (runIdRef.current !== runId2) return;
|
|
2395
|
+
setCompletedStepIndex(2);
|
|
2396
|
+
setActiveStepIndex(3);
|
|
2397
|
+
},
|
|
2398
|
+
runId2
|
|
2399
|
+
);
|
|
2400
|
+
schedule(
|
|
2401
|
+
STEP_INTERVAL_MS * 2,
|
|
2402
|
+
() => {
|
|
2403
|
+
if (runIdRef.current !== runId2) return;
|
|
2404
|
+
setCompletedStepIndex(3);
|
|
2405
|
+
setActiveStepIndex(4);
|
|
2406
|
+
},
|
|
2407
|
+
runId2
|
|
2408
|
+
);
|
|
2409
|
+
schedule(
|
|
2410
|
+
STEP_INTERVAL_MS * 3,
|
|
2411
|
+
() => {
|
|
2412
|
+
if (runIdRef.current !== runId2) return;
|
|
2413
|
+
setCompletedStepIndex(4);
|
|
2414
|
+
setActiveStepIndex(4);
|
|
2415
|
+
onGenerateSolution == null ? void 0 : onGenerateSolution(generateResult);
|
|
2416
|
+
onClose();
|
|
2417
|
+
},
|
|
2418
|
+
runId2
|
|
2419
|
+
);
|
|
2420
|
+
} catch (err) {
|
|
2421
|
+
if (runIdRef.current !== runId2) return;
|
|
2422
|
+
if (err instanceof InfeasibleScheduleError) {
|
|
2423
|
+
setLastFailure(err.failure);
|
|
2424
|
+
} else {
|
|
2425
|
+
const msg = err instanceof Error ? err.message : "Unknown error";
|
|
2426
|
+
setLastFailure({
|
|
2427
|
+
violatedConstraints: [{ title: "Error", detail: msg }],
|
|
2428
|
+
recommendedFixes: []
|
|
2429
|
+
});
|
|
2430
|
+
}
|
|
2431
|
+
setScreen("failed");
|
|
2432
|
+
}
|
|
2433
|
+
},
|
|
2434
|
+
runId2
|
|
2435
|
+
);
|
|
2436
|
+
return;
|
|
2437
|
+
}
|
|
2088
2438
|
const payload = buildSchedulePayload({
|
|
2089
2439
|
employees,
|
|
2090
2440
|
openShifts,
|
|
2091
2441
|
timeOffs,
|
|
2092
2442
|
selectedLocationIds,
|
|
2093
2443
|
jurisdictions,
|
|
2094
|
-
selectedConstraintIds,
|
|
2444
|
+
selectedConstraintIds: effectiveSelectedConstraintIds,
|
|
2095
2445
|
constraintValues,
|
|
2096
|
-
selectedOptimizationIds,
|
|
2097
|
-
optimizationValues
|
|
2446
|
+
selectedOptimizationIds: effectiveSelectedOptimizationIds,
|
|
2447
|
+
optimizationValues,
|
|
2448
|
+
trafficForecast,
|
|
2449
|
+
constraintDefinitionIds,
|
|
2450
|
+
constraintDefinitions,
|
|
2451
|
+
organizationId
|
|
2098
2452
|
});
|
|
2099
2453
|
persistConfigForLocations == null ? void 0 : persistConfigForLocations(selectedLocationIds, {
|
|
2100
2454
|
selectedLocationIds,
|
|
2101
|
-
selectedConstraintIds,
|
|
2455
|
+
selectedConstraintIds: effectiveSelectedConstraintIds,
|
|
2102
2456
|
constraintValues,
|
|
2103
|
-
selectedOptimizationIds,
|
|
2104
|
-
optimizationValues
|
|
2457
|
+
selectedOptimizationIds: effectiveSelectedOptimizationIds,
|
|
2458
|
+
optimizationValues,
|
|
2459
|
+
selectedBuiltinIds: defaultSelectedBuiltinIds
|
|
2105
2460
|
});
|
|
2106
2461
|
const runId = resetBeforeSubmit();
|
|
2107
|
-
schedule(
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2462
|
+
schedule(
|
|
2463
|
+
STEP_INTERVAL_MS,
|
|
2464
|
+
async () => {
|
|
2465
|
+
setCompletedStepIndex(0);
|
|
2466
|
+
setActiveStepIndex(1);
|
|
2467
|
+
try {
|
|
2468
|
+
const token = await getToken(getTokenURLAndHeaders.url, getTokenURLAndHeaders.headers);
|
|
2469
|
+
if (!token) {
|
|
2470
|
+
throw new Error("Failed to get auto-scheduler token");
|
|
2471
|
+
}
|
|
2472
|
+
const selectedDates = [...selectedScheduleDates].sort();
|
|
2473
|
+
const horizonOptions = selectedDates.length > 0 ? {
|
|
2474
|
+
horizonStartDate: selectedDates[0],
|
|
2475
|
+
horizonEndDate: selectedDates[selectedDates.length - 1]
|
|
2476
|
+
} : {};
|
|
2477
|
+
const auth = { authorization: `Bearer ${token}`, ...horizonOptions };
|
|
2478
|
+
const { jobId } = await submitSolveJob(baseURL, payload, auth);
|
|
2479
|
+
if (runIdRef.current !== runId) {
|
|
2480
|
+
return;
|
|
2481
|
+
}
|
|
2482
|
+
setCompletedStepIndex(1);
|
|
2483
|
+
setActiveStepIndex(OPTIMIZING_STEP_INDEX);
|
|
2484
|
+
const pollForResult = async () => {
|
|
2126
2485
|
if (runIdRef.current !== runId) return;
|
|
2127
|
-
|
|
2486
|
+
try {
|
|
2487
|
+
const statusRes = await getJobStatus(baseURL, jobId, auth);
|
|
2488
|
+
if (runIdRef.current !== runId) return;
|
|
2489
|
+
if (statusRes.status === "NOT_SOLVING" || statusRes.status === "TERMINATED_EARLY") {
|
|
2490
|
+
if (pollIntervalRef.current) {
|
|
2491
|
+
clearInterval(pollIntervalRef.current);
|
|
2492
|
+
pollIntervalRef.current = null;
|
|
2493
|
+
}
|
|
2494
|
+
const response = await getJobResult(baseURL, jobId, auth);
|
|
2495
|
+
if (runIdRef.current !== runId) return;
|
|
2496
|
+
let responseForHandling = response;
|
|
2497
|
+
if (response.explanation.isFeasible) {
|
|
2498
|
+
const solution = await getJobSolution(baseURL, jobId, auth);
|
|
2499
|
+
if (runIdRef.current !== runId) return;
|
|
2500
|
+
responseForHandling = { ...response, solution };
|
|
2501
|
+
}
|
|
2502
|
+
handleSolveResult(runId, responseForHandling);
|
|
2503
|
+
return;
|
|
2504
|
+
}
|
|
2505
|
+
} catch (err) {
|
|
2506
|
+
if (runIdRef.current !== runId) return;
|
|
2128
2507
|
if (pollIntervalRef.current) {
|
|
2129
2508
|
clearInterval(pollIntervalRef.current);
|
|
2130
2509
|
pollIntervalRef.current = null;
|
|
2131
2510
|
}
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
if (response.explanation.isFeasible) {
|
|
2136
|
-
const solution = await getJobSolution(baseURL, jobId, auth);
|
|
2137
|
-
if (runIdRef.current !== runId) return;
|
|
2138
|
-
responseForHandling = { ...response, solution };
|
|
2139
|
-
}
|
|
2140
|
-
handleSolveResult(runId, responseForHandling);
|
|
2141
|
-
return;
|
|
2142
|
-
}
|
|
2143
|
-
} catch (err) {
|
|
2144
|
-
if (runIdRef.current !== runId) return;
|
|
2145
|
-
if (pollIntervalRef.current) {
|
|
2146
|
-
clearInterval(pollIntervalRef.current);
|
|
2147
|
-
pollIntervalRef.current = null;
|
|
2511
|
+
console.error(err);
|
|
2512
|
+
setLastFailure(NETWORK_ERROR_FAILURE);
|
|
2513
|
+
setScreen("failed");
|
|
2148
2514
|
}
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2515
|
+
};
|
|
2516
|
+
pollForResult();
|
|
2517
|
+
pollIntervalRef.current = setInterval(pollForResult, POLL_INTERVAL_MS);
|
|
2518
|
+
} catch (err) {
|
|
2519
|
+
if (runIdRef.current !== runId) {
|
|
2520
|
+
return;
|
|
2152
2521
|
}
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
} catch (err) {
|
|
2157
|
-
if (runIdRef.current !== runId) {
|
|
2158
|
-
return;
|
|
2522
|
+
console.error(err);
|
|
2523
|
+
setLastFailure(NETWORK_ERROR_FAILURE);
|
|
2524
|
+
setScreen("failed");
|
|
2159
2525
|
}
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
}
|
|
2164
|
-
}, runId);
|
|
2526
|
+
},
|
|
2527
|
+
runId
|
|
2528
|
+
);
|
|
2165
2529
|
};
|
|
2166
2530
|
const handleSolveResult = (runId, response) => {
|
|
2167
|
-
var _a
|
|
2531
|
+
var _a;
|
|
2168
2532
|
if (!response.explanation.isFeasible) {
|
|
2169
|
-
const result = mapViolationsToResult(
|
|
2170
|
-
|
|
2171
|
-
response.explanation.summary
|
|
2172
|
-
);
|
|
2173
|
-
const selectedLocations = jurisdictions.flatMap((j) => j.locations).filter(
|
|
2174
|
-
(l) => selectedLocationIds.includes(l.id)
|
|
2175
|
-
);
|
|
2533
|
+
const result = mapViolationsToResult((_a = response.explanation.violations) != null ? _a : [], response.explanation.summary, dynamicViolationMessages);
|
|
2534
|
+
const selectedLocations = jurisdictions.flatMap((j) => j.locations).filter((l) => selectedLocationIds.includes(l.id));
|
|
2176
2535
|
const recommendationPayload = generateRecommendationsURLAndHeaders ? {
|
|
2177
2536
|
context: {
|
|
2178
2537
|
selectedLocationIds,
|
|
2179
|
-
selectedConstraintIds,
|
|
2538
|
+
selectedConstraintIds: effectiveSelectedConstraintIds,
|
|
2180
2539
|
constraintValues,
|
|
2181
|
-
selectedOptimizationIds,
|
|
2540
|
+
selectedOptimizationIds: effectiveSelectedOptimizationIds,
|
|
2182
2541
|
optimizationValues,
|
|
2183
|
-
timezone:
|
|
2542
|
+
timezone: selectedLocationTimezone
|
|
2184
2543
|
},
|
|
2185
2544
|
locations: selectedLocations,
|
|
2186
2545
|
employees,
|
|
2187
|
-
openShifts: openShifts.filter(
|
|
2188
|
-
|
|
2546
|
+
openShifts: openShifts.filter(
|
|
2547
|
+
(s) => {
|
|
2548
|
+
var _a2;
|
|
2549
|
+
return !s.employeeId && selectedLocationIds.includes(String((_a2 = s.location) == null ? void 0 : _a2.id));
|
|
2550
|
+
}
|
|
2551
|
+
),
|
|
2552
|
+
assignedShifts: openShifts.filter(
|
|
2553
|
+
(s) => {
|
|
2554
|
+
var _a2;
|
|
2555
|
+
return Boolean(s.employeeId) && selectedLocationIds.includes(String((_a2 = s.location) == null ? void 0 : _a2.id));
|
|
2556
|
+
}
|
|
2557
|
+
),
|
|
2189
2558
|
timeOffs: timeOffs != null ? timeOffs : [],
|
|
2190
2559
|
explanation: response.explanation
|
|
2191
2560
|
} : void 0;
|
|
@@ -2204,17 +2573,34 @@ function AutoSchedulerModal({
|
|
|
2204
2573
|
setScreen("configure");
|
|
2205
2574
|
};
|
|
2206
2575
|
const handleToggleLocation = (locationId) => {
|
|
2207
|
-
if (!locationIdsWithOpenShifts.has(locationId)) {
|
|
2576
|
+
if (!onGenerate && !locationIdsWithOpenShifts.has(locationId)) {
|
|
2208
2577
|
return;
|
|
2209
2578
|
}
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2579
|
+
const newLocationIds = selectedLocationIds.includes(locationId) ? selectedLocationIds.filter((id) => id !== locationId) : [...selectedLocationIds, locationId];
|
|
2580
|
+
setSelectedLocationIds(newLocationIds);
|
|
2581
|
+
const filteredSubs = selectedSubLocationIds.filter((subId) => {
|
|
2582
|
+
const sub = subLocations == null ? void 0 : subLocations.find((s) => s.id === subId);
|
|
2583
|
+
return sub ? newLocationIds.includes(sub.parentId) : false;
|
|
2584
|
+
});
|
|
2585
|
+
if (filteredSubs.length !== selectedSubLocationIds.length) {
|
|
2586
|
+
setSelectedSubLocationIds(filteredSubs);
|
|
2213
2587
|
}
|
|
2214
|
-
|
|
2588
|
+
onSelectionChange == null ? void 0 : onSelectionChange(
|
|
2589
|
+
newLocationIds,
|
|
2590
|
+
filteredSubs.length !== selectedSubLocationIds.length ? filteredSubs : selectedSubLocationIds
|
|
2591
|
+
);
|
|
2592
|
+
};
|
|
2593
|
+
const handleToggleSubLocation = (subLocationId) => {
|
|
2594
|
+
const next = selectedSubLocationIds.includes(subLocationId) ? selectedSubLocationIds.filter((id) => id !== subLocationId) : [...selectedSubLocationIds, subLocationId];
|
|
2595
|
+
setSelectedSubLocationIds(next);
|
|
2596
|
+
onSelectionChange == null ? void 0 : onSelectionChange(selectedLocationIds, next);
|
|
2597
|
+
};
|
|
2598
|
+
const handleToggleTeam = (teamId) => {
|
|
2599
|
+
setSelectedTeamIds((prev) => prev.includes(teamId) ? prev.filter((id) => id !== teamId) : [...prev, teamId]);
|
|
2215
2600
|
};
|
|
2216
2601
|
const handleToggleConstraint = (constraintId) => {
|
|
2217
2602
|
setSelectedConstraintIds((prev) => {
|
|
2603
|
+
var _a;
|
|
2218
2604
|
if (prev.includes(constraintId)) {
|
|
2219
2605
|
setConstraintValues((values) => {
|
|
2220
2606
|
const next = { ...values };
|
|
@@ -2223,10 +2609,11 @@ function AutoSchedulerModal({
|
|
|
2223
2609
|
});
|
|
2224
2610
|
return prev.filter((id) => id !== constraintId);
|
|
2225
2611
|
}
|
|
2226
|
-
|
|
2612
|
+
const defaultVal = (_a = effectiveHardConstraintOptions.find((o) => o.id === constraintId)) == null ? void 0 : _a.defaultValue;
|
|
2613
|
+
if (defaultVal) {
|
|
2227
2614
|
setConstraintValues((values) => ({
|
|
2228
2615
|
...values,
|
|
2229
|
-
[constraintId]:
|
|
2616
|
+
[constraintId]: defaultVal
|
|
2230
2617
|
}));
|
|
2231
2618
|
}
|
|
2232
2619
|
return [...prev, constraintId];
|
|
@@ -2278,12 +2665,12 @@ function AutoSchedulerModal({
|
|
|
2278
2665
|
const configurationContent = /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react4.Stack, { spacing: "20px", children: [
|
|
2279
2666
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react4.Box, { sx: styles.banner, children: [
|
|
2280
2667
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Box, { sx: styles.bannerIcon, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(MagicIcon, {}) }),
|
|
2281
|
-
|
|
2282
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Text, { sx: styles.bannerTitle, children:
|
|
2283
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Text, { sx: styles.bannerText, children:
|
|
2668
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react4.Stack, { spacing: "4px", flex: 1, children: [
|
|
2669
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Text, { sx: styles.bannerTitle, children: bannerTitle }),
|
|
2670
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Text, { sx: styles.bannerText, children: bannerText })
|
|
2284
2671
|
] })
|
|
2285
2672
|
] }),
|
|
2286
|
-
openShifts.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
2673
|
+
!onGenerate && openShifts.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
2287
2674
|
import_react4.Box,
|
|
2288
2675
|
{
|
|
2289
2676
|
sx: {
|
|
@@ -2326,30 +2713,33 @@ function AutoSchedulerModal({
|
|
|
2326
2713
|
] }) }) }),
|
|
2327
2714
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.PopoverContent, { sx: styles.popoverContent, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.PopoverBody, { p: 0, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Stack, { spacing: "4px", children: jurisdictions.map((jurisdiction) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react4.Box, { children: [
|
|
2328
2715
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Text, { sx: styles.groupHeader, children: jurisdiction.title }),
|
|
2329
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Stack, { spacing: "2px", children: jurisdiction.locations.map((location) =>
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2716
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Stack, { spacing: "2px", children: jurisdiction.locations.map((location) => {
|
|
2717
|
+
const isEnabled = onGenerate || locationIdsWithOpenShifts.has(location.id);
|
|
2718
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
2719
|
+
import_react4.HStack,
|
|
2720
|
+
{
|
|
2721
|
+
as: "label",
|
|
2722
|
+
sx: {
|
|
2723
|
+
...styles.optionRow,
|
|
2724
|
+
cursor: isEnabled ? "pointer" : "not-allowed",
|
|
2725
|
+
opacity: isEnabled ? 1 : 0.55
|
|
2726
|
+
},
|
|
2727
|
+
children: [
|
|
2728
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
2729
|
+
import_react4.Checkbox,
|
|
2730
|
+
{
|
|
2731
|
+
id: `location-${location.id}`,
|
|
2732
|
+
isChecked: selectedLocationIds.includes(location.id),
|
|
2733
|
+
isDisabled: !isEnabled,
|
|
2734
|
+
onChange: () => handleToggleLocation(location.id)
|
|
2735
|
+
}
|
|
2736
|
+
),
|
|
2737
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Text, { sx: styles.optionLabel, children: location.name })
|
|
2738
|
+
]
|
|
2337
2739
|
},
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
{
|
|
2342
|
-
id: `location-${location.id}`,
|
|
2343
|
-
isChecked: selectedLocationIds.includes(location.id),
|
|
2344
|
-
isDisabled: !locationIdsWithOpenShifts.has(location.id),
|
|
2345
|
-
onChange: () => handleToggleLocation(location.id)
|
|
2346
|
-
}
|
|
2347
|
-
),
|
|
2348
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Text, { sx: styles.optionLabel, children: location.name })
|
|
2349
|
-
]
|
|
2350
|
-
},
|
|
2351
|
-
location.id
|
|
2352
|
-
)) })
|
|
2740
|
+
location.id
|
|
2741
|
+
);
|
|
2742
|
+
}) })
|
|
2353
2743
|
] }, jurisdiction.id)) }) }) })
|
|
2354
2744
|
] }),
|
|
2355
2745
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Stack, { spacing: "8px", children: selectedByJurisdiction.map((jurisdiction) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react4.Box, { sx: styles.selectedBanner, children: [
|
|
@@ -2363,11 +2753,128 @@ function AutoSchedulerModal({
|
|
|
2363
2753
|
] })
|
|
2364
2754
|
] }, jurisdiction.id)) })
|
|
2365
2755
|
] }),
|
|
2756
|
+
showSubLocations && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react4.Stack, { spacing: "8px", sx: styles.locationSection, children: [
|
|
2757
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Stack, { spacing: "4px", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react4.HStack, { sx: styles.locationLabelRow, children: [
|
|
2758
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Text, { sx: styles.locationLabel, children: "Sub-location" }),
|
|
2759
|
+
selectedSubLocationIds.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Badge, { sx: styles.locationBadge, children: selectedSubLocationIds.length })
|
|
2760
|
+
] }) }),
|
|
2761
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react4.Popover, { placement: "bottom-start", matchWidth: true, isLazy: true, children: [
|
|
2762
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.PopoverTrigger, { children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
2763
|
+
import_react4.Button,
|
|
2764
|
+
{
|
|
2765
|
+
variant: "unstyled",
|
|
2766
|
+
isDisabled: selectedLocationIds.length === 0 || filteredSubLocations.length === 0,
|
|
2767
|
+
sx: {
|
|
2768
|
+
...styles.locationTrigger,
|
|
2769
|
+
...selectedLocationIds.length === 0 || filteredSubLocations.length === 0 ? { opacity: 0.5, cursor: "not-allowed" } : {}
|
|
2770
|
+
},
|
|
2771
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react4.HStack, { justify: "space-between", w: "full", children: [
|
|
2772
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
2773
|
+
import_react4.Text,
|
|
2774
|
+
{
|
|
2775
|
+
sx: {
|
|
2776
|
+
...styles.locationTriggerText,
|
|
2777
|
+
...selectedSubLocationIds.length > 0 ? { color: "#303030" } : {}
|
|
2778
|
+
},
|
|
2779
|
+
noOfLines: 1,
|
|
2780
|
+
children: selectedSubLocationIds.length > 0 ? filteredSubLocations.filter((s) => selectedSubLocationIds.includes(s.id)).map((s) => s.name).join(", ") : "Select sub-locations"
|
|
2781
|
+
}
|
|
2782
|
+
),
|
|
2783
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Box, { color: "#5c5c5c", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ChevronDownIcon, {}) })
|
|
2784
|
+
] })
|
|
2785
|
+
}
|
|
2786
|
+
) }),
|
|
2787
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.PopoverContent, { sx: styles.popoverContent, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.PopoverBody, { p: 0, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Stack, { spacing: "2px", children: filteredSubLocations.map((sub) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react4.HStack, { as: "label", sx: { ...styles.optionRow, cursor: "pointer" }, children: [
|
|
2788
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
2789
|
+
import_react4.Checkbox,
|
|
2790
|
+
{
|
|
2791
|
+
id: `sublocation-${sub.id}`,
|
|
2792
|
+
isChecked: selectedSubLocationIds.includes(sub.id),
|
|
2793
|
+
onChange: () => handleToggleSubLocation(sub.id)
|
|
2794
|
+
}
|
|
2795
|
+
),
|
|
2796
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Text, { sx: styles.optionLabel, children: sub.name })
|
|
2797
|
+
] }, sub.id)) }) }) })
|
|
2798
|
+
] })
|
|
2799
|
+
] }),
|
|
2800
|
+
showTeams && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react4.Stack, { spacing: "8px", sx: styles.locationSection, children: [
|
|
2801
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Stack, { spacing: "4px", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react4.HStack, { sx: styles.locationLabelRow, children: [
|
|
2802
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Text, { sx: styles.locationLabel, children: "Teams" }),
|
|
2803
|
+
selectedTeamIds.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Badge, { sx: styles.locationBadge, children: selectedTeamIds.length })
|
|
2804
|
+
] }) }),
|
|
2805
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react4.Popover, { placement: "bottom-start", matchWidth: true, isLazy: true, children: [
|
|
2806
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.PopoverTrigger, { children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
2807
|
+
import_react4.Button,
|
|
2808
|
+
{
|
|
2809
|
+
variant: "unstyled",
|
|
2810
|
+
isDisabled: !teams || teams.length === 0,
|
|
2811
|
+
sx: {
|
|
2812
|
+
...styles.locationTrigger,
|
|
2813
|
+
...!teams || teams.length === 0 ? { opacity: 0.5, cursor: "not-allowed" } : {}
|
|
2814
|
+
},
|
|
2815
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react4.HStack, { justify: "space-between", w: "full", children: [
|
|
2816
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
2817
|
+
import_react4.Text,
|
|
2818
|
+
{
|
|
2819
|
+
sx: {
|
|
2820
|
+
...styles.locationTriggerText,
|
|
2821
|
+
...selectedTeamIds.length > 0 ? { color: "#303030" } : {}
|
|
2822
|
+
},
|
|
2823
|
+
noOfLines: 1,
|
|
2824
|
+
children: selectedTeamIds.length > 0 ? (teams != null ? teams : []).filter((t) => selectedTeamIds.includes(t.id)).map((t) => t.name).join(", ") : "Select teams"
|
|
2825
|
+
}
|
|
2826
|
+
),
|
|
2827
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Box, { color: "#5c5c5c", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ChevronDownIcon, {}) })
|
|
2828
|
+
] })
|
|
2829
|
+
}
|
|
2830
|
+
) }),
|
|
2831
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.PopoverContent, { sx: styles.popoverContent, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.PopoverBody, { p: 0, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Stack, { spacing: "2px", children: (teams != null ? teams : []).map((team) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react4.HStack, { as: "label", sx: { ...styles.optionRow, cursor: "pointer" }, children: [
|
|
2832
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
2833
|
+
import_react4.Checkbox,
|
|
2834
|
+
{
|
|
2835
|
+
id: `team-${team.id}`,
|
|
2836
|
+
isChecked: selectedTeamIds.includes(team.id),
|
|
2837
|
+
onChange: () => handleToggleTeam(team.id)
|
|
2838
|
+
}
|
|
2839
|
+
),
|
|
2840
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Text, { sx: styles.optionLabel, children: team.name })
|
|
2841
|
+
] }, team.id)) }) }) })
|
|
2842
|
+
] })
|
|
2843
|
+
] }),
|
|
2844
|
+
scheduleWeekStartYmd ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react4.Stack, { spacing: "8px", sx: styles.locationSection, children: [
|
|
2845
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Text, { sx: { ...styles.locationLabel, fontWeight: 600 }, children: "Generate schedule for:" }),
|
|
2846
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.HStack, { spacing: 2, flexWrap: "wrap", children: weekDatesOrdered.map((ymd) => {
|
|
2847
|
+
const selected = selectedScheduleDates.includes(ymd);
|
|
2848
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
2849
|
+
import_react4.Button,
|
|
2850
|
+
{
|
|
2851
|
+
type: "button",
|
|
2852
|
+
variant: "unstyled",
|
|
2853
|
+
onClick: () => toggleScheduleDate(ymd),
|
|
2854
|
+
borderRadius: "full",
|
|
2855
|
+
minW: "40px",
|
|
2856
|
+
h: "40px",
|
|
2857
|
+
px: 0,
|
|
2858
|
+
fontSize: "sm",
|
|
2859
|
+
fontWeight: 600,
|
|
2860
|
+
borderWidth: selected ? "2px" : "1px",
|
|
2861
|
+
borderStyle: "solid",
|
|
2862
|
+
borderColor: selected ? "#5b44ff" : "#e2e8f0",
|
|
2863
|
+
bg: selected ? "#ede9fe" : "#ffffff",
|
|
2864
|
+
color: selected ? "#5b44ff" : "#303030",
|
|
2865
|
+
"aria-pressed": selected,
|
|
2866
|
+
"aria-label": `${weekdayTwoLetterFromYmd(ymd)} ${ymd}`,
|
|
2867
|
+
children: weekdayTwoLetterFromYmd(ymd)
|
|
2868
|
+
},
|
|
2869
|
+
ymd
|
|
2870
|
+
);
|
|
2871
|
+
}) })
|
|
2872
|
+
] }) : null,
|
|
2366
2873
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react4.Stack, { spacing: "16px", sx: styles.hardConstraintsSection, children: [
|
|
2367
2874
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react4.Stack, { spacing: "4px", children: [
|
|
2368
2875
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react4.HStack, { sx: styles.hardConstraintsLabelRow, children: [
|
|
2369
2876
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Text, { sx: styles.hardConstraintsLabel, children: "Required rules" }),
|
|
2370
|
-
|
|
2877
|
+
selectedConstraintDisplayOptions.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Badge, { sx: styles.hardConstraintsBadge, children: selectedConstraintDisplayOptions.length }) : null
|
|
2371
2878
|
] }),
|
|
2372
2879
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Text, { sx: styles.hardConstraintsSubtitle, children: "Assigned shifts will be required to meet the following rules:" })
|
|
2373
2880
|
] }),
|
|
@@ -2379,35 +2886,30 @@ function AutoSchedulerModal({
|
|
|
2379
2886
|
{
|
|
2380
2887
|
sx: {
|
|
2381
2888
|
...styles.hardConstraintsTriggerText,
|
|
2382
|
-
...
|
|
2889
|
+
...selectedConstraintDisplayOptions.length > 0 ? { color: "#303030" } : {}
|
|
2383
2890
|
},
|
|
2384
2891
|
noOfLines: 1,
|
|
2385
|
-
children:
|
|
2892
|
+
children: selectedConstraintDisplayOptions.length > 0 ? selectedConstraintDisplayOptions.map((o) => o.name).join(", ") : "Select rules"
|
|
2386
2893
|
}
|
|
2387
2894
|
),
|
|
2388
2895
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Box, { color: "#5c5c5c", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ChevronDownIcon, {}) })
|
|
2389
2896
|
] }) }) }),
|
|
2390
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.PopoverContent, { sx: styles.hardConstraintsPopoverContent, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.PopoverBody, { p: 0, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Stack, { spacing: "4px", children:
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Text, { sx: styles.hardConstraintsOptionLabel, children: option.name })
|
|
2405
|
-
]
|
|
2406
|
-
},
|
|
2407
|
-
option.id
|
|
2408
|
-
)) }) }) })
|
|
2897
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.PopoverContent, { sx: styles.hardConstraintsPopoverContent, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.PopoverBody, { p: 0, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Stack, { spacing: "4px", children: effectiveHardConstraintOptions.map((option) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react4.HStack, { as: "label", sx: { ...styles.hardConstraintsOptionRow, cursor: "pointer" }, children: [
|
|
2898
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
2899
|
+
import_react4.Checkbox,
|
|
2900
|
+
{
|
|
2901
|
+
id: `constraint-${option.id}`,
|
|
2902
|
+
isChecked: selectedConstraintIds.includes(option.id) || Boolean(option.readOnly && option.defaultSelected),
|
|
2903
|
+
isDisabled: option.readOnly,
|
|
2904
|
+
onChange: () => {
|
|
2905
|
+
if (!option.readOnly) handleToggleConstraint(option.id);
|
|
2906
|
+
}
|
|
2907
|
+
}
|
|
2908
|
+
),
|
|
2909
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Text, { sx: styles.hardConstraintsOptionLabel, children: option.name })
|
|
2910
|
+
] }, option.id)) }) }) })
|
|
2409
2911
|
] }),
|
|
2410
|
-
selectedConstraints.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Box, { sx: styles.hardConstraintsInputsContainer, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Stack, { spacing: "16px", children: selectedConstraints.map((option, index) => {
|
|
2912
|
+
selectedConstraints.filter((o) => o.defaultValue != null).length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Box, { sx: styles.hardConstraintsInputsContainer, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Stack, { spacing: "16px", children: selectedConstraints.filter((o) => o.defaultValue != null).map((option, index) => {
|
|
2411
2913
|
var _a;
|
|
2412
2914
|
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react4.Stack, { sx: styles.hardConstraintsInputItem, children: [
|
|
2413
2915
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react4.Text, { sx: styles.hardConstraintsInputLabel, children: [
|
|
@@ -2425,13 +2927,13 @@ function AutoSchedulerModal({
|
|
|
2425
2927
|
...prev,
|
|
2426
2928
|
[option.id]: event.target.value
|
|
2427
2929
|
})),
|
|
2428
|
-
min: option.
|
|
2429
|
-
max: option.
|
|
2430
|
-
step: option.
|
|
2930
|
+
min: option.min,
|
|
2931
|
+
max: option.max,
|
|
2932
|
+
step: option.max != null ? void 0 : "any",
|
|
2431
2933
|
sx: styles.hardConstraintsInputField
|
|
2432
2934
|
}
|
|
2433
2935
|
),
|
|
2434
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.InputRightAddon, { sx: styles.optimizationSuffix, children: option.
|
|
2936
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.InputRightAddon, { sx: styles.optimizationSuffix, children: option.suffix })
|
|
2435
2937
|
] })
|
|
2436
2938
|
] }, option.id);
|
|
2437
2939
|
}) }) }) : null
|
|
@@ -2440,89 +2942,100 @@ function AutoSchedulerModal({
|
|
|
2440
2942
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react4.Stack, { spacing: "4px", children: [
|
|
2441
2943
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react4.HStack, { sx: styles.optimizationLabelRow, children: [
|
|
2442
2944
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Text, { sx: styles.optimizationLabel, children: "Optimization" }),
|
|
2443
|
-
|
|
2945
|
+
selectedOptimizationDisplayOptions.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Badge, { sx: styles.optimizationBadge, children: selectedOptimizationDisplayOptions.length }) : null
|
|
2444
2946
|
] }),
|
|
2445
2947
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Text, { sx: styles.optimizationSubtitle, children: "Your schedule will be optimized based on the following priorities:" })
|
|
2446
2948
|
] }),
|
|
2447
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react4.Popover, { placement: "
|
|
2949
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react4.Popover, { placement: "top-start", strategy: "fixed", matchWidth: true, isLazy: true, gutter: 4, children: [
|
|
2448
2950
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.PopoverTrigger, { children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Button, { variant: "unstyled", sx: styles.optimizationTrigger, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react4.HStack, { justify: "space-between", w: "full", children: [
|
|
2449
2951
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
2450
2952
|
import_react4.Text,
|
|
2451
2953
|
{
|
|
2452
2954
|
sx: {
|
|
2453
2955
|
...styles.optimizationTriggerText,
|
|
2454
|
-
...
|
|
2956
|
+
...selectedOptimizationDisplayOptions.length > 0 ? { color: "#303030" } : {}
|
|
2455
2957
|
},
|
|
2456
2958
|
noOfLines: 1,
|
|
2457
|
-
children:
|
|
2959
|
+
children: selectedOptimizationDisplayOptions.length > 0 ? selectedOptimizationDisplayOptions.map((o) => o.title).join(", ") : "Select optimization priorities"
|
|
2458
2960
|
}
|
|
2459
2961
|
),
|
|
2460
2962
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Box, { color: "#5c5c5c", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ChevronDownIcon, {}) })
|
|
2461
2963
|
] }) }) }),
|
|
2462
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
|
|
2471
|
-
|
|
2472
|
-
|
|
2473
|
-
import_react4.Checkbox,
|
|
2474
|
-
{
|
|
2475
|
-
id: `optimization-${option.id}`,
|
|
2476
|
-
isChecked: selectedOptimizationIds.includes(option.id),
|
|
2477
|
-
isDisabled: !selectedOptimizationIds.includes(option.id) && selectedOptimizationIds.length >= 3,
|
|
2478
|
-
onChange: () => handleToggleOptimization(option.id)
|
|
2479
|
-
}
|
|
2480
|
-
),
|
|
2481
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Text, { sx: styles.optimizationOptionLabel, children: option.title })
|
|
2482
|
-
] }),
|
|
2483
|
-
option.subtitle ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Text, { sx: styles.optimizationOptionHelp, children: option.subtitle }) : null
|
|
2484
|
-
]
|
|
2964
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Portal, { appendToParentPortal: false, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
2965
|
+
import_react4.PopoverContent,
|
|
2966
|
+
{
|
|
2967
|
+
sx: {
|
|
2968
|
+
...styles.optimizationPopoverContent,
|
|
2969
|
+
maxH: "min(560px, calc(100vh - 96px))",
|
|
2970
|
+
overflowY: "auto",
|
|
2971
|
+
zIndex: 1500,
|
|
2972
|
+
scrollbarWidth: "none",
|
|
2973
|
+
msOverflowStyle: "none",
|
|
2974
|
+
"&::-webkit-scrollbar": { display: "none" }
|
|
2485
2975
|
},
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
2976
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.PopoverBody, { p: 0, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Stack, { spacing: "4px", children: effectiveSoftConstraintGroups.map((group) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react4.Box, { children: [
|
|
2977
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Text, { sx: styles.optimizationGroupHeader, children: group.name }),
|
|
2978
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Stack, { spacing: "2px", children: group.options.map((option) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react4.Box, { children: [
|
|
2979
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
2980
|
+
import_react4.Stack,
|
|
2981
|
+
{
|
|
2982
|
+
as: "label",
|
|
2983
|
+
spacing: "2px",
|
|
2984
|
+
sx: { ...styles.optimizationOptionRow, cursor: "pointer" },
|
|
2985
|
+
children: [
|
|
2986
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react4.HStack, { spacing: "8px", children: [
|
|
2987
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
2988
|
+
import_react4.Checkbox,
|
|
2989
|
+
{
|
|
2990
|
+
id: `optimization-${option.id}`,
|
|
2991
|
+
isChecked: selectedOptimizationIds.includes(option.id) || Boolean(option.readOnly && option.defaultSelected),
|
|
2992
|
+
isDisabled: option.readOnly || !selectedOptimizationIds.includes(option.id) && selectedOptimizationIds.length >= 3,
|
|
2993
|
+
onChange: () => {
|
|
2994
|
+
if (!option.readOnly) handleToggleOptimization(option.id);
|
|
2995
|
+
}
|
|
2996
|
+
}
|
|
2997
|
+
),
|
|
2998
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Text, { sx: styles.optimizationOptionLabel, children: option.title })
|
|
2999
|
+
] }),
|
|
3000
|
+
option.subtitle ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Text, { sx: styles.optimizationOptionHelp, children: option.subtitle }) : null
|
|
3001
|
+
]
|
|
3002
|
+
}
|
|
3003
|
+
),
|
|
3004
|
+
optimizationFooter && (option.id === "cost_to_sales_ratio" || option.id === "maximize_sales") ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Box, { px: "12px", pt: "4px", pb: "6px", children: resolveOptimizationFooter(optimizationFooter, option.id) }) : null
|
|
3005
|
+
] }, option.id)) })
|
|
3006
|
+
] }, group.id)) }) })
|
|
3007
|
+
}
|
|
3008
|
+
) })
|
|
2489
3009
|
] }),
|
|
2490
3010
|
selectedOptimizationOptions.length >= 2 ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react4.Box, { sx: styles.optimizationInputsContainer, children: [
|
|
2491
3011
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Stack, { spacing: "16px", children: selectedOptimizationOptions.map((option, index) => {
|
|
2492
3012
|
var _a;
|
|
2493
|
-
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
2494
|
-
import_react4.
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
|
|
2518
|
-
|
|
2519
|
-
),
|
|
2520
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.InputRightAddon, { sx: styles.optimizationSuffix, children: "points" })
|
|
2521
|
-
] })
|
|
2522
|
-
] })
|
|
2523
|
-
},
|
|
2524
|
-
option.id
|
|
2525
|
-
);
|
|
3013
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Stack, { sx: styles.optimizationInputItem, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react4.Stack, { sx: styles.hardConstraintsInputItem, children: [
|
|
3014
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react4.Text, { sx: styles.hardConstraintsInputLabel, children: [
|
|
3015
|
+
index + 1,
|
|
3016
|
+
". ",
|
|
3017
|
+
option.title
|
|
3018
|
+
] }),
|
|
3019
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react4.InputGroup, { children: [
|
|
3020
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
3021
|
+
import_react4.Input,
|
|
3022
|
+
{
|
|
3023
|
+
type: "text",
|
|
3024
|
+
value: (_a = optimizationValues[`${option.id}_weightPoints`]) != null ? _a : "1",
|
|
3025
|
+
onChange: (e) => {
|
|
3026
|
+
const val = e.target.value;
|
|
3027
|
+
if (val !== "" && (!/^\d+$/.test(val) || Number(val) < 1)) return;
|
|
3028
|
+
setOptimizationValues((prev) => ({
|
|
3029
|
+
...prev,
|
|
3030
|
+
[`${option.id}_weightPoints`]: val
|
|
3031
|
+
}));
|
|
3032
|
+
},
|
|
3033
|
+
sx: styles.hardConstraintsInputField
|
|
3034
|
+
}
|
|
3035
|
+
),
|
|
3036
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.InputRightAddon, { sx: styles.optimizationSuffix, children: "points" })
|
|
3037
|
+
] })
|
|
3038
|
+
] }) }, option.id);
|
|
2526
3039
|
}) }),
|
|
2527
3040
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Divider, { sx: { ...styles.optimizationDivider, my: "16px" } }),
|
|
2528
3041
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Text, { sx: styles.optimizationSummary, children: allocationSummary })
|
|
@@ -2531,7 +3044,9 @@ function AutoSchedulerModal({
|
|
|
2531
3044
|
] });
|
|
2532
3045
|
const stepsContent = /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react4.Stack, { spacing: "16px", sx: styles.stepsContainer, children: [
|
|
2533
3046
|
showFailed ? violationPanel : null,
|
|
2534
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Stack, { spacing: "14px", sx: styles.stepsList, children: SCHEDULE_STEPS.map((
|
|
3047
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Stack, { spacing: "14px", sx: styles.stepsList, children: SCHEDULE_STEPS.map((rawStep, index) => {
|
|
3048
|
+
const isQueueWait = index === 1 && solveStatus === "QUEUED";
|
|
3049
|
+
const step = isQueueWait ? { title: "Waiting in queue\u2026", subtitle: void 0 } : rawStep;
|
|
2535
3050
|
const status = stepStatusForIndex(index);
|
|
2536
3051
|
const isFailedStep = showFailed && index === activeStepIndex;
|
|
2537
3052
|
const iconStyles = {
|
|
@@ -2549,26 +3064,26 @@ function AutoSchedulerModal({
|
|
|
2549
3064
|
...isFailedStep ? styles.stepTitleFailed : null
|
|
2550
3065
|
};
|
|
2551
3066
|
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react4.HStack, { sx: styles.stepRow, children: [
|
|
2552
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Box, { sx: iconStyles, children: isFailedStep ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(FailedIcon, { width: 12, height: 12 }) : status === "active" ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Spinner, { size: "sm", thickness: "2px", speed: "0.7s" }) : status === "done" ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(CheckIcon, { width:
|
|
3067
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Box, { sx: iconStyles, children: isFailedStep ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(FailedIcon, { width: 12, height: 12 }) : status === "active" ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Spinner, { size: "sm", thickness: "2px", speed: "0.7s" }) : status === "done" ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(CheckIcon, { width: 10, height: 10 }) : null }),
|
|
2553
3068
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react4.Box, { children: [
|
|
2554
3069
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Text, { sx: titleStyles, children: step.title }),
|
|
2555
3070
|
status === "active" && step.subtitle ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Text, { sx: styles.stepSubtitle, children: step.subtitle }) : null
|
|
2556
3071
|
] })
|
|
2557
|
-
] },
|
|
3072
|
+
] }, rawStep.title);
|
|
2558
3073
|
}) })
|
|
2559
3074
|
] });
|
|
2560
|
-
const
|
|
3075
|
+
const footerButtons = showConfiguration ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
|
|
2561
3076
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Button, { variant: "outline", onClick: onClose, sx: styles.cancelButton, children: cancelLabel }),
|
|
2562
3077
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
2563
3078
|
import_react4.Button,
|
|
2564
3079
|
{
|
|
2565
3080
|
onClick: handleCreateSchedule,
|
|
2566
|
-
isDisabled: selectedCount === 0 || openShifts.length === 0 || hasEmptyInputs,
|
|
3081
|
+
isDisabled: selectedCount === 0 || !onGenerate && openShifts.length === 0 || hasEmptyInputs || Boolean(scheduleWeekStartYmd) && selectedScheduleDates.length === 0,
|
|
2567
3082
|
sx: styles.primaryButton,
|
|
2568
3083
|
children: primaryActionLabel
|
|
2569
3084
|
}
|
|
2570
3085
|
)
|
|
2571
|
-
] }) : showFailed ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
3086
|
+
] }) : showFailed ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
|
|
2572
3087
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
2573
3088
|
import_react4.Button,
|
|
2574
3089
|
{
|
|
@@ -2577,10 +3092,11 @@ function AutoSchedulerModal({
|
|
|
2577
3092
|
if (lastFailure) {
|
|
2578
3093
|
onFixManually == null ? void 0 : onFixManually(lastFailure, {
|
|
2579
3094
|
selectedLocationIds,
|
|
2580
|
-
selectedConstraintIds,
|
|
3095
|
+
selectedConstraintIds: effectiveSelectedConstraintIds,
|
|
2581
3096
|
constraintValues,
|
|
2582
|
-
selectedOptimizationIds,
|
|
2583
|
-
optimizationValues
|
|
3097
|
+
selectedOptimizationIds: effectiveSelectedOptimizationIds,
|
|
3098
|
+
optimizationValues,
|
|
3099
|
+
selectedBuiltinIds: defaultSelectedBuiltinIds
|
|
2584
3100
|
});
|
|
2585
3101
|
}
|
|
2586
3102
|
onClose();
|
|
@@ -2591,31 +3107,75 @@ function AutoSchedulerModal({
|
|
|
2591
3107
|
),
|
|
2592
3108
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Button, { onClick: handleAdjustConstraints, sx: styles.failedPrimaryButton, children: "Adjust constraints" })
|
|
2593
3109
|
] }) : null;
|
|
2594
|
-
const
|
|
2595
|
-
|
|
2596
|
-
|
|
2597
|
-
|
|
2598
|
-
|
|
2599
|
-
|
|
2600
|
-
|
|
2601
|
-
|
|
2602
|
-
|
|
2603
|
-
|
|
2604
|
-
|
|
2605
|
-
|
|
2606
|
-
|
|
2607
|
-
|
|
2608
|
-
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
|
|
2612
|
-
|
|
2613
|
-
|
|
2614
|
-
|
|
3110
|
+
const bodyStyles = showConfiguration ? styles.body : { ...styles.body, ...styles.creatingBody };
|
|
3111
|
+
const bodyContent = showConfiguration ? configurationContent : stepsContent;
|
|
3112
|
+
const modalContent = /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
3113
|
+
import_react4.Modal,
|
|
3114
|
+
{
|
|
3115
|
+
isOpen,
|
|
3116
|
+
onClose,
|
|
3117
|
+
isCentered: true,
|
|
3118
|
+
motionPreset: "scale",
|
|
3119
|
+
closeOnOverlayClick: false,
|
|
3120
|
+
closeOnEsc: false,
|
|
3121
|
+
trapFocus: false,
|
|
3122
|
+
children: [
|
|
3123
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.ModalOverlay, { sx: styles.overlay }),
|
|
3124
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react4.ModalContent, { sx: styles.content, children: [
|
|
3125
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.ModalHeader, { sx: styles.header, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react4.HStack, { justify: "space-between", w: "full", children: [
|
|
3126
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Text, { sx: styles.title, children: modalTitle }),
|
|
3127
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
3128
|
+
import_react4.IconButton,
|
|
3129
|
+
{
|
|
3130
|
+
"aria-label": "Close modal",
|
|
3131
|
+
variant: "ghost",
|
|
3132
|
+
size: "sm",
|
|
3133
|
+
icon: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(CloseIcon, {}),
|
|
3134
|
+
onClick: onClose,
|
|
3135
|
+
sx: styles.closeButton
|
|
3136
|
+
}
|
|
3137
|
+
)
|
|
3138
|
+
] }) }),
|
|
3139
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.ModalBody, { sx: bodyStyles, children: bodyContent }),
|
|
3140
|
+
footerButtons && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.ModalFooter, { sx: showFailed ? styles.failedFooter : styles.footer, children: footerButtons })
|
|
3141
|
+
] })
|
|
3142
|
+
]
|
|
3143
|
+
}
|
|
3144
|
+
);
|
|
3145
|
+
const drawerFooterNode = footerButtons ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
3146
|
+
import_react4.HStack,
|
|
3147
|
+
{
|
|
3148
|
+
align: "center",
|
|
3149
|
+
justify: "flex-end",
|
|
3150
|
+
px: 6,
|
|
3151
|
+
h: "68px",
|
|
3152
|
+
bg: "gray.50",
|
|
3153
|
+
borderTop: "1px solid",
|
|
3154
|
+
borderColor: "gray.200",
|
|
3155
|
+
gap: 2,
|
|
3156
|
+
flexShrink: 0,
|
|
3157
|
+
w: "full",
|
|
3158
|
+
children: footerButtons
|
|
3159
|
+
}
|
|
3160
|
+
) : void 0;
|
|
3161
|
+
const drawerContent = /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
3162
|
+
import_web_ui.Drawer,
|
|
3163
|
+
{
|
|
3164
|
+
isOpen,
|
|
3165
|
+
onClose,
|
|
3166
|
+
title: modalTitle,
|
|
3167
|
+
closeOnOverlayClick: false,
|
|
3168
|
+
maxW: DRAWER_WIDTH_MAP[drawerSize],
|
|
3169
|
+
bodySx: bodyStyles,
|
|
3170
|
+
footer: drawerFooterNode,
|
|
3171
|
+
children: bodyContent
|
|
3172
|
+
}
|
|
3173
|
+
);
|
|
3174
|
+
const content = displayMode === "drawer" ? drawerContent : modalContent;
|
|
2615
3175
|
if (theme || cssVarsRoot) {
|
|
2616
|
-
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.ChakraProvider, { theme: mergedTheme, cssVarsRoot, children:
|
|
3176
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.ChakraProvider, { theme: mergedTheme, cssVarsRoot, children: content });
|
|
2617
3177
|
}
|
|
2618
|
-
return
|
|
3178
|
+
return content;
|
|
2619
3179
|
}
|
|
2620
3180
|
|
|
2621
3181
|
// src/AutoSchedulerModalWithProvider.tsx
|
|
@@ -2692,11 +3252,17 @@ var RequestError = class extends Error {
|
|
|
2692
3252
|
0 && (module.exports = {
|
|
2693
3253
|
AutoSchedulerModal,
|
|
2694
3254
|
AutoSchedulerModalWithProvider,
|
|
3255
|
+
InfeasibleScheduleError,
|
|
3256
|
+
MAXIMIZE_CONVERSION_RATE_RULE_ID,
|
|
2695
3257
|
RequestError,
|
|
3258
|
+
TRAFFIC_FORECAST_INCENTIVE_RULE_ID,
|
|
2696
3259
|
ViolatedConstraintsPanel,
|
|
2697
3260
|
ViolatedConstraintsPanelWithProvider,
|
|
2698
3261
|
buildRulesPayload,
|
|
2699
3262
|
buildSchedulePayload,
|
|
2700
|
-
|
|
3263
|
+
defaultSelectedScheduleDates,
|
|
3264
|
+
getFriendlyRuleName,
|
|
3265
|
+
getSequentialWeekDatesFromWeekStart,
|
|
3266
|
+
weekdayTwoLetterFromYmd
|
|
2701
3267
|
});
|
|
2702
3268
|
//# sourceMappingURL=index.cjs.map
|