File diff suppressed because one or more lines are too long
@@ -1,3 +1,3 @@
|
||||
<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><meta name="csrf-token" content="{{ csrf_token() }}"><title>@yield('title','Cresta 31 | Lodge escapes')</title><meta name="description" content="Escape to Cresta 31. Discover the lodge, check availability and request your stay."><link rel="preconnect" href="https://fonts.googleapis.com"><link rel="preconnect" href="https://fonts.gstatic.com" crossorigin><link href="https://fonts.googleapis.com/css2?family=DM+Sans:wght@400;500;600;700&family=Playfair+Display:wght@500;600&display=swap" rel="stylesheet"><style>
|
||||
:root{--ink:#1b312b;--muted:#697b70;--sand:#f6f3eb;--gold:#c49b5a;--white:#fff}*{box-sizing:border-box}body{margin:0;background:var(--sand);color:var(--ink);font-family:'DM Sans',sans-serif}a{color:inherit;text-decoration:none}h1,h2,h3{font-family:'Playfair Display',serif;font-weight:500}header{position:absolute;top:0;left:0;right:0;z-index:2;color:#fff;display:flex;align-items:center;justify-content:space-between;padding:25px max(5vw,24px)}header .brand{font-family:'Playfair Display',serif;font-size:30px}header nav{display:flex;gap:25px;align-items:center}header a.button{background:#fff;color:var(--ink)}.button,button{display:inline-block;background:var(--ink);color:white;border:0;padding:15px 24px;border-radius:3px;font:600 14px 'DM Sans';cursor:pointer}.button:hover,button:hover{opacity:.88}.wrap{max-width:1200px;margin:auto;padding:0 24px}.hero{height:min(85vh,780px);min-height:530px;position:relative;display:flex;align-items:center;background:#233d34 url('/images/lodge/hero/photo-01.webp') center/cover;color:#fff}.hero:before{content:'';position:absolute;inset:0;background:linear-gradient(90deg,rgba(12,30,24,.7),rgba(12,30,24,.08))}.hero .content{position:relative;max-width:750px;padding:80px 0}.eyebrow{text-transform:uppercase;letter-spacing:.24em;font-size:12px;font-weight:700}.hero h1{font-size:clamp(50px,7vw,95px);line-height:1.05;margin:20px 0}.hero p{font-size:18px;max-width:480px;line-height:1.8}.section{padding:85px 0}.section h2{font-size:clamp(34px,4vw,54px);margin:10px 0 25px}.intro{display:grid;grid-template-columns:1fr 1fr;gap:70px;align-items:center}.intro p{line-height:1.9;color:var(--muted)}.intro img{width:100%;height:480px;object-fit:cover}.gallery{display:grid;grid-template-columns:repeat(3,1fr);gap:14px}.gallery img{width:100%;height:275px;object-fit:cover;border-radius:3px}.booking{background:#fff;padding:34px;box-shadow:0 14px 60px #172e2220;border-radius:5px}.booking-grid{display:grid;grid-template-columns:repeat(3,1fr) auto;gap:15px;align-items:end}label{display:block;font-size:12px;text-transform:uppercase;letter-spacing:.12em;font-weight:700;margin-bottom:9px}input,select,textarea{display:block;width:100%;border:1px solid #d5ddd7;padding:14px;font:inherit;border-radius:3px;background:#fff;color:var(--ink)}.small{color:var(--muted);font-size:14px}.card{background:#fff;padding:36px;border-radius:5px;max-width:760px;margin:auto}.card h1{font-size:46px}.form-grid{display:grid;grid-template-columns:1fr 1fr;gap:18px}.full{grid-column:1/-1}.errors{background:#fff0ed;color:#8c3024;padding:16px;margin:16px 0}.subhero{background:var(--ink);color:#fff;padding:155px 24px 65px}.subhero h1{font-size:54px;margin:0}footer{background:#142820;color:#fff;padding:45px 0;margin-top:30px}footer .wrap{display:flex;justify-content:space-between}.price{font-size:30px;font-family:'Playfair Display',serif}table{border-collapse:collapse;width:100%;background:white}td,th{padding:13px;text-align:left;border-bottom:1px solid #ddd}@media(max-width:760px){header nav a:not(.button){display:none}.intro{grid-template-columns:1fr;gap:25px}.gallery{grid-template-columns:repeat(2,1fr)}.gallery img{height:180px}.booking-grid,.form-grid{grid-template-columns:1fr}.hero{min-height:600px}.section{padding:55px 0}footer .wrap{display:block}}
|
||||
</style></head><body><header><a class="brand" href="{{ route('home') }}">Cresta 31<span style="color:#d9b985">.</span></a><nav><a href="{{ route('home') }}#the-lodge">The lodge</a><a href="{{ route('home') }}#gallery">Gallery</a><a class="button" href="{{ route('home') }}#book">Check availability</a></nav></header>@yield('content')<footer><div class="wrap"><div><strong>Cresta 31</strong><p class="small">Your next lodge escape starts here.</p></div><div>© {{ date('Y') }} Cresta 31</div></div></footer></body></html>
|
||||
</style></head><body><header><a class="brand" href="{{ route('home') }}">Cresta 31<span style="color:#d9b985">.</span></a><nav><a href="{{ route('home') }}#the-lodge">The lodge</a><a href="{{ route('home') }}#gallery">Gallery</a><a class="button" href="{{ route('home') }}#book">Check availability</a>@auth @if(filled(config('lodge.admin_email')) && strcasecmp(trim(auth()->user()->email),trim(config('lodge.admin_email')))===0)<a href="{{ route('admin.dashboard') }}" style="border:1px solid rgba(255,255,255,.8);padding:10px 15px;border-radius:4px;font-weight:700">Admin dashboard →</a>@endif @endauth</nav></header>@yield('content')<footer><div class="wrap"><div><strong>Cresta 31</strong><p class="small">Your next lodge escape starts here.</p></div><div>© {{ date('Y') }} Cresta 31</div></div></footer></body></html>
|
||||
@@ -0,0 +1 @@
|
||||
@extends('lodge.admin-layout')@section('title','Dashboard')@section('section','Dashboard')@section('content')<div class="page-heading"><div><div class="eyebrow">LODGE OVERVIEW</div><h1>Dashboard</h1><p>Welcome back. Here is what is happening at Cresta 31.</p></div><a class="btn primary" href="{{ route('lodge.admin') }}">Manage bookings →</a></div><div class="stat-grid"><div class="stat"><span>Total bookings</span><strong>{{ $total }}</strong><small>All-time requests</small></div><div class="stat"><span>Pending requests</span><strong>{{ $pending }}</strong><small>Awaiting review</small></div><div class="stat"><span>Confirmed stays</span><strong>{{ $confirmed }}</strong><small>Approved bookings</small></div><div class="stat"><span>Gallery photos</span><strong>{{ $photoCount }}</strong><small>Uploaded images</small></div></div><div class="panel"><div class="panel-head"><div><h2>Recent bookings</h2><p>Latest booking requests</p></div><a class="text-link" href="{{ route('lodge.admin') }}">View all →</a></div><div class="table-scroll"><table><thead><tr><th>Reference</th><th>Guest</th><th>Stay</th><th>Status</th><th>Total</th></tr></thead><tbody>@forelse($recent as $b)<tr><td><strong>{{ $b->reference }}</strong></td><td>{{ $b->guest_name }}</td><td>{{ $b->check_in->format('d M') }} – {{ $b->check_out->format('d M Y') }}</td><td><span class="badge {{ $b->status }}">{{ ucfirst($b->status) }}</span></td><td>£{{ number_format($b->total_pence / 100,2) }}</td></tr>@empty<tr><td colspan="5" class="empty">No bookings yet.</td></tr>@endforelse</tbody></table></div></div><div class="quick-grid"><a class="quick" href="{{ route('lodge.admin') }}"><span>▤</span><strong>Manage bookings</strong><small>Review and update guest reservations →</small></a><a class="quick" href="{{ route('admin.photos') }}"><span>▧</span><strong>Manage photographs</strong><small>Upload and organise lodge images →</small></a><a class="quick" href="{{ route('home') }}" target="_blank"><span>↗</span><strong>Visit public website</strong><small>Open Cresta 31 in a new tab →</small></a></div>@endsection
|
||||
@@ -1,9 +1 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><meta name="csrf-token" content="{{ csrf_token() }}"><title>@yield('title', 'Administration') · Cresta 31</title><link rel="stylesheet" href="{{ asset('css/cresta-admin.css') }}"></head>
|
||||
<body><div class="admin-shell" id="admin-shell"><aside class="sidebar" id="sidebar"><a class="brand" href="{{ route('lodge.admin') }}"><span class="brand-mark">31</span><span><strong>CRESTA 31</strong><small>MANAGEMENT SUITE</small></span></a><div class="nav-caption">WORKSPACE</div><nav class="side-nav" aria-label="Administration">
|
||||
<a class="nav-link {{ request()->routeIs('dashboard') || request()->routeIs('lodge.admin') && !request()->has('section') ? 'active' : '' }}" href="{{ route('lodge.admin') }}"><span class="nav-icon">▦</span>Overview</a>
|
||||
<a class="nav-link {{ request()->routeIs('lodge.admin') && request('section') === 'bookings' ? 'active' : '' }}" href="{{ route('lodge.admin', ['section'=>'bookings']) }}"><span class="nav-icon">▤</span>Bookings</a>
|
||||
<a class="nav-link {{ request()->routeIs('lodge.admin') && request('section') === 'photos' ? 'active' : '' }}" href="{{ route('lodge.admin', ['section'=>'photos']) }}"><span class="nav-icon">▧</span>Photographs</a>
|
||||
@if(Route::has('admin.modules.index'))<a class="nav-link {{ request()->routeIs('admin.modules.*') ? 'active' : '' }}" href="{{ route('admin.modules.index') }}"><span class="nav-icon">▥</span>Modules</a>@elseif(Route::has('modules.admin.index'))<a class="nav-link" href="{{ route('modules.admin.index') }}"><span class="nav-icon">▥</span>Modules</a>@endif
|
||||
</nav><div class="nav-caption">ACCOUNT</div><nav class="side-nav"><a class="nav-link" href="{{ url('/') }}" target="_blank" rel="noopener"><span class="nav-icon">↗</span>View website</a>@if(Route::has('profile.edit'))<a class="nav-link" href="{{ route('profile.edit') }}"><span class="nav-icon">⚙</span>Profile & settings</a>@endif</nav><div class="sidebar-bottom"><span class="status-dot"></span> Site administration <small>CRESTA 31 · {{ date('Y') }}</small></div></aside>
|
||||
<div class="main-area"><header class="topbar"><div class="topbar-left"><button class="menu-toggle" type="button" aria-label="Toggle navigation" aria-controls="sidebar" aria-expanded="false" onclick="var s=document.getElementById('admin-shell');s.classList.toggle('menu-open');this.setAttribute('aria-expanded',s.classList.contains('menu-open'))">☰</button><div class="crumb">Cresta 31 <span>/</span> <strong>@yield('section-label','Overview')</strong></div></div><div class="topbar-right"><span class="site-pill"><span class="status-dot"></span> Site online</span><div class="avatar">{{ strtoupper(substr(auth()->user()->name ?? auth()->user()->email ?? 'A', 0, 1)) }}</div><div class="account-name">{{ auth()->user()->name ?? auth()->user()->email }}</div><form action="{{ route('logout') }}" method="POST">@csrf<button class="signout" type="submit">Sign out</button></form></div></header><main class="content">@if(session('success'))<div class="notice success">{{ session('success') }}</div>@endif@if($errors->any())<div class="notice error">{{ $errors->first() }}</div>@endif @yield('content')</main><footer class="footer">Cresta 31 <span>Property administration</span></footer></div></div><div class="mobile-shade" onclick="document.getElementById('admin-shell').classList.remove('menu-open')"></div></body></html>
|
||||
<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><meta name="csrf-token" content="{{ csrf_token() }}"><title>@yield('title','Administration') · Cresta 31</title><link rel="stylesheet" href="{{ asset('css/cresta-admin.css') }}"></head><body class="admin-body"><input type="checkbox" id="nav-toggle" class="nav-toggle"><label for="nav-toggle" class="nav-shade"></label><aside class="sidebar"><a class="brand" href="{{ route('admin.dashboard') }}"><span class="brand-symbol">C<span>31</span></span><span><strong>Cresta 31</strong><small>ADMINISTRATION</small></span></a><div class="side-scroll"><div class="nav-caption">OVERVIEW</div><a class="nav-item {{ request()->routeIs('admin.dashboard') ? 'active' : '' }}" href="{{ route('admin.dashboard') }}"><span class="ico">▦</span> Dashboard</a><div class="nav-caption">LODGE MANAGEMENT</div><a class="nav-item {{ request()->routeIs('lodge.admin') ? 'active' : '' }}" href="{{ route('lodge.admin') }}"><span class="ico">▤</span> Bookings</a><a class="nav-item {{ request()->routeIs('admin.photos') ? 'active' : '' }}" href="{{ route('admin.photos') }}"><span class="ico">▧</span> Photo gallery</a><a class="nav-item" href="{{ route('home') }}#book" target="_blank" rel="noopener"><span class="ico">▦</span> Availability <span class="external">↗</span></a><div class="nav-caption">SYSTEM</div>@if(\Illuminate\Support\Facades\Route::has('admin.modules.index'))<a class="nav-item {{ request()->routeIs('admin.modules.*') ? 'active' : '' }}" href="{{ route('admin.modules.index') }}"><span class="ico">◈</span> Modules</a>@elseif(\Illuminate\Support\Facades\Route::has('modules.index'))<a class="nav-item" href="{{ route('modules.index') }}"><span class="ico">◈</span> Modules</a>@endif<a class="nav-item" href="{{ route('profile.edit') }}"><span class="ico">⚙</span> Account settings</a><a class="nav-item" href="{{ route('home') }}" target="_blank" rel="noopener"><span class="ico">↗</span> View website</a></div><div class="side-footer"><div class="avatar">{{ strtoupper(substr(auth()->user()->name ?? 'A',0,1)) }}</div><div class="user"><strong>{{ auth()->user()->name }}</strong><small>{{ auth()->user()->email }}</small></div></div></aside><div class="main-shell"><header class="topbar"><label for="nav-toggle" class="menu-toggle" aria-label="Toggle sidebar">☰</label><div class="crumb">Cresta 31 <span>/</span> @yield('section','Dashboard')</div><div class="top-actions"><a class="site-link" href="{{ route('home') }}" target="_blank" rel="noopener">View site ↗</a><form method="POST" action="{{ route('logout') }}">@csrf<button class="logout" type="submit">Sign out</button></form></div></header><main class="main-content">@if(session('success'))<div class="notice success">{{ session('success') }}</div>@endif@if($errors->any())<div class="notice error">{{ $errors->first() }}</div>@endif@yield('content')</main><footer class="admin-footer">Cresta 31 · Administration <span>{{ date('Y') }}</span></footer></div></body></html>
|
||||
@@ -0,0 +1 @@
|
||||
@extends('lodge.admin-layout')@section('title','Photographs')@section('section','Photo gallery')@section('content')<div class="page-heading"><div><div class="eyebrow">LODGE MANAGEMENT</div><h1>Photo gallery</h1><p>Manage the images shown on the public website.</p></div><a class="btn secondary" href="{{ route('home') }}#gallery" target="_blank">View public gallery ↗</a></div><div class="panel"><div class="panel-head"><div><h2>Upload photographs</h2><p>Up to 10 JPEG, PNG or WebP images, 15 MB each. Uploaded images are optimised automatically.</p></div></div><form class="upload-form" action="{{ route('lodge.photos.store') }}" method="POST" enctype="multipart/form-data">@csrf<label class="upload-box" for="photos"><span class="upload-icon">↑</span><strong>Select photographs</strong><small>Choose files to upload</small><input id="photos" name="photos[]" type="file" accept="image/jpeg,image/png,image/webp" multiple required></label><button class="btn primary">Upload and optimise</button></form></div><div class="panel"><div class="panel-head"><div><h2>Uploaded photographs</h2><p>{{ $photos->count() }} images</p></div></div><div class="photo-grid">@forelse($photos as $photo)<div class="photo-card"><img src="{{ asset('storage/'.$photo->thumbnail_path) }}" alt="{{ $photo->alt_text }}" loading="lazy"><div class="photo-meta"><span>{{ $photo->alt_text ?: 'Lodge photograph' }}</span><form action="{{ route('lodge.photos.destroy',$photo) }}" method="POST" onsubmit="return confirm('Delete this photograph?')">@csrf @method('DELETE')<button class="danger">Delete</button></form></div></div>@empty<div class="empty">No uploaded photographs yet. The built-in lodge photographs remain on the website.</div>@endforelse</div></div>@endsection
|
||||
@@ -1,23 +1 @@
|
||||
@extends('lodge.admin-layout')
|
||||
@section('title','Bookings & overview')
|
||||
@section('section-label',request('section') === 'photos' ? 'Photographs' : (request('section') === 'bookings' ? 'Bookings' : 'Overview'))
|
||||
@section('content')
|
||||
@php
|
||||
$section = request('section', 'overview');
|
||||
$items = $bookings->getCollection();
|
||||
$allCount = $bookings->total();
|
||||
$pendingOnPage = $items->where('status','pending')->count();
|
||||
$confirmedOnPage = $items->where('status','confirmed')->count();
|
||||
$revenueOnPage = $items->where('status','confirmed')->sum('total_pence') / 100;
|
||||
@endphp
|
||||
@if($section === 'photos')
|
||||
<div class="page-heading"><div><div class="eyebrow">PROPERTY MEDIA</div><h1>Photographs</h1><p>Manage the photographs displayed on your lodge website.</p></div><a class="btn secondary" href="{{ route('lodge.admin') }}">← Back to overview</a></div>
|
||||
@if(Route::has('lodge.photos.store'))<section class="panel"><div class="panel-heading"><div><h2>Upload photographs</h2><p>Add new images to your lodge gallery.</p></div></div><form method="POST" action="{{ route('lodge.photos.store') }}" enctype="multipart/form-data" class="upload-form">@csrf<label for="photo-upload">Select image</label><input id="photo-upload" type="file" name="photo" accept="image/*" required><button class="btn primary" type="submit">Upload photograph</button></form></section>@endif
|
||||
@if(isset($photos) && count($photos))<section class="panel"><div class="panel-heading"><h2>Gallery</h2><span class="count-chip">{{ count($photos) }} photos</span></div><div class="photo-grid">@foreach($photos as $photo)<div class="photo-card">@php $photoPath = $photo->url ?? $photo->path ?? $photo->filename ?? null; @endphp @if($photoPath)<img loading="lazy" src="{{ str_starts_with($photoPath,'http') || str_starts_with($photoPath,'/') ? $photoPath : asset('storage/'.$photoPath) }}" alt="Lodge photograph">@else<div class="photo-placeholder">Image</div>@endif<div class="photo-caption"><span>Photo #{{ $photo->id }}</span>@if(Route::has('lodge.photos.destroy'))<form method="POST" action="{{ route('lodge.photos.destroy',$photo) }}" onsubmit="return confirm('Delete this photograph?')">@csrf @method('DELETE')<button class="link-danger" type="submit">Delete</button></form>@endif</div></div>@endforeach</div></section>@elseif(!Route::has('lodge.photos.store'))<div class="empty-panel">Photo management is not installed in this version of Cresta 31.</div>@else<div class="empty-panel">No uploaded photographs yet.</div>@endif
|
||||
@else
|
||||
<div class="page-heading"><div><div class="eyebrow">PROPERTY MANAGEMENT</div><h1>{{ $section === 'bookings' ? 'Bookings' : 'Welcome back' }}</h1><p>{{ $section === 'bookings' ? 'Review reservations and update booking statuses.' : 'Here’s what’s happening with your lodge bookings.' }}</p></div><a class="btn secondary" href="{{ url('/') }}" target="_blank" rel="noopener">View website ↗</a></div>
|
||||
<div class="stats-grid"><div class="stat-card"><div class="stat-label">Total bookings <span>▤</span></div><div class="stat-value">{{ number_format($allCount) }}</div><div class="stat-foot">All reservations</div></div><div class="stat-card"><div class="stat-label">Pending <span>◷</span></div><div class="stat-value">{{ $pendingOnPage }}</div><div class="stat-foot">On this page</div></div><div class="stat-card"><div class="stat-label">Confirmed <span>✓</span></div><div class="stat-value">{{ $confirmedOnPage }}</div><div class="stat-foot">On this page</div></div><div class="stat-card"><div class="stat-label">Confirmed value <span>£</span></div><div class="stat-value">£{{ number_format($revenueOnPage,2) }}</div><div class="stat-foot">On this page</div></div></div>
|
||||
<section class="panel"><div class="panel-heading"><div><h2>{{ $section === 'bookings' ? 'All bookings' : 'Recent bookings' }}</h2><p>Manage guest reservations and their current status.</p></div><span class="count-chip">{{ number_format($allCount) }} total</span></div><div class="table-scroll"><table class="booking-table"><thead><tr><th>Reference</th><th>Guest</th><th>Stay dates</th><th>Guests</th><th>Amount</th><th>Status</th><th>Update</th></tr></thead><tbody>@forelse($bookings as $b)<tr><td><strong class="reference">{{ $b->reference }}</strong></td><td><strong>{{ $b->guest_name }}</strong><small>{{ $b->guest_email }}</small>@if($b->guest_phone)<small>{{ $b->guest_phone }}</small>@endif</td><td><span class="date-cell">{{ $b->check_in?->format('d M Y') }}<br><span class="date-to">to {{ $b->check_out?->format('d M Y') }}</span></span></td><td>{{ $b->guests }}</td><td><strong>£{{ number_format($b->total_pence/100,2) }}</strong></td><td><span class="badge {{ $b->status }}">{{ ucfirst($b->status) }}</span></td><td><form class="update-form" method="POST" action="{{ route('lodge.update',$b) }}">@csrf @method('PATCH')<select name="status" aria-label="Status for {{ $b->reference }}"><option value="pending" @selected($b->status === 'pending')>Pending</option><option value="confirmed" @selected($b->status === 'confirmed')>Confirmed</option><option value="cancelled" @selected($b->status === 'cancelled')>Cancelled</option></select><button type="submit">Save</button></form></td></tr>@empty<tr><td colspan="7" class="empty-table">No bookings yet. New reservations will appear here.</td></tr>@endforelse</tbody></table></div><div class="pagination-area">{{ $bookings->appends(request()->query())->links() }}</div></section>
|
||||
@if($section !== 'bookings')<div class="quick-grid"><a class="quick-card" href="{{ route('lodge.admin',['section'=>'bookings']) }}"><span class="quick-icon">▤</span><div><strong>Manage bookings</strong><small>Review and update reservations</small></div><span class="quick-arrow">→</span></a><a class="quick-card" href="{{ route('lodge.admin',['section'=>'photos']) }}"><span class="quick-icon">▧</span><div><strong>Lodge gallery</strong><small>Upload and organise photos</small></div><span class="quick-arrow">→</span></a></div>@endif
|
||||
@endif
|
||||
@endsection
|
||||
@extends('lodge.admin-layout')@section('title','Bookings')@section('section','Bookings')@section('content')<div class="page-heading"><div><div class="eyebrow">LODGE MANAGEMENT</div><h1>Bookings</h1><p>Review guest requests and manage their status.</p></div><a class="btn secondary" href="{{ route('admin.dashboard') }}">← Dashboard</a></div><div class="panel"><div class="panel-head"><div><h2>All bookings</h2><p>{{ $bookings->total() }} booking requests</p></div></div><div class="table-scroll"><table><thead><tr><th>Reference</th><th>Guest details</th><th>Stay dates</th><th>Guests</th><th>Total</th><th>Status / Action</th></tr></thead><tbody>@forelse($bookings as $b)<tr><td><strong>{{ $b->reference }}</strong></td><td><strong>{{ $b->guest_name }}</strong><small class="cell-note">{{ $b->guest_email }}<br>{{ $b->guest_phone }}</small></td><td>{{ $b->check_in->format('d M Y') }}<small class="cell-note">to {{ $b->check_out->format('d M Y') }}</small></td><td>{{ $b->guests }}</td><td>£{{ number_format($b->total_pence / 100,2) }}</td><td><form class="status-form" method="POST" action="{{ route('lodge.update',$b) }}">@csrf @method('PATCH')<select name="status" aria-label="Booking status"><option value="pending" @selected($b->status==='pending')>Pending</option><option value="confirmed" @selected($b->status==='confirmed')>Confirmed</option><option value="cancelled" @selected($b->status==='cancelled')>Cancelled</option></select><button class="btn small primary">Save</button></form></td></tr>@empty<tr><td colspan="6" class="empty">No bookings yet.</td></tr>@endforelse</tbody></table></div><div class="pagination">{{ $bookings->links() }}</div></div>@endsection
|
||||
@@ -1,3 +1,3 @@
|
||||
<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><meta name="csrf-token" content="{{ csrf_token() }}"><title>@yield('title','Cresta 31 | Lodge escapes')</title><meta name="description" content="Escape to Cresta 31. Discover the lodge, check availability and request your stay."><link rel="preconnect" href="https://fonts.googleapis.com"><link rel="preconnect" href="https://fonts.gstatic.com" crossorigin><link href="https://fonts.googleapis.com/css2?family=DM+Sans:wght@400;500;600;700&family=Playfair+Display:wght@500;600&display=swap" rel="stylesheet"><style>
|
||||
:root{--ink:#1b312b;--muted:#697b70;--sand:#f6f3eb;--gold:#c49b5a;--white:#fff}*{box-sizing:border-box}body{margin:0;background:var(--sand);color:var(--ink);font-family:'DM Sans',sans-serif}a{color:inherit;text-decoration:none}h1,h2,h3{font-family:'Playfair Display',serif;font-weight:500}header{position:absolute;top:0;left:0;right:0;z-index:2;color:#fff;display:flex;align-items:center;justify-content:space-between;padding:25px max(5vw,24px)}header .brand{font-family:'Playfair Display',serif;font-size:30px}header nav{display:flex;gap:25px;align-items:center}header a.button{background:#fff;color:var(--ink)}.button,button{display:inline-block;background:var(--ink);color:white;border:0;padding:15px 24px;border-radius:3px;font:600 14px 'DM Sans';cursor:pointer}.button:hover,button:hover{opacity:.88}.wrap{max-width:1200px;margin:auto;padding:0 24px}.hero{height:min(85vh,780px);min-height:530px;position:relative;display:flex;align-items:center;background:#233d34 url('/images/lodge/hero/photo-01.webp') center/cover;color:#fff}.hero:before{content:'';position:absolute;inset:0;background:linear-gradient(90deg,rgba(12,30,24,.7),rgba(12,30,24,.08))}.hero .content{position:relative;max-width:750px;padding:80px 0}.eyebrow{text-transform:uppercase;letter-spacing:.24em;font-size:12px;font-weight:700}.hero h1{font-size:clamp(50px,7vw,95px);line-height:1.05;margin:20px 0}.hero p{font-size:18px;max-width:480px;line-height:1.8}.section{padding:85px 0}.section h2{font-size:clamp(34px,4vw,54px);margin:10px 0 25px}.intro{display:grid;grid-template-columns:1fr 1fr;gap:70px;align-items:center}.intro p{line-height:1.9;color:var(--muted)}.intro img{width:100%;height:480px;object-fit:cover}.gallery{display:grid;grid-template-columns:repeat(3,1fr);gap:14px}.gallery img{width:100%;height:275px;object-fit:cover;border-radius:3px}.booking{background:#fff;padding:34px;box-shadow:0 14px 60px #172e2220;border-radius:5px}.booking-grid{display:grid;grid-template-columns:repeat(3,1fr) auto;gap:15px;align-items:end}label{display:block;font-size:12px;text-transform:uppercase;letter-spacing:.12em;font-weight:700;margin-bottom:9px}input,select,textarea{display:block;width:100%;border:1px solid #d5ddd7;padding:14px;font:inherit;border-radius:3px;background:#fff;color:var(--ink)}.small{color:var(--muted);font-size:14px}.card{background:#fff;padding:36px;border-radius:5px;max-width:760px;margin:auto}.card h1{font-size:46px}.form-grid{display:grid;grid-template-columns:1fr 1fr;gap:18px}.full{grid-column:1/-1}.errors{background:#fff0ed;color:#8c3024;padding:16px;margin:16px 0}.subhero{background:var(--ink);color:#fff;padding:155px 24px 65px}.subhero h1{font-size:54px;margin:0}footer{background:#142820;color:#fff;padding:45px 0;margin-top:30px}footer .wrap{display:flex;justify-content:space-between}.price{font-size:30px;font-family:'Playfair Display',serif}table{border-collapse:collapse;width:100%;background:white}td,th{padding:13px;text-align:left;border-bottom:1px solid #ddd}@media(max-width:760px){header nav a:not(.button){display:none}.intro{grid-template-columns:1fr;gap:25px}.gallery{grid-template-columns:repeat(2,1fr)}.gallery img{height:180px}.booking-grid,.form-grid{grid-template-columns:1fr}.hero{min-height:600px}.section{padding:55px 0}footer .wrap{display:block}}
|
||||
</style></head><body><header><a class="brand" href="{{ route('home') }}">Cresta 31<span style="color:#d9b985">.</span></a><nav><a href="{{ route('home') }}#the-lodge">The lodge</a><a href="{{ route('home') }}#gallery">Gallery</a><a class="button" href="{{ route('home') }}#book">Check availability</a></nav></header>@yield('content')<footer><div class="wrap"><div><strong>Cresta 31</strong><p class="small">Your next lodge escape starts here.</p></div><div>© {{ date('Y') }} Cresta 31</div></div></footer></body></html>
|
||||
</style></head><body><header><a class="brand" href="{{ route('home') }}">Cresta 31<span style="color:#d9b985">.</span></a><nav><a href="{{ route('home') }}#the-lodge">The lodge</a><a href="{{ route('home') }}#gallery">Gallery</a><a class="button" href="{{ route('home') }}#book">Check availability</a>@auth @if(filled(config('lodge.admin_email')) && strcasecmp(trim(auth()->user()->email),trim(config('lodge.admin_email')))===0)<a href="{{ route('admin.dashboard') }}" style="border:1px solid rgba(255,255,255,.8);padding:10px 15px;border-radius:4px;font-weight:700">Admin dashboard →</a>@endif @endauth</nav></header>@yield('content')<footer><div class="wrap"><div><strong>Cresta 31</strong><p class="small">Your next lodge escape starts here.</p></div><div>© {{ date('Y') }} Cresta 31</div></div></footer></body></html>
|
||||
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
use App\Models\LodgeBooking;
|
||||
use App\Models\LodgePhoto;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
|
||||
Route::middleware(['auth', 'verified'])->prefix('admin')->group(function () {
|
||||
$isAdmin = static function ($request): void {
|
||||
abort_unless(
|
||||
filled(config('lodge.admin_email'))
|
||||
&& strcasecmp(trim((string) $request->user()?->email), trim((string) config('lodge.admin_email'))) === 0,
|
||||
403
|
||||
);
|
||||
};
|
||||
|
||||
Route::get('/', function (\Illuminate\Http\Request $request) use ($isAdmin) {
|
||||
$isAdmin($request);
|
||||
return view('lodge.admin-dashboard', [
|
||||
'total' => LodgeBooking::count(),
|
||||
'pending' => LodgeBooking::where('status', 'pending')->count(),
|
||||
'confirmed' => LodgeBooking::where('status', 'confirmed')->count(),
|
||||
'photoCount' => LodgePhoto::count(),
|
||||
'recent' => LodgeBooking::latest()->take(8)->get(),
|
||||
]);
|
||||
})->name('admin.dashboard');
|
||||
|
||||
Route::get('/photos', function (\Illuminate\Http\Request $request) use ($isAdmin) {
|
||||
$isAdmin($request);
|
||||
return view('lodge.admin-photos', ['photos' => LodgePhoto::latest()->get()]);
|
||||
})->name('admin.photos');
|
||||
});
|
||||
@@ -8,3 +8,5 @@ Route::post('/book',[LodgeController::class,'book'])->middleware('throttle:8,1')
|
||||
Route::get('/booking/{reference}',[LodgeController::class,'confirmation'])->middleware('throttle:30,1')->name('lodge.confirmation');
|
||||
Route::middleware(['auth','verified'])->group(function(){Route::get('/dashboard',fn()=>redirect()->route('lodge.admin'))->name('dashboard');Route::get('/admin/bookings',[LodgeController::class,'admin'])->name('lodge.admin');Route::patch('/admin/bookings/{booking}',[LodgeController::class,'update'])->name('lodge.update');Route::post('/admin/photos',[LodgePhotoController::class,'store'])->name('lodge.photos.store');Route::delete('/admin/photos/{photo}',[LodgePhotoController::class,'destroy'])->name('lodge.photos.destroy');});
|
||||
require __DIR__.'/settings.php';
|
||||
|
||||
require __DIR__.'/cresta-admin.php';
|
||||
|
||||
Reference in New Issue
Block a user