Skip to content

Commit b718420

Browse files
jh-RLIclaude
andcommitted
Summary: classify each field by its current state (fixes contributor view)
The gather only read suggestion/deny from current_review.reviews (this session's responses), so on the contributor page the reviewer's suggested/denied fields - which live in state_dict, not the contributor's own reviews - fell through to 'To review'. Rewrote the gather as one role-agnostic pass that classifies each field by its current state (getFieldState/state_dict, reflecting whoever acted last), sourcing the suggestion/comment text from this round's response when present, else the server-rendered field row. The contributor now sees Suggested/Deny correctly; the reviewer view is unchanged. Drops the old three-pass dedup logic. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
1 parent 190b5fc commit b718420

1 file changed

Lines changed: 60 additions & 200 deletions

File tree

dataedit/static/peer_review/ui/summary.js

Lines changed: 60 additions & 200 deletions
Original file line numberDiff line numberDiff line change
@@ -14,217 +14,77 @@ import {
1414
} from "../utilities.js";
1515
import { updatePercentageDisplay } from "./navigation.js";
1616
export function renderSummaryPageFields() {
17-
const acceptedFields = [];
18-
const suggestingFields = [];
19-
const rejectedFields = [];
20-
const missingFields = [];
21-
const emptyFields = [];
22-
23-
const processedFields = new Set();
24-
25-
if (window.state_dict && Object.keys(window.state_dict).length > 0) {
26-
const fields = document.querySelectorAll(".field");
27-
for (let field of fields) {
28-
let field_id = field.id.slice(6);
29-
const fieldValue = $(field)
30-
.find(".value")
31-
.text()
32-
.replace(/\s+/g, " ")
33-
.trim();
34-
const fieldState = getFieldState(field_id);
35-
const fieldCategory = field.getAttribute("data-category");
36-
const fieldSuggestion =
37-
field
38-
.querySelector(".suggestion.suggestion--highlight")
39-
?.textContent.trim() || "";
40-
41-
// ADD THIS: read comment from DOM just like fieldSuggestion
42-
const fieldComment =
43-
field.querySelector(".suggestion--comment")?.textContent.trim() ||
44-
field
45-
.querySelector(".suggestion--additional-comment")
46-
?.textContent.trim() ||
47-
"";
48-
49-
let fieldName = field_id.replace(/\./g, " ");
50-
51-
if (fieldCategory !== "general") {
52-
fieldName = fieldName.split(" ").slice(1).join(" ");
53-
}
54-
55-
const uniqueFieldIdentifier = `${fieldName}-${fieldCategory}`;
56-
57-
if (isEffectivelyEmpty(field_id, fieldValue)) {
58-
emptyFields.push({
59-
fieldName,
60-
fieldValue,
61-
fieldCategory,
62-
fieldSuggestion,
63-
fieldComment, // now defined
64-
});
65-
} else if (fieldState === "ok") {
66-
acceptedFields.push({
67-
fieldName,
68-
fieldValue,
69-
fieldCategory,
70-
fieldSuggestion,
71-
fieldComment, // now defined
72-
});
73-
processedFields.add(uniqueFieldIdentifier);
74-
}
75-
76-
for (const review of current_review.reviews) {
77-
const fieldDomId = `field_${review.key}`;
78-
const fieldEl = document.getElementById(fieldDomId);
79-
const fieldValue = fieldEl
80-
? $(fieldEl).find(".value").text().replace(/\s+/g, " ").trim()
81-
: "";
82-
const fieldState = review.fieldReview.state;
83-
const fieldCategory = review.category;
84-
const field_id = field.id.slice(6);
85-
const fieldSuggestion = review.fieldReview.reviewerSuggestion || "";
86-
const fieldComment =
87-
review.fieldReview.comment ||
88-
review.fieldReview.additionalComment ||
89-
"";
90-
91-
let fieldName = review.key.replace(/\./g, " ");
92-
93-
if (fieldCategory !== "general") {
94-
fieldName = fieldName.split(" ").slice(1).join(" ");
95-
}
96-
97-
const uniqueFieldIdentifier = `${fieldName}-${fieldCategory}`;
98-
99-
if (processedFields.has(uniqueFieldIdentifier)) {
100-
continue;
101-
}
102-
103-
if (isEffectivelyEmpty(field_id, fieldValue)) {
104-
emptyFields.push({
105-
fieldName,
106-
fieldValue,
107-
fieldCategory,
108-
fieldSuggestion,
109-
fieldComment,
110-
});
111-
} else if (fieldState === "ok") {
112-
acceptedFields.push({
113-
fieldName,
114-
fieldValue,
115-
fieldCategory,
116-
fieldSuggestion,
117-
fieldComment,
118-
});
119-
} else if (fieldState === "suggestion") {
120-
suggestingFields.push({
121-
fieldName,
122-
fieldValue,
123-
fieldCategory,
124-
fieldSuggestion,
125-
fieldComment,
126-
});
127-
} else if (fieldState === "rejected") {
128-
rejectedFields.push({
129-
fieldName,
130-
fieldValue,
131-
fieldCategory,
132-
fieldSuggestion,
133-
fieldComment,
134-
});
135-
}
136-
137-
processedFields.add(uniqueFieldIdentifier);
138-
}
139-
}
140-
}
141-
142-
const categories = document.querySelectorAll(".tab-pane");
17+
// This round's in-progress responses, indexed by field key — used for the
18+
// suggestion/comment text the current actor just entered.
19+
const reviewsByKey = {};
20+
((current_review && current_review.reviews) || []).forEach((r) => {
21+
reviewsByKey[r.key] = r;
22+
});
14323

144-
for (const category of categories) {
145-
const category_name = category.id;
24+
// One row per field, classified by its CURRENT state (window.state_dict, which
25+
// reflects whoever acted last). This is role-agnostic: the contributor sees the
26+
// reviewer's suggested/denied fields, the reviewer sees the contributor's, and
27+
// each sees their own answers — not only this session's entries.
28+
const allData = [];
29+
const categoriesMap = {};
14630

147-
if (category_name === "summary") {
148-
continue;
149-
}
150-
const category_fields = category.querySelectorAll(".field");
151-
for (let field of category_fields) {
152-
const field_id = field.id.slice(6);
153-
const fieldValue = $(field)
154-
.find(".value")
155-
.text()
156-
.replace(/\s+/g, " ")
157-
.trim();
158-
const found = current_review.reviews.some(
159-
(review) => review.key === field_id
160-
);
161-
const fieldState = getFieldState(field_id);
162-
const fieldCategory = field.getAttribute("data-category");
163-
const fieldSuggestion =
164-
field
31+
document.querySelectorAll(".field").forEach((field) => {
32+
const key = field.id.slice(6);
33+
if (!key) return;
34+
35+
const fieldCategory = field.getAttribute("data-category") || "general";
36+
const fieldValue = $(field)
37+
.find(".value")
38+
.text()
39+
.replace(/\s+/g, " ")
40+
.trim();
41+
const empty = isEffectivelyEmpty(key, fieldValue);
42+
const state = empty ? null : getFieldState(key);
43+
44+
let fieldStatus;
45+
if (empty) fieldStatus = "Empty";
46+
else if (state === "ok") fieldStatus = "Accepted";
47+
else if (state === "suggestion") fieldStatus = "Suggested";
48+
else if (state === "rejected") fieldStatus = "Rejected";
49+
else fieldStatus = "Missing";
50+
51+
// Suggestion / comment: prefer this round's own response, else the
52+
// server-rendered values already in the field row (the other party's latest
53+
// contribution).
54+
const review = reviewsByKey[key];
55+
const fr =
56+
review && review.fieldReview && !Array.isArray(review.fieldReview)
57+
? review.fieldReview
58+
: null;
59+
const fieldSuggestion = fr
60+
? fr.reviewerSuggestion || fr.newValue || ""
61+
: field
16562
.querySelector(".suggestion.suggestion--highlight")
16663
?.textContent.trim() || "";
167-
168-
const fieldComment =
169-
field.querySelector(".suggestion--comment")?.textContent.trim() ||
64+
const fieldComment = fr
65+
? fr.comment || fr.additionalComment || ""
66+
: field.querySelector(".suggestion--comment")?.textContent.trim() ||
17067
field
17168
.querySelector(".suggestion--additional-comment")
17269
?.textContent.trim() ||
17370
"";
17471

175-
let fieldName = field_id.replace(/\./g, " ");
176-
177-
if (fieldCategory !== "general") {
178-
fieldName = fieldName.split(" ").slice(1).join(" ");
179-
}
180-
181-
const uniqueFieldIdentifier = `${fieldName}-${fieldCategory}`;
182-
183-
if (
184-
!found &&
185-
fieldState !== "ok" &&
186-
!isEffectivelyEmpty(field_id, fieldValue) &&
187-
!processedFields.has(uniqueFieldIdentifier)
188-
) {
189-
missingFields.push({
190-
fieldName,
191-
fieldValue,
192-
fieldCategory,
193-
fieldSuggestion,
194-
fieldComment,
195-
});
196-
processedFields.add(uniqueFieldIdentifier);
197-
}
72+
let fieldName = key.replace(/\./g, " ");
73+
if (fieldCategory !== "general") {
74+
fieldName = fieldName.split(" ").slice(1).join(" ");
19875
}
199-
}
200-
201-
const allData = [];
202-
allData.push(
203-
...missingFields.map((item) => ({ ...item, fieldStatus: "Missing" }))
204-
);
205-
allData.push(
206-
...acceptedFields.map((item) => ({ ...item, fieldStatus: "Accepted" }))
207-
);
208-
allData.push(
209-
...suggestingFields.map((item) => ({ ...item, fieldStatus: "Suggested" }))
210-
);
211-
allData.push(
212-
...rejectedFields.map((item) => ({ ...item, fieldStatus: "Rejected" }))
213-
);
214-
allData.push(
215-
...emptyFields.map((item) => ({ ...item, fieldStatus: "Empty" }))
216-
);
217-
218-
const categoriesMap = {};
219-
220-
function addFieldToCategory(category, field) {
221-
if (!categoriesMap[category]) categoriesMap[category] = [];
222-
categoriesMap[category].push(field);
223-
}
22476

225-
allData.forEach((item) => {
226-
const category = item.fieldCategory || "general";
227-
addFieldToCategory(category, item);
77+
const item = {
78+
fieldName,
79+
fieldValue,
80+
fieldCategory,
81+
fieldSuggestion,
82+
fieldComment,
83+
fieldStatus,
84+
};
85+
allData.push(item);
86+
if (!categoriesMap[fieldCategory]) categoriesMap[fieldCategory] = [];
87+
categoriesMap[fieldCategory].push(item);
22888
});
22989

23090
// ---- Render: condensed, grouped overview of the review state ----------

0 commit comments

Comments
 (0)