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.Activity;
|
||||||
import es.asepeyo.recordalexia.domain.AfternoonRoutine;
|
import es.asepeyo.recordalexia.domain.AfternoonRoutine;
|
||||||
import es.asepeyo.recordalexia.domain.Child;
|
import es.asepeyo.recordalexia.domain.Child;
|
||||||
|
import es.asepeyo.recordalexia.domain.RoutineTask;
|
||||||
import es.asepeyo.recordalexia.domain.WeeklyTemplateEntry;
|
import es.asepeyo.recordalexia.domain.WeeklyTemplateEntry;
|
||||||
import es.asepeyo.recordalexia.exception.NotFoundException;
|
import es.asepeyo.recordalexia.exception.NotFoundException;
|
||||||
import es.asepeyo.recordalexia.repository.ActivityRepository;
|
import es.asepeyo.recordalexia.repository.ActivityRepository;
|
||||||
import es.asepeyo.recordalexia.repository.AfternoonRoutineRepository;
|
import es.asepeyo.recordalexia.repository.AfternoonRoutineRepository;
|
||||||
import es.asepeyo.recordalexia.repository.ChildRepository;
|
import es.asepeyo.recordalexia.repository.ChildRepository;
|
||||||
|
import es.asepeyo.recordalexia.repository.RoutineTaskRepository;
|
||||||
import es.asepeyo.recordalexia.repository.WeeklyTemplateEntryRepository;
|
import es.asepeyo.recordalexia.repository.WeeklyTemplateEntryRepository;
|
||||||
import es.asepeyo.recordalexia.security.FamilyContext;
|
import es.asepeyo.recordalexia.security.FamilyContext;
|
||||||
import es.asepeyo.recordalexia.web.dto.ParentDtos.AfternoonRoutineRequest;
|
import es.asepeyo.recordalexia.web.dto.ParentDtos.AfternoonRoutineRequest;
|
||||||
@@ -36,17 +38,20 @@ public class ParentScheduleController {
|
|||||||
|
|
||||||
private final WeeklyTemplateEntryRepository templateRepository;
|
private final WeeklyTemplateEntryRepository templateRepository;
|
||||||
private final AfternoonRoutineRepository routineRepository;
|
private final AfternoonRoutineRepository routineRepository;
|
||||||
|
private final RoutineTaskRepository routineTaskRepository;
|
||||||
private final ChildRepository childRepository;
|
private final ChildRepository childRepository;
|
||||||
private final ActivityRepository activityRepository;
|
private final ActivityRepository activityRepository;
|
||||||
private final FamilyContext familyContext;
|
private final FamilyContext familyContext;
|
||||||
|
|
||||||
public ParentScheduleController(WeeklyTemplateEntryRepository templateRepository,
|
public ParentScheduleController(WeeklyTemplateEntryRepository templateRepository,
|
||||||
AfternoonRoutineRepository routineRepository,
|
AfternoonRoutineRepository routineRepository,
|
||||||
|
RoutineTaskRepository routineTaskRepository,
|
||||||
ChildRepository childRepository,
|
ChildRepository childRepository,
|
||||||
ActivityRepository activityRepository,
|
ActivityRepository activityRepository,
|
||||||
FamilyContext familyContext) {
|
FamilyContext familyContext) {
|
||||||
this.templateRepository = templateRepository;
|
this.templateRepository = templateRepository;
|
||||||
this.routineRepository = routineRepository;
|
this.routineRepository = routineRepository;
|
||||||
|
this.routineTaskRepository = routineTaskRepository;
|
||||||
this.childRepository = childRepository;
|
this.childRepository = childRepository;
|
||||||
this.activityRepository = activityRepository;
|
this.activityRepository = activityRepository;
|
||||||
this.familyContext = familyContext;
|
this.familyContext = familyContext;
|
||||||
@@ -97,14 +102,20 @@ public class ParentScheduleController {
|
|||||||
.map(this::toRoutineView).toList();
|
.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")
|
@PostMapping("/routines")
|
||||||
public ResponseEntity<RoutineView> createRoutine(@RequestBody AfternoonRoutineRequest req) {
|
@Transactional
|
||||||
|
public ResponseEntity<List<RoutineView>> createRoutine(@RequestBody AfternoonRoutineRequest req) {
|
||||||
Child child = requireChild(req.childId());
|
Child child = requireChild(req.childId());
|
||||||
AfternoonRoutine routine = new AfternoonRoutine(child, DayOfWeek.valueOf(req.dayOfWeek()),
|
RoutineTask task = routineTaskRepository.findByIdAndFamilyId(req.routineTaskId(), fid())
|
||||||
req.labelEs(), req.labelCa(), req.icon(), req.color(),
|
.orElseThrow(() -> new NotFoundException("No existe la rutina con id " + req.routineTaskId()));
|
||||||
req.orderIndex() != null ? req.orderIndex() : 0);
|
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());
|
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. */
|
/** 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) {
|
private RoutineView toRoutineView(AfternoonRoutine r) {
|
||||||
|
RoutineTask t = r.getRoutineTask();
|
||||||
return new RoutineView(r.getId(), r.getChild().getId(), r.getDayOfWeek().name(),
|
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());
|
r.getCoinsReward());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,14 +32,23 @@ public final class ParentDtos {
|
|||||||
String labelEs, String labelCa, String icon, String color, String category) {
|
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 ---
|
// --- Horario / rutinas ---
|
||||||
public record WeeklyEntryRequest(
|
public record WeeklyEntryRequest(
|
||||||
Long childId, String dayOfWeek, Long activityId, Integer orderIndex, Integer coinsReward) {
|
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(
|
public record AfternoonRoutineRequest(
|
||||||
Long childId, String dayOfWeek, String labelEs, String labelCa, String icon, String color,
|
Long childId, Long routineTaskId, List<String> daysOfWeek, Integer orderIndex,
|
||||||
Integer orderIndex, Integer coinsReward) {
|
Integer coinsReward) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Reordenación de rutinas: lista de ids en el nuevo orden deseado. */
|
/** 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) {
|
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(
|
public record WeeklyEntryView(
|
||||||
Long id, Long childId, String dayOfWeek, Long activityId, String activityLabelEs,
|
Long id, Long childId, String dayOfWeek, Long activityId, String activityLabelEs,
|
||||||
String icon, String color, int orderIndex, Integer coinsReward) {
|
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(
|
public record RoutineView(
|
||||||
Long id, Long childId, String dayOfWeek, String labelEs, String labelCa, String icon,
|
Long id, Long childId, String dayOfWeek, Long routineTaskId, String labelEs, String labelCa,
|
||||||
String color, int orderIndex, Integer coinsReward) {
|
String icon, String color, int orderIndex, Integer coinsReward) {
|
||||||
}
|
}
|
||||||
|
|
||||||
public record EventAdminView(
|
public record EventAdminView(
|
||||||
|
|||||||
Reference in New Issue
Block a user