From 8011913c4d568ae49e0f6fd1cc6f2a9f8664a3c7 Mon Sep 17 00:00:00 2001 From: kayela Date: Tue, 6 Jan 2026 12:05:34 -0600 Subject: [PATCH] Enhance Admin Dashboard and Members Directory with improved layout, pagination, and member details display. Applied requested changes to UI --- src/components/AdminSidebar.js | 6 +- src/components/ui/tabs.jsx | 4 +- src/pages/BoardOfDirectors.js | 86 +++---- src/pages/admin/AdminDashboard.js | 320 +++++++++++++------------- src/pages/admin/AdminStaff.js | 2 +- src/pages/members/MembersDirectory.js | 244 +++++++++++++------- 6 files changed, 377 insertions(+), 285 deletions(-) diff --git a/src/components/AdminSidebar.js b/src/components/AdminSidebar.js index 8e03656..fc70405 100644 --- a/src/components/AdminSidebar.js +++ b/src/components/AdminSidebar.js @@ -22,7 +22,7 @@ import { Scale, HardDrive, Repeat, - Heart + Heart, } from 'lucide-react'; const AdminSidebar = ({ isOpen, onToggle, isMobile }) => { @@ -369,7 +369,7 @@ const AdminSidebar = ({ isOpen, onToggle, isMobile }) => { {/* User Section */}
{isOpen && user && ( -
+
{user.first_name?.[0]}{user.last_name?.[0]} @@ -383,6 +383,8 @@ const AdminSidebar = ({ isOpen, onToggle, isMobile }) => {

+ +
)} diff --git a/src/components/ui/tabs.jsx b/src/components/ui/tabs.jsx index f37e97a..318a8a9 100644 --- a/src/components/ui/tabs.jsx +++ b/src/components/ui/tabs.jsx @@ -9,7 +9,7 @@ const TabsList = React.forwardRef(({ className, ...props }, ref) => ( ( { ]; const boardMembers = [ - { name: 'Danita Cole' }, - { name: 'Roxanne Cherico' }, - { name: 'Lucretia Copeland' }, - { name: 'Julie Fischer' } + { name: 'Danita Cole', title: 'Director' }, + { name: 'Roxanne Cherico', title: 'Director' }, + { name: 'Lucretia Copeland', title: 'Director' }, + { name: 'Julie Fischer', title: 'Director' } ]; @@ -112,50 +112,50 @@ const BoardOfDirectors = () => { Our elections take place at our December holiday social. Here are some things to know if you are thinking about serving on the Board of Directors.

{/* card */} - -
    -
  1. - Nominations are due by November 1. Nomination Form:{' '} - - Click Here - -
  2. -
  3. Nominees must have been a member for at least 1 year, however it is possible to be elected prior to 1 year, but start the term on the 1 year anniversary.
  4. -
  5. Officer positions are only available to current directors.
  6. + +
      +
    1. + Nominations are due by November 1. Nomination Form:{' '} + + Click Here + +
    2. +
    3. Nominees must have been a member for at least 1 year, however it is possible to be elected prior to 1 year, but start the term on the 1 year anniversary.
    4. +
    5. Officer positions are only available to current directors.
    6. -
    7. Each director shall serve a 2-year term.
    8. +
    9. Each director shall serve a 2-year term.
    10. -
    11. The time commitment is approximately 1–2 hours per week.
    12. +
    13. The time commitment is approximately 1–2 hours per week.
    14. -
    15. - The tasks that directors perform depend on individual interests. Recent - tasks include researching how to obtain an extra PO Box key, ordering - Welcome Team name tags, taking pictures at events, researching new venues - for holiday socials, and monitoring Facebook posts. For more information - about director duties, see Article 2 of the bylaws in the Members Only - section of the website:  - - https://loaftx.org/bylaws/ - -
    16. +
    17. + The tasks that directors perform depend on individual interests. Recent + tasks include researching how to obtain an extra PO Box key, ordering + Welcome Team name tags, taking pictures at events, researching new venues + for holiday socials, and monitoring Facebook posts. For more information + about director duties, see Article 2 of the bylaws in the Members Only + section of the website:  + + https://loaftx.org/bylaws/ + +
    18. -
    19. - Directors must attend Board meetings held on the second Thursday of each - month at 6:30pm via Zoom. -
    20. +
    21. + Directors must attend Board meetings held on the second Thursday of each + month at 6:30pm via Zoom. +
    22. -
    23. - We are a fun group, and we would love for you to join us in providing this - service for our community. -
    24. -
    -
    +
  7. + We are a fun group, and we would love for you to join us in providing this + service for our community. +
  8. +
+
diff --git a/src/pages/admin/AdminDashboard.js b/src/pages/admin/AdminDashboard.js index c965695..44f9ed5 100644 --- a/src/pages/admin/AdminDashboard.js +++ b/src/pages/admin/AdminDashboard.js @@ -4,7 +4,7 @@ import api from '../../utils/api'; import { Card } from '../../components/ui/card'; import { Button } from '../../components/ui/button'; import { Badge } from '../../components/ui/badge'; -import { Users, Calendar, Clock, CheckCircle, Mail, AlertCircle } from 'lucide-react'; +import { Users, Calendar, Clock, CheckCircle, Mail, AlertCircle,Globe } from 'lucide-react'; const AdminDashboard = () => { const [stats, setStats] = useState({ @@ -42,8 +42,8 @@ const AdminDashboard = () => { }).map(u => ({ ...u, totalReminders: (u.email_verification_reminders_sent || 0) + - (u.event_attendance_reminders_sent || 0) + - (u.payment_reminders_sent || 0) + (u.event_attendance_reminders_sent || 0) + + (u.payment_reminders_sent || 0) })).sort((a, b) => b.totalReminders - a.totalReminders).slice(0, 5); // Top 5 setUsersNeedingAttention(needingAttention); @@ -56,173 +56,183 @@ const AdminDashboard = () => { return ( <> -
-

- Admin Dashboard -

-

- Manage users, events, and membership applications. -

+
+
+

+ Admin Dashboard +

+

+ Manage users, events, and membership applications. +

+
+ + +
- {/* Stats Grid */} -
- -
-
- -
+ {/* Stats Grid */} +
+ +
+
+
-

- {loading ? '-' : stats.totalMembers} -

-

Total Members

- +
+

+ {loading ? '-' : stats.totalMembers} +

+

Total Members

+
- -
-
- -
+ +
+
+
-

- {loading ? '-' : stats.pendingValidations} -

-

Pending Validations

- +
+

+ {loading ? '-' : stats.pendingValidations} +

+

Pending Validations

+
- -
-
- -
+ +
+
+
-

- {loading ? '-' : stats.activeMembers} +

+

+ {loading ? '-' : stats.activeMembers} +

+

Active Members

+
+
+ + {/* Quick Actions */} +
+ + + +

+ Manage Members +

+

+ View and manage paying members and their subscription status.

-

Active Members

+
-
+ - {/* Quick Actions */} -
- - - -

- Manage Members -

-

- View and manage paying members and their subscription status. -

- -
- + + + +

+ Validation Queue +

+

+ Review and validate pending membership applications. +

+ +
+ +
- - - -

- Validation Queue -

-

- Review and validate pending membership applications. -

- -
- -
- - {/* Users Needing Attention Widget */} - {usersNeedingAttention.length > 0 && ( -
- -
-
- -
-
-

- Members Needing Personal Outreach -

-

- These members have received multiple reminder emails. Consider calling them directly. -

-
+ {/* Users Needing Attention Widget */} + {usersNeedingAttention.length > 0 && ( +
+ +
+
+
- -
- {usersNeedingAttention.map(user => ( - -
-
-
-
-

- {user.first_name} {user.last_name} -

- - {user.totalReminders} reminder{user.totalReminders !== 1 ? 's' : ''} - -
-
-

Email: {user.email}

-

Phone: {user.phone || 'N/A'}

-

Status: {user.status.replace('_', ' ')}

- {user.email_verification_reminders_sent > 0 && ( -

- - {user.email_verification_reminders_sent} email verification reminder{user.email_verification_reminders_sent !== 1 ? 's' : ''} -

- )} - {user.event_attendance_reminders_sent > 0 && ( -

- - {user.event_attendance_reminders_sent} event reminder{user.event_attendance_reminders_sent !== 1 ? 's' : ''} -

- )} - {user.payment_reminders_sent > 0 && ( -

- - {user.payment_reminders_sent} payment reminder{user.payment_reminders_sent !== 1 ? 's' : ''} -

- )} -
-
- -
-
- - ))} -
- -
+
+

+ Members Needing Personal Outreach +

- 💡 Tip for helping older members: Many of our members are older ladies who may struggle with email. - A friendly phone call can help them complete the registration process and feel more welcomed to the community. + These members have received multiple reminder emails. Consider calling them directly.

- -
- )} +
+ +
+ {usersNeedingAttention.map(user => ( + +
+
+
+
+

+ {user.first_name} {user.last_name} +

+ + {user.totalReminders} reminder{user.totalReminders !== 1 ? 's' : ''} + +
+
+

Email: {user.email}

+

Phone: {user.phone || 'N/A'}

+

Status: {user.status.replace('_', ' ')}

+ {user.email_verification_reminders_sent > 0 && ( +

+ + {user.email_verification_reminders_sent} email verification reminder{user.email_verification_reminders_sent !== 1 ? 's' : ''} +

+ )} + {user.event_attendance_reminders_sent > 0 && ( +

+ + {user.event_attendance_reminders_sent} event reminder{user.event_attendance_reminders_sent !== 1 ? 's' : ''} +

+ )} + {user.payment_reminders_sent > 0 && ( +

+ + {user.payment_reminders_sent} payment reminder{user.payment_reminders_sent !== 1 ? 's' : ''} +

+ )} +
+
+ +
+
+ + ))} +
+ +
+

+ 💡 Tip for helping older members: Many of our members are older ladies who may struggle with email. + A friendly phone call can help them complete the registration process and feel more welcomed to the community. +

+
+
+
+ )} ); }; diff --git a/src/pages/admin/AdminStaff.js b/src/pages/admin/AdminStaff.js index 289f908..602f2dc 100644 --- a/src/pages/admin/AdminStaff.js +++ b/src/pages/admin/AdminStaff.js @@ -118,7 +118,7 @@ const AdminStaff = () => { const getStatusBadge = (status) => { const config = { active: { label: 'Active', className: 'bg-[#81B29A] text-white' }, - inactive: { label: 'Inactive', className: 'bg-gray-400 text-white' } + inactive: { label: 'Inactive', className: 'bg-gray-400 text-white ' } }; const statusConfig = config[status] || config.inactive; diff --git a/src/pages/members/MembersDirectory.js b/src/pages/members/MembersDirectory.js index 01940a6..411df25 100644 --- a/src/pages/members/MembersDirectory.js +++ b/src/pages/members/MembersDirectory.js @@ -24,6 +24,8 @@ const MembersDirectory = () => { const [selectedMember, setSelectedMember] = useState(null); const [profileDialogOpen, setProfileDialogOpen] = useState(false); const { toast } = useToast(); + const [currentPage, setCurrentPage] = useState(1); + const pageSize = 12; useEffect(() => { fetchMembers(); @@ -33,6 +35,10 @@ const MembersDirectory = () => { filterMembers(); }, [searchQuery, members]); + useEffect(() => { + setCurrentPage(1); + }, [searchQuery, members]); + const fetchMembers = async () => { try { const response = await api.get('/members/directory'); @@ -66,6 +72,14 @@ const MembersDirectory = () => { setFilteredMembers(filtered); }; + const totalPages = Math.max(1, Math.ceil(filteredMembers.length / pageSize)); + + const pageStart = (currentPage - 1) * pageSize; + + const paginatedMembers = filteredMembers.slice(pageStart, pageStart + pageSize); + + const totalMembers = members.length; + const getInitials = (firstName, lastName) => { return `${firstName.charAt(0)}${lastName.charAt(0)}`.toUpperCase(); }; @@ -97,9 +111,15 @@ const MembersDirectory = () => { if (!dateString) return null; return new Date(dateString).toLocaleDateString('en-US', { month: 'long', day: 'numeric' }); }; - + const Border = ({ yaxis = false }) => { + return ( + yaxis ? +
+ :
+ ) + } const MemberCard = ({ member }) => ( - + {/* Profile Photo */}
{member.profile_photo_url ? ( @@ -259,39 +279,48 @@ const MembersDirectory = () => { ); return ( -
+
-
- {/* Header */} -
-

- Members Directory -

-

- Connect with fellow LOAF members in our community. -

-
+
- {/* Search Bar */} -
-
- - setSearchQuery(e.target.value)} - className="pl-12 pr-4 py-6 text-lg border-[#ddd8eb] rounded-xl focus:border-[#664fa3] focus:ring-[#664fa3]" - style={{ fontFamily: "'Nunito Sans', sans-serif" }} - /> -
- {searchQuery && ( -

- Found {filteredMembers.length} {filteredMembers.length === 1 ? 'member' : 'members'} + {/* Header and Search bar */} +

+ + {/* Header */} +
+

+ LOAF Members +

+

+ Number of current memebers in the directory: {totalMembers}

- )} +
+ + {/* Search Bar */} +
+
+ + setSearchQuery(e.target.value)} + className="pl-12 pr-4 py-6 text-3xl font-medium bg-background border-foreground rounded-full focus:border-[#664fa3] focus:ring-[#664fa3]" + style={{ fontFamily: "'Nunito Sans', sans-serif" }} + /> +
+ {searchQuery && ( +

+ Found {filteredMembers.length} {filteredMembers.length === 1 ? 'member' : 'members'} +

+ )} +
+
+ {/* Border Decoration */} + + {/* Members Grid */} {loading ? ( @@ -300,7 +329,7 @@ const MembersDirectory = () => {
) : filteredMembers.length > 0 ? (
- {filteredMembers.map((member) => ( + {paginatedMembers.map((member) => ( ))}
@@ -318,6 +347,11 @@ const MembersDirectory = () => {
)} + + + {/* Border Decoration */} + + {/* Info Card */} {!loading && members.length > 0 && ( @@ -465,67 +499,113 @@ const MembersDirectory = () => { {/* Social Media */} {(selectedMember.social_media_facebook || selectedMember.social_media_instagram || selectedMember.social_media_twitter || selectedMember.social_media_linkedin) && ( -
-

- Connect on Social Media -

-
- {selectedMember.social_media_facebook && ( - - - - )} +
+

+ Connect on Social Media +

+
+ {selectedMember.social_media_facebook && ( + + + + )} - {selectedMember.social_media_instagram && ( - - - - )} + {selectedMember.social_media_instagram && ( + + + + )} - {selectedMember.social_media_twitter && ( - - - - )} + {selectedMember.social_media_twitter && ( + + + + )} - {selectedMember.social_media_linkedin && ( - - - - )} + {selectedMember.social_media_linkedin && ( + + + + )} +
-
- )} + )}
)} + + + {/* Pagination */} + {!loading && filteredMembers.length > 0 && ( +
+

+ Showing {pageStart + 1}–{Math.min(pageStart + pageSize, filteredMembers.length)} of {filteredMembers.length} +

+
+ + + {Array.from({ length: totalPages }, (_, index) => { + const pageNumber = index + 1; + const isActive = pageNumber === currentPage; + return ( + + ); + })} + + +
+
+ )} +
);