diff --git a/frontend/src/router/index.ts b/frontend/src/router/index.ts index 985ab31..15810f0 100644 --- a/frontend/src/router/index.ts +++ b/frontend/src/router/index.ts @@ -1,104 +1,210 @@ -import { createRouter, createWebHistory } from 'vue-router' +import { createRouter, createWebHistory } from "vue-router"; + +import { isMobileHost, toMobilePath } from "./mobileHost"; const router = createRouter({ history: createWebHistory(), routes: [ - { path: '/admin', redirect: '/admin/dashboard' }, - { 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') }, - { path: '/orders', name: 'orders', component: () => import('@/views/account/OrdersView.vue') }, - { path: '/orders/:id', name: 'order-detail', component: () => import('@/views/account/OrderDetailView.vue') }, - { path: '/wallet', name: 'wallet', component: () => import('@/views/account/WalletView.vue') }, - { path: '/notifications', name: 'notifications', component: () => import('@/views/account/NotificationsView.vue') }, - { path: '/realname', name: 'realname', component: () => import('@/views/account/RealnameView.vue') }, - { path: '/seller/listings', name: 'seller-listings', component: () => import('@/views/seller/SellerListingsView.vue') }, - { path: '/seller/listings/create', name: 'seller-listing-create', component: () => import('@/views/seller/SellerListingCreateView.vue') }, - { path: '/seller/handoffs', name: 'seller-handoffs', component: () => import('@/views/seller/SellerHandoffsView.vue') }, - { path: '/seller/earnings', name: 'seller-earnings', component: () => import('@/views/seller/SellerEarningsView.vue') }, - { path: '/admin/login', name: 'admin-login', component: () => import('@/views/admin/AdminLoginView.vue'), meta: { layout: 'blank' } }, + { path: "/admin", redirect: "/admin/dashboard" }, { - path: '/admin/dashboard', - name: 'admin-dashboard', - component: () => import('@/views/admin/AdminDashboardView.vue'), - meta: { layout: 'admin', requiresAdmin: true }, + path: "/m", + name: "mobile-home", + component: () => import("@/views/mobile/MobileHomeView.vue"), + meta: { layout: "blank" }, }, { - path: '/admin/users', - name: 'admin-users', - component: () => import('@/views/admin/AdminUsersView.vue'), - meta: { layout: 'admin', requiresAdmin: true }, + path: "/m/listings", + name: "mobile-listings", + component: () => import("@/views/mobile/MobileHomeView.vue"), + meta: { layout: "blank" }, }, { - path: '/admin/orders', - name: 'admin-orders', - component: () => import('@/views/admin/AdminOrdersView.vue'), - meta: { layout: 'admin', requiresAdmin: true }, + path: "/m/listings/:id", + name: "mobile-listing-detail", + component: () => import("@/views/public/ListingDetailView.vue"), + meta: { layout: "blank" }, }, { - path: '/admin/orders/:id', - name: 'admin-order-detail', - component: () => import('@/views/admin/AdminOrderDetailView.vue'), - meta: { layout: 'admin', requiresAdmin: true }, + path: "/m/login", + name: "mobile-login", + component: () => import("@/views/account/LoginView.vue"), + meta: { layout: "blank" }, }, { - path: '/admin/listings', - name: 'admin-listings', - component: () => import('@/views/admin/AdminListingsView.vue'), - meta: { layout: 'admin', requiresAdmin: true }, + path: "/m/orders", + name: "mobile-orders", + component: () => import("@/views/account/OrdersView.vue"), + meta: { layout: "blank" }, }, { - path: '/admin/listings/:id', - name: 'admin-listing-detail', - component: () => import('@/views/admin/AdminListingDetailView.vue'), - meta: { layout: 'admin', requiresAdmin: true }, + path: "/m/seller/listings/create", + name: "mobile-seller-listing-create", + component: () => import("@/views/seller/SellerListingCreateView.vue"), + meta: { layout: "blank" }, }, { - path: '/admin/listings/review', - name: 'admin-listing-review', - component: () => import('@/views/admin/AdminListingReviewView.vue'), - meta: { layout: 'admin', requiresAdmin: true }, + path: "/", + name: "home", + component: () => import("@/views/public/HomeView.vue"), }, { - path: '/admin/disputes', - name: 'admin-disputes', - component: () => import('@/views/admin/AdminDisputesView.vue'), - meta: { layout: 'admin', requiresAdmin: true }, + path: "/login", + name: "login", + component: () => import("@/views/account/LoginView.vue"), }, { - path: '/admin/wallet-ledger', - name: 'admin-wallet-ledger', - component: () => import('@/views/admin/AdminWalletLedgerView.vue'), - meta: { layout: 'admin', requiresAdmin: true }, + path: "/listings", + name: "listings", + component: () => import("@/views/public/ListingsView.vue"), }, { - path: '/admin/system-configs', - name: 'admin-system-configs', - component: () => import('@/views/admin/AdminSystemConfigsView.vue'), - meta: { layout: 'admin', requiresAdmin: true }, + path: "/listings/:id", + name: "listing-detail", + component: () => import("@/views/public/ListingDetailView.vue"), }, { - path: '/admin/audit-logs', - name: 'admin-audit-logs', - component: () => import('@/views/admin/AdminAuditLogsView.vue'), - meta: { layout: 'admin', requiresAdmin: true }, + path: "/orders/create", + name: "order-create", + component: () => import("@/views/account/OrderCreateView.vue"), + }, + { + path: "/orders", + name: "orders", + component: () => import("@/views/account/OrdersView.vue"), + }, + { + path: "/orders/:id", + name: "order-detail", + component: () => import("@/views/account/OrderDetailView.vue"), + }, + { + path: "/wallet", + name: "wallet", + component: () => import("@/views/account/WalletView.vue"), + }, + { + path: "/notifications", + name: "notifications", + component: () => import("@/views/account/NotificationsView.vue"), + }, + { + path: "/realname", + name: "realname", + component: () => import("@/views/account/RealnameView.vue"), + }, + { + path: "/seller/listings", + name: "seller-listings", + component: () => import("@/views/seller/SellerListingsView.vue"), + }, + { + path: "/seller/listings/create", + name: "seller-listing-create", + component: () => import("@/views/seller/SellerListingCreateView.vue"), + }, + { + path: "/seller/handoffs", + name: "seller-handoffs", + component: () => import("@/views/seller/SellerHandoffsView.vue"), + }, + { + path: "/seller/earnings", + name: "seller-earnings", + component: () => import("@/views/seller/SellerEarningsView.vue"), + }, + { + 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 }, }, ], -}) +}); router.beforeEach((to) => { - if (to.path === '/admin/login' && localStorage.getItem('admin_access_token')) { - return '/admin/dashboard' + if (isMobileHost() && to.path !== toMobilePath(to.path)) { + return { path: toMobilePath(to.path), query: to.query, hash: to.hash }; + } + + 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/mobileHost.ts b/frontend/src/router/mobileHost.ts new file mode 100644 index 0000000..83ba5b8 --- /dev/null +++ b/frontend/src/router/mobileHost.ts @@ -0,0 +1,13 @@ +export function isMobileHost(hostname = window.location.hostname) { + return hostname === 'm.localhost' || hostname.startsWith('m.') +} + +export function toMobilePath(path: string) { + if (path === '/') { + return '/m' + } + if (path.startsWith('/m') || path.startsWith('/admin')) { + return path + } + return `/m${path}` +} diff --git a/frontend/src/views/mobile/MobileHomeView.vue b/frontend/src/views/mobile/MobileHomeView.vue new file mode 100644 index 0000000..d198f3a --- /dev/null +++ b/frontend/src/views/mobile/MobileHomeView.vue @@ -0,0 +1,567 @@ + + + + +