Refactor Members Directory and Newsletter Archive styles to use new color palette

- Updated color classes in MembersDirectory.js to use new color variables for borders, backgrounds, and text.
- Enhanced visual consistency by replacing hardcoded colors with Tailwind CSS color utilities.
- Modified NewsletterArchive.js to align with the new design system, ensuring a cohesive look across components.
- Added new color variables in tailwind.config.js for better maintainability and scalability.
This commit is contained in:
2026-01-07 11:36:07 -06:00
parent a93e2aa863
commit 4ba44d8997
79 changed files with 2152 additions and 2033 deletions

15
.crossnote/config.js Normal file
View File

@@ -0,0 +1,15 @@
({
katexConfig: {
"macros": {}
},
mathjaxConfig: {
"tex": {},
"options": {},
"loader": {}
},
mermaidConfig: {
"startOnLoad": false
},
})

6
.crossnote/head.html Normal file
View File

@@ -0,0 +1,6 @@
<!-- The content below will be included at the end of the <head> element. -->
<script type="text/javascript">
document.addEventListener("DOMContentLoaded", function () {
// your code here
});
</script>

12
.crossnote/parser.js Normal file
View File

@@ -0,0 +1,12 @@
({
// Please visit the URL below for more information:
// https://shd101wyy.github.io/markdown-preview-enhanced/#/extend-parser
onWillParseMarkdown: async function(markdown) {
return markdown;
},
onDidParseMarkdown: async function(html) {
return html;
},
})

8
.crossnote/style.less Normal file
View File

@@ -0,0 +1,8 @@
/* Please visit the URL below for more information: */
/* https://shd101wyy.github.io/markdown-preview-enhanced/#/customize-css */
.markdown-preview.markdown-preview {
// modify your style here
// eg: background-color: blue;
}

245
README.md
View File

@@ -33,6 +33,7 @@ npm install
``` ```
**Key Dependencies:** **Key Dependencies:**
- `react@19.0.0` - UI library - `react@19.0.0` - UI library
- `react-router-dom@7.5.1` - Routing - `react-router-dom@7.5.1` - Routing
- `axios@1.8.4` - HTTP client - `axios@1.8.4` - HTTP client
@@ -57,6 +58,7 @@ REACT_APP_BACKEND_URL=http://localhost:8000
``` ```
**Important:** **Important:**
- All environment variables must start with `REACT_APP_` - All environment variables must start with `REACT_APP_`
- Restart development server after changing `.env` - Restart development server after changing `.env`
@@ -71,6 +73,7 @@ npm start
``` ```
**Development server will be available at:** **Development server will be available at:**
- Frontend: http://localhost:3000 - Frontend: http://localhost:3000
- Auto-reloads on file changes - Auto-reloads on file changes
@@ -154,31 +157,33 @@ frontend/
### Core Technologies ### Core Technologies
| Technology | Purpose | Version | | Technology | Purpose | Version |
|------------|---------|---------| | --------------- | ------------------- | ------- |
| React | UI library | 19.0.0 | | React | UI library | 19.0.0 |
| React Router | Client-side routing | 7.5.1 | | React Router | Client-side routing | 7.5.1 |
| Axios | HTTP requests | 1.8.4 | | Axios | HTTP requests | 1.8.4 |
| React Hook Form | Form handling | 7.56.2 | | React Hook Form | Form handling | 7.56.2 |
| Zod | Schema validation | 3.24.4 | | Zod | Schema validation | 3.24.4 |
| Tailwind CSS | CSS framework | 3.4.17 | | Tailwind CSS | CSS framework | 3.4.17 |
| Radix UI | Component library | Latest | | Radix UI | Component library | Latest |
| Lucide React | Icons | 0.507.0 | | Lucide React | Icons | 0.507.0 |
| Sonner | Toast notifications | 1.7.4 | | Sonner | Toast notifications | 1.7.4 |
### Key Features ### Key Features
#### Authentication System #### Authentication System
**Global Auth Context** (`src/context/AuthContext.js`): **Global Auth Context** (`src/context/AuthContext.js`):
- JWT token storage in localStorage - JWT token storage in localStorage
- Automatic token injection via Axios interceptor - Automatic token injection via Axios interceptor
- User state management - User state management
- Protected route wrapper - Protected route wrapper
**Usage:** **Usage:**
```jsx ```jsx
import { useAuth } from '../context/AuthContext'; import { useAuth } from "../context/AuthContext";
function MyComponent() { function MyComponent() {
const { user, login, logout, isAuthenticated } = useAuth(); const { user, login, logout, isAuthenticated } = useAuth();
@@ -190,6 +195,7 @@ function MyComponent() {
#### Protected Routes #### Protected Routes
**PrivateRoute Wrapper:** **PrivateRoute Wrapper:**
```jsx ```jsx
<Route path="/dashboard" element={ <Route path="/dashboard" element={
<PrivateRoute> <PrivateRoute>
@@ -208,61 +214,68 @@ function MyComponent() {
#### API Integration #### API Integration
**Axios Instance** (`src/utils/api.js`): **Axios Instance** (`src/utils/api.js`):
- Automatic JWT token injection - Automatic JWT token injection
- Request/response interceptors - Request/response interceptors
- Error handling - Error handling
- Base URL configuration - Base URL configuration
**Usage:** **Usage:**
```jsx ```jsx
import api from '../utils/api'; import api from "../utils/api";
// GET request // GET request
const response = await api.get('/members/profile'); const response = await api.get("/members/profile");
// POST request with data // POST request with data
const response = await api.post('/admin/users/123/reject', { const response = await api.post("/admin/users/123/reject", {
reason: 'Incomplete application' reason: "Incomplete application",
}); });
// File upload // File upload
const formData = new FormData(); const formData = new FormData();
formData.append('file', file); formData.append("file", file);
const response = await api.post('/members/profile/upload-photo', formData, { const response = await api.post("/members/profile/upload-photo", formData, {
headers: { 'Content-Type': 'multipart/form-data' } headers: { "Content-Type": "multipart/form-data" },
}); });
``` ```
#### Form Handling #### Form Handling
**React Hook Form + Zod Pattern:** **React Hook Form + Zod Pattern:**
```jsx ```jsx
import { useForm } from 'react-hook-form'; import { useForm } from "react-hook-form";
import { zodResolver } from '@hookform/resolvers/zod'; import { zodResolver } from "@hookform/resolvers/zod";
import { z } from 'zod'; import { z } from "zod";
const schema = z.object({ const schema = z.object({
email: z.string().email('Invalid email address'), email: z.string().email("Invalid email address"),
password: z.string().min(8, 'Password must be at least 8 characters') password: z.string().min(8, "Password must be at least 8 characters"),
}); });
function LoginForm() { function LoginForm() {
const { register, handleSubmit, formState: { errors } } = useForm({ const {
resolver: zodResolver(schema) register,
handleSubmit,
formState: { errors },
} = useForm({
resolver: zodResolver(schema),
}); });
const onSubmit = async (data) => { const onSubmit = async (data) => {
try { try {
await api.post('/auth/login', data); await api.post("/auth/login", data);
toast.success('Login successful!'); toast.success("Login successful!");
} catch (error) { } catch (error) {
toast.error(error.response?.data?.detail || 'Login failed'); toast.error(error.response?.data?.detail || "Login failed");
} }
}; };
return ( return (
<form onSubmit={handleSubmit(onSubmit)}> <form onSubmit={handleSubmit(onSubmit)}>
<Input {...register('email')} /> <Input {...register("email")} />
{errors.email && <span>{errors.email.message}</span>} {errors.email && <span>{errors.email.message}</span>}
{/* ... */} {/* ... */}
</form> </form>
@@ -273,18 +286,19 @@ function LoginForm() {
#### Toast Notifications #### Toast Notifications
**Using Sonner:** **Using Sonner:**
```jsx ```jsx
import { toast } from 'sonner'; import { toast } from "sonner";
// Success // Success
toast.success('Profile updated successfully!'); toast.success("Profile updated successfully!");
// Error // Error
toast.error('Failed to upload photo'); toast.error("Failed to upload photo");
// Custom // Custom
toast('Processing...', { toast("Processing...", {
description: 'Please wait while we process your request' description: "Please wait while we process your request",
}); });
``` ```
@@ -293,11 +307,13 @@ toast('Processing...', {
#### Public Pages #### Public Pages
**Landing.js** **Landing.js**
- Hero section with LOAF branding - Hero section with LOAF branding
- Feature highlights - Feature highlights
- Call-to-action buttons - Call-to-action buttons
**Register.js** (388 lines) **Register.js** (388 lines)
- 4-step registration wizard: - 4-step registration wizard:
1. Basic Info (email, password, name) 1. Basic Info (email, password, name)
2. Personal Details (phone, address, DOB) 2. Personal Details (phone, address, DOB)
@@ -308,6 +324,7 @@ toast('Processing...', {
- Email verification trigger - Email verification trigger
**Login.js** **Login.js**
- Email/password authentication - Email/password authentication
- JWT token storage - JWT token storage
- Remember me functionality - Remember me functionality
@@ -316,12 +333,14 @@ toast('Processing...', {
#### Member Pages #### Member Pages
**Dashboard.js** **Dashboard.js**
- Welcome message with user name - Welcome message with user name
- Upcoming events preview - Upcoming events preview
- Membership status card - Membership status card
- Quick action buttons - Quick action buttons
**Profile.js** **Profile.js**
- Profile photo upload with Avatar component - Profile photo upload with Avatar component
- File validation (image types, 50MB max) - File validation (image types, 50MB max)
- Personal information editing - Personal information editing
@@ -329,12 +348,14 @@ toast('Processing...', {
- Save changes with API update - Save changes with API update
**Events.js** **Events.js**
- Event listing with filters - Event listing with filters
- Search functionality - Search functionality
- Upcoming/past events toggle - Upcoming/past events toggle
- Event cards with cover images - Event cards with cover images
**EventDetails.js** **EventDetails.js**
- Full event information - Full event information
- RSVP form (Yes/No/Maybe) - RSVP form (Yes/No/Maybe)
- Attendance confirmation - Attendance confirmation
@@ -343,36 +364,42 @@ toast('Processing...', {
#### Admin Pages #### Admin Pages
**AdminDashboard.js** **AdminDashboard.js**
- Statistics overview - Statistics overview
- Pending validations count - Pending validations count
- Recent activity feed - Recent activity feed
- Quick links to management pages - Quick links to management pages
**AdminUsers.js** **AdminUsers.js**
- User listing with search/filters - User listing with search/filters
- Status badges - Status badges
- Bulk operations - Bulk operations
- CSV export - CSV export
**AdminValidations.js** **AdminValidations.js**
- Pending user applications - Pending user applications
- Approve button - Approve button
- **Reject button with RejectionDialog** - **Reject button with RejectionDialog**
- Validation workflow management - Validation workflow management
**AdminSubscriptions.js** **AdminSubscriptions.js**
- Subscription listing - Subscription listing
- Status filters (active, cancelled, expired) - Status filters (active, cancelled, expired)
- **CSV export dropdown** (Export All / Export Current View) - **CSV export dropdown** (Export All / Export Current View)
- Edit subscription dialog - Edit subscription dialog
**AdminDonations.js** (400+ lines) **AdminDonations.js** (400+ lines)
- 4 stats cards: Total, Member, Public, Total Amount - 4 stats cards: Total, Member, Public, Total Amount
- Filters: type, status, date range, search - Filters: type, status, date range, search
- Responsive table with mobile cards - Responsive table with mobile cards
- **CSV export functionality** - **CSV export functionality**
**AdminEvents.js** **AdminEvents.js**
- Event management interface - Event management interface
- Create/edit events - Create/edit events
- Publish/unpublish toggle - Publish/unpublish toggle
@@ -385,40 +412,50 @@ toast('Processing...', {
**45+ UI Components** in `src/components/ui/`: **45+ UI Components** in `src/components/ui/`:
**Form Components:** **Form Components:**
- Button, Input, Textarea, Checkbox, Radio - Button, Input, Textarea, Checkbox, Radio
- Select, Label, Form - Select, Label, Form
**Layout Components:** **Layout Components:**
- Card, Dialog, Sheet (Drawer), Popover - Card, Dialog, Sheet (Drawer), Popover
- Dropdown Menu, Tabs, Accordion - Dropdown Menu, Tabs, Accordion
**Feedback Components:** **Feedback Components:**
- Alert, Badge, Toast (Sonner) - Alert, Badge, Toast (Sonner)
- Progress, Skeleton, Spinner - Progress, Skeleton, Spinner
**Navigation:** **Navigation:**
- Navigation Menu, Breadcrumb, Pagination - Navigation Menu, Breadcrumb, Pagination
**Usage Example:** **Usage Example:**
```jsx
import { Button } from './components/ui/button';
import { Dialog, DialogContent, DialogTitle } from './components/ui/dialog';
import { Select, SelectTrigger, SelectContent, SelectItem } from './components/ui/select';
<Button className="bg-[#664fa3] text-white"> ```jsx
Click me import { Button } from "./components/ui/button";
</Button> import { Dialog, DialogContent, DialogTitle } from "./components/ui/dialog";
import {
Select,
SelectTrigger,
SelectContent,
SelectItem,
} from "./components/ui/select";
<Button className="bg-muted-foreground foreground">Click me</Button>;
``` ```
### Admin Sidebar Features ### Admin Sidebar Features
**Logo Integration:** **Logo Integration:**
- LOAF logo in header - LOAF logo in header
- Shows logo + "Admin" text when expanded - Shows logo + "Admin" text when expanded
- Logo only when collapsed - Logo only when collapsed
- Smooth transition animations - Smooth transition animations
**Navigation Groups:** **Navigation Groups:**
- **Dashboard** (standalone) - **Dashboard** (standalone)
- **MEMBERSHIP** - Staff, Members, Validations - **MEMBERSHIP** - Staff, Members, Validations
- **FINANCIALS** - Plans, Subscriptions, Donations - **FINANCIALS** - Plans, Subscriptions, Donations
@@ -433,6 +470,7 @@ import { Select, SelectTrigger, SelectContent, SelectItem } from './components/u
### Color Palette ### Color Palette
**LOAF Brand Colors:** **LOAF Brand Colors:**
```css ```css
--primary: #422268 /* Deep Purple - Primary brand */ --primary: #422268 /* Deep Purple - Primary brand */
--secondary: #664fa3 /* Light Purple - Secondary elements */ --secondary: #664fa3 /* Light Purple - Secondary elements */
@@ -443,21 +481,24 @@ import { Select, SelectTrigger, SelectContent, SelectItem } from './components/u
``` ```
**Usage in Tailwind:** **Usage in Tailwind:**
```jsx ```jsx
<div className="bg-[#422268] text-white"> <div className="bg-primary foreground">
<h1 className="text-[#ff9e77]">Accent Text</h1> <h1 className="text-accent">Accent Text</h1>
<p className="text-[#664fa3]">Secondary Text</p> <p className="text-muted-foreground">Secondary Text</p>
</div> </div>
``` ```
### Typography ### Typography
**Font Families:** **Font Families:**
- **Headings**: 'Inter', sans-serif - **Headings**: 'Inter', sans-serif
- **Body**: 'Nunito Sans', sans-serif - **Body**: 'Nunito Sans', sans-serif
- **Code**: 'Fira Code', monospace (if needed) - **Code**: 'Fira Code', monospace (if needed)
**Font Sizes:** **Font Sizes:**
```css ```css
text-xs 0.75rem (12px) text-xs 0.75rem (12px)
text-sm 0.875rem (14px) text-sm 0.875rem (14px)
@@ -469,11 +510,12 @@ text-3xl → 1.875rem (30px)
``` ```
**Usage:** **Usage:**
```jsx ```jsx
<h1 className="text-3xl font-semibold text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}> <h1 className="text-3xl font-semibold text-primary" style={{ fontFamily: "'Inter', sans-serif" }}>
Page Title Page Title
</h1> </h1>
<p className="text-base text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-base text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Body text Body text
</p> </p>
``` ```
@@ -481,6 +523,7 @@ text-3xl → 1.875rem (30px)
### Spacing System ### Spacing System
**Tailwind Spacing Scale:** **Tailwind Spacing Scale:**
```css ```css
p-2 0.5rem (8px) p-2 0.5rem (8px)
p-4 1rem (16px) p-4 1rem (16px)
@@ -493,42 +536,46 @@ gap-2, gap-4, gap-6, gap-8 (same scale for flex/grid gaps)
### Component Styling Patterns ### Component Styling Patterns
**Cards:** **Cards:**
```jsx ```jsx
<Card className="p-6 bg-white rounded-2xl border-2 border-[#ddd8eb]"> <Card className="p-6 bg-background rounded-2xl border-2 border-muted">
{/* Content */} {/* Content */}
</Card> </Card>
``` ```
**Buttons:** **Buttons:**
```jsx ```jsx
// Primary // Primary
<Button className="bg-[#664fa3] text-white hover:bg-[#422268] rounded-full px-6 py-3"> <Button className="bg-muted-foreground foreground hover:bg-primary rounded-full px-6 py-3">
Primary Action Primary Action
</Button> </Button>
// Secondary // Secondary
<Button variant="outline" className="border-2 border-[#ddd8eb] text-[#664fa3] hover:bg-[#f1eef9] rounded-full"> <Button variant="outline" className="border-2 border-muted text-muted-foreground hover:bg-muted rounded-full">
Secondary Action Secondary Action
</Button> </Button>
// Destructive // Destructive
<Button className="bg-red-600 text-white hover:bg-red-700 rounded-full"> <Button className="bg-red-600 foreground hover:bg-red-700 rounded-full">
Delete Delete
</Button> </Button>
``` ```
**Form Inputs:** **Form Inputs:**
```jsx ```jsx
<Input className="rounded-xl border-2 border-[#ddd8eb] focus:border-[#664fa3]" /> <Input className="rounded-xl border-2 border-muted focus:border-muted-foreground" />
<Textarea className="rounded-xl border-2 border-[#ddd8eb] min-h-[120px]" /> <Textarea className="rounded-xl border-2 border-muted min-h-[120px]" />
<Select> <Select>
<SelectTrigger className="rounded-xl border-2 border-[#ddd8eb]"> <SelectTrigger className="rounded-xl border-2 border-muted">
<SelectValue placeholder="Select..." /> <SelectValue placeholder="Select..." />
</SelectTrigger> </SelectTrigger>
</Select> </Select>
``` ```
**Badges:** **Badges:**
```jsx ```jsx
// Status badges // Status badges
<Badge className="bg-green-100 text-green-800">Active</Badge> <Badge className="bg-green-100 text-green-800">Active</Badge>
@@ -539,22 +586,38 @@ gap-2, gap-4, gap-6, gap-8 (same scale for flex/grid gaps)
### Icons (Lucide React) ### Icons (Lucide React)
**Common Icons:** **Common Icons:**
```jsx ```jsx
import { import {
User, Mail, Phone, MapPin, // User info User,
Calendar, Clock, CheckCircle, // Status Mail,
Edit, Trash2, X, Check, // Actions Phone,
Search, Filter, Download, // Utilities MapPin, // User info
Heart, DollarSign, CreditCard, // Financial Calendar,
AlertTriangle, Info, HelpCircle // Alerts Clock,
} from 'lucide-react'; CheckCircle, // Status
Edit,
Trash2,
X,
Check, // Actions
Search,
Filter,
Download, // Utilities
Heart,
DollarSign,
CreditCard, // Financial
AlertTriangle,
Info,
HelpCircle, // Alerts
} from "lucide-react";
<User className="h-5 w-5 text-[#664fa3]" /> <User className="h-5 w-5 text-muted-foreground" />;
``` ```
### Responsive Design ### Responsive Design
**Breakpoints:** **Breakpoints:**
```css ```css
sm: 640px @media (min-width: 640px) sm: 640px @media (min-width: 640px)
md: 768px @media (min-width: 768px) md: 768px @media (min-width: 768px)
@@ -564,6 +627,7 @@ xl: 1280px → @media (min-width: 1280px)
``` ```
**Mobile-First Approach:** **Mobile-First Approach:**
```jsx ```jsx
<div className="flex flex-col md:flex-row gap-4"> <div className="flex flex-col md:flex-row gap-4">
{/* Stacks vertically on mobile, horizontal on tablet+ */} {/* Stacks vertically on mobile, horizontal on tablet+ */}
@@ -581,6 +645,7 @@ xl: 1280px → @media (min-width: 1280px)
### Animations ### Animations
**Tailwind Transitions:** **Tailwind Transitions:**
```jsx ```jsx
<Button className="transition-all duration-200 hover:scale-105"> <Button className="transition-all duration-200 hover:scale-105">
Hover me Hover me
@@ -600,6 +665,7 @@ xl: 1280px → @media (min-width: 1280px)
#### 1. Environment Variables #### 1. Environment Variables
Create `.env.production`: Create `.env.production`:
```bash ```bash
REACT_APP_BACKEND_URL=https://api.loaf.org REACT_APP_BACKEND_URL=https://api.loaf.org
REACT_APP_SENTRY_DSN=your-production-sentry-dsn REACT_APP_SENTRY_DSN=your-production-sentry-dsn
@@ -622,6 +688,7 @@ Build output will be in `/build` directory.
### Option A: Netlify (Recommended) ### Option A: Netlify (Recommended)
**Via Netlify CLI:** **Via Netlify CLI:**
```bash ```bash
# Install Netlify CLI # Install Netlify CLI
npm install -g netlify-cli npm install -g netlify-cli
@@ -634,6 +701,7 @@ netlify deploy --prod --dir=build
``` ```
**Via Git Integration:** **Via Git Integration:**
1. Push code to GitHub/GitLab 1. Push code to GitHub/GitLab
2. Connect repository in Netlify dashboard 2. Connect repository in Netlify dashboard
3. Configure: 3. Configure:
@@ -643,9 +711,11 @@ netlify deploy --prod --dir=build
4. Deploy automatically on push 4. Deploy automatically on push
**Configure Redirects** (`public/_redirects`): **Configure Redirects** (`public/_redirects`):
``` ```
/* /index.html 200 /* /index.html 200
``` ```
This enables client-side routing. This enables client-side routing.
### Option B: Vercel ### Option B: Vercel
@@ -659,11 +729,10 @@ vercel --prod
``` ```
**Configure** (`vercel.json`): **Configure** (`vercel.json`):
```json ```json
{ {
"rewrites": [ "rewrites": [{ "source": "/(.*)", "destination": "/index.html" }],
{ "source": "/(.*)", "destination": "/index.html" }
],
"env": { "env": {
"REACT_APP_BACKEND_URL": "https://api.loaf.org" "REACT_APP_BACKEND_URL": "https://api.loaf.org"
} }
@@ -673,11 +742,13 @@ vercel --prod
### Option C: Traditional Web Server (Nginx) ### Option C: Traditional Web Server (Nginx)
**1. Copy build files to server:** **1. Copy build files to server:**
```bash ```bash
scp -r build/* user@server:/var/www/loaf-frontend/ scp -r build/* user@server:/var/www/loaf-frontend/
``` ```
**2. Configure Nginx** (`/etc/nginx/sites-available/loaf-frontend`): **2. Configure Nginx** (`/etc/nginx/sites-available/loaf-frontend`):
```nginx ```nginx
server { server {
listen 80; listen 80;
@@ -702,6 +773,7 @@ server {
``` ```
**3. Enable site and restart:** **3. Enable site and restart:**
```bash ```bash
sudo ln -s /etc/nginx/sites-available/loaf-frontend /etc/nginx/sites-enabled/ sudo ln -s /etc/nginx/sites-available/loaf-frontend /etc/nginx/sites-enabled/
sudo nginx -t sudo nginx -t
@@ -709,6 +781,7 @@ sudo systemctl restart nginx
``` ```
**4. SSL Certificate:** **4. SSL Certificate:**
```bash ```bash
sudo certbot --nginx -d app.loaf.org sudo certbot --nginx -d app.loaf.org
``` ```
@@ -716,12 +789,14 @@ sudo certbot --nginx -d app.loaf.org
### Option D: AWS S3 + CloudFront ### Option D: AWS S3 + CloudFront
**1. Create S3 bucket:** **1. Create S3 bucket:**
```bash ```bash
aws s3 mb s3://loaf-frontend aws s3 mb s3://loaf-frontend
aws s3 sync build/ s3://loaf-frontend --delete aws s3 sync build/ s3://loaf-frontend --delete
``` ```
**2. Configure bucket for static hosting:** **2. Configure bucket for static hosting:**
```bash ```bash
aws s3 website s3://loaf-frontend --index-document index.html --error-document index.html aws s3 website s3://loaf-frontend --index-document index.html --error-document index.html
``` ```
@@ -731,25 +806,31 @@ aws s3 website s3://loaf-frontend --index-document index.html --error-document i
### Performance Optimization ### Performance Optimization
**1. Code Splitting:** **1. Code Splitting:**
```jsx ```jsx
// Lazy load routes // Lazy load routes
import { lazy, Suspense } from 'react'; import { lazy, Suspense } from "react";
const AdminDonations = lazy(() => import('./pages/admin/AdminDonations')); const AdminDonations = lazy(() => import("./pages/admin/AdminDonations"));
<Route path="/admin/donations" element={ <Route
<Suspense fallback={<div>Loading...</div>}> path="/admin/donations"
<AdminDonations /> element={
</Suspense> <Suspense fallback={<div>Loading...</div>}>
} /> <AdminDonations />
</Suspense>
}
/>;
``` ```
**2. Image Optimization:** **2. Image Optimization:**
- Use WebP format when possible - Use WebP format when possible
- Compress images before upload - Compress images before upload
- Use lazy loading: `loading="lazy"` - Use lazy loading: `loading="lazy"`
**3. Bundle Analysis:** **3. Bundle Analysis:**
```bash ```bash
# Install analyzer # Install analyzer
yarn add --dev webpack-bundle-analyzer yarn add --dev webpack-bundle-analyzer
@@ -762,6 +843,7 @@ npx webpack-bundle-analyzer build/static/js/*.js
### CI/CD Pipeline ### CI/CD Pipeline
**GitHub Actions** (`.github/workflows/deploy.yml`): **GitHub Actions** (`.github/workflows/deploy.yml`):
```yaml ```yaml
name: Deploy Frontend name: Deploy Frontend
@@ -776,7 +858,7 @@ jobs:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- uses: actions/setup-node@v3 - uses: actions/setup-node@v3
with: with:
node-version: '18' node-version: "18"
- run: yarn install - run: yarn install
- run: yarn build - run: yarn build
env: env:
@@ -800,6 +882,7 @@ jobs:
**Error:** `Module not found: Can't resolve 'package-name'` **Error:** `Module not found: Can't resolve 'package-name'`
**Solution:** **Solution:**
```bash ```bash
# Clear node_modules and reinstall # Clear node_modules and reinstall
rm -rf node_modules yarn.lock rm -rf node_modules yarn.lock
@@ -815,6 +898,7 @@ npm install
**Error:** `Access to XMLHttpRequest blocked by CORS policy` **Error:** `Access to XMLHttpRequest blocked by CORS policy`
**Solution:** **Solution:**
- Backend must include frontend URL in CORS_ORIGINS - Backend must include frontend URL in CORS_ORIGINS
- Check REACT_APP_BACKEND_URL is correct - Check REACT_APP_BACKEND_URL is correct
- Backend: `CORS_ORIGINS=http://localhost:3000,https://app.loaf.org` - Backend: `CORS_ORIGINS=http://localhost:3000,https://app.loaf.org`
@@ -824,6 +908,7 @@ npm install
**Error:** API returns 401 after some time **Error:** API returns 401 after some time
**Solution:** **Solution:**
- JWT token expired (default 30 minutes) - JWT token expired (default 30 minutes)
- User needs to log in again - User needs to log in again
- Check token is being sent in Authorization header - Check token is being sent in Authorization header
@@ -833,6 +918,7 @@ npm install
**Error:** `process.env.REACT_APP_BACKEND_URL is undefined` **Error:** `process.env.REACT_APP_BACKEND_URL is undefined`
**Solution:** **Solution:**
- Ensure variable name starts with `REACT_APP_` - Ensure variable name starts with `REACT_APP_`
- Restart development server after changing .env - Restart development server after changing .env
- Don't commit .env to git (use .env.example) - Don't commit .env to git (use .env.example)
@@ -842,6 +928,7 @@ npm install
**Error:** `npm run build` fails with memory error **Error:** `npm run build` fails with memory error
**Solution:** **Solution:**
```bash ```bash
# Increase Node memory limit # Increase Node memory limit
NODE_OPTIONS=--max_old_space_size=4096 yarn build NODE_OPTIONS=--max_old_space_size=4096 yarn build
@@ -852,6 +939,7 @@ NODE_OPTIONS=--max_old_space_size=4096 yarn build
**Error:** Refresh on /dashboard returns 404 **Error:** Refresh on /dashboard returns 404
**Solution:** **Solution:**
- Configure server to redirect all routes to index.html - Configure server to redirect all routes to index.html
- Netlify: Add `_redirects` file - Netlify: Add `_redirects` file
- Nginx: Use `try_files $uri /index.html` - Nginx: Use `try_files $uri /index.html`
@@ -862,6 +950,7 @@ NODE_OPTIONS=--max_old_space_size=4096 yarn build
**Error:** Profile photos return 404 **Error:** Profile photos return 404
**Solution:** **Solution:**
- Check R2_PUBLIC_URL is correct in backend .env - Check R2_PUBLIC_URL is correct in backend .env
- Verify Cloudflare R2 bucket is public - Verify Cloudflare R2 bucket is public
- Check CORS settings in R2 bucket - Check CORS settings in R2 bucket
@@ -870,16 +959,16 @@ NODE_OPTIONS=--max_old_space_size=4096 yarn build
```jsx ```jsx
// Add to any component for debugging // Add to any component for debugging
console.log('Component rendered', { user, props }); console.log("Component rendered", { user, props });
// Check API responses // Check API responses
api.interceptors.response.use( api.interceptors.response.use(
response => { (response) => {
console.log('API Response:', response); console.log("API Response:", response);
return response; return response;
}, },
error => { (error) => {
console.error('API Error:', error.response); console.error("API Error:", error.response);
return Promise.reject(error); return Promise.reject(error);
} }
); );

View File

@@ -128,7 +128,7 @@ export default function AddToCalendarButton({
{event && ( {event && (
<> <>
{/* Single Event Export Options */} {/* Single Event Export Options */}
<div className="px-2 py-1.5 text-sm font-semibold text-[#422268]"> <div className="px-2 py-1.5 text-sm font-semibold text-primary">
Add This Event Add This Event
</div> </div>
@@ -137,7 +137,7 @@ export default function AddToCalendarButton({
className="cursor-pointer" className="cursor-pointer"
> >
<svg className="h-4 w-4 mr-2" viewBox="0 0 24 24" fill="currentColor"> <svg className="h-4 w-4 mr-2" viewBox="0 0 24 24" fill="currentColor">
<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1 17.93c-3.95-.49-7-3.85-7-7.93 0-.62.08-1.21.21-1.79L9 15v1c0 1.1.9 2 2 2v1.93zm6.9-2.54c-.26-.81-1-1.39-1.9-1.39h-1v-3c0-.55-.45-1-1-1H8v-2h2c.55 0 1-.45 1-1V7h2c1.1 0 2-.9 2-2v-.41c2.93 1.19 5 4.06 5 7.41 0 2.08-.8 3.97-2.1 5.39z"/> <path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1 17.93c-3.95-.49-7-3.85-7-7.93 0-.62.08-1.21.21-1.79L9 15v1c0 1.1.9 2 2 2v1.93zm6.9-2.54c-.26-.81-1-1.39-1.9-1.39h-1v-3c0-.55-.45-1-1-1H8v-2h2c.55 0 1-.45 1-1V7h2c1.1 0 2-.9 2-2v-.41c2.93 1.19 5 4.06 5 7.41 0 2.08-.8 3.97-2.1 5.39z" />
</svg> </svg>
Google Calendar Google Calendar
</DropdownMenuItem> </DropdownMenuItem>
@@ -147,7 +147,7 @@ export default function AddToCalendarButton({
className="cursor-pointer" className="cursor-pointer"
> >
<svg className="h-4 w-4 mr-2" viewBox="0 0 24 24" fill="currentColor"> <svg className="h-4 w-4 mr-2" viewBox="0 0 24 24" fill="currentColor">
<path d="M7 2h14v20H7V2zm7 11c0 2.761-2.239 5-5 5H2c-.552 0-1-.448-1-1s.448-1 1-1h7c1.657 0 3-1.343 3-3V9c0-1.657-1.343-3-3-3H2c-.552 0-1-.448-1-1s.448-1 1-1h7c2.761 0 5 2.239 5 5v4z"/> <path d="M7 2h14v20H7V2zm7 11c0 2.761-2.239 5-5 5H2c-.552 0-1-.448-1-1s.448-1 1-1h7c1.657 0 3-1.343 3-3V9c0-1.657-1.343-3-3-3H2c-.552 0-1-.448-1-1s.448-1 1-1h7c2.761 0 5 2.239 5 5v4z" />
</svg> </svg>
Outlook Web Outlook Web
</DropdownMenuItem> </DropdownMenuItem>
@@ -157,7 +157,7 @@ export default function AddToCalendarButton({
className="cursor-pointer" className="cursor-pointer"
> >
<svg className="h-4 w-4 mr-2" viewBox="0 0 24 24" fill="currentColor"> <svg className="h-4 w-4 mr-2" viewBox="0 0 24 24" fill="currentColor">
<path d="M17.05 20.28c-.98.95-2.05.8-3.08.35-1.09-.46-2.09-.48-3.24 0-1.44.62-2.2.44-3.06-.35C2.79 15.25 3.51 7.59 9.05 7.31c1.35.07 2.29.74 3.08.8 1.18-.24 2.31-.93 3.57-.84 1.51.12 2.65.72 3.4 1.8-3.12 1.87-2.38 5.98.48 7.13-.57 1.5-1.31 2.99-2.54 4.09l.01-.01zM12.03 7.25c-.15-2.23 1.66-4.07 3.74-4.25.29 2.58-2.34 4.5-3.74 4.25z"/> <path d="M17.05 20.28c-.98.95-2.05.8-3.08.35-1.09-.46-2.09-.48-3.24 0-1.44.62-2.2.44-3.06-.35C2.79 15.25 3.51 7.59 9.05 7.31c1.35.07 2.29.74 3.08.8 1.18-.24 2.31-.93 3.57-.84 1.51.12 2.65.72 3.4 1.8-3.12 1.87-2.38 5.98.48 7.13-.57 1.5-1.31 2.99-2.54 4.09l.01-.01zM12.03 7.25c-.15-2.23 1.66-4.07 3.74-4.25.29 2.58-2.34 4.5-3.74 4.25z" />
</svg> </svg>
Apple Calendar Apple Calendar
</DropdownMenuItem> </DropdownMenuItem>
@@ -177,7 +177,7 @@ export default function AddToCalendarButton({
{showSubscribe && ( {showSubscribe && (
<> <>
{/* Subscription Options */} {/* Subscription Options */}
<div className="px-2 py-1.5 text-sm font-semibold text-[#422268]"> <div className="px-2 py-1.5 text-sm font-semibold text-primary">
Calendar Feeds Calendar Feeds
</div> </div>
@@ -187,7 +187,7 @@ export default function AddToCalendarButton({
> >
<RefreshCw className="h-4 w-4 mr-2" /> <RefreshCw className="h-4 w-4 mr-2" />
Subscribe to My Events Subscribe to My Events
<div className="text-xs text-[#664fa3] mt-0.5"> <div className="text-xs text-muted-foreground mt-0.5">
Auto-syncs your RSVP'd events Auto-syncs your RSVP'd events
</div> </div>
</DropdownMenuItem> </DropdownMenuItem>
@@ -198,7 +198,7 @@ export default function AddToCalendarButton({
> >
<Download className="h-4 w-4 mr-2" /> <Download className="h-4 w-4 mr-2" />
Download All Events Download All Events
<div className="text-xs text-[#664fa3] mt-0.5"> <div className="text-xs text-muted-foreground mt-0.5">
One-time import of all upcoming events One-time import of all upcoming events
</div> </div>
</DropdownMenuItem> </DropdownMenuItem>
@@ -206,7 +206,7 @@ export default function AddToCalendarButton({
)} )}
{!event && !showSubscribe && ( {!event && !showSubscribe && (
<div className="px-2 py-6 text-center text-sm text-[#664fa3]"> <div className="px-2 py-6 text-center text-sm text-muted-foreground">
No event selected No event selected
</div> </div>
)} )}

View File

@@ -211,10 +211,10 @@ 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-[#664fa3]' ? 'opacity-50 cursor-not-allowed text-muted-foreground'
: active : active
? 'bg-[#ff9e77]/10 text-[#ff9e77]' ? 'bg-accent/10 text-accent'
: 'text-[#422268] hover:bg-[#DDD8EB]/20' : 'text-primary hover:bg-chart-6/20'
} }
`} `}
> >
@@ -229,7 +229,7 @@ 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-[#DDD8EB] text-[#422268] text-xs px-2 py-0.5"> <Badge className="bg-chart-6 text-primary text-xs px-2 py-0.5">
Soon Soon
</Badge> </Badge>
)} )}
@@ -265,7 +265,7 @@ const AdminSidebar = ({ isOpen, onToggle, isMobile }) => {
{/* Sidebar */} {/* Sidebar */}
<aside <aside
className={` className={`
bg-white border-r border-[#ddd8eb] transition-all duration-300 ease-out bg-background border-r border-chart-6 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'}
@@ -273,7 +273,7 @@ const AdminSidebar = ({ isOpen, onToggle, isMobile }) => {
`} `}
> >
{/* Header */} {/* Header */}
<div className="flex items-center justify-between p-4 border-b border-[#ddd8eb]"> <div className="flex items-center justify-between p-4 border-b border-chart-6">
<Link to="/" className="flex items-center gap-3 group flex-1 min-w-0"> <Link to="/" className="flex items-center gap-3 group flex-1 min-w-0">
<img <img
src={`${process.env.PUBLIC_URL}/loaf-logo.png`} src={`${process.env.PUBLIC_URL}/loaf-logo.png`}
@@ -294,7 +294,7 @@ const AdminSidebar = ({ isOpen, onToggle, isMobile }) => {
</Link> </Link>
<button <button
onClick={onToggle} onClick={onToggle}
className="p-2 rounded-lg hover:bg-[#DDD8EB]/20 transition-colors" className="p-2 rounded-lg hover:bg-chart-6/20 transition-colors"
aria-label={isOpen ? 'Collapse sidebar' : 'Expand sidebar'} aria-label={isOpen ? 'Collapse sidebar' : 'Expand sidebar'}
> >
{isMobile ? ( {isMobile ? (
@@ -376,11 +376,11 @@ const AdminSidebar = ({ isOpen, onToggle, isMobile }) => {
</nav> </nav>
{/* User Section */} {/* User Section */}
<div className="border-t border-[#ddd8eb] p-4 space-y-2"> <div className="border-t border-chart-6 p-4 space-y-2">
{isOpen && user && ( {isOpen && user && (
<div className="px-4 py-3 mb-2 flex justify-between items-center"> <div className="px-4 py-3 mb-2 flex justify-between items-center">
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
<div className="h-10 w-10 rounded-full bg-[#DDD8EB] flex items-center justify-center text-[#422268] font-semibold"> <div className="h-10 w-10 rounded-full bg-chart-6 flex items-center justify-center text-primary 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">
@@ -392,7 +392,7 @@ const AdminSidebar = ({ isOpen, onToggle, isMobile }) => {
</p> </p>
</div> </div>
</div> </div>
<Link to='/profile'><Settings size={16} /> <Link to='/profile' className='text-foreground'><Settings size={16} />
</Link> </Link>
</div> </div>
)} )}
@@ -427,12 +427,12 @@ const AdminSidebar = ({ isOpen, onToggle, isMobile }) => {
{/* Storage Usage Widget */} {/* Storage Usage Widget */}
<div className="mb-2"> <div className="mb-2">
{isOpen ? ( {isOpen ? (
<div className="px-4 py-3 bg-[#F8F7FB] rounded-lg"> <div className="px-4 py-3 bg-chart-7 rounded-lg">
<div className="flex items-center justify-between mb-2"> <div className="flex items-center justify-between mb-2">
<span className="text-sm font-medium text-primary">Storage Usage</span> <span className="text-sm font-medium text-primary">Storage Usage</span>
<span className="text-xs text-muted-foreground">{storagePercentage}%</span> <span className="text-xs text-muted-foreground">{storagePercentage}%</span>
</div> </div>
<div className="w-full bg-[#ddd8eb] rounded-full h-2"> <div className="w-full bg-chart-6 rounded-full h-2">
<div <div
className={`h-2 rounded-full transition-all ${storagePercentage > 90 ? 'bg-red-500' : className={`h-2 rounded-full transition-all ${storagePercentage > 90 ? 'bg-red-500' :
storagePercentage > 75 ? 'bg-yellow-500' : storagePercentage > 75 ? 'bg-yellow-500' :

View File

@@ -55,21 +55,21 @@ export const AttendanceDialog = ({ event, open, onOpenChange, onSuccess }) => {
return ( return (
<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-background">
<DialogHeader> <DialogHeader>
<DialogTitle className="text-2xl text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}> <DialogTitle className="text-2xl text-primary" 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-[#664fa3] py-8" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>No RSVPs yet</p> <p className="text-center text-muted-foreground 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-[#ddd8eb] rounded-xl hover:border-[#664fa3] transition-colors" className="flex items-center gap-3 p-4 border-2 border-chart-6 rounded-xl hover:border-muted-foreground 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-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}>{rsvp.user_name}</p> <p className="font-medium text-primary" style={{ fontFamily: "'Inter', sans-serif" }}>{rsvp.user_name}</p>
<p className="text-sm text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>{rsvp.user_email}</p> <p className="text-sm text-muted-foreground" 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-[#DDD8EB] text-[#422268] hover:bg-white rounded-full" className="flex-1 bg-chart-6 text-primary hover:bg-background 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-[#ddd8eb] text-[#664fa3] hover:bg-white hover:text-[#422268] rounded-full" className="flex-1 border-2 border-chart-6 text-muted-foreground hover:bg-background hover:text-primary rounded-full"
> >
Cancel Cancel
</Button> </Button>

View File

@@ -66,17 +66,17 @@ const ChangePasswordDialog = ({ open, onOpenChange }) => {
return ( return (
<Dialog open={open} onOpenChange={onOpenChange}> <Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent className="sm:max-w-md bg-white"> <DialogContent className="sm:max-w-md bg-background">
<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-[#f1eef9]"> <div className="inline-flex items-center justify-center w-10 h-10 rounded-full bg-muted">
<Lock className="h-5 w-5 text-[#ff9e77]" /> <Lock className="h-5 w-5 text-accent" />
</div> </div>
<DialogTitle className="text-2xl font-semibold text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}> <DialogTitle className="text-2xl font-semibold text-primary" style={{ fontFamily: "'Inter', sans-serif" }}>
Change Password Change Password
</DialogTitle> </DialogTitle>
</div> </div>
<DialogDescription className="text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <DialogDescription className="text-muted-foreground" 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-[#ddd8eb] focus:border-[#664fa3]" className="h-12 rounded-xl border-2 border-chart-6 focus:border-muted-foreground"
/> />
</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-[#ddd8eb] focus:border-[#664fa3]" className="h-12 rounded-xl border-2 border-chart-6 focus:border-muted-foreground"
/> />
</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-[#ddd8eb] focus:border-[#664fa3]" className="h-12 rounded-xl border-2 border-chart-6 focus:border-muted-foreground"
/> />
</div> </div>
@@ -136,7 +136,7 @@ const ChangePasswordDialog = ({ open, onOpenChange }) => {
<Button <Button
type="submit" type="submit"
disabled={loading} disabled={loading}
className="bg-[#DDD8EB] text-[#422268] hover:bg-white rounded-full px-6 disabled:opacity-50" className="bg-chart-6 text-primary hover:bg-background rounded-full px-6 disabled:opacity-50"
> >
{loading ? 'Changing...' : 'Change Password'} {loading ? 'Changing...' : 'Change Password'}
</Button> </Button>

View File

@@ -38,8 +38,8 @@ const ConfirmationDialog = ({
const variants = { const variants = {
warning: { warning: {
icon: AlertTriangle, icon: AlertTriangle,
iconColor: 'text-[#ff9e77]', iconColor: 'text-accent',
confirmButtonClass: 'bg-[#ff9e77] text-white hover:bg-[#e88d66] rounded-full px-6', confirmButtonClass: 'bg-accent text-white hover:bg-[#e88d66] rounded-full px-6',
}, },
danger: { danger: {
icon: AlertTriangle, icon: AlertTriangle,
@@ -48,8 +48,8 @@ const ConfirmationDialog = ({
}, },
info: { info: {
icon: Info, icon: Info,
iconColor: 'text-[#664fa3]', iconColor: 'text-muted-foreground',
confirmButtonClass: 'bg-[#664fa3] text-white hover:bg-[#553d8a] rounded-full px-6', confirmButtonClass: 'bg-muted-foreground text-white hover:bg-[#553d8a] rounded-full px-6',
}, },
success: { success: {
icon: CheckCircle, icon: CheckCircle,
@@ -63,21 +63,21 @@ const ConfirmationDialog = ({
return ( return (
<AlertDialog open={open} onOpenChange={onOpenChange}> <AlertDialog open={open} onOpenChange={onOpenChange}>
<AlertDialogContent className="bg-white rounded-2xl border border-[#ddd8eb] p-0 overflow-hidden max-w-md"> <AlertDialogContent className="bg-background rounded-2xl border border-chart-6 p-0 overflow-hidden max-w-md">
<AlertDialogHeader className="p-6 pb-4"> <AlertDialogHeader className="p-6 pb-4">
<div className="flex items-start gap-4"> <div className="flex items-start gap-4">
<div className={`p-3 rounded-full bg-[#F8F7FB] ${config.iconColor}`}> <div className={`p-3 rounded-full bg-chart-7 ${config.iconColor}`}>
<Icon className="h-6 w-6" /> <Icon className="h-6 w-6" />
</div> </div>
<div className="flex-1"> <div className="flex-1">
<AlertDialogTitle <AlertDialogTitle
className="text-xl font-semibold text-[#422268] mb-2" className="text-xl font-semibold text-primary mb-2"
style={{ fontFamily: "'Inter', sans-serif" }} style={{ fontFamily: "'Inter', sans-serif" }}
> >
{title} {title}
</AlertDialogTitle> </AlertDialogTitle>
<AlertDialogDescription <AlertDialogDescription
className="text-[#664fa3] text-sm leading-relaxed" className="text-muted-foreground text-sm leading-relaxed"
style={{ fontFamily: "'Nunito Sans', sans-serif" }} style={{ fontFamily: "'Nunito Sans', sans-serif" }}
> >
{description} {description}
@@ -85,9 +85,9 @@ const ConfirmationDialog = ({
</div> </div>
</div> </div>
</AlertDialogHeader> </AlertDialogHeader>
<AlertDialogFooter className="p-6 pt-4 bg-[#F8F7FB] flex-row gap-3 justify-end"> <AlertDialogFooter className="p-6 pt-4 bg-chart-7 flex-row gap-3 justify-end">
<AlertDialogCancel <AlertDialogCancel
className="border-2 border-[#ddd8eb] text-[#664fa3] hover:bg-white rounded-full px-6" className="border-2 border-chart-6 text-muted-foreground hover:bg-background rounded-full px-6"
disabled={loading} disabled={loading}
> >
{cancelText} {cancelText}

View File

@@ -121,11 +121,11 @@ const CreateMemberDialog = ({ open, onOpenChange, onSuccess }) => {
<Dialog open={open} onOpenChange={onOpenChange}> <Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent className="sm:max-w-[700px] rounded-2xl max-h-[90vh] overflow-y-auto"> <DialogContent className="sm:max-w-[700px] rounded-2xl max-h-[90vh] overflow-y-auto">
<DialogHeader> <DialogHeader>
<DialogTitle className="text-2xl text-[#422268] flex items-center gap-2" style={{ fontFamily: "'Inter', sans-serif" }}> <DialogTitle className="text-2xl text-primary flex items-center gap-2" style={{ fontFamily: "'Inter', sans-serif" }}>
<UserPlus className="h-6 w-6" /> <UserPlus className="h-6 w-6" />
Create Member Create Member
</DialogTitle> </DialogTitle>
<DialogDescription className="text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <DialogDescription className="text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Create a new member account with direct login access. Member will be created immediately. Create a new member account with direct login access. Member will be created immediately.
</DialogDescription> </DialogDescription>
</DialogHeader> </DialogHeader>
@@ -135,7 +135,7 @@ const CreateMemberDialog = ({ open, onOpenChange, onSuccess }) => {
{/* Email & Password Row */} {/* Email & Password Row */}
<div className="grid md:grid-cols-2 gap-4"> <div className="grid md:grid-cols-2 gap-4">
<div className="grid gap-2"> <div className="grid gap-2">
<Label htmlFor="email" className="text-[#422268]"> <Label htmlFor="email" className="text-primary">
Email <span className="text-red-500">*</span> Email <span className="text-red-500">*</span>
</Label> </Label>
<Input <Input
@@ -143,7 +143,7 @@ const CreateMemberDialog = ({ open, onOpenChange, onSuccess }) => {
type="email" type="email"
value={formData.email} value={formData.email}
onChange={(e) => handleChange('email', e.target.value)} onChange={(e) => handleChange('email', e.target.value)}
className="rounded-xl border-2 border-[#ddd8eb] focus:border-[#664fa3]" className="rounded-xl border-2 border-chart-6 focus:border-muted-foreground"
placeholder="member@example.com" placeholder="member@example.com"
/> />
{errors.email && ( {errors.email && (
@@ -152,7 +152,7 @@ const CreateMemberDialog = ({ open, onOpenChange, onSuccess }) => {
</div> </div>
<div className="grid gap-2"> <div className="grid gap-2">
<Label htmlFor="password" className="text-[#422268]"> <Label htmlFor="password" className="text-primary">
Password <span className="text-red-500">*</span> Password <span className="text-red-500">*</span>
</Label> </Label>
<Input <Input
@@ -160,7 +160,7 @@ const CreateMemberDialog = ({ open, onOpenChange, onSuccess }) => {
type="password" type="password"
value={formData.password} value={formData.password}
onChange={(e) => handleChange('password', e.target.value)} onChange={(e) => handleChange('password', e.target.value)}
className="rounded-xl border-2 border-[#ddd8eb] focus:border-[#664fa3]" className="rounded-xl border-2 border-chart-6 focus:border-muted-foreground"
placeholder="Minimum 8 characters" placeholder="Minimum 8 characters"
/> />
{errors.password && ( {errors.password && (
@@ -172,14 +172,14 @@ const CreateMemberDialog = ({ open, onOpenChange, onSuccess }) => {
{/* Name Row */} {/* Name Row */}
<div className="grid md:grid-cols-2 gap-4"> <div className="grid md:grid-cols-2 gap-4">
<div className="grid gap-2"> <div className="grid gap-2">
<Label htmlFor="first_name" className="text-[#422268]"> <Label htmlFor="first_name" className="text-primary">
First Name <span className="text-red-500">*</span> First Name <span className="text-red-500">*</span>
</Label> </Label>
<Input <Input
id="first_name" id="first_name"
value={formData.first_name} value={formData.first_name}
onChange={(e) => handleChange('first_name', e.target.value)} onChange={(e) => handleChange('first_name', e.target.value)}
className="rounded-xl border-2 border-[#ddd8eb] focus:border-[#664fa3]" className="rounded-xl border-2 border-chart-6 focus:border-muted-foreground"
placeholder="John" placeholder="John"
/> />
{errors.first_name && ( {errors.first_name && (
@@ -188,14 +188,14 @@ const CreateMemberDialog = ({ open, onOpenChange, onSuccess }) => {
</div> </div>
<div className="grid gap-2"> <div className="grid gap-2">
<Label htmlFor="last_name" className="text-[#422268]"> <Label htmlFor="last_name" className="text-primary">
Last Name <span className="text-red-500">*</span> Last Name <span className="text-red-500">*</span>
</Label> </Label>
<Input <Input
id="last_name" id="last_name"
value={formData.last_name} value={formData.last_name}
onChange={(e) => handleChange('last_name', e.target.value)} onChange={(e) => handleChange('last_name', e.target.value)}
className="rounded-xl border-2 border-[#ddd8eb] focus:border-[#664fa3]" className="rounded-xl border-2 border-chart-6 focus:border-muted-foreground"
placeholder="Doe" placeholder="Doe"
/> />
{errors.last_name && ( {errors.last_name && (
@@ -206,7 +206,7 @@ const CreateMemberDialog = ({ open, onOpenChange, onSuccess }) => {
{/* Phone */} {/* Phone */}
<div className="grid gap-2"> <div className="grid gap-2">
<Label htmlFor="phone" className="text-[#422268]"> <Label htmlFor="phone" className="text-primary">
Phone <span className="text-red-500">*</span> Phone <span className="text-red-500">*</span>
</Label> </Label>
<Input <Input
@@ -214,7 +214,7 @@ const CreateMemberDialog = ({ open, onOpenChange, onSuccess }) => {
type="tel" type="tel"
value={formData.phone} value={formData.phone}
onChange={(e) => handleChange('phone', e.target.value)} onChange={(e) => handleChange('phone', e.target.value)}
className="rounded-xl border-2 border-[#ddd8eb] focus:border-[#664fa3]" className="rounded-xl border-2 border-chart-6 focus:border-muted-foreground"
placeholder="(555) 123-4567" placeholder="(555) 123-4567"
/> />
{errors.phone && ( {errors.phone && (
@@ -224,14 +224,14 @@ const CreateMemberDialog = ({ open, onOpenChange, onSuccess }) => {
{/* Address */} {/* Address */}
<div className="grid gap-2"> <div className="grid gap-2">
<Label htmlFor="address" className="text-[#422268]"> <Label htmlFor="address" className="text-primary">
Address Address
</Label> </Label>
<Input <Input
id="address" id="address"
value={formData.address} value={formData.address}
onChange={(e) => handleChange('address', e.target.value)} onChange={(e) => handleChange('address', e.target.value)}
className="rounded-xl border-2 border-[#ddd8eb] focus:border-[#664fa3]" className="rounded-xl border-2 border-chart-6 focus:border-muted-foreground"
placeholder="123 Main St" placeholder="123 Main St"
/> />
</div> </div>
@@ -239,35 +239,35 @@ const CreateMemberDialog = ({ open, onOpenChange, onSuccess }) => {
{/* City, State, Zipcode Row */} {/* City, State, Zipcode Row */}
<div className="grid md:grid-cols-3 gap-4"> <div className="grid md:grid-cols-3 gap-4">
<div className="grid gap-2"> <div className="grid gap-2">
<Label htmlFor="city" className="text-[#422268]">City</Label> <Label htmlFor="city" className="text-primary">City</Label>
<Input <Input
id="city" id="city"
value={formData.city} value={formData.city}
onChange={(e) => handleChange('city', e.target.value)} onChange={(e) => handleChange('city', e.target.value)}
className="rounded-xl border-2 border-[#ddd8eb] focus:border-[#664fa3]" className="rounded-xl border-2 border-chart-6 focus:border-muted-foreground"
placeholder="San Francisco" placeholder="San Francisco"
/> />
</div> </div>
<div className="grid gap-2"> <div className="grid gap-2">
<Label htmlFor="state" className="text-[#422268]">State</Label> <Label htmlFor="state" className="text-primary">State</Label>
<Input <Input
id="state" id="state"
value={formData.state} value={formData.state}
onChange={(e) => handleChange('state', e.target.value)} onChange={(e) => handleChange('state', e.target.value)}
className="rounded-xl border-2 border-[#ddd8eb] focus:border-[#664fa3]" className="rounded-xl border-2 border-chart-6 focus:border-muted-foreground"
placeholder="CA" placeholder="CA"
maxLength={2} maxLength={2}
/> />
</div> </div>
<div className="grid gap-2"> <div className="grid gap-2">
<Label htmlFor="zipcode" className="text-[#422268]">Zipcode</Label> <Label htmlFor="zipcode" className="text-primary">Zipcode</Label>
<Input <Input
id="zipcode" id="zipcode"
value={formData.zipcode} value={formData.zipcode}
onChange={(e) => handleChange('zipcode', e.target.value)} onChange={(e) => handleChange('zipcode', e.target.value)}
className="rounded-xl border-2 border-[#ddd8eb] focus:border-[#664fa3]" className="rounded-xl border-2 border-chart-6 focus:border-muted-foreground"
placeholder="94102" placeholder="94102"
/> />
</div> </div>
@@ -276,24 +276,24 @@ const CreateMemberDialog = ({ open, onOpenChange, onSuccess }) => {
{/* Dates Row */} {/* Dates Row */}
<div className="grid md:grid-cols-2 gap-4"> <div className="grid md:grid-cols-2 gap-4">
<div className="grid gap-2"> <div className="grid gap-2">
<Label htmlFor="date_of_birth" className="text-[#422268]">Date of Birth</Label> <Label htmlFor="date_of_birth" className="text-primary">Date of Birth</Label>
<Input <Input
id="date_of_birth" id="date_of_birth"
type="date" type="date"
value={formData.date_of_birth} value={formData.date_of_birth}
onChange={(e) => handleChange('date_of_birth', e.target.value)} onChange={(e) => handleChange('date_of_birth', e.target.value)}
className="rounded-xl border-2 border-[#ddd8eb] focus:border-[#664fa3]" className="rounded-xl border-2 border-chart-6 focus:border-muted-foreground"
/> />
</div> </div>
<div className="grid gap-2"> <div className="grid gap-2">
<Label htmlFor="member_since" className="text-[#422268]">Member Since</Label> <Label htmlFor="member_since" className="text-primary">Member Since</Label>
<Input <Input
id="member_since" id="member_since"
type="date" type="date"
value={formData.member_since} value={formData.member_since}
onChange={(e) => handleChange('member_since', e.target.value)} onChange={(e) => handleChange('member_since', e.target.value)}
className="rounded-xl border-2 border-[#ddd8eb] focus:border-[#664fa3]" className="rounded-xl border-2 border-chart-6 focus:border-muted-foreground"
/> />
</div> </div>
</div> </div>

View File

@@ -101,11 +101,11 @@ const CreateStaffDialog = ({ open, onOpenChange, onSuccess }) => {
<Dialog open={open} onOpenChange={onOpenChange}> <Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent className="sm:max-w-[600px] rounded-2xl"> <DialogContent className="sm:max-w-[600px] rounded-2xl">
<DialogHeader> <DialogHeader>
<DialogTitle className="text-2xl text-[#422268] flex items-center gap-2" style={{ fontFamily: "'Inter', sans-serif" }}> <DialogTitle className="text-2xl text-primary flex items-center gap-2" style={{ fontFamily: "'Inter', sans-serif" }}>
<UserPlus className="h-6 w-6" /> <UserPlus className="h-6 w-6" />
Create Staff Member Create Staff Member
</DialogTitle> </DialogTitle>
<DialogDescription className="text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <DialogDescription className="text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Create a new staff account with direct login access. User will be created immediately. Create a new staff account with direct login access. User will be created immediately.
</DialogDescription> </DialogDescription>
</DialogHeader> </DialogHeader>
@@ -114,7 +114,7 @@ const CreateStaffDialog = ({ open, onOpenChange, onSuccess }) => {
<div className="grid gap-6 py-4"> <div className="grid gap-6 py-4">
{/* Email */} {/* Email */}
<div className="grid gap-2"> <div className="grid gap-2">
<Label htmlFor="email" className="text-[#422268]"> <Label htmlFor="email" className="text-primary">
Email <span className="text-red-500">*</span> Email <span className="text-red-500">*</span>
</Label> </Label>
<Input <Input
@@ -122,7 +122,7 @@ const CreateStaffDialog = ({ open, onOpenChange, onSuccess }) => {
type="email" type="email"
value={formData.email} value={formData.email}
onChange={(e) => handleChange('email', e.target.value)} onChange={(e) => handleChange('email', e.target.value)}
className="rounded-xl border-2 border-[#ddd8eb] focus:border-[#664fa3]" className="rounded-xl border-2 border-chart-6 focus:border-muted-foreground"
placeholder="staff@example.com" placeholder="staff@example.com"
/> />
{errors.email && ( {errors.email && (
@@ -132,7 +132,7 @@ const CreateStaffDialog = ({ open, onOpenChange, onSuccess }) => {
{/* Password */} {/* Password */}
<div className="grid gap-2"> <div className="grid gap-2">
<Label htmlFor="password" className="text-[#422268]"> <Label htmlFor="password" className="text-primary">
Password <span className="text-red-500">*</span> Password <span className="text-red-500">*</span>
</Label> </Label>
<Input <Input
@@ -140,7 +140,7 @@ const CreateStaffDialog = ({ open, onOpenChange, onSuccess }) => {
type="password" type="password"
value={formData.password} value={formData.password}
onChange={(e) => handleChange('password', e.target.value)} onChange={(e) => handleChange('password', e.target.value)}
className="rounded-xl border-2 border-[#ddd8eb] focus:border-[#664fa3]" className="rounded-xl border-2 border-chart-6 focus:border-muted-foreground"
placeholder="Minimum 8 characters" placeholder="Minimum 8 characters"
/> />
{errors.password && ( {errors.password && (
@@ -150,14 +150,14 @@ const CreateStaffDialog = ({ open, onOpenChange, onSuccess }) => {
{/* First Name */} {/* First Name */}
<div className="grid gap-2"> <div className="grid gap-2">
<Label htmlFor="first_name" className="text-[#422268]"> <Label htmlFor="first_name" className="text-primary">
First Name <span className="text-red-500">*</span> First Name <span className="text-red-500">*</span>
</Label> </Label>
<Input <Input
id="first_name" id="first_name"
value={formData.first_name} value={formData.first_name}
onChange={(e) => handleChange('first_name', e.target.value)} onChange={(e) => handleChange('first_name', e.target.value)}
className="rounded-xl border-2 border-[#ddd8eb] focus:border-[#664fa3]" className="rounded-xl border-2 border-chart-6 focus:border-muted-foreground"
placeholder="John" placeholder="John"
/> />
{errors.first_name && ( {errors.first_name && (
@@ -167,14 +167,14 @@ const CreateStaffDialog = ({ open, onOpenChange, onSuccess }) => {
{/* Last Name */} {/* Last Name */}
<div className="grid gap-2"> <div className="grid gap-2">
<Label htmlFor="last_name" className="text-[#422268]"> <Label htmlFor="last_name" className="text-primary">
Last Name <span className="text-red-500">*</span> Last Name <span className="text-red-500">*</span>
</Label> </Label>
<Input <Input
id="last_name" id="last_name"
value={formData.last_name} value={formData.last_name}
onChange={(e) => handleChange('last_name', e.target.value)} onChange={(e) => handleChange('last_name', e.target.value)}
className="rounded-xl border-2 border-[#ddd8eb] focus:border-[#664fa3]" className="rounded-xl border-2 border-chart-6 focus:border-muted-foreground"
placeholder="Doe" placeholder="Doe"
/> />
{errors.last_name && ( {errors.last_name && (
@@ -184,7 +184,7 @@ const CreateStaffDialog = ({ open, onOpenChange, onSuccess }) => {
{/* Phone */} {/* Phone */}
<div className="grid gap-2"> <div className="grid gap-2">
<Label htmlFor="phone" className="text-[#422268]"> <Label htmlFor="phone" className="text-primary">
Phone <span className="text-red-500">*</span> Phone <span className="text-red-500">*</span>
</Label> </Label>
<Input <Input
@@ -192,7 +192,7 @@ const CreateStaffDialog = ({ open, onOpenChange, onSuccess }) => {
type="tel" type="tel"
value={formData.phone} value={formData.phone}
onChange={(e) => handleChange('phone', e.target.value)} onChange={(e) => handleChange('phone', e.target.value)}
className="rounded-xl border-2 border-[#ddd8eb] focus:border-[#664fa3]" className="rounded-xl border-2 border-chart-6 focus:border-muted-foreground"
placeholder="(555) 123-4567" placeholder="(555) 123-4567"
/> />
{errors.phone && ( {errors.phone && (
@@ -202,11 +202,11 @@ const CreateStaffDialog = ({ open, onOpenChange, onSuccess }) => {
{/* Role */} {/* Role */}
<div className="grid gap-2"> <div className="grid gap-2">
<Label htmlFor="role" className="text-[#422268]"> <Label htmlFor="role" className="text-primary">
Role <span className="text-red-500">*</span> Role <span className="text-red-500">*</span>
</Label> </Label>
<Select value={formData.role} onValueChange={(value) => handleChange('role', value)}> <Select value={formData.role} onValueChange={(value) => handleChange('role', value)}>
<SelectTrigger className="rounded-xl border-2 border-[#ddd8eb]"> <SelectTrigger className="rounded-xl border-2 border-chart-6">
<SelectValue /> <SelectValue />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>

View File

@@ -140,11 +140,11 @@ const ImportMembersDialog = ({ open, onOpenChange, onSuccess }) => {
<Dialog open={open} onOpenChange={handleClose}> <Dialog open={open} onOpenChange={handleClose}>
<DialogContent className="sm:max-w-[800px] rounded-2xl max-h-[90vh] overflow-y-auto"> <DialogContent className="sm:max-w-[800px] rounded-2xl max-h-[90vh] overflow-y-auto">
<DialogHeader> <DialogHeader>
<DialogTitle className="text-2xl text-[#422268] flex items-center gap-2" style={{ fontFamily: "'Inter', sans-serif" }}> <DialogTitle className="text-2xl text-primary flex items-center gap-2" style={{ fontFamily: "'Inter', sans-serif" }}>
<Upload className="h-6 w-6" /> <Upload className="h-6 w-6" />
{importResult ? 'Import Results' : 'Import Members from CSV'} {importResult ? 'Import Results' : 'Import Members from CSV'}
</DialogTitle> </DialogTitle>
<DialogDescription className="text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <DialogDescription className="text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{importResult {importResult
? 'Review the import results below' ? 'Review the import results below'
: 'Upload a CSV file to bulk import members. Ensure the CSV has the required columns.'} : 'Upload a CSV file to bulk import members. Ensure the CSV has the required columns.'}
@@ -155,8 +155,8 @@ const ImportMembersDialog = ({ open, onOpenChange, onSuccess }) => {
// Upload Form // Upload Form
<div className="grid gap-6 py-4"> <div className="grid gap-6 py-4">
{/* CSV Format Instructions */} {/* CSV Format Instructions */}
<Alert className="border-[#664fa3] bg-[#F9F8FB]"> <Alert className="border-muted-foreground bg-[#F9F8FB]">
<AlertDescription className="text-sm text-[#422268]"> <AlertDescription className="text-sm text-primary">
<strong>Required columns:</strong> Email, First Name, Last Name, Phone, Role <strong>Required columns:</strong> Email, First Name, Last Name, Phone, Role
<br /> <br />
<strong>Optional columns:</strong> Status, Address, City, State, Zipcode, Date of Birth, Member Since <strong>Optional columns:</strong> Status, Address, City, State, Zipcode, Date of Birth, Member Since
@@ -167,11 +167,10 @@ const ImportMembersDialog = ({ open, onOpenChange, onSuccess }) => {
{/* File Upload Area */} {/* File Upload Area */}
<div <div
className={`border-2 border-dashed rounded-2xl p-12 text-center transition-colors ${ className={`border-2 border-dashed rounded-2xl p-12 text-center transition-colors ${dragActive
dragActive ? 'border-muted-foreground bg-[#F9F8FB]'
? 'border-[#664fa3] bg-[#F9F8FB]' : 'border-chart-6 hover:border-muted-foreground hover:bg-[#F9F8FB]'
: 'border-[#ddd8eb] hover:border-[#664fa3] hover:bg-[#F9F8FB]' }`}
}`}
onDragEnter={handleDrag} onDragEnter={handleDrag}
onDragLeave={handleDrag} onDragLeave={handleDrag}
onDragOver={handleDrag} onDragOver={handleDrag}
@@ -181,10 +180,10 @@ const ImportMembersDialog = ({ open, onOpenChange, onSuccess }) => {
<div className="flex flex-col items-center gap-4"> <div className="flex flex-col items-center gap-4">
<FileUp className="h-16 w-16 text-[#81B29A]" /> <FileUp className="h-16 w-16 text-[#81B29A]" />
<div> <div>
<p className="text-lg font-semibold text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}> <p className="text-lg font-semibold text-primary" style={{ fontFamily: "'Inter', sans-serif" }}>
{file.name} {file.name}
</p> </p>
<p className="text-sm text-[#664fa3]"> <p className="text-sm text-muted-foreground">
{(file.size / 1024).toFixed(2)} KB {(file.size / 1024).toFixed(2)} KB
</p> </p>
</div> </div>
@@ -199,12 +198,12 @@ const ImportMembersDialog = ({ open, onOpenChange, onSuccess }) => {
</div> </div>
) : ( ) : (
<div className="flex flex-col items-center gap-4"> <div className="flex flex-col items-center gap-4">
<Upload className="h-16 w-16 text-[#ddd8eb]" /> <Upload className="h-16 w-16 text-chart-6" />
<div> <div>
<p className="text-lg font-semibold text-[#422268] mb-2" style={{ fontFamily: "'Inter', sans-serif" }}> <p className="text-lg font-semibold text-primary mb-2" style={{ fontFamily: "'Inter', sans-serif" }}>
Drag and drop your CSV file here Drag and drop your CSV file here
</p> </p>
<p className="text-sm text-[#664fa3] mb-4">or</p> <p className="text-sm text-muted-foreground mb-4">or</p>
<Label htmlFor="file-upload"> <Label htmlFor="file-upload">
<Button variant="outline" className="rounded-xl cursor-pointer" asChild> <Button variant="outline" className="rounded-xl cursor-pointer" asChild>
<span>Browse Files</span> <span>Browse Files</span>
@@ -228,9 +227,9 @@ const ImportMembersDialog = ({ open, onOpenChange, onSuccess }) => {
checked={updateExisting} checked={updateExisting}
onCheckedChange={setUpdateExisting} onCheckedChange={setUpdateExisting}
id="update-existing" id="update-existing"
className="h-5 w-5 border-2 border-[#664fa3] data-[state=checked]:bg-[#664fa3]" className="h-5 w-5 border-2 border-muted-foreground data-[state=checked]:bg-muted-foreground"
/> />
<Label htmlFor="update-existing" className="text-[#422268] cursor-pointer"> <Label htmlFor="update-existing" className="text-primary cursor-pointer">
Update existing members (if email already exists) Update existing members (if email already exists)
</Label> </Label>
</div> </div>
@@ -240,9 +239,9 @@ const ImportMembersDialog = ({ open, onOpenChange, onSuccess }) => {
<div className="grid gap-6 py-4"> <div className="grid gap-6 py-4">
{/* Summary Cards */} {/* Summary Cards */}
<div className="grid md:grid-cols-4 gap-4"> <div className="grid md:grid-cols-4 gap-4">
<div className="p-4 bg-white rounded-xl border border-[#ddd8eb] text-center"> <div className="p-4 bg-background rounded-xl border border-chart-6 text-center">
<p className="text-sm text-[#664fa3] mb-1">Total Rows</p> <p className="text-sm text-muted-foreground mb-1">Total Rows</p>
<p className="text-2xl font-semibold text-[#422268]">{importResult.total_rows}</p> <p className="text-2xl font-semibold text-primary">{importResult.total_rows}</p>
</div> </div>
<div className="p-4 bg-green-50 rounded-xl border border-green-200 text-center"> <div className="p-4 bg-green-50 rounded-xl border border-green-200 text-center">
<p className="text-sm text-green-700 mb-1">Successful</p> <p className="text-sm text-green-700 mb-1">Successful</p>
@@ -252,7 +251,7 @@ const ImportMembersDialog = ({ open, onOpenChange, onSuccess }) => {
<p className="text-sm text-red-700 mb-1">Failed</p> <p className="text-sm text-red-700 mb-1">Failed</p>
<p className="text-2xl font-semibold text-red-600">{importResult.failed_rows}</p> <p className="text-2xl font-semibold text-red-600">{importResult.failed_rows}</p>
</div> </div>
<div className="p-4 bg-white rounded-xl border border-[#ddd8eb] flex items-center justify-center gap-2"> <div className="p-4 bg-background rounded-xl border border-chart-6 flex items-center justify-center gap-2">
{getStatusIcon(importResult.status)} {getStatusIcon(importResult.status)}
{getStatusBadge(importResult.status)} {getStatusBadge(importResult.status)}
</div> </div>
@@ -261,23 +260,23 @@ const ImportMembersDialog = ({ open, onOpenChange, onSuccess }) => {
{/* Errors Table */} {/* Errors Table */}
{importResult.errors && importResult.errors.length > 0 && ( {importResult.errors && importResult.errors.length > 0 && (
<div> <div>
<h3 className="text-lg font-semibold text-[#422268] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}> <h3 className="text-lg font-semibold text-primary mb-4" style={{ fontFamily: "'Inter', sans-serif" }}>
Errors ({importResult.errors.length} {importResult.errors.length === 10 ? '- showing first 10' : ''}) Errors ({importResult.errors.length} {importResult.errors.length === 10 ? '- showing first 10' : ''})
</h3> </h3>
<div className="border border-[#ddd8eb] rounded-xl overflow-hidden"> <div className="border border-chart-6 rounded-xl overflow-hidden">
<Table> <Table>
<TableHeader> <TableHeader>
<TableRow className="bg-[#DDD8EB] hover:bg-[#DDD8EB]"> <TableRow className="bg-chart-6 hover:bg-chart-6">
<TableHead className="text-[#422268] font-semibold">Row</TableHead> <TableHead className="text-primary font-semibold">Row</TableHead>
<TableHead className="text-[#422268] font-semibold">Email</TableHead> <TableHead className="text-primary font-semibold">Email</TableHead>
<TableHead className="text-[#422268] font-semibold">Error</TableHead> <TableHead className="text-primary font-semibold">Error</TableHead>
</TableRow> </TableRow>
</TableHeader> </TableHeader>
<TableBody> <TableBody>
{importResult.errors.map((error, idx) => ( {importResult.errors.map((error, idx) => (
<TableRow key={idx} className="hover:bg-[#F9F8FB]"> <TableRow key={idx} className="hover:bg-[#F9F8FB]">
<TableCell className="font-medium text-[#422268]">{error.row}</TableCell> <TableCell className="font-medium text-primary">{error.row}</TableCell>
<TableCell className="text-[#664fa3]">{error.email}</TableCell> <TableCell className="text-muted-foreground">{error.email}</TableCell>
<TableCell className="text-red-600 text-sm">{error.error}</TableCell> <TableCell className="text-red-600 text-sm">{error.error}</TableCell>
</TableRow> </TableRow>
))} ))}

View File

@@ -125,11 +125,11 @@ const InviteStaffDialog = ({ open, onOpenChange, onSuccess }) => {
<Dialog open={open} onOpenChange={handleClose}> <Dialog open={open} onOpenChange={handleClose}>
<DialogContent className="sm:max-w-[600px] rounded-2xl"> <DialogContent className="sm:max-w-[600px] rounded-2xl">
<DialogHeader> <DialogHeader>
<DialogTitle className="text-2xl text-[#422268] flex items-center gap-2" style={{ fontFamily: "'Inter', sans-serif" }}> <DialogTitle className="text-2xl text-primary flex items-center gap-2" style={{ fontFamily: "'Inter', sans-serif" }}>
<Mail className="h-6 w-6" /> <Mail className="h-6 w-6" />
{invitationUrl ? 'Invitation Sent' : 'Invite Staff Member'} {invitationUrl ? 'Invitation Sent' : 'Invite Staff Member'}
</DialogTitle> </DialogTitle>
<DialogDescription className="text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <DialogDescription className="text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{invitationUrl {invitationUrl
? 'The invitation has been sent via email. You can also copy the link below.' ? 'The invitation has been sent via email. You can also copy the link below.'
: 'Send an email invitation to join as staff. They will set their own password.'} : 'Send an email invitation to join as staff. They will set their own password.'}
@@ -139,16 +139,16 @@ const InviteStaffDialog = ({ open, onOpenChange, onSuccess }) => {
{invitationUrl ? ( {invitationUrl ? (
// Show invitation URL after successful send // Show invitation URL after successful send
<div className="py-4"> <div className="py-4">
<Label className="text-[#422268] mb-2 block">Invitation Link (expires in 7 days)</Label> <Label className="text-primary mb-2 block">Invitation Link (expires in 7 days)</Label>
<div className="flex gap-2"> <div className="flex gap-2">
<Input <Input
value={invitationUrl} value={invitationUrl}
readOnly readOnly
className="rounded-xl border-2 border-[#ddd8eb] bg-gray-50" className="rounded-xl border-2 border-chart-6 bg-gray-50"
/> />
<Button <Button
onClick={copyToClipboard} onClick={copyToClipboard}
className="rounded-xl bg-[#664fa3] hover:bg-[#422268] text-white flex-shrink-0" className="rounded-xl bg-muted-foreground hover:bg-primary text-white flex-shrink-0"
> >
{copied ? ( {copied ? (
<> <>
@@ -170,7 +170,7 @@ const InviteStaffDialog = ({ open, onOpenChange, onSuccess }) => {
<div className="grid gap-6 py-4"> <div className="grid gap-6 py-4">
{/* Email */} {/* Email */}
<div className="grid gap-2"> <div className="grid gap-2">
<Label htmlFor="email" className="text-[#422268]"> <Label htmlFor="email" className="text-primary">
Email <span className="text-red-500">*</span> Email <span className="text-red-500">*</span>
</Label> </Label>
<Input <Input
@@ -178,7 +178,7 @@ const InviteStaffDialog = ({ open, onOpenChange, onSuccess }) => {
type="email" type="email"
value={formData.email} value={formData.email}
onChange={(e) => handleChange('email', e.target.value)} onChange={(e) => handleChange('email', e.target.value)}
className="rounded-xl border-2 border-[#ddd8eb] focus:border-[#664fa3]" className="rounded-xl border-2 border-chart-6 focus:border-muted-foreground"
placeholder="staff@example.com" placeholder="staff@example.com"
/> />
{errors.email && ( {errors.email && (
@@ -188,35 +188,35 @@ const InviteStaffDialog = ({ open, onOpenChange, onSuccess }) => {
{/* First Name (Optional) */} {/* First Name (Optional) */}
<div className="grid gap-2"> <div className="grid gap-2">
<Label htmlFor="first_name" className="text-[#422268]"> <Label htmlFor="first_name" className="text-primary">
First Name <span className="text-gray-400">(Optional)</span> First Name <span className="text-gray-400">(Optional)</span>
</Label> </Label>
<Input <Input
id="first_name" id="first_name"
value={formData.first_name} value={formData.first_name}
onChange={(e) => handleChange('first_name', e.target.value)} onChange={(e) => handleChange('first_name', e.target.value)}
className="rounded-xl border-2 border-[#ddd8eb] focus:border-[#664fa3]" className="rounded-xl border-2 border-chart-6 focus:border-muted-foreground"
placeholder="John" placeholder="John"
/> />
</div> </div>
{/* Last Name (Optional) */} {/* Last Name (Optional) */}
<div className="grid gap-2"> <div className="grid gap-2">
<Label htmlFor="last_name" className="text-[#422268]"> <Label htmlFor="last_name" className="text-primary">
Last Name <span className="text-gray-400">(Optional)</span> Last Name <span className="text-gray-400">(Optional)</span>
</Label> </Label>
<Input <Input
id="last_name" id="last_name"
value={formData.last_name} value={formData.last_name}
onChange={(e) => handleChange('last_name', e.target.value)} onChange={(e) => handleChange('last_name', e.target.value)}
className="rounded-xl border-2 border-[#ddd8eb] focus:border-[#664fa3]" className="rounded-xl border-2 border-chart-6 focus:border-muted-foreground"
placeholder="Doe" placeholder="Doe"
/> />
</div> </div>
{/* Phone (Optional) */} {/* Phone (Optional) */}
<div className="grid gap-2"> <div className="grid gap-2">
<Label htmlFor="phone" className="text-[#422268]"> <Label htmlFor="phone" className="text-primary">
Phone <span className="text-gray-400">(Optional)</span> Phone <span className="text-gray-400">(Optional)</span>
</Label> </Label>
<Input <Input
@@ -224,14 +224,14 @@ const InviteStaffDialog = ({ open, onOpenChange, onSuccess }) => {
type="tel" type="tel"
value={formData.phone} value={formData.phone}
onChange={(e) => handleChange('phone', e.target.value)} onChange={(e) => handleChange('phone', e.target.value)}
className="rounded-xl border-2 border-[#ddd8eb] focus:border-[#664fa3]" className="rounded-xl border-2 border-chart-6 focus:border-muted-foreground"
placeholder="(555) 123-4567" placeholder="(555) 123-4567"
/> />
</div> </div>
{/* Role */} {/* Role */}
<div className="grid gap-2"> <div className="grid gap-2">
<Label htmlFor="role" className="text-[#422268]"> <Label htmlFor="role" className="text-primary">
Role <span className="text-red-500">*</span> Role <span className="text-red-500">*</span>
</Label> </Label>
<Select <Select
@@ -239,7 +239,7 @@ const InviteStaffDialog = ({ open, onOpenChange, onSuccess }) => {
onValueChange={(value) => handleChange('role', value)} onValueChange={(value) => handleChange('role', value)}
disabled={loadingRoles} disabled={loadingRoles}
> >
<SelectTrigger className="rounded-xl border-2 border-[#ddd8eb]"> <SelectTrigger className="rounded-xl border-2 border-chart-6">
<SelectValue placeholder={loadingRoles ? "Loading roles..." : "Select a role"} /> <SelectValue placeholder={loadingRoles ? "Loading roles..." : "Select a role"} />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>

View File

@@ -4,7 +4,7 @@ import { Calendar, Users, User, BookOpen, FileText, DollarSign, Scale } from 'lu
const MemberFooter = () => { const MemberFooter = () => {
return ( return (
<footer className="bg-[#422268] text-white mt-auto"> <footer className="bg-primary text-white mt-auto">
<div className="max-w-7xl mx-auto px-6 py-12"> <div className="max-w-7xl mx-auto px-6 py-12">
<div className="grid md:grid-cols-4 gap-8"> <div className="grid md:grid-cols-4 gap-8">
{/* Logo & About */} {/* Logo & About */}
@@ -104,7 +104,7 @@ const MemberFooter = () => {
</div> </div>
{/* Bottom Bar */} {/* Bottom Bar */}
<div className="border-t border-[#664fa3]"> <div className="border-t border-muted-foreground">
<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 flex-col md:flex-row justify-between items-center gap-4 text-sm text-gray-400" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <div className="flex flex-col md:flex-row justify-between items-center gap-4 text-sm text-gray-400" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<div className="flex gap-6"> <div className="flex gap-6">

View File

@@ -53,7 +53,7 @@ const Navbar = () => {
</button> </button>
<Link to="/donate"> <Link to="/donate">
<Button <Button
className="bg-[#ff9e77] hover:bg-[#ff8c64] text-[#48286e] rounded-[25px] px-[54px] py-[10px] text-[16.5px] font-semibold h-[41px]" className="bg-accent hover:bg-[#ff8c64] text-[#48286e] rounded-[25px] px-[54px] py-[10px] text-[16.5px] font-semibold h-[41px]"
style={{ fontFamily: "'Montserrat', sans-serif" }} style={{ fontFamily: "'Montserrat', sans-serif" }}
> >
Donate Donate
@@ -62,7 +62,7 @@ const Navbar = () => {
</header> </header>
{/* Main Header - Member Navigation */} {/* Main Header - Member Navigation */}
<header className="bg-[#664fa3] px-4 sm:px-8 md:px-16 py-2 flex justify-between items-center"> <header className="bg-muted-foreground px-4 sm:px-8 md:px-16 py-2 flex justify-between items-center">
<Link to="/dashboard"> <Link to="/dashboard">
<img src={loafLogo} alt="LOAF Logo" className="h-16 w-16 sm:h-20 sm:w-20 md:h-28 md:w-28 object-contain" /> <img src={loafLogo} alt="LOAF Logo" className="h-16 w-16 sm:h-20 sm:w-20 md:h-28 md:w-28 object-contain" />
</Link> </Link>
@@ -84,21 +84,21 @@ const Navbar = () => {
<ChevronDown className="h-4 w-4" /> <ChevronDown className="h-4 w-4" />
</button> </button>
</DropdownMenuTrigger> </DropdownMenuTrigger>
<DropdownMenuContent align="start" className="bg-white min-w-[220px]"> <DropdownMenuContent align="start" className="bg-background min-w-[220px]">
<DropdownMenuItem asChild> <DropdownMenuItem asChild>
<Link to="/about/history" className="w-full px-3 py-2 text-[#48286e] hover:bg-[#f1eef9] cursor-pointer" <Link to="/about/history" className="w-full px-3 py-2 text-[#48286e] hover:bg-muted cursor-pointer"
style={{ fontFamily: "'Poppins', sans-serif" }}> style={{ fontFamily: "'Poppins', sans-serif" }}>
History History
</Link> </Link>
</DropdownMenuItem> </DropdownMenuItem>
<DropdownMenuItem asChild> <DropdownMenuItem asChild>
<Link to="/about/mission-values" className="w-full px-3 py-2 text-[#48286e] hover:bg-[#f1eef9] cursor-pointer" <Link to="/about/mission-values" className="w-full px-3 py-2 text-[#48286e] hover:bg-muted cursor-pointer"
style={{ fontFamily: "'Poppins', sans-serif" }}> style={{ fontFamily: "'Poppins', sans-serif" }}>
Mission and Values Mission and Values
</Link> </Link>
</DropdownMenuItem> </DropdownMenuItem>
<DropdownMenuItem asChild> <DropdownMenuItem asChild>
<Link to="/about/board" className="w-full px-3 py-2 text-[#48286e] hover:bg-[#f1eef9] cursor-pointer" <Link to="/about/board" className="w-full px-3 py-2 text-[#48286e] hover:bg-muted cursor-pointer"
style={{ fontFamily: "'Poppins', sans-serif" }}> style={{ fontFamily: "'Poppins', sans-serif" }}>
Board of Directors Board of Directors
</Link> </Link>
@@ -149,21 +149,21 @@ const Navbar = () => {
<ChevronDown className="h-4 w-4" /> <ChevronDown className="h-4 w-4" />
</button> </button>
</DropdownMenuTrigger> </DropdownMenuTrigger>
<DropdownMenuContent align="start" className="bg-white min-w-[220px]"> <DropdownMenuContent align="start" className="bg-background min-w-[220px]">
<DropdownMenuItem asChild> <DropdownMenuItem asChild>
<Link to="/members/newsletters" className="w-full px-3 py-2 text-[#48286e] hover:bg-[#f1eef9] cursor-pointer" <Link to="/members/newsletters" className="w-full px-3 py-2 text-[#48286e] hover:bg-muted cursor-pointer"
style={{ fontFamily: "'Poppins', sans-serif" }}> style={{ fontFamily: "'Poppins', sans-serif" }}>
Newsletters Newsletters
</Link> </Link>
</DropdownMenuItem> </DropdownMenuItem>
<DropdownMenuItem asChild> <DropdownMenuItem asChild>
<Link to="/members/financials" className="w-full px-3 py-2 text-[#48286e] hover:bg-[#f1eef9] cursor-pointer" <Link to="/members/financials" className="w-full px-3 py-2 text-[#48286e] hover:bg-muted cursor-pointer"
style={{ fontFamily: "'Poppins', sans-serif" }}> style={{ fontFamily: "'Poppins', sans-serif" }}>
Financials Financials
</Link> </Link>
</DropdownMenuItem> </DropdownMenuItem>
<DropdownMenuItem asChild> <DropdownMenuItem asChild>
<Link to="/members/bylaws" className="w-full px-3 py-2 text-[#48286e] hover:bg-[#f1eef9] cursor-pointer" <Link to="/members/bylaws" className="w-full px-3 py-2 text-[#48286e] hover:bg-muted cursor-pointer"
style={{ fontFamily: "'Poppins', sans-serif" }}> style={{ fontFamily: "'Poppins', sans-serif" }}>
Bylaws Bylaws
</Link> </Link>
@@ -183,7 +183,7 @@ const Navbar = () => {
{/* Mobile Hamburger Button */} {/* Mobile Hamburger Button */}
<button <button
onClick={() => setIsMobileMenuOpen(true)} onClick={() => setIsMobileMenuOpen(true)}
className="lg:hidden p-2 text-white hover:bg-white/10 rounded-lg transition-colors" className="lg:hidden p-2 text-white hover:bg-background/10 rounded-lg transition-colors"
aria-label="Open menu" aria-label="Open menu"
> >
<Menu className="h-6 w-6" /> <Menu className="h-6 w-6" />
@@ -200,7 +200,7 @@ const Navbar = () => {
/> />
{/* Drawer */} {/* Drawer */}
<div className="fixed right-0 top-0 h-full w-[280px] bg-gradient-to-b from-[#664fa3] to-[#48286e] shadow-2xl flex flex-col"> <div className="fixed right-0 top-0 h-full w-[280px] bg-gradient-to-b from-muted-foreground to-[#48286e] shadow-2xl flex flex-col">
{/* Header */} {/* Header */}
<div className="flex items-center justify-between p-6 border-b border-white/20"> <div className="flex items-center justify-between p-6 border-b border-white/20">
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
@@ -211,7 +211,7 @@ const Navbar = () => {
</div> </div>
<button <button
onClick={() => setIsMobileMenuOpen(false)} onClick={() => setIsMobileMenuOpen(false)}
className="p-2 text-white hover:bg-white/10 rounded-lg transition-colors" className="p-2 text-white hover:bg-background/10 rounded-lg transition-colors"
aria-label="Close menu" aria-label="Close menu"
> >
<X className="h-6 w-6" /> <X className="h-6 w-6" />
@@ -236,7 +236,7 @@ const Navbar = () => {
<Link <Link
to="/" to="/"
onClick={() => setIsMobileMenuOpen(false)} onClick={() => setIsMobileMenuOpen(false)}
className="block px-4 py-3 text-white text-base font-medium hover:bg-white/10 rounded-lg transition-colors" className="block px-4 py-3 text-white text-base font-medium hover:bg-background/10 rounded-lg transition-colors"
style={{ fontFamily: "'Poppins', sans-serif" }} style={{ fontFamily: "'Poppins', sans-serif" }}
> >
Home Home
@@ -250,7 +250,7 @@ const Navbar = () => {
<Link <Link
to="/about/history" to="/about/history"
onClick={() => setIsMobileMenuOpen(false)} onClick={() => setIsMobileMenuOpen(false)}
className="block px-6 py-2 text-white text-base hover:bg-white/10 rounded-lg transition-colors" className="block px-6 py-2 text-white text-base hover:bg-background/10 rounded-lg transition-colors"
style={{ fontFamily: "'Poppins', sans-serif" }} style={{ fontFamily: "'Poppins', sans-serif" }}
> >
History History
@@ -258,7 +258,7 @@ const Navbar = () => {
<Link <Link
to="/about/mission-values" to="/about/mission-values"
onClick={() => setIsMobileMenuOpen(false)} onClick={() => setIsMobileMenuOpen(false)}
className="block px-6 py-2 text-white text-base hover:bg-white/10 rounded-lg transition-colors" className="block px-6 py-2 text-white text-base hover:bg-background/10 rounded-lg transition-colors"
style={{ fontFamily: "'Poppins', sans-serif" }} style={{ fontFamily: "'Poppins', sans-serif" }}
> >
Mission and Values Mission and Values
@@ -266,7 +266,7 @@ const Navbar = () => {
<Link <Link
to="/about/board" to="/about/board"
onClick={() => setIsMobileMenuOpen(false)} onClick={() => setIsMobileMenuOpen(false)}
className="block px-6 py-2 text-white text-base hover:bg-white/10 rounded-lg transition-colors" className="block px-6 py-2 text-white text-base hover:bg-background/10 rounded-lg transition-colors"
style={{ fontFamily: "'Poppins', sans-serif" }} style={{ fontFamily: "'Poppins', sans-serif" }}
> >
Board of Directors Board of Directors
@@ -276,7 +276,7 @@ const Navbar = () => {
<Link <Link
to="/dashboard" to="/dashboard"
onClick={() => setIsMobileMenuOpen(false)} onClick={() => setIsMobileMenuOpen(false)}
className="block px-4 py-3 text-white text-base font-medium hover:bg-white/10 rounded-lg transition-colors" className="block px-4 py-3 text-white text-base font-medium hover:bg-background/10 rounded-lg transition-colors"
style={{ fontFamily: "'Poppins', sans-serif" }} style={{ fontFamily: "'Poppins', sans-serif" }}
> >
Dashboard Dashboard
@@ -285,7 +285,7 @@ const Navbar = () => {
<Link <Link
to="/events" to="/events"
onClick={() => setIsMobileMenuOpen(false)} onClick={() => setIsMobileMenuOpen(false)}
className="block px-4 py-3 text-white text-base font-medium hover:bg-white/10 rounded-lg transition-colors" className="block px-4 py-3 text-white text-base font-medium hover:bg-background/10 rounded-lg transition-colors"
style={{ fontFamily: "'Poppins', sans-serif" }} style={{ fontFamily: "'Poppins', sans-serif" }}
data-testid="mobile-events-nav-button" data-testid="mobile-events-nav-button"
> >
@@ -295,7 +295,7 @@ const Navbar = () => {
<Link <Link
to="/members/calendar" to="/members/calendar"
onClick={() => setIsMobileMenuOpen(false)} onClick={() => setIsMobileMenuOpen(false)}
className="block px-4 py-3 text-white text-base font-medium hover:bg-white/10 rounded-lg transition-colors" className="block px-4 py-3 text-white text-base font-medium hover:bg-background/10 rounded-lg transition-colors"
style={{ fontFamily: "'Poppins', sans-serif" }} style={{ fontFamily: "'Poppins', sans-serif" }}
> >
Calendar Calendar
@@ -304,7 +304,7 @@ const Navbar = () => {
<Link <Link
to="/members/directory" to="/members/directory"
onClick={() => setIsMobileMenuOpen(false)} onClick={() => setIsMobileMenuOpen(false)}
className="block px-4 py-3 text-white text-base font-medium hover:bg-white/10 rounded-lg transition-colors" className="block px-4 py-3 text-white text-base font-medium hover:bg-background/10 rounded-lg transition-colors"
style={{ fontFamily: "'Poppins', sans-serif" }} style={{ fontFamily: "'Poppins', sans-serif" }}
> >
Directory Directory
@@ -313,7 +313,7 @@ const Navbar = () => {
<Link <Link
to="/members/gallery" to="/members/gallery"
onClick={() => setIsMobileMenuOpen(false)} onClick={() => setIsMobileMenuOpen(false)}
className="block px-4 py-3 text-white text-base font-medium hover:bg-white/10 rounded-lg transition-colors" className="block px-4 py-3 text-white text-base font-medium hover:bg-background/10 rounded-lg transition-colors"
style={{ fontFamily: "'Poppins', sans-serif" }} style={{ fontFamily: "'Poppins', sans-serif" }}
> >
Gallery Gallery
@@ -327,7 +327,7 @@ const Navbar = () => {
<Link <Link
to="/members/newsletters" to="/members/newsletters"
onClick={() => setIsMobileMenuOpen(false)} onClick={() => setIsMobileMenuOpen(false)}
className="block px-6 py-2 text-white text-base hover:bg-white/10 rounded-lg transition-colors" className="block px-6 py-2 text-white text-base hover:bg-background/10 rounded-lg transition-colors"
style={{ fontFamily: "'Poppins', sans-serif" }} style={{ fontFamily: "'Poppins', sans-serif" }}
> >
Newsletters Newsletters
@@ -335,7 +335,7 @@ const Navbar = () => {
<Link <Link
to="/members/financials" to="/members/financials"
onClick={() => setIsMobileMenuOpen(false)} onClick={() => setIsMobileMenuOpen(false)}
className="block px-6 py-2 text-white text-base hover:bg-white/10 rounded-lg transition-colors" className="block px-6 py-2 text-white text-base hover:bg-background/10 rounded-lg transition-colors"
style={{ fontFamily: "'Poppins', sans-serif" }} style={{ fontFamily: "'Poppins', sans-serif" }}
> >
Financials Financials
@@ -343,7 +343,7 @@ const Navbar = () => {
<Link <Link
to="/members/bylaws" to="/members/bylaws"
onClick={() => setIsMobileMenuOpen(false)} onClick={() => setIsMobileMenuOpen(false)}
className="block px-6 py-2 text-white text-base hover:bg-white/10 rounded-lg transition-colors" className="block px-6 py-2 text-white text-base hover:bg-background/10 rounded-lg transition-colors"
style={{ fontFamily: "'Poppins', sans-serif" }} style={{ fontFamily: "'Poppins', sans-serif" }}
> >
Bylaws Bylaws
@@ -353,7 +353,7 @@ const Navbar = () => {
<Link <Link
to="/profile" to="/profile"
onClick={() => setIsMobileMenuOpen(false)} onClick={() => setIsMobileMenuOpen(false)}
className="block px-4 py-3 text-white text-base font-medium hover:bg-white/10 rounded-lg transition-colors" className="block px-4 py-3 text-white text-base font-medium hover:bg-background/10 rounded-lg transition-colors"
style={{ fontFamily: "'Poppins', sans-serif" }} style={{ fontFamily: "'Poppins', sans-serif" }}
data-testid="mobile-profile-nav-button" data-testid="mobile-profile-nav-button"
> >
@@ -370,7 +370,7 @@ const Navbar = () => {
onClick={() => setIsMobileMenuOpen(false)} onClick={() => setIsMobileMenuOpen(false)}
> >
<Button <Button
className="w-full bg-white/20 hover:bg-white/30 text-white rounded-lg" className="w-full bg-background/20 hover:bg-background/30 text-white rounded-lg"
style={{ fontFamily: "'Poppins', sans-serif" }} style={{ fontFamily: "'Poppins', sans-serif" }}
> >
Admin Panel Admin Panel
@@ -383,7 +383,7 @@ const Navbar = () => {
onClick={() => setIsMobileMenuOpen(false)} onClick={() => setIsMobileMenuOpen(false)}
> >
<Button <Button
className="w-full bg-[#ff9e77] hover:bg-[#ff8c64] text-[#48286e] rounded-lg font-semibold" className="w-full bg-accent hover:bg-[#ff8c64] text-[#48286e] rounded-lg font-semibold"
style={{ fontFamily: "'Montserrat', sans-serif" }} style={{ fontFamily: "'Montserrat', sans-serif" }}
> >
Donate Donate
@@ -396,7 +396,7 @@ const Navbar = () => {
handleLogout(); handleLogout();
}} }}
variant="outline" variant="outline"
className="w-full border-2 border-white/30 text-white hover:bg-white/10 rounded-lg" className="w-full border-2 border-white/30 text-white hover:bg-background/10 rounded-lg"
style={{ fontFamily: "'Poppins', sans-serif" }} style={{ fontFamily: "'Poppins', sans-serif" }}
data-testid="mobile-logout-button" data-testid="mobile-logout-button"
> >

View File

@@ -157,12 +157,12 @@ const PaymentActivationDialog = ({ open, onOpenChange, user, onSuccess }) => {
return ( return (
<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-background rounded-2xl">
<DialogHeader> <DialogHeader>
<DialogTitle className="text-2xl font-semibold text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}> <DialogTitle className="text-2xl font-semibold text-primary" style={{ fontFamily: "'Inter', sans-serif" }}>
Activate Manual Payment Activate Manual Payment
</DialogTitle> </DialogTitle>
<DialogDescription className="text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <DialogDescription className="text-muted-foreground" 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>
@@ -170,7 +170,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-[#422268] font-medium" style={{ fontFamily: "'Inter', sans-serif" }}> <Label htmlFor="plan_id" className="text-primary font-medium" style={{ fontFamily: "'Inter', sans-serif" }}>
Subscription Plan Subscription Plan
</Label> </Label>
<Select <Select
@@ -187,7 +187,7 @@ const PaymentActivationDialog = ({ open, onOpenChange, user, onSuccess }) => {
}); });
}} }}
> >
<SelectTrigger className="rounded-xl border-2 border-[#ddd8eb]"> <SelectTrigger className="rounded-xl border-2 border-chart-6">
<SelectValue placeholder="Select subscription plan" /> <SelectValue placeholder="Select subscription plan" />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
@@ -203,7 +203,7 @@ const PaymentActivationDialog = ({ open, onOpenChange, user, onSuccess }) => {
</SelectContent> </SelectContent>
</Select> </Select>
{selectedPlan && ( {selectedPlan && (
<p className="text-xs text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-xs text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{selectedPlan.description || `${selectedPlan.billing_cycle} subscription`} {selectedPlan.description || `${selectedPlan.billing_cycle} subscription`}
</p> </p>
)} )}
@@ -211,7 +211,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-[#422268] font-medium" style={{ fontFamily: "'Inter', sans-serif" }}> <Label htmlFor="amount" className="text-primary font-medium" style={{ fontFamily: "'Inter', sans-serif" }}>
Payment Amount ($) Payment Amount ($)
</Label> </Label>
<Input <Input
@@ -221,12 +221,12 @@ const PaymentActivationDialog = ({ open, onOpenChange, user, onSuccess }) => {
min="0" min="0"
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-[#ddd8eb] focus:border-[#664fa3]" className="rounded-xl border-2 border-chart-6 focus:border-muted-foreground"
required required
/> />
{selectedPlan && ( {selectedPlan && (
<p className="text-xs text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-xs text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Minimum: {formatPrice(selectedPlan.minimum_price_cents || selectedPlan.price_cents || 3000)} Minimum: {formatPrice(selectedPlan.minimum_price_cents || selectedPlan.price_cents || 3000)}
</p> </p>
)} )}
@@ -234,14 +234,14 @@ const PaymentActivationDialog = ({ open, onOpenChange, user, onSuccess }) => {
{/* Breakdown Display */} {/* Breakdown Display */}
{breakdown && breakdown.total >= breakdown.base && ( {breakdown && breakdown.total >= breakdown.base && (
<Card className="p-4 bg-[#f9f5ff] border border-[#DDD8EB]"> <Card className="p-4 bg-[#f9f5ff] border border-chart-6">
<div className="space-y-2 text-sm" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <div className="space-y-2 text-sm" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<div className="flex justify-between text-[#422268]"> <div className="flex justify-between text-primary">
<span>Membership Fee:</span> <span>Membership Fee:</span>
<span className="font-semibold">{formatPrice(breakdown.base)}</span> <span className="font-semibold">{formatPrice(breakdown.base)}</span>
</div> </div>
{breakdown.donation > 0 && ( {breakdown.donation > 0 && (
<div className="flex justify-between text-[#ff9e77]"> <div className="flex justify-between text-accent">
<span className="flex items-center gap-1"> <span className="flex items-center gap-1">
<Heart className="h-4 w-4" /> <Heart className="h-4 w-4" />
Additional Donation: Additional Donation:
@@ -249,7 +249,7 @@ const PaymentActivationDialog = ({ open, onOpenChange, user, onSuccess }) => {
<span className="font-semibold">{formatPrice(breakdown.donation)}</span> <span className="font-semibold">{formatPrice(breakdown.donation)}</span>
</div> </div>
)} )}
<div className="flex justify-between text-[#422268] font-bold text-base pt-2 border-t border-[#DDD8EB]"> <div className="flex justify-between text-primary font-bold text-base pt-2 border-t border-chart-6">
<span>Total:</span> <span>Total:</span>
<span>{formatPrice(breakdown.total)}</span> <span>{formatPrice(breakdown.total)}</span>
</div> </div>
@@ -259,17 +259,17 @@ const PaymentActivationDialog = ({ open, onOpenChange, user, onSuccess }) => {
{/* Payment Date */} {/* Payment Date */}
<div className="space-y-2"> <div className="space-y-2">
<Label htmlFor="payment_date" className="text-[#422268] font-medium" style={{ fontFamily: "'Inter', sans-serif" }}> <Label htmlFor="payment_date" className="text-primary 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-[#664fa3]" /> <Calendar className="absolute left-4 top-1/2 transform -translate-y-1/2 h-5 w-5 text-muted-foreground" />
<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-[#ddd8eb] focus:border-[#664fa3]" className="pl-12 rounded-xl border-2 border-chart-6 focus:border-muted-foreground"
required required
/> />
</div> </div>
@@ -277,14 +277,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-[#422268] font-medium" style={{ fontFamily: "'Inter', sans-serif" }}> <Label htmlFor="payment_method" className="text-primary 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-[#ddd8eb]"> <SelectTrigger className="rounded-xl border-2 border-chart-6">
<SelectValue placeholder="Select payment method" /> <SelectValue placeholder="Select payment method" />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
@@ -298,7 +298,7 @@ const PaymentActivationDialog = ({ open, onOpenChange, user, onSuccess }) => {
{/* Subscription Period */} {/* Subscription Period */}
<div className="space-y-3"> <div className="space-y-3">
<Label className="text-[#422268] font-medium" style={{ fontFamily: "'Inter', sans-serif" }}>Subscription Period</Label> <Label className="text-primary 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
@@ -306,9 +306,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-[#ddd8eb]" className="rounded border-chart-6"
/> />
<Label htmlFor="use_custom_period" className="text-sm text-[#664fa3] font-normal cursor-pointer" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <Label htmlFor="use_custom_period" className="text-sm text-muted-foreground 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>
@@ -316,39 +316,39 @@ 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-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}> <Label htmlFor="custom_period_start" className="text-sm text-primary" style={{ fontFamily: "'Inter', sans-serif" }}>
Start Date Start Date
</Label> </Label>
<Input <Input
id="custom_period_start" id="custom_period_start"
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-[#ddd8eb] focus:border-[#664fa3]" className="rounded-xl border-2 border-chart-6 focus:border-muted-foreground"
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-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}> <Label htmlFor="custom_period_end" className="text-sm text-primary" style={{ fontFamily: "'Inter', sans-serif" }}>
End Date End Date
</Label> </Label>
<Input <Input
id="custom_period_end" id="custom_period_end"
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-[#ddd8eb] focus:border-[#664fa3]" className="rounded-xl border-2 border-chart-6 focus:border-muted-foreground"
required={useCustomPeriod} required={useCustomPeriod}
/> />
</div> </div>
</div> </div>
) : ( ) : (
selectedPlan && ( selectedPlan && (
<div className="text-sm text-[#664fa3] bg-[#f1eef9] p-3 rounded-lg space-y-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <div className="text-sm text-muted-foreground bg-muted p-3 rounded-lg space-y-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{selectedPlan.custom_cycle_enabled ? ( {selectedPlan.custom_cycle_enabled ? (
<> <>
<p> <p>
<span className="font-medium text-[#422268]">Plan uses custom billing cycle:</span> <span className="font-medium text-primary">Plan uses custom billing cycle:</span>
<br /> <br />
{(() => { {(() => {
const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
@@ -367,8 +367,8 @@ const PaymentActivationDialog = ({ open, onOpenChange, user, onSuccess }) => {
<br /> <br />
Starts today, ends {selectedPlan.billing_cycle === 'monthly' ? '30 days' : Starts today, ends {selectedPlan.billing_cycle === 'monthly' ? '30 days' :
selectedPlan.billing_cycle === 'quarterly' ? '90 days' : selectedPlan.billing_cycle === 'quarterly' ? '90 days' :
selectedPlan.billing_cycle === 'yearly' ? '1 year' : selectedPlan.billing_cycle === 'yearly' ? '1 year' :
selectedPlan.billing_cycle === 'lifetime' ? 'lifetime' : '1 year'} from now selectedPlan.billing_cycle === 'lifetime' ? 'lifetime' : '1 year'} from now
</p> </p>
)} )}
</div> </div>
@@ -378,15 +378,15 @@ const PaymentActivationDialog = ({ open, onOpenChange, user, onSuccess }) => {
{/* Notes */} {/* Notes */}
<div className="space-y-2"> <div className="space-y-2">
<Label htmlFor="notes" className="text-[#422268] font-medium" style={{ fontFamily: "'Inter', sans-serif" }}> <Label htmlFor="notes" className="text-primary font-medium" style={{ fontFamily: "'Inter', sans-serif" }}>
Notes (Optional) Notes (Optional)
</Label> </Label>
<Textarea <Textarea
id="notes" id="notes"
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-[#ddd8eb] focus:border-[#664fa3] min-h-[100px]" className="rounded-xl border-2 border-chart-6 focus:border-muted-foreground min-h-[100px]"
/> />
</div> </div>
@@ -395,7 +395,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-[#ddd8eb]" className="rounded-full border-2 border-chart-6"
> >
Cancel Cancel
</Button> </Button>

View File

@@ -73,9 +73,9 @@ const PendingInvitationsTable = () => {
const getRoleBadge = (role) => { const getRoleBadge = (role) => {
const config = { const config = {
superadmin: { label: 'Superadmin', className: 'bg-[#664fa3] text-white' }, superadmin: { label: 'Superadmin', className: 'bg-muted-foreground text-white' },
admin: { label: 'Admin', className: 'bg-[#81B29A] text-white' }, admin: { label: 'Admin', className: 'bg-[#81B29A] text-white' },
member: { label: 'Member', className: 'bg-[#DDD8EB] text-[#422268]' } member: { label: 'Member', className: 'bg-chart-6 text-primary' }
}; };
const roleConfig = config[role] || { label: role, className: 'bg-gray-500 text-white' }; const roleConfig = config[role] || { label: role, className: 'bg-gray-500 text-white' };
@@ -111,7 +111,7 @@ const PendingInvitationsTable = () => {
if (loading) { if (loading) {
return ( return (
<div className="text-center py-8"> <div className="text-center py-8">
<p className="text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Loading invitations... Loading invitations...
</p> </p>
</div> </div>
@@ -120,12 +120,12 @@ const PendingInvitationsTable = () => {
if (invitations.length === 0) { if (invitations.length === 0) {
return ( return (
<Card className="p-12 bg-white rounded-2xl border border-[#ddd8eb] text-center"> <Card className="p-12 bg-background rounded-2xl border border-chart-6 text-center">
<Mail className="h-16 w-16 text-[#ddd8eb] mx-auto mb-4" /> <Mail className="h-16 w-16 text-chart-6 mx-auto mb-4" />
<h3 className="text-xl font-semibold text-[#422268] mb-2" style={{ fontFamily: "'Inter', sans-serif" }}> <h3 className="text-xl font-semibold text-primary mb-2" style={{ fontFamily: "'Inter', sans-serif" }}>
No Pending Invitations No Pending Invitations
</h3> </h3>
<p className="text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
All invitations have been accepted or expired All invitations have been accepted or expired
</p> </p>
</Card> </Card>
@@ -134,37 +134,37 @@ const PendingInvitationsTable = () => {
return ( return (
<> <>
<Card className="bg-white rounded-2xl border border-[#ddd8eb] overflow-hidden"> <Card className="bg-background rounded-2xl border border-chart-6 overflow-hidden">
<Table> <Table>
<TableHeader> <TableHeader>
<TableRow className="bg-[#DDD8EB] hover:bg-[#DDD8EB]"> <TableRow className="bg-chart-6 hover:bg-chart-6">
<TableHead className="text-[#422268] font-semibold">Email</TableHead> <TableHead className="text-primary font-semibold">Email</TableHead>
<TableHead className="text-[#422268] font-semibold">Name</TableHead> <TableHead className="text-primary font-semibold">Name</TableHead>
<TableHead className="text-[#422268] font-semibold">Role</TableHead> <TableHead className="text-primary font-semibold">Role</TableHead>
<TableHead className="text-[#422268] font-semibold">Invited</TableHead> <TableHead className="text-primary font-semibold">Invited</TableHead>
<TableHead className="text-[#422268] font-semibold">Expires</TableHead> <TableHead className="text-primary font-semibold">Expires</TableHead>
<TableHead className="text-[#422268] font-semibold text-right">Actions</TableHead> <TableHead className="text-primary font-semibold text-right">Actions</TableHead>
</TableRow> </TableRow>
</TableHeader> </TableHeader>
<TableBody> <TableBody>
{invitations.map((invitation) => ( {invitations.map((invitation) => (
<TableRow key={invitation.id} className="hover:bg-[#F9F8FB]"> <TableRow key={invitation.id} className="hover:bg-[#F9F8FB]">
<TableCell className="font-medium text-[#422268]"> <TableCell className="font-medium text-primary">
{invitation.email} {invitation.email}
</TableCell> </TableCell>
<TableCell className="text-[#664fa3]"> <TableCell className="text-muted-foreground">
{invitation.first_name && invitation.last_name {invitation.first_name && invitation.last_name
? `${invitation.first_name} ${invitation.last_name}` ? `${invitation.first_name} ${invitation.last_name}`
: '-'} : '-'}
</TableCell> </TableCell>
<TableCell>{getRoleBadge(invitation.role)}</TableCell> <TableCell>{getRoleBadge(invitation.role)}</TableCell>
<TableCell className="text-[#664fa3]"> <TableCell className="text-muted-foreground">
{new Date(invitation.invited_at).toLocaleDateString()} {new Date(invitation.invited_at).toLocaleDateString()}
</TableCell> </TableCell>
<TableCell> <TableCell>
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<Clock className={`h-4 w-4 ${isExpiringSoon(invitation.expires_at) ? 'text-orange-500' : 'text-[#664fa3]'}`} /> <Clock className={`h-4 w-4 ${isExpiringSoon(invitation.expires_at) ? 'text-orange-500' : 'text-muted-foreground'}`} />
<span className={`text-sm ${isExpiringSoon(invitation.expires_at) ? 'text-orange-500 font-semibold' : 'text-[#664fa3]'}`}> <span className={`text-sm ${isExpiringSoon(invitation.expires_at) ? 'text-orange-500 font-semibold' : 'text-muted-foreground'}`}>
{formatDate(invitation.expires_at)} {formatDate(invitation.expires_at)}
</span> </span>
</div> </div>
@@ -208,10 +208,10 @@ const PendingInvitationsTable = () => {
<AlertDialog open={revokeDialog.open} onOpenChange={(open) => setRevokeDialog({ open, invitation: null })}> <AlertDialog open={revokeDialog.open} onOpenChange={(open) => setRevokeDialog({ open, invitation: null })}>
<AlertDialogContent className="rounded-2xl"> <AlertDialogContent className="rounded-2xl">
<AlertDialogHeader> <AlertDialogHeader>
<AlertDialogTitle className="text-2xl text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}> <AlertDialogTitle className="text-2xl text-primary" style={{ fontFamily: "'Inter', sans-serif" }}>
Revoke Invitation Revoke Invitation
</AlertDialogTitle> </AlertDialogTitle>
<AlertDialogDescription className="text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <AlertDialogDescription className="text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Are you sure you want to revoke the invitation for{' '} Are you sure you want to revoke the invitation for{' '}
<span className="font-semibold">{revokeDialog.invitation?.email}</span>? <span className="font-semibold">{revokeDialog.invitation?.email}</span>?
This action cannot be undone. This action cannot be undone.

View File

@@ -118,7 +118,7 @@ const PlanDialog = ({ open, onOpenChange, plan, onSuccess }) => {
// Validate custom cycle dates if enabled // Validate custom cycle dates if enabled
if (formData.custom_cycle_enabled) { if (formData.custom_cycle_enabled) {
if (!formData.custom_cycle_start_month || !formData.custom_cycle_start_day || if (!formData.custom_cycle_start_month || !formData.custom_cycle_start_day ||
!formData.custom_cycle_end_month || !formData.custom_cycle_end_day) { !formData.custom_cycle_end_month || !formData.custom_cycle_end_day) {
toast.error('All custom cycle dates must be provided'); toast.error('All custom cycle dates must be provided');
setLoading(false); setLoading(false);
return; return;
@@ -161,10 +161,10 @@ const PlanDialog = ({ open, onOpenChange, plan, onSuccess }) => {
<Dialog open={open} onOpenChange={onOpenChange}> <Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent className="sm:max-w-[700px] max-h-[90vh] overflow-y-auto"> <DialogContent className="sm:max-w-[700px] max-h-[90vh] overflow-y-auto">
<DialogHeader> <DialogHeader>
<DialogTitle className="text-2xl text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}> <DialogTitle className="text-2xl text-primary" style={{ fontFamily: "'Inter', sans-serif" }}>
{plan ? 'Edit Plan' : 'Create New Plan'} {plan ? 'Edit Plan' : 'Create New Plan'}
</DialogTitle> </DialogTitle>
<DialogDescription className="text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <DialogDescription className="text-muted-foreground" 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>
@@ -197,8 +197,8 @@ const PlanDialog = ({ open, onOpenChange, plan, onSuccess }) => {
</div> </div>
{/* Dynamic Pricing */} {/* Dynamic Pricing */}
<div className="border-2 border-[#DDD8EB] rounded-lg p-4 space-y-4"> <div className="border-2 border-chart-6 rounded-lg p-4 space-y-4">
<h3 className="font-semibold text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}> <h3 className="font-semibold text-primary" style={{ fontFamily: "'Inter', sans-serif" }}>
Dynamic Pricing Dynamic Pricing
</h3> </h3>
@@ -216,7 +216,7 @@ const PlanDialog = ({ open, onOpenChange, plan, onSuccess }) => {
required required
className="mt-2" className="mt-2"
/> />
<p className="text-xs text-[#664fa3] mt-1">Minimum $30</p> <p className="text-xs text-muted-foreground mt-1">Minimum $30</p>
</div> </div>
<div> <div>
@@ -232,7 +232,7 @@ const PlanDialog = ({ open, onOpenChange, plan, onSuccess }) => {
required required
className="mt-2" className="mt-2"
/> />
<p className="text-xs text-[#664fa3] mt-1">Pre-filled amount</p> <p className="text-xs text-muted-foreground mt-1">Pre-filled amount</p>
</div> </div>
</div> </div>
@@ -240,7 +240,7 @@ const PlanDialog = ({ open, onOpenChange, plan, onSuccess }) => {
<div className="flex items-center justify-between pt-2"> <div className="flex items-center justify-between pt-2">
<div> <div>
<Label htmlFor="allow_donation">Allow Donations</Label> <Label htmlFor="allow_donation">Allow Donations</Label>
<p className="text-sm text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-sm text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Members can pay more than minimum Members can pay more than minimum
</p> </p>
</div> </div>
@@ -252,7 +252,7 @@ const PlanDialog = ({ open, onOpenChange, plan, onSuccess }) => {
onChange={(e) => setFormData({ ...formData, allow_donation: e.target.checked })} onChange={(e) => setFormData({ ...formData, allow_donation: 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-[#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> <div className="w-11 h-6 bg-gray-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-muted-foreground/20 rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-background after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-[#81B29A]"></div>
</label> </label>
</div> </div>
</div> </div>
@@ -279,11 +279,11 @@ const PlanDialog = ({ open, onOpenChange, plan, onSuccess }) => {
{/* Custom Billing Cycle Dates */} {/* Custom Billing Cycle Dates */}
{formData.billing_cycle === 'custom' && ( {formData.billing_cycle === 'custom' && (
<div className="border-2 border-[#DDD8EB] rounded-lg p-4 space-y-4"> <div className="border-2 border-chart-6 rounded-lg p-4 space-y-4">
<h3 className="font-semibold text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}> <h3 className="font-semibold text-primary" style={{ fontFamily: "'Inter', sans-serif" }}>
Custom Billing Period Custom Billing Period
</h3> </h3>
<p className="text-sm text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-sm text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Set recurring date range (e.g., Jan 1 - Dec 31 for calendar year) Set recurring date range (e.g., Jan 1 - Dec 31 for calendar year)
</p> </p>
@@ -349,8 +349,8 @@ const PlanDialog = ({ open, onOpenChange, plan, onSuccess }) => {
</div> </div>
</div> </div>
<div className="bg-[#f9f5ff] border border-[#DDD8EB] rounded p-3"> <div className="bg-[#f9f5ff] border border-chart-6 rounded p-3">
<p className="text-sm text-[#422268]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-sm text-primary" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<strong>Example:</strong> Jan 1 - Dec 31 for calendar year, or Jul 1 - Jun 30 for fiscal year <strong>Example:</strong> Jan 1 - Dec 31 for calendar year, or Jul 1 - Jun 30 for fiscal year
</p> </p>
</div> </div>
@@ -361,7 +361,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-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-sm text-muted-foreground" 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>
@@ -373,7 +373,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-[#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> <div className="w-11 h-6 bg-gray-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-muted-foreground/20 rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-background after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-[#81B29A]"></div>
</label> </label>
</div> </div>
@@ -389,7 +389,7 @@ const PlanDialog = ({ open, onOpenChange, plan, onSuccess }) => {
<Button <Button
type="submit" type="submit"
disabled={loading} disabled={loading}
className="bg-[#DDD8EB] text-[#422268] hover:bg-white" className="bg-chart-6 text-primary hover:bg-background"
> >
{loading ? ( {loading ? (
<> <>

View File

@@ -19,28 +19,28 @@ const PublicFooter = () => {
<div className="pb-2 lg:pb-4"> <div className="pb-2 lg:pb-4">
<p className="text-white text-xl font-medium" style={{ fontFamily: "'Poppins', sans-serif" }}>About</p> <p className="text-white text-xl font-medium" style={{ fontFamily: "'Poppins', sans-serif" }}>About</p>
</div> </div>
<Link to="/about/history" className="text-[#ddd8eb] text-sm sm:text-base font-medium hover:text-white transition-colors" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>History</Link> <Link to="/about/history" className="text-chart-6 text-sm sm:text-base font-medium hover:text-white transition-colors" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>History</Link>
<Link to="/about/mission-values" className="text-[#ddd8eb] text-sm sm:text-base font-medium hover:text-white transition-colors" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Mission and Values</Link> <Link to="/about/mission-values" className="text-chart-6 text-sm sm:text-base font-medium hover:text-white transition-colors" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Mission and Values</Link>
<Link to="/about/board" className="text-[#ddd8eb] text-sm sm:text-base font-medium hover:text-white transition-colors" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Board of Directors</Link> <Link to="/about/board" className="text-chart-6 text-sm sm:text-base font-medium hover:text-white transition-colors" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Board of Directors</Link>
</div> </div>
<div className="hidden md:flex flex-col gap-2 items-start text-left w-full sm:w-auto sm:min-w-[148px]"> <div className="hidden md:flex flex-col gap-2 items-start text-left w-full sm:w-auto sm:min-w-[148px]">
<div className="pb-2 lg:pb-4"> <div className="pb-2 lg:pb-4">
<p className="text-white text-xl font-medium" style={{ fontFamily: "'Poppins', sans-serif" }}>Connect</p> <p className="text-white text-xl font-medium" style={{ fontFamily: "'Poppins', sans-serif" }}>Connect</p>
</div> </div>
<Link to="/become-a-member" className="text-[#ddd8eb] text-sm sm:text-base font-medium hover:text-white transition-colors" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Become a Member</Link> <Link to="/become-a-member" className="text-chart-6 text-sm sm:text-base font-medium hover:text-white transition-colors" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Become a Member</Link>
<Link to="/contact-us" className="text-[#ddd8eb] text-sm sm:text-base font-medium hover:text-white transition-colors" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Contact Us</Link> <Link to="/contact-us" className="text-chart-6 text-sm sm:text-base font-medium hover:text-white transition-colors" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Contact Us</Link>
<Link to="/resources" className="text-[#ddd8eb] text-sm sm:text-base font-medium hover:text-white transition-colors" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Resources</Link> <Link to="/resources" className="text-chart-6 text-sm sm:text-base font-medium hover:text-white transition-colors" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Resources</Link>
</div> </div>
<div className="flex flex-col gap-2 items-center 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 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="pb-4 w-full flex justify-center lg:justify-start"> <div className="pb-4 w-full flex justify-center lg:justify-start">
<Link to="/donate" className="block"> <Link to="/donate" className="block">
<Button className="bg-[#ff9e77] hover:bg-[#ff8c64] text-[#48286e] rounded-full px-12 lg:px-16 py-6 text-lg sm:text-lg font-medium "> <Button className="bg-accent hover:bg-[#ff8c64] text-[#48286e] rounded-full px-12 lg:px-16 py-6 text-lg sm:text-lg font-medium ">
Donate Donate
</Button> </Button>
</Link> </Link>
</div> </div>
<p className="text-[#ddd8eb] text-sm sm:text-base font-medium text-center md:text-left w-full" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-chart-6 text-sm sm:text-base font-medium text-center md:text-left w-full" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
LOAF is supported by<br />the Hollyfield Foundation LOAF is supported by<br />the Hollyfield Foundation
</p> </p>
</div> </div>

View File

@@ -46,7 +46,7 @@ const PublicNavbar = () => {
const getDesktopLinkClasses = (path) => { const getDesktopLinkClasses = (path) => {
const baseClasses = "text-[17.5px] font-medium transition-all px-3 py-1 rounded-md"; const baseClasses = "text-[17.5px] font-medium transition-all px-3 py-1 rounded-md";
if (isActive(path)) { if (isActive(path)) {
return `${baseClasses} text-[#ff9e77] hover:text-[#ff8c64] `; return `${baseClasses} text-accent hover:text-[#ff8c64] `;
} }
return `${baseClasses} text-white hover:opacity-80`; return `${baseClasses} text-white hover:opacity-80`;
}; };
@@ -55,7 +55,7 @@ const PublicNavbar = () => {
const getMobileLinkClasses = (path) => { const getMobileLinkClasses = (path) => {
const baseClasses = "text-base font-medium px-4 py-3 rounded-md transition-colors"; const baseClasses = "text-base font-medium px-4 py-3 rounded-md transition-colors";
if (isActive(path)) { if (isActive(path)) {
return `${baseClasses} bg-[#ff9e77] hover:bg-[#ff8c64] text-[#48286e]`; return `${baseClasses} bg-accent hover:bg-[#ff8c64] text-[#48286e]`;
} }
return `${baseClasses} text-white hover:bg-[#48286e]`; return `${baseClasses} text-white hover:bg-[#48286e]`;
}; };
@@ -64,9 +64,9 @@ const PublicNavbar = () => {
const getMobileSubLinkClasses = (path) => { const getMobileSubLinkClasses = (path) => {
const baseClasses = "text-sm font-medium px-6 py-2 rounded-md transition-colors block"; const baseClasses = "text-sm font-medium px-6 py-2 rounded-md transition-colors block";
if (isActive(path)) { if (isActive(path)) {
return `${baseClasses} bg-[#ff9e77] hover:bg-[#ff8c64] text-[#48286e]`; return `${baseClasses} bg-accent hover:bg-[#ff8c64] text-[#48286e]`;
} }
return `${baseClasses} text-[#ddd8eb] hover:bg-[#48286e] hover:text-white`; return `${baseClasses} text-chart-6 hover:bg-[#48286e] hover:text-white`;
}; };
return ( return (
@@ -96,7 +96,7 @@ const PublicNavbar = () => {
</div> </div>
<Link to="/donate"> <Link to="/donate">
<Button <Button
className="bg-[#ff9e77] hover:bg-[#ff8c64] text-[#48286e] rounded-[25px] px-[50px] py-[5px] text-[16.5px] font-semibold h-[41px]" className="bg-accent hover:bg-[#ff8c64] text-[#48286e] rounded-[25px] px-[50px] py-[5px] text-[16.5px] font-semibold h-[41px]"
style={{ fontFamily: "'Montserrat', sans-serif" }} style={{ fontFamily: "'Montserrat', sans-serif" }}
> >
Donate Donate
@@ -105,7 +105,7 @@ const PublicNavbar = () => {
</header> </header>
{/* Main Header - Navigation */} {/* Main Header - Navigation */}
<header className=" bg-[#664fa3] px-[20px] py-2 flex justify-between items-center"> <header className=" bg-muted-foreground px-[20px] py-2 flex justify-between items-center">
<Link to="/"> <Link to="/">
<img src={loafLogo} alt="LOAF Logo" className="h-16 w-16 sm:h-20 sm:w-20 md:h-28 md:w-28 object-contain" /> <img src={loafLogo} alt="LOAF Logo" className="h-16 w-16 sm:h-20 sm:w-20 md:h-28 md:w-28 object-contain" />
</Link> </Link>
@@ -132,28 +132,28 @@ const PublicNavbar = () => {
<DropdownMenuTrigger asChild> <DropdownMenuTrigger asChild>
<button <button
className={`${isAboutActive() className={`${isAboutActive()
? "text-[#ff9e77] hover:text-[#ff8c64]" ? "text-accent hover:text-[#ff8c64]"
: "text-white hover:opacity-80"} text-[17.5px] font-medium transition-all flex items-center gap-1 bg-transparent border-none cursor-pointer px-3 py-1 rounded-md`} : "text-white hover:opacity-80"} text-[17.5px] font-medium transition-all flex items-center gap-1 bg-transparent border-none cursor-pointer px-3 py-1 rounded-md`}
style={{ fontFamily: "'Nunito Sans', sans-serif" }}> style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
About Us About Us
<ChevronDown className="h-4 w-4" /> <ChevronDown className="h-4 w-4" />
</button> </button>
</DropdownMenuTrigger> </DropdownMenuTrigger>
<DropdownMenuContent align="start" className="bg-white min-w-[220px]"> <DropdownMenuContent align="start" className="bg-background min-w-[220px]">
<DropdownMenuItem asChild> <DropdownMenuItem asChild>
<Link to="/about/history" className="w-full px-3 py-2 text-[#48286e] hover:bg-[#f1eef9] cursor-pointer" <Link to="/about/history" className="w-full px-3 py-2 text-[#48286e] hover:bg-muted cursor-pointer"
style={{ fontFamily: "'Nunito Sans', sans-serif" }}> style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
History History
</Link> </Link>
</DropdownMenuItem> </DropdownMenuItem>
<DropdownMenuItem asChild> <DropdownMenuItem asChild>
<Link to="/about/mission-values" className="w-full px-3 py-2 text-[#48286e] hover:bg-[#f1eef9] cursor-pointer" <Link to="/about/mission-values" className="w-full px-3 py-2 text-[#48286e] hover:bg-muted cursor-pointer"
style={{ fontFamily: "'Nunito Sans', sans-serif" }}> style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Mission and Values Mission and Values
</Link> </Link>
</DropdownMenuItem> </DropdownMenuItem>
<DropdownMenuItem asChild> <DropdownMenuItem asChild>
<Link to="/about/board" className="w-full px-3 py-2 text-[#48286e] hover:bg-[#f1eef9] cursor-pointer" <Link to="/about/board" className="w-full px-3 py-2 text-[#48286e] hover:bg-muted cursor-pointer"
style={{ fontFamily: "'Nunito Sans', sans-serif" }}> style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Board of Directors Board of Directors
</Link> </Link>
@@ -204,7 +204,7 @@ const PublicNavbar = () => {
/> />
{/* Drawer */} {/* Drawer */}
<div className="fixed right-0 top-0 h-full w-[280px] bg-[#664fa3] shadow-xl overflow-y-auto"> <div className="fixed right-0 top-0 h-full w-[280px] bg-muted-foreground shadow-xl overflow-y-auto">
{/* Header */} {/* Header */}
<div className="flex justify-between items-center p-6 border-b border-[#48286e]"> <div className="flex justify-between items-center p-6 border-b border-[#48286e]">
<span className="text-white text-lg font-semibold" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <span className="text-white text-lg font-semibold" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
@@ -233,7 +233,7 @@ const PublicNavbar = () => {
{/* About Us Section */} {/* About Us Section */}
<div className="space-y-2"> <div className="space-y-2">
<p <p
className={`text-base font-semibold px-4 py-2 rounded-md ${isAboutActive() ? 'text-[#ff9e77]' : 'text-white'}`} className={`text-base font-semibold px-4 py-2 rounded-md ${isAboutActive() ? 'text-accent' : 'text-white'}`}
style={{ fontFamily: "'Nunito Sans', sans-serif" }} style={{ fontFamily: "'Nunito Sans', sans-serif" }}
> >
About Us About Us
@@ -329,7 +329,7 @@ const PublicNavbar = () => {
onClick={() => setIsMobileMenuOpen(false)} onClick={() => setIsMobileMenuOpen(false)}
className="block w-full" className="block w-full"
> >
<Button className="w-full bg-[#ff9e77] hover:bg-[#ff8c64] text-[#48286e] rounded-[25px] px-6 py-3 text-base font-semibold"> <Button className="w-full bg-accent hover:bg-[#ff8c64] text-[#48286e] rounded-[25px] px-6 py-3 text-base font-semibold">
Donate Donate
</Button> </Button>
</Link> </Link>

View File

@@ -31,33 +31,33 @@ export default function RejectionDialog({ open, onOpenChange, onConfirm, user, l
return ( return (
<Dialog open={open} onOpenChange={handleClose}> <Dialog open={open} onOpenChange={handleClose}>
<DialogContent className="sm:max-w-[500px] rounded-2xl border-2 border-[#ddd8eb]"> <DialogContent className="sm:max-w-[500px] rounded-2xl border-2 border-chart-6">
<DialogHeader> <DialogHeader>
<div className="flex items-center gap-3 mb-2"> <div className="flex items-center gap-3 mb-2">
<div className="p-3 bg-red-100 rounded-full"> <div className="p-3 bg-red-100 rounded-full">
<AlertTriangle className="h-6 w-6 text-red-600" /> <AlertTriangle className="h-6 w-6 text-red-600" />
</div> </div>
<DialogTitle className="text-2xl font-semibold text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}> <DialogTitle className="text-2xl font-semibold text-primary" style={{ fontFamily: "'Inter', sans-serif" }}>
Reject Application Reject Application
</DialogTitle> </DialogTitle>
</div> </div>
<DialogDescription className="text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <DialogDescription className="text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
You are about to reject <strong>{user?.first_name} {user?.last_name}</strong>'s membership application. You are about to reject <strong>{user?.first_name} {user?.last_name}</strong>'s membership application.
</DialogDescription> </DialogDescription>
</DialogHeader> </DialogHeader>
<div className="space-y-4 py-4"> <div className="space-y-4 py-4">
<div className="bg-[#f9f5ff] border border-[#ddd8eb] rounded-lg p-4"> <div className="bg-[#f9f5ff] border border-chart-6 rounded-lg p-4">
<p className="text-sm text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-sm text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<strong>Applicant:</strong> {user?.email} <strong>Applicant:</strong> {user?.email}
</p> </p>
<p className="text-sm text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-sm text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<strong>Status:</strong> {user?.status} <strong>Status:</strong> {user?.status}
</p> </p>
</div> </div>
<div className="space-y-2"> <div className="space-y-2">
<Label htmlFor="reason" className="text-[#422268] font-medium"> <Label htmlFor="reason" className="text-primary font-medium">
Rejection Reason <span className="text-red-500">*</span> Rejection Reason <span className="text-red-500">*</span>
</Label> </Label>
<Textarea <Textarea
@@ -68,13 +68,13 @@ export default function RejectionDialog({ open, onOpenChange, onConfirm, user, l
setError(''); setError('');
}} }}
placeholder="Please provide a clear reason for rejection. This will be sent to the applicant." placeholder="Please provide a clear reason for rejection. This will be sent to the applicant."
className="rounded-xl border-2 border-[#ddd8eb] focus:border-red-500 min-h-[120px]" className="rounded-xl border-2 border-chart-6 focus:border-red-500 min-h-[120px]"
disabled={loading} disabled={loading}
/> />
{error && ( {error && (
<p className="text-sm text-red-500">{error}</p> <p className="text-sm text-red-500">{error}</p>
)} )}
<p className="text-xs text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-xs text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
The applicant will receive an email with this reason. The applicant will receive an email with this reason.
</p> </p>
</div> </div>
@@ -85,7 +85,7 @@ export default function RejectionDialog({ open, onOpenChange, onConfirm, user, l
type="button" type="button"
onClick={handleClose} onClick={handleClose}
variant="outline" variant="outline"
className="border-2 border-[#ddd8eb] text-[#664fa3] hover:bg-[#f1eef9] rounded-full px-6" className="border-2 border-chart-6 text-muted-foreground hover:bg-muted rounded-full px-6"
disabled={loading} disabled={loading}
> >
<X className="h-4 w-4 mr-2" /> <X className="h-4 w-4 mr-2" />

View File

@@ -370,15 +370,15 @@ export default function WordPressImportWizard({ open, onOpenChange, onSuccess })
const Step1Upload = () => ( const Step1Upload = () => (
<div className="space-y-6"> <div className="space-y-6">
<div> <div>
<h3 className="text-lg font-semibold text-[#422268] mb-2">Upload WordPress CSV Export</h3> <h3 className="text-lg font-semibold text-primary mb-2">Upload WordPress CSV Export</h3>
<p className="text-sm text-[#664fa3]"> <p className="text-sm text-muted-foreground">
Select the WordPress user export CSV file. The file will be analyzed for data quality issues. Select the WordPress user export CSV file. The file will be analyzed for data quality issues.
</p> </p>
</div> </div>
<Card className="p-6 border-2 border-dashed border-[#ddd8eb] bg-[#f9f5ff]"> <Card className="p-6 border-2 border-dashed border-chart-6 bg-[#f9f5ff]">
<div className="flex flex-col items-center gap-4"> <div className="flex flex-col items-center gap-4">
<Upload className="h-12 w-12 text-[#664fa3]" /> <Upload className="h-12 w-12 text-muted-foreground" />
<div className="text-center"> <div className="text-center">
<Input <Input
type="file" type="file"
@@ -387,7 +387,7 @@ export default function WordPressImportWizard({ open, onOpenChange, onSuccess })
className="max-w-xs" className="max-w-xs"
/> />
{uploadedFile && ( {uploadedFile && (
<p className="text-sm text-[#664fa3] mt-2"> <p className="text-sm text-muted-foreground mt-2">
Selected: {uploadedFile.name} Selected: {uploadedFile.name}
</p> </p>
)} )}
@@ -399,7 +399,7 @@ export default function WordPressImportWizard({ open, onOpenChange, onSuccess })
<Button <Button
onClick={handleUpload} onClick={handleUpload}
disabled={uploading} disabled={uploading}
className="w-full bg-[#664fa3] hover:bg-[#422268]" className="w-full bg-muted-foreground hover:bg-primary"
> >
{uploading ? ( {uploading ? (
<> <>
@@ -465,8 +465,8 @@ export default function WordPressImportWizard({ open, onOpenChange, onSuccess })
const Step2FieldMapping = () => ( const Step2FieldMapping = () => (
<div className="space-y-6"> <div className="space-y-6">
<div> <div>
<h3 className="text-lg font-semibold text-[#422268] mb-2">Field Mapping</h3> <h3 className="text-lg font-semibold text-primary mb-2">Field Mapping</h3>
<p className="text-sm text-[#664fa3]"> <p className="text-sm text-muted-foreground">
WordPress fields have been automatically mapped to LOAF platform fields. WordPress fields have been automatically mapped to LOAF platform fields.
</p> </p>
</div> </div>
@@ -537,20 +537,20 @@ export default function WordPressImportWizard({ open, onOpenChange, onSuccess })
const Step3ReviewStatus = () => ( const Step3ReviewStatus = () => (
<div className="space-y-6"> <div className="space-y-6">
<div> <div>
<h3 className="text-lg font-semibold text-[#422268] mb-2">Review & Adjust User Status</h3> <h3 className="text-lg font-semibold text-primary mb-2">Review & Adjust User Status</h3>
<p className="text-sm text-[#664fa3]"> <p className="text-sm text-muted-foreground">
Review suggested status mappings and override as needed before import. Review suggested status mappings and override as needed before import.
</p> </p>
</div> </div>
{/* Bulk edit toolbar */} {/* Bulk edit toolbar */}
<Card className="p-4 bg-[#f9f5ff] border-[#ddd8eb]"> <Card className="p-4 bg-[#f9f5ff] border-chart-6">
<div className="flex items-center gap-4"> <div className="flex items-center gap-4">
<Checkbox <Checkbox
checked={selectedRows.size === previewData.length && previewData.length > 0} checked={selectedRows.size === previewData.length && previewData.length > 0}
onCheckedChange={toggleSelectAll} onCheckedChange={toggleSelectAll}
/> />
<span className="text-sm text-[#664fa3] font-medium"> <span className="text-sm text-muted-foreground font-medium">
{selectedRows.size > 0 ? `${selectedRows.size} selected` : 'Select all'} {selectedRows.size > 0 ? `${selectedRows.size} selected` : 'Select all'}
</span> </span>
{selectedRows.size > 0 && ( {selectedRows.size > 0 && (
@@ -572,7 +572,7 @@ export default function WordPressImportWizard({ open, onOpenChange, onSuccess })
{/* Data table */} {/* Data table */}
{loading ? ( {loading ? (
<div className="flex items-center justify-center py-12"> <div className="flex items-center justify-center py-12">
<Loader2 className="h-8 w-8 animate-spin text-[#664fa3]" /> <Loader2 className="h-8 w-8 animate-spin text-muted-foreground" />
</div> </div>
) : ( ) : (
<div className="border rounded-lg overflow-hidden"> <div className="border rounded-lg overflow-hidden">
@@ -606,7 +606,7 @@ export default function WordPressImportWizard({ open, onOpenChange, onSuccess })
{row.first_name} {row.last_name} {row.first_name} {row.last_name}
</TableCell> </TableCell>
<TableCell> <TableCell>
<Badge className="bg-[#ddd8eb] text-[#422268]"> <Badge className="bg-chart-6 text-primary">
{row.wordpress_roles?.join(', ') || 'N/A'} {row.wordpress_roles?.join(', ') || 'N/A'}
</Badge> </Badge>
</TableCell> </TableCell>
@@ -651,7 +651,7 @@ export default function WordPressImportWizard({ open, onOpenChange, onSuccess })
{/* Pagination */} {/* Pagination */}
{totalPages > 1 && ( {totalPages > 1 && (
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<p className="text-sm text-[#664fa3]"> <p className="text-sm text-muted-foreground">
Page {currentPage} of {totalPages} Page {currentPage} of {totalPages}
</p> </p>
<div className="flex gap-2"> <div className="flex gap-2">
@@ -689,41 +689,41 @@ export default function WordPressImportWizard({ open, onOpenChange, onSuccess })
return ( return (
<div className="space-y-6"> <div className="space-y-6">
<div> <div>
<h3 className="text-lg font-semibold text-[#422268] mb-2">Import Preview</h3> <h3 className="text-lg font-semibold text-primary mb-2">Import Preview</h3>
<p className="text-sm text-[#664fa3]"> <p className="text-sm text-muted-foreground">
Review the final import settings before execution. Review the final import settings before execution.
</p> </p>
</div> </div>
<div className="grid md:grid-cols-3 gap-4"> <div className="grid md:grid-cols-3 gap-4">
<Card className="p-6"> <Card className="p-6">
<p className="text-sm text-[#664fa3]">Total Users</p> <p className="text-sm text-muted-foreground">Total Users</p>
<p className="text-3xl font-semibold text-[#422268]">{analysisResult?.total_rows}</p> <p className="text-3xl font-semibold text-primary">{analysisResult?.total_rows}</p>
</Card> </Card>
<Card className="p-6"> <Card className="p-6">
<p className="text-sm text-[#664fa3]">Status Overrides</p> <p className="text-sm text-muted-foreground">Status Overrides</p>
<p className="text-3xl font-semibold text-[#422268]">{overrideCount}</p> <p className="text-3xl font-semibold text-primary">{overrideCount}</p>
</Card> </Card>
<Card className="p-6"> <Card className="p-6">
<p className="text-sm text-[#664fa3]">Expected Imports</p> <p className="text-sm text-muted-foreground">Expected Imports</p>
<p className="text-3xl font-semibold text-[#422268]">{analysisResult?.valid_rows}</p> <p className="text-3xl font-semibold text-primary">{analysisResult?.valid_rows}</p>
</Card> </Card>
</div> </div>
<Card className="p-6"> <Card className="p-6">
<h4 className="font-semibold text-[#422268] mb-4">Import Options</h4> <h4 className="font-semibold text-primary mb-4">Import Options</h4>
<div className="space-y-3"> <div className="space-y-3">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<CheckCircle className="h-5 w-5 text-green-600" /> <CheckCircle className="h-5 w-5 text-green-600" />
<span className="text-sm text-[#664fa3]">Send password reset emails to all imported users</span> <span className="text-sm text-muted-foreground">Send password reset emails to all imported users</span>
</div> </div>
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<CheckCircle className="h-5 w-5 text-green-600" /> <CheckCircle className="h-5 w-5 text-green-600" />
<span className="text-sm text-[#664fa3]">Skip rows with errors and continue import</span> <span className="text-sm text-muted-foreground">Skip rows with errors and continue import</span>
</div> </div>
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<CheckCircle className="h-5 w-5 text-green-600" /> <CheckCircle className="h-5 w-5 text-green-600" />
<span className="text-sm text-[#664fa3]">Full rollback capability available after import</span> <span className="text-sm text-muted-foreground">Full rollback capability available after import</span>
</div> </div>
</div> </div>
</Card> </Card>
@@ -748,10 +748,10 @@ export default function WordPressImportWizard({ open, onOpenChange, onSuccess })
const Step5Execute = () => ( const Step5Execute = () => (
<div className="space-y-6"> <div className="space-y-6">
<div> <div>
<h3 className="text-lg font-semibold text-[#422268] mb-2"> <h3 className="text-lg font-semibold text-primary mb-2">
{importing ? 'Import in Progress...' : 'Ready to Import'} {importing ? 'Import in Progress...' : 'Ready to Import'}
</h3> </h3>
<p className="text-sm text-[#664fa3]"> <p className="text-sm text-muted-foreground">
{importing {importing
? 'Please wait while users are imported. This may take a few minutes.' ? 'Please wait while users are imported. This may take a few minutes.'
: 'Click "Start Import" to begin importing users.'} : 'Click "Start Import" to begin importing users.'}
@@ -761,7 +761,7 @@ export default function WordPressImportWizard({ open, onOpenChange, onSuccess })
{importing && ( {importing && (
<div className="space-y-4"> <div className="space-y-4">
<Progress value={importProgress} className="w-full" /> <Progress value={importProgress} className="w-full" />
<p className="text-center text-sm text-[#664fa3]"> <p className="text-center text-sm text-muted-foreground">
{importProgress.toFixed(1)}% complete {importProgress.toFixed(1)}% complete
</p> </p>
</div> </div>
@@ -770,7 +770,7 @@ export default function WordPressImportWizard({ open, onOpenChange, onSuccess })
{!importing && !importResults && ( {!importing && !importResults && (
<Button <Button
onClick={handleExecuteImport} onClick={handleExecuteImport}
className="w-full bg-[#664fa3] hover:bg-[#422268] py-6 text-lg" className="w-full bg-muted-foreground hover:bg-primary py-6 text-lg"
> >
<Play className="mr-2 h-5 w-5" /> <Play className="mr-2 h-5 w-5" />
Start Import Start Import
@@ -786,8 +786,8 @@ export default function WordPressImportWizard({ open, onOpenChange, onSuccess })
const Step6Results = () => ( const Step6Results = () => (
<div className="space-y-6"> <div className="space-y-6">
<div> <div>
<h3 className="text-lg font-semibold text-[#422268] mb-2">Import Complete</h3> <h3 className="text-lg font-semibold text-primary mb-2">Import Complete</h3>
<p className="text-sm text-[#664fa3]"> <p className="text-sm text-muted-foreground">
Review the import results and download error reports if needed. Review the import results and download error reports if needed.
</p> </p>
</div> </div>
@@ -850,11 +850,11 @@ export default function WordPressImportWizard({ open, onOpenChange, onSuccess })
<div className="p-3 bg-red-100 rounded-full"> <div className="p-3 bg-red-100 rounded-full">
<AlertTriangle className="h-6 w-6 text-red-600" /> <AlertTriangle className="h-6 w-6 text-red-600" />
</div> </div>
<DialogTitle className="text-2xl font-semibold text-[#422268]"> <DialogTitle className="text-2xl font-semibold text-primary">
Confirm Rollback Confirm Rollback
</DialogTitle> </DialogTitle>
</div> </div>
<DialogDescription className="text-[#664fa3]"> <DialogDescription className="text-muted-foreground">
This will permanently delete{' '} This will permanently delete{' '}
<strong>{importResults?.successful_rows} users</strong> that were imported. <strong>{importResults?.successful_rows} users</strong> that were imported.
This action cannot be undone. This action cannot be undone.
@@ -898,10 +898,10 @@ export default function WordPressImportWizard({ open, onOpenChange, onSuccess })
<Dialog open={open} onOpenChange={onOpenChange}> <Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent className="max-w-6xl max-h-[90vh] overflow-y-auto"> <DialogContent className="max-w-6xl max-h-[90vh] overflow-y-auto">
<DialogHeader> <DialogHeader>
<DialogTitle className="text-2xl font-semibold text-[#422268]"> <DialogTitle className="text-2xl font-semibold text-primary">
WordPress Import Wizard WordPress Import Wizard
</DialogTitle> </DialogTitle>
<DialogDescription className="text-[#664fa3]"> <DialogDescription className="text-muted-foreground">
Import WordPress users with interactive status review and full rollback capability Import WordPress users with interactive status review and full rollback capability
</DialogDescription> </DialogDescription>
</DialogHeader> </DialogHeader>
@@ -919,7 +919,7 @@ export default function WordPressImportWizard({ open, onOpenChange, onSuccess })
<div <div
className={` className={`
w-10 h-10 rounded-full flex items-center justify-center w-10 h-10 rounded-full flex items-center justify-center
${isCurrent ? 'bg-[#664fa3] text-white' : ''} ${isCurrent ? 'bg-muted-foreground text-white' : ''}
${isCompleted ? 'bg-green-600 text-white' : ''} ${isCompleted ? 'bg-green-600 text-white' : ''}
${!isCurrent && !isCompleted ? 'bg-gray-200 text-gray-600' : ''} ${!isCurrent && !isCompleted ? 'bg-gray-200 text-gray-600' : ''}
`} `}
@@ -930,7 +930,7 @@ export default function WordPressImportWizard({ open, onOpenChange, onSuccess })
<StepIcon className="h-5 w-5" /> <StepIcon className="h-5 w-5" />
)} )}
</div> </div>
<p className={`text-xs mt-1 ${isCurrent ? 'font-semibold text-[#422268]' : 'text-gray-600'}`}> <p className={`text-xs mt-1 ${isCurrent ? 'font-semibold text-primary' : 'text-gray-600'}`}>
{step.title} {step.title}
</p> </p>
</div> </div>
@@ -962,7 +962,7 @@ export default function WordPressImportWizard({ open, onOpenChange, onSuccess })
<Button <Button
onClick={handleNext} onClick={handleNext}
disabled={!canProceed()} disabled={!canProceed()}
className="bg-[#664fa3] hover:bg-[#422268]" className="bg-muted-foreground hover:bg-primary"
> >
Next Next
<ChevronRight className="h-4 w-4 ml-2" /> <ChevronRight className="h-4 w-4 ml-2" />
@@ -975,7 +975,7 @@ export default function WordPressImportWizard({ open, onOpenChange, onSuccess })
onOpenChange(false); onOpenChange(false);
if (onSuccess) onSuccess(); if (onSuccess) onSuccess();
}} }}
className="bg-[#664fa3] hover:bg-[#422268]" className="bg-muted-foreground hover:bg-primary"
> >
Close Close
</Button> </Button>

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 text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}> <h2 className="text-2xl font-semibold text-primary" 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-[#ddd8eb] focus:border-[#664fa3]" className="h-14 rounded-xl border-2 border-chart-6 focus:border-muted-foreground"
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-[#ddd8eb] focus:border-[#664fa3]" className="h-14 rounded-xl border-2 border-chart-6 focus:border-muted-foreground"
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-[#ddd8eb] focus:border-[#664fa3]" className="h-14 rounded-xl border-2 border-chart-6 focus:border-muted-foreground"
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-[#ddd8eb] focus:border-[#664fa3]" className="h-14 rounded-xl border-2 border-chart-6 focus:border-muted-foreground"
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-[#ddd8eb] focus:border-[#664fa3]" className="h-14 rounded-xl border-2 border-chart-6 focus:border-muted-foreground"
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-[#ddd8eb] focus:border-[#664fa3]" className="h-14 rounded-xl border-2 border-chart-6 focus:border-muted-foreground"
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-[#ddd8eb] focus:border-[#664fa3]" className="h-14 rounded-xl border-2 border-chart-6 focus:border-muted-foreground"
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 text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}> <h2 className="text-2xl font-semibold text-primary" 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 text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}> <h2 className="text-2xl font-semibold text-primary" 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-[#ddd8eb] focus:border-[#664fa3]" className="h-14 rounded-xl border-2 border-chart-6 focus:border-muted-foreground"
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-[#ddd8eb] focus:border-[#664fa3]" className="h-14 rounded-xl border-2 border-chart-6 focus:border-muted-foreground"
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 text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}> <h2 className="text-2xl font-semibold text-primary" style={{ fontFamily: "'Inter', sans-serif" }}>
Newsletter Publication Preferences * Newsletter Publication Preferences *
</h2> </h2>
<p className="text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-muted-foreground" 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 text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}> <h2 className="text-2xl font-semibold text-primary" 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-[#ddd8eb] focus:border-[#664fa3]" className="h-14 rounded-xl border-2 border-chart-6 focus:border-muted-foreground"
data-testid="referral-input" data-testid="referral-input"
/> />
<p className="text-sm text-[#664fa3] mt-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-sm text-muted-foreground 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 text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}> <h2 className="text-2xl font-semibold text-primary" style={{ fontFamily: "'Inter', sans-serif" }}>
Volunteer Interests (Optional) Volunteer Interests (Optional)
</h2> </h2>
<p className="text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-muted-foreground" 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-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-sm text-muted-foreground" 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-[#ddd8eb] focus:border-[#664fa3]" className="rounded-xl border-2 border-chart-6 focus:border-muted-foreground"
/> />
</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 text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}> <h2 className="text-2xl font-semibold text-primary" style={{ fontFamily: "'Inter', sans-serif" }}>
Members Directory Members Directory
</h2> </h2>
<p className="text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-muted-foreground" 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-[#ff9e77] bg-[#ff9e77]/5' ? 'border-accent bg-accent/5'
: 'border-[#ddd8eb] hover:border-[#664fa3]' : 'border-chart-6 hover:border-muted-foreground'
} }
`} `}
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-[#ff9e77]' : 'border-[#ddd8eb]'} ${formData.show_in_directory ? 'border-accent' : 'border-chart-6'}
`}> `}>
{formData.show_in_directory && ( {formData.show_in_directory && (
<div className="w-3 h-3 rounded-full bg-[#ff9e77]" /> <div className="w-3 h-3 rounded-full bg-accent" />
)} )}
</div> </div>
<span className="font-medium text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}> <span className="font-medium text-primary" 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-[#ff9e77] bg-[#ff9e77]/5' ? 'border-accent bg-accent/5'
: 'border-[#ddd8eb] hover:border-[#664fa3]' : 'border-chart-6 hover:border-muted-foreground'
} }
`} `}
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-[#ff9e77]' : 'border-[#ddd8eb]'} ${!formData.show_in_directory ? 'border-accent' : 'border-chart-6'}
`}> `}>
{!formData.show_in_directory && ( {!formData.show_in_directory && (
<div className="w-3 h-3 rounded-full bg-[#ff9e77]" /> <div className="w-3 h-3 rounded-full bg-accent" />
)} )}
</div> </div>
<span className="font-medium text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}> <span className="font-medium text-primary" 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-white rounded-xl border border-[#ddd8eb]"> <div className="space-y-4 mt-6 p-6 bg-background rounded-xl border border-chart-6">
<p className="text-[#664fa3] text-sm" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-muted-foreground 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-[#ddd8eb] focus:border-[#664fa3]" className="h-14 rounded-xl border-2 border-chart-6 focus:border-muted-foreground"
/> />
</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-[#ddd8eb] focus:border-[#664fa3]" className="rounded-xl border-2 border-chart-6 focus:border-muted-foreground"
/> />
</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-[#ddd8eb] focus:border-[#664fa3]" className="h-14 rounded-xl border-2 border-chart-6 focus:border-muted-foreground"
/> />
</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-[#ddd8eb] focus:border-[#664fa3]" className="h-14 rounded-xl border-2 border-chart-6 focus:border-muted-foreground"
/> />
</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-[#ddd8eb] focus:border-[#664fa3]" className="h-14 rounded-xl border-2 border-chart-6 focus:border-muted-foreground"
/> />
</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-[#ddd8eb] focus:border-[#664fa3]" className="h-14 rounded-xl border-2 border-chart-6 focus:border-muted-foreground"
/> />
</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 text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}> <h2 className="text-2xl font-semibold text-primary" style={{ fontFamily: "'Inter', sans-serif" }}>
Account Credentials Account Credentials
</h2> </h2>
<p className="text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-muted-foreground" 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 validated. Please note you can only login after your application is validated.
</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-[#ddd8eb] focus:border-[#664fa3]" className="h-14 rounded-xl border-2 border-chart-6 focus:border-muted-foreground"
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-[#ddd8eb] focus:border-[#664fa3]" className="h-14 rounded-xl border-2 border-chart-6 focus:border-muted-foreground"
data-testid="password-input" data-testid="password-input"
/> />
<p className="text-sm text-[#664fa3] mt-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-sm text-muted-foreground 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-[#ddd8eb] focus:border-[#664fa3]" className="h-14 rounded-xl border-2 border-chart-6 focus:border-muted-foreground"
data-testid="confirm-password-input" data-testid="confirm-password-input"
/> />
{formData.confirmPassword && formData.password !== formData.confirmPassword && ( {formData.confirmPassword && formData.password !== formData.confirmPassword && (
@@ -71,7 +71,7 @@ const RegistrationStep4 = ({ formData, handleInputChange }) => {
</div> </div>
{/* Terms of Service Acceptance */} {/* Terms of Service Acceptance */}
<div className="p-4 bg-[#F8F7FB] rounded-lg border border-[#ddd8eb]"> <div className="p-4 bg-chart-7 rounded-lg border border-chart-6">
<div className="flex items-start gap-3"> <div className="flex items-start gap-3">
<input <input
type="checkbox" type="checkbox"
@@ -79,7 +79,7 @@ const RegistrationStep4 = ({ formData, handleInputChange }) => {
name="accepts_tos" name="accepts_tos"
checked={formData.accepts_tos || false} checked={formData.accepts_tos || false}
onChange={handleInputChange} onChange={handleInputChange}
className="mt-1 w-4 h-4 text-[#664fa3] border-gray-300 rounded focus:ring-[#664fa3]" className="mt-1 w-4 h-4 text-muted-foreground border-gray-300 rounded focus:ring-muted-foreground"
required required
data-testid="tos-checkbox" data-testid="tos-checkbox"
/> />
@@ -89,7 +89,7 @@ const RegistrationStep4 = ({ formData, handleInputChange }) => {
href="/become-a-member/terms-of-service" href="/become-a-member/terms-of-service"
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
className="text-[#664fa3] hover:text-[#422268] font-semibold underline" className="text-muted-foreground hover:text-primary font-semibold underline"
> >
Terms of Service Terms of Service
</a> </a>
@@ -98,7 +98,7 @@ const RegistrationStep4 = ({ formData, handleInputChange }) => {
href="become-a-member/privacy-policy" href="become-a-member/privacy-policy"
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
className="text-[#664fa3] hover:text-[#422268] font-semibold underline" className="text-muted-foreground hover:text-primary font-semibold underline"
> >
Privacy Policy Privacy Policy
</a> </a>

View File

@@ -20,17 +20,17 @@ 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-[#ff9e77] text-white scale-110 shadow-lg' ? 'bg-accent text-white scale-110 shadow-lg'
: currentStep > step.number : currentStep > step.number
? 'bg-[#81B29A] text-white' ? 'bg-[#81B29A] text-white'
: 'bg-[#ddd8eb] text-[#664fa3]' : 'bg-chart-6 text-muted-foreground'
} }
`}> `}>
{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-[#ff9e77]' : 'text-[#664fa3]'} ${currentStep === step.number ? 'text-accent' : 'text-muted-foreground'}
`} style={{ fontFamily: "'Nunito Sans', sans-serif" }}> `} style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{step.title} {step.title}
</span> </span>
@@ -38,7 +38,7 @@ const RegistrationStepIndicator = ({ currentStep, totalSteps = 4 }) => {
{/* Connecting Line */} {/* Connecting Line */}
{index < steps.length - 1 && ( {index < steps.length - 1 && (
<div className="flex-1 h-1 mx-2 relative -top-6 bg-[#ddd8eb]"> <div className="flex-1 h-1 mx-2 relative -top-6 bg-chart-6">
<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-[#664fa3] mt-6 text-lg" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-center text-muted-foreground mt-6 text-lg" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Step <span className="font-semibold text-[#ff9e77]">{currentStep}</span> of {totalSteps} Step <span className="font-semibold text-accent">{currentStep}</span> of {totalSteps}
</p> </p>
</div> </div>
); );

View File

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

View File

@@ -46,6 +46,8 @@ code {
--chart-3: 268 33% 89%; --chart-3: 268 33% 89%;
--chart-4: 280 44% 29%; --chart-4: 280 44% 29%;
--chart-5: 268 35% 47%; --chart-5: 268 35% 47%;
--chart-6: 256 32% 88%;
--chart-7: 255 33% 98%;
--radius: 0.5rem; --radius: 0.5rem;
} }
.dark { .dark {
@@ -61,8 +63,10 @@ code {
--secondary-foreground: 0 0% 98%; --secondary-foreground: 0 0% 98%;
--muted: 0 0% 14.9%; --muted: 0 0% 14.9%;
--muted-foreground: 0 0% 63.9%; --muted-foreground: 0 0% 63.9%;
--accent: 0 0% 14.9%; /* --accent: 0 0% 14.9%;
--accent-foreground: 0 0% 98%; --accent-foreground: 0 0% 98%; */
--accent: 17 100% 73%;
--accent-foreground: 280 47% 27%;
--destructive: 0 62.8% 30.6%; --destructive: 0 62.8% 30.6%;
--destructive-foreground: 0 0% 98%; --destructive-foreground: 0 0% 98%;
--border: 0 0% 14.9%; --border: 0 0% 14.9%;
@@ -73,6 +77,8 @@ code {
--chart-3: 30 80% 55%; --chart-3: 30 80% 55%;
--chart-4: 280 65% 60%; --chart-4: 280 65% 60%;
--chart-5: 340 75% 55%; --chart-5: 340 75% 55%;
--chart-6: 0 0% 14.9%;
--chart-7: 0 0% 14.9%;
} }
} }

View File

@@ -163,9 +163,9 @@ const AcceptInvitation = () => {
const getRoleBadge = (role) => { const getRoleBadge = (role) => {
const config = { const config = {
superadmin: { label: 'Superadmin', className: 'bg-[#664fa3] text-white' }, superadmin: { label: 'Superadmin', className: 'bg-muted-foreground text-white' },
admin: { label: 'Admin', className: 'bg-[#81B29A] text-white' }, admin: { label: 'Admin', className: 'bg-[#81B29A] text-white' },
member: { label: 'Member', className: 'bg-[#DDD8EB] text-[#422268]' } member: { label: 'Member', className: 'bg-chart-6 text-primary' }
}; };
const roleConfig = config[role] || { label: role, className: 'bg-gray-500 text-white' }; const roleConfig = config[role] || { label: role, className: 'bg-gray-500 text-white' };
@@ -180,9 +180,9 @@ const AcceptInvitation = () => {
if (loading) { if (loading) {
return ( return (
<div className="min-h-screen bg-gradient-to-br from-[#F9F8FB] to-white flex items-center justify-center p-4"> <div className="min-h-screen bg-gradient-to-br from-[#F9F8FB] to-white flex items-center justify-center p-4">
<Card className="w-full max-w-md p-12 bg-white rounded-2xl border border-[#ddd8eb] text-center"> <Card className="w-full max-w-md p-12 bg-background rounded-2xl border border-chart-6 text-center">
<Loader2 className="h-12 w-12 text-[#664fa3] mx-auto mb-4 animate-spin" /> <Loader2 className="h-12 w-12 text-muted-foreground mx-auto mb-4 animate-spin" />
<p className="text-lg text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}> <p className="text-lg text-primary" style={{ fontFamily: "'Inter', sans-serif" }}>
Verifying your invitation... Verifying your invitation...
</p> </p>
</Card> </Card>
@@ -193,17 +193,17 @@ const AcceptInvitation = () => {
if (error) { if (error) {
return ( return (
<div className="min-h-screen bg-gradient-to-br from-[#F9F8FB] to-white flex items-center justify-center p-4"> <div className="min-h-screen bg-gradient-to-br from-[#F9F8FB] to-white flex items-center justify-center p-4">
<Card className="w-full max-w-md p-12 bg-white rounded-2xl border border-[#ddd8eb] text-center"> <Card className="w-full max-w-md p-12 bg-background rounded-2xl border border-chart-6 text-center">
<XCircle className="h-16 w-16 text-red-500 mx-auto mb-6" /> <XCircle className="h-16 w-16 text-red-500 mx-auto mb-6" />
<h1 className="text-2xl font-semibold text-[#422268] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}> <h1 className="text-2xl font-semibold text-primary mb-4" style={{ fontFamily: "'Inter', sans-serif" }}>
Invalid Invitation Invalid Invitation
</h1> </h1>
<p className="text-[#664fa3] mb-8" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-muted-foreground mb-8" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{error} {error}
</p> </p>
<Button <Button
onClick={() => navigate('/login')} onClick={() => navigate('/login')}
className="rounded-xl bg-[#664fa3] hover:bg-[#422268] text-white" className="rounded-xl bg-muted-foreground hover:bg-primary text-white"
> >
Go to Login Go to Login
</Button> </Button>
@@ -217,7 +217,7 @@ const AcceptInvitation = () => {
return ( return (
<div className="min-h-screen bg-gradient-to-br from-[#F9F8FB] to-white flex items-center justify-center p-4"> <div className="min-h-screen bg-gradient-to-br from-[#F9F8FB] to-white flex items-center justify-center p-4">
<Card className="w-full max-w-2xl p-12 bg-white rounded-2xl border border-[#ddd8eb] text-center"> <Card className="w-full max-w-2xl p-12 bg-background rounded-2xl border border-chart-6 text-center">
{/* Success Animation */} {/* Success Animation */}
<div className="mb-8"> <div className="mb-8">
<div className="h-24 w-24 mx-auto rounded-full bg-gradient-to-br from-[#81B29A] to-[#6DA085] flex items-center justify-center animate-bounce"> <div className="h-24 w-24 mx-auto rounded-full bg-gradient-to-br from-[#81B29A] to-[#6DA085] flex items-center justify-center animate-bounce">
@@ -226,40 +226,40 @@ const AcceptInvitation = () => {
</div> </div>
{/* Success Message */} {/* Success Message */}
<h1 className="text-4xl font-semibold text-[#422268] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}> <h1 className="text-4xl font-semibold text-primary mb-4" style={{ fontFamily: "'Inter', sans-serif" }}>
Welcome to LOAF! 🎉 Welcome to LOAF! 🎉
</h1> </h1>
<p className="text-xl text-[#664fa3] mb-8" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-xl text-muted-foreground mb-8" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Your account has been created successfully. Your account has been created successfully.
</p> </p>
{/* User Info Card */} {/* User Info Card */}
<div className="mb-8 p-6 bg-gradient-to-r from-[#DDD8EB] to-[#F9F8FB] rounded-xl"> <div className="mb-8 p-6 bg-gradient-to-r from-chart-6 to-[#F9F8FB] rounded-xl">
<div className="grid md:grid-cols-2 gap-4 text-left"> <div className="grid md:grid-cols-2 gap-4 text-left">
<div> <div>
<p className="text-sm text-[#664fa3] mb-1" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-sm text-muted-foreground mb-1" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Name Name
</p> </p>
<p className="font-semibold text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}> <p className="font-semibold text-primary" style={{ fontFamily: "'Inter', sans-serif" }}>
{successUser?.first_name} {successUser?.last_name} {successUser?.first_name} {successUser?.last_name}
</p> </p>
</div> </div>
<div> <div>
<p className="text-sm text-[#664fa3] mb-1" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-sm text-muted-foreground mb-1" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Email Email
</p> </p>
<p className="font-semibold text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}> <p className="font-semibold text-primary" style={{ fontFamily: "'Inter', sans-serif" }}>
{successUser?.email} {successUser?.email}
</p> </p>
</div> </div>
<div> <div>
<p className="text-sm text-[#664fa3] mb-1" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-sm text-muted-foreground mb-1" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Role Role
</p> </p>
<div>{getRoleBadge(successUser?.role)}</div> <div>{getRoleBadge(successUser?.role)}</div>
</div> </div>
<div> <div>
<p className="text-sm text-[#664fa3] mb-1" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-sm text-muted-foreground mb-1" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Status Status
</p> </p>
<Badge className="bg-[#81B29A] text-white px-4 py-2 rounded-full text-sm"> <Badge className="bg-[#81B29A] text-white px-4 py-2 rounded-full text-sm">
@@ -291,45 +291,45 @@ const AcceptInvitation = () => {
return ( return (
<div className="min-h-screen bg-gradient-to-br from-[#F9F8FB] to-white flex items-center justify-center p-4"> <div className="min-h-screen bg-gradient-to-br from-[#F9F8FB] to-white flex items-center justify-center p-4">
<Card className="w-full max-w-3xl p-8 md:p-12 bg-white rounded-2xl border border-[#ddd8eb]"> <Card className="w-full max-w-3xl p-8 md:p-12 bg-background rounded-2xl border border-chart-6">
{/* Header */} {/* Header */}
<div className="text-center mb-8"> <div className="text-center mb-8">
<div className="flex justify-center mb-4"> <div className="flex justify-center mb-4">
<div className="h-16 w-16 rounded-full bg-gradient-to-br from-[#664fa3] to-[#422268] flex items-center justify-center"> <div className="h-16 w-16 rounded-full bg-gradient-to-br from-muted-foreground to-primary flex items-center justify-center">
<Mail className="h-8 w-8 text-white" /> <Mail className="h-8 w-8 text-white" />
</div> </div>
</div> </div>
<h1 className="text-3xl md:text-4xl font-semibold text-[#422268] mb-3" style={{ fontFamily: "'Inter', sans-serif" }}> <h1 className="text-3xl md:text-4xl font-semibold text-primary mb-3" style={{ fontFamily: "'Inter', sans-serif" }}>
Welcome to LOAF! Welcome to LOAF!
</h1> </h1>
<p className="text-lg text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-lg text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Complete your profile to accept the invitation Complete your profile to accept the invitation
</p> </p>
</div> </div>
{/* Invitation Details */} {/* Invitation Details */}
<div className="mb-8 p-6 bg-gradient-to-r from-[#DDD8EB] to-[#F9F8FB] rounded-xl"> <div className="mb-8 p-6 bg-gradient-to-r from-chart-6 to-[#F9F8FB] rounded-xl">
<div className="grid md:grid-cols-2 gap-4 text-sm"> <div className="grid md:grid-cols-2 gap-4 text-sm">
<div> <div>
<p className="text-[#664fa3] mb-1" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-muted-foreground mb-1" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Email Address Email Address
</p> </p>
<p className="font-semibold text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}> <p className="font-semibold text-primary" style={{ fontFamily: "'Inter', sans-serif" }}>
{invitation?.email} {invitation?.email}
</p> </p>
</div> </div>
<div> <div>
<p className="text-[#664fa3] mb-1" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-muted-foreground mb-1" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Role Role
</p> </p>
<div>{getRoleBadge(invitation?.role)}</div> <div>{getRoleBadge(invitation?.role)}</div>
</div> </div>
<div className="md:col-span-2"> <div className="md:col-span-2">
<p className="text-[#664fa3] mb-1 flex items-center gap-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-muted-foreground mb-1 flex items-center gap-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<Calendar className="h-4 w-4" /> <Calendar className="h-4 w-4" />
Invitation Expires Invitation Expires
</p> </p>
<p className="font-semibold text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}> <p className="font-semibold text-primary" style={{ fontFamily: "'Inter', sans-serif" }}>
{invitation?.expires_at ? new Date(invitation.expires_at).toLocaleString() : 'N/A'} {invitation?.expires_at ? new Date(invitation.expires_at).toLocaleString() : 'N/A'}
</p> </p>
</div> </div>
@@ -342,7 +342,7 @@ const AcceptInvitation = () => {
{/* Password Fields */} {/* Password Fields */}
<div className="grid md:grid-cols-2 gap-4"> <div className="grid md:grid-cols-2 gap-4">
<div className="grid gap-2"> <div className="grid gap-2">
<Label htmlFor="password" className="text-[#422268]"> <Label htmlFor="password" className="text-primary">
Password <span className="text-red-500">*</span> Password <span className="text-red-500">*</span>
</Label> </Label>
<Input <Input
@@ -350,7 +350,7 @@ const AcceptInvitation = () => {
type="password" type="password"
value={formData.password} value={formData.password}
onChange={(e) => handleChange('password', e.target.value)} onChange={(e) => handleChange('password', e.target.value)}
className="rounded-xl border-2 border-[#ddd8eb] focus:border-[#664fa3]" className="rounded-xl border-2 border-chart-6 focus:border-muted-foreground"
placeholder="Minimum 8 characters" placeholder="Minimum 8 characters"
/> />
{formErrors.password && ( {formErrors.password && (
@@ -359,7 +359,7 @@ const AcceptInvitation = () => {
</div> </div>
<div className="grid gap-2"> <div className="grid gap-2">
<Label htmlFor="confirmPassword" className="text-[#422268]"> <Label htmlFor="confirmPassword" className="text-primary">
Confirm Password <span className="text-red-500">*</span> Confirm Password <span className="text-red-500">*</span>
</Label> </Label>
<Input <Input
@@ -367,7 +367,7 @@ const AcceptInvitation = () => {
type="password" type="password"
value={formData.confirmPassword} value={formData.confirmPassword}
onChange={(e) => handleChange('confirmPassword', e.target.value)} onChange={(e) => handleChange('confirmPassword', e.target.value)}
className="rounded-xl border-2 border-[#ddd8eb] focus:border-[#664fa3]" className="rounded-xl border-2 border-chart-6 focus:border-muted-foreground"
placeholder="Re-enter password" placeholder="Re-enter password"
/> />
{formErrors.confirmPassword && ( {formErrors.confirmPassword && (
@@ -379,14 +379,14 @@ const AcceptInvitation = () => {
{/* Name Fields */} {/* Name Fields */}
<div className="grid md:grid-cols-2 gap-4"> <div className="grid md:grid-cols-2 gap-4">
<div className="grid gap-2"> <div className="grid gap-2">
<Label htmlFor="first_name" className="text-[#422268]"> <Label htmlFor="first_name" className="text-primary">
First Name <span className="text-red-500">*</span> First Name <span className="text-red-500">*</span>
</Label> </Label>
<Input <Input
id="first_name" id="first_name"
value={formData.first_name} value={formData.first_name}
onChange={(e) => handleChange('first_name', e.target.value)} onChange={(e) => handleChange('first_name', e.target.value)}
className="rounded-xl border-2 border-[#ddd8eb] focus:border-[#664fa3]" className="rounded-xl border-2 border-chart-6 focus:border-muted-foreground"
placeholder="John" placeholder="John"
/> />
{formErrors.first_name && ( {formErrors.first_name && (
@@ -395,14 +395,14 @@ const AcceptInvitation = () => {
</div> </div>
<div className="grid gap-2"> <div className="grid gap-2">
<Label htmlFor="last_name" className="text-[#422268]"> <Label htmlFor="last_name" className="text-primary">
Last Name <span className="text-red-500">*</span> Last Name <span className="text-red-500">*</span>
</Label> </Label>
<Input <Input
id="last_name" id="last_name"
value={formData.last_name} value={formData.last_name}
onChange={(e) => handleChange('last_name', e.target.value)} onChange={(e) => handleChange('last_name', e.target.value)}
className="rounded-xl border-2 border-[#ddd8eb] focus:border-[#664fa3]" className="rounded-xl border-2 border-chart-6 focus:border-muted-foreground"
placeholder="Doe" placeholder="Doe"
/> />
{formErrors.last_name && ( {formErrors.last_name && (
@@ -413,7 +413,7 @@ const AcceptInvitation = () => {
{/* Phone */} {/* Phone */}
<div className="grid gap-2"> <div className="grid gap-2">
<Label htmlFor="phone" className="text-[#422268]"> <Label htmlFor="phone" className="text-primary">
Phone <span className="text-red-500">*</span> Phone <span className="text-red-500">*</span>
</Label> </Label>
<Input <Input
@@ -421,7 +421,7 @@ const AcceptInvitation = () => {
type="tel" type="tel"
value={formData.phone} value={formData.phone}
onChange={(e) => handleChange('phone', e.target.value)} onChange={(e) => handleChange('phone', e.target.value)}
className="rounded-xl border-2 border-[#ddd8eb] focus:border-[#664fa3]" className="rounded-xl border-2 border-chart-6 focus:border-muted-foreground"
placeholder="(555) 123-4567" placeholder="(555) 123-4567"
/> />
{formErrors.phone && ( {formErrors.phone && (
@@ -432,20 +432,20 @@ const AcceptInvitation = () => {
{/* Optional Fields Section */} {/* Optional Fields Section */}
{invitation?.role === 'member' && ( {invitation?.role === 'member' && (
<> <>
<div className="border-t border-[#ddd8eb] pt-6 mt-2"> <div className="border-t border-chart-6 pt-6 mt-2">
<h3 className="text-lg font-semibold text-[#422268] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}> <h3 className="text-lg font-semibold text-primary mb-4" style={{ fontFamily: "'Inter', sans-serif" }}>
Additional Information (Optional) Additional Information (Optional)
</h3> </h3>
</div> </div>
{/* Address */} {/* Address */}
<div className="grid gap-2"> <div className="grid gap-2">
<Label htmlFor="address" className="text-[#422268]">Address</Label> <Label htmlFor="address" className="text-primary">Address</Label>
<Input <Input
id="address" id="address"
value={formData.address} value={formData.address}
onChange={(e) => handleChange('address', e.target.value)} onChange={(e) => handleChange('address', e.target.value)}
className="rounded-xl border-2 border-[#ddd8eb] focus:border-[#664fa3]" className="rounded-xl border-2 border-chart-6 focus:border-muted-foreground"
placeholder="123 Main St" placeholder="123 Main St"
/> />
</div> </div>
@@ -453,35 +453,35 @@ const AcceptInvitation = () => {
{/* City, State, Zipcode */} {/* City, State, Zipcode */}
<div className="grid md:grid-cols-3 gap-4"> <div className="grid md:grid-cols-3 gap-4">
<div className="grid gap-2"> <div className="grid gap-2">
<Label htmlFor="city" className="text-[#422268]">City</Label> <Label htmlFor="city" className="text-primary">City</Label>
<Input <Input
id="city" id="city"
value={formData.city} value={formData.city}
onChange={(e) => handleChange('city', e.target.value)} onChange={(e) => handleChange('city', e.target.value)}
className="rounded-xl border-2 border-[#ddd8eb] focus:border-[#664fa3]" className="rounded-xl border-2 border-chart-6 focus:border-muted-foreground"
placeholder="San Francisco" placeholder="San Francisco"
/> />
</div> </div>
<div className="grid gap-2"> <div className="grid gap-2">
<Label htmlFor="state" className="text-[#422268]">State</Label> <Label htmlFor="state" className="text-primary">State</Label>
<Input <Input
id="state" id="state"
value={formData.state} value={formData.state}
onChange={(e) => handleChange('state', e.target.value)} onChange={(e) => handleChange('state', e.target.value)}
className="rounded-xl border-2 border-[#ddd8eb] focus:border-[#664fa3]" className="rounded-xl border-2 border-chart-6 focus:border-muted-foreground"
placeholder="CA" placeholder="CA"
maxLength={2} maxLength={2}
/> />
</div> </div>
<div className="grid gap-2"> <div className="grid gap-2">
<Label htmlFor="zipcode" className="text-[#422268]">Zipcode</Label> <Label htmlFor="zipcode" className="text-primary">Zipcode</Label>
<Input <Input
id="zipcode" id="zipcode"
value={formData.zipcode} value={formData.zipcode}
onChange={(e) => handleChange('zipcode', e.target.value)} onChange={(e) => handleChange('zipcode', e.target.value)}
className="rounded-xl border-2 border-[#ddd8eb] focus:border-[#664fa3]" className="rounded-xl border-2 border-chart-6 focus:border-muted-foreground"
placeholder="94102" placeholder="94102"
/> />
</div> </div>
@@ -489,13 +489,13 @@ const AcceptInvitation = () => {
{/* Date of Birth */} {/* Date of Birth */}
<div className="grid gap-2"> <div className="grid gap-2">
<Label htmlFor="date_of_birth" className="text-[#422268]">Date of Birth</Label> <Label htmlFor="date_of_birth" className="text-primary">Date of Birth</Label>
<Input <Input
id="date_of_birth" id="date_of_birth"
type="date" type="date"
value={formData.date_of_birth} value={formData.date_of_birth}
onChange={(e) => handleChange('date_of_birth', e.target.value)} onChange={(e) => handleChange('date_of_birth', e.target.value)}
className="rounded-xl border-2 border-[#ddd8eb] focus:border-[#664fa3]" className="rounded-xl border-2 border-chart-6 focus:border-muted-foreground"
/> />
</div> </div>
</> </>
@@ -526,11 +526,11 @@ const AcceptInvitation = () => {
{/* Footer Note */} {/* Footer Note */}
<div className="mt-6 text-center"> <div className="mt-6 text-center">
<p className="text-sm text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-sm text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Already have an account?{' '} Already have an account?{' '}
<button <button
onClick={() => navigate('/login')} onClick={() => navigate('/login')}
className="text-[#664fa3] hover:text-[#422268] font-semibold underline" className="text-muted-foreground hover:text-primary font-semibold underline"
> >
Sign in instead Sign in instead
</button> </button>

View File

@@ -71,7 +71,7 @@ const BecomeMember = () => {
</div> </div>
{/* Membership Process Section */} {/* Membership Process Section */}
<div className="relative bg-gray-50 py-32 bg-gradient-to-bl from-[#F9FAFB] to-[#DDD8EB] "> <div className="relative bg-gray-50 py-32 bg-gradient-to-bl from-[#F9FAFB] to-chart-6 ">
{/* Decorative shooting star element */} {/* Decorative shooting star element */}
<div className="hidden lg:block absolute left-0 top-64 w-[195px] h-[1130px] pointer-events-none opacity-50"> <div className="hidden lg:block absolute left-0 top-64 w-[195px] h-[1130px] pointer-events-none opacity-50">
<img <img
@@ -106,7 +106,7 @@ const BecomeMember = () => {
className="w-full h-full object-contain" className="w-full h-full object-contain"
/> />
</div> </div>
<div className="flex-1 bg-white rounded-[25px] px-4 py-6 sm:px-6 sm:py-7 md:px-8 md:py-8"> <div className="flex-1 bg-background rounded-[25px] px-4 py-6 sm:px-6 sm:py-7 md:px-8 md:py-8">
<h3 <h3
className="text-xl sm:text-2xl md:text-[32px] font-semibold text-[#48286e] mb-3 sm:mb-4 md:mb-5 leading-[1.49]" className="text-xl sm:text-2xl md:text-[32px] font-semibold text-[#48286e] mb-3 sm:mb-4 md:mb-5 leading-[1.49]"
style={{ fontFamily: "'Poppins', sans-serif" }} style={{ fontFamily: "'Poppins', sans-serif" }}
@@ -136,7 +136,7 @@ const BecomeMember = () => {
className="w-full h-full object-contain" className="w-full h-full object-contain"
/> />
</div> </div>
<div className="flex-1 bg-white rounded-[25px] px-4 py-6 sm:px-6 sm:py-7 md:px-8 md:py-8"> <div className="flex-1 bg-background rounded-[25px] px-4 py-6 sm:px-6 sm:py-7 md:px-8 md:py-8">
<h3 <h3
className="text-xl sm:text-2xl md:text-[32px] font-semibold text-[#48286e] mb-3 sm:mb-4 md:mb-5 leading-[1.49]" className="text-xl sm:text-2xl md:text-[32px] font-semibold text-[#48286e] mb-3 sm:mb-4 md:mb-5 leading-[1.49]"
style={{ fontFamily: "'Poppins', sans-serif" }} style={{ fontFamily: "'Poppins', sans-serif" }}
@@ -165,7 +165,7 @@ const BecomeMember = () => {
className="w-full h-full object-contain" className="w-full h-full object-contain"
/> />
</div> </div>
<div className="flex-1 bg-white rounded-[25px] px-4 py-6 sm:px-6 sm:py-7 md:px-8 md:py-8"> <div className="flex-1 bg-background rounded-[25px] px-4 py-6 sm:px-6 sm:py-7 md:px-8 md:py-8">
<h3 <h3
className="text-xl sm:text-2xl md:text-[32px] font-semibold text-[#48286e] mb-3 sm:mb-4 md:mb-5 leading-[1.49]" className="text-xl sm:text-2xl md:text-[32px] font-semibold text-[#48286e] mb-3 sm:mb-4 md:mb-5 leading-[1.49]"
style={{ fontFamily: "'Poppins', sans-serif" }} style={{ fontFamily: "'Poppins', sans-serif" }}
@@ -195,7 +195,7 @@ const BecomeMember = () => {
className="w-full h-full object-contain" className="w-full h-full object-contain"
/> />
</div> </div>
<div className="flex-1 bg-gradient-to-r from-[#48286e] to-[#664fa3] rounded-[25px] px-4 py-6 sm:px-6 sm:py-7 md:px-8 md:py-8"> <div className="flex-1 bg-gradient-to-r from-[#48286e] to-muted-foreground rounded-[25px] px-4 py-6 sm:px-6 sm:py-7 md:px-8 md:py-8">
<h3 <h3
className="text-xl sm:text-2xl md:text-[32px] font-semibold text-white mb-3 sm:mb-4 md:mb-5 leading-[1.49]" className="text-xl sm:text-2xl md:text-[32px] font-semibold text-white mb-3 sm:mb-4 md:mb-5 leading-[1.49]"
style={{ fontFamily: "'Poppins', sans-serif" }} style={{ fontFamily: "'Poppins', sans-serif" }}
@@ -223,7 +223,7 @@ const BecomeMember = () => {
</h2> </h2>
<Link to="/register"> <Link to="/register">
<Button <Button
className="bg-[#664fa3] text-white hover:bg-[#48286e] rounded-[35px] px-6 py-3 sm:px-12 sm:py-5 md:px-16 md:py-6 text-[19px] sm:text-lg font-medium tracking-[-0.09px] leading-5 h-auto" className="bg-muted-foreground text-white hover:bg-[#48286e] rounded-[35px] px-6 py-3 sm:px-12 sm:py-5 md:px-16 md:py-6 text-[19px] sm:text-lg font-medium tracking-[-0.09px] leading-5 h-auto"
style={{ fontFamily: "'Inter', sans-serif" }} style={{ fontFamily: "'Inter', sans-serif" }}
> >
Register Now! Register Now!

View File

@@ -22,7 +22,7 @@ const BoardOfDirectors = () => {
const DirectorCards = ({ title, members }) => { const DirectorCards = ({ title, members }) => {
return ( return (
<section className=" w-full"> <section className=" w-full">
<div className="mx-auto bg-white rounded-3xl p-10 shadow-lg h-full"> <div className="mx-auto bg-background rounded-3xl p-10 shadow-lg h-full">
{title && ( {title && (
<h2 <h2
className="text-2xl sm:text-4xl font-bold text-[#48286e] text-center mb-8" className="text-2xl sm:text-4xl font-bold text-[#48286e] text-center mb-8"
@@ -66,10 +66,10 @@ const BoardOfDirectors = () => {
}; };
return ( return (
<div className="min-h-screen bg-white"> <div className="min-h-screen bg-background">
<PublicNavbar /> <PublicNavbar />
<main className="bg-gradient-to-b from-[#f9fafb] to-[#ddd8eb] pt-8 sm:pt-10 md:pt-12"> <main className="bg-gradient-to-b from-[#f9fafb] to-chart-6 pt-8 sm:pt-10 md:pt-12">
{/* Hero Section */} {/* Hero Section */}
<section className=" pt-16 pb-4 px-4 sm:px-6 md:px-8 lg:px-12 xl:px-20"> <section className=" pt-16 pb-4 px-4 sm:px-6 md:px-8 lg:px-12 xl:px-20">
<div className="max-w-5xl mx-auto text-center px-8"> <div className="max-w-5xl mx-auto text-center px-8">
@@ -81,7 +81,7 @@ const BoardOfDirectors = () => {
</section> </section>
{/* Contact Info */} {/* Contact Info */}
<section className="flex justify-center mt-4 mb-8"> <section className="flex justify-center mt-4 mb-8">
<div className=" w-full text-center px-8 justify-center bg-gradient-to-r from-[#664fa3] to-[#48286e] max-w-5xl mx-6 flex lg:mx-12 py-5 rounded-3xl sm:px-6 md:px-8 lg:px-12 xl:px-20"> <div className=" w-full text-center px-8 justify-center bg-gradient-to-r from-muted-foreground to-[#48286e] max-w-5xl mx-6 flex lg:mx-12 py-5 rounded-3xl sm:px-6 md:px-8 lg:px-12 xl:px-20">
<p className="text-white text-xl font-bold " style={{ fontFamily: "'Poppins', sans-serif" }}> <p className="text-white text-xl font-bold " style={{ fontFamily: "'Poppins', sans-serif" }}>
For any questions or inquiries please email us at{' '} For any questions or inquiries please email us at{' '}
@@ -102,7 +102,7 @@ const BoardOfDirectors = () => {
</section> </section>
{/* Join the Board Section */} {/* Join the Board Section */}
<section className="py-12 bg-white mt-12 "> <section className="py-12 bg-background mt-12 ">
{/* content containter */} {/* content containter */}
<div className="w-full mx-auto flex flex-col px-4 sm:px-6 md:px-8 lg:px-12 xl:px-20"> <div className="w-full mx-auto flex flex-col px-4 sm:px-6 md:px-8 lg:px-12 xl:px-20">
<h2 className="text-xl mx-auto sm:text-2xl md:text-4xl font-bold text-[#48286e] text-center mb-8" style={{ fontFamily: "'Poppins', sans-serif" }}> <h2 className="text-xl mx-auto sm:text-2xl md:text-4xl font-bold text-[#48286e] text-center mb-8" style={{ fontFamily: "'Poppins', sans-serif" }}>
@@ -117,7 +117,7 @@ const BoardOfDirectors = () => {
<li> <li>
Nominations are due by November 1. Nomination Form:{' '} Nominations are due by November 1. Nomination Form:{' '}
<a href="https://docs.google.com/forms/d/e/1FAIpQLSfNomination" target="_blank" rel="noopener noreferrer" <a href="https://docs.google.com/forms/d/e/1FAIpQLSfNomination" target="_blank" rel="noopener noreferrer"
className="text-[#664fa3] underline hover:text-[#48286e] transition-colors"> className="text-muted-foreground underline hover:text-[#48286e] transition-colors">
Click Here Click Here
</a> </a>
</li> </li>

View File

@@ -83,19 +83,19 @@ const ChangePasswordRequired = () => {
} }
return ( return (
<div className="min-h-screen bg-white"> <div className="min-h-screen bg-background">
<Navbar /> <Navbar />
<div className="max-w-md mx-auto px-6 py-12"> <div className="max-w-md mx-auto px-6 py-12">
<Card className="p-8 md:p-12 bg-white rounded-2xl border border-[#ddd8eb] shadow-lg"> <Card className="p-8 md:p-12 bg-background rounded-2xl border border-chart-6 shadow-lg">
<div className="mb-8 text-center"> <div className="mb-8 text-center">
<div className="inline-flex items-center justify-center w-16 h-16 rounded-full bg-[#FFEBEE] mb-4"> <div className="inline-flex items-center justify-center w-16 h-16 rounded-full bg-[#FFEBEE] mb-4">
<AlertTriangle className="h-8 w-8 text-orange-500" /> <AlertTriangle className="h-8 w-8 text-orange-500" />
</div> </div>
<h1 className="text-4xl md:text-5xl font-semibold text-[#422268] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}> <h1 className="text-4xl md:text-5xl font-semibold text-primary mb-4" style={{ fontFamily: "'Inter', sans-serif" }}>
Password Change Required Password Change Required
</h1> </h1>
<p className="text-lg text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-lg text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Your password was reset by an administrator. Please create a new password to continue. Your password was reset by an administrator. Please create a new password to continue.
</p> </p>
</div> </div>
@@ -111,7 +111,7 @@ const ChangePasswordRequired = () => {
value={formData.currentPassword} value={formData.currentPassword}
onChange={handleInputChange} onChange={handleInputChange}
placeholder="Enter temporary password" placeholder="Enter temporary password"
className="h-14 rounded-xl border-2 border-[#ddd8eb] focus:border-[#664fa3]" className="h-14 rounded-xl border-2 border-chart-6 focus:border-muted-foreground"
/> />
</div> </div>
@@ -125,7 +125,7 @@ const ChangePasswordRequired = () => {
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-14 rounded-xl border-2 border-[#ddd8eb] focus:border-[#664fa3]" className="h-14 rounded-xl border-2 border-chart-6 focus:border-muted-foreground"
/> />
</div> </div>
@@ -139,15 +139,15 @@ const ChangePasswordRequired = () => {
value={formData.confirmPassword} value={formData.confirmPassword}
onChange={handleInputChange} onChange={handleInputChange}
placeholder="Re-enter new password" placeholder="Re-enter new password"
className="h-14 rounded-xl border-2 border-[#ddd8eb] focus:border-[#664fa3]" className="h-14 rounded-xl border-2 border-chart-6 focus:border-muted-foreground"
/> />
</div> </div>
<div className="bg-[#f1eef9] border-l-4 border-[#664fa3] p-4 rounded-lg"> <div className="bg-muted border-l-4 border-muted-foreground p-4 rounded-lg">
<div className="flex items-start"> <div className="flex items-start">
<Lock className="h-5 w-5 text-[#664fa3] mr-2 mt-0.5 flex-shrink-0" /> <Lock className="h-5 w-5 text-muted-foreground mr-2 mt-0.5 flex-shrink-0" />
<div className="text-sm text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <div className="text-sm text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p className="font-medium text-[#422268] mb-1">Password Requirements:</p> <p className="font-medium text-primary mb-1">Password Requirements:</p>
<ul className="list-disc list-inside space-y-1"> <ul className="list-disc list-inside space-y-1">
<li>At least 6 characters long</li> <li>At least 6 characters long</li>
<li>Both passwords must match</li> <li>Both passwords must match</li>
@@ -159,17 +159,17 @@ const ChangePasswordRequired = () => {
<Button <Button
type="submit" type="submit"
disabled={loading} disabled={loading}
className="w-full bg-[#DDD8EB] text-[#422268] hover:bg-white rounded-full py-6 text-lg font-medium shadow-lg hover:scale-105 transition-transform disabled:opacity-50" className="w-full bg-chart-6 text-primary hover:bg-background rounded-full py-6 text-lg font-medium shadow-lg hover:scale-105 transition-transform disabled:opacity-50"
> >
{loading ? 'Changing Password...' : 'Change Password'} {loading ? 'Changing Password...' : 'Change Password'}
<ArrowRight className="ml-2 h-5 w-5" /> <ArrowRight className="ml-2 h-5 w-5" />
</Button> </Button>
<div className="text-center pt-4 border-t border-[#ddd8eb]"> <div className="text-center pt-4 border-t border-chart-6">
<button <button
type="button" type="button"
onClick={handleLogout} onClick={handleLogout}
className="text-[#664fa3] hover:text-[#ff9e77] text-sm underline" className="text-muted-foreground hover:text-accent text-sm underline"
> >
Logout instead Logout instead
</button> </button>

View File

@@ -96,14 +96,14 @@ const ContactUs = () => {
}; };
return ( return (
<div className="min-h-screen bg-white"> <div className="min-h-screen bg-background">
<PublicNavbar /> <PublicNavbar />
<main className="bg-gradient-to-b from-[#e8e0f5] to-[#f1eef9] px-6 py-16"> <main className="bg-gradient-to-b from-[#e8e0f5] to-muted px-6 py-16">
<div className="max-w-7xl mx-auto"> <div className="max-w-7xl mx-auto">
<div className="grid grid-cols-1 md:grid-cols-2 gap-6 md:gap-8"> <div className="grid grid-cols-1 md:grid-cols-2 gap-6 md:gap-8">
{/* Contact Form */} {/* Contact Form */}
<Card className="p-8 bg-white rounded-2xl"> <Card className="p-8 bg-background rounded-2xl">
<h1 className="text-2xl sm:text-[28px] leading-5 font-bold text-[#48286e] mb-12" style={{ fontFamily: "'Poppins', sans-serif" }}> <h1 className="text-2xl sm:text-[28px] leading-5 font-bold text-[#48286e] mb-12" style={{ fontFamily: "'Poppins', sans-serif" }}>
Contact Form Contact Form
</h1> </h1>
@@ -120,7 +120,7 @@ const ContactUs = () => {
name="firstName" name="firstName"
value={formData.firstName} value={formData.firstName}
onChange={handleChange} onChange={handleChange}
className="border-2 border-[#ddd8eb] bg-[#eaedf4] focus:border-[#664fa3] rounded-full h-12 px-4" className="border-2 border-chart-6 bg-[#eaedf4] focus:border-muted-foreground rounded-full h-12 px-4"
style={{ fontFamily: "'Nunito Sans', sans-serif" }} style={{ fontFamily: "'Nunito Sans', sans-serif" }}
required required
/> />
@@ -134,7 +134,7 @@ const ContactUs = () => {
name="lastName" name="lastName"
value={formData.lastName} value={formData.lastName}
onChange={handleChange} onChange={handleChange}
className="border-2 bg-[#eaedf4] border-[#ddd8eb] focus:border-[#664fa3] rounded-full h-12 px-4" className="border-2 bg-[#eaedf4] border-chart-6 focus:border-muted-foreground rounded-full h-12 px-4"
style={{ fontFamily: "'Nunito Sans', sans-serif" }} style={{ fontFamily: "'Nunito Sans', sans-serif" }}
required required
/> />
@@ -152,7 +152,7 @@ const ContactUs = () => {
type="email" type="email"
value={formData.email} value={formData.email}
onChange={handleChange} onChange={handleChange}
className="border-2 bg-[#eaedf4] border-[#ddd8eb] focus:border-[#664fa3] rounded-full h-12 px-4" className="border-2 bg-[#eaedf4] border-chart-6 focus:border-muted-foreground rounded-full h-12 px-4"
style={{ fontFamily: "'Nunito Sans', sans-serif" }} style={{ fontFamily: "'Nunito Sans', sans-serif" }}
required required
/> />
@@ -168,7 +168,7 @@ const ContactUs = () => {
name="subject" name="subject"
value={formData.subject} value={formData.subject}
onChange={handleChange} onChange={handleChange}
className="border-2 bg-[#eaedf4] border-[#ddd8eb] focus:border-[#664fa3] rounded-full h-12 px-4" className="border-2 bg-[#eaedf4] border-chart-6 focus:border-muted-foreground rounded-full h-12 px-4"
style={{ fontFamily: "'Nunito Sans', sans-serif" }} style={{ fontFamily: "'Nunito Sans', sans-serif" }}
required required
/> />
@@ -184,7 +184,7 @@ const ContactUs = () => {
name="message" name="message"
value={formData.message} value={formData.message}
onChange={handleChange} onChange={handleChange}
className="border-2 bg-[#eaedf4] border-[#ddd8eb] focus:border-[#664fa3] rounded-2xl min-h-[150px] px-4 py-3 resize-none" className="border-2 bg-[#eaedf4] border-chart-6 focus:border-muted-foreground rounded-2xl min-h-[150px] px-4 py-3 resize-none"
style={{ fontFamily: "'Nunito Sans', sans-serif" }} style={{ fontFamily: "'Nunito Sans', sans-serif" }}
required required
/> />
@@ -196,7 +196,7 @@ const ContactUs = () => {
id="consent" id="consent"
checked={formData.consent} checked={formData.consent}
onCheckedChange={handleConsentChange} onCheckedChange={handleConsentChange}
className="mt-1 border-2 border-[#ddd8eb] data-[state=checked]:bg-[#664fa3] data-[state=checked]:border-[#664fa3]" className="mt-1 border-2 border-chart-6 data-[state=checked]:bg-muted-foreground data-[state=checked]:border-muted-foreground"
/> />
<Label htmlFor="consent" className="text-[#48286e] text-sm font-normal cursor-pointer" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <Label htmlFor="consent" className="text-[#48286e] text-sm font-normal cursor-pointer" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
I consent to LOAF storing my submitted information so they can respond to my inquiry <span className="text-red-500">*</span> I consent to LOAF storing my submitted information so they can respond to my inquiry <span className="text-red-500">*</span>
@@ -207,7 +207,7 @@ const ContactUs = () => {
<Button <Button
type="submit" type="submit"
disabled={loading} disabled={loading}
className="w-full bg-[#664fa3] hover:bg-[#48286e] text-white rounded-full py-6 text-lg font-semibold disabled:opacity-50" className="w-full bg-muted-foreground hover:bg-[#48286e] text-white rounded-full py-6 text-lg font-semibold disabled:opacity-50"
style={{ fontFamily: "'Inter', sans-serif" }} style={{ fontFamily: "'Inter', sans-serif" }}
> >
{loading ? ( {loading ? (
@@ -225,17 +225,17 @@ const ContactUs = () => {
{/* Contact Information */} {/* Contact Information */}
<div className="space-y-6"> <div className="space-y-6">
{/* Message Card */} {/* Message Card */}
<Card className="p-8 bg-gradient-to-r from-[#664fa3] to-[#48286e] rounded-2xl shadow-lg text-white"> <Card className="p-8 bg-gradient-to-r from-muted-foreground to-[#48286e] rounded-2xl shadow-lg text-white">
<p className="text-[28px] font-semibold leading-relaxed" style={{ fontFamily: "'Poppins', sans-serif" }}> <p className="text-[28px] font-semibold leading-relaxed" style={{ fontFamily: "'Poppins', sans-serif" }}>
If you have questions, or are interested in joining, we would love hearing from you. If you have questions, or are interested in joining, we would love hearing from you.
</p> </p>
</Card> </Card>
{/* Email Card */} {/* Email Card */}
<Card className="p-6 bg-white rounded-2xl"> <Card className="p-6 bg-background rounded-2xl">
<div className="flex items-center gap-4"> <div className="flex items-center gap-4">
<div className="flex items-center justify-center flex-shrink-0"> <div className="flex items-center justify-center flex-shrink-0">
<Mail className="size-12 text-[#664fa3]" /> <Mail className="size-12 text-muted-foreground" />
</div> </div>
<div> <div>
<a <a
@@ -250,10 +250,10 @@ const ContactUs = () => {
</Card> </Card>
{/* Address Card */} {/* Address Card */}
<Card className="p-6 bg-white rounded-2xl "> <Card className="p-6 bg-background rounded-2xl ">
<div className="flex items-start gap-4"> <div className="flex items-start gap-4">
<div className="flex items-center justify-center flex-shrink-0"> <div className="flex items-center justify-center flex-shrink-0">
<PiMapTrifoldBold className="size-12 text-[#664fa3]" /> <PiMapTrifoldBold className="size-12 text-muted-foreground" />
</div> </div>
<div> <div>
<p className="text-[#48286e] text-[28px] font-semibold mb-1" style={{ fontFamily: "'Poppins', sans-serif" }}> <p className="text-[#48286e] text-[28px] font-semibold mb-1" style={{ fontFamily: "'Poppins', sans-serif" }}>

View File

@@ -111,30 +111,30 @@ const Dashboard = () => {
}; };
return ( return (
<div className="min-h-screen bg-white"> <div className="min-h-screen bg-background">
<Navbar /> <Navbar />
<div className="max-w-7xl mx-auto px-6 py-12"> <div className="max-w-7xl mx-auto px-6 py-12">
{/* Welcome Section */} {/* Welcome Section */}
<div className="mb-12"> <div className="mb-12">
<h1 className="text-4xl md:text-5xl font-semibold text-[#422268] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}> <h1 className="text-4xl md:text-5xl font-semibold text-primary mb-4" style={{ fontFamily: "'Inter', sans-serif" }}>
Welcome Back, {user?.first_name}! Welcome Back, {user?.first_name}!
</h1> </h1>
<p className="text-lg text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-lg text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Here's an overview of your membership status and upcoming events. Here's an overview of your membership status and upcoming events.
</p> </p>
</div> </div>
{/* Email Verification Alert */} {/* Email Verification Alert */}
{user && !user.email_verified && ( {user && !user.email_verified && (
<Card className="p-6 bg-[#f1eef9] border-2 border-[#664fa3] mb-8"> <Card className="p-6 bg-muted border-2 border-muted-foreground mb-8">
<div className="flex items-start gap-4"> <div className="flex items-start gap-4">
<AlertCircle className="h-6 w-6 text-[#664fa3] flex-shrink-0 mt-1" /> <AlertCircle className="h-6 w-6 text-muted-foreground flex-shrink-0 mt-1" />
<div className="flex-1"> <div className="flex-1">
<h3 className="text-lg font-semibold text-[#422268] mb-2" style={{ fontFamily: "'Inter', sans-serif" }}> <h3 className="text-lg font-semibold text-primary mb-2" style={{ fontFamily: "'Inter', sans-serif" }}>
Verify Your Email Address Verify Your Email Address
</h3> </h3>
<p className="text-[#664fa3] mb-4" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-muted-foreground mb-4" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Please verify your email address to complete your registration. Please verify your email address to complete your registration.
Check your inbox for the verification link. Check your inbox for the verification link.
</p> </p>
@@ -142,7 +142,7 @@ const Dashboard = () => {
onClick={handleResendVerification} onClick={handleResendVerification}
disabled={resendLoading} disabled={resendLoading}
variant="outline" variant="outline"
className="border-2 border-[#664fa3] text-[#664fa3] hover:bg-[#664fa3] hover:text-white" className="border-2 border-muted-foreground text-muted-foreground hover:bg-muted-foreground hover:text-white"
> >
<Mail className="h-4 w-4 mr-2" /> <Mail className="h-4 w-4 mr-2" />
{resendLoading ? 'Sending...' : 'Resend Verification Email'} {resendLoading ? 'Sending...' : 'Resend Verification Email'}
@@ -153,22 +153,22 @@ const Dashboard = () => {
)} )}
{/* Status Card */} {/* Status Card */}
<Card className="p-8 bg-white rounded-2xl border border-[#ddd8eb] shadow-lg mb-8" data-testid="status-card"> <Card className="p-8 bg-background rounded-2xl border border-chart-6 shadow-lg mb-8" data-testid="status-card">
<div className="flex items-start justify-between flex-wrap gap-4"> <div className="flex items-start justify-between flex-wrap gap-4">
<div> <div>
<h2 className="text-2xl font-semibold text-[#422268] mb-2" style={{ fontFamily: "'Inter', sans-serif" }}> <h2 className="text-2xl font-semibold text-primary mb-2" style={{ fontFamily: "'Inter', sans-serif" }}>
Membership Status Membership Status
</h2> </h2>
<div className="mb-4"> <div className="mb-4">
{getStatusBadge(user?.status)} {getStatusBadge(user?.status)}
</div> </div>
<p className="text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{getStatusMessage(user?.status)} {getStatusMessage(user?.status)}
</p> </p>
</div> </div>
<Link to="/profile"> <Link to="/profile">
<Button <Button
className="bg-[#DDD8EB] text-[#422268] hover:bg-white rounded-full px-6" className="bg-chart-6 text-primary hover:bg-background rounded-full px-6"
data-testid="view-profile-button" data-testid="view-profile-button"
> >
<User className="h-4 w-4 mr-2" /> <User className="h-4 w-4 mr-2" />
@@ -181,36 +181,36 @@ const Dashboard = () => {
{/* Grid Layout */} {/* Grid Layout */}
<div className="grid lg:grid-cols-3 gap-8"> <div className="grid lg:grid-cols-3 gap-8">
{/* Quick Stats */} {/* Quick Stats */}
<Card className="p-6 bg-white rounded-2xl border border-[#ddd8eb]" data-testid="quick-stats-card"> <Card className="p-6 bg-background rounded-2xl border border-chart-6" data-testid="quick-stats-card">
<h3 className="text-xl font-semibold text-[#422268] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}> <h3 className="text-xl font-semibold text-primary mb-4" style={{ fontFamily: "'Inter', sans-serif" }}>
Quick Info Quick Info
</h3> </h3>
<div className="space-y-4"> <div className="space-y-4">
<div> <div>
<p className="text-sm text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Email</p> <p className="text-sm text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Email</p>
<p className="text-[#422268] font-medium" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>{user?.email}</p> <p className="text-primary font-medium" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>{user?.email}</p>
</div> </div>
<div> <div>
<p className="text-sm text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Role</p> <p className="text-sm text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Role</p>
<p className="text-[#422268] font-medium capitalize" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>{user?.role}</p> <p className="text-primary font-medium capitalize" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>{user?.role}</p>
</div> </div>
<div> <div>
<p className="text-sm text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Member Since</p> <p className="text-sm text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Member Since</p>
<p className="text-[#422268] font-medium" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-primary font-medium" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{user?.created_at ? new Date(user.created_at).toLocaleDateString() : 'N/A'} {user?.created_at ? new Date(user.created_at).toLocaleDateString() : 'N/A'}
</p> </p>
</div> </div>
{user?.subscription_start_date && user?.subscription_end_date && ( {user?.subscription_start_date && user?.subscription_end_date && (
<> <>
<div className="pt-4 border-t border-[#ddd8eb]"> <div className="pt-4 border-t border-chart-6">
<p className="text-sm text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Membership Period</p> <p className="text-sm text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Membership Period</p>
<p className="text-[#422268] font-medium" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-primary font-medium" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{new Date(user.subscription_start_date).toLocaleDateString()} - {new Date(user.subscription_end_date).toLocaleDateString()} {new Date(user.subscription_start_date).toLocaleDateString()} - {new Date(user.subscription_end_date).toLocaleDateString()}
</p> </p>
</div> </div>
<div> <div>
<p className="text-sm text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Days Remaining</p> <p className="text-sm text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Days Remaining</p>
<p className="text-[#422268] font-medium" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-primary font-medium" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{Math.max(0, Math.ceil((new Date(user.subscription_end_date) - new Date()) / (1000 * 60 * 60 * 24)))} days {Math.max(0, Math.ceil((new Date(user.subscription_end_date) - new Date()) / (1000 * 60 * 60 * 24)))} days
</p> </p>
</div> </div>
@@ -220,15 +220,15 @@ const Dashboard = () => {
</Card> </Card>
{/* Upcoming Events */} {/* Upcoming Events */}
<Card className="lg:col-span-2 p-6 bg-white rounded-2xl border border-[#ddd8eb]" data-testid="upcoming-events-card"> <Card className="lg:col-span-2 p-6 bg-background rounded-2xl border border-chart-6" data-testid="upcoming-events-card">
<div className="flex justify-between items-center mb-6"> <div className="flex justify-between items-center mb-6">
<h3 className="text-xl font-semibold text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}> <h3 className="text-xl font-semibold text-primary" style={{ fontFamily: "'Inter', sans-serif" }}>
Upcoming Events Upcoming Events
</h3> </h3>
<Link to="/events"> <Link to="/events">
<Button <Button
variant="ghost" variant="ghost"
className="text-[#ff9e77] hover:text-[#664fa3]" className="text-accent hover:text-muted-foreground"
data-testid="view-all-events-button" data-testid="view-all-events-button"
> >
View All View All
@@ -237,26 +237,26 @@ const Dashboard = () => {
</div> </div>
{loading ? ( {loading ? (
<p className="text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Loading events...</p> <p className="text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Loading events...</p>
) : events.length > 0 ? ( ) : events.length > 0 ? (
<div className="space-y-4"> <div className="space-y-4">
{events.map((event) => ( {events.map((event) => (
<Link to={`/events/${event.id}`} key={event.id}> <Link to={`/events/${event.id}`} key={event.id}>
<div <div
className="p-4 border border-[#ddd8eb] rounded-xl hover:border-[#664fa3] hover:shadow-md transition-all cursor-pointer" className="p-4 border border-chart-6 rounded-xl hover:border-muted-foreground hover:shadow-md transition-all cursor-pointer"
data-testid={`event-card-${event.id}`} data-testid={`event-card-${event.id}`}
> >
<div className="flex items-start gap-4"> <div className="flex items-start gap-4">
<div className="bg-[#DDD8EB]/20 p-3 rounded-lg"> <div className="bg-chart-6/20 p-3 rounded-lg">
<Calendar className="h-6 w-6 text-[#664fa3]" /> <Calendar className="h-6 w-6 text-muted-foreground" />
</div> </div>
<div className="flex-1"> <div className="flex-1">
<h4 className="font-semibold text-[#422268] mb-1" style={{ fontFamily: "'Inter', sans-serif" }}>{event.title}</h4> <h4 className="font-semibold text-primary mb-1" style={{ fontFamily: "'Inter', sans-serif" }}>{event.title}</h4>
<p className="text-sm text-[#664fa3] mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-sm text-muted-foreground mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{new Date(event.start_at).toLocaleDateString()} at{' '} {new Date(event.start_at).toLocaleDateString()} at{' '}
{new Date(event.start_at).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })} {new Date(event.start_at).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}
</p> </p>
<p className="text-sm text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>{event.location}</p> <p className="text-sm text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>{event.location}</p>
</div> </div>
</div> </div>
</div> </div>
@@ -265,9 +265,9 @@ const Dashboard = () => {
</div> </div>
) : ( ) : (
<div className="text-center py-12"> <div className="text-center py-12">
<Calendar className="h-16 w-16 text-[#ddd8eb] mx-auto mb-4" /> <Calendar className="h-16 w-16 text-chart-6 mx-auto mb-4" />
<p className="text-[#664fa3] mb-4" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>No upcoming events at the moment.</p> <p className="text-muted-foreground mb-4" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>No upcoming events at the moment.</p>
<p className="text-sm text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Check back later for new events!</p> <p className="text-sm text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Check back later for new events!</p>
</div> </div>
)} )}
</Card> </Card>
@@ -275,12 +275,12 @@ const Dashboard = () => {
{/* CTA Section */} {/* CTA Section */}
{user?.status === 'pending_validation' && ( {user?.status === 'pending_validation' && (
<Card className="mt-8 p-8 bg-gradient-to-br from-[#DDD8EB]/20 to-[#f1eef9]/20 rounded-2xl border border-[#ddd8eb]"> <Card className="mt-8 p-8 bg-gradient-to-br from-chart-6/20 to-muted/20 rounded-2xl border border-chart-6">
<div className="text-center"> <div className="text-center">
<h3 className="text-2xl font-semibold text-[#422268] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}> <h3 className="text-2xl font-semibold text-primary mb-4" style={{ fontFamily: "'Inter', sans-serif" }}>
Application Under Review Application Under Review
</h3> </h3>
<p className="text-[#664fa3] mb-6" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-muted-foreground mb-6" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Your membership application is being reviewed by our admin team. You'll be notified once validated! Your membership application is being reviewed by our admin team. You'll be notified once validated!
</p> </p>
</div> </div>
@@ -289,20 +289,20 @@ const Dashboard = () => {
{/* Payment Prompt for payment_pending status */} {/* Payment Prompt for payment_pending status */}
{user?.status === 'payment_pending' && ( {user?.status === 'payment_pending' && (
<Card className="mt-8 p-8 bg-gradient-to-br from-[#DDD8EB]/20 to-[#f1eef9]/20 rounded-2xl border-2 border-[#664fa3]"> <Card className="mt-8 p-8 bg-gradient-to-br from-chart-6/20 to-muted/20 rounded-2xl border-2 border-muted-foreground">
<div className="text-center"> <div className="text-center">
<div className="mb-4"> <div className="mb-4">
<AlertCircle className="h-16 w-16 text-[#664fa3] mx-auto" /> <AlertCircle className="h-16 w-16 text-muted-foreground mx-auto" />
</div> </div>
<h3 className="text-2xl font-semibold text-[#422268] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}> <h3 className="text-2xl font-semibold text-primary mb-4" style={{ fontFamily: "'Inter', sans-serif" }}>
Complete Your Payment Complete Your Payment
</h3> </h3>
<p className="text-[#664fa3] mb-6" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-muted-foreground mb-6" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Great news! Your membership application has been validated. Complete your payment to activate your membership and gain full access to all member benefits. Great news! Your membership application has been validated. Complete your payment to activate your membership and gain full access to all member benefits.
</p> </p>
<Link to="/plans"> <Link to="/plans">
<Button <Button
className="bg-[#DDD8EB] text-[#422268] hover:bg-white rounded-full px-8 py-6 text-lg font-semibold" className="bg-chart-6 text-primary hover:bg-background rounded-full px-8 py-6 text-lg font-semibold"
data-testid="complete-payment-cta" data-testid="complete-payment-cta"
> >
<CheckCircle className="mr-2 h-5 w-5" /> <CheckCircle className="mr-2 h-5 w-5" />
@@ -316,38 +316,38 @@ const Dashboard = () => {
{/* Event Activity Section */} {/* Event Activity Section */}
<div className="mt-12"> <div className="mt-12">
<div className="flex justify-between items-center mb-6"> <div className="flex justify-between items-center mb-6">
<h2 className="text-3xl font-semibold text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}> <h2 className="text-3xl font-semibold text-primary" style={{ fontFamily: "'Inter', sans-serif" }}>
My Event Activity My Event Activity
</h2> </h2>
</div> </div>
{activityLoading ? ( {activityLoading ? (
<p className="text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Loading event activity...</p> <p className="text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Loading event activity...</p>
) : eventActivity ? ( ) : eventActivity ? (
<div className="space-y-8"> <div className="space-y-8">
{/* Stats Cards */} {/* Stats Cards */}
<div className="grid md:grid-cols-2 gap-6"> <div className="grid md:grid-cols-2 gap-6">
<Card className="p-6 bg-white rounded-2xl border border-[#ddd8eb]"> <Card className="p-6 bg-background rounded-2xl border border-chart-6">
<div className="flex items-center gap-4"> <div className="flex items-center gap-4">
<div className="bg-[#DDD8EB]/20 p-4 rounded-lg"> <div className="bg-chart-6/20 p-4 rounded-lg">
<Calendar className="h-8 w-8 text-[#664fa3]" /> <Calendar className="h-8 w-8 text-muted-foreground" />
</div> </div>
<div> <div>
<p className="text-sm text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Total RSVPs</p> <p className="text-sm text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Total RSVPs</p>
<p className="text-3xl font-semibold text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}> <p className="text-3xl font-semibold text-primary" style={{ fontFamily: "'Inter', sans-serif" }}>
{eventActivity.total_rsvps} {eventActivity.total_rsvps}
</p> </p>
</div> </div>
</div> </div>
</Card> </Card>
<Card className="p-6 bg-white rounded-2xl border border-[#ddd8eb]"> <Card className="p-6 bg-background rounded-2xl border border-chart-6">
<div className="flex items-center gap-4"> <div className="flex items-center gap-4">
<div className="bg-[#81B29A]/20 p-4 rounded-lg"> <div className="bg-[#81B29A]/20 p-4 rounded-lg">
<CheckCircle className="h-8 w-8 text-[#81B29A]" /> <CheckCircle className="h-8 w-8 text-[#81B29A]" />
</div> </div>
<div> <div>
<p className="text-sm text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Events Attended</p> <p className="text-sm text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Events Attended</p>
<p className="text-3xl font-semibold text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}> <p className="text-3xl font-semibold text-primary" style={{ fontFamily: "'Inter', sans-serif" }}>
{eventActivity.total_attended} {eventActivity.total_attended}
</p> </p>
</div> </div>
@@ -357,30 +357,30 @@ const Dashboard = () => {
{/* Upcoming RSVP'd Events */} {/* Upcoming RSVP'd Events */}
{eventActivity.upcoming_events && eventActivity.upcoming_events.length > 0 && ( {eventActivity.upcoming_events && eventActivity.upcoming_events.length > 0 && (
<Card className="p-6 bg-white rounded-2xl border border-[#ddd8eb]"> <Card className="p-6 bg-background rounded-2xl border border-chart-6">
<h3 className="text-xl font-semibold text-[#422268] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}> <h3 className="text-xl font-semibold text-primary mb-4" style={{ fontFamily: "'Inter', sans-serif" }}>
Upcoming Events (RSVP'd) Upcoming Events (RSVP'd)
</h3> </h3>
<div className="space-y-3"> <div className="space-y-3">
{eventActivity.upcoming_events.map((event) => ( {eventActivity.upcoming_events.map((event) => (
<Link to={`/events/${event.id}`} key={event.id}> <Link to={`/events/${event.id}`} key={event.id}>
<div className="p-4 border border-[#ddd8eb] rounded-xl hover:border-[#664fa3] hover:shadow-md transition-all"> <div className="p-4 border border-chart-6 rounded-xl hover:border-muted-foreground hover:shadow-md transition-all">
<div className="flex items-start justify-between gap-4"> <div className="flex items-start justify-between gap-4">
<div className="flex-1"> <div className="flex-1">
<h4 className="font-semibold text-[#422268] mb-1" style={{ fontFamily: "'Inter', sans-serif" }}>{event.title}</h4> <h4 className="font-semibold text-primary mb-1" style={{ fontFamily: "'Inter', sans-serif" }}>{event.title}</h4>
<p className="text-sm text-[#664fa3] mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-sm text-muted-foreground mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{new Date(event.start_at).toLocaleDateString()} at{' '} {new Date(event.start_at).toLocaleDateString()} at{' '}
{new Date(event.start_at).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })} {new Date(event.start_at).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}
</p> </p>
<p className="text-sm text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>{event.location}</p> <p className="text-sm text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>{event.location}</p>
</div> </div>
<Badge className={ <Badge className={
event.rsvp_status === 'yes' ? 'bg-[#81B29A] text-white' : event.rsvp_status === 'yes' ? 'bg-[#81B29A] text-white' :
event.rsvp_status === 'maybe' ? 'bg-orange-100 text-orange-700' : event.rsvp_status === 'maybe' ? 'bg-orange-100 text-orange-700' :
'bg-gray-200 text-gray-700' 'bg-gray-200 text-gray-700'
}> }>
{event.rsvp_status === 'yes' ? 'Going' : {event.rsvp_status === 'yes' ? 'Going' :
event.rsvp_status === 'maybe' ? 'Maybe' : 'Not Going'} event.rsvp_status === 'maybe' ? 'Maybe' : 'Not Going'}
</Badge> </Badge>
</div> </div>
</div> </div>
@@ -392,17 +392,17 @@ const Dashboard = () => {
{/* Past Events & Attendance */} {/* Past Events & Attendance */}
{eventActivity.past_events && eventActivity.past_events.length > 0 && ( {eventActivity.past_events && eventActivity.past_events.length > 0 && (
<Card className="p-6 bg-white rounded-2xl border border-[#ddd8eb]"> <Card className="p-6 bg-background rounded-2xl border border-chart-6">
<h3 className="text-xl font-semibold text-[#422268] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}> <h3 className="text-xl font-semibold text-primary mb-4" style={{ fontFamily: "'Inter', sans-serif" }}>
Past Events Past Events
</h3> </h3>
<div className="space-y-3"> <div className="space-y-3">
{eventActivity.past_events.slice(0, 5).map((event) => ( {eventActivity.past_events.slice(0, 5).map((event) => (
<div key={event.id} className="p-4 border border-[#ddd8eb] rounded-xl"> <div key={event.id} className="p-4 border border-chart-6 rounded-xl">
<div className="flex items-start justify-between gap-4"> <div className="flex items-start justify-between gap-4">
<div className="flex-1"> <div className="flex-1">
<h4 className="font-semibold text-[#422268] mb-1" style={{ fontFamily: "'Inter', sans-serif" }}>{event.title}</h4> <h4 className="font-semibold text-primary mb-1" style={{ fontFamily: "'Inter', sans-serif" }}>{event.title}</h4>
<p className="text-sm text-[#664fa3] mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-sm text-muted-foreground mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{new Date(event.start_at).toLocaleDateString()} at{' '} {new Date(event.start_at).toLocaleDateString()} at{' '}
{new Date(event.start_at).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })} {new Date(event.start_at).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}
</p> </p>
@@ -412,7 +412,7 @@ const Dashboard = () => {
{event.attended ? 'Attended' : 'Did not attend'} {event.attended ? 'Attended' : 'Did not attend'}
</Badge> </Badge>
{event.attended && event.attended_at && ( {event.attended && event.attended_at && (
<p className="text-xs text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-xs text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Checked in: {new Date(event.attended_at).toLocaleDateString()} Checked in: {new Date(event.attended_at).toLocaleDateString()}
</p> </p>
)} )}
@@ -422,7 +422,7 @@ const Dashboard = () => {
))} ))}
</div> </div>
{eventActivity.past_events.length > 5 && ( {eventActivity.past_events.length > 5 && (
<p className="text-sm text-center text-[#664fa3] mt-4" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-sm text-center text-muted-foreground mt-4" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Showing 5 of {eventActivity.past_events.length} past events Showing 5 of {eventActivity.past_events.length} past events
</p> </p>
)} )}
@@ -431,31 +431,31 @@ const Dashboard = () => {
{/* No Events Message */} {/* No Events Message */}
{(!eventActivity.upcoming_events || eventActivity.upcoming_events.length === 0) && {(!eventActivity.upcoming_events || eventActivity.upcoming_events.length === 0) &&
(!eventActivity.past_events || eventActivity.past_events.length === 0) && ( (!eventActivity.past_events || eventActivity.past_events.length === 0) && (
<Card className="p-12 bg-white rounded-2xl border border-[#ddd8eb]"> <Card className="p-12 bg-background rounded-2xl border border-chart-6">
<div className="text-center"> <div className="text-center">
<Calendar className="h-16 w-16 text-[#ddd8eb] mx-auto mb-4" /> <Calendar className="h-16 w-16 text-chart-6 mx-auto mb-4" />
<h3 className="text-xl font-semibold text-[#422268] mb-2" style={{ fontFamily: "'Inter', sans-serif" }}> <h3 className="text-xl font-semibold text-primary mb-2" style={{ fontFamily: "'Inter', sans-serif" }}>
No Event Activity Yet No Event Activity Yet
</h3> </h3>
<p className="text-[#664fa3] mb-6" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-muted-foreground mb-6" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Browse upcoming events and RSVP to start building your event history! Browse upcoming events and RSVP to start building your event history!
</p> </p>
<Link to="/events"> <Link to="/events">
<Button className="bg-[#DDD8EB] text-[#422268] hover:bg-white rounded-full px-6"> <Button className="bg-chart-6 text-primary hover:bg-background rounded-full px-6">
<Calendar className="h-4 w-4 mr-2" /> <Calendar className="h-4 w-4 mr-2" />
Browse Events Browse Events
</Button> </Button>
</Link> </Link>
</div> </div>
</Card> </Card>
)} )}
</div> </div>
) : ( ) : (
<Card className="p-12 bg-white rounded-2xl border border-[#ddd8eb]"> <Card className="p-12 bg-background rounded-2xl border border-chart-6">
<div className="text-center"> <div className="text-center">
<AlertCircle className="h-16 w-16 text-[#ddd8eb] mx-auto mb-4" /> <AlertCircle className="h-16 w-16 text-chart-6 mx-auto mb-4" />
<p className="text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Failed to load event activity. Please try refreshing the page. Failed to load event activity. Please try refreshing the page.
</p> </p>
</div> </div>

View File

@@ -58,7 +58,7 @@ const Donate = () => {
<div className="min-h-screen"> <div className="min-h-screen">
<PublicNavbar /> <PublicNavbar />
<main className="bg-gradient-to-bl from-[#F9FAFB] to-[#DDD8EB] px-4 sm:px-6 md:px-8 lg:px-12 xl:px-16 py-8 sm:py-10 md:py-12"> <main className="bg-gradient-to-bl from-[#F9FAFB] to-chart-6 px-4 sm:px-6 md:px-8 lg:px-12 xl:px-16 py-8 sm:py-10 md:py-12">
{/* Hero Section */} {/* Hero Section */}
<section className="py-12"> <section className="py-12">
<div className="max-w-4xl mx-auto text-center h-full"> <div className="max-w-4xl mx-auto text-center h-full">
@@ -79,96 +79,96 @@ const Donate = () => {
<div className="py-12"> <div className="py-12">
<div className='grid grid-cols-1 items-stretch lg:grid-cols-[2fr_1fr] gap-8 lg:max-h-[450px]'> <div className='grid grid-cols-1 items-stretch lg:grid-cols-[2fr_1fr] gap-8 lg:max-h-[450px]'>
{/* Donation Amount Buttons */} {/* Donation Amount Buttons */}
<section className="flex flex-col h-full"> <section className="flex flex-col h-full">
<div className="mx-auto flex-1 w-full h-full"> <div className="mx-auto flex-1 w-full h-full">
<Card className="p-8 bg-white rounded-3xl w-full h-full content-center"> <Card className="p-8 bg-background rounded-3xl w-full h-full content-center">
<div className="flex items-center gap-4 mb-6"> <div className="flex items-center gap-4 mb-6">
<CreditCard className="size-24 text-[#664fa3]" /> <CreditCard className="size-24 text-muted-foreground" />
<h2 className="text-3xl font-bold text-[#48286e]" style={{ fontFamily: "'Inter', sans-serif" }}> <h2 className="text-3xl font-bold text-[#48286e]" style={{ fontFamily: "'Inter', sans-serif" }}>
Select Your Donation Amount Select Your Donation Amount
</h2> </h2>
</div>
{/* Donation Buttons Grid */}
<div className="grid grid-cols-2 md:grid-cols-4 gap-4 mb-6">
{[25, 50, 100, 250].map(amount => (
<Button
key={amount}
onClick={() => handleDonateAmount(amount * 100)}
disabled={processingAmount === amount * 100}
className="bg-[#664fa3] hover:bg-[#48286e] text-white text-xl py-8 rounded-full disabled:opacity-50"
>
{processingAmount === amount * 100 ? (
<Loader2 className="h-6 w-6 animate-spin" />
) : (
`$${amount}`
)}
</Button>
))}
</div>
{/* Custom Amount Button */}
<Button
onClick={() => setCustomAmountDialogOpen(true)}
disabled={processingAmount !== null}
className="w-full bg-[#664fa3] hover:bg-[#48286e] text-white text-xl py-8 rounded-full flex items-center justify-center gap-2"
>
<Heart className="h-6 w-6" />
Donate Any Amount
</Button>
<p className="text-sm text-[#664fa3] text-center mt-4" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Secure donation processing powered by Stripe
</p>
</Card>
</div>
</section>
{/* Alternative Payment Methods */}
<section className="flex flex-col">
<div className="max-w-6xl mx-auto w-full">
<div className="flex flex-col gap-8 w-full">
{/* Mail Check */}
<Card className="p-8 bg-white rounded-3xl flex gap-4 items-center flex-1">
<Mail className="size-24 text-[#664fa3]" />
<div>
<h3 className="text-2xl font-bold text-[#48286e] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}>
Mail a Check
</h3>
<p className="text-lg text-[#48286e] leading-relaxed" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Our mailing address for checks:<br />
<span className="font-semibold">LOAF</span><br />
P.O. Box 7207<br />
Houston, Texas 77248-7207
</p>
</div>
</Card>
{/* Zelle */}
<Card className="p-8 bg-white rounded-3xl flex gap-4 items-center flex-1">
<div className="w-44">
<img src={zelleLogo} alt="Zelle" className=" w-32" onError={(e) => e.target.style.display = 'none'} />
</div> </div>
<div> {/* Donation Buttons Grid */}
<h3 className="text-2xl font-bold text-[#48286e] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}> <div className="grid grid-cols-2 md:grid-cols-4 gap-4 mb-6">
Pay with Zelle {[25, 50, 100, 250].map(amount => (
</h3> <Button
<p className="text-lg text-[#48286e] leading-relaxed mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> key={amount}
If your bank allows the use of Zelle, please feel free to send money to: onClick={() => handleDonateAmount(amount * 100)}
</p> disabled={processingAmount === amount * 100}
<a href="mailto:LOAFHoustonTX@gmail.com" className="bg-muted-foreground hover:bg-[#48286e] text-white text-xl py-8 rounded-full disabled:opacity-50"
className="text-[#664fa3] text-lg font-bold underline hover:text-[#48286e] transition-colors" >
style={{ fontFamily: "'Nunito Sans', sans-serif" }}> {processingAmount === amount * 100 ? (
LOAFHoustonTX@gmail.com <Loader2 className="h-6 w-6 animate-spin" />
</a> ) : (
`$${amount}`
)}
</Button>
))}
</div> </div>
{/* Custom Amount Button */}
<Button
onClick={() => setCustomAmountDialogOpen(true)}
disabled={processingAmount !== null}
className="w-full bg-muted-foreground hover:bg-[#48286e] text-white text-xl py-8 rounded-full flex items-center justify-center gap-2"
>
<Heart className="h-6 w-6" />
Donate Any Amount
</Button>
<p className="text-sm text-muted-foreground text-center mt-4" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Secure donation processing powered by Stripe
</p>
</Card> </Card>
</div> </div>
</div> </section>
</section>
{/* Alternative Payment Methods */}
<section className="flex flex-col">
<div className="max-w-6xl mx-auto w-full">
<div className="flex flex-col gap-8 w-full">
{/* Mail Check */}
<Card className="p-8 bg-background rounded-3xl flex gap-4 items-center flex-1">
<Mail className="size-24 text-muted-foreground" />
<div>
<h3 className="text-2xl font-bold text-[#48286e] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}>
Mail a Check
</h3>
<p className="text-lg text-[#48286e] leading-relaxed" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Our mailing address for checks:<br />
<span className="font-semibold">LOAF</span><br />
P.O. Box 7207<br />
Houston, Texas 77248-7207
</p>
</div>
</Card>
{/* Zelle */}
<Card className="p-8 bg-background rounded-3xl flex gap-4 items-center flex-1">
<div className="w-44">
<img src={zelleLogo} alt="Zelle" className=" w-32" onError={(e) => e.target.style.display = 'none'} />
</div>
<div>
<h3 className="text-2xl font-bold text-[#48286e] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}>
Pay with Zelle
</h3>
<p className="text-lg text-[#48286e] leading-relaxed mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
If your bank allows the use of Zelle, please feel free to send money to:
</p>
<a href="mailto:LOAFHoustonTX@gmail.com"
className="text-muted-foreground text-lg font-bold underline hover:text-[#48286e] transition-colors"
style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
LOAFHoustonTX@gmail.com
</a>
</div>
</Card>
</div>
</div>
</section>
</div> </div>
</div> </div>
@@ -179,23 +179,23 @@ const Donate = () => {
{/* Custom Amount Dialog */} {/* Custom Amount Dialog */}
<Dialog open={customAmountDialogOpen} onOpenChange={setCustomAmountDialogOpen}> <Dialog open={customAmountDialogOpen} onOpenChange={setCustomAmountDialogOpen}>
<DialogContent className="sm:max-w-[450px] bg-white rounded-3xl"> <DialogContent className="sm:max-w-[450px] bg-background rounded-3xl">
<DialogHeader> <DialogHeader>
<DialogTitle className="text-2xl font-semibold text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}> <DialogTitle className="text-2xl font-semibold text-primary" style={{ fontFamily: "'Inter', sans-serif" }}>
Enter Donation Amount Enter Donation Amount
</DialogTitle> </DialogTitle>
<DialogDescription className="text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <DialogDescription className="text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Choose how much you'd like to donate to support our community Choose how much you'd like to donate to support our community
</DialogDescription> </DialogDescription>
</DialogHeader> </DialogHeader>
<div className="space-y-4 py-4"> <div className="space-y-4 py-4">
<div> <div>
<Label htmlFor="customAmount" className="text-[#422268] font-medium" style={{ fontFamily: "'Inter', sans-serif" }}> <Label htmlFor="customAmount" className="text-primary font-medium" style={{ fontFamily: "'Inter', sans-serif" }}>
Amount (USD) Amount (USD)
</Label> </Label>
<div className="relative mt-2"> <div className="relative mt-2">
<span className="absolute left-4 top-1/2 transform -translate-y-1/2 text-[#664fa3] text-xl font-semibold"> <span className="absolute left-4 top-1/2 transform -translate-y-1/2 text-muted-foreground text-xl font-semibold">
$ $
</span> </span>
<Input <Input
@@ -206,7 +206,7 @@ const Donate = () => {
value={customAmount} value={customAmount}
onChange={(e) => setCustomAmount(e.target.value)} onChange={(e) => setCustomAmount(e.target.value)}
placeholder="50.00" placeholder="50.00"
className="pl-10 h-14 text-xl border-2 border-[#ddd8eb] focus:border-[#664fa3] rounded-xl" className="pl-10 h-14 text-xl border-2 border-chart-6 focus:border-muted-foreground rounded-xl"
onKeyPress={(e) => { onKeyPress={(e) => {
if (e.key === 'Enter') { if (e.key === 'Enter') {
handleCustomDonate(); handleCustomDonate();
@@ -214,13 +214,13 @@ const Donate = () => {
}} }}
/> />
</div> </div>
<p className="text-sm text-[#664fa3] mt-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-sm text-muted-foreground mt-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Minimum donation: $1.00 Minimum donation: $1.00
</p> </p>
</div> </div>
<div className="bg-[#f1eef9] rounded-lg p-4"> <div className="bg-muted rounded-lg p-4">
<p className="text-sm text-[#422268] text-center" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-sm text-primary text-center" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<strong>Thank you for supporting LOAF!</strong><br /> <strong>Thank you for supporting LOAF!</strong><br />
Your donation helps us continue our mission and provide meaningful experiences for our community. Your donation helps us continue our mission and provide meaningful experiences for our community.
</p> </p>
@@ -232,14 +232,14 @@ const Donate = () => {
type="button" type="button"
variant="outline" variant="outline"
onClick={() => setCustomAmountDialogOpen(false)} onClick={() => setCustomAmountDialogOpen(false)}
className="rounded-full border-2 border-[#ddd8eb]" className="rounded-full border-2 border-chart-6"
> >
Cancel Cancel
</Button> </Button>
<Button <Button
type="button" type="button"
onClick={handleCustomDonate} onClick={handleCustomDonate}
className="bg-[#664fa3] text-white hover:bg-[#48286e] rounded-full" className="bg-muted-foreground text-white hover:bg-[#48286e] rounded-full"
> >
Continue to Payment Continue to Payment
</Button> </Button>

View File

@@ -11,12 +11,12 @@ const DonationSuccess = () => {
const loafHearts = `${process.env.PUBLIC_URL}/loaf-hearts.png`; const loafHearts = `${process.env.PUBLIC_URL}/loaf-hearts.png`;
return ( return (
<div className="min-h-screen bg-white"> <div className="min-h-screen bg-background">
<PublicNavbar /> <PublicNavbar />
<main className="bg-gradient-to-b from-white to-[#f1eef9] px-6 py-20"> <main className="bg-gradient-to-b from-white to-muted px-6 py-20">
<div className="max-w-2xl mx-auto"> <div className="max-w-2xl mx-auto">
<Card className="p-6 sm:p-8 md:p-12 bg-white rounded-2xl border-2 border-[#ddd8eb] shadow-xl text-center"> <Card className="p-6 sm:p-8 md:p-12 bg-background rounded-2xl border-2 border-chart-6 shadow-xl text-center">
{/* Success Icon */} {/* Success Icon */}
<div className="flex justify-center mb-4"> <div className="flex justify-center mb-4">
<img <img
@@ -31,29 +31,29 @@ const DonationSuccess = () => {
</div> </div>
{/* Title */} {/* Title */}
<h1 className="text-2xl sm:text-3xl md:text-4xl font-semibold text-[#422268] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}> <h1 className="text-2xl sm:text-3xl md:text-4xl font-semibold text-primary mb-4" style={{ fontFamily: "'Inter', sans-serif" }}>
Thank You for Your Donation! Thank You for Your Donation!
</h1> </h1>
{/* Message */} {/* Message */}
<div className="space-y-4 mb-8"> <div className="space-y-4 mb-8">
<p className="text-xl text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-xl text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Your generous contribution helps support our community and continue our mission. Your generous contribution helps support our community and continue our mission.
</p> </p>
<div className="bg-gradient-to-r from-[#f1eef9] to-[#DDD8EB]/30 rounded-2xl p-6 border-2 border-[#ddd8eb]"> <div className="bg-gradient-to-r from-muted to-chart-6/30 rounded-2xl p-6 border-2 border-chart-6">
<div className="flex items-center justify-center gap-2 text-[#ff9e77] mb-2"> <div className="flex items-center justify-center gap-2 text-accent mb-2">
<Heart className="h-6 w-6" /> <Heart className="h-6 w-6" />
<span className="text-lg font-semibold" style={{ fontFamily: "'Inter', sans-serif" }}> <span className="text-lg font-semibold" style={{ fontFamily: "'Inter', sans-serif" }}>
Your Support Makes a Difference Your Support Makes a Difference
</span> </span>
</div> </div>
<p className="text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
A receipt for your donation has been sent to your email address. A receipt for your donation has been sent to your email address.
</p> </p>
</div> </div>
<p className="text-base text-[#664fa3] pt-4" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-base text-muted-foreground pt-4" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
We deeply appreciate your support and commitment to LOAF's mission of building a vibrant, inclusive community. We deeply appreciate your support and commitment to LOAF's mission of building a vibrant, inclusive community.
</p> </p>
</div> </div>
@@ -62,7 +62,7 @@ const DonationSuccess = () => {
<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('/')} onClick={() => navigate('/')}
className="bg-[#664fa3] text-white hover:bg-[#422268] rounded-full px-8 py-6 text-lg font-medium shadow-lg" className="bg-muted-foreground text-white hover:bg-primary rounded-full px-8 py-6 text-lg font-medium shadow-lg"
style={{ fontFamily: "'Inter', sans-serif" }} style={{ fontFamily: "'Inter', sans-serif" }}
> >
Return to Home Return to Home
@@ -70,7 +70,7 @@ const DonationSuccess = () => {
<Button <Button
onClick={() => navigate('/donate')} onClick={() => navigate('/donate')}
variant="outline" variant="outline"
className="border-2 border-[#664fa3] text-[#664fa3] hover:bg-[#DDD8EB]/20 rounded-full px-8 py-6 text-lg font-medium" className="border-2 border-muted-foreground text-muted-foreground hover:bg-chart-6/20 rounded-full px-8 py-6 text-lg font-medium"
style={{ fontFamily: "'Inter', sans-serif" }} style={{ fontFamily: "'Inter', sans-serif" }}
> >
Make Another Donation Make Another Donation
@@ -80,12 +80,12 @@ const DonationSuccess = () => {
{/* Additional Info */} {/* Additional Info */}
<div className="mt-12 text-center"> <div className="mt-12 text-center">
<p className="text-sm text-[#664fa3] mb-4" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-sm text-muted-foreground mb-4" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Have questions about your donation? Have questions about your donation?
</p> </p>
<a <a
href="mailto:support@loaf.org" href="mailto:support@loaf.org"
className="text-[#ff9e77] hover:text-[#664fa3] font-medium transition-colors" className="text-accent hover:text-muted-foreground font-medium transition-colors"
style={{ fontFamily: "'Inter', sans-serif" }} style={{ fontFamily: "'Inter', sans-serif" }}
> >
Contact us at support@loaf.org Contact us at support@loaf.org

View File

@@ -48,10 +48,10 @@ const EventDetails = () => {
if (loading) { if (loading) {
return ( return (
<div className="min-h-screen bg-white"> <div className="min-h-screen bg-background">
<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-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Loading event...</p> <p className="text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Loading event...</p>
</div> </div>
</div> </div>
); );
@@ -62,34 +62,33 @@ const EventDetails = () => {
} }
return ( return (
<div className="min-h-screen bg-white"> <div className="min-h-screen bg-background">
<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-[#664fa3] hover:text-[#ff9e77] transition-colors mb-8" className="inline-flex items-center text-muted-foreground hover:text-accent 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-[#ddd8eb] shadow-lg"> <Card className="p-8 md:p-12 bg-background rounded-2xl border border-chart-6 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-[#DDD8EB]/20 p-4 rounded-xl"> <div className="bg-chart-6/20 p-4 rounded-xl">
<Calendar className="h-10 w-10 text-[#664fa3]" /> <Calendar className="h-10 w-10 text-muted-foreground" />
</div> </div>
{event.user_rsvp_status && ( {event.user_rsvp_status && (
<Badge <Badge
className={`px-4 py-2 rounded-full text-sm ${ className={`px-4 py-2 rounded-full text-sm ${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-gray-400 text-white' ? 'bg-gray-400 text-white'
: 'bg-orange-100 text-orange-700' : 'bg-orange-100 text-orange-700'
}`} }`}
> >
{event.user_rsvp_status === 'yes' && 'Going'} {event.user_rsvp_status === 'yes' && 'Going'}
{event.user_rsvp_status === 'no' && 'Not Going'} {event.user_rsvp_status === 'no' && 'Not Going'}
@@ -98,12 +97,12 @@ const EventDetails = () => {
)} )}
</div> </div>
<h1 className="text-4xl md:text-5xl font-semibold text-[#422268] mb-6" style={{ fontFamily: "'Inter', sans-serif" }}> <h1 className="text-4xl md:text-5xl font-semibold text-primary 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-[#664fa3]"> <div className="flex items-center gap-3 text-muted-foreground">
<Calendar className="h-5 w-5" /> <Calendar className="h-5 w-5" />
<span style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <span style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{new Date(event.start_at).toLocaleDateString('en-US', { {new Date(event.start_at).toLocaleDateString('en-US', {
@@ -114,18 +113,18 @@ const EventDetails = () => {
})} })}
</span> </span>
</div> </div>
<div className="flex items-center gap-3 text-[#664fa3]"> <div className="flex items-center gap-3 text-muted-foreground">
<Calendar className="h-5 w-5" /> <Calendar className="h-5 w-5" />
<span style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <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-[#664fa3]"> <div className="flex items-center gap-3 text-muted-foreground">
<MapPin className="h-5 w-5" /> <MapPin className="h-5 w-5" />
<span style={{ fontFamily: "'Nunito Sans', sans-serif" }}>{event.location}</span> <span style={{ fontFamily: "'Nunito Sans', sans-serif" }}>{event.location}</span>
</div> </div>
<div className="flex items-center gap-3 text-[#664fa3]"> <div className="flex items-center gap-3 text-muted-foreground">
<Users className="h-5 w-5" /> <Users className="h-5 w-5" />
<span style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <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
@@ -136,29 +135,28 @@ const EventDetails = () => {
</div> </div>
{event.description && ( {event.description && (
<div className="mb-8 pb-8 border-b border-[#ddd8eb]"> <div className="mb-8 pb-8 border-b border-chart-6">
<h2 className="text-2xl font-semibold text-[#422268] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}> <h2 className="text-2xl font-semibold text-primary mb-4" style={{ fontFamily: "'Inter', sans-serif" }}>
About This Event About This Event
</h2> </h2>
<p className="text-[#664fa3] leading-relaxed whitespace-pre-line" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-muted-foreground 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 text-[#422268] mb-6" style={{ fontFamily: "'Inter', sans-serif" }}> <h2 className="text-2xl font-semibold text-primary 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">
<Button <Button
onClick={() => handleRSVP('yes')} onClick={() => handleRSVP('yes')}
disabled={rsvpLoading} disabled={rsvpLoading}
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-chart-6 text-primary hover:bg-background'
: 'bg-[#DDD8EB] text-[#422268] hover:bg-white' }`}
}`}
data-testid="rsvp-yes-button" data-testid="rsvp-yes-button"
> >
<Check className="h-5 w-5" /> <Check className="h-5 w-5" />
@@ -168,11 +166,10 @@ const EventDetails = () => {
onClick={() => handleRSVP('maybe')} onClick={() => handleRSVP('maybe')}
disabled={rsvpLoading} disabled={rsvpLoading}
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-orange-400 bg-orange-100 text-orange-700'
? 'border-orange-400 bg-orange-100 text-orange-700' : 'border-muted-foreground text-muted-foreground hover:bg-muted'
: 'border-[#664fa3] text-[#664fa3] hover:bg-[#f1eef9]' }`}
}`}
data-testid="rsvp-maybe-button" data-testid="rsvp-maybe-button"
> >
<HelpCircle className="h-5 w-5" /> <HelpCircle className="h-5 w-5" />
@@ -182,11 +179,10 @@ const EventDetails = () => {
onClick={() => handleRSVP('no')} onClick={() => handleRSVP('no')}
disabled={rsvpLoading} disabled={rsvpLoading}
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-gray-400 bg-gray-100 text-gray-700'
? 'border-gray-400 bg-gray-100 text-gray-700' : 'border-gray-400 text-gray-600 hover:bg-gray-50'
: 'border-gray-400 text-gray-600 hover:bg-gray-50' }`}
}`}
data-testid="rsvp-no-button" data-testid="rsvp-no-button"
> >
<X className="h-5 w-5" /> <X className="h-5 w-5" />
@@ -195,11 +191,11 @@ const EventDetails = () => {
</div> </div>
{/* Add to Calendar Section */} {/* Add to Calendar Section */}
<div className="mt-8 pt-8 border-t border-[#ddd8eb]"> <div className="mt-8 pt-8 border-t border-chart-6">
<h2 className="text-xl font-semibold text-[#422268] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}> <h2 className="text-xl font-semibold text-primary mb-4" style={{ fontFamily: "'Inter', sans-serif" }}>
Add to Your Calendar Add to Your Calendar
</h2> </h2>
<p className="text-[#664fa3] mb-4" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-muted-foreground mb-4" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Never miss this event! Add it to your calendar app for reminders. Never miss this event! Add it to your calendar app for reminders.
</p> </p>
<AddToCalendarButton <AddToCalendarButton

View File

@@ -46,67 +46,67 @@ const Events = () => {
}; };
return ( return (
<div className="min-h-screen bg-white"> <div className="min-h-screen bg-background">
<Navbar /> <Navbar />
<div className="max-w-7xl mx-auto px-6 py-12"> <div className="max-w-7xl mx-auto px-6 py-12">
<div className="mb-12"> <div className="mb-12">
<h1 className="text-4xl md:text-5xl font-semibold text-[#422268] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}> <h1 className="text-4xl md:text-5xl font-semibold text-primary mb-4" style={{ fontFamily: "'Inter', sans-serif" }}>
Upcoming Events Upcoming Events
</h1> </h1>
<p className="text-lg text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-lg text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Browse and RSVP to our community events. Browse and RSVP to our community events.
</p> </p>
</div> </div>
{loading ? ( {loading ? (
<div className="text-center py-20"> <div className="text-center py-20">
<p className="text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Loading events...</p> <p className="text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Loading events...</p>
</div> </div>
) : events.length > 0 ? ( ) : events.length > 0 ? (
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6 sm:gap-8"> <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6 sm:gap-8">
{events.map((event) => ( {events.map((event) => (
<Link to={`/events/${event.id}`} key={event.id}> <Link to={`/events/${event.id}`} key={event.id}>
<Card <Card
className="p-6 bg-white rounded-2xl border border-[#ddd8eb] hover:shadow-lg hover:-translate-y-1 transition-all cursor-pointer h-full" className="p-6 bg-background rounded-2xl border border-chart-6 hover:shadow-lg hover:-translate-y-1 transition-all cursor-pointer h-full"
data-testid={`event-card-${event.id}`} data-testid={`event-card-${event.id}`}
> >
<div className="flex justify-between items-start mb-4"> <div className="flex justify-between items-start mb-4">
<div className="bg-[#DDD8EB]/20 p-3 rounded-lg"> <div className="bg-chart-6/20 p-3 rounded-lg">
<Calendar className="h-6 w-6 text-[#664fa3]" /> <Calendar className="h-6 w-6 text-muted-foreground" />
</div> </div>
{getRSVPBadge(event.user_rsvp_status)} {getRSVPBadge(event.user_rsvp_status)}
</div> </div>
<h3 className="text-xl font-semibold text-[#422268] mb-3" style={{ fontFamily: "'Inter', sans-serif" }}> <h3 className="text-xl font-semibold text-primary mb-3" style={{ fontFamily: "'Inter', sans-serif" }}>
{event.title} {event.title}
</h3> </h3>
{event.description && ( {event.description && (
<p className="text-[#664fa3] mb-4 line-clamp-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-muted-foreground mb-4 line-clamp-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{event.description} {event.description}
</p> </p>
)} )}
<div className="space-y-2 text-sm"> <div className="space-y-2 text-sm">
<div className="flex items-center gap-2 text-[#664fa3]"> <div className="flex items-center gap-2 text-muted-foreground">
<Calendar className="h-4 w-4" /> <Calendar className="h-4 w-4" />
<span style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <span style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{new Date(event.start_at).toLocaleDateString()} at{' '} {new Date(event.start_at).toLocaleDateString()} at{' '}
{new Date(event.start_at).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })} {new Date(event.start_at).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}
</span> </span>
</div> </div>
<div className="flex items-center gap-2 text-[#664fa3]"> <div className="flex items-center gap-2 text-muted-foreground">
<MapPin className="h-4 w-4" /> <MapPin className="h-4 w-4" />
<span style={{ fontFamily: "'Nunito Sans', sans-serif" }}>{event.location}</span> <span style={{ fontFamily: "'Nunito Sans', sans-serif" }}>{event.location}</span>
</div> </div>
<div className="flex items-center gap-2 text-[#664fa3]"> <div className="flex items-center gap-2 text-muted-foreground">
<Users className="h-4 w-4" /> <Users className="h-4 w-4" />
<span style={{ fontFamily: "'Nunito Sans', sans-serif" }}>{event.rsvp_count || 0} attending</span> <span style={{ fontFamily: "'Nunito Sans', sans-serif" }}>{event.rsvp_count || 0} attending</span>
</div> </div>
</div> </div>
<div className="mt-6 flex items-center text-[#ff9e77] font-medium"> <div className="mt-6 flex items-center text-accent font-medium">
View Details View Details
<ArrowRight className="ml-2 h-4 w-4" /> <ArrowRight className="ml-2 h-4 w-4" />
</div> </div>
@@ -116,11 +116,11 @@ const Events = () => {
</div> </div>
) : ( ) : (
<div className="text-center py-20"> <div className="text-center py-20">
<Calendar className="h-20 w-20 text-[#ddd8eb] mx-auto mb-6" /> <Calendar className="h-20 w-20 text-chart-6 mx-auto mb-6" />
<h3 className="text-2xl font-semibold text-[#422268] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}> <h3 className="text-2xl font-semibold text-primary mb-4" style={{ fontFamily: "'Inter', sans-serif" }}>
No Events Available No Events Available
</h3> </h3>
<p className="text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
There are no upcoming events at the moment. Check back later! There are no upcoming events at the moment. Check back later!
</p> </p>
</div> </div>

View File

@@ -32,28 +32,28 @@ const ForgotPassword = () => {
}; };
return ( return (
<div className="min-h-screen bg-white"> <div className="min-h-screen bg-background">
<PublicNavbar /> <PublicNavbar />
<div className="max-w-md mx-auto px-6 py-12"> <div className="max-w-md mx-auto px-6 py-12">
<div className="mb-8"> <div className="mb-8">
<Link to="/login" className="inline-flex items-center text-[#664fa3] hover:text-[#ff9e77] transition-colors"> <Link to="/login" className="inline-flex items-center text-muted-foreground hover:text-accent transition-colors">
<ArrowLeft className="h-4 w-4 mr-2" /> <ArrowLeft className="h-4 w-4 mr-2" />
Back to Login Back to Login
</Link> </Link>
</div> </div>
<Card className="p-8 md:p-12 bg-white rounded-2xl border border-[#ddd8eb] shadow-lg"> <Card className="p-8 md:p-12 bg-background rounded-2xl border border-chart-6 shadow-lg">
{!submitted ? ( {!submitted ? (
<> <>
<div className="mb-8 text-center"> <div className="mb-8 text-center">
<div className="inline-flex items-center justify-center w-16 h-16 rounded-full bg-[#f1eef9] mb-4"> <div className="inline-flex items-center justify-center w-16 h-16 rounded-full bg-muted mb-4">
<Mail className="h-8 w-8 text-[#664fa3]" /> <Mail className="h-8 w-8 text-muted-foreground" />
</div> </div>
<h1 className="text-4xl md:text-5xl font-semibold text-[#422268] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}> <h1 className="text-4xl md:text-5xl font-semibold text-primary mb-4" style={{ fontFamily: "'Inter', sans-serif" }}>
Forgot Password? Forgot Password?
</h1> </h1>
<p className="text-lg text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-lg text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
No worries! Enter your email and we'll send you reset instructions. No worries! Enter your email and we'll send you reset instructions.
</p> </p>
</div> </div>
@@ -69,22 +69,22 @@ const ForgotPassword = () => {
value={email} value={email}
onChange={(e) => setEmail(e.target.value)} onChange={(e) => setEmail(e.target.value)}
placeholder="your.email@example.com" placeholder="your.email@example.com"
className="h-14 rounded-xl border-2 border-[#ddd8eb] focus:border-[#664fa3]" className="h-14 rounded-xl border-2 border-chart-6 focus:border-muted-foreground"
/> />
</div> </div>
<Button <Button
type="submit" type="submit"
disabled={loading} disabled={loading}
className="w-full bg-[#DDD8EB] text-[#422268] hover:bg-white rounded-full py-6 text-lg font-medium shadow-lg hover:scale-105 transition-transform disabled:opacity-50" className="w-full bg-chart-6 text-primary hover:bg-background rounded-full py-6 text-lg font-medium shadow-lg hover:scale-105 transition-transform disabled:opacity-50"
> >
{loading ? 'Sending...' : 'Send Reset Link'} {loading ? 'Sending...' : 'Send Reset Link'}
<ArrowRight className="ml-2 h-5 w-5" /> <ArrowRight className="ml-2 h-5 w-5" />
</Button> </Button>
<p className="text-center text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-center text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Remember your password?{' '} Remember your password?{' '}
<Link to="/login" className="text-[#ff9e77] hover:underline font-medium"> <Link to="/login" className="text-accent hover:underline font-medium">
Login here Login here
</Link> </Link>
</p> </p>
@@ -95,18 +95,18 @@ const ForgotPassword = () => {
<div className="inline-flex items-center justify-center w-16 h-16 rounded-full bg-[#E8F5E9] mb-6"> <div className="inline-flex items-center justify-center w-16 h-16 rounded-full bg-[#E8F5E9] mb-6">
<CheckCircle className="h-8 w-8 text-[#4CAF50]" /> <CheckCircle className="h-8 w-8 text-[#4CAF50]" />
</div> </div>
<h1 className="text-4xl md:text-5xl font-semibold text-[#422268] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}> <h1 className="text-4xl md:text-5xl font-semibold text-primary mb-4" style={{ fontFamily: "'Inter', sans-serif" }}>
Check Your Email Check Your Email
</h1> </h1>
<p className="text-lg text-[#664fa3] mb-8" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-lg text-muted-foreground mb-8" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
If an account exists for <span className="font-medium text-[#422268]">{email}</span>, If an account exists for <span className="font-medium text-primary">{email}</span>,
you will receive a password reset link shortly. you will receive a password reset link shortly.
</p> </p>
<p className="text-sm text-[#664fa3] mb-8" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-sm text-muted-foreground mb-8" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
The link will expire in 1 hour. If you don't see the email, check your spam folder. The link will expire in 1 hour. If you don't see the email, check your spam folder.
</p> </p>
<Link to="/login"> <Link to="/login">
<Button className="bg-[#DDD8EB] text-[#422268] hover:bg-white rounded-full px-8 py-6 text-lg font-medium shadow-lg hover:scale-105 transition-transform"> <Button className="bg-chart-6 text-primary hover:bg-background rounded-full px-8 py-6 text-lg font-medium shadow-lg hover:scale-105 transition-transform">
Return to Login Return to Login
<ArrowRight className="ml-2 h-5 w-5" /> <ArrowRight className="ml-2 h-5 w-5" />
</Button> </Button>

View File

@@ -9,7 +9,7 @@ import { LuArrowDown } from "react-icons/lu";
const CardSection = ({ children, className = '', arrow = true }) => ( const CardSection = ({ children, className = '', arrow = true }) => (
<section className={` ${className}`}> <section className={` ${className}`}>
<div className="max-w-7xl mx-auto"> <div className="max-w-7xl mx-auto">
<Card className="p-14 bg-white rounded-3xl"> <Card className="p-14 bg-background rounded-3xl">
{children} {children}
</Card> </Card>
</div> </div>
@@ -43,7 +43,7 @@ const History = () => {
return ( return (
<div className="min-h-screen bg-white"> <div className="min-h-screen bg-background">
<PublicNavbar /> <PublicNavbar />
<main className="bg-gradient-to-br from-[#F9FAFB] to-[#DCD7EA] px-4 sm:px-6 md:px-8 lg:px-12 xl:px-16 py-8 sm:py-10 md:py-12"> <main className="bg-gradient-to-br from-[#F9FAFB] to-[#DCD7EA] px-4 sm:px-6 md:px-8 lg:px-12 xl:px-16 py-8 sm:py-10 md:py-12">
@@ -246,7 +246,7 @@ const History = () => {
<section className="py-20 bg-[#48286e] mx-0"> <section className="py-20 bg-[#48286e] mx-0">
<div className="max-w-7xl mx-auto px-8"> <div className="max-w-7xl mx-auto px-8">
<div className="flex gap-8 md:flex-row flex-col"> <div className="flex gap-8 md:flex-row flex-col">
<Card className="p-8 text-center bg-white rounded-2xl shadow-lg hover:shadow-xl transition-shadow"> <Card className="p-8 text-center bg-background rounded-2xl shadow-lg hover:shadow-xl transition-shadow">
<h3 className="text-2xl font-bold text-[#48286e] mb-4" style={{ fontFamily: "'Poppins', sans-serif" }}> <h3 className="text-2xl font-bold text-[#48286e] mb-4" style={{ fontFamily: "'Poppins', sans-serif" }}>
A Life Remembered A Life Remembered
</h3> </h3>
@@ -254,13 +254,13 @@ const History = () => {
Check out "A Life Remembered", a tribute dedicated to Arden Eversmeyer, one of the founding mothers of LOAF. Check out "A Life Remembered", a tribute dedicated to Arden Eversmeyer, one of the founding mothers of LOAF.
</p> </p>
<a href="https://www.oldlesbianhistory.org/arden-eversmeyer" target="_blank" rel="noopener noreferrer"> <a href="https://www.oldlesbianhistory.org/arden-eversmeyer" target="_blank" rel="noopener noreferrer">
<Button className="bg-[#664fa3] hover:bg-[#48286e] text-white rounded-full px-6 py-3"> <Button className="bg-muted-foreground hover:bg-[#48286e] text-white rounded-full px-6 py-3">
View Arden's Tribute View Arden's Tribute
</Button> </Button>
</a> </a>
</Card> </Card>
<Card className="p-8 text-center bg-white rounded-2xl shadow-lg hover:shadow-xl transition-shadow"> <Card className="p-8 text-center bg-background rounded-2xl shadow-lg hover:shadow-xl transition-shadow">
<h3 className="text-2xl font-bold text-[#48286e] mb-4" style={{ fontFamily: "'Poppins', sans-serif" }}> <h3 className="text-2xl font-bold text-[#48286e] mb-4" style={{ fontFamily: "'Poppins', sans-serif" }}>
The Old Lesbian Oral Herstory Project The Old Lesbian Oral Herstory Project
</h3> </h3>
@@ -268,7 +268,7 @@ const History = () => {
Arden Eversmeyer was also involved with The Old Lesbian Oral Herstory Project, preserving the stories of old lesbians. Arden Eversmeyer was also involved with The Old Lesbian Oral Herstory Project, preserving the stories of old lesbians.
</p> </p>
<a href="https://www.olohp.org" target="_blank" rel="noopener noreferrer"> <a href="https://www.olohp.org" target="_blank" rel="noopener noreferrer">
<Button className="bg-[#664fa3] hover:bg-[#48286e] text-white rounded-full px-6 py-3"> <Button className="bg-muted-foreground hover:bg-[#48286e] text-white rounded-full px-6 py-3">
Learn More About OLOHP Learn More About OLOHP
</Button> </Button>
</a> </a>

View File

@@ -15,7 +15,7 @@ const Landing = () => {
const heroLoaf = `${process.env.PUBLIC_URL}/hero-loaf.png`; const heroLoaf = `${process.env.PUBLIC_URL}/hero-loaf.png`;
const friendships = `${process.env.PUBLIC_URL}/friendships.png`; const friendships = `${process.env.PUBLIC_URL}/friendships.png`;
const InfoCard = ({ iconSrc, infoTitle, description }) => ( const InfoCard = ({ iconSrc, infoTitle, description }) => (
<Card className="relative bg-white rounded-2xl overflow-visible flex flex-col gap-3.5 items-center pt-16 pb-0 w-full max-w-none lg:max-w-[363px]"> <Card className="relative bg-background rounded-2xl overflow-visible flex flex-col gap-3.5 items-center pt-16 pb-0 w-full max-w-none lg:max-w-[363px]">
<div className="absolute -top-20 md:-top-40 flex justify-center w-full"> <div className="absolute -top-20 md:-top-40 flex justify-center w-full">
<img <img
src={iconSrc} src={iconSrc}
@@ -71,17 +71,17 @@ const Landing = () => {
]; ];
return ( return (
<div className="min-h-screen bg-white"> <div className="min-h-screen bg-background">
<PublicNavbar /> <PublicNavbar />
{/* Hero Section */} {/* Hero Section */}
<section className="relative bg-gradient-to-b from-[#48286e] to-[#664fa3] py-20 sm:py-8 md:py-12 lg:py-16 flex flex-col lg:flex-row gap-8 md:gap-12 lg:gap-16 items-center justify-center w-full"> <section className="relative bg-gradient-to-b from-[#48286e] to-muted-foreground py-20 sm:py-8 md:py-12 lg:py-16 flex flex-col lg:flex-row gap-8 md:gap-12 lg:gap-16 items-center justify-center w-full">
{/* Friendships background image */} {/* Friendships background image */}
<div className="absolute inset-0 z-0 flex overflow-hidden top-[-32rem] lg:-top-32"> <div className="absolute inset-0 z-0 flex overflow-hidden top-[-32rem] lg:-top-32">
<img src={friendships} alt="Friendships" className="lg:max-w-screen opacity-15 max-w-full max-h-full object-contain" /> <img src={friendships} alt="Friendships" className="lg:max-w-screen opacity-15 max-w-full max-h-full object-contain" />
</div> </div>
{/* Blur Overlay */} {/* Blur Overlay */}
<div className="absolute inset-0 z-[1] bg-white/5 backdrop-blur-xs"></div> <div className="absolute inset-0 z-[1] bg-background/5 backdrop-blur-xs"></div>
{/* Left column Loaf Image */} {/* Left column Loaf Image */}
<div className="relative z-10 lg:py-20 py-7 flex flex-col gap-6 sm:gap-8 items-center justify-center w-full lg:w-[530px] lg:flex-shrink-0"> <div className="relative z-10 lg:py-20 py-7 flex flex-col gap-6 sm:gap-8 items-center justify-center w-full lg:w-[530px] lg:flex-shrink-0">
<div className="flex flex-col gap-6 items-center"> <div className="flex flex-col gap-6 items-center">
@@ -89,7 +89,7 @@ const Landing = () => {
</div> </div>
<div className="flex flex-col gap-4 items-center justify-center w-full max-w-[339px]"> <div className="flex flex-col gap-4 items-center justify-center w-full max-w-[339px]">
<Link to="/become-a-member" className="w-full"> <Link to="/become-a-member" className="w-full">
<Button style={{ fontFamily: "'Nunito sans', sans-serif" }} className="bg-[#DDD8EB] hover:bg-white text-[#422268] rounded-full px-6 py-6 sm:py-[32px] text-base sm:text-lg font-medium w-full transition-colors"> <Button style={{ fontFamily: "'Nunito sans', sans-serif" }} className="bg-chart-6 hover:bg-background text-primary rounded-full px-6 py-6 sm:py-[32px] text-base sm:text-lg font-medium w-full transition-colors">
Become a Member Become a Member
</Button> </Button>
</Link> </Link>
@@ -105,7 +105,7 @@ const Landing = () => {
</section> </section>
{/* About Section */} {/* About Section */}
<section id="about" className="bg-gradient-to-b pb-10 lg:pb-44 from-white to-[#f1eef9] px-4 sm:px-6 md:px-8 lg:px-12 xl:px-16 pt-4 sm:pt-16 md:pt-20 lg:pt-30 flex flex-col"> <section id="about" className="bg-gradient-to-b pb-10 lg:pb-44 from-white to-muted px-4 sm:px-6 md:px-8 lg:px-12 xl:px-16 pt-4 sm:pt-16 md:pt-20 lg:pt-30 flex flex-col">
<div className="flex flex-col items-center pt-4"> <div className="flex flex-col items-center pt-4">
<h3 className="text-[#48286e] px-4 pb-6 md:py-8 text-4xl leading-[60px] md:text-5xl lg:text-6xl font-extrabold text-center" style={{ fontFamily: "'Inter', sans-serif" }}> <h3 className="text-[#48286e] px-4 pb-6 md:py-8 text-4xl leading-[60px] md:text-5xl lg:text-6xl font-extrabold text-center" style={{ fontFamily: "'Inter', sans-serif" }}>
Welcome to LOAF Welcome to LOAF
@@ -118,7 +118,7 @@ const Landing = () => {
</section> </section>
{/* Feature Cards Section */} {/* Feature Cards Section */}
<section className="bg-gradient-to-b pb-20 from-[#f1eef9] to-[#ddd8eb] px-4 sm:px-6 md:px-8 lg:px-12 xl:px-16 py-4 md:py-20 lg:py-30 flex flex-col sm:w-full lg:flex-row gap-40 md:gap-64 lg:gap-8 items-stretch justify-center"> <section className="bg-gradient-to-b pb-20 from-muted to-chart-6 px-4 sm:px-6 md:px-8 lg:px-12 xl:px-16 py-4 md:py-20 lg:py-30 flex flex-col sm:w-full lg:flex-row gap-40 md:gap-64 lg:gap-8 items-stretch justify-center">
{infoCardData.map((card) => ( {infoCardData.map((card) => (
<InfoCard key={card.infoTitle} {...card} /> <InfoCard key={card.infoTitle} {...card} />
))} ))}
@@ -128,7 +128,7 @@ const Landing = () => {
<section className="bg-gradient-to-b from-[#644c9f] to-[#48286e] px-4 sm:px-6 md:px-8 lg:px-12 xl:px-16 py-12 sm:py-16 md:py-20 lg:py-30 flex items-center justify-center"> <section className="bg-gradient-to-b from-[#644c9f] to-[#48286e] px-4 sm:px-6 md:px-8 lg:px-12 xl:px-16 py-12 sm:py-16 md:py-20 lg:py-30 flex items-center justify-center">
<div className="flex flex-col-reverse md:flex-col lg:flex-row gap-8 sm:gap-10 md:gap-12 items-center justify-center w-full max-w-6xl"> <div className="flex flex-col-reverse md:flex-col lg:flex-row gap-8 sm:gap-10 md:gap-12 items-center justify-center w-full max-w-6xl">
<Link to="/register" className="w-full sm:w-auto flex items-center justify-center"> <Link to="/register" className="w-full sm:w-auto flex items-center justify-center">
<Button className="bg-[#DDD8EB] hover:bg-white text-[#422268] rounded-full <Button className="bg-chart-6 hover:bg-background text-primary rounded-full
py-8 text-xl font-normal px-12 sm:w-[392px] transition-colors "> py-8 text-xl font-normal px-12 sm:w-[392px] transition-colors ">
Become a Member Become a Member
</Button> </Button>

View File

@@ -55,23 +55,23 @@ const Login = () => {
}; };
return ( return (
<div className="min-h-screen bg-white"> <div className="min-h-screen bg-background">
<PublicNavbar /> <PublicNavbar />
<div className="max-w-md mx-auto px-6 py-12"> <div className="max-w-md mx-auto px-6 py-12">
<div className="mb-8"> <div className="mb-8">
<Link to="/" className="inline-flex items-center text-[#664fa3] hover:text-[#ff9e77] transition-colors"> <Link to="/" className="inline-flex items-center text-muted-foreground hover:text-accent transition-colors">
<ArrowLeft className="h-4 w-4 mr-2" /> <ArrowLeft className="h-4 w-4 mr-2" />
Back to Home Back to Home
</Link> </Link>
</div> </div>
<Card className="p-8 md:p-12 bg-white rounded-2xl border border-[#ddd8eb] shadow-lg"> <Card className="p-8 md:p-12 bg-background rounded-2xl border border-chart-6 shadow-lg">
<div className="mb-8 text-center"> <div className="mb-8 text-center">
<h1 className="text-4xl md:text-5xl font-semibold text-[#422268] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}> <h1 className="text-4xl md:text-5xl font-semibold text-primary mb-4" style={{ fontFamily: "'Inter', sans-serif" }}>
Welcome Back Welcome Back
</h1> </h1>
<p className="text-lg text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-lg text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Login to access your member dashboard. Login to access your member dashboard.
</p> </p>
</div> </div>
@@ -87,7 +87,7 @@ const Login = () => {
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-[#ddd8eb] focus:border-[#664fa3]" className="h-14 rounded-xl border-2 border-chart-6 focus:border-muted-foreground"
data-testid="login-email-input" data-testid="login-email-input"
/> />
</div> </div>
@@ -95,7 +95,7 @@ const Login = () => {
<div> <div>
<div className="flex items-center justify-between mb-2"> <div className="flex items-center justify-between mb-2">
<Label htmlFor="password">Password</Label> <Label htmlFor="password">Password</Label>
<Link to="/forgot-password" className="text-sm text-[#ff9e77] hover:underline"> <Link to="/forgot-password" className="text-sm text-accent hover:underline">
Forgot password? Forgot password?
</Link> </Link>
</div> </div>
@@ -106,7 +106,7 @@ const Login = () => {
value={formData.password} value={formData.password}
onChange={handleInputChange} onChange={handleInputChange}
placeholder="Enter your password" placeholder="Enter your password"
className="h-14 rounded-xl border-2 border-[#ddd8eb] focus:border-[#664fa3]" className="h-14 rounded-xl border-2 border-chart-6 focus:border-muted-foreground"
data-testid="login-password-input" data-testid="login-password-input"
/> />
</div> </div>
@@ -114,16 +114,16 @@ const Login = () => {
<Button <Button
type="submit" type="submit"
disabled={loading} disabled={loading}
className="w-full bg-[#DDD8EB] text-[#422268] hover:bg-white rounded-full py-6 text-lg font-medium shadow-lg hover:scale-105 transition-transform disabled:opacity-50" className="w-full bg-chart-6 text-primary hover:bg-background rounded-full py-6 text-lg font-medium shadow-lg hover:scale-105 transition-transform disabled:opacity-50"
data-testid="login-submit-button" data-testid="login-submit-button"
> >
{loading ? 'Logging in...' : 'Login'} {loading ? 'Logging in...' : 'Login'}
<ArrowRight className="ml-2 h-5 w-5" /> <ArrowRight className="ml-2 h-5 w-5" />
</Button> </Button>
<p className="text-center text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-center text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Don't have an account?{' '} Don't have an account?{' '}
<Link to="/register" className="text-[#ff9e77] hover:underline font-medium"> <Link to="/register" className="text-accent hover:underline font-medium">
Register here Register here
</Link> </Link>
</p> </p>

View File

@@ -7,14 +7,14 @@ const MissionValues = () => {
const loafLogo = `${process.env.PUBLIC_URL}/loaf-logo.png`; const loafLogo = `${process.env.PUBLIC_URL}/loaf-logo.png`;
return ( return (
<div className="min-h-screen bg-white"> <div className="min-h-screen bg-background">
<PublicNavbar /> <PublicNavbar />
<main className="bg-gradient-to-b from-[#f9fafb] to-[#ddd8eb] px-4 sm:px-6 py-8 sm:py-12 md:py-20"> <main className="bg-gradient-to-b from-[#f9fafb] to-chart-6 px-4 sm:px-6 py-8 sm:py-12 md:py-20">
<div className="max-w-[1400px] mx-auto"> <div className="max-w-[1400px] mx-auto">
<div className="flex md:flex-row flex-col gap-10 items-stretch"> <div className="flex md:flex-row flex-col gap-10 items-stretch">
{/* Left Card - Mission (Purple Gradient) */} {/* Left Card - Mission (Purple Gradient) */}
<Card className=" bg-gradient-to-br from-[#664fa3] to-[#48286e] p-16 rounded-2xl shadow-lg flex flex-col items-center justify-between flex-1 w-full md:w-1/2 "> <Card className=" bg-gradient-to-br from-muted-foreground to-[#48286e] p-16 rounded-2xl shadow-lg flex flex-col items-center justify-between flex-1 w-full md:w-1/2 ">
<h2 className="text-2xl sm:text-3xl md:text-4xl font-bold text-white text-center mb-6" <h2 className="text-2xl sm:text-3xl md:text-4xl font-bold text-white text-center mb-6"
style={{ fontFamily: "'Poppins', sans-serif" }}> style={{ fontFamily: "'Poppins', sans-serif" }}>
LOAF Mission LOAF Mission
@@ -29,7 +29,7 @@ const MissionValues = () => {
</Card> </Card>
{/* Right Card - Values */} {/* Right Card - Values */}
<Card className="bg-white p-16 rounded-2xl shadow-lg flex-1 w-full md:w-1/2 "> <Card className="bg-background p-16 rounded-2xl shadow-lg flex-1 w-full md:w-1/2 ">
<h2 className="text-2xl sm:text-3xl md:text-4xl font-bold text-[#48286e] text-center mb-6" <h2 className="text-2xl sm:text-3xl md:text-4xl font-bold text-[#48286e] text-center mb-6"
style={{ fontFamily: "'Poppins', sans-serif" }}> style={{ fontFamily: "'Poppins', sans-serif" }}>
LOAF Values LOAF Values

View File

@@ -9,31 +9,31 @@ const NotFound = () => {
return ( return (
<div className="min-h-screen bg-gradient-to-br from-[#F9F8FB] to-white flex items-center justify-center p-4"> <div className="min-h-screen bg-gradient-to-br from-[#F9F8FB] to-white flex items-center justify-center p-4">
<Card className="w-full max-w-2xl p-12 bg-white rounded-2xl border border-[#ddd8eb] text-center"> <Card className="w-full max-w-2xl p-12 bg-background rounded-2xl border border-chart-6 text-center">
{/* 404 Illustration */} {/* 404 Illustration */}
<div className="mb-8"> <div className="mb-8">
<div className="relative"> <div className="relative">
<h1 <h1
className="text-[180px] font-bold text-transparent bg-clip-text bg-gradient-to-br from-[#ddd8eb] to-[#f9f8fb] leading-none" className="text-[180px] font-bold text-transparent bg-clip-text bg-gradient-to-br from-chart-6 to-[#f9f8fb] leading-none"
style={{ fontFamily: "'Inter', sans-serif" }} style={{ fontFamily: "'Inter', sans-serif" }}
> >
404 404
</h1> </h1>
<div className="absolute inset-0 flex items-center justify-center"> <div className="absolute inset-0 flex items-center justify-center">
<Search className="h-24 w-24 text-[#664fa3] opacity-30" /> <Search className="h-24 w-24 text-muted-foreground opacity-30" />
</div> </div>
</div> </div>
</div> </div>
{/* Message */} {/* Message */}
<h2 <h2
className="text-3xl font-semibold text-[#422268] mb-4" className="text-3xl font-semibold text-primary mb-4"
style={{ fontFamily: "'Inter', sans-serif" }} style={{ fontFamily: "'Inter', sans-serif" }}
> >
Page Not Found Page Not Found
</h2> </h2>
<p <p
className="text-lg text-[#664fa3] mb-8 max-w-md mx-auto" className="text-lg text-muted-foreground mb-8 max-w-md mx-auto"
style={{ fontFamily: "'Nunito Sans', sans-serif" }} style={{ fontFamily: "'Nunito Sans', sans-serif" }}
> >
Oops! The page you're looking for doesn't exist. It might have been moved or deleted. Oops! The page you're looking for doesn't exist. It might have been moved or deleted.
@@ -44,14 +44,14 @@ const NotFound = () => {
<Button <Button
onClick={() => navigate(-1)} onClick={() => navigate(-1)}
variant="outline" variant="outline"
className="rounded-xl border-2 border-[#664fa3] text-[#664fa3] hover:bg-[#f9f8fb] px-6 py-6" className="rounded-xl border-2 border-muted-foreground text-muted-foreground hover:bg-[#f9f8fb] px-6 py-6"
> >
<ArrowLeft className="h-5 w-5 mr-2" /> <ArrowLeft className="h-5 w-5 mr-2" />
Go Back Go Back
</Button> </Button>
<Button <Button
onClick={() => navigate('/')} onClick={() => navigate('/')}
className="rounded-xl bg-gradient-to-r from-[#664fa3] to-[#422268] hover:from-[#422268] hover:to-[#664fa3] text-white px-6 py-6" className="rounded-xl bg-gradient-to-r from-muted-foreground to-primary hover:from-primary hover:to-muted-foreground text-white px-6 py-6"
> >
<Home className="h-5 w-5 mr-2" /> <Home className="h-5 w-5 mr-2" />
Back to Home Back to Home
@@ -59,15 +59,15 @@ const NotFound = () => {
</div> </div>
{/* Help Text */} {/* Help Text */}
<div className="mt-8 pt-8 border-t border-[#ddd8eb]"> <div className="mt-8 pt-8 border-t border-chart-6">
<p <p
className="text-sm text-[#664fa3]" className="text-sm text-muted-foreground"
style={{ fontFamily: "'Nunito Sans', sans-serif" }} style={{ fontFamily: "'Nunito Sans', sans-serif" }}
> >
Need help? Contact us at{' '} Need help? Contact us at{' '}
<a <a
href="mailto:support@loaftx.org" href="mailto:support@loaftx.org"
className="text-[#664fa3] hover:text-[#422268] font-semibold underline" className="text-muted-foreground hover:text-primary font-semibold underline"
> >
support@loaftx.org support@loaftx.org
</a> </a>

View File

@@ -9,7 +9,7 @@ const PaymentCancel = () => {
const navigate = useNavigate(); const navigate = useNavigate();
return ( return (
<div className="min-h-screen bg-white"> <div className="min-h-screen bg-background">
<Navbar /> <Navbar />
<div className="max-w-4xl mx-auto px-6 py-12"> <div className="max-w-4xl mx-auto px-6 py-12">
@@ -22,48 +22,48 @@ const PaymentCancel = () => {
</div> </div>
{/* Cancel Message */} {/* Cancel Message */}
<h1 className="text-4xl md:text-5xl font-semibold text-[#422268] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}> <h1 className="text-4xl md:text-5xl font-semibold text-primary mb-4" style={{ fontFamily: "'Inter', sans-serif" }}>
Payment Cancelled Payment Cancelled
</h1> </h1>
<p className="text-lg text-[#664fa3] max-w-2xl mx-auto mb-8" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-lg text-muted-foreground 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-[#ddd8eb] shadow-lg mb-8"> <Card className="p-8 bg-background rounded-2xl border border-chart-6 shadow-lg mb-8">
<h2 className="text-2xl font-semibold text-[#422268] mb-6 text-center" style={{ fontFamily: "'Inter', sans-serif" }}> <h2 className="text-2xl font-semibold text-primary 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-[#664fa3] text-center" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-muted-foreground 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-[#DDD8EB]/20 p-6 rounded-xl"> <div className="bg-chart-6/20 p-6 rounded-xl">
<h3 className="text-lg font-semibold text-[#422268] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}> <h3 className="text-lg font-semibold text-primary 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-[#664fa3] flex-shrink-0 mt-0.5" /> <CreditCard className="h-5 w-5 text-muted-foreground flex-shrink-0 mt-0.5" />
<span className="text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <span className="text-muted-foreground" 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-[#664fa3] flex-shrink-0 mt-0.5" /> <Mail className="h-5 w-5 text-muted-foreground flex-shrink-0 mt-0.5" />
<span className="text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <span className="text-muted-foreground" 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-[#f1eef9] p-6 rounded-xl"> <div className="bg-muted p-6 rounded-xl">
<p className="text-sm text-[#664fa3] text-center mb-4" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-sm text-muted-foreground text-center mb-4" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<span className="font-medium text-[#422268]">Note:</span>{' '} <span className="font-medium text-primary">Note:</span>{' '}
Your membership application is still validated. You can complete payment whenever you're ready. Your membership application is still validated. 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-[#DDD8EB] text-[#422268] hover:bg-white rounded-full px-8 py-6 text-lg font-semibold" className="bg-chart-6 text-primary hover:bg-background 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-[#664fa3] text-[#664fa3] hover:bg-[#664fa3] hover:text-white rounded-full px-8 py-6 text-lg font-semibold" className="border-2 border-muted-foreground text-muted-foreground hover:bg-muted-foreground 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-[#DDD8EB]/20 to-[#f1eef9]/20 rounded-2xl border border-[#ddd8eb]"> <Card className="p-6 bg-gradient-to-br from-chart-6/20 to-muted/20 rounded-2xl border border-chart-6">
<h3 className="text-lg font-semibold text-[#422268] mb-3 text-center" style={{ fontFamily: "'Inter', sans-serif" }}> <h3 className="text-lg font-semibold text-primary mb-3 text-center" style={{ fontFamily: "'Inter', sans-serif" }}>
Need Assistance? Need Assistance?
</h3> </h3>
<p className="text-[#664fa3] text-center mb-4" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-muted-foreground 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-[#ff9e77] hover:text-[#664fa3] font-medium text-lg" className="text-accent hover:text-muted-foreground 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-white"> <div className="min-h-screen bg-background">
<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 text-[#422268] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}> <h1 className="text-4xl md:text-5xl font-semibold text-primary mb-4" style={{ fontFamily: "'Inter', sans-serif" }}>
Payment Successful! Payment Successful!
</h1> </h1>
<p className="text-lg text-[#664fa3] max-w-2xl mx-auto mb-8" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-lg text-muted-foreground 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-[#ddd8eb] shadow-lg mb-8"> <Card className="p-8 bg-background rounded-2xl border border-chart-6 shadow-lg mb-8">
<h2 className="text-2xl font-semibold text-[#422268] mb-6 text-center" style={{ fontFamily: "'Inter', sans-serif" }}> <h2 className="text-2xl font-semibold text-primary 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-[#f1eef9] p-6 rounded-xl"> <div className="bg-muted p-6 rounded-xl">
<h3 className="text-lg font-semibold text-[#422268] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}> <h3 className="text-lg font-semibold text-primary 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-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <span className="text-muted-foreground" 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-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <span className="text-muted-foreground" 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-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <span className="text-muted-foreground" 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-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <span className="text-muted-foreground" 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-[#DDD8EB]/20 p-4 rounded-xl"> <div className="bg-chart-6/20 p-4 rounded-xl">
<p className="text-sm text-[#664fa3] text-center" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-sm text-muted-foreground text-center" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<span className="font-medium text-[#422268]">Transaction ID:</span>{' '} <span className="font-medium text-primary">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-[#664fa3] text-center mt-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-xs text-muted-foreground 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-[#DDD8EB] text-[#422268] hover:bg-white rounded-full px-8 py-6 text-lg font-semibold" className="bg-chart-6 text-primary hover:bg-background 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-[#664fa3] text-[#664fa3] hover:bg-[#664fa3] hover:text-white rounded-full px-8 py-6 text-lg font-semibold" className="border-2 border-muted-foreground text-muted-foreground hover:bg-muted-foreground 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-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-sm text-muted-foreground" 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-[#ff9e77] hover:text-[#664fa3] font-medium" className="text-accent hover:text-muted-foreground font-medium"
> >
support@loaf.org support@loaf.org
</a> </a>

View File

@@ -208,36 +208,36 @@ const Plans = () => {
const breakdown = getAmountBreakdown(); const breakdown = getAmountBreakdown();
return ( return (
<div className="min-h-screen bg-white"> <div className="min-h-screen bg-background">
<Navbar /> <Navbar />
<div className="max-w-7xl mx-auto px-6 py-12"> <div className="max-w-7xl mx-auto px-6 py-12">
{/* Header */} {/* Header */}
<div className="mb-12 text-center"> <div className="mb-12 text-center">
<h1 className="text-4xl md:text-5xl font-semibold text-[#422268] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}> <h1 className="text-4xl md:text-5xl font-semibold text-primary mb-4" style={{ fontFamily: "'Inter', sans-serif" }}>
Membership Plans Membership Plans
</h1> </h1>
<p className="text-lg text-[#664fa3] max-w-2xl mx-auto" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-lg text-muted-foreground max-w-2xl mx-auto" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Choose the membership plan that works best for you and become part of our vibrant community. Choose the membership plan that works best for you and become part of our vibrant community.
</p> </p>
</div> </div>
{/* Status Banner */} {/* Status Banner */}
{statusInfo && statusInfo.title && ( {statusInfo && statusInfo.title && (
<Card className="max-w-3xl mx-auto mb-8 p-6 bg-gradient-to-r from-[#f1eef9] to-[#DDD8EB]/30 border-2 border-[#664fa3]"> <Card className="max-w-3xl mx-auto mb-8 p-6 bg-gradient-to-r from-muted to-chart-6/30 border-2 border-muted-foreground">
<div className="flex items-start gap-4"> <div className="flex items-start gap-4">
<AlertCircle className="h-6 w-6 text-[#664fa3] flex-shrink-0 mt-1" /> <AlertCircle className="h-6 w-6 text-muted-foreground flex-shrink-0 mt-1" />
<div className="flex-1"> <div className="flex-1">
<h3 className="text-xl font-semibold text-[#422268] mb-2" style={{ fontFamily: "'Inter', sans-serif" }}> <h3 className="text-xl font-semibold text-primary mb-2" style={{ fontFamily: "'Inter', sans-serif" }}>
{statusInfo.title} {statusInfo.title}
</h3> </h3>
<p className="text-[#664fa3] mb-4" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-muted-foreground mb-4" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{statusInfo.message} {statusInfo.message}
</p> </p>
{statusInfo.action && statusInfo.actionLink && ( {statusInfo.action && statusInfo.actionLink && (
<Button <Button
onClick={() => navigate(statusInfo.actionLink)} onClick={() => navigate(statusInfo.actionLink)}
className="bg-[#664fa3] text-white hover:bg-[#422268] rounded-full" className="bg-muted-foreground text-white hover:bg-primary rounded-full"
> >
{statusInfo.action} {statusInfo.action}
</Button> </Button>
@@ -249,17 +249,16 @@ const Plans = () => {
{loading ? ( {loading ? (
<div className="text-center py-20"> <div className="text-center py-20">
<Loader2 className="h-12 w-12 text-[#664fa3] mx-auto mb-4 animate-spin" /> <Loader2 className="h-12 w-12 text-muted-foreground mx-auto mb-4 animate-spin" />
<p className="text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Loading plans...</p> <p className="text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Loading plans...</p>
</div> </div>
) : plans.length > 0 ? ( ) : plans.length > 0 ? (
<div className={`grid gap-6 sm:gap-8 mx-auto ${ <div className={`grid gap-6 sm:gap-8 mx-auto ${plans.length === 1
plans.length === 1 ? 'grid-cols-1 max-w-md'
? 'grid-cols-1 max-w-md' : plans.length === 2
: plans.length === 2
? 'grid-cols-1 sm:grid-cols-2 max-w-3xl' ? 'grid-cols-1 sm:grid-cols-2 max-w-3xl'
: 'grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 max-w-5xl' : 'grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 max-w-5xl'
}`}> }`}>
{plans.map((plan) => { {plans.map((plan) => {
const minimumPrice = plan.minimum_price_cents || plan.price_cents || 3000; const minimumPrice = plan.minimum_price_cents || plan.price_cents || 3000;
const suggestedPrice = plan.suggested_price_cents || minimumPrice; const suggestedPrice = plan.suggested_price_cents || minimumPrice;
@@ -267,19 +266,19 @@ const Plans = () => {
return ( return (
<Card <Card
key={plan.id} key={plan.id}
className="p-8 bg-white rounded-2xl border-2 border-[#ddd8eb] hover:border-[#664fa3] hover:shadow-xl transition-all" className="p-8 bg-background rounded-2xl border-2 border-chart-6 hover:border-muted-foreground hover:shadow-xl transition-all"
data-testid={`plan-card-${plan.id}`} data-testid={`plan-card-${plan.id}`}
> >
{/* Plan Header */} {/* Plan Header */}
<div className="text-center mb-6"> <div className="text-center mb-6">
<div className="bg-[#DDD8EB]/20 p-4 rounded-full w-16 h-16 mx-auto mb-4 flex items-center justify-center"> <div className="bg-chart-6/20 p-4 rounded-full w-16 h-16 mx-auto mb-4 flex items-center justify-center">
<CreditCard className="h-8 w-8 text-[#664fa3]" /> <CreditCard className="h-8 w-8 text-muted-foreground" />
</div> </div>
<h2 className="text-2xl font-semibold text-[#422268] mb-2" style={{ fontFamily: "'Inter', sans-serif" }}> <h2 className="text-2xl font-semibold text-primary mb-2" style={{ fontFamily: "'Inter', sans-serif" }}>
{plan.name} {plan.name}
</h2> </h2>
{plan.description && ( {plan.description && (
<p className="text-sm text-[#664fa3] mb-4" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-sm text-muted-foreground mb-4" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{plan.description} {plan.description}
</p> </p>
)} )}
@@ -287,22 +286,22 @@ const Plans = () => {
{/* Pricing */} {/* Pricing */}
<div className="text-center mb-8"> <div className="text-center mb-8">
<div className="text-sm text-[#664fa3] mb-1" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <div className="text-sm text-muted-foreground mb-1" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Starting at Starting at
</div> </div>
<div className="text-2xl sm:text-3xl md:text-4xl font-bold text-[#422268] mb-2" style={{ fontFamily: "'Inter', sans-serif" }}> <div className="text-2xl sm:text-3xl md:text-4xl font-bold text-primary mb-2" style={{ fontFamily: "'Inter', sans-serif" }}>
{formatPrice(minimumPrice)} {formatPrice(minimumPrice)}
</div> </div>
{suggestedPrice > minimumPrice && ( {suggestedPrice > minimumPrice && (
<div className="text-sm text-[#664fa3] mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <div className="text-sm text-muted-foreground mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Suggested: {formatPrice(suggestedPrice)} Suggested: {formatPrice(suggestedPrice)}
</div> </div>
)} )}
<p className="text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{getBillingCycleLabel(plan.billing_cycle)} {getBillingCycleLabel(plan.billing_cycle)}
</p> </p>
{plan.allow_donation && ( {plan.allow_donation && (
<div className="mt-2 flex items-center justify-center gap-1 text-xs text-[#ff9e77]"> <div className="mt-2 flex items-center justify-center gap-1 text-xs text-accent">
<Heart className="h-3 w-3" /> <Heart className="h-3 w-3" />
<span>Donations welcome</span> <span>Donations welcome</span>
</div> </div>
@@ -313,19 +312,19 @@ const Plans = () => {
<div className="space-y-3 mb-8"> <div className="space-y-3 mb-8">
<div className="flex items-start gap-3"> <div 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-sm text-[#422268]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Access to all member events</span> <span className="text-sm text-primary" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Access to all member events</span>
</div> </div>
<div className="flex items-start gap-3"> <div 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-sm text-[#422268]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Community directory access</span> <span className="text-sm text-primary" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Community directory access</span>
</div> </div>
<div className="flex items-start gap-3"> <div 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-sm text-[#422268]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Exclusive member benefits</span> <span className="text-sm text-primary" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Exclusive member benefits</span>
</div> </div>
<div className="flex items-start gap-3"> <div 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-sm text-[#422268]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Newsletter subscription</span> <span className="text-sm text-primary" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Newsletter subscription</span>
</div> </div>
</div> </div>
@@ -333,7 +332,7 @@ const Plans = () => {
<Button <Button
onClick={() => handleSelectPlan(plan)} onClick={() => handleSelectPlan(plan)}
disabled={processingPlanId === plan.id || (statusInfo && !statusInfo.canSubscribe)} disabled={processingPlanId === plan.id || (statusInfo && !statusInfo.canSubscribe)}
className="w-full bg-[#DDD8EB] text-[#422268] hover:bg-white rounded-full py-6 text-lg font-semibold disabled:opacity-50 disabled:cursor-not-allowed" className="w-full bg-chart-6 text-primary hover:bg-background rounded-full py-6 text-lg font-semibold disabled:opacity-50 disabled:cursor-not-allowed"
data-testid={`subscribe-button-${plan.id}`} data-testid={`subscribe-button-${plan.id}`}
> >
{processingPlanId === plan.id ? ( {processingPlanId === plan.id ? (
@@ -353,11 +352,11 @@ const Plans = () => {
</div> </div>
) : ( ) : (
<div className="text-center py-20"> <div className="text-center py-20">
<CreditCard className="h-20 w-20 text-[#ddd8eb] mx-auto mb-6" /> <CreditCard className="h-20 w-20 text-chart-6 mx-auto mb-6" />
<h3 className="text-2xl font-semibold text-[#422268] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}> <h3 className="text-2xl font-semibold text-primary mb-4" style={{ fontFamily: "'Inter', sans-serif" }}>
No Plans Available No Plans Available
</h3> </h3>
<p className="text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Membership plans are not currently available. Please check back later! Membership plans are not currently available. Please check back later!
</p> </p>
</div> </div>
@@ -365,17 +364,17 @@ const Plans = () => {
{/* Info Section */} {/* Info Section */}
<div className="mt-16 max-w-3xl mx-auto"> <div className="mt-16 max-w-3xl mx-auto">
<Card className="p-8 bg-gradient-to-br from-[#DDD8EB]/20 to-[#f1eef9]/20 rounded-2xl border border-[#ddd8eb]"> <Card className="p-8 bg-gradient-to-br from-chart-6/20 to-muted/20 rounded-2xl border border-chart-6">
<h3 className="text-xl font-semibold text-[#422268] mb-4 text-center" style={{ fontFamily: "'Inter', sans-serif" }}> <h3 className="text-xl font-semibold text-primary mb-4 text-center" style={{ fontFamily: "'Inter', sans-serif" }}>
Need Help Choosing? Need Help Choosing?
</h3> </h3>
<p className="text-[#664fa3] text-center mb-4" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-muted-foreground text-center mb-4" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
If you have any questions about our membership plans or need assistance, please contact us. If you have any questions about our membership plans or need assistance, please contact us.
</p> </p>
<div className="text-center"> <div className="text-center">
<a <a
href="mailto:support@loaf.org" href="mailto:support@loaf.org"
className="text-[#ff9e77] hover:text-[#664fa3] font-medium" className="text-accent hover:text-muted-foreground font-medium"
> >
support@loaf.org support@loaf.org
</a> </a>
@@ -388,10 +387,10 @@ const Plans = () => {
<Dialog open={amountDialogOpen} onOpenChange={setAmountDialogOpen}> <Dialog open={amountDialogOpen} onOpenChange={setAmountDialogOpen}>
<DialogContent className="sm:max-w-[500px]"> <DialogContent className="sm:max-w-[500px]">
<DialogHeader> <DialogHeader>
<DialogTitle className="text-2xl text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}> <DialogTitle className="text-2xl text-primary" style={{ fontFamily: "'Inter', sans-serif" }}>
Choose Your Amount Choose Your Amount
</DialogTitle> </DialogTitle>
<DialogDescription className="text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <DialogDescription className="text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{selectedPlan?.name} - {getBillingCycleLabel(selectedPlan?.billing_cycle)} {selectedPlan?.name} - {getBillingCycleLabel(selectedPlan?.billing_cycle)}
</DialogDescription> </DialogDescription>
</DialogHeader> </DialogHeader>
@@ -399,11 +398,11 @@ const Plans = () => {
<div className="space-y-6"> <div className="space-y-6">
{/* Amount Input */} {/* Amount Input */}
<div> <div>
<Label htmlFor="amount" className="text-[#422268]"> <Label htmlFor="amount" className="text-primary">
Amount (USD) * Amount (USD) *
</Label> </Label>
<div className="relative mt-2"> <div className="relative mt-2">
<span className="absolute left-4 top-1/2 transform -translate-y-1/2 text-[#664fa3] text-lg font-semibold"> <span className="absolute left-4 top-1/2 transform -translate-y-1/2 text-muted-foreground text-lg font-semibold">
$ $
</span> </span>
<Input <Input
@@ -413,25 +412,25 @@ const Plans = () => {
min={selectedPlan ? (selectedPlan.minimum_price_cents / 100).toFixed(2) : "30.00"} min={selectedPlan ? (selectedPlan.minimum_price_cents / 100).toFixed(2) : "30.00"}
value={amountInput} value={amountInput}
onChange={(e) => setAmountInput(e.target.value)} onChange={(e) => setAmountInput(e.target.value)}
className="pl-8 h-14 text-xl border-2 border-[#ddd8eb] focus:border-[#664fa3]" className="pl-8 h-14 text-xl border-2 border-chart-6 focus:border-muted-foreground"
placeholder="50.00" placeholder="50.00"
/> />
</div> </div>
<p className="text-sm text-[#664fa3] mt-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-sm text-muted-foreground mt-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Minimum: {selectedPlan ? formatPrice(selectedPlan.minimum_price_cents || 3000) : '$30.00'} Minimum: {selectedPlan ? formatPrice(selectedPlan.minimum_price_cents || 3000) : '$30.00'}
</p> </p>
</div> </div>
{/* Breakdown Display */} {/* Breakdown Display */}
{breakdown && breakdown.total >= breakdown.base && ( {breakdown && breakdown.total >= breakdown.base && (
<Card className="p-4 bg-[#f9f5ff] border border-[#DDD8EB]"> <Card className="p-4 bg-[#f9f5ff] border border-chart-6">
<div className="space-y-2 text-sm" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <div className="space-y-2 text-sm" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<div className="flex justify-between text-[#422268]"> <div className="flex justify-between text-primary">
<span>Membership Fee:</span> <span>Membership Fee:</span>
<span className="font-semibold">{formatPrice(breakdown.base)}</span> <span className="font-semibold">{formatPrice(breakdown.base)}</span>
</div> </div>
{breakdown.donation > 0 && ( {breakdown.donation > 0 && (
<div className="flex justify-between text-[#ff9e77]"> <div className="flex justify-between text-accent">
<span className="flex items-center gap-1"> <span className="flex items-center gap-1">
<Heart className="h-4 w-4" /> <Heart className="h-4 w-4" />
Additional Donation: Additional Donation:
@@ -439,7 +438,7 @@ const Plans = () => {
<span className="font-semibold">{formatPrice(breakdown.donation)}</span> <span className="font-semibold">{formatPrice(breakdown.donation)}</span>
</div> </div>
)} )}
<div className="flex justify-between text-[#422268] font-bold text-base pt-2 border-t border-[#DDD8EB]"> <div className="flex justify-between text-primary font-bold text-base pt-2 border-t border-chart-6">
<span>Total:</span> <span>Total:</span>
<span>{formatPrice(breakdown.total)}</span> <span>{formatPrice(breakdown.total)}</span>
</div> </div>
@@ -449,8 +448,8 @@ const Plans = () => {
{/* Donation Message */} {/* Donation Message */}
{selectedPlan?.allow_donation && ( {selectedPlan?.allow_donation && (
<div className="bg-[#DDD8EB]/20 rounded-lg p-4"> <div className="bg-chart-6/20 rounded-lg p-4">
<p className="text-sm text-[#422268] text-center" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-sm text-primary text-center" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<strong>Thank you for supporting our community!</strong><br /> <strong>Thank you for supporting our community!</strong><br />
Your donation helps us continue our mission and provide meaningful experiences for all members. Your donation helps us continue our mission and provide meaningful experiences for all members.
</p> </p>
@@ -470,7 +469,7 @@ const Plans = () => {
<Button <Button
type="button" type="button"
onClick={handleCheckout} onClick={handleCheckout}
className="flex-1 bg-[#DDD8EB] text-[#422268] hover:bg-white" className="flex-1 bg-chart-6 text-primary hover:bg-background"
> >
Continue to Checkout Continue to Checkout
</Button> </Button>

View File

@@ -7,10 +7,10 @@ export default function PrivacyPolicy() {
return ( return (
<> <>
<PublicNavbar /> <PublicNavbar />
<main className="bg-gradient-to-bl from-[#F9FAFB] to-[#DDD8EB] text-[#48286E]"> <main className="bg-gradient-to-bl from-[#F9FAFB] to-chart-6 text-[#48286E]">
<div className="mx-auto w-full max-w-5xl px-4 sm:px-6 lg:px-8 py-10"> <div className="mx-auto w-full max-w-5xl px-4 sm:px-6 lg:px-8 py-10">
<header className="border-b pb-6"> <header className="border-b pb-6">
<h1 className="text-3xl sm:text-4xl font-bold tracking-tight" style={{fontFamily: 'Poppins'}}> <h1 className="text-3xl sm:text-4xl font-bold tracking-tight" style={{ fontFamily: 'Poppins' }}>
LOAFers, Inc. Website Privacy Policy LOAFers, Inc. Website Privacy Policy
</h1> </h1>
</header> </header>
@@ -184,7 +184,7 @@ export default function PrivacyPolicy() {
<div className="mt-8 text-center"> <div className="mt-8 text-center">
<Link <Link
to="/" to="/"
className="text-[#664fa3] hover:text-[#422268] font-semibold transition-colors inline-flex items-center gap-2" className="text-muted-foreground hover:text-primary font-semibold transition-colors inline-flex items-center gap-2"
style={{ fontFamily: "'Nunito Sans', sans-serif" }} style={{ fontFamily: "'Nunito Sans', sans-serif" }}
> >
<span></span> Back to Home <span></span> Back to Home

View File

@@ -213,52 +213,52 @@ const Profile = () => {
if (!profileData) { if (!profileData) {
return ( return (
<div className="min-h-screen bg-white"> <div className="min-h-screen bg-background">
<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-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Loading profile...</p> <p className="text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Loading profile...</p>
</div> </div>
</div> </div>
); );
} }
return ( return (
<div className="min-h-screen bg-white"> <div className="min-h-screen bg-background">
<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="mb-8"> <div className="mb-8">
<h1 className="text-4xl md:text-5xl font-semibold text-[#422268] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}> <h1 className="text-4xl md:text-5xl font-semibold text-primary mb-4" style={{ fontFamily: "'Inter', sans-serif" }}>
My Profile My Profile
</h1> </h1>
<p className="text-lg text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-lg text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Update your personal information below. Update your personal information below.
</p> </p>
</div> </div>
<Card className="p-8 bg-white rounded-2xl border border-[#ddd8eb] shadow-lg"> <Card className="p-8 bg-background rounded-2xl border border-chart-6 shadow-lg">
{/* Read-only Information */} {/* Read-only Information */}
<div className="mb-8 pb-8 border-b border-[#ddd8eb]"> <div className="mb-8 pb-8 border-b border-chart-6">
<h2 className="text-2xl font-semibold text-[#422268] mb-6 flex items-center gap-2" style={{ fontFamily: "'Inter', sans-serif" }}> <h2 className="text-2xl font-semibold text-primary mb-6 flex items-center gap-2" style={{ fontFamily: "'Inter', sans-serif" }}>
<User className="h-6 w-6 text-[#664fa3]" /> <User className="h-6 w-6 text-muted-foreground" />
Account Information Account Information
</h2> </h2>
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4 sm:gap-6"> <div className="grid grid-cols-1 sm:grid-cols-2 gap-4 sm:gap-6">
<div> <div>
<p className="text-sm text-[#664fa3] mb-1" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Email</p> <p className="text-sm text-muted-foreground mb-1" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Email</p>
<p className="text-[#422268] font-medium" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>{profileData.email}</p> <p className="text-primary font-medium" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>{profileData.email}</p>
</div> </div>
<div> <div>
<p className="text-sm text-[#664fa3] mb-1" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Status</p> <p className="text-sm text-muted-foreground mb-1" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Status</p>
<p className="text-[#422268] font-medium capitalize" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>{profileData.status.replace('_', ' ')}</p> <p className="text-primary font-medium capitalize" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>{profileData.status.replace('_', ' ')}</p>
</div> </div>
<div> <div>
<p className="text-sm text-[#664fa3] mb-1" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Role</p> <p className="text-sm text-muted-foreground mb-1" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Role</p>
<p className="text-[#422268] font-medium capitalize" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>{profileData.role}</p> <p className="text-primary font-medium capitalize" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>{profileData.role}</p>
</div> </div>
<div> <div>
<p className="text-sm text-[#664fa3] mb-1" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Date of Birth</p> <p className="text-sm text-muted-foreground mb-1" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Date of Birth</p>
<p className="text-[#422268] font-medium" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-primary font-medium" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{new Date(profileData.date_of_birth).toLocaleDateString()} {new Date(profileData.date_of_birth).toLocaleDateString()}
</p> </p>
</div> </div>
@@ -269,7 +269,7 @@ const Profile = () => {
type="button" type="button"
onClick={() => setPasswordDialogOpen(true)} onClick={() => setPasswordDialogOpen(true)}
variant="outline" variant="outline"
className="border-2 border-[#664fa3] text-[#664fa3] hover:bg-[#f1eef9] rounded-full px-6 py-3" className="border-2 border-muted-foreground text-muted-foreground hover:bg-muted rounded-full px-6 py-3"
> >
<Lock className="h-4 w-4 mr-2" /> <Lock className="h-4 w-4 mr-2" />
Change Password Change Password
@@ -278,15 +278,15 @@ const Profile = () => {
</div> </div>
{/* Profile Photo Section */} {/* Profile Photo Section */}
<div className="pb-8 mb-8 border-b border-[#ddd8eb]"> <div className="pb-8 mb-8 border-b border-chart-6">
<h2 className="text-2xl font-semibold text-[#422268] mb-6 flex items-center gap-2" style={{ fontFamily: "'Inter', sans-serif" }}> <h2 className="text-2xl font-semibold text-primary mb-6 flex items-center gap-2" style={{ fontFamily: "'Inter', sans-serif" }}>
<Camera className="h-6 w-6 text-[#664fa3]" /> <Camera className="h-6 w-6 text-muted-foreground" />
Profile Photo Profile Photo
</h2> </h2>
<div className="flex flex-col md:flex-row items-center gap-6"> <div className="flex flex-col md:flex-row items-center gap-6">
<Avatar className="h-32 w-32 border-4 border-[#ddd8eb]"> <Avatar className="h-32 w-32 border-4 border-chart-6">
<AvatarImage src={previewImage} alt="Profile" /> <AvatarImage src={previewImage} alt="Profile" />
<AvatarFallback className="bg-[#f1eef9] text-[#664fa3] text-3xl"> <AvatarFallback className="bg-muted text-muted-foreground text-3xl">
{profileData?.first_name?.charAt(0)}{profileData?.last_name?.charAt(0)} {profileData?.first_name?.charAt(0)}{profileData?.last_name?.charAt(0)}
</AvatarFallback> </AvatarFallback>
</Avatar> </Avatar>
@@ -304,7 +304,7 @@ const Profile = () => {
type="button" type="button"
onClick={() => fileInputRef.current?.click()} onClick={() => fileInputRef.current?.click()}
disabled={uploadingPhoto} disabled={uploadingPhoto}
className="bg-[#664fa3] text-white hover:bg-[#422268] rounded-full px-6 py-3" className="bg-muted-foreground text-white hover:bg-primary rounded-full px-6 py-3"
> >
<Upload className="h-4 w-4 mr-2" /> <Upload className="h-4 w-4 mr-2" />
{uploadingPhoto ? 'Uploading...' : 'Upload Photo'} {uploadingPhoto ? 'Uploading...' : 'Upload Photo'}
@@ -323,7 +323,7 @@ const Profile = () => {
</Button> </Button>
)} )}
<p className="text-sm text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-sm text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Upload a profile photo (Max {maxFileSizeMB}MB) Upload a profile photo (Max {maxFileSizeMB}MB)
</p> </p>
</div> </div>
@@ -332,7 +332,7 @@ const Profile = () => {
{/* Editable Form */} {/* Editable Form */}
<form onSubmit={handleSubmit} className="space-y-6" data-testid="profile-form"> <form onSubmit={handleSubmit} className="space-y-6" data-testid="profile-form">
<h2 className="text-2xl font-semibold text-[#422268] mb-6" style={{ fontFamily: "'Inter', sans-serif" }}> <h2 className="text-2xl font-semibold text-primary mb-6" style={{ fontFamily: "'Inter', sans-serif" }}>
Personal Information Personal Information
</h2> </h2>
@@ -344,7 +344,7 @@ const Profile = () => {
name="first_name" name="first_name"
value={formData.first_name} value={formData.first_name}
onChange={handleInputChange} onChange={handleInputChange}
className="h-14 rounded-xl border-2 border-[#ddd8eb] focus:border-[#664fa3]" className="h-14 rounded-xl border-2 border-chart-6 focus:border-muted-foreground"
data-testid="first-name-input" data-testid="first-name-input"
/> />
</div> </div>
@@ -355,7 +355,7 @@ const Profile = () => {
name="last_name" name="last_name"
value={formData.last_name} value={formData.last_name}
onChange={handleInputChange} onChange={handleInputChange}
className="h-14 rounded-xl border-2 border-[#ddd8eb] focus:border-[#664fa3]" className="h-14 rounded-xl border-2 border-chart-6 focus:border-muted-foreground"
data-testid="last-name-input" data-testid="last-name-input"
/> />
</div> </div>
@@ -369,7 +369,7 @@ const Profile = () => {
type="tel" type="tel"
value={formData.phone} value={formData.phone}
onChange={handleInputChange} onChange={handleInputChange}
className="h-14 rounded-xl border-2 border-[#ddd8eb] focus:border-[#664fa3]" className="h-14 rounded-xl border-2 border-chart-6 focus:border-muted-foreground"
data-testid="phone-input" data-testid="phone-input"
/> />
</div> </div>
@@ -381,7 +381,7 @@ const Profile = () => {
name="address" name="address"
value={formData.address} value={formData.address}
onChange={handleInputChange} onChange={handleInputChange}
className="h-14 rounded-xl border-2 border-[#ddd8eb] focus:border-[#664fa3]" className="h-14 rounded-xl border-2 border-chart-6 focus:border-muted-foreground"
data-testid="address-input" data-testid="address-input"
/> />
</div> </div>
@@ -394,7 +394,7 @@ const Profile = () => {
name="city" name="city"
value={formData.city} value={formData.city}
onChange={handleInputChange} onChange={handleInputChange}
className="h-14 rounded-xl border-2 border-[#ddd8eb] focus:border-[#664fa3]" className="h-14 rounded-xl border-2 border-chart-6 focus:border-muted-foreground"
data-testid="city-input" data-testid="city-input"
/> />
</div> </div>
@@ -405,7 +405,7 @@ const Profile = () => {
name="state" name="state"
value={formData.state} value={formData.state}
onChange={handleInputChange} onChange={handleInputChange}
className="h-14 rounded-xl border-2 border-[#ddd8eb] focus:border-[#664fa3]" className="h-14 rounded-xl border-2 border-chart-6 focus:border-muted-foreground"
data-testid="state-input" data-testid="state-input"
/> />
</div> </div>
@@ -416,16 +416,16 @@ const Profile = () => {
name="zipcode" name="zipcode"
value={formData.zipcode} value={formData.zipcode}
onChange={handleInputChange} onChange={handleInputChange}
className="h-14 rounded-xl border-2 border-[#ddd8eb] focus:border-[#664fa3]" className="h-14 rounded-xl border-2 border-chart-6 focus:border-muted-foreground"
data-testid="zipcode-input" data-testid="zipcode-input"
/> />
</div> </div>
</div> </div>
{/* Section 2: Partner Information */} {/* Section 2: Partner Information */}
<div className="pt-8 mt-8 border-t border-[#ddd8eb]"> <div className="pt-8 mt-8 border-t border-chart-6">
<h2 className="text-2xl font-semibold text-[#422268] mb-6 flex items-center gap-2" style={{ fontFamily: "'Inter', sans-serif" }}> <h2 className="text-2xl font-semibold text-primary mb-6 flex items-center gap-2" style={{ fontFamily: "'Inter', sans-serif" }}>
<Heart className="h-6 w-6 text-[#ff9e77]" /> <Heart className="h-6 w-6 text-accent" />
Partner Information Partner Information
</h2> </h2>
<div className="space-y-6"> <div className="space-y-6">
@@ -437,7 +437,7 @@ const Profile = () => {
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-[#ddd8eb] focus:border-[#664fa3]" className="h-14 rounded-xl border-2 border-chart-6 focus:border-muted-foreground"
placeholder="Optional" placeholder="Optional"
/> />
</div> </div>
@@ -448,7 +448,7 @@ const Profile = () => {
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-[#ddd8eb] focus:border-[#664fa3]" className="h-14 rounded-xl border-2 border-chart-6 focus:border-muted-foreground"
placeholder="Optional" placeholder="Optional"
/> />
</div> </div>
@@ -461,9 +461,9 @@ const Profile = () => {
name="partner_is_member" name="partner_is_member"
checked={formData.partner_is_member} checked={formData.partner_is_member}
onChange={handleCheckboxChange} onChange={handleCheckboxChange}
className="w-5 h-5 text-[#664fa3] border-2 border-[#ddd8eb] rounded focus:ring-[#664fa3]" className="w-5 h-5 text-muted-foreground border-2 border-chart-6 rounded focus:ring-muted-foreground"
/> />
<Label htmlFor="partner_is_member" className="cursor-pointer text-[#422268]"> <Label htmlFor="partner_is_member" className="cursor-pointer text-primary">
My partner is a current member My partner is a current member
</Label> </Label>
</div> </div>
@@ -474,9 +474,9 @@ const Profile = () => {
name="partner_plan_to_become_member" name="partner_plan_to_become_member"
checked={formData.partner_plan_to_become_member} checked={formData.partner_plan_to_become_member}
onChange={handleCheckboxChange} onChange={handleCheckboxChange}
className="w-5 h-5 text-[#664fa3] border-2 border-[#ddd8eb] rounded focus:ring-[#664fa3]" className="w-5 h-5 text-muted-foreground border-2 border-chart-6 rounded focus:ring-muted-foreground"
/> />
<Label htmlFor="partner_plan_to_become_member" className="cursor-pointer text-[#422268]"> <Label htmlFor="partner_plan_to_become_member" className="cursor-pointer text-primary">
My partner plans to become a member My partner plans to become a member
</Label> </Label>
</div> </div>
@@ -485,12 +485,12 @@ const Profile = () => {
</div> </div>
{/* Section 3: Newsletter Preferences */} {/* Section 3: Newsletter Preferences */}
<div className="pt-8 mt-8 border-t border-[#ddd8eb]"> <div className="pt-8 mt-8 border-t border-chart-6">
<h2 className="text-2xl font-semibold text-[#422268] mb-6 flex items-center gap-2" style={{ fontFamily: "'Inter', sans-serif" }}> <h2 className="text-2xl font-semibold text-primary mb-6 flex items-center gap-2" style={{ fontFamily: "'Inter', sans-serif" }}>
<Mail className="h-6 w-6 text-[#81B29A]" /> <Mail className="h-6 w-6 text-[#81B29A]" />
Newsletter Preferences Newsletter Preferences
</h2> </h2>
<p className="text-[#664fa3] mb-4" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-muted-foreground mb-4" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Choose what information you'd like published in our member newsletter. Choose what information you'd like published in our member newsletter.
</p> </p>
<div className="space-y-3"> <div className="space-y-3">
@@ -501,9 +501,9 @@ const Profile = () => {
name="newsletter_publish_name" name="newsletter_publish_name"
checked={formData.newsletter_publish_name} checked={formData.newsletter_publish_name}
onChange={handleCheckboxChange} onChange={handleCheckboxChange}
className="w-5 h-5 text-[#664fa3] border-2 border-[#ddd8eb] rounded focus:ring-[#664fa3]" className="w-5 h-5 text-muted-foreground border-2 border-chart-6 rounded focus:ring-muted-foreground"
/> />
<Label htmlFor="newsletter_publish_name" className="cursor-pointer text-[#422268]"> <Label htmlFor="newsletter_publish_name" className="cursor-pointer text-primary">
Publish my name Publish my name
</Label> </Label>
</div> </div>
@@ -514,9 +514,9 @@ const Profile = () => {
name="newsletter_publish_photo" name="newsletter_publish_photo"
checked={formData.newsletter_publish_photo} checked={formData.newsletter_publish_photo}
onChange={handleCheckboxChange} onChange={handleCheckboxChange}
className="w-5 h-5 text-[#664fa3] border-2 border-[#ddd8eb] rounded focus:ring-[#664fa3]" className="w-5 h-5 text-muted-foreground border-2 border-chart-6 rounded focus:ring-muted-foreground"
/> />
<Label htmlFor="newsletter_publish_photo" className="cursor-pointer text-[#422268]"> <Label htmlFor="newsletter_publish_photo" className="cursor-pointer text-primary">
Publish my photo Publish my photo
</Label> </Label>
</div> </div>
@@ -527,9 +527,9 @@ const Profile = () => {
name="newsletter_publish_birthday" name="newsletter_publish_birthday"
checked={formData.newsletter_publish_birthday} checked={formData.newsletter_publish_birthday}
onChange={handleCheckboxChange} onChange={handleCheckboxChange}
className="w-5 h-5 text-[#664fa3] border-2 border-[#ddd8eb] rounded focus:ring-[#664fa3]" className="w-5 h-5 text-muted-foreground border-2 border-chart-6 rounded focus:ring-muted-foreground"
/> />
<Label htmlFor="newsletter_publish_birthday" className="cursor-pointer text-[#422268]"> <Label htmlFor="newsletter_publish_birthday" className="cursor-pointer text-primary">
Publish my birthday Publish my birthday
</Label> </Label>
</div> </div>
@@ -540,9 +540,9 @@ const Profile = () => {
name="newsletter_publish_none" name="newsletter_publish_none"
checked={formData.newsletter_publish_none} checked={formData.newsletter_publish_none}
onChange={handleCheckboxChange} onChange={handleCheckboxChange}
className="w-5 h-5 text-[#664fa3] border-2 border-[#ddd8eb] rounded focus:ring-[#664fa3]" className="w-5 h-5 text-muted-foreground border-2 border-chart-6 rounded focus:ring-muted-foreground"
/> />
<Label htmlFor="newsletter_publish_none" className="cursor-pointer text-[#422268]"> <Label htmlFor="newsletter_publish_none" className="cursor-pointer text-primary">
Do not publish any information Do not publish any information
</Label> </Label>
</div> </div>
@@ -550,12 +550,12 @@ const Profile = () => {
</div> </div>
{/* Section 4: Volunteer Interests */} {/* Section 4: Volunteer Interests */}
<div className="pt-8 mt-8 border-t border-[#ddd8eb]"> <div className="pt-8 mt-8 border-t border-chart-6">
<h2 className="text-2xl font-semibold text-[#422268] mb-6 flex items-center gap-2" style={{ fontFamily: "'Inter', sans-serif" }}> <h2 className="text-2xl font-semibold text-primary mb-6 flex items-center gap-2" style={{ fontFamily: "'Inter', sans-serif" }}>
<Users className="h-6 w-6 text-[#664fa3]" /> <Users className="h-6 w-6 text-muted-foreground" />
Volunteer Interests Volunteer Interests
</h2> </h2>
<p className="text-[#664fa3] mb-4" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-muted-foreground mb-4" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Select areas where you'd like to volunteer and help our community. Select areas where you'd like to volunteer and help our community.
</p> </p>
<div className="grid md:grid-cols-2 gap-3"> <div className="grid md:grid-cols-2 gap-3">
@@ -566,11 +566,11 @@ const Profile = () => {
id={`volunteer_${option.replace(/\s+/g, '_').toLowerCase()}`} id={`volunteer_${option.replace(/\s+/g, '_').toLowerCase()}`}
checked={formData.volunteer_interests.includes(option)} checked={formData.volunteer_interests.includes(option)}
onChange={() => handleVolunteerToggle(option)} onChange={() => handleVolunteerToggle(option)}
className="w-5 h-5 text-[#664fa3] border-2 border-[#ddd8eb] rounded focus:ring-[#664fa3]" className="w-5 h-5 text-muted-foreground border-2 border-chart-6 rounded focus:ring-muted-foreground"
/> />
<Label <Label
htmlFor={`volunteer_${option.replace(/\s+/g, '_').toLowerCase()}`} htmlFor={`volunteer_${option.replace(/\s+/g, '_').toLowerCase()}`}
className="cursor-pointer text-[#422268]" className="cursor-pointer text-primary"
> >
{option} {option}
</Label> </Label>
@@ -580,12 +580,12 @@ const Profile = () => {
</div> </div>
{/* Section 5: Member Directory Settings */} {/* Section 5: Member Directory Settings */}
<div className="pt-8 mt-8 border-t border-[#ddd8eb]"> <div className="pt-8 mt-8 border-t border-chart-6">
<h2 className="text-2xl font-semibold text-[#422268] mb-6 flex items-center gap-2" style={{ fontFamily: "'Inter', sans-serif" }}> <h2 className="text-2xl font-semibold text-primary mb-6 flex items-center gap-2" style={{ fontFamily: "'Inter', sans-serif" }}>
<BookUser className="h-6 w-6 text-[#ff9e77]" /> <BookUser className="h-6 w-6 text-accent" />
Member Directory Settings Member Directory Settings
</h2> </h2>
<p className="text-[#664fa3] mb-6" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-muted-foreground mb-6" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Control your visibility and information in the member directory. Control your visibility and information in the member directory.
</p> </p>
@@ -597,15 +597,15 @@ const Profile = () => {
name="show_in_directory" name="show_in_directory"
checked={formData.show_in_directory} checked={formData.show_in_directory}
onChange={handleCheckboxChange} onChange={handleCheckboxChange}
className="w-5 h-5 text-[#664fa3] border-2 border-[#ddd8eb] rounded focus:ring-[#664fa3]" className="w-5 h-5 text-muted-foreground border-2 border-chart-6 rounded focus:ring-muted-foreground"
/> />
<Label htmlFor="show_in_directory" className="cursor-pointer text-[#422268] font-medium"> <Label htmlFor="show_in_directory" className="cursor-pointer text-primary font-medium">
Include me in the member directory Include me in the member directory
</Label> </Label>
</div> </div>
{formData.show_in_directory && ( {formData.show_in_directory && (
<div className="space-y-6 pl-4 border-l-4 border-[#DDD8EB]"> <div className="space-y-6 pl-4 border-l-4 border-chart-6">
<div> <div>
<Label htmlFor="directory_email">Directory Email</Label> <Label htmlFor="directory_email">Directory Email</Label>
<Input <Input
@@ -614,7 +614,7 @@ const Profile = () => {
type="email" type="email"
value={formData.directory_email} value={formData.directory_email}
onChange={handleInputChange} onChange={handleInputChange}
className="h-14 rounded-xl border-2 border-[#ddd8eb] focus:border-[#664fa3]" className="h-14 rounded-xl border-2 border-chart-6 focus:border-muted-foreground"
placeholder="Optional - email to show in directory" placeholder="Optional - email to show in directory"
/> />
</div> </div>
@@ -626,7 +626,7 @@ const Profile = () => {
name="directory_bio" name="directory_bio"
value={formData.directory_bio} value={formData.directory_bio}
onChange={handleInputChange} onChange={handleInputChange}
className="rounded-xl border-2 border-[#ddd8eb] focus:border-[#664fa3] min-h-[100px]" className="rounded-xl border-2 border-chart-6 focus:border-muted-foreground min-h-[100px]"
placeholder="Tell other members about yourself..." placeholder="Tell other members about yourself..."
/> />
</div> </div>
@@ -638,7 +638,7 @@ const Profile = () => {
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-[#ddd8eb] focus:border-[#664fa3]" className="h-14 rounded-xl border-2 border-chart-6 focus:border-muted-foreground"
placeholder="Optional - address to show in directory" placeholder="Optional - address to show in directory"
/> />
</div> </div>
@@ -651,7 +651,7 @@ const Profile = () => {
type="tel" type="tel"
value={formData.directory_phone} value={formData.directory_phone}
onChange={handleInputChange} onChange={handleInputChange}
className="h-14 rounded-xl border-2 border-[#ddd8eb] focus:border-[#664fa3]" className="h-14 rounded-xl border-2 border-chart-6 focus:border-muted-foreground"
placeholder="Optional - phone to show in directory" placeholder="Optional - phone to show in directory"
/> />
</div> </div>
@@ -664,7 +664,7 @@ const Profile = () => {
type="date" type="date"
value={formData.directory_dob} value={formData.directory_dob}
onChange={handleInputChange} onChange={handleInputChange}
className="h-14 rounded-xl border-2 border-[#ddd8eb] focus:border-[#664fa3]" className="h-14 rounded-xl border-2 border-chart-6 focus:border-muted-foreground"
/> />
</div> </div>
@@ -675,7 +675,7 @@ const Profile = () => {
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-[#ddd8eb] focus:border-[#664fa3]" className="h-14 rounded-xl border-2 border-chart-6 focus:border-muted-foreground"
placeholder="Optional - partner name to show in directory" placeholder="Optional - partner name to show in directory"
/> />
</div> </div>
@@ -684,11 +684,11 @@ const Profile = () => {
</div> </div>
</div> </div>
<div className="pt-8 mt-8 border-t border-[#ddd8eb]"> <div className="pt-8 mt-8 border-t border-chart-6">
<Button <Button
type="submit" type="submit"
disabled={loading} disabled={loading}
className="bg-[#DDD8EB] text-[#422268] hover:bg-white rounded-full px-8 py-6 text-lg font-medium shadow-lg disabled:opacity-50" className="bg-chart-6 text-primary hover:bg-background rounded-full px-8 py-6 text-lg font-medium shadow-lg disabled:opacity-50"
data-testid="save-profile-button" data-testid="save-profile-button"
> >
<Save className="h-5 w-5 mr-2" /> <Save className="h-5 w-5 mr-2" />

View File

@@ -183,23 +183,23 @@ const Register = () => {
}; };
return ( return (
<div className="min-h-screen bg-white"> <div className="min-h-screen bg-background">
<PublicNavbar /> <PublicNavbar />
<div className="max-w-4xl mx-auto px-6 py-12"> <div className="max-w-4xl mx-auto px-6 py-12">
<div className="mb-8"> <div className="mb-8">
<Link to="/" className="inline-flex items-center text-[#664fa3] hover:text-[#ff9e77] transition-colors"> <Link to="/" className="inline-flex items-center text-muted-foreground hover:text-accent transition-colors">
<ArrowLeft className="h-4 w-4 mr-2" /> <ArrowLeft className="h-4 w-4 mr-2" />
Back to Home Back to Home
</Link> </Link>
</div> </div>
<Card className="p-8 md:p-12 bg-white rounded-2xl border border-[#ddd8eb] shadow-lg"> <Card className="p-8 md:p-12 bg-background rounded-2xl border border-chart-6 shadow-lg">
<div className="mb-8"> <div className="mb-8">
<h1 className="text-4xl md:text-5xl font-semibold text-[#422268] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}> <h1 className="text-4xl md:text-5xl font-semibold text-primary mb-4" style={{ fontFamily: "'Inter', sans-serif" }}>
Join Our Community Join Our Community
</h1> </h1>
<p className="text-lg text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-lg text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Fill out the form below to start your membership journey. Fill out the form below to start your membership journey.
</p> </p>
</div> </div>
@@ -245,7 +245,7 @@ const Register = () => {
type="button" type="button"
onClick={handleBack} onClick={handleBack}
variant="outline" variant="outline"
className="rounded-full px-6 py-6 text-lg border-2 border-[#ddd8eb] hover:border-[#664fa3] text-[#422268]" className="rounded-full px-6 py-6 text-lg border-2 border-chart-6 hover:border-muted-foreground text-primary"
> >
<ArrowLeft className="mr-2 h-5 w-5" /> <ArrowLeft className="mr-2 h-5 w-5" />
Back Back
@@ -258,7 +258,7 @@ const Register = () => {
<Button <Button
type="button" type="button"
onClick={handleNext} onClick={handleNext}
className="bg-[#DDD8EB] text-[#422268] hover:bg-white rounded-full px-6 py-6 text-lg font-medium shadow-lg hover:scale-105 transition-transform" className="bg-chart-6 text-primary hover:bg-background rounded-full px-6 py-6 text-lg font-medium shadow-lg hover:scale-105 transition-transform"
> >
Next Next
<ArrowRight className="ml-2 h-5 w-5" /> <ArrowRight className="ml-2 h-5 w-5" />
@@ -267,7 +267,7 @@ const Register = () => {
<Button <Button
type="submit" type="submit"
disabled={loading} disabled={loading}
className="bg-[#DDD8EB] text-[#422268] hover:bg-white rounded-full px-6 py-6 text-lg font-medium shadow-lg hover:scale-105 transition-transform disabled:opacity-50 disabled:cursor-not-allowed" className="bg-chart-6 text-primary hover:bg-background rounded-full px-6 py-6 text-lg font-medium shadow-lg hover:scale-105 transition-transform disabled:opacity-50 disabled:cursor-not-allowed"
data-testid="submit-register-button" data-testid="submit-register-button"
> >
{loading ? 'Creating Account...' : 'Create Account'} {loading ? 'Creating Account...' : 'Create Account'}
@@ -276,9 +276,9 @@ const Register = () => {
)} )}
</div> </div>
<p className="text-center text-[#664fa3] mt-4" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-center text-muted-foreground mt-4" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Already have an account?{' '} Already have an account?{' '}
<Link to="/login" className="text-[#ff9e77] hover:underline font-medium"> <Link to="/login" className="text-accent hover:underline font-medium">
Login here Login here
</Link> </Link>
</p> </p>

View File

@@ -64,19 +64,19 @@ const ResetPassword = () => {
}; };
return ( return (
<div className="min-h-screen bg-white"> <div className="min-h-screen bg-background">
<PublicNavbar /> <PublicNavbar />
<div className="max-w-md mx-auto px-6 py-12"> <div className="max-w-md mx-auto px-6 py-12">
<Card className="p-8 md:p-12 bg-white rounded-2xl border border-[#ddd8eb] shadow-lg"> <Card className="p-8 md:p-12 bg-background rounded-2xl border border-chart-6 shadow-lg">
<div className="mb-8 text-center"> <div className="mb-8 text-center">
<div className="inline-flex items-center justify-center w-16 h-16 rounded-full bg-[#f1eef9] mb-4"> <div className="inline-flex items-center justify-center w-16 h-16 rounded-full bg-muted mb-4">
<Lock className="h-8 w-8 text-[#664fa3]" /> <Lock className="h-8 w-8 text-muted-foreground" />
</div> </div>
<h1 className="text-4xl md:text-5xl font-semibold text-[#422268] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}> <h1 className="text-4xl md:text-5xl font-semibold text-primary mb-4" style={{ fontFamily: "'Inter', sans-serif" }}>
Reset Password Reset Password
</h1> </h1>
<p className="text-lg text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-lg text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Enter your new password below. Enter your new password below.
</p> </p>
</div> </div>
@@ -92,7 +92,7 @@ const ResetPassword = () => {
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-14 rounded-xl border-2 border-[#ddd8eb] focus:border-[#664fa3]" className="h-14 rounded-xl border-2 border-chart-6 focus:border-muted-foreground"
/> />
</div> </div>
@@ -106,15 +106,15 @@ const ResetPassword = () => {
value={formData.confirmPassword} value={formData.confirmPassword}
onChange={handleInputChange} onChange={handleInputChange}
placeholder="Re-enter new password" placeholder="Re-enter new password"
className="h-14 rounded-xl border-2 border-[#ddd8eb] focus:border-[#664fa3]" className="h-14 rounded-xl border-2 border-chart-6 focus:border-muted-foreground"
/> />
</div> </div>
<div className="bg-[#f1eef9] border-l-4 border-[#664fa3] p-4 rounded-lg"> <div className="bg-muted border-l-4 border-muted-foreground p-4 rounded-lg">
<div className="flex items-start"> <div className="flex items-start">
<AlertCircle className="h-5 w-5 text-[#664fa3] mr-2 mt-0.5 flex-shrink-0" /> <AlertCircle className="h-5 w-5 text-muted-foreground mr-2 mt-0.5 flex-shrink-0" />
<div className="text-sm text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <div className="text-sm text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p className="font-medium text-[#422268] mb-1">Password Requirements:</p> <p className="font-medium text-primary mb-1">Password Requirements:</p>
<ul className="list-disc list-inside space-y-1"> <ul className="list-disc list-inside space-y-1">
<li>At least 6 characters long</li> <li>At least 6 characters long</li>
<li>Both passwords must match</li> <li>Both passwords must match</li>
@@ -126,15 +126,15 @@ const ResetPassword = () => {
<Button <Button
type="submit" type="submit"
disabled={loading} disabled={loading}
className="w-full bg-[#DDD8EB] text-[#422268] hover:bg-white rounded-full py-6 text-lg font-medium shadow-lg hover:scale-105 transition-transform disabled:opacity-50" className="w-full bg-chart-6 text-primary hover:bg-background rounded-full py-6 text-lg font-medium shadow-lg hover:scale-105 transition-transform disabled:opacity-50"
> >
{loading ? 'Resetting Password...' : 'Reset Password'} {loading ? 'Resetting Password...' : 'Reset Password'}
<ArrowRight className="ml-2 h-5 w-5" /> <ArrowRight className="ml-2 h-5 w-5" />
</Button> </Button>
<p className="text-center text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-center text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Remember your password?{' '} Remember your password?{' '}
<Link to="/login" className="text-[#ff9e77] hover:underline font-medium"> <Link to="/login" className="text-accent hover:underline font-medium">
Login here Login here
</Link> </Link>
</p> </p>

View File

@@ -97,10 +97,10 @@ const Resources = () => {
]; ];
return ( return (
<div className="min-h-screen bg-white"> <div className="min-h-screen bg-background">
<PublicNavbar /> <PublicNavbar />
<main className="bg-gradient-to-b from-white via-[#f1eef9] to-[#e8e0f5] px-6 py-16"> <main className="bg-gradient-to-b from-white via-muted to-[#e8e0f5] px-6 py-16">
{/* Header Section */} {/* Header Section */}
<section className="max-w-7xl mx-auto mb-12"> <section className="max-w-7xl mx-auto mb-12">
<h1 className="text-[28px] font-bold text-[#48286e] text-center mb-12" style={{ fontFamily: "'Inter', sans-serif" }}> <h1 className="text-[28px] font-bold text-[#48286e] text-center mb-12" style={{ fontFamily: "'Inter', sans-serif" }}>
@@ -130,7 +130,7 @@ const Resources = () => {
{/* Accordion Button */} {/* Accordion Button */}
<button <button
onClick={() => toggleAccordion(categoryIndex, resourceIndex)} onClick={() => toggleAccordion(categoryIndex, resourceIndex)}
className={`w-full bg-gradient-to-tr from-[#48286E] to-[#664FA3] hover:bg-[#5a4290] text-white px-6 py-4 rounded-3xl flex items-center justify-between transition-all ${isExpanded ? 'rounded-b-none rounded-t-3xl' : ''}` className={`w-full bg-gradient-to-tr from-[#48286E] to-muted-foreground hover:bg-[#5a4290] text-white px-6 py-4 rounded-3xl flex items-center justify-between transition-all ${isExpanded ? 'rounded-b-none rounded-t-3xl' : ''}`
} }
> >
@@ -148,7 +148,7 @@ const Resources = () => {
className={`transition-all duration-300 ease-in-out ${isExpanded ? 'max-h-[1000px] opacity-100' : 'max-h-0 opacity-0' className={`transition-all duration-300 ease-in-out ${isExpanded ? 'max-h-[1000px] opacity-100' : 'max-h-0 opacity-0'
}`} }`}
> >
<Card className="p-6 bg-white rounded-b-2xl rounded-t-none border-none "> <Card className="p-6 bg-background rounded-b-2xl rounded-t-none border-none ">
{/* Description */} {/* Description */}
<p className="text-[#48286e] mb-4 leading-relaxed" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-[#48286e] mb-4 leading-relaxed" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{resource.description} {resource.description}
@@ -158,7 +158,7 @@ const Resources = () => {
<div className="space-y-3"> <div className="space-y-3">
{/* Location */} {/* Location */}
{resource.location && ( {resource.location && (
<div className="flex items-start gap-2 text-[#664fa3]"> <div className="flex items-start gap-2 text-muted-foreground">
<MapPin className="h-5 w-5 flex-shrink-0 mt-0.5" /> <MapPin className="h-5 w-5 flex-shrink-0 mt-0.5" />
<span className="text-sm" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <span className="text-sm" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{resource.location} {resource.location}
@@ -168,7 +168,7 @@ const Resources = () => {
{/* Contact */} {/* Contact */}
{resource.contact && ( {resource.contact && (
<div className="text-[#664fa3]"> <div className="text-muted-foreground">
<p className="text-sm font-medium mb-1" style={{ fontFamily: "'Inter', sans-serif" }}> <p className="text-sm font-medium mb-1" style={{ fontFamily: "'Inter', sans-serif" }}>
Contact: {resource.contact} Contact: {resource.contact}
</p> </p>
@@ -207,7 +207,7 @@ const Resources = () => {
href={resource.link} href={resource.link}
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
className="inline-flex items-center gap-2 text-[#ff9e77] hover:text-[#e88a63] font-medium transition-colors mt-2" className="inline-flex items-center gap-2 text-accent hover:text-[#e88a63] font-medium transition-colors mt-2"
style={{ fontFamily: "'Inter', sans-serif" }} style={{ fontFamily: "'Inter', sans-serif" }}
> >
Visit Website Visit Website
@@ -228,7 +228,7 @@ const Resources = () => {
{/* Additional Help Section */} {/* Additional Help Section */}
<section className="max-w-4xl mx-auto mt-16"> <section className="max-w-4xl mx-auto mt-16">
<Card className="p-8 bg-gradient-to-r from-[#664fa3] to-[#48286e] rounded-2xl shadow-xl text-center"> <Card className="p-8 bg-gradient-to-r from-muted-foreground to-[#48286e] rounded-2xl shadow-xl text-center">
<h3 className="text-2xl font-bold text-white mb-4" style={{ fontFamily: "'Inter', sans-serif" }}> <h3 className="text-2xl font-bold text-white mb-4" style={{ fontFamily: "'Inter', sans-serif" }}>
Need Additional Support? Need Additional Support?
</h3> </h3>
@@ -237,7 +237,7 @@ const Resources = () => {
</p> </p>
<a <a
href="mailto:support@loaf.org" href="mailto:support@loaf.org"
className="inline-block bg-white text-[#48286e] px-8 py-3 rounded-full font-semibold hover:bg-[#f1eef9] transition-colors shadow-lg" className="inline-block bg-background text-[#48286e] px-8 py-3 rounded-full font-semibold hover:bg-muted transition-colors shadow-lg"
style={{ fontFamily: "'Inter', sans-serif" }} style={{ fontFamily: "'Inter', sans-serif" }}
> >
Contact Us Contact Us

View File

@@ -8,7 +8,7 @@ export default function TermsOfService() {
return ( return (
<> <>
<PublicNavbar /> <PublicNavbar />
<main className="bg-gradient-to-bl from-[#F9FAFB] to-[#DDD8EB] text-[#48286E]"> <main className="bg-gradient-to-bl from-[#F9FAFB] to-chart-6 text-[#48286E]">
<div className="mx-auto w-full max-w-5xl px-4 sm:px-6 lg:px-8 py-10"> <div className="mx-auto w-full max-w-5xl px-4 sm:px-6 lg:px-8 py-10">
{/* Title */} {/* Title */}
<header className="border-b pb-6"> <header className="border-b pb-6">
@@ -589,7 +589,7 @@ export default function TermsOfService() {
</div> </div>
{/* Back to Home Link */} {/* Back to Home Link */}
<div className="mt-8 text-center"> <div className="mt-8 text-center">
<Link to="/" className="text-[#664fa3] hover:text-[#422268] font-semibold transition-colors inline-flex items-center gap-2" <Link to="/" className="text-muted-foreground hover:text-primary font-semibold transition-colors inline-flex items-center gap-2"
style={{ fontFamily: "'Nunito Sans', sans-serif" }}> style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<span></span> Back to Home <span></span> Back to Home
</Link> </Link>

View File

@@ -45,18 +45,18 @@ const VerifyEmail = () => {
}, [token]); }, [token]);
return ( return (
<div className="min-h-screen bg-white"> <div className="min-h-screen bg-background">
<PublicNavbar /> <PublicNavbar />
<div className="max-w-2xl mx-auto px-6 py-20"> <div className="max-w-2xl mx-auto px-6 py-20">
<Card className="p-6 sm:p-8 md:p-12 bg-white rounded-2xl border border-[#ddd8eb] shadow-lg text-center"> <Card className="p-6 sm:p-8 md:p-12 bg-background rounded-2xl border border-chart-6 shadow-lg text-center">
{status === 'loading' && ( {status === 'loading' && (
<> <>
<Loader2 className="h-20 w-20 text-[#664fa3] mx-auto mb-6 animate-spin" /> <Loader2 className="h-20 w-20 text-muted-foreground mx-auto mb-6 animate-spin" />
<h1 className="text-3xl font-semibold text-[#422268] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}> <h1 className="text-3xl font-semibold text-primary mb-4" style={{ fontFamily: "'Inter', sans-serif" }}>
Verifying Your Email... Verifying Your Email...
</h1> </h1>
<p className="text-lg text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-lg text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Please wait while we verify your email address. Please wait while we verify your email address.
</p> </p>
</> </>
@@ -65,18 +65,18 @@ const VerifyEmail = () => {
{status === 'success' && ( {status === 'success' && (
<> <>
<CheckCircle className="h-20 w-20 text-[#81B29A] mx-auto mb-6" /> <CheckCircle className="h-20 w-20 text-[#81B29A] mx-auto mb-6" />
<h1 className="text-3xl font-semibold text-[#422268] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}> <h1 className="text-3xl font-semibold text-primary mb-4" style={{ fontFamily: "'Inter', sans-serif" }}>
Email Verified Successfully! Email Verified Successfully!
</h1> </h1>
<p className="text-lg text-[#664fa3] mb-8" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-lg text-muted-foreground mb-8" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{message} {message}
</p> </p>
<p className="text-base text-[#664fa3] mb-8" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-base text-muted-foreground mb-8" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Next steps: Attend an event and meet a board member within 90 days. Once you've attended an event, our admin team will review your application. Next steps: Attend an event and meet a board member within 90 days. Once you've attended an event, our admin team will review your application.
</p> </p>
<Link to="/login"> <Link to="/login">
<Button <Button
className="bg-[#DDD8EB] text-[#422268] hover:bg-white rounded-full px-12 py-6 text-lg font-medium shadow-lg" className="bg-chart-6 text-primary hover:bg-background rounded-full px-12 py-6 text-lg font-medium shadow-lg"
data-testid="login-redirect-button" data-testid="login-redirect-button"
> >
Go to Login Go to Login
@@ -88,15 +88,15 @@ const VerifyEmail = () => {
{status === 'error' && ( {status === 'error' && (
<> <>
<XCircle className="h-20 w-20 text-red-500 mx-auto mb-6" /> <XCircle className="h-20 w-20 text-red-500 mx-auto mb-6" />
<h1 className="text-3xl font-semibold text-[#422268] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}> <h1 className="text-3xl font-semibold text-primary mb-4" style={{ fontFamily: "'Inter', sans-serif" }}>
Verification Failed Verification Failed
</h1> </h1>
<p className="text-lg text-[#664fa3] mb-8" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-lg text-muted-foreground mb-8" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{message} {message}
</p> </p>
<Link to="/"> <Link to="/">
<Button <Button
className="bg-[#DDD8EB] text-[#422268] hover:bg-white rounded-full px-12 py-6 text-lg font-medium shadow-lg" className="bg-chart-6 text-primary hover:bg-background rounded-full px-12 py-6 text-lg font-medium shadow-lg"
data-testid="home-redirect-button" data-testid="home-redirect-button"
> >
Go to Home Go to Home

View File

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

View File

@@ -4,7 +4,7 @@ import api from '../../utils/api';
import { Card } from '../../components/ui/card'; import { Card } from '../../components/ui/card';
import { Button } from '../../components/ui/button'; import { Button } from '../../components/ui/button';
import { Badge } from '../../components/ui/badge'; import { Badge } from '../../components/ui/badge';
import { Users, Calendar, Clock, CheckCircle, Mail, AlertCircle,Globe } from 'lucide-react'; import { Users, Calendar, Clock, CheckCircle, Mail, AlertCircle, Globe } from 'lucide-react';
const AdminDashboard = () => { const AdminDashboard = () => {
const [stats, setStats] = useState({ const [stats, setStats] = useState({
@@ -58,16 +58,16 @@ const AdminDashboard = () => {
<> <>
<div className='flex justify-between items-center'> <div className='flex justify-between items-center'>
<div className="mb-8"> <div className="mb-8">
<h1 className="text-4xl md:text-5xl font-semibold text-[#422268] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}> <h1 className="text-4xl md:text-5xl font-semibold text-primary mb-4" style={{ fontFamily: "'Inter', sans-serif" }}>
Admin Dashboard Admin Dashboard
</h1> </h1>
<p className="text-lg text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-lg text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Manage users, events, and membership applications. Manage users, events, and membership applications.
</p> </p>
</div> </div>
<Link to={'/'}> <Link to={'/'}>
<Button <Button
className="bg-[#664fa3] text-white hover:bg-[#533a82] rounded-full flex items-center gap-2" className="bg-muted-foreground text-background hover:text-white hover:bg-[#533a82] rounded-full flex items-center gap-2"
> >
<Globe /> <Globe />
View Public Site View Public Site
@@ -77,56 +77,56 @@ 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-white rounded-2xl border border-[#ddd8eb]" data-testid="stat-total-users"> <Card className="p-6 bg-background rounded-2xl border border-chart-6" 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-[#DDD8EB]/20 p-3 rounded-lg"> <div className="bg-chart-6/20 p-3 rounded-lg">
<Users className="h-6 w-6 text-[#664fa3]" /> <Users className="h-6 w-6 text-muted-foreground" />
</div> </div>
</div> </div>
<p className="text-3xl font-semibold text-[#422268] mb-1" style={{ fontFamily: "'Inter', sans-serif" }}> <p className="text-3xl font-semibold text-primary mb-1" style={{ fontFamily: "'Inter', sans-serif" }}>
{loading ? '-' : stats.totalMembers} {loading ? '-' : stats.totalMembers}
</p> </p>
<p className="text-sm text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Total Members</p> <p className="text-sm text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Total Members</p>
</Card> </Card>
<Card className="p-6 bg-white rounded-2xl border border-[#ddd8eb]" data-testid="stat-pending-validations"> <Card className="p-6 bg-background rounded-2xl border border-chart-6" data-testid="stat-pending-validations">
<div className="flex items-center justify-between mb-4"> <div className="flex items-center justify-between mb-4">
<div className="bg-orange-100 p-3 rounded-lg"> <div className="bg-orange-100 p-3 rounded-lg">
<Clock className="h-6 w-6 text-orange-600" /> <Clock className="h-6 w-6 text-orange-600" />
</div> </div>
</div> </div>
<p className="text-3xl font-semibold text-[#422268] mb-1" style={{ fontFamily: "'Inter', sans-serif" }}> <p className="text-3xl font-semibold text-primary mb-1" style={{ fontFamily: "'Inter', sans-serif" }}>
{loading ? '-' : stats.pendingValidations} {loading ? '-' : stats.pendingValidations}
</p> </p>
<p className="text-sm text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Pending Validations</p> <p className="text-sm text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Pending Validations</p>
</Card> </Card>
<Card className="p-6 bg-white rounded-2xl border border-[#ddd8eb]" data-testid="stat-active-members"> <Card className="p-6 bg-background rounded-2xl border border-chart-6" 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 text-[#422268] mb-1" style={{ fontFamily: "'Inter', sans-serif" }}> <p className="text-3xl font-semibold text-primary mb-1" style={{ fontFamily: "'Inter', sans-serif" }}>
{loading ? '-' : stats.activeMembers} {loading ? '-' : stats.activeMembers}
</p> </p>
<p className="text-sm text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Active Members</p> <p className="text-sm text-muted-foreground" 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/members"> <Link to="/admin/members">
<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"> <Card className="p-8 bg-background rounded-2xl border border-chart-6 hover:shadow-lg hover:-translate-y-1 transition-all cursor-pointer" data-testid="quick-action-users">
<Users className="h-12 w-12 text-[#664fa3] mb-4" /> <Users className="h-12 w-12 text-muted-foreground mb-4" />
<h3 className="text-xl font-semibold text-[#422268] mb-2" style={{ fontFamily: "'Inter', sans-serif" }}> <h3 className="text-xl font-semibold text-primary mb-2" style={{ fontFamily: "'Inter', sans-serif" }}>
Manage Members Manage Members
</h3> </h3>
<p className="text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
View and manage paying members and their subscription status. View and manage paying members and their subscription status.
</p> </p>
<Button <Button
className="mt-4 bg-[#DDD8EB] text-[#422268] hover:bg-white rounded-full" className="mt-4 bg-chart-6 text-primary hover:bg-background rounded-full"
data-testid="manage-users-button" data-testid="manage-users-button"
> >
Go to Members Go to Members
@@ -135,16 +135,16 @@ const AdminDashboard = () => {
</Link> </Link>
<Link to="/admin/validations"> <Link to="/admin/validations">
<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-validations"> <Card className="p-8 bg-background rounded-2xl border border-chart-6 hover:shadow-lg hover:-translate-y-1 transition-all cursor-pointer" data-testid="quick-action-validations">
<Clock className="h-12 w-12 text-orange-600 mb-4" /> <Clock className="h-12 w-12 text-orange-600 mb-4" />
<h3 className="text-xl font-semibold text-[#422268] mb-2" style={{ fontFamily: "'Inter', sans-serif" }}> <h3 className="text-xl font-semibold text-primary mb-2" style={{ fontFamily: "'Inter', sans-serif" }}>
Validation Queue Validation Queue
</h3> </h3>
<p className="text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Review and validate pending membership applications. Review and validate pending membership applications.
</p> </p>
<Button <Button
className="mt-4 bg-[#DDD8EB] text-[#422268] hover:bg-white rounded-full" className="mt-4 bg-chart-6 text-primary hover:bg-background rounded-full"
data-testid="manage-validations-button" data-testid="manage-validations-button"
> >
View Validations View Validations
@@ -156,16 +156,16 @@ const AdminDashboard = () => {
{/* Users Needing Attention Widget */} {/* Users Needing Attention Widget */}
{usersNeedingAttention.length > 0 && ( {usersNeedingAttention.length > 0 && (
<div className="mt-12"> <div className="mt-12">
<Card className="p-8 bg-white rounded-2xl border-2 border-[#ff9e77] shadow-lg"> <Card className="p-8 bg-background rounded-2xl border-2 border-accent shadow-lg">
<div className="flex items-center gap-3 mb-6"> <div className="flex items-center gap-3 mb-6">
<div className="bg-[#ff9e77]/20 p-3 rounded-lg"> <div className="bg-accent/20 p-3 rounded-lg">
<AlertCircle className="h-6 w-6 text-[#ff9e77]" /> <AlertCircle className="h-6 w-6 text-accent" />
</div> </div>
<div> <div>
<h3 className="text-2xl font-semibold text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}> <h3 className="text-2xl font-semibold text-primary" style={{ fontFamily: "'Inter', sans-serif" }}>
Members Needing Personal Outreach Members Needing Personal Outreach
</h3> </h3>
<p className="text-sm text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-sm text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
These members have received multiple reminder emails. Consider calling them directly. These members have received multiple reminder emails. Consider calling them directly.
</p> </p>
</div> </div>
@@ -174,18 +174,18 @@ const AdminDashboard = () => {
<div className="space-y-4"> <div className="space-y-4">
{usersNeedingAttention.map(user => ( {usersNeedingAttention.map(user => (
<Link key={user.id} to={`/admin/users/${user.id}`}> <Link key={user.id} to={`/admin/users/${user.id}`}>
<div className="p-4 bg-[#F8F7FB] rounded-xl border border-[#ddd8eb] hover:border-[#ff9e77] hover:shadow-md transition-all cursor-pointer"> <div className="p-4 bg-chart-7 rounded-xl border border-chart-6 hover:border-accent hover:shadow-md transition-all cursor-pointer">
<div className="flex items-start justify-between gap-4"> <div className="flex items-start justify-between 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">
<h4 className="font-semibold text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}> <h4 className="font-semibold text-primary" style={{ fontFamily: "'Inter', sans-serif" }}>
{user.first_name} {user.last_name} {user.first_name} {user.last_name}
</h4> </h4>
<Badge className="bg-[#ff9e77] text-white px-3 py-1 rounded-full text-xs"> <Badge className="bg-accent text-white px-3 py-1 rounded-full text-xs">
{user.totalReminders} reminder{user.totalReminders !== 1 ? 's' : ''} {user.totalReminders} reminder{user.totalReminders !== 1 ? 's' : ''}
</Badge> </Badge>
</div> </div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-2 text-sm text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <div className="grid grid-cols-1 md:grid-cols-2 gap-2 text-sm text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<p>Email: {user.email}</p> <p>Email: {user.email}</p>
<p>Phone: {user.phone || 'N/A'}</p> <p>Phone: {user.phone || 'N/A'}</p>
<p className="capitalize">Status: {user.status.replace('_', ' ')}</p> <p className="capitalize">Status: {user.status.replace('_', ' ')}</p>
@@ -210,7 +210,7 @@ const AdminDashboard = () => {
</div> </div>
</div> </div>
<Button <Button
className="bg-[#DDD8EB] text-[#422268] hover:bg-white rounded-full text-sm" className="bg-chart-6 text-primary hover:bg-background rounded-full text-sm"
onClick={(e) => { onClick={(e) => {
e.preventDefault(); e.preventDefault();
window.location.href = `tel:${user.phone}`; window.location.href = `tel:${user.phone}`;
@@ -224,8 +224,8 @@ const AdminDashboard = () => {
))} ))}
</div> </div>
<div className="mt-6 p-4 bg-[#DDD8EB]/20 rounded-lg border border-[#ddd8eb]"> <div className="mt-6 p-4 bg-chart-6/20 rounded-lg border border-chart-6">
<p className="text-sm text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-sm text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<strong>💡 Tip for helping older members:</strong> Many of our members are older ladies who may struggle with email. <strong>💡 Tip for helping older members:</strong> Many of our members are older ladies who may struggle with email.
A friendly phone call can help them complete the registration process and feel more welcomed to the community. A friendly phone call can help them complete the registration process and feel more welcomed to the community.
</p> </p>

View File

@@ -167,13 +167,13 @@ const AdminDonations = () => {
}; };
const getTypeBadgeColor = (type) => { const getTypeBadgeColor = (type) => {
return type === 'member' ? 'bg-[#81B29A]' : 'bg-[#664fa3]'; return type === 'member' ? 'bg-[#81B29A]' : 'bg-muted-foreground';
}; };
if (loading) { if (loading) {
return ( return (
<div className="flex items-center justify-center min-h-screen"> <div className="flex items-center justify-center min-h-screen">
<Loader2 className="h-12 w-12 animate-spin text-[#664fa3]" /> <Loader2 className="h-12 w-12 animate-spin text-muted-foreground" />
</div> </div>
); );
} }
@@ -182,36 +182,36 @@ const AdminDonations = () => {
<div className="space-y-8"> <div className="space-y-8">
{/* Header */} {/* Header */}
<div> <div>
<h1 className="text-3xl font-semibold text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}> <h1 className="text-3xl font-semibold text-primary" style={{ fontFamily: "'Inter', sans-serif" }}>
Donation Management Donation Management
</h1> </h1>
<p className="text-[#664fa3] mt-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-muted-foreground mt-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Track and manage all donations from members and the public Track and manage all donations from members and the public
</p> </p>
</div> </div>
{/* Stats Cards */} {/* Stats Cards */}
<div className="grid md:grid-cols-4 gap-6"> <div className="grid md:grid-cols-4 gap-6">
<Card className="p-6 bg-white rounded-2xl border-2 border-[#ddd8eb]"> <Card className="p-6 bg-background rounded-2xl border-2 border-chart-6">
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<div> <div>
<p className="text-sm text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-sm text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Total Donations Total Donations
</p> </p>
<p className="text-3xl font-bold text-[#422268] mt-2" style={{ fontFamily: "'Inter', sans-serif" }}> <p className="text-3xl font-bold text-primary mt-2" style={{ fontFamily: "'Inter', sans-serif" }}>
{stats.total_donations || 0} {stats.total_donations || 0}
</p> </p>
</div> </div>
<div className="p-3 bg-[#DDD8EB]/20 rounded-full"> <div className="p-3 bg-chart-6/20 rounded-full">
<Heart className="h-6 w-6 text-[#664fa3]" /> <Heart className="h-6 w-6 text-muted-foreground" />
</div> </div>
</div> </div>
</Card> </Card>
<Card className="p-6 bg-white rounded-2xl border-2 border-[#ddd8eb]"> <Card className="p-6 bg-background rounded-2xl border-2 border-chart-6">
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<div> <div>
<p className="text-sm text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-sm text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Member Donations Member Donations
</p> </p>
<p className="text-3xl font-bold text-[#81B29A] mt-2" style={{ fontFamily: "'Inter', sans-serif" }}> <p className="text-3xl font-bold text-[#81B29A] mt-2" style={{ fontFamily: "'Inter', sans-serif" }}>
@@ -224,51 +224,51 @@ const AdminDonations = () => {
</div> </div>
</Card> </Card>
<Card className="p-6 bg-white rounded-2xl border-2 border-[#ddd8eb]"> <Card className="p-6 bg-background rounded-2xl border-2 border-chart-6">
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<div> <div>
<p className="text-sm text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-sm text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Public Donations Public Donations
</p> </p>
<p className="text-3xl font-bold text-[#664fa3] mt-2" style={{ fontFamily: "'Inter', sans-serif" }}> <p className="text-3xl font-bold text-muted-foreground mt-2" style={{ fontFamily: "'Inter', sans-serif" }}>
{stats.public_donations || 0} {stats.public_donations || 0}
</p> </p>
</div> </div>
<div className="p-3 bg-[#DDD8EB]/20 rounded-full"> <div className="p-3 bg-chart-6/20 rounded-full">
<Globe className="h-6 w-6 text-[#664fa3]" /> <Globe className="h-6 w-6 text-muted-foreground" />
</div> </div>
</div> </div>
</Card> </Card>
<Card className="p-6 bg-white rounded-2xl border-2 border-[#ddd8eb]"> <Card className="p-6 bg-background rounded-2xl border-2 border-chart-6">
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<div> <div>
<p className="text-sm text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-sm text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Total Amount Total Amount
</p> </p>
<p className="text-3xl font-bold text-[#422268] mt-2" style={{ fontFamily: "'Inter', sans-serif" }}> <p className="text-3xl font-bold text-primary mt-2" style={{ fontFamily: "'Inter', sans-serif" }}>
{stats.total_amount || '$0.00'} {stats.total_amount || '$0.00'}
</p> </p>
</div> </div>
<div className="p-3 bg-[#DDD8EB]/20 rounded-full"> <div className="p-3 bg-chart-6/20 rounded-full">
<DollarSign className="h-6 w-6 text-[#664fa3]" /> <DollarSign className="h-6 w-6 text-muted-foreground" />
</div> </div>
</div> </div>
</Card> </Card>
</div> </div>
{/* Filters and Actions */} {/* Filters and Actions */}
<Card className="p-6 bg-white rounded-2xl border-2 border-[#ddd8eb]"> <Card className="p-6 bg-background rounded-2xl border-2 border-chart-6">
<div className="space-y-4"> <div className="space-y-4">
{/* Search and Export Row */} {/* Search and Export Row */}
<div className="flex flex-col md:flex-row gap-4 justify-between"> <div className="flex flex-col md:flex-row gap-4 justify-between">
<div className="flex-1 relative"> <div className="flex-1 relative">
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 h-5 w-5 text-[#664fa3]" /> <Search className="absolute left-3 top-1/2 transform -translate-y-1/2 h-5 w-5 text-muted-foreground" />
<Input <Input
placeholder="Search by donor name or email..." placeholder="Search by donor name or email..."
value={searchQuery} value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)} onChange={(e) => setSearchQuery(e.target.value)}
className="pl-10 rounded-full border-2 border-[#ddd8eb] focus:border-[#664fa3]" className="pl-10 rounded-full border-2 border-chart-6 focus:border-muted-foreground"
/> />
</div> </div>
{hasPermission('donations.export') && ( {hasPermission('donations.export') && (
@@ -282,20 +282,20 @@ const AdminDonations = () => {
{exporting ? 'Exporting...' : 'Export'} {exporting ? 'Exporting...' : 'Export'}
</Button> </Button>
</DropdownMenuTrigger> </DropdownMenuTrigger>
<DropdownMenuContent align="end" className="w-56 bg-white rounded-xl border-2 border-[#ddd8eb] shadow-lg"> <DropdownMenuContent align="end" className="w-56 bg-background rounded-xl border-2 border-chart-6 shadow-lg">
<DropdownMenuItem <DropdownMenuItem
onClick={() => handleExport('all')} onClick={() => handleExport('all')}
className="cursor-pointer hover:bg-[#f1eef9] rounded-lg p-3" className="cursor-pointer hover:bg-muted rounded-lg p-3"
> >
<FileDown className="h-4 w-4 mr-2 text-[#664fa3]" /> <FileDown className="h-4 w-4 mr-2 text-muted-foreground" />
<span className="text-[#422268]">Export All Donations</span> <span className="text-primary">Export All Donations</span>
</DropdownMenuItem> </DropdownMenuItem>
<DropdownMenuItem <DropdownMenuItem
onClick={() => handleExport('current')} onClick={() => handleExport('current')}
className="cursor-pointer hover:bg-[#f1eef9] rounded-lg p-3" className="cursor-pointer hover:bg-muted rounded-lg p-3"
> >
<FileDown className="h-4 w-4 mr-2 text-[#664fa3]" /> <FileDown className="h-4 w-4 mr-2 text-muted-foreground" />
<span className="text-[#422268]">Export Current View</span> <span className="text-primary">Export Current View</span>
</DropdownMenuItem> </DropdownMenuItem>
</DropdownMenuContent> </DropdownMenuContent>
</DropdownMenu> </DropdownMenu>
@@ -306,7 +306,7 @@ const AdminDonations = () => {
<div className="grid grid-cols-1 md:grid-cols-4 gap-4"> <div className="grid grid-cols-1 md:grid-cols-4 gap-4">
<div> <div>
<Select value={typeFilter} onValueChange={setTypeFilter}> <Select value={typeFilter} onValueChange={setTypeFilter}>
<SelectTrigger className="rounded-full border-2 border-[#ddd8eb]"> <SelectTrigger className="rounded-full border-2 border-chart-6">
<SelectValue placeholder="All Types" /> <SelectValue placeholder="All Types" />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
@@ -319,7 +319,7 @@ const AdminDonations = () => {
<div> <div>
<Select value={statusFilter} onValueChange={setStatusFilter}> <Select value={statusFilter} onValueChange={setStatusFilter}>
<SelectTrigger className="rounded-full border-2 border-[#ddd8eb]"> <SelectTrigger className="rounded-full border-2 border-chart-6">
<SelectValue placeholder="All Statuses" /> <SelectValue placeholder="All Statuses" />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
@@ -336,7 +336,7 @@ const AdminDonations = () => {
type="date" type="date"
value={startDate} value={startDate}
onChange={(e) => setStartDate(e.target.value)} onChange={(e) => setStartDate(e.target.value)}
className="rounded-full border-2 border-[#ddd8eb]" className="rounded-full border-2 border-chart-6"
placeholder="Start Date" placeholder="Start Date"
/> />
</div> </div>
@@ -346,7 +346,7 @@ const AdminDonations = () => {
type="date" type="date"
value={endDate} value={endDate}
onChange={(e) => setEndDate(e.target.value)} onChange={(e) => setEndDate(e.target.value)}
className="rounded-full border-2 border-[#ddd8eb]" className="rounded-full border-2 border-chart-6"
placeholder="End Date" placeholder="End Date"
/> />
</div> </div>
@@ -354,7 +354,7 @@ const AdminDonations = () => {
{/* Active Filters Summary */} {/* Active Filters Summary */}
{(searchQuery || typeFilter !== 'all' || statusFilter !== 'all' || startDate || endDate) && ( {(searchQuery || typeFilter !== 'all' || statusFilter !== 'all' || startDate || endDate) && (
<div className="text-sm text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <div className="text-sm text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Showing {filteredDonations.length} of {donations.length} donations Showing {filteredDonations.length} of {donations.length} donations
</div> </div>
)} )}
@@ -362,38 +362,38 @@ const AdminDonations = () => {
</Card> </Card>
{/* Donations Table */} {/* Donations Table */}
<Card className="bg-white rounded-2xl border-2 border-[#ddd8eb] overflow-hidden"> <Card className="bg-background rounded-2xl border-2 border-chart-6 overflow-hidden">
<div className="overflow-x-auto"> <div className="overflow-x-auto">
<table className="w-full"> <table className="w-full">
<thead className="bg-[#f1eef9] border-b-2 border-[#ddd8eb]"> <thead className="bg-muted border-b-2 border-chart-6">
<tr> <tr>
<th className="px-6 py-4 text-left text-sm font-semibold text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}> <th className="px-6 py-4 text-left text-sm font-semibold text-primary" style={{ fontFamily: "'Inter', sans-serif" }}>
Donor Donor
</th> </th>
<th className="px-6 py-4 text-left text-sm font-semibold text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}> <th className="px-6 py-4 text-left text-sm font-semibold text-primary" style={{ fontFamily: "'Inter', sans-serif" }}>
Type Type
</th> </th>
<th className="px-6 py-4 text-left text-sm font-semibold text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}> <th className="px-6 py-4 text-left text-sm font-semibold text-primary" style={{ fontFamily: "'Inter', sans-serif" }}>
Amount Amount
</th> </th>
<th className="px-6 py-4 text-left text-sm font-semibold text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}> <th className="px-6 py-4 text-left text-sm font-semibold text-primary" style={{ fontFamily: "'Inter', sans-serif" }}>
Status Status
</th> </th>
<th className="px-6 py-4 text-left text-sm font-semibold text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}> <th className="px-6 py-4 text-left text-sm font-semibold text-primary" style={{ fontFamily: "'Inter', sans-serif" }}>
Date Date
</th> </th>
<th className="px-6 py-4 text-left text-sm font-semibold text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}> <th className="px-6 py-4 text-left text-sm font-semibold text-primary" style={{ fontFamily: "'Inter', sans-serif" }}>
Payment Method Payment Method
</th> </th>
</tr> </tr>
</thead> </thead>
<tbody className="divide-y divide-[#ddd8eb]"> <tbody className="divide-y divide-chart-6">
{filteredDonations.length === 0 ? ( {filteredDonations.length === 0 ? (
<tr> <tr>
<td colSpan="6" className="px-6 py-12 text-center"> <td colSpan="6" className="px-6 py-12 text-center">
<div className="flex flex-col items-center gap-3"> <div className="flex flex-col items-center gap-3">
<Heart className="h-12 w-12 text-[#ddd8eb]" /> <Heart className="h-12 w-12 text-chart-6" />
<p className="text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{donations.length === 0 ? 'No donations yet' : 'No donations match your filters'} {donations.length === 0 ? 'No donations yet' : 'No donations match your filters'}
</p> </p>
</div> </div>
@@ -404,10 +404,10 @@ const AdminDonations = () => {
<tr key={donation.id} className="hover:bg-[#f9f5ff] transition-colors"> <tr key={donation.id} className="hover:bg-[#f9f5ff] transition-colors">
<td className="px-6 py-4"> <td className="px-6 py-4">
<div> <div>
<p className="font-medium text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}> <p className="font-medium text-primary" style={{ fontFamily: "'Inter', sans-serif" }}>
{donation.donor_name || 'Anonymous'} {donation.donor_name || 'Anonymous'}
</p> </p>
<p className="text-sm text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-sm text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{donation.donor_email || 'No email'} {donation.donor_email || 'No email'}
</p> </p>
</div> </div>
@@ -421,7 +421,7 @@ const AdminDonations = () => {
</Badge> </Badge>
</td> </td>
<td className="px-6 py-4"> <td className="px-6 py-4">
<p className="font-semibold text-[#422268] text-lg" style={{ fontFamily: "'Inter', sans-serif" }}> <p className="font-semibold text-primary text-lg" style={{ fontFamily: "'Inter', sans-serif" }}>
{donation.amount} {donation.amount}
</p> </p>
</td> </td>
@@ -431,7 +431,7 @@ const AdminDonations = () => {
</Badge> </Badge>
</td> </td>
<td className="px-6 py-4"> <td className="px-6 py-4">
<div className="flex items-center gap-2 text-[#664fa3]"> <div className="flex items-center gap-2 text-muted-foreground">
<Calendar className="h-4 w-4" /> <Calendar className="h-4 w-4" />
<span style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <span style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{formatDate(donation.created_at)} {formatDate(donation.created_at)}
@@ -439,7 +439,7 @@ const AdminDonations = () => {
</div> </div>
</td> </td>
<td className="px-6 py-4"> <td className="px-6 py-4">
<p className="text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{donation.payment_method || 'N/A'} {donation.payment_method || 'N/A'}
</p> </p>
</td> </td>
@@ -453,18 +453,18 @@ const AdminDonations = () => {
{/* This Month Summary */} {/* This Month Summary */}
{stats.this_month_count > 0 && ( {stats.this_month_count > 0 && (
<Card className="p-6 bg-gradient-to-r from-[#f9f5ff] to-[#f1eef9] rounded-2xl border-2 border-[#ddd8eb]"> <Card className="p-6 bg-gradient-to-r from-[#f9f5ff] to-muted rounded-2xl border-2 border-chart-6">
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<div> <div>
<p className="text-[#664fa3] font-medium" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-muted-foreground font-medium" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
This Month's Donations This Month's Donations
</p> </p>
<p className="text-2xl font-bold text-[#422268] mt-2" style={{ fontFamily: "'Inter', sans-serif" }}> <p className="text-2xl font-bold text-primary mt-2" style={{ fontFamily: "'Inter', sans-serif" }}>
{stats.this_month_count} donations • {stats.this_month_amount} {stats.this_month_count} donations • {stats.this_month_amount}
</p> </p>
</div> </div>
<div className="p-4 bg-white rounded-full shadow-sm"> <div className="p-4 bg-background rounded-full shadow-sm">
<Heart className="h-8 w-8 text-[#ff9e77]" /> <Heart className="h-8 w-8 text-accent" />
</div> </div>
</div> </div>
</Card> </Card>

View File

@@ -213,7 +213,7 @@ const AdminEventAttendance = () => {
if (loading) { if (loading) {
return ( return (
<div className="flex items-center justify-center h-64"> <div className="flex items-center justify-center h-64">
<div className="text-[#664fa3]">Loading event data...</div> <div className="text-muted-foreground">Loading event data...</div>
</div> </div>
); );
} }
@@ -221,8 +221,8 @@ const AdminEventAttendance = () => {
if (!event) { if (!event) {
return ( return (
<div className="text-center py-12"> <div className="text-center py-12">
<p className="text-[#664fa3] mb-4">Event not found</p> <p className="text-muted-foreground mb-4">Event not found</p>
<Button onClick={() => navigate('/admin/events')} className="bg-[#664fa3] hover:bg-[#422268] text-white rounded-xl"> <Button onClick={() => navigate('/admin/events')} className="bg-muted-foreground hover:bg-primary text-white rounded-xl">
Back to Events Back to Events
</Button> </Button>
</div> </div>
@@ -237,17 +237,17 @@ const AdminEventAttendance = () => {
<Button <Button
onClick={() => navigate('/admin/events')} onClick={() => navigate('/admin/events')}
variant="outline" variant="outline"
className="border-[#ddd8eb] text-[#664fa3] rounded-xl" className="border-chart-6 text-muted-foreground rounded-xl"
style={{ fontFamily: "'Inter', sans-serif" }} style={{ fontFamily: "'Inter', sans-serif" }}
> >
<ArrowLeft className="h-4 w-4 mr-2" /> <ArrowLeft className="h-4 w-4 mr-2" />
Back to Events Back to Events
</Button> </Button>
<div> <div>
<h1 className="text-3xl font-semibold text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}> <h1 className="text-3xl font-semibold text-primary" style={{ fontFamily: "'Inter', sans-serif" }}>
Event Attendance Event Attendance
</h1> </h1>
<p className="text-[#664fa3] mt-1" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-muted-foreground mt-1" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Manage RSVPs and track attendance for this event Manage RSVPs and track attendance for this event
</p> </p>
</div> </div>
@@ -263,13 +263,13 @@ const AdminEventAttendance = () => {
</div> </div>
{/* Event Details Card */} {/* Event Details Card */}
<Card className="p-6 bg-white border-[#ddd8eb] rounded-xl"> <Card className="p-6 bg-background border-chart-6 rounded-xl">
<div className="flex items-start justify-between"> <div className="flex items-start justify-between">
<div> <div>
<h2 className="text-2xl font-semibold text-[#422268] mb-2" style={{ fontFamily: "'Inter', sans-serif" }}> <h2 className="text-2xl font-semibold text-primary mb-2" style={{ fontFamily: "'Inter', sans-serif" }}>
{event.title} {event.title}
</h2> </h2>
<div className="flex flex-wrap gap-4 text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <div className="flex flex-wrap gap-4 text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<Calendar className="h-4 w-4" /> <Calendar className="h-4 w-4" />
<span>{moment(event.start_at).format('MMMM D, YYYY [at] h:mm A')}</span> <span>{moment(event.start_at).format('MMMM D, YYYY [at] h:mm A')}</span>
@@ -282,7 +282,7 @@ const AdminEventAttendance = () => {
)} )}
</div> </div>
</div> </div>
<Badge className={`${event.published ? 'bg-[#81B29A]' : 'bg-[#ddd8eb]'} text-white px-3 py-1`}> <Badge className={`${event.published ? 'bg-[#81B29A]' : 'bg-chart-6'} text-white px-3 py-1`}>
{event.published ? 'Published' : 'Draft'} {event.published ? 'Published' : 'Draft'}
</Badge> </Badge>
</div> </div>
@@ -290,70 +290,69 @@ const AdminEventAttendance = () => {
{/* Statistics Cards */} {/* Statistics Cards */}
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-5 gap-4"> <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-5 gap-4">
<Card className="p-4 bg-white border-[#ddd8eb] rounded-xl"> <Card className="p-4 bg-background border-chart-6 rounded-xl">
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
<Users className="h-8 w-8 text-[#664fa3]" /> <Users className="h-8 w-8 text-muted-foreground" />
<div> <div>
<p className="text-sm text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Total RSVPs</p> <p className="text-sm text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Total RSVPs</p>
<p className="text-2xl font-semibold text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}>{stats.total}</p> <p className="text-2xl font-semibold text-primary" style={{ fontFamily: "'Inter', sans-serif" }}>{stats.total}</p>
</div> </div>
</div> </div>
</Card> </Card>
<Card className="p-4 bg-white border-[#ddd8eb] rounded-xl"> <Card className="p-4 bg-background border-chart-6 rounded-xl">
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
<UserCheck className="h-8 w-8 text-[#81B29A]" /> <UserCheck className="h-8 w-8 text-[#81B29A]" />
<div> <div>
<p className="text-sm text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Yes</p> <p className="text-sm text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Yes</p>
<p className="text-2xl font-semibold text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}>{stats.yesCount}</p> <p className="text-2xl font-semibold text-primary" style={{ fontFamily: "'Inter', sans-serif" }}>{stats.yesCount}</p>
</div> </div>
</div> </div>
</Card> </Card>
<Card className="p-4 bg-white border-[#ddd8eb] rounded-xl"> <Card className="p-4 bg-background border-chart-6 rounded-xl">
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
<UserX className="h-8 w-8 text-[#E07A5F]" /> <UserX className="h-8 w-8 text-[#E07A5F]" />
<div> <div>
<p className="text-sm text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>No</p> <p className="text-sm text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>No</p>
<p className="text-2xl font-semibold text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}>{stats.noCount}</p> <p className="text-2xl font-semibold text-primary" style={{ fontFamily: "'Inter', sans-serif" }}>{stats.noCount}</p>
</div> </div>
</div> </div>
</Card> </Card>
<Card className="p-4 bg-white border-[#ddd8eb] rounded-xl"> <Card className="p-4 bg-background border-chart-6 rounded-xl">
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
<HelpCircle className="h-8 w-8 text-[#F2CC8F]" /> <HelpCircle className="h-8 w-8 text-[#F2CC8F]" />
<div> <div>
<p className="text-sm text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Maybe</p> <p className="text-sm text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Maybe</p>
<p className="text-2xl font-semibold text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}>{stats.maybeCount}</p> <p className="text-2xl font-semibold text-primary" style={{ fontFamily: "'Inter', sans-serif" }}>{stats.maybeCount}</p>
</div> </div>
</div> </div>
</Card> </Card>
<Card className="p-4 bg-white border-[#ddd8eb] rounded-xl"> <Card className="p-4 bg-background border-chart-6 rounded-xl">
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
<Check className="h-8 w-8 text-[#664fa3]" /> <Check className="h-8 w-8 text-muted-foreground" />
<div> <div>
<p className="text-sm text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Attended</p> <p className="text-sm text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Attended</p>
<p className="text-2xl font-semibold text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}>{stats.attendedCount}</p> <p className="text-2xl font-semibold text-primary" style={{ fontFamily: "'Inter', sans-serif" }}>{stats.attendedCount}</p>
</div> </div>
</div> </div>
</Card> </Card>
</div> </div>
{/* Filters and Actions */} {/* Filters and Actions */}
<Card className="p-6 bg-white border-[#ddd8eb] rounded-xl"> <Card className="p-6 bg-background border-chart-6 rounded-xl">
<div className="space-y-4"> <div className="space-y-4">
{/* Tab Filters */} {/* Tab Filters */}
<div className="flex flex-wrap gap-2"> <div className="flex flex-wrap gap-2">
<Button <Button
onClick={() => setActiveTab('all')} onClick={() => setActiveTab('all')}
variant={activeTab === 'all' ? 'default' : 'outline'} variant={activeTab === 'all' ? 'default' : 'outline'}
className={`rounded-xl ${ className={`rounded-xl ${activeTab === 'all'
activeTab === 'all' ? 'bg-muted-foreground hover:bg-primary text-white'
? 'bg-[#664fa3] hover:bg-[#422268] text-white' : 'border-chart-6 text-muted-foreground hover:bg-chart-7'
: 'border-[#ddd8eb] text-[#664fa3] hover:bg-[#F8F7FB]' }`}
}`}
style={{ fontFamily: "'Inter', sans-serif" }} style={{ fontFamily: "'Inter', sans-serif" }}
> >
All ({stats.total}) All ({stats.total})
@@ -361,11 +360,10 @@ const AdminEventAttendance = () => {
<Button <Button
onClick={() => setActiveTab('yes')} onClick={() => setActiveTab('yes')}
variant={activeTab === 'yes' ? 'default' : 'outline'} variant={activeTab === 'yes' ? 'default' : 'outline'}
className={`rounded-xl ${ className={`rounded-xl ${activeTab === 'yes'
activeTab === 'yes' ? 'bg-[#81B29A] hover:bg-[#6a9a83] text-white'
? 'bg-[#81B29A] hover:bg-[#6a9a83] text-white' : 'border-chart-6 text-muted-foreground hover:bg-chart-7'
: 'border-[#ddd8eb] text-[#664fa3] hover:bg-[#F8F7FB]' }`}
}`}
style={{ fontFamily: "'Inter', sans-serif" }} style={{ fontFamily: "'Inter', sans-serif" }}
> >
Yes ({stats.yesCount}) Yes ({stats.yesCount})
@@ -373,11 +371,10 @@ const AdminEventAttendance = () => {
<Button <Button
onClick={() => setActiveTab('no')} onClick={() => setActiveTab('no')}
variant={activeTab === 'no' ? 'default' : 'outline'} variant={activeTab === 'no' ? 'default' : 'outline'}
className={`rounded-xl ${ className={`rounded-xl ${activeTab === 'no'
activeTab === 'no' ? 'bg-[#E07A5F] hover:bg-[#d16b54] text-white'
? 'bg-[#E07A5F] hover:bg-[#d16b54] text-white' : 'border-chart-6 text-muted-foreground hover:bg-chart-7'
: 'border-[#ddd8eb] text-[#664fa3] hover:bg-[#F8F7FB]' }`}
}`}
style={{ fontFamily: "'Inter', sans-serif" }} style={{ fontFamily: "'Inter', sans-serif" }}
> >
No ({stats.noCount}) No ({stats.noCount})
@@ -385,11 +382,10 @@ const AdminEventAttendance = () => {
<Button <Button
onClick={() => setActiveTab('maybe')} onClick={() => setActiveTab('maybe')}
variant={activeTab === 'maybe' ? 'default' : 'outline'} variant={activeTab === 'maybe' ? 'default' : 'outline'}
className={`rounded-xl ${ className={`rounded-xl ${activeTab === 'maybe'
activeTab === 'maybe' ? 'bg-[#F2CC8F] hover:bg-[#e8bf7a] text-primary'
? 'bg-[#F2CC8F] hover:bg-[#e8bf7a] text-[#422268]' : 'border-chart-6 text-muted-foreground hover:bg-chart-7'
: 'border-[#ddd8eb] text-[#664fa3] hover:bg-[#F8F7FB]' }`}
}`}
style={{ fontFamily: "'Inter', sans-serif" }} style={{ fontFamily: "'Inter', sans-serif" }}
> >
Maybe ({stats.maybeCount}) Maybe ({stats.maybeCount})
@@ -399,20 +395,20 @@ const AdminEventAttendance = () => {
{/* Search and Bulk Actions */} {/* Search and Bulk Actions */}
<div className="flex flex-wrap gap-3 items-center justify-between"> <div className="flex flex-wrap gap-3 items-center justify-between">
<div className="flex-1 min-w-[200px] max-w-md relative"> <div className="flex-1 min-w-[200px] max-w-md relative">
<Search className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-[#664fa3]" /> <Search className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" />
<Input <Input
type="text" type="text"
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-10 border-[#ddd8eb] rounded-xl" className="pl-10 border-chart-6 rounded-xl"
style={{ fontFamily: "'Nunito Sans', sans-serif" }} style={{ fontFamily: "'Nunito Sans', sans-serif" }}
/> />
</div> </div>
{selectedRsvps.size > 0 && ( {selectedRsvps.size > 0 && (
<div className="flex gap-2"> <div className="flex gap-2">
<Badge className="bg-[#664fa3] text-white px-3 py-1"> <Badge className="bg-muted-foreground text-white px-3 py-1">
{selectedRsvps.size} selected {selectedRsvps.size} selected
</Badge> </Badge>
<Button <Button
@@ -440,10 +436,10 @@ const AdminEventAttendance = () => {
</Card> </Card>
{/* RSVP Table */} {/* RSVP Table */}
<Card className="bg-white border-[#ddd8eb] rounded-xl overflow-hidden"> <Card className="bg-background border-chart-6 rounded-xl overflow-hidden">
<div className="overflow-x-auto"> <div className="overflow-x-auto">
<table className="w-full"> <table className="w-full">
<thead className="bg-[#F8F7FB] border-b border-[#ddd8eb]"> <thead className="bg-chart-7 border-b border-chart-6">
<tr> <tr>
<th className="px-4 py-3 text-left"> <th className="px-4 py-3 text-left">
<Checkbox <Checkbox
@@ -451,19 +447,19 @@ const AdminEventAttendance = () => {
onCheckedChange={handleSelectAll} onCheckedChange={handleSelectAll}
/> />
</th> </th>
<th className="px-4 py-3 text-left text-sm font-semibold text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}> <th className="px-4 py-3 text-left text-sm font-semibold text-primary" style={{ fontFamily: "'Inter', sans-serif" }}>
Name Name
</th> </th>
<th className="px-4 py-3 text-left text-sm font-semibold text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}> <th className="px-4 py-3 text-left text-sm font-semibold text-primary" style={{ fontFamily: "'Inter', sans-serif" }}>
Email Email
</th> </th>
<th className="px-4 py-3 text-left text-sm font-semibold text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}> <th className="px-4 py-3 text-left text-sm font-semibold text-primary" style={{ fontFamily: "'Inter', sans-serif" }}>
RSVP Status RSVP Status
</th> </th>
<th className="px-4 py-3 text-center text-sm font-semibold text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}> <th className="px-4 py-3 text-center text-sm font-semibold text-primary" style={{ fontFamily: "'Inter', sans-serif" }}>
Attendance Attendance
</th> </th>
<th className="px-4 py-3 text-left text-sm font-semibold text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}> <th className="px-4 py-3 text-left text-sm font-semibold text-primary" style={{ fontFamily: "'Inter', sans-serif" }}>
Attended At Attended At
</th> </th>
</tr> </tr>
@@ -471,28 +467,27 @@ const AdminEventAttendance = () => {
<tbody> <tbody>
{filteredRsvps.length > 0 ? ( {filteredRsvps.length > 0 ? (
filteredRsvps.map((rsvp) => ( filteredRsvps.map((rsvp) => (
<tr key={rsvp.user_id} className="border-b border-[#ddd8eb] hover:bg-[#F8F7FB] transition-colors"> <tr key={rsvp.user_id} className="border-b border-chart-6 hover:bg-chart-7 transition-colors">
<td className="px-4 py-3"> <td className="px-4 py-3">
<Checkbox <Checkbox
checked={selectedRsvps.has(rsvp.user_id)} checked={selectedRsvps.has(rsvp.user_id)}
onCheckedChange={() => handleSelectRsvp(rsvp.user_id)} onCheckedChange={() => handleSelectRsvp(rsvp.user_id)}
/> />
</td> </td>
<td className="px-4 py-3 text-sm text-[#422268]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <td className="px-4 py-3 text-sm text-primary" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{rsvp.user_name} {rsvp.user_name}
</td> </td>
<td className="px-4 py-3 text-sm text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <td className="px-4 py-3 text-sm text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{rsvp.user_email} {rsvp.user_email}
</td> </td>
<td className="px-4 py-3"> <td className="px-4 py-3">
<Badge <Badge
className={`${ className={`${rsvp.rsvp_status === 'yes'
rsvp.rsvp_status === 'yes' ? 'bg-[#81B29A]'
? 'bg-[#81B29A]' : rsvp.rsvp_status === 'no'
: rsvp.rsvp_status === 'no'
? 'bg-[#E07A5F]' ? 'bg-[#E07A5F]'
: 'bg-[#F2CC8F] text-[#422268]' : 'bg-[#F2CC8F] text-primary'
} text-white text-xs px-2 py-1`} } text-white text-xs px-2 py-1`}
> >
{rsvp.rsvp_status.toUpperCase()} {rsvp.rsvp_status.toUpperCase()}
</Badge> </Badge>
@@ -515,7 +510,7 @@ const AdminEventAttendance = () => {
disabled={saving} disabled={saving}
size="sm" size="sm"
variant="outline" variant="outline"
className="border-[#ddd8eb] text-[#664fa3] hover:bg-[#81B29A] hover:text-white hover:border-[#81B29A] rounded-lg min-w-[120px]" className="border-chart-6 text-muted-foreground hover:bg-[#81B29A] hover:text-white hover:border-[#81B29A] rounded-lg min-w-[120px]"
style={{ fontFamily: "'Inter', sans-serif" }} style={{ fontFamily: "'Inter', sans-serif" }}
> >
<X className="h-3 w-3 mr-1" /> <X className="h-3 w-3 mr-1" />
@@ -523,7 +518,7 @@ const AdminEventAttendance = () => {
</Button> </Button>
)} )}
</td> </td>
<td className="px-4 py-3 text-sm text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <td className="px-4 py-3 text-sm text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{rsvp.attended_at ? moment(rsvp.attended_at).format('MMM D, YYYY h:mm A') : '-'} {rsvp.attended_at ? moment(rsvp.attended_at).format('MMM D, YYYY h:mm A') : '-'}
</td> </td>
</tr> </tr>
@@ -531,7 +526,7 @@ const AdminEventAttendance = () => {
) : ( ) : (
<tr> <tr>
<td colSpan="6" className="px-4 py-12 text-center"> <td colSpan="6" className="px-4 py-12 text-center">
<p className="text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{searchQuery ? 'No RSVPs match your search' : 'No RSVPs for this filter'} {searchQuery ? 'No RSVPs match your search' : 'No RSVPs for this filter'}
</p> </p>
</td> </td>

View File

@@ -134,15 +134,15 @@ const AdminEvents = () => {
{/* Header */} {/* Header */}
<div className="mb-8 flex flex-col sm:flex-row gap-4 justify-between items-start sm:items-center"> <div className="mb-8 flex flex-col sm:flex-row gap-4 justify-between items-start sm:items-center">
<div> <div>
<h1 className="text-4xl md:text-5xl font-semibold text-[#422268] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}> <h1 className="text-4xl md:text-5xl font-semibold text-primary mb-4" style={{ fontFamily: "'Inter', sans-serif" }}>
Event Management Event Management
</h1> </h1>
<p className="text-lg text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-lg text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Create and manage community events. Create and manage community events.
</p> </p>
</div> </div>
{(hasPermission('events.create') || hasPermission('events.edit')) && ( {(hasPermission('events.create') || hasPermission('events.edit')) && (
<Dialog open={isCreateDialogOpen} onOpenChange={setIsCreateDialogOpen}> <Dialog open={isCreateDialogOpen} onOpenChange={setIsCreateDialogOpen}>
<DialogTrigger asChild> <DialogTrigger asChild>
<Button <Button
@@ -150,7 +150,7 @@ const AdminEvents = () => {
resetForm(); resetForm();
setEditingEvent(null); setEditingEvent(null);
}} }}
className="bg-[#DDD8EB] text-[#422268] hover:bg-white rounded-full px-6" className="bg-chart-6 text-primary hover:bg-background 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" />
@@ -160,39 +160,39 @@ const AdminEvents = () => {
<DialogContent className="max-w-[calc(100vw-2rem)] sm:max-w-2xl max-h-[90vh] overflow-y-auto"> <DialogContent className="max-w-[calc(100vw-2rem)] sm:max-w-2xl max-h-[90vh] overflow-y-auto">
<DialogHeader> <DialogHeader>
<DialogTitle className="text-2xl text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}> <DialogTitle className="text-2xl text-primary" 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-[#422268] mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <label className="block text-sm font-medium text-primary 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-[#ddd8eb] focus:border-[#664fa3]" className="border-2 border-chart-6 focus:border-muted-foreground"
/> />
</div> </div>
<div> <div>
<label className="block text-sm font-medium text-[#422268] mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <label className="block text-sm font-medium text-primary 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-[#ddd8eb] focus:border-[#664fa3] rounded-lg p-3" className="w-full border-2 border-chart-6 focus:border-muted-foreground rounded-lg p-3"
/> />
</div> </div>
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4"> <div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
<div> <div>
<label className="block text-sm font-medium text-[#422268] mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <label className="block text-sm font-medium text-primary mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Start Date & Time * Start Date & Time *
</label> </label>
<Input <Input
@@ -200,12 +200,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-[#ddd8eb] focus:border-[#664fa3]" className="border-2 border-chart-6 focus:border-muted-foreground"
/> />
</div> </div>
<div> <div>
<label className="block text-sm font-medium text-[#422268] mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <label className="block text-sm font-medium text-primary mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
End Date & Time * End Date & Time *
</label> </label>
<Input <Input
@@ -213,25 +213,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-[#ddd8eb] focus:border-[#664fa3]" className="border-2 border-chart-6 focus:border-muted-foreground"
/> />
</div> </div>
</div> </div>
<div> <div>
<label className="block text-sm font-medium text-[#422268] mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <label className="block text-sm font-medium text-primary 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-[#ddd8eb] focus:border-[#664fa3]" className="border-2 border-chart-6 focus:border-muted-foreground"
/> />
</div> </div>
<div> <div>
<label className="block text-sm font-medium text-[#422268] mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <label className="block text-sm font-medium text-primary mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Capacity (optional) Capacity (optional)
</label> </label>
<Input <Input
@@ -239,7 +239,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-[#ddd8eb] focus:border-[#664fa3]" className="border-2 border-chart-6 focus:border-muted-foreground"
/> />
</div> </div>
@@ -249,9 +249,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-[#664fa3] border-[#ddd8eb] rounded focus:ring-[#664fa3]" className="w-4 h-4 text-muted-foreground border-chart-6 rounded focus:ring-muted-foreground"
/> />
<label htmlFor="published" className="text-sm font-medium text-[#422268]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <label htmlFor="published" className="text-sm font-medium text-primary" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Publish event (make visible to members) Publish event (make visible to members)
</label> </label>
</div> </div>
@@ -267,7 +267,7 @@ const AdminEvents = () => {
</Button> </Button>
<Button <Button
type="submit" type="submit"
className="flex-1 bg-[#DDD8EB] text-[#422268] hover:bg-white rounded-full" className="flex-1 bg-chart-6 text-primary hover:bg-background rounded-full"
> >
{editingEvent ? 'Update Event' : 'Create Event'} {editingEvent ? 'Update Event' : 'Create Event'}
</Button> </Button>
@@ -275,146 +275,145 @@ const AdminEvents = () => {
</form> </form>
</DialogContent> </DialogContent>
</Dialog> </Dialog>
)} )}
</div>
{/* Events List */}
{loading ? (
<div className="text-center py-20">
<p className="text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Loading events...</p>
</div> </div>
) : events.length > 0 ? (
{/* Events List */} <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
{loading ? ( {events.map((event) => (
<div className="text-center py-20"> <Card
<p className="text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Loading events...</p> key={event.id}
</div> className="p-6 bg-background rounded-2xl border border-chart-6 hover:shadow-lg transition-all"
) : events.length > 0 ? ( data-testid={`event-card-${event.id}`}
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6"> >
{events.map((event) => ( {/* Event Header */}
<Card <div className="flex justify-between items-start mb-4">
key={event.id} <div className="bg-chart-6/20 p-3 rounded-lg">
className="p-6 bg-white rounded-2xl border border-[#ddd8eb] hover:shadow-lg transition-all" <Calendar className="h-6 w-6 text-muted-foreground" />
data-testid={`event-card-${event.id}`} </div>
> <Badge
{/* Event Header */} className={`${event.published
<div className="flex justify-between items-start mb-4"> ? 'bg-[#81B29A] text-white'
<div className="bg-[#DDD8EB]/20 p-3 rounded-lg"> : 'bg-gray-400 text-white'
<Calendar className="h-6 w-6 text-[#664fa3]" />
</div>
<Badge
className={`${
event.published
? 'bg-[#81B29A] 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'}
</Badge> </Badge>
</div>
{/* Event Details */}
<h3 className="text-xl font-semibold text-primary mb-3" style={{ fontFamily: "'Inter', sans-serif" }}>
{event.title}
</h3>
{event.description && (
<p className="text-muted-foreground mb-4 line-clamp-2 text-sm" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{event.description}
</p>
)}
<div className="space-y-2 mb-4">
<div className="flex items-center gap-2 text-sm text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<Calendar className="h-4 w-4" />
<span>
{new Date(event.start_at).toLocaleDateString()} at{' '}
{new Date(event.start_at).toLocaleTimeString([], {
hour: '2-digit',
minute: '2-digit'
})}
</span>
</div> </div>
<div className="flex items-center gap-2 text-sm text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{/* Event Details */} <MapPin className="h-4 w-4" />
<h3 className="text-xl font-semibold text-[#422268] mb-3" style={{ fontFamily: "'Inter', sans-serif" }}> <span className="truncate">{event.location}</span>
{event.title}
</h3>
{event.description && (
<p className="text-[#664fa3] mb-4 line-clamp-2 text-sm" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{event.description}
</p>
)}
<div className="space-y-2 mb-4">
<div className="flex items-center gap-2 text-sm text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<Calendar className="h-4 w-4" />
<span>
{new Date(event.start_at).toLocaleDateString()} at{' '}
{new Date(event.start_at).toLocaleTimeString([], {
hour: '2-digit',
minute: '2-digit'
})}
</span>
</div>
<div className="flex items-center gap-2 text-sm text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<MapPin className="h-4 w-4" />
<span className="truncate">{event.location}</span>
</div>
<div className="flex items-center gap-2 text-sm text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<Users className="h-4 w-4" />
<span>{event.rsvp_count || 0} attending</span>
</div>
</div> </div>
<div className="flex items-center gap-2 text-sm text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<Users className="h-4 w-4" />
<span>{event.rsvp_count || 0} attending</span>
</div>
</div>
{/* Actions */} {/* Actions */}
<div className="space-y-2 pt-4 border-t border-[#ddd8eb]"> <div className="space-y-2 pt-4 border-t border-chart-6">
{/* Manage Attendance Button */} {/* Manage Attendance Button */}
<Button
onClick={() => navigate(`/admin/events/${event.id}/attendance`)}
variant="outline"
size="sm"
className="w-full border-[#81B29A] text-[#81B29A] hover:bg-[#81B29A] hover:text-white"
data-testid={`mark-attendance-${event.id}`}
>
<Users className="h-4 w-4 mr-2" />
Manage Attendance ({event.rsvp_count || 0} RSVPs)
</Button>
{/* Other Actions */}
<div className="flex gap-2">
<Button <Button
onClick={() => navigate(`/admin/events/${event.id}/attendance`)} onClick={() => togglePublish(event)}
variant="outline" variant="outline"
size="sm" size="sm"
className="w-full border-[#81B29A] text-[#81B29A] hover:bg-[#81B29A] hover:text-white" className="flex-1 border-muted-foreground text-muted-foreground hover:bg-muted-foreground hover:text-white"
data-testid={`mark-attendance-${event.id}`} data-testid={`toggle-publish-${event.id}`}
> >
<Users className="h-4 w-4 mr-2" /> {event.published ? (
Manage Attendance ({event.rsvp_count || 0} RSVPs) <>
<EyeOff className="h-4 w-4 mr-1" />
Unpublish
</>
) : (
<>
<Eye className="h-4 w-4 mr-1" />
Publish
</>
)}
</Button>
<Button
onClick={() => handleEdit(event)}
variant="outline"
size="sm"
className="border-gray-400 text-gray-600 hover:bg-gray-400 hover:text-white"
data-testid={`edit-event-${event.id}`}
>
<Edit className="h-4 w-4" />
</Button>
<Button
onClick={() => handleDelete(event.id)}
variant="outline"
size="sm"
className="border-red-500 text-red-500 hover:bg-red-500 hover:text-white"
data-testid={`delete-event-${event.id}`}
>
<Trash2 className="h-4 w-4" />
</Button> </Button>
{/* Other Actions */}
<div className="flex gap-2">
<Button
onClick={() => togglePublish(event)}
variant="outline"
size="sm"
className="flex-1 border-[#664fa3] text-[#664fa3] hover:bg-[#664fa3] hover:text-white"
data-testid={`toggle-publish-${event.id}`}
>
{event.published ? (
<>
<EyeOff className="h-4 w-4 mr-1" />
Unpublish
</>
) : (
<>
<Eye className="h-4 w-4 mr-1" />
Publish
</>
)}
</Button>
<Button
onClick={() => handleEdit(event)}
variant="outline"
size="sm"
className="border-gray-400 text-gray-600 hover:bg-gray-400 hover:text-white"
data-testid={`edit-event-${event.id}`}
>
<Edit className="h-4 w-4" />
</Button>
<Button
onClick={() => handleDelete(event.id)}
variant="outline"
size="sm"
className="border-red-500 text-red-500 hover:bg-red-500 hover:text-white"
data-testid={`delete-event-${event.id}`}
>
<Trash2 className="h-4 w-4" />
</Button>
</div>
</div> </div>
</Card> </div>
))} </Card>
</div> ))}
) : ( </div>
<div className="text-center py-20"> ) : (
<Calendar className="h-20 w-20 text-[#ddd8eb] mx-auto mb-6" /> <div className="text-center py-20">
<h3 className="text-2xl font-semibold text-[#422268] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}> <Calendar className="h-20 w-20 text-chart-6 mx-auto mb-6" />
No Events Yet <h3 className="text-2xl font-semibold text-primary mb-4" style={{ fontFamily: "'Inter', sans-serif" }}>
</h3> No Events Yet
<p className="text-[#664fa3] mb-6" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> </h3>
Create your first event to get started! <p className="text-muted-foreground mb-6" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
</p> Create your first event to get started!
<Button </p>
onClick={() => setIsCreateDialogOpen(true)} <Button
className="bg-[#DDD8EB] text-[#422268] hover:bg-white rounded-full px-8" onClick={() => setIsCreateDialogOpen(true)}
> className="bg-chart-6 text-primary hover:bg-background rounded-full px-8"
<Plus className="mr-2 h-5 w-5" /> >
Create Event <Plus className="mr-2 h-5 w-5" />
</Button> Create Event
</div> </Button>
)} </div>
)}
</> </>
); );
}; };

View File

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

View File

@@ -153,23 +153,23 @@ const AdminGallery = () => {
<div className="space-y-6"> <div className="space-y-6">
{/* Header */} {/* Header */}
<div> <div>
<h1 className="text-3xl font-semibold text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}> <h1 className="text-3xl font-semibold text-primary" style={{ fontFamily: "'Inter', sans-serif" }}>
Event Gallery Management Event Gallery Management
</h1> </h1>
<p className="text-[#664fa3] mt-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-muted-foreground mt-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Upload and manage photos for event galleries Upload and manage photos for event galleries
</p> </p>
</div> </div>
{/* Event Selection */} {/* Event Selection */}
<Card className="p-6 bg-white border-[#ddd8eb] rounded-xl"> <Card className="p-6 bg-background border-chart-6 rounded-xl">
<div className="space-y-4"> <div className="space-y-4">
<div> <div>
<Label className="text-[#422268] mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <Label className="text-primary mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Select Event Select Event
</Label> </Label>
<Select value={selectedEvent || ''} onValueChange={setSelectedEvent}> <Select value={selectedEvent || ''} onValueChange={setSelectedEvent}>
<SelectTrigger className="border-[#ddd8eb] rounded-xl"> <SelectTrigger className="border-chart-6 rounded-xl">
<SelectValue placeholder="Choose an event..." /> <SelectValue placeholder="Choose an event..." />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
@@ -184,19 +184,19 @@ const AdminGallery = () => {
{/* Empty State Message */} {/* Empty State Message */}
{events.length === 0 && ( {events.length === 0 && (
<div className="mt-4 p-4 bg-[#f1eef9] border-2 border-[#DDD8EB] rounded-xl"> <div className="mt-4 p-4 bg-muted border-2 border-chart-6 rounded-xl">
<div className="flex items-start gap-3"> <div className="flex items-start gap-3">
<AlertCircle className="h-5 w-5 text-[#664fa3] flex-shrink-0 mt-0.5" /> <AlertCircle className="h-5 w-5 text-muted-foreground flex-shrink-0 mt-0.5" />
<div className="flex-1"> <div className="flex-1">
<h4 className="text-sm font-semibold text-[#422268] mb-1" style={{ fontFamily: "'Inter', sans-serif" }}> <h4 className="text-sm font-semibold text-primary mb-1" style={{ fontFamily: "'Inter', sans-serif" }}>
No Events Available No Events Available
</h4> </h4>
<p className="text-sm text-[#664fa3] mb-3" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-sm text-muted-foreground mb-3" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
You need to create an event before uploading gallery images. Events help organize photos by occasion. You need to create an event before uploading gallery images. Events help organize photos by occasion.
</p> </p>
<Link to="/admin/events"> <Link to="/admin/events">
<Button <Button
className="bg-[#664fa3] hover:bg-[#422268] text-white rounded-xl text-sm" className="bg-muted-foreground hover:bg-primary text-white rounded-xl text-sm"
style={{ fontFamily: "'Inter', sans-serif" }} style={{ fontFamily: "'Inter', sans-serif" }}
> >
<Calendar className="h-4 w-4 mr-2" /> <Calendar className="h-4 w-4 mr-2" />
@@ -221,7 +221,7 @@ const AdminGallery = () => {
<Button <Button
onClick={() => fileInputRef.current?.click()} onClick={() => fileInputRef.current?.click()}
disabled={uploading} disabled={uploading}
className="bg-[#664fa3] hover:bg-[#422268] text-white rounded-xl" className="bg-muted-foreground hover:bg-primary text-white rounded-xl"
style={{ fontFamily: "'Inter', sans-serif" }} style={{ fontFamily: "'Inter', sans-serif" }}
> >
{uploading ? ( {uploading ? (
@@ -236,7 +236,7 @@ const AdminGallery = () => {
</> </>
)} )}
</Button> </Button>
<p className="text-sm text-[#664fa3] mt-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-sm text-muted-foreground mt-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
You can select multiple images. Max {formatFileSize(maxFileSize)} per image. You can select multiple images. Max {formatFileSize(maxFileSize)} per image.
</p> </p>
</div> </div>
@@ -246,12 +246,12 @@ const AdminGallery = () => {
{/* Gallery Grid */} {/* Gallery Grid */}
{selectedEvent && ( {selectedEvent && (
<Card className="p-6 bg-white border-[#ddd8eb] rounded-xl"> <Card className="p-6 bg-background border-chart-6 rounded-xl">
<div className="flex items-center justify-between mb-6"> <div className="flex items-center justify-between mb-6">
<h2 className="text-xl font-semibold text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}> <h2 className="text-xl font-semibold text-primary" style={{ fontFamily: "'Inter', sans-serif" }}>
Gallery Images Gallery Images
</h2> </h2>
<Badge className="bg-[#664fa3] text-white px-3 py-1"> <Badge className="bg-muted-foreground text-white px-3 py-1">
{galleryImages.length} {galleryImages.length === 1 ? 'image' : 'images'} {galleryImages.length} {galleryImages.length === 1 ? 'image' : 'images'}
</Badge> </Badge>
</div> </div>
@@ -260,7 +260,7 @@ const AdminGallery = () => {
<div className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4"> <div className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4">
{galleryImages.map((image) => ( {galleryImages.map((image) => (
<div key={image.id} className="relative group"> <div key={image.id} className="relative group">
<div className="aspect-square rounded-xl overflow-hidden bg-[#F8F7FB]"> <div className="aspect-square rounded-xl overflow-hidden bg-chart-7">
<img <img
src={image.image_url} src={image.image_url}
alt={image.caption || 'Gallery image'} alt={image.caption || 'Gallery image'}
@@ -275,7 +275,7 @@ const AdminGallery = () => {
<Button <Button
onClick={() => openEditCaption(image)} onClick={() => openEditCaption(image)}
size="sm" size="sm"
className="bg-white/90 hover:bg-white text-[#422268] rounded-lg" className="bg-background/90 hover:bg-background text-primary rounded-lg"
style={{ fontFamily: "'Inter', sans-serif" }} style={{ fontFamily: "'Inter', sans-serif" }}
> >
<Edit className="h-4 w-4 mr-1" /> <Edit className="h-4 w-4 mr-1" />
@@ -299,7 +299,7 @@ const AdminGallery = () => {
{/* Caption Preview */} {/* Caption Preview */}
{image.caption && ( {image.caption && (
<div className="mt-2"> <div className="mt-2">
<p className="text-sm text-[#664fa3] line-clamp-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-sm text-muted-foreground line-clamp-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{image.caption} {image.caption}
</p> </p>
</div> </div>
@@ -307,7 +307,7 @@ const AdminGallery = () => {
{/* File Size */} {/* File Size */}
<div className="mt-1"> <div className="mt-1">
<p className="text-xs text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-xs text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{formatFileSize(image.file_size_bytes)} {formatFileSize(image.file_size_bytes)}
</p> </p>
</div> </div>
@@ -316,11 +316,11 @@ const AdminGallery = () => {
</div> </div>
) : ( ) : (
<div className="text-center py-16"> <div className="text-center py-16">
<ImageIcon className="h-16 w-16 text-[#ddd8eb] mx-auto mb-4" /> <ImageIcon className="h-16 w-16 text-chart-6 mx-auto mb-4" />
<h3 className="text-lg font-semibold text-[#422268] mb-2" style={{ fontFamily: "'Inter', sans-serif" }}> <h3 className="text-lg font-semibold text-primary mb-2" style={{ fontFamily: "'Inter', sans-serif" }}>
No Images Yet No Images Yet
</h3> </h3>
<p className="text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Upload images to create a gallery for this event. Upload images to create a gallery for this event.
</p> </p>
</div> </div>
@@ -330,16 +330,16 @@ const AdminGallery = () => {
{/* Edit Caption Dialog */} {/* Edit Caption Dialog */}
<Dialog open={!!editingCaption} onOpenChange={() => setEditingCaption(null)}> <Dialog open={!!editingCaption} onOpenChange={() => setEditingCaption(null)}>
<DialogContent className="bg-white sm:max-w-[600px]"> <DialogContent className="bg-background sm:max-w-[600px]">
<DialogHeader> <DialogHeader>
<DialogTitle className="text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}> <DialogTitle className="text-primary" style={{ fontFamily: "'Inter', sans-serif" }}>
Edit Image Caption Edit Image Caption
</DialogTitle> </DialogTitle>
</DialogHeader> </DialogHeader>
{editingCaption && ( {editingCaption && (
<div className="space-y-4"> <div className="space-y-4">
<div className="aspect-video rounded-xl overflow-hidden bg-[#F8F7FB]"> <div className="aspect-video rounded-xl overflow-hidden bg-chart-7">
<img <img
src={editingCaption.image_url} src={editingCaption.image_url}
alt="Preview" alt="Preview"
@@ -348,7 +348,7 @@ const AdminGallery = () => {
</div> </div>
<div> <div>
<Label className="text-[#422268]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <Label className="text-primary" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Caption Caption
</Label> </Label>
<Textarea <Textarea
@@ -356,7 +356,7 @@ const AdminGallery = () => {
onChange={(e) => setNewCaption(e.target.value)} onChange={(e) => setNewCaption(e.target.value)}
placeholder="Add a caption for this image..." placeholder="Add a caption for this image..."
rows={3} rows={3}
className="border-[#ddd8eb] rounded-xl mt-2" className="border-chart-6 rounded-xl mt-2"
style={{ fontFamily: "'Nunito Sans', sans-serif" }} style={{ fontFamily: "'Nunito Sans', sans-serif" }}
/> />
</div> </div>
@@ -367,14 +367,14 @@ const AdminGallery = () => {
<Button <Button
onClick={() => setEditingCaption(null)} onClick={() => setEditingCaption(null)}
variant="outline" variant="outline"
className="border-[#ddd8eb] text-[#664fa3] rounded-xl" className="border-chart-6 text-muted-foreground rounded-xl"
style={{ fontFamily: "'Inter', sans-serif" }} style={{ fontFamily: "'Inter', sans-serif" }}
> >
Cancel Cancel
</Button> </Button>
<Button <Button
onClick={handleUpdateCaption} onClick={handleUpdateCaption}
className="bg-[#664fa3] hover:bg-[#422268] text-white rounded-xl" className="bg-muted-foreground hover:bg-primary text-white rounded-xl"
style={{ fontFamily: "'Inter', sans-serif" }} style={{ fontFamily: "'Inter', sans-serif" }}
> >
Save Caption Save Caption

View File

@@ -234,9 +234,9 @@ const AdminMembers = () => {
renewalReminders, renewalReminders,
totalReminders, totalReminders,
lastReminderAt: user.last_email_verification_reminder_at || lastReminderAt: user.last_email_verification_reminder_at ||
user.last_event_attendance_reminder_at || user.last_event_attendance_reminder_at ||
user.last_payment_reminder_at || user.last_payment_reminder_at ||
user.last_renewal_reminder_at user.last_renewal_reminder_at
}; };
}; };
@@ -245,10 +245,10 @@ const AdminMembers = () => {
<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 text-[#422268] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}> <h1 className="text-4xl md:text-5xl font-semibold text-primary mb-4" style={{ fontFamily: "'Inter', sans-serif" }}>
Members Management Members Management
</h1> </h1>
<p className="text-lg text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-lg text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Manage paying members and their subscriptions. Manage paying members and their subscriptions.
</p> </p>
</div> </div>
@@ -257,7 +257,7 @@ const AdminMembers = () => {
<DropdownMenu> <DropdownMenu>
<DropdownMenuTrigger asChild> <DropdownMenuTrigger asChild>
<Button <Button
className="bg-[#664fa3] hover:bg-[#422268] text-white rounded-xl h-12 px-6" className="bg-muted-foreground hover:bg-primary text-white rounded-xl h-12 px-6"
disabled={exporting} disabled={exporting}
> >
{exporting ? ( {exporting ? (
@@ -298,7 +298,7 @@ const AdminMembers = () => {
{hasPermission('users.invite') && ( {hasPermission('users.invite') && (
<Button <Button
onClick={() => setInviteDialogOpen(true)} onClick={() => setInviteDialogOpen(true)}
className="bg-[#664fa3] hover:bg-[#422268] text-white rounded-xl h-12 px-6" className="bg-muted-foreground hover:bg-primary text-white rounded-xl h-12 px-6"
> >
<Mail className="h-5 w-5 mr-2" /> <Mail className="h-5 w-5 mr-2" />
Invite Member Invite Member
@@ -320,47 +320,47 @@ const AdminMembers = () => {
{/* Stats */} {/* Stats */}
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-4 gap-4 mb-8"> <div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-4 gap-4 mb-8">
<Card className="p-6 bg-white rounded-2xl border border-[#ddd8eb]"> <Card className="p-6 bg-background rounded-2xl border border-chart-6">
<p className="text-sm text-[#664fa3] mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Total Members</p> <p className="text-sm text-muted-foreground mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Total Members</p>
<p className="text-3xl font-semibold text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}> <p className="text-3xl font-semibold text-primary" style={{ fontFamily: "'Inter', sans-serif" }}>
{users.length} {users.length}
</p> </p>
</Card> </Card>
<Card className="p-6 bg-white rounded-2xl border border-[#ddd8eb]"> <Card className="p-6 bg-background rounded-2xl border border-chart-6">
<p className="text-sm text-[#664fa3] mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Active</p> <p className="text-sm text-muted-foreground mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Active</p>
<p className="text-3xl font-semibold text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}> <p className="text-3xl font-semibold text-primary" 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-[#ddd8eb]"> <Card className="p-6 bg-background rounded-2xl border border-chart-6">
<p className="text-sm text-[#664fa3] mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Payment Pending</p> <p className="text-sm text-muted-foreground mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Payment Pending</p>
<p className="text-3xl font-semibold text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}> <p className="text-3xl font-semibold text-primary" 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-[#ddd8eb]"> <Card className="p-6 bg-background rounded-2xl border border-chart-6">
<p className="text-sm text-[#664fa3] mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Inactive</p> <p className="text-sm text-muted-foreground mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Inactive</p>
<p className="text-3xl font-semibold text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}> <p className="text-3xl font-semibold text-primary" 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-[#ddd8eb] mb-8"> <Card className="p-6 bg-background rounded-2xl border border-chart-6 mb-8">
<div className="grid grid-cols-1 md:grid-cols-2 gap-4"> <div className="grid grid-cols-1 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-[#664fa3]" /> <Search className="absolute left-4 top-1/2 transform -translate-y-1/2 h-5 w-5 text-muted-foreground" />
<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-[#ddd8eb] focus:border-[#664fa3]" className="pl-12 h-14 rounded-xl border-2 border-chart-6 focus:border-muted-foreground"
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-[#ddd8eb]" data-testid="status-filter-select"> <SelectTrigger className="h-14 rounded-xl border-2 border-chart-6" data-testid="status-filter-select">
<SelectValue placeholder="Filter by status" /> <SelectValue placeholder="Filter by status" />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
@@ -381,32 +381,32 @@ const AdminMembers = () => {
{/* Members List */} {/* Members List */}
{loading ? ( {loading ? (
<div className="text-center py-20"> <div className="text-center py-20">
<p className="text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Loading members...</p> <p className="text-muted-foreground" 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-[#ddd8eb] hover:shadow-md transition-shadow" className="p-6 bg-background rounded-2xl border border-chart-6 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-[#DDD8EB] flex items-center justify-center text-[#422268] font-semibold text-lg flex-shrink-0"> <div className="h-14 w-14 rounded-full bg-chart-6 flex items-center justify-center text-primary 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 text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}> <h3 className="text-xl font-semibold text-primary" 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-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <div className="grid md:grid-cols-2 gap-2 text-sm text-muted-foreground" 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>
@@ -420,19 +420,19 @@ const AdminMembers = () => {
const reminderInfo = getReminderInfo(user); const reminderInfo = getReminderInfo(user);
if (reminderInfo.totalReminders > 0) { if (reminderInfo.totalReminders > 0) {
return ( return (
<div className="mt-4 p-3 bg-[#F8F7FB] rounded-lg border border-[#ddd8eb]"> <div className="mt-4 p-3 bg-chart-7 rounded-lg border border-chart-6">
<div className="flex items-center gap-2 mb-2"> <div className="flex items-center gap-2 mb-2">
<AlertCircle className="h-4 w-4 text-[#ff9e77]" /> <AlertCircle className="h-4 w-4 text-accent" />
<span className="text-sm font-semibold text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}> <span className="text-sm font-semibold text-primary" style={{ fontFamily: "'Inter', sans-serif" }}>
{reminderInfo.totalReminders} reminder{reminderInfo.totalReminders !== 1 ? 's' : ''} sent {reminderInfo.totalReminders} reminder{reminderInfo.totalReminders !== 1 ? 's' : ''} sent
{reminderInfo.totalReminders >= 3 && ( {reminderInfo.totalReminders >= 3 && (
<Badge className="ml-2 bg-[#ff9e77] text-white px-2 py-0.5 rounded-full text-xs"> <Badge className="ml-2 bg-accent text-white px-2 py-0.5 rounded-full text-xs">
Needs attention Needs attention
</Badge> </Badge>
)} )}
</span> </span>
</div> </div>
<div className="grid grid-cols-2 md:grid-cols-4 gap-2 text-xs text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <div className="grid grid-cols-2 md:grid-cols-4 gap-2 text-xs text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{reminderInfo.emailReminders > 0 && ( {reminderInfo.emailReminders > 0 && (
<p> <p>
<Mail className="inline h-3 w-3 mr-1" /> <Mail className="inline h-3 w-3 mr-1" />
@@ -459,7 +459,7 @@ const AdminMembers = () => {
)} )}
</div> </div>
{reminderInfo.lastReminderAt && ( {reminderInfo.lastReminderAt && (
<p className="mt-2 text-xs text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="mt-2 text-xs text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Last reminder: {new Date(reminderInfo.lastReminderAt).toLocaleDateString()} at {new Date(reminderInfo.lastReminderAt).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })} Last reminder: {new Date(reminderInfo.lastReminderAt).toLocaleDateString()} at {new Date(reminderInfo.lastReminderAt).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}
</p> </p>
)} )}
@@ -478,7 +478,7 @@ const AdminMembers = () => {
<Button <Button
variant="outline" variant="outline"
size="sm" size="sm"
className="border-[#664fa3] text-[#664fa3] hover:bg-[#664fa3] hover:text-white" className="border-muted-foreground text-muted-foreground hover:bg-muted-foreground hover:text-white"
> >
<Eye className="h-4 w-4 mr-1" /> <Eye className="h-4 w-4 mr-1" />
View Profile View Profile
@@ -490,7 +490,7 @@ const AdminMembers = () => {
<Button <Button
onClick={() => handleActivatePayment(user)} onClick={() => handleActivatePayment(user)}
size="sm" size="sm"
className="bg-[#DDD8EB] text-[#422268] hover:bg-white" className="bg-chart-6 text-primary hover:bg-background"
> >
<CheckCircle className="h-4 w-4 mr-1" /> <CheckCircle className="h-4 w-4 mr-1" />
Activate Payment Activate Payment
@@ -500,7 +500,7 @@ const AdminMembers = () => {
{/* Status Management */} {/* Status Management */}
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<span className="text-sm text-[#664fa3] whitespace-nowrap" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <span className="text-sm text-muted-foreground whitespace-nowrap" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Change Status: Change Status:
</span> </span>
<Select <Select
@@ -508,7 +508,7 @@ const AdminMembers = () => {
onValueChange={(newStatus) => handleStatusChangeRequest(user.id, user.status, newStatus, user)} onValueChange={(newStatus) => handleStatusChangeRequest(user.id, user.status, newStatus, user)}
disabled={statusChanging === user.id} disabled={statusChanging === user.id}
> >
<SelectTrigger className="w-[180px] h-9 border-[#ddd8eb]"> <SelectTrigger className="w-[180px] h-9 border-chart-6">
<SelectValue /> <SelectValue />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
@@ -527,11 +527,11 @@ const AdminMembers = () => {
</div> </div>
) : ( ) : (
<div className="text-center py-20"> <div className="text-center py-20">
<Users className="h-20 w-20 text-[#ddd8eb] mx-auto mb-6" /> <Users className="h-20 w-20 text-chart-6 mx-auto mb-6" />
<h3 className="text-2xl font-semibold text-[#422268] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}> <h3 className="text-2xl font-semibold text-primary mb-4" style={{ fontFamily: "'Inter', sans-serif" }}>
No Members Found No Members Found
</h3> </h3>
<p className="text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-muted-foreground" 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

@@ -175,7 +175,7 @@ const AdminNewsletters = () => {
if (loading) { if (loading) {
return ( return (
<div className="flex items-center justify-center min-h-[60vh]"> <div className="flex items-center justify-center min-h-[60vh]">
<p className="text-[#664fa3]">Loading newsletters...</p> <p className="text-muted-foreground">Loading newsletters...</p>
</div> </div>
); );
} }
@@ -185,17 +185,17 @@ const AdminNewsletters = () => {
{/* Header */} {/* Header */}
<div className="flex justify-between items-center"> <div className="flex justify-between items-center">
<div> <div>
<h1 className="text-3xl font-semibold text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}> <h1 className="text-3xl font-semibold text-primary" style={{ fontFamily: "'Inter', sans-serif" }}>
Newsletter Management Newsletter Management
</h1> </h1>
<p className="text-[#664fa3] mt-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-muted-foreground mt-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Create and manage newsletter archive Create and manage newsletter archive
</p> </p>
</div> </div>
{hasPermission('newsletters.create') && ( {hasPermission('newsletters.create') && (
<Button <Button
onClick={handleCreate} onClick={handleCreate}
className="bg-[#664fa3] text-white hover:bg-[#533a82] rounded-full flex items-center gap-2" className="bg-muted-foreground text-white hover:bg-[#533a82] rounded-full flex items-center gap-2"
> >
<Plus className="h-4 w-4" /> <Plus className="h-4 w-4" />
Add Newsletter Add Newsletter
@@ -206,10 +206,10 @@ const AdminNewsletters = () => {
{/* Newsletters List */} {/* Newsletters List */}
{newsletters.length === 0 ? ( {newsletters.length === 0 ? (
<Card className="p-12 text-center"> <Card className="p-12 text-center">
<FileText className="h-16 w-16 text-[#ddd8eb] mx-auto mb-4" /> <FileText className="h-16 w-16 text-chart-6 mx-auto mb-4" />
<p className="text-[#664fa3] text-lg mb-4">No newsletters yet</p> <p className="text-muted-foreground text-lg mb-4">No newsletters yet</p>
{hasPermission('newsletters.create') && ( {hasPermission('newsletters.create') && (
<Button onClick={handleCreate} className="bg-[#664fa3] text-white"> <Button onClick={handleCreate} className="bg-muted-foreground text-white">
<Plus className="h-4 w-4 mr-2" /> <Plus className="h-4 w-4 mr-2" />
Create First Newsletter Create First Newsletter
</Button> </Button>
@@ -219,7 +219,7 @@ const AdminNewsletters = () => {
<div className="space-y-6"> <div className="space-y-6">
{sortedYears.map(year => ( {sortedYears.map(year => (
<div key={year}> <div key={year}>
<h2 className="text-xl font-semibold text-[#422268] mb-4 flex items-center gap-2"> <h2 className="text-xl font-semibold text-primary mb-4 flex items-center gap-2">
<Calendar className="h-5 w-5" /> <Calendar className="h-5 w-5" />
{year} {year}
</h2> </h2>
@@ -228,24 +228,24 @@ const AdminNewsletters = () => {
<Card key={newsletter.id} className="p-6"> <Card key={newsletter.id} className="p-6">
<div className="flex items-start justify-between"> <div className="flex items-start justify-between">
<div className="flex-1"> <div className="flex-1">
<h3 className="text-lg font-semibold text-[#422268] mb-2"> <h3 className="text-lg font-semibold text-primary mb-2">
{newsletter.title} {newsletter.title}
</h3> </h3>
{newsletter.description && ( {newsletter.description && (
<p className="text-[#664fa3] mb-3">{newsletter.description}</p> <p className="text-muted-foreground mb-3">{newsletter.description}</p>
)} )}
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
<Badge className="bg-[#DDD8EB] text-[#422268]"> <Badge className="bg-chart-6 text-primary">
{formatDate(newsletter.published_date)} {formatDate(newsletter.published_date)}
</Badge> </Badge>
<Badge variant="outline" className="border-[#664fa3] text-[#664fa3]"> <Badge variant="outline" className="border-muted-foreground text-muted-foreground">
{newsletter.document_type === 'upload' ? 'PDF Upload' : 'Link'} {newsletter.document_type === 'upload' ? 'PDF Upload' : 'Link'}
</Badge> </Badge>
<Button <Button
variant="ghost" variant="ghost"
size="sm" size="sm"
onClick={() => window.open(newsletter.document_url, '_blank')} onClick={() => window.open(newsletter.document_url, '_blank')}
className="text-[#664fa3] hover:text-[#533a82]" className="text-muted-foreground hover:text-[#533a82]"
> >
<ExternalLink className="h-4 w-4 mr-1" /> <ExternalLink className="h-4 w-4 mr-1" />
View View
@@ -259,7 +259,7 @@ const AdminNewsletters = () => {
variant="outline" variant="outline"
size="sm" size="sm"
onClick={() => handleEdit(newsletter)} onClick={() => handleEdit(newsletter)}
className="border-[#664fa3] text-[#664fa3]" className="border-muted-foreground text-muted-foreground"
> >
<Edit className="h-4 w-4" /> <Edit className="h-4 w-4" />
</Button> </Button>
@@ -361,12 +361,12 @@ const AdminNewsletters = () => {
required={!selectedNewsletter} required={!selectedNewsletter}
/> />
{uploadedFile && ( {uploadedFile && (
<p className="text-sm text-[#664fa3] mt-1"> <p className="text-sm text-muted-foreground mt-1">
Selected: {uploadedFile.name} Selected: {uploadedFile.name}
</p> </p>
)} )}
{selectedNewsletter && !uploadedFile && ( {selectedNewsletter && !uploadedFile && (
<p className="text-sm text-[#664fa3] mt-1"> <p className="text-sm text-muted-foreground mt-1">
Current file will be kept if no new file is selected Current file will be kept if no new file is selected
</p> </p>
)} )}
@@ -381,7 +381,7 @@ const AdminNewsletters = () => {
placeholder="https://docs.google.com/document/d/... or https://example.com/file.pdf" placeholder="https://docs.google.com/document/d/... or https://example.com/file.pdf"
required required
/> />
<p className="text-sm text-[#664fa3] mt-1"> <p className="text-sm text-muted-foreground mt-1">
Paste the shareable link to your document (Google Docs, Dropbox, PDF URL, etc.) Paste the shareable link to your document (Google Docs, Dropbox, PDF URL, etc.)
</p> </p>
</div> </div>
@@ -398,7 +398,7 @@ const AdminNewsletters = () => {
</Button> </Button>
<Button <Button
type="submit" type="submit"
className="bg-[#664fa3] text-white" className="bg-muted-foreground text-white"
disabled={submitting} disabled={submitting}
> >
{submitting ? 'Saving...' : selectedNewsletter ? 'Update' : 'Create'} {submitting ? 'Saving...' : selectedNewsletter ? 'Update' : 'Create'}

View File

@@ -188,7 +188,7 @@ const AdminPermissions = () => {
const getRoleBadge = (role) => { const getRoleBadge = (role) => {
const config = { const config = {
admin: { label: 'Admin', color: 'bg-[#81B29A]', icon: Shield }, admin: { label: 'Admin', color: 'bg-[#81B29A]', icon: Shield },
member: { label: 'Member', color: 'bg-[#664fa3]', icon: Shield }, member: { label: 'Member', color: 'bg-muted-foreground', icon: Shield },
guest: { label: 'Guest', color: 'bg-gray-400', icon: Shield } guest: { label: 'Guest', color: 'bg-gray-400', icon: Shield }
}; };
@@ -206,7 +206,7 @@ const AdminPermissions = () => {
if (loading) { if (loading) {
return ( return (
<div className="text-center py-20"> <div className="text-center py-20">
<p className="text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Loading permissions... Loading permissions...
</p> </p>
</div> </div>
@@ -217,10 +217,10 @@ const AdminPermissions = () => {
return ( return (
<div className="text-center py-20"> <div className="text-center py-20">
<Lock className="h-20 w-20 text-red-500 mx-auto mb-6" /> <Lock className="h-20 w-20 text-red-500 mx-auto mb-6" />
<h2 className="text-3xl font-semibold text-[#422268] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}> <h2 className="text-3xl font-semibold text-primary mb-4" style={{ fontFamily: "'Inter', sans-serif" }}>
Access Denied Access Denied
</h2> </h2>
<p className="text-lg text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-lg text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
You don't have permission to manage role permissions. You don't have permission to manage role permissions.
</p> </p>
<p className="text-sm text-gray-500 mt-2"> <p className="text-sm text-gray-500 mt-2">
@@ -233,10 +233,10 @@ const AdminPermissions = () => {
return ( return (
<> <>
<div className="mb-8"> <div className="mb-8">
<h1 className="text-4xl md:text-5xl font-semibold text-[#422268] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}> <h1 className="text-4xl md:text-5xl font-semibold text-primary mb-4" style={{ fontFamily: "'Inter', sans-serif" }}>
Permission Management Permission Management
</h1> </h1>
<p className="text-lg text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-lg text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Configure granular permissions for each role. Superadmin always has all permissions. Configure granular permissions for each role. Superadmin always has all permissions.
</p> </p>
</div> </div>
@@ -259,27 +259,27 @@ const AdminPermissions = () => {
<TabsContent key={role} value={role}> <TabsContent key={role} value={role}>
{/* Stats */} {/* Stats */}
<div className="grid md:grid-cols-3 gap-4 mb-8"> <div className="grid md:grid-cols-3 gap-4 mb-8">
<Card className="p-6 bg-white rounded-2xl border border-[#ddd8eb]"> <Card className="p-6 bg-background rounded-2xl border border-chart-6">
<p className="text-sm text-[#664fa3] mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-sm text-muted-foreground mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Total Permissions Total Permissions
</p> </p>
<p className="text-3xl font-semibold text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}> <p className="text-3xl font-semibold text-primary" style={{ fontFamily: "'Inter', sans-serif" }}>
{permissions.length} {permissions.length}
</p> </p>
</Card> </Card>
<Card className="p-6 bg-white rounded-2xl border border-[#ddd8eb]"> <Card className="p-6 bg-background rounded-2xl border border-chart-6">
<p className="text-sm text-[#664fa3] mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-sm text-muted-foreground mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Assigned Assigned
</p> </p>
<p className="text-3xl font-semibold text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}> <p className="text-3xl font-semibold text-primary" style={{ fontFamily: "'Inter', sans-serif" }}>
{selectedPermissions[role].length} {selectedPermissions[role].length}
</p> </p>
</Card> </Card>
<Card className="p-6 bg-white rounded-2xl border border-[#ddd8eb]"> <Card className="p-6 bg-background rounded-2xl border border-chart-6">
<p className="text-sm text-[#664fa3] mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-sm text-muted-foreground mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Modules Modules
</p> </p>
<p className="text-3xl font-semibold text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}> <p className="text-3xl font-semibold text-primary" style={{ fontFamily: "'Inter', sans-serif" }}>
{Object.keys(groupedPermissions).length} {Object.keys(groupedPermissions).length}
</p> </p>
</Card> </Card>
@@ -288,10 +288,10 @@ const AdminPermissions = () => {
{/* Permissions by Module */} {/* Permissions by Module */}
<div className="space-y-4"> <div className="space-y-4">
{Object.entries(groupedPermissions).map(([module, perms]) => ( {Object.entries(groupedPermissions).map(([module, perms]) => (
<Card key={module} className="bg-white rounded-2xl border border-[#ddd8eb] overflow-hidden"> <Card key={module} className="bg-background rounded-2xl border border-chart-6 overflow-hidden">
{/* Module Header */} {/* Module Header */}
<div <div
className="p-6 bg-gradient-to-r from-[#DDD8EB] to-white cursor-pointer hover:from-[#C5BFD9] transition-colors" className="p-6 bg-gradient-to-r from-chart-6 to-white cursor-pointer hover:from-[#C5BFD9] transition-colors"
onClick={() => toggleModuleExpansion(module)} onClick={() => toggleModuleExpansion(module)}
> >
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
@@ -300,21 +300,21 @@ const AdminPermissions = () => {
checked={isModuleFullySelected(role, module)} checked={isModuleFullySelected(role, module)}
onCheckedChange={() => toggleModule(role, module)} onCheckedChange={() => toggleModule(role, module)}
onClick={(e) => e.stopPropagation()} onClick={(e) => e.stopPropagation()}
className="h-6 w-6 border-2 border-[#664fa3] data-[state=checked]:bg-[#664fa3]" className="h-6 w-6 border-2 border-muted-foreground data-[state=checked]:bg-muted-foreground"
/> />
<div> <div>
<h3 className="text-xl font-semibold text-[#422268] capitalize" style={{ fontFamily: "'Inter', sans-serif" }}> <h3 className="text-xl font-semibold text-primary capitalize" style={{ fontFamily: "'Inter', sans-serif" }}>
{module} {module}
</h3> </h3>
<p className="text-sm text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-sm text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{getModuleProgress(role, module)} permissions {getModuleProgress(role, module)} permissions
</p> </p>
</div> </div>
</div> </div>
{expandedModules[module] ? ( {expandedModules[module] ? (
<ChevronUp className="h-6 w-6 text-[#664fa3]" /> <ChevronUp className="h-6 w-6 text-muted-foreground" />
) : ( ) : (
<ChevronDown className="h-6 w-6 text-[#664fa3]" /> <ChevronDown className="h-6 w-6 text-muted-foreground" />
)} )}
</div> </div>
</div> </div>
@@ -326,18 +326,18 @@ const AdminPermissions = () => {
{perms.map(perm => ( {perms.map(perm => (
<div <div
key={perm.code} key={perm.code}
className="flex items-start gap-4 p-4 rounded-xl hover:bg-[#F9F8FB] transition-colors border border-transparent hover:border-[#ddd8eb]" className="flex items-start gap-4 p-4 rounded-xl hover:bg-[#F9F8FB] transition-colors border border-transparent hover:border-chart-6"
> >
<Checkbox <Checkbox
checked={selectedPermissions[role].includes(perm.code)} checked={selectedPermissions[role].includes(perm.code)}
onCheckedChange={() => togglePermission(role, perm.code)} onCheckedChange={() => togglePermission(role, perm.code)}
className="mt-1 h-5 w-5 border-2 border-[#664fa3] data-[state=checked]:bg-[#664fa3]" className="mt-1 h-5 w-5 border-2 border-muted-foreground data-[state=checked]:bg-muted-foreground"
/> />
<div className="flex-1"> <div className="flex-1">
<p className="font-semibold text-[#422268] mb-1" style={{ fontFamily: "'Inter', sans-serif" }}> <p className="font-semibold text-primary mb-1" style={{ fontFamily: "'Inter', sans-serif" }}>
{perm.name} {perm.name}
</p> </p>
<p className="text-sm text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-sm text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{perm.description} {perm.description}
</p> </p>
<p className="text-xs text-gray-400 mt-1 font-mono"> <p className="text-xs text-gray-400 mt-1 font-mono">
@@ -357,7 +357,7 @@ const AdminPermissions = () => {
</Tabs> </Tabs>
{/* Superadmin Note */} {/* Superadmin Note */}
<Card className="p-6 bg-gradient-to-r from-[#664fa3] to-[#422268] rounded-2xl border-none mb-8"> <Card className="p-6 bg-gradient-to-r from-muted-foreground to-primary rounded-2xl border-none mb-8">
<div className="flex items-start gap-4"> <div className="flex items-start gap-4">
<Lock className="h-6 w-6 text-white flex-shrink-0 mt-1" /> <Lock className="h-6 w-6 text-white flex-shrink-0 mt-1" />
<div className="text-white"> <div className="text-white">
@@ -389,10 +389,10 @@ const AdminPermissions = () => {
<AlertDialog open={showConfirmDialog} onOpenChange={setShowConfirmDialog}> <AlertDialog open={showConfirmDialog} onOpenChange={setShowConfirmDialog}>
<AlertDialogContent className="rounded-2xl"> <AlertDialogContent className="rounded-2xl">
<AlertDialogHeader> <AlertDialogHeader>
<AlertDialogTitle className="text-2xl text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}> <AlertDialogTitle className="text-2xl text-primary" style={{ fontFamily: "'Inter', sans-serif" }}>
Confirm Permission Changes Confirm Permission Changes
</AlertDialogTitle> </AlertDialogTitle>
<AlertDialogDescription className="text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <AlertDialogDescription className="text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Are you sure you want to update permissions for <span className="font-semibold capitalize">{selectedRole}</span>? Are you sure you want to update permissions for <span className="font-semibold capitalize">{selectedRole}</span>?
This will immediately affect all users with this role. This will immediately affect all users with this role.
</AlertDialogDescription> </AlertDialogDescription>

View File

@@ -131,17 +131,17 @@ 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 text-[#422268] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}> <h1 className="text-4xl md:text-5xl font-semibold text-primary mb-4" style={{ fontFamily: "'Inter', sans-serif" }}>
Subscription Plans Subscription Plans
</h1> </h1>
<p className="text-lg text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-lg text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Manage membership plans and pricing. Manage membership plans and pricing.
</p> </p>
</div> </div>
{hasPermission('subscriptions.plans') && ( {hasPermission('subscriptions.plans') && (
<Button <Button
onClick={handleCreatePlan} onClick={handleCreatePlan}
className="bg-[#DDD8EB] text-[#422268] hover:bg-white rounded-full px-6" className="bg-chart-6 text-primary hover:bg-background rounded-full px-6"
> >
<Plus className="h-4 w-4 mr-2" /> <Plus className="h-4 w-4 mr-2" />
Create Plan Create Plan
@@ -152,27 +152,27 @@ const AdminPlans = () => {
{/* Stats */} {/* Stats */}
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-4 gap-4 mb-8"> <div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-4 gap-4 mb-8">
<Card className="p-6 bg-white rounded-2xl border border-[#ddd8eb]"> <Card className="p-6 bg-background rounded-2xl border border-chart-6">
<p className="text-sm text-[#664fa3] mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Total Plans</p> <p className="text-sm text-muted-foreground mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Total Plans</p>
<p className="text-3xl font-semibold text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}> <p className="text-3xl font-semibold text-primary" style={{ fontFamily: "'Inter', sans-serif" }}>
{plans.length} {plans.length}
</p> </p>
</Card> </Card>
<Card className="p-6 bg-white rounded-2xl border border-[#ddd8eb]"> <Card className="p-6 bg-background rounded-2xl border border-chart-6">
<p className="text-sm text-[#664fa3] mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Active Plans</p> <p className="text-sm text-muted-foreground mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Active Plans</p>
<p className="text-3xl font-semibold text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}> <p className="text-3xl font-semibold text-primary" 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-[#ddd8eb]"> <Card className="p-6 bg-background rounded-2xl border border-chart-6">
<p className="text-sm text-[#664fa3] mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Total Subscribers</p> <p className="text-sm text-muted-foreground mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Total Subscribers</p>
<p className="text-3xl font-semibold text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}> <p className="text-3xl font-semibold text-primary" 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-[#ddd8eb]"> <Card className="p-6 bg-background rounded-2xl border border-chart-6">
<p className="text-sm text-[#664fa3] mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Revenue (Annual Est.)</p> <p className="text-sm text-muted-foreground mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Revenue (Annual Est.)</p>
<p className="text-3xl font-semibold text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}> <p className="text-3xl font-semibold text-primary" 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'
@@ -186,19 +186,19 @@ const AdminPlans = () => {
</div> </div>
{/* Filters */} {/* Filters */}
<Card className="p-6 bg-white rounded-2xl border border-[#ddd8eb] mb-8"> <Card className="p-6 bg-background rounded-2xl border border-chart-6 mb-8">
<div className="grid grid-cols-1 md:grid-cols-2 gap-4"> <div className="grid grid-cols-1 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-[#664fa3]" /> <Search className="absolute left-4 top-1/2 transform -translate-y-1/2 h-5 w-5 text-muted-foreground" />
<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-[#ddd8eb] focus:border-[#664fa3]" className="pl-12 h-14 rounded-xl border-2 border-chart-6 focus:border-muted-foreground"
/> />
</div> </div>
<Select value={activeFilter} onValueChange={setActiveFilter}> <Select value={activeFilter} onValueChange={setActiveFilter}>
<SelectTrigger className="h-14 rounded-xl border-2 border-[#ddd8eb]"> <SelectTrigger className="h-14 rounded-xl border-2 border-chart-6">
<SelectValue placeholder="Filter by status" /> <SelectValue placeholder="Filter by status" />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
@@ -213,56 +213,54 @@ const AdminPlans = () => {
{/* Plans Grid */} {/* Plans Grid */}
{loading ? ( {loading ? (
<div className="text-center py-20"> <div className="text-center py-20">
<p className="text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Loading plans...</p> <p className="text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Loading plans...</p>
</div> </div>
) : filteredPlans.length > 0 ? ( ) : filteredPlans.length > 0 ? (
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6"> <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
{filteredPlans.map((plan) => ( {filteredPlans.map((plan) => (
<Card <Card
key={plan.id} key={plan.id}
className={`p-6 bg-white rounded-2xl border-2 transition-all hover:shadow-lg ${ className={`p-6 bg-background rounded-2xl border-2 transition-all hover:shadow-lg ${plan.active
plan.active ? 'border-chart-6 hover:border-muted-foreground'
? 'border-[#ddd8eb] hover:border-[#664fa3]' : 'border-gray-400 opacity-60'
: 'border-gray-400 opacity-60' }`}
}`}
> >
{/* Header with badges */} {/* Header with badges */}
<div className="flex flex-wrap gap-2 mb-4"> <div className="flex flex-wrap gap-2 mb-4">
<Badge <Badge
className={`${ className={`${plan.active
plan.active ? 'bg-[#81B29A] text-white'
? 'bg-[#81B29A] text-white' : 'bg-gray-400 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-[#DDD8EB] text-[#422268]"> <Badge className="bg-chart-6 text-primary">
<Users className="h-3 w-3 mr-1" /> <Users className="h-3 w-3 mr-1" />
{plan.subscriber_count} {plan.subscriber_count}
</Badge> </Badge>
)} )}
{plan.custom_cycle_enabled && ( {plan.custom_cycle_enabled && (
<Badge className="bg-[#664fa3] text-white"> <Badge className="bg-muted-foreground text-white">
Custom Dates Custom Dates
</Badge> </Badge>
)} )}
{plan.allow_donation && ( {plan.allow_donation && (
<Badge className="bg-[#ff9e77] text-white"> <Badge className="bg-accent text-white">
Donations Enabled Donations Enabled
</Badge> </Badge>
)} )}
</div> </div>
{/* Plan Name */} {/* Plan Name */}
<h3 className="text-2xl font-semibold text-[#422268] mb-2" style={{ fontFamily: "'Inter', sans-serif" }}> <h3 className="text-2xl font-semibold text-primary mb-2" style={{ fontFamily: "'Inter', sans-serif" }}>
{plan.name} {plan.name}
</h3> </h3>
{/* Description */} {/* Description */}
{plan.description && ( {plan.description && (
<p className="text-sm text-[#664fa3] mb-4 line-clamp-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-sm text-muted-foreground mb-4 line-clamp-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{plan.description} {plan.description}
</p> </p>
)} )}
@@ -270,20 +268,20 @@ const AdminPlans = () => {
{/* Price */} {/* Price */}
<div className="mb-4"> <div className="mb-4">
<div className="flex items-baseline gap-2"> <div className="flex items-baseline gap-2">
<div className="text-3xl font-bold text-[#ff9e77]" style={{ fontFamily: "'Inter', sans-serif" }}> <div className="text-3xl font-bold text-accent" style={{ fontFamily: "'Inter', sans-serif" }}>
{formatPrice(plan.minimum_price_cents || plan.price_cents)} {formatPrice(plan.minimum_price_cents || plan.price_cents)}
</div> </div>
{plan.suggested_price_cents && plan.suggested_price_cents > plan.minimum_price_cents && ( {plan.suggested_price_cents && plan.suggested_price_cents > plan.minimum_price_cents && (
<div className="text-lg text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <div className="text-lg text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
(Suggested: {formatPrice(plan.suggested_price_cents)}) (Suggested: {formatPrice(plan.suggested_price_cents)})
</div> </div>
)} )}
</div> </div>
<p className="text-sm text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-sm text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{getBillingCycleLabel(plan.billing_cycle)} {getBillingCycleLabel(plan.billing_cycle)}
</p> </p>
{plan.custom_cycle_enabled && ( {plan.custom_cycle_enabled && (
<p className="text-xs text-[#664fa3] font-mono mt-1"> <p className="text-xs text-muted-foreground font-mono mt-1">
{formatCustomCycleDates(plan)} {formatCustomCycleDates(plan)}
</p> </p>
)} )}
@@ -291,12 +289,12 @@ const AdminPlans = () => {
{/* Actions */} {/* Actions */}
{hasPermission('subscriptions.plans') && ( {hasPermission('subscriptions.plans') && (
<div className="flex flex-col sm:flex-row gap-2 pt-4 border-t border-[#ddd8eb]"> <div className="flex flex-col sm:flex-row gap-2 pt-4 border-t border-chart-6">
<Button <Button
onClick={() => handleEditPlan(plan)} onClick={() => handleEditPlan(plan)}
variant="outline" variant="outline"
size="sm" size="sm"
className="flex-1 border-[#664fa3] text-[#664fa3] hover:bg-[#664fa3] hover:text-white rounded-full" className="flex-1 border-muted-foreground text-muted-foreground hover:bg-muted-foreground hover:text-white rounded-full"
> >
<Edit className="h-4 w-4 mr-1" /> <Edit className="h-4 w-4 mr-1" />
Edit Edit
@@ -316,7 +314,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-[#664fa3] mt-2 text-center" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-xs text-muted-foreground mt-2 text-center" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Cannot delete plan with active subscribers Cannot delete plan with active subscribers
</p> </p>
)} )}
@@ -325,11 +323,11 @@ const AdminPlans = () => {
</div> </div>
) : ( ) : (
<div className="text-center py-20"> <div className="text-center py-20">
<CreditCard className="h-20 w-20 text-[#ddd8eb] mx-auto mb-6" /> <CreditCard className="h-20 w-20 text-chart-6 mx-auto mb-6" />
<h3 className="text-2xl font-semibold text-[#422268] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}> <h3 className="text-2xl font-semibold text-primary mb-4" style={{ fontFamily: "'Inter', sans-serif" }}>
No Plans Found No Plans Found
</h3> </h3>
<p className="text-[#664fa3] mb-6" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-muted-foreground 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'}
@@ -337,7 +335,7 @@ const AdminPlans = () => {
{!searchQuery && activeFilter === 'all' && hasPermission('subscriptions.plans') && ( {!searchQuery && activeFilter === 'all' && hasPermission('subscriptions.plans') && (
<Button <Button
onClick={handleCreatePlan} onClick={handleCreatePlan}
className="bg-[#DDD8EB] text-[#422268] hover:bg-white rounded-full px-8" className="bg-chart-6 text-primary hover:bg-background 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
@@ -357,11 +355,11 @@ const AdminPlans = () => {
{/* Delete Confirmation Dialog */} {/* Delete Confirmation Dialog */}
{deleteDialogOpen && ( {deleteDialogOpen && (
<div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50 p-4"> <div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50 p-4">
<Card className="p-6 sm:p-8 bg-white rounded-2xl max-w-md w-full"> <Card className="p-6 sm:p-8 bg-background rounded-2xl max-w-md w-full">
<h2 className="text-xl sm:text-2xl font-semibold text-[#422268] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}> <h2 className="text-xl sm:text-2xl font-semibold text-primary mb-4" style={{ fontFamily: "'Inter', sans-serif" }}>
Delete Plan Delete Plan
</h2> </h2>
<p className="text-sm sm:text-base text-[#664fa3] mb-6" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-sm sm:text-base text-muted-foreground 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>

View File

@@ -99,9 +99,9 @@ const AdminStaff = () => {
const getRoleBadge = (role) => { const getRoleBadge = (role) => {
const config = { const config = {
superadmin: { label: 'Superadmin', className: 'bg-[#664fa3] text-white' }, superadmin: { label: 'Superadmin', className: 'bg-muted-foreground text-white' },
admin: { label: 'Admin', className: 'bg-[#81B29A] text-white' }, admin: { label: 'Admin', className: 'bg-[#81B29A] text-white' },
moderator: { label: 'Moderator', className: 'bg-[#DDD8EB] text-[#422268]' }, moderator: { label: 'Moderator', className: 'bg-chart-6 text-primary' },
staff: { label: 'Staff', className: 'bg-gray-200 text-gray-700' }, staff: { label: 'Staff', className: 'bg-gray-200 text-gray-700' },
media: { label: 'Media', className: 'bg-gray-400 text-white' } media: { label: 'Media', className: 'bg-gray-400 text-white' }
}; };
@@ -134,10 +134,10 @@ const AdminStaff = () => {
<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 text-[#422268] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}> <h1 className="text-4xl md:text-5xl font-semibold text-primary mb-4" style={{ fontFamily: "'Inter', sans-serif" }}>
Staff Management Staff Management
</h1> </h1>
<p className="text-lg text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-lg text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Manage internal team members and their roles. Manage internal team members and their roles.
</p> </p>
</div> </div>
@@ -145,7 +145,7 @@ const AdminStaff = () => {
{hasPermission('users.create') && ( {hasPermission('users.create') && (
<Button <Button
onClick={() => setInviteDialogOpen(true)} onClick={() => setInviteDialogOpen(true)}
className="bg-[#664fa3] hover:bg-[#422268] text-white rounded-xl h-12 px-6" className="bg-muted-foreground hover:bg-primary text-white rounded-xl h-12 px-6"
> >
<Mail className="h-5 w-5 mr-2" /> <Mail className="h-5 w-5 mr-2" />
Invite Staff Invite Staff
@@ -166,27 +166,27 @@ const AdminStaff = () => {
{/* 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-[#ddd8eb]"> <Card className="p-6 bg-background rounded-2xl border border-chart-6">
<p className="text-sm text-[#664fa3] mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Total Staff</p> <p className="text-sm text-muted-foreground mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Total Staff</p>
<p className="text-3xl font-semibold text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}> <p className="text-3xl font-semibold text-primary" style={{ fontFamily: "'Inter', sans-serif" }}>
{users.length} {users.length}
</p> </p>
</Card> </Card>
<Card className="p-6 bg-white rounded-2xl border border-[#ddd8eb]"> <Card className="p-6 bg-background rounded-2xl border border-chart-6">
<p className="text-sm text-[#664fa3] mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Admins</p> <p className="text-sm text-muted-foreground mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Admins</p>
<p className="text-3xl font-semibold text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}> <p className="text-3xl font-semibold text-primary" 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-[#ddd8eb]"> <Card className="p-6 bg-background rounded-2xl border border-chart-6">
<p className="text-sm text-[#664fa3] mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Moderators</p> <p className="text-sm text-muted-foreground mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Moderators</p>
<p className="text-3xl font-semibold text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}> <p className="text-3xl font-semibold text-primary" 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-[#ddd8eb]"> <Card className="p-6 bg-background rounded-2xl border border-chart-6">
<p className="text-sm text-[#664fa3] mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Active</p> <p className="text-sm text-muted-foreground mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Active</p>
<p className="text-3xl font-semibold text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}> <p className="text-3xl font-semibold text-primary" style={{ fontFamily: "'Inter', sans-serif" }}>
{users.filter(u => u.status === 'active').length} {users.filter(u => u.status === 'active').length}
</p> </p>
</Card> </Card>
@@ -207,20 +207,20 @@ const AdminStaff = () => {
<TabsContent value="staff-list"> <TabsContent value="staff-list">
{/* Filters */} {/* Filters */}
<Card className="p-6 bg-white rounded-2xl border border-[#ddd8eb] mb-8"> <Card className="p-6 bg-background rounded-2xl border border-chart-6 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-[#664fa3]" /> <Search className="absolute left-4 top-1/2 transform -translate-y-1/2 h-5 w-5 text-muted-foreground" />
<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-[#ddd8eb] focus:border-[#664fa3]" className="pl-12 h-14 rounded-xl border-2 border-chart-6 focus:border-muted-foreground"
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-[#ddd8eb]" data-testid="role-filter-select"> <SelectTrigger className="h-14 rounded-xl border-2 border-chart-6" data-testid="role-filter-select">
<SelectValue placeholder="Filter by role" /> <SelectValue placeholder="Filter by role" />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
@@ -238,33 +238,33 @@ const AdminStaff = () => {
{/* Staff List */} {/* Staff List */}
{loading ? ( {loading ? (
<div className="text-center py-20"> <div className="text-center py-20">
<p className="text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Loading staff...</p> <p className="text-muted-foreground" 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-[#ddd8eb] hover:shadow-md transition-shadow" className="p-6 bg-background rounded-2xl border border-chart-6 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-[#DDD8EB] flex items-center justify-center text-[#422268] font-semibold text-lg flex-shrink-0"> <div className="h-14 w-14 rounded-full bg-chart-6 flex items-center justify-center text-primary 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 text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}> <h3 className="text-xl font-semibold text-primary" 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-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <div className="grid md:grid-cols-2 gap-2 text-sm text-muted-foreground" 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>
@@ -280,7 +280,7 @@ const AdminStaff = () => {
<Button <Button
onClick={() => navigate(`/admin/users/${user.id}`)} onClick={() => navigate(`/admin/users/${user.id}`)}
variant="outline" variant="outline"
className="border-2 border-[#664fa3] text-[#664fa3] hover:bg-[#f1eef9] rounded-full px-4 py-2" className="border-2 border-muted-foreground text-muted-foreground hover:bg-muted rounded-full px-4 py-2"
> >
<Edit className="h-4 w-4 mr-2" /> <Edit className="h-4 w-4 mr-2" />
Manage Manage
@@ -290,11 +290,10 @@ const AdminStaff = () => {
<Button <Button
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 ${ className={`border-2 rounded-full px-4 py-2 ${user.status === 'active'
user.status === 'active' ? 'border-orange-500 text-orange-600 hover:bg-orange-50'
? 'border-orange-500 text-orange-600 hover:bg-orange-50' : 'border-green-500 text-green-600 hover:bg-green-50'
: 'border-green-500 text-green-600 hover:bg-green-50' }`}
}`}
> >
{user.status === 'active' ? ( {user.status === 'active' ? (
<> <>
@@ -327,11 +326,11 @@ const AdminStaff = () => {
</div> </div>
) : ( ) : (
<div className="text-center py-20"> <div className="text-center py-20">
<UserCog className="h-20 w-20 text-[#ddd8eb] mx-auto mb-6" /> <UserCog className="h-20 w-20 text-chart-6 mx-auto mb-6" />
<h3 className="text-2xl font-semibold text-[#422268] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}> <h3 className="text-2xl font-semibold text-primary mb-4" style={{ fontFamily: "'Inter', sans-serif" }}>
No Staff Found No Staff Found
</h3> </h3>
<p className="text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-muted-foreground" 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

@@ -277,7 +277,7 @@ Proceed with activation?`;
if (loading) { if (loading) {
return ( return (
<div className="flex items-center justify-center min-h-screen"> <div className="flex items-center justify-center min-h-screen">
<Loader2 className="h-12 w-12 animate-spin text-[#664fa3]" /> <Loader2 className="h-12 w-12 animate-spin text-muted-foreground" />
</div> </div>
); );
} }
@@ -286,36 +286,36 @@ Proceed with activation?`;
<div className="space-y-8"> <div className="space-y-8">
{/* Header */} {/* Header */}
<div> <div>
<h1 className="text-3xl font-semibold text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}> <h1 className="text-3xl font-semibold text-primary" style={{ fontFamily: "'Inter', sans-serif" }}>
Subscription Management Subscription Management
</h1> </h1>
<p className="text-[#664fa3] mt-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-muted-foreground mt-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
View and manage all member subscriptions View and manage all member subscriptions
</p> </p>
</div> </div>
{/* Stats Cards */} {/* Stats Cards */}
<div className="grid md:grid-cols-4 gap-6"> <div className="grid md:grid-cols-4 gap-6">
<Card className="p-6 bg-white rounded-2xl border-2 border-[#ddd8eb]"> <Card className="p-6 bg-background rounded-2xl border-2 border-chart-6">
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<div> <div>
<p className="text-sm text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-sm text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Total Subscriptions Total Subscriptions
</p> </p>
<p className="text-3xl font-bold text-[#422268] mt-2" style={{ fontFamily: "'Inter', sans-serif" }}> <p className="text-3xl font-bold text-primary mt-2" style={{ fontFamily: "'Inter', sans-serif" }}>
{stats.total || 0} {stats.total || 0}
</p> </p>
</div> </div>
<div className="p-3 bg-[#DDD8EB]/20 rounded-full"> <div className="p-3 bg-chart-6/20 rounded-full">
<CreditCard className="h-6 w-6 text-[#664fa3]" /> <CreditCard className="h-6 w-6 text-muted-foreground" />
</div> </div>
</div> </div>
</Card> </Card>
<Card className="p-6 bg-white rounded-2xl border-2 border-[#ddd8eb]"> <Card className="p-6 bg-background rounded-2xl border-2 border-chart-6">
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<div> <div>
<p className="text-sm text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-sm text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Active Members Active Members
</p> </p>
<p className="text-3xl font-bold text-[#81B29A] mt-2" style={{ fontFamily: "'Inter', sans-serif" }}> <p className="text-3xl font-bold text-[#81B29A] mt-2" style={{ fontFamily: "'Inter', sans-serif" }}>
@@ -328,51 +328,51 @@ Proceed with activation?`;
</div> </div>
</Card> </Card>
<Card className="p-6 bg-white rounded-2xl border-2 border-[#ddd8eb]"> <Card className="p-6 bg-background rounded-2xl border-2 border-chart-6">
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<div> <div>
<p className="text-sm text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-sm text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Total Revenue Total Revenue
</p> </p>
<p className="text-3xl font-bold text-[#422268] mt-2" style={{ fontFamily: "'Inter', sans-serif" }}> <p className="text-3xl font-bold text-primary mt-2" style={{ fontFamily: "'Inter', sans-serif" }}>
{formatPrice(stats.total_revenue || 0)} {formatPrice(stats.total_revenue || 0)}
</p> </p>
</div> </div>
<div className="p-3 bg-[#DDD8EB]/20 rounded-full"> <div className="p-3 bg-chart-6/20 rounded-full">
<DollarSign className="h-6 w-6 text-[#664fa3]" /> <DollarSign className="h-6 w-6 text-muted-foreground" />
</div> </div>
</div> </div>
</Card> </Card>
<Card className="p-6 bg-white rounded-2xl border-2 border-[#ddd8eb]"> <Card className="p-6 bg-background rounded-2xl border-2 border-chart-6">
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<div> <div>
<p className="text-sm text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-sm text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Total Donations Total Donations
</p> </p>
<p className="text-3xl font-bold text-[#ff9e77] mt-2" style={{ fontFamily: "'Inter', sans-serif" }}> <p className="text-3xl font-bold text-accent mt-2" style={{ fontFamily: "'Inter', sans-serif" }}>
{formatPrice(stats.total_donations || 0)} {formatPrice(stats.total_donations || 0)}
</p> </p>
</div> </div>
<div className="p-3 bg-[#ff9e77]/10 rounded-full"> <div className="p-3 bg-accent/10 rounded-full">
<Heart className="h-6 w-6 text-[#ff9e77]" /> <Heart className="h-6 w-6 text-accent" />
</div> </div>
</div> </div>
</Card> </Card>
</div> </div>
{/* Search & Filter Bar */} {/* Search & Filter Bar */}
<Card className="p-6 bg-white rounded-2xl border-2 border-[#ddd8eb]"> <Card className="p-6 bg-background rounded-2xl border-2 border-chart-6">
<div className="grid md:grid-cols-3 gap-4"> <div className="grid md:grid-cols-3 gap-4">
{/* Search */} {/* Search */}
<div className="md:col-span-1"> <div className="md:col-span-1">
<div className="relative"> <div className="relative">
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 h-5 w-5 text-[#664fa3]" /> <Search className="absolute left-3 top-1/2 transform -translate-y-1/2 h-5 w-5 text-muted-foreground" />
<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-10 rounded-xl border-2 border-[#ddd8eb] focus:border-[#664fa3]" className="pl-10 rounded-xl border-2 border-chart-6 focus:border-muted-foreground"
/> />
</div> </div>
</div> </div>
@@ -380,7 +380,7 @@ Proceed with activation?`;
{/* Status Filter */} {/* Status Filter */}
<div> <div>
<Select value={statusFilter} onValueChange={setStatusFilter}> <Select value={statusFilter} onValueChange={setStatusFilter}>
<SelectTrigger className="rounded-xl border-2 border-[#ddd8eb]"> <SelectTrigger className="rounded-xl border-2 border-chart-6">
<SelectValue placeholder="All Statuses" /> <SelectValue placeholder="All Statuses" />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
@@ -395,7 +395,7 @@ Proceed with activation?`;
{/* Plan Filter */} {/* Plan Filter */}
<div> <div>
<Select value={planFilter} onValueChange={setPlanFilter}> <Select value={planFilter} onValueChange={setPlanFilter}>
<SelectTrigger className="rounded-xl border-2 border-[#ddd8eb]"> <SelectTrigger className="rounded-xl border-2 border-chart-6">
<SelectValue placeholder="All Plans" /> <SelectValue placeholder="All Plans" />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
@@ -409,7 +409,7 @@ Proceed with activation?`;
</div> </div>
<div className="mt-4 flex items-center justify-between"> <div className="mt-4 flex items-center justify-between">
<div className="text-sm text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <div className="text-sm text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Showing {filteredSubscriptions.length} of {subscriptions.length} subscriptions Showing {filteredSubscriptions.length} of {subscriptions.length} subscriptions
</div> </div>
@@ -425,20 +425,20 @@ Proceed with activation?`;
{exporting ? 'Exporting...' : 'Export'} {exporting ? 'Exporting...' : 'Export'}
</Button> </Button>
</DropdownMenuTrigger> </DropdownMenuTrigger>
<DropdownMenuContent align="end" className="w-56 bg-white rounded-xl border-2 border-[#ddd8eb] shadow-lg"> <DropdownMenuContent align="end" className="w-56 bg-background rounded-xl border-2 border-chart-6 shadow-lg">
<DropdownMenuItem <DropdownMenuItem
onClick={() => handleExport('all')} onClick={() => handleExport('all')}
className="cursor-pointer hover:bg-[#f1eef9] rounded-lg p-3" className="cursor-pointer hover:bg-muted rounded-lg p-3"
> >
<FileDown className="h-4 w-4 mr-2 text-[#664fa3]" /> <FileDown className="h-4 w-4 mr-2 text-muted-foreground" />
<span className="text-[#422268]">Export All Subscriptions</span> <span className="text-primary">Export All Subscriptions</span>
</DropdownMenuItem> </DropdownMenuItem>
<DropdownMenuItem <DropdownMenuItem
onClick={() => handleExport('current')} onClick={() => handleExport('current')}
className="cursor-pointer hover:bg-[#f1eef9] rounded-lg p-3" className="cursor-pointer hover:bg-muted rounded-lg p-3"
> >
<FileDown className="h-4 w-4 mr-2 text-[#664fa3]" /> <FileDown className="h-4 w-4 mr-2 text-muted-foreground" />
<span className="text-[#422268]">Export Current View</span> <span className="text-primary">Export Current View</span>
</DropdownMenuItem> </DropdownMenuItem>
</DropdownMenuContent> </DropdownMenuContent>
</DropdownMenu> </DropdownMenu>
@@ -447,20 +447,20 @@ Proceed with activation?`;
</Card> </Card>
{/* Subscriptions Table */} {/* Subscriptions Table */}
<Card className="bg-white rounded-2xl border-2 border-[#ddd8eb] overflow-hidden"> <Card className="bg-background rounded-2xl border-2 border-chart-6 overflow-hidden">
{/* Mobile Card View */} {/* Mobile Card View */}
<div className="md:hidden p-4 space-y-4"> <div className="md:hidden p-4 space-y-4">
{filteredSubscriptions.length > 0 ? ( {filteredSubscriptions.length > 0 ? (
filteredSubscriptions.map((sub) => ( filteredSubscriptions.map((sub) => (
<Card key={sub.id} className="p-4 border border-[#ddd8eb] bg-[#f9f5ff]/30"> <Card key={sub.id} className="p-4 border border-chart-6 bg-[#f9f5ff]/30">
<div className="space-y-3"> <div className="space-y-3">
{/* Member Info */} {/* Member Info */}
<div className="flex justify-between items-start border-b border-[#ddd8eb] pb-3"> <div className="flex justify-between items-start border-b border-chart-6 pb-3">
<div className="flex-1"> <div className="flex-1">
<p className="font-semibold text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}> <p className="font-semibold text-primary" style={{ fontFamily: "'Inter', sans-serif" }}>
{sub.user.first_name} {sub.user.last_name} {sub.user.first_name} {sub.user.last_name}
</p> </p>
<p className="text-sm text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-sm text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{sub.user.email} {sub.user.email}
</p> </p>
</div> </div>
@@ -470,13 +470,13 @@ Proceed with activation?`;
{/* Plan & Period */} {/* Plan & Period */}
<div className="grid grid-cols-2 gap-3 text-sm"> <div className="grid grid-cols-2 gap-3 text-sm">
<div> <div>
<p className="text-xs text-[#664fa3] mb-1">Plan</p> <p className="text-xs text-muted-foreground mb-1">Plan</p>
<p className="font-medium text-[#422268]">{sub.plan.name}</p> <p className="font-medium text-primary">{sub.plan.name}</p>
<p className="text-xs text-[#664fa3]">{sub.plan.billing_cycle}</p> <p className="text-xs text-muted-foreground">{sub.plan.billing_cycle}</p>
</div> </div>
<div> <div>
<p className="text-xs text-[#664fa3] mb-1">Period</p> <p className="text-xs text-muted-foreground mb-1">Period</p>
<p className="text-[#422268]"> <p className="text-primary">
{new Date(sub.current_period_start).toLocaleDateString()} - {new Date(sub.current_period_start).toLocaleDateString()} -
{new Date(sub.current_period_end).toLocaleDateString()} {new Date(sub.current_period_end).toLocaleDateString()}
</p> </p>
@@ -484,22 +484,22 @@ Proceed with activation?`;
</div> </div>
{/* Pricing */} {/* Pricing */}
<div className="grid grid-cols-3 gap-2 text-sm bg-white/50 p-3 rounded"> <div className="grid grid-cols-3 gap-2 text-sm bg-background/50 p-3 rounded">
<div> <div>
<p className="text-xs text-[#664fa3] mb-1">Base Fee</p> <p className="text-xs text-muted-foreground mb-1">Base Fee</p>
<p className="font-medium text-[#422268]"> <p className="font-medium text-primary">
${(sub.base_fee_cents / 100).toFixed(2)} ${(sub.base_fee_cents / 100).toFixed(2)}
</p> </p>
</div> </div>
<div> <div>
<p className="text-xs text-[#664fa3] mb-1">Donation</p> <p className="text-xs text-muted-foreground mb-1">Donation</p>
<p className="font-medium text-[#422268]"> <p className="font-medium text-primary">
${(sub.donation_cents / 100).toFixed(2)} ${(sub.donation_cents / 100).toFixed(2)}
</p> </p>
</div> </div>
<div> <div>
<p className="text-xs text-[#664fa3] mb-1">Total</p> <p className="text-xs text-muted-foreground mb-1">Total</p>
<p className="font-semibold text-[#422268]"> <p className="font-semibold text-primary">
${(sub.total_cents / 100).toFixed(2)} ${(sub.total_cents / 100).toFixed(2)}
</p> </p>
</div> </div>
@@ -512,7 +512,7 @@ Proceed with activation?`;
size="sm" size="sm"
variant="outline" variant="outline"
onClick={() => handleEdit(sub)} onClick={() => handleEdit(sub)}
className="flex-1 text-[#664fa3] hover:bg-[#DDD8EB]" className="flex-1 text-muted-foreground hover:bg-chart-6"
> >
<Edit className="h-4 w-4 mr-2" /> <Edit className="h-4 w-4 mr-2" />
Edit Edit
@@ -534,7 +534,7 @@ Proceed with activation?`;
</Card> </Card>
)) ))
) : ( ) : (
<div className="p-12 text-center text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <div className="p-12 text-center text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
No subscriptions found No subscriptions found
</div> </div>
)} )}
@@ -544,29 +544,29 @@ Proceed with activation?`;
<div className="hidden md:block overflow-x-auto"> <div className="hidden md:block overflow-x-auto">
<table className="w-full"> <table className="w-full">
<thead> <thead>
<tr className="bg-[#DDD8EB]/20 border-b border-[#ddd8eb]"> <tr className="bg-chart-6/20 border-b border-chart-6">
<th className="text-left p-4 text-[#422268] font-semibold" style={{ fontFamily: "'Inter', sans-serif" }}> <th className="text-left p-4 text-primary font-semibold" style={{ fontFamily: "'Inter', sans-serif" }}>
Member Member
</th> </th>
<th className="text-left p-4 text-[#422268] font-semibold" style={{ fontFamily: "'Inter', sans-serif" }}> <th className="text-left p-4 text-primary font-semibold" style={{ fontFamily: "'Inter', sans-serif" }}>
Plan Plan
</th> </th>
<th className="text-left p-4 text-[#422268] font-semibold" style={{ fontFamily: "'Inter', sans-serif" }}> <th className="text-left p-4 text-primary font-semibold" style={{ fontFamily: "'Inter', sans-serif" }}>
Status Status
</th> </th>
<th className="text-left p-4 text-[#422268] font-semibold" style={{ fontFamily: "'Inter', sans-serif" }}> <th className="text-left p-4 text-primary font-semibold" style={{ fontFamily: "'Inter', sans-serif" }}>
Period Period
</th> </th>
<th className="text-right p-4 text-[#422268] font-semibold" style={{ fontFamily: "'Inter', sans-serif" }}> <th className="text-right p-4 text-primary font-semibold" style={{ fontFamily: "'Inter', sans-serif" }}>
Base Fee Base Fee
</th> </th>
<th className="text-right p-4 text-[#422268] font-semibold" style={{ fontFamily: "'Inter', sans-serif" }}> <th className="text-right p-4 text-primary font-semibold" style={{ fontFamily: "'Inter', sans-serif" }}>
Donation Donation
</th> </th>
<th className="text-right p-4 text-[#422268] font-semibold" style={{ fontFamily: "'Inter', sans-serif" }}> <th className="text-right p-4 text-primary font-semibold" style={{ fontFamily: "'Inter', sans-serif" }}>
Total Total
</th> </th>
<th className="text-center p-4 text-[#422268] font-semibold" style={{ fontFamily: "'Inter', sans-serif" }}> <th className="text-center p-4 text-primary font-semibold" style={{ fontFamily: "'Inter', sans-serif" }}>
Actions Actions
</th> </th>
</tr> </tr>
@@ -574,20 +574,20 @@ Proceed with activation?`;
<tbody> <tbody>
{filteredSubscriptions.length > 0 ? ( {filteredSubscriptions.length > 0 ? (
filteredSubscriptions.map((sub) => ( filteredSubscriptions.map((sub) => (
<tr key={sub.id} className="border-b border-[#ddd8eb] hover:bg-[#f9f5ff] transition-colors"> <tr key={sub.id} className="border-b border-chart-6 hover:bg-[#f9f5ff] transition-colors">
<td className="p-4"> <td className="p-4">
<div className="font-medium text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}> <div className="font-medium text-primary" style={{ fontFamily: "'Inter', sans-serif" }}>
{sub.user.first_name} {sub.user.last_name} {sub.user.first_name} {sub.user.last_name}
</div> </div>
<div className="text-sm text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <div className="text-sm text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{sub.user.email} {sub.user.email}
</div> </div>
</td> </td>
<td className="p-4"> <td className="p-4">
<div className="text-[#422268]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <div className="text-primary" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{sub.plan.name} {sub.plan.name}
</div> </div>
<div className="text-xs text-[#664fa3]"> <div className="text-xs text-muted-foreground">
{sub.plan.billing_cycle} {sub.plan.billing_cycle}
</div> </div>
</td> </td>
@@ -597,18 +597,18 @@ Proceed with activation?`;
</Badge> </Badge>
</td> </td>
<td className="p-4"> <td className="p-4">
<div className="text-sm text-[#422268]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <div className="text-sm text-primary" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<div>{formatDate(sub.start_date)}</div> <div>{formatDate(sub.start_date)}</div>
<div className="text-xs text-[#664fa3]">to {formatDate(sub.end_date)}</div> <div className="text-xs text-muted-foreground">to {formatDate(sub.end_date)}</div>
</div> </div>
</td> </td>
<td className="p-4 text-right text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}> <td className="p-4 text-right text-primary" style={{ fontFamily: "'Inter', sans-serif" }}>
{formatPrice(sub.base_subscription_cents || 0)} {formatPrice(sub.base_subscription_cents || 0)}
</td> </td>
<td className="p-4 text-right text-[#ff9e77]" style={{ fontFamily: "'Inter', sans-serif" }}> <td className="p-4 text-right text-accent" style={{ fontFamily: "'Inter', sans-serif" }}>
{formatPrice(sub.donation_cents || 0)} {formatPrice(sub.donation_cents || 0)}
</td> </td>
<td className="p-4 text-right font-semibold text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}> <td className="p-4 text-right font-semibold text-primary" style={{ fontFamily: "'Inter', sans-serif" }}>
{formatPrice(sub.amount_paid_cents || 0)} {formatPrice(sub.amount_paid_cents || 0)}
</td> </td>
<td className="p-4"> <td className="p-4">
@@ -618,7 +618,7 @@ Proceed with activation?`;
size="sm" size="sm"
variant="outline" variant="outline"
onClick={() => handleEdit(sub)} onClick={() => handleEdit(sub)}
className="text-[#664fa3] hover:bg-[#DDD8EB]" className="text-muted-foreground hover:bg-chart-6"
> >
<Edit className="h-4 w-4" /> <Edit className="h-4 w-4" />
</Button> </Button>
@@ -639,7 +639,7 @@ Proceed with activation?`;
)) ))
) : ( ) : (
<tr> <tr>
<td colSpan="8" className="p-12 text-center text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <td colSpan="8" className="p-12 text-center text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
No subscriptions found No subscriptions found
</td> </td>
</tr> </tr>
@@ -651,12 +651,12 @@ Proceed with activation?`;
{/* Edit Subscription Dialog */} {/* Edit Subscription Dialog */}
<Dialog open={editDialogOpen} onOpenChange={setEditDialogOpen}> <Dialog open={editDialogOpen} onOpenChange={setEditDialogOpen}>
<DialogContent className="sm:max-w-[500px] bg-white rounded-2xl"> <DialogContent className="sm:max-w-[500px] bg-background rounded-2xl">
<DialogHeader> <DialogHeader>
<DialogTitle className="text-2xl font-semibold text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}> <DialogTitle className="text-2xl font-semibold text-primary" style={{ fontFamily: "'Inter', sans-serif" }}>
Edit Subscription Edit Subscription
</DialogTitle> </DialogTitle>
<DialogDescription className="text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <DialogDescription className="text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Update subscription status or end date for {selectedSubscription?.user.first_name} {selectedSubscription?.user.last_name} Update subscription status or end date for {selectedSubscription?.user.first_name} {selectedSubscription?.user.last_name}
</DialogDescription> </DialogDescription>
</DialogHeader> </DialogHeader>
@@ -664,14 +664,14 @@ Proceed with activation?`;
<div className="space-y-6 py-4"> <div className="space-y-6 py-4">
{/* Status */} {/* Status */}
<div className="space-y-2"> <div className="space-y-2">
<Label htmlFor="status" className="text-[#422268] font-medium" style={{ fontFamily: "'Inter', sans-serif" }}> <Label htmlFor="status" className="text-primary font-medium" style={{ fontFamily: "'Inter', sans-serif" }}>
Status Status
</Label> </Label>
<Select <Select
value={editFormData.status} value={editFormData.status}
onValueChange={(value) => setEditFormData({ ...editFormData, status: value })} onValueChange={(value) => setEditFormData({ ...editFormData, status: value })}
> >
<SelectTrigger className="rounded-xl border-2 border-[#ddd8eb]"> <SelectTrigger className="rounded-xl border-2 border-chart-6">
<SelectValue placeholder="Select status" /> <SelectValue placeholder="Select status" />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
@@ -683,13 +683,12 @@ Proceed with activation?`;
{/* Warning Box - Show when status is different */} {/* Warning Box - Show when status is different */}
{selectedSubscription && editFormData.status !== selectedSubscription.status && ( {selectedSubscription && editFormData.status !== selectedSubscription.status && (
<div className={`mt-3 p-4 rounded-xl border-2 ${ <div className={`mt-3 p-4 rounded-xl border-2 ${editFormData.status === 'cancelled'
editFormData.status === 'cancelled' ? 'bg-red-50 border-red-300'
? 'bg-red-50 border-red-300' : editFormData.status === 'expired'
: editFormData.status === 'expired'
? 'bg-orange-50 border-orange-300' ? 'bg-orange-50 border-orange-300'
: 'bg-green-50 border-green-300' : 'bg-green-50 border-green-300'
}`}> }`}>
<div className="flex items-start gap-3"> <div className="flex items-start gap-3">
{editFormData.status === 'cancelled' || editFormData.status === 'expired' ? ( {editFormData.status === 'cancelled' || editFormData.status === 'expired' ? (
<AlertTriangle className="h-5 w-5 text-red-600 flex-shrink-0 mt-0.5" /> <AlertTriangle className="h-5 w-5 text-red-600 flex-shrink-0 mt-0.5" />
@@ -737,17 +736,17 @@ Proceed with activation?`;
{/* End Date */} {/* End Date */}
<div className="space-y-2"> <div className="space-y-2">
<Label htmlFor="end_date" className="text-[#422268] font-medium" style={{ fontFamily: "'Inter', sans-serif" }}> <Label htmlFor="end_date" className="text-primary font-medium" style={{ fontFamily: "'Inter', sans-serif" }}>
End Date End 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-[#664fa3]" /> <Calendar className="absolute left-4 top-1/2 transform -translate-y-1/2 h-5 w-5 text-muted-foreground" />
<Input <Input
id="end_date" id="end_date"
type="date" type="date"
value={editFormData.end_date} value={editFormData.end_date}
onChange={(e) => setEditFormData({ ...editFormData, end_date: e.target.value })} onChange={(e) => setEditFormData({ ...editFormData, end_date: e.target.value })}
className="pl-12 rounded-xl border-2 border-[#ddd8eb] focus:border-[#664fa3]" className="pl-12 rounded-xl border-2 border-chart-6 focus:border-muted-foreground"
/> />
</div> </div>
</div> </div>
@@ -758,7 +757,7 @@ Proceed with activation?`;
type="button" type="button"
variant="outline" variant="outline"
onClick={() => setEditDialogOpen(false)} onClick={() => setEditDialogOpen(false)}
className="rounded-full border-2 border-[#ddd8eb]" className="rounded-full border-2 border-chart-6"
disabled={isUpdating} disabled={isUpdating}
> >
Cancel Cancel

View File

@@ -218,12 +218,12 @@ const AdminUserView = () => {
</Button> </Button>
{/* User Profile Header */} {/* User Profile Header */}
<Card className="p-8 bg-white rounded-2xl border border-[#ddd8eb] mb-8"> <Card className="p-8 bg-background rounded-2xl border border-chart-6 mb-8">
<div className="flex items-start gap-6"> <div className="flex items-start gap-6">
{/* Avatar */} {/* Avatar */}
<Avatar className="h-24 w-24 border-4 border-[#ddd8eb]"> <Avatar className="h-24 w-24 border-4 border-chart-6">
<AvatarImage src={user.profile_photo_url} alt={`${user.first_name} ${user.last_name}`} /> <AvatarImage src={user.profile_photo_url} alt={`${user.first_name} ${user.last_name}`} />
<AvatarFallback className="bg-[#DDD8EB] text-[#422268] font-semibold text-3xl"> <AvatarFallback className="bg-chart-6 text-primary font-semibold text-3xl">
{user.first_name?.[0]}{user.last_name?.[0]} {user.first_name?.[0]}{user.last_name?.[0]}
</AvatarFallback> </AvatarFallback>
</Avatar> </Avatar>
@@ -231,7 +231,7 @@ const AdminUserView = () => {
{/* 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 text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}> <h1 className="text-3xl font-semibold text-primary" 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 */}
@@ -240,7 +240,7 @@ const AdminUserView = () => {
</div> </div>
{/* Contact Info */} {/* Contact Info */}
<div className="grid md:grid-cols-2 gap-4 text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <div className="grid md:grid-cols-2 gap-4 text-muted-foreground" 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>
@@ -263,8 +263,8 @@ const AdminUserView = () => {
</Card> </Card>
{/* Admin Actions */} {/* Admin Actions */}
<Card className="p-6 bg-white rounded-2xl border border-[#ddd8eb] mb-8"> <Card className="p-6 bg-background rounded-2xl border border-chart-6 mb-8">
<h2 className="text-lg font-semibold text-[#422268] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}> <h2 className="text-lg font-semibold text-primary 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">
@@ -272,7 +272,7 @@ const AdminUserView = () => {
onClick={handleResetPasswordRequest} onClick={handleResetPasswordRequest}
disabled={resetPasswordLoading} disabled={resetPasswordLoading}
variant="outline" variant="outline"
className="border-2 border-[#664fa3] text-[#664fa3] hover:bg-[#f1eef9] rounded-full px-4 py-2 disabled:opacity-50" className="border-2 border-muted-foreground text-muted-foreground hover:bg-muted 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'}
@@ -283,7 +283,7 @@ const AdminUserView = () => {
onClick={handleResendVerificationRequest} onClick={handleResendVerificationRequest}
disabled={resendVerificationLoading} disabled={resendVerificationLoading}
variant="outline" variant="outline"
className="border-2 border-[#ff9e77] text-[#ff9e77] hover:bg-[#FFF3E0] rounded-full px-4 py-2 disabled:opacity-50" className="border-2 border-accent text-accent 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'}
@@ -321,7 +321,7 @@ const AdminUserView = () => {
</Button> </Button>
)} )}
<div className="flex items-center gap-2 text-sm text-[#664fa3] ml-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <div className="flex items-center gap-2 text-sm text-muted-foreground 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>
@@ -329,28 +329,28 @@ const AdminUserView = () => {
</Card> </Card>
{/* Additional Details */} {/* Additional Details */}
<Card className="p-8 bg-white rounded-2xl border border-[#ddd8eb]"> <Card className="p-8 bg-background rounded-2xl border border-chart-6">
<h2 className="text-2xl font-semibold text-[#422268] mb-6" style={{ fontFamily: "'Inter', sans-serif" }}> <h2 className="text-2xl font-semibold text-primary 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-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Address</label> <label className="text-sm font-medium text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Address</label>
<p className="text-[#422268] mt-1" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>{user.address}</p> <p className="text-primary mt-1" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>{user.address}</p>
</div> </div>
<div> <div>
<label className="text-sm font-medium text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Date of Birth</label> <label className="text-sm font-medium text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Date of Birth</label>
<p className="text-[#422268] mt-1" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-primary 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-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Partner</label> <label className="text-sm font-medium text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Partner</label>
<p className="text-[#422268] mt-1" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-primary 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>
@@ -358,14 +358,14 @@ const AdminUserView = () => {
{user.referred_by_member_name && ( {user.referred_by_member_name && (
<div> <div>
<label className="text-sm font-medium text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Referred By</label> <label className="text-sm font-medium text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Referred By</label>
<p className="text-[#422268] mt-1" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>{user.referred_by_member_name}</p> <p className="text-primary 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-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Lead Sources</label> <label className="text-sm font-medium text-muted-foreground" 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>
@@ -378,32 +378,32 @@ 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-[#ddd8eb] mt-8"> <Card className="p-8 bg-background rounded-2xl border border-chart-6 mt-8">
<h2 className="text-2xl font-semibold text-[#422268] mb-6" style={{ fontFamily: "'Inter', sans-serif" }}> <h2 className="text-2xl font-semibold text-primary mb-6" style={{ fontFamily: "'Inter', sans-serif" }}>
Subscription Information Subscription Information
</h2> </h2>
{subscriptionsLoading ? ( {subscriptionsLoading ? (
<p className="text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Loading subscriptions...</p> <p className="text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Loading subscriptions...</p>
) : subscriptions.length === 0 ? ( ) : subscriptions.length === 0 ? (
<p className="text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>No subscriptions found for this member.</p> <p className="text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>No subscriptions found for this member.</p>
) : ( ) : (
<div className="space-y-6"> <div className="space-y-6">
{subscriptions.map((sub) => ( {subscriptions.map((sub) => (
<div key={sub.id} className="p-6 bg-[#F8F7FB] rounded-xl border border-[#ddd8eb]"> <div key={sub.id} className="p-6 bg-chart-7 rounded-xl border border-chart-6">
<div className="flex items-start justify-between mb-4"> <div className="flex items-start justify-between mb-4">
<div> <div>
<h3 className="text-lg font-semibold text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}> <h3 className="text-lg font-semibold text-primary" style={{ fontFamily: "'Inter', sans-serif" }}>
{sub.plan.name} {sub.plan.name}
</h3> </h3>
<p className="text-sm text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-sm text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{sub.plan.billing_cycle} {sub.plan.billing_cycle}
</p> </p>
</div> </div>
<Badge className={ <Badge className={
sub.status === 'active' ? 'bg-[#81B29A] text-white' : sub.status === 'active' ? 'bg-[#81B29A] text-white' :
sub.status === 'expired' ? 'bg-red-500 text-white' : sub.status === 'expired' ? 'bg-red-500 text-white' :
'bg-gray-400 text-white' 'bg-gray-400 text-white'
}> }>
{sub.status} {sub.status}
</Badge> </Badge>
@@ -411,51 +411,51 @@ const AdminUserView = () => {
<div className="grid md:grid-cols-2 gap-4 text-sm"> <div className="grid md:grid-cols-2 gap-4 text-sm">
<div> <div>
<label className="text-[#664fa3] font-medium" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Start Date</label> <label className="text-muted-foreground font-medium" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Start Date</label>
<p className="text-[#422268]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-primary" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{new Date(sub.start_date).toLocaleDateString()} {new Date(sub.start_date).toLocaleDateString()}
</p> </p>
</div> </div>
{sub.end_date && ( {sub.end_date && (
<div> <div>
<label className="text-[#664fa3] font-medium" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>End Date</label> <label className="text-muted-foreground font-medium" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>End Date</label>
<p className="text-[#422268]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-primary" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{new Date(sub.end_date).toLocaleDateString()} {new Date(sub.end_date).toLocaleDateString()}
</p> </p>
</div> </div>
)} )}
<div> <div>
<label className="text-[#664fa3] font-medium" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Base Amount</label> <label className="text-muted-foreground font-medium" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Base Amount</label>
<p className="text-[#422268]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-primary" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
${(sub.base_subscription_cents / 100).toFixed(2)} ${(sub.base_subscription_cents / 100).toFixed(2)}
</p> </p>
</div> </div>
{sub.donation_cents > 0 && ( {sub.donation_cents > 0 && (
<div> <div>
<label className="text-[#664fa3] font-medium" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Donation</label> <label className="text-muted-foreground font-medium" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Donation</label>
<p className="text-[#422268]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-primary" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
${(sub.donation_cents / 100).toFixed(2)} ${(sub.donation_cents / 100).toFixed(2)}
</p> </p>
</div> </div>
)} )}
<div> <div>
<label className="text-[#664fa3] font-medium" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Total Paid</label> <label className="text-muted-foreground font-medium" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Total Paid</label>
<p className="text-[#422268] font-semibold" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-primary font-semibold" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
${(sub.amount_paid_cents / 100).toFixed(2)} ${(sub.amount_paid_cents / 100).toFixed(2)}
</p> </p>
</div> </div>
{sub.payment_method && ( {sub.payment_method && (
<div> <div>
<label className="text-[#664fa3] font-medium" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Payment Method</label> <label className="text-muted-foreground font-medium" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Payment Method</label>
<p className="text-[#422268]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-primary" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{sub.payment_method} {sub.payment_method}
</p> </p>
</div> </div>
)} )}
{sub.stripe_subscription_id && ( {sub.stripe_subscription_id && (
<div className="md:col-span-2"> <div className="md:col-span-2">
<label className="text-[#664fa3] font-medium" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Stripe Subscription ID</label> <label className="text-muted-foreground font-medium" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Stripe Subscription ID</label>
<p className="text-[#422268] text-xs font-mono" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-primary text-xs font-mono" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{sub.stripe_subscription_id} {sub.stripe_subscription_id}
</p> </p>
</div> </div>

View File

@@ -279,44 +279,44 @@ const AdminValidations = () => {
<> <>
{/* Header */} {/* Header */}
<div className="mb-8"> <div className="mb-8">
<h1 className="text-4xl md:text-5xl font-semibold text-[#422268] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}> <h1 className="text-4xl md:text-5xl font-semibold text-primary mb-4" style={{ fontFamily: "'Inter', sans-serif" }}>
Validation Queue Validation Queue
</h1> </h1>
<p className="text-lg text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-lg text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Review and validate pending membership applications. Review and validate pending membership applications.
</p> </p>
</div> </div>
{/* Stats Card */} {/* Stats Card */}
<Card className="p-6 bg-white rounded-2xl border border-[#ddd8eb] mb-8"> <Card className="p-6 bg-background rounded-2xl border border-chart-6 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-[#664fa3] mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Total Pending</p> <p className="text-sm text-muted-foreground mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Total Pending</p>
<p className="text-3xl font-semibold text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}> <p className="text-3xl font-semibold text-primary" style={{ fontFamily: "'Inter', sans-serif" }}>
{pendingUsers.length} {pendingUsers.length}
</p> </p>
</div> </div>
<div> <div>
<p className="text-sm text-[#664fa3] mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Awaiting Email</p> <p className="text-sm text-muted-foreground mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Awaiting Email</p>
<p className="text-3xl font-semibold text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}> <p className="text-3xl font-semibold text-primary" 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-[#664fa3] mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Pending Validation</p> <p className="text-sm text-muted-foreground mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Pending Validation</p>
<p className="text-3xl font-semibold text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}> <p className="text-3xl font-semibold text-primary" style={{ fontFamily: "'Inter', sans-serif" }}>
{pendingUsers.filter(u => u.status === 'pending_validation').length} {pendingUsers.filter(u => u.status === 'pending_validation').length}
</p> </p>
</div> </div>
<div> <div>
<p className="text-sm text-[#664fa3] mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Pre-Validated</p> <p className="text-sm text-muted-foreground mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Pre-Validated</p>
<p className="text-3xl font-semibold text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}> <p className="text-3xl font-semibold text-primary" style={{ fontFamily: "'Inter', sans-serif" }}>
{pendingUsers.filter(u => u.status === 'pre_validated').length} {pendingUsers.filter(u => u.status === 'pre_validated').length}
</p> </p>
</div> </div>
<div> <div>
<p className="text-sm text-[#664fa3] mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Payment Pending</p> <p className="text-sm text-muted-foreground mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Payment Pending</p>
<p className="text-3xl font-semibold text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}> <p className="text-3xl font-semibold text-primary" 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>
@@ -330,20 +330,20 @@ const AdminValidations = () => {
</Card> </Card>
{/* Filter Card */} {/* Filter Card */}
<Card className="p-6 bg-white rounded-2xl border border-[#ddd8eb] mb-8"> <Card className="p-6 bg-background rounded-2xl border border-chart-6 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-[#664fa3]" /> <Search className="absolute left-4 top-1/2 transform -translate-y-1/2 h-5 w-5 text-muted-foreground" />
<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-[#ddd8eb] focus:border-[#664fa3]" className="pl-12 h-14 rounded-xl border-2 border-chart-6 focus:border-muted-foreground"
/> />
</div> </div>
<Select value={statusFilter} onValueChange={setStatusFilter}> <Select value={statusFilter} onValueChange={setStatusFilter}>
<SelectTrigger className="h-14 rounded-xl border-2 border-[#ddd8eb]"> <SelectTrigger className="h-14 rounded-xl border-2 border-chart-6">
<SelectValue placeholder="Filter by status" /> <SelectValue placeholder="Filter by status" />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
@@ -361,16 +361,16 @@ const AdminValidations = () => {
{/* Table */} {/* Table */}
{loading ? ( {loading ? (
<div className="text-center py-20"> <div className="text-center py-20">
<p className="text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Loading pending applications...</p> <p className="text-muted-foreground" 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-[#ddd8eb] overflow-hidden"> <Card className="bg-background rounded-2xl border border-chart-6 overflow-hidden">
<Table> <Table>
<TableHeader> <TableHeader>
<TableRow> <TableRow>
<TableHead <TableHead
className="cursor-pointer hover:bg-[#DDD8EB]/20" className="cursor-pointer hover:bg-chart-6/20"
onClick={() => handleSort('first_name')} onClick={() => handleSort('first_name')}
> >
Name {renderSortIcon('first_name')} Name {renderSortIcon('first_name')}
@@ -378,13 +378,13 @@ const AdminValidations = () => {
<TableHead>Email</TableHead> <TableHead>Email</TableHead>
<TableHead>Phone</TableHead> <TableHead>Phone</TableHead>
<TableHead <TableHead
className="cursor-pointer hover:bg-[#DDD8EB]/20" className="cursor-pointer hover:bg-chart-6/20"
onClick={() => handleSort('status')} onClick={() => handleSort('status')}
> >
Status {renderSortIcon('status')} Status {renderSortIcon('status')}
</TableHead> </TableHead>
<TableHead <TableHead
className="cursor-pointer hover:bg-[#DDD8EB]/20" className="cursor-pointer hover:bg-chart-6/20"
onClick={() => handleSort('created_at')} onClick={() => handleSort('created_at')}
> >
Registered {renderSortIcon('created_at')} Registered {renderSortIcon('created_at')}
@@ -426,7 +426,7 @@ const AdminValidations = () => {
onClick={() => handleBypassAndValidateRequest(user)} onClick={() => handleBypassAndValidateRequest(user)}
disabled={actionLoading === user.id} disabled={actionLoading === user.id}
size="sm" size="sm"
className="bg-[#DDD8EB] text-[#422268] hover:bg-white" className="bg-chart-6 text-primary hover:bg-background"
> >
{actionLoading === user.id ? 'Validating...' : 'Bypass & Validate'} {actionLoading === user.id ? 'Validating...' : 'Bypass & Validate'}
</Button> </Button>
@@ -450,7 +450,7 @@ const AdminValidations = () => {
<Button <Button
onClick={() => handleActivatePayment(user)} onClick={() => handleActivatePayment(user)}
size="sm" size="sm"
className="bg-[#DDD8EB] text-[#422268] hover:bg-white" className="bg-chart-6 text-primary hover:bg-background"
> >
<CheckCircle className="h-4 w-4 mr-1" /> <CheckCircle className="h-4 w-4 mr-1" />
Activate Payment Activate Payment
@@ -507,7 +507,7 @@ const AdminValidations = () => {
<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-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Show</p> <p className="text-sm text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Show</p>
<Select <Select
value={itemsPerPage.toString()} value={itemsPerPage.toString()}
onValueChange={(val) => { onValueChange={(val) => {
@@ -525,7 +525,7 @@ const AdminValidations = () => {
<SelectItem value="100">100</SelectItem> <SelectItem value="100">100</SelectItem>
</SelectContent> </SelectContent>
</Select> </Select>
<p className="text-sm text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-sm text-muted-foreground" 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>
@@ -544,7 +544,7 @@ const AdminValidations = () => {
{[...Array(totalPages)].map((_, i) => { {[...Array(totalPages)].map((_, i) => {
const showPage = i < 2 || i >= totalPages - 2 || const showPage = i < 2 || i >= totalPages - 2 ||
Math.abs(i - currentPage + 1) <= 1; Math.abs(i - currentPage + 1) <= 1;
if (!showPage && i === 2) { if (!showPage && i === 2) {
return ( return (
@@ -582,11 +582,11 @@ const AdminValidations = () => {
</> </>
) : ( ) : (
<div className="text-center py-20"> <div className="text-center py-20">
<Clock className="h-20 w-20 text-[#ddd8eb] mx-auto mb-6" /> <Clock className="h-20 w-20 text-chart-6 mx-auto mb-6" />
<h3 className="text-2xl font-semibold text-[#422268] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}> <h3 className="text-2xl font-semibold text-primary mb-4" style={{ fontFamily: "'Inter', sans-serif" }}>
No Pending Validations No Pending Validations
</h3> </h3>
<p className="text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-muted-foreground" 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

@@ -51,10 +51,10 @@ export default function Bylaws() {
if (loading) { if (loading) {
return ( return (
<div className="min-h-screen bg-white"> <div className="min-h-screen bg-background">
<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-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Loading bylaws... Loading bylaws...
</p> </p>
</div> </div>
@@ -63,30 +63,30 @@ export default function Bylaws() {
} }
return ( return (
<div className="min-h-screen bg-white"> <div className="min-h-screen bg-background">
<Navbar /> <Navbar />
<div className="max-w-5xl mx-auto px-6 py-12"> <div className="max-w-5xl mx-auto px-6 py-12">
{/* Header */} {/* Header */}
<div className="mb-8"> <div className="mb-8">
<h1 className="text-4xl font-semibold text-[#422268] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}> <h1 className="text-4xl font-semibold text-primary mb-4" style={{ fontFamily: "'Inter', sans-serif" }}>
LOAF Bylaws LOAF Bylaws
</h1> </h1>
<p className="text-[#664fa3] mb-6" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-muted-foreground mb-6" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Review the official governing bylaws and policies of the LOAF community. Review the official governing bylaws and policies of the LOAF community.
</p> </p>
</div> </div>
{/* Current Bylaws */} {/* Current Bylaws */}
{currentBylaws ? ( {currentBylaws ? (
<Card className="p-8 bg-white rounded-2xl border-2 border-[#664fa3] mb-6"> <Card className="p-8 bg-background rounded-2xl border-2 border-muted-foreground mb-6">
<div className="flex items-start gap-4 mb-6"> <div className="flex items-start gap-4 mb-6">
<div className="bg-gradient-to-br from-[#664fa3] to-[#422268] p-4 rounded-xl"> <div className="bg-gradient-to-br from-muted-foreground to-primary p-4 rounded-xl">
<Scale className="h-8 w-8 text-white" /> <Scale className="h-8 w-8 text-white" />
</div> </div>
<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">
<h2 className="text-2xl font-semibold text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}> <h2 className="text-2xl font-semibold text-primary" style={{ fontFamily: "'Inter', sans-serif" }}>
{currentBylaws.title} {currentBylaws.title}
</h2> </h2>
<Badge className="bg-[#81B29A] text-white"> <Badge className="bg-[#81B29A] text-white">
@@ -94,7 +94,7 @@ export default function Bylaws() {
Current Version Current Version
</Badge> </Badge>
</div> </div>
<div className="flex items-center gap-4 text-[#664fa3] mb-4"> <div className="flex items-center gap-4 text-muted-foreground mb-4">
<span style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <span style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Version: <strong>{currentBylaws.version}</strong> Version: <strong>{currentBylaws.version}</strong>
</span> </span>
@@ -106,7 +106,7 @@ export default function Bylaws() {
<Button <Button
onClick={() => window.open(currentBylaws.document_url, '_blank')} onClick={() => window.open(currentBylaws.document_url, '_blank')}
size="lg" size="lg"
className="bg-[#664fa3] text-white hover:bg-[#533a82] rounded-full flex items-center gap-2" className="bg-muted-foreground text-white hover:bg-[#533a82] rounded-full flex items-center gap-2"
> >
<ExternalLink className="h-5 w-5" /> <ExternalLink className="h-5 w-5" />
View Current Bylaws View Current Bylaws
@@ -115,9 +115,9 @@ export default function Bylaws() {
</div> </div>
</Card> </Card>
) : ( ) : (
<Card className="p-12 text-center bg-white rounded-2xl border border-[#ddd8eb] mb-6"> <Card className="p-12 text-center bg-background rounded-2xl border border-chart-6 mb-6">
<Scale className="h-16 w-16 text-[#ddd8eb] mx-auto mb-4" /> <Scale className="h-16 w-16 text-chart-6 mx-auto mb-4" />
<p className="text-[#664fa3] text-lg" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-muted-foreground text-lg" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
No current bylaws document available No current bylaws document available
</p> </p>
</Card> </Card>
@@ -129,7 +129,7 @@ export default function Bylaws() {
<Button <Button
onClick={() => setShowHistory(!showHistory)} onClick={() => setShowHistory(!showHistory)}
variant="outline" variant="outline"
className="w-full border-[#ddd8eb] text-[#664fa3] hover:bg-[#f1eef9] rounded-full flex items-center justify-center gap-2" className="w-full border-chart-6 text-muted-foreground hover:bg-muted rounded-full flex items-center justify-center gap-2"
> >
<History className="h-4 w-4" /> <History className="h-4 w-4" />
{showHistory ? 'Hide' : 'View'} Version History ({history.length - 1} previous {history.length - 1 === 1 ? 'version' : 'versions'}) {showHistory ? 'Hide' : 'View'} Version History ({history.length - 1} previous {history.length - 1 === 1 ? 'version' : 'versions'})
@@ -140,17 +140,17 @@ export default function Bylaws() {
{/* Version History */} {/* Version History */}
{showHistory && history.length > 1 && ( {showHistory && history.length > 1 && (
<div className="space-y-4"> <div className="space-y-4">
<h3 className="text-xl font-semibold text-[#422268] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}> <h3 className="text-xl font-semibold text-primary mb-4" style={{ fontFamily: "'Inter', sans-serif" }}>
Previous Versions Previous Versions
</h3> </h3>
{history.filter(b => !b.is_current).map(bylaws => ( {history.filter(b => !b.is_current).map(bylaws => (
<Card key={bylaws.id} className="p-6 bg-[#f9f7fc] rounded-xl border border-[#ddd8eb]"> <Card key={bylaws.id} className="p-6 bg-[#f9f7fc] rounded-xl border border-chart-6">
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<div> <div>
<h4 className="text-lg font-semibold text-[#422268] mb-1" style={{ fontFamily: "'Inter', sans-serif" }}> <h4 className="text-lg font-semibold text-primary mb-1" style={{ fontFamily: "'Inter', sans-serif" }}>
{bylaws.title} {bylaws.title}
</h4> </h4>
<div className="flex items-center gap-3 text-sm text-[#664fa3]"> <div className="flex items-center gap-3 text-sm text-muted-foreground">
<span>Version {bylaws.version}</span> <span>Version {bylaws.version}</span>
<span></span> <span></span>
<span>Effective {formatDate(bylaws.effective_date)}</span> <span>Effective {formatDate(bylaws.effective_date)}</span>
@@ -160,7 +160,7 @@ export default function Bylaws() {
onClick={() => window.open(bylaws.document_url, '_blank')} onClick={() => window.open(bylaws.document_url, '_blank')}
variant="outline" variant="outline"
size="sm" size="sm"
className="border-[#664fa3] text-[#664fa3] hover:bg-[#f1eef9] rounded-full flex items-center gap-2" className="border-muted-foreground text-muted-foreground hover:bg-muted rounded-full flex items-center gap-2"
> >
<ExternalLink className="h-4 w-4" /> <ExternalLink className="h-4 w-4" />
View View
@@ -172,14 +172,14 @@ export default function Bylaws() {
)} )}
{/* Information Card */} {/* Information Card */}
<Card className="mt-8 p-6 bg-[#f9f7fc] border border-[#ddd8eb]"> <Card className="mt-8 p-6 bg-[#f9f7fc] border border-chart-6">
<div className="flex items-start gap-3"> <div className="flex items-start gap-3">
<Scale className="h-5 w-5 text-[#664fa3] mt-1" /> <Scale className="h-5 w-5 text-muted-foreground mt-1" />
<div> <div>
<h4 className="font-semibold text-[#422268] mb-2" style={{ fontFamily: "'Inter', sans-serif" }}> <h4 className="font-semibold text-primary mb-2" style={{ fontFamily: "'Inter', sans-serif" }}>
About LOAF Bylaws About LOAF Bylaws
</h4> </h4>
<p className="text-sm text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-sm text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
The bylaws serve as the governing document for LOAF, outlining the organization's structure, The bylaws serve as the governing document for LOAF, outlining the organization's structure,
membership requirements, officer responsibilities, and operational procedures. All members are membership requirements, officer responsibilities, and operational procedures. All members are
encouraged to familiarize themselves with these guidelines. encouraged to familiarize themselves with these guidelines.

View File

@@ -107,11 +107,11 @@ const EventGallery = () => {
const EventCard = ({ event }) => ( const EventCard = ({ event }) => (
<Card <Card
className="p-6 bg-white rounded-2xl border border-[#ddd8eb] hover:shadow-lg hover:-translate-y-1 transition-all cursor-pointer h-full" className="p-6 bg-background rounded-2xl border border-chart-6 hover:shadow-lg hover:-translate-y-1 transition-all cursor-pointer h-full"
onClick={() => handleEventClick(event)} onClick={() => handleEventClick(event)}
> >
{/* Thumbnail */} {/* Thumbnail */}
<div className="relative h-48 mb-4 rounded-xl overflow-hidden bg-[#F8F7FB]"> <div className="relative h-48 mb-4 rounded-xl overflow-hidden bg-chart-7">
{event.thumbnail_url ? ( {event.thumbnail_url ? (
<img <img
src={event.thumbnail_url} src={event.thumbnail_url}
@@ -120,35 +120,35 @@ const EventGallery = () => {
/> />
) : ( ) : (
<div className="w-full h-full flex items-center justify-center"> <div className="w-full h-full flex items-center justify-center">
<ImageIcon className="h-16 w-16 text-[#ddd8eb]" /> <ImageIcon className="h-16 w-16 text-chart-6" />
</div> </div>
)} )}
<div className="absolute top-3 right-3"> <div className="absolute top-3 right-3">
<Badge className="bg-[#664fa3] text-white px-3 py-1 rounded-full"> <Badge className="bg-muted-foreground text-white px-3 py-1 rounded-full">
{event.gallery_count} {event.gallery_count === 1 ? 'photo' : 'photos'} {event.gallery_count} {event.gallery_count === 1 ? 'photo' : 'photos'}
</Badge> </Badge>
</div> </div>
</div> </div>
{/* Event Info */} {/* Event Info */}
<h3 className="text-xl font-semibold text-[#422268] mb-2" style={{ fontFamily: "'Inter', sans-serif" }}> <h3 className="text-xl font-semibold text-primary mb-2" style={{ fontFamily: "'Inter', sans-serif" }}>
{event.title} {event.title}
</h3> </h3>
{event.description && ( {event.description && (
<p className="text-[#664fa3] mb-3 line-clamp-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-muted-foreground mb-3 line-clamp-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{event.description} {event.description}
</p> </p>
)} )}
<div className="space-y-2 text-sm"> <div className="space-y-2 text-sm">
<div className="flex items-center gap-2 text-[#664fa3]"> <div className="flex items-center gap-2 text-muted-foreground">
<Calendar className="h-4 w-4" /> <Calendar className="h-4 w-4" />
<span style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <span style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{moment(event.start_at).format('MMMM D, YYYY')} {moment(event.start_at).format('MMMM D, YYYY')}
</span> </span>
</div> </div>
<div className="flex items-center gap-2 text-[#664fa3]"> <div className="flex items-center gap-2 text-muted-foreground">
<MapPin className="h-4 w-4" /> <MapPin className="h-4 w-4" />
<span style={{ fontFamily: "'Nunito Sans', sans-serif" }}>{event.location}</span> <span style={{ fontFamily: "'Nunito Sans', sans-serif" }}>{event.location}</span>
</div> </div>
@@ -159,16 +159,16 @@ const EventGallery = () => {
// Event Gallery Grid View // Event Gallery Grid View
if (!selectedEvent) { if (!selectedEvent) {
return ( return (
<div className="min-h-screen bg-white"> <div className="min-h-screen bg-background">
<Navbar /> <Navbar />
<div className="max-w-7xl mx-auto px-6 py-12"> <div className="max-w-7xl mx-auto px-6 py-12">
{/* Header */} {/* Header */}
<div className="mb-8"> <div className="mb-8">
<h1 className="text-4xl md:text-5xl font-semibold text-[#422268] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}> <h1 className="text-4xl md:text-5xl font-semibold text-primary mb-4" style={{ fontFamily: "'Inter', sans-serif" }}>
Event Gallery Event Gallery
</h1> </h1>
<p className="text-lg text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-lg text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Browse photos from past LOAF events. Browse photos from past LOAF events.
</p> </p>
</div> </div>
@@ -176,7 +176,7 @@ const EventGallery = () => {
{/* Events Grid */} {/* Events Grid */}
{loading ? ( {loading ? (
<div className="text-center py-20"> <div className="text-center py-20">
<p className="text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Loading galleries...</p> <p className="text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Loading galleries...</p>
</div> </div>
) : events.length > 0 ? ( ) : events.length > 0 ? (
<div className="grid md:grid-cols-2 lg:grid-cols-3 gap-8"> <div className="grid md:grid-cols-2 lg:grid-cols-3 gap-8">
@@ -186,11 +186,11 @@ const EventGallery = () => {
</div> </div>
) : ( ) : (
<div className="text-center py-20"> <div className="text-center py-20">
<ImageIcon className="h-20 w-20 text-[#ddd8eb] mx-auto mb-6" /> <ImageIcon className="h-20 w-20 text-chart-6 mx-auto mb-6" />
<h3 className="text-2xl font-semibold text-[#422268] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}> <h3 className="text-2xl font-semibold text-primary mb-4" style={{ fontFamily: "'Inter', sans-serif" }}>
No Event Galleries Yet No Event Galleries Yet
</h3> </h3>
<p className="text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Event photos will appear here once admins upload them. Event photos will appear here once admins upload them.
</p> </p>
</div> </div>
@@ -202,7 +202,7 @@ const EventGallery = () => {
// Individual Event Gallery View // Individual Event Gallery View
return ( return (
<div className="min-h-screen bg-white"> <div className="min-h-screen bg-background">
<Navbar /> <Navbar />
<div className="max-w-7xl mx-auto px-6 py-12"> <div className="max-w-7xl mx-auto px-6 py-12">
@@ -210,7 +210,7 @@ const EventGallery = () => {
<Button <Button
onClick={handleBackToEvents} onClick={handleBackToEvents}
variant="ghost" variant="ghost"
className="mb-6 text-[#664fa3] hover:text-[#422268] hover:bg-[#F8F7FB]" className="mb-6 text-muted-foreground hover:text-primary hover:bg-chart-7"
style={{ fontFamily: "'Inter', sans-serif" }} style={{ fontFamily: "'Inter', sans-serif" }}
> >
<ArrowLeft className="h-4 w-4 mr-2" /> <ArrowLeft className="h-4 w-4 mr-2" />
@@ -219,10 +219,10 @@ const EventGallery = () => {
{/* Event Header */} {/* Event Header */}
<div className="mb-8"> <div className="mb-8">
<h1 className="text-4xl md:text-5xl font-semibold text-[#422268] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}> <h1 className="text-4xl md:text-5xl font-semibold text-primary mb-4" style={{ fontFamily: "'Inter', sans-serif" }}>
{selectedEvent.title} {selectedEvent.title}
</h1> </h1>
<div className="flex flex-wrap gap-4 text-[#664fa3]"> <div className="flex flex-wrap gap-4 text-muted-foreground">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<Calendar className="h-5 w-5" /> <Calendar className="h-5 w-5" />
<span style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <span style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
@@ -233,7 +233,7 @@ const EventGallery = () => {
<MapPin className="h-5 w-5" /> <MapPin className="h-5 w-5" />
<span style={{ fontFamily: "'Nunito Sans', sans-serif" }}>{selectedEvent.location}</span> <span style={{ fontFamily: "'Nunito Sans', sans-serif" }}>{selectedEvent.location}</span>
</div> </div>
<Badge className="bg-[#664fa3] text-white px-3 py-1 rounded-full"> <Badge className="bg-muted-foreground text-white px-3 py-1 rounded-full">
{selectedEvent.gallery_count} {selectedEvent.gallery_count === 1 ? 'photo' : 'photos'} {selectedEvent.gallery_count} {selectedEvent.gallery_count === 1 ? 'photo' : 'photos'}
</Badge> </Badge>
</div> </div>
@@ -242,7 +242,7 @@ const EventGallery = () => {
{/* Gallery Grid */} {/* Gallery Grid */}
{galleryLoading ? ( {galleryLoading ? (
<div className="text-center py-20"> <div className="text-center py-20">
<p className="text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Loading images...</p> <p className="text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Loading images...</p>
</div> </div>
) : galleryImages.length > 0 ? ( ) : galleryImages.length > 0 ? (
<div className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4"> <div className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4">
@@ -272,11 +272,11 @@ const EventGallery = () => {
</div> </div>
) : ( ) : (
<div className="text-center py-20"> <div className="text-center py-20">
<ImageIcon className="h-20 w-20 text-[#ddd8eb] mx-auto mb-6" /> <ImageIcon className="h-20 w-20 text-chart-6 mx-auto mb-6" />
<h3 className="text-2xl font-semibold text-[#422268] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}> <h3 className="text-2xl font-semibold text-primary mb-4" style={{ fontFamily: "'Inter', sans-serif" }}>
No Photos Yet No Photos Yet
</h3> </h3>
<p className="text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Photos from this event will appear here once uploaded. Photos from this event will appear here once uploaded.
</p> </p>
</div> </div>

View File

@@ -29,10 +29,10 @@ export default function Financials() {
if (loading) { if (loading) {
return ( return (
<div className="min-h-screen bg-white"> <div className="min-h-screen bg-background">
<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-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Loading financial reports... Loading financial reports...
</p> </p>
</div> </div>
@@ -41,35 +41,35 @@ export default function Financials() {
} }
return ( return (
<div className="min-h-screen bg-white"> <div className="min-h-screen bg-background">
<Navbar /> <Navbar />
<div className="max-w-5xl mx-auto px-6 py-12"> <div className="max-w-5xl mx-auto px-6 py-12">
{/* Header */} {/* Header */}
<div className="mb-8"> <div className="mb-8">
<h1 className="text-4xl font-semibold text-[#422268] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}> <h1 className="text-4xl font-semibold text-primary mb-4" style={{ fontFamily: "'Inter', sans-serif" }}>
Financial Reports Financial Reports
</h1> </h1>
<p className="text-[#664fa3] mb-6" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-muted-foreground mb-6" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Access annual financial reports and stay informed about LOAF's fiscal responsibility. Access annual financial reports and stay informed about LOAF's fiscal responsibility.
</p> </p>
</div> </div>
{/* Reports List */} {/* Reports List */}
{reports.length === 0 ? ( {reports.length === 0 ? (
<Card className="p-12 text-center bg-white rounded-2xl border border-[#ddd8eb]"> <Card className="p-12 text-center bg-background rounded-2xl border border-chart-6">
<TrendingUp className="h-16 w-16 text-[#ddd8eb] mx-auto mb-4" /> <TrendingUp className="h-16 w-16 text-chart-6 mx-auto mb-4" />
<p className="text-[#664fa3] text-lg" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-muted-foreground text-lg" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
No financial reports available yet No financial reports available yet
</p> </p>
</Card> </Card>
) : ( ) : (
<div className="space-y-6"> <div className="space-y-6">
{reports.map(report => ( {reports.map(report => (
<Card key={report.id} className="p-8 bg-white rounded-2xl border border-[#ddd8eb] hover:shadow-lg transition-shadow"> <Card key={report.id} className="p-8 bg-background rounded-2xl border border-chart-6 hover:shadow-lg transition-shadow">
<div className="flex items-center gap-6"> <div className="flex items-center gap-6">
{/* Year Badge */} {/* Year Badge */}
<div className="bg-gradient-to-br from-[#664fa3] to-[#422268] p-6 rounded-xl text-white min-w-[120px] text-center"> <div className="bg-gradient-to-br from-muted-foreground to-primary p-6 rounded-xl text-white min-w-[120px] text-center">
<DollarSign className="h-8 w-8 mx-auto mb-2" /> <DollarSign className="h-8 w-8 mx-auto mb-2" />
<div className="text-3xl font-bold" style={{ fontFamily: "'Inter', sans-serif" }}> <div className="text-3xl font-bold" style={{ fontFamily: "'Inter', sans-serif" }}>
{report.year} {report.year}
@@ -79,17 +79,17 @@ export default function Financials() {
{/* Report Details */} {/* Report Details */}
<div className="flex-1"> <div className="flex-1">
<h3 className="text-2xl font-semibold text-[#422268] mb-2" style={{ fontFamily: "'Inter', sans-serif" }}> <h3 className="text-2xl font-semibold text-primary mb-2" style={{ fontFamily: "'Inter', sans-serif" }}>
{report.title} {report.title}
</h3> </h3>
<div className="flex items-center gap-2 mb-4"> <div className="flex items-center gap-2 mb-4">
<Badge variant="outline" className="border-[#664fa3] text-[#664fa3]"> <Badge variant="outline" className="border-muted-foreground text-muted-foreground">
{report.document_type === 'google_drive' ? 'Google Drive' : report.document_type.toUpperCase()} {report.document_type === 'google_drive' ? 'Google Drive' : report.document_type.toUpperCase()}
</Badge> </Badge>
</div> </div>
<Button <Button
onClick={() => window.open(report.document_url, '_blank')} onClick={() => window.open(report.document_url, '_blank')}
className="bg-[#664fa3] text-white hover:bg-[#533a82] rounded-full flex items-center gap-2" className="bg-muted-foreground text-white hover:bg-[#533a82] rounded-full flex items-center gap-2"
> >
<ExternalLink className="h-4 w-4" /> <ExternalLink className="h-4 w-4" />
View Report View Report
@@ -103,14 +103,14 @@ export default function Financials() {
{/* Transparency Note */} {/* Transparency Note */}
{reports.length > 0 && ( {reports.length > 0 && (
<Card className="mt-8 p-6 bg-[#f9f7fc] border border-[#ddd8eb]"> <Card className="mt-8 p-6 bg-[#f9f7fc] border border-chart-6">
<div className="flex items-start gap-3"> <div className="flex items-start gap-3">
<TrendingUp className="h-5 w-5 text-[#664fa3] mt-1" /> <TrendingUp className="h-5 w-5 text-muted-foreground mt-1" />
<div> <div>
<h4 className="font-semibold text-[#422268] mb-2" style={{ fontFamily: "'Inter', sans-serif" }}> <h4 className="font-semibold text-primary mb-2" style={{ fontFamily: "'Inter', sans-serif" }}>
Transparency & Accountability Transparency & Accountability
</h4> </h4>
<p className="text-sm text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-sm text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
LOAF is committed to financial transparency. These reports provide detailed information about our LOAF is committed to financial transparency. These reports provide detailed information about our
revenue, expenses, and how member contributions support our community programs and operations. revenue, expenses, and how member contributions support our community programs and operations.
</p> </p>

View File

@@ -124,10 +124,10 @@ export default function MemberCalendar() {
if (loading) { if (loading) {
return ( return (
<div className="min-h-screen bg-white"> <div className="min-h-screen bg-background">
<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-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Loading calendar... Loading calendar...
</p> </p>
</div> </div>
@@ -136,15 +136,15 @@ export default function MemberCalendar() {
} }
return ( return (
<div className="min-h-screen bg-white"> <div className="min-h-screen bg-background">
<Navbar /> <Navbar />
<div className="max-w-7xl mx-auto px-6 py-12"> <div className="max-w-7xl mx-auto px-6 py-12">
<div className="mb-8"> <div className="mb-8">
<h1 className="text-4xl font-semibold text-[#422268] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}> <h1 className="text-4xl font-semibold text-primary mb-4" style={{ fontFamily: "'Inter', sans-serif" }}>
Event Calendar Event Calendar
</h1> </h1>
<p className="text-[#664fa3] mb-6" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-muted-foreground mb-6" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
View and manage your event RSVPs. Click on any event to see details and update your RSVP. View and manage your event RSVPs. Click on any event to see details and update your RSVP.
</p> </p>
@@ -157,25 +157,25 @@ export default function MemberCalendar() {
<div className="flex gap-4 ml-auto"> <div className="flex gap-4 ml-auto">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<div className="w-4 h-4 rounded bg-[#81B29A]"></div> <div className="w-4 h-4 rounded bg-[#81B29A]"></div>
<span className="text-sm text-[#664fa3]">Going</span> <span className="text-sm text-muted-foreground">Going</span>
</div> </div>
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<div className="w-4 h-4 rounded bg-[#fb923c]"></div> <div className="w-4 h-4 rounded bg-[#fb923c]"></div>
<span className="text-sm text-[#664fa3]">Maybe</span> <span className="text-sm text-muted-foreground">Maybe</span>
</div> </div>
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<div className="w-4 h-4 rounded bg-[#9ca3af]"></div> <div className="w-4 h-4 rounded bg-[#9ca3af]"></div>
<span className="text-sm text-[#664fa3]">Not Going</span> <span className="text-sm text-muted-foreground">Not Going</span>
</div> </div>
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<div className="w-4 h-4 rounded bg-[#DDD8EB]"></div> <div className="w-4 h-4 rounded bg-chart-6"></div>
<span className="text-sm text-[#664fa3]">No RSVP</span> <span className="text-sm text-muted-foreground">No RSVP</span>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<Card className="p-6 bg-white rounded-2xl border border-[#ddd8eb] shadow-lg"> <Card className="p-6 bg-background rounded-2xl border border-chart-6 shadow-lg">
<Calendar <Calendar
localizer={localizer} localizer={localizer}
events={calendarEvents} events={calendarEvents}
@@ -200,18 +200,17 @@ export default function MemberCalendar() {
<> <>
<DialogHeader> <DialogHeader>
<div className="flex items-center gap-3 mb-2"> <div className="flex items-center gap-3 mb-2">
<div className="bg-[#DDD8EB]/20 p-3 rounded-lg"> <div className="bg-chart-6/20 p-3 rounded-lg">
<CalendarIcon className="h-6 w-6 text-[#664fa3]" /> <CalendarIcon className="h-6 w-6 text-muted-foreground" />
</div> </div>
{selectedEvent.user_rsvp_status && ( {selectedEvent.user_rsvp_status && (
<Badge <Badge
className={`px-3 py-1 rounded-full text-sm ${ className={`px-3 py-1 rounded-full text-sm ${selectedEvent.user_rsvp_status === 'yes'
selectedEvent.user_rsvp_status === 'yes' ? 'bg-[#81B29A] text-white'
? 'bg-[#81B29A] text-white' : selectedEvent.user_rsvp_status === 'no'
: selectedEvent.user_rsvp_status === 'no'
? 'bg-gray-400 text-white' ? 'bg-gray-400 text-white'
: 'bg-orange-400 text-white' : 'bg-orange-400 text-white'
}`} }`}
> >
{selectedEvent.user_rsvp_status === 'yes' && 'Going'} {selectedEvent.user_rsvp_status === 'yes' && 'Going'}
{selectedEvent.user_rsvp_status === 'no' && 'Not Going'} {selectedEvent.user_rsvp_status === 'no' && 'Not Going'}
@@ -219,14 +218,14 @@ export default function MemberCalendar() {
</Badge> </Badge>
)} )}
</div> </div>
<DialogTitle className="text-2xl text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}> <DialogTitle className="text-2xl text-primary" style={{ fontFamily: "'Inter', sans-serif" }}>
{selectedEvent.title} {selectedEvent.title}
</DialogTitle> </DialogTitle>
</DialogHeader> </DialogHeader>
<div className="space-y-4 mt-4"> <div className="space-y-4 mt-4">
<div className="space-y-3"> <div className="space-y-3">
<div className="flex items-center gap-3 text-[#664fa3]"> <div className="flex items-center gap-3 text-muted-foreground">
<CalendarIcon className="h-5 w-5" /> <CalendarIcon className="h-5 w-5" />
<span style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <span style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{new Date(selectedEvent.start_at).toLocaleDateString('en-US', { {new Date(selectedEvent.start_at).toLocaleDateString('en-US', {
@@ -237,17 +236,17 @@ export default function MemberCalendar() {
})} })}
</span> </span>
</div> </div>
<div className="flex items-center gap-3 text-[#664fa3]"> <div className="flex items-center gap-3 text-muted-foreground">
<Clock className="h-5 w-5" /> <Clock className="h-5 w-5" />
<span style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <span style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{new Date(selectedEvent.start_at).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })} - {new Date(selectedEvent.end_at).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })} {new Date(selectedEvent.start_at).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })} - {new Date(selectedEvent.end_at).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}
</span> </span>
</div> </div>
<div className="flex items-center gap-3 text-[#664fa3]"> <div className="flex items-center gap-3 text-muted-foreground">
<MapPin className="h-5 w-5" /> <MapPin className="h-5 w-5" />
<span style={{ fontFamily: "'Nunito Sans', sans-serif" }}>{selectedEvent.location}</span> <span style={{ fontFamily: "'Nunito Sans', sans-serif" }}>{selectedEvent.location}</span>
</div> </div>
<div className="flex items-center gap-3 text-[#664fa3]"> <div className="flex items-center gap-3 text-muted-foreground">
<Users className="h-5 w-5" /> <Users className="h-5 w-5" />
<span style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <span style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{selectedEvent.rsvp_count || 0} {selectedEvent.rsvp_count === 1 ? 'person' : 'people'} attending {selectedEvent.rsvp_count || 0} {selectedEvent.rsvp_count === 1 ? 'person' : 'people'} attending
@@ -257,18 +256,18 @@ export default function MemberCalendar() {
</div> </div>
{selectedEvent.description && ( {selectedEvent.description && (
<div className="pt-4 border-t border-[#ddd8eb]"> <div className="pt-4 border-t border-chart-6">
<h3 className="text-lg font-semibold text-[#422268] mb-2" style={{ fontFamily: "'Inter', sans-serif" }}> <h3 className="text-lg font-semibold text-primary mb-2" style={{ fontFamily: "'Inter', sans-serif" }}>
About This Event About This Event
</h3> </h3>
<p className="text-[#664fa3] leading-relaxed whitespace-pre-line" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-muted-foreground leading-relaxed whitespace-pre-line" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{selectedEvent.description} {selectedEvent.description}
</p> </p>
</div> </div>
)} )}
<div className="pt-4 border-t border-[#ddd8eb]"> <div className="pt-4 border-t border-chart-6">
<h3 className="text-lg font-semibold text-[#422268] mb-3" style={{ fontFamily: "'Inter', sans-serif" }}> <h3 className="text-lg font-semibold text-primary mb-3" style={{ fontFamily: "'Inter', sans-serif" }}>
Your RSVP Your RSVP
</h3> </h3>
<div className="flex gap-3 flex-wrap"> <div className="flex gap-3 flex-wrap">
@@ -276,11 +275,10 @@ export default function MemberCalendar() {
onClick={() => handleRSVP('yes')} onClick={() => handleRSVP('yes')}
disabled={rsvpLoading} disabled={rsvpLoading}
size="sm" size="sm"
className={`rounded-full px-6 flex items-center gap-2 ${ className={`rounded-full px-6 flex items-center gap-2 ${selectedEvent.user_rsvp_status === 'yes'
selectedEvent.user_rsvp_status === 'yes' ? 'bg-[#81B29A] text-white hover:bg-[#66927e]'
? 'bg-[#81B29A] text-white hover:bg-[#66927e]' : 'bg-chart-6 text-primary hover:bg-[#c4bed8]'
: 'bg-[#DDD8EB] text-[#422268] hover:bg-[#c4bed8]' }`}
}`}
> >
<Check className="h-4 w-4" /> <Check className="h-4 w-4" />
I'm Going I'm Going
@@ -290,11 +288,10 @@ export default function MemberCalendar() {
disabled={rsvpLoading} disabled={rsvpLoading}
size="sm" size="sm"
variant="outline" variant="outline"
className={`rounded-full px-6 flex items-center gap-2 border-2 ${ className={`rounded-full px-6 flex items-center gap-2 border-2 ${selectedEvent.user_rsvp_status === 'maybe'
selectedEvent.user_rsvp_status === 'maybe' ? 'border-orange-400 bg-orange-100 text-orange-700'
? 'border-orange-400 bg-orange-100 text-orange-700' : 'border-muted-foreground text-muted-foreground hover:bg-muted'
: 'border-[#664fa3] text-[#664fa3] hover:bg-[#f1eef9]' }`}
}`}
> >
<HelpCircle className="h-4 w-4" /> <HelpCircle className="h-4 w-4" />
Maybe Maybe
@@ -304,11 +301,10 @@ export default function MemberCalendar() {
disabled={rsvpLoading} disabled={rsvpLoading}
size="sm" size="sm"
variant="outline" variant="outline"
className={`rounded-full px-6 flex items-center gap-2 border-2 ${ className={`rounded-full px-6 flex items-center gap-2 border-2 ${selectedEvent.user_rsvp_status === 'no'
selectedEvent.user_rsvp_status === 'no' ? 'border-gray-400 bg-gray-100 text-gray-700'
? 'border-gray-400 bg-gray-100 text-gray-700' : 'border-gray-400 text-gray-600 hover:bg-gray-50'
: 'border-gray-400 text-gray-600 hover:bg-gray-50' }`}
}`}
> >
<X className="h-4 w-4" /> <X className="h-4 w-4" />
Can't Attend Can't Attend
@@ -316,8 +312,8 @@ export default function MemberCalendar() {
</div> </div>
</div> </div>
<div className="pt-4 border-t border-[#ddd8eb]"> <div className="pt-4 border-t border-chart-6">
<h3 className="text-lg font-semibold text-[#422268] mb-3" style={{ fontFamily: "'Inter', sans-serif" }}> <h3 className="text-lg font-semibold text-primary mb-3" style={{ fontFamily: "'Inter', sans-serif" }}>
Add to Your Calendar Add to Your Calendar
</h3> </h3>
<AddToCalendarButton <AddToCalendarButton

View File

@@ -191,11 +191,11 @@ const MemberProfile = () => {
if (loading) { if (loading) {
return ( return (
<div className="min-h-screen bg-white"> <div className="min-h-screen bg-background">
<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 py-20"> <div className="text-center py-20">
<p className="text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Loading profile...</p> <p className="text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Loading profile...</p>
</div> </div>
</div> </div>
</div> </div>
@@ -203,24 +203,24 @@ const MemberProfile = () => {
} }
return ( return (
<div className="min-h-screen bg-white"> <div className="min-h-screen bg-background">
<Navbar /> <Navbar />
<div className="max-w-4xl mx-auto px-6 py-12"> <div className="max-w-4xl mx-auto px-6 py-12">
{/* Header */} {/* Header */}
<div className="mb-8"> <div className="mb-8">
<h1 className="text-4xl md:text-5xl font-semibold text-[#422268] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}> <h1 className="text-4xl md:text-5xl font-semibold text-primary mb-4" style={{ fontFamily: "'Inter', sans-serif" }}>
Member Profile Member Profile
</h1> </h1>
<p className="text-lg text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-lg text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Enhance your profile with a photo and social media links. Enhance your profile with a photo and social media links.
</p> </p>
</div> </div>
<form onSubmit={handleSubmit} className="space-y-8"> <form onSubmit={handleSubmit} className="space-y-8">
{/* Profile Photo Section */} {/* Profile Photo Section */}
<Card className="p-8 bg-white border-[#ddd8eb] rounded-2xl"> <Card className="p-8 bg-background border-chart-6 rounded-2xl">
<h2 className="text-2xl font-semibold text-[#422268] mb-6" style={{ fontFamily: "'Inter', sans-serif" }}> <h2 className="text-2xl font-semibold text-primary mb-6" style={{ fontFamily: "'Inter', sans-serif" }}>
Profile Photo Profile Photo
</h2> </h2>
@@ -231,7 +231,7 @@ const MemberProfile = () => {
<img <img
src={previewImage} src={previewImage}
alt="Profile" alt="Profile"
className="w-40 h-40 rounded-full object-cover border-4 border-[#ddd8eb]" className="w-40 h-40 rounded-full object-cover border-4 border-chart-6"
/> />
<Button <Button
type="button" type="button"
@@ -242,8 +242,8 @@ const MemberProfile = () => {
</Button> </Button>
</div> </div>
) : ( ) : (
<div className="w-40 h-40 rounded-full bg-[#F8F7FB] border-4 border-[#ddd8eb] flex items-center justify-center"> <div className="w-40 h-40 rounded-full bg-chart-7 border-4 border-chart-6 flex items-center justify-center">
<User className="h-20 w-20 text-[#ddd8eb]" /> <User className="h-20 w-20 text-chart-6" />
</div> </div>
)} )}
</div> </div>
@@ -260,7 +260,7 @@ const MemberProfile = () => {
type="button" type="button"
onClick={() => fileInputRef.current?.click()} onClick={() => fileInputRef.current?.click()}
disabled={uploading} disabled={uploading}
className="bg-[#664fa3] hover:bg-[#422268] text-white rounded-xl px-6 py-3 flex items-center gap-2" className="bg-muted-foreground hover:bg-primary text-white rounded-xl px-6 py-3 flex items-center gap-2"
style={{ fontFamily: "'Inter', sans-serif" }} style={{ fontFamily: "'Inter', sans-serif" }}
> >
{uploading ? ( {uploading ? (
@@ -275,7 +275,7 @@ const MemberProfile = () => {
</> </>
)} )}
</Button> </Button>
<p className="text-sm text-[#664fa3] mt-3" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-sm text-muted-foreground mt-3" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
JPG, PNG, WebP, or GIF. Max 50MB. JPG, PNG, WebP, or GIF. Max 50MB.
</p> </p>
</div> </div>
@@ -283,14 +283,14 @@ const MemberProfile = () => {
</Card> </Card>
{/* Social Media Section */} {/* Social Media Section */}
<Card className="p-8 bg-white border-[#ddd8eb] rounded-2xl"> <Card className="p-8 bg-background border-chart-6 rounded-2xl">
<h2 className="text-2xl font-semibold text-[#422268] mb-6" style={{ fontFamily: "'Inter', sans-serif" }}> <h2 className="text-2xl font-semibold text-primary mb-6" style={{ fontFamily: "'Inter', sans-serif" }}>
Social Media Links Social Media Links
</h2> </h2>
<div className="space-y-6"> <div className="space-y-6">
<div> <div>
<Label className="flex items-center gap-2 text-[#422268] mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <Label className="flex items-center gap-2 text-primary mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<Facebook className="h-4 w-4 text-[#1877F2]" /> <Facebook className="h-4 w-4 text-[#1877F2]" />
Facebook Profile URL Facebook Profile URL
</Label> </Label>
@@ -300,13 +300,13 @@ const MemberProfile = () => {
value={formData.social_media_facebook} value={formData.social_media_facebook}
onChange={handleInputChange} onChange={handleInputChange}
placeholder="https://facebook.com/yourprofile" placeholder="https://facebook.com/yourprofile"
className="border-[#ddd8eb] rounded-xl focus:border-[#664fa3] focus:ring-[#664fa3]" className="border-chart-6 rounded-xl focus:border-muted-foreground focus:ring-muted-foreground"
style={{ fontFamily: "'Nunito Sans', sans-serif" }} style={{ fontFamily: "'Nunito Sans', sans-serif" }}
/> />
</div> </div>
<div> <div>
<Label className="flex items-center gap-2 text-[#422268] mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <Label className="flex items-center gap-2 text-primary mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<Instagram className="h-4 w-4 text-[#E4405F]" /> <Instagram className="h-4 w-4 text-[#E4405F]" />
Instagram Profile URL Instagram Profile URL
</Label> </Label>
@@ -316,13 +316,13 @@ const MemberProfile = () => {
value={formData.social_media_instagram} value={formData.social_media_instagram}
onChange={handleInputChange} onChange={handleInputChange}
placeholder="https://instagram.com/yourprofile" placeholder="https://instagram.com/yourprofile"
className="border-[#ddd8eb] rounded-xl focus:border-[#664fa3] focus:ring-[#664fa3]" className="border-chart-6 rounded-xl focus:border-muted-foreground focus:ring-muted-foreground"
style={{ fontFamily: "'Nunito Sans', sans-serif" }} style={{ fontFamily: "'Nunito Sans', sans-serif" }}
/> />
</div> </div>
<div> <div>
<Label className="flex items-center gap-2 text-[#422268] mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <Label className="flex items-center gap-2 text-primary mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<Twitter className="h-4 w-4 text-[#1DA1F2]" /> <Twitter className="h-4 w-4 text-[#1DA1F2]" />
Twitter/X Profile URL Twitter/X Profile URL
</Label> </Label>
@@ -332,13 +332,13 @@ const MemberProfile = () => {
value={formData.social_media_twitter} value={formData.social_media_twitter}
onChange={handleInputChange} onChange={handleInputChange}
placeholder="https://twitter.com/yourprofile" placeholder="https://twitter.com/yourprofile"
className="border-[#ddd8eb] rounded-xl focus:border-[#664fa3] focus:ring-[#664fa3]" className="border-chart-6 rounded-xl focus:border-muted-foreground focus:ring-muted-foreground"
style={{ fontFamily: "'Nunito Sans', sans-serif" }} style={{ fontFamily: "'Nunito Sans', sans-serif" }}
/> />
</div> </div>
<div> <div>
<Label className="flex items-center gap-2 text-[#422268] mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <Label className="flex items-center gap-2 text-primary mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<Linkedin className="h-4 w-4 text-[#0A66C2]" /> <Linkedin className="h-4 w-4 text-[#0A66C2]" />
LinkedIn Profile URL LinkedIn Profile URL
</Label> </Label>
@@ -348,7 +348,7 @@ const MemberProfile = () => {
value={formData.social_media_linkedin} value={formData.social_media_linkedin}
onChange={handleInputChange} onChange={handleInputChange}
placeholder="https://linkedin.com/in/yourprofile" placeholder="https://linkedin.com/in/yourprofile"
className="border-[#ddd8eb] rounded-xl focus:border-[#664fa3] focus:ring-[#664fa3]" className="border-chart-6 rounded-xl focus:border-muted-foreground focus:ring-muted-foreground"
style={{ fontFamily: "'Nunito Sans', sans-serif" }} style={{ fontFamily: "'Nunito Sans', sans-serif" }}
/> />
</div> </div>
@@ -356,33 +356,33 @@ const MemberProfile = () => {
</Card> </Card>
{/* Directory Settings Section */} {/* Directory Settings Section */}
<Card className="p-8 bg-white border-[#ddd8eb] rounded-2xl"> <Card className="p-8 bg-background border-chart-6 rounded-2xl">
<h2 className="text-2xl font-semibold text-[#422268] mb-6" style={{ fontFamily: "'Inter', sans-serif" }}> <h2 className="text-2xl font-semibold text-primary mb-6" style={{ fontFamily: "'Inter', sans-serif" }}>
Directory Settings Directory Settings
</h2> </h2>
<div className="space-y-6"> <div className="space-y-6">
<div className="flex items-center justify-between p-4 bg-[#F8F7FB] rounded-xl"> <div className="flex items-center justify-between p-4 bg-chart-7 rounded-xl">
<div className="flex-1"> <div className="flex-1">
<Label className="text-[#422268] font-medium flex items-center gap-2" style={{ fontFamily: "'Inter', sans-serif" }}> <Label className="text-primary font-medium flex items-center gap-2" style={{ fontFamily: "'Inter', sans-serif" }}>
<Eye className="h-4 w-4 text-[#664fa3]" /> <Eye className="h-4 w-4 text-muted-foreground" />
Show in Members Directory Show in Members Directory
</Label> </Label>
<p className="text-sm text-[#664fa3] mt-1" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-sm text-muted-foreground mt-1" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Allow other members to see your profile in the directory Allow other members to see your profile in the directory
</p> </p>
</div> </div>
<Switch <Switch
checked={formData.show_in_directory} checked={formData.show_in_directory}
onCheckedChange={handleSwitchChange} onCheckedChange={handleSwitchChange}
className="data-[state=checked]:bg-[#664fa3]" className="data-[state=checked]:bg-muted-foreground"
/> />
</div> </div>
{formData.show_in_directory && ( {formData.show_in_directory && (
<> <>
<div> <div>
<Label className="text-[#422268] mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <Label className="text-primary mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Directory Email (visible to members) Directory Email (visible to members)
</Label> </Label>
<Input <Input
@@ -391,13 +391,13 @@ const MemberProfile = () => {
value={formData.directory_email} value={formData.directory_email}
onChange={handleInputChange} onChange={handleInputChange}
placeholder="public.email@example.com" placeholder="public.email@example.com"
className="border-[#ddd8eb] rounded-xl focus:border-[#664fa3] focus:ring-[#664fa3]" className="border-chart-6 rounded-xl focus:border-muted-foreground focus:ring-muted-foreground"
style={{ fontFamily: "'Nunito Sans', sans-serif" }} style={{ fontFamily: "'Nunito Sans', sans-serif" }}
/> />
</div> </div>
<div> <div>
<Label className="text-[#422268] mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <Label className="text-primary mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Bio (visible to members) Bio (visible to members)
</Label> </Label>
<Textarea <Textarea
@@ -406,14 +406,14 @@ const MemberProfile = () => {
onChange={handleInputChange} onChange={handleInputChange}
placeholder="Tell other members about yourself..." placeholder="Tell other members about yourself..."
rows={4} rows={4}
className="border-[#ddd8eb] rounded-xl focus:border-[#664fa3] focus:ring-[#664fa3]" className="border-chart-6 rounded-xl focus:border-muted-foreground focus:ring-muted-foreground"
style={{ fontFamily: "'Nunito Sans', sans-serif" }} style={{ fontFamily: "'Nunito Sans', sans-serif" }}
/> />
</div> </div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6"> <div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<div> <div>
<Label className="text-[#422268] mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <Label className="text-primary mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Directory Address (optional) Directory Address (optional)
</Label> </Label>
<Input <Input
@@ -422,13 +422,13 @@ const MemberProfile = () => {
value={formData.directory_address} value={formData.directory_address}
onChange={handleInputChange} onChange={handleInputChange}
placeholder="123 Main St, City, State" placeholder="123 Main St, City, State"
className="border-[#ddd8eb] rounded-xl focus:border-[#664fa3] focus:ring-[#664fa3]" className="border-chart-6 rounded-xl focus:border-muted-foreground focus:ring-muted-foreground"
style={{ fontFamily: "'Nunito Sans', sans-serif" }} style={{ fontFamily: "'Nunito Sans', sans-serif" }}
/> />
</div> </div>
<div> <div>
<Label className="text-[#422268] mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <Label className="text-primary mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Directory Phone (optional) Directory Phone (optional)
</Label> </Label>
<Input <Input
@@ -437,14 +437,14 @@ const MemberProfile = () => {
value={formData.directory_phone} value={formData.directory_phone}
onChange={handleInputChange} onChange={handleInputChange}
placeholder="(555) 123-4567" placeholder="(555) 123-4567"
className="border-[#ddd8eb] rounded-xl focus:border-[#664fa3] focus:ring-[#664fa3]" className="border-chart-6 rounded-xl focus:border-muted-foreground focus:ring-muted-foreground"
style={{ fontFamily: "'Nunito Sans', sans-serif" }} style={{ fontFamily: "'Nunito Sans', sans-serif" }}
/> />
</div> </div>
</div> </div>
<div> <div>
<Label className="text-[#422268] mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <Label className="text-primary mb-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Partner Name (if applicable) Partner Name (if applicable)
</Label> </Label>
<Input <Input
@@ -453,7 +453,7 @@ const MemberProfile = () => {
value={formData.directory_partner_name} value={formData.directory_partner_name}
onChange={handleInputChange} onChange={handleInputChange}
placeholder="Partner's name" placeholder="Partner's name"
className="border-[#ddd8eb] rounded-xl focus:border-[#664fa3] focus:ring-[#664fa3]" className="border-chart-6 rounded-xl focus:border-muted-foreground focus:ring-muted-foreground"
style={{ fontFamily: "'Nunito Sans', sans-serif" }} style={{ fontFamily: "'Nunito Sans', sans-serif" }}
/> />
</div> </div>
@@ -467,7 +467,7 @@ const MemberProfile = () => {
<Button <Button
type="submit" type="submit"
disabled={saving} disabled={saving}
className="bg-[#ff9e77] hover:bg-[#ff8c5a] text-white rounded-xl px-8 py-3 text-lg" className="bg-accent hover:bg-[#ff8c5a] text-white rounded-xl px-8 py-3 text-lg"
style={{ fontFamily: "'Inter', sans-serif" }} style={{ fontFamily: "'Inter', sans-serif" }}
> >
{saving ? ( {saving ? (

View File

@@ -114,23 +114,23 @@ const MembersDirectory = () => {
const Border = ({ yaxis = false }) => { const Border = ({ yaxis = false }) => {
return ( return (
yaxis ? yaxis ?
<div className=' border-2 w-full border-[#664FA3] my-24' /> <div className=' border-2 w-full border-muted-foreground my-24' />
: <div className=' border-2 w-full border-[#664FA3] mb-24' /> : <div className=' border-2 w-full border-muted-foreground mb-24' />
) )
} }
const MemberCard = ({ member }) => ( const MemberCard = ({ member }) => (
<Card className="p-6 bg-white rounded-3xl border border-[#ddd8eb] hover:shadow-lg transition-all h-full"> <Card className="p-6 bg-background rounded-3xl border border-chart-6 hover:shadow-lg transition-all h-full">
{/* Profile Photo */} {/* Profile Photo */}
<div className="flex justify-center mb-4"> <div className="flex justify-center mb-4">
{member.profile_photo_url ? ( {member.profile_photo_url ? (
<img <img
src={member.profile_photo_url} src={member.profile_photo_url}
alt={`${member.first_name} ${member.last_name}`} alt={`${member.first_name} ${member.last_name}`}
className="w-32 h-32 rounded-full object-cover border-4 border-[#ddd8eb]" className="w-32 h-32 rounded-full object-cover border-4 border-chart-6"
/> />
) : ( ) : (
<div className="w-32 h-32 rounded-full bg-[#DDD8EB] border-4 border-[#ddd8eb] flex items-center justify-center"> <div className="w-32 h-32 rounded-full bg-chart-6 border-4 border-chart-6 flex items-center justify-center">
<span className="text-4xl font-semibold text-[#664fa3]" style={{ fontFamily: "'Inter', sans-serif" }}> <span className="text-4xl font-semibold text-muted-foreground" style={{ fontFamily: "'Inter', sans-serif" }}>
{getInitials(member.first_name, member.last_name)} {getInitials(member.first_name, member.last_name)}
</span> </span>
</div> </div>
@@ -138,15 +138,15 @@ const MembersDirectory = () => {
</div> </div>
{/* Name */} {/* Name */}
<h3 className="text-2xl font-semibold text-[#422268] text-center mb-3" style={{ fontFamily: "'Inter', sans-serif" }}> <h3 className="text-2xl font-semibold text-primary text-center mb-3" style={{ fontFamily: "'Inter', sans-serif" }}>
{member.first_name} {member.last_name} {member.first_name} {member.last_name}
</h3> </h3>
{/* Partner Name */} {/* Partner Name */}
{member.directory_partner_name && ( {member.directory_partner_name && (
<div className="flex items-center justify-center gap-2 mb-4"> <div className="flex items-center justify-center gap-2 mb-4">
<Heart className="h-4 w-4 text-[#ff9e77]" /> <Heart className="h-4 w-4 text-accent" />
<span className="text-sm text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <span className="text-sm text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Partner: {member.directory_partner_name} Partner: {member.directory_partner_name}
</span> </span>
</div> </div>
@@ -154,7 +154,7 @@ const MembersDirectory = () => {
{/* Bio */} {/* Bio */}
{member.directory_bio && ( {member.directory_bio && (
<p className="text-[#664fa3] text-center mb-4 line-clamp-3" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-muted-foreground text-center mb-4 line-clamp-3" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{member.directory_bio} {member.directory_bio}
</p> </p>
)} )}
@@ -162,8 +162,8 @@ const MembersDirectory = () => {
{/* Member Since */} {/* Member Since */}
{member.created_at && ( {member.created_at && (
<div className="flex items-center justify-center gap-2 mb-4"> <div className="flex items-center justify-center gap-2 mb-4">
<Calendar className="h-4 w-4 text-[#664fa3]" /> <Calendar className="h-4 w-4 text-muted-foreground" />
<span className="text-sm text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <span className="text-sm text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Member since {new Date(member.created_at).toLocaleDateString('en-US', { Member since {new Date(member.created_at).toLocaleDateString('en-US', {
month: 'long', month: 'long',
year: 'numeric' year: 'numeric'
@@ -176,10 +176,10 @@ const MembersDirectory = () => {
<div className="space-y-3 mb-4"> <div className="space-y-3 mb-4">
{member.directory_email && ( {member.directory_email && (
<div className="flex items-center gap-2 text-sm"> <div className="flex items-center gap-2 text-sm">
<Mail className="h-4 w-4 text-[#664fa3] flex-shrink-0" /> <Mail className="h-4 w-4 text-muted-foreground flex-shrink-0" />
<a <a
href={`mailto:${member.directory_email}`} href={`mailto:${member.directory_email}`}
className="text-[#664fa3] hover:text-[#422268] truncate" className="text-muted-foreground hover:text-primary truncate"
style={{ fontFamily: "'Nunito Sans', sans-serif" }} style={{ fontFamily: "'Nunito Sans', sans-serif" }}
> >
{member.directory_email} {member.directory_email}
@@ -189,10 +189,10 @@ const MembersDirectory = () => {
{member.directory_phone && ( {member.directory_phone && (
<div className="flex items-center gap-2 text-sm"> <div className="flex items-center gap-2 text-sm">
<Phone className="h-4 w-4 text-[#664fa3] flex-shrink-0" /> <Phone className="h-4 w-4 text-muted-foreground flex-shrink-0" />
<a <a
href={`tel:${member.directory_phone}`} href={`tel:${member.directory_phone}`}
className="text-[#664fa3] hover:text-[#422268]" className="text-muted-foreground hover:text-primary"
style={{ fontFamily: "'Nunito Sans', sans-serif" }} style={{ fontFamily: "'Nunito Sans', sans-serif" }}
> >
{member.directory_phone} {member.directory_phone}
@@ -202,8 +202,8 @@ const MembersDirectory = () => {
{member.directory_address && ( {member.directory_address && (
<div className="flex items-start gap-2 text-sm"> <div className="flex items-start gap-2 text-sm">
<MapPin className="h-4 w-4 text-[#664fa3] flex-shrink-0 mt-0.5" /> <MapPin className="h-4 w-4 text-muted-foreground flex-shrink-0 mt-0.5" />
<span className="text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <span className="text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{member.directory_address} {member.directory_address}
</span> </span>
</div> </div>
@@ -212,14 +212,14 @@ const MembersDirectory = () => {
{/* Social Media Links */} {/* Social Media Links */}
{(member.social_media_facebook || member.social_media_instagram || member.social_media_twitter || member.social_media_linkedin) && ( {(member.social_media_facebook || member.social_media_instagram || member.social_media_twitter || member.social_media_linkedin) && (
<div className="pt-4 border-t border-[#ddd8eb]"> <div className="pt-4 border-t border-chart-6">
<div className="flex justify-center gap-3"> <div className="flex justify-center gap-3">
{member.social_media_facebook && ( {member.social_media_facebook && (
<a <a
href={getSocialMediaLink(member.social_media_facebook)} href={getSocialMediaLink(member.social_media_facebook)}
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
className="p-2 rounded-lg bg-[#F8F7FB] hover:bg-[#DDD8EB] transition-colors" className="p-2 rounded-lg bg-chart-7 hover:bg-chart-6 transition-colors"
title="Facebook" title="Facebook"
> >
<Facebook className="h-5 w-5 text-[#1877F2]" /> <Facebook className="h-5 w-5 text-[#1877F2]" />
@@ -231,7 +231,7 @@ const MembersDirectory = () => {
href={getSocialMediaLink(member.social_media_instagram)} href={getSocialMediaLink(member.social_media_instagram)}
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
className="p-2 rounded-lg bg-[#F8F7FB] hover:bg-[#DDD8EB] transition-colors" className="p-2 rounded-lg bg-chart-7 hover:bg-chart-6 transition-colors"
title="Instagram" title="Instagram"
> >
<Instagram className="h-5 w-5 text-[#E4405F]" /> <Instagram className="h-5 w-5 text-[#E4405F]" />
@@ -243,7 +243,7 @@ const MembersDirectory = () => {
href={getSocialMediaLink(member.social_media_twitter)} href={getSocialMediaLink(member.social_media_twitter)}
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
className="p-2 rounded-lg bg-[#F8F7FB] hover:bg-[#DDD8EB] transition-colors" className="p-2 rounded-lg bg-chart-7 hover:bg-chart-6 transition-colors"
title="Twitter/X" title="Twitter/X"
> >
<Twitter className="h-5 w-5 text-[#1DA1F2]" /> <Twitter className="h-5 w-5 text-[#1DA1F2]" />
@@ -255,7 +255,7 @@ const MembersDirectory = () => {
href={getSocialMediaLink(member.social_media_linkedin)} href={getSocialMediaLink(member.social_media_linkedin)}
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
className="p-2 rounded-lg bg-[#F8F7FB] hover:bg-[#DDD8EB] transition-colors" className="p-2 rounded-lg bg-chart-7 hover:bg-chart-6 transition-colors"
title="LinkedIn" title="LinkedIn"
> >
<Linkedin className="h-5 w-5 text-[#0A66C2]" /> <Linkedin className="h-5 w-5 text-[#0A66C2]" />
@@ -266,10 +266,10 @@ const MembersDirectory = () => {
)} )}
{/* View Profile Button */} {/* View Profile Button */}
<div className="pt-4 mt-4 border-t border-[#ddd8eb]"> <div className="pt-4 mt-4 border-t border-chart-6">
<Button <Button
onClick={() => handleViewProfile(member.id)} onClick={() => handleViewProfile(member.id)}
className="w-full bg-[#DDD8EB] text-[#422268] hover:bg-[#664fa3] hover:text-white rounded-full py-5" className="w-full bg-chart-6 text-primary hover:bg-muted-foreground hover:text-white rounded-full py-5"
> >
<UserCircle className="h-4 w-4 mr-2" /> <UserCircle className="h-4 w-4 mr-2" />
View Full Profile View Full Profile
@@ -279,7 +279,7 @@ const MembersDirectory = () => {
); );
return ( return (
<div className="min-h-screen bg-gradient-to-bl from-[#F9FAFB] to-[#DDD8EB]"> <div className="min-h-screen bg-gradient-to-bl from-[#F9FAFB] to-chart-6">
<Navbar /> <Navbar />
<div className="max-w-7xl mx-auto py-12"> <div className="max-w-7xl mx-auto py-12">
@@ -289,29 +289,29 @@ const MembersDirectory = () => {
{/* Header */} {/* Header */}
<div className="m-8 mt-14 flex flex-col sm:flex-row justify-between items-center "> <div className="m-8 mt-14 flex flex-col sm:flex-row justify-between items-center ">
<h1 className="text-4xl md:text-5xl font-bold text-[#422268] mb-4" style={{ fontFamily: "'Poppins', sans-serif" }}> <h1 className="text-4xl md:text-5xl font-bold text-primary mb-4" style={{ fontFamily: "'Poppins', sans-serif" }}>
LOAF Members LOAF Members
</h1> </h1>
<p className="text-lg " style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-lg " style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<span className='text-foreground'>Number of current memebers in the directory: </span> <span className='text-[#664fa3] font-medium'>{totalMembers}</span> <span className='text-foreground'>Number of current memebers in the directory: </span> <span className='text-muted-foreground font-medium'>{totalMembers}</span>
</p> </p>
</div> </div>
{/* Search Bar */} {/* Search Bar */}
<div className="mb-24 mx-10"> <div className="mb-24 mx-10">
<div className="relative w-full "> <div className="relative w-full ">
<Search className="absolute left-4 top-1/2 transform -translate-y-1/2 h-5 w-5 text-[#664fa3]" /> <Search className="absolute left-4 top-1/2 transform -translate-y-1/2 h-5 w-5 text-muted-foreground" />
<Input <Input
type="text" type="text"
placeholder="Search by name or bio..." placeholder="Search by name or bio..."
value={searchQuery} value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)} onChange={(e) => setSearchQuery(e.target.value)}
className="pl-12 pr-4 py-6 text-3xl font-medium bg-background border-foreground rounded-full focus:border-[#664fa3] focus:ring-[#664fa3]" className="pl-12 pr-4 py-6 text-3xl font-medium bg-background border-foreground rounded-full focus:border-muted-foreground focus:ring-muted-foreground"
style={{ fontFamily: "'Nunito Sans', sans-serif" }} style={{ fontFamily: "'Nunito Sans', sans-serif" }}
/> />
</div> </div>
{searchQuery && ( {searchQuery && (
<p className="mt-3 text-sm text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="mt-3 text-sm text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Found {filteredMembers.length} {filteredMembers.length === 1 ? 'member' : 'members'} Found {filteredMembers.length} {filteredMembers.length === 1 ? 'member' : 'members'}
</p> </p>
)} )}
@@ -325,7 +325,7 @@ const MembersDirectory = () => {
{/* Members Grid */} {/* Members Grid */}
{loading ? ( {loading ? (
<div className="text-center py-20"> <div className="text-center py-20">
<p className="text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Loading members...</p> <p className="text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Loading members...</p>
</div> </div>
) : filteredMembers.length > 0 ? ( ) : filteredMembers.length > 0 ? (
<div className="grid md:grid-cols-2 lg:grid-cols-3 gap-8"> <div className="grid md:grid-cols-2 lg:grid-cols-3 gap-8">
@@ -335,11 +335,11 @@ const MembersDirectory = () => {
</div> </div>
) : ( ) : (
<div className="text-center py-20"> <div className="text-center py-20">
<User className="h-20 w-20 text-[#ddd8eb] mx-auto mb-6" /> <User className="h-20 w-20 text-chart-6 mx-auto mb-6" />
<h3 className="text-2xl font-semibold text-[#422268] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}> <h3 className="text-2xl font-semibold text-primary mb-4" style={{ fontFamily: "'Inter', sans-serif" }}>
{searchQuery ? 'No Members Found' : 'No Members in Directory'} {searchQuery ? 'No Members Found' : 'No Members in Directory'}
</h3> </h3>
<p className="text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{searchQuery {searchQuery
? 'Try adjusting your search query.' ? 'Try adjusting your search query.'
: 'Members who opt in to the directory will appear here.'} : 'Members who opt in to the directory will appear here.'}
@@ -354,18 +354,18 @@ const MembersDirectory = () => {
{/* Info Card */} {/* Info Card */}
{!loading && members.length > 0 && ( {!loading && members.length > 0 && (
<Card className="mt-12 p-6 bg-[#F8F7FB] border-[#ddd8eb]"> <Card className="mt-12 p-6 bg-chart-7 border-chart-6">
<div className="flex items-start gap-4"> <div className="flex items-start gap-4">
<div className="bg-[#DDD8EB]/20 p-3 rounded-lg"> <div className="bg-chart-6/20 p-3 rounded-lg">
<User className="h-6 w-6 text-[#664fa3]" /> <User className="h-6 w-6 text-muted-foreground" />
</div> </div>
<div> <div>
<h3 className="text-lg font-semibold text-[#422268] mb-2" style={{ fontFamily: "'Inter', sans-serif" }}> <h3 className="text-lg font-semibold text-primary mb-2" style={{ fontFamily: "'Inter', sans-serif" }}>
Want to appear in the directory? Want to appear in the directory?
</h3> </h3>
<p className="text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Update your profile settings to show in the directory and add your photo, bio, and contact information.{' '} Update your profile settings to show in the directory and add your photo, bio, and contact information.{' '}
<a href="/members/profile" className="text-[#ff9e77] hover:underline font-medium"> <a href="/members/profile" className="text-accent hover:underline font-medium">
Edit your profile Edit your profile
</a> </a>
</p> </p>
@@ -377,17 +377,17 @@ const MembersDirectory = () => {
{/* Profile Detail Dialog */} {/* Profile Detail Dialog */}
<Dialog open={profileDialogOpen} onOpenChange={setProfileDialogOpen}> <Dialog open={profileDialogOpen} onOpenChange={setProfileDialogOpen}>
<DialogContent className="sm:max-w-[600px] bg-white rounded-2xl max-h-[90vh] overflow-y-auto"> <DialogContent className="sm:max-w-[600px] bg-background rounded-2xl max-h-[90vh] overflow-y-auto">
{selectedMember && ( {selectedMember && (
<> <>
<DialogHeader> <DialogHeader>
<DialogTitle className="text-3xl font-semibold text-[#422268]" style={{ fontFamily: "'Inter', sans-serif" }}> <DialogTitle className="text-3xl font-semibold text-primary" style={{ fontFamily: "'Inter', sans-serif" }}>
{selectedMember.first_name} {selectedMember.last_name} {selectedMember.first_name} {selectedMember.last_name}
</DialogTitle> </DialogTitle>
{selectedMember.directory_partner_name && ( {selectedMember.directory_partner_name && (
<DialogDescription className="flex items-center gap-2 text-lg" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <DialogDescription className="flex items-center gap-2 text-lg" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
<Heart className="h-5 w-5 text-[#ff9e77]" /> <Heart className="h-5 w-5 text-accent" />
<span className="text-[#664fa3]">Partner: {selectedMember.directory_partner_name}</span> <span className="text-muted-foreground">Partner: {selectedMember.directory_partner_name}</span>
</DialogDescription> </DialogDescription>
)} )}
</DialogHeader> </DialogHeader>
@@ -396,10 +396,10 @@ const MembersDirectory = () => {
{/* Bio */} {/* Bio */}
{selectedMember.directory_bio && ( {selectedMember.directory_bio && (
<div> <div>
<h3 className="text-lg font-semibold text-[#422268] mb-2" style={{ fontFamily: "'Inter', sans-serif" }}> <h3 className="text-lg font-semibold text-primary mb-2" style={{ fontFamily: "'Inter', sans-serif" }}>
About About
</h3> </h3>
<p className="text-[#664fa3] leading-relaxed" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-muted-foreground leading-relaxed" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{selectedMember.directory_bio} {selectedMember.directory_bio}
</p> </p>
</div> </div>
@@ -407,20 +407,20 @@ const MembersDirectory = () => {
{/* Contact Information */} {/* Contact Information */}
<div> <div>
<h3 className="text-lg font-semibold text-[#422268] mb-3" style={{ fontFamily: "'Inter', sans-serif" }}> <h3 className="text-lg font-semibold text-primary mb-3" style={{ fontFamily: "'Inter', sans-serif" }}>
Contact Information Contact Information
</h3> </h3>
<div className="space-y-3"> <div className="space-y-3">
{selectedMember.directory_email && ( {selectedMember.directory_email && (
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
<div className="p-2 rounded-lg bg-[#F8F7FB]"> <div className="p-2 rounded-lg bg-chart-7">
<Mail className="h-5 w-5 text-[#664fa3]" /> <Mail className="h-5 w-5 text-muted-foreground" />
</div> </div>
<div> <div>
<p className="text-xs text-[#664fa3] mb-1" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Email</p> <p className="text-xs text-muted-foreground mb-1" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Email</p>
<a <a
href={`mailto:${selectedMember.directory_email}`} href={`mailto:${selectedMember.directory_email}`}
className="text-[#422268] hover:text-[#664fa3] font-medium" className="text-primary hover:text-muted-foreground font-medium"
style={{ fontFamily: "'Nunito Sans', sans-serif" }} style={{ fontFamily: "'Nunito Sans', sans-serif" }}
> >
{selectedMember.directory_email} {selectedMember.directory_email}
@@ -431,14 +431,14 @@ const MembersDirectory = () => {
{selectedMember.directory_phone && ( {selectedMember.directory_phone && (
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
<div className="p-2 rounded-lg bg-[#F8F7FB]"> <div className="p-2 rounded-lg bg-chart-7">
<Phone className="h-5 w-5 text-[#664fa3]" /> <Phone className="h-5 w-5 text-muted-foreground" />
</div> </div>
<div> <div>
<p className="text-xs text-[#664fa3] mb-1" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Phone</p> <p className="text-xs text-muted-foreground mb-1" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Phone</p>
<a <a
href={`tel:${selectedMember.directory_phone}`} href={`tel:${selectedMember.directory_phone}`}
className="text-[#422268] hover:text-[#664fa3] font-medium" className="text-primary hover:text-muted-foreground font-medium"
style={{ fontFamily: "'Nunito Sans', sans-serif" }} style={{ fontFamily: "'Nunito Sans', sans-serif" }}
> >
{selectedMember.directory_phone} {selectedMember.directory_phone}
@@ -449,12 +449,12 @@ const MembersDirectory = () => {
{selectedMember.directory_address && ( {selectedMember.directory_address && (
<div className="flex items-start gap-3"> <div className="flex items-start gap-3">
<div className="p-2 rounded-lg bg-[#F8F7FB]"> <div className="p-2 rounded-lg bg-chart-7">
<MapPin className="h-5 w-5 text-[#664fa3]" /> <MapPin className="h-5 w-5 text-muted-foreground" />
</div> </div>
<div> <div>
<p className="text-xs text-[#664fa3] mb-1" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Address</p> <p className="text-xs text-muted-foreground mb-1" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Address</p>
<p className="text-[#422268] font-medium" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-primary font-medium" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{selectedMember.directory_address} {selectedMember.directory_address}
</p> </p>
</div> </div>
@@ -463,12 +463,12 @@ const MembersDirectory = () => {
{selectedMember.directory_dob && ( {selectedMember.directory_dob && (
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
<div className="p-2 rounded-lg bg-[#F8F7FB]"> <div className="p-2 rounded-lg bg-chart-7">
<Heart className="h-5 w-5 text-[#ff9e77]" /> <Heart className="h-5 w-5 text-accent" />
</div> </div>
<div> <div>
<p className="text-xs text-[#664fa3] mb-1" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Birthday</p> <p className="text-xs text-muted-foreground mb-1" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>Birthday</p>
<p className="text-[#422268] font-medium" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-primary font-medium" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{formatDate(selectedMember.directory_dob)} {formatDate(selectedMember.directory_dob)}
</p> </p>
</div> </div>
@@ -480,14 +480,14 @@ const MembersDirectory = () => {
{/* Volunteer Interests */} {/* Volunteer Interests */}
{selectedMember.volunteer_interests && selectedMember.volunteer_interests.length > 0 && ( {selectedMember.volunteer_interests && selectedMember.volunteer_interests.length > 0 && (
<div> <div>
<h3 className="text-lg font-semibold text-[#422268] mb-3" style={{ fontFamily: "'Inter', sans-serif" }}> <h3 className="text-lg font-semibold text-primary mb-3" style={{ fontFamily: "'Inter', sans-serif" }}>
Volunteer Interests Volunteer Interests
</h3> </h3>
<div className="flex flex-wrap gap-2"> <div className="flex flex-wrap gap-2">
{selectedMember.volunteer_interests.map((interest, index) => ( {selectedMember.volunteer_interests.map((interest, index) => (
<Badge <Badge
key={index} key={index}
className="bg-[#DDD8EB] text-[#422268] hover:bg-[#664fa3] hover:text-white" className="bg-chart-6 text-primary hover:bg-muted-foreground hover:text-white"
> >
{interest} {interest}
</Badge> </Badge>
@@ -500,7 +500,7 @@ const MembersDirectory = () => {
{(selectedMember.social_media_facebook || selectedMember.social_media_instagram || {(selectedMember.social_media_facebook || selectedMember.social_media_instagram ||
selectedMember.social_media_twitter || selectedMember.social_media_linkedin) && ( selectedMember.social_media_twitter || selectedMember.social_media_linkedin) && (
<div> <div>
<h3 className="text-lg font-semibold text-[#422268] mb-3" style={{ fontFamily: "'Inter', sans-serif" }}> <h3 className="text-lg font-semibold text-primary mb-3" style={{ fontFamily: "'Inter', sans-serif" }}>
Connect on Social Media Connect on Social Media
</h3> </h3>
<div className="flex gap-3"> <div className="flex gap-3">
@@ -509,7 +509,7 @@ const MembersDirectory = () => {
href={getSocialMediaLink(selectedMember.social_media_facebook)} href={getSocialMediaLink(selectedMember.social_media_facebook)}
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
className="p-3 rounded-lg bg-[#F8F7FB] hover:bg-[#DDD8EB] transition-colors" className="p-3 rounded-lg bg-chart-7 hover:bg-chart-6 transition-colors"
title="Facebook" title="Facebook"
> >
<Facebook className="h-6 w-6 text-[#1877F2]" /> <Facebook className="h-6 w-6 text-[#1877F2]" />
@@ -521,7 +521,7 @@ const MembersDirectory = () => {
href={getSocialMediaLink(selectedMember.social_media_instagram)} href={getSocialMediaLink(selectedMember.social_media_instagram)}
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
className="p-3 rounded-lg bg-[#F8F7FB] hover:bg-[#DDD8EB] transition-colors" className="p-3 rounded-lg bg-chart-7 hover:bg-chart-6 transition-colors"
title="Instagram" title="Instagram"
> >
<Instagram className="h-6 w-6 text-[#E4405F]" /> <Instagram className="h-6 w-6 text-[#E4405F]" />
@@ -533,7 +533,7 @@ const MembersDirectory = () => {
href={getSocialMediaLink(selectedMember.social_media_twitter)} href={getSocialMediaLink(selectedMember.social_media_twitter)}
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
className="p-3 rounded-lg bg-[#F8F7FB] hover:bg-[#DDD8EB] transition-colors" className="p-3 rounded-lg bg-chart-7 hover:bg-chart-6 transition-colors"
title="Twitter/X" title="Twitter/X"
> >
<Twitter className="h-6 w-6 text-[#1DA1F2]" /> <Twitter className="h-6 w-6 text-[#1DA1F2]" />
@@ -545,7 +545,7 @@ const MembersDirectory = () => {
href={getSocialMediaLink(selectedMember.social_media_linkedin)} href={getSocialMediaLink(selectedMember.social_media_linkedin)}
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
className="p-3 rounded-lg bg-[#F8F7FB] hover:bg-[#DDD8EB] transition-colors" className="p-3 rounded-lg bg-chart-7 hover:bg-chart-6 transition-colors"
title="LinkedIn" title="LinkedIn"
> >
<Linkedin className="h-6 w-6 text-[#0A66C2]" /> <Linkedin className="h-6 w-6 text-[#0A66C2]" />
@@ -565,21 +565,21 @@ const MembersDirectory = () => {
{/* Pagination */} {/* Pagination */}
{!loading && filteredMembers.length > 0 && ( {!loading && filteredMembers.length > 0 && (
<div className="mt-10 flex flex-col items-center gap-4 pb-12"> <div className="mt-10 flex flex-col items-center gap-4 pb-12">
<p className="text-sm text-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-sm text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Showing {pageStart + 1}{Math.min(pageStart + pageSize, filteredMembers.length)} of {filteredMembers.length} Showing {pageStart + 1}{Math.min(pageStart + pageSize, filteredMembers.length)} of {filteredMembers.length}
</p> </p>
<div className="flex flex-wrap items-center justify-center gap-2"> <div className="flex flex-wrap items-center justify-center gap-2">
<Button <Button
onClick={() => setCurrentPage(1)} onClick={() => setCurrentPage(1)}
disabled={currentPage === 1} disabled={currentPage === 1}
className="bg-[#DDD8EB] rounded-full text-[#422268] hover:bg-[#664fa3] hover:text-white" className="bg-chart-6 rounded-full text-primary hover:bg-muted-foreground hover:text-white"
> >
First Page First Page
</Button> </Button>
<Button <Button
onClick={() => setCurrentPage((page) => Math.max(1, page - 1))} onClick={() => setCurrentPage((page) => Math.max(1, page - 1))}
disabled={currentPage === 1} disabled={currentPage === 1}
className="bg-[#DDD8EB] rounded-full text-[#422268] hover:bg-[#664fa3] hover:text-white" className="bg-chart-6 rounded-full text-primary hover:bg-muted-foreground hover:text-white"
> >
Previous Previous
</Button> </Button>
@@ -593,8 +593,8 @@ const MembersDirectory = () => {
onClick={() => setCurrentPage(pageNumber)} onClick={() => setCurrentPage(pageNumber)}
className={ className={
isActive isActive
? "bg-[#664fa3] text-white hover:bg-[#422268] rounded-full" ? "bg-muted-foreground text-white hover:bg-primary rounded-full"
: "bg-[#DDD8EB] text-[#422268] hover:bg-[#664fa3] hover:text-white rounded-full" : "bg-chart-6 text-primary hover:bg-muted-foreground hover:text-white rounded-full"
} }
> >
{pageNumber} {pageNumber}
@@ -605,14 +605,14 @@ const MembersDirectory = () => {
<Button <Button
onClick={() => setCurrentPage((page) => Math.min(totalPages, page + 1))} onClick={() => setCurrentPage((page) => Math.min(totalPages, page + 1))}
disabled={currentPage === totalPages} disabled={currentPage === totalPages}
className="bg-[#DDD8EB] text-[#422268] hover:bg-[#664fa3] rounded-full hover:text-white" className="bg-chart-6 text-primary hover:bg-muted-foreground rounded-full hover:text-white"
> >
Next Next
</Button> </Button>
<Button <Button
onClick={() => setCurrentPage(totalPages)} onClick={() => setCurrentPage(totalPages)}
disabled={currentPage === totalPages} disabled={currentPage === totalPages}
className="bg-[#DDD8EB] text-[#422268] hover:bg-[#664fa3] rounded-full hover:text-white" className="bg-chart-6 text-primary hover:bg-muted-foreground rounded-full hover:text-white"
> >
Last Page Last Page
</Button> </Button>

View File

@@ -83,10 +83,10 @@ export default function NewsletterArchive() {
if (loading) { if (loading) {
return ( return (
<div className="min-h-screen bg-white"> <div className="min-h-screen bg-background">
<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-[#664fa3]" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-muted-foreground" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Loading newsletters... Loading newsletters...
</p> </p>
</div> </div>
@@ -95,16 +95,16 @@ export default function NewsletterArchive() {
} }
return ( return (
<div className="min-h-screen bg-white"> <div className="min-h-screen bg-background">
<Navbar /> <Navbar />
<div className="max-w-7xl mx-auto px-6 py-12"> <div className="max-w-7xl mx-auto px-6 py-12">
{/* Header */} {/* Header */}
<div className="mb-8"> <div className="mb-8">
<h1 className="text-4xl font-semibold text-[#422268] mb-4" style={{ fontFamily: "'Inter', sans-serif" }}> <h1 className="text-4xl font-semibold text-primary mb-4" style={{ fontFamily: "'Inter', sans-serif" }}>
Newsletter Archive Newsletter Archive
</h1> </h1>
<p className="text-[#664fa3] mb-6" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-muted-foreground mb-6" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
Browse past monthly newsletters and stay informed about LOAF community updates. Browse past monthly newsletters and stay informed about LOAF community updates.
</p> </p>
@@ -112,13 +112,13 @@ export default function NewsletterArchive() {
<div className="flex gap-4 flex-wrap items-center"> <div className="flex gap-4 flex-wrap items-center">
{/* Search */} {/* Search */}
<div className="relative flex-1 min-w-[300px]"> <div className="relative flex-1 min-w-[300px]">
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 h-4 w-4 text-[#664fa3]" /> <Search className="absolute left-3 top-1/2 transform -translate-y-1/2 h-4 w-4 text-muted-foreground" />
<Input <Input
type="text" type="text"
placeholder="Search newsletters..." placeholder="Search newsletters..."
value={searchTerm} value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)} onChange={(e) => setSearchTerm(e.target.value)}
className="pl-10 border-[#ddd8eb] focus:border-[#664fa3]" className="pl-10 border-chart-6 focus:border-muted-foreground"
/> />
</div> </div>
@@ -128,7 +128,7 @@ export default function NewsletterArchive() {
onClick={clearFilter} onClick={clearFilter}
variant={selectedYear === null ? "default" : "outline"} variant={selectedYear === null ? "default" : "outline"}
size="sm" size="sm"
className={selectedYear === null ? "bg-[#664fa3] text-white" : "border-[#664fa3] text-[#664fa3]"} className={selectedYear === null ? "bg-muted-foreground text-white" : "border-muted-foreground text-muted-foreground"}
> >
All Years All Years
</Button> </Button>
@@ -138,7 +138,7 @@ export default function NewsletterArchive() {
onClick={() => handleYearFilter(year)} onClick={() => handleYearFilter(year)}
variant={selectedYear === year ? "default" : "outline"} variant={selectedYear === year ? "default" : "outline"}
size="sm" size="sm"
className={selectedYear === year ? "bg-[#664fa3] text-white" : "border-[#664fa3] text-[#664fa3]"} className={selectedYear === year ? "bg-muted-foreground text-white" : "border-muted-foreground text-muted-foreground"}
> >
{year} {year}
</Button> </Button>
@@ -149,9 +149,9 @@ export default function NewsletterArchive() {
{/* Newsletter List */} {/* Newsletter List */}
{filteredNewsletters.length === 0 ? ( {filteredNewsletters.length === 0 ? (
<Card className="p-12 text-center bg-white rounded-2xl border border-[#ddd8eb]"> <Card className="p-12 text-center bg-background rounded-2xl border border-chart-6">
<FileText className="h-16 w-16 text-[#ddd8eb] mx-auto mb-4" /> <FileText className="h-16 w-16 text-chart-6 mx-auto mb-4" />
<p className="text-[#664fa3] text-lg" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-muted-foreground text-lg" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
No newsletters found No newsletters found
</p> </p>
</Card> </Card>
@@ -159,37 +159,37 @@ export default function NewsletterArchive() {
<div className="space-y-8"> <div className="space-y-8">
{sortedYears.map(year => ( {sortedYears.map(year => (
<div key={year}> <div key={year}>
<h2 className="text-2xl font-semibold text-[#422268] mb-4 flex items-center gap-2" style={{ fontFamily: "'Inter', sans-serif" }}> <h2 className="text-2xl font-semibold text-primary mb-4 flex items-center gap-2" style={{ fontFamily: "'Inter', sans-serif" }}>
<Calendar className="h-6 w-6" /> <Calendar className="h-6 w-6" />
{year} {year}
</h2> </h2>
<div className="grid md:grid-cols-2 gap-6"> <div className="grid md:grid-cols-2 gap-6">
{groupedNewsletters[year].map(newsletter => ( {groupedNewsletters[year].map(newsletter => (
<Card key={newsletter.id} className="p-6 bg-white rounded-2xl border border-[#ddd8eb] hover:shadow-lg transition-shadow"> <Card key={newsletter.id} className="p-6 bg-background rounded-2xl border border-chart-6 hover:shadow-lg transition-shadow">
<div className="flex items-start gap-4"> <div className="flex items-start gap-4">
<div className="bg-[#DDD8EB]/20 p-3 rounded-lg flex-shrink-0"> <div className="bg-chart-6/20 p-3 rounded-lg flex-shrink-0">
<FileText className="h-6 w-6 text-[#664fa3]" /> <FileText className="h-6 w-6 text-muted-foreground" />
</div> </div>
<div className="flex-1 min-w-0"> <div className="flex-1 min-w-0">
<h3 className="text-xl font-semibold text-[#422268] mb-2" style={{ fontFamily: "'Inter', sans-serif" }}> <h3 className="text-xl font-semibold text-primary mb-2" style={{ fontFamily: "'Inter', sans-serif" }}>
{newsletter.title} {newsletter.title}
</h3> </h3>
{newsletter.description && ( {newsletter.description && (
<p className="text-[#664fa3] mb-3 line-clamp-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}> <p className="text-muted-foreground mb-3 line-clamp-2" style={{ fontFamily: "'Nunito Sans', sans-serif" }}>
{newsletter.description} {newsletter.description}
</p> </p>
)} )}
<div className="flex items-center gap-3 mb-4"> <div className="flex items-center gap-3 mb-4">
<Badge className="bg-[#DDD8EB] text-[#422268] hover:bg-[#DDD8EB]"> <Badge className="bg-chart-6 text-primary hover:bg-chart-6">
{formatDate(newsletter.published_date)} {formatDate(newsletter.published_date)}
</Badge> </Badge>
<Badge variant="outline" className="border-[#664fa3] text-[#664fa3]"> <Badge variant="outline" className="border-muted-foreground text-muted-foreground">
{newsletter.document_type === 'google_docs' ? 'Google Docs' : newsletter.document_type.toUpperCase()} {newsletter.document_type === 'google_docs' ? 'Google Docs' : newsletter.document_type.toUpperCase()}
</Badge> </Badge>
</div> </div>
<Button <Button
onClick={() => window.open(newsletter.document_url, '_blank')} onClick={() => window.open(newsletter.document_url, '_blank')}
className="w-full bg-[#664fa3] text-white hover:bg-[#533a82] rounded-full flex items-center justify-center gap-2" className="w-full bg-muted-foreground text-white hover:bg-[#533a82] rounded-full flex items-center justify-center gap-2"
> >
<ExternalLink className="h-4 w-4" /> <ExternalLink className="h-4 w-4" />
View Newsletter View Newsletter

View File

@@ -51,7 +51,10 @@ module.exports = {
'2': 'hsl(var(--chart-2))', '2': 'hsl(var(--chart-2))',
'3': 'hsl(var(--chart-3))', '3': 'hsl(var(--chart-3))',
'4': 'hsl(var(--chart-4))', '4': 'hsl(var(--chart-4))',
'5': 'hsl(var(--chart-5))' '5': 'hsl(var(--chart-5))',
'6': 'hsl(var(--chart-6))',
'7': 'hsl(var(--chart-7))',
} }
}, },
keyframes: { keyframes: {