Admin portal, public pages, footer, site config
This commit is contained in:
+80
-3
@@ -1,8 +1,85 @@
|
||||
'use client';
|
||||
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
interface Room {
|
||||
id: number;
|
||||
name: string;
|
||||
description: string;
|
||||
price: string;
|
||||
photos: string[];
|
||||
}
|
||||
|
||||
const gold = '#E8A849';
|
||||
const navy = '#010D1E';
|
||||
const cream = '#f5f0e8';
|
||||
|
||||
export default function RoomsPage() {
|
||||
const [rooms, setRooms] = useState<Room[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState('');
|
||||
|
||||
useEffect(() => {
|
||||
fetch('/api/rooms')
|
||||
.then(async (res) => {
|
||||
if (!res.ok) throw new Error('Failed to load rooms');
|
||||
const json = await res.json();
|
||||
setRooms(json.data || []);
|
||||
})
|
||||
.catch((err) => setError(err.message))
|
||||
.finally(() => setLoading(false));
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<main style={{ padding: '2rem' }}>
|
||||
<h1>Rooms</h1>
|
||||
<p>Discover our comfortable rooms with stunning views.</p>
|
||||
<main style={{ padding: '2rem', maxWidth: '72rem', margin: '0 auto', minHeight: '60vh' }}>
|
||||
<h1 style={{ fontSize: 'clamp(32px, 5vw, 48px)', fontWeight: 400, fontStyle: 'italic', color: navy, margin: '0 0 0.5rem' }}>Rooms & Suites</h1>
|
||||
<p style={{ color: '#555', marginBottom: '2rem', maxWidth: '50ch' }}>
|
||||
Comfortable stays with views of the vineyard and cloud forest.
|
||||
</p>
|
||||
|
||||
{loading && <p style={{ color: '#a6683c' }}>Loading rooms...</p>}
|
||||
{error && <p style={{ color: '#c23b22' }}>{error}</p>}
|
||||
|
||||
{!loading && rooms.length === 0 && <p style={{ color: '#777' }}>No rooms available yet.</p>}
|
||||
|
||||
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(300px, 1fr))', gap: '1.5rem' }}>
|
||||
{rooms.map((room) => (
|
||||
<article
|
||||
key={room.id}
|
||||
style={{
|
||||
background: cream,
|
||||
borderRadius: '20px',
|
||||
overflow: 'hidden',
|
||||
boxShadow: '0 2px 8px rgba(1,13,30,0.08)',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
transition: 'transform 0.2s, box-shadow 0.2s',
|
||||
}}
|
||||
onMouseEnter={(e) => { e.currentTarget.style.transform = 'translateY(-3px)'; e.currentTarget.style.boxShadow = '0 8px 24px rgba(1,13,30,0.14)'; }}
|
||||
onMouseLeave={(e) => { e.currentTarget.style.transform = 'translateY(0)'; e.currentTarget.style.boxShadow = '0 2px 8px rgba(1,13,30,0.08)'; }}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
height: '220px',
|
||||
background: '#ddd',
|
||||
backgroundImage: room.photos?.length > 0 ? `url(${room.photos[0]})` : undefined,
|
||||
backgroundSize: 'cover',
|
||||
backgroundPosition: 'center',
|
||||
}}
|
||||
/>
|
||||
<div style={{ padding: '1.5rem', flex: 1, display: 'flex', flexDirection: 'column' }}>
|
||||
<h2 style={{ margin: '0 0 0.5rem', color: navy, fontSize: '22px' }}>{room.name}</h2>
|
||||
<p style={{ color: '#555', lineHeight: 1.5, flex: 1 }}>{room.description}</p>
|
||||
<div style={{ marginTop: '1rem', display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
|
||||
<span style={{ color: warm, fontWeight: 700, fontSize: '18px' }}>${room.price}</span>
|
||||
<span style={{ fontSize: '13px', color: '#777' }}>per night</span>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
))}
|
||||
</div>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
|
||||
const warm = '#a6683c';
|
||||
|
||||
Reference in New Issue
Block a user