diff --git a/app/api/wakatime/sync/route.ts b/app/api/wakatime/sync/route.ts index 81505dd..d47abe8 100644 --- a/app/api/wakatime/sync/route.ts +++ b/app/api/wakatime/sync/route.ts @@ -41,7 +41,14 @@ export async function GET(request: Request) { // Check last fetch const { data: existing } = await supabase .from("user_stats") - .select("*") + .select( + ` + *, + projects:user_projects ( + projects + ) + `, + ) .eq("user_id", user.id) .single(); @@ -84,14 +91,37 @@ export async function GET(request: Request) { .eq("id", user.id); } - const { data: result, error } = await supabase.from("user_stats").upsert({ - user_id: user.id, - total_seconds: Math.floor(data.data.total_seconds), - languages: data.data.languages, - operating_systems: data.data.operating_systems, - editors: data.data.editors, - last_fetched_at: new Date().toISOString(), + const { data: statsResult, error: statsError } = await supabase + .from("user_stats") + .upsert({ + user_id: user.id, + total_seconds: Math.floor(data.data.total_seconds), + languages: data.data.languages, + operating_systems: data.data.operating_systems, + editors: data.data.editors, + last_fetched_at: new Date().toISOString(), + }) + .select() + .single(); + + const { data: projectsResult, error: projectsError } = await supabase + .from("user_projects") + .upsert({ + user_id: user.id, + projects: data.data.projects, + last_fetched_at: new Date().toISOString(), + }) + .select() + .single(); + + const mergedResult = { + ...statsResult, + projects: projectsResult?.projects || [], + }; + + return NextResponse.json({ + success: !!statsResult && !statsError && !projectsError, + data: mergedResult, + error: statsError || projectsError, }); - - return NextResponse.json({ success: !!result, data: result, error }); } diff --git a/app/components/DevIcon.tsx b/app/components/DevIcon.tsx new file mode 100644 index 0000000..aee78b0 --- /dev/null +++ b/app/components/DevIcon.tsx @@ -0,0 +1,4 @@ +export const getDevIcon = (lang: string) => { + if (!lang) return null; + return ; +}; diff --git a/app/components/dashboard/Stats.tsx b/app/components/dashboard/Stats.tsx index d007a23..489d721 100644 --- a/app/components/dashboard/Stats.tsx +++ b/app/components/dashboard/Stats.tsx @@ -4,38 +4,23 @@ import { useEffect, useState } from "react"; import AOS from "aos"; import "devicon/devicon.min.css"; import { toast } from "react-toastify"; -import { - AreaChart, - Area, - XAxis, - YAxis, - CartesianGrid, - Tooltip, - ResponsiveContainer, - PieChart, - Pie, - Cell, -} from "recharts"; - -type StatsData = { +import CodingActivity from "./widgets/CodingActivity"; +import { formatHours } from "@/app/utils/time"; +import StatsCard from "./widgets/StatsCard"; +import LanguageDestribution from "./widgets/LanguageDestribution"; +import Editors from "./widgets/Editors"; +import OperatingSystem from "./widgets/OperatingSystem"; +import Projects from "./widgets/Projects"; + +export interface StatsData { total_seconds: number; daily_average: number; languages: { name: string; total_seconds: number; percent: number }[]; editors: { name: string; total_seconds: number; percent: number }[]; operating_systems: { name: string; total_seconds: number; percent: number }[]; + projects?: { name: string; total_seconds: number }[]; last_fetched_at?: string; -}; - -const CHART_COLORS = [ - "#6366f1", - "#8b5cf6", - "#a78bfa", - "#c084fc", - "#22d3ee", - "#34d399", - "#fbbf24", - "#f87171", -]; +} export default function Stats() { const [syncing, setSyncing] = useState(false); @@ -46,6 +31,7 @@ export default function Stats() { languages: [], editors: [], operating_systems: [], + projects: [], }); const fetchStats = () => { @@ -72,7 +58,7 @@ export default function Stats() { setTimeout(() => { AOS.refresh(); }, 200); - + // Trigger progress bar animation shortly after mounting/syncing const timer = setTimeout(() => { setAnimated(true); @@ -87,20 +73,6 @@ export default function Stats() { fetchStats(); }; - const formatHours = (seconds: number) => { - const safeSeconds = Number.isFinite(seconds) ? Math.max(0, seconds) : 0; - const totalMinutes = Math.round(safeSeconds / 60); - const hrs = Math.floor(totalMinutes / 60); - const mins = totalMinutes % 60; - if (hrs > 0) return `${hrs}h ${mins}m`; - return `${mins}m`; - }; - - const getLanguageIcon = (lang: string) => { - if (!lang) return null; - return ; - }; - const totalHoursFormatted = formatHours(stats.total_seconds); const avgDailyFormatted = stats.daily_average ? formatHours(stats.daily_average) @@ -111,11 +83,14 @@ export default function Stats() { const days = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]; const avgPerDay = stats.total_seconds / 7; const dailyData = days.map((day, i) => { - const varianceRaw = 0.4 + Math.sin(i * 1.5 + 1) * 0.6; + const varianceRaw = 0.6 + Math.sin(i * 1.5 + 1) * 0.4; const variance = Math.max(0, varianceRaw); return { day, - hours: Math.max(0, parseFloat(((avgPerDay * variance) / 3600).toFixed(1))), + hours: Math.max( + 0, + parseFloat(((avgPerDay * variance) / 3600).toFixed(1)), + ), }; }); @@ -125,8 +100,14 @@ export default function Stats() { value: l.total_seconds, })); - const totalCodingProgress = Math.min(100, (stats.total_seconds / (40 * 3600)) * 100); - const dailyAverageProgress = Math.min(100, ((stats.daily_average || stats.total_seconds / 7) / (8 * 3600)) * 100); + const totalCodingProgress = Math.min( + 100, + (stats.total_seconds / (40 * 3600)) * 100, + ); + const dailyAverageProgress = Math.min( + 100, + ((stats.daily_average || stats.total_seconds / 7) / (8 * 3600)) * 100, + ); const topLangProgress = stats.languages[0]?.percent || 0; const topEditorProgress = stats.editors[0]?.percent || 0; @@ -169,10 +150,14 @@ export default function Stats() { }, ]; + /** + * i think ive seen this code before... where was it... hmmm... oh yeah, i wrote it like 5 minutes ago in the StatsCard component. maybe i should just move this logic there? nah, its fine here for now, its not like its used anywhere else and hey btw, congrations for making it this far into the code! you must be really interested in how this dashboard works. if you have any suggestions or want to contribute, feel free to reach out or check the repo on github. happy coding! Ohhh your still reading this comment? well i guess i can share a little secret with you... the key to becoming a better developer is to always keep learning and building. don't be afraid to experiment, break things, and learn from your mistakes. also, remember to take breaks and have fun with coding! it's not just about writing code, it's about creating something awesome that can make a difference. so keep pushing forward, and who knows, maybe one day you'll be the one writing comments like this in your own code! hahaha - the DevPulse Team + */ + return (
- {card.label} -
- - {card.trend} - -- {card.value} -
-{card.sub}
- {/* Mini bar */} -No project data available.
++ {card.label} +
+ + {card.trend} + +{card.value}
+{card.sub}
+ {/* Mini bar */} +