@open-mercato/core 0.6.4-develop.4095.1.9c790dc021 → 0.6.4-develop.4110.1.836aafde58

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.
Files changed (35) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/dist/modules/currencies/acl.js +10 -5
  3. package/dist/modules/currencies/acl.js.map +2 -2
  4. package/dist/modules/directory/acl.js +12 -2
  5. package/dist/modules/directory/acl.js.map +2 -2
  6. package/dist/modules/directory/api/tenants/route.js +48 -34
  7. package/dist/modules/directory/api/tenants/route.js.map +2 -2
  8. package/dist/modules/entities/acl.js +12 -2
  9. package/dist/modules/entities/acl.js.map +2 -2
  10. package/dist/modules/integrations/api/[id]/credentials/route.js +20 -2
  11. package/dist/modules/integrations/api/[id]/credentials/route.js.map +2 -2
  12. package/dist/modules/integrations/lib/credentials-service.js +15 -25
  13. package/dist/modules/integrations/lib/credentials-service.js.map +2 -2
  14. package/dist/modules/planner/components/AvailabilityRulesEditor.js +39 -26
  15. package/dist/modules/planner/components/AvailabilityRulesEditor.js.map +2 -2
  16. package/dist/modules/planner/components/availabilityRulesEditorState.js +8 -0
  17. package/dist/modules/planner/components/availabilityRulesEditorState.js.map +7 -0
  18. package/dist/modules/query_index/acl.js +12 -2
  19. package/dist/modules/query_index/acl.js.map +2 -2
  20. package/dist/modules/resources/acl.js +6 -1
  21. package/dist/modules/resources/acl.js.map +2 -2
  22. package/dist/modules/sync_excel/acl.js +6 -1
  23. package/dist/modules/sync_excel/acl.js.map +2 -2
  24. package/package.json +7 -7
  25. package/src/modules/currencies/acl.ts +5 -0
  26. package/src/modules/directory/acl.ts +12 -2
  27. package/src/modules/directory/api/tenants/route.ts +55 -41
  28. package/src/modules/entities/acl.ts +12 -2
  29. package/src/modules/integrations/api/[id]/credentials/route.ts +21 -3
  30. package/src/modules/integrations/lib/credentials-service.ts +15 -33
  31. package/src/modules/planner/components/AvailabilityRulesEditor.tsx +40 -26
  32. package/src/modules/planner/components/availabilityRulesEditorState.ts +11 -0
  33. package/src/modules/query_index/acl.ts +12 -2
  34. package/src/modules/resources/acl.ts +6 -1
  35. package/src/modules/sync_excel/acl.ts +6 -1
@@ -29,6 +29,7 @@ import { useConfirmDialog } from "@open-mercato/ui/backend/confirm-dialog";
29
29
  import { parseAvailabilityRuleWindow } from "@open-mercato/core/modules/planner/lib/availabilitySchedule";
30
30
  import { CrudForm } from "@open-mercato/ui/backend/CrudForm";
31
31
  import { Calendar, Clock, List, PencilLine, Plus, Trash2 } from "lucide-react";
