Skip to content

Commit

Permalink
Merge pull request #340 from NIAEFEUP/feature/switchSidebarPosition
Browse files Browse the repository at this point in the history
Feature: Button that allows the user to switch the side where the sidebar is displayed
  • Loading branch information
tomaspalma authored Mar 5, 2025
2 parents 18a61f0 + fd5b5be commit a3377f1
Show file tree
Hide file tree
Showing 7 changed files with 125 additions and 46 deletions.
2 changes: 1 addition & 1 deletion src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ const App = () => {
<Layout location={page.location} title={page.location} liquid={page.liquid}>
<div>
<page.element />
<Toaster />
<Toaster/>
</div>
</Layout>
}
Expand Down
42 changes: 42 additions & 0 deletions src/components/layout/SidebarPosition.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { createContext, useContext, useState } from "react";
import PropTypes from "prop-types";

type SidebarContextType = {
sidebarPosition: 'left' | 'right';
toggleSidebarPosition: () => void;
};

const SidebarContext = createContext<SidebarContextType | undefined>(undefined);

export const SidebarProvider = ({ children }: { children: JSX.Element }) => {
const [sidebarPosition, setSidebarPosition] = useState<'left' | 'right'>(() => {
const storedPosition = window.localStorage.getItem("sidebar-position");
return storedPosition === "left" || storedPosition === "right" ? storedPosition : "right";
});

const toggleSidebarPosition = () => {
setSidebarPosition((prev) => {
const newPosition = prev === "right" ? "left" : "right";
window.localStorage.setItem("sidebar-position", newPosition);
return newPosition;
});
};

return (
<SidebarContext.Provider value={{ sidebarPosition, toggleSidebarPosition }}>
{children}
</SidebarContext.Provider>
);
};

SidebarProvider.propTypes = {
children: PropTypes.node.isRequired,
}

export const useSidebarContext = () => {
const context = useContext(SidebarContext);
if (!context) {
throw new Error('useSidebarContext must be used within a SidebarProvider')
}
return context
}
47 changes: 45 additions & 2 deletions src/components/planner/Sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,43 @@ import OptionsController from './sidebar/OptionsController'
import SelectedOptionController from './sidebar/SelectedOptionController'
import CoursesController from './sidebar/CoursesController'
import MultipleOptionsContext from '../../contexts/MultipleOptionsContext'
import { useSidebarContext } from '../layout/SidebarPosition'
import { ArrowsRightLeftIcon, TrashIcon } from '@heroicons/react/24/outline'
import { Button } from '../ui/button'

/**
* Sidebar with all the main schedule interactions
*/
const Sidebar = () => {
const { multipleOptions, selectedOption } = useContext(MultipleOptionsContext);
const { multipleOptions, selectedOption, setMultipleOptions } = useContext(MultipleOptionsContext);
const { toggleSidebarPosition } = useSidebarContext();

const noClassesPicked = !multipleOptions[selectedOption]?.course_options.some(
(option) => option.picked_class_id !== null
);

const eraseClasses = () => {
const currentOption = multipleOptions[selectedOption];

const updatedCourseOptions = currentOption.course_options.map(courseOption => ({
...courseOption,
picked_class_id: null,
locked: false,
}));

const updatedMultipleOptions = [...multipleOptions];
updatedMultipleOptions[selectedOption] = {
...currentOption,
course_options: updatedCourseOptions,
};

setMultipleOptions(updatedMultipleOptions);
};


return (
<div className="lg:min-h-adjusted order-2 col-span-12 flex min-h-min flex-col justify-between rounded-md bg-lightest px-3 py-3 dark:bg-dark lg:col-span-3 2xl:px-4 2xl:py-4">
<div className="space-y-2">
<div className="flex-grow space-y-2">
<div className="relative flex flex-row flex-wrap items-center justify-center gap-x-2 gap-y-2 lg:justify-start">
<SessionController />
<OptionsController />
Expand All @@ -23,6 +50,22 @@ const Sidebar = () => {
<CoursesController />
</div>
</div>
<footer className="mt-4 gap-x-1 border-white-300 pt-3 text-center flex items-end justify-end">
<Button
onClick={eraseClasses}
variant="icon"
className={`bg-lightish text-darkish gap-1.5 ${noClassesPicked ? 'opacity-50 pointer-events-none' : ''}`}
>
<TrashIcon className="h-5 w-5" />
<span>Limpar</span>
</Button>
<button title='Mudar o lado da Sidebar'
onClick={toggleSidebarPosition}
className="hidden md:flex items-center justify-center gap-2 w-[48px] h-[40px] bg-primary hover:opacity-80 dark:text-white rounded-md p-1 text-gray text-sm"
>
<ArrowsRightLeftIcon className="h-5 w-5 text-white dark:text-white" />
</button>
</footer>
</div>
)
}
Expand Down
32 changes: 0 additions & 32 deletions src/components/planner/sidebar/CoursesController.tsx
Original file line number Diff line number Diff line change
@@ -1,34 +1,14 @@
import { useContext } from 'react'
import { PlannerClassSelector }from './CoursesController/PlannerClassSelector'
import CourseContext from '../../../contexts/CourseContext'
import MultipleOptionsContext from '../../../contexts/MultipleOptionsContext'
import { TrashIcon } from "@heroicons/react/24/outline"
import { NoMajorSelectedSVG } from '../../svgs'
import { Button } from '../../ui/button'

const CoursesController = () => {
const { pickedCourses, setUcsModalOpen } = useContext(CourseContext);
const { multipleOptions, selectedOption, setMultipleOptions } = useContext(MultipleOptionsContext);

const noCoursesPicked = pickedCourses.length === 0;

const eraseClasses = () => {
const currentOption = multipleOptions[selectedOption];

const updatedCourseOptions = currentOption.course_options.map(courseOption => ({
...courseOption,
picked_class_id: null,
locked: false,
}));

const updatedMultipleOptions = [...multipleOptions];
updatedMultipleOptions[selectedOption] = {
...currentOption,
course_options: updatedCourseOptions,
};

setMultipleOptions(updatedMultipleOptions);
};

return (
<div className={`flex ${noCoursesPicked ? 'h-max justify-center' : ''} w-full flex-col gap-4 px-0 py-2`}>
Expand All @@ -53,18 +33,6 @@ const CoursesController = () => {
))
)}

