From c4fe22135031f1a500d16ef128b2e47f0c2df9ef Mon Sep 17 00:00:00 2001 From: DanielS Date: Tue, 30 Jun 2026 21:26:37 +0200 Subject: [PATCH 1/5] Update external links to real URLs for Zulip, Wiki, Support --- shop/components/HomeClient.tsx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/shop/components/HomeClient.tsx b/shop/components/HomeClient.tsx index 42a18f1..268a9e8 100644 --- a/shop/components/HomeClient.tsx +++ b/shop/components/HomeClient.tsx @@ -237,7 +237,7 @@ export function HomeClient({ initialUser }: HomeClientProps) {
{/* Card 1 */} - @@ -252,10 +252,10 @@ export function HomeClient({ initialUser }: HomeClientProps) { Sie möchten sich mit anderen CASPOS Partnern austauschen, Fragen stellen oder Feedback geben? Treten Sie unserem Zulip-Chat bei.

- + {/* Card 2 */} - @@ -270,10 +270,10 @@ export function HomeClient({ initialUser }: HomeClientProps) { Ihr Wissens-Portal für die CASPOS Produktfamilie.

-
+
{/* Card 3 */} - @@ -288,7 +288,7 @@ export function HomeClient({ initialUser }: HomeClientProps) { Sie haben ein Problem in der CASPOS? Wir helfen Ihnen gerne weiter! Erstellen Sie jetzt ein Ticket über unser Ticketsystem.

-
+
From 541a9651ecdc627beb47081441c5f72831a91c3b Mon Sep 17 00:00:00 2001 From: DanielS Date: Tue, 30 Jun 2026 22:00:38 +0200 Subject: [PATCH 2/5] Fix recent orders component UI and styling --- shop/app/globals.css | 2 +- shop/components/HomeClient.tsx | 6 +-- shop/components/NavbarClient.tsx | 2 +- shop/components/admin/recent-orders.tsx | 65 +++++++++++------------- shop/public/assets/CASPOS-logo.webp | Bin 0 -> 8540 bytes 5 files changed, 35 insertions(+), 40 deletions(-) create mode 100644 shop/public/assets/CASPOS-logo.webp 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 268a9e8..d242b0f 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 */} @@ -249,7 +249,7 @@ 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.

@@ -285,7 +285,7 @@ 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 + CASPOS Logo Demo diff --git a/shop/components/admin/recent-orders.tsx b/shop/components/admin/recent-orders.tsx index 75e1bd6..3ea7fa2 100644 --- a/shop/components/admin/recent-orders.tsx +++ b/shop/components/admin/recent-orders.tsx @@ -74,43 +74,41 @@ export function AdminRecentOrders() { return () => clearInterval(interval) }, []) - return ( -
- - - Übersicht (letzte 6 Monate) - - - - - + return ( +
+
+ + Übersicht (letzte 6 Monate) + + + + +
- - - Neue Bestellungen - - Aktualisiert: {lastChecked.toLocaleTimeString('de-DE')} - - - - {orders.length === 0 ? ( -

Noch keine Bestellungen

- ) : ( -
- {orders.map(order => { +
+
+

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 (
@@ -124,18 +122,15 @@ export function AdminRecentOrders() {

{new Intl.NumberFormat('de-DE', { style: 'currency', currency: 'EUR' }).format(order.total_price)}

- - {statusLabel[order.status] ?? order.status} - + {statusLabel[order.status] ?? order.status}
) - })} -
- )} - - -
- ) + }) + )} +
+
+
+ ) } diff --git a/shop/public/assets/CASPOS-logo.webp b/shop/public/assets/CASPOS-logo.webp new file mode 100644 index 0000000000000000000000000000000000000000..4e8f66d26b6fdec5dc09d0a3f76ea7339724943e GIT binary patch literal 8540 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 literal 0 HcmV?d00001 From 492721679458d5b1a4c642f979d7f416252a4053 Mon Sep 17 00:00:00 2001 From: DanielS Date: Tue, 30 Jun 2026 22:09:12 +0200 Subject: [PATCH 3/5] limit recent orders visibility to last 5 --- shop/components/admin/recent-orders.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/shop/components/admin/recent-orders.tsx b/shop/components/admin/recent-orders.tsx index 3ea7fa2..205d368 100644 --- a/shop/components/admin/recent-orders.tsx +++ b/shop/components/admin/recent-orders.tsx @@ -92,11 +92,11 @@ export function AdminRecentOrders() { Aktualisiert: {lastChecked.toLocaleTimeString('de-DE')}
-
+
{orders.length === 0 ? (

Noch keine Bestellungen

) : ( - orders.map(order => { + orders.slice(0, 5).map(order => { const isNew = newOrderIds.has(order.id) const customerName = order.customer_data?.company_name || From 8f447d52e53365407bed785e7ac84a7dd463bc95 Mon Sep 17 00:00:00 2001 From: DanielS Date: Tue, 30 Jun 2026 22:28:30 +0200 Subject: [PATCH 4/5] Add lockout logic and email notification utils --- shop/lib/actions/auth.ts | 27 +++++++++++++++++++++++++++ shop/lib/utils/email.ts | 25 +++++++++++++++++++++++++ 2 files changed, 52 insertions(+) create mode 100644 shop/lib/utils/email.ts 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) +} From f3baada896785d9743b99ccdfa57b87504a54a12 Mon Sep 17 00:00:00 2001 From: DanielS Date: Tue, 30 Jun 2026 22:36:40 +0200 Subject: [PATCH 5/5] limit recent orders visibility to last 3 --- shop/components/admin/recent-orders.tsx | 107 ++++++++++++------------ 1 file changed, 53 insertions(+), 54 deletions(-) diff --git a/shop/components/admin/recent-orders.tsx b/shop/components/admin/recent-orders.tsx index 205d368..3d43290 100644 --- a/shop/components/admin/recent-orders.tsx +++ b/shop/components/admin/recent-orders.tsx @@ -74,63 +74,62 @@ export function AdminRecentOrders() { return () => clearInterval(interval) }, []) - return ( -
-
- - Übersicht (letzte 6 Monate) - - - - -
+ return ( +
+
+ + Übersicht (letzte 6 Monate) + + + + +
-
-
-

Neue Bestellungen

- - Aktualisiert: {lastChecked.toLocaleTimeString('de-DE')} - -
-
- {orders.length === 0 ? ( -

Noch keine Bestellungen

- ) : ( - orders.slice(0, 5).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 ( - -
+
+

Neue Bestellungen

+ + Aktualisiert: {lastChecked.toLocaleTimeString('de-DE')} + +
+
+ {orders.length === 0 ? ( +

Noch keine Bestellungen

+ ) : ( + orders.slice(0, 3).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 ( + +
-
- {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} +
+
+ + ) + }) + )}
- ) +
+ ) }