From 45bb31b98eee3db19678db15357eefb1a9bcfb90 Mon Sep 17 00:00:00 2001 From: adarshm11 Date: Sun, 5 Jul 2026 17:12:42 -0400 Subject: [PATCH 1/2] add portal for logging cash payments --- src/Components/Navbar/AdminNavbar.js | 10 ++ src/Pages/CashPayments/CashPayments.js | 142 +++++++++++++++++++++++++ src/Routes.js | 9 ++ src/config/config.example.json | 3 +- 4 files changed, 163 insertions(+), 1 deletion(-) create mode 100644 src/Pages/CashPayments/CashPayments.js diff --git a/src/Components/Navbar/AdminNavbar.js b/src/Components/Navbar/AdminNavbar.js index e18b75c14..9920a9411 100644 --- a/src/Components/Navbar/AdminNavbar.js +++ b/src/Components/Navbar/AdminNavbar.js @@ -134,6 +134,16 @@ export default function UserNavBar(props) { ) }, + { + title: 'Cash Payments', + route: '/cash-payments', + icon: ( + + + + + ) + }, ]; const renderRoutesForNavbar = (navbarLinks) => { diff --git a/src/Pages/CashPayments/CashPayments.js b/src/Pages/CashPayments/CashPayments.js new file mode 100644 index 000000000..e4ff53bd0 --- /dev/null +++ b/src/Pages/CashPayments/CashPayments.js @@ -0,0 +1,142 @@ +import React, { useState } from 'react'; +import config from '../../config/config.json'; + +const MIN_AMOUNT = 20; + +export default function CashPaymentsPage() { + const [name, setName] = useState(''); + const [email, setEmail] = useState(''); + const [amount, setAmount] = useState(''); + const [message, setMessage] = useState(''); + const [messageIsError, setMessageIsError] = useState(false); + const [submitting, setSubmitting] = useState(false); + + const parsedAmount = parseFloat(amount); + const isValidName = name.trim().length > 0; + const isValidEmail = email.trim().length > 0; + const isValidAmount = !Number.isNaN(parsedAmount) && parsedAmount >= MIN_AMOUNT; + const canSubmit = isValidName && isValidEmail && isValidAmount; + + async function handleSubmit(event) { + event.preventDefault(); + if (!canSubmit || submitting) { + return; + } + + const payload = { + payerName: name, + payerEmail: email, + amount: parsedAmount, + note: `SCE Membership: ${email}`, + transactionId: 'N/A - cash payment', + }; + + setSubmitting(true); + setMessage(''); + setMessageIsError(false); + + try { + const response = await fetch('/api/MembershipPayment/storePayment', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'x-api-key': config.MEMBERSHIP_PAYMENT_API_KEY, + }, + body: JSON.stringify(payload), + }); + const responseMessage = await response.text(); + setMessage(responseMessage); + setMessageIsError(!response.ok); + } catch { + setMessage('Failed to submit payment.'); + setMessageIsError(true); + } finally { + setSubmitting(false); + } + } + + function maybeRenderAmountError() { + if (amount === '') { + return null; + } + if (Number.isNaN(parsedAmount)) { + return

Please enter a valid number

; + } + if (parsedAmount < MIN_AMOUNT) { + return ( +

+ Amount must be at least ${MIN_AMOUNT.toFixed(2)} +

+ ); + } + } + + return ( +
+

+ Cash Payments +

+
+
+ +
+
+ +
+
+ +
+ + {message && ( +

+ {message} +

+ )} +
+
+ ); +} diff --git a/src/Routes.js b/src/Routes.js index bc42c2671..e8bfdb780 100644 --- a/src/Routes.js +++ b/src/Routes.js @@ -24,6 +24,7 @@ import CreateEventPage from './Pages/Events/CreateEventPage.js'; import EventRegistration from './Pages/Events/EventsRegistration.js'; import EditEventPage from './Pages/Events/EditEventPage.js'; import EventAttendeesDashboard from './Pages/Events/EventAttendeesDashboard.js'; +import CashPaymentsPage from './Pages/CashPayments/CashPayments.js'; // Declare an enum for permission check export const allowedIf = { @@ -206,6 +207,14 @@ export const officerOrAdminRoutes = [ inAdminNavbar: true, hideFromShortcutSuggestions: true }, + { + Component: CashPaymentsPage, + path: '/cash-payments', + pageName: 'Cash Payments', + allowedIf: allowedIf.OFFICER_OR_ADMIN, + redirect: '/', + inAdminNavbar: true + }, ...memberRoutes, ]; diff --git a/src/config/config.example.json b/src/config/config.example.json index 1afbb2902..ea374b3ff 100644 --- a/src/config/config.example.json +++ b/src/config/config.example.json @@ -4,5 +4,6 @@ "GOOGLE_API_CLIENT_ID": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX.apps.googleusercontent.com", "SCEvents": { "BASE_URL": "/api/scevents" - } + }, + "MEMBERSHIP_PAYMENT_API_KEY": "GO_AWAY_LOL" } From 1bfba36618c3026fd8bb71471c2abd8eac87a640 Mon Sep 17 00:00:00 2001 From: adarshm11 Date: Sun, 5 Jul 2026 18:21:06 -0400 Subject: [PATCH 2/2] clear input on successful request --- src/Pages/CashPayments/CashPayments.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Pages/CashPayments/CashPayments.js b/src/Pages/CashPayments/CashPayments.js index e4ff53bd0..7d1148b7f 100644 --- a/src/Pages/CashPayments/CashPayments.js +++ b/src/Pages/CashPayments/CashPayments.js @@ -47,6 +47,11 @@ export default function CashPaymentsPage() { const responseMessage = await response.text(); setMessage(responseMessage); setMessageIsError(!response.ok); + if (response.ok) { + setName(''); + setEmail(''); + setAmount(''); + } } catch { setMessage('Failed to submit payment.'); setMessageIsError(true);