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, { 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]"
/>
</div>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
disabled={exporting}
className="bg-[#81B29A] text-white hover:bg-[#6a9680] rounded-full px-6 py-3 flex items-center gap-2"
>
<Download className="h-4 w-4" />
{exporting ? 'Exporting...' : 'Export'}
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end" className="w-56 bg-white rounded-xl border-2 border-[#ddd8eb] shadow-lg">
<DropdownMenuItem
onClick={() => handleExport('all')}
className="cursor-pointer hover:bg-[#f1eef9] rounded-lg p-3"
>
<FileDown className="h-4 w-4 mr-2 text-[#664fa3]" />
<span className="text-[#422268]">Export All Donations</span>
</DropdownMenuItem>
<DropdownMenuItem
onClick={() => handleExport('current')}
className="cursor-pointer hover:bg-[#f1eef9] rounded-lg p-3"
>
<FileDown className="h-4 w-4 mr-2 text-[#664fa3]" />
<span className="text-[#422268]">Export Current View</span>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
{hasPermission('donations.export') && (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
disabled={exporting}
className="bg-[#81B29A] text-white hover:bg-[#6a9680] rounded-full px-6 py-3 flex items-center gap-2"
>
<Download className="h-4 w-4" />
{exporting ? 'Exporting...' : 'Export'}
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end" className="w-56 bg-white rounded-xl border-2 border-[#ddd8eb] shadow-lg">
<DropdownMenuItem
onClick={() => handleExport('all')}
className="cursor-pointer hover:bg-[#f1eef9] rounded-lg p-3"
>
<FileDown className="h-4 w-4 mr-2 text-[#664fa3]" />
<span className="text-[#422268]">Export All Donations</span>
</DropdownMenuItem>
<DropdownMenuItem
onClick={() => handleExport('current')}
className="cursor-pointer hover:bg-[#f1eef9] rounded-lg p-3"
>
<FileDown className="h-4 w-4 mr-2 text-[#664fa3]" />
<span className="text-[#422268]">Export Current View</span>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
)}
</div>
{/* Filters Row */}