refactor(admin): unify AdminTaxis into AdminDrivers - multi-shifts, vehicle_type, is_accessible; remove redundant AdminTaxis.vue + DB migration
This commit is contained in:
@ -75,11 +75,19 @@
|
||||
<p class="phone">📞 {{ taxi.phone_number }}</p>
|
||||
<div class="badges">
|
||||
<span class="badge plate">{{ taxi.license_plate }}</span>
|
||||
<span class="badge">{{ taxi.corregimiento }}</span>
|
||||
<span class="badge">{{ getShiftLabel(taxi.shift) }}</span>
|
||||
<span class="badge" v-if="taxi.corregimiento">{{ taxi.corregimiento }}</span>
|
||||
<span class="badge" v-if="taxi.vehicle_type">{{ taxi.vehicle_type }}</span>
|
||||
<span v-if="taxi.english_speaking" class="badge english">🌐 Inglés</span>
|
||||
<span v-if="taxi.is_accessible" class="badge accessible">♿ Accesible</span>
|
||||
</div>
|
||||
<div class="taxi-meta">
|
||||
<span class="shifts-badges">
|
||||
<span
|
||||
v-for="s in (taxi.shifts?.length ? taxi.shifts : (taxi.shift ? [taxi.shift] : []))"
|
||||
:key="s"
|
||||
class="shift-pill"
|
||||
>{{ getShiftLabel(s) }}</span>
|
||||
</span>
|
||||
<span class="rating">⭐ {{ taxi.rating || 5.0 }}</span>
|
||||
<span v-if="taxi.cooperative" class="cooperative">{{ taxi.cooperative }}</span>
|
||||
<span :class="taxi.is_active ? 'status-active' : 'status-inactive'">
|
||||
@ -265,6 +273,11 @@
|
||||
<input v-model="taxiForm.license_plate" type="text" placeholder="CHI-1234" required>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Tipo de Vehículo</label>
|
||||
<input v-model="taxiForm.vehicle_type" type="text" placeholder="Toyota Corolla / SUV / Van">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Zona de Servicio *</label>
|
||||
<select v-model="taxiForm.corregimiento" required>
|
||||
@ -276,41 +289,44 @@
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Horario *</label>
|
||||
<select v-model="taxiForm.shift" required>
|
||||
<option value="">Seleccionar...</option>
|
||||
<option value="dia">Día</option>
|
||||
<option value="tarde">Tarde</option>
|
||||
<option value="noche">Noche</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Cooperativa</label>
|
||||
<input v-model="taxiForm.cooperative" type="text" placeholder="Cooperativa Boquete">
|
||||
</div>
|
||||
|
||||
|
||||
<div class="form-group">
|
||||
<label>Rating (1-5)</label>
|
||||
<input v-model.number="taxiForm.rating" type="number" min="1" max="5" step="0.1" placeholder="5.0">
|
||||
</div>
|
||||
|
||||
<div class="form-group checkbox-group">
|
||||
<label>
|
||||
<input v-model="taxiForm.english_speaking" type="checkbox">
|
||||
<span>Habla Inglés</span>
|
||||
</label>
|
||||
|
||||
<!-- Horarios (múltiples) -->
|
||||
<div class="form-group full-col">
|
||||
<label>Horarios de Servicio</label>
|
||||
<div class="checkbox-group horizontal">
|
||||
<label v-for="s in ['dia', 'tarde', 'noche', 'aeropuerto']" :key="s" class="checkbox-item">
|
||||
<input type="checkbox" v-model="taxiForm.shifts" :value="s">
|
||||
<span>{{ getShiftLabel(s) }}</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group checkbox-group">
|
||||
<label>
|
||||
|
||||
<!-- Flags -->
|
||||
<div class="form-group full-col flags-row">
|
||||
<label class="checkbox-item">
|
||||
<input v-model="taxiForm.english_speaking" type="checkbox">
|
||||
<span>🌐 Habla Inglés</span>
|
||||
</label>
|
||||
<label class="checkbox-item">
|
||||
<input v-model="taxiForm.is_accessible" type="checkbox">
|
||||
<span>♿ Accesible para discapacitados</span>
|
||||
</label>
|
||||
<label class="checkbox-item">
|
||||
<input v-model="taxiForm.is_active" type="checkbox">
|
||||
<span>Activo en el directorio</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="form-group full-width">
|
||||
<div class="form-group full-col">
|
||||
<label>Foto del Conductor</label>
|
||||
<input type="file" @change="handleTaxiFileChange" accept="image/*">
|
||||
<small>Opcional - Foto para el directorio público</small>
|
||||
@ -378,11 +394,13 @@ const taxiForm = reactive({
|
||||
owner_name: '',
|
||||
phone_number: '',
|
||||
license_plate: '',
|
||||
vehicle_type: '',
|
||||
corregimiento: '',
|
||||
shift: '',
|
||||
shifts: [] as string[],
|
||||
cooperative: '',
|
||||
rating: 5.0,
|
||||
english_speaking: false,
|
||||
is_accessible: false,
|
||||
is_active: true
|
||||
})
|
||||
|
||||
@ -487,15 +505,21 @@ const handleRegisterDriver = async () => {
|
||||
function editTaxi(taxi: any) {
|
||||
editingTaxi.value = taxi
|
||||
modalMode.value = 'taxi'
|
||||
// shifts: preferimos el array `shifts`, pero si solo tiene el legacy `shift` lo convertimos
|
||||
const shiftsArr: string[] = Array.isArray(taxi.shifts) && taxi.shifts.length
|
||||
? taxi.shifts
|
||||
: (taxi.shift ? [taxi.shift] : [])
|
||||
Object.assign(taxiForm, {
|
||||
owner_name: taxi.owner_name,
|
||||
phone_number: taxi.phone_number,
|
||||
license_plate: taxi.license_plate,
|
||||
vehicle_type: taxi.vehicle_type || '',
|
||||
corregimiento: taxi.corregimiento,
|
||||
shift: taxi.shift,
|
||||
shifts: shiftsArr,
|
||||
cooperative: taxi.cooperative || '',
|
||||
rating: taxi.rating || 5.0,
|
||||
english_speaking: taxi.english_speaking || false,
|
||||
is_accessible: taxi.is_accessible || false,
|
||||
is_active: taxi.is_active
|
||||
})
|
||||
photoFile.value = null
|
||||
@ -524,7 +548,12 @@ async function saveTaxi() {
|
||||
const { data: urlData } = supabase.storage.from('uploads').getPublicUrl(filename)
|
||||
image_url = urlData.publicUrl
|
||||
}
|
||||
const payload = { ...taxiForm, image_url }
|
||||
// Guardamos shifts (array) y tambien shift (primer valor) para compatibilidad legacy
|
||||
const payload = {
|
||||
...taxiForm,
|
||||
shift: taxiForm.shifts[0] || null, // backward compat
|
||||
image_url
|
||||
}
|
||||
if (editingTaxi.value) {
|
||||
const { error: e } = await supabase.from('taxis').update(payload).eq('id', editingTaxi.value.id)
|
||||
if (e) throw e
|
||||
@ -534,8 +563,9 @@ async function saveTaxi() {
|
||||
}
|
||||
closeModal()
|
||||
Object.assign(taxiForm, {
|
||||
owner_name: '', phone_number: '', license_plate: '', corregimiento: '',
|
||||
shift: '', cooperative: '', rating: 5.0, english_speaking: false, is_active: true
|
||||
owner_name: '', phone_number: '', license_plate: '', vehicle_type: '', corregimiento: '',
|
||||
shifts: [], cooperative: '', rating: 5.0, english_speaking: false,
|
||||
is_accessible: false, is_active: true
|
||||
})
|
||||
await loadData()
|
||||
} catch (e: any) {
|
||||
@ -559,9 +589,7 @@ async function deleteTaxi(taxi: any) {
|
||||
|
||||
function getShiftLabel(shift: string) {
|
||||
const labels: Record<string, string> = {
|
||||
'dia': 'Día',
|
||||
'tarde': 'Tarde',
|
||||
'noche': 'Noche'
|
||||
'dia': 'Día', 'tarde': 'Tarde', 'noche': 'Noche', 'aeropuerto': 'Aeropuerto'
|
||||
}
|
||||
return labels[shift] || shift
|
||||
}
|
||||
@ -803,6 +831,29 @@ h1 {
|
||||
border-color: #4a90e2;
|
||||
}
|
||||
|
||||
.badge.accessible {
|
||||
background: #0ea5e9;
|
||||
color: white;
|
||||
border-color: #0ea5e9;
|
||||
}
|
||||
|
||||
/* Shift pills in taxi card */
|
||||
.shifts-badges {
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.shift-pill {
|
||||
background: #fee715;
|
||||
color: #101820;
|
||||
padding: 2px 8px;
|
||||
border-radius: 10px;
|
||||
font-size: 0.72rem;
|
||||
font-weight: 800;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.taxi-meta {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
@ -972,6 +1023,27 @@ h1 {
|
||||
grid-column: 1 / -1;
|
||||
}
|
||||
|
||||
.form-group.full-col {
|
||||
grid-column: 1 / -1;
|
||||
}
|
||||
|
||||
.checkbox-group.horizontal {
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
gap: 16px;
|
||||
background: var(--bg-secondary);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 8px;
|
||||
padding: 10px 14px;
|
||||
}
|
||||
|
||||
.flags-row {
|
||||
flex-direction: row !important;
|
||||
flex-wrap: wrap;
|
||||
gap: 20px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.form-group.checkbox-group {
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
|
||||
Reference in New Issue
Block a user