feat: update ProjectE application
This commit is contained in:
@@ -59,7 +59,7 @@ export function HabitCard({ habit, onComplete }: HabitCardProps) {
|
||||
<span className="text-muted-foreground">Score</span>
|
||||
<span className="font-semibold">{habit.score}/100</span>
|
||||
</div>
|
||||
<Progress value={habit.score} className="h-2" />
|
||||
<Progress value={habit.score} className="h-2" aria-label={`${habit.name} score: ${habit.score} out of 100`} />
|
||||
</div>
|
||||
|
||||
{/* Frequency badge */}
|
||||
|
||||
@@ -89,7 +89,7 @@ export function HabitCompletionDialog({
|
||||
<Input
|
||||
id="quantity"
|
||||
type="number"
|
||||
placeholder="e.g., 30 minutes, 10 pages"
|
||||
placeholder="e.g., 30"
|
||||
value={quantity ?? ''}
|
||||
onChange={(e) =>
|
||||
setQuantity(e.target.value ? Number(e.target.value) : undefined)
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import CalendarHeatmap from 'react-calendar-heatmap';
|
||||
import type { Habit } from '@project-e/shared';
|
||||
import { Button } from '@/components/ui/button';
|
||||
|
||||
interface HeatmapValue {
|
||||
date: Date | string;
|
||||
@@ -16,6 +17,7 @@ interface HabitHeatmapProps {
|
||||
export function HabitHeatmap({ habits }: HabitHeatmapProps) {
|
||||
const [values, setValues] = useState<HeatmapValue[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
fetchHeatmapData();
|
||||
@@ -23,6 +25,8 @@ export function HabitHeatmap({ habits }: HabitHeatmapProps) {
|
||||
}, [habits.length]);
|
||||
|
||||
async function fetchHeatmapData() {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
try {
|
||||
const oneYearAgo = new Date();
|
||||
oneYearAgo.setFullYear(oneYearAgo.getFullYear() - 1);
|
||||
@@ -30,28 +34,30 @@ export function HabitHeatmap({ habits }: HabitHeatmapProps) {
|
||||
const response = await fetch(
|
||||
`/api/habit-logs?start=${oneYearAgo.toISOString()}`
|
||||
);
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
const logs: Array<{ logged_at: string }> = data.items || [];
|
||||
|
||||
// Group by date
|
||||
const byDate: Record<string, number> = {};
|
||||
logs.forEach((log) => {
|
||||
const date = new Date(log.logged_at).toISOString().split('T')[0];
|
||||
byDate[date] = (byDate[date] || 0) + 1;
|
||||
});
|
||||
|
||||
const heatmapValues: HeatmapValue[] = Object.entries(byDate).map(
|
||||
([date, count]) => ({
|
||||
date,
|
||||
count,
|
||||
})
|
||||
);
|
||||
|
||||
setValues(heatmapValues);
|
||||
if (!response.ok) {
|
||||
throw new Error('Habit logs could not be loaded.');
|
||||
}
|
||||
const data = await response.json();
|
||||
const logs: Array<{ logged_at: string }> = data.items || [];
|
||||
|
||||
// Group logs by calendar date.
|
||||
const byDate: Record<string, number> = {};
|
||||
logs.forEach((log) => {
|
||||
const date = new Date(log.logged_at).toISOString().split('T')[0];
|
||||
byDate[date] = (byDate[date] || 0) + 1;
|
||||
});
|
||||
|
||||
const heatmapValues: HeatmapValue[] = Object.entries(byDate).map(
|
||||
([date, count]) => ({
|
||||
date,
|
||||
count,
|
||||
})
|
||||
);
|
||||
|
||||
setValues(heatmapValues);
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch heatmap data:', error);
|
||||
setError('Habit activity could not be loaded.');
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
@@ -61,12 +67,26 @@ export function HabitHeatmap({ habits }: HabitHeatmapProps) {
|
||||
return <p className="text-sm text-muted-foreground">Loading...</p>;
|
||||
}
|
||||
|
||||
if (error) {
|
||||
return (
|
||||
<div className="space-y-3 text-sm text-muted-foreground" role="alert">
|
||||
<p>{error}</p>
|
||||
<Button variant="outline" size="sm" onClick={fetchHeatmapData}>Retry</Button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const today = new Date();
|
||||
const oneYearAgo = new Date();
|
||||
oneYearAgo.setFullYear(today.getFullYear() - 1);
|
||||
|
||||
return (
|
||||
<div className="overflow-x-auto">
|
||||
<p className="sr-only">
|
||||
Habit completion heatmap for the past year. {values.length === 0
|
||||
? 'No habit completions recorded.'
|
||||
: values.map((value) => `${new Date(value.date).toLocaleDateString()}: ${value.count} completion${value.count === 1 ? '' : 's'}.`).join(' ')}
|
||||
</p>
|
||||
<CalendarHeatmap
|
||||
startDate={oneYearAgo}
|
||||
endDate={today}
|
||||
|
||||
Reference in New Issue
Block a user