styling consistency
This commit is contained in:
@@ -13,11 +13,9 @@ const MemberFooter = () => {
|
||||
|
||||
<div className="grid md:grid-cols-5 gap-8">
|
||||
{/* Logo & About */}
|
||||
<div>
|
||||
<div className="w-40 sm:w-40 md:w-48 lg:w-[180px] flex-shrink-0">
|
||||
<img src={loafLogo} alt="LOAF Logo" className="w-full h-auto aspect-square object-contain" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Member Resources */}
|
||||
<div>
|
||||
@@ -104,7 +102,7 @@ const MemberFooter = () => {
|
||||
</div>
|
||||
|
||||
{/* donate button */}
|
||||
<div className="flex flex-col gap-2 items-center self-start justify-center md:items-start text-left w-full sm:w-auto sm:min-w-[200px] md:min-w-[200px] lg:min-w-[220px]">
|
||||
<div className="flex flex-col gap-2 items-center self-start justify-center md:items-start text-left w-full sm:w-auto sm:min-w-[200px] md:min-w-[200px] ">
|
||||
<div className="pb-4 w-full flex justify-center lg:justify-start">
|
||||
<Link to="/donate" className="block">
|
||||
<Button className="bg-[var(--orange-light)] hover:bg-[var(--orange-coral)] text-[var(--purple-deep)] rounded-full px-12 lg:px-16 py-6 text-lg sm:text-lg font-medium ">
|
||||
@@ -123,13 +121,14 @@ const MemberFooter = () => {
|
||||
|
||||
{/* Bottom Bar */}
|
||||
<div className="bg-gradient-to-r from-[var(--purple-deep)] to-[var(--purple-amethyst)] border-t border-[rgba(0,0,0,0.1)]">
|
||||
<div className="max-w-7xl mx-auto px-6 py-4">
|
||||
<div className="max-w-7xl mx-auto px-8 py-4 grid-cols-3">
|
||||
<div className="flex flex-col md:flex-row justify-between items-center gap-4 text-sm text-gray-300" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
|
||||
<div className="flex gap-6">
|
||||
<a href="/membership/terms-of-service" className="hover:text-white transition-colors">Terms of Service</a>
|
||||
<a href="/membership/privacy-policy" className="hover:text-white transition-colors">Privacy Policy</a>
|
||||
</div>
|
||||
<p>© {new Date().getFullYear()} LOAF. All rights reserved.</p>
|
||||
<p>Designed and Managed by <a href='https://konceptkit.com' target='_blank' className='font-bold text-white hover:underline cursor-pointer'>Konceptkit</a></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -7,7 +7,7 @@ import { cn } from "@/lib/utils";
|
||||
const buttonVariants = cva("btn", {
|
||||
variants: {
|
||||
variant: {
|
||||
default: "btn-primary",
|
||||
default: "btn-purple",
|
||||
secondary: "btn-secondary",
|
||||
ghost: "btn-ghost",
|
||||
outline: "btn-outline",
|
||||
@@ -15,6 +15,7 @@ const buttonVariants = cva("btn", {
|
||||
accent: "btn-accent",
|
||||
destructive: "btn-destructive",
|
||||
link: "btn-link",
|
||||
green: 'btn-green'
|
||||
},
|
||||
size: {
|
||||
default: "btn-md",
|
||||
|
||||
@@ -298,7 +298,7 @@ const AdminDonations = () => {
|
||||
placeholder="Search by donor name or email..."
|
||||
value={searchQuery}
|
||||
onChange={(e) => setSearchQuery(e.target.value)}
|
||||
className="pl-10 rounded-full border-2 border-[var(--neutral-800)] focus:border-brand-purple "
|
||||
className="pl-10 rounded-xl border-2 border-[var(--neutral-800)] focus:border-brand-purple "
|
||||
/>
|
||||
</div>
|
||||
{hasPermission('donations.export') && (
|
||||
@@ -336,7 +336,7 @@ const AdminDonations = () => {
|
||||
<div className="grid grid-cols-1 md:grid-cols-4 gap-4">
|
||||
<div>
|
||||
<Select value={typeFilter} onValueChange={setTypeFilter}>
|
||||
<SelectTrigger className="rounded-full border-2 border-[var(--neutral-800)]">
|
||||
<SelectTrigger className="rounded-xl border-2 border-[var(--neutral-800)]">
|
||||
<SelectValue placeholder="All Types" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
@@ -349,7 +349,7 @@ const AdminDonations = () => {
|
||||
|
||||
<div>
|
||||
<Select value={statusFilter} onValueChange={setStatusFilter}>
|
||||
<SelectTrigger className="rounded-full border-2 border-[var(--neutral-800)]">
|
||||
<SelectTrigger className="rounded-xl border-2 border-[var(--neutral-800)]">
|
||||
<SelectValue placeholder="All Statuses" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
@@ -366,7 +366,7 @@ const AdminDonations = () => {
|
||||
type="date"
|
||||
value={startDate}
|
||||
onChange={(e) => setStartDate(e.target.value)}
|
||||
className="rounded-full border-2 border-[var(--neutral-800)]"
|
||||
className="rounded-xl border-2 border-[var(--neutral-800)]"
|
||||
placeholder="Start Date"
|
||||
/>
|
||||
</div>
|
||||
@@ -376,7 +376,7 @@ const AdminDonations = () => {
|
||||
type="date"
|
||||
value={endDate}
|
||||
onChange={(e) => setEndDate(e.target.value)}
|
||||
className="rounded-full border-2 border-[var(--neutral-800)]"
|
||||
className="rounded-xl border-2 border-[var(--neutral-800)]"
|
||||
placeholder="End Date"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -354,42 +354,11 @@ const AdminMembers = () => {
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex gap-3 flex-wrap ">
|
||||
{hasPermission('users.export') && (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button
|
||||
className="btn-util-purple "
|
||||
disabled={exporting}
|
||||
>
|
||||
{exporting ? (
|
||||
<>
|
||||
<Download className="h-5 w-5 mr-2 animate-bounce" />
|
||||
Exporting...
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<FileDown className="h-5 w-5 mr-2" />
|
||||
Export
|
||||
<ChevronDown className="h-4 w-4 ml-2" />
|
||||
</>
|
||||
)}
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent className="rounded-xl">
|
||||
<DropdownMenuItem onClick={() => handleExport('all')} className="cursor-pointer">
|
||||
Export All Members
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem onClick={() => handleExport('current')} className="cursor-pointer">
|
||||
Export Current View
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
)}
|
||||
|
||||
{hasPermission('users.import') && (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button className="btn-util-green">
|
||||
<Button className="">
|
||||
<Upload className="h-5 w-5 mr-2" />
|
||||
Import
|
||||
<ChevronDown className="h-4 w-4 ml-2" />
|
||||
@@ -440,7 +409,9 @@ const AdminMembers = () => {
|
||||
{hasPermission('users.invite') && (
|
||||
<Button
|
||||
onClick={() => setInviteDialogOpen(true)}
|
||||
className="btn-util-purple "
|
||||
className=" "
|
||||
variant='outline'
|
||||
|
||||
>
|
||||
<Mail className="h-5 w-5 mr-2" />
|
||||
Invite Member
|
||||
@@ -450,7 +421,8 @@ const AdminMembers = () => {
|
||||
{hasPermission('users.create') && (
|
||||
<Button
|
||||
onClick={() => setCreateDialogOpen(true)}
|
||||
className="btn-util-green "
|
||||
className=""
|
||||
variant='outline'
|
||||
>
|
||||
<UserPlus className="h-5 w-5 mr-2" />
|
||||
Create Member
|
||||
@@ -499,19 +471,20 @@ const AdminMembers = () => {
|
||||
|
||||
{/* Filters */}
|
||||
<Card className="p-6 bg-background rounded-2xl border border-[var(--neutral-800)] mb-8">
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div className="relative">
|
||||
<Search className="absolute left-4 top-1/2 transform -translate-y-1/2 h-5 w-5 text-brand-purple " />
|
||||
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 text-brand-purple " />
|
||||
<Input
|
||||
placeholder="Search by name or email..."
|
||||
value={searchQuery}
|
||||
onChange={(e) => setSearchQuery(e.target.value)}
|
||||
className="pl-12 h-14 rounded-xl border-2 border-[var(--neutral-800)] focus:border-brand-purple "
|
||||
className="pl-12 rounded-xl border-2 border-[var(--neutral-800)] focus:border-brand-purple "
|
||||
data-testid="search-members-input"
|
||||
/>
|
||||
</div>
|
||||
<Select value={statusFilter} onValueChange={setStatusFilter}>
|
||||
<SelectTrigger className="h-14 rounded-xl border-2 border-[var(--neutral-800)]" data-testid="status-filter-select">
|
||||
<SelectTrigger className=" rounded-xl border-2 border-[var(--neutral-800)]" data-testid="status-filter-select">
|
||||
<SelectValue placeholder="Filter by status" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
@@ -525,6 +498,41 @@ const AdminMembers = () => {
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
<div className='mt-4 flex items-center justify-between'>
|
||||
<div className='text-sm text-brand-purple pl-2'>Showing {filteredUsers.length} of {users.length} </div>
|
||||
{hasPermission('users.export') && (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button
|
||||
className="btn-green "
|
||||
disabled={exporting}
|
||||
>
|
||||
{exporting ? (
|
||||
<>
|
||||
<Download className=" b h-5 w-5 mr-2 animate-bounce" />
|
||||
Exporting...
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Download className="h-5 w-5 mr-2" />
|
||||
Export
|
||||
<ChevronDown className=" h-4 w-4 ml-2" />
|
||||
</>
|
||||
)}
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent className="rounded-xl">
|
||||
<DropdownMenuItem onClick={() => handleExport('all')} className="cursor-pointer">
|
||||
Export All Members
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem onClick={() => handleExport('current')} className="cursor-pointer">
|
||||
Export Current View
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
)}
|
||||
</div>
|
||||
|
||||
</Card>
|
||||
|
||||
{/* Import Job Quick Select */}
|
||||
@@ -654,11 +662,10 @@ const AdminMembers = () => {
|
||||
return (
|
||||
<Card
|
||||
key={user.id}
|
||||
className={`p-6 bg-background rounded-2xl border hover:shadow-md transition-shadow ${
|
||||
selectedUsers.has(user.id)
|
||||
? 'border-brand-purple bg-[var(--lavender-500)]'
|
||||
: 'border-[var(--neutral-800)]'
|
||||
}`}
|
||||
className={`p-6 bg-background rounded-2xl border hover:shadow-md transition-shadow ${selectedUsers.has(user.id)
|
||||
? 'border-brand-purple bg-[var(--lavender-500)]'
|
||||
: 'border-[var(--neutral-800)]'
|
||||
}`}
|
||||
data-testid={`member-card-${user.id}`}
|
||||
>
|
||||
<div className="flex justify-between items-start flex-wrap gap-4">
|
||||
|
||||
@@ -195,11 +195,11 @@ const AdminPlans = () => {
|
||||
placeholder="Search plans..."
|
||||
value={searchQuery}
|
||||
onChange={(e) => setSearchQuery(e.target.value)}
|
||||
className="pl-12 h-14 rounded-xl border-2 border-[var(--neutral-800)] focus:border-brand-purple "
|
||||
className="pl-12 rounded-xl border-2 border-[var(--neutral-800)] focus:border-brand-purple "
|
||||
/>
|
||||
</div>
|
||||
<Select value={activeFilter} onValueChange={setActiveFilter}>
|
||||
<SelectTrigger className="h-14 rounded-xl border-2 border-[var(--neutral-800)]">
|
||||
<SelectTrigger className="rounded-xl border-2 border-[var(--neutral-800)]">
|
||||
<SelectValue placeholder="Filter by status" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
@@ -208,6 +208,13 @@ const AdminPlans = () => {
|
||||
<SelectItem value="inactive">Inactive Only</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
|
||||
<div className="mt-4 flex items-center justify-between">
|
||||
<div className="text-sm text-brand-purple " style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
|
||||
Showing {filteredPlans.length} of {plans.length} plans
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
|
||||
@@ -83,7 +83,7 @@ const AdminStaff = () => {
|
||||
{hasPermission('users.create') && (
|
||||
<Button
|
||||
onClick={() => setInviteDialogOpen(true)}
|
||||
className="btn-util-purple h-12 px-6"
|
||||
className="btn-purple px-6"
|
||||
>
|
||||
<Mail className="h-5 w-5 mr-2" />
|
||||
Invite Staff
|
||||
@@ -92,7 +92,7 @@ const AdminStaff = () => {
|
||||
{hasPermission('users.create') && (
|
||||
<Button
|
||||
onClick={() => setCreateDialogOpen(true)}
|
||||
className="btn-util-green h-12 px-6"
|
||||
className="btn-green px-6"
|
||||
>
|
||||
<UserPlus className="h-5 w-5 mr-2" />
|
||||
Create Staff
|
||||
@@ -163,12 +163,12 @@ const AdminStaff = () => {
|
||||
placeholder="Search by name or email..."
|
||||
value={searchQuery}
|
||||
onChange={(e) => setSearchQuery(e.target.value)}
|
||||
className="pl-12 h-14 rounded-xl border-2 border-[var(--neutral-800)] focus:border-brand-purple "
|
||||
className="pl-12 rounded-xl border-2 border-[var(--neutral-800)] focus:border-brand-purple "
|
||||
data-testid="search-staff-input"
|
||||
/>
|
||||
</div>
|
||||
<Select value={roleFilter} onValueChange={setRoleFilter}>
|
||||
<SelectTrigger className="h-14 rounded-xl border-2 border-[var(--neutral-800)]" data-testid="role-filter-select">
|
||||
<SelectTrigger className=" rounded-xl border-2 border-[var(--neutral-800)]" data-testid="role-filter-select">
|
||||
<SelectValue placeholder="Filter by role" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
@@ -180,6 +180,11 @@ const AdminStaff = () => {
|
||||
<SelectItem value="media">Media</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<div className="mt-4 flex items-center justify-between">
|
||||
<div className="text-sm text-brand-purple " style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
|
||||
Showing {filteredUsers.length} of {users.length} subscriptions
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
|
||||
@@ -338,7 +338,7 @@ Proceed with activation?`;
|
||||
{hasPermission('users.create') && (
|
||||
<Button
|
||||
onClick={() => setCreateDialogOpen(true)}
|
||||
className="btn-util-green "
|
||||
className="btn-green "
|
||||
>
|
||||
<Repeat className="h-5 w-5 mr-2" />
|
||||
Create Subscription
|
||||
@@ -459,7 +459,7 @@ Proceed with activation?`;
|
||||
</Select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* second row */}
|
||||
<div className="mt-4 flex items-center justify-between">
|
||||
<div className="text-sm text-brand-purple " style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
|
||||
Showing {filteredSubscriptions.length} of {subscriptions.length} subscriptions
|
||||
@@ -496,6 +496,7 @@ Proceed with activation?`;
|
||||
</DropdownMenu>
|
||||
)}
|
||||
</div>
|
||||
|
||||
</Card>
|
||||
|
||||
{/* Subscriptions Table */}
|
||||
|
||||
@@ -407,12 +407,12 @@ const AdminValidations = () => {
|
||||
placeholder="Search by name, email, or phone..."
|
||||
value={searchQuery}
|
||||
onChange={(e) => setSearchQuery(e.target.value)}
|
||||
className="pl-12 h-14 rounded-xl border-2 border-[var(--neutral-800)] focus:border-brand-purple "
|
||||
className="pl-12 rounded-xl border-2 border-[var(--neutral-800)] focus:border-brand-purple "
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Select value={statusFilter} onValueChange={setStatusFilter}>
|
||||
<SelectTrigger className="h-14 rounded-xl border-2 border-[var(--neutral-800)]">
|
||||
<SelectTrigger className=" rounded-xl border-2 border-[var(--neutral-800)]">
|
||||
<SelectValue placeholder="Filter by status" />
|
||||
</SelectTrigger>
|
||||
<SelectContent className="">
|
||||
@@ -423,6 +423,13 @@ const AdminValidations = () => {
|
||||
<SelectItem value="rejected" >Rejected</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
|
||||
<div className="mt-4 flex items-center justify-between">
|
||||
<div className="text-sm text-brand-purple " style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
|
||||
Showing {filteredUsers.length} of {pendingUsers.length} pending validations
|
||||
</div>
|
||||
{/*TODO: <div>Export button here?</div> */}
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
}
|
||||
|
||||
.btn-outline {
|
||||
@apply border border-primary border-2 text-primary shadow-sm hover:bg-primary/10 rounded-full disabled:opacity-50 px-6 transition-transform;
|
||||
@apply border-brand-purple border-2 text-brand-purple shadow-sm hover:bg-brand-purple/10 rounded-full disabled:opacity-50 px-6 transition-transform;
|
||||
}
|
||||
|
||||
.btn-accent {
|
||||
@@ -58,6 +58,10 @@
|
||||
.btn-green {
|
||||
@apply bg-[var(--green-light)] hover:bg-[var(--green-forest)] text-white transition-transform rounded-full px-6;
|
||||
}
|
||||
|
||||
.btn-purple {
|
||||
@apply bg-brand-purple text-background shadow hover:bg-brand-purple/90 rounded-full px-6 disabled:opacity-50 px-6 transition-transform
|
||||
}
|
||||
.btn-util-green {
|
||||
@apply bg-[var(--green-light)] hover:bg-[var(--green-forest)] text-white transition-transform rounded-xl h-12 px-6;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user