@easyteam/auto-scheduler-modal-ui 0.1.2 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs 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 src_exports = {};
33
- __export(src_exports, {
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
- getFriendlyRuleName: () => getFriendlyRuleName
44
+ defaultSelectedScheduleDates: () => defaultSelectedScheduleDates,
45
+ getFriendlyRuleName: () => getFriendlyRuleName,
46
+ getSequentialWeekDatesFromWeekStart: () => getSequentialWeekDatesFromWeekStart,
47
+ weekdayTwoLetterFromYmd: () => weekdayTwoLetterFromYmd
42
48
  });
43
- module.exports = __toCommonJS(src_exports);
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 20 20",
185
- fill: "none",
186
- width: "20",
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
- d: "M5.5 10.5l3 3 6-6",
193
- stroke: "currentColor",
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: "#008060",
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 buildRulesPayload(selectedConstraintIds, constraintValues, selectedOptimizationIds, optimizationValues) {
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
  }
@@ -1361,7 +1389,7 @@ function buildRulesPayload(selectedConstraintIds, constraintValues, selectedOpti
1361
1389
  return { ruleIds: allSelectedRuleIds, ruleOverrides };
1362
1390
  }
1363
1391
  function toApiShift(shift, includeEmployeePreferences) {
1364
- var _a, _b;
1392
+ var _a;
1365
1393
  const startDateTime = "startDateTime" in shift ? shift.startDateTime : shift.startTime;
1366
1394
  const endDateTime = "endDateTime" in shift ? shift.endDateTime : shift.endTime;
1367
1395
  return {
@@ -1369,8 +1397,8 @@ function toApiShift(shift, includeEmployeePreferences) {
1369
1397
  startDateTime,
1370
1398
  endDateTime,
1371
1399
  ...shift.employeeId ? { employeeId: String(shift.employeeId) } : {},
1372
- requiredPosition: (_a = shift.requiredPosition) != null ? _a : void 0,
1373
- requiredCount: (_b = shift.requiredCount) != null ? _b : 1,
1400
+ requiredPosition: shift.requiredPosition || void 0,
1401
+ requiredCount: (_a = shift.requiredCount) != null ? _a : 1,
1374
1402
  location: shift.location ? {
1375
1403
  id: String(shift.location.id),
1376
1404
  timezone: "timezone" in shift.location ? shift.location.timezone : void 0,
@@ -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: openShifts.map((shift) => toApiShift(shift, includeEmployeePreferences)),
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);
@@ -1700,12 +1781,15 @@ function ViolatedConstraintsPanel({
1700
1781
  onSecondaryButtonClick,
1701
1782
  generateRecommendationsURLAndHeaders,
1702
1783
  styles: stylesProp,
1703
- styleOverrides
1784
+ styleOverrides,
1785
+ errorToast
1704
1786
  }) {
1705
1787
  const [showRecommendations, setShowRecommendations] = (0, import_react.useState)(false);
1706
1788
  const [isExpanded, setIsExpanded] = (0, import_react.useState)(true);
1707
1789
  const [recommendations, setRecommendations] = (0, import_react.useState)(null);
1708
1790
  const [isLoadingRecommendations, setIsLoadingRecommendations] = (0, import_react.useState)(false);
1791
+ const [isDisabledRecommendations, setIsDisabledRecommendations] = (0, import_react.useState)(false);
1792
+ const [recommendationsButtonText, setRecommendationsButtonText] = (0, import_react.useState)("Show recommendations");
1709
1793
  const styles = (0, import_react.useMemo)(() => {
1710
1794
  if (stylesProp) return stylesProp;
1711
1795
  return buildStyles(defaultStyles, styleOverrides);
@@ -1716,13 +1800,12 @@ function ViolatedConstraintsPanel({
1716
1800
  const displayRecommendations = recommendations !== null ? recommendations : violatedConstraints.recommendedFixes;
1717
1801
  const hasRecommendations = displayRecommendations.length > 0;
1718
1802
  const handleToggleRecommendations = (0, import_react.useCallback)(async () => {
1719
- var _a, _b;
1720
1803
  if (showRecommendations) {
1721
1804
  setShowRecommendations(false);
1722
1805
  return;
1723
1806
  }
1724
- setShowRecommendations(true);
1725
1807
  if (recommendations !== null) {
1808
+ setShowRecommendations(true);
1726
1809
  return;
1727
1810
  }
1728
1811
  if (!canFetchRecommendations) {
@@ -1741,9 +1824,21 @@ function ViolatedConstraintsPanel({
1741
1824
  }
1742
1825
  }
1743
1826
  );
1744
- const data = (_b = (_a = recResponse.data) == null ? void 0 : _a.data) != null ? _b : recResponse.data;
1745
- if (data) {
1746
- setRecommendations(data.map((r) => r.recommendation));
1827
+ const { success, data, msg } = recResponse.data;
1828
+ const { isExpired, recommendations: recommendations2 } = data != null ? data : {};
1829
+ if (!success) {
1830
+ const errorMsg = msg || "Failed to fetch recommendations";
1831
+ errorToast == null ? void 0 : errorToast(errorMsg);
1832
+ if (isExpired) {
1833
+ setIsDisabledRecommendations(true);
1834
+ setRecommendationsButtonText("Daily limit reached");
1835
+ }
1836
+ return;
1837
+ }
1838
+ setShowRecommendations(true);
1839
+ const fetchedRecommendations = recommendations2;
1840
+ if (fetchedRecommendations) {
1841
+ setRecommendations(fetchedRecommendations.map((r) => r.recommendation));
1747
1842
  } else {
1748
1843
  setRecommendations([]);
1749
1844
  }
@@ -1758,7 +1853,8 @@ function ViolatedConstraintsPanel({
1758
1853
  recommendations,
1759
1854
  canFetchRecommendations,
1760
1855
  violatedConstraints.recommendationPayload,
1761
- generateRecommendationsURLAndHeaders
1856
+ generateRecommendationsURLAndHeaders,
1857
+ errorToast
1762
1858
  ]);
1763
1859
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react2.Box, { sx: styles.violationPanel, children: [
1764
1860
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_react2.HStack, { sx: styles.violationHeaderRow, children: [
@@ -1810,9 +1906,9 @@ function ViolatedConstraintsPanel({
1810
1906
  variant: "outline",
1811
1907
  size: "xs",
1812
1908
  onClick: handleToggleRecommendations,
1813
- isDisabled: isLoadingRecommendations,
1909
+ isDisabled: isLoadingRecommendations || isDisabledRecommendations,
1814
1910
  sx: { ...styles.recommendationsToggle, minWidth: "140px" },
1815
- children: isLoadingRecommendations ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react2.Spinner, { size: "sm", thickness: "2px" }) : showRecommendations ? "Hide recommendations" : "Show recommendations"
1911
+ children: isLoadingRecommendations ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react2.Spinner, { size: "sm", thickness: "2px" }) : showRecommendations ? "Hide recommendations" : recommendationsButtonText
1816
1912
  }
1817
1913
  ) : null,
1818
1914
  showSecondaryButton && secondaryButtonTitle && onSecondaryButtonClick ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
@@ -1830,13 +1926,78 @@ function ViolatedConstraintsPanel({
1830
1926
  ] });
1831
1927
  }
1832
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
+
1833
1984
  // src/AutoSchedulerModal.tsx
1834
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" };
1835
1993
  function AutoSchedulerModal({
1994
+ displayMode,
1995
+ drawerSize = "lg",
1836
1996
  baseURL,
1837
1997
  isOpen,
1838
1998
  title = "Auto-scheduling",
1839
- bannerText,
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.",
1840
2001
  jurisdictions,
1841
2002
  openShifts,
1842
2003
  employees,
@@ -1854,22 +2015,79 @@ function AutoSchedulerModal({
1854
2015
  getTokenURLAndHeaders,
1855
2016
  onSolution,
1856
2017
  getStoredConfigForLocations,
1857
- persistConfigForLocations
2018
+ persistConfigForLocations,
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
1858
2039
  }) {
1859
2040
  const baseTheme = (0, import_react4.useTheme)();
1860
- const { chakraOverride, styleOverrides } = (0, import_react3.useMemo)(
1861
- () => resolveThemeParts(theme),
1862
- [theme]
1863
- );
2041
+ const { chakraOverride, styleOverrides } = (0, import_react3.useMemo)(() => resolveThemeParts(theme), [theme]);
1864
2042
  const mergedTheme = (0, import_react3.useMemo)(
1865
2043
  () => mergeThemeWithBase(baseTheme, chakraOverride),
1866
2044
  [baseTheme, chakraOverride]
1867
2045
  );
1868
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)([]);
1869
2086
  const [selectedConstraintIds, setSelectedConstraintIds] = (0, import_react3.useState)([]);
1870
2087
  const [constraintValues, setConstraintValues] = (0, import_react3.useState)({});
1871
2088
  const [selectedOptimizationIds, setSelectedOptimizationIds] = (0, import_react3.useState)([]);
1872
2089
  const [optimizationValues, setOptimizationValues] = (0, import_react3.useState)({});
2090
+ const [selectedScheduleDates, setSelectedScheduleDates] = (0, import_react3.useState)([]);
1873
2091
  const [screen, setScreen] = (0, import_react3.useState)("configure");
1874
2092
  const [activeStepIndex, setActiveStepIndex] = (0, import_react3.useState)(0);
1875
2093
  const [completedStepIndex, setCompletedStepIndex] = (0, import_react3.useState)(-1);
@@ -1878,12 +2096,19 @@ function AutoSchedulerModal({
1878
2096
  const timersRef = (0, import_react3.useRef)([]);
1879
2097
  const pollIntervalRef = (0, import_react3.useRef)(null);
1880
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
+ };
1881
2108
  const selectedByJurisdiction = (0, import_react3.useMemo)(
1882
2109
  () => jurisdictions.map((jurisdiction) => ({
1883
2110
  ...jurisdiction,
1884
- selectedLocations: jurisdiction.locations.filter(
1885
- (location) => selectedLocationIds.includes(location.id)
1886
- )
2111
+ selectedLocations: jurisdiction.locations.filter((location) => selectedLocationIds.includes(location.id))
1887
2112
  })).filter((jurisdiction) => jurisdiction.selectedLocations.length > 0),
1888
2113
  [jurisdictions, selectedLocationIds]
1889
2114
  );
@@ -1891,39 +2116,85 @@ function AutoSchedulerModal({
1891
2116
  (total, jurisdiction) => total + jurisdiction.selectedLocations.length,
1892
2117
  0
1893
2118
  );
2119
+ const locationIdsWithOpenShifts = (0, import_react3.useMemo)(() => {
2120
+ return new Set(openShifts.filter((shift) => !shift.employeeId).map((shift) => shift.location.id));
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]);
1894
2141
  const selectedConstraints = (0, import_react3.useMemo)(
1895
- () => HARD_CONSTRAINT_OPTIONS.filter((option) => selectedConstraintIds.includes(option.id)),
1896
- [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]
1897
2150
  );
1898
2151
  const optimizationOptions = (0, import_react3.useMemo)(
1899
- () => OPTIMIZATION_OPTION_GROUPS.flatMap(
2152
+ () => effectiveSoftConstraintGroups.flatMap(
1900
2153
  (group) => group.options.map((option) => ({
1901
2154
  ...option,
1902
2155
  groupId: group.id,
1903
2156
  groupName: group.name
1904
2157
  }))
1905
2158
  ),
1906
- []
2159
+ [effectiveSoftConstraintGroups]
1907
2160
  );
1908
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)(
1909
2166
  () => optimizationOptions.filter(
1910
- (option) => selectedOptimizationIds.includes(option.id)
2167
+ (option) => selectedOptimizationIds.includes(option.id) || option.readOnly && option.defaultSelected
1911
2168
  ),
1912
2169
  [optimizationOptions, selectedOptimizationIds]
1913
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
+ );
1914
2189
  const hasEmptyInputs = (0, import_react3.useMemo)(() => {
1915
- const hasEmptyConstraint = selectedConstraints.some(
1916
- (opt) => {
1917
- var _a;
1918
- return !((_a = constraintValues[opt.id]) == null ? void 0 : _a.trim());
1919
- }
1920
- );
1921
- const hasEmptyWeight = selectedOptimizationOptions.length >= 2 && selectedOptimizationOptions.some(
1922
- (opt) => {
1923
- var _a;
1924
- return !((_a = optimizationValues[`${opt.id}_weightPoints`]) == null ? void 0 : _a.trim());
1925
- }
1926
- );
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
+ });
1927
2198
  return hasEmptyConstraint || hasEmptyWeight;
1928
2199
  }, [selectedConstraints, constraintValues, selectedOptimizationOptions, optimizationValues]);
1929
2200
  const allocationSummary = (0, import_react3.useMemo)(() => {
@@ -1957,13 +2228,25 @@ function AutoSchedulerModal({
1957
2228
  setScreen("configure");
1958
2229
  setLastFailure(null);
1959
2230
  setSelectedLocationIds([]);
2231
+ setSelectedSubLocationIds([]);
2232
+ setSelectedTeamIds([]);
1960
2233
  setSelectedOptimizationIds([]);
1961
2234
  setSelectedConstraintIds([]);
1962
2235
  setConstraintValues({});
1963
2236
  setOptimizationValues({});
2237
+ setSelectedScheduleDates([]);
1964
2238
  resetProgress();
1965
2239
  }
1966
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]);
1967
2250
  (0, import_react3.useEffect)(() => {
1968
2251
  if (isOpen && initialConfig) {
1969
2252
  setSelectedLocationIds(initialConfig.selectedLocationIds);
@@ -1995,6 +2278,13 @@ function AutoSchedulerModal({
1995
2278
  }
1996
2279
  }, [selectedLocationIds, initialConfig, getStoredConfigForLocations]);
1997
2280
  (0, import_react3.useEffect)(() => () => clearTimers(), []);
2281
+ (0, import_react3.useEffect)(() => {
2282
+ if (onGenerate) return;
2283
+ setSelectedLocationIds((previous) => {
2284
+ const filtered = previous.filter((id) => locationIdsWithOpenShifts.has(id));
2285
+ return filtered.length === previous.length ? previous : filtered;
2286
+ });
2287
+ }, [locationIdsWithOpenShifts, onGenerate]);
1998
2288
  const stepStatusForIndex = (index) => {
1999
2289
  if (index <= completedStepIndex) {
2000
2290
  return "done";
@@ -2022,144 +2312,249 @@ function AutoSchedulerModal({
2022
2312
  timersRef.current.push(timerId);
2023
2313
  };
2024
2314
  const afterSuccessSteps = (runId, response) => {
2025
- schedule(STEP_INTERVAL_MS, () => {
2026
- if (runIdRef.current !== runId) return;
2027
- setCompletedStepIndex(2);
2028
- setActiveStepIndex(3);
2029
- }, runId);
2030
- schedule(STEP_INTERVAL_MS * 2, () => {
2031
- if (runIdRef.current !== runId) return;
2032
- setCompletedStepIndex(3);
2033
- setActiveStepIndex(4);
2034
- }, runId);
2035
- schedule(STEP_INTERVAL_MS * 3, async () => {
2036
- if (runIdRef.current !== runId) return;
2037
- try {
2038
- await onSolution(response.solution);
2039
- 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);
2040
2329
  setActiveStepIndex(4);
2041
- } catch (err) {
2042
- console.error(err);
2043
- const defaultErrorMessage = "Failed to create schedule shifts";
2044
- const detailedErrorMessage = err instanceof Error ? err.message || defaultErrorMessage : defaultErrorMessage;
2045
- setLastFailure({
2046
- violatedConstraints: [
2047
- {
2048
- title: "Something went wrong",
2049
- detail: detailedErrorMessage
2050
- }
2051
- ],
2052
- recommendedFixes: []
2053
- });
2054
- setScreen("failed");
2055
- }
2056
- }, runId);
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
+ );
2057
2359
  };
2058
2360
  const handleCreateSchedule = () => {
2059
2361
  if (selectedCount === 0) {
2060
2362
  return;
2061
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
+ }
2062
2438
  const payload = buildSchedulePayload({
2063
2439
  employees,
2064
2440
  openShifts,
2065
2441
  timeOffs,
2066
2442
  selectedLocationIds,
2067
2443
  jurisdictions,
2068
- selectedConstraintIds,
2444
+ selectedConstraintIds: effectiveSelectedConstraintIds,
2069
2445
  constraintValues,
2070
- selectedOptimizationIds,
2071
- optimizationValues
2446
+ selectedOptimizationIds: effectiveSelectedOptimizationIds,
2447
+ optimizationValues,
2448
+ trafficForecast,
2449
+ constraintDefinitionIds,
2450
+ constraintDefinitions,
2451
+ organizationId
2072
2452
  });
2073
2453
  persistConfigForLocations == null ? void 0 : persistConfigForLocations(selectedLocationIds, {
2074
2454
  selectedLocationIds,
2075
- selectedConstraintIds,
2455
+ selectedConstraintIds: effectiveSelectedConstraintIds,
2076
2456
  constraintValues,
2077
- selectedOptimizationIds,
2078
- optimizationValues
2457
+ selectedOptimizationIds: effectiveSelectedOptimizationIds,
2458
+ optimizationValues,
2459
+ selectedBuiltinIds: defaultSelectedBuiltinIds
2079
2460
  });
2080
2461
  const runId = resetBeforeSubmit();
2081
- schedule(STEP_INTERVAL_MS, async () => {
2082
- setCompletedStepIndex(0);
2083
- setActiveStepIndex(1);
2084
- try {
2085
- const token = await getToken(getTokenURLAndHeaders.url, getTokenURLAndHeaders.headers);
2086
- if (!token) {
2087
- throw new Error("Failed to get auto-scheduler token");
2088
- }
2089
- const auth = { authorization: `Bearer ${token}` };
2090
- const { jobId } = await submitSolveJob(baseURL, payload, auth);
2091
- if (runIdRef.current !== runId) {
2092
- return;
2093
- }
2094
- setCompletedStepIndex(1);
2095
- setActiveStepIndex(OPTIMIZING_STEP_INDEX);
2096
- const pollForResult = async () => {
2097
- if (runIdRef.current !== runId) return;
2098
- try {
2099
- const statusRes = await getJobStatus(baseURL, jobId, auth);
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 () => {
2100
2485
  if (runIdRef.current !== runId) return;
2101
- if (statusRes.status === "NOT_SOLVING" || statusRes.status === "TERMINATED_EARLY") {
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;
2102
2507
  if (pollIntervalRef.current) {
2103
2508
  clearInterval(pollIntervalRef.current);
2104
2509
  pollIntervalRef.current = null;
2105
2510
  }
2106
- const response = await getJobResult(baseURL, jobId, auth);
2107
- if (runIdRef.current !== runId) return;
2108
- let responseForHandling = response;
2109
- if (response.explanation.isFeasible) {
2110
- const solution = await getJobSolution(baseURL, jobId, auth);
2111
- if (runIdRef.current !== runId) return;
2112
- responseForHandling = { ...response, solution };
2113
- }
2114
- handleSolveResult(runId, responseForHandling);
2115
- return;
2116
- }
2117
- } catch (err) {
2118
- if (runIdRef.current !== runId) return;
2119
- if (pollIntervalRef.current) {
2120
- clearInterval(pollIntervalRef.current);
2121
- pollIntervalRef.current = null;
2511
+ console.error(err);
2512
+ setLastFailure(NETWORK_ERROR_FAILURE);
2513
+ setScreen("failed");
2122
2514
  }
2123
- console.error(err);
2124
- setLastFailure(NETWORK_ERROR_FAILURE);
2125
- setScreen("failed");
2515
+ };
2516
+ pollForResult();
2517
+ pollIntervalRef.current = setInterval(pollForResult, POLL_INTERVAL_MS);
2518
+ } catch (err) {
2519
+ if (runIdRef.current !== runId) {
2520
+ return;
2126
2521
  }
2127
- };
2128
- pollForResult();
2129
- pollIntervalRef.current = setInterval(pollForResult, POLL_INTERVAL_MS);
2130
- } catch (err) {
2131
- if (runIdRef.current !== runId) {
2132
- return;
2522
+ console.error(err);
2523
+ setLastFailure(NETWORK_ERROR_FAILURE);
2524
+ setScreen("failed");
2133
2525
  }
2134
- console.error(err);
2135
- setLastFailure(NETWORK_ERROR_FAILURE);
2136
- setScreen("failed");
2137
- }
2138
- }, runId);
2526
+ },
2527
+ runId
2528
+ );
2139
2529
  };
2140
2530
  const handleSolveResult = (runId, response) => {
2141
- var _a, _b;
2531
+ var _a;
2142
2532
  if (!response.explanation.isFeasible) {
2143
- const result = mapViolationsToResult(
2144
- (_a = response.explanation.violations) != null ? _a : [],
2145
- response.explanation.summary
2146
- );
2147
- const selectedLocations = jurisdictions.flatMap((j) => j.locations).filter(
2148
- (l) => selectedLocationIds.includes(l.id)
2149
- );
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));
2150
2535
  const recommendationPayload = generateRecommendationsURLAndHeaders ? {
2151
2536
  context: {
2152
2537
  selectedLocationIds,
2153
- selectedConstraintIds,
2538
+ selectedConstraintIds: effectiveSelectedConstraintIds,
2154
2539
  constraintValues,
2155
- selectedOptimizationIds,
2540
+ selectedOptimizationIds: effectiveSelectedOptimizationIds,
2156
2541
  optimizationValues,
2157
- timezone: (_b = selectedLocations[0]) == null ? void 0 : _b.timezone
2542
+ timezone: selectedLocationTimezone
2158
2543
  },
2159
2544
  locations: selectedLocations,
2160
2545
  employees,
2161
- openShifts: openShifts.filter((s) => !s.employeeId),
2162
- assignedShifts: openShifts.filter((s) => Boolean(s.employeeId)),
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
+ ),
2163
2558
  timeOffs: timeOffs != null ? timeOffs : [],
2164
2559
  explanation: response.explanation
2165
2560
  } : void 0;
@@ -2178,14 +2573,34 @@ function AutoSchedulerModal({
2178
2573
  setScreen("configure");
2179
2574
  };
2180
2575
  const handleToggleLocation = (locationId) => {
2181
- if (selectedLocationIds.includes(locationId)) {
2182
- setSelectedLocationIds(selectedLocationIds.filter((id) => id !== locationId));
2576
+ if (!onGenerate && !locationIdsWithOpenShifts.has(locationId)) {
2183
2577
  return;
2184
2578
  }
2185
- setSelectedLocationIds([...selectedLocationIds, locationId]);
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);
2587
+ }
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]);
2186
2600
  };
2187
2601
  const handleToggleConstraint = (constraintId) => {
2188
2602
  setSelectedConstraintIds((prev) => {
2603
+ var _a;
2189
2604
  if (prev.includes(constraintId)) {
2190
2605
  setConstraintValues((values) => {
2191
2606
  const next = { ...values };
@@ -2194,10 +2609,11 @@ function AutoSchedulerModal({
2194
2609
  });
2195
2610
  return prev.filter((id) => id !== constraintId);
2196
2611
  }
2197
- if (HARD_CONSTRAINT_DEFAULTS[constraintId]) {
2612
+ const defaultVal = (_a = effectiveHardConstraintOptions.find((o) => o.id === constraintId)) == null ? void 0 : _a.defaultValue;
2613
+ if (defaultVal) {
2198
2614
  setConstraintValues((values) => ({
2199
2615
  ...values,
2200
- [constraintId]: HARD_CONSTRAINT_DEFAULTS[constraintId]
2616
+ [constraintId]: defaultVal
2201
2617
  }));
2202
2618
  }
2203
2619
  return [...prev, constraintId];
@@ -2241,6 +2657,7 @@ function AutoSchedulerModal({
2241
2657
  violatedConstraints: lastFailure,
2242
2658
  title: violationTitle,
2243
2659
  subtitle: "",
2660
+ errorToast,
2244
2661
  generateRecommendationsURLAndHeaders,
2245
2662
  styles
2246
2663
  }
@@ -2248,12 +2665,12 @@ function AutoSchedulerModal({
2248
2665
  const configurationContent = /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react4.Stack, { spacing: "20px", children: [
2249
2666
  /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react4.Box, { sx: styles.banner, children: [
2250
2667
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Box, { sx: styles.bannerIcon, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(MagicIcon, {}) }),
2251
- bannerText ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Text, { sx: styles.bannerText, children: bannerText }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react4.Stack, { spacing: "4px", flex: 1, children: [
2252
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Text, { sx: styles.bannerTitle, children: "Create a smarter retail schedule in seconds." }),
2253
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Text, { sx: styles.bannerText, children: "AI assigns open shifts using your rules, availability, and optimization priorities while keeping existing assigned shifts in place." })
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 })
2254
2671
  ] })
2255
2672
  ] }),
2256
- openShifts.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
2673
+ !onGenerate && openShifts.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
2257
2674
  import_react4.Box,
2258
2675
  {
2259
2676
  sx: {
@@ -2296,25 +2713,33 @@ function AutoSchedulerModal({
2296
2713
  ] }) }) }),
2297
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: [
2298
2715
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Text, { sx: styles.groupHeader, children: jurisdiction.title }),
2299
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Stack, { spacing: "2px", children: jurisdiction.locations.map((location) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
2300
- import_react4.HStack,
2301
- {
2302
- as: "label",
2303
- sx: { ...styles.optionRow, cursor: "pointer" },
2304
- children: [
2305
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
2306
- import_react4.Checkbox,
2307
- {
2308
- id: `location-${location.id}`,
2309
- isChecked: selectedLocationIds.includes(location.id),
2310
- onChange: () => handleToggleLocation(location.id)
2311
- }
2312
- ),
2313
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Text, { sx: styles.optionLabel, children: location.name })
2314
- ]
2315
- },
2316
- location.id
2317
- )) })
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
+ ]
2739
+ },
2740
+ location.id
2741
+ );
2742
+ }) })
2318
2743
  ] }, jurisdiction.id)) }) }) })
