scaffold: Vue 3 frontend — router, stores, views, composables, layouts

Complete frontend skeleton: Vite + Vue 3 + TypeScript + Tailwind CSS,
Pinia stores (auth, server, wipe, plugins), authenticated API composable,
full route tree with auth guards, DashboardLayout with sidebar nav,
23 view stubs across auth/admin/public, all TypeScript interfaces.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Vantz Stockwell
2026-02-14 21:42:21 -05:00
parent 175d6f0a7b
commit e2f2f64d33
46 changed files with 3335 additions and 0 deletions

View File

@@ -0,0 +1,163 @@
import { createRouter, createWebHistory, type RouteRecordRaw } from 'vue-router'
import { useAuthStore } from '@/stores/auth'
const routes: RouteRecordRaw[] = [
// Auth routes (no layout)
{
path: '/login',
name: 'login',
component: () => import('@/views/auth/LoginView.vue'),
meta: { guest: true },
},
{
path: '/register',
name: 'register',
component: () => import('@/views/auth/RegisterView.vue'),
meta: { guest: true },
},
{
path: '/setup',
name: 'setup-wizard',
component: () => import('@/views/auth/SetupWizardView.vue'),
meta: { requiresAuth: true },
},
// Admin dashboard routes (with sidebar layout)
{
path: '/',
component: () => import('@/components/layout/DashboardLayout.vue'),
meta: { requiresAuth: true },
children: [
{
path: '',
name: 'dashboard',
component: () => import('@/views/admin/DashboardView.vue'),
},
{
path: 'server',
name: 'server',
component: () => import('@/views/admin/ServerView.vue'),
},
{
path: 'console',
name: 'console',
component: () => import('@/views/admin/ConsoleView.vue'),
},
{
path: 'players',
name: 'players',
component: () => import('@/views/admin/PlayersView.vue'),
},
{
path: 'plugins',
name: 'plugins',
component: () => import('@/views/admin/PluginsView.vue'),
},
{
path: 'wipes',
name: 'wipes',
component: () => import('@/views/admin/WipesView.vue'),
},
{
path: 'wipes/profiles',
name: 'wipe-profiles',
component: () => import('@/views/admin/WipeProfilesView.vue'),
},
{
path: 'wipes/calendar',
name: 'wipe-calendar',
component: () => import('@/views/admin/WipeCalendarView.vue'),
},
{
path: 'wipes/history',
name: 'wipe-history',
component: () => import('@/views/admin/WipeHistoryView.vue'),
},
{
path: 'maps',
name: 'maps',
component: () => import('@/views/admin/MapsView.vue'),
},
{
path: 'chat',
name: 'chat',
component: () => import('@/views/admin/ChatLogView.vue'),
},
{
path: 'analytics',
name: 'analytics',
component: () => import('@/views/admin/AnalyticsView.vue'),
},
{
path: 'notifications',
name: 'notifications',
component: () => import('@/views/admin/NotificationsView.vue'),
},
{
path: 'team',
name: 'team',
component: () => import('@/views/admin/TeamView.vue'),
},
{
path: 'store/manage',
name: 'store-manage',
component: () => import('@/views/admin/StoreManageView.vue'),
},
{
path: 'modules',
name: 'modules',
component: () => import('@/views/admin/ModuleStoreView.vue'),
},
{
path: 'settings',
name: 'settings',
component: () => import('@/views/admin/SettingsView.vue'),
},
],
},
// Public server site routes (different layout)
{
path: '/s/:subdomain',
component: () => import('@/components/layout/PublicLayout.vue'),
children: [
{
path: '',
name: 'public-server',
component: () => import('@/views/public/ServerInfoView.vue'),
},
{
path: 'store',
name: 'public-store',
component: () => import('@/views/public/StoreView.vue'),
},
],
},
// Status page
{
path: '/status',
name: 'status',
component: () => import('@/views/public/StatusPageView.vue'),
},
]
const router = createRouter({
history: createWebHistory(),
routes,
})
// Auth guard
router.beforeEach((to, _from, next) => {
const auth = useAuthStore()
if (to.meta.requiresAuth && !auth.isAuthenticated) {
next({ name: 'login', query: { redirect: to.fullPath } })
} else if (to.meta.guest && auth.isAuthenticated) {
next({ name: 'dashboard' })
} else {
next()
}
})
export default router