Refactor color scheme in member-related pages to use brand colors instead of CSS variables for consistency and improved readability

This commit is contained in:
2026-01-13 22:01:33 -06:00
parent 30d32d8823
commit e04d39fe17
61 changed files with 731 additions and 735 deletions

View File

@@ -169,7 +169,7 @@ const AdminBylaws = () => {
if (loading) {
return (
<div className="flex items-center justify-center min-h-[60vh]">
<p className="text-[var(--purple-lavender)]">Loading bylaws...</p>
<p className="text-brand-purple ">Loading bylaws...</p>
</div>
);
}
@@ -182,14 +182,14 @@ const AdminBylaws = () => {
<h1 className="text-3xl font-semibold text-[var(--purple-ink)]" style={{ fontFamily: "'Inter', sans-serif" }}>
Bylaws Management
</h1>
<p className="text-[var(--purple-lavender)] mt-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p className="text-brand-purple mt-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Manage LOAF governing bylaws and version history
</p>
</div>
{hasPermission('bylaws.create') && (
<Button
onClick={handleCreate}
className="bg-[var(--purple-lavender)] text-white hover:bg-[var(--purple-muted)] rounded-full flex items-center gap-2"
className="bg-brand-purple text-white hover:bg-[var(--purple-muted)] rounded-full flex items-center gap-2"
>
<Plus className="h-4 w-4" />
Add Version
@@ -199,10 +199,10 @@ const AdminBylaws = () => {
{/* Current Bylaws */}
{currentBylaws ? (
<Card className="p-6 border-2 border-[var(--purple-lavender)]">
<Card className="p-6 border-2 border-brand-purple ">
<div className="flex items-center justify-between mb-4">
<div className="flex items-center gap-3">
<div className="bg-gradient-to-br from-[var(--purple-lavender)] to-[var(--purple-ink)] p-3 rounded-xl">
<div className="bg-gradient-to-br from-brand-purple to-[var(--purple-ink)] p-3 rounded-xl">
<Scale className="h-6 w-6 text-white" />
</div>
<div>
@@ -214,7 +214,7 @@ const AdminBylaws = () => {
<Check className="h-3 w-3 mr-1" />
Current Version
</Badge>
<span className="text-[var(--purple-lavender)] text-sm">
<span className="text-brand-purple text-sm">
Version {currentBylaws.version}
</span>
</div>
@@ -225,7 +225,7 @@ const AdminBylaws = () => {
variant="outline"
size="sm"
onClick={() => window.open(currentBylaws.document_url, '_blank')}
className="border-[var(--purple-lavender)] text-[var(--purple-lavender)]"
className="border-brand-purple text-brand-purple "
>
<ExternalLink className="h-4 w-4 mr-1" />
View
@@ -235,7 +235,7 @@ const AdminBylaws = () => {
variant="outline"
size="sm"
onClick={() => handleEdit(currentBylaws)}
className="border-[var(--purple-lavender)] text-[var(--purple-lavender)]"
className="border-brand-purple text-brand-purple "
>
<Edit className="h-4 w-4" />
</Button>
@@ -252,7 +252,7 @@ const AdminBylaws = () => {
)}
</div>
</div>
<div className="flex items-center gap-4 text-sm text-[var(--purple-lavender)]">
<div className="flex items-center gap-4 text-sm text-brand-purple ">
<span>Effective Date: <strong>{formatDate(currentBylaws.effective_date)}</strong></span>
<span></span>
<span>Document Type: <strong>{currentBylaws.document_type === 'upload' ? 'PDF Upload' : 'Link'}</strong></span>
@@ -261,9 +261,9 @@ const AdminBylaws = () => {
) : (
<Card className="p-12 text-center">
<Scale className="h-16 w-16 text-[var(--neutral-800)] mx-auto mb-4" />
<p className="text-[var(--purple-lavender)] text-lg mb-4">No current bylaws set</p>
<p className="text-brand-purple text-lg mb-4">No current bylaws set</p>
{hasPermission('bylaws.create') && (
<Button onClick={handleCreate} className="bg-[var(--purple-lavender)] text-white">
<Button onClick={handleCreate} className="bg-brand-purple text-white">
<Plus className="h-4 w-4 mr-2" />
Create Bylaws
</Button>
@@ -285,7 +285,7 @@ const AdminBylaws = () => {
<h3 className="text-lg font-semibold text-[var(--purple-ink)] mb-1">
{bylawsDoc.title}
</h3>
<div className="flex items-center gap-3 text-sm text-[var(--purple-lavender)]">
<div className="flex items-center gap-3 text-sm text-brand-purple ">
<span>Version {bylawsDoc.version}</span>
<span></span>
<span>Effective {formatDate(bylawsDoc.effective_date)}</span>
@@ -296,7 +296,7 @@ const AdminBylaws = () => {
variant="outline"
size="sm"
onClick={() => window.open(bylawsDoc.document_url, '_blank')}
className="border-[var(--purple-lavender)] text-[var(--purple-lavender)]"
className="border-brand-purple text-brand-purple "
>
<ExternalLink className="h-4 w-4" />
</Button>
@@ -305,7 +305,7 @@ const AdminBylaws = () => {
variant="outline"
size="sm"
onClick={() => handleEdit(bylawsDoc)}
className="border-[var(--purple-lavender)] text-[var(--purple-lavender)]"
className="border-brand-purple text-brand-purple "
>
<Edit className="h-4 w-4" />
</Button>
@@ -404,12 +404,12 @@ const AdminBylaws = () => {
required={!selectedBylaws}
/>
{uploadedFile && (
<p className="text-sm text-[var(--purple-lavender)] mt-1">
<p className="text-sm text-brand-purple mt-1">
Selected: {uploadedFile.name}
</p>
)}
{selectedBylaws && !uploadedFile && (
<p className="text-sm text-[var(--purple-lavender)] mt-1">
<p className="text-sm text-brand-purple mt-1">
Current file will be kept if no new file is selected
</p>
)}
@@ -424,7 +424,7 @@ const AdminBylaws = () => {
placeholder="https://docs.google.com/... or https://example.com/file.pdf"
required
/>
<p className="text-sm text-[var(--purple-lavender)] mt-1">
<p className="text-sm text-brand-purple mt-1">
Paste the shareable link to your document (Google Drive, Dropbox, PDF URL, etc.)
</p>
</div>
@@ -455,7 +455,7 @@ const AdminBylaws = () => {
</Button>
<Button
type="submit"
className="bg-[var(--purple-lavender)] text-white"
className="bg-brand-purple text-white"
disabled={submitting}
>
{submitting ? 'Saving...' : selectedBylaws ? 'Update' : 'Create'}

View File

@@ -91,7 +91,7 @@ const AdminDashboard = () => {
<Card className="p-6 bg-background rounded-2xl border border-[var(--neutral-800)]" data-testid="stat-pending-validations">
<div className="flex items-center justify-between mb-4">
<div className="bg-orange-100 p-3 rounded-lg">
<div className=" p-3 rounded-lg">
<Clock className="h-6 w-6 text-orange-600" />
</div>
</div>

View File

@@ -167,13 +167,13 @@ const AdminDonations = () => {
};
const getTypeBadgeColor = (type) => {
return type === 'member' ? 'bg-[var(--green-light)]' : 'bg-[var(--purple-lavender)]';
return type === 'member' ? 'bg-[var(--green-light)]' : 'bg-brand-purple ';
};
if (loading) {
return (
<div className="flex items-center justify-center min-h-screen">
<Loader2 className="h-12 w-12 animate-spin text-[var(--purple-lavender)]" />
<Loader2 className="h-12 w-12 animate-spin text-brand-purple " />
</div>
);
}
@@ -185,7 +185,7 @@ const AdminDonations = () => {
<h1 className="text-3xl font-semibold text-[var(--purple-ink)]" style={{ fontFamily: "'Inter', sans-serif" }}>
Donation Management
</h1>
<p className="text-[var(--purple-lavender)] mt-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p className="text-brand-purple mt-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Track and manage all donations from members and the public
</p>
</div>
@@ -195,7 +195,7 @@ const AdminDonations = () => {
<Card className="p-6 bg-background rounded-2xl border-2 border-[var(--neutral-800)]">
<div className="flex items-center justify-between">
<div>
<p className="text-sm text-[var(--purple-lavender)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p className="text-sm text-brand-purple " style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Total Donations
</p>
<p className="text-3xl font-bold text-[var(--purple-ink)] mt-2" style={{ fontFamily: "'Inter', sans-serif" }}>
@@ -203,7 +203,7 @@ const AdminDonations = () => {
</p>
</div>
<div className="p-3 bg-[var(--neutral-800)]/20 rounded-full">
<Heart className="h-6 w-6 text-[var(--purple-lavender)]" />
<Heart className="h-6 w-6 text-brand-purple " />
</div>
</div>
</Card>
@@ -211,7 +211,7 @@ const AdminDonations = () => {
<Card className="p-6 bg-background rounded-2xl border-2 border-[var(--neutral-800)]">
<div className="flex items-center justify-between">
<div>
<p className="text-sm text-[var(--purple-lavender)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p className="text-sm text-brand-purple " style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Member Donations
</p>
<p className="text-3xl font-bold text-[var(--green-light)] mt-2" style={{ fontFamily: "'Inter', sans-serif" }}>
@@ -227,15 +227,15 @@ const AdminDonations = () => {
<Card className="p-6 bg-background rounded-2xl border-2 border-[var(--neutral-800)]">
<div className="flex items-center justify-between">
<div>
<p className="text-sm text-[var(--purple-lavender)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p className="text-sm text-brand-purple " style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Public Donations
</p>
<p className="text-3xl font-bold text-[var(--purple-lavender)] mt-2" style={{ fontFamily: "'Inter', sans-serif" }}>
<p className="text-3xl font-bold text-brand-purple mt-2" style={{ fontFamily: "'Inter', sans-serif" }}>
{stats.public_donations || 0}
</p>
</div>
<div className="p-3 bg-[var(--neutral-800)]/20 rounded-full">
<Globe className="h-6 w-6 text-[var(--purple-lavender)]" />
<Globe className="h-6 w-6 text-brand-purple " />
</div>
</div>
</Card>
@@ -243,7 +243,7 @@ const AdminDonations = () => {
<Card className="p-6 bg-background rounded-2xl border-2 border-[var(--neutral-800)]">
<div className="flex items-center justify-between">
<div>
<p className="text-sm text-[var(--purple-lavender)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p className="text-sm text-brand-purple " style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Total Amount
</p>
<p className="text-3xl font-bold text-[var(--purple-ink)] mt-2" style={{ fontFamily: "'Inter', sans-serif" }}>
@@ -251,7 +251,7 @@ const AdminDonations = () => {
</p>
</div>
<div className="p-3 bg-[var(--neutral-800)]/20 rounded-full">
<DollarSign className="h-6 w-6 text-[var(--purple-lavender)]" />
<DollarSign className="h-6 w-6 text-brand-purple " />
</div>
</div>
</Card>
@@ -263,12 +263,12 @@ const AdminDonations = () => {
{/* Search and Export Row */}
<div className="flex flex-col md:flex-row gap-4 justify-between">
<div className="flex-1 relative">
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 h-5 w-5 text-[var(--purple-lavender)]" />
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 h-5 w-5 text-brand-purple " />
<Input
placeholder="Search by donor name or email..."
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
className="pl-10 rounded-full border-2 border-[var(--neutral-800)] focus:border-[var(--purple-lavender)]"
className="pl-10 rounded-full border-2 border-[var(--neutral-800)] focus:border-brand-purple "
/>
</div>
{hasPermission('donations.export') && (
@@ -287,14 +287,14 @@ const AdminDonations = () => {
onClick={() => handleExport('all')}
className="cursor-pointer hover:bg-[var(--lavender-300)] rounded-lg p-3"
>
<FileDown className="h-4 w-4 mr-2 text-[var(--purple-lavender)]" />
<FileDown className="h-4 w-4 mr-2 text-brand-purple " />
<span className="text-[var(--purple-ink)]">Export All Donations</span>
</DropdownMenuItem>
<DropdownMenuItem
onClick={() => handleExport('current')}
className="cursor-pointer hover:bg-[var(--lavender-300)] rounded-lg p-3"
>
<FileDown className="h-4 w-4 mr-2 text-[var(--purple-lavender)]" />
<FileDown className="h-4 w-4 mr-2 text-brand-purple " />
<span className="text-[var(--purple-ink)]">Export Current View</span>
</DropdownMenuItem>
</DropdownMenuContent>
@@ -354,7 +354,7 @@ const AdminDonations = () => {
{/* Active Filters Summary */}
{(searchQuery || typeFilter !== 'all' || statusFilter !== 'all' || startDate || endDate) && (
<div className="text-sm text-[var(--purple-lavender)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<div className="text-sm text-brand-purple " style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Showing {filteredDonations.length} of {donations.length} donations
</div>
)}
@@ -393,7 +393,7 @@ const AdminDonations = () => {
<td colSpan="6" className="px-6 py-12 text-center">
<div className="flex flex-col items-center gap-3">
<Heart className="h-12 w-12 text-[var(--neutral-800)]" />
<p className="text-[var(--purple-lavender)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p className="text-brand-purple " style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{donations.length === 0 ? 'No donations yet' : 'No donations match your filters'}
</p>
</div>
@@ -407,7 +407,7 @@ const AdminDonations = () => {
<p className="font-medium text-[var(--purple-ink)]" style={{ fontFamily: "'Inter', sans-serif" }}>
{donation.donor_name || 'Anonymous'}
</p>
<p className="text-sm text-[var(--purple-lavender)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p className="text-sm text-brand-purple " style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{donation.donor_email || 'No email'}
</p>
</div>
@@ -431,7 +431,7 @@ const AdminDonations = () => {
</Badge>
</td>
<td className="px-6 py-4">
<div className="flex items-center gap-2 text-[var(--purple-lavender)]">
<div className="flex items-center gap-2 text-brand-purple ">
<Calendar className="h-4 w-4" />
<span style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{formatDate(donation.created_at)}
@@ -439,7 +439,7 @@ const AdminDonations = () => {
</div>
</td>
<td className="px-6 py-4">
<p className="text-[var(--purple-lavender)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p className="text-brand-purple " style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{donation.payment_method || 'N/A'}
</p>
</td>
@@ -456,7 +456,7 @@ const AdminDonations = () => {
<Card className="p-6 bg-gradient-to-r from-[var(--lavender-400)] to-[var(--lavender-300)] rounded-2xl border-2 border-[var(--neutral-800)]">
<div className="flex items-center justify-between">
<div>
<p className="text-[var(--purple-lavender)] font-medium" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p className="text-brand-purple font-medium" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
This Month's Donations
</p>
<p className="text-2xl font-bold text-[var(--purple-ink)] mt-2" style={{ fontFamily: "'Inter', sans-serif" }}>

View File

@@ -213,7 +213,7 @@ const AdminEventAttendance = () => {
if (loading) {
return (
<div className="flex items-center justify-center h-64">
<div className="text-[var(--purple-lavender)]">Loading event data...</div>
<div className="text-brand-purple ">Loading event data...</div>
</div>
);
}
@@ -221,8 +221,8 @@ const AdminEventAttendance = () => {
if (!event) {
return (
<div className="text-center py-12">
<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">
<p className="text-brand-purple mb-4">Event not found</p>
<Button onClick={() => navigate('/admin/events')} className="bg-brand-purple hover:bg-[var(--purple-ink)] text-white rounded-xl">
Back to Events
</Button>
</div>
@@ -237,7 +237,7 @@ const AdminEventAttendance = () => {
<Button
onClick={() => navigate('/admin/events')}
variant="outline"
className="border-[var(--neutral-800)] text-[var(--purple-lavender)] rounded-xl"
className="border-[var(--neutral-800)] text-brand-purple rounded-xl"
style={{ fontFamily: "'Inter', sans-serif" }}
>
<ArrowLeft className="h-4 w-4 mr-2" />
@@ -247,7 +247,7 @@ const AdminEventAttendance = () => {
<h1 className="text-3xl font-semibold text-[var(--purple-ink)]" style={{ fontFamily: "'Inter', sans-serif" }}>
Event Attendance
</h1>
<p className="text-[var(--purple-lavender)] mt-1" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p className="text-brand-purple mt-1" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Manage RSVPs and track attendance for this event
</p>
</div>
@@ -269,7 +269,7 @@ const AdminEventAttendance = () => {
<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-[var(--purple-lavender)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<div className="flex flex-wrap gap-4 text-brand-purple " 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>
@@ -292,9 +292,9 @@ const AdminEventAttendance = () => {
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-5 gap-4">
<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-[var(--purple-lavender)]" />
<Users className="h-8 w-8 text-brand-purple " />
<div>
<p className="text-sm text-[var(--purple-lavender)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Total RSVPs</p>
<p className="text-sm text-brand-purple " 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>
@@ -304,7 +304,7 @@ const AdminEventAttendance = () => {
<div className="flex items-center gap-3">
<UserCheck className="h-8 w-8 text-[var(--green-light)]" />
<div>
<p className="text-sm text-[var(--purple-lavender)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Yes</p>
<p className="text-sm text-brand-purple " 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>
@@ -314,7 +314,7 @@ const AdminEventAttendance = () => {
<div className="flex items-center gap-3">
<UserX className="h-8 w-8 text-[var(--orange-soft)]" />
<div>
<p className="text-sm text-[var(--purple-lavender)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>No</p>
<p className="text-sm text-brand-purple " 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>
@@ -324,7 +324,7 @@ const AdminEventAttendance = () => {
<div className="flex items-center gap-3">
<HelpCircle className="h-8 w-8 text-[var(--gold-warm)]" />
<div>
<p className="text-sm text-[var(--purple-lavender)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Maybe</p>
<p className="text-sm text-brand-purple " 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>
@@ -332,9 +332,9 @@ const AdminEventAttendance = () => {
<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-[var(--purple-lavender)]" />
<Check className="h-8 w-8 text-brand-purple " />
<div>
<p className="text-sm text-[var(--purple-lavender)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Attended</p>
<p className="text-sm text-brand-purple " 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>
@@ -350,8 +350,8 @@ const AdminEventAttendance = () => {
onClick={() => setActiveTab('all')}
variant={activeTab === 'all' ? 'default' : 'outline'}
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)]'
? 'bg-brand-purple hover:bg-[var(--purple-ink)] text-white'
: 'border-[var(--neutral-800)] text-brand-purple hover:bg-[var(--lavender-500)]'
}`}
style={{ fontFamily: "'Inter', sans-serif" }}
>
@@ -362,7 +362,7 @@ const AdminEventAttendance = () => {
variant={activeTab === 'yes' ? 'default' : 'outline'}
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)]'
: 'border-[var(--neutral-800)] text-brand-purple hover:bg-[var(--lavender-500)]'
}`}
style={{ fontFamily: "'Inter', sans-serif" }}
>
@@ -373,7 +373,7 @@ const AdminEventAttendance = () => {
variant={activeTab === 'no' ? 'default' : 'outline'}
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)]'
: 'border-[var(--neutral-800)] text-brand-purple hover:bg-[var(--lavender-500)]'
}`}
style={{ fontFamily: "'Inter', sans-serif" }}
>
@@ -384,7 +384,7 @@ const AdminEventAttendance = () => {
variant={activeTab === 'maybe' ? 'default' : 'outline'}
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)]'
: 'border-[var(--neutral-800)] text-brand-purple hover:bg-[var(--lavender-500)]'
}`}
style={{ fontFamily: "'Inter', sans-serif" }}
>
@@ -395,7 +395,7 @@ 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-[var(--purple-lavender)]" />
<Search className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-brand-purple " />
<Input
type="text"
placeholder="Search by name or email..."
@@ -408,7 +408,7 @@ const AdminEventAttendance = () => {
{selectedRsvps.size > 0 && (
<div className="flex gap-2">
<Badge className="bg-[var(--purple-lavender)] text-white px-3 py-1">
<Badge className="bg-brand-purple text-white px-3 py-1">
{selectedRsvps.size} selected
</Badge>
<Button
@@ -477,7 +477,7 @@ const AdminEventAttendance = () => {
<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-[var(--purple-lavender)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<td className="px-4 py-3 text-sm text-brand-purple " style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{rsvp.user_email}
</td>
<td className="px-4 py-3">
@@ -510,7 +510,7 @@ const AdminEventAttendance = () => {
disabled={saving}
size="sm"
variant="outline"
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]"
className="border-[var(--neutral-800)] text-brand-purple 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" />
@@ -518,7 +518,7 @@ const AdminEventAttendance = () => {
</Button>
)}
</td>
<td className="px-4 py-3 text-sm text-[var(--purple-lavender)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<td className="px-4 py-3 text-sm text-brand-purple " style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{rsvp.attended_at ? moment(rsvp.attended_at).format('MMM D, YYYY h:mm A') : '-'}
</td>
</tr>
@@ -526,7 +526,7 @@ const AdminEventAttendance = () => {
) : (
<tr>
<td colSpan="6" className="px-4 py-12 text-center">
<p className="text-[var(--purple-lavender)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p className="text-brand-purple " style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{searchQuery ? 'No RSVPs match your search' : 'No RSVPs for this filter'}
</p>
</td>

View File

@@ -137,7 +137,7 @@ const AdminEvents = () => {
<h1 className="text-4xl md:text-5xl font-semibold text-[var(--purple-ink)] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}>
Event Management
</h1>
<p className="text-lg text-[var(--purple-lavender)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p className="text-lg text-brand-purple " style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Create and manage community events.
</p>
</div>
@@ -174,7 +174,7 @@ const AdminEvents = () => {
value={formData.title}
onChange={(e) => setFormData({ ...formData, title: e.target.value })}
required
className="border-2 border-[var(--neutral-800)] focus:border-[var(--purple-lavender)]"
className="border-2 border-[var(--neutral-800)] focus:border-brand-purple "
/>
</div>
@@ -186,7 +186,7 @@ const AdminEvents = () => {
value={formData.description}
onChange={(e) => setFormData({ ...formData, description: e.target.value })}
rows={4}
className="w-full border-2 border-[var(--neutral-800)] focus:border-[var(--purple-lavender)] rounded-lg p-3"
className="w-full border-2 border-[var(--neutral-800)] focus:border-brand-purple rounded-lg p-3"
/>
</div>
@@ -200,7 +200,7 @@ const AdminEvents = () => {
value={formData.start_at}
onChange={(e) => setFormData({ ...formData, start_at: e.target.value })}
required
className="border-2 border-[var(--neutral-800)] focus:border-[var(--purple-lavender)]"
className="border-2 border-[var(--neutral-800)] focus:border-brand-purple "
/>
</div>
@@ -213,7 +213,7 @@ const AdminEvents = () => {
value={formData.end_at}
onChange={(e) => setFormData({ ...formData, end_at: e.target.value })}
required
className="border-2 border-[var(--neutral-800)] focus:border-[var(--purple-lavender)]"
className="border-2 border-[var(--neutral-800)] focus:border-brand-purple "
/>
</div>
</div>
@@ -226,7 +226,7 @@ const AdminEvents = () => {
value={formData.location}
onChange={(e) => setFormData({ ...formData, location: e.target.value })}
required
className="border-2 border-[var(--neutral-800)] focus:border-[var(--purple-lavender)]"
className="border-2 border-[var(--neutral-800)] focus:border-brand-purple "
/>
</div>
@@ -239,7 +239,7 @@ const AdminEvents = () => {
value={formData.capacity}
onChange={(e) => setFormData({ ...formData, capacity: e.target.value })}
placeholder="Leave empty for unlimited"
className="border-2 border-[var(--neutral-800)] focus:border-[var(--purple-lavender)]"
className="border-2 border-[var(--neutral-800)] focus:border-brand-purple "
/>
</div>
@@ -249,7 +249,7 @@ const AdminEvents = () => {
id="published"
checked={formData.published}
onChange={(e) => setFormData({ ...formData, published: e.target.checked })}
className="w-4 h-4 text-[var(--purple-lavender)] border-[var(--neutral-800)] rounded focus:ring-[var(--purple-lavender)]"
className="w-4 h-4 text-brand-purple border-[var(--neutral-800)] rounded focus:ring-brand-purple "
/>
<label htmlFor="published" className="text-sm font-medium text-[var(--purple-ink)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Publish event (make visible to members)
@@ -281,7 +281,7 @@ const AdminEvents = () => {
{/* Events List */}
{loading ? (
<div className="text-center py-20">
<p className="text-[var(--purple-lavender)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Loading events...</p>
<p className="text-brand-purple " style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Loading events...</p>
</div>
) : events.length > 0 ? (
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
@@ -294,7 +294,7 @@ const AdminEvents = () => {
{/* Event Header */}
<div className="flex justify-between items-start mb-4">
<div className="bg-[var(--neutral-800)]/20 p-3 rounded-lg">
<Calendar className="h-6 w-6 text-[var(--purple-lavender)]" />
<Calendar className="h-6 w-6 text-brand-purple " />
</div>
<Badge
className={`${event.published
@@ -312,13 +312,13 @@ const AdminEvents = () => {
</h3>
{event.description && (
<p className="text-[var(--purple-lavender)] mb-4 line-clamp-2 text-sm" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p className="text-brand-purple mb-4 line-clamp-2 text-sm" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{event.description}
</p>
)}
<div className="space-y-2 mb-4">
<div className="flex items-center gap-2 text-sm text-[var(--purple-lavender)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<div className="flex items-center gap-2 text-sm text-brand-purple " style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<Calendar className="h-4 w-4" />
<span>
{new Date(event.start_at).toLocaleDateString()} at{' '}
@@ -328,11 +328,11 @@ const AdminEvents = () => {
})}
</span>
</div>
<div className="flex items-center gap-2 text-sm text-[var(--purple-lavender)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<div className="flex items-center gap-2 text-sm text-brand-purple " style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<MapPin className="h-4 w-4" />
<span className="truncate">{event.location}</span>
</div>
<div className="flex items-center gap-2 text-sm text-[var(--purple-lavender)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<div className="flex items-center gap-2 text-sm text-brand-purple " style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<Users className="h-4 w-4" />
<span>{event.rsvp_count || 0} attending</span>
</div>
@@ -358,7 +358,7 @@ const AdminEvents = () => {
onClick={() => togglePublish(event)}
variant="outline"
size="sm"
className="flex-1 border-[var(--purple-lavender)] text-[var(--purple-lavender)] hover:bg-[var(--purple-lavender)] hover:text-white"
className="flex-1 border-brand-purple text-brand-purple hover:bg-brand-purple hover:text-white"
data-testid={`toggle-publish-${event.id}`}
>
{event.published ? (
@@ -402,7 +402,7 @@ const AdminEvents = () => {
<h3 className="text-2xl font-semibold text-[var(--purple-ink)] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}>
No Events Yet
</h3>
<p className="text-[var(--purple-lavender)] mb-6" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p className="text-brand-purple mb-6" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Create your first event to get started!
</p>
<Button

View File

@@ -147,7 +147,7 @@ const AdminFinancials = () => {
if (loading) {
return (
<div className="flex items-center justify-center min-h-[60vh]">
<p className="text-[var(--purple-lavender)]">Loading financial reports...</p>
<p className="text-brand-purple ">Loading financial reports...</p>
</div>
);
}
@@ -160,14 +160,14 @@ const AdminFinancials = () => {
<h1 className="text-3xl font-semibold text-[var(--purple-ink)]" style={{ fontFamily: "'Inter', sans-serif" }}>
Financial Reports Management
</h1>
<p className="text-[var(--purple-lavender)] mt-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p className="text-brand-purple mt-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Manage annual financial reports
</p>
</div>
{hasPermission('financials.create') && (
<Button
onClick={handleCreate}
className="bg-[var(--purple-lavender)] text-white hover:bg-[var(--purple-muted)] rounded-full flex items-center gap-2"
className="bg-brand-purple text-white hover:bg-[var(--purple-muted)] rounded-full flex items-center gap-2"
>
<Plus className="h-4 w-4" />
Add Report
@@ -179,9 +179,9 @@ const AdminFinancials = () => {
{reports.length === 0 ? (
<Card className="p-12 text-center">
<TrendingUp className="h-16 w-16 text-[var(--neutral-800)] mx-auto mb-4" />
<p className="text-[var(--purple-lavender)] text-lg mb-4">No financial reports yet</p>
<p className="text-brand-purple text-lg mb-4">No financial reports yet</p>
{hasPermission('financials.create') && (
<Button onClick={handleCreate} className="bg-[var(--purple-lavender)] text-white">
<Button onClick={handleCreate} className="bg-brand-purple text-white">
<Plus className="h-4 w-4 mr-2" />
Create First Report
</Button>
@@ -192,7 +192,7 @@ const AdminFinancials = () => {
{reports.map(report => (
<Card key={report.id} className="p-6">
<div className="flex items-center gap-6">
<div className="bg-gradient-to-br from-[var(--purple-lavender)] to-[var(--purple-ink)] p-4 rounded-xl text-white min-w-[100px] text-center">
<div className="bg-gradient-to-br from-brand-purple to-[var(--purple-ink)] p-4 rounded-xl text-white min-w-[100px] text-center">
<DollarSign className="h-6 w-6 mx-auto mb-1" />
<div className="text-2xl font-bold">{report.year}</div>
</div>
@@ -201,14 +201,14 @@ const AdminFinancials = () => {
{report.title}
</h3>
<div className="flex items-center gap-3">
<Badge variant="outline" className="border-[var(--purple-lavender)] text-[var(--purple-lavender)]">
<Badge variant="outline" className="border-brand-purple text-brand-purple ">
{report.document_type === 'google_drive' ? 'Google Drive' : report.document_type.toUpperCase()}
</Badge>
<Button
variant="ghost"
size="sm"
onClick={() => window.open(report.document_url, '_blank')}
className="text-[var(--purple-lavender)] hover:text-[var(--purple-muted)]"
className="text-brand-purple hover:text-[var(--purple-muted)]"
>
<ExternalLink className="h-4 w-4 mr-1" />
View
@@ -222,7 +222,7 @@ const AdminFinancials = () => {
variant="outline"
size="sm"
onClick={() => handleEdit(report)}
className="border-[var(--purple-lavender)] text-[var(--purple-lavender)]"
className="border-brand-purple text-brand-purple "
>
<Edit className="h-4 w-4" />
</Button>
@@ -313,12 +313,12 @@ const AdminFinancials = () => {
required={!selectedReport}
/>
{uploadedFile && (
<p className="text-sm text-[var(--purple-lavender)] mt-1">
<p className="text-sm text-brand-purple mt-1">
Selected: {uploadedFile.name}
</p>
)}
{selectedReport && !uploadedFile && (
<p className="text-sm text-[var(--purple-lavender)] mt-1">
<p className="text-sm text-brand-purple mt-1">
Current file will be kept if no new file is selected
</p>
)}
@@ -333,7 +333,7 @@ const AdminFinancials = () => {
placeholder="https://docs.google.com/... or https://example.com/file.pdf"
required
/>
<p className="text-sm text-[var(--purple-lavender)] mt-1">
<p className="text-sm text-brand-purple mt-1">
Paste the shareable link to your document (Google Drive, Dropbox, PDF URL, etc.)
</p>
</div>
@@ -350,7 +350,7 @@ const AdminFinancials = () => {
</Button>
<Button
type="submit"
className="bg-[var(--purple-lavender)] text-white"
className="bg-brand-purple text-white"
disabled={submitting}
>
{submitting ? 'Saving...' : selectedReport ? 'Update' : 'Create'}

View File

@@ -156,7 +156,7 @@ const AdminGallery = () => {
<h1 className="text-3xl font-semibold text-[var(--purple-ink)]" style={{ fontFamily: "'Inter', sans-serif" }}>
Event Gallery Management
</h1>
<p className="text-[var(--purple-lavender)] mt-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p className="text-brand-purple mt-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Upload and manage photos for event galleries
</p>
</div>
@@ -186,17 +186,17 @@ const AdminGallery = () => {
{events.length === 0 && (
<div className="mt-4 p-4 bg-[var(--lavender-300)] border-2 border-[var(--neutral-800)] rounded-xl">
<div className="flex items-start gap-3">
<AlertCircle className="h-5 w-5 text-[var(--purple-lavender)] flex-shrink-0 mt-0.5" />
<AlertCircle className="h-5 w-5 text-brand-purple flex-shrink-0 mt-0.5" />
<div className="flex-1">
<h4 className="text-sm font-semibold text-[var(--purple-ink)] mb-1" style={{ fontFamily: "'Inter', sans-serif" }}>
No Events Available
</h4>
<p className="text-sm text-[var(--purple-lavender)] mb-3" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p className="text-sm text-brand-purple mb-3" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
You need to create an event before uploading gallery images. Events help organize photos by occasion.
</p>
<Link to="/admin/events">
<Button
className="bg-[var(--purple-lavender)] hover:bg-[var(--purple-ink)] text-white rounded-xl text-sm"
className="bg-brand-purple hover:bg-[var(--purple-ink)] text-white rounded-xl text-sm"
style={{ fontFamily: "'Inter', sans-serif" }}
>
<Calendar className="h-4 w-4 mr-2" />
@@ -221,7 +221,7 @@ const AdminGallery = () => {
<Button
onClick={() => fileInputRef.current?.click()}
disabled={uploading}
className="bg-[var(--purple-lavender)] hover:bg-[var(--purple-ink)] text-white rounded-xl"
className="bg-brand-purple hover:bg-[var(--purple-ink)] text-white rounded-xl"
style={{ fontFamily: "'Inter', sans-serif" }}
>
{uploading ? (
@@ -236,7 +236,7 @@ const AdminGallery = () => {
</>
)}
</Button>
<p className="text-sm text-[var(--purple-lavender)] mt-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p className="text-sm text-brand-purple mt-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
You can select multiple images. Max {formatFileSize(maxFileSize)} per image.
</p>
</div>
@@ -251,7 +251,7 @@ const AdminGallery = () => {
<h2 className="text-xl font-semibold text-[var(--purple-ink)]" style={{ fontFamily: "'Inter', sans-serif" }}>
Gallery Images
</h2>
<Badge className="bg-[var(--purple-lavender)] text-white px-3 py-1">
<Badge className="bg-brand-purple text-white px-3 py-1">
{galleryImages.length} {galleryImages.length === 1 ? 'image' : 'images'}
</Badge>
</div>
@@ -275,7 +275,7 @@ const AdminGallery = () => {
<Button
onClick={() => openEditCaption(image)}
size="sm"
className="bg-background/90 hover:bg-background text-[var(--purple-ink)] rounded-lg"
className="bg-background/90 hover:bg-background text-[var(--purple-ink)] dark:text-[#ddd8eb] rounded-lg"
style={{ fontFamily: "'Inter', sans-serif" }}
>
<Edit className="h-4 w-4 mr-1" />
@@ -299,7 +299,7 @@ const AdminGallery = () => {
{/* Caption Preview */}
{image.caption && (
<div className="mt-2">
<p className="text-sm text-[var(--purple-lavender)] line-clamp-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p className="text-sm text-brand-purple line-clamp-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{image.caption}
</p>
</div>
@@ -307,7 +307,7 @@ const AdminGallery = () => {
{/* File Size */}
<div className="mt-1">
<p className="text-xs text-[var(--purple-lavender)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p className="text-xs text-brand-purple " style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{formatFileSize(image.file_size_bytes)}
</p>
</div>
@@ -320,7 +320,7 @@ const AdminGallery = () => {
<h3 className="text-lg font-semibold text-[var(--purple-ink)] mb-2" style={{ fontFamily: "'Inter', sans-serif" }}>
No Images Yet
</h3>
<p className="text-[var(--purple-lavender)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p className="text-brand-purple " style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Upload images to create a gallery for this event.
</p>
</div>
@@ -367,14 +367,14 @@ const AdminGallery = () => {
<Button
onClick={() => setEditingCaption(null)}
variant="outline"
className="border-[var(--neutral-800)] text-[var(--purple-lavender)] rounded-xl"
className="border-[var(--neutral-800)] text-brand-purple rounded-xl"
style={{ fontFamily: "'Inter', sans-serif" }}
>
Cancel
</Button>
<Button
onClick={handleUpdateCaption}
className="bg-[var(--purple-lavender)] hover:bg-[var(--purple-ink)] text-white rounded-xl"
className="bg-brand-purple hover:bg-[var(--purple-ink)] text-white rounded-xl"
style={{ fontFamily: "'Inter', sans-serif" }}
>
Save Caption

View File

@@ -248,7 +248,7 @@ const AdminMembers = () => {
<h1 className="text-4xl md:text-5xl font-semibold text-[var(--purple-ink)] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}>
Members Management
</h1>
<p className="text-lg text-[var(--purple-lavender)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p className="text-lg text-brand-purple " style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Manage paying members and their subscriptions.
</p>
</div>
@@ -257,7 +257,7 @@ const AdminMembers = () => {
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
className="bg-[var(--purple-lavender)] hover:bg-[var(--purple-ink)] text-white rounded-xl h-12 px-6"
className="bg-brand-purple hover:bg-[var(--purple-ink)] text-white rounded-xl h-12 px-6"
disabled={exporting}
>
{exporting ? (
@@ -298,7 +298,7 @@ const AdminMembers = () => {
{hasPermission('users.invite') && (
<Button
onClick={() => setInviteDialogOpen(true)}
className="bg-[var(--purple-lavender)] hover:bg-[var(--purple-ink)] text-white rounded-xl h-12 px-6"
className="bg-brand-purple hover:bg-[var(--purple-ink)] text-white rounded-xl h-12 px-6"
>
<Mail className="h-5 w-5 mr-2" />
Invite Member
@@ -321,25 +321,25 @@ const AdminMembers = () => {
{/* Stats */}
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-4 gap-4 mb-8">
<Card className="p-6 bg-background rounded-2xl border border-[var(--neutral-800)]">
<p className="text-sm text-[var(--purple-lavender)] mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Total Members</p>
<p className="text-sm text-brand-purple mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Total Members</p>
<p className="text-3xl font-semibold text-[var(--purple-ink)]" style={{ fontFamily: "'Inter', sans-serif" }}>
{users.length}
</p>
</Card>
<Card className="p-6 bg-background rounded-2xl border border-[var(--neutral-800)]">
<p className="text-sm text-[var(--purple-lavender)] mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Active</p>
<p className="text-sm text-brand-purple mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Active</p>
<p className="text-3xl font-semibold text-[var(--purple-ink)]" style={{ fontFamily: "'Inter', sans-serif" }}>
{users.filter(u => u.status === 'active').length}
</p>
</Card>
<Card className="p-6 bg-background rounded-2xl border border-[var(--neutral-800)]">
<p className="text-sm text-[var(--purple-lavender)] mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Payment Pending</p>
<p className="text-sm text-brand-purple mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Payment Pending</p>
<p className="text-3xl font-semibold text-[var(--purple-ink)]" style={{ fontFamily: "'Inter', sans-serif" }}>
{users.filter(u => u.status === 'payment_pending').length}
</p>
</Card>
<Card className="p-6 bg-background rounded-2xl border border-[var(--neutral-800)]">
<p className="text-sm text-[var(--purple-lavender)] mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Inactive</p>
<p className="text-sm text-brand-purple mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Inactive</p>
<p className="text-3xl font-semibold text-[var(--purple-ink)]" style={{ fontFamily: "'Inter', sans-serif" }}>
{users.filter(u => u.status === 'inactive').length}
</p>
@@ -350,12 +350,12 @@ const AdminMembers = () => {
<Card className="p-6 bg-background rounded-2xl border border-[var(--neutral-800)] mb-8">
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div className="relative">
<Search className="absolute left-4 top-1/2 transform -translate-y-1/2 h-5 w-5 text-[var(--purple-lavender)]" />
<Search className="absolute left-4 top-1/2 transform -translate-y-1/2 h-5 w-5 text-brand-purple " />
<Input
placeholder="Search by name or email..."
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
className="pl-12 h-14 rounded-xl border-2 border-[var(--neutral-800)] focus:border-[var(--purple-lavender)]"
className="pl-12 h-14 rounded-xl border-2 border-[var(--neutral-800)] focus:border-brand-purple "
data-testid="search-members-input"
/>
</div>
@@ -381,7 +381,7 @@ const AdminMembers = () => {
{/* Members List */}
{loading ? (
<div className="text-center py-20">
<p className="text-[var(--purple-lavender)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Loading members...</p>
<p className="text-brand-purple " style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Loading members...</p>
</div>
) : filteredUsers.length > 0 ? (
<div className="space-y-4">
@@ -406,7 +406,7 @@ const AdminMembers = () => {
</h3>
{getStatusBadge(user.status)}
</div>
<div className="grid md:grid-cols-2 gap-2 text-sm text-[var(--purple-lavender)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<div className="grid md:grid-cols-2 gap-2 text-sm text-brand-purple " style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p>Email: {user.email}</p>
<p>Phone: {user.phone}</p>
<p>Joined: {new Date(user.created_at).toLocaleDateString()}</p>
@@ -432,7 +432,7 @@ const AdminMembers = () => {
)}
</span>
</div>
<div className="grid grid-cols-2 md:grid-cols-4 gap-2 text-xs text-[var(--purple-lavender)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<div className="grid grid-cols-2 md:grid-cols-4 gap-2 text-xs text-brand-purple " style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{reminderInfo.emailReminders > 0 && (
<p>
<Mail className="inline h-3 w-3 mr-1" />
@@ -459,7 +459,7 @@ const AdminMembers = () => {
)}
</div>
{reminderInfo.lastReminderAt && (
<p className="mt-2 text-xs text-[var(--purple-lavender)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p className="mt-2 text-xs text-brand-purple " style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Last reminder: {new Date(reminderInfo.lastReminderAt).toLocaleDateString()} at {new Date(reminderInfo.lastReminderAt).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}
</p>
)}
@@ -478,7 +478,7 @@ const AdminMembers = () => {
<Button
variant="outline"
size="sm"
className="border-[var(--purple-lavender)] text-[var(--purple-lavender)] hover:bg-[var(--purple-lavender)] hover:text-white"
className="border-brand-purple text-brand-purple hover:bg-brand-purple hover:text-white"
>
<Eye className="h-4 w-4 mr-1" />
View Profile
@@ -500,7 +500,7 @@ const AdminMembers = () => {
{/* Status Management */}
<div className="flex items-center gap-2">
<span className="text-sm text-[var(--purple-lavender)] whitespace-nowrap" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<span className="text-sm text-brand-purple whitespace-nowrap" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Change Status:
</span>
<Select
@@ -531,7 +531,7 @@ const AdminMembers = () => {
<h3 className="text-2xl font-semibold text-[var(--purple-ink)] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}>
No Members Found
</h3>
<p className="text-[var(--purple-lavender)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p className="text-brand-purple " style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{searchQuery || statusFilter !== 'all'
? 'Try adjusting your filters'
: 'No members yet'}

View File

@@ -175,7 +175,7 @@ const AdminNewsletters = () => {
if (loading) {
return (
<div className="flex items-center justify-center min-h-[60vh]">
<p className="text-[var(--purple-lavender)]">Loading newsletters...</p>
<p className="text-brand-purple ">Loading newsletters...</p>
</div>
);
}
@@ -188,14 +188,14 @@ const AdminNewsletters = () => {
<h1 className="text-3xl font-semibold text-[var(--purple-ink)]" style={{ fontFamily: "'Inter', sans-serif" }}>
Newsletter Management
</h1>
<p className="text-[var(--purple-lavender)] mt-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p className="text-brand-purple mt-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Create and manage newsletter archive
</p>
</div>
{hasPermission('newsletters.create') && (
<Button
onClick={handleCreate}
className="bg-[var(--purple-lavender)] text-white hover:bg-[var(--purple-muted)] rounded-full flex items-center gap-2"
className="bg-brand-purple text-white hover:bg-[var(--purple-muted)] rounded-full flex items-center gap-2"
>
<Plus className="h-4 w-4" />
Add Newsletter
@@ -207,9 +207,9 @@ const AdminNewsletters = () => {
{newsletters.length === 0 ? (
<Card className="p-12 text-center">
<FileText className="h-16 w-16 text-[var(--neutral-800)] mx-auto mb-4" />
<p className="text-[var(--purple-lavender)] text-lg mb-4">No newsletters yet</p>
<p className="text-brand-purple text-lg mb-4">No newsletters yet</p>
{hasPermission('newsletters.create') && (
<Button onClick={handleCreate} className="bg-[var(--purple-lavender)] text-white">
<Button onClick={handleCreate} className="bg-brand-purple text-white">
<Plus className="h-4 w-4 mr-2" />
Create First Newsletter
</Button>
@@ -232,20 +232,20 @@ const AdminNewsletters = () => {
{newsletter.title}
</h3>
{newsletter.description && (
<p className="text-[var(--purple-lavender)] mb-3">{newsletter.description}</p>
<p className="text-brand-purple mb-3">{newsletter.description}</p>
)}
<div className="flex items-center gap-3">
<Badge className="bg-[var(--neutral-800)] text-[var(--purple-ink)]">
{formatDate(newsletter.published_date)}
</Badge>
<Badge variant="outline" className="border-[var(--purple-lavender)] text-[var(--purple-lavender)]">
<Badge variant="outline" className="border-brand-purple text-brand-purple ">
{newsletter.document_type === 'upload' ? 'PDF Upload' : 'Link'}
</Badge>
<Button
variant="ghost"
size="sm"
onClick={() => window.open(newsletter.document_url, '_blank')}
className="text-[var(--purple-lavender)] hover:text-[var(--purple-muted)]"
className="text-brand-purple hover:text-[var(--purple-muted)]"
>
<ExternalLink className="h-4 w-4 mr-1" />
View
@@ -259,7 +259,7 @@ const AdminNewsletters = () => {
variant="outline"
size="sm"
onClick={() => handleEdit(newsletter)}
className="border-[var(--purple-lavender)] text-[var(--purple-lavender)]"
className="border-brand-purple text-brand-purple "
>
<Edit className="h-4 w-4" />
</Button>
@@ -361,12 +361,12 @@ const AdminNewsletters = () => {
required={!selectedNewsletter}
/>
{uploadedFile && (
<p className="text-sm text-[var(--purple-lavender)] mt-1">
<p className="text-sm text-brand-purple mt-1">
Selected: {uploadedFile.name}
</p>
)}
{selectedNewsletter && !uploadedFile && (
<p className="text-sm text-[var(--purple-lavender)] mt-1">
<p className="text-sm text-brand-purple mt-1">
Current file will be kept if no new file is selected
</p>
)}
@@ -381,7 +381,7 @@ const AdminNewsletters = () => {
placeholder="https://docs.google.com/document/d/... or https://example.com/file.pdf"
required
/>
<p className="text-sm text-[var(--purple-lavender)] mt-1">
<p className="text-sm text-brand-purple mt-1">
Paste the shareable link to your document (Google Docs, Dropbox, PDF URL, etc.)
</p>
</div>
@@ -398,7 +398,7 @@ const AdminNewsletters = () => {
</Button>
<Button
type="submit"
className="bg-[var(--purple-lavender)] text-white"
className="bg-brand-purple text-white"
disabled={submitting}
>
{submitting ? 'Saving...' : selectedNewsletter ? 'Update' : 'Create'}

View File

@@ -188,7 +188,7 @@ const AdminPermissions = () => {
const getRoleBadge = (role) => {
const config = {
admin: { label: 'Admin', color: 'bg-[var(--green-light)]', icon: Shield },
member: { label: 'Member', color: 'bg-[var(--purple-lavender)]', icon: Shield },
member: { label: 'Member', color: 'bg-brand-purple ', icon: Shield },
guest: { label: 'Guest', color: 'bg-gray-400', icon: Shield }
};
@@ -206,7 +206,7 @@ const AdminPermissions = () => {
if (loading) {
return (
<div className="text-center py-20">
<p className="text-[var(--purple-lavender)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p className="text-brand-purple " style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Loading permissions...
</p>
</div>
@@ -220,7 +220,7 @@ const AdminPermissions = () => {
<h2 className="text-3xl font-semibold text-[var(--purple-ink)] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}>
Access Denied
</h2>
<p className="text-lg text-[var(--purple-lavender)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p className="text-lg text-brand-purple " style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
You don't have permission to manage role permissions.
</p>
<p className="text-sm text-gray-500 mt-2">
@@ -236,7 +236,7 @@ const AdminPermissions = () => {
<h1 className="text-4xl md:text-5xl font-semibold text-[var(--purple-ink)] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}>
Permission Management
</h1>
<p className="text-lg text-[var(--purple-lavender)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p className="text-lg text-brand-purple " style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Configure granular permissions for each role. Superadmin always has all permissions.
</p>
</div>
@@ -260,7 +260,7 @@ const AdminPermissions = () => {
{/* Stats */}
<div className="grid md:grid-cols-3 gap-4 mb-8">
<Card className="p-6 bg-background rounded-2xl border border-[var(--neutral-800)]">
<p className="text-sm text-[var(--purple-lavender)] mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p className="text-sm text-brand-purple mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Total Permissions
</p>
<p className="text-3xl font-semibold text-[var(--purple-ink)]" style={{ fontFamily: "'Inter', sans-serif" }}>
@@ -268,7 +268,7 @@ const AdminPermissions = () => {
</p>
</Card>
<Card className="p-6 bg-background rounded-2xl border border-[var(--neutral-800)]">
<p className="text-sm text-[var(--purple-lavender)] mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p className="text-sm text-brand-purple mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Assigned
</p>
<p className="text-3xl font-semibold text-[var(--purple-ink)]" style={{ fontFamily: "'Inter', sans-serif" }}>
@@ -276,7 +276,7 @@ const AdminPermissions = () => {
</p>
</Card>
<Card className="p-6 bg-background rounded-2xl border border-[var(--neutral-800)]">
<p className="text-sm text-[var(--purple-lavender)] mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p className="text-sm text-brand-purple mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Modules
</p>
<p className="text-3xl font-semibold text-[var(--purple-ink)]" style={{ fontFamily: "'Inter', sans-serif" }}>
@@ -300,21 +300,21 @@ const AdminPermissions = () => {
checked={isModuleFullySelected(role, module)}
onCheckedChange={() => toggleModule(role, module)}
onClick={(e) => e.stopPropagation()}
className="h-6 w-6 border-2 border-[var(--purple-lavender)] data-[state=checked]:bg-[var(--purple-lavender)]"
className="h-6 w-6 border-2 border-brand-purple data-[state=checked]:bg-brand-purple "
/>
<div>
<h3 className="text-xl font-semibold text-[var(--purple-ink)] capitalize" style={{ fontFamily: "'Inter', sans-serif" }}>
{module}
</h3>
<p className="text-sm text-[var(--purple-lavender)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p className="text-sm text-brand-purple " style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{getModuleProgress(role, module)} permissions
</p>
</div>
</div>
{expandedModules[module] ? (
<ChevronUp className="h-6 w-6 text-[var(--purple-lavender)]" />
<ChevronUp className="h-6 w-6 text-brand-purple " />
) : (
<ChevronDown className="h-6 w-6 text-[var(--purple-lavender)]" />
<ChevronDown className="h-6 w-6 text-brand-purple " />
)}
</div>
</div>
@@ -331,13 +331,13 @@ const AdminPermissions = () => {
<Checkbox
checked={selectedPermissions[role].includes(perm.code)}
onCheckedChange={() => togglePermission(role, perm.code)}
className="mt-1 h-5 w-5 border-2 border-[var(--purple-lavender)] data-[state=checked]:bg-[var(--purple-lavender)]"
className="mt-1 h-5 w-5 border-2 border-brand-purple data-[state=checked]:bg-brand-purple "
/>
<div className="flex-1">
<p className="font-semibold text-[var(--purple-ink)] mb-1" style={{ fontFamily: "'Inter', sans-serif" }}>
{perm.name}
</p>
<p className="text-sm text-[var(--purple-lavender)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p className="text-sm text-brand-purple " style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{perm.description}
</p>
<p className="text-xs text-gray-400 mt-1 font-mono">
@@ -357,7 +357,7 @@ const AdminPermissions = () => {
</Tabs>
{/* Superadmin Note */}
<Card className="p-6 bg-gradient-to-r from-[var(--purple-lavender)] to-[var(--purple-ink)] rounded-2xl border-none mb-8">
<Card className="p-6 bg-gradient-to-r from-brand-purple to-[var(--purple-ink)] rounded-2xl border-none mb-8">
<div className="flex items-start gap-4">
<Lock className="h-6 w-6 text-white flex-shrink-0 mt-1" />
<div className="text-white">
@@ -392,7 +392,7 @@ const AdminPermissions = () => {
<AlertDialogTitle className="text-2xl text-[var(--purple-ink)]" style={{ fontFamily: "'Inter', sans-serif" }}>
Confirm Permission Changes
</AlertDialogTitle>
<AlertDialogDescription className="text-[var(--purple-lavender)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<AlertDialogDescription className="text-brand-purple " style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Are you sure you want to update permissions for <span className="font-semibold capitalize">{selectedRole}</span>?
This will immediately affect all users with this role.
</AlertDialogDescription>

View File

@@ -134,7 +134,7 @@ const AdminPlans = () => {
<h1 className="text-4xl md:text-5xl font-semibold text-[var(--purple-ink)] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}>
Subscription Plans
</h1>
<p className="text-lg text-[var(--purple-lavender)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p className="text-lg text-brand-purple " style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Manage membership plans and pricing.
</p>
</div>
@@ -153,25 +153,25 @@ const AdminPlans = () => {
{/* Stats */}
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-4 gap-4 mb-8">
<Card className="p-6 bg-background rounded-2xl border border-[var(--neutral-800)]">
<p className="text-sm text-[var(--purple-lavender)] mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Total Plans</p>
<p className="text-sm text-brand-purple mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Total Plans</p>
<p className="text-3xl font-semibold text-[var(--purple-ink)]" style={{ fontFamily: "'Inter', sans-serif" }}>
{plans.length}
</p>
</Card>
<Card className="p-6 bg-background rounded-2xl border border-[var(--neutral-800)]">
<p className="text-sm text-[var(--purple-lavender)] mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Active Plans</p>
<p className="text-sm text-brand-purple mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Active Plans</p>
<p className="text-3xl font-semibold text-[var(--purple-ink)]" style={{ fontFamily: "'Inter', sans-serif" }}>
{plans.filter(p => p.active).length}
</p>
</Card>
<Card className="p-6 bg-background rounded-2xl border border-[var(--neutral-800)]">
<p className="text-sm text-[var(--purple-lavender)] mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Total Subscribers</p>
<p className="text-sm text-brand-purple mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Total Subscribers</p>
<p className="text-3xl font-semibold text-[var(--purple-ink)]" style={{ fontFamily: "'Inter', sans-serif" }}>
{plans.reduce((sum, p) => sum + (p.subscriber_count || 0), 0)}
</p>
</Card>
<Card className="p-6 bg-background rounded-2xl border border-[var(--neutral-800)]">
<p className="text-sm text-[var(--purple-lavender)] mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Revenue (Annual Est.)</p>
<p className="text-sm text-brand-purple mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Revenue (Annual Est.)</p>
<p className="text-3xl font-semibold text-[var(--purple-ink)]" style={{ fontFamily: "'Inter', sans-serif" }}>
{formatPrice(
plans.reduce((sum, p) => {
@@ -189,12 +189,12 @@ const AdminPlans = () => {
<Card className="p-6 bg-background rounded-2xl border border-[var(--neutral-800)] mb-8">
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div className="relative">
<Search className="absolute left-4 top-1/2 transform -translate-y-1/2 h-5 w-5 text-[var(--purple-lavender)]" />
<Search className="absolute left-4 top-1/2 transform -translate-y-1/2 h-5 w-5 text-brand-purple " />
<Input
placeholder="Search plans..."
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
className="pl-12 h-14 rounded-xl border-2 border-[var(--neutral-800)] focus:border-[var(--purple-lavender)]"
className="pl-12 h-14 rounded-xl border-2 border-[var(--neutral-800)] focus:border-brand-purple "
/>
</div>
<Select value={activeFilter} onValueChange={setActiveFilter}>
@@ -213,7 +213,7 @@ const AdminPlans = () => {
{/* Plans Grid */}
{loading ? (
<div className="text-center py-20">
<p className="text-[var(--purple-lavender)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Loading plans...</p>
<p className="text-brand-purple " style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Loading plans...</p>
</div>
) : filteredPlans.length > 0 ? (
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
@@ -221,7 +221,7 @@ const AdminPlans = () => {
<Card
key={plan.id}
className={`p-6 bg-background rounded-2xl border-2 transition-all hover:shadow-lg ${plan.active
? 'border-[var(--neutral-800)] hover:border-[var(--purple-lavender)]'
? 'border-[var(--neutral-800)] hover:border-brand-purple '
: 'border-gray-400 opacity-60'
}`}
>
@@ -242,7 +242,7 @@ const AdminPlans = () => {
</Badge>
)}
{plan.custom_cycle_enabled && (
<Badge className="bg-[var(--purple-lavender)] text-white">
<Badge className="bg-brand-purple text-white">
Custom Dates
</Badge>
)}
@@ -260,7 +260,7 @@ const AdminPlans = () => {
{/* Description */}
{plan.description && (
<p className="text-sm text-[var(--purple-lavender)] mb-4 line-clamp-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p className="text-sm text-brand-purple mb-4 line-clamp-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{plan.description}
</p>
)}
@@ -272,16 +272,16 @@ const AdminPlans = () => {
{formatPrice(plan.minimum_price_cents || plan.price_cents)}
</div>
{plan.suggested_price_cents && plan.suggested_price_cents > plan.minimum_price_cents && (
<div className="text-lg text-[var(--purple-lavender)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<div className="text-lg text-brand-purple " style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
(Suggested: {formatPrice(plan.suggested_price_cents)})
</div>
)}
</div>
<p className="text-sm text-[var(--purple-lavender)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p className="text-sm text-brand-purple " style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{getBillingCycleLabel(plan.billing_cycle)}
</p>
{plan.custom_cycle_enabled && (
<p className="text-xs text-[var(--purple-lavender)] font-mono mt-1">
<p className="text-xs text-brand-purple font-mono mt-1">
{formatCustomCycleDates(plan)}
</p>
)}
@@ -294,7 +294,7 @@ const AdminPlans = () => {
onClick={() => handleEditPlan(plan)}
variant="outline"
size="sm"
className="flex-1 border-[var(--purple-lavender)] text-[var(--purple-lavender)] hover:bg-[var(--purple-lavender)] hover:text-white rounded-full"
className="flex-1 border-brand-purple text-brand-purple hover:bg-brand-purple hover:text-white rounded-full"
>
<Edit className="h-4 w-4 mr-1" />
Edit
@@ -314,7 +314,7 @@ const AdminPlans = () => {
{/* Warning for plans with subscribers */}
{plan.subscriber_count > 0 && (
<p className="text-xs text-[var(--purple-lavender)] mt-2 text-center" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p className="text-xs text-brand-purple mt-2 text-center" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Cannot delete plan with active subscribers
</p>
)}
@@ -327,7 +327,7 @@ const AdminPlans = () => {
<h3 className="text-2xl font-semibold text-[var(--purple-ink)] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}>
No Plans Found
</h3>
<p className="text-[var(--purple-lavender)] mb-6" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p className="text-brand-purple mb-6" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{searchQuery || activeFilter !== 'all'
? 'Try adjusting your filters'
: 'Create your first subscription plan to get started'}
@@ -359,7 +359,7 @@ const AdminPlans = () => {
<h2 className="text-xl sm:text-2xl font-semibold text-[var(--purple-ink)] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}>
Delete Plan
</h2>
<p className="text-sm sm:text-base text-[var(--purple-lavender)] mb-6" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p className="text-sm sm:text-base text-brand-purple mb-6" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Are you sure you want to delete "{planToDelete?.name}"? This action
will deactivate the plan and it won't be available for new subscriptions.
</p>

View File

@@ -188,7 +188,7 @@ const AdminRoles = () => {
{/* Header */}
<div className="flex justify-between items-center">
<div>
<h1 className="text-3xl font-bold">Role Management</h1>
<h1 className="text-3xl font-bold text-[var(--purple-ink)]">Role Management</h1>
<p className="text-gray-600 mt-1">
Create and manage custom roles with specific permissions
</p>

View File

@@ -99,7 +99,7 @@ const AdminStaff = () => {
const getRoleBadge = (role) => {
const config = {
superadmin: { label: 'Superadmin', className: 'bg-[var(--purple-lavender)] text-white' },
superadmin: { label: 'Superadmin', className: 'bg-brand-purple text-white' },
admin: { label: 'Admin', className: 'bg-[var(--green-light)] text-white' },
moderator: { label: 'Moderator', className: 'bg-[var(--neutral-800)] text-[var(--purple-ink)]' },
staff: { label: 'Staff', className: 'bg-gray-200 text-gray-700' },
@@ -137,7 +137,7 @@ const AdminStaff = () => {
<h1 className="text-4xl md:text-5xl font-semibold text-[var(--purple-ink)] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}>
Staff Management
</h1>
<p className="text-lg text-[var(--purple-lavender)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p className="text-lg text-brand-purple " style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Manage internal team members and their roles.
</p>
</div>
@@ -145,7 +145,7 @@ const AdminStaff = () => {
{hasPermission('users.create') && (
<Button
onClick={() => setInviteDialogOpen(true)}
className="bg-[var(--purple-lavender)] hover:bg-[var(--purple-ink)] text-white rounded-xl h-12 px-6"
className="bg-brand-purple hover:bg-[var(--purple-ink)] dark:hover:bg-brand-dark-lavender text-white rounded-xl h-12 px-6"
>
<Mail className="h-5 w-5 mr-2" />
Invite Staff
@@ -154,7 +154,7 @@ const AdminStaff = () => {
{hasPermission('users.create') && (
<Button
onClick={() => setCreateDialogOpen(true)}
className="bg-[var(--green-light)] hover:bg-[var(--green-fern)] text-white rounded-xl h-12 px-6"
className="bg-[var(--green-light)] hover:bg-[var(--green-sage)] text-white rounded-xl h-12 px-6"
>
<UserPlus className="h-5 w-5 mr-2" />
Create Staff
@@ -167,25 +167,25 @@ const AdminStaff = () => {
{/* Stats */}
<div className="grid md:grid-cols-4 gap-4 mb-8">
<Card className="p-6 bg-background rounded-2xl border border-[var(--neutral-800)]">
<p className="text-sm text-[var(--purple-lavender)] mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Total Staff</p>
<p className="text-sm text-brand-purple mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Total Staff</p>
<p className="text-3xl font-semibold text-[var(--purple-ink)]" style={{ fontFamily: "'Inter', sans-serif" }}>
{users.length}
</p>
</Card>
<Card className="p-6 bg-background rounded-2xl border border-[var(--neutral-800)]">
<p className="text-sm text-[var(--purple-lavender)] mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Admins</p>
<p className="text-sm text-brand-purple mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Admins</p>
<p className="text-3xl font-semibold text-[var(--purple-ink)]" style={{ fontFamily: "'Inter', sans-serif" }}>
{users.filter(u => ['admin', 'superadmin'].includes(u.role)).length}
</p>
</Card>
<Card className="p-6 bg-background rounded-2xl border border-[var(--neutral-800)]">
<p className="text-sm text-[var(--purple-lavender)] mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Moderators</p>
<p className="text-sm text-brand-purple mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Moderators</p>
<p className="text-3xl font-semibold text-[var(--purple-ink)]" style={{ fontFamily: "'Inter', sans-serif" }}>
{users.filter(u => u.role === 'moderator').length}
</p>
</Card>
<Card className="p-6 bg-background rounded-2xl border border-[var(--neutral-800)]">
<p className="text-sm text-[var(--purple-lavender)] mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Active</p>
<p className="text-sm text-brand-purple mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Active</p>
<p className="text-3xl font-semibold text-[var(--purple-ink)]" style={{ fontFamily: "'Inter', sans-serif" }}>
{users.filter(u => u.status === 'active').length}
</p>
@@ -210,12 +210,12 @@ const AdminStaff = () => {
<Card className="p-6 bg-background rounded-2xl border border-[var(--neutral-800)] mb-8">
<div className="grid md:grid-cols-2 gap-4">
<div className="relative">
<Search className="absolute left-4 top-1/2 transform -translate-y-1/2 h-5 w-5 text-[var(--purple-lavender)]" />
<Search className="absolute left-4 top-1/2 transform -translate-y-1/2 h-5 w-5 text-brand-purple " />
<Input
placeholder="Search by name or email..."
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
className="pl-12 h-14 rounded-xl border-2 border-[var(--neutral-800)] focus:border-[var(--purple-lavender)]"
className="pl-12 h-14 rounded-xl border-2 border-[var(--neutral-800)] focus:border-brand-purple "
data-testid="search-staff-input"
/>
</div>
@@ -238,7 +238,7 @@ const AdminStaff = () => {
{/* Staff List */}
{loading ? (
<div className="text-center py-20">
<p className="text-[var(--purple-lavender)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Loading staff...</p>
<p className="text-brand-purple " style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Loading staff...</p>
</div>
) : filteredUsers.length > 0 ? (
<div className="space-y-4">
@@ -264,7 +264,7 @@ const AdminStaff = () => {
{getRoleBadge(user.role)}
{getStatusBadge(user.status)}
</div>
<div className="grid md:grid-cols-2 gap-2 text-sm text-[var(--purple-lavender)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<div className="grid md:grid-cols-2 gap-2 text-sm text-brand-purple " style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p>Email: {user.email}</p>
<p>Phone: {user.phone}</p>
<p>Joined: {new Date(user.created_at).toLocaleDateString()}</p>
@@ -280,7 +280,7 @@ const AdminStaff = () => {
<Button
onClick={() => navigate(`/admin/users/${user.id}`)}
variant="outline"
className="border-2 border-[var(--purple-lavender)] text-[var(--purple-lavender)] hover:bg-[var(--lavender-300)] rounded-full px-4 py-2"
className="border-2 border-brand-purple text-brand-purple hover:bg-[var(--lavender-300)] rounded-full px-4 py-2"
>
<Edit className="h-4 w-4 mr-2" />
Manage
@@ -330,7 +330,7 @@ const AdminStaff = () => {
<h3 className="text-2xl font-semibold text-[var(--purple-ink)] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}>
No Staff Found
</h3>
<p className="text-[var(--purple-lavender)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p className="text-brand-purple " style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{searchQuery || roleFilter !== 'all'
? 'Try adjusting your filters'
: 'No staff members yet'}

View File

@@ -277,7 +277,7 @@ Proceed with activation?`;
if (loading) {
return (
<div className="flex items-center justify-center min-h-screen">
<Loader2 className="h-12 w-12 animate-spin text-[var(--purple-lavender)]" />
<Loader2 className="h-12 w-12 animate-spin text-brand-purple " />
</div>
);
}
@@ -289,7 +289,7 @@ Proceed with activation?`;
<h1 className="text-3xl font-semibold text-[var(--purple-ink)]" style={{ fontFamily: "'Inter', sans-serif" }}>
Subscription Management
</h1>
<p className="text-[var(--purple-lavender)] mt-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p className="text-brand-purple mt-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
View and manage all member subscriptions
</p>
</div>
@@ -299,7 +299,7 @@ Proceed with activation?`;
<Card className="p-6 bg-background rounded-2xl border-2 border-[var(--neutral-800)]">
<div className="flex items-center justify-between">
<div>
<p className="text-sm text-[var(--purple-lavender)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p className="text-sm text-brand-purple " style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Total Subscriptions
</p>
<p className="text-3xl font-bold text-[var(--purple-ink)] mt-2" style={{ fontFamily: "'Inter', sans-serif" }}>
@@ -307,7 +307,7 @@ Proceed with activation?`;
</p>
</div>
<div className="p-3 bg-[var(--neutral-800)]/20 rounded-full">
<CreditCard className="h-6 w-6 text-[var(--purple-lavender)]" />
<CreditCard className="h-6 w-6 text-brand-purple " />
</div>
</div>
</Card>
@@ -315,7 +315,7 @@ Proceed with activation?`;
<Card className="p-6 bg-background rounded-2xl border-2 border-[var(--neutral-800)]">
<div className="flex items-center justify-between">
<div>
<p className="text-sm text-[var(--purple-lavender)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p className="text-sm text-brand-purple " style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Active Members
</p>
<p className="text-3xl font-bold text-[var(--green-light)] mt-2" style={{ fontFamily: "'Inter', sans-serif" }}>
@@ -331,7 +331,7 @@ Proceed with activation?`;
<Card className="p-6 bg-background rounded-2xl border-2 border-[var(--neutral-800)]">
<div className="flex items-center justify-between">
<div>
<p className="text-sm text-[var(--purple-lavender)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p className="text-sm text-brand-purple " style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Total Revenue
</p>
<p className="text-3xl font-bold text-[var(--purple-ink)] mt-2" style={{ fontFamily: "'Inter', sans-serif" }}>
@@ -339,7 +339,7 @@ Proceed with activation?`;
</p>
</div>
<div className="p-3 bg-[var(--neutral-800)]/20 rounded-full">
<DollarSign className="h-6 w-6 text-[var(--purple-lavender)]" />
<DollarSign className="h-6 w-6 text-brand-purple " />
</div>
</div>
</Card>
@@ -347,7 +347,7 @@ Proceed with activation?`;
<Card className="p-6 bg-background rounded-2xl border-2 border-[var(--neutral-800)]">
<div className="flex items-center justify-between">
<div>
<p className="text-sm text-[var(--purple-lavender)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p className="text-sm text-brand-purple " style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Total Donations
</p>
<p className="text-3xl font-bold text-[var(--orange-light)] mt-2" style={{ fontFamily: "'Inter', sans-serif" }}>
@@ -367,12 +367,12 @@ Proceed with activation?`;
{/* Search */}
<div className="md:col-span-1">
<div className="relative">
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 h-5 w-5 text-[var(--purple-lavender)]" />
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 h-5 w-5 text-brand-purple " />
<Input
placeholder="Search by name or email..."
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
className="pl-10 rounded-xl border-2 border-[var(--neutral-800)] focus:border-[var(--purple-lavender)]"
className="pl-10 rounded-xl border-2 border-[var(--neutral-800)] focus:border-brand-purple "
/>
</div>
</div>
@@ -409,7 +409,7 @@ Proceed with activation?`;
</div>
<div className="mt-4 flex items-center justify-between">
<div className="text-sm text-[var(--purple-lavender)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<div className="text-sm text-brand-purple " style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Showing {filteredSubscriptions.length} of {subscriptions.length} subscriptions
</div>
@@ -430,14 +430,14 @@ Proceed with activation?`;
onClick={() => handleExport('all')}
className="cursor-pointer hover:bg-[var(--lavender-300)] rounded-lg p-3"
>
<FileDown className="h-4 w-4 mr-2 text-[var(--purple-lavender)]" />
<FileDown className="h-4 w-4 mr-2 text-brand-purple " />
<span className="text-[var(--purple-ink)]">Export All Subscriptions</span>
</DropdownMenuItem>
<DropdownMenuItem
onClick={() => handleExport('current')}
className="cursor-pointer hover:bg-[var(--lavender-300)] rounded-lg p-3"
>
<FileDown className="h-4 w-4 mr-2 text-[var(--purple-lavender)]" />
<FileDown className="h-4 w-4 mr-2 text-brand-purple " />
<span className="text-[var(--purple-ink)]">Export Current View</span>
</DropdownMenuItem>
</DropdownMenuContent>
@@ -460,7 +460,7 @@ Proceed with activation?`;
<p className="font-semibold text-[var(--purple-ink)]" style={{ fontFamily: "'Inter', sans-serif" }}>
{sub.user.first_name} {sub.user.last_name}
</p>
<p className="text-sm text-[var(--purple-lavender)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p className="text-sm text-brand-purple " style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{sub.user.email}
</p>
</div>
@@ -470,12 +470,12 @@ Proceed with activation?`;
{/* Plan & Period */}
<div className="grid grid-cols-2 gap-3 text-sm">
<div>
<p className="text-xs text-[var(--purple-lavender)] mb-1">Plan</p>
<p className="text-xs text-brand-purple mb-1">Plan</p>
<p className="font-medium text-[var(--purple-ink)]">{sub.plan.name}</p>
<p className="text-xs text-[var(--purple-lavender)]">{sub.plan.billing_cycle}</p>
<p className="text-xs text-brand-purple ">{sub.plan.billing_cycle}</p>
</div>
<div>
<p className="text-xs text-[var(--purple-lavender)] mb-1">Period</p>
<p className="text-xs text-brand-purple mb-1">Period</p>
<p className="text-[var(--purple-ink)]">
{new Date(sub.current_period_start).toLocaleDateString()} -
{new Date(sub.current_period_end).toLocaleDateString()}
@@ -486,19 +486,19 @@ Proceed with activation?`;
{/* Pricing */}
<div className="grid grid-cols-3 gap-2 text-sm bg-background/50 p-3 rounded">
<div>
<p className="text-xs text-[var(--purple-lavender)] mb-1">Base Fee</p>
<p className="text-xs text-brand-purple mb-1">Base Fee</p>
<p className="font-medium text-[var(--purple-ink)]">
${(sub.base_fee_cents / 100).toFixed(2)}
</p>
</div>
<div>
<p className="text-xs text-[var(--purple-lavender)] mb-1">Donation</p>
<p className="text-xs text-brand-purple mb-1">Donation</p>
<p className="font-medium text-[var(--purple-ink)]">
${(sub.donation_cents / 100).toFixed(2)}
</p>
</div>
<div>
<p className="text-xs text-[var(--purple-lavender)] mb-1">Total</p>
<p className="text-xs text-brand-purple mb-1">Total</p>
<p className="font-semibold text-[var(--purple-ink)]">
${(sub.total_cents / 100).toFixed(2)}
</p>
@@ -512,7 +512,7 @@ Proceed with activation?`;
size="sm"
variant="outline"
onClick={() => handleEdit(sub)}
className="flex-1 text-[var(--purple-lavender)] hover:bg-[var(--neutral-800)]"
className="flex-1 text-brand-purple hover:bg-[var(--neutral-800)]"
>
<Edit className="h-4 w-4 mr-2" />
Edit
@@ -534,7 +534,7 @@ Proceed with activation?`;
</Card>
))
) : (
<div className="p-12 text-center text-[var(--purple-lavender)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<div className="p-12 text-center text-brand-purple " style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
No subscriptions found
</div>
)}
@@ -579,7 +579,7 @@ Proceed with activation?`;
<div className="font-medium text-[var(--purple-ink)]" style={{ fontFamily: "'Inter', sans-serif" }}>
{sub.user.first_name} {sub.user.last_name}
</div>
<div className="text-sm text-[var(--purple-lavender)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<div className="text-sm text-brand-purple " style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{sub.user.email}
</div>
</td>
@@ -587,7 +587,7 @@ Proceed with activation?`;
<div className="text-[var(--purple-ink)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{sub.plan.name}
</div>
<div className="text-xs text-[var(--purple-lavender)]">
<div className="text-xs text-brand-purple ">
{sub.plan.billing_cycle}
</div>
</td>
@@ -599,7 +599,7 @@ Proceed with activation?`;
<td className="p-4">
<div className="text-sm text-[var(--purple-ink)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<div>{formatDate(sub.start_date)}</div>
<div className="text-xs text-[var(--purple-lavender)]">to {formatDate(sub.end_date)}</div>
<div className="text-xs text-brand-purple ">to {formatDate(sub.end_date)}</div>
</div>
</td>
<td className="p-4 text-right text-[var(--purple-ink)]" style={{ fontFamily: "'Inter', sans-serif" }}>
@@ -618,7 +618,7 @@ Proceed with activation?`;
size="sm"
variant="outline"
onClick={() => handleEdit(sub)}
className="text-[var(--purple-lavender)] hover:bg-[var(--neutral-800)]"
className="text-brand-purple hover:bg-[var(--neutral-800)]"
>
<Edit className="h-4 w-4" />
</Button>
@@ -639,7 +639,7 @@ Proceed with activation?`;
))
) : (
<tr>
<td colSpan="8" className="p-12 text-center text-[var(--purple-lavender)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<td colSpan="8" className="p-12 text-center text-brand-purple " style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
No subscriptions found
</td>
</tr>
@@ -656,7 +656,7 @@ Proceed with activation?`;
<DialogTitle className="text-2xl font-semibold text-[var(--purple-ink)]" style={{ fontFamily: "'Inter', sans-serif" }}>
Edit Subscription
</DialogTitle>
<DialogDescription className="text-[var(--purple-lavender)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<DialogDescription className="text-brand-purple " style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Update subscription status or end date for {selectedSubscription?.user.first_name} {selectedSubscription?.user.last_name}
</DialogDescription>
</DialogHeader>
@@ -740,13 +740,13 @@ Proceed with activation?`;
End Date
</Label>
<div className="relative">
<Calendar className="absolute left-4 top-1/2 transform -translate-y-1/2 h-5 w-5 text-[var(--purple-lavender)]" />
<Calendar className="absolute left-4 top-1/2 transform -translate-y-1/2 h-5 w-5 text-brand-purple " />
<Input
id="end_date"
type="date"
value={editFormData.end_date}
onChange={(e) => setEditFormData({ ...editFormData, end_date: e.target.value })}
className="pl-12 rounded-xl border-2 border-[var(--neutral-800)] focus:border-[var(--purple-lavender)]"
className="pl-12 rounded-xl border-2 border-[var(--neutral-800)] focus:border-brand-purple "
/>
</div>
</div>

View File

@@ -240,7 +240,7 @@ const AdminUserView = () => {
</div>
{/* Contact Info */}
<div className="grid md:grid-cols-2 gap-4 text-[var(--purple-lavender)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<div className="grid md:grid-cols-2 gap-4 text-brand-purple " style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<div className="flex items-center gap-2">
<Mail className="h-4 w-4" />
<span>{user.email}</span>
@@ -272,7 +272,7 @@ const AdminUserView = () => {
onClick={handleResetPasswordRequest}
disabled={resetPasswordLoading}
variant="outline"
className="border-2 border-[var(--purple-lavender)] text-[var(--purple-lavender)] hover:bg-[var(--lavender-300)] rounded-full px-4 py-2 disabled:opacity-50"
className="border-2 border-brand-purple text-brand-purple hover:bg-[var(--lavender-300)] rounded-full px-4 py-2 disabled:opacity-50"
>
<Lock className="h-4 w-4 mr-2" />
{resetPasswordLoading ? 'Resetting...' : 'Reset Password'}
@@ -321,7 +321,7 @@ const AdminUserView = () => {
</Button>
)}
<div className="flex items-center gap-2 text-sm text-[var(--purple-lavender)] ml-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<div className="flex items-center gap-2 text-sm text-brand-purple ml-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<AlertTriangle className="h-4 w-4" />
<span>User will receive a temporary password via email</span>
</div>
@@ -336,12 +336,12 @@ const AdminUserView = () => {
<div className="grid md:grid-cols-2 gap-6">
<div>
<label className="text-sm font-medium text-[var(--purple-lavender)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Address</label>
<label className="text-sm font-medium text-brand-purple " style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Address</label>
<p className="text-[var(--purple-ink)] mt-1" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>{user.address}</p>
</div>
<div>
<label className="text-sm font-medium text-[var(--purple-lavender)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Date of Birth</label>
<label className="text-sm font-medium text-brand-purple " style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Date of Birth</label>
<p className="text-[var(--purple-ink)] mt-1" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{new Date(user.date_of_birth).toLocaleDateString()}
</p>
@@ -349,7 +349,7 @@ const AdminUserView = () => {
{user.partner_first_name && (
<div>
<label className="text-sm font-medium text-[var(--purple-lavender)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Partner</label>
<label className="text-sm font-medium text-brand-purple " style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Partner</label>
<p className="text-[var(--purple-ink)] mt-1" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{user.partner_first_name} {user.partner_last_name}
</p>
@@ -358,14 +358,14 @@ const AdminUserView = () => {
{user.referred_by_member_name && (
<div>
<label className="text-sm font-medium text-[var(--purple-lavender)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Referred By</label>
<label className="text-sm font-medium text-brand-purple " style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Referred By</label>
<p className="text-[var(--purple-ink)] mt-1" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>{user.referred_by_member_name}</p>
</div>
)}
{user.lead_sources && user.lead_sources.length > 0 && (
<div className="md:col-span-2">
<label className="text-sm font-medium text-[var(--purple-lavender)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Lead Sources</label>
<label className="text-sm font-medium text-brand-purple " style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Lead Sources</label>
<div className="flex flex-wrap gap-2 mt-2">
{user.lead_sources.map((source, idx) => (
<Badge key={idx} variant="outline">{source}</Badge>
@@ -384,9 +384,9 @@ const AdminUserView = () => {
</h2>
{subscriptionsLoading ? (
<p className="text-[var(--purple-lavender)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Loading subscriptions...</p>
<p className="text-brand-purple " style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Loading subscriptions...</p>
) : subscriptions.length === 0 ? (
<p className="text-[var(--purple-lavender)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>No subscriptions found for this member.</p>
<p className="text-brand-purple " style={{ fontFamily: "'Nunito Sans', sans-serif" }}>No subscriptions found for this member.</p>
) : (
<div className="space-y-6">
{subscriptions.map((sub) => (
@@ -396,7 +396,7 @@ const AdminUserView = () => {
<h3 className="text-lg font-semibold text-[var(--purple-ink)]" style={{ fontFamily: "'Inter', sans-serif" }}>
{sub.plan.name}
</h3>
<p className="text-sm text-[var(--purple-lavender)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p className="text-sm text-brand-purple " style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{sub.plan.billing_cycle}
</p>
</div>
@@ -411,42 +411,42 @@ const AdminUserView = () => {
<div className="grid md:grid-cols-2 gap-4 text-sm">
<div>
<label className="text-[var(--purple-lavender)] font-medium" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Start Date</label>
<label className="text-brand-purple font-medium" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Start Date</label>
<p className="text-[var(--purple-ink)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{new Date(sub.start_date).toLocaleDateString()}
</p>
</div>
{sub.end_date && (
<div>
<label className="text-[var(--purple-lavender)] font-medium" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>End Date</label>
<label className="text-brand-purple font-medium" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>End Date</label>
<p className="text-[var(--purple-ink)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{new Date(sub.end_date).toLocaleDateString()}
</p>
</div>
)}
<div>
<label className="text-[var(--purple-lavender)] font-medium" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Base Amount</label>
<label className="text-brand-purple font-medium" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Base Amount</label>
<p className="text-[var(--purple-ink)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
${(sub.base_subscription_cents / 100).toFixed(2)}
</p>
</div>
{sub.donation_cents > 0 && (
<div>
<label className="text-[var(--purple-lavender)] font-medium" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Donation</label>
<label className="text-brand-purple font-medium" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Donation</label>
<p className="text-[var(--purple-ink)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
${(sub.donation_cents / 100).toFixed(2)}
</p>
</div>
)}
<div>
<label className="text-[var(--purple-lavender)] font-medium" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Total Paid</label>
<label className="text-brand-purple font-medium" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Total Paid</label>
<p className="text-[var(--purple-ink)] font-semibold" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
${(sub.amount_paid_cents / 100).toFixed(2)}
</p>
</div>
{sub.payment_method && (
<div>
<label className="text-[var(--purple-lavender)] font-medium" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Payment Method</label>
<label className="text-brand-purple font-medium" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Payment Method</label>
<p className="text-[var(--purple-ink)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{sub.payment_method}
</p>
@@ -454,7 +454,7 @@ const AdminUserView = () => {
)}
{sub.stripe_subscription_id && (
<div className="md:col-span-2">
<label className="text-[var(--purple-lavender)] font-medium" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Stripe Subscription ID</label>
<label className="text-brand-purple font-medium" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Stripe Subscription ID</label>
<p className="text-[var(--purple-ink)] text-xs font-mono" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{sub.stripe_subscription_id}
</p>

View File

@@ -282,7 +282,7 @@ const AdminValidations = () => {
<h1 className="text-4xl md:text-5xl font-semibold text-[var(--purple-ink)] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}>
Validation Queue
</h1>
<p className="text-lg text-[var(--purple-lavender)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p className="text-lg text-brand-purple " style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Review and validate pending membership applications.
</p>
</div>
@@ -291,31 +291,31 @@ const AdminValidations = () => {
<Card className="p-6 bg-background rounded-2xl border border-[var(--neutral-800)] mb-8">
<div className="grid grid-cols-2 md:grid-cols-5 gap-4">
<div>
<p className="text-sm text-[var(--purple-lavender)] mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Total Pending</p>
<p className="text-sm text-brand-purple mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Total Pending</p>
<p className="text-3xl font-semibold text-[var(--purple-ink)]" style={{ fontFamily: "'Inter', sans-serif" }}>
{pendingUsers.length}
</p>
</div>
<div>
<p className="text-sm text-[var(--purple-lavender)] mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Awaiting Email</p>
<p className="text-sm text-brand-purple mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Awaiting Email</p>
<p className="text-3xl font-semibold text-[var(--purple-ink)]" style={{ fontFamily: "'Inter', sans-serif" }}>
{pendingUsers.filter(u => u.status === 'pending_email').length}
</p>
</div>
<div>
<p className="text-sm text-[var(--purple-lavender)] mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Pending Validation</p>
<p className="text-sm text-brand-purple mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Pending Validation</p>
<p className="text-3xl font-semibold text-[var(--purple-ink)]" style={{ fontFamily: "'Inter', sans-serif" }}>
{pendingUsers.filter(u => u.status === 'pending_validation').length}
</p>
</div>
<div>
<p className="text-sm text-[var(--purple-lavender)] mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Pre-Validated</p>
<p className="text-sm text-brand-purple mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Pre-Validated</p>
<p className="text-3xl font-semibold text-[var(--purple-ink)]" style={{ fontFamily: "'Inter', sans-serif" }}>
{pendingUsers.filter(u => u.status === 'pre_validated').length}
</p>
</div>
<div>
<p className="text-sm text-[var(--purple-lavender)] mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Payment Pending</p>
<p className="text-sm text-brand-purple mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Payment Pending</p>
<p className="text-3xl font-semibold text-[var(--purple-ink)]" style={{ fontFamily: "'Inter', sans-serif" }}>
{pendingUsers.filter(u => u.status === 'payment_pending').length}
</p>
@@ -333,12 +333,12 @@ const AdminValidations = () => {
<Card className="p-6 bg-background rounded-2xl border border-[var(--neutral-800)] mb-8">
<div className="grid md:grid-cols-3 gap-4">
<div className="relative md:col-span-2">
<Search className="absolute left-4 top-1/2 transform -translate-y-1/2 h-5 w-5 text-[var(--purple-lavender)]" />
<Search className="absolute left-4 top-1/2 transform -translate-y-1/2 h-5 w-5 text-brand-purple " />
<Input
placeholder="Search by name, email, or phone..."
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
className="pl-12 h-14 rounded-xl border-2 border-[var(--neutral-800)] focus:border-[var(--purple-lavender)]"
className="pl-12 h-14 rounded-xl border-2 border-[var(--neutral-800)] focus:border-brand-purple "
/>
</div>
@@ -361,7 +361,7 @@ const AdminValidations = () => {
{/* Table */}
{loading ? (
<div className="text-center py-20">
<p className="text-[var(--purple-lavender)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Loading pending applications...</p>
<p className="text-brand-purple " style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Loading pending applications...</p>
</div>
) : filteredUsers.length > 0 ? (
<>
@@ -507,7 +507,7 @@ const AdminValidations = () => {
<div className="mt-8 flex flex-col md:flex-row justify-between items-center gap-4">
{/* Page size selector */}
<div className="flex items-center gap-2">
<p className="text-sm text-[var(--purple-lavender)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Show</p>
<p className="text-sm text-brand-purple " style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Show</p>
<Select
value={itemsPerPage.toString()}
onValueChange={(val) => {
@@ -525,7 +525,7 @@ const AdminValidations = () => {
<SelectItem value="100">100</SelectItem>
</SelectContent>
</Select>
<p className="text-sm text-[var(--purple-lavender)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p className="text-sm text-brand-purple " style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
entries (showing {(currentPage - 1) * itemsPerPage + 1}-
{Math.min(currentPage * itemsPerPage, filteredUsers.length)} of {filteredUsers.length})
</p>
@@ -586,7 +586,7 @@ const AdminValidations = () => {
<h3 className="text-2xl font-semibold text-[var(--purple-ink)] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}>
No Pending Validations
</h3>
<p className="text-[var(--purple-lavender)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p className="text-brand-purple " style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{searchQuery || statusFilter !== 'all'
? 'Try adjusting your filters'
: 'All applications have been reviewed!'}