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.
This commit is contained in:
2026-01-12 20:10:33 -06:00
parent a93e2aa863
commit 7694532d53
77 changed files with 2519 additions and 2338 deletions

View File

@@ -213,7 +213,7 @@ const AdminEventAttendance = () => {
if (loading) {
return (
<div className="flex items-center justify-center h-64">
<div className="text-[#664fa3]">Loading event data...</div>
<div className="text-var(--purple-lavender)">Loading event data...</div>
</div>
);
}
@@ -221,8 +221,8 @@ const AdminEventAttendance = () => {
if (!event) {
return (
<div className="text-center py-12">
<p className="text-[#664fa3] mb-4">Event not found</p>
<Button onClick={() => navigate('/admin/events')} className="bg-[#664fa3] hover:bg-[#422268] text-white rounded-xl">
<p className="text-var(--purple-lavender) mb-4">Event not found</p>
<Button onClick={() => navigate('/admin/events')} className="bg-var(--purple-lavender) hover:bg-var(--purple-ink) text-white rounded-xl">
Back to Events
</Button>
</div>
@@ -237,24 +237,24 @@ const AdminEventAttendance = () => {
<Button
onClick={() => navigate('/admin/events')}
variant="outline"
className="border-[#ddd8eb] text-[#664fa3] rounded-xl"
className="border-var(--neutral-800) text-var(--purple-lavender) rounded-xl"
style={{ fontFamily: "'Inter', sans-serif" }}
>
<ArrowLeft className="h-4 w-4 mr-2" />
Back to Events
</Button>
<div>
<h1 className="text-3xl font-semibold text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}>
<h1 className="text-3xl font-semibold text-var(--purple-ink)" style={{ fontFamily: "'Inter', sans-serif" }}>
Event Attendance
</h1>
<p className="text-[#664fa3] mt-1" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p className="text-var(--purple-lavender) mt-1" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Manage RSVPs and track attendance for this event
</p>
</div>
</div>
<Button
onClick={exportToCSV}
className="bg-[#81B29A] hover:bg-[#6a9a83] text-white rounded-xl"
className="bg-var(--green-light) hover:bg-var(--green-eucalyptus) text-white rounded-xl"
style={{ fontFamily: "'Inter', sans-serif" }}
>
<Download className="h-4 w-4 mr-2" />
@@ -263,13 +263,13 @@ const AdminEventAttendance = () => {
</div>
{/* Event Details Card */}
<Card className="p-6 bg-white border-[#ddd8eb] rounded-xl">
<Card className="p-6 bg-background border-var(--neutral-800) rounded-xl">
<div className="flex items-start justify-between">
<div>
<h2 className="text-2xl font-semibold text-[#422268] mb-2" style={{ fontFamily: "'Inter', sans-serif" }}>
<h2 className="text-2xl font-semibold text-var(--purple-ink) mb-2" style={{ fontFamily: "'Inter', sans-serif" }}>
{event.title}
</h2>
<div className="flex flex-wrap gap-4 text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<div className="flex flex-wrap gap-4 text-var(--purple-lavender)" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<div className="flex items-center gap-2">
<Calendar className="h-4 w-4" />
<span>{moment(event.start_at).format('MMMM D, YYYY [at] h:mm A')}</span>
@@ -282,7 +282,7 @@ const AdminEventAttendance = () => {
)}
</div>
</div>
<Badge className={`${event.published ? 'bg-[#81B29A]' : 'bg-[#ddd8eb]'} text-white px-3 py-1`}>
<Badge className={`${event.published ? 'bg-var(--green-light)' : 'bg-var(--neutral-800)'} text-white px-3 py-1`}>
{event.published ? 'Published' : 'Draft'}
</Badge>
</div>
@@ -290,70 +290,69 @@ const AdminEventAttendance = () => {
{/* Statistics Cards */}
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-5 gap-4">
<Card className="p-4 bg-white border-[#ddd8eb] rounded-xl">
<Card className="p-4 bg-background border-var(--neutral-800) rounded-xl">
<div className="flex items-center gap-3">
<Users className="h-8 w-8 text-[#664fa3]" />
<Users className="h-8 w-8 text-var(--purple-lavender)" />
<div>
<p className="text-sm text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Total RSVPs</p>
<p className="text-2xl font-semibold text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}>{stats.total}</p>
<p className="text-sm text-var(--purple-lavender)" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Total RSVPs</p>
<p className="text-2xl font-semibold text-var(--purple-ink)" style={{ fontFamily: "'Inter', sans-serif" }}>{stats.total}</p>
</div>
</div>
</Card>
<Card className="p-4 bg-white border-[#ddd8eb] rounded-xl">
<Card className="p-4 bg-background border-var(--neutral-800) rounded-xl">
<div className="flex items-center gap-3">
<UserCheck className="h-8 w-8 text-[#81B29A]" />
<UserCheck className="h-8 w-8 text-var(--green-light)" />
<div>
<p className="text-sm text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Yes</p>
<p className="text-2xl font-semibold text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}>{stats.yesCount}</p>
<p className="text-sm text-var(--purple-lavender)" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Yes</p>
<p className="text-2xl font-semibold text-var(--purple-ink)" style={{ fontFamily: "'Inter', sans-serif" }}>{stats.yesCount}</p>
</div>
</div>
</Card>
<Card className="p-4 bg-white border-[#ddd8eb] rounded-xl">
<Card className="p-4 bg-background border-var(--neutral-800) rounded-xl">
<div className="flex items-center gap-3">
<UserX className="h-8 w-8 text-[#E07A5F]" />
<UserX className="h-8 w-8 text-var(--orange-soft)" />
<div>
<p className="text-sm text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>No</p>
<p className="text-2xl font-semibold text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}>{stats.noCount}</p>
<p className="text-sm text-var(--purple-lavender)" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>No</p>
<p className="text-2xl font-semibold text-var(--purple-ink)" style={{ fontFamily: "'Inter', sans-serif" }}>{stats.noCount}</p>
</div>
</div>
</Card>
<Card className="p-4 bg-white border-[#ddd8eb] rounded-xl">
<Card className="p-4 bg-background border-var(--neutral-800) rounded-xl">
<div className="flex items-center gap-3">
<HelpCircle className="h-8 w-8 text-[#F2CC8F]" />
<HelpCircle className="h-8 w-8 text-var(--gold-warm)" />
<div>
<p className="text-sm text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Maybe</p>
<p className="text-2xl font-semibold text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}>{stats.maybeCount}</p>
<p className="text-sm text-var(--purple-lavender)" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Maybe</p>
<p className="text-2xl font-semibold text-var(--purple-ink)" style={{ fontFamily: "'Inter', sans-serif" }}>{stats.maybeCount}</p>
</div>
</div>
</Card>
<Card className="p-4 bg-white border-[#ddd8eb] rounded-xl">
<Card className="p-4 bg-background border-var(--neutral-800) rounded-xl">
<div className="flex items-center gap-3">
<Check className="h-8 w-8 text-[#664fa3]" />
<Check className="h-8 w-8 text-var(--purple-lavender)" />
<div>
<p className="text-sm text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Attended</p>
<p className="text-2xl font-semibold text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}>{stats.attendedCount}</p>
<p className="text-sm text-var(--purple-lavender)" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Attended</p>
<p className="text-2xl font-semibold text-var(--purple-ink)" style={{ fontFamily: "'Inter', sans-serif" }}>{stats.attendedCount}</p>
</div>
</div>
</Card>
</div>
{/* Filters and Actions */}
<Card className="p-6 bg-white border-[#ddd8eb] rounded-xl">
<Card className="p-6 bg-background border-var(--neutral-800) rounded-xl">
<div className="space-y-4">
{/* Tab Filters */}
<div className="flex flex-wrap gap-2">
<Button
onClick={() => setActiveTab('all')}
variant={activeTab === 'all' ? 'default' : 'outline'}
className={`rounded-xl ${
activeTab === 'all'
? 'bg-[#664fa3] hover:bg-[#422268] text-white'
: 'border-[#ddd8eb] text-[#664fa3] hover:bg-[#F8F7FB]'
}`}
className={`rounded-xl ${activeTab === 'all'
? 'bg-var(--purple-lavender) hover:bg-var(--purple-ink) text-white'
: 'border-var(--neutral-800) text-var(--purple-lavender) hover:bg-var(--lavender-500)'
}`}
style={{ fontFamily: "'Inter', sans-serif" }}
>
All ({stats.total})
@@ -361,11 +360,10 @@ const AdminEventAttendance = () => {
<Button
onClick={() => setActiveTab('yes')}
variant={activeTab === 'yes' ? 'default' : 'outline'}
className={`rounded-xl ${
activeTab === 'yes'
? 'bg-[#81B29A] hover:bg-[#6a9a83] text-white'
: 'border-[#ddd8eb] text-[#664fa3] hover:bg-[#F8F7FB]'
}`}
className={`rounded-xl ${activeTab === 'yes'
? 'bg-var(--green-light) hover:bg-var(--green-eucalyptus) text-white'
: 'border-var(--neutral-800) text-var(--purple-lavender) hover:bg-var(--lavender-500)'
}`}
style={{ fontFamily: "'Inter', sans-serif" }}
>
Yes ({stats.yesCount})
@@ -373,11 +371,10 @@ const AdminEventAttendance = () => {
<Button
onClick={() => setActiveTab('no')}
variant={activeTab === 'no' ? 'default' : 'outline'}
className={`rounded-xl ${
activeTab === 'no'
? 'bg-[#E07A5F] hover:bg-[#d16b54] text-white'
: 'border-[#ddd8eb] text-[#664fa3] hover:bg-[#F8F7FB]'
}`}
className={`rounded-xl ${activeTab === 'no'
? 'bg-var(--orange-soft) hover:bg-var(--orange-rust) text-white'
: 'border-var(--neutral-800) text-var(--purple-lavender) hover:bg-var(--lavender-500)'
}`}
style={{ fontFamily: "'Inter', sans-serif" }}
>
No ({stats.noCount})
@@ -385,11 +382,10 @@ const AdminEventAttendance = () => {
<Button
onClick={() => setActiveTab('maybe')}
variant={activeTab === 'maybe' ? 'default' : 'outline'}
className={`rounded-xl ${
activeTab === 'maybe'
? 'bg-[#F2CC8F] hover:bg-[#e8bf7a] text-[#422268]'
: 'border-[#ddd8eb] text-[#664fa3] hover:bg-[#F8F7FB]'
}`}
className={`rounded-xl ${activeTab === 'maybe'
? 'bg-var(--gold-warm) hover:bg-var(--gold-soft) text-var(--purple-ink)'
: 'border-var(--neutral-800) text-var(--purple-lavender) hover:bg-var(--lavender-500)'
}`}
style={{ fontFamily: "'Inter', sans-serif" }}
>
Maybe ({stats.maybeCount})
@@ -399,26 +395,26 @@ const AdminEventAttendance = () => {
{/* Search and Bulk Actions */}
<div className="flex flex-wrap gap-3 items-center justify-between">
<div className="flex-1 min-w-[200px] max-w-md relative">
<Search className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-[#664fa3]" />
<Search className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-var(--purple-lavender)" />
<Input
type="text"
placeholder="Search by name or email..."
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
className="pl-10 border-[#ddd8eb] rounded-xl"
className="pl-10 border-var(--neutral-800) rounded-xl"
style={{ fontFamily: "'Nunito Sans', sans-serif" }}
/>
</div>
{selectedRsvps.size > 0 && (
<div className="flex gap-2">
<Badge className="bg-[#664fa3] text-white px-3 py-1">
<Badge className="bg-var(--purple-lavender) text-white px-3 py-1">
{selectedRsvps.size} selected
</Badge>
<Button
onClick={() => handleBulkAttendance(true)}
disabled={saving}
className="bg-[#81B29A] hover:bg-[#6a9a83] text-white rounded-xl"
className="bg-var(--green-light) hover:bg-var(--green-eucalyptus) text-white rounded-xl"
style={{ fontFamily: "'Inter', sans-serif" }}
>
<Check className="h-4 w-4 mr-1" />
@@ -427,7 +423,7 @@ const AdminEventAttendance = () => {
<Button
onClick={() => handleBulkAttendance(false)}
disabled={saving}
className="bg-[#E07A5F] hover:bg-[#d16b54] text-white rounded-xl"
className="bg-var(--orange-soft) hover:bg-var(--orange-rust) text-white rounded-xl"
style={{ fontFamily: "'Inter', sans-serif" }}
>
<X className="h-4 w-4 mr-1" />
@@ -440,10 +436,10 @@ const AdminEventAttendance = () => {
</Card>
{/* RSVP Table */}
<Card className="bg-white border-[#ddd8eb] rounded-xl overflow-hidden">
<Card className="bg-background border-var(--neutral-800) rounded-xl overflow-hidden">
<div className="overflow-x-auto">
<table className="w-full">
<thead className="bg-[#F8F7FB] border-b border-[#ddd8eb]">
<thead className="bg-var(--lavender-500) border-b border-var(--neutral-800)">
<tr>
<th className="px-4 py-3 text-left">
<Checkbox
@@ -451,19 +447,19 @@ const AdminEventAttendance = () => {
onCheckedChange={handleSelectAll}
/>
</th>
<th className="px-4 py-3 text-left text-sm font-semibold text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}>
<th className="px-4 py-3 text-left text-sm font-semibold text-var(--purple-ink)" style={{ fontFamily: "'Inter', sans-serif" }}>
Name
</th>
<th className="px-4 py-3 text-left text-sm font-semibold text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}>
<th className="px-4 py-3 text-left text-sm font-semibold text-var(--purple-ink)" style={{ fontFamily: "'Inter', sans-serif" }}>
Email
</th>
<th className="px-4 py-3 text-left text-sm font-semibold text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}>
<th className="px-4 py-3 text-left text-sm font-semibold text-var(--purple-ink)" style={{ fontFamily: "'Inter', sans-serif" }}>
RSVP Status
</th>
<th className="px-4 py-3 text-center text-sm font-semibold text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}>
<th className="px-4 py-3 text-center text-sm font-semibold text-var(--purple-ink)" style={{ fontFamily: "'Inter', sans-serif" }}>
Attendance
</th>
<th className="px-4 py-3 text-left text-sm font-semibold text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}>
<th className="px-4 py-3 text-left text-sm font-semibold text-var(--purple-ink)" style={{ fontFamily: "'Inter', sans-serif" }}>
Attended At
</th>
</tr>
@@ -471,28 +467,27 @@ const AdminEventAttendance = () => {
<tbody>
{filteredRsvps.length > 0 ? (
filteredRsvps.map((rsvp) => (
<tr key={rsvp.user_id} className="border-b border-[#ddd8eb] hover:bg-[#F8F7FB] transition-colors">
<tr key={rsvp.user_id} className="border-b border-var(--neutral-800) hover:bg-var(--lavender-500) transition-colors">
<td className="px-4 py-3">
<Checkbox
checked={selectedRsvps.has(rsvp.user_id)}
onCheckedChange={() => handleSelectRsvp(rsvp.user_id)}
/>
</td>
<td className="px-4 py-3 text-sm text-[#422268]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<td className="px-4 py-3 text-sm text-var(--purple-ink)" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{rsvp.user_name}
</td>
<td className="px-4 py-3 text-sm text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<td className="px-4 py-3 text-sm text-var(--purple-lavender)" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{rsvp.user_email}
</td>
<td className="px-4 py-3">
<Badge
className={`${
rsvp.rsvp_status === 'yes'
? 'bg-[#81B29A]'
: rsvp.rsvp_status === 'no'
? 'bg-[#E07A5F]'
: 'bg-[#F2CC8F] text-[#422268]'
} text-white text-xs px-2 py-1`}
className={`${rsvp.rsvp_status === 'yes'
? 'bg-var(--green-light)'
: rsvp.rsvp_status === 'no'
? 'bg-var(--orange-soft)'
: 'bg-var(--gold-warm) text-var(--purple-ink)'
} text-white text-xs px-2 py-1`}
>
{rsvp.rsvp_status.toUpperCase()}
</Badge>
@@ -503,7 +498,7 @@ const AdminEventAttendance = () => {
onClick={() => handleIndividualAttendance(rsvp.user_id, false)}
disabled={saving}
size="sm"
className="bg-[#81B29A] hover:bg-[#6a9a83] text-white rounded-lg min-w-[120px]"
className="bg-var(--green-light) hover:bg-var(--green-eucalyptus) text-white rounded-lg min-w-[120px]"
style={{ fontFamily: "'Inter', sans-serif" }}
>
<Check className="h-3 w-3 mr-1" />
@@ -515,7 +510,7 @@ const AdminEventAttendance = () => {
disabled={saving}
size="sm"
variant="outline"
className="border-[#ddd8eb] text-[#664fa3] hover:bg-[#81B29A] hover:text-white hover:border-[#81B29A] rounded-lg min-w-[120px]"
className="border-var(--neutral-800) text-var(--purple-lavender) hover:bg-var(--green-light) hover:text-white hover:border-var(--green-light) rounded-lg min-w-[120px]"
style={{ fontFamily: "'Inter', sans-serif" }}
>
<X className="h-3 w-3 mr-1" />
@@ -523,7 +518,7 @@ const AdminEventAttendance = () => {
</Button>
)}
</td>
<td className="px-4 py-3 text-sm text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<td className="px-4 py-3 text-sm text-var(--purple-lavender)" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{rsvp.attended_at ? moment(rsvp.attended_at).format('MMM D, YYYY h:mm A') : '-'}
</td>
</tr>
@@ -531,7 +526,7 @@ const AdminEventAttendance = () => {
) : (
<tr>
<td colSpan="6" className="px-4 py-12 text-center">
<p className="text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p className="text-var(--purple-lavender)" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{searchQuery ? 'No RSVPs match your search' : 'No RSVPs for this filter'}
</p>
</td>