Refactored projects page, fixed security user modal

This commit is contained in:
2026-10-22 07:39:33 +02:00
parent c24e809b88
commit ccc49df720
7 changed files with 288 additions and 337 deletions
@@ -723,23 +723,23 @@ export const ProjectsPage: React.FC = () => {
{/* Projects Table */}
<div className="overflow-x-auto">
<table className="w-full text-left text-xs text-slate-300 border-collapse">
<table className="w-full text-left text-xs text-slate-300 border-collapse min-w-[960px]">
<thead>
<tr className="border-b border-slate-800/80 bg-[#090d16] text-[11px] font-semibold text-slate-400 select-none">
<th className="py-3 px-3 w-10 text-center"></th>
<SortableTh column="name" currentColumn={sortColumn} direction={sortDirection} onSort={handleSort} className="py-3 px-4">
<SortableTh column="name" currentColumn={sortColumn} direction={sortDirection} onSort={handleSort} className="py-3 px-4 min-w-[180px]">
Project Name & ID
</SortableTh>
<SortableTh column="programme" currentColumn={sortColumn} direction={sortDirection} onSort={handleSort} className="py-3 px-4">
<SortableTh column="programme" currentColumn={sortColumn} direction={sortDirection} onSort={handleSort} className="py-3 px-4 min-w-[140px]">
Parent Programme
</SortableTh>
<SortableTh column="duration" currentColumn={sortColumn} direction={sortDirection} onSort={handleSort} className="py-3 px-4">
<SortableTh column="duration" currentColumn={sortColumn} direction={sortDirection} onSort={handleSort} className="py-3 px-4 min-w-[140px] whitespace-nowrap">
Schedule (Timeline)
</SortableTh>
<SortableTh column="progress" currentColumn={sortColumn} direction={sortDirection} onSort={handleSort} className="py-3 px-4">
<SortableTh column="progress" currentColumn={sortColumn} direction={sortDirection} onSort={handleSort} className="py-3 px-4 w-28 whitespace-nowrap">
WBS Progress
</SortableTh>
<SortableTh column="risk" currentColumn={sortColumn} direction={sortDirection} onSort={handleSort} className="py-3 px-4">
<SortableTh column="risk" currentColumn={sortColumn} direction={sortDirection} onSort={handleSort} className="py-3 px-4 min-w-[125px] whitespace-nowrap">
<div className="flex items-center gap-1">
<span>Schedule Risk</span>
<HelpTooltip
@@ -750,10 +750,10 @@ export const ProjectsPage: React.FC = () => {
/>
</div>
</SortableTh>
<SortableTh column="status" currentColumn={sortColumn} direction={sortDirection} onSort={handleSort} className="py-3 px-4">
<SortableTh column="status" currentColumn={sortColumn} direction={sortDirection} onSort={handleSort} className="py-3 px-4 w-32 whitespace-nowrap">
Status
</SortableTh>
<th className="py-3 px-4 text-right font-semibold">Resource Management Hub</th>
<th className="py-3 px-4 text-right font-semibold whitespace-nowrap min-w-[170px]">Actions</th>
</tr>
</thead>
<tbody className="divide-y divide-slate-800/60 font-medium">
@@ -952,8 +952,8 @@ export const ProjectsPage: React.FC = () => {
</td>
{/* Governance Status */}
<td className="py-4 px-4" onClick={(e) => e.stopPropagation()}>
<div className="space-y-1">
<td className="py-4 px-4 whitespace-nowrap" onClick={(e) => e.stopPropagation()}>
<div className="w-32">
{(() => {
const canApproveThis = canApproveRecord('projects_milestones', p.createdBy);
const isSelf = isMaker(p.createdBy);
@@ -969,16 +969,16 @@ export const ProjectsPage: React.FC = () => {
status: Number(e.target.value) as ApprovalStatus,
})
}
className="text-xs font-semibold bg-[#090d16] border border-slate-700/80 rounded-xl px-2.5 py-1 text-slate-200 focus:outline-none focus:ring-1 focus:ring-blue-500 cursor-pointer disabled:opacity-60 disabled:cursor-not-allowed"
className="w-full text-xs font-semibold bg-[#090d16] border border-slate-700/80 rounded-xl px-2.5 py-1 text-slate-200 focus:outline-none focus:ring-1 focus:ring-blue-500 cursor-pointer disabled:opacity-60 disabled:cursor-not-allowed truncate"
>
{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 Permission)';
reasonText = ' (Requires Update)';
} else if (isApprovedOpt && isSelf) {
reasonText = ' (Maker-Checker: Cannot approve own record)';
reasonText = ' (Maker-Checker)';
}
return (
<option key={opt.value} value={opt.value} disabled={isOptDisabled}>
@@ -993,57 +993,57 @@ export const ProjectsPage: React.FC = () => {
</td>
{/* Row Level Buttons: Milestones, Activities, Indicators, Risks, Edit */}
<td className="py-4 px-4 text-right" onClick={(e) => e.stopPropagation()}>
<div className="flex items-center justify-end gap-1.5 flex-wrap">
<td className="py-4 px-4 text-right whitespace-nowrap" onClick={(e) => e.stopPropagation()}>
<div className="flex items-center justify-end gap-1.5 flex-nowrap">
{/* Milestones Modal Button */}
<button
type="button"
onClick={(e) => handleOpenMilestones(p, e)}
className="inline-flex items-center gap-1 px-2.5 py-1 text-[11px] font-semibold text-blue-300 hover:text-white bg-blue-500/10 hover:bg-blue-500/20 border border-blue-500/30 rounded-lg transition cursor-pointer shadow-sm"
className="inline-flex items-center gap-1.5 px-2.5 py-1.5 text-[11px] font-semibold text-blue-300 hover:text-white bg-blue-500/10 hover:bg-blue-500/20 border border-blue-500/30 rounded-xl transition cursor-pointer shadow-xs shrink-0"
title="Manage Contractual Milestones"
>
<Flag className="w-3 h-3 text-blue-400" />
<span>Milestones</span>
<Flag className="w-3.5 h-3.5 text-blue-400 shrink-0" />
<span className="hidden xl:inline">Milestones</span>
</button>
{/* Activities Modal Button */}
<button
type="button"
onClick={(e) => handleOpenActivities(p, e)}
className="inline-flex items-center gap-1 px-2.5 py-1 text-[11px] font-semibold text-cyan-300 hover:text-white bg-cyan-500/10 hover:bg-cyan-500/20 border border-cyan-500/30 rounded-lg transition cursor-pointer shadow-sm"
className="inline-flex items-center gap-1.5 px-2.5 py-1.5 text-[11px] font-semibold text-cyan-300 hover:text-white bg-cyan-500/10 hover:bg-cyan-500/20 border border-cyan-500/30 rounded-xl transition cursor-pointer shadow-xs shrink-0"
title="Manage Work Breakdown Activities"
>
<Briefcase className="w-3 h-3 text-cyan-400" />
<span>Activities</span>
<Briefcase className="w-3.5 h-3.5 text-cyan-400 shrink-0" />
<span className="hidden xl:inline">Activities</span>
</button>
{/* Indicators Modal Button */}
<button
type="button"
onClick={(e) => handleOpenIndicators(p, e)}
className="inline-flex items-center gap-1 px-2.5 py-1 text-[11px] font-semibold text-indigo-300 hover:text-white bg-indigo-500/10 hover:bg-indigo-500/20 border border-indigo-500/30 rounded-lg transition cursor-pointer shadow-sm"
className="inline-flex items-center gap-1.5 px-2.5 py-1.5 text-[11px] font-semibold text-indigo-300 hover:text-white bg-indigo-500/10 hover:bg-indigo-500/20 border border-indigo-500/30 rounded-xl transition cursor-pointer shadow-xs shrink-0"
title="Manage M&E Performance Indicators"
>
<Target className="w-3 h-3 text-indigo-400" />
<span>Indicators</span>
<Target className="w-3.5 h-3.5 text-indigo-400 shrink-0" />
<span className="hidden xl:inline">Indicators</span>
</button>
{/* Risks Modal Button */}
<button
type="button"
onClick={(e) => handleOpenRisks(p, e)}
className="inline-flex items-center gap-1 px-2.5 py-1 text-[11px] font-semibold text-rose-300 hover:text-white bg-rose-500/10 hover:bg-rose-500/20 border border-rose-500/30 rounded-lg transition cursor-pointer shadow-sm"
className="inline-flex items-center gap-1.5 px-2.5 py-1.5 text-[11px] font-semibold text-rose-300 hover:text-white bg-rose-500/10 hover:bg-rose-500/20 border border-rose-500/30 rounded-xl transition cursor-pointer shadow-xs shrink-0"
title="Manage Risk Register (ISO 31000)"
>
<ShieldAlert className="w-3 h-3 text-rose-400" />
<span>Risks</span>
<ShieldAlert className="w-3.5 h-3.5 text-rose-400 shrink-0" />
<span className="hidden xl:inline">Risks</span>
</button>
{/* Edit Project Button */}
<button
type="button"
onClick={(e) => handleOpenEditProject(p, e)}
className="p-1.5 rounded-lg text-slate-400 hover:text-white hover:bg-slate-800 transition cursor-pointer"
className="p-1.5 rounded-xl text-slate-400 hover:text-white hover:bg-slate-800 border border-transparent hover:border-slate-700/60 transition cursor-pointer shrink-0"
title="Edit Project Scope & Schedule"
>
<Edit2 className="w-3.5 h-3.5" />