Anpassung für Aufruf für proxy
This commit is contained in:
@@ -17,6 +17,12 @@ php -S 127.0.0.1:8000
|
||||
|
||||
Danach ist die Seite unter `http://127.0.0.1:8000` erreichbar.
|
||||
|
||||
Wenn die Anwendung unter einem Unterordner laeuft, kannst du zusaetzlich `FOTOBOX_BASE_PATH` setzen, z. B.:
|
||||
|
||||
```bash
|
||||
FOTOBOX_BASE_PATH=/fotobox php -S 127.0.0.1:8000
|
||||
```
|
||||
|
||||
## Admin-Zugang
|
||||
|
||||
- Benutzername: `admin`
|
||||
|
||||
+303
-94
@@ -1,19 +1,20 @@
|
||||
:root {
|
||||
--bg: #f4efe7;
|
||||
--bg-panel: rgba(255, 255, 255, 0.72);
|
||||
--card: #fdf9f2;
|
||||
--bg: #f4f0e8;
|
||||
--bg-panel: rgba(255, 252, 247, 0.84);
|
||||
--card: #fffdf8;
|
||||
--surface: #ffffff;
|
||||
--line: rgba(55, 35, 18, 0.12);
|
||||
--text: #1f160f;
|
||||
--muted: #6d5a4d;
|
||||
--accent: #bd5f2d;
|
||||
--accent-deep: #7a3412;
|
||||
--accent-soft: #f1d1ba;
|
||||
--line: rgba(30, 24, 19, 0.1);
|
||||
--text: #171412;
|
||||
--muted: #5a5048;
|
||||
--accent: #b56a38;
|
||||
--accent-deep: #6e3413;
|
||||
--accent-soft: #edd8c9;
|
||||
--trust: #2f5a4d;
|
||||
--success: #1f6c46;
|
||||
--error: #8a2630;
|
||||
--shadow: 0 24px 60px rgba(57, 35, 18, 0.14);
|
||||
--radius-lg: 28px;
|
||||
--radius-md: 18px;
|
||||
--shadow: 0 24px 60px rgba(33, 24, 17, 0.1);
|
||||
--radius-lg: 32px;
|
||||
--radius-md: 20px;
|
||||
--radius-sm: 12px;
|
||||
}
|
||||
|
||||
@@ -28,12 +29,12 @@ html {
|
||||
body {
|
||||
margin: 0;
|
||||
min-height: 100vh;
|
||||
font-family: "Avenir Next", "Segoe UI", sans-serif;
|
||||
font-family: "Trebuchet MS", "Gill Sans", sans-serif;
|
||||
color: var(--text);
|
||||
background:
|
||||
radial-gradient(circle at top left, rgba(203, 135, 77, 0.25), transparent 28%),
|
||||
radial-gradient(circle at 85% 15%, rgba(122, 52, 18, 0.12), transparent 22%),
|
||||
linear-gradient(180deg, #faf4eb 0%, var(--bg) 100%);
|
||||
radial-gradient(circle at top left, rgba(214, 171, 135, 0.2), transparent 28%),
|
||||
radial-gradient(circle at 90% 12%, rgba(59, 95, 82, 0.08), transparent 26%),
|
||||
linear-gradient(180deg, #fbf8f2 0%, var(--bg) 100%);
|
||||
}
|
||||
|
||||
a {
|
||||
@@ -64,8 +65,8 @@ textarea {
|
||||
top: 18px;
|
||||
z-index: 20;
|
||||
margin-bottom: 32px;
|
||||
background: rgba(252, 248, 241, 0.82);
|
||||
border: 1px solid rgba(255, 255, 255, 0.7);
|
||||
background: rgba(252, 248, 241, 0.88);
|
||||
border: 1px solid rgba(255, 255, 255, 0.75);
|
||||
border-radius: 999px;
|
||||
backdrop-filter: blur(16px);
|
||||
box-shadow: 0 12px 30px rgba(58, 39, 24, 0.08);
|
||||
@@ -75,14 +76,14 @@ textarea {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 14px;
|
||||
font-family: Georgia, "Times New Roman", serif;
|
||||
font-family: "Baskerville", "Iowan Old Style", Georgia, serif;
|
||||
}
|
||||
|
||||
.brand strong,
|
||||
h1,
|
||||
h2,
|
||||
h3 {
|
||||
font-family: Georgia, "Times New Roman", serif;
|
||||
font-family: "Baskerville", "Iowan Old Style", Georgia, serif;
|
||||
letter-spacing: -0.02em;
|
||||
}
|
||||
|
||||
@@ -90,7 +91,7 @@ h3 {
|
||||
display: block;
|
||||
color: var(--muted);
|
||||
font-size: 0.8rem;
|
||||
font-family: "Avenir Next", "Segoe UI", sans-serif;
|
||||
font-family: "Trebuchet MS", "Gill Sans", sans-serif;
|
||||
}
|
||||
|
||||
.brand-mark {
|
||||
@@ -140,7 +141,7 @@ h3 {
|
||||
}
|
||||
|
||||
.button-secondary {
|
||||
background: rgba(255, 255, 255, 0.72);
|
||||
background: rgba(255, 255, 255, 0.78);
|
||||
color: var(--text);
|
||||
border: 1px solid var(--line);
|
||||
}
|
||||
@@ -167,8 +168,10 @@ main {
|
||||
}
|
||||
|
||||
.hero-section,
|
||||
.trust-bar,
|
||||
.feature-strip,
|
||||
.content-grid,
|
||||
.occasion-section,
|
||||
.pricing-panel,
|
||||
.availability-section,
|
||||
.booking-section,
|
||||
@@ -183,9 +186,9 @@ main {
|
||||
|
||||
.hero-section {
|
||||
display: grid;
|
||||
grid-template-columns: 1.2fr 0.9fr;
|
||||
gap: 28px;
|
||||
padding: 42px;
|
||||
grid-template-columns: 1.08fr 0.92fr;
|
||||
gap: 34px;
|
||||
padding: 48px;
|
||||
}
|
||||
|
||||
.eyebrow {
|
||||
@@ -201,8 +204,8 @@ main {
|
||||
.admin-login-card h1,
|
||||
.section-header h1 {
|
||||
margin: 0;
|
||||
font-size: clamp(2.6rem, 5vw, 4.8rem);
|
||||
line-height: 0.96;
|
||||
font-size: clamp(2.8rem, 5vw, 5rem);
|
||||
line-height: 0.94;
|
||||
}
|
||||
|
||||
.hero-text,
|
||||
@@ -221,25 +224,59 @@ main {
|
||||
margin: 28px 0 26px;
|
||||
}
|
||||
|
||||
.hero-points,
|
||||
.check-list,
|
||||
.compact-list {
|
||||
margin: 0;
|
||||
padding-left: 20px;
|
||||
color: var(--muted);
|
||||
.hero-highlight-grid {
|
||||
display: grid;
|
||||
gap: 10px;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.hero-highlight-card,
|
||||
.feature-strip article,
|
||||
.content-block,
|
||||
.pricing-aside,
|
||||
.booking-form-card,
|
||||
.table-card,
|
||||
.admin-login-card,
|
||||
.booking-info-card,
|
||||
.availability-card,
|
||||
.trust-bar article,
|
||||
.occasion-grid article,
|
||||
.form-section {
|
||||
background: rgba(255, 255, 255, 0.76);
|
||||
border: 1px solid var(--line);
|
||||
border-radius: var(--radius-md);
|
||||
}
|
||||
|
||||
.hero-highlight-card {
|
||||
padding: 16px 18px;
|
||||
}
|
||||
|
||||
.hero-highlight-card span {
|
||||
display: block;
|
||||
color: var(--trust);
|
||||
font-size: 0.76rem;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.12em;
|
||||
margin-bottom: 8px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.hero-highlight-card strong {
|
||||
display: block;
|
||||
font-size: 1rem;
|
||||
line-height: 1.35;
|
||||
}
|
||||
|
||||
.hero-card {
|
||||
display: grid;
|
||||
gap: 16px;
|
||||
padding: 18px;
|
||||
padding: 22px;
|
||||
background:
|
||||
linear-gradient(145deg, rgba(255, 244, 232, 0.92), rgba(246, 225, 209, 0.85)),
|
||||
linear-gradient(145deg, rgba(255, 251, 246, 0.95), rgba(246, 233, 220, 0.9)),
|
||||
var(--card);
|
||||
border-radius: 24px;
|
||||
min-height: 520px;
|
||||
border: 1px solid rgba(104, 65, 39, 0.12);
|
||||
}
|
||||
|
||||
.hero-card-panel {
|
||||
@@ -248,7 +285,7 @@ main {
|
||||
gap: 16px;
|
||||
padding: 16px 18px;
|
||||
border-radius: 18px;
|
||||
background: rgba(255, 255, 255, 0.55);
|
||||
background: rgba(255, 255, 255, 0.58);
|
||||
color: var(--muted);
|
||||
}
|
||||
|
||||
@@ -262,56 +299,142 @@ main {
|
||||
place-items: center;
|
||||
overflow: hidden;
|
||||
border-radius: 24px;
|
||||
min-height: 320px;
|
||||
background: linear-gradient(180deg, rgba(118, 59, 31, 0.15), rgba(33, 18, 11, 0.24));
|
||||
min-height: 340px;
|
||||
background:
|
||||
radial-gradient(circle at 50% 12%, rgba(255, 243, 229, 0.95), transparent 28%),
|
||||
linear-gradient(180deg, rgba(212, 189, 170, 0.42), rgba(57, 43, 35, 0.08));
|
||||
}
|
||||
|
||||
.camera-glow {
|
||||
.device-plinth {
|
||||
position: absolute;
|
||||
width: 240px;
|
||||
height: 240px;
|
||||
border-radius: 50%;
|
||||
background: radial-gradient(circle, rgba(255, 228, 195, 0.9) 0%, rgba(241, 179, 119, 0.15) 58%, transparent 72%);
|
||||
bottom: 32px;
|
||||
width: 220px;
|
||||
height: 24px;
|
||||
border-radius: 999px;
|
||||
background: rgba(52, 38, 30, 0.12);
|
||||
filter: blur(6px);
|
||||
}
|
||||
|
||||
.camera-body {
|
||||
.camera-tower {
|
||||
position: relative;
|
||||
width: 280px;
|
||||
height: 200px;
|
||||
display: grid;
|
||||
justify-items: center;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.camera-head {
|
||||
position: relative;
|
||||
width: 210px;
|
||||
height: 230px;
|
||||
border-radius: 30px;
|
||||
background: linear-gradient(180deg, #2a2320 0%, #13100f 100%);
|
||||
background: linear-gradient(180deg, #25201c 0%, #12100e 100%);
|
||||
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.06), 0 18px 42px rgba(0, 0, 0, 0.35);
|
||||
}
|
||||
|
||||
.camera-body::before {
|
||||
.camera-head::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
inset: -22px 40px auto auto;
|
||||
width: 90px;
|
||||
height: 70px;
|
||||
border-radius: 24px 24px 8px 8px;
|
||||
inset: 18px 18px auto auto;
|
||||
width: 58px;
|
||||
height: 90px;
|
||||
border-radius: 18px;
|
||||
background: linear-gradient(180deg, rgba(239, 179, 137, 0.9), rgba(255, 252, 248, 0.18));
|
||||
}
|
||||
|
||||
.camera-head::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
inset: -20px auto auto 34px;
|
||||
width: 72px;
|
||||
height: 52px;
|
||||
border-radius: 18px 18px 8px 8px;
|
||||
background: linear-gradient(180deg, #28211e 0%, #141111 100%);
|
||||
}
|
||||
|
||||
.camera-lens {
|
||||
position: absolute;
|
||||
inset: 34px auto auto 78px;
|
||||
width: 124px;
|
||||
height: 124px;
|
||||
inset: 54px auto auto 38px;
|
||||
width: 132px;
|
||||
height: 132px;
|
||||
border-radius: 50%;
|
||||
background:
|
||||
radial-gradient(circle at 42% 42%, rgba(95, 170, 221, 0.95) 0%, rgba(41, 86, 111, 0.9) 22%, rgba(4, 7, 10, 0.95) 58%, #050607 100%);
|
||||
border: 12px solid #4a403a;
|
||||
}
|
||||
|
||||
.camera-screen {
|
||||
.camera-flash {
|
||||
position: absolute;
|
||||
inset: 48px 28px auto auto;
|
||||
width: 56px;
|
||||
height: 92px;
|
||||
border-radius: 16px;
|
||||
background: linear-gradient(180deg, rgba(255, 188, 125, 0.85), rgba(255, 240, 224, 0.22));
|
||||
box-shadow: inset 0 0 0 2px rgba(255, 255, 255, 0.12);
|
||||
inset: 32px 28px auto auto;
|
||||
width: 26px;
|
||||
height: 26px;
|
||||
border-radius: 50%;
|
||||
background: radial-gradient(circle, rgba(255, 251, 245, 1) 0%, rgba(246, 197, 151, 0.78) 58%, rgba(255, 255, 255, 0.15) 100%);
|
||||
}
|
||||
|
||||
.camera-stand {
|
||||
width: 18px;
|
||||
height: 108px;
|
||||
background: linear-gradient(180deg, #161311 0%, #3c342f 100%);
|
||||
}
|
||||
|
||||
.camera-base {
|
||||
width: 160px;
|
||||
height: 14px;
|
||||
border-radius: 999px;
|
||||
background: #241d18;
|
||||
}
|
||||
|
||||
.photo-strip {
|
||||
position: absolute;
|
||||
top: 46px;
|
||||
display: grid;
|
||||
gap: 10px;
|
||||
padding: 14px 10px;
|
||||
border-radius: 18px;
|
||||
background: rgba(255, 255, 255, 0.78);
|
||||
border: 1px solid rgba(31, 21, 14, 0.08);
|
||||
color: var(--trust);
|
||||
font-weight: 700;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.08em;
|
||||
font-size: 0.76rem;
|
||||
}
|
||||
|
||||
.photo-strip-left {
|
||||
left: 28px;
|
||||
transform: rotate(-8deg);
|
||||
}
|
||||
|
||||
.photo-strip-right {
|
||||
right: 28px;
|
||||
top: 80px;
|
||||
transform: rotate(7deg);
|
||||
}
|
||||
|
||||
.trust-bar {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||
gap: 14px;
|
||||
padding: 18px;
|
||||
}
|
||||
|
||||
.trust-bar article {
|
||||
padding: 18px 20px;
|
||||
}
|
||||
|
||||
.trust-bar span {
|
||||
display: block;
|
||||
font-size: 0.78rem;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.12em;
|
||||
color: var(--trust);
|
||||
margin-bottom: 10px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.trust-bar strong {
|
||||
display: block;
|
||||
line-height: 1.35;
|
||||
}
|
||||
|
||||
.feature-strip {
|
||||
@@ -323,19 +446,9 @@ main {
|
||||
|
||||
.feature-strip article,
|
||||
.content-block,
|
||||
.pricing-aside,
|
||||
.booking-form-card,
|
||||
.table-card,
|
||||
.admin-login-card,
|
||||
.booking-info-card,
|
||||
.availability-card {
|
||||
background: rgba(255, 255, 255, 0.7);
|
||||
border: 1px solid var(--line);
|
||||
border-radius: var(--radius-md);
|
||||
}
|
||||
|
||||
.feature-strip article {
|
||||
padding: 24px;
|
||||
.admin-login-card {
|
||||
padding: 28px;
|
||||
}
|
||||
|
||||
.feature-strip h2,
|
||||
@@ -343,7 +456,8 @@ main {
|
||||
.pricing-panel h2,
|
||||
.booking-copy h2,
|
||||
.faq-section h2,
|
||||
.table-card h2 {
|
||||
.table-card h2,
|
||||
.section-heading h2 {
|
||||
margin: 0 0 12px;
|
||||
font-size: clamp(1.6rem, 3vw, 2.3rem);
|
||||
}
|
||||
@@ -360,7 +474,8 @@ main {
|
||||
.stack-form span,
|
||||
.stack-form label,
|
||||
.booking-form span,
|
||||
.form-note {
|
||||
.form-note,
|
||||
.occasion-grid span {
|
||||
color: var(--muted);
|
||||
line-height: 1.6;
|
||||
}
|
||||
@@ -374,12 +489,6 @@ main {
|
||||
padding: 24px;
|
||||
}
|
||||
|
||||
.content-block,
|
||||
.table-card,
|
||||
.admin-login-card {
|
||||
padding: 28px;
|
||||
}
|
||||
|
||||
.step-list {
|
||||
display: grid;
|
||||
gap: 18px;
|
||||
@@ -391,14 +500,41 @@ main {
|
||||
.step-list li {
|
||||
display: grid;
|
||||
gap: 6px;
|
||||
padding: 16px 18px;
|
||||
border-radius: var(--radius-sm);
|
||||
background: rgba(246, 232, 221, 0.72);
|
||||
padding: 18px 20px;
|
||||
border-radius: 18px;
|
||||
background: rgba(246, 238, 229, 0.72);
|
||||
border-left: 4px solid var(--accent);
|
||||
}
|
||||
|
||||
.step-list strong {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.step-number {
|
||||
display: inline-grid;
|
||||
place-items: center;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
border-radius: 50%;
|
||||
background: var(--text);
|
||||
color: #fff;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.check-list,
|
||||
.compact-list {
|
||||
margin: 0;
|
||||
padding-left: 20px;
|
||||
display: grid;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.pricing-panel,
|
||||
.availability-section,
|
||||
.faq-section {
|
||||
.faq-section,
|
||||
.occasion-section {
|
||||
padding: 28px;
|
||||
}
|
||||
|
||||
@@ -420,6 +556,22 @@ main {
|
||||
font-size: 1.45rem;
|
||||
}
|
||||
|
||||
.section-heading {
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
|
||||
.occasion-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.occasion-grid article {
|
||||
padding: 22px;
|
||||
display: grid;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.availability-list,
|
||||
.faq-list,
|
||||
.stats-grid {
|
||||
@@ -438,6 +590,11 @@ main {
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.availability-card small {
|
||||
color: var(--trust);
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.availability-card-empty {
|
||||
grid-column: 1 / -1;
|
||||
}
|
||||
@@ -456,16 +613,45 @@ main {
|
||||
padding: 28px;
|
||||
}
|
||||
|
||||
.booking-form,
|
||||
.stack-form {
|
||||
display: grid;
|
||||
gap: 18px;
|
||||
}
|
||||
|
||||
.form-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.booking-form,
|
||||
.stack-form {
|
||||
display: grid;
|
||||
gap: 18px;
|
||||
.form-grid-two {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
|
||||
.form-section {
|
||||
padding: 18px;
|
||||
}
|
||||
|
||||
.form-section-header {
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
|
||||
.form-section-header h3 {
|
||||
margin: 6px 0 0;
|
||||
font-size: 1.3rem;
|
||||
}
|
||||
|
||||
.form-step {
|
||||
font-size: 0.78rem;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.12em;
|
||||
color: var(--trust);
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.field-full {
|
||||
grid-column: 1 / -1;
|
||||
}
|
||||
|
||||
label {
|
||||
@@ -491,11 +677,16 @@ textarea {
|
||||
|
||||
.price-summary {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
gap: 16px;
|
||||
padding: 18px;
|
||||
border-radius: 18px;
|
||||
background: rgba(245, 223, 202, 0.55);
|
||||
background: linear-gradient(135deg, rgba(244, 230, 217, 0.78), rgba(220, 234, 228, 0.72));
|
||||
}
|
||||
|
||||
.price-summary-total {
|
||||
border-left: 1px solid rgba(31, 21, 14, 0.12);
|
||||
padding-left: 16px;
|
||||
}
|
||||
|
||||
.flash {
|
||||
@@ -627,6 +818,10 @@ th {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.theme-admin .site-header {
|
||||
background: rgba(250, 248, 244, 0.92);
|
||||
}
|
||||
|
||||
@media (max-width: 1080px) {
|
||||
.hero-section,
|
||||
.content-grid,
|
||||
@@ -637,6 +832,11 @@ th {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.trust-bar,
|
||||
.occasion-grid {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
|
||||
.stats-grid {
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
}
|
||||
@@ -659,6 +859,9 @@ th {
|
||||
.feature-strip,
|
||||
.faq-list,
|
||||
.form-grid,
|
||||
.hero-highlight-grid,
|
||||
.trust-bar,
|
||||
.occasion-grid,
|
||||
.price-summary {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
@@ -674,7 +877,8 @@ th {
|
||||
.availability-section,
|
||||
.booking-section,
|
||||
.faq-section,
|
||||
.admin-section {
|
||||
.admin-section,
|
||||
.occasion-section {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
@@ -683,6 +887,11 @@ th {
|
||||
.section-header h1 {
|
||||
font-size: 2.35rem;
|
||||
}
|
||||
|
||||
.price-summary-total {
|
||||
border-left: 0;
|
||||
padding-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 520px) {
|
||||
|
||||
@@ -3,6 +3,9 @@
|
||||
declare(strict_types=1);
|
||||
|
||||
return [
|
||||
'app' => [
|
||||
'base_path' => getenv('FOTOBOX_BASE_PATH') ?: '',
|
||||
],
|
||||
'company' => [
|
||||
'name' => 'Fotobox Moments',
|
||||
'tagline' => 'Fotobox-Vermietung fuer Hochzeiten, Geburtstage und Firmenevents',
|
||||
|
||||
@@ -2,6 +2,16 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
function setAppConfig(array $config): void
|
||||
{
|
||||
$GLOBALS['app_config'] = $config;
|
||||
}
|
||||
|
||||
function appConfig(): array
|
||||
{
|
||||
return $GLOBALS['app_config'] ?? [];
|
||||
}
|
||||
|
||||
function render(string $view, array $data = []): void
|
||||
{
|
||||
extract($data, EXTR_SKIP);
|
||||
@@ -9,14 +19,75 @@ function render(string $view, array $data = []): void
|
||||
require dirname(__DIR__, 1) . '/../views/layout.php';
|
||||
}
|
||||
|
||||
function basePath(): string
|
||||
{
|
||||
$configured = trim((string) (appConfig()['app']['base_path'] ?? ''));
|
||||
if ($configured !== '' && $configured !== '/') {
|
||||
return '/' . trim($configured, '/');
|
||||
}
|
||||
|
||||
$forwardedPrefix = trim((string) ($_SERVER['HTTP_X_FORWARDED_PREFIX'] ?? ''));
|
||||
if ($forwardedPrefix !== '' && $forwardedPrefix !== '/') {
|
||||
return '/' . trim($forwardedPrefix, '/');
|
||||
}
|
||||
|
||||
$requestPath = parse_url($_SERVER['REQUEST_URI'] ?? '/', PHP_URL_PATH) ?: '/';
|
||||
|
||||
$knownSuffixes = [
|
||||
'/admin/order/invoice',
|
||||
'/admin/order/update',
|
||||
'/admin/invoice/pdf',
|
||||
'/admin/create',
|
||||
'/admin/order',
|
||||
'/admin/login',
|
||||
'/admin/logout',
|
||||
'/admin',
|
||||
'/book',
|
||||
'/assets/styles.css',
|
||||
'/assets/app.js',
|
||||
'/',
|
||||
];
|
||||
|
||||
foreach ($knownSuffixes as $suffix) {
|
||||
if ($suffix === '/') {
|
||||
if ($requestPath !== '/' && str_ends_with($requestPath, '/')) {
|
||||
return rtrim($requestPath, '/');
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($requestPath === $suffix) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if (str_ends_with($requestPath, $suffix)) {
|
||||
$prefix = substr($requestPath, 0, -strlen($suffix));
|
||||
return $prefix === false ? '' : rtrim($prefix, '/');
|
||||
}
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
function url(string $path = ''): string
|
||||
{
|
||||
$base = basePath();
|
||||
if ($path === '' || $path === '/') {
|
||||
return $base === '' ? '/' : $base;
|
||||
}
|
||||
|
||||
return ($base === '' ? '' : $base) . '/' . ltrim($path, '/');
|
||||
}
|
||||
|
||||
function asset(string $path): string
|
||||
{
|
||||
return '/' . ltrim($path, '/');
|
||||
return url($path);
|
||||
}
|
||||
|
||||
function redirect(string $path): void
|
||||
{
|
||||
header('Location: ' . $path);
|
||||
header('Location: ' . url($path));
|
||||
exit;
|
||||
}
|
||||
|
||||
|
||||
+56
-4
@@ -14,6 +14,7 @@ session_start();
|
||||
function runApplication(): void
|
||||
{
|
||||
$config = require dirname(__DIR__) . '/config.php';
|
||||
setAppConfig($config);
|
||||
|
||||
[$bookingRepository, $invoiceRepository] = resolveRepositories($config);
|
||||
|
||||
@@ -21,7 +22,11 @@ function runApplication(): void
|
||||
$invoicePdfService = new InvoicePdfService($config);
|
||||
|
||||
$method = $_SERVER['REQUEST_METHOD'] ?? 'GET';
|
||||
$path = parse_url($_SERVER['REQUEST_URI'] ?? '/', PHP_URL_PATH) ?: '/';
|
||||
$path = requestPath();
|
||||
|
||||
if (serveStaticAssetIfRequested($path)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($method === 'POST' && $path === '/book') {
|
||||
handlePublicBooking($bookingService);
|
||||
@@ -52,6 +57,53 @@ function runApplication(): void
|
||||
renderHome($bookingService, $config);
|
||||
}
|
||||
|
||||
function requestPath(): string
|
||||
{
|
||||
$path = parse_url($_SERVER['REQUEST_URI'] ?? '/', PHP_URL_PATH) ?: '/';
|
||||
$basePath = basePath();
|
||||
|
||||
if ($basePath !== '' && str_starts_with($path, $basePath)) {
|
||||
$path = substr($path, strlen($basePath)) ?: '/';
|
||||
}
|
||||
|
||||
if ($path === '') {
|
||||
return '/';
|
||||
}
|
||||
|
||||
return '/' . ltrim($path, '/');
|
||||
}
|
||||
|
||||
function serveStaticAssetIfRequested(string $path): bool
|
||||
{
|
||||
if (!str_starts_with($path, '/assets/')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$file = dirname(__DIR__) . $path;
|
||||
if (!is_file($file)) {
|
||||
http_response_code(404);
|
||||
echo 'Datei nicht gefunden.';
|
||||
return true;
|
||||
}
|
||||
|
||||
$extension = strtolower(pathinfo($file, PATHINFO_EXTENSION));
|
||||
$mimeTypes = [
|
||||
'css' => 'text/css; charset=UTF-8',
|
||||
'js' => 'application/javascript; charset=UTF-8',
|
||||
'svg' => 'image/svg+xml',
|
||||
'png' => 'image/png',
|
||||
'jpg' => 'image/jpeg',
|
||||
'jpeg' => 'image/jpeg',
|
||||
'webp' => 'image/webp',
|
||||
];
|
||||
|
||||
header('Content-Type: ' . ($mimeTypes[$extension] ?? 'application/octet-stream'));
|
||||
header('Content-Length: ' . (string) filesize($file));
|
||||
readfile($file);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function resolveRepositories(array $config): array
|
||||
{
|
||||
$databaseFile = $config['database']['credentials_file'];
|
||||
@@ -155,17 +207,17 @@ function handleAdminRequest(string $path, string $method, BookingService $bookin
|
||||
flash('error', $exception->getMessage());
|
||||
}
|
||||
|
||||
redirect('/admin/order?id=' . urlencode((string) ($_POST['booking_id'] ?? '')));
|
||||
redirect('admin/order?id=' . urlencode((string) ($_POST['booking_id'] ?? '')));
|
||||
}
|
||||
|
||||
if ($method === 'POST' && $path === '/admin/order/invoice') {
|
||||
try {
|
||||
$invoiceId = $bookingService->createInvoiceForBooking((string) ($_POST['booking_id'] ?? ''), $_POST);
|
||||
flash('success', 'Die Rechnung wurde erstellt.');
|
||||
redirect('/admin/order?id=' . urlencode((string) ($_POST['booking_id'] ?? '')) . '&invoice=' . urlencode($invoiceId));
|
||||
redirect('admin/order?id=' . urlencode((string) ($_POST['booking_id'] ?? '')) . '&invoice=' . urlencode($invoiceId));
|
||||
} catch (Throwable $exception) {
|
||||
flash('error', $exception->getMessage());
|
||||
redirect('/admin/order?id=' . urlencode((string) ($_POST['booking_id'] ?? '')));
|
||||
redirect('admin/order?id=' . urlencode((string) ($_POST['booking_id'] ?? '')));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<p class="eyebrow">Manuelle Buchung</p>
|
||||
<h1>Bestellung fuer Kunden anlegen</h1>
|
||||
</div>
|
||||
<a class="button-secondary" href="/admin">Zurueck zum Dashboard</a>
|
||||
<a class="button-secondary" href="<?= h(url('admin')) ?>">Zurueck zum Dashboard</a>
|
||||
</div>
|
||||
|
||||
<?php if (!empty($flashSuccess)): ?>
|
||||
@@ -14,7 +14,7 @@
|
||||
<div class="flash flash-error"><?= h((string) $flashError) ?></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<form method="post" action="/admin/create" class="booking-form admin-form" data-day-rate="<?= h((string) $defaults['price_per_day_cents']) ?>">
|
||||
<form method="post" action="<?= h(url('admin/create')) ?>" class="booking-form admin-form" data-day-rate="<?= h((string) $defaults['price_per_day_cents']) ?>">
|
||||
<div class="form-grid">
|
||||
<label>
|
||||
<span>Name</span>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<p class="eyebrow">Dashboard</p>
|
||||
<h1>Anfragen, Buchungen und Rechnungen</h1>
|
||||
</div>
|
||||
<a class="button-primary" href="/admin/create">Bestellung fuer Kunden anlegen</a>
|
||||
<a class="button-primary" href="<?= h(url('admin/create')) ?>">Bestellung fuer Kunden anlegen</a>
|
||||
</div>
|
||||
|
||||
<?php if (!empty($flashSuccess)): ?>
|
||||
@@ -62,7 +62,7 @@
|
||||
<tbody>
|
||||
<?php foreach ($bookings as $booking): ?>
|
||||
<tr>
|
||||
<td><a href="/admin/order?id=<?= h(urlencode($booking['id'])) ?>"><?= h($booking['reference']) ?></a></td>
|
||||
<td><a href="<?= h(url('admin/order?id=' . urlencode($booking['id']))) ?>"><?= h($booking['reference']) ?></a></td>
|
||||
<td><?= h($booking['customer']['name']) ?></td>
|
||||
<td><?= h(formatDate($booking['start_date'])) ?> - <?= h(formatDate($booking['end_date'])) ?></td>
|
||||
<td><?= h($booking['status_label']) ?></td>
|
||||
@@ -99,7 +99,7 @@
|
||||
<tbody>
|
||||
<?php foreach ($invoices as $invoice): ?>
|
||||
<tr>
|
||||
<td><a href="/admin/invoice/pdf?id=<?= h(urlencode($invoice['id'])) ?>" target="_blank"><?= h($invoice['invoice_number']) ?></a></td>
|
||||
<td><a href="<?= h(url('admin/invoice/pdf?id=' . urlencode($invoice['id']))) ?>" target="_blank"><?= h($invoice['invoice_number']) ?></a></td>
|
||||
<td><?= h($invoice['booking_id']) ?></td>
|
||||
<td><?= h(formatDate($invoice['due_date'])) ?></td>
|
||||
<td><?= h($invoice['payment_method_label']) ?></td>
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
<div class="flash flash-error"><?= h((string) $flashError) ?></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<form method="post" action="/admin/login" class="stack-form">
|
||||
<form method="post" action="<?= h(url('admin/login')) ?>" class="stack-form">
|
||||
<label>
|
||||
<span>Benutzername</span>
|
||||
<input type="text" name="username" value="admin" required>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<p class="eyebrow">Auftragsdetail</p>
|
||||
<h1><?= h($booking['reference']) ?></h1>
|
||||
</div>
|
||||
<a class="button-secondary" href="/admin">Zurueck zum Dashboard</a>
|
||||
<a class="button-secondary" href="<?= h(url('admin')) ?>">Zurueck zum Dashboard</a>
|
||||
</div>
|
||||
|
||||
<?php if (!empty($flashSuccess)): ?>
|
||||
@@ -34,7 +34,7 @@
|
||||
|
||||
<article class="table-card">
|
||||
<h2>Verwaltung</h2>
|
||||
<form method="post" action="/admin/order/update" class="stack-form">
|
||||
<form method="post" action="<?= h(url('admin/order/update')) ?>" class="stack-form">
|
||||
<input type="hidden" name="booking_id" value="<?= h($booking['id']) ?>">
|
||||
<label>
|
||||
<span>Status</span>
|
||||
@@ -75,10 +75,10 @@
|
||||
<li>Gesamtbetrag <?= h(formatCurrency((int) $invoice['total_cents'])) ?></li>
|
||||
<li>Zahlungsart <?= h($invoice['payment_method_label']) ?></li>
|
||||
</ul>
|
||||
<a class="button-secondary" href="/admin/invoice/pdf?id=<?= h(urlencode($invoice['id'])) ?>" target="_blank">PDF oeffnen</a>
|
||||
<a class="button-secondary" href="<?= h(url('admin/invoice/pdf?id=' . urlencode($invoice['id']))) ?>" target="_blank">PDF oeffnen</a>
|
||||
<?php else: ?>
|
||||
<p>Fuer diesen Auftrag wurde noch keine Rechnung erstellt.</p>
|
||||
<form method="post" action="/admin/order/invoice" class="stack-form">
|
||||
<form method="post" action="<?= h(url('admin/order/invoice')) ?>" class="stack-form">
|
||||
<input type="hidden" name="booking_id" value="<?= h($booking['id']) ?>">
|
||||
<label>
|
||||
<span>Faelligkeitsdatum</span>
|
||||
|
||||
+132
-45
@@ -5,32 +5,58 @@ $company = $config['company'];
|
||||
<section class="hero-section">
|
||||
<div class="hero-copy">
|
||||
<p class="eyebrow">Fotobox-Vermietung neu gedacht</p>
|
||||
<h1>Die Fotobox fuer Hochzeiten, Geburtstage und Firmenevents</h1>
|
||||
<h1>Fotobox mieten. Online anfragen. Fotos direkt aufs Handy.</h1>
|
||||
<p class="hero-text">
|
||||
Unkompliziert mieten, schnell aufbauen, kinderleicht bedienen und alle Bilder direkt digital sichern.
|
||||
Unsere Fotobox verbindet hochwertige Technik mit einem klaren Buchungsprozess ohne Shop-Chaos.
|
||||
Fuer Hochzeiten, Geburtstage, Firmenfeiern und Jubilaeen: hochwertige Fotobox-Technik,
|
||||
einfache Bedienung, flexible Lieferung und ein klarer Buchungsprozess ohne Shop-Chaos.
|
||||
</p>
|
||||
<div class="hero-actions">
|
||||
<a class="button-primary" href="#buchung">Verfuegbarkeit pruefen</a>
|
||||
<a class="button-secondary" href="#ablauf">So funktioniert die Miete</a>
|
||||
</div>
|
||||
<ul class="hero-points">
|
||||
<li>Spiegelreflexkamera, Studioblitz und Softbox</li>
|
||||
<li>WLAN-Download direkt aufs Handy</li>
|
||||
<li>Selbstabholung oder Lieferung mit Aufbau</li>
|
||||
<li>99,99 EUR pro Kalendertag</li>
|
||||
</ul>
|
||||
<div class="hero-highlight-grid">
|
||||
<article class="hero-highlight-card">
|
||||
<span>Technik</span>
|
||||
<strong>DSLR, Blitz und Softbox</strong>
|
||||
</article>
|
||||
<article class="hero-highlight-card">
|
||||
<span>Sharing</span>
|
||||
<strong>WLAN-Download direkt vor Ort</strong>
|
||||
</article>
|
||||
<article class="hero-highlight-card">
|
||||
<span>Logistik</span>
|
||||
<strong>Abholung oder Lieferung</strong>
|
||||
</article>
|
||||
<article class="hero-highlight-card">
|
||||
<span>Preis</span>
|
||||
<strong>99,99 EUR pro Kalendertag</strong>
|
||||
</article>
|
||||
</div>
|
||||
</div>
|
||||
<div class="hero-card">
|
||||
<div class="hero-card-panel hero-card-panel-top">
|
||||
<span>Eventbereit in wenigen Minuten</span>
|
||||
<strong>Abholung ab 17:00 Uhr</strong>
|
||||
<span>Produktionsreif fuer dein Event</span>
|
||||
<strong>einsatzklar in wenigen Minuten</strong>
|
||||
</div>
|
||||
<div class="hero-card-visual">
|
||||
<div class="camera-glow"></div>
|
||||
<div class="camera-body">
|
||||
<div class="device-plinth"></div>
|
||||
<div class="photo-strip photo-strip-left">
|
||||
<span>WLAN</span>
|
||||
<span>Live</span>
|
||||
<span>Shots</span>
|
||||
</div>
|
||||
<div class="camera-tower">
|
||||
<div class="camera-head">
|
||||
<div class="camera-lens"></div>
|
||||
<div class="camera-screen"></div>
|
||||
<div class="camera-flash"></div>
|
||||
</div>
|
||||
<div class="camera-stand"></div>
|
||||
<div class="camera-base"></div>
|
||||
</div>
|
||||
<div class="photo-strip photo-strip-right">
|
||||
<span>Reel</span>
|
||||
<span>Share</span>
|
||||
<span>Smile</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="hero-card-panel">
|
||||
@@ -40,6 +66,25 @@ $company = $config['company'];
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="trust-bar">
|
||||
<article>
|
||||
<span>Tagessatz</span>
|
||||
<strong>99,99 EUR / Tag</strong>
|
||||
</article>
|
||||
<article>
|
||||
<span>Bilduebergabe</span>
|
||||
<strong>alle Fotos digital</strong>
|
||||
</article>
|
||||
<article>
|
||||
<span>Zahlung</span>
|
||||
<strong>Rechnung oder PayPal</strong>
|
||||
</article>
|
||||
<article>
|
||||
<span>Verfuegbarkeit</span>
|
||||
<strong>online und verwaltbar</strong>
|
||||
</article>
|
||||
</section>
|
||||
|
||||
<section class="feature-strip" id="leistungen">
|
||||
<article>
|
||||
<h2>Technik, die nicht zickt</h2>
|
||||
@@ -61,19 +106,19 @@ $company = $config['company'];
|
||||
<h2>So laeuft die Miete ab</h2>
|
||||
<ol class="step-list">
|
||||
<li>
|
||||
<strong>1. Zeitraum waehlen</strong>
|
||||
<strong><span class="step-number">1</span> Zeitraum waehlen</strong>
|
||||
<span>Du waehlst Mietbeginn und Mietende und siehst sofort die voraussichtlichen Kosten.</span>
|
||||
</li>
|
||||
<li>
|
||||
<strong>2. Leistung festlegen</strong>
|
||||
<strong><span class="step-number">2</span> Leistung festlegen</strong>
|
||||
<span>Selbstabholung, Lieferung mit Aufbau oder Vor-Ort-Betreuung passend zu deinem Event.</span>
|
||||
</li>
|
||||
<li>
|
||||
<strong>3. Anfrage absenden</strong>
|
||||
<strong><span class="step-number">3</span> Anfrage absenden</strong>
|
||||
<span>Wir speichern alle Kundendaten und bereiten auf Wunsch direkt die Rechnungsabwicklung vor.</span>
|
||||
</li>
|
||||
<li>
|
||||
<strong>4. Fotos geniessen</strong>
|
||||
<strong><span class="step-number">4</span> Fotos geniessen</strong>
|
||||
<span>Am Eventtag steht die Box bereit und danach bekommst du alle Bilder digital zur Weitergabe.</span>
|
||||
</li>
|
||||
</ol>
|
||||
@@ -117,6 +162,19 @@ $company = $config['company'];
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="occasion-section">
|
||||
<div class="section-heading">
|
||||
<p class="eyebrow">Anlaesse</p>
|
||||
<h2>Passend fuer kleine Feiern und grosse Events</h2>
|
||||
</div>
|
||||
<div class="occasion-grid">
|
||||
<article><strong>Hochzeiten</strong><span>emotionale Erinnerungen, direkt teilbar</span></article>
|
||||
<article><strong>Geburtstage</strong><span>einfacher Aufbau, unkomplizierter Spass</span></article>
|
||||
<article><strong>Firmenfeiern</strong><span>sauberer Ablauf mit Rechnung und Verwaltung</span></article>
|
||||
<article><strong>Jubilaeen</strong><span>hochwertige Bilder ohne Fotostress</span></article>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="availability-section">
|
||||
<div>
|
||||
<p class="eyebrow">Online-Verfuegbarkeit</p>
|
||||
@@ -162,7 +220,53 @@ $company = $config['company'];
|
||||
<div class="flash flash-error"><?= h((string) $flashError) ?></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<form method="post" action="/book" class="booking-form" data-day-rate="<?= h((string) $dayRate) ?>">
|
||||
<form method="post" action="<?= h(url('book')) ?>" class="booking-form" data-day-rate="<?= h((string) $dayRate) ?>">
|
||||
<div class="form-section">
|
||||
<div class="form-section-header">
|
||||
<span class="form-step">Schritt 1</span>
|
||||
<h3>Zeitraum waehlen</h3>
|
||||
</div>
|
||||
<div class="form-grid form-grid-two">
|
||||
<label>
|
||||
<span>Mietbeginn</span>
|
||||
<input type="date" name="start_date" data-booking-start value="<?= h((string) ($old['start_date'] ?? '')) ?>" required>
|
||||
</label>
|
||||
<label>
|
||||
<span>Mietende</span>
|
||||
<input type="date" name="end_date" data-booking-end value="<?= h((string) ($old['end_date'] ?? '')) ?>" required>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-section">
|
||||
<div class="form-section-header">
|
||||
<span class="form-step">Schritt 2</span>
|
||||
<h3>Leistung und Zahlung</h3>
|
||||
</div>
|
||||
<div class="form-grid form-grid-two">
|
||||
<label>
|
||||
<span>Lieferart</span>
|
||||
<select name="delivery_mode">
|
||||
<option value="self_pickup" <?= selected((string) ($old['delivery_mode'] ?? 'self_pickup'), 'self_pickup') ?>>Selbstabholung</option>
|
||||
<option value="delivery_setup" <?= selected((string) ($old['delivery_mode'] ?? ''), 'delivery_setup') ?>>Lieferung und Aufbau</option>
|
||||
<option value="on_site_support" <?= selected((string) ($old['delivery_mode'] ?? ''), 'on_site_support') ?>>Lieferung, Aufbau und Vor-Ort-Betreuung</option>
|
||||
</select>
|
||||
</label>
|
||||
<label>
|
||||
<span>Zahlungsart</span>
|
||||
<select name="payment_method">
|
||||
<option value="invoice_transfer" <?= selected((string) ($old['payment_method'] ?? 'invoice_transfer'), 'invoice_transfer') ?>>Rechnung / Ueberweisung</option>
|
||||
<option value="paypal" <?= selected((string) ($old['payment_method'] ?? ''), 'paypal') ?>>PayPal</option>
|
||||
</select>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-section">
|
||||
<div class="form-section-header">
|
||||
<span class="form-step">Schritt 3</span>
|
||||
<h3>Kontaktdaten</h3>
|
||||
</div>
|
||||
<div class="form-grid">
|
||||
<label>
|
||||
<span>Name</span>
|
||||
@@ -196,34 +300,13 @@ $company = $config['company'];
|
||||
<span>Anlass</span>
|
||||
<input type="text" name="event_type" value="<?= h((string) ($old['event_type'] ?? '')) ?>" placeholder="z. B. Hochzeit oder Sommerfest">
|
||||
</label>
|
||||
<label>
|
||||
<label class="field-full">
|
||||
<span>Veranstaltungsort</span>
|
||||
<input type="text" name="event_location" value="<?= h((string) ($old['event_location'] ?? '')) ?>" placeholder="Location oder Stadt">
|
||||
</label>
|
||||
<label>
|
||||
<span>Mietbeginn</span>
|
||||
<input type="date" name="start_date" data-booking-start value="<?= h((string) ($old['start_date'] ?? '')) ?>" required>
|
||||
</label>
|
||||
<label>
|
||||
<span>Mietende</span>
|
||||
<input type="date" name="end_date" data-booking-end value="<?= h((string) ($old['end_date'] ?? '')) ?>" required>
|
||||
</label>
|
||||
<label>
|
||||
<span>Lieferart</span>
|
||||
<select name="delivery_mode">
|
||||
<option value="self_pickup" <?= selected((string) ($old['delivery_mode'] ?? 'self_pickup'), 'self_pickup') ?>>Selbstabholung</option>
|
||||
<option value="delivery_setup" <?= selected((string) ($old['delivery_mode'] ?? ''), 'delivery_setup') ?>>Lieferung und Aufbau</option>
|
||||
<option value="on_site_support" <?= selected((string) ($old['delivery_mode'] ?? ''), 'on_site_support') ?>>Lieferung, Aufbau und Vor-Ort-Betreuung</option>
|
||||
</select>
|
||||
</label>
|
||||
<label>
|
||||
<span>Zahlungsart</span>
|
||||
<select name="payment_method">
|
||||
<option value="invoice_transfer" <?= selected((string) ($old['payment_method'] ?? 'invoice_transfer'), 'invoice_transfer') ?>>Rechnung / Ueberweisung</option>
|
||||
<option value="paypal" <?= selected((string) ($old['payment_method'] ?? ''), 'paypal') ?>>PayPal</option>
|
||||
</select>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<label>
|
||||
<span>Nachricht</span>
|
||||
<textarea name="notes_customer" rows="4" placeholder="Sonderwuensche, Lieferdetails oder Aufbauhinweise"><?= h((string) ($old['notes_customer'] ?? '')) ?></textarea>
|
||||
@@ -235,13 +318,17 @@ $company = $config['company'];
|
||||
<strong data-summary-days>Noch nicht gewaehlt</strong>
|
||||
</div>
|
||||
<div>
|
||||
<span>Tagespreis</span>
|
||||
<strong><?= h(formatCurrency($dayRate)) ?></strong>
|
||||
</div>
|
||||
<div class="price-summary-total">
|
||||
<span>Gesamtpreis</span>
|
||||
<strong data-summary-total><?= h(formatCurrency($dayRate)) ?></strong>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="button-primary button-block">Anfrage absenden</button>
|
||||
<p class="form-note">Mit dem Absenden wird eine verwaltbare Anfrage angelegt. Die finale Bestaetigung erfolgt durch den Verwalter.</p>
|
||||
<button type="submit" class="button-primary button-block">Buchungsanfrage senden</button>
|
||||
<p class="form-note">Keine Sofortabbuchung. Der Verwalter bestaetigt den Termin, pflegt die Buchung und kann direkt eine Rechnung erzeugen.</p>
|
||||
</form>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
+8
-6
@@ -1,6 +1,8 @@
|
||||
<?php
|
||||
$metaTitle = isset($pageTitle) ? $pageTitle . ' | Fotobox Moments' : 'Fotobox Moments';
|
||||
$isAdminArea = str_contains($viewPath, '/admin/');
|
||||
$styleVersion = is_file(dirname(__DIR__) . '/assets/styles.css') ? (string) filemtime(dirname(__DIR__) . '/assets/styles.css') : '1';
|
||||
$scriptVersion = is_file(dirname(__DIR__) . '/assets/app.js') ? (string) filemtime(dirname(__DIR__) . '/assets/app.js') : '1';
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
@@ -9,13 +11,13 @@ $isAdminArea = str_contains($viewPath, '/admin/');
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title><?= h($metaTitle) ?></title>
|
||||
<meta name="description" content="Fotobox mieten mit einfacher Online-Anfrage, flexibler Lieferung und kompletter Admin-Verwaltung.">
|
||||
<link rel="stylesheet" href="<?= h(asset('assets/styles.css')) ?>">
|
||||
<script defer src="<?= h(asset('assets/app.js')) ?>"></script>
|
||||
<link rel="stylesheet" href="<?= h(asset('assets/styles.css')) ?>?v=<?= h($styleVersion) ?>">
|
||||
<script defer src="<?= h(asset('assets/app.js')) ?>?v=<?= h($scriptVersion) ?>"></script>
|
||||
</head>
|
||||
<body class="<?= $isAdminArea ? 'theme-admin' : 'theme-public' ?>">
|
||||
<div class="page-shell">
|
||||
<header class="site-header">
|
||||
<a class="brand" href="<?= $isAdminArea ? '/admin' : '/' ?>">
|
||||
<a class="brand" href="<?= h($isAdminArea ? url('admin') : url('/')) ?>">
|
||||
<span class="brand-mark">FM</span>
|
||||
<span>
|
||||
<strong>Fotobox Moments</strong>
|
||||
@@ -24,9 +26,9 @@ $isAdminArea = str_contains($viewPath, '/admin/');
|
||||
</a>
|
||||
<nav class="site-nav">
|
||||
<?php if ($isAdminArea): ?>
|
||||
<a href="/admin">Dashboard</a>
|
||||
<a href="/admin/create">Bestellung anlegen</a>
|
||||
<form method="post" action="/admin/logout">
|
||||
<a href="<?= h(url('admin')) ?>">Dashboard</a>
|
||||
<a href="<?= h(url('admin/create')) ?>">Bestellung anlegen</a>
|
||||
<form method="post" action="<?= h(url('admin/logout')) ?>">
|
||||
<button type="submit" class="ghost-button">Logout</button>
|
||||
</form>
|
||||
<?php else: ?>
|
||||
|
||||
Reference in New Issue
Block a user