added yoga stuff, i18n, workout tracking and a dashboard to track progress
This commit is contained in:
@@ -1,17 +1,92 @@
|
||||
<x-app-layout>
|
||||
<x-slot name="header">
|
||||
<h2 class="font-semibold text-xl text-gray-800 dark:text-gray-200 leading-tight">
|
||||
{{ __('Dashboard') }}
|
||||
</h2>
|
||||
</x-slot>
|
||||
|
||||
<div class="py-12">
|
||||
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
|
||||
<div class="bg-white dark:bg-gray-800 overflow-hidden shadow-sm sm:rounded-lg">
|
||||
<div class="p-6 text-gray-900 dark:text-gray-100">
|
||||
{{ __("You're logged in!") }}
|
||||
<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>
|
||||
<h1 class="text-2xl sm:text-3xl font-extrabold tracking-tight text-gray-900 dark:text-white">{{ __('app.dashboard.title') }}</h1>
|
||||
<p class="mt-2 text-gray-600 dark:text-gray-300">{{ __('app.dashboard.subtitle') }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8 py-6">
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
|
||||
<div class="rounded-2xl ring-1 ring-gray-200 dark:ring-gray-700 bg-white dark:bg-gray-800 p-5">
|
||||
<div class="text-sm text-gray-500">{{ __('app.dashboard.metrics.workouts') }}</div>
|
||||
<div class="mt-2 text-3xl font-bold text-gray-900 dark:text-white">{{ $workoutsCount }}</div>
|
||||
</div>
|
||||
<div class="rounded-2xl ring-1 ring-gray-200 dark:ring-gray-700 bg-white dark:bg-gray-800 p-5">
|
||||
<div class="text-sm text-gray-500">{{ __('app.dashboard.metrics.sessions') }}</div>
|
||||
<div class="mt-2 text-3xl font-bold text-gray-900 dark:text-white">{{ $sessionsCount }}</div>
|
||||
</div>
|
||||
<div class="rounded-2xl ring-1 ring-gray-200 dark:ring-gray-700 bg-white dark:bg-gray-800 p-5">
|
||||
<div class="text-sm text-gray-500">{{ __('app.dashboard.metrics.completed') }}</div>
|
||||
<div class="mt-2 text-3xl font-bold text-gray-900 dark:text-white">{{ $completedSessions }}</div>
|
||||
</div>
|
||||
<div class="rounded-2xl ring-1 ring-gray-200 dark:ring-gray-700 bg-white dark:bg-gray-800 p-5">
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="text-sm text-gray-500">{{ __('app.dashboard.metrics.completion_rate') }}</div>
|
||||
<div class="text-sm font-semibold text-indigo-600">{{ $completionRate }}%</div>
|
||||
</div>
|
||||
<div class="mt-3 h-2 w-full rounded-full bg-gray-200 dark:bg-gray-700 overflow-hidden">
|
||||
<div class="h-2 bg-gradient-to-r from-emerald-500 to-teal-500" style="width: {{ $completionRate }}%"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-8 grid grid-cols-1 lg:grid-cols-12 gap-6">
|
||||
<div class="lg:col-span-7 rounded-2xl ring-1 ring-gray-200 dark:ring-gray-700 bg-white dark:bg-gray-800 p-5">
|
||||
<div class="flex items-center justify-between">
|
||||
<h2 class="text-lg font-semibold text-gray-900 dark:text-white">{{ __('app.dashboard.recent_sessions') }}</h2>
|
||||
</div>
|
||||
@if($recentSessions->isEmpty())
|
||||
<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">{{ __('app.dashboard.no_sessions') }}</div>
|
||||
@else
|
||||
<ul class="mt-4 space-y-4">
|
||||
@foreach($recentSessions as $s)
|
||||
<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-center justify-between gap-4">
|
||||
<div>
|
||||
<div class="text-base font-semibold text-gray-900 dark:text-white">{{ $s['workout_title'] }}</div>
|
||||
<div class="mt-1 text-sm text-gray-600 dark:text-gray-300">
|
||||
{{ $s['done'] }} / {{ $s['total'] }} · {{ $s['pct'] }}%
|
||||
@if($s['completed_at'])
|
||||
· <span class="text-emerald-600">{{ __('app.dashboard.completed') }}</span>
|
||||
@endif
|
||||
</div>
|
||||
<div class="mt-2 h-2 w-56 rounded-full bg-gray-200 dark:bg-gray-700 overflow-hidden">
|
||||
<div class="h-2 bg-indigo-500" style="width: {{ $s['pct'] }}%"></div>
|
||||
</div>
|
||||
</div>
|
||||
<a href="{{ route('sessions.show', $s['id']) }}" class="text-sm font-semibold text-indigo-600 hover:text-indigo-500">{{ __('app.dashboard.view_session') }}</a>
|
||||
</div>
|
||||
</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
<div class="lg:col-span-5 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">{{ __('app.dashboard.activity_30d') }}</h2>
|
||||
<p class="mt-1 text-sm text-gray-600 dark:text-gray-300">{{ __('app.dashboard.activity_30d_help') }}</p>
|
||||
@php
|
||||
$today = now()->startOfDay();
|
||||
$days = [];
|
||||
for ($i = 29; $i >= 0; $i--) { $d = $today->copy()->subDays($i)->format('Y-m-d'); $days[] = $d; }
|
||||
$max = max(1, $dailyActivity->max() ?? 0);
|
||||
@endphp
|
||||
<div class="mt-4 grid grid-cols-10 gap-2">
|
||||
@foreach($days as $d)
|
||||
@php $count = $dailyActivity[$d] ?? 0; $pct = $max ? intval(($count / $max) * 100) : 0; @endphp
|
||||
<div class="h-14 rounded-lg bg-gray-100 dark:bg-gray-900 ring-1 ring-gray-200 dark:ring-gray-700 flex items-center justify-center">
|
||||
<div class="w-8 h-8 rounded-full bg-indigo-500/20 flex items-center justify-center">
|
||||
<span class="text-xs font-semibold text-indigo-600">{{ $count }}</span>
|
||||
</div>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</x-app-layout>
|
||||
|
||||
@@ -90,4 +90,41 @@
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@if(isset($similarExercises))
|
||||
<section class="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8 pb-12">
|
||||
<div class="mt-8">
|
||||
<div class="flex items-end justify-between gap-4 mb-4">
|
||||
<h2 class="text-2xl font-bold text-gray-900 dark:text-white">{{ __('app.exercises.similar') }}</h2>
|
||||
<a href="{{ route('exercises.index') }}" class="text-sm font-semibold text-indigo-600 hover:text-indigo-500">{{ __('app.home.browse_all') }}</a>
|
||||
</div>
|
||||
@if($similarExercises->isEmpty())
|
||||
<div class="rounded-2xl border border-dashed border-gray-300 dark:border-gray-700 p-6 text-center text-gray-600 dark:text-gray-300">{{ __('app.exercises.similar_none') }}</div>
|
||||
@else
|
||||
@php $badgePalette = ['bg-rose-100 text-rose-700','bg-amber-100 text-amber-700','bg-emerald-100 text-emerald-700','bg-sky-100 text-sky-700','bg-violet-100 text-violet-700']; @endphp
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
@foreach($similarExercises as $ex)
|
||||
<a href="{{ route('exercises.show', $ex->slug) }}" class="group rounded-3xl overflow-hidden bg-white dark:bg-gray-800 ring-1 ring-gray-200 dark:ring-gray-700 shadow-sm hover:shadow-xl transition">
|
||||
<div class="relative aspect-[16/9] overflow-hidden">
|
||||
<img src="{{ $ex->gif_url ?: 'https://images.unsplash.com/photo-1517836357463-d25dfeac3438?q=80&w=1200&auto=format&fit=crop' }}" alt="{{ $ex->name }}" class="h-full w-full object-cover transition-transform duration-500 group-hover:scale-105">
|
||||
<div class="absolute inset-0 bg-gradient-to-t from-black/50 to-transparent"></div>
|
||||
<div class="absolute bottom-3 left-3 inline-flex items-center rounded-full bg-white/90 px-3 py-1 text-xs font-semibold text-gray-900 shadow">{{ $ex->name }}</div>
|
||||
</div>
|
||||
<div class="p-4">
|
||||
<div class="mt-1 flex flex-wrap gap-2">
|
||||
@foreach($ex->targetMuscles->take(2) as $i => $m)
|
||||
<span class="inline-flex items-center rounded-full px-2.5 py-1 text-xs font-medium {{ $badgePalette[$i % count($badgePalette)] }}">{{ $m->name }}</span>
|
||||
@endforeach
|
||||
@if($ex->bodyParts->count())
|
||||
<span class="inline-flex items-center rounded-full px-2.5 py-1 text-xs font-medium bg-gray-100 text-gray-700 dark:bg-gray-700 dark:text-gray-200">{{ $ex->bodyParts->first()->name }}</span>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
@endforeach
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</section>
|
||||
@endif
|
||||
</x-app-layout>
|
||||
|
||||
@@ -11,41 +11,41 @@
|
||||
<div aria-hidden="true" class="pointer-events-none absolute -bottom-24 -right-24 h-72 w-72 rounded-full bg-cyan-400/30 blur-3xl"></div>
|
||||
|
||||
<div class="relative mx-auto max-w-7xl px-4 sm:px-6 lg:px-8 py-20 sm:py-28 text-center">
|
||||
<span class="inline-flex items-center gap-2 rounded-full bg-white/10 px-3 py-1 text-xs font-semibold text-white ring-1 ring-white/20 backdrop-blur">New • Explore 1,000+ moves</span>
|
||||
<span class="inline-flex items-center gap-2 rounded-full bg-white/10 px-3 py-1 text-xs font-semibold text-white ring-1 ring-white/20 backdrop-blur">{{ __('app.home.badge') }}</span>
|
||||
<h1 class="mt-4 text-4xl sm:text-6xl font-extrabold tracking-tight text-white drop-shadow">
|
||||
Discover 1,000+ Exercises. Build Your Perfect Workout.
|
||||
{{ __('app.home.title') }}
|
||||
</h1>
|
||||
<p class="mt-4 text-lg text-indigo-100 max-w-2xl mx-auto">
|
||||
Explore FitAtlas to find exercises by muscle group and equipment, then craft routines that match your goals.
|
||||
{{ __('app.home.subtitle') }}
|
||||
</p>
|
||||
<!-- Search Bar -->
|
||||
<form action="{{ route('exercises.index') }}" method="GET" class="mt-8 max-w-2xl mx-auto">
|
||||
<div class="flex rounded-2xl shadow-lg ring-1 ring-white/20 focus-within:ring-2 focus-within:ring-fuchsia-400/80 overflow-hidden bg-white/90 backdrop-blur">
|
||||
<input type="text" name="q" placeholder="Search exercises (e.g. push up, dumbbell, back)" class="w-full px-4 py-3 text-gray-900 placeholder-gray-500 bg-transparent focus:outline-none">
|
||||
<button type="submit" class="px-5 py-3 bg-gradient-to-r from-fuchsia-600 to-indigo-600 hover:from-fuchsia-500 hover:to-indigo-500 text-white font-semibold">Search</button>
|
||||
<input type="text" name="q" placeholder="{{ __('app.home.search_placeholder') }}" class="w-full px-4 py-3 text-gray-900 placeholder-gray-500 bg-transparent focus:outline-none">
|
||||
<button type="submit" class="px-5 py-3 bg-gradient-to-r from-fuchsia-600 to-indigo-600 hover:from-fuchsia-500 hover:to-indigo-500 text-white font-semibold">{{ __('app.home.search') }}</button>
|
||||
</div>
|
||||
</form>
|
||||
<!-- Quick Filters -->
|
||||
<div class="mt-6 flex flex-wrap items-center justify-center gap-3">
|
||||
@php
|
||||
$filters = ['Arms', 'Chest', 'Legs', 'Core', 'Back'];
|
||||
$filters = ['arms', 'chest', 'legs', 'core', 'back'];
|
||||
$filterColors = [
|
||||
'Arms' => 'from-amber-400 to-orange-500',
|
||||
'Chest' => 'from-rose-400 to-pink-500',
|
||||
'Legs' => 'from-emerald-400 to-teal-500',
|
||||
'Core' => 'from-sky-400 to-cyan-500',
|
||||
'Back' => 'from-violet-400 to-indigo-500',
|
||||
'arms' => 'from-amber-400 to-orange-500',
|
||||
'chest' => 'from-rose-400 to-pink-500',
|
||||
'legs' => 'from-emerald-400 to-teal-500',
|
||||
'core' => 'from-sky-400 to-cyan-500',
|
||||
'back' => 'from-violet-400 to-indigo-500',
|
||||
];
|
||||
@endphp
|
||||
@foreach($filters as $filter)
|
||||
<a href="{{ route('exercises.index') }}?muscle={{ strtolower($filter) }}" class="inline-flex items-center rounded-full bg-gradient-to-r {{ $filterColors[$filter] }} px-4 py-1.5 text-sm font-semibold text-white shadow hover:opacity-95 focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-white/60">
|
||||
{{ $filter }}
|
||||
<a href="{{ route('exercises.index') }}?muscle={{ $filter }}" class="inline-flex items-center rounded-full bg-gradient-to-r {{ $filterColors[$filter] }} px-4 py-1.5 text-sm font-semibold text-white shadow hover:opacity-95 focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-white/60">
|
||||
{{ __('app.home.filters.' . $filter) }}
|
||||
</a>
|
||||
@endforeach
|
||||
</div>
|
||||
<div class="mt-8">
|
||||
<a href="{{ route('workouts.builder') }}" class="inline-flex items-center gap-2 rounded-xl bg-gradient-to-r from-fuchsia-600 to-indigo-600 px-5 py-3 text-sm sm:text-base font-semibold text-white shadow-lg hover:from-fuchsia-500 hover:to-indigo-500">
|
||||
Build a Workout
|
||||
{{ __('app.nav.build_workout') }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
@@ -54,8 +54,8 @@
|
||||
<!-- Featured Exercises -->
|
||||
<section class="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8 py-12 sm:py-16">
|
||||
<div class="flex items-end justify-between gap-4 mb-6">
|
||||
<h2 class="text-2xl font-bold text-gray-900 dark:text-white">Featured Exercises</h2>
|
||||
<a href="{{ route('exercises.index') }}" class="text-sm font-semibold text-indigo-600 hover:text-indigo-500">Browse all</a>
|
||||
<h2 class="text-2xl font-bold text-gray-900 dark:text-white">{{ __('app.home.featured') }}</h2>
|
||||
<a href="{{ route('exercises.index') }}" class="text-sm font-semibold text-indigo-600 hover:text-indigo-500">{{ __('app.home.browse_all') }}</a>
|
||||
</div>
|
||||
@php
|
||||
$exercises = $exercises ?? [
|
||||
@@ -96,9 +96,9 @@
|
||||
<div class="relative mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
|
||||
<div class="grid grid-cols-1 lg:grid-cols-2 gap-8 items-center">
|
||||
<div>
|
||||
<h2 class="text-2xl sm:text-3xl font-extrabold text-gray-900 dark:text-white">Favorites</h2>
|
||||
<p class="mt-3 text-gray-600 dark:text-gray-300">A balanced routine targeting all major muscle groups. Suitable for most levels and easy to adapt with available equipment.</p>
|
||||
<a href="{{ route('workouts.builder') }}" class="mt-6 inline-flex items-center rounded-xl bg-gradient-to-r from-fuchsia-600 to-indigo-600 px-5 py-3 text-white font-semibold shadow-lg hover:from-fuchsia-500 hover:to-indigo-500">Try This Routine</a>
|
||||
<h2 class="text-2xl sm:text-3xl font-extrabold text-gray-900 dark:text-white">{{ __('app.home.favorites') }}</h2>
|
||||
<p class="mt-3 text-gray-600 dark:text-gray-300">{{ __('app.home.favorites_desc') }}</p>
|
||||
<a href="{{ route('workouts.builder') }}" class="mt-6 inline-flex items-center rounded-xl bg-gradient-to-r from-fuchsia-600 to-indigo-600 px-5 py-3 text-white font-semibold shadow-lg hover:from-fuchsia-500 hover:to-indigo-500">{{ __('app.home.try_routine') }}</a>
|
||||
</div>
|
||||
<div class="relative h-64 sm:h-80">
|
||||
<div class="absolute inset-0 rounded-3xl overflow-hidden ring-1 ring-gray-200 dark:ring-gray-700">
|
||||
@@ -116,10 +116,10 @@
|
||||
<div class="flex flex-col sm:flex-row items-center justify-between gap-4">
|
||||
<div class="text-sm text-gray-600 dark:text-gray-300">© {{ date('Y') }} FitAtlas</div>
|
||||
<div class="flex items-center gap-6 text-sm">
|
||||
<a href="#" class="text-gray-600 dark:text-gray-300 hover:text-gray-900 dark:hover:text-white">About</a>
|
||||
<a href="#" class="text-gray-600 dark:text-gray-300 hover:text-gray-900 dark:hover:text-white">Privacy</a>
|
||||
<a href="#" class="text-gray-600 dark:text-gray-300 hover:text-gray-900 dark:hover:text-white">Terms</a>
|
||||
<a href="#" class="text-gray-600 dark:text-gray-300 hover:text-gray-900 dark:hover:text-white">Contact</a>
|
||||
<a href="#" class="text-gray-600 dark:text-gray-300 hover:text-gray-900 dark:hover:text-white">{{ __('app.home.footer.about') }}</a>
|
||||
<a href="#" class="text-gray-600 dark:text-gray-300 hover:text-gray-900 dark:hover:text-white">{{ __('app.home.footer.privacy') }}</a>
|
||||
<a href="#" class="text-gray-600 dark:text-gray-300 hover:text-gray-900 dark:hover:text-white">{{ __('app.home.footer.terms') }}</a>
|
||||
<a href="#" class="text-gray-600 dark:text-gray-300 hover:text-gray-900 dark:hover:text-white">{{ __('app.home.footer.contact') }}</a>
|
||||
</div>
|
||||
<div class="flex items-center gap-4">
|
||||
<a href="#" aria-label="Twitter" class="text-gray-500 hover:text-gray-900 dark:text-gray-400 dark:hover:text-white">
|
||||
|
||||
@@ -47,5 +47,6 @@
|
||||
{{ $slot }}
|
||||
</main>
|
||||
</div>
|
||||
@stack('scripts')
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -13,9 +13,10 @@
|
||||
|
||||
<!-- Navigation Links -->
|
||||
<div class="hidden sm:flex items-center gap-6">
|
||||
<a href="{{ route('exercises.index') }}" class="text-sm font-medium text-gray-700 dark:text-gray-300 hover:text-gray-900 dark:hover:text-white">Exercises</a>
|
||||
<a href="{{ route('workouts.index', [], false) }}" class="text-sm font-medium text-gray-700 dark:text-gray-300 hover:text-gray-900 dark:hover:text-white">Workouts</a>
|
||||
<a href="{{ route('blog.index', [], false) }}" class="text-sm font-medium text-gray-700 dark:text-gray-300 hover:text-gray-900 dark:hover:text-white">Blog</a>
|
||||
<a href="{{ route('exercises.index') }}" class="text-sm font-medium text-gray-700 dark:text-gray-300 hover:text-gray-900 dark:hover:text-white">{{ __('app.nav.exercises') }}</a>
|
||||
<a href="{{ route('yoga.index') }}" class="text-sm font-medium text-gray-700 dark:text-gray-300 hover:text-gray-900 dark:hover:text-white">{{ __('app.nav.yoga') }}</a>
|
||||
<a href="{{ route('workouts.index', [], false) }}" class="text-sm font-medium text-gray-700 dark:text-gray-300 hover:text-gray-900 dark:hover:text-white">{{ __('app.nav.workouts') }}</a>
|
||||
<a href="{{ route('blog.index', [], false) }}" class="text-sm font-medium text-gray-700 dark:text-gray-300 hover:text-gray-900 dark:hover:text-white">{{ __('app.nav.blog') }}</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -24,7 +25,12 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="h-5 w-5 dark:hidden"><path d="M12 18a6 6 0 1 0 0-12 6 6 0 0 0 0 12zm0 4a1 1 0 0 1-1-1v-1.06a9.01 9.01 0 0 1-6.94-6.94H3a1 1 0 1 1 0-2h1.06A9.01 9.01 0 0 1 11 4.06V3a1 1 0 1 1 2 0v1.06A9.01 9.01 0 0 1 19.94 11H21a1 1 0 1 1 0 2h-1.06A9.01 9.01 0 0 1 13 19.94V21a1 1 0 0 1-1 1z"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="h-5 w-5 hidden dark:block"><path d="M21.64 13.02A9 9 0 1 1 10.98 2.36a1 1 0 0 1 1.3 1.3 7 7 0 1 0 9.05 9.05 1 1 0 0 1 1.3 1.3z"/></svg>
|
||||
</button>
|
||||
<a href="{{ route('workouts.builder') }}" class="inline-flex items-center rounded-md bg-indigo-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-indigo-500">Build a Workout</a>
|
||||
<div class="hidden sm:flex items-center gap-2">
|
||||
<a href="{{ request()->fullUrlWithQuery(['lang' => 'en']) }}" class="text-xs font-semibold text-gray-700 dark:text-gray-300 hover:text-gray-900">EN</a>
|
||||
<span class="text-gray-300">|</span>
|
||||
<a href="{{ request()->fullUrlWithQuery(['lang' => 'ro']) }}" class="text-xs font-semibold text-gray-700 dark:text-gray-300 hover:text-gray-900">RO</a>
|
||||
</div>
|
||||
<a href="{{ route('workouts.builder') }}" class="inline-flex items-center rounded-md bg-indigo-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-indigo-500">{{ __('app.nav.build_workout') }}</a>
|
||||
@auth
|
||||
<x-dropdown align="right" width="48">
|
||||
<x-slot name="trigger">
|
||||
@@ -46,9 +52,9 @@
|
||||
</x-slot>
|
||||
</x-dropdown>
|
||||
@else
|
||||
<a href="{{ route('login') }}" class="text-sm font-medium text-gray-700 dark:text-gray-300 hover:text-gray-900 dark:hover:text-white">Log in</a>
|
||||
<a href="{{ route('login') }}" class="text-sm font-medium text-gray-700 dark:text-gray-300 hover:text-gray-900 dark:hover:text-white">{{ __('app.nav.login') }}</a>
|
||||
@if (Route::has('register'))
|
||||
<a href="{{ route('register') }}" class="text-sm font-semibold text-gray-700 dark:text-gray-200 hover:text-gray-900 dark:hover:text-white">Sign up</a>
|
||||
<a href="{{ route('register') }}" class="text-sm font-semibold text-gray-700 dark:text-gray-200 hover:text-gray-900 dark:hover:text-white">{{ __('app.nav.register') }}</a>
|
||||
@endif
|
||||
@endauth
|
||||
</div>
|
||||
@@ -70,14 +76,21 @@
|
||||
<div class="pt-2 pb-3 space-y-1 border-t border-gray-100 dark:border-gray-800">
|
||||
<div class="px-4 pb-2">
|
||||
<button type="button" aria-label="Toggle dark mode" class="inline-flex items-center gap-2 rounded-md border border-gray-200 dark:border-gray-700 px-3 py-2 text-sm text-gray-700 dark:text-gray-200 bg-white dark:bg-gray-900" onclick="(function(){const c=document.documentElement.classList;const now=c.toggle('dark');try{localStorage.setItem('theme', now?'dark':'light');}catch(e){}})()">
|
||||
<span class="dark:hidden">Enable Dark Mode</span>
|
||||
<span class="hidden dark:inline">Disable Dark Mode</span>
|
||||
<span class="dark:hidden">{{ __('app.nav.enable_dark') }}</span>
|
||||
<span class="hidden dark:inline">{{ __('app.nav.disable_dark') }}</span>
|
||||
</button>
|
||||
<div class="mt-3 px-4 flex items-center gap-3">
|
||||
<span class="text-xs text-gray-500">{{ __('app.nav.language') }}:</span>
|
||||
<a href="{{ request()->fullUrlWithQuery(['lang' => 'en']) }}" class="text-xs font-semibold text-gray-700 dark:text-gray-300 hover:text-gray-900">EN</a>
|
||||
<span class="text-gray-300">|</span>
|
||||
<a href="{{ request()->fullUrlWithQuery(['lang' => 'ro']) }}" class="text-xs font-semibold text-gray-700 dark:text-gray-300 hover:text-gray-900">RO</a>
|
||||
</div>
|
||||
</div>
|
||||
<x-responsive-nav-link :href="route('exercises.index')">Exercises</x-responsive-nav-link>
|
||||
<x-responsive-nav-link :href="route('workouts.index', [], false)">Workouts</x-responsive-nav-link>
|
||||
<x-responsive-nav-link :href="route('blog.index', [], false)">Blog</x-responsive-nav-link>
|
||||
<x-responsive-nav-link :href="route('workouts.builder')">Build a Workout</x-responsive-nav-link>
|
||||
<x-responsive-nav-link :href="route('exercises.index')">{{ __('app.nav.exercises') }}</x-responsive-nav-link>
|
||||
<x-responsive-nav-link :href="route('yoga.index')">{{ __('app.nav.yoga') }}</x-responsive-nav-link>
|
||||
<x-responsive-nav-link :href="route('workouts.index', [], false)">{{ __('app.nav.workouts') }}</x-responsive-nav-link>
|
||||
<x-responsive-nav-link :href="route('blog.index', [], false)">{{ __('app.nav.blog') }}</x-responsive-nav-link>
|
||||
<x-responsive-nav-link :href="route('workouts.builder')">{{ __('app.nav.build_workout') }}</x-responsive-nav-link>
|
||||
</div>
|
||||
<div class="pt-4 pb-1 border-t border-gray-200 dark:border-gray-800">
|
||||
@auth
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
<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>
|
||||
<h1 class="text-2xl sm:text-3xl font-extrabold tracking-tight text-gray-900 dark:text-white">My Workouts</h1>
|
||||
<p class="mt-2 text-gray-600 dark:text-gray-300">Create routines and manage your exercises.</p>
|
||||
<h1 class="text-2xl sm:text-3xl font-extrabold tracking-tight text-gray-900 dark:text-white">{{ __('app.workouts.my_workouts') }}</h1>
|
||||
<p class="mt-2 text-gray-600 dark:text-gray-300">{{ __('app.workouts.manage_desc') }}</p>
|
||||
</div>
|
||||
<a href="{{ route('workouts.create') }}" 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">New Workout</a>
|
||||
<a href="{{ route('workouts.create') }}" 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">{{ __('app.workouts.new_workout') }}</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
@@ -16,7 +16,7 @@
|
||||
<div class="mb-4 rounded-xl bg-emerald-50 text-emerald-700 px-4 py-3">{{ session('status') }}</div>
|
||||
@endif
|
||||
@if($workouts->count() === 0)
|
||||
<div class="rounded-2xl border border-dashed border-gray-300 dark:border-gray-700 p-8 text-center text-gray-600 dark:text-gray-300">No workouts yet. Create your first workout.</div>
|
||||
<div class="rounded-2xl border border-dashed border-gray-300 dark:border-gray-700 p-8 text-center text-gray-600 dark:text-gray-300">{{ __('app.workouts.none_yet') }}</div>
|
||||
@else
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
@foreach($workouts as $workout)
|
||||
@@ -31,11 +31,11 @@
|
||||
@endif
|
||||
</div>
|
||||
<div class="mt-4 flex items-center justify-between">
|
||||
<a href="{{ route('workouts.show', $workout) }}" class="text-sm font-semibold text-indigo-600 hover:text-indigo-500">Open</a>
|
||||
<form method="POST" action="{{ route('workouts.destroy', $workout) }}" onsubmit="return confirm('Delete this workout?');">
|
||||
<a href="{{ route('workouts.show', $workout) }}" class="text-sm font-semibold text-indigo-600 hover:text-indigo-500">{{ __('app.workouts.open') }}</a>
|
||||
<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">Delete</button>
|
||||
<button type="submit" class="text-sm text-red-600 hover:text-red-500">{{ __('app.workouts.delete') }}</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,125 @@
|
||||
<x-app-layout>
|
||||
<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>
|
||||
<a href="{{ route('workouts.show', $workout) }}" class="text-sm text-gray-600 dark:text-gray-300 hover:text-gray-900 dark:hover:text-white">← {{ __('app.sessions.back_to_workout') }}</a>
|
||||
<h1 class="mt-2 text-2xl sm:text-3xl font-extrabold tracking-tight text-gray-900 dark:text-white">{{ $workout->title }}</h1>
|
||||
<p class="mt-1 text-gray-600 dark:text-gray-300">{{ __('app.sessions.progress') }}: {{ $doneByItemId->where('done', true)->count() }} / {{ $workout->items->count() }}</p>
|
||||
</div>
|
||||
<form method="POST" action="{{ route('sessions.complete', $session) }}" onsubmit="return confirm('{{ __('app.sessions.mark_complete_confirm') }}');">
|
||||
@csrf
|
||||
<button type="submit" class="inline-flex items-center rounded-xl bg-gradient-to-r from-indigo-600 to-fuchsia-600 px-4 py-2 text-sm font-semibold text-white shadow hover:from-indigo-500 hover:to-fuchsia-500" title="{{ __('app.sessions.mark_complete') }}" aria-label="{{ __('app.sessions.mark_complete') }}">{{ __('app.sessions.mark_complete') }}</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8 py-8">
|
||||
<div class="rounded-2xl ring-1 ring-gray-200 dark:ring-gray-700 bg-white dark:bg-gray-800 p-5">
|
||||
@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">{{ __('app.sessions.no_items') }}</div>
|
||||
@else
|
||||
<ul class="mt-1 space-y-3">
|
||||
@foreach($workout->items as $item)
|
||||
@php $si = $doneByItemId->get($item->id); $isDone = $si && $si->done; @endphp
|
||||
<li class="rounded-xl ring-1 ring-gray-200 dark:ring-gray-700 bg-white dark:bg-gray-900 p-4 flex items-start justify-between">
|
||||
<div class="flex items-start gap-3">
|
||||
<form method="POST" action="{{ route('sessions.items.toggle', [$session, $item]) }}" class="session-toggle-form" data-item-id="{{ $item->id }}" data-initial-done="{{ $isDone ? '1' : '0' }}">
|
||||
@csrf
|
||||
@method('PATCH')
|
||||
<button type="submit" class="h-6 w-6 rounded-md border flex items-center justify-center transition-colors duration-150"
|
||||
aria-pressed="{{ $isDone ? 'true' : 'false' }}"
|
||||
title="{{ $isDone ? __('app.sessions.mark_complete') : __('app.sessions.progress') }}">
|
||||
<span class="sr-only">Toggle</span>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="h-4 w-4 {{ $isDone ? 'text-white' : 'text-transparent' }}">
|
||||
<path fill-rule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-7.5 7.5a1 1 0 01-1.414 0l-3-3a1 1 0 111.414-1.414L8.5 12.086l6.793-6.793a1 1 0 011.414 0z" clip-rule="evenodd" />
|
||||
</svg>
|
||||
</button>
|
||||
</form>
|
||||
<div>
|
||||
<div class="exercise-title text-base font-semibold {{ $isDone ? 'line-through text-gray-500 dark:text-gray-400' : 'text-gray-900 dark:text-white' }}">{{ $item->exercise->name }}</div>
|
||||
<div class="mt-1 text-sm text-gray-600 dark:text-gray-300">
|
||||
@if($item->sets) {{ $item->sets }} {{ __('app.sessions.sets') }} @endif
|
||||
@if($item->reps) · {{ $item->reps }} {{ __('app.sessions.reps') }} @endif
|
||||
@if($item->weight) · {{ $item->weight }} kg @endif
|
||||
@if($item->rest_seconds) · {{ $item->rest_seconds }}s {{ __('app.sessions.rest') }} @endif
|
||||
</div>
|
||||
@if($item->notes)
|
||||
<div class="mt-2 text-sm text-gray-600 dark:text-gray-300">{{ $item->notes }}</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
@endif
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@push('scripts')
|
||||
<script>
|
||||
(function(){
|
||||
function updateBtnState(btn, done) {
|
||||
btn.setAttribute('aria-pressed', done ? 'true' : 'false');
|
||||
// Background/border colors
|
||||
if (done) {
|
||||
btn.classList.add('bg-emerald-500','border-emerald-600','text-white');
|
||||
btn.classList.remove('border-gray-300','dark:border-gray-700');
|
||||
} else {
|
||||
btn.classList.remove('bg-emerald-500','border-emerald-600','text-white');
|
||||
btn.classList.add('border-gray-300','dark:border-gray-700');
|
||||
}
|
||||
const icon = btn.querySelector('svg');
|
||||
if (icon) {
|
||||
icon.classList.toggle('text-white', !!done);
|
||||
icon.classList.toggle('text-transparent', !done);
|
||||
}
|
||||
// Also toggle line-through for exercise name in the same item row
|
||||
const row = btn.closest('li');
|
||||
if (row) {
|
||||
const titleEl = row.querySelector('.exercise-title');
|
||||
if (titleEl) {
|
||||
if (done) {
|
||||
titleEl.classList.add('line-through','text-gray-500','dark:text-gray-400');
|
||||
titleEl.classList.remove('text-gray-900','dark:text-white');
|
||||
} else {
|
||||
titleEl.classList.remove('line-through','text-gray-500','dark:text-gray-400');
|
||||
titleEl.classList.add('text-gray-900','dark:text-white');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener('click', function(e){
|
||||
const btn = e.target.closest('.session-toggle-form button[type="submit"]');
|
||||
if (!btn) return;
|
||||
const form = btn.closest('form.session-toggle-form');
|
||||
if (!form) return;
|
||||
e.preventDefault();
|
||||
|
||||
// Optimistically toggle UI
|
||||
const wasDone = btn.getAttribute('aria-pressed') === 'true';
|
||||
const next = !wasDone;
|
||||
updateBtnState(btn, next);
|
||||
|
||||
fetch(form.action, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'X-Requested-With': 'XMLHttpRequest',
|
||||
'X-CSRF-TOKEN': form.querySelector('input[name="_token"]').value,
|
||||
'Accept': 'text/html',
|
||||
},
|
||||
body: new URLSearchParams(new FormData(form))
|
||||
}).then(function(res){
|
||||
if (!res.ok) throw new Error('Network response was not ok');
|
||||
}).catch(function(){
|
||||
// Revert on error
|
||||
updateBtnState(btn, wasDone);
|
||||
alert('Failed to update. Please try again.');
|
||||
});
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
@endpush
|
||||
</x-app-layout>
|
||||
@@ -25,11 +25,19 @@
|
||||
</div>
|
||||
<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>
|
||||
<form method="POST" action="{{ route('workouts.destroy', $workout) }}" onsubmit="return confirm('Delete this workout?');">
|
||||
@csrf
|
||||
@method('DELETE')
|
||||
<button type="submit" class="text-sm text-red-600 hover:text-red-500">Delete</button>
|
||||
</form>
|
||||
<div class="flex items-center gap-3">
|
||||
@if(Auth::id() === $workout->user_id)
|
||||
<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>
|
||||
<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>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
<x-app-layout>
|
||||
<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-start justify-between flex-col lg:flex-row gap-6">
|
||||
<div>
|
||||
<h1 class="text-2xl sm:text-3xl font-extrabold tracking-tight text-gray-900 dark:text-white">{{ __('app.yoga.browse') }}</h1>
|
||||
<p class="mt-2 text-gray-600 dark:text-gray-300">{{ __('app.yoga.browse_desc') }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8 py-6">
|
||||
<div class="grid grid-cols-1 lg:grid-cols-12 gap-6">
|
||||
<aside class="lg:col-span-12">
|
||||
<form action="{{ route('yoga.index') }}" method="GET" class="space-y-4 lg:space-y-0 lg:flex lg:flex-wrap lg:items-end lg:gap-4 bg-white dark:bg-gray-800 rounded-2xl p-4 ring-1 ring-gray-200 dark:ring-gray-700">
|
||||
<div class="w-full lg:w-72">
|
||||
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300">{{ __('app.yoga.search') }}</label>
|
||||
<input type="text" name="q" value="{{ $filters['q'] ?? '' }}" placeholder="{{ __('app.yoga.search_placeholder') }}" 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>
|
||||
<div class="w-full lg:w-56">
|
||||
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300">{{ __('app.yoga.category') }}</label>
|
||||
<select name="category" 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 focus:border-indigo-500 focus:ring-indigo-500 px-3 py-2">
|
||||
<option value="0">{{ __('app.yoga.any') }}</option>
|
||||
@foreach($categories as $c)
|
||||
<option value="{{ $c->id }}" @selected(($filters['category'] ?? 0) == $c->id)>{{ $c->name }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
<div class="w-full lg:w-72">
|
||||
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300">{{ __('app.yoga.benefit') }}</label>
|
||||
<input type="text" name="benefit" value="{{ $filters['benefit'] ?? '' }}" placeholder="{{ __('app.yoga.benefit_placeholder') }}" 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>
|
||||
<div class="w-full lg:w-auto flex items-center justify-between lg:justify-end gap-2 lg:ml-auto">
|
||||
<a href="{{ route('yoga.index') }}" class="text-sm text-gray-600 dark:text-gray-300 hover:text-gray-900 dark:hover:text-white">{{ __('app.yoga.reset') }}</a>
|
||||
<button type="submit" 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">{{ __('app.yoga.apply_filters') }}</button>
|
||||
</div>
|
||||
</form>
|
||||
</aside>
|
||||
|
||||
<div class="lg:col-span-12">
|
||||
<div class="flex items-center justify-between mb-4">
|
||||
<p class="text-sm text-gray-600 dark:text-gray-300">{{ __('app.yoga.showing') }} <span class="font-medium">{{ $poses->total() }}</span> {{ __('app.yoga.poses_noun') }}</p>
|
||||
<div class="flex items-center gap-2">
|
||||
<label class="text-sm text-gray-600 dark:text-gray-300">{{ __('app.exercises.per_page') }}</label>
|
||||
<select name="per_page" form="per-page-form" class="rounded-lg border-gray-300 dark:border-gray-700 bg-white/80 dark:bg-gray-900 text-gray-900 dark:text-gray-100 focus:border-indigo-500 focus:ring-indigo-500 px-2 py-1">
|
||||
@foreach([12, 24, 36] as $n)
|
||||
<option value="{{ $n }}" @selected(($filters['per_page'] ?? 12) == $n)>{{ $n }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
<form id="per-page-form" action="{{ route('yoga.index') }}" method="GET" class="hidden">
|
||||
@foreach(request()->except('per_page') as $k => $v)
|
||||
<input type="hidden" name="{{ $k }}" value="{{ $v }}">
|
||||
@endforeach
|
||||
<input type="hidden" name="per_page" value="{{ $filters['per_page'] ?? 12 }}">
|
||||
</form>
|
||||
<script>
|
||||
document.addEventListener('change', (e) => {
|
||||
if (e.target && e.target.name === 'per_page') {
|
||||
const form = document.getElementById('per-page-form');
|
||||
form.querySelector('input[name="per_page"]').value = e.target.value;
|
||||
form.submit();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if($poses->count() === 0)
|
||||
<div class="rounded-2xl border border-dashed border-gray-300 dark:border-gray-700 p-8 text-center text-gray-600 dark:text-gray-300">{{ __('app.yoga.none_found') }}</div>
|
||||
@else
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
@foreach($poses as $pose)
|
||||
<a href="{{ route('yoga.show', $pose->slug) }}" class="group rounded-3xl overflow-hidden bg-white dark:bg-gray-800 ring-1 ring-gray-200 dark:ring-gray-700 shadow-sm hover:shadow-xl transition">
|
||||
<div class="relative aspect-[16/9] overflow-hidden bg-white">
|
||||
@php $img = $pose->url_png ?: $pose->url_svg ?: $pose->url_svg_alt; @endphp
|
||||
@if($img)
|
||||
<img src="{{ $img }}" alt="{{ $pose->english_name }}" class="h-full w-full object-contain p-4 transition-transform duration-500 group-hover:scale-105">
|
||||
@else
|
||||
<div class="h-full w-full flex items-center justify-center text-gray-400">{{ __('app.yoga.no_image') }}</div>
|
||||
@endif
|
||||
<div class="absolute inset-0 bg-gradient-to-t from-black/10 to-transparent"></div>
|
||||
<div class="absolute bottom-3 left-3 inline-flex items-center rounded-full bg-white/90 px-3 py-1 text-xs font-semibold text-gray-900 shadow">{{ $pose->english_name }}</div>
|
||||
</div>
|
||||
<div class="p-4">
|
||||
<div class="text-sm text-gray-600 dark:text-gray-300">{{ optional($pose->category)->name }}</div>
|
||||
@if($pose->sanskrit_name)
|
||||
<div class="mt-1 text-xs text-gray-500 italic">{{ $pose->sanskrit_name }}</div>
|
||||
@endif
|
||||
</div>
|
||||
</a>
|
||||
@endforeach
|
||||
</div>
|
||||
|
||||
<div class="mt-6">{{ $poses->links() }}</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</x-app-layout>
|
||||
@@ -0,0 +1,124 @@
|
||||
<x-app-layout>
|
||||
<section class="relative overflow-hidden">
|
||||
<div class="absolute inset-0">
|
||||
@php $img = $pose->url_png ?: $pose->url_svg ?: $pose->url_svg_alt; @endphp
|
||||
@if($img)
|
||||
<img src="{{ $img }}" alt="{{ $pose->english_name }}" class="h-full w-full object-contain bg-white">
|
||||
@else
|
||||
<div class="h-full w-full bg-gray-100 dark:bg-gray-800"></div>
|
||||
@endif
|
||||
<div class="absolute inset-0 bg-gradient-to-b from-indigo-900/40 via-indigo-800/30 to-transparent"></div>
|
||||
</div>
|
||||
<div class="relative mx-auto max-w-7xl px-4 sm:px-6 lg:px-8 py-12 sm:py-20">
|
||||
<a href="{{ url()->previous() ?? route('yoga.index') }}" class="inline-flex items-center text-sm font-medium text-white/90 hover:text-white">← {{ __('app.yoga.back') }}</a>
|
||||
<div class="mt-3 flex items-start justify-between gap-4">
|
||||
<h1 class="text-3xl sm:text-5xl font-extrabold tracking-tight text-white drop-shadow">{{ $pose->english_name }}</h1>
|
||||
</div>
|
||||
<div class="mt-4 flex flex-wrap items-center gap-2">
|
||||
@if($pose->category)
|
||||
<span class="inline-flex items-center rounded-full bg-white/90 px-3 py-1 text-xs font-semibold text-gray-900 shadow">{{ $pose->category->name }}</span>
|
||||
@endif
|
||||
@if($pose->sanskrit_name)
|
||||
<span class="inline-flex items-center rounded-full bg-emerald-100 text-emerald-700 px-2.5 py-1 text-xs font-semibold">Sanskrit: {{ $pose->sanskrit_name }}</span>
|
||||
@endif
|
||||
@if($pose->sanskrit_name_adapted)
|
||||
<span class="inline-flex items-center rounded-full bg-sky-100 text-sky-700 px-2.5 py-1 text-xs font-semibold">{{ $pose->sanskrit_name_adapted }}</span>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8 py-10">
|
||||
<div class="grid grid-cols-1 lg:grid-cols-12 gap-8">
|
||||
<div class="lg:col-span-8">
|
||||
<div class="rounded-3xl overflow-hidden ring-1 ring-gray-200 dark:ring-gray-700 bg-white dark:bg-gray-800 p-6">
|
||||
<h2 class="text-lg font-semibold text-gray-900 dark:text-white">{{ __('app.yoga.description') }}</h2>
|
||||
<div class="prose prose-indigo dark:prose-invert max-w-none mt-3">
|
||||
@php $desc = trim((string) $pose->description); @endphp
|
||||
@if($desc !== '')
|
||||
@foreach(preg_split('/\r?\n+/', $desc) as $line)
|
||||
@if(trim($line) !== '')<p>{{ $line }}</p>@endif
|
||||
@endforeach
|
||||
@else
|
||||
<p>{{ __('app.yoga.no_description') }}</p>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-6 rounded-3xl overflow-hidden ring-1 ring-gray-200 dark:ring-gray-700 bg-white dark:bg-gray-800 p-6">
|
||||
<h2 class="text-lg font-semibold text-gray-900 dark:text-white">{{ __('app.yoga.benefits') }}</h2>
|
||||
<div class="prose prose-indigo dark:prose-invert max-w-none mt-3">
|
||||
@php $benef = trim((string) $pose->benefits); @endphp
|
||||
@if($benef !== '')
|
||||
@foreach(preg_split('/\r?\n+/', $benef) as $line)
|
||||
@if(trim($line) !== '')<p>{{ $line }}</p>@endif
|
||||
@endforeach
|
||||
@else
|
||||
<p>{{ __('app.yoga.no_benefits') }}</p>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="lg:col-span-4">
|
||||
<div class="rounded-2xl p-5 ring-1 ring-gray-200 dark:ring-gray-700 bg-white dark:bg-gray-800">
|
||||
<h3 class="text-sm font-semibold text-gray-900 dark:text-white">{{ __('app.yoga.pose_info') }}</h3>
|
||||
<dl class="mt-3 space-y-2 text-sm text-gray-700 dark:text-gray-300">
|
||||
@if($pose->translation_name)
|
||||
<div class="flex justify-between"><dt>{{ __('app.yoga.translation') }}</dt><dd class="text-right ms-3">{{ $pose->translation_name }}</dd></div>
|
||||
@endif
|
||||
@if($pose->sanskrit_name)
|
||||
<div class="flex justify-between"><dt>{{ __('app.yoga.sanskrit') }}</dt><dd class="text-right ms-3">{{ $pose->sanskrit_name }}</dd></div>
|
||||
@endif
|
||||
@if($pose->sanskrit_name_adapted)
|
||||
<div class="flex justify-between"><dt>{{ __('app.yoga.sanskrit_adapted') }}</dt><dd class="text-right ms-3">{{ $pose->sanskrit_name_adapted }}</dd></div>
|
||||
@endif
|
||||
@if($pose->category)
|
||||
<div class="flex justify-between"><dt>{{ __('app.yoga.category') }}</dt><dd class="text-right ms-3">{{ $pose->category->name }}</dd></div>
|
||||
@endif
|
||||
</dl>
|
||||
<div class="mt-5">
|
||||
<a href="{{ route('yoga.index') }}" 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">{{ __('app.yoga.browse_all') }}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@isset($similarPoses)
|
||||
<section class="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8 pb-12">
|
||||
<div class="mt-8">
|
||||
<div class="flex items-end justify-between gap-4 mb-4">
|
||||
<h2 class="text-2xl font-bold text-gray-900 dark:text-white">{{ __('app.yoga.similar') }}</h2>
|
||||
<a href="{{ route('yoga.index') }}" class="text-sm font-semibold text-indigo-600 hover:text-indigo-500">{{ __('app.yoga.browse_all') }}</a>
|
||||
</div>
|
||||
@if($similarPoses->isEmpty())
|
||||
<div class="rounded-2xl border border-dashed border-gray-300 dark:border-gray-700 p-6 text-center text-gray-600 dark:text-gray-300">{{ __('app.yoga.similar_none') }}</div>
|
||||
@else
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
@foreach($similarPoses as $p)
|
||||
<a href="{{ route('yoga.show', $p->slug) }}" class="group rounded-3xl overflow-hidden bg-white dark:bg-gray-800 ring-1 ring-gray-200 dark:ring-gray-700 shadow-sm hover:shadow-xl transition">
|
||||
<div class="relative aspect-[16/9] overflow-hidden bg-white">
|
||||
@php $img = $p->url_png ?: $p->url_svg ?: $p->url_svg_alt; @endphp
|
||||
@if($img)
|
||||
<img src="{{ $img }}" alt="{{ $p->english_name }}" class="h-full w-full object-contain transition-transform duration-500 group-hover:scale-105">
|
||||
@else
|
||||
<div class="h-full w-full flex items-center justify-center text-sm text-gray-500">{{ __('app.yoga.no_image') }}</div>
|
||||
@endif
|
||||
<div class="absolute inset-0 bg-gradient-to-t from-black/20 to-transparent"></div>
|
||||
<div class="absolute bottom-3 left-3 inline-flex items-center rounded-full bg-white/90 px-3 py-1 text-xs font-semibold text-gray-900 shadow">{{ $p->english_name }}</div>
|
||||
</div>
|
||||
<div class="p-4">
|
||||
<div class="mt-1 flex flex-wrap gap-2">
|
||||
@if($p->category)
|
||||
<span class="inline-flex items-center rounded-full px-2.5 py-1 text-xs font-medium bg-gray-100 text-gray-700 dark:bg-gray-700 dark:text-gray-200">{{ $p->category->name }}</span>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
@endforeach
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</section>
|
||||
@endisset
|
||||
</x-app-layout>
|
||||
Reference in New Issue
Block a user