{!noCoursesPicked && (
<div className="mt-4 flex justify-end">
<Button
onClick={eraseClasses}
variant="icon"
className="bg-lightish text-darkish gap-1.5"
>
<TrashIcon className="h-5 w-5" />
<span>Limpar</span>
</Button>
</div>
)}

</div>
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,4 +74,4 @@ const ClassItem = ({ course_id, classInfo, onSelect, onMouseEnter, onMouseLeave
)
}

export default ClassItem
export default ClassItem
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ import CoursePickerContext from '../../../../contexts/coursePicker/CoursePickerC

//TODO: absolute imports with @


const CoursePicker = () => {

const {
coursesStorage,
setCoursesInfo,
Expand Down
44 changes: 34 additions & 10 deletions src/pages/TimeTableSelector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@ import { useEffect, useContext } from 'react'
import { Sidebar } from '../components/planner'
import { Major } from '../@types'
import MajorContext from '../contexts/MajorContext'
import { useSidebarContext } from '../components/layout/SidebarPosition'
import { SidebarProvider } from '../components/layout/SidebarPosition'
import PlannerSchedule from '../components/planner/schedule/PlannerSchedule'

const TimeTableSelectorPage = () => {
const {setMajors} = useContext(MajorContext);
const { setMajors } = useContext(MajorContext);

// fetch majors when component is ready
useEffect(() => {
Expand All @@ -17,17 +19,39 @@ const TimeTableSelectorPage = () => {
}, [])

return (
<div className="grid w-full grid-cols-12 gap-x-4 gap-y-4 px-4 py-4">
{/* Schedule Preview */}
<div className="lg:min-h-adjusted order-1 col-span-12 min-h-min rounded bg-lightest px-3 py-3 dark:bg-dark lg:col-span-9 2xl:px-5 2xl:py-5">
<div className="h-full w-full">
<PlannerSchedule />
</div>
</div>
<SidebarProvider>
<Content />
</SidebarProvider>
);
};

const Content = () => {
const { sidebarPosition } = useSidebarContext();

<Sidebar />
return (
<div className="grid w-full grid-cols-12 gap-x-4 gap-y-4 px-4 py-4">
{sidebarPosition === 'left' ? (
<>
<div className='col-span-12 lg:col-span-3 min-h'>
<Sidebar />
</div>
<div className='col-span-12 lg:col-span-9 min-h rounded-md bg-lightest px-3 py-3 dark:bg-dark 2xl:px-5 2xl:py-5'>
<PlannerSchedule />
</div>
</>
) : (
<>
<div className='col-span-12 lg:col-span-9 min-h rounded-md bg-lightest px-3 py-3 dark:bg-dark 2xl:px-5 2xl:py-5'>
<PlannerSchedule />
</div>
<div className='col-span-12 lg:col-span-3 min-h'>
<Sidebar />
</div>
</>
)}
</div>
)
}

export default TimeTableSelectorPage;

export default TimeTableSelectorPage;

0 comments on commit a3377f1

Please sign in to comment.