diff --git a/src/App.js b/src/App.js index 7023eb9..82e87c1 100644 --- a/src/App.js +++ b/src/App.js @@ -23,6 +23,7 @@ import AdminStaff from './pages/admin/AdminStaff'; import AdminMembers from './pages/admin/AdminMembers'; import AdminPermissions from './pages/admin/AdminPermissions'; import AdminSettings from './pages/admin/AdminSettings'; +import AdminMemberTiers from './pages/admin/AdminMemberTiers'; import AdminRoles from './pages/admin/AdminRoles'; import AdminEvents from './pages/admin/AdminEvents'; import AdminEventAttendance from './pages/admin/AdminEventAttendance'; @@ -31,6 +32,7 @@ import AdminPlans from './pages/admin/AdminPlans'; import AdminSubscriptions from './pages/admin/AdminSubscriptions'; import AdminDonations from './pages/admin/AdminDonations'; import AdminLayout from './layouts/AdminLayout'; +import SettingsLayout from './layouts/SettingsLayout'; import { AuthProvider, useAuth } from './context/AuthContext'; import MemberRoute from './components/MemberRoute'; import MemberCalendar from './pages/members/MemberCalendar'; @@ -286,18 +288,21 @@ function App() { } /> - - - + } /> - + - } /> + }> + } /> + } /> + } /> + } /> + {/* 404 - Catch all undefined routes */} } /> diff --git a/src/components/AdminSidebar.js b/src/components/AdminSidebar.js index 12401d5..415f015 100644 --- a/src/components/AdminSidebar.js +++ b/src/components/AdminSidebar.js @@ -169,13 +169,7 @@ const AdminSidebar = ({ isOpen, onToggle, isMobile }) => { path: '/admin/bylaws', disabled: false }, - { - name: 'Permissions', - icon: Shield, - path: '/admin/permissions', - disabled: false, - superadminOnly: true - }, + { name: 'Settings', icon: Settings, diff --git a/src/components/MemberBadge.js b/src/components/MemberBadge.js new file mode 100644 index 0000000..a7e1387 --- /dev/null +++ b/src/components/MemberBadge.js @@ -0,0 +1,19 @@ +// src/components/MemberBadge.js +import React from 'react'; +import { Badge } from './ui/badge'; +import { getTierForMember } from '../utils/member-tiers'; +import { MEMBER_TIER_ICONS } from '../config/memberTierIcons'; + +const MemberBadge = ({ memberSince, tiers }) => { + const tier = getTierForMember(memberSince, tiers); + const Icon = MEMBER_TIER_ICONS[tier.icon] || MEMBER_TIER_ICONS.FaUser; + + return ( + + + {tier.label} + + ); +}; + +export default MemberBadge; \ No newline at end of file diff --git a/src/components/MemberCard.js b/src/components/MemberCard.js index 8c82666..40bb13f 100644 --- a/src/components/MemberCard.js +++ b/src/components/MemberCard.js @@ -1,5 +1,4 @@ import React from 'react' -import StatusBadge from './StatusBadge'; import { Card } from './ui/card'; import { Button } from './ui/button'; import { Heart, Calendar, Mail, Phone, MapPin, Facebook, Instagram, Twitter, Linkedin, UserCircle } from 'lucide-react'; @@ -24,7 +23,7 @@ const MemberCard = ({ member, onViewProfile }) => { {/* Profile Photo */}
- + member since badge
{member.profile_photo_url ? ( diff --git a/src/components/SettingsSidebar.js b/src/components/SettingsSidebar.js new file mode 100644 index 0000000..0cb1ee1 --- /dev/null +++ b/src/components/SettingsSidebar.js @@ -0,0 +1,46 @@ +import React from 'react'; +import { NavLink } from 'react-router-dom'; +import { CreditCard, Shield, Settings, Star } from 'lucide-react'; + +const settingsItems = [ + { label: 'Stripe Integration', path: '/admin/settings/stripe', icon: CreditCard }, + { label: 'Permissions', path: '/admin/settings/permissions', icon: Shield }, + { label: 'Member Tiers', path: '/admin/settings/member-tiers', icon: Star }, +]; + +const SettingsSidebar = () => { + return ( + + ); +}; + +export default SettingsSidebar; diff --git a/src/config/MemberTiers.js b/src/config/MemberTiers.js new file mode 100644 index 0000000..a2fd9bf --- /dev/null +++ b/src/config/MemberTiers.js @@ -0,0 +1,27 @@ +// src/config/memberTiers.js +export const DEFAULT_MEMBER_TIERS = [ + { + id: 'new', + label: 'New Member', + minDays: 0, + maxDays: 364, // < 1 year + icon: 'FaSeedling', + badgeClass: 'bg-[var(--lavender-300)] text-[var(--purple-ink)]', + }, + { + id: 'silver', + label: 'Silver Member', + minDays: 365, + maxDays: 729, + icon: 'FaMedal', + badgeClass: 'bg-slate-200 text-slate-900', + }, + { + id: 'gold', + label: 'Gold Member', + minDays: 730, + maxDays: null, // open-ended + icon: 'FaCrown', + badgeClass: 'bg-amber-200 text-amber-900', + }, +]; \ No newline at end of file diff --git a/src/config/memberTierIcons.js b/src/config/memberTierIcons.js new file mode 100644 index 0000000..40f70d8 --- /dev/null +++ b/src/config/memberTierIcons.js @@ -0,0 +1,9 @@ +// src/config/memberTierIcons.js +import { FaSeedling, FaMedal, FaCrown, FaUser } from 'react-icons/fa'; + +export const MEMBER_TIER_ICONS = { + FaSeedling, + FaMedal, + FaCrown, + FaUser, +}; \ No newline at end of file diff --git a/src/layouts/SettingsLayout.js b/src/layouts/SettingsLayout.js new file mode 100644 index 0000000..0f800fb --- /dev/null +++ b/src/layouts/SettingsLayout.js @@ -0,0 +1,16 @@ +import React from 'react'; +import { Outlet } from 'react-router-dom'; +import SettingsSidebar from '../components/SettingsSidebar'; + +const SettingsLayout = () => { + return ( +
+ +
+ +
+
+ ); +}; + +export default SettingsLayout; diff --git a/src/pages/admin/AdminMemberTiers.js b/src/pages/admin/AdminMemberTiers.js new file mode 100644 index 0000000..92a09a1 --- /dev/null +++ b/src/pages/admin/AdminMemberTiers.js @@ -0,0 +1,50 @@ +import React from 'react'; +import { Card } from '../../components/ui/card'; +import { Badge } from '../../components/ui/badge'; +import { DEFAULT_MEMBER_TIERS } from '../../config/MemberTiers'; + +const AdminMemberTiers = () => { + return ( +
+
+

+ Member Tiers +

+

+ Configure tier names, time ranges, and badges used in the members directory. +

+
+ + +
+ {DEFAULT_MEMBER_TIERS.map((tier) => { + const rangeLabel = tier.maxDays == null + ? `${tier.minDays}+ days` + : `${tier.minDays}–${tier.maxDays} days`; + + return ( +
+
+
+ {tier.label} +
+
+ {rangeLabel} +
+
+ + {tier.icon} + +
+ ); + })} +
+
+
+ ); +}; + +export default AdminMemberTiers; diff --git a/src/utils/member-tiers.js b/src/utils/member-tiers.js new file mode 100644 index 0000000..a4c86ac --- /dev/null +++ b/src/utils/member-tiers.js @@ -0,0 +1,23 @@ +// src/utils/member-tiers.js +import { differenceInDays } from 'date-fns'; +import { DEFAULT_MEMBER_TIERS } from '../config/memberTiers'; + +export const getTenureDays = (memberSince) => { + if (!memberSince) return null; + const since = new Date(memberSince); + if (Number.isNaN(since.getTime())) return null; + return Math.max(0, differenceInDays(new Date(), since)); +}; + +export const getTierForMember = (memberSince, tiers = DEFAULT_MEMBER_TIERS) => { + const days = getTenureDays(memberSince); + if (days == null) return tiers[0]; + + const match = tiers.find( + (tier) => + days >= tier.minDays && + (tier.maxDays == null || days <= tier.maxDays) + ); + + return match || tiers[0]; +}; \ No newline at end of file