2319
2744
  ] }),
2320
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: [
@@ -2328,11 +2753,128 @@ function AutoSchedulerModal({
2328
2753
  ] })
2329
2754
  ] }, jurisdiction.id)) })
2330
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,
2331
2873
  /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react4.Stack, { spacing: "16px", sx: styles.hardConstraintsSection, children: [
2332
2874
  /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react4.Stack, { spacing: "4px", children: [
2333
2875
  /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react4.HStack, { sx: styles.hardConstraintsLabelRow, children: [
2334
2876
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Text, { sx: styles.hardConstraintsLabel, children: "Required rules" }),
2335
- selectedConstraintIds.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Badge, { sx: styles.hardConstraintsBadge, children: selectedConstraintIds.length }) : null
2877
+ selectedConstraintDisplayOptions.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Badge, { sx: styles.hardConstraintsBadge, children: selectedConstraintDisplayOptions.length }) : null
2336
2878
  ] }),
2337
2879
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Text, { sx: styles.hardConstraintsSubtitle, children: "Assigned shifts will be required to meet the following rules:" })
2338
2880
  ] }),
@@ -2344,35 +2886,30 @@ function AutoSchedulerModal({
2344
2886
  {
2345
2887
  sx: {
2346
2888
  ...styles.hardConstraintsTriggerText,
2347
- ...selectedConstraintIds.length > 0 ? { color: "#303030" } : {}
2889
+ ...selectedConstraintDisplayOptions.length > 0 ? { color: "#303030" } : {}
2348
2890
  },
2349
2891
  noOfLines: 1,
2350
- children: selectedConstraintIds.length > 0 ? HARD_CONSTRAINT_OPTIONS.filter((o) => selectedConstraintIds.includes(o.id)).map((o) => o.name).join(", ") : "Select rules"
2892
+ children: selectedConstraintDisplayOptions.length > 0 ? selectedConstraintDisplayOptions.map((o) => o.name).join(", ") : "Select rules"
2351
2893
  }
2352
2894
  ),
2353
2895
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Box, { color: "#5c5c5c", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ChevronDownIcon, {}) })
2354
2896
  ] }) }) }),
