sergiomarquez778 7 days ago
parent
commit
0cd425c2ce
  1. 39
      app/Exports/PrestamosExport.php
  2. 23
      app/Exports/despartamentosExport.php
  3. 9
      app/Http/Controllers/Auth/LoginController.php
  4. 127
      app/Http/Controllers/DespartamentoController.php
  5. 82
      app/Http/Controllers/MaintenanceController.php
  6. 21
      app/Http/Controllers/MarcaController.php
  7. 133
      app/Http/Controllers/PrestamoController.php
  8. 50
      app/Http/Controllers/usuariosController.php
  9. 32
      app/Models/Maintenance.php
  10. 6
      app/Models/User.php
  11. 13
      app/Models/despartamento.php
  12. 3
      app/Models/prestamo.php
  13. 23
      database/factories/DespartamentoFactory.php
  14. 32
      database/migrations/2013_04_01_183413_create_despartamentos_table.php
  15. 6
      database/migrations/2014_10_12_000000_create_users_table.php
  16. 27
      database/migrations/2025_03_07_000000_add_plate_to_tipos_veiculos.php
  17. 28
      database/migrations/2025_03_08_000000_create_maintenances_table.php
  18. 2
      database/migrations/2025_03_28_183038_add_estado_to_prestamos_table.php
  19. 28
      database/migrations/2025_04_01_191019_add_columneliminado_todespartamentos.php
  20. 28
      database/migrations/2025_04_01_191325_add_estado_to_prestamos_table.php
  21. 28
      database/migrations/2025_04_01_192918_add_fecha_aceptacion_to_prestamos_table.php
  22. 22
      database/migrations/2025_05_07_000000_add_rol_to_users_table.php
  23. 20
      database/migrations/[timestamp]_add_fecha_aceptacion_to_prestamos_table.php
  24. 347
      package-lock.json
  25. 7
      package.json
  26. 143
      resources/js/components/dashboard/JorgeDashboard.jsx
  27. 151
      resources/js/components/dashboard/NewMaintenanceForm.jsx
  28. 10
      resources/js/maintenance-dashboard.js
  29. 142
      resources/views/despartamentos.blade.php
  30. 82
      resources/views/despartamentosCrearEditar.blade.php
  31. 36
      resources/views/exports/departamentos.blade.php
  32. 36
      resources/views/exports/prestamos-pdf.blade.php
  33. 16
      resources/views/layouts/dashboard.blade.php
  34. 9
      resources/views/maintenance/dashboard.blade.php
  35. 79
      resources/views/prestamos.blade.php
  36. 141
      resources/views/prestamos/aceptar.blade.php
  37. 160
      resources/views/prestamos/historial.blade.php
  38. 7
      resources/views/usuarios.blade.php
  39. 17
      resources/views/usuariosCrearEditar.blade.php
  40. 10
      routes/api.php
  41. 76
      routes/web.php

39
app/Exports/PrestamosExport.php

@ -2,55 +2,32 @@
namespace App\Exports;
use App\Models\Prestamo;
use Maatwebsite\Excel\Concerns\FromCollection;
use Maatwebsite\Excel\Concerns\WithHeadings;
use Maatwebsite\Excel\Concerns\WithMapping;
class PrestamosExport implements FromCollection, WithHeadings, WithMapping
class PrestamosExport implements FromCollection, WithHeadings
{
protected $prestamos;
public function __construct($prestamos = null)
{
$this->prestamos = $prestamos;
}
public function collection()
{
return $this->prestamos;
return Prestamo::where('eliminado', 0)->get();
}
public function headings(): array
{
return [
'ID',
'Solicitante',
'Nombre Solicitante',
'Destino',
'Fecha Salida',
'Fecha Llegada',
'Fecha y Hora Salida',
'Fecha y Hora Llegada',
'Motivo',
'Domicilio',
'Número de Personas',
'Chofer',
'Estado',
'Fecha Actualización'
];
}
public function map($prestamo): array
{
return [
$prestamo->id,
$prestamo->nombre_solicitante,
$prestamo->destino,
$prestamo->fecha_hora_salida,
$prestamo->fecha_hora_llegada,
$prestamo->motivo,
$prestamo->domicilio,
$prestamo->numero_personas,
$prestamo->chofer ? 'Sí' : 'No',
ucfirst($prestamo->estado),
$prestamo->updated_at->format('d/m/Y H:i')
'Fecha de Creación',
'Última Actualización'
];
}
}

23
app/Exports/despartamentosExport.php

@ -1,23 +0,0 @@
<?php
namespace App\Exports;
use App\Models\Despartamento; // Asegúrate de que el modelo esté correctamente importado
use Maatwebsite\Excel\Concerns\FromCollection;
use Maatwebsite\Excel\Concerns\WithHeadings;
class DespartamentosExport implements FromCollection, WithHeadings
{
public function collection()
{
return Despartamento::where('eliminado', 0)->get(); // Obtiene todos los departamentos activos
}
public function headings(): array
{
return [
'ID',
'Departamento'
];
}
}

9
app/Http/Controllers/Auth/LoginController.php

@ -4,6 +4,7 @@ namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
use Illuminate\Http\Request;
class LoginController extends Controller
{
@ -37,4 +38,12 @@ class LoginController extends Controller
$this->middleware('guest')->except('logout');
$this->middleware('auth')->only('logout');
}
protected function authenticated(Request $request, $user)
{
if ($user->rol === 'mantenimiento') {
return redirect('/maintenance');
}
return redirect('/dashboard');
}
}

127
app/Http/Controllers/DespartamentoController.php

@ -1,127 +0,0 @@
<?php
namespace App\Http\Controllers;
use App\Models\despartamento;
use Illuminate\Http\Request;
use App\Exports\DespartamentosExport;
use Maatwebsite\Excel\Facades\Excel;
use PDF;
class DespartamentoController extends Controller
{
/**
* Display a listing of the resource.
*/
public function index(Request $request)
{
$busqueda = $request->busqueda;
if ($busqueda) {
// Busca en la columna 'nombre' de la tabla 'despartamentos'
$despartamentos = despartamento::where('nombre', 'LIKE', "%{$busqueda}%")->where('eliminado', 0)->get();
if ($despartamentos->isEmpty()) {
return redirect()->route('despartamento.index')
->with('error', 'No existe ningún departamento con el nombre "' . $busqueda . '". Por favor, inténtalo de nuevo.');
}
} else {
// Si no hay búsqueda, mostrar todos los departamentos
$despartamentos = despartamento::where('eliminado', 0)->get();
}
return view('despartamentos', ['despartamentos' => $despartamentos]);
}
/**
* Show the form for creating a new resource.
*/
public function create()
{
return view('despartamentosCrearEditar', ['despartamento' => null]); // No se necesita pasar departamentos
}
/**
* Store a newly created resource in storage.
*/
public function store(Request $request)
{
// Valida la entrada
$request->validate([
'departamento' => 'required|string|max:255|unique:despartamentos,departamento', // Asegúrate de que 'departamento' sea único
], [
'departamento.required' => 'El campo departamento es obligatorio.',
'departamento.string' => 'El campo departamento debe ser una cadena de texto.',
'departamento.max' => 'El campo departamento no puede tener más de 255 caracteres.',
'departamento.unique' => 'Ya existe un departamento con ese nombre.',
]);
// Crea un nuevo departamento
$despartamento = new despartamento();
$despartamento->departamento = $request->departamento; // Asigna el nombre ingresado por el usuario
$despartamento->eliminado = 0; // Departamento como activo por defecto
$despartamento->save();
return redirect()->route('despartamentos.index')->with('success', 'Departamento creado exitosamente.');
}
/**
* Show the form for editing the specified resource.
*/
public function edit($id)
{
$despartamento = despartamento::findOrFail($id); // Busca el departamento por ID
return view('despartamentosCrearEditar', ['despartamento' => $despartamento]); // Pasa el departamento a la vista
}
/**
* Update the specified resource in storage.
*/
public function update(Request $request, $id)
{
$request->validate([
'departamento' => 'required|string|max:255|unique:despartamentos,departamento', // Asegúrate de que 'departamento' sea único
], [
'departamento.required' => 'El campo departamento es obligatorio.',
'departamento.string' => 'El campo departamento debe ser una cadena de texto.',
'departamento.max' => 'El campo departamento no puede tener más de 255 caracteres.',
'departamento.unique' => 'Ya existe un departamento con ese nombre, por favor elige otro.',
]);
$despartamento = despartamento::findOrFail($id); // Encuentra el departamento por ID
// Verifica si el nombre del departamento ha cambiado
if ($despartamento->departamento !== $request->departamento) {
$despartamento->departamento = $request->departamento; // Actualiza el nombre del departamento
}
$despartamento->eliminado = 0; // Cambia el estado a activo si se está editando
$despartamento->save(); // Guarda los cambios
return redirect()->route('despartamentos.index')->with('success', 'Departamento actualizado correctamente.');
}
/**
* Remove the specified resource from storage.
*/
public function destroy($id)
{
$despartamento = despartamento::findOrFail($id); // Encuentra el departamento por ID
$despartamento->eliminado = 1; // Cambia el estado a inactivo
$despartamento->save(); // Guarda los cambios
return redirect()->route('despartamentos.index')->with('success', 'Departamento inactivado correctamente.');
}
public function exportExcel()
{
return Excel::download(new DespartamentosExport, 'despartamentos.xlsx');
}
public function exportPDF()
{
$departamentos = despartamento::where('eliminado', 0)->get();
$pdf = PDF::loadView('exports.departamentos', ['departamentos' => $departamentos]);
return $pdf->download('departamentos.pdf');
}
}

82
app/Http/Controllers/MaintenanceController.php

@ -0,0 +1,82 @@
<?php
namespace App\Http\Controllers;
use App\Models\Maintenance;
use App\Models\Vehicle;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
class MaintenanceController extends Controller
{
public function index()
{
return view('maintenance.dashboard');
}
public function getMaintenanceHistory()
{
$maintenances = Maintenance::with('vehicle')
->orderBy('date', 'desc')
->get();
return response()->json($maintenances);
}
public function getPendingAlerts()
{
$alerts = Maintenance::with('vehicle')
->where('status', 'Pendiente')
->where('next_maintenance_date', '<=', now()->addDays(30))
->orderBy('next_maintenance_date', 'asc')
->get();
return response()->json($alerts);
}
public function store(Request $request)
{
$validated = $request->validate([
'vehicle_id' => 'required|exists:vehicles,id',
'maintenance_type' => 'required|string',
'date' => 'required|date',
'description' => 'required|string',
'cost' => 'required|numeric|min:0',
'status' => 'required|in:Pendiente,En Proceso,Completado',
'next_maintenance_date' => 'nullable|date'
]);
$maintenance = Maintenance::create($validated);
return response()->json([
'message' => 'Mantenimiento registrado exitosamente',
'maintenance' => $maintenance
], 201);
}
public function update(Request $request, Maintenance $maintenance)
{
$validated = $request->validate([
'vehicle_id' => 'required|exists:vehicles,id',
'maintenance_type' => 'required|string',
'date' => 'required|date',
'description' => 'required|string',
'cost' => 'required|numeric|min:0',
'status' => 'required|in:Pendiente,En Proceso,Completado',
'next_maintenance_date' => 'nullable|date'
]);
$maintenance->update($validated);
return response()->json([
'message' => 'Mantenimiento actualizado exitosamente',
'maintenance' => $maintenance
]);
}
public function getVehicles()
{
$vehicles = Vehicle::where('status', 'Activo')->get();
return response()->json($vehicles);
}
}

