diff --git a/src/pages/courts/CourtReportCreate.tsx b/src/pages/courts/CourtReportCreate.tsx index 840000a..493ceb3 100644 --- a/src/pages/courts/CourtReportCreate.tsx +++ b/src/pages/courts/CourtReportCreate.tsx @@ -26,6 +26,7 @@ import { courtLabel, CourtStandingReference, CourtTargetPreview, + CourtTriggerCounter, formatCourtInstant, } from "./components/CourtPrimitives"; import { @@ -47,7 +48,8 @@ import { import { courtLaneDisplay, courtOffenseDisplay, - courtStandingDisplay, + courtReportLaneChoiceLabel, + courtReportRouteDescription, } from "./model/courtPresentation"; import { courtErrorIssue, @@ -316,6 +318,11 @@ const CourtReportCreate: React.FC = () => { setSubmissionError("Choose a report reason."); return; } + if (!respondentId.trim()) { + setSubmissionError("A respondent address is required."); + focusCourtField("court-report-respondent"); + return; + } const startsAt = Date.parse(incidentStartsAt); const endsAt = incidentEndsAt ? Date.parse(incidentEndsAt) : null; if (endsAt !== null && endsAt < startsAt) { @@ -499,7 +506,7 @@ const CourtReportCreate: React.FC = () => { value={`${reason.offenseCode}:${reason.lane}`} > {courtOffenseDisplay(reason.offenseCode).label} ·{" "} - {courtLaneDisplay(reason.lane).label} + {courtReportLaneChoiceLabel(reason.lane)} ))} @@ -527,7 +534,10 @@ const CourtReportCreate: React.FC = () => { direct={selectedReason.standing.directStanding} source={selectedReason.standing.source} /> - {`. ${courtStandingDisplay(selectedReason.standing).description}`} + {`. ${courtReportRouteDescription( + selectedReason.reason.lane, + selectedReason.standing, + )}`} ) : capabilityLoading ? ( "The server is verifying the target, incident time, standing, and available lanes." @@ -535,12 +545,40 @@ const CourtReportCreate: React.FC = () => { "Reasons are limited to those verified for this exact target and incident time." )}

- {selectedReason && capability?.population ? ( -

- Population basis: {courtLabel(capability.population.basis)} ·{" "} - effective{" "} - {formatCourtInstant(capability.population.effectiveAt)}. -

+ {selectedReason ? ( +
+ {selectedReason.reason.lane === "court_report" && + !selectedReason.standing.directStanding ? ( + capability?.population?.communityThreshold ? ( + + ) : ( +

+ A community Court trigger is unavailable because fewer + than three eligible Governors remain after excluding the + respondent. +

+ ) + ) : ( + + )} + {capability?.population ? ( +

+ Governor population:{" "} + {capability.population.eligibleGovernorCount} ·{" "} + {courtLabel(capability.population.basis)} · effective{" "} + {formatCourtInstant(capability.population.effectiveAt)}. +

+ ) : null} +
) : null} {capabilityError ? (
@@ -570,14 +608,19 @@ const CourtReportCreate: React.FC = () => { hint={ capability?.defaults.respondentIdSource === "sole_target_owner" - ? "Suggested from the record owner. Change it if another Human Node is responsible." - : "The Human Node whose conduct is being reported, when known." + ? "Set from the owner of the reported record." + : "The Human Node whose conduct is being reported." } > setRespondentId(event.target.value)} + readOnly={ + capability?.defaults.respondentIdSource === + "sole_target_owner" + } + required /> { incidentStartsAt={incidentStartsAt} onGoodFaithAttestedChange={setGoodFaithAttested} protectiveReviewRequested={immediateProtectionRequested} + respondentId={respondentId} selectedReason={selectedReason} statementAccess={statementAccess} statementLength={statement.trim().length} @@ -715,6 +759,7 @@ const CourtReportCreate: React.FC = () => { !capability || capabilityLoading || !reasonKey || + !respondentId.trim() || statement.trim().length < COURT_STATEMENT_MIN_LENGTH || statement.trim().length > COURT_STATEMENT_MAX_LENGTH || !goodFaithAttested || diff --git a/src/pages/courts/CourtReportDetail.tsx b/src/pages/courts/CourtReportDetail.tsx index 55de05e..06bdcd0 100644 --- a/src/pages/courts/CourtReportDetail.tsx +++ b/src/pages/courts/CourtReportDetail.tsx @@ -48,6 +48,7 @@ import { CourtStandingReference, CourtStateSummary, CourtTargetPreview, + CourtTriggerCounter, courtTone, formatCourtInstant, } from "./components/CourtPrimitives"; @@ -349,6 +350,17 @@ const CourtReportDetail: React.FC = () => { label="Target" value={courtLabel(report.target.type)} /> + {report.respondentId ? ( + + } + /> + ) : null} { } /> + {report.triggerProgress ? ( + + ) : null} {process ? (

Next: {process.nextStep} @@ -492,7 +511,15 @@ const CourtReportDetail: React.FC = () => { onChange={(event) => setRespondentId(event.target.value) } + readOnly={Boolean(report.respondentId)} + required /> + {report.respondentId ? ( + + The respondent is fixed to the reported record and + cannot be changed by amendment. + + ) : null}

+
+

{label}

+

+ {current === undefined + ? `${required} required` + : `${completed} / ${required}`} +

+
+ {current !== undefined ? ( +
+
+
+ ) : null} +

+ {description ?? + (current === undefined + ? `This action needs matching reports from ${required} eligible Governors.` + : completed >= required + ? "The Governor threshold has been reached." + : `${required - completed} more matching Governor ${required - completed === 1 ? "report" : "reports"} required.`)} + {viewerCounts === undefined + ? "" + : viewerCounts + ? " Your report counts toward this threshold." + : " Your report is recorded, but it does not add Governor support."} +

+
+ ); +} + export function courtTone( value: string, ): "danger" | "neutral" | "ok" | "primary" | "warn" { diff --git a/src/pages/courts/components/CourtRecordCards.tsx b/src/pages/courts/components/CourtRecordCards.tsx index c86815f..d6cb3ef 100644 --- a/src/pages/courts/components/CourtRecordCards.tsx +++ b/src/pages/courts/components/CourtRecordCards.tsx @@ -26,6 +26,7 @@ import { CourtCopyValue, CourtDeadline, CourtStandingReference, + CourtTriggerCounter, courtLabel, courtTone, formatCourtInstant, @@ -171,6 +172,17 @@ export function CourtReportCard({ label="Updated" value={formatCourtInstant(report.updatedAt)} /> + {report.respondentId ? ( + + } + /> + ) : null} {report.amendmentDueAt ? ( ) : null} + {report.triggerProgress ? ( + + ) : null}
{report.target.route ? (