32
+ import { resolveRuleSetSelectValue } from "./availabilityRulesEditorState.js";
32
33
  const DAY_LABELS = [
33
34
  { code: "SU", short: "S", nameKey: "schedule.weekday.sunday", fallback: "Sunday" },
34
35
  { code: "MO", short: "M", nameKey: "schedule.weekday.monday", fallback: "Monday" },
@@ -313,6 +314,7 @@ function AvailabilityRulesEditor({
313
314
  const [createRuleSetOpen, setCreateRuleSetOpen] = React.useState(false);
314
315
  const [isWeeklyAutoSaving, setIsWeeklyAutoSaving] = React.useState(false);
315
316
  const [customOverridesEnabled, setCustomOverridesEnabled] = React.useState(false);
317
+ const [selectedRulesetId, setSelectedRulesetId] = React.useState(rulesetId ?? null);
316
318
  const autoSaveTimerRef = React.useRef(null);
317
319
  const lastSavedWeeklyKeyRef = React.useRef(null);
318
320
  const weeklySaveStateRef = React.useRef({ inFlight: false, queued: false });
@@ -321,7 +323,11 @@ function AvailabilityRulesEditor({
321
323
  const viewModeRef = React.useRef(viewMode);
322
324
  const saveWeeklyRef = React.useRef(null);
323
325
  const timezoneOptions = React.useMemo(() => getTimezoneOptions(), []);
324
- const usingRuleSet = Boolean(rulesetId) && availabilityRules.length === 0 && !customOverridesEnabled;
326
+ React.useEffect(() => {
327
+ setSelectedRulesetId(rulesetId ?? null);
328
+ }, [rulesetId]);
329
+ const effectiveRulesetId = selectedRulesetId;
330
+ const usingRuleSet = Boolean(effectiveRulesetId) && availabilityRules.length === 0 && !customOverridesEnabled;
325
331
  const activeRules = usingRuleSet ? rulesetRules : availabilityRules;
326
332
  const scheduleRules = React.useMemo(() => {
327
333
  const dateBlockers = /* @__PURE__ */ new Set();
@@ -455,7 +461,7 @@ function AvailabilityRulesEditor({
455
461
  }
456
462
  }, [labelPrefix, subjectId, subjectType, t]);
457
463
  const refreshRuleSetRules = React.useCallback(async () => {
458
- if (!rulesetId) {
464
+ if (!effectiveRulesetId) {
459
465
  setRulesetRules([]);
460
466
  return;
461
467
  }
@@ -464,7 +470,7 @@ function AvailabilityRulesEditor({
464
470
  page: "1",
465
471
  pageSize: "100",
466
472
  subjectType: "ruleset",
467
- subjectIds: rulesetId
473
+ subjectIds: effectiveRulesetId
468
474
  });
469
475
  const call = await apiCall(`/api/planner/availability?${params.toString()}`);
470
476
  const items = Array.isArray(call.result?.items) ? call.result.items : [];
@@ -472,7 +478,7 @@ function AvailabilityRulesEditor({
472
478
  } catch {
473
479
  setRulesetRules([]);
474
480
  }
475
- }, [rulesetId]);
481
+ }, [effectiveRulesetId]);
476
482
  const refreshRuleSets = React.useCallback(async () => {
477
483
  if (!onRulesetChange) return;
478
484
  setRuleSetsLoading(true);
@@ -550,20 +556,20 @@ function AvailabilityRulesEditor({
550
556
  }, [activeRules, initialTimezone, timezoneDirty]);
551
557
  React.useEffect(() => {
552
558
  if (timezoneDirty) return;
553
- if (!usingRuleSet || !rulesetId) return;
554
- const ruleset = ruleSets.find((entry) => entry.id === rulesetId);
559
+ if (!usingRuleSet || !effectiveRulesetId) return;
560
+ const ruleset = ruleSets.find((entry) => entry.id === effectiveRulesetId);
555
561
  if (ruleset?.timezone) setTimezone(ruleset.timezone);
556
- }, [rulesetId, ruleSets, timezoneDirty, usingRuleSet]);
562
+ }, [effectiveRulesetId, ruleSets, timezoneDirty, usingRuleSet]);
557
563
  React.useEffect(() => {
558
564
  if (availabilityRules.length > 0) {
559
565
  setCustomOverridesEnabled(true);
560
566
  }
561
567
  }, [availabilityRules.length]);
562
568
  React.useEffect(() => {
563
- if (!rulesetId) {
569
+ if (!effectiveRulesetId) {
564
570
  setCustomOverridesEnabled(false);
565
571
  }
566
- }, [rulesetId]);
572
+ }, [effectiveRulesetId]);
567
573
  const refreshBookedEvents = React.useCallback(async () => {
568
574
  if (!loadBookedEvents) return;
569
575
  setBookedLoading(true);
@@ -647,7 +653,7 @@ function AvailabilityRulesEditor({
647
653
  const saveWeeklyHours = React.useCallback(async (options) => {
648
654
  if (isReadOnly) return;
649
655
  const subjectForRules = usingRuleSet ? "ruleset" : subjectType;
650
- const subjectIdForRules = usingRuleSet ? rulesetId ?? "" : subjectId;
656
+ const subjectIdForRules = usingRuleSet ? effectiveRulesetId ?? "" : subjectId;
651
657
  if (!subjectIdForRules) return;
652
658
  if (weeklyHasErrors) return;
653
659
  const shouldSkipRefresh = Boolean(options?.skipRefresh);
@@ -684,7 +690,7 @@ function AvailabilityRulesEditor({
684
690
  listLabels.saveWeeklySuccess,
685
691
  refreshAvailability,
686
692
  refreshRuleSetRules,
687
- rulesetId,
693
+ effectiveRulesetId,
688
694
  subjectId,
689
695
  subjectType,
690
696
  timezone,
@@ -734,7 +740,7 @@ function AvailabilityRulesEditor({
734
740
  if (isReadOnly) return;
735
741
  if (timezoneSaveInFlightRef.current) return;
736
742
  const trimmedTimezone = nextTimezone.trim() || "UTC";
737
- const rulesetTimezoneId = subjectType === "ruleset" ? subjectId : rulesetId;
743
+ const rulesetTimezoneId = subjectType === "ruleset" ? subjectId : effectiveRulesetId;
738
744
  const rulesToUpdate = activeRules.filter((rule) => rule.timezone !== trimmedTimezone);
739
745
  if (!rulesToUpdate.length && !rulesetTimezoneId) {
740
746
  setTimezoneDirty(false);
@@ -773,7 +779,7 @@ function AvailabilityRulesEditor({
773
779
  listLabels.timezoneSaveError,
774
780
  refreshAvailability,
775
781
  refreshRuleSetRules,
776
- rulesetId,
782
+ effectiveRulesetId,
777
783
  subjectId,
778
784
  subjectType,
779
785
  isReadOnly
@@ -802,7 +808,7 @@ function AvailabilityRulesEditor({
802
808
  }, [canManageUnavailability, isReadOnly]);
803
809
  const handleCustomize = React.useCallback(async () => {
804
810
  if (isReadOnly) return;
805
- if (!rulesetId) return;
811
+ if (!effectiveRulesetId) return;
806
812
  try {
807
813
  const creations = rulesetRules.map((rule) => createCrud("planner/availability", {
808
814
  subjectType,
@@ -820,10 +826,10 @@ function AvailabilityRulesEditor({
820
826
  const message = error2 instanceof Error ? error2.message : listLabels.saveWeeklyError;
821
827
  flash(message, "error");
822
828
  }
823
- }, [listLabels.saveWeeklyError, refreshAvailability, rulesetId, rulesetRules, subjectId, subjectType, isReadOnly]);
829
+ }, [effectiveRulesetId, listLabels.saveWeeklyError, refreshAvailability, rulesetRules, subjectId, subjectType, isReadOnly]);
824
830
  const handleResetToRuleSet = React.useCallback(async () => {
825
831
  if (isReadOnly) return;
826
- if (!rulesetId) return;
832
+ if (!effectiveRulesetId) return;
827
833
  try {
828
834
  await Promise.all(
829
835
  availabilityRules.map((rule) => deleteCrud("planner/availability", rule.id, { errorMessage: listLabels.saveWeeklyError }))
@@ -834,11 +840,11 @@ function AvailabilityRulesEditor({
834
840
  const message = error2 instanceof Error ? error2.message : listLabels.saveWeeklyError;
835
841
  flash(message, "error");
836
842
  }
837
- }, [availabilityRules, listLabels.saveWeeklyError, refreshAvailability, rulesetId, isReadOnly]);
843
+ }, [availabilityRules, effectiveRulesetId, listLabels.saveWeeklyError, refreshAvailability, isReadOnly]);
838
844
  const handleRuleSetChange = React.useCallback(async (nextId) => {
839
845
  if (isReadOnly) return;
840
846
  if (!onRulesetChange) return;
841
- if (availabilityRules.length > 0 && nextId !== rulesetId) {
847
+ if (availabilityRules.length > 0 && nextId !== effectiveRulesetId) {
842
848
  const confirmed = await confirm({
843
849
  title: listLabels.ruleSetConfirm,
844
850
  variant: "default"
@@ -848,17 +854,23 @@ function AvailabilityRulesEditor({
848
854
  availabilityRules.map((rule) => deleteCrud("planner/availability", rule.id, { errorMessage: listLabels.saveWeeklyError }))
849
855
  );
850
856
  }
857
+ setSelectedRulesetId(nextId);
851
858
  setCustomOverridesEnabled(false);
852
- await onRulesetChange(nextId);
853
- await refreshAvailability();
859
+ try {
860
+ await onRulesetChange(nextId);
861
+ await refreshAvailability();
862
+ } catch (error2) {
863
+ setSelectedRulesetId(effectiveRulesetId);
864
+ throw error2;
865
+ }
854
866
  }, [
855
867
  availabilityRules,
856
868
  confirm,
869
+ effectiveRulesetId,
857
870
  listLabels.ruleSetConfirm,
858
871
  listLabels.saveWeeklyError,
859
872
  onRulesetChange,
860
873
  refreshAvailability,
861
- rulesetId,
862
874
  isReadOnly
863
875
  ]);
864
876
  const ruleSetFormSchema = React.useMemo(
@@ -927,6 +939,7 @@ function AvailabilityRulesEditor({
927
939
  }
928
940
  await refreshRuleSets();
929
941
  if (onRulesetChange) {
942
+ setSelectedRulesetId(id);
930
943
  await onRulesetChange(id);
931
944
  await refreshAvailability();
932
945
  }
@@ -1015,7 +1028,7 @@ function AvailabilityRulesEditor({
1015
1028
  if (isReadOnly) return;
1016
1029
  if (editorUnavailable && !canManageUnavailability) return;
1017
1030
  const subjectForRules = usingRuleSet ? "ruleset" : subjectType;
1018
- const subjectIdForRules = usingRuleSet ? rulesetId ?? "" : subjectId;
1031
+ const subjectIdForRules = usingRuleSet ? effectiveRulesetId ?? "" : subjectId;
1019
1032
  if (!subjectIdForRules) return;
1020
1033
  if (!editorUnavailable && editorWindowErrors.some(Boolean)) return;
1021
1034
  const validWindows = editorWindows;
@@ -1091,7 +1104,7 @@ function AvailabilityRulesEditor({
1091
1104
  refreshAvailability,
1092
1105
  refreshRuleSetRules,
1093
1106
  reasonEntriesById,
1094
- rulesetId,
1107
+ effectiveRulesetId,
1095
1108
  subjectId,
1096
1109
  subjectType,
1097
1110
  timezone,
@@ -1163,7 +1176,7 @@ function AvailabilityRulesEditor({
1163
1176
  ruleSetsLoading ? /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: listLabels.ruleSetLoading }) : /* @__PURE__ */ jsxs(
1164
1177
  Select,
1165
1178
  {
1166
- value: rulesetId || void 0,
1179
+ value: resolveRuleSetSelectValue(ruleSets, effectiveRulesetId),
1167
1180
  onValueChange: (value) => {
1168
1181
  void handleRuleSetChange(value ? value : null);
1169
1182
  },
@@ -1178,8 +1191,8 @@ function AvailabilityRulesEditor({
1178
1191
  /* @__PURE__ */ jsx(Plus, { className: "size-4 mr-2", "aria-hidden": true }),
1179
1192
  listLabels.ruleSetCreateLabel
1180
1193
  ] }),
1181
- rulesetId && usingRuleSet ? /* @__PURE__ */ jsx(Button, { type: "button", variant: "outline", size: "sm", onClick: handleCustomize, disabled: isReadOnly, children: listLabels.ruleSetCustomize }) : null,
1182
- rulesetId && !usingRuleSet ? /* @__PURE__ */ jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: handleResetToRuleSet, disabled: isReadOnly, children: listLabels.ruleSetReset }) : null
1194
+ effectiveRulesetId && usingRuleSet ? /* @__PURE__ */ jsx(Button, { type: "button", variant: "outline", size: "sm", onClick: handleCustomize, disabled: isReadOnly, children: listLabels.ruleSetCustomize }) : null,
1195
+ effectiveRulesetId && !usingRuleSet ? /* @__PURE__ */ jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: handleResetToRuleSet, disabled: isReadOnly, children: listLabels.ruleSetReset }) : null
1183
1196
  ] }) : null,
1184
1197
  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-xs text-muted-foreground", children: [
1185
1198
  /* @__PURE__ */ jsx("span", { children: listLabels.timezoneLabel }),