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,53 @@
import { defineStore } from 'pinia'
import { ref, computed } from 'vue'
import type { User, License } from '@/types'
export const useAuthStore = defineStore('auth', () => {
const user = ref<User | null>(null)
const license = ref<License | null>(null)
const accessToken = ref<string | null>(null)
const refreshToken = ref<string | null>(null)
const isAuthenticated = computed(() => !!accessToken.value)
const hasLicense = computed(() => !!license.value)
const isLicenseActive = computed(() => license.value?.status === 'active')
function setAuth(data: { access_token: string; refresh_token: string; user: User }) {
accessToken.value = data.access_token
refreshToken.value = data.refresh_token
user.value = data.user
}
function setLicense(data: License) {
license.value = data
}
function logout() {
user.value = null
license.value = null
accessToken.value = null
refreshToken.value = null
}
function hasModule(moduleSlug: string): boolean {
return license.value?.modules_enabled?.includes(moduleSlug) ?? false
}
return {
user,
license,
accessToken,
refreshToken,
isAuthenticated,
hasLicense,
isLicenseActive,
setAuth,
setLicense,
logout,
hasModule,
}
}, {
persist: {
pick: ['accessToken', 'refreshToken', 'user', 'license'],
},
})

View File

@@ -0,0 +1,33 @@
import { defineStore } from 'pinia'
import { ref } from 'vue'
import type { PluginEntry } from '@/types'
export const usePluginStore = defineStore('plugins', () => {
const plugins = ref<PluginEntry[]>([])
const isLoading = ref(false)
async function fetchPlugins() {
// TODO: GET /api/plugins
}
async function installPlugin(slug: string) {
// TODO: POST /api/plugins/install
}
async function reloadPlugin(pluginId: string) {
// TODO: POST /api/plugins/:id/reload
}
async function searchUmod(query: string) {
// TODO: GET /api/plugins/search?q=query
}
return {
plugins,
isLoading,
fetchPlugins,
installPlugin,
reloadPlugin,
searchUmod,
}
})

View File

@@ -0,0 +1,52 @@
import { defineStore } from 'pinia'
import { ref } from 'vue'
import type { ServerConnection, ServerConfig, ServerStats } from '@/types'
export const useServerStore = defineStore('server', () => {
const connection = ref<ServerConnection | null>(null)
const config = ref<ServerConfig | null>(null)
const stats = ref<ServerStats | null>(null)
const isLoading = ref(false)
async function fetchServerStatus() {
// TODO: Fetch from API
}
async function fetchServerConfig() {
// TODO: Fetch from API
}
async function startServer() {
// TODO: POST /api/servers/:id/start
}
async function stopServer() {
// TODO: POST /api/servers/:id/stop
}
async function restartServer() {
// TODO: POST /api/servers/:id/restart
}
async function sendCommand(command: string) {
// TODO: POST /api/servers/:id/command
}
function updateStats(newStats: ServerStats) {
stats.value = newStats
}
return {
connection,
config,
stats,
isLoading,
fetchServerStatus,
fetchServerConfig,
startServer,
stopServer,
restartServer,
sendCommand,
updateStats,
}
})

View File

@@ -0,0 +1,42 @@
import { defineStore } from 'pinia'
import { ref } from 'vue'
import type { WipeProfile, WipeSchedule, WipeHistory } from '@/types'
export const useWipeStore = defineStore('wipe', () => {
const profiles = ref<WipeProfile[]>([])
const schedules = ref<WipeSchedule[]>([])
const history = ref<WipeHistory[]>([])
const isLoading = ref(false)
async function fetchProfiles() {
// TODO: GET /api/profiles
}
async function fetchSchedules() {
// TODO: GET /api/schedules
}
async function fetchHistory() {
// TODO: GET /api/wipes/history
}
async function triggerWipe(wipeType: string, profileId: string) {
// TODO: POST /api/wipes/:server_id/trigger
}
async function triggerDryRun(wipeType: string, profileId: string) {
// TODO: POST /api/wipes/:server_id/dry-run
}
return {
profiles,
schedules,
history,
isLoading,
fetchProfiles,
fetchSchedules,
fetchHistory,
triggerWipe,
triggerDryRun,
}
})