Add a community workout library

Workouts were private to their owner with no way to browse or copy
another user's - FavoriteController::toggleWorkout even had a comment
anticipating sharing ("users can still favorite others' workouts if
shared later") that was never followed up on.

Adds an is_public flag workouts can be toggled to from their own page,
a /workouts/community browse+search page, and a copy action that
duplicates a public workout (including all its exercises/sets/reps)
into the copier's own library.

Making this work required splitting workout authorization into two
tiers: authorizeView() (owner, the assigning trainer, or anyone if the
workout is public - read-only) and the existing authorizeWorkout()
(owner or assigning trainer - can add/edit/remove exercises). Deletion
stays strictly owner-only as before. The show page now hides the
"Add Exercise" panel and per-exercise edit/remove controls for viewers
who can see but not manage a workout.

Found and fixed a real Blade compiler quirk via live testing: `@if`
directly after a word character with no space (`exercises@if(...)`)
doesn't compile - Blade's directive regex treats it as email-like text
and leaves it literal - while the matching `@endif` still compiles,
producing invalid mismatched PHP. Fixed by adding a space.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-10 12:39:16 +03:00
co-authored by Claude Sonnet 5
parent bbdf684a6a
commit e74aa2bda4
8 changed files with 351 additions and 28 deletions
+3
View File
@@ -44,11 +44,14 @@ Route::prefix('/exercises')->group(function () {
Route::middleware('auth')->prefix('/workouts')->group(function () {
Route::get('/', [\App\Http\Controllers\WorkoutController::class, 'index'])->name('workouts.index');
Route::get('/builder', [\App\Http\Controllers\WorkoutController::class, 'builder'])->name('workouts.builder');
Route::get('/community', [\App\Http\Controllers\WorkoutController::class, 'community'])->name('workouts.community');
Route::get('/create', [\App\Http\Controllers\WorkoutController::class, 'create'])->name('workouts.create');
Route::post('/', [\App\Http\Controllers\WorkoutController::class, 'store'])->name('workouts.store');
Route::get('/{workout}', [\App\Http\Controllers\WorkoutController::class, 'show'])->name('workouts.show');
Route::delete('/{workout}', [\App\Http\Controllers\WorkoutController::class, 'destroy'])->name('workouts.destroy');
Route::post('/{workout}/toggle-public', [\App\Http\Controllers\WorkoutController::class, 'togglePublic'])->name('workouts.toggle-public');
Route::post('/{workout}/copy', [\App\Http\Controllers\WorkoutController::class, 'copy'])->name('workouts.copy');
// Manage exercises within a workout
Route::get('/{workout}/exercises/search', [\App\Http\Controllers\WorkoutController::class, 'searchExercises'])->name('workouts.exercises.search');