Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Front pages #14

Merged
merged 7 commits into from
Nov 27, 2023
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
8 changes: 4 additions & 4 deletions components.json
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
{
"$schema": "https://ui.shadcn.com/schema.json",
"style": "default",
"rsc": false,
"rsc": true,
"tsx": true,
"tailwind": {
"config": "tailwind.config.js",
"css": "app/globals.css",
"css": "styles/globals.css",
"baseColor": "slate",
"cssVariables": false
"cssVariables": true
},
"aliases": {
"components": "@/components",
"utils": "@/lib/utils"
}
}
}
107 changes: 107 additions & 0 deletions components/AddCourseDialog.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
"use client";

import * as z from "zod";
import { zodResolver } from "@hookform/resolvers/zod";
import { useForm } from "react-hook-form";

import { Button } from "@/components/ui/button";
import {
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogClose,
DialogTitle,
} from "@/components/ui/dialog";
import {
Form,
FormControl,
FormDescription,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";

const formSchema = z.object({
courseName: z.string().min(2),
ListTAs: z.string().min(2),
});

export function AddCourseDialog() {
const form = useForm<z.infer<typeof formSchema>>({
resolver: zodResolver(formSchema),
defaultValues: {
courseName: "",
ListTAs: "",
},
});

async function onSubmit(values: z.infer<typeof formSchema>) {
let courseName = values.courseName;
// make API call to create course by api at /api/courses/[courseName]/create
let response = await fetch(
"http://localhost:3000/api/" + courseName + "/create",
{
method: "GET",
}
);

response = await response;

if (response.status == 200) {
console.log("Course created");
} else {
console.log("Course not created");
}
}

return (
<DialogContent className="sm:max-w-[425px]">
<DialogHeader>
<DialogTitle>Create new course</DialogTitle>
<DialogDescription>
Add course details and save when you&apos;re done.
</DialogDescription>
</DialogHeader>
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-8">
<FormField
control={form.control}
name="courseName"
render={({ field }) => (
<FormItem>
<FormLabel>Course Name</FormLabel>
<FormControl>
<Input placeholder="CS 1101" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="ListTAs"
render={({ field }) => (
<FormItem>
<FormLabel>List TAs</FormLabel>
<FormControl>
<Input placeholder="@username" {...field} />
</FormControl>
<FormDescription>Enter as comma-seperated list</FormDescription>
<FormMessage />
</FormItem>
)}
/>
<DialogClose asChild>
<DialogFooter>
<Button type="submit">Save changes</Button>
</DialogFooter>
</DialogClose>
</form>
</Form>
</DialogContent>
);
}
67 changes: 67 additions & 0 deletions components/Navbar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
"use client";
import Link from "next/link";
import { FC } from "react";
import { useCourseContext } from "../context/courseContext";
import { Course, CourseList, User } from "@/lib/types";

import { UserNav } from "./user-nav";
import { useRouter } from "next/router";

type NavbarProps = {
courseList: CourseList;
// user: User;
};

const Navbar: FC<NavbarProps> = ({ courseList }) => {
const { currentCourse, setCurrentCourse } = useCourseContext();

const pathName = useRouter();

const isActive = (route: string) => {
const pathList = pathName.asPath.split("/");
/*pathList = ["", dashboard] or ["", courses, [courseName], ...]*/
return route === pathList[1];
};
return (
<nav className="flex flex-col bg-navbarColor text-white h-screen">
<div className="p-4 mb-2 mt-4 flex flex-col items-center">
{/* <UserNav username={user.name} imageURL={user.image} /> */}
</div>
<div className="flex flex-col flex-grow">
{/* Links */}
<Link
href="/dashboard"
className={`p-4 block ${
isActive("dashboard") ? "bg-sidebarColor" : "hover:bg-sidebarColor"
}`}
onClick={() => setCurrentCourse("")}
>
Dashboard
</Link>
{courseList.map((course: Course) => (
<Link
key={course.id}
href={`/${course.name || currentCourse}`}
className={`p-4 block ${
isActive(`${course.name}`)
? "bg-sidebarColor"
: "hover:bg-sidebarColor"
}`}
onClick={() => setCurrentCourse(course.name)}
>
{course.name}
</Link>
))}
</div>
<div className="p-4">
{/* Exit or logout link */}
<Link href="/logout" className="hover:bg-gray-700 p-2 rounded-full">
{/* Replace with an appropriate icon */}
Exit
</Link>
</div>
</nav>
);
};

export default Navbar;
182 changes: 182 additions & 0 deletions components/QuizEditor.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
"use client";

import { Label } from "@/components/ui/label";
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
import { Textarea } from "@/components/ui/textarea";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";

import { ScrollArea } from "@/components/ui/scroll-area";

import { Dispatch, SetStateAction, useState } from "react";
import { Quiz, QuizQuestion } from "@/lib/types";

interface quizQuestionsProps {
questions: QuizQuestion[];
setQuestions: Dispatch<SetStateAction<QuizQuestion[]>>;
}

const QuizEditor: React.FC<quizQuestionsProps> = ({
questions,
setQuestions,
}) => {
// const [questions, setQuestions] = useState<QuizQuestion[]>(questions);

const defaultMCQQuestion: QuizQuestion = {
questionNum: questions.length + 1,
questionType: "MCQ",
question: "",
options: ["", "", "", ""], // Assuming a default of 4 options
answer: 1, // Assuming no answer selected by default
points: 10, // Default points value
};

const defaultFRQQuestion: QuizQuestion = {
questionNum: questions.length + 1,
questionType: "FRQ",
question: "",
options: [],
answer: "",
points: 80, // Default points value for FRQ
};

// Function to add a new MCQ question
const addMCQQuestion = () => {
setQuestions(
questions.concat({
...defaultMCQQuestion,
questionNum: questions.length + 1,
})
);
};

// Function to add a new FRQ question
const addFRQQuestion = () => {
setQuestions(
questions.concat({
...defaultFRQQuestion,
questionNum: questions.length + 1,
})
);
};

const deleteQuestion = (questionNum: number) => {
setQuestions(questions.filter((q) => q.questionNum !== questionNum));
};

// Function to handle reordering the question numbers after deletion
const reorderQuestions = (questions: QuizQuestion[]) => {
return questions.map((q, index) => ({
...q,
questionNum: index + 1, // Reset question numbers to be in order
}));
};

// Adjusted delete function that also reorders question numbers after deletion
const handleDeleteQuestion = (questionNum: number) => {
const updatedQuestions = questions.filter(
(q) => q.questionNum !== questionNum
);
setQuestions(reorderQuestions(updatedQuestions));
};

const handleQuestionChange = (index: number, newText: string) => {
const updatedQuestions = [...questions];
updatedQuestions[index].question = newText;
setQuestions(updatedQuestions);
};

const handleOptionChange = (
questionIndex: number,
optionIndex: number,
newOptionText: string
) => {
const updatedQuestions = [...questions];
updatedQuestions[questionIndex].options[optionIndex] = newOptionText;
setQuestions(updatedQuestions);
};

const addOption = (questionIndex: number) => {
const updatedQuestions = [...questions];
updatedQuestions[questionIndex].options.push("");
setQuestions(updatedQuestions);
};

const deleteOption = (questionIndex: number, optionIndex: number) => {
const updatedQuestions = [...questions];
updatedQuestions[questionIndex].options.splice(optionIndex, 1);
setQuestions(updatedQuestions);
};

const handleSubmit = () => {
/*
1. Need a popup to make sure the Ta finished editing.
2. update the quesitons
3. back to the previous page
*/
};

return (
<div className="hidden h-full flex-1 flex-col space-y-8 p-8 md:flex">
<ScrollArea className="h-[600px] w-[800px] rounded-md border p-4">
<div>
{questions.map((q, qIndex) => (
<div
className="flex flex-col rounded-md border m-5 p-4 "
key={q.questionNum}
>
{q.questionNum}.
<Input
value={q.question}
onChange={(e) => handleQuestionChange(qIndex, e.target.value)}
placeholder="Type your question here."
/>
{q.questionType == "MCQ" && (
<>
{q.options.map((option, oIndex) => (
<div
className="flex items-center space-x-2 p-2"
key={`option-${q.questionNum}-${oIndex}`}
>
<Input
value={option}
onChange={(e) =>
handleOptionChange(qIndex, oIndex, e.target.value)
}
/>
<Button
className="w-8 h-8 p-2"
onClick={() => deleteOption(qIndex, oIndex)}
>
-
</Button>
</div>
))}
<div className="flex justify-center">
<Button
className="w-8 h-8"
onClick={() => addOption(qIndex)}
>
+
</Button>
</div>
</>
)}
<Button
className="w-30 h-8 p-2"
onClick={() => handleDeleteQuestion(q.questionNum)}
>
Delete
</Button>
</div>
))}
</div>
<Button onClick={addMCQQuestion}>ADD MCQ</Button>
<Button onClick={addFRQQuestion}>ADD FRQ</Button>
</ScrollArea>
<Button onClick={handleSubmit}>submit</Button>
</div>
);
};

export default QuizEditor;
Loading
Loading