feat: restructure flow to date-first and add support for long/short days and activities

This commit is contained in:
2026-08-12 13:13:54 +02:00
parent 3d7da6c8da
commit fc01c503b7
9 changed files with 191 additions and 58 deletions
@@ -0,0 +1,10 @@
i want to perform a little logical restructure
there are long and short acitivies and there are long activitiees
there are days where we have more time and days where we have less time
on days where there is more time, we can do one long activity or two short activities
on days where there is less time, we can only do one short activity
so in the appsettings.json file i need to be able to define which days are long days and which days are short days.
and i need to be able to define which activities are long and which activities are short.
from now on, the date-selection comes first, then the activity selection. IF the user selected a long day AND a short activity, the user needs to be informed that on that day, there is still more time for another activity and have the option to select another short activity. If the user selects a short day, he will only have the option to select a short activity but doesnt need to be informed that there were options for long activities in the first place
+28
View File
@@ -73,3 +73,31 @@
color: var(--color-rose-dark); color: var(--color-rose-dark);
font-weight: 700; font-weight: 700;
} }
.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);
}
}
+6 -2
View File
@@ -12,10 +12,14 @@
</head> </head>
<body class="page-activity has-continue-bar"> <body class="page-activity has-continue-bar">
<main class="container"> <main class="container">
<a class="back-link" href="index.html"> Zurück</a> <a class="back-link" href="datetime.html"> Zurück</a>
<div class="intro"> <div class="intro">
<h1>Worauf hast du Lust? 🥰</h1> <h1>Worauf hast du Lust? 🥰</h1>
<p>Wähle eure gemeinsame Aktivität für das Date aus.</p> <p id="dateSummary">Wähle eure 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 2. kurze Aktivität auswählen.
</div> </div>
<div class="activity-grid" id="activityGrid"></div> <div class="activity-grid" id="activityGrid"></div>
+94 -12
View File
@@ -2,24 +2,59 @@
(function () { (function () {
const activityGrid = document.getElementById("activityGrid"); const activityGrid = document.getElementById("activityGrid");
const dateSummary = document.getElementById("dateSummary");
const timeNotice = document.getElementById("timeNotice");
const continueBtn = document.getElementById("continueBtn"); const continueBtn = document.getElementById("continueBtn");
let selectedActivity = null; 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 euer Date am ${formattedDate}.`;
let availableActivities = [];
let selectedList = [];
fetch("appsettings.json") fetch("appsettings.json")
.then((response) => response.json()) .then((response) => response.json())
.then((settings) => renderActivities(settings.activities || [])) .then((settings) => {
const allActivities = settings.activities || [];
if (selectedDateType === "short") {
availableActivities = allActivities.filter((a) => a.type === "short");
} else {
availableActivities = allActivities;
}
renderActivities();
})
.catch(() => { .catch(() => {
activityGrid.textContent = "Aktivitäten konnten nicht geladen werden."; activityGrid.textContent = "Aktivitäten konnten nicht geladen werden.";
}); });
function renderActivities(activities) { function renderActivities() {
activities.forEach((activity) => { activityGrid.innerHTML = "";
availableActivities.forEach((activity) => {
const card = document.createElement("button"); const card = document.createElement("button");
card.type = "button"; card.type = "button";
card.className = "activity-card"; card.className = "activity-card";
card.dataset.activity = activity.name; card.dataset.activity = activity.name;
const isSelected = selectedList.some((a) => a.name === activity.name);
if (isSelected) {
card.classList.add("selected");
}
const icon = document.createElement("span"); const icon = document.createElement("span");
icon.className = "icon"; icon.className = "icon";
icon.setAttribute("aria-hidden", "true"); icon.setAttribute("aria-hidden", "true");
@@ -30,23 +65,70 @@
label.textContent = activity.name; label.textContent = activity.name;
card.append(icon, label); card.append(icon, label);
card.addEventListener("click", () => { card.addEventListener("click", () => {
activityGrid.querySelectorAll(".activity-card").forEach((c) => c.classList.remove("selected")); handleCardClick(activity);
card.classList.add("selected");
selectedActivity = activity.name;
continueBtn.disabled = false;
}); });
activityGrid.appendChild(card); 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 a long activity was selected, replace it
if (selectedList.some((a) => a.type === "long")) {
selectedList = [activity];
} else if (selectedList.length >= 2) {
// Max 2 short activities, replace the second one
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 2. 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 euren langen Tag ausgewählt.";
timeNotice.classList.remove("hidden");
} else {
timeNotice.classList.add("hidden");
}
continueBtn.disabled = selectedList.length === 0;
} }
continueBtn.addEventListener("click", () => { continueBtn.addEventListener("click", () => {
if (!selectedActivity) { if (selectedList.length === 0) {
return; return;
} }
// Ablage für die spätere Anbindung an Datenbank/Storage localStorage.setItem("selectedActivities", JSON.stringify(selectedList));
localStorage.setItem("selectedActivity", selectedActivity); localStorage.setItem("selectedActivity", selectedList.map((a) => a.name).join(" + "));
window.location.href = "datetime.html"; window.location.href = "name.html";
}); });
})(); })();
+14 -16
View File
@@ -1,22 +1,20 @@
{ {
"activities": [ "activities": [
{ "name": "Essen bei Namaste", "icon": "🍛" }, { "name": "Essen bei Namaste", "icon": "🍛", "type": "short" },
{ "name": "Kinofilm", "icon": "🎬" }, { "name": "Kinofilm", "icon": "🎬", "type": "long" },
{ "name": "Film zu Hause + Essen bestellen", "icon": "🛋️" }, { "name": "Film zu Hause + Essen bestellen", "icon": "🛋️", "type": "long" },
{ "name": "Spaziergang im Wald", "icon": "🌲" }, { "name": "Spaziergang im Wald", "icon": "🌲", "type": "short" },
{ "name": "Museum für Gegenwartskunst", "icon": "🖼️" }, { "name": "Museum für Gegenwartskunst", "icon": "🖼️", "type": "long" },
{ "name": "Picknick auf der Wiese", "icon": "🧺" }, { "name": "Picknick auf der Wiese", "icon": "🧺", "type": "short" },
{ "name": "Massage zu Hause", "icon": "💆" } { "name": "Massage zu Hause", "icon": "💆", "type": "short" }
], ],
"availableDates": [ "availableDates": [
"2026-08-15", { "date": "2026-08-15", "type": "long" },
"2026-08-16", { "date": "2026-08-16", "type": "short" },
"2026-08-23", { "date": "2026-08-23", "type": "long" },
"2026-08-30", { "date": "2026-08-30", "type": "short" },
"2026-09-05", { "date": "2026-09-05", "type": "long" },
"2026-09-06" { "date": "2026-09-06", "type": "short" }
], ],
"names": [ "names": []
]
} }
+1 -1
View File
@@ -22,6 +22,6 @@
}); });
yesBtn.addEventListener("click", () => { yesBtn.addEventListener("click", () => {
window.location.href = "activity.html"; window.location.href = "datetime.html";
}); });
})(); })();
+10 -1
View File
@@ -31,7 +31,15 @@
}); });
summaryDate.textContent = formattedDate; summaryDate.textContent = formattedDate;
// Icon aus appsettings.json laden, falls verfügbar // 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") fetch("appsettings.json")
.then((res) => res.json()) .then((res) => res.json())
.then((settings) => { .then((settings) => {
@@ -41,6 +49,7 @@
} }
}) })
.catch(() => {}); .catch(() => {});
}
checkoutBtn.addEventListener("click", () => { checkoutBtn.addEventListener("click", () => {
// Später: API / Mail-Versand / Kalendereintrag ausführen // Später: API / Mail-Versand / Kalendereintrag ausführen
+2 -2
View File
@@ -16,10 +16,10 @@
</div> </div>
<main class="container"> <main class="container">
<a class="back-link" href="activity.html"> Zurück</a> <a class="back-link" href="index.html"> Zurück</a>
<div class="intro"> <div class="intro">
<h1>Wann soll es losgehen? 💌</h1> <h1>Wann soll es losgehen? 💌</h1>
<p id="activitySummary">Wähle das Datum für euer Date.</p> <p id="activitySummary">Wähle das passende Datum für euer Date aus.</p>
</div> </div>
<section class="calendar-card" aria-label="Kalender"> <section class="calendar-card" aria-label="Kalender">
+17 -15
View File
@@ -7,7 +7,6 @@
"Juli", "August", "September", "Oktober", "November", "Dezember", "Juli", "August", "September", "Oktober", "November", "Dezember",
]; ];
const activitySummary = document.getElementById("activitySummary");
const monthLabel = document.getElementById("monthLabel"); const monthLabel = document.getElementById("monthLabel");
const weekdayRow = document.getElementById("weekdayRow"); const weekdayRow = document.getElementById("weekdayRow");
const dayGrid = document.getElementById("dayGrid"); const dayGrid = document.getElementById("dayGrid");
@@ -15,21 +14,14 @@
const nextMonthBtn = document.getElementById("nextMonthBtn"); const nextMonthBtn = document.getElementById("nextMonthBtn");
const continueBtn = document.getElementById("continueBtn"); const continueBtn = document.getElementById("continueBtn");
// Ohne gewählte Aktivität ergibt diese Seite keinen Sinn.
const selectedActivity = localStorage.getItem("selectedActivity");
if (!selectedActivity) {
window.location.href = "activity.html";
return;
}
activitySummary.textContent = `Für unser Date: „${selectedActivity}`;
const today = new Date(); const today = new Date();
today.setHours(0, 0, 0, 0); today.setHours(0, 0, 0, 0);
let availableDates = new Set(); let availableDatesMap = new Map();
let viewYear = today.getFullYear(); let viewYear = today.getFullYear();
let viewMonth = today.getMonth(); let viewMonth = today.getMonth();
let selectedDate = null; let selectedDate = null;
let selectedDateType = "long";
WEEKDAYS.forEach((day) => { WEEKDAYS.forEach((day) => {
const span = document.createElement("span"); const span = document.createElement("span");
@@ -47,9 +39,16 @@
fetch("appsettings.json") fetch("appsettings.json")
.then((response) => response.json()) .then((response) => response.json())
.then((settings) => { .then((settings) => {
availableDates = new Set(settings.availableDates || []); 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(availableDates) const upcoming = Array.from(availableDatesMap.keys())
.map((dateKey) => new Date(`${dateKey}T00:00:00`)) .map((dateKey) => new Date(`${dateKey}T00:00:00`))
.filter((date) => date >= today) .filter((date) => date >= today)
.sort((a, b) => a - b); .sort((a, b) => a - b);
@@ -82,12 +81,14 @@
for (let day = 1; day <= daysInMonth; day++) { for (let day = 1; day <= daysInMonth; day++) {
const cellDate = new Date(viewYear, viewMonth, day); const cellDate = new Date(viewYear, viewMonth, day);
const key = toDateKey(cellDate);
const button = document.createElement("button"); const button = document.createElement("button");
button.type = "button"; button.type = "button";
button.className = "day-cell"; button.className = "day-cell";
button.textContent = String(day); button.textContent = String(day);
const isAvailable = availableDates.has(toDateKey(cellDate)); const dateInfo = availableDatesMap.get(key);
const isAvailable = Boolean(dateInfo);
const isToday = cellDate.getTime() === today.getTime(); const isToday = cellDate.getTime() === today.getTime();
const isSelected = selectedDate && cellDate.getTime() === selectedDate.getTime(); const isSelected = selectedDate && cellDate.getTime() === selectedDate.getTime();
@@ -104,6 +105,7 @@
button.addEventListener("click", () => { button.addEventListener("click", () => {
selectedDate = cellDate; selectedDate = cellDate;
selectedDateType = dateInfo ? dateInfo.type : "long";
renderCalendar(); renderCalendar();
updateContinueState(); updateContinueState();
}); });
@@ -141,9 +143,9 @@
if (!selectedDate) { if (!selectedDate) {
return; return;
} }
// Ablage für die spätere Anbindung an Datenbank/Storage
localStorage.setItem("selectedDate", toDateKey(selectedDate)); localStorage.setItem("selectedDate", toDateKey(selectedDate));
window.location.href = "name.html"; localStorage.setItem("selectedDateType", selectedDateType);
window.location.href = "activity.html";
}); });
})(); })();