Task: ReportCard Component
Type: Component
Milestone: M0.5 — Shared Component Library
Estimate: M
Change Log
Updated: Now explicitly composes ListCard (not base Card directly). ListCard ticket must be completed before this one starts. Claimed state and deriveAvailableActions logic documented explicitly.
Component Type
Props Interface
interface ReportCardProps {
report: Report;
isExpanded: boolean;
onToggleExpand: () => void;
onAction: (reportId: string, action: ModerationAction) => void;
isClaimed?: boolean;
claimedBy?: string;
}
Internal Logic — deriveAvailableActions(report)
This function is the only place that decides which action buttons render. Maps directly to the Java backend state machine validateTransition().
| Button |
Condition |
| Run AI screening |
status === 'PENDING' && aiConfidenceScore === 0 |
| Remove content |
(status === 'PENDING' or 'ESCALATED') && targetType !== 'USER' |
| Ban author |
status === 'ESCALATED_TO_HUMAN' |
| Warn author |
status === 'PENDING' or 'ESCALATED_TO_HUMAN' |
| Dismiss |
status === 'PENDING' or 'ESCALATED_TO_HUMAN' |
| No buttons |
isClaimed === true — shows lock message instead |
Variants / States
| State |
Description |
| Collapsed |
Header row only — id, badges, title, AI score bar |
| Pending unscreened |
Expanded — "Run AI screening" button only |
| Pending screened |
Expanded — Remove + Warn + Dismiss |
| Escalated |
Expanded — all buttons including Ban author |
| Claimed |
Expanded — no buttons, shows "🔒 Being reviewed by {claimedBy}" |
| Resolved / Dismissed |
Expanded — read only, no action buttons |
Acceptance Criteria
Notes
ListCard must be merged and signed off before this ticket starts
StatusPipeline and AuditTrail are sub-components private to this file — extract only if another feature needs them
deriveAvailableActions must be updated here if the backend state machine adds new valid transitions — single point of change
Task: ReportCard Component
Type: Component
Milestone: M0.5 — Shared Component Library
Estimate: M
Change Log
Component Type
Props Interface
Internal Logic —
deriveAvailableActions(report)This function is the only place that decides which action buttons render. Maps directly to the Java backend state machine
validateTransition().status === 'PENDING' && aiConfidenceScore === 0(status === 'PENDING' or 'ESCALATED') && targetType !== 'USER'status === 'ESCALATED_TO_HUMAN'status === 'PENDING' or 'ESCALATED_TO_HUMAN'status === 'PENDING' or 'ESCALATED_TO_HUMAN'isClaimed === true— shows lock message insteadVariants / States
Acceptance Criteria
ListCard— zero layout or expand logic duplicated herederiveAvailableActions()is a pure function, unit tested independentlyonActionfires with correct(reportId, ModerationAction)— card does not execute the action itselfe.stopPropagation()on button clicks handled byListCardfooter — verify this works before closingProgressBarshared component — not a raw divBadgeshared componentButtonshared componentStatusPipelinesub-component (private to this file unless reused)deriveAvailableActionsfor all 6 statusesNotes
ListCardmust be merged and signed off before this ticket startsStatusPipelineandAuditTrailare sub-components private to this file — extract only if another feature needs themderiveAvailableActionsmust be updated here if the backend state machine adds new valid transitions — single point of change