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