2355
- /* @__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: HARD_CONSTRAINT_OPTIONS.map((option) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
2356
- import_react4.HStack,
2357
- {
2358
- as: "label",
2359
- sx: { ...styles.hardConstraintsOptionRow, cursor: "pointer" },
2360
- children: [
2361
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
2362
- import_react4.Checkbox,
2363
- {
2364
- id: `constraint-${option.id}`,
2365
- isChecked: selectedConstraintIds.includes(option.id),
2366
- onChange: () => handleToggleConstraint(option.id)
2367
- }
2368
- ),
2369
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Text, { sx: styles.hardConstraintsOptionLabel, children: option.name })
2370
- ]
2371
- },
2372
- option.id
2373
- )) }) }) })
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)) }) }) })
2374
2911
  ] }),
2375
- 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) => {
2376
2913
  var _a;
2377
2914
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react4.Stack, { sx: styles.hardConstraintsInputItem, children: [
2378
2915
  /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react4.Text, { sx: styles.hardConstraintsInputLabel, children: [
@@ -2390,13 +2927,13 @@ function AutoSchedulerModal({
2390
2927
  ...prev,
2391
2928
  [option.id]: event.target.value
2392
2929
  })),
2393
- min: option.id === "max_work_days_per_week" ? 1 : void 0,
2394
- max: option.id === "max_work_days_per_week" ? 6 : void 0,
2395
- step: option.id === "max_work_days_per_week" ? void 0 : "any",
2930
+ min: option.min,
2931
+ max: option.max,
2932
+ step: option.max != null ? void 0 : "any",
2396
2933
  sx: styles.hardConstraintsInputField
2397
2934
  }
2398
2935
  ),
2399
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.InputRightAddon, { sx: styles.optimizationSuffix, children: option.id === "max_work_days_per_week" ? "days" : "hours" })
2936
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.InputRightAddon, { sx: styles.optimizationSuffix, children: option.suffix })
2400
2937
  ] })
2401
2938
  ] }, option.id);
2402
2939
  }) }) }) : null
@@ -2405,89 +2942,100 @@ function AutoSchedulerModal({
2405
2942
  /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react4.Stack, { spacing: "4px", children: [
2406
2943
  /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react4.HStack, { sx: styles.optimizationLabelRow, children: [
2407
2944
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Text, { sx: styles.optimizationLabel, children: "Optimization" }),
2408
- selectedOptimizationIds.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Badge, { sx: styles.optimizationBadge, children: selectedOptimizationIds.length }) : null
2945
+ selectedOptimizationDisplayOptions.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Badge, { sx: styles.optimizationBadge, children: selectedOptimizationDisplayOptions.length }) : null
2409
2946
  ] }),
2410
2947
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Text, { sx: styles.optimizationSubtitle, children: "Your schedule will be optimized based on the following priorities:" })
2411
2948
  ] }),
2412
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react4.Popover, { placement: "bottom-start", matchWidth: true, isLazy: true, children: [
2949
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react4.Popover, { placement: "top-start", strategy: "fixed", matchWidth: true, isLazy: true, gutter: 4, children: [
2413
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: [
2414
2951
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
2415
2952
  import_react4.Text,
2416
2953
  {
2417
2954
  sx: {
2418
2955
  ...styles.optimizationTriggerText,
2419
- ...selectedOptimizationIds.length > 0 ? { color: "#303030" } : {}
2956
+ ...selectedOptimizationDisplayOptions.length > 0 ? { color: "#303030" } : {}
2420
2957
  },
2421
2958
  noOfLines: 1,
2422
- children: selectedOptimizationIds.length > 0 ? optimizationOptions.filter((o) => selectedOptimizationIds.includes(o.id)).map((o) => o.title).join(", ") : "Select optimization priorities"
2959
+ children: selectedOptimizationDisplayOptions.length > 0 ? selectedOptimizationDisplayOptions.map((o) => o.title).join(", ") : "Select optimization priorities"
2423
2960
  }
2424
2961
  ),
2425
2962
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Box, { color: "#5c5c5c", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ChevronDownIcon, {}) })
2426
2963
  ] }) }) }),
2427
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.PopoverContent, { sx: styles.optimizationPopoverContent, 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: OPTIMIZATION_OPTION_GROUPS.map((group) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react4.Box, { children: [
2428
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Text, { sx: styles.optimizationGroupHeader, children: group.name }),
2429
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Stack, { spacing: "2px", children: group.options.map((option) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
2430
- import_react4.Stack,
2431
- {
2432
- as: "label",
2433
- spacing: "2px",
2434
- sx: { ...styles.optimizationOptionRow, cursor: "pointer" },
2435
- children: [
2436
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react4.HStack, { spacing: "8px", children: [
2437
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
2438
- import_react4.Checkbox,
2439
- {
2440
- id: `optimization-${option.id}`,
2441
- isChecked: selectedOptimizationIds.includes(option.id),
2442
- isDisabled: !selectedOptimizationIds.includes(option.id) && selectedOptimizationIds.length >= 3,
2443
- onChange: () => handleToggleOptimization(option.id)
2444
- }
2445
- ),
2446
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Text, { sx: styles.optimizationOptionLabel, children: option.title })
2447
- ] }),
2448
- option.subtitle ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Text, { sx: styles.optimizationOptionHelp, children: option.subtitle }) : null
2449
- ]
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" }
2450
2975
  },
2451
- option.id
2452
- )) })
2453
- ] }, group.id)) }) }) })
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
+ ) })
2454
3009
  ] }),
2455
3010
  selectedOptimizationOptions.length >= 2 ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react4.Box, { sx: styles.optimizationInputsContainer, children: [
2456
3011
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Stack, { spacing: "16px", children: selectedOptimizationOptions.map((option, index) => {
2457
3012
  var _a;
2458
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
2459
- import_react4.Stack,
2460
- {
2461
- sx: styles.optimizationInputItem,
2462
- children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react4.Stack, { sx: styles.hardConstraintsInputItem, children: [
2463
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react4.Text, { sx: styles.hardConstraintsInputLabel, children: [
2464
- index + 1,
2465
- ". ",
2466
- option.title
2467
- ] }),
2468
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react4.InputGroup, { children: [
2469
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
2470
- import_react4.Input,
2471
- {
2472
- type: "text",
2473
- value: (_a = optimizationValues[`${option.id}_weightPoints`]) != null ? _a : "1",
2474
- onChange: (e) => {
2475
- const val = e.target.value;
2476
- if (val !== "" && (!/^\d+$/.test(val) || Number(val) < 1)) return;
2477
- setOptimizationValues((prev) => ({
2478
- ...prev,
2479
- [`${option.id}_weightPoints`]: val
2480
- }));
2481
- },
2482
- sx: styles.hardConstraintsInputField
2483
- }
2484
- ),
2485
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.InputRightAddon, { sx: styles.optimizationSuffix, children: "points" })
2486
- ] })
2487
- ] })
2488
- },
2489
- option.id
2490
- );
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);
2491
3039
  }) }),
2492
3040
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Divider, { sx: { ...styles.optimizationDivider, my: "16px" } }),
2493
3041
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Text, { sx: styles.optimizationSummary, children: allocationSummary })
@@ -2496,7 +3044,9 @@ function AutoSchedulerModal({
2496
3044
  ] });
2497
3045
  const stepsContent = /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react4.Stack, { spacing: "16px", sx: styles.stepsContainer, children: [
2498
3046
  showFailed ? violationPanel : null,
2499
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Stack, { spacing: "14px", sx: styles.stepsList, children: SCHEDULE_STEPS.map((step, index) => {
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;
2500
3050
  const status = stepStatusForIndex(index);
2501
3051
  const isFailedStep = showFailed && index === activeStepIndex;
2502
3052
  const iconStyles = {
@@ -2514,26 +3064,26 @@ function AutoSchedulerModal({
2514
3064
  ...isFailedStep ? styles.stepTitleFailed : null
2515
3065
  };
2516
3066
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react4.HStack, { sx: styles.stepRow, children: [
2517
- /* @__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: 12, height: 12 }) : null }),
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 }),
2518
3068
  /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react4.Box, { children: [
2519
3069
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Text, { sx: titleStyles, children: step.title }),
2520
3070
  status === "active" && step.subtitle ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Text, { sx: styles.stepSubtitle, children: step.subtitle }) : null
2521
3071
  ] })
2522
- ] }, step.title);
3072
+ ] }, rawStep.title);
2523
3073
  }) })
2524
3074
  ] });
2525
- const modalFooter = showConfiguration ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react4.ModalFooter, { sx: styles.footer, children: [
3075
+ const footerButtons = showConfiguration ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
2526
3076
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Button, { variant: "outline", onClick: onClose, sx: styles.cancelButton, children: cancelLabel }),
2527
3077
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
2528
3078
  import_react4.Button,
2529
3079
  {
2530
3080
  onClick: handleCreateSchedule,
2531
- isDisabled: selectedCount === 0 || openShifts.length === 0 || hasEmptyInputs,
3081
+ isDisabled: selectedCount === 0 || !onGenerate && openShifts.length === 0 || hasEmptyInputs || Boolean(scheduleWeekStartYmd) && selectedScheduleDates.length === 0,
2532
3082
  sx: styles.primaryButton,
2533
3083
  children: primaryActionLabel
2534
3084
  }
2535
3085
  )
2536
- ] }) : showFailed ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react4.ModalFooter, { sx: styles.failedFooter, children: [
3086
+ ] }) : showFailed ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
2537
3087
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
2538
3088
  import_react4.Button,
2539
3089
  {
@@ -2542,10 +3092,11 @@ function AutoSchedulerModal({
2542
3092
  if (lastFailure) {
2543
3093
  onFixManually == null ? void 0 : onFixManually(lastFailure, {
2544
3094
  selectedLocationIds,
2545
- selectedConstraintIds,
3095
+ selectedConstraintIds: effectiveSelectedConstraintIds,
2546
3096
  constraintValues,
2547
- selectedOptimizationIds,
2548
- optimizationValues
3097
+ selectedOptimizationIds: effectiveSelectedOptimizationIds,
3098
+ optimizationValues,
3099
+ selectedBuiltinIds: defaultSelectedBuiltinIds
2549
3100
  });
2550
3101
  }
2551
3102
  onClose();
@@ -2556,31 +3107,75 @@ function AutoSchedulerModal({
2556
3107
  ),
2557
3108
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Button, { onClick: handleAdjustConstraints, sx: styles.failedPrimaryButton, children: "Adjust constraints" })
2558
3109
  ] }) : null;
2559
- const modalContent = /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react4.Modal, { isOpen, onClose, isCentered: true, motionPreset: "scale", closeOnOverlayClick: false, closeOnEsc: false, children: [
2560
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.ModalOverlay, { sx: styles.overlay }),
2561
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react4.ModalContent, { sx: styles.content, children: [
2562
- /* @__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: [
2563
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Text, { sx: styles.title, children: modalTitle }),
2564
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
2565
- import_react4.IconButton,
2566
- {
2567
- "aria-label": "Close modal",
2568
- variant: "ghost",
2569
- size: "sm",
2570
- icon: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(CloseIcon, {}),
2571
- onClick: onClose,
2572
- sx: styles.closeButton
2573
- }
2574
- )
2575
- ] }) }),
2576
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.ModalBody, { sx: showConfiguration ? styles.body : { ...styles.body, ...styles.creatingBody }, children: showConfiguration ? configurationContent : stepsContent }),
2577
- modalFooter
2578
- ] })
2579
- ] });
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;
2580
3175
  if (theme || cssVarsRoot) {
2581
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.ChakraProvider, { theme: mergedTheme, cssVarsRoot, children: modalContent });
3176
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.ChakraProvider, { theme: mergedTheme, cssVarsRoot, children: content });
2582
3177
  }
2583
- return modalContent;
3178
+ return content;
2584
3179
  }
2585
3180
 
2586
3181
  // src/AutoSchedulerModalWithProvider.tsx
@@ -2657,11 +3252,17 @@ var RequestError = class extends Error {
2657
3252
  0 && (module.exports = {
2658
3253
  AutoSchedulerModal,
2659
3254
  AutoSchedulerModalWithProvider,
3255
+ InfeasibleScheduleError,
3256
+ MAXIMIZE_CONVERSION_RATE_RULE_ID,
2660
3257
  RequestError,
3258
+ TRAFFIC_FORECAST_INCENTIVE_RULE_ID,
2661
3259
  ViolatedConstraintsPanel,
2662
3260
  ViolatedConstraintsPanelWithProvider,
2663
3261
  buildRulesPayload,
2664
3262
  buildSchedulePayload,
2665
- getFriendlyRuleName
3263
+ defaultSelectedScheduleDates,
3264
+ getFriendlyRuleName,
3265
+ getSequentialWeekDatesFromWeekStart,
3266
+ weekdayTwoLetterFromYmd
2666
3267
  });
2667
3268
  //# sourceMappingURL=index.cjs.map