feat: restructure flow to date-first and add support for long/short days and activities
This commit is contained in:
@@ -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
|
||||
@@ -73,3 +73,31 @@
|
||||
color: var(--color-rose-dark);
|
||||
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
@@ -12,10 +12,14 @@
|
||||
</head>
|
||||
<body class="page-activity has-continue-bar">
|
||||
<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">
|
||||
<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 class="activity-grid" id="activityGrid"></div>
|
||||
|
||||
+94
-12
@@ -2,24 +2,59 @@
|
||||
|
||||
(function () {
|
||||
const activityGrid = document.getElementById("activityGrid");
|
||||
const dateSummary = document.getElementById("dateSummary");
|
||||
const timeNotice = document.getElementById("timeNotice");
|
||||
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")
|
||||
.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(() => {
|
||||
activityGrid.textContent = "Aktivitäten konnten nicht geladen werden.";
|
||||
});
|
||||
|
||||
function renderActivities(activities) {
|
||||
activities.forEach((activity) => {
|
||||
function renderActivities() {
|
||||
activityGrid.innerHTML = "";
|
||||
|
||||
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 icon = document.createElement("span");
|
||||
icon.className = "icon";
|
||||
icon.setAttribute("aria-hidden", "true");
|
||||
@@ -30,23 +65,70 @@
|
||||
label.textContent = activity.name;
|
||||
|
||||
card.append(icon, label);
|
||||
|
||||
card.addEventListener("click", () => {
|
||||
activityGrid.querySelectorAll(".activity-card").forEach((c) => c.classList.remove("selected"));
|
||||
card.classList.add("selected");
|
||||
selectedActivity = activity.name;
|
||||
continueBtn.disabled = false;
|
||||
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 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", () => {
|
||||
if (!selectedActivity) {
|
||||
if (selectedList.length === 0) {
|
||||
return;
|
||||
}
|
||||
// Ablage für die spätere Anbindung an Datenbank/Storage
|
||||
localStorage.setItem("selectedActivity", selectedActivity);
|
||||
window.location.href = "datetime.html";
|
||||
localStorage.setItem("selectedActivities", JSON.stringify(selectedList));
|
||||
localStorage.setItem("selectedActivity", selectedList.map((a) => a.name).join(" + "));
|
||||
window.location.href = "name.html";
|
||||
});
|
||||
})();
|
||||
|
||||
+14
-16
@@ -1,22 +1,20 @@
|
||||
{
|
||||
"activities": [
|
||||
{ "name": "Essen bei Namaste", "icon": "🍛" },
|
||||
{ "name": "Kinofilm", "icon": "🎬" },
|
||||
{ "name": "Film zu Hause + Essen bestellen", "icon": "🛋️" },
|
||||
{ "name": "Spaziergang im Wald", "icon": "🌲" },
|
||||
{ "name": "Museum für Gegenwartskunst", "icon": "🖼️" },
|
||||
{ "name": "Picknick auf der Wiese", "icon": "🧺" },
|
||||
{ "name": "Massage zu Hause", "icon": "💆" }
|
||||
{ "name": "Essen bei Namaste", "icon": "🍛", "type": "short" },
|
||||
{ "name": "Kinofilm", "icon": "🎬", "type": "long" },
|
||||
{ "name": "Film zu Hause + Essen bestellen", "icon": "🛋️", "type": "long" },
|
||||
{ "name": "Spaziergang im Wald", "icon": "🌲", "type": "short" },
|
||||
{ "name": "Museum für Gegenwartskunst", "icon": "🖼️", "type": "long" },
|
||||
{ "name": "Picknick auf der Wiese", "icon": "🧺", "type": "short" },
|
||||
{ "name": "Massage zu Hause", "icon": "💆", "type": "short" }
|
||||
],
|
||||
"availableDates": [
|
||||
"2026-08-15",
|
||||
"2026-08-16",
|
||||
"2026-08-23",
|
||||
"2026-08-30",
|
||||
"2026-09-05",
|
||||
"2026-09-06"
|
||||
{ "date": "2026-08-15", "type": "long" },
|
||||
{ "date": "2026-08-16", "type": "short" },
|
||||
{ "date": "2026-08-23", "type": "long" },
|
||||
{ "date": "2026-08-30", "type": "short" },
|
||||
{ "date": "2026-09-05", "type": "long" },
|
||||
{ "date": "2026-09-06", "type": "short" }
|
||||
],
|
||||
"names": [
|
||||
|
||||
]
|
||||
"names": []
|
||||
}
|
||||
|
||||
@@ -22,6 +22,6 @@
|
||||
});
|
||||
|
||||
yesBtn.addEventListener("click", () => {
|
||||
window.location.href = "activity.html";
|
||||
window.location.href = "datetime.html";
|
||||
});
|
||||
})();
|
||||
|
||||
+10
-1
@@ -31,7 +31,15 @@
|
||||
});
|
||||
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")
|
||||
.then((res) => res.json())
|
||||
.then((settings) => {
|
||||
@@ -41,6 +49,7 @@
|
||||
}
|
||||
})
|
||||
.catch(() => {});
|
||||
}
|
||||
|
||||
checkoutBtn.addEventListener("click", () => {
|
||||
// Später: API / Mail-Versand / Kalendereintrag ausführen
|
||||
|
||||
+2
-2
@@ -16,10 +16,10 @@
|
||||
</div>
|
||||
|
||||
<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">
|
||||
<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>
|
||||
|
||||
<section class="calendar-card" aria-label="Kalender">
|
||||
|
||||
+17
-15
@@ -7,7 +7,6 @@
|
||||
"Juli", "August", "September", "Oktober", "November", "Dezember",
|
||||
];
|
||||
|
||||
const activitySummary = document.getElementById("activitySummary");
|
||||
const monthLabel = document.getElementById("monthLabel");
|
||||
const weekdayRow = document.getElementById("weekdayRow");
|
||||
const dayGrid = document.getElementById("dayGrid");
|
||||
@@ -15,21 +14,14 @@
|
||||
const nextMonthBtn = document.getElementById("nextMonthBtn");
|
||||
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();
|
||||
today.setHours(0, 0, 0, 0);
|
||||
|
||||
let availableDates = new Set();
|
||||
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");
|
||||
@@ -47,9 +39,16 @@
|
||||
fetch("appsettings.json")
|
||||
.then((response) => response.json())
|
||||
.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`))
|
||||
.filter((date) => date >= today)
|
||||
.sort((a, b) => a - b);
|
||||
@@ -82,12 +81,14 @@
|
||||
|
||||
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 isAvailable = availableDates.has(toDateKey(cellDate));
|
||||
const dateInfo = availableDatesMap.get(key);
|
||||
const isAvailable = Boolean(dateInfo);
|
||||
const isToday = cellDate.getTime() === today.getTime();
|
||||
const isSelected = selectedDate && cellDate.getTime() === selectedDate.getTime();
|
||||
|
||||
@@ -104,6 +105,7 @@
|
||||
|
||||
button.addEventListener("click", () => {
|
||||
selectedDate = cellDate;
|
||||
selectedDateType = dateInfo ? dateInfo.type : "long";
|
||||
renderCalendar();
|
||||
updateContinueState();
|
||||
});
|
||||
@@ -141,9 +143,9 @@
|
||||
if (!selectedDate) {
|
||||
return;
|
||||
}
|
||||
// Ablage für die spätere Anbindung an Datenbank/Storage
|
||||
localStorage.setItem("selectedDate", toDateKey(selectedDate));
|
||||
window.location.href = "name.html";
|
||||
localStorage.setItem("selectedDateType", selectedDateType);
|
||||
window.location.href = "activity.html";
|
||||
});
|
||||
|
||||
})();
|
||||
|
||||
Reference in New Issue
Block a user