feat: add Go microservice, Dockerfile, docker-compose, and API submission endpoint
This commit is contained in:
@@ -0,0 +1,112 @@
|
||||
/* ==========================================================================
|
||||
Page: Aktivitäts-Auswahl (activity.html)
|
||||
========================================================================== */
|
||||
|
||||
.page-activity .intro {
|
||||
text-align: center;
|
||||
margin-bottom: 1.2em;
|
||||
}
|
||||
|
||||
.page-activity .intro p {
|
||||
color: var(--color-text-light);
|
||||
}
|
||||
|
||||
.activity-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
@media (min-width: 600px) {
|
||||
.activity-grid {
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.activity-card {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
min-height: 115px;
|
||||
padding: 12px 8px;
|
||||
background: var(--color-white);
|
||||
border: 2px solid transparent;
|
||||
border-radius: var(--radius-md);
|
||||
box-shadow: var(--shadow-soft);
|
||||
cursor: pointer;
|
||||
touch-action: manipulation;
|
||||
transition: transform 0.2s ease, box-shadow 0.2s ease, border-color 0.2s ease, background 0.2s ease;
|
||||
}
|
||||
|
||||
.activity-card:active {
|
||||
transform: scale(0.97);
|
||||
}
|
||||
|
||||
.activity-card .icon {
|
||||
font-size: 2.2rem;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.activity-card .label {
|
||||
font-family: var(--font-heading);
|
||||
font-weight: 700;
|
||||
font-size: 0.9rem;
|
||||
text-align: center;
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
.activity-card.selected {
|
||||
border-color: var(--color-rose);
|
||||
background: linear-gradient(135deg, var(--color-rose-light) 0%, var(--color-white) 100%);
|
||||
box-shadow: var(--shadow-strong);
|
||||
}
|
||||
|
||||
.activity-card.selected::after {
|
||||
content: "✓";
|
||||
position: absolute;
|
||||
top: 8px;
|
||||
right: 10px;
|
||||
color: var(--color-rose-dark);
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.activity-card.is-disabled,
|
||||
.activity-card:disabled {
|
||||
opacity: 0.4;
|
||||
cursor: not-allowed;
|
||||
filter: grayscale(0.4);
|
||||
box-shadow: none;
|
||||
background: #f7e8ec;
|
||||
}
|
||||
|
||||
.time-notice {
|
||||
background: var(--color-white);
|
||||
border: 2px solid var(--color-peach);
|
||||
border-radius: var(--radius-md);
|
||||
padding: 12px 16px;
|
||||
margin-bottom: 16px;
|
||||
font-size: 0.95rem;
|
||||
color: var(--color-rose-dark);
|
||||
text-align: center;
|
||||
box-shadow: var(--shadow-soft);
|
||||
animation: fadeIn 0.3s ease;
|
||||
}
|
||||
|
||||
.hidden {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(-6px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0" />
|
||||
<title>Wähle unsere Aktivität – Date-Planer</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||
<link href="https://fonts.googleapis.com/css2?family=Nunito:wght@400;600;700&family=Quicksand:wght@600;700&display=swap" rel="stylesheet" />
|
||||
<link rel="stylesheet" href="style.css" />
|
||||
<link rel="stylesheet" href="activity.css" />
|
||||
</head>
|
||||
<body class="page-activity has-continue-bar">
|
||||
<main class="container">
|
||||
<a class="back-link" href="datetime.html">‹ Zurück</a>
|
||||
<div class="intro">
|
||||
<h1>Worauf hast du Lust? 🥰</h1>
|
||||
<p id="dateSummary">Wähle unsere gemeinsame Aktivität für das Date aus.</p>
|
||||
</div>
|
||||
|
||||
<div id="timeNotice" class="time-notice hidden">
|
||||
💡 An diesem Tag haben wir reichlich Zeit! Wenn du möchtest, kannst du noch eine zweite kurze Aktivität auswählen.
|
||||
</div>
|
||||
|
||||
<div class="activity-grid" id="activityGrid"></div>
|
||||
</main>
|
||||
|
||||
<div class="continue-bar">
|
||||
<div class="container">
|
||||
<button id="continueBtn" class="btn btn-primary" type="button" disabled>Weiter</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="activity.js" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,145 @@
|
||||
// Aktivitäts-Auswahl: lädt Aktivitäten aus appsettings.json, Kartenauswahl + Weiter-Button
|
||||
|
||||
(function () {
|
||||
const activityGrid = document.getElementById("activityGrid");
|
||||
const dateSummary = document.getElementById("dateSummary");
|
||||
const timeNotice = document.getElementById("timeNotice");
|
||||
const continueBtn = document.getElementById("continueBtn");
|
||||
|
||||
const selectedDate = localStorage.getItem("selectedDate");
|
||||
const selectedDateType = localStorage.getItem("selectedDateType") || "long";
|
||||
|
||||
if (!selectedDate) {
|
||||
window.location.href = "datetime.html";
|
||||
return;
|
||||
}
|
||||
|
||||
const dateObj = new Date(`${selectedDate}T00:00:00`);
|
||||
const formattedDate = dateObj.toLocaleDateString("de-DE", {
|
||||
weekday: "long",
|
||||
day: "numeric",
|
||||
month: "long",
|
||||
year: "numeric",
|
||||
});
|
||||
dateSummary.textContent = `Für unser Date am ${formattedDate}.`;
|
||||
|
||||
let availableActivities = [];
|
||||
let selectedList = [];
|
||||
|
||||
fetch("appsettings.json")
|
||||
.then((response) => response.json())
|
||||
.then((settings) => {
|
||||
const allActivities = settings.activities || [];
|
||||
if (selectedDateType === "short") {
|
||||
availableActivities = allActivities.filter((a) => a.type === "short");
|
||||
} else {
|
||||
availableActivities = allActivities;
|
||||
}
|
||||
renderActivities();
|
||||
})
|
||||
.catch(() => {
|
||||
activityGrid.textContent = "Aktivitäten konnten nicht geladen werden.";
|
||||
});
|
||||
|
||||
function renderActivities() {
|
||||
activityGrid.innerHTML = "";
|
||||
const hasShortSelected = selectedList.some((a) => a.type === "short");
|
||||
|
||||
availableActivities.forEach((activity) => {
|
||||
const card = document.createElement("button");
|
||||
card.type = "button";
|
||||
card.className = "activity-card";
|
||||
card.dataset.activity = activity.name;
|
||||
|
||||
const isSelected = selectedList.some((a) => a.name === activity.name);
|
||||
if (isSelected) {
|
||||
card.classList.add("selected");
|
||||
}
|
||||
|
||||
const isLongDisabled = hasShortSelected && activity.type === "long";
|
||||
if (isLongDisabled) {
|
||||
card.classList.add("is-disabled");
|
||||
card.disabled = true;
|
||||
}
|
||||
|
||||
const icon = document.createElement("span");
|
||||
icon.className = "icon";
|
||||
icon.setAttribute("aria-hidden", "true");
|
||||
icon.textContent = activity.icon;
|
||||
|
||||
const label = document.createElement("span");
|
||||
label.className = "label";
|
||||
label.textContent = activity.name;
|
||||
|
||||
card.append(icon, label);
|
||||
|
||||
card.addEventListener("click", () => {
|
||||
handleCardClick(activity);
|
||||
});
|
||||
|
||||
activityGrid.appendChild(card);
|
||||
});
|
||||
|
||||
updateNoticeAndContinue();
|
||||
}
|
||||
|
||||
function handleCardClick(activity) {
|
||||
const isAlreadySelected = selectedList.some((a) => a.name === activity.name);
|
||||
|
||||
if (activity.type === "long") {
|
||||
if (isAlreadySelected) {
|
||||
selectedList = [];
|
||||
} else {
|
||||
selectedList = [activity];
|
||||
}
|
||||
} else {
|
||||
// Short activity
|
||||
if (isAlreadySelected) {
|
||||
selectedList = selectedList.filter((a) => a.name !== activity.name);
|
||||
} else if (selectedDateType === "short") {
|
||||
// An kurzen Tagen ist nur maximal eine kurze Aktivität erlaubt
|
||||
selectedList = [activity];
|
||||
} else {
|
||||
// An langen Tagen:
|
||||
// Falls vorher eine lange Aktivität gewählt war, ersetzen
|
||||
if (selectedList.some((a) => a.type === "long")) {
|
||||
selectedList = [activity];
|
||||
} else if (selectedList.length >= 2) {
|
||||
// Maximal 2 kurze Aktivitäten, die zweite ersetzen
|
||||
selectedList = [selectedList[0], activity];
|
||||
} else {
|
||||
selectedList.push(activity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
renderActivities();
|
||||
}
|
||||
|
||||
function updateNoticeAndContinue() {
|
||||
const shortCount = selectedList.filter((a) => a.type === "short").length;
|
||||
const hasLong = selectedList.some((a) => a.type === "long");
|
||||
|
||||
if (selectedDateType === "long" && shortCount === 1 && !hasLong) {
|
||||
timeNotice.textContent =
|
||||
"💡 An diesem Tag haben wir reichlich Zeit! Wenn du möchtest, kannst du noch eine zweite kurze Aktivität auswählen.";
|
||||
timeNotice.classList.remove("hidden");
|
||||
} else if (selectedDateType === "long" && shortCount === 2) {
|
||||
timeNotice.textContent = "🎉 Perfekt! Du hast 2 kurze Aktivitäten für unseren Tag ausgewählt.";
|
||||
timeNotice.classList.remove("hidden");
|
||||
} else {
|
||||
timeNotice.classList.add("hidden");
|
||||
}
|
||||
|
||||
continueBtn.disabled = selectedList.length === 0;
|
||||
}
|
||||
|
||||
continueBtn.addEventListener("click", () => {
|
||||
if (selectedList.length === 0) {
|
||||
return;
|
||||
}
|
||||
localStorage.setItem("selectedActivities", JSON.stringify(selectedList));
|
||||
localStorage.setItem("selectedActivity", selectedList.map((a) => a.name).join(" + "));
|
||||
window.location.href = "name.html";
|
||||
});
|
||||
})();
|
||||
@@ -0,0 +1,53 @@
|
||||
{
|
||||
"activities": [
|
||||
{ "name": "Picknick auf der Wiese", "icon": "🧺", "type": "short" },
|
||||
{ "name": "Cocktailabend zu zweit", "icon": "🍸", "type": "long" },
|
||||
{ "name": "Massage zu Hause", "icon": "💆", "type": "short" },
|
||||
{ "name": "Ausgedehnte Wanderung", "icon": "🥾", "type": "long" },
|
||||
{ "name": "Kinofilm", "icon": "🎬", "type": "short" },
|
||||
{ "name": "Sushi selber machen", "icon": "🍣", "type": "short" },
|
||||
{ "name": "Untersetzer aus Ton basteln", "icon": "🪨", "type": "long" },
|
||||
{ "name": "Freizeitpark", "icon": "🎢", "type": "long" },
|
||||
{ "name": "Spaziergang im Wald", "icon": "🌲", "type": "short" },
|
||||
{ "name": "Kirschbier Tasting", "icon": "🍒", "type": "short" },
|
||||
{ "name": "Nudeln selber machen", "icon": "🍜", "type": "long" },
|
||||
{ "name": "Chillen im Freibad", "icon": "🏊", "type": "short" },
|
||||
{ "name": "Film zu Hause + Essen bestellen", "icon": "🛋️", "type": "long" },
|
||||
{ "name": "Squash spielen", "icon": "🏸", "type": "short" },
|
||||
{ "name": "Essen im Namaste", "icon": "🍛", "type": "short" },
|
||||
{ "name": "Museum für Gegenwartskunst", "icon": "🖼️", "type": "short" },
|
||||
{ "name": "Playstation Co-Op", "icon": "🎮", "type": "short" }
|
||||
],
|
||||
"availableDates": [
|
||||
{ "date": "2026-08-11", "type": "short" },
|
||||
{ "date": "2026-08-15", "type": "short" },
|
||||
{ "date": "2026-08-18", "type": "short" },
|
||||
{ "date": "2026-08-20", "type": "short" },
|
||||
{ "date": "2026-08-20", "type": "short" },
|
||||
{ "date": "2026-08-22", "type": "long" },
|
||||
{ "date": "2026-08-23", "type": "long" },
|
||||
{ "date": "2026-08-24", "type": "short" },
|
||||
{ "date": "2026-08-26", "type": "short" },
|
||||
{ "date": "2026-08-27", "type": "short" },
|
||||
{ "date": "2026-08-28", "type": "short" },
|
||||
{ "date": "2026-08-29", "type": "long" },
|
||||
{ "date": "2026-08-31", "type": "short" },
|
||||
{ "date": "2026-09-01", "type": "short" },
|
||||
{ "date": "2026-09-07", "type": "short" },
|
||||
{ "date": "2026-09-08", "type": "short" },
|
||||
{ "date": "2026-09-14", "type": "short" },
|
||||
{ "date": "2026-09-15", "type": "short" },
|
||||
{ "date": "2026-09-17", "type": "short" },
|
||||
{ "date": "2026-09-18", "type": "short" },
|
||||
{ "date": "2026-09-25", "type": "short" },
|
||||
{ "date": "2026-09-26", "type": "long" },
|
||||
{ "date": "2026-09-27", "type": "short" },
|
||||
{ "date": "2026-09-28", "type": "short" },
|
||||
{ "date": "2026-09-29", "type": "short" },
|
||||
{ "date": "2026-09-30", "type": "short" }
|
||||
|
||||
|
||||
|
||||
],
|
||||
"names": []
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/* ==========================================================================
|
||||
Page: Frage-Seite (index.html)
|
||||
Heart-shaped decorative background
|
||||
========================================================================== */
|
||||
|
||||
body.page-ask {
|
||||
display: flex;
|
||||
min-height: 100vh;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.page-ask .content {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
min-height: 100vh;
|
||||
width: 100%;
|
||||
max-width: 480px;
|
||||
margin: 0 auto;
|
||||
padding: 24px 20px;
|
||||
}
|
||||
|
||||
.page-ask .question {
|
||||
margin-bottom: 0.3em;
|
||||
}
|
||||
|
||||
.page-ask .subtitle {
|
||||
color: var(--color-text-light);
|
||||
font-size: clamp(1rem, 4vw, 1.1rem);
|
||||
margin-bottom: 2.5em;
|
||||
}
|
||||
|
||||
.page-ask .button-group {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 20px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.page-ask .btn {
|
||||
transform-origin: center;
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
// Frage-Seite: Ja/Nein-Interaktion
|
||||
// Der Nein-Button schrumpft und der Ja-Button wächst bei jedem Klick auf "Nein".
|
||||
|
||||
(function () {
|
||||
const yesBtn = document.getElementById("yesBtn");
|
||||
const noBtn = document.getElementById("noBtn");
|
||||
|
||||
const SHRINK_FACTOR = 0.66;
|
||||
const GROW_FACTOR = 1.33;
|
||||
const MIN_NO_SCALE = 0.12; // bleibt minimal antippbar
|
||||
const MAX_YES_SCALE = 3.2; // wird nicht größer als der Bildschirm
|
||||
|
||||
let noScale = 1;
|
||||
let yesScale = 1;
|
||||
|
||||
noBtn.addEventListener("click", () => {
|
||||
noScale = Math.max(MIN_NO_SCALE, noScale * SHRINK_FACTOR);
|
||||
yesScale = Math.min(MAX_YES_SCALE, yesScale * GROW_FACTOR);
|
||||
|
||||
noBtn.style.transform = `scale(${noScale})`;
|
||||
yesBtn.style.transform = `scale(${yesScale})`;
|
||||
});
|
||||
|
||||
yesBtn.addEventListener("click", () => {
|
||||
window.location.href = "datetime.html";
|
||||
});
|
||||
})();
|
||||
@@ -0,0 +1,123 @@
|
||||
/* ==========================================================================
|
||||
Page: Checkout / Bestätigung (checkout.html)
|
||||
========================================================================== */
|
||||
|
||||
.page-checkout .container {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.page-checkout .intro {
|
||||
text-align: center;
|
||||
margin-bottom: 1.5em;
|
||||
}
|
||||
|
||||
.page-checkout .intro p {
|
||||
color: var(--color-text-light);
|
||||
}
|
||||
|
||||
.summary-card {
|
||||
background: var(--color-white);
|
||||
border-radius: var(--radius-lg);
|
||||
box-shadow: var(--shadow-soft);
|
||||
padding: 20px;
|
||||
margin-bottom: 24px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.summary-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
padding-bottom: 12px;
|
||||
border-bottom: 1px solid var(--color-rose-light);
|
||||
}
|
||||
|
||||
.summary-item:last-child {
|
||||
padding-bottom: 0;
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.summary-icon {
|
||||
font-size: 1.8rem;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.summary-details {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.summary-label {
|
||||
font-size: 0.85rem;
|
||||
color: var(--color-text-light);
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
.summary-value {
|
||||
font-family: var(--font-heading);
|
||||
font-weight: 700;
|
||||
font-size: 1.1rem;
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
.checkout-btn {
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
/* Confirmation section --------------------------------------------------- */
|
||||
|
||||
.confirmation-card {
|
||||
background: var(--color-white);
|
||||
border-radius: var(--radius-lg);
|
||||
box-shadow: var(--shadow-strong);
|
||||
padding: 32px 24px;
|
||||
text-align: center;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
animation: fadeInScale 0.4s cubic-bezier(0.34, 1.56, 0.64, 1);
|
||||
}
|
||||
|
||||
.confirmation-icon {
|
||||
font-size: 3.5rem;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.confirmation-card h2 {
|
||||
margin: 0;
|
||||
font-size: 1.6rem;
|
||||
color: var(--color-rose-dark);
|
||||
}
|
||||
|
||||
.confirmation-card p {
|
||||
color: var(--color-text);
|
||||
font-size: 1.05rem;
|
||||
margin: 0;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.start-over-btn {
|
||||
margin-top: 12px;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.hidden {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
@keyframes fadeInScale {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: scale(0.9);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0" />
|
||||
<title>Date Bestätigung – Date-Planer</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||
<link href="https://fonts.googleapis.com/css2?family=Nunito:wght@400;600;700&family=Quicksand:wght@600;700&display=swap" rel="stylesheet" />
|
||||
<link rel="stylesheet" href="style.css" />
|
||||
<link rel="stylesheet" href="checkout.css" />
|
||||
</head>
|
||||
<body class="page-checkout">
|
||||
<div class="heart-background" aria-hidden="true">
|
||||
<div class="heart-shape"></div>
|
||||
</div>
|
||||
|
||||
<main class="container">
|
||||
<a id="backLink" class="back-link" href="name.html">‹ Zurück</a>
|
||||
|
||||
<div id="summarySection">
|
||||
<div class="intro">
|
||||
<h1>Fast geschafft! ✨</h1>
|
||||
<p>Überprüfe noch einmal eure Angaben für das perfekt geplante Date.</p>
|
||||
</div>
|
||||
|
||||
<div class="summary-card">
|
||||
<div class="summary-item">
|
||||
<span class="summary-icon" id="activityIcon">💌</span>
|
||||
<div class="summary-details">
|
||||
<span class="summary-label">Aktivität</span>
|
||||
<span class="summary-value" id="summaryActivity">-</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="summary-item">
|
||||
<span class="summary-icon">📅</span>
|
||||
<div class="summary-details">
|
||||
<span class="summary-label">Datum</span>
|
||||
<span class="summary-value" id="summaryDate">-</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="summary-item">
|
||||
<span class="summary-icon">👤</span>
|
||||
<div class="summary-details">
|
||||
<span class="summary-label">Name</span>
|
||||
<span class="summary-value" id="summaryName">-</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button id="checkoutBtn" class="btn btn-primary checkout-btn" type="button">Date verbindlich buchen 🎉</button>
|
||||
</div>
|
||||
|
||||
<div id="confirmationSection" class="confirmation-card hidden">
|
||||
<div class="confirmation-icon">🎉</div>
|
||||
<h2>Reservierung bestätigt!</h2>
|
||||
<p id="confirmationMessage">Deine Date-Reservierung wurde erfolgreich übermittelt. Ich freue mich schon riesig auf uns!</p>
|
||||
<a href="index.html" class="btn btn-primary start-over-btn">Zur Startseite</a>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<script src="checkout.js" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,100 @@
|
||||
// Checkout & Bestätigungsseite
|
||||
|
||||
(function () {
|
||||
const backLink = document.getElementById("backLink");
|
||||
const summarySection = document.getElementById("summarySection");
|
||||
const confirmationSection = document.getElementById("confirmationSection");
|
||||
const summaryActivity = document.getElementById("summaryActivity");
|
||||
const summaryDate = document.getElementById("summaryDate");
|
||||
const summaryName = document.getElementById("summaryName");
|
||||
const activityIcon = document.getElementById("activityIcon");
|
||||
const checkoutBtn = document.getElementById("checkoutBtn");
|
||||
|
||||
const selectedActivity = localStorage.getItem("selectedActivity");
|
||||
const selectedDate = localStorage.getItem("selectedDate");
|
||||
const selectedName = localStorage.getItem("selectedName");
|
||||
|
||||
if (!selectedActivity || !selectedDate || !selectedName) {
|
||||
window.location.href = "index.html";
|
||||
return;
|
||||
}
|
||||
|
||||
summaryActivity.textContent = selectedActivity;
|
||||
summaryName.textContent = selectedName;
|
||||
|
||||
const dateObj = new Date(`${selectedDate}T00:00:00`);
|
||||
const formattedDate = dateObj.toLocaleDateString("de-DE", {
|
||||
weekday: "long",
|
||||
day: "numeric",
|
||||
month: "long",
|
||||
year: "numeric",
|
||||
});
|
||||
summaryDate.textContent = formattedDate;
|
||||
|
||||
// Icons aus selectedActivities oder appsettings.json laden
|
||||
let storedActivities = [];
|
||||
try {
|
||||
storedActivities = JSON.parse(localStorage.getItem("selectedActivities") || "[]");
|
||||
} catch (e) {}
|
||||
|
||||
if (storedActivities.length > 0) {
|
||||
activityIcon.textContent = storedActivities.map((a) => a.icon).join(" ");
|
||||
} else {
|
||||
fetch("appsettings.json")
|
||||
.then((res) => res.json())
|
||||
.then((settings) => {
|
||||
const match = (settings.activities || []).find((a) => a.name === selectedActivity);
|
||||
if (match && match.icon) {
|
||||
activityIcon.textContent = match.icon;
|
||||
}
|
||||
})
|
||||
.catch(() => {});
|
||||
}
|
||||
|
||||
checkoutBtn.addEventListener("click", () => {
|
||||
checkoutBtn.disabled = true;
|
||||
const originalText = checkoutBtn.textContent;
|
||||
checkoutBtn.textContent = "Wird übermittelt... 💌";
|
||||
|
||||
const payload = {
|
||||
activity: selectedActivity,
|
||||
activities: storedActivities,
|
||||
date: selectedDate,
|
||||
dateType: localStorage.getItem("selectedDateType") || "long",
|
||||
name: selectedName,
|
||||
};
|
||||
|
||||
fetch("/api/submit", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(payload),
|
||||
})
|
||||
.then((res) => {
|
||||
if (!res.ok) {
|
||||
throw new Error("Fehler beim Übermitteln.");
|
||||
}
|
||||
return res.json();
|
||||
})
|
||||
.then((data) => {
|
||||
if (data.success) {
|
||||
summarySection.classList.add("hidden");
|
||||
if (backLink) {
|
||||
backLink.classList.add("hidden");
|
||||
}
|
||||
confirmationSection.classList.remove("hidden");
|
||||
} else {
|
||||
alert(data.message || "Es gab ein Problem bei der Übermittlung.");
|
||||
checkoutBtn.disabled = false;
|
||||
checkoutBtn.textContent = originalText;
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error("Submission error:", err);
|
||||
alert("Übermittlung fehlgeschlagen. Bitte versuche es erneut.");
|
||||
checkoutBtn.disabled = false;
|
||||
checkoutBtn.textContent = originalText;
|
||||
});
|
||||
});
|
||||
})();
|
||||
@@ -0,0 +1,119 @@
|
||||
/* ==========================================================================
|
||||
Page: Datum (datetime.html)
|
||||
========================================================================== */
|
||||
|
||||
.page-datetime .container {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.page-datetime .intro {
|
||||
text-align: center;
|
||||
margin-bottom: 1.5em;
|
||||
}
|
||||
|
||||
.page-datetime .intro p {
|
||||
color: var(--color-text-light);
|
||||
}
|
||||
|
||||
/* Calendar card ----------------------------------------------------------- */
|
||||
|
||||
.calendar-card {
|
||||
background: var(--color-white);
|
||||
border-radius: var(--radius-lg);
|
||||
box-shadow: var(--shadow-soft);
|
||||
padding: 16px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.calendar-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.month-label {
|
||||
margin: 0;
|
||||
font-size: 1.1rem;
|
||||
color: var(--color-rose-dark);
|
||||
}
|
||||
|
||||
.month-nav-btn {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border: none;
|
||||
border-radius: 50%;
|
||||
background: var(--color-rose-light);
|
||||
color: var(--color-rose-dark);
|
||||
font-size: 1.2rem;
|
||||
font-weight: 700;
|
||||
cursor: pointer;
|
||||
touch-action: manipulation;
|
||||
}
|
||||
|
||||
.month-nav-btn:disabled {
|
||||
opacity: 0.35;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.weekday-row,
|
||||
.day-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(7, 1fr);
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.weekday-row {
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.weekday-row span {
|
||||
text-align: center;
|
||||
font-size: 0.8rem;
|
||||
font-weight: 700;
|
||||
color: var(--color-text-light);
|
||||
}
|
||||
|
||||
.day-cell {
|
||||
position: relative;
|
||||
aspect-ratio: 1 / 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border: none;
|
||||
border-radius: 50%;
|
||||
background: transparent;
|
||||
font-family: var(--font-body);
|
||||
font-size: 0.95rem;
|
||||
font-weight: 600;
|
||||
color: var(--color-text);
|
||||
cursor: pointer;
|
||||
touch-action: manipulation;
|
||||
transition: background 0.2s ease, color 0.2s ease, transform 0.15s ease;
|
||||
}
|
||||
|
||||
.day-cell:active {
|
||||
transform: scale(0.92);
|
||||
}
|
||||
|
||||
.day-cell.is-empty {
|
||||
cursor: default;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.day-cell.is-disabled {
|
||||
color: #d9c3cb;
|
||||
cursor: not-allowed;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.day-cell.is-today {
|
||||
box-shadow: inset 0 0 0 2px var(--color-rose);
|
||||
}
|
||||
|
||||
.day-cell.is-selected {
|
||||
background: linear-gradient(135deg, var(--color-rose) 0%, var(--color-rose-dark) 100%);
|
||||
color: var(--color-white);
|
||||
box-shadow: var(--shadow-strong);
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0" />
|
||||
<title>Wähle das Datum – Date-Planer</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||
<link href="https://fonts.googleapis.com/css2?family=Nunito:wght@400;600;700&family=Quicksand:wght@600;700&display=swap" rel="stylesheet" />
|
||||
<link rel="stylesheet" href="style.css" />
|
||||
<link rel="stylesheet" href="datetime.css" />
|
||||
</head>
|
||||
<body class="page-datetime has-continue-bar">
|
||||
<div class="heart-background" aria-hidden="true">
|
||||
<div class="heart-shape"></div>
|
||||
</div>
|
||||
|
||||
<main class="container">
|
||||
<a class="back-link" href="index.html">‹ Zurück</a>
|
||||
<div class="intro">
|
||||
<h1>Wann soll es losgehen? 💌</h1>
|
||||
<p id="activitySummary">Wähl das passende Datum für unser Date aus.</p>
|
||||
</div>
|
||||
|
||||
<section class="calendar-card" aria-label="Kalender">
|
||||
<div class="calendar-header">
|
||||
<button id="prevMonthBtn" class="month-nav-btn" type="button" aria-label="Vorheriger Monat">‹</button>
|
||||
<h2 id="monthLabel" class="month-label">Monat Jahr</h2>
|
||||
<button id="nextMonthBtn" class="month-nav-btn" type="button" aria-label="Nächster Monat">›</button>
|
||||
</div>
|
||||
<div class="weekday-row" id="weekdayRow"></div>
|
||||
<div class="day-grid" id="dayGrid"></div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<div class="continue-bar">
|
||||
<div class="container">
|
||||
<button id="continueBtn" class="btn btn-primary" type="button" disabled>Weiter</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="datetime.js" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,152 @@
|
||||
// Datum: Kalender-Widget
|
||||
|
||||
(function () {
|
||||
const WEEKDAYS = ["Mo", "Di", "Mi", "Do", "Fr", "Sa", "So"];
|
||||
const MONTHS = [
|
||||
"Januar", "Februar", "März", "April", "Mai", "Juni",
|
||||
"Juli", "August", "September", "Oktober", "November", "Dezember",
|
||||
];
|
||||
|
||||
const monthLabel = document.getElementById("monthLabel");
|
||||
const weekdayRow = document.getElementById("weekdayRow");
|
||||
const dayGrid = document.getElementById("dayGrid");
|
||||
const prevMonthBtn = document.getElementById("prevMonthBtn");
|
||||
const nextMonthBtn = document.getElementById("nextMonthBtn");
|
||||
const continueBtn = document.getElementById("continueBtn");
|
||||
|
||||
const today = new Date();
|
||||
today.setHours(0, 0, 0, 0);
|
||||
|
||||
let availableDatesMap = new Map();
|
||||
let viewYear = today.getFullYear();
|
||||
let viewMonth = today.getMonth();
|
||||
let selectedDate = null;
|
||||
let selectedDateType = "long";
|
||||
|
||||
WEEKDAYS.forEach((day) => {
|
||||
const span = document.createElement("span");
|
||||
span.textContent = day;
|
||||
weekdayRow.appendChild(span);
|
||||
});
|
||||
|
||||
function toDateKey(date) {
|
||||
const year = date.getFullYear();
|
||||
const month = String(date.getMonth() + 1).padStart(2, "0");
|
||||
const day = String(date.getDate()).padStart(2, "0");
|
||||
return `${year}-${month}-${day}`;
|
||||
}
|
||||
|
||||
fetch("appsettings.json")
|
||||
.then((response) => response.json())
|
||||
.then((settings) => {
|
||||
const datesList = settings.availableDates || [];
|
||||
datesList.forEach((item) => {
|
||||
if (typeof item === "string") {
|
||||
availableDatesMap.set(item, { type: "long" });
|
||||
} else if (item && item.date) {
|
||||
availableDatesMap.set(item.date, { type: item.type || "long" });
|
||||
}
|
||||
});
|
||||
|
||||
const upcoming = Array.from(availableDatesMap.keys())
|
||||
.map((dateKey) => new Date(`${dateKey}T00:00:00`))
|
||||
.filter((date) => date > today)
|
||||
.sort((a, b) => a - b);
|
||||
|
||||
if (upcoming.length > 0) {
|
||||
viewYear = upcoming[0].getFullYear();
|
||||
viewMonth = upcoming[0].getMonth();
|
||||
}
|
||||
|
||||
renderCalendar();
|
||||
})
|
||||
.catch(() => {
|
||||
dayGrid.textContent = "Verfügbare Termine konnten nicht geladen werden.";
|
||||
});
|
||||
|
||||
function renderCalendar() {
|
||||
monthLabel.textContent = `${MONTHS[viewMonth]} ${viewYear}`;
|
||||
dayGrid.innerHTML = "";
|
||||
|
||||
const firstOfMonth = new Date(viewYear, viewMonth, 1);
|
||||
// Montag = 0 ... Sonntag = 6
|
||||
const leadingBlanks = (firstOfMonth.getDay() + 6) % 7;
|
||||
const daysInMonth = new Date(viewYear, viewMonth + 1, 0).getDate();
|
||||
|
||||
for (let i = 0; i < leadingBlanks; i++) {
|
||||
const blank = document.createElement("span");
|
||||
blank.className = "day-cell is-empty";
|
||||
dayGrid.appendChild(blank);
|
||||
}
|
||||
|
||||
for (let day = 1; day <= daysInMonth; day++) {
|
||||
const cellDate = new Date(viewYear, viewMonth, day);
|
||||
const key = toDateKey(cellDate);
|
||||
const button = document.createElement("button");
|
||||
button.type = "button";
|
||||
button.className = "day-cell";
|
||||
button.textContent = String(day);
|
||||
|
||||
const dateInfo = availableDatesMap.get(key);
|
||||
const isPastOrToday = cellDate <= today;
|
||||
const isAvailable = Boolean(dateInfo) && !isPastOrToday;
|
||||
const isToday = cellDate.getTime() === today.getTime();
|
||||
const isSelected = selectedDate && cellDate.getTime() === selectedDate.getTime();
|
||||
|
||||
if (!isAvailable) {
|
||||
button.classList.add("is-disabled");
|
||||
button.disabled = true;
|
||||
}
|
||||
if (isToday) {
|
||||
button.classList.add("is-today");
|
||||
}
|
||||
if (isSelected) {
|
||||
button.classList.add("is-selected");
|
||||
}
|
||||
|
||||
button.addEventListener("click", () => {
|
||||
selectedDate = cellDate;
|
||||
selectedDateType = dateInfo ? dateInfo.type : "long";
|
||||
renderCalendar();
|
||||
updateContinueState();
|
||||
});
|
||||
|
||||
dayGrid.appendChild(button);
|
||||
}
|
||||
|
||||
const isCurrentMonth = viewYear === today.getFullYear() && viewMonth === today.getMonth();
|
||||
prevMonthBtn.disabled = isCurrentMonth;
|
||||
}
|
||||
|
||||
prevMonthBtn.addEventListener("click", () => {
|
||||
viewMonth -= 1;
|
||||
if (viewMonth < 0) {
|
||||
viewMonth = 11;
|
||||
viewYear -= 1;
|
||||
}
|
||||
renderCalendar();
|
||||
});
|
||||
|
||||
nextMonthBtn.addEventListener("click", () => {
|
||||
viewMonth += 1;
|
||||
if (viewMonth > 11) {
|
||||
viewMonth = 0;
|
||||
viewYear += 1;
|
||||
}
|
||||
renderCalendar();
|
||||
});
|
||||
|
||||
function updateContinueState() {
|
||||
continueBtn.disabled = !selectedDate;
|
||||
}
|
||||
|
||||
continueBtn.addEventListener("click", () => {
|
||||
if (!selectedDate) {
|
||||
return;
|
||||
}
|
||||
localStorage.setItem("selectedDate", toDateKey(selectedDate));
|
||||
localStorage.setItem("selectedDateType", selectedDateType);
|
||||
window.location.href = "activity.html";
|
||||
});
|
||||
|
||||
})();
|
||||
@@ -0,0 +1,30 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0" />
|
||||
<title>Willst du mit mir gehen? – Date-Planer</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||
<link href="https://fonts.googleapis.com/css2?family=Nunito:wght@400;600;700&family=Quicksand:wght@600;700&display=swap" rel="stylesheet" />
|
||||
<link rel="stylesheet" href="style.css" />
|
||||
<link rel="stylesheet" href="ask.css" />
|
||||
</head>
|
||||
<body class="page-ask">
|
||||
<div class="heart-background" aria-hidden="true">
|
||||
<div class="heart-shape"></div>
|
||||
</div>
|
||||
|
||||
<main class="content">
|
||||
<h1 class="question">Hallo, ich bin dein Ehemann.<br>Hättest du Lust, mit mir auszugehen? 💕</h1>
|
||||
<p class="subtitle">Eine kleine Frage mit hoffentlich großer Antwort …</p>
|
||||
|
||||
<div class="button-group">
|
||||
<button id="yesBtn" class="btn btn-yes" type="button">Ja!</button>
|
||||
<button id="noBtn" class="btn btn-no" type="button">Nein</button>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<script src="ask.js" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,65 @@
|
||||
/* ==========================================================================
|
||||
Page: Namensauswahl (name.html)
|
||||
========================================================================== */
|
||||
|
||||
.page-name .container {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.page-name .intro {
|
||||
text-align: center;
|
||||
margin-bottom: 1.5em;
|
||||
}
|
||||
|
||||
.page-name .intro p {
|
||||
color: var(--color-text-light);
|
||||
}
|
||||
|
||||
.name-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 14px;
|
||||
}
|
||||
|
||||
.name-card {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 14px;
|
||||
width: 100%;
|
||||
min-height: 64px;
|
||||
padding: 14px 20px;
|
||||
background: var(--color-white);
|
||||
border: 2px solid transparent;
|
||||
border-radius: var(--radius-md);
|
||||
box-shadow: var(--shadow-soft);
|
||||
cursor: pointer;
|
||||
touch-action: manipulation;
|
||||
transition: transform 0.2s ease, box-shadow 0.2s ease, border-color 0.2s ease, background 0.2s ease;
|
||||
}
|
||||
|
||||
.name-card:active {
|
||||
transform: scale(0.98);
|
||||
}
|
||||
|
||||
.name-card .icon {
|
||||
font-size: 1.6rem;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.name-card .label {
|
||||
font-family: var(--font-heading);
|
||||
font-weight: 700;
|
||||
font-size: 1.1rem;
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
.name-card.selected {
|
||||
border-color: var(--color-rose);
|
||||
background: linear-gradient(135deg, var(--color-rose-light) 0%, var(--color-white) 100%);
|
||||
box-shadow: var(--shadow-strong);
|
||||
}
|
||||
|
||||
.name-card.selected .label {
|
||||
color: var(--color-rose-dark);
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0" />
|
||||
<title>Wer plant das Date? – Date-Planer</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||
<link href="https://fonts.googleapis.com/css2?family=Nunito:wght@400;600;700&family=Quicksand:wght@600;700&display=swap" rel="stylesheet" />
|
||||
<link rel="stylesheet" href="style.css" />
|
||||
<link rel="stylesheet" href="name.css" />
|
||||
</head>
|
||||
<body class="page-name has-continue-bar">
|
||||
<div class="heart-background" aria-hidden="true">
|
||||
<div class="heart-shape"></div>
|
||||
</div>
|
||||
|
||||
<main class="container">
|
||||
<a class="back-link" href="datetime.html">‹ Zurück</a>
|
||||
<div class="intro">
|
||||
<h1>Wer bist du? 💖</h1>
|
||||
<p id="summary">Verrate mir, wer du bist.</p>
|
||||
</div>
|
||||
|
||||
<div class="name-list" id="nameList"></div>
|
||||
</main>
|
||||
|
||||
<div class="continue-bar">
|
||||
<div class="container">
|
||||
<button id="continueBtn" class="btn btn-primary" type="button" disabled>Weiter</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="name.js" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,76 @@
|
||||
// Namensauswahl: lädt Namen aus appsettings.json, Kartenauswahl + Abschluss
|
||||
|
||||
(function () {
|
||||
const nameList = document.getElementById("nameList");
|
||||
const summary = document.getElementById("summary");
|
||||
const continueBtn = document.getElementById("continueBtn");
|
||||
|
||||
const selectedActivity = localStorage.getItem("selectedActivity");
|
||||
const selectedDate = localStorage.getItem("selectedDate");
|
||||
|
||||
// Ohne Aktivität und Datum ergibt diese Seite keinen Sinn.
|
||||
if (!selectedActivity) {
|
||||
window.location.href = "activity.html";
|
||||
return;
|
||||
}
|
||||
if (!selectedDate) {
|
||||
window.location.href = "datetime.html";
|
||||
return;
|
||||
}
|
||||
|
||||
const dateLabel = new Date(`${selectedDate}T00:00:00`).toLocaleDateString("de-DE", {
|
||||
weekday: "long",
|
||||
day: "numeric",
|
||||
month: "long",
|
||||
year: "numeric",
|
||||
});
|
||||
summary.textContent = `${selectedActivity} am ${dateLabel} – verrate mir, wer du bist.`;
|
||||
|
||||
let selectedName = null;
|
||||
|
||||
fetch("appsettings.json")
|
||||
.then((response) => response.json())
|
||||
.then((settings) => renderNames(settings.names || []))
|
||||
.catch(() => {
|
||||
nameList.textContent = "Namen konnten nicht geladen werden.";
|
||||
});
|
||||
|
||||
function renderNames(names) {
|
||||
const options = names.length > 0 ? names : ["Du weißt, wer ich bin"];
|
||||
|
||||
options.forEach((name) => {
|
||||
const card = document.createElement("button");
|
||||
card.type = "button";
|
||||
card.className = "name-card";
|
||||
card.dataset.name = name;
|
||||
|
||||
const icon = document.createElement("span");
|
||||
icon.className = "icon";
|
||||
icon.setAttribute("aria-hidden", "true");
|
||||
icon.textContent = "💖";
|
||||
|
||||
const label = document.createElement("span");
|
||||
label.className = "label";
|
||||
label.textContent = name;
|
||||
|
||||
card.append(icon, label);
|
||||
card.addEventListener("click", () => {
|
||||
nameList.querySelectorAll(".name-card").forEach((c) => c.classList.remove("selected"));
|
||||
card.classList.add("selected");
|
||||
selectedName = name;
|
||||
continueBtn.disabled = false;
|
||||
});
|
||||
|
||||
nameList.appendChild(card);
|
||||
});
|
||||
}
|
||||
|
||||
continueBtn.addEventListener("click", () => {
|
||||
if (!selectedName) {
|
||||
return;
|
||||
}
|
||||
// Ablage für die spätere Anbindung an Datenbank/Storage
|
||||
localStorage.setItem("selectedName", selectedName);
|
||||
window.location.href = "checkout.html";
|
||||
});
|
||||
})();
|
||||
@@ -0,0 +1,230 @@
|
||||
/* ==========================================================================
|
||||
Date-Planer – Shared mobile-first stylesheet
|
||||
Base color: love-rose, complemented by warm peach and gold tones.
|
||||
========================================================================== */
|
||||
|
||||
:root {
|
||||
--color-rose: #e0577e;
|
||||
--color-rose-dark: #b83a5e;
|
||||
--color-rose-light: #ffd1dc;
|
||||
--color-peach: #ffb4a2;
|
||||
--color-gold: #ffd166;
|
||||
--color-bg: #fff5f7;
|
||||
--color-bg-gradient: linear-gradient(160deg, #fff5f7 0%, #ffe8ec 45%, #ffe3d6 100%);
|
||||
--color-text: #4a2c3d;
|
||||
--color-text-light: #7a5568;
|
||||
--color-white: #fffdfd;
|
||||
|
||||
--font-heading: "Quicksand", "Segoe UI", sans-serif;
|
||||
--font-body: "Nunito", "Segoe UI", sans-serif;
|
||||
|
||||
--radius-lg: 24px;
|
||||
--radius-md: 16px;
|
||||
--shadow-soft: 0 8px 24px rgba(184, 58, 94, 0.2);
|
||||
--shadow-strong: 0 12px 32px rgba(184, 58, 94, 0.32);
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
html {
|
||||
min-height: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
min-height: 100vh;
|
||||
min-height: 100dvh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
font-family: var(--font-body);
|
||||
font-size: clamp(1rem, 3.6vw, 1.15rem);
|
||||
line-height: 1.5;
|
||||
color: var(--color-text);
|
||||
background: var(--color-bg-gradient);
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
h1,
|
||||
h2,
|
||||
h3 {
|
||||
font-family: var(--font-heading);
|
||||
font-weight: 700;
|
||||
margin: 0 0 0.5em;
|
||||
color: var(--color-rose-dark);
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: clamp(1.75rem, 7vw, 2.5rem);
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 0 0 1em;
|
||||
}
|
||||
|
||||
.container {
|
||||
width: 100%;
|
||||
max-width: 480px;
|
||||
margin: 0 auto;
|
||||
padding: 20px 20px 32px;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
/* Buttons -------------------------------------------------------------- */
|
||||
|
||||
.btn {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
min-height: 56px;
|
||||
padding: 14px 28px;
|
||||
border: none;
|
||||
border-radius: var(--radius-lg);
|
||||
font-family: var(--font-heading);
|
||||
font-size: clamp(1.05rem, 4.5vw, 1.25rem);
|
||||
font-weight: 700;
|
||||
cursor: pointer;
|
||||
touch-action: manipulation;
|
||||
transition: transform 0.35s cubic-bezier(0.34, 1.56, 0.64, 1), box-shadow 0.2s ease, opacity 0.2s ease;
|
||||
}
|
||||
|
||||
.btn:active {
|
||||
transform: scale(0.96);
|
||||
}
|
||||
|
||||
.btn-yes {
|
||||
background: linear-gradient(135deg, var(--color-rose) 0%, var(--color-rose-dark) 100%);
|
||||
color: var(--color-white);
|
||||
box-shadow: var(--shadow-soft);
|
||||
}
|
||||
|
||||
.btn-no {
|
||||
background: var(--color-white);
|
||||
color: var(--color-text-light);
|
||||
box-shadow: var(--shadow-soft);
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
width: 100%;
|
||||
background: linear-gradient(135deg, var(--color-rose) 0%, var(--color-rose-dark) 100%);
|
||||
color: var(--color-white);
|
||||
box-shadow: var(--shadow-soft);
|
||||
}
|
||||
|
||||
.btn-primary:disabled {
|
||||
background: #e6d3d8;
|
||||
color: #b39aa4;
|
||||
box-shadow: none;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
/* Heart-shaped decorative background (reused across pages) -------------- */
|
||||
|
||||
.heart-background {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.heart-shape {
|
||||
position: relative;
|
||||
width: 62vmin;
|
||||
height: 56vmin;
|
||||
}
|
||||
|
||||
.heart-shape::before,
|
||||
.heart-shape::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
width: 31vmin;
|
||||
height: 50vmin;
|
||||
background: linear-gradient(135deg, var(--color-rose-light) 0%, var(--color-peach) 100%);
|
||||
border-radius: 31vmin 31vmin 0 0;
|
||||
opacity: 0.55;
|
||||
}
|
||||
|
||||
.heart-shape::before {
|
||||
left: 31vmin;
|
||||
transform: rotate(45deg);
|
||||
transform-origin: 0 100%;
|
||||
}
|
||||
|
||||
.heart-shape::after {
|
||||
left: 0;
|
||||
transform: rotate(-45deg);
|
||||
transform-origin: 100% 100%;
|
||||
}
|
||||
|
||||
/* Fixed bottom bar for a primary continue action ------------------------- */
|
||||
|
||||
body.has-continue-bar main.container {
|
||||
padding-bottom: calc(110px + env(safe-area-inset-bottom));
|
||||
}
|
||||
|
||||
.continue-bar {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
z-index: 100;
|
||||
padding: 12px 20px calc(12px + env(safe-area-inset-bottom));
|
||||
background: linear-gradient(180deg, rgba(255, 245, 247, 0) 0%, var(--color-bg) 50%);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.continue-bar .container {
|
||||
padding: 0;
|
||||
max-width: 480px;
|
||||
}
|
||||
|
||||
.continue-bar .btn {
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.container {
|
||||
flex: none;
|
||||
}
|
||||
|
||||
body.has-continue-bar main.container {
|
||||
padding-bottom: 12px;
|
||||
}
|
||||
|
||||
.continue-bar {
|
||||
position: static;
|
||||
padding: 0 20px 40px;
|
||||
background: none;
|
||||
pointer-events: auto;
|
||||
}
|
||||
}
|
||||
|
||||
/* Back link --------------------------------------------------------------- */
|
||||
|
||||
.back-link {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
margin-bottom: 12px;
|
||||
color: var(--color-text-light);
|
||||
font-family: var(--font-heading);
|
||||
font-weight: 700;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
/* Utility ---------------------------------------------------------------- */
|
||||
|
||||
.visually-hidden {
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
overflow: hidden;
|
||||
clip: rect(0 0 0 0);
|
||||
white-space: nowrap;
|
||||
}
|
||||
Reference in New Issue
Block a user