Verbessere Booking-Form UX: Reset selectedTime bei Treatment-Wechsel, bessere Loading-States und lokale Datumsvalidierung

This commit is contained in:
2025-10-04 18:09:46 +02:00
parent 3a13c8dffb
commit 97c1d3493f
9 changed files with 1481 additions and 155 deletions

View File

@@ -1,4 +1,4 @@
import { useState } from "react";
import { useState, useEffect } from "react";
import { useAuth } from "@/client/components/auth-provider";
import { LoginForm } from "@/client/components/login-form";
import { UserProfile } from "@/client/components/user-profile";
@@ -14,6 +14,13 @@ import LegalPage from "@/client/components/legal-page";
function App() {
const { user, isLoading, isOwner } = useAuth();
const [activeTab, setActiveTab] = useState<"booking" | "admin-treatments" | "admin-bookings" | "admin-calendar" | "admin-availability" | "profile" | "legal">("booking");
const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);
// Prevent background scroll when menu is open
useEffect(() => {
document.body.classList.toggle('overflow-hidden', isMobileMenuOpen);
return () => document.body.classList.remove('overflow-hidden');
}, [isMobileMenuOpen]);
// Handle booking status page
const path = window.location.pathname;
@@ -84,11 +91,26 @@ function App() {
</div>
</div>
{/* Hamburger Button für Mobile */}
<button
type="button"
aria-label="Menü öffnen"
aria-controls="mobile-menu"
aria-expanded={isMobileMenuOpen}
className="md:hidden p-2 -ml-2 text-3xl text-gray-700 hover:text-pink-600 transition-colors"
onClick={() => setIsMobileMenuOpen(true)}
>
</button>
{user && (
<div className="flex items-center space-x-4">
<span className="text-sm text-gray-600">
<span className="text-sm text-gray-600 hidden sm:inline">
Willkommen, {user.username}
</span>
<span className="text-sm text-gray-600 sm:hidden">
{user.username}
</span>
{isOwner && (
<span className="bg-pink-100 text-pink-800 px-2 py-1 rounded-full text-xs font-medium">
Inhaber
@@ -100,8 +122,8 @@ function App() {
</div>
</header>
{/* Navigation */}
<nav className="bg-white shadow-sm">
{/* Desktop Navigation */}
<nav className="bg-white shadow-sm hidden md:block">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="flex space-x-8">
{tabs.map((tab) => {
@@ -146,6 +168,76 @@ function App() {
</div>
</nav>
{/* Mobile Backdrop */}
{isMobileMenuOpen && (
<div
className="fixed inset-0 bg-black/50 z-40 md:hidden"
onClick={() => setIsMobileMenuOpen(false)}
/>
)}
{/* Mobile Slide-in Panel */}
<div
id="mobile-menu"
role="dialog"
aria-modal="true"
aria-label="Navigation"
className={`fixed inset-y-0 left-0 w-64 bg-white shadow-xl z-50 md:hidden transform transition-transform duration-300 ease-in-out ${isMobileMenuOpen ? 'translate-x-0' : '-translate-x-full'}`}
>
<button
type="button"
aria-label="Menü schließen"
className="absolute top-4 right-4 text-2xl text-gray-600 hover:text-pink-600"
onClick={() => setIsMobileMenuOpen(false)}
>
×
</button>
<div className="p-6">
<img
src="/assets/stargilnails_logo_transparent_112.png"
alt="Stargil Nails Logo"
className="w-10 h-10 mb-6 object-contain"
/>
<nav className="mt-2 flex flex-col space-y-2">
{tabs.map((tab) => {
// Hide admin tabs for non-owners
if (tab.requiresAuth && !isOwner && tab.id !== 'profile') return null;
return (
<button
key={tab.id}
onClick={() => {
setActiveTab(tab.id as any);
setIsMobileMenuOpen(false);
}}
className={`flex items-center space-x-3 px-4 py-3 rounded-lg transition-colors ${
activeTab === tab.id ? 'bg-pink-100 text-pink-600' : 'text-gray-700 hover:bg-gray-100'
}`}
>
<span>{tab.icon}</span>
<span>{tab.label}</span>
</button>
);
})}
{!user && (
<button
onClick={() => {
setActiveTab('profile');
setIsMobileMenuOpen(false);
}}
className="flex items-center space-x-3 px-4 py-3 rounded-lg text-gray-700 hover:bg-gray-100 transition-colors"
>
<span>🔑</span>
<span>Inhaber Login</span>
</button>
)}
</nav>
</div>
</div>
{/* Main Content */}
<main className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
{activeTab === "booking" && (