From a93e2aa863b078180f688f7fa20b0c69d38a2d99 Mon Sep 17 00:00:00 2001 From: kayela Date: Wed, 7 Jan 2026 11:01:54 -0600 Subject: [PATCH 01/21] Theme provider --- src/components/AdminSidebar.js | 80 ++++++++++++++++++++++++---------- src/index.js | 10 ++++- src/layouts/AdminLayout.js | 5 ++- 3 files changed, 71 insertions(+), 24 deletions(-) diff --git a/src/components/AdminSidebar.js b/src/components/AdminSidebar.js index fc70405..ffc10bf 100644 --- a/src/components/AdminSidebar.js +++ b/src/components/AdminSidebar.js @@ -1,5 +1,6 @@ import React, { useState, useEffect } from 'react'; import { Link, useLocation, useNavigate } from 'react-router-dom'; +import { useTheme } from 'next-themes'; import { useAuth } from '../context/AuthContext'; import api from '../utils/api'; import { Badge } from './ui/badge'; @@ -23,16 +24,20 @@ import { HardDrive, Repeat, Heart, + Sun, + Moon, } from 'lucide-react'; const AdminSidebar = ({ isOpen, onToggle, isMobile }) => { const location = useLocation(); const navigate = useNavigate(); const { user, logout } = useAuth(); + const { theme, setTheme } = useTheme(); const [pendingCount, setPendingCount] = useState(0); const [storageUsed, setStorageUsed] = useState(0); const [storageLimit, setStorageLimit] = useState(0); const [storagePercentage, setStoragePercentage] = useState(0); + const isDark = theme === 'dark'; // Fetch pending approvals count useEffect(() => { @@ -86,6 +91,10 @@ const AdminSidebar = ({ isOpen, onToggle, isMobile }) => { navigate('/login'); }; + const handleThemeToggle = () => { + setTheme(isDark ? 'light' : 'dark'); + }; + const navItems = [ { name: 'Dashboard', @@ -211,7 +220,7 @@ const AdminSidebar = ({ isOpen, onToggle, isMobile }) => { > {/* Active border */} {active && !item.disabled && ( -
+
)} @@ -225,7 +234,7 @@ const AdminSidebar = ({ isOpen, onToggle, isMobile }) => { )} {item.badge > 0 && !item.disabled && ( - + {item.badge} )} @@ -234,7 +243,7 @@ const AdminSidebar = ({ isOpen, onToggle, isMobile }) => { {/* Badge when collapsed */} {!isOpen && item.badge > 0 && !item.disabled && ( -
+
{item.badge}
)} @@ -242,7 +251,7 @@ const AdminSidebar = ({ isOpen, onToggle, isMobile }) => { {/* Tooltip when collapsed */} {!isOpen && ( -
+
{item.name} {item.badge > 0 && ` (${item.badge})`}
@@ -274,10 +283,10 @@ const AdminSidebar = ({ isOpen, onToggle, isMobile }) => { /> {isOpen && (
-

+

Admin

-

+

View Public Site

@@ -289,11 +298,11 @@ const AdminSidebar = ({ isOpen, onToggle, isMobile }) => { aria-label={isOpen ? 'Collapse sidebar' : 'Expand sidebar'} > {isMobile ? ( - + ) : isOpen ? ( - + ) : ( - + )}
@@ -306,7 +315,7 @@ const AdminSidebar = ({ isOpen, onToggle, isMobile }) => { {/* MEMBERSHIP Section */} {isOpen && (
-

+

Membership

@@ -320,7 +329,7 @@ const AdminSidebar = ({ isOpen, onToggle, isMobile }) => { {/* FINANCIALS Section */} {isOpen && (
-

+

Financials

@@ -334,7 +343,7 @@ const AdminSidebar = ({ isOpen, onToggle, isMobile }) => { {/* EVENTS & MEDIA Section */} {isOpen && (
-

+

Events & Media

@@ -347,7 +356,7 @@ const AdminSidebar = ({ isOpen, onToggle, isMobile }) => { {/* DOCUMENTATION Section */} {isOpen && (
-

+

Documentation

@@ -375,10 +384,10 @@ const AdminSidebar = ({ isOpen, onToggle, isMobile }) => { {user.first_name?.[0]}{user.last_name?.[0]}
-

+

{user.first_name} {user.last_name}

-

+

{user.role}

@@ -388,13 +397,40 @@ const AdminSidebar = ({ isOpen, onToggle, isMobile }) => {
)} + {/* Theme Toggle */} +
+ + {!isOpen && ( +
+ {isDark ? 'Light mode' : 'Dark mode'} +
+ )} +
+ {/* Storage Usage Widget */}
{isOpen ? (
- Storage Usage - {storagePercentage}% + Storage Usage + {storagePercentage}%
{ style={{ width: `${storagePercentage}%` }} />
-

+

{formatBytes(storageUsed)} / {formatBytes(storageLimit)}

@@ -414,13 +450,13 @@ const AdminSidebar = ({ isOpen, onToggle, isMobile }) => {
90 ? 'text-red-500' : storagePercentage > 75 ? 'text-yellow-500' : - 'text-[#664fa3]' + 'text-muted-foreground' }`} /> {storagePercentage > 75 && (
)} {/* Tooltip */} -
+
Storage: {storagePercentage}%
@@ -433,7 +469,7 @@ const AdminSidebar = ({ isOpen, onToggle, isMobile }) => { onClick={handleLogout} className={` flex items-center gap-3 px-4 py-3 rounded-lg w-full - text-[#ff9e77] hover:bg-[#ff9e77]/10 transition-colors + text-accent hover:bg-accent/10 transition-colors ${!isOpen && 'justify-center'} `} > @@ -444,7 +480,7 @@ const AdminSidebar = ({ isOpen, onToggle, isMobile }) => { {/* Logout tooltip when collapsed */} {!isOpen && (
-
+
Logout
diff --git a/src/index.js b/src/index.js index 79282d0..a4a0d50 100644 --- a/src/index.js +++ b/src/index.js @@ -1,5 +1,6 @@ import React from 'react'; import ReactDOM from 'react-dom/client'; +import { ThemeProvider } from 'next-themes'; import '@fontsource/fraunces/600.css'; import '@fontsource/dm-sans/400.css'; import '@fontsource/dm-sans/700.css'; @@ -9,6 +10,13 @@ import App from './App'; const root = ReactDOM.createRoot(document.getElementById('root')); root.render( - + + + ); diff --git a/src/layouts/AdminLayout.js b/src/layouts/AdminLayout.js index 668f6b9..a93df83 100644 --- a/src/layouts/AdminLayout.js +++ b/src/layouts/AdminLayout.js @@ -1,9 +1,12 @@ import React, { useState, useEffect } from 'react'; +import { useTheme } from 'next-themes'; import AdminSidebar from '../components/AdminSidebar'; const AdminLayout = ({ children }) => { const [sidebarOpen, setSidebarOpen] = useState(true); const [isMobile, setIsMobile] = useState(false); + const { theme } = useTheme(); + const isDark = theme === 'dark'; // Initialize sidebar state from localStorage useEffect(() => { @@ -43,7 +46,7 @@ const AdminLayout = ({ children }) => { }; return ( -
+
{/* Sidebar */} Date: Mon, 12 Jan 2026 20:10:33 -0600 Subject: [PATCH 02/21] refactor: update styles in MembersDirectory and NewsletterArchive for consistency and improved theming - Updated color classes to use CSS variables for better maintainability and theming. - Refactored component styles in MembersDirectory.js to enhance visual consistency. - Adjusted loading states and empty states in NewsletterArchive.js for improved user experience. - Added new brand colors to tailwind.config.js for future use. --- README.md | 243 +++++++++----- src/components/AddToCalendarButton.js | 16 +- src/components/AdminSidebar.js | 24 +- src/components/AttendanceDialog.js | 18 +- src/components/ChangePasswordDialog.js | 18 +- src/components/ConfirmationDialog.js | 24 +- src/components/CreateMemberDialog.js | 50 +-- src/components/CreateStaffDialog.js | 30 +- src/components/ImportMembersDialog.js | 65 ++-- src/components/InviteStaffDialog.js | 34 +- src/components/MemberFooter.js | 4 +- src/components/MemberRoute.js | 4 +- src/components/Navbar.js | 60 ++-- src/components/PaymentActivationDialog.js | 78 ++--- src/components/PendingInvitationsTable.js | 50 +-- src/components/PlanDialog.js | 34 +- src/components/PublicFooter.js | 28 +- src/components/PublicNavbar.js | 44 +-- src/components/RejectionDialog.js | 20 +- src/components/WordPressImportWizard.js | 84 ++--- .../registration/RegistrationStep1.js | 26 +- .../registration/RegistrationStep2.js | 18 +- .../registration/RegistrationStep3.js | 40 +-- .../registration/RegistrationStep4.js | 20 +- .../registration/RegistrationStepIndicator.js | 16 +- src/components/ui/password-input.jsx | 18 +- src/components/ui/tabs.jsx | 2 +- src/index.css | 129 ++++++-- src/pages/AcceptInvitation.js | 124 ++++---- src/pages/BecomeMember.js | 42 +-- src/pages/BoardOfDirectors.js | 32 +- src/pages/ChangePasswordRequired.js | 28 +- src/pages/ContactUs.js | 50 +-- src/pages/Dashboard.js | 200 ++++++------ src/pages/Donate.js | 198 ++++++------ src/pages/DonationSuccess.js | 30 +- src/pages/EventDetails.js | 74 +++-- src/pages/Events.js | 34 +- src/pages/ForgotPassword.js | 36 +-- src/pages/History.js | 86 ++--- src/pages/Landing.js | 28 +- src/pages/Login.js | 22 +- src/pages/MissionValues.js | 12 +- src/pages/NotFound.js | 22 +- src/pages/PaymentCancel.js | 42 +-- src/pages/PaymentSuccess.js | 48 +-- src/pages/Plans.js | 107 ++++--- src/pages/PrivacyPolicy.js | 28 +- src/pages/Profile.js | 152 ++++----- src/pages/Register.js | 20 +- src/pages/ResetPassword.js | 30 +- src/pages/Resources.js | 30 +- src/pages/TermsOfService.js | 98 +++--- src/pages/VerifyEmail.js | 26 +- src/pages/admin/AdminBylaws.js | 50 +-- src/pages/admin/AdminDashboard.js | 76 ++--- src/pages/admin/AdminDonations.js | 124 ++++---- src/pages/admin/AdminEventAttendance.js | 151 +++++---- src/pages/admin/AdminEvents.js | 297 +++++++++--------- src/pages/admin/AdminFinancials.js | 32 +- src/pages/admin/AdminGallery.js | 58 ++-- src/pages/admin/AdminMembers.js | 90 +++--- src/pages/admin/AdminNewsletters.js | 36 +-- src/pages/admin/AdminPermissions.js | 64 ++-- src/pages/admin/AdminPlans.js | 96 +++--- src/pages/admin/AdminStaff.js | 75 +++-- src/pages/admin/AdminSubscriptions.js | 183 ++++++----- src/pages/admin/AdminUserView.js | 92 +++--- src/pages/admin/AdminValidations.js | 66 ++-- src/pages/members/Bylaws.js | 48 +-- src/pages/members/EventGallery.js | 48 +-- src/pages/members/Financials.js | 34 +- src/pages/members/MemberCalendar.js | 92 +++--- src/pages/members/MemberProfile.js | 88 +++--- src/pages/members/MembersDirectory.js | 172 +++++----- src/pages/members/NewsletterArchive.js | 42 +-- tailwind.config.js | 47 ++- 77 files changed, 2519 insertions(+), 2338 deletions(-) diff --git a/README.md b/README.md index 2589962..3c2f651 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,7 @@ npm install ``` **Key Dependencies:** + - `react@19.0.0` - UI library - `react-router-dom@7.5.1` - Routing - `axios@1.8.4` - HTTP client @@ -57,6 +58,7 @@ REACT_APP_BACKEND_URL=http://localhost:8000 ``` **Important:** + - All environment variables must start with `REACT_APP_` - Restart development server after changing `.env` @@ -71,6 +73,7 @@ npm start ``` **Development server will be available at:** + - Frontend: http://localhost:3000 - Auto-reloads on file changes @@ -154,31 +157,33 @@ frontend/ ### Core Technologies -| Technology | Purpose | Version | -|------------|---------|---------| -| React | UI library | 19.0.0 | -| React Router | Client-side routing | 7.5.1 | -| Axios | HTTP requests | 1.8.4 | -| React Hook Form | Form handling | 7.56.2 | -| Zod | Schema validation | 3.24.4 | -| Tailwind CSS | CSS framework | 3.4.17 | -| Radix UI | Component library | Latest | -| Lucide React | Icons | 0.507.0 | -| Sonner | Toast notifications | 1.7.4 | +| Technology | Purpose | Version | +| --------------- | ------------------- | ------- | +| React | UI library | 19.0.0 | +| React Router | Client-side routing | 7.5.1 | +| Axios | HTTP requests | 1.8.4 | +| React Hook Form | Form handling | 7.56.2 | +| Zod | Schema validation | 3.24.4 | +| Tailwind CSS | CSS framework | 3.4.17 | +| Radix UI | Component library | Latest | +| Lucide React | Icons | 0.507.0 | +| Sonner | Toast notifications | 1.7.4 | ### Key Features #### Authentication System **Global Auth Context** (`src/context/AuthContext.js`): + - JWT token storage in localStorage - Automatic token injection via Axios interceptor - User state management - Protected route wrapper **Usage:** + ```jsx -import { useAuth } from '../context/AuthContext'; +import { useAuth } from "../context/AuthContext"; function MyComponent() { const { user, login, logout, isAuthenticated } = useAuth(); @@ -190,6 +195,7 @@ function MyComponent() { #### Protected Routes **PrivateRoute Wrapper:** + ```jsx @@ -208,61 +214,68 @@ function MyComponent() { #### API Integration **Axios Instance** (`src/utils/api.js`): + - Automatic JWT token injection - Request/response interceptors - Error handling - Base URL configuration **Usage:** + ```jsx -import api from '../utils/api'; +import api from "../utils/api"; // GET request -const response = await api.get('/members/profile'); +const response = await api.get("/members/profile"); // POST request with data -const response = await api.post('/admin/users/123/reject', { - reason: 'Incomplete application' +const response = await api.post("/admin/users/123/reject", { + reason: "Incomplete application", }); // File upload const formData = new FormData(); -formData.append('file', file); -const response = await api.post('/members/profile/upload-photo', formData, { - headers: { 'Content-Type': 'multipart/form-data' } +formData.append("file", file); +const response = await api.post("/members/profile/upload-photo", formData, { + headers: { "Content-Type": "multipart/form-data" }, }); ``` #### Form Handling **React Hook Form + Zod Pattern:** + ```jsx -import { useForm } from 'react-hook-form'; -import { zodResolver } from '@hookform/resolvers/zod'; -import { z } from 'zod'; +import { useForm } from "react-hook-form"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { z } from "zod"; const schema = z.object({ - email: z.string().email('Invalid email address'), - password: z.string().min(8, 'Password must be at least 8 characters') + email: z.string().email("Invalid email address"), + password: z.string().min(8, "Password must be at least 8 characters"), }); function LoginForm() { - const { register, handleSubmit, formState: { errors } } = useForm({ - resolver: zodResolver(schema) + const { + register, + handleSubmit, + formState: { errors }, + } = useForm({ + resolver: zodResolver(schema), }); const onSubmit = async (data) => { try { - await api.post('/auth/login', data); - toast.success('Login successful!'); + await api.post("/auth/login", data); + toast.success("Login successful!"); } catch (error) { - toast.error(error.response?.data?.detail || 'Login failed'); + toast.error(error.response?.data?.detail || "Login failed"); } }; return (
- + {errors.email && {errors.email.message}} {/* ... */}
@@ -273,18 +286,19 @@ function LoginForm() { #### Toast Notifications **Using Sonner:** + ```jsx -import { toast } from 'sonner'; +import { toast } from "sonner"; // Success -toast.success('Profile updated successfully!'); +toast.success("Profile updated successfully!"); // Error -toast.error('Failed to upload photo'); +toast.error("Failed to upload photo"); // Custom -toast('Processing...', { - description: 'Please wait while we process your request' +toast("Processing...", { + description: "Please wait while we process your request", }); ``` @@ -293,11 +307,13 @@ toast('Processing...', { #### Public Pages **Landing.js** + - Hero section with LOAF branding - Feature highlights - Call-to-action buttons **Register.js** (388 lines) + - 4-step registration wizard: 1. Basic Info (email, password, name) 2. Personal Details (phone, address, DOB) @@ -308,6 +324,7 @@ toast('Processing...', { - Email verification trigger **Login.js** + - Email/password authentication - JWT token storage - Remember me functionality @@ -316,12 +333,14 @@ toast('Processing...', { #### Member Pages **Dashboard.js** + - Welcome message with user name - Upcoming events preview - Membership status card - Quick action buttons **Profile.js** + - Profile photo upload with Avatar component - File validation (image types, 50MB max) - Personal information editing @@ -329,12 +348,14 @@ toast('Processing...', { - Save changes with API update **Events.js** + - Event listing with filters - Search functionality - Upcoming/past events toggle - Event cards with cover images **EventDetails.js** + - Full event information - RSVP form (Yes/No/Maybe) - Attendance confirmation @@ -343,36 +364,42 @@ toast('Processing...', { #### Admin Pages **AdminDashboard.js** + - Statistics overview - Pending validations count - Recent activity feed - Quick links to management pages **AdminUsers.js** + - User listing with search/filters - Status badges - Bulk operations - CSV export **AdminValidations.js** + - Pending user applications - Approve button - **Reject button with RejectionDialog** - Validation workflow management **AdminSubscriptions.js** + - Subscription listing - Status filters (active, cancelled, expired) - **CSV export dropdown** (Export All / Export Current View) - Edit subscription dialog **AdminDonations.js** (400+ lines) + - 4 stats cards: Total, Member, Public, Total Amount - Filters: type, status, date range, search - Responsive table with mobile cards - **CSV export functionality** **AdminEvents.js** + - Event management interface - Create/edit events - Publish/unpublish toggle @@ -385,40 +412,50 @@ toast('Processing...', { **45+ UI Components** in `src/components/ui/`: **Form Components:** + - Button, Input, Textarea, Checkbox, Radio - Select, Label, Form **Layout Components:** + - Card, Dialog, Sheet (Drawer), Popover - Dropdown Menu, Tabs, Accordion **Feedback Components:** + - Alert, Badge, Toast (Sonner) - Progress, Skeleton, Spinner **Navigation:** + - Navigation Menu, Breadcrumb, Pagination **Usage Example:** -```jsx -import { Button } from './components/ui/button'; -import { Dialog, DialogContent, DialogTitle } from './components/ui/dialog'; -import { Select, SelectTrigger, SelectContent, SelectItem } from './components/ui/select'; - +```jsx +import { Button } from "./components/ui/button"; +import { Dialog, DialogContent, DialogTitle } from "./components/ui/dialog"; +import { + Select, + SelectTrigger, + SelectContent, + SelectItem, +} from "./components/ui/select"; + +; ``` ### Admin Sidebar Features **Logo Integration:** + - LOAF logo in header - Shows logo + "Admin" text when expanded - Logo only when collapsed - Smooth transition animations **Navigation Groups:** + - **Dashboard** (standalone) - **MEMBERSHIP** - Staff, Members, Validations - **FINANCIALS** - Plans, Subscriptions, Donations @@ -433,6 +470,7 @@ import { Select, SelectTrigger, SelectContent, SelectItem } from './components/u ### Color Palette **LOAF Brand Colors:** + ```css --primary: #422268 /* Deep Purple - Primary brand */ --secondary: #664fa3 /* Light Purple - Secondary elements */ @@ -443,21 +481,24 @@ import { Select, SelectTrigger, SelectContent, SelectItem } from './components/u ``` **Usage in Tailwind:** + ```jsx -
-

Accent Text

-

Secondary Text

+
+

Accent Text

+

Secondary Text

``` ### Typography **Font Families:** + - **Headings**: 'Inter', sans-serif - **Body**: 'Nunito Sans', sans-serif - **Code**: 'Fira Code', monospace (if needed) **Font Sizes:** + ```css text-xs → 0.75rem (12px) text-sm → 0.875rem (14px) @@ -469,11 +510,12 @@ text-3xl → 1.875rem (30px) ``` **Usage:** + ```jsx -

+

Page Title

-

+

Body text

``` @@ -481,6 +523,7 @@ text-3xl → 1.875rem (30px) ### Spacing System **Tailwind Spacing Scale:** + ```css p-2 → 0.5rem (8px) p-4 → 1rem (16px) @@ -493,21 +536,23 @@ gap-2, gap-4, gap-6, gap-8 (same scale for flex/grid gaps) ### Component Styling Patterns **Cards:** + ```jsx - + {/* Content */} ``` **Buttons:** + ```jsx // Primary - // Secondary - @@ -518,17 +563,19 @@ gap-2, gap-4, gap-6, gap-8 (same scale for flex/grid gaps) ``` **Form Inputs:** + ```jsx - -