theme-provider #16

Merged
andika merged 13 commits from theme-provider into dev 2026-01-16 10:40:04 +00:00
14 changed files with 85 additions and 68 deletions
Showing only changes of commit 4ccaca192d - Show all commits

View File

@@ -16,7 +16,7 @@ const badgeVariants = cva(
"border-transparent bg-destructive text-destructive-foreground shadow hover:bg-destructive/80", "border-transparent bg-destructive text-destructive-foreground shadow hover:bg-destructive/80",
outline: "text-foreground", outline: "text-foreground",
green: green:
"border-transparent bg-[var(--green-light)] text-white hover:bg-[var(--green-forest)]", "border-transparent bg-[var(--green-forest)] text-white hover:bg-[var(--green-fern)]",
orange: orange:
"border-transparent bg-orange-500 text-white hover:bg-orange-500/80", "border-transparent bg-orange-500 text-white hover:bg-orange-500/80",
orange2: orange2:

View File

@@ -1,8 +1,8 @@
import * as React from "react" import * as React from "react";
import { Slot } from "@radix-ui/react-slot" import { Slot } from "@radix-ui/react-slot";
import { cva } from "class-variance-authority" import { cva } from "class-variance-authority";
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils";
const buttonVariants = cva("btn", { const buttonVariants = cva("btn", {
variants: { variants: {
@@ -11,10 +11,10 @@ const buttonVariants = cva("btn", {
secondary: "btn-secondary", secondary: "btn-secondary",
ghost: "btn-ghost", ghost: "btn-ghost",
outline: "btn-outline", outline: "btn-outline",
"outline-destructive": "btn-outline-destructive",
accent: "btn-accent", accent: "btn-accent",
destructive: "btn-destructive", destructive: "btn-destructive",
link: "btn-link", link: "btn-link",
}, },
size: { size: {
default: "btn-md", default: "btn-md",
@@ -27,18 +27,20 @@ const buttonVariants = cva("btn", {
variant: "default", variant: "default",
size: "default", size: "default",
}, },
}) });
const Button = React.forwardRef(({ className, variant, size, asChild = false, ...props }, ref) => { const Button = React.forwardRef(
const Comp = asChild ? Slot : "button" ({ className, variant, size, asChild = false, ...props }, ref) => {
return ( const Comp = asChild ? Slot : "button";
<Comp return (
className={cn(buttonVariants({ variant, size }), className)} <Comp
ref={ref} className={cn(buttonVariants({ variant, size }), className)}
{...props} ref={ref}
/> {...props}
) />
}) );
Button.displayName = "Button" }
);
Button.displayName = "Button";
export { Button, buttonVariants } export { Button, buttonVariants };

View File

@@ -210,7 +210,7 @@ const AdminBylaws = () => {
{currentBylaws.title} {currentBylaws.title}
</h3> </h3>
<div className="flex items-center gap-2 mt-1"> <div className="flex items-center gap-2 mt-1">
<Badge className="bg-[var(--green-light)] text-white"> <Badge variant={'green'} className="">
<Check className="h-3 w-3 mr-1" /> <Check className="h-3 w-3 mr-1" />
Current Version Current Version
</Badge> </Badge>
@@ -222,7 +222,7 @@ const AdminBylaws = () => {
</div> </div>
<div className="flex gap-2"> <div className="flex gap-2">
<Button <Button
variant="outline" variant="ghost"
size="sm" size="sm"
onClick={() => window.open(currentBylaws.document_url, '_blank')} onClick={() => window.open(currentBylaws.document_url, '_blank')}
className="border-brand-purple text-brand-purple " className="border-brand-purple text-brand-purple "
@@ -242,10 +242,10 @@ const AdminBylaws = () => {
)} )}
{hasPermission('bylaws.delete') && ( {hasPermission('bylaws.delete') && (
<Button <Button
variant="outline" variant="outline-destructive"
size="sm" size="sm"
onClick={() => handleDelete(currentBylaws)} onClick={() => handleDelete(currentBylaws)}
className="border-red-500 text-red-500 hover:bg-red-50" className="border-red-500 text-red-500"
> >
<Trash2 className="h-4 w-4" /> <Trash2 className="h-4 w-4" />
</Button> </Button>

View File

@@ -78,38 +78,48 @@ const AdminDashboard = () => {
{/* 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-background rounded-2xl border border-[var(--neutral-800)]" data-testid="stat-total-users"> <Card className="p-6 bg-background rounded-2xl border border-[var(--neutral-800)]" data-testid="stat-total-users">
<div className="flex items-center justify-between mb-4"> <div className='flex items-start justify-between'>
<div className="bg-[var(--neutral-800)]/20 p-3 rounded-lg"> <p className="text-3xl font-semibold text-[var(--purple-ink)] mb-1" style={{ fontFamily: "'Inter', sans-serif" }}>
<Users className="h-6 w-6 text-brand-purple" /> {loading ? '-' : stats.totalMembers}
</p>
<div className="flex items-start justify-between mb-4">
<div className="bg-[var(--neutral-800)]/20 p-3 rounded-lg">
<Users className="h-6 w-6 text-brand-purple" />
</div>
</div> </div>
</div> </div>
<p className="text-3xl font-semibold text-[var(--purple-ink)] mb-1" style={{ fontFamily: "'Inter', sans-serif" }}>
{loading ? '-' : stats.totalMembers}
</p>
<p className="text-sm text-brand-purple" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Total Members</p> <p className="text-sm text-brand-purple" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Total Members</p>
</Card> </Card>
<Card className="p-6 bg-background rounded-2xl border border-[var(--neutral-800)]" data-testid="stat-pending-validations"> <Card className="p-6 bg-background rounded-2xl border border-[var(--neutral-800)]" data-testid="stat-pending-validations">
<div className="flex items-center justify-between mb-4"> <div className='flex items-start justify-between'>
<div className=" p-3 rounded-lg"> <p className="text-3xl font-semibold text-[var(--purple-ink)] mb-1" style={{ fontFamily: "'Inter', sans-serif" }}>
<Clock className="h-6 w-6 text-orange-600" /> {loading ? '-' : stats.pendingValidations}
</p>
<div className="flex items-start justify-between mb-4">
<div className=" p-3 rounded-lg">
<Clock className="h-6 w-6 text-orange-600" />
</div>
</div> </div>
</div> </div>
<p className="text-3xl font-semibold text-[var(--purple-ink)] mb-1" style={{ fontFamily: "'Inter', sans-serif" }}>
{loading ? '-' : stats.pendingValidations}
</p>
<p className="text-sm text-brand-purple" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Pending Validations</p> <p className="text-sm text-brand-purple" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Pending Validations</p>
</Card> </Card>
<Card className="p-6 bg-background rounded-2xl border border-[var(--neutral-800)]" data-testid="stat-active-members"> <Card className="p-6 bg-background rounded-2xl border border-[var(--neutral-800)]" data-testid="stat-active-members">
<div className="flex items-center justify-between mb-4"> <div className='flex items-start justify-between'>
<div className="bg-[var(--green-light)]/20 p-3 rounded-lg"> <p className="text-3xl font-semibold text-[var(--purple-ink)] mb-1" style={{ fontFamily: "'Inter', sans-serif" }}>
<CheckCircle className="h-6 w-6 text-[var(--green-light)]" /> {loading ? '-' : stats.activeMembers}
</p>
<div className="flex items-start justify-between mb-4">
<div className="bg-[var(--green-light)]/20 p-3 rounded-lg">
<CheckCircle className="h-6 w-6 text-[var(--green-light)]" />
</div>
</div> </div>
</div> </div>
<p className="text-3xl font-semibold text-[var(--purple-ink)] mb-1" style={{ fontFamily: "'Inter', sans-serif" }}>
{loading ? '-' : stats.activeMembers}
</p>
<p className="text-sm text-brand-purple" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Active Members</p> <p className="text-sm text-brand-purple" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Active Members</p>
</Card> </Card>
</div> </div>

View File

@@ -297,8 +297,9 @@ const AdminEvents = () => {
<Calendar className="h-6 w-6 text-brand-purple " /> <Calendar className="h-6 w-6 text-brand-purple " />
</div> </div>
<Badge <Badge
className={`${event.published className={`${event.published
? 'bg-[var(--green-light)] text-white' ? 'border-transparent bg-[var(--green-light)] text-white hover:bg-[var(--green-forest)]'
: 'bg-gray-400 text-white' : 'bg-gray-400 text-white'
} px-3 py-1 rounded-full`} } px-3 py-1 rounded-full`}
> >
@@ -345,7 +346,7 @@ const AdminEvents = () => {
onClick={() => navigate(`/admin/events/${event.id}/attendance`)} onClick={() => navigate(`/admin/events/${event.id}/attendance`)}
variant="outline" variant="outline"
size="sm" size="sm"
className="w-full border-[var(--green-light)] text-[var(--green-light)] hover:bg-[var(--green-light)] hover:text-white" className="w-full border-[var(--green-light)] text-[var(--green-light)] hover:bg-[var(--green-light)] hover:text-white dark:hover:text-background"
data-testid={`mark-attendance-${event.id}`} data-testid={`mark-attendance-${event.id}`}
> >
<Users className="h-4 w-4 mr-2" /> <Users className="h-4 w-4 mr-2" />
@@ -358,7 +359,7 @@ const AdminEvents = () => {
onClick={() => togglePublish(event)} onClick={() => togglePublish(event)}
variant="outline" variant="outline"
size="sm" size="sm"
className="flex-1 border-brand-purple text-brand-purple hover:bg-brand-purple hover:text-white" className="flex-1 border-brand-purple text-brand-purple hover:bg-brand-purple hover:text-white dark:hover:bg-brand-lavender dark:hover:text-background"
data-testid={`toggle-publish-${event.id}`} data-testid={`toggle-publish-${event.id}`}
> >
{event.published ? ( {event.published ? (
@@ -377,7 +378,7 @@ const AdminEvents = () => {
onClick={() => handleEdit(event)} onClick={() => handleEdit(event)}
variant="outline" variant="outline"
size="sm" size="sm"
className="border-gray-400 text-gray-600 hover:bg-gray-400 hover:text-white" className="border-gray-400 text-gray-600 dark:text-gray-400 hover:bg-gray-400 dark:hover:text-background 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" />

View File

@@ -229,10 +229,10 @@ const AdminFinancials = () => {
)} )}
{hasPermission('financials.delete') && ( {hasPermission('financials.delete') && (
<Button <Button
variant="outline" variant="outline-destructive"
size="sm" size="sm"
onClick={() => handleDelete(report)} onClick={() => handleDelete(report)}
className="border-red-500 text-red-500 hover:bg-red-50" className=""
> >
<Trash2 className="h-4 w-4" /> <Trash2 className="h-4 w-4" />
</Button> </Button>

View File

@@ -478,7 +478,7 @@ const AdminMembers = () => {
<Button <Button
variant="outline" variant="outline"
size="sm" size="sm"
className="border-brand-purple text-brand-purple dark:bg-background dark:border-brand-lavender dark:text-brand-light-lavender hover:bg-brand-light-lavender " className=""
> >
<Eye className="h-4 w-4 mr-1" /> <Eye className="h-4 w-4 mr-1" />
View Profile View Profile

View File

@@ -266,10 +266,10 @@ const AdminNewsletters = () => {
)} )}
{hasPermission('newsletters.delete') && ( {hasPermission('newsletters.delete') && (
<Button <Button
variant="outline" variant="outline-destructive"
size="sm" size="sm"
onClick={() => handleDelete(newsletter)} onClick={() => handleDelete(newsletter)}
className="border-red-500 text-red-500 hover:bg-red-50" className=""
> >
<Trash2 className="h-4 w-4" /> <Trash2 className="h-4 w-4" />
</Button> </Button>

View File

@@ -294,7 +294,7 @@ const AdminPlans = () => {
onClick={() => handleEditPlan(plan)} onClick={() => handleEditPlan(plan)}
variant="outline" variant="outline"
size="sm" size="sm"
className="flex-1 border-brand-purple text-brand-purple hover:bg-brand-purple hover:text-white rounded-full" className="flex-1 border-brand-purple text-brand-purple hover:bg-brand-purple hover:text-white rounded-full dark:hover:text-background"
> >
<Edit className="h-4 w-4 mr-1" /> <Edit className="h-4 w-4 mr-1" />
Edit Edit
@@ -303,7 +303,7 @@ const AdminPlans = () => {
onClick={() => handleDeleteClick(plan)} onClick={() => handleDeleteClick(plan)}
variant="outline" variant="outline"
size="sm" size="sm"
className="flex-1 border-red-500 text-red-500 hover:bg-red-500 hover:text-white rounded-full" className="flex-1 border-red-500 text-red-500 hover:bg-red-500 dark:hover:bg-red-500/10 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" />

View File

@@ -291,8 +291,8 @@ const AdminStaff = () => {
onClick={() => handleToggleStatus(user.id, user.status)} onClick={() => handleToggleStatus(user.id, user.status)}
variant="outline" variant="outline"
className={`border-2 rounded-full px-4 py-2 ${user.status === 'active' className={`border-2 rounded-full px-4 py-2 ${user.status === 'active'
? 'border-orange-500 text-orange-600 hover:bg-orange-50' ? 'border-orange-500 text-orange-600 hover:bg-orange-50 dark:hover:bg-orange-600/10'
: 'border-green-500 text-green-600 hover:bg-green-50' : 'border-green-500 text-green-600 hover:bg-green-50 hover:dark:bg-green-600/10'
}`} }`}
> >
{user.status === 'active' ? ( {user.status === 'active' ? (
@@ -313,7 +313,7 @@ const AdminStaff = () => {
<Button <Button
onClick={() => handleDeleteUser(user.id, `${user.first_name} ${user.last_name}`)} onClick={() => handleDeleteUser(user.id, `${user.first_name} ${user.last_name}`)}
variant="outline" variant="outline"
className="border-2 border-red-500 text-red-600 hover:bg-red-50 rounded-full px-4 py-2" className="border-2 border-red-500 text-red-600 hover:bg-red-50 dark:hover:bg-red-600/10 rounded-full px-4 py-2"
> >
<Trash2 className="h-4 w-4 mr-2" /> <Trash2 className="h-4 w-4 mr-2" />
Delete Delete

View File

@@ -419,7 +419,7 @@ Proceed with activation?`;
<DropdownMenuTrigger asChild> <DropdownMenuTrigger asChild>
<Button <Button
disabled={exporting} disabled={exporting}
className="bg-[var(--green-light)] text-white hover:bg-[var(--green-soft)] rounded-full px-6 py-2 flex items-center gap-2" className="btn-green py-2 flex items-center gap-2"
> >
<Download className="h-4 w-4" /> <Download className="h-4 w-4" />
{exporting ? 'Exporting...' : 'Export'} {exporting ? 'Exporting...' : 'Export'}
@@ -521,9 +521,9 @@ Proceed with activation?`;
{sub.status === 'active' && hasPermission('subscriptions.cancel') && ( {sub.status === 'active' && hasPermission('subscriptions.cancel') && (
<Button <Button
size="sm" size="sm"
variant="outline" variant="outline-destructive"
onClick={() => handleCancelSubscription(sub.id)} onClick={() => handleCancelSubscription(sub.id)}
className="flex-1 text-red-600 hover:bg-red-50" className="flex-1 "
> >
<XCircle className="h-4 w-4 mr-2" /> <XCircle className="h-4 w-4 mr-2" />
Cancel Cancel
@@ -626,9 +626,9 @@ Proceed with activation?`;
{sub.status === 'active' && hasPermission('subscriptions.cancel') && ( {sub.status === 'active' && hasPermission('subscriptions.cancel') && (
<Button <Button
size="sm" size="sm"
variant="outline" variant="outline-destructive"
onClick={() => handleCancelSubscription(sub.id)} onClick={() => handleCancelSubscription(sub.id)}
className="text-red-600 hover:bg-red-50" className=""
> >
<XCircle className="h-4 w-4" /> <XCircle className="h-4 w-4" />
</Button> </Button>

View File

@@ -437,7 +437,7 @@ const AdminValidations = () => {
disabled={actionLoading === user.id} disabled={actionLoading === user.id}
size="sm" size="sm"
variant="outline" variant="outline"
className="border-2 border-red-500 text-red-500 hover:bg-red-50" className="border-2 border-red-500 text-red-500 hover:bg-red-50 dark:hover:bg-red-500/10"
> >
<X className="h-4 w-4 mr-1" /> <X className="h-4 w-4 mr-1" />
Reject Reject
@@ -462,7 +462,7 @@ const AdminValidations = () => {
disabled={actionLoading === user.id} disabled={actionLoading === user.id}
size="sm" size="sm"
variant="outline" variant="outline"
className="border-2 border-red-500 text-red-500 hover:bg-red-50" className="border-2 border-red-500 text-red-500 hover:bg-red-50 dark:hover:bg-red-500/10"
> >
<X className="h-4 w-4 mr-1" /> <X className="h-4 w-4 mr-1" />
Reject Reject
@@ -487,7 +487,7 @@ const AdminValidations = () => {
disabled={actionLoading === user.id} disabled={actionLoading === user.id}
size="sm" size="sm"
variant="outline" variant="outline"
className="border-2 border-red-500 text-red-500 hover:bg-red-50" className="border-2 border-red-500 text-red-500 hover:bg-red-50 dark:hover:bg-red-500/10"
> >
<X className="h-4 w-4 mr-1" /> <X className="h-4 w-4 mr-1" />
Reject Reject

View File

@@ -17,7 +17,7 @@
} }
.btn-ghost { .btn-ghost {
@apply hover:bg-accent hover:text-accent-foreground rounded-full disabled:opacity-50 px-6 transition-transform; @apply hover:bg-brand-purple bg-brand-purple/10 rounded-full disabled:opacity-50 px-6 transition-transform text-brand-purple hover:text-[var(--purple-muted)];
} }
.btn-outline { .btn-outline {
@@ -33,7 +33,7 @@
} }
.btn-outline-destructive { .btn-outline-destructive {
@apply border border-destructive border-2 text-destructive shadow-sm hover:bg-destructive/10 dark:hover:bg-destructive/10 rounded-full disabled:opacity-50 px-6 transition-transform; @apply border border-destructive bg-none border-2 text-destructive shadow-sm hover:bg-destructive/10 dark:hover:bg-destructive/10 rounded-full disabled:opacity-50 px-6 transition-transform;
} }
.btn-link { .btn-link {
@@ -55,12 +55,15 @@
.btn-icon { .btn-icon {
@apply h-9 w-9 rounded-full disabled:opacity-50 px-6; @apply h-9 w-9 rounded-full disabled:opacity-50 px-6;
} }
.btn-green {
@apply bg-[var(--green-light)] hover:bg-[var(--green-forest)] text-white transition-transform rounded-full px-6 ;
}
.btn-util-green { .btn-util-green {
@apply bg-[var(--green-light)] hover:bg-[var(--green-forest)] text-white transition-transform rounded-xl h-12 px-6 transition-transform; @apply bg-[var(--green-light)] hover:bg-[var(--green-forest)] text-white transition-transform rounded-xl h-12 px-6 ;
} }
.btn-util-purple { .btn-util-purple {
@apply bg-[var(--purple-lavender)] transition-transform hover:bg-[var(--purple-ink-2)] text-background dark:text-white dark:hover:text-white rounded-xl h-12 px-6 transition-transform; @apply bg-[var(--purple-lavender)] hover:bg-[var(--purple-ink-2)] text-background dark:text-white dark:hover:text-white rounded-xl h-12 px-6 transition-transform;
} }
.btn-light-orange { .btn-light-orange {

View File

@@ -54,6 +54,7 @@
--blue-linkedin: #0a66c2; --blue-linkedin: #0a66c2;
--blue-facebook: #1877f2; --blue-facebook: #1877f2;
--blue-twitter: #1da1f2; --blue-twitter: #1da1f2;
--red-instagram: #e4405f;
--purple-ink: #422268; --purple-ink: #422268;
--purple-ink-2: #422268; --purple-ink-2: #422268;
--purple-deep: #48286e; --purple-deep: #48286e;
@@ -96,7 +97,6 @@
--gold-warm: #f2cc8f; --gold-warm: #f2cc8f;
--gold-soft: #e8bf7a; --gold-soft: #e8bf7a;
--gold-warm: #f2cc8f; --gold-warm: #f2cc8f;
--red-instagram: #e4405f;
--red-soft: #ffebee; --red-soft: #ffebee;
--lavender-100: #e8e0f5; --lavender-100: #e8e0f5;
--lavender-200: #eeebf4; --lavender-200: #eeebf4;
@@ -243,3 +243,4 @@
--neutral-900: #f4f4ff; /* highest contrast text */ --neutral-900: #f4f4ff; /* highest contrast text */
} }
} }