21
app/Http/Controllers/MarcaController.php

@ -49,11 +49,6 @@ class MarcaController extends Controller
// Validación de datos
$request->validate([
'marca' => 'required|string|max:255|unique:marcas,marca', // Asegúrate de que 'marca' sea único
], [
'marca.required' => 'El campo marca es obligatorio.',
'marca.string' => 'El campo marca debe ser una cadena de texto.',
'marca.max' => 'El campo marca no puede tener más de 255 caracteres.',
'marca.unique' => 'La marca ya existe en la base de datos.',
]);
// Crea una nueva marca
@ -79,22 +74,8 @@ class MarcaController extends Controller
*/
public function update(Request $request, $id)
{
$request->validate([
'marca' => 'required|string|max:255|unique:marcas,marca', // Asegúrate de que 'marca' sea único
], [
'marca.required' => 'El campo marca es obligatorio.',
'marca.string' => 'El campo marca debe ser una cadena de texto.',
'marca.max' => 'El campo marca no puede tener más de 255 caracteres.',
'marca.unique' => 'La marca ya existe en la base de datos.',
]);
$marca = Marca::findOrFail($id); // Encuentra la marca por ID
// Verifica si el nombre de la marca ha cambiado
if ($marca->marca !== $request->marca) {
$marca->marca = $request->marca; // Actualiza el nombre de la marca
}
$marca->marca = $request->marca; // Actualiza el nombre de la marca
$marca->eliminado = 0; // Cambia el estado a activo si se está editando
$marca->save(); // Guarda los cambios

133
app/Http/Controllers/PrestamoController.php

@ -21,15 +21,18 @@ class PrestamoController extends Controller
// Busca en la columna 'nombre_solicitante' de la tabla 'prestamos'
$prestamos = Prestamo::where('nombre_solicitante', 'LIKE', "%{$busqueda}%")
->where('eliminado', 0)
->where('estado', 'pendiente')
->get();
if ($prestamos->isEmpty()) {
return redirect()->route('prestamo.index')
return redirect()->route('prestamos.index')
->with('error', 'No existe ningún préstamo con el solicitante "' . $busqueda . '". Por favor, inténtalo de nuevo.');
}
} else {
// Si no hay búsqueda, mostrar todos los préstamos
$prestamos = Prestamo::where('eliminado', 0)->get();
// Si no hay búsqueda, mostrar todos los préstamos pendientes
$prestamos = Prestamo::where('eliminado', 0)
->where('estado', 'pendiente')
->get();
}
return view('prestamos', ['prestamos' => $prestamos]);
@ -48,18 +51,31 @@ class PrestamoController extends Controller
*/
public function store(Request $request)
{
// Preparar los datos
$datos = $request->all();
$datos['chofer'] = $request->has('chofer') ? 1 : 0; // Convertir 'on' a 1, o ausencia a 0
// Validación de datos
$request->validate([
'nombre_solicitante' => 'required|string|max:255',
'destino' => 'required|string|max:255',
'fecha_hora_salida' => 'required|date',
'fecha_hora_llegada' => 'required|date',
'motivo' => 'required|string|max:255',
'domicilio' => 'required|string|max:255',
'numero_personas' => 'required|integer',
]);
$prestamo = new Prestamo($datos);
$prestamo->estado = 'pendiente'; // Estado inicial
// Crea un nuevo préstamo
$prestamo = new Prestamo();
$prestamo->nombre_solicitante = $request->nombre_solicitante;
$prestamo->destino = $request->destino;
$prestamo->fecha_hora_salida = $request->fecha_hora_salida;
$prestamo->fecha_hora_llegada = $request->fecha_hora_llegada;
$prestamo->motivo = $request->motivo;
$prestamo->domicilio = $request->domicilio;
$prestamo->numero_personas = $request->numero_personas;
$prestamo->chofer = $request->has('chofer') ? 1 : 0; // Manejo del checkbox
$prestamo->eliminado = 0; // Marca como activo por defecto
$prestamo->save();
// Aquí puedes agregar notificaciones para los administradores
return redirect()->route('prestamos.index')
->with('success', 'Préstamo solicitado correctamente. Esperando aprobación.');
return redirect()->route('prestamos.index')->with('success', 'Préstamo creado exitosamente.');
}
/**
@ -136,17 +152,23 @@ class PrestamoController extends Controller
{
$busqueda = $request->busqueda;
$prestamos = Prestamo::when($busqueda, function($query) use ($busqueda) {
return $query->where(function($q) use ($busqueda) {
$q->where('nombre_solicitante', 'LIKE', "%{$busqueda}%")
->orWhere('destino', 'LIKE', "%{$busqueda}%")
->orWhere('motivo', 'LIKE', "%{$busqueda}%");
});
})
->orderBy('created_at', 'desc')
->get();
return view('prestamos.aceptar', ['prestamos' => $prestamos]);
if ($busqueda) {
$prestamos = Prestamo::where('nombre_solicitante', 'LIKE', "%{$busqueda}%")
->where('eliminado', 0)
->where('estado', 'aceptado')
->get();
if ($prestamos->isEmpty()) {
return redirect()->route('prestamos.aceptados')
->with('error', 'No existe ningún préstamo aceptado con el solicitante "' . $busqueda . '". Por favor, inténtalo de nuevo.');
}
} else {
$prestamos = Prestamo::where('eliminado', 0)
->where('estado', 'aceptado')
->get();
}
return view('prestamos.aceptados', ['prestamos' => $prestamos]);
}
public function aceptar($id)
@ -155,10 +177,8 @@ class PrestamoController extends Controller
$prestamo->estado = 'aceptado';
$prestamo->save();
// Aquí puedes agregar notificaciones si lo deseas
return redirect()->route('prestamos.aceptados')
->with('success', 'Préstamo aceptado correctamente');
return redirect()->route('prestamos.index')
->with('success', 'Préstamo aceptado exitosamente.');
}
public function rechazar($id)
@ -167,62 +187,7 @@ class PrestamoController extends Controller
$prestamo->estado = 'rechazado';
$prestamo->save();
// Aquí puedes agregar notificaciones si lo deseas
return redirect()->route('prestamos.aceptados')
->with('success', 'Préstamo rechazado correctamente');
}
public function historial(Request $request)
{
$query = Prestamo::query()
->whereIn('estado', ['aceptado', 'rechazado']);
// Aplicar filtros de búsqueda
if ($request->busqueda) {
$query->where(function($q) use ($request) {
$q->where('nombre_solicitante', 'LIKE', "%{$request->busqueda}%")
->orWhere('destino', 'LIKE', "%{$request->busqueda}%")
->orWhere('motivo', 'LIKE', "%{$request->busqueda}%");
});
}
// Filtro por estado
if ($request->estado) {
$query->where('estado', $request->estado);
}
// Filtro por fechas
if ($request->fecha_desde) {
$query->whereDate('created_at', '>=', $request->fecha_desde);
}
if ($request->fecha_hasta) {
$query->whereDate('created_at', '<=', $request->fecha_hasta);
}
// Ordenar por fecha de actualización
$query->orderBy('updated_at', 'desc');
// Paginar resultados
$prestamos = $query->paginate(10);
return view('prestamos.historial', ['prestamos' => $prestamos]);
}
public function export($format)
{
$prestamos = Prestamo::whereIn('estado', ['aceptado', 'rechazado'])
->orderBy('updated_at', 'desc')
->get();
switch($format) {
case 'excel':
return Excel::download(new PrestamosExport($prestamos), 'historial_prestamos.xlsx');
case 'pdf':
$pdf = PDF::loadView('exports.prestamos-pdf', ['prestamos' => $prestamos]);
return $pdf->download('historial_prestamos.pdf');
default:
return redirect()->back()->with('error', 'Formato no soportado');
}
return redirect()->route('prestamos.index')
->with('success', 'Préstamo rechazado exitosamente.');
}
}

50
app/Http/Controllers/usuariosController.php

@ -8,7 +8,6 @@ use App\Exports\UsuariosExport;
use Maatwebsite\Excel\Facades\Excel;
use PDF; // Asegúrate de incluir la clase PDF
use App\Models\Puesto;
use App\Models\Despartamento;
class usuariosController extends Controller
{
@ -40,11 +39,11 @@ class usuariosController extends Controller
*/
public function create()
{
$despartamentos = Despartamento::all();
$puestos = Puesto::all();
return view('usuariosCrearEditar', ['usuario' => null, 'puestos' => $puestos, 'despartamentos'=> $despartamentos]);
return view('usuariosCrearEditar', ['usuario' => null, 'puestos' => $puestos]);
}
/**
* Store a newly created resource in storage.
*/
@ -56,19 +55,9 @@ class usuariosController extends Controller
'email' => 'required|string|email|max:255|unique:users',
'apellido' => 'required|string|max:255',
'puesto_id' => 'required|exists:puestos,id',
'departamento_id' => 'required|exists:despartamentos,id',
'carrera' => 'required|string|max:255',
'telefono' => 'required|string|max:255',
'password' => 'required|string|min:8|confirmed',
], [
'name.required' => 'El campo nombre es obligatorio.',
'email.required' => 'El campo email es obligatorio.',
'email.unique' => 'El email ya está registrado.',
'apellido.required' => 'El campo apellido es obligatorio.',
'puesto_id.required' => 'El campo puesto es obligatorio.',
'departamento_id.required' => 'El campo departamento es obligatorio.',
'telefono.required' => 'El campo teléfono es obligatorio.',
'password.required' => 'El campo contraseña es obligatorio.',
'password.confirmed' => 'Las contraseñas no coinciden.',
]);
// Si la validación pasa, crea el usuario
@ -77,7 +66,7 @@ class usuariosController extends Controller
$usuario->email = $request->email;
$usuario->apellido = $request->apellido;
$usuario->puesto_id = $request->puesto_id;
$usuario->departamento_id = $request->departamento_id;
$usuario->carrera = $request->carrera;
$usuario->telefono = $request->telefono;
$usuario->password = bcrypt($request->password);
$usuario->save();
@ -96,13 +85,15 @@ class usuariosController extends Controller
/**
* Show the form for editing the specified resource.
*/
public function edit($id)
// ... existing code ...
public function edit($id)
{
$user = User::findOrFail($id);
$puestos = Puesto::all();
$despartamentos = Despartamento::all();
return view('usuariosCrearEditar',['usuario' => $user, 'puestos' => $puestos,'despartamentos'=> $despartamentos]); // Asegúrate de que la clave sea 'usuario'
return view('usuariosCrearEditar',['usuario' => $user, 'puestos' => $puestos]); // Asegúrate de que la clave sea 'usuario'
}
// ... existing code ...
/**
* Update the specified resource in storage.
@ -112,22 +103,12 @@ class usuariosController extends Controller
// Validación de datos
$request->validate([
'name' => 'required|string|max:255',
'email' => 'required|string|email|max:255|unique:users',
'email' => 'required|string|email|max:255',
'apellido' => 'required|string|max:255',
'puesto_id' => 'required|exists:puestos,id',
'departamento_id' => 'required|exists:despartamentos,id',
//'puesto_id' => 'required|exists:puesto_id',
'carrera' => 'required|string|max:255',
'telefono' => 'required|string|max:255',
'password' => 'required|string|min:8|confirmed',
], [
'name.required' => 'El campo nombre es obligatorio.',
'email.required' => 'El campo email es obligatorio.',
'email.unique' => 'El email ya está registrado.',
'apellido.required' => 'El campo apellido es obligatorio.',
'puesto_id.required' => 'El campo puesto es obligatorio.',
'departamento_id.required' => 'El campo departamento es obligatorio.',
'telefono.required' => 'El campo teléfono es obligatorio.',
'password.required' => 'El campo contraseña es obligatorio.',
'password.confirmed' => 'Las contraseñas no coinciden.',
'password' => 'nullable|string|min:8|confirmed',
]);
// Actualizar usuario
@ -136,7 +117,7 @@ class usuariosController extends Controller
$usuario->email = $request->email;
$usuario->apellido = $request->apellido;
$usuario->puesto_id = $request->puesto_id;
$usuario->departamento_id = $request->departamento_id;
$usuario->carrera = $request->carrera;
$usuario->telefono = $request->telefono;
if ($request->filled('password')) {
@ -151,7 +132,8 @@ class usuariosController extends Controller
/**
* Remove the specified resource from storage.
*/
public function destroy($id)
public function destroy($id)
{
// Buscar el usuario por ID
$usuario = User::findOrFail($id);

32
app/Models/Maintenance.php

@ -0,0 +1,32 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Maintenance extends Model
{
use HasFactory;
protected $fillable = [
'vehicle_id',
'maintenance_type',
'date',
'description',
'cost',
'status',
'next_maintenance_date'
];
protected $casts = [
'date' => 'date',
'next_maintenance_date' => 'date',
'cost' => 'decimal:2'
];
public function vehicle()
{
return $this->belongsTo(TiposVeiculo::class, 'vehicle_id');
}
}

6
app/Models/User.php

@ -3,7 +3,6 @@
namespace App\Models;
// use Illuminate\Contracts\Auth\MustVerifyEmail;
use App\Http\Controllers\DespartamentoController;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
@ -24,7 +23,7 @@ class User extends Authenticatable
'email',
'apellido',
'puesto_id',
'departamento_id',
'carrera',
'telefono',
'password',
];
@ -52,7 +51,4 @@ class User extends Authenticatable
public function puesto():HasOne{
return $this->hasOne(Puesto::class, 'id','puesto_id');
}
public function despartamento():HasOne{
return $this->hasOne(Despartamento::class, 'id','departamento_id');
}
}

