Update FE

This commit is contained in:
Koncept Kit
2025-12-08 20:45:40 +07:00
parent d8c1e133ac
commit 1f27c3224b
23 changed files with 438 additions and 438 deletions

View File

@@ -116,7 +116,7 @@ const AdminSidebar = ({ isOpen, onToggle, isMobile }) => {
{/* Sidebar */} {/* Sidebar */}
<aside <aside
className={` className={`
bg-white border-r border-[#EAE0D5] transition-all duration-300 ease-out bg-white border-r border-[#ddd8eb] transition-all duration-300 ease-out
${isMobile ? 'fixed inset-y-0 left-0 z-40' : 'relative'} ${isMobile ? 'fixed inset-y-0 left-0 z-40' : 'relative'}
${isOpen ? 'w-64' : 'w-16'} ${isOpen ? 'w-64' : 'w-16'}
${isMobile && !isOpen ? '-translate-x-full' : 'translate-x-0'} ${isMobile && !isOpen ? '-translate-x-full' : 'translate-x-0'}
@@ -124,23 +124,23 @@ const AdminSidebar = ({ isOpen, onToggle, isMobile }) => {
`} `}
> >
{/* Header */} {/* Header */}
<div className="flex items-center justify-between p-4 border-b border-[#EAE0D5]"> <div className="flex items-center justify-between p-4 border-b border-[#ddd8eb]">
{isOpen && ( {isOpen && (
<h2 className="text-xl font-semibold fraunces text-[#3D405B]"> <h2 className="text-xl font-semibold text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}>
Admin Admin
</h2> </h2>
)} )}
<button <button
onClick={onToggle} onClick={onToggle}
className="p-2 rounded-lg hover:bg-[#F2CC8F]/20 transition-colors ml-auto" className="p-2 rounded-lg hover:bg-[#DDD8EB]/20 transition-colors ml-auto"
aria-label={isOpen ? 'Collapse sidebar' : 'Expand sidebar'} aria-label={isOpen ? 'Collapse sidebar' : 'Expand sidebar'}
> >
{isMobile ? ( {isMobile ? (
<Menu className="h-5 w-5 text-[#3D405B]" /> <Menu className="h-5 w-5 text-[#422268]" />
) : isOpen ? ( ) : isOpen ? (
<ChevronLeft className="h-5 w-5 text-[#3D405B]" /> <ChevronLeft className="h-5 w-5 text-[#422268]" />
) : ( ) : (
<ChevronRight className="h-5 w-5 text-[#3D405B]" /> <ChevronRight className="h-5 w-5 text-[#422268]" />
)} )}
</button> </button>
</div> </div>
@@ -163,16 +163,16 @@ const AdminSidebar = ({ isOpen, onToggle, isMobile }) => {
className={` className={`
flex items-center gap-3 px-4 py-3 rounded-lg transition-all relative flex items-center gap-3 px-4 py-3 rounded-lg transition-all relative
${item.disabled ${item.disabled
? 'opacity-50 cursor-not-allowed text-[#6B708D]' ? 'opacity-50 cursor-not-allowed text-[#664fa3]'
: active : active
? 'bg-[#E07A5F]/10 text-[#E07A5F]' ? 'bg-[#ff9e77]/10 text-[#ff9e77]'
: 'text-[#3D405B] hover:bg-[#F2CC8F]/20' : 'text-[#422268] hover:bg-[#DDD8EB]/20'
} }
`} `}
> >
{/* Active border */} {/* Active border */}
{active && !item.disabled && ( {active && !item.disabled && (
<div className="absolute left-0 top-0 bottom-0 w-1 bg-[#E07A5F] rounded-r" /> <div className="absolute left-0 top-0 bottom-0 w-1 bg-[#ff9e77] rounded-r" />
)} )}
<Icon className="h-5 w-5 flex-shrink-0" /> <Icon className="h-5 w-5 flex-shrink-0" />
@@ -181,12 +181,12 @@ const AdminSidebar = ({ isOpen, onToggle, isMobile }) => {
<> <>
<span className="flex-1">{item.name}</span> <span className="flex-1">{item.name}</span>
{item.disabled && ( {item.disabled && (
<Badge className="bg-[#F2CC8F] text-[#3D405B] text-xs px-2 py-0.5"> <Badge className="bg-[#DDD8EB] text-[#422268] text-xs px-2 py-0.5">
Soon Soon
</Badge> </Badge>
)} )}
{item.badge > 0 && !item.disabled && ( {item.badge > 0 && !item.disabled && (
<Badge className="bg-[#E07A5F] text-white text-xs px-2 py-0.5"> <Badge className="bg-[#ff9e77] text-white text-xs px-2 py-0.5">
{item.badge} {item.badge}
</Badge> </Badge>
)} )}
@@ -195,7 +195,7 @@ const AdminSidebar = ({ isOpen, onToggle, isMobile }) => {
{/* Badge when collapsed */} {/* Badge when collapsed */}
{!isOpen && item.badge > 0 && !item.disabled && ( {!isOpen && item.badge > 0 && !item.disabled && (
<div className="absolute -top-1 -right-1 bg-[#E07A5F] text-white text-xs rounded-full h-5 w-5 flex items-center justify-center font-medium"> <div className="absolute -top-1 -right-1 bg-[#ff9e77] text-white text-xs rounded-full h-5 w-5 flex items-center justify-center font-medium">
{item.badge} {item.badge}
</div> </div>
)} )}
@@ -203,7 +203,7 @@ const AdminSidebar = ({ isOpen, onToggle, isMobile }) => {
{/* Tooltip when collapsed */} {/* Tooltip when collapsed */}
{!isOpen && ( {!isOpen && (
<div className="absolute left-full ml-2 top-1/2 -translate-y-1/2 px-3 py-2 bg-[#3D405B] text-white text-sm rounded-lg opacity-0 group-hover:opacity-100 pointer-events-none transition-opacity whitespace-nowrap z-50"> <div className="absolute left-full ml-2 top-1/2 -translate-y-1/2 px-3 py-2 bg-[#422268] text-white text-sm rounded-lg opacity-0 group-hover:opacity-100 pointer-events-none transition-opacity whitespace-nowrap z-50">
{item.name} {item.name}
{item.badge > 0 && ` (${item.badge})`} {item.badge > 0 && ` (${item.badge})`}
</div> </div>
@@ -214,18 +214,18 @@ const AdminSidebar = ({ isOpen, onToggle, isMobile }) => {
</nav> </nav>
{/* User Section */} {/* User Section */}
<div className="border-t border-[#EAE0D5] p-4 space-y-2"> <div className="border-t border-[#ddd8eb] p-4 space-y-2">
{isOpen && user && ( {isOpen && user && (
<div className="px-4 py-3 mb-2"> <div className="px-4 py-3 mb-2">
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
<div className="h-10 w-10 rounded-full bg-[#F2CC8F] flex items-center justify-center text-[#3D405B] font-semibold"> <div className="h-10 w-10 rounded-full bg-[#DDD8EB] flex items-center justify-center text-[#422268] font-semibold">
{user.first_name?.[0]}{user.last_name?.[0]} {user.first_name?.[0]}{user.last_name?.[0]}
</div> </div>
<div className="flex-1 min-w-0"> <div className="flex-1 min-w-0">
<p className="text-sm font-medium text-[#3D405B] truncate"> <p className="text-sm font-medium text-[#422268] truncate" style={{ fontFamily: "'Inter', sans-serif" }}>
{user.first_name} {user.last_name} {user.first_name} {user.last_name}
</p> </p>
<p className="text-xs text-[#6B708D] capitalize truncate"> <p className="text-xs text-[#664fa3] capitalize truncate" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{user.role} {user.role}
</p> </p>
</div> </div>
@@ -238,7 +238,7 @@ const AdminSidebar = ({ isOpen, onToggle, isMobile }) => {
onClick={handleLogout} onClick={handleLogout}
className={` className={`
flex items-center gap-3 px-4 py-3 rounded-lg w-full flex items-center gap-3 px-4 py-3 rounded-lg w-full
text-[#E07A5F] hover:bg-[#E07A5F]/10 transition-colors text-[#ff9e77] hover:bg-[#ff9e77]/10 transition-colors
${!isOpen && 'justify-center'} ${!isOpen && 'justify-center'}
`} `}
> >
@@ -249,7 +249,7 @@ const AdminSidebar = ({ isOpen, onToggle, isMobile }) => {
{/* Logout tooltip when collapsed */} {/* Logout tooltip when collapsed */}
{!isOpen && ( {!isOpen && (
<div className="relative group"> <div className="relative group">
<div className="absolute left-full ml-2 bottom-0 px-3 py-2 bg-[#3D405B] text-white text-sm rounded-lg opacity-0 group-hover:opacity-100 pointer-events-none transition-opacity whitespace-nowrap z-50"> <div className="absolute left-full ml-2 bottom-0 px-3 py-2 bg-[#422268] text-white text-sm rounded-lg opacity-0 group-hover:opacity-100 pointer-events-none transition-opacity whitespace-nowrap z-50">
Logout Logout
</div> </div>
</div> </div>

View File

@@ -57,19 +57,19 @@ export const AttendanceDialog = ({ event, open, onOpenChange, onSuccess }) => {
<Dialog open={open} onOpenChange={onOpenChange}> <Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent className="max-w-2xl max-h-[80vh] overflow-y-auto bg-white"> <DialogContent className="max-w-2xl max-h-[80vh] overflow-y-auto bg-white">
<DialogHeader> <DialogHeader>
<DialogTitle className="text-2xl fraunces text-[#3D405B]"> <DialogTitle className="text-2xl text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}>
Mark Attendance: {event?.title} Mark Attendance: {event?.title}
</DialogTitle> </DialogTitle>
</DialogHeader> </DialogHeader>
<div className="space-y-4 mt-4"> <div className="space-y-4 mt-4">
{rsvps.length === 0 ? ( {rsvps.length === 0 ? (
<p className="text-center text-[#6B708D] py-8">No RSVPs yet</p> <p className="text-center text-[#664fa3] py-8" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>No RSVPs yet</p>
) : ( ) : (
rsvps.map((rsvp) => ( rsvps.map((rsvp) => (
<div <div
key={rsvp.user_id} key={rsvp.user_id}
className="flex items-center gap-3 p-4 border-2 border-[#EAE0D5] rounded-xl hover:border-[#E07A5F] transition-colors" className="flex items-center gap-3 p-4 border-2 border-[#ddd8eb] rounded-xl hover:border-[#664fa3] transition-colors"
> >
<Checkbox <Checkbox
checked={attendance[rsvp.user_id] || false} checked={attendance[rsvp.user_id] || false}
@@ -79,8 +79,8 @@ export const AttendanceDialog = ({ event, open, onOpenChange, onSuccess }) => {
className="w-5 h-5" className="w-5 h-5"
/> />
<div className="flex-1"> <div className="flex-1">
<p className="font-medium text-[#3D405B]">{rsvp.user_name}</p> <p className="font-medium text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}>{rsvp.user_name}</p>
<p className="text-sm text-[#6B708D]">{rsvp.user_email}</p> <p className="text-sm text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>{rsvp.user_email}</p>
</div> </div>
{rsvp.attended && ( {rsvp.attended && (
<span className="text-sm text-[#81B29A] font-medium"> <span className="text-sm text-[#81B29A] font-medium">
@@ -96,14 +96,14 @@ export const AttendanceDialog = ({ event, open, onOpenChange, onSuccess }) => {
<Button <Button
onClick={handleSave} onClick={handleSave}
disabled={loading} disabled={loading}
className="flex-1 bg-[#E07A5F] text-white hover:bg-[#D0694E] rounded-full" className="flex-1 bg-[#DDD8EB] text-[#422268] hover:bg-white rounded-full"
> >
{loading ? 'Saving...' : 'Save Attendance'} {loading ? 'Saving...' : 'Save Attendance'}
</Button> </Button>
<Button <Button
onClick={() => onOpenChange(false)} onClick={() => onOpenChange(false)}
variant="outline" variant="outline"
className="flex-1 border-2 border-[#6B708D] text-[#6B708D] hover:bg-[#6B708D] hover:text-white rounded-full" className="flex-1 border-2 border-[#ddd8eb] text-[#664fa3] hover:bg-white hover:text-[#422268] rounded-full"
> >
Cancel Cancel
</Button> </Button>

View File

@@ -69,14 +69,14 @@ const ChangePasswordDialog = ({ open, onOpenChange }) => {
<DialogContent className="sm:max-w-md bg-white"> <DialogContent className="sm:max-w-md bg-white">
<DialogHeader> <DialogHeader>
<div className="flex items-center gap-2 mb-2"> <div className="flex items-center gap-2 mb-2">
<div className="inline-flex items-center justify-center w-10 h-10 rounded-full bg-[#FFF3E0]"> <div className="inline-flex items-center justify-center w-10 h-10 rounded-full bg-[#f1eef9]">
<Lock className="h-5 w-5 text-[#E07A5F]" /> <Lock className="h-5 w-5 text-[#ff9e77]" />
</div> </div>
<DialogTitle className="text-2xl font-semibold text-[#3D405B]"> <DialogTitle className="text-2xl font-semibold text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}>
Change Password Change Password
</DialogTitle> </DialogTitle>
</div> </div>
<DialogDescription className="text-[#6B708D]"> <DialogDescription className="text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Update your password to keep your account secure. Update your password to keep your account secure.
</DialogDescription> </DialogDescription>
</DialogHeader> </DialogHeader>
@@ -92,7 +92,7 @@ const ChangePasswordDialog = ({ open, onOpenChange }) => {
value={formData.currentPassword} value={formData.currentPassword}
onChange={handleInputChange} onChange={handleInputChange}
placeholder="Enter current password" placeholder="Enter current password"
className="h-12 rounded-xl border-2 border-[#EAE0D5] focus:border-[#E07A5F]" className="h-12 rounded-xl border-2 border-[#ddd8eb] focus:border-[#664fa3]"
/> />
</div> </div>
@@ -106,7 +106,7 @@ const ChangePasswordDialog = ({ open, onOpenChange }) => {
value={formData.newPassword} value={formData.newPassword}
onChange={handleInputChange} onChange={handleInputChange}
placeholder="Enter new password (min. 6 characters)" placeholder="Enter new password (min. 6 characters)"
className="h-12 rounded-xl border-2 border-[#EAE0D5] focus:border-[#E07A5F]" className="h-12 rounded-xl border-2 border-[#ddd8eb] focus:border-[#664fa3]"
/> />
</div> </div>
@@ -120,7 +120,7 @@ const ChangePasswordDialog = ({ open, onOpenChange }) => {
value={formData.confirmPassword} value={formData.confirmPassword}
onChange={handleInputChange} onChange={handleInputChange}
placeholder="Re-enter new password" placeholder="Re-enter new password"
className="h-12 rounded-xl border-2 border-[#EAE0D5] focus:border-[#E07A5F]" className="h-12 rounded-xl border-2 border-[#ddd8eb] focus:border-[#664fa3]"
/> />
</div> </div>
@@ -136,7 +136,7 @@ const ChangePasswordDialog = ({ open, onOpenChange }) => {
<Button <Button
type="submit" type="submit"
disabled={loading} disabled={loading}
className="bg-[#E07A5F] text-white hover:bg-[#D0694E] rounded-full px-6 disabled:opacity-50" className="bg-[#DDD8EB] text-[#422268] hover:bg-white rounded-full px-6 disabled:opacity-50"
> >
{loading ? 'Changing...' : 'Change Password'} {loading ? 'Changing...' : 'Change Password'}
</Button> </Button>

View File

@@ -14,12 +14,12 @@ const Navbar = () => {
}; };
return ( return (
<nav className="bg-white border-b border-[#EAE0D5] sticky top-0 z-50 backdrop-blur-sm bg-white/90"> <nav className="bg-white border-b border-[#ddd8eb] sticky top-0 z-50 backdrop-blur-sm bg-white/90">
<div className="max-w-7xl mx-auto px-6 py-4"> <div className="max-w-7xl mx-auto px-6 py-4">
<div className="flex justify-between items-center"> <div className="flex justify-between items-center">
<Link to={user ? "/dashboard" : "/"} className="flex items-center gap-2"> <Link to={user ? "/dashboard" : "/"} className="flex items-center gap-2">
<Users className="h-8 w-8 text-[#E07A5F]" strokeWidth={1.5} /> <Users className="h-8 w-8 text-[#ff9e77]" strokeWidth={1.5} />
<span className="text-2xl font-semibold fraunces text-[#3D405B]">Membership</span> <span className="text-2xl font-semibold text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}>Membership</span>
</Link> </Link>
<div className="flex items-center gap-4"> <div className="flex items-center gap-4">
@@ -29,7 +29,7 @@ const Navbar = () => {
<Link to="/admin"> <Link to="/admin">
<Button <Button
variant="ghost" variant="ghost"
className="text-[#3D405B] hover:text-[#E07A5F] hover:bg-[#F2CC8F]/10" className="text-[#422268] hover:text-[#ff9e77] hover:bg-[#DDD8EB]/10"
data-testid="admin-nav-button" data-testid="admin-nav-button"
> >
<LayoutDashboard className="h-4 w-4 mr-2" /> <LayoutDashboard className="h-4 w-4 mr-2" />
@@ -40,7 +40,7 @@ const Navbar = () => {
<Link to="/events"> <Link to="/events">
<Button <Button
variant="ghost" variant="ghost"
className="text-[#3D405B] hover:text-[#E07A5F] hover:bg-[#F2CC8F]/10" className="text-[#422268] hover:text-[#ff9e77] hover:bg-[#DDD8EB]/10"
data-testid="events-nav-button" data-testid="events-nav-button"
> >
Events Events
@@ -49,7 +49,7 @@ const Navbar = () => {
<Link to="/profile"> <Link to="/profile">
<Button <Button
variant="ghost" variant="ghost"
className="text-[#3D405B] hover:text-[#E07A5F] hover:bg-[#F2CC8F]/10" className="text-[#422268] hover:text-[#ff9e77] hover:bg-[#DDD8EB]/10"
data-testid="profile-nav-button" data-testid="profile-nav-button"
> >
Profile Profile
@@ -57,7 +57,7 @@ const Navbar = () => {
</Link> </Link>
<Button <Button
onClick={handleLogout} onClick={handleLogout}
className="bg-[#E07A5F] text-white hover:bg-[#D0694E] rounded-full px-6" className="bg-[#DDD8EB] text-[#422268] hover:bg-white rounded-full px-6"
data-testid="logout-button" data-testid="logout-button"
> >
<LogOut className="h-4 w-4 mr-2" /> <LogOut className="h-4 w-4 mr-2" />
@@ -69,7 +69,7 @@ const Navbar = () => {
<Link to="/login"> <Link to="/login">
<Button <Button
variant="ghost" variant="ghost"
className="text-[#3D405B] hover:text-[#E07A5F] hover:bg-[#F2CC8F]/10" className="text-[#422268] hover:text-[#ff9e77] hover:bg-[#DDD8EB]/10"
data-testid="login-nav-button" data-testid="login-nav-button"
> >
Login Login
@@ -77,7 +77,7 @@ const Navbar = () => {
</Link> </Link>
<Link to="/register"> <Link to="/register">
<Button <Button
className="bg-[#E07A5F] text-white hover:bg-[#D0694E] rounded-full px-6" className="bg-[#DDD8EB] text-[#422268] hover:bg-white rounded-full px-6"
data-testid="register-nav-button" data-testid="register-nav-button"
> >
Join Us Join Us

View File

@@ -126,10 +126,10 @@ const PaymentActivationDialog = ({ open, onOpenChange, user, onSuccess }) => {
<Dialog open={open} onOpenChange={onOpenChange}> <Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent className="sm:max-w-[600px] bg-white rounded-2xl"> <DialogContent className="sm:max-w-[600px] bg-white rounded-2xl">
<DialogHeader> <DialogHeader>
<DialogTitle className="text-2xl font-semibold fraunces text-[#3D405B]"> <DialogTitle className="text-2xl font-semibold text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}>
Activate Manual Payment Activate Manual Payment
</DialogTitle> </DialogTitle>
<DialogDescription className="text-[#6B708D]"> <DialogDescription className="text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Record offline payment for {user.first_name} {user.last_name} ({user.email}) Record offline payment for {user.first_name} {user.last_name} ({user.email})
</DialogDescription> </DialogDescription>
</DialogHeader> </DialogHeader>
@@ -137,7 +137,7 @@ const PaymentActivationDialog = ({ open, onOpenChange, user, onSuccess }) => {
<form onSubmit={handleSubmit} className="space-y-6 py-4"> <form onSubmit={handleSubmit} className="space-y-6 py-4">
{/* Subscription Plan Selection */} {/* Subscription Plan Selection */}
<div className="space-y-2"> <div className="space-y-2">
<Label htmlFor="plan_id" className="text-[#3D405B] font-medium"> <Label htmlFor="plan_id" className="text-[#422268] font-medium" style={{ fontFamily: "'Inter', sans-serif" }}>
Subscription Plan Subscription Plan
</Label> </Label>
<Select <Select
@@ -152,7 +152,7 @@ const PaymentActivationDialog = ({ open, onOpenChange, user, onSuccess }) => {
}); });
}} }}
> >
<SelectTrigger className="rounded-xl border-2 border-[#EAE0D5]"> <SelectTrigger className="rounded-xl border-2 border-[#ddd8eb]">
<SelectValue placeholder="Select subscription plan" /> <SelectValue placeholder="Select subscription plan" />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
@@ -164,7 +164,7 @@ const PaymentActivationDialog = ({ open, onOpenChange, user, onSuccess }) => {
</SelectContent> </SelectContent>
</Select> </Select>
{selectedPlan && ( {selectedPlan && (
<p className="text-xs text-[#6B708D]"> <p className="text-xs text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{selectedPlan.description || `${selectedPlan.billing_cycle} subscription`} {selectedPlan.description || `${selectedPlan.billing_cycle} subscription`}
</p> </p>
)} )}
@@ -172,7 +172,7 @@ const PaymentActivationDialog = ({ open, onOpenChange, user, onSuccess }) => {
{/* Payment Amount */} {/* Payment Amount */}
<div className="space-y-2"> <div className="space-y-2">
<Label htmlFor="amount" className="text-[#3D405B] font-medium"> <Label htmlFor="amount" className="text-[#422268] font-medium" style={{ fontFamily: "'Inter', sans-serif" }}>
Payment Amount ($) Payment Amount ($)
</Label> </Label>
<Input <Input
@@ -183,27 +183,27 @@ const PaymentActivationDialog = ({ open, onOpenChange, user, onSuccess }) => {
placeholder="Enter amount" placeholder="Enter amount"
value={formData.amount} value={formData.amount}
onChange={(e) => setFormData({...formData, amount: e.target.value})} onChange={(e) => setFormData({...formData, amount: e.target.value})}
className="rounded-xl border-2 border-[#EAE0D5] focus:border-[#E07A5F]" className="rounded-xl border-2 border-[#ddd8eb] focus:border-[#664fa3]"
required required
/> />
<p className="text-xs text-[#6B708D]"> <p className="text-xs text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Amount can differ from plan price if offering a discount or partial payment Amount can differ from plan price if offering a discount or partial payment
</p> </p>
</div> </div>
{/* Payment Date */} {/* Payment Date */}
<div className="space-y-2"> <div className="space-y-2">
<Label htmlFor="payment_date" className="text-[#3D405B] font-medium"> <Label htmlFor="payment_date" className="text-[#422268] font-medium" style={{ fontFamily: "'Inter', sans-serif" }}>
Payment Date Payment Date
</Label> </Label>
<div className="relative"> <div className="relative">
<Calendar className="absolute left-4 top-1/2 transform -translate-y-1/2 h-5 w-5 text-[#6B708D]" /> <Calendar className="absolute left-4 top-1/2 transform -translate-y-1/2 h-5 w-5 text-[#664fa3]" />
<Input <Input
id="payment_date" id="payment_date"
type="date" type="date"
value={formData.payment_date} value={formData.payment_date}
onChange={(e) => setFormData({...formData, payment_date: e.target.value})} onChange={(e) => setFormData({...formData, payment_date: e.target.value})}
className="pl-12 rounded-xl border-2 border-[#EAE0D5] focus:border-[#E07A5F]" className="pl-12 rounded-xl border-2 border-[#ddd8eb] focus:border-[#664fa3]"
required required
/> />
</div> </div>
@@ -211,14 +211,14 @@ const PaymentActivationDialog = ({ open, onOpenChange, user, onSuccess }) => {
{/* Payment Method */} {/* Payment Method */}
<div className="space-y-2"> <div className="space-y-2">
<Label htmlFor="payment_method" className="text-[#3D405B] font-medium"> <Label htmlFor="payment_method" className="text-[#422268] font-medium" style={{ fontFamily: "'Inter', sans-serif" }}>
Payment Method Payment Method
</Label> </Label>
<Select <Select
value={formData.payment_method} value={formData.payment_method}
onValueChange={(value) => setFormData({...formData, payment_method: value})} onValueChange={(value) => setFormData({...formData, payment_method: value})}
> >
<SelectTrigger className="rounded-xl border-2 border-[#EAE0D5]"> <SelectTrigger className="rounded-xl border-2 border-[#ddd8eb]">
<SelectValue placeholder="Select payment method" /> <SelectValue placeholder="Select payment method" />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
@@ -232,7 +232,7 @@ const PaymentActivationDialog = ({ open, onOpenChange, user, onSuccess }) => {
{/* Subscription Period */} {/* Subscription Period */}
<div className="space-y-3"> <div className="space-y-3">
<Label className="text-[#3D405B] font-medium">Subscription Period</Label> <Label className="text-[#422268] font-medium" style={{ fontFamily: "'Inter', sans-serif" }}>Subscription Period</Label>
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<input <input
@@ -240,9 +240,9 @@ const PaymentActivationDialog = ({ open, onOpenChange, user, onSuccess }) => {
id="use_custom_period" id="use_custom_period"
checked={useCustomPeriod} checked={useCustomPeriod}
onChange={(e) => setUseCustomPeriod(e.target.checked)} onChange={(e) => setUseCustomPeriod(e.target.checked)}
className="rounded border-[#EAE0D5]" className="rounded border-[#ddd8eb]"
/> />
<Label htmlFor="use_custom_period" className="text-sm text-[#6B708D] font-normal cursor-pointer"> <Label htmlFor="use_custom_period" className="text-sm text-[#664fa3] font-normal cursor-pointer" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Use custom dates instead of plan's billing cycle Use custom dates instead of plan's billing cycle
</Label> </Label>
</div> </div>
@@ -250,7 +250,7 @@ const PaymentActivationDialog = ({ open, onOpenChange, user, onSuccess }) => {
{useCustomPeriod ? ( {useCustomPeriod ? (
<div className="grid grid-cols-2 gap-4"> <div className="grid grid-cols-2 gap-4">
<div className="space-y-2"> <div className="space-y-2">
<Label htmlFor="custom_period_start" className="text-sm text-[#3D405B]"> <Label htmlFor="custom_period_start" className="text-sm text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}>
Start Date Start Date
</Label> </Label>
<Input <Input
@@ -258,12 +258,12 @@ const PaymentActivationDialog = ({ open, onOpenChange, user, onSuccess }) => {
type="date" type="date"
value={formData.custom_period_start} value={formData.custom_period_start}
onChange={(e) => setFormData({...formData, custom_period_start: e.target.value})} onChange={(e) => setFormData({...formData, custom_period_start: e.target.value})}
className="rounded-xl border-2 border-[#EAE0D5] focus:border-[#E07A5F]" className="rounded-xl border-2 border-[#ddd8eb] focus:border-[#664fa3]"
required={useCustomPeriod} required={useCustomPeriod}
/> />
</div> </div>
<div className="space-y-2"> <div className="space-y-2">
<Label htmlFor="custom_period_end" className="text-sm text-[#3D405B]"> <Label htmlFor="custom_period_end" className="text-sm text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}>
End Date End Date
</Label> </Label>
<Input <Input
@@ -271,14 +271,14 @@ const PaymentActivationDialog = ({ open, onOpenChange, user, onSuccess }) => {
type="date" type="date"
value={formData.custom_period_end} value={formData.custom_period_end}
onChange={(e) => setFormData({...formData, custom_period_end: e.target.value})} onChange={(e) => setFormData({...formData, custom_period_end: e.target.value})}
className="rounded-xl border-2 border-[#EAE0D5] focus:border-[#E07A5F]" className="rounded-xl border-2 border-[#ddd8eb] focus:border-[#664fa3]"
required={useCustomPeriod} required={useCustomPeriod}
/> />
</div> </div>
</div> </div>
) : ( ) : (
selectedPlan && ( selectedPlan && (
<p className="text-sm text-[#6B708D] bg-[#F2CC8F]/10 p-3 rounded-lg"> <p className="text-sm text-[#664fa3] bg-[#f1eef9] p-3 rounded-lg" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Will use plan's billing cycle: <span className="font-medium">{selectedPlan.billing_cycle}</span> Will use plan's billing cycle: <span className="font-medium">{selectedPlan.billing_cycle}</span>
<br /> <br />
Starts today, ends {selectedPlan.billing_cycle === 'monthly' ? '30 days' : Starts today, ends {selectedPlan.billing_cycle === 'monthly' ? '30 days' :
@@ -292,7 +292,7 @@ const PaymentActivationDialog = ({ open, onOpenChange, user, onSuccess }) => {
{/* Notes */} {/* Notes */}
<div className="space-y-2"> <div className="space-y-2">
<Label htmlFor="notes" className="text-[#3D405B] font-medium"> <Label htmlFor="notes" className="text-[#422268] font-medium" style={{ fontFamily: "'Inter', sans-serif" }}>
Notes (Optional) Notes (Optional)
</Label> </Label>
<Textarea <Textarea
@@ -300,7 +300,7 @@ const PaymentActivationDialog = ({ open, onOpenChange, user, onSuccess }) => {
placeholder="Additional notes about the payment..." placeholder="Additional notes about the payment..."
value={formData.notes} value={formData.notes}
onChange={(e) => setFormData({...formData, notes: e.target.value})} onChange={(e) => setFormData({...formData, notes: e.target.value})}
className="rounded-xl border-2 border-[#EAE0D5] focus:border-[#E07A5F] min-h-[100px]" className="rounded-xl border-2 border-[#ddd8eb] focus:border-[#664fa3] min-h-[100px]"
/> />
</div> </div>
@@ -309,7 +309,7 @@ const PaymentActivationDialog = ({ open, onOpenChange, user, onSuccess }) => {
type="button" type="button"
variant="outline" variant="outline"
onClick={() => onOpenChange(false)} onClick={() => onOpenChange(false)}
className="rounded-full border-2 border-[#EAE0D5]" className="rounded-full border-2 border-[#ddd8eb]"
> >
Cancel Cancel
</Button> </Button>

View File

@@ -94,10 +94,10 @@ const PlanDialog = ({ open, onOpenChange, plan, onSuccess }) => {
<Dialog open={open} onOpenChange={onOpenChange}> <Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent className="sm:max-w-[600px]"> <DialogContent className="sm:max-w-[600px]">
<DialogHeader> <DialogHeader>
<DialogTitle className="fraunces text-2xl"> <DialogTitle className="text-2xl text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}>
{plan ? 'Edit Plan' : 'Create New Plan'} {plan ? 'Edit Plan' : 'Create New Plan'}
</DialogTitle> </DialogTitle>
<DialogDescription> <DialogDescription className="text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{plan ? 'Update plan details below' : 'Enter plan details to create a new subscription plan'} {plan ? 'Update plan details below' : 'Enter plan details to create a new subscription plan'}
</DialogDescription> </DialogDescription>
</DialogHeader> </DialogHeader>
@@ -175,7 +175,7 @@ const PlanDialog = ({ open, onOpenChange, plan, onSuccess }) => {
placeholder="price_xxxxxxxxxxxxx" placeholder="price_xxxxxxxxxxxxx"
className="mt-2 font-mono text-sm" className="mt-2 font-mono text-sm"
/> />
<p className="text-sm text-[#6B708D] mt-1"> <p className="text-sm text-[#664fa3] mt-1" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Optional. Leave empty for manual/test plans. Optional. Leave empty for manual/test plans.
</p> </p>
</div> </div>
@@ -184,7 +184,7 @@ const PlanDialog = ({ open, onOpenChange, plan, onSuccess }) => {
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<div> <div>
<Label htmlFor="active">Active Status</Label> <Label htmlFor="active">Active Status</Label>
<p className="text-sm text-[#6B708D]"> <p className="text-sm text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Inactive plans won't appear for new subscriptions Inactive plans won't appear for new subscriptions
</p> </p>
</div> </div>
@@ -196,7 +196,7 @@ const PlanDialog = ({ open, onOpenChange, plan, onSuccess }) => {
onChange={(e) => setFormData({ ...formData, active: e.target.checked })} onChange={(e) => setFormData({ ...formData, active: e.target.checked })}
className="sr-only peer" className="sr-only peer"
/> />
<div className="w-11 h-6 bg-gray-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-[#E07A5F]/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-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-[#81B29A]"></div> <div className="w-11 h-6 bg-gray-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-[#664fa3]/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-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-[#81B29A]"></div>
</label> </label>
</div> </div>
@@ -212,7 +212,7 @@ const PlanDialog = ({ open, onOpenChange, plan, onSuccess }) => {
<Button <Button
type="submit" type="submit"
disabled={loading} disabled={loading}
className="bg-[#E07A5F] hover:bg-[#D0694E]" className="bg-[#DDD8EB] text-[#422268] hover:bg-white"
> >
{loading ? ( {loading ? (
<> <>

View File

@@ -26,7 +26,7 @@ const RegistrationStep1 = ({ formData, setFormData, handleInputChange }) => {
<div className="space-y-8"> <div className="space-y-8">
{/* Personal Information */} {/* Personal Information */}
<div className="space-y-4"> <div className="space-y-4">
<h2 className="text-2xl font-semibold fraunces text-[#3D405B]"> <h2 className="text-2xl font-semibold text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}>
Personal Information Personal Information
</h2> </h2>
@@ -40,7 +40,7 @@ const RegistrationStep1 = ({ formData, setFormData, handleInputChange }) => {
required required
value={formData.first_name} value={formData.first_name}
onChange={handleInputChange} onChange={handleInputChange}
className="h-14 rounded-xl border-2 border-[#EAE0D5] focus:border-[#E07A5F]" className="h-14 rounded-xl border-2 border-[#ddd8eb] focus:border-[#664fa3]"
data-testid="first-name-input" data-testid="first-name-input"
/> />
</div> </div>
@@ -52,7 +52,7 @@ const RegistrationStep1 = ({ formData, setFormData, handleInputChange }) => {
required required
value={formData.last_name} value={formData.last_name}
onChange={handleInputChange} onChange={handleInputChange}
className="h-14 rounded-xl border-2 border-[#EAE0D5] focus:border-[#E07A5F]" className="h-14 rounded-xl border-2 border-[#ddd8eb] focus:border-[#664fa3]"
data-testid="last-name-input" data-testid="last-name-input"
/> />
</div> </div>
@@ -69,7 +69,7 @@ const RegistrationStep1 = ({ formData, setFormData, handleInputChange }) => {
required required
value={formData.phone} value={formData.phone}
onChange={handleInputChange} onChange={handleInputChange}
className="h-14 rounded-xl border-2 border-[#EAE0D5] focus:border-[#E07A5F]" className="h-14 rounded-xl border-2 border-[#ddd8eb] focus:border-[#664fa3]"
data-testid="phone-input" data-testid="phone-input"
/> />
</div> </div>
@@ -82,7 +82,7 @@ const RegistrationStep1 = ({ formData, setFormData, handleInputChange }) => {
required required
value={formData.date_of_birth} value={formData.date_of_birth}
onChange={handleInputChange} onChange={handleInputChange}
className="h-14 rounded-xl border-2 border-[#EAE0D5] focus:border-[#E07A5F]" className="h-14 rounded-xl border-2 border-[#ddd8eb] focus:border-[#664fa3]"
data-testid="dob-input" data-testid="dob-input"
/> />
</div> </div>
@@ -112,7 +112,7 @@ const RegistrationStep1 = ({ formData, setFormData, handleInputChange }) => {
required required
value={formData.city} value={formData.city}
onChange={handleInputChange} onChange={handleInputChange}
className="h-14 rounded-xl border-2 border-[#EAE0D5] focus:border-[#E07A5F]" className="h-14 rounded-xl border-2 border-[#ddd8eb] focus:border-[#664fa3]"
data-testid="city-input" data-testid="city-input"
/> />
</div> </div>
@@ -124,7 +124,7 @@ const RegistrationStep1 = ({ formData, setFormData, handleInputChange }) => {
required required
value={formData.state} value={formData.state}
onChange={handleInputChange} onChange={handleInputChange}
className="h-14 rounded-xl border-2 border-[#EAE0D5] focus:border-[#E07A5F]" className="h-14 rounded-xl border-2 border-[#ddd8eb] focus:border-[#664fa3]"
data-testid="state-input" data-testid="state-input"
/> />
</div> </div>
@@ -136,7 +136,7 @@ const RegistrationStep1 = ({ formData, setFormData, handleInputChange }) => {
required required
value={formData.zipcode} value={formData.zipcode}
onChange={handleInputChange} onChange={handleInputChange}
className="h-14 rounded-xl border-2 border-[#EAE0D5] focus:border-[#E07A5F]" className="h-14 rounded-xl border-2 border-[#ddd8eb] focus:border-[#664fa3]"
data-testid="zipcode-input" data-testid="zipcode-input"
/> />
</div> </div>
@@ -145,7 +145,7 @@ const RegistrationStep1 = ({ formData, setFormData, handleInputChange }) => {
{/* How Did You Hear About Us */} {/* How Did You Hear About Us */}
<div className="space-y-4"> <div className="space-y-4">
<h2 className="text-2xl font-semibold fraunces text-[#3D405B]"> <h2 className="text-2xl font-semibold text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}>
How Did You Hear About Us? * How Did You Hear About Us? *
</h2> </h2>
<div className="space-y-3"> <div className="space-y-3">
@@ -167,7 +167,7 @@ const RegistrationStep1 = ({ formData, setFormData, handleInputChange }) => {
{/* Partner Information */} {/* Partner Information */}
<div className="space-y-4"> <div className="space-y-4">
<h2 className="text-2xl font-semibold fraunces text-[#3D405B]"> <h2 className="text-2xl font-semibold text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}>
Partner Information (Optional) Partner Information (Optional)
</h2> </h2>
@@ -179,7 +179,7 @@ const RegistrationStep1 = ({ formData, setFormData, handleInputChange }) => {
name="partner_first_name" name="partner_first_name"
value={formData.partner_first_name} value={formData.partner_first_name}
onChange={handleInputChange} onChange={handleInputChange}
className="h-14 rounded-xl border-2 border-[#EAE0D5] focus:border-[#E07A5F]" className="h-14 rounded-xl border-2 border-[#ddd8eb] focus:border-[#664fa3]"
data-testid="partner-first-name-input" data-testid="partner-first-name-input"
/> />
</div> </div>
@@ -190,7 +190,7 @@ const RegistrationStep1 = ({ formData, setFormData, handleInputChange }) => {
name="partner_last_name" name="partner_last_name"
value={formData.partner_last_name} value={formData.partner_last_name}
onChange={handleInputChange} onChange={handleInputChange}
className="h-14 rounded-xl border-2 border-[#EAE0D5] focus:border-[#E07A5F]" className="h-14 rounded-xl border-2 border-[#ddd8eb] focus:border-[#664fa3]"
data-testid="partner-last-name-input" data-testid="partner-last-name-input"
/> />
</div> </div>

View File

@@ -33,10 +33,10 @@ const RegistrationStep2 = ({ formData, setFormData, handleInputChange }) => {
<div className="space-y-8"> <div className="space-y-8">
{/* Newsletter Publication Preferences */} {/* Newsletter Publication Preferences */}
<div className="space-y-4"> <div className="space-y-4">
<h2 className="text-2xl font-semibold fraunces text-[#3D405B]"> <h2 className="text-2xl font-semibold text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}>
Newsletter Publication Preferences * Newsletter Publication Preferences *
</h2> </h2>
<p className="text-[#6B708D]"> <p className="text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Please check what information may be published in LOAF Newsletter Please check what information may be published in LOAF Newsletter
</p> </p>
@@ -97,7 +97,7 @@ const RegistrationStep2 = ({ formData, setFormData, handleInputChange }) => {
{/* Referral */} {/* Referral */}
<div className="space-y-4"> <div className="space-y-4">
<h2 className="text-2xl font-semibold fraunces text-[#3D405B]"> <h2 className="text-2xl font-semibold text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}>
Referral Referral
</h2> </h2>
<div> <div>
@@ -110,10 +110,10 @@ const RegistrationStep2 = ({ formData, setFormData, handleInputChange }) => {
value={formData.referred_by_member_name} value={formData.referred_by_member_name}
onChange={handleInputChange} onChange={handleInputChange}
placeholder="Enter member name or email" placeholder="Enter member name or email"
className="h-14 rounded-xl border-2 border-[#EAE0D5] focus:border-[#E07A5F]" className="h-14 rounded-xl border-2 border-[#ddd8eb] focus:border-[#664fa3]"
data-testid="referral-input" data-testid="referral-input"
/> />
<p className="text-sm text-[#6B708D] mt-2"> <p className="text-sm text-[#664fa3] mt-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
If referred by a current member, you may skip the event attendance requirement. If referred by a current member, you may skip the event attendance requirement.
</p> </p>
</div> </div>
@@ -121,10 +121,10 @@ const RegistrationStep2 = ({ formData, setFormData, handleInputChange }) => {
{/* Volunteer Interests */} {/* Volunteer Interests */}
<div className="space-y-4"> <div className="space-y-4">
<h2 className="text-2xl font-semibold fraunces text-[#3D405B]"> <h2 className="text-2xl font-semibold text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}>
Volunteer Interests (Optional) Volunteer Interests (Optional)
</h2> </h2>
<p className="text-[#6B708D]"> <p className="text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
I may at some time be interested in volunteering with LOAF in the following ways (training is provided) I may at some time be interested in volunteering with LOAF in the following ways (training is provided)
</p> </p>
@@ -158,7 +158,7 @@ const RegistrationStep2 = ({ formData, setFormData, handleInputChange }) => {
I am requesting for scholarship I am requesting for scholarship
</Label> </Label>
</div> </div>
<p className="text-sm text-[#6B708D]"> <p className="text-sm text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Scholarship information is kept confidential Scholarship information is kept confidential
</p> </p>
@@ -174,7 +174,7 @@ const RegistrationStep2 = ({ formData, setFormData, handleInputChange }) => {
onChange={handleInputChange} onChange={handleInputChange}
placeholder="Tell us why you're requesting a scholarship..." placeholder="Tell us why you're requesting a scholarship..."
rows={4} rows={4}
className="rounded-xl border-2 border-[#EAE0D5] focus:border-[#E07A5F]" className="rounded-xl border-2 border-[#ddd8eb] focus:border-[#664fa3]"
/> />
</div> </div>
)} )}

View File

@@ -23,11 +23,11 @@ const RegistrationStep3 = ({ formData, setFormData, handleInputChange }) => {
return ( return (
<div className="space-y-6"> <div className="space-y-6">
<div className="space-y-4"> <div className="space-y-4">
<h2 className="text-2xl font-semibold fraunces text-[#3D405B]"> <h2 className="text-2xl font-semibold text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}>
Members Directory Members Directory
</h2> </h2>
<p className="text-[#6B708D]"> <p className="text-[#664fa3]" 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) Would you like to be displayed on our private members directory? (optional and you can change the answer later)
</p> </p>
@@ -37,8 +37,8 @@ const RegistrationStep3 = ({ formData, setFormData, handleInputChange }) => {
className={` className={`
p-4 rounded-xl border-2 cursor-pointer transition-all p-4 rounded-xl border-2 cursor-pointer transition-all
${formData.show_in_directory ${formData.show_in_directory
? 'border-[#E07A5F] bg-[#E07A5F]/5' ? 'border-[#ff9e77] bg-[#ff9e77]/5'
: 'border-[#EAE0D5] hover:border-[#6B708D]' : 'border-[#ddd8eb] hover:border-[#664fa3]'
} }
`} `}
onClick={() => setFormData(prev => ({ ...prev, show_in_directory: true }))} onClick={() => setFormData(prev => ({ ...prev, show_in_directory: true }))}
@@ -46,13 +46,13 @@ const RegistrationStep3 = ({ formData, setFormData, handleInputChange }) => {
<div className="flex items-center space-x-3"> <div className="flex items-center space-x-3">
<div className={` <div className={`
w-5 h-5 rounded-full border-2 flex items-center justify-center w-5 h-5 rounded-full border-2 flex items-center justify-center
${formData.show_in_directory ? 'border-[#E07A5F]' : 'border-[#EAE0D5]'} ${formData.show_in_directory ? 'border-[#ff9e77]' : 'border-[#ddd8eb]'}
`}> `}>
{formData.show_in_directory && ( {formData.show_in_directory && (
<div className="w-3 h-3 rounded-full bg-[#E07A5F]" /> <div className="w-3 h-3 rounded-full bg-[#ff9e77]" />
)} )}
</div> </div>
<span className="font-medium text-[#3D405B]"> <span className="font-medium text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}>
Yes, include me in the Members Directory Yes, include me in the Members Directory
</span> </span>
</div> </div>
@@ -62,8 +62,8 @@ const RegistrationStep3 = ({ formData, setFormData, handleInputChange }) => {
className={` className={`
p-4 rounded-xl border-2 cursor-pointer transition-all p-4 rounded-xl border-2 cursor-pointer transition-all
${!formData.show_in_directory ${!formData.show_in_directory
? 'border-[#E07A5F] bg-[#E07A5F]/5' ? 'border-[#ff9e77] bg-[#ff9e77]/5'
: 'border-[#EAE0D5] hover:border-[#6B708D]' : 'border-[#ddd8eb] hover:border-[#664fa3]'
} }
`} `}
onClick={() => setFormData(prev => ({ ...prev, show_in_directory: false }))} onClick={() => setFormData(prev => ({ ...prev, show_in_directory: false }))}
@@ -71,13 +71,13 @@ const RegistrationStep3 = ({ formData, setFormData, handleInputChange }) => {
<div className="flex items-center space-x-3"> <div className="flex items-center space-x-3">
<div className={` <div className={`
w-5 h-5 rounded-full border-2 flex items-center justify-center w-5 h-5 rounded-full border-2 flex items-center justify-center
${!formData.show_in_directory ? 'border-[#E07A5F]' : 'border-[#EAE0D5]'} ${!formData.show_in_directory ? 'border-[#ff9e77]' : 'border-[#ddd8eb]'}
`}> `}>
{!formData.show_in_directory && ( {!formData.show_in_directory && (
<div className="w-3 h-3 rounded-full bg-[#E07A5F]" /> <div className="w-3 h-3 rounded-full bg-[#ff9e77]" />
)} )}
</div> </div>
<span className="font-medium text-[#3D405B]"> <span className="font-medium text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}>
No, don't include me in the Members Directory No, don't include me in the Members Directory
</span> </span>
</div> </div>
@@ -87,8 +87,8 @@ const RegistrationStep3 = ({ formData, setFormData, handleInputChange }) => {
{/* Conditional Directory Fields */} {/* Conditional Directory Fields */}
{formData.show_in_directory && ( {formData.show_in_directory && (
<div className="space-y-4 mt-6 p-6 bg-[#FDFCF8] rounded-xl border border-[#EAE0D5]"> <div className="space-y-4 mt-6 p-6 bg-white rounded-xl border border-[#ddd8eb]">
<p className="text-[#6B708D] text-sm"> <p className="text-[#664fa3] text-sm" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Below, choose what information you would like include in the Members Only Directory. 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) (If you ever want to update this information, remember the Directory Section and Account Section are separate)
</p> </p>
@@ -101,7 +101,7 @@ const RegistrationStep3 = ({ formData, setFormData, handleInputChange }) => {
type="email" type="email"
value={formData.directory_email} value={formData.directory_email}
onChange={handleInputChange} onChange={handleInputChange}
className="h-14 rounded-xl border-2 border-[#EAE0D5] focus:border-[#E07A5F]" className="h-14 rounded-xl border-2 border-[#ddd8eb] focus:border-[#664fa3]"
/> />
</div> </div>
@@ -114,7 +114,7 @@ const RegistrationStep3 = ({ formData, setFormData, handleInputChange }) => {
onChange={handleInputChange} onChange={handleInputChange}
placeholder="Tell other members about yourself..." placeholder="Tell other members about yourself..."
rows={4} rows={4}
className="rounded-xl border-2 border-[#EAE0D5] focus:border-[#E07A5F]" className="rounded-xl border-2 border-[#ddd8eb] focus:border-[#664fa3]"
/> />
</div> </div>
@@ -125,7 +125,7 @@ const RegistrationStep3 = ({ formData, setFormData, handleInputChange }) => {
name="directory_address" name="directory_address"
value={formData.directory_address} value={formData.directory_address}
onChange={handleInputChange} onChange={handleInputChange}
className="h-14 rounded-xl border-2 border-[#EAE0D5] focus:border-[#E07A5F]" className="h-14 rounded-xl border-2 border-[#ddd8eb] focus:border-[#664fa3]"
/> />
</div> </div>
@@ -137,7 +137,7 @@ const RegistrationStep3 = ({ formData, setFormData, handleInputChange }) => {
type="tel" type="tel"
value={formData.directory_phone} value={formData.directory_phone}
onChange={handleInputChange} onChange={handleInputChange}
className="h-14 rounded-xl border-2 border-[#EAE0D5] focus:border-[#E07A5F]" className="h-14 rounded-xl border-2 border-[#ddd8eb] focus:border-[#664fa3]"
/> />
</div> </div>
@@ -149,7 +149,7 @@ const RegistrationStep3 = ({ formData, setFormData, handleInputChange }) => {
type="date" type="date"
value={formData.directory_dob} value={formData.directory_dob}
onChange={handleInputChange} onChange={handleInputChange}
className="h-14 rounded-xl border-2 border-[#EAE0D5] focus:border-[#E07A5F]" className="h-14 rounded-xl border-2 border-[#ddd8eb] focus:border-[#664fa3]"
/> />
</div> </div>
@@ -162,7 +162,7 @@ const RegistrationStep3 = ({ formData, setFormData, handleInputChange }) => {
name="directory_partner_name" name="directory_partner_name"
value={formData.directory_partner_name} value={formData.directory_partner_name}
onChange={handleInputChange} onChange={handleInputChange}
className="h-14 rounded-xl border-2 border-[#EAE0D5] focus:border-[#E07A5F]" className="h-14 rounded-xl border-2 border-[#ddd8eb] focus:border-[#664fa3]"
/> />
</div> </div>
</div> </div>

View File

@@ -7,11 +7,11 @@ const RegistrationStep4 = ({ formData, handleInputChange }) => {
return ( return (
<div className="space-y-6"> <div className="space-y-6">
<div className="space-y-4"> <div className="space-y-4">
<h2 className="text-2xl font-semibold fraunces text-[#3D405B]"> <h2 className="text-2xl font-semibold text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}>
Account Credentials Account Credentials
</h2> </h2>
<p className="text-[#6B708D]"> <p className="text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Your email is also your username that you can use to login. Your email is also your username that you can use to login.
Please note you can only login after your application is approved. Please note you can only login after your application is approved.
</p> </p>
@@ -28,7 +28,7 @@ const RegistrationStep4 = ({ formData, handleInputChange }) => {
value={formData.email} value={formData.email}
onChange={handleInputChange} onChange={handleInputChange}
placeholder="your.email@example.com" placeholder="your.email@example.com"
className="h-14 rounded-xl border-2 border-[#EAE0D5] focus:border-[#E07A5F]" className="h-14 rounded-xl border-2 border-[#ddd8eb] focus:border-[#664fa3]"
data-testid="email-input" data-testid="email-input"
/> />
</div> </div>
@@ -43,10 +43,10 @@ const RegistrationStep4 = ({ formData, handleInputChange }) => {
value={formData.password} value={formData.password}
onChange={handleInputChange} onChange={handleInputChange}
placeholder="At least 6 characters" placeholder="At least 6 characters"
className="h-14 rounded-xl border-2 border-[#EAE0D5] focus:border-[#E07A5F]" className="h-14 rounded-xl border-2 border-[#ddd8eb] focus:border-[#664fa3]"
data-testid="password-input" data-testid="password-input"
/> />
<p className="text-sm text-[#6B708D] mt-2"> <p className="text-sm text-[#664fa3] mt-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Must be at least 6 characters long Must be at least 6 characters long
</p> </p>
</div> </div>
@@ -60,7 +60,7 @@ const RegistrationStep4 = ({ formData, handleInputChange }) => {
value={formData.confirmPassword} value={formData.confirmPassword}
onChange={handleInputChange} onChange={handleInputChange}
placeholder="Re-enter your password" placeholder="Re-enter your password"
className="h-14 rounded-xl border-2 border-[#EAE0D5] focus:border-[#E07A5F]" className="h-14 rounded-xl border-2 border-[#ddd8eb] focus:border-[#664fa3]"
data-testid="confirm-password-input" data-testid="confirm-password-input"
/> />
{formData.confirmPassword && formData.password !== formData.confirmPassword && ( {formData.confirmPassword && formData.password !== formData.confirmPassword && (

View File

@@ -20,25 +20,25 @@ const RegistrationStepIndicator = ({ currentStep, totalSteps = 4 }) => {
w-12 h-12 rounded-full flex items-center justify-center font-semibold text-lg w-12 h-12 rounded-full flex items-center justify-center font-semibold text-lg
transition-all duration-300 transition-all duration-300
${currentStep === step.number ${currentStep === step.number
? 'bg-[#E07A5F] text-white scale-110 shadow-lg' ? 'bg-[#ff9e77] text-white scale-110 shadow-lg'
: currentStep > step.number : currentStep > step.number
? 'bg-[#81B29A] text-white' ? 'bg-[#81B29A] text-white'
: 'bg-[#EAE0D5] text-[#6B708D]' : 'bg-[#ddd8eb] text-[#664fa3]'
} }
`}> `}>
{currentStep > step.number ? '✓' : step.number} {currentStep > step.number ? '✓' : step.number}
</div> </div>
<span className={` <span className={`
text-sm mt-2 font-medium transition-colors text-sm mt-2 font-medium transition-colors
${currentStep === step.number ? 'text-[#E07A5F]' : 'text-[#6B708D]'} ${currentStep === step.number ? 'text-[#ff9e77]' : 'text-[#664fa3]'}
`}> `} style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{step.title} {step.title}
</span> </span>
</div> </div>
{/* Connecting Line */} {/* Connecting Line */}
{index < steps.length - 1 && ( {index < steps.length - 1 && (
<div className="flex-1 h-1 mx-2 relative -top-6 bg-[#EAE0D5]"> <div className="flex-1 h-1 mx-2 relative -top-6 bg-[#ddd8eb]">
<div <div
className={` className={`
h-full transition-all duration-500 h-full transition-all duration-500
@@ -52,8 +52,8 @@ const RegistrationStepIndicator = ({ currentStep, totalSteps = 4 }) => {
</div> </div>
{/* Step Counter */} {/* Step Counter */}
<p className="text-center text-[#6B708D] mt-6 text-lg"> <p className="text-center text-[#664fa3] mt-6 text-lg" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Step <span className="font-semibold text-[#E07A5F]">{currentStep}</span> of {totalSteps} Step <span className="font-semibold text-[#ff9e77]">{currentStep}</span> of {totalSteps}
</p> </p>
</div> </div>
); );

View File

@@ -43,7 +43,7 @@ const AdminLayout = ({ children }) => {
}; };
return ( return (
<div className="flex h-screen bg-[#FDFCF8]"> <div className="flex h-screen bg-white">
{/* Sidebar */} {/* Sidebar */}
<AdminSidebar <AdminSidebar
isOpen={sidebarOpen} isOpen={sidebarOpen}

View File

@@ -46,10 +46,10 @@ const EventDetails = () => {
if (loading) { if (loading) {
return ( return (
<div className="min-h-screen bg-[#FDFCF8]"> <div className="min-h-screen bg-white">
<Navbar /> <Navbar />
<div className="flex items-center justify-center min-h-[60vh]"> <div className="flex items-center justify-center min-h-[60vh]">
<p className="text-[#6B708D]">Loading event...</p> <p className="text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Loading event...</p>
</div> </div>
</div> </div>
); );
@@ -60,24 +60,24 @@ const EventDetails = () => {
} }
return ( return (
<div className="min-h-screen bg-[#FDFCF8]"> <div className="min-h-screen bg-white">
<Navbar /> <Navbar />
<div className="max-w-4xl mx-auto px-6 py-12"> <div className="max-w-4xl mx-auto px-6 py-12">
<button <button
onClick={() => navigate('/events')} onClick={() => navigate('/events')}
className="inline-flex items-center text-[#6B708D] hover:text-[#E07A5F] transition-colors mb-8" className="inline-flex items-center text-[#664fa3] hover:text-[#ff9e77] transition-colors mb-8"
data-testid="back-to-events-button" data-testid="back-to-events-button"
> >
<ArrowLeft className="h-4 w-4 mr-2" /> <ArrowLeft className="h-4 w-4 mr-2" />
Back to Events Back to Events
</button> </button>
<Card className="p-8 md:p-12 bg-white rounded-2xl border border-[#EAE0D5] shadow-lg"> <Card className="p-8 md:p-12 bg-white rounded-2xl border border-[#ddd8eb] shadow-lg">
<div className="mb-8"> <div className="mb-8">
<div className="flex items-center gap-4 mb-6"> <div className="flex items-center gap-4 mb-6">
<div className="bg-[#F2CC8F]/20 p-4 rounded-xl"> <div className="bg-[#DDD8EB]/20 p-4 rounded-xl">
<Calendar className="h-10 w-10 text-[#E07A5F]" /> <Calendar className="h-10 w-10 text-[#664fa3]" />
</div> </div>
{event.user_rsvp_status && ( {event.user_rsvp_status && (
<Badge <Badge
@@ -85,8 +85,8 @@ const EventDetails = () => {
event.user_rsvp_status === 'yes' event.user_rsvp_status === 'yes'
? 'bg-[#81B29A] text-white' ? 'bg-[#81B29A] text-white'
: event.user_rsvp_status === 'no' : event.user_rsvp_status === 'no'
? 'bg-[#6B708D] text-white' ? 'bg-gray-400 text-white'
: 'bg-[#F2CC8F] text-[#3D405B]' : 'bg-orange-100 text-orange-700'
}`} }`}
> >
{event.user_rsvp_status === 'yes' && 'Going'} {event.user_rsvp_status === 'yes' && 'Going'}
@@ -96,14 +96,14 @@ const EventDetails = () => {
)} )}
</div> </div>
<h1 className="text-4xl md:text-5xl font-semibold fraunces text-[#3D405B] mb-6"> <h1 className="text-4xl md:text-5xl font-semibold text-[#422268] mb-6" style={{ fontFamily: "'Inter', sans-serif" }}>
{event.title} {event.title}
</h1> </h1>
<div className="space-y-4 text-lg"> <div className="space-y-4 text-lg">
<div className="flex items-center gap-3 text-[#6B708D]"> <div className="flex items-center gap-3 text-[#664fa3]">
<Calendar className="h-5 w-5" /> <Calendar className="h-5 w-5" />
<span> <span style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{new Date(event.start_at).toLocaleDateString('en-US', { {new Date(event.start_at).toLocaleDateString('en-US', {
weekday: 'long', weekday: 'long',
year: 'numeric', year: 'numeric',
@@ -112,20 +112,20 @@ const EventDetails = () => {
})} })}
</span> </span>
</div> </div>
<div className="flex items-center gap-3 text-[#6B708D]"> <div className="flex items-center gap-3 text-[#664fa3]">
<Calendar className="h-5 w-5" /> <Calendar className="h-5 w-5" />
<span> <span style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{new Date(event.start_at).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })} -{' '} {new Date(event.start_at).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })} -{' '}
{new Date(event.end_at).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })} {new Date(event.end_at).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}
</span> </span>
</div> </div>
<div className="flex items-center gap-3 text-[#6B708D]"> <div className="flex items-center gap-3 text-[#664fa3]">
<MapPin className="h-5 w-5" /> <MapPin className="h-5 w-5" />
<span>{event.location}</span> <span style={{ fontFamily: "'Nunito Sans', sans-serif" }}>{event.location}</span>
</div> </div>
<div className="flex items-center gap-3 text-[#6B708D]"> <div className="flex items-center gap-3 text-[#664fa3]">
<Users className="h-5 w-5" /> <Users className="h-5 w-5" />
<span> <span style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{event.rsvp_count || 0} {event.rsvp_count === 1 ? 'person' : 'people'} attending {event.rsvp_count || 0} {event.rsvp_count === 1 ? 'person' : 'people'} attending
{event.capacity && ` (Capacity: ${event.capacity})`} {event.capacity && ` (Capacity: ${event.capacity})`}
</span> </span>
@@ -134,18 +134,18 @@ const EventDetails = () => {
</div> </div>
{event.description && ( {event.description && (
<div className="mb-8 pb-8 border-b border-[#EAE0D5]"> <div className="mb-8 pb-8 border-b border-[#ddd8eb]">
<h2 className="text-2xl font-semibold fraunces text-[#3D405B] mb-4"> <h2 className="text-2xl font-semibold text-[#422268] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}>
About This Event About This Event
</h2> </h2>
<p className="text-[#6B708D] leading-relaxed whitespace-pre-line"> <p className="text-[#664fa3] leading-relaxed whitespace-pre-line" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{event.description} {event.description}
</p> </p>
</div> </div>
)} )}
<div> <div>
<h2 className="text-2xl font-semibold fraunces text-[#3D405B] mb-6"> <h2 className="text-2xl font-semibold text-[#422268] mb-6" style={{ fontFamily: "'Inter', sans-serif" }}>
RSVP to This Event RSVP to This Event
</h2> </h2>
<div className="flex gap-4 flex-wrap"> <div className="flex gap-4 flex-wrap">
@@ -155,7 +155,7 @@ const EventDetails = () => {
className={`rounded-full px-8 py-6 flex items-center gap-2 ${ className={`rounded-full px-8 py-6 flex items-center gap-2 ${
event.user_rsvp_status === 'yes' event.user_rsvp_status === 'yes'
? 'bg-[#81B29A] text-white' ? 'bg-[#81B29A] text-white'
: 'bg-[#E07A5F] text-white hover:bg-[#D0694E]' : 'bg-[#DDD8EB] text-[#422268] hover:bg-white'
}`} }`}
data-testid="rsvp-yes-button" data-testid="rsvp-yes-button"
> >
@@ -168,8 +168,8 @@ const EventDetails = () => {
variant="outline" variant="outline"
className={`rounded-full px-8 py-6 flex items-center gap-2 border-2 ${ className={`rounded-full px-8 py-6 flex items-center gap-2 border-2 ${
event.user_rsvp_status === 'maybe' event.user_rsvp_status === 'maybe'
? 'border-[#F2CC8F] bg-[#F2CC8F]/20 text-[#3D405B]' ? 'border-orange-400 bg-orange-100 text-orange-700'
: 'border-[#3D405B] text-[#3D405B] hover:bg-[#F2CC8F]/10' : 'border-[#664fa3] text-[#664fa3] hover:bg-[#f1eef9]'
}`} }`}
data-testid="rsvp-maybe-button" data-testid="rsvp-maybe-button"
> >
@@ -182,8 +182,8 @@ const EventDetails = () => {
variant="outline" variant="outline"
className={`rounded-full px-8 py-6 flex items-center gap-2 border-2 ${ className={`rounded-full px-8 py-6 flex items-center gap-2 border-2 ${
event.user_rsvp_status === 'no' event.user_rsvp_status === 'no'
? 'border-[#6B708D] bg-[#6B708D]/20 text-[#3D405B]' ? 'border-gray-400 bg-gray-100 text-gray-700'
: 'border-[#6B708D] text-[#6B708D] hover:bg-[#6B708D]/10' : 'border-gray-400 text-gray-600 hover:bg-gray-50'
}`} }`}
data-testid="rsvp-no-button" data-testid="rsvp-no-button"
> >

View File

@@ -9,61 +9,61 @@ const PaymentCancel = () => {
const navigate = useNavigate(); const navigate = useNavigate();
return ( return (
<div className="min-h-screen bg-[#FDFCF8]"> <div className="min-h-screen bg-white">
<Navbar /> <Navbar />
<div className="max-w-4xl mx-auto px-6 py-12"> <div className="max-w-4xl mx-auto px-6 py-12">
<div className="text-center mb-12"> <div className="text-center mb-12">
{/* Cancel Icon */} {/* Cancel Icon */}
<div className="mb-8"> <div className="mb-8">
<div className="bg-[#6B708D] rounded-full w-24 h-24 mx-auto flex items-center justify-center"> <div className="bg-gray-400 rounded-full w-24 h-24 mx-auto flex items-center justify-center">
<XCircle className="h-12 w-12 text-white" /> <XCircle className="h-12 w-12 text-white" />
</div> </div>
</div> </div>
{/* Cancel Message */} {/* Cancel Message */}
<h1 className="text-4xl md:text-5xl font-semibold fraunces text-[#3D405B] mb-4"> <h1 className="text-4xl md:text-5xl font-semibold text-[#422268] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}>
Payment Cancelled Payment Cancelled
</h1> </h1>
<p className="text-lg text-[#6B708D] max-w-2xl mx-auto mb-8"> <p className="text-lg text-[#664fa3] 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. Your payment was cancelled. No charges have been made to your account.
</p> </p>
</div> </div>
{/* Info Card */} {/* Info Card */}
<Card className="p-8 bg-white rounded-2xl border border-[#EAE0D5] shadow-lg mb-8"> <Card className="p-8 bg-white rounded-2xl border border-[#ddd8eb] shadow-lg mb-8">
<h2 className="text-2xl font-semibold fraunces text-[#3D405B] mb-6 text-center"> <h2 className="text-2xl font-semibold text-[#422268] mb-6 text-center" style={{ fontFamily: "'Inter', sans-serif" }}>
What Happened? What Happened?
</h2> </h2>
<div className="space-y-6 mb-8"> <div className="space-y-6 mb-8">
<p className="text-[#6B708D] text-center"> <p className="text-[#664fa3] 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. You cancelled the payment process or closed the checkout page. Your membership has not been activated yet.
</p> </p>
<div className="bg-[#F2CC8F]/20 p-6 rounded-xl"> <div className="bg-[#DDD8EB]/20 p-6 rounded-xl">
<h3 className="text-lg font-semibold text-[#3D405B] mb-4"> <h3 className="text-lg font-semibold text-[#422268] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}>
Ready to Complete Your Membership? Ready to Complete Your Membership?
</h3> </h3>
<ul className="space-y-3"> <ul className="space-y-3">
<li className="flex items-start gap-3"> <li className="flex items-start gap-3">
<CreditCard className="h-5 w-5 text-[#E07A5F] flex-shrink-0 mt-0.5" /> <CreditCard className="h-5 w-5 text-[#664fa3] flex-shrink-0 mt-0.5" />
<span className="text-[#6B708D]"> <span className="text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Return to the plans page to complete your subscription Return to the plans page to complete your subscription
</span> </span>
</li> </li>
<li className="flex items-start gap-3"> <li className="flex items-start gap-3">
<Mail className="h-5 w-5 text-[#E07A5F] flex-shrink-0 mt-0.5" /> <Mail className="h-5 w-5 text-[#664fa3] flex-shrink-0 mt-0.5" />
<span className="text-[#6B708D]"> <span className="text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Contact us if you experienced any issues during checkout Contact us if you experienced any issues during checkout
</span> </span>
</li> </li>
</ul> </ul>
</div> </div>
<div className="bg-[#FDFCF8] p-6 rounded-xl"> <div className="bg-[#f1eef9] p-6 rounded-xl">
<p className="text-sm text-[#6B708D] text-center mb-4"> <p className="text-sm text-[#664fa3] text-center mb-4" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<span className="font-medium text-[#3D405B]">Note:</span>{' '} <span className="font-medium text-[#422268]">Note:</span>{' '}
Your membership application is still approved. You can complete payment whenever you're ready. Your membership application is still approved. You can complete payment whenever you're ready.
</p> </p>
</div> </div>
@@ -73,7 +73,7 @@ const PaymentCancel = () => {
<div className="flex flex-col sm:flex-row gap-4 justify-center"> <div className="flex flex-col sm:flex-row gap-4 justify-center">
<Button <Button
onClick={() => navigate('/plans')} onClick={() => navigate('/plans')}
className="bg-[#E07A5F] text-white hover:bg-[#D0694E] rounded-full px-8 py-6 text-lg font-semibold" className="bg-[#DDD8EB] text-[#422268] hover:bg-white rounded-full px-8 py-6 text-lg font-semibold"
data-testid="try-again-button" data-testid="try-again-button"
> >
<CreditCard className="mr-2 h-5 w-5" /> <CreditCard className="mr-2 h-5 w-5" />
@@ -82,7 +82,7 @@ const PaymentCancel = () => {
<Button <Button
onClick={() => navigate('/dashboard')} onClick={() => navigate('/dashboard')}
variant="outline" variant="outline"
className="border-2 border-[#6B708D] text-[#6B708D] hover:bg-[#6B708D] hover:text-white rounded-full px-8 py-6 text-lg font-semibold" className="border-2 border-[#664fa3] text-[#664fa3] hover:bg-[#664fa3] hover:text-white rounded-full px-8 py-6 text-lg font-semibold"
data-testid="back-to-dashboard-button" data-testid="back-to-dashboard-button"
> >
<ArrowLeft className="mr-2 h-5 w-5" /> <ArrowLeft className="mr-2 h-5 w-5" />
@@ -92,17 +92,17 @@ const PaymentCancel = () => {
</Card> </Card>
{/* Support Section */} {/* Support Section */}
<Card className="p-6 bg-gradient-to-br from-[#F2CC8F]/20 to-[#E07A5F]/20 rounded-2xl border border-[#EAE0D5]"> <Card className="p-6 bg-gradient-to-br from-[#DDD8EB]/20 to-[#f1eef9]/20 rounded-2xl border border-[#ddd8eb]">
<h3 className="text-lg font-semibold fraunces text-[#3D405B] mb-3 text-center"> <h3 className="text-lg font-semibold text-[#422268] mb-3 text-center" style={{ fontFamily: "'Inter', sans-serif" }}>
Need Assistance? Need Assistance?
</h3> </h3>
<p className="text-[#6B708D] text-center mb-4"> <p className="text-[#664fa3] 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. If you encountered any technical issues or have questions about the payment process, our support team is here to help.
</p> </p>
<div className="text-center"> <div className="text-center">
<a <a
href="mailto:support@loaf.org" href="mailto:support@loaf.org"
className="text-[#E07A5F] hover:text-[#D0694E] font-medium text-lg" className="text-[#ff9e77] hover:text-[#664fa3] font-medium text-lg"
> >
support@loaf.org support@loaf.org
</a> </a>

View File

@@ -20,7 +20,7 @@ const PaymentSuccess = () => {
}, [refreshUser]); }, [refreshUser]);
return ( return (
<div className="min-h-screen bg-[#FDFCF8]"> <div className="min-h-screen bg-white">
<Navbar /> <Navbar />
<div className="max-w-4xl mx-auto px-6 py-12"> <div className="max-w-4xl mx-auto px-6 py-12">
@@ -33,47 +33,47 @@ const PaymentSuccess = () => {
</div> </div>
{/* Success Message */} {/* Success Message */}
<h1 className="text-4xl md:text-5xl font-semibold fraunces text-[#3D405B] mb-4"> <h1 className="text-4xl md:text-5xl font-semibold text-[#422268] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}>
Payment Successful! Payment Successful!
</h1> </h1>
<p className="text-lg text-[#6B708D] max-w-2xl mx-auto mb-8"> <p className="text-lg text-[#664fa3] max-w-2xl mx-auto mb-8" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Thank you for your payment. Your LOAF membership is now active! Thank you for your payment. Your LOAF membership is now active!
</p> </p>
</div> </div>
{/* Confirmation Card */} {/* Confirmation Card */}
<Card className="p-8 bg-white rounded-2xl border border-[#EAE0D5] shadow-lg mb-8"> <Card className="p-8 bg-white rounded-2xl border border-[#ddd8eb] shadow-lg mb-8">
<h2 className="text-2xl font-semibold fraunces text-[#3D405B] mb-6 text-center"> <h2 className="text-2xl font-semibold text-[#422268] mb-6 text-center" style={{ fontFamily: "'Inter', sans-serif" }}>
Welcome to the LOAF Community! Welcome to the LOAF Community!
</h2> </h2>
<div className="space-y-6 mb-8"> <div className="space-y-6 mb-8">
<div className="bg-[#FDFCF8] p-6 rounded-xl"> <div className="bg-[#f1eef9] p-6 rounded-xl">
<h3 className="text-lg font-semibold text-[#3D405B] mb-4"> <h3 className="text-lg font-semibold text-[#422268] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}>
What's Next? What's Next?
</h3> </h3>
<ul className="space-y-3"> <ul className="space-y-3">
<li className="flex items-start gap-3"> <li className="flex items-start gap-3">
<CheckCircle className="h-5 w-5 text-[#81B29A] flex-shrink-0 mt-0.5" /> <CheckCircle className="h-5 w-5 text-[#81B29A] flex-shrink-0 mt-0.5" />
<span className="text-[#6B708D]"> <span className="text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Your membership is now active and you have full access to all member benefits Your membership is now active and you have full access to all member benefits
</span> </span>
</li> </li>
<li className="flex items-start gap-3"> <li className="flex items-start gap-3">
<CheckCircle className="h-5 w-5 text-[#81B29A] flex-shrink-0 mt-0.5" /> <CheckCircle className="h-5 w-5 text-[#81B29A] flex-shrink-0 mt-0.5" />
<span className="text-[#6B708D]"> <span className="text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
You can now RSVP and attend members-only events You can now RSVP and attend members-only events
</span> </span>
</li> </li>
<li className="flex items-start gap-3"> <li className="flex items-start gap-3">
<CheckCircle className="h-5 w-5 text-[#81B29A] flex-shrink-0 mt-0.5" /> <CheckCircle className="h-5 w-5 text-[#81B29A] flex-shrink-0 mt-0.5" />
<span className="text-[#6B708D]"> <span className="text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Access the community directory and connect with other members Access the community directory and connect with other members
</span> </span>
</li> </li>
<li className="flex items-start gap-3"> <li className="flex items-start gap-3">
<CheckCircle className="h-5 w-5 text-[#81B29A] flex-shrink-0 mt-0.5" /> <CheckCircle className="h-5 w-5 text-[#81B29A] flex-shrink-0 mt-0.5" />
<span className="text-[#6B708D]"> <span className="text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
You'll receive our newsletter with exclusive updates and announcements You'll receive our newsletter with exclusive updates and announcements
</span> </span>
</li> </li>
@@ -81,12 +81,12 @@ const PaymentSuccess = () => {
</div> </div>
{sessionId && ( {sessionId && (
<div className="bg-[#F2CC8F]/20 p-4 rounded-xl"> <div className="bg-[#DDD8EB]/20 p-4 rounded-xl">
<p className="text-sm text-[#6B708D] text-center"> <p className="text-sm text-[#664fa3] text-center" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<span className="font-medium text-[#3D405B]">Transaction ID:</span>{' '} <span className="font-medium text-[#422268]">Transaction ID:</span>{' '}
<span className="font-mono text-xs">{sessionId}</span> <span className="font-mono text-xs">{sessionId}</span>
</p> </p>
<p className="text-xs text-[#6B708D] text-center mt-2"> <p className="text-xs text-[#664fa3] text-center mt-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
A confirmation email has been sent to your registered email address. A confirmation email has been sent to your registered email address.
</p> </p>
</div> </div>
@@ -97,7 +97,7 @@ const PaymentSuccess = () => {
<div className="flex flex-col sm:flex-row gap-4 justify-center"> <div className="flex flex-col sm:flex-row gap-4 justify-center">
<Button <Button
onClick={() => navigate('/dashboard')} onClick={() => navigate('/dashboard')}
className="bg-[#E07A5F] text-white hover:bg-[#D0694E] rounded-full px-8 py-6 text-lg font-semibold" className="bg-[#DDD8EB] text-[#422268] hover:bg-white rounded-full px-8 py-6 text-lg font-semibold"
data-testid="go-to-dashboard-button" data-testid="go-to-dashboard-button"
> >
<User className="mr-2 h-5 w-5" /> <User className="mr-2 h-5 w-5" />
@@ -106,7 +106,7 @@ const PaymentSuccess = () => {
<Button <Button
onClick={() => navigate('/events')} onClick={() => navigate('/events')}
variant="outline" variant="outline"
className="border-2 border-[#E07A5F] text-[#E07A5F] hover:bg-[#E07A5F] hover:text-white rounded-full px-8 py-6 text-lg font-semibold" className="border-2 border-[#664fa3] text-[#664fa3] hover:bg-[#664fa3] hover:text-white rounded-full px-8 py-6 text-lg font-semibold"
data-testid="browse-events-button" data-testid="browse-events-button"
> >
<Calendar className="mr-2 h-5 w-5" /> <Calendar className="mr-2 h-5 w-5" />
@@ -117,11 +117,11 @@ const PaymentSuccess = () => {
{/* Additional Info */} {/* Additional Info */}
<div className="text-center"> <div className="text-center">
<p className="text-sm text-[#6B708D]"> <p className="text-sm text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Need help? Contact us at{' '} Need help? Contact us at{' '}
<a <a
href="mailto:support@loaf.org" href="mailto:support@loaf.org"
className="text-[#E07A5F] hover:text-[#D0694E] font-medium" className="text-[#ff9e77] hover:text-[#664fa3] font-medium"
> >
support@loaf.org support@loaf.org
</a> </a>

View File

@@ -164,10 +164,10 @@ const AdminApprovals = () => {
const getStatusBadge = (status) => { const getStatusBadge = (status) => {
const config = { const config = {
pending_email: { label: 'Awaiting Email', className: 'bg-[#F2CC8F] text-[#3D405B]' }, pending_email: { label: 'Awaiting Email', className: 'bg-orange-100 text-orange-700' },
pending_approval: { label: 'Pending', className: 'bg-[#A3B1C6] text-white' }, pending_approval: { label: 'Pending', className: 'bg-gray-200 text-gray-700' },
pre_approved: { label: 'Pre-Approved', className: 'bg-[#81B29A] text-white' }, pre_approved: { label: 'Pre-Approved', className: 'bg-[#81B29A] text-white' },
payment_pending: { label: 'Payment Pending', className: 'bg-[#E07A5F] text-white' } payment_pending: { label: 'Payment Pending', className: 'bg-orange-500 text-white' }
}; };
const statusConfig = config[status]; const statusConfig = config[status];
@@ -205,44 +205,44 @@ const AdminApprovals = () => {
<> <>
{/* Header */} {/* Header */}
<div className="mb-8"> <div className="mb-8">
<h1 className="text-4xl md:text-5xl font-semibold fraunces text-[#3D405B] mb-4"> <h1 className="text-4xl md:text-5xl font-semibold text-[#422268] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}>
Approval Queue Approval Queue
</h1> </h1>
<p className="text-lg text-[#6B708D]"> <p className="text-lg text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Review and approve pending membership applications. Review and approve pending membership applications.
</p> </p>
</div> </div>
{/* Stats Card */} {/* Stats Card */}
<Card className="p-6 bg-white rounded-2xl border border-[#EAE0D5] mb-8"> <Card className="p-6 bg-white rounded-2xl border border-[#ddd8eb] mb-8">
<div className="grid grid-cols-2 md:grid-cols-5 gap-4"> <div className="grid grid-cols-2 md:grid-cols-5 gap-4">
<div> <div>
<p className="text-sm text-[#6B708D] mb-2">Total Pending</p> <p className="text-sm text-[#664fa3] mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Total Pending</p>
<p className="text-3xl font-semibold fraunces text-[#3D405B]"> <p className="text-3xl font-semibold text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}>
{pendingUsers.length} {pendingUsers.length}
</p> </p>
</div> </div>
<div> <div>
<p className="text-sm text-[#6B708D] mb-2">Awaiting Email</p> <p className="text-sm text-[#664fa3] mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Awaiting Email</p>
<p className="text-3xl font-semibold fraunces text-[#3D405B]"> <p className="text-3xl font-semibold text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}>
{pendingUsers.filter(u => u.status === 'pending_email').length} {pendingUsers.filter(u => u.status === 'pending_email').length}
</p> </p>
</div> </div>
<div> <div>
<p className="text-sm text-[#6B708D] mb-2">Pending Approval</p> <p className="text-sm text-[#664fa3] mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Pending Approval</p>
<p className="text-3xl font-semibold fraunces text-[#3D405B]"> <p className="text-3xl font-semibold text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}>
{pendingUsers.filter(u => u.status === 'pending_approval').length} {pendingUsers.filter(u => u.status === 'pending_approval').length}
</p> </p>
</div> </div>
<div> <div>
<p className="text-sm text-[#6B708D] mb-2">Pre-Approved</p> <p className="text-sm text-[#664fa3] mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Pre-Approved</p>
<p className="text-3xl font-semibold fraunces text-[#3D405B]"> <p className="text-3xl font-semibold text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}>
{pendingUsers.filter(u => u.status === 'pre_approved').length} {pendingUsers.filter(u => u.status === 'pre_approved').length}
</p> </p>
</div> </div>
<div> <div>
<p className="text-sm text-[#6B708D] mb-2">Payment Pending</p> <p className="text-sm text-[#664fa3] mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Payment Pending</p>
<p className="text-3xl font-semibold fraunces text-[#3D405B]"> <p className="text-3xl font-semibold text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}>
{pendingUsers.filter(u => u.status === 'payment_pending').length} {pendingUsers.filter(u => u.status === 'payment_pending').length}
</p> </p>
</div> </div>
@@ -250,20 +250,20 @@ const AdminApprovals = () => {
</Card> </Card>
{/* Filter Card */} {/* Filter Card */}
<Card className="p-6 bg-white rounded-2xl border border-[#EAE0D5] mb-8"> <Card className="p-6 bg-white rounded-2xl border border-[#ddd8eb] mb-8">
<div className="grid md:grid-cols-3 gap-4"> <div className="grid md:grid-cols-3 gap-4">
<div className="relative md:col-span-2"> <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-[#6B708D]" /> <Search className="absolute left-4 top-1/2 transform -translate-y-1/2 h-5 w-5 text-[#664fa3]" />
<Input <Input
placeholder="Search by name, email, or phone..." placeholder="Search by name, email, or phone..."
value={searchQuery} value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)} onChange={(e) => setSearchQuery(e.target.value)}
className="pl-12 h-14 rounded-xl border-2 border-[#EAE0D5] focus:border-[#E07A5F]" className="pl-12 h-14 rounded-xl border-2 border-[#ddd8eb] focus:border-[#664fa3]"
/> />
</div> </div>
<Select value={statusFilter} onValueChange={setStatusFilter}> <Select value={statusFilter} onValueChange={setStatusFilter}>
<SelectTrigger className="h-14 rounded-xl border-2 border-[#EAE0D5]"> <SelectTrigger className="h-14 rounded-xl border-2 border-[#ddd8eb]">
<SelectValue placeholder="Filter by status" /> <SelectValue placeholder="Filter by status" />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
@@ -279,16 +279,16 @@ const AdminApprovals = () => {
{/* Table */} {/* Table */}
{loading ? ( {loading ? (
<div className="text-center py-20"> <div className="text-center py-20">
<p className="text-[#6B708D]">Loading pending applications...</p> <p className="text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Loading pending applications...</p>
</div> </div>
) : filteredUsers.length > 0 ? ( ) : filteredUsers.length > 0 ? (
<> <>
<Card className="bg-white rounded-2xl border border-[#EAE0D5] overflow-hidden"> <Card className="bg-white rounded-2xl border border-[#ddd8eb] overflow-hidden">
<Table> <Table>
<TableHeader> <TableHeader>
<TableRow> <TableRow>
<TableHead <TableHead
className="cursor-pointer hover:bg-[#F2CC8F]/20" className="cursor-pointer hover:bg-[#DDD8EB]/20"
onClick={() => handleSort('first_name')} onClick={() => handleSort('first_name')}
> >
Name {renderSortIcon('first_name')} Name {renderSortIcon('first_name')}
@@ -296,13 +296,13 @@ const AdminApprovals = () => {
<TableHead>Email</TableHead> <TableHead>Email</TableHead>
<TableHead>Phone</TableHead> <TableHead>Phone</TableHead>
<TableHead <TableHead
className="cursor-pointer hover:bg-[#F2CC8F]/20" className="cursor-pointer hover:bg-[#DDD8EB]/20"
onClick={() => handleSort('status')} onClick={() => handleSort('status')}
> >
Status {renderSortIcon('status')} Status {renderSortIcon('status')}
</TableHead> </TableHead>
<TableHead <TableHead
className="cursor-pointer hover:bg-[#F2CC8F]/20" className="cursor-pointer hover:bg-[#DDD8EB]/20"
onClick={() => handleSort('created_at')} onClick={() => handleSort('created_at')}
> >
Registered {renderSortIcon('created_at')} Registered {renderSortIcon('created_at')}
@@ -333,7 +333,7 @@ const AdminApprovals = () => {
onClick={() => handleBypassAndApprove(user.id)} onClick={() => handleBypassAndApprove(user.id)}
disabled={actionLoading === user.id} disabled={actionLoading === user.id}
size="sm" size="sm"
className="bg-[#E07A5F] text-white hover:bg-[#D0694E]" className="bg-[#DDD8EB] text-[#422268] hover:bg-white"
> >
{actionLoading === user.id ? 'Approving...' : 'Bypass & Approve'} {actionLoading === user.id ? 'Approving...' : 'Bypass & Approve'}
</Button> </Button>
@@ -341,7 +341,7 @@ const AdminApprovals = () => {
<Button <Button
onClick={() => handleActivatePayment(user)} onClick={() => handleActivatePayment(user)}
size="sm" size="sm"
className="bg-[#E07A5F] text-white hover:bg-[#D0694E]" className="bg-[#DDD8EB] text-[#422268] hover:bg-white"
> >
<CheckCircle className="h-4 w-4 mr-1" /> <CheckCircle className="h-4 w-4 mr-1" />
Activate Payment Activate Payment
@@ -368,7 +368,7 @@ const AdminApprovals = () => {
<div className="mt-8 flex flex-col md:flex-row justify-between items-center gap-4"> <div className="mt-8 flex flex-col md:flex-row justify-between items-center gap-4">
{/* Page size selector */} {/* Page size selector */}
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<p className="text-sm text-[#6B708D]">Show</p> <p className="text-sm text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Show</p>
<Select <Select
value={itemsPerPage.toString()} value={itemsPerPage.toString()}
onValueChange={(val) => { onValueChange={(val) => {
@@ -386,7 +386,7 @@ const AdminApprovals = () => {
<SelectItem value="100">100</SelectItem> <SelectItem value="100">100</SelectItem>
</SelectContent> </SelectContent>
</Select> </Select>
<p className="text-sm text-[#6B708D]"> <p className="text-sm text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
entries (showing {(currentPage - 1) * itemsPerPage + 1}- entries (showing {(currentPage - 1) * itemsPerPage + 1}-
{Math.min(currentPage * itemsPerPage, filteredUsers.length)} of {filteredUsers.length}) {Math.min(currentPage * itemsPerPage, filteredUsers.length)} of {filteredUsers.length})
</p> </p>
@@ -443,11 +443,11 @@ const AdminApprovals = () => {
</> </>
) : ( ) : (
<div className="text-center py-20"> <div className="text-center py-20">
<Clock className="h-20 w-20 text-[#EAE0D5] mx-auto mb-6" /> <Clock className="h-20 w-20 text-[#ddd8eb] mx-auto mb-6" />
<h3 className="text-2xl font-semibold fraunces text-[#3D405B] mb-4"> <h3 className="text-2xl font-semibold text-[#422268] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}>
No Pending Approvals No Pending Approvals
</h3> </h3>
<p className="text-[#6B708D]"> <p className="text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{searchQuery || statusFilter !== 'all' {searchQuery || statusFilter !== 'all'
? 'Try adjusting your filters' ? 'Try adjusting your filters'
: 'All applications have been reviewed!'} : 'All applications have been reviewed!'}

View File

@@ -40,66 +40,66 @@ const AdminDashboard = () => {
return ( return (
<> <>
<div className="mb-8"> <div className="mb-8">
<h1 className="text-4xl md:text-5xl font-semibold fraunces text-[#3D405B] mb-4"> <h1 className="text-4xl md:text-5xl font-semibold text-[#422268] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}>
Admin Dashboard Admin Dashboard
</h1> </h1>
<p className="text-lg text-[#6B708D]"> <p className="text-lg text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Manage users, events, and membership applications. Manage users, events, and membership applications.
</p> </p>
</div> </div>
{/* Stats Grid */} {/* Stats Grid */}
<div className="grid md:grid-cols-2 lg:grid-cols-3 gap-6 mb-12"> <div className="grid md:grid-cols-2 lg:grid-cols-3 gap-6 mb-12">
<Card className="p-6 bg-white rounded-2xl border border-[#EAE0D5]" data-testid="stat-total-users"> <Card className="p-6 bg-white rounded-2xl border border-[#ddd8eb]" data-testid="stat-total-users">
<div className="flex items-center justify-between mb-4"> <div className="flex items-center justify-between mb-4">
<div className="bg-[#A3B1C6]/20 p-3 rounded-lg"> <div className="bg-[#DDD8EB]/20 p-3 rounded-lg">
<Users className="h-6 w-6 text-[#A3B1C6]" /> <Users className="h-6 w-6 text-[#664fa3]" />
</div> </div>
</div> </div>
<p className="text-3xl font-semibold fraunces text-[#3D405B] mb-1"> <p className="text-3xl font-semibold text-[#422268] mb-1" style={{ fontFamily: "'Inter', sans-serif" }}>
{loading ? '-' : stats.totalMembers} {loading ? '-' : stats.totalMembers}
</p> </p>
<p className="text-sm text-[#6B708D]">Total Members</p> <p className="text-sm text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Total Members</p>
</Card> </Card>
<Card className="p-6 bg-white rounded-2xl border border-[#EAE0D5]" data-testid="stat-pending-approvals"> <Card className="p-6 bg-white rounded-2xl border border-[#ddd8eb]" data-testid="stat-pending-approvals">
<div className="flex items-center justify-between mb-4"> <div className="flex items-center justify-between mb-4">
<div className="bg-[#F2CC8F]/20 p-3 rounded-lg"> <div className="bg-orange-100 p-3 rounded-lg">
<Clock className="h-6 w-6 text-[#E07A5F]" /> <Clock className="h-6 w-6 text-orange-600" />
</div> </div>
</div> </div>
<p className="text-3xl font-semibold fraunces text-[#3D405B] mb-1"> <p className="text-3xl font-semibold text-[#422268] mb-1" style={{ fontFamily: "'Inter', sans-serif" }}>
{loading ? '-' : stats.pendingApprovals} {loading ? '-' : stats.pendingApprovals}
</p> </p>
<p className="text-sm text-[#6B708D]">Pending Approvals</p> <p className="text-sm text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Pending Approvals</p>
</Card> </Card>
<Card className="p-6 bg-white rounded-2xl border border-[#EAE0D5]" data-testid="stat-active-members"> <Card className="p-6 bg-white rounded-2xl border border-[#ddd8eb]" data-testid="stat-active-members">
<div className="flex items-center justify-between mb-4"> <div className="flex items-center justify-between mb-4">
<div className="bg-[#81B29A]/20 p-3 rounded-lg"> <div className="bg-[#81B29A]/20 p-3 rounded-lg">
<CheckCircle className="h-6 w-6 text-[#81B29A]" /> <CheckCircle className="h-6 w-6 text-[#81B29A]" />
</div> </div>
</div> </div>
<p className="text-3xl font-semibold fraunces text-[#3D405B] mb-1"> <p className="text-3xl font-semibold text-[#422268] mb-1" style={{ fontFamily: "'Inter', sans-serif" }}>
{loading ? '-' : stats.activeMembers} {loading ? '-' : stats.activeMembers}
</p> </p>
<p className="text-sm text-[#6B708D]">Active Members</p> <p className="text-sm text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Active Members</p>
</Card> </Card>
</div> </div>
{/* Quick Actions */} {/* Quick Actions */}
<div className="grid md:grid-cols-2 gap-8"> <div className="grid md:grid-cols-2 gap-8">
<Link to="/admin/users"> <Link to="/admin/users">
<Card className="p-8 bg-white rounded-2xl border border-[#EAE0D5] hover:shadow-lg hover:-translate-y-1 transition-all cursor-pointer" data-testid="quick-action-users"> <Card className="p-8 bg-white rounded-2xl border border-[#ddd8eb] hover:shadow-lg hover:-translate-y-1 transition-all cursor-pointer" data-testid="quick-action-users">
<Users className="h-12 w-12 text-[#E07A5F] mb-4" /> <Users className="h-12 w-12 text-[#664fa3] mb-4" />
<h3 className="text-xl font-semibold fraunces text-[#3D405B] mb-2"> <h3 className="text-xl font-semibold text-[#422268] mb-2" style={{ fontFamily: "'Inter', sans-serif" }}>
Manage Users Manage Users
</h3> </h3>
<p className="text-[#6B708D]"> <p className="text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
View and manage all registered users and their membership status. View and manage all registered users and their membership status.
</p> </p>
<Button <Button
className="mt-4 bg-[#F2CC8F] text-[#3D405B] hover:bg-[#E5B875] rounded-full" className="mt-4 bg-[#DDD8EB] text-[#422268] hover:bg-white rounded-full"
data-testid="manage-users-button" data-testid="manage-users-button"
> >
Go to Users Go to Users
@@ -108,16 +108,16 @@ const AdminDashboard = () => {
</Link> </Link>
<Link to="/admin/approvals"> <Link to="/admin/approvals">
<Card className="p-8 bg-white rounded-2xl border border-[#EAE0D5] hover:shadow-lg hover:-translate-y-1 transition-all cursor-pointer" data-testid="quick-action-approvals"> <Card className="p-8 bg-white rounded-2xl border border-[#ddd8eb] hover:shadow-lg hover:-translate-y-1 transition-all cursor-pointer" data-testid="quick-action-approvals">
<Clock className="h-12 w-12 text-[#E07A5F] mb-4" /> <Clock className="h-12 w-12 text-orange-600 mb-4" />
<h3 className="text-xl font-semibold fraunces text-[#3D405B] mb-2"> <h3 className="text-xl font-semibold text-[#422268] mb-2" style={{ fontFamily: "'Inter', sans-serif" }}>
Approval Queue Approval Queue
</h3> </h3>
<p className="text-[#6B708D]"> <p className="text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Review and approve pending membership applications. Review and approve pending membership applications.
</p> </p>
<Button <Button
className="mt-4 bg-[#F2CC8F] text-[#3D405B] hover:bg-[#E5B875] rounded-full" className="mt-4 bg-[#DDD8EB] text-[#422268] hover:bg-white rounded-full"
data-testid="manage-approvals-button" data-testid="manage-approvals-button"
> >
View Approvals View Approvals

View File

@@ -133,10 +133,10 @@ const AdminEvents = () => {
{/* Header */} {/* Header */}
<div className="mb-8 flex justify-between items-center"> <div className="mb-8 flex justify-between items-center">
<div> <div>
<h1 className="text-4xl md:text-5xl font-semibold fraunces text-[#3D405B] mb-4"> <h1 className="text-4xl md:text-5xl font-semibold text-[#422268] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}>
Event Management Event Management
</h1> </h1>
<p className="text-lg text-[#6B708D]"> <p className="text-lg text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Create and manage community events. Create and manage community events.
</p> </p>
</div> </div>
@@ -148,7 +148,7 @@ const AdminEvents = () => {
resetForm(); resetForm();
setEditingEvent(null); setEditingEvent(null);
}} }}
className="bg-[#E07A5F] text-white hover:bg-[#D0694E] rounded-full px-6" className="bg-[#DDD8EB] text-[#422268] hover:bg-white rounded-full px-6"
data-testid="create-event-button" data-testid="create-event-button"
> >
<Plus className="mr-2 h-5 w-5" /> <Plus className="mr-2 h-5 w-5" />
@@ -158,39 +158,39 @@ const AdminEvents = () => {
<DialogContent className="max-w-2xl max-h-[90vh] overflow-y-auto"> <DialogContent className="max-w-2xl max-h-[90vh] overflow-y-auto">
<DialogHeader> <DialogHeader>
<DialogTitle className="text-2xl fraunces text-[#3D405B]"> <DialogTitle className="text-2xl text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}>
{editingEvent ? 'Edit Event' : 'Create New Event'} {editingEvent ? 'Edit Event' : 'Create New Event'}
</DialogTitle> </DialogTitle>
</DialogHeader> </DialogHeader>
<form onSubmit={handleSubmit} className="space-y-4 mt-4"> <form onSubmit={handleSubmit} className="space-y-4 mt-4">
<div> <div>
<label className="block text-sm font-medium text-[#3D405B] mb-2"> <label className="block text-sm font-medium text-[#422268] mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Title * Title *
</label> </label>
<Input <Input
value={formData.title} value={formData.title}
onChange={(e) => setFormData({ ...formData, title: e.target.value })} onChange={(e) => setFormData({ ...formData, title: e.target.value })}
required required
className="border-2 border-[#EAE0D5] focus:border-[#E07A5F]" className="border-2 border-[#ddd8eb] focus:border-[#664fa3]"
/> />
</div> </div>
<div> <div>
<label className="block text-sm font-medium text-[#3D405B] mb-2"> <label className="block text-sm font-medium text-[#422268] mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Description Description
</label> </label>
<textarea <textarea
value={formData.description} value={formData.description}
onChange={(e) => setFormData({ ...formData, description: e.target.value })} onChange={(e) => setFormData({ ...formData, description: e.target.value })}
rows={4} rows={4}
className="w-full border-2 border-[#EAE0D5] focus:border-[#E07A5F] rounded-lg p-3" className="w-full border-2 border-[#ddd8eb] focus:border-[#664fa3] rounded-lg p-3"
/> />
</div> </div>
<div className="grid md:grid-cols-2 gap-4"> <div className="grid md:grid-cols-2 gap-4">
<div> <div>
<label className="block text-sm font-medium text-[#3D405B] mb-2"> <label className="block text-sm font-medium text-[#422268] mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Start Date & Time * Start Date & Time *
</label> </label>
<Input <Input
@@ -198,12 +198,12 @@ const AdminEvents = () => {
value={formData.start_at} value={formData.start_at}
onChange={(e) => setFormData({ ...formData, start_at: e.target.value })} onChange={(e) => setFormData({ ...formData, start_at: e.target.value })}
required required
className="border-2 border-[#EAE0D5] focus:border-[#E07A5F]" className="border-2 border-[#ddd8eb] focus:border-[#664fa3]"
/> />
</div> </div>
<div> <div>
<label className="block text-sm font-medium text-[#3D405B] mb-2"> <label className="block text-sm font-medium text-[#422268] mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
End Date & Time * End Date & Time *
</label> </label>
<Input <Input
@@ -211,25 +211,25 @@ const AdminEvents = () => {
value={formData.end_at} value={formData.end_at}
onChange={(e) => setFormData({ ...formData, end_at: e.target.value })} onChange={(e) => setFormData({ ...formData, end_at: e.target.value })}
required required
className="border-2 border-[#EAE0D5] focus:border-[#E07A5F]" className="border-2 border-[#ddd8eb] focus:border-[#664fa3]"
/> />
</div> </div>
</div> </div>
<div> <div>
<label className="block text-sm font-medium text-[#3D405B] mb-2"> <label className="block text-sm font-medium text-[#422268] mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Location * Location *
</label> </label>
<Input <Input
value={formData.location} value={formData.location}
onChange={(e) => setFormData({ ...formData, location: e.target.value })} onChange={(e) => setFormData({ ...formData, location: e.target.value })}
required required
className="border-2 border-[#EAE0D5] focus:border-[#E07A5F]" className="border-2 border-[#ddd8eb] focus:border-[#664fa3]"
/> />
</div> </div>
<div> <div>
<label className="block text-sm font-medium text-[#3D405B] mb-2"> <label className="block text-sm font-medium text-[#422268] mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Capacity (optional) Capacity (optional)
</label> </label>
<Input <Input
@@ -237,7 +237,7 @@ const AdminEvents = () => {
value={formData.capacity} value={formData.capacity}
onChange={(e) => setFormData({ ...formData, capacity: e.target.value })} onChange={(e) => setFormData({ ...formData, capacity: e.target.value })}
placeholder="Leave empty for unlimited" placeholder="Leave empty for unlimited"
className="border-2 border-[#EAE0D5] focus:border-[#E07A5F]" className="border-2 border-[#ddd8eb] focus:border-[#664fa3]"
/> />
</div> </div>
@@ -247,9 +247,9 @@ const AdminEvents = () => {
id="published" id="published"
checked={formData.published} checked={formData.published}
onChange={(e) => setFormData({ ...formData, published: e.target.checked })} onChange={(e) => setFormData({ ...formData, published: e.target.checked })}
className="w-4 h-4 text-[#E07A5F] border-[#EAE0D5] rounded focus:ring-[#E07A5F]" className="w-4 h-4 text-[#664fa3] border-[#ddd8eb] rounded focus:ring-[#664fa3]"
/> />
<label htmlFor="published" className="text-sm font-medium text-[#3D405B]"> <label htmlFor="published" className="text-sm font-medium text-[#422268]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Publish event (make visible to members) Publish event (make visible to members)
</label> </label>
</div> </div>
@@ -257,7 +257,7 @@ const AdminEvents = () => {
<div className="flex gap-3 pt-4"> <div className="flex gap-3 pt-4">
<Button <Button
type="submit" type="submit"
className="flex-1 bg-[#E07A5F] text-white hover:bg-[#D0694E] rounded-full" className="flex-1 bg-[#DDD8EB] text-[#422268] hover:bg-white rounded-full"
> >
{editingEvent ? 'Update Event' : 'Create Event'} {editingEvent ? 'Update Event' : 'Create Event'}
</Button> </Button>
@@ -265,7 +265,7 @@ const AdminEvents = () => {
type="button" type="button"
variant="outline" variant="outline"
onClick={handleDialogClose} onClick={handleDialogClose}
className="flex-1 border-2 border-[#6B708D] text-[#6B708D] hover:bg-[#6B708D] hover:text-white rounded-full" className="flex-1 border-2 border-gray-400 text-gray-600 hover:bg-gray-400 hover:text-white rounded-full"
> >
Cancel Cancel
</Button> </Button>
@@ -278,26 +278,26 @@ const AdminEvents = () => {
{/* Events List */} {/* Events List */}
{loading ? ( {loading ? (
<div className="text-center py-20"> <div className="text-center py-20">
<p className="text-[#6B708D]">Loading events...</p> <p className="text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Loading events...</p>
</div> </div>
) : events.length > 0 ? ( ) : events.length > 0 ? (
<div className="grid md:grid-cols-2 lg:grid-cols-3 gap-6"> <div className="grid md:grid-cols-2 lg:grid-cols-3 gap-6">
{events.map((event) => ( {events.map((event) => (
<Card <Card
key={event.id} key={event.id}
className="p-6 bg-white rounded-2xl border border-[#EAE0D5] hover:shadow-lg transition-all" className="p-6 bg-white rounded-2xl border border-[#ddd8eb] hover:shadow-lg transition-all"
data-testid={`event-card-${event.id}`} data-testid={`event-card-${event.id}`}
> >
{/* Event Header */} {/* Event Header */}
<div className="flex justify-between items-start mb-4"> <div className="flex justify-between items-start mb-4">
<div className="bg-[#F2CC8F]/20 p-3 rounded-lg"> <div className="bg-[#DDD8EB]/20 p-3 rounded-lg">
<Calendar className="h-6 w-6 text-[#E07A5F]" /> <Calendar className="h-6 w-6 text-[#664fa3]" />
</div> </div>
<Badge <Badge
className={`${ className={`${
event.published event.published
? 'bg-[#81B29A] text-white' ? 'bg-[#81B29A] text-white'
: 'bg-[#6B708D] text-white' : 'bg-gray-400 text-white'
} px-3 py-1 rounded-full`} } px-3 py-1 rounded-full`}
> >
{event.published ? 'Published' : 'Draft'} {event.published ? 'Published' : 'Draft'}
@@ -305,18 +305,18 @@ const AdminEvents = () => {
</div> </div>
{/* Event Details */} {/* Event Details */}
<h3 className="text-xl font-semibold fraunces text-[#3D405B] mb-3"> <h3 className="text-xl font-semibold text-[#422268] mb-3" style={{ fontFamily: "'Inter', sans-serif" }}>
{event.title} {event.title}
</h3> </h3>
{event.description && ( {event.description && (
<p className="text-[#6B708D] mb-4 line-clamp-2 text-sm"> <p className="text-[#664fa3] mb-4 line-clamp-2 text-sm" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{event.description} {event.description}
</p> </p>
)} )}
<div className="space-y-2 mb-4"> <div className="space-y-2 mb-4">
<div className="flex items-center gap-2 text-sm text-[#6B708D]"> <div className="flex items-center gap-2 text-sm text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<Calendar className="h-4 w-4" /> <Calendar className="h-4 w-4" />
<span> <span>
{new Date(event.start_at).toLocaleDateString()} at{' '} {new Date(event.start_at).toLocaleDateString()} at{' '}
@@ -326,18 +326,18 @@ const AdminEvents = () => {
})} })}
</span> </span>
</div> </div>
<div className="flex items-center gap-2 text-sm text-[#6B708D]"> <div className="flex items-center gap-2 text-sm text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<MapPin className="h-4 w-4" /> <MapPin className="h-4 w-4" />
<span className="truncate">{event.location}</span> <span className="truncate">{event.location}</span>
</div> </div>
<div className="flex items-center gap-2 text-sm text-[#6B708D]"> <div className="flex items-center gap-2 text-sm text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<Users className="h-4 w-4" /> <Users className="h-4 w-4" />
<span>{event.rsvp_count || 0} attending</span> <span>{event.rsvp_count || 0} attending</span>
</div> </div>
</div> </div>
{/* Actions */} {/* Actions */}
<div className="space-y-2 pt-4 border-t border-[#EAE0D5]"> <div className="space-y-2 pt-4 border-t border-[#ddd8eb]">
{/* Mark Attendance Button */} {/* Mark Attendance Button */}
<Button <Button
onClick={() => { onClick={() => {
@@ -359,7 +359,7 @@ const AdminEvents = () => {
onClick={() => togglePublish(event)} onClick={() => togglePublish(event)}
variant="outline" variant="outline"
size="sm" size="sm"
className="flex-1 border-[#E07A5F] text-[#E07A5F] hover:bg-[#E07A5F] hover:text-white" className="flex-1 border-[#664fa3] text-[#664fa3] hover:bg-[#664fa3] hover:text-white"
data-testid={`toggle-publish-${event.id}`} data-testid={`toggle-publish-${event.id}`}
> >
{event.published ? ( {event.published ? (
@@ -378,7 +378,7 @@ const AdminEvents = () => {
onClick={() => handleEdit(event)} onClick={() => handleEdit(event)}
variant="outline" variant="outline"
size="sm" size="sm"
className="border-[#6B708D] text-[#6B708D] hover:bg-[#6B708D] hover:text-white" className="border-gray-400 text-gray-600 hover:bg-gray-400 hover:text-white"
data-testid={`edit-event-${event.id}`} data-testid={`edit-event-${event.id}`}
> >
<Edit className="h-4 w-4" /> <Edit className="h-4 w-4" />
@@ -399,16 +399,16 @@ const AdminEvents = () => {
</div> </div>
) : ( ) : (
<div className="text-center py-20"> <div className="text-center py-20">
<Calendar className="h-20 w-20 text-[#EAE0D5] mx-auto mb-6" /> <Calendar className="h-20 w-20 text-[#ddd8eb] mx-auto mb-6" />
<h3 className="text-2xl font-semibold fraunces text-[#3D405B] mb-4"> <h3 className="text-2xl font-semibold text-[#422268] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}>
No Events Yet No Events Yet
</h3> </h3>
<p className="text-[#6B708D] mb-6"> <p className="text-[#664fa3] mb-6" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Create your first event to get started! Create your first event to get started!
</p> </p>
<Button <Button
onClick={() => setIsCreateDialogOpen(true)} onClick={() => setIsCreateDialogOpen(true)}
className="bg-[#E07A5F] text-white hover:bg-[#D0694E] rounded-full px-8" className="bg-[#DDD8EB] text-[#422268] hover:bg-white rounded-full px-8"
> >
<Plus className="mr-2 h-5 w-5" /> <Plus className="mr-2 h-5 w-5" />
Create Event Create Event

View File

@@ -72,12 +72,12 @@ const AdminMembers = () => {
const getStatusBadge = (status) => { const getStatusBadge = (status) => {
const config = { const config = {
pending_email: { label: 'Pending Email', className: 'bg-[#F2CC8F] text-[#3D405B]' }, pending_email: { label: 'Pending Email', className: 'bg-orange-100 text-orange-700' },
pending_approval: { label: 'Pending Approval', className: 'bg-[#A3B1C6] text-white' }, pending_approval: { label: 'Pending Approval', className: 'bg-gray-200 text-gray-700' },
pre_approved: { label: 'Pre-Approved', className: 'bg-[#81B29A] text-white' }, pre_approved: { label: 'Pre-Approved', className: 'bg-[#81B29A] text-white' },
payment_pending: { label: 'Payment Pending', className: 'bg-[#E07A5F] text-white' }, payment_pending: { label: 'Payment Pending', className: 'bg-orange-500 text-white' },
active: { label: 'Active', className: 'bg-[#81B29A] text-white' }, active: { label: 'Active', className: 'bg-[#81B29A] text-white' },
inactive: { label: 'Inactive', className: 'bg-[#6B708D] text-white' } inactive: { label: 'Inactive', className: 'bg-gray-400 text-white' }
}; };
const statusConfig = config[status] || config.inactive; const statusConfig = config[status] || config.inactive;
@@ -91,57 +91,57 @@ const AdminMembers = () => {
return ( return (
<> <>
<div className="mb-8"> <div className="mb-8">
<h1 className="text-4xl md:text-5xl font-semibold fraunces text-[#3D405B] mb-4"> <h1 className="text-4xl md:text-5xl font-semibold text-[#422268] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}>
Members Management Members Management
</h1> </h1>
<p className="text-lg text-[#6B708D]"> <p className="text-lg text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Manage paying members and their subscriptions. Manage paying members and their subscriptions.
</p> </p>
</div> </div>
{/* Stats */} {/* Stats */}
<div className="grid md:grid-cols-4 gap-4 mb-8"> <div className="grid md:grid-cols-4 gap-4 mb-8">
<Card className="p-6 bg-white rounded-2xl border border-[#EAE0D5]"> <Card className="p-6 bg-white rounded-2xl border border-[#ddd8eb]">
<p className="text-sm text-[#6B708D] mb-2">Total Members</p> <p className="text-sm text-[#664fa3] mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Total Members</p>
<p className="text-3xl font-semibold fraunces text-[#3D405B]"> <p className="text-3xl font-semibold text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}>
{users.length} {users.length}
</p> </p>
</Card> </Card>
<Card className="p-6 bg-white rounded-2xl border border-[#EAE0D5]"> <Card className="p-6 bg-white rounded-2xl border border-[#ddd8eb]">
<p className="text-sm text-[#6B708D] mb-2">Active</p> <p className="text-sm text-[#664fa3] mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Active</p>
<p className="text-3xl font-semibold fraunces text-[#3D405B]"> <p className="text-3xl font-semibold text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}>
{users.filter(u => u.status === 'active').length} {users.filter(u => u.status === 'active').length}
</p> </p>
</Card> </Card>
<Card className="p-6 bg-white rounded-2xl border border-[#EAE0D5]"> <Card className="p-6 bg-white rounded-2xl border border-[#ddd8eb]">
<p className="text-sm text-[#6B708D] mb-2">Payment Pending</p> <p className="text-sm text-[#664fa3] mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Payment Pending</p>
<p className="text-3xl font-semibold fraunces text-[#3D405B]"> <p className="text-3xl font-semibold text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}>
{users.filter(u => u.status === 'payment_pending').length} {users.filter(u => u.status === 'payment_pending').length}
</p> </p>
</Card> </Card>
<Card className="p-6 bg-white rounded-2xl border border-[#EAE0D5]"> <Card className="p-6 bg-white rounded-2xl border border-[#ddd8eb]">
<p className="text-sm text-[#6B708D] mb-2">Inactive</p> <p className="text-sm text-[#664fa3] mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Inactive</p>
<p className="text-3xl font-semibold fraunces text-[#3D405B]"> <p className="text-3xl font-semibold text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}>
{users.filter(u => u.status === 'inactive').length} {users.filter(u => u.status === 'inactive').length}
</p> </p>
</Card> </Card>
</div> </div>
{/* Filters */} {/* Filters */}
<Card className="p-6 bg-white rounded-2xl border border-[#EAE0D5] mb-8"> <Card className="p-6 bg-white rounded-2xl border border-[#ddd8eb] mb-8">
<div className="grid md:grid-cols-2 gap-4"> <div className="grid md:grid-cols-2 gap-4">
<div className="relative"> <div className="relative">
<Search className="absolute left-4 top-1/2 transform -translate-y-1/2 h-5 w-5 text-[#6B708D]" /> <Search className="absolute left-4 top-1/2 transform -translate-y-1/2 h-5 w-5 text-[#664fa3]" />
<Input <Input
placeholder="Search by name or email..." placeholder="Search by name or email..."
value={searchQuery} value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)} onChange={(e) => setSearchQuery(e.target.value)}
className="pl-12 h-14 rounded-xl border-2 border-[#EAE0D5] focus:border-[#E07A5F]" className="pl-12 h-14 rounded-xl border-2 border-[#ddd8eb] focus:border-[#664fa3]"
data-testid="search-members-input" data-testid="search-members-input"
/> />
</div> </div>
<Select value={statusFilter} onValueChange={setStatusFilter}> <Select value={statusFilter} onValueChange={setStatusFilter}>
<SelectTrigger className="h-14 rounded-xl border-2 border-[#EAE0D5]" data-testid="status-filter-select"> <SelectTrigger className="h-14 rounded-xl border-2 border-[#ddd8eb]" data-testid="status-filter-select">
<SelectValue placeholder="Filter by status" /> <SelectValue placeholder="Filter by status" />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
@@ -159,32 +159,32 @@ const AdminMembers = () => {
{/* Members List */} {/* Members List */}
{loading ? ( {loading ? (
<div className="text-center py-20"> <div className="text-center py-20">
<p className="text-[#6B708D]">Loading members...</p> <p className="text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Loading members...</p>
</div> </div>
) : filteredUsers.length > 0 ? ( ) : filteredUsers.length > 0 ? (
<div className="space-y-4"> <div className="space-y-4">
{filteredUsers.map((user) => ( {filteredUsers.map((user) => (
<Card <Card
key={user.id} key={user.id}
className="p-6 bg-white rounded-2xl border border-[#EAE0D5] hover:shadow-md transition-shadow" className="p-6 bg-white rounded-2xl border border-[#ddd8eb] hover:shadow-md transition-shadow"
data-testid={`member-card-${user.id}`} data-testid={`member-card-${user.id}`}
> >
<div className="flex justify-between items-start flex-wrap gap-4"> <div className="flex justify-between items-start flex-wrap gap-4">
<div className="flex items-start gap-4 flex-1"> <div className="flex items-start gap-4 flex-1">
{/* Avatar */} {/* Avatar */}
<div className="h-14 w-14 rounded-full bg-[#F2CC8F] flex items-center justify-center text-[#3D405B] font-semibold text-lg flex-shrink-0"> <div className="h-14 w-14 rounded-full bg-[#DDD8EB] flex items-center justify-center text-[#422268] font-semibold text-lg flex-shrink-0">
{user.first_name?.[0]}{user.last_name?.[0]} {user.first_name?.[0]}{user.last_name?.[0]}
</div> </div>
{/* Info */} {/* Info */}
<div className="flex-1 min-w-0"> <div className="flex-1 min-w-0">
<div className="flex items-center gap-3 mb-2 flex-wrap"> <div className="flex items-center gap-3 mb-2 flex-wrap">
<h3 className="text-xl font-semibold fraunces text-[#3D405B]"> <h3 className="text-xl font-semibold text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}>
{user.first_name} {user.last_name} {user.first_name} {user.last_name}
</h3> </h3>
{getStatusBadge(user.status)} {getStatusBadge(user.status)}
</div> </div>
<div className="grid md:grid-cols-2 gap-2 text-sm text-[#6B708D]"> <div className="grid md:grid-cols-2 gap-2 text-sm text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p>Email: {user.email}</p> <p>Email: {user.email}</p>
<p>Phone: {user.phone}</p> <p>Phone: {user.phone}</p>
<p>Joined: {new Date(user.created_at).toLocaleDateString()}</p> <p>Joined: {new Date(user.created_at).toLocaleDateString()}</p>
@@ -201,7 +201,7 @@ const AdminMembers = () => {
<Button <Button
variant="outline" variant="outline"
size="sm" size="sm"
className="border-[#A3B1C6] text-[#A3B1C6] hover:bg-[#A3B1C6] hover:text-white" className="border-[#664fa3] text-[#664fa3] hover:bg-[#664fa3] hover:text-white"
> >
<Eye className="h-4 w-4 mr-1" /> <Eye className="h-4 w-4 mr-1" />
View Profile View Profile
@@ -213,7 +213,7 @@ const AdminMembers = () => {
<Button <Button
onClick={() => handleActivatePayment(user)} onClick={() => handleActivatePayment(user)}
size="sm" size="sm"
className="bg-[#E07A5F] text-white hover:bg-[#D0694E]" className="bg-[#DDD8EB] text-[#422268] hover:bg-white"
> >
<CheckCircle className="h-4 w-4 mr-1" /> <CheckCircle className="h-4 w-4 mr-1" />
Activate Payment Activate Payment
@@ -238,11 +238,11 @@ const AdminMembers = () => {
</div> </div>
) : ( ) : (
<div className="text-center py-20"> <div className="text-center py-20">
<Users className="h-20 w-20 text-[#EAE0D5] mx-auto mb-6" /> <Users className="h-20 w-20 text-[#ddd8eb] mx-auto mb-6" />
<h3 className="text-2xl font-semibold fraunces text-[#3D405B] mb-4"> <h3 className="text-2xl font-semibold text-[#422268] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}>
No Members Found No Members Found
</h3> </h3>
<p className="text-[#6B708D]"> <p className="text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{searchQuery || statusFilter !== 'all' {searchQuery || statusFilter !== 'all'
? 'Try adjusting your filters' ? 'Try adjusting your filters'
: 'No members yet'} : 'No members yet'}

View File

@@ -118,16 +118,16 @@ const AdminPlans = () => {
<div className="mb-8"> <div className="mb-8">
<div className="flex justify-between items-start mb-4"> <div className="flex justify-between items-start mb-4">
<div> <div>
<h1 className="text-4xl md:text-5xl font-semibold fraunces text-[#3D405B] mb-4"> <h1 className="text-4xl md:text-5xl font-semibold text-[#422268] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}>
Subscription Plans Subscription Plans
</h1> </h1>
<p className="text-lg text-[#6B708D]"> <p className="text-lg text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Manage membership plans and pricing. Manage membership plans and pricing.
</p> </p>
</div> </div>
<Button <Button
onClick={handleCreatePlan} onClick={handleCreatePlan}
className="bg-[#E07A5F] hover:bg-[#D0694E] text-white rounded-full px-6" className="bg-[#DDD8EB] text-[#422268] hover:bg-white rounded-full px-6"
> >
<Plus className="h-4 w-4 mr-2" /> <Plus className="h-4 w-4 mr-2" />
Create Plan Create Plan
@@ -137,27 +137,27 @@ const AdminPlans = () => {
{/* Stats */} {/* Stats */}
<div className="grid md:grid-cols-4 gap-4 mb-8"> <div className="grid md:grid-cols-4 gap-4 mb-8">
<Card className="p-6 bg-white rounded-2xl border border-[#EAE0D5]"> <Card className="p-6 bg-white rounded-2xl border border-[#ddd8eb]">
<p className="text-sm text-[#6B708D] mb-2">Total Plans</p> <p className="text-sm text-[#664fa3] mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Total Plans</p>
<p className="text-3xl font-semibold fraunces text-[#3D405B]"> <p className="text-3xl font-semibold text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}>
{plans.length} {plans.length}
</p> </p>
</Card> </Card>
<Card className="p-6 bg-white rounded-2xl border border-[#EAE0D5]"> <Card className="p-6 bg-white rounded-2xl border border-[#ddd8eb]">
<p className="text-sm text-[#6B708D] mb-2">Active Plans</p> <p className="text-sm text-[#664fa3] mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Active Plans</p>
<p className="text-3xl font-semibold fraunces text-[#3D405B]"> <p className="text-3xl font-semibold text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}>
{plans.filter(p => p.active).length} {plans.filter(p => p.active).length}
</p> </p>
</Card> </Card>
<Card className="p-6 bg-white rounded-2xl border border-[#EAE0D5]"> <Card className="p-6 bg-white rounded-2xl border border-[#ddd8eb]">
<p className="text-sm text-[#6B708D] mb-2">Total Subscribers</p> <p className="text-sm text-[#664fa3] mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Total Subscribers</p>
<p className="text-3xl font-semibold fraunces text-[#3D405B]"> <p className="text-3xl font-semibold text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}>
{plans.reduce((sum, p) => sum + (p.subscriber_count || 0), 0)} {plans.reduce((sum, p) => sum + (p.subscriber_count || 0), 0)}
</p> </p>
</Card> </Card>
<Card className="p-6 bg-white rounded-2xl border border-[#EAE0D5]"> <Card className="p-6 bg-white rounded-2xl border border-[#ddd8eb]">
<p className="text-sm text-[#6B708D] mb-2">Revenue (Annual Est.)</p> <p className="text-sm text-[#664fa3] mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Revenue (Annual Est.)</p>
<p className="text-3xl font-semibold fraunces text-[#3D405B]"> <p className="text-3xl font-semibold text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}>
{formatPrice( {formatPrice(
plans.reduce((sum, p) => { plans.reduce((sum, p) => {
const annualPrice = p.billing_cycle === 'yearly' const annualPrice = p.billing_cycle === 'yearly'
@@ -171,19 +171,19 @@ const AdminPlans = () => {
</div> </div>
{/* Filters */} {/* Filters */}
<Card className="p-6 bg-white rounded-2xl border border-[#EAE0D5] mb-8"> <Card className="p-6 bg-white rounded-2xl border border-[#ddd8eb] mb-8">
<div className="grid md:grid-cols-2 gap-4"> <div className="grid md:grid-cols-2 gap-4">
<div className="relative"> <div className="relative">
<Search className="absolute left-4 top-1/2 transform -translate-y-1/2 h-5 w-5 text-[#6B708D]" /> <Search className="absolute left-4 top-1/2 transform -translate-y-1/2 h-5 w-5 text-[#664fa3]" />
<Input <Input
placeholder="Search plans..." placeholder="Search plans..."
value={searchQuery} value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)} onChange={(e) => setSearchQuery(e.target.value)}
className="pl-12 h-14 rounded-xl border-2 border-[#EAE0D5] focus:border-[#E07A5F]" className="pl-12 h-14 rounded-xl border-2 border-[#ddd8eb] focus:border-[#664fa3]"
/> />
</div> </div>
<Select value={activeFilter} onValueChange={setActiveFilter}> <Select value={activeFilter} onValueChange={setActiveFilter}>
<SelectTrigger className="h-14 rounded-xl border-2 border-[#EAE0D5]"> <SelectTrigger className="h-14 rounded-xl border-2 border-[#ddd8eb]">
<SelectValue placeholder="Filter by status" /> <SelectValue placeholder="Filter by status" />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
@@ -198,7 +198,7 @@ const AdminPlans = () => {
{/* Plans Grid */} {/* Plans Grid */}
{loading ? ( {loading ? (
<div className="text-center py-20"> <div className="text-center py-20">
<p className="text-[#6B708D]">Loading plans...</p> <p className="text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Loading plans...</p>
</div> </div>
) : filteredPlans.length > 0 ? ( ) : filteredPlans.length > 0 ? (
<div className="grid md:grid-cols-2 lg:grid-cols-3 gap-6"> <div className="grid md:grid-cols-2 lg:grid-cols-3 gap-6">
@@ -207,8 +207,8 @@ const AdminPlans = () => {
key={plan.id} key={plan.id}
className={`p-6 bg-white rounded-2xl border-2 transition-all hover:shadow-lg ${ className={`p-6 bg-white rounded-2xl border-2 transition-all hover:shadow-lg ${
plan.active plan.active
? 'border-[#EAE0D5] hover:border-[#E07A5F]' ? 'border-[#ddd8eb] hover:border-[#664fa3]'
: 'border-[#6B708D] opacity-60' : 'border-gray-400 opacity-60'
}`} }`}
> >
{/* Header with badges */} {/* Header with badges */}
@@ -217,13 +217,13 @@ const AdminPlans = () => {
className={`${ className={`${
plan.active plan.active
? 'bg-[#81B29A] text-white' ? 'bg-[#81B29A] text-white'
: 'bg-[#6B708D] text-white' : 'bg-gray-400 text-white'
}`} }`}
> >
{plan.active ? 'Active' : 'Inactive'} {plan.active ? 'Active' : 'Inactive'}
</Badge> </Badge>
{plan.subscriber_count > 0 && ( {plan.subscriber_count > 0 && (
<Badge className="bg-[#F2CC8F] text-[#3D405B]"> <Badge className="bg-[#DDD8EB] text-[#422268]">
<Users className="h-3 w-3 mr-1" /> <Users className="h-3 w-3 mr-1" />
{plan.subscriber_count} {plan.subscriber_count}
</Badge> </Badge>
@@ -231,23 +231,23 @@ const AdminPlans = () => {
</div> </div>
{/* Plan Name */} {/* Plan Name */}
<h3 className="text-2xl font-semibold fraunces text-[#3D405B] mb-2"> <h3 className="text-2xl font-semibold text-[#422268] mb-2" style={{ fontFamily: "'Inter', sans-serif" }}>
{plan.name} {plan.name}
</h3> </h3>
{/* Description */} {/* Description */}
{plan.description && ( {plan.description && (
<p className="text-sm text-[#6B708D] mb-4 line-clamp-2"> <p className="text-sm text-[#664fa3] mb-4 line-clamp-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{plan.description} {plan.description}
</p> </p>
)} )}
{/* Price */} {/* Price */}
<div className="mb-4"> <div className="mb-4">
<div className="text-3xl font-bold fraunces text-[#E07A5F]"> <div className="text-3xl font-bold text-[#ff9e77]" style={{ fontFamily: "'Inter', sans-serif" }}>
{formatPrice(plan.price_cents)} {formatPrice(plan.price_cents)}
</div> </div>
<p className="text-sm text-[#6B708D]"> <p className="text-sm text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{getBillingCycleLabel(plan.billing_cycle)} {getBillingCycleLabel(plan.billing_cycle)}
</p> </p>
</div> </div>
@@ -260,19 +260,19 @@ const AdminPlans = () => {
Stripe Integrated Stripe Integrated
</Badge> </Badge>
) : ( ) : (
<Badge className="bg-[#F2CC8F] text-[#3D405B] text-xs"> <Badge className="bg-[#DDD8EB] text-[#422268] text-xs">
Manual/Test Plan Manual/Test Plan
</Badge> </Badge>
)} )}
</div> </div>
{/* Actions */} {/* Actions */}
<div className="flex gap-2 pt-4 border-t border-[#EAE0D5]"> <div className="flex gap-2 pt-4 border-t border-[#ddd8eb]">
<Button <Button
onClick={() => handleEditPlan(plan)} onClick={() => handleEditPlan(plan)}
variant="outline" variant="outline"
size="sm" size="sm"
className="flex-1 border-[#A3B1C6] text-[#A3B1C6] hover:bg-[#A3B1C6] hover:text-white rounded-full" className="flex-1 border-[#664fa3] text-[#664fa3] hover:bg-[#664fa3] hover:text-white rounded-full"
> >
<Edit className="h-4 w-4 mr-1" /> <Edit className="h-4 w-4 mr-1" />
Edit Edit
@@ -281,7 +281,7 @@ const AdminPlans = () => {
onClick={() => handleDeleteClick(plan)} onClick={() => handleDeleteClick(plan)}
variant="outline" variant="outline"
size="sm" size="sm"
className="flex-1 border-[#E07A5F] text-[#E07A5F] hover:bg-[#E07A5F] hover:text-white rounded-full" className="flex-1 border-red-500 text-red-500 hover:bg-red-500 hover:text-white rounded-full"
disabled={plan.subscriber_count > 0} disabled={plan.subscriber_count > 0}
> >
<Trash2 className="h-4 w-4 mr-1" /> <Trash2 className="h-4 w-4 mr-1" />
@@ -291,7 +291,7 @@ const AdminPlans = () => {
{/* Warning for plans with subscribers */} {/* Warning for plans with subscribers */}
{plan.subscriber_count > 0 && ( {plan.subscriber_count > 0 && (
<p className="text-xs text-[#6B708D] mt-2 text-center"> <p className="text-xs text-[#664fa3] mt-2 text-center" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Cannot delete plan with active subscribers Cannot delete plan with active subscribers
</p> </p>
)} )}
@@ -300,11 +300,11 @@ const AdminPlans = () => {
</div> </div>
) : ( ) : (
<div className="text-center py-20"> <div className="text-center py-20">
<CreditCard className="h-20 w-20 text-[#EAE0D5] mx-auto mb-6" /> <CreditCard className="h-20 w-20 text-[#ddd8eb] mx-auto mb-6" />
<h3 className="text-2xl font-semibold fraunces text-[#3D405B] mb-4"> <h3 className="text-2xl font-semibold text-[#422268] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}>
No Plans Found No Plans Found
</h3> </h3>
<p className="text-[#6B708D] mb-6"> <p className="text-[#664fa3] mb-6" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{searchQuery || activeFilter !== 'all' {searchQuery || activeFilter !== 'all'
? 'Try adjusting your filters' ? 'Try adjusting your filters'
: 'Create your first subscription plan to get started'} : 'Create your first subscription plan to get started'}
@@ -312,7 +312,7 @@ const AdminPlans = () => {
{!searchQuery && activeFilter === 'all' && ( {!searchQuery && activeFilter === 'all' && (
<Button <Button
onClick={handleCreatePlan} onClick={handleCreatePlan}
className="bg-[#E07A5F] hover:bg-[#D0694E] text-white rounded-full px-8" className="bg-[#DDD8EB] text-[#422268] hover:bg-white rounded-full px-8"
> >
<Plus className="h-4 w-4 mr-2" /> <Plus className="h-4 w-4 mr-2" />
Create First Plan Create First Plan
@@ -333,10 +333,10 @@ const AdminPlans = () => {
{deleteDialogOpen && ( {deleteDialogOpen && (
<div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50"> <div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50">
<Card className="p-8 bg-white rounded-2xl max-w-md mx-4"> <Card className="p-8 bg-white rounded-2xl max-w-md mx-4">
<h2 className="text-2xl font-semibold fraunces text-[#3D405B] mb-4"> <h2 className="text-2xl font-semibold text-[#422268] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}>
Delete Plan Delete Plan
</h2> </h2>
<p className="text-[#6B708D] mb-6"> <p className="text-[#664fa3] mb-6" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Are you sure you want to delete "{planToDelete?.name}"? This action Are you sure you want to delete "{planToDelete?.name}"? This action
will deactivate the plan and it won't be available for new subscriptions. will deactivate the plan and it won't be available for new subscriptions.
</p> </p>
@@ -350,7 +350,7 @@ const AdminPlans = () => {
</Button> </Button>
<Button <Button
onClick={handleDeleteConfirm} onClick={handleDeleteConfirm}
className="flex-1 bg-[#E07A5F] hover:bg-[#D0694E] text-white" className="flex-1 bg-red-500 hover:bg-red-600 text-white"
> >
Delete Plan Delete Plan
</Button> </Button>

View File

@@ -62,11 +62,11 @@ const AdminStaff = () => {
const getRoleBadge = (role) => { const getRoleBadge = (role) => {
const config = { const config = {
superadmin: { label: 'Superadmin', className: 'bg-[#E07A5F] text-white' }, superadmin: { label: 'Superadmin', className: 'bg-[#664fa3] text-white' },
admin: { label: 'Admin', className: 'bg-[#81B29A] text-white' }, admin: { label: 'Admin', className: 'bg-[#81B29A] text-white' },
moderator: { label: 'Moderator', className: 'bg-[#A3B1C6] text-white' }, moderator: { label: 'Moderator', className: 'bg-[#DDD8EB] text-[#422268]' },
staff: { label: 'Staff', className: 'bg-[#F2CC8F] text-[#3D405B]' }, staff: { label: 'Staff', className: 'bg-gray-200 text-gray-700' },
media: { label: 'Media', className: 'bg-[#6B708D] text-white' } media: { label: 'Media', className: 'bg-gray-400 text-white' }
}; };
const roleConfig = config[role] || { label: role, className: 'bg-gray-500 text-white' }; const roleConfig = config[role] || { label: role, className: 'bg-gray-500 text-white' };
@@ -81,7 +81,7 @@ const AdminStaff = () => {
const getStatusBadge = (status) => { const getStatusBadge = (status) => {
const config = { const config = {
active: { label: 'Active', className: 'bg-[#81B29A] text-white' }, active: { label: 'Active', className: 'bg-[#81B29A] text-white' },
inactive: { label: 'Inactive', className: 'bg-[#6B708D] text-white' } inactive: { label: 'Inactive', className: 'bg-gray-400 text-white' }
}; };
const statusConfig = config[status] || config.inactive; const statusConfig = config[status] || config.inactive;
@@ -95,57 +95,57 @@ const AdminStaff = () => {
return ( return (
<> <>
<div className="mb-8"> <div className="mb-8">
<h1 className="text-4xl md:text-5xl font-semibold fraunces text-[#3D405B] mb-4"> <h1 className="text-4xl md:text-5xl font-semibold text-[#422268] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}>
Staff Management Staff Management
</h1> </h1>
<p className="text-lg text-[#6B708D]"> <p className="text-lg text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Manage internal team members and their roles. Manage internal team members and their roles.
</p> </p>
</div> </div>
{/* Stats */} {/* Stats */}
<div className="grid md:grid-cols-4 gap-4 mb-8"> <div className="grid md:grid-cols-4 gap-4 mb-8">
<Card className="p-6 bg-white rounded-2xl border border-[#EAE0D5]"> <Card className="p-6 bg-white rounded-2xl border border-[#ddd8eb]">
<p className="text-sm text-[#6B708D] mb-2">Total Staff</p> <p className="text-sm text-[#664fa3] mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Total Staff</p>
<p className="text-3xl font-semibold fraunces text-[#3D405B]"> <p className="text-3xl font-semibold text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}>
{users.length} {users.length}
</p> </p>
</Card> </Card>
<Card className="p-6 bg-white rounded-2xl border border-[#EAE0D5]"> <Card className="p-6 bg-white rounded-2xl border border-[#ddd8eb]">
<p className="text-sm text-[#6B708D] mb-2">Admins</p> <p className="text-sm text-[#664fa3] mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Admins</p>
<p className="text-3xl font-semibold fraunces text-[#3D405B]"> <p className="text-3xl font-semibold text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}>
{users.filter(u => ['admin', 'superadmin'].includes(u.role)).length} {users.filter(u => ['admin', 'superadmin'].includes(u.role)).length}
</p> </p>
</Card> </Card>
<Card className="p-6 bg-white rounded-2xl border border-[#EAE0D5]"> <Card className="p-6 bg-white rounded-2xl border border-[#ddd8eb]">
<p className="text-sm text-[#6B708D] mb-2">Moderators</p> <p className="text-sm text-[#664fa3] mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Moderators</p>
<p className="text-3xl font-semibold fraunces text-[#3D405B]"> <p className="text-3xl font-semibold text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}>
{users.filter(u => u.role === 'moderator').length} {users.filter(u => u.role === 'moderator').length}
</p> </p>
</Card> </Card>
<Card className="p-6 bg-white rounded-2xl border border-[#EAE0D5]"> <Card className="p-6 bg-white rounded-2xl border border-[#ddd8eb]">
<p className="text-sm text-[#6B708D] mb-2">Active</p> <p className="text-sm text-[#664fa3] mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Active</p>
<p className="text-3xl font-semibold fraunces text-[#3D405B]"> <p className="text-3xl font-semibold text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}>
{users.filter(u => u.status === 'active').length} {users.filter(u => u.status === 'active').length}
</p> </p>
</Card> </Card>
</div> </div>
{/* Filters */} {/* Filters */}
<Card className="p-6 bg-white rounded-2xl border border-[#EAE0D5] mb-8"> <Card className="p-6 bg-white rounded-2xl border border-[#ddd8eb] mb-8">
<div className="grid md:grid-cols-2 gap-4"> <div className="grid md:grid-cols-2 gap-4">
<div className="relative"> <div className="relative">
<Search className="absolute left-4 top-1/2 transform -translate-y-1/2 h-5 w-5 text-[#6B708D]" /> <Search className="absolute left-4 top-1/2 transform -translate-y-1/2 h-5 w-5 text-[#664fa3]" />
<Input <Input
placeholder="Search by name or email..." placeholder="Search by name or email..."
value={searchQuery} value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)} onChange={(e) => setSearchQuery(e.target.value)}
className="pl-12 h-14 rounded-xl border-2 border-[#EAE0D5] focus:border-[#E07A5F]" className="pl-12 h-14 rounded-xl border-2 border-[#ddd8eb] focus:border-[#664fa3]"
data-testid="search-staff-input" data-testid="search-staff-input"
/> />
</div> </div>
<Select value={roleFilter} onValueChange={setRoleFilter}> <Select value={roleFilter} onValueChange={setRoleFilter}>
<SelectTrigger className="h-14 rounded-xl border-2 border-[#EAE0D5]" data-testid="role-filter-select"> <SelectTrigger className="h-14 rounded-xl border-2 border-[#ddd8eb]" data-testid="role-filter-select">
<SelectValue placeholder="Filter by role" /> <SelectValue placeholder="Filter by role" />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
@@ -163,33 +163,33 @@ const AdminStaff = () => {
{/* Staff List */} {/* Staff List */}
{loading ? ( {loading ? (
<div className="text-center py-20"> <div className="text-center py-20">
<p className="text-[#6B708D]">Loading staff...</p> <p className="text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Loading staff...</p>
</div> </div>
) : filteredUsers.length > 0 ? ( ) : filteredUsers.length > 0 ? (
<div className="space-y-4"> <div className="space-y-4">
{filteredUsers.map((user) => ( {filteredUsers.map((user) => (
<Card <Card
key={user.id} key={user.id}
className="p-6 bg-white rounded-2xl border border-[#EAE0D5] hover:shadow-md transition-shadow" className="p-6 bg-white rounded-2xl border border-[#ddd8eb] hover:shadow-md transition-shadow"
data-testid={`staff-card-${user.id}`} data-testid={`staff-card-${user.id}`}
> >
<div className="flex justify-between items-start flex-wrap gap-4"> <div className="flex justify-between items-start flex-wrap gap-4">
<div className="flex items-start gap-4 flex-1"> <div className="flex items-start gap-4 flex-1">
{/* Avatar */} {/* Avatar */}
<div className="h-14 w-14 rounded-full bg-[#F2CC8F] flex items-center justify-center text-[#3D405B] font-semibold text-lg flex-shrink-0"> <div className="h-14 w-14 rounded-full bg-[#DDD8EB] flex items-center justify-center text-[#422268] font-semibold text-lg flex-shrink-0">
{user.first_name?.[0]}{user.last_name?.[0]} {user.first_name?.[0]}{user.last_name?.[0]}
</div> </div>
{/* Info */} {/* Info */}
<div className="flex-1 min-w-0"> <div className="flex-1 min-w-0">
<div className="flex items-center gap-3 mb-2 flex-wrap"> <div className="flex items-center gap-3 mb-2 flex-wrap">
<h3 className="text-xl font-semibold fraunces text-[#3D405B]"> <h3 className="text-xl font-semibold text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}>
{user.first_name} {user.last_name} {user.first_name} {user.last_name}
</h3> </h3>
{getRoleBadge(user.role)} {getRoleBadge(user.role)}
{getStatusBadge(user.status)} {getStatusBadge(user.status)}
</div> </div>
<div className="grid md:grid-cols-2 gap-2 text-sm text-[#6B708D]"> <div className="grid md:grid-cols-2 gap-2 text-sm text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p>Email: {user.email}</p> <p>Email: {user.email}</p>
<p>Phone: {user.phone}</p> <p>Phone: {user.phone}</p>
<p>Joined: {new Date(user.created_at).toLocaleDateString()}</p> <p>Joined: {new Date(user.created_at).toLocaleDateString()}</p>
@@ -205,11 +205,11 @@ const AdminStaff = () => {
</div> </div>
) : ( ) : (
<div className="text-center py-20"> <div className="text-center py-20">
<UserCog className="h-20 w-20 text-[#EAE0D5] mx-auto mb-6" /> <UserCog className="h-20 w-20 text-[#ddd8eb] mx-auto mb-6" />
<h3 className="text-2xl font-semibold fraunces text-[#3D405B] mb-4"> <h3 className="text-2xl font-semibold text-[#422268] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}>
No Staff Found No Staff Found
</h3> </h3>
<p className="text-[#6B708D]"> <p className="text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{searchQuery || roleFilter !== 'all' {searchQuery || roleFilter !== 'all'
? 'Try adjusting your filters' ? 'Try adjusting your filters'
: 'No staff members yet'} : 'No staff members yet'}

View File

@@ -93,17 +93,17 @@ const AdminUserView = () => {
</Button> </Button>
{/* User Profile Header */} {/* User Profile Header */}
<Card className="p-8 bg-white rounded-2xl border border-[#EAE0D5] mb-8"> <Card className="p-8 bg-white rounded-2xl border border-[#ddd8eb] mb-8">
<div className="flex items-start gap-6"> <div className="flex items-start gap-6">
{/* Avatar */} {/* Avatar */}
<div className="h-24 w-24 rounded-full bg-[#F2CC8F] flex items-center justify-center text-[#3D405B] font-semibold text-3xl"> <div className="h-24 w-24 rounded-full bg-[#DDD8EB] flex items-center justify-center text-[#422268] font-semibold text-3xl">
{user.first_name?.[0]}{user.last_name?.[0]} {user.first_name?.[0]}{user.last_name?.[0]}
</div> </div>
{/* User Info */} {/* User Info */}
<div className="flex-1"> <div className="flex-1">
<div className="flex items-center gap-4 mb-4"> <div className="flex items-center gap-4 mb-4">
<h1 className="text-3xl font-semibold fraunces text-[#3D405B]"> <h1 className="text-3xl font-semibold text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}>
{user.first_name} {user.last_name} {user.first_name} {user.last_name}
</h1> </h1>
{/* Status & Role Badges */} {/* Status & Role Badges */}
@@ -112,7 +112,7 @@ const AdminUserView = () => {
</div> </div>
{/* Contact Info */} {/* Contact Info */}
<div className="grid md:grid-cols-2 gap-4 text-[#6B708D]"> <div className="grid md:grid-cols-2 gap-4 text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<Mail className="h-4 w-4" /> <Mail className="h-4 w-4" />
<span>{user.email}</span> <span>{user.email}</span>
@@ -135,8 +135,8 @@ const AdminUserView = () => {
</Card> </Card>
{/* Admin Actions */} {/* Admin Actions */}
<Card className="p-6 bg-white rounded-2xl border border-[#EAE0D5] mb-8"> <Card className="p-6 bg-white rounded-2xl border border-[#ddd8eb] mb-8">
<h2 className="text-lg font-semibold fraunces text-[#3D405B] mb-4"> <h2 className="text-lg font-semibold text-[#422268] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}>
Admin Actions Admin Actions
</h2> </h2>
<div className="flex flex-wrap gap-3"> <div className="flex flex-wrap gap-3">
@@ -144,7 +144,7 @@ const AdminUserView = () => {
onClick={handleResetPassword} onClick={handleResetPassword}
disabled={resetPasswordLoading} disabled={resetPasswordLoading}
variant="outline" variant="outline"
className="border-2 border-[#E07A5F] text-[#E07A5F] hover:bg-[#FFF3E0] rounded-full px-4 py-2 disabled:opacity-50" className="border-2 border-[#664fa3] text-[#664fa3] hover:bg-[#f1eef9] rounded-full px-4 py-2 disabled:opacity-50"
> >
<Lock className="h-4 w-4 mr-2" /> <Lock className="h-4 w-4 mr-2" />
{resetPasswordLoading ? 'Resetting...' : 'Reset Password'} {resetPasswordLoading ? 'Resetting...' : 'Reset Password'}
@@ -155,14 +155,14 @@ const AdminUserView = () => {
onClick={handleResendVerification} onClick={handleResendVerification}
disabled={resendVerificationLoading} disabled={resendVerificationLoading}
variant="outline" variant="outline"
className="border-2 border-[#E07A5F] text-[#E07A5F] hover:bg-[#FFF3E0] rounded-full px-4 py-2 disabled:opacity-50" className="border-2 border-[#ff9e77] text-[#ff9e77] hover:bg-[#FFF3E0] rounded-full px-4 py-2 disabled:opacity-50"
> >
<Mail className="h-4 w-4 mr-2" /> <Mail className="h-4 w-4 mr-2" />
{resendVerificationLoading ? 'Sending...' : 'Resend Verification Email'} {resendVerificationLoading ? 'Sending...' : 'Resend Verification Email'}
</Button> </Button>
)} )}
<div className="flex items-center gap-2 text-sm text-[#6B708D] ml-2"> <div className="flex items-center gap-2 text-sm text-[#664fa3] ml-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<AlertTriangle className="h-4 w-4" /> <AlertTriangle className="h-4 w-4" />
<span>User will receive a temporary password via email</span> <span>User will receive a temporary password via email</span>
</div> </div>
@@ -170,28 +170,28 @@ const AdminUserView = () => {
</Card> </Card>
{/* Additional Details */} {/* Additional Details */}
<Card className="p-8 bg-white rounded-2xl border border-[#EAE0D5]"> <Card className="p-8 bg-white rounded-2xl border border-[#ddd8eb]">
<h2 className="text-2xl font-semibold fraunces text-[#3D405B] mb-6"> <h2 className="text-2xl font-semibold text-[#422268] mb-6" style={{ fontFamily: "'Inter', sans-serif" }}>
Additional Information Additional Information
</h2> </h2>
<div className="grid md:grid-cols-2 gap-6"> <div className="grid md:grid-cols-2 gap-6">
<div> <div>
<label className="text-sm font-medium text-[#6B708D]">Address</label> <label className="text-sm font-medium text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Address</label>
<p className="text-[#3D405B] mt-1">{user.address}</p> <p className="text-[#422268] mt-1" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>{user.address}</p>
</div> </div>
<div> <div>
<label className="text-sm font-medium text-[#6B708D]">Date of Birth</label> <label className="text-sm font-medium text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Date of Birth</label>
<p className="text-[#3D405B] mt-1"> <p className="text-[#422268] mt-1" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{new Date(user.date_of_birth).toLocaleDateString()} {new Date(user.date_of_birth).toLocaleDateString()}
</p> </p>
</div> </div>
{user.partner_first_name && ( {user.partner_first_name && (
<div> <div>
<label className="text-sm font-medium text-[#6B708D]">Partner</label> <label className="text-sm font-medium text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Partner</label>
<p className="text-[#3D405B] mt-1"> <p className="text-[#422268] mt-1" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{user.partner_first_name} {user.partner_last_name} {user.partner_first_name} {user.partner_last_name}
</p> </p>
</div> </div>
@@ -199,14 +199,14 @@ const AdminUserView = () => {
{user.referred_by_member_name && ( {user.referred_by_member_name && (
<div> <div>
<label className="text-sm font-medium text-[#6B708D]">Referred By</label> <label className="text-sm font-medium text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Referred By</label>
<p className="text-[#3D405B] mt-1">{user.referred_by_member_name}</p> <p className="text-[#422268] mt-1" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>{user.referred_by_member_name}</p>
</div> </div>
)} )}
{user.lead_sources && user.lead_sources.length > 0 && ( {user.lead_sources && user.lead_sources.length > 0 && (
<div className="md:col-span-2"> <div className="md:col-span-2">
<label className="text-sm font-medium text-[#6B708D]">Lead Sources</label> <label className="text-sm font-medium text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Lead Sources</label>
<div className="flex flex-wrap gap-2 mt-2"> <div className="flex flex-wrap gap-2 mt-2">
{user.lead_sources.map((source, idx) => ( {user.lead_sources.map((source, idx) => (
<Badge key={idx} variant="outline">{source}</Badge> <Badge key={idx} variant="outline">{source}</Badge>
@@ -219,12 +219,12 @@ const AdminUserView = () => {
{/* Subscription Info (if applicable) */} {/* Subscription Info (if applicable) */}
{user.role === 'member' && ( {user.role === 'member' && (
<Card className="p-8 bg-white rounded-2xl border border-[#EAE0D5] mt-8"> <Card className="p-8 bg-white rounded-2xl border border-[#ddd8eb] mt-8">
<h2 className="text-2xl font-semibold fraunces text-[#3D405B] mb-6"> <h2 className="text-2xl font-semibold text-[#422268] mb-6" style={{ fontFamily: "'Inter', sans-serif" }}>
Subscription Information Subscription Information
</h2> </h2>
{/* TODO: Fetch and display subscription data */} {/* TODO: Fetch and display subscription data */}
<p className="text-[#6B708D]">Subscription details coming soon...</p> <p className="text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Subscription details coming soon...</p>
</Card> </Card>
)} )}
</> </>

View File

@@ -59,12 +59,12 @@ const AdminUsers = () => {
const getStatusBadge = (status) => { const getStatusBadge = (status) => {
const config = { const config = {
pending_email: { label: 'Pending Email', className: 'bg-[#F2CC8F] text-[#3D405B]' }, pending_email: { label: 'Pending Email', className: 'bg-orange-100 text-orange-700' },
pending_approval: { label: 'Pending Approval', className: 'bg-[#A3B1C6] text-white' }, pending_approval: { label: 'Pending Approval', className: 'bg-gray-200 text-gray-700' },
pre_approved: { label: 'Pre-Approved', className: 'bg-[#81B29A] text-white' }, pre_approved: { label: 'Pre-Approved', className: 'bg-[#81B29A] text-white' },
payment_pending: { label: 'Payment Pending', className: 'bg-[#E07A5F] text-white' }, payment_pending: { label: 'Payment Pending', className: 'bg-orange-500 text-white' },
active: { label: 'Active', className: 'bg-[#81B29A] text-white' }, active: { label: 'Active', className: 'bg-[#81B29A] text-white' },
inactive: { label: 'Inactive', className: 'bg-[#6B708D] text-white' } inactive: { label: 'Inactive', className: 'bg-gray-400 text-white' }
}; };
const statusConfig = config[status] || config.inactive; const statusConfig = config[status] || config.inactive;
@@ -97,29 +97,29 @@ const AdminUsers = () => {
return ( return (
<> <>
<div className="mb-8"> <div className="mb-8">
<h1 className="text-4xl md:text-5xl font-semibold fraunces text-[#3D405B] mb-4"> <h1 className="text-4xl md:text-5xl font-semibold text-[#422268] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}>
User Management User Management
</h1> </h1>
<p className="text-lg text-[#6B708D]"> <p className="text-lg text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
View and manage all registered users. View and manage all registered users.
</p> </p>
</div> </div>
{/* Filters */} {/* Filters */}
<Card className="p-6 bg-white rounded-2xl border border-[#EAE0D5] mb-8"> <Card className="p-6 bg-white rounded-2xl border border-[#ddd8eb] mb-8">
<div className="grid md:grid-cols-2 gap-4"> <div className="grid md:grid-cols-2 gap-4">
<div className="relative"> <div className="relative">
<Search className="absolute left-4 top-1/2 transform -translate-y-1/2 h-5 w-5 text-[#6B708D]" /> <Search className="absolute left-4 top-1/2 transform -translate-y-1/2 h-5 w-5 text-[#664fa3]" />
<Input <Input
placeholder="Search by name or email..." placeholder="Search by name or email..."
value={searchQuery} value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)} onChange={(e) => setSearchQuery(e.target.value)}
className="pl-12 h-14 rounded-xl border-2 border-[#EAE0D5] focus:border-[#E07A5F]" className="pl-12 h-14 rounded-xl border-2 border-[#ddd8eb] focus:border-[#664fa3]"
data-testid="search-users-input" data-testid="search-users-input"
/> />
</div> </div>
<Select value={statusFilter} onValueChange={setStatusFilter}> <Select value={statusFilter} onValueChange={setStatusFilter}>
<SelectTrigger className="h-14 rounded-xl border-2 border-[#EAE0D5]" data-testid="status-filter-select"> <SelectTrigger className="h-14 rounded-xl border-2 border-[#ddd8eb]" data-testid="status-filter-select">
<SelectValue placeholder="Filter by status" /> <SelectValue placeholder="Filter by status" />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
@@ -138,25 +138,25 @@ const AdminUsers = () => {
{/* Users List */} {/* Users List */}
{loading ? ( {loading ? (
<div className="text-center py-20"> <div className="text-center py-20">
<p className="text-[#6B708D]">Loading users...</p> <p className="text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Loading users...</p>
</div> </div>
) : filteredUsers.length > 0 ? ( ) : filteredUsers.length > 0 ? (
<div className="space-y-4"> <div className="space-y-4">
{filteredUsers.map((user) => ( {filteredUsers.map((user) => (
<Card <Card
key={user.id} key={user.id}
className="p-6 bg-white rounded-2xl border border-[#EAE0D5] hover:shadow-md transition-shadow" className="p-6 bg-white rounded-2xl border border-[#ddd8eb] hover:shadow-md transition-shadow"
data-testid={`user-card-${user.id}`} data-testid={`user-card-${user.id}`}
> >
<div className="flex justify-between items-start flex-wrap gap-4"> <div className="flex justify-between items-start flex-wrap gap-4">
<div className="flex-1"> <div className="flex-1">
<div className="flex items-center gap-3 mb-2"> <div className="flex items-center gap-3 mb-2">
<h3 className="text-xl font-semibold fraunces text-[#3D405B]"> <h3 className="text-xl font-semibold text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}>
{user.first_name} {user.last_name} {user.first_name} {user.last_name}
</h3> </h3>
{getStatusBadge(user.status)} {getStatusBadge(user.status)}
</div> </div>
<div className="grid md:grid-cols-2 gap-2 text-sm text-[#6B708D]"> <div className="grid md:grid-cols-2 gap-2 text-sm text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p>Email: {user.email}</p> <p>Email: {user.email}</p>
<p>Phone: {user.phone}</p> <p>Phone: {user.phone}</p>
<p>Role: <span className="capitalize">{user.role}</span></p> <p>Role: <span className="capitalize">{user.role}</span></p>
@@ -171,7 +171,7 @@ const AdminUsers = () => {
onClick={() => navigate(`/admin/users/${user.id}`)} onClick={() => navigate(`/admin/users/${user.id}`)}
variant="ghost" variant="ghost"
size="sm" size="sm"
className="text-[#6B708D] hover:text-[#3D405B]" className="text-[#664fa3] hover:text-[#422268]"
> >
<Eye className="h-4 w-4 mr-1" /> <Eye className="h-4 w-4 mr-1" />
View View
@@ -183,7 +183,7 @@ const AdminUsers = () => {
disabled={resendingUserId === user.id} disabled={resendingUserId === user.id}
variant="ghost" variant="ghost"
size="sm" size="sm"
className="text-[#E07A5F] hover:text-[#D0694E]" className="text-[#ff9e77] hover:text-[#664fa3]"
> >
<Mail className="h-4 w-4 mr-1" /> <Mail className="h-4 w-4 mr-1" />
{resendingUserId === user.id ? 'Sending...' : 'Resend Verification'} {resendingUserId === user.id ? 'Sending...' : 'Resend Verification'}
@@ -196,11 +196,11 @@ const AdminUsers = () => {
</div> </div>
) : ( ) : (
<div className="text-center py-20"> <div className="text-center py-20">
<Users className="h-20 w-20 text-[#EAE0D5] mx-auto mb-6" /> <Users className="h-20 w-20 text-[#ddd8eb] mx-auto mb-6" />
<h3 className="text-2xl font-semibold fraunces text-[#3D405B] mb-4"> <h3 className="text-2xl font-semibold text-[#422268] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}>
No Users Found No Users Found
</h3> </h3>
<p className="text-[#6B708D]"> <p className="text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{searchQuery || statusFilter !== 'all' {searchQuery || statusFilter !== 'all'
? 'Try adjusting your filters' ? 'Try adjusting your filters'
: 'No users registered yet'} : 'No users registered yet'}