seller-dashboard 1.0.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/README.md +24 -0
- package/app/components/features/attributes.vue +9 -0
- package/app/components/features/incentives.vue +9 -0
- package/app/components/features/integrations.vue +9 -0
- package/app/components/features/media.vue +9 -0
- package/app/components/features/products.vue +9 -0
- package/app/components/features/reviews.vue +9 -0
- package/app/components/features/settings.vue +9 -0
- package/app/components/features/spaces.vue +9 -0
- package/app/composables/_auth.ts +14 -0
- package/app/composables/registry.ts +162 -0
- package/app/composables/useSellerOrders.ts +23 -0
- package/app/composables/useSellerProducts.ts +33 -0
- package/app/composables/useSellerReviews.ts +18 -0
- package/app/composables/useSellerShipments.ts +18 -0
- package/app/composables/useSellerShops.ts +23 -0
- package/app/composables/useSellerSpaces.ts +13 -0
- package/app/middleware/seller-only.global.ts +14 -0
- package/app/pages/dashboard/index.vue +16 -0
- package/app/pages/dashboard/request.vue +18 -0
- package/app/pages/index.vue +20 -0
- package/app/pages/orders/index.vue +23 -0
- package/app/pages/products/[id].vue +53 -0
- package/app/pages/products/create.vue +37 -0
- package/app/pages/products/index.vue +23 -0
- package/app/pages/reviews/index.vue +23 -0
- package/app/pages/shipments/index.vue +23 -0
- package/app/pages/shops/index.vue +23 -0
- package/app/pages/spaces/index.vue +23 -0
- package/global.d.ts +12 -0
- package/nuxt.config.ts +11 -0
- package/package.json +20 -0
- package/runtime/register-magento.ts +16 -0
- package/tsconfig.json +21 -0
package/README.md
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Seller Dashboard Layer
|
|
2
|
+
|
|
3
|
+
This layer provides a seller/admin dashboard UI and runtime provider hooks to integrate with e-commerce adapters (for example `adapter-magento`).
|
|
4
|
+
|
|
5
|
+
Key points
|
|
6
|
+
- Register a runtime provider using `registerSellerProviderRuntime(name, provider)` exported from `app/composables/registry.ts`.
|
|
7
|
+
- The UI composables (e.g. `useSellerProducts`, `useSellerOrders`, `useSellerReviews`) call the registered provider; a memory provider is used by default for development.
|
|
8
|
+
- Routes under this layer are protected by a global middleware that only allows users with `seller` or `admin` roles.
|
|
9
|
+
|
|
10
|
+
Owner-scoped operations
|
|
11
|
+
- Composables automatically pass the current seller id (when available) to provider calls so providers can enforce ownership and return only seller-specific data. The helper `getCurrentSellerId()` is available in `app/composables/_auth.ts` and memory provider enforces owner scoping by default.
|
|
12
|
+
|
|
13
|
+
Integration example (Nuxt runtime):
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
import { registerSellerProviderRuntime } from '~~/layers/seller-dashboard/app/composables/registry'
|
|
17
|
+
import { createMagentoSellerAdapter } from 'adapter-magento'
|
|
18
|
+
|
|
19
|
+
registerSellerProviderRuntime('magento', createMagentoSellerAdapter({ /* config */ }))
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Next steps
|
|
23
|
+
- Scaffold product/order/review management pages and wire to real adapters.
|
|
24
|
+
- Add tests and CI checks.
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export function getCurrentSellerId(): string | undefined {
|
|
2
|
+
try {
|
|
3
|
+
const nuxtApp = useNuxtApp() as any
|
|
4
|
+
const authUser = nuxtApp?.$auth?.user ?? undefined
|
|
5
|
+
if (authUser?.id && (authUser.role === 'seller' || (authUser.roles && authUser.roles.includes('seller')))) return authUser.id
|
|
6
|
+
|
|
7
|
+
const stateUser = (useState as any)('authUser')?.value ?? (useState as any)('user')?.value
|
|
8
|
+
if (stateUser?.id && (stateUser.role === 'seller' || (stateUser.roles && stateUser.roles.includes('seller')))) return stateUser.id
|
|
9
|
+
|
|
10
|
+
return undefined
|
|
11
|
+
} catch (e) {
|
|
12
|
+
return undefined
|
|
13
|
+
}
|
|
14
|
+
}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
export type RequestContext = { sellerId?: string }
|
|
2
|
+
|
|
3
|
+
export type SellerDashboardProvider = {
|
|
4
|
+
// Products
|
|
5
|
+
listProducts?: (params?: any, ctx?: RequestContext) => Promise<any[]>;
|
|
6
|
+
getProduct?: (id: string, ctx?: RequestContext) => Promise<any>;
|
|
7
|
+
createProduct?: (payload: any, ctx?: RequestContext) => Promise<any>;
|
|
8
|
+
updateProduct?: (id: string, payload: any, ctx?: RequestContext) => Promise<any>;
|
|
9
|
+
deleteProduct?: (id: string, ctx?: RequestContext) => Promise<void>;
|
|
10
|
+
|
|
11
|
+
// Orders
|
|
12
|
+
listOrders?: (params?: any, ctx?: RequestContext) => Promise<any[]>;
|
|
13
|
+
getOrder?: (id: string, ctx?: RequestContext) => Promise<any>;
|
|
14
|
+
updateOrderStatus?: (id: string, status: string, ctx?: RequestContext) => Promise<any>;
|
|
15
|
+
|
|
16
|
+
// Reviews
|
|
17
|
+
listReviews?: (params?: any, ctx?: RequestContext) => Promise<any[]>;
|
|
18
|
+
moderateReview?: (id: string, action: 'approve' | 'reject' | 'hide', ctx?: RequestContext) => Promise<any>;
|
|
19
|
+
|
|
20
|
+
// Shops
|
|
21
|
+
listShops?: (params?: any, ctx?: RequestContext) => Promise<any[]>;
|
|
22
|
+
getShop?: (id: string, ctx?: RequestContext) => Promise<any>;
|
|
23
|
+
updateShop?: (id: string, payload: any, ctx?: RequestContext) => Promise<any>;
|
|
24
|
+
|
|
25
|
+
// Spaces
|
|
26
|
+
listSpaces?: (params?: any, ctx?: RequestContext) => Promise<any[]>;
|
|
27
|
+
|
|
28
|
+
// Shipments
|
|
29
|
+
listShipments?: (params?: any, ctx?: RequestContext) => Promise<any[]>;
|
|
30
|
+
updateShipment?: (id: string, payload: any, ctx?: RequestContext) => Promise<any>;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
let runtimeProvider: SellerDashboardProvider | null = null;
|
|
34
|
+
|
|
35
|
+
export function registerSellerProviderRuntime(name: string, provider: SellerDashboardProvider) {
|
|
36
|
+
// simple registration; future: support multiple named providers
|
|
37
|
+
runtimeProvider = provider;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function getSellerProvider(): SellerDashboardProvider {
|
|
41
|
+
return runtimeProvider ?? memoryProvider;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Minimal in-memory provider useful for development and tests
|
|
45
|
+
const memoryState = {
|
|
46
|
+
products: [] as any[],
|
|
47
|
+
orders: [] as any[],
|
|
48
|
+
reviews: [] as any[],
|
|
49
|
+
shops: [] as any[],
|
|
50
|
+
spaces: [] as any[],
|
|
51
|
+
shipments: [] as any[],
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
const memoryProvider: SellerDashboardProvider = {
|
|
55
|
+
// Products
|
|
56
|
+
async listProducts(params?: any, ctx?: RequestContext) {
|
|
57
|
+
const sellerId = ctx?.sellerId
|
|
58
|
+
if (sellerId) return memoryState.products.filter(p => p.ownerId === sellerId)
|
|
59
|
+
return [...memoryState.products]
|
|
60
|
+
},
|
|
61
|
+
async getProduct(id: string, ctx?: RequestContext) {
|
|
62
|
+
const sellerId = ctx?.sellerId
|
|
63
|
+
const p = memoryState.products.find(p => p.id === id) ?? null
|
|
64
|
+
if (p && sellerId && p.ownerId !== sellerId) return null
|
|
65
|
+
return p
|
|
66
|
+
},
|
|
67
|
+
async createProduct(payload: any, ctx?: RequestContext) {
|
|
68
|
+
const item = { id: String(Date.now()), ownerId: ctx?.sellerId ?? 'unknown', ...payload };
|
|
69
|
+
memoryState.products.push(item);
|
|
70
|
+
return item;
|
|
71
|
+
},
|
|
72
|
+
async updateProduct(id: string, payload: any, ctx?: RequestContext) {
|
|
73
|
+
const idx = memoryState.products.findIndex(p => p.id === id);
|
|
74
|
+
if (idx === -1) return null;
|
|
75
|
+
if (ctx?.sellerId && memoryState.products[idx].ownerId !== ctx.sellerId) return null;
|
|
76
|
+
memoryState.products[idx] = { ...memoryState.products[idx], ...payload };
|
|
77
|
+
return memoryState.products[idx];
|
|
78
|
+
},
|
|
79
|
+
async deleteProduct(id: string, ctx?: RequestContext) {
|
|
80
|
+
memoryState.products = memoryState.products.filter(p => p.id !== id || (ctx?.sellerId && p.ownerId !== ctx.sellerId));
|
|
81
|
+
},
|
|
82
|
+
|
|
83
|
+
// Orders
|
|
84
|
+
async listOrders(params?: any, ctx?: RequestContext) {
|
|
85
|
+
const sellerId = ctx?.sellerId
|
|
86
|
+
if (sellerId) return memoryState.orders.filter(o => o.sellerId === sellerId)
|
|
87
|
+
return [...memoryState.orders]
|
|
88
|
+
},
|
|
89
|
+
async getOrder(id: string, ctx?: RequestContext) {
|
|
90
|
+
const sellerId = ctx?.sellerId
|
|
91
|
+
const o = memoryState.orders.find(o => o.id === id) ?? null
|
|
92
|
+
if (o && sellerId && o.sellerId !== sellerId) return null
|
|
93
|
+
return o
|
|
94
|
+
},
|
|
95
|
+
async updateOrderStatus(id: string, status: string, ctx?: RequestContext) {
|
|
96
|
+
const o = memoryState.orders.find(o => o.id === id);
|
|
97
|
+
if (!o) return null;
|
|
98
|
+
if (ctx?.sellerId && o.sellerId !== ctx.sellerId) return null;
|
|
99
|
+
o.status = status;
|
|
100
|
+
return o;
|
|
101
|
+
},
|
|
102
|
+
|
|
103
|
+
// Reviews
|
|
104
|
+
async listReviews(params?: any, ctx?: RequestContext) {
|
|
105
|
+
const sellerId = ctx?.sellerId
|
|
106
|
+
if (sellerId) return memoryState.reviews.filter(r => r.sellerId === sellerId)
|
|
107
|
+
return [...memoryState.reviews]
|
|
108
|
+
},
|
|
109
|
+
async moderateReview(id: string, action: 'approve' | 'reject' | 'hide', ctx?: RequestContext) {
|
|
110
|
+
const r = memoryState.reviews.find(r => r.id === id);
|
|
111
|
+
if (!r) return null;
|
|
112
|
+
if (ctx?.sellerId && r.sellerId !== ctx.sellerId) return null;
|
|
113
|
+
r.moderation = action;
|
|
114
|
+
return r;
|
|
115
|
+
},
|
|
116
|
+
|
|
117
|
+
// Shops
|
|
118
|
+
async listShops(params?: any, ctx?: RequestContext) {
|
|
119
|
+
const sellerId = ctx?.sellerId
|
|
120
|
+
if (sellerId) return memoryState.shops.filter(s => s.ownerId === sellerId)
|
|
121
|
+
return [...memoryState.shops]
|
|
122
|
+
},
|
|
123
|
+
async getShop(id: string, ctx?: RequestContext) {
|
|
124
|
+
const sellerId = ctx?.sellerId
|
|
125
|
+
const s = memoryState.shops.find(s => s.id === id) ?? null
|
|
126
|
+
if (s && sellerId && s.ownerId !== sellerId) return null
|
|
127
|
+
return s
|
|
128
|
+
},
|
|
129
|
+
async updateShop(id: string, payload: any, ctx?: RequestContext) {
|
|
130
|
+
const idx = memoryState.shops.findIndex(s => s.id === id);
|
|
131
|
+
if (idx === -1) return null;
|
|
132
|
+
if (ctx?.sellerId && memoryState.shops[idx].ownerId !== ctx.sellerId) return null;
|
|
133
|
+
memoryState.shops[idx] = { ...memoryState.shops[idx], ...payload };
|
|
134
|
+
return memoryState.shops[idx];
|
|
135
|
+
},
|
|
136
|
+
|
|
137
|
+
// Spaces
|
|
138
|
+
async listSpaces(params?: any, ctx?: RequestContext) {
|
|
139
|
+
const sellerId = ctx?.sellerId
|
|
140
|
+
if (sellerId) return memoryState.spaces.filter(sp => sp.ownerId === sellerId)
|
|
141
|
+
return [...memoryState.spaces]
|
|
142
|
+
},
|
|
143
|
+
|
|
144
|
+
// Shipments
|
|
145
|
+
async listShipments(params?: any, ctx?: RequestContext) {
|
|
146
|
+
const sellerId = ctx?.sellerId
|
|
147
|
+
if (sellerId) return memoryState.shipments.filter(s => s.sellerId === sellerId)
|
|
148
|
+
return [...memoryState.shipments]
|
|
149
|
+
},
|
|
150
|
+
async updateShipment(id: string, payload: any, ctx?: RequestContext) {
|
|
151
|
+
const s = memoryState.shipments.find(s => s.id === id);
|
|
152
|
+
if (!s) return null;
|
|
153
|
+
if (ctx?.sellerId && s.sellerId !== ctx.sellerId) return null;
|
|
154
|
+
Object.assign(s, payload);
|
|
155
|
+
return s;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export default {
|
|
160
|
+
registerSellerProviderRuntime,
|
|
161
|
+
getSellerProvider,
|
|
162
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { getSellerProvider } from './registry'
|
|
2
|
+
import { getCurrentSellerId } from './_auth'
|
|
3
|
+
|
|
4
|
+
export function useSellerOrders() {
|
|
5
|
+
const provider = getSellerProvider()
|
|
6
|
+
|
|
7
|
+
async function listOrders(params?: any) {
|
|
8
|
+
const sellerId = getCurrentSellerId()
|
|
9
|
+
return provider.listOrders?.(params, { sellerId }) ?? []
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
async function getOrder(id: string) {
|
|
13
|
+
const sellerId = getCurrentSellerId()
|
|
14
|
+
return provider.getOrder?.(id, { sellerId }) ?? null
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
async function updateOrderStatus(id: string, status: string) {
|
|
18
|
+
const sellerId = getCurrentSellerId()
|
|
19
|
+
return provider.updateOrderStatus?.(id, status, { sellerId })
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return { listOrders, getOrder, updateOrderStatus }
|
|
23
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { getSellerProvider } from './registry'
|
|
2
|
+
import { getCurrentSellerId } from './_auth'
|
|
3
|
+
|
|
4
|
+
export function useSellerProducts() {
|
|
5
|
+
const provider = getSellerProvider()
|
|
6
|
+
|
|
7
|
+
async function listProducts(params?: any) {
|
|
8
|
+
const sellerId = getCurrentSellerId()
|
|
9
|
+
return provider.listProducts?.(params, { sellerId }) ?? []
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
async function getProduct(id: string) {
|
|
13
|
+
const sellerId = getCurrentSellerId()
|
|
14
|
+
return provider.getProduct?.(id, { sellerId }) ?? null
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
async function createProduct(payload: any) {
|
|
18
|
+
const sellerId = getCurrentSellerId()
|
|
19
|
+
return provider.createProduct?.(payload, { sellerId })
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
async function updateProduct(id: string, payload: any) {
|
|
23
|
+
const sellerId = getCurrentSellerId()
|
|
24
|
+
return provider.updateProduct?.(id, payload, { sellerId })
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
async function deleteProduct(id: string) {
|
|
28
|
+
const sellerId = getCurrentSellerId()
|
|
29
|
+
return provider.deleteProduct?.(id, { sellerId })
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return { listProducts, getProduct, createProduct, updateProduct, deleteProduct }
|
|
33
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { getSellerProvider } from './registry'
|
|
2
|
+
import { getCurrentSellerId } from './_auth'
|
|
3
|
+
|
|
4
|
+
export function useSellerReviews() {
|
|
5
|
+
const provider = getSellerProvider()
|
|
6
|
+
|
|
7
|
+
async function listReviews(params?: any) {
|
|
8
|
+
const sellerId = getCurrentSellerId()
|
|
9
|
+
return provider.listReviews?.(params, { sellerId }) ?? []
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
async function moderateReview(id: string, action: 'approve' | 'reject' | 'hide') {
|
|
13
|
+
const sellerId = getCurrentSellerId()
|
|
14
|
+
return provider.moderateReview?.(id, action, { sellerId })
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
return { listReviews, moderateReview }
|
|
18
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { getSellerProvider } from './registry'
|
|
2
|
+
import { getCurrentSellerId } from './_auth'
|
|
3
|
+
|
|
4
|
+
export function useSellerShipments() {
|
|
5
|
+
const provider = getSellerProvider()
|
|
6
|
+
|
|
7
|
+
async function listShipments(params?: any) {
|
|
8
|
+
const sellerId = getCurrentSellerId()
|
|
9
|
+
return provider.listShipments?.(params, { sellerId }) ?? []
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
async function updateShipment(id: string, payload: any) {
|
|
13
|
+
const sellerId = getCurrentSellerId()
|
|
14
|
+
return provider.updateShipment?.(id, payload, { sellerId })
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
return { listShipments, updateShipment }
|
|
18
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { getSellerProvider } from './registry'
|
|
2
|
+
import { getCurrentSellerId } from './_auth'
|
|
3
|
+
|
|
4
|
+
export function useSellerShops() {
|
|
5
|
+
const provider = getSellerProvider()
|
|
6
|
+
|
|
7
|
+
async function listShops(params?: any) {
|
|
8
|
+
const sellerId = getCurrentSellerId()
|
|
9
|
+
return provider.listShops?.(params, { sellerId }) ?? []
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
async function getShop(id: string) {
|
|
13
|
+
const sellerId = getCurrentSellerId()
|
|
14
|
+
return provider.getShop?.(id, { sellerId }) ?? null
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
async function updateShop(id: string, payload: any) {
|
|
18
|
+
const sellerId = getCurrentSellerId()
|
|
19
|
+
return provider.updateShop?.(id, payload, { sellerId })
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return { listShops, getShop, updateShop }
|
|
23
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { getSellerProvider } from './registry'
|
|
2
|
+
import { getCurrentSellerId } from './_auth'
|
|
3
|
+
|
|
4
|
+
export function useSellerSpaces() {
|
|
5
|
+
const provider = getSellerProvider()
|
|
6
|
+
|
|
7
|
+
async function listSpaces(params?: any) {
|
|
8
|
+
const sellerId = getCurrentSellerId()
|
|
9
|
+
return provider.listSpaces?.(params, { sellerId }) ?? []
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
return { listSpaces }
|
|
13
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export default defineNuxtRouteMiddleware((to, from) => {
|
|
2
|
+
// Try a few common places for the current user so the middleware is flexible
|
|
3
|
+
const nuxtApp = useNuxtApp()
|
|
4
|
+
const user = (nuxtApp as any).$auth?.user ?? (useState as any)('authUser')?.value ?? (useState as any)('user')?.value
|
|
5
|
+
|
|
6
|
+
if (!user) {
|
|
7
|
+
return navigateTo('/login')
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const roles: string[] = user.role ? [user.role] : user.roles ?? []
|
|
11
|
+
if (!roles.includes('seller') && !roles.includes('admin')) {
|
|
12
|
+
return navigateTo('/')
|
|
13
|
+
}
|
|
14
|
+
})
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div v-if="!user.isSeller">
|
|
3
|
+
<p>Your seller account is pending approval.</p>
|
|
4
|
+
<v-btn v-if="!user.profile?.seller_requested" @click="requestSeller">
|
|
5
|
+
Request Seller Access
|
|
6
|
+
</v-btn>
|
|
7
|
+
</div>
|
|
8
|
+
</template>
|
|
9
|
+
|
|
10
|
+
<script setup>
|
|
11
|
+
const user = useUserStore()
|
|
12
|
+
|
|
13
|
+
const requestSeller = async () => {
|
|
14
|
+
await $fetch('/api/seller/request', {
|
|
15
|
+
method: 'POST'
|
|
16
|
+
})
|
|
17
|
+
}
|
|
18
|
+
</script>
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="seller-dashboard">
|
|
3
|
+
<h1>Seller Dashboard</h1>
|
|
4
|
+
<nav>
|
|
5
|
+
<ul>
|
|
6
|
+
<li><NuxtLink to="/seller-dashboard/products">Products</NuxtLink></li>
|
|
7
|
+
<li><NuxtLink to="/seller-dashboard/orders">Orders</NuxtLink></li>
|
|
8
|
+
<li><NuxtLink to="/seller-dashboard/reviews">Reviews</NuxtLink></li>
|
|
9
|
+
<li><NuxtLink to="/seller-dashboard/shops">Shops</NuxtLink></li>
|
|
10
|
+
<li><NuxtLink to="/seller-dashboard/shipments">Shipments</NuxtLink></li>
|
|
11
|
+
</ul>
|
|
12
|
+
</nav>
|
|
13
|
+
<section>
|
|
14
|
+
<p>Use the links to manage your store. Backend adapters (e.g. adapter-magento) can register at runtime.</p>
|
|
15
|
+
</section>
|
|
16
|
+
</div>
|
|
17
|
+
</template>
|
|
18
|
+
|
|
19
|
+
<script setup>
|
|
20
|
+
</script>
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div>
|
|
3
|
+
<h2>Orders</h2>
|
|
4
|
+
<button @click="refresh">Refresh</button>
|
|
5
|
+
<ul>
|
|
6
|
+
<li v-for="o in orders" :key="o.id">Order #{{ o.id }} — {{ o.status }}</li>
|
|
7
|
+
</ul>
|
|
8
|
+
</div>
|
|
9
|
+
</template>
|
|
10
|
+
|
|
11
|
+
<script setup>
|
|
12
|
+
import { ref, onMounted } from 'vue'
|
|
13
|
+
import { useSellerOrders } from '~/layers/seller-dashboard/app/composables/useSellerOrders'
|
|
14
|
+
|
|
15
|
+
const orders = ref([])
|
|
16
|
+
const { listOrders } = useSellerOrders()
|
|
17
|
+
|
|
18
|
+
async function refresh() {
|
|
19
|
+
orders.value = await listOrders()
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
onMounted(refresh)
|
|
23
|
+
</script>
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div>
|
|
3
|
+
<h2>Edit Product</h2>
|
|
4
|
+
<div v-if="loading">Loading...</div>
|
|
5
|
+
<form v-else @submit.prevent="onSubmit">
|
|
6
|
+
<div>
|
|
7
|
+
<label>Name</label>
|
|
8
|
+
<input v-model="form.name" />
|
|
9
|
+
</div>
|
|
10
|
+
<div>
|
|
11
|
+
<label>Price</label>
|
|
12
|
+
<input v-model.number="form.price" type="number" />
|
|
13
|
+
</div>
|
|
14
|
+
<div>
|
|
15
|
+
<label>Description</label>
|
|
16
|
+
<textarea v-model="form.description" />
|
|
17
|
+
</div>
|
|
18
|
+
<button type="submit">Save</button>
|
|
19
|
+
</form>
|
|
20
|
+
</div>
|
|
21
|
+
</template>
|
|
22
|
+
|
|
23
|
+
<script setup>
|
|
24
|
+
import { reactive, ref, onMounted } from 'vue'
|
|
25
|
+
import { useRoute, useRouter } from '#imports'
|
|
26
|
+
import { useSellerProducts } from '~/layers/seller-dashboard/app/composables/useSellerProducts'
|
|
27
|
+
|
|
28
|
+
const route = useRoute()
|
|
29
|
+
const router = useRouter()
|
|
30
|
+
const { getProduct, updateProduct } = useSellerProducts()
|
|
31
|
+
const loading = ref(true)
|
|
32
|
+
const form = reactive({ name: '', price: 0, description: '' })
|
|
33
|
+
|
|
34
|
+
async function load() {
|
|
35
|
+
loading.value = true
|
|
36
|
+
const id = route.params.id as string
|
|
37
|
+
const p = await getProduct(id)
|
|
38
|
+
if (p) {
|
|
39
|
+
form.name = p.title || p.name || ''
|
|
40
|
+
form.price = p.price || 0
|
|
41
|
+
form.description = p.description || ''
|
|
42
|
+
}
|
|
43
|
+
loading.value = false
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
async function onSubmit() {
|
|
47
|
+
const id = route.params.id as string
|
|
48
|
+
await updateProduct(id, { title: form.name, price: form.price, description: form.description })
|
|
49
|
+
await router.push('/seller-dashboard/products')
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
onMounted(load)
|
|
53
|
+
</script>
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div>
|
|
3
|
+
<h2>Create Product</h2>
|
|
4
|
+
<form @submit.prevent="onSubmit">
|
|
5
|
+
<div>
|
|
6
|
+
<label>Name</label>
|
|
7
|
+
<input v-model="form.name" />
|
|
8
|
+
</div>
|
|
9
|
+
<div>
|
|
10
|
+
<label>Price</label>
|
|
11
|
+
<input v-model.number="form.price" type="number" />
|
|
12
|
+
</div>
|
|
13
|
+
<div>
|
|
14
|
+
<label>Description</label>
|
|
15
|
+
<textarea v-model="form.description" />
|
|
16
|
+
</div>
|
|
17
|
+
<button type="submit">Create</button>
|
|
18
|
+
</form>
|
|
19
|
+
</div>
|
|
20
|
+
</template>
|
|
21
|
+
|
|
22
|
+
<script setup>
|
|
23
|
+
import { reactive } from 'vue'
|
|
24
|
+
import { useRouter } from '#imports'
|
|
25
|
+
import { useSellerProducts } from '~/layers/seller-dashboard/app/composables/useSellerProducts'
|
|
26
|
+
|
|
27
|
+
const form = reactive({ name: '', price: 0, description: '' })
|
|
28
|
+
const { createProduct } = useSellerProducts()
|
|
29
|
+
const router = useRouter()
|
|
30
|
+
|
|
31
|
+
async function onSubmit() {
|
|
32
|
+
const created = await createProduct({ title: form.name, price: form.price, description: form.description })
|
|
33
|
+
if (created && created.id) {
|
|
34
|
+
await router.push(`/seller-dashboard/products/${created.id}`)
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
</script>
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div>
|
|
3
|
+
<h2>Products</h2>
|
|
4
|
+
<button @click="refresh">Refresh</button>
|
|
5
|
+
<ul>
|
|
6
|
+
<li v-for="p in products" :key="p.id">{{ p.title || p.name || p.id }}</li>
|
|
7
|
+
</ul>
|
|
8
|
+
</div>
|
|
9
|
+
</template>
|
|
10
|
+
|
|
11
|
+
<script setup>
|
|
12
|
+
import { ref, onMounted } from 'vue'
|
|
13
|
+
import { useSellerProducts } from '~/layers/seller-dashboard/app/composables/useSellerProducts'
|
|
14
|
+
|
|
15
|
+
const products = ref([])
|
|
16
|
+
const { listProducts } = useSellerProducts()
|
|
17
|
+
|
|
18
|
+
async function refresh() {
|
|
19
|
+
products.value = await listProducts()
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
onMounted(refresh)
|
|
23
|
+
</script>
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div>
|
|
3
|
+
<h2>Reviews</h2>
|
|
4
|
+
<button @click="refresh">Refresh</button>
|
|
5
|
+
<ul>
|
|
6
|
+
<li v-for="r in reviews" :key="r.id">{{ r.author || r.id }} — {{ r.rating || '' }} — {{ r.content || '' }}</li>
|
|
7
|
+
</ul>
|
|
8
|
+
</div>
|
|
9
|
+
</template>
|
|
10
|
+
|
|
11
|
+
<script setup>
|
|
12
|
+
import { ref, onMounted } from 'vue'
|
|
13
|
+
import { useSellerReviews } from '~/layers/seller-dashboard/app/composables/useSellerReviews'
|
|
14
|
+
|
|
15
|
+
const reviews = ref([])
|
|
16
|
+
const { listReviews } = useSellerReviews()
|
|
17
|
+
|
|
18
|
+
async function refresh() {
|
|
19
|
+
reviews.value = await listReviews()
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
onMounted(refresh)
|
|
23
|
+
</script>
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div>
|
|
3
|
+
<h2>Shipments</h2>
|
|
4
|
+
<button @click="refresh">Refresh</button>
|
|
5
|
+
<ul>
|
|
6
|
+
<li v-for="s in shipments" :key="s.id">Shipment #{{ s.id }} — {{ s.status }}</li>
|
|
7
|
+
</ul>
|
|
8
|
+
</div>
|
|
9
|
+
</template>
|
|
10
|
+
|
|
11
|
+
<script setup>
|
|
12
|
+
import { ref, onMounted } from 'vue'
|
|
13
|
+
import { useSellerShipments } from '~/layers/seller-dashboard/app/composables/useSellerShipments'
|
|
14
|
+
|
|
15
|
+
const shipments = ref([])
|
|
16
|
+
const { listShipments } = useSellerShipments()
|
|
17
|
+
|
|
18
|
+
async function refresh() {
|
|
19
|
+
shipments.value = await listShipments()
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
onMounted(refresh)
|
|
23
|
+
</script>
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div>
|
|
3
|
+
<h2>Shops</h2>
|
|
4
|
+
<button @click="refresh">Refresh</button>
|
|
5
|
+
<ul>
|
|
6
|
+
<li v-for="s in shops" :key="s.id">{{ s.name || s.id }}</li>
|
|
7
|
+
</ul>
|
|
8
|
+
</div>
|
|
9
|
+
</template>
|
|
10
|
+
|
|
11
|
+
<script setup>
|
|
12
|
+
import { ref, onMounted } from 'vue'
|
|
13
|
+
import { useSellerShops } from '~/layers/seller-dashboard/app/composables/useSellerShops'
|
|
14
|
+
|
|
15
|
+
const shops = ref([])
|
|
16
|
+
const { listShops } = useSellerShops()
|
|
17
|
+
|
|
18
|
+
async function refresh() {
|
|
19
|
+
shops.value = await listShops()
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
onMounted(refresh)
|
|
23
|
+
</script>
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div>
|
|
3
|
+
<h2>Spaces</h2>
|
|
4
|
+
<button @click="refresh">Refresh</button>
|
|
5
|
+
<ul>
|
|
6
|
+
<li v-for="sp in spaces" :key="sp.id">{{ sp.name || sp.id }}</li>
|
|
7
|
+
</ul>
|
|
8
|
+
</div>
|
|
9
|
+
</template>
|
|
10
|
+
|
|
11
|
+
<script setup>
|
|
12
|
+
import { ref, onMounted } from 'vue'
|
|
13
|
+
import { useSellerSpaces } from '~/layers/seller-dashboard/app/composables/useSellerSpaces'
|
|
14
|
+
|
|
15
|
+
const spaces = ref([])
|
|
16
|
+
const { listSpaces } = useSellerSpaces()
|
|
17
|
+
|
|
18
|
+
async function refresh() {
|
|
19
|
+
spaces.value = await listSpaces()
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
onMounted(refresh)
|
|
23
|
+
</script>
|
package/global.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
declare function useNuxtApp(): any
|
|
2
|
+
declare function useState<T=any>(key: string): { value: T | undefined }
|
|
3
|
+
declare function defineNuxtRouteMiddleware(fn: (to: any, from: any) => any): any
|
|
4
|
+
declare function navigateTo(path: string): any
|
|
5
|
+
declare function useRouter(): any
|
|
6
|
+
declare function useRoute(): any
|
|
7
|
+
|
|
8
|
+
declare module '*.vue' {
|
|
9
|
+
import type { DefineComponent } from 'vue'
|
|
10
|
+
const component: DefineComponent<{}, {}, any>
|
|
11
|
+
export default component
|
|
12
|
+
}
|
package/nuxt.config.ts
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "seller-dashboard",
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"description": "Seller dashboard for Meeovi platform",
|
|
5
|
+
"main": "./nuxt.config.ts",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
8
|
+
},
|
|
9
|
+
"keywords": ["seller", "dashboard", "meeovi"],
|
|
10
|
+
"author": "Meeovi",
|
|
11
|
+
"license": "MIT",
|
|
12
|
+
"type": "module",
|
|
13
|
+
"devDependencies": {
|
|
14
|
+
"nuxt": "^4.3.0"
|
|
15
|
+
},
|
|
16
|
+
"dependencies": {
|
|
17
|
+
"nuxt-charts": "^2.1.1",
|
|
18
|
+
"typescript": "^5.9.3"
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// Example runtime registration for an adapter-magento style adapter.
|
|
2
|
+
// This file demonstrates how to register a Magento-backed provider at app startup.
|
|
3
|
+
import { registerSellerProviderRuntime } from '../app/composables/registry'
|
|
4
|
+
|
|
5
|
+
// Example factory signature expected from an adapter package
|
|
6
|
+
type MagentoAdapterFactory = (opts: any) => any
|
|
7
|
+
|
|
8
|
+
export function registerMagentoAdapter(factory: MagentoAdapterFactory, opts: any) {
|
|
9
|
+
const adapter = factory(opts)
|
|
10
|
+
registerSellerProviderRuntime('magento', adapter)
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// Usage (Nuxt runtime):
|
|
14
|
+
// import { registerMagentoAdapter } from '~~/layers/seller-dashboard/runtime/register-magento'
|
|
15
|
+
// import { createMagentoSellerAdapter } from 'adapter-magento'
|
|
16
|
+
// registerMagentoAdapter(createMagentoSellerAdapter, { url: process.env.MAGENTO_URL })
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"declaration": true,
|
|
4
|
+
"emitDeclarationOnly": false,
|
|
5
|
+
"outDir": "dist",
|
|
6
|
+
"moduleResolution": "bundler",
|
|
7
|
+
"module": "ESNext",
|
|
8
|
+
"target": "ESNext",
|
|
9
|
+
"strict": true,
|
|
10
|
+
"jsx": "react-jsx",
|
|
11
|
+
"skipLibCheck": true,
|
|
12
|
+
"noEmitOnError": false
|
|
13
|
+
}
|
|
14
|
+
,
|
|
15
|
+
"include": [
|
|
16
|
+
"src/**/*",
|
|
17
|
+
"app/**/*",
|
|
18
|
+
"index.ts",
|
|
19
|
+
"package.json"
|
|
20
|
+
, "global.d.ts" ]
|
|
21
|
+
}
|