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
@@ -0,0 +1,30 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('workouts', function (Blueprint $table) {
$table->boolean('is_public')->default(false)->after('title');
$table->index('is_public');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('workouts', function (Blueprint $table) {
$table->dropIndex(['is_public']);
$table->dropColumn('is_public');
});
}
};