From dd2c3859e9d41650d2876b503fe8fed5bc4fba66 Mon Sep 17 00:00:00 2001 From: DanielS Date: Tue, 23 Jun 2026 23:27:23 +0200 Subject: [PATCH] feat: add custom navbar with inline supabase clients and custom dropdown --- shop/app/login/page.tsx | 5 + shop/app/page.tsx | 131 ++++++++++--------- shop/components/Navbar.tsx | 49 +++++++ shop/components/NavbarClient.tsx | 217 +++++++++++++++++++++++++++++++ 4 files changed, 342 insertions(+), 60 deletions(-) create mode 100644 shop/app/login/page.tsx create mode 100644 shop/components/Navbar.tsx create mode 100644 shop/components/NavbarClient.tsx diff --git a/shop/app/login/page.tsx b/shop/app/login/page.tsx new file mode 100644 index 0000000..4a4eec8 --- /dev/null +++ b/shop/app/login/page.tsx @@ -0,0 +1,5 @@ +import { redirect } from "next/navigation"; + +export default function LoginPage() { + redirect("/auth/login"); +} diff --git a/shop/app/page.tsx b/shop/app/page.tsx index 3e04bd3..46f14d8 100644 --- a/shop/app/page.tsx +++ b/shop/app/page.tsx @@ -1,66 +1,46 @@ import { Button } from '@/components/ui/button' import Link from 'next/link' -import { Rocket, Shield, Cpu, ArrowRight } from 'lucide-react' +import { Shield, Cpu, ArrowRight, Key, Zap, CheckCircle2 } from 'lucide-react' +import { Navbar } from '@/components/Navbar' export default function Home() { return ( -
-
+
+ {/* Demo Banner */} +
⚠️ Dies ist eine **Demo-Webseite (Dummy-Shop)**. Es werden keine echten Bestellungen verarbeitet oder Zahlungen abgewickelt.
-
- -
- -
- CASPOS Store - - Demo - - - -
+ + +
+ {/* Hero Section: Zielgerichtet auf Lizenzen */}
-
+
-
- ⚠️ Demo-Umgebung für Testbestellungen +
+ CASPOS Lizenz-Management-Server

- CASPOS Software-Bestellung + POS Software Lizenzen & Add-ons

-

- Modulare Lösungen, einfaches Deployment und ein intuitives Interface für Ihre Business-Anforderungen. +

+ Erwerben und verwalten Sie digitale Lizenzen für CASPOS Kassensysteme. Sofortige Aktivierung und automatische Bereitstellung für Ihre Kunden.

+ + {/* High-Contrast Buttons (Light/Dark Ready) */}
- -
@@ -68,41 +48,72 @@ export default function Home() {
-
+ {/* Feature Grid: Aufgeteilt nach echten POS-Modulen */} +
-
-
- + + {/* Modul 1 */} +
+
+
-

Handel

-

Von der einfachen Handelskasse bis hin zur vollwertigen ERP. Wir können es.

+

Handel & ERP

+

Erweiterungen für Handelskassen, Bestandsführung und vollwertige Warenwirtschaftssysteme (ERP).

-
-
- + + {/* Modul 2 */} +
+
+
-

Gastro

-

Egal ob mobiles Ordern, einer Küchen-Anzeige oder einen Pick-Up-Bord. Wir können es.

+

Gastronomie

+

Lizenzen für mobiles Bestellen (Orderman), digitale Küchen-Monitore und interaktive Abhol-Boards.

-
-
- + + {/* Modul 3 */} +
+
+
-

Hybrid Systeme

-

Sie benötigen eine Lösung aus beiden Welten? Sprechen Sie uns an.

+

Hybride Systeme

+

Das beste aus beiden Welten. Handel mit Gastronomie und umgekehrt.

+
+ +
+
+
+ + {/* Kurze Info-Sektion für Partner / Händler */} +
+
+

Wie funktioniert die Bereitstellung?

+
+
+ +

1. Lizenz wählen: Gewünschtes POS-Modul im Shop selektieren.

+
+
+ +

2. Kunde zuweisen: Lizenz direkt einem Kunden-Account zuordnen.

+
+
+ +

3. Live gehen: Lizenz-Key (`.key`) wird sofort serverseitig aktiviert.

-
+ + {/* Footer */} +

© 2026 CASPOS GmbH. Alle Rechte vorbehalten.

) -} +} \ No newline at end of file diff --git a/shop/components/Navbar.tsx b/shop/components/Navbar.tsx new file mode 100644 index 0000000..938adae --- /dev/null +++ b/shop/components/Navbar.tsx @@ -0,0 +1,49 @@ +import { createServerClient } from "@supabase/ssr"; +import { cookies } from "next/headers"; +import { NavbarClient } from "@/components/NavbarClient"; + +export async function Navbar() { + const cookieStore = await cookies(); + + const supabaseUrl = process.env.SUPABASE_URL || process.env.NEXT_PUBLIC_SUPABASE_URL; + const supabaseAnonKey = process.env.SUPABASE_ANON_KEY || process.env.NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY || process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY; + + if (!supabaseUrl || !supabaseAnonKey) { + // Falls keine Env-Vars da sind, leeren User an Client übergeben + return ; + } + + const supabase = createServerClient( + supabaseUrl, + supabaseAnonKey, + { + cookies: { + getAll() { + return cookieStore.getAll(); + }, + setAll(cookiesToSet) { + try { + cookiesToSet.forEach(({ name, value, options }) => + cookieStore.set(name, value, options), + ); + } catch { + // Kann in Server Components ignoriert werden, da dort keine Cookies gesetzt werden können + } + }, + }, + cookieOptions: { + name: "webshop-auth-token", + }, + }, + ); + + let user = null; + try { + const { data } = await supabase.auth.getUser(); + user = data.user; + } catch (error) { + console.error("Fehler beim Abrufen des Users in Navbar:", error); + } + + return ; +} diff --git a/shop/components/NavbarClient.tsx b/shop/components/NavbarClient.tsx new file mode 100644 index 0000000..1b42b9a --- /dev/null +++ b/shop/components/NavbarClient.tsx @@ -0,0 +1,217 @@ +"use client"; + +import { useEffect, useState } from "react"; +import Link from "next/link"; +import { useRouter } from "next/navigation"; +import { createBrowserClient } from "@supabase/ssr"; +import { type User } from "@supabase/supabase-js"; +import { Rocket, Menu, X, User as UserIcon, LogOut } from "lucide-react"; +import { Button } from "@/components/ui/button"; + +interface NavbarClientProps { + user: User | null; +} + +export function NavbarClient({ user }: NavbarClientProps) { + const router = useRouter(); + const [currentUser, setCurrentUser] = useState(user); + const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false); + const [open, setOpen] = useState(false); + + // Supabase URL & Anon Key auslesen (für Browser-Client) + const supabaseUrl = process.env.SUPABASE_URL || process.env.NEXT_PUBLIC_SUPABASE_URL; + const supabaseAnonKey = process.env.SUPABASE_ANON_KEY || process.env.NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY || process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY; + + // Supabase URL Auflösung inline + let resolvedUrl = supabaseUrl; + if (resolvedUrl && typeof window !== "undefined") { + try { + const parsed = new URL(resolvedUrl); + if (parsed.hostname === "supabase-kong" || parsed.hostname === "kong") { + parsed.hostname = window.location.hostname; + resolvedUrl = parsed.toString().replace(/\/$/, ""); + } + } catch { + resolvedUrl = resolvedUrl + .replace("//supabase-kong", `//${window.location.hostname}`) + .replace("//kong", `//${window.location.hostname}`); + } + } + + // Browser Client inline erstellen + const supabase = createBrowserClient( + resolvedUrl!, + supabaseAnonKey!, + { + cookieOptions: { + name: "webshop-auth-token", + }, + } + ); + + // Auf Auth-Statusänderungen reagieren + useEffect(() => { + const { + data: { subscription }, + } = supabase.auth.onAuthStateChange((event, session) => { + setCurrentUser(session?.user ?? null); + }); + + return () => { + subscription.unsubscribe(); + }; + }, [supabase]); + + const handleSignOut = async () => { + await supabase.auth.signOut(); + setCurrentUser(null); + setIsMobileMenuOpen(false); + router.refresh(); + router.push("/"); + }; + + return ( + <> +
+ + CASPOS Store + + Demo + + + + {/* Desktop Navigation */} + + + {/* Mobile Hamburger Trigger */} + +
+ + {/* Mobile Menu Panel */} + {isMobileMenuOpen && ( +
+ setIsMobileMenuOpen(false)} + > + Bestellen + + setIsMobileMenuOpen(false)} + > + Wiki + + setIsMobileMenuOpen(false)} + > + Support + + + {currentUser && ( + <> +
+
Mein Account ({currentUser.email})
+ setIsMobileMenuOpen(false)} + > + Account Kunden + + + )} + +
+ + {currentUser ? ( + + ) : ( + + )} +
+ )} + + ); +}