diff --git a/shop/app/globals.css b/shop/app/globals.css
index f777ed7..83ae263 100644
--- a/shop/app/globals.css
+++ b/shop/app/globals.css
@@ -10,7 +10,7 @@
--card-foreground: 220 40% 2%;
--popover: 0 0% 100%;
--popover-foreground: 220 40% 2%;
- --primary: 220 90% 56%;
+ --primary: 199 100% 43%;
--primary-foreground: 0 0% 100%;
--secondary: 220 14% 90%;
--secondary-foreground: 220 40% 2%;
diff --git a/shop/components/HomeClient.tsx b/shop/components/HomeClient.tsx
index 4b1b667..8672baa 100644
--- a/shop/components/HomeClient.tsx
+++ b/shop/components/HomeClient.tsx
@@ -121,7 +121,7 @@ export function HomeClient({ initialUser }: HomeClientProps) {
>
- CASPOS Lizenz-Order-Portal
+ CASPOS Lizenz-Portal
{/* Headline */}
@@ -237,7 +237,7 @@ export function HomeClient({ initialUser }: HomeClientProps) {
{/* Card 1 */}
-
@@ -249,13 +249,13 @@ export function HomeClient({ initialUser }: HomeClientProps) {
Zulip
- Sie möchten sich mit anderen CASPOS Partnern austauschen, Fragen stellen oder Feedback geben? Treten Sie unserem Zulip-Chat bei.
+ Treten Sie unserem Partner-Chat bei! tauschen Sie sich mit anderen CASPOS Partnern aus.
-
+
{/* Card 2 */}
-
@@ -270,10 +270,10 @@ export function HomeClient({ initialUser }: HomeClientProps) {
Ihr Wissens-Portal für die CASPOS Produktfamilie.
-
+
{/* Card 3 */}
-
@@ -285,10 +285,10 @@ export function HomeClient({ initialUser }: HomeClientProps) {
Support
- Sie haben ein Problem in der CASPOS? Wir helfen Ihnen gerne weiter! Erstellen Sie jetzt ein Ticket über unser Ticketsystem.
+ Sie haben ein technisches Problem in der CASPOS? Erstellen Sie jetzt ein Ticket!
-
+
diff --git a/shop/components/NavbarClient.tsx b/shop/components/NavbarClient.tsx
index 3b111c3..78d6daf 100644
--- a/shop/components/NavbarClient.tsx
+++ b/shop/components/NavbarClient.tsx
@@ -98,7 +98,7 @@ export function NavbarClient({ user, role = "partner" }: NavbarClientProps) {
<>
- CASPOS Shop
+
Demo
diff --git a/shop/components/admin/recent-orders.tsx b/shop/components/admin/recent-orders.tsx
index 75e1bd6..3d43290 100644
--- a/shop/components/admin/recent-orders.tsx
+++ b/shop/components/admin/recent-orders.tsx
@@ -75,67 +75,61 @@ export function AdminRecentOrders() {
}, [])
return (
-
-
+
+
- Übersicht (letzte 6 Monate)
+ Übersicht (letzte 6 Monate)
-
+
-
-
- Neue Bestellungen
+
+
+
Neue Bestellungen
Aktualisiert: {lastChecked.toLocaleTimeString('de-DE')}
-
-
+
+
{orders.length === 0 ? (
Noch keine Bestellungen
) : (
-
- {orders.map(order => {
- const isNew = newOrderIds.has(order.id)
- const customerName =
- order.customer_data?.company_name ||
- `${order.customer_data?.first_name ?? ''} ${order.customer_data?.last_name ?? ''}`.trim() ||
- 'Unbekannt'
-
- return (
-
-
{
+ const isNew = newOrderIds.has(order.id)
+ const customerName =
+ order.customer_data?.company_name ||
+ `${order.customer_data?.first_name ?? ''} ${order.customer_data?.last_name ?? ''}`.trim() ||
+ 'Unbekannt'
+ return (
+
+
-
- {customerName.slice(0, 2).toUpperCase()}
-
-
-
{customerName}
-
#{order.order_number}
-
-
-
- {new Intl.NumberFormat('de-DE', { style: 'currency', currency: 'EUR' }).format(order.total_price)}
-
-
- {statusLabel[order.status] ?? order.status}
-
-
+ >
+
+ {customerName.slice(0, 2).toUpperCase()}
-
- )
- })}
-
+
+
{customerName}
+
#{order.order_number}
+
+
+
+ {new Intl.NumberFormat('de-DE', { style: 'currency', currency: 'EUR' }).format(order.total_price)}
+
+
{statusLabel[order.status] ?? order.status}
+
+
+
+ )
+ })
)}
-
-
+
+
)
}
diff --git a/shop/lib/actions/auth.ts b/shop/lib/actions/auth.ts
index 23d255a..8b45ce3 100644
--- a/shop/lib/actions/auth.ts
+++ b/shop/lib/actions/auth.ts
@@ -3,6 +3,7 @@
import { createClient } from '@/lib/supabase/server'
import { createAdminClient } from '@/lib/supabase/admin'
import { headers } from 'next/headers'
+import { sendLockoutEmail } from '@/lib/utils/email'
export async function signIn(email: string, password: string) {
try {
@@ -12,6 +13,32 @@ export async function signIn(email: string, password: string) {
password,
})
if (error) {
+ // Track failed attempts
+ const { data: usr, error: usrError } = await supabase
+ .from('users')
+ .select('failed_attempts, email')
+ .eq('email', email)
+ .single()
+
+ const attempts = (usr?.failed_attempts ?? 0) + 1
+
+ // Update attempts count
+ await supabase
+ .from('users')
+ .update({ failed_attempts: attempts })
+ .eq('email', email)
+
+ // Lock account on 5th failure
+ if (attempts >= 5) {
+ await supabase
+ .from('users')
+ .update({ role: 'gesperrt' })
+ .eq('email', email)
+ // Notify admin
+ await sendLockoutEmail('info@hephex.de')
+ return { success: false, error: 'Konto gesperrt nach 5 Fehlversuchen.' }
+ }
+
return { success: false, error: error.message }
}
diff --git a/shop/lib/utils/email.ts b/shop/lib/utils/email.ts
new file mode 100644
index 0000000..ab3dcca
--- /dev/null
+++ b/shop/lib/utils/email.ts
@@ -0,0 +1,25 @@
+'use server'
+
+import nodemailer from 'nodemailer'
+
+export async function sendLockoutEmail(to: string) {
+ const transporter = nodemailer.createTransport({
+ host: process.env.SMTP_HOST,
+ port: Number(process.env.SMTP_PORT),
+ secure: process.env.SMTP_SECURE === 'true',
+ auth: {
+ user: process.env.SMTP_USER,
+ pass: process.env.SMTP_PASS,
+ },
+ })
+
+ const message = {
+ from: `"Admin" <${process.env.SMTP_FROM}>`,
+ to,
+ subject: 'Konto gesperrt',
+ text: 'Ihr Konto wurde wegen zu vieler falscher Passwortversuche gesperrt.',
+ html: `Ihr Konto wurde gesperrt, weil das Passwort zu oft falsch eingegeben wurde.
`,
+ }
+
+ await transporter.sendMail(message)
+}
diff --git a/shop/public/assets/CASPOS-logo.webp b/shop/public/assets/CASPOS-logo.webp
new file mode 100644
index 0000000..4e8f66d
Binary files /dev/null and b/shop/public/assets/CASPOS-logo.webp differ