@open-mercato/core 0.4.5-develop-610fbb24ec → 0.4.5-develop-811deeb983
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/dist/modules/catalog/backend/catalog/categories/[id]/edit/page.js +17 -2
- package/dist/modules/catalog/backend/catalog/categories/[id]/edit/page.js.map +2 -2
- package/dist/modules/catalog/backend/catalog/products/[id]/page.js +15 -0
- package/dist/modules/catalog/backend/catalog/products/[id]/page.js.map +2 -2
- package/dist/modules/catalog/backend/catalog/products/[productId]/variants/[variantId]/page.js +30 -0
- package/dist/modules/catalog/backend/catalog/products/[productId]/variants/[variantId]/page.js.map +2 -2
- package/dist/modules/catalog/data/validators.js +4 -3
- package/dist/modules/catalog/data/validators.js.map +2 -2
- package/dist/modules/catalog/lib/messageObjectPreviews.js +146 -0
- package/dist/modules/catalog/lib/messageObjectPreviews.js.map +7 -0
- package/dist/modules/catalog/message-objects.js +95 -0
- package/dist/modules/catalog/message-objects.js.map +7 -0
- package/dist/modules/currencies/backend/currencies/[id]/page.js +21 -0
- package/dist/modules/currencies/backend/currencies/[id]/page.js.map +2 -2
- package/dist/modules/currencies/lib/messageObjectPreviews.js +51 -0
- package/dist/modules/currencies/lib/messageObjectPreviews.js.map +7 -0
- package/dist/modules/currencies/message-objects.js +41 -0
- package/dist/modules/currencies/message-objects.js.map +7 -0
- package/dist/modules/customers/backend/customers/companies/[id]/page.js +20 -0
- package/dist/modules/customers/backend/customers/companies/[id]/page.js.map +2 -2
- package/dist/modules/customers/backend/customers/deals/[id]/page.js +12 -1
- package/dist/modules/customers/backend/customers/deals/[id]/page.js.map +2 -2
- package/dist/modules/customers/backend/customers/people/[id]/page.js +20 -0
- package/dist/modules/customers/backend/customers/people/[id]/page.js.map +2 -2
- package/dist/modules/customers/components/detail/CompanyHighlights.js +18 -14
- package/dist/modules/customers/components/detail/CompanyHighlights.js.map +2 -2
- package/dist/modules/customers/components/detail/PersonHighlights.js +18 -14
- package/dist/modules/customers/components/detail/PersonHighlights.js.map +2 -2
- package/dist/modules/customers/lib/messageObjectPreviews.js +41 -5
- package/dist/modules/customers/lib/messageObjectPreviews.js.map +2 -2
- package/dist/modules/customers/message-objects.js +31 -11
- package/dist/modules/customers/message-objects.js.map +2 -2
- package/dist/modules/messages/commands/messages.js +3 -0
- package/dist/modules/messages/commands/messages.js.map +2 -2
- package/dist/modules/messages/components/message-detail/panels/objects-panel.js +6 -1
- package/dist/modules/messages/components/message-detail/panels/objects-panel.js.map +2 -2
- package/dist/modules/messages/components/message-detail/panels/thread-panel.js +4 -1
- package/dist/modules/messages/components/message-detail/panels/thread-panel.js.map +2 -2
- package/dist/modules/messages/frontend/messages/view/[token]/page.js +1 -0
- package/dist/modules/messages/frontend/messages/view/[token]/page.js.map +2 -2
- package/dist/modules/resources/backend/resources/resources/[id]/page.js +24 -7
- package/dist/modules/resources/backend/resources/resources/[id]/page.js.map +2 -2
- package/dist/modules/resources/lib/messageObjectPreviews.js +43 -0
- package/dist/modules/resources/lib/messageObjectPreviews.js.map +7 -0
- package/dist/modules/resources/message-objects.js +37 -0
- package/dist/modules/resources/message-objects.js.map +7 -0
- package/dist/modules/sales/backend/sales/channels/[channelId]/edit/page.js +19 -0
- package/dist/modules/sales/backend/sales/channels/[channelId]/edit/page.js.map +2 -2
- package/dist/modules/sales/backend/sales/documents/[id]/page.js +23 -2
- package/dist/modules/sales/backend/sales/documents/[id]/page.js.map +2 -2
- package/dist/modules/sales/backend/sales/quotes/[id]/page.js +1 -1
- package/dist/modules/sales/backend/sales/quotes/[id]/page.js.map +2 -2
- package/dist/modules/sales/lib/messageObjectPreviews.js +49 -4
- package/dist/modules/sales/lib/messageObjectPreviews.js.map +2 -2
- package/dist/modules/sales/message-objects.js +44 -2
- package/dist/modules/sales/message-objects.js.map +2 -2
- package/dist/modules/sales/widgets/messages/SalesDocumentMessageDetail.js +59 -30
- package/dist/modules/sales/widgets/messages/SalesDocumentMessageDetail.js.map +2 -2
- package/dist/modules/sales/widgets/messages/SalesDocumentMessagePreview.js +1 -1
- package/dist/modules/sales/widgets/messages/SalesDocumentMessagePreview.js.map +1 -1
- package/dist/modules/staff/backend/staff/leave-requests/[id]/page.js +8 -30
- package/dist/modules/staff/backend/staff/leave-requests/[id]/page.js.map +2 -2
- package/dist/modules/staff/backend/staff/my-availability/page.js +13 -0
- package/dist/modules/staff/backend/staff/my-availability/page.js.map +2 -2
- package/dist/modules/staff/backend/staff/my-leave-requests/[id]/page.js +8 -31
- package/dist/modules/staff/backend/staff/my-leave-requests/[id]/page.js.map +2 -2
- package/dist/modules/staff/backend/staff/team-members/[id]/page.js +32 -10
- package/dist/modules/staff/backend/staff/team-members/[id]/page.js.map +2 -2
- package/dist/modules/staff/backend/staff/team-roles/[id]/edit/page.js +14 -1
- package/dist/modules/staff/backend/staff/team-roles/[id]/edit/page.js.map +2 -2
- package/dist/modules/staff/backend/staff/teams/[id]/edit/page.js +14 -1
- package/dist/modules/staff/backend/staff/teams/[id]/edit/page.js.map +2 -2
- package/dist/modules/staff/components/TeamForm.js +4 -2
- package/dist/modules/staff/components/TeamForm.js.map +2 -2
- package/dist/modules/staff/components/TeamRoleForm.js +4 -2
- package/dist/modules/staff/components/TeamRoleForm.js.map +2 -2
- package/dist/modules/staff/lib/messageObjectPreviews.js +111 -2
- package/dist/modules/staff/lib/messageObjectPreviews.js.map +2 -2
- package/dist/modules/staff/message-objects.js +79 -8
- package/dist/modules/staff/message-objects.js.map +2 -2
- package/package.json +3 -3
- package/src/modules/catalog/backend/catalog/categories/[id]/edit/page.tsx +19 -5
- package/src/modules/catalog/backend/catalog/products/[id]/page.tsx +14 -0
- package/src/modules/catalog/backend/catalog/products/[productId]/variants/[variantId]/page.tsx +40 -0
- package/src/modules/catalog/data/validators.ts +47 -45
- package/src/modules/catalog/lib/messageObjectPreviews.ts +176 -0
- package/src/modules/catalog/message-objects.ts +102 -0
- package/src/modules/currencies/backend/currencies/[id]/page.tsx +20 -0
- package/src/modules/currencies/lib/messageObjectPreviews.ts +65 -0
- package/src/modules/currencies/message-objects.ts +40 -0
- package/src/modules/customers/backend/customers/companies/[id]/page.tsx +19 -0
- package/src/modules/customers/backend/customers/deals/[id]/page.tsx +13 -0
- package/src/modules/customers/backend/customers/people/[id]/page.tsx +19 -0
- package/src/modules/customers/components/detail/CompanyHighlights.tsx +14 -9
- package/src/modules/customers/components/detail/PersonHighlights.tsx +14 -9
- package/src/modules/customers/lib/messageObjectPreviews.ts +43 -3
- package/src/modules/customers/message-objects.ts +31 -11
- package/src/modules/customers/migrations/.snapshot-open-mercato.json +236 -0
- package/src/modules/customers/migrations/.snapshot-openmercato.json +236 -0
- package/src/modules/messages/commands/messages.ts +4 -0
- package/src/modules/messages/components/message-detail/panels/objects-panel.tsx +8 -1
- package/src/modules/messages/components/message-detail/panels/thread-panel.tsx +3 -0
- package/src/modules/messages/frontend/messages/view/[token]/page.tsx +1 -0
- package/src/modules/resources/backend/resources/resources/[id]/page.tsx +20 -4
- package/src/modules/resources/lib/messageObjectPreviews.ts +55 -0
- package/src/modules/resources/message-objects.ts +36 -0
- package/src/modules/sales/backend/sales/channels/[channelId]/edit/page.tsx +18 -0
- package/src/modules/sales/backend/sales/documents/[id]/page.tsx +23 -0
- package/src/modules/sales/backend/sales/quotes/[id]/page.tsx +1 -1
- package/src/modules/sales/lib/messageObjectPreviews.ts +54 -4
- package/src/modules/sales/message-objects.ts +44 -2
- package/src/modules/sales/widgets/messages/SalesDocumentMessageDetail.tsx +72 -34
- package/src/modules/sales/widgets/messages/SalesDocumentMessagePreview.tsx +1 -1
- package/src/modules/staff/backend/staff/leave-requests/[id]/page.tsx +7 -29
- package/src/modules/staff/backend/staff/my-availability/page.tsx +14 -0
- package/src/modules/staff/backend/staff/my-leave-requests/[id]/page.tsx +8 -30
- package/src/modules/staff/backend/staff/team-members/[id]/page.tsx +28 -7
- package/src/modules/staff/backend/staff/team-roles/[id]/edit/page.tsx +12 -0
- package/src/modules/staff/backend/staff/teams/[id]/edit/page.tsx +12 -0
- package/src/modules/staff/components/TeamForm.tsx +3 -0
- package/src/modules/staff/components/TeamRoleForm.tsx +3 -0
- package/src/modules/staff/lib/messageObjectPreviews.ts +133 -2
- package/src/modules/staff/message-objects.ts +79 -8
- package/dist/modules/customers/widgets/messages/CustomerMessageObjectDetail.js +0 -51
- package/dist/modules/customers/widgets/messages/CustomerMessageObjectDetail.js.map +0 -7
- package/dist/modules/customers/widgets/messages/CustomerMessageObjectPreview.js +0 -35
- package/dist/modules/customers/widgets/messages/CustomerMessageObjectPreview.js.map +0 -7
- package/dist/modules/customers/widgets/messages/index.js +0 -7
- package/dist/modules/customers/widgets/messages/index.js.map +0 -7
- package/dist/modules/staff/widgets/messages/StaffMessageObjectDetail.js +0 -51
- package/dist/modules/staff/widgets/messages/StaffMessageObjectDetail.js.map +0 -7
- package/dist/modules/staff/widgets/messages/StaffMessageObjectPreview.js +0 -34
- package/dist/modules/staff/widgets/messages/StaffMessageObjectPreview.js.map +0 -7
- package/dist/modules/staff/widgets/messages/index.js +0 -7
- package/dist/modules/staff/widgets/messages/index.js.map +0 -7
- package/src/modules/customers/widgets/messages/CustomerMessageObjectDetail.tsx +0 -57
- package/src/modules/customers/widgets/messages/CustomerMessageObjectPreview.tsx +0 -49
- package/src/modules/customers/widgets/messages/index.ts +0 -2
- package/src/modules/staff/widgets/messages/StaffMessageObjectDetail.tsx +0 -57
- package/src/modules/staff/widgets/messages/StaffMessageObjectPreview.tsx +0 -44
- package/src/modules/staff/widgets/messages/index.ts +0 -2
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../src/modules/messages/components/message-detail/panels/thread-panel.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\n\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { MarkdownContent } from '@open-mercato/ui/backend/markdown/MarkdownContent'\nimport { getMessageUiComponentRegistry } from '../../utils/typeUiRegistry'\nimport type { MessageDetail, MessageDetailObject } from '../types'\nimport { formatDateTime } from '../utils'\n\nexport function MessageDetailThreadSection({ detail }: { detail: MessageDetail }) {\n const t = useT()\n const messageUiRegistry = getMessageUiComponentRegistry()\n\n if ((detail.thread ?? []).length === 0) return null\n\n return (\n <section className=\"space-y-3 border-l pl-4 py-2\">\n <h2 className=\"text-base font-semibold\">{t('messages.detail.thread', 'Thread')}</h2>\n <div className=\"space-y-3\">\n {(detail.thread ?? []).map((threadItem) => (\n <article key={threadItem.id} className=\"rounded border p-3\">\n <p className=\"text-xs text-muted-foreground\">\n {(threadItem.senderName || threadItem.senderEmail || threadItem.senderUserId)} \u2022 {formatDateTime(threadItem.sentAt)}\n </p>\n <div className=\"mt-2 max-h-[60vh] overflow-y-auto pr-1\">\n <MarkdownContent\n body={threadItem.body}\n format={threadItem.bodyFormat ?? 'text'}\n className=\"text-sm whitespace-pre-wrap [&>*]:mb-2 [&>*:last-child]:mb-0 [&_ul]:ml-4 [&_ul]:list-disc [&_ol]:ml-4 [&_ol]:list-decimal [&_code]:rounded [&_code]:bg-muted [&_code]:px-1 [&_code]:py-0.5 [&_pre]:rounded-md [&_pre]:bg-muted [&_pre]:p-3 [&_pre]:text-xs\"\n />\n </div>\n\n {(threadItem.objects ?? []).length > 0 && (\n <div className=\"mt-3 space-y-2\">\n <p className=\"text-xs text-muted-foreground font-medium\">\n {t('messages.attachedObjects', 'Attached objects')}\n </p>\n {(threadItem.objects ?? []).map((obj: MessageDetailObject) => {\n const componentKey = `${obj.entityModule}:${obj.entityType}`\n const PreviewComponent = messageUiRegistry.objectPreviewComponents[componentKey]\n ?? messageUiRegistry.objectPreviewComponents['messages:default']\n if (!PreviewComponent) return null\n\n return (\n <div key={obj.id} className=\"scale-95 origin-left\">\n <PreviewComponent\n entityId={obj.entityId}\n entityModule={obj.entityModule}\n entityType={obj.entityType}\n snapshot={obj.snapshot ?? undefined}\n previewData={obj.preview ?? undefined}\n actionRequired={obj.actionRequired}\n actionType={obj.actionType ?? undefined}\n actionLabel={obj.actionLabel ?? undefined}\n />\n </div>\n )\n })}\n </div>\n )}\n </article>\n ))}\n </div>\n </section>\n )\n}\n"],
|
|
5
|
-
"mappings": ";
|
|
4
|
+
"sourcesContent": ["\"use client\"\n\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { MarkdownContent } from '@open-mercato/ui/backend/markdown/MarkdownContent'\nimport { getMessageUiComponentRegistry } from '../../utils/typeUiRegistry'\nimport { getMessageObjectType } from '../../../lib/message-objects-registry'\nimport type { MessageDetail, MessageDetailObject } from '../types'\nimport { formatDateTime } from '../utils'\n\nexport function MessageDetailThreadSection({ detail }: { detail: MessageDetail }) {\n const t = useT()\n const messageUiRegistry = getMessageUiComponentRegistry()\n\n if ((detail.thread ?? []).length === 0) return null\n\n return (\n <section className=\"space-y-3 border-l pl-4 py-2\">\n <h2 className=\"text-base font-semibold\">{t('messages.detail.thread', 'Thread')}</h2>\n <div className=\"space-y-3\">\n {(detail.thread ?? []).map((threadItem) => (\n <article key={threadItem.id} className=\"rounded border p-3\">\n <p className=\"text-xs text-muted-foreground\">\n {(threadItem.senderName || threadItem.senderEmail || threadItem.senderUserId)} \u2022 {formatDateTime(threadItem.sentAt)}\n </p>\n <div className=\"mt-2 max-h-[60vh] overflow-y-auto pr-1\">\n <MarkdownContent\n body={threadItem.body}\n format={threadItem.bodyFormat ?? 'text'}\n className=\"text-sm whitespace-pre-wrap [&>*]:mb-2 [&>*:last-child]:mb-0 [&_ul]:ml-4 [&_ul]:list-disc [&_ol]:ml-4 [&_ol]:list-decimal [&_code]:rounded [&_code]:bg-muted [&_code]:px-1 [&_code]:py-0.5 [&_pre]:rounded-md [&_pre]:bg-muted [&_pre]:p-3 [&_pre]:text-xs\"\n />\n </div>\n\n {(threadItem.objects ?? []).length > 0 && (\n <div className=\"mt-3 space-y-2\">\n <p className=\"text-xs text-muted-foreground font-medium\">\n {t('messages.attachedObjects', 'Attached objects')}\n </p>\n {(threadItem.objects ?? []).map((obj: MessageDetailObject) => {\n const componentKey = `${obj.entityModule}:${obj.entityType}`\n const PreviewComponent = messageUiRegistry.objectPreviewComponents[componentKey]\n ?? messageUiRegistry.objectPreviewComponents['messages:default']\n const objectType = getMessageObjectType(obj.entityModule, obj.entityType)\n if (!PreviewComponent) return null\n\n return (\n <div key={obj.id} className=\"scale-95 origin-left\">\n <PreviewComponent\n entityId={obj.entityId}\n entityModule={obj.entityModule}\n entityType={obj.entityType}\n snapshot={obj.snapshot ?? undefined}\n previewData={obj.preview ?? undefined}\n actionRequired={obj.actionRequired}\n actionType={obj.actionType ?? undefined}\n actionLabel={obj.actionLabel ?? undefined}\n icon={objectType?.icon}\n />\n </div>\n )\n })}\n </div>\n )}\n </article>\n ))}\n </div>\n </section>\n )\n}\n"],
|
|
5
|
+
"mappings": ";AAiBM,cAIM,YAJN;AAfN,SAAS,YAAY;AACrB,SAAS,uBAAuB;AAChC,SAAS,qCAAqC;AAC9C,SAAS,4BAA4B;AAErC,SAAS,sBAAsB;AAExB,SAAS,2BAA2B,EAAE,OAAO,GAA8B;AAChF,QAAM,IAAI,KAAK;AACf,QAAM,oBAAoB,8BAA8B;AAExD,OAAK,OAAO,UAAU,CAAC,GAAG,WAAW,EAAG,QAAO;AAE/C,SACE,qBAAC,aAAQ,WAAU,gCACjB;AAAA,wBAAC,QAAG,WAAU,2BAA2B,YAAE,0BAA0B,QAAQ,GAAE;AAAA,IAC/E,oBAAC,SAAI,WAAU,aACX,kBAAO,UAAU,CAAC,GAAG,IAAI,CAAC,eAC1B,qBAAC,aAA4B,WAAU,sBACrC;AAAA,2BAAC,OAAE,WAAU,iCACT;AAAA,mBAAW,cAAc,WAAW,eAAe,WAAW;AAAA,QAAc;AAAA,QAAI,eAAe,WAAW,MAAM;AAAA,SACpH;AAAA,MACA,oBAAC,SAAI,WAAU,0CACb;AAAA,QAAC;AAAA;AAAA,UACC,MAAM,WAAW;AAAA,UACjB,QAAQ,WAAW,cAAc;AAAA,UACjC,WAAU;AAAA;AAAA,MACZ,GACF;AAAA,OAEE,WAAW,WAAW,CAAC,GAAG,SAAS,KACnC,qBAAC,SAAI,WAAU,kBACb;AAAA,4BAAC,OAAE,WAAU,6CACV,YAAE,4BAA4B,kBAAkB,GACnD;AAAA,SACE,WAAW,WAAW,CAAC,GAAG,IAAI,CAAC,QAA6B;AAC5D,gBAAM,eAAe,GAAG,IAAI,YAAY,IAAI,IAAI,UAAU;AAC1D,gBAAM,mBAAmB,kBAAkB,wBAAwB,YAAY,KAC1E,kBAAkB,wBAAwB,kBAAkB;AACjE,gBAAM,aAAa,qBAAqB,IAAI,cAAc,IAAI,UAAU;AACxE,cAAI,CAAC,iBAAkB,QAAO;AAE9B,iBACE,oBAAC,SAAiB,WAAU,wBAC1B;AAAA,YAAC;AAAA;AAAA,cACC,UAAU,IAAI;AAAA,cACd,cAAc,IAAI;AAAA,cAClB,YAAY,IAAI;AAAA,cAChB,UAAU,IAAI,YAAY;AAAA,cAC1B,aAAa,IAAI,WAAW;AAAA,cAC5B,gBAAgB,IAAI;AAAA,cACpB,YAAY,IAAI,cAAc;AAAA,cAC9B,aAAa,IAAI,eAAe;AAAA,cAChC,MAAM,YAAY;AAAA;AAAA,UACpB,KAXQ,IAAI,EAYd;AAAA,QAEJ,CAAC;AAAA,SACH;AAAA,SAxCU,WAAW,EA0CzB,CACD,GACH;AAAA,KACF;AAEJ;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -231,6 +231,7 @@ function MessageTokenPage({ params }) {
|
|
|
231
231
|
actionTaken: data.actionTaken ?? null,
|
|
232
232
|
actionTakenAt: data.actionTakenAt ? new Date(data.actionTakenAt) : null,
|
|
233
233
|
actionTakenByUserId: data.actionTakenByUserId ?? null,
|
|
234
|
+
icon: objectType?.icon,
|
|
234
235
|
onAction: async () => {
|
|
235
236
|
return;
|
|
236
237
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../../src/modules/messages/frontend/messages/view/%5Btoken%5D/page.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport Link from 'next/link'\nimport type {\n MessageContentProps,\n MessageObjectAction,\n} from '@open-mercato/shared/modules/messages/types'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { MarkdownContent } from '@open-mercato/ui/backend/markdown/MarkdownContent'\nimport { apiCall } from '@open-mercato/ui/backend/utils/apiCall'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Spinner } from '@open-mercato/ui/primitives/spinner'\nimport {\n getMessageUiComponentRegistry,\n} from '../../../../components/utils/typeUiRegistry'\nimport { getMessageObjectType } from '../../../../lib/message-objects-registry'\nimport { getMessageTypeOrDefault } from '../../../../lib/message-types-registry'\n\ntype TokenMessageObject = {\n id: string\n entityModule: string\n entityType: string\n entityId: string\n actionRequired: boolean\n actionType?: string | null\n actionLabel?: string | null\n snapshot?: Record<string, unknown> | null\n}\n\ntype MessageTokenResponse = {\n id: string\n type: string\n subject: string\n body: string\n bodyFormat: 'text' | 'markdown'\n priority: 'low' | 'normal' | 'high' | 'urgent'\n senderUserId: string\n sentAt?: string | null\n actionData?: {\n actions: Array<{\n id: string\n label: string\n labelKey?: string\n variant?: 'default' | 'secondary' | 'destructive' | 'outline' | 'ghost'\n icon?: string\n commandId?: string\n href?: string\n isTerminal?: boolean\n confirmRequired?: boolean\n confirmMessage?: string\n }>\n primaryActionId?: string\n expiresAt?: string\n } | null\n actionTaken?: string | null\n actionTakenAt?: string | null\n actionTakenByUserId?: string | null\n objects: TokenMessageObject[]\n requiresAuth: boolean\n recipientUserId: string\n}\n\nfunction toErrorMessage(payload: unknown): string | null {\n if (!payload) return null\n if (typeof payload === 'string') return payload\n if (Array.isArray(payload)) {\n for (const entry of payload) {\n const nested = toErrorMessage(entry)\n if (nested) return nested\n }\n return null\n }\n if (typeof payload === 'object') {\n const record = payload as Record<string, unknown>\n return (\n toErrorMessage(record.error)\n ?? toErrorMessage(record.message)\n ?? toErrorMessage(record.detail)\n ?? toErrorMessage(record.details)\n ?? null\n )\n }\n return null\n}\n\nfunction formatDateTime(value: string | null | undefined): string {\n if (!value) return '\u2014'\n const date = new Date(value)\n if (Number.isNaN(date.getTime())) return '\u2014'\n return date.toLocaleString()\n}\n\nfunction toObjectActions(\n objectActions: Array<{\n id: string\n labelKey: string\n variant?: 'default' | 'secondary' | 'destructive' | 'outline'\n icon?: string\n commandId?: string\n href?: string\n isTerminal?: boolean\n confirmRequired?: boolean\n confirmMessage?: string\n }>,\n): MessageObjectAction[] {\n return objectActions.map((action) => ({\n id: action.id,\n labelKey: action.labelKey,\n variant: action.variant,\n icon: action.icon,\n commandId: action.commandId,\n href: action.href,\n isTerminal: action.isTerminal,\n confirmRequired: action.confirmRequired,\n confirmMessage: action.confirmMessage,\n }))\n}\n\nfunction mergeTokenActions(\n messageActions: MessageTokenResponse['actionData'],\n defaultActions: Array<{\n id: string\n label: string\n labelKey?: string\n variant?: 'default' | 'secondary' | 'destructive' | 'outline' | 'ghost'\n icon?: string\n commandId?: string\n href?: string\n isTerminal?: boolean\n confirmRequired?: boolean\n confirmMessage?: string\n }> | undefined,\n): MessageTokenResponse['actionData'] {\n const deduped = new Map<string, NonNullable<MessageTokenResponse['actionData']>['actions'][number]>()\n\n for (const action of messageActions?.actions ?? []) {\n const normalizedId = action.id.trim()\n if (!normalizedId) continue\n deduped.set(normalizedId, { ...action, id: normalizedId })\n }\n\n for (const action of defaultActions ?? []) {\n const normalizedId = action.id.trim()\n if (!normalizedId || deduped.has(normalizedId)) continue\n deduped.set(normalizedId, { ...action, id: normalizedId })\n }\n\n const actions = Array.from(deduped.values())\n if (actions.length === 0 && !messageActions?.expiresAt) return null\n\n return {\n actions,\n primaryActionId: messageActions?.primaryActionId,\n expiresAt: messageActions?.expiresAt,\n }\n}\n\nexport default function MessageTokenPage({ params }: { params: { token: string } }) {\n const t = useT()\n const messageUiRegistry = React.useMemo(() => getMessageUiComponentRegistry(), [])\n const token = params?.token\n\n const [loading, setLoading] = React.useState(true)\n const [data, setData] = React.useState<MessageTokenResponse | null>(null)\n const [errorMessage, setErrorMessage] = React.useState<string | null>(null)\n const [errorStatus, setErrorStatus] = React.useState<number | null>(null)\n\n React.useEffect(() => {\n let mounted = true\n\n async function run() {\n if (!token) return\n\n setLoading(true)\n setData(null)\n setErrorMessage(null)\n setErrorStatus(null)\n\n try {\n const call = await apiCall<MessageTokenResponse>(`/api/messages/token/${encodeURIComponent(token)}`)\n\n if (!mounted) return\n\n if (!call.ok || !call.result) {\n const status = call.status\n setErrorStatus(status)\n\n const fallback = status === 404\n ? t('messages.token.errors.notFound', 'This message link is invalid or has already been used.')\n : status === 409\n ? t('messages.token.errors.limitExceeded', 'This message link reached its usage limit.')\n : status === 410\n ? t('messages.token.errors.expired', 'This message link has expired.')\n : t('messages.token.errors.generic', 'Unable to load this message.')\n\n setErrorMessage(toErrorMessage(call.result) ?? fallback)\n return\n }\n\n setData(call.result)\n } catch (error) {\n if (!mounted) return\n setErrorMessage(\n error instanceof Error\n ? error.message\n : t('messages.token.errors.generic', 'Unable to load this message.'),\n )\n } finally {\n if (mounted) {\n setLoading(false)\n }\n }\n }\n\n void run()\n\n return () => {\n mounted = false\n }\n }, [t, token])\n\n if (loading) {\n return (\n <main className=\"mx-auto max-w-3xl p-6\">\n <p className=\"flex items-center gap-2 text-sm text-muted-foreground\">\n <Spinner className=\"h-4 w-4 animate-spin\" />\n {t('messages.token.loading', 'Loading message...')}\n </p>\n </main>\n )\n }\n\n if (errorMessage || !data) {\n return (\n <main className=\"mx-auto max-w-3xl space-y-3 p-6\">\n <h1 className=\"text-2xl font-semibold\">{t('messages.token.pageTitle', 'Message')}</h1>\n <p className=\"text-sm text-destructive\">{errorMessage ?? t('messages.token.errors.generic', 'Unable to load this message.')}</p>\n {errorStatus ? <p className=\"text-xs text-muted-foreground\">HTTP {errorStatus}</p> : null}\n </main>\n )\n }\n\n if (data.requiresAuth) {\n return (\n <main className=\"mx-auto max-w-3xl space-y-4 p-6\">\n <h1 className=\"text-2xl font-semibold\">{t('messages.token.authRequired.title', 'Sign in required')}</h1>\n <p className=\"text-sm text-muted-foreground\">\n {t('messages.token.authRequired.description', 'This message includes protected objects. Sign in to continue.')}\n </p>\n <Button asChild>\n <Link href=\"/login\">{t('auth.signIn', 'Sign in')}</Link>\n </Button>\n </main>\n )\n }\n\n const messageType = getMessageTypeOrDefault(data.type)\n const contentComponentKey = messageType.ui?.contentComponent\n const actionsComponentKey = messageType.ui?.actionsComponent\n const ContentComponent = contentComponentKey\n ? messageUiRegistry.contentComponents[contentComponentKey] ?? null\n : null\n const ActionsComponent = actionsComponentKey\n ? messageUiRegistry.actionsComponents[actionsComponentKey] ?? null\n : null\n const resolvedActionData = mergeTokenActions(data.actionData ?? null, messageType.defaultActions)\n\n const contentProps: MessageContentProps = {\n message: {\n id: data.id,\n type: data.type,\n subject: data.subject,\n body: data.body,\n bodyFormat: data.bodyFormat,\n priority: data.priority,\n sentAt: data.sentAt ? new Date(data.sentAt) : null,\n senderUserId: data.senderUserId,\n actionData: resolvedActionData,\n actionTaken: data.actionTaken ?? null,\n actionTakenAt: data.actionTakenAt ? new Date(data.actionTakenAt) : null,\n },\n objects: data.objects.map((objectItem) => ({\n id: objectItem.id,\n entityModule: objectItem.entityModule,\n entityType: objectItem.entityType,\n entityId: objectItem.entityId,\n actionRequired: objectItem.actionRequired,\n snapshot: objectItem.snapshot ?? undefined,\n })),\n attachments: [],\n }\n\n const tokenActions = resolvedActionData?.actions ?? []\n\n return (\n <main className=\"mx-auto max-w-3xl space-y-6 p-6\">\n <header className=\"space-y-1\">\n <h1 className=\"text-2xl font-semibold\">{data.subject}</h1>\n <p className=\"text-sm text-muted-foreground\">\n {t('messages.token.sentAt', 'Sent')}: {formatDateTime(data.sentAt)}\n </p>\n </header>\n\n <section className=\"rounded-xl border bg-card p-4\">\n {ContentComponent ? (\n <ContentComponent {...contentProps} />\n ) : (\n <MarkdownContent\n body={data.body}\n format={data.bodyFormat}\n className=\"text-sm whitespace-pre-wrap [&>*]:mb-2 [&>*:last-child]:mb-0 [&_ul]:ml-4 [&_ul]:list-disc [&_ol]:ml-4 [&_ol]:list-decimal [&_code]:rounded [&_code]:bg-muted [&_code]:px-1 [&_code]:py-0.5 [&_pre]:rounded-md [&_pre]:bg-muted [&_pre]:p-3 [&_pre]:text-xs\"\n />\n )}\n </section>\n\n {tokenActions.length > 0 ? (\n <section className=\"space-y-3 rounded-xl border bg-card p-4\">\n <h2 className=\"text-base font-semibold\">{t('messages.actions.title', 'Actions')}</h2>\n {ActionsComponent ? (\n <ActionsComponent\n message={{\n id: data.id,\n type: data.type,\n actionData: resolvedActionData,\n actionTaken: data.actionTaken ?? null,\n }}\n onExecuteAction={async (_actionId) => {\n return\n }}\n isExecuting={false}\n executingActionId={null}\n />\n ) : (\n <div className=\"flex flex-wrap gap-2\">\n {tokenActions.map((action) => (\n <Button\n key={action.id}\n type=\"button\"\n variant={action.variant ?? 'default'}\n disabled\n >\n {t(action.labelKey ?? action.label, action.label)}\n </Button>\n ))}\n </div>\n )}\n </section>\n ) : null}\n\n <section className=\"space-y-2 rounded-xl border bg-card p-4\">\n <h2 className=\"text-base font-semibold\">{t('messages.attachedObjects', 'Attached objects')}</h2>\n {data.objects.length === 0 ? (\n null\n ) : (\n <div className=\"space-y-2\">\n {data.objects.map((objectItem) => {\n const objectType = getMessageObjectType(objectItem.entityModule, objectItem.entityType)\n const objectActions = toObjectActions(objectType?.actions ?? [])\n const detailComponentKey = `${objectItem.entityModule}:${objectItem.entityType}`\n const DetailComponent = messageUiRegistry.objectDetailComponents[detailComponentKey] ?? null\n\n return DetailComponent ? (\n <DetailComponent\n key={objectItem.id}\n entityId={objectItem.entityId}\n entityModule={objectItem.entityModule}\n entityType={objectItem.entityType}\n snapshot={objectItem.snapshot ?? undefined}\n previewData={undefined}\n actionRequired={objectItem.actionRequired}\n actionType={objectItem.actionType ?? undefined}\n actionLabel={objectItem.actionLabel ?? undefined}\n actions={objectActions}\n actionTaken={data.actionTaken ?? null}\n actionTakenAt={data.actionTakenAt ? new Date(data.actionTakenAt) : null}\n actionTakenByUserId={data.actionTakenByUserId ?? null}\n onAction={async () => {\n return\n }}\n />\n ) : null\n })}\n </div>\n )}\n </section>\n </main>\n )\n}\n"],
|
|
5
|
-
"mappings": "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
|
|
4
|
+
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport Link from 'next/link'\nimport type {\n MessageContentProps,\n MessageObjectAction,\n} from '@open-mercato/shared/modules/messages/types'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { MarkdownContent } from '@open-mercato/ui/backend/markdown/MarkdownContent'\nimport { apiCall } from '@open-mercato/ui/backend/utils/apiCall'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Spinner } from '@open-mercato/ui/primitives/spinner'\nimport {\n getMessageUiComponentRegistry,\n} from '../../../../components/utils/typeUiRegistry'\nimport { getMessageObjectType } from '../../../../lib/message-objects-registry'\nimport { getMessageTypeOrDefault } from '../../../../lib/message-types-registry'\n\ntype TokenMessageObject = {\n id: string\n entityModule: string\n entityType: string\n entityId: string\n actionRequired: boolean\n actionType?: string | null\n actionLabel?: string | null\n snapshot?: Record<string, unknown> | null\n}\n\ntype MessageTokenResponse = {\n id: string\n type: string\n subject: string\n body: string\n bodyFormat: 'text' | 'markdown'\n priority: 'low' | 'normal' | 'high' | 'urgent'\n senderUserId: string\n sentAt?: string | null\n actionData?: {\n actions: Array<{\n id: string\n label: string\n labelKey?: string\n variant?: 'default' | 'secondary' | 'destructive' | 'outline' | 'ghost'\n icon?: string\n commandId?: string\n href?: string\n isTerminal?: boolean\n confirmRequired?: boolean\n confirmMessage?: string\n }>\n primaryActionId?: string\n expiresAt?: string\n } | null\n actionTaken?: string | null\n actionTakenAt?: string | null\n actionTakenByUserId?: string | null\n objects: TokenMessageObject[]\n requiresAuth: boolean\n recipientUserId: string\n}\n\nfunction toErrorMessage(payload: unknown): string | null {\n if (!payload) return null\n if (typeof payload === 'string') return payload\n if (Array.isArray(payload)) {\n for (const entry of payload) {\n const nested = toErrorMessage(entry)\n if (nested) return nested\n }\n return null\n }\n if (typeof payload === 'object') {\n const record = payload as Record<string, unknown>\n return (\n toErrorMessage(record.error)\n ?? toErrorMessage(record.message)\n ?? toErrorMessage(record.detail)\n ?? toErrorMessage(record.details)\n ?? null\n )\n }\n return null\n}\n\nfunction formatDateTime(value: string | null | undefined): string {\n if (!value) return '\u2014'\n const date = new Date(value)\n if (Number.isNaN(date.getTime())) return '\u2014'\n return date.toLocaleString()\n}\n\nfunction toObjectActions(\n objectActions: Array<{\n id: string\n labelKey: string\n variant?: 'default' | 'secondary' | 'destructive' | 'outline'\n icon?: string\n commandId?: string\n href?: string\n isTerminal?: boolean\n confirmRequired?: boolean\n confirmMessage?: string\n }>,\n): MessageObjectAction[] {\n return objectActions.map((action) => ({\n id: action.id,\n labelKey: action.labelKey,\n variant: action.variant,\n icon: action.icon,\n commandId: action.commandId,\n href: action.href,\n isTerminal: action.isTerminal,\n confirmRequired: action.confirmRequired,\n confirmMessage: action.confirmMessage,\n }))\n}\n\nfunction mergeTokenActions(\n messageActions: MessageTokenResponse['actionData'],\n defaultActions: Array<{\n id: string\n label: string\n labelKey?: string\n variant?: 'default' | 'secondary' | 'destructive' | 'outline' | 'ghost'\n icon?: string\n commandId?: string\n href?: string\n isTerminal?: boolean\n confirmRequired?: boolean\n confirmMessage?: string\n }> | undefined,\n): MessageTokenResponse['actionData'] {\n const deduped = new Map<string, NonNullable<MessageTokenResponse['actionData']>['actions'][number]>()\n\n for (const action of messageActions?.actions ?? []) {\n const normalizedId = action.id.trim()\n if (!normalizedId) continue\n deduped.set(normalizedId, { ...action, id: normalizedId })\n }\n\n for (const action of defaultActions ?? []) {\n const normalizedId = action.id.trim()\n if (!normalizedId || deduped.has(normalizedId)) continue\n deduped.set(normalizedId, { ...action, id: normalizedId })\n }\n\n const actions = Array.from(deduped.values())\n if (actions.length === 0 && !messageActions?.expiresAt) return null\n\n return {\n actions,\n primaryActionId: messageActions?.primaryActionId,\n expiresAt: messageActions?.expiresAt,\n }\n}\n\nexport default function MessageTokenPage({ params }: { params: { token: string } }) {\n const t = useT()\n const messageUiRegistry = React.useMemo(() => getMessageUiComponentRegistry(), [])\n const token = params?.token\n\n const [loading, setLoading] = React.useState(true)\n const [data, setData] = React.useState<MessageTokenResponse | null>(null)\n const [errorMessage, setErrorMessage] = React.useState<string | null>(null)\n const [errorStatus, setErrorStatus] = React.useState<number | null>(null)\n\n React.useEffect(() => {\n let mounted = true\n\n async function run() {\n if (!token) return\n\n setLoading(true)\n setData(null)\n setErrorMessage(null)\n setErrorStatus(null)\n\n try {\n const call = await apiCall<MessageTokenResponse>(`/api/messages/token/${encodeURIComponent(token)}`)\n\n if (!mounted) return\n\n if (!call.ok || !call.result) {\n const status = call.status\n setErrorStatus(status)\n\n const fallback = status === 404\n ? t('messages.token.errors.notFound', 'This message link is invalid or has already been used.')\n : status === 409\n ? t('messages.token.errors.limitExceeded', 'This message link reached its usage limit.')\n : status === 410\n ? t('messages.token.errors.expired', 'This message link has expired.')\n : t('messages.token.errors.generic', 'Unable to load this message.')\n\n setErrorMessage(toErrorMessage(call.result) ?? fallback)\n return\n }\n\n setData(call.result)\n } catch (error) {\n if (!mounted) return\n setErrorMessage(\n error instanceof Error\n ? error.message\n : t('messages.token.errors.generic', 'Unable to load this message.'),\n )\n } finally {\n if (mounted) {\n setLoading(false)\n }\n }\n }\n\n void run()\n\n return () => {\n mounted = false\n }\n }, [t, token])\n\n if (loading) {\n return (\n <main className=\"mx-auto max-w-3xl p-6\">\n <p className=\"flex items-center gap-2 text-sm text-muted-foreground\">\n <Spinner className=\"h-4 w-4 animate-spin\" />\n {t('messages.token.loading', 'Loading message...')}\n </p>\n </main>\n )\n }\n\n if (errorMessage || !data) {\n return (\n <main className=\"mx-auto max-w-3xl space-y-3 p-6\">\n <h1 className=\"text-2xl font-semibold\">{t('messages.token.pageTitle', 'Message')}</h1>\n <p className=\"text-sm text-destructive\">{errorMessage ?? t('messages.token.errors.generic', 'Unable to load this message.')}</p>\n {errorStatus ? <p className=\"text-xs text-muted-foreground\">HTTP {errorStatus}</p> : null}\n </main>\n )\n }\n\n if (data.requiresAuth) {\n return (\n <main className=\"mx-auto max-w-3xl space-y-4 p-6\">\n <h1 className=\"text-2xl font-semibold\">{t('messages.token.authRequired.title', 'Sign in required')}</h1>\n <p className=\"text-sm text-muted-foreground\">\n {t('messages.token.authRequired.description', 'This message includes protected objects. Sign in to continue.')}\n </p>\n <Button asChild>\n <Link href=\"/login\">{t('auth.signIn', 'Sign in')}</Link>\n </Button>\n </main>\n )\n }\n\n const messageType = getMessageTypeOrDefault(data.type)\n const contentComponentKey = messageType.ui?.contentComponent\n const actionsComponentKey = messageType.ui?.actionsComponent\n const ContentComponent = contentComponentKey\n ? messageUiRegistry.contentComponents[contentComponentKey] ?? null\n : null\n const ActionsComponent = actionsComponentKey\n ? messageUiRegistry.actionsComponents[actionsComponentKey] ?? null\n : null\n const resolvedActionData = mergeTokenActions(data.actionData ?? null, messageType.defaultActions)\n\n const contentProps: MessageContentProps = {\n message: {\n id: data.id,\n type: data.type,\n subject: data.subject,\n body: data.body,\n bodyFormat: data.bodyFormat,\n priority: data.priority,\n sentAt: data.sentAt ? new Date(data.sentAt) : null,\n senderUserId: data.senderUserId,\n actionData: resolvedActionData,\n actionTaken: data.actionTaken ?? null,\n actionTakenAt: data.actionTakenAt ? new Date(data.actionTakenAt) : null,\n },\n objects: data.objects.map((objectItem) => ({\n id: objectItem.id,\n entityModule: objectItem.entityModule,\n entityType: objectItem.entityType,\n entityId: objectItem.entityId,\n actionRequired: objectItem.actionRequired,\n snapshot: objectItem.snapshot ?? undefined,\n })),\n attachments: [],\n }\n\n const tokenActions = resolvedActionData?.actions ?? []\n\n return (\n <main className=\"mx-auto max-w-3xl space-y-6 p-6\">\n <header className=\"space-y-1\">\n <h1 className=\"text-2xl font-semibold\">{data.subject}</h1>\n <p className=\"text-sm text-muted-foreground\">\n {t('messages.token.sentAt', 'Sent')}: {formatDateTime(data.sentAt)}\n </p>\n </header>\n\n <section className=\"rounded-xl border bg-card p-4\">\n {ContentComponent ? (\n <ContentComponent {...contentProps} />\n ) : (\n <MarkdownContent\n body={data.body}\n format={data.bodyFormat}\n className=\"text-sm whitespace-pre-wrap [&>*]:mb-2 [&>*:last-child]:mb-0 [&_ul]:ml-4 [&_ul]:list-disc [&_ol]:ml-4 [&_ol]:list-decimal [&_code]:rounded [&_code]:bg-muted [&_code]:px-1 [&_code]:py-0.5 [&_pre]:rounded-md [&_pre]:bg-muted [&_pre]:p-3 [&_pre]:text-xs\"\n />\n )}\n </section>\n\n {tokenActions.length > 0 ? (\n <section className=\"space-y-3 rounded-xl border bg-card p-4\">\n <h2 className=\"text-base font-semibold\">{t('messages.actions.title', 'Actions')}</h2>\n {ActionsComponent ? (\n <ActionsComponent\n message={{\n id: data.id,\n type: data.type,\n actionData: resolvedActionData,\n actionTaken: data.actionTaken ?? null,\n }}\n onExecuteAction={async (_actionId) => {\n return\n }}\n isExecuting={false}\n executingActionId={null}\n />\n ) : (\n <div className=\"flex flex-wrap gap-2\">\n {tokenActions.map((action) => (\n <Button\n key={action.id}\n type=\"button\"\n variant={action.variant ?? 'default'}\n disabled\n >\n {t(action.labelKey ?? action.label, action.label)}\n </Button>\n ))}\n </div>\n )}\n </section>\n ) : null}\n\n <section className=\"space-y-2 rounded-xl border bg-card p-4\">\n <h2 className=\"text-base font-semibold\">{t('messages.attachedObjects', 'Attached objects')}</h2>\n {data.objects.length === 0 ? (\n null\n ) : (\n <div className=\"space-y-2\">\n {data.objects.map((objectItem) => {\n const objectType = getMessageObjectType(objectItem.entityModule, objectItem.entityType)\n const objectActions = toObjectActions(objectType?.actions ?? [])\n const detailComponentKey = `${objectItem.entityModule}:${objectItem.entityType}`\n const DetailComponent = messageUiRegistry.objectDetailComponents[detailComponentKey] ?? null\n\n return DetailComponent ? (\n <DetailComponent\n key={objectItem.id}\n entityId={objectItem.entityId}\n entityModule={objectItem.entityModule}\n entityType={objectItem.entityType}\n snapshot={objectItem.snapshot ?? undefined}\n previewData={undefined}\n actionRequired={objectItem.actionRequired}\n actionType={objectItem.actionType ?? undefined}\n actionLabel={objectItem.actionLabel ?? undefined}\n actions={objectActions}\n actionTaken={data.actionTaken ?? null}\n actionTakenAt={data.actionTakenAt ? new Date(data.actionTakenAt) : null}\n actionTakenByUserId={data.actionTakenByUserId ?? null}\n icon={objectType?.icon}\n onAction={async () => {\n return\n }}\n />\n ) : null\n })}\n </div>\n )}\n </section>\n </main>\n )\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -12,6 +12,7 @@ import { updateCrud, deleteCrud } from "@open-mercato/ui/backend/utils/crud";
|
|
|
12
12
|
import { flash } from "@open-mercato/ui/backend/FlashMessages";
|
|
13
13
|
import { ActivitiesSection, NotesSection } from "@open-mercato/ui/backend/detail";
|
|
14
14
|
import { VersionHistoryAction } from "@open-mercato/ui/backend/version-history";
|
|
15
|
+
import { SendObjectMessageDialog } from "@open-mercato/ui/backend/messages";
|
|
15
16
|
import { useT } from "@open-mercato/shared/lib/i18n/context";
|
|
16
17
|
import { createTranslatorWithFallback } from "@open-mercato/shared/lib/i18n/translate";
|
|
17
18
|
import { buildResourceScheduleItems } from "@open-mercato/core/modules/resources/lib/resourceSchedule";
|
|
@@ -391,13 +392,29 @@ function ResourcesResourceDetailPage({ params }) {
|
|
|
391
392
|
mode: "detail",
|
|
392
393
|
backHref: "/backend/resources/resources",
|
|
393
394
|
backLabel: t("resources.resources.detail.back", "Back to resources"),
|
|
394
|
-
utilityActions: /* @__PURE__ */
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
395
|
+
utilityActions: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
396
|
+
resourceId ? /* @__PURE__ */ jsx(
|
|
397
|
+
SendObjectMessageDialog,
|
|
398
|
+
{
|
|
399
|
+
object: {
|
|
400
|
+
entityModule: "resources",
|
|
401
|
+
entityType: "resource",
|
|
402
|
+
entityId: resourceId,
|
|
403
|
+
previewData: {
|
|
404
|
+
title: resourceTitle
|
|
405
|
+
}
|
|
406
|
+
},
|
|
407
|
+
viewHref: `/backend/resources/resources/${resourceId}`
|
|
408
|
+
}
|
|
409
|
+
) : null,
|
|
410
|
+
/* @__PURE__ */ jsx(
|
|
411
|
+
VersionHistoryAction,
|
|
412
|
+
{
|
|
413
|
+
config: resourceId ? { resourceKind: "resources.resource", resourceId, includeRelated: true } : null,
|
|
414
|
+
t
|
|
415
|
+
}
|
|
416
|
+
)
|
|
417
|
+
] }),
|
|
401
418
|
title: resourceTitle,
|
|
402
419
|
subtitle: t("resources.resources.detail.subtitle", "Resource profile and activity")
|
|
403
420
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../../src/modules/resources/backend/resources/resources/%5Bid%5D/page.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { useRouter, useSearchParams } from 'next/navigation'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { FormHeader } from '@open-mercato/ui/backend/forms'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { apiCallOrThrow, readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'\nimport { collectCustomFieldValues } from '@open-mercato/ui/backend/utils/customFieldValues'\nimport { createCrudFormError } from '@open-mercato/ui/backend/utils/serverErrors'\nimport { updateCrud, deleteCrud } from '@open-mercato/ui/backend/utils/crud'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { ActivitiesSection, NotesSection, type SectionAction, type TagOption } from '@open-mercato/ui/backend/detail'\nimport { VersionHistoryAction } from '@open-mercato/ui/backend/version-history'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { createTranslatorWithFallback } from '@open-mercato/shared/lib/i18n/translate'\nimport { buildResourceScheduleItems } from '@open-mercato/core/modules/resources/lib/resourceSchedule'\nimport { RESOURCES_RESOURCE_FIELDSET_DEFAULT } from '@open-mercato/core/modules/resources/lib/resourceCustomFields'\nimport type { AvailabilityScheduleItemBuilder } from '@open-mercato/core/modules/planner/components/AvailabilityRulesEditor'\nimport { AvailabilityRulesEditor } from '@open-mercato/core/modules/planner/components/AvailabilityRulesEditor'\nimport { ResourcesResourceForm, useResourcesResourceFormConfig } from '@open-mercato/core/modules/resources/components/ResourceCrudForm'\nimport { renderDictionaryColor, renderDictionaryIcon, ICON_SUGGESTIONS } from '@open-mercato/core/modules/dictionaries/components/dictionaryAppearance'\nimport { createResourceNotesAdapter } from '@open-mercato/core/modules/resources/components/detail/notesAdapter'\nimport { createResourceActivitiesAdapter } from '@open-mercato/core/modules/resources/components/detail/activitiesAdapter'\nimport {\n createResourceDictionaryEntry,\n loadResourceDictionary,\n type DictionaryEntryOption,\n} from '@open-mercato/core/modules/resources/components/detail/dictionaries'\nimport type { DictionarySelectLabels } from '@open-mercato/core/modules/dictionaries/components/DictionaryEntrySelect'\n\ntype ResourceRecord = {\n id: string\n name: string\n description?: string | null\n resourceTypeId: string | null\n capacity: number | null\n capacityUnitValue: string | null\n capacityUnitName: string | null\n capacityUnitColor: string | null\n capacityUnitIcon: string | null\n tags?: TagOption[] | null\n isActive: boolean\n appearanceIcon?: string | null\n appearanceColor?: string | null\n resource_type_id?: string | null\n capacity_unit_value?: string | null\n capacity_unit_name?: string | null\n capacity_unit_color?: string | null\n capacity_unit_icon?: string | null\n appearance_icon?: string | null\n appearance_color?: string | null\n is_active?: boolean\n availabilityRuleSetId?: string | null\n availability_rule_set_id?: string | null\n} & Record<string, unknown>\n\ntype ResourceResponse = {\n items: ResourceRecord[]\n}\n\nfunction normalizeResourceRecord(record: ResourceRecord): ResourceRecord {\n return {\n ...record,\n resourceTypeId: record.resourceTypeId ?? record.resource_type_id ?? null,\n description: record.description ?? null,\n capacityUnitValue: record.capacityUnitValue ?? record.capacity_unit_value ?? null,\n capacityUnitName: record.capacityUnitName ?? record.capacity_unit_name ?? null,\n capacityUnitColor: record.capacityUnitColor ?? record.capacity_unit_color ?? null,\n capacityUnitIcon: record.capacityUnitIcon ?? record.capacity_unit_icon ?? null,\n appearanceIcon: record.appearanceIcon ?? record.appearance_icon ?? null,\n appearanceColor: record.appearanceColor ?? record.appearance_color ?? null,\n isActive: record.isActive ?? record.is_active ?? true,\n }\n}\n\nexport default function ResourcesResourceDetailPage({ params }: { params?: { id?: string } }) {\n const resourceId = params?.id\n const t = useT()\n const detailTranslator = React.useMemo(() => createTranslatorWithFallback(t), [t])\n const router = useRouter()\n const searchParams = useSearchParams()\n const [initialValues, setInitialValues] = React.useState<Record<string, unknown> | null>(null)\n const [tags, setTags] = React.useState<TagOption[]>([])\n const [activeTab, setActiveTab] = React.useState<'details' | 'availability'>('details')\n const [activeDetailTab, setActiveDetailTab] = React.useState<'notes' | 'activities'>('notes')\n const [sectionAction, setSectionAction] = React.useState<SectionAction | null>(null)\n const [availabilityRuleSetId, setAvailabilityRuleSetId] = React.useState<string | null>(null)\n const [activityDictionaryId, setActivityDictionaryId] = React.useState<string | null>(null)\n const [activityTypeEntries, setActivityTypeEntries] = React.useState<DictionaryEntryOption[]>([])\n const flashShownRef = React.useRef(false)\n\n const availabilityMode = 'availability'\n const notesAdapter = React.useMemo(() => createResourceNotesAdapter(detailTranslator), [detailTranslator])\n const activitiesAdapter = React.useMemo(() => createResourceActivitiesAdapter(detailTranslator), [detailTranslator])\n\n const activityTypeLabels = React.useMemo<DictionarySelectLabels>(() => ({\n placeholder: t('resources.resources.detail.activities.dictionary.placeholder', 'Select an activity type'),\n addLabel: t('resources.resources.detail.activities.dictionary.add', 'Add type'),\n addPrompt: t('resources.resources.detail.activities.dictionary.prompt', 'Name the type'),\n dialogTitle: t('resources.resources.detail.activities.dictionary.dialogTitle', 'Add activity type'),\n valueLabel: t('resources.resources.detail.activities.dictionary.valueLabel', 'Name'),\n valuePlaceholder: t('resources.resources.detail.activities.dictionary.valuePlaceholder', 'Name'),\n labelLabel: t('resources.resources.detail.activities.dictionary.labelLabel', 'Label'),\n labelPlaceholder: t('resources.resources.detail.activities.dictionary.labelPlaceholder', 'Display name shown in UI'),\n emptyError: t('resources.resources.detail.activities.dictionary.emptyError', 'Please enter a name'),\n cancelLabel: t('resources.resources.detail.activities.dictionary.cancel', 'Cancel'),\n saveLabel: t('resources.resources.detail.activities.dictionary.save', 'Save'),\n saveShortcutHint: t('resources.resources.detail.activities.dictionary.saveShortcut', '\u2318/Ctrl + Enter'),\n errorLoad: t('resources.resources.detail.activities.dictionary.errorLoad', 'Failed to load options'),\n errorSave: t('resources.resources.detail.activities.dictionary.errorSave', 'Failed to save option'),\n loadingLabel: t('resources.resources.detail.activities.dictionary.loading', 'Loading\u2026'),\n manageTitle: t('resources.resources.detail.activities.dictionary.manage', 'Manage dictionary'),\n }), [t])\n\n const loadActivityOptions = React.useCallback(async () => {\n const { dictionary, entries } = await loadResourceDictionary('activityTypes')\n setActivityDictionaryId(dictionary?.id ?? null)\n setActivityTypeEntries(entries)\n return entries\n }, [])\n\n const createActivityOption = React.useCallback(\n async (input: { value: string; label?: string; color?: string | null; icon?: string | null }) => {\n const entry = await createResourceDictionaryEntry('activityTypes', input)\n if (!entry) {\n throw new Error(t('resources.resources.detail.activities.dictionary.errorSave', 'Failed to save option'))\n }\n return entry\n },\n [t],\n )\n\n React.useEffect(() => {\n loadActivityOptions().catch(() => {})\n }, [loadActivityOptions])\n\n const activityTypeMap = React.useMemo(\n () => new Map(activityTypeEntries.map((entry) => [entry.value, entry])),\n [activityTypeEntries],\n )\n\n const resolveActivityPresentation = React.useCallback(\n (activity: { activityType: string; appearanceIcon?: string | null; appearanceColor?: string | null }) => {\n const entry = activityTypeMap.get(activity.activityType)\n return {\n label: entry?.label ?? activity.activityType,\n icon: entry?.icon ?? activity.appearanceIcon ?? null,\n color: entry?.color ?? activity.appearanceColor ?? null,\n }\n },\n [activityTypeMap],\n )\n\n const manageActivityHref = React.useMemo(() => {\n if (!activityDictionaryId) return '/backend/config/dictionaries'\n return `/backend/config/dictionaries?dictionaryId=${encodeURIComponent(activityDictionaryId)}`\n }, [activityDictionaryId])\n\n const appearanceLabels = React.useMemo(() => ({\n colorLabel: t('resources.resources.detail.activities.appearance.colorLabel', 'Color'),\n colorHelp: t('resources.resources.detail.activities.appearance.colorHelp', 'Pick a highlight color for this entry.'),\n colorClearLabel: t('resources.resources.detail.activities.appearance.colorClear', 'Remove color'),\n iconLabel: t('resources.resources.detail.activities.appearance.iconLabel', 'Icon or emoji'),\n iconPlaceholder: t('resources.resources.detail.activities.appearance.iconPlaceholder', 'Type an emoji or pick one of the suggestions.'),\n iconPickerTriggerLabel: t('resources.resources.detail.activities.appearance.iconBrowse', 'Browse icons and emojis'),\n iconSearchPlaceholder: t('resources.resources.detail.activities.appearance.iconSearchPlaceholder', 'Search icons or emojis\u2026'),\n iconSearchEmptyLabel: t('resources.resources.detail.activities.appearance.iconSearchEmpty', 'No icons match your search.'),\n iconSuggestionsLabel: t('resources.resources.detail.activities.appearance.iconSuggestions', 'Suggestions'),\n iconClearLabel: t('resources.resources.detail.activities.appearance.iconClear', 'Remove icon'),\n previewEmptyLabel: t('resources.resources.detail.activities.appearance.previewEmpty', 'No appearance selected'),\n }), [t])\n\n const renderCustomFields = React.useCallback((activity: { id?: string; customFields?: Array<{ key: string; label?: string | null; value: unknown }> }) => {\n const entries = Array.isArray(activity.customFields) ? activity.customFields : []\n if (!entries.length) return null\n const emptyLabel = t('resources.resources.detail.activities.customFields.empty', 'Not provided')\n return (\n <div className=\"grid gap-3 sm:grid-cols-2\">\n {entries.map((entry, index) => {\n const label = entry.label ?? entry.key\n const value = entry.value\n const hasValue = !(value == null || value === '' || (Array.isArray(value) && value.length === 0))\n const content = hasValue\n ? Array.isArray(value)\n ? value.map((item) => String(item)).join(', ')\n : String(value)\n : emptyLabel\n return (\n <div\n key={`activity-${activity.id ?? 'row'}-custom-${index}`}\n className=\"rounded-md border border-border/60 bg-muted/10 px-3 py-2\"\n >\n <div className=\"text-xs font-medium text-muted-foreground\">{label}</div>\n <div className=\"mt-1 text-sm text-foreground\">{content}</div>\n </div>\n )\n })}\n </div>\n )\n }, [t])\n\n React.useEffect(() => {\n if (!searchParams) return\n const tabParam = searchParams.get('tab')\n if (tabParam === 'availability') {\n setActiveTab('availability')\n }\n const created = searchParams.get('created') === '1'\n if (created && !flashShownRef.current) {\n flashShownRef.current = true\n flash(t('resources.resources.flash.createdAvailability', 'Saved. You can now set availability.'), 'success')\n const nextParams = new URLSearchParams(searchParams.toString())\n nextParams.delete('created')\n const nextQuery = nextParams.toString()\n const nextPath = resourceId\n ? `/backend/resources/resources/${encodeURIComponent(resourceId)}${nextQuery ? `?${nextQuery}` : ''}`\n : `/backend/resources/resources${nextQuery ? `?${nextQuery}` : ''}`\n router.replace(nextPath)\n }\n }, [resourceId, router, searchParams, t])\n\n const buildScheduleItems = React.useCallback<AvailabilityScheduleItemBuilder>(\n ({ availabilityRules, translate }) => buildResourceScheduleItems({\n availabilityRules,\n isAvailableByDefault: false,\n translate,\n }),\n [],\n )\n\n const tagLabels = React.useMemo(\n () => ({\n loading: t('resources.resources.tags.loading', 'Loading tags...'),\n placeholder: t('resources.resources.tags.placeholder', 'Type to add tags'),\n empty: t('resources.resources.tags.placeholder', 'No tags yet. Add labels to keep resources organized.'),\n loadError: t('resources.resources.tags.loadError', 'Failed to load tags.'),\n createError: t('resources.resources.tags.createError', 'Failed to create tag.'),\n updateError: t('resources.resources.tags.updateError', 'Failed to update tags.'),\n labelRequired: t('resources.resources.tags.labelRequired', 'Tag name is required.'),\n saveShortcut: t('resources.resources.tags.saveShortcut', 'Save Cmd+Enter / Ctrl+Enter'),\n cancelShortcut: t('resources.resources.tags.cancelShortcut', 'Cancel (Esc)'),\n edit: t('ui.forms.actions.edit', 'Edit'),\n cancel: t('ui.forms.actions.cancel', 'Cancel'),\n success: t('resources.resources.tags.success', 'Tags updated.'),\n }),\n [t],\n )\n\n const handleSubmit = React.useCallback(async (values: Record<string, unknown>) => {\n if (!resourceId) return\n const appearance = values.appearance && typeof values.appearance === 'object'\n ? values.appearance as { icon?: string | null; color?: string | null }\n : {}\n const { appearance: _appearance, ...rest } = values\n const customFieldsetCode = typeof values.customFieldsetCode === 'string' && values.customFieldsetCode.trim().length\n ? values.customFieldsetCode.trim()\n : RESOURCES_RESOURCE_FIELDSET_DEFAULT\n const payload: Record<string, unknown> = {\n ...rest,\n id: resourceId,\n resourceTypeId: values.resourceTypeId || null,\n capacity: values.capacity ? Number(values.capacity) : null,\n capacityUnitValue: values.capacityUnitValue ? String(values.capacityUnitValue) : null,\n appearanceIcon: appearance.icon ?? null,\n appearanceColor: appearance.color ?? null,\n isActive: values.isActive ?? true,\n customFieldsetCode,\n ...collectCustomFieldValues(values),\n }\n if (!payload.name || String(payload.name).trim().length === 0) {\n throw createCrudFormError(t('resources.resources.form.errors.nameRequired', 'Name is required.'))\n }\n await updateCrud('resources/resources', payload, {\n errorMessage: t('resources.resources.form.errors.update', 'Failed to update resource.'),\n })\n flash(t('resources.resources.form.flash.updated', 'Resource updated.'), 'success')\n }, [resourceId, t])\n\n const tabs = React.useMemo(() => ([\n { id: 'details', label: t('resources.resources.tabs.details', 'Details') },\n { id: 'availability', label: t('resources.resources.tabs.availability', 'Availability') },\n ]), [t])\n const detailTabs = React.useMemo(() => ([\n { id: 'notes' as const, label: t('resources.resources.detail.tabs.notes', 'Notes') },\n { id: 'activities' as const, label: t('resources.resources.detail.tabs.activities', 'Activities') },\n ]), [t])\n\n const loadTagOptions = React.useCallback(\n async (query?: string): Promise<TagOption[]> => {\n const params = new URLSearchParams({ pageSize: '100' })\n if (query) params.set('search', query)\n const payload = await readApiResultOrThrow<Record<string, unknown>>(\n `/api/resources/tags?${params.toString()}`,\n undefined,\n { errorMessage: t('resources.resources.tags.loadError', 'Failed to load tags.') },\n )\n const items = Array.isArray(payload?.items) ? payload.items : []\n return items\n .map((item: unknown): TagOption | null => {\n if (!item || typeof item !== 'object') return null\n const raw = item as { id?: unknown; tagId?: unknown; label?: unknown; slug?: unknown; color?: unknown }\n const rawId =\n typeof raw.id === 'string'\n ? raw.id\n : typeof raw.tagId === 'string'\n ? raw.tagId\n : null\n if (!rawId) return null\n const labelValue =\n (typeof raw.label === 'string' && raw.label.trim().length && raw.label.trim()) ||\n (typeof raw.slug === 'string' && raw.slug.trim().length && raw.slug.trim()) ||\n rawId\n const color = typeof raw.color === 'string' && raw.color.trim().length ? raw.color.trim() : null\n return { id: rawId, label: labelValue, color }\n })\n .filter((entry): entry is TagOption => entry !== null)\n },\n [t],\n )\n\n const createTag = React.useCallback(\n async (label: string): Promise<TagOption> => {\n const trimmed = label.trim()\n if (!trimmed.length) {\n throw new Error(t('resources.resources.tags.labelRequired', 'Tag name is required.'))\n }\n const response = await apiCallOrThrow<Record<string, unknown>>(\n '/api/resources/tags',\n {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ label: trimmed }),\n },\n { errorMessage: t('resources.resources.tags.createError', 'Failed to create tag.') },\n )\n const payload = response.result ?? {}\n const id =\n typeof payload?.id === 'string'\n ? payload.id\n : typeof (payload as any)?.tagId === 'string'\n ? (payload as any).tagId\n : ''\n if (!id) throw new Error(t('resources.resources.tags.createError', 'Failed to create tag.'))\n const color = typeof (payload as any)?.color === 'string' && (payload as any).color.trim().length\n ? (payload as any).color.trim()\n : null\n return { id, label: trimmed, color }\n },\n [t],\n )\n\n const assignTag = React.useCallback(async (tagId: string) => {\n if (!resourceId) return\n await apiCallOrThrow(\n '/api/resources/resources/tags/assign',\n {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ tagId, resourceId }),\n },\n { errorMessage: t('resources.resources.tags.updateError', 'Failed to update tags.') },\n )\n }, [resourceId, t])\n\n const unassignTag = React.useCallback(async (tagId: string) => {\n if (!resourceId) return\n await apiCallOrThrow(\n '/api/resources/resources/tags/unassign',\n {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ tagId, resourceId }),\n },\n { errorMessage: t('resources.resources.tags.updateError', 'Failed to update tags.') },\n )\n }, [resourceId, t])\n\n const handleTagsSave = React.useCallback(\n async ({ next, added, removed }: { next: TagOption[]; added: TagOption[]; removed: TagOption[] }) => {\n if (!resourceId) return\n for (const tag of added) {\n await assignTag(tag.id)\n }\n for (const tag of removed) {\n await unassignTag(tag.id)\n }\n setTags(next)\n flash(t('resources.resources.tags.success', 'Tags updated.'), 'success')\n },\n [assignTag, resourceId, t, unassignTag],\n )\n\n const tagsSection = React.useMemo(\n () => ({\n title: t('resources.resources.tags.title', 'Tags'),\n tags,\n onChange: setTags,\n loadOptions: loadTagOptions,\n createTag,\n onSave: handleTagsSave,\n labels: tagLabels,\n }),\n [createTag, handleTagsSave, loadTagOptions, t, tagLabels, tags],\n )\n\n const formConfig = useResourcesResourceFormConfig({ tagsSection })\n const { resourceTypesLoaded, resolveFieldsetCode } = formConfig\n\n React.useEffect(() => {\n if (!resourceId || !resourceTypesLoaded) return\n let cancelled = false\n async function loadResource() {\n try {\n const params = new URLSearchParams()\n params.set('page', '1')\n params.set('pageSize', '1')\n if (resourceId) params.set('ids', resourceId)\n const record = await readApiResultOrThrow<ResourceResponse>(`/api/resources/resources?${params.toString()}`)\n const resourceRaw = Array.isArray(record?.items) ? record.items[0] : null\n const resource = resourceRaw ? normalizeResourceRecord(resourceRaw) : null\n if (!resource) throw new Error(t('resources.resources.form.errors.notFound', 'Resource not found.'))\n if (!cancelled) {\n const customValues: Record<string, unknown> = {}\n for (const [key, value] of Object.entries(resource)) {\n if (key.startsWith('cf_')) customValues[key] = value\n else if (key.startsWith('cf:')) customValues[`cf_${key.slice(3)}`] = value\n }\n setTags(Array.isArray(resource.tags) ? resource.tags : [])\n setAvailabilityRuleSetId(\n typeof resource.availabilityRuleSetId === 'string'\n ? resource.availabilityRuleSetId\n : typeof resource.availability_rule_set_id === 'string'\n ? resource.availability_rule_set_id\n : null,\n )\n setInitialValues({\n id: resource.id,\n name: resource.name,\n description: resource.description ?? '',\n resourceTypeId: resource.resourceTypeId || '',\n capacity: resource.capacity ?? '',\n capacityUnitValue: resource.capacityUnitValue ?? '',\n appearance: { icon: resource.appearanceIcon ?? null, color: resource.appearanceColor ?? null },\n isActive: resource.isActive ?? true,\n customFieldsetCode: resource.resourceTypeId\n ? resolveFieldsetCode(resource.resourceTypeId)\n : RESOURCES_RESOURCE_FIELDSET_DEFAULT,\n ...customValues,\n })\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : t('resources.resources.form.errors.load', 'Failed to load resource.')\n flash(message, 'error')\n }\n }\n loadResource()\n return () => { cancelled = true }\n }, [resourceId, resourceTypesLoaded, resolveFieldsetCode, t])\n\n const handleDelete = React.useCallback(async () => {\n if (!resourceId) return\n await deleteCrud('resources/resources', resourceId, {\n errorMessage: t('resources.resources.form.errors.delete', 'Failed to delete resource.'),\n })\n flash(t('resources.resources.form.flash.deleted', 'Resource deleted.'), 'success')\n router.push('/backend/resources/resources')\n }, [resourceId, router, t])\n\n const handleRulesetChange = React.useCallback(async (nextId: string | null) => {\n if (!resourceId) return\n await updateCrud('resources/resources', { id: resourceId, availabilityRuleSetId: nextId }, {\n errorMessage: t('resources.resources.availability.ruleset.updateError', 'Failed to update schedule.'),\n })\n setAvailabilityRuleSetId(nextId)\n flash(t('resources.resources.availability.ruleset.updateSuccess', 'Schedule updated.'), 'success')\n }, [resourceId, t])\n\n const resourceTitle =\n typeof initialValues?.name === 'string' && initialValues.name.trim().length > 0\n ? initialValues.name.trim()\n : t('resources.resources.detail.untitled', 'Unnamed resource')\n\n return (\n <Page>\n <PageBody>\n <div className=\"space-y-6\">\n <FormHeader\n mode=\"detail\"\n backHref=\"/backend/resources/resources\"\n backLabel={t('resources.resources.detail.back', 'Back to resources')}\n utilityActions={(\n <VersionHistoryAction\n config={resourceId ? { resourceKind: 'resources.resource', resourceId, includeRelated: true } : null}\n t={t}\n />\n )}\n title={resourceTitle}\n subtitle={t('resources.resources.detail.subtitle', 'Resource profile and activity')}\n />\n\n <div className=\"border-b\">\n <nav className=\"flex flex-wrap items-center gap-5 text-sm\" aria-label={t('resources.resources.tabs.label', 'Resource sections')}>\n {tabs.map((tab) => (\n <Button\n key={tab.id}\n type=\"button\"\n role=\"tab\"\n aria-selected={activeTab === tab.id}\n variant=\"ghost\"\n size=\"sm\"\n className={`relative -mb-px h-auto rounded-none border-b-2 px-0 py-2 font-medium ${\n activeTab === tab.id\n ? 'border-primary text-foreground'\n : 'border-transparent text-muted-foreground hover:text-foreground'\n }`}\n onClick={() => setActiveTab(tab.id as 'details' | 'availability')}\n >\n {tab.label}\n </Button>\n ))}\n </nav>\n </div>\n\n {activeTab === 'details' ? (\n <>\n <div className=\"rounded-lg border bg-card p-4\">\n <div className=\"flex flex-wrap items-center justify-between gap-3\">\n <div className=\"flex gap-2\">\n {detailTabs.map((tab) => (\n <Button\n key={tab.id}\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n className={`relative -mb-px h-auto rounded-none border-b-2 px-0 py-1 font-medium ${\n activeDetailTab === tab.id\n ? 'border-primary text-foreground'\n : 'border-transparent text-muted-foreground hover:text-foreground'\n }`}\n onClick={() => setActiveDetailTab(tab.id)}\n >\n {tab.label}\n </Button>\n ))}\n </div>\n {sectionAction ? (\n <Button\n type=\"button\"\n size=\"sm\"\n disabled={sectionAction.disabled}\n onClick={() => sectionAction.onClick()}\n >\n {sectionAction.icon ?? null}\n {sectionAction.label}\n </Button>\n ) : null}\n </div>\n {activeDetailTab === 'notes' ? (\n <NotesSection\n entityId={resourceId ?? null}\n emptyLabel={t('resources.resources.detail.notes.empty', 'No notes yet.')}\n viewerUserId={null}\n viewerName={null}\n viewerEmail={null}\n addActionLabel={t('resources.resources.detail.notes.add', 'Add note')}\n emptyState={{\n title: t('resources.resources.detail.notes.emptyTitle', 'Keep everyone in the loop'),\n actionLabel: t('resources.resources.detail.notes.emptyAction', 'Add a note'),\n }}\n onActionChange={setSectionAction}\n translator={detailTranslator}\n labelPrefix=\"resources.resources.detail.notes\"\n inlineLabelPrefix=\"resources.resources.detail.inline\"\n dataAdapter={notesAdapter}\n renderIcon={renderDictionaryIcon}\n renderColor={renderDictionaryColor}\n iconSuggestions={ICON_SUGGESTIONS}\n />\n ) : null}\n {activeDetailTab === 'activities' ? (\n <ActivitiesSection\n entityId={resourceId ?? null}\n addActionLabel={t('resources.resources.detail.activities.add', 'Log activity')}\n emptyState={{\n title: t('resources.resources.detail.activities.emptyTitle', 'No activities yet'),\n actionLabel: t('resources.resources.detail.activities.emptyAction', 'Add an activity'),\n }}\n onActionChange={setSectionAction}\n dataAdapter={activitiesAdapter}\n activityTypeLabels={activityTypeLabels}\n loadActivityOptions={loadActivityOptions}\n createActivityOption={createActivityOption}\n resolveActivityPresentation={resolveActivityPresentation}\n renderCustomFields={renderCustomFields}\n labelPrefix=\"resources.resources.detail.activities\"\n renderIcon={renderDictionaryIcon}\n renderColor={renderDictionaryColor}\n appearanceLabels={appearanceLabels}\n manageHref={manageActivityHref}\n customFieldEntityIds={['resources:resources_resource_activity']}\n />\n ) : null}\n </div>\n\n <div className=\"rounded-lg border bg-card p-4\">\n <h2 className=\"mb-4 text-sm font-semibold uppercase text-muted-foreground\">\n {t('resources.resources.detail.formTitle', 'Resource settings')}\n </h2>\n <ResourcesResourceForm\n embedded\n title={t('resources.resources.form.editTitle', 'Edit resource')}\n backHref=\"/backend/resources/resources\"\n cancelHref=\"/backend/resources/resources\"\n successRedirect=\"/backend/resources/resources\"\n formConfig={formConfig}\n initialValues={initialValues ?? undefined}\n onSubmit={handleSubmit}\n onDelete={handleDelete}\n isLoading={!initialValues}\n loadingMessage={t('resources.resources.form.loading', 'Loading resource...')}\n />\n </div>\n </>\n ) : (\n <AvailabilityRulesEditor\n subjectType=\"resource\"\n subjectId={resourceId ?? ''}\n labelPrefix=\"resources.resources\"\n mode={availabilityMode}\n rulesetId={availabilityRuleSetId}\n onRulesetChange={handleRulesetChange}\n buildScheduleItems={buildScheduleItems}\n />\n )}\n </div>\n </PageBody>\n </Page>\n )\n}\n"],
|
|
5
|
-
"mappings": ";AA6LY,SAgVA,UA5UE,KAJF;AA3LZ,YAAY,WAAW;AACvB,SAAS,WAAW,uBAAuB;AAC3C,SAAS,MAAM,gBAAgB;AAC/B,SAAS,kBAAkB;AAC3B,SAAS,cAAc;AACvB,SAAS,gBAAgB,4BAA4B;AACrD,SAAS,gCAAgC;AACzC,SAAS,2BAA2B;AACpC,SAAS,YAAY,kBAAkB;AACvC,SAAS,aAAa;AACtB,SAAS,mBAAmB,oBAAwD;AACpF,SAAS,4BAA4B;AACrC,SAAS,YAAY;AACrB,SAAS,oCAAoC;AAC7C,SAAS,kCAAkC;AAC3C,SAAS,2CAA2C;AAEpD,SAAS,+BAA+B;AACxC,SAAS,uBAAuB,sCAAsC;AACtE,SAAS,uBAAuB,sBAAsB,wBAAwB;AAC9E,SAAS,kCAAkC;AAC3C,SAAS,uCAAuC;AAChD;AAAA,EACE;AAAA,EACA;AAAA,OAEK;AAiCP,SAAS,wBAAwB,QAAwC;AACvE,SAAO;AAAA,IACL,GAAG;AAAA,IACH,gBAAgB,OAAO,kBAAkB,OAAO,oBAAoB;AAAA,IACpE,aAAa,OAAO,eAAe;AAAA,IACnC,mBAAmB,OAAO,qBAAqB,OAAO,uBAAuB;AAAA,IAC7E,kBAAkB,OAAO,oBAAoB,OAAO,sBAAsB;AAAA,IAC1E,mBAAmB,OAAO,qBAAqB,OAAO,uBAAuB;AAAA,IAC7E,kBAAkB,OAAO,oBAAoB,OAAO,sBAAsB;AAAA,IAC1E,gBAAgB,OAAO,kBAAkB,OAAO,mBAAmB;AAAA,IACnE,iBAAiB,OAAO,mBAAmB,OAAO,oBAAoB;AAAA,IACtE,UAAU,OAAO,YAAY,OAAO,aAAa;AAAA,EACnD;AACF;AAEe,SAAR,4BAA6C,EAAE,OAAO,GAAiC;AAC5F,QAAM,aAAa,QAAQ;AAC3B,QAAM,IAAI,KAAK;AACf,QAAM,mBAAmB,MAAM,QAAQ,MAAM,6BAA6B,CAAC,GAAG,CAAC,CAAC,CAAC;AACjF,QAAM,SAAS,UAAU;AACzB,QAAM,eAAe,gBAAgB;AACrC,QAAM,CAAC,eAAe,gBAAgB,IAAI,MAAM,SAAyC,IAAI;AAC7F,QAAM,CAAC,MAAM,OAAO,IAAI,MAAM,SAAsB,CAAC,CAAC;AACtD,QAAM,CAAC,WAAW,YAAY,IAAI,MAAM,SAAqC,SAAS;AACtF,QAAM,CAAC,iBAAiB,kBAAkB,IAAI,MAAM,SAAiC,OAAO;AAC5F,QAAM,CAAC,eAAe,gBAAgB,IAAI,MAAM,SAA+B,IAAI;AACnF,QAAM,CAAC,uBAAuB,wBAAwB,IAAI,MAAM,SAAwB,IAAI;AAC5F,QAAM,CAAC,sBAAsB,uBAAuB,IAAI,MAAM,SAAwB,IAAI;AAC1F,QAAM,CAAC,qBAAqB,sBAAsB,IAAI,MAAM,SAAkC,CAAC,CAAC;AAChG,QAAM,gBAAgB,MAAM,OAAO,KAAK;AAExC,QAAM,mBAAmB;AACzB,QAAM,eAAe,MAAM,QAAQ,MAAM,2BAA2B,gBAAgB,GAAG,CAAC,gBAAgB,CAAC;AACzG,QAAM,oBAAoB,MAAM,QAAQ,MAAM,gCAAgC,gBAAgB,GAAG,CAAC,gBAAgB,CAAC;AAEnH,QAAM,qBAAqB,MAAM,QAAgC,OAAO;AAAA,IACtE,aAAa,EAAE,gEAAgE,yBAAyB;AAAA,IACxG,UAAU,EAAE,wDAAwD,UAAU;AAAA,IAC9E,WAAW,EAAE,2DAA2D,eAAe;AAAA,IACvF,aAAa,EAAE,gEAAgE,mBAAmB;AAAA,IAClG,YAAY,EAAE,+DAA+D,MAAM;AAAA,IACnF,kBAAkB,EAAE,qEAAqE,MAAM;AAAA,IAC/F,YAAY,EAAE,+DAA+D,OAAO;AAAA,IACpF,kBAAkB,EAAE,qEAAqE,0BAA0B;AAAA,IACnH,YAAY,EAAE,+DAA+D,qBAAqB;AAAA,IAClG,aAAa,EAAE,2DAA2D,QAAQ;AAAA,IAClF,WAAW,EAAE,yDAAyD,MAAM;AAAA,IAC5E,kBAAkB,EAAE,iEAAiE,qBAAgB;AAAA,IACrG,WAAW,EAAE,8DAA8D,wBAAwB;AAAA,IACnG,WAAW,EAAE,8DAA8D,uBAAuB;AAAA,IAClG,cAAc,EAAE,4DAA4D,eAAU;AAAA,IACtF,aAAa,EAAE,2DAA2D,mBAAmB;AAAA,EAC/F,IAAI,CAAC,CAAC,CAAC;AAEP,QAAM,sBAAsB,MAAM,YAAY,YAAY;AACxD,UAAM,EAAE,YAAY,QAAQ,IAAI,MAAM,uBAAuB,eAAe;AAC5E,4BAAwB,YAAY,MAAM,IAAI;AAC9C,2BAAuB,OAAO;AAC9B,WAAO;AAAA,EACT,GAAG,CAAC,CAAC;AAEL,QAAM,uBAAuB,MAAM;AAAA,IACjC,OAAO,UAA0F;AAC/F,YAAM,QAAQ,MAAM,8BAA8B,iBAAiB,KAAK;AACxE,UAAI,CAAC,OAAO;AACV,cAAM,IAAI,MAAM,EAAE,8DAA8D,uBAAuB,CAAC;AAAA,MAC1G;AACA,aAAO;AAAA,IACT;AAAA,IACA,CAAC,CAAC;AAAA,EACJ;AAEA,QAAM,UAAU,MAAM;AACpB,wBAAoB,EAAE,MAAM,MAAM;AAAA,IAAC,CAAC;AAAA,EACtC,GAAG,CAAC,mBAAmB,CAAC;AAExB,QAAM,kBAAkB,MAAM;AAAA,IAC5B,MAAM,IAAI,IAAI,oBAAoB,IAAI,CAAC,UAAU,CAAC,MAAM,OAAO,KAAK,CAAC,CAAC;AAAA,IACtE,CAAC,mBAAmB;AAAA,EACtB;AAEA,QAAM,8BAA8B,MAAM;AAAA,IACxC,CAAC,aAAwG;AACvG,YAAM,QAAQ,gBAAgB,IAAI,SAAS,YAAY;AACvD,aAAO;AAAA,QACL,OAAO,OAAO,SAAS,SAAS;AAAA,QAChC,MAAM,OAAO,QAAQ,SAAS,kBAAkB;AAAA,QAChD,OAAO,OAAO,SAAS,SAAS,mBAAmB;AAAA,MACrD;AAAA,IACF;AAAA,IACA,CAAC,eAAe;AAAA,EAClB;AAEA,QAAM,qBAAqB,MAAM,QAAQ,MAAM;AAC7C,QAAI,CAAC,qBAAsB,QAAO;AAClC,WAAO,6CAA6C,mBAAmB,oBAAoB,CAAC;AAAA,EAC9F,GAAG,CAAC,oBAAoB,CAAC;AAEzB,QAAM,mBAAmB,MAAM,QAAQ,OAAO;AAAA,IAC5C,YAAY,EAAE,+DAA+D,OAAO;AAAA,IACpF,WAAW,EAAE,8DAA8D,wCAAwC;AAAA,IACnH,iBAAiB,EAAE,+DAA+D,cAAc;AAAA,IAChG,WAAW,EAAE,8DAA8D,eAAe;AAAA,IAC1F,iBAAiB,EAAE,oEAAoE,+CAA+C;AAAA,IACtI,wBAAwB,EAAE,+DAA+D,yBAAyB;AAAA,IAClH,uBAAuB,EAAE,0EAA0E,8BAAyB;AAAA,IAC5H,sBAAsB,EAAE,oEAAoE,6BAA6B;AAAA,IACzH,sBAAsB,EAAE,oEAAoE,aAAa;AAAA,IACzG,gBAAgB,EAAE,8DAA8D,aAAa;AAAA,IAC7F,mBAAmB,EAAE,iEAAiE,wBAAwB;AAAA,EAChH,IAAI,CAAC,CAAC,CAAC;AAEP,QAAM,qBAAqB,MAAM,YAAY,CAAC,aAA4G;AACxJ,UAAM,UAAU,MAAM,QAAQ,SAAS,YAAY,IAAI,SAAS,eAAe,CAAC;AAChF,QAAI,CAAC,QAAQ,OAAQ,QAAO;AAC5B,UAAM,aAAa,EAAE,4DAA4D,cAAc;AAC/F,WACE,oBAAC,SAAI,WAAU,6BACZ,kBAAQ,IAAI,CAAC,OAAO,UAAU;AAC7B,YAAM,QAAQ,MAAM,SAAS,MAAM;AACnC,YAAM,QAAQ,MAAM;AACpB,YAAM,WAAW,EAAE,SAAS,QAAQ,UAAU,MAAO,MAAM,QAAQ,KAAK,KAAK,MAAM,WAAW;AAC9F,YAAM,UAAU,WACZ,MAAM,QAAQ,KAAK,IACjB,MAAM,IAAI,CAAC,SAAS,OAAO,IAAI,CAAC,EAAE,KAAK,IAAI,IAC3C,OAAO,KAAK,IACd;AACJ,aACE;AAAA,QAAC;AAAA;AAAA,UAEC,WAAU;AAAA,UAEV;AAAA,gCAAC,SAAI,WAAU,6CAA6C,iBAAM;AAAA,YAClE,oBAAC,SAAI,WAAU,gCAAgC,mBAAQ;AAAA;AAAA;AAAA,QAJlD,YAAY,SAAS,MAAM,KAAK,WAAW,KAAK;AAAA,MAKvD;AAAA,IAEJ,CAAC,GACH;AAAA,EAEJ,GAAG,CAAC,CAAC,CAAC;AAEN,QAAM,UAAU,MAAM;AACpB,QAAI,CAAC,aAAc;AACnB,UAAM,WAAW,aAAa,IAAI,KAAK;AACvC,QAAI,aAAa,gBAAgB;AAC/B,mBAAa,cAAc;AAAA,IAC7B;AACA,UAAM,UAAU,aAAa,IAAI,SAAS,MAAM;AAChD,QAAI,WAAW,CAAC,cAAc,SAAS;AACrC,oBAAc,UAAU;AACxB,YAAM,EAAE,iDAAiD,sCAAsC,GAAG,SAAS;AAC3G,YAAM,aAAa,IAAI,gBAAgB,aAAa,SAAS,CAAC;AAC9D,iBAAW,OAAO,SAAS;AAC3B,YAAM,YAAY,WAAW,SAAS;AACtC,YAAM,WAAW,aACb,gCAAgC,mBAAmB,UAAU,CAAC,GAAG,YAAY,IAAI,SAAS,KAAK,EAAE,KACjG,+BAA+B,YAAY,IAAI,SAAS,KAAK,EAAE;AACnE,aAAO,QAAQ,QAAQ;AAAA,IACzB;AAAA,EACF,GAAG,CAAC,YAAY,QAAQ,cAAc,CAAC,CAAC;AAExC,QAAM,qBAAqB,MAAM;AAAA,IAC/B,CAAC,EAAE,mBAAmB,UAAU,MAAM,2BAA2B;AAAA,MAC/D;AAAA,MACA,sBAAsB;AAAA,MACtB;AAAA,IACF,CAAC;AAAA,IACD,CAAC;AAAA,EACH;AAEA,QAAM,YAAY,MAAM;AAAA,IACtB,OAAO;AAAA,MACL,SAAS,EAAE,oCAAoC,iBAAiB;AAAA,MAChE,aAAa,EAAE,wCAAwC,kBAAkB;AAAA,MACzE,OAAO,EAAE,wCAAwC,sDAAsD;AAAA,MACvG,WAAW,EAAE,sCAAsC,sBAAsB;AAAA,MACzE,aAAa,EAAE,wCAAwC,uBAAuB;AAAA,MAC9E,aAAa,EAAE,wCAAwC,wBAAwB;AAAA,MAC/E,eAAe,EAAE,0CAA0C,uBAAuB;AAAA,MAClF,cAAc,EAAE,yCAAyC,6BAA6B;AAAA,MACtF,gBAAgB,EAAE,2CAA2C,cAAc;AAAA,MAC3E,MAAM,EAAE,yBAAyB,MAAM;AAAA,MACvC,QAAQ,EAAE,2BAA2B,QAAQ;AAAA,MAC7C,SAAS,EAAE,oCAAoC,eAAe;AAAA,IAChE;AAAA,IACA,CAAC,CAAC;AAAA,EACJ;AAEA,QAAM,eAAe,MAAM,YAAY,OAAO,WAAoC;AAChF,QAAI,CAAC,WAAY;AACjB,UAAM,aAAa,OAAO,cAAc,OAAO,OAAO,eAAe,WACjE,OAAO,aACP,CAAC;AACL,UAAM,EAAE,YAAY,aAAa,GAAG,KAAK,IAAI;AAC7C,UAAM,qBAAqB,OAAO,OAAO,uBAAuB,YAAY,OAAO,mBAAmB,KAAK,EAAE,SACzG,OAAO,mBAAmB,KAAK,IAC/B;AACJ,UAAM,UAAmC;AAAA,MACvC,GAAG;AAAA,MACH,IAAI;AAAA,MACJ,gBAAgB,OAAO,kBAAkB;AAAA,MACzC,UAAU,OAAO,WAAW,OAAO,OAAO,QAAQ,IAAI;AAAA,MACtD,mBAAmB,OAAO,oBAAoB,OAAO,OAAO,iBAAiB,IAAI;AAAA,MACjF,gBAAgB,WAAW,QAAQ;AAAA,MACnC,iBAAiB,WAAW,SAAS;AAAA,MACrC,UAAU,OAAO,YAAY;AAAA,MAC7B;AAAA,MACA,GAAG,yBAAyB,MAAM;AAAA,IACpC;AACA,QAAI,CAAC,QAAQ,QAAQ,OAAO,QAAQ,IAAI,EAAE,KAAK,EAAE,WAAW,GAAG;AAC7D,YAAM,oBAAoB,EAAE,gDAAgD,mBAAmB,CAAC;AAAA,IAClG;AACA,UAAM,WAAW,uBAAuB,SAAS;AAAA,MAC/C,cAAc,EAAE,0CAA0C,4BAA4B;AAAA,IACxF,CAAC;AACD,UAAM,EAAE,0CAA0C,mBAAmB,GAAG,SAAS;AAAA,EACnF,GAAG,CAAC,YAAY,CAAC,CAAC;AAElB,QAAM,OAAO,MAAM,QAAQ,MAAO;AAAA,IAChC,EAAE,IAAI,WAAW,OAAO,EAAE,oCAAoC,SAAS,EAAE;AAAA,IACzE,EAAE,IAAI,gBAAgB,OAAO,EAAE,yCAAyC,cAAc,EAAE;AAAA,EAC1F,GAAI,CAAC,CAAC,CAAC;AACP,QAAM,aAAa,MAAM,QAAQ,MAAO;AAAA,IACtC,EAAE,IAAI,SAAkB,OAAO,EAAE,yCAAyC,OAAO,EAAE;AAAA,IACnF,EAAE,IAAI,cAAuB,OAAO,EAAE,8CAA8C,YAAY,EAAE;AAAA,EACpG,GAAI,CAAC,CAAC,CAAC;AAEP,QAAM,iBAAiB,MAAM;AAAA,IAC3B,OAAO,UAAyC;AAC9C,YAAMA,UAAS,IAAI,gBAAgB,EAAE,UAAU,MAAM,CAAC;AACtD,UAAI,MAAO,CAAAA,QAAO,IAAI,UAAU,KAAK;AACrC,YAAM,UAAU,MAAM;AAAA,QACpB,uBAAuBA,QAAO,SAAS,CAAC;AAAA,QACxC;AAAA,QACA,EAAE,cAAc,EAAE,sCAAsC,sBAAsB,EAAE;AAAA,MAClF;AACA,YAAM,QAAQ,MAAM,QAAQ,SAAS,KAAK,IAAI,QAAQ,QAAQ,CAAC;AAC/D,aAAO,MACJ,IAAI,CAAC,SAAoC;AACxC,YAAI,CAAC,QAAQ,OAAO,SAAS,SAAU,QAAO;AAC9C,cAAM,MAAM;AACZ,cAAM,QACJ,OAAO,IAAI,OAAO,WACd,IAAI,KACJ,OAAO,IAAI,UAAU,WACnB,IAAI,QACJ;AACR,YAAI,CAAC,MAAO,QAAO;AACnB,cAAM,aACH,OAAO,IAAI,UAAU,YAAY,IAAI,MAAM,KAAK,EAAE,UAAU,IAAI,MAAM,KAAK,KAC3E,OAAO,IAAI,SAAS,YAAY,IAAI,KAAK,KAAK,EAAE,UAAU,IAAI,KAAK,KAAK,KACzE;AACF,cAAM,QAAQ,OAAO,IAAI,UAAU,YAAY,IAAI,MAAM,KAAK,EAAE,SAAS,IAAI,MAAM,KAAK,IAAI;AAC5F,eAAO,EAAE,IAAI,OAAO,OAAO,YAAY,MAAM;AAAA,MAC/C,CAAC,EACA,OAAO,CAAC,UAA8B,UAAU,IAAI;AAAA,IACzD;AAAA,IACA,CAAC,CAAC;AAAA,EACJ;AAEA,QAAM,YAAY,MAAM;AAAA,IACtB,OAAO,UAAsC;AAC3C,YAAM,UAAU,MAAM,KAAK;AAC3B,UAAI,CAAC,QAAQ,QAAQ;AACnB,cAAM,IAAI,MAAM,EAAE,0CAA0C,uBAAuB,CAAC;AAAA,MACtF;AACA,YAAM,WAAW,MAAM;AAAA,QACrB;AAAA,QACA;AAAA,UACE,QAAQ;AAAA,UACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,UAC9C,MAAM,KAAK,UAAU,EAAE,OAAO,QAAQ,CAAC;AAAA,QACzC;AAAA,QACA,EAAE,cAAc,EAAE,wCAAwC,uBAAuB,EAAE;AAAA,MACrF;AACA,YAAM,UAAU,SAAS,UAAU,CAAC;AACpC,YAAM,KACJ,OAAO,SAAS,OAAO,WACnB,QAAQ,KACR,OAAQ,SAAiB,UAAU,WAChC,QAAgB,QACjB;AACR,UAAI,CAAC,GAAI,OAAM,IAAI,MAAM,EAAE,wCAAwC,uBAAuB,CAAC;AAC3F,YAAM,QAAQ,OAAQ,SAAiB,UAAU,YAAa,QAAgB,MAAM,KAAK,EAAE,SACtF,QAAgB,MAAM,KAAK,IAC5B;AACJ,aAAO,EAAE,IAAI,OAAO,SAAS,MAAM;AAAA,IACrC;AAAA,IACA,CAAC,CAAC;AAAA,EACJ;AAEA,QAAM,YAAY,MAAM,YAAY,OAAO,UAAkB;AAC3D,QAAI,CAAC,WAAY;AACjB,UAAM;AAAA,MACJ;AAAA,MACA;AAAA,QACE,QAAQ;AAAA,QACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,QAC9C,MAAM,KAAK,UAAU,EAAE,OAAO,WAAW,CAAC;AAAA,MAC5C;AAAA,MACA,EAAE,cAAc,EAAE,wCAAwC,wBAAwB,EAAE;AAAA,IACtF;AAAA,EACF,GAAG,CAAC,YAAY,CAAC,CAAC;AAElB,QAAM,cAAc,MAAM,YAAY,OAAO,UAAkB;AAC7D,QAAI,CAAC,WAAY;AACjB,UAAM;AAAA,MACJ;AAAA,MACA;AAAA,QACE,QAAQ;AAAA,QACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,QAC9C,MAAM,KAAK,UAAU,EAAE,OAAO,WAAW,CAAC;AAAA,MAC5C;AAAA,MACA,EAAE,cAAc,EAAE,wCAAwC,wBAAwB,EAAE;AAAA,IACtF;AAAA,EACF,GAAG,CAAC,YAAY,CAAC,CAAC;AAElB,QAAM,iBAAiB,MAAM;AAAA,IAC3B,OAAO,EAAE,MAAM,OAAO,QAAQ,MAAuE;AACnG,UAAI,CAAC,WAAY;AACjB,iBAAW,OAAO,OAAO;AACvB,cAAM,UAAU,IAAI,EAAE;AAAA,MACxB;AACA,iBAAW,OAAO,SAAS;AACzB,cAAM,YAAY,IAAI,EAAE;AAAA,MAC1B;AACA,cAAQ,IAAI;AACZ,YAAM,EAAE,oCAAoC,eAAe,GAAG,SAAS;AAAA,IACzE;AAAA,IACA,CAAC,WAAW,YAAY,GAAG,WAAW;AAAA,EACxC;AAEA,QAAM,cAAc,MAAM;AAAA,IACxB,OAAO;AAAA,MACL,OAAO,EAAE,kCAAkC,MAAM;AAAA,MACjD;AAAA,MACA,UAAU;AAAA,MACV,aAAa;AAAA,MACb;AAAA,MACA,QAAQ;AAAA,MACR,QAAQ;AAAA,IACV;AAAA,IACA,CAAC,WAAW,gBAAgB,gBAAgB,GAAG,WAAW,IAAI;AAAA,EAChE;AAEA,QAAM,aAAa,+BAA+B,EAAE,YAAY,CAAC;AACjE,QAAM,EAAE,qBAAqB,oBAAoB,IAAI;AAErD,QAAM,UAAU,MAAM;AACpB,QAAI,CAAC,cAAc,CAAC,oBAAqB;AACzC,QAAI,YAAY;AAChB,mBAAe,eAAe;AAC5B,UAAI;AACF,cAAMA,UAAS,IAAI,gBAAgB;AACnC,QAAAA,QAAO,IAAI,QAAQ,GAAG;AACtB,QAAAA,QAAO,IAAI,YAAY,GAAG;AAC1B,YAAI,WAAY,CAAAA,QAAO,IAAI,OAAO,UAAU;AAC5C,cAAM,SAAS,MAAM,qBAAuC,4BAA4BA,QAAO,SAAS,CAAC,EAAE;AAC3G,cAAM,cAAc,MAAM,QAAQ,QAAQ,KAAK,IAAI,OAAO,MAAM,CAAC,IAAI;AACrE,cAAM,WAAW,cAAc,wBAAwB,WAAW,IAAI;AACtE,YAAI,CAAC,SAAU,OAAM,IAAI,MAAM,EAAE,4CAA4C,qBAAqB,CAAC;AACnG,YAAI,CAAC,WAAW;AACd,gBAAM,eAAwC,CAAC;AAC/C,qBAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,QAAQ,GAAG;AACnD,gBAAI,IAAI,WAAW,KAAK,EAAG,cAAa,GAAG,IAAI;AAAA,qBACtC,IAAI,WAAW,KAAK,EAAG,cAAa,MAAM,IAAI,MAAM,CAAC,CAAC,EAAE,IAAI;AAAA,UACvE;AACA,kBAAQ,MAAM,QAAQ,SAAS,IAAI,IAAI,SAAS,OAAO,CAAC,CAAC;AACzD;AAAA,YACE,OAAO,SAAS,0BAA0B,WACtC,SAAS,wBACT,OAAO,SAAS,6BAA6B,WAC3C,SAAS,2BACT;AAAA,UACR;AACA,2BAAiB;AAAA,YACf,IAAI,SAAS;AAAA,YACb,MAAM,SAAS;AAAA,YACf,aAAa,SAAS,eAAe;AAAA,YACrC,gBAAgB,SAAS,kBAAkB;AAAA,YAC3C,UAAU,SAAS,YAAY;AAAA,YAC/B,mBAAmB,SAAS,qBAAqB;AAAA,YACjD,YAAY,EAAE,MAAM,SAAS,kBAAkB,MAAM,OAAO,SAAS,mBAAmB,KAAK;AAAA,YAC7F,UAAU,SAAS,YAAY;AAAA,YAC/B,oBAAoB,SAAS,iBACzB,oBAAoB,SAAS,cAAc,IAC3C;AAAA,YACJ,GAAG;AAAA,UACL,CAAC;AAAA,QACH;AAAA,MACF,SAAS,OAAO;AACd,cAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,EAAE,wCAAwC,0BAA0B;AAC7H,cAAM,SAAS,OAAO;AAAA,MACxB;AAAA,IACF;AACA,iBAAa;AACb,WAAO,MAAM;AAAE,kBAAY;AAAA,IAAK;AAAA,EAClC,GAAG,CAAC,YAAY,qBAAqB,qBAAqB,CAAC,CAAC;AAE5D,QAAM,eAAe,MAAM,YAAY,YAAY;AACjD,QAAI,CAAC,WAAY;AACjB,UAAM,WAAW,uBAAuB,YAAY;AAAA,MAClD,cAAc,EAAE,0CAA0C,4BAA4B;AAAA,IACxF,CAAC;AACD,UAAM,EAAE,0CAA0C,mBAAmB,GAAG,SAAS;AACjF,WAAO,KAAK,8BAA8B;AAAA,EAC5C,GAAG,CAAC,YAAY,QAAQ,CAAC,CAAC;AAE1B,QAAM,sBAAsB,MAAM,YAAY,OAAO,WAA0B;AAC7E,QAAI,CAAC,WAAY;AACjB,UAAM,WAAW,uBAAuB,EAAE,IAAI,YAAY,uBAAuB,OAAO,GAAG;AAAA,MACzF,cAAc,EAAE,wDAAwD,4BAA4B;AAAA,IACtG,CAAC;AACD,6BAAyB,MAAM;AAC/B,UAAM,EAAE,0DAA0D,mBAAmB,GAAG,SAAS;AAAA,EACnG,GAAG,CAAC,YAAY,CAAC,CAAC;AAElB,QAAM,gBACJ,OAAO,eAAe,SAAS,YAAY,cAAc,KAAK,KAAK,EAAE,SAAS,IAC1E,cAAc,KAAK,KAAK,IACxB,EAAE,uCAAuC,kBAAkB;AAEjE,SACE,oBAAC,QACC,8BAAC,YACC,+BAAC,SAAI,WAAU,aACb;AAAA;AAAA,MAAC;AAAA;AAAA,QACC,MAAK;AAAA,QACL,UAAS;AAAA,QACT,WAAW,EAAE,mCAAmC,mBAAmB;AAAA,QACnE,gBACE;AAAA,UAAC;AAAA;AAAA,YACC,QAAQ,aAAa,EAAE,cAAc,sBAAsB,YAAY,gBAAgB,KAAK,IAAI;AAAA,YAChG;AAAA;AAAA,QACF;AAAA,QAEF,OAAO;AAAA,QACP,UAAU,EAAE,uCAAuC,+BAA+B;AAAA;AAAA,IACpF;AAAA,IAEA,oBAAC,SAAI,WAAU,YACb,8BAAC,SAAI,WAAU,6CAA4C,cAAY,EAAE,kCAAkC,mBAAmB,GAC3H,eAAK,IAAI,CAAC,QACT;AAAA,MAAC;AAAA;AAAA,QAEC,MAAK;AAAA,QACL,MAAK;AAAA,QACL,iBAAe,cAAc,IAAI;AAAA,QACjC,SAAQ;AAAA,QACR,MAAK;AAAA,QACL,WAAW,wEACT,cAAc,IAAI,KACd,mCACA,gEACN;AAAA,QACA,SAAS,MAAM,aAAa,IAAI,EAAgC;AAAA,QAE/D,cAAI;AAAA;AAAA,MAbA,IAAI;AAAA,IAcX,CACD,GACH,GACF;AAAA,IAEC,cAAc,YACb,iCACE;AAAA,2BAAC,SAAI,WAAU,iCACb;AAAA,6BAAC,SAAI,WAAU,qDACb;AAAA,8BAAC,SAAI,WAAU,cACZ,qBAAW,IAAI,CAAC,QACf;AAAA,YAAC;AAAA;AAAA,cAEC,MAAK;AAAA,cACL,SAAQ;AAAA,cACR,MAAK;AAAA,cACL,WAAW,wEACT,oBAAoB,IAAI,KACpB,mCACA,gEACN;AAAA,cACA,SAAS,MAAM,mBAAmB,IAAI,EAAE;AAAA,cAEvC,cAAI;AAAA;AAAA,YAXA,IAAI;AAAA,UAYX,CACD,GACH;AAAA,UACC,gBACC;AAAA,YAAC;AAAA;AAAA,cACC,MAAK;AAAA,cACL,MAAK;AAAA,cACL,UAAU,cAAc;AAAA,cACxB,SAAS,MAAM,cAAc,QAAQ;AAAA,cAEpC;AAAA,8BAAc,QAAQ;AAAA,gBACtB,cAAc;AAAA;AAAA;AAAA,UACjB,IACE;AAAA,WACN;AAAA,QACC,oBAAoB,UACnB;AAAA,UAAC;AAAA;AAAA,YACC,UAAU,cAAc;AAAA,YACxB,YAAY,EAAE,0CAA0C,eAAe;AAAA,YACvE,cAAc;AAAA,YACd,YAAY;AAAA,YACZ,aAAa;AAAA,YACb,gBAAgB,EAAE,wCAAwC,UAAU;AAAA,YACpE,YAAY;AAAA,cACV,OAAO,EAAE,+CAA+C,2BAA2B;AAAA,cACnF,aAAa,EAAE,gDAAgD,YAAY;AAAA,YAC7E;AAAA,YACA,gBAAgB;AAAA,YAChB,YAAY;AAAA,YACZ,aAAY;AAAA,YACZ,mBAAkB;AAAA,YAClB,aAAa;AAAA,YACb,YAAY;AAAA,YACZ,aAAa;AAAA,YACb,iBAAiB;AAAA;AAAA,QACnB,IACE;AAAA,QACH,oBAAoB,eACnB;AAAA,UAAC;AAAA;AAAA,YACC,UAAU,cAAc;AAAA,YACxB,gBAAgB,EAAE,6CAA6C,cAAc;AAAA,YAC7E,YAAY;AAAA,cACV,OAAO,EAAE,oDAAoD,mBAAmB;AAAA,cAChF,aAAa,EAAE,qDAAqD,iBAAiB;AAAA,YACvF;AAAA,YACA,gBAAgB;AAAA,YAChB,aAAa;AAAA,YACb;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,YACA,aAAY;AAAA,YACZ,YAAY;AAAA,YACZ,aAAa;AAAA,YACb;AAAA,YACA,YAAY;AAAA,YACZ,sBAAsB,CAAC,uCAAuC;AAAA;AAAA,QAChE,IACE;AAAA,SACN;AAAA,MAEA,qBAAC,SAAI,WAAU,iCACb;AAAA,4BAAC,QAAG,WAAU,8DACX,YAAE,wCAAwC,mBAAmB,GAChE;AAAA,QACA;AAAA,UAAC;AAAA;AAAA,YACC,UAAQ;AAAA,YACR,OAAO,EAAE,sCAAsC,eAAe;AAAA,YAC9D,UAAS;AAAA,YACT,YAAW;AAAA,YACX,iBAAgB;AAAA,YAChB;AAAA,YACA,eAAe,iBAAiB;AAAA,YAChC,UAAU;AAAA,YACV,UAAU;AAAA,YACV,WAAW,CAAC;AAAA,YACZ,gBAAgB,EAAE,oCAAoC,qBAAqB;AAAA;AAAA,QAC7E;AAAA,SACF;AAAA,OACF,IAEA;AAAA,MAAC;AAAA;AAAA,QACC,aAAY;AAAA,QACZ,WAAW,cAAc;AAAA,QACzB,aAAY;AAAA,QACZ,MAAM;AAAA,QACN,WAAW;AAAA,QACX,iBAAiB;AAAA,QACjB;AAAA;AAAA,IACF;AAAA,KAEJ,GACF,GACF;AAEJ;",
|
|
4
|
+
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { useRouter, useSearchParams } from 'next/navigation'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { FormHeader } from '@open-mercato/ui/backend/forms'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { apiCallOrThrow, readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'\nimport { collectCustomFieldValues } from '@open-mercato/ui/backend/utils/customFieldValues'\nimport { createCrudFormError } from '@open-mercato/ui/backend/utils/serverErrors'\nimport { updateCrud, deleteCrud } from '@open-mercato/ui/backend/utils/crud'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { ActivitiesSection, NotesSection, type SectionAction, type TagOption } from '@open-mercato/ui/backend/detail'\nimport { VersionHistoryAction } from '@open-mercato/ui/backend/version-history'\nimport { SendObjectMessageDialog } from '@open-mercato/ui/backend/messages'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { createTranslatorWithFallback } from '@open-mercato/shared/lib/i18n/translate'\nimport { buildResourceScheduleItems } from '@open-mercato/core/modules/resources/lib/resourceSchedule'\nimport { RESOURCES_RESOURCE_FIELDSET_DEFAULT } from '@open-mercato/core/modules/resources/lib/resourceCustomFields'\nimport type { AvailabilityScheduleItemBuilder } from '@open-mercato/core/modules/planner/components/AvailabilityRulesEditor'\nimport { AvailabilityRulesEditor } from '@open-mercato/core/modules/planner/components/AvailabilityRulesEditor'\nimport { ResourcesResourceForm, useResourcesResourceFormConfig } from '@open-mercato/core/modules/resources/components/ResourceCrudForm'\nimport { renderDictionaryColor, renderDictionaryIcon, ICON_SUGGESTIONS } from '@open-mercato/core/modules/dictionaries/components/dictionaryAppearance'\nimport { createResourceNotesAdapter } from '@open-mercato/core/modules/resources/components/detail/notesAdapter'\nimport { createResourceActivitiesAdapter } from '@open-mercato/core/modules/resources/components/detail/activitiesAdapter'\nimport {\n createResourceDictionaryEntry,\n loadResourceDictionary,\n type DictionaryEntryOption,\n} from '@open-mercato/core/modules/resources/components/detail/dictionaries'\nimport type { DictionarySelectLabels } from '@open-mercato/core/modules/dictionaries/components/DictionaryEntrySelect'\n\ntype ResourceRecord = {\n id: string\n name: string\n description?: string | null\n resourceTypeId: string | null\n capacity: number | null\n capacityUnitValue: string | null\n capacityUnitName: string | null\n capacityUnitColor: string | null\n capacityUnitIcon: string | null\n tags?: TagOption[] | null\n isActive: boolean\n appearanceIcon?: string | null\n appearanceColor?: string | null\n resource_type_id?: string | null\n capacity_unit_value?: string | null\n capacity_unit_name?: string | null\n capacity_unit_color?: string | null\n capacity_unit_icon?: string | null\n appearance_icon?: string | null\n appearance_color?: string | null\n is_active?: boolean\n availabilityRuleSetId?: string | null\n availability_rule_set_id?: string | null\n} & Record<string, unknown>\n\ntype ResourceResponse = {\n items: ResourceRecord[]\n}\n\nfunction normalizeResourceRecord(record: ResourceRecord): ResourceRecord {\n return {\n ...record,\n resourceTypeId: record.resourceTypeId ?? record.resource_type_id ?? null,\n description: record.description ?? null,\n capacityUnitValue: record.capacityUnitValue ?? record.capacity_unit_value ?? null,\n capacityUnitName: record.capacityUnitName ?? record.capacity_unit_name ?? null,\n capacityUnitColor: record.capacityUnitColor ?? record.capacity_unit_color ?? null,\n capacityUnitIcon: record.capacityUnitIcon ?? record.capacity_unit_icon ?? null,\n appearanceIcon: record.appearanceIcon ?? record.appearance_icon ?? null,\n appearanceColor: record.appearanceColor ?? record.appearance_color ?? null,\n isActive: record.isActive ?? record.is_active ?? true,\n }\n}\n\nexport default function ResourcesResourceDetailPage({ params }: { params?: { id?: string } }) {\n const resourceId = params?.id\n const t = useT()\n const detailTranslator = React.useMemo(() => createTranslatorWithFallback(t), [t])\n const router = useRouter()\n const searchParams = useSearchParams()\n const [initialValues, setInitialValues] = React.useState<Record<string, unknown> | null>(null)\n const [tags, setTags] = React.useState<TagOption[]>([])\n const [activeTab, setActiveTab] = React.useState<'details' | 'availability'>('details')\n const [activeDetailTab, setActiveDetailTab] = React.useState<'notes' | 'activities'>('notes')\n const [sectionAction, setSectionAction] = React.useState<SectionAction | null>(null)\n const [availabilityRuleSetId, setAvailabilityRuleSetId] = React.useState<string | null>(null)\n const [activityDictionaryId, setActivityDictionaryId] = React.useState<string | null>(null)\n const [activityTypeEntries, setActivityTypeEntries] = React.useState<DictionaryEntryOption[]>([])\n const flashShownRef = React.useRef(false)\n\n const availabilityMode = 'availability'\n const notesAdapter = React.useMemo(() => createResourceNotesAdapter(detailTranslator), [detailTranslator])\n const activitiesAdapter = React.useMemo(() => createResourceActivitiesAdapter(detailTranslator), [detailTranslator])\n\n const activityTypeLabels = React.useMemo<DictionarySelectLabels>(() => ({\n placeholder: t('resources.resources.detail.activities.dictionary.placeholder', 'Select an activity type'),\n addLabel: t('resources.resources.detail.activities.dictionary.add', 'Add type'),\n addPrompt: t('resources.resources.detail.activities.dictionary.prompt', 'Name the type'),\n dialogTitle: t('resources.resources.detail.activities.dictionary.dialogTitle', 'Add activity type'),\n valueLabel: t('resources.resources.detail.activities.dictionary.valueLabel', 'Name'),\n valuePlaceholder: t('resources.resources.detail.activities.dictionary.valuePlaceholder', 'Name'),\n labelLabel: t('resources.resources.detail.activities.dictionary.labelLabel', 'Label'),\n labelPlaceholder: t('resources.resources.detail.activities.dictionary.labelPlaceholder', 'Display name shown in UI'),\n emptyError: t('resources.resources.detail.activities.dictionary.emptyError', 'Please enter a name'),\n cancelLabel: t('resources.resources.detail.activities.dictionary.cancel', 'Cancel'),\n saveLabel: t('resources.resources.detail.activities.dictionary.save', 'Save'),\n saveShortcutHint: t('resources.resources.detail.activities.dictionary.saveShortcut', '\u2318/Ctrl + Enter'),\n errorLoad: t('resources.resources.detail.activities.dictionary.errorLoad', 'Failed to load options'),\n errorSave: t('resources.resources.detail.activities.dictionary.errorSave', 'Failed to save option'),\n loadingLabel: t('resources.resources.detail.activities.dictionary.loading', 'Loading\u2026'),\n manageTitle: t('resources.resources.detail.activities.dictionary.manage', 'Manage dictionary'),\n }), [t])\n\n const loadActivityOptions = React.useCallback(async () => {\n const { dictionary, entries } = await loadResourceDictionary('activityTypes')\n setActivityDictionaryId(dictionary?.id ?? null)\n setActivityTypeEntries(entries)\n return entries\n }, [])\n\n const createActivityOption = React.useCallback(\n async (input: { value: string; label?: string; color?: string | null; icon?: string | null }) => {\n const entry = await createResourceDictionaryEntry('activityTypes', input)\n if (!entry) {\n throw new Error(t('resources.resources.detail.activities.dictionary.errorSave', 'Failed to save option'))\n }\n return entry\n },\n [t],\n )\n\n React.useEffect(() => {\n loadActivityOptions().catch(() => {})\n }, [loadActivityOptions])\n\n const activityTypeMap = React.useMemo(\n () => new Map(activityTypeEntries.map((entry) => [entry.value, entry])),\n [activityTypeEntries],\n )\n\n const resolveActivityPresentation = React.useCallback(\n (activity: { activityType: string; appearanceIcon?: string | null; appearanceColor?: string | null }) => {\n const entry = activityTypeMap.get(activity.activityType)\n return {\n label: entry?.label ?? activity.activityType,\n icon: entry?.icon ?? activity.appearanceIcon ?? null,\n color: entry?.color ?? activity.appearanceColor ?? null,\n }\n },\n [activityTypeMap],\n )\n\n const manageActivityHref = React.useMemo(() => {\n if (!activityDictionaryId) return '/backend/config/dictionaries'\n return `/backend/config/dictionaries?dictionaryId=${encodeURIComponent(activityDictionaryId)}`\n }, [activityDictionaryId])\n\n const appearanceLabels = React.useMemo(() => ({\n colorLabel: t('resources.resources.detail.activities.appearance.colorLabel', 'Color'),\n colorHelp: t('resources.resources.detail.activities.appearance.colorHelp', 'Pick a highlight color for this entry.'),\n colorClearLabel: t('resources.resources.detail.activities.appearance.colorClear', 'Remove color'),\n iconLabel: t('resources.resources.detail.activities.appearance.iconLabel', 'Icon or emoji'),\n iconPlaceholder: t('resources.resources.detail.activities.appearance.iconPlaceholder', 'Type an emoji or pick one of the suggestions.'),\n iconPickerTriggerLabel: t('resources.resources.detail.activities.appearance.iconBrowse', 'Browse icons and emojis'),\n iconSearchPlaceholder: t('resources.resources.detail.activities.appearance.iconSearchPlaceholder', 'Search icons or emojis\u2026'),\n iconSearchEmptyLabel: t('resources.resources.detail.activities.appearance.iconSearchEmpty', 'No icons match your search.'),\n iconSuggestionsLabel: t('resources.resources.detail.activities.appearance.iconSuggestions', 'Suggestions'),\n iconClearLabel: t('resources.resources.detail.activities.appearance.iconClear', 'Remove icon'),\n previewEmptyLabel: t('resources.resources.detail.activities.appearance.previewEmpty', 'No appearance selected'),\n }), [t])\n\n const renderCustomFields = React.useCallback((activity: { id?: string; customFields?: Array<{ key: string; label?: string | null; value: unknown }> }) => {\n const entries = Array.isArray(activity.customFields) ? activity.customFields : []\n if (!entries.length) return null\n const emptyLabel = t('resources.resources.detail.activities.customFields.empty', 'Not provided')\n return (\n <div className=\"grid gap-3 sm:grid-cols-2\">\n {entries.map((entry, index) => {\n const label = entry.label ?? entry.key\n const value = entry.value\n const hasValue = !(value == null || value === '' || (Array.isArray(value) && value.length === 0))\n const content = hasValue\n ? Array.isArray(value)\n ? value.map((item) => String(item)).join(', ')\n : String(value)\n : emptyLabel\n return (\n <div\n key={`activity-${activity.id ?? 'row'}-custom-${index}`}\n className=\"rounded-md border border-border/60 bg-muted/10 px-3 py-2\"\n >\n <div className=\"text-xs font-medium text-muted-foreground\">{label}</div>\n <div className=\"mt-1 text-sm text-foreground\">{content}</div>\n </div>\n )\n })}\n </div>\n )\n }, [t])\n\n React.useEffect(() => {\n if (!searchParams) return\n const tabParam = searchParams.get('tab')\n if (tabParam === 'availability') {\n setActiveTab('availability')\n }\n const created = searchParams.get('created') === '1'\n if (created && !flashShownRef.current) {\n flashShownRef.current = true\n flash(t('resources.resources.flash.createdAvailability', 'Saved. You can now set availability.'), 'success')\n const nextParams = new URLSearchParams(searchParams.toString())\n nextParams.delete('created')\n const nextQuery = nextParams.toString()\n const nextPath = resourceId\n ? `/backend/resources/resources/${encodeURIComponent(resourceId)}${nextQuery ? `?${nextQuery}` : ''}`\n : `/backend/resources/resources${nextQuery ? `?${nextQuery}` : ''}`\n router.replace(nextPath)\n }\n }, [resourceId, router, searchParams, t])\n\n const buildScheduleItems = React.useCallback<AvailabilityScheduleItemBuilder>(\n ({ availabilityRules, translate }) => buildResourceScheduleItems({\n availabilityRules,\n isAvailableByDefault: false,\n translate,\n }),\n [],\n )\n\n const tagLabels = React.useMemo(\n () => ({\n loading: t('resources.resources.tags.loading', 'Loading tags...'),\n placeholder: t('resources.resources.tags.placeholder', 'Type to add tags'),\n empty: t('resources.resources.tags.placeholder', 'No tags yet. Add labels to keep resources organized.'),\n loadError: t('resources.resources.tags.loadError', 'Failed to load tags.'),\n createError: t('resources.resources.tags.createError', 'Failed to create tag.'),\n updateError: t('resources.resources.tags.updateError', 'Failed to update tags.'),\n labelRequired: t('resources.resources.tags.labelRequired', 'Tag name is required.'),\n saveShortcut: t('resources.resources.tags.saveShortcut', 'Save Cmd+Enter / Ctrl+Enter'),\n cancelShortcut: t('resources.resources.tags.cancelShortcut', 'Cancel (Esc)'),\n edit: t('ui.forms.actions.edit', 'Edit'),\n cancel: t('ui.forms.actions.cancel', 'Cancel'),\n success: t('resources.resources.tags.success', 'Tags updated.'),\n }),\n [t],\n )\n\n const handleSubmit = React.useCallback(async (values: Record<string, unknown>) => {\n if (!resourceId) return\n const appearance = values.appearance && typeof values.appearance === 'object'\n ? values.appearance as { icon?: string | null; color?: string | null }\n : {}\n const { appearance: _appearance, ...rest } = values\n const customFieldsetCode = typeof values.customFieldsetCode === 'string' && values.customFieldsetCode.trim().length\n ? values.customFieldsetCode.trim()\n : RESOURCES_RESOURCE_FIELDSET_DEFAULT\n const payload: Record<string, unknown> = {\n ...rest,\n id: resourceId,\n resourceTypeId: values.resourceTypeId || null,\n capacity: values.capacity ? Number(values.capacity) : null,\n capacityUnitValue: values.capacityUnitValue ? String(values.capacityUnitValue) : null,\n appearanceIcon: appearance.icon ?? null,\n appearanceColor: appearance.color ?? null,\n isActive: values.isActive ?? true,\n customFieldsetCode,\n ...collectCustomFieldValues(values),\n }\n if (!payload.name || String(payload.name).trim().length === 0) {\n throw createCrudFormError(t('resources.resources.form.errors.nameRequired', 'Name is required.'))\n }\n await updateCrud('resources/resources', payload, {\n errorMessage: t('resources.resources.form.errors.update', 'Failed to update resource.'),\n })\n flash(t('resources.resources.form.flash.updated', 'Resource updated.'), 'success')\n }, [resourceId, t])\n\n const tabs = React.useMemo(() => ([\n { id: 'details', label: t('resources.resources.tabs.details', 'Details') },\n { id: 'availability', label: t('resources.resources.tabs.availability', 'Availability') },\n ]), [t])\n const detailTabs = React.useMemo(() => ([\n { id: 'notes' as const, label: t('resources.resources.detail.tabs.notes', 'Notes') },\n { id: 'activities' as const, label: t('resources.resources.detail.tabs.activities', 'Activities') },\n ]), [t])\n\n const loadTagOptions = React.useCallback(\n async (query?: string): Promise<TagOption[]> => {\n const params = new URLSearchParams({ pageSize: '100' })\n if (query) params.set('search', query)\n const payload = await readApiResultOrThrow<Record<string, unknown>>(\n `/api/resources/tags?${params.toString()}`,\n undefined,\n { errorMessage: t('resources.resources.tags.loadError', 'Failed to load tags.') },\n )\n const items = Array.isArray(payload?.items) ? payload.items : []\n return items\n .map((item: unknown): TagOption | null => {\n if (!item || typeof item !== 'object') return null\n const raw = item as { id?: unknown; tagId?: unknown; label?: unknown; slug?: unknown; color?: unknown }\n const rawId =\n typeof raw.id === 'string'\n ? raw.id\n : typeof raw.tagId === 'string'\n ? raw.tagId\n : null\n if (!rawId) return null\n const labelValue =\n (typeof raw.label === 'string' && raw.label.trim().length && raw.label.trim()) ||\n (typeof raw.slug === 'string' && raw.slug.trim().length && raw.slug.trim()) ||\n rawId\n const color = typeof raw.color === 'string' && raw.color.trim().length ? raw.color.trim() : null\n return { id: rawId, label: labelValue, color }\n })\n .filter((entry): entry is TagOption => entry !== null)\n },\n [t],\n )\n\n const createTag = React.useCallback(\n async (label: string): Promise<TagOption> => {\n const trimmed = label.trim()\n if (!trimmed.length) {\n throw new Error(t('resources.resources.tags.labelRequired', 'Tag name is required.'))\n }\n const response = await apiCallOrThrow<Record<string, unknown>>(\n '/api/resources/tags',\n {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ label: trimmed }),\n },\n { errorMessage: t('resources.resources.tags.createError', 'Failed to create tag.') },\n )\n const payload = response.result ?? {}\n const id =\n typeof payload?.id === 'string'\n ? payload.id\n : typeof (payload as any)?.tagId === 'string'\n ? (payload as any).tagId\n : ''\n if (!id) throw new Error(t('resources.resources.tags.createError', 'Failed to create tag.'))\n const color = typeof (payload as any)?.color === 'string' && (payload as any).color.trim().length\n ? (payload as any).color.trim()\n : null\n return { id, label: trimmed, color }\n },\n [t],\n )\n\n const assignTag = React.useCallback(async (tagId: string) => {\n if (!resourceId) return\n await apiCallOrThrow(\n '/api/resources/resources/tags/assign',\n {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ tagId, resourceId }),\n },\n { errorMessage: t('resources.resources.tags.updateError', 'Failed to update tags.') },\n )\n }, [resourceId, t])\n\n const unassignTag = React.useCallback(async (tagId: string) => {\n if (!resourceId) return\n await apiCallOrThrow(\n '/api/resources/resources/tags/unassign',\n {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ tagId, resourceId }),\n },\n { errorMessage: t('resources.resources.tags.updateError', 'Failed to update tags.') },\n )\n }, [resourceId, t])\n\n const handleTagsSave = React.useCallback(\n async ({ next, added, removed }: { next: TagOption[]; added: TagOption[]; removed: TagOption[] }) => {\n if (!resourceId) return\n for (const tag of added) {\n await assignTag(tag.id)\n }\n for (const tag of removed) {\n await unassignTag(tag.id)\n }\n setTags(next)\n flash(t('resources.resources.tags.success', 'Tags updated.'), 'success')\n },\n [assignTag, resourceId, t, unassignTag],\n )\n\n const tagsSection = React.useMemo(\n () => ({\n title: t('resources.resources.tags.title', 'Tags'),\n tags,\n onChange: setTags,\n loadOptions: loadTagOptions,\n createTag,\n onSave: handleTagsSave,\n labels: tagLabels,\n }),\n [createTag, handleTagsSave, loadTagOptions, t, tagLabels, tags],\n )\n\n const formConfig = useResourcesResourceFormConfig({ tagsSection })\n const { resourceTypesLoaded, resolveFieldsetCode } = formConfig\n\n React.useEffect(() => {\n if (!resourceId || !resourceTypesLoaded) return\n let cancelled = false\n async function loadResource() {\n try {\n const params = new URLSearchParams()\n params.set('page', '1')\n params.set('pageSize', '1')\n if (resourceId) params.set('ids', resourceId)\n const record = await readApiResultOrThrow<ResourceResponse>(`/api/resources/resources?${params.toString()}`)\n const resourceRaw = Array.isArray(record?.items) ? record.items[0] : null\n const resource = resourceRaw ? normalizeResourceRecord(resourceRaw) : null\n if (!resource) throw new Error(t('resources.resources.form.errors.notFound', 'Resource not found.'))\n if (!cancelled) {\n const customValues: Record<string, unknown> = {}\n for (const [key, value] of Object.entries(resource)) {\n if (key.startsWith('cf_')) customValues[key] = value\n else if (key.startsWith('cf:')) customValues[`cf_${key.slice(3)}`] = value\n }\n setTags(Array.isArray(resource.tags) ? resource.tags : [])\n setAvailabilityRuleSetId(\n typeof resource.availabilityRuleSetId === 'string'\n ? resource.availabilityRuleSetId\n : typeof resource.availability_rule_set_id === 'string'\n ? resource.availability_rule_set_id\n : null,\n )\n setInitialValues({\n id: resource.id,\n name: resource.name,\n description: resource.description ?? '',\n resourceTypeId: resource.resourceTypeId || '',\n capacity: resource.capacity ?? '',\n capacityUnitValue: resource.capacityUnitValue ?? '',\n appearance: { icon: resource.appearanceIcon ?? null, color: resource.appearanceColor ?? null },\n isActive: resource.isActive ?? true,\n customFieldsetCode: resource.resourceTypeId\n ? resolveFieldsetCode(resource.resourceTypeId)\n : RESOURCES_RESOURCE_FIELDSET_DEFAULT,\n ...customValues,\n })\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : t('resources.resources.form.errors.load', 'Failed to load resource.')\n flash(message, 'error')\n }\n }\n loadResource()\n return () => { cancelled = true }\n }, [resourceId, resourceTypesLoaded, resolveFieldsetCode, t])\n\n const handleDelete = React.useCallback(async () => {\n if (!resourceId) return\n await deleteCrud('resources/resources', resourceId, {\n errorMessage: t('resources.resources.form.errors.delete', 'Failed to delete resource.'),\n })\n flash(t('resources.resources.form.flash.deleted', 'Resource deleted.'), 'success')\n router.push('/backend/resources/resources')\n }, [resourceId, router, t])\n\n const handleRulesetChange = React.useCallback(async (nextId: string | null) => {\n if (!resourceId) return\n await updateCrud('resources/resources', { id: resourceId, availabilityRuleSetId: nextId }, {\n errorMessage: t('resources.resources.availability.ruleset.updateError', 'Failed to update schedule.'),\n })\n setAvailabilityRuleSetId(nextId)\n flash(t('resources.resources.availability.ruleset.updateSuccess', 'Schedule updated.'), 'success')\n }, [resourceId, t])\n\n const resourceTitle =\n typeof initialValues?.name === 'string' && initialValues.name.trim().length > 0\n ? initialValues.name.trim()\n : t('resources.resources.detail.untitled', 'Unnamed resource')\n\n return (\n <Page>\n <PageBody>\n <div className=\"space-y-6\">\n <FormHeader\n mode=\"detail\"\n backHref=\"/backend/resources/resources\"\n backLabel={t('resources.resources.detail.back', 'Back to resources')}\n utilityActions={(\n <>\n {resourceId ? (\n <SendObjectMessageDialog\n object={{\n entityModule: 'resources',\n entityType: 'resource',\n entityId: resourceId,\n previewData: {\n title: resourceTitle,\n },\n }}\n viewHref={`/backend/resources/resources/${resourceId}`}\n />\n ) : null}\n <VersionHistoryAction\n config={resourceId ? { resourceKind: 'resources.resource', resourceId, includeRelated: true } : null}\n t={t}\n />\n </>\n )}\n title={resourceTitle}\n subtitle={t('resources.resources.detail.subtitle', 'Resource profile and activity')}\n />\n\n <div className=\"border-b\">\n <nav className=\"flex flex-wrap items-center gap-5 text-sm\" aria-label={t('resources.resources.tabs.label', 'Resource sections')}>\n {tabs.map((tab) => (\n <Button\n key={tab.id}\n type=\"button\"\n role=\"tab\"\n aria-selected={activeTab === tab.id}\n variant=\"ghost\"\n size=\"sm\"\n className={`relative -mb-px h-auto rounded-none border-b-2 px-0 py-2 font-medium ${\n activeTab === tab.id\n ? 'border-primary text-foreground'\n : 'border-transparent text-muted-foreground hover:text-foreground'\n }`}\n onClick={() => setActiveTab(tab.id as 'details' | 'availability')}\n >\n {tab.label}\n </Button>\n ))}\n </nav>\n </div>\n\n {activeTab === 'details' ? (\n <>\n <div className=\"rounded-lg border bg-card p-4\">\n <div className=\"flex flex-wrap items-center justify-between gap-3\">\n <div className=\"flex gap-2\">\n {detailTabs.map((tab) => (\n <Button\n key={tab.id}\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n className={`relative -mb-px h-auto rounded-none border-b-2 px-0 py-1 font-medium ${\n activeDetailTab === tab.id\n ? 'border-primary text-foreground'\n : 'border-transparent text-muted-foreground hover:text-foreground'\n }`}\n onClick={() => setActiveDetailTab(tab.id)}\n >\n {tab.label}\n </Button>\n ))}\n </div>\n {sectionAction ? (\n <Button\n type=\"button\"\n size=\"sm\"\n disabled={sectionAction.disabled}\n onClick={() => sectionAction.onClick()}\n >\n {sectionAction.icon ?? null}\n {sectionAction.label}\n </Button>\n ) : null}\n </div>\n {activeDetailTab === 'notes' ? (\n <NotesSection\n entityId={resourceId ?? null}\n emptyLabel={t('resources.resources.detail.notes.empty', 'No notes yet.')}\n viewerUserId={null}\n viewerName={null}\n viewerEmail={null}\n addActionLabel={t('resources.resources.detail.notes.add', 'Add note')}\n emptyState={{\n title: t('resources.resources.detail.notes.emptyTitle', 'Keep everyone in the loop'),\n actionLabel: t('resources.resources.detail.notes.emptyAction', 'Add a note'),\n }}\n onActionChange={setSectionAction}\n translator={detailTranslator}\n labelPrefix=\"resources.resources.detail.notes\"\n inlineLabelPrefix=\"resources.resources.detail.inline\"\n dataAdapter={notesAdapter}\n renderIcon={renderDictionaryIcon}\n renderColor={renderDictionaryColor}\n iconSuggestions={ICON_SUGGESTIONS}\n />\n ) : null}\n {activeDetailTab === 'activities' ? (\n <ActivitiesSection\n entityId={resourceId ?? null}\n addActionLabel={t('resources.resources.detail.activities.add', 'Log activity')}\n emptyState={{\n title: t('resources.resources.detail.activities.emptyTitle', 'No activities yet'),\n actionLabel: t('resources.resources.detail.activities.emptyAction', 'Add an activity'),\n }}\n onActionChange={setSectionAction}\n dataAdapter={activitiesAdapter}\n activityTypeLabels={activityTypeLabels}\n loadActivityOptions={loadActivityOptions}\n createActivityOption={createActivityOption}\n resolveActivityPresentation={resolveActivityPresentation}\n renderCustomFields={renderCustomFields}\n labelPrefix=\"resources.resources.detail.activities\"\n renderIcon={renderDictionaryIcon}\n renderColor={renderDictionaryColor}\n appearanceLabels={appearanceLabels}\n manageHref={manageActivityHref}\n customFieldEntityIds={['resources:resources_resource_activity']}\n />\n ) : null}\n </div>\n\n <div className=\"rounded-lg border bg-card p-4\">\n <h2 className=\"mb-4 text-sm font-semibold uppercase text-muted-foreground\">\n {t('resources.resources.detail.formTitle', 'Resource settings')}\n </h2>\n <ResourcesResourceForm\n embedded\n title={t('resources.resources.form.editTitle', 'Edit resource')}\n backHref=\"/backend/resources/resources\"\n cancelHref=\"/backend/resources/resources\"\n successRedirect=\"/backend/resources/resources\"\n formConfig={formConfig}\n initialValues={initialValues ?? undefined}\n onSubmit={handleSubmit}\n onDelete={handleDelete}\n isLoading={!initialValues}\n loadingMessage={t('resources.resources.form.loading', 'Loading resource...')}\n />\n </div>\n </>\n ) : (\n <AvailabilityRulesEditor\n subjectType=\"resource\"\n subjectId={resourceId ?? ''}\n labelPrefix=\"resources.resources\"\n mode={availabilityMode}\n rulesetId={availabilityRuleSetId}\n onRulesetChange={handleRulesetChange}\n buildScheduleItems={buildScheduleItems}\n />\n )}\n </div>\n </PageBody>\n </Page>\n )\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["params"]
|
|
7
7
|
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { createRequestContainer } from "@open-mercato/shared/lib/di/container";
|
|
2
|
+
import { findOneWithDecryption } from "@open-mercato/shared/lib/encryption/find";
|
|
3
|
+
import { resolveTranslations } from "@open-mercato/shared/lib/i18n/server";
|
|
4
|
+
import { ResourcesResource } from "../data/entities.js";
|
|
5
|
+
async function resolveEm() {
|
|
6
|
+
const { resolve } = await createRequestContainer();
|
|
7
|
+
return resolve("em");
|
|
8
|
+
}
|
|
9
|
+
async function loadResourcePreview(entityId, ctx) {
|
|
10
|
+
const { t } = await resolveTranslations();
|
|
11
|
+
const defaultTitle = t("resources.messageObjects.resource.title");
|
|
12
|
+
if (!ctx.organizationId) {
|
|
13
|
+
return { title: defaultTitle, subtitle: entityId };
|
|
14
|
+
}
|
|
15
|
+
const em = await resolveEm();
|
|
16
|
+
const entity = await findOneWithDecryption(
|
|
17
|
+
em,
|
|
18
|
+
ResourcesResource,
|
|
19
|
+
{
|
|
20
|
+
id: entityId,
|
|
21
|
+
tenantId: ctx.tenantId,
|
|
22
|
+
organizationId: ctx.organizationId,
|
|
23
|
+
deletedAt: null
|
|
24
|
+
},
|
|
25
|
+
void 0,
|
|
26
|
+
{ tenantId: ctx.tenantId, organizationId: ctx.organizationId }
|
|
27
|
+
);
|
|
28
|
+
if (!entity) {
|
|
29
|
+
return {
|
|
30
|
+
title: defaultTitle,
|
|
31
|
+
subtitle: entityId,
|
|
32
|
+
status: t("customers.messageObjects.notFound"),
|
|
33
|
+
statusColor: "gray"
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
return {
|
|
37
|
+
title: entity.name
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
export {
|
|
41
|
+
loadResourcePreview
|
|
42
|
+
};
|
|
43
|
+
//# sourceMappingURL=messageObjectPreviews.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/modules/resources/lib/messageObjectPreviews.ts"],
|
|
4
|
+
"sourcesContent": ["import { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport type { ObjectPreviewData } from '@open-mercato/shared/modules/messages/types'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { ResourcesResource } from '../data/entities'\n\ntype PreviewContext = {\n tenantId: string\n organizationId?: string | null\n}\n\nasync function resolveEm() {\n const { resolve } = await createRequestContainer()\n return resolve('em') as EntityManager\n}\n\nexport async function loadResourcePreview(\n entityId: string,\n ctx: PreviewContext,\n): Promise<ObjectPreviewData> {\n const { t } = await resolveTranslations()\n const defaultTitle = t('resources.messageObjects.resource.title')\n\n if (!ctx.organizationId) {\n return { title: defaultTitle, subtitle: entityId }\n }\n\n const em = await resolveEm()\n const entity = await findOneWithDecryption(\n em,\n ResourcesResource,\n {\n id: entityId,\n tenantId: ctx.tenantId,\n organizationId: ctx.organizationId,\n deletedAt: null,\n },\n undefined,\n { tenantId: ctx.tenantId, organizationId: ctx.organizationId },\n )\n\n if (!entity) {\n return {\n title: defaultTitle,\n subtitle: entityId,\n status: t('customers.messageObjects.notFound'),\n statusColor: 'gray',\n }\n }\n\n return {\n title: entity.name,\n }\n}\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,8BAA8B;AACvC,SAAS,6BAA6B;AACtC,SAAS,2BAA2B;AAGpC,SAAS,yBAAyB;AAOlC,eAAe,YAAY;AACzB,QAAM,EAAE,QAAQ,IAAI,MAAM,uBAAuB;AACjD,SAAO,QAAQ,IAAI;AACrB;AAEA,eAAsB,oBACpB,UACA,KAC4B;AAC5B,QAAM,EAAE,EAAE,IAAI,MAAM,oBAAoB;AACxC,QAAM,eAAe,EAAE,yCAAyC;AAEhE,MAAI,CAAC,IAAI,gBAAgB;AACvB,WAAO,EAAE,OAAO,cAAc,UAAU,SAAS;AAAA,EACnD;AAEA,QAAM,KAAK,MAAM,UAAU;AAC3B,QAAM,SAAS,MAAM;AAAA,IACnB;AAAA,IACA;AAAA,IACA;AAAA,MACE,IAAI;AAAA,MACJ,UAAU,IAAI;AAAA,MACd,gBAAgB,IAAI;AAAA,MACpB,WAAW;AAAA,IACb;AAAA,IACA;AAAA,IACA,EAAE,UAAU,IAAI,UAAU,gBAAgB,IAAI,eAAe;AAAA,EAC/D;AAEA,MAAI,CAAC,QAAQ;AACX,WAAO;AAAA,MACL,OAAO;AAAA,MACP,UAAU;AAAA,MACV,QAAQ,EAAE,mCAAmC;AAAA,MAC7C,aAAa;AAAA,IACf;AAAA,EACF;AAEA,SAAO;AAAA,IACL,OAAO,OAAO;AAAA,EAChB;AACF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { MessageObjectDetail, MessageObjectPreview } from "@open-mercato/ui/backend/messages";
|
|
2
|
+
const objectMessageTypes = ["default", "messages.defaultWithObjects"];
|
|
3
|
+
const messageObjectTypes = [
|
|
4
|
+
{
|
|
5
|
+
module: "resources",
|
|
6
|
+
entityType: "resource",
|
|
7
|
+
messageTypes: objectMessageTypes,
|
|
8
|
+
entityId: "resources:resources_resource",
|
|
9
|
+
optionLabelField: "name",
|
|
10
|
+
optionSubtitleField: "description",
|
|
11
|
+
labelKey: "resources.messageObjects.resource.title",
|
|
12
|
+
icon: "package",
|
|
13
|
+
PreviewComponent: MessageObjectPreview,
|
|
14
|
+
DetailComponent: MessageObjectDetail,
|
|
15
|
+
actions: [
|
|
16
|
+
{
|
|
17
|
+
id: "view",
|
|
18
|
+
labelKey: "common.view",
|
|
19
|
+
variant: "outline",
|
|
20
|
+
href: "/backend/resources/resources/{entityId}"
|
|
21
|
+
}
|
|
22
|
+
],
|
|
23
|
+
loadPreview: async (entityId, ctx) => {
|
|
24
|
+
if (typeof window !== "undefined") {
|
|
25
|
+
return { title: "Resource", subtitle: entityId };
|
|
26
|
+
}
|
|
27
|
+
const { loadResourcePreview } = await import("./lib/messageObjectPreviews.js");
|
|
28
|
+
return loadResourcePreview(entityId, ctx);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
];
|
|
32
|
+
var message_objects_default = messageObjectTypes;
|
|
33
|
+
export {
|
|
34
|
+
message_objects_default as default,
|
|
35
|
+
messageObjectTypes
|
|
36
|
+
};
|
|
37
|
+
//# sourceMappingURL=message-objects.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/modules/resources/message-objects.ts"],
|
|
4
|
+
"sourcesContent": ["import type { MessageObjectTypeDefinition } from '@open-mercato/shared/modules/messages/types'\nimport { MessageObjectDetail, MessageObjectPreview } from '@open-mercato/ui/backend/messages'\n\nconst objectMessageTypes = ['default', 'messages.defaultWithObjects']\n\nexport const messageObjectTypes: MessageObjectTypeDefinition[] = [\n {\n module: 'resources',\n entityType: 'resource',\n messageTypes: objectMessageTypes,\n entityId: 'resources:resources_resource',\n optionLabelField: 'name',\n optionSubtitleField: 'description',\n labelKey: 'resources.messageObjects.resource.title',\n icon: 'package',\n PreviewComponent: MessageObjectPreview,\n DetailComponent: MessageObjectDetail,\n actions: [\n {\n id: 'view',\n labelKey: 'common.view',\n variant: 'outline',\n href: '/backend/resources/resources/{entityId}',\n },\n ],\n loadPreview: async (entityId, ctx) => {\n if (typeof window !== 'undefined') {\n return { title: 'Resource', subtitle: entityId }\n }\n const { loadResourcePreview } = await import('./lib/messageObjectPreviews')\n return loadResourcePreview(entityId, ctx)\n },\n },\n]\n\nexport default messageObjectTypes\n"],
|
|
5
|
+
"mappings": "AACA,SAAS,qBAAqB,4BAA4B;AAE1D,MAAM,qBAAqB,CAAC,WAAW,6BAA6B;AAE7D,MAAM,qBAAoD;AAAA,EAC/D;AAAA,IACE,QAAQ;AAAA,IACR,YAAY;AAAA,IACZ,cAAc;AAAA,IACd,UAAU;AAAA,IACV,kBAAkB;AAAA,IAClB,qBAAqB;AAAA,IACrB,UAAU;AAAA,IACV,MAAM;AAAA,IACN,kBAAkB;AAAA,IAClB,iBAAiB;AAAA,IACjB,SAAS;AAAA,MACP;AAAA,QACE,IAAI;AAAA,QACJ,UAAU;AAAA,QACV,SAAS;AAAA,QACT,MAAM;AAAA,MACR;AAAA,IACF;AAAA,IACA,aAAa,OAAO,UAAU,QAAQ;AACpC,UAAI,OAAO,WAAW,aAAa;AACjC,eAAO,EAAE,OAAO,YAAY,UAAU,SAAS;AAAA,MACjD;AACA,YAAM,EAAE,oBAAoB,IAAI,MAAM,OAAO,6BAA6B;AAC1E,aAAO,oBAAoB,UAAU,GAAG;AAAA,IAC1C;AAAA,EACF;AACF;AAEA,IAAO,0BAAQ;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -12,6 +12,7 @@ import { useT } from "@open-mercato/shared/lib/i18n/context";
|
|
|
12
12
|
import { useChannelFields, buildChannelPayload } from "@open-mercato/core/modules/sales/components/channels/channelFormFields";
|
|
13
13
|
import { E } from "../../../../../../../generated/entities.ids.generated.js";
|
|
14
14
|
import { SalesChannelOffersPanel } from "@open-mercato/core/modules/sales/components/channels/SalesChannelOffersPanel";
|
|
15
|
+
import { SendObjectMessageDialog } from "@open-mercato/ui/backend/messages";
|
|
15
16
|
function EditChannelPage({ params }) {
|
|
16
17
|
const channelId = params?.channelId ?? "";
|
|
17
18
|
const router = useRouter();
|
|
@@ -108,6 +109,24 @@ function EditChannelPage({ params }) {
|
|
|
108
109
|
{
|
|
109
110
|
title: t("sales.channels.form.editTitle", "Edit channel"),
|
|
110
111
|
versionHistory: { resourceKind: "sales.channel", resourceId: channelId ? String(channelId) : "" },
|
|
112
|
+
extraActions: channelId ? /* @__PURE__ */ jsx(
|
|
113
|
+
SendObjectMessageDialog,
|
|
114
|
+
{
|
|
115
|
+
object: {
|
|
116
|
+
entityModule: "sales",
|
|
117
|
+
entityType: "channel",
|
|
118
|
+
entityId: channelId,
|
|
119
|
+
previewData: {
|
|
120
|
+
title: initialValues?.name ?? "",
|
|
121
|
+
metadata: {
|
|
122
|
+
[t("sales.channels.form.contactEmail")]: initialValues?.contactEmail ?? "-",
|
|
123
|
+
[t("sales.channels.form.websiteUrl")]: initialValues?.websiteUrl ?? "-"
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
},
|
|
127
|
+
viewHref: `/backend/sales/channels/${channelId}/edit`
|
|
128
|
+
}
|
|
129
|
+
) : void 0,
|
|
111
130
|
entityId: E.sales.sales_channel,
|
|
112
131
|
fields,
|
|
113
132
|
groups: [
|