@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
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { jsx } from "react/jsx-runtime";
|
|
3
3
|
import SalesDocumentDetailPage from "../../documents/[id]/page.js";
|
|
4
4
|
function SalesQuoteDetailPage(props) {
|
|
5
|
-
return /* @__PURE__ */ jsx(SalesDocumentDetailPage, { ...props, initialKind: "quote" });
|
|
5
|
+
return /* @__PURE__ */ jsx(SalesDocumentDetailPage, { ...props, initialKind: "quote", includeAmountInMessageMetadata: true });
|
|
6
6
|
}
|
|
7
7
|
export {
|
|
8
8
|
SalesQuoteDetailPage as default
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../../src/modules/sales/backend/sales/quotes/%5Bid%5D/page.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\n\nimport SalesDocumentDetailPage from '../../documents/[id]/page'\n\nexport default function SalesQuoteDetailPage(props: { params: { id: string } }) {\n return <SalesDocumentDetailPage {...props} initialKind=\"quote\" />\n}\n"],
|
|
5
|
-
"mappings": ";AAKS;AAHT,OAAO,6BAA6B;AAErB,SAAR,qBAAsC,OAAmC;AAC9E,SAAO,oBAAC,2BAAyB,GAAG,OAAO,aAAY,SAAQ;
|
|
4
|
+
"sourcesContent": ["\"use client\"\n\nimport SalesDocumentDetailPage from '../../documents/[id]/page'\n\nexport default function SalesQuoteDetailPage(props: { params: { id: string } }) {\n return <SalesDocumentDetailPage {...props} initialKind=\"quote\" includeAmountInMessageMetadata />\n}\n"],
|
|
5
|
+
"mappings": ";AAKS;AAHT,OAAO,6BAA6B;AAErB,SAAR,qBAAsC,OAAmC;AAC9E,SAAO,oBAAC,2BAAyB,GAAG,OAAO,aAAY,SAAQ,gCAA8B,MAAC;AAChG;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createRequestContainer } from "@open-mercato/shared/lib/di/container";
|
|
2
2
|
import { findOneWithDecryption } from "@open-mercato/shared/lib/encryption/find";
|
|
3
3
|
import { resolveTranslations } from "@open-mercato/shared/lib/i18n/server";
|
|
4
|
-
import { SalesOrder, SalesQuote } from "../data/entities.js";
|
|
4
|
+
import { SalesChannel, SalesOrder, SalesQuote } from "../data/entities.js";
|
|
5
5
|
function resolveCustomerName(snapshot) {
|
|
6
6
|
if (!snapshot) return null;
|
|
7
7
|
const customer = snapshot.customer;
|
|
@@ -56,9 +56,12 @@ async function buildPreview(kind, entityId, record) {
|
|
|
56
56
|
const subtitleParts = [customerName, total].filter((part) => Boolean(part && part.trim().length > 0));
|
|
57
57
|
const subtitle = subtitleParts.length > 0 ? subtitleParts.join(" \u2022 ") : entityId;
|
|
58
58
|
const metadata = {};
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
59
|
+
const numberLabel = t("sales.documents.detail.number");
|
|
60
|
+
const customerLabel = t("sales.documents.detail.customer");
|
|
61
|
+
const totalLabel = t("sales.documents.detail.totals.grandTotalGross");
|
|
62
|
+
if (number) metadata[numberLabel] = number;
|
|
63
|
+
if (customerName) metadata[customerLabel] = customerName;
|
|
64
|
+
if (total) metadata[totalLabel] = total;
|
|
62
65
|
return {
|
|
63
66
|
title: number && number.trim().length > 0 ? number : defaultTitle,
|
|
64
67
|
subtitle,
|
|
@@ -107,7 +110,49 @@ async function loadSalesOrderPreview(entityId, ctx) {
|
|
|
107
110
|
const record = await loadDocumentRecord("order", entityId, ctx);
|
|
108
111
|
return await buildPreview("order", entityId, record);
|
|
109
112
|
}
|
|
113
|
+
async function loadSalesChannelPreview(entityId, ctx) {
|
|
114
|
+
const { t } = await resolveTranslations();
|
|
115
|
+
const defaultTitle = t("sales.messageObjects.channel.title");
|
|
116
|
+
if (!ctx.organizationId) {
|
|
117
|
+
return { title: defaultTitle, subtitle: entityId };
|
|
118
|
+
}
|
|
119
|
+
const { resolve } = await createRequestContainer();
|
|
120
|
+
const em = resolve("em");
|
|
121
|
+
const entity = await findOneWithDecryption(
|
|
122
|
+
em,
|
|
123
|
+
SalesChannel,
|
|
124
|
+
{
|
|
125
|
+
id: entityId,
|
|
126
|
+
tenantId: ctx.tenantId,
|
|
127
|
+
organizationId: ctx.organizationId,
|
|
128
|
+
deletedAt: null
|
|
129
|
+
},
|
|
130
|
+
void 0,
|
|
131
|
+
{ tenantId: ctx.tenantId, organizationId: ctx.organizationId }
|
|
132
|
+
);
|
|
133
|
+
if (!entity) {
|
|
134
|
+
return {
|
|
135
|
+
title: defaultTitle,
|
|
136
|
+
subtitle: entityId,
|
|
137
|
+
status: t("sales.messageObjects.notFound"),
|
|
138
|
+
statusColor: "gray"
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
const metadata = {};
|
|
142
|
+
const contactEmailLabel = t("sales.channels.form.contactEmail");
|
|
143
|
+
const websiteUrlLabel = t("sales.channels.form.websiteUrl");
|
|
144
|
+
if (entity.contactEmail && entity.contactEmail.trim().length > 0) metadata[contactEmailLabel] = entity.contactEmail;
|
|
145
|
+
if (entity.websiteUrl && entity.websiteUrl.trim().length > 0) metadata[websiteUrlLabel] = entity.websiteUrl;
|
|
146
|
+
return {
|
|
147
|
+
title: entity.name,
|
|
148
|
+
subtitle: entity.status ?? void 0,
|
|
149
|
+
status: entity.status ?? void 0,
|
|
150
|
+
statusColor: statusColor(entity.status),
|
|
151
|
+
metadata: Object.keys(metadata).length > 0 ? metadata : void 0
|
|
152
|
+
};
|
|
153
|
+
}
|
|
110
154
|
export {
|
|
155
|
+
loadSalesChannelPreview,
|
|
111
156
|
loadSalesOrderPreview,
|
|
112
157
|
loadSalesQuotePreview
|
|
113
158
|
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/modules/sales/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 { SalesOrder, SalesQuote } from '../data/entities'\n\ntype PreviewContext = {\n tenantId: string\n organizationId?: string | null\n}\n\ntype DocumentKind = 'order' | 'quote'\n\ntype SalesDocumentPreviewRecord = {\n id: string\n status?: string | null\n customerSnapshot?: Record<string, unknown> | null\n currencyCode?: string | null\n grandTotalGrossAmount?: string | null\n orderNumber?: string\n quoteNumber?: string\n}\n\nfunction resolveCustomerName(snapshot: Record<string, unknown> | null): string | null {\n if (!snapshot) return null\n const customer = snapshot.customer as Record<string, unknown> | undefined\n const contact = snapshot.contact as Record<string, unknown> | undefined\n const displayName = typeof customer?.displayName === 'string' ? customer.displayName : null\n if (displayName && displayName.trim().length > 0) return displayName.trim()\n const first = typeof contact?.firstName === 'string' ? contact.firstName : null\n const last = typeof contact?.lastName === 'string' ? contact.lastName : null\n const preferred = typeof contact?.preferredName === 'string' ? contact.preferredName : null\n const parts = [preferred ?? first, last]\n .map((part) => (typeof part === 'string' ? part.trim() : ''))\n .filter((part) => part.length > 0)\n return parts.length > 0 ? parts.join(' ') : null\n}\n\nfunction formatTotal(amount: string | null | undefined, currency: string | null | undefined): string | null {\n if (!amount) return null\n const value = Number(amount)\n if (!Number.isFinite(value)) return currency ? `${amount} ${currency}` : amount\n if (!currency) return value.toLocaleString()\n try {\n return new Intl.NumberFormat(undefined, { style: 'currency', currency }).format(value)\n } catch {\n return `${value.toLocaleString()} ${currency}`\n }\n}\n\nfunction statusColor(status: string | null | undefined): string | undefined {\n if (!status) return undefined\n const value = status.toLowerCase()\n if (value.includes('approved') || value.includes('paid') || value.includes('fulfilled') || value.includes('accepted')) {\n return 'green'\n }\n if (value.includes('cancel') || value.includes('rejected') || value.includes('void')) {\n return 'red'\n }\n if (value.includes('draft') || value.includes('pending') || value.includes('open') || value.includes('sent')) {\n return 'amber'\n }\n return 'blue'\n}\n\nasync function buildPreview(kind: DocumentKind, entityId: string, record: SalesDocumentPreviewRecord | null): Promise<ObjectPreviewData> {\n const { t } = await resolveTranslations()\n const defaultTitle = kind === 'quote' ? t('sales.messageObjects.quote.title') : t('sales.messageObjects.order.title')\n if (!record) {\n return {\n title: defaultTitle,\n subtitle: entityId,\n status: t('sales.messageObjects.notFound'),\n statusColor: 'gray',\n }\n }\n\n const number = kind === 'quote' ? record.quoteNumber : record.orderNumber\n const customerName = resolveCustomerName(record.customerSnapshot ?? null)\n const total = formatTotal(record.grandTotalGrossAmount, record.currencyCode)\n\n const subtitleParts = [customerName, total].filter((part): part is string => Boolean(part && part.trim().length > 0))\n const subtitle = subtitleParts.length > 0 ? subtitleParts.join(' \u2022 ') : entityId\n\n const metadata: Record<string, string> = {}\n if (number) metadata
|
|
5
|
-
"mappings": "AAAA,SAAS,8BAA8B;AACvC,SAAS,6BAA6B;AACtC,SAAS,2BAA2B;AAGpC,SAAS,YAAY,kBAAkB;
|
|
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 { SalesChannel, SalesOrder, SalesQuote } from '../data/entities'\n\ntype PreviewContext = {\n tenantId: string\n organizationId?: string | null\n}\n\ntype DocumentKind = 'order' | 'quote'\n\ntype SalesDocumentPreviewRecord = {\n id: string\n status?: string | null\n customerSnapshot?: Record<string, unknown> | null\n currencyCode?: string | null\n grandTotalGrossAmount?: string | null\n orderNumber?: string\n quoteNumber?: string\n}\n\nfunction resolveCustomerName(snapshot: Record<string, unknown> | null): string | null {\n if (!snapshot) return null\n const customer = snapshot.customer as Record<string, unknown> | undefined\n const contact = snapshot.contact as Record<string, unknown> | undefined\n const displayName = typeof customer?.displayName === 'string' ? customer.displayName : null\n if (displayName && displayName.trim().length > 0) return displayName.trim()\n const first = typeof contact?.firstName === 'string' ? contact.firstName : null\n const last = typeof contact?.lastName === 'string' ? contact.lastName : null\n const preferred = typeof contact?.preferredName === 'string' ? contact.preferredName : null\n const parts = [preferred ?? first, last]\n .map((part) => (typeof part === 'string' ? part.trim() : ''))\n .filter((part) => part.length > 0)\n return parts.length > 0 ? parts.join(' ') : null\n}\n\nfunction formatTotal(amount: string | null | undefined, currency: string | null | undefined): string | null {\n if (!amount) return null\n const value = Number(amount)\n if (!Number.isFinite(value)) return currency ? `${amount} ${currency}` : amount\n if (!currency) return value.toLocaleString()\n try {\n return new Intl.NumberFormat(undefined, { style: 'currency', currency }).format(value)\n } catch {\n return `${value.toLocaleString()} ${currency}`\n }\n}\n\nfunction statusColor(status: string | null | undefined): string | undefined {\n if (!status) return undefined\n const value = status.toLowerCase()\n if (value.includes('approved') || value.includes('paid') || value.includes('fulfilled') || value.includes('accepted')) {\n return 'green'\n }\n if (value.includes('cancel') || value.includes('rejected') || value.includes('void')) {\n return 'red'\n }\n if (value.includes('draft') || value.includes('pending') || value.includes('open') || value.includes('sent')) {\n return 'amber'\n }\n return 'blue'\n}\n\nasync function buildPreview(kind: DocumentKind, entityId: string, record: SalesDocumentPreviewRecord | null): Promise<ObjectPreviewData> {\n const { t } = await resolveTranslations()\n const defaultTitle = kind === 'quote' ? t('sales.messageObjects.quote.title') : t('sales.messageObjects.order.title')\n if (!record) {\n return {\n title: defaultTitle,\n subtitle: entityId,\n status: t('sales.messageObjects.notFound'),\n statusColor: 'gray',\n }\n }\n\n const number = kind === 'quote' ? record.quoteNumber : record.orderNumber\n const customerName = resolveCustomerName(record.customerSnapshot ?? null)\n const total = formatTotal(record.grandTotalGrossAmount, record.currencyCode)\n\n const subtitleParts = [customerName, total].filter((part): part is string => Boolean(part && part.trim().length > 0))\n const subtitle = subtitleParts.length > 0 ? subtitleParts.join(' \u2022 ') : entityId\n\n const metadata: Record<string, string> = {}\n const numberLabel = t('sales.documents.detail.number')\n const customerLabel = t('sales.documents.detail.customer')\n const totalLabel = t('sales.documents.detail.totals.grandTotalGross')\n if (number) metadata[numberLabel] = number\n if (customerName) metadata[customerLabel] = customerName\n if (total) metadata[totalLabel] = total\n\n return {\n title: number && number.trim().length > 0 ? number : defaultTitle,\n subtitle,\n status: record.status ?? undefined,\n statusColor: statusColor(record.status),\n metadata: Object.keys(metadata).length > 0 ? metadata : undefined,\n }\n}\n\nasync function loadDocumentRecord(\n kind: DocumentKind,\n entityId: string,\n ctx: PreviewContext,\n): Promise<SalesDocumentPreviewRecord | null> {\n if (!ctx.organizationId) return null\n\n const { resolve } = await createRequestContainer()\n const em = resolve('em') as EntityManager\n const scope = { tenantId: ctx.tenantId, organizationId: ctx.organizationId }\n\n if (kind === 'quote') {\n return await findOneWithDecryption(\n em,\n SalesQuote,\n {\n id: entityId,\n tenantId: ctx.tenantId,\n organizationId: ctx.organizationId,\n deletedAt: null,\n },\n undefined,\n scope,\n )\n }\n\n return await findOneWithDecryption(\n em,\n SalesOrder,\n {\n id: entityId,\n tenantId: ctx.tenantId,\n organizationId: ctx.organizationId,\n deletedAt: null,\n },\n undefined,\n scope,\n )\n}\n\nexport async function loadSalesQuotePreview(entityId: string, ctx: PreviewContext): Promise<ObjectPreviewData> {\n const record = await loadDocumentRecord('quote', entityId, ctx)\n return await buildPreview('quote', entityId, record)\n}\n\nexport async function loadSalesOrderPreview(entityId: string, ctx: PreviewContext): Promise<ObjectPreviewData> {\n const record = await loadDocumentRecord('order', entityId, ctx)\n return await buildPreview('order', entityId, record)\n}\n\nexport async function loadSalesChannelPreview(entityId: string, ctx: PreviewContext): Promise<ObjectPreviewData> {\n const { t } = await resolveTranslations()\n const defaultTitle = t('sales.messageObjects.channel.title')\n\n if (!ctx.organizationId) {\n return { title: defaultTitle, subtitle: entityId }\n }\n\n const { resolve } = await createRequestContainer()\n const em = resolve('em') as EntityManager\n const entity = await findOneWithDecryption(\n em,\n SalesChannel,\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('sales.messageObjects.notFound'),\n statusColor: 'gray',\n }\n }\n\n const metadata: Record<string, string> = {}\n const contactEmailLabel = t('sales.channels.form.contactEmail')\n const websiteUrlLabel = t('sales.channels.form.websiteUrl')\n if (entity.contactEmail && entity.contactEmail.trim().length > 0) metadata[contactEmailLabel] = entity.contactEmail\n if (entity.websiteUrl && entity.websiteUrl.trim().length > 0) metadata[websiteUrlLabel] = entity.websiteUrl\n\n return {\n title: entity.name,\n subtitle: entity.status ?? undefined,\n status: entity.status ?? undefined,\n statusColor: statusColor(entity.status),\n metadata: Object.keys(metadata).length > 0 ? metadata : undefined,\n }\n}\n\n\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { MessageObjectDetail, MessageObjectPreview } from "@open-mercato/ui/backend/messages";
|
|
1
2
|
import { SalesDocumentMessageDetail } from "./widgets/messages/SalesDocumentMessageDetail.js";
|
|
2
3
|
import { SalesDocumentMessagePreview } from "./widgets/messages/SalesDocumentMessagePreview.js";
|
|
3
4
|
const objectMessageTypes = ["default", "messages.defaultWithObjects"];
|
|
@@ -13,7 +14,14 @@ const messageObjectTypes = [
|
|
|
13
14
|
icon: "receipt-text",
|
|
14
15
|
PreviewComponent: SalesDocumentMessagePreview,
|
|
15
16
|
DetailComponent: SalesDocumentMessageDetail,
|
|
16
|
-
actions: [
|
|
17
|
+
actions: [
|
|
18
|
+
{
|
|
19
|
+
id: "view",
|
|
20
|
+
labelKey: "common.view",
|
|
21
|
+
variant: "outline",
|
|
22
|
+
href: "/backend/sales/orders/{entityId}"
|
|
23
|
+
}
|
|
24
|
+
],
|
|
17
25
|
loadPreview: async (entityId, ctx) => {
|
|
18
26
|
if (typeof window !== "undefined") {
|
|
19
27
|
return {
|
|
@@ -36,7 +44,14 @@ const messageObjectTypes = [
|
|
|
36
44
|
icon: "file-text",
|
|
37
45
|
PreviewComponent: SalesDocumentMessagePreview,
|
|
38
46
|
DetailComponent: SalesDocumentMessageDetail,
|
|
39
|
-
actions: [
|
|
47
|
+
actions: [
|
|
48
|
+
{
|
|
49
|
+
id: "view",
|
|
50
|
+
labelKey: "common.view",
|
|
51
|
+
variant: "outline",
|
|
52
|
+
href: "/backend/sales/quotes/{entityId}"
|
|
53
|
+
}
|
|
54
|
+
],
|
|
40
55
|
loadPreview: async (entityId, ctx) => {
|
|
41
56
|
if (typeof window !== "undefined") {
|
|
42
57
|
return {
|
|
@@ -47,6 +62,33 @@ const messageObjectTypes = [
|
|
|
47
62
|
const { loadSalesQuotePreview } = await import("./lib/messageObjectPreviews.js");
|
|
48
63
|
return loadSalesQuotePreview(entityId, ctx);
|
|
49
64
|
}
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
module: "sales",
|
|
68
|
+
entityType: "channel",
|
|
69
|
+
messageTypes: objectMessageTypes,
|
|
70
|
+
entityId: "sales:sales_channel",
|
|
71
|
+
optionLabelField: "name",
|
|
72
|
+
optionSubtitleField: "status",
|
|
73
|
+
labelKey: "sales.messageObjects.channel.title",
|
|
74
|
+
icon: "store",
|
|
75
|
+
PreviewComponent: MessageObjectPreview,
|
|
76
|
+
DetailComponent: MessageObjectDetail,
|
|
77
|
+
actions: [
|
|
78
|
+
{
|
|
79
|
+
id: "view",
|
|
80
|
+
labelKey: "common.view",
|
|
81
|
+
variant: "outline",
|
|
82
|
+
href: "/backend/sales/channels/{entityId}/edit"
|
|
83
|
+
}
|
|
84
|
+
],
|
|
85
|
+
loadPreview: async (entityId, ctx) => {
|
|
86
|
+
if (typeof window !== "undefined") {
|
|
87
|
+
return { title: "Sales channel", subtitle: entityId };
|
|
88
|
+
}
|
|
89
|
+
const { loadSalesChannelPreview } = await import("./lib/messageObjectPreviews.js");
|
|
90
|
+
return loadSalesChannelPreview(entityId, ctx);
|
|
91
|
+
}
|
|
50
92
|
}
|
|
51
93
|
];
|
|
52
94
|
var message_objects_default = messageObjectTypes;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../src/modules/sales/message-objects.ts"],
|
|
4
|
-
"sourcesContent": ["import type { MessageObjectTypeDefinition } from '@open-mercato/shared/modules/messages/types'\nimport { SalesDocumentMessageDetail } from './widgets/messages/SalesDocumentMessageDetail'\nimport { SalesDocumentMessagePreview } from './widgets/messages/SalesDocumentMessagePreview'\n\nconst objectMessageTypes = ['default', 'messages.defaultWithObjects']\n\nexport const messageObjectTypes: MessageObjectTypeDefinition[] = [\n {\n module: 'sales',\n entityType: 'order',\n messageTypes: objectMessageTypes,\n entityId: 'sales:sales_order',\n optionLabelField: 'number',\n optionSubtitleField: 'status',\n labelKey: 'sales.documents.detail.order',\n icon: 'receipt-text',\n PreviewComponent: SalesDocumentMessagePreview,\n DetailComponent: SalesDocumentMessageDetail,\n actions: [],\n loadPreview: async (entityId, ctx) => {\n if (typeof window !== 'undefined') {\n return {\n title: 'Sales order',\n subtitle: entityId,\n }\n }\n const { loadSalesOrderPreview } = await import('./lib/messageObjectPreviews')\n return loadSalesOrderPreview(entityId, ctx)\n },\n },\n {\n module: 'sales',\n entityType: 'quote',\n messageTypes: objectMessageTypes,\n entityId: 'sales:sales_quote',\n optionLabelField: 'number',\n optionSubtitleField: 'status',\n labelKey: 'sales.documents.detail.quote',\n icon: 'file-text',\n PreviewComponent: SalesDocumentMessagePreview,\n DetailComponent: SalesDocumentMessageDetail,\n actions: [],\n loadPreview: async (entityId, ctx) => {\n if (typeof window !== 'undefined') {\n return {\n title: 'Sales quote',\n subtitle: entityId,\n }\n }\n const { loadSalesQuotePreview } = await import('./lib/messageObjectPreviews')\n return loadSalesQuotePreview(entityId, ctx)\n },\n },\n]\n\nexport default messageObjectTypes\n"],
|
|
5
|
-
"mappings": "AACA,SAAS,kCAAkC;AAC3C,SAAS,mCAAmC;AAE5C,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,
|
|
4
|
+
"sourcesContent": ["import type { MessageObjectTypeDefinition } from '@open-mercato/shared/modules/messages/types'\nimport { MessageObjectDetail, MessageObjectPreview } from '@open-mercato/ui/backend/messages'\nimport { SalesDocumentMessageDetail } from './widgets/messages/SalesDocumentMessageDetail'\nimport { SalesDocumentMessagePreview } from './widgets/messages/SalesDocumentMessagePreview'\n\nconst objectMessageTypes = ['default', 'messages.defaultWithObjects']\n\nexport const messageObjectTypes: MessageObjectTypeDefinition[] = [\n {\n module: 'sales',\n entityType: 'order',\n messageTypes: objectMessageTypes,\n entityId: 'sales:sales_order',\n optionLabelField: 'number',\n optionSubtitleField: 'status',\n labelKey: 'sales.documents.detail.order',\n icon: 'receipt-text',\n PreviewComponent: SalesDocumentMessagePreview,\n DetailComponent: SalesDocumentMessageDetail,\n actions: [\n {\n id: 'view',\n labelKey: 'common.view',\n variant: 'outline',\n href: '/backend/sales/orders/{entityId}',\n },\n ],\n loadPreview: async (entityId, ctx) => {\n if (typeof window !== 'undefined') {\n return {\n title: 'Sales order',\n subtitle: entityId,\n }\n }\n const { loadSalesOrderPreview } = await import('./lib/messageObjectPreviews')\n return loadSalesOrderPreview(entityId, ctx)\n },\n },\n {\n module: 'sales',\n entityType: 'quote',\n messageTypes: objectMessageTypes,\n entityId: 'sales:sales_quote',\n optionLabelField: 'number',\n optionSubtitleField: 'status',\n labelKey: 'sales.documents.detail.quote',\n icon: 'file-text',\n PreviewComponent: SalesDocumentMessagePreview,\n DetailComponent: SalesDocumentMessageDetail,\n actions: [\n {\n id: 'view',\n labelKey: 'common.view',\n variant: 'outline',\n href: '/backend/sales/quotes/{entityId}',\n },\n ],\n loadPreview: async (entityId, ctx) => {\n if (typeof window !== 'undefined') {\n return {\n title: 'Sales quote',\n subtitle: entityId,\n }\n }\n const { loadSalesQuotePreview } = await import('./lib/messageObjectPreviews')\n return loadSalesQuotePreview(entityId, ctx)\n },\n },\n {\n module: 'sales',\n entityType: 'channel',\n messageTypes: objectMessageTypes,\n entityId: 'sales:sales_channel',\n optionLabelField: 'name',\n optionSubtitleField: 'status',\n labelKey: 'sales.messageObjects.channel.title',\n icon: 'store',\n PreviewComponent: MessageObjectPreview,\n DetailComponent: MessageObjectDetail,\n actions: [\n {\n id: 'view',\n labelKey: 'common.view',\n variant: 'outline',\n href: '/backend/sales/channels/{entityId}/edit',\n },\n ],\n loadPreview: async (entityId, ctx) => {\n if (typeof window !== 'undefined') {\n return { title: 'Sales channel', subtitle: entityId }\n }\n const { loadSalesChannelPreview } = await import('./lib/messageObjectPreviews')\n return loadSalesChannelPreview(entityId, ctx)\n },\n },\n]\n\nexport default messageObjectTypes\n"],
|
|
5
|
+
"mappings": "AACA,SAAS,qBAAqB,4BAA4B;AAC1D,SAAS,kCAAkC;AAC3C,SAAS,mCAAmC;AAE5C,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;AAAA,UACL,OAAO;AAAA,UACP,UAAU;AAAA,QACZ;AAAA,MACF;AACA,YAAM,EAAE,sBAAsB,IAAI,MAAM,OAAO,6BAA6B;AAC5E,aAAO,sBAAsB,UAAU,GAAG;AAAA,IAC5C;AAAA,EACF;AAAA,EACA;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;AAAA,UACL,OAAO;AAAA,UACP,UAAU;AAAA,QACZ;AAAA,MACF;AACA,YAAM,EAAE,sBAAsB,IAAI,MAAM,OAAO,6BAA6B;AAC5E,aAAO,sBAAsB,UAAU,GAAG;AAAA,IAC5C;AAAA,EACF;AAAA,EACA;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,iBAAiB,UAAU,SAAS;AAAA,MACtD;AACA,YAAM,EAAE,wBAAwB,IAAI,MAAM,OAAO,6BAA6B;AAC9E,aAAO,wBAAwB,UAAU,GAAG;AAAA,IAC9C;AAAA,EACF;AACF;AAEA,IAAO,0BAAQ;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,46 +1,75 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
3
|
import * as React from "react";
|
|
4
|
+
import Link from "next/link";
|
|
4
5
|
import { useT } from "@open-mercato/shared/lib/i18n/context";
|
|
5
6
|
import { Button } from "@open-mercato/ui/primitives/button";
|
|
6
7
|
import { SalesDocumentMessagePreview } from "./SalesDocumentMessagePreview.js";
|
|
8
|
+
function resolveActionHref(template, entityId) {
|
|
9
|
+
return template.replace("{entityId}", encodeURIComponent(entityId));
|
|
10
|
+
}
|
|
7
11
|
function SalesDocumentMessageDetail(props) {
|
|
8
12
|
const t = useT();
|
|
9
13
|
const [executingActionId, setExecutingActionId] = React.useState(null);
|
|
14
|
+
const viewAction = props.actions.find((a) => a.id === "view");
|
|
15
|
+
const otherActions = props.actions.filter((a) => a.id !== "view");
|
|
16
|
+
const preview = /* @__PURE__ */ jsx(
|
|
17
|
+
SalesDocumentMessagePreview,
|
|
18
|
+
{
|
|
19
|
+
entityId: props.entityId,
|
|
20
|
+
entityModule: props.entityModule,
|
|
21
|
+
entityType: props.entityType,
|
|
22
|
+
snapshot: props.snapshot,
|
|
23
|
+
previewData: props.previewData,
|
|
24
|
+
actionRequired: props.actionRequired,
|
|
25
|
+
actionType: props.actionType,
|
|
26
|
+
actionLabel: props.actionLabel
|
|
27
|
+
}
|
|
28
|
+
);
|
|
10
29
|
return /* @__PURE__ */ jsxs("div", { className: "space-y-3 rounded border p-3", children: [
|
|
11
|
-
/* @__PURE__ */ jsx(
|
|
12
|
-
|
|
30
|
+
viewAction?.href ? /* @__PURE__ */ jsx(
|
|
31
|
+
Link,
|
|
13
32
|
{
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
snapshot: props.snapshot,
|
|
18
|
-
previewData: props.previewData,
|
|
19
|
-
actionRequired: props.actionRequired,
|
|
20
|
-
actionType: props.actionType,
|
|
21
|
-
actionLabel: props.actionLabel
|
|
33
|
+
href: resolveActionHref(viewAction.href, props.entityId),
|
|
34
|
+
className: "block rounded-md transition-opacity hover:opacity-75",
|
|
35
|
+
children: preview
|
|
22
36
|
}
|
|
23
|
-
),
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
37
|
+
) : preview,
|
|
38
|
+
otherActions.length > 0 ? /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-2", children: otherActions.map((action) => {
|
|
39
|
+
if (action.href) {
|
|
40
|
+
return /* @__PURE__ */ jsx(
|
|
41
|
+
Button,
|
|
42
|
+
{
|
|
43
|
+
type: "button",
|
|
44
|
+
size: "sm",
|
|
45
|
+
variant: action.variant ?? "default",
|
|
46
|
+
asChild: true,
|
|
47
|
+
children: /* @__PURE__ */ jsx(Link, { href: resolveActionHref(action.href, props.entityId), children: t(action.labelKey ?? action.id, action.id) })
|
|
48
|
+
},
|
|
49
|
+
action.id
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
return /* @__PURE__ */ jsx(
|
|
53
|
+
Button,
|
|
54
|
+
{
|
|
55
|
+
type: "button",
|
|
56
|
+
size: "sm",
|
|
57
|
+
variant: action.variant ?? "default",
|
|
58
|
+
disabled: executingActionId !== null,
|
|
59
|
+
onClick: async () => {
|
|
60
|
+
if (executingActionId) return;
|
|
61
|
+
setExecutingActionId(action.id);
|
|
62
|
+
try {
|
|
63
|
+
await props.onAction(action.id, { id: props.entityId });
|
|
64
|
+
} finally {
|
|
65
|
+
setExecutingActionId(null);
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
children: executingActionId === action.id ? t("messages.actions.executing", "Executing...") : t(action.labelKey ?? action.id, action.id)
|
|
39
69
|
},
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
)) }) : null
|
|
70
|
+
action.id
|
|
71
|
+
);
|
|
72
|
+
}) }) : null
|
|
44
73
|
] });
|
|
45
74
|
}
|
|
46
75
|
var SalesDocumentMessageDetail_default = SalesDocumentMessageDetail;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/modules/sales/widgets/messages/SalesDocumentMessageDetail.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport type { ObjectDetailProps } from '@open-mercato/shared/modules/messages/types'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { SalesDocumentMessagePreview } from './SalesDocumentMessagePreview'\n\nexport function SalesDocumentMessageDetail(props: ObjectDetailProps) {\n const t = useT()\n const [executingActionId, setExecutingActionId] = React.useState<string | null>(null)\n\n
|
|
5
|
-
"mappings": ";
|
|
4
|
+
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport Link from 'next/link'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport type { ObjectDetailProps } from '@open-mercato/shared/modules/messages/types'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { SalesDocumentMessagePreview } from './SalesDocumentMessagePreview'\n\nfunction resolveActionHref(template: string, entityId: string): string {\n return template.replace('{entityId}', encodeURIComponent(entityId))\n}\n\nexport function SalesDocumentMessageDetail(props: ObjectDetailProps) {\n const t = useT()\n const [executingActionId, setExecutingActionId] = React.useState<string | null>(null)\n\n const viewAction = props.actions.find((a) => a.id === 'view')\n const otherActions = props.actions.filter((a) => a.id !== 'view')\n\n const preview = (\n <SalesDocumentMessagePreview\n entityId={props.entityId}\n entityModule={props.entityModule}\n entityType={props.entityType}\n snapshot={props.snapshot}\n previewData={props.previewData}\n actionRequired={props.actionRequired}\n actionType={props.actionType}\n actionLabel={props.actionLabel}\n />\n )\n\n return (\n <div className=\"space-y-3 rounded border p-3\">\n {viewAction?.href ? (\n <Link\n href={resolveActionHref(viewAction.href, props.entityId)}\n className=\"block rounded-md transition-opacity hover:opacity-75\"\n >\n {preview}\n </Link>\n ) : (\n preview\n )}\n\n {otherActions.length > 0 ? (\n <div className=\"flex flex-wrap gap-2\">\n {otherActions.map((action) => {\n if (action.href) {\n return (\n <Button\n key={action.id}\n type=\"button\"\n size=\"sm\"\n variant={action.variant ?? 'default'}\n asChild\n >\n <Link href={resolveActionHref(action.href, props.entityId)}>\n {t(action.labelKey ?? action.id, action.id)}\n </Link>\n </Button>\n )\n }\n return (\n <Button\n key={action.id}\n type=\"button\"\n size=\"sm\"\n variant={action.variant ?? 'default'}\n disabled={executingActionId !== null}\n onClick={async () => {\n if (executingActionId) return\n setExecutingActionId(action.id)\n try {\n await props.onAction(action.id, { id: props.entityId })\n } finally {\n setExecutingActionId(null)\n }\n }}\n >\n {executingActionId === action.id\n ? t('messages.actions.executing', 'Executing...')\n : t(action.labelKey ?? action.id, action.id)}\n </Button>\n )\n })}\n </div>\n ) : null}\n </div>\n )\n}\n\nexport default SalesDocumentMessageDetail\n\n"],
|
|
5
|
+
"mappings": ";AAqBI,cAaA,YAbA;AAnBJ,YAAY,WAAW;AACvB,OAAO,UAAU;AACjB,SAAS,YAAY;AAErB,SAAS,cAAc;AACvB,SAAS,mCAAmC;AAE5C,SAAS,kBAAkB,UAAkB,UAA0B;AACrE,SAAO,SAAS,QAAQ,cAAc,mBAAmB,QAAQ,CAAC;AACpE;AAEO,SAAS,2BAA2B,OAA0B;AACnE,QAAM,IAAI,KAAK;AACf,QAAM,CAAC,mBAAmB,oBAAoB,IAAI,MAAM,SAAwB,IAAI;AAEpF,QAAM,aAAa,MAAM,QAAQ,KAAK,CAAC,MAAM,EAAE,OAAO,MAAM;AAC5D,QAAM,eAAe,MAAM,QAAQ,OAAO,CAAC,MAAM,EAAE,OAAO,MAAM;AAEhE,QAAM,UACJ;AAAA,IAAC;AAAA;AAAA,MACC,UAAU,MAAM;AAAA,MAChB,cAAc,MAAM;AAAA,MACpB,YAAY,MAAM;AAAA,MAClB,UAAU,MAAM;AAAA,MAChB,aAAa,MAAM;AAAA,MACnB,gBAAgB,MAAM;AAAA,MACtB,YAAY,MAAM;AAAA,MAClB,aAAa,MAAM;AAAA;AAAA,EACrB;AAGF,SACE,qBAAC,SAAI,WAAU,gCACZ;AAAA,gBAAY,OACX;AAAA,MAAC;AAAA;AAAA,QACC,MAAM,kBAAkB,WAAW,MAAM,MAAM,QAAQ;AAAA,QACvD,WAAU;AAAA,QAET;AAAA;AAAA,IACH,IAEA;AAAA,IAGD,aAAa,SAAS,IACrB,oBAAC,SAAI,WAAU,wBACZ,uBAAa,IAAI,CAAC,WAAW;AAC5B,UAAI,OAAO,MAAM;AACf,eACE;AAAA,UAAC;AAAA;AAAA,YAEC,MAAK;AAAA,YACL,MAAK;AAAA,YACL,SAAS,OAAO,WAAW;AAAA,YAC3B,SAAO;AAAA,YAEP,8BAAC,QAAK,MAAM,kBAAkB,OAAO,MAAM,MAAM,QAAQ,GACtD,YAAE,OAAO,YAAY,OAAO,IAAI,OAAO,EAAE,GAC5C;AAAA;AAAA,UARK,OAAO;AAAA,QASd;AAAA,MAEJ;AACA,aACE;AAAA,QAAC;AAAA;AAAA,UAEC,MAAK;AAAA,UACL,MAAK;AAAA,UACL,SAAS,OAAO,WAAW;AAAA,UAC3B,UAAU,sBAAsB;AAAA,UAChC,SAAS,YAAY;AACnB,gBAAI,kBAAmB;AACvB,iCAAqB,OAAO,EAAE;AAC9B,gBAAI;AACF,oBAAM,MAAM,SAAS,OAAO,IAAI,EAAE,IAAI,MAAM,SAAS,CAAC;AAAA,YACxD,UAAE;AACA,mCAAqB,IAAI;AAAA,YAC3B;AAAA,UACF;AAAA,UAEC,gCAAsB,OAAO,KAC1B,EAAE,8BAA8B,cAAc,IAC9C,EAAE,OAAO,YAAY,OAAO,IAAI,OAAO,EAAE;AAAA;AAAA,QAjBxC,OAAO;AAAA,MAkBd;AAAA,IAEJ,CAAC,GACH,IACE;AAAA,KACN;AAEJ;AAEA,IAAO,qCAAQ;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -15,7 +15,7 @@ function SalesDocumentMessagePreview({
|
|
|
15
15
|
const Icon = isQuote ? FileText : ReceiptText;
|
|
16
16
|
const fallbackTitle = isQuote ? t("sales.documents.detail.quote", "Sales quote") : t("sales.documents.detail.order", "Sales order");
|
|
17
17
|
const title = previewData?.title || fallbackTitle;
|
|
18
|
-
const subtitle = previewData?.subtitle ||
|
|
18
|
+
const subtitle = previewData?.subtitle || "";
|
|
19
19
|
return /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-3 rounded-md border bg-muted/20 p-3", children: [
|
|
20
20
|
/* @__PURE__ */ jsx(Icon, { className: "mt-0.5 h-4 w-4 text-muted-foreground" }),
|
|
21
21
|
/* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1 space-y-1", children: [
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../src/modules/sales/widgets/messages/SalesDocumentMessagePreview.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\n\nimport { FileText, ReceiptText } from 'lucide-react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport type { ObjectPreviewProps } from '@open-mercato/shared/modules/messages/types'\nimport { Badge } from '@open-mercato/ui/primitives/badge'\n\nexport function SalesDocumentMessagePreview({\n entityType,\n entityId,\n previewData,\n actionRequired,\n actionLabel,\n}: ObjectPreviewProps) {\n const t = useT()\n const isQuote = entityType === 'quote'\n const Icon = isQuote ? FileText : ReceiptText\n const fallbackTitle = isQuote\n ? t('sales.documents.detail.quote', 'Sales quote')\n : t('sales.documents.detail.order', 'Sales order')\n const title = previewData?.title || fallbackTitle\n const subtitle = previewData?.subtitle ||
|
|
4
|
+
"sourcesContent": ["\"use client\"\n\nimport { FileText, ReceiptText } from 'lucide-react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport type { ObjectPreviewProps } from '@open-mercato/shared/modules/messages/types'\nimport { Badge } from '@open-mercato/ui/primitives/badge'\n\nexport function SalesDocumentMessagePreview({\n entityType,\n entityId,\n previewData,\n actionRequired,\n actionLabel,\n}: ObjectPreviewProps) {\n const t = useT()\n const isQuote = entityType === 'quote'\n const Icon = isQuote ? FileText : ReceiptText\n const fallbackTitle = isQuote\n ? t('sales.documents.detail.quote', 'Sales quote')\n : t('sales.documents.detail.order', 'Sales order')\n const title = previewData?.title || fallbackTitle\n const subtitle = previewData?.subtitle || \"\"\n\n return (\n <div className=\"flex items-start gap-3 rounded-md border bg-muted/20 p-3\">\n <Icon className=\"mt-0.5 h-4 w-4 text-muted-foreground\" />\n <div className=\"min-w-0 flex-1 space-y-1\">\n <div className=\"flex items-center gap-2\">\n <p className=\"truncate text-sm font-medium\">{title}</p>\n {actionRequired ? (\n <Badge variant=\"secondary\" className=\"text-xs\">\n {actionLabel || t('messages.composer.objectActionRequired', 'Action required')}\n </Badge>\n ) : null}\n </div>\n <p className=\"truncate text-xs text-muted-foreground\">{subtitle}</p>\n {previewData?.status ? (\n <Badge variant=\"outline\" className=\"text-xs\">{previewData.status}</Badge>\n ) : null}\n </div>\n </div>\n )\n}\n\nexport default SalesDocumentMessagePreview\n\n"],
|
|
5
5
|
"mappings": ";AAyBM,cAEE,YAFF;AAvBN,SAAS,UAAU,mBAAmB;AACtC,SAAS,YAAY;AAErB,SAAS,aAAa;AAEf,SAAS,4BAA4B;AAAA,EAC1C;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAuB;AACrB,QAAM,IAAI,KAAK;AACf,QAAM,UAAU,eAAe;AAC/B,QAAM,OAAO,UAAU,WAAW;AAClC,QAAM,gBAAgB,UAClB,EAAE,gCAAgC,aAAa,IAC/C,EAAE,gCAAgC,aAAa;AACnD,QAAM,QAAQ,aAAa,SAAS;AACpC,QAAM,WAAW,aAAa,YAAY;AAE1C,SACE,qBAAC,SAAI,WAAU,4DACb;AAAA,wBAAC,QAAK,WAAU,wCAAuC;AAAA,IACvD,qBAAC,SAAI,WAAU,4BACb;AAAA,2BAAC,SAAI,WAAU,2BACb;AAAA,4BAAC,OAAE,WAAU,gCAAgC,iBAAM;AAAA,QAClD,iBACC,oBAAC,SAAM,SAAQ,aAAY,WAAU,WAClC,yBAAe,EAAE,0CAA0C,iBAAiB,GAC/E,IACE;AAAA,SACN;AAAA,MACA,oBAAC,OAAE,WAAU,0CAA0C,oBAAS;AAAA,MAC/D,aAAa,SACZ,oBAAC,SAAM,SAAQ,WAAU,WAAU,WAAW,sBAAY,QAAO,IAC/D;AAAA,OACN;AAAA,KACF;AAEJ;AAEA,IAAO,sCAAQ;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
import { useRouter } from "next/navigation";
|
|
5
|
-
import { Send } from "lucide-react";
|
|
6
5
|
import { Page, PageBody } from "@open-mercato/ui/backend/Page";
|
|
7
6
|
import { Badge } from "@open-mercato/ui/primitives/badge";
|
|
8
7
|
import { Button } from "@open-mercato/ui/primitives/button";
|
|
@@ -80,19 +79,6 @@ function StaffLeaveRequestDetailPage({ params }) {
|
|
|
80
79
|
unavailabilityReasonValue: record?.unavailabilityReasonValue ?? record?.unavailability_reason_value ?? null,
|
|
81
80
|
note: record?.note ?? null
|
|
82
81
|
}), [record, memberLabel]);
|
|
83
|
-
const messageContextPreview = React.useMemo(() => /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
|
|
84
|
-
/* @__PURE__ */ jsx("p", { className: "font-medium", children: t("staff.leaveRequests.messages.contextTitle", "Linked leave request") }),
|
|
85
|
-
memberLabel ? /* @__PURE__ */ jsxs("p", { className: "text-xs text-muted-foreground", children: [
|
|
86
|
-
t("staff.leaveRequests.detail.member", "Team member"),
|
|
87
|
-
": ",
|
|
88
|
-
memberLabel
|
|
89
|
-
] }) : null,
|
|
90
|
-
/* @__PURE__ */ jsxs("p", { className: "text-xs text-muted-foreground", children: [
|
|
91
|
-
t("staff.leaveRequests.detail.dates", "Dates"),
|
|
92
|
-
": ",
|
|
93
|
-
dateSummary
|
|
94
|
-
] })
|
|
95
|
-
] }), [dateSummary, memberLabel, t]);
|
|
96
82
|
const handleSubmit = React.useCallback(async (values) => {
|
|
97
83
|
if (!record?.id) return;
|
|
98
84
|
const payload = buildLeaveRequestPayload(values, { id: record.id });
|
|
@@ -181,8 +167,14 @@ function StaffLeaveRequestDetailPage({ params }) {
|
|
|
181
167
|
entityType: "leave_request",
|
|
182
168
|
entityId: record.id,
|
|
183
169
|
sourceEntityType: "staff:leave_request",
|
|
184
|
-
sourceEntityId: record.id
|
|
170
|
+
sourceEntityId: record.id,
|
|
171
|
+
previewData: {
|
|
172
|
+
title: memberLabel || t("staff.leaveRequests.messages.contextTitle", "Linked leave request"),
|
|
173
|
+
subtitle: dateSummary || void 0,
|
|
174
|
+
status: record?.status ?? void 0
|
|
175
|
+
}
|
|
185
176
|
},
|
|
177
|
+
viewHref: `/backend/staff/leave-requests/${record.id}`,
|
|
186
178
|
lockedType: "staff.leave_request_approval",
|
|
187
179
|
requiredActionConfig: {
|
|
188
180
|
mode: "required",
|
|
@@ -195,21 +187,7 @@ function StaffLeaveRequestDetailPage({ params }) {
|
|
|
195
187
|
type: "staff.leave_request_approval",
|
|
196
188
|
subject: t("staff.leaveRequests.messages.compose.subject", "Leave request approval needed"),
|
|
197
189
|
body: t("staff.leaveRequests.messages.compose.body", "Please review this leave request and take action.")
|
|
198
|
-
}
|
|
199
|
-
contextPreview: messageContextPreview,
|
|
200
|
-
renderTrigger: ({ openComposer, disabled }) => /* @__PURE__ */ jsx(
|
|
201
|
-
Button,
|
|
202
|
-
{
|
|
203
|
-
type: "button",
|
|
204
|
-
size: "icon",
|
|
205
|
-
variant: "outline",
|
|
206
|
-
onClick: openComposer,
|
|
207
|
-
disabled,
|
|
208
|
-
"aria-label": t("staff.leaveRequests.messages.compose.action", "Send for review"),
|
|
209
|
-
title: t("staff.leaveRequests.messages.compose.action", "Send for review"),
|
|
210
|
-
children: /* @__PURE__ */ jsx(Send, { className: "h-4 w-4" })
|
|
211
|
-
}
|
|
212
|
-
)
|
|
190
|
+
}
|
|
213
191
|
}
|
|
214
192
|
) : null
|
|
215
193
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../../src/modules/staff/backend/staff/leave-requests/%5Bid%5D/page.tsx"],
|
|
4
|
-
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { useRouter } from 'next/navigation'\nimport { Send } from 'lucide-react'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { Badge } from '@open-mercato/ui/primitives/badge'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Textarea } from '@open-mercato/ui/primitives/textarea'\nimport { LoadingMessage, ErrorMessage } from '@open-mercato/ui/backend/detail'\nimport { SendObjectMessageDialog } from '@open-mercato/ui/backend/messages'\nimport { apiCallOrThrow, readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'\nimport { updateCrud } from '@open-mercato/ui/backend/utils/crud'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { LeaveRequestForm, buildLeaveRequestPayload, type LeaveRequestFormValues } from '@open-mercato/core/modules/staff/components/LeaveRequestForm'\n\ntype LeaveRequestRecord = {\n id: string\n member?: { id?: string; displayName?: string }\n memberId?: string | null\n member_id?: string | null\n startDate?: string | null\n start_date?: string | null\n endDate?: string | null\n end_date?: string | null\n timezone?: string | null\n status?: 'pending' | 'approved' | 'rejected'\n unavailabilityReasonEntryId?: string | null\n unavailability_reason_entry_id?: string | null\n unavailabilityReasonValue?: string | null\n unavailability_reason_value?: string | null\n note?: string | null\n decisionComment?: string | null\n decision_comment?: string | null\n decidedAt?: string | null\n decided_at?: string | null\n} & Record<string, unknown>\n\ntype LeaveRequestsResponse = {\n items?: LeaveRequestRecord[]\n}\n\nexport default function StaffLeaveRequestDetailPage({ params }: { params?: { id?: string } }) {\n const id = params?.id\n const t = useT()\n const router = useRouter()\n const [isLoading, setIsLoading] = React.useState(true)\n const [error, setError] = React.useState<string | null>(null)\n const [record, setRecord] = React.useState<LeaveRequestRecord | null>(null)\n const [decisionComment, setDecisionComment] = React.useState('')\n\n React.useEffect(() => {\n if (!id) {\n setError(t('staff.leaveRequests.errors.notFound', 'Leave request not found.'))\n setIsLoading(false)\n return\n }\n const requestId = id\n let cancelled = false\n async function load() {\n setIsLoading(true)\n setError(null)\n try {\n const params = new URLSearchParams({ page: '1', pageSize: '1', ids: requestId })\n const payload = await readApiResultOrThrow<LeaveRequestsResponse>(\n `/api/staff/leave-requests?${params.toString()}`,\n undefined,\n { errorMessage: t('staff.leaveRequests.errors.load', 'Failed to load leave request.') },\n )\n const entry = Array.isArray(payload.items) ? payload.items[0] : null\n if (!entry) throw new Error(t('staff.leaveRequests.errors.notFound', 'Leave request not found.'))\n if (!cancelled) {\n setRecord(entry)\n setDecisionComment(\n typeof entry.decisionComment === 'string'\n ? entry.decisionComment\n : typeof entry.decision_comment === 'string'\n ? entry.decision_comment\n : ''\n )\n }\n } catch (err) {\n if (!cancelled) {\n const message = err instanceof Error ? err.message : t('staff.leaveRequests.errors.load', 'Failed to load leave request.')\n setError(message)\n setRecord(null)\n }\n } finally {\n if (!cancelled) setIsLoading(false)\n }\n }\n void load()\n return () => { cancelled = true }\n }, [id, t])\n\n const status = record?.status ?? 'pending'\n const memberLabel = record?.member?.displayName ?? null\n const dateSummary = formatDateRange(\n record?.startDate ?? record?.start_date ?? null,\n record?.endDate ?? record?.end_date ?? null,\n )\n const initialValues = React.useMemo<LeaveRequestFormValues>(() => ({\n id: record?.id,\n memberId: record?.memberId ?? record?.member_id ?? null,\n memberLabel,\n startDate: record?.startDate ?? record?.start_date ?? null,\n endDate: record?.endDate ?? record?.end_date ?? null,\n timezone: record?.timezone ?? null,\n unavailabilityReasonEntryId: record?.unavailabilityReasonEntryId ?? record?.unavailability_reason_entry_id ?? null,\n unavailabilityReasonValue: record?.unavailabilityReasonValue ?? record?.unavailability_reason_value ?? null,\n note: record?.note ?? null,\n }), [record, memberLabel])\n\n const messageContextPreview = React.useMemo(() => (\n <div className=\"space-y-1\">\n <p className=\"font-medium\">{t('staff.leaveRequests.messages.contextTitle', 'Linked leave request')}</p>\n {memberLabel ? (\n <p className=\"text-xs text-muted-foreground\">\n {t('staff.leaveRequests.detail.member', 'Team member')}: {memberLabel}\n </p>\n ) : null}\n <p className=\"text-xs text-muted-foreground\">\n {t('staff.leaveRequests.detail.dates', 'Dates')}: {dateSummary}\n </p>\n </div>\n ), [dateSummary, memberLabel, t])\n\n const handleSubmit = React.useCallback(async (values: LeaveRequestFormValues) => {\n if (!record?.id) return\n const payload = buildLeaveRequestPayload(values, { id: record.id })\n await updateCrud('staff/leave-requests', payload, {\n errorMessage: t('staff.leaveRequests.form.errors.update', 'Failed to update leave request.'),\n })\n flash(t('staff.leaveRequests.form.flash.updated', 'Leave request updated.'), 'success')\n router.push('/backend/staff/leave-requests')\n }, [record?.id, router, t])\n\n const handleDecision = React.useCallback(async (action: 'accept' | 'reject') => {\n if (!record?.id) return\n const endpoint = action === 'accept' ? '/api/staff/leave-requests/accept' : '/api/staff/leave-requests/reject'\n await apiCallOrThrow(endpoint, {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ id: record.id, decisionComment: decisionComment || null }),\n })\n flash(\n action === 'accept'\n ? t('staff.leaveRequests.messages.accepted', 'Leave request approved.')\n : t('staff.leaveRequests.messages.rejected', 'Leave request rejected.'),\n 'success',\n )\n router.refresh()\n }, [decisionComment, record?.id, router, t])\n\n if (isLoading) {\n return (\n <Page>\n <PageBody>\n <LoadingMessage label={t('staff.leaveRequests.form.loading', 'Loading leave request...')} />\n </PageBody>\n </Page>\n )\n }\n\n if (error || !record) {\n return (\n <Page>\n <PageBody>\n <ErrorMessage label={error ?? t('staff.leaveRequests.errors.load', 'Failed to load leave request.')} />\n </PageBody>\n </Page>\n )\n }\n\n return (\n <Page>\n <PageBody>\n <div className=\"mb-6 space-y-2 rounded-lg border bg-card p-4\">\n <div className=\"flex flex-wrap items-center gap-3\">\n <Badge variant={resolveStatusVariant(status)}>\n {t(`staff.leaveRequests.status.${status}`, status)}\n </Badge>\n {record.decided_at || record.decidedAt ? (\n <span className=\"text-xs text-muted-foreground\">\n {t('staff.leaveRequests.decision.at', 'Decision at')} {formatDateLabel(record.decidedAt ?? record.decided_at ?? null)}\n </span>\n ) : null}\n </div>\n {memberLabel ? (\n <p className=\"text-sm text-muted-foreground\">\n {t('staff.leaveRequests.detail.member', 'Team member')}: {memberLabel}\n </p>\n ) : null}\n <p className=\"text-sm text-muted-foreground\">\n {t('staff.leaveRequests.detail.dates', 'Dates')}: {dateSummary}\n </p>\n </div>\n\n {status === 'pending' ? (\n <div className=\"mb-6 rounded-lg border bg-card p-4\">\n <div className=\"mb-3 text-sm font-medium\">{t('staff.leaveRequests.decision.title', 'Decision')}</div>\n <Textarea\n value={decisionComment}\n onChange={(event) => setDecisionComment(event.target.value)}\n placeholder={t('staff.leaveRequests.decision.placeholder', 'Add a comment (optional)')}\n className=\"mb-3\"\n />\n <div className=\"flex flex-wrap gap-2\">\n <Button onClick={() => handleDecision('accept')}>\n {t('staff.leaveRequests.actions.accept', 'Approve')}\n </Button>\n <Button variant=\"destructive\" onClick={() => handleDecision('reject')}>\n {t('staff.leaveRequests.actions.reject', 'Reject')}\n </Button>\n </div>\n </div>\n ) : record.decisionComment || record.decision_comment ? (\n <div className=\"mb-6 rounded-lg border bg-card p-4 text-sm text-muted-foreground\">\n <div className=\"mb-1 font-medium text-foreground\">{t('staff.leaveRequests.decision.comment', 'Decision comment')}</div>\n <p>{record.decisionComment ?? record.decision_comment}</p>\n </div>\n ) : null}\n\n <LeaveRequestForm\n title={t('staff.leaveRequests.form.editTitle', 'Leave request')}\n submitLabel={t('staff.leaveRequests.form.actions.save', 'Save')}\n backHref=\"/backend/staff/leave-requests\"\n cancelHref=\"/backend/staff/leave-requests\"\n initialValues={initialValues}\n onSubmit={handleSubmit}\n allowMemberSelect\n memberLabel={memberLabel}\n extraActions={record.id ? (\n <SendObjectMessageDialog\n object={{\n entityModule: 'staff',\n entityType: 'leave_request',\n entityId: record.id,\n sourceEntityType: 'staff:leave_request',\n sourceEntityId: record.id,\n }}\n lockedType=\"staff.leave_request_approval\"\n requiredActionConfig={{\n mode: 'required',\n options: [\n { id: 'approve', label: t('staff.notifications.leaveRequest.actions.approve', 'Approve') },\n { id: 'reject', label: t('staff.notifications.leaveRequest.actions.reject', 'Reject') },\n ],\n }}\n defaultValues={{\n type: 'staff.leave_request_approval',\n subject: t('staff.leaveRequests.messages.compose.subject', 'Leave request approval needed'),\n body: t('staff.leaveRequests.messages.compose.body', 'Please review this leave request and take action.'),\n }}\n contextPreview={messageContextPreview}\n renderTrigger={({ openComposer, disabled }) => (\n <Button\n type=\"button\"\n size=\"icon\"\n variant=\"outline\"\n onClick={openComposer}\n disabled={disabled}\n aria-label={t('staff.leaveRequests.messages.compose.action', 'Send for review')}\n title={t('staff.leaveRequests.messages.compose.action', 'Send for review')}\n >\n <Send className=\"h-4 w-4\" />\n </Button>\n )}\n />\n ) : null}\n />\n </PageBody>\n </Page>\n )\n}\n\nfunction resolveStatusVariant(status: 'pending' | 'approved' | 'rejected') {\n if (status === 'approved') return 'default'\n if (status === 'rejected') return 'destructive'\n return 'secondary'\n}\n\nfunction formatDateLabel(value?: string | null): string {\n if (!value) return ''\n const date = new Date(value)\n if (Number.isNaN(date.getTime())) return value\n return date.toLocaleDateString()\n}\n\nfunction formatDateRange(start?: string | null, end?: string | null): string {\n const startLabel = formatDateLabel(start)\n const endLabel = formatDateLabel(end)\n if (startLabel && endLabel) return `${startLabel} -> ${endLabel}`\n return startLabel || endLabel || '-'\n}\n"],
|
|
5
|
-
"mappings": ";
|
|
4
|
+
"sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { useRouter } from 'next/navigation'\nimport { Send } from 'lucide-react'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { Badge } from '@open-mercato/ui/primitives/badge'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Textarea } from '@open-mercato/ui/primitives/textarea'\nimport { LoadingMessage, ErrorMessage } from '@open-mercato/ui/backend/detail'\nimport { SendObjectMessageDialog } from '@open-mercato/ui/backend/messages'\nimport { apiCallOrThrow, readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'\nimport { updateCrud } from '@open-mercato/ui/backend/utils/crud'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { LeaveRequestForm, buildLeaveRequestPayload, type LeaveRequestFormValues } from '@open-mercato/core/modules/staff/components/LeaveRequestForm'\n\ntype LeaveRequestRecord = {\n id: string\n member?: { id?: string; displayName?: string }\n memberId?: string | null\n member_id?: string | null\n startDate?: string | null\n start_date?: string | null\n endDate?: string | null\n end_date?: string | null\n timezone?: string | null\n status?: 'pending' | 'approved' | 'rejected'\n unavailabilityReasonEntryId?: string | null\n unavailability_reason_entry_id?: string | null\n unavailabilityReasonValue?: string | null\n unavailability_reason_value?: string | null\n note?: string | null\n decisionComment?: string | null\n decision_comment?: string | null\n decidedAt?: string | null\n decided_at?: string | null\n} & Record<string, unknown>\n\ntype LeaveRequestsResponse = {\n items?: LeaveRequestRecord[]\n}\n\nexport default function StaffLeaveRequestDetailPage({ params }: { params?: { id?: string } }) {\n const id = params?.id\n const t = useT()\n const router = useRouter()\n const [isLoading, setIsLoading] = React.useState(true)\n const [error, setError] = React.useState<string | null>(null)\n const [record, setRecord] = React.useState<LeaveRequestRecord | null>(null)\n const [decisionComment, setDecisionComment] = React.useState('')\n\n React.useEffect(() => {\n if (!id) {\n setError(t('staff.leaveRequests.errors.notFound', 'Leave request not found.'))\n setIsLoading(false)\n return\n }\n const requestId = id\n let cancelled = false\n async function load() {\n setIsLoading(true)\n setError(null)\n try {\n const params = new URLSearchParams({ page: '1', pageSize: '1', ids: requestId })\n const payload = await readApiResultOrThrow<LeaveRequestsResponse>(\n `/api/staff/leave-requests?${params.toString()}`,\n undefined,\n { errorMessage: t('staff.leaveRequests.errors.load', 'Failed to load leave request.') },\n )\n const entry = Array.isArray(payload.items) ? payload.items[0] : null\n if (!entry) throw new Error(t('staff.leaveRequests.errors.notFound', 'Leave request not found.'))\n if (!cancelled) {\n setRecord(entry)\n setDecisionComment(\n typeof entry.decisionComment === 'string'\n ? entry.decisionComment\n : typeof entry.decision_comment === 'string'\n ? entry.decision_comment\n : ''\n )\n }\n } catch (err) {\n if (!cancelled) {\n const message = err instanceof Error ? err.message : t('staff.leaveRequests.errors.load', 'Failed to load leave request.')\n setError(message)\n setRecord(null)\n }\n } finally {\n if (!cancelled) setIsLoading(false)\n }\n }\n void load()\n return () => { cancelled = true }\n }, [id, t])\n\n const status = record?.status ?? 'pending'\n const memberLabel = record?.member?.displayName ?? null\n const dateSummary = formatDateRange(\n record?.startDate ?? record?.start_date ?? null,\n record?.endDate ?? record?.end_date ?? null,\n )\n const initialValues = React.useMemo<LeaveRequestFormValues>(() => ({\n id: record?.id,\n memberId: record?.memberId ?? record?.member_id ?? null,\n memberLabel,\n startDate: record?.startDate ?? record?.start_date ?? null,\n endDate: record?.endDate ?? record?.end_date ?? null,\n timezone: record?.timezone ?? null,\n unavailabilityReasonEntryId: record?.unavailabilityReasonEntryId ?? record?.unavailability_reason_entry_id ?? null,\n unavailabilityReasonValue: record?.unavailabilityReasonValue ?? record?.unavailability_reason_value ?? null,\n note: record?.note ?? null,\n }), [record, memberLabel])\n\nconst handleSubmit = React.useCallback(async (values: LeaveRequestFormValues) => {\n if (!record?.id) return\n const payload = buildLeaveRequestPayload(values, { id: record.id })\n await updateCrud('staff/leave-requests', payload, {\n errorMessage: t('staff.leaveRequests.form.errors.update', 'Failed to update leave request.'),\n })\n flash(t('staff.leaveRequests.form.flash.updated', 'Leave request updated.'), 'success')\n router.push('/backend/staff/leave-requests')\n }, [record?.id, router, t])\n\n const handleDecision = React.useCallback(async (action: 'accept' | 'reject') => {\n if (!record?.id) return\n const endpoint = action === 'accept' ? '/api/staff/leave-requests/accept' : '/api/staff/leave-requests/reject'\n await apiCallOrThrow(endpoint, {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ id: record.id, decisionComment: decisionComment || null }),\n })\n flash(\n action === 'accept'\n ? t('staff.leaveRequests.messages.accepted', 'Leave request approved.')\n : t('staff.leaveRequests.messages.rejected', 'Leave request rejected.'),\n 'success',\n )\n router.refresh()\n }, [decisionComment, record?.id, router, t])\n\n if (isLoading) {\n return (\n <Page>\n <PageBody>\n <LoadingMessage label={t('staff.leaveRequests.form.loading', 'Loading leave request...')} />\n </PageBody>\n </Page>\n )\n }\n\n if (error || !record) {\n return (\n <Page>\n <PageBody>\n <ErrorMessage label={error ?? t('staff.leaveRequests.errors.load', 'Failed to load leave request.')} />\n </PageBody>\n </Page>\n )\n }\n\n return (\n <Page>\n <PageBody>\n <div className=\"mb-6 space-y-2 rounded-lg border bg-card p-4\">\n <div className=\"flex flex-wrap items-center gap-3\">\n <Badge variant={resolveStatusVariant(status)}>\n {t(`staff.leaveRequests.status.${status}`, status)}\n </Badge>\n {record.decided_at || record.decidedAt ? (\n <span className=\"text-xs text-muted-foreground\">\n {t('staff.leaveRequests.decision.at', 'Decision at')} {formatDateLabel(record.decidedAt ?? record.decided_at ?? null)}\n </span>\n ) : null}\n </div>\n {memberLabel ? (\n <p className=\"text-sm text-muted-foreground\">\n {t('staff.leaveRequests.detail.member', 'Team member')}: {memberLabel}\n </p>\n ) : null}\n <p className=\"text-sm text-muted-foreground\">\n {t('staff.leaveRequests.detail.dates', 'Dates')}: {dateSummary}\n </p>\n </div>\n\n {status === 'pending' ? (\n <div className=\"mb-6 rounded-lg border bg-card p-4\">\n <div className=\"mb-3 text-sm font-medium\">{t('staff.leaveRequests.decision.title', 'Decision')}</div>\n <Textarea\n value={decisionComment}\n onChange={(event) => setDecisionComment(event.target.value)}\n placeholder={t('staff.leaveRequests.decision.placeholder', 'Add a comment (optional)')}\n className=\"mb-3\"\n />\n <div className=\"flex flex-wrap gap-2\">\n <Button onClick={() => handleDecision('accept')}>\n {t('staff.leaveRequests.actions.accept', 'Approve')}\n </Button>\n <Button variant=\"destructive\" onClick={() => handleDecision('reject')}>\n {t('staff.leaveRequests.actions.reject', 'Reject')}\n </Button>\n </div>\n </div>\n ) : record.decisionComment || record.decision_comment ? (\n <div className=\"mb-6 rounded-lg border bg-card p-4 text-sm text-muted-foreground\">\n <div className=\"mb-1 font-medium text-foreground\">{t('staff.leaveRequests.decision.comment', 'Decision comment')}</div>\n <p>{record.decisionComment ?? record.decision_comment}</p>\n </div>\n ) : null}\n\n <LeaveRequestForm\n title={t('staff.leaveRequests.form.editTitle', 'Leave request')}\n submitLabel={t('staff.leaveRequests.form.actions.save', 'Save')}\n backHref=\"/backend/staff/leave-requests\"\n cancelHref=\"/backend/staff/leave-requests\"\n initialValues={initialValues}\n onSubmit={handleSubmit}\n allowMemberSelect\n memberLabel={memberLabel}\n extraActions={record.id ? (\n <SendObjectMessageDialog\n object={{\n entityModule: 'staff',\n entityType: 'leave_request',\n entityId: record.id,\n sourceEntityType: 'staff:leave_request',\n sourceEntityId: record.id,\n previewData: {\n title: memberLabel || t('staff.leaveRequests.messages.contextTitle', 'Linked leave request'),\n subtitle: dateSummary || undefined,\n status: record?.status ?? undefined,\n },\n }}\n viewHref={`/backend/staff/leave-requests/${record.id}`}\n lockedType=\"staff.leave_request_approval\"\n requiredActionConfig={{\n mode: 'required',\n options: [\n { id: 'approve', label: t('staff.notifications.leaveRequest.actions.approve', 'Approve') },\n { id: 'reject', label: t('staff.notifications.leaveRequest.actions.reject', 'Reject') },\n ],\n }}\n defaultValues={{\n type: 'staff.leave_request_approval',\n subject: t('staff.leaveRequests.messages.compose.subject', 'Leave request approval needed'),\n body: t('staff.leaveRequests.messages.compose.body', 'Please review this leave request and take action.'),\n }}\n />\n ) : null}\n />\n </PageBody>\n </Page>\n )\n}\n\nfunction resolveStatusVariant(status: 'pending' | 'approved' | 'rejected') {\n if (status === 'approved') return 'default'\n if (status === 'rejected') return 'destructive'\n return 'secondary'\n}\n\nfunction formatDateLabel(value?: string | null): string {\n if (!value) return ''\n const date = new Date(value)\n if (Number.isNaN(date.getTime())) return value\n return date.toLocaleDateString()\n}\n\nfunction formatDateRange(start?: string | null, end?: string | null): string {\n const startLabel = formatDateLabel(start)\n const endLabel = formatDateLabel(end)\n if (startLabel && endLabel) return `${startLabel} -> ${endLabel}`\n return startLabel || endLabel || '-'\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["params"]
|
|
7
7
|
}
|
|
@@ -9,6 +9,7 @@ import { apiCall } from "@open-mercato/ui/backend/utils/apiCall";
|
|
|
9
9
|
import { AvailabilityRulesEditor } from "@open-mercato/core/modules/planner/components/AvailabilityRulesEditor";
|
|
10
10
|
import { buildMemberScheduleItems } from "@open-mercato/core/modules/staff/lib/memberSchedule";
|
|
11
11
|
import { useT } from "@open-mercato/shared/lib/i18n/context";
|
|
12
|
+
import { SendObjectMessageDialog } from "@open-mercato/ui/backend/messages";
|
|
12
13
|
function StaffMyAvailabilityPage() {
|
|
13
14
|
const t = useT();
|
|
14
15
|
const [member, setMember] = React.useState(null);
|
|
@@ -70,6 +71,18 @@ function StaffMyAvailabilityPage() {
|
|
|
70
71
|
] }) }) });
|
|
71
72
|
}
|
|
72
73
|
return /* @__PURE__ */ jsx(Page, { children: /* @__PURE__ */ jsx(PageBody, { children: /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
|
|
74
|
+
member.availabilityRuleSetId ? /* @__PURE__ */ jsx("div", { className: "flex justify-end", children: /* @__PURE__ */ jsx(
|
|
75
|
+
SendObjectMessageDialog,
|
|
76
|
+
{
|
|
77
|
+
object: {
|
|
78
|
+
entityModule: "staff",
|
|
79
|
+
entityType: "my_availability",
|
|
80
|
+
entityId: member.availabilityRuleSetId,
|
|
81
|
+
previewData: { title: member.displayName ?? t("staff.myAvailability.title", "My Availability") }
|
|
82
|
+
},
|
|
83
|
+
viewHref: "/backend/staff/my-availability"
|
|
84
|
+
}
|
|
85
|
+
) }) : null,
|
|
73
86
|
!canManageAvailability ? /* @__PURE__ */ jsxs("div", { className: "space-y-2 rounded-lg border bg-card p-4 text-sm text-muted-foreground", children: [
|
|
74
87
|
/* @__PURE__ */ jsx("p", { className: "font-medium text-foreground", children: t("staff.myAvailability.readOnly.title", "Only an administrator can manage your availability.") }),
|
|
75
88
|
/* @__PURE__ */ jsx("p", { children: t("staff.myAvailability.readOnly.body", "Use leave requests to request changes.") }),
|