@spree/seller-dashboard 1.0.0-beta.1
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/LICENSE +21 -0
- package/README.md +31 -0
- package/dist/vite-plugin/index.js +51 -0
- package/package.json +64 -0
- package/src/api-client.ts +203 -0
- package/src/components/centered-message.tsx +8 -0
- package/src/components/finance/balance-summary.tsx +69 -0
- package/src/components/onboarding-nav-badge.tsx +38 -0
- package/src/components/orders/fulfillment-deliveries.tsx +116 -0
- package/src/components/orders/fulfillment-delivery-dialog.tsx +82 -0
- package/src/components/orders/fulfillment-edit-dialog.tsx +95 -0
- package/src/components/orders/fulfillment-fulfill-form.tsx +132 -0
- package/src/components/orders/fulfillment-label-upload-dialog.tsx +95 -0
- package/src/components/orders/fulfillment-split-dialog.tsx +142 -0
- package/src/components/orders/fulfillments-card.tsx +289 -0
- package/src/components/orders/line-label.ts +28 -0
- package/src/components/orders/order-cancel-dialog.tsx +74 -0
- package/src/components/orders/order-cards.tsx +297 -0
- package/src/components/orders/order-earnings-card.tsx +99 -0
- package/src/components/orders/order-notes-cards.tsx +83 -0
- package/src/components/orders/order-payment-card.tsx +94 -0
- package/src/components/orders/post-sale-cards.tsx +347 -0
- package/src/components/orders/post-sale-create-dialogs.tsx +201 -0
- package/src/components/orders/returns-card.tsx +258 -0
- package/src/components/orders/shipping-label-row.tsx +124 -0
- package/src/components/product-status-card.tsx +111 -0
- package/src/components/read-row.tsx +16 -0
- package/src/components/retryable-error.tsx +23 -0
- package/src/components/seller-address-card.tsx +123 -0
- package/src/components/seller-business-card.tsx +152 -0
- package/src/components/seller-returns-location-card.tsx +151 -0
- package/src/components/seller-switcher.tsx +124 -0
- package/src/components/seller-tax-identifiers-card.tsx +63 -0
- package/src/create-router.ts +32 -0
- package/src/hooks/use-fulfillments.ts +155 -0
- package/src/hooks/use-ledger.ts +46 -0
- package/src/hooks/use-order.ts +62 -0
- package/src/hooks/use-post-sale.ts +202 -0
- package/src/hooks/use-reasons.ts +34 -0
- package/src/hooks/use-stock-locations.ts +23 -0
- package/src/hooks/use-store-timezone.ts +26 -0
- package/src/i18n.ts +26 -0
- package/src/index.ts +27 -0
- package/src/lib/packing-slip.ts +109 -0
- package/src/locales/en.json +841 -0
- package/src/nav/default.ts +108 -0
- package/src/nav/settings.ts +114 -0
- package/src/pages/accept-invitation.tsx +127 -0
- package/src/pages/delivery-methods.tsx +647 -0
- package/src/pages/earnings.tsx +44 -0
- package/src/pages/forgot-password.tsx +100 -0
- package/src/pages/home.tsx +132 -0
- package/src/pages/login.tsx +94 -0
- package/src/pages/onboarding.tsx +539 -0
- package/src/pages/order.tsx +119 -0
- package/src/pages/orders.tsx +33 -0
- package/src/pages/payout.tsx +154 -0
- package/src/pages/payouts.tsx +32 -0
- package/src/pages/policies.tsx +292 -0
- package/src/pages/product.tsx +250 -0
- package/src/pages/products.tsx +228 -0
- package/src/pages/profile.tsx +389 -0
- package/src/pages/reset-password.tsx +105 -0
- package/src/pages/seller-picker.tsx +46 -0
- package/src/pages/team.tsx +468 -0
- package/src/panel-chrome.tsx +36 -0
- package/src/routeTree.gen.ts +590 -0
- package/src/routes/__root.tsx +15 -0
- package/src/routes/_authenticated/$sellerId/earnings/index.tsx +15 -0
- package/src/routes/_authenticated/$sellerId/index.tsx +6 -0
- package/src/routes/_authenticated/$sellerId/onboarding.tsx +6 -0
- package/src/routes/_authenticated/$sellerId/orders/$orderId.tsx +6 -0
- package/src/routes/_authenticated/$sellerId/orders/index.tsx +15 -0
- package/src/routes/_authenticated/$sellerId/payouts/$payoutId.tsx +7 -0
- package/src/routes/_authenticated/$sellerId/payouts/index.tsx +15 -0
- package/src/routes/_authenticated/$sellerId/products/$productId.tsx +6 -0
- package/src/routes/_authenticated/$sellerId/products/index.tsx +23 -0
- package/src/routes/_authenticated/$sellerId/products/new.tsx +6 -0
- package/src/routes/_authenticated/$sellerId/profile.tsx +6 -0
- package/src/routes/_authenticated/$sellerId/settings/delivery-methods.tsx +14 -0
- package/src/routes/_authenticated/$sellerId/settings/index.tsx +18 -0
- package/src/routes/_authenticated/$sellerId/settings/package-types.tsx +21 -0
- package/src/routes/_authenticated/$sellerId/settings/policies.tsx +14 -0
- package/src/routes/_authenticated/$sellerId/settings/stock-locations.tsx +22 -0
- package/src/routes/_authenticated/$sellerId/settings/team.tsx +6 -0
- package/src/routes/_authenticated/$sellerId/settings.tsx +18 -0
- package/src/routes/_authenticated/$sellerId.tsx +49 -0
- package/src/routes/_authenticated/index.tsx +48 -0
- package/src/routes/_authenticated.tsx +13 -0
- package/src/routes/accept-invitation.$invitationId.tsx +19 -0
- package/src/routes/forgot-password.tsx +15 -0
- package/src/routes/login.tsx +15 -0
- package/src/routes/reset-password.tsx +21 -0
- package/src/schemas/fulfillment.ts +31 -0
- package/src/seller-dashboard.tsx +50 -0
- package/src/styles.css +18 -0
- package/src/tables/delivery-methods.tsx +68 -0
- package/src/tables/orders.tsx +116 -0
- package/src/tables/payouts.tsx +87 -0
- package/src/tables/policies.tsx +37 -0
- package/src/tables/products.tsx +56 -0
- package/src/tables/transfers.tsx +118 -0
- package/src/vite/index.ts +87 -0
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { defineTable, formatStoreDateTime } from '@spree/dashboard-core'
|
|
2
|
+
import { ResourceNameCell, StatusBadge } from '@spree/dashboard-ui'
|
|
3
|
+
import { HandCoinsIcon } from '@spree/dashboard-ui/icons'
|
|
4
|
+
import type { Payout } from '@spree/seller-sdk'
|
|
5
|
+
import i18n from 'i18next'
|
|
6
|
+
import { useStoreTimezone } from '../hooks/use-store-timezone'
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* A ledger row's date in the marketplace's timezone.
|
|
10
|
+
*
|
|
11
|
+
* Its own component because a table definition is module-level and has no
|
|
12
|
+
* access to hooks, while `render` is called inside the table's JSX — so a
|
|
13
|
+
* cell may use them like any other component.
|
|
14
|
+
*/
|
|
15
|
+
function LedgerDate({ iso }: { iso: string }) {
|
|
16
|
+
const timezone = useStoreTimezone()
|
|
17
|
+
|
|
18
|
+
return <>{formatStoreDateTime(iso, timezone)}</>
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const STATUSES = ['pending', 'processing', 'completed', 'failed', 'unresolved'] as const
|
|
22
|
+
|
|
23
|
+
defineTable<Payout>('seller-payouts', {
|
|
24
|
+
title: i18n.t('payouts.title'),
|
|
25
|
+
searchParam: 'reference_cont',
|
|
26
|
+
searchPlaceholder: i18n.t('payouts.search_placeholder'),
|
|
27
|
+
defaultSort: { field: 'created_at', direction: 'desc' },
|
|
28
|
+
emptyIcon: <HandCoinsIcon className="size-8 text-muted-foreground" />,
|
|
29
|
+
emptyMessage: i18n.t('payouts.empty'),
|
|
30
|
+
columns: [
|
|
31
|
+
{
|
|
32
|
+
key: 'created_at',
|
|
33
|
+
label: i18n.t('payouts.columns.date'),
|
|
34
|
+
sortable: true,
|
|
35
|
+
filterable: true,
|
|
36
|
+
filterType: 'date',
|
|
37
|
+
quickFilter: true,
|
|
38
|
+
default: true,
|
|
39
|
+
render: (payout) => (
|
|
40
|
+
<ResourceNameCell
|
|
41
|
+
id={payout.id}
|
|
42
|
+
dataAttr="data-payout-id"
|
|
43
|
+
name={<LedgerDate iso={payout.created_at} />}
|
|
44
|
+
/>
|
|
45
|
+
),
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
key: 'status',
|
|
49
|
+
label: i18n.t('payouts.columns.status'),
|
|
50
|
+
sortable: true,
|
|
51
|
+
filterable: true,
|
|
52
|
+
filterType: 'enum',
|
|
53
|
+
filterOptions: STATUSES.map((status) => ({
|
|
54
|
+
value: status,
|
|
55
|
+
label: i18n.t(`earnings.statuses.${status}`),
|
|
56
|
+
})),
|
|
57
|
+
quickFilter: true,
|
|
58
|
+
default: true,
|
|
59
|
+
render: (payout) => (
|
|
60
|
+
<StatusBadge
|
|
61
|
+
status={payout.status}
|
|
62
|
+
label={i18n.t(`earnings.statuses.${payout.status}`, { defaultValue: payout.status })}
|
|
63
|
+
/>
|
|
64
|
+
),
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
key: 'transfers_count',
|
|
68
|
+
label: i18n.t('payouts.columns.earnings'),
|
|
69
|
+
default: true,
|
|
70
|
+
render: (payout) => payout.transfers_count,
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
key: 'reference',
|
|
74
|
+
label: i18n.t('payouts.columns.reference'),
|
|
75
|
+
default: true,
|
|
76
|
+
render: (payout) => payout.reference ?? '—',
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
key: 'amount',
|
|
80
|
+
label: i18n.t('payouts.columns.amount'),
|
|
81
|
+
sortable: true,
|
|
82
|
+
default: true,
|
|
83
|
+
className: 'text-right tabular-nums',
|
|
84
|
+
render: (payout) => payout.display_amount,
|
|
85
|
+
},
|
|
86
|
+
],
|
|
87
|
+
})
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { defineTable } from '@spree/dashboard-core'
|
|
2
|
+
import { RelativeTime, ResourceNameCell } from '@spree/dashboard-ui'
|
|
3
|
+
import { ScrollTextIcon } from '@spree/dashboard-ui/icons'
|
|
4
|
+
import type { Policy } from '@spree/seller-sdk'
|
|
5
|
+
import i18n from 'i18next'
|
|
6
|
+
|
|
7
|
+
defineTable<Policy>('seller-policies', {
|
|
8
|
+
title: i18n.t('policies.title'),
|
|
9
|
+
searchParam: 'name_cont',
|
|
10
|
+
searchPlaceholder: i18n.t('policies.search_placeholder'),
|
|
11
|
+
defaultSort: { field: 'name', direction: 'asc' },
|
|
12
|
+
emptyIcon: <ScrollTextIcon className="size-8 text-muted-foreground" />,
|
|
13
|
+
emptyMessage: i18n.t('policies.empty.title'),
|
|
14
|
+
columns: [
|
|
15
|
+
{
|
|
16
|
+
key: 'name',
|
|
17
|
+
label: i18n.t('policies.columns.name'),
|
|
18
|
+
sortable: true,
|
|
19
|
+
default: true,
|
|
20
|
+
render: (policy) => (
|
|
21
|
+
<ResourceNameCell
|
|
22
|
+
id={policy.id}
|
|
23
|
+
dataAttr="data-policy-id"
|
|
24
|
+
name={policy.name}
|
|
25
|
+
secondary={policy.slug}
|
|
26
|
+
/>
|
|
27
|
+
),
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
key: 'updated_at',
|
|
31
|
+
label: i18n.t('policies.columns.updated'),
|
|
32
|
+
sortable: true,
|
|
33
|
+
default: true,
|
|
34
|
+
render: (policy) => <RelativeTime iso={policy.updated_at} />,
|
|
35
|
+
},
|
|
36
|
+
],
|
|
37
|
+
})
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { defineTable } from '@spree/dashboard-core'
|
|
2
|
+
import { ResourceNameCell, StatusBadge, Thumbnail } from '@spree/dashboard-ui'
|
|
3
|
+
import { PackageIcon } from '@spree/dashboard-ui/icons'
|
|
4
|
+
import type { Product } from '@spree/seller-sdk'
|
|
5
|
+
import i18n from 'i18next'
|
|
6
|
+
|
|
7
|
+
/** Mirrors the operator dashboard's product statuses. */
|
|
8
|
+
const PRODUCT_STATUSES = ['active', 'draft', 'proposed', 'rejected', 'archived'] as const
|
|
9
|
+
|
|
10
|
+
defineTable<Product>('seller-products', {
|
|
11
|
+
title: i18n.t('products.title'),
|
|
12
|
+
searchParam: 'name_cont',
|
|
13
|
+
searchPlaceholder: i18n.t('products.search_placeholder'),
|
|
14
|
+
defaultSort: { field: 'created_at', direction: 'desc' },
|
|
15
|
+
emptyIcon: <PackageIcon className="size-8 text-muted-foreground" />,
|
|
16
|
+
emptyMessage: i18n.t('products.empty'),
|
|
17
|
+
columns: [
|
|
18
|
+
{
|
|
19
|
+
key: 'name',
|
|
20
|
+
label: i18n.t('products.columns.name'),
|
|
21
|
+
sortable: true,
|
|
22
|
+
default: true,
|
|
23
|
+
render: (product) => (
|
|
24
|
+
<div className="flex items-center gap-3">
|
|
25
|
+
<Thumbnail src={product.thumbnail_url} alt={product.name} />
|
|
26
|
+
<ResourceNameCell id={product.id} dataAttr="data-product-id" name={product.name} />
|
|
27
|
+
</div>
|
|
28
|
+
),
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
key: 'status',
|
|
32
|
+
label: i18n.t('products.columns.status'),
|
|
33
|
+
sortable: true,
|
|
34
|
+
filterable: true,
|
|
35
|
+
filterType: 'enum',
|
|
36
|
+
filterOptions: PRODUCT_STATUSES.map((status) => ({
|
|
37
|
+
value: status,
|
|
38
|
+
label: i18n.t(`products.statuses.${status}`),
|
|
39
|
+
})),
|
|
40
|
+
quickFilter: true,
|
|
41
|
+
default: true,
|
|
42
|
+
render: (product) => (
|
|
43
|
+
<StatusBadge
|
|
44
|
+
status={product.status}
|
|
45
|
+
label={i18n.t(`products.statuses.${product.status}`)}
|
|
46
|
+
/>
|
|
47
|
+
),
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
key: 'price',
|
|
51
|
+
label: i18n.t('products.columns.price'),
|
|
52
|
+
default: true,
|
|
53
|
+
render: (product) => product.price?.display_amount ?? '—',
|
|
54
|
+
},
|
|
55
|
+
],
|
|
56
|
+
})
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { defineTable, formatStoreDateTime, useTenantId } from '@spree/dashboard-core'
|
|
2
|
+
import { StatusBadge } from '@spree/dashboard-ui'
|
|
3
|
+
import { BanknoteIcon } from '@spree/dashboard-ui/icons'
|
|
4
|
+
import type { Transfer } from '@spree/seller-sdk'
|
|
5
|
+
import { Link } from '@tanstack/react-router'
|
|
6
|
+
import i18n from 'i18next'
|
|
7
|
+
import { useStoreTimezone } from '../hooks/use-store-timezone'
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* The sale behind an earning — what a seller checking a figure wants next.
|
|
11
|
+
*
|
|
12
|
+
* Its own component because a table definition is module-level and holds no
|
|
13
|
+
* router context, while the link needs the seller whose panel this is. That
|
|
14
|
+
* is what `useTenantId` answers, and a cell is rendered inside React like any
|
|
15
|
+
* other component.
|
|
16
|
+
*/
|
|
17
|
+
function OrderCell({ transfer }: { transfer: Transfer }) {
|
|
18
|
+
const sellerId = useTenantId()
|
|
19
|
+
|
|
20
|
+
if (!transfer.order_id) return <>—</>
|
|
21
|
+
|
|
22
|
+
return (
|
|
23
|
+
<Link
|
|
24
|
+
to="/$sellerId/orders/$orderId"
|
|
25
|
+
params={{ sellerId, orderId: transfer.order_id }}
|
|
26
|
+
className="no-underline"
|
|
27
|
+
>
|
|
28
|
+
{transfer.order_number ?? transfer.order_id}
|
|
29
|
+
</Link>
|
|
30
|
+
)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* A ledger row's date in the marketplace's timezone.
|
|
35
|
+
*
|
|
36
|
+
* Its own component because a table definition is module-level and has no
|
|
37
|
+
* access to hooks, while `render` is called inside the table's JSX — so a
|
|
38
|
+
* cell may use them like any other component.
|
|
39
|
+
*/
|
|
40
|
+
function LedgerDate({ iso }: { iso: string }) {
|
|
41
|
+
const timezone = useStoreTimezone()
|
|
42
|
+
|
|
43
|
+
return <>{formatStoreDateTime(iso, timezone)}</>
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const STATUSES = ['pending', 'processing', 'completed', 'failed', 'unresolved'] as const
|
|
47
|
+
const KINDS = ['earning', 'refund_reversal'] as const
|
|
48
|
+
|
|
49
|
+
defineTable<Transfer>('seller-transfers', {
|
|
50
|
+
title: i18n.t('earnings.transfers.title'),
|
|
51
|
+
// Without this the toolbar sends `name_cont`, which the ledger does not
|
|
52
|
+
// whitelist — Ransack drops an unknown condition, so the box would look
|
|
53
|
+
// like it worked and return everything.
|
|
54
|
+
searchParam: 'reference_cont',
|
|
55
|
+
searchPlaceholder: i18n.t('earnings.search_placeholder'),
|
|
56
|
+
defaultSort: { field: 'created_at', direction: 'desc' },
|
|
57
|
+
emptyIcon: <BanknoteIcon className="size-8 text-muted-foreground" />,
|
|
58
|
+
emptyMessage: i18n.t('earnings.transfers.empty'),
|
|
59
|
+
columns: [
|
|
60
|
+
{
|
|
61
|
+
key: 'created_at',
|
|
62
|
+
label: i18n.t('earnings.columns.date'),
|
|
63
|
+
sortable: true,
|
|
64
|
+
filterable: true,
|
|
65
|
+
filterType: 'date',
|
|
66
|
+
quickFilter: true,
|
|
67
|
+
default: true,
|
|
68
|
+
render: (transfer) => <LedgerDate iso={transfer.created_at} />,
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
key: 'order_number',
|
|
72
|
+
label: i18n.t('earnings.columns.order'),
|
|
73
|
+
default: true,
|
|
74
|
+
render: (transfer) => <OrderCell transfer={transfer} />,
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
key: 'kind',
|
|
78
|
+
label: i18n.t('earnings.columns.kind'),
|
|
79
|
+
filterable: true,
|
|
80
|
+
filterType: 'enum',
|
|
81
|
+
filterOptions: KINDS.map((kind) => ({
|
|
82
|
+
value: kind,
|
|
83
|
+
label: i18n.t(`earnings.kinds.${kind}`),
|
|
84
|
+
})),
|
|
85
|
+
quickFilter: true,
|
|
86
|
+
default: true,
|
|
87
|
+
render: (transfer) =>
|
|
88
|
+
i18n.t(`earnings.kinds.${transfer.kind}`, { defaultValue: transfer.kind }),
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
key: 'status',
|
|
92
|
+
label: i18n.t('earnings.columns.status'),
|
|
93
|
+
sortable: true,
|
|
94
|
+
filterable: true,
|
|
95
|
+
filterType: 'enum',
|
|
96
|
+
filterOptions: STATUSES.map((status) => ({
|
|
97
|
+
value: status,
|
|
98
|
+
label: i18n.t(`earnings.statuses.${status}`),
|
|
99
|
+
})),
|
|
100
|
+
quickFilter: true,
|
|
101
|
+
default: true,
|
|
102
|
+
render: (transfer) => (
|
|
103
|
+
<StatusBadge
|
|
104
|
+
status={transfer.status}
|
|
105
|
+
label={i18n.t(`earnings.statuses.${transfer.status}`, { defaultValue: transfer.status })}
|
|
106
|
+
/>
|
|
107
|
+
),
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
key: 'amount',
|
|
111
|
+
label: i18n.t('earnings.columns.amount'),
|
|
112
|
+
sortable: true,
|
|
113
|
+
default: true,
|
|
114
|
+
className: 'text-right tabular-nums',
|
|
115
|
+
render: (transfer) => transfer.display_amount,
|
|
116
|
+
},
|
|
117
|
+
],
|
|
118
|
+
})
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { createRequire } from 'node:module'
|
|
2
|
+
import path from 'node:path'
|
|
3
|
+
import {
|
|
4
|
+
type SpreeDashboardPluginOptions as CoreOptions,
|
|
5
|
+
spreeDashboardPlugin as spreeDashboardCorePlugin,
|
|
6
|
+
} from '@spree/dashboard-core/vite'
|
|
7
|
+
import { TanStackRouterVite } from '@tanstack/router-plugin/vite'
|
|
8
|
+
import { index, layout, physical, rootRoute, route } from '@tanstack/virtual-file-routes'
|
|
9
|
+
import type { PluginOption } from 'vite'
|
|
10
|
+
|
|
11
|
+
export interface SpreeSellerDashboardPluginOptions extends CoreOptions {
|
|
12
|
+
/**
|
|
13
|
+
* Where the composed route tree is generated, relative to the host root.
|
|
14
|
+
* Commit this file: it regenerates on every dev start/build, and its diff
|
|
15
|
+
* on a `@spree/seller-dashboard` upgrade shows exactly which pages the
|
|
16
|
+
* upgrade added or moved.
|
|
17
|
+
*/
|
|
18
|
+
generatedRouteTree?: string
|
|
19
|
+
/**
|
|
20
|
+
* Host project root. Defaults to `process.cwd()` — correct when Vite runs
|
|
21
|
+
* from the package directory, which is how the starter operates.
|
|
22
|
+
*/
|
|
23
|
+
root?: string
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Vite integration for the seller panel.
|
|
28
|
+
*
|
|
29
|
+
* The Tailwind and plugin-discovery half comes from `@spree/dashboard-core`
|
|
30
|
+
* unchanged — neither is admin-specific. Only the route skeleton differs,
|
|
31
|
+
* because a seller's panel is scoped by seller where the operator's is scoped
|
|
32
|
+
* by store.
|
|
33
|
+
*/
|
|
34
|
+
export function spreeSellerDashboardPlugin(
|
|
35
|
+
options: SpreeSellerDashboardPluginOptions = {},
|
|
36
|
+
): PluginOption[] {
|
|
37
|
+
const hostRoot = options.root ?? process.cwd()
|
|
38
|
+
return [
|
|
39
|
+
// This shell is always scanned, whatever the host passes. Core defaults to
|
|
40
|
+
// the operator's dashboard, which a seller's host does not install — and a
|
|
41
|
+
// host naming its own shell is adding one, not replacing the panel whose
|
|
42
|
+
// components it renders. Overwriting instead of merging drops the panel's
|
|
43
|
+
// classes from the production CSS, and only in a built bundle.
|
|
44
|
+
...spreeDashboardCorePlugin({
|
|
45
|
+
...options,
|
|
46
|
+
shellPackages: ['@spree/seller-dashboard', ...(options.shellPackages ?? [])],
|
|
47
|
+
}),
|
|
48
|
+
sellerRouterPlugin(hostRoot, options),
|
|
49
|
+
]
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* The TanStack Router generator, configured with a virtual route config
|
|
54
|
+
* mirroring the panel's layout skeleton: public sign-in, then an
|
|
55
|
+
* authenticated branch whose pages all hang off a seller.
|
|
56
|
+
*/
|
|
57
|
+
function sellerRouterPlugin(hostRoot: string, options: SpreeSellerDashboardPluginOptions) {
|
|
58
|
+
const fromHost = createRequire(path.join(hostRoot, 'package.json'))
|
|
59
|
+
// Resolve the shell's routes directory from wherever the host's
|
|
60
|
+
// `@spree/seller-dashboard` lives (workspace symlink or npm install). The
|
|
61
|
+
// `.` export maps to a file in src/, so `routes/` is its sibling.
|
|
62
|
+
const shellEntry = fromHost.resolve('@spree/seller-dashboard')
|
|
63
|
+
const shellRoutesDir = path.join(path.dirname(shellEntry), 'routes')
|
|
64
|
+
|
|
65
|
+
const virtualRouteConfig = rootRoute('__root.tsx', [
|
|
66
|
+
route('/login', 'login.tsx'),
|
|
67
|
+
route('/forgot-password', 'forgot-password.tsx'),
|
|
68
|
+
route('/reset-password', 'reset-password.tsx'),
|
|
69
|
+
route('/accept-invitation/$invitationId', 'accept-invitation.$invitationId.tsx'),
|
|
70
|
+
layout('_authenticated.tsx', [
|
|
71
|
+
index('_authenticated/index.tsx'),
|
|
72
|
+
route('/$sellerId', '_authenticated/$sellerId.tsx', [
|
|
73
|
+
physical('', '_authenticated/$sellerId'),
|
|
74
|
+
]),
|
|
75
|
+
]),
|
|
76
|
+
])
|
|
77
|
+
|
|
78
|
+
return TanStackRouterVite({
|
|
79
|
+
target: 'react',
|
|
80
|
+
virtualRouteConfig,
|
|
81
|
+
routesDirectory: shellRoutesDir,
|
|
82
|
+
generatedRouteTree: path.resolve(
|
|
83
|
+
hostRoot,
|
|
84
|
+
options.generatedRouteTree ?? 'src/routeTree.gen.ts',
|
|
85
|
+
),
|
|
86
|
+
})
|
|
87
|
+
}
|