✨ Update design

This commit is contained in:
2026-09-01 12:32:00 +02:00
parent e19461e41d
commit a768ea99df
24 changed files with 9476 additions and 104 deletions
+1 -1
View File
@@ -54,7 +54,7 @@ export function AppSidebar({ staffUser, sectionLinks }: Props) {
<nav className="app-sidebar" aria-label="Hauptnavigation" ref={navRef}>
<div className="app-sidebar-bar">
<Link className="wordmark app-sidebar-brand" to="/" onClick={() => setIsOpen(false)}>
Steakhaus <span>bei Mirko</span>
Historisches Rathaus <span>Resto-Bar</span>
</Link>
<button
type="button"
+3 -3
View File
@@ -137,7 +137,7 @@ export function PdfMenu({ src }: Props) {
if (error) {
return (
<p className="field-hint">
Die Speisekarte konnte nicht geladen werden.{' '}
Die Karte konnte nicht geladen werden.{' '}
<a href={src} target="_blank" rel="noopener noreferrer">
Als PDF öffnen
</a>
@@ -153,7 +153,7 @@ export function PdfMenu({ src }: Props) {
{numPages === 0 && (
<div className="pdf-menu-loading" role="status">
<div className="pdf-menu-spinner" aria-hidden="true" />
<p>Die Speisekarte wird aufgetischt …</p>
<p>Die Karte wird aufgetischt …</p>
</div>
)}
</div>
@@ -181,7 +181,7 @@ export function PdfMenu({ src }: Props) {
</div>
)}
<p className="field-hint">
Speisekarte nicht geladen?{' '}
Karte nicht geladen?{' '}
<a href={src} target="_blank" rel="noopener noreferrer">
Klick hier
</a>
+2 -2
View File
@@ -4,9 +4,9 @@ export function SiteFooter() {
return (
<footer className="site-footer">
<span className="wordmark">
Steakhaus <span>bei Mirko</span>
Historisches Rathaus <span>Resto-Bar</span>
</span>
Meppener Straße 8 · 49716 Meppen · 05931 - 12750
Markt 49 · 49716 Meppen · 05931 - 848 0161
<nav className="footer-nav">
<Link to="/">Startseite</Link>
<Link to="/impressum">Impressum</Link>
+1 -1
View File
@@ -5,7 +5,7 @@ export function SiteHeader() {
<header className="site-header">
<div className="site-header-inner">
<Link className="wordmark" to="/">
Steakhaus <span>bei Mirko</span>
Historisches Rathaus <span>Resto-Bar</span>
</Link>
<Link className="staff-login-link" to="/staff/login">
Mitarbeiter-Login
+14 -14
View File
@@ -8,13 +8,13 @@ const dateTimeFormatter = new Intl.DateTimeFormat('de-DE', {
})
const COLORS = {
bg: '#17120f',
bgRaised: '#1e1712',
line: '#3a2f26',
text: '#f3ead9',
textMuted: '#c9baa3',
gold: '#c9a15a',
goldBright: '#e0bb78',
bg: '#fbf4ef',
bgRaised: '#ffffff',
line: '#e6d9cf',
text: '#241417',
textMuted: '#6b5f5c',
gold: '#6b0f22',
goldBright: '#a3182f',
}
function escapeHtml(value: string) {
@@ -49,7 +49,7 @@ type ReservationEmailInfo = {
table: Pick<RestaurantTable, 'name'>
}
const CONTACT_EMAIL = 'info@steakhaus-meppen.de'
const CONTACT_EMAIL = 'info@restobar-meppen.de'
function getSiteUrl() {
return process.env.BETTER_AUTH_URL ?? 'http://localhost:3000'
@@ -96,7 +96,7 @@ function renderReservationEmailHtml(
<tr>
<td align="center" style="padding-bottom:28px;">
<span style="font:600 20px/1.3 Georgia,'Times New Roman',serif;letter-spacing:0.03em;color:${COLORS.text};">
Steakhaus <span style="color:${COLORS.goldBright};">bei Mirko</span>
Historisches Rathaus <span style="color:${COLORS.goldBright};">Resto-Bar</span>
</span>
</td>
</tr>
@@ -173,7 +173,7 @@ function renderReservationEmailHtml(
</tr>
<tr>
<td align="center" style="padding-top:16px;color:${COLORS.textMuted};font-size:12px;">
Meppener Straße 8 · 49716 Meppen · 05931 - 12750
Markt 49 · 49716 Meppen · 05931 - 848 0161
</td>
</tr>
</table>
@@ -193,7 +193,7 @@ function renderReservationEmailText(
const formattedTime = dateTimeFormatter.format(reservation.reservationTime)
return [
'Steakhaus bei Mirko',
'Historisches Rathaus Resto-Bar',
'',
heading,
'',
@@ -214,8 +214,8 @@ function renderReservationEmailText(
]
: []),
'Wir freuen uns auf Sie!',
'Steakhaus bei Mirko',
'Meppener Straße 8 · 49716 Meppen · 05931 - 12750',
'Historisches Rathaus Resto-Bar',
'Markt 49 · 49716 Meppen · 05931 - 848 0161',
].join('\n')
}
@@ -232,7 +232,7 @@ async function sendReservationEmail(
return
}
const fromAddress = process.env.RESERVATIONS_FROM_EMAIL ?? 'Steakhaus bei Mirko <reservierungen@steakhaus-meppen.de>'
const fromAddress = process.env.RESERVATIONS_FROM_EMAIL ?? 'Historisches Rathaus Resto-Bar <reservierungen@restobar-meppen.de>'
try {
await client.emails.send({
+6 -9
View File
@@ -7,16 +7,13 @@ export type OpeningRange = { start: string; end: string }
// Keyed by the JS `Date#getDay()` convention: 0 = Sunday … 6 = Saturday.
const OPENING_HOURS_BY_WEEKDAY: Record<number, OpeningRange[]> = {
0: [
{ start: '11:30', end: '13:30' },
{ start: '17:00', end: '22:00' },
],
0: [{ start: '12:00', end: '20:00' }],
1: [],
2: [{ start: '17:00', end: '21:30' }],
3: [{ start: '17:00', end: '21:30' }],
4: [{ start: '17:00', end: '21:30' }],
5: [{ start: '17:00', end: '22:00' }],
6: [{ start: '17:00', end: '22:00' }],
2: [{ start: '12:00', end: '20:00' }],
3: [{ start: '12:00', end: '20:00' }],
4: [{ start: '12:00', end: '20:00' }],
5: [{ start: '12:00', end: '20:00' }],
6: [{ start: '12:00', end: '20:00' }],
}
const WEEKDAY_LABELS_MONDAY_FIRST: Array<{ day: string; weekday: number }> = [
+1 -1
View File
@@ -247,7 +247,7 @@ const createReservationSchema = z.object({
.max(500, 'Message must be at most 500 characters')
.optional()
.transform((value) => value || null),
partySize: z.number().int().min(1, 'At least 1 guest').max(20),
partySize: z.number().int().min(1, 'At least 1 guest').max(30),
reservationTime: z.string().refine((value) => !Number.isNaN(Date.parse(value)), {
message: 'Enter a valid date and time',
}),
+2 -2
View File
@@ -13,12 +13,12 @@ export const Route = createRootRoute({
content: 'width=device-width, initial-scale=1',
},
{
title: 'Steakhaus bei Mirko | Meppen',
title: 'Historisches Rathaus Resto-Bar | Meppen',
},
{
name: 'description',
content:
'Steakhaus bei Mirko in Meppen – Steaks vom Grill, gemütliche Atmosphäre. Reservieren Sie Ihren Tisch online.',
'Historisches Rathaus Resto-Bar in Meppen – Deine Gastronomie im Herzen der Stadt. Reservieren Sie Ihren Tisch online.',
},
],
links: [
+8 -6
View File
@@ -17,18 +17,20 @@ function Impressum() {
<h2>Angaben gemäß § 5 TMG</h2>
<p>
Drenica &amp; Mousa GbR
Ilie Cosmin Ivan
<br />
Meppener Str. 8
Historisches Rathaus Resto-Bar
<br />
Markt 49
<br />
49716 Meppen
</p>
<h2>Kontakt</h2>
<p>
Telefon: <a href="tel:+49593112750">+49 (5931) 12750</a>
Telefon: <a href="tel:+4959318480161">+49 5931 84 80161</a>
<br />
E-Mail: <a href="mailto:info@steakhaus-meppen.de">info@steakhaus-meppen.de</a>
E-Mail: <a href="mailto:info@restobar-meppen.de">info@restobar-meppen.de</a>
</p>
<h2>Haftungsausschluss</h2>
@@ -59,8 +61,8 @@ function Impressum() {
<h2 id="datenschutz">Datenschutz</h2>
<p>
Verantwortlich für die Verarbeitung personenbezogener Daten auf dieser Website ist die
oben genannte Drenica &amp; Mousa GbR.
Verantwortlich für die Verarbeitung personenbezogener Daten auf dieser Website ist der
oben genannte Ilie Cosmin Ivan.
</p>
<p>
Wenn Sie über das Reservierungsformular einen Tisch buchen, speichern wir die von Ihnen
+20 -10
View File
@@ -108,7 +108,7 @@ function Home() {
EMAIL_PATTERN.test(customerEmail) &&
PHONE_PATTERN.test(customerPhone) &&
partySize >= 1 &&
partySize <= 20 &&
partySize <= 30 &&
Boolean(reservationTime.split('T')[1]) &&
acceptsTerms
@@ -175,13 +175,14 @@ function Home() {
<SiteHeader />
<section className="hero" id="top">
<img className="hero-flame" src="/logo.svg" alt="" aria-hidden="true" />
<img className="hero-logo" src="/hero.jpg" alt="Historisches Rathaus Resto-Bar" />
<h1>
Vom Feuer verwöhnt, <span>mit Liebe serviert</span>
Deine Gastronomie <span>im Herzen der Stadt</span>
</h1>
<p>
Dicke Steaks, knisterndes Holzkohlefeuer und Gelächter am Nachbartisch – so schmeckt ein
guter Abend mitten in Meppen. Reservieren Sie Ihren Tisch, wir legen schon auf.
Mitten auf dem Markt, im historischen Rathaus von Meppen – gute Küche, entspannte
Atmosphäre und Gelächter am Nachbartisch. Reservieren Sie Ihren Tisch, wir freuen uns
auf Sie.
</p>
<div className="hero-actions">
<a className="btn btn-primary" href="#reservieren">
@@ -221,7 +222,16 @@ function Home() {
<span className="section-kicker">Was uns schmeckt</span>
<h2 className="section-heading">Speisekarte</h2>
</div>
<PdfMenu src="/menu.pdf" />
<div className="menu-stack">
<div>
<h3 className="menu-stack-heading">Speisen</h3>
<PdfMenu src="/speisekarte.pdf" />
</div>
<div>
<h3 className="menu-stack-heading">Getränke</h3>
<PdfMenu src="/getraenkekarte.pdf" />
</div>
</div>
</div>
</section>
@@ -319,7 +329,7 @@ function Home() {
type="number"
inputMode="numeric"
min={1}
max={20}
max={30}
value={partySize}
onChange={(e) => setPartySize(Number(e.target.value))}
required
@@ -391,18 +401,18 @@ function Home() {
<li>
<span className="label">Adresse</span>
<span>
Meppener Straße 8
Markt 49
<br />
49716 Meppen
</span>
</li>
<li>
<span className="label">Telefon</span>
<a href="tel:+49593112750">05931 - 12750</a>
<a href="tel:+4959318480161">05931 - 848 0161</a>
</li>
<li>
<span className="label">E-Mail</span>
<a href="mailto:info@steakhaus-meppen.de">info@steakhaus-meppen.de</a>
<a href="mailto:info@restobar-meppen.de">info@restobar-meppen.de</a>
</li>
</ul>
</div>
+1 -1
View File
@@ -164,7 +164,7 @@ function StaffDashboard() {
<div className="staff-nav">
<header className="staff-topbar">
<Link className="wordmark" to="/">
Steakhaus <span>bei Mirko</span>
Historisches Rathaus <span>Resto-Bar</span>
</Link>
<div className="staff-topbar-right">
<span>Angemeldet als {staffUser.name}</span>
+1 -1
View File
@@ -150,7 +150,7 @@ function StaffSettings() {
<div className="staff-nav">
<header className="staff-topbar">
<Link className="wordmark" to="/">
Steakhaus <span>bei Mirko</span>
Historisches Rathaus <span>Resto-Bar</span>
</Link>
<div className="staff-topbar-right">
<span>Angemeldet als {staffUser.name}</span>
+1 -1
View File
@@ -159,7 +159,7 @@ function StaffTables() {
<div className="staff-nav">
<header className="staff-topbar">
<Link className="wordmark" to="/">
Steakhaus <span>bei Mirko</span>
Historisches Rathaus <span>Resto-Bar</span>
</Link>
<div className="staff-topbar-right">
<span>Angemeldet als {staffUser.name}</span>
+1 -1
View File
@@ -212,7 +212,7 @@ function StaffTeam() {
<div className="staff-nav">
<header className="staff-topbar">
<Link className="wordmark" to="/">
Steakhaus <span>bei Mirko</span>
Historisches Rathaus <span>Resto-Bar</span>
</Link>
<div className="staff-topbar-right">
<span>Angemeldet als {staffUser.name}</span>
+73 -49
View File
@@ -1,14 +1,14 @@
@import url('https://fonts.googleapis.com/css2?family=Fraunces:opsz,wght@9..144,500;9..144,600;9..144,700&family=Inter:wght@400;500;600&display=swap');
:root {
--bg: #17120f;
--bg-raised: #1e1712;
--line: #3a2f26;
--text: #f3ead9;
--text-muted: #c9baa3;
--gold: #c9a15a;
--gold-bright: #e0bb78;
--ember: #a8402c;
--bg: #fbf4ef;
--bg-raised: #ffffff;
--line: #e6d9cf;
--text: #241417;
--text-muted: #6b5f5c;
--gold: #6b0f22;
--gold-bright: #a3182f;
--ember: #cf2e2e;
}
* {
@@ -131,9 +131,9 @@ a {
padding: 4.5rem 1.5rem 5rem;
text-align: center;
background:
radial-gradient(ellipse at top, rgba(200, 90, 50, 0.2), transparent 60%),
radial-gradient(ellipse at top, rgba(107, 15, 34, 0.25), transparent 60%),
linear-gradient(rgba(23, 18, 15, 0.55), rgba(23, 18, 15, 0.93)),
url('/hero-interior.jpg');
url('/hero-banner.jpeg');
background-size: auto, auto, cover;
background-position: center, center, center 55%;
background-repeat: no-repeat;
@@ -141,29 +141,43 @@ a {
overflow: hidden;
}
.hero-flame {
.hero-logo {
display: block;
width: 7rem;
height: 3.5rem;
width: 9rem;
height: 9rem;
object-fit: contain;
margin: 0 auto 1.5rem;
}
.hero h1 {
font-size: clamp(2.25rem, 5vw, 3.5rem);
color: var(--text);
color: #fbf4ef;
}
.hero h1 span {
color: var(--text);
color: #fbf4ef;
}
.hero p {
max-width: 34rem;
margin: 1rem auto 0;
color: var(--text-muted);
color: #d9c9c2;
font-size: 1.05rem;
}
/* The hero sits on a photo with a dark scrim regardless of the site's light
theme, so `.btn-secondary` needs its own light-on-dark treatment here —
the global version assumes it's sitting on the page's (light) background. */
.hero .btn-secondary {
border-color: rgba(251, 244, 239, 0.4);
color: #fbf4ef;
}
.hero .btn-secondary:hover {
border-color: rgba(251, 244, 239, 0.7);
color: #fbf4ef;
}
.hero-actions {
margin-top: 2rem;
display: flex;
@@ -190,7 +204,7 @@ a {
.btn-primary {
background: var(--gold);
color: #201709;
color: #fbf4ef;
}
.btn-primary:hover {
@@ -216,7 +230,7 @@ a {
.btn-ghost:hover {
color: var(--text);
background: rgba(243, 234, 217, 0.06);
background: rgba(36, 20, 23, 0.05);
}
/* Sections */
@@ -260,6 +274,17 @@ a {
}
}
.menu-stack {
display: flex;
flex-direction: column;
gap: 3rem;
}
.menu-stack-heading {
font-size: 1.1rem;
margin-bottom: 1rem;
}
/* Opening hours */
.hours-list {
@@ -600,7 +625,7 @@ a {
.reservation-form textarea:focus {
outline: none;
border-color: var(--gold);
box-shadow: 0 0 0 2px rgba(201, 161, 90, 0.25);
box-shadow: 0 0 0 2px rgba(107, 15, 34, 0.25);
}
/* Overrides the generic `.reservation-form input` box styling above — that rule
@@ -671,7 +696,7 @@ a {
border: none;
border-radius: 0.3rem;
background: var(--gold);
color: #201709;
color: #fbf4ef;
cursor: pointer;
}
@@ -707,15 +732,15 @@ a {
}
.message-error {
background: #3a1414;
color: #f3a9a9;
border: 1px solid #5c1f1f;
background: #fbe4e4;
color: #8a2020;
border: 1px solid #eeb3b3;
}
.message-success {
background: #17301c;
color: #a9d9b4;
border: 1px solid #23502c;
background: #e5f3e8;
color: #1f6b34;
border: 1px solid #b8ddc2;
}
.staff-page-messages {
@@ -1164,7 +1189,7 @@ a {
.table-filters select:focus {
outline: none;
border-color: var(--gold);
box-shadow: 0 0 0 2px rgba(201, 161, 90, 0.25);
box-shadow: 0 0 0 2px rgba(107, 15, 34, 0.25);
}
.table-search {
@@ -1209,11 +1234,11 @@ a {
}
.reservations-table tr.is-editing {
background: rgba(243, 234, 217, 0.06);
background: rgba(36, 20, 23, 0.05);
}
.reservations-table-clickable tbody tr:hover {
background: rgba(243, 234, 217, 0.05);
background: rgba(36, 20, 23, 0.04);
cursor: pointer;
}
@@ -1307,21 +1332,21 @@ a {
}
.status-badge.status-confirmed {
background: rgba(201, 161, 90, 0.15);
background: rgba(107, 15, 34, 0.12);
color: var(--gold-bright);
border: 1px solid rgba(201, 161, 90, 0.35);
border: 1px solid rgba(107, 15, 34, 0.3);
}
.status-badge.status-no_show {
background: #3a1414;
color: #f3a9a9;
border: 1px solid #5c1f1f;
background: #fbe4e4;
color: #8a2020;
border: 1px solid #eeb3b3;
}
.status-badge.status-completed {
background: #17301c;
color: #a9d9b4;
border: 1px solid #23502c;
background: #e5f3e8;
color: #1f6b34;
border: 1px solid #b8ddc2;
}
/* A single grouped 3-way toggle track, not three loose floating pills — the
@@ -1359,12 +1384,12 @@ a {
.status-option:hover:not(:disabled):not(.is-active) {
color: var(--text);
background: rgba(243, 234, 217, 0.06);
background: rgba(36, 20, 23, 0.05);
}
.status-option.is-active.status-confirmed {
background: var(--gold);
color: #201709;
color: #fbf4ef;
}
.status-option.is-active.status-no_show {
@@ -1545,7 +1570,7 @@ a {
text-align: center;
font-family: 'Fraunces', Georgia, serif;
font-style: italic;
color: #6b5f4d;
color: #6b5f5c;
}
.pdf-menu-spinner {
@@ -1623,17 +1648,17 @@ a {
.table-marker.is-selected .table-marker-label,
.table-marker.is-active .table-marker-label {
color: #201709;
color: #fbf4ef;
}
.btn-danger {
background: transparent;
border-color: #5c1f1f;
color: #f3a9a9;
border-color: #eeb3b3;
color: #a3182f;
}
.btn-danger:hover:not(:disabled) {
background: #3a1414;
background: #fbe4e4;
}
.btn-danger:disabled {
@@ -1719,16 +1744,16 @@ a {
gap: 0.6rem;
margin: 0 0 1.5rem;
padding: 0.75rem 0.9rem;
border: 1px solid #5c1f1f;
border: 1px solid #eeb3b3;
border-radius: 0.4rem;
background: rgba(168, 64, 44, 0.14);
color: #f3a9a9;
background: rgba(207, 46, 46, 0.08);
color: #8a2020;
}
.confirm-dialog-warning svg {
flex-shrink: 0;
margin-top: 0.1rem;
color: #f3a9a9;
color: #8a2020;
}
.confirm-dialog-warning span {
@@ -1988,5 +2013,4 @@ a {
.legal-page p {
color: var(--text-muted);
max-width: 42rem;
}