From 8d1fc7a15c7828c909da5a21f37a4eec5b735b73 Mon Sep 17 00:00:00 2001 From: Kavuru Buvanesh <169604812+Kavurubuvanesh@users.noreply.github.com> Date: Sat, 27 Jun 2026 12:24:02 +0530 Subject: [PATCH 1/2] feat(dashboard): add Gemini-powered weekly standup generator widget --- src/app/api/ai/roast/route.ts | 48 ++++++-------------------- src/components/WeeklyStandupWidget.tsx | 38 ++++++++++++++++++++ 2 files changed, 49 insertions(+), 37 deletions(-) create mode 100644 src/components/WeeklyStandupWidget.tsx diff --git a/src/app/api/ai/roast/route.ts b/src/app/api/ai/roast/route.ts index 2eb468a8c..8967e6d4e 100644 --- a/src/app/api/ai/roast/route.ts +++ b/src/app/api/ai/roast/route.ts @@ -1,53 +1,27 @@ import { NextResponse } from 'next/server'; import { GoogleGenerativeAI } from '@google/generative-ai'; -// Initialize the Google Generative AI SDK const genAI = new GoogleGenerativeAI(process.env.GEMINI_API_KEY || ''); -export async function POST(req: Request) { +export async function POST(request: Request) { try { - const body = await req.json(); - const { mode, stats } = body; + const { commits, prsMerged, issuesClosed } = await request.json(); - if (!mode || !stats) { - return NextResponse.json( - { error: 'Mode (roast/hype) and user stats are required.' }, - { status: 400 } - ); - } - - const model = genAI.getGenerativeModel({ model: 'gemini-2.5-flash' }); - - let systemInstruction = ''; - if (mode === 'roast') { - systemInstruction = `You are a hilariously brutal, sarcastic senior developer reviewing a junior's code stats. Roast their coding habits, commit streaks, or languages used based on the provided stats. Keep it strictly safe for work (SFW), funny, and under 3 sentences. No cursing.`; - } else if (mode === 'hype') { - systemInstruction = `You are the ultimate enthusiastic developer hype-man. Look at the user's coding stats and hype them up! Make them feel like a 10x coding god. Keep it energetic, modern, and under 3 sentences.`; - } else { - return NextResponse.json({ error: 'Invalid mode.' }, { status: 400 }); + if (!process.env.GEMINI_API_KEY) { + return NextResponse.json({ error: 'Gemini API key not configured.' }, { status: 500 }); } + const model = genAI.getGenerativeModel({ model: 'gemini-1.5-flash' }); const prompt = ` - ${systemInstruction} - - User Stats: - - Commits this week: ${stats.commits || 0} - - Top Languages: ${stats.languages?.join(', ') || 'None'} - - Merged PRs: ${stats.mergedPRs || 0} - - Failed Goals: ${stats.failedGoals || 0} - - Give me the ${mode}! + You are an expert developer assistant. Write a concise, professional weekly standup update based on: + Commits: ${commits}, PRs: ${prsMerged}, Issues: ${issuesClosed} + Rules: Under 4 sentences, professional tone, Slack-ready format, use emojis. DO NOT hallucinate details. `; const result = await model.generateContent(prompt); - const responseText = result.response.text(); + return NextResponse.json({ standup: result.response.text() }, { status: 200 }); - return NextResponse.json({ message: responseText.trim() }); } catch (error) { - console.error('Gemini API Error:', error); - return NextResponse.json( - { error: 'Failed to generate response. Please try again.' }, - { status: 500 } - ); + return NextResponse.json({ error: 'Failed to generate update.' }, { status: 500 }); } -} \ No newline at end of file +} diff --git a/src/components/WeeklyStandupWidget.tsx b/src/components/WeeklyStandupWidget.tsx new file mode 100644 index 000000000..8f45288f5 --- /dev/null +++ b/src/components/WeeklyStandupWidget.tsx @@ -0,0 +1,38 @@ +'use client'; +import { useState } from 'react'; + +export default function WeeklyStandupWidget({ commits, prsMerged, issuesClosed }: { commits: number, prsMerged: number, issuesClosed: number }) { + const [standup, setStandup] = useState(''); + const [isLoading, setIsLoading] = useState(false); + + const handleGenerate = async () => { + setIsLoading(true); + try { + const res = await fetch('/api/ai/standup', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ commits, prsMerged, issuesClosed }), + }); + const data = await res.json(); + if (res.ok) setStandup(data.standup); + } finally { + setIsLoading(false); + } + }; + + return ( +
{standup}
+{standup}
+{standup}
+