Files
fitness/resources/views/workouts/show.blade.php
T

225 lines
18 KiB
PHP
Raw Normal View History

2025-10-02 22:52:13 +03:00
<x-app-layout>
2026-08-10 12:39:16 +03:00
@php
$isOwner = Auth::id() === $workout->user_id;
$canManage = $isOwner || (Auth::check() && $workout->assigned_by_trainer_id === Auth::id());
@endphp
2025-10-02 22:52:13 +03:00
<section class="bg-white dark:bg-gray-900 border-b border-gray-100 dark:border-gray-800">
<div class="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8 py-8">
<div class="flex items-center justify-between">
<div>
2026-08-10 12:39:16 +03:00
<a href="{{ url()->previous() === url()->current() ? route('workouts.index') : url()->previous() }}" class="text-sm text-gray-600 dark:text-gray-300 hover:text-gray-900 dark:hover:text-white">← Back</a>
2025-10-02 23:52:07 +03:00
<div class="mt-2 flex items-center gap-3">
<h1 class="text-2xl sm:text-3xl font-extrabold tracking-tight text-gray-900 dark:text-white">{{ $workout->title }}</h1>
@auth
@php $isFav = auth()->user()->hasFavoritedWorkout($workout); @endphp
<form method="POST" action="{{ route('workouts.favorite', $workout) }}">
@csrf
<button type="submit" class="inline-flex items-center gap-1.5 rounded-full bg-gray-100 dark:bg-gray-800 px-3 py-1 text-xs font-semibold text-gray-900 dark:text-gray-100 ring-1 ring-gray-200 dark:ring-gray-700 hover:bg-gray-200 dark:hover:bg-gray-700">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="h-4 w-4 {{ $isFav ? 'text-rose-600' : 'text-gray-400' }}">
@if($isFav)
<path d="M11.645 20.91l-.007-.003-.022-.01a15.247 15.247 0 01-.383-.18 25.18 25.18 0 01-4.244-2.686C4.688 15.734 2.25 13.153 2.25 9.75 2.25 7.005 4.46 4.875 7.125 4.875c1.6 0 3.09.744 4.125 1.917a5.625 5.625 0 014.125-1.917c2.665 0 4.875 2.13 4.875 4.875 0 3.403-2.438 5.984-4.739 7.58a25.175 25.175 0 01-4.244 2.685 15.247 15.247 0 01-.383.18l-.022.01-.007.004-.003.001a.75.75 0 01-.592 0l-.003-.001z"/>
@else
<path fill-rule="evenodd" d="M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 6.01 4.01 4 6.5 4c1.74 0 3.41.81 4.5 2.09A6.004 6.004 0 0115.5 4C17.99 4 20 6.01 20 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z" clip-rule="evenodd"/>
@endif
</svg>
<span>{{ $isFav ? 'Favorited' : 'Favorite' }}</span>
</button>
</form>
@endauth
</div>
2026-08-10 12:28:23 +03:00
@if($workout->assignedByTrainer)
<span class="mt-2 inline-flex items-center rounded-full bg-indigo-100 dark:bg-indigo-900/40 text-indigo-700 dark:text-indigo-300 px-2.5 py-0.5 text-xs font-medium">Assigned by {{ $workout->assignedByTrainer->name }}</span>
@endif
2026-08-10 12:39:16 +03:00
@if(!$isOwner && $workout->is_public)
<span class="mt-2 inline-flex items-center rounded-full bg-emerald-100 dark:bg-emerald-900/40 text-emerald-700 dark:text-emerald-300 px-2.5 py-0.5 text-xs font-medium">Public workout by {{ $workout->user->name }}</span>
@endif
2025-10-02 22:52:13 +03:00
<p class="mt-1 text-gray-600 dark:text-gray-300">@if($workout->estimated_minutes) ~ {{ $workout->estimated_minutes }} min · @endif {{ $workout->items->count() }} exercises</p>
</div>
<div class="flex items-center gap-3">
2026-08-10 12:39:16 +03:00
@if($isOwner)
<form method="POST" action="{{ route('sessions.store', $workout) }}">
@csrf
<button type="submit" class="inline-flex items-center rounded-xl bg-gradient-to-r from-emerald-600 to-teal-600 px-3 py-2 text-sm font-semibold text-white shadow hover:from-emerald-500 hover:to-teal-500" title="{{ __('app.sessions.start') }}" aria-label="{{ __('app.sessions.start') }}">{{ __('app.sessions.start') }}</button>
</form>
2026-08-10 12:39:16 +03:00
<form method="POST" action="{{ route('workouts.toggle-public', $workout) }}">
@csrf
<button type="submit" class="inline-flex items-center gap-1.5 rounded-full bg-gray-100 dark:bg-gray-800 px-3 py-1 text-xs font-semibold text-gray-900 dark:text-gray-100 ring-1 ring-gray-200 dark:ring-gray-700 hover:bg-gray-200 dark:hover:bg-gray-700">
{{ $workout->is_public ? 'Public' : 'Private' }}
</button>
</form>
<form method="POST" action="{{ route('workouts.destroy', $workout) }}" onsubmit="return confirm('{{ __('app.workouts.confirm_delete') }}');">
@csrf
@method('DELETE')
<button type="submit" class="text-sm text-red-600 hover:text-red-500">{{ __('app.workouts.delete') }}</button>
</form>
2026-08-10 12:39:16 +03:00
@elseif(Auth::check() && $workout->is_public)
<form method="POST" action="{{ route('workouts.copy', $workout) }}">
@csrf
<button type="submit" class="inline-flex items-center rounded-xl bg-gradient-to-r from-fuchsia-600 to-indigo-600 px-3 py-2 text-sm font-semibold text-white shadow hover:from-fuchsia-500 hover:to-indigo-500">Copy to My Workouts</button>
</form>
@endif
</div>
2025-10-02 22:52:13 +03:00
</div>
</div>
</section>
<section class="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8 py-8">
@if(session('status'))
<div class="mb-4 rounded-xl bg-emerald-50 text-emerald-700 px-4 py-3">{{ session('status') }}</div>
@endif
@if ($errors->any())
<div class="mb-4 rounded-xl bg-red-50 text-red-700 px-4 py-3">
<ul class="list-disc ms-5 text-sm">
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
@endif
<div class="grid grid-cols-1 lg:grid-cols-12 gap-8">
<div class="lg:col-span-7">
<div class="rounded-2xl ring-1 ring-gray-200 dark:ring-gray-700 bg-white dark:bg-gray-800 p-5">
<h2 class="text-lg font-semibold text-gray-900 dark:text-white">Exercises</h2>
@if($workout->items->count() === 0)
<div class="mt-3 rounded-xl border border-dashed border-gray-300 dark:border-gray-700 p-6 text-sm text-gray-600 dark:text-gray-300">No exercises yet. Add your first one on the right.</div>
@else
<ul class="mt-4 space-y-4">
@foreach($workout->items as $item)
<li class="rounded-xl ring-1 ring-gray-200 dark:ring-gray-700 bg-white dark:bg-gray-900 p-4">
<div class="flex items-start justify-between gap-4">
<div>
<div class="text-sm text-gray-500">#{{ $item->position }}</div>
2025-10-04 19:40:13 +03:00
<div class="text-base font-semibold text-gray-900 dark:text-white">{{ $item->exercise->display_name }}</div>
2025-10-02 22:52:13 +03:00
<div class="mt-1 text-sm text-gray-600 dark:text-gray-300">
@if($item->sets) {{ $item->sets }} sets @endif
@if($item->reps) · {{ $item->reps }} reps @endif
@if($item->weight) · {{ $item->weight }} kg @endif
@if($item->rest_seconds) · {{ $item->rest_seconds }}s rest @endif
</div>
@if($item->notes)
<div class="mt-2 text-sm text-gray-600 dark:text-gray-300">{{ $item->notes }}</div>
@endif
</div>
2026-08-10 12:39:16 +03:00
@if($canManage)
<div class="flex items-center gap-3">
<form method="POST" action="{{ route('workouts.exercises.remove', [$workout, $item]) }}" onsubmit="return confirm('Remove this exercise?');">
@csrf
@method('DELETE')
<button type="submit" class="text-sm text-red-600 hover:text-red-500">Remove</button>
</form>
2025-10-02 22:52:13 +03:00
</div>
2026-08-10 12:39:16 +03:00
@endif
</div>
@if($canManage)
<details class="mt-3">
<summary class="cursor-pointer text-sm text-indigo-600">Edit</summary>
<form method="POST" action="{{ route('workouts.exercises.update', [$workout, $item]) }}" class="mt-3 grid grid-cols-2 md:grid-cols-3 lg:grid-cols-6 gap-3">
@csrf
@method('PATCH')
<input type="number" name="position" value="{{ $item->position }}" min="1" placeholder="Position" class="rounded-lg border-gray-300 dark:border-gray-700 bg-white/80 dark:bg-gray-900 text-gray-900 dark:text-gray-100 px-3 py-2" />
<input type="number" name="sets" value="{{ $item->sets }}" min="1" placeholder="Sets" class="rounded-lg border-gray-300 dark:border-gray-700 bg-white/80 dark:bg-gray-900 text-gray-900 dark:text-gray-100 px-3 py-2" />
<input type="number" name="reps" value="{{ $item->reps }}" min="1" placeholder="Reps" class="rounded-lg border-gray-300 dark:border-gray-700 bg-white/80 dark:bg-gray-900 text-gray-900 dark:text-gray-100 px-3 py-2" />
<input type="number" step="0.5" name="weight" value="{{ $item->weight }}" min="0" placeholder="Weight (kg)" class="rounded-lg border-gray-300 dark:border-gray-700 bg-white/80 dark:bg-gray-900 text-gray-900 dark:text-gray-100 px-3 py-2" />
<input type="number" name="rest_seconds" value="{{ $item->rest_seconds }}" min="0" placeholder="Rest (s)" class="rounded-lg border-gray-300 dark:border-gray-700 bg-white/80 dark:bg-gray-900 text-gray-900 dark:text-gray-100 px-3 py-2" />
<input type="text" name="notes" value="{{ $item->notes }}" placeholder="Notes" class="lg:col-span-6 rounded-lg border-gray-300 dark:border-gray-700 bg-white/80 dark:bg-gray-900 text-gray-900 dark:text-gray-100 px-3 py-2" />
<div class="lg:col-span-6">
<button type="submit" class="inline-flex items-center rounded-lg bg-indigo-600 px-3 py-2 text-sm font-semibold text-white hover:bg-indigo-500">Save</button>
</div>
</form>
</details>
@endif
2025-10-02 22:52:13 +03:00
</li>
@endforeach
</ul>
@endif
</div>
</div>
<div class="lg:col-span-5">
2026-08-10 12:39:16 +03:00
@if($canManage)
2025-10-02 22:52:13 +03:00
<div class="rounded-2xl ring-1 ring-gray-200 dark:ring-gray-700 bg-white dark:bg-gray-800 p-5">
<h2 class="text-lg font-semibold text-gray-900 dark:text-white">Add Exercise</h2>
<form method="POST" action="{{ route('workouts.exercises.add', $workout) }}" class="mt-4 space-y-4"
x-data="exercisePicker({
searchUrl: '{{ route('workouts.exercises.search', $workout) }}',
initial: {{ $exercises->map(fn($e) => ['id' => $e->id, 'name' => $e->display_name])->values()->toJson() }}
})">
2025-10-02 22:52:13 +03:00
@csrf
<div class="relative">
2025-10-02 22:52:13 +03:00
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300">Exercise</label>
<input type="hidden" name="exercise_id" :value="selectedId" required>
<input type="text" x-model="query" @input.debounce.250ms="search()" @focus="open = true" @keydown.escape="open = false"
placeholder="Search exercises by name…" autocomplete="off"
class="mt-1 w-full rounded-xl border-gray-300 dark:border-gray-700 bg-white/80 dark:bg-gray-900 text-gray-900 dark:text-gray-100 placeholder-gray-400 focus:border-indigo-500 focus:ring-indigo-500 px-3 py-2">
<div x-show="open && results.length" @click.outside="open = false" x-cloak
class="absolute z-10 mt-1 w-full max-h-64 overflow-auto rounded-xl bg-white dark:bg-gray-900 ring-1 ring-gray-200 dark:ring-gray-700 shadow-lg">
<template x-for="result in results" :key="result.id">
<button type="button" @click="select(result)"
class="block w-full text-left px-3 py-2 text-sm text-gray-900 dark:text-gray-100 hover:bg-indigo-50 dark:hover:bg-indigo-900/30"
x-text="result.name"></button>
</template>
</div>
<p x-show="open && query.length > 0 && !results.length" x-cloak class="mt-1 text-xs text-gray-500">No exercises found.</p>
2025-10-02 22:52:13 +03:00
</div>
<div class="grid grid-cols-2 md:grid-cols-4 gap-3">
<div>
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300">Sets</label>
<input type="number" name="sets" min="1" value="3" class="mt-1 w-full rounded-xl border-gray-300 dark:border-gray-700 bg-white/80 dark:bg-gray-900 text-gray-900 dark:text-gray-100 px-3 py-2" />
</div>
<div>
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300">Reps</label>
<input type="number" name="reps" min="1" class="mt-1 w-full rounded-xl border-gray-300 dark:border-gray-700 bg-white/80 dark:bg-gray-900 text-gray-900 dark:text-gray-100 px-3 py-2" />
</div>
<div>
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300">Weight (kg)</label>
<input type="number" step="0.5" name="weight" min="0" class="mt-1 w-full rounded-xl border-gray-300 dark:border-gray-700 bg-white/80 dark:bg-gray-900 text-gray-900 dark:text-gray-100 px-3 py-2" />
</div>
<div>
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300">Rest (s)</label>
<input type="number" name="rest_seconds" min="0" class="mt-1 w-full rounded-xl border-gray-300 dark:border-gray-700 bg-white/80 dark:bg-gray-900 text-gray-900 dark:text-gray-100 px-3 py-2" />
</div>
</div>
<div>
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300">Notes</label>
<input type="text" name="notes" class="mt-1 w-full rounded-xl border-gray-300 dark:border-gray-700 bg-white/80 dark:bg-gray-900 text-gray-900 dark:text-gray-100 px-3 py-2" />
</div>
<div>
<button type="submit" :disabled="!selectedId" :class="!selectedId && 'opacity-50 cursor-not-allowed'" class="inline-flex items-center rounded-xl bg-gradient-to-r from-fuchsia-600 to-indigo-600 px-4 py-2 text-sm font-semibold text-white shadow hover:from-fuchsia-500 hover:to-indigo-500">Add Exercise</button>
2025-10-02 22:52:13 +03:00
</div>
</form>
</div>
2026-08-10 12:39:16 +03:00
@endif
2025-10-02 22:52:13 +03:00
</div>
</div>
</section>
@push('scripts')
<script>
function exercisePicker({ searchUrl, initial }) {
return {
query: '',
results: initial || [],
selectedId: '',
open: false,
async search() {
this.selectedId = '';
if (this.query.trim() === '') {
this.results = initial || [];
this.open = true;
return;
}
const res = await fetch(searchUrl + '?q=' + encodeURIComponent(this.query));
this.results = res.ok ? await res.json() : [];
this.open = true;
},
select(result) {
this.selectedId = result.id;
this.query = result.name;
this.open = false;
},
};
}
</script>
@endpush
2025-10-02 22:52:13 +03:00
</x-app-layout>