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

@@ -187,7 +187,7 @@ export default function AddToCalendarButton({
>
<RefreshCw className="h-4 w-4 mr-2" />
Subscribe to My Events
<div className="text-xs text-[var(--purple-lavender)] mt-0.5">
<div className="text-xs text-brand-purple mt-0.5">
Auto-syncs your RSVP'd events
</div>
</DropdownMenuItem>
@@ -198,7 +198,7 @@ export default function AddToCalendarButton({
>
<Download className="h-4 w-4 mr-2" />
Download All Events
<div className="text-xs text-[var(--purple-lavender)] mt-0.5">
<div className="text-xs text-brand-purple mt-0.5">
One-time import of all upcoming events
</div>
</DropdownMenuItem>
@@ -206,7 +206,7 @@ export default function AddToCalendarButton({
)}
{!event && !showSubscribe && (
<div className="px-2 py-6 text-center text-sm text-[var(--purple-lavender)]">
<div className="px-2 py-6 text-center text-sm text-brand-purple ">
No event selected
</div>
)}

View File

@@ -211,7 +211,7 @@ const AdminSidebar = ({ isOpen, onToggle, isMobile }) => {
className={`
flex items-center gap-3 px-4 py-3 rounded-lg transition-all relative
${item.disabled
? 'opacity-50 cursor-not-allowed text-[var(--purple-lavender)]'
? 'opacity-50 cursor-not-allowed text-brand-purple '
: active
? 'bg-[var(--orange-light)]/10 text-[var(--orange-light)]'
: 'text-[var(--purple-ink)] hover:bg-[var(--neutral-800)]/20'

View File

@@ -64,12 +64,12 @@ export const AttendanceDialog = ({ event, open, onOpenChange, onSuccess }) => {
<div className="space-y-4 mt-4">
{rsvps.length === 0 ? (
<p className="text-center text-[var(--purple-lavender)] py-8" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>No RSVPs yet</p>
<p className="text-center text-brand-purple py-8" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>No RSVPs yet</p>
) : (
rsvps.map((rsvp) => (
<div
key={rsvp.user_id}
className="flex items-center gap-3 p-4 border-2 border-[var(--neutral-800)] rounded-xl hover:border-[var(--purple-lavender)] transition-colors"
className="flex items-center gap-3 p-4 border-2 border-[var(--neutral-800)] rounded-xl hover:border-brand-purple transition-colors"
>
<Checkbox
checked={attendance[rsvp.user_id] || false}
@@ -80,7 +80,7 @@ export const AttendanceDialog = ({ event, open, onOpenChange, onSuccess }) => {
/>
<div className="flex-1">
<p className="font-medium text-[var(--purple-ink)]" style={{ fontFamily: "'Inter', sans-serif" }}>{rsvp.user_name}</p>
<p className="text-sm text-[var(--purple-lavender)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>{rsvp.user_email}</p>
<p className="text-sm text-brand-purple " style={{ fontFamily: "'Nunito Sans', sans-serif" }}>{rsvp.user_email}</p>
</div>
{rsvp.attended && (
<span className="text-sm text-[var(--green-light)] font-medium">
@@ -103,7 +103,7 @@ export const AttendanceDialog = ({ event, open, onOpenChange, onSuccess }) => {
<Button
onClick={() => onOpenChange(false)}
variant="outline"
className="flex-1 border-2 border-[var(--neutral-800)] text-[var(--purple-lavender)] hover:bg-background hover:text-[var(--purple-ink)] rounded-full"
className="flex-1 border-2 border-[var(--neutral-800)] text-brand-purple hover:bg-background hover:text-[var(--purple-ink)] rounded-full"
>
Cancel
</Button>

View File

@@ -76,7 +76,7 @@ const ChangePasswordDialog = ({ open, onOpenChange }) => {
Change Password
</DialogTitle>
</div>
<DialogDescription className="text-[var(--purple-lavender)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<DialogDescription className="text-brand-purple " style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Update your password to keep your account secure.
</DialogDescription>
</DialogHeader>
@@ -92,7 +92,7 @@ const ChangePasswordDialog = ({ open, onOpenChange }) => {
value={formData.currentPassword}
onChange={handleInputChange}
placeholder="Enter current password"
className="h-12 rounded-xl border-2 border-[var(--neutral-800)] focus:border-[var(--purple-lavender)]"
className="h-12 rounded-xl border-2 border-[var(--neutral-800)] focus:border-brand-purple "
/>
</div>
@@ -106,7 +106,7 @@ const ChangePasswordDialog = ({ open, onOpenChange }) => {
value={formData.newPassword}
onChange={handleInputChange}
placeholder="Enter new password (min. 6 characters)"
className="h-12 rounded-xl border-2 border-[var(--neutral-800)] focus:border-[var(--purple-lavender)]"
className="h-12 rounded-xl border-2 border-[var(--neutral-800)] focus:border-brand-purple "
/>
</div>
@@ -120,7 +120,7 @@ const ChangePasswordDialog = ({ open, onOpenChange }) => {
value={formData.confirmPassword}
onChange={handleInputChange}
placeholder="Re-enter new password"
className="h-12 rounded-xl border-2 border-[var(--neutral-800)] focus:border-[var(--purple-lavender)]"
className="h-12 rounded-xl border-2 border-[var(--neutral-800)] focus:border-brand-purple "
/>
</div>

View File

@@ -48,8 +48,8 @@ const ConfirmationDialog = ({
},
info: {
icon: Info,
iconColor: 'text-[var(--purple-lavender)]',
confirmButtonClass: 'bg-[var(--purple-lavender)] text-white hover:bg-[var(--purple-plum)] rounded-full px-6',
iconColor: 'text-brand-purple ',
confirmButtonClass: 'bg-brand-purple text-white hover:bg-[var(--purple-plum)] rounded-full px-6',
},
success: {
icon: CheckCircle,
@@ -77,7 +77,7 @@ const ConfirmationDialog = ({
{title}
</AlertDialogTitle>
<AlertDialogDescription
className="text-[var(--purple-lavender)] text-sm leading-relaxed"
className="text-brand-purple text-sm leading-relaxed"
style={{ fontFamily: "'Nunito Sans', sans-serif" }}
>
{description}
@@ -87,7 +87,7 @@ const ConfirmationDialog = ({
</AlertDialogHeader>
<AlertDialogFooter className="p-6 pt-4 bg-[var(--lavender-500)] flex-row gap-3 justify-end">
<AlertDialogCancel
className="border-2 border-[var(--neutral-800)] text-[var(--purple-lavender)] hover:bg-background rounded-full px-6"
className="border-2 border-[var(--neutral-800)] text-brand-purple hover:bg-background rounded-full px-6"
disabled={loading}
>
{cancelText}

View File

@@ -125,7 +125,7 @@ const CreateMemberDialog = ({ open, onOpenChange, onSuccess }) => {
<UserPlus className="h-6 w-6" />
Create Member
</DialogTitle>
<DialogDescription className="text-[var(--purple-lavender)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<DialogDescription className="text-brand-purple " style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Create a new member account with direct login access. Member will be created immediately.
</DialogDescription>
</DialogHeader>
@@ -143,7 +143,7 @@ const CreateMemberDialog = ({ open, onOpenChange, onSuccess }) => {
type="email"
value={formData.email}
onChange={(e) => handleChange('email', e.target.value)}
className="rounded-xl border-2 border-[var(--neutral-800)] focus:border-[var(--purple-lavender)]"
className="rounded-xl border-2 border-[var(--neutral-800)] focus:border-brand-purple "
placeholder="member@example.com"
/>
{errors.email && (
@@ -160,7 +160,7 @@ const CreateMemberDialog = ({ open, onOpenChange, onSuccess }) => {
type="password"
value={formData.password}
onChange={(e) => handleChange('password', e.target.value)}
className="rounded-xl border-2 border-[var(--neutral-800)] focus:border-[var(--purple-lavender)]"
className="rounded-xl border-2 border-[var(--neutral-800)] focus:border-brand-purple "
placeholder="Minimum 8 characters"
/>
{errors.password && (
@@ -179,7 +179,7 @@ const CreateMemberDialog = ({ open, onOpenChange, onSuccess }) => {
id="first_name"
value={formData.first_name}
onChange={(e) => handleChange('first_name', e.target.value)}
className="rounded-xl border-2 border-[var(--neutral-800)] focus:border-[var(--purple-lavender)]"
className="rounded-xl border-2 border-[var(--neutral-800)] focus:border-brand-purple "
placeholder="John"
/>
{errors.first_name && (
@@ -195,7 +195,7 @@ const CreateMemberDialog = ({ open, onOpenChange, onSuccess }) => {
id="last_name"
value={formData.last_name}
onChange={(e) => handleChange('last_name', e.target.value)}
className="rounded-xl border-2 border-[var(--neutral-800)] focus:border-[var(--purple-lavender)]"
className="rounded-xl border-2 border-[var(--neutral-800)] focus:border-brand-purple "
placeholder="Doe"
/>
{errors.last_name && (
@@ -214,7 +214,7 @@ const CreateMemberDialog = ({ open, onOpenChange, onSuccess }) => {
type="tel"
value={formData.phone}
onChange={(e) => handleChange('phone', e.target.value)}
className="rounded-xl border-2 border-[var(--neutral-800)] focus:border-[var(--purple-lavender)]"
className="rounded-xl border-2 border-[var(--neutral-800)] focus:border-brand-purple "
placeholder="(555) 123-4567"
/>
{errors.phone && (
@@ -231,7 +231,7 @@ const CreateMemberDialog = ({ open, onOpenChange, onSuccess }) => {
id="address"
value={formData.address}
onChange={(e) => handleChange('address', e.target.value)}
className="rounded-xl border-2 border-[var(--neutral-800)] focus:border-[var(--purple-lavender)]"
className="rounded-xl border-2 border-[var(--neutral-800)] focus:border-brand-purple "
placeholder="123 Main St"
/>
</div>
@@ -244,7 +244,7 @@ const CreateMemberDialog = ({ open, onOpenChange, onSuccess }) => {
id="city"
value={formData.city}
onChange={(e) => handleChange('city', e.target.value)}
className="rounded-xl border-2 border-[var(--neutral-800)] focus:border-[var(--purple-lavender)]"
className="rounded-xl border-2 border-[var(--neutral-800)] focus:border-brand-purple "
placeholder="San Francisco"
/>
</div>
@@ -255,7 +255,7 @@ const CreateMemberDialog = ({ open, onOpenChange, onSuccess }) => {
id="state"
value={formData.state}
onChange={(e) => handleChange('state', e.target.value)}
className="rounded-xl border-2 border-[var(--neutral-800)] focus:border-[var(--purple-lavender)]"
className="rounded-xl border-2 border-[var(--neutral-800)] focus:border-brand-purple "
placeholder="CA"
maxLength={2}
/>
@@ -267,7 +267,7 @@ const CreateMemberDialog = ({ open, onOpenChange, onSuccess }) => {
id="zipcode"
value={formData.zipcode}
onChange={(e) => handleChange('zipcode', e.target.value)}
className="rounded-xl border-2 border-[var(--neutral-800)] focus:border-[var(--purple-lavender)]"
className="rounded-xl border-2 border-[var(--neutral-800)] focus:border-brand-purple "
placeholder="94102"
/>
</div>
@@ -282,7 +282,7 @@ const CreateMemberDialog = ({ open, onOpenChange, onSuccess }) => {
type="date"
value={formData.date_of_birth}
onChange={(e) => handleChange('date_of_birth', e.target.value)}
className="rounded-xl border-2 border-[var(--neutral-800)] focus:border-[var(--purple-lavender)]"
className="rounded-xl border-2 border-[var(--neutral-800)] focus:border-brand-purple "
/>
</div>
@@ -293,7 +293,7 @@ const CreateMemberDialog = ({ open, onOpenChange, onSuccess }) => {
type="date"
value={formData.member_since}
onChange={(e) => handleChange('member_since', e.target.value)}
className="rounded-xl border-2 border-[var(--neutral-800)] focus:border-[var(--purple-lavender)]"
className="rounded-xl border-2 border-[var(--neutral-800)] focus:border-brand-purple "
/>
</div>
</div>

View File

@@ -105,7 +105,7 @@ const CreateStaffDialog = ({ open, onOpenChange, onSuccess }) => {
<UserPlus className="h-6 w-6" />
Create Staff Member
</DialogTitle>
<DialogDescription className="text-[var(--purple-lavender)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<DialogDescription className="text-brand-purple " style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Create a new staff account with direct login access. User will be created immediately.
</DialogDescription>
</DialogHeader>
@@ -122,7 +122,7 @@ const CreateStaffDialog = ({ open, onOpenChange, onSuccess }) => {
type="email"
value={formData.email}
onChange={(e) => handleChange('email', e.target.value)}
className="rounded-xl border-2 border-[var(--neutral-800)] focus:border-[var(--purple-lavender)]"
className="rounded-xl border-2 border-[var(--neutral-800)] focus:border-brand-purple "
placeholder="staff@example.com"
/>
{errors.email && (
@@ -140,7 +140,7 @@ const CreateStaffDialog = ({ open, onOpenChange, onSuccess }) => {
type="password"
value={formData.password}
onChange={(e) => handleChange('password', e.target.value)}
className="rounded-xl border-2 border-[var(--neutral-800)] focus:border-[var(--purple-lavender)]"
className="rounded-xl border-2 border-[var(--neutral-800)] focus:border-brand-purple "
placeholder="Minimum 8 characters"
/>
{errors.password && (
@@ -157,7 +157,7 @@ const CreateStaffDialog = ({ open, onOpenChange, onSuccess }) => {
id="first_name"
value={formData.first_name}
onChange={(e) => handleChange('first_name', e.target.value)}
className="rounded-xl border-2 border-[var(--neutral-800)] focus:border-[var(--purple-lavender)]"
className="rounded-xl border-2 border-[var(--neutral-800)] focus:border-brand-purple "
placeholder="John"
/>
{errors.first_name && (
@@ -174,7 +174,7 @@ const CreateStaffDialog = ({ open, onOpenChange, onSuccess }) => {
id="last_name"
value={formData.last_name}
onChange={(e) => handleChange('last_name', e.target.value)}
className="rounded-xl border-2 border-[var(--neutral-800)] focus:border-[var(--purple-lavender)]"
className="rounded-xl border-2 border-[var(--neutral-800)] focus:border-brand-purple "
placeholder="Doe"
/>
{errors.last_name && (
@@ -192,7 +192,7 @@ const CreateStaffDialog = ({ open, onOpenChange, onSuccess }) => {
type="tel"
value={formData.phone}
onChange={(e) => handleChange('phone', e.target.value)}
className="rounded-xl border-2 border-[var(--neutral-800)] focus:border-[var(--purple-lavender)]"
className="rounded-xl border-2 border-[var(--neutral-800)] focus:border-brand-purple "
placeholder="(555) 123-4567"
/>
{errors.phone && (

View File

@@ -144,7 +144,7 @@ const ImportMembersDialog = ({ open, onOpenChange, onSuccess }) => {
<Upload className="h-6 w-6" />
{importResult ? 'Import Results' : 'Import Members from CSV'}
</DialogTitle>
<DialogDescription className="text-[var(--purple-lavender)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<DialogDescription className="text-brand-purple " style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{importResult
? 'Review the import results below'
: 'Upload a CSV file to bulk import members. Ensure the CSV has the required columns.'}
@@ -155,7 +155,7 @@ const ImportMembersDialog = ({ open, onOpenChange, onSuccess }) => {
// Upload Form
<div className="grid gap-6 py-4">
{/* CSV Format Instructions */}
<Alert className="border-[var(--purple-lavender)] bg-[var(--lavender-700)]">
<Alert className="border-brand-purple bg-[var(--lavender-700)]">
<AlertDescription className="text-sm text-[var(--purple-ink)]">
<strong>Required columns:</strong> Email, First Name, Last Name, Phone, Role
<br />
@@ -168,8 +168,8 @@ const ImportMembersDialog = ({ open, onOpenChange, onSuccess }) => {
{/* File Upload Area */}
<div
className={`border-2 border-dashed rounded-2xl p-12 text-center transition-colors ${dragActive
? 'border-[var(--purple-lavender)] bg-[var(--lavender-700)]'
: 'border-[var(--neutral-800)] hover:border-[var(--purple-lavender)] hover:bg-[var(--lavender-700)]'
? 'border-brand-purple bg-[var(--lavender-700)]'
: 'border-[var(--neutral-800)] hover:border-brand-purple hover:bg-[var(--lavender-700)]'
}`}
onDragEnter={handleDrag}
onDragLeave={handleDrag}
@@ -183,7 +183,7 @@ const ImportMembersDialog = ({ open, onOpenChange, onSuccess }) => {
<p className="text-lg font-semibold text-[var(--purple-ink)]" style={{ fontFamily: "'Inter', sans-serif" }}>
{file.name}
</p>
<p className="text-sm text-[var(--purple-lavender)]">
<p className="text-sm text-brand-purple ">
{(file.size / 1024).toFixed(2)} KB
</p>
</div>
@@ -203,7 +203,7 @@ const ImportMembersDialog = ({ open, onOpenChange, onSuccess }) => {
<p className="text-lg font-semibold text-[var(--purple-ink)] mb-2" style={{ fontFamily: "'Inter', sans-serif" }}>
Drag and drop your CSV file here
</p>
<p className="text-sm text-[var(--purple-lavender)] mb-4">or</p>
<p className="text-sm text-brand-purple mb-4">or</p>
<Label htmlFor="file-upload">
<Button variant="outline" className="rounded-xl cursor-pointer" asChild>
<span>Browse Files</span>
@@ -227,7 +227,7 @@ const ImportMembersDialog = ({ open, onOpenChange, onSuccess }) => {
checked={updateExisting}
onCheckedChange={setUpdateExisting}
id="update-existing"
className="h-5 w-5 border-2 border-[var(--purple-lavender)] data-[state=checked]:bg-[var(--purple-lavender)]"
className="h-5 w-5 border-2 border-brand-purple data-[state=checked]:bg-brand-purple "
/>
<Label htmlFor="update-existing" className="text-[var(--purple-ink)] cursor-pointer">
Update existing members (if email already exists)
@@ -240,7 +240,7 @@ const ImportMembersDialog = ({ open, onOpenChange, onSuccess }) => {
{/* Summary Cards */}
<div className="grid md:grid-cols-4 gap-4">
<div className="p-4 bg-background rounded-xl border border-[var(--neutral-800)] text-center">
<p className="text-sm text-[var(--purple-lavender)] mb-1">Total Rows</p>
<p className="text-sm text-brand-purple mb-1">Total Rows</p>
<p className="text-2xl font-semibold text-[var(--purple-ink)]">{importResult.total_rows}</p>
</div>
<div className="p-4 bg-green-50 rounded-xl border border-green-200 text-center">
@@ -276,7 +276,7 @@ const ImportMembersDialog = ({ open, onOpenChange, onSuccess }) => {
{importResult.errors.map((error, idx) => (
<TableRow key={idx} className="hover:bg-[var(--lavender-700)]">
<TableCell className="font-medium text-[var(--purple-ink)]">{error.row}</TableCell>
<TableCell className="text-[var(--purple-lavender)]">{error.email}</TableCell>
<TableCell className="text-brand-purple ">{error.email}</TableCell>
<TableCell className="text-red-600 text-sm">{error.error}</TableCell>
</TableRow>
))}

View File

@@ -129,7 +129,7 @@ const InviteStaffDialog = ({ open, onOpenChange, onSuccess }) => {
<Mail className="h-6 w-6" />
{invitationUrl ? 'Invitation Sent' : 'Invite Staff Member'}
</DialogTitle>
<DialogDescription className="text-[var(--purple-lavender)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<DialogDescription className="text-brand-purple " style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{invitationUrl
? 'The invitation has been sent via email. You can also copy the link below.'
: 'Send an email invitation to join as staff. They will set their own password.'}
@@ -148,7 +148,7 @@ const InviteStaffDialog = ({ open, onOpenChange, onSuccess }) => {
/>
<Button
onClick={copyToClipboard}
className="rounded-xl bg-[var(--purple-lavender)] hover:bg-[var(--purple-ink)] text-white flex-shrink-0"
className="rounded-xl bg-brand-purple hover:bg-[var(--purple-ink)] text-white flex-shrink-0"
>
{copied ? (
<>
@@ -178,7 +178,7 @@ const InviteStaffDialog = ({ open, onOpenChange, onSuccess }) => {
type="email"
value={formData.email}
onChange={(e) => handleChange('email', e.target.value)}
className="rounded-xl border-2 border-[var(--neutral-800)] focus:border-[var(--purple-lavender)]"
className="rounded-xl border-2 border-[var(--neutral-800)] focus:border-brand-purple "
placeholder="staff@example.com"
/>
{errors.email && (
@@ -195,7 +195,7 @@ const InviteStaffDialog = ({ open, onOpenChange, onSuccess }) => {
id="first_name"
value={formData.first_name}
onChange={(e) => handleChange('first_name', e.target.value)}
className="rounded-xl border-2 border-[var(--neutral-800)] focus:border-[var(--purple-lavender)]"
className="rounded-xl border-2 border-[var(--neutral-800)] focus:border-brand-purple "
placeholder="John"
/>
</div>
@@ -209,7 +209,7 @@ const InviteStaffDialog = ({ open, onOpenChange, onSuccess }) => {
id="last_name"
value={formData.last_name}
onChange={(e) => handleChange('last_name', e.target.value)}
className="rounded-xl border-2 border-[var(--neutral-800)] focus:border-[var(--purple-lavender)]"
className="rounded-xl border-2 border-[var(--neutral-800)] focus:border-brand-purple "
placeholder="Doe"
/>
</div>
@@ -224,7 +224,7 @@ const InviteStaffDialog = ({ open, onOpenChange, onSuccess }) => {
type="tel"
value={formData.phone}
onChange={(e) => handleChange('phone', e.target.value)}
className="rounded-xl border-2 border-[var(--neutral-800)] focus:border-[var(--purple-lavender)]"
className="rounded-xl border-2 border-[var(--neutral-800)] focus:border-brand-purple "
placeholder="(555) 123-4567"
/>
</div>

View File

@@ -4,7 +4,7 @@ import { Calendar, Users, User, BookOpen, FileText, DollarSign, Scale } from 'lu
const MemberFooter = () => {
return (
<footer className="bg-[var(--purple-ink)] text-white mt-auto">
<footer className="bg-brand-dark-lavender text-white mt-auto">
<div className="max-w-7xl mx-auto px-6 py-12">
<div className="grid md:grid-cols-4 gap-8">
{/* Logo & About */}
@@ -89,12 +89,12 @@ const MemberFooter = () => {
</Link>
</li>
<li>
<a href="/#contact" className="text-gray-300 hover:text-white transition-colors">
<a href="/membership/contact-us" className="text-gray-300 hover:text-white transition-colors">
Contact Us
</a>
</li>
<li>
<a href="/#donate" className="text-gray-300 hover:text-white transition-colors">
<a href="/membership/donate" className="text-gray-300 hover:text-white transition-colors">
Donate
</a>
</li>
@@ -106,10 +106,10 @@ const MemberFooter = () => {
{/* Bottom Bar */}
<div className="border-t border-[var(--purple-lavender)]">
<div className="max-w-7xl mx-auto px-6 py-4">
<div className="flex flex-col md:flex-row justify-between items-center gap-4 text-sm text-gray-400" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<div className="flex flex-col md:flex-row justify-between items-center gap-4 text-sm text-gray-300" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<div className="flex gap-6">
<a href="/#terms" className="hover:text-white transition-colors">Terms of Service</a>
<a href="/#privacy" className="hover:text-white transition-colors">Privacy Policy</a>
<a href="/membership/terms-of-service" className="hover:text-white transition-colors">Terms of Service</a>
<a href="/membership/privacy-policy" className="hover:text-white transition-colors">Privacy Policy</a>
</div>
<p>© 2025 LOAF. All rights reserved.</p>
</div>

View File

@@ -162,7 +162,7 @@ const PaymentActivationDialog = ({ open, onOpenChange, user, onSuccess }) => {
<DialogTitle className="text-2xl font-semibold text-[var(--purple-ink)]" style={{ fontFamily: "'Inter', sans-serif" }}>
Activate Manual Payment
</DialogTitle>
<DialogDescription className="text-[var(--purple-lavender)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<DialogDescription className="text-brand-purple " style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Record offline payment for {user.first_name} {user.last_name} ({user.email})
</DialogDescription>
</DialogHeader>
@@ -203,7 +203,7 @@ const PaymentActivationDialog = ({ open, onOpenChange, user, onSuccess }) => {
</SelectContent>
</Select>
{selectedPlan && (
<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" }}>
{selectedPlan.description || `${selectedPlan.billing_cycle} subscription`}
</p>
)}
@@ -222,11 +222,11 @@ const PaymentActivationDialog = ({ open, onOpenChange, user, onSuccess }) => {
placeholder="Enter amount"
value={formData.amount}
onChange={(e) => setFormData({ ...formData, amount: e.target.value })}
className="rounded-xl border-2 border-[var(--neutral-800)] focus:border-[var(--purple-lavender)]"
className="rounded-xl border-2 border-[var(--neutral-800)] focus:border-brand-purple "
required
/>
{selectedPlan && (
<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" }}>
Minimum: {formatPrice(selectedPlan.minimum_price_cents || selectedPlan.price_cents || 3000)}
</p>
)}
@@ -263,13 +263,13 @@ const PaymentActivationDialog = ({ open, onOpenChange, user, onSuccess }) => {
Payment 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="payment_date"
type="date"
value={formData.payment_date}
onChange={(e) => setFormData({ ...formData, payment_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 "
required
/>
</div>
@@ -308,7 +308,7 @@ const PaymentActivationDialog = ({ open, onOpenChange, user, onSuccess }) => {
onChange={(e) => setUseCustomPeriod(e.target.checked)}
className="rounded border-[var(--neutral-800)]"
/>
<Label htmlFor="use_custom_period" className="text-sm text-[var(--purple-lavender)] font-normal cursor-pointer" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<Label htmlFor="use_custom_period" className="text-sm text-brand-purple font-normal cursor-pointer" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Use custom dates instead of plan's billing cycle
</Label>
</div>
@@ -324,7 +324,7 @@ const PaymentActivationDialog = ({ open, onOpenChange, user, onSuccess }) => {
type="date"
value={formData.custom_period_start}
onChange={(e) => setFormData({ ...formData, custom_period_start: e.target.value })}
className="rounded-xl border-2 border-[var(--neutral-800)] focus:border-[var(--purple-lavender)]"
className="rounded-xl border-2 border-[var(--neutral-800)] focus:border-brand-purple "
required={useCustomPeriod}
/>
</div>
@@ -337,14 +337,14 @@ const PaymentActivationDialog = ({ open, onOpenChange, user, onSuccess }) => {
type="date"
value={formData.custom_period_end}
onChange={(e) => setFormData({ ...formData, custom_period_end: e.target.value })}
className="rounded-xl border-2 border-[var(--neutral-800)] focus:border-[var(--purple-lavender)]"
className="rounded-xl border-2 border-[var(--neutral-800)] focus:border-brand-purple "
required={useCustomPeriod}
/>
</div>
</div>
) : (
selectedPlan && (
<div className="text-sm text-[var(--purple-lavender)] bg-[var(--lavender-300)] p-3 rounded-lg space-y-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<div className="text-sm text-brand-purple bg-[var(--lavender-300)] p-3 rounded-lg space-y-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{selectedPlan.custom_cycle_enabled ? (
<>
<p>
@@ -386,7 +386,7 @@ const PaymentActivationDialog = ({ open, onOpenChange, user, onSuccess }) => {
placeholder="Additional notes about the payment..."
value={formData.notes}
onChange={(e) => setFormData({ ...formData, notes: e.target.value })}
className="rounded-xl border-2 border-[var(--neutral-800)] focus:border-[var(--purple-lavender)] min-h-[100px]"
className="rounded-xl border-2 border-[var(--neutral-800)] focus:border-brand-purple min-h-[100px]"
/>
</div>

View File

@@ -73,7 +73,7 @@ const PendingInvitationsTable = () => {
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' },
member: { label: 'Member', className: 'bg-[var(--neutral-800)] text-[var(--purple-ink)]' }
};
@@ -111,7 +111,7 @@ const PendingInvitationsTable = () => {
if (loading) {
return (
<div className="text-center py-8">
<p className="text-[var(--purple-lavender)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p className="text-brand-purple " style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Loading invitations...
</p>
</div>
@@ -125,7 +125,7 @@ const PendingInvitationsTable = () => {
<h3 className="text-xl font-semibold text-[var(--purple-ink)] mb-2" style={{ fontFamily: "'Inter', sans-serif" }}>
No Pending Invitations
</h3>
<p className="text-[var(--purple-lavender)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p className="text-brand-purple " style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
All invitations have been accepted or expired
</p>
</Card>
@@ -152,19 +152,19 @@ const PendingInvitationsTable = () => {
<TableCell className="font-medium text-[var(--purple-ink)]">
{invitation.email}
</TableCell>
<TableCell className="text-[var(--purple-lavender)]">
<TableCell className="text-brand-purple ">
{invitation.first_name && invitation.last_name
? `${invitation.first_name} ${invitation.last_name}`
: '-'}
</TableCell>
<TableCell>{getRoleBadge(invitation.role)}</TableCell>
<TableCell className="text-[var(--purple-lavender)]">
<TableCell className="text-brand-purple ">
{new Date(invitation.invited_at).toLocaleDateString()}
</TableCell>
<TableCell>
<div className="flex items-center gap-2">
<Clock className={`h-4 w-4 ${isExpiringSoon(invitation.expires_at) ? 'text-orange-500' : 'text-[var(--purple-lavender)]'}`} />
<span className={`text-sm ${isExpiringSoon(invitation.expires_at) ? 'text-orange-500 font-semibold' : 'text-[var(--purple-lavender)]'}`}>
<Clock className={`h-4 w-4 ${isExpiringSoon(invitation.expires_at) ? 'text-orange-500' : 'text-brand-purple '}`} />
<span className={`text-sm ${isExpiringSoon(invitation.expires_at) ? 'text-orange-500 font-semibold' : 'text-brand-purple '}`}>
{formatDate(invitation.expires_at)}
</span>
</div>
@@ -211,7 +211,7 @@ const PendingInvitationsTable = () => {
<AlertDialogTitle className="text-2xl text-[var(--purple-ink)]" style={{ fontFamily: "'Inter', sans-serif" }}>
Revoke Invitation
</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 revoke the invitation for{' '}
<span className="font-semibold">{revokeDialog.invitation?.email}</span>?
This action cannot be undone.

View File

@@ -164,7 +164,7 @@ const PlanDialog = ({ open, onOpenChange, plan, onSuccess }) => {
<DialogTitle className="text-2xl text-[var(--purple-ink)]" style={{ fontFamily: "'Inter', sans-serif" }}>
{plan ? 'Edit Plan' : 'Create New Plan'}
</DialogTitle>
<DialogDescription className="text-[var(--purple-lavender)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<DialogDescription className="text-brand-purple " style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{plan ? 'Update plan details below' : 'Enter plan details to create a new subscription plan'}
</DialogDescription>
</DialogHeader>
@@ -216,7 +216,7 @@ const PlanDialog = ({ open, onOpenChange, plan, onSuccess }) => {
required
className="mt-2"
/>
<p className="text-xs text-[var(--purple-lavender)] mt-1">Minimum $30</p>
<p className="text-xs text-brand-purple mt-1">Minimum $30</p>
</div>
<div>
@@ -232,7 +232,7 @@ const PlanDialog = ({ open, onOpenChange, plan, onSuccess }) => {
required
className="mt-2"
/>
<p className="text-xs text-[var(--purple-lavender)] mt-1">Pre-filled amount</p>
<p className="text-xs text-brand-purple mt-1">Pre-filled amount</p>
</div>
</div>
@@ -240,7 +240,7 @@ const PlanDialog = ({ open, onOpenChange, plan, onSuccess }) => {
<div className="flex items-center justify-between pt-2">
<div>
<Label htmlFor="allow_donation">Allow Donations</Label>
<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" }}>
Members can pay more than minimum
</p>
</div>
@@ -252,7 +252,7 @@ const PlanDialog = ({ open, onOpenChange, plan, onSuccess }) => {
onChange={(e) => setFormData({ ...formData, allow_donation: e.target.checked })}
className="sr-only peer"
/>
<div className="w-11 h-6 bg-gray-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-[var(--purple-lavender)]/20 rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-background after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-[var(--green-light)]"></div>
<div className="w-11 h-6 bg-gray-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-brand-purple /20 rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-background after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-[var(--green-light)]"></div>
</label>
</div>
</div>
@@ -283,7 +283,7 @@ const PlanDialog = ({ open, onOpenChange, plan, onSuccess }) => {
<h3 className="font-semibold text-[var(--purple-ink)]" style={{ fontFamily: "'Inter', sans-serif" }}>
Custom Billing Period
</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" }}>
Set recurring date range (e.g., Jan 1 - Dec 31 for calendar year)
</p>
@@ -361,7 +361,7 @@ const PlanDialog = ({ open, onOpenChange, plan, onSuccess }) => {
<div className="flex items-center justify-between">
<div>
<Label htmlFor="active">Active Status</Label>
<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" }}>
Inactive plans won't appear for new subscriptions
</p>
</div>
@@ -373,7 +373,7 @@ const PlanDialog = ({ open, onOpenChange, plan, onSuccess }) => {
onChange={(e) => setFormData({ ...formData, active: e.target.checked })}
className="sr-only peer"
/>
<div className="w-11 h-6 bg-gray-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-[var(--purple-lavender)]/20 rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-background after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-[var(--green-light)]"></div>
<div className="w-11 h-6 bg-gray-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-brand-purple /20 rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-background after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-[var(--green-light)]"></div>
</label>
</div>

View File

@@ -105,7 +105,7 @@ const PublicNavbar = () => {
</header>
{/* Main Header - Navigation */}
<header className=" bg-[var(--purple-lavender)] px-[20px] py-2 flex justify-between items-center">
<header className=" bg-brand-purple px-[20px] py-2 flex justify-between items-center">
<Link to="/">
<img src={loafLogo} alt="LOAF Logo" className="h-16 w-16 sm:h-20 sm:w-20 md:h-28 md:w-28 object-contain" />
</Link>
@@ -204,7 +204,7 @@ const PublicNavbar = () => {
/>
{/* Drawer */}
<div className="fixed right-0 top-0 h-full w-[280px] bg-[var(--purple-lavender)] shadow-xl overflow-y-auto">
<div className="fixed right-0 top-0 h-full w-[280px] bg-brand-purple shadow-xl overflow-y-auto">
{/* Header */}
<div className="flex justify-between items-center p-6 border-b border-[var(--purple-deep)]">
<span className="text-white text-lg font-semibold" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>

View File

@@ -41,17 +41,17 @@ export default function RejectionDialog({ open, onOpenChange, onConfirm, user, l
Reject Application
</DialogTitle>
</div>
<DialogDescription className="text-[var(--purple-lavender)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<DialogDescription className="text-brand-purple " style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
You are about to reject <strong>{user?.first_name} {user?.last_name}</strong>'s membership application.
</DialogDescription>
</DialogHeader>
<div className="space-y-4 py-4">
<div className="bg-[var(--lavender-400)] border border-[var(--neutral-800)] rounded-lg p-4">
<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" }}>
<strong>Applicant:</strong> {user?.email}
</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" }}>
<strong>Status:</strong> {user?.status}
</p>
</div>
@@ -74,7 +74,7 @@ export default function RejectionDialog({ open, onOpenChange, onConfirm, user, l
{error && (
<p className="text-sm text-red-500">{error}</p>
)}
<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" }}>
The applicant will receive an email with this reason.
</p>
</div>
@@ -85,7 +85,7 @@ export default function RejectionDialog({ open, onOpenChange, onConfirm, user, l
type="button"
onClick={handleClose}
variant="outline"
className="border-2 border-[var(--neutral-800)] text-[var(--purple-lavender)] hover:bg-[var(--lavender-300)] rounded-full px-6"
className="border-2 border-[var(--neutral-800)] text-brand-purple hover:bg-[var(--lavender-300)] rounded-full px-6"
disabled={loading}
>
<X className="h-4 w-4 mr-2" />

View File

@@ -371,14 +371,14 @@ export default function WordPressImportWizard({ open, onOpenChange, onSuccess })
<div className="space-y-6">
<div>
<h3 className="text-lg font-semibold text-[var(--purple-ink)] mb-2">Upload WordPress CSV Export</h3>
<p className="text-sm text-[var(--purple-lavender)]">
<p className="text-sm text-brand-purple ">
Select the WordPress user export CSV file. The file will be analyzed for data quality issues.
</p>
</div>
<Card className="p-6 border-2 border-dashed border-[var(--neutral-800)] bg-[var(--lavender-400)]">
<div className="flex flex-col items-center gap-4">
<Upload className="h-12 w-12 text-[var(--purple-lavender)]" />
<Upload className="h-12 w-12 text-brand-purple " />
<div className="text-center">
<Input
type="file"
@@ -387,7 +387,7 @@ export default function WordPressImportWizard({ open, onOpenChange, onSuccess })
className="max-w-xs"
/>
{uploadedFile && (
<p className="text-sm text-[var(--purple-lavender)] mt-2">
<p className="text-sm text-brand-purple mt-2">
Selected: {uploadedFile.name}
</p>
)}
@@ -399,7 +399,7 @@ export default function WordPressImportWizard({ open, onOpenChange, onSuccess })
<Button
onClick={handleUpload}
disabled={uploading}
className="w-full bg-[var(--purple-lavender)] hover:bg-[var(--purple-ink)]"
className="w-full bg-brand-purple hover:bg-[var(--purple-ink)]"
>
{uploading ? (
<>
@@ -466,7 +466,7 @@ export default function WordPressImportWizard({ open, onOpenChange, onSuccess })
<div className="space-y-6">
<div>
<h3 className="text-lg font-semibold text-[var(--purple-ink)] mb-2">Field Mapping</h3>
<p className="text-sm text-[var(--purple-lavender)]">
<p className="text-sm text-brand-purple ">
WordPress fields have been automatically mapped to LOAF platform fields.
</p>
</div>
@@ -538,7 +538,7 @@ export default function WordPressImportWizard({ open, onOpenChange, onSuccess })
<div className="space-y-6">
<div>
<h3 className="text-lg font-semibold text-[var(--purple-ink)] mb-2">Review & Adjust User Status</h3>
<p className="text-sm text-[var(--purple-lavender)]">
<p className="text-sm text-brand-purple ">
Review suggested status mappings and override as needed before import.
</p>
</div>
@@ -550,7 +550,7 @@ export default function WordPressImportWizard({ open, onOpenChange, onSuccess })
checked={selectedRows.size === previewData.length && previewData.length > 0}
onCheckedChange={toggleSelectAll}
/>
<span className="text-sm text-[var(--purple-lavender)] font-medium">
<span className="text-sm text-brand-purple font-medium">
{selectedRows.size > 0 ? `${selectedRows.size} selected` : 'Select all'}
</span>
{selectedRows.size > 0 && (
@@ -572,7 +572,7 @@ export default function WordPressImportWizard({ open, onOpenChange, onSuccess })
{/* Data table */}
{loading ? (
<div className="flex items-center justify-center py-12">
<Loader2 className="h-8 w-8 animate-spin text-[var(--purple-lavender)]" />
<Loader2 className="h-8 w-8 animate-spin text-brand-purple " />
</div>
) : (
<div className="border rounded-lg overflow-hidden">
@@ -651,7 +651,7 @@ export default function WordPressImportWizard({ open, onOpenChange, onSuccess })
{/* Pagination */}
{totalPages > 1 && (
<div className="flex items-center justify-between">
<p className="text-sm text-[var(--purple-lavender)]">
<p className="text-sm text-brand-purple ">
Page {currentPage} of {totalPages}
</p>
<div className="flex gap-2">
@@ -690,22 +690,22 @@ export default function WordPressImportWizard({ open, onOpenChange, onSuccess })
<div className="space-y-6">
<div>
<h3 className="text-lg font-semibold text-[var(--purple-ink)] mb-2">Import Preview</h3>
<p className="text-sm text-[var(--purple-lavender)]">
<p className="text-sm text-brand-purple ">
Review the final import settings before execution.
</p>
</div>
<div className="grid md:grid-cols-3 gap-4">
<Card className="p-6">
<p className="text-sm text-[var(--purple-lavender)]">Total Users</p>
<p className="text-sm text-brand-purple ">Total Users</p>
<p className="text-3xl font-semibold text-[var(--purple-ink)]">{analysisResult?.total_rows}</p>
</Card>
<Card className="p-6">
<p className="text-sm text-[var(--purple-lavender)]">Status Overrides</p>
<p className="text-sm text-brand-purple ">Status Overrides</p>
<p className="text-3xl font-semibold text-[var(--purple-ink)]">{overrideCount}</p>
</Card>
<Card className="p-6">
<p className="text-sm text-[var(--purple-lavender)]">Expected Imports</p>
<p className="text-sm text-brand-purple ">Expected Imports</p>
<p className="text-3xl font-semibold text-[var(--purple-ink)]">{analysisResult?.valid_rows}</p>
</Card>
</div>
@@ -715,15 +715,15 @@ export default function WordPressImportWizard({ open, onOpenChange, onSuccess })
<div className="space-y-3">
<div className="flex items-center gap-2">
<CheckCircle className="h-5 w-5 text-green-600" />
<span className="text-sm text-[var(--purple-lavender)]">Send password reset emails to all imported users</span>
<span className="text-sm text-brand-purple ">Send password reset emails to all imported users</span>
</div>
<div className="flex items-center gap-2">
<CheckCircle className="h-5 w-5 text-green-600" />
<span className="text-sm text-[var(--purple-lavender)]">Skip rows with errors and continue import</span>
<span className="text-sm text-brand-purple ">Skip rows with errors and continue import</span>
</div>
<div className="flex items-center gap-2">
<CheckCircle className="h-5 w-5 text-green-600" />
<span className="text-sm text-[var(--purple-lavender)]">Full rollback capability available after import</span>
<span className="text-sm text-brand-purple ">Full rollback capability available after import</span>
</div>
</div>
</Card>
@@ -751,7 +751,7 @@ export default function WordPressImportWizard({ open, onOpenChange, onSuccess })
<h3 className="text-lg font-semibold text-[var(--purple-ink)] mb-2">
{importing ? 'Import in Progress...' : 'Ready to Import'}
</h3>
<p className="text-sm text-[var(--purple-lavender)]">
<p className="text-sm text-brand-purple ">
{importing
? 'Please wait while users are imported. This may take a few minutes.'
: 'Click "Start Import" to begin importing users.'}
@@ -761,7 +761,7 @@ export default function WordPressImportWizard({ open, onOpenChange, onSuccess })
{importing && (
<div className="space-y-4">
<Progress value={importProgress} className="w-full" />
<p className="text-center text-sm text-[var(--purple-lavender)]">
<p className="text-center text-sm text-brand-purple ">
{importProgress.toFixed(1)}% complete
</p>
</div>
@@ -770,7 +770,7 @@ export default function WordPressImportWizard({ open, onOpenChange, onSuccess })
{!importing && !importResults && (
<Button
onClick={handleExecuteImport}
className="w-full bg-[var(--purple-lavender)] hover:bg-[var(--purple-ink)] py-6 text-lg"
className="w-full bg-brand-purple hover:bg-[var(--purple-ink)] py-6 text-lg"
>
<Play className="mr-2 h-5 w-5" />
Start Import
@@ -787,7 +787,7 @@ export default function WordPressImportWizard({ open, onOpenChange, onSuccess })
<div className="space-y-6">
<div>
<h3 className="text-lg font-semibold text-[var(--purple-ink)] mb-2">Import Complete</h3>
<p className="text-sm text-[var(--purple-lavender)]">
<p className="text-sm text-brand-purple ">
Review the import results and download error reports if needed.
</p>
</div>
@@ -854,7 +854,7 @@ export default function WordPressImportWizard({ open, onOpenChange, onSuccess })
Confirm Rollback
</DialogTitle>
</div>
<DialogDescription className="text-[var(--purple-lavender)]">
<DialogDescription className="text-brand-purple ">
This will permanently delete{' '}
<strong>{importResults?.successful_rows} users</strong> that were imported.
This action cannot be undone.
@@ -901,7 +901,7 @@ export default function WordPressImportWizard({ open, onOpenChange, onSuccess })
<DialogTitle className="text-2xl font-semibold text-[var(--purple-ink)]">
WordPress Import Wizard
</DialogTitle>
<DialogDescription className="text-[var(--purple-lavender)]">
<DialogDescription className="text-brand-purple ">
Import WordPress users with interactive status review and full rollback capability
</DialogDescription>
</DialogHeader>
@@ -919,7 +919,7 @@ export default function WordPressImportWizard({ open, onOpenChange, onSuccess })
<div
className={`
w-10 h-10 rounded-full flex items-center justify-center
${isCurrent ? 'bg-[var(--purple-lavender)] text-white' : ''}
${isCurrent ? 'bg-brand-purple text-white' : ''}
${isCompleted ? 'bg-green-600 text-white' : ''}
${!isCurrent && !isCompleted ? 'bg-gray-200 text-gray-600' : ''}
`}
@@ -962,7 +962,7 @@ export default function WordPressImportWizard({ open, onOpenChange, onSuccess })
<Button
onClick={handleNext}
disabled={!canProceed()}
className="bg-[var(--purple-lavender)] hover:bg-[var(--purple-ink)]"
className="bg-brand-purple hover:bg-[var(--purple-ink)]"
>
Next
<ChevronRight className="h-4 w-4 ml-2" />
@@ -975,7 +975,7 @@ export default function WordPressImportWizard({ open, onOpenChange, onSuccess })
onOpenChange(false);
if (onSuccess) onSuccess();
}}
className="bg-[var(--purple-lavender)] hover:bg-[var(--purple-ink)]"
className="bg-brand-purple hover:bg-[var(--purple-ink)]"
>
Close
</Button>

View File

@@ -40,7 +40,7 @@ const RegistrationStep1 = ({ formData, setFormData, handleInputChange }) => {
required
value={formData.first_name}
onChange={handleInputChange}
className="h-14 rounded-xl border-2 border-[var(--neutral-800)] focus:border-[var(--purple-lavender)]"
className="h-14 rounded-xl border-2 border-[var(--neutral-800)] focus:border-brand-purple "
data-testid="first-name-input"
/>
</div>
@@ -52,7 +52,7 @@ const RegistrationStep1 = ({ formData, setFormData, handleInputChange }) => {
required
value={formData.last_name}
onChange={handleInputChange}
className="h-14 rounded-xl border-2 border-[var(--neutral-800)] focus:border-[var(--purple-lavender)]"
className="h-14 rounded-xl border-2 border-[var(--neutral-800)] focus:border-brand-purple "
data-testid="last-name-input"
/>
</div>
@@ -69,7 +69,7 @@ const RegistrationStep1 = ({ formData, setFormData, handleInputChange }) => {
required
value={formData.phone}
onChange={handleInputChange}
className="h-14 rounded-xl border-2 border-[var(--neutral-800)] focus:border-[var(--purple-lavender)]"
className="h-14 rounded-xl border-2 border-[var(--neutral-800)] focus:border-brand-purple "
data-testid="phone-input"
/>
</div>
@@ -82,7 +82,7 @@ const RegistrationStep1 = ({ formData, setFormData, handleInputChange }) => {
required
value={formData.date_of_birth}
onChange={handleInputChange}
className="h-14 rounded-xl border-2 border-[var(--neutral-800)] focus:border-[var(--purple-lavender)]"
className="h-14 rounded-xl border-2 border-[var(--neutral-800)] focus:border-brand-purple "
data-testid="dob-input"
/>
</div>
@@ -112,7 +112,7 @@ const RegistrationStep1 = ({ formData, setFormData, handleInputChange }) => {
required
value={formData.city}
onChange={handleInputChange}
className="h-14 rounded-xl border-2 border-[var(--neutral-800)] focus:border-[var(--purple-lavender)]"
className="h-14 rounded-xl border-2 border-[var(--neutral-800)] focus:border-brand-purple "
data-testid="city-input"
/>
</div>
@@ -124,7 +124,7 @@ const RegistrationStep1 = ({ formData, setFormData, handleInputChange }) => {
required
value={formData.state}
onChange={handleInputChange}
className="h-14 rounded-xl border-2 border-[var(--neutral-800)] focus:border-[var(--purple-lavender)]"
className="h-14 rounded-xl border-2 border-[var(--neutral-800)] focus:border-brand-purple "
data-testid="state-input"
/>
</div>
@@ -136,7 +136,7 @@ const RegistrationStep1 = ({ formData, setFormData, handleInputChange }) => {
required
value={formData.zipcode}
onChange={handleInputChange}
className="h-14 rounded-xl border-2 border-[var(--neutral-800)] focus:border-[var(--purple-lavender)]"
className="h-14 rounded-xl border-2 border-[var(--neutral-800)] focus:border-brand-purple "
data-testid="zipcode-input"
/>
</div>
@@ -179,7 +179,7 @@ const RegistrationStep1 = ({ formData, setFormData, handleInputChange }) => {
name="partner_first_name"
value={formData.partner_first_name}
onChange={handleInputChange}
className="h-14 rounded-xl border-2 border-[var(--neutral-800)] focus:border-[var(--purple-lavender)]"
className="h-14 rounded-xl border-2 border-[var(--neutral-800)] focus:border-brand-purple "
data-testid="partner-first-name-input"
/>
</div>
@@ -190,7 +190,7 @@ const RegistrationStep1 = ({ formData, setFormData, handleInputChange }) => {
name="partner_last_name"
value={formData.partner_last_name}
onChange={handleInputChange}
className="h-14 rounded-xl border-2 border-[var(--neutral-800)] focus:border-[var(--purple-lavender)]"
className="h-14 rounded-xl border-2 border-[var(--neutral-800)] focus:border-brand-purple "
data-testid="partner-last-name-input"
/>
</div>

View File

@@ -36,7 +36,7 @@ const RegistrationStep2 = ({ formData, setFormData, handleInputChange }) => {
<h2 className="text-2xl font-semibold text-[var(--purple-ink)]" style={{ fontFamily: "'Inter', sans-serif" }}>
Newsletter Publication Preferences *
</h2>
<p className="text-[var(--purple-lavender)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p className="text-brand-purple " style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Please check what information may be published in LOAF Newsletter
</p>
@@ -110,10 +110,10 @@ const RegistrationStep2 = ({ formData, setFormData, handleInputChange }) => {
value={formData.referred_by_member_name}
onChange={handleInputChange}
placeholder="Enter member name or email"
className="h-14 rounded-xl border-2 border-[var(--neutral-800)] focus:border-[var(--purple-lavender)]"
className="h-14 rounded-xl border-2 border-[var(--neutral-800)] focus:border-brand-purple "
data-testid="referral-input"
/>
<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" }}>
If referred by a current member, you may skip the event attendance requirement.
</p>
</div>
@@ -124,7 +124,7 @@ const RegistrationStep2 = ({ formData, setFormData, handleInputChange }) => {
<h2 className="text-2xl font-semibold text-[var(--purple-ink)]" style={{ fontFamily: "'Inter', sans-serif" }}>
Volunteer Interests (Optional)
</h2>
<p className="text-[var(--purple-lavender)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p className="text-brand-purple " style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
I may at some time be interested in volunteering with LOAF in the following ways (training is provided)
</p>
@@ -158,7 +158,7 @@ const RegistrationStep2 = ({ formData, setFormData, handleInputChange }) => {
I am requesting for scholarship
</Label>
</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" }}>
Scholarship information is kept confidential
</p>
@@ -174,7 +174,7 @@ const RegistrationStep2 = ({ formData, setFormData, handleInputChange }) => {
onChange={handleInputChange}
placeholder="Tell us why you're requesting a scholarship..."
rows={4}
className="rounded-xl border-2 border-[var(--neutral-800)] focus:border-[var(--purple-lavender)]"
className="rounded-xl border-2 border-[var(--neutral-800)] focus:border-brand-purple "
/>
</div>
)}

View File

@@ -27,7 +27,7 @@ const RegistrationStep3 = ({ formData, setFormData, handleInputChange }) => {
Members Directory
</h2>
<p className="text-[var(--purple-lavender)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p className="text-brand-purple " style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Would you like to be displayed on our private members directory? (optional and you can change the answer later)
</p>
@@ -38,7 +38,7 @@ const RegistrationStep3 = ({ formData, setFormData, handleInputChange }) => {
p-4 rounded-xl border-2 cursor-pointer transition-all
${formData.show_in_directory
? 'border-[var(--orange-light)] bg-[var(--orange-light)]/5'
: 'border-[var(--neutral-800)] hover:border-[var(--purple-lavender)]'
: 'border-[var(--neutral-800)] hover:border-brand-purple '
}
`}
onClick={() => setFormData(prev => ({ ...prev, show_in_directory: true }))}
@@ -63,7 +63,7 @@ const RegistrationStep3 = ({ formData, setFormData, handleInputChange }) => {
p-4 rounded-xl border-2 cursor-pointer transition-all
${!formData.show_in_directory
? 'border-[var(--orange-light)] bg-[var(--orange-light)]/5'
: 'border-[var(--neutral-800)] hover:border-[var(--purple-lavender)]'
: 'border-[var(--neutral-800)] hover:border-brand-purple '
}
`}
onClick={() => setFormData(prev => ({ ...prev, show_in_directory: false }))}
@@ -88,7 +88,7 @@ const RegistrationStep3 = ({ formData, setFormData, handleInputChange }) => {
{/* Conditional Directory Fields */}
{formData.show_in_directory && (
<div className="space-y-4 mt-6 p-6 bg-background rounded-xl border border-[var(--neutral-800)]">
<p className="text-[var(--purple-lavender)] text-sm" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p className="text-brand-purple text-sm" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Below, choose what information you would like include in the Members Only Directory.
(If you ever want to update this information, remember the Directory Section and Account Section are separate)
</p>
@@ -101,7 +101,7 @@ const RegistrationStep3 = ({ formData, setFormData, handleInputChange }) => {
type="email"
value={formData.directory_email}
onChange={handleInputChange}
className="h-14 rounded-xl border-2 border-[var(--neutral-800)] focus:border-[var(--purple-lavender)]"
className="h-14 rounded-xl border-2 border-[var(--neutral-800)] focus:border-brand-purple "
/>
</div>
@@ -114,7 +114,7 @@ const RegistrationStep3 = ({ formData, setFormData, handleInputChange }) => {
onChange={handleInputChange}
placeholder="Tell other members about yourself..."
rows={4}
className="rounded-xl border-2 border-[var(--neutral-800)] focus:border-[var(--purple-lavender)]"
className="rounded-xl border-2 border-[var(--neutral-800)] focus:border-brand-purple "
/>
</div>
@@ -125,7 +125,7 @@ const RegistrationStep3 = ({ formData, setFormData, handleInputChange }) => {
name="directory_address"
value={formData.directory_address}
onChange={handleInputChange}
className="h-14 rounded-xl border-2 border-[var(--neutral-800)] focus:border-[var(--purple-lavender)]"
className="h-14 rounded-xl border-2 border-[var(--neutral-800)] focus:border-brand-purple "
/>
</div>
@@ -137,7 +137,7 @@ const RegistrationStep3 = ({ formData, setFormData, handleInputChange }) => {
type="tel"
value={formData.directory_phone}
onChange={handleInputChange}
className="h-14 rounded-xl border-2 border-[var(--neutral-800)] focus:border-[var(--purple-lavender)]"
className="h-14 rounded-xl border-2 border-[var(--neutral-800)] focus:border-brand-purple "
/>
</div>
@@ -149,7 +149,7 @@ const RegistrationStep3 = ({ formData, setFormData, handleInputChange }) => {
type="date"
value={formData.directory_dob}
onChange={handleInputChange}
className="h-14 rounded-xl border-2 border-[var(--neutral-800)] focus:border-[var(--purple-lavender)]"
className="h-14 rounded-xl border-2 border-[var(--neutral-800)] focus:border-brand-purple "
/>
</div>
@@ -162,7 +162,7 @@ const RegistrationStep3 = ({ formData, setFormData, handleInputChange }) => {
name="directory_partner_name"
value={formData.directory_partner_name}
onChange={handleInputChange}
className="h-14 rounded-xl border-2 border-[var(--neutral-800)] focus:border-[var(--purple-lavender)]"
className="h-14 rounded-xl border-2 border-[var(--neutral-800)] focus:border-brand-purple "
/>
</div>
</div>

View File

@@ -11,7 +11,7 @@ const RegistrationStep4 = ({ formData, handleInputChange }) => {
Account Credentials
</h2>
<p className="text-[var(--purple-lavender)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p className="text-brand-purple " style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Your email is also your username that you can use to login.
Please note you can only login after your application is validated.
</p>
@@ -28,7 +28,7 @@ const RegistrationStep4 = ({ formData, handleInputChange }) => {
value={formData.email}
onChange={handleInputChange}
placeholder="your.email@example.com"
className="h-14 rounded-xl border-2 border-[var(--neutral-800)] focus:border-[var(--purple-lavender)]"
className="h-14 rounded-xl border-2 border-[var(--neutral-800)] focus:border-brand-purple "
data-testid="email-input"
/>
</div>
@@ -43,10 +43,10 @@ const RegistrationStep4 = ({ formData, handleInputChange }) => {
value={formData.password}
onChange={handleInputChange}
placeholder="At least 6 characters"
className="h-14 rounded-xl border-2 border-[var(--neutral-800)] focus:border-[var(--purple-lavender)]"
className="h-14 rounded-xl border-2 border-[var(--neutral-800)] focus:border-brand-purple "
data-testid="password-input"
/>
<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" }}>
Must be at least 6 characters long
</p>
</div>
@@ -60,7 +60,7 @@ const RegistrationStep4 = ({ formData, handleInputChange }) => {
value={formData.confirmPassword}
onChange={handleInputChange}
placeholder="Re-enter your password"
className="h-14 rounded-xl border-2 border-[var(--neutral-800)] focus:border-[var(--purple-lavender)]"
className="h-14 rounded-xl border-2 border-[var(--neutral-800)] focus:border-brand-purple "
data-testid="confirm-password-input"
/>
{formData.confirmPassword && formData.password !== formData.confirmPassword && (
@@ -79,7 +79,7 @@ const RegistrationStep4 = ({ formData, handleInputChange }) => {
name="accepts_tos"
checked={formData.accepts_tos || false}
onChange={handleInputChange}
className="mt-1 w-4 h-4 text-[var(--purple-lavender)] border-gray-300 rounded focus:ring-[var(--purple-lavender)]"
className="mt-1 w-4 h-4 text-brand-purple border-gray-300 rounded focus:ring-brand-purple "
required
data-testid="tos-checkbox"
/>
@@ -89,7 +89,7 @@ const RegistrationStep4 = ({ formData, handleInputChange }) => {
href="/become-a-member/terms-of-service"
target="_blank"
rel="noopener noreferrer"
className="text-[var(--purple-lavender)] hover:text-[var(--purple-ink)] font-semibold underline"
className="text-brand-purple hover:text-[var(--purple-ink)] font-semibold underline"
>
Terms of Service
</a>
@@ -98,7 +98,7 @@ const RegistrationStep4 = ({ formData, handleInputChange }) => {
href="become-a-member/privacy-policy"
target="_blank"
rel="noopener noreferrer"
className="text-[var(--purple-lavender)] hover:text-[var(--purple-ink)] font-semibold underline"
className="text-brand-purple hover:text-[var(--purple-ink)] font-semibold underline"
>
Privacy Policy
</a>

View File

@@ -23,14 +23,14 @@ const RegistrationStepIndicator = ({ currentStep, totalSteps = 4 }) => {
? 'bg-[var(--orange-light)] text-white scale-110 shadow-lg'
: currentStep > step.number
? 'bg-[var(--green-light)] text-white'
: 'bg-[var(--neutral-800)] text-[var(--purple-lavender)]'
: 'bg-[var(--neutral-800)] text-brand-purple '
}
`}>
{currentStep > step.number ? '✓' : step.number}
</div>
<span className={`
text-sm mt-2 font-medium transition-colors
${currentStep === step.number ? 'text-[var(--orange-light)]' : 'text-[var(--purple-lavender)]'}
${currentStep === step.number ? 'text-[var(--orange-light)]' : 'text-brand-purple '}
`} style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{step.title}
</span>
@@ -52,7 +52,7 @@ const RegistrationStepIndicator = ({ currentStep, totalSteps = 4 }) => {
</div>
{/* Step Counter */}
<p className="text-center text-[var(--purple-lavender)] mt-6 text-lg" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p className="text-center text-brand-purple mt-6 text-lg" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Step <span className="font-semibold text-[var(--orange-light)]">{currentStep}</span> of {totalSteps}
</p>
</div>

View File

@@ -1,47 +1,42 @@
import * as React from "react"
import { Slot } from "@radix-ui/react-slot"
import { cva } from "class-variance-authority";
import { cva } from "class-variance-authority"
import { cn } from "@/lib/utils"
const buttonVariants = cva(
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
{
variants: {
variant: {
default:
"bg-primary text-primary-foreground shadow hover:bg-primary/90",
destructive:
"bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90",
outline:
"border border-input shadow-sm hover:bg-accent hover:text-accent-foreground",
secondary:
"bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80",
ghost: "hover:bg-accent hover:text-accent-foreground",
link: "text-primary underline-offset-4 hover:underline",
},
size: {
default: "h-9 px-4 py-2",
sm: "h-8 rounded-md px-3 text-xs",
lg: "h-10 rounded-md px-8",
icon: "h-9 w-9",
},
const buttonVariants = cva("btn", {
variants: {
variant: {
default: "btn-primary",
secondary: "btn-secondary",
ghost: "btn-ghost",
outline: "btn-outline",
accent: "btn-accent",
destructive: "btn-destructive",
link: "btn-link",
},
defaultVariants: {
variant: "default",
size: "default",
size: {
default: "btn-md",
sm: "btn-sm",
lg: "btn-lg",
icon: "btn-icon",
},
}
)
},
defaultVariants: {
variant: "default",
size: "default",
},
})
const Button = React.forwardRef(({ className, variant, size, asChild = false, ...props }, ref) => {
const Comp = asChild ? Slot : "button"
return (
<Comp
className={cn(buttonVariants({ variant, size, className }))}
className={cn(buttonVariants({ variant, size }), className)}
ref={ref}
{...props} />
);
{...props}
/>
)
})
Button.displayName = "Button"

View File

@@ -21,7 +21,7 @@ const TabsTrigger = React.forwardRef(({ className, ...props }, ref) => (
<TabsPrimitive.Trigger
ref={ref}
className={cn(
"inline-flex items-center justify-center whitespace-nowrap hover:bg-[var(--lavender-300)] border-2 border-[var(--purple-lavender)] rounded-2xl px-3 py-1 text-[var(--purple-lavender)] text-sm font-medium ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-foreground data-[state=active]:text-background data-[state=active]:border-foreground data-[state=active]:shadow",
"inline-flex items-center justify-center whitespace-nowrap hover:bg-[var(--lavender-300)] border-2 border-brand-purple rounded-2xl px-3 py-1 text-brand-purple text-sm font-medium ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-foreground data-[state=active]:text-background data-[state=active]:border-foreground data-[state=active]:shadow",
className
)}
{...props}

View File

@@ -163,7 +163,7 @@ const AcceptInvitation = () => {
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' },
member: { label: 'Member', className: 'bg-[var(--neutral-800)] text-[var(--purple-ink)]' }
};
@@ -181,7 +181,7 @@ const AcceptInvitation = () => {
return (
<div className="min-h-screen bg-gradient-to-br from-[var(--lavender-700)] to-white flex items-center justify-center p-4">
<Card className="w-full max-w-md p-12 bg-background rounded-2xl border border-[var(--neutral-800)] text-center">
<Loader2 className="h-12 w-12 text-[var(--purple-lavender)] mx-auto mb-4 animate-spin" />
<Loader2 className="h-12 w-12 text-brand-purple mx-auto mb-4 animate-spin" />
<p className="text-lg text-[var(--purple-ink)]" style={{ fontFamily: "'Inter', sans-serif" }}>
Verifying your invitation...
</p>
@@ -198,12 +198,12 @@ const AcceptInvitation = () => {
<h1 className="text-2xl font-semibold text-[var(--purple-ink)] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}>
Invalid Invitation
</h1>
<p className="text-[var(--purple-lavender)] mb-8" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p className="text-brand-purple mb-8" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{error}
</p>
<Button
onClick={() => navigate('/login')}
className="rounded-xl bg-[var(--purple-lavender)] hover:bg-[var(--purple-ink)] text-white"
className="rounded-xl bg-brand-purple hover:bg-[var(--purple-ink)] text-white"
>
Go to Login
</Button>
@@ -229,7 +229,7 @@ const AcceptInvitation = () => {
<h1 className="text-4xl font-semibold text-[var(--purple-ink)] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}>
Welcome to LOAF! 🎉
</h1>
<p className="text-xl text-[var(--purple-lavender)] mb-8" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p className="text-xl text-brand-purple mb-8" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Your account has been created successfully.
</p>
@@ -237,7 +237,7 @@ const AcceptInvitation = () => {
<div className="mb-8 p-6 bg-gradient-to-r from-[var(--neutral-800)] to-[var(--lavender-700)] rounded-xl">
<div className="grid md:grid-cols-2 gap-4 text-left">
<div>
<p className="text-sm text-[var(--purple-lavender)] mb-1" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p className="text-sm text-brand-purple mb-1" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Name
</p>
<p className="font-semibold text-[var(--purple-ink)]" style={{ fontFamily: "'Inter', sans-serif" }}>
@@ -245,7 +245,7 @@ const AcceptInvitation = () => {
</p>
</div>
<div>
<p className="text-sm text-[var(--purple-lavender)] mb-1" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p className="text-sm text-brand-purple mb-1" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Email
</p>
<p className="font-semibold text-[var(--purple-ink)]" style={{ fontFamily: "'Inter', sans-serif" }}>
@@ -253,13 +253,13 @@ const AcceptInvitation = () => {
</p>
</div>
<div>
<p className="text-sm text-[var(--purple-lavender)] mb-1" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p className="text-sm text-brand-purple mb-1" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Role
</p>
<div>{getRoleBadge(successUser?.role)}</div>
</div>
<div>
<p className="text-sm text-[var(--purple-lavender)] mb-1" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p className="text-sm text-brand-purple mb-1" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Status
</p>
<Badge className="bg-[var(--green-light)] text-white px-4 py-2 rounded-full text-sm">
@@ -295,14 +295,14 @@ const AcceptInvitation = () => {
{/* Header */}
<div className="text-center mb-8">
<div className="flex justify-center mb-4">
<div className="h-16 w-16 rounded-full bg-gradient-to-br from-[var(--purple-lavender)] to-[var(--purple-ink)] flex items-center justify-center">
<div className="h-16 w-16 rounded-full bg-gradient-to-br from-brand-purple to-[var(--purple-ink)] flex items-center justify-center">
<Mail className="h-8 w-8 text-white" />
</div>
</div>
<h1 className="text-3xl md:text-4xl font-semibold text-[var(--purple-ink)] mb-3" style={{ fontFamily: "'Inter', sans-serif" }}>
Welcome to LOAF!
</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" }}>
Complete your profile to accept the invitation
</p>
</div>
@@ -311,7 +311,7 @@ const AcceptInvitation = () => {
<div className="mb-8 p-6 bg-gradient-to-r from-[var(--neutral-800)] to-[var(--lavender-700)] rounded-xl">
<div className="grid md:grid-cols-2 gap-4 text-sm">
<div>
<p className="text-[var(--purple-lavender)] mb-1" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p className="text-brand-purple mb-1" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Email Address
</p>
<p className="font-semibold text-[var(--purple-ink)]" style={{ fontFamily: "'Inter', sans-serif" }}>
@@ -319,13 +319,13 @@ const AcceptInvitation = () => {
</p>
</div>
<div>
<p className="text-[var(--purple-lavender)] mb-1" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p className="text-brand-purple mb-1" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Role
</p>
<div>{getRoleBadge(invitation?.role)}</div>
</div>
<div className="md:col-span-2">
<p className="text-[var(--purple-lavender)] mb-1 flex items-center gap-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p className="text-brand-purple mb-1 flex items-center gap-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<Calendar className="h-4 w-4" />
Invitation Expires
</p>
@@ -350,7 +350,7 @@ const AcceptInvitation = () => {
type="password"
value={formData.password}
onChange={(e) => handleChange('password', e.target.value)}
className="rounded-xl border-2 border-[var(--neutral-800)] focus:border-[var(--purple-lavender)]"
className="rounded-xl border-2 border-[var(--neutral-800)] focus:border-brand-purple "
placeholder="Minimum 8 characters"
/>
{formErrors.password && (
@@ -367,7 +367,7 @@ const AcceptInvitation = () => {
type="password"
value={formData.confirmPassword}
onChange={(e) => handleChange('confirmPassword', e.target.value)}
className="rounded-xl border-2 border-[var(--neutral-800)] focus:border-[var(--purple-lavender)]"
className="rounded-xl border-2 border-[var(--neutral-800)] focus:border-brand-purple "
placeholder="Re-enter password"
/>
{formErrors.confirmPassword && (
@@ -386,7 +386,7 @@ const AcceptInvitation = () => {
id="first_name"
value={formData.first_name}
onChange={(e) => handleChange('first_name', e.target.value)}
className="rounded-xl border-2 border-[var(--neutral-800)] focus:border-[var(--purple-lavender)]"
className="rounded-xl border-2 border-[var(--neutral-800)] focus:border-brand-purple "
placeholder="John"
/>
{formErrors.first_name && (
@@ -402,7 +402,7 @@ const AcceptInvitation = () => {
id="last_name"
value={formData.last_name}
onChange={(e) => handleChange('last_name', e.target.value)}
className="rounded-xl border-2 border-[var(--neutral-800)] focus:border-[var(--purple-lavender)]"
className="rounded-xl border-2 border-[var(--neutral-800)] focus:border-brand-purple "
placeholder="Doe"
/>
{formErrors.last_name && (
@@ -421,7 +421,7 @@ const AcceptInvitation = () => {
type="tel"
value={formData.phone}
onChange={(e) => handleChange('phone', e.target.value)}
className="rounded-xl border-2 border-[var(--neutral-800)] focus:border-[var(--purple-lavender)]"
className="rounded-xl border-2 border-[var(--neutral-800)] focus:border-brand-purple "
placeholder="(555) 123-4567"
/>
{formErrors.phone && (
@@ -445,7 +445,7 @@ const AcceptInvitation = () => {
id="address"
value={formData.address}
onChange={(e) => handleChange('address', e.target.value)}
className="rounded-xl border-2 border-[var(--neutral-800)] focus:border-[var(--purple-lavender)]"
className="rounded-xl border-2 border-[var(--neutral-800)] focus:border-brand-purple "
placeholder="123 Main St"
/>
</div>
@@ -458,7 +458,7 @@ const AcceptInvitation = () => {
id="city"
value={formData.city}
onChange={(e) => handleChange('city', e.target.value)}
className="rounded-xl border-2 border-[var(--neutral-800)] focus:border-[var(--purple-lavender)]"
className="rounded-xl border-2 border-[var(--neutral-800)] focus:border-brand-purple "
placeholder="San Francisco"
/>
</div>
@@ -469,7 +469,7 @@ const AcceptInvitation = () => {
id="state"
value={formData.state}
onChange={(e) => handleChange('state', e.target.value)}
className="rounded-xl border-2 border-[var(--neutral-800)] focus:border-[var(--purple-lavender)]"
className="rounded-xl border-2 border-[var(--neutral-800)] focus:border-brand-purple "
placeholder="CA"
maxLength={2}
/>
@@ -481,7 +481,7 @@ const AcceptInvitation = () => {
id="zipcode"
value={formData.zipcode}
onChange={(e) => handleChange('zipcode', e.target.value)}
className="rounded-xl border-2 border-[var(--neutral-800)] focus:border-[var(--purple-lavender)]"
className="rounded-xl border-2 border-[var(--neutral-800)] focus:border-brand-purple "
placeholder="94102"
/>
</div>
@@ -495,7 +495,7 @@ const AcceptInvitation = () => {
type="date"
value={formData.date_of_birth}
onChange={(e) => handleChange('date_of_birth', e.target.value)}
className="rounded-xl border-2 border-[var(--neutral-800)] focus:border-[var(--purple-lavender)]"
className="rounded-xl border-2 border-[var(--neutral-800)] focus:border-brand-purple "
/>
</div>
</>
@@ -526,11 +526,11 @@ const AcceptInvitation = () => {
{/* Footer Note */}
<div className="mt-6 text-center">
<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" }}>
Already have an account?{' '}
<button
onClick={() => navigate('/login')}
className="text-[var(--purple-lavender)] hover:text-[var(--purple-ink)] font-semibold underline"
className="text-brand-purple hover:text-[var(--purple-ink)] font-semibold underline"
>
Sign in instead
</button>

View File

@@ -95,7 +95,7 @@ const ChangePasswordRequired = () => {
<h1 className="text-4xl md:text-5xl font-semibold text-[var(--purple-ink)] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}>
Password Change Required
</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" }}>
Your password was reset by an administrator. Please create a new password to continue.
</p>
</div>
@@ -111,7 +111,7 @@ const ChangePasswordRequired = () => {
value={formData.currentPassword}
onChange={handleInputChange}
placeholder="Enter temporary password"
className="h-14 rounded-xl border-2 border-[var(--neutral-800)] focus:border-[var(--purple-lavender)]"
className="h-14 rounded-xl border-2 border-[var(--neutral-800)] focus:border-brand-purple "
/>
</div>
@@ -125,7 +125,7 @@ const ChangePasswordRequired = () => {
value={formData.newPassword}
onChange={handleInputChange}
placeholder="Enter new password (min. 6 characters)"
className="h-14 rounded-xl border-2 border-[var(--neutral-800)] focus:border-[var(--purple-lavender)]"
className="h-14 rounded-xl border-2 border-[var(--neutral-800)] focus:border-brand-purple "
/>
</div>
@@ -139,14 +139,14 @@ const ChangePasswordRequired = () => {
value={formData.confirmPassword}
onChange={handleInputChange}
placeholder="Re-enter new password"
className="h-14 rounded-xl border-2 border-[var(--neutral-800)] focus:border-[var(--purple-lavender)]"
className="h-14 rounded-xl border-2 border-[var(--neutral-800)] focus:border-brand-purple "
/>
</div>
<div className="bg-[var(--lavender-300)] border-l-4 border-[var(--purple-lavender)] p-4 rounded-lg">
<div className="bg-[var(--lavender-300)] border-l-4 border-brand-purple p-4 rounded-lg">
<div className="flex items-start">
<Lock className="h-5 w-5 text-[var(--purple-lavender)] mr-2 mt-0.5 flex-shrink-0" />
<div className="text-sm text-[var(--purple-lavender)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<Lock className="h-5 w-5 text-brand-purple mr-2 mt-0.5 flex-shrink-0" />
<div className="text-sm text-brand-purple " style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p className="font-medium text-[var(--purple-ink)] mb-1">Password Requirements:</p>
<ul className="list-disc list-inside space-y-1">
<li>At least 6 characters long</li>
@@ -169,7 +169,7 @@ const ChangePasswordRequired = () => {
<button
type="button"
onClick={handleLogout}
className="text-[var(--purple-lavender)] hover:text-[var(--orange-light)] text-sm underline"
className="text-brand-purple hover:text-[var(--orange-light)] text-sm underline"
>
Logout instead
</button>

View File

@@ -120,21 +120,21 @@ const Dashboard = () => {
<h1 className="text-4xl md:text-5xl font-semibold text-[var(--purple-ink)] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}>
Welcome Back, {user?.first_name}!
</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" }}>
Here's an overview of your membership status and upcoming events.
</p>
</div>
{/* Email Verification Alert */}
{user && !user.email_verified && (
<Card className="p-6 bg-[var(--lavender-300)] border-2 border-[var(--purple-lavender)] mb-8">
<Card className="p-6 bg-[var(--lavender-300)] border-2 border-brand-purple mb-8">
<div className="flex items-start gap-4">
<AlertCircle className="h-6 w-6 text-[var(--purple-lavender)] flex-shrink-0 mt-1" />
<AlertCircle className="h-6 w-6 text-brand-purple flex-shrink-0 mt-1" />
<div className="flex-1">
<h3 className="text-lg font-semibold text-[var(--purple-ink)] mb-2" style={{ fontFamily: "'Inter', sans-serif" }}>
Verify Your Email Address
</h3>
<p className="text-[var(--purple-lavender)] mb-4" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p className="text-brand-purple mb-4" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Please verify your email address to complete your registration.
Check your inbox for the verification link.
</p>
@@ -142,7 +142,7 @@ const Dashboard = () => {
onClick={handleResendVerification}
disabled={resendLoading}
variant="outline"
className="border-2 border-[var(--purple-lavender)] text-[var(--purple-lavender)] hover:bg-[var(--purple-lavender)] hover:text-white"
className="border-2 border-brand-purple text-brand-purple hover:bg-brand-purple hover:text-white"
>
<Mail className="h-4 w-4 mr-2" />
{resendLoading ? 'Sending...' : 'Resend Verification Email'}
@@ -162,7 +162,7 @@ const Dashboard = () => {
<div className="mb-4">
{getStatusBadge(user?.status)}
</div>
<p className="text-[var(--purple-lavender)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p className="text-brand-purple " style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{getStatusMessage(user?.status)}
</p>
</div>
@@ -187,15 +187,15 @@ const Dashboard = () => {
</h3>
<div className="space-y-4">
<div>
<p className="text-sm text-[var(--purple-lavender)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Email</p>
<p className="text-sm text-brand-purple " style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Email</p>
<p className="text-[var(--purple-ink)] font-medium" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>{user?.email}</p>
</div>
<div>
<p className="text-sm text-[var(--purple-lavender)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Role</p>
<p className="text-sm text-brand-purple " style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Role</p>
<p className="text-[var(--purple-ink)] font-medium capitalize" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>{user?.role}</p>
</div>
<div>
<p className="text-sm text-[var(--purple-lavender)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Member Since</p>
<p className="text-sm text-brand-purple " style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Member Since</p>
<p className="text-[var(--purple-ink)] font-medium" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{user?.created_at ? new Date(user.created_at).toLocaleDateString() : 'N/A'}
</p>
@@ -203,13 +203,13 @@ const Dashboard = () => {
{user?.subscription_start_date && user?.subscription_end_date && (
<>
<div className="pt-4 border-t border-[var(--neutral-800)]">
<p className="text-sm text-[var(--purple-lavender)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Membership Period</p>
<p className="text-sm text-brand-purple " style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Membership Period</p>
<p className="text-[var(--purple-ink)] font-medium" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{new Date(user.subscription_start_date).toLocaleDateString()} - {new Date(user.subscription_end_date).toLocaleDateString()}
</p>
</div>
<div>
<p className="text-sm text-[var(--purple-lavender)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Days Remaining</p>
<p className="text-sm text-brand-purple " style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Days Remaining</p>
<p className="text-[var(--purple-ink)] font-medium" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{Math.max(0, Math.ceil((new Date(user.subscription_end_date) - new Date()) / (1000 * 60 * 60 * 24)))} days
</p>
@@ -228,7 +228,7 @@ const Dashboard = () => {
<Link to="/events">
<Button
variant="ghost"
className="text-[var(--orange-light)] hover:text-[var(--purple-lavender)]"
className="text-[var(--orange-light)] hover:text-brand-purple "
data-testid="view-all-events-button"
>
View All
@@ -237,26 +237,26 @@ const Dashboard = () => {
</div>
{loading ? (
<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>
) : events.length > 0 ? (
<div className="space-y-4">
{events.map((event) => (
<Link to={`/events/${event.id}`} key={event.id}>
<div
className="p-4 border border-[var(--neutral-800)] rounded-xl hover:border-[var(--purple-lavender)] hover:shadow-md transition-all cursor-pointer"
className="p-4 border border-[var(--neutral-800)] rounded-xl hover:border-brand-purple hover:shadow-md transition-all cursor-pointer"
data-testid={`event-card-${event.id}`}
>
<div className="flex items-start gap-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>
<div className="flex-1">
<h4 className="font-semibold text-[var(--purple-ink)] mb-1" style={{ fontFamily: "'Inter', sans-serif" }}>{event.title}</h4>
<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" }}>
{new Date(event.start_at).toLocaleDateString()} at{' '}
{new Date(event.start_at).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}
</p>
<p className="text-sm text-[var(--purple-lavender)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>{event.location}</p>
<p className="text-sm text-brand-purple " style={{ fontFamily: "'Nunito Sans', sans-serif" }}>{event.location}</p>
</div>
</div>
</div>
@@ -266,8 +266,8 @@ const Dashboard = () => {
) : (
<div className="text-center py-12">
<Calendar className="h-16 w-16 text-[var(--neutral-800)] mx-auto mb-4" />
<p className="text-[var(--purple-lavender)] mb-4" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>No upcoming events at the moment.</p>
<p className="text-sm text-[var(--purple-lavender)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Check back later for new events!</p>
<p className="text-brand-purple mb-4" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>No upcoming events at the moment.</p>
<p className="text-sm text-brand-purple " style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Check back later for new events!</p>
</div>
)}
</Card>
@@ -280,7 +280,7 @@ const Dashboard = () => {
<h3 className="text-2xl font-semibold text-[var(--purple-ink)] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}>
Application Under Review
</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" }}>
Your membership application is being reviewed by our admin team. You'll be notified once validated!
</p>
</div>
@@ -289,15 +289,15 @@ const Dashboard = () => {
{/* Payment Prompt for payment_pending status */}
{user?.status === 'payment_pending' && (
<Card className="mt-8 p-8 bg-gradient-to-br from-[var(--neutral-800)]/20 to-[var(--lavender-300)]/20 rounded-2xl border-2 border-[var(--purple-lavender)]">
<Card className="mt-8 p-8 bg-gradient-to-br from-[var(--neutral-800)]/20 to-[var(--lavender-300)]/20 rounded-2xl border-2 border-brand-purple ">
<div className="text-center">
<div className="mb-4">
<AlertCircle className="h-16 w-16 text-[var(--purple-lavender)] mx-auto" />
<AlertCircle className="h-16 w-16 text-brand-purple mx-auto" />
</div>
<h3 className="text-2xl font-semibold text-[var(--purple-ink)] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}>
Complete Your Payment
</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" }}>
Great news! Your membership application has been validated. Complete your payment to activate your membership and gain full access to all member benefits.
</p>
<Link to="/plans">
@@ -322,7 +322,7 @@ const Dashboard = () => {
</div>
{activityLoading ? (
<p className="text-[var(--purple-lavender)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Loading event activity...</p>
<p className="text-brand-purple " style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Loading event activity...</p>
) : eventActivity ? (
<div className="space-y-8">
{/* Stats Cards */}
@@ -330,10 +330,10 @@ const Dashboard = () => {
<Card className="p-6 bg-background rounded-2xl border border-[var(--neutral-800)]">
<div className="flex items-center gap-4">
<div className="bg-[var(--neutral-800)]/20 p-4 rounded-lg">
<Calendar className="h-8 w-8 text-[var(--purple-lavender)]" />
<Calendar className="h-8 w-8 text-brand-purple " />
</div>
<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-3xl font-semibold text-[var(--purple-ink)]" style={{ fontFamily: "'Inter', sans-serif" }}>
{eventActivity.total_rsvps}
</p>
@@ -346,7 +346,7 @@ const Dashboard = () => {
<CheckCircle className="h-8 w-8 text-[var(--green-light)]" />
</div>
<div>
<p className="text-sm text-[var(--purple-lavender)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Events Attended</p>
<p className="text-sm text-brand-purple " style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Events Attended</p>
<p className="text-3xl font-semibold text-[var(--purple-ink)]" style={{ fontFamily: "'Inter', sans-serif" }}>
{eventActivity.total_attended}
</p>
@@ -364,15 +364,15 @@ const Dashboard = () => {
<div className="space-y-3">
{eventActivity.upcoming_events.map((event) => (
<Link to={`/events/${event.id}`} key={event.id}>
<div className="p-4 border border-[var(--neutral-800)] rounded-xl hover:border-[var(--purple-lavender)] hover:shadow-md transition-all">
<div className="p-4 border border-[var(--neutral-800)] rounded-xl hover:border-brand-purple hover:shadow-md transition-all">
<div className="flex items-start justify-between gap-4">
<div className="flex-1">
<h4 className="font-semibold text-[var(--purple-ink)] mb-1" style={{ fontFamily: "'Inter', sans-serif" }}>{event.title}</h4>
<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" }}>
{new Date(event.start_at).toLocaleDateString()} at{' '}
{new Date(event.start_at).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}
</p>
<p className="text-sm text-[var(--purple-lavender)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>{event.location}</p>
<p className="text-sm text-brand-purple " style={{ fontFamily: "'Nunito Sans', sans-serif" }}>{event.location}</p>
</div>
<Badge className={
event.rsvp_status === 'yes' ? 'bg-[var(--green-light)] text-white' :
@@ -402,7 +402,7 @@ const Dashboard = () => {
<div className="flex items-start justify-between gap-4">
<div className="flex-1">
<h4 className="font-semibold text-[var(--purple-ink)] mb-1" style={{ fontFamily: "'Inter', sans-serif" }}>{event.title}</h4>
<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" }}>
{new Date(event.start_at).toLocaleDateString()} at{' '}
{new Date(event.start_at).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}
</p>
@@ -412,7 +412,7 @@ const Dashboard = () => {
{event.attended ? 'Attended' : 'Did not attend'}
</Badge>
{event.attended && event.attended_at && (
<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" }}>
Checked in: {new Date(event.attended_at).toLocaleDateString()}
</p>
)}
@@ -422,7 +422,7 @@ const Dashboard = () => {
))}
</div>
{eventActivity.past_events.length > 5 && (
<p className="text-sm text-center text-[var(--purple-lavender)] mt-4" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p className="text-sm text-center text-brand-purple mt-4" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Showing 5 of {eventActivity.past_events.length} past events
</p>
)}
@@ -438,7 +438,7 @@ const Dashboard = () => {
<h3 className="text-xl font-semibold text-[var(--purple-ink)] mb-2" style={{ fontFamily: "'Inter', sans-serif" }}>
No Event Activity 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" }}>
Browse upcoming events and RSVP to start building your event history!
</p>
<Link to="/events">
@@ -455,7 +455,7 @@ const Dashboard = () => {
<Card className="p-12 bg-background rounded-2xl border border-[var(--neutral-800)]">
<div className="text-center">
<AlertCircle className="h-16 w-16 text-[var(--neutral-800)] mx-auto mb-4" />
<p className="text-[var(--purple-lavender)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p className="text-brand-purple " style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Failed to load event activity. Please try refreshing the page.
</p>
</div>

View File

@@ -37,7 +37,7 @@ const DonationSuccess = () => {
{/* Message */}
<div className="space-y-4 mb-8">
<p className="text-xl text-[var(--purple-lavender)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p className="text-xl text-brand-purple " style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Your generous contribution helps support our community and continue our mission.
</p>
@@ -48,12 +48,12 @@ const DonationSuccess = () => {
Your Support Makes a Difference
</span>
</div>
<p className="text-[var(--purple-lavender)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p className="text-brand-purple " style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
A receipt for your donation has been sent to your email address.
</p>
</div>
<p className="text-base text-[var(--purple-lavender)] pt-4" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p className="text-base text-brand-purple pt-4" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
We deeply appreciate your support and commitment to LOAF's mission of building a vibrant, inclusive community.
</p>
</div>
@@ -62,7 +62,7 @@ const DonationSuccess = () => {
<div className="flex flex-col sm:flex-row gap-4 justify-center">
<Button
onClick={() => navigate('/')}
className="bg-[var(--purple-lavender)] text-white hover:bg-[var(--purple-ink)] rounded-full px-8 py-6 text-lg font-medium shadow-lg"
className="bg-brand-purple text-white hover:bg-[var(--purple-ink)] rounded-full px-8 py-6 text-lg font-medium shadow-lg"
style={{ fontFamily: "'Inter', sans-serif" }}
>
Return to Home
@@ -70,7 +70,7 @@ const DonationSuccess = () => {
<Button
onClick={() => navigate('/donate')}
variant="outline"
className="border-2 border-[var(--purple-lavender)] text-[var(--purple-lavender)] hover:bg-[var(--neutral-800)]/20 rounded-full px-8 py-6 text-lg font-medium"
className="border-2 border-brand-purple text-brand-purple hover:bg-[var(--neutral-800)]/20 rounded-full px-8 py-6 text-lg font-medium"
style={{ fontFamily: "'Inter', sans-serif" }}
>
Make Another Donation
@@ -80,12 +80,12 @@ const DonationSuccess = () => {
{/* Additional Info */}
<div className="mt-12 text-center">
<p className="text-sm text-[var(--purple-lavender)] mb-4" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p className="text-sm text-brand-purple mb-4" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Have questions about your donation?
</p>
<a
href="mailto:support@loaf.org"
className="text-[var(--orange-light)] hover:text-[var(--purple-lavender)] font-medium transition-colors"
className="text-[var(--orange-light)] hover:text-brand-purple font-medium transition-colors"
style={{ fontFamily: "'Inter', sans-serif" }}
>
Contact us at support@loaf.org

View File

@@ -51,7 +51,7 @@ const EventDetails = () => {
<div className="min-h-screen bg-background">
<Navbar />
<div className="flex items-center justify-center min-h-[60vh]">
<p className="text-[var(--purple-lavender)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Loading event...</p>
<p className="text-brand-purple " style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Loading event...</p>
</div>
</div>
);
@@ -68,7 +68,7 @@ const EventDetails = () => {
<div className="max-w-4xl mx-auto px-6 py-12">
<button
onClick={() => navigate('/events')}
className="inline-flex items-center text-[var(--purple-lavender)] hover:text-[var(--orange-light)] transition-colors mb-8"
className="inline-flex items-center text-brand-purple hover:text-[var(--orange-light)] transition-colors mb-8"
data-testid="back-to-events-button"
>
<ArrowLeft className="h-4 w-4 mr-2" />
@@ -79,7 +79,7 @@ const EventDetails = () => {
<div className="mb-8">
<div className="flex items-center gap-4 mb-6">
<div className="bg-[var(--neutral-800)]/20 p-4 rounded-xl">
<Calendar className="h-10 w-10 text-[var(--purple-lavender)]" />
<Calendar className="h-10 w-10 text-brand-purple " />
</div>
{event.user_rsvp_status && (
<Badge
@@ -102,7 +102,7 @@ const EventDetails = () => {
</h1>
<div className="space-y-4 text-lg">
<div className="flex items-center gap-3 text-[var(--purple-lavender)]">
<div className="flex items-center gap-3 text-brand-purple ">
<Calendar className="h-5 w-5" />
<span style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{new Date(event.start_at).toLocaleDateString('en-US', {
@@ -113,18 +113,18 @@ const EventDetails = () => {
})}
</span>
</div>
<div className="flex items-center gap-3 text-[var(--purple-lavender)]">
<div className="flex items-center gap-3 text-brand-purple ">
<Calendar className="h-5 w-5" />
<span style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{new Date(event.start_at).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })} -{' '}
{new Date(event.end_at).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}
</span>
</div>
<div className="flex items-center gap-3 text-[var(--purple-lavender)]">
<div className="flex items-center gap-3 text-brand-purple ">
<MapPin className="h-5 w-5" />
<span style={{ fontFamily: "'Nunito Sans', sans-serif" }}>{event.location}</span>
</div>
<div className="flex items-center gap-3 text-[var(--purple-lavender)]">
<div className="flex items-center gap-3 text-brand-purple ">
<Users className="h-5 w-5" />
<span style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{event.rsvp_count || 0} {event.rsvp_count === 1 ? 'person' : 'people'} attending
@@ -139,7 +139,7 @@ const EventDetails = () => {
<h2 className="text-2xl font-semibold text-[var(--purple-ink)] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}>
About This Event
</h2>
<p className="text-[var(--purple-lavender)] leading-relaxed whitespace-pre-line" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p className="text-brand-purple leading-relaxed whitespace-pre-line" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{event.description}
</p>
</div>
@@ -168,7 +168,7 @@ const EventDetails = () => {
variant="outline"
className={`rounded-full px-8 py-6 flex items-center gap-2 border-2 ${event.user_rsvp_status === 'maybe'
? 'border-orange-400 bg-orange-100 text-orange-700'
: 'border-[var(--purple-lavender)] text-[var(--purple-lavender)] hover:bg-[var(--lavender-300)]'
: 'border-brand-purple text-brand-purple hover:bg-[var(--lavender-300)]'
}`}
data-testid="rsvp-maybe-button"
>
@@ -195,7 +195,7 @@ const EventDetails = () => {
<h2 className="text-xl font-semibold text-[var(--purple-ink)] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}>
Add to Your Calendar
</h2>
<p className="text-[var(--purple-lavender)] mb-4" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p className="text-brand-purple mb-4" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Never miss this event! Add it to your calendar app for reminders.
</p>
<AddToCalendarButton

View File

@@ -54,14 +54,14 @@ const Events = () => {
<h1 className="text-4xl md:text-5xl font-semibold text-[var(--purple-ink)] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}>
Upcoming Events
</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" }}>
Browse and RSVP to our community events.
</p>
</div>
{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 sm:gap-8">
@@ -73,7 +73,7 @@ const Events = () => {
>
<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>
{getRSVPBadge(event.user_rsvp_status)}
</div>
@@ -83,24 +83,24 @@ const Events = () => {
</h3>
{event.description && (
<p className="text-[var(--purple-lavender)] mb-4 line-clamp-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p className="text-brand-purple mb-4 line-clamp-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{event.description}
</p>
)}
<div className="space-y-2 text-sm">
<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" }}>
{new Date(event.start_at).toLocaleDateString()} at{' '}
{new Date(event.start_at).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}
</span>
</div>
<div className="flex items-center gap-2 text-[var(--purple-lavender)]">
<div className="flex items-center gap-2 text-brand-purple ">
<MapPin className="h-4 w-4" />
<span style={{ fontFamily: "'Nunito Sans', sans-serif" }}>{event.location}</span>
</div>
<div className="flex items-center gap-2 text-[var(--purple-lavender)]">
<div className="flex items-center gap-2 text-brand-purple ">
<Users className="h-4 w-4" />
<span style={{ fontFamily: "'Nunito Sans', sans-serif" }}>{event.rsvp_count || 0} attending</span>
</div>
@@ -120,7 +120,7 @@ const Events = () => {
<h3 className="text-2xl font-semibold text-[var(--purple-ink)] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}>
No Events Available
</h3>
<p className="text-[var(--purple-lavender)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p className="text-brand-purple " style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
There are no upcoming events at the moment. Check back later!
</p>
</div>

View File

@@ -37,7 +37,7 @@ const ForgotPassword = () => {
<div className="max-w-md mx-auto px-6 py-12">
<div className="mb-8">
<Link to="/login" className="inline-flex items-center text-[var(--purple-lavender)] hover:text-[var(--orange-light)] transition-colors">
<Link to="/login" className="inline-flex items-center text-brand-purple hover:text-[var(--orange-light)] transition-colors">
<ArrowLeft className="h-4 w-4 mr-2" />
Back to Login
</Link>
@@ -48,12 +48,12 @@ const ForgotPassword = () => {
<>
<div className="mb-8 text-center">
<div className="inline-flex items-center justify-center w-16 h-16 rounded-full bg-[var(--lavender-300)] mb-4">
<Mail className="h-8 w-8 text-[var(--purple-lavender)]" />
<Mail className="h-8 w-8 text-brand-purple " />
</div>
<h1 className="text-4xl md:text-5xl font-semibold text-[var(--purple-ink)] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}>
Forgot Password?
</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" }}>
No worries! Enter your email and we'll send you reset instructions.
</p>
</div>
@@ -69,7 +69,7 @@ const ForgotPassword = () => {
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="your.email@example.com"
className="h-14 rounded-xl border-2 border-[var(--neutral-800)] focus:border-[var(--purple-lavender)]"
className="h-14 rounded-xl border-2 border-[var(--neutral-800)] focus:border-brand-purple "
/>
</div>
@@ -82,7 +82,7 @@ const ForgotPassword = () => {
<ArrowRight className="ml-2 h-5 w-5" />
</Button>
<p className="text-center text-[var(--purple-lavender)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p className="text-center text-brand-purple " style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Remember your password?{' '}
<Link to="/login" className="text-[var(--orange-light)] hover:underline font-medium">
Login here
@@ -98,11 +98,11 @@ const ForgotPassword = () => {
<h1 className="text-4xl md:text-5xl font-semibold text-[var(--purple-ink)] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}>
Check Your Email
</h1>
<p className="text-lg text-[var(--purple-lavender)] mb-8" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p className="text-lg text-brand-purple mb-8" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
If an account exists for <span className="font-medium text-[var(--purple-ink)]">{email}</span>,
you will receive a password reset link shortly.
</p>
<p className="text-sm text-[var(--purple-lavender)] mb-8" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p className="text-sm text-brand-purple mb-8" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
The link will expire in 1 hour. If you don't see the email, check your spam folder.
</p>
<Link to="/login">

View File

@@ -60,7 +60,7 @@ const Login = () => {
<div className="max-w-md mx-auto px-6 py-12">
<div className="mb-8">
<Link to="/" className="inline-flex items-center text-[var(--purple-lavender)] hover:text-[var(--orange-light)] transition-colors">
<Link to="/" className="inline-flex items-center text-brand-purple hover:text-[var(--orange-light)] transition-colors">
<ArrowLeft className="h-4 w-4 mr-2" />
Back to Home
</Link>
@@ -71,7 +71,7 @@ const Login = () => {
<h1 className="text-4xl md:text-5xl font-semibold text-[var(--purple-ink)] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}>
Welcome Back
</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" }}>
Login to access your member dashboard.
</p>
</div>
@@ -87,7 +87,7 @@ const Login = () => {
value={formData.email}
onChange={handleInputChange}
placeholder="your.email@example.com"
className="h-14 rounded-xl border-2 border-[var(--neutral-800)] focus:border-[var(--purple-lavender)]"
className="h-14 rounded-xl border-2 border-[var(--neutral-800)] focus:border-brand-purple "
data-testid="login-email-input"
/>
</div>
@@ -106,7 +106,7 @@ const Login = () => {
value={formData.password}
onChange={handleInputChange}
placeholder="Enter your password"
className="h-14 rounded-xl border-2 border-[var(--neutral-800)] focus:border-[var(--purple-lavender)]"
className="h-14 rounded-xl border-2 border-[var(--neutral-800)] focus:border-brand-purple "
data-testid="login-password-input"
/>
</div>
@@ -121,7 +121,7 @@ const Login = () => {
<ArrowRight className="ml-2 h-5 w-5" />
</Button>
<p className="text-center text-[var(--purple-lavender)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p className="text-center text-brand-purple " style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Don't have an account?{' '}
<Link to="/register" className="text-[var(--orange-light)] hover:underline font-medium">
Register here

View File

@@ -20,7 +20,7 @@ const NotFound = () => {
404
</h1>
<div className="absolute inset-0 flex items-center justify-center">
<Search className="h-24 w-24 text-[var(--purple-lavender)] opacity-30" />
<Search className="h-24 w-24 text-brand-purple opacity-30" />
</div>
</div>
</div>
@@ -33,7 +33,7 @@ const NotFound = () => {
Page Not Found
</h2>
<p
className="text-lg text-[var(--purple-lavender)] mb-8 max-w-md mx-auto"
className="text-lg text-brand-purple mb-8 max-w-md mx-auto"
style={{ fontFamily: "'Nunito Sans', sans-serif" }}
>
Oops! The page you're looking for doesn't exist. It might have been moved or deleted.
@@ -44,14 +44,14 @@ const NotFound = () => {
<Button
onClick={() => navigate(-1)}
variant="outline"
className="rounded-xl border-2 border-[var(--purple-lavender)] text-[var(--purple-lavender)] hover:bg-[var(--lavender-700)] px-6 py-6"
className="rounded-xl border-2 border-brand-purple text-brand-purple hover:bg-[var(--lavender-700)] px-6 py-6"
>
<ArrowLeft className="h-5 w-5 mr-2" />
Go Back
</Button>
<Button
onClick={() => navigate('/')}
className="rounded-xl bg-gradient-to-r from-[var(--purple-lavender)] to-[var(--purple-ink)] hover:from-[var(--purple-ink)] hover:to-[var(--purple-lavender)] text-white px-6 py-6"
className="rounded-xl bg-gradient-to-r from-brand-purple to-[var(--purple-ink)] hover:from-[var(--purple-ink)] hover:to-brand-purple text-white px-6 py-6"
>
<Home className="h-5 w-5 mr-2" />
Back to Home
@@ -61,13 +61,13 @@ const NotFound = () => {
{/* Help Text */}
<div className="mt-8 pt-8 border-t border-[var(--neutral-800)]">
<p
className="text-sm text-[var(--purple-lavender)]"
className="text-sm text-brand-purple "
style={{ fontFamily: "'Nunito Sans', sans-serif" }}
>
Need help? Contact us at{' '}
<a
href="mailto:support@loaftx.org"
className="text-[var(--purple-lavender)] hover:text-[var(--purple-ink)] font-semibold underline"
className="text-brand-purple hover:text-[var(--purple-ink)] font-semibold underline"
>
support@loaftx.org
</a>

View File

@@ -25,7 +25,7 @@ const PaymentCancel = () => {
<h1 className="text-4xl md:text-5xl font-semibold text-[var(--purple-ink)] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}>
Payment Cancelled
</h1>
<p className="text-lg text-[var(--purple-lavender)] max-w-2xl mx-auto mb-8" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p className="text-lg text-brand-purple max-w-2xl mx-auto mb-8" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Your payment was cancelled. No charges have been made to your account.
</p>
</div>
@@ -37,7 +37,7 @@ const PaymentCancel = () => {
</h2>
<div className="space-y-6 mb-8">
<p className="text-[var(--purple-lavender)] text-center" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p className="text-brand-purple text-center" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
You cancelled the payment process or closed the checkout page. Your membership has not been activated yet.
</p>
@@ -47,14 +47,14 @@ const PaymentCancel = () => {
</h3>
<ul className="space-y-3">
<li className="flex items-start gap-3">
<CreditCard className="h-5 w-5 text-[var(--purple-lavender)] flex-shrink-0 mt-0.5" />
<span className="text-[var(--purple-lavender)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<CreditCard className="h-5 w-5 text-brand-purple flex-shrink-0 mt-0.5" />
<span className="text-brand-purple " style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Return to the plans page to complete your subscription
</span>
</li>
<li className="flex items-start gap-3">
<Mail className="h-5 w-5 text-[var(--purple-lavender)] flex-shrink-0 mt-0.5" />
<span className="text-[var(--purple-lavender)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<Mail className="h-5 w-5 text-brand-purple flex-shrink-0 mt-0.5" />
<span className="text-brand-purple " style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Contact us if you experienced any issues during checkout
</span>
</li>
@@ -62,7 +62,7 @@ const PaymentCancel = () => {
</div>
<div className="bg-[var(--lavender-300)] p-6 rounded-xl">
<p className="text-sm text-[var(--purple-lavender)] text-center mb-4" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p className="text-sm text-brand-purple text-center mb-4" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<span className="font-medium text-[var(--purple-ink)]">Note:</span>{' '}
Your membership application is still validated. You can complete payment whenever you're ready.
</p>
@@ -82,7 +82,7 @@ const PaymentCancel = () => {
<Button
onClick={() => navigate('/dashboard')}
variant="outline"
className="border-2 border-[var(--purple-lavender)] text-[var(--purple-lavender)] hover:bg-[var(--purple-lavender)] hover:text-white rounded-full px-8 py-6 text-lg font-semibold"
className="border-2 border-brand-purple text-brand-purple hover:bg-brand-purple hover:text-white rounded-full px-8 py-6 text-lg font-semibold"
data-testid="back-to-dashboard-button"
>
<ArrowLeft className="mr-2 h-5 w-5" />
@@ -96,13 +96,13 @@ const PaymentCancel = () => {
<h3 className="text-lg font-semibold text-[var(--purple-ink)] mb-3 text-center" style={{ fontFamily: "'Inter', sans-serif" }}>
Need Assistance?
</h3>
<p className="text-[var(--purple-lavender)] text-center mb-4" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p className="text-brand-purple text-center mb-4" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
If you encountered any technical issues or have questions about the payment process, our support team is here to help.
</p>
<div className="text-center">
<a
href="mailto:support@loaf.org"
className="text-[var(--orange-light)] hover:text-[var(--purple-lavender)] font-medium text-lg"
className="text-[var(--orange-light)] hover:text-brand-purple font-medium text-lg"
>
support@loaf.org
</a>

View File

@@ -36,7 +36,7 @@ const PaymentSuccess = () => {
<h1 className="text-4xl md:text-5xl font-semibold text-[var(--purple-ink)] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}>
Payment Successful!
</h1>
<p className="text-lg text-[var(--purple-lavender)] max-w-2xl mx-auto mb-8" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p className="text-lg text-brand-purple max-w-2xl mx-auto mb-8" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Thank you for your payment. Your LOAF membership is now active!
</p>
</div>
@@ -55,25 +55,25 @@ const PaymentSuccess = () => {
<ul className="space-y-3">
<li className="flex items-start gap-3">
<CheckCircle className="h-5 w-5 text-[var(--green-light)] flex-shrink-0 mt-0.5" />
<span className="text-[var(--purple-lavender)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<span className="text-brand-purple " style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Your membership is now active and you have full access to all member benefits
</span>
</li>
<li className="flex items-start gap-3">
<CheckCircle className="h-5 w-5 text-[var(--green-light)] flex-shrink-0 mt-0.5" />
<span className="text-[var(--purple-lavender)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<span className="text-brand-purple " style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
You can now RSVP and attend members-only events
</span>
</li>
<li className="flex items-start gap-3">
<CheckCircle className="h-5 w-5 text-[var(--green-light)] flex-shrink-0 mt-0.5" />
<span className="text-[var(--purple-lavender)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<span className="text-brand-purple " style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Access the community directory and connect with other members
</span>
</li>
<li className="flex items-start gap-3">
<CheckCircle className="h-5 w-5 text-[var(--green-light)] flex-shrink-0 mt-0.5" />
<span className="text-[var(--purple-lavender)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<span className="text-brand-purple " style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
You'll receive our newsletter with exclusive updates and announcements
</span>
</li>
@@ -82,11 +82,11 @@ const PaymentSuccess = () => {
{sessionId && (
<div className="bg-[var(--neutral-800)]/20 p-4 rounded-xl">
<p className="text-sm text-[var(--purple-lavender)] text-center" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p className="text-sm text-brand-purple text-center" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<span className="font-medium text-[var(--purple-ink)]">Transaction ID:</span>{' '}
<span className="font-mono text-xs">{sessionId}</span>
</p>
<p className="text-xs text-[var(--purple-lavender)] text-center mt-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p className="text-xs text-brand-purple text-center mt-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
A confirmation email has been sent to your registered email address.
</p>
</div>
@@ -106,7 +106,7 @@ const PaymentSuccess = () => {
<Button
onClick={() => navigate('/events')}
variant="outline"
className="border-2 border-[var(--purple-lavender)] text-[var(--purple-lavender)] hover:bg-[var(--purple-lavender)] hover:text-white rounded-full px-8 py-6 text-lg font-semibold"
className="border-2 border-brand-purple text-brand-purple hover:bg-brand-purple hover:text-white rounded-full px-8 py-6 text-lg font-semibold"
data-testid="browse-events-button"
>
<Calendar className="mr-2 h-5 w-5" />
@@ -117,11 +117,11 @@ const PaymentSuccess = () => {
{/* Additional Info */}
<div className="text-center">
<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" }}>
Need help? Contact us at{' '}
<a
href="mailto:support@loaf.org"
className="text-[var(--orange-light)] hover:text-[var(--purple-lavender)] font-medium"
className="text-[var(--orange-light)] hover:text-brand-purple font-medium"
>
support@loaf.org
</a>

View File

@@ -217,27 +217,27 @@ const Plans = () => {
<h1 className="text-4xl md:text-5xl font-semibold text-[var(--purple-ink)] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}>
Membership Plans
</h1>
<p className="text-lg text-[var(--purple-lavender)] max-w-2xl mx-auto" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p className="text-lg text-brand-purple max-w-2xl mx-auto" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Choose the membership plan that works best for you and become part of our vibrant community.
</p>
</div>
{/* Status Banner */}
{statusInfo && statusInfo.title && (
<Card className="max-w-3xl mx-auto mb-8 p-6 bg-gradient-to-r from-[var(--lavender-300)] to-[var(--neutral-800)]/30 border-2 border-[var(--purple-lavender)]">
<Card className="max-w-3xl mx-auto mb-8 p-6 bg-gradient-to-r from-[var(--lavender-300)] to-[var(--neutral-800)]/30 border-2 border-brand-purple ">
<div className="flex items-start gap-4">
<AlertCircle className="h-6 w-6 text-[var(--purple-lavender)] flex-shrink-0 mt-1" />
<AlertCircle className="h-6 w-6 text-brand-purple flex-shrink-0 mt-1" />
<div className="flex-1">
<h3 className="text-xl font-semibold text-[var(--purple-ink)] mb-2" style={{ fontFamily: "'Inter', sans-serif" }}>
{statusInfo.title}
</h3>
<p className="text-[var(--purple-lavender)] mb-4" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p className="text-brand-purple mb-4" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{statusInfo.message}
</p>
{statusInfo.action && statusInfo.actionLink && (
<Button
onClick={() => navigate(statusInfo.actionLink)}
className="bg-[var(--purple-lavender)] text-white hover:bg-[var(--purple-ink)] rounded-full"
className="bg-brand-purple text-white hover:bg-[var(--purple-ink)] rounded-full"
>
{statusInfo.action}
</Button>
@@ -249,8 +249,8 @@ const Plans = () => {
{loading ? (
<div className="text-center py-20">
<Loader2 className="h-12 w-12 text-[var(--purple-lavender)] mx-auto mb-4 animate-spin" />
<p className="text-[var(--purple-lavender)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Loading plans...</p>
<Loader2 className="h-12 w-12 text-brand-purple mx-auto mb-4 animate-spin" />
<p className="text-brand-purple " style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Loading plans...</p>
</div>
) : plans.length > 0 ? (
<div className={`grid gap-6 sm:gap-8 mx-auto ${plans.length === 1
@@ -266,19 +266,19 @@ const Plans = () => {
return (
<Card
key={plan.id}
className="p-8 bg-background rounded-2xl border-2 border-[var(--neutral-800)] hover:border-[var(--purple-lavender)] hover:shadow-xl transition-all"
className="p-8 bg-background rounded-2xl border-2 border-[var(--neutral-800)] hover:border-brand-purple hover:shadow-xl transition-all"
data-testid={`plan-card-${plan.id}`}
>
{/* Plan Header */}
<div className="text-center mb-6">
<div className="bg-[var(--neutral-800)]/20 p-4 rounded-full w-16 h-16 mx-auto mb-4 flex items-center justify-center">
<CreditCard className="h-8 w-8 text-[var(--purple-lavender)]" />
<CreditCard className="h-8 w-8 text-brand-purple " />
</div>
<h2 className="text-2xl font-semibold text-[var(--purple-ink)] mb-2" style={{ fontFamily: "'Inter', sans-serif" }}>
{plan.name}
</h2>
{plan.description && (
<p className="text-sm text-[var(--purple-lavender)] mb-4" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p className="text-sm text-brand-purple mb-4" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{plan.description}
</p>
)}
@@ -286,18 +286,18 @@ const Plans = () => {
{/* Pricing */}
<div className="text-center mb-8">
<div className="text-sm text-[var(--purple-lavender)] mb-1" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<div className="text-sm text-brand-purple mb-1" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Starting at
</div>
<div className="text-2xl sm:text-3xl md:text-4xl font-bold text-[var(--purple-ink)] mb-2" style={{ fontFamily: "'Inter', sans-serif" }}>
{formatPrice(minimumPrice)}
</div>
{suggestedPrice > minimumPrice && (
<div className="text-sm text-[var(--purple-lavender)] mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<div className="text-sm text-brand-purple mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Suggested: {formatPrice(suggestedPrice)}
</div>
)}
<p className="text-[var(--purple-lavender)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p className="text-brand-purple " style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{getBillingCycleLabel(plan.billing_cycle)}
</p>
{plan.allow_donation && (
@@ -356,7 +356,7 @@ const Plans = () => {
<h3 className="text-2xl font-semibold text-[var(--purple-ink)] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}>
No Plans Available
</h3>
<p className="text-[var(--purple-lavender)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p className="text-brand-purple " style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Membership plans are not currently available. Please check back later!
</p>
</div>
@@ -368,13 +368,13 @@ const Plans = () => {
<h3 className="text-xl font-semibold text-[var(--purple-ink)] mb-4 text-center" style={{ fontFamily: "'Inter', sans-serif" }}>
Need Help Choosing?
</h3>
<p className="text-[var(--purple-lavender)] text-center mb-4" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p className="text-brand-purple text-center mb-4" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
If you have any questions about our membership plans or need assistance, please contact us.
</p>
<div className="text-center">
<a
href="mailto:support@loaf.org"
className="text-[var(--orange-light)] hover:text-[var(--purple-lavender)] font-medium"
className="text-[var(--orange-light)] hover:text-brand-purple font-medium"
>
support@loaf.org
</a>
@@ -390,7 +390,7 @@ const Plans = () => {
<DialogTitle className="text-2xl text-[var(--purple-ink)]" style={{ fontFamily: "'Inter', sans-serif" }}>
Choose Your Amount
</DialogTitle>
<DialogDescription className="text-[var(--purple-lavender)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<DialogDescription className="text-brand-purple " style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{selectedPlan?.name} - {getBillingCycleLabel(selectedPlan?.billing_cycle)}
</DialogDescription>
</DialogHeader>
@@ -402,7 +402,7 @@ const Plans = () => {
Amount (USD) *
</Label>
<div className="relative mt-2">
<span className="absolute left-4 top-1/2 transform -translate-y-1/2 text-[var(--purple-lavender)] text-lg font-semibold">
<span className="absolute left-4 top-1/2 transform -translate-y-1/2 text-brand-purple text-lg font-semibold">
$
</span>
<Input
@@ -412,11 +412,11 @@ const Plans = () => {
min={selectedPlan ? (selectedPlan.minimum_price_cents / 100).toFixed(2) : "30.00"}
value={amountInput}
onChange={(e) => setAmountInput(e.target.value)}
className="pl-8 h-14 text-xl border-2 border-[var(--neutral-800)] focus:border-[var(--purple-lavender)]"
className="pl-8 h-14 text-xl border-2 border-[var(--neutral-800)] focus:border-brand-purple "
placeholder="50.00"
/>
</div>
<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" }}>
Minimum: {selectedPlan ? formatPrice(selectedPlan.minimum_price_cents || 3000) : '$30.00'}
</p>
</div>

View File

@@ -213,17 +213,17 @@ const Profile = () => {
if (!profileData) {
return (
<div className="min-h-screen bg-background">
<div className="min-h-screen bg-white">
<Navbar />
<div className="flex items-center justify-center min-h-[60vh]">
<p className="text-[var(--purple-lavender)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Loading profile...</p>
<p className="text-brand-purple " style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Loading profile...</p>
</div>
</div>
);
}
return (
<div className="min-h-screen bg-background">
<div className="min-h-screen bg-white">
<Navbar />
<div className="max-w-4xl mx-auto px-6 py-12">
@@ -231,33 +231,33 @@ const Profile = () => {
<h1 className="text-4xl md:text-5xl font-semibold text-[var(--purple-ink)] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}>
My Profile
</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" }}>
Update your personal information below.
</p>
</div>
<Card className="p-8 bg-background rounded-2xl border border-[var(--neutral-800)] shadow-lg">
<Card className="p-8 bg-white rounded-2xl border border-[var(--neutral-800)] shadow-lg">
{/* Read-only Information */}
<div className="mb-8 pb-8 border-b border-[var(--neutral-800)]">
<h2 className="text-2xl font-semibold text-[var(--purple-ink)] mb-6 flex items-center gap-2" style={{ fontFamily: "'Inter', sans-serif" }}>
<User className="h-6 w-6 text-[var(--purple-lavender)]" />
<User className="h-6 w-6 text-brand-purple " />
Account Information
</h2>
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4 sm:gap-6">
<div>
<p className="text-sm text-[var(--purple-lavender)] mb-1" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Email</p>
<p className="text-sm text-brand-purple mb-1" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Email</p>
<p className="text-[var(--purple-ink)] font-medium" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>{profileData.email}</p>
</div>
<div>
<p className="text-sm text-[var(--purple-lavender)] mb-1" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Status</p>
<p className="text-sm text-brand-purple mb-1" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Status</p>
<p className="text-[var(--purple-ink)] font-medium capitalize" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>{profileData.status.replace('_', ' ')}</p>
</div>
<div>
<p className="text-sm text-[var(--purple-lavender)] mb-1" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Role</p>
<p className="text-sm text-brand-purple mb-1" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Role</p>
<p className="text-[var(--purple-ink)] font-medium capitalize" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>{profileData.role}</p>
</div>
<div>
<p className="text-sm text-[var(--purple-lavender)] mb-1" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Date of Birth</p>
<p className="text-sm text-brand-purple mb-1" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Date of Birth</p>
<p className="text-[var(--purple-ink)] font-medium" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{new Date(profileData.date_of_birth).toLocaleDateString()}
</p>
@@ -269,7 +269,7 @@ const Profile = () => {
type="button"
onClick={() => setPasswordDialogOpen(true)}
variant="outline"
className="border-2 border-[var(--purple-lavender)] text-[var(--purple-lavender)] hover:bg-[var(--lavender-300)] rounded-full px-6 py-3"
className="border-2 border-brand-purple text-brand-purple hover:bg-[var(--lavender-300)] rounded-full px-6 py-3"
>
<Lock className="h-4 w-4 mr-2" />
Change Password
@@ -280,13 +280,13 @@ const Profile = () => {
{/* Profile Photo Section */}
<div className="pb-8 mb-8 border-b border-[var(--neutral-800)]">
<h2 className="text-2xl font-semibold text-[var(--purple-ink)] mb-6 flex items-center gap-2" style={{ fontFamily: "'Inter', sans-serif" }}>
<Camera className="h-6 w-6 text-[var(--purple-lavender)]" />
<Camera className="h-6 w-6 text-brand-purple " />
Profile Photo
</h2>
<div className="flex flex-col md:flex-row items-center gap-6">
<Avatar className="h-32 w-32 border-4 border-[var(--neutral-800)]">
<AvatarImage src={previewImage} alt="Profile" />
<AvatarFallback className="bg-[var(--lavender-300)] text-[var(--purple-lavender)] text-3xl">
<AvatarFallback className="bg-[var(--lavender-300)] text-brand-purple text-3xl">
{profileData?.first_name?.charAt(0)}{profileData?.last_name?.charAt(0)}
</AvatarFallback>
</Avatar>
@@ -304,7 +304,7 @@ const Profile = () => {
type="button"
onClick={() => fileInputRef.current?.click()}
disabled={uploadingPhoto}
className="bg-[var(--purple-lavender)] text-white hover:bg-[var(--purple-ink)] rounded-full px-6 py-3"
className="bg-brand-purple text-white hover:bg-[var(--purple-ink)] rounded-full px-6 py-3"
>
<Upload className="h-4 w-4 mr-2" />
{uploadingPhoto ? 'Uploading...' : 'Upload Photo'}
@@ -323,7 +323,7 @@ const Profile = () => {
</Button>
)}
<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" }}>
Upload a profile photo (Max {maxFileSizeMB}MB)
</p>
</div>
@@ -344,7 +344,7 @@ const Profile = () => {
name="first_name"
value={formData.first_name}
onChange={handleInputChange}
className="h-14 rounded-xl border-2 border-[var(--neutral-800)] focus:border-[var(--purple-lavender)]"
className="h-14 rounded-xl border-2 border-[var(--neutral-800)] focus:border-brand-purple "
data-testid="first-name-input"
/>
</div>
@@ -355,7 +355,7 @@ const Profile = () => {
name="last_name"
value={formData.last_name}
onChange={handleInputChange}
className="h-14 rounded-xl border-2 border-[var(--neutral-800)] focus:border-[var(--purple-lavender)]"
className="h-14 rounded-xl border-2 border-[var(--neutral-800)] focus:border-brand-purple "
data-testid="last-name-input"
/>
</div>
@@ -369,7 +369,7 @@ const Profile = () => {
type="tel"
value={formData.phone}
onChange={handleInputChange}
className="h-14 rounded-xl border-2 border-[var(--neutral-800)] focus:border-[var(--purple-lavender)]"
className="h-14 rounded-xl border-2 border-[var(--neutral-800)] focus:border-brand-purple "
data-testid="phone-input"
/>
</div>
@@ -381,7 +381,7 @@ const Profile = () => {
name="address"
value={formData.address}
onChange={handleInputChange}
className="h-14 rounded-xl border-2 border-[var(--neutral-800)] focus:border-[var(--purple-lavender)]"
className="h-14 rounded-xl border-2 border-[var(--neutral-800)] focus:border-brand-purple "
data-testid="address-input"
/>
</div>
@@ -394,7 +394,7 @@ const Profile = () => {
name="city"
value={formData.city}
onChange={handleInputChange}
className="h-14 rounded-xl border-2 border-[var(--neutral-800)] focus:border-[var(--purple-lavender)]"
className="h-14 rounded-xl border-2 border-[var(--neutral-800)] focus:border-brand-purple "
data-testid="city-input"
/>
</div>
@@ -405,7 +405,7 @@ const Profile = () => {
name="state"
value={formData.state}
onChange={handleInputChange}
className="h-14 rounded-xl border-2 border-[var(--neutral-800)] focus:border-[var(--purple-lavender)]"
className="h-14 rounded-xl border-2 border-[var(--neutral-800)] focus:border-brand-purple "
data-testid="state-input"
/>
</div>
@@ -416,7 +416,7 @@ const Profile = () => {
name="zipcode"
value={formData.zipcode}
onChange={handleInputChange}
className="h-14 rounded-xl border-2 border-[var(--neutral-800)] focus:border-[var(--purple-lavender)]"
className="h-14 rounded-xl border-2 border-[var(--neutral-800)] focus:border-brand-purple "
data-testid="zipcode-input"
/>
</div>
@@ -437,7 +437,7 @@ const Profile = () => {
name="partner_first_name"
value={formData.partner_first_name}
onChange={handleInputChange}
className="h-14 rounded-xl border-2 border-[var(--neutral-800)] focus:border-[var(--purple-lavender)]"
className="h-14 rounded-xl border-2 border-[var(--neutral-800)] focus:border-brand-purple "
placeholder="Optional"
/>
</div>
@@ -448,7 +448,7 @@ const Profile = () => {
name="partner_last_name"
value={formData.partner_last_name}
onChange={handleInputChange}
className="h-14 rounded-xl border-2 border-[var(--neutral-800)] focus:border-[var(--purple-lavender)]"
className="h-14 rounded-xl border-2 border-[var(--neutral-800)] focus:border-brand-purple "
placeholder="Optional"
/>
</div>
@@ -460,10 +460,11 @@ const Profile = () => {
id="partner_is_member"
name="partner_is_member"
checked={formData.partner_is_member}
accent-color="var(--brand-white)"
onChange={handleCheckboxChange}
className="w-5 h-5 text-[var(--purple-lavender)] border-2 border-[var(--neutral-800)] rounded focus:ring-[var(--purple-lavender)]"
className="ui-checkbox "
/>
<Label htmlFor="partner_is_member" className="cursor-pointer text-[var(--purple-ink)]">
<Label htmlFor="partner_is_member" className="cursor-pointer text-[var(--purple-ink)] ">
My partner is a current member
</Label>
</div>
@@ -474,7 +475,7 @@ const Profile = () => {
name="partner_plan_to_become_member"
checked={formData.partner_plan_to_become_member}
onChange={handleCheckboxChange}
className="w-5 h-5 text-[var(--purple-lavender)] border-2 border-[var(--neutral-800)] rounded focus:ring-[var(--purple-lavender)]"
className="ui-checkbox "
/>
<Label htmlFor="partner_plan_to_become_member" className="cursor-pointer text-[var(--purple-ink)]">
My partner plans to become a member
@@ -490,7 +491,7 @@ const Profile = () => {
<Mail className="h-6 w-6 text-[var(--green-light)]" />
Newsletter Preferences
</h2>
<p className="text-[var(--purple-lavender)] mb-4" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p className="text-brand-purple mb-4" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Choose what information you'd like published in our member newsletter.
</p>
<div className="space-y-3">
@@ -501,7 +502,7 @@ const Profile = () => {
name="newsletter_publish_name"
checked={formData.newsletter_publish_name}
onChange={handleCheckboxChange}
className="w-5 h-5 text-[var(--purple-lavender)] border-2 border-[var(--neutral-800)] rounded focus:ring-[var(--purple-lavender)]"
className="ui-checkbox "
/>
<Label htmlFor="newsletter_publish_name" className="cursor-pointer text-[var(--purple-ink)]">
Publish my name
@@ -514,7 +515,7 @@ const Profile = () => {
name="newsletter_publish_photo"
checked={formData.newsletter_publish_photo}
onChange={handleCheckboxChange}
className="w-5 h-5 text-[var(--purple-lavender)] border-2 border-[var(--neutral-800)] rounded focus:ring-[var(--purple-lavender)]"
className="ui-checkbox"
/>
<Label htmlFor="newsletter_publish_photo" className="cursor-pointer text-[var(--purple-ink)]">
Publish my photo
@@ -527,7 +528,7 @@ const Profile = () => {
name="newsletter_publish_birthday"
checked={formData.newsletter_publish_birthday}
onChange={handleCheckboxChange}
className="w-5 h-5 text-[var(--purple-lavender)] border-2 border-[var(--neutral-800)] rounded focus:ring-[var(--purple-lavender)]"
className="ui-checkbox"
/>
<Label htmlFor="newsletter_publish_birthday" className="cursor-pointer text-[var(--purple-ink)]">
Publish my birthday
@@ -540,7 +541,7 @@ const Profile = () => {
name="newsletter_publish_none"
checked={formData.newsletter_publish_none}
onChange={handleCheckboxChange}
className="w-5 h-5 text-[var(--purple-lavender)] border-2 border-[var(--neutral-800)] rounded focus:ring-[var(--purple-lavender)]"
className="ui-checkbox"
/>
<Label htmlFor="newsletter_publish_none" className="cursor-pointer text-[var(--purple-ink)]">
Do not publish any information
@@ -552,10 +553,10 @@ const Profile = () => {
{/* Section 4: Volunteer Interests */}
<div className="pt-8 mt-8 border-t border-[var(--neutral-800)]">
<h2 className="text-2xl font-semibold text-[var(--purple-ink)] mb-6 flex items-center gap-2" style={{ fontFamily: "'Inter', sans-serif" }}>
<Users className="h-6 w-6 text-[var(--purple-lavender)]" />
<Users className="h-6 w-6 text-brand-purple " />
Volunteer Interests
</h2>
<p className="text-[var(--purple-lavender)] mb-4" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p className="text-brand-purple mb-4" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Select areas where you'd like to volunteer and help our community.
</p>
<div className="grid md:grid-cols-2 gap-3">
@@ -566,7 +567,7 @@ const Profile = () => {
id={`volunteer_${option.replace(/\s+/g, '_').toLowerCase()}`}
checked={formData.volunteer_interests.includes(option)}
onChange={() => handleVolunteerToggle(option)}
className="w-5 h-5 text-[var(--purple-lavender)] border-2 border-[var(--neutral-800)] rounded focus:ring-[var(--purple-lavender)]"
className="ui-checkbox "
/>
<Label
htmlFor={`volunteer_${option.replace(/\s+/g, '_').toLowerCase()}`}
@@ -585,7 +586,7 @@ const Profile = () => {
<BookUser className="h-6 w-6 text-[var(--orange-light)]" />
Member Directory Settings
</h2>
<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" }}>
Control your visibility and information in the member directory.
</p>
@@ -597,7 +598,7 @@ const Profile = () => {
name="show_in_directory"
checked={formData.show_in_directory}
onChange={handleCheckboxChange}
className="w-5 h-5 text-[var(--purple-lavender)] border-2 border-[var(--neutral-800)] rounded focus:ring-[var(--purple-lavender)]"
className="ui-checkbox"
/>
<Label htmlFor="show_in_directory" className="cursor-pointer text-[var(--purple-ink)] font-medium">
Include me in the member directory
@@ -614,7 +615,7 @@ const Profile = () => {
type="email"
value={formData.directory_email}
onChange={handleInputChange}
className="h-14 rounded-xl border-2 border-[var(--neutral-800)] focus:border-[var(--purple-lavender)]"
className="h-14 rounded-xl border-2 border-[var(--neutral-800)] focus:border-brand-purple "
placeholder="Optional - email to show in directory"
/>
</div>
@@ -626,7 +627,7 @@ const Profile = () => {
name="directory_bio"
value={formData.directory_bio}
onChange={handleInputChange}
className="rounded-xl border-2 border-[var(--neutral-800)] focus:border-[var(--purple-lavender)] min-h-[100px]"
className="rounded-xl border-2 border-[var(--neutral-800)] focus:border-brand-purple min-h-[100px]"
placeholder="Tell other members about yourself..."
/>
</div>
@@ -638,7 +639,7 @@ const Profile = () => {
name="directory_address"
value={formData.directory_address}
onChange={handleInputChange}
className="h-14 rounded-xl border-2 border-[var(--neutral-800)] focus:border-[var(--purple-lavender)]"
className="h-14 rounded-xl border-2 border-[var(--neutral-800)] focus:border-brand-purple "
placeholder="Optional - address to show in directory"
/>
</div>
@@ -651,7 +652,7 @@ const Profile = () => {
type="tel"
value={formData.directory_phone}
onChange={handleInputChange}
className="h-14 rounded-xl border-2 border-[var(--neutral-800)] focus:border-[var(--purple-lavender)]"
className="h-14 rounded-xl border-2 border-[var(--neutral-800)] focus:border-brand-purple "
placeholder="Optional - phone to show in directory"
/>
</div>
@@ -664,7 +665,7 @@ const Profile = () => {
type="date"
value={formData.directory_dob}
onChange={handleInputChange}
className="h-14 rounded-xl border-2 border-[var(--neutral-800)] focus:border-[var(--purple-lavender)]"
className="h-14 rounded-xl border-2 border-[var(--neutral-800)] focus:border-brand-purple "
/>
</div>
@@ -675,7 +676,7 @@ const Profile = () => {
name="directory_partner_name"
value={formData.directory_partner_name}
onChange={handleInputChange}
className="h-14 rounded-xl border-2 border-[var(--neutral-800)] focus:border-[var(--purple-lavender)]"
className="h-14 rounded-xl border-2 border-[var(--neutral-800)] focus:border-brand-purple "
placeholder="Optional - partner name to show in directory"
/>
</div>
@@ -688,7 +689,7 @@ const Profile = () => {
<Button
type="submit"
disabled={loading}
className="bg-[var(--neutral-800)] text-[var(--purple-ink)] hover:bg-background rounded-full px-8 py-6 text-lg font-medium shadow-lg disabled:opacity-50"
className="bg-brand-purple text-white hover:bg-brand-dark-lavender rounded-full px-8 py-6 text-lg font-medium shadow-lg disabled:opacity-50"
data-testid="save-profile-button"
>
<Save className="h-5 w-5 mr-2" />

View File

@@ -188,7 +188,7 @@ const Register = () => {
<div className="max-w-4xl mx-auto px-6 py-12">
<div className="mb-8">
<Link to="/" className="inline-flex items-center text-[var(--purple-lavender)] hover:text-[var(--orange-light)] transition-colors">
<Link to="/" className="inline-flex items-center text-brand-purple hover:text-[var(--orange-light)] transition-colors">
<ArrowLeft className="h-4 w-4 mr-2" />
Back to Home
</Link>
@@ -199,7 +199,7 @@ const Register = () => {
<h1 className="text-4xl md:text-5xl font-semibold text-[var(--purple-ink)] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}>
Join Our Community
</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" }}>
Fill out the form below to start your membership journey.
</p>
</div>
@@ -245,7 +245,7 @@ const Register = () => {
type="button"
onClick={handleBack}
variant="outline"
className="rounded-full px-6 py-6 text-lg border-2 border-[var(--neutral-800)] hover:border-[var(--purple-lavender)] text-[var(--purple-ink)]"
className="rounded-full px-6 py-6 text-lg border-2 border-[var(--neutral-800)] hover:border-brand-purple text-[var(--purple-ink)]"
>
<ArrowLeft className="mr-2 h-5 w-5" />
Back
@@ -276,7 +276,7 @@ const Register = () => {
)}
</div>
<p className="text-center text-[var(--purple-lavender)] mt-4" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p className="text-center text-brand-purple mt-4" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Already have an account?{' '}
<Link to="/login" className="text-[var(--orange-light)] hover:underline font-medium">
Login here

View File

@@ -71,12 +71,12 @@ const ResetPassword = () => {
<Card className="p-8 md:p-12 bg-background rounded-2xl border border-[var(--neutral-800)] shadow-lg">
<div className="mb-8 text-center">
<div className="inline-flex items-center justify-center w-16 h-16 rounded-full bg-[var(--lavender-300)] mb-4">
<Lock className="h-8 w-8 text-[var(--purple-lavender)]" />
<Lock className="h-8 w-8 text-brand-purple " />
</div>
<h1 className="text-4xl md:text-5xl font-semibold text-[var(--purple-ink)] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}>
Reset Password
</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" }}>
Enter your new password below.
</p>
</div>
@@ -92,7 +92,7 @@ const ResetPassword = () => {
value={formData.newPassword}
onChange={handleInputChange}
placeholder="Enter new password (min. 6 characters)"
className="h-14 rounded-xl border-2 border-[var(--neutral-800)] focus:border-[var(--purple-lavender)]"
className="h-14 rounded-xl border-2 border-[var(--neutral-800)] focus:border-brand-purple "
/>
</div>
@@ -106,14 +106,14 @@ const ResetPassword = () => {
value={formData.confirmPassword}
onChange={handleInputChange}
placeholder="Re-enter new password"
className="h-14 rounded-xl border-2 border-[var(--neutral-800)] focus:border-[var(--purple-lavender)]"
className="h-14 rounded-xl border-2 border-[var(--neutral-800)] focus:border-brand-purple "
/>
</div>
<div className="bg-[var(--lavender-300)] border-l-4 border-[var(--purple-lavender)] p-4 rounded-lg">
<div className="bg-[var(--lavender-300)] border-l-4 border-brand-purple p-4 rounded-lg">
<div className="flex items-start">
<AlertCircle className="h-5 w-5 text-[var(--purple-lavender)] mr-2 mt-0.5 flex-shrink-0" />
<div className="text-sm text-[var(--purple-lavender)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<AlertCircle className="h-5 w-5 text-brand-purple mr-2 mt-0.5 flex-shrink-0" />
<div className="text-sm text-brand-purple " style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p className="font-medium text-[var(--purple-ink)] mb-1">Password Requirements:</p>
<ul className="list-disc list-inside space-y-1">
<li>At least 6 characters long</li>
@@ -132,7 +132,7 @@ const ResetPassword = () => {
<ArrowRight className="ml-2 h-5 w-5" />
</Button>
<p className="text-center text-[var(--purple-lavender)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p className="text-center text-brand-purple " style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Remember your password?{' '}
<Link to="/login" className="text-[var(--orange-light)] hover:underline font-medium">
Login here

View File

@@ -52,11 +52,11 @@ const VerifyEmail = () => {
<Card className="p-6 sm:p-8 md:p-12 bg-background rounded-2xl border border-[var(--neutral-800)] shadow-lg text-center">
{status === 'loading' && (
<>
<Loader2 className="h-20 w-20 text-[var(--purple-lavender)] mx-auto mb-6 animate-spin" />
<Loader2 className="h-20 w-20 text-brand-purple mx-auto mb-6 animate-spin" />
<h1 className="text-3xl font-semibold text-[var(--purple-ink)] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}>
Verifying Your Email...
</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" }}>
Please wait while we verify your email address.
</p>
</>
@@ -68,10 +68,10 @@ const VerifyEmail = () => {
<h1 className="text-3xl font-semibold text-[var(--purple-ink)] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}>
Email Verified Successfully!
</h1>
<p className="text-lg text-[var(--purple-lavender)] mb-8" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p className="text-lg text-brand-purple mb-8" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{message}
</p>
<p className="text-base text-[var(--purple-lavender)] mb-8" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p className="text-base text-brand-purple mb-8" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Next steps: Attend an event and meet a board member within 90 days. Once you've attended an event, our admin team will review your application.
</p>
<Link to="/login">
@@ -91,7 +91,7 @@ const VerifyEmail = () => {
<h1 className="text-3xl font-semibold text-[var(--purple-ink)] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}>
Verification Failed
</h1>
<p className="text-lg text-[var(--purple-lavender)] mb-8" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p className="text-lg text-brand-purple mb-8" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{message}
</p>
<Link to="/">

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!'}

View File

@@ -54,7 +54,7 @@ export default function Bylaws() {
<div className="min-h-screen bg-background">
<Navbar />
<div className="flex items-center justify-center min-h-[60vh]">
<p className="text-[var(--purple-lavender)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p className="text-brand-purple " style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Loading bylaws...
</p>
</div>
@@ -72,16 +72,16 @@ export default function Bylaws() {
<h1 className="text-4xl font-semibold text-[var(--purple-ink)] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}>
LOAF Bylaws
</h1>
<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" }}>
Review the official governing bylaws and policies of the LOAF community.
</p>
</div>
{/* Current Bylaws */}
{currentBylaws ? (
<Card className="p-8 bg-background rounded-2xl border-2 border-[var(--purple-lavender)] mb-6">
<Card className="p-8 bg-background rounded-2xl border-2 border-brand-purple mb-6">
<div className="flex items-start gap-4 mb-6">
<div className="bg-gradient-to-br from-[var(--purple-lavender)] to-[var(--purple-ink)] p-4 rounded-xl">
<div className="bg-gradient-to-br from-brand-purple to-[var(--purple-ink)] p-4 rounded-xl">
<Scale className="h-8 w-8 text-white" />
</div>
<div className="flex-1">
@@ -94,7 +94,7 @@ export default function Bylaws() {
Current Version
</Badge>
</div>
<div className="flex items-center gap-4 text-[var(--purple-lavender)] mb-4">
<div className="flex items-center gap-4 text-brand-purple mb-4">
<span style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Version: <strong>{currentBylaws.version}</strong>
</span>
@@ -106,7 +106,7 @@ export default function Bylaws() {
<Button
onClick={() => window.open(currentBylaws.document_url, '_blank')}
size="lg"
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"
>
<ExternalLink className="h-5 w-5" />
View Current Bylaws
@@ -117,7 +117,7 @@ export default function Bylaws() {
) : (
<Card className="p-12 text-center bg-background rounded-2xl border border-[var(--neutral-800)] mb-6">
<Scale className="h-16 w-16 text-[var(--neutral-800)] mx-auto mb-4" />
<p className="text-[var(--purple-lavender)] text-lg" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p className="text-brand-purple text-lg" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
No current bylaws document available
</p>
</Card>
@@ -129,7 +129,7 @@ export default function Bylaws() {
<Button
onClick={() => setShowHistory(!showHistory)}
variant="outline"
className="w-full border-[var(--neutral-800)] text-[var(--purple-lavender)] hover:bg-[var(--lavender-300)] rounded-full flex items-center justify-center gap-2"
className="w-full border-[var(--neutral-800)] text-brand-purple hover:bg-[var(--lavender-300)] rounded-full flex items-center justify-center gap-2"
>
<History className="h-4 w-4" />
{showHistory ? 'Hide' : 'View'} Version History ({history.length - 1} previous {history.length - 1 === 1 ? 'version' : 'versions'})
@@ -150,7 +150,7 @@ export default function Bylaws() {
<h4 className="text-lg font-semibold text-[var(--purple-ink)] mb-1" style={{ fontFamily: "'Inter', sans-serif" }}>
{bylaws.title}
</h4>
<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 {bylaws.version}</span>
<span></span>
<span>Effective {formatDate(bylaws.effective_date)}</span>
@@ -160,7 +160,7 @@ export default function Bylaws() {
onClick={() => window.open(bylaws.document_url, '_blank')}
variant="outline"
size="sm"
className="border-[var(--purple-lavender)] text-[var(--purple-lavender)] hover:bg-[var(--lavender-300)] rounded-full flex items-center gap-2"
className="border-brand-purple text-brand-purple hover:bg-[var(--lavender-300)] rounded-full flex items-center gap-2"
>
<ExternalLink className="h-4 w-4" />
View
@@ -174,12 +174,12 @@ export default function Bylaws() {
{/* Information Card */}
<Card className="mt-8 p-6 bg-[var(--lavender-600)] border border-[var(--neutral-800)]">
<div className="flex items-start gap-3">
<Scale className="h-5 w-5 text-[var(--purple-lavender)] mt-1" />
<Scale className="h-5 w-5 text-brand-purple mt-1" />
<div>
<h4 className="font-semibold text-[var(--purple-ink)] mb-2" style={{ fontFamily: "'Inter', sans-serif" }}>
About LOAF Bylaws
</h4>
<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" }}>
The bylaws serve as the governing document for LOAF, outlining the organization's structure,
membership requirements, officer responsibilities, and operational procedures. All members are
encouraged to familiarize themselves with these guidelines.

View File

@@ -124,7 +124,7 @@ const EventGallery = () => {
</div>
)}
<div className="absolute top-3 right-3">
<Badge className="bg-[var(--purple-lavender)] text-white px-3 py-1 rounded-full">
<Badge className="bg-brand-purple text-white px-3 py-1 rounded-full">
{event.gallery_count} {event.gallery_count === 1 ? 'photo' : 'photos'}
</Badge>
</div>
@@ -136,19 +136,19 @@ const EventGallery = () => {
</h3>
{event.description && (
<p className="text-[var(--purple-lavender)] mb-3 line-clamp-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p className="text-brand-purple mb-3 line-clamp-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{event.description}
</p>
)}
<div className="space-y-2 text-sm">
<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" }}>
{moment(event.start_at).format('MMMM D, YYYY')}
</span>
</div>
<div className="flex items-center gap-2 text-[var(--purple-lavender)]">
<div className="flex items-center gap-2 text-brand-purple ">
<MapPin className="h-4 w-4" />
<span style={{ fontFamily: "'Nunito Sans', sans-serif" }}>{event.location}</span>
</div>
@@ -168,7 +168,7 @@ const EventGallery = () => {
<h1 className="text-4xl md:text-5xl font-semibold text-[var(--purple-ink)] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}>
Event Gallery
</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" }}>
Browse photos from past LOAF events.
</p>
</div>
@@ -176,7 +176,7 @@ const EventGallery = () => {
{/* Events Grid */}
{loading ? (
<div className="text-center py-20">
<p className="text-[var(--purple-lavender)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Loading galleries...</p>
<p className="text-brand-purple " style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Loading galleries...</p>
</div>
) : events.length > 0 ? (
<div className="grid md:grid-cols-2 lg:grid-cols-3 gap-8">
@@ -190,7 +190,7 @@ const EventGallery = () => {
<h3 className="text-2xl font-semibold text-[var(--purple-ink)] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}>
No Event Galleries 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" }}>
Event photos will appear here once admins upload them.
</p>
</div>
@@ -210,7 +210,7 @@ const EventGallery = () => {
<Button
onClick={handleBackToEvents}
variant="ghost"
className="mb-6 text-[var(--purple-lavender)] hover:text-[var(--purple-ink)] hover:bg-[var(--lavender-500)]"
className="mb-6 text-brand-purple hover:text-[var(--purple-ink)] hover:bg-[var(--lavender-500)]"
style={{ fontFamily: "'Inter', sans-serif" }}
>
<ArrowLeft className="h-4 w-4 mr-2" />
@@ -222,7 +222,7 @@ const EventGallery = () => {
<h1 className="text-4xl md:text-5xl font-semibold text-[var(--purple-ink)] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}>
{selectedEvent.title}
</h1>
<div className="flex flex-wrap gap-4 text-[var(--purple-lavender)]">
<div className="flex flex-wrap gap-4 text-brand-purple ">
<div className="flex items-center gap-2">
<Calendar className="h-5 w-5" />
<span style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
@@ -233,7 +233,7 @@ const EventGallery = () => {
<MapPin className="h-5 w-5" />
<span style={{ fontFamily: "'Nunito Sans', sans-serif" }}>{selectedEvent.location}</span>
</div>
<Badge className="bg-[var(--purple-lavender)] text-white px-3 py-1 rounded-full">
<Badge className="bg-brand-purple text-white px-3 py-1 rounded-full">
{selectedEvent.gallery_count} {selectedEvent.gallery_count === 1 ? 'photo' : 'photos'}
</Badge>
</div>
@@ -242,7 +242,7 @@ const EventGallery = () => {
{/* Gallery Grid */}
{galleryLoading ? (
<div className="text-center py-20">
<p className="text-[var(--purple-lavender)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Loading images...</p>
<p className="text-brand-purple " style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Loading images...</p>
</div>
) : galleryImages.length > 0 ? (
<div className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4">
@@ -276,7 +276,7 @@ const EventGallery = () => {
<h3 className="text-2xl font-semibold text-[var(--purple-ink)] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}>
No Photos 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" }}>
Photos from this event will appear here once uploaded.
</p>
</div>

View File

@@ -32,7 +32,7 @@ export default function Financials() {
<div className="min-h-screen bg-background">
<Navbar />
<div className="flex items-center justify-center min-h-[60vh]">
<p className="text-[var(--purple-lavender)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p className="text-brand-purple " style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Loading financial reports...
</p>
</div>
@@ -50,7 +50,7 @@ export default function Financials() {
<h1 className="text-4xl font-semibold text-[var(--purple-ink)] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}>
Financial Reports
</h1>
<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" }}>
Access annual financial reports and stay informed about LOAF's fiscal responsibility.
</p>
</div>
@@ -59,7 +59,7 @@ export default function Financials() {
{reports.length === 0 ? (
<Card className="p-12 text-center bg-background rounded-2xl border border-[var(--neutral-800)]">
<TrendingUp className="h-16 w-16 text-[var(--neutral-800)] mx-auto mb-4" />
<p className="text-[var(--purple-lavender)] text-lg" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p className="text-brand-purple text-lg" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
No financial reports available yet
</p>
</Card>
@@ -69,7 +69,7 @@ export default function Financials() {
<Card key={report.id} className="p-8 bg-background rounded-2xl border border-[var(--neutral-800)] hover:shadow-lg transition-shadow">
<div className="flex items-center gap-6">
{/* Year Badge */}
<div className="bg-gradient-to-br from-[var(--purple-lavender)] to-[var(--purple-ink)] p-6 rounded-xl text-white min-w-[120px] text-center">
<div className="bg-gradient-to-br from-brand-purple to-[var(--purple-ink)] p-6 rounded-xl text-white min-w-[120px] text-center">
<DollarSign className="h-8 w-8 mx-auto mb-2" />
<div className="text-3xl font-bold" style={{ fontFamily: "'Inter', sans-serif" }}>
{report.year}
@@ -83,13 +83,13 @@ export default function Financials() {
{report.title}
</h3>
<div className="flex items-center gap-2 mb-4">
<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>
</div>
<Button
onClick={() => window.open(report.document_url, '_blank')}
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"
>
<ExternalLink className="h-4 w-4" />
View Report
@@ -105,12 +105,12 @@ export default function Financials() {
{reports.length > 0 && (
<Card className="mt-8 p-6 bg-[var(--lavender-600)] border border-[var(--neutral-800)]">
<div className="flex items-start gap-3">
<TrendingUp className="h-5 w-5 text-[var(--purple-lavender)] mt-1" />
<TrendingUp className="h-5 w-5 text-brand-purple mt-1" />
<div>
<h4 className="font-semibold text-[var(--purple-ink)] mb-2" style={{ fontFamily: "'Inter', sans-serif" }}>
Transparency & Accountability
</h4>
<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" }}>
LOAF is committed to financial transparency. These reports provide detailed information about our
revenue, expenses, and how member contributions support our community programs and operations.
</p>

View File

@@ -127,7 +127,7 @@ export default function MemberCalendar() {
<div className="min-h-screen bg-background">
<Navbar />
<div className="flex items-center justify-center min-h-[60vh]">
<p className="text-[var(--purple-lavender)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p className="text-brand-purple " style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Loading calendar...
</p>
</div>
@@ -144,7 +144,7 @@ export default function MemberCalendar() {
<h1 className="text-4xl font-semibold text-[var(--purple-ink)] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}>
Event Calendar
</h1>
<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" }}>
View and manage your event RSVPs. Click on any event to see details and update your RSVP.
</p>
@@ -157,19 +157,19 @@ export default function MemberCalendar() {
<div className="flex gap-4 ml-auto">
<div className="flex items-center gap-2">
<div className="w-4 h-4 rounded bg-[var(--green-light)]"></div>
<span className="text-sm text-[var(--purple-lavender)]">Going</span>
<span className="text-sm text-brand-purple ">Going</span>
</div>
<div className="flex items-center gap-2">
<div className="w-4 h-4 rounded bg-[var(--orange-400)]"></div>
<span className="text-sm text-[var(--purple-lavender)]">Maybe</span>
<span className="text-sm text-brand-purple ">Maybe</span>
</div>
<div className="flex items-center gap-2">
<div className="w-4 h-4 rounded bg-[var(--slate-400)]"></div>
<span className="text-sm text-[var(--purple-lavender)]">Not Going</span>
<span className="text-sm text-brand-purple ">Not Going</span>
</div>
<div className="flex items-center gap-2">
<div className="w-4 h-4 rounded bg-[var(--neutral-800)]"></div>
<span className="text-sm text-[var(--purple-lavender)]">No RSVP</span>
<span className="text-sm text-brand-purple ">No RSVP</span>
</div>
</div>
</div>
@@ -201,7 +201,7 @@ export default function MemberCalendar() {
<DialogHeader>
<div className="flex items-center gap-3 mb-2">
<div className="bg-[var(--neutral-800)]/20 p-3 rounded-lg">
<CalendarIcon className="h-6 w-6 text-[var(--purple-lavender)]" />
<CalendarIcon className="h-6 w-6 text-brand-purple " />
</div>
{selectedEvent.user_rsvp_status && (
<Badge
@@ -225,7 +225,7 @@ export default function MemberCalendar() {
<div className="space-y-4 mt-4">
<div className="space-y-3">
<div className="flex items-center gap-3 text-[var(--purple-lavender)]">
<div className="flex items-center gap-3 text-brand-purple ">
<CalendarIcon className="h-5 w-5" />
<span style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{new Date(selectedEvent.start_at).toLocaleDateString('en-US', {
@@ -236,17 +236,17 @@ export default function MemberCalendar() {
})}
</span>
</div>
<div className="flex items-center gap-3 text-[var(--purple-lavender)]">
<div className="flex items-center gap-3 text-brand-purple ">
<Clock className="h-5 w-5" />
<span style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{new Date(selectedEvent.start_at).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })} - {new Date(selectedEvent.end_at).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}
</span>
</div>
<div className="flex items-center gap-3 text-[var(--purple-lavender)]">
<div className="flex items-center gap-3 text-brand-purple ">
<MapPin className="h-5 w-5" />
<span style={{ fontFamily: "'Nunito Sans', sans-serif" }}>{selectedEvent.location}</span>
</div>
<div className="flex items-center gap-3 text-[var(--purple-lavender)]">
<div className="flex items-center gap-3 text-brand-purple ">
<Users className="h-5 w-5" />
<span style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{selectedEvent.rsvp_count || 0} {selectedEvent.rsvp_count === 1 ? 'person' : 'people'} attending
@@ -260,7 +260,7 @@ export default function MemberCalendar() {
<h3 className="text-lg font-semibold text-[var(--purple-ink)] mb-2" style={{ fontFamily: "'Inter', sans-serif" }}>
About This Event
</h3>
<p className="text-[var(--purple-lavender)] leading-relaxed whitespace-pre-line" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p className="text-brand-purple leading-relaxed whitespace-pre-line" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{selectedEvent.description}
</p>
</div>
@@ -290,7 +290,7 @@ export default function MemberCalendar() {
variant="outline"
className={`rounded-full px-6 flex items-center gap-2 border-2 ${selectedEvent.user_rsvp_status === 'maybe'
? 'border-orange-400 bg-orange-100 text-orange-700'
: 'border-[var(--purple-lavender)] text-[var(--purple-lavender)] hover:bg-[var(--lavender-300)]'
: 'border-brand-purple text-brand-purple hover:bg-[var(--lavender-300)]'
}`}
>
<HelpCircle className="h-4 w-4" />

View File

@@ -114,8 +114,8 @@ const MembersDirectory = () => {
const Border = ({ yaxis = false }) => {
return (
yaxis ?
<div className=' border-2 w-full border-[var(--purple-lavender)] my-24' />
: <div className=' border-2 w-full border-[var(--purple-lavender)] mb-24' />
<div className=' border-2 w-full border-brand-purple my-24' />
: <div className=' border-2 w-full border-brand-purple mb-24' />
)
}
const MemberCard = ({ member }) => (
@@ -130,7 +130,7 @@ const MembersDirectory = () => {
/>
) : (
<div className="w-32 h-32 rounded-full bg-[var(--neutral-800)] border-4 border-[var(--neutral-800)] flex items-center justify-center">
<span className="text-4xl font-semibold text-[var(--purple-lavender)]" style={{ fontFamily: "'Inter', sans-serif" }}>
<span className="text-4xl font-semibold text-brand-purple " style={{ fontFamily: "'Inter', sans-serif" }}>
{getInitials(member.first_name, member.last_name)}
</span>
</div>
@@ -146,7 +146,7 @@ const MembersDirectory = () => {
{member.directory_partner_name && (
<div className="flex items-center justify-center gap-2 mb-4">
<Heart className="h-4 w-4 text-[var(--orange-light)]" />
<span className="text-sm text-[var(--purple-lavender)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<span className="text-sm text-brand-purple " style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Partner: {member.directory_partner_name}
</span>
</div>
@@ -154,7 +154,7 @@ const MembersDirectory = () => {
{/* Bio */}
{member.directory_bio && (
<p className="text-[var(--purple-lavender)] text-center mb-4 line-clamp-3" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p className="text-brand-purple text-center mb-4 line-clamp-3" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{member.directory_bio}
</p>
)}
@@ -162,8 +162,8 @@ const MembersDirectory = () => {
{/* Member Since */}
{member.created_at && (
<div className="flex items-center justify-center gap-2 mb-4">
<Calendar className="h-4 w-4 text-[var(--purple-lavender)]" />
<span className="text-sm text-[var(--purple-lavender)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<Calendar className="h-4 w-4 text-brand-purple " />
<span className="text-sm text-brand-purple " style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Member since {new Date(member.created_at).toLocaleDateString('en-US', {
month: 'long',
year: 'numeric'
@@ -176,10 +176,10 @@ const MembersDirectory = () => {
<div className="space-y-3 mb-4">
{member.directory_email && (
<div className="flex items-center gap-2 text-sm">
<Mail className="h-4 w-4 text-[var(--purple-lavender)] flex-shrink-0" />
<Mail className="h-4 w-4 text-brand-purple flex-shrink-0" />
<a
href={`mailto:${member.directory_email}`}
className="text-[var(--purple-lavender)] hover:text-[var(--purple-ink)] truncate"
className="text-brand-purple hover:text-[var(--purple-ink)] truncate"
style={{ fontFamily: "'Nunito Sans', sans-serif" }}
>
{member.directory_email}
@@ -189,10 +189,10 @@ const MembersDirectory = () => {
{member.directory_phone && (
<div className="flex items-center gap-2 text-sm">
<Phone className="h-4 w-4 text-[var(--purple-lavender)] flex-shrink-0" />
<Phone className="h-4 w-4 text-brand-purple flex-shrink-0" />
<a
href={`tel:${member.directory_phone}`}
className="text-[var(--purple-lavender)] hover:text-[var(--purple-ink)]"
className="text-brand-purple hover:text-[var(--purple-ink)]"
style={{ fontFamily: "'Nunito Sans', sans-serif" }}
>
{member.directory_phone}
@@ -202,8 +202,8 @@ const MembersDirectory = () => {
{member.directory_address && (
<div className="flex items-start gap-2 text-sm">
<MapPin className="h-4 w-4 text-[var(--purple-lavender)] flex-shrink-0 mt-0.5" />
<span className="text-[var(--purple-lavender)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<MapPin className="h-4 w-4 text-brand-purple flex-shrink-0 mt-0.5" />
<span className="text-brand-purple " style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{member.directory_address}
</span>
</div>
@@ -269,7 +269,7 @@ const MembersDirectory = () => {
<div className="pt-4 mt-4 border-t border-[var(--neutral-800)]">
<Button
onClick={() => handleViewProfile(member.id)}
className="w-full bg-[var(--neutral-800)] text-[var(--purple-ink)] hover:bg-[var(--purple-lavender)] hover:text-white rounded-full py-5"
className="w-full bg-[var(--neutral-800)] text-[var(--purple-ink)] hover:bg-brand-purple hover:text-white rounded-full py-5"
>
<UserCircle className="h-4 w-4 mr-2" />
View Full Profile
@@ -293,25 +293,25 @@ const MembersDirectory = () => {
LOAF Members
</h1>
<p className="text-lg " style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<span className='text-foreground'>Number of current memebers in the directory: </span> <span className='text-[var(--purple-lavender)] font-medium'>{totalMembers}</span>
<span className='text-foreground'>Number of current memebers in the directory: </span> <span className='text-brand-purple font-medium'>{totalMembers}</span>
</p>
</div>
{/* Search Bar */}
<div className="mb-24 mx-10">
<div className="relative w-full ">
<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
type="text"
placeholder="Search by name or bio..."
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
className="pl-12 pr-4 py-6 text-3xl font-medium bg-background border-foreground rounded-full focus:border-[var(--purple-lavender)] focus:ring-[var(--purple-lavender)]"
className="pl-12 pr-4 py-6 text-3xl font-medium bg-background border-foreground rounded-full focus:border-brand-purple focus:ring-brand-purple "
style={{ fontFamily: "'Nunito Sans', sans-serif" }}
/>
</div>
{searchQuery && (
<p className="mt-3 text-sm text-[var(--purple-lavender)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p className="mt-3 text-sm text-brand-purple " style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Found {filteredMembers.length} {filteredMembers.length === 1 ? 'member' : 'members'}
</p>
)}
@@ -325,7 +325,7 @@ const MembersDirectory = () => {
{/* Members Grid */}
{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>
) : filteredMembers.length > 0 ? (
<div className="grid md:grid-cols-2 lg:grid-cols-3 gap-8">
@@ -339,7 +339,7 @@ const MembersDirectory = () => {
<h3 className="text-2xl font-semibold text-[var(--purple-ink)] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}>
{searchQuery ? 'No Members Found' : 'No Members in Directory'}
</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
? 'Try adjusting your search query.'
: 'Members who opt in to the directory will appear here.'}
@@ -357,13 +357,13 @@ const MembersDirectory = () => {
<Card className="mt-12 p-6 bg-[var(--lavender-500)] border-[var(--neutral-800)]">
<div className="flex items-start gap-4">
<div className="bg-[var(--neutral-800)]/20 p-3 rounded-lg">
<User className="h-6 w-6 text-[var(--purple-lavender)]" />
<User className="h-6 w-6 text-brand-purple " />
</div>
<div>
<h3 className="text-lg font-semibold text-[var(--purple-ink)] mb-2" style={{ fontFamily: "'Inter', sans-serif" }}>
Want to appear in the directory?
</h3>
<p className="text-[var(--purple-lavender)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p className="text-brand-purple " style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Update your profile settings to show in the directory and add your photo, bio, and contact information.{' '}
<a href="/members/profile" className="text-[var(--orange-light)] hover:underline font-medium">
Edit your profile
@@ -387,7 +387,7 @@ const MembersDirectory = () => {
{selectedMember.directory_partner_name && (
<DialogDescription className="flex items-center gap-2 text-lg" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<Heart className="h-5 w-5 text-[var(--orange-light)]" />
<span className="text-[var(--purple-lavender)]">Partner: {selectedMember.directory_partner_name}</span>
<span className="text-brand-purple ">Partner: {selectedMember.directory_partner_name}</span>
</DialogDescription>
)}
</DialogHeader>
@@ -399,7 +399,7 @@ const MembersDirectory = () => {
<h3 className="text-lg font-semibold text-[var(--purple-ink)] mb-2" style={{ fontFamily: "'Inter', sans-serif" }}>
About
</h3>
<p className="text-[var(--purple-lavender)] leading-relaxed" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p className="text-brand-purple leading-relaxed" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{selectedMember.directory_bio}
</p>
</div>
@@ -414,13 +414,13 @@ const MembersDirectory = () => {
{selectedMember.directory_email && (
<div className="flex items-center gap-3">
<div className="p-2 rounded-lg bg-[var(--lavender-500)]">
<Mail className="h-5 w-5 text-[var(--purple-lavender)]" />
<Mail className="h-5 w-5 text-brand-purple " />
</div>
<div>
<p className="text-xs text-[var(--purple-lavender)] mb-1" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Email</p>
<p className="text-xs text-brand-purple mb-1" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Email</p>
<a
href={`mailto:${selectedMember.directory_email}`}
className="text-[var(--purple-ink)] hover:text-[var(--purple-lavender)] font-medium"
className="text-[var(--purple-ink)] hover:text-brand-purple font-medium"
style={{ fontFamily: "'Nunito Sans', sans-serif" }}
>
{selectedMember.directory_email}
@@ -432,13 +432,13 @@ const MembersDirectory = () => {
{selectedMember.directory_phone && (
<div className="flex items-center gap-3">
<div className="p-2 rounded-lg bg-[var(--lavender-500)]">
<Phone className="h-5 w-5 text-[var(--purple-lavender)]" />
<Phone className="h-5 w-5 text-brand-purple " />
</div>
<div>
<p className="text-xs text-[var(--purple-lavender)] mb-1" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Phone</p>
<p className="text-xs text-brand-purple mb-1" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Phone</p>
<a
href={`tel:${selectedMember.directory_phone}`}
className="text-[var(--purple-ink)] hover:text-[var(--purple-lavender)] font-medium"
className="text-[var(--purple-ink)] hover:text-brand-purple font-medium"
style={{ fontFamily: "'Nunito Sans', sans-serif" }}
>
{selectedMember.directory_phone}
@@ -450,10 +450,10 @@ const MembersDirectory = () => {
{selectedMember.directory_address && (
<div className="flex items-start gap-3">
<div className="p-2 rounded-lg bg-[var(--lavender-500)]">
<MapPin className="h-5 w-5 text-[var(--purple-lavender)]" />
<MapPin className="h-5 w-5 text-brand-purple " />
</div>
<div>
<p className="text-xs text-[var(--purple-lavender)] mb-1" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Address</p>
<p className="text-xs text-brand-purple mb-1" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Address</p>
<p className="text-[var(--purple-ink)] font-medium" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{selectedMember.directory_address}
</p>
@@ -467,7 +467,7 @@ const MembersDirectory = () => {
<Heart className="h-5 w-5 text-[var(--orange-light)]" />
</div>
<div>
<p className="text-xs text-[var(--purple-lavender)] mb-1" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Birthday</p>
<p className="text-xs text-brand-purple mb-1" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Birthday</p>
<p className="text-[var(--purple-ink)] font-medium" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{formatDate(selectedMember.directory_dob)}
</p>
@@ -487,7 +487,7 @@ const MembersDirectory = () => {
{selectedMember.volunteer_interests.map((interest, index) => (
<Badge
key={index}
className="bg-[var(--neutral-800)] text-[var(--purple-ink)] hover:bg-[var(--purple-lavender)] hover:text-white"
className="bg-[var(--neutral-800)] text-[var(--purple-ink)] hover:bg-brand-purple hover:text-white"
>
{interest}
</Badge>
@@ -565,21 +565,21 @@ const MembersDirectory = () => {
{/* Pagination */}
{!loading && filteredMembers.length > 0 && (
<div className="mt-10 flex flex-col items-center gap-4 pb-12">
<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" }}>
Showing {pageStart + 1}{Math.min(pageStart + pageSize, filteredMembers.length)} of {filteredMembers.length}
</p>
<div className="flex flex-wrap items-center justify-center gap-2">
<Button
onClick={() => setCurrentPage(1)}
disabled={currentPage === 1}
className="bg-[var(--neutral-800)] rounded-full text-[var(--purple-ink)] hover:bg-[var(--purple-lavender)] hover:text-white"
className="bg-[var(--neutral-800)] rounded-full text-[var(--purple-ink)] hover:bg-brand-purple hover:text-white"
>
First Page
</Button>
<Button
onClick={() => setCurrentPage((page) => Math.max(1, page - 1))}
disabled={currentPage === 1}
className="bg-[var(--neutral-800)] rounded-full text-[var(--purple-ink)] hover:bg-[var(--purple-lavender)] hover:text-white"
className="bg-[var(--neutral-800)] rounded-full text-[var(--purple-ink)] hover:bg-brand-purple hover:text-white"
>
Previous
</Button>
@@ -593,8 +593,8 @@ const MembersDirectory = () => {
onClick={() => setCurrentPage(pageNumber)}
className={
isActive
? "bg-[var(--purple-lavender)] text-white hover:bg-[var(--purple-ink)] rounded-full"
: "bg-[var(--neutral-800)] text-[var(--purple-ink)] hover:bg-[var(--purple-lavender)] hover:text-white rounded-full"
? "bg-brand-purple text-white hover:bg-[var(--purple-ink)] rounded-full"
: "bg-[var(--neutral-800)] text-[var(--purple-ink)] hover:bg-brand-purple hover:text-white rounded-full"
}
>
{pageNumber}
@@ -605,14 +605,14 @@ const MembersDirectory = () => {
<Button
onClick={() => setCurrentPage((page) => Math.min(totalPages, page + 1))}
disabled={currentPage === totalPages}
className="bg-[var(--neutral-800)] text-[var(--purple-ink)] hover:bg-[var(--purple-lavender)] rounded-full hover:text-white"
className="bg-[var(--neutral-800)] text-[var(--purple-ink)] hover:bg-brand-purple rounded-full hover:text-white"
>
Next
</Button>
<Button
onClick={() => setCurrentPage(totalPages)}
disabled={currentPage === totalPages}
className="bg-[var(--neutral-800)] text-[var(--purple-ink)] hover:bg-[var(--purple-lavender)] rounded-full hover:text-white"
className="bg-[var(--neutral-800)] text-[var(--purple-ink)] hover:bg-brand-purple rounded-full hover:text-white"
>
Last Page
</Button>

View File

@@ -86,7 +86,7 @@ export default function NewsletterArchive() {
<div className="min-h-screen bg-background">
<Navbar />
<div className="flex items-center justify-center min-h-[60vh]">
<p className="text-[var(--purple-lavender)]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p className="text-brand-purple " style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Loading newsletters...
</p>
</div>
@@ -104,7 +104,7 @@ export default function NewsletterArchive() {
<h1 className="text-4xl font-semibold text-[var(--purple-ink)] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}>
Newsletter Archive
</h1>
<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" }}>
Browse past monthly newsletters and stay informed about LOAF community updates.
</p>
@@ -112,13 +112,13 @@ export default function NewsletterArchive() {
<div className="flex gap-4 flex-wrap items-center">
{/* Search */}
<div className="relative flex-1 min-w-[300px]">
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 h-4 w-4 text-[var(--purple-lavender)]" />
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 h-4 w-4 text-brand-purple " />
<Input
type="text"
placeholder="Search newsletters..."
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
className="pl-10 border-[var(--neutral-800)] focus:border-[var(--purple-lavender)]"
className="pl-10 border-[var(--neutral-800)] focus:border-brand-purple "
/>
</div>
@@ -128,7 +128,7 @@ export default function NewsletterArchive() {
onClick={clearFilter}
variant={selectedYear === null ? "default" : "outline"}
size="sm"
className={selectedYear === null ? "bg-[var(--purple-lavender)] text-white" : "border-[var(--purple-lavender)] text-[var(--purple-lavender)]"}
className={selectedYear === null ? "bg-brand-purple text-white" : "border-brand-purple text-brand-purple "}
>
All Years
</Button>
@@ -138,7 +138,7 @@ export default function NewsletterArchive() {
onClick={() => handleYearFilter(year)}
variant={selectedYear === year ? "default" : "outline"}
size="sm"
className={selectedYear === year ? "bg-[var(--purple-lavender)] text-white" : "border-[var(--purple-lavender)] text-[var(--purple-lavender)]"}
className={selectedYear === year ? "bg-brand-purple text-white" : "border-brand-purple text-brand-purple "}
>
{year}
</Button>
@@ -151,7 +151,7 @@ export default function NewsletterArchive() {
{filteredNewsletters.length === 0 ? (
<Card className="p-12 text-center bg-background rounded-2xl border border-[var(--neutral-800)]">
<FileText className="h-16 w-16 text-[var(--neutral-800)] mx-auto mb-4" />
<p className="text-[var(--purple-lavender)] text-lg" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p className="text-brand-purple text-lg" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
No newsletters found
</p>
</Card>
@@ -168,14 +168,14 @@ export default function NewsletterArchive() {
<Card key={newsletter.id} className="p-6 bg-background rounded-2xl border border-[var(--neutral-800)] hover:shadow-lg transition-shadow">
<div className="flex items-start gap-4">
<div className="bg-[var(--neutral-800)]/20 p-3 rounded-lg flex-shrink-0">
<FileText className="h-6 w-6 text-[var(--purple-lavender)]" />
<FileText className="h-6 w-6 text-brand-purple " />
</div>
<div className="flex-1 min-w-0">
<h3 className="text-xl font-semibold text-[var(--purple-ink)] mb-2" style={{ fontFamily: "'Inter', sans-serif" }}>
{newsletter.title}
</h3>
{newsletter.description && (
<p className="text-[var(--purple-lavender)] mb-3 line-clamp-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p className="text-brand-purple mb-3 line-clamp-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{newsletter.description}
</p>
)}
@@ -183,13 +183,13 @@ export default function NewsletterArchive() {
<Badge className="bg-[var(--neutral-800)] text-[var(--purple-ink)] hover:bg-[var(--neutral-800)]">
{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 === 'google_docs' ? 'Google Docs' : newsletter.document_type.toUpperCase()}
</Badge>
</div>
<Button
onClick={() => window.open(newsletter.document_url, '_blank')}
className="w-full bg-[var(--purple-lavender)] text-white hover:bg-[var(--purple-muted)] rounded-full flex items-center justify-center gap-2"
className="w-full bg-brand-purple text-white hover:bg-[var(--purple-muted)] rounded-full flex items-center justify-center gap-2"
>
<ExternalLink className="h-4 w-4" />
View Newsletter