Skip to content

Commit

Permalink
Merge pull request #276 from NIAEFEUP/release/3.1.0
Browse files Browse the repository at this point in the history
Release 3.1.0
  • Loading branch information
tomaspalma authored Sep 3, 2024
2 parents aaf2af1 + 0c42075 commit e838337
Show file tree
Hide file tree
Showing 42 changed files with 1,122 additions and 662 deletions.
24 changes: 0 additions & 24 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@
"plausible-tracker": "^0.3.9",
"react": "^18.2.0",
"react-dom": "^18.1.0",
"react-hot-toast": "^2.4.0",
"react-router-dom": "^6.3.0",
"react-sortablejs": "^6.1.4",
"react-toastify": "^9.1.1",
Expand Down
24 changes: 11 additions & 13 deletions src/@types/index.d.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
enum lesson_type {
T = "T",
TP = "TP",
P = "P",
PL = "PL",
OT = "OT",
O = "O",
E = "E"
T = "T", // Ensino teórico
TP = "TP", // Ensino teórico-prático
P = "P", // Ensino prático
PL = "PL", // Ensino prático e laboratorial
OT = "OT", // Orientação tutorial
O = "O", // Outra
E = "E" // Estágio
}
// https://sigarra.up.pt/feup/pt/web_base.gera_pagina?p_pagina=h_ds_func_relatorios.querylist

/* Majors */
export type Major = {
Expand All @@ -27,6 +28,7 @@ export type CourseInfo = {
acronym: string,
name: string,
url: string,
hash: string,
classes?: Array<ClassInfo>
}

Expand Down Expand Up @@ -78,7 +80,7 @@ export type CourseOption = {
export type ClassDescriptor = {
classInfo: ClassInfo
courseInfo: CourseInfo
slotInfo?: SlotInfo
slotInfo?: SlotInfo // used for conflict calculation
}

export type ConflictInfo = {
Expand All @@ -88,11 +90,7 @@ export type ConflictInfo = {

export type Conflicts = Map<number, ConflictInfo>

export type Lesson = {
course: Course
schedule: CourseSchedule
}

export type ImportedCourses = {
[key: string]: string
}

22 changes: 12 additions & 10 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,26 @@
import { Toaster } from 'react-hot-toast'
import { Toaster } from './components/ui/toaster'
import { BrowserRouter, Routes, Route, Navigate } from 'react-router-dom'
import './app.css'
import CombinedProvider from './contexts/CombinedProvider'
import { AboutPage, TimeTableSchedulerPage, FaqsPage, NotFoundPage } from './pages'
import { getPath, config, plausible } from './utils'
import { getPath, config, dev_config, plausible } from './utils'
import Layout from './components/layout'

const configToUse = Number(import.meta.env.VITE_APP_PROD) ? config : dev_config

// Configures the path for pages.
const pages = [
{ path: getPath(config.paths.about), location: 'Sobre', element: AboutPage, liquid: true },
{ path: getPath(config.paths.planner), location: 'Horários', element: TimeTableSchedulerPage, liquid: true },
{ path: getPath(config.paths.faqs), location: 'FAQs', element: FaqsPage, liquid: true },
{ path: getPath(config.paths.notfound), location: 'NotFound', element: NotFoundPage, liquid: true },
{ path: getPath(configToUse.paths.about), location: 'Sobre', element: AboutPage, liquid: true },
{ path: getPath(configToUse.paths.planner), location: 'Horários', element: TimeTableSchedulerPage, liquid: true },
{ path: getPath(configToUse.paths.faqs), location: 'FAQs', element: FaqsPage, liquid: true },
{ path: getPath(configToUse.paths.notfound), location: 'NotFound', element: NotFoundPage, liquid: true },
]

const redirects = [
{ from: "/", to: getPath(config.paths.planner) },
{ from: config.pathPrefix, to: getPath(config.paths.planner) },
{ from: config.pathPrefix.slice(0, -1), to: getPath(config.paths.planner) },
{ from: getPath(config.paths.home), to: getPath(config.paths.about) },
{ from: "/", to: getPath(configToUse.paths.planner) },
{ from: configToUse.pathPrefix, to: getPath(configToUse.paths.planner) },
{ from: configToUse.pathPrefix.slice(0, -1), to: getPath(configToUse.paths.planner) },
{ from: getPath(configToUse.paths.home), to: getPath(configToUse.paths.about) },
]

const App = () => {
Expand Down
23 changes: 23 additions & 0 deletions src/api/backend.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,28 @@ const getInfo = async () => {
return await apiRequest('/info/')
}


/**
* Retrieves hashes for a list of course unit IDs.
* @param ids Array of course unit IDs
* @returns Object mapping course unit IDs to their hashes
*/
const getCourseUnitHashes = async (ids: number[]) => {
if (ids.length === 0) return {};

try {
const queryString = ids.join(',');
const response = await fetch(`${BACKEND_URL}/course_unit/hash?ids=${queryString}`);
if (!response.ok) throw new Error('Network response was not ok');
return await response.json();
} catch (error) {
console.error('Error fetching course unit hashes:', error);
throw error;
}
};



const api = {
getMajors,
getCourses,
Expand All @@ -108,6 +130,7 @@ const api = {
getCoursesClasses,
getCourseUnit,
getInfo,
getCourseUnitHashes,
BACKEND_URL
}

Expand Down
10 changes: 9 additions & 1 deletion src/components/faqs/PlannerFaqs.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import classNames from 'classnames'
import { Transition, Disclosure } from '@headlessui/react'
import { ChevronUpIcon } from '@heroicons/react/24/outline'
import { AnalyticsTracker } from '../../utils/AnalyticsTracker'

const PlannerFaqs = () => {
const data = [
Expand Down Expand Up @@ -158,7 +159,14 @@ const PlannerFaqs = () => {
as="div"
defaultOpen={faqIdx === 0}
key={`planner-faq-${faqIdx}`}
className="rounded-2xl bg-white p-3 dark:bg-dark"
className={`rounded-2xl bg-white p-3 dark:bg-dark faq-disclosure-${faqIdx}`}
onClick={() => {
const disclosure = document.querySelector(`.faq-disclosure-${faqIdx}`);
const isOpen = disclosure?.getAttribute('data-headlessui-state') !== 'open';
if (isOpen && faq.question.type === 'span') {
AnalyticsTracker.trackFaq(faq.question.props.children)
}
}}
>
{({ open }) => (
<>
Expand Down
10 changes: 10 additions & 0 deletions src/components/layout/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ import {
} from '@heroicons/react/24/outline'
import { LogoNIAEFEUPImage } from '../../images'
import { getPath, config } from '../../utils'
import useVerifyCourseUnitHashes from '../../hooks/useVerifyCourseUnitHashes'
import CourseContext from '../../contexts/CourseContext'
import { useContext, useEffect } from 'react'

const navigation = [
{
Expand All @@ -34,6 +37,9 @@ type Props = {
}

const Header = ({ siteTitle, location }: Props) => {
const { pickedCourses,} =useContext(CourseContext);
const { mismatchedMap } = useVerifyCourseUnitHashes(pickedCourses);

return (
<Disclosure
as="nav"
Expand Down Expand Up @@ -82,6 +88,8 @@ const Header = ({ siteTitle, location }: Props) => {
</div>

<div className="hidden self-center md:inline-flex">


<DarkModeSwitch />
</div>
</div>
Expand Down Expand Up @@ -125,7 +133,9 @@ const Hamburger = ({ open }: HamburgerProps) => (
</Link>

<div className="flex items-center space-x-1">

<DarkModeSwitch />

<Disclosure.Button className="group text-gray-800 transition duration-200 ease-in dark:text-white md:hidden">
<span className="sr-only">Open nav menu</span>
{open ? (
Expand Down
57 changes: 31 additions & 26 deletions src/components/planner/Schedule.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
import '../../styles/schedule.css'
import classNames from 'classnames'
import { useEffect, useMemo, useRef, useState } from 'react'
import { ScheduleGrid, } from './schedules'
import { LessonBox, ScheduleGrid, } from './schedules'
import ToggleScheduleGrid from './schedule/ToggleScheduleGrid'
import PrintSchedule from './schedule/PrintSchedule'
import { useContext } from 'react'
import ClassBox from './schedules/ClassBox'
import SlotBox from './schedules/SlotBox'
import ScheduleTypes from './ScheduleType'
import { ClassDescriptor } from '../../@types'
import { ClassDescriptor, SlotInfo } from '../../@types'
import CourseContext from '../../contexts/CourseContext'
import MultipleOptionsContext from '../../contexts/MultipleOptionsContext'
import { useShowGrid } from '../../hooks'
import { maxHour, minHour, convertWeekdayLong, convertHour } from '../../utils'
import SlotBoxes from './schedules/SlotBoxes'

const dayValues = Array.from({ length: 6 }, (_, i) => i)
const hourValues = Array.from({ length: maxHour - minHour + 1 }, (_, i) => minHour + i)
Expand All @@ -21,6 +22,7 @@ const Schedule = () => {
const { multipleOptions, selectedOption } = useContext(MultipleOptionsContext)

const [classes, setClasses] = useState<ClassDescriptor[]>([])
const [slots, setSlots] = useState<SlotInfo[]>([])
const scheduleRef = useRef(null)

useEffect(() => {
Expand All @@ -43,10 +45,11 @@ const Schedule = () => {
}

setClasses(newClasses)
setSlots(newClasses.map((newClass) => newClass.classInfo.slots).flat())
}, [multipleOptions, pickedCourses, selectedOption])

// TODO: Improvements by functional programming
const slotTypes = useMemo(() => {
const slotTypes: string[] = useMemo(() => {
let aux = new Set()

for (const currentClass of classes) {
Expand All @@ -60,6 +63,18 @@ const Schedule = () => {
return Array.from(aux) as string[]
}, [classes])

const slotsOrderedByDay = (slots: Array<SlotInfo>): Array<SlotInfo> => {
return slots.sort((slot1, slot2) => {
if (slot1.day === slot2.day) {
return slot1.start_time - slot2.start_time;
}

return slot1.day - slot2.day
});
}

// Bottom Bar Configurations
const [hiddenLessonsTypes, setHiddenLessonsTypes] = useState<string[]>([])
const [showGrid, setShowGrid] = useShowGrid()

return (
Expand Down Expand Up @@ -91,26 +106,20 @@ const Schedule = () => {
<div className={classNames('schedule-grid-wrapper', showGrid ? 'show-grid-yes' : 'show-grid-no')}>
<ScheduleGrid showGrid={showGrid} />
<div className="schedule-classes">
{classes
.filter((c) => c.classInfo !== undefined)
.map((c) => (
<ClassBox
key={`course[${c.courseInfo.id}]-class[${c.classInfo.id}]`}
courseInfo={c.courseInfo}
classInfo={c.classInfo ?? null}
classes={classes}
/>
))}
<SlotBoxes
slots={slots}
hiddenLessonsTypes={hiddenLessonsTypes}
classes={classes}
/>
</div>
</div>
</div>
</div>


{/* Bottom bar */}
<div className="flex justify-between gap-5 pl-16">
<div className="flex flex-wrap gap-4 gap-y-1 text-sm text-gray-600 dark:text-white 2xl:gap-y-2 2xl:text-base">
<ScheduleTypes types={slotTypes} />
<ScheduleTypes types={slotTypes} hiddenLessonsTypes={hiddenLessonsTypes} setHiddenLessonsTypes={setHiddenLessonsTypes} />
</div>
<div className="flex gap-2">
<ToggleScheduleGrid showGridHook={[showGrid, setShowGrid]} />
Expand All @@ -124,16 +133,12 @@ const Schedule = () => {
<div className="flex w-full items-center justify-start gap-2" key={`responsive-lesson-row-${""}`}>
<div className="h-full w-1 rounded bg-primary" />
<div className="flex w-full flex-row flex-wrap items-center justify-start gap-2">
{classes
.filter((c) => c.classInfo !== undefined)
.map((c) => (
<ClassBox
key={`course[${c.courseInfo.id}]-class[${c.classInfo.id}]`}
courseInfo={c.courseInfo}
classInfo={c.classInfo ?? null}
classes={classes}
/>
))}
{slots.length === 0 && <p>Ainda não foram selecionadas turmas!</p>}
<SlotBoxes
slots={slotsOrderedByDay(slots)}
classes={classes}
hiddenLessonsTypes={hiddenLessonsTypes}
/>
</div>
</div>
</div>
Expand Down
Loading

0 comments on commit e838337

Please sign in to comment.