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,43 @@
import SQLiteESMFactory from '@journeyapps/wa-sqlite/dist/wa-sqlite-async.mjs'
import { IDBBatchAtomicVFS } from '@journeyapps/wa-sqlite/src/examples/IDBBatchAtomicVFS.js'
import * as SQLite from '@journeyapps/wa-sqlite'
import { SCHEMA_SQL } from './schema.js'
let db = null
let sqlite3 = null
const tableListeners = new Map()
export async function initDatabase() {
if (db) return db
const module = await SQLiteESMFactory()
sqlite3 = SQLite.Factory(module)
const vfs = await IDBBatchAtomicVFS.create('roaauto', module)
sqlite3.vfs_register(vfs, true)
db = await sqlite3.open_v2('roaauto.db',
SQLite.SQLITE_OPEN_READWRITE | SQLite.SQLITE_OPEN_CREATE, 'roaauto')
for (const sql of SCHEMA_SQL.split(';').filter(s => s.trim())) {
await sqlite3.exec(db, sql)
}
return db
}
export function notifyTableChanged(table) {
tableListeners.get(table)?.forEach(cb => cb())
}
export function onTableChange(table, cb) {
if (!tableListeners.has(table)) tableListeners.set(table, new Set())
tableListeners.get(table).add(cb)
return () => tableListeners.get(table).delete(cb)
}
export async function execSQL(sql, params = []) {
if (!db) throw new Error('DB not initialized')
const results = []
await sqlite3.exec(db, sql, (row, cols) => {
const obj = {}
cols.forEach((c, i) => { obj[c] = row[i] })
results.push(obj)
})
return results
}