@open-mercato/core 0.6.6-develop.6257.1.6d0af84d26 → 0.6.6-develop.6290.1.4bb5a8ba3f
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/.turbo/turbo-build.log +2 -2
- package/dist/generated/entities/carrier_shipment_idempotency_key/index.js +19 -0
- package/dist/generated/entities/carrier_shipment_idempotency_key/index.js.map +7 -0
- package/dist/generated/entities.ids.generated.js +2 -1
- package/dist/generated/entities.ids.generated.js.map +2 -2
- package/dist/generated/entity-fields-registry.js +10 -0
- package/dist/generated/entity-fields-registry.js.map +2 -2
- package/dist/modules/catalog/lib/productResolution.js +45 -0
- package/dist/modules/catalog/lib/productResolution.js.map +7 -0
- package/dist/modules/communication_channels/workers/channel-import-history.js +1 -1
- package/dist/modules/communication_channels/workers/channel-import-history.js.map +2 -2
- package/dist/modules/currencies/api/fetch-configs/route.js +34 -0
- package/dist/modules/currencies/api/fetch-configs/route.js.map +2 -2
- package/dist/modules/currencies/components/CurrencyFetchingConfig.js +17 -15
- package/dist/modules/currencies/components/CurrencyFetchingConfig.js.map +2 -2
- package/dist/modules/customers/api/companies/[id]/route.js +176 -161
- package/dist/modules/customers/api/companies/[id]/route.js.map +2 -2
- package/dist/modules/customers/api/people/[id]/route.js +75 -59
- package/dist/modules/customers/api/people/[id]/route.js.map +2 -2
- package/dist/modules/customers/backend/config/customers/pipeline-stages/page.js +17 -10
- package/dist/modules/customers/backend/config/customers/pipeline-stages/page.js.map +2 -2
- package/dist/modules/customers/components/detail/ActivitiesAddNewMenu.js +6 -4
- package/dist/modules/customers/components/detail/ActivitiesAddNewMenu.js.map +2 -2
- package/dist/modules/customers/components/detail/AiActionChips.js +5 -2
- package/dist/modules/customers/components/detail/AiActionChips.js.map +2 -2
- package/dist/modules/customers/components/detail/schedule/ParticipantsField.js +19 -21
- package/dist/modules/customers/components/detail/schedule/ParticipantsField.js.map +2 -2
- package/dist/modules/data_sync/api/runs/[id]/cancel.js +1 -1
- package/dist/modules/data_sync/api/runs/[id]/cancel.js.map +2 -2
- package/dist/modules/data_sync/backend/data-sync/page.js +9 -45
- package/dist/modules/data_sync/backend/data-sync/page.js.map +2 -2
- package/dist/modules/data_sync/backend/data-sync/runs/[id]/page.js +11 -17
- package/dist/modules/data_sync/backend/data-sync/runs/[id]/page.js.map +2 -2
- package/dist/modules/data_sync/components/IntegrationScheduleTab.js +12 -10
- package/dist/modules/data_sync/components/IntegrationScheduleTab.js.map +2 -2
- package/dist/modules/data_sync/lib/id-mapping.js +46 -1
- package/dist/modules/data_sync/lib/id-mapping.js.map +2 -2
- package/dist/modules/data_sync/lib/sync-engine.js +2 -2
- package/dist/modules/data_sync/lib/sync-engine.js.map +2 -2
- package/dist/modules/data_sync/lib/syncRunStatus.js +30 -0
- package/dist/modules/data_sync/lib/syncRunStatus.js.map +7 -0
- package/dist/modules/data_sync/workers/sync-scheduled.js +13 -14
- package/dist/modules/data_sync/workers/sync-scheduled.js.map +2 -2
- package/dist/modules/directory/components/TenantSelect.js +29 -16
- package/dist/modules/directory/components/TenantSelect.js.map +2 -2
- package/dist/modules/entities/api/records.js +6 -0
- package/dist/modules/entities/api/records.js.map +2 -2
- package/dist/modules/feature_toggles/api/global/route.js +17 -15
- package/dist/modules/feature_toggles/api/global/route.js.map +2 -2
- package/dist/modules/feature_toggles/api/openapi.js +2 -2
- package/dist/modules/feature_toggles/api/openapi.js.map +2 -2
- package/dist/modules/feature_toggles/commands/global.js +14 -0
- package/dist/modules/feature_toggles/commands/global.js.map +2 -2
- package/dist/modules/inbox_ops/backend/inbox-ops/page.js +6 -0
- package/dist/modules/inbox_ops/backend/inbox-ops/page.js.map +2 -2
- package/dist/modules/messages/components/message-detail/hooks/useMessageDetails.js +3 -1
- package/dist/modules/messages/components/message-detail/hooks/useMessageDetails.js.map +2 -2
- package/dist/modules/messages/components/message-detail/hooks/useMessageDetailsActions.js +12 -5
- package/dist/modules/messages/components/message-detail/hooks/useMessageDetailsActions.js.map +2 -2
- package/dist/modules/messages/components/message-detail/hooks/useMessageDetailsQueries.js +10 -1
- package/dist/modules/messages/components/message-detail/hooks/useMessageDetailsQueries.js.map +2 -2
- package/dist/modules/payment_gateways/api/cancel/route.js +5 -0
- package/dist/modules/payment_gateways/api/cancel/route.js.map +2 -2
- package/dist/modules/payment_gateways/api/capture/route.js +5 -0
- package/dist/modules/payment_gateways/api/capture/route.js.map +2 -2
- package/dist/modules/payment_gateways/api/refund/route.js +5 -0
- package/dist/modules/payment_gateways/api/refund/route.js.map +2 -2
- package/dist/modules/payment_gateways/lib/gateway-service.js +52 -25
- package/dist/modules/payment_gateways/lib/gateway-service.js.map +2 -2
- package/dist/modules/payment_gateways/lib/status-machine.js +13 -0
- package/dist/modules/payment_gateways/lib/status-machine.js.map +2 -2
- package/dist/modules/perspectives/api/[tableId]/route.js +8 -2
- package/dist/modules/perspectives/api/[tableId]/route.js.map +2 -2
- package/dist/modules/perspectives/data/entities.js +33 -3
- package/dist/modules/perspectives/data/entities.js.map +2 -2
- package/dist/modules/perspectives/migrations/Migration20260619120000_perspectives_live_row_uniqueness.js +63 -0
- package/dist/modules/perspectives/migrations/Migration20260619120000_perspectives_live_row_uniqueness.js.map +7 -0
- package/dist/modules/perspectives/services/perspectiveService.js +17 -0
- package/dist/modules/perspectives/services/perspectiveService.js.map +2 -2
- package/dist/modules/planner/commands/availability-weekly.js +33 -1
- package/dist/modules/planner/commands/availability-weekly.js.map +2 -2
- package/dist/modules/planner/components/AvailabilityRulesEditor.js +14 -2
- package/dist/modules/planner/components/AvailabilityRulesEditor.js.map +2 -2
- package/dist/modules/portal/frontend/[orgSlug]/portal/dashboard/hiddenWidgetsStorage.js +35 -0
- package/dist/modules/portal/frontend/[orgSlug]/portal/dashboard/hiddenWidgetsStorage.js.map +7 -0
- package/dist/modules/portal/frontend/[orgSlug]/portal/dashboard/page.js +18 -19
- package/dist/modules/portal/frontend/[orgSlug]/portal/dashboard/page.js.map +2 -2
- package/dist/modules/progress/api/active/route.js +1 -1
- package/dist/modules/progress/api/active/route.js.map +2 -2
- package/dist/modules/progress/lib/progressService.js.map +1 -1
- package/dist/modules/progress/lib/progressServiceImpl.js +33 -8
- package/dist/modules/progress/lib/progressServiceImpl.js.map +2 -2
- package/dist/modules/query_index/api/status.js +19 -24
- package/dist/modules/query_index/api/status.js.map +3 -3
- package/dist/modules/query_index/lib/coverage.js +35 -0
- package/dist/modules/query_index/lib/coverage.js.map +2 -2
- package/dist/modules/query_index/lib/engine.js +11 -6
- package/dist/modules/query_index/lib/engine.js.map +2 -2
- package/dist/modules/resources/api/resource-types.js +5 -37
- package/dist/modules/resources/api/resource-types.js.map +2 -2
- package/dist/modules/resources/api/resourceTypeCounts.js +35 -0
- package/dist/modules/resources/api/resourceTypeCounts.js.map +7 -0
- package/dist/modules/resources/backend/resources/resource-types/[id]/edit/page.js +1 -1
- package/dist/modules/resources/backend/resources/resource-types/[id]/edit/page.js.map +1 -1
- package/dist/modules/resources/backend/resources/resource-types/page.js +2 -1
- package/dist/modules/resources/backend/resources/resource-types/page.js.map +2 -2
- package/dist/modules/sales/components/documents/ReturnDialog.js +4 -1
- package/dist/modules/sales/components/documents/ReturnDialog.js.map +2 -2
- package/dist/modules/sales/components/documents/ShipmentDialog.js +17 -2
- package/dist/modules/sales/components/documents/ShipmentDialog.js.map +2 -2
- package/dist/modules/sales/di.js +4 -0
- package/dist/modules/sales/di.js.map +2 -2
- package/dist/modules/sales/services/salesOrderService.js +26 -0
- package/dist/modules/sales/services/salesOrderService.js.map +7 -0
- package/dist/modules/shipping_carriers/api/shipments/route.js +8 -0
- package/dist/modules/shipping_carriers/api/shipments/route.js.map +2 -2
- package/dist/modules/shipping_carriers/data/entities.js +38 -0
- package/dist/modules/shipping_carriers/data/entities.js.map +2 -2
- package/dist/modules/shipping_carriers/data/validators.js +2 -1
- package/dist/modules/shipping_carriers/data/validators.js.map +2 -2
- package/dist/modules/shipping_carriers/lib/shipment-idempotency.js +76 -0
- package/dist/modules/shipping_carriers/lib/shipment-idempotency.js.map +7 -0
- package/dist/modules/shipping_carriers/lib/shipment-wizard/hooks/shipmentApi.js +2 -1
- package/dist/modules/shipping_carriers/lib/shipment-wizard/hooks/shipmentApi.js.map +2 -2
- package/dist/modules/shipping_carriers/lib/shipment-wizard/hooks/useShipmentWizard.js +20 -2
- package/dist/modules/shipping_carriers/lib/shipment-wizard/hooks/useShipmentWizard.js.map +2 -2
- package/dist/modules/shipping_carriers/lib/shipping-service.js +82 -43
- package/dist/modules/shipping_carriers/lib/shipping-service.js.map +2 -2
- package/dist/modules/shipping_carriers/migrations/Migration20260618173616_shipping_carriers.js +14 -0
- package/dist/modules/shipping_carriers/migrations/Migration20260618173616_shipping_carriers.js.map +7 -0
- package/dist/modules/staff/lib/timesheets-ui/ListView.js +15 -6
- package/dist/modules/staff/lib/timesheets-ui/ListView.js.map +2 -2
- package/dist/modules/translations/components/TranslationManager.js +9 -18
- package/dist/modules/translations/components/TranslationManager.js.map +2 -2
- package/generated/entities/carrier_shipment_idempotency_key/index.ts +8 -0
- package/generated/entities.ids.generated.ts +2 -1
- package/generated/entity-fields-registry.ts +10 -0
- package/package.json +9 -8
- package/src/modules/catalog/lib/productResolution.ts +75 -0
- package/src/modules/communication_channels/workers/channel-import-history.ts +1 -1
- package/src/modules/currencies/api/fetch-configs/route.ts +36 -0
- package/src/modules/currencies/components/CurrencyFetchingConfig.tsx +20 -17
- package/src/modules/currencies/i18n/de.json +2 -1
- package/src/modules/currencies/i18n/en.json +2 -1
- package/src/modules/currencies/i18n/es.json +2 -1
- package/src/modules/currencies/i18n/pl.json +2 -1
- package/src/modules/customers/api/companies/[id]/route.ts +214 -189
- package/src/modules/customers/api/people/[id]/route.ts +106 -80
- package/src/modules/customers/backend/config/customers/pipeline-stages/page.tsx +19 -12
- package/src/modules/customers/components/detail/ActivitiesAddNewMenu.tsx +8 -6
- package/src/modules/customers/components/detail/AiActionChips.tsx +6 -3
- package/src/modules/customers/components/detail/schedule/ParticipantsField.tsx +16 -18
- package/src/modules/data_sync/api/runs/[id]/cancel.ts +1 -1
- package/src/modules/data_sync/backend/data-sync/page.tsx +11 -58
- package/src/modules/data_sync/backend/data-sync/runs/[id]/page.tsx +13 -20
- package/src/modules/data_sync/components/IntegrationScheduleTab.tsx +10 -8
- package/src/modules/data_sync/lib/id-mapping.ts +58 -1
- package/src/modules/data_sync/lib/sync-engine.ts +2 -2
- package/src/modules/data_sync/lib/syncRunStatus.ts +39 -0
- package/src/modules/data_sync/workers/sync-scheduled.ts +13 -14
- package/src/modules/directory/components/TenantSelect.tsx +33 -16
- package/src/modules/entities/api/records.ts +14 -0
- package/src/modules/feature_toggles/api/global/route.ts +16 -14
- package/src/modules/feature_toggles/api/openapi.ts +2 -2
- package/src/modules/feature_toggles/commands/global.ts +15 -0
- package/src/modules/inbox_ops/backend/inbox-ops/page.tsx +7 -0
- package/src/modules/messages/components/message-detail/hooks/useMessageDetails.ts +2 -0
- package/src/modules/messages/components/message-detail/hooks/useMessageDetailsActions.ts +14 -4
- package/src/modules/messages/components/message-detail/hooks/useMessageDetailsQueries.ts +15 -1
- package/src/modules/payment_gateways/api/cancel/route.ts +5 -0
- package/src/modules/payment_gateways/api/capture/route.ts +5 -0
- package/src/modules/payment_gateways/api/refund/route.ts +5 -0
- package/src/modules/payment_gateways/lib/gateway-service.ts +58 -25
- package/src/modules/payment_gateways/lib/status-machine.ts +16 -0
- package/src/modules/perspectives/api/[tableId]/route.ts +8 -2
- package/src/modules/perspectives/data/entities.ts +43 -3
- package/src/modules/perspectives/migrations/.snapshot-open-mercato.json +69 -25
- package/src/modules/perspectives/migrations/Migration20260619120000_perspectives_live_row_uniqueness.ts +65 -0
- package/src/modules/perspectives/services/perspectiveService.ts +17 -0
- package/src/modules/planner/commands/availability-weekly.ts +48 -1
- package/src/modules/planner/components/AvailabilityRulesEditor.tsx +31 -10
- package/src/modules/portal/frontend/[orgSlug]/portal/dashboard/hiddenWidgetsStorage.ts +36 -0
- package/src/modules/portal/frontend/[orgSlug]/portal/dashboard/page.tsx +22 -23
- package/src/modules/progress/api/active/route.ts +1 -1
- package/src/modules/progress/lib/progressService.ts +2 -2
- package/src/modules/progress/lib/progressServiceImpl.ts +33 -8
- package/src/modules/query_index/api/status.ts +28 -32
- package/src/modules/query_index/lib/coverage.ts +63 -0
- package/src/modules/query_index/lib/engine.ts +37 -24
- package/src/modules/resources/api/resource-types.ts +4 -44
- package/src/modules/resources/api/resourceTypeCounts.ts +74 -0
- package/src/modules/resources/backend/resources/resource-types/[id]/edit/page.tsx +1 -1
- package/src/modules/resources/backend/resources/resource-types/page.tsx +1 -0
- package/src/modules/sales/components/documents/ReturnDialog.tsx +4 -1
- package/src/modules/sales/components/documents/ShipmentDialog.tsx +20 -2
- package/src/modules/sales/di.ts +6 -0
- package/src/modules/sales/services/salesOrderService.ts +38 -0
- package/src/modules/shipping_carriers/api/shipments/route.ts +8 -0
- package/src/modules/shipping_carriers/data/entities.ts +33 -0
- package/src/modules/shipping_carriers/data/validators.ts +1 -0
- package/src/modules/shipping_carriers/i18n/de.json +1 -0
- package/src/modules/shipping_carriers/i18n/en.json +1 -0
- package/src/modules/shipping_carriers/i18n/es.json +1 -0
- package/src/modules/shipping_carriers/i18n/pl.json +1 -0
- package/src/modules/shipping_carriers/lib/shipment-idempotency.ts +119 -0
- package/src/modules/shipping_carriers/lib/shipment-wizard/hooks/shipmentApi.ts +3 -1
- package/src/modules/shipping_carriers/lib/shipment-wizard/hooks/useShipmentWizard.ts +24 -2
- package/src/modules/shipping_carriers/lib/shipping-service.ts +87 -43
- package/src/modules/shipping_carriers/migrations/.snapshot-open-mercato.json +197 -0
- package/src/modules/shipping_carriers/migrations/Migration20260618173616_shipping_carriers.ts +14 -0
- package/src/modules/staff/i18n/de.json +1 -0
- package/src/modules/staff/i18n/en.json +1 -0
- package/src/modules/staff/i18n/es.json +1 -0
- package/src/modules/staff/i18n/pl.json +1 -0
- package/src/modules/staff/lib/timesheets-ui/ListView.tsx +15 -7
- package/src/modules/translations/components/TranslationManager.tsx +17 -24
- package/src/modules/translations/i18n/de.json +1 -0
- package/src/modules/translations/i18n/en.json +1 -0
- package/src/modules/translations/i18n/es.json +1 -0
- package/src/modules/translations/i18n/pl.json +1 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/modules/planner/commands/availability-weekly.ts"],
|
|
4
|
-
"sourcesContent": ["import type { CommandHandler } from '@open-mercato/shared/lib/commands'\nimport { registerCommand } from '@open-mercato/shared/lib/commands'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { parseAvailabilityRuleWindow } from '@open-mercato/core/modules/planner/lib/availabilitySchedule'\nimport { PlannerAvailabilityRule } from '../data/entities'\nimport {\n plannerAvailabilityWeeklyReplaceSchema,\n type PlannerAvailabilityWeeklyReplaceInput,\n} from '../data/validators'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport type { PlannerAvailabilityKind, PlannerAvailabilitySubjectType } from '../data/entities'\nimport { ensureOrganizationScope, ensureTenantScope, extractUndoPayload } from './shared'\n\nconst AVAILABILITY_RULE_RESOURCE_KIND = 'planner.availability.rule'\n\nconst DAY_CODES = ['SU', 'MO', 'TU', 'WE', 'TH', 'FR', 'SA']\n\ntype AvailabilityRuleSnapshot = {\n id: string\n tenantId: string\n organizationId: string\n subjectType: PlannerAvailabilitySubjectType\n subjectId: string\n timezone: string\n rrule: string\n exdates: string[]\n kind: PlannerAvailabilityKind\n note: string | null\n deletedAt: Date | null\n}\n\ntype WeeklyUndoPayload = {\n before: AvailabilityRuleSnapshot[]\n after: AvailabilityRuleSnapshot[]\n}\n\nfunction parseTimeInput(value: string): { hours: number; minutes: number } | null {\n const [hours, minutes] = value.split(':').map((part) => Number(part))\n if (!Number.isFinite(hours) || !Number.isFinite(minutes)) return null\n if (hours < 0 || hours > 23 || minutes < 0 || minutes > 59) return null\n return { hours, minutes }\n}\n\nfunction toDateForWeekday(weekday: number, time: string): Date | null {\n const parsed = parseTimeInput(time)\n if (!parsed) return null\n const now = new Date()\n const base = new Date(now.getFullYear(), now.getMonth(), now.getDate())\n const diff = (weekday - base.getDay() + 7) % 7\n const target = new Date(base.getTime() + diff * 24 * 60 * 60 * 1000)\n target.setHours(parsed.hours, parsed.minutes, 0, 0)\n return target\n}\n\nfunction formatDuration(minutes: number): string {\n const clamped = Math.max(1, minutes)\n const hours = Math.floor(clamped / 60)\n const mins = clamped % 60\n if (hours > 0 && mins > 0) return `PT${hours}H${mins}M`\n if (hours > 0) return `PT${hours}H`\n return `PT${mins}M`\n}\n\nfunction buildWeeklyRrule(start: Date, end: Date): string {\n const dtStart = start.toISOString().replace(/[-:]/g, '').split('.')[0] + 'Z'\n const durationMinutes = Math.max(1, Math.round((end.getTime() - start.getTime()) / 60000))\n const duration = formatDuration(durationMinutes)\n const dayCode = DAY_CODES[start.getDay()] ?? 'MO'\n return `DTSTART:${dtStart}\\nDURATION:${duration}\\nRRULE:FREQ=WEEKLY;BYDAY=${dayCode}`\n}\n\nfunction toAvailabilityRuleSnapshot(record: PlannerAvailabilityRule): AvailabilityRuleSnapshot {\n return {\n id: record.id,\n tenantId: record.tenantId,\n organizationId: record.organizationId,\n subjectType: record.subjectType,\n subjectId: record.subjectId,\n timezone: record.timezone,\n rrule: record.rrule,\n exdates: [...(record.exdates ?? [])],\n kind: record.kind,\n note: record.note ?? null,\n deletedAt: record.deletedAt ?? null,\n }\n}\n\nasync function loadWeeklySnapshots(\n em: EntityManager,\n params: {\n tenantId: string\n organizationId: string\n subjectType: PlannerAvailabilitySubjectType\n subjectId: string\n }\n): Promise<AvailabilityRuleSnapshot[]> {\n const existing = await em.find(PlannerAvailabilityRule, {\n tenantId: params.tenantId,\n organizationId: params.organizationId,\n subjectType: params.subjectType,\n subjectId: params.subjectId,\n deletedAt: null,\n })\n return existing\n .filter((rule) => {\n const repeat = parseAvailabilityRuleWindow(rule).repeat\n return repeat === 'weekly' || repeat === 'daily'\n })\n .map(toAvailabilityRuleSnapshot)\n}\n\nasync function restoreAvailabilityRuleFromSnapshot(em: EntityManager, snapshot: AvailabilityRuleSnapshot): Promise<void> {\n let record = await em.findOne(PlannerAvailabilityRule, { id: snapshot.id })\n if (!record) {\n record = em.create(PlannerAvailabilityRule, {\n id: snapshot.id,\n tenantId: snapshot.tenantId,\n organizationId: snapshot.organizationId,\n subjectType: snapshot.subjectType,\n subjectId: snapshot.subjectId,\n timezone: snapshot.timezone,\n rrule: snapshot.rrule,\n exdates: snapshot.exdates ?? [],\n kind: snapshot.kind ?? 'availability',\n note: snapshot.note ?? null,\n createdAt: new Date(),\n updatedAt: new Date(),\n deletedAt: snapshot.deletedAt ?? null,\n })\n em.persist(record)\n } else {\n record.subjectType = snapshot.subjectType\n record.subjectId = snapshot.subjectId\n record.timezone = snapshot.timezone\n record.rrule = snapshot.rrule\n record.exdates = snapshot.exdates ?? []\n record.kind = snapshot.kind ?? 'availability'\n record.note = snapshot.note ?? null\n record.deletedAt = snapshot.deletedAt ?? null\n }\n}\n\nconst replaceWeeklyAvailabilityCommand: CommandHandler<PlannerAvailabilityWeeklyReplaceInput, { ok: true }> = {\n id: 'planner.availability.weekly.replace',\n async prepare(input, ctx) {\n const parsed = plannerAvailabilityWeeklyReplaceSchema.parse(input)\n ensureTenantScope(ctx, parsed.tenantId)\n ensureOrganizationScope(ctx, parsed.organizationId)\n const em = (ctx.container.resolve('em') as EntityManager)\n const before = await loadWeeklySnapshots(em, {\n tenantId: parsed.tenantId,\n organizationId: parsed.organizationId,\n subjectType: parsed.subjectType,\n subjectId: parsed.subjectId,\n })\n return { before }\n },\n async execute(input, ctx) {\n const parsed = plannerAvailabilityWeeklyReplaceSchema.parse(input)\n ensureTenantScope(ctx, parsed.tenantId)\n ensureOrganizationScope(ctx, parsed.organizationId)\n\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const now = new Date()\n\n await em.transactional(async (trx) => {\n const existing = await trx.find(PlannerAvailabilityRule, {\n tenantId: parsed.tenantId,\n organizationId: parsed.organizationId,\n subjectType: parsed.subjectType,\n subjectId: parsed.subjectId,\n deletedAt: null,\n })\n\n const toDelete = existing.filter((rule) => {\n const repeat = parseAvailabilityRuleWindow(rule).repeat\n return repeat === 'weekly' || repeat === 'daily'\n })\n\n toDelete.forEach((rule) => {\n rule.deletedAt = now\n rule.updatedAt = now\n })\n\n if (toDelete.length) {\n trx.persist(toDelete)\n }\n\n parsed.windows.forEach((window) => {\n const start = toDateForWeekday(window.weekday, window.start)\n const end = toDateForWeekday(window.weekday, window.end)\n if (!start || !end || start >= end) return\n const rrule = buildWeeklyRrule(start, end)\n const record = trx.create(PlannerAvailabilityRule, {\n tenantId: parsed.tenantId,\n organizationId: parsed.organizationId,\n subjectType: parsed.subjectType,\n subjectId: parsed.subjectId,\n timezone: parsed.timezone,\n rrule,\n exdates: [],\n kind: 'availability',\n note: null,\n createdAt: now,\n updatedAt: now,\n })\n trx.persist(record)\n })\n\n await trx.flush()\n })\n\n return { ok: true }\n },\n buildLog: async ({ input, snapshots, ctx }) => {\n const parsed = plannerAvailabilityWeeklyReplaceSchema.parse(input)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const after = await loadWeeklySnapshots(em, {\n tenantId: parsed.tenantId,\n organizationId: parsed.organizationId,\n subjectType: parsed.subjectType,\n subjectId: parsed.subjectId,\n })\n const before = (snapshots.before as AvailabilityRuleSnapshot[] | undefined) ?? []\n const { translate } = await resolveTranslations()\n return {\n actionLabel: translate('planner.audit.availability.weekly.replace', 'Replace weekly availability'),\n resourceKind: AVAILABILITY_RULE_RESOURCE_KIND,\n resourceId: null,\n tenantId: parsed.tenantId,\n organizationId: parsed.organizationId,\n snapshotBefore: before,\n snapshotAfter: after,\n payload: {\n undo: {\n before,\n after,\n } satisfies WeeklyUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<WeeklyUndoPayload>(logEntry)\n const before = payload?.before ?? []\n const after = payload?.after ?? []\n if (!before.length && !after.length) return\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n await em.transactional(async (trx) => {\n if (after.length) {\n const ids = after.map((rule) => rule.id)\n const records = await trx.find(PlannerAvailabilityRule, { id: { $in: ids } })\n records.forEach((record) => {\n record.deletedAt = new Date()\n })\n if (records.length) trx.persist(records)\n }\n\n for (const snapshot of before) {\n await restoreAvailabilityRuleFromSnapshot(trx, { ...snapshot, deletedAt: null })\n }\n\n await trx.flush()\n })\n },\n}\n\nregisterCommand(replaceWeeklyAvailabilityCommand)\n"],
|
|
5
|
-
"mappings": "AACA,SAAS,uBAAuB;AAEhC,SAAS,mCAAmC;AAC5C,SAAS
|
|
4
|
+
"sourcesContent": ["import type { CommandHandler } from '@open-mercato/shared/lib/commands'\nimport { registerCommand } from '@open-mercato/shared/lib/commands'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { parseAvailabilityRuleWindow } from '@open-mercato/core/modules/planner/lib/availabilitySchedule'\nimport { PlannerAvailabilityRule, PlannerAvailabilityRuleSet } from '../data/entities'\nimport {\n plannerAvailabilityWeeklyReplaceSchema,\n type PlannerAvailabilityWeeklyReplaceInput,\n} from '../data/validators'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport type { PlannerAvailabilityKind, PlannerAvailabilitySubjectType } from '../data/entities'\nimport {\n enforceCommandOptimisticLock,\n enforceRecordGoneIsConflict,\n} from '@open-mercato/shared/lib/crud/optimistic-lock-command'\nimport { ensureOrganizationScope, ensureTenantScope, extractUndoPayload } from './shared'\n\nconst AVAILABILITY_RULE_RESOURCE_KIND = 'planner.availability.rule'\n\n// Canonical resource kind for the parent rule set, matching the tag the CRUD\n// factory derives for `planner.availability-rule-sets.*` commands. Weekly\n// replace mutates the rule set's child `availability_rules`, so the parent is\n// the optimistic-lock consistency boundary (document-aggregate pattern).\nconst AVAILABILITY_RULE_SET_RESOURCE_KIND = 'planner.availability.rule.set'\n\nconst DAY_CODES = ['SU', 'MO', 'TU', 'WE', 'TH', 'FR', 'SA']\n\ntype AvailabilityRuleSnapshot = {\n id: string\n tenantId: string\n organizationId: string\n subjectType: PlannerAvailabilitySubjectType\n subjectId: string\n timezone: string\n rrule: string\n exdates: string[]\n kind: PlannerAvailabilityKind\n note: string | null\n deletedAt: Date | null\n}\n\ntype WeeklyUndoPayload = {\n before: AvailabilityRuleSnapshot[]\n after: AvailabilityRuleSnapshot[]\n}\n\nfunction parseTimeInput(value: string): { hours: number; minutes: number } | null {\n const [hours, minutes] = value.split(':').map((part) => Number(part))\n if (!Number.isFinite(hours) || !Number.isFinite(minutes)) return null\n if (hours < 0 || hours > 23 || minutes < 0 || minutes > 59) return null\n return { hours, minutes }\n}\n\nfunction toDateForWeekday(weekday: number, time: string): Date | null {\n const parsed = parseTimeInput(time)\n if (!parsed) return null\n const now = new Date()\n const base = new Date(now.getFullYear(), now.getMonth(), now.getDate())\n const diff = (weekday - base.getDay() + 7) % 7\n const target = new Date(base.getTime() + diff * 24 * 60 * 60 * 1000)\n target.setHours(parsed.hours, parsed.minutes, 0, 0)\n return target\n}\n\nfunction formatDuration(minutes: number): string {\n const clamped = Math.max(1, minutes)\n const hours = Math.floor(clamped / 60)\n const mins = clamped % 60\n if (hours > 0 && mins > 0) return `PT${hours}H${mins}M`\n if (hours > 0) return `PT${hours}H`\n return `PT${mins}M`\n}\n\nfunction buildWeeklyRrule(start: Date, end: Date): string {\n const dtStart = start.toISOString().replace(/[-:]/g, '').split('.')[0] + 'Z'\n const durationMinutes = Math.max(1, Math.round((end.getTime() - start.getTime()) / 60000))\n const duration = formatDuration(durationMinutes)\n const dayCode = DAY_CODES[start.getDay()] ?? 'MO'\n return `DTSTART:${dtStart}\\nDURATION:${duration}\\nRRULE:FREQ=WEEKLY;BYDAY=${dayCode}`\n}\n\nfunction toAvailabilityRuleSnapshot(record: PlannerAvailabilityRule): AvailabilityRuleSnapshot {\n return {\n id: record.id,\n tenantId: record.tenantId,\n organizationId: record.organizationId,\n subjectType: record.subjectType,\n subjectId: record.subjectId,\n timezone: record.timezone,\n rrule: record.rrule,\n exdates: [...(record.exdates ?? [])],\n kind: record.kind,\n note: record.note ?? null,\n deletedAt: record.deletedAt ?? null,\n }\n}\n\nasync function loadWeeklySnapshots(\n em: EntityManager,\n params: {\n tenantId: string\n organizationId: string\n subjectType: PlannerAvailabilitySubjectType\n subjectId: string\n }\n): Promise<AvailabilityRuleSnapshot[]> {\n const existing = await em.find(PlannerAvailabilityRule, {\n tenantId: params.tenantId,\n organizationId: params.organizationId,\n subjectType: params.subjectType,\n subjectId: params.subjectId,\n deletedAt: null,\n })\n return existing\n .filter((rule) => {\n const repeat = parseAvailabilityRuleWindow(rule).repeat\n return repeat === 'weekly' || repeat === 'daily'\n })\n .map(toAvailabilityRuleSnapshot)\n}\n\nasync function restoreAvailabilityRuleFromSnapshot(em: EntityManager, snapshot: AvailabilityRuleSnapshot): Promise<void> {\n let record = await em.findOne(PlannerAvailabilityRule, { id: snapshot.id })\n if (!record) {\n record = em.create(PlannerAvailabilityRule, {\n id: snapshot.id,\n tenantId: snapshot.tenantId,\n organizationId: snapshot.organizationId,\n subjectType: snapshot.subjectType,\n subjectId: snapshot.subjectId,\n timezone: snapshot.timezone,\n rrule: snapshot.rrule,\n exdates: snapshot.exdates ?? [],\n kind: snapshot.kind ?? 'availability',\n note: snapshot.note ?? null,\n createdAt: new Date(),\n updatedAt: new Date(),\n deletedAt: snapshot.deletedAt ?? null,\n })\n em.persist(record)\n } else {\n record.subjectType = snapshot.subjectType\n record.subjectId = snapshot.subjectId\n record.timezone = snapshot.timezone\n record.rrule = snapshot.rrule\n record.exdates = snapshot.exdates ?? []\n record.kind = snapshot.kind ?? 'availability'\n record.note = snapshot.note ?? null\n record.deletedAt = snapshot.deletedAt ?? null\n }\n}\n\nconst replaceWeeklyAvailabilityCommand: CommandHandler<PlannerAvailabilityWeeklyReplaceInput, { ok: true }> = {\n id: 'planner.availability.weekly.replace',\n async prepare(input, ctx) {\n const parsed = plannerAvailabilityWeeklyReplaceSchema.parse(input)\n ensureTenantScope(ctx, parsed.tenantId)\n ensureOrganizationScope(ctx, parsed.organizationId)\n const em = (ctx.container.resolve('em') as EntityManager)\n const before = await loadWeeklySnapshots(em, {\n tenantId: parsed.tenantId,\n organizationId: parsed.organizationId,\n subjectType: parsed.subjectType,\n subjectId: parsed.subjectId,\n })\n return { before }\n },\n async execute(input, ctx) {\n const parsed = plannerAvailabilityWeeklyReplaceSchema.parse(input)\n ensureTenantScope(ctx, parsed.tenantId)\n ensureOrganizationScope(ctx, parsed.organizationId)\n\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const now = new Date()\n\n await em.transactional(async (trx) => {\n // The weekly rules of a rule set are a sub-resource of that rule set: the\n // parent is the optimistic-lock consistency boundary. Guard the parent's\n // version (so a stale weekly save loses to a concurrent rule-set\n // change/delete) and bump its `updated_at` after the replace (so a\n // concurrent rule-set delete/update with a stale token conflicts). See #2927.\n let ruleSet: PlannerAvailabilityRuleSet | null = null\n if (parsed.subjectType === 'ruleset') {\n ruleSet = await trx.findOne(PlannerAvailabilityRuleSet, {\n id: parsed.subjectId,\n tenantId: parsed.tenantId,\n organizationId: parsed.organizationId,\n deletedAt: null,\n })\n if (ruleSet) {\n enforceCommandOptimisticLock({\n resourceKind: AVAILABILITY_RULE_SET_RESOURCE_KIND,\n resourceId: ruleSet.id,\n current: ruleSet.updatedAt,\n request: ctx.request ?? null,\n })\n } else {\n // The rule set was deleted concurrently. When the client opted into\n // optimistic locking, surface the unified conflict instead of\n // silently writing orphan rules; otherwise preserve legacy behavior.\n enforceRecordGoneIsConflict({\n resourceKind: AVAILABILITY_RULE_SET_RESOURCE_KIND,\n resourceId: parsed.subjectId,\n request: ctx.request ?? null,\n })\n }\n }\n\n const existing = await trx.find(PlannerAvailabilityRule, {\n tenantId: parsed.tenantId,\n organizationId: parsed.organizationId,\n subjectType: parsed.subjectType,\n subjectId: parsed.subjectId,\n deletedAt: null,\n })\n\n const toDelete = existing.filter((rule) => {\n const repeat = parseAvailabilityRuleWindow(rule).repeat\n return repeat === 'weekly' || repeat === 'daily'\n })\n\n toDelete.forEach((rule) => {\n rule.deletedAt = now\n rule.updatedAt = now\n })\n\n if (toDelete.length) {\n trx.persist(toDelete)\n }\n\n parsed.windows.forEach((window) => {\n const start = toDateForWeekday(window.weekday, window.start)\n const end = toDateForWeekday(window.weekday, window.end)\n if (!start || !end || start >= end) return\n const rrule = buildWeeklyRrule(start, end)\n const record = trx.create(PlannerAvailabilityRule, {\n tenantId: parsed.tenantId,\n organizationId: parsed.organizationId,\n subjectType: parsed.subjectType,\n subjectId: parsed.subjectId,\n timezone: parsed.timezone,\n rrule,\n exdates: [],\n kind: 'availability',\n note: null,\n createdAt: now,\n updatedAt: now,\n })\n trx.persist(record)\n })\n\n if (ruleSet) {\n ruleSet.updatedAt = now\n trx.persist(ruleSet)\n }\n\n await trx.flush()\n })\n\n return { ok: true }\n },\n buildLog: async ({ input, snapshots, ctx }) => {\n const parsed = plannerAvailabilityWeeklyReplaceSchema.parse(input)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const after = await loadWeeklySnapshots(em, {\n tenantId: parsed.tenantId,\n organizationId: parsed.organizationId,\n subjectType: parsed.subjectType,\n subjectId: parsed.subjectId,\n })\n const before = (snapshots.before as AvailabilityRuleSnapshot[] | undefined) ?? []\n const { translate } = await resolveTranslations()\n return {\n actionLabel: translate('planner.audit.availability.weekly.replace', 'Replace weekly availability'),\n resourceKind: AVAILABILITY_RULE_RESOURCE_KIND,\n resourceId: null,\n tenantId: parsed.tenantId,\n organizationId: parsed.organizationId,\n snapshotBefore: before,\n snapshotAfter: after,\n payload: {\n undo: {\n before,\n after,\n } satisfies WeeklyUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<WeeklyUndoPayload>(logEntry)\n const before = payload?.before ?? []\n const after = payload?.after ?? []\n if (!before.length && !after.length) return\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n await em.transactional(async (trx) => {\n if (after.length) {\n const ids = after.map((rule) => rule.id)\n const records = await trx.find(PlannerAvailabilityRule, { id: { $in: ids } })\n records.forEach((record) => {\n record.deletedAt = new Date()\n })\n if (records.length) trx.persist(records)\n }\n\n for (const snapshot of before) {\n await restoreAvailabilityRuleFromSnapshot(trx, { ...snapshot, deletedAt: null })\n }\n\n await trx.flush()\n })\n },\n}\n\nregisterCommand(replaceWeeklyAvailabilityCommand)\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -719,12 +719,13 @@ function AvailabilityRulesEditor({
|
|
|
719
719
|
const subjectIdForRules = usingRuleSet ? effectiveRulesetId ?? "" : subjectId;
|
|
720
720
|
if (!subjectIdForRules) return;
|
|
721
721
|
if (weeklyHasErrors) return;
|
|
722
|
+
const parentRuleSet = subjectForRules === "ruleset" ? ruleSets.find((entry) => entry.id === subjectIdForRules) ?? null : null;
|
|
722
723
|
const shouldSkipRefresh = Boolean(options?.skipRefresh);
|
|
723
724
|
setIsWeeklyAutoSaving(options?.silentSuccess === true);
|
|
724
725
|
try {
|
|
725
726
|
const windows = buildWeeklyPayload(normalizeWeeklyWindows(weeklyWindowsRef.current));
|
|
726
727
|
await runMutation({
|
|
727
|
-
operation: () => apiCallOrThrow("/api/planner/availability-weekly", {
|
|
728
|
+
operation: () => withOptimisticLockForRuleSet(parentRuleSet, () => apiCallOrThrow("/api/planner/availability-weekly", {
|
|
728
729
|
method: "POST",
|
|
729
730
|
headers: { "Content-Type": "application/json" },
|
|
730
731
|
body: JSON.stringify({
|
|
@@ -733,7 +734,7 @@ function AvailabilityRulesEditor({
|
|
|
733
734
|
timezone,
|
|
734
735
|
windows
|
|
735
736
|
})
|
|
736
|
-
}, { errorMessage: listLabels.saveWeeklyError }),
|
|
737
|
+
}, { errorMessage: listLabels.saveWeeklyError })),
|
|
737
738
|
context: mutationContext,
|
|
738
739
|
mutationPayload: { action: "save-weekly", subjectType: subjectForRules, subjectId: subjectIdForRules }
|
|
739
740
|
});
|
|
@@ -746,7 +747,13 @@ function AvailabilityRulesEditor({
|
|
|
746
747
|
await refreshAvailability();
|
|
747
748
|
await refreshRuleSetRules();
|
|
748
749
|
}
|
|
750
|
+
if (parentRuleSet) {
|
|
751
|
+
await refreshRuleSets();
|
|
752
|
+
}
|
|
749
753
|
} catch (error2) {
|
|
754
|
+
if (surfaceRecordConflict(error2, t)) {
|
|
755
|
+
return;
|
|
756
|
+
}
|
|
750
757
|
const message = error2 instanceof Error ? error2.message : listLabels.saveWeeklyError;
|
|
751
758
|
flash(message, "error");
|
|
752
759
|
} finally {
|
|
@@ -757,9 +764,12 @@ function AvailabilityRulesEditor({
|
|
|
757
764
|
listLabels.saveWeeklySuccess,
|
|
758
765
|
refreshAvailability,
|
|
759
766
|
refreshRuleSetRules,
|
|
767
|
+
refreshRuleSets,
|
|
768
|
+
ruleSets,
|
|
760
769
|
effectiveRulesetId,
|
|
761
770
|
subjectId,
|
|
762
771
|
subjectType,
|
|
772
|
+
t,
|
|
763
773
|
timezone,
|
|
764
774
|
usingRuleSet,
|
|
765
775
|
weeklyHasErrors,
|
|
@@ -1000,6 +1010,7 @@ function AvailabilityRulesEditor({
|
|
|
1000
1010
|
refreshRuleSets,
|
|
1001
1011
|
onSuccess: () => flash(listLabels.ruleSetDeleteSuccess, "success"),
|
|
1002
1012
|
onError: (error2) => {
|
|
1013
|
+
if (surfaceRecordConflict(error2, t)) return;
|
|
1003
1014
|
console.error("planner.availability-rule-sets.delete", error2);
|
|
1004
1015
|
const normalized = normalizeCrudServerError(error2);
|
|
1005
1016
|
flash(normalized.message ?? listLabels.ruleSetDeleteError, "error");
|
|
@@ -1017,6 +1028,7 @@ function AvailabilityRulesEditor({
|
|
|
1017
1028
|
refreshRuleSets,
|
|
1018
1029
|
ruleSets,
|
|
1019
1030
|
rulesetId,
|
|
1031
|
+
t,
|
|
1020
1032
|
mutationContext,
|
|
1021
1033
|
runMutation
|
|
1022
1034
|
]);
|