@vendure/dashboard 3.4.3-master-202509230228 → 3.4.3-master-202509240228
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/package.json +4 -4
- package/src/app/routes/_authenticated/_customers/components/customer-history/customer-history-container.tsx +2 -2
- package/src/app/routes/_authenticated/_customers/components/customer-history/customer-history-types.ts +5 -0
- package/src/app/routes/_authenticated/_customers/components/customer-history/customer-history-utils.tsx +124 -0
- package/src/app/routes/_authenticated/_customers/components/customer-history/customer-history.tsx +91 -59
- package/src/app/routes/_authenticated/_customers/components/customer-history/default-customer-history-components.tsx +176 -0
- package/src/app/routes/_authenticated/_customers/components/customer-history/index.ts +4 -2
- package/src/app/routes/_authenticated/_customers/customers.graphql.ts +2 -0
- package/src/app/routes/_authenticated/_orders/components/order-history/default-order-history-components.tsx +98 -0
- package/src/app/routes/_authenticated/_orders/components/order-history/order-history-container.tsx +9 -7
- package/src/app/routes/_authenticated/_orders/components/order-history/order-history-types.ts +5 -0
- package/src/app/routes/_authenticated/_orders/components/order-history/order-history-utils.tsx +173 -0
- package/src/app/routes/_authenticated/_orders/components/order-history/order-history.tsx +64 -408
- package/src/app/routes/_authenticated/_orders/orders.graphql.ts +4 -0
- package/src/lib/components/shared/history-timeline/history-note-entry.tsx +65 -0
- package/src/lib/components/shared/history-timeline/history-timeline-with-grouping.tsx +141 -0
- package/src/lib/components/shared/history-timeline/use-history-note-editor.ts +26 -0
- package/src/lib/framework/extension-api/define-dashboard-extension.ts +5 -0
- package/src/lib/framework/extension-api/extension-api-types.ts +7 -0
- package/src/lib/framework/extension-api/logic/history-entries.ts +24 -0
- package/src/lib/framework/extension-api/logic/index.ts +1 -0
- package/src/lib/framework/extension-api/types/history-entries.ts +120 -0
- package/src/lib/framework/extension-api/types/index.ts +1 -0
- package/src/lib/framework/history-entry/history-entry-extensions.ts +11 -0
- package/src/lib/framework/history-entry/history-entry.tsx +129 -0
- package/src/lib/framework/registry/registry-types.ts +2 -0
- package/src/lib/index.ts +5 -1
- package/src/lib/components/shared/history-timeline/history-entry.tsx +0 -188
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vendure/dashboard",
|
|
3
3
|
"private": false,
|
|
4
|
-
"version": "3.4.3-master-
|
|
4
|
+
"version": "3.4.3-master-202509240228",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
@@ -104,8 +104,8 @@
|
|
|
104
104
|
"@types/react": "^19.0.10",
|
|
105
105
|
"@types/react-dom": "^19.0.4",
|
|
106
106
|
"@uidotdev/usehooks": "^2.4.1",
|
|
107
|
-
"@vendure/common": "^3.4.3-master-
|
|
108
|
-
"@vendure/core": "^3.4.3-master-
|
|
107
|
+
"@vendure/common": "^3.4.3-master-202509240228",
|
|
108
|
+
"@vendure/core": "^3.4.3-master-202509240228",
|
|
109
109
|
"@vitejs/plugin-react": "^4.3.4",
|
|
110
110
|
"acorn": "^8.11.3",
|
|
111
111
|
"acorn-walk": "^8.3.2",
|
|
@@ -156,5 +156,5 @@
|
|
|
156
156
|
"lightningcss-linux-arm64-musl": "^1.29.3",
|
|
157
157
|
"lightningcss-linux-x64-musl": "^1.29.1"
|
|
158
158
|
},
|
|
159
|
-
"gitHead": "
|
|
159
|
+
"gitHead": "3cf135a11d028df5b1fbbb5ab6a1b117e91e13f7"
|
|
160
160
|
}
|
|
@@ -63,13 +63,13 @@ export function CustomerHistoryContainer({ customerId }: Readonly<CustomerHistor
|
|
|
63
63
|
<>
|
|
64
64
|
<CustomerHistory
|
|
65
65
|
customer={customer}
|
|
66
|
-
historyEntries={historyEntries
|
|
66
|
+
historyEntries={historyEntries}
|
|
67
67
|
onAddNote={addNote}
|
|
68
68
|
onUpdateNote={updateNote}
|
|
69
69
|
onDeleteNote={deleteNote}
|
|
70
70
|
/>
|
|
71
71
|
{hasNextPage && (
|
|
72
|
-
<Button type="button" variant="outline" onClick={() => fetchNextPage()}>
|
|
72
|
+
<Button type="button" variant="outline" onClick={() => fetchNextPage?.()}>
|
|
73
73
|
<Trans>Load more</Trans>
|
|
74
74
|
</Button>
|
|
75
75
|
)}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { HistoryEntryItem } from '@/vdb/framework/extension-api/types/index.js';
|
|
2
|
+
import { Trans } from '@/vdb/lib/trans.js';
|
|
3
|
+
import { CheckIcon, Edit3, KeyIcon, Mail, MapPin, SquarePen, User, UserCheck, Users } from 'lucide-react';
|
|
4
|
+
import { CustomerHistoryCustomerDetail } from './customer-history-types.js';
|
|
5
|
+
|
|
6
|
+
export function customerHistoryUtils(customer: CustomerHistoryCustomerDetail) {
|
|
7
|
+
const getTimelineIcon = (entry: HistoryEntryItem) => {
|
|
8
|
+
switch (entry.type) {
|
|
9
|
+
case 'CUSTOMER_REGISTERED':
|
|
10
|
+
return <User className="h-4 w-4" />;
|
|
11
|
+
case 'CUSTOMER_VERIFIED':
|
|
12
|
+
return <UserCheck className="h-4 w-4" />;
|
|
13
|
+
case 'CUSTOMER_NOTE':
|
|
14
|
+
return <SquarePen className="h-4 w-4" />;
|
|
15
|
+
case 'CUSTOMER_ADDED_TO_GROUP':
|
|
16
|
+
case 'CUSTOMER_REMOVED_FROM_GROUP':
|
|
17
|
+
return <Users className="h-4 w-4" />;
|
|
18
|
+
case 'CUSTOMER_DETAIL_UPDATED':
|
|
19
|
+
return <Edit3 className="h-4 w-4" />;
|
|
20
|
+
case 'CUSTOMER_ADDRESS_CREATED':
|
|
21
|
+
case 'CUSTOMER_ADDRESS_UPDATED':
|
|
22
|
+
case 'CUSTOMER_ADDRESS_DELETED':
|
|
23
|
+
return <MapPin className="h-4 w-4" />;
|
|
24
|
+
case 'CUSTOMER_PASSWORD_UPDATED':
|
|
25
|
+
case 'CUSTOMER_PASSWORD_RESET_REQUESTED':
|
|
26
|
+
case 'CUSTOMER_PASSWORD_RESET_VERIFIED':
|
|
27
|
+
return <KeyIcon className="h-4 w-4" />;
|
|
28
|
+
case 'CUSTOMER_EMAIL_UPDATE_REQUESTED':
|
|
29
|
+
case 'CUSTOMER_EMAIL_UPDATE_VERIFIED':
|
|
30
|
+
return <Mail className="h-4 w-4" />;
|
|
31
|
+
default:
|
|
32
|
+
return <CheckIcon className="h-4 w-4" />;
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const getTitle = (entry: HistoryEntryItem) => {
|
|
37
|
+
switch (entry.type) {
|
|
38
|
+
case 'CUSTOMER_REGISTERED':
|
|
39
|
+
return <Trans>Customer registered</Trans>;
|
|
40
|
+
case 'CUSTOMER_VERIFIED':
|
|
41
|
+
return <Trans>Customer verified</Trans>;
|
|
42
|
+
case 'CUSTOMER_NOTE':
|
|
43
|
+
return <Trans>Note added</Trans>;
|
|
44
|
+
case 'CUSTOMER_DETAIL_UPDATED':
|
|
45
|
+
return <Trans>Customer details updated</Trans>;
|
|
46
|
+
case 'CUSTOMER_ADDED_TO_GROUP':
|
|
47
|
+
return <Trans>Added to group</Trans>;
|
|
48
|
+
case 'CUSTOMER_REMOVED_FROM_GROUP':
|
|
49
|
+
return <Trans>Removed from group</Trans>;
|
|
50
|
+
case 'CUSTOMER_ADDRESS_CREATED':
|
|
51
|
+
return <Trans>Address created</Trans>;
|
|
52
|
+
case 'CUSTOMER_ADDRESS_UPDATED':
|
|
53
|
+
return <Trans>Address updated</Trans>;
|
|
54
|
+
case 'CUSTOMER_ADDRESS_DELETED':
|
|
55
|
+
return <Trans>Address deleted</Trans>;
|
|
56
|
+
case 'CUSTOMER_PASSWORD_UPDATED':
|
|
57
|
+
return <Trans>Password updated</Trans>;
|
|
58
|
+
case 'CUSTOMER_PASSWORD_RESET_REQUESTED':
|
|
59
|
+
return <Trans>Password reset requested</Trans>;
|
|
60
|
+
case 'CUSTOMER_PASSWORD_RESET_VERIFIED':
|
|
61
|
+
return <Trans>Password reset verified</Trans>;
|
|
62
|
+
case 'CUSTOMER_EMAIL_UPDATE_REQUESTED':
|
|
63
|
+
return <Trans>Email update requested</Trans>;
|
|
64
|
+
case 'CUSTOMER_EMAIL_UPDATE_VERIFIED':
|
|
65
|
+
return <Trans>Email update verified</Trans>;
|
|
66
|
+
default:
|
|
67
|
+
return <Trans>{entry.type.replace(/_/g, ' ').toLowerCase()}</Trans>;
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
const getIconColor = (entry: HistoryEntryItem) => {
|
|
72
|
+
// Check for success states
|
|
73
|
+
if (
|
|
74
|
+
entry.type === 'CUSTOMER_VERIFIED' ||
|
|
75
|
+
entry.type === 'CUSTOMER_EMAIL_UPDATE_VERIFIED' ||
|
|
76
|
+
entry.type === 'CUSTOMER_PASSWORD_RESET_VERIFIED'
|
|
77
|
+
) {
|
|
78
|
+
return 'bg-success text-success-foreground';
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Check for destructive states
|
|
82
|
+
if (entry.type === 'CUSTOMER_REMOVED_FROM_GROUP' || entry.type === 'CUSTOMER_ADDRESS_DELETED') {
|
|
83
|
+
return 'bg-destructive text-destructive-foreground';
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Registration gets muted style
|
|
87
|
+
if (entry.type === 'CUSTOMER_REGISTERED') {
|
|
88
|
+
return 'bg-muted text-muted-foreground';
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// All other entries use neutral colors
|
|
92
|
+
return 'bg-muted text-muted-foreground';
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
const getActorName = (entry: HistoryEntryItem) => {
|
|
96
|
+
if (entry.administrator) {
|
|
97
|
+
return `${entry.administrator.firstName} ${entry.administrator.lastName}`;
|
|
98
|
+
} else if (customer) {
|
|
99
|
+
return `${customer.firstName} ${customer.lastName}`;
|
|
100
|
+
}
|
|
101
|
+
return '';
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
const isPrimaryEvent = (entry: HistoryEntryItem) => {
|
|
105
|
+
switch (entry.type) {
|
|
106
|
+
case 'CUSTOMER_REGISTERED':
|
|
107
|
+
case 'CUSTOMER_VERIFIED':
|
|
108
|
+
case 'CUSTOMER_NOTE':
|
|
109
|
+
case 'CUSTOMER_EMAIL_UPDATE_VERIFIED':
|
|
110
|
+
case 'CUSTOMER_PASSWORD_RESET_VERIFIED':
|
|
111
|
+
return true;
|
|
112
|
+
default:
|
|
113
|
+
return false;
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
return {
|
|
118
|
+
getTimelineIcon,
|
|
119
|
+
getTitle,
|
|
120
|
+
getIconColor,
|
|
121
|
+
getActorName,
|
|
122
|
+
isPrimaryEvent,
|
|
123
|
+
};
|
|
124
|
+
}
|
package/src/app/routes/_authenticated/_customers/components/customer-history/customer-history.tsx
CHANGED
|
@@ -1,81 +1,113 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { HistoryNoteEditor } from '@/vdb/components/shared/history-timeline/history-note-editor.js';
|
|
2
|
+
import { HistoryNoteEntry } from '@/vdb/components/shared/history-timeline/history-note-entry.js';
|
|
2
3
|
import { HistoryNoteInput } from '@/vdb/components/shared/history-timeline/history-note-input.js';
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
4
|
+
import { HistoryTimelineWithGrouping } from '@/vdb/components/shared/history-timeline/history-timeline-with-grouping.js';
|
|
5
|
+
import { useHistoryNoteEditor } from '@/vdb/components/shared/history-timeline/use-history-note-editor.js';
|
|
6
|
+
import { HistoryEntryItem } from '@/vdb/framework/extension-api/types/history-entries.js';
|
|
7
|
+
import { HistoryEntryProps } from '@/vdb/framework/history-entry/history-entry.js';
|
|
8
|
+
import { CustomerHistoryCustomerDetail } from './customer-history-types.js';
|
|
9
|
+
import { customerHistoryUtils } from './customer-history-utils.js';
|
|
10
|
+
import {
|
|
11
|
+
CustomerAddRemoveGroupComponent,
|
|
12
|
+
CustomerAddressCreatedComponent,
|
|
13
|
+
CustomerAddressDeletedComponent,
|
|
14
|
+
CustomerAddressUpdatedComponent,
|
|
15
|
+
CustomerDetailUpdatedComponent,
|
|
16
|
+
CustomerEmailUpdateComponent,
|
|
17
|
+
CustomerPasswordResetRequestedComponent,
|
|
18
|
+
CustomerPasswordResetVerifiedComponent,
|
|
19
|
+
CustomerPasswordUpdatedComponent,
|
|
20
|
+
CustomerRegisteredOrVerifiedComponent,
|
|
21
|
+
} from './default-customer-history-components.js';
|
|
7
22
|
|
|
8
23
|
interface CustomerHistoryProps {
|
|
9
|
-
customer:
|
|
10
|
-
|
|
11
|
-
};
|
|
12
|
-
historyEntries: Array<HistoryEntryItem>;
|
|
24
|
+
customer: CustomerHistoryCustomerDetail;
|
|
25
|
+
historyEntries: HistoryEntryItem[];
|
|
13
26
|
onAddNote: (note: string, isPrivate: boolean) => void;
|
|
14
27
|
onUpdateNote?: (entryId: string, note: string, isPrivate: boolean) => void;
|
|
15
28
|
onDeleteNote?: (entryId: string) => void;
|
|
16
29
|
}
|
|
17
30
|
|
|
18
31
|
export function CustomerHistory({
|
|
32
|
+
customer,
|
|
19
33
|
historyEntries,
|
|
20
34
|
onAddNote,
|
|
21
35
|
onUpdateNote,
|
|
22
36
|
onDeleteNote,
|
|
23
|
-
}: CustomerHistoryProps) {
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
default:
|
|
29
|
-
return <CheckIcon className="h-4 w-4" />;
|
|
30
|
-
}
|
|
37
|
+
}: Readonly<CustomerHistoryProps>) {
|
|
38
|
+
const { noteState, noteEditorOpen, handleEditNote, setNoteEditorOpen } = useHistoryNoteEditor();
|
|
39
|
+
|
|
40
|
+
const handleDeleteNote = (noteId: string) => {
|
|
41
|
+
onDeleteNote?.(noteId);
|
|
31
42
|
};
|
|
32
43
|
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
44
|
+
const handleNoteEditorSave = (noteId: string, note: string, isPrivate: boolean) => {
|
|
45
|
+
onUpdateNote?.(noteId, note, isPrivate);
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const { getTimelineIcon, getTitle, getIconColor, getActorName, isPrimaryEvent } =
|
|
49
|
+
customerHistoryUtils(customer);
|
|
50
|
+
|
|
51
|
+
const renderEntryContent = (entry: HistoryEntryItem) => {
|
|
52
|
+
const props: HistoryEntryProps = {
|
|
53
|
+
entry,
|
|
54
|
+
title: getTitle(entry),
|
|
55
|
+
actorName: getActorName(entry),
|
|
56
|
+
timelineIcon: getTimelineIcon(entry),
|
|
57
|
+
timelineIconClassName: getIconColor(entry),
|
|
58
|
+
isPrimary: isPrimaryEvent(entry),
|
|
59
|
+
children: null,
|
|
60
|
+
};
|
|
61
|
+
if (entry.type === 'CUSTOMER_REGISTERED' || entry.type === 'CUSTOMER_VERIFIED') {
|
|
62
|
+
return <CustomerRegisteredOrVerifiedComponent {...props} />;
|
|
63
|
+
} else if (entry.type === 'CUSTOMER_DETAIL_UPDATED') {
|
|
64
|
+
return <CustomerDetailUpdatedComponent {...props} />;
|
|
65
|
+
} else if (entry.type === 'CUSTOMER_ADDED_TO_GROUP' || entry.type === 'CUSTOMER_REMOVED_FROM_GROUP') {
|
|
66
|
+
return <CustomerAddRemoveGroupComponent {...props} />;
|
|
67
|
+
} else if (entry.type === 'CUSTOMER_ADDRESS_CREATED') {
|
|
68
|
+
return <CustomerAddressCreatedComponent {...props} />;
|
|
69
|
+
} else if (entry.type === 'CUSTOMER_ADDRESS_UPDATED') {
|
|
70
|
+
return <CustomerAddressUpdatedComponent {...props} />;
|
|
71
|
+
} else if (entry.type === 'CUSTOMER_ADDRESS_DELETED') {
|
|
72
|
+
return <CustomerAddressDeletedComponent {...props} />;
|
|
73
|
+
} else if (entry.type === 'CUSTOMER_NOTE') {
|
|
74
|
+
return (
|
|
75
|
+
<HistoryNoteEntry {...props} onEditNote={handleEditNote} onDeleteNote={handleDeleteNote} />
|
|
76
|
+
);
|
|
77
|
+
} else if (entry.type === 'CUSTOMER_PASSWORD_UPDATED') {
|
|
78
|
+
return <CustomerPasswordUpdatedComponent {...props} />;
|
|
79
|
+
} else if (entry.type === 'CUSTOMER_PASSWORD_RESET_REQUESTED') {
|
|
80
|
+
return <CustomerPasswordResetRequestedComponent {...props} />;
|
|
81
|
+
} else if (entry.type === 'CUSTOMER_PASSWORD_RESET_VERIFIED') {
|
|
82
|
+
return <CustomerPasswordResetVerifiedComponent {...props} />;
|
|
83
|
+
} else if (
|
|
84
|
+
entry.type === 'CUSTOMER_EMAIL_UPDATE_REQUESTED' ||
|
|
85
|
+
entry.type === 'CUSTOMER_EMAIL_UPDATE_VERIFIED'
|
|
86
|
+
) {
|
|
87
|
+
return <CustomerEmailUpdateComponent {...props} />;
|
|
39
88
|
}
|
|
89
|
+
return null;
|
|
40
90
|
};
|
|
41
91
|
|
|
42
92
|
return (
|
|
43
|
-
|
|
44
|
-
<
|
|
93
|
+
<>
|
|
94
|
+
<HistoryTimelineWithGrouping
|
|
95
|
+
historyEntries={historyEntries}
|
|
96
|
+
isPrimaryEvent={isPrimaryEvent}
|
|
97
|
+
renderEntryContent={renderEntryContent}
|
|
98
|
+
entity={customer}
|
|
99
|
+
>
|
|
45
100
|
<HistoryNoteInput onAddNote={onAddNote} />
|
|
46
|
-
</
|
|
47
|
-
<
|
|
48
|
-
{
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
<div className="flex items-center space-x-2">
|
|
58
|
-
<Badge variant={entry.isPublic ? 'outline' : 'secondary'} className="text-xs">
|
|
59
|
-
{entry.isPublic ? 'Public' : 'Private'}
|
|
60
|
-
</Badge>
|
|
61
|
-
<span>{entry.data.note}</span>
|
|
62
|
-
</div>
|
|
63
|
-
)}
|
|
64
|
-
<div className="text-sm text-muted-foreground">
|
|
65
|
-
{entry.type === 'CUSTOMER_NOTE' && (
|
|
66
|
-
<Trans>
|
|
67
|
-
From {entry.data.from} to {entry.data.to}
|
|
68
|
-
</Trans>
|
|
69
|
-
)}
|
|
70
|
-
{entry.type === 'ORDER_PAYMENT_TRANSITION' && (
|
|
71
|
-
<Trans>
|
|
72
|
-
Payment #{entry.data.paymentId} transitioned to {entry.data.to}
|
|
73
|
-
</Trans>
|
|
74
|
-
)}
|
|
75
|
-
</div>
|
|
76
|
-
</HistoryEntry>
|
|
77
|
-
))}
|
|
78
|
-
</HistoryTimeline>
|
|
79
|
-
</div>
|
|
101
|
+
</HistoryTimelineWithGrouping>
|
|
102
|
+
<HistoryNoteEditor
|
|
103
|
+
key={noteState.noteId}
|
|
104
|
+
open={noteEditorOpen}
|
|
105
|
+
onOpenChange={setNoteEditorOpen}
|
|
106
|
+
onNoteChange={handleNoteEditorSave}
|
|
107
|
+
note={noteState.note}
|
|
108
|
+
isPrivate={noteState.isPrivate}
|
|
109
|
+
noteId={noteState.noteId}
|
|
110
|
+
/>
|
|
111
|
+
</>
|
|
80
112
|
);
|
|
81
113
|
}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import { HistoryEntry, HistoryEntryProps } from '@/vdb/framework/history-entry/history-entry.js';
|
|
2
|
+
import { Trans } from '@/vdb/lib/trans.js';
|
|
3
|
+
|
|
4
|
+
export function CustomerRegisteredOrVerifiedComponent(props: Readonly<HistoryEntryProps>) {
|
|
5
|
+
const { entry } = props;
|
|
6
|
+
const strategy = entry.data?.strategy || 'native';
|
|
7
|
+
|
|
8
|
+
return (
|
|
9
|
+
<HistoryEntry {...props}>
|
|
10
|
+
<div className="space-y-1">
|
|
11
|
+
{strategy === 'native' ? (
|
|
12
|
+
<p className="text-xs text-muted-foreground">
|
|
13
|
+
<Trans>Using native authentication strategy</Trans>
|
|
14
|
+
</p>
|
|
15
|
+
) : (
|
|
16
|
+
<p className="text-xs text-muted-foreground">
|
|
17
|
+
<Trans>Using external authentication strategy: {strategy}</Trans>
|
|
18
|
+
</p>
|
|
19
|
+
)}
|
|
20
|
+
</div>
|
|
21
|
+
</HistoryEntry>
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function CustomerDetailUpdatedComponent(props: Readonly<HistoryEntryProps>) {
|
|
26
|
+
const { entry } = props;
|
|
27
|
+
return (
|
|
28
|
+
<HistoryEntry {...props}>
|
|
29
|
+
<div className="space-y-2">
|
|
30
|
+
{entry.data?.input && (
|
|
31
|
+
<details className="text-xs">
|
|
32
|
+
<summary className="cursor-pointer text-muted-foreground hover:text-foreground">
|
|
33
|
+
<Trans>View changes</Trans>
|
|
34
|
+
</summary>
|
|
35
|
+
<pre className="mt-2 p-2 bg-muted rounded text-xs overflow-auto">
|
|
36
|
+
{JSON.stringify(entry.data.input, null, 2)}
|
|
37
|
+
</pre>
|
|
38
|
+
</details>
|
|
39
|
+
)}
|
|
40
|
+
</div>
|
|
41
|
+
</HistoryEntry>
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function CustomerAddRemoveGroupComponent(props: Readonly<HistoryEntryProps>) {
|
|
46
|
+
const { entry } = props;
|
|
47
|
+
const groupName = entry.data?.groupName || 'Unknown Group';
|
|
48
|
+
|
|
49
|
+
return (
|
|
50
|
+
<HistoryEntry {...props}>
|
|
51
|
+
<p className="text-xs text-muted-foreground">{groupName}</p>
|
|
52
|
+
</HistoryEntry>
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function CustomerAddressCreatedComponent(props: Readonly<HistoryEntryProps>) {
|
|
57
|
+
const { entry } = props;
|
|
58
|
+
return (
|
|
59
|
+
<HistoryEntry {...props}>
|
|
60
|
+
<div className="space-y-1">
|
|
61
|
+
<p className="text-xs text-muted-foreground">
|
|
62
|
+
<Trans>Address created</Trans>
|
|
63
|
+
</p>
|
|
64
|
+
{entry.data?.address && (
|
|
65
|
+
<div className="text-xs p-2 bg-muted rounded">{entry.data.address}</div>
|
|
66
|
+
)}
|
|
67
|
+
</div>
|
|
68
|
+
</HistoryEntry>
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export function CustomerAddressUpdatedComponent(props: Readonly<HistoryEntryProps>) {
|
|
73
|
+
const { entry } = props;
|
|
74
|
+
return (
|
|
75
|
+
<HistoryEntry {...props}>
|
|
76
|
+
<div className="space-y-2">
|
|
77
|
+
<p className="text-xs text-muted-foreground">
|
|
78
|
+
<Trans>Address updated</Trans>
|
|
79
|
+
</p>
|
|
80
|
+
{entry.data?.address && (
|
|
81
|
+
<div className="text-xs p-2 bg-muted rounded">{entry.data.address}</div>
|
|
82
|
+
)}
|
|
83
|
+
{entry.data?.input && (
|
|
84
|
+
<details className="text-xs">
|
|
85
|
+
<summary className="cursor-pointer text-muted-foreground hover:text-foreground">
|
|
86
|
+
<Trans>View changes</Trans>
|
|
87
|
+
</summary>
|
|
88
|
+
<pre className="mt-2 p-2 bg-muted rounded text-xs overflow-auto">
|
|
89
|
+
{JSON.stringify(entry.data.input, null, 2)}
|
|
90
|
+
</pre>
|
|
91
|
+
</details>
|
|
92
|
+
)}
|
|
93
|
+
</div>
|
|
94
|
+
</HistoryEntry>
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export function CustomerAddressDeletedComponent(props: Readonly<HistoryEntryProps>) {
|
|
99
|
+
const { entry } = props;
|
|
100
|
+
return (
|
|
101
|
+
<HistoryEntry {...props}>
|
|
102
|
+
<div className="space-y-1">
|
|
103
|
+
<p className="text-xs text-muted-foreground">
|
|
104
|
+
<Trans>Address deleted</Trans>
|
|
105
|
+
</p>
|
|
106
|
+
{entry.data?.address && (
|
|
107
|
+
<div className="text-xs p-2 bg-muted rounded">{entry.data.address}</div>
|
|
108
|
+
)}
|
|
109
|
+
</div>
|
|
110
|
+
</HistoryEntry>
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export function CustomerPasswordUpdatedComponent(props: Readonly<HistoryEntryProps>) {
|
|
115
|
+
return (
|
|
116
|
+
<HistoryEntry {...props}>
|
|
117
|
+
<p className="text-xs text-muted-foreground">
|
|
118
|
+
<Trans>Password updated</Trans>
|
|
119
|
+
</p>
|
|
120
|
+
</HistoryEntry>
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export function CustomerPasswordResetRequestedComponent(props: Readonly<HistoryEntryProps>) {
|
|
125
|
+
return (
|
|
126
|
+
<HistoryEntry {...props}>
|
|
127
|
+
<p className="text-xs text-muted-foreground">
|
|
128
|
+
<Trans>Password reset requested</Trans>
|
|
129
|
+
</p>
|
|
130
|
+
</HistoryEntry>
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export function CustomerPasswordResetVerifiedComponent(props: Readonly<HistoryEntryProps>) {
|
|
135
|
+
return (
|
|
136
|
+
<HistoryEntry {...props}>
|
|
137
|
+
<p className="text-xs text-muted-foreground">
|
|
138
|
+
<Trans>Password reset verified</Trans>
|
|
139
|
+
</p>
|
|
140
|
+
</HistoryEntry>
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export function CustomerEmailUpdateComponent({ entry }: Readonly<HistoryEntryProps>) {
|
|
145
|
+
const { oldEmailAddress, newEmailAddress } = entry.data || {};
|
|
146
|
+
|
|
147
|
+
return (
|
|
148
|
+
<div className="space-y-2">
|
|
149
|
+
{(oldEmailAddress || newEmailAddress) && (
|
|
150
|
+
<details className="text-xs">
|
|
151
|
+
<summary className="cursor-pointer text-muted-foreground hover:text-foreground">
|
|
152
|
+
<Trans>View details</Trans>
|
|
153
|
+
</summary>
|
|
154
|
+
<div className="mt-2 space-y-1">
|
|
155
|
+
{oldEmailAddress && (
|
|
156
|
+
<div>
|
|
157
|
+
<span className="font-medium">
|
|
158
|
+
<Trans>Old email:</Trans>
|
|
159
|
+
</span>{' '}
|
|
160
|
+
{oldEmailAddress}
|
|
161
|
+
</div>
|
|
162
|
+
)}
|
|
163
|
+
{newEmailAddress && (
|
|
164
|
+
<div>
|
|
165
|
+
<span className="font-medium">
|
|
166
|
+
<Trans>New email:</Trans>
|
|
167
|
+
</span>{' '}
|
|
168
|
+
{newEmailAddress}
|
|
169
|
+
</div>
|
|
170
|
+
)}
|
|
171
|
+
</div>
|
|
172
|
+
</details>
|
|
173
|
+
)}
|
|
174
|
+
</div>
|
|
175
|
+
);
|
|
176
|
+
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
-
export * from './customer-history.js';
|
|
2
1
|
export * from './customer-history-container.js';
|
|
3
|
-
export * from './
|
|
2
|
+
export * from './customer-history-utils.js';
|
|
3
|
+
export * from './customer-history.js';
|
|
4
|
+
export * from './default-customer-history-components.js';
|
|
5
|
+
export * from './default-customer-history-registry.js';
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { HistoryEntry, HistoryEntryProps } from '@/vdb/framework/history-entry/history-entry.js';
|
|
2
|
+
import { Trans } from '@/vdb/lib/trans.js';
|
|
3
|
+
|
|
4
|
+
export function OrderStateTransitionComponent(props: Readonly<HistoryEntryProps>) {
|
|
5
|
+
const { entry } = props;
|
|
6
|
+
if (entry.data.from === 'Created') return null;
|
|
7
|
+
|
|
8
|
+
return (
|
|
9
|
+
<HistoryEntry {...props}>
|
|
10
|
+
<p className="text-xs text-muted-foreground">
|
|
11
|
+
<Trans>
|
|
12
|
+
From {entry.data.from} to {entry.data.to}
|
|
13
|
+
</Trans>
|
|
14
|
+
</p>
|
|
15
|
+
</HistoryEntry>
|
|
16
|
+
);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function OrderPaymentTransitionComponent(props: Readonly<HistoryEntryProps>) {
|
|
20
|
+
const { entry } = props;
|
|
21
|
+
return (
|
|
22
|
+
<HistoryEntry {...props}>
|
|
23
|
+
<p className="text-xs text-muted-foreground">
|
|
24
|
+
<Trans>
|
|
25
|
+
Payment #{entry.data.paymentId} transitioned to {entry.data.to}
|
|
26
|
+
</Trans>
|
|
27
|
+
</p>
|
|
28
|
+
</HistoryEntry>
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function OrderRefundTransitionComponent(props: Readonly<HistoryEntryProps>) {
|
|
33
|
+
const { entry } = props;
|
|
34
|
+
return (
|
|
35
|
+
<HistoryEntry {...props}>
|
|
36
|
+
<p className="text-xs text-muted-foreground">
|
|
37
|
+
<Trans>
|
|
38
|
+
Refund #{entry.data.refundId} transitioned to {entry.data.to}
|
|
39
|
+
</Trans>
|
|
40
|
+
</p>
|
|
41
|
+
</HistoryEntry>
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function OrderFulfillmentTransitionComponent(props: Readonly<HistoryEntryProps>) {
|
|
46
|
+
const { entry } = props;
|
|
47
|
+
return (
|
|
48
|
+
<HistoryEntry {...props}>
|
|
49
|
+
<p className="text-xs text-muted-foreground">
|
|
50
|
+
<Trans>
|
|
51
|
+
Fulfillment #{entry.data.fulfillmentId} from {entry.data.from} to {entry.data.to}
|
|
52
|
+
</Trans>
|
|
53
|
+
</p>
|
|
54
|
+
</HistoryEntry>
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function OrderFulfillmentComponent(props: Readonly<HistoryEntryProps>) {
|
|
59
|
+
const { entry } = props;
|
|
60
|
+
return (
|
|
61
|
+
<HistoryEntry {...props}>
|
|
62
|
+
<p className="text-xs text-muted-foreground">
|
|
63
|
+
<Trans>Fulfillment #{entry.data.fulfillmentId} created</Trans>
|
|
64
|
+
</p>
|
|
65
|
+
</HistoryEntry>
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export function OrderModifiedComponent(props: Readonly<HistoryEntryProps>) {
|
|
70
|
+
const { entry } = props;
|
|
71
|
+
return (
|
|
72
|
+
<HistoryEntry {...props}>
|
|
73
|
+
<p className="text-xs text-muted-foreground">
|
|
74
|
+
<Trans>Order modification #{entry.data.modificationId}</Trans>
|
|
75
|
+
</p>
|
|
76
|
+
</HistoryEntry>
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export function OrderCustomerUpdatedComponent(props: Readonly<HistoryEntryProps>) {
|
|
81
|
+
return (
|
|
82
|
+
<HistoryEntry {...props}>
|
|
83
|
+
<p className="text-xs text-muted-foreground">
|
|
84
|
+
<Trans>Customer information updated</Trans>
|
|
85
|
+
</p>
|
|
86
|
+
</HistoryEntry>
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export function OrderCancellationComponent(props: Readonly<HistoryEntryProps>) {
|
|
91
|
+
return (
|
|
92
|
+
<HistoryEntry {...props}>
|
|
93
|
+
<p className="text-xs text-muted-foreground">
|
|
94
|
+
<Trans>Order cancelled</Trans>
|
|
95
|
+
</p>
|
|
96
|
+
</HistoryEntry>
|
|
97
|
+
);
|
|
98
|
+
}
|
package/src/app/routes/_authenticated/_orders/components/order-history/order-history-container.tsx
CHANGED
|
@@ -61,13 +61,15 @@ export function OrderHistoryContainer({ orderId }: Readonly<OrderHistoryContaine
|
|
|
61
61
|
|
|
62
62
|
return (
|
|
63
63
|
<>
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
64
|
+
{order ? (
|
|
65
|
+
<OrderHistory
|
|
66
|
+
order={order}
|
|
67
|
+
historyEntries={historyEntries ?? []}
|
|
68
|
+
onAddNote={addNote}
|
|
69
|
+
onUpdateNote={updateNote}
|
|
70
|
+
onDeleteNote={deleteNote}
|
|
71
|
+
/>
|
|
72
|
+
) : null}
|
|
71
73
|
{hasNextPage && (
|
|
72
74
|
<Button type="button" variant="outline" onClick={() => fetchNextPage()}>
|
|
73
75
|
<Trans>Load more</Trans>
|