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 + CASPOS Logo 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