feat: Implement user profile management and navigation updates
- Added a new profile page (my_profil.html) for users to manage their events and personal information. - Introduced a new CSS file (my_profil.css) for styling the profile page. - Created a JavaScript file (my_profil.js) to handle profile data retrieval, event registration management, and form submission. - Updated navigation logic (navigation.js) to dynamically display login/signup or event management links based on user authentication status. - Enhanced event creation and detail pages to support user-specific actions (registration/unregistration). - Improved login and signup processes to handle user data more robustly, including fallback user creation. - Refactored event overview to show user-specific events and registrations. - Added error handling and validation for user input in profile management.
This commit is contained in:
parent
46074df578
commit
1efa4dcd39
@ -281,6 +281,14 @@
|
|||||||
filter: brightness(0.95);
|
filter: brightness(0.95);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.btn-primary-own,
|
||||||
|
.btn-primary-own:disabled {
|
||||||
|
background: var(--olive-light);
|
||||||
|
color: var(--black);
|
||||||
|
opacity: 1;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
/* ---------------------------------------------------------
|
/* ---------------------------------------------------------
|
||||||
Overview Empty State
|
Overview Empty State
|
||||||
--------------------------------------------------------- */
|
--------------------------------------------------------- */
|
||||||
@ -693,6 +701,15 @@
|
|||||||
cursor: not-allowed;
|
cursor: not-allowed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.detail-primary-btn-own,
|
||||||
|
.detail-primary-btn-own:disabled {
|
||||||
|
border-color: var(--olive-light);
|
||||||
|
background: var(--olive-light);
|
||||||
|
color: var(--black);
|
||||||
|
opacity: 1;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* ---------------------------------------------------------
|
/* ---------------------------------------------------------
|
||||||
Responsive: Tablet (<= 850px)
|
Responsive: Tablet (<= 850px)
|
||||||
|
|||||||
251
css/my_profil.css
Normal file
251
css/my_profil.css
Normal file
@ -0,0 +1,251 @@
|
|||||||
|
.profile-page {
|
||||||
|
/* Reserve a large safe zone below sticky nav so title/actions are never covered. */
|
||||||
|
margin-top: 0;
|
||||||
|
padding-top: 6.5rem;
|
||||||
|
margin-bottom: var(--space-8);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Kopfbereich mit Titel und Logout-Aktion. */
|
||||||
|
.profile-hero {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: var(--space-4);
|
||||||
|
margin-bottom: var(--space-5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-kicker {
|
||||||
|
margin: 0;
|
||||||
|
color: var(--olive);
|
||||||
|
font-size: 1rem;
|
||||||
|
font-weight: 500;
|
||||||
|
letter-spacing: var(--ls-label);
|
||||||
|
}
|
||||||
|
|
||||||
|
#profile-headline {
|
||||||
|
margin: 0.4rem 0;
|
||||||
|
color: var(--brown);
|
||||||
|
font-size: clamp(2rem, 4.4vw, 2.8rem);
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-subline {
|
||||||
|
margin: 0;
|
||||||
|
max-width: 48rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-logout {
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
gap: var(--space-4);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Konsistentes Karten-Layout fuer alle Profilsektionen. */
|
||||||
|
.profile-panel {
|
||||||
|
background: rgba(255, 255, 255, 0.88);
|
||||||
|
border-radius: var(--radius-lg);
|
||||||
|
box-shadow: 0 3px 12px rgba(102, 52, 13, 0.1);
|
||||||
|
padding: var(--space-5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel-head {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: var(--space-3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel-title {
|
||||||
|
margin: 0;
|
||||||
|
color: var(--brown);
|
||||||
|
font-size: 1.8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel-count {
|
||||||
|
min-width: 2rem;
|
||||||
|
padding: 0.1rem 0.65rem;
|
||||||
|
border-radius: var(--radius-pill);
|
||||||
|
background: var(--olive-light);
|
||||||
|
color: var(--black);
|
||||||
|
font-size: 0.95rem;
|
||||||
|
font-weight: 600;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-card-list {
|
||||||
|
display: grid;
|
||||||
|
gap: var(--space-2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Einzelne Eventkarte fuer "Meine Events" und "Meine Anmeldungen". */
|
||||||
|
.profile-event-card {
|
||||||
|
border: 1px solid rgba(107, 107, 5, 0.25);
|
||||||
|
border-radius: var(--radius-md);
|
||||||
|
padding: var(--space-3);
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
gap: var(--space-3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-event-title {
|
||||||
|
margin: 0;
|
||||||
|
color: var(--black);
|
||||||
|
font-family: "Jost", sans-serif;
|
||||||
|
font-size: 1.25rem;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-event-meta {
|
||||||
|
margin: 0.3rem 0 0;
|
||||||
|
font-size: 0.95rem;
|
||||||
|
color: var(--olive);
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-event-link {
|
||||||
|
flex-shrink: 0;
|
||||||
|
color: var(--blue);
|
||||||
|
font-weight: 500;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-event-link:hover,
|
||||||
|
.profile-event-link:focus-visible {
|
||||||
|
text-decoration: underline;
|
||||||
|
text-underline-offset: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-event-actions {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: var(--space-2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-unregister-btn {
|
||||||
|
border: none;
|
||||||
|
border-radius: var(--radius-md);
|
||||||
|
background: var(--tomato);
|
||||||
|
color: var(--butter-light);
|
||||||
|
padding: 0.45rem 0.95rem;
|
||||||
|
font-family: "Jost", sans-serif;
|
||||||
|
font-size: 0.95rem;
|
||||||
|
font-weight: 500;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-unregister-btn:hover,
|
||||||
|
.profile-unregister-btn:focus-visible {
|
||||||
|
background: var(--tomato-dark);
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-empty {
|
||||||
|
margin: 0;
|
||||||
|
color: var(--black);
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
gap: var(--space-3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-group {
|
||||||
|
margin-bottom: var(--space-3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-group label {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 0.35rem;
|
||||||
|
font-size: 0.95rem;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-group input {
|
||||||
|
width: 100%;
|
||||||
|
border: 1px solid #d8d8d8;
|
||||||
|
border-radius: var(--radius-sm);
|
||||||
|
background: var(--white);
|
||||||
|
padding: 0.7rem 0.85rem;
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-group input:focus {
|
||||||
|
outline: 2px solid rgba(107, 107, 5, 0.35);
|
||||||
|
outline-offset: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-hint {
|
||||||
|
margin: 0.4rem 0 0;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
color: #535353;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-error {
|
||||||
|
margin-top: 0.35rem;
|
||||||
|
color: var(--error);
|
||||||
|
font-size: 0.85rem;
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-group.has-error .input-error {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-group.has-error input {
|
||||||
|
border-color: var(--error);
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-feedback {
|
||||||
|
margin: 0.75rem 0 0;
|
||||||
|
font-size: 0.95rem;
|
||||||
|
color: var(--olive);
|
||||||
|
min-height: 1.3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-cta-row {
|
||||||
|
display: flex;
|
||||||
|
gap: var(--space-2);
|
||||||
|
margin-top: var(--space-3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-button-secondary {
|
||||||
|
background: var(--tomato);
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-button-secondary:hover {
|
||||||
|
background: var(--tomato-dark);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 48rem) {
|
||||||
|
.profile-page {
|
||||||
|
padding-top: 5.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-hero {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: stretch;
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-logout {
|
||||||
|
width: max-content;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-grid {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-event-card {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.profile-event-actions {
|
||||||
|
width: 100%;
|
||||||
|
justify-content: flex-start;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -210,6 +210,25 @@ p {
|
|||||||
color: var(--butter-light);
|
color: var(--butter-light);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Auth-Links in ausgeloggter Navigation: klarer Aktiv-/Default-Zustand. */
|
||||||
|
.auth-nav-button--default {
|
||||||
|
background: transparent;
|
||||||
|
color: var(--olive);
|
||||||
|
border: 2px solid var(--olive);
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-nav-button--default:hover,
|
||||||
|
.auth-nav-button--default:focus-visible {
|
||||||
|
background: var(--olive-light);
|
||||||
|
color: var(--black);
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-nav-button--active {
|
||||||
|
background: var(--olive);
|
||||||
|
color: var(--butter-light);
|
||||||
|
border: 2px solid var(--olive);
|
||||||
|
}
|
||||||
|
|
||||||
.profile-pill {
|
.profile-pill {
|
||||||
width: 2.375rem;
|
width: 2.375rem;
|
||||||
height: 2.375rem;
|
height: 2.375rem;
|
||||||
|
|||||||
@ -9,6 +9,7 @@
|
|||||||
<link rel="stylesheet" href="css/event_create.css" />
|
<link rel="stylesheet" href="css/event_create.css" />
|
||||||
<!-- Globales Stylesheet -->
|
<!-- Globales Stylesheet -->
|
||||||
<link rel="stylesheet" href="css/stylesheet_global.css">
|
<link rel="stylesheet" href="css/stylesheet_global.css">
|
||||||
|
<script src="js/navigation.js" defer></script>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
@ -19,9 +20,7 @@
|
|||||||
<img src="assets/logo_invite.svg" alt="Invite Logo">
|
<img src="assets/logo_invite.svg" alt="Invite Logo">
|
||||||
</a>
|
</a>
|
||||||
<nav class="nav-tab-links" aria-label="Hauptnavigation">
|
<nav class="nav-tab-links" aria-label="Hauptnavigation">
|
||||||
<a class="nav-tab" href="event_overview.html">Event finden</a>
|
<a class="button-small" href="login.html" aria-label="Login">Login</a>
|
||||||
<a class="nav-tab" href="event_create.html">Event erstellen</a>
|
|
||||||
<a class="button-small" href="login.html" aria-label="Profil">Login</a>
|
|
||||||
</nav>
|
</nav>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|||||||
@ -9,6 +9,7 @@
|
|||||||
<link rel="stylesheet" href="css/event_overview.css">
|
<link rel="stylesheet" href="css/event_overview.css">
|
||||||
<!-- Globales Stylesheet -->
|
<!-- Globales Stylesheet -->
|
||||||
<link rel="stylesheet" href="css/stylesheet_global.css">
|
<link rel="stylesheet" href="css/stylesheet_global.css">
|
||||||
|
<script src="js/navigation.js" defer></script>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
@ -19,9 +20,7 @@
|
|||||||
<img src="assets/logo_invite.svg" alt="Invite Logo">
|
<img src="assets/logo_invite.svg" alt="Invite Logo">
|
||||||
</a>
|
</a>
|
||||||
<nav class="nav-tab-links" aria-label="Hauptnavigation">
|
<nav class="nav-tab-links" aria-label="Hauptnavigation">
|
||||||
<a class="nav-tab" href="event_overview.html">Event finden</a>
|
<a class="button-small" href="login.html" aria-label="Login">Login</a>
|
||||||
<a class="nav-tab" href="event_create.html">Event erstellen</a>
|
|
||||||
<a class="button-small" href="login.html" aria-label="Profil">Login</a>
|
|
||||||
</nav>
|
</nav>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|||||||
@ -9,6 +9,7 @@
|
|||||||
<link rel="stylesheet" href="css/event_overview.css">
|
<link rel="stylesheet" href="css/event_overview.css">
|
||||||
<!-- Globales Stylesheet -->
|
<!-- Globales Stylesheet -->
|
||||||
<link rel="stylesheet" href="css/stylesheet_global.css">
|
<link rel="stylesheet" href="css/stylesheet_global.css">
|
||||||
|
<script src="js/navigation.js" defer></script>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
@ -19,9 +20,7 @@
|
|||||||
<img src="assets/logo_invite.svg" alt="Invite Logo">
|
<img src="assets/logo_invite.svg" alt="Invite Logo">
|
||||||
</a>
|
</a>
|
||||||
<nav class="nav-tab-links" aria-label="Hauptnavigation">
|
<nav class="nav-tab-links" aria-label="Hauptnavigation">
|
||||||
<a class="nav-tab" href="event_overview.html">Event finden</a>
|
<a class="button-small" href="login.html" aria-label="Login">Login</a>
|
||||||
<a class="nav-tab" href="event_create.html">Event erstellen</a>
|
|
||||||
<a class="button-small" href="login.html" aria-label="Profil">Login</a>
|
|
||||||
</nav>
|
</nav>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|||||||
@ -9,6 +9,7 @@
|
|||||||
<link rel="stylesheet" href="css/landingpage.css?v=2" />
|
<link rel="stylesheet" href="css/landingpage.css?v=2" />
|
||||||
<!-- Globales Stylesheet -->
|
<!-- Globales Stylesheet -->
|
||||||
<link rel="stylesheet" href="css/stylesheet_global.css?v=2">
|
<link rel="stylesheet" href="css/stylesheet_global.css?v=2">
|
||||||
|
<script src="js/navigation.js" defer></script>
|
||||||
|
|
||||||
<!-- Font Awesome
|
<!-- Font Awesome
|
||||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.0/css/all.min.css" integrity="sha512-dU7ZrF1pFq5kVnPzlV9+04YhARzNjCX5Q5P1shgMpuN4s5I8mI8QD4981h7kYtV7sSgNldR0z5pZW5bS2ZpY3Q==" crossorigin="anonymous" referrerpolicy="no-referrer" /> -->
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.0/css/all.min.css" integrity="sha512-dU7ZrF1pFq5kVnPzlV9+04YhARzNjCX5Q5P1shgMpuN4s5I8mI8QD4981h7kYtV7sSgNldR0z5pZW5bS2ZpY3Q==" crossorigin="anonymous" referrerpolicy="no-referrer" /> -->
|
||||||
@ -22,9 +23,8 @@
|
|||||||
<img src="assets/logo_invite.svg" alt="Invite Logo">
|
<img src="assets/logo_invite.svg" alt="Invite Logo">
|
||||||
</a>
|
</a>
|
||||||
<nav class="nav-tab-links" aria-label="Hauptnavigation">
|
<nav class="nav-tab-links" aria-label="Hauptnavigation">
|
||||||
<a class="nav-tab" href="event_overview.html">Event finden</a>
|
<a class="button-small auth-nav-button auth-nav-button--default" href="login.html" aria-label="Login">Login</a>
|
||||||
<a class="nav-tab" href="event_create.html">Event erstellen</a>
|
<a class="button-small auth-nav-button auth-nav-button--default" href="signup.html" aria-label="Signup">Signup</a>
|
||||||
<a class="button-small" href="login.html" aria-label="Profil">Login</a>
|
|
||||||
</nav>
|
</nav>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|||||||
@ -15,6 +15,7 @@ const usernameElement = document.getElementById("username");
|
|||||||
const flowFooter = document.getElementById("flowFooter");
|
const flowFooter = document.getElementById("flowFooter");
|
||||||
const submissionSuccess = document.getElementById("submissionSuccess");
|
const submissionSuccess = document.getElementById("submissionSuccess");
|
||||||
const EVENTS_STORAGE_KEY = "socialCookingEvents";
|
const EVENTS_STORAGE_KEY = "socialCookingEvents";
|
||||||
|
const CURRENT_USER_KEY = "socialCookingCurrentUser";
|
||||||
|
|
||||||
// =============================
|
// =============================
|
||||||
// STATE: aktueller Schritt im Flow
|
// STATE: aktueller Schritt im Flow
|
||||||
@ -36,8 +37,20 @@ const nextLabels = {
|
|||||||
7: "Event veröffentlichen"
|
7: "Event veröffentlichen"
|
||||||
};
|
};
|
||||||
|
|
||||||
// Demo-Wert: Später könnte der Name z. B. aus einem User-Profil kommen
|
// Liest den aktiven Benutzer aus localStorage und setzt den Anzeigenamen im Header.
|
||||||
usernameElement.textContent = "Mia";
|
function getCurrentUser() {
|
||||||
|
try {
|
||||||
|
const raw = localStorage.getItem(CURRENT_USER_KEY);
|
||||||
|
return raw ? JSON.parse(raw) : null;
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Aktueller Benutzer konnte nicht gelesen werden:", error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentUser = getCurrentUser();
|
||||||
|
const displayName = currentUser?.vorname?.trim() || "Mia";
|
||||||
|
usernameElement.textContent = displayName;
|
||||||
|
|
||||||
|
|
||||||
// =============================
|
// =============================
|
||||||
@ -409,6 +422,7 @@ function buildStoredEvent() {
|
|||||||
name: usernameElement.textContent.trim() || "Host",
|
name: usernameElement.textContent.trim() || "Host",
|
||||||
initial: (usernameElement.textContent.trim().charAt(0) || "H").toUpperCase()
|
initial: (usernameElement.textContent.trim().charAt(0) || "H").toUpperCase()
|
||||||
},
|
},
|
||||||
|
hostEmail: currentUser?.email || "",
|
||||||
hostMessage: [eventDescription],
|
hostMessage: [eventDescription],
|
||||||
menu: buildMenuItems(menuDescription),
|
menu: buildMenuItems(menuDescription),
|
||||||
specifications: getCheckboxValues("allergies") === "Keine Angabe"
|
specifications: getCheckboxValues("allergies") === "Keine Angabe"
|
||||||
|
|||||||
@ -1,10 +1,13 @@
|
|||||||
document.addEventListener('DOMContentLoaded', async () => {
|
document.addEventListener('DOMContentLoaded', async () => {
|
||||||
const EVENTS_STORAGE_KEY = 'socialCookingEvents';
|
const EVENTS_STORAGE_KEY = 'socialCookingEvents';
|
||||||
|
const CURRENT_USER_KEY = 'socialCookingCurrentUser';
|
||||||
|
const REGISTRATION_STORAGE_KEY = 'socialCookingRegistrations';
|
||||||
// -------------------------------------------------------------
|
// -------------------------------------------------------------
|
||||||
// DOM entry point and shared asset path.
|
// DOM entry point and shared asset path.
|
||||||
// -------------------------------------------------------------
|
// -------------------------------------------------------------
|
||||||
const detailContainer = document.getElementById('detail-view');
|
const detailContainer = document.getElementById('detail-view');
|
||||||
const locationIconPath = 'assets/location-pin.svg';
|
const locationIconPath = 'assets/location-pin.svg';
|
||||||
|
const currentUser = getCurrentUser();
|
||||||
|
|
||||||
// Read event id from query string (detail page deep-link support).
|
// Read event id from query string (detail page deep-link support).
|
||||||
const params = new URLSearchParams(window.location.search);
|
const params = new URLSearchParams(window.location.search);
|
||||||
@ -25,6 +28,49 @@ document.addEventListener('DOMContentLoaded', async () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getCurrentUser() {
|
||||||
|
try {
|
||||||
|
const stored = localStorage.getItem(CURRENT_USER_KEY);
|
||||||
|
return stored ? JSON.parse(stored) : null;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Aktueller Benutzer konnte nicht gelesen werden.', error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getRegistrationMap() {
|
||||||
|
try {
|
||||||
|
const stored = localStorage.getItem(REGISTRATION_STORAGE_KEY);
|
||||||
|
return stored ? JSON.parse(stored) : {};
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Anmeldedaten konnten nicht gelesen werden.', error);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function setRegistrationMap(registrationMap) {
|
||||||
|
localStorage.setItem(REGISTRATION_STORAGE_KEY, JSON.stringify(registrationMap));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ermittelt, ob das Event vom aktuell eingeloggten Benutzer erstellt wurde.
|
||||||
|
function isEventOwnedByCurrentUser(event, user) {
|
||||||
|
if (!event || !user) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const userEmail = String(user.email || '').trim().toLowerCase();
|
||||||
|
const hostEmail = String(event.hostEmail || '').trim().toLowerCase();
|
||||||
|
|
||||||
|
if (userEmail && hostEmail) {
|
||||||
|
return userEmail === hostEmail;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback fuer aeltere Datensaetze ohne hostEmail.
|
||||||
|
const userFirstName = String(user.vorname || '').trim().toLowerCase();
|
||||||
|
const hostName = String(event.host?.name || '').trim().toLowerCase();
|
||||||
|
return Boolean(userFirstName && hostName && userFirstName === hostName);
|
||||||
|
}
|
||||||
|
|
||||||
// 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');
|
||||||
@ -116,6 +162,14 @@ document.addEventListener('DOMContentLoaded', async () => {
|
|||||||
const confirmedGuests = participants.length;
|
const confirmedGuests = participants.length;
|
||||||
const freePlaces = Math.max(0, totalGuests - confirmedGuests);
|
const freePlaces = Math.max(0, totalGuests - confirmedGuests);
|
||||||
const isFull = freePlaces === 0;
|
const isFull = freePlaces === 0;
|
||||||
|
const isOwnEvent = isEventOwnedByCurrentUser(event, currentUser);
|
||||||
|
const registrationMap = getRegistrationMap();
|
||||||
|
const userRegistrations = currentUser?.email && Array.isArray(registrationMap[currentUser.email])
|
||||||
|
? registrationMap[currentUser.email].map(id => Number(id))
|
||||||
|
: [];
|
||||||
|
const isRegistered = userRegistrations.includes(Number(event.id));
|
||||||
|
const actionButtonLabel = isOwnEvent ? 'Dein Event' : !currentUser ? 'Einloggen' : isRegistered ? 'Abmelden' : 'Anmelden';
|
||||||
|
const actionButtonDisabled = isOwnEvent || (!isRegistered && isFull);
|
||||||
const detailChips = [
|
const detailChips = [
|
||||||
`<span class="event-tag">${eventCategory}</span>`,
|
`<span class="event-tag">${eventCategory}</span>`,
|
||||||
`<span class="event-tag">${dietLabel}</span>`,
|
`<span class="event-tag">${dietLabel}</span>`,
|
||||||
@ -200,8 +254,8 @@ document.addEventListener('DOMContentLoaded', async () => {
|
|||||||
<span class="detail-spots-pill${isFull ? ' detail-spots-pill-full' : ''}">
|
<span class="detail-spots-pill${isFull ? ' detail-spots-pill-full' : ''}">
|
||||||
${isFull ? 'AUSGEBUCHT' : `${freePlaces} Plaetze frei`}
|
${isFull ? 'AUSGEBUCHT' : `${freePlaces} Plaetze frei`}
|
||||||
</span>
|
</span>
|
||||||
<button class="detail-primary-btn" type="button" ${isFull ? 'disabled' : ''}>
|
<button class="detail-primary-btn${isOwnEvent ? ' detail-primary-btn-own' : ''}" type="button" data-register-button ${actionButtonDisabled ? 'disabled' : ''}>
|
||||||
Anmelden
|
${actionButtonLabel}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
@ -224,6 +278,39 @@ document.addEventListener('DOMContentLoaded', async () => {
|
|||||||
const lightboxImage = detailContainer.querySelector('.detail-lightbox-image');
|
const lightboxImage = detailContainer.querySelector('.detail-lightbox-image');
|
||||||
const lightboxClose = detailContainer.querySelector('.detail-lightbox-close');
|
const lightboxClose = detailContainer.querySelector('.detail-lightbox-close');
|
||||||
const galleryButtons = detailContainer.querySelectorAll('.detail-gallery-item');
|
const galleryButtons = detailContainer.querySelectorAll('.detail-gallery-item');
|
||||||
|
const registerButton = detailContainer.querySelector('[data-register-button]');
|
||||||
|
|
||||||
|
// Anmeldung toggeln und im lokalen Registrierungs-Store persistieren.
|
||||||
|
if (registerButton) {
|
||||||
|
registerButton.addEventListener('click', () => {
|
||||||
|
if (isOwnEvent) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!currentUser || !currentUser.email) {
|
||||||
|
window.location.href = 'login.html';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const nextRegistrationMap = getRegistrationMap();
|
||||||
|
const currentList = Array.isArray(nextRegistrationMap[currentUser.email])
|
||||||
|
? nextRegistrationMap[currentUser.email].map(id => Number(id))
|
||||||
|
: [];
|
||||||
|
const registrationSet = new Set(currentList);
|
||||||
|
|
||||||
|
if (registrationSet.has(Number(event.id))) {
|
||||||
|
registrationSet.delete(Number(event.id));
|
||||||
|
} else if (!isFull) {
|
||||||
|
registrationSet.add(Number(event.id));
|
||||||
|
}
|
||||||
|
|
||||||
|
nextRegistrationMap[currentUser.email] = Array.from(registrationSet);
|
||||||
|
setRegistrationMap(nextRegistrationMap);
|
||||||
|
|
||||||
|
// Re-Render aktualisiert Buttonzustand und CTA ohne Seitenreload.
|
||||||
|
renderDetailPage(event);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Central close helper to keep all close paths consistent.
|
// Central close helper to keep all close paths consistent.
|
||||||
function closeLightbox() {
|
function closeLightbox() {
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
document.addEventListener('DOMContentLoaded', () => {
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
const EVENTS_STORAGE_KEY = 'socialCookingEvents';
|
const EVENTS_STORAGE_KEY = 'socialCookingEvents';
|
||||||
|
const CURRENT_USER_KEY = 'socialCookingCurrentUser';
|
||||||
// -------------------------------------------------------------
|
// -------------------------------------------------------------
|
||||||
// DOM references used throughout the page lifecycle.
|
// DOM references used throughout the page lifecycle.
|
||||||
// -------------------------------------------------------------
|
// -------------------------------------------------------------
|
||||||
@ -14,6 +15,35 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
// -------------------------------------------------------------
|
// -------------------------------------------------------------
|
||||||
let allEvents = [];
|
let allEvents = [];
|
||||||
let activeCategory = 'ALLE';
|
let activeCategory = 'ALLE';
|
||||||
|
const currentUser = getCurrentUser();
|
||||||
|
|
||||||
|
function getCurrentUser() {
|
||||||
|
try {
|
||||||
|
const stored = localStorage.getItem(CURRENT_USER_KEY);
|
||||||
|
return stored ? JSON.parse(stored) : null;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Aktueller Benutzer konnte nicht gelesen werden.', error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prueft, ob ein Event dem aktuellen Benutzer gehoert.
|
||||||
|
function isEventOwnedByCurrentUser(event, user) {
|
||||||
|
if (!event || !user) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const userEmail = String(user.email || '').trim().toLowerCase();
|
||||||
|
const hostEmail = String(event.hostEmail || '').trim().toLowerCase();
|
||||||
|
|
||||||
|
if (userEmail && hostEmail) {
|
||||||
|
return userEmail === hostEmail;
|
||||||
|
}
|
||||||
|
|
||||||
|
const userFirstName = String(user.vorname || '').trim().toLowerCase();
|
||||||
|
const hostName = String(event.host?.name || '').trim().toLowerCase();
|
||||||
|
return Boolean(userFirstName && hostName && userFirstName === hostName);
|
||||||
|
}
|
||||||
|
|
||||||
function getStoredEvents() {
|
function getStoredEvents() {
|
||||||
try {
|
try {
|
||||||
@ -225,12 +255,19 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
const totalCapacity = event.spots;
|
const totalCapacity = event.spots;
|
||||||
const freePlaces = Math.max(0, totalCapacity - bookedSeats);
|
const freePlaces = Math.max(0, totalCapacity - bookedSeats);
|
||||||
const isFull = freePlaces === 0;
|
const isFull = freePlaces === 0;
|
||||||
|
const isOwnEvent = isEventOwnedByCurrentUser(event, currentUser);
|
||||||
|
|
||||||
// Build optional specification chips only when data exists.
|
// Build optional specification chips only when data exists.
|
||||||
const specsChips = event.specifications && event.specifications.length > 0
|
const specsChips = event.specifications && event.specifications.length > 0
|
||||||
? event.specifications.map(spec => `<span class="event-tag">${spec}</span>`).join('')
|
? event.specifications.map(spec => `<span class="event-tag">${spec}</span>`).join('')
|
||||||
: '';
|
: '';
|
||||||
|
|
||||||
|
const actionMarkup = isOwnEvent
|
||||||
|
? '<button class="btn-primary btn-primary-own" type="button" disabled>Dein Event</button>'
|
||||||
|
: isFull
|
||||||
|
? ''
|
||||||
|
: '<button class="btn-primary" type="button">Anmelden</button>';
|
||||||
|
|
||||||
card.innerHTML = `
|
card.innerHTML = `
|
||||||
<div class="event-main">
|
<div class="event-main">
|
||||||
<div class="event-top-row">
|
<div class="event-top-row">
|
||||||
@ -249,7 +286,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
</div>
|
</div>
|
||||||
<div class="event-side${isFull ? ' event-side-full' : ''}">
|
<div class="event-side${isFull ? ' event-side-full' : ''}">
|
||||||
<span class="event-spots${isFull ? ' event-spots-full' : ''}">${isFull ? 'AUSGEBUCHT' : `${freePlaces} Plätze FREI`}</span>
|
<span class="event-spots${isFull ? ' event-spots-full' : ''}">${isFull ? 'AUSGEBUCHT' : `${freePlaces} Plätze FREI`}</span>
|
||||||
${isFull ? '' : '<button class="btn-primary" type="button">Anmelden</button>'}
|
${actionMarkup}
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
|||||||
52
js/login.js
52
js/login.js
@ -4,6 +4,42 @@ const passwortInput = document.getElementById('passwort');
|
|||||||
const emailError = document.getElementById('emailError');
|
const emailError = document.getElementById('emailError');
|
||||||
const passwortError = document.getElementById('passwortError');
|
const passwortError = document.getElementById('passwortError');
|
||||||
|
|
||||||
|
const USERS_STORAGE_KEY = 'socialCookingUsers';
|
||||||
|
const CURRENT_USER_KEY = 'socialCookingCurrentUser';
|
||||||
|
|
||||||
|
// Liest alle registrierten Benutzer robust aus localStorage.
|
||||||
|
function getStoredUsers() {
|
||||||
|
try {
|
||||||
|
const raw = localStorage.getItem(USERS_STORAGE_KEY);
|
||||||
|
return raw ? JSON.parse(raw) : [];
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Benutzerdaten konnten nicht gelesen werden.', error);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Speichert den aktiven Benutzer fuer nachfolgende Seiten.
|
||||||
|
function setCurrentUser(user) {
|
||||||
|
localStorage.setItem(CURRENT_USER_KEY, JSON.stringify(user));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Erstellt einen Demo-Benutzer, falls fuer die E-Mail noch kein Account existiert.
|
||||||
|
function createFallbackUser(email, passwort) {
|
||||||
|
const localPart = email.split('@')[0] || 'Gast';
|
||||||
|
const normalized = localPart.replace(/[._-]/g, ' ').trim();
|
||||||
|
const guessedVorname = normalized ? normalized.split(' ')[0] : 'Gast';
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: Date.now(),
|
||||||
|
vorname: guessedVorname.charAt(0).toUpperCase() + guessedVorname.slice(1),
|
||||||
|
nachname: '',
|
||||||
|
email,
|
||||||
|
passwort,
|
||||||
|
createdAt: new Date().toISOString(),
|
||||||
|
source: 'login-fallback'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// Validierungsfunktion
|
// Validierungsfunktion
|
||||||
function validateForm(event) {
|
function validateForm(event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
@ -43,11 +79,21 @@ function validateForm(event) {
|
|||||||
passwortGroup.classList.remove('has-error');
|
passwortGroup.classList.remove('has-error');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wenn alle Validierungen bestanden, Form absenden
|
// Wenn alle Validierungen bestanden, Benutzer pruefen und Session speichern.
|
||||||
if (isValid) {
|
if (isValid) {
|
||||||
//alert('Login erfolgreich! (Dies ist eine Demo)');
|
const users = getStoredUsers();
|
||||||
|
const matchedUser = users.find(user => user.email?.toLowerCase() === emailValue.toLowerCase());
|
||||||
|
|
||||||
// Weiterleitung zur event overview Page
|
if (matchedUser && matchedUser.passwort !== passwortValue) {
|
||||||
|
passwortGroup.classList.add('has-error');
|
||||||
|
passwortError.textContent = 'Das Passwort ist nicht korrekt.';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const userToLogin = matchedUser || createFallbackUser(emailValue, passwortValue);
|
||||||
|
setCurrentUser(userToLogin);
|
||||||
|
|
||||||
|
// Weiterleitung zur Event-Overview-Seite.
|
||||||
window.location.href = 'event_overview.html';
|
window.location.href = 'event_overview.html';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
367
js/my_profil.js
Normal file
367
js/my_profil.js
Normal file
@ -0,0 +1,367 @@
|
|||||||
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
|
const EVENTS_STORAGE_KEY = 'socialCookingEvents';
|
||||||
|
const USERS_STORAGE_KEY = 'socialCookingUsers';
|
||||||
|
const CURRENT_USER_KEY = 'socialCookingCurrentUser';
|
||||||
|
const REGISTRATION_STORAGE_KEY = 'socialCookingRegistrations';
|
||||||
|
|
||||||
|
// Zentrale DOM-Referenzen fuer klare, testbare Funktionen.
|
||||||
|
const loggedOutState = document.getElementById('logged-out-state');
|
||||||
|
const loggedInContent = document.getElementById('logged-in-content');
|
||||||
|
const profileHeadline = document.getElementById('profile-headline');
|
||||||
|
const profileSubline = document.getElementById('profile-subline');
|
||||||
|
const logoutButton = document.getElementById('logout-button');
|
||||||
|
|
||||||
|
const myEventsCount = document.getElementById('my-events-count');
|
||||||
|
const myRegistrationsCount = document.getElementById('my-registrations-count');
|
||||||
|
const myEventsList = document.getElementById('my-events-list');
|
||||||
|
const myRegistrationsList = document.getElementById('my-registrations-list');
|
||||||
|
|
||||||
|
const profileForm = document.getElementById('profile-form');
|
||||||
|
const profileFeedback = document.getElementById('profile-feedback');
|
||||||
|
const vornameInput = document.getElementById('vorname');
|
||||||
|
const nachnameInput = document.getElementById('nachname');
|
||||||
|
const emailInput = document.getElementById('email');
|
||||||
|
const passwortInput = document.getElementById('passwort');
|
||||||
|
|
||||||
|
let currentUser = getCurrentUser();
|
||||||
|
let allEvents = [];
|
||||||
|
|
||||||
|
init();
|
||||||
|
|
||||||
|
async function init() {
|
||||||
|
if (!currentUser) {
|
||||||
|
renderLoggedOutState();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
renderLoggedInState(currentUser);
|
||||||
|
bindFormHandlers();
|
||||||
|
|
||||||
|
allEvents = await loadAllEvents();
|
||||||
|
renderMyEvents(allEvents, currentUser);
|
||||||
|
renderMyRegistrations(allEvents, currentUser);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Liest den aktuell eingeloggten Benutzer robust aus dem Storage.
|
||||||
|
function getCurrentUser() {
|
||||||
|
try {
|
||||||
|
const raw = localStorage.getItem(CURRENT_USER_KEY);
|
||||||
|
return raw ? JSON.parse(raw) : null;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Der aktuelle Benutzer konnte nicht geladen werden.', error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Liest lokal erstellte Events aus dem Storage.
|
||||||
|
function getStoredEvents() {
|
||||||
|
try {
|
||||||
|
const raw = localStorage.getItem(EVENTS_STORAGE_KEY);
|
||||||
|
return raw ? JSON.parse(raw) : [];
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Lokale Events konnten nicht gelesen werden.', error);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Liest den Anmeldestatus pro Benutzer-E-Mail.
|
||||||
|
function getRegistrationMap() {
|
||||||
|
try {
|
||||||
|
const raw = localStorage.getItem(REGISTRATION_STORAGE_KEY);
|
||||||
|
return raw ? JSON.parse(raw) : {};
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Anmeldedaten konnten nicht gelesen werden.', error);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Schreibt den gesamten Registrierungszustand in localStorage.
|
||||||
|
function setRegistrationMap(registrationMap) {
|
||||||
|
localStorage.setItem(REGISTRATION_STORAGE_KEY, JSON.stringify(registrationMap));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fuehrt JSON-Daten und lokal erstellte Events in einer Liste zusammen.
|
||||||
|
async function loadAllEvents() {
|
||||||
|
try {
|
||||||
|
const response = await fetch('data/events.json');
|
||||||
|
const apiEvents = await response.json();
|
||||||
|
return [...getStoredEvents(), ...apiEvents];
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Events konnten nicht geladen werden.', error);
|
||||||
|
return getStoredEvents();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Schaltet in den ausgeloggten Zustand und blendet geschuetzte Inhalte aus.
|
||||||
|
function renderLoggedOutState() {
|
||||||
|
loggedOutState.classList.remove('hidden');
|
||||||
|
loggedInContent.classList.add('hidden');
|
||||||
|
logoutButton.classList.add('hidden');
|
||||||
|
profileHeadline.textContent = 'Mein Profil';
|
||||||
|
profileSubline.textContent = 'Bitte logge dich ein, um deinen Bereich zu sehen.';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fuellt Ueberschriften und Formular mit den aktuellen Benutzerdaten.
|
||||||
|
function renderLoggedInState(user) {
|
||||||
|
loggedOutState.classList.add('hidden');
|
||||||
|
loggedInContent.classList.remove('hidden');
|
||||||
|
logoutButton.classList.remove('hidden');
|
||||||
|
|
||||||
|
profileHeadline.textContent = `Hallo ${user.vorname || 'Gast'}`;
|
||||||
|
profileSubline.textContent = 'Hier kannst du deine Events und Anmeldungen verwalten.';
|
||||||
|
|
||||||
|
vornameInput.value = user.vorname || '';
|
||||||
|
nachnameInput.value = user.nachname || '';
|
||||||
|
emailInput.value = user.email || '';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bindet Submit-, Input- und Logout-Verhalten an die Profilseite.
|
||||||
|
function bindFormHandlers() {
|
||||||
|
profileForm.addEventListener('submit', handleProfileSubmit);
|
||||||
|
myRegistrationsList.addEventListener('click', handleRegistrationListClick);
|
||||||
|
|
||||||
|
[vornameInput, nachnameInput, emailInput, passwortInput].forEach(input => {
|
||||||
|
input.addEventListener('input', () => {
|
||||||
|
input.parentElement.classList.remove('has-error');
|
||||||
|
profileFeedback.textContent = '';
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
logoutButton.addEventListener('click', () => {
|
||||||
|
localStorage.removeItem(CURRENT_USER_KEY);
|
||||||
|
window.location.href = 'login.html';
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reagiert auf Aktionen in der Liste "Meine Anmeldungen" per Event Delegation.
|
||||||
|
function handleRegistrationListClick(event) {
|
||||||
|
const target = event.target;
|
||||||
|
if (!(target instanceof HTMLElement)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const unregisterButton = target.closest('[data-unregister-id]');
|
||||||
|
if (!unregisterButton || !currentUser?.email) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const eventId = Number(unregisterButton.getAttribute('data-unregister-id'));
|
||||||
|
if (!Number.isFinite(eventId)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
unregisterFromEvent(eventId, currentUser.email);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Entfernt eine Event-ID aus der Benutzerliste und aktualisiert die UI sofort.
|
||||||
|
function unregisterFromEvent(eventId, userEmail) {
|
||||||
|
const registrationMap = getRegistrationMap();
|
||||||
|
const currentIds = Array.isArray(registrationMap[userEmail]) ? registrationMap[userEmail] : [];
|
||||||
|
const nextIds = currentIds
|
||||||
|
.map(id => Number(id))
|
||||||
|
.filter(id => Number.isFinite(id) && id !== eventId);
|
||||||
|
|
||||||
|
registrationMap[userEmail] = nextIds;
|
||||||
|
setRegistrationMap(registrationMap);
|
||||||
|
|
||||||
|
renderMyRegistrations(allEvents, currentUser);
|
||||||
|
profileFeedback.textContent = 'Du wurdest von dem Event abgemeldet.';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validiert Profildaten konsistent und liefert true/false zur Submit-Steuerung.
|
||||||
|
function validateProfileForm() {
|
||||||
|
let isValid = true;
|
||||||
|
|
||||||
|
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||||
|
|
||||||
|
if (!vornameInput.value.trim()) {
|
||||||
|
vornameInput.parentElement.classList.add('has-error');
|
||||||
|
isValid = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!nachnameInput.value.trim()) {
|
||||||
|
nachnameInput.parentElement.classList.add('has-error');
|
||||||
|
isValid = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!emailRegex.test(emailInput.value.trim())) {
|
||||||
|
emailInput.parentElement.classList.add('has-error');
|
||||||
|
isValid = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (passwortInput.value && passwortInput.value.length < 6) {
|
||||||
|
passwortInput.parentElement.classList.add('has-error');
|
||||||
|
isValid = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return isValid;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Speichert Profilaenderungen lokal und synchronisiert auch den Benutzerkatalog.
|
||||||
|
function handleProfileSubmit(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
if (!validateProfileForm()) {
|
||||||
|
profileFeedback.textContent = 'Bitte pruefe die markierten Felder.';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const previousEmail = currentUser.email;
|
||||||
|
const nextUser = {
|
||||||
|
...currentUser,
|
||||||
|
vorname: vornameInput.value.trim(),
|
||||||
|
nachname: nachnameInput.value.trim(),
|
||||||
|
email: emailInput.value.trim(),
|
||||||
|
passwort: passwortInput.value ? passwortInput.value : currentUser.passwort,
|
||||||
|
updatedAt: new Date().toISOString()
|
||||||
|
};
|
||||||
|
|
||||||
|
currentUser = nextUser;
|
||||||
|
localStorage.setItem(CURRENT_USER_KEY, JSON.stringify(nextUser));
|
||||||
|
syncUserInUserStore(previousEmail, nextUser);
|
||||||
|
|
||||||
|
// Falls sich die E-Mail geaendert hat, verschieben wir bestehende Anmeldungen auf die neue E-Mail.
|
||||||
|
migrateRegistrationEmail(previousEmail, nextUser.email);
|
||||||
|
|
||||||
|
passwortInput.value = '';
|
||||||
|
profileHeadline.textContent = `Hallo ${nextUser.vorname}`;
|
||||||
|
profileFeedback.textContent = 'Profil erfolgreich gespeichert.';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Synchronisiert einen Benutzer im zentralen User-Array.
|
||||||
|
function syncUserInUserStore(previousEmail, nextUser) {
|
||||||
|
let users = [];
|
||||||
|
|
||||||
|
try {
|
||||||
|
const raw = localStorage.getItem(USERS_STORAGE_KEY);
|
||||||
|
users = raw ? JSON.parse(raw) : [];
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Benutzerdaten konnten nicht gelesen werden.', error);
|
||||||
|
}
|
||||||
|
|
||||||
|
const nextUsers = users.filter(user => user.email !== previousEmail && user.email !== nextUser.email);
|
||||||
|
nextUsers.unshift(nextUser);
|
||||||
|
localStorage.setItem(USERS_STORAGE_KEY, JSON.stringify(nextUsers));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Migriert bestehende Registrierungen, falls die E-Mail aktualisiert wurde.
|
||||||
|
function migrateRegistrationEmail(previousEmail, nextEmail) {
|
||||||
|
if (!previousEmail || !nextEmail || previousEmail === nextEmail) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const map = getRegistrationMap();
|
||||||
|
const existingRegistrations = Array.isArray(map[previousEmail]) ? map[previousEmail] : [];
|
||||||
|
const alreadyPresent = Array.isArray(map[nextEmail]) ? map[nextEmail] : [];
|
||||||
|
|
||||||
|
map[nextEmail] = Array.from(new Set([...alreadyPresent, ...existingRegistrations]));
|
||||||
|
delete map[previousEmail];
|
||||||
|
|
||||||
|
localStorage.setItem(REGISTRATION_STORAGE_KEY, JSON.stringify(map));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ermittelt gehostete Events anhand Host-E-Mail oder Host-Vorname.
|
||||||
|
function getMyHostedEvents(events, user) {
|
||||||
|
const userFirstName = normalizeText(user.vorname);
|
||||||
|
|
||||||
|
return events.filter(event => {
|
||||||
|
const hostEmail = normalizeText(event.hostEmail || '');
|
||||||
|
const hostName = normalizeText(event.host?.name || '');
|
||||||
|
|
||||||
|
if (hostEmail && hostEmail === normalizeText(user.email)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return userFirstName && hostName === userFirstName;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ermittelt angemeldete Events ueber die Registration-Map.
|
||||||
|
function getMyRegisteredEvents(events, user) {
|
||||||
|
const registrationMap = getRegistrationMap();
|
||||||
|
const registeredIds = Array.isArray(registrationMap[user.email]) ? registrationMap[user.email] : [];
|
||||||
|
const idSet = new Set(registeredIds.map(id => Number(id)));
|
||||||
|
|
||||||
|
return events.filter(event => idSet.has(Number(event.id)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rendert gehostete Events inkl. Zaehler.
|
||||||
|
function renderMyEvents(events, user) {
|
||||||
|
const hostedEvents = getMyHostedEvents(events, user);
|
||||||
|
myEventsCount.textContent = String(hostedEvents.length);
|
||||||
|
renderEventCards(myEventsList, hostedEvents, 'Du hast noch kein eigenes Event erstellt.', false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rendert angemeldete Events inkl. Zaehler.
|
||||||
|
function renderMyRegistrations(events, user) {
|
||||||
|
const registeredEvents = getMyRegisteredEvents(events, user);
|
||||||
|
myRegistrationsCount.textContent = String(registeredEvents.length);
|
||||||
|
renderEventCards(myRegistrationsList, registeredEvents, 'Du bist aktuell bei keinem Event angemeldet.', true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Baut die Eventkarten fuer beide Listen in einheitlichem Markup.
|
||||||
|
function renderEventCards(container, events, emptyText, withUnregisterButton) {
|
||||||
|
container.innerHTML = '';
|
||||||
|
|
||||||
|
if (events.length === 0) {
|
||||||
|
const emptyElement = document.createElement('p');
|
||||||
|
emptyElement.className = 'profile-empty';
|
||||||
|
emptyElement.textContent = emptyText;
|
||||||
|
container.appendChild(emptyElement);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
events.forEach(event => {
|
||||||
|
const card = document.createElement('article');
|
||||||
|
card.className = 'profile-event-card';
|
||||||
|
|
||||||
|
const actionMarkup = withUnregisterButton
|
||||||
|
? `
|
||||||
|
<div class="profile-event-actions">
|
||||||
|
<a class="profile-event-link" href="event_detail.html?id=${event.id}">Zum Event</a>
|
||||||
|
<button class="profile-unregister-btn" type="button" data-unregister-id="${event.id}">Abmelden</button>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
: `<a class="profile-event-link" href="event_detail.html?id=${event.id}">Zum Event</a>`;
|
||||||
|
|
||||||
|
card.innerHTML = `
|
||||||
|
<div>
|
||||||
|
<h3 class="profile-event-title">${event.title}</h3>
|
||||||
|
<p class="profile-event-meta">${event.location} | ${formatEventDate(event.date)} | ${formatEventTime(event.time)}</p>
|
||||||
|
</div>
|
||||||
|
${actionMarkup}
|
||||||
|
`;
|
||||||
|
|
||||||
|
container.appendChild(card);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Formatiert ein Eventdatum konsistent fuer die Profilkarten.
|
||||||
|
function formatEventDate(dateString) {
|
||||||
|
if (!dateString) {
|
||||||
|
return 'Kein Datum';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (/^\d{4}-\d{2}-\d{2}$/.test(dateString)) {
|
||||||
|
const [year, month, day] = dateString.split('-');
|
||||||
|
const monthLabel = ['Januar', 'Februar', 'Maerz', 'April', 'Mai', 'Juni', 'Juli', 'August', 'September', 'Oktober', 'November', 'Dezember'][Number(month) - 1];
|
||||||
|
return `${Number(day)}. ${monthLabel} ${year}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return dateString;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Vereinheitlicht die Zeitanzeige fuer die Profilseite.
|
||||||
|
function formatEventTime(timeString) {
|
||||||
|
if (!timeString) {
|
||||||
|
return 'Keine Uhrzeit';
|
||||||
|
}
|
||||||
|
|
||||||
|
return timeString.includes('UHR') ? timeString.replace('UHR', 'Uhr').trim() : timeString;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Normalisiert Vergleichswerte fuer robuste String-Matches.
|
||||||
|
function normalizeText(value) {
|
||||||
|
return String(value || '').trim().toLowerCase();
|
||||||
|
}
|
||||||
|
});
|
||||||
63
js/navigation.js
Normal file
63
js/navigation.js
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
|
const CURRENT_USER_KEY = 'socialCookingCurrentUser';
|
||||||
|
const navContainers = document.querySelectorAll('.nav-tab-links');
|
||||||
|
const currentPage = (window.location.pathname.split('/').pop() || 'index.html').toLowerCase();
|
||||||
|
|
||||||
|
// Beendet frueh, falls auf einer Seite keine Hauptnavigation vorhanden ist.
|
||||||
|
if (!navContainers.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Liest den aktiven Benutzer robust aus localStorage.
|
||||||
|
function getCurrentUser() {
|
||||||
|
try {
|
||||||
|
const stored = localStorage.getItem(CURRENT_USER_KEY);
|
||||||
|
return stored ? JSON.parse(stored) : null;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Aktueller Benutzer konnte nicht gelesen werden.', error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Baut die Navigation fuer ausgeloggte Besucher.
|
||||||
|
function buildLoggedOutNavigation() {
|
||||||
|
const loginIsActive = currentPage === 'login.html';
|
||||||
|
const signupIsActive = currentPage === 'signup.html';
|
||||||
|
|
||||||
|
return `
|
||||||
|
<a
|
||||||
|
class="button-small auth-nav-button ${loginIsActive ? 'auth-nav-button--active' : 'auth-nav-button--default'}"
|
||||||
|
href="login.html"
|
||||||
|
aria-label="Login"
|
||||||
|
${loginIsActive ? 'aria-current="page"' : ''}
|
||||||
|
>
|
||||||
|
Login
|
||||||
|
</a>
|
||||||
|
<a
|
||||||
|
class="button-small auth-nav-button ${signupIsActive ? 'auth-nav-button--active' : 'auth-nav-button--default'}"
|
||||||
|
href="signup.html"
|
||||||
|
aria-label="Signup"
|
||||||
|
${signupIsActive ? 'aria-current="page"' : ''}
|
||||||
|
>
|
||||||
|
Signup
|
||||||
|
</a>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Baut die Navigation fuer eingeloggte Benutzer.
|
||||||
|
function buildLoggedInNavigation() {
|
||||||
|
return `
|
||||||
|
<a class="nav-tab" href="event_overview.html">Event finden</a>
|
||||||
|
<a class="nav-tab" href="event_create.html">Event erstellen</a>
|
||||||
|
<a class="button-small" href="my_profil.html" aria-label="Mein Profil">Mein Profil</a>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentUser = getCurrentUser();
|
||||||
|
const nextMarkup = currentUser ? buildLoggedInNavigation() : buildLoggedOutNavigation();
|
||||||
|
|
||||||
|
// Wendet das passende Markup auf alle vorhandenen Kopf-Navigationen an.
|
||||||
|
navContainers.forEach(container => {
|
||||||
|
container.innerHTML = nextMarkup;
|
||||||
|
});
|
||||||
|
});
|
||||||
55
js/signup.js
55
js/signup.js
@ -5,6 +5,30 @@ const emailInput = document.getElementById('email');
|
|||||||
const passwortInput = document.getElementById('passwort');
|
const passwortInput = document.getElementById('passwort');
|
||||||
const welcomeModal = document.getElementById('welcomeModal');
|
const welcomeModal = document.getElementById('welcomeModal');
|
||||||
|
|
||||||
|
const USERS_STORAGE_KEY = 'socialCookingUsers';
|
||||||
|
const CURRENT_USER_KEY = 'socialCookingCurrentUser';
|
||||||
|
|
||||||
|
// Liest bestehende Benutzerliste robust aus localStorage.
|
||||||
|
function getStoredUsers() {
|
||||||
|
try {
|
||||||
|
const raw = localStorage.getItem(USERS_STORAGE_KEY);
|
||||||
|
return raw ? JSON.parse(raw) : [];
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Benutzerdaten konnten nicht gelesen werden.', error);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Schreibt die komplette Benutzerliste in localStorage.
|
||||||
|
function setStoredUsers(users) {
|
||||||
|
localStorage.setItem(USERS_STORAGE_KEY, JSON.stringify(users));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Speichert den aktiven Benutzer fuer nachfolgende Seiten.
|
||||||
|
function setCurrentUser(user) {
|
||||||
|
localStorage.setItem(CURRENT_USER_KEY, JSON.stringify(user));
|
||||||
|
}
|
||||||
|
|
||||||
// Funktion zum Öffnen des Welcome Modals
|
// Funktion zum Öffnen des Welcome Modals
|
||||||
function openWelcomeModal() {
|
function openWelcomeModal() {
|
||||||
welcomeModal.classList.add('show');
|
welcomeModal.classList.add('show');
|
||||||
@ -78,12 +102,35 @@ function validateForm(event) {
|
|||||||
passwortGroup.classList.remove('has-error');
|
passwortGroup.classList.remove('has-error');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wenn alle Validierungen bestanden, Modal anzeigen
|
// Wenn alle Validierungen bestanden, Benutzer speichern und Session setzen.
|
||||||
if (isValid) {
|
if (isValid) {
|
||||||
openWelcomeModal();
|
const existingUsers = getStoredUsers();
|
||||||
// Hier würde später die Registrierung zum Backend gesendet
|
const emailLower = emailValue.toLowerCase();
|
||||||
|
const emailAlreadyUsed = existingUsers.some(user => user.email?.toLowerCase() === emailLower);
|
||||||
|
|
||||||
// Weiterleitung zur event overview Page
|
if (emailAlreadyUsed) {
|
||||||
|
emailGroup.classList.add('has-error');
|
||||||
|
document.getElementById('emailError').textContent = 'Diese E-Mail ist bereits registriert. Bitte nutze den Login.';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const newUser = {
|
||||||
|
id: Date.now(),
|
||||||
|
vorname: vornameValue,
|
||||||
|
nachname: nachnameValue,
|
||||||
|
email: emailValue,
|
||||||
|
passwort: passwortValue,
|
||||||
|
createdAt: new Date().toISOString(),
|
||||||
|
source: 'signup'
|
||||||
|
};
|
||||||
|
|
||||||
|
setStoredUsers([newUser, ...existingUsers]);
|
||||||
|
setCurrentUser(newUser);
|
||||||
|
|
||||||
|
openWelcomeModal();
|
||||||
|
// Hier würde spaeter die Registrierung zum Backend gesendet.
|
||||||
|
|
||||||
|
// Weiterleitung zur Event-Overview-Seite.
|
||||||
window.location.href = 'event_overview.html';
|
window.location.href = 'event_overview.html';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,6 +9,7 @@
|
|||||||
<link rel="stylesheet" href="css/login_signup.css">
|
<link rel="stylesheet" href="css/login_signup.css">
|
||||||
<!-- Globales Stylesheet -->
|
<!-- Globales Stylesheet -->
|
||||||
<link rel="stylesheet" href="css/stylesheet_global.css">
|
<link rel="stylesheet" href="css/stylesheet_global.css">
|
||||||
|
<script src="js/navigation.js" defer></script>
|
||||||
|
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
@ -20,9 +21,8 @@
|
|||||||
<img src="assets/logo_invite.svg" alt="Invite Logo">
|
<img src="assets/logo_invite.svg" alt="Invite Logo">
|
||||||
</a>
|
</a>
|
||||||
<nav class="nav-tab-links" aria-label="Hauptnavigation">
|
<nav class="nav-tab-links" aria-label="Hauptnavigation">
|
||||||
<a class="nav-tab" href="event_overview.html">Event finden</a>
|
<a class="button-small auth-nav-button auth-nav-button--active" href="login.html" aria-label="Login" aria-current="page">Login</a>
|
||||||
<a class="nav-tab" href="event_create.html">Event erstellen</a>
|
<a class="button-small auth-nav-button auth-nav-button--default" href="signup.html" aria-label="Signup">Signup</a>
|
||||||
<a class="button-small" href="login.html" aria-label="Profil">Login</a>
|
|
||||||
</nav>
|
</nav>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|||||||
102
my_profil.html
Normal file
102
my_profil.html
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="de">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Mein Profil | Invité</title>
|
||||||
|
|
||||||
|
<!-- Stylesheet für diese Seite -->
|
||||||
|
<link rel="stylesheet" href="css/my_profil.css">
|
||||||
|
<!-- Globales Stylesheet -->
|
||||||
|
<link rel="stylesheet" href="css/stylesheet_global.css">
|
||||||
|
<script src="js/navigation.js" defer></script>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<header class="top-nav-wrap">
|
||||||
|
<div class="top-nav">
|
||||||
|
<a class="brand" href="index.html" aria-label="Zur Startseite">
|
||||||
|
<img src="assets/logo_invite.svg" alt="Invite Logo">
|
||||||
|
</a>
|
||||||
|
<nav class="nav-tab-links" aria-label="Hauptnavigation">
|
||||||
|
<a class="button-small" href="login.html" aria-label="Login">Login</a>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<main class="container profile-page">
|
||||||
|
<section class="profile-hero" aria-label="Profilübersicht">
|
||||||
|
<div>
|
||||||
|
<p class="profile-kicker">Mein Bereich</p>
|
||||||
|
<h1 id="profile-headline">Mein Profil</h1>
|
||||||
|
<p id="profile-subline" class="profile-subline">Hier findest du deine Events, deine Anmeldungen und kannst deine Profildaten verwalten.</p>
|
||||||
|
</div>
|
||||||
|
<button id="logout-button" class="button-small profile-logout" type="button">Logout</button>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section id="logged-out-state" class="profile-panel hidden" aria-live="polite">
|
||||||
|
<h2 class="panel-title">Du bist noch nicht eingeloggt</h2>
|
||||||
|
<p>Melde dich an, damit wir deine Events und Anmeldungen anzeigen können.</p>
|
||||||
|
<div class="profile-cta-row">
|
||||||
|
<a class="button" href="login.html">Zum Login</a>
|
||||||
|
<a class="button profile-button-secondary" href="signup.html">Konto erstellen</a>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section id="logged-in-content" class="profile-grid">
|
||||||
|
<article class="profile-panel">
|
||||||
|
<div class="panel-head">
|
||||||
|
<h2 class="panel-title">Meine Events</h2>
|
||||||
|
<span id="my-events-count" class="panel-count">0</span>
|
||||||
|
</div>
|
||||||
|
<div id="my-events-list" class="profile-card-list"></div>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
<article class="profile-panel">
|
||||||
|
<div class="panel-head">
|
||||||
|
<h2 class="panel-title">Meine Anmeldungen</h2>
|
||||||
|
<span id="my-registrations-count" class="panel-count">0</span>
|
||||||
|
</div>
|
||||||
|
<div id="my-registrations-list" class="profile-card-list"></div>
|
||||||
|
</article>
|
||||||
|
|
||||||
|
<article class="profile-panel profile-panel-form">
|
||||||
|
<h2 class="panel-title">Profil verwalten</h2>
|
||||||
|
<form id="profile-form" novalidate>
|
||||||
|
<div class="form-grid">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="vorname">Vorname</label>
|
||||||
|
<input type="text" id="vorname" name="vorname" required>
|
||||||
|
<p class="input-error" id="vorname-error">Bitte gib deinen Vornamen ein.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="nachname">Nachname</label>
|
||||||
|
<input type="text" id="nachname" name="nachname" required>
|
||||||
|
<p class="input-error" id="nachname-error">Bitte gib deinen Nachnamen ein.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="email">E-Mail</label>
|
||||||
|
<input type="email" id="email" name="email" required>
|
||||||
|
<p class="input-error" id="email-error">Bitte gib eine gültige E-Mail-Adresse ein.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="passwort">Passwort</label>
|
||||||
|
<input type="password" id="passwort" name="passwort" minlength="6" placeholder="Mindestens 6 Zeichen">
|
||||||
|
<p class="input-hint">Nur ausfüllen, wenn du dein Passwort ändern möchtest.</p>
|
||||||
|
<p class="input-error" id="passwort-error">Das Passwort muss mindestens 6 Zeichen lang sein.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button class="button" type="submit">Profil speichern</button>
|
||||||
|
<p id="profile-feedback" class="profile-feedback" aria-live="polite"></p>
|
||||||
|
</form>
|
||||||
|
</article>
|
||||||
|
</section>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<script src="js/my_profil.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@ -9,6 +9,7 @@
|
|||||||
<link rel="stylesheet" href="css/login_signup.css">
|
<link rel="stylesheet" href="css/login_signup.css">
|
||||||
<!-- Globales Stylesheet -->
|
<!-- Globales Stylesheet -->
|
||||||
<link rel="stylesheet" href="css/stylesheet_global.css">
|
<link rel="stylesheet" href="css/stylesheet_global.css">
|
||||||
|
<script src="js/navigation.js" defer></script>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
@ -19,9 +20,8 @@
|
|||||||
<img src="assets/logo_invite.svg" alt="Invite Logo">
|
<img src="assets/logo_invite.svg" alt="Invite Logo">
|
||||||
</a>
|
</a>
|
||||||
<nav class="nav-tab-links" aria-label="Hauptnavigation">
|
<nav class="nav-tab-links" aria-label="Hauptnavigation">
|
||||||
<a class="nav-tab" href="event_overview.html">Event finden</a>
|
<a class="button-small auth-nav-button auth-nav-button--default" href="login.html" aria-label="Login">Login</a>
|
||||||
<a class="nav-tab" href="event_create.html">Event erstellen</a>
|
<a class="button-small auth-nav-button auth-nav-button--active" href="signup.html" aria-label="Signup" aria-current="page">Signup</a>
|
||||||
<a class="button-small" href="login.html" aria-label="Profil">Login</a>
|
|
||||||
</nav>
|
</nav>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user