Skip to content

Commit 0161eeb

Browse files
hubyrodclaude
andcommitted
Add loading animation and show current/previous week labels
Pulsing dot animation while waiting for the summary. Week dropdown now defaults to current week number with (current) and (previous) labels on the appropriate entries. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent de36329 commit 0161eeb

2 files changed

Lines changed: 65 additions & 9 deletions

File tree

app/summary/summary-form.tsx

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,28 @@ interface SummaryFormProps {
66
groups: string[];
77
}
88

9-
const WEEK_OPTIONS = [
10-
{ value: "current", label: "Current week" },
11-
{ value: "previous", label: "Previous week" },
12-
...Array.from({ length: 52 }, (_, i) => ({
13-
value: `week${String(i + 1).padStart(2, "0")}`,
14-
label: `Week ${String(i + 1).padStart(2, "0")}`,
15-
})),
16-
];
9+
function getISOWeek(date: Date): number {
10+
const d = new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate()));
11+
d.setUTCDate(d.getUTCDate() + 4 - (d.getUTCDay() || 7));
12+
const yearStart = new Date(Date.UTC(d.getUTCFullYear(), 0, 1));
13+
return Math.ceil(((d.getTime() - yearStart.getTime()) / 86400000 + 1) / 7);
14+
}
15+
16+
const currentWeek = getISOWeek(new Date());
17+
const previousWeek = getISOWeek(new Date(Date.now() - 7 * 86400000));
18+
19+
const WEEK_OPTIONS = Array.from({ length: 52 }, (_, i) => {
20+
const num = i + 1;
21+
const pad = String(num).padStart(2, "0");
22+
let label = `Week ${pad}`;
23+
if (num === currentWeek) label += " (current)";
24+
else if (num === previousWeek) label += " (previous)";
25+
return { value: `week${pad}`, label };
26+
});
1727

1828
export default function SummaryForm({ groups }: SummaryFormProps) {
1929
const [group, setGroup] = useState(groups[0] ?? "");
20-
const [week, setWeek] = useState("current");
30+
const [week, setWeek] = useState(`week${String(currentWeek).padStart(2, "0")}`);
2131
const [loading, setLoading] = useState(false);
2232
const [summary, setSummary] = useState("");
2333
const [postCount, setPostCount] = useState<number | null>(null);
@@ -109,6 +119,15 @@ export default function SummaryForm({ groups }: SummaryFormProps) {
109119
</div>
110120
</form>
111121

122+
{loading && (
123+
<div className="sum-loading">
124+
<div className="sum-loading-dots">
125+
<span /><span /><span />
126+
</div>
127+
<span>Summarizing...</span>
128+
</div>
129+
)}
130+
112131
{error && (
113132
<div className="sum-error">{error}</div>
114133
)}

app/summary/summary.css

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,43 @@
125125
cursor: not-allowed;
126126
}
127127

128+
/* Loading */
129+
.sum-loading {
130+
margin-top: 1.25rem;
131+
display: flex;
132+
align-items: center;
133+
gap: 0.625rem;
134+
font-family: var(--cfg-mono);
135+
font-size: 0.75rem;
136+
color: var(--cfg-text-muted);
137+
}
138+
139+
.sum-loading-dots {
140+
display: flex;
141+
gap: 0.25rem;
142+
}
143+
144+
.sum-loading-dots span {
145+
width: 6px;
146+
height: 6px;
147+
border-radius: 50%;
148+
background: var(--cfg-accent);
149+
animation: sum-dot-pulse 1.2s ease-in-out infinite;
150+
}
151+
152+
.sum-loading-dots span:nth-child(2) {
153+
animation-delay: 0.2s;
154+
}
155+
156+
.sum-loading-dots span:nth-child(3) {
157+
animation-delay: 0.4s;
158+
}
159+
160+
@keyframes sum-dot-pulse {
161+
0%, 80%, 100% { opacity: 0.2; transform: scale(0.8); }
162+
40% { opacity: 1; transform: scale(1); }
163+
}
164+
128165
/* Error */
129166
.sum-error {
130167
margin-top: 1rem;

0 commit comments

Comments
 (0)