feat(frontend): Vue 3 + wa-sqlite + sync engine + auth + layouts

- package.json with Vue 3, Pinia, vue-router, wa-sqlite, Tailwind CSS 4, Vite
- wa-sqlite database layer with IDBBatchAtomicVFS (offline-first)
- Full schema mirroring backend tables (vehicles, orders, invoices, etc.)
- SyncEngine: fullSync, incrementalSync, pushQueue for offline queue
- Auth store with JWT parsing, login/register, plan tier detection
- Router with all routes and auth navigation guards
- AppLayout (sidebar desktop / bottom nav mobile) + AuthLayout
- Login/Register views connected to API contract
- SyncIndicator component (online/offline status)
- Reactive SQL query composable (useSqlQuery)
- Placeholder views for dashboard, orders, vehicles, appointments, catalog, settings

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-13 17:22:50 +02:00
parent a16d01a669
commit c3482bba8d
27 changed files with 7614 additions and 0 deletions

View File

@@ -0,0 +1,27 @@
import { createRouter, createWebHistory } from 'vue-router'
import { useAuthStore } from '../stores/auth.js'
const router = createRouter({
history: createWebHistory(),
routes: [
{ path: '/login', component: () => import('../views/auth/LoginView.vue'), meta: { layout: 'auth' } },
{ path: '/register', component: () => import('../views/auth/RegisterView.vue'), meta: { layout: 'auth' } },
{ path: '/dashboard', component: () => import('../views/dashboard/DashboardView.vue'), meta: { requiresAuth: true } },
{ path: '/orders', component: () => import('../views/orders/OrdersListView.vue'), meta: { requiresAuth: true } },
{ path: '/orders/new', component: () => import('../views/orders/OrderCreateView.vue'), meta: { requiresAuth: true } },
{ path: '/orders/:id', component: () => import('../views/orders/OrderDetailView.vue'), meta: { requiresAuth: true } },
{ path: '/vehicles', component: () => import('../views/vehicles/VehiclesListView.vue'), meta: { requiresAuth: true } },
{ path: '/appointments', component: () => import('../views/appointments/AppointmentsView.vue'), meta: { requiresAuth: true } },
{ path: '/catalog', component: () => import('../views/catalog/CatalogView.vue'), meta: { requiresAuth: true } },
{ path: '/settings', component: () => import('../views/settings/SettingsView.vue'), meta: { requiresAuth: true } },
{ path: '/p/:token', component: () => import('../views/client/DevizPublicView.vue') },
{ path: '/', redirect: '/dashboard' },
],
scrollBehavior: () => ({ top: 0 })
})
router.beforeEach((to) => {
if (to.meta.requiresAuth && !useAuthStore().isAuthenticated) return '/login'
})
export default router