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,66 @@
import { useAuthStore } from '@/stores/auth'
const API_BASE = '/api'
interface RequestOptions {
method?: string
body?: unknown
headers?: Record<string, string>
}
/**
* Composable for making authenticated API requests.
* Automatically attaches JWT token and handles token refresh.
*/
export function useApi() {
const auth = useAuthStore()
async function request<T>(path: string, options: RequestOptions = {}): Promise<T> {
const headers: Record<string, string> = {
'Content-Type': 'application/json',
...options.headers,
}
if (auth.accessToken) {
headers['Authorization'] = `Bearer ${auth.accessToken}`
}
const response = await fetch(`${API_BASE}${path}`, {
method: options.method || 'GET',
headers,
body: options.body ? JSON.stringify(options.body) : undefined,
})
if (response.status === 401) {
// TODO: Attempt token refresh, retry, or redirect to login
auth.logout()
window.location.href = '/login'
throw new Error('Unauthorized')
}
if (!response.ok) {
const error = await response.json().catch(() => ({ message: 'Request failed' }))
throw new Error(error.message || `HTTP ${response.status}`)
}
return response.json()
}
function get<T>(path: string) {
return request<T>(path)
}
function post<T>(path: string, body?: unknown) {
return request<T>(path, { method: 'POST', body })
}
function put<T>(path: string, body?: unknown) {
return request<T>(path, { method: 'PUT', body })
}
function del<T>(path: string) {
return request<T>(path, { method: 'DELETE' })
}
return { request, get, post, put, del }
}