@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
|
@@ -2,6 +2,15 @@ import { findOneWithDecryption } from "@open-mercato/shared/lib/encryption/find"
|
|
|
2
2
|
import { CarrierShipment } from "../data/entities.js";
|
|
3
3
|
import { emitShippingEvent } from "../events.js";
|
|
4
4
|
import { getShippingAdapter } from "./adapter-registry.js";
|
|
5
|
+
import {
|
|
6
|
+
claimShipmentIdempotency,
|
|
7
|
+
computeShipmentRequestHash,
|
|
8
|
+
findShipmentIdempotencyClaim,
|
|
9
|
+
isShipmentIdempotencyConflictError,
|
|
10
|
+
releaseShipmentIdempotency,
|
|
11
|
+
resolveShipmentIdempotency,
|
|
12
|
+
ShipmentIdempotencyConflictError
|
|
13
|
+
} from "./shipment-idempotency.js";
|
|
5
14
|
import {
|
|
6
15
|
getTerminalShippingEvent,
|
|
7
16
|
isValidShippingTransition,
|
|
@@ -54,49 +63,79 @@ function createShippingCarrierService(deps) {
|
|
|
54
63
|
});
|
|
55
64
|
},
|
|
56
65
|
async createShipment(input) {
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
66
|
+
const scope = { organizationId: input.organizationId, tenantId: input.tenantId };
|
|
67
|
+
let claim = null;
|
|
68
|
+
if (input.idempotencyKey) {
|
|
69
|
+
const { idempotencyKey: _idempotencyKey, ...fingerprint } = input;
|
|
70
|
+
const requestHash = computeShipmentRequestHash(fingerprint);
|
|
71
|
+
const existing = await findShipmentIdempotencyClaim(em, input.idempotencyKey, input.providerKey, scope);
|
|
72
|
+
if (existing) {
|
|
73
|
+
if (existing.requestHash !== requestHash) {
|
|
74
|
+
throw new ShipmentIdempotencyConflictError(input.idempotencyKey);
|
|
75
|
+
}
|
|
76
|
+
if (existing.shipmentId) {
|
|
77
|
+
return findShipmentOrThrow(existing.shipmentId, scope);
|
|
78
|
+
}
|
|
79
|
+
throw new ShipmentIdempotencyConflictError(input.idempotencyKey);
|
|
80
|
+
}
|
|
81
|
+
claim = await claimShipmentIdempotency(em, input.idempotencyKey, input.providerKey, requestHash, scope);
|
|
82
|
+
if (!claim) {
|
|
83
|
+
throw new ShipmentIdempotencyConflictError(input.idempotencyKey);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
let carrierShipmentCreated = false;
|
|
87
|
+
try {
|
|
88
|
+
const { adapter, credentials } = await resolveAdapter(input.providerKey, scope);
|
|
89
|
+
const mergedCredentials = {
|
|
90
|
+
...credentials,
|
|
91
|
+
...input.senderPhone !== void 0 ? { senderPhone: input.senderPhone } : {},
|
|
92
|
+
...input.senderEmail !== void 0 ? { senderEmail: input.senderEmail } : {},
|
|
93
|
+
...input.receiverPhone !== void 0 ? { receiverPhone: input.receiverPhone } : {},
|
|
94
|
+
...input.receiverEmail !== void 0 ? { receiverEmail: input.receiverEmail } : {},
|
|
95
|
+
...input.targetPoint !== void 0 ? { targetPoint: input.targetPoint } : {},
|
|
96
|
+
...input.c2cSendingMethod !== void 0 ? { c2cSendingMethod: input.c2cSendingMethod } : {}
|
|
97
|
+
};
|
|
98
|
+
const created = await adapter.createShipment({
|
|
99
|
+
orderId: input.orderId,
|
|
100
|
+
origin: input.origin,
|
|
101
|
+
destination: input.destination,
|
|
102
|
+
packages: input.packages,
|
|
103
|
+
serviceCode: input.serviceCode,
|
|
104
|
+
credentials: mergedCredentials,
|
|
105
|
+
labelFormat: input.labelFormat
|
|
106
|
+
});
|
|
107
|
+
carrierShipmentCreated = true;
|
|
108
|
+
const shipment = em.create(CarrierShipment, {
|
|
109
|
+
orderId: input.orderId,
|
|
110
|
+
providerKey: input.providerKey,
|
|
111
|
+
carrierShipmentId: created.shipmentId,
|
|
112
|
+
trackingNumber: created.trackingNumber,
|
|
113
|
+
unifiedStatus: "label_created",
|
|
114
|
+
labelUrl: created.labelUrl ?? null,
|
|
115
|
+
labelData: created.labelData ?? null,
|
|
116
|
+
organizationId: input.organizationId,
|
|
117
|
+
tenantId: input.tenantId
|
|
118
|
+
});
|
|
119
|
+
await em.persist(shipment).flush();
|
|
120
|
+
if (claim) {
|
|
121
|
+
await resolveShipmentIdempotency(em, claim, shipment.id);
|
|
122
|
+
}
|
|
123
|
+
await emitShippingEvent("shipping_carriers.shipment.created", {
|
|
124
|
+
shipmentId: shipment.id,
|
|
125
|
+
orderId: input.orderId,
|
|
126
|
+
providerKey: input.providerKey,
|
|
127
|
+
trackingNumber: created.trackingNumber,
|
|
128
|
+
organizationId: input.organizationId,
|
|
129
|
+
tenantId: input.tenantId
|
|
130
|
+
});
|
|
131
|
+
return shipment;
|
|
132
|
+
} catch (error) {
|
|
133
|
+
if (claim && !carrierShipmentCreated && input.idempotencyKey && !isShipmentIdempotencyConflictError(error)) {
|
|
134
|
+
await releaseShipmentIdempotency(em, input.idempotencyKey, input.providerKey, scope).catch(() => {
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
throw error;
|
|
138
|
+
}
|
|
100
139
|
},
|
|
101
140
|
async getTracking(input) {
|
|
102
141
|
const { adapter, credentials } = await resolveAdapter(input.providerKey, {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/modules/shipping_carriers/lib/shipping-service.ts"],
|
|
4
|
-
"sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport type { CredentialsService } from '../../integrations/lib/credentials-service'\nimport { CarrierShipment } from '../data/entities'\nimport { emitShippingEvent } from '../events'\nimport { getShippingAdapter } from './adapter-registry'\nimport type { UnifiedShipmentStatus } from './adapter'\nimport {\n getTerminalShippingEvent,\n isValidShippingTransition,\n ShipmentCancelNotAllowedError,\n syncShipmentStatus,\n TERMINAL_SHIPPING_STATUSES,\n} from './status-sync'\n\nexport function createShippingCarrierService(deps: {\n em: EntityManager\n integrationCredentialsService: CredentialsService\n}) {\n const { em, integrationCredentialsService } = deps\n\n async function findShipmentOrThrow(\n shipmentId: string,\n scope: { organizationId: string; tenantId: string },\n ): Promise<CarrierShipment> {\n const shipment = await findOneWithDecryption(\n em,\n CarrierShipment,\n {\n id: shipmentId,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n },\n undefined,\n scope,\n )\n if (!shipment) {\n throw new Error('Shipment not found')\n }\n return shipment\n }\n\n async function resolveAdapter(providerKey: string, scope: { organizationId: string; tenantId: string }) {\n const adapter = getShippingAdapter(providerKey)\n if (!adapter) throw new Error(`No shipping adapter registered for provider: ${providerKey}`)\n const credentials = await integrationCredentialsService.resolve(`carrier_${providerKey}`, scope) ?? {}\n return { adapter, credentials }\n }\n\n return {\n async calculateRates(input: {\n providerKey: string\n origin: { countryCode: string; postalCode: string; city: string; line1: string; line2?: string }\n destination: { countryCode: string; postalCode: string; city: string; line1: string; line2?: string }\n packages: Array<{ weightKg: number; lengthCm: number; widthCm: number; heightCm: number }>\n receiverPhone?: string\n receiverEmail?: string\n organizationId: string\n tenantId: string\n }) {\n const { adapter, credentials } = await resolveAdapter(input.providerKey, {\n organizationId: input.organizationId,\n tenantId: input.tenantId,\n })\n const mergedCredentials = {\n ...credentials,\n ...(input.receiverPhone !== undefined ? { receiverPhone: input.receiverPhone } : {}),\n ...(input.receiverEmail !== undefined ? { receiverEmail: input.receiverEmail } : {}),\n }\n return adapter.calculateRates({\n origin: input.origin,\n destination: input.destination,\n packages: input.packages,\n credentials: mergedCredentials,\n })\n },\n\n async createShipment(input: {\n providerKey: string\n orderId: string\n origin: { countryCode: string; postalCode: string; city: string; line1: string; line2?: string }\n destination: { countryCode: string; postalCode: string; city: string; line1: string; line2?: string }\n packages: Array<{ weightKg: number; lengthCm: number; widthCm: number; heightCm: number }>\n serviceCode: string\n labelFormat?: 'pdf' | 'zpl' | 'png'\n senderPhone?: string\n senderEmail?: string\n receiverPhone?: string\n receiverEmail?: string\n targetPoint?: string\n c2cSendingMethod?: string\n organizationId: string\n tenantId: string\n }) {\n const { adapter, credentials } = await resolveAdapter(input.providerKey, {\n organizationId: input.organizationId,\n tenantId: input.tenantId,\n })\n const mergedCredentials = {\n ...credentials,\n ...(input.senderPhone !== undefined ? { senderPhone: input.senderPhone } : {}),\n ...(input.senderEmail !== undefined ? { senderEmail: input.senderEmail } : {}),\n ...(input.receiverPhone !== undefined ? { receiverPhone: input.receiverPhone } : {}),\n ...(input.receiverEmail !== undefined ? { receiverEmail: input.receiverEmail } : {}),\n ...(input.targetPoint !== undefined ? { targetPoint: input.targetPoint } : {}),\n ...(input.c2cSendingMethod !== undefined ? { c2cSendingMethod: input.c2cSendingMethod } : {}),\n }\n const created = await adapter.createShipment({\n orderId: input.orderId,\n origin: input.origin,\n destination: input.destination,\n packages: input.packages,\n serviceCode: input.serviceCode,\n credentials: mergedCredentials,\n labelFormat: input.labelFormat,\n })\n const shipment = em.create(CarrierShipment, {\n orderId: input.orderId,\n providerKey: input.providerKey,\n carrierShipmentId: created.shipmentId,\n trackingNumber: created.trackingNumber,\n unifiedStatus: 'label_created',\n labelUrl: created.labelUrl ?? null,\n labelData: created.labelData ?? null,\n organizationId: input.organizationId,\n tenantId: input.tenantId,\n })\n await em.persist(shipment).flush()\n await emitShippingEvent('shipping_carriers.shipment.created', {\n shipmentId: shipment.id,\n orderId: input.orderId,\n providerKey: input.providerKey,\n trackingNumber: created.trackingNumber,\n organizationId: input.organizationId,\n tenantId: input.tenantId,\n })\n return shipment\n },\n\n async getTracking(input: {\n providerKey: string\n shipmentId?: string\n trackingNumber?: string\n organizationId: string\n tenantId: string\n }) {\n const { adapter, credentials } = await resolveAdapter(input.providerKey, {\n organizationId: input.organizationId,\n tenantId: input.tenantId,\n })\n const shipment = input.shipmentId\n ? await findOneWithDecryption(\n em,\n CarrierShipment,\n {\n id: input.shipmentId,\n organizationId: input.organizationId,\n tenantId: input.tenantId,\n deletedAt: null,\n },\n undefined,\n {\n organizationId: input.organizationId,\n tenantId: input.tenantId,\n },\n )\n : null\n return adapter.getTracking({\n shipmentId: shipment?.carrierShipmentId ?? input.shipmentId,\n trackingNumber: input.trackingNumber ?? shipment?.trackingNumber,\n credentials,\n })\n },\n\n async refreshTracking(input: {\n providerKey: string\n shipmentId: string\n organizationId: string\n tenantId: string\n }) {\n const scope = { organizationId: input.organizationId, tenantId: input.tenantId }\n const shipment = await findShipmentOrThrow(input.shipmentId, scope)\n const { adapter, credentials } = await resolveAdapter(input.providerKey, scope)\n const tracking = await adapter.getTracking({\n shipmentId: shipment.carrierShipmentId,\n trackingNumber: shipment.trackingNumber,\n credentials,\n })\n const previousStatus = shipment.unifiedStatus\n shipment.trackingEvents = tracking.events\n shipment.lastPolledAt = new Date()\n const transitionApplied = syncShipmentStatus(shipment, tracking.status)\n await em.flush()\n if (transitionApplied) {\n const eventPayload = {\n shipmentId: shipment.id,\n providerKey: input.providerKey,\n previousStatus,\n newStatus: tracking.status,\n organizationId: input.organizationId,\n tenantId: input.tenantId,\n }\n await emitShippingEvent('shipping_carriers.shipment.status_changed', eventPayload)\n if (TERMINAL_SHIPPING_STATUSES.has(tracking.status)) {\n const terminalEvent = getTerminalShippingEvent(tracking.status)\n if (terminalEvent) await emitShippingEvent(terminalEvent, eventPayload)\n }\n }\n return tracking\n },\n\n async cancelShipment(input: {\n providerKey: string\n shipmentId: string\n reason?: string\n organizationId: string\n tenantId: string\n }) {\n const scope = { organizationId: input.organizationId, tenantId: input.tenantId }\n const shipment = await findShipmentOrThrow(input.shipmentId, scope)\n if (!isValidShippingTransition(shipment.unifiedStatus as UnifiedShipmentStatus, 'cancelled')) {\n throw new ShipmentCancelNotAllowedError(shipment.unifiedStatus)\n }\n const { adapter, credentials } = await resolveAdapter(input.providerKey, {\n organizationId: input.organizationId,\n tenantId: input.tenantId,\n })\n const result = await adapter.cancelShipment({\n shipmentId: shipment.carrierShipmentId,\n reason: input.reason,\n credentials,\n })\n shipment.unifiedStatus = result.status\n await em.flush()\n await emitShippingEvent('shipping_carriers.shipment.cancelled', {\n shipmentId: shipment.id,\n providerKey: input.providerKey,\n organizationId: input.organizationId,\n tenantId: input.tenantId,\n })\n return result\n },\n\n async findShipmentByCarrierId(\n providerKey: string,\n carrierShipmentId: string,\n scope: { organizationId: string; tenantId: string },\n ) {\n return findOneWithDecryption(\n em,\n CarrierShipment,\n {\n providerKey,\n carrierShipmentId,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n },\n undefined,\n scope,\n )\n },\n\n async searchDropOffPoints(input: {\n providerKey: string\n query?: string\n type?: string\n postCode?: string\n organizationId: string\n tenantId: string\n }) {\n const { adapter, credentials } = await resolveAdapter(input.providerKey, {\n organizationId: input.organizationId,\n tenantId: input.tenantId,\n })\n if (!adapter.searchDropOffPoints) {\n throw new Error(`Provider ${input.providerKey} does not support drop-off point search`)\n }\n return adapter.searchDropOffPoints({\n query: input.query,\n type: input.type,\n postCode: input.postCode,\n credentials,\n })\n },\n }\n}\n\nexport type ShippingCarrierService = ReturnType<typeof createShippingCarrierService>\n"],
|
|
5
|
-
"mappings": "AACA,SAAS,6BAA6B;AAEtC,SAAS,
|
|
4
|
+
"sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport type { CredentialsService } from '../../integrations/lib/credentials-service'\nimport { CarrierShipment, type CarrierShipmentIdempotencyKey } from '../data/entities'\nimport { emitShippingEvent } from '../events'\nimport { getShippingAdapter } from './adapter-registry'\nimport type { UnifiedShipmentStatus } from './adapter'\nimport {\n claimShipmentIdempotency,\n computeShipmentRequestHash,\n findShipmentIdempotencyClaim,\n isShipmentIdempotencyConflictError,\n releaseShipmentIdempotency,\n resolveShipmentIdempotency,\n ShipmentIdempotencyConflictError,\n} from './shipment-idempotency'\nimport {\n getTerminalShippingEvent,\n isValidShippingTransition,\n ShipmentCancelNotAllowedError,\n syncShipmentStatus,\n TERMINAL_SHIPPING_STATUSES,\n} from './status-sync'\n\nexport function createShippingCarrierService(deps: {\n em: EntityManager\n integrationCredentialsService: CredentialsService\n}) {\n const { em, integrationCredentialsService } = deps\n\n async function findShipmentOrThrow(\n shipmentId: string,\n scope: { organizationId: string; tenantId: string },\n ): Promise<CarrierShipment> {\n const shipment = await findOneWithDecryption(\n em,\n CarrierShipment,\n {\n id: shipmentId,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n },\n undefined,\n scope,\n )\n if (!shipment) {\n throw new Error('Shipment not found')\n }\n return shipment\n }\n\n async function resolveAdapter(providerKey: string, scope: { organizationId: string; tenantId: string }) {\n const adapter = getShippingAdapter(providerKey)\n if (!adapter) throw new Error(`No shipping adapter registered for provider: ${providerKey}`)\n const credentials = await integrationCredentialsService.resolve(`carrier_${providerKey}`, scope) ?? {}\n return { adapter, credentials }\n }\n\n return {\n async calculateRates(input: {\n providerKey: string\n origin: { countryCode: string; postalCode: string; city: string; line1: string; line2?: string }\n destination: { countryCode: string; postalCode: string; city: string; line1: string; line2?: string }\n packages: Array<{ weightKg: number; lengthCm: number; widthCm: number; heightCm: number }>\n receiverPhone?: string\n receiverEmail?: string\n organizationId: string\n tenantId: string\n }) {\n const { adapter, credentials } = await resolveAdapter(input.providerKey, {\n organizationId: input.organizationId,\n tenantId: input.tenantId,\n })\n const mergedCredentials = {\n ...credentials,\n ...(input.receiverPhone !== undefined ? { receiverPhone: input.receiverPhone } : {}),\n ...(input.receiverEmail !== undefined ? { receiverEmail: input.receiverEmail } : {}),\n }\n return adapter.calculateRates({\n origin: input.origin,\n destination: input.destination,\n packages: input.packages,\n credentials: mergedCredentials,\n })\n },\n\n async createShipment(input: {\n providerKey: string\n orderId: string\n origin: { countryCode: string; postalCode: string; city: string; line1: string; line2?: string }\n destination: { countryCode: string; postalCode: string; city: string; line1: string; line2?: string }\n packages: Array<{ weightKg: number; lengthCm: number; widthCm: number; heightCm: number }>\n serviceCode: string\n labelFormat?: 'pdf' | 'zpl' | 'png'\n senderPhone?: string\n senderEmail?: string\n receiverPhone?: string\n receiverEmail?: string\n targetPoint?: string\n c2cSendingMethod?: string\n idempotencyKey?: string\n organizationId: string\n tenantId: string\n }) {\n const scope = { organizationId: input.organizationId, tenantId: input.tenantId }\n let claim: CarrierShipmentIdempotencyKey | null = null\n if (input.idempotencyKey) {\n const { idempotencyKey: _idempotencyKey, ...fingerprint } = input\n const requestHash = computeShipmentRequestHash(fingerprint)\n const existing = await findShipmentIdempotencyClaim(em, input.idempotencyKey, input.providerKey, scope)\n if (existing) {\n if (existing.requestHash !== requestHash) {\n throw new ShipmentIdempotencyConflictError(input.idempotencyKey)\n }\n if (existing.shipmentId) {\n return findShipmentOrThrow(existing.shipmentId, scope)\n }\n // Claim exists but the original request has not resolved yet (concurrent in-flight).\n throw new ShipmentIdempotencyConflictError(input.idempotencyKey)\n }\n claim = await claimShipmentIdempotency(em, input.idempotencyKey, input.providerKey, requestHash, scope)\n if (!claim) {\n // Lost the claim race with a concurrent request creating the same shipment.\n throw new ShipmentIdempotencyConflictError(input.idempotencyKey)\n }\n }\n let carrierShipmentCreated = false\n try {\n const { adapter, credentials } = await resolveAdapter(input.providerKey, scope)\n const mergedCredentials = {\n ...credentials,\n ...(input.senderPhone !== undefined ? { senderPhone: input.senderPhone } : {}),\n ...(input.senderEmail !== undefined ? { senderEmail: input.senderEmail } : {}),\n ...(input.receiverPhone !== undefined ? { receiverPhone: input.receiverPhone } : {}),\n ...(input.receiverEmail !== undefined ? { receiverEmail: input.receiverEmail } : {}),\n ...(input.targetPoint !== undefined ? { targetPoint: input.targetPoint } : {}),\n ...(input.c2cSendingMethod !== undefined ? { c2cSendingMethod: input.c2cSendingMethod } : {}),\n }\n const created = await adapter.createShipment({\n orderId: input.orderId,\n origin: input.origin,\n destination: input.destination,\n packages: input.packages,\n serviceCode: input.serviceCode,\n credentials: mergedCredentials,\n labelFormat: input.labelFormat,\n })\n carrierShipmentCreated = true\n const shipment = em.create(CarrierShipment, {\n orderId: input.orderId,\n providerKey: input.providerKey,\n carrierShipmentId: created.shipmentId,\n trackingNumber: created.trackingNumber,\n unifiedStatus: 'label_created',\n labelUrl: created.labelUrl ?? null,\n labelData: created.labelData ?? null,\n organizationId: input.organizationId,\n tenantId: input.tenantId,\n })\n await em.persist(shipment).flush()\n if (claim) {\n await resolveShipmentIdempotency(em, claim, shipment.id)\n }\n await emitShippingEvent('shipping_carriers.shipment.created', {\n shipmentId: shipment.id,\n orderId: input.orderId,\n providerKey: input.providerKey,\n trackingNumber: created.trackingNumber,\n organizationId: input.organizationId,\n tenantId: input.tenantId,\n })\n return shipment\n } catch (error: unknown) {\n // Release the claim only when the carrier was NOT successfully called, so a retry can\n // proceed. If the carrier already created the shipment, keep the claim so a retry does\n // not produce a duplicate upstream shipment.\n if (claim && !carrierShipmentCreated && input.idempotencyKey && !isShipmentIdempotencyConflictError(error)) {\n await releaseShipmentIdempotency(em, input.idempotencyKey, input.providerKey, scope).catch(() => {})\n }\n throw error\n }\n },\n\n async getTracking(input: {\n providerKey: string\n shipmentId?: string\n trackingNumber?: string\n organizationId: string\n tenantId: string\n }) {\n const { adapter, credentials } = await resolveAdapter(input.providerKey, {\n organizationId: input.organizationId,\n tenantId: input.tenantId,\n })\n const shipment = input.shipmentId\n ? await findOneWithDecryption(\n em,\n CarrierShipment,\n {\n id: input.shipmentId,\n organizationId: input.organizationId,\n tenantId: input.tenantId,\n deletedAt: null,\n },\n undefined,\n {\n organizationId: input.organizationId,\n tenantId: input.tenantId,\n },\n )\n : null\n return adapter.getTracking({\n shipmentId: shipment?.carrierShipmentId ?? input.shipmentId,\n trackingNumber: input.trackingNumber ?? shipment?.trackingNumber,\n credentials,\n })\n },\n\n async refreshTracking(input: {\n providerKey: string\n shipmentId: string\n organizationId: string\n tenantId: string\n }) {\n const scope = { organizationId: input.organizationId, tenantId: input.tenantId }\n const shipment = await findShipmentOrThrow(input.shipmentId, scope)\n const { adapter, credentials } = await resolveAdapter(input.providerKey, scope)\n const tracking = await adapter.getTracking({\n shipmentId: shipment.carrierShipmentId,\n trackingNumber: shipment.trackingNumber,\n credentials,\n })\n const previousStatus = shipment.unifiedStatus\n shipment.trackingEvents = tracking.events\n shipment.lastPolledAt = new Date()\n const transitionApplied = syncShipmentStatus(shipment, tracking.status)\n await em.flush()\n if (transitionApplied) {\n const eventPayload = {\n shipmentId: shipment.id,\n providerKey: input.providerKey,\n previousStatus,\n newStatus: tracking.status,\n organizationId: input.organizationId,\n tenantId: input.tenantId,\n }\n await emitShippingEvent('shipping_carriers.shipment.status_changed', eventPayload)\n if (TERMINAL_SHIPPING_STATUSES.has(tracking.status)) {\n const terminalEvent = getTerminalShippingEvent(tracking.status)\n if (terminalEvent) await emitShippingEvent(terminalEvent, eventPayload)\n }\n }\n return tracking\n },\n\n async cancelShipment(input: {\n providerKey: string\n shipmentId: string\n reason?: string\n organizationId: string\n tenantId: string\n }) {\n const scope = { organizationId: input.organizationId, tenantId: input.tenantId }\n const shipment = await findShipmentOrThrow(input.shipmentId, scope)\n if (!isValidShippingTransition(shipment.unifiedStatus as UnifiedShipmentStatus, 'cancelled')) {\n throw new ShipmentCancelNotAllowedError(shipment.unifiedStatus)\n }\n const { adapter, credentials } = await resolveAdapter(input.providerKey, {\n organizationId: input.organizationId,\n tenantId: input.tenantId,\n })\n const result = await adapter.cancelShipment({\n shipmentId: shipment.carrierShipmentId,\n reason: input.reason,\n credentials,\n })\n shipment.unifiedStatus = result.status\n await em.flush()\n await emitShippingEvent('shipping_carriers.shipment.cancelled', {\n shipmentId: shipment.id,\n providerKey: input.providerKey,\n organizationId: input.organizationId,\n tenantId: input.tenantId,\n })\n return result\n },\n\n async findShipmentByCarrierId(\n providerKey: string,\n carrierShipmentId: string,\n scope: { organizationId: string; tenantId: string },\n ) {\n return findOneWithDecryption(\n em,\n CarrierShipment,\n {\n providerKey,\n carrierShipmentId,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n },\n undefined,\n scope,\n )\n },\n\n async searchDropOffPoints(input: {\n providerKey: string\n query?: string\n type?: string\n postCode?: string\n organizationId: string\n tenantId: string\n }) {\n const { adapter, credentials } = await resolveAdapter(input.providerKey, {\n organizationId: input.organizationId,\n tenantId: input.tenantId,\n })\n if (!adapter.searchDropOffPoints) {\n throw new Error(`Provider ${input.providerKey} does not support drop-off point search`)\n }\n return adapter.searchDropOffPoints({\n query: input.query,\n type: input.type,\n postCode: input.postCode,\n credentials,\n })\n },\n }\n}\n\nexport type ShippingCarrierService = ReturnType<typeof createShippingCarrierService>\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/dist/modules/shipping_carriers/migrations/Migration20260618173616_shipping_carriers.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Migration } from "@mikro-orm/migrations";
|
|
2
|
+
class Migration20260618173616_shipping_carriers extends Migration {
|
|
3
|
+
up() {
|
|
4
|
+
this.addSql(`create table "carrier_shipment_idempotency_keys" ("id" uuid not null default gen_random_uuid(), "provider_key" text not null, "idempotency_key" text not null, "request_hash" text not null, "shipment_id" uuid null, "organization_id" uuid not null, "tenant_id" uuid not null, "created_at" timestamptz not null, primary key ("id"));`);
|
|
5
|
+
this.addSql(`alter table "carrier_shipment_idempotency_keys" add constraint "carrier_shipment_idempotency_keys_unique" unique ("idempotency_key", "provider_key", "organization_id", "tenant_id");`);
|
|
6
|
+
}
|
|
7
|
+
down() {
|
|
8
|
+
this.addSql(`drop table if exists "carrier_shipment_idempotency_keys";`);
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
export {
|
|
12
|
+
Migration20260618173616_shipping_carriers
|
|
13
|
+
};
|
|
14
|
+
//# sourceMappingURL=Migration20260618173616_shipping_carriers.js.map
|
package/dist/modules/shipping_carriers/migrations/Migration20260618173616_shipping_carriers.js.map
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/modules/shipping_carriers/migrations/Migration20260618173616_shipping_carriers.ts"],
|
|
4
|
+
"sourcesContent": ["import { Migration } from '@mikro-orm/migrations';\n\nexport class Migration20260618173616_shipping_carriers extends Migration {\n\n override up(): void | Promise<void> {\n this.addSql(`create table \"carrier_shipment_idempotency_keys\" (\"id\" uuid not null default gen_random_uuid(), \"provider_key\" text not null, \"idempotency_key\" text not null, \"request_hash\" text not null, \"shipment_id\" uuid null, \"organization_id\" uuid not null, \"tenant_id\" uuid not null, \"created_at\" timestamptz not null, primary key (\"id\"));`);\n this.addSql(`alter table \"carrier_shipment_idempotency_keys\" add constraint \"carrier_shipment_idempotency_keys_unique\" unique (\"idempotency_key\", \"provider_key\", \"organization_id\", \"tenant_id\");`);\n }\n\n override down(): void | Promise<void> {\n this.addSql(`drop table if exists \"carrier_shipment_idempotency_keys\";`);\n }\n\n}\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,iBAAiB;AAEnB,MAAM,kDAAkD,UAAU;AAAA,EAE9D,KAA2B;AAClC,SAAK,OAAO,2UAA2U;AACvV,SAAK,OAAO,uLAAuL;AAAA,EACrM;AAAA,EAES,OAA6B;AACpC,SAAK,OAAO,2DAA2D;AAAA,EACzE;AAEF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
import { useT } from "@open-mercato/shared/lib/i18n/context";
|
|
5
|
-
import {
|
|
5
|
+
import { apiCallOrThrow } from "@open-mercato/ui/backend/utils/apiCall";
|
|
6
|
+
import { surfaceRecordConflict } from "@open-mercato/ui/backend/conflicts";
|
|
7
|
+
import { flash } from "@open-mercato/ui/backend/FlashMessages";
|
|
6
8
|
import { ProjectColorDot } from "./ProjectColorDot.js";
|
|
7
9
|
function getLocalDateStr(date) {
|
|
8
10
|
const year = date.getFullYear();
|
|
@@ -47,6 +49,7 @@ function InlineDescription({
|
|
|
47
49
|
placeholder,
|
|
48
50
|
onSaved
|
|
49
51
|
}) {
|
|
52
|
+
const t = useT();
|
|
50
53
|
const [editing, setEditing] = React.useState(false);
|
|
51
54
|
const [value, setValue] = React.useState(initialValue ?? "");
|
|
52
55
|
const [saving, setSaving] = React.useState(false);
|
|
@@ -62,18 +65,24 @@ function InlineDescription({
|
|
|
62
65
|
}
|
|
63
66
|
setSaving(true);
|
|
64
67
|
try {
|
|
65
|
-
await
|
|
68
|
+
await apiCallOrThrow(`/api/staff/timesheets/time-entries`, {
|
|
66
69
|
method: "PUT",
|
|
67
70
|
headers: { "Content-Type": "application/json" },
|
|
68
71
|
body: JSON.stringify({ id: entryId, notes: trimmed || null })
|
|
69
72
|
});
|
|
70
|
-
onSaved?.();
|
|
71
|
-
} catch {
|
|
72
|
-
} finally {
|
|
73
73
|
setSaving(false);
|
|
74
74
|
setEditing(false);
|
|
75
|
+
onSaved?.();
|
|
76
|
+
} catch (err) {
|
|
77
|
+
setSaving(false);
|
|
78
|
+
if (!surfaceRecordConflict(err, t)) {
|
|
79
|
+
flash(
|
|
80
|
+
t("staff.timesheets.my.list.saveError", "Failed to save description. Please try again."),
|
|
81
|
+
"error"
|
|
82
|
+
);
|
|
83
|
+
}
|
|
75
84
|
}
|
|
76
|
-
}, [entryId, value, initialValue, onSaved]);
|
|
85
|
+
}, [entryId, value, initialValue, onSaved, t]);
|
|
77
86
|
if (!editing) {
|
|
78
87
|
return /* @__PURE__ */ jsx(
|
|
79
88
|
"button",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/modules/staff/lib/timesheets-ui/ListView.tsx"],
|
|
4
|
-
"sourcesContent": ["'use client'\n\nimport * as React from 'react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport type { TranslateFn } from '@open-mercato/shared/lib/i18n/context'\nimport {
|
|
5
|
-
"mappings": ";
|
|
4
|
+
"sourcesContent": ["'use client'\n\nimport * as React from 'react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport type { TranslateFn } from '@open-mercato/shared/lib/i18n/context'\nimport { apiCallOrThrow } from '@open-mercato/ui/backend/utils/apiCall'\nimport { surfaceRecordConflict } from '@open-mercato/ui/backend/conflicts'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { ProjectColorDot } from './ProjectColorDot'\n\ntype TimeEntry = {\n id: string\n date: string\n durationMinutes: number\n projectId: string\n projectName: string\n projectCode: string | null\n projectColor: string | null\n notes: string | null\n source: string\n startedAt: string | null\n endedAt: string | null\n}\n\ntype ListViewProps = {\n entries: TimeEntry[]\n onEntryUpdated?: () => void\n}\n\nfunction getLocalDateStr(date: Date): string {\n const year = date.getFullYear()\n const month = String(date.getMonth() + 1).padStart(2, '0')\n const day = String(date.getDate()).padStart(2, '0')\n return `${year}-${month}-${day}`\n}\n\nfunction formatDayLabel(dateStr: string, translate: TranslateFn): string {\n const today = new Date()\n const todayStr = getLocalDateStr(today)\n\n const yesterday = new Date(today)\n yesterday.setDate(yesterday.getDate() - 1)\n const yesterdayStr = getLocalDateStr(yesterday)\n\n if (dateStr === todayStr) return translate('staff.timesheets.my.list.today', 'Today')\n if (dateStr === yesterdayStr) return translate('staff.timesheets.my.list.yesterday', 'Yesterday')\n\n const date = new Date(dateStr + 'T00:00:00')\n const dayName = date.toLocaleDateString(undefined, { weekday: 'short' })\n const day = date.getDate()\n const month = date.toLocaleDateString(undefined, { month: 'short' })\n\n return `${dayName}, ${day} ${month}`\n}\n\nfunction formatDuration(minutes: number): string {\n if (minutes < 60) return `${minutes}m`\n\n const hours = Math.floor(minutes / 60)\n const remainingMinutes = minutes % 60\n\n if (remainingMinutes === 0) return `${hours}h`\n return `${hours}h ${remainingMinutes}m`\n}\n\nfunction formatTimeRange(startedAt: string, endedAt: string): string {\n const start = new Date(startedAt)\n const end = new Date(endedAt)\n\n const format = (date: Date) =>\n date.toLocaleTimeString(undefined, {\n hour: 'numeric',\n minute: '2-digit',\n hour12: true,\n })\n\n return `${format(start)} - ${format(end)}`\n}\n\nfunction InlineDescription({\n entryId,\n initialValue,\n placeholder,\n onSaved,\n}: {\n entryId: string\n initialValue: string | null\n placeholder: string\n onSaved?: () => void\n}) {\n const t = useT()\n const [editing, setEditing] = React.useState(false)\n const [value, setValue] = React.useState(initialValue ?? '')\n const [saving, setSaving] = React.useState(false)\n const inputRef = React.useRef<HTMLInputElement>(null)\n\n React.useEffect(() => {\n if (editing && inputRef.current) inputRef.current.focus()\n }, [editing])\n\n const save = React.useCallback(async () => {\n const trimmed = value.trim()\n if (trimmed === (initialValue ?? '')) {\n setEditing(false)\n return\n }\n setSaving(true)\n try {\n await apiCallOrThrow(`/api/staff/timesheets/time-entries`, {\n method: 'PUT',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ id: entryId, notes: trimmed || null }),\n })\n setSaving(false)\n setEditing(false)\n onSaved?.()\n } catch (err) {\n setSaving(false)\n if (!surfaceRecordConflict(err, t)) {\n flash(\n t('staff.timesheets.my.list.saveError', 'Failed to save description. Please try again.'),\n 'error',\n )\n }\n }\n }, [entryId, value, initialValue, onSaved, t])\n\n if (!editing) {\n return (\n <button\n type=\"button\"\n onClick={() => setEditing(true)}\n className={`text-sm text-left cursor-pointer hover:underline ${\n initialValue ? '' : 'text-muted-foreground italic'\n }`}\n >\n {initialValue || placeholder}\n </button>\n )\n }\n\n return (\n <input\n ref={inputRef}\n type=\"text\"\n value={value}\n disabled={saving}\n onChange={(e) => setValue(e.target.value)}\n onBlur={() => { void save() }}\n onKeyDown={(e) => {\n if (e.key === 'Enter') { void save() }\n if (e.key === 'Escape') { setValue(initialValue ?? ''); setEditing(false) }\n }}\n placeholder={placeholder}\n className=\"text-sm w-full bg-transparent border-b border-primary/40 outline-none py-0.5 placeholder:text-muted-foreground/50\"\n />\n )\n}\n\nexport function ListView({ entries, onEntryUpdated }: ListViewProps) {\n const t = useT()\n\n if (entries.length === 0) {\n return (\n <div className=\"flex items-center justify-center py-12 text-sm text-muted-foreground\">\n {t('staff.timesheets.my.list.noEntries', 'No entries for this period.')}\n </div>\n )\n }\n\n const grouped = new Map<string, TimeEntry[]>()\n for (const entry of entries) {\n const existing = grouped.get(entry.date)\n if (existing) {\n existing.push(entry)\n } else {\n grouped.set(entry.date, [entry])\n }\n }\n\n const sortedDays = [...grouped.keys()].sort((a, b) => b.localeCompare(a))\n\n return (\n <div>\n {sortedDays.map((dateStr) => {\n const dayEntries = grouped.get(dateStr)!\n const dailyTotal = dayEntries.reduce(\n (sum, entry) => sum + entry.durationMinutes,\n 0,\n )\n\n return (\n <div key={dateStr} className=\"rounded-lg border mb-4\">\n <div className=\"flex justify-between items-center p-4 border-b bg-muted/30 font-medium text-sm\">\n <span>{formatDayLabel(dateStr, t)}</span>\n <span className=\"font-mono tabular-nums\">\n {formatDuration(dailyTotal)}\n </span>\n </div>\n\n {dayEntries.map((entry) => (\n <div\n key={entry.id}\n className=\"flex items-center justify-between p-3 border-b last:border-0 hover:bg-muted/20\"\n >\n <div className=\"flex flex-col gap-1 min-w-0 flex-1 mr-4\">\n <InlineDescription\n entryId={entry.id}\n initialValue={entry.notes}\n placeholder={t('staff.timesheets.my.list.addDescription', 'Add description')}\n onSaved={onEntryUpdated}\n />\n <span className=\"inline-flex items-center gap-1 text-xs font-medium text-muted-foreground\">\n <ProjectColorDot colorKey={entry.projectColor} projectName={entry.projectName} size=\"xs\" />\n {entry.projectName}\n </span>\n </div>\n\n <div className=\"flex flex-col items-end gap-1 shrink-0\">\n {entry.source === 'timer' &&\n entry.startedAt &&\n entry.endedAt ? (\n <span className=\"text-xs text-muted-foreground\">\n {formatTimeRange(entry.startedAt, entry.endedAt)}\n </span>\n ) : null}\n <span className=\"text-sm font-mono tabular-nums\">\n {formatDuration(entry.durationMinutes)}\n </span>\n </div>\n </div>\n ))}\n </div>\n )\n })}\n </div>\n )\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -3,6 +3,8 @@ import { jsx, jsxs } from "react/jsx-runtime";
|
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
|
|
5
5
|
import { Button } from "@open-mercato/ui/primitives/button";
|
|
6
|
+
import { IconButton } from "@open-mercato/ui/primitives/icon-button";
|
|
7
|
+
import { Tabs, TabsList, TabsTrigger } from "@open-mercato/ui/primitives/tabs";
|
|
6
8
|
import { Input } from "@open-mercato/ui/primitives/input";
|
|
7
9
|
import { ComboboxInput } from "@open-mercato/ui/backend/inputs";
|
|
8
10
|
import { LoadingMessage, ErrorMessage } from "@open-mercato/ui/backend/detail";
|
|
@@ -272,21 +274,7 @@ function TranslationManager({
|
|
|
272
274
|
)
|
|
273
275
|
] });
|
|
274
276
|
};
|
|
275
|
-
const renderLocaleTabs = () => /* @__PURE__ */ jsx(
|
|
276
|
-
const isActive = activeLocale === locale;
|
|
277
|
-
return /* @__PURE__ */ jsx(
|
|
278
|
-
"button",
|
|
279
|
-
{
|
|
280
|
-
type: "button",
|
|
281
|
-
"data-state": isActive ? "active" : "inactive",
|
|
282
|
-
"data-locale": locale,
|
|
283
|
-
className: `px-3 py-1.5 text-sm font-medium transition-colors ${isActive ? "border-b-2 border-accent-indigo text-foreground" : "text-muted-foreground hover:text-foreground"}`,
|
|
284
|
-
onClick: () => setActiveLocale(locale),
|
|
285
|
-
children: locale.toUpperCase()
|
|
286
|
-
},
|
|
287
|
-
locale
|
|
288
|
-
);
|
|
289
|
-
}) });
|
|
277
|
+
const renderLocaleTabs = () => /* @__PURE__ */ jsx(Tabs, { variant: "underline", value: activeLocale, onValueChange: setActiveLocale, children: /* @__PURE__ */ jsx(TabsList, { children: locales.map((locale) => /* @__PURE__ */ jsx(TabsTrigger, { value: locale, children: locale.toUpperCase() }, locale)) }) });
|
|
290
278
|
const renderFieldTable = () => {
|
|
291
279
|
if (!entityType || !recordId) {
|
|
292
280
|
return /* @__PURE__ */ jsx("div", { className: "rounded border bg-background/80 p-4 text-sm text-muted-foreground", children: t("translations.manager.selectFirst", "Select an entity and record to manage translations.") });
|
|
@@ -496,10 +484,13 @@ function LocaleManager() {
|
|
|
496
484
|
locale.toUpperCase(),
|
|
497
485
|
getIso639Label(locale) ? ` \u2014 ${getIso639Label(locale)}` : "",
|
|
498
486
|
locales.length > 1 && /* @__PURE__ */ jsx(
|
|
499
|
-
|
|
487
|
+
IconButton,
|
|
500
488
|
{
|
|
501
|
-
|
|
502
|
-
|
|
489
|
+
variant: "ghost",
|
|
490
|
+
size: "xs",
|
|
491
|
+
fullRadius: true,
|
|
492
|
+
"aria-label": t("translations.locales.remove", "Remove {{locale}}", { locale: getIso639Label(locale) ?? locale.toUpperCase() }),
|
|
493
|
+
title: t("translations.locales.remove", "Remove {{locale}}", { locale: getIso639Label(locale) ?? locale.toUpperCase() }),
|
|
503
494
|
onClick: () => removeLocale(locale),
|
|
504
495
|
disabled: mutation.isPending,
|
|
505
496
|
children: /* @__PURE__ */ jsx(X, { className: "h-3 w-3" })
|