Skip to content

Commit f9b4d8f

Browse files
committed
feat: add AI chat assistant with dedicated API route and glowing UI component.
1 parent 7255c0c commit f9b4d8f

2 files changed

Lines changed: 97 additions & 3 deletions

File tree

app/api/assistant/route.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,15 @@ Rules:
5858
- If you can't find the answer in the tools or page content, admit it:
5959
"I’m not sure about that based on the information publicly available on this site."
6060
61+
**UI Components you can use:**
62+
- **Buttons / CTAs:** Use markdown links with the title "button" or "cta". Example: \`[Join Now](/join "cta")\`
63+
- **Follow-up actions:** Use markdown links with the title "follow-up". This creates a clickable bubble for the user to quickly ask a follow up question. Example: \`[How do I register?](# "follow-up")\`
64+
- **Section Hyperlinks:** Use markdown links starting with '#' to jump to sections on the page. Example: \`[Go to Rules](#rules)\`
65+
- **Charts:** To show stats, output a markdown code block with the language \`chart\` containing a valid JSON array of objects with 'name' and 'value' properties. Example:
66+
\`\`\`chart
67+
[{"name": "2024", "value": 100}, {"name": "2025", "value": 300}]
68+
\`\`\`
69+
6170
**India Innovates 2026 Event Details:**
6271
- **What is it:** India's Biggest Tech Innovation Summit (civic tech, data, cybersecurity) where Code Meets Constitution. Hosted at Bharat Mandapam, Pragati Maidan, New Delhi. Exhibition-focused, not a classic overnight hackathon. **Bits&Bytes is the Executive Partner for this event, handling everything operational: participant groups, queries, social media creatives, docs/SOPs, college outreach, and full on-ground coordination.**
6372
- **Participation:** Open to all students, professionals, and tech enthusiasts from across India.

components/ui/glowing-ai-chat-assistant.tsx

Lines changed: 88 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import type { KeyboardEvent, ChangeEvent, MouseEvent as ReactMouseEvent } from "
55
import { useRouter } from "next/navigation"
66
import ReactMarkdown from "react-markdown"
77
import remarkGfm from "remark-gfm"
8+
import { BarChart, Bar, XAxis, Tooltip, ResponsiveContainer } from "recharts"
89

910
import { Mic, Send, Info, Bot, X, Trash } from "lucide-react"
1011

@@ -588,9 +589,93 @@ const FloatingAiAssistant: React.FC = () => {
588589
ol: ({ children }) => <ol className="my-1 list-decimal pl-4">{children}</ol>,
589590
li: ({ children }) => <li className="my-0.5 text-[0.75rem]">{children}</li>,
590591
strong: ({ children }) => <strong className="font-semibold">{children}</strong>,
591-
code: ({ children }) => (
592-
<code className="rounded bg-zinc-800 px-1 py-0.5 text-[0.7rem]">{children}</code>
593-
),
592+
a: ({ href, title, children, ...props }) => {
593+
if (title === "button" || title === "cta") {
594+
return (
595+
<a
596+
href={href}
597+
className="inline-flex mt-2 mb-1 w-full sm:w-auto items-center justify-center rounded-xl bg-[var(--brand-pink)] px-4 py-2 text-xs font-semibold text-white shadow-lg shadow-[#e45a92]/30 transition-all hover:scale-105 hover:shadow-xl hover:shadow-[#e45a92]/40 text-center"
598+
{...props}
599+
>
600+
{children}
601+
</a>
602+
)
603+
}
604+
if (title === "follow-up") {
605+
return (
606+
<button
607+
onClick={(e) => {
608+
e.preventDefault()
609+
const promptText = Array.isArray(children) ? children.join("") : String(children)
610+
handleQuickPrompt(promptText)
611+
}}
612+
className="block w-full mt-2 text-left rounded-xl border border-zinc-700/80 bg-zinc-800/50 px-3 py-2.5 text-xs text-zinc-300 transition-all hover:border-[#e45a92] hover:bg-zinc-800 hover:text-white"
613+
>
614+
{children}
615+
</button>
616+
)
617+
}
618+
if (href?.startsWith("#")) {
619+
return (
620+
<a href={href} className="text-[#e45a92] underline decoration-[#e45a92]/30 underline-offset-2 hover:decoration-[#e45a92] transition-colors" {...props}>
621+
{children}
622+
</a>
623+
)
624+
}
625+
return (
626+
<a href={href} className="text-emerald-400 hover:text-emerald-300 underline decoration-emerald-400/30 underline-offset-2 hover:decoration-emerald-400 transition-colors" target="_blank" rel="noreferrer" {...props}>
627+
{children}
628+
</a>
629+
)
630+
},
631+
code: ({ className, children, ...props }) => {
632+
const match = /language-(\w+)/.exec(className || "")
633+
const isChart = match && match[1] === "chart"
634+
635+
if (isChart) {
636+
try {
637+
const rawData = String(children).replace(/\n$/, "")
638+
const data = JSON.parse(rawData)
639+
if (Array.isArray(data)) {
640+
return (
641+
<div className="my-4 h-52 w-full rounded-xl bg-zinc-950/80 p-3 border border-zinc-800/80 shadow-inner">
642+
<ResponsiveContainer width="100%" height="100%">
643+
<BarChart data={data} margin={{ top: 10, right: 10, left: -20, bottom: 0 }}>
644+
<XAxis dataKey="name" fontSize={10} tickLine={false} axisLine={false} stroke="#a1a1aa" />
645+
<Tooltip
646+
cursor={{ fill: "#27272a", opacity: 0.4 }}
647+
contentStyle={{ backgroundColor: "#18181b", border: "1px solid #3f3f46", borderRadius: "8px", fontSize: "12px", color: "#f4f4f5" }}
648+
itemStyle={{ color: "#e45a92" }}
649+
/>
650+
<Bar dataKey="value" fill="#e45a92" radius={[4, 4, 0, 0]} maxBarSize={40} />
651+
</BarChart>
652+
</ResponsiveContainer>
653+
</div>
654+
)
655+
}
656+
} catch (e) {
657+
console.error("Failed to parse chart data", e)
658+
return (
659+
<div className="my-2 p-2 rounded bg-red-950/30 border border-red-900/50 text-red-400 text-xs">
660+
Error visualizing chart data.
661+
</div>
662+
)
663+
}
664+
}
665+
666+
const isInline = !match
667+
return (
668+
<code
669+
className={`${isInline
670+
? "rounded bg-zinc-800 px-1 py-0.5 text-[0.7rem]"
671+
: "block rounded-xl bg-zinc-950 p-3 text-[0.75rem] overflow-x-auto border border-zinc-800/80 text-zinc-300 mt-2 mb-2"
672+
} ${className || ""}`}
673+
{...props}
674+
>
675+
{children}
676+
</code>
677+
)
678+
},
594679
}}
595680
>
596681
{m.content || "..."}

0 commit comments

Comments
 (0)