diff --git a/.env.development b/.env.development new file mode 100644 index 0000000..6ec835a --- /dev/null +++ b/.env.development @@ -0,0 +1,3 @@ +REACT_APP_BACKEND_URL=http://localhost:8000 +REACT_APP_BASENAME=/membership +PUBLIC_URL=/membership diff --git a/src/components/InviteStaffDialog.js b/src/components/InviteStaffDialog.js index b15d4ae..97e9252 100644 --- a/src/components/InviteStaffDialog.js +++ b/src/components/InviteStaffDialog.js @@ -40,15 +40,14 @@ const InviteStaffDialog = ({ open, onOpenChange, onSuccess }) => { const fetchRoles = async () => { setLoadingRoles(true); try { - const response = await api.get('/admin/roles'); - // Filter to show only admin-type roles (not guest or member) - const staffRoles = response.data.filter(role => - ['admin', 'superadmin', 'finance'].includes(role.code) || !role.is_system_role - ); - setRoles(staffRoles); + // New endpoint returns roles based on user's permission level + // Superadmin: all roles + // Admin: admin, finance, and non-elevated custom roles + const response = await api.get('/admin/roles/assignable'); + setRoles(response.data); } catch (error) { - console.error('Failed to fetch roles:', error); - toast.error('Failed to load roles'); + console.error('Failed to fetch assignable roles:', error); + toast.error('Failed to load roles. Please try again.'); } finally { setLoadingRoles(false); } diff --git a/src/pages/admin/AdminBylaws.js b/src/pages/admin/AdminBylaws.js index e66ece4..54f07df 100644 --- a/src/pages/admin/AdminBylaws.js +++ b/src/pages/admin/AdminBylaws.js @@ -1,4 +1,5 @@ import React, { useEffect, useState } from 'react'; +import { useAuth } from '../../context/AuthContext'; import api from '../../utils/api'; import { Card } from '../../components/ui/card'; import { Button } from '../../components/ui/button'; @@ -32,6 +33,7 @@ import { } from 'lucide-react'; const AdminBylaws = () => { + const { hasPermission } = useAuth(); const [bylaws, setBylaws] = useState([]); const [loading, setLoading] = useState(true); const [dialogOpen, setDialogOpen] = useState(false); @@ -184,13 +186,15 @@ const AdminBylaws = () => { Manage LOAF governing bylaws and version history

- + {hasPermission('bylaws.create') && ( + + )} {/* Current Bylaws */} @@ -226,22 +230,26 @@ const AdminBylaws = () => { View - - + {hasPermission('bylaws.edit') && ( + + )} + {hasPermission('bylaws.delete') && ( + + )}
@@ -254,10 +262,12 @@ const AdminBylaws = () => {

No current bylaws set

- + {hasPermission('bylaws.create') && ( + + )}
)} @@ -290,22 +300,26 @@ const AdminBylaws = () => { > - - + {hasPermission('bylaws.edit') && ( + + )} + {hasPermission('bylaws.delete') && ( + + )}
diff --git a/src/pages/admin/AdminDonations.js b/src/pages/admin/AdminDonations.js index 6ac6bc6..4bf773f 100644 --- a/src/pages/admin/AdminDonations.js +++ b/src/pages/admin/AdminDonations.js @@ -1,4 +1,5 @@ import React, { useState, useEffect } from 'react'; +import { useAuth } from '../../context/AuthContext'; import { Card } from '../../components/ui/card'; import { Button } from '../../components/ui/button'; import { Input } from '../../components/ui/input'; @@ -31,6 +32,7 @@ import { } from 'lucide-react'; const AdminDonations = () => { + const { hasPermission } = useAuth(); const [donations, setDonations] = useState([]); const [filteredDonations, setFilteredDonations] = useState([]); const [stats, setStats] = useState({}); @@ -269,33 +271,35 @@ const AdminDonations = () => { className="pl-10 rounded-full border-2 border-[#ddd8eb] focus:border-[#664fa3]" /> - - - - - - handleExport('all')} - className="cursor-pointer hover:bg-[#f1eef9] rounded-lg p-3" - > - - Export All Donations - - handleExport('current')} - className="cursor-pointer hover:bg-[#f1eef9] rounded-lg p-3" - > - - Export Current View - - - + {hasPermission('donations.export') && ( + + + + + + handleExport('all')} + className="cursor-pointer hover:bg-[#f1eef9] rounded-lg p-3" + > + + Export All Donations + + handleExport('current')} + className="cursor-pointer hover:bg-[#f1eef9] rounded-lg p-3" + > + + Export Current View + + + + )} {/* Filters Row */} diff --git a/src/pages/admin/AdminFinancials.js b/src/pages/admin/AdminFinancials.js index e79a2b7..6669e59 100644 --- a/src/pages/admin/AdminFinancials.js +++ b/src/pages/admin/AdminFinancials.js @@ -1,4 +1,5 @@ import React, { useEffect, useState } from 'react'; +import { useAuth } from '../../context/AuthContext'; import api from '../../utils/api'; import { Card } from '../../components/ui/card'; import { Button } from '../../components/ui/button'; @@ -31,6 +32,7 @@ import { } from 'lucide-react'; const AdminFinancials = () => { + const { hasPermission } = useAuth(); const [reports, setReports] = useState([]); const [loading, setLoading] = useState(true); const [dialogOpen, setDialogOpen] = useState(false); @@ -162,13 +164,15 @@ const AdminFinancials = () => { Manage annual financial reports

- + {hasPermission('financials.create') && ( + + )} {/* Reports List */} @@ -176,10 +180,12 @@ const AdminFinancials = () => {

No financial reports yet

- + {hasPermission('financials.create') && ( + + )}
) : (
@@ -209,24 +215,30 @@ const AdminFinancials = () => {
-
- - -
+ {(hasPermission('financials.edit') || hasPermission('financials.delete')) && ( +
+ {hasPermission('financials.edit') && ( + + )} + {hasPermission('financials.delete') && ( + + )} +
+ )} ))} diff --git a/src/pages/admin/AdminGallery.js b/src/pages/admin/AdminGallery.js index e7aac7d..173570e 100644 --- a/src/pages/admin/AdminGallery.js +++ b/src/pages/admin/AdminGallery.js @@ -1,5 +1,6 @@ import React, { useState, useEffect, useRef } from 'react'; import { Link } from 'react-router-dom'; +import { useAuth } from '../../context/AuthContext'; import api from '../../utils/api'; import { Card } from '../../components/ui/card'; import { Button } from '../../components/ui/button'; @@ -20,6 +21,7 @@ import { toast } from 'sonner'; import moment from 'moment'; const AdminGallery = () => { + const { hasPermission } = useAuth(); const [events, setEvents] = useState([]); const [selectedEvent, setSelectedEvent] = useState(null); const [galleryImages, setGalleryImages] = useState([]); @@ -206,7 +208,7 @@ const AdminGallery = () => { )} - {selectedEvent && ( + {selectedEvent && hasPermission('gallery.upload') && (
{
{/* Overlay with Actions */} -
- - -
+ {(hasPermission('gallery.edit') || hasPermission('gallery.delete')) && ( +
+ {hasPermission('gallery.edit') && ( + + )} + {hasPermission('gallery.delete') && ( + + )} +
+ )} {/* Caption Preview */} {image.caption && ( diff --git a/src/pages/admin/AdminNewsletters.js b/src/pages/admin/AdminNewsletters.js index e6d807b..64ac9f5 100644 --- a/src/pages/admin/AdminNewsletters.js +++ b/src/pages/admin/AdminNewsletters.js @@ -1,4 +1,5 @@ import React, { useEffect, useState } from 'react'; +import { useAuth } from '../../context/AuthContext'; import api from '../../utils/api'; import { Card } from '../../components/ui/card'; import { Button } from '../../components/ui/button'; @@ -32,6 +33,7 @@ import { } from 'lucide-react'; const AdminNewsletters = () => { + const { hasPermission } = useAuth(); const [newsletters, setNewsletters] = useState([]); const [loading, setLoading] = useState(true); const [dialogOpen, setDialogOpen] = useState(false); @@ -190,13 +192,15 @@ const AdminNewsletters = () => { Create and manage newsletter archive

- + {hasPermission('newsletters.create') && ( + + )} {/* Newsletters List */} @@ -204,10 +208,12 @@ const AdminNewsletters = () => {

No newsletters yet

- + {hasPermission('newsletters.create') && ( + + )}
) : (
@@ -246,24 +252,30 @@ const AdminNewsletters = () => {
-
- - -
+ {(hasPermission('newsletters.edit') || hasPermission('newsletters.delete')) && ( +
+ {hasPermission('newsletters.edit') && ( + + )} + {hasPermission('newsletters.delete') && ( + + )} +
+ )} ))} diff --git a/src/pages/admin/AdminPlans.js b/src/pages/admin/AdminPlans.js index 1a6e5c0..439f75a 100644 --- a/src/pages/admin/AdminPlans.js +++ b/src/pages/admin/AdminPlans.js @@ -1,4 +1,5 @@ import React, { useEffect, useState } from 'react'; +import { useAuth } from '../../context/AuthContext'; import api from '../../utils/api'; import { Card } from '../../components/ui/card'; import { Button } from '../../components/ui/button'; @@ -24,6 +25,7 @@ import { } from 'lucide-react'; const AdminPlans = () => { + const { hasPermission } = useAuth(); const [plans, setPlans] = useState([]); const [filteredPlans, setFilteredPlans] = useState([]); const [loading, setLoading] = useState(true); @@ -136,13 +138,15 @@ const AdminPlans = () => { Manage membership plans and pricing.

- + {hasPermission('subscriptions.plans') && ( + + )} @@ -286,27 +290,29 @@ const AdminPlans = () => { {/* Actions */} -
- - -
+ {hasPermission('subscriptions.plans') && ( +
+ + +
+ )} {/* Warning for plans with subscribers */} {plan.subscriber_count > 0 && ( @@ -328,7 +334,7 @@ const AdminPlans = () => { ? 'Try adjusting your filters' : 'Create your first subscription plan to get started'}

- {!searchQuery && activeFilter === 'all' && ( + {!searchQuery && activeFilter === 'all' && hasPermission('subscriptions.plans') && ( - - - handleExport('all')} - className="cursor-pointer hover:bg-[#f1eef9] rounded-lg p-3" - > - - Export All Subscriptions - - handleExport('current')} - className="cursor-pointer hover:bg-[#f1eef9] rounded-lg p-3" - > - - Export Current View - - - + {hasPermission('subscriptions.export') && ( + + + + + + handleExport('all')} + className="cursor-pointer hover:bg-[#f1eef9] rounded-lg p-3" + > + + Export All Subscriptions + + handleExport('current')} + className="cursor-pointer hover:bg-[#f1eef9] rounded-lg p-3" + > + + Export Current View + + + + )} @@ -503,16 +507,18 @@ Proceed with activation?`; {/* Actions */}
- - {sub.status === 'active' && ( + {hasPermission('subscriptions.edit') && ( + + )} + {sub.status === 'active' && hasPermission('subscriptions.cancel') && ( - {sub.status === 'active' && ( + {hasPermission('subscriptions.edit') && ( + + )} + {sub.status === 'active' && hasPermission('subscriptions.cancel') && ( ) : user.status === 'pending_email' ? ( <> - - + {hasPermission('users.approve') && ( + + )} + {hasPermission('users.approve') && ( + + )} ) : user.status === 'payment_pending' ? ( <> - - + {hasPermission('subscriptions.activate') && ( + + )} + {hasPermission('users.approve') && ( + + )} ) : ( <> - - + {hasPermission('users.approve') && ( + + )} + {hasPermission('users.approve') && ( + + )} )}