diff --git a/frontend/src/router/accountRoutes.ts b/frontend/src/router/accountRoutes.ts new file mode 100644 index 0000000..bf55ac0 --- /dev/null +++ b/frontend/src/router/accountRoutes.ts @@ -0,0 +1,45 @@ +import type { RouteRecordRaw } from 'vue-router' + +export const accountRoutes: RouteRecordRaw[] = [ + { + path: '/login', + name: 'login', + component: () => import('@/views/account/LoginView.vue'), + }, + { + path: '/orders/create', + name: 'order-create', + component: () => import('@/views/account/OrderCreateView.vue'), + meta: { requiresAuth: true }, + }, + { + path: '/orders', + name: 'orders', + component: () => import('@/views/account/OrdersView.vue'), + meta: { requiresAuth: true }, + }, + { + path: '/orders/:id', + name: 'order-detail', + component: () => import('@/views/account/OrderDetailView.vue'), + meta: { requiresAuth: true }, + }, + { + path: '/wallet', + name: 'wallet', + component: () => import('@/views/account/WalletView.vue'), + meta: { requiresAuth: true }, + }, + { + path: '/notifications', + name: 'notifications', + component: () => import('@/views/account/NotificationsView.vue'), + meta: { requiresAuth: true }, + }, + { + path: '/realname', + name: 'realname', + component: () => import('@/views/account/RealnameView.vue'), + meta: { requiresAuth: true }, + }, +] diff --git a/frontend/src/router/adminRoutes.ts b/frontend/src/router/adminRoutes.ts new file mode 100644 index 0000000..74eecb7 --- /dev/null +++ b/frontend/src/router/adminRoutes.ts @@ -0,0 +1,79 @@ +import type { RouteRecordRaw } from 'vue-router' + +const adminMeta = { layout: 'admin', requiresAdmin: true } + +export const adminRoutes: RouteRecordRaw[] = [ + { path: '/admin', redirect: '/admin/dashboard' }, + { + path: '/admin/login', + name: 'admin-login', + component: () => import('@/views/admin/AdminLoginView.vue'), + meta: { layout: 'blank' }, + }, + { + path: '/admin/dashboard', + name: 'admin-dashboard', + component: () => import('@/views/admin/AdminDashboardView.vue'), + meta: adminMeta, + }, + { + path: '/admin/users', + name: 'admin-users', + component: () => import('@/views/admin/AdminUsersView.vue'), + meta: adminMeta, + }, + { + path: '/admin/orders', + name: 'admin-orders', + component: () => import('@/views/admin/AdminOrdersView.vue'), + meta: adminMeta, + }, + { + path: '/admin/orders/:id', + name: 'admin-order-detail', + component: () => import('@/views/admin/AdminOrderDetailView.vue'), + meta: adminMeta, + }, + { + path: '/admin/listings', + name: 'admin-listings', + component: () => import('@/views/admin/AdminListingsView.vue'), + meta: adminMeta, + }, + { + path: '/admin/listings/:id', + name: 'admin-listing-detail', + component: () => import('@/views/admin/AdminListingDetailView.vue'), + meta: adminMeta, + }, + { + path: '/admin/listings/review', + name: 'admin-listing-review', + component: () => import('@/views/admin/AdminListingReviewView.vue'), + meta: adminMeta, + }, + { + path: '/admin/disputes', + name: 'admin-disputes', + component: () => import('@/views/admin/AdminDisputesView.vue'), + meta: adminMeta, + }, + { + path: '/admin/wallet-ledger', + name: 'admin-wallet-ledger', + component: () => import('@/views/admin/AdminWalletLedgerView.vue'), + meta: adminMeta, + }, + { + path: '/admin/system-configs', + name: 'admin-system-configs', + component: () => import('@/views/admin/AdminSystemConfigsView.vue'), + meta: adminMeta, + }, + { + path: '/admin/audit-logs', + name: 'admin-audit-logs', + component: () => import('@/views/admin/AdminAuditLogsView.vue'), + meta: adminMeta, + }, +] diff --git a/frontend/src/router/index.ts b/frontend/src/router/index.ts index 80dafb7..b71807f 100644 --- a/frontend/src/router/index.ts +++ b/frontend/src/router/index.ts @@ -1,289 +1,76 @@ -import { createRouter, createWebHistory } from "vue-router"; +import { createRouter, createWebHistory } from 'vue-router' -import { useSessionStore } from "@/stores/session"; -import { isMobileHost, toMobilePath } from "./mobileHost"; +import { useSessionStore } from '@/stores/session' +import { accountRoutes } from './accountRoutes' +import { adminRoutes } from './adminRoutes' +import { isMobileHost, toMobilePath } from './mobileHost' +import { mobileRoutes } from './mobileRoutes' +import { publicRoutes } from './publicRoutes' +import { sellerRoutes } from './sellerRoutes' const router = createRouter({ history: createWebHistory(), routes: [ - { path: "/admin", redirect: "/admin/dashboard" }, - { - path: "/m", - name: "mobile-home", - component: () => import("@/views/mobile/MobileHomeView.vue"), - meta: { layout: "blank" }, - }, - { - path: "/m/listings", - name: "mobile-listings", - component: () => import("@/views/mobile/MobileHomeView.vue"), - meta: { layout: "blank" }, - }, - { - path: "/m/listings/:id", - name: "mobile-listing-detail", - component: () => import("@/views/mobile/MobileListingDetailView.vue"), - meta: { layout: "blank" }, - }, - { - path: "/m/login", - name: "mobile-login", - component: () => import("@/views/mobile/MobileLoginView.vue"), - meta: { layout: "blank" }, - }, - { - path: "/m/register", - name: "mobile-register", - component: () => import("@/views/mobile/MobileRegisterView.vue"), - meta: { layout: "blank" }, - }, - { - path: "/m/messages", - name: "mobile-messages", - component: () => import("@/views/mobile/MobileMessagesView.vue"), - meta: { layout: "blank", requiresAuth: true }, - }, - { - path: "/m/realname", - name: "mobile-realname", - component: () => import("@/views/mobile/MobileRealnameView.vue"), - meta: { layout: "blank", requiresAuth: true }, - }, - { - path: "/m/profile", - name: "mobile-profile", - component: () => import("@/views/mobile/MobileProfileView.vue"), - meta: { layout: "blank", requiresAuth: true }, - }, - { - path: "/m/orders", - name: "mobile-orders", - component: () => import("@/views/mobile/MobileOrdersView.vue"), - meta: { layout: "blank", requiresAuth: true }, - }, - { - path: "/m/orders/:id", - name: "mobile-order-detail", - component: () => import("@/views/account/OrderDetailView.vue"), - meta: { layout: "blank", requiresAuth: true }, - }, - { - path: "/m/seller/listings/create", - name: "mobile-seller-listing-create", - component: () => - import("@/views/mobile/MobileSellerListingCreateView.vue"), - meta: { layout: "blank", requiresAuth: true, requiresRealname: true }, - }, - { - path: "/", - name: "home", - component: () => import("@/views/public/HomeView.vue"), - }, - { - path: "/login", - name: "login", - component: () => import("@/views/account/LoginView.vue"), - }, - { - path: "/listings", - name: "listings", - component: () => import("@/views/public/ListingsView.vue"), - }, - { - path: "/listings/:id", - name: "listing-detail", - component: () => import("@/views/public/ListingDetailView.vue"), - }, - { - path: "/orders/create", - name: "order-create", - component: () => import("@/views/account/OrderCreateView.vue"), - meta: { requiresAuth: true }, - }, - { - path: "/orders", - name: "orders", - component: () => import("@/views/account/OrdersView.vue"), - meta: { requiresAuth: true }, - }, - { - path: "/orders/:id", - name: "order-detail", - component: () => import("@/views/account/OrderDetailView.vue"), - meta: { requiresAuth: true }, - }, - { - path: "/wallet", - name: "wallet", - component: () => import("@/views/account/WalletView.vue"), - meta: { requiresAuth: true }, - }, - { - path: "/notifications", - name: "notifications", - component: () => import("@/views/account/NotificationsView.vue"), - meta: { requiresAuth: true }, - }, - { - path: "/realname", - name: "realname", - component: () => import("@/views/account/RealnameView.vue"), - meta: { requiresAuth: true }, - }, - { - path: "/seller/listings", - name: "seller-listings", - component: () => import("@/views/seller/SellerListingsView.vue"), - meta: { requiresAuth: true }, - }, - { - path: "/seller/listings/create", - name: "seller-listing-create", - component: () => import("@/views/seller/SellerListingCreateView.vue"), - meta: { requiresAuth: true, requiresRealname: true }, - }, - { - path: "/seller/handoffs", - name: "seller-handoffs", - component: () => import("@/views/seller/SellerHandoffsView.vue"), - meta: { requiresAuth: true }, - }, - { - path: "/seller/earnings", - name: "seller-earnings", - component: () => import("@/views/seller/SellerEarningsView.vue"), - meta: { requiresAuth: true }, - }, - { - path: "/admin/login", - name: "admin-login", - component: () => import("@/views/admin/AdminLoginView.vue"), - meta: { layout: "blank" }, - }, - { - path: "/admin/dashboard", - name: "admin-dashboard", - component: () => import("@/views/admin/AdminDashboardView.vue"), - meta: { layout: "admin", requiresAdmin: true }, - }, - { - path: "/admin/users", - name: "admin-users", - component: () => import("@/views/admin/AdminUsersView.vue"), - meta: { layout: "admin", requiresAdmin: true }, - }, - { - path: "/admin/orders", - name: "admin-orders", - component: () => import("@/views/admin/AdminOrdersView.vue"), - meta: { layout: "admin", requiresAdmin: true }, - }, - { - path: "/admin/orders/:id", - name: "admin-order-detail", - component: () => import("@/views/admin/AdminOrderDetailView.vue"), - meta: { layout: "admin", requiresAdmin: true }, - }, - { - path: "/admin/listings", - name: "admin-listings", - component: () => import("@/views/admin/AdminListingsView.vue"), - meta: { layout: "admin", requiresAdmin: true }, - }, - { - path: "/admin/listings/:id", - name: "admin-listing-detail", - component: () => import("@/views/admin/AdminListingDetailView.vue"), - meta: { layout: "admin", requiresAdmin: true }, - }, - { - path: "/admin/listings/review", - name: "admin-listing-review", - component: () => import("@/views/admin/AdminListingReviewView.vue"), - meta: { layout: "admin", requiresAdmin: true }, - }, - { - path: "/admin/disputes", - name: "admin-disputes", - component: () => import("@/views/admin/AdminDisputesView.vue"), - meta: { layout: "admin", requiresAdmin: true }, - }, - { - path: "/admin/wallet-ledger", - name: "admin-wallet-ledger", - component: () => import("@/views/admin/AdminWalletLedgerView.vue"), - meta: { layout: "admin", requiresAdmin: true }, - }, - { - path: "/admin/system-configs", - name: "admin-system-configs", - component: () => import("@/views/admin/AdminSystemConfigsView.vue"), - meta: { layout: "admin", requiresAdmin: true }, - }, - { - path: "/admin/audit-logs", - name: "admin-audit-logs", - component: () => import("@/views/admin/AdminAuditLogsView.vue"), - meta: { layout: "admin", requiresAdmin: true }, - }, + ...adminRoutes, + ...mobileRoutes, + ...publicRoutes, + ...accountRoutes, + ...sellerRoutes, ], -}); +}) router.beforeEach(async (to) => { if (isMobileHost() && to.path !== toMobilePath(to.path)) { - return { path: toMobilePath(to.path), query: to.query, hash: to.hash }; + return { path: toMobilePath(to.path), query: to.query, hash: to.hash } } if (to.meta.requiresAuth) { - const session = useSessionStore(); - const hasToken = !!localStorage.getItem("access_token"); + const session = useSessionStore() + const hasToken = !!localStorage.getItem('access_token') if (!hasToken) { - const loginPath = to.path.startsWith("/m") ? "/m/login" : "/login"; - return { path: loginPath, query: { redirect: to.fullPath } }; + const loginPath = to.path.startsWith('/m') ? '/m/login' : '/login' + return { path: loginPath, query: { redirect: to.fullPath } } } if (!session.phone) { try { - await session.loadMe(); + await session.loadMe() } catch { - const loginPath = to.path.startsWith("/m") ? "/m/login" : "/login"; - return { path: loginPath, query: { redirect: to.fullPath } }; + const loginPath = to.path.startsWith('/m') ? '/m/login' : '/login' + return { path: loginPath, query: { redirect: to.fullPath } } } } } if (to.meta.requiresRealname) { - const session = useSessionStore(); - const loginPath = to.path.startsWith("/m") ? "/m/login" : "/login"; - const realnamePath = to.path.startsWith("/m") ? "/m/realname" : "/realname"; + const session = useSessionStore() + const loginPath = to.path.startsWith('/m') ? '/m/login' : '/login' + const realnamePath = to.path.startsWith('/m') ? '/m/realname' : '/realname' - if (session.realnameStatus !== "verified") { + if (session.realnameStatus !== 'verified') { try { - await session.loadMe(); + await session.loadMe() } catch { - if (!localStorage.getItem("access_token")) { - return { path: loginPath, query: { redirect: to.fullPath } }; + if (!localStorage.getItem('access_token')) { + return { path: loginPath, query: { redirect: to.fullPath } } } } } - if (session.realnameStatus !== "verified") { - return { path: realnamePath, query: { redirect: to.fullPath } }; + if (session.realnameStatus !== 'verified') { + return { path: realnamePath, query: { redirect: to.fullPath } } } } - if ( - to.path === "/admin/login" && - localStorage.getItem("admin_access_token") - ) { - return "/admin/dashboard"; + if (to.path === '/admin/login' && localStorage.getItem('admin_access_token')) { + return '/admin/dashboard' } if (to.meta.requiresAdmin) { - const token = localStorage.getItem("admin_access_token"); + const token = localStorage.getItem('admin_access_token') if (!token) { - return "/admin/login"; + return '/admin/login' } } - return true; -}); + return true +}) -export default router; +export default router diff --git a/frontend/src/router/mobileRoutes.ts b/frontend/src/router/mobileRoutes.ts new file mode 100644 index 0000000..8eae620 --- /dev/null +++ b/frontend/src/router/mobileRoutes.ts @@ -0,0 +1,70 @@ +import type { RouteRecordRaw } from 'vue-router' + +export const mobileRoutes: RouteRecordRaw[] = [ + { + path: '/m', + name: 'mobile-home', + component: () => import('@/views/mobile/MobileHomeView.vue'), + meta: { layout: 'blank' }, + }, + { + path: '/m/listings', + name: 'mobile-listings', + component: () => import('@/views/mobile/MobileHomeView.vue'), + meta: { layout: 'blank' }, + }, + { + path: '/m/listings/:id', + name: 'mobile-listing-detail', + component: () => import('@/views/mobile/MobileListingDetailView.vue'), + meta: { layout: 'blank' }, + }, + { + path: '/m/login', + name: 'mobile-login', + component: () => import('@/views/mobile/MobileLoginView.vue'), + meta: { layout: 'blank' }, + }, + { + path: '/m/register', + name: 'mobile-register', + component: () => import('@/views/mobile/MobileRegisterView.vue'), + meta: { layout: 'blank' }, + }, + { + path: '/m/messages', + name: 'mobile-messages', + component: () => import('@/views/mobile/MobileMessagesView.vue'), + meta: { layout: 'blank', requiresAuth: true }, + }, + { + path: '/m/realname', + name: 'mobile-realname', + component: () => import('@/views/mobile/MobileRealnameView.vue'), + meta: { layout: 'blank', requiresAuth: true }, + }, + { + path: '/m/profile', + name: 'mobile-profile', + component: () => import('@/views/mobile/MobileProfileView.vue'), + meta: { layout: 'blank', requiresAuth: true }, + }, + { + path: '/m/orders', + name: 'mobile-orders', + component: () => import('@/views/mobile/MobileOrdersView.vue'), + meta: { layout: 'blank', requiresAuth: true }, + }, + { + path: '/m/orders/:id', + name: 'mobile-order-detail', + component: () => import('@/views/account/OrderDetailView.vue'), + meta: { layout: 'blank', requiresAuth: true }, + }, + { + path: '/m/seller/listings/create', + name: 'mobile-seller-listing-create', + component: () => import('@/views/mobile/MobileSellerListingCreateView.vue'), + meta: { layout: 'blank', requiresAuth: true, requiresRealname: true }, + }, +] diff --git a/frontend/src/router/publicRoutes.ts b/frontend/src/router/publicRoutes.ts new file mode 100644 index 0000000..efcbd1b --- /dev/null +++ b/frontend/src/router/publicRoutes.ts @@ -0,0 +1,19 @@ +import type { RouteRecordRaw } from 'vue-router' + +export const publicRoutes: RouteRecordRaw[] = [ + { + path: '/', + name: 'home', + component: () => import('@/views/public/HomeView.vue'), + }, + { + path: '/listings', + name: 'listings', + component: () => import('@/views/public/ListingsView.vue'), + }, + { + path: '/listings/:id', + name: 'listing-detail', + component: () => import('@/views/public/ListingDetailView.vue'), + }, +] diff --git a/frontend/src/router/sellerRoutes.ts b/frontend/src/router/sellerRoutes.ts new file mode 100644 index 0000000..5926a22 --- /dev/null +++ b/frontend/src/router/sellerRoutes.ts @@ -0,0 +1,28 @@ +import type { RouteRecordRaw } from 'vue-router' + +export const sellerRoutes: RouteRecordRaw[] = [ + { + path: '/seller/listings', + name: 'seller-listings', + component: () => import('@/views/seller/SellerListingsView.vue'), + meta: { requiresAuth: true }, + }, + { + path: '/seller/listings/create', + name: 'seller-listing-create', + component: () => import('@/views/seller/SellerListingCreateView.vue'), + meta: { requiresAuth: true, requiresRealname: true }, + }, + { + path: '/seller/handoffs', + name: 'seller-handoffs', + component: () => import('@/views/seller/SellerHandoffsView.vue'), + meta: { requiresAuth: true }, + }, + { + path: '/seller/earnings', + name: 'seller-earnings', + component: () => import('@/views/seller/SellerEarningsView.vue'), + meta: { requiresAuth: true }, + }, +]