feat(rutinas): API de catálogo y asignación multi-día
CRUD del catálogo en /api/parents/routine-tasks y alta de una rutina a varios días de una vez en ParentScheduleController. RoutineView deriva la definición del catálogo e incluye routineTaskId.
This commit is contained in:
@@ -0,0 +1,85 @@
|
||||
package es.asepeyo.recordalexia.web;
|
||||
|
||||
import es.asepeyo.recordalexia.domain.RoutineTask;
|
||||
import es.asepeyo.recordalexia.exception.NotFoundException;
|
||||
import es.asepeyo.recordalexia.repository.FamilyRepository;
|
||||
import es.asepeyo.recordalexia.repository.RoutineTaskRepository;
|
||||
import es.asepeyo.recordalexia.security.FamilyContext;
|
||||
import es.asepeyo.recordalexia.web.dto.ParentDtos.RoutineTaskRequest;
|
||||
import es.asepeyo.recordalexia.web.dto.ParentViews.RoutineTaskView;
|
||||
import java.util.List;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* CRUD del catálogo de rutinas de tarde, por familia (panel de padres). Es el gemelo de
|
||||
* {@link ParentCatalogController} para las actividades de mañana: aquí se define una
|
||||
* rutina una sola vez y luego se asigna a días desde {@link ParentScheduleController}.
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api/parents/routine-tasks")
|
||||
public class ParentRoutineCatalogController {
|
||||
|
||||
private final RoutineTaskRepository routineTaskRepository;
|
||||
private final FamilyRepository familyRepository;
|
||||
private final FamilyContext familyContext;
|
||||
|
||||
public ParentRoutineCatalogController(RoutineTaskRepository routineTaskRepository,
|
||||
FamilyRepository familyRepository,
|
||||
FamilyContext familyContext) {
|
||||
this.routineTaskRepository = routineTaskRepository;
|
||||
this.familyRepository = familyRepository;
|
||||
this.familyContext = familyContext;
|
||||
}
|
||||
|
||||
private Long fid() {
|
||||
return familyContext.currentFamilyId();
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
public List<RoutineTaskView> list() {
|
||||
return routineTaskRepository.findByFamilyId(fid()).stream().map(this::toView).toList();
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
public ResponseEntity<RoutineTaskView> create(@RequestBody RoutineTaskRequest req) {
|
||||
RoutineTask task = new RoutineTask(req.labelEs(), req.labelCa(), req.icon(), req.color());
|
||||
task.setFamily(familyRepository.getReferenceById(fid()));
|
||||
task.setCoinsReward(req.coinsReward());
|
||||
return ResponseEntity.ok(toView(routineTaskRepository.save(task)));
|
||||
}
|
||||
|
||||
/** Editar la definición se refleja en todos los días que usan esta rutina. */
|
||||
@PutMapping("/{id}")
|
||||
public ResponseEntity<RoutineTaskView> update(@PathVariable Long id,
|
||||
@RequestBody RoutineTaskRequest req) {
|
||||
RoutineTask task = routineTaskRepository.findByIdAndFamilyId(id, fid())
|
||||
.orElseThrow(() -> new NotFoundException("No existe la rutina con id " + id));
|
||||
task.setLabelEs(req.labelEs());
|
||||
task.setLabelCa(req.labelCa());
|
||||
task.setIcon(req.icon());
|
||||
task.setColor(req.color());
|
||||
task.setCoinsReward(req.coinsReward());
|
||||
return ResponseEntity.ok(toView(routineTaskRepository.save(task)));
|
||||
}
|
||||
|
||||
@DeleteMapping("/{id}")
|
||||
public ResponseEntity<Void> delete(@PathVariable Long id) {
|
||||
routineTaskRepository.findByIdAndFamilyId(id, fid())
|
||||
.orElseThrow(() -> new NotFoundException("No existe la rutina con id " + id));
|
||||
routineTaskRepository.deleteById(id);
|
||||
return ResponseEntity.noContent().build();
|
||||
}
|
||||
|
||||
private RoutineTaskView toView(RoutineTask t) {
|
||||
return new RoutineTaskView(t.getId(), t.getLabelEs(), t.getLabelCa(), t.getIcon(),
|
||||
t.getColor(), t.getCoinsReward());
|
||||
}
|
||||
}
|
||||
@@ -3,11 +3,13 @@ package es.asepeyo.recordalexia.web;
|
||||
import es.asepeyo.recordalexia.domain.Activity;
|
||||
import es.asepeyo.recordalexia.domain.AfternoonRoutine;
|
||||
import es.asepeyo.recordalexia.domain.Child;
|
||||
import es.asepeyo.recordalexia.domain.RoutineTask;
|
||||
import es.asepeyo.recordalexia.domain.WeeklyTemplateEntry;
|
||||
import es.asepeyo.recordalexia.exception.NotFoundException;
|
||||
import es.asepeyo.recordalexia.repository.ActivityRepository;
|
||||
import es.asepeyo.recordalexia.repository.AfternoonRoutineRepository;
|
||||
import es.asepeyo.recordalexia.repository.ChildRepository;
|
||||
import es.asepeyo.recordalexia.repository.RoutineTaskRepository;
|
||||
import es.asepeyo.recordalexia.repository.WeeklyTemplateEntryRepository;
|
||||
import es.asepeyo.recordalexia.security.FamilyContext;
|
||||
import es.asepeyo.recordalexia.web.dto.ParentDtos.AfternoonRoutineRequest;
|
||||
@@ -36,17 +38,20 @@ public class ParentScheduleController {
|
||||
|
||||
private final WeeklyTemplateEntryRepository templateRepository;
|
||||
private final AfternoonRoutineRepository routineRepository;
|
||||
private final RoutineTaskRepository routineTaskRepository;
|
||||
private final ChildRepository childRepository;
|
||||
private final ActivityRepository activityRepository;
|
||||
private final FamilyContext familyContext;
|
||||
|
||||
public ParentScheduleController(WeeklyTemplateEntryRepository templateRepository,
|
||||
AfternoonRoutineRepository routineRepository,
|
||||
RoutineTaskRepository routineTaskRepository,
|
||||
ChildRepository childRepository,
|
||||
ActivityRepository activityRepository,
|
||||
FamilyContext familyContext) {
|
||||
this.templateRepository = templateRepository;
|
||||
this.routineRepository = routineRepository;
|
||||
this.routineTaskRepository = routineTaskRepository;
|
||||
this.childRepository = childRepository;
|
||||
this.activityRepository = activityRepository;
|
||||
this.familyContext = familyContext;
|
||||
@@ -97,14 +102,20 @@ public class ParentScheduleController {
|
||||
.map(this::toRoutineView).toList();
|
||||
}
|
||||
|
||||
/** Asigna una rutina del catálogo a uno o varios días de una vez (sin duplicar definición). */
|
||||
@PostMapping("/routines")
|
||||
public ResponseEntity<RoutineView> createRoutine(@RequestBody AfternoonRoutineRequest req) {
|
||||
@Transactional
|
||||
public ResponseEntity<List<RoutineView>> createRoutine(@RequestBody AfternoonRoutineRequest req) {
|
||||
Child child = requireChild(req.childId());
|
||||
AfternoonRoutine routine = new AfternoonRoutine(child, DayOfWeek.valueOf(req.dayOfWeek()),
|
||||
req.labelEs(), req.labelCa(), req.icon(), req.color(),
|
||||
req.orderIndex() != null ? req.orderIndex() : 0);
|
||||
RoutineTask task = routineTaskRepository.findByIdAndFamilyId(req.routineTaskId(), fid())
|
||||
.orElseThrow(() -> new NotFoundException("No existe la rutina con id " + req.routineTaskId()));
|
||||
int orderIndex = req.orderIndex() != null ? req.orderIndex() : 0;
|
||||
List<RoutineView> created = req.daysOfWeek().stream().map(day -> {
|
||||
AfternoonRoutine routine = new AfternoonRoutine(child, DayOfWeek.valueOf(day), task, orderIndex);
|
||||
routine.setCoinsReward(req.coinsReward());
|
||||
return ResponseEntity.ok(toRoutineView(routineRepository.save(routine)));
|
||||
return toRoutineView(routineRepository.save(routine));
|
||||
}).toList();
|
||||
return ResponseEntity.ok(created);
|
||||
}
|
||||
|
||||
/** Reordena rutinas (solo las de la familia): orderIndex según la posición recibida. */
|
||||
@@ -143,8 +154,9 @@ public class ParentScheduleController {
|
||||
}
|
||||
|
||||
private RoutineView toRoutineView(AfternoonRoutine r) {
|
||||
RoutineTask t = r.getRoutineTask();
|
||||
return new RoutineView(r.getId(), r.getChild().getId(), r.getDayOfWeek().name(),
|
||||
r.getLabelEs(), r.getLabelCa(), r.getIcon(), r.getColor(), r.getOrderIndex(),
|
||||
t.getId(), t.getLabelEs(), t.getLabelCa(), t.getIcon(), t.getColor(), r.getOrderIndex(),
|
||||
r.getCoinsReward());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,14 +32,23 @@ public final class ParentDtos {
|
||||
String labelEs, String labelCa, String icon, String color, String category) {
|
||||
}
|
||||
|
||||
/** Alta/edición de una rutina del catálogo de la familia (definición reutilizable). */
|
||||
public record RoutineTaskRequest(
|
||||
String labelEs, String labelCa, String icon, String color, Integer coinsReward) {
|
||||
}
|
||||
|
||||
// --- Horario / rutinas ---
|
||||
public record WeeklyEntryRequest(
|
||||
Long childId, String dayOfWeek, Long activityId, Integer orderIndex, Integer coinsReward) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Asigna una rutina del catálogo a un niño para varios días de una vez. El orden y
|
||||
* las monedas son opcionales (override por día).
|
||||
*/
|
||||
public record AfternoonRoutineRequest(
|
||||
Long childId, String dayOfWeek, String labelEs, String labelCa, String icon, String color,
|
||||
Integer orderIndex, Integer coinsReward) {
|
||||
Long childId, Long routineTaskId, List<String> daysOfWeek, Integer orderIndex,
|
||||
Integer coinsReward) {
|
||||
}
|
||||
|
||||
/** Reordenación de rutinas: lista de ids en el nuevo orden deseado. */
|
||||
|
||||
@@ -21,14 +21,20 @@ public final class ParentViews {
|
||||
Long id, String labelEs, String labelCa, String icon, String color, List<Long> materialIds) {
|
||||
}
|
||||
|
||||
/** Rutina del catálogo de la familia (definición reutilizable). */
|
||||
public record RoutineTaskView(
|
||||
Long id, String labelEs, String labelCa, String icon, String color, Integer coinsReward) {
|
||||
}
|
||||
|
||||
public record WeeklyEntryView(
|
||||
Long id, Long childId, String dayOfWeek, Long activityId, String activityLabelEs,
|
||||
String icon, String color, int orderIndex, Integer coinsReward) {
|
||||
}
|
||||
|
||||
/** Asignación de una rutina a un día; labels/icon/color derivan del catálogo. */
|
||||
public record RoutineView(
|
||||
Long id, Long childId, String dayOfWeek, String labelEs, String labelCa, String icon,
|
||||
String color, int orderIndex, Integer coinsReward) {
|
||||
Long id, Long childId, String dayOfWeek, Long routineTaskId, String labelEs, String labelCa,
|
||||
String icon, String color, int orderIndex, Integer coinsReward) {
|
||||
}
|
||||
|
||||
public record EventAdminView(
|
||||
|
||||
Reference in New Issue
Block a user