@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 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
  }
@@ -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);
@@ -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
- 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.",
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
- () => HARD_CONSTRAINT_OPTIONS.filter((option) => selectedConstraintIds.includes(option.id)),
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
- () => OPTIMIZATION_OPTION_GROUPS.flatMap(
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
- (opt) => {
1937
- var _a;
1938
- return !((_a = constraintValues[opt.id]) == null ? void 0 : _a.trim());
1939
- }
1940
- );
1941
- const hasEmptyWeight = selectedOptimizationOptions.length >= 2 && selectedOptimizationOptions.some(
1942
- (opt) => {
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(STEP_INTERVAL_MS, () => {
2052
- if (runIdRef.current !== runId) return;
2053
- setCompletedStepIndex(2);
2054
- setActiveStepIndex(3);
2055
- }, runId);
2056
- schedule(STEP_INTERVAL_MS * 2, () => {
2057
- if (runIdRef.current !== runId) return;
2058
- setCompletedStepIndex(3);
2059
- setActiveStepIndex(4);
2060
- }, runId);
2061
- schedule(STEP_INTERVAL_MS * 3, async () => {
2062
- if (runIdRef.current !== runId) return;
2063
- try {
2064
- await onSolution(response.solution);
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
- } catch (err) {
2068
- console.error(err);
2069
- const defaultErrorMessage = "Failed to create schedule shifts";
2070
- const detailedErrorMessage = err instanceof Error ? err.message || defaultErrorMessage : defaultErrorMessage;
2071
- setLastFailure({
2072
- violatedConstraints: [
2073
- {
2074
- title: "Something went wrong",
2075
- detail: detailedErrorMessage
2076
- }
2077
- ],
2078
- recommendedFixes: []
2079
- });
2080
- setScreen("failed");
2081
- }
2082
- }, 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
+ );
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(STEP_INTERVAL_MS, async () => {
2108
- setCompletedStepIndex(0);
2109
- setActiveStepIndex(1);
2110
- try {
2111
- const token = await getToken(getTokenURLAndHeaders.url, getTokenURLAndHeaders.headers);
2112
- if (!token) {
2113
- throw new Error("Failed to get auto-scheduler token");
2114
- }
2115
- const auth = { authorization: `Bearer ${token}` };
2116
- const { jobId } = await submitSolveJob(baseURL, payload, auth);
2117
- if (runIdRef.current !== runId) {
2118
- return;
2119
- }
2120
- setCompletedStepIndex(1);
2121
- setActiveStepIndex(OPTIMIZING_STEP_INDEX);
2122
- const pollForResult = async () => {
2123
- if (runIdRef.current !== runId) return;
2124
- try {
2125
- 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 () => {
2126
2485
  if (runIdRef.current !== runId) return;
2127
- 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;
2128
2507
  if (pollIntervalRef.current) {
2129
2508
  clearInterval(pollIntervalRef.current);
2130
2509
  pollIntervalRef.current = null;
2131
2510
  }
2132
- const response = await getJobResult(baseURL, jobId, auth);
2133
- if (runIdRef.current !== runId) return;
2134
- let responseForHandling = response;
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
- console.error(err);
2150
- setLastFailure(NETWORK_ERROR_FAILURE);
2151
- setScreen("failed");
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
- pollForResult();
2155
- pollIntervalRef.current = setInterval(pollForResult, POLL_INTERVAL_MS);
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
- console.error(err);
2161
- setLastFailure(NETWORK_ERROR_FAILURE);
2162
- setScreen("failed");
2163
- }
2164
- }, runId);
2526
+ },
2527
+ runId
2528
+ );
2165
2529
  };
2166
2530
  const handleSolveResult = (runId, response) => {
2167
- var _a, _b;
2531
+ var _a;
2168
2532
  if (!response.explanation.isFeasible) {
2169
- const result = mapViolationsToResult(
2170
- (_a = response.explanation.violations) != null ? _a : [],
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: (_b = selectedLocations[0]) == null ? void 0 : _b.timezone
2542
+ timezone: selectedLocationTimezone
2184
2543
  },
2185
2544
  locations: selectedLocations,
2186
2545
  employees,
2187
- openShifts: openShifts.filter((s) => !s.employeeId),
2188
- 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
+ ),
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
- if (selectedLocationIds.includes(locationId)) {
2211
- setSelectedLocationIds(selectedLocationIds.filter((id) => id !== locationId));
2212
- return;
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
- setSelectedLocationIds([...selectedLocationIds, locationId]);
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
- if (HARD_CONSTRAINT_DEFAULTS[constraintId]) {
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]: HARD_CONSTRAINT_DEFAULTS[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
- 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: [
2282
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Text, { sx: styles.bannerTitle, children: "Create a smarter retail schedule in seconds." }),
2283
- /* @__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 })
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) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
2330
- import_react4.HStack,
2331
- {
2332
- as: "label",
2333
- sx: {
2334
- ...styles.optionRow,
2335
- cursor: locationIdsWithOpenShifts.has(location.id) ? "pointer" : "not-allowed",
2336
- opacity: locationIdsWithOpenShifts.has(location.id) ? 1 : 0.55
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
- children: [
2339
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
2340
- import_react4.Checkbox,
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
- 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
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
- ...selectedConstraintIds.length > 0 ? { color: "#303030" } : {}
2889
+ ...selectedConstraintDisplayOptions.length > 0 ? { color: "#303030" } : {}
2383
2890
  },
2384
2891
  noOfLines: 1,
2385
- 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"
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: HARD_CONSTRAINT_OPTIONS.map((option) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
2391
- import_react4.HStack,
2392
- {
2393
- as: "label",
2394
- sx: { ...styles.hardConstraintsOptionRow, cursor: "pointer" },
2395
- children: [
2396
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
2397
- import_react4.Checkbox,
2398
- {
2399
- id: `constraint-${option.id}`,
2400
- isChecked: selectedConstraintIds.includes(option.id),
2401
- onChange: () => handleToggleConstraint(option.id)
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.id === "max_work_days_per_week" ? 1 : void 0,
2429
- max: option.id === "max_work_days_per_week" ? 6 : void 0,
2430
- 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",
2431
2933
  sx: styles.hardConstraintsInputField
2432
2934
  }
2433
2935
  ),
2434
- /* @__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 })
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
- 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
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: "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: [
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
- ...selectedOptimizationIds.length > 0 ? { color: "#303030" } : {}
2956
+ ...selectedOptimizationDisplayOptions.length > 0 ? { color: "#303030" } : {}
2455
2957
  },
2456
2958
  noOfLines: 1,
2457
- 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"
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.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: [
2463
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Text, { sx: styles.optimizationGroupHeader, children: group.name }),
2464
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Stack, { spacing: "2px", children: group.options.map((option) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
2465
- import_react4.Stack,
2466
- {
2467
- as: "label",
2468
- spacing: "2px",
2469
- sx: { ...styles.optimizationOptionRow, cursor: "pointer" },
2470
- children: [
2471
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react4.HStack, { spacing: "8px", children: [
2472
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
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
- option.id
2487
- )) })
2488
- ] }, 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
+ ) })
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.Stack,
2495
- {
2496
- sx: styles.optimizationInputItem,
2497
- children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react4.Stack, { sx: styles.hardConstraintsInputItem, children: [
2498
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react4.Text, { sx: styles.hardConstraintsInputLabel, children: [
2499
- index + 1,
2500
- ". ",
2501
- option.title
2502
- ] }),
2503
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react4.InputGroup, { children: [
2504
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
2505
- import_react4.Input,
2506
- {
2507
- type: "text",
2508
- value: (_a = optimizationValues[`${option.id}_weightPoints`]) != null ? _a : "1",
2509
- onChange: (e) => {
2510
- const val = e.target.value;
2511
- if (val !== "" && (!/^\d+$/.test(val) || Number(val) < 1)) return;
2512
- setOptimizationValues((prev) => ({
2513
- ...prev,
2514
- [`${option.id}_weightPoints`]: val
2515
- }));
2516
- },
2517
- sx: styles.hardConstraintsInputField
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((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;
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: 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 }),
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
- ] }, step.title);
3072
+ ] }, rawStep.title);
2558
3073
  }) })
2559
3074
  ] });
2560
- 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: [
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)(import_react4.ModalFooter, { sx: styles.failedFooter, children: [
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 modalContent = /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react4.Modal, { isOpen, onClose, isCentered: true, motionPreset: "scale", closeOnOverlayClick: false, closeOnEsc: false, children: [
2595
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.ModalOverlay, { sx: styles.overlay }),
2596
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_react4.ModalContent, { sx: styles.content, children: [
2597
- /* @__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: [
2598
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.Text, { sx: styles.title, children: modalTitle }),
2599
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
2600
- import_react4.IconButton,
2601
- {
2602
- "aria-label": "Close modal",
2603
- variant: "ghost",
2604
- size: "sm",
2605
- icon: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(CloseIcon, {}),
2606
- onClick: onClose,
2607
- sx: styles.closeButton
2608
- }
2609
- )
2610
- ] }) }),
2611
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.ModalBody, { sx: showConfiguration ? styles.body : { ...styles.body, ...styles.creatingBody }, children: showConfiguration ? configurationContent : stepsContent }),
2612
- modalFooter
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: modalContent });
3176
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react4.ChakraProvider, { theme: mergedTheme, cssVarsRoot, children: content });
2617
3177
  }
2618
- return modalContent;
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
- getFriendlyRuleName
3263
+ defaultSelectedScheduleDates,
3264
+ getFriendlyRuleName,
3265
+ getSequentialWeekDatesFromWeekStart,
3266
+ weekdayTwoLetterFromYmd
2701
3267
  });
2702
3268
  //# sourceMappingURL=index.cjs.map