feat: deploy approval UX and four-eyes principle#47
Conversation
- Fleet: collapse node labels into a compact count button with popover - Fleet: replace native confirm() with styled ConfirmDialog for maintenance mode - Dashboard: add drag, resize, and reorder support for custom view panels using react-grid-layout - Pipelines: center the SLI health dot in the Health column - Profile: display user role and super admin status in personal info
…types stub - Add queryClient.invalidateQueries for dashboard.listViews on updateView mutation success so remounting loads the persisted layout - Remove @types/react-grid-layout (deprecated stub, v2 ships own types)
- Use a ref (filtersRef) to hold latest filter values so the debounce timer reads current pipelineIds/nodeIds when it fires, not the values captured at scheduling time - Clear pending debounce timer on component unmount to prevent stale mutations after navigation
- Move filtersRef.current assignment into a useEffect to satisfy react-hooks/refs lint rule (no ref writes during render) - Wrap panels cast in useMemo to stabilize the dependency for downstream useMemo hooks
- Move deploy approval toggle from edit form to dedicated "Deploy Settings" section with live-save toggle, greyed out for non-admins - Allow editors to approve/reject other editors' deploy requests (four-eyes principle) — self-approval still blocked - Show admin warning banner on deploy dialog when bypassing approval - Include configYaml in pending requests for editor reviewers
Greptile SummaryThis PR introduces the four-eyes deploy approval workflow and relocates the approval toggle into a dedicated Deploy Settings section. The majority of the changes are well-implemented — the self-approval guard in
Confidence Score: 2/5
Important Files Changed
Sequence DiagramsequenceDiagram
participant EditorA as Editor A (Requester)
participant EditorB as Editor B (Reviewer)
participant Admin as Admin
participant Server as tRPC Server
participant DB as Database
EditorA->>Server: deploy.agent (requiresApproval=true)
Server->>DB: Create DeployRequest (PENDING)
Server-->>EditorA: { requestId }
EditorB->>Server: deploy.listPendingRequests
Server->>DB: findMany (PENDING)
DB-->>Server: requests (configYaml excluded for editors ⚠️ currently included)
Server-->>EditorB: pending requests
alt Four-eyes: EditorB reviews
EditorB->>Server: deploy.approveDeployRequest
Server->>DB: check requestedById ≠ reviewerId
Server->>DB: updateMany status=APPROVED (atomic)
Server->>Server: deployAgent(configYaml snapshot)
Server-->>EditorB: deploy result
else EditorB rejects
EditorB->>Server: deploy.rejectDeployRequest
Note over Server: ⚠️ No self-rejection guard
Server->>DB: updateMany status=REJECTED (atomic)
Server-->>EditorB: { rejected: true }
end
alt Admin bypass
Admin->>Server: deploy.agent (direct, no approval needed)
Server-->>Admin: deploy result (with amber warning shown in UI)
end
|
Summary
Test plan