RBAC Improvements

Caching improvements
Maker-Checker Implementation
This commit is contained in:
2026-10-21 07:27:47 +02:00
parent dee9098379
commit 5d7e2f74ce
25 changed files with 1269 additions and 309 deletions
@@ -25,6 +25,7 @@ import {
updateMilestone,
} from '../../services/api';
import { Project, Milestone, Priority, ApprovalStatus } from '../../types';
import { useRbac } from '../../auth/rbac';
import {
formatDate,
getApprovalStatusBadge,
@@ -55,6 +56,7 @@ export const ProjectMilestonesModal: React.FC<ProjectMilestonesModalProps> = ({
}) => {
const queryClient = useQueryClient();
const { success, error: toastError } = useToast();
const { canAdd, canApprove, canApproveRecord, isMaker } = useRbac();
const [isFormOpen, setIsFormOpen] = useState(false);
const [editingMilestone, setEditingMilestone] = useState<Milestone | null>(null);
@@ -175,7 +177,7 @@ export const ProjectMilestonesModal: React.FC<ProjectMilestonesModalProps> = ({
</div>
<div className="flex items-center gap-2">
{!isFormOpen && (
{!isFormOpen && canAdd('projects_milestones') && (
<button
type="button"
onClick={openCreateForm}
@@ -255,16 +257,33 @@ export const ProjectMilestonesModal: React.FC<ProjectMilestonesModalProps> = ({
{editingMilestone && (
<div>
<label className="block text-xs font-semibold text-slate-300 mb-1">Approval / Verification Status</label>
<select
{...form.register('status')}
className="w-full px-3 py-2 text-xs border border-slate-700/80 rounded-xl focus:outline-none focus:ring-1 focus:ring-blue-500 bg-[#090d16] text-white font-medium"
>
{APPROVAL_STATUS_OPTIONS.map((opt) => (
<option key={opt.value} value={opt.value}>
{opt.label}
</option>
))}
</select>
{(() => {
const canApproveMilestone = canApproveRecord('projects_milestones', editingMilestone.createdBy);
const isSelf = isMaker(editingMilestone.createdBy);
return (
<select
{...form.register('status')}
className="w-full px-3 py-2 text-xs border border-slate-700/80 rounded-xl focus:outline-none focus:ring-1 focus:ring-blue-500 bg-[#090d16] text-white font-medium"
>
{APPROVAL_STATUS_OPTIONS.map((opt) => {
const isApprovedOpt = opt.value === ApprovalStatus.Approved;
const isOptDisabled = isApprovedOpt && !canApproveMilestone;
let reasonText = '';
if (isApprovedOpt && !canApprove('projects_milestones')) {
reasonText = ' (Requires Update Permission)';
} else if (isApprovedOpt && isSelf) {
reasonText = ' (Maker-Checker: Cannot approve own record)';
}
return (
<option key={opt.value} value={opt.value} disabled={isOptDisabled}>
{opt.label}{reasonText}
</option>
);
})}
</select>
);
})()}
</div>
)}