@open-mercato/core 0.4.5-develop-636d33c995 → 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/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 +2 -2
- 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/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/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,4 +1,5 @@
|
|
|
1
1
|
import type { MessageObjectTypeDefinition } from '@open-mercato/shared/modules/messages/types'
|
|
2
|
+
import { MessageObjectDetail, MessageObjectPreview } from '@open-mercato/ui/backend/messages'
|
|
2
3
|
import { SalesDocumentMessageDetail } from './widgets/messages/SalesDocumentMessageDetail'
|
|
3
4
|
import { SalesDocumentMessagePreview } from './widgets/messages/SalesDocumentMessagePreview'
|
|
4
5
|
|
|
@@ -16,7 +17,14 @@ export const messageObjectTypes: MessageObjectTypeDefinition[] = [
|
|
|
16
17
|
icon: 'receipt-text',
|
|
17
18
|
PreviewComponent: SalesDocumentMessagePreview,
|
|
18
19
|
DetailComponent: SalesDocumentMessageDetail,
|
|
19
|
-
actions: [
|
|
20
|
+
actions: [
|
|
21
|
+
{
|
|
22
|
+
id: 'view',
|
|
23
|
+
labelKey: 'common.view',
|
|
24
|
+
variant: 'outline',
|
|
25
|
+
href: '/backend/sales/orders/{entityId}',
|
|
26
|
+
},
|
|
27
|
+
],
|
|
20
28
|
loadPreview: async (entityId, ctx) => {
|
|
21
29
|
if (typeof window !== 'undefined') {
|
|
22
30
|
return {
|
|
@@ -39,7 +47,14 @@ export const messageObjectTypes: MessageObjectTypeDefinition[] = [
|
|
|
39
47
|
icon: 'file-text',
|
|
40
48
|
PreviewComponent: SalesDocumentMessagePreview,
|
|
41
49
|
DetailComponent: SalesDocumentMessageDetail,
|
|
42
|
-
actions: [
|
|
50
|
+
actions: [
|
|
51
|
+
{
|
|
52
|
+
id: 'view',
|
|
53
|
+
labelKey: 'common.view',
|
|
54
|
+
variant: 'outline',
|
|
55
|
+
href: '/backend/sales/quotes/{entityId}',
|
|
56
|
+
},
|
|
57
|
+
],
|
|
43
58
|
loadPreview: async (entityId, ctx) => {
|
|
44
59
|
if (typeof window !== 'undefined') {
|
|
45
60
|
return {
|
|
@@ -51,6 +66,33 @@ export const messageObjectTypes: MessageObjectTypeDefinition[] = [
|
|
|
51
66
|
return loadSalesQuotePreview(entityId, ctx)
|
|
52
67
|
},
|
|
53
68
|
},
|
|
69
|
+
{
|
|
70
|
+
module: 'sales',
|
|
71
|
+
entityType: 'channel',
|
|
72
|
+
messageTypes: objectMessageTypes,
|
|
73
|
+
entityId: 'sales:sales_channel',
|
|
74
|
+
optionLabelField: 'name',
|
|
75
|
+
optionSubtitleField: 'status',
|
|
76
|
+
labelKey: 'sales.messageObjects.channel.title',
|
|
77
|
+
icon: 'store',
|
|
78
|
+
PreviewComponent: MessageObjectPreview,
|
|
79
|
+
DetailComponent: MessageObjectDetail,
|
|
80
|
+
actions: [
|
|
81
|
+
{
|
|
82
|
+
id: 'view',
|
|
83
|
+
labelKey: 'common.view',
|
|
84
|
+
variant: 'outline',
|
|
85
|
+
href: '/backend/sales/channels/{entityId}/edit',
|
|
86
|
+
},
|
|
87
|
+
],
|
|
88
|
+
loadPreview: async (entityId, ctx) => {
|
|
89
|
+
if (typeof window !== 'undefined') {
|
|
90
|
+
return { title: 'Sales channel', subtitle: entityId }
|
|
91
|
+
}
|
|
92
|
+
const { loadSalesChannelPreview } = await import('./lib/messageObjectPreviews')
|
|
93
|
+
return loadSalesChannelPreview(entityId, ctx)
|
|
94
|
+
},
|
|
95
|
+
},
|
|
54
96
|
]
|
|
55
97
|
|
|
56
98
|
export default messageObjectTypes
|
|
@@ -1,52 +1,90 @@
|
|
|
1
1
|
"use client"
|
|
2
2
|
|
|
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 type { ObjectDetailProps } from '@open-mercato/shared/modules/messages/types'
|
|
6
7
|
import { Button } from '@open-mercato/ui/primitives/button'
|
|
7
8
|
import { SalesDocumentMessagePreview } from './SalesDocumentMessagePreview'
|
|
8
9
|
|
|
10
|
+
function resolveActionHref(template: string, entityId: string): string {
|
|
11
|
+
return template.replace('{entityId}', encodeURIComponent(entityId))
|
|
12
|
+
}
|
|
13
|
+
|
|
9
14
|
export function SalesDocumentMessageDetail(props: ObjectDetailProps) {
|
|
10
15
|
const t = useT()
|
|
11
16
|
const [executingActionId, setExecutingActionId] = React.useState<string | null>(null)
|
|
12
17
|
|
|
18
|
+
const viewAction = props.actions.find((a) => a.id === 'view')
|
|
19
|
+
const otherActions = props.actions.filter((a) => a.id !== 'view')
|
|
20
|
+
|
|
21
|
+
const preview = (
|
|
22
|
+
<SalesDocumentMessagePreview
|
|
23
|
+
entityId={props.entityId}
|
|
24
|
+
entityModule={props.entityModule}
|
|
25
|
+
entityType={props.entityType}
|
|
26
|
+
snapshot={props.snapshot}
|
|
27
|
+
previewData={props.previewData}
|
|
28
|
+
actionRequired={props.actionRequired}
|
|
29
|
+
actionType={props.actionType}
|
|
30
|
+
actionLabel={props.actionLabel}
|
|
31
|
+
/>
|
|
32
|
+
)
|
|
33
|
+
|
|
13
34
|
return (
|
|
14
35
|
<div className="space-y-3 rounded border p-3">
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
{
|
|
36
|
+
{viewAction?.href ? (
|
|
37
|
+
<Link
|
|
38
|
+
href={resolveActionHref(viewAction.href, props.entityId)}
|
|
39
|
+
className="block rounded-md transition-opacity hover:opacity-75"
|
|
40
|
+
>
|
|
41
|
+
{preview}
|
|
42
|
+
</Link>
|
|
43
|
+
) : (
|
|
44
|
+
preview
|
|
45
|
+
)}
|
|
46
|
+
|
|
47
|
+
{otherActions.length > 0 ? (
|
|
27
48
|
<div className="flex flex-wrap gap-2">
|
|
28
|
-
{
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
49
|
+
{otherActions.map((action) => {
|
|
50
|
+
if (action.href) {
|
|
51
|
+
return (
|
|
52
|
+
<Button
|
|
53
|
+
key={action.id}
|
|
54
|
+
type="button"
|
|
55
|
+
size="sm"
|
|
56
|
+
variant={action.variant ?? 'default'}
|
|
57
|
+
asChild
|
|
58
|
+
>
|
|
59
|
+
<Link href={resolveActionHref(action.href, props.entityId)}>
|
|
60
|
+
{t(action.labelKey ?? action.id, action.id)}
|
|
61
|
+
</Link>
|
|
62
|
+
</Button>
|
|
63
|
+
)
|
|
64
|
+
}
|
|
65
|
+
return (
|
|
66
|
+
<Button
|
|
67
|
+
key={action.id}
|
|
68
|
+
type="button"
|
|
69
|
+
size="sm"
|
|
70
|
+
variant={action.variant ?? 'default'}
|
|
71
|
+
disabled={executingActionId !== null}
|
|
72
|
+
onClick={async () => {
|
|
73
|
+
if (executingActionId) return
|
|
74
|
+
setExecutingActionId(action.id)
|
|
75
|
+
try {
|
|
76
|
+
await props.onAction(action.id, { id: props.entityId })
|
|
77
|
+
} finally {
|
|
78
|
+
setExecutingActionId(null)
|
|
79
|
+
}
|
|
80
|
+
}}
|
|
81
|
+
>
|
|
82
|
+
{executingActionId === action.id
|
|
83
|
+
? t('messages.actions.executing', 'Executing...')
|
|
84
|
+
: t(action.labelKey ?? action.id, action.id)}
|
|
85
|
+
</Button>
|
|
86
|
+
)
|
|
87
|
+
})}
|
|
50
88
|
</div>
|
|
51
89
|
) : null}
|
|
52
90
|
</div>
|
|
@@ -19,7 +19,7 @@ export function SalesDocumentMessagePreview({
|
|
|
19
19
|
? t('sales.documents.detail.quote', 'Sales quote')
|
|
20
20
|
: t('sales.documents.detail.order', 'Sales order')
|
|
21
21
|
const title = previewData?.title || fallbackTitle
|
|
22
|
-
const subtitle = previewData?.subtitle ||
|
|
22
|
+
const subtitle = previewData?.subtitle || ""
|
|
23
23
|
|
|
24
24
|
return (
|
|
25
25
|
<div className="flex items-start gap-3 rounded-md border bg-muted/20 p-3">
|
|
@@ -112,21 +112,7 @@ export default function StaffLeaveRequestDetailPage({ params }: { params?: { id?
|
|
|
112
112
|
note: record?.note ?? null,
|
|
113
113
|
}), [record, memberLabel])
|
|
114
114
|
|
|
115
|
-
|
|
116
|
-
<div className="space-y-1">
|
|
117
|
-
<p className="font-medium">{t('staff.leaveRequests.messages.contextTitle', 'Linked leave request')}</p>
|
|
118
|
-
{memberLabel ? (
|
|
119
|
-
<p className="text-xs text-muted-foreground">
|
|
120
|
-
{t('staff.leaveRequests.detail.member', 'Team member')}: {memberLabel}
|
|
121
|
-
</p>
|
|
122
|
-
) : null}
|
|
123
|
-
<p className="text-xs text-muted-foreground">
|
|
124
|
-
{t('staff.leaveRequests.detail.dates', 'Dates')}: {dateSummary}
|
|
125
|
-
</p>
|
|
126
|
-
</div>
|
|
127
|
-
), [dateSummary, memberLabel, t])
|
|
128
|
-
|
|
129
|
-
const handleSubmit = React.useCallback(async (values: LeaveRequestFormValues) => {
|
|
115
|
+
const handleSubmit = React.useCallback(async (values: LeaveRequestFormValues) => {
|
|
130
116
|
if (!record?.id) return
|
|
131
117
|
const payload = buildLeaveRequestPayload(values, { id: record.id })
|
|
132
118
|
await updateCrud('staff/leave-requests', payload, {
|
|
@@ -239,7 +225,13 @@ export default function StaffLeaveRequestDetailPage({ params }: { params?: { id?
|
|
|
239
225
|
entityId: record.id,
|
|
240
226
|
sourceEntityType: 'staff:leave_request',
|
|
241
227
|
sourceEntityId: record.id,
|
|
228
|
+
previewData: {
|
|
229
|
+
title: memberLabel || t('staff.leaveRequests.messages.contextTitle', 'Linked leave request'),
|
|
230
|
+
subtitle: dateSummary || undefined,
|
|
231
|
+
status: record?.status ?? undefined,
|
|
232
|
+
},
|
|
242
233
|
}}
|
|
234
|
+
viewHref={`/backend/staff/leave-requests/${record.id}`}
|
|
243
235
|
lockedType="staff.leave_request_approval"
|
|
244
236
|
requiredActionConfig={{
|
|
245
237
|
mode: 'required',
|
|
@@ -253,20 +245,6 @@ export default function StaffLeaveRequestDetailPage({ params }: { params?: { id?
|
|
|
253
245
|
subject: t('staff.leaveRequests.messages.compose.subject', 'Leave request approval needed'),
|
|
254
246
|
body: t('staff.leaveRequests.messages.compose.body', 'Please review this leave request and take action.'),
|
|
255
247
|
}}
|
|
256
|
-
contextPreview={messageContextPreview}
|
|
257
|
-
renderTrigger={({ openComposer, disabled }) => (
|
|
258
|
-
<Button
|
|
259
|
-
type="button"
|
|
260
|
-
size="icon"
|
|
261
|
-
variant="outline"
|
|
262
|
-
onClick={openComposer}
|
|
263
|
-
disabled={disabled}
|
|
264
|
-
aria-label={t('staff.leaveRequests.messages.compose.action', 'Send for review')}
|
|
265
|
-
title={t('staff.leaveRequests.messages.compose.action', 'Send for review')}
|
|
266
|
-
>
|
|
267
|
-
<Send className="h-4 w-4" />
|
|
268
|
-
</Button>
|
|
269
|
-
)}
|
|
270
248
|
/>
|
|
271
249
|
) : null}
|
|
272
250
|
/>
|
|
@@ -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
|
|
|
13
14
|
type SelfMemberResponse = {
|
|
14
15
|
member?: {
|
|
@@ -112,6 +113,19 @@ export default function StaffMyAvailabilityPage() {
|
|
|
112
113
|
<Page>
|
|
113
114
|
<PageBody>
|
|
114
115
|
<div className="space-y-4">
|
|
116
|
+
{member.availabilityRuleSetId ? (
|
|
117
|
+
<div className="flex justify-end">
|
|
118
|
+
<SendObjectMessageDialog
|
|
119
|
+
object={{
|
|
120
|
+
entityModule: 'staff',
|
|
121
|
+
entityType: 'my_availability',
|
|
122
|
+
entityId: member.availabilityRuleSetId,
|
|
123
|
+
previewData: { title: member.displayName ?? t('staff.myAvailability.title', 'My Availability') },
|
|
124
|
+
}}
|
|
125
|
+
viewHref="/backend/staff/my-availability"
|
|
126
|
+
/>
|
|
127
|
+
</div>
|
|
128
|
+
) : null}
|
|
115
129
|
{!canManageAvailability ? (
|
|
116
130
|
<div className="space-y-2 rounded-lg border bg-card p-4 text-sm text-muted-foreground">
|
|
117
131
|
<p className="font-medium text-foreground">
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import * as React from 'react'
|
|
4
4
|
import { useRouter } from 'next/navigation'
|
|
5
|
-
import {
|
|
5
|
+
import { Send } from 'lucide-react'
|
|
6
6
|
import { Page, PageBody } from '@open-mercato/ui/backend/Page'
|
|
7
7
|
import { Badge } from '@open-mercato/ui/primitives/badge'
|
|
8
8
|
import { Button } from '@open-mercato/ui/primitives/button'
|
|
@@ -102,21 +102,7 @@ export default function StaffMyLeaveRequestDetailPage({ params }: { params?: { i
|
|
|
102
102
|
record?.startDate ?? record?.start_date ?? null,
|
|
103
103
|
record?.endDate ?? record?.end_date ?? null,
|
|
104
104
|
)
|
|
105
|
-
|
|
106
|
-
<div className="space-y-1">
|
|
107
|
-
<p className="font-medium">{t('staff.leaveRequests.messages.contextTitle', 'Linked leave request')}</p>
|
|
108
|
-
{memberLabel ? (
|
|
109
|
-
<p className="text-xs text-muted-foreground">
|
|
110
|
-
{t('staff.leaveRequests.detail.member', 'Team member')}: {memberLabel}
|
|
111
|
-
</p>
|
|
112
|
-
) : null}
|
|
113
|
-
<p className="text-xs text-muted-foreground">
|
|
114
|
-
{t('staff.leaveRequests.detail.dates', 'Dates')}: {dateSummary}
|
|
115
|
-
</p>
|
|
116
|
-
</div>
|
|
117
|
-
), [dateSummary, memberLabel, t])
|
|
118
|
-
|
|
119
|
-
const handleSubmit = React.useCallback(async (values: LeaveRequestFormValues) => {
|
|
105
|
+
const handleSubmit = React.useCallback(async (values: LeaveRequestFormValues) => {
|
|
120
106
|
if (!record?.id) return
|
|
121
107
|
const payload = buildLeaveRequestPayload(values, { id: record.id })
|
|
122
108
|
await updateCrud('staff/leave-requests', payload, {
|
|
@@ -186,7 +172,13 @@ export default function StaffMyLeaveRequestDetailPage({ params }: { params?: { i
|
|
|
186
172
|
entityId: record.id,
|
|
187
173
|
sourceEntityType: 'staff:leave_request',
|
|
188
174
|
sourceEntityId: record.id,
|
|
175
|
+
previewData: {
|
|
176
|
+
title: memberLabel || t('staff.leaveRequests.messages.contextTitle', 'Linked leave request'),
|
|
177
|
+
subtitle: dateSummary || undefined,
|
|
178
|
+
status: record?.status ?? undefined,
|
|
179
|
+
},
|
|
189
180
|
}}
|
|
181
|
+
viewHref={`/backend/staff/leave-requests/${record.id}`}
|
|
190
182
|
lockedType="staff.leave_request_approval"
|
|
191
183
|
requiredActionConfig={{
|
|
192
184
|
mode: 'required',
|
|
@@ -200,20 +192,6 @@ export default function StaffMyLeaveRequestDetailPage({ params }: { params?: { i
|
|
|
200
192
|
subject: t('staff.leaveRequests.messages.compose.subject', 'Leave request approval needed'),
|
|
201
193
|
body: t('staff.leaveRequests.messages.compose.body', 'Please review this leave request and take action.'),
|
|
202
194
|
}}
|
|
203
|
-
contextPreview={messageContextPreview}
|
|
204
|
-
renderTrigger={({ openComposer, disabled }) => (
|
|
205
|
-
<Button
|
|
206
|
-
type="button"
|
|
207
|
-
size="icon"
|
|
208
|
-
variant="outline"
|
|
209
|
-
onClick={openComposer}
|
|
210
|
-
disabled={disabled}
|
|
211
|
-
aria-label={t('staff.leaveRequests.messages.compose.action', 'Send for review')}
|
|
212
|
-
title={t('staff.leaveRequests.messages.compose.action', 'Send for review')}
|
|
213
|
-
>
|
|
214
|
-
<Leaf className="h-4 w-4" />
|
|
215
|
-
</Button>
|
|
216
|
-
)}
|
|
217
195
|
/>
|
|
218
196
|
) : null}
|
|
219
197
|
/>
|
|
@@ -31,6 +31,7 @@ import { JobHistorySection } from '@open-mercato/core/modules/staff/components/d
|
|
|
31
31
|
import type { DictionarySelectLabels } from '@open-mercato/core/modules/dictionaries/components/DictionaryEntrySelect'
|
|
32
32
|
import { Plus } from 'lucide-react'
|
|
33
33
|
import { TranslationDrawerAction } from '@open-mercato/core/modules/translations/components/TranslationDrawerAction'
|
|
34
|
+
import { SendObjectMessageDialog } from '@open-mercato/ui/backend/messages'
|
|
34
35
|
|
|
35
36
|
const MARKDOWN_CLASSNAME =
|
|
36
37
|
'text-sm text-muted-foreground break-words [&>*]: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'
|
|
@@ -325,13 +326,33 @@ export default function StaffTeamMemberDetailPage({ params }: { params?: { id?:
|
|
|
325
326
|
</p>
|
|
326
327
|
</div>
|
|
327
328
|
</div>
|
|
328
|
-
<
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
329
|
+
<div className="flex items-center gap-2">
|
|
330
|
+
{memberId ? (
|
|
331
|
+
<SendObjectMessageDialog
|
|
332
|
+
object={{
|
|
333
|
+
entityModule: 'staff',
|
|
334
|
+
entityType: 'team_member',
|
|
335
|
+
entityId: memberId,
|
|
336
|
+
previewData: {
|
|
337
|
+
title: displayName,
|
|
338
|
+
metadata: {
|
|
339
|
+
[t('staff.teamMembers.detail.fields.team')]: teamLabel,
|
|
340
|
+
[t('staff.teamMembers.detail.fields.user')]: userEmail ?? t('staff.teamMembers.detail.fields.userEmpty', 'No user linked'),
|
|
341
|
+
[t('staff.teamMembers.detail.fields.roles')]: roleLabels.join(', '),
|
|
342
|
+
},
|
|
343
|
+
},
|
|
344
|
+
}}
|
|
345
|
+
viewHref={`/backend/staff/team-members/${memberId}`}
|
|
346
|
+
/>
|
|
347
|
+
) : null}
|
|
348
|
+
<TranslationDrawerAction
|
|
349
|
+
config={memberId ? {
|
|
350
|
+
entityType: 'staff:staff_team_member',
|
|
351
|
+
recordId: memberId,
|
|
352
|
+
baseValues: memberRecord ?? undefined,
|
|
353
|
+
} : null}
|
|
354
|
+
/>
|
|
355
|
+
</div>
|
|
335
356
|
</div>
|
|
336
357
|
|
|
337
358
|
<div className="border-b">
|
|
@@ -8,6 +8,7 @@ import { updateCrud, deleteCrud } from '@open-mercato/ui/backend/utils/crud'
|
|
|
8
8
|
import { flash } from '@open-mercato/ui/backend/FlashMessages'
|
|
9
9
|
import { useT } from '@open-mercato/shared/lib/i18n/context'
|
|
10
10
|
import { TeamRoleForm, type TeamRoleFormValues, type TeamRoleOption, buildTeamRolePayload } from '@open-mercato/core/modules/staff/components/TeamRoleForm'
|
|
11
|
+
import { SendObjectMessageDialog } from '@open-mercato/ui/backend/messages'
|
|
11
12
|
import { extractCustomFieldEntries } from '@open-mercato/shared/lib/crud/custom-fields-client'
|
|
12
13
|
import { useOrganizationScopeVersion } from '@open-mercato/shared/lib/frontend/useOrganizationScope'
|
|
13
14
|
|
|
@@ -143,6 +144,17 @@ export default function StaffTeamRoleEditPage({ params }: { params?: { id?: stri
|
|
|
143
144
|
onDelete={handleDelete}
|
|
144
145
|
isLoading={!initialValues}
|
|
145
146
|
loadingMessage={t('staff.teamRoles.form.loading', 'Loading team role...')}
|
|
147
|
+
extraActions={roleId ? (
|
|
148
|
+
<SendObjectMessageDialog
|
|
149
|
+
object={{
|
|
150
|
+
entityModule: 'staff',
|
|
151
|
+
entityType: 'team_role',
|
|
152
|
+
entityId: roleId,
|
|
153
|
+
previewData: { title: initialValues?.name ?? ''},
|
|
154
|
+
}}
|
|
155
|
+
viewHref={`/backend/staff/team-roles/${roleId}/edit`}
|
|
156
|
+
/>
|
|
157
|
+
) : undefined}
|
|
146
158
|
/>
|
|
147
159
|
</PageBody>
|
|
148
160
|
</Page>
|
|
@@ -14,6 +14,7 @@ import { BooleanIcon } from '@open-mercato/ui/backend/ValueIcons'
|
|
|
14
14
|
import { flash } from '@open-mercato/ui/backend/FlashMessages'
|
|
15
15
|
import { useT } from '@open-mercato/shared/lib/i18n/context'
|
|
16
16
|
import { TeamForm, type TeamFormValues, buildTeamPayload } from '@open-mercato/core/modules/staff/components/TeamForm'
|
|
17
|
+
import { SendObjectMessageDialog } from '@open-mercato/ui/backend/messages'
|
|
17
18
|
import { extractCustomFieldEntries } from '@open-mercato/shared/lib/crud/custom-fields-client'
|
|
18
19
|
import { useOrganizationScopeVersion } from '@open-mercato/shared/lib/frontend/useOrganizationScope'
|
|
19
20
|
import { Plus } from 'lucide-react'
|
|
@@ -333,6 +334,17 @@ export default function StaffTeamEditPage({ params }: { params?: { id?: string }
|
|
|
333
334
|
onDelete={handleDelete}
|
|
334
335
|
isLoading={!initialValues}
|
|
335
336
|
loadingMessage={t('staff.teams.form.loading', 'Loading team...')}
|
|
337
|
+
extraActions={teamId ? (
|
|
338
|
+
<SendObjectMessageDialog
|
|
339
|
+
object={{
|
|
340
|
+
entityModule: 'staff',
|
|
341
|
+
entityType: 'team',
|
|
342
|
+
entityId: teamId,
|
|
343
|
+
previewData: { title: initialValues?.name ?? ''},
|
|
344
|
+
}}
|
|
345
|
+
viewHref={`/backend/staff/teams/${teamId}/edit`}
|
|
346
|
+
/>
|
|
347
|
+
) : undefined}
|
|
336
348
|
/>
|
|
337
349
|
) : (
|
|
338
350
|
<DataTable<TeamMemberRow>
|
|
@@ -24,6 +24,7 @@ export type TeamFormProps = {
|
|
|
24
24
|
onDelete?: () => Promise<void>
|
|
25
25
|
isLoading?: boolean
|
|
26
26
|
loadingMessage?: string
|
|
27
|
+
extraActions?: React.ReactNode
|
|
27
28
|
}
|
|
28
29
|
|
|
29
30
|
const normalizeCustomFieldSubmitValue = (value: unknown): unknown => {
|
|
@@ -60,6 +61,7 @@ export function TeamForm(props: TeamFormProps) {
|
|
|
60
61
|
onDelete,
|
|
61
62
|
isLoading,
|
|
62
63
|
loadingMessage,
|
|
64
|
+
extraActions,
|
|
63
65
|
} = props
|
|
64
66
|
const t = useT()
|
|
65
67
|
|
|
@@ -91,6 +93,7 @@ export function TeamForm(props: TeamFormProps) {
|
|
|
91
93
|
onDelete={onDelete}
|
|
92
94
|
isLoading={isLoading}
|
|
93
95
|
loadingMessage={loadingMessage}
|
|
96
|
+
extraActions={extraActions}
|
|
94
97
|
/>
|
|
95
98
|
)
|
|
96
99
|
}
|
|
@@ -32,6 +32,7 @@ export type TeamRoleFormProps = {
|
|
|
32
32
|
onDelete?: () => Promise<void>
|
|
33
33
|
isLoading?: boolean
|
|
34
34
|
loadingMessage?: string
|
|
35
|
+
extraActions?: React.ReactNode
|
|
35
36
|
}
|
|
36
37
|
|
|
37
38
|
const normalizeCustomFieldSubmitValue = (value: unknown): unknown => {
|
|
@@ -75,6 +76,7 @@ export function TeamRoleForm(props: TeamRoleFormProps) {
|
|
|
75
76
|
onDelete,
|
|
76
77
|
isLoading,
|
|
77
78
|
loadingMessage,
|
|
79
|
+
extraActions,
|
|
78
80
|
} = props
|
|
79
81
|
const t = useT()
|
|
80
82
|
|
|
@@ -154,6 +156,7 @@ export function TeamRoleForm(props: TeamRoleFormProps) {
|
|
|
154
156
|
onDelete={onDelete}
|
|
155
157
|
isLoading={isLoading}
|
|
156
158
|
loadingMessage={loadingMessage}
|
|
159
|
+
extraActions={extraActions}
|
|
157
160
|
/>
|
|
158
161
|
)
|
|
159
162
|
}
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { createRequestContainer } from '@open-mercato/shared/lib/di/container'
|
|
2
|
-
import { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'
|
|
2
|
+
import { findOneWithDecryption, findWithDecryption } from '@open-mercato/shared/lib/encryption/find'
|
|
3
3
|
import { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'
|
|
4
4
|
import type { ObjectPreviewData } from '@open-mercato/shared/modules/messages/types'
|
|
5
5
|
import type { EntityManager } from '@mikro-orm/postgresql'
|
|
6
|
-
import { StaffLeaveRequest, StaffTeam, StaffTeamMember } from '../data/entities'
|
|
6
|
+
import { StaffLeaveRequest, StaffTeam, StaffTeamMember, StaffTeamRole } from '../data/entities'
|
|
7
|
+
import { PlannerAvailabilityRuleSet } from '../../planner/data/entities'
|
|
8
|
+
import { User } from '../../auth/data/entities'
|
|
7
9
|
|
|
8
10
|
type PreviewContext = {
|
|
9
11
|
tenantId: string
|
|
@@ -170,6 +172,56 @@ export async function loadTeamMemberPreview(
|
|
|
170
172
|
|
|
171
173
|
const tags = Array.isArray(member.tags) ? member.tags : []
|
|
172
174
|
const metadata: Record<string, string> = {}
|
|
175
|
+
const teamLabel = t('staff.teamMembers.detail.fields.team')
|
|
176
|
+
const userLabel = t('staff.teamMembers.detail.fields.user')
|
|
177
|
+
const rolesLabel = t('staff.teamMembers.detail.fields.roles')
|
|
178
|
+
|
|
179
|
+
if (member.teamId) {
|
|
180
|
+
const team = await findOneWithDecryption(
|
|
181
|
+
em,
|
|
182
|
+
StaffTeam,
|
|
183
|
+
{
|
|
184
|
+
id: member.teamId,
|
|
185
|
+
tenantId: ctx.tenantId,
|
|
186
|
+
organizationId: ctx.organizationId,
|
|
187
|
+
deletedAt: null,
|
|
188
|
+
},
|
|
189
|
+
undefined,
|
|
190
|
+
{ tenantId: ctx.tenantId, organizationId: ctx.organizationId },
|
|
191
|
+
)
|
|
192
|
+
if (team?.name) metadata[teamLabel] = team.name
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
if (member.userId) {
|
|
196
|
+
const user = await findOneWithDecryption(
|
|
197
|
+
em,
|
|
198
|
+
User,
|
|
199
|
+
{ id: member.userId },
|
|
200
|
+
undefined,
|
|
201
|
+
{ tenantId: ctx.tenantId, organizationId: ctx.organizationId },
|
|
202
|
+
)
|
|
203
|
+
if (user?.email) metadata[userLabel] = user.email
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
if (Array.isArray(member.roleIds) && member.roleIds.length > 0) {
|
|
207
|
+
const roles = await findWithDecryption(
|
|
208
|
+
em,
|
|
209
|
+
StaffTeamRole,
|
|
210
|
+
{
|
|
211
|
+
id: { $in: member.roleIds },
|
|
212
|
+
tenantId: ctx.tenantId,
|
|
213
|
+
organizationId: ctx.organizationId,
|
|
214
|
+
deletedAt: null,
|
|
215
|
+
},
|
|
216
|
+
{ orderBy: { name: 'ASC' } },
|
|
217
|
+
{ tenantId: ctx.tenantId, organizationId: ctx.organizationId },
|
|
218
|
+
)
|
|
219
|
+
const roleNames = roles
|
|
220
|
+
.map((role) => role.name?.trim())
|
|
221
|
+
.filter((name): name is string => Boolean(name && name.length > 0))
|
|
222
|
+
if (roleNames.length > 0) metadata[rolesLabel] = roleNames.join(', ')
|
|
223
|
+
}
|
|
224
|
+
|
|
173
225
|
if (tags.length > 0) metadata.Tags = tags.slice(0, 5).join(', ')
|
|
174
226
|
|
|
175
227
|
return {
|
|
@@ -180,3 +232,82 @@ export async function loadTeamMemberPreview(
|
|
|
180
232
|
metadata: Object.keys(metadata).length > 0 ? metadata : undefined,
|
|
181
233
|
}
|
|
182
234
|
}
|
|
235
|
+
|
|
236
|
+
export async function loadStaffTeamRolePreview(
|
|
237
|
+
entityId: string,
|
|
238
|
+
ctx: PreviewContext,
|
|
239
|
+
): Promise<ObjectPreviewData> {
|
|
240
|
+
const { t } = await resolveTranslations()
|
|
241
|
+
const defaultTitle = t('staff.messageObjects.teamRole.title')
|
|
242
|
+
|
|
243
|
+
if (!ctx.organizationId) {
|
|
244
|
+
return { title: defaultTitle, subtitle: entityId }
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
const { resolve } = await createRequestContainer()
|
|
248
|
+
const em = resolve('em') as EntityManager
|
|
249
|
+
|
|
250
|
+
const role = await findOneWithDecryption(
|
|
251
|
+
em,
|
|
252
|
+
StaffTeamRole,
|
|
253
|
+
{
|
|
254
|
+
id: entityId,
|
|
255
|
+
tenantId: ctx.tenantId,
|
|
256
|
+
organizationId: ctx.organizationId,
|
|
257
|
+
deletedAt: null,
|
|
258
|
+
},
|
|
259
|
+
undefined,
|
|
260
|
+
{ tenantId: ctx.tenantId, organizationId: ctx.organizationId },
|
|
261
|
+
)
|
|
262
|
+
|
|
263
|
+
if (!role) {
|
|
264
|
+
return {
|
|
265
|
+
title: defaultTitle,
|
|
266
|
+
subtitle: entityId,
|
|
267
|
+
status: t('staff.messageObjects.notFound'),
|
|
268
|
+
statusColor: 'gray',
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
return {
|
|
273
|
+
title: role.name,
|
|
274
|
+
subtitle: role.description ?? undefined,
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
export async function loadStaffAvailabilityPreview(
|
|
279
|
+
entityId: string,
|
|
280
|
+
ctx: PreviewContext,
|
|
281
|
+
): Promise<ObjectPreviewData> {
|
|
282
|
+
const { t } = await resolveTranslations()
|
|
283
|
+
const defaultTitle = t('staff.messageObjects.myAvailability.title')
|
|
284
|
+
|
|
285
|
+
if (!ctx.organizationId) {
|
|
286
|
+
return { title: defaultTitle, subtitle: entityId }
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
const { resolve } = await createRequestContainer()
|
|
290
|
+
const em = resolve('em') as EntityManager
|
|
291
|
+
|
|
292
|
+
const ruleSet = await findOneWithDecryption(
|
|
293
|
+
em,
|
|
294
|
+
PlannerAvailabilityRuleSet,
|
|
295
|
+
{
|
|
296
|
+
id: entityId,
|
|
297
|
+
tenantId: ctx.tenantId,
|
|
298
|
+
organizationId: ctx.organizationId,
|
|
299
|
+
deletedAt: null,
|
|
300
|
+
},
|
|
301
|
+
undefined,
|
|
302
|
+
{ tenantId: ctx.tenantId, organizationId: ctx.organizationId },
|
|
303
|
+
)
|
|
304
|
+
|
|
305
|
+
if (!ruleSet) {
|
|
306
|
+
return { title: defaultTitle, subtitle: entityId }
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
return {
|
|
310
|
+
title: ruleSet.name,
|
|
311
|
+
subtitle: ruleSet.description ?? undefined,
|
|
312
|
+
}
|
|
313
|
+
}
|