diff --git a/src/app/api/daily-note/route.ts b/src/app/api/daily-note/route.ts new file mode 100644 index 00000000..48ce9627 --- /dev/null +++ b/src/app/api/daily-note/route.ts @@ -0,0 +1,121 @@ +import { NextResponse ,NextRequest} from "next/server"; +import { supabaseAdmin } from "@/lib/supabase"; +import { getToken } from "next-auth/jwt"; + +export async function GET(req: NextRequest){ + + try{ + const token = await getToken({ + req, + secret: process.env.NEXTAUTH_SECRET, + }); + console.log(token) + + const userId = token?.githubId; + + if(!userId){ + return NextResponse.json( + {error: `Unauthorized`}, + {status: 400} + ); + } + + const today = new Date(); + const todayDate = today.toISOString().split("T")[0]; + + const yesterday = new Date(); + yesterday.setDate(yesterday.getDate() -1); + + const yesterdayDate = yesterday.toISOString().split("T")[0]; + + const {data : todayData} = await supabaseAdmin + .from("daily_notes") + .select("*") + .eq("user_id",userId) + .eq("date",todayDate) + .single(); + + const {data : yesterdayData} = await supabaseAdmin + .from("daily_notes") + .select("*") + .eq("user_id",userId) + .eq("date",yesterdayDate) + .single(); + console.log(todayData,yesterdayData); + return NextResponse.json({ + todayNote: todayData?.note || "", + yesterdayNote: yesterdayData?.note || "", + }); + + }catch(error){ + return NextResponse.json( + {error: `Something went wrong `}, + {status: 500} + ); + + } +} + +export async function POST(req: NextRequest) { + try{ + + const token = await getToken({ + req, + secret: process.env.NEXTAUTH_SECRET, + }); + console.log(token) + + const userId = token?.githubId; + const body = await req.json(); + console.log(body); + const { note} = body; + if(!userId){ + console.log("no user id"); + return NextResponse.json( + {error: "User id is requied "}, + {status: 400} + ); + } + if(!note || !note.trim()){ + console.log("no note "); + return NextResponse.json( + {error: "Note cannot be empty"}, + {status: 400} + ); + } + if(note.length >280){ + console.log("max len "); + return NextResponse.json( + {error: "Maximum 280 characters allowed"}, + {status: 400} + ); + } + const today = new Date().toISOString().split("T")[0]; + const {data, error} = await supabaseAdmin + .from("daily_notes") + .upsert({ + user_id: userId, + date: today, + note: note.trim(), + },{ + onConflict:"user_id,date" + }) + .select() + .single(); + if(error){ + console.log(error.message); + return NextResponse.json( + {error: error.message}, + {status:500} + ); + } + console.log(data); + return NextResponse.json(data); + }catch(error){ + console.log(error); + return NextResponse.json( + {error: "Something went wrong"}, + {status:500} + ); + } +} \ No newline at end of file diff --git a/src/app/dashboard/page.tsx b/src/app/dashboard/page.tsx index 08f0d988..6bf8254c 100644 --- a/src/app/dashboard/page.tsx +++ b/src/app/dashboard/page.tsx @@ -96,6 +96,19 @@ const PRReviewTrendChart = dynamic( () => import("@/components/PRReviewTrendChart"), { ssr: false, loading: () => }, ); +import WeeklySummaryCard from "@/components/WeeklySummaryCard"; +import { AIMentorWidget } from "@/components/AIMentorWidget"; +import ExportButton from "@/components/ExportButton"; +import Link from "next/link"; +import PersonalRecords from "@/components/PersonalRecords"; +import LocalCodingTime from "@/components/LocalCodingTime"; +import CodingTimeWidget from "@/components/CodingTimeWidget"; +import RecentActivity from "@/components/RecentActivity"; +import { authOptions } from "@/lib/auth"; +import { getServerSession } from "next-auth"; +import { redirect } from "next/navigation"; +import DailyNoteWidget from "@/components/DailyNoteWidget"; +import DashboardSSEProvider from "@/components/DashboardSSEProvider"; export default async function DashboardPage() { const session = await getServerSession(authOptions); @@ -170,6 +183,15 @@ export default async function DashboardPage() { +
+ + + +
+ +
+ {/* Row 2: PR metrics, community metrics, PR breakdown & Time Chart */} +
{/* ── Row 2: PR metrics + Community metrics ── */}
diff --git a/src/app/page.tsx b/src/app/page.tsx index 457788be..ef74e051 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -91,7 +91,6 @@ async function fetchRepoStats(): Promise { export default async function HomePage() { const session = await getServerSession(authOptions); - if (session) { redirect("/dashboard"); } diff --git a/src/components/DailyNoteWidget.tsx b/src/components/DailyNoteWidget.tsx new file mode 100644 index 00000000..77388c48 --- /dev/null +++ b/src/components/DailyNoteWidget.tsx @@ -0,0 +1,126 @@ +"use client"; + +import { useEffect, useState } from "react"; + +export default function DailyNoteWidget(){ + + const [loading,setLoading] = useState(false); + const [note,setNote] = useState(""); + const [yesterdayNote, setYesterdayNote] = useState(""); + const [showYesterday,setShowYesterday] = useState(false); + + useEffect(()=>{ + + const fetchNotes = async ()=>{ + + try{ + setLoading(true); + + const response = await fetch("/api/daily-note"); + + const data = await response.json(); + setNote(data.todayNote||""); + setYesterdayNote(data.yesterdayNote ||"" ); + + }catch(error){ + console.error("Failed to fetch notes"); + }finally{ + setLoading(false); + } + + }; + fetchNotes(); + },[]); + + +// auto save with debounce +const debounceFunction = async()=>{ + + if(!note.trim()) return ; + + try{ + await fetch("/api/daily-note",{ + method:"POST", + headers:{ + "Content-Type": "application/json", + }, + body: JSON.stringify({ + note, + }), + }); + }catch(error){ + console.error("Failed to save note"); + } +} +useEffect(()=>{ + + const timeout= setTimeout(()=>{ + debounceFunction(); + },500); + return ()=>clearTimeout(timeout); +},[note]); + + +if (loading) { + return ( +
+
+
+
+
+
+
+ ); +} + + return( + +
+
+

+ Today's Plan` ` +

+ +

Plan your coding session.

+
+
+ + +