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

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
@@ -1251,7 +1251,7 @@ var buildStyles = (defaultStyles2, overrides) => ({
1251
1251
  }
1252
1252
  });
1253
1253
 
1254
- // src/buildSchedulePayload.ts
1254
+ // src/utils/ruleIdMapping.ts
1255
1255
  var UI_TO_BACKEND_RULE_ID = {
1256
1256
  max_weekly_hours: "max-weekly-hours",
1257
1257
  max_daily_hours: "max-daily-hours",
@@ -1263,15 +1263,17 @@ var UI_TO_BACKEND_RULE_ID = {
1263
1263
  maximize_sales: "maximize-sales-performance",
1264
1264
  staff_preferences: "staff-open-shift-preferences"
1265
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"]);
1269
1266
  var BACKEND_TO_UI_RULE_ID = Object.fromEntries(
1270
1267
  Object.entries(UI_TO_BACKEND_RULE_ID).map(([ui, backend]) => [backend, ui])
1271
1268
  );
1272
1269
  function toBackendRuleId(uiOptionId) {
1273
1270
  return UI_TO_BACKEND_RULE_ID[uiOptionId];
1274
1271
  }
1272
+
1273
+ // src/buildSchedulePayload.ts
1274
+ var MAXIMIZE_CONVERSION_RATE_RULE_ID = "maximize-conversion-rate";
1275
+ var TRAFFIC_FORECAST_INCENTIVE_RULE_ID = "traffic-forecast-incentive";
1276
+ var SALES_OPTIMIZATION_UI_IDS = /* @__PURE__ */ new Set(["maximize_sales", "cost_to_sales_ratio"]);
1275
1277
  function buildParametersForRule(backendRuleId, constraintValues, _optimizationValues) {
1276
1278
  var _a, _b, _c, _d;
1277
1279
  const params = {};
@@ -1585,6 +1587,22 @@ function withHorizonQuery(url, options) {
1585
1587
  }
1586
1588
 
1587
1589
  // src/utils/violationMessages.ts
1590
+ var INTERNAL_VIOLATION_PATTERNS = [
1591
+ /\w+Rule\{/,
1592
+ /\[I@/,
1593
+ /Employee\{id=/,
1594
+ /Accumulator violation \(Rule:/,
1595
+ /^Schedule has \d+ hard, \d+ medium, and \d+ soft constraint violation/,
1596
+ /ConstraintMatch\{/,
1597
+ /MatchAnalysis\{/,
1598
+ /Indictment\{/,
1599
+ /Hard constraint violation \(count:/
1600
+ ];
1601
+ function isInternalViolationText(text) {
1602
+ const trimmed = text == null ? void 0 : text.trim();
1603
+ if (!trimmed) return false;
1604
+ return INTERNAL_VIOLATION_PATTERNS.some((pattern) => pattern.test(trimmed));
1605
+ }
1588
1606
  var SYSTEM_CONSTRAINT_MESSAGES = {
1589
1607
  "system-unassigned-Hard": {
1590
1608
  title: "Not enough eligible staff to cover all shifts",
@@ -1604,15 +1622,19 @@ var SYSTEM_CONSTRAINT_MESSAGES = {
1604
1622
  },
1605
1623
  "system-employee-unavailable-time-Hard": {
1606
1624
  title: "Unavailable time",
1607
- detail: "Unavailable time"
1625
+ detail: "Some open shifts fall outside the available time windows for the remaining eligible team members."
1608
1626
  },
1609
1627
  "system-employee-available-time-Hard": {
1610
1628
  title: "Outside available time",
1611
- detail: "Outside available time"
1629
+ detail: "Some open shifts fall outside the available time windows for the remaining eligible team members."
1612
1630
  },
1613
1631
  "system-employee-time-off-Hard": {
1614
1632
  title: "Time off prevents assignment",
1615
1633
  detail: "Some open shifts overlap with approved time off for the remaining eligible team members."
1634
+ },
1635
+ "system-no-overlapping-shifts-Hard": {
1636
+ title: "Overlapping shifts",
1637
+ detail: "Some team members would be assigned to overlapping shifts if all open shifts are assigned."
1616
1638
  }
1617
1639
  };
1618
1640
  var RULE_ID_MESSAGES = {
@@ -1628,108 +1650,251 @@ var RULE_ID_MESSAGES = {
1628
1650
  title: "Minimum rest between shifts",
1629
1651
  detail: "Some team members would not receive the required rest time if all open shifts are assigned."
1630
1652
  },
1631
- "min-days-off-per-week": {
1653
+ "max-work-days-per-week": {
1632
1654
  title: "Maximum work days per week exceeded",
1633
1655
  detail: "Some team members would exceed the allowed work days this week if all open shifts are assigned."
1634
1656
  },
1657
+ "min-days-off-per-week": {
1658
+ title: "Minimum days off not met",
1659
+ detail: "Some team members would not receive the required days off this week if all open shifts are assigned."
1660
+ },
1635
1661
  "fair-hour-distribution": {
1636
1662
  title: "Work distribution not balanced",
1637
- detail: "Work distribution not balanced"
1663
+ detail: "Some team members would receive an uneven share of shifts if all open shifts are assigned."
1638
1664
  },
1639
1665
  "overtime-balance": {
1640
1666
  title: "Overtime balance rule violated",
1641
- detail: "Overtime balance rule violated"
1667
+ detail: "Some team members would exceed overtime limits if all open shifts are assigned."
1668
+ },
1669
+ "labor-cost-optimization": {
1670
+ title: "Labor cost optimization conflict",
1671
+ detail: "Some open shifts cannot be assigned while meeting labor cost optimization priorities."
1672
+ },
1673
+ "maximize-sales-performance": {
1674
+ title: "Sales optimization conflict",
1675
+ detail: "Some open shifts cannot be assigned while meeting sales optimization priorities."
1676
+ },
1677
+ "staff-open-shift-preferences": {
1678
+ title: "Staff preferences conflict",
1679
+ detail: "Some open shifts cannot be assigned while respecting all staff shift preferences."
1680
+ },
1681
+ "split-shift-penalty": {
1682
+ title: "Split shift rule violated",
1683
+ detail: "Some team members would receive split shifts that violate scheduling preferences."
1684
+ },
1685
+ "shift-continuity-penalty": {
1686
+ title: "Shift continuity rule violated",
1687
+ detail: "Some team members would receive shift patterns that violate continuity preferences."
1642
1688
  },
1643
1689
  "manager-cashier-ratio": {
1644
1690
  title: "Staffing ratio violated",
1645
- detail: "Staffing ratio violated"
1691
+ detail: "Some open shifts cannot be assigned while meeting required staffing ratios."
1646
1692
  },
1647
1693
  "min-staffing": {
1648
1694
  title: "Minimum staffing not met",
1649
- detail: "Minimum staffing not met"
1695
+ detail: "Some time periods would not meet minimum staffing requirements if all open shifts are assigned."
1650
1696
  },
1651
1697
  "shift-duration-limits": {
1652
1698
  title: "Shift duration outside allowed range",
1653
- detail: "Shift duration outside allowed range"
1699
+ detail: "Some open shifts fall outside the allowed shift duration range."
1654
1700
  },
1655
1701
  "max-consecutive-days": {
1656
1702
  title: "Shift sequence rule violated",
1657
- detail: "Shift sequence rule violated"
1703
+ detail: "Some team members would violate consecutive shift rules if all open shifts are assigned."
1658
1704
  },
1659
1705
  "some-transactional-rule": {
1660
1706
  title: "Policy rule violated",
1661
- detail: "Policy rule violated"
1707
+ detail: "Some open shifts cannot be assigned while meeting all policy requirements."
1708
+ }
1709
+ };
1710
+ var CONSTRAINT_NAME_MESSAGES = {
1711
+ "AccumulatorDays-Hard": {
1712
+ title: "Maximum work days per week exceeded",
1713
+ detail: "Some team members would exceed the allowed work days this week if all open shifts are assigned."
1714
+ },
1715
+ "Accumulator-Hard": {
1716
+ title: "Hours limit exceeded",
1717
+ detail: "Some team members would exceed hour limits if all open shifts are assigned."
1718
+ },
1719
+ "Spacer-Hard": {
1720
+ title: "Rest period not met",
1721
+ detail: "Some team members would not receive the required rest time if all open shifts are assigned."
1722
+ },
1723
+ "SpacerPeriodic-Hard": {
1724
+ title: "Required rest period not met",
1725
+ detail: "Some team members would not receive the required rest period if all open shifts are assigned."
1726
+ },
1727
+ "Composition-Hard": {
1728
+ title: "Staffing ratio violated",
1729
+ detail: "Some open shifts cannot be assigned while meeting required staffing ratios."
1730
+ },
1731
+ "Transactional-Hard": {
1732
+ title: "Policy rule violated",
1733
+ detail: "Some open shifts cannot be assigned while meeting all policy requirements."
1734
+ },
1735
+ "Balancer-Hard": {
1736
+ title: "Work distribution not balanced",
1737
+ detail: "Some team members would receive an uneven share of shifts if all open shifts are assigned."
1738
+ },
1739
+ "Coverage-Hard": {
1740
+ title: "Minimum staffing not met",
1741
+ detail: "Some time periods would not meet minimum staffing requirements if all open shifts are assigned."
1742
+ },
1743
+ "Sizing-Hard": {
1744
+ title: "Shift duration outside allowed range",
1745
+ detail: "Some open shifts fall outside the allowed shift duration range."
1746
+ },
1747
+ "Sequencer-Hard": {
1748
+ title: "Shift sequence rule violated",
1749
+ detail: "Some team members would violate consecutive shift rules if all open shifts are assigned."
1750
+ },
1751
+ "OptimizerMinimize-Hard": {
1752
+ title: "Optimization rule violated",
1753
+ detail: "Some open shifts cannot be assigned while meeting all optimization priorities."
1754
+ },
1755
+ "OptimizerMaximize-Hard": {
1756
+ title: "Optimization rule violated",
1757
+ detail: "Some open shifts cannot be assigned while meeting all optimization priorities."
1662
1758
  }
1663
1759
  };
1664
1760
  var ARCHETYPE_FALLBACK_MESSAGES = {
1665
- "Accumulator violation (HARD)": {
1761
+ AccumulatorDays: {
1762
+ title: "Maximum work days per week exceeded",
1763
+ detail: "Some team members would exceed the allowed work days this week if all open shifts are assigned."
1764
+ },
1765
+ Accumulator: {
1666
1766
  title: "Hours limit exceeded",
1667
- detail: "Hours limit exceeded"
1767
+ detail: "Some team members would exceed hour limits if all open shifts are assigned."
1668
1768
  },
1669
- "Spacer violation (HARD)": {
1769
+ Spacer: {
1670
1770
  title: "Rest period not met",
1671
- detail: "Rest period not met"
1771
+ detail: "Some team members would not receive the required rest time if all open shifts are assigned."
1672
1772
  },
1673
- "Spacer periodic violation (HARD)": {
1773
+ SpacerPeriodic: {
1674
1774
  title: "Required rest period not met",
1675
- detail: "Required rest period not met"
1775
+ detail: "Some team members would not receive the required rest period if all open shifts are assigned."
1676
1776
  },
1677
- "Composition violation (HARD)": {
1777
+ Composition: {
1678
1778
  title: "Staffing ratio violated",
1679
- detail: "Staffing ratio violated"
1779
+ detail: "Some open shifts cannot be assigned while meeting required staffing ratios."
1680
1780
  },
1681
- "Transactional violation (HARD)": {
1781
+ Transactional: {
1682
1782
  title: "Policy rule violated",
1683
- detail: "Policy rule violated"
1783
+ detail: "Some open shifts cannot be assigned while meeting all policy requirements."
1684
1784
  },
1685
- "Balancer violation (HARD)": {
1785
+ Balancer: {
1686
1786
  title: "Work distribution not balanced",
1687
- detail: "Work distribution not balanced"
1787
+ detail: "Some team members would receive an uneven share of shifts if all open shifts are assigned."
1688
1788
  },
1689
- "Coverage violation (HARD)": {
1789
+ Coverage: {
1690
1790
  title: "Minimum staffing not met",
1691
- detail: "Minimum staffing not met"
1791
+ detail: "Some time periods would not meet minimum staffing requirements if all open shifts are assigned."
1692
1792
  },
1693
- "Sizing violation (HARD)": {
1793
+ Sizing: {
1694
1794
  title: "Shift duration outside allowed range",
1695
- detail: "Shift duration outside allowed range"
1795
+ detail: "Some open shifts fall outside the allowed shift duration range."
1696
1796
  },
1697
- "Sequencer violation (HARD)": {
1797
+ Sequencer: {
1698
1798
  title: "Shift sequence rule violated",
1699
- detail: "Shift sequence rule violated"
1799
+ detail: "Some team members would violate consecutive shift rules if all open shifts are assigned."
1700
1800
  },
1701
- "Overtime balance violation (HARD)": {
1702
- title: "Overtime balance rule violated",
1703
- detail: "Overtime balance rule violated"
1801
+ OptimizerMinimize: {
1802
+ title: "Optimization rule violated",
1803
+ detail: "Some open shifts cannot be assigned while meeting all optimization priorities."
1804
+ },
1805
+ OptimizerMaximize: {
1806
+ title: "Optimization rule violated",
1807
+ detail: "Some open shifts cannot be assigned while meeting all optimization priorities."
1704
1808
  }
1705
1809
  };
1706
1810
  var FALLBACK_MESSAGE = {
1707
1811
  title: "Rule violated",
1708
- detail: "Rule violated"
1812
+ detail: "Please adjust constraints and try again."
1709
1813
  };
1710
- function getSystemConstraintMessage(constraintName, description) {
1711
- if (constraintName && constraintName in SYSTEM_CONSTRAINT_MESSAGES) {
1712
- return SYSTEM_CONSTRAINT_MESSAGES[constraintName];
1814
+ function parseBackendConstraintName(constraintName) {
1815
+ if (!(constraintName == null ? void 0 : constraintName.trim())) return {};
1816
+ let name = constraintName.trim();
1817
+ for (const suffix of ["Hard", "Medium", "Soft"]) {
1818
+ if (name.endsWith(`-${suffix}`)) {
1819
+ name = name.slice(0, -(suffix.length + 1));
1820
+ break;
1821
+ }
1822
+ }
1823
+ if (name.startsWith("system-")) {
1824
+ return {};
1825
+ }
1826
+ const lastDash = name.lastIndexOf("-");
1827
+ if (lastDash > 0) {
1828
+ return {
1829
+ ruleId: name.slice(0, lastDash),
1830
+ archetype: name.slice(lastDash + 1)
1831
+ };
1713
1832
  }
1714
- if (description == null ? void 0 : description.trim()) {
1715
- return { title: description, detail: description };
1833
+ return { archetype: name };
1834
+ }
1835
+ function resolveRuleIdMessage(ruleId) {
1836
+ if (!(ruleId == null ? void 0 : ruleId.trim())) return void 0;
1837
+ return RULE_ID_MESSAGES[ruleId.trim()];
1838
+ }
1839
+ function stripSeveritySuffix(name) {
1840
+ return name.replace(/-(Hard|Medium|Soft)$/, "");
1841
+ }
1842
+ function resolveConstraintNameMessage(constraintName) {
1843
+ if (!(constraintName == null ? void 0 : constraintName.trim())) return void 0;
1844
+ const trimmed = constraintName.trim();
1845
+ const exact = CONSTRAINT_NAME_MESSAGES[trimmed];
1846
+ if (exact) return exact;
1847
+ const severityStripped = stripSeveritySuffix(trimmed);
1848
+ const directRuleId = resolveRuleIdMessage(severityStripped);
1849
+ if (directRuleId) return directRuleId;
1850
+ const parsed = parseBackendConstraintName(trimmed);
1851
+ if (parsed.ruleId) {
1852
+ const fromEmbeddedRuleId = resolveRuleIdMessage(parsed.ruleId);
1853
+ if (fromEmbeddedRuleId) return fromEmbeddedRuleId;
1716
1854
  }
1717
- return FALLBACK_MESSAGE;
1855
+ if (parsed.archetype && parsed.archetype in ARCHETYPE_FALLBACK_MESSAGES) {
1856
+ return ARCHETYPE_FALLBACK_MESSAGES[parsed.archetype];
1857
+ }
1858
+ return void 0;
1718
1859
  }
1719
- function getRuleBasedViolationMessage(ruleId, constraintName, description) {
1720
- if (ruleId && ruleId in RULE_ID_MESSAGES) {
1721
- return RULE_ID_MESSAGES[ruleId];
1860
+ function safeDescriptionMessage(description) {
1861
+ if (!(description == null ? void 0 : description.trim()) || isInternalViolationText(description)) {
1862
+ return void 0;
1722
1863
  }
1723
- if (constraintName && constraintName in ARCHETYPE_FALLBACK_MESSAGES) {
1724
- return ARCHETYPE_FALLBACK_MESSAGES[constraintName];
1864
+ return { title: description.trim(), detail: description.trim() };
1865
+ }
1866
+ function getSystemConstraintMessage(constraintName, description) {
1867
+ var _a;
1868
+ if (constraintName && constraintName in SYSTEM_CONSTRAINT_MESSAGES) {
1869
+ return SYSTEM_CONSTRAINT_MESSAGES[constraintName];
1725
1870
  }
1726
- if (description == null ? void 0 : description.trim()) {
1727
- return { title: description, detail: description };
1871
+ const fromConstraintName = resolveConstraintNameMessage(constraintName);
1872
+ if (fromConstraintName) return fromConstraintName;
1873
+ return (_a = safeDescriptionMessage(description)) != null ? _a : FALLBACK_MESSAGE;
1874
+ }
1875
+ function getRuleBasedViolationMessage(ruleId, constraintName, description, archetype) {
1876
+ var _a;
1877
+ const fromRuleId = resolveRuleIdMessage(ruleId);
1878
+ if (fromRuleId) return fromRuleId;
1879
+ const fromConstraintName = resolveConstraintNameMessage(constraintName);
1880
+ if (fromConstraintName) return fromConstraintName;
1881
+ if ((archetype == null ? void 0 : archetype.trim()) && archetype.trim() in ARCHETYPE_FALLBACK_MESSAGES) {
1882
+ return ARCHETYPE_FALLBACK_MESSAGES[archetype.trim()];
1728
1883
  }
1729
- return FALLBACK_MESSAGE;
1884
+ return (_a = safeDescriptionMessage(description)) != null ? _a : FALLBACK_MESSAGE;
1730
1885
  }
1731
1886
 
1732
1887
  // src/utils/mapViolationsToResult.ts
1888
+ function resolveCustomViolationMessage(ruleId, customMessages) {
1889
+ if (!customMessages) return void 0;
1890
+ const direct = customMessages[ruleId];
1891
+ if (direct) return direct;
1892
+ const uiRuleId = BACKEND_TO_UI_RULE_ID[ruleId];
1893
+ if (uiRuleId) {
1894
+ return customMessages[uiRuleId];
1895
+ }
1896
+ return void 0;
1897
+ }
1733
1898
  function mapViolationsToResult(violations, summary, customMessages) {
1734
1899
  var _a;
1735
1900
  const hardViolations = violations.filter((v) => v.isHardViolation === true);
@@ -1737,7 +1902,7 @@ function mapViolationsToResult(violations, summary, customMessages) {
1737
1902
  const violatedConstraints = [];
1738
1903
  for (const v of hardViolations) {
1739
1904
  const hasRuleId = v.ruleId != null && v.ruleId !== void 0 && String(v.ruleId).trim() !== "";
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);
1905
+ const msg = hasRuleId ? (_a = resolveCustomViolationMessage(v.ruleId, customMessages)) != null ? _a : getRuleBasedViolationMessage(v.ruleId, v.constraintName, v.description, v.archetype) : getSystemConstraintMessage(v.constraintName, v.description);
1741
1906
  if (!seenTitles.has(msg.title)) {
1742
1907
  seenTitles.add(msg.title);
1743
1908
  violatedConstraints.push(msg);
@@ -1747,18 +1912,12 @@ function mapViolationsToResult(violations, summary, customMessages) {
1747
1912
  violatedConstraints,
1748
1913
  recommendedFixes: []
1749
1914
  };
1750
- if (violatedConstraints.length === 0 && (summary == null ? void 0 : summary.trim())) {
1751
- result.violatedConstraints = [
1752
- {
1753
- title: "Schedule could not be created",
1754
- detail: summary
1755
- }
1756
- ];
1757
- } else if (violatedConstraints.length === 0) {
1915
+ if (violatedConstraints.length === 0) {
1916
+ const safeSummary = (summary == null ? void 0 : summary.trim()) && !isInternalViolationText(summary) ? summary.trim() : void 0;
1758
1917
  result.violatedConstraints = [
1759
1918
  {
1760
1919
  title: "Schedule could not be created",
1761
- detail: "Please adjust constraints and try again."
1920
+ detail: safeSummary != null ? safeSummary : "Please adjust constraints and try again."
1762
1921
  }
1763
1922
  ];
1764
1923
  }
@@ -2072,11 +2231,21 @@ function AutoSchedulerModal({
2072
2231
  const dynamicViolationMessages = (0, import_react3.useMemo)(() => {
2073
2232
  const map = {};
2074
2233
  for (const opt of effectiveHardConstraintOptions) {
2075
- map[opt.id] = { title: opt.name, detail: opt.description || opt.name };
2234
+ const msg = { title: opt.name, detail: opt.description || opt.name };
2235
+ map[opt.id] = msg;
2236
+ const backendRuleId = toBackendRuleId(opt.id);
2237
+ if (backendRuleId) {
2238
+ map[backendRuleId] = msg;
2239
+ }
2076
2240
  }
2077
2241
  for (const group of effectiveSoftConstraintGroups) {
2078
2242
  for (const opt of group.options) {
2079
- map[opt.id] = { title: opt.title, detail: opt.subtitle || opt.title };
2243
+ const msg = { title: opt.title, detail: opt.subtitle || opt.title };
2244
+ map[opt.id] = msg;
2245
+ const backendRuleId = toBackendRuleId(opt.id);
2246
+ if (backendRuleId) {
2247
+ map[backendRuleId] = msg;
2248
+ }
2080
2249
  }
2081
2250
  }
2082
2251
  return map;