@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
|
@@ -485,37 +485,13 @@ export async function GET(_req: Request, ctx: { params?: { id?: string } }) {
|
|
|
485
485
|
return forbidden('Access denied')
|
|
486
486
|
}
|
|
487
487
|
|
|
488
|
-
|
|
488
|
+
const [profileResult, personCompanyLinks] = await Promise.all([
|
|
489
|
+
findOneWithDecryption(em, CustomerPersonProfile, { entity: person }, { populate: ['company'] }, { tenantId: person.tenantId ?? auth.tenantId ?? null, organizationId: person.organizationId ?? auth.orgId ?? null }),
|
|
490
|
+
loadPersonCompanyLinks(em, person),
|
|
491
|
+
])
|
|
492
|
+
profile = profileResult
|
|
489
493
|
profiler.mark('profile_loaded', { found: !!profile })
|
|
490
|
-
companies = summarizePersonCompanies(profile,
|
|
491
|
-
|
|
492
|
-
if (includeAddresses) {
|
|
493
|
-
addresses = await findWithDecryption(em, CustomerAddress, { entity: person.id }, { orderBy: { isPrimary: 'desc', createdAt: 'desc' } }, { tenantId: person.tenantId ?? auth.tenantId ?? null, organizationId: person.organizationId ?? auth.orgId ?? null })
|
|
494
|
-
profiler.mark('addresses_loaded', { count: addresses.length })
|
|
495
|
-
}
|
|
496
|
-
|
|
497
|
-
tagAssignments = await findWithDecryption(
|
|
498
|
-
em,
|
|
499
|
-
CustomerTagAssignment,
|
|
500
|
-
{ entity: person.id },
|
|
501
|
-
{ populate: ['tag'] },
|
|
502
|
-
{ tenantId: person.tenantId ?? auth.tenantId ?? null, organizationId: person.organizationId ?? auth.orgId ?? null },
|
|
503
|
-
)
|
|
504
|
-
profiler.mark('tags_loaded', { count: tagAssignments.length })
|
|
505
|
-
|
|
506
|
-
const labelAssignments = await findWithDecryption(
|
|
507
|
-
em,
|
|
508
|
-
CustomerLabelAssignment,
|
|
509
|
-
{ entity: person.id },
|
|
510
|
-
{ populate: ['label'] },
|
|
511
|
-
{ tenantId: person.tenantId ?? auth.tenantId ?? null, organizationId: person.organizationId ?? auth.orgId ?? null },
|
|
512
|
-
)
|
|
513
|
-
profiler.mark('labels_loaded', { count: labelAssignments.length })
|
|
514
|
-
|
|
515
|
-
if (includeComments) {
|
|
516
|
-
comments = await findWithDecryption(em, CustomerComment, { entity: person.id }, { orderBy: { createdAt: 'desc' }, limit: 50 }, { tenantId: person.tenantId ?? auth.tenantId ?? null, organizationId: person.organizationId ?? auth.orgId ?? null })
|
|
517
|
-
profiler.mark('comments_loaded', { count: comments.length })
|
|
518
|
-
}
|
|
494
|
+
companies = summarizePersonCompanies(profile, personCompanyLinks)
|
|
519
495
|
|
|
520
496
|
// Per-user email privacy (CRM email integration spec, "Layer 1 — DB filter"):
|
|
521
497
|
// exclude private email interactions owned by other users from every read
|
|
@@ -528,18 +504,46 @@ export async function GET(_req: Request, ctx: { params?: { id?: string } }) {
|
|
|
528
504
|
userFeatures: undefined,
|
|
529
505
|
})
|
|
530
506
|
|
|
507
|
+
const personScope = { tenantId: person.tenantId ?? auth.tenantId ?? null, organizationId: person.organizationId ?? auth.orgId ?? null }
|
|
531
508
|
const shouldLoadCanonicalInteractions = includeInteractions || includeActivities || includeTodos
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
509
|
+
|
|
510
|
+
// These reads only depend on the resolved person + scope + email visibility
|
|
511
|
+
// filter, so dispatch them together to avoid a server-side request waterfall
|
|
512
|
+
// before the detail surface can render (issue #3203).
|
|
513
|
+
const [
|
|
514
|
+
addressesResult,
|
|
515
|
+
tagAssignmentsResult,
|
|
516
|
+
labelAssignments,
|
|
517
|
+
commentsResult,
|
|
518
|
+
canonicalInteractionRows,
|
|
519
|
+
] = await Promise.all([
|
|
520
|
+
includeAddresses
|
|
521
|
+
? findWithDecryption(em, CustomerAddress, { entity: person.id }, { orderBy: { isPrimary: 'desc', createdAt: 'desc' } }, personScope)
|
|
522
|
+
: Promise.resolve<CustomerAddress[]>([]),
|
|
523
|
+
findWithDecryption(em, CustomerTagAssignment, { entity: person.id }, { populate: ['tag'] }, personScope),
|
|
524
|
+
findWithDecryption(em, CustomerLabelAssignment, { entity: person.id }, { populate: ['label'] }, personScope),
|
|
525
|
+
includeComments
|
|
526
|
+
? findWithDecryption(em, CustomerComment, { entity: person.id }, { orderBy: { createdAt: 'desc' }, limit: 50 }, personScope)
|
|
527
|
+
: Promise.resolve<CustomerComment[]>([]),
|
|
528
|
+
shouldLoadCanonicalInteractions
|
|
529
|
+
? findWithDecryption(
|
|
530
|
+
em,
|
|
531
|
+
CustomerInteraction,
|
|
532
|
+
interactionFlags.unified
|
|
533
|
+
? { entity: person.id, deletedAt: null, ...emailVisibilityFilter }
|
|
534
|
+
: { entity: person.id, ...emailVisibilityFilter },
|
|
535
|
+
{ orderBy: { scheduledAt: 'asc', createdAt: 'desc' }, limit: 100 },
|
|
536
|
+
personScope,
|
|
537
|
+
)
|
|
538
|
+
: Promise.resolve<CustomerInteraction[]>([]),
|
|
539
|
+
])
|
|
540
|
+
addresses = addressesResult
|
|
541
|
+
tagAssignments = tagAssignmentsResult
|
|
542
|
+
comments = commentsResult
|
|
543
|
+
profiler.mark('addresses_loaded', { count: addresses.length })
|
|
544
|
+
profiler.mark('tags_loaded', { count: tagAssignments.length })
|
|
545
|
+
profiler.mark('labels_loaded', { count: labelAssignments.length })
|
|
546
|
+
profiler.mark('comments_loaded', { count: comments.length })
|
|
543
547
|
profiler.mark('canonical_interactions_loaded', { count: canonicalInteractionRows.length })
|
|
544
548
|
const canonicalActiveInteractions = canonicalInteractionRows.filter((interaction) => !interaction.deletedAt)
|
|
545
549
|
const canonicalInteractions = shouldLoadCanonicalInteractions
|
|
@@ -676,35 +680,39 @@ export async function GET(_req: Request, ctx: { params?: { id?: string } }) {
|
|
|
676
680
|
profiler.mark('deals_loaded', { count: deals.length })
|
|
677
681
|
}
|
|
678
682
|
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
recordIds: [person.id],
|
|
683
|
-
tenantIdByRecord: { [person.id]: person.tenantId ?? null },
|
|
684
|
-
organizationIdByRecord: { [person.id]: person.organizationId ?? null },
|
|
685
|
-
tenantFallbacks: [
|
|
686
|
-
person.tenantId ?? auth.tenantId ?? null,
|
|
687
|
-
].filter((v): v is string => !!v),
|
|
688
|
-
})
|
|
689
|
-
profiler.mark('entity_custom_fields_loaded', { keys: Object.keys(entityCustomFieldValues?.[person.id] ?? {}).length })
|
|
690
|
-
|
|
691
|
-
let profileCustomFieldValues: Record<string, Record<string, unknown>> = {}
|
|
683
|
+
// Entity custom fields, profile custom fields, and the routing lookup do not
|
|
684
|
+
// depend on each other (profileId is already resolved above), so load them in
|
|
685
|
+
// parallel instead of three sequential awaits (issue #3203).
|
|
692
686
|
const profileId = profile?.id ?? null
|
|
693
|
-
|
|
694
|
-
|
|
687
|
+
const [entityCustomFieldValues, profileCustomFieldValues, routing] = await Promise.all([
|
|
688
|
+
loadCustomFieldValues({
|
|
695
689
|
em,
|
|
696
|
-
entityId: E.customers.
|
|
697
|
-
recordIds: [
|
|
698
|
-
tenantIdByRecord: { [
|
|
699
|
-
organizationIdByRecord: { [
|
|
690
|
+
entityId: E.customers.customer_entity,
|
|
691
|
+
recordIds: [person.id],
|
|
692
|
+
tenantIdByRecord: { [person.id]: person.tenantId ?? null },
|
|
693
|
+
organizationIdByRecord: { [person.id]: person.organizationId ?? null },
|
|
700
694
|
tenantFallbacks: [
|
|
701
|
-
|
|
695
|
+
person.tenantId ?? auth.tenantId ?? null,
|
|
702
696
|
].filter((v): v is string => !!v),
|
|
703
|
-
})
|
|
697
|
+
}),
|
|
698
|
+
profileId
|
|
699
|
+
? loadCustomFieldValues({
|
|
700
|
+
em,
|
|
701
|
+
entityId: E.customers.customer_person_profile,
|
|
702
|
+
recordIds: [profileId],
|
|
703
|
+
tenantIdByRecord: { [profileId]: profile?.tenantId ?? null },
|
|
704
|
+
organizationIdByRecord: { [profileId]: profile?.organizationId ?? null },
|
|
705
|
+
tenantFallbacks: [
|
|
706
|
+
profile?.tenantId ?? person.tenantId ?? auth.tenantId ?? null,
|
|
707
|
+
].filter((v): v is string => !!v),
|
|
708
|
+
})
|
|
709
|
+
: Promise.resolve<Record<string, Record<string, unknown>>>({}),
|
|
710
|
+
resolvePersonCustomFieldRouting(em, person.tenantId ?? null, person.organizationId ?? null),
|
|
711
|
+
])
|
|
712
|
+
profiler.mark('entity_custom_fields_loaded', { keys: Object.keys(entityCustomFieldValues?.[person.id] ?? {}).length })
|
|
713
|
+
if (profileId) {
|
|
704
714
|
profiler.mark('profile_custom_fields_loaded', { keys: Object.keys(profileCustomFieldValues?.[profileId] ?? {}).length })
|
|
705
715
|
}
|
|
706
|
-
|
|
707
|
-
const routing = await resolvePersonCustomFieldRouting(em, person.tenantId ?? null, person.organizationId ?? null)
|
|
708
716
|
profiler.mark('custom_field_routing_resolved', { keys: routing.size })
|
|
709
717
|
customFields = normalizeCustomerDetailCustomFields(
|
|
710
718
|
mergePersonCustomFieldValues(
|
|
@@ -716,16 +724,25 @@ export async function GET(_req: Request, ctx: { params?: { id?: string } }) {
|
|
|
716
724
|
profiler.mark('custom_fields_merged', { keys: Object.keys(customFields).length })
|
|
717
725
|
|
|
718
726
|
const viewerUserIdFinal = viewerUserId
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
727
|
+
// The count fields are independent of each other; dispatch them together so
|
|
728
|
+
// the response counts object is assembled in one round of parallel queries
|
|
729
|
+
// instead of an inline await waterfall (issue #3203).
|
|
730
|
+
const [
|
|
731
|
+
commentsCount,
|
|
732
|
+
activitiesCount,
|
|
733
|
+
interactionsCount,
|
|
734
|
+
todosCount,
|
|
735
|
+
addressesCount,
|
|
736
|
+
dealsCount,
|
|
737
|
+
] = await Promise.all([
|
|
738
|
+
includeComments
|
|
739
|
+
? Promise.resolve(comments.length)
|
|
740
|
+
: em.count(CustomerComment, {
|
|
724
741
|
entity: person.id,
|
|
725
742
|
organizationId: person.organizationId,
|
|
726
743
|
tenantId: person.tenantId,
|
|
727
744
|
}),
|
|
728
|
-
|
|
745
|
+
em.count(CustomerInteraction, {
|
|
729
746
|
entity: person.id,
|
|
730
747
|
organizationId: person.organizationId,
|
|
731
748
|
tenantId: person.tenantId,
|
|
@@ -733,38 +750,47 @@ export async function GET(_req: Request, ctx: { params?: { id?: string } }) {
|
|
|
733
750
|
interactionType: { $ne: 'task' },
|
|
734
751
|
...emailVisibilityFilter,
|
|
735
752
|
}),
|
|
736
|
-
|
|
753
|
+
em.count(CustomerInteraction, {
|
|
737
754
|
entity: person.id,
|
|
738
755
|
organizationId: person.organizationId,
|
|
739
756
|
tenantId: person.tenantId,
|
|
740
757
|
deletedAt: null,
|
|
741
758
|
...emailVisibilityFilter,
|
|
742
759
|
}),
|
|
743
|
-
|
|
744
|
-
?
|
|
760
|
+
interactionFlags.unified
|
|
761
|
+
? em.count(CustomerInteraction, {
|
|
745
762
|
entity: person.id,
|
|
746
763
|
organizationId: person.organizationId,
|
|
747
764
|
tenantId: person.tenantId,
|
|
748
765
|
deletedAt: null,
|
|
749
766
|
interactionType: 'task',
|
|
750
767
|
})
|
|
751
|
-
:
|
|
768
|
+
: em.count(CustomerTodoLink, {
|
|
752
769
|
entity: person.id,
|
|
753
770
|
organizationId: person.organizationId,
|
|
754
771
|
tenantId: person.tenantId,
|
|
755
772
|
}),
|
|
756
|
-
|
|
757
|
-
? addresses.length
|
|
758
|
-
:
|
|
773
|
+
includeAddresses
|
|
774
|
+
? Promise.resolve(addresses.length)
|
|
775
|
+
: em.count(CustomerAddress, {
|
|
759
776
|
entity: person.id,
|
|
760
777
|
organizationId: person.organizationId,
|
|
761
778
|
tenantId: person.tenantId,
|
|
762
779
|
}),
|
|
763
|
-
|
|
764
|
-
? deals.length
|
|
765
|
-
:
|
|
780
|
+
includeDeals
|
|
781
|
+
? Promise.resolve(deals.length)
|
|
782
|
+
: em.count(CustomerDealPersonLink, {
|
|
766
783
|
person: person.id,
|
|
767
784
|
}),
|
|
785
|
+
])
|
|
786
|
+
const counts = {
|
|
787
|
+
tags: tagAssignments.length + labelAssignments.length,
|
|
788
|
+
comments: commentsCount,
|
|
789
|
+
activities: activitiesCount,
|
|
790
|
+
interactions: interactionsCount,
|
|
791
|
+
todos: todosCount,
|
|
792
|
+
addresses: addressesCount,
|
|
793
|
+
deals: dealsCount,
|
|
768
794
|
companies: companies.length,
|
|
769
795
|
}
|
|
770
796
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
3
|
import * as React from 'react'
|
|
4
|
+
import { ChevronDown, ChevronUp } from 'lucide-react'
|
|
4
5
|
import { Page, PageBody } from '@open-mercato/ui/backend/Page'
|
|
5
6
|
import { useT } from '@open-mercato/shared/lib/i18n/context'
|
|
6
7
|
import { apiCall, withScopedApiRequestHeaders } from '@open-mercato/ui/backend/utils/apiCall'
|
|
@@ -8,6 +9,8 @@ import { buildOptimisticLockHeader } from '@open-mercato/ui/backend/utils/optimi
|
|
|
8
9
|
import { useGuardedMutation } from '@open-mercato/ui/backend/injection/useGuardedMutation'
|
|
9
10
|
import { flash } from '@open-mercato/ui/backend/FlashMessages'
|
|
10
11
|
import { Button } from '@open-mercato/ui/primitives/button'
|
|
12
|
+
import { Checkbox } from '@open-mercato/ui/primitives/checkbox'
|
|
13
|
+
import { IconButton } from '@open-mercato/ui/primitives/icon-button'
|
|
11
14
|
import { Input } from '@open-mercato/ui/primitives/input'
|
|
12
15
|
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from '@open-mercato/ui/primitives/dialog'
|
|
13
16
|
import {
|
|
@@ -516,24 +519,28 @@ export default function PipelineStagesPage() {
|
|
|
516
519
|
{stages.map((stage, index) => (
|
|
517
520
|
<div key={stage.id} className="flex items-center gap-3 px-4 py-3">
|
|
518
521
|
<div className="flex flex-col gap-1">
|
|
519
|
-
<
|
|
522
|
+
<IconButton
|
|
520
523
|
type="button"
|
|
521
|
-
|
|
524
|
+
variant="ghost"
|
|
525
|
+
size="xs"
|
|
526
|
+
className="text-muted-foreground hover:text-foreground"
|
|
522
527
|
onClick={() => moveStage(index, 'up')}
|
|
523
528
|
disabled={index === 0}
|
|
524
529
|
aria-label={t('customers.config.pipelineStages.moveUp', 'Move up')}
|
|
525
530
|
>
|
|
526
|
-
|
|
527
|
-
</
|
|
528
|
-
<
|
|
531
|
+
<ChevronUp className="size-4" />
|
|
532
|
+
</IconButton>
|
|
533
|
+
<IconButton
|
|
529
534
|
type="button"
|
|
530
|
-
|
|
535
|
+
variant="ghost"
|
|
536
|
+
size="xs"
|
|
537
|
+
className="text-muted-foreground hover:text-foreground"
|
|
531
538
|
onClick={() => moveStage(index, 'down')}
|
|
532
539
|
disabled={index === stages.length - 1}
|
|
533
540
|
aria-label={t('customers.config.pipelineStages.moveDown', 'Move down')}
|
|
534
541
|
>
|
|
535
|
-
|
|
536
|
-
</
|
|
542
|
+
<ChevronDown className="size-4" />
|
|
543
|
+
</IconButton>
|
|
537
544
|
</div>
|
|
538
545
|
<span className="flex-1 text-sm flex items-center gap-2">
|
|
539
546
|
{stage.color ? renderDictionaryColor(stage.color) : null}
|
|
@@ -583,11 +590,11 @@ export default function PipelineStagesPage() {
|
|
|
583
590
|
autoFocus
|
|
584
591
|
/>
|
|
585
592
|
</div>
|
|
586
|
-
<label className="flex items-center gap-2 text-sm cursor-pointer">
|
|
587
|
-
<
|
|
588
|
-
|
|
593
|
+
<label htmlFor="pipeline-is-default" className="flex items-center gap-2 text-sm cursor-pointer">
|
|
594
|
+
<Checkbox
|
|
595
|
+
id="pipeline-is-default"
|
|
589
596
|
checked={pipelineIsDefault}
|
|
590
|
-
|
|
597
|
+
onCheckedChange={(checked) => setPipelineIsDefault(checked === true)}
|
|
591
598
|
/>
|
|
592
599
|
{t('customers.config.pipelineStages.setAsDefault', 'Set as default pipeline')}
|
|
593
600
|
</label>
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import * as React from 'react'
|
|
4
4
|
import { Check, Phone, Mail, Users, CheckSquare } from 'lucide-react'
|
|
5
5
|
import { useT } from '@open-mercato/shared/lib/i18n/context'
|
|
6
|
+
import { Button } from '@open-mercato/ui/primitives/button'
|
|
6
7
|
import { Popover, PopoverContent, PopoverTrigger } from '@open-mercato/ui/primitives/popover'
|
|
7
8
|
|
|
8
9
|
export type ActivityKind = 'meeting' | 'call' | 'task' | 'email'
|
|
@@ -34,28 +35,29 @@ export function ActivitiesAddNewMenu({ onSelect, disabled }: ActivitiesAddNewMen
|
|
|
34
35
|
return (
|
|
35
36
|
<Popover open={open} onOpenChange={setOpen}>
|
|
36
37
|
<PopoverTrigger asChild>
|
|
37
|
-
<
|
|
38
|
+
<Button
|
|
38
39
|
type="button"
|
|
39
40
|
disabled={disabled}
|
|
40
41
|
aria-label={t('customers.activities.addNew', 'Add new')}
|
|
41
|
-
className="
|
|
42
|
+
className="gap-1.5 overflow-hidden rounded-md bg-foreground pl-3 pr-3.5 py-2 text-xs font-semibold text-background hover:bg-foreground/90 disabled:opacity-60"
|
|
42
43
|
>
|
|
43
44
|
<Check className="size-3.5" />
|
|
44
45
|
{t('customers.activities.addNew', 'Add new')}
|
|
45
|
-
</
|
|
46
|
+
</Button>
|
|
46
47
|
</PopoverTrigger>
|
|
47
48
|
<PopoverContent align="end" className="w-[180px] p-1">
|
|
48
49
|
<ul className="flex flex-col">
|
|
49
50
|
{MENU_ITEMS.map(({ kind, icon: Icon, key, fallback }) => (
|
|
50
51
|
<li key={kind}>
|
|
51
|
-
<
|
|
52
|
+
<Button
|
|
52
53
|
type="button"
|
|
54
|
+
variant="ghost"
|
|
53
55
|
onClick={() => handleSelect(kind)}
|
|
54
|
-
className="
|
|
56
|
+
className="h-auto w-full justify-start gap-2 rounded-md px-2 py-1.5 text-sm font-normal text-foreground hover:bg-accent/40"
|
|
55
57
|
>
|
|
56
58
|
<Icon className="size-4 text-muted-foreground" />
|
|
57
59
|
{t(key, fallback)}
|
|
58
|
-
</
|
|
60
|
+
</Button>
|
|
59
61
|
</li>
|
|
60
62
|
))}
|
|
61
63
|
</ul>
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import * as React from 'react'
|
|
4
4
|
import { useT } from '@open-mercato/shared/lib/i18n/context'
|
|
5
5
|
import { AiIcon } from '@open-mercato/ui/ai/AiIcon'
|
|
6
|
+
import { Button } from '@open-mercato/ui/primitives/button'
|
|
6
7
|
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@open-mercato/ui/primitives/tooltip'
|
|
7
8
|
import { AI_TIMELINE_ACTIONS_BY_TYPE, resolveAiActions } from './aiActionCatalog'
|
|
8
9
|
|
|
@@ -23,13 +24,15 @@ export function AiActionChips({ activityType }: AiActionChipsProps) {
|
|
|
23
24
|
{actions.map((action) => (
|
|
24
25
|
<Tooltip key={action.key}>
|
|
25
26
|
<TooltipTrigger asChild>
|
|
26
|
-
<
|
|
27
|
+
<Button
|
|
27
28
|
type="button"
|
|
28
|
-
|
|
29
|
+
variant="outline"
|
|
30
|
+
size="sm"
|
|
31
|
+
className="h-auto gap-1 rounded-[4px] border-dashed bg-card pl-1.5 pr-[7px] py-[3px] text-[9px] font-medium text-muted-foreground/70 shadow-none hover:border-muted-foreground hover:text-foreground"
|
|
29
32
|
>
|
|
30
33
|
<AiIcon className="size-2.5 shrink-0" />
|
|
31
34
|
{t(action.i18nKey, action.fallback)}
|
|
32
|
-
</
|
|
35
|
+
</Button>
|
|
33
36
|
</TooltipTrigger>
|
|
34
37
|
<TooltipContent side="bottom" className="text-xs">
|
|
35
38
|
{t('customers.ai.comingSoon', 'Coming soon')}
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
3
|
import * as React from 'react'
|
|
4
|
-
import { Users,
|
|
4
|
+
import { Users, X, Clock, CheckCircle2, XCircle } from 'lucide-react'
|
|
5
5
|
import { cn } from '@open-mercato/shared/lib/utils'
|
|
6
6
|
import { useT } from '@open-mercato/shared/lib/i18n/context'
|
|
7
7
|
import { Button } from '@open-mercato/ui/primitives/button'
|
|
8
|
+
import { Checkbox } from '@open-mercato/ui/primitives/checkbox'
|
|
8
9
|
import { IconButton } from '@open-mercato/ui/primitives/icon-button'
|
|
9
10
|
import { Popover, PopoverContent, PopoverTrigger } from '@open-mercato/ui/primitives/popover'
|
|
11
|
+
import { SearchInput } from '@open-mercato/ui/primitives/search-input'
|
|
10
12
|
import { fetchAssignableStaffMembersPage } from '../assignableStaff'
|
|
11
13
|
import type { ActivityType, ScheduleFieldId } from './fieldConfig'
|
|
12
14
|
import { isVisible, getFieldLabel } from './fieldConfig'
|
|
@@ -84,17 +86,13 @@ function ParticipantSearchPopover({
|
|
|
84
86
|
</Button>
|
|
85
87
|
</PopoverTrigger>
|
|
86
88
|
<PopoverContent align="start" className="w-72 p-2">
|
|
87
|
-
<
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
className="flex-1 bg-transparent text-sm focus:outline-none"
|
|
95
|
-
autoFocus
|
|
96
|
-
/>
|
|
97
|
-
</div>
|
|
89
|
+
<SearchInput
|
|
90
|
+
value={query}
|
|
91
|
+
onChange={setQuery}
|
|
92
|
+
placeholder={t('customers.schedule.searchParticipant', 'Search team members...')}
|
|
93
|
+
className="mb-2"
|
|
94
|
+
autoFocus
|
|
95
|
+
/>
|
|
98
96
|
{selectableResults.length ? (
|
|
99
97
|
<div className="mb-2">
|
|
100
98
|
<Button
|
|
@@ -228,16 +226,16 @@ export function ParticipantsField({
|
|
|
228
226
|
{participants.length > 0 && (
|
|
229
227
|
<div className="mt-3 flex flex-wrap items-center gap-x-[16px] gap-y-[6px] text-xs">
|
|
230
228
|
<span className="font-medium text-muted-foreground">{t('customers.schedule.guestPermissions', 'Guest permissions:')}</span>
|
|
231
|
-
<label className="flex items-center gap-1.5 cursor-pointer">
|
|
232
|
-
<
|
|
229
|
+
<label htmlFor="guest-perm-invite" className="flex items-center gap-1.5 cursor-pointer">
|
|
230
|
+
<Checkbox id="guest-perm-invite" checked={guestPermissions.canInviteOthers} onCheckedChange={(checked) => setGuestPermissions((p) => ({ ...p, canInviteOthers: checked === true }))} />
|
|
233
231
|
{t('customers.schedule.guestPerm.invite', 'Invite others')}
|
|
234
232
|
</label>
|
|
235
|
-
<label className="flex items-center gap-1.5 cursor-pointer">
|
|
236
|
-
<
|
|
233
|
+
<label htmlFor="guest-perm-modify" className="flex items-center gap-1.5 cursor-pointer">
|
|
234
|
+
<Checkbox id="guest-perm-modify" checked={guestPermissions.canModify} onCheckedChange={(checked) => setGuestPermissions((p) => ({ ...p, canModify: checked === true }))} />
|
|
237
235
|
{t('customers.schedule.guestPerm.modify', 'Modify')}
|
|
238
236
|
</label>
|
|
239
|
-
<label className="flex items-center gap-1.5 cursor-pointer">
|
|
240
|
-
<
|
|
237
|
+
<label htmlFor="guest-perm-see-list" className="flex items-center gap-1.5 cursor-pointer">
|
|
238
|
+
<Checkbox id="guest-perm-see-list" checked={guestPermissions.canSeeList} onCheckedChange={(checked) => setGuestPermissions((p) => ({ ...p, canSeeList: checked === true }))} />
|
|
241
239
|
{t('customers.schedule.guestPerm.seeList', 'See list')}
|
|
242
240
|
</label>
|
|
243
241
|
</div>
|
|
@@ -79,7 +79,7 @@ export async function POST(req: Request, ctx: { params?: Promise<{ id?: string }
|
|
|
79
79
|
} catch (error) {
|
|
80
80
|
const job = await progressService.getJob(run.progressJobId, progressCtx)
|
|
81
81
|
const cancelRequested = job && (job.status === 'running' || job.status === 'cancelled')
|
|
82
|
-
? await progressService.isCancellationRequested(run.progressJobId, progressCtx.tenantId)
|
|
82
|
+
? await progressService.isCancellationRequested(run.progressJobId, progressCtx.tenantId, progressCtx.organizationId)
|
|
83
83
|
: false
|
|
84
84
|
|
|
85
85
|
if (job?.status !== 'cancelled' && !cancelRequested) {
|
|
@@ -7,7 +7,8 @@ import { DataTable } from '@open-mercato/ui/backend/DataTable'
|
|
|
7
7
|
import type { ColumnDef } from '@tanstack/react-table'
|
|
8
8
|
import type { FilterDef, FilterValues } from '@open-mercato/ui/backend/FilterBar'
|
|
9
9
|
import { useGuardedMutation } from '@open-mercato/ui/backend/injection/useGuardedMutation'
|
|
10
|
-
import { Badge
|
|
10
|
+
import { Badge } from '@open-mercato/ui/primitives/badge'
|
|
11
|
+
import { StatusBadge } from '@open-mercato/ui/primitives/status-badge'
|
|
11
12
|
import { Card, CardContent, CardHeader, CardTitle } from '@open-mercato/ui/primitives/card'
|
|
12
13
|
import { Button } from '@open-mercato/ui/primitives/button'
|
|
13
14
|
import { Input } from '@open-mercato/ui/primitives/input'
|
|
@@ -40,6 +41,7 @@ import {
|
|
|
40
41
|
Settings2,
|
|
41
42
|
ShieldCheck,
|
|
42
43
|
} from 'lucide-react'
|
|
44
|
+
import { getSyncRunStatusVariant, getSyncSummaryVariant } from '../../lib/syncRunStatus'
|
|
43
45
|
|
|
44
46
|
type SyncRunRow = {
|
|
45
47
|
id: string
|
|
@@ -107,57 +109,8 @@ type SyncScheduleEditorState = {
|
|
|
107
109
|
updatedAt?: string | null
|
|
108
110
|
}
|
|
109
111
|
|
|
110
|
-
const STATUS_STYLES: Record<string, string> = {
|
|
111
|
-
pending: 'bg-gray-100 text-gray-800',
|
|
112
|
-
running: 'bg-blue-100 text-blue-800',
|
|
113
|
-
completed: 'bg-green-100 text-green-800',
|
|
114
|
-
failed: 'bg-red-100 text-red-800',
|
|
115
|
-
cancelled: 'bg-yellow-100 text-yellow-800',
|
|
116
|
-
paused: 'bg-orange-100 text-orange-800',
|
|
117
|
-
}
|
|
118
|
-
|
|
119
112
|
const DEFAULT_TIMEZONE = Intl.DateTimeFormat().resolvedOptions().timeZone || 'UTC'
|
|
120
113
|
|
|
121
|
-
type SummaryBadgeStyle = {
|
|
122
|
-
variant: BadgeProps['variant']
|
|
123
|
-
className?: string
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
function getSummaryBadgeStyle(kind: 'enabled' | 'disabled' | 'ready' | 'missing' | 'scheduled' | 'paused' | 'none'): SummaryBadgeStyle {
|
|
127
|
-
if (kind === 'enabled' || kind === 'ready') {
|
|
128
|
-
return {
|
|
129
|
-
variant: 'outline',
|
|
130
|
-
className: 'border-emerald-500/30 bg-emerald-500/15 text-emerald-200',
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
if (kind === 'disabled' || kind === 'missing') {
|
|
135
|
-
return {
|
|
136
|
-
variant: 'outline',
|
|
137
|
-
className: 'border-red-500/30 bg-red-500/15 text-red-200',
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
if (kind === 'paused') {
|
|
142
|
-
return {
|
|
143
|
-
variant: 'outline',
|
|
144
|
-
className: 'border-amber-500/30 bg-amber-500/15 text-amber-200',
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
if (kind === 'scheduled') {
|
|
149
|
-
return {
|
|
150
|
-
variant: 'outline',
|
|
151
|
-
className: 'border-sky-500/30 bg-sky-500/15 text-sky-200',
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
return {
|
|
156
|
-
variant: 'outline',
|
|
157
|
-
className: 'border-muted-foreground/20 bg-muted/50 text-muted-foreground',
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
|
|
161
114
|
function formatEntityTypeLabel(entityType: string): string {
|
|
162
115
|
return entityType
|
|
163
116
|
.replace(/[_-]+/g, ' ')
|
|
@@ -589,9 +542,9 @@ export default function SyncRunsDashboardPage() {
|
|
|
589
542
|
accessorKey: 'status',
|
|
590
543
|
header: t('data_sync.dashboard.columns.status'),
|
|
591
544
|
cell: ({ row }) => (
|
|
592
|
-
<
|
|
545
|
+
<StatusBadge variant={getSyncRunStatusVariant(row.original.status)}>
|
|
593
546
|
{t(`data_sync.dashboard.status.${row.original.status}`)}
|
|
594
|
-
</
|
|
547
|
+
</StatusBadge>
|
|
595
548
|
),
|
|
596
549
|
},
|
|
597
550
|
{
|
|
@@ -622,9 +575,9 @@ export default function SyncRunsDashboardPage() {
|
|
|
622
575
|
)
|
|
623
576
|
const hasSavedSchedule = Boolean(scheduleEditor.id)
|
|
624
577
|
const selectedEntityLabel = selectedEntityType ? formatEntityTypeLabel(selectedEntityType) : t('data_sync.dashboard.columns.entityType')
|
|
625
|
-
const
|
|
626
|
-
const
|
|
627
|
-
const
|
|
578
|
+
const integrationStateVariant = getSyncSummaryVariant(selectedIntegration?.isEnabled ? 'enabled' : 'disabled')
|
|
579
|
+
const credentialsVariant = getSyncSummaryVariant(selectedIntegration?.hasCredentials ? 'ready' : 'missing')
|
|
580
|
+
const scheduleVariant = getSyncSummaryVariant(
|
|
628
581
|
hasSavedSchedule
|
|
629
582
|
? (scheduleEditor.isEnabled ? 'scheduled' : 'paused')
|
|
630
583
|
: 'none',
|
|
@@ -668,19 +621,19 @@ export default function SyncRunsDashboardPage() {
|
|
|
668
621
|
<ArrowRightLeft className="size-3.5" />
|
|
669
622
|
{t(`data_sync.dashboard.direction.${selectedDirection}`)}
|
|
670
623
|
</Badge>
|
|
671
|
-
<Badge variant={
|
|
624
|
+
<Badge variant={integrationStateVariant} className="gap-1.5">
|
|
672
625
|
<ShieldCheck className="size-3.5" />
|
|
673
626
|
{selectedIntegration.isEnabled
|
|
674
627
|
? t('data_sync.dashboard.start.status.enabled', 'Integration enabled')
|
|
675
628
|
: t('data_sync.dashboard.start.status.disabled', 'Integration disabled')}
|
|
676
629
|
</Badge>
|
|
677
|
-
<Badge variant={
|
|
630
|
+
<Badge variant={credentialsVariant} className="gap-1.5">
|
|
678
631
|
<PlugZap className="size-3.5" />
|
|
679
632
|
{selectedIntegration.hasCredentials
|
|
680
633
|
? t('data_sync.dashboard.start.status.credentialsReady', 'Credentials ready')
|
|
681
634
|
: t('data_sync.dashboard.start.status.credentialsMissing', 'Credentials missing')}
|
|
682
635
|
</Badge>
|
|
683
|
-
<Badge variant={
|
|
636
|
+
<Badge variant={scheduleVariant} className="gap-1.5">
|
|
684
637
|
<CalendarClock className="size-3.5" />
|
|
685
638
|
{hasSavedSchedule
|
|
686
639
|
? (scheduleEditor.isEnabled
|