@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
@@ -30,6 +30,7 @@ import { useConfirmDialog } from '@open-mercato/ui/backend/confirm-dialog'
30
30
  import { parseAvailabilityRuleWindow } from '@open-mercato/core/modules/planner/lib/availabilitySchedule'
31
31
  import { CrudForm, type CrudField } from '@open-mercato/ui/backend/CrudForm'
32
32
  import { Calendar, Clock, List, PencilLine, Plus, Trash2 } from 'lucide-react'
33
+ import { resolveRuleSetSelectValue } from './availabilityRulesEditorState'
33
34
 
34
35
  type AvailabilityRepeat = 'once' | 'daily' | 'weekly'
35
36
  type AvailabilitySubjectType = 'member' | 'resource' | 'ruleset'
@@ -398,6 +399,7 @@ export function AvailabilityRulesEditor({
398
399
  const [createRuleSetOpen, setCreateRuleSetOpen] = React.useState(false)
399
400
  const [isWeeklyAutoSaving, setIsWeeklyAutoSaving] = React.useState(false)
400
401
  const [customOverridesEnabled, setCustomOverridesEnabled] = React.useState(false)
402
+ const [selectedRulesetId, setSelectedRulesetId] = React.useState<string | null>(rulesetId ?? null)
401
403
  const autoSaveTimerRef = React.useRef<number | null>(null)
402
404
  const lastSavedWeeklyKeyRef = React.useRef<string | null>(null)
403
405
  const weeklySaveStateRef = React.useRef({ inFlight: false, queued: false })
@@ -409,7 +411,12 @@ export function AvailabilityRulesEditor({
409
411
  >(null)
410
412
  const timezoneOptions = React.useMemo(() => getTimezoneOptions(), [])
411
413
 
412
- const usingRuleSet = Boolean(rulesetId) && availabilityRules.length === 0 && !customOverridesEnabled
414
+ React.useEffect(() => {
415
+ setSelectedRulesetId(rulesetId ?? null)
416
+ }, [rulesetId])
417
+
418
+ const effectiveRulesetId = selectedRulesetId
419
+ const usingRuleSet = Boolean(effectiveRulesetId) && availabilityRules.length === 0 && !customOverridesEnabled
413
420
  const activeRules = usingRuleSet ? rulesetRules : availabilityRules
414
421
  const scheduleRules = React.useMemo(() => {
415
422
  const dateBlockers = new Set<string>()
@@ -547,7 +554,7 @@ export function AvailabilityRulesEditor({
547
554
  }, [labelPrefix, subjectId, subjectType, t])
548
555
 
549
556
  const refreshRuleSetRules = React.useCallback(async () => {
550
- if (!rulesetId) {
557
+ if (!effectiveRulesetId) {
551
558
  setRulesetRules([])
552
559
  return
553
560
  }
@@ -556,7 +563,7 @@ export function AvailabilityRulesEditor({
556
563
  page: '1',
557
564
  pageSize: '100',
558
565
  subjectType: 'ruleset',
559
- subjectIds: rulesetId,
566
+ subjectIds: effectiveRulesetId,
560
567
  })
561
568
  const call = await apiCall<{ items?: AvailabilityRule[] }>(`/api/planner/availability?${params.toString()}`)
562
569
  const items = Array.isArray(call.result?.items) ? call.result.items : []
@@ -564,7 +571,7 @@ export function AvailabilityRulesEditor({
564
571
  } catch {
565
572
  setRulesetRules([])
566
573
  }
567
- }, [rulesetId])
574
+ }, [effectiveRulesetId])
568
575
 
569
576
  const refreshRuleSets = React.useCallback(async () => {
570
577
  if (!onRulesetChange) return
@@ -653,10 +660,10 @@ export function AvailabilityRulesEditor({
653
660
 
654
661
  React.useEffect(() => {
655
662
  if (timezoneDirty) return
656
- if (!usingRuleSet || !rulesetId) return
657
- const ruleset = ruleSets.find((entry) => entry.id === rulesetId)
663
+ if (!usingRuleSet || !effectiveRulesetId) return
664
+ const ruleset = ruleSets.find((entry) => entry.id === effectiveRulesetId)
658
665
  if (ruleset?.timezone) setTimezone(ruleset.timezone)
659
- }, [rulesetId, ruleSets, timezoneDirty, usingRuleSet])
666
+ }, [effectiveRulesetId, ruleSets, timezoneDirty, usingRuleSet])
660
667
 
661
668
  React.useEffect(() => {
662
669
  if (availabilityRules.length > 0) {
@@ -665,10 +672,10 @@ export function AvailabilityRulesEditor({
665
672
  }, [availabilityRules.length])
666
673
 
667
674
  React.useEffect(() => {
668
- if (!rulesetId) {
675
+ if (!effectiveRulesetId) {
669
676
  setCustomOverridesEnabled(false)
670
677
  }
671
- }, [rulesetId])
678
+ }, [effectiveRulesetId])
672
679
 
673
680
  const refreshBookedEvents = React.useCallback(async () => {
674
681
  if (!loadBookedEvents) return
@@ -764,7 +771,7 @@ export function AvailabilityRulesEditor({
764
771
  const saveWeeklyHours = React.useCallback(async (options?: { silentSuccess?: boolean; skipRefresh?: boolean }) => {
765
772
  if (isReadOnly) return
766
773
  const subjectForRules: AvailabilitySubjectType = usingRuleSet ? 'ruleset' : subjectType
767
- const subjectIdForRules = usingRuleSet ? (rulesetId ?? '') : subjectId
774
+ const subjectIdForRules = usingRuleSet ? (effectiveRulesetId ?? '') : subjectId
768
775
  if (!subjectIdForRules) return
769
776
  if (weeklyHasErrors) return
770
777
 
@@ -802,7 +809,7 @@ export function AvailabilityRulesEditor({
802
809
  listLabels.saveWeeklySuccess,
803
810
  refreshAvailability,
804
811
  refreshRuleSetRules,
805
- rulesetId,
812
+ effectiveRulesetId,
806
813
  subjectId,
807
814
  subjectType,
808
815
  timezone,
@@ -856,7 +863,7 @@ export function AvailabilityRulesEditor({
856
863
  if (isReadOnly) return
857
864
  if (timezoneSaveInFlightRef.current) return
858
865
  const trimmedTimezone = nextTimezone.trim() || 'UTC'
859
- const rulesetTimezoneId = subjectType === 'ruleset' ? subjectId : rulesetId
866
+ const rulesetTimezoneId = subjectType === 'ruleset' ? subjectId : effectiveRulesetId
860
867
  const rulesToUpdate = activeRules.filter((rule) => rule.timezone !== trimmedTimezone)
861
868
  if (!rulesToUpdate.length && !rulesetTimezoneId) {
862
869
  setTimezoneDirty(false)
@@ -895,7 +902,7 @@ export function AvailabilityRulesEditor({
895
902
  listLabels.timezoneSaveError,
896
903
  refreshAvailability,
897
904
  refreshRuleSetRules,
898
- rulesetId,
905
+ effectiveRulesetId,
899
906
  subjectId,
900
907
  subjectType,
901
908
  isReadOnly,
@@ -927,7 +934,7 @@ export function AvailabilityRulesEditor({
927
934
 
928
935
  const handleCustomize = React.useCallback(async () => {
929
936
  if (isReadOnly) return
930
- if (!rulesetId) return
937
+ if (!effectiveRulesetId) return
931
938
  try {
932
939
  const creations = rulesetRules.map((rule) => createCrud('planner/availability', {
933
940
  subjectType,
@@ -945,11 +952,11 @@ export function AvailabilityRulesEditor({
945
952
  const message = error instanceof Error ? error.message : listLabels.saveWeeklyError
946
953
  flash(message, 'error')
947
954
  }
948
- }, [listLabels.saveWeeklyError, refreshAvailability, rulesetId, rulesetRules, subjectId, subjectType, isReadOnly])
955
+ }, [effectiveRulesetId, listLabels.saveWeeklyError, refreshAvailability, rulesetRules, subjectId, subjectType, isReadOnly])
949
956
 
950
957
  const handleResetToRuleSet = React.useCallback(async () => {
951
958
  if (isReadOnly) return
952
- if (!rulesetId) return
959
+ if (!effectiveRulesetId) return
953
960
  try {
954
961
  await Promise.all(
955
962
  availabilityRules.map((rule) => deleteCrud('planner/availability', rule.id, { errorMessage: listLabels.saveWeeklyError })),
@@ -960,12 +967,12 @@ export function AvailabilityRulesEditor({
960
967
  const message = error instanceof Error ? error.message : listLabels.saveWeeklyError
961
968
  flash(message, 'error')
962
969
  }
963
- }, [availabilityRules, listLabels.saveWeeklyError, refreshAvailability, rulesetId, isReadOnly])
970
+ }, [availabilityRules, effectiveRulesetId, listLabels.saveWeeklyError, refreshAvailability, isReadOnly])
964
971
 
965
972
  const handleRuleSetChange = React.useCallback(async (nextId: string | null) => {
966
973
  if (isReadOnly) return
967
974
  if (!onRulesetChange) return
968
- if (availabilityRules.length > 0 && nextId !== rulesetId) {
975
+ if (availabilityRules.length > 0 && nextId !== effectiveRulesetId) {
969
976
  const confirmed = await confirm({
970
977
  title: listLabels.ruleSetConfirm,
971
978
  variant: 'default',
@@ -975,17 +982,23 @@ export function AvailabilityRulesEditor({
975
982
  availabilityRules.map((rule) => deleteCrud('planner/availability', rule.id, { errorMessage: listLabels.saveWeeklyError })),
976
983
  )
977
984
  }
985
+ setSelectedRulesetId(nextId)
978
986
  setCustomOverridesEnabled(false)
979
- await onRulesetChange(nextId)
980
- await refreshAvailability()
987
+ try {
988
+ await onRulesetChange(nextId)
989
+ await refreshAvailability()
990
+ } catch (error) {
991
+ setSelectedRulesetId(effectiveRulesetId)
992
+ throw error
993
+ }
981
994
  }, [
982
995
  availabilityRules,
983
996
  confirm,
997
+ effectiveRulesetId,
984
998
  listLabels.ruleSetConfirm,
985
999
  listLabels.saveWeeklyError,
986
1000
  onRulesetChange,
987
1001
  refreshAvailability,
988
- rulesetId,
989
1002
  isReadOnly,
990
1003
  ])
991
1004
 
@@ -1058,6 +1071,7 @@ export function AvailabilityRulesEditor({
1058
1071
  }
1059
1072
  await refreshRuleSets()
1060
1073
  if (onRulesetChange) {
1074
+ setSelectedRulesetId(id)
1061
1075
  await onRulesetChange(id)
1062
1076
  await refreshAvailability()
1063
1077
  }
@@ -1155,7 +1169,7 @@ export function AvailabilityRulesEditor({
1155
1169
  if (isReadOnly) return
1156
1170
  if (editorUnavailable && !canManageUnavailability) return
1157
1171
  const subjectForRules: AvailabilitySubjectType = usingRuleSet ? 'ruleset' : subjectType
1158
- const subjectIdForRules = usingRuleSet ? (rulesetId ?? '') : subjectId
1172
+ const subjectIdForRules = usingRuleSet ? (effectiveRulesetId ?? '') : subjectId
1159
1173
  if (!subjectIdForRules) return
1160
1174
  if (!editorUnavailable && editorWindowErrors.some(Boolean)) return
1161
1175
  const validWindows = editorWindows
@@ -1235,7 +1249,7 @@ export function AvailabilityRulesEditor({
1235
1249
  refreshAvailability,
1236
1250
  refreshRuleSetRules,
1237
1251
  reasonEntriesById,
1238
- rulesetId,
1252
+ effectiveRulesetId,
1239
1253
  subjectId,
1240
1254
  subjectType,
1241
1255
  timezone,
@@ -1316,7 +1330,7 @@ export function AvailabilityRulesEditor({
1316
1330
  <span className="text-xs text-muted-foreground">{listLabels.ruleSetLoading}</span>
1317
1331
  ) : (
1318
1332
  <Select
1319
- value={rulesetId || undefined}
1333
+ value={resolveRuleSetSelectValue(ruleSets, effectiveRulesetId)}
1320
1334
  onValueChange={(value) => {
1321
1335
  void handleRuleSetChange(value ? value : null)
1322
1336
  }}
@@ -1338,12 +1352,12 @@ export function AvailabilityRulesEditor({
1338
1352
  <Plus className="size-4 mr-2" aria-hidden />
1339
1353
  {listLabels.ruleSetCreateLabel}
1340
1354
  </Button>
1341
- {rulesetId && usingRuleSet ? (
1355
+ {effectiveRulesetId && usingRuleSet ? (
1342
1356
  <Button type="button" variant="outline" size="sm" onClick={handleCustomize} disabled={isReadOnly}>
1343
1357
  {listLabels.ruleSetCustomize}
1344
1358
  </Button>
1345
1359
  ) : null}
1346
- {rulesetId && !usingRuleSet ? (
1360
+ {effectiveRulesetId && !usingRuleSet ? (
1347
1361
  <Button type="button" variant="ghost" size="sm" onClick={handleResetToRuleSet} disabled={isReadOnly}>
1348
1362
  {listLabels.ruleSetReset}
1349
1363
  </Button>
@@ -0,0 +1,11 @@
1
+ export type AvailabilityRuleSetOption = {
2
+ id: string
3
+ }
4
+
5
+ export function resolveRuleSetSelectValue(
6
+ ruleSets: AvailabilityRuleSetOption[],
7
+ selectedRulesetId: string | null | undefined,
8
+ ): string | undefined {
9
+ if (!selectedRulesetId) return undefined
10
+ return ruleSets.some((ruleSet) => ruleSet.id === selectedRulesetId) ? selectedRulesetId : undefined
11
+ }
@@ -1,7 +1,17 @@
1
1
  export const features = [
2
2
  { id: 'query_index.status.view', title: 'View index status', module: 'query_index' },
3
- { id: 'query_index.reindex', title: 'Trigger reindex', module: 'query_index' },
4
- { id: 'query_index.purge', title: 'Purge index', module: 'query_index' },
3
+ {
4
+ id: 'query_index.reindex',
5
+ title: 'Trigger reindex',
6
+ module: 'query_index',
7
+ dependsOn: ['query_index.status.view'],
8
+ },
9
+ {
10
+ id: 'query_index.purge',
11
+ title: 'Purge index',
12
+ module: 'query_index',
13
+ dependsOn: ['query_index.status.view'],
14
+ },
5
15
  ]
6
16
 
7
17
  export default features
@@ -1,6 +1,11 @@
1
1
  export const features = [
2
2
  { id: 'resources.view', title: 'View resources', module: 'resources' },
3
- { id: 'resources.manage_resources', title: 'Manage resources', module: 'resources' },
3
+ {
4
+ id: 'resources.manage_resources',
5
+ title: 'Manage resources',
6
+ module: 'resources',
7
+ dependsOn: ['resources.view'],
8
+ },
4
9
  ]
5
10
 
6
11
  export default features
@@ -1,6 +1,11 @@
1
1
  export const features = [
2
2
  { id: 'sync_excel.view', title: 'View sync_excel uploads and previews', module: 'sync_excel' },
3
- { id: 'sync_excel.run', title: 'Upload CSV files and prepare imports', module: 'sync_excel' },
3
+ {
4
+ id: 'sync_excel.run',
5
+ title: 'Upload CSV files and prepare imports',
6
+ module: 'sync_excel',
7
+ dependsOn: ['sync_excel.view'],
8
+ },
4
9
  ]
5
10
 
6
11
  export default features