Files
roaauto/frontend/src/components/vehicles/VehiclePicker.vue
Marius Mutu ad41956ea1 feat(frontend): Dashboard + Orders UI + Vehicle Picker + Vehicles list
- Pinia stores: orders (CRUD, line management, totals recalc, stats) and vehicles (CRUD, search, marca/model cascade)
- useSync composable: auto-sync on window focus + periodic 60s interval
- VehiclePicker component: debounced autocomplete search by nr. inmatriculare or client name
- OrderLineForm component: manopera/material toggle with live total preview
- DashboardView: stats cards (orders, vehicles, revenue), recent orders list
- OrdersListView: filterable table (all/draft/validat/facturat), clickable rows
- OrderCreateView: vehicle picker + inline new vehicle form, tip deviz select, km/observatii
- OrderDetailView: order info, lines table with add/remove, totals, validate action
- VehiclesListView: searchable table, inline create form with marca/model cascade
- AppLayout: mobile hamburger menu with slide-in sidebar overlay

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-13 17:29:02 +02:00

109 lines
3.3 KiB
Vue

<template>
<div class="relative">
<label v-if="label" class="block text-sm font-medium text-gray-700 mb-1">{{ label }}</label>
<input
v-model="query"
type="text"
:placeholder="placeholder"
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
@input="onSearch"
@focus="showDropdown = true"
/>
<!-- Selected vehicle display -->
<div v-if="selected" class="mt-1 text-sm text-gray-600">
{{ selected.nr_inmatriculare }} - {{ selected.client_nume }}
<span v-if="selected.marca_denumire"> ({{ selected.marca_denounire }} {{ selected.model_denumire }})</span>
<button @click="clear" class="ml-2 text-red-500 hover:text-red-700 text-xs">Sterge</button>
</div>
<!-- Dropdown results -->
<ul
v-if="showDropdown && results.length > 0"
class="absolute z-10 mt-1 w-full bg-white border border-gray-200 rounded-md shadow-lg max-h-48 overflow-auto"
>
<li
v-for="v in results"
:key="v.id"
class="px-3 py-2 hover:bg-blue-50 cursor-pointer text-sm"
@mousedown="selectVehicle(v)"
>
<span class="font-medium">{{ v.nr_inmatriculare }}</span>
<span class="text-gray-500 ml-2">{{ v.client_nume }}</span>
<span v-if="v.marca_denumire" class="text-gray-400 ml-1">
({{ v.marca_denumire }} {{ v.model_denumire }})
</span>
</li>
</ul>
<!-- No results -->
<div
v-if="showDropdown && query.length >= 2 && results.length === 0 && !loading"
class="absolute z-10 mt-1 w-full bg-white border border-gray-200 rounded-md shadow-lg p-3 text-sm text-gray-500"
>
Niciun vehicul gasit
</div>
</div>
</template>
<script setup>
import { ref, watch } from 'vue'
import { useVehiclesStore } from '../../stores/vehicles.js'
const props = defineProps({
modelValue: { type: String, default: null },
label: { type: String, default: 'Vehicul' },
placeholder: { type: String, default: 'Cauta dupa nr. inmatriculare sau client...' },
})
const emit = defineEmits(['update:modelValue', 'select'])
const vehiclesStore = useVehiclesStore()
const query = ref('')
const results = ref([])
const selected = ref(null)
const showDropdown = ref(false)
const loading = ref(false)
let searchTimeout = null
function onSearch() {
if (searchTimeout) clearTimeout(searchTimeout)
searchTimeout = setTimeout(async () => {
if (query.value.length < 2) {
results.value = []
return
}
loading.value = true
results.value = await vehiclesStore.search(query.value)
loading.value = false
}, 200)
}
function selectVehicle(v) {
selected.value = v
query.value = v.nr_inmatriculare
showDropdown.value = false
emit('update:modelValue', v.id)
emit('select', v)
}
function clear() {
selected.value = null
query.value = ''
emit('update:modelValue', null)
emit('select', null)
}
// Load initial vehicle if modelValue is set
watch(() => props.modelValue, async (id) => {
if (id && !selected.value) {
const v = await vehiclesStore.getById(id)
if (v) {
selected.value = v
query.value = v.nr_inmatriculare
}
}
}, { immediate: true })
// Close dropdown on outside click
function onClickOutside() { showDropdown.value = false }
</script>