Files
beauty-bookings/src/client/app.tsx
elpatron 40d76680fd Implementiere Impressum/Datenschutz-System und bereinige URL-Konfiguration
- Neues Impressum/Datenschutz-Tab mit konfigurierbaren rechtlichen Daten
- Konfigurationsdatei legal-config.ts für alle rechtlichen Informationen
- RPC-Endpoint legal.getConfig() für rechtliche Daten
- Schöne Tab-Navigation zwischen Impressum und Datenschutz
- Responsive Design mit Loading-States und Fehlerbehandlung
- Alle rechtlichen Daten über Umgebungsvariablen konfigurierbar
- FRONTEND_URL entfernt - nur noch DOMAIN wird verwendet
- Hilfsfunktion generateUrl() für konsistente URL-Generierung
- Code-Duplikation in bookings.ts eliminiert
- .env.example aktualisiert mit allen neuen Variablen
- README.md dokumentiert neue rechtliche Konfiguration
- DSGVO- und TMG-konforme Inhalte implementiert
2025-09-30 18:14:01 +02:00

263 lines
9.7 KiB
TypeScript

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";
import { BookingForm } from "@/client/components/booking-form";
import { AdminTreatments } from "@/client/components/admin-treatments";
import { AdminBookings } from "@/client/components/admin-bookings";
import { AdminCalendar } from "@/client/components/admin-calendar";
import { InitialDataLoader } from "@/client/components/initial-data-loader";
import { AdminAvailability } from "@/client/components/admin-availability";
import CancellationPage from "@/client/components/cancellation-page";
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");
// Check for cancellation token in URL
useEffect(() => {
const path = window.location.pathname;
if (path.startsWith('/cancel/')) {
const token = path.split('/cancel/')[1];
if (token) {
// Set a special state to show cancellation page
setActiveTab("cancellation" as any);
return;
}
}
}, []);
// Handle cancellation page
const path = window.location.pathname;
if (path.startsWith('/cancel/')) {
const token = path.split('/cancel/')[1];
if (token) {
return <CancellationPage token={token} />;
}
}
// Show loading spinner while checking authentication
if (isLoading) {
return (
<div className="min-h-screen flex items-center justify-center bg-gradient-to-br from-pink-50 to-purple-50">
<div className="text-center">
<img
src="/assets/stargilnails_logo_transparent_112.png"
alt="Stargil Nails Logo"
className="w-16 h-16 mx-auto mb-4 object-contain animate-pulse"
/>
<div className="text-lg text-gray-600">Lade...</div>
</div>
</div>
);
}
// Show login form if user is not authenticated and trying to access admin features
const needsAuth = !user && (activeTab === "admin-treatments" || activeTab === "admin-bookings" || activeTab === "admin-calendar" || activeTab === "admin-availability" || activeTab === "profile");
if (needsAuth) {
return <LoginForm />;
}
// Show legal page if legal tab is active
if (activeTab === "legal") {
return <LegalPage />;
}
const tabs = [
{ id: "booking", label: "Termin buchen", icon: "📅", requiresAuth: false },
{ id: "legal", label: "Impressum/Datenschutz", icon: "📋", requiresAuth: false },
{ id: "admin-treatments", label: "Behandlungen verwalten", icon: "💅", requiresAuth: true },
{ id: "admin-bookings", label: "Buchungen verwalten", icon: "📋", requiresAuth: true },
{ id: "admin-calendar", label: "Kalender", icon: "📆", requiresAuth: true },
{ id: "admin-availability", label: "Verfügbarkeiten", icon: "⏰", requiresAuth: true },
...(user ? [{ id: "profile", label: "Profil", icon: "👤", requiresAuth: true }] : []),
] as const;
return (
<div className="min-h-screen bg-gradient-to-br from-pink-50 to-purple-50">
<InitialDataLoader />
{/* Header */}
<header className="bg-white shadow-sm border-b border-pink-100">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="flex justify-between items-center py-6">
<div
className="flex items-center space-x-3 cursor-pointer hover:opacity-80 transition-opacity"
onClick={() => setActiveTab("booking")}
>
<img
src="/assets/stargilnails_logo_transparent_112.png"
alt="Stargil Nails Logo"
className="w-12 h-12 object-contain"
/>
<div>
<h1 className="text-2xl font-bold text-gray-900">Stargirlnails Kiel</h1>
<p className="text-sm text-gray-600">Professional Nail Design & Care</p>
</div>
</div>
{user && (
<div className="flex items-center space-x-4">
<span className="text-sm text-gray-600">
Willkommen, {user.username}
</span>
{isOwner && (
<span className="bg-pink-100 text-pink-800 px-2 py-1 rounded-full text-xs font-medium">
Inhaber
</span>
)}
</div>
)}
</div>
</div>
</header>
{/* Navigation */}
<nav className="bg-white shadow-sm">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="flex space-x-8">
{tabs.map((tab) => {
// Hide admin tabs for non-owners
if (tab.requiresAuth && !isOwner && tab.id !== "profile") {
return null;
}
return (
<button
key={tab.id}
onClick={() => {
if (tab.requiresAuth && !user) {
// This will trigger the login form
setActiveTab(tab.id as any);
} else {
setActiveTab(tab.id as any);
}
}}
className={`py-4 px-1 border-b-2 font-medium text-sm flex items-center space-x-2 ${
activeTab === tab.id
? "border-pink-500 text-pink-600"
: "border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300"
}`}
>
<span>{tab.icon}</span>
<span>{tab.label}</span>
</button>
);
})}
{!user && (
<button
onClick={() => setActiveTab("profile")} // This will trigger login
className="py-4 px-1 border-b-2 border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300 font-medium text-sm flex items-center space-x-2"
>
<span>🔑</span>
<span>Inhaber Login</span>
</button>
)}
</div>
</div>
</nav>
{/* Main Content */}
<main className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
{activeTab === "booking" && (
<div>
<div className="text-center mb-8">
<h2 className="text-3xl font-bold text-gray-900 mb-4">
Buche deine perfekte Nagelbehandlung
</h2>
<p className="text-lg text-gray-600 max-w-2xl mx-auto">
Erlebe professionelle Nagelpflege mit unseren Experten.
Wähle aus unserem breiten Angebot an Behandlungen und buche noch heute deinen Termin.
</p>
</div>
<BookingForm />
</div>
)}
{activeTab === "admin-treatments" && isOwner && (
<div>
<div className="text-center mb-8">
<h2 className="text-3xl font-bold text-gray-900 mb-4">
Behandlungen verwalten
</h2>
<p className="text-lg text-gray-600">
Füge Behandlungen hinzu, bearbeite und verwalte deine Nagelbehandlungen.
</p>
</div>
<AdminTreatments />
</div>
)}
{activeTab === "admin-bookings" && isOwner && (
<div>
<div className="text-center mb-8">
<h2 className="text-3xl font-bold text-gray-900 mb-4">
Buchungen verwalten
</h2>
<p className="text-lg text-gray-600">
Sieh dir Kundentermine an und verwalte Buchungen.
</p>
</div>
<AdminBookings />
</div>
)}
{activeTab === "admin-calendar" && isOwner && (
<div>
<div className="text-center mb-8">
<h2 className="text-3xl font-bold text-gray-900 mb-4">
Kalender - Bevorstehende Buchungen
</h2>
<p className="text-lg text-gray-600">
Übersichtliche Darstellung aller bevorstehenden Termine im Kalenderformat.
</p>
</div>
<AdminCalendar />
</div>
)}
{activeTab === "admin-availability" && isOwner && (
<div>
<div className="text-center mb-8">
<h2 className="text-3xl font-bold text-gray-900 mb-4">
Verfügbarkeiten verwalten
</h2>
<p className="text-lg text-gray-600">
Lege freie Slots an und entferne sie bei Bedarf.
</p>
</div>
<AdminAvailability />
</div>
)}
{activeTab === "profile" && user && (
<div>
<div className="text-center mb-8">
<h2 className="text-3xl font-bold text-gray-900 mb-4">
Benutzerprofil
</h2>
<p className="text-lg text-gray-600">
Verwalte deine Kontoinformationen und Einstellungen.
</p>
</div>
<UserProfile />
</div>
)}
</main>
{/* Footer */}
<footer className="bg-white border-t border-pink-100 mt-16">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
<div className="text-center text-gray-600">
<p>&copy; 2024 Stargirlnails Kiel. Professional nail care services.</p>
</div>
</div>
</footer>
</div>
);
}
export default App;