From e7693c4be123dd8c5513b202a11c0ef8f227d9ff Mon Sep 17 00:00:00 2001 From: Elias Bennour Date: Thu, 22 May 2025 00:19:50 +0200 Subject: [PATCH] add custom amount option --- src/pages/api/user/increase-balance.ts | 74 ++++++++++++++++---------- src/pages/dashboard.tsx | 58 +++++++++++++++++--- 2 files changed, 97 insertions(+), 35 deletions(-) diff --git a/src/pages/api/user/increase-balance.ts b/src/pages/api/user/increase-balance.ts index cacd928..9a5493d 100644 --- a/src/pages/api/user/increase-balance.ts +++ b/src/pages/api/user/increase-balance.ts @@ -2,16 +2,16 @@ import type { NextApiRequest, NextApiResponse } from 'next'; import { getServerSession } from 'next-auth/next'; import { authOptions } from '../auth/[...nextauth]'; // Pfad zu deiner authOptions anpassen +import prisma from '../../../lib/prisma'; // Pfad zu deinem Prisma Client Singleton anpassen import { Decimal } from '@prisma/client/runtime/library'; -import prisma from "@/lib/prisma"; // Für korrekte Decimal-Handhabung -// Definiere die erwartete Struktur des Request-Body interface IncreaseBalanceRequestBody { - amount: number; // Der Betrag, um den erhöht werden soll (z.B. 0.10, 0.50, 1.00) + amount: number; // Kann jetzt ein beliebiger positiver Betrag oder einer der vordefinierten sein + isCustom?: boolean; // Optional: Flag, um zu signalisieren, dass es ein individueller Betrag ist } -// Definiere die erlaubten Beträge und ihre zugehörigen Transaktionstypen -const ALLOWED_AMOUNTS: Record = { +// Vordefinierte Beträge bleiben optional bestehen +const ALLOWED_PREDEFINED_AMOUNTS: Record = { 0.10: "deposit_0.10", 0.20: "deposit_0.20", 0.50: "deposit_0.50", @@ -19,16 +19,27 @@ const ALLOWED_AMOUNTS: Record = { 2.00: "deposit_2.00", }; +interface IncreaseBalanceApiResponse { + message: string; + newBalance: string; + transactionId: string; +} + +interface ErrorResponse { + message: string; + allowedAmounts?: number[]; // Für den Fall, dass nur vordefinierte gemeint waren +} + + export default async function handler( req: NextApiRequest, - res: NextApiResponse + res: NextApiResponse ) { if (req.method !== 'POST') { res.setHeader('Allow', ['POST']); return res.status(405).json({ message: `Method ${req.method} Not Allowed` }); } - // Benutzer-Session abrufen, um sicherzustellen, dass der Benutzer angemeldet ist const session = await getServerSession(req, res, authOptions); if (!session || !session.user || !session.user.id) { @@ -38,22 +49,31 @@ export default async function handler( const userId = session.user.id; try { - const { amount } = req.body as IncreaseBalanceRequestBody; + const { amount, isCustom } = req.body as IncreaseBalanceRequestBody; - // Validierung des Betrags - if (typeof amount !== 'number' || !ALLOWED_AMOUNTS[amount]) { - return res.status(400).json({ - message: 'Ungültiger oder nicht erlaubter Betrag.', - allowedAmounts: Object.keys(ALLOWED_AMOUNTS).map(Number), - }); + if (typeof amount !== 'number' || amount <= 0) { + return res.status(400).json({ message: 'Der Betrag muss eine positive Zahl sein.' }); } + let transactionType: string; const increaseAmountDecimal = new Decimal(amount); - const transactionType = ALLOWED_AMOUNTS[amount]; - // Prisma-Transaktion verwenden, um sicherzustellen, dass beide Operationen atomar sind + if (isCustom) { + // Für individuelle Beträge + transactionType = `deposit_custom_${amount.toFixed(2)}`; // oder einfach "deposit_custom" + } else { + // Für vordefinierte Beträge + if (!ALLOWED_PREDEFINED_AMOUNTS[amount]) { + return res.status(400).json({ + message: 'Ungültiger oder nicht erlaubter vordefinierter Betrag.', + allowedAmounts: Object.keys(ALLOWED_PREDEFINED_AMOUNTS).map(Number), + }); + } + transactionType = ALLOWED_PREDEFINED_AMOUNTS[amount]; + } + + const result = await prisma.$transaction(async (tx) => { - // 1. Benutzer-Saldo aktualisieren const updatedUser = await tx.user.update({ where: { id: userId }, data: { @@ -61,10 +81,9 @@ export default async function handler( increment: increaseAmountDecimal, }, }, - select: { // Nur die benötigten Felder auswählen + select: { id: true, balance: true, - name: true, } }); @@ -72,13 +91,12 @@ export default async function handler( throw new Error('Benutzer nicht gefunden oder Update fehlgeschlagen.'); } - // 2. Transaktion erstellen const newTransaction = await tx.transaction.create({ data: { amount: increaseAmountDecimal, type: transactionType, userId: userId, - // description: `Einzahlung von ${amount.toFixed(2)}€`, // Optionale Beschreibung + description: `Betrag hinzugefügt: ${increaseAmountDecimal.toFixed(2)}€`, }, }); @@ -86,17 +104,19 @@ export default async function handler( }); return res.status(200).json({ - message: `Saldo erfolgreich um ${amount.toFixed(2)}€ erhöht.`, - newBalance: result.updatedUser.balance, + message: `Betrag erfolgreich um ${amount.toFixed(2)}€ hinzugefügt.`, + newBalance: result.updatedUser.balance?.toFixed(2), transactionId: result.newTransaction.id, }); } catch (error) { console.error('Fehler beim Erhöhen des Saldos:', error); - // Spezifischere Fehlerbehandlung basierend auf dem Fehlerobjekt + let errorMessage = 'Interner Serverfehler beim Erhöhen des Saldos.'; if (error instanceof Error && error.message.includes('Benutzer nicht gefunden')) { - return res.status(404).json({ message: 'Benutzer nicht gefunden.' }); + errorMessage = 'Benutzer nicht gefunden.'; + return res.status(404).json({ message: errorMessage }); } - return res.status(500).json({ message: 'Interner Serverfehler beim Erhöhen des Saldos.' }); + // Hier könntest du weitere spezifische Fehlerbehandlungen hinzufügen + return res.status(500).json({ message: errorMessage }); } -} \ No newline at end of file +} diff --git a/src/pages/dashboard.tsx b/src/pages/dashboard.tsx index 16ccd40..933fb88 100644 --- a/src/pages/dashboard.tsx +++ b/src/pages/dashboard.tsx @@ -4,7 +4,6 @@ import { useSession, signOut } from 'next-auth/react'; import { useRouter } from 'next/router'; import { useEffect, useState } from 'react'; -// Typ für Transaktionen, wie sie vom Backend (API) erwartet werden interface DashboardTransaction { id: string; userId: string; @@ -47,6 +46,10 @@ const DashboardPage: NextPage = () => { const [error, setError] = useState(null); const [successMessage, setSuccessMessage] = useState(null); + const [customAmount, setCustomAmount] = useState(''); + const [isSubmittingCustom, setIsSubmittingCustom] = useState(false); + + useEffect(() => { if (status === 'unauthenticated') { router.push('/auth/signin'); @@ -111,16 +114,18 @@ const DashboardPage: NextPage = () => { } }, [status]); - const handleIncreaseBalance = async (amount: number) => { + const handleAddExpense = async (amountValue: number, isCustomAmount: boolean = false) => { setError(null); setSuccessMessage(null); + if (isCustomAmount) setIsSubmittingCustom(true); + try { const response = await fetch('/api/user/increase-balance', { method: 'POST', headers: { 'Content-Type': 'application/json', }, - body: JSON.stringify({ amount }), + body: JSON.stringify({ amount: amountValue, isCustom: isCustomAmount }), }); const data: IncreaseBalanceApiResponse = await response.json(); if (!response.ok) { @@ -128,6 +133,7 @@ const DashboardPage: NextPage = () => { } setBalance(data.newBalance); setSuccessMessage(data.message); + if (isCustomAmount) setCustomAmount(''); await fetchTransactions(); } catch (err: unknown) { if (err instanceof Error) { @@ -136,9 +142,21 @@ const DashboardPage: NextPage = () => { setError('Unbekannter Fehler beim Hinzufügen der Ausgabe aufgetreten.'); } console.error("Fehler beim Hinzufügen:", err); + } finally { + if (isCustomAmount) setIsSubmittingCustom(false); } }; + const handleCustomAmountSubmit = (e: React.FormEvent) => { + e.preventDefault(); + const amountNumber = parseFloat(customAmount); + if (isNaN(amountNumber) || amountNumber <= 0) { + setError("Bitte einen gültigen positiven Betrag eingeben."); + return; + } + handleAddExpense(amountNumber, true); + }; + const predefinedAmounts = [0.10, 0.20, 0.50, 1.00, 2.00]; if (status === 'loading') { @@ -159,13 +177,12 @@ const DashboardPage: NextPage = () => { return (
- {/* Header angepasst für mobile Ansicht */}
-
{/* Div für Willkommenstext */} +

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

Deine Strichliste

-
{/* Container für Buttons */} +
{session?.user?.role === 'admin' && ( ))}
+
+ +
+ setCustomAmount(e.target.value)} + className="focus:ring-indigo-500 focus:border-indigo-500 flex-1 block w-full rounded-none rounded-l-md sm:text-sm border-gray-300 dark:border-gray-600 dark:bg-slate-700 dark:text-gray-100 px-3 py-2" + placeholder="z.B. 1.25" + /> + +
+