From ad536090990c283be4f7407346d5c7153c51fee1 Mon Sep 17 00:00:00 2001 From: Elias Bennour Date: Thu, 22 May 2025 00:33:05 +0200 Subject: [PATCH] add profile page --- src/pages/api/user/profile.ts | 100 +++++++++++++++++ src/pages/dashboard.tsx | 37 +++++-- src/pages/profile.tsx | 198 ++++++++++++++++++++++++++++++++++ 3 files changed, 326 insertions(+), 9 deletions(-) create mode 100644 src/pages/api/user/profile.ts create mode 100644 src/pages/profile.tsx diff --git a/src/pages/api/user/profile.ts b/src/pages/api/user/profile.ts new file mode 100644 index 0000000..822ca08 --- /dev/null +++ b/src/pages/api/user/profile.ts @@ -0,0 +1,100 @@ +// Datei: pages/api/user/profile.ts +import type { NextApiRequest, NextApiResponse } from 'next'; +import { getServerSession } from 'next-auth/next'; +import { authOptions } from '../auth/[...nextauth]'; // Pfad anpassen +import prisma from '../../../lib/prisma'; // Pfad anpassen + +interface ProfileUpdateRequest { + name?: string; + // email?: string; // E-Mail-Änderung ist komplexer wegen Verifizierung, lassen wir erstmal weg +} + +interface ProfileResponse { + id: string; + name: string | null; + email: string | null; + // Füge hier weitere Felder hinzu, die du im Profil anzeigen möchtest +} + +interface ErrorResponse { + message: string; + errors?: { field: string; message: string }[]; +} + +export default async function handler( + req: NextApiRequest, + res: NextApiResponse +) { + const session = await getServerSession(req, res, authOptions); + + if (!session || !session.user || !session.user.id) { + return res.status(401).json({ message: 'Nicht autorisiert.' }); + } + + const userId = session.user.id; + + if (req.method === 'GET') { + try { + const user = await prisma.user.findUnique({ + where: { id: userId }, + select: { + id: true, + name: true, + email: true, + // Wähle hier weitere Felder aus, die du im Profil anzeigen möchtest + }, + }); + + if (!user) { + return res.status(404).json({ message: 'Benutzer nicht gefunden.' }); + } + return res.status(200).json(user); + } catch (error) { + console.error('Fehler beim Abrufen des Profils:', error); + return res.status(500).json({ message: 'Fehler beim Abrufen des Profils.' }); + } + } else if (req.method === 'PUT') { + const { name } = req.body as ProfileUpdateRequest; + const validationErrors: { field: string; message: string }[] = []; + + if (name !== undefined) { + if (typeof name !== 'string' || name.trim().length < 2) { + validationErrors.push({ field: 'name', message: 'Name ist erforderlich und muss mindestens 2 Zeichen lang sein.' }); + } + } else { + // Wenn nichts zum Aktualisieren gesendet wird + return res.status(400).json({ message: 'Keine Daten zum Aktualisieren angegeben.' }); + } + + + if (validationErrors.length > 0) { + return res.status(400).json({ message: 'Validierungsfehler.', errors: validationErrors }); + } + + try { + const updatedUser = await prisma.user.update({ + where: { id: userId }, + data: { + ...(name && { name: name.trim() }), + }, + select: { // Gib die aktualisierten Daten zurück + id: true, + name: true, + email: true, + } + }); + + // Wichtig: Die Session muss nicht unbedingt serverseitig aktualisiert werden, + // da NextAuth die Session bei der nächsten Anfrage neu validiert/aufbaut. + // Für eine sofortige UI-Aktualisierung muss das Frontend die neuen Daten verwenden. + + return res.status(200).json({ message: 'Profil erfolgreich aktualisiert.', ...updatedUser }); + } catch (error) { + console.error('Fehler beim Aktualisieren des Profils:', error); + return res.status(500).json({ message: 'Fehler beim Aktualisieren des Profils.' }); + } + } else { + res.setHeader('Allow', ['GET', 'PUT']); + return res.status(405).json({ message: `Method ${req.method} Not Allowed` }); + } +} diff --git a/src/pages/dashboard.tsx b/src/pages/dashboard.tsx index 0d2efa0..a6b1280 100644 --- a/src/pages/dashboard.tsx +++ b/src/pages/dashboard.tsx @@ -2,6 +2,7 @@ import type { NextPage } from 'next'; import { useSession, signOut } from 'next-auth/react'; import { useRouter } from 'next/router'; +import Link from 'next/link'; // Link-Komponente importieren import { useEffect, useState } from 'react'; interface DashboardTransaction { @@ -59,8 +60,18 @@ const DashboardPage: NextPage = () => { useEffect(() => { if (status === 'authenticated') { setIsLoadingBalance(true); - fetch('/api/user/balance') + fetch('/api/user/profile') // Profil-API aufrufen, um ggf. aktualisierten Namen zu bekommen .then(async (res) => { + if (!res.ok) { + // Versuche, den Saldo trotzdem zu laden, falls Profil nicht kritisch ist + console.warn('Fehler beim Laden des Profils für den Namen, fahre mit Saldo fort.'); + return fetch('/api/user/balance'); // Lade Saldo separat + } + const profileData = await res.json(); + setUserName(profileData.name || session?.user?.name || null); + return fetch('/api/user/balance'); // Lade Saldo + }) + .then(async (res) => { // Dieser .then Block ist für die Saldo-Antwort if (!res.ok) { const errorData = await res.json(); throw new Error(errorData.message || 'Fehler beim Laden des Saldos'); @@ -69,19 +80,22 @@ const DashboardPage: NextPage = () => { }) .then((data) => { setBalance(data.balance); - setUserName(data.userName || session?.user?.name || null); + // userName wird bereits im vorherigen .then gesetzt, falls Profil erfolgreich geladen wurde + if (!userName && session?.user?.name) { // Fallback, falls Profil-Fetch fehlschlug + setUserName(session.user.name); + } }) .catch((err: unknown) => { if (err instanceof Error) { setError(err.message); } else { - setError('Unbekannter Fehler beim Laden des Saldos aufgetreten.'); + setError('Unbekannter Fehler beim Laden der Dashboard-Daten.'); } - console.error("Fehler Saldo:", err); + console.error("Fehler Dashboard Daten:", err); }) .finally(() => setIsLoadingBalance(false)); } - }, [status, session]); + }, [status, session, userName]); // userName als Abhängigkeit hinzugefügt, um Header ggf. neu zu rendern const fetchTransactions = async () => { if (status === 'authenticated') { @@ -157,7 +171,7 @@ const DashboardPage: NextPage = () => { handleAddExpense(amountNumber, true); }; - const predefinedAmounts = [0.10, 0.20, 0.50, 1.00, 1.50, 2.00]; // 1.50 hinzugefügt + const predefinedAmounts = [0.10, 0.20, 0.50, 1.00, 1.50, 2.00]; if (status === 'loading') { return ( @@ -179,10 +193,15 @@ const DashboardPage: NextPage = () => {
-

Willkommen, {userName || session.user.name}!

+

Willkommen, {userName || session.user.name || 'Benutzer'}!

Deine Strichliste

-
+
{/* items-stretch für mobile Buttons */} + + + Profil + + {session?.user?.role === 'admin' && ( +
+ +
+
+ ); +}; + +export default ProfilePage;