Skip to content

Commit 02e41cd

Browse files
Valentin Grünerclaude
andcommitted
refactor(profile): keep an outcome's icon with its words
`ReviewObservationRow` held a second table mapping each outcome to an icon, beside the one in `observation-outcome` holding the label and the tone for the same six values. A seventh outcome could have gained words there and no glyph here, and nothing would have said so. The icon moves in with them. The review-run fixtures also pinned `reviewedAt` to a calendar date while the story clock moves, so a timeline that reads "2 days ago" today reads as ancient next year. They take it from `daysBefore` now, like the profile stories beside them. The `createdAt` literals stay: nothing renders them. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MAQxdMw579siMh1wXKEEh4
1 parent a965d4a commit 02e41cd

5 files changed

Lines changed: 30 additions & 29 deletions

File tree

webapp/src/components/profile/PracticeGroupDetailPage.test.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { fireEvent, render, screen } from "@testing-library/react";
22
import { describe, expect, it, vi } from "vitest";
33
import type { PracticeGroup, PracticeGroupReviewRun, PracticeGroupStanding } from "@/api/types.gen";
4+
import { daysBefore } from "@/components/common/story-clock";
45
import { PracticeGroupDetailPage } from "./PracticeGroupDetailPage";
56

67
const group: PracticeGroup = {
@@ -23,7 +24,7 @@ const standing: PracticeGroupStanding = {
2324

2425
const run: PracticeGroupReviewRun = {
2526
reviewId: "00000000-0000-0000-0000-000000000901",
26-
reviewedAt: new Date("2026-08-12T10:26:00Z"),
27+
reviewedAt: daysBefore(2),
2728
reviewedWork: { id: 902, type: "scm.pull_request", provider: "GITHUB", number: 902 },
2829
observations: [],
2930
};

webapp/src/components/profile/ReviewObservationRow.tsx

Lines changed: 3 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,4 @@
1-
import {
2-
ChevronDownIcon,
3-
CircleAlertIcon,
4-
CircleCheckIcon,
5-
CircleDashedIcon,
6-
CircleHelpIcon,
7-
CircleXIcon,
8-
ShieldCheckIcon,
9-
} from "lucide-react";
1+
import { ChevronDownIcon } from "lucide-react";
102
import type { PracticeGroupReviewObservation } from "@/api/types.gen";
113
import { QueryErrorAlert } from "@/components/common/QueryErrorAlert";
124
import {
@@ -31,26 +23,13 @@ import { cn } from "@/lib/utils";
3123
import { toEvidenceLocations } from "./evidence";
3224
import { EvidenceFileBlock } from "./EvidenceFileBlock";
3325
import { FeedbackComment } from "./FeedbackComment";
34-
import {
35-
OBSERVATION_OUTCOME_PRESENTATION,
36-
type ObservationOutcome,
37-
observationOutcome,
38-
} from "./observation-outcome";
26+
import { OBSERVATION_OUTCOME_PRESENTATION, observationOutcome } from "./observation-outcome";
3927
import {
4028
type FeedbackResponse,
4129
feedbackResponseOf,
4230
type ObservationDetailState,
4331
} from "./review-runs";
4432

45-
const OUTCOME_ICON: Record<ObservationOutcome, typeof CircleCheckIcon> = {
46-
PRESENT_GOOD: CircleCheckIcon,
47-
ABSENT_GOOD: ShieldCheckIcon,
48-
PRESENT_BAD: CircleAlertIcon,
49-
ABSENT_BAD: CircleXIcon,
50-
NOT_APPLICABLE: CircleDashedIcon,
51-
INCONCLUSIVE: CircleHelpIcon,
52-
};
53-
5433
interface ResponseChoiceProps<TValue extends string> {
5534
legend: string;
5635
defs: StatusDefs<TValue>;
@@ -126,7 +105,7 @@ export function ReviewObservationRow({
126105
}: ReviewObservationRowProps) {
127106
const outcome = observationOutcome(observation);
128107
const status = OBSERVATION_OUTCOME_PRESENTATION[outcome];
129-
const StatusIcon = OUTCOME_ICON[outcome];
108+
const StatusIcon = status.icon;
130109
const canRespond = Boolean(observation.feedbackId && onRespond);
131110
const canOpen = onToggle !== undefined || canRespond;
132111
const detail = detailState?.detail;

webapp/src/components/profile/ReviewRunTimeline.stories.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
import type { Meta, StoryObj } from "@storybook/react";
22
import type { PracticeGroupReviewRun } from "@/api/types.gen";
3+
import { daysBefore } from "@/components/common/story-clock";
34
import { ReviewRunTimeline } from "./ReviewRunTimeline";
45

56
const runs: PracticeGroupReviewRun[] = [
67
{
78
reviewId: "00000000-0000-0000-0000-000000000101",
8-
reviewedAt: new Date("2026-08-12T10:26:00Z"),
9+
reviewedAt: daysBefore(2),
910
reviewedWork: {
1011
type: "scm.pull_request",
1112
id: 902,
@@ -27,7 +28,7 @@ const runs: PracticeGroupReviewRun[] = [
2728
},
2829
{
2930
reviewId: "00000000-0000-0000-0000-000000000201",
30-
reviewedAt: new Date("2026-08-09T16:40:00Z"),
31+
reviewedAt: daysBefore(5),
3132
reviewedWork: {
3233
type: "chat.conversation_thread",
3334
id: 42,

webapp/src/components/profile/ReviewRunTimeline.test.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { fireEvent, render, screen } from "@testing-library/react";
22
import { describe, expect, it, vi } from "vitest";
33
import type { PracticeGroupReviewObservation, PracticeGroupReviewRun } from "@/api/types.gen";
4+
import { daysBefore } from "@/components/common/story-clock";
45
import { ReviewRunTimeline } from "./ReviewRunTimeline";
56

67
const baseObservation = {
@@ -18,7 +19,7 @@ const baseObservation = {
1819

1920
const run = {
2021
reviewId: "00000000-0000-0000-0000-000000000101",
21-
reviewedAt: new Date("2026-08-12T10:26:00Z"),
22+
reviewedAt: daysBefore(2),
2223
reviewedWork: {
2324
type: "scm.pull_request",
2425
id: 902,

webapp/src/components/profile/observation-outcome.ts

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,13 @@
1+
import {
2+
CircleAlertIcon,
3+
CircleCheckIcon,
4+
CircleDashedIcon,
5+
CircleHelpIcon,
6+
CircleXIcon,
7+
type LucideIcon,
8+
ShieldCheckIcon,
9+
} from "lucide-react";
10+
111
import type { Assessment } from "@/components/practice-vocabulary/assessment-defs";
212
import type { Presence } from "@/components/practice-vocabulary/presence-defs";
313

@@ -21,32 +31,41 @@ export type ObservationOutcome =
2131
export const OBSERVATION_OUTCOME_PRESENTATION = {
2232
PRESENT_GOOD: {
2333
label: "Strength shown",
34+
icon: CircleCheckIcon,
2435
className: "text-success",
2536
},
2637
ABSENT_GOOD: {
2738
label: "Risk avoided",
39+
icon: ShieldCheckIcon,
2840
className: "text-success",
2941
},
3042
PRESENT_BAD: {
3143
label: "Problem observed",
44+
icon: CircleAlertIcon,
3245
className: "text-destructive",
3346
},
3447
ABSENT_BAD: {
3548
label: "Expected practice missing",
49+
icon: CircleXIcon,
3650
className: "text-destructive",
3751
},
3852
NOT_APPLICABLE: {
3953
label: "Not assessed",
54+
icon: CircleDashedIcon,
4055
className: "text-muted-foreground",
4156
},
4257
// A distinct silence from NOT_APPLICABLE: the reviewer looked at work that DID offer the opportunity
4358
// and could not claim either way. Collapsing the two would report "no opportunity" for a practice the
4459
// reviewer was simply unsure about.
4560
INCONCLUSIVE: {
4661
label: "Not certain enough to say",
62+
icon: CircleHelpIcon,
4763
className: "text-muted-foreground",
4864
},
49-
} as const satisfies Record<ObservationOutcome, { label: string; className: string }>;
65+
} as const satisfies Record<
66+
ObservationOutcome,
67+
{ label: string; icon: LucideIcon; className: string }
68+
>;
5069

5170
/**
5271
* Derives the complete 2×2 observation state plus the two verdict-less states. The API guarantees no

0 commit comments

Comments
 (0)