- 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>
94 lines
3.0 KiB
Vue
94 lines
3.0 KiB
Vue
<template>
|
|
<div class="bg-white rounded-lg shadow p-6">
|
|
<h2 class="text-xl font-semibold mb-6">Inregistrare</h2>
|
|
<form @submit.prevent="handleRegister" class="space-y-4">
|
|
<div>
|
|
<label class="block text-sm font-medium text-gray-700 mb-1">Numele service-ului</label>
|
|
<input
|
|
v-model="tenantName"
|
|
type="text"
|
|
required
|
|
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
|
placeholder="Service Auto Ionescu"
|
|
/>
|
|
</div>
|
|
<div>
|
|
<label class="block text-sm font-medium text-gray-700 mb-1">Telefon</label>
|
|
<input
|
|
v-model="telefon"
|
|
type="tel"
|
|
required
|
|
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
|
placeholder="0722 000 000"
|
|
/>
|
|
</div>
|
|
<div>
|
|
<label class="block text-sm font-medium text-gray-700 mb-1">Email</label>
|
|
<input
|
|
v-model="email"
|
|
type="email"
|
|
required
|
|
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
|
placeholder="email@exemplu.ro"
|
|
/>
|
|
</div>
|
|
<div>
|
|
<label class="block text-sm font-medium text-gray-700 mb-1">Parola</label>
|
|
<input
|
|
v-model="password"
|
|
type="password"
|
|
required
|
|
minlength="6"
|
|
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
|
|
placeholder="Minim 6 caractere"
|
|
/>
|
|
</div>
|
|
<p v-if="error" class="text-sm text-red-600">{{ error }}</p>
|
|
<button
|
|
type="submit"
|
|
:disabled="loading"
|
|
class="w-full py-2 px-4 bg-blue-600 text-white rounded-md hover:bg-blue-700 disabled:opacity-50"
|
|
>
|
|
{{ loading ? 'Se creeaza contul...' : 'Creeaza cont (trial 30 zile)' }}
|
|
</button>
|
|
</form>
|
|
<p class="mt-4 text-center text-sm text-gray-500">
|
|
Ai deja cont?
|
|
<router-link to="/login" class="text-blue-600 hover:underline">Autentificare</router-link>
|
|
</p>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { ref } from 'vue'
|
|
import { useRouter } from 'vue-router'
|
|
import { useAuthStore } from '../../stores/auth.js'
|
|
import { initDatabase } from '../../db/database.js'
|
|
import { syncEngine } from '../../db/sync.js'
|
|
|
|
const router = useRouter()
|
|
const auth = useAuthStore()
|
|
|
|
const tenantName = ref('')
|
|
const telefon = ref('')
|
|
const email = ref('')
|
|
const password = ref('')
|
|
const error = ref('')
|
|
const loading = ref(false)
|
|
|
|
async function handleRegister() {
|
|
error.value = ''
|
|
loading.value = true
|
|
try {
|
|
await auth.register(email.value, password.value, tenantName.value, telefon.value)
|
|
await initDatabase()
|
|
syncEngine.fullSync()
|
|
router.push('/dashboard')
|
|
} catch (e) {
|
|
error.value = e.message
|
|
} finally {
|
|
loading.value = false
|
|
}
|
|
}
|
|
</script>
|