Skip to content

Commit 06a1780

Browse files
feat: Display research history for completed/errored researches (#28)
* feat: Display research history for completed/errored researches Adds a collapsible section to the research details page to display the research history. This section is only visible when the research is completed or in an error state. The history is fetched from the `research_status_history` table and displayed using the `ResearchStatusHistoryDisplay` component. The section is collapsed by default. * feat: Display full research history in a compact view This commit introduces two main changes: 1. Fetches all research history entries for completed (status 2) or errored (status 3) researches in `src/index.tsx`. Previously, it was limited to the last 5 entries. 2. Updates the `ResearchStatusHistoryDisplay` component in `src/templates/layout.tsx` to support a compact display mode. - A new `isLiveView` prop is added. - When `isLiveView` is false (for completed/errored researches), the history is displayed without timestamps and with a smaller font for better readability of longer logs. - When `isLiveView` is true (for in-progress researches), the display remains the same, showing timestamps and regular font size for live updates. These changes allow you to view the complete history of a research job in a more condensed format once it's no longer in a live state. --------- Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
1 parent 50e60fd commit 06a1780

File tree

2 files changed

+77
-35
lines changed

2 files changed

+77
-35
lines changed

src/index.tsx

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -273,16 +273,21 @@ app.get("/details/:id", async (c) => {
273273
.replaceAll("```", "");
274274

275275
let statusHistory: any[] = [];
276-
if (resp.results && resp.results.status === 1) {
276+
if (resp.results && (resp.results.status === 1 || resp.results.status === 2 || resp.results.status === 3)) {
277277
const historyQb = new D1QB(c.env.DB);
278-
const historyResult = await historyQb
278+
const queryBuilder = historyQb
279279
.select<{ status_text: string; timestamp: string }>(
280280
"research_status_history",
281281
)
282282
.where("research_id = ?", id)
283-
.orderBy("timestamp desc")
284-
.limit(5)
285-
.all();
283+
.orderBy("timestamp desc");
284+
285+
// Conditionally apply limit only for status 1 (in progress)
286+
if (resp.results.status === 1) {
287+
queryBuilder.limit(5);
288+
}
289+
290+
const historyResult = await queryBuilder.all();
286291
statusHistory = historyResult.results || [];
287292
}
288293

src/templates/layout.tsx

Lines changed: 67 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -24,41 +24,67 @@ export const TopBar: FC = (props) => {
2424

2525
export const ResearchStatusHistoryDisplay: FC<{
2626
statusHistory: { status_text: string; timestamp: string }[];
27+
statusHistory: { status_text: string; timestamp: string }[];
28+
isLiveView?: boolean;
2729
}> = (props) => {
28-
if (!props.statusHistory || props.statusHistory.length === 0) {
29-
return <p class="text-sm text-gray-600">No status updates yet.</p>;
30+
const { statusHistory, isLiveView = false } = props; // Default isLiveView to false
31+
const title = isLiveView ? "Live Status Updates" : "Research History Log";
32+
33+
if (!statusHistory || statusHistory.length === 0) {
34+
return <p class={`text-gray-500 ${isLiveView ? 'text-sm' : 'text-xs'}`}>No status updates yet.</p>;
3035
}
3136

32-
return (
33-
// Removed mt-8, h3 title, and outer div. The parent container will handle margins.
34-
// The hx-swap will replace the content of the container, so the title should be outside.
35-
<ul class="space-y-3">
36-
{props.statusHistory.map((entry, index) => (
37-
<li
38-
key={index}
39-
class="flex items-start p-3 bg-white rounded-md border border-gray-200 hover:bg-gray-50 transition-colors duration-150"
40-
>
41-
<svg
42-
class="h-5 w-5 text-blue-500 mr-3 flex-shrink-0"
43-
fill="currentColor"
44-
viewBox="0 0 20 20"
45-
aria-hidden="true"
37+
// For non-live view, use a more compact rendering.
38+
// The existing live view (isLiveView = true path) should retain its original styling structure
39+
// but it's being moved into this conditional rendering.
40+
if (isLiveView) {
41+
return (
42+
<ul class="space-y-3">
43+
{statusHistory.map((entry, index) => (
44+
<li
45+
key={index}
46+
class="flex items-start p-3 bg-white rounded-md border border-gray-200 hover:bg-gray-50 transition-colors duration-150"
4647
>
47-
<path
48-
fill-rule="evenodd"
49-
d="M10 18a8 8 0 100-16 8 8 0 000 16zm-1.25-7.25a1.25 1.25 0 112.5 0 1.25 1.25 0 01-2.5 0z"
50-
clip-rule="evenodd"
51-
></path>
52-
</svg>
53-
<span class="text-sm text-gray-700">
54-
{entry.status_text}
55-
<span class="block text-xs text-gray-500 mt-1">
56-
{new Date(entry.timestamp).toLocaleString()}
48+
<svg
49+
class="h-5 w-5 text-blue-500 mr-3 flex-shrink-0"
50+
fill="currentColor"
51+
viewBox="0 0 20 20"
52+
aria-hidden="true"
53+
>
54+
<path
55+
fill-rule="evenodd"
56+
d="M10 18a8 8 0 100-16 8 8 0 000 16zm-1.25-7.25a1.25 1.25 0 112.5 0 1.25 1.25 0 01-2.5 0z"
57+
clip-rule="evenodd"
58+
></path>
59+
</svg>
60+
<span class="text-sm text-gray-700">
61+
{entry.status_text}
62+
<span class="block text-xs text-gray-500 mt-1">
63+
{new Date(entry.timestamp).toLocaleString()}
64+
</span>
5765
</span>
58-
</span>
59-
</li>
60-
))}
61-
</ul>
66+
</li>
67+
))}
68+
</ul>
69+
);
70+
}
71+
72+
// Compact view for historical logs (isLiveView = false)
73+
return (
74+
<div class={`border rounded-lg bg-gray-50 ${isLiveView ? "p-4 mt-4" : "p-3"}`}>
75+
{/* Title is only needed if it's not part of a collapsible section summary */}
76+
{/* <h3 class="text-md font-semibold mb-2">{title}</h3> */}
77+
<ul class={`space-y-1 ${isLiveView ? 'space-y-2' : ''}`}>
78+
{statusHistory.map((entry, index) => (
79+
<li key={index} class={`text-gray-700 ${isLiveView ? 'text-sm' : 'text-xs'}`}>
80+
{/* Timestamp only for live view, or consider a more compact format for non-live */}
81+
{/* {isLiveView && <span class="font-medium">{new Date(entry.timestamp).toLocaleString()}</span>} */}
82+
{/* {isLiveView ? ": " : ""} */}
83+
{entry.status_text}
84+
</li>
85+
))}
86+
</ul>
87+
</div>
6288
);
6389
};
6490

@@ -579,6 +605,7 @@ export const ResearchDetails: FC = (props) => {
579605
</h3>
580606
<ResearchStatusHistoryDisplay
581607
statusHistory={researchData.statusHistory}
608+
isLiveView={true}
582609
/>
583610
<div
584611
id={statusUpdateIndicatorId}
@@ -609,6 +636,16 @@ export const ResearchDetails: FC = (props) => {
609636
</div>
610637
)}
611638

639+
{/* Add this new section for research history */}
640+
{(researchData.status === 2 || researchData.status === 3) && researchData.statusHistory && researchData.statusHistory.length > 0 && (
641+
<details className="mt-4 mb-8">
642+
<summary className="cursor-pointer font-semibold text-gray-800 mb-3">Research History</summary>
643+
<div className="mt-2">
644+
<ResearchStatusHistoryDisplay statusHistory={researchData.statusHistory} isLiveView={false} />
645+
</div>
646+
</details>
647+
)}
648+
612649
{/* Report Content - direct child of main */}
613650
{researchData.status !== 1 && (
614651
<div className="prose prose-lg max-w-none">

0 commit comments

Comments
 (0)