Files
storkit/frontend/src/components/TodoPanel.tsx

176 lines
3.7 KiB
TypeScript
Raw Normal View History

interface StoryTodos {
storyId: string;
storyName: string | null;
items: string[];
}
interface TodoPanelProps {
todos: StoryTodos[];
isTodoLoading: boolean;
todoError: string | null;
lastTodoRefresh: Date | null;
onRefresh: () => void;
}
const formatTimestamp = (value: Date | null): string => {
if (!value) return "\u2014";
return value.toLocaleTimeString([], {
hour: "2-digit",
minute: "2-digit",
second: "2-digit",
});
};
export function TodoPanel({
todos,
isTodoLoading,
todoError,
lastTodoRefresh,
onRefresh,
}: TodoPanelProps) {
const totalTodos = todos.reduce((sum, s) => sum + s.items.length, 0);
return (
<div
style={{
border: "1px solid #333",
borderRadius: "10px",
padding: "12px 16px",
background: "#1f1f1f",
display: "flex",
flexDirection: "column",
gap: "8px",
}}
>
<div
style={{
display: "flex",
alignItems: "center",
justifyContent: "space-between",
gap: "12px",
}}
>
<div
style={{
display: "flex",
alignItems: "center",
gap: "8px",
}}
>
<div style={{ fontWeight: 600 }}>Story TODOs</div>
<button
type="button"
onClick={onRefresh}
disabled={isTodoLoading}
style={{
padding: "4px 10px",
borderRadius: "999px",
border: "1px solid #333",
background: isTodoLoading ? "#2a2a2a" : "#2f2f2f",
color: isTodoLoading ? "#777" : "#aaa",
cursor: isTodoLoading ? "not-allowed" : "pointer",
fontSize: "0.75em",
fontWeight: 600,
}}
>
Refresh
</button>
</div>
<div
style={{
display: "flex",
flexDirection: "column",
alignItems: "flex-end",
gap: "2px",
fontSize: "0.85em",
color: totalTodos === 0 ? "#7ee787" : "#aaa",
}}
>
<div>{totalTodos} remaining</div>
<div style={{ fontSize: "0.8em", color: "#777" }}>
Updated {formatTimestamp(lastTodoRefresh)}
</div>
</div>
</div>
{isTodoLoading ? (
<div style={{ fontSize: "0.85em", color: "#aaa" }}>
Loading story TODOs...
</div>
) : todoError ? (
<div
style={{
fontSize: "0.85em",
color: "#ff7b72",
display: "flex",
alignItems: "center",
gap: "8px",
flexWrap: "wrap",
}}
>
<span>{todoError}</span>
<button
type="button"
onClick={onRefresh}
disabled={isTodoLoading}
style={{
padding: "4px 10px",
borderRadius: "999px",
border: "1px solid #333",
background: isTodoLoading ? "#2a2a2a" : "#2f2f2f",
color: isTodoLoading ? "#777" : "#aaa",
cursor: isTodoLoading ? "not-allowed" : "pointer",
fontSize: "0.75em",
fontWeight: 600,
}}
>
Retry
</button>
</div>
) : totalTodos === 0 ? (
<div style={{ fontSize: "0.85em", color: "#7ee787" }}>
All acceptance criteria complete.
</div>
) : (
<div
style={{
display: "flex",
flexDirection: "column",
gap: "6px",
}}
>
{todos
.filter((s) => s.items.length > 0)
.map((story) => (
<div key={story.storyId}>
{todos.length > 1 && (
<div
style={{
fontSize: "0.8em",
color: "#777",
marginBottom: "4px",
}}
>
{story.storyName ?? story.storyId}
</div>
)}
<ul
style={{
margin: "0 0 0 16px",
padding: 0,
fontSize: "0.85em",
color: "#ccc",
}}
>
{story.items.map((item) => (
<li key={`todo-${story.storyId}-${item}`}>{item}</li>
))}
</ul>
</div>
))}
</div>
)}
</div>
);
}