orderByDesc('created_at') ->paginate(10); return response()->json($comunicados); } // Admin: crear comunicado public function store(Request $request) { $request->validate([ 'titulo' => 'required|string|max:255', 'fecha_inicio' => 'nullable|date', 'fecha_fin' => 'nullable|date|after_or_equal:fecha_inicio', 'url_accion' => 'nullable|url|max:500', 'texto_boton' => 'nullable|string|max:60', 'imagenes' => 'required|array|min:1', 'imagenes.*' => 'required|file|mimes:jpg,jpeg,png,webp|max:5120', ]); $comunicado = Comunicado::create([ 'titulo' => $request->titulo, 'activo' => false, 'fecha_inicio' => $request->fecha_inicio, 'fecha_fin' => $request->fecha_fin, 'url_accion' => $request->url_accion, 'texto_boton' => $request->texto_boton, ]); foreach ($request->file('imagenes') as $orden => $imagen) { $filename = uniqid() . '.' . $imagen->getClientOriginalExtension(); $path = "comunicados/{$comunicado->id}/{$filename}"; Storage::disk('public')->put($path, file_get_contents($imagen->getRealPath())); ComunicadoImagen::create([ 'comunicado_id' => $comunicado->id, 'imagen_path' => $path, 'orden' => $orden + 1, ]); } return response()->json($comunicado->load('imagenes'), 201); } // Admin: actualizar comunicado public function update(Request $request, int $id) { $comunicado = Comunicado::findOrFail($id); $request->validate([ 'titulo' => 'sometimes|required|string|max:255', 'fecha_inicio' => 'nullable|date', 'fecha_fin' => 'nullable|date|after_or_equal:fecha_inicio', 'url_accion' => 'nullable|url|max:500', 'texto_boton' => 'nullable|string|max:60', 'imagenes' => 'sometimes|array|min:1', 'imagenes.*' => 'file|mimes:jpg,jpeg,png,webp|max:5120', ]); $comunicado->update($request->only('titulo', 'fecha_inicio', 'fecha_fin', 'url_accion', 'texto_boton')); // Si se enviaron nuevas imágenes, AGREGAR a las existentes (no reemplazar) if ($request->hasFile('imagenes')) { $maxOrden = $comunicado->imagenes()->max('orden') ?? 0; foreach ($request->file('imagenes') as $idx => $imagen) { $filename = uniqid() . '.' . $imagen->getClientOriginalExtension(); $path = "comunicados/{$comunicado->id}/{$filename}"; Storage::disk('public')->put($path, file_get_contents($imagen->getRealPath())); ComunicadoImagen::create([ 'comunicado_id' => $comunicado->id, 'imagen_path' => $path, 'orden' => $maxOrden + $idx + 1, ]); } } return response()->json($comunicado->load('imagenes')); } // Admin: eliminar comunicado public function destroy(int $id) { $comunicado = Comunicado::with('imagenes')->findOrFail($id); foreach ($comunicado->imagenes as $img) { Storage::disk('public')->delete($img->imagen_path); } $comunicado->delete(); return response()->json(['message' => 'Comunicado eliminado correctamente.']); } // Admin: activar/desactivar (uno activo a la vez) public function toggleActivo(int $id) { $comunicado = Comunicado::findOrFail($id); if ($comunicado->activo) { // Si ya estaba activo, simplemente lo desactiva $comunicado->update(['activo' => false]); } else { // Desactiva todos los demás y activa este Comunicado::where('activo', true)->update(['activo' => false]); $comunicado->update(['activo' => true]); } return response()->json($comunicado->load('imagenes')); } // Admin: eliminar una imagen individual public function destroyImagen(int $imagenId) { $imagen = ComunicadoImagen::findOrFail($imagenId); Storage::disk('public')->delete($imagen->imagen_path); $imagen->delete(); return response()->json(['message' => 'Imagen eliminada correctamente.']); } // Público: devuelve el comunicado activo con sus imágenes (respeta vigencia) public function activo() { $hoy = Carbon::today(); $comunicado = Comunicado::with('imagenes') ->where('activo', true) ->where(function ($q) use ($hoy) { $q->whereNull('fecha_inicio')->orWhere('fecha_inicio', '<=', $hoy); }) ->where(function ($q) use ($hoy) { $q->whereNull('fecha_fin')->orWhere('fecha_fin', '>=', $hoy); }) ->first(); return response()->json($comunicado); } }