mirror of
https://github.com/Tony0410/quietthanks.git
synced 2026-05-24 21:31:41 +08:00
A calm, private gratitude and mood log built with Next.js 16, TypeScript, Tailwind CSS, and SQLite/Drizzle ORM. Features: - Quick check-in with autosave (800ms debounce) - Optional mood selector (5 levels) with accessibility labels - Optional tags with tap-to-add from recent - Timeline with weekly reflection card - Filters by mood, tag, and rough day - Export to Markdown and JSON - Dark mode default - Delete with undo toast - Docker deployment ready Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
66 lines
1.9 KiB
TypeScript
66 lines
1.9 KiB
TypeScript
"use client";
|
|
|
|
import type { EntryWithTags } from "@/lib/types";
|
|
import { isWithinWeek } from "@/lib/utils/date";
|
|
|
|
interface WeeklyReflectionProps {
|
|
entries: EntryWithTags[];
|
|
}
|
|
|
|
export function WeeklyReflection({ entries }: WeeklyReflectionProps) {
|
|
// Filter to last 7 days
|
|
const weekEntries = entries.filter((e) => isWithinWeek(e.date));
|
|
|
|
if (weekEntries.length === 0) {
|
|
return null;
|
|
}
|
|
|
|
// Count tag occurrences
|
|
const tagCounts = new Map<string, { name: string; count: number }>();
|
|
for (const entry of weekEntries) {
|
|
for (const tag of entry.tags) {
|
|
const existing = tagCounts.get(tag.id);
|
|
if (existing) {
|
|
existing.count++;
|
|
} else {
|
|
tagCounts.set(tag.id, { name: tag.name, count: 1 });
|
|
}
|
|
}
|
|
}
|
|
|
|
// Get top 3 tags
|
|
const topTags = Array.from(tagCounts.values())
|
|
.sort((a, b) => b.count - a.count)
|
|
.slice(0, 3);
|
|
|
|
// Generate summary
|
|
let summary = "";
|
|
if (topTags.length > 0) {
|
|
const tagNames = topTags.map((t) => capitalize(t.name));
|
|
if (tagNames.length === 1) {
|
|
summary = `You mentioned: ${tagNames[0]}.`;
|
|
} else if (tagNames.length === 2) {
|
|
summary = `You mentioned: ${tagNames[0]} and ${tagNames[1]}.`;
|
|
} else {
|
|
summary = `You mentioned: ${tagNames.slice(0, -1).join(", ")}, and ${tagNames[tagNames.length - 1]}.`;
|
|
}
|
|
}
|
|
|
|
return (
|
|
<div className="bg-surface border border-border rounded-xl p-4 mb-6">
|
|
<h2 className="text-sm font-medium text-muted mb-2">This week</h2>
|
|
<div className="flex items-baseline gap-4 mb-2">
|
|
<span className="text-2xl font-light">{weekEntries.length}</span>
|
|
<span className="text-sm text-muted">
|
|
{weekEntries.length === 1 ? "entry" : "entries"}
|
|
</span>
|
|
</div>
|
|
{summary && <p className="text-sm text-muted italic">{summary}</p>}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function capitalize(str: string): string {
|
|
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
}
|