diff --git a/frontend/src/components/admin/AdminPageHeader.vue b/frontend/src/components/admin/AdminPageHeader.vue new file mode 100644 index 0000000..13883b2 --- /dev/null +++ b/frontend/src/components/admin/AdminPageHeader.vue @@ -0,0 +1,112 @@ + + + + + diff --git a/frontend/src/views/AdminDrivers.vue b/frontend/src/views/AdminDrivers.vue index b1b24fc..fb4e109 100644 --- a/frontend/src/views/AdminDrivers.vue +++ b/frontend/src/views/AdminDrivers.vue @@ -1,168 +1,196 @@ @@ -171,20 +199,19 @@ import { computed, onMounted, reactive, ref } from 'vue' import { useRouter } from 'vue-router' import { supabase } from '@/supabase' import AppImage from '@/components/AppImage.vue' +import AdminPageHeader from '@/components/admin/AdminPageHeader.vue' +import LoadingBranded from '@/components/common/LoadingBranded.vue' const router = useRouter() const isLoading = ref(false) const taxis = ref([]) -// Modal state const showModal = ref(false) -const modalMode = ref<'driver' | 'taxi'>('taxi') const editingTaxi = ref(null) - -// Taxi form const isSaving = ref(false) const taxiError = ref('') const photoFile = ref(null) + const taxiForm = reactive({ owner_name: '', phone_number: '', @@ -199,30 +226,24 @@ const taxiForm = reactive({ is_active: true }) -const modalTitle = computed(() => { - return editingTaxi.value ? 'Editar Taxi' : 'Nuevo Taxi' -}) +const modalTitle = computed(() => editingTaxi.value ? 'Editar taxi' : 'Nuevo taxi') -onMounted(() => { - loadData() -}) +onMounted(loadData) async function loadData() { isLoading.value = true try { - // Load taxis from Supabase - const { data: taxisData, error: errorTaxis } = await supabase.from('taxis').select('*').order('owner_name') - if (errorTaxis) throw errorTaxis - taxis.value = taxisData || [] + const { data, error } = await supabase.from('taxis').select('*').order('owner_name') + if (error) throw error + taxis.value = data || [] } catch (e) { - console.error('Error cargando datos:', e) + console.error('Error cargando taxis:', e) } finally { isLoading.value = false } } function openRegisterModal() { - modalMode.value = 'taxi' showModal.value = true } @@ -231,12 +252,15 @@ function closeModal() { editingTaxi.value = null photoFile.value = null taxiError.value = '' + Object.assign(taxiForm, { + owner_name: '', phone_number: '', license_plate: '', vehicle_type: '', + corregimiento: '', shifts: [], cooperative: '', rating: 5.0, + english_speaking: false, is_accessible: false, is_active: true + }) } 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] : []) @@ -260,17 +284,14 @@ function editTaxi(taxi: any) { function handleTaxiFileChange(event: Event) { const target = event.target as HTMLInputElement - if (target.files && target.files[0]) { - photoFile.value = target.files[0] - } + if (target.files?.[0]) photoFile.value = target.files[0] } async function saveTaxi() { isSaving.value = true taxiError.value = '' - try { - let image_url = editingTaxi.value?.image_url || null + let image_url = editingTaxi.value?.image_url ?? null if (photoFile.value) { const ext = photoFile.value.name.split('.').pop() const filename = `taxis/${Date.now()}.${ext}` @@ -279,29 +300,18 @@ async function saveTaxi() { const { data: urlData } = supabase.storage.from('uploads').getPublicUrl(filename) image_url = urlData.publicUrl } - // Guardamos shifts (array) y tambien shift (primer valor) para compatibilidad legacy - const payload = { - ...taxiForm, - shift: taxiForm.shifts[0] || null, // backward compat - image_url - } + const payload = { ...taxiForm, shift: taxiForm.shifts[0] || null, image_url } if (editingTaxi.value) { - const { error: e } = await supabase.from('taxis').update(payload).eq('id', editingTaxi.value.id) - if (e) throw e + const { error } = await supabase.from('taxis').update(payload).eq('id', editingTaxi.value.id) + if (error) throw error } else { - const { error: e } = await supabase.from('taxis').insert([payload]) - if (e) throw e + const { error } = await supabase.from('taxis').insert([payload]) + if (error) throw error } closeModal() - Object.assign(taxiForm, { - 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) { - taxiError.value = e.message || 'Error al guardar el taxi' - console.error('Error saving taxi:', e) + taxiError.value = e.message || 'Error al guardar' } finally { isSaving.value = false } @@ -310,645 +320,600 @@ async function saveTaxi() { async function deleteTaxi(taxi: any) { if (!confirm(`¿Eliminar a ${taxi.owner_name} del directorio?`)) return try { - const { error: e } = await supabase.from('taxis').delete().eq('id', taxi.id) - if (e) throw e + const { error } = await supabase.from('taxis').delete().eq('id', taxi.id) + if (error) throw error await loadData() - } catch (e) { + } catch { alert('Error al eliminar el taxi') } } function getShiftLabel(shift: string) { const labels: Record = { - 'dia': 'Día', 'tarde': 'Tarde', 'noche': 'Noche', 'aeropuerto': 'Aeropuerto' + dia: 'Día', tarde: 'Tarde', noche: 'Noche', aeropuerto: 'Aeropuerto' } return labels[shift] || shift } - - - diff --git a/frontend/src/views/AdminPanel.vue b/frontend/src/views/AdminPanel.vue index 27a5691..e8e84a8 100644 --- a/frontend/src/views/AdminPanel.vue +++ b/frontend/src/views/AdminPanel.vue @@ -1,288 +1,284 @@ + + - - diff --git a/frontend/src/views/AdminReports.vue b/frontend/src/views/AdminReports.vue index bf17a13..ad42a0c 100644 --- a/frontend/src/views/AdminReports.vue +++ b/frontend/src/views/AdminReports.vue @@ -1,70 +1,80 @@ @@ -73,25 +83,24 @@ import { ref, onMounted, computed } from 'vue' import { reportsService, type Report } from '@/services/reportsService' import LoadingBranded from '@/components/common/LoadingBranded.vue' +import AdminPageHeader from '@/components/admin/AdminPageHeader.vue' const reports = ref([]) const isLoading = ref(true) -const sortedReports = computed(() => { - return [...reports.value].sort((a, b) => - new Date(b.created_at).getTime() - new Date(a.created_at).getTime() - ) -}) +const sortedReports = computed(() => + [...reports.value].sort((a, b) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime()) +) -onMounted(async () => { - await fetchReports() -}) +const pending = computed(() => reports.value.filter(r => r.status === 'pending').length) +const resolved = computed(() => reports.value.filter(r => r.status === 'resolved').length) + +onMounted(fetchReports) async function fetchReports() { isLoading.value = true try { - const data = await reportsService.getReports() - reports.value = data || [] + reports.value = (await reportsService.getReports()) || [] } catch (e) { console.error('Error fetching reports:', e) } finally { @@ -103,155 +112,205 @@ async function handleUpdateStatus(id: string, status: string) { try { await reportsService.updateReportStatus(id, status) await fetchReports() - } catch (e) { - alert('Error al actualizar el estado del reporte') + } catch { + alert('Error al actualizar el reporte') } } function formatDate(dateStr: string) { return new Date(dateStr).toLocaleString('es-ES', { - day: '2-digit', - month: 'short', - hour: '2-digit', - minute: '2-digit' + day: '2-digit', month: 'short', hour: '2-digit', minute: '2-digit' }) } function statusDisplay(status: string) { - const mapping: Record = { - 'pending': 'Pendiente', - 'resolved': 'Resuelto', - 'archived': 'Archivado' - } - return mapping[status] || status + const m: Record = { pending: 'Pendiente', resolved: 'Resuelto', archived: 'Archivado' } + return m[status] || status }