13
app/Models/despartamento.php

@ -1,13 +0,0 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class despartamento extends Model
{
use HasFactory;
protected $table = 'despartamentos';
protected $fillable = ['departamento'];
}

3
app/Models/prestamo.php

@ -19,7 +19,6 @@ protected $fillable = [
'domicilio',
'numero_personas',
'chofer',
'estado',
'eliminado'
];
}

23
database/factories/DespartamentoFactory.php

@ -1,23 +0,0 @@
<?php
namespace Database\Factories;
use Illuminate\Database\Eloquent\Factories\Factory;
/**
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\despartamento>
*/
class DespartamentoFactory extends Factory
{
/**
* Define the model's default state.
*
* @return array<string, mixed>
*/
public function definition(): array
{
return [
//
];
}
}

32
database/migrations/2013_04_01_183413_create_despartamentos_table.php

@ -1,32 +0,0 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('despartamentos', function (Blueprint $table) {
$table->id();
$table->string('departamento');
$table->timestamps();
});
DB::table('despartamentos')->insert(['departamento'=> 'fw3f']);
DB::table('despartamentos')->insert(['departamento'=> 'wfaf']);
DB::table('despartamentos')->insert(['departamento'=> 'asef']);
DB::table('despartamentos')->insert(['departamento'=> 'FE fe']);
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('despartamentos');
}
};

6
database/migrations/2014_10_12_000000_create_users_table.php

@ -18,17 +18,13 @@ return new class extends Migration
$table->timestamp('email_verified_at')->nullable();
$table->string('apellido')->nullable();
$table->unsignedBigInteger('puesto_id')->nullable();
$table->unsignedBigInteger('departamento_id')->nullable();
$table->string('carrera')->nullable();
$table->string('telefono')->nullable();
$table->string('password');
$table->rememberToken();
$table->timestamps();
$table->foreign('puesto_id')->references('id')->on('puestos');
$table->foreign('departamento_id')->references('id')->on('despartamentos');
});
DB::table('users')->insert([
'name'=> 'monse',

27
database/migrations/2025_03_07_000000_add_plate_to_tipos_veiculos.php

@ -0,0 +1,27 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up()
{
Schema::table('tipos_veiculos', function (Blueprint $table) {
if (!Schema::hasColumn('tipos_veiculos', 'plate')) {
$table->string('plate')->after('nombre')->nullable();
}
if (!Schema::hasColumn('tipos_veiculos', 'model')) {
$table->string('model')->after('plate')->nullable();
}
});
}
public function down()
{
Schema::table('tipos_veiculos', function (Blueprint $table) {
$table->dropColumn(['plate', 'model']);
});
}
};

28
database/migrations/2025_03_08_000000_create_maintenances_table.php

@ -0,0 +1,28 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up()
{
Schema::create('maintenances', function (Blueprint $table) {
$table->id();
$table->foreignId('vehicle_id')->constrained('tipos_veiculos')->onDelete('cascade');
$table->string('maintenance_type');
$table->date('date');
$table->text('description');
$table->decimal('cost', 10, 2);
$table->enum('status', ['Pendiente', 'En Proceso', 'Completado'])->default('Pendiente');
$table->date('next_maintenance_date')->nullable();
$table->timestamps();
});
}
public function down()
{
Schema::dropIfExists('maintenances');
}
};

2
database/migrations/2025_03_28_183038_add_estado_to_prestamos_table.php

@ -12,7 +12,7 @@ return new class extends Migration
public function up(): void
{
Schema::table('prestamos', function (Blueprint $table) {
$table->string('estado')->default('pendiente'); // pendiente, aceptado, rechazado
$table->enum('estado', ['pendiente', 'aceptado', 'rechazado'])->default('pendiente');
});
}

28
database/migrations/2025_04_01_191019_add_columneliminado_todespartamentos.php

@ -1,28 +0,0 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('despartamentos', function (Blueprint $table) {
$table->boolean('eliminado')->default(false);
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('despartamentos', function (Blueprint $table) {
$table->dropColumn('eliminado');
});
}
};

28
database/migrations/2025_04_01_191325_add_estado_to_prestamos_table.php

@ -1,28 +0,0 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('prestamos', function (Blueprint $table) {
$table->enum('estado', ['pendiente', 'aceptado', 'rechazado'])->default('pendiente')->after('chofer');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('prestamos', function (Blueprint $table) {
$table->dropColumn('estado');
});
}
};

28
database/migrations/2025_04_01_192918_add_fecha_aceptacion_to_prestamos_table.php

@ -1,28 +0,0 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('prestamos', function (Blueprint $table) {
//
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('prestamos', function (Blueprint $table) {
//
});
}
};

22
database/migrations/2025_05_07_000000_add_rol_to_users_table.php

@ -0,0 +1,22 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up()
{
Schema::table('users', function (Blueprint $table) {
$table->string('rol')->default('usuario')->after('email');
});
}
public function down()
{
Schema::table('users', function (Blueprint $table) {
$table->dropColumn('rol');
});
}
};

20
database/migrations/[timestamp]_add_fecha_aceptacion_to_prestamos_table.php

@ -1,20 +0,0 @@
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class AddFechaAceptacionToPrestamosTable extends Migration
{
public function up()
{
Schema::table('prestamos', function (Blueprint $table) {
$table->timestamp('fecha_aceptacion')->nullable()->after('estado');
});
}
public function down()
{
Schema::table('prestamos', function (Blueprint $table) {
$table->dropColumn('fecha_aceptacion');
});
}
}

347
package-lock.json

@ -4,15 +4,27 @@
"requires": true,
"packages": {
"": {
"dependencies": {
"react-bootstrap": "^2.10.9"
},
"devDependencies": {
"@popperjs/core": "^2.11.6",
"axios": "^1.1.2",
"bootstrap": "^5.2.3",
"axios": "^1.9.0",
"bootstrap": "^5.3.6",
"laravel-vite-plugin": "^0.7.2",
"sass": "^1.56.1",
"vite": "^4.0.0"
}
},
"node_modules/@babel/runtime": {
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.1.tgz",
"integrity": "sha512-1x3D2xEk2fRo3PAhwQwu5UubzgiVWSXTBfWpVd2Mx2AzRqJuDJCsgaDVZ7HB5iGzDW1Hl1sWN2mFyKjmR9uAog==",
"license": "MIT",
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@esbuild/android-arm": {
"version": "0.18.20",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.20.tgz",
@ -701,13 +713,120 @@
"version": "2.11.8",
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
"integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==",
"dev": true,
"license": "MIT",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/popperjs"
}
},
"node_modules/@react-aria/ssr": {
"version": "3.9.8",
"resolved": "https://registry.npmjs.org/@react-aria/ssr/-/ssr-3.9.8.tgz",
"integrity": "sha512-lQDE/c9uTfBSDOjaZUJS8xP2jCKVk4zjQeIlCH90xaLhHDgbpCdns3xvFpJJujfj3nI4Ll9K7A+ONUBDCASOuw==",
"license": "Apache-2.0",
"dependencies": {
"@swc/helpers": "^0.5.0"
},
"engines": {
"node": ">= 12"
},
"peerDependencies": {
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
}
},
"node_modules/@restart/hooks": {
"version": "0.4.16",
"resolved": "https://registry.npmjs.org/@restart/hooks/-/hooks-0.4.16.tgz",
"integrity": "sha512-f7aCv7c+nU/3mF7NWLtVVr0Ra80RqsO89hO72r+Y/nvQr5+q0UFGkocElTH6MJApvReVh6JHUFYn2cw1WdHF3w==",
"license": "MIT",
"dependencies": {
"dequal": "^2.0.3"
},
"peerDependencies": {
"react": ">=16.8.0"
}
},
"node_modules/@restart/ui": {
"version": "1.9.4",
"resolved": "https://registry.npmjs.org/@restart/ui/-/ui-1.9.4.tgz",
"integrity": "sha512-N4C7haUc3vn4LTwVUPlkJN8Ach/+yIMvRuTVIhjilNHqegY60SGLrzud6errOMNJwSnmYFnt1J0H/k8FE3A4KA==",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.26.0",
"@popperjs/core": "^2.11.8",
"@react-aria/ssr": "^3.5.0",
"@restart/hooks": "^0.5.0",
"@types/warning": "^3.0.3",
"dequal": "^2.0.3",
"dom-helpers": "^5.2.0",
"uncontrollable": "^8.0.4",
"warning": "^4.0.3"
},
"peerDependencies": {
"react": ">=16.14.0",
"react-dom": ">=16.14.0"
}
},
"node_modules/@restart/ui/node_modules/@restart/hooks": {
"version": "0.5.1",
"resolved": "https://registry.npmjs.org/@restart/hooks/-/hooks-0.5.1.tgz",
"integrity": "sha512-EMoH04NHS1pbn07iLTjIjgttuqb7qu4+/EyhAx27MHpoENcB2ZdSsLTNxmKD+WEPnZigo62Qc8zjGnNxoSE/5Q==",
"license": "MIT",
"dependencies": {
"dequal": "^2.0.3"
},
"peerDependencies": {
"react": ">=16.8.0"
}
},
"node_modules/@restart/ui/node_modules/uncontrollable": {
"version": "8.0.4",
"resolved": "https://registry.npmjs.org/uncontrollable/-/uncontrollable-8.0.4.tgz",
"integrity": "sha512-ulRWYWHvscPFc0QQXvyJjY6LIXU56f0h8pQFvhxiKk5V1fcI8gp9Ht9leVAhrVjzqMw0BgjspBINx9r6oyJUvQ==",
"license": "MIT",
"peerDependencies": {
"react": ">=16.14.0"
}
},
"node_modules/@swc/helpers": {
"version": "0.5.17",
"resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.17.tgz",
"integrity": "sha512-5IKx/Y13RsYd+sauPb2x+U/xZikHjolzfuDgTAl/Tdf3Q8rslRvC19NKDLgAJQ6wsqADk10ntlv08nPFw/gO/A==",
"license": "Apache-2.0",
"dependencies": {
"tslib": "^2.8.0"
}
},
"node_modules/@types/prop-types": {
"version": "15.7.14",
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.14.tgz",
"integrity": "sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ==",
"license": "MIT"
},
"node_modules/@types/react": {
"version": "19.1.3",
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.3.tgz",
"integrity": "sha512-dLWQ+Z0CkIvK1J8+wrDPwGxEYFA4RAyHoZPxHVGspYmFVnwGSNT24cGIhFJrtfRnWVuW8X7NO52gCXmhkVUWGQ==",
"license": "MIT",
"dependencies": {
"csstype": "^3.0.2"
}
},
"node_modules/@types/react-transition-group": {
"version": "4.4.12",
"resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.12.tgz",
"integrity": "sha512-8TV6R3h2j7a91c+1DXdJi3Syo69zzIZbz7Lg5tORM5LEJG7X/E6a1V3drRyBRZq7/utz7A+c4OgYLiLcYGHG6w==",
"license": "MIT",
"peerDependencies": {
"@types/react": "*"
}
},
"node_modules/@types/warning": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/@types/warning/-/warning-3.0.3.tgz",
"integrity": "sha512-D1XC7WK8K+zZEveUPY+cf4+kgauk8N4eHr/XIHXGlGYkHLud6hK9lYfZk1ry1TNh798cZUCgb6MqGEG8DkJt6Q==",
"license": "MIT"
},
"node_modules/asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
@ -716,9 +835,9 @@
"license": "MIT"
},
"node_modules/axios": {
"version": "1.8.1",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.8.1.tgz",
"integrity": "sha512-NN+fvwH/kV01dYUQ3PTOZns4LWtWhOFCAhQ/pHb88WQ1hNe5V/dvFwc4VJcDL11LT9xSX0QtsR8sWUuyOuOq7g==",
"version": "1.9.0",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.9.0.tgz",
"integrity": "sha512-re4CqKTJaURpzbLHtIi6XpDv20/CnpXOtjRY5/CU32L8gU8ek9UIivcfvSWvmKEngmVbrUtPpdDwWDWL7DNHvg==",
"dev": true,
"license": "MIT",
"dependencies": {
@ -728,9 +847,9 @@
}
},
"node_modules/bootstrap": {
"version": "5.3.3",
"resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.3.tgz",
"integrity": "sha512-8HLCdWgyoMguSO9o+aH+iuZ+aht+mzW0u3HIMzVu7Srrpv7EBBxTnrFlSCskwdY1+EOFQSm7uMJhNQHkdPcmjg==",
"version": "5.3.6",
"resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.6.tgz",
"integrity": "sha512-jX0GAcRzvdwISuvArXn3m7KZscWWFAf1MKBcnzaN02qWMb3jpMoUX4/qgeiGzqyIb4ojulRzs89UCUmGcFSzTA==",
"dev": true,
"funding": [
{
@ -791,6 +910,12 @@
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/classnames": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz",
"integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==",
"license": "MIT"
},
"node_modules/combined-stream": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
@ -804,6 +929,12 @@
"node": ">= 0.8"
}
},
"node_modules/csstype": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
"license": "MIT"
},
"node_modules/delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
@ -814,6 +945,15 @@
"node": ">=0.4.0"
}
},
"node_modules/dequal": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz",
"integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==",
"license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/detect-libc": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz",
@ -828,6 +968,16 @@
"node": ">=0.10"
}
},
"node_modules/dom-helpers": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz",
"integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.8.7",
"csstype": "^3.0.2"
}
},
"node_modules/dunder-proto": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
@ -1107,6 +1257,15 @@
"dev": true,
"license": "MIT"
},
"node_modules/invariant": {
"version": "2.2.4",
"resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
"integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==",
"license": "MIT",
"dependencies": {
"loose-envify": "^1.0.0"
}
},
"node_modules/is-extglob": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
@ -1143,6 +1302,12 @@
"node": ">=0.12.0"
}
},
"node_modules/js-tokens": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
"license": "MIT"
},
"node_modules/laravel-vite-plugin": {
"version": "0.7.8",
"resolved": "https://registry.npmjs.org/laravel-vite-plugin/-/laravel-vite-plugin-0.7.8.tgz",
@ -1160,6 +1325,18 @@
"vite": "^3.0.0 || ^4.0.0"
}
},
"node_modules/loose-envify": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
"integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
"license": "MIT",
"dependencies": {
"js-tokens": "^3.0.0 || ^4.0.0"
},
"bin": {
"loose-envify": "cli.js"
}
},
"node_modules/math-intrinsics": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
@ -1235,6 +1412,15 @@
"license": "MIT",
"optional": true
},
"node_modules/object-assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/picocolors": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
@ -1284,6 +1470,30 @@
"node": "^10 || ^12 || >=14"
}
},
"node_modules/prop-types": {
"version": "15.8.1",
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
"integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
"license": "MIT",
"dependencies": {
"loose-envify": "^1.4.0",
"object-assign": "^4.1.1",
"react-is": "^16.13.1"
}
},
"node_modules/prop-types-extra": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/prop-types-extra/-/prop-types-extra-1.1.1.tgz",
"integrity": "sha512-59+AHNnHYCdiC+vMwY52WmvP5dM3QLeoumYuEyceQDi9aEhtwN9zIQ2ZNo25sMyXnbh32h+P1ezDsUpUH3JAew==",
"license": "MIT",
"dependencies": {
"react-is": "^16.3.2",
"warning": "^4.0.0"
},
"peerDependencies": {
"react": ">=0.14.0"
}
},
"node_modules/proxy-from-env": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
@ -1291,6 +1501,88 @@
"dev": true,
"license": "MIT"
},
"node_modules/react": {
"version": "19.1.0",
"resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz",
"integrity": "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==",
"license": "MIT",
"peer": true,
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/react-bootstrap": {
"version": "2.10.9",
"resolved": "https://registry.npmjs.org/react-bootstrap/-/react-bootstrap-2.10.9.tgz",
"integrity": "sha512-TJUCuHcxdgYpOqeWmRApM/Dy0+hVsxNRFvq2aRFQuxhNi/+ivOxC5OdWIeHS3agxvzJ4Ev4nDw2ZdBl9ymd/JQ==",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.24.7",
"@restart/hooks": "^0.4.9",
"@restart/ui": "^1.9.4",
"@types/prop-types": "^15.7.12",
"@types/react-transition-group": "^4.4.6",
"classnames": "^2.3.2",
"dom-helpers": "^5.2.1",
"invariant": "^2.2.4",
"prop-types": "^15.8.1",
"prop-types-extra": "^1.1.0",
"react-transition-group": "^4.4.5",
"uncontrollable": "^7.2.1",
"warning": "^4.0.3"
},
"peerDependencies": {
"@types/react": ">=16.14.8",
"react": ">=16.14.0",
"react-dom": ">=16.14.0"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/react-dom": {
"version": "19.1.0",
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.0.tgz",
"integrity": "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==",
"license": "MIT",
"peer": true,
"dependencies": {
"scheduler": "^0.26.0"
},
"peerDependencies": {
"react": "^19.1.0"
}
},
"node_modules/react-is": {
"version": "16.13.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
"license": "MIT"
},
"node_modules/react-lifecycles-compat": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz",
"integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==",
"license": "MIT"
},
"node_modules/react-transition-group": {
"version": "4.4.5",
"resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz",
"integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==",
"license": "BSD-3-Clause",
"dependencies": {
"@babel/runtime": "^7.5.5",
"dom-helpers": "^5.0.1",
"loose-envify": "^1.4.0",
"prop-types": "^15.6.2"
},
"peerDependencies": {
"react": ">=16.6.0",
"react-dom": ">=16.6.0"
}
},
"node_modules/readdirp": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz",
@ -1343,6 +1635,13 @@
"@parcel/watcher": "^2.4.1"
}
},
"node_modules/scheduler": {
"version": "0.26.0",
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz",
"integrity": "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==",
"license": "MIT",
"peer": true
},
"node_modules/source-map-js": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
@ -1367,6 +1666,27 @@
"node": ">=8.0"
}
},
"node_modules/tslib": {
"version": "2.8.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
"license": "0BSD"
},
"node_modules/uncontrollable": {
"version": "7.2.1",
"resolved": "https://registry.npmjs.org/uncontrollable/-/uncontrollable-7.2.1.tgz",
"integrity": "sha512-svtcfoTADIB0nT9nltgjujTi7BzVmwjZClOmskKu/E8FW9BXzg9os8OLr4f8Dlnk0rYWJIWr4wv9eKUXiQvQwQ==",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.6.3",
"@types/react": ">=16.9.11",
"invariant": "^2.2.4",
"react-lifecycles-compat": "^3.0.4"
},
"peerDependencies": {
"react": ">=15.0.0"
}
},
"node_modules/vite": {
"version": "4.5.9",
"resolved": "https://registry.npmjs.org/vite/-/vite-4.5.9.tgz",
@ -1433,6 +1753,15 @@
"picocolors": "^1.0.0",
"picomatch": "^2.3.1"
}
},
"node_modules/warning": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz",
"integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==",
"license": "MIT",
"dependencies": {
"loose-envify": "^1.0.0"
}
}
}
}

7
package.json

@ -6,10 +6,13 @@
},
"devDependencies": {
"@popperjs/core": "^2.11.6",
"axios": "^1.1.2",
"bootstrap": "^5.2.3",
"axios": "^1.9.0",
"bootstrap": "^5.3.6",
"laravel-vite-plugin": "^0.7.2",
"sass": "^1.56.1",
"vite": "^4.0.0"
},
"dependencies": {
"react-bootstrap": "^2.10.9"
}
}

143
resources/js/components/dashboard/JorgeDashboard.jsx

@ -0,0 +1,143 @@
import React, { useState, useEffect } from 'react';
import { Card, Row, Col, Button, Table, Alert } from 'react-bootstrap';
import NewMaintenanceForm from './NewMaintenanceForm';
import axios from 'axios';
const JorgeDashboard = () => {
const [maintenanceHistory, setMaintenanceHistory] = useState([]);
const [pendingAlerts, setPendingAlerts] = useState([]);
const [showNewMaintenanceForm, setShowNewMaintenanceForm] = useState(false);
const [vehicles, setVehicles] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
fetchData();
}, []);
const fetchData = async () => {
try {
setLoading(true);
const [historyRes, alertsRes, vehiclesRes] = await Promise.all([
axios.get('/api/maintenance/history'),
axios.get('/api/maintenance/alerts'),
axios.get('/api/maintenance/vehicles')
]);
setMaintenanceHistory(historyRes.data);
setPendingAlerts(alertsRes.data);
setVehicles(vehiclesRes.data);
setError(null);
} catch (err) {
setError('Error al cargar los datos. Por favor, intente nuevamente.');
console.error('Error fetching data:', err);
} finally {
setLoading(false);
}
};
const handleNewMaintenance = async (formData) => {
try {
const response = await axios.post('/api/maintenance', formData);
setMaintenanceHistory(prev => [response.data.maintenance, ...prev]);
setShowNewMaintenanceForm(false);
} catch (err) {
setError('Error al guardar el mantenimiento. Por favor, intente nuevamente.');
console.error('Error saving maintenance:', err);
}
};
if (loading) {
return <div className="text-center p-5">Cargando...</div>;
}
if (error) {
return <Alert variant="danger">{error}</Alert>;
}
return (
<div className="container-fluid">
<h2 className="mb-4">Panel de Control - Mantenimiento de Vehículos</h2>
{/* Alertas Pendientes */}
<Row className="mb-4">
<Col>
<Card>
<Card.Header>
<h4>Alertas de Mantenimiento Pendiente</h4>
</Card.Header>
<Card.Body>
{pendingAlerts.length === 0 ? (
<Alert variant="success">No hay alertas pendientes</Alert>
) : (
pendingAlerts.map(alert => (
<Alert key={alert.id} variant="warning">
<strong>{alert.vehicle.plate}</strong> - {alert.maintenance_type} pendiente para: {new Date(alert.next_maintenance_date).toLocaleDateString()}
</Alert>
))
)}
</Card.Body>
</Card>
</Col>
</Row>
{/* Historial de Mantenimientos */}
<Row>
<Col>
<Card>
<Card.Header className="d-flex justify-content-between align-items-center">
<h4>Historial de Mantenimientos</h4>
<Button
variant="primary"
onClick={() => setShowNewMaintenanceForm(true)}
>
Nuevo Mantenimiento
</Button>
</Card.Header>
<Card.Body>
<Table striped bordered hover>
<thead>
<tr>
<th>Vehículo</th>
<th>Fecha</th>
<th>Tipo</th>
<th>Estado</th>
<th>Acciones</th>
</tr>
</thead>
<tbody>
{maintenanceHistory.map(record => (
<tr key={record.id}>
<td>{record.vehicle.plate}</td>
<td>{new Date(record.date).toLocaleDateString()}</td>
<td>{record.maintenance_type}</td>
<td>{record.status}</td>
<td>
<Button variant="info" size="sm" className="me-2">
Ver Detalles
</Button>
<Button variant="secondary" size="sm">
Editar
</Button>
</td>
</tr>
))}
</tbody>
</Table>
</Card.Body>
</Card>
</Col>
</Row>
{/* Modal de Nuevo Mantenimiento */}
<NewMaintenanceForm
show={showNewMaintenanceForm}
handleClose={() => setShowNewMaintenanceForm(false)}
onSubmit={handleNewMaintenance}
vehicles={vehicles}
/>
</div>
);
};
export default JorgeDashboard;

151
resources/js/components/dashboard/NewMaintenanceForm.jsx

@ -0,0 +1,151 @@
import React, { useState } from 'react';
import { Modal, Form, Button } from 'react-bootstrap';
const NewMaintenanceForm = ({ show, handleClose, onSubmit, vehicles }) => {
const [formData, setFormData] = useState({
vehicle_id: '',
maintenance_type: '',
date: '',
description: '',
cost: '',
status: 'Pendiente',
next_maintenance_date: ''
});
const handleChange = (e) => {
const { name, value } = e.target;
setFormData(prev => ({
...prev,
[name]: value
}));
};
const handleSubmit = (e) => {
e.preventDefault();
onSubmit(formData);
// Resetear el formulario
setFormData({
vehicle_id: '',
maintenance_type: '',
date: '',
description: '',
cost: '',
status: 'Pendiente',
next_maintenance_date: ''
});
};
return (
<Modal show={show} onHide={handleClose}>
<Modal.Header closeButton>
<Modal.Title>Nuevo Registro de Mantenimiento</Modal.Title>
</Modal.Header>
<Modal.Body>
<Form onSubmit={handleSubmit}>
<Form.Group className="mb-3">
<Form.Label>Vehículo</Form.Label>
<Form.Select
name="vehicle_id"
value={formData.vehicle_id}
onChange={handleChange}
required
>
<option value="">Seleccione un vehículo</option>
{vehicles.map(vehicle => (
<option key={vehicle.id} value={vehicle.id}>
{vehicle.plate} - {vehicle.model}
</option>
))}
</Form.Select>
</Form.Group>
<Form.Group className="mb-3">
<Form.Label>Tipo de Mantenimiento</Form.Label>
<Form.Select
name="maintenance_type"
value={formData.maintenance_type}
onChange={handleChange}
required
>
<option value="">Seleccione un tipo</option>
<option value="Preventivo">Preventivo</option>
<option value="Correctivo">Correctivo</option>
<option value="Cambio de Aceite">Cambio de Aceite</option>
<option value="Revisión General">Revisión General</option>
</Form.Select>
</Form.Group>
<Form.Group className="mb-3">
<Form.Label>Fecha</Form.Label>
<Form.Control
type="date"
name="date"
value={formData.date}
onChange={handleChange}
required
/>
</Form.Group>
<Form.Group className="mb-3">
<Form.Label>Descripción</Form.Label>
<Form.Control
as="textarea"
rows={3}
name="description"
value={formData.description}
onChange={handleChange}
required
/>
</Form.Group>
<Form.Group className="mb-3">
<Form.Label>Costo</Form.Label>
<Form.Control
type="number"
step="0.01"
name="cost"
value={formData.cost}
onChange={handleChange}
required
/>
</Form.Group>
<Form.Group className="mb-3">
<Form.Label>Estado</Form.Label>
<Form.Select
name="status"
value={formData.status}
onChange={handleChange}
required
>
<option value="Pendiente">Pendiente</option>
<option value="En Proceso">En Proceso</option>
<option value="Completado">Completado</option>
</Form.Select>
</Form.Group>
<Form.Group className="mb-3">
<Form.Label>Próxima Fecha de Mantenimiento</Form.Label>
<Form.Control
type="date"
name="next_maintenance_date"
value={formData.next_maintenance_date}
onChange={handleChange}
/>
</Form.Group>
<div className="d-flex justify-content-end gap-2">
<Button variant="secondary" onClick={handleClose}>
Cancelar
</Button>
<Button variant="primary" type="submit">
Guardar
</Button>
</div>
</Form>
</Modal.Body>
</Modal>
);
};
export default NewMaintenanceForm;

10
resources/js/maintenance-dashboard.js

@ -0,0 +1,10 @@
import 'bootstrap/dist/css/bootstrap.min.css';
import React from 'react';
import { createRoot } from 'react-dom/client';
import JorgeDashboard from './components/dashboard/JorgeDashboard';
const container = document.getElementById('maintenance-dashboard');
if (container) {
const root = createRoot(container);
root.render(<JorgeDashboard />);
}

142
resources/views/despartamentos.blade.php

@ -1,142 +0,0 @@
@extends('layouts.dashboard')
@section('content')
<div class="container mx-auto px-4 py-6">
<!-- Mensajes de éxito y error -->
@if(session('success'))
<div id="success-message" class="bg-green-100 border border-green-400 text-green-700 px-4 py-3 rounded relative mb-4" role="alert">
<span class="block sm:inline">{{ session('success') }}</span>
</div>
@endif
@if(session('error'))
<div class="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded relative mb-4" role="alert">
<span class="block sm:inline">{{ session('error') }}</span>
</div>
@endif
<div class="bg-white rounded-lg shadow-lg">
<!-- Encabezado con título y botones de acción -->
<div class="p-4 border-b border-gray-200 flex justify-between items-center">
<h2 class="text-2xl font-bold">Gestión de Departamentos</h2>
<div class="flex items-center space-x-6">
<!-- Íconos de agregar -->
<div class="flex space-x-4">
<a href="{{ route('despartamentos.excel') }}"
class="text-green-600 hover:text-green-700 transition-colors duration-200"
title="Exportar a Excel">
<i class="fas fa-file-excel text-xl"></i>
</a>
<a href="{{ route('despartamentos.pdf') }}"
class="text-red-600 hover:text-red-700 transition-colors duration-200"
title="Exportar a PDF">
<i class="fas fa-file-pdf text-xl"></i>
</a>
<!-- Agregar nuevo departamento -->
<a href="{{ route('despartamentos.create') }}"
class="text-blue-500 hover:text-blue-600 transition-colors duration-200"
title="Agregar nuevo departamento">
<i class="fas fa-plus text-xl"></i>
</a>
</div>
</div>
</div>
<!-- Barra de búsqueda -->
<div class="p-4 border-b border-gray-200 bg-gray-50">
<form action="{{ route('despartamentos.index') }}" method="GET" class="flex gap-2">
<div class="relative w-full sm:w-64">
<input type="text"
name="busqueda"
placeholder="Buscar departamento..."
value="{{ request('busqueda') }}"
class="w-full pl-10 pr-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500">
<div class="absolute left-3 top-2.5 text-gray-400">
<i class="fas fa-search"></i>
</div>
</div>
<button type="submit" class="px-4 py-2 bg-blue-500 text-white rounded-lg hover:bg-blue-600">
Buscar
</button>
@if(request('busqueda'))
<a href="{{ route('despartamentos.index') }}" class="px-4 py-2 bg-gray-500 text-white rounded-lg hover:bg-gray-600">
Limpiar
</a>
@endif
</form>
</div>
<!-- Tabla de departamentos -->
<div class="overflow-x-auto">
<table class="min-w-full divide-y divide-gray-200">
<thead class="bg-gray-50">
<tr>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Número</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Departamento</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Acciones</th>
</tr>
</thead>
<tbody class="bg-white divide-y divide-gray-200">
@foreach($despartamentos as $index => $despartamento)
<tr class="hover:bg-gray-50">
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">{{ $index + 1 }}</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">
<i class="fas fa-building text-blue-500 mr-2"></i>
{{ $despartamento->departamento }}
</td>
<td class="flex space-x-2 px-6 py-4 whitespace-nowrap text-sm">
<a href="{{ route('despartamentos.edit', $despartamento->id) }}"
class="text-yellow-600 hover:text-yellow-700 transition-colors duration-200"
title="Editar departamento">
<i class="fas fa-edit"></i>
</a>
<form action="{{ route('despartamentos.destroy', $despartamento->id) }}" method="POST" class="d-inline">
@csrf
@method('DELETE')
<a href="#" onclick="event.preventDefault(); confirmarEliminacion(this);"
class="text-red-600 hover:text-red-700 transition-colors duration-200"
title="Eliminar departamento">
<i class="fas fa-trash"></i>
</a>
</form>
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
</div>
</div>
<script>
// Desaparecer el mensaje después de 3 segundos
setTimeout(function() {
var message = document.getElementById('success-message');
if (message) {
message.style.transition = 'opacity 0.5s ease';
message.style.opacity = '0';
setTimeout(function() {
message.remove();
}, 500);
}
}, 3000);
function confirmarEliminacion(button) {
Swal.fire({
title: '¿Estás seguro?',
text: "Esta acción no se puede deshacer",
icon: 'warning',
showCancelButton: true,
confirmButtonColor: '#3085d6',
cancelButtonColor: '#d33',
confirmButtonText: 'Sí, eliminar',
cancelButtonText: 'Cancelar'
}).then((result) => {
if (result.isConfirmed) {
button.closest('form').submit();
}
});
}
</script>
@endsection

82
resources/views/despartamentosCrearEditar.blade.php

@ -1,82 +0,0 @@
@extends('layouts.dashboard')
@section('content')
<div class="container mx-auto px-4 py-6">
<div class="max-w-lg mx-auto">
<div class="bg-white rounded-lg shadow-lg overflow-hidden">
<div class="p-6">
<!-- Encabezado del formulario -->
<div class="flex items-center justify-between mb-6">
<h2 class="text-2xl font-bold text-gray-800">
{{ isset($despartamento) ? 'Editar Departamento' : 'Nuevo Departamento' }}
</h2>
<div class="h-10 w-10 bg-blue-100 rounded-full flex items-center justify-center">
<i class="fas fa-building text-blue-600"></i>
</div>
</div>
<!-- Mensajes de error -->
@if($errors->any())
<div class="mb-6 bg-red-50 border-l-4 border-red-500 p-4 rounded-r-lg">
<div class="flex items-center">
<i class="fas fa-exclamation-circle text-red-500 mr-3"></i>
<div class="text-red-700">
<ul>
@foreach($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
</div>
</div>
@endif
<!-- Formulario -->
<form id="despartamentoForm"
action="{{ isset($despartamento) ? route('despartamentos.update', $despartamento->id) : route('despartamentos.store') }}"
method="POST">
@csrf
@if(isset($despartamento))
@method('PUT')
@endif
<div class="space-y-6">
<!-- Campo Nombre -->
<div>
<label for="departamento" class="block text-sm font-medium text-gray-700 mb-2">
Nombre del Departamento
</label>
<div class="relative rounded-md shadow-sm">
<div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
<i class="fas fa-tag text-gray-400"></i>
</div>
<input type="text"
name="departamento"
id="departamento"
class="block w-full pl-10 pr-3 py-2 border border-gray-300 rounded-md focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
required
placeholder="Ingresa el nombre del departamento"
value="{{ isset($despartamento) ? $despartamento->departamento : old('departamento') }}">
</div>
@error('departamento')
<p class="mt-1 text-sm text-red-600">{{ $message }}</p>
@enderror
</div>
<!-- Botones de acción -->
<div class="flex justify-end space-x-2 pt-4 border-t border-gray-200">
<a href="{{ route('despartamentos.index') }}"
class="px-4 py-2 border border-gray-300 rounded-md text-sm font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500">
Cancelar
</a>
<button type="submit"
class="px-4 py-2 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500">
{{ isset($despartamento) ? 'Actualizar' : 'Guardar' }}
</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
@endsection

36
resources/views/exports/departamentos.blade.php

@ -1,36 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title>Departamentos</title>
<style>
table {
width: 100%;
border-collapse: collapse;
}
th, td {
border: 1px solid black;
padding: 8px;
text-align: left;
}
</style>
</head>
<body>
<h1>Lista de Departamentos</h1>
<table>
<thead>
<tr>
<th>ID</th>
<th>Departamento</th>
</tr>
</thead>
<tbody>
@foreach($departamentos as $departamento)
<tr>
<td>{{ $departamento->id }}</td>
<td>{{ $departamento->departamento }}</td>
</tr>
@endforeach
</tbody>
</table>
</body>
</html>

36
resources/views/exports/prestamos-pdf.blade.php

@ -1,7 +1,7 @@
<!DOCTYPE html>
<html>
<head>
<title>Historial de Préstamos</title>
<title>Lista de Préstamos</title>
<style>
body {
font-family: Arial, sans-serif;
@ -20,22 +20,24 @@
th {
background-color: #f2f2f2;
}
.header {
h1 {
text-align: center;
margin-bottom: 20px;
}
.estado-aceptado {
color: green;
.header {
margin-bottom: 20px;
}
.estado-rechazado {
color: red;
.footer {
margin-top: 20px;
text-align: right;
font-size: 10px;
}
</style>
</head>
<body>
<div class="header">
<h2>Historial de Préstamos</h2>
<p>Fecha de generación: {{ now()->format('d/m/Y H:i') }}</p>
<h1>Lista de Préstamos</h1>
<p>Fecha de generación: {{ date('d/m/Y H:i:s') }}</p>
</div>
<table>
@ -44,10 +46,12 @@
<th>ID</th>
<th>Solicitante</th>
<th>Destino</th>
<th>Fecha Salida</th>
<th>Fecha Llegada</th>
<th>Salida</th>
<th>Llegada</th>
<th>Motivo</th>
<th>Domicilio</th>
<th>Personas</th>
<th>Chofer</th>
<th>Estado</th>
</tr>
</thead>
@ -57,16 +61,16 @@
<td>{{ $prestamo->id }}</td>
<td>{{ $prestamo->nombre_solicitante }}</td>
<td>{{ $prestamo->destino }}</td>
<td>{{ \Carbon\Carbon::parse($prestamo->fecha_hora_salida)->format('d/m/Y H:i') }}</td>
<td>{{ \Carbon\Carbon::parse($prestamo->fecha_hora_llegada)->format('d/m/Y H:i') }}</td>
<td>{{ $prestamo->fecha_hora_salida }}</td>
<td>{{ $prestamo->fecha_hora_llegada }}</td>
<td>{{ $prestamo->motivo }}</td>
<td>{{ $prestamo->domicilio }}</td>
<td>{{ $prestamo->numero_personas }}</td>
<td class="estado-{{ $prestamo->estado }}">
{{ ucfirst($prestamo->estado) }}
</td>
<td>{{ $prestamo->chofer ? 'Sí' : 'No' }}</td>
<td>{{ $prestamo->eliminado == 0 ? 'Activo' : 'Inactivo' }}</td>
</tr>
@endforeach
</tbody>
</table>
</body>
</html>
</html>

16
resources/views/layouts/dashboard.blade.php

@ -113,6 +113,14 @@
</a>
</li>
<!-- Mantenimiento -->
<li>
<a href="/maintenance" class="nav-item-hover flex items-center space-x-3 px-4 py-3 rounded-lg hover:bg-white/10 backdrop-blur-sm {{ request()->is('maintenance') ? 'bg-white/20' : '' }}">
<i class="fas fa-tools text-white/80"></i>
<span class="font-light">Mantenimiento</span>
</a>
</li>
<!-- Configuración con Submenú -->
<li x-data="{ open: false }" class="relative">
<button @click="open = !open"
@ -143,14 +151,6 @@
</a>
</li>
<!-- departamento -->
<li>
<a href="{{ route('despartamentos.index') }}" class="nav-item-hover flex items-center space-x-3 px-4 py-3 rounded-lg hover:bg-white/10 backdrop-blur-sm {{ request()->is('departamentos*') ? 'bg-white/20' : '' }}">
<i class="fas fa-building text-white/80"></i>
<span class="font-light">Departamentos</span>
</a>
</li>
<!-- Tipos -->
<li>
<a href="{{ route('vehiculos.index') }}" class="nav-item-hover flex items-center space-x-3 px-4 py-3 rounded-lg hover:bg-white/10 backdrop-blur-sm {{ request()->is('vehiculos*') ? 'bg-white/20' : '' }}">

9
resources/views/maintenance/dashboard.blade.php

@ -0,0 +1,9 @@
@extends('layouts.dashboard')
@section('content')
<div id="maintenance-dashboard"></div>
@endsection
@push('scripts')
@vite('resources/js/maintenance-dashboard.js')
@endpush

79
resources/views/prestamos.blade.php

@ -15,44 +15,34 @@
</div>
@endif
@if($errors->any())
<div class="mb-6 bg-red-50 border-l-4 border-red-500 p-4 rounded-r-lg">
<div class="flex items-center">
<div class="text-red-700">
<ul>
@foreach($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
</div>
</div>
@endif
<div class="bg-white rounded-lg shadow-lg">
<!-- Encabezado con título y botones de acción -->
<div class="p-4 border-b border-gray-200 flex justify-between items-center">
<h2 class="text-2xl font-bold">Gestión de Préstamos</h2>
<div class="flex items-center space-x-6">
<!-- Exportar a Excel -->
<a href="{{ route('prestamos.excel') }}"
class="text-green-600 hover:text-green-700 transition-colors duration-200"
title="Exportar a Excel">
<i class="fas fa-file-excel text-xl"></i>
</a>
<!-- Exportar a PDF -->
<a href="{{ route('prestamos.pdf') }}"
class="text-red-600 hover:text-red-700 transition-colors duration-200"
title="Exportar a PDF">
<i class="fas fa-file-pdf text-xl"></i>
</a>
<!-- Agregar nuevo préstamo -->
<a href="{{ route('prestamos.create') }}"
class="text-blue-500 hover:text-blue-600 transition-colors duration-200"
title="Agregar nuevo préstamo">
<i class="fas fa-plus text-xl"></i>
</a>
<!-- Íconos de exportación -->
<div class="flex space-x-4">
<!-- Exportar a Excel -->
<a href="{{ route('prestamos.excel') }}"
class="text-green-600 hover:text-green-700 transition-colors duration-200"
title="Exportar a Excel">
<i class="fas fa-file-excel text-xl"></i>
</a>
<!-- Exportar a PDF -->
<a href="{{ route('prestamos.pdf') }}"
class="text-red-600 hover:text-red-700 transition-colors duration-200"
title="Exportar a PDF">
<i class="fas fa-file-pdf text-xl"></i>
</a>
<!-- Agregar nuevo préstamo -->
<a href="{{ route('prestamos.create') }}"
class="text-blue-500 hover:text-blue-600 transition-colors duration-200"
title="Agregar nuevo préstamo">
<i class="fas fa-plus text-xl"></i>
</a>
</div>
</div>
</div>
@ -161,16 +151,28 @@
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium">
<div class="flex space-x-2">
<a href="{{ route('prestamos.edit', $prestamo->id) }}" class="text-yellow-600 hover:text-blue-900">
<a href="{{ route('prestamos.edit', $prestamo->id) }}" class="text-blue-600 hover:text-blue-900">
<i class="fas fa-edit"></i>
</a>
<form action="{{ route('prestamos.destroy', $prestamo->id) }}" method="POST" class="inline" onsubmit="return confirmarEliminacion(this);">
<form action="{{ route('prestamos.destroy', $prestamo->id) }}" method="POST" class="inline">
@csrf
@method('DELETE')
<button type="submit" class="text-red-600 hover:text-red-900">
<button type="submit" class="text-red-600 hover:text-red-900" onclick="return confirm('¿Estás seguro de que deseas eliminar este préstamo?')">
<i class="fas fa-trash"></i>
</button>
</form>
<form action="{{ route('prestamos.aceptar', $prestamo->id) }}" method="POST" class="inline">
@csrf
<button type="submit" class="text-green-600 hover:text-green-900" onclick="return confirm('¿Estás seguro de que deseas aceptar este préstamo?')">
<i class="fas fa-check"></i>
</button>
</form>
<form action="{{ route('prestamos.rechazar', $prestamo->id) }}" method="POST" class="inline">
@csrf
<button type="submit" class="text-yellow-600 hover:text-yellow-900" onclick="return confirm('¿Estás seguro de que deseas rechazar este préstamo?')">
<i class="fas fa-times"></i>
</button>
</form>
</div>
</td>
</tr>
@ -194,8 +196,8 @@
}
}, 3000);
function confirmarEliminacion(form) {
return Swal.fire({
function confirmarEliminacion(button) {
Swal.fire({
title: '¿Estás seguro?',
text: "Esta acción no se puede deshacer",
icon: 'warning',
@ -206,9 +208,8 @@
cancelButtonText: 'Cancelar'
}).then((result) => {
if (result.isConfirmed) {
form.submit();
button.closest('form').submit();
}
return false; // Evitar el envío del formulario por defecto
});
}
</script>

141
resources/views/prestamos/aceptar.blade.php

@ -1,141 +0,0 @@
@extends('layouts.dashboard')
@section('content')
<div class="container mx-auto px-4 py-6">
<!-- Mensajes de éxito/error -->
@if(session('success'))
<div id="success-message" class="bg-green-100 border border-green-400 text-green-700 px-4 py-3 rounded relative mb-4" role="alert">
{{ session('success') }}
</div>
@endif
@if(session('error'))
<div class="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded relative mb-4" role="alert">
<span class="block sm:inline">{{ session('error') }}</span>
</div>
@endif
<div class="bg-white rounded-lg shadow-lg">
<div class="p-4 border-b border-gray-200 flex justify-between items-center">
<h2 class="text-2xl font-bold">Aceptar Prestamos</h2>
</div>
<!-- Barra de búsqueda -->
<div class="p-4 border-b border-gray-200 bg-gray-50">
<form action="{{ route('prestamos.aceptados') }}" method="GET" class="flex gap-2">
<div class="relative w-full sm:w-64">
<input type="text"
name="busqueda"
placeholder="Buscar préstamo..."
value="{{ request('busqueda') }}"
class="w-full pl-10 pr-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500">
<div class="absolute left-3 top-2.5 text-gray-400">
<i class="fas fa-search"></i>
</div>
</div>
<button type="submit" class="px-4 py-2 bg-blue-500 text-white rounded-lg hover:bg-blue-600">
Buscar
</button>
@if(request('busqueda'))
<a href="{{ route('prestamos.aceptados') }}" class="px-4 py-2 bg-gray-500 text-white rounded-lg hover:bg-gray-600">
Limpiar
</a>
@endif
</form>
</div>
<div class="overflow-x-auto">
<table class="min-w-full divide-y divide-gray-200">
<thead class="bg-gray-50">
<tr>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Número</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Solicitante</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Destino</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Fecha Salida</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Fecha Llegada</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Motivo</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Domicilio</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Personas</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Chofer</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Estado</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Acciones</th>
</tr>
</thead>
<tbody class="bg-white divide-y divide-gray-200">
@foreach($prestamos as $prestamo)
<tr class="hover:bg-gray-50">
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">{{ $prestamo->id }}</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">
<i class="fas fa-user text-blue-500 mr-2"></i>
{{ $prestamo->nombre_solicitante }}
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm">
<i class="fas fa-map-marker-alt text-red-500 mr-2"></i>
{{ $prestamo->destino }}
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm">
<i class="fas fa-calendar text-green-500 mr-2"></i>
{{ \Carbon\Carbon::parse($prestamo->fecha_hora_salida)->format('d/m/Y H:i') }}
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm">
<i class="fas fa-calendar-check text-yellow-500 mr-2"></i>
{{ \Carbon\Carbon::parse($prestamo->fecha_hora_llegada)->format('d/m/Y H:i') }}
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm">{{ $prestamo->motivo }}</td>
<td class="px-6 py-4 whitespace-nowrap text-sm">{{ $prestamo->domicilio }}</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-center">{{ $prestamo->numero_personas }}</td>
<td class="px-6 py-4 whitespace-nowrap text-sm">{{ $prestamo->chofer }}</td>
<td class="px-6 py-4 whitespace-nowrap text-sm">
<span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full
{{ $prestamo->estado === 'pendiente' ? 'bg-yellow-100 text-yellow-800' :
($prestamo->estado === 'aceptado' ? 'bg-green-100 text-green-800' : 'bg-red-100 text-red-800') }}">
{{ ucfirst($prestamo->estado) }}
</span>
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm">
<div class="flex gap-2">
@if($prestamo->estado === 'pendiente')
<form action="{{ route('prestamos.aceptar', $prestamo->id) }}" method="POST" class="inline">
@csrf
<button type="submit" class="text-green-600 hover:text-green-900" title="Aceptar">
<i class="fas fa-check"></i>
</button>
</form>
<form action="{{ route('prestamos.rechazar', $prestamo->id) }}" method="POST" class="inline">
@csrf
<button type="submit" class="text-red-600 hover:text-red-900" title="Rechazar">
<i class="fas fa-times"></i>
</button>
</form>
@else
<span class="text-gray-400">
<i class="fas fa-check-circle"></i>
Procesado
</span>
@endif
</div>
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
// Desaparecer mensaje de éxito
var message = document.getElementById('success-message');
if (message) {
setTimeout(function() {
message.style.transition = 'opacity 0.5s ease';
message.style.opacity = '0';
setTimeout(function() {
message.remove();
}, 500);
}, 3000);
}
});
</script>
@endsection

160
resources/views/prestamos/historial.blade.php

@ -1,160 +0,0 @@
@extends('layouts.dashboard')
@section('content')
<div class="container mx-auto px-4 py-6">
<!-- Mensajes de éxito/error -->
@if(session('success'))
<div id="success-message" class="bg-green-100 border border-green-400 text-green-700 px-4 py-3 rounded relative mb-4" role="alert">
{{ session('success') }}
</div>
@endif
@if(session('error'))
<div class="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded relative mb-4" role="alert">
<span class="block sm:inline">{{ session('error') }}</span>
</div>
@endif
<div class="bg-white rounded-lg shadow-lg">
<div class="p-4 border-b border-gray-200 flex justify-between items-center">
<h2 class="text-2xl font-bold">Historial de Préstamos</h2>
<!-- Botones de exportación -->
<div class="flex gap-2">
<a href="{{ route('prestamos.export', ['format' => 'excel']) }}" class="text-gray-600 hover:text-green-700 p-2" title="Exportar a Excel">
<i class="fas fa-file-excel"></i>
</a>
<a href="{{ route('prestamos.export', ['format' => 'pdf']) }}" class="text-gray-600 hover:text-red-600 p-2" title="Exportar a PDF">
<i class="fas fa-file-pdf"></i>
</a>
<button onclick="window.print()" class="text-gray-600 hover:text-gray-800 p-2" title="Imprimir">
<i class="fas fa-print"></i>
</button>
</div>
</div>
<!-- Barra de búsqueda y filtros -->
<div class="p-4 border-b border-gray-200 bg-gray-50">
<form action="{{ route('prestamos.historial') }}" method="GET" class="flex flex-wrap gap-4">
<div class="relative flex-1 min-w-[200px]">
<input type="text"
name="busqueda"
placeholder="Buscar préstamo..."
value="{{ request('busqueda') }}"
class="w-full pl-10 pr-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500">
<div class="absolute left-3 top-2.5 text-gray-400">
<i class="fas fa-search"></i>
</div>
</div>
<!-- Filtro de estado -->
<select name="estado" class="border border-gray-300 rounded-lg px-4 py-2 focus:ring-2 focus:ring-blue-500 focus:border-blue-500">
<option value="">Todos los estados</option>
<option value="aceptado" {{ request('estado') === 'aceptado' ? 'selected' : '' }}>Aceptados</option>
<option value="rechazado" {{ request('estado') === 'rechazado' ? 'selected' : '' }}>Rechazados</option>
</select>
<!-- Filtro de fechas -->
<input type="date"
name="fecha_desde"
value="{{ request('fecha_desde') }}"
class="border border-gray-300 rounded-lg px-4 py-2 focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
placeholder="Fecha desde">
<input type="date"
name="fecha_hasta"
value="{{ request('fecha_hasta') }}"
class="border border-gray-300 rounded-lg px-4 py-2 focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
placeholder="Fecha hasta">
<button type="submit" class="px-4 py-2 bg-blue-500 text-white rounded-lg hover:bg-blue-600">
Filtrar
</button>
@if(request('busqueda') || request('estado') || request('fecha_desde') || request('fecha_hasta'))
<a href="{{ route('prestamos.historial') }}" class="px-4 py-2 bg-gray-500 text-white rounded-lg hover:bg-gray-600">
Limpiar
</a>
@endif
</form>
</div>
<div class="overflow-x-auto">
<table class="min-w-full divide-y divide-gray-200">
<thead class="bg-gray-50">
<tr>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Número</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Solicitante</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Destino</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Fecha Salida</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Fecha Llegada</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Motivo</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Domicilio</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Personas</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Chofer</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Estado</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Fecha Actualización</th>
</tr>
</thead>
<tbody class="bg-white divide-y divide-gray-200">
@foreach($prestamos as $prestamo)
<tr class="hover:bg-gray-50">
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">{{ $prestamo->id }}</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">
<i class="fas fa-user text-blue-500 mr-2"></i>
{{ $prestamo->nombre_solicitante }}
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm">
<i class="fas fa-map-marker-alt text-red-500 mr-2"></i>
{{ $prestamo->destino }}
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm">
<i class="fas fa-calendar text-green-500 mr-2"></i>
{{ \Carbon\Carbon::parse($prestamo->fecha_hora_salida)->format('d/m/Y H:i') }}
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm">
<i class="fas fa-calendar-check text-yellow-500 mr-2"></i>
{{ \Carbon\Carbon::parse($prestamo->fecha_hora_llegada)->format('d/m/Y H:i') }}
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm">{{ $prestamo->motivo }}</td>
<td class="px-6 py-4 whitespace-nowrap text-sm">{{ $prestamo->domicilio }}</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-center">{{ $prestamo->numero_personas }}</td>
<td class="px-6 py-4 whitespace-nowrap text-sm">{{ $prestamo->chofer }}</td>
<td class="px-6 py-4 whitespace-nowrap text-sm">
<span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full
{{ $prestamo->estado === 'aceptado' ? 'bg-green-100 text-green-800' : 'bg-red-100 text-red-800' }}">
{{ ucfirst($prestamo->estado) }}
</span>
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
{{ \Carbon\Carbon::parse($prestamo->updated_at)->format('d/m/Y H:i') }}
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
<!-- Paginación -->
<div class="px-6 py-4 border-t border-gray-200">
{{ $prestamos->links() }}
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
// Desaparecer mensaje de éxito
var message = document.getElementById('success-message');
if (message) {
setTimeout(function() {
message.style.transition = 'opacity 0.5s ease';
message.style.opacity = '0';
setTimeout(function() {
message.remove();
}, 500);
}, 3000);
}
});
</script>
@endsection

7
resources/views/usuarios.blade.php

@ -84,7 +84,7 @@
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Email </th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Apellido </th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Puesto </th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Departamento </th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Carrera </th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Teléfono </th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Estado </th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Acciones </th>
@ -116,10 +116,7 @@
</div>
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">
<div class="flex items-center">
<i class="fas fa-building text-gray-400 mr-2"></i>
{{ $usuario->despartamento->departamento ?? 'Sin departamento asignado' }}
</div>
<i class="fas fa-graduation-cap text-purple-500"></i> {{ $usuario->carrera }}
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">
<i class="fas fa-phone text-red-500"></i> {{ $usuario->telefono }}

17
resources/views/usuariosCrearEditar.blade.php

@ -90,20 +90,15 @@
</div>
<div>
<label for="departamento_id" class="block text-sm font-medium text-gray-700 mb-2">Departamento</label>
<label for="carrera" class="block text-sm font-medium text-gray-700 mb-2">Carrera</label>
<div class="relative">
<div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
<i class="fas fa-building text-gray-400"></i>
<i class="fas fa-graduation-cap text-gray-400"></i>
</div>
<select name="departamento_id" id="departamento_id" class="block w-full pl-10 pr-3 py-2 border border-gray-300 rounded-md" required>
<option value="">Selecciona un departamento</option>
@foreach($despartamentos as $departamento)
<option value="{{ $departamento->id }}"
{{ (isset($usuario) && $usuario->departamento_id == $departamento->id) ? 'selected' : '' }}>
{{ $departamento->departamento }}
</option>
@endforeach
</select>
<input type="text" name="carrera" id="carrera"
class="block w-full pl-10 pr-3 py-2 border border-gray-300 rounded-md"
placeholder="Ingresa la carrera"
required value="{{ isset($usuario) ? $usuario->carrera : old('carrera') }}">
</div>
</div>

10
routes/api.php

@ -2,6 +2,7 @@
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\MaintenanceController;
/*
|--------------------------------------------------------------------------
@ -17,3 +18,12 @@ use Illuminate\Support\Facades\Route;
Route::middleware('auth:sanctum')->get('/user', function (Request $request) {
return $request->user();
});
Route::middleware('auth:sanctum')->group(function () {
// Rutas de mantenimiento
Route::get('/maintenance/history', [MaintenanceController::class, 'getMaintenanceHistory']);
Route::get('/maintenance/alerts', [MaintenanceController::class, 'getPendingAlerts']);
Route::get('/maintenance/vehicles', [MaintenanceController::class, 'getVehicles']);
Route::post('/maintenance', [MaintenanceController::class, 'store']);
Route::put('/maintenance/{maintenance}', [MaintenanceController::class, 'update']);
});

76
routes/web.php

@ -9,8 +9,7 @@ use App\Http\Controllers\TiposVeiculosController;
use App\Http\Controllers\TiposLicenciasController;
use App\Http\Controllers\CapacidadController;
use App\Http\Controllers\PrestamoController;
use App\Http\Controllers\DespartamentoController;
use App\Http\Controllers\MaintenanceController;
/*
|--------------------------------------------------------------------------
@ -27,12 +26,10 @@ use App\Http\Controllers\DespartamentoController;
return redirect()->route('dashboard');
});
Auth::routes(['register'=>false,'reset'=>false]);
Auth::routes(['register'=>true,'reset'=>false]);
Route::resource('marca', MarcaController::class);
Route::resource('docentes', DocentesController::class);
Route::resource('despartamentos', DespartamentoController::class);
// Rutas específicas de vehículos
Route::get('vehiculos/excel', [TiposVeiculosController::class, 'exportExcel'])->name('vehiculos.excel');
@ -52,17 +49,12 @@ use App\Http\Controllers\DespartamentoController;
Route::get('/marcas/excel', [MarcaController::class, 'exportExcel'])->name('marcas.excel');
Route::get('/marcas/pdf', [MarcaController::class, 'exportPDF'])->name('marcas.pdf');
Route::get('/despartamento/export/excel', [DespartamentoController::class, 'exportExcel'])->name('despartamentos.excel');
Route::get('/despartamento/export/pdf', [DespartamentoController::class, 'exportPDF'])->name('despartamentos.pdf');
// Primero las rutas de exportación (más específicas)
Route::get('/prestamos/excel', [PrestamoController::class, 'exportExcel'])->name('prestamos.excel');
Route::get('/prestamos/pdf', [PrestamoController::class, 'exportPDF'])->name('prestamos.pdf');
Route::get('/prestamos/export/{format}', [PrestamoController::class, 'export'])->name('prestamos.export');
Route::get('/prestamos/aceptados', [PrestamoController::class, 'aceptados'])->name('prestamos.aceptados');
Route::post('/prestamos/{id}/aceptar', [PrestamoController::class, 'aceptar'])->name('prestamos.aceptar');
Route::post('/prestamos/{id}/rechazar', [PrestamoController::class, 'rechazar'])->name('prestamos.rechazar');
Route::get('/prestamos/historial', [PrestamoController::class, 'historial'])->name('prestamos.historial');
// Después la ruta de recurso (más general)
Route::resource('prestamos', PrestamoController::class);
@ -73,50 +65,22 @@ use App\Http\Controllers\DespartamentoController;
// Rutas protegidas que requieren autenticación
// Rutas protegidas que requieren autenticación
Route::middleware(['auth', 'admin'])->group(function () {
Route::get('/dashboard', [HomeController::class, 'index'])->name('dashboard');
Route::get('/usuarios', [usuariosController::class,'index'])->name('usuarios');
Route::get('/usuarios/nuevo', [usuariosController::class, 'create'])->name('usuarios.create');
Route::post('/usuarios/store', [usuariosController::class,'store'])->name('usuarios.store');
Route::get('/usuarios/edit/{id}', [usuariosController::class, 'edit'])->name('usuarios.edit');
Route::put('/usuarios/{id}', [usuariosController::class, 'update'])->name('usuarios.update');
Route::delete('/usuarios/destroy/{id}', [usuariosController::class, 'destroy'])->name('usuarios.destroy');
Route::get('usuarios/excel', [usuariosController::class, 'exportExcel'])->name('usuarios.excel');
Route::get('usuarios/pdf', [usuariosController::class, 'exportPDF'])->name('usuarios.pdf');
Route::get('/home', [HomeController::class, 'index'])->name('home');
// Rutas específicas de vehículos
Route::get('vehiculos/excel', [TiposVeiculosController::class, 'exportExcel'])->name('vehiculos.excel');
Route::get('vehiculos/pdf', [TiposVeiculosController::class, 'exportPDF'])->name('vehiculos.pdf');
Route::get('/vehiculos/{id}/toggle-status', [TiposVeiculosController::class, 'toggleStatus'])->name('vehiculos.toggle-status');
Route::resource('vehiculos', TiposVeiculosController::class);
// Rutas específicas de tipos de licencias
Route::get('tiposLicencias/excel', [TiposLicenciasController::class, 'exportExcel'])->name('tiposLicencias.excel');
Route::get('tiposLicencias/pdf', [TiposLicenciasController::class, 'exportPDF'])->name('tiposLicencias.pdf');
Route::get('tiposLicencias/{id}/toggle-status', [TiposLicenciasController::class, 'toggleStatus'])->name('tiposLicencias.toggle-status');
Route::resource('tiposLicencias', TiposLicenciasController::class);
Route::resource('capacidades', CapacidadController::class);
Route::get('/marcas/excel', [MarcaController::class, 'exportExcel'])->name('marcas.excel');
Route::get('/marcas/pdf', [MarcaController::class, 'exportPDF'])->name('marcas.pdf');
Route::get('/despartamento/export/excel', [DespartamentoController::class, 'exportExcel'])->name('despartamentos.excel');
Route::get('/despartamento/export/pdf', [DespartamentoController::class, 'exportPDF'])->name('despartamentos.pdf');
// Rutas de préstamos
Route::get('/prestamos/excel', [PrestamoController::class, 'exportExcel'])->name('prestamos.excel');
Route::get('/prestamos/pdf', [PrestamoController::class, 'exportPDF'])->name('prestamos.pdf');
Route::get('/prestamos/export/{format}', [PrestamoController::class, 'export'])->name('prestamos.export');
Route::get('/prestamos/aceptados', [PrestamoController::class, 'aceptados'])->name('prestamos.aceptados');
Route::post('/prestamos/{id}/aceptar', [PrestamoController::class, 'aceptar'])->name('prestamos.aceptar');
Route::post('/prestamos/{id}/rechazar', [PrestamoController::class, 'rechazar'])->name('prestamos.rechazar');
Route::get('/prestamos/historial', [PrestamoController::class, 'historial'])->name('prestamos.historial');
Route::resource('prestamos', PrestamoController::class);
Route::get('/docentes/export/{format}', [DocentesController::class, 'export'])->name('docentes.export');
Route::get('/docentes/{id}/toggle-status', [DocentesController::class, 'toggleStatus'])->name('docentes.toggle-status');
});
Route::middleware(['auth'])->group(function () {
Route::get('/dashboard', [HomeController::class, 'index'])->name('dashboard');
Route::get('/maintenance', [MaintenanceController::class, 'index'])->name('maintenance.dashboard');
Route::get('/usuarios', [usuariosController::class,'index'])->name('usuarios');
Route::get('/usuarios/nuevo', [usuariosController::class, 'create'])->name('usuarios.create');
Route::post('/usuarios/store', [usuariosController::class,'store'])->name('usuarios.store');
Route::get('/usuarios/edit/{id}', [usuariosController::class, 'edit'])->name('usuarios.edit');
Route::put('/usuarios/{id}', [usuariosController::class, 'update'])->name('usuarios.update');
Route::delete('/usuarios/destroy/{id}', [usuariosController::class, 'destroy'])->name('usuarios.destroy');
Route::get('usuarios/excel', [usuariosController::class, 'exportExcel'])->name('usuarios.excel');
Route::get('usuarios/pdf', [usuariosController::class, 'exportPDF'])->name('usuarios.pdf');
Route::get('/home', [HomeController::class, 'index'])->name('home');
////corerecion rubi
});

Loading…
Cancel
Save