fix: persist feedback/comments across page reloads on review page

Feedback was saving to DB but never loaded back on page revisit.
Three-point fix:
- Backend schema: add feedback list to OutputRowResponse
- Backend service: eagerly load feedback relationship in preview query
- Frontend mapper: map latest feedback entry to OutputRow.feedback

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
DJP 2026-04-10 16:54:49 -04:00
parent 5e0a148b96
commit 8b07a59da0
3 changed files with 35 additions and 1 deletions

View file

@ -1,3 +1,4 @@
from datetime import datetime
from typing import Any
from uuid import UUID
@ -6,6 +7,17 @@ from pydantic import BaseModel
from app.models.output import ConfidenceTier
class FeedbackSummary(BaseModel):
"""Lightweight feedback info embedded in output row responses."""
id: UUID
flag_type: str
comment: str | None = None
user_id: UUID
created_at: datetime
model_config = {"from_attributes": True}
class OutputRowResponse(BaseModel):
id: UUID
instance_id: UUID
@ -26,6 +38,7 @@ class OutputRowResponse(BaseModel):
character_count_option_1: int | None = None
character_count_option_2: int | None = None
character_count_option_3: int | None = None
feedback: list[FeedbackSummary] = []
model_config = {"from_attributes": True}

View file

@ -47,11 +47,12 @@ class OutputService:
for sl in source_result.scalars().all()
]
# Get output rows
# Get output rows with feedback eagerly loaded
output_result = await db.execute(
select(OutputRow)
.where(OutputRow.instance_id == instance.id)
.order_by(OutputRow.row_order)
.options(selectinload(OutputRow.feedback))
)
output_rows = [
OutputRowResponse.model_validate(row)

View file

@ -357,6 +357,18 @@ export async function getOutputPreview(
});
}
// Map feedback - take the most recent entry if any exist
const feedbackList = row.feedback || [];
const latestFeedback = feedbackList.length > 0
? feedbackList[feedbackList.length - 1]
: null;
const STATUS_MAP: Record<string, "APPROVED" | "NEEDS_REVISION" | "COMMENTED"> = {
approved: "APPROVED",
needs_revision: "NEEDS_REVISION",
comment: "COMMENTED",
};
return {
id: String(row.id),
locale_instance_id: String(row.instance_id),
@ -368,6 +380,14 @@ export async function getOutputPreview(
options,
rationale: row.rationale_1 || "",
is_flagged: row.confidence_tier === "low",
feedback: latestFeedback ? {
id: String(latestFeedback.id),
output_row_id: String(row.id),
status: STATUS_MAP[latestFeedback.flag_type] || "COMMENTED",
comment: latestFeedback.comment || undefined,
reviewer: String(latestFeedback.user_id),
created_at: latestFeedback.created_at,
} : undefined,
};
});