workouts()->with('assignedByTrainer')->latest()->paginate(10); return $this->paginated($workouts); } public function community(Request $request): JsonResponse { $q = trim((string) $request->query('q', '')); $workouts = Workout::query() ->where('is_public', true) ->with(['user:id,name', 'items']) ->when($q !== '', fn ($qb) => $qb->where('title', 'like', "%{$q}%")) ->latest() ->paginate(12) ->withQueryString(); return $this->paginated($workouts); } public function store(Request $request): JsonResponse { $data = $request->validate([ 'title' => 'required|string|max:255', 'notes' => 'nullable|string', 'estimated_minutes' => 'nullable|integer|min:1|max:600', ]); $workout = Auth::user()->workouts()->create($data); return response()->json(['workout' => new WorkoutResource($workout)], 201); } public function show(Workout $workout): JsonResponse { $this->authorizeView($workout); $workout->load(['items.exercise', 'assignedByTrainer', 'user:id,name']); return response()->json(['workout' => new WorkoutResource($workout)]); } public function searchExercises(Request $request, Workout $workout): JsonResponse { $this->authorizeWorkout($workout); $q = trim((string) $request->query('q', '')); $exercises = Exercise::query() ->with(['aliases' => fn ($rel) => $rel->where('user_id', Auth::id())]) ->when($q !== '', fn ($qb) => $qb->where('name', 'like', "%{$q}%")) ->orderBy('name') ->limit(20) ->get(['id', 'name']); return response()->json(ExerciseResource::collection($exercises)); } public function togglePublic(Workout $workout): JsonResponse { if ($workout->user_id !== Auth::id()) { abort(403); } $workout->is_public = !$workout->is_public; $workout->save(); return response()->json(['workout' => new WorkoutResource($workout)]); } public function copy(Workout $workout): JsonResponse { if (!$workout->is_public && $workout->user_id !== Auth::id()) { abort(403); } $workout->loadMissing('items'); $copy = Auth::user()->workouts()->create([ 'title' => $workout->title.' (copy)', 'notes' => $workout->notes, 'estimated_minutes' => $workout->estimated_minutes, ]); foreach ($workout->items as $item) { $copy->items()->create([ 'exercise_id' => $item->exercise_id, 'position' => $item->position, 'sets' => $item->sets, 'reps' => $item->reps, 'weight' => $item->weight, 'rest_seconds' => $item->rest_seconds, 'notes' => $item->notes, ]); } $copy->load(['items.exercise']); return response()->json(['workout' => new WorkoutResource($copy)], 201); } public function addExercise(Request $request, Workout $workout): JsonResponse { $this->authorizeWorkout($workout); $data = $request->validate([ 'exercise_id' => 'required|exists:exercises,id', 'position' => 'nullable|integer|min:1', 'sets' => 'nullable|integer|min:1|max:20', 'reps' => 'nullable|integer|min:1|max:200', 'weight' => 'nullable|numeric|min:0|max:999.99', 'rest_seconds' => 'nullable|integer|min:0|max:600', 'notes' => 'nullable|string', ]); $nextPos = ($workout->items()->max('position') ?? 0) + 1; $data['position'] = $data['position'] ?? $nextPos; $item = $workout->items()->create($data); $item->load('exercise'); return response()->json(['item' => new WorkoutExerciseResource($item)], 201); } public function updateExercise(Request $request, Workout $workout, WorkoutExercise $item): JsonResponse { $this->authorizeWorkout($workout); if ($item->workout_id !== $workout->id) { abort(404); } $data = $request->validate([ 'position' => 'nullable|integer|min:1', 'sets' => 'nullable|integer|min:1|max:20', 'reps' => 'nullable|integer|min:1|max:200', 'weight' => 'nullable|numeric|min:0|max:999.99', 'rest_seconds' => 'nullable|integer|min:0|max:600', 'notes' => 'nullable|string', ]); $item->update($data); $item->load('exercise'); return response()->json(['item' => new WorkoutExerciseResource($item)]); } public function removeExercise(Workout $workout, WorkoutExercise $item): JsonResponse { $this->authorizeWorkout($workout); if ($item->workout_id !== $workout->id) { abort(404); } $item->delete(); return response()->json(['message' => 'Exercise removed']); } public function destroy(Workout $workout): JsonResponse { if ($workout->user_id !== Auth::id()) { abort(403); } $workout->delete(); return response()->json(['message' => 'Workout deleted']); } protected function authorizeWorkout(Workout $workout): void { if ($workout->user_id === Auth::id()) { return; } if ($workout->assigned_by_trainer_id && $workout->assigned_by_trainer_id === Auth::id()) { return; } abort(403); } protected function authorizeView(Workout $workout): void { if ($workout->is_public) { return; } $this->authorizeWorkout($workout); } protected function paginated($paginator): JsonResponse { return response()->json([ 'data' => WorkoutResource::collection($paginator->items()), 'meta' => [ 'current_page' => $paginator->currentPage(), 'last_page' => $paginator->lastPage(), 'per_page' => $paginator->perPage(), 'total' => $paginator->total(), ], ]); } }