diff --git a/app/api/getplancourses/route.ts b/app/api/getplancourses/route.ts index 684e5de..564a13d 100644 --- a/app/api/getplancourses/route.ts +++ b/app/api/getplancourses/route.ts @@ -16,7 +16,7 @@ export async function GET(request: NextRequest) { //@ts-ignore uuid: session?.user.id, }, - id: parseInt(planCookie), + //id: parseInt(planCookie), }, }, include: { @@ -25,3 +25,26 @@ export async function GET(request: NextRequest) { }); return NextResponse.json(courses, { status: 200 }); } + +export async function POST(request: NextRequest) { + const data = await request.json(); + + const course = data.course; + const plan = data.plan; + const session = await auth(); + + let courses = await prisma.coursePlan.update({ + where: { + id: parseInt(plan), + }, + data: { + courses: { + disconnect: { + id: course.id, + }, + }, + }, + }); + + return NextResponse.json(courses, { status: 200 }); +} diff --git a/app/calendar/page.tsx b/app/calendar/page.tsx index 41ed74e..49e12f6 100644 --- a/app/calendar/page.tsx +++ b/app/calendar/page.tsx @@ -1,9 +1,97 @@ import { title } from "@/components/primitives"; +import FullCalendar from "@fullcalendar/react"; +import timeGridPlugin from "@fullcalendar/timegrid"; // a plugin! +import CreatePlan from "@/components/CreatePlan"; +import moment from "moment"; +import Calendar from "@/components/Calendar"; +import prisma from "@/lib/prisma"; +import { auth } from "@/lib/auth"; +import { getPlanCookie } from "@/app/actions"; +import { BorderColor } from "@mui/icons-material"; +import { courseColors } from "@/components/primitives"; -export default function DocsPage() { +export default async function CalendarPage() { + async function getEvents() { + let output: any = []; + let planCookie: any = await getPlanCookie(); + + function generateColorFromName(name: string) { + let hash = 0; + + for (let i = 0; i < name.length; i++) { + hash += name.charCodeAt(i); + } + + return courseColors[hash % courseColors.length]; + } + + let courses; + if (planCookie) { + let plan = await prisma.coursePlan.findUnique({ + where: { + id: parseInt(planCookie), + }, + include: { + courses: true, + }, + }); + + courses = plan?.courses; + } + + if (courses) { + for (let i = 0; i < courses.length; i++) { + let color = generateColorFromName(courses[i].subject); + + let num: string = courses[i].courseReferenceNumber; + let meetingTimes = await prisma.meetingTime.findFirst({ + where: { + courseReferenceNumber: num, + }, + }); + output.push({ + classNames: "font-sans", + textColor: "white", + title: courses[i]?.courseTitle, + daColor: color, + subject: courses[i]?.subject, + color: "rgba(0,0,0,0)", + + borderWidth: "0px", + daysOfWeek: [ + meetingTimes?.sunday && "0", + meetingTimes?.monday && "1", + meetingTimes?.tuesday && "2", + meetingTimes?.wednesday && "3", + meetingTimes?.thursday && "4", + meetingTimes?.friday && "5", + meetingTimes?.saturday && "6", + ], + + startTime: + meetingTimes?.beginTime.slice(0, 2) + + ":" + + meetingTimes?.beginTime.slice(2), + endTime: + meetingTimes?.endTime.slice(0, 2) + + ":" + + meetingTimes?.endTime.slice(2), + }); + } + } + + return output; + } + + let events = await getEvents(); return ( -
-

Calendar

+
+
+ +
+
+ +
); } diff --git a/app/page.tsx b/app/page.tsx index af092e4..618cf59 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -45,7 +45,7 @@ export default async function Page(props: { async function Home(props: any) { return ( <> -
+
@@ -56,7 +56,7 @@ async function Home(props: any) {
-
+
diff --git a/components/Calendar.tsx b/components/Calendar.tsx new file mode 100644 index 0000000..5ba2ebc --- /dev/null +++ b/components/Calendar.tsx @@ -0,0 +1,56 @@ +"use client"; + +import FullCalendar from "@fullcalendar/react"; +import timeGridPlugin from "@fullcalendar/timegrid"; // a plugin! +import { Card } from "@nextui-org/react"; +import moment from "moment"; +import { generateColorFromName } from "./primitives"; + +export default function Calendar(props: any) { + function dayHeaderContent(args: any) { + return moment(args.date).format("ddd"); + } + + function renderEventContent(eventInfo: any) { + console.log(eventInfo); + return ( + + {/* + + +
+ */} + + {eventInfo.timeText} +
{eventInfo.event.title}
+ + ); + } + + return ( + + ); +} diff --git a/components/CourseCard.tsx b/components/CourseCard.tsx index 96cd80e..09f0665 100644 --- a/components/CourseCard.tsx +++ b/components/CourseCard.tsx @@ -11,6 +11,7 @@ import { Button, } from "@nextui-org/react"; import { tv } from "tailwind-variants"; +import { courseColors } from "@/components/primitives"; import { InstructorCard } from "./InstructorCard"; import AddIcon from "@mui/icons-material/Add"; @@ -28,18 +29,6 @@ const { role, } = card(); -const courseColors = [ - "#D1FAFF", - "#9BD1E5", - "#6A8EAE", - "#57A773", - "#157145", - "#1E2D2F", - "#C57B57", - "#9B489B", - "#4ECDC4", -]; - function generateColorFromName(name: string) { let hash = 0; diff --git a/components/CreatePlan.tsx b/components/CreatePlan.tsx index d2072bb..201fd05 100644 --- a/components/CreatePlan.tsx +++ b/components/CreatePlan.tsx @@ -11,6 +11,7 @@ import { Input, Button, Skeleton, + CardHeader, } from "@nextui-org/react"; import { Dropdown, @@ -27,6 +28,7 @@ import IosShareIcon from "@mui/icons-material/IosShare"; import { tv } from "tailwind-variants"; import axios from "axios"; import { Select, SelectItem } from "@nextui-org/react"; +import { useRouter } from "next/navigation"; import { InstructorCard } from "./InstructorCard"; import { usePathname } from "next/navigation"; @@ -35,13 +37,14 @@ import { useEffect, useState } from "react"; import useSWR from "swr"; import { setPlanCookie } from "@/app/actions"; import { useCookies } from "next-client-cookies"; - +import { courseColors } from "@/components/primitives"; export default function CreatePlan(props: any) { const cookies = useCookies(); + const router = useRouter(); const pathname = usePathname(); const { data: session, status } = useSession(); - const [coursePlanName, setCoursePlanName] = useState(); + const [coursePlanName, setCoursePlanName]: any = useState(""); const [selectedCoursePlan, setSelectedCoursePlan]: any = useState([]); const fetcher = (url: any) => fetch(url).then((r) => r.json()); @@ -64,7 +67,10 @@ export default function CreatePlan(props: any) { planName: coursePlanName, }) .then(function (response: any) { - setPlanCookie(response.id); + setCoursePlanName(""); + setSelectedCoursePlan([response.data.id]); + setPlanCookie(response.data.id); + //console.log(response); }) .catch(function (error) { @@ -72,6 +78,20 @@ export default function CreatePlan(props: any) { }); } } + async function removeCourseFromPlan(plan: any, course: any) { + await axios + .post("/api/getplancourses", { + plan: plan, + course: course, + }) + .then(function (response: any) { + //console.log(response); + router.refresh(); + }) + .catch(function (error) { + console.log(error); + }); + } async function deletePlan() { if (cookies.get("plan")) { axios @@ -81,6 +101,7 @@ export default function CreatePlan(props: any) { }, }) .then(function (response) { + setSelectedCoursePlan([]); //setPlanCookie("-55"); //console.log(response); }) @@ -96,18 +117,6 @@ export default function CreatePlan(props: any) { setPlanCookie(e.target.value); }; - const courseColors = [ - "#D1FAFF", - "#9BD1E5", - "#6A8EAE", - "#57A773", - "#157145", - "#1E2D2F", - "#C57B57", - "#9B489B", - "#4ECDC4", - ]; - function generateColorFromName(name: string) { let hash = 0; @@ -123,7 +132,7 @@ export default function CreatePlan(props: any) { setSelectedCoursePlan([cookies.get("plan")]); var objDiv: any = document.getElementById("scrollMe"); objDiv.scrollTop = objDiv.scrollHeight; - }, [cookies.get("plan")]); + }, [data]); const CoursesList = () => { let output: any = []; @@ -131,32 +140,46 @@ export default function CreatePlan(props: any) { return ; } if (data && !isLoading) { - for (let i = 0; i < data.length; i++) { - if ((data.id = selectedCoursePlan)) { - data[i].courses.map((course: any) => - output.push( - -
- - - {course.courseTitle} - - - ) - ); - } + let filtered_data = data.filter( + (course: any) => course.id == selectedCoursePlan[0] + ); + for (let i = 0; i < filtered_data.length; i++) { + filtered_data[i].courses.map((course: any) => + output.push( + removeCourseFromPlan(selectedCoursePlan, course)} + > +
+ + +
+ {course.courseTitle.replace(/&/g, "&")} +
+ +
+ + ) + ); } } @@ -165,7 +188,7 @@ export default function CreatePlan(props: any) { return ( <> - +
Create a Plan
@@ -173,19 +196,19 @@ export default function CreatePlan(props: any) { { setCoursePlanName(event.target.value); }} />
@@ -199,15 +222,14 @@ export default function CreatePlan(props: any) { {/* --------------------------------- or --------------------------- */} -
Select a Plan
-
+
Select a Plan
+
- + > +
-
- - - {props.course.courseTitle} - - - ); -} diff --git a/components/PlanCardList.tsx b/components/PlanCardList.tsx deleted file mode 100644 index c037251..0000000 --- a/components/PlanCardList.tsx +++ /dev/null @@ -1,40 +0,0 @@ -import { Course, CoursePlan } from "@prisma/client"; -import prisma from "../lib/prisma"; -import PlanCard from "./PlanCard"; - -async function getPlans(planId: any) { - const courses = await prisma.course.findMany({ - where: { - id: planId, - }, - }); - /* - const plans = await prisma.coursePlan.findMany({ - where: { - User: { - //@ts-ignore - uuid: session?.user?.id, - }, - }, - include: { - courses: true, - }, - });*/ - return courses; -} - -export async function PlanCardList(planId: any) { - const courses: Course[] = await getPlans(planId); - - return ( - <> -
- {courses?.map((course: any) => ( -
- -
- ))} -
- - ); -} diff --git a/components/primitives.ts b/components/primitives.ts index 66d31b2..5b16a76 100644 --- a/components/primitives.ts +++ b/components/primitives.ts @@ -54,3 +54,29 @@ export const subtitle = tv({ fullWidth: true, }, }); + +export const courseColors = [ + "#093145", + "#107896", + "#829356", + + "#C2571A", + "#9A2617", + "#636363", + "#087E8B", + "#590925", + "#034748", + "#19381F", + "#631D76", + "#4B4E6D", +]; + +export function generateColorFromName(name: string) { + let hash = 0; + + for (let i = 0; i < name.length; i++) { + hash += name.charCodeAt(i); + } + + return courseColors[hash % courseColors.length]; +} diff --git a/package-lock.json b/package-lock.json index 68e46ea..27a2d4a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,10 @@ "dependencies": { "@emotion/react": "^11.13.3", "@emotion/styled": "^11.13.0", + "@fullcalendar/core": "^6.1.15", + "@fullcalendar/daygrid": "^6.1.15", + "@fullcalendar/react": "^6.1.15", + "@fullcalendar/timegrid": "^6.1.15", "@mui/icons-material": "^6.1.5", "@nextui-org/button": "2.0.38", "@nextui-org/code": "2.0.33", @@ -31,6 +35,7 @@ "clsx": "2.1.1", "framer-motion": "~11.11.10", "intl-messageformat": "^10.7.3", + "moment": "^2.30.1", "next": "^15.0.1", "next-auth": "^4.24.10", "next-client-cookies": "^2.0.0", @@ -509,6 +514,57 @@ "tslib": "2" } }, + "node_modules/@fullcalendar/core": { + "version": "6.1.15", + "resolved": "https://registry.npmjs.org/@fullcalendar/core/-/core-6.1.15.tgz", + "integrity": "sha512-BuX7o6ALpLb84cMw1FCB9/cSgF4JbVO894cjJZ6kP74jzbUZNjtwffwRdA+Id8rrLjT30d/7TrkW90k4zbXB5Q==", + "license": "MIT", + "dependencies": { + "preact": "~10.12.1" + } + }, + "node_modules/@fullcalendar/core/node_modules/preact": { + "version": "10.12.1", + "resolved": "https://registry.npmjs.org/preact/-/preact-10.12.1.tgz", + "integrity": "sha512-l8386ixSsBdbreOAkqtrwqHwdvR35ID8c3rKPa8lCWuO86dBi32QWHV4vfsZK1utLLFMvw+Z5Ad4XLkZzchscg==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/preact" + } + }, + "node_modules/@fullcalendar/daygrid": { + "version": "6.1.15", + "resolved": "https://registry.npmjs.org/@fullcalendar/daygrid/-/daygrid-6.1.15.tgz", + "integrity": "sha512-j8tL0HhfiVsdtOCLfzK2J0RtSkiad3BYYemwQKq512cx6btz6ZZ2RNc/hVnIxluuWFyvx5sXZwoeTJsFSFTEFA==", + "license": "MIT", + "peerDependencies": { + "@fullcalendar/core": "~6.1.15" + } + }, + "node_modules/@fullcalendar/react": { + "version": "6.1.15", + "resolved": "https://registry.npmjs.org/@fullcalendar/react/-/react-6.1.15.tgz", + "integrity": "sha512-L0b9hybS2J4e7lq6G2CD4nqriyLEqOH1tE8iI6JQjAMTVh5JicOo5Mqw+fhU5bJ7hLfMw2K3fksxX3Ul1ssw5w==", + "license": "MIT", + "peerDependencies": { + "@fullcalendar/core": "~6.1.15", + "react": "^16.7.0 || ^17 || ^18 || ^19", + "react-dom": "^16.7.0 || ^17 || ^18 || ^19" + } + }, + "node_modules/@fullcalendar/timegrid": { + "version": "6.1.15", + "resolved": "https://registry.npmjs.org/@fullcalendar/timegrid/-/timegrid-6.1.15.tgz", + "integrity": "sha512-61ORr3A148RtxQ2FNG7JKvacyA/TEVZ7z6I+3E9Oeu3dqTf6M928bFcpehRTIK6zIA6Yifs7BeWHgOE9dFnpbw==", + "license": "MIT", + "dependencies": { + "@fullcalendar/daygrid": "~6.1.15" + }, + "peerDependencies": { + "@fullcalendar/core": "~6.1.15" + } + }, "node_modules/@humanfs/core": { "version": "0.19.0", "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.0.tgz", @@ -7897,6 +7953,15 @@ "node": ">=16 || 14 >=14.17" } }, + "node_modules/moment": { + "version": "2.30.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", + "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==", + "license": "MIT", + "engines": { + "node": "*" + } + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", diff --git a/package.json b/package.json index 46745b7..fc84f5d 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,10 @@ "dependencies": { "@emotion/react": "^11.13.3", "@emotion/styled": "^11.13.0", + "@fullcalendar/core": "^6.1.15", + "@fullcalendar/daygrid": "^6.1.15", + "@fullcalendar/react": "^6.1.15", + "@fullcalendar/timegrid": "^6.1.15", "@mui/icons-material": "^6.1.5", "@nextui-org/button": "2.0.38", "@nextui-org/code": "2.0.33", @@ -32,6 +36,7 @@ "clsx": "2.1.1", "framer-motion": "~11.11.10", "intl-messageformat": "^10.7.3", + "moment": "^2.30.1", "next": "^15.0.1", "next-auth": "^4.24.10", "next-client-cookies": "^2.0.0", diff --git a/styles/globals.css b/styles/globals.css index b5c61c9..ffb33bb 100644 --- a/styles/globals.css +++ b/styles/globals.css @@ -1,3 +1,57 @@ @tailwind base; @tailwind components; @tailwind utilities; + +/* override fullcalendar white shadow and border; add filters */ +.fc-timegrid-event-harness-inset .fc-timegrid-event { + border: none !important; + box-shadow: none !important; + filter: drop-shadow(4px 6px 10px #00000044) brightness(100%) !important; + -webkit-filter: drop-shadow(4px 6px 10px #00000044) brightness(100%); + font-family: var(--font-sans); +} + +.fc-timegrid-event-harness-inset .fc-timegrid-event:hover { + box-shadow: none !important; + filter: drop-shadow(4px 6px 10px #00000044) brightness(80%) !important; + -webkit-filter: drop-shadow(4px 6px 10px #00000044) brightness(80%); + -webkit-transition: all 0.5s ease; + -moz-transition: all 0.5s ease; + -o-transition: all 0.5s ease; + -ms-transition: all 0.5s ease; + transition: all 0.5s ease; + font-family: var(--font-sans); +} +.fc-day-today { + background-color: inherit !important; +} +.fc-col-header-cell-cushion { + color: white; +} + +.fc-col-header-cell-cushion:hover { + color: white; + font-family: var(--font-sans); +} + +.fc-col-header { + background-color: #151d2b; + font-family: var(--font-sans); +} + +.fc-event-main-frame { + cursor: pointer; +} + +.fc-event-title { + text-overflow: ellipsis; +} + +h2 { + font-size: 1.1em; +} + +.smallFont { + font-size: small; + margin: 0; +} diff --git a/swatscraper b/swatscraper index 26f1d82..32a5d97 160000 --- a/swatscraper +++ b/swatscraper @@ -1 +1 @@ -Subproject commit 26f1d82850014294d64734ae2f9c5b3429bd0eb7 +Subproject commit 32a5d975a8cbe906764c553846d220de1f0ed4ab