Anpassungen event erstellung Sprint 1
This commit is contained in:
parent
c6d8df790e
commit
ad6b6c2c8c
BIN
assets/eventcreate_foodtable.jpg
Normal file
BIN
assets/eventcreate_foodtable.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 156 KiB |
@ -6,6 +6,25 @@
|
|||||||
--control-min-height: 3rem;
|
--control-min-height: 3rem;
|
||||||
--input-min-height: 3.5rem;
|
--input-min-height: 3.5rem;
|
||||||
--card-min-height: 6rem;
|
--card-min-height: 6rem;
|
||||||
|
|
||||||
|
--color-bg: var(--butter);
|
||||||
|
--color-surface: var(--white);
|
||||||
|
--color-surface-soft: var(--butter-light);
|
||||||
|
--color-text: var(--black);
|
||||||
|
--color-text-secondary: rgba(34, 33, 26, 0.8);
|
||||||
|
--color-muted: rgba(34, 33, 26, 0.68);
|
||||||
|
--color-border: rgba(102, 52, 13, 0.16);
|
||||||
|
--color-border-strong: var(--brown);
|
||||||
|
--color-divider: rgba(102, 52, 13, 0.14);
|
||||||
|
--color-primary: var(--olive);
|
||||||
|
--color-primary-hover: var(--olive-dark);
|
||||||
|
--color-progress-bg: rgba(212, 75, 36, 0.18);
|
||||||
|
--color-focus: var(--blue);
|
||||||
|
--color-error: var(--error);
|
||||||
|
--shadow-soft: 0 12px 30px rgba(102, 52, 13, 0.1);
|
||||||
|
--input-border-soft: rgba(102, 52, 13, 0.2);
|
||||||
|
--input-border-focus: rgba(107, 107, 5, 0.45);
|
||||||
|
--input-shadow-focus: 0 0 0 4px rgba(107, 107, 5, 0.12);
|
||||||
}
|
}
|
||||||
|
|
||||||
*,
|
*,
|
||||||
@ -108,6 +127,10 @@ a {
|
|||||||
padding: var(--space-4) 0 var(--space-7);
|
padding: var(--space-4) 0 var(--space-7);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.submission-success {
|
||||||
|
padding: var(--space-4) 0 var(--space-7);
|
||||||
|
}
|
||||||
|
|
||||||
.step--active {
|
.step--active {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
@ -123,6 +146,7 @@ a {
|
|||||||
min-height: 60vh;
|
min-height: 60vh;
|
||||||
align-content: center;
|
align-content: center;
|
||||||
grid-template-columns: 1fr;
|
grid-template-columns: 1fr;
|
||||||
|
gap: var(--space-7);
|
||||||
}
|
}
|
||||||
|
|
||||||
.step-copy,
|
.step-copy,
|
||||||
@ -185,9 +209,22 @@ h2 {
|
|||||||
background: linear-gradient(135deg, var(--color-surface), var(--color-surface-soft));
|
background: linear-gradient(135deg, var(--color-surface), var(--color-surface-soft));
|
||||||
}
|
}
|
||||||
|
|
||||||
.intro-card-emoji {
|
.intro-card--image {
|
||||||
font-size: 2rem;
|
width: 100%;
|
||||||
margin-bottom: var(--space-3);
|
padding: 0;
|
||||||
|
border: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
background: transparent;
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.intro-image {
|
||||||
|
width: 100%;
|
||||||
|
aspect-ratio: 16 / 10;
|
||||||
|
display: block;
|
||||||
|
object-fit: cover;
|
||||||
|
border-radius: 1.875rem;
|
||||||
|
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.12);
|
||||||
}
|
}
|
||||||
|
|
||||||
label,
|
label,
|
||||||
@ -208,11 +245,13 @@ input[type="number"],
|
|||||||
textarea {
|
textarea {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
min-height: var(--input-min-height);
|
min-height: var(--input-min-height);
|
||||||
padding: 0.95rem 1rem;
|
padding: 1rem 1.1rem;
|
||||||
border: 1px solid var(--color-border);
|
border: 1px solid var(--input-border-soft);
|
||||||
border-radius: 1rem;
|
border-radius: 1.125rem;
|
||||||
background: var(--color-surface);
|
background: var(--butter-light);
|
||||||
color: var(--color-text);
|
color: var(--color-text);
|
||||||
|
box-shadow: 0 1px 2px rgba(102, 52, 13, 0.04);
|
||||||
|
transition: border-color 0.2s ease, box-shadow 0.2s ease, background-color 0.2s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
textarea {
|
textarea {
|
||||||
@ -220,6 +259,30 @@ textarea {
|
|||||||
resize: vertical;
|
resize: vertical;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
input[type="text"]:hover,
|
||||||
|
input[type="date"]:hover,
|
||||||
|
input[type="time"]:hover,
|
||||||
|
input[type="number"]:hover,
|
||||||
|
textarea:hover {
|
||||||
|
border-color: rgba(102, 52, 13, 0.28);
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="text"]:focus,
|
||||||
|
input[type="date"]:focus,
|
||||||
|
input[type="time"]:focus,
|
||||||
|
input[type="number"]:focus,
|
||||||
|
textarea:focus {
|
||||||
|
border-color: var(--input-border-focus);
|
||||||
|
box-shadow: var(--input-shadow-focus);
|
||||||
|
background: var(--butter-light);
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.field-invalid {
|
||||||
|
border-color: var(--tomato) !important;
|
||||||
|
box-shadow: 0 0 0 2px rgba(212, 75, 36, 0.14);
|
||||||
|
}
|
||||||
|
|
||||||
.field-row {
|
.field-row {
|
||||||
display: grid;
|
display: grid;
|
||||||
gap: var(--space-4);
|
gap: var(--space-4);
|
||||||
@ -238,8 +301,8 @@ textarea {
|
|||||||
padding: 1rem 1rem 1rem 1.05rem;
|
padding: 1rem 1rem 1rem 1.05rem;
|
||||||
border: 1px solid var(--color-border);
|
border: 1px solid var(--color-border);
|
||||||
border-radius: 1rem;
|
border-radius: 1rem;
|
||||||
background: var(--color-surface);
|
background: var(--butter-light);
|
||||||
transition: border-color 0.2s ease, box-shadow 0.2s ease, transform 0.2s ease;
|
transition: border-color 0.2s ease, box-shadow 0.2s ease, transform 0.2s ease, background-color 0.2s ease, color 0.2s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.option-card small {
|
.option-card small {
|
||||||
@ -247,6 +310,7 @@ textarea {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.option-card:hover {
|
.option-card:hover {
|
||||||
|
background: var(--olive-light);
|
||||||
transform: translateY(-1px);
|
transform: translateY(-1px);
|
||||||
box-shadow: var(--shadow-soft);
|
box-shadow: var(--shadow-soft);
|
||||||
}
|
}
|
||||||
@ -259,7 +323,18 @@ textarea {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.option-card:has(input:checked) {
|
.option-card:has(input:checked) {
|
||||||
border: 2px solid var(--color-border-strong);
|
border: 1px solid var(--color-primary);
|
||||||
|
background: var(--color-primary);
|
||||||
|
color: var(--white);
|
||||||
|
}
|
||||||
|
|
||||||
|
.option-card:has(input:checked) small {
|
||||||
|
color: rgba(247, 246, 230, 0.88);
|
||||||
|
}
|
||||||
|
|
||||||
|
.option-card--invalid {
|
||||||
|
border-color: var(--tomato) !important;
|
||||||
|
box-shadow: 0 0 0 2px rgba(212, 75, 36, 0.14);
|
||||||
}
|
}
|
||||||
|
|
||||||
.counter {
|
.counter {
|
||||||
@ -281,15 +356,44 @@ textarea {
|
|||||||
.counter-button {
|
.counter-button {
|
||||||
width: var(--control-min-height);
|
width: var(--control-min-height);
|
||||||
height: var(--control-min-height);
|
height: var(--control-min-height);
|
||||||
border: 1px solid var(--color-border);
|
border: 1px solid var(--color-primary);
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
background: var(--color-surface);
|
background: var(--color-primary);
|
||||||
|
color: var(--white);
|
||||||
font-size: 1.5rem;
|
font-size: 1.5rem;
|
||||||
|
line-height: 1;
|
||||||
|
box-shadow: 0 6px 16px rgba(107, 107, 5, 0.18);
|
||||||
|
transition: background-color 0.2s ease, transform 0.2s ease, box-shadow 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.counter-button:hover {
|
||||||
|
background: var(--color-primary-hover);
|
||||||
|
transform: translateY(-1px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.counter-button:focus-visible {
|
||||||
|
outline: 3px solid rgba(107, 107, 5, 0.22);
|
||||||
|
outline-offset: 3px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.review-card {
|
.review-card {
|
||||||
padding: var(--space-5);
|
display: grid;
|
||||||
border-radius: var(--radius-lg);
|
gap: var(--space-4);
|
||||||
|
padding: 0;
|
||||||
|
border: 0;
|
||||||
|
border-radius: 0;
|
||||||
|
background: transparent;
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.review-card--success {
|
||||||
|
display: grid;
|
||||||
|
gap: var(--space-5);
|
||||||
|
padding: var(--space-3) 0 0;
|
||||||
|
border: 0;
|
||||||
|
border-radius: 0;
|
||||||
|
background: transparent;
|
||||||
|
box-shadow: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.review-list {
|
.review-list {
|
||||||
@ -300,14 +404,29 @@ textarea {
|
|||||||
|
|
||||||
.review-item {
|
.review-item {
|
||||||
display: grid;
|
display: grid;
|
||||||
gap: var(--space-1);
|
gap: var(--space-2);
|
||||||
padding-bottom: var(--space-4);
|
padding: 1rem 1.1rem;
|
||||||
border-bottom: 1px solid var(--color-divider);
|
border: 1px solid var(--input-border-soft);
|
||||||
|
border-radius: 1.125rem;
|
||||||
|
background: var(--butter-light);
|
||||||
|
box-shadow: 0 1px 2px rgba(102, 52, 13, 0.04);
|
||||||
|
cursor: pointer;
|
||||||
|
transition: border-color 0.2s ease, box-shadow 0.2s ease, transform 0.2s ease, background-color 0.2s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.review-item:last-child {
|
.review-item:last-child {
|
||||||
border-bottom: 0;
|
border-bottom: 1px solid var(--input-border-soft);
|
||||||
padding-bottom: 0;
|
}
|
||||||
|
|
||||||
|
.review-item:hover {
|
||||||
|
border-color: rgba(102, 52, 13, 0.28);
|
||||||
|
background: rgba(247, 246, 230, 0.92);
|
||||||
|
transform: translateY(-1px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.review-item:focus-visible {
|
||||||
|
outline: 3px solid rgba(107, 107, 5, 0.2);
|
||||||
|
outline-offset: 3px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.review-item dt {
|
.review-item dt {
|
||||||
@ -320,16 +439,29 @@ textarea {
|
|||||||
color: var(--color-text-secondary);
|
color: var(--color-text-secondary);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.submission-success-actions {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
.flow-footer {
|
.flow-footer {
|
||||||
position: sticky;
|
position: sticky;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
z-index: 5;
|
z-index: 5;
|
||||||
margin-top: auto;
|
margin-top: auto;
|
||||||
background: rgba(247, 247, 242, 0.96);
|
background: var(--color-bg);
|
||||||
backdrop-filter: blur(8px);
|
backdrop-filter: none;
|
||||||
|
padding-top: var(--space-4);
|
||||||
padding-bottom: env(safe-area-inset-bottom);
|
padding-bottom: env(safe-area-inset-bottom);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.progress-wrap {
|
||||||
|
position: relative;
|
||||||
|
width: min(100%, var(--content-width));
|
||||||
|
margin: 0 auto;
|
||||||
|
padding-top: 4.35rem;
|
||||||
|
}
|
||||||
|
|
||||||
.progress {
|
.progress {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 0.375rem;
|
height: 0.375rem;
|
||||||
@ -340,15 +472,50 @@ textarea {
|
|||||||
display: block;
|
display: block;
|
||||||
width: 0;
|
width: 0;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
background: var(--color-primary);
|
background: var(--tomato);
|
||||||
transition: width 0.25s ease;
|
transition: width 0.25s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.progress-marker {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
display: grid;
|
||||||
|
justify-items: center;
|
||||||
|
gap: 0.2rem;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress-marker::after {
|
||||||
|
content: "";
|
||||||
|
width: 0.125rem;
|
||||||
|
height: 1rem;
|
||||||
|
background: var(--tomato);
|
||||||
|
border-radius: 999px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress-marker__circle {
|
||||||
|
width: 2.9rem;
|
||||||
|
height: 2.9rem;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: var(--tomato);
|
||||||
|
color: var(--butter-light);
|
||||||
|
font-size: 1.35rem;
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: 1;
|
||||||
|
box-shadow: 0 10px 24px rgba(212, 75, 36, 0.18);
|
||||||
|
}
|
||||||
|
|
||||||
.flow-actions {
|
.flow-actions {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
gap: var(--space-4);
|
gap: var(--space-4);
|
||||||
|
width: min(100%, var(--content-width));
|
||||||
|
margin: 0 auto;
|
||||||
padding: var(--space-4) 0;
|
padding: var(--space-4) 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -455,8 +622,10 @@ textarea:focus-visible {
|
|||||||
|
|
||||||
@media (min-width: 768px) {
|
@media (min-width: 768px) {
|
||||||
.step-layout--intro {
|
.step-layout--intro {
|
||||||
grid-template-columns: 1.25fr 0.8fr;
|
width: min(100%, 56rem);
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
gap: var(--space-8);
|
||||||
}
|
}
|
||||||
|
|
||||||
.field-row {
|
.field-row {
|
||||||
@ -470,4 +639,4 @@ textarea:focus-visible {
|
|||||||
.option-grid--4 {
|
.option-grid--4 {
|
||||||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -43,16 +43,19 @@
|
|||||||
<h1 id="intro-title">Hey <span id="username">{{username}}</span>, was hast du vor?</h1>
|
<h1 id="intro-title">Hey <span id="username">{{username}}</span>, was hast du vor?</h1>
|
||||||
<p class="step-text">
|
<p class="step-text">
|
||||||
Erzähl uns von deiner Idee – vom Essen bis zur Stimmung. Ob Dinner, Brunch
|
Erzähl uns von deiner Idee – vom Essen bis zur Stimmung. Ob Dinner, Brunch
|
||||||
oder etwas ganz Eigenes – wir helfen dir dabei, dein Event Schritt für Schritt aufzubauen.
|
oder etwas ganz Eigenes – wir helfen dir dabei, dein Event in sieben Schritten aufzubauen.
|
||||||
</p>
|
</p>
|
||||||
<button type="button" class="button button--primary button--intro" data-start-flow>
|
<button type="button" class="button button--primary button--intro" data-start-flow>
|
||||||
Los geht’s!
|
Los geht’s!
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<aside class="intro-card" aria-label="Hinweis zur Event-Erstellung">
|
<aside class="intro-card intro-card--image" aria-label="Stimmungsbild zur Event-Erstellung">
|
||||||
<div class="intro-card-emoji" aria-hidden="true">🍽️</div>
|
<img
|
||||||
<p>Aus einer Idee wird Schritt für Schritt dein Event.</p>
|
class="intro-image"
|
||||||
|
src="assets/eventcreate_foodtable.jpg"
|
||||||
|
alt="Ein gedeckter Tisch mit gemeinsamem Essen"
|
||||||
|
/>
|
||||||
</aside>
|
</aside>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
@ -69,11 +72,6 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="step-fields">
|
<div class="step-fields">
|
||||||
<div class="form-field">
|
|
||||||
<label for="eventTitle">Wie soll dein Event heißen?</label>
|
|
||||||
<input type="text" id="eventTitle" name="eventTitle" required />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<fieldset class="form-field">
|
<fieldset class="form-field">
|
||||||
<legend>Art des Essens / Eventtyp</legend>
|
<legend>Art des Essens / Eventtyp</legend>
|
||||||
|
|
||||||
@ -99,47 +97,7 @@
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section class="step" data-step="2" aria-labelledby="step2-title">
|
|
||||||
<div class="step-layout">
|
|
||||||
<div class="step-copy">
|
|
||||||
<p class="step-kicker">Schritt 2</p>
|
|
||||||
<h2 id="step2-title">Was kommt auf den Tisch?</h2>
|
|
||||||
<p class="step-text">
|
|
||||||
Mach uns neugierig. Was kochst du – und wie fühlt sich dein Abend an?
|
|
||||||
Hier entsteht die Geschichte, auf die sich deine Gäste freuen.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="step-fields">
|
|
||||||
<div class="form-field">
|
|
||||||
<label for="menuDescription">Was ist das Menü?</label>
|
|
||||||
<textarea id="menuDescription" name="menuDescription" rows="5" required></textarea>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-field">
|
|
||||||
<label for="eventDescription">Beschreibung des Event-Abends</label>
|
|
||||||
<textarea id="eventDescription" name="eventDescription" rows="6" required></textarea>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section class="step" data-step="3" aria-labelledby="step3-title">
|
|
||||||
<div class="step-layout">
|
|
||||||
<div class="step-copy">
|
|
||||||
<p class="step-kicker">Schritt 3</p>
|
|
||||||
<h2 id="step3-title">Wen lädst du ein?</h2>
|
|
||||||
<p class="step-text">
|
|
||||||
Wie viele Gäste passen zu deinem Event? Und gibt es etwas, das du bei
|
|
||||||
Ernährung oder Unverträglichkeiten beachten möchtest?
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="step-fields">
|
|
||||||
<fieldset class="form-field">
|
<fieldset class="form-field">
|
||||||
<legend>Maximale Personenanzahl</legend>
|
<legend>Maximale Personenanzahl</legend>
|
||||||
|
|
||||||
@ -171,7 +129,21 @@
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="step" data-step="2" aria-labelledby="step2-title">
|
||||||
|
<div class="step-layout">
|
||||||
|
<div class="step-copy">
|
||||||
|
<p class="step-kicker">Schritt 2</p>
|
||||||
|
<h2 id="step2-title">Was kommt auf den Tisch?</h2>
|
||||||
|
<p class="step-text">
|
||||||
|
Mach uns neugierig. Was gibt es zu essen? Gibt es eine bestimmte Ernährungsform oder ein Motto? Je mehr du verrätst, desto besser können sich deine Gäste auf dein Event freuen.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="step-fields">
|
||||||
<fieldset class="form-field">
|
<fieldset class="form-field">
|
||||||
<legend>Ernährungsform</legend>
|
<legend>Ernährungsform</legend>
|
||||||
|
|
||||||
@ -194,6 +166,25 @@
|
|||||||
</div>
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
||||||
|
<div class="form-field">
|
||||||
|
<label for="menuDescription">Was ist das Menü?</label>
|
||||||
|
<textarea id="menuDescription" name="menuDescription" rows="5" required></textarea>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="step" data-step="3" aria-labelledby="step3-title">
|
||||||
|
<div class="step-layout">
|
||||||
|
<div class="step-copy">
|
||||||
|
<p class="step-kicker">Schritt 3</p>
|
||||||
|
<h2 id="step3-title">Gibt es etwas zu beachten?</h2>
|
||||||
|
<p class="step-text">
|
||||||
|
Gibt es Allergien, Unverträglichkeiten oder andere Hinweise, die für dein Event wichtig sind? So wissen deine Gäste gleich, worauf sie sich einstellen können.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="step-fields">
|
||||||
<fieldset class="form-field">
|
<fieldset class="form-field">
|
||||||
<legend>Allergene / Unverträglichkeiten</legend>
|
<legend>Allergene / Unverträglichkeiten</legend>
|
||||||
<p class="field-hint">Optional – nur auswählen, wenn es für dein Event relevant ist.</p>
|
<p class="field-hint">Optional – nur auswählen, wenn es für dein Event relevant ist.</p>
|
||||||
@ -214,12 +205,12 @@
|
|||||||
<span>ohne Nüsse</span>
|
<span>ohne Nüsse</span>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-field">
|
|
||||||
<label for="allergiesOther">Weitere Unverträglichkeiten oder Hinweise (optional)</label>
|
|
||||||
<textarea id="allergiesOther" name="allergiesOther" rows="3"></textarea>
|
|
||||||
</div>
|
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
||||||
|
<div class="form-field">
|
||||||
|
<label for="allergiesOther">Weitere Unverträglichkeiten oder Hinweise (optional)</label>
|
||||||
|
<textarea id="allergiesOther" name="allergiesOther" rows="3"></textarea>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
@ -230,8 +221,7 @@
|
|||||||
<p class="step-kicker">Schritt 4</p>
|
<p class="step-kicker">Schritt 4</p>
|
||||||
<h2 id="step4-title">Wann findet dein Event statt?</h2>
|
<h2 id="step4-title">Wann findet dein Event statt?</h2>
|
||||||
<p class="step-text">
|
<p class="step-text">
|
||||||
Wähle Datum und Uhrzeit – und sag uns, wo dein Event stattfindet.
|
Wähle Datum und Uhrzeit für dein Event. So können deine Gäste direkt einschätzen, ob der Termin für sie passt.
|
||||||
Keine Sorge: Die genaue Adresse sehen Gäste erst nach der Buchung.
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -247,7 +237,21 @@
|
|||||||
<input type="time" id="eventTime" name="eventTime" required />
|
<input type="time" id="eventTime" name="eventTime" required />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="step" data-step="5" aria-labelledby="step5-title">
|
||||||
|
<div class="step-layout">
|
||||||
|
<div class="step-copy">
|
||||||
|
<p class="step-kicker">Schritt 5</p>
|
||||||
|
<h2 id="step5-title">Wo findet dein Event statt?</h2>
|
||||||
|
<p class="step-text">
|
||||||
|
Sag uns, wo dein Event stattfindet. Keine Sorge: Die genaue Adresse sehen Gäste erst nach der Buchung.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="step-fields">
|
||||||
<div class="form-field">
|
<div class="form-field">
|
||||||
<label for="eventAddress">Adresse</label>
|
<label for="eventAddress">Adresse</label>
|
||||||
<input type="text" id="eventAddress" name="eventAddress" autocomplete="street-address" required />
|
<input type="text" id="eventAddress" name="eventAddress" autocomplete="street-address" required />
|
||||||
@ -261,81 +265,111 @@
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section class="step" data-step="5" aria-labelledby="step5-title">
|
<section class="step" data-step="6" aria-labelledby="step6-title">
|
||||||
<div class="step-layout">
|
<div class="step-layout">
|
||||||
<div class="step-copy">
|
<div class="step-copy">
|
||||||
<p class="step-kicker">Schritt 5</p>
|
<p class="step-kicker">Schritt 6</p>
|
||||||
<h2 id="step5-title">Alles bereit für deine Gäste?</h2>
|
<h2 id="step6-title">Gib deinem Event den letzten Schliff.</h2>
|
||||||
<p class="step-text">
|
<p class="step-text">
|
||||||
Schau dir dein Event nochmal in Ruhe an. Passt alles?
|
Jetzt bekommt dein Event seinen Namen und die Atmosphäre, die Lust aufs Dabeisein macht.
|
||||||
Dann kannst du es jetzt veröffentlichen und Gäste einladen.
|
Ein klarer Titel (z.B. "Italienische Tavolata") und ein guter Beschreibungstext (Ablauf etc.) machen den Unterschied.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="step-fields">
|
||||||
|
<div class="form-field">
|
||||||
|
<label for="eventTitle">Wie soll dein Event heißen?</label>
|
||||||
|
<input type="text" id="eventTitle" name="eventTitle" required />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-field">
|
||||||
|
<label for="eventDescription">Beschreibung des Event-Abends</label>
|
||||||
|
<textarea id="eventDescription" name="eventDescription" rows="6" required></textarea>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="step" data-step="7" aria-labelledby="step7-title">
|
||||||
|
<div class="step-layout">
|
||||||
|
<div class="step-copy">
|
||||||
|
<p class="step-kicker">Schritt 7</p>
|
||||||
|
<h2 id="step7-title">Dein Event auf einen Blick.</h2>
|
||||||
|
<p class="step-text">
|
||||||
|
Schau dir alle Details nochmal in Ruhe an. Wenn alles passt,
|
||||||
|
kannst du dein Event jetzt veröffentlichen und Gäste einladen.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="review-card" aria-live="polite">
|
<div class="review-card" aria-live="polite">
|
||||||
<dl class="review-list">
|
<dl class="review-list">
|
||||||
<div class="review-item">
|
<div class="review-item" data-edit-step="1" data-edit-field="eventType" role="button" tabindex="0" aria-label="Eventtyp bearbeiten">
|
||||||
<dt>Eventtitel</dt>
|
|
||||||
<dd data-review="eventTitle">–</dd>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="review-item">
|
|
||||||
<dt>Eventtyp</dt>
|
<dt>Eventtyp</dt>
|
||||||
<dd data-review="eventType">–</dd>
|
<dd data-review="eventType">–</dd>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="review-item">
|
<div class="review-item" data-edit-step="1" data-edit-field="maxGuests" role="button" tabindex="0" aria-label="Maximale Personenanzahl bearbeiten">
|
||||||
<dt>Menü</dt>
|
|
||||||
<dd data-review="menuDescription">–</dd>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="review-item">
|
|
||||||
<dt>Event-Abend</dt>
|
|
||||||
<dd data-review="eventDescription">–</dd>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="review-item">
|
|
||||||
<dt>Maximale Personenanzahl</dt>
|
<dt>Maximale Personenanzahl</dt>
|
||||||
<dd data-review="maxGuests">–</dd>
|
<dd data-review="maxGuests">–</dd>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="review-item">
|
<div class="review-item" data-edit-step="2" data-edit-field="dietType" role="button" tabindex="0" aria-label="Ernährungsform bearbeiten">
|
||||||
<dt>Ernährungsform</dt>
|
<dt>Ernährungsform</dt>
|
||||||
<dd data-review="dietType">–</dd>
|
<dd data-review="dietType">–</dd>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="review-item">
|
<div class="review-item" data-edit-step="2" data-edit-field="menuDescription" role="button" tabindex="0" aria-label="Menü bearbeiten">
|
||||||
|
<dt>Menü</dt>
|
||||||
|
<dd data-review="menuDescription">–</dd>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="review-item" data-edit-step="3" data-edit-field="allergiesOther" role="button" tabindex="0" aria-label="Allergene und Unverträglichkeiten bearbeiten">
|
||||||
<dt>Allergene / Unverträglichkeiten</dt>
|
<dt>Allergene / Unverträglichkeiten</dt>
|
||||||
<dd data-review="allergies">Keine Angabe</dd>
|
<dd data-review="allergies">Keine Angabe</dd>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="review-item">
|
<div class="review-item" data-edit-step="4" data-edit-field="eventDate" role="button" tabindex="0" aria-label="Datum bearbeiten">
|
||||||
<dt>Datum</dt>
|
<dt>Datum</dt>
|
||||||
<dd data-review="eventDate">–</dd>
|
<dd data-review="eventDate">–</dd>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="review-item">
|
<div class="review-item" data-edit-step="4" data-edit-field="eventTime" role="button" tabindex="0" aria-label="Uhrzeit bearbeiten">
|
||||||
<dt>Uhrzeit</dt>
|
<dt>Uhrzeit</dt>
|
||||||
<dd data-review="eventTime">–</dd>
|
<dd data-review="eventTime">–</dd>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="review-item">
|
<div class="review-item" data-edit-step="5" data-edit-field="eventAddress" role="button" tabindex="0" aria-label="Adresse bearbeiten">
|
||||||
<dt>Adresse</dt>
|
<dt>Adresse</dt>
|
||||||
<dd data-review="eventAddress">–</dd>
|
<dd data-review="eventAddress">–</dd>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="review-item">
|
<div class="review-item" data-edit-step="5" data-edit-field="eventCity" role="button" tabindex="0" aria-label="Ort bearbeiten">
|
||||||
<dt>Ort</dt>
|
<dt>Ort</dt>
|
||||||
<dd data-review="eventCity">–</dd>
|
<dd data-review="eventCity">–</dd>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="review-item" data-edit-step="6" data-edit-field="eventTitle" role="button" tabindex="0" aria-label="Eventtitel bearbeiten">
|
||||||
|
<dt>Eventtitel</dt>
|
||||||
|
<dd data-review="eventTitle">–</dd>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="review-item" data-edit-step="6" data-edit-field="eventDescription" role="button" tabindex="0" aria-label="Event-Abend bearbeiten">
|
||||||
|
<dt>Event-Abend</dt>
|
||||||
|
<dd data-review="eventDescription">–</dd>
|
||||||
|
</div>
|
||||||
</dl>
|
</dl>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<div class="flow-footer" id="flowFooter" hidden>
|
<div class="flow-footer" id="flowFooter" hidden>
|
||||||
<div class="progress" aria-hidden="true">
|
<div class="progress-wrap" aria-hidden="true">
|
||||||
<span id="progressBar" class="progress-bar"></span>
|
<div class="progress-marker" id="progressMarker">
|
||||||
|
<span class="progress-marker__circle" id="progressMarkerLabel">1</span>
|
||||||
|
</div>
|
||||||
|
<div class="progress">
|
||||||
|
<span id="progressBar" class="progress-bar"></span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flow-actions">
|
<div class="flow-actions">
|
||||||
@ -347,6 +381,31 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<section
|
||||||
|
id="submissionSuccess"
|
||||||
|
class="submission-success"
|
||||||
|
aria-labelledby="success-title"
|
||||||
|
aria-live="polite"
|
||||||
|
hidden
|
||||||
|
>
|
||||||
|
<div class="step-layout">
|
||||||
|
<div class="step-copy">
|
||||||
|
<p class="step-kicker">Event erstellt</p>
|
||||||
|
<h2 id="success-title">Dein Event ist ready.</h2>
|
||||||
|
<p class="step-text">
|
||||||
|
Sieht gut aus: Deine Idee ist jetzt live und bereit für Gäste.
|
||||||
|
Im Profil kannst du dein Event anschauen, verwalten oder direkt das nächste planen.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="review-card review-card--success">
|
||||||
|
<div class="submission-success-actions">
|
||||||
|
<a class="button button--primary" href="event_overview.html">Weiter zu deinem Profil</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
</form>
|
</form>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
@ -356,4 +415,4 @@
|
|||||||
|
|
||||||
<script src="js/event_create.js"></script>
|
<script src="js/event_create.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@ -8,9 +8,13 @@ const steps = Array.from(document.querySelectorAll(".step"));
|
|||||||
const backButton = document.getElementById("backButton");
|
const backButton = document.getElementById("backButton");
|
||||||
const nextButton = document.getElementById("nextButton");
|
const nextButton = document.getElementById("nextButton");
|
||||||
const progressBar = document.getElementById("progressBar");
|
const progressBar = document.getElementById("progressBar");
|
||||||
|
const progressMarker = document.getElementById("progressMarker");
|
||||||
|
const progressMarkerLabel = document.getElementById("progressMarkerLabel");
|
||||||
const errorMessage = document.getElementById("errorMessage");
|
const errorMessage = document.getElementById("errorMessage");
|
||||||
const usernameElement = document.getElementById("username");
|
const usernameElement = document.getElementById("username");
|
||||||
const flowFooter = document.getElementById("flowFooter");
|
const flowFooter = document.getElementById("flowFooter");
|
||||||
|
const submissionSuccess = document.getElementById("submissionSuccess");
|
||||||
|
const EVENTS_STORAGE_KEY = "socialCookingEvents";
|
||||||
|
|
||||||
// =============================
|
// =============================
|
||||||
// STATE: aktueller Schritt im Flow
|
// STATE: aktueller Schritt im Flow
|
||||||
@ -27,7 +31,9 @@ const nextLabels = {
|
|||||||
2: "Weiter",
|
2: "Weiter",
|
||||||
3: "Weiter",
|
3: "Weiter",
|
||||||
4: "Weiter",
|
4: "Weiter",
|
||||||
5: "Event veröffentlichen"
|
5: "Weiter",
|
||||||
|
6: "Weiter",
|
||||||
|
7: "Event veröffentlichen"
|
||||||
};
|
};
|
||||||
|
|
||||||
// Demo-Wert: Später könnte der Name z. B. aus einem User-Profil kommen
|
// Demo-Wert: Später könnte der Name z. B. aus einem User-Profil kommen
|
||||||
@ -57,6 +63,39 @@ function setErrorMessage(message = "") {
|
|||||||
errorMessage.textContent = message;
|
errorMessage.textContent = message;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Entfernt alle Fehlermarkierungen innerhalb eines Schritts.
|
||||||
|
*/
|
||||||
|
function clearStepInvalidState(stepIndex) {
|
||||||
|
if (!steps[stepIndex]) return;
|
||||||
|
|
||||||
|
steps[stepIndex]
|
||||||
|
.querySelectorAll(".field-invalid, .option-card--invalid")
|
||||||
|
.forEach(element => {
|
||||||
|
element.classList.remove("field-invalid", "option-card--invalid");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Markiert ein einzelnes Feld visuell als ungültig.
|
||||||
|
*/
|
||||||
|
function markFieldInvalid(field) {
|
||||||
|
field.classList.add("field-invalid");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Markiert eine ganze Radio-Gruppe visuell als ungültig.
|
||||||
|
*/
|
||||||
|
function markRadioGroupInvalid(group) {
|
||||||
|
group.forEach(field => {
|
||||||
|
const card = field.closest(".option-card");
|
||||||
|
|
||||||
|
if (card) {
|
||||||
|
card.classList.add("option-card--invalid");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// =============================
|
// =============================
|
||||||
// STEP 2: Schritt anzeigen & Oberfläche aktualisieren
|
// STEP 2: Schritt anzeigen & Oberfläche aktualisieren
|
||||||
@ -71,6 +110,8 @@ function setErrorMessage(message = "") {
|
|||||||
*/
|
*/
|
||||||
function showStep(index) {
|
function showStep(index) {
|
||||||
currentStep = index;
|
currentStep = index;
|
||||||
|
submissionSuccess.hidden = true;
|
||||||
|
clearStepInvalidState(index);
|
||||||
|
|
||||||
// Nur der aktuelle Schritt soll sichtbar sein
|
// Nur der aktuelle Schritt soll sichtbar sein
|
||||||
steps.forEach((step, stepIndex) => {
|
steps.forEach((step, stepIndex) => {
|
||||||
@ -115,12 +156,21 @@ function updateFlowVisibility(stepIndex) {
|
|||||||
*/
|
*/
|
||||||
function updateProgressBar(stepIndex, totalStepIndex) {
|
function updateProgressBar(stepIndex, totalStepIndex) {
|
||||||
let progress = 0;
|
let progress = 0;
|
||||||
|
let markerPosition = 0;
|
||||||
|
let markerStep = 1;
|
||||||
|
let markerTransform = "translateX(-50%)";
|
||||||
|
|
||||||
if (stepIndex > 0) {
|
if (stepIndex > 0) {
|
||||||
progress = ((stepIndex - 1) / (totalStepIndex - 1)) * 100;
|
progress = ((stepIndex - 1) / (totalStepIndex - 1)) * 100;
|
||||||
|
markerPosition = ((stepIndex - 1) / (totalStepIndex - 1)) * 100;
|
||||||
|
markerStep = stepIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
progressBar.style.width = `${progress}%`;
|
progressBar.style.width = `${progress}%`;
|
||||||
|
progressMarker.style.left = `${markerPosition}%`;
|
||||||
|
progressMarker.style.transform = markerTransform;
|
||||||
|
progressMarker.hidden = stepIndex === 0;
|
||||||
|
progressMarkerLabel.textContent = String(markerStep);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -225,7 +275,18 @@ function buildAllergiesReviewValue() {
|
|||||||
* und schreibt sie gesammelt in die Review-Ansicht.
|
* und schreibt sie gesammelt in die Review-Ansicht.
|
||||||
*/
|
*/
|
||||||
function updateReview() {
|
function updateReview() {
|
||||||
const reviewValues = {
|
const reviewValues = getReviewValues();
|
||||||
|
|
||||||
|
Object.entries(reviewValues).forEach(([key, value]) => {
|
||||||
|
updateReviewField(key, value);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Liest alle wichtigen Formularwerte gesammelt aus.
|
||||||
|
*/
|
||||||
|
function getReviewValues() {
|
||||||
|
return {
|
||||||
eventTitle: getFieldValue("eventTitle"),
|
eventTitle: getFieldValue("eventTitle"),
|
||||||
eventType: getFieldValue("eventType"),
|
eventType: getFieldValue("eventType"),
|
||||||
menuDescription: getFieldValue("menuDescription"),
|
menuDescription: getFieldValue("menuDescription"),
|
||||||
@ -238,12 +299,137 @@ function updateReview() {
|
|||||||
eventAddress: getFieldValue("eventAddress"),
|
eventAddress: getFieldValue("eventAddress"),
|
||||||
eventCity: getFieldValue("eventCity")
|
eventCity: getFieldValue("eventCity")
|
||||||
};
|
};
|
||||||
|
|
||||||
Object.entries(reviewValues).forEach(([key, value]) => {
|
|
||||||
updateReviewField(key, value);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Liest lokal gespeicherte Events robust aus dem Browser-Storage.
|
||||||
|
*/
|
||||||
|
function getStoredEvents() {
|
||||||
|
try {
|
||||||
|
const stored = localStorage.getItem(EVENTS_STORAGE_KEY);
|
||||||
|
return stored ? JSON.parse(stored) : [];
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Lokale Events konnten nicht gelesen werden:", error);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Speichert die komplette Eventliste zurück in den Browser-Storage.
|
||||||
|
*/
|
||||||
|
function setStoredEvents(events) {
|
||||||
|
localStorage.setItem(EVENTS_STORAGE_KEY, JSON.stringify(events));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formatiert ein ISO-Datum in das bestehende Eventformat der Demo-Daten.
|
||||||
|
*/
|
||||||
|
function formatDateForStorage(value) {
|
||||||
|
if (!value) return "";
|
||||||
|
|
||||||
|
const date = new Date(value);
|
||||||
|
if (Number.isNaN(date.getTime())) return value;
|
||||||
|
|
||||||
|
const monthMap = {
|
||||||
|
0: "JAN",
|
||||||
|
1: "FEB",
|
||||||
|
2: "MRZ",
|
||||||
|
3: "APR",
|
||||||
|
4: "MAI",
|
||||||
|
5: "JUN",
|
||||||
|
6: "JUL",
|
||||||
|
7: "AUG",
|
||||||
|
8: "SEP",
|
||||||
|
9: "OKT",
|
||||||
|
10: "NOV",
|
||||||
|
11: "DEZ"
|
||||||
|
};
|
||||||
|
|
||||||
|
const day = String(date.getDate()).padStart(2, "0");
|
||||||
|
const month = monthMap[date.getMonth()];
|
||||||
|
const year = date.getFullYear();
|
||||||
|
|
||||||
|
return `${day}. ${month}. ${year}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formatiert die Zeit in das bestehende Eventformat der Demo-Daten.
|
||||||
|
*/
|
||||||
|
function formatTimeForStorage(value) {
|
||||||
|
return value ? `${value} UHR` : "";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Zerlegt das Menü-Textarea in saubere Listenpunkte.
|
||||||
|
*/
|
||||||
|
function buildMenuItems(value) {
|
||||||
|
return value
|
||||||
|
.split("\n")
|
||||||
|
.map(item => item.replace(/^[•-]\s*/, "").trim())
|
||||||
|
.filter(Boolean);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Leitet den gewählten Eventtyp in die Kategorien der Übersicht über.
|
||||||
|
*/
|
||||||
|
function mapEventTypeToCategory(value) {
|
||||||
|
const categoryMap = {
|
||||||
|
Brunch: "BRUNCH",
|
||||||
|
Lunch: "LUNCH",
|
||||||
|
Dinner: "DINNER",
|
||||||
|
"Kaffee + Kuchen": "COFFEE"
|
||||||
|
};
|
||||||
|
|
||||||
|
return categoryMap[value] || value.toUpperCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Baut aus den Formulardaten ein lokal speicherbares Event-Objekt.
|
||||||
|
*/
|
||||||
|
function buildStoredEvent() {
|
||||||
|
const eventType = getFieldValue("eventType");
|
||||||
|
const dietType = getFieldValue("dietType");
|
||||||
|
const menuDescription = form.elements.menuDescription.value.trim();
|
||||||
|
const eventDescription = form.elements.eventDescription.value.trim();
|
||||||
|
const eventDate = form.elements.eventDate.value;
|
||||||
|
const eventTime = form.elements.eventTime.value;
|
||||||
|
const eventCity = form.elements.eventCity.value.trim();
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: Date.now(),
|
||||||
|
title: form.elements.eventTitle.value.trim(),
|
||||||
|
location: eventCity,
|
||||||
|
address: form.elements.eventAddress.value.trim(),
|
||||||
|
date: formatDateForStorage(eventDate),
|
||||||
|
time: formatTimeForStorage(eventTime),
|
||||||
|
category: mapEventTypeToCategory(eventType),
|
||||||
|
diet: dietType,
|
||||||
|
spots: Number(form.elements.maxGuests.value),
|
||||||
|
host: {
|
||||||
|
name: usernameElement.textContent.trim() || "Host",
|
||||||
|
initial: (usernameElement.textContent.trim().charAt(0) || "H").toUpperCase()
|
||||||
|
},
|
||||||
|
hostMessage: [eventDescription],
|
||||||
|
menu: buildMenuItems(menuDescription),
|
||||||
|
specifications: getCheckboxValues("allergies") === "Keine Angabe"
|
||||||
|
? []
|
||||||
|
: getCheckboxValues("allergies").split(", ").filter(Boolean),
|
||||||
|
allergiesNote: form.elements.allergiesOther.value.trim(),
|
||||||
|
participants: [usernameElement.textContent.trim() || "Host"],
|
||||||
|
gallery: [],
|
||||||
|
createdAt: new Date().toISOString(),
|
||||||
|
source: "local"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Speichert das aktuell erstellte Event lokal im Browser.
|
||||||
|
*/
|
||||||
|
function saveCurrentEvent() {
|
||||||
|
const storedEvents = getStoredEvents();
|
||||||
|
const nextEvents = [buildStoredEvent(), ...storedEvents];
|
||||||
|
setStoredEvents(nextEvents);
|
||||||
|
}
|
||||||
|
|
||||||
// =============================
|
// =============================
|
||||||
// STEP 5: Validierung
|
// STEP 5: Validierung
|
||||||
@ -263,6 +449,7 @@ function validateCurrentStep() {
|
|||||||
if (currentStep === 0 || currentStep === lastStep) return true;
|
if (currentStep === 0 || currentStep === lastStep) return true;
|
||||||
|
|
||||||
const fields = getStepFields(currentStep);
|
const fields = getStepFields(currentStep);
|
||||||
|
clearStepInvalidState(currentStep);
|
||||||
|
|
||||||
// Zuerst Radio-Gruppen prüfen
|
// Zuerst Radio-Gruppen prüfen
|
||||||
const radioCheck = validateRadioGroups(fields);
|
const radioCheck = validateRadioGroups(fields);
|
||||||
@ -297,6 +484,7 @@ function validateRadioGroups(fields) {
|
|||||||
const selected = group.some(f => f.checked);
|
const selected = group.some(f => f.checked);
|
||||||
|
|
||||||
if (required && !selected) {
|
if (required && !selected) {
|
||||||
|
markRadioGroupInvalid(group);
|
||||||
return {
|
return {
|
||||||
isValid: false,
|
isValid: false,
|
||||||
message: "Bitte wähle eine Option aus."
|
message: "Bitte wähle eine Option aus."
|
||||||
@ -318,6 +506,7 @@ function validateRequiredFields(fields) {
|
|||||||
if (field.type === "radio" || field.type === "checkbox") continue;
|
if (field.type === "radio" || field.type === "checkbox") continue;
|
||||||
|
|
||||||
if (!field.checkValidity()) {
|
if (!field.checkValidity()) {
|
||||||
|
markFieldInvalid(field);
|
||||||
return {
|
return {
|
||||||
isValid: false,
|
isValid: false,
|
||||||
message: "Bitte fülle alle Pflichtfelder aus."
|
message: "Bitte fülle alle Pflichtfelder aus."
|
||||||
@ -374,11 +563,12 @@ function handleNextClick() {
|
|||||||
*/
|
*/
|
||||||
function handleFormSubmit(event) {
|
function handleFormSubmit(event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
// 1. Feedback geben
|
saveCurrentEvent();
|
||||||
alert("Dein Event wurde erfolgreich veröffentlicht!");
|
steps.forEach(step => step.classList.remove("step--active"));
|
||||||
|
flowFooter.hidden = true;
|
||||||
// 2. Weiterleiten (z. B. zur Event-Übersicht)
|
submissionSuccess.hidden = false;
|
||||||
window.location.href = "event_overview.html";
|
setErrorMessage("");
|
||||||
|
window.scrollTo({ top: 0, behavior: "smooth" });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -412,6 +602,126 @@ function updateCounterValue(input, change) {
|
|||||||
input.value = Math.max(min, currentValue + change);
|
input.value = Math.max(min, currentValue + change);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Macht aus "-"+Enter im Menüfeld eine einfache Bullet-Liste.
|
||||||
|
*/
|
||||||
|
function registerMenuBulletHandler() {
|
||||||
|
const menuField = document.getElementById("menuDescription");
|
||||||
|
|
||||||
|
if (!menuField) return;
|
||||||
|
|
||||||
|
menuField.addEventListener("keydown", event => {
|
||||||
|
if (event.key !== "Enter") return;
|
||||||
|
|
||||||
|
const { selectionStart, selectionEnd, value } = menuField;
|
||||||
|
const lineStart = value.lastIndexOf("\n", selectionStart - 1) + 1;
|
||||||
|
const lineEnd = value.indexOf("\n", selectionStart);
|
||||||
|
const currentLineEnd = lineEnd === -1 ? value.length : lineEnd;
|
||||||
|
const currentLine = value.slice(lineStart, currentLineEnd);
|
||||||
|
const trimmedLine = currentLine.trim();
|
||||||
|
|
||||||
|
if (trimmedLine !== "-" && !currentLine.startsWith("• ")) return;
|
||||||
|
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
const isEmptyBullet = currentLine.trim() === "•";
|
||||||
|
|
||||||
|
if (isEmptyBullet) {
|
||||||
|
const beforeLine = value.slice(0, lineStart);
|
||||||
|
const afterLine = value.slice(currentLineEnd);
|
||||||
|
const separator = beforeLine.endsWith("\n") || afterLine.startsWith("\n") ? "" : "\n";
|
||||||
|
const nextValue = `${beforeLine}${separator}${afterLine}`.replace(/\n{3,}/g, "\n\n");
|
||||||
|
|
||||||
|
menuField.value = nextValue;
|
||||||
|
const caretPosition = Math.min(lineStart, nextValue.length);
|
||||||
|
menuField.setSelectionRange(caretPosition, caretPosition);
|
||||||
|
menuField.dispatchEvent(new Event("input", { bubbles: true }));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const bulletLine = currentLine.startsWith("• ") ? currentLine : currentLine.replace("-", "•");
|
||||||
|
const updatedLine = bulletLine.startsWith("• ") ? bulletLine : `• ${trimmedLine.slice(1).trimStart()}`;
|
||||||
|
const beforeLine = value.slice(0, lineStart);
|
||||||
|
const afterLine = value.slice(currentLineEnd);
|
||||||
|
const nextValue = `${beforeLine}${updatedLine}\n• ${afterLine}`;
|
||||||
|
const caretPosition = beforeLine.length + updatedLine.length + 3;
|
||||||
|
|
||||||
|
menuField.value = nextValue;
|
||||||
|
menuField.setSelectionRange(caretPosition, caretPosition);
|
||||||
|
menuField.dispatchEvent(new Event("input", { bubbles: true }));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Springt aus der Review zurück zum passenden Schritt
|
||||||
|
* und fokussiert das gewünschte Feld für direktes Weiterbearbeiten.
|
||||||
|
*/
|
||||||
|
function registerReviewEditHandlers() {
|
||||||
|
document.querySelectorAll(".review-item[data-edit-step]").forEach(item => {
|
||||||
|
const activateEdit = () => {
|
||||||
|
const stepIndex = Number(item.dataset.editStep);
|
||||||
|
const fieldName = item.dataset.editField;
|
||||||
|
|
||||||
|
showStep(stepIndex);
|
||||||
|
focusFieldByName(fieldName);
|
||||||
|
};
|
||||||
|
|
||||||
|
item.addEventListener("click", activateEdit);
|
||||||
|
item.addEventListener("keydown", event => {
|
||||||
|
if (event.key === "Enter" || event.key === " ") {
|
||||||
|
event.preventDefault();
|
||||||
|
activateEdit();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Entfernt Fehlermarkierungen, sobald der User ein Feld korrigiert.
|
||||||
|
*/
|
||||||
|
function registerValidationFeedbackHandlers() {
|
||||||
|
form.querySelectorAll("input, textarea, select").forEach(field => {
|
||||||
|
const clearInvalidState = () => {
|
||||||
|
field.classList.remove("field-invalid");
|
||||||
|
|
||||||
|
if (field.type === "radio") {
|
||||||
|
const group = Array.from(form.querySelectorAll(`input[name="${field.name}"]`));
|
||||||
|
const hasSelection = group.some(item => item.checked);
|
||||||
|
|
||||||
|
if (hasSelection) {
|
||||||
|
group.forEach(item => {
|
||||||
|
const card = item.closest(".option-card");
|
||||||
|
|
||||||
|
if (card) {
|
||||||
|
card.classList.remove("option-card--invalid");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
field.addEventListener("input", clearInvalidState);
|
||||||
|
field.addEventListener("change", clearInvalidState);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setzt den Fokus auf ein bestimmtes Feld oder die erste Option einer Radio-Gruppe.
|
||||||
|
*/
|
||||||
|
function focusFieldByName(fieldName) {
|
||||||
|
const field = form.elements[fieldName];
|
||||||
|
|
||||||
|
if (!field) return;
|
||||||
|
|
||||||
|
const focusTarget = field instanceof RadioNodeList ? field[0] : field;
|
||||||
|
|
||||||
|
if (focusTarget && typeof focusTarget.focus === "function") {
|
||||||
|
window.setTimeout(() => {
|
||||||
|
focusTarget.focus();
|
||||||
|
}, 150);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// =============================
|
// =============================
|
||||||
// STEP 9: Alles starten
|
// STEP 9: Alles starten
|
||||||
@ -438,10 +748,14 @@ function initEventCreationFlow() {
|
|||||||
|
|
||||||
// Counter aktivieren
|
// Counter aktivieren
|
||||||
registerCounterHandlers();
|
registerCounterHandlers();
|
||||||
|
registerMenuBulletHandler();
|
||||||
|
registerValidationFeedbackHandlers();
|
||||||
|
registerReviewEditHandlers();
|
||||||
|
|
||||||
// Startzustand: Intro anzeigen
|
// Startzustand: Intro anzeigen
|
||||||
|
submissionSuccess.hidden = true;
|
||||||
showStep(0);
|
showStep(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Startpunkt des Skripts
|
// Startpunkt des Skripts
|
||||||
initEventCreationFlow();
|
initEventCreationFlow();
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
document.addEventListener('DOMContentLoaded', async () => {
|
document.addEventListener('DOMContentLoaded', async () => {
|
||||||
|
const EVENTS_STORAGE_KEY = 'socialCookingEvents';
|
||||||
// -------------------------------------------------------------
|
// -------------------------------------------------------------
|
||||||
// DOM entry point and shared asset path.
|
// DOM entry point and shared asset path.
|
||||||
// -------------------------------------------------------------
|
// -------------------------------------------------------------
|
||||||
@ -14,10 +15,21 @@ document.addEventListener('DOMContentLoaded', async () => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getStoredEvents() {
|
||||||
|
try {
|
||||||
|
const stored = localStorage.getItem(EVENTS_STORAGE_KEY);
|
||||||
|
return stored ? JSON.parse(stored) : [];
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Lokale Events konnten nicht gelesen werden.', error);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Fetch data source and resolve the matching event record.
|
// Fetch data source and resolve the matching event record.
|
||||||
try {
|
try {
|
||||||
const response = await fetch('data/events.json');
|
const response = await fetch('data/events.json');
|
||||||
const allEvents = await response.json();
|
const apiEvents = await response.json();
|
||||||
|
const allEvents = [...getStoredEvents(), ...apiEvents];
|
||||||
const event = allEvents.find(e => e.id === eventId);
|
const event = allEvents.find(e => e.id === eventId);
|
||||||
|
|
||||||
if (event) {
|
if (event) {
|
||||||
@ -257,4 +269,4 @@ document.addEventListener('DOMContentLoaded', async () => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
document.addEventListener('DOMContentLoaded', () => {
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
|
const EVENTS_STORAGE_KEY = 'socialCookingEvents';
|
||||||
// -------------------------------------------------------------
|
// -------------------------------------------------------------
|
||||||
// DOM references used throughout the page lifecycle.
|
// DOM references used throughout the page lifecycle.
|
||||||
// -------------------------------------------------------------
|
// -------------------------------------------------------------
|
||||||
@ -14,6 +15,16 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
let allEvents = [];
|
let allEvents = [];
|
||||||
let activeCategory = 'ALLE';
|
let activeCategory = 'ALLE';
|
||||||
|
|
||||||
|
function getStoredEvents() {
|
||||||
|
try {
|
||||||
|
const stored = localStorage.getItem(EVENTS_STORAGE_KEY);
|
||||||
|
return stored ? JSON.parse(stored) : [];
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Lokale Events konnten nicht gelesen werden.', error);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// -------------------------------------------------------------
|
// -------------------------------------------------------------
|
||||||
// Initial data bootstrap:
|
// Initial data bootstrap:
|
||||||
// 1) fetch JSON,
|
// 1) fetch JSON,
|
||||||
@ -24,7 +35,9 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
async function fetchEvents() {
|
async function fetchEvents() {
|
||||||
try {
|
try {
|
||||||
const response = await fetch('data/events.json');
|
const response = await fetch('data/events.json');
|
||||||
allEvents = await response.json();
|
const apiEvents = await response.json();
|
||||||
|
const localEvents = getStoredEvents();
|
||||||
|
allEvents = [...localEvents, ...apiEvents];
|
||||||
populateMetaFilters();
|
populateMetaFilters();
|
||||||
|
|
||||||
const savedCategory = sessionStorage.getItem('activeFilter') || 'ALLE';
|
const savedCategory = sessionStorage.getItem('activeFilter') || 'ALLE';
|
||||||
@ -62,6 +75,10 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
|
|
||||||
// Convert localized event date (e.g. 19. MÄR. 2026) into ISO format for date input comparison.
|
// Convert localized event date (e.g. 19. MÄR. 2026) into ISO format for date input comparison.
|
||||||
function parseEventDateToIso(dateString) {
|
function parseEventDateToIso(dateString) {
|
||||||
|
if (/^\d{4}-\d{2}-\d{2}$/.test(dateString)) {
|
||||||
|
return dateString;
|
||||||
|
}
|
||||||
|
|
||||||
const months = {
|
const months = {
|
||||||
JAN: '01',
|
JAN: '01',
|
||||||
FEB: '02',
|
FEB: '02',
|
||||||
@ -92,6 +109,11 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
|
|
||||||
// Convert short month notation into full German month label for UI display.
|
// Convert short month notation into full German month label for UI display.
|
||||||
function formatEventDate(dateString) {
|
function formatEventDate(dateString) {
|
||||||
|
if (/^\d{4}-\d{2}-\d{2}$/.test(dateString)) {
|
||||||
|
const [year, month, day] = dateString.split('-');
|
||||||
|
return `${Number(day)}. ${['Januar', 'Februar', 'März', 'April', 'Mai', 'Juni', 'Juli', 'August', 'September', 'Oktober', 'November', 'Dezember'][Number(month) - 1]} ${year}`;
|
||||||
|
}
|
||||||
|
|
||||||
const labels = {
|
const labels = {
|
||||||
JAN: 'Januar',
|
JAN: 'Januar',
|
||||||
FEB: 'Februar',
|
FEB: 'Februar',
|
||||||
@ -122,7 +144,13 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
|
|
||||||
// Normalize time label from UHR to Uhr for consistent typography.
|
// Normalize time label from UHR to Uhr for consistent typography.
|
||||||
function formatEventTime(timeString) {
|
function formatEventTime(timeString) {
|
||||||
return timeString.replace('UHR', 'Uhr').trim();
|
if (!timeString) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
return timeString.includes('UHR')
|
||||||
|
? timeString.replace('UHR', 'Uhr').trim()
|
||||||
|
: `${timeString} Uhr`;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Safely verify whether a value exists in the given select element.
|
// Safely verify whether a value exists in the given select element.
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user