filled('q')) { $term = $request->string('q'); $q->where(function ($sub) use ($term) { $sub->where('titulo', 'like', "%{$term}%") ->orWhere('slug', 'like', "%{$term}%"); }); } if ($request->filled('estado')) { $q->where('estado', $request->string('estado')); } if ($request->filled('publicado')) { $q->where('publicado', (bool) $request->boolean('publicado')); } if ($request->boolean('include_detalles')) { $q->with(['detalles' => fn($d) => $d->orderBy('id', 'desc')]); } $q->orderByDesc('id'); $perPage = (int) $request->input('per_page', 15); return response()->json($q->paginate($perPage)); } // GET /api/admin/procesos-admision/{id}?include_detalles=1 public function show(Request $request, $id) { $q = ProcesoAdmision::query(); if ($request->boolean('include_detalles')) { $q->with('detalles'); } return response()->json($q->findOrFail($id)); } // POST /api/admin/procesos-admision (multipart/form-data) public function store(Request $request) { $data = $request->validate([ 'titulo' => ['required','string','max:255'], 'subtitulo' => ['nullable','string','max:255'], 'descripcion' => ['nullable','string'], 'slug' => ['nullable','string','max:120', 'unique:procesos_admision,slug'], 'tipo_proceso' => ['nullable','string','max:60'], 'modalidad' => ['nullable','string','max:50'], 'publicado' => ['sometimes','boolean'], 'fecha_publicacion' => ['nullable','date'], 'fecha_inicio_preinscripcion' => ['nullable','date'], 'fecha_fin_preinscripcion' => ['nullable','date','after_or_equal:fecha_inicio_preinscripcion'], 'fecha_inicio_inscripcion' => ['nullable','date'], 'fecha_fin_inscripcion' => ['nullable','date','after_or_equal:fecha_inicio_inscripcion'], 'fecha_examen1' => ['nullable','date'], 'fecha_examen2' => ['nullable','date','after_or_equal:fecha_examen1'], 'fecha_resultados' => ['nullable','date'], 'fecha_inicio_biometrico' => ['nullable','date'], 'fecha_fin_biometrico' => ['nullable','date','after_or_equal:fecha_inicio_biometrico'], 'link_preinscripcion' => ['nullable','string','max:500'], 'link_inscripcion' => ['nullable','string','max:500'], 'link_resultados' => ['nullable','string','max:500'], 'link_reglamento' => ['nullable','string','max:500'], 'estado' => ['sometimes', Rule::in(['nuevo','publicado','en_proceso','finalizado','cancelado'])], // Archivos (uploads) 'imagen' => ['nullable','image','mimes:jpg,jpeg,png,webp','max:10240'], 'banner' => ['nullable','image','mimes:jpg,jpeg,png,webp','max:10240'], 'brochure' => ['nullable','file','mimes:pdf','max:10240'], ]); // slug auto si no viene if (empty($data['slug'])) { $data['slug'] = Str::slug($data['titulo']); } $data['publicado'] = $data['publicado'] ?? false; // Guardar archivos if ($request->hasFile('imagen')) { $data['imagen_path'] = $request->file('imagen')->store('admisiones/procesos', 'public'); } if ($request->hasFile('banner')) { $data['banner_path'] = $request->file('banner')->store('admisiones/procesos', 'public'); } if ($request->hasFile('brochure')) { $data['brochure_path'] = $request->file('brochure')->store('admisiones/procesos', 'public'); } $proceso = ProcesoAdmision::create($data); return response()->json($proceso, 201); } // PATCH /api/admin/procesos-admision/{id} (multipart/form-data) public function update(Request $request, $id) { $proceso = ProcesoAdmision::findOrFail($id); $data = $request->validate([ 'titulo' => ['sometimes','string','max:255'], 'subtitulo' => ['sometimes','nullable','string','max:255'], 'descripcion' => ['sometimes','nullable','string'], 'slug' => ['sometimes','nullable','string','max:120', Rule::unique('procesos_admision','slug')->ignore($proceso->id)], 'tipo_proceso' => ['sometimes','nullable','string','max:60'], 'modalidad' => ['sometimes','nullable','string','max:50'], 'publicado' => ['sometimes','boolean'], 'fecha_publicacion' => ['sometimes','nullable','date'], 'fecha_inicio_preinscripcion' => ['sometimes','nullable','date'], 'fecha_fin_preinscripcion' => ['sometimes','nullable','date','after_or_equal:fecha_inicio_preinscripcion'], 'fecha_inicio_inscripcion' => ['sometimes','nullable','date'], 'fecha_fin_inscripcion' => ['sometimes','nullable','date','after_or_equal:fecha_inicio_inscripcion'], 'fecha_examen1' => ['sometimes','nullable','date'], 'fecha_examen2' => ['sometimes','nullable','date','after_or_equal:fecha_examen1'], 'fecha_resultados' => ['sometimes','nullable','date'], 'fecha_inicio_biometrico' => ['sometimes','nullable','date'], 'fecha_fin_biometrico' => ['sometimes','nullable','date','after_or_equal:fecha_inicio_biometrico'], 'link_preinscripcion' => ['sometimes','nullable','string','max:500'], 'link_inscripcion' => ['sometimes','nullable','string','max:500'], 'link_resultados' => ['sometimes','nullable','string','max:500'], 'link_reglamento' => ['sometimes','nullable','string','max:500'], 'estado' => ['sometimes', Rule::in(['nuevo','publicado','en_proceso','finalizado','cancelado'])], // Archivos 'imagen' => ['sometimes','nullable','image','mimes:jpg,jpeg,png,webp','max:10240'], 'banner' => ['sometimes','nullable','image','mimes:jpg,jpeg,png,webp','max:10240'], 'brochure' => ['sometimes','nullable','file','mimes:pdf','max:10240'], ]); // slug auto si lo mandan vacĂ­o if (array_key_exists('slug', $data) && empty($data['slug'])) { $data['slug'] = Str::slug($data['titulo'] ?? $proceso->titulo); } // Reemplazo de archivos (borra anterior) if ($request->hasFile('imagen')) { if ($proceso->imagen_path) Storage::disk('public')->delete($proceso->imagen_path); $data['imagen_path'] = $request->file('imagen')->store('admisiones/procesos', 'public'); } if ($request->hasFile('banner')) { if ($proceso->banner_path) Storage::disk('public')->delete($proceso->banner_path); $data['banner_path'] = $request->file('banner')->store('admisiones/procesos', 'public'); } if ($request->hasFile('brochure')) { if ($proceso->brochure_path) Storage::disk('public')->delete($proceso->brochure_path); $data['brochure_path'] = $request->file('brochure')->store('admisiones/procesos', 'public'); } $proceso->update($data); return response()->json($proceso->fresh()); } // DELETE /api/admin/procesos-admision/{id} public function destroy($id) { $proceso = ProcesoAdmision::findOrFail($id); // Borrar archivos asociados if ($proceso->imagen_path) Storage::disk('public')->delete($proceso->imagen_path); if ($proceso->banner_path) Storage::disk('public')->delete($proceso->banner_path); if ($proceso->brochure_path) Storage::disk('public')->delete($proceso->brochure_path); $proceso->delete(); return response()->json(['message' => 'Proceso eliminado']); } }