Enforced approval rights

This commit is contained in:
2026-10-22 11:25:55 +02:00
parent 0261a6093e
commit 92891438f6
32 changed files with 1891 additions and 870 deletions
@@ -21,6 +21,7 @@ import {
Sparkles,
ExternalLink,
SlidersHorizontal,
Lock,
} from 'lucide-react';
import { Modal } from '../ui/Modal';
import { HelpTooltip } from '../ui/HelpTooltip';
@@ -282,36 +283,46 @@ export const ProjectViewModal: React.FC<ProjectViewModalProps> = ({
</div>
)}
{/* Real-time Governance Status Dropdown */}
<div className="w-36">
<select
value={project.status}
disabled={!canModifyStatus}
onChange={(e) =>
statusMutation.mutate({
projectId: project.id,
status: Number(e.target.value) as ApprovalStatus,
})
}
className="w-full text-xs font-semibold bg-[#090d16] border border-slate-700/90 rounded-xl px-2.5 py-2 text-slate-200 focus:outline-none focus:ring-2 focus:ring-blue-500 cursor-pointer disabled:opacity-60 disabled:cursor-not-allowed truncate shadow-md"
title="Project Governance Clearance Status"
>
{APPROVAL_STATUS_OPTIONS.map((opt) => {
const isApprovedOpt = opt.value === ApprovalStatus.Approved;
const isOptDisabled = isApprovedOpt && !canApproveThis;
let reasonText = '';
if (isApprovedOpt && !canApprove('projects_milestones')) {
reasonText = ' (Requires Update)';
} else if (isApprovedOpt && isSelf) {
reasonText = ' (Maker-Checker)';
{/* Real-time Governance Status Dropdown / Immutable Locked State */}
<div className="min-w-[140px]">
{Number(project.status) === ApprovalStatus.Approved ? (
<div
className="inline-flex items-center gap-1.5 px-3 py-2 rounded-xl text-xs font-bold bg-emerald-500/15 border border-emerald-500/30 text-emerald-300 shadow-sm cursor-default"
title="Approved records are immutable. Status cannot be modified."
>
<Lock className="w-3.5 h-3.5 text-emerald-400" />
<span>Approved (Locked)</span>
</div>
) : (
<select
value={project.status}
disabled={!canModifyStatus}
onChange={(e) =>
statusMutation.mutate({
projectId: project.id,
status: Number(e.target.value) as ApprovalStatus,
})
}
return (
<option key={opt.value} value={opt.value} disabled={isOptDisabled}>
{opt.label}{reasonText}
</option>
);
})}
</select>
className="w-full text-xs font-semibold bg-[#090d16] border border-slate-700/90 rounded-xl px-2.5 py-2 text-slate-200 focus:outline-none focus:ring-2 focus:ring-blue-500 cursor-pointer disabled:opacity-60 disabled:cursor-not-allowed truncate shadow-md"
title="Project Governance Clearance Status"
>
{APPROVAL_STATUS_OPTIONS.map((opt) => {
const isApprovedOpt = opt.value === ApprovalStatus.Approved;
const isOptDisabled = isApprovedOpt && !canApproveThis;
let reasonText = '';
if (isApprovedOpt && !canApprove('projects_milestones')) {
reasonText = ' (Requires Update)';
} else if (isApprovedOpt && isSelf) {
reasonText = ' (Maker-Checker)';
}
return (
<option key={opt.value} value={opt.value} disabled={isOptDisabled}>
{opt.label}{reasonText}
</option>
);
})}
</select>
)}
</div>
{/* Action Buttons */}