Initial Commit
tests / ci (push) Canceled after 0s

This commit is contained in:
Mark Gregory
2026-10-15 16:20:20 +01:00
parent ebb20d56ea
commit bf705a3803
32 changed files with 125 additions and 32 deletions
@@ -0,0 +1 @@
@extends('lodge.layout')@section('title','Manage bookings | Cresta 31')@section('content')<div class="subhero"><div class="wrap"><h1>Manage bookings</h1></div></div><main class="section wrap">@if(session('success'))<p>{{ session('success') }}</p>@endif@if($errors->any())<div class="errors">{{ $errors->first() }}</div>@endif<div style="overflow-x:auto"><table><thead><tr><th>Reference</th><th>Guest</th><th>Dates</th><th>Guests</th><th>Total</th><th>Status</th><th>Action</th></tr></thead><tbody>@forelse($bookings as $b)<tr><td>{{ $b->reference }}</td><td>{{ $b->guest_name }}<br><small>{{ $b->guest_email }}<br>{{ $b->guest_phone }}</small></td><td>{{ $b->check_in->format('d M Y') }} – {{ $b->check_out->format('d M Y') }}</td><td>{{ $b->guests }}</td><td>£{{ number_format($b->total_pence/100,2) }}</td><td>{{ ucfirst($b->status) }}</td><td><form method="post" action="{{ route('lodge.update',$b) }}">@csrf @method('PATCH')<select name="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 style="margin-top:8px">Save</button></form></td></tr>@empty<tr><td colspan="7">No bookings yet.</td></tr>@endforelse</tbody></table></div><div style="margin-top:25px">{{ $bookings->links() }}</div></main>@endsection
@@ -0,0 +1 @@
@extends('lodge.layout')@section('title','Availability | Cresta 31')@section('content')<div class="subhero"><div class="wrap"><h1>Your stay</h1></div></div><main class="section wrap"><div class="card"><h2>{{ $available?'Your dates are available':'These dates are unavailable' }}</h2><p>{{ $v['check_in'] }} → {{ $v['check_out'] }} · {{ $nights }} nights · {{ $v['guests'] }} guests</p>@if($available)<p class="price">£{{ number_format($total,2) }} <span class="small">estimated total</span></p><p class="small">Submit a booking request. Your booking remains pending until confirmed by the lodge owner. No payment is taken online.</p>@if($errors->any())<div class="errors">{{ $errors->first() }}</div>@endif<form method="post" action="{{ route('lodge.book') }}">@csrf<input type="hidden" name="check_in" value="{{ $v['check_in'] }}"><input type="hidden" name="check_out" value="{{ $v['check_out'] }}"><input type="hidden" name="guests" value="{{ $v['guests'] }}"><div class="form-grid"><div><label>Name</label><input name="guest_name" required value="{{ old('guest_name') }}"></div><div><label>Email</label><input name="guest_email" type="email" required value="{{ old('guest_email') }}"></div><div class="full"><label>Phone (optional)</label><input name="guest_phone" value="{{ old('guest_phone') }}"></div><div class="full"><label>Anything we should know? (optional)</label><textarea name="notes" rows="4">{{ old('notes') }}</textarea></div><div class="full"><button type="submit">Request this stay →</button></div></div></form>@else<p>Someone has already requested or booked part of this stay. Please select alternative dates.</p><a class="button" href="{{ route('home') }}#book">Choose new dates</a>@endif</div></main>@endsection
@@ -0,0 +1 @@
@extends('lodge.layout')@section('title','Booking request received | Cresta 31')@section('content')<div class="subhero"><div class="wrap"><h1>Request received</h1></div></div><main class="section wrap"><div class="card"><div class="eyebrow">Cresta 31</div><h2>Thank you for your booking request.</h2><p>Your reference is <strong>{{ $booking->reference }}</strong>.</p><p>{{ $booking->check_in->format('j M Y') }} – {{ $booking->check_out->format('j M Y') }} · {{ $booking->nights }} nights</p><p class="price">£{{ number_format($booking->total_pence/100,2) }}</p><p><strong>Status:</strong> {{ ucfirst($booking->status) }}</p><p class="small">This is a request, not a confirmed reservation. No payment has been collected. Please retain your reference.</p><a class="button" href="{{ route('home') }}">Back to the lodge</a></div></main>@endsection
@@ -0,0 +1 @@
@extends('lodge.layout')@section('content')<section class="hero"><div class="wrap" style="width:100%"><div class="content"><div class="eyebrow">Unwind · Escape · Explore</div><h1>A little escape.<br>A lot to love.</h1><p>Welcome to Cresta 31 — a comfortable lodge retreat made for slow mornings, fresh air and memorable stays.</p><a class="button" style="background:#fff;color:#1b312b" href="#book">Explore your stay →</a></div></div></section><div class="wrap"><div class="booking" id="book" style="position:relative;margin-top:-45px"><form action="{{ route('lodge.availability') }}" method="get"><div class="booking-grid"><div><label for="check_in">Check in</label><input id="check_in" name="check_in" type="date" min="{{ date('Y-m-d') }}" required></div><div><label for="check_out">Check out</label><input id="check_out" name="check_out" type="date" min="{{ date('Y-m-d',strtotime('+1 day')) }}" required></div><div><label for="guests">Guests</label><select id="guests" name="guests">@for($i=1;$i<=config('lodge.max_guests');$i++)<option value="{{ $i }}">{{ $i }} {{ $i===1?'guest':'guests' }}</option>@endfor</select></div><button>Check dates →</button></div></form></div></div><section class="section" id="the-lodge"><div class="wrap intro"><div><div class="eyebrow" style="color:#b48d51">Welcome to Cresta 31</div><h2>Make yourself<br>right at home.</h2><p>Discover the spaces that make Cresta 31 a special place to get away. Whether you're taking a peaceful break or spending time together, settle in and enjoy a change of scenery.</p><p>Browse the lodge photographs below, then choose your dates to check availability.</p><div class="price">From £{{ number_format($rate,0) }} <span class="small">/ night</span></div></div><img src="{{ asset('images/lodge/photo-02.png') }}" alt="Cresta 31 lodge interior" loading="lazy"></div></section><section class="section" id="gallery" style="background:#eaece6"><div class="wrap"><div class="eyebrow" style="color:#b48d51">A closer look</div><h2>Explore the lodge</h2><div class="gallery">@for($i=1;$i<=11;$i++)<a href="{{ asset(sprintf('images/lodge/photo-%02d.png',$i)) }}" target="_blank"><img src="{{ asset(sprintf('images/lodge/photo-%02d.png',$i)) }}" loading="lazy" alt="Cresta 31 lodge photo {{ $i }}"></a>@endfor</div></div></section><section class="section"><div class="wrap" style="text-align:center"><div class="eyebrow">Your next getaway</div><h2>Ready to stay?</h2><p class="small">Choose your dates and send a booking request.</p><a class="button" href="#book">Check availability →</a></div></section>@endsection
@@ -0,0 +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/photo-01.png') 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>
+1
View File
@@ -0,0 +1 @@
@extends('lodge.layout')@section('title','Manage bookings | Cresta 31')@section('content')<div class="subhero"><div class="wrap"><h1>Manage bookings</h1></div></div><main class="section wrap">@if(session('success'))<p>{{ session('success') }}</p>@endif@if($errors->any())<div class="errors">{{ $errors->first() }}</div>@endif<div style="overflow-x:auto"><table><thead><tr><th>Reference</th><th>Guest</th><th>Dates</th><th>Guests</th><th>Total</th><th>Status</th><th>Action</th></tr></thead><tbody>@forelse($bookings as $b)<tr><td>{{ $b->reference }}</td><td>{{ $b->guest_name }}<br><small>{{ $b->guest_email }}<br>{{ $b->guest_phone }}</small></td><td>{{ $b->check_in->format('d M Y') }} – {{ $b->check_out->format('d M Y') }}</td><td>{{ $b->guests }}</td><td>£{{ number_format($b->total_pence/100,2) }}</td><td>{{ ucfirst($b->status) }}</td><td><form method="post" action="{{ route('lodge.update',$b) }}">@csrf @method('PATCH')<select name="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 style="margin-top:8px">Save</button></form></td></tr>@empty<tr><td colspan="7">No bookings yet.</td></tr>@endforelse</tbody></table></div><div style="margin-top:25px">{{ $bookings->links() }}</div></main>@endsection
@@ -0,0 +1 @@
@extends('lodge.layout')@section('title','Availability | Cresta 31')@section('content')<div class="subhero"><div class="wrap"><h1>Your stay</h1></div></div><main class="section wrap"><div class="card"><h2>{{ $available?'Your dates are available':'These dates are unavailable' }}</h2><p>{{ $v['check_in'] }} → {{ $v['check_out'] }} · {{ $nights }} nights · {{ $v['guests'] }} guests</p>@if($available)<p class="price">£{{ number_format($total,2) }} <span class="small">estimated total</span></p><p class="small">Submit a booking request. Your booking remains pending until confirmed by the lodge owner. No payment is taken online.</p>@if($errors->any())<div class="errors">{{ $errors->first() }}</div>@endif<form method="post" action="{{ route('lodge.book') }}">@csrf<input type="hidden" name="check_in" value="{{ $v['check_in'] }}"><input type="hidden" name="check_out" value="{{ $v['check_out'] }}"><input type="hidden" name="guests" value="{{ $v['guests'] }}"><div class="form-grid"><div><label>Name</label><input name="guest_name" required value="{{ old('guest_name') }}"></div><div><label>Email</label><input name="guest_email" type="email" required value="{{ old('guest_email') }}"></div><div class="full"><label>Phone (optional)</label><input name="guest_phone" value="{{ old('guest_phone') }}"></div><div class="full"><label>Anything we should know? (optional)</label><textarea name="notes" rows="4">{{ old('notes') }}</textarea></div><div class="full"><button type="submit">Request this stay →</button></div></div></form>@else<p>Someone has already requested or booked part of this stay. Please select alternative dates.</p><a class="button" href="{{ route('home') }}#book">Choose new dates</a>@endif</div></main>@endsection
@@ -0,0 +1 @@
@extends('lodge.layout')@section('title','Booking request received | Cresta 31')@section('content')<div class="subhero"><div class="wrap"><h1>Request received</h1></div></div><main class="section wrap"><div class="card"><div class="eyebrow">Cresta 31</div><h2>Thank you for your booking request.</h2><p>Your reference is <strong>{{ $booking->reference }}</strong>.</p><p>{{ $booking->check_in->format('j M Y') }} – {{ $booking->check_out->format('j M Y') }} · {{ $booking->nights }} nights</p><p class="price">£{{ number_format($booking->total_pence/100,2) }}</p><p><strong>Status:</strong> {{ ucfirst($booking->status) }}</p><p class="small">This is a request, not a confirmed reservation. No payment has been collected. Please retain your reference.</p><a class="button" href="{{ route('home') }}">Back to the lodge</a></div></main>@endsection
+1
View File
@@ -0,0 +1 @@
@extends('lodge.layout')@section('content')<section class="hero"><div class="wrap" style="width:100%"><div class="content"><div class="eyebrow">Unwind · Escape · Explore</div><h1>A little escape.<br>A lot to love.</h1><p>Welcome to Cresta 31 — a comfortable lodge retreat made for slow mornings, fresh air and memorable stays.</p><a class="button" style="background:#fff;color:#1b312b" href="#book">Explore your stay →</a></div></div></section><div class="wrap"><div class="booking" id="book" style="position:relative;margin-top:-45px"><form action="{{ route('lodge.availability') }}" method="get"><div class="booking-grid"><div><label for="check_in">Check in</label><input id="check_in" name="check_in" type="date" min="{{ date('Y-m-d') }}" required></div><div><label for="check_out">Check out</label><input id="check_out" name="check_out" type="date" min="{{ date('Y-m-d',strtotime('+1 day')) }}" required></div><div><label for="guests">Guests</label><select id="guests" name="guests">@for($i=1;$i<=config('lodge.max_guests');$i++)<option value="{{ $i }}">{{ $i }} {{ $i===1?'guest':'guests' }}</option>@endfor</select></div><button>Check dates →</button></div></form></div></div><section class="section" id="the-lodge"><div class="wrap intro"><div><div class="eyebrow" style="color:#b48d51">Welcome to Cresta 31</div><h2>Make yourself<br>right at home.</h2><p>Discover the spaces that make Cresta 31 a special place to get away. Whether you're taking a peaceful break or spending time together, settle in and enjoy a change of scenery.</p><p>Browse the lodge photographs below, then choose your dates to check availability.</p><div class="price">From £{{ number_format($rate,0) }} <span class="small">/ night</span></div></div><img src="{{ asset('images/lodge/photo-02.png') }}" alt="Cresta 31 lodge interior" loading="lazy"></div></section><section class="section" id="gallery" style="background:#eaece6"><div class="wrap"><div class="eyebrow" style="color:#b48d51">A closer look</div><h2>Explore the lodge</h2><div class="gallery">@for($i=1;$i<=11;$i++)<a href="{{ asset(sprintf('images/lodge/photo-%02d.png',$i)) }}" target="_blank"><img src="{{ asset(sprintf('images/lodge/photo-%02d.png',$i)) }}" loading="lazy" alt="Cresta 31 lodge photo {{ $i }}"></a>@endfor</div></div></section><section class="section"><div class="wrap" style="text-align:center"><div class="eyebrow">Your next getaway</div><h2>Ready to stay?</h2><p class="small">Choose your dates and send a booking request.</p><a class="button" href="#book">Check availability →</a></div></section>@endsection
+3
View File
@@ -0,0 +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/photo-01.png') 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>