Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 16 additions & 4 deletions app/api/ai/workspace/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ import { generateWorkspace } from "@/lib/ai/workspace-service";
import { WorkspaceAction } from "@/types/ai";
import { getPrimaryKey } from "@/lib/ai/key-manager";
import { trackMetric } from "@/lib/ai/metrics";
import { getTopicById } from "@/lib/content";

interface WorkspaceBody {
topic?: string;
module?: string;
topicId?: string;
subjectCode?: string;
action?: WorkspaceAction;
forceRefresh?: boolean;
Expand Down Expand Up @@ -163,9 +163,21 @@ export async function POST(request: NextRequest) {
try {
const body = (await request.json()) as WorkspaceBody;

const topic = body.topic?.trim();
const moduleTitle = body.module?.trim();
const topicId = body.topicId?.trim();
const subjectCode = body.subjectCode?.trim().toUpperCase();
const topicData =
topicId && subjectCode
? await getTopicById(
"common",
"semester-1",
subjectCode.toLowerCase(),
topicId
)
: null;

const topic = topicData?.topic.title;

const moduleTitle = topicData?.module.title;
Comment thread
imuniqueshiv marked this conversation as resolved.
const forceRefresh = body.forceRefresh ?? false;
const question = body.question?.trim();
const messages = body.messages || [];
Expand Down
113 changes: 63 additions & 50 deletions app/rgpv/[branch]/[semester]/[subject]/ai/page.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { BrainCircuit } from "lucide-react";
import WorkspaceChat from "@/components/ai/workspace-chat";
import { getTopicById } from "@/lib/content";

interface AIPageProps {
params: Promise<{
Expand All @@ -9,17 +10,24 @@ interface AIPageProps {
}>;

searchParams: Promise<{
topic?: string;
module?: string;
topicId?: string;
}>;
}

export default async function AIPage({ params, searchParams }: AIPageProps) {
const { branch, semester, subject } = await params;

const { topic, module } = await searchParams;
const { topicId } = await searchParams;

const isTopicMode = Boolean(topic) && Boolean(module);
const topicData = topicId
? await getTopicById(branch, semester, subject, topicId)
: null;

const topicTitle = topicData?.topic.title ?? "";

const moduleTitle = topicData?.module.title ?? "";

const isTopicMode = topicData !== null;

// Suggested prompts based on topic
const getSuggestedPrompts = (mainTopic: string) => {
Expand All @@ -41,51 +49,57 @@ export default async function AIPage({ params, searchParams }: AIPageProps) {
];
};

const suggestedPrompts =
isTopicMode && topic
? getSuggestedPrompts(topic)
: [
"Explain the concept",
"Generate notes",
"Create revision sheet",
"Generate exam questions",
"Explain with examples",
];
const suggestedPrompts = isTopicMode
? getSuggestedPrompts(topicTitle)
: [
"Explain the concept",
"Generate notes",
"Create revision sheet",
"Generate exam questions",
"Explain with examples",
];

// Initial prompts for WorkspaceChat
const initialPrompts =
isTopicMode && topic && module
? [
{
prompt: `Explain ${topic} in detail`,
action: "explain",
topic: topic,
module: module,
},
{
prompt: `Generate 5 mark answer on ${topic}`,
action: "generate",
topic: topic,
module: module,
},
{
prompt: `Create revision sheet for ${topic}`,
action: "summarize",
topic: topic,
module: module,
},
]
: [
{ prompt: "Explain the concept", action: "explain" },
{ prompt: "Generate notes", action: "generate" },
{ prompt: "Create revision sheet", action: "summarize" },
];
const initialPrompts = isTopicMode
? [
{
prompt: `Explain ${topicTitle} in detail`,
action: "explain",
topic: topicTitle,
module: moduleTitle,
},
{
prompt: `Generate 5 mark answer on ${topicTitle}`,
action: "generate",
topic: topicTitle,
module: moduleTitle,
},
{
prompt: `Create revision sheet for ${topicTitle}`,
action: "summarize",
topic: topicTitle,
module: moduleTitle,
},
]
: [
{
prompt: "Explain the concept",
action: "explain",
},
{
prompt: "Generate notes",
action: "generate",
},
{
prompt: "Create revision sheet",
action: "summarize",
},
];

// Build welcome message with topic context
const welcomeMessage =
isTopicMode && topic
? `Ask me anything about **${topic}**. I can help with explanations, examples, exam preparation, and suggest related topics based on your learning needs.`
: "Ask me anything about your subject. I can help with explanations, examples, and exam preparation.";
const welcomeMessage = isTopicMode
? `Ask me anything about **${topicTitle}**. I can help with explanations, examples, exam preparation, and suggest related topics based on your learning needs.`
: "Ask me anything about your subject. I can help with explanations, examples, and exam preparation.";

return (
<main className="min-h-screen bg-background">
Expand All @@ -106,11 +120,11 @@ export default async function AIPage({ params, searchParams }: AIPageProps) {
{isTopicMode ? (
<>
<h1 className="mt-6 text-3xl font-extrabold tracking-tight text-foreground md:text-5xl lg:text-6xl">
{topic}
{topicTitle}
</h1>

<p className="mt-4 text-base font-medium text-blue-600 dark:text-blue-400 md:text-lg">
{module}
{moduleTitle}
</p>

<p className="mt-3 text-sm text-muted-foreground md:text-base">
Expand Down Expand Up @@ -150,14 +164,13 @@ export default async function AIPage({ params, searchParams }: AIPageProps) {
<div className="mx-auto max-w-7xl px-6 lg:px-8">
<div className="grid gap-8 lg:grid-cols-[1fr_340px]">
{/* Main Chat Area */}
<div className="min-h-[500px] rounded-[2rem] border border-border bg-card p-4 shadow-sm md:p-6">
<div className="min-h-125 rounded-[2rem] border border-border bg-card p-4 shadow-sm md:p-6">
<WorkspaceChat
subjectCode={subject.toUpperCase()}
initialPrompts={initialPrompts}
welcomeMessage={welcomeMessage}
apiEndpoint="/api/ai/workspace"
topic={topic}
module={module}
topicId={topicId}
/>
</div>

Expand Down
3 changes: 2 additions & 1 deletion app/rgpv/[branch]/[semester]/[subject]/syllabus/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,14 @@ import { BookOpen } from "lucide-react";
import { getSyllabus, getPYQs } from "@/lib/content";
import { getQuestionsForModule } from "@/lib/content/question-mapper";
import ModuleCard from "@/components/syllabus/module-card";
import type { Topic } from "@/types/topic";

interface Module {
id: string;
number: number;
title: string;
hours: number;
topics?: string[];
topics?: Topic[];
questionIds?: string[];
predictedQuestionIds?: string[];
}
Expand Down
14 changes: 5 additions & 9 deletions components/ai/workspace-chat.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,13 @@ interface WorkspaceChatProps {
subjectCode: string;
initialPrompts?: Array<{
prompt: string;
topic?: string;
module?: string;
topicId?: string;
action?: string;
}>;
welcomeMessage?: string;
inputPlaceholder?: string;
apiEndpoint?: string;
topic?: string;
module?: string;
topicId?: string;
}

export default function WorkspaceChat({
Expand All @@ -35,8 +33,7 @@ export default function WorkspaceChat({
welcomeMessage = "Ask me anything about your subject. I can help with explanations, examples, and exam preparation.",
inputPlaceholder = "Type your question here...",
apiEndpoint = API_ENDPOINTS.AI_WORKSPACE,
topic,
module,
topicId,
}: WorkspaceChatProps) {
const [messages, setMessages] = useState<Message[]>([]);
const [input, setInput] = useState("");
Expand Down Expand Up @@ -95,8 +92,7 @@ export default function WorkspaceChat({
body: JSON.stringify({
question: content.trim(),
subjectCode,
topic,
module,
topicId,
messages: messages.map((m) => ({
role: m.role,
content: m.content,
Expand Down Expand Up @@ -223,7 +219,7 @@ export default function WorkspaceChat({
</p>
{initialPrompts.map((item, index) => {
const promptText =
item.prompt || `Ask about ${item.topic || "this topic"}`;
item.prompt || `Ask about ${item.topicId || "this topic"}`;

return (
<button
Expand Down
49 changes: 0 additions & 49 deletions components/navbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,55 +43,6 @@ const navLinks = [
},
];

function MagneticNavItem({
children,
isActive,
onClick,
href,
label,
}: {
children: React.ReactNode;
isActive: boolean;
onClick: (e: React.MouseEvent<HTMLAnchorElement>, href: string) => void;
href: string;
label: string;
}) {
const ref = useRef<HTMLAnchorElement>(null);
const [position, setPosition] = useState({ x: 0, y: 0 });

const handleMouse = (e: React.MouseEvent) => {
const { clientX, clientY } = e;
if (!ref.current) return;
const { height, width, left, top } = ref.current.getBoundingClientRect();
const middleX = clientX - (left + width / 2);
const middleY = clientY - (top + height / 2);
setPosition({ x: middleX * 0.1, y: middleY * 0.1 });
};

const reset = () => {
setPosition({ x: 0, y: 0 });
};

return (
<motion.a
ref={ref}
href={href}
onMouseMove={handleMouse}
onMouseLeave={reset}
onClick={(e) => onClick(e, href)}
animate={{ x: position.x, y: position.y }}
transition={{ type: "spring", stiffness: 200, damping: 20, mass: 0.1 }}
className="group relative flex h-[48px] w-[64px] cursor-pointer items-center justify-center rounded-[8px] outline-none transition-colors duration-200 hover:bg-white/[0.04]"
aria-label={label}
title={label}
>
<div className="relative z-10 flex items-center justify-center">
{children}
</div>
</motion.a>
);
}

export default function Navbar() {
const [isOpen, setIsOpen] = useState(false);
const [isMobileSearchOpen, setIsMobileSearchOpen] = useState(false);
Expand Down
13 changes: 7 additions & 6 deletions components/syllabus/module-card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
BookOpen,
CheckCircle2,
} from "lucide-react";
import type { Topic } from "@/types/topic";

interface MappedQuestion {
id: string;
Expand All @@ -25,7 +26,7 @@ interface ModuleCardProps {
number: number;
title: string;
hours: number;
topics?: string[];
topics?: Topic[];
questionIds?: string[];
predictedQuestionIds?: string[];
};
Expand Down Expand Up @@ -105,14 +106,14 @@ export default function ModuleCard({
<div className="flex flex-wrap gap-3">
{(module.topics || []).map((topic) => (
<Link
key={topic}
href={`/rgpv/${branch}/${semester}/${subject}/ai?module=${encodeURIComponent(
module.title
)}&topic=${encodeURIComponent(topic)}`}
key={topic.id}
href={`/rgpv/${branch}/${semester}/${subject}/ai?topicId=${encodeURIComponent(
topic.id
)}`}
onClick={(e) => e.stopPropagation()}
className="rounded-full border border-border bg-background px-4 py-2 text-sm font-medium text-foreground transition hover:border-blue-500/40 hover:text-blue-500"
>
{topic}
{topic.title}
</Link>
))}
</div>
Expand Down
Loading
Loading