feat(frontend): Comprehensive RBAC implementation across admin pages

**Option 3 Implementation (Latest):**
- InviteStaffDialog: Use /admin/roles/assignable endpoint
- AdminStaff: Enable admin users to see 'Invite Staff' button

**Permission Checks Added (8 admin pages):**
- AdminNewsletters: newsletters.create/edit/delete
- AdminFinancials: financials.create/edit/delete
- AdminBylaws: bylaws.create/edit/delete
- AdminValidations: users.approve, subscriptions.activate
- AdminSubscriptions: subscriptions.export/edit/cancel
- AdminDonations: donations.export
- AdminGallery: gallery.upload/edit/delete
- AdminPlans: subscriptions.plans

**Pattern Established:**
All admin action buttons now wrapped with hasPermission() checks.
UI hides what users can't access, backend enforces rules.

**Files Modified:** 10 files, 100+ permission checks added
This commit is contained in:
Koncept Kit
2026-01-06 14:45:15 +07:00
parent 0249cad261
commit 40a0e3f342
11 changed files with 368 additions and 288 deletions

View File

@@ -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.
</p>
</div>
<Button
onClick={handleCreatePlan}
className="bg-[#DDD8EB] text-[#422268] hover:bg-white rounded-full px-6"
>
<Plus className="h-4 w-4 mr-2" />
Create Plan
</Button>
{hasPermission('subscriptions.plans') && (
<Button
onClick={handleCreatePlan}
className="bg-[#DDD8EB] text-[#422268] hover:bg-white rounded-full px-6"
>
<Plus className="h-4 w-4 mr-2" />
Create Plan
</Button>
)}
</div>
</div>
@@ -286,27 +290,29 @@ const AdminPlans = () => {
</div>
{/* Actions */}
<div className="flex flex-col sm:flex-row gap-2 pt-4 border-t border-[#ddd8eb]">
<Button
onClick={() => handleEditPlan(plan)}
variant="outline"
size="sm"
className="flex-1 border-[#664fa3] text-[#664fa3] hover:bg-[#664fa3] hover:text-white rounded-full"
>
<Edit className="h-4 w-4 mr-1" />
Edit
</Button>
<Button
onClick={() => handleDeleteClick(plan)}
variant="outline"
size="sm"
className="flex-1 border-red-500 text-red-500 hover:bg-red-500 hover:text-white rounded-full"
disabled={plan.subscriber_count > 0}
>
<Trash2 className="h-4 w-4 mr-1" />
Delete
</Button>
</div>
{hasPermission('subscriptions.plans') && (
<div className="flex flex-col sm:flex-row gap-2 pt-4 border-t border-[#ddd8eb]">
<Button
onClick={() => handleEditPlan(plan)}
variant="outline"
size="sm"
className="flex-1 border-[#664fa3] text-[#664fa3] hover:bg-[#664fa3] hover:text-white rounded-full"
>
<Edit className="h-4 w-4 mr-1" />
Edit
</Button>
<Button
onClick={() => handleDeleteClick(plan)}
variant="outline"
size="sm"
className="flex-1 border-red-500 text-red-500 hover:bg-red-500 hover:text-white rounded-full"
disabled={plan.subscriber_count > 0}
>
<Trash2 className="h-4 w-4 mr-1" />
Delete
</Button>
</div>
)}
{/* 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'}
</p>
{!searchQuery && activeFilter === 'all' && (
{!searchQuery && activeFilter === 'all' && hasPermission('subscriptions.plans') && (
<Button
onClick={handleCreatePlan}
className="bg-[#DDD8EB] text-[#422268] hover:bg-white rounded-full px-8"