From 349fb67b195366847265db39e71d8eb32b102bd4 Mon Sep 17 00:00:00 2001 From: David William Date: Thu, 8 Aug 2024 14:40:37 -0300 Subject: [PATCH 01/16] feat: refactory of aside schedule drawer; refactory of class time dialog --- api/.env-e | 29 + docker-compose.yml | 4 + scripts/config.sh | 2 +- scripts/env.sh | 2 +- web/.prettierrc | 4 + web/app/components/AsideButton.tsx | 36 +- .../AsideSchedulePopUp/ClassInfoBox.tsx | 99 +-- .../AsideSchedulePopUp/Form/InputForm.tsx | 115 ++-- .../AsideSchedulePopUp/ScheduleDrawer.tsx | 25 + .../ScheduleDrawerContent.tsx | 97 +++ .../components/AsideSchedulePopUp/Tooltip.tsx | 94 +-- web/app/components/Button.tsx | 36 +- web/app/components/ClassInfo.tsx | 117 ++-- web/app/components/DisciplineBox.tsx | 71 +- web/app/components/Modal/Dialog.tsx | 46 ++ web/app/components/ui/dialog.tsx | 135 ++++ web/app/components/ui/drawer.tsx | 130 ++++ web/app/globals.css | 71 +- .../home/components/AddDisciplineButton.tsx | 36 +- web/app/schedules/home/page.tsx | 112 +-- web/app/schedules/layout.tsx | 244 ++++--- web/components.json | 17 + web/package-lock.json | 635 +++++++++++++++--- web/package.json | 10 +- web/tailwind.config.ts | 93 ++- 25 files changed, 1750 insertions(+), 510 deletions(-) create mode 100644 api/.env-e create mode 100644 web/.prettierrc create mode 100644 web/app/components/AsideSchedulePopUp/ScheduleDrawer.tsx create mode 100644 web/app/components/AsideSchedulePopUp/ScheduleDrawerContent.tsx create mode 100644 web/app/components/Modal/Dialog.tsx create mode 100644 web/app/components/ui/dialog.tsx create mode 100644 web/app/components/ui/drawer.tsx create mode 100644 web/components.json diff --git a/api/.env-e b/api/.env-e new file mode 100644 index 00000000..a2d1b575 --- /dev/null +++ b/api/.env-e @@ -0,0 +1,29 @@ +# Configuração Django +# Gerar secret key: +# python3 -c 'from django.core.management.utils import get_random_secret_key; print(get_random_secret_key())' +DJANGO_SECRET_KEY="s0KORJPRaFEh06Z8OOhqO3Xu6UXAaNSweoB4YEc9YcIrIehESiWaK6jL4fR99k9s" +SETTINGS_FILE_PATH="core.settings.dev" + +# Banco de Dados +DB_ENGINE="django.db.backends.postgresql" +DB_NAME="postgres" +DB_USERNAME="suagradeunb" +DB_PASSWORD="suagradeunb" +DB_HOSTNAME="locahost" +DB_PORT=5432 + +# PostgreSQL +POSTGRES_DB="postgres" +POSTGRES_USER="suagradeunb" +POSTGRES_PASSWORD="suagradeunb" + +# Redis +REDIS_CACHE_LOCATION="redis://localhost:6379/1" + +# Credenciais de acesso ao admin +ADMIN_NAME="admin" +ADMIN_PASS="admin" +ADMIN_EMAIL="admin@gmail.com" + +# Google OAuth2 Mock +GOOGLE_OAUTH2_MOCK_TOKEN="your_google_oauth2_mock_token" diff --git a/docker-compose.yml b/docker-compose.yml index 67b51be6..bed72758 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -6,10 +6,14 @@ services: - postgres_data:/var/lib/postgresql/data/ env_file: - ./api/.env + ports: + - 5432:5432 redis: image: redis:alpine container_name: rediscache + ports: + - 6379:6379 api: restart: unless-stopped diff --git a/scripts/config.sh b/scripts/config.sh index 7f33f36d..43503f97 100644 --- a/scripts/config.sh +++ b/scripts/config.sh @@ -1,3 +1,3 @@ #!/bin/bash -sed -i "s/your_google_oauth2_mock_token/$(python3 -c 'import string; import random; print("".join(random.SystemRandom().choice(string.ascii_uppercase + string.ascii_lowercase + string.digits) for _ in range(64)))')/g" ./api/.env +sed -i -e "s/your_google_oauth2_mock_token/$(python3 -c 'import string; import random; print("".join(random.SystemRandom().choice(string.ascii_uppercase + string.ascii_lowercase + string.digits) for _ in range(64)))')/g" ./api/.env diff --git a/scripts/env.sh b/scripts/env.sh index 54bc6e25..dbf2a7b6 100644 --- a/scripts/env.sh +++ b/scripts/env.sh @@ -1,3 +1,3 @@ #!/bin/bash -sed -i "s/your_secret_key/$(python3 -c 'import string; import random; print("".join(random.SystemRandom().choice(string.ascii_uppercase + string.ascii_lowercase + string.digits) for _ in range(64)))')/g" ./api/.env +sed -i -e "s/your_secret_key/$(python3 -c 'import string; import random; print("".join(random.SystemRandom().choice(string.ascii_uppercase + string.ascii_lowercase + string.digits) for _ in range(64)))')/g" ./api/.env diff --git a/web/.prettierrc b/web/.prettierrc new file mode 100644 index 00000000..c1a6f667 --- /dev/null +++ b/web/.prettierrc @@ -0,0 +1,4 @@ +{ + "singleQuote": true, + "trailingComma": "es5" +} diff --git a/web/app/components/AsideButton.tsx b/web/app/components/AsideButton.tsx index 46ac0ba2..351dcbfd 100644 --- a/web/app/components/AsideButton.tsx +++ b/web/app/components/AsideButton.tsx @@ -3,20 +3,30 @@ import Image, { StaticImageData } from 'next/image'; import Button from './Button'; interface AsideButtonPropsType { - image: StaticImageData; - pageName: string; - innerRef?: (node: any) => void; - onClick?: () => void; + image: React.ReactNode; + pageName: string; + innerRef?: (node: any) => void; + onClick?: () => void; } - -export default function AsideButton({ image, pageName, innerRef, onClick }: AsideButtonPropsType) { - return ( - - ); -} \ No newline at end of file + /> */} + + ); +} diff --git a/web/app/components/AsideSchedulePopUp/ClassInfoBox.tsx b/web/app/components/AsideSchedulePopUp/ClassInfoBox.tsx index 6ee8623a..7479b27a 100644 --- a/web/app/components/AsideSchedulePopUp/ClassInfoBox.tsx +++ b/web/app/components/AsideSchedulePopUp/ClassInfoBox.tsx @@ -7,47 +7,70 @@ import Image from 'next/image'; import addIcon from '@/public/icons/add.jpg'; import removeIcon from '@/public/icons/remove.jpg'; +import { twMerge } from 'tailwind-merge'; +import { FiPlus, FiMinus } from 'react-icons/fi'; interface ClassInfoBoxPropsType extends HTMLProps { - currentDiscipline: DisciplineType, - currentClass: ClassType, + currentDiscipline: DisciplineType; + currentClass: ClassType; } -export default function ClassInfoBox({ currentDiscipline, currentClass, ...props }: ClassInfoBoxPropsType) { - const { classesChange, selectedClasses } = useSelectedClasses(); - const [selected, setSelected] = useState(false); +export default function ClassInfoBox({ + currentDiscipline, + currentClass, + ...props +}: ClassInfoBoxPropsType) { + const { classesChange, selectedClasses } = useSelectedClasses(); + const [selected, setSelected] = useState(false); - useEffect(() => { - if (selectedClasses.get(currentDiscipline.id)?.has(currentClass.id)) setSelected(true); - else setSelected(false); - }, [selectedClasses, classesChange, currentDiscipline, currentClass]); + useEffect(() => { + if (selectedClasses.get(currentDiscipline.id)?.has(currentClass.id)) + setSelected(true); + else setSelected(false); + }, [selectedClasses, classesChange, currentDiscipline, currentClass]); - return ( -
- - -
- ); -} \ No newline at end of file + return ( +
+ + +
+ ); +} diff --git a/web/app/components/AsideSchedulePopUp/Form/InputForm.tsx b/web/app/components/AsideSchedulePopUp/Form/InputForm.tsx index 260f79d1..1bd31dc4 100644 --- a/web/app/components/AsideSchedulePopUp/Form/InputForm.tsx +++ b/web/app/components/AsideSchedulePopUp/Form/InputForm.tsx @@ -6,69 +6,90 @@ import Image from 'next/image'; import searchIcon from '@/public/icons/search.jpg'; -import searchDiscipline, { DisciplineType } from '@/app/utils/api/searchDiscipline'; +import searchDiscipline, { + DisciplineType, +} from '@/app/utils/api/searchDiscipline'; import { FormData, FormType, InputFormPropsType } from '../types/types'; +import { FiSearch } from 'react-icons/fi'; interface FormPropsType { - form: FormType, - handleSearch: (event: React.FormEvent) => Promise, - inputRef: React.RefObject + form: FormType; + handleSearch: (event: React.FormEvent) => Promise; + inputRef: React.RefObject; } function Form(props: FormPropsType) { - const { formData, setFormData } = props.form; - const { handleSearch, inputRef } = props; - - return ( -
- setFormData({ ...formData, search: e.target.value })} - ref={inputRef} - className='h-14 p-2 w-full rounded-xl focus:outline-none' - /> - -
- ); + const { formData, setFormData } = props.form; + const { handleSearch, inputRef } = props; + + return ( +
+ setFormData({ ...formData, search: e.target.value })} + ref={inputRef} + className="h-14 p-2 w-full rounded-xl focus:outline-none" + /> + +
+ ); } -async function makeDisciplineSearch(textSearch: string, year: string, period: string, setInfos: (infos: Array) => void) { - const data = await searchDiscipline(textSearch, year, period); - if (data) { - let infos: Array = []; - data.forEach(discipline => infos.push({ ...discipline, expanded: false })); - setInfos(infos); - } +async function makeDisciplineSearch( + textSearch: string, + year: string, + period: string, + setInfos: (infos: Array) => void +) { + const data = await searchDiscipline(textSearch, year, period); + if (data) { + let infos: Array = []; + data.forEach((discipline) => + infos.push({ ...discipline, expanded: false }) + ); + setInfos(infos); + } } -async function handleDisciplineSearch(formData: FormData, setInfos: (infos: Array) => void) { - const { search, year, period } = formData; - const textSearch = search.trim(); +async function handleDisciplineSearch( + formData: FormData, + setInfos: (infos: Array) => void +) { + const { search, year, period } = formData; + const textSearch = search.trim(); - if (!textSearch) toast.error('Escreva no nome da disciplina'); - else if (!year || !period) toast.error('Escolha o ano/período'); - else await makeDisciplineSearch(textSearch, year, period, setInfos); + if (!textSearch) toast.error('Escreva no nome da disciplina'); + else if (!year || !period) toast.error('Escolha o ano/período'); + else await makeDisciplineSearch(textSearch, year, period, setInfos); } export default function InputForm(props: InputFormPropsType) { - const inputRef = useRef(null); - const { formData } = props.form; + const inputRef = useRef(null); + const { formData } = props.form; - async function handleSearch(event: React.FormEvent) { - event.preventDefault(); + async function handleSearch(event: React.FormEvent) { + event.preventDefault(); - if (inputRef && inputRef.current) inputRef.current.blur(); + if (inputRef && inputRef.current) inputRef.current.blur(); - await handleDisciplineSearch(formData, props.setInfos); - } + await handleDisciplineSearch(formData, props.setInfos); + } - return
; -} \ No newline at end of file + return ( + + ); +} diff --git a/web/app/components/AsideSchedulePopUp/ScheduleDrawer.tsx b/web/app/components/AsideSchedulePopUp/ScheduleDrawer.tsx new file mode 100644 index 00000000..c75ef826 --- /dev/null +++ b/web/app/components/AsideSchedulePopUp/ScheduleDrawer.tsx @@ -0,0 +1,25 @@ +import AddDisciplineButton from '@/app/schedules/home/components/AddDisciplineButton'; +import { + Drawer, + DrawerContent, + DrawerTitle, + DrawerTrigger, +} from '../ui/drawer'; +import { ScheduleDrawerContent } from './ScheduleDrawerContent'; + +export const ScheduleDrawer = () => { + return ( + + + + + + + + + + ); +}; diff --git a/web/app/components/AsideSchedulePopUp/ScheduleDrawerContent.tsx b/web/app/components/AsideSchedulePopUp/ScheduleDrawerContent.tsx new file mode 100644 index 00000000..97cfadb1 --- /dev/null +++ b/web/app/components/AsideSchedulePopUp/ScheduleDrawerContent.tsx @@ -0,0 +1,97 @@ +import { useEffect, useState } from 'react'; +import DisciplineOptionForm from './Form/DisciplineOptionForm'; +import { ClassType, DisciplineType } from '@/app/utils/api/searchDiscipline'; +import useSelectedClasses from '@/app/hooks/useSelectedClasses'; +import { CheckDisciplineObjPropsType } from './types/types'; +import { ClassValueType } from '@/app/contexts/SelectedClassesContext/types'; +import _ from 'lodash'; +import DisciplineFragment from './DisciplineFragment'; + +export const ScheduleDrawerContent = () => { + const { selectedClasses, setSelectedClasses } = useSelectedClasses(); + const [searchedDisciplines, setSearchedDisciplines] = useState< + Array + >([]); + + function handleSelectClass(discipline: DisciplineType, cls: ClassType) { + const disciplineId = discipline.id; + const classId = cls.id; + + const newSelectedClasses = _.cloneDeep(selectedClasses); + + const disciplineObj = newSelectedClasses.get(disciplineId); + + const newClassInfo = createNewClassInfo({ cls, discipline }); + + if (disciplineObj) + checkDisciplineObj({ + disciplineObj, + newSelectedClasses, + classId, + disciplineId, + newClassInfo, + }); + else { + const newDiscipline = new Map(); + newDiscipline.set(classId, newClassInfo); + + newSelectedClasses.set(disciplineId, newDiscipline); + } + + setSelectedClasses(newSelectedClasses); + } + + return ( +
+
+ +
+ {searchedDisciplines.map((discipline, index) => ( + + ))} +
+
+
+ ); +}; + +function createNewClassInfo(props: { + cls: ClassType; + discipline: DisciplineType; +}) { + const { cls, discipline } = props; + + return { + class: cls, + discipline: { + id: discipline.id, + name: discipline.name, + code: discipline.code, + }, + }; +} + +function checkDisciplineObj(props: CheckDisciplineObjPropsType) { + const { + disciplineObj, + newSelectedClasses, + classId, + disciplineId, + newClassInfo, + } = props; + + if (disciplineObj.has(classId)) { + disciplineObj.delete(classId); + + if (!disciplineObj.size) newSelectedClasses.delete(disciplineId); + } else disciplineObj.set(classId, newClassInfo); +} diff --git a/web/app/components/AsideSchedulePopUp/Tooltip.tsx b/web/app/components/AsideSchedulePopUp/Tooltip.tsx index d1286929..7e71bc42 100644 --- a/web/app/components/AsideSchedulePopUp/Tooltip.tsx +++ b/web/app/components/AsideSchedulePopUp/Tooltip.tsx @@ -11,53 +11,59 @@ import questionIcon from '@/public/icons/question.jpg'; import closeIcon from '@/public/icons/close.jpg'; interface TooltipPropsType { - children: React.ReactNode, -}; + children: React.ReactNode; +} export const isMobile = (width?: number) => { - return width && width <= 768; + return width && width <= 768; }; export default function Tooltip({ children }: TooltipPropsType) { - const [active, setActive] = useState(false); - const { width } = useWindowDimensions(); - - useEffect(() => { - function keyPress(event: KeyboardEvent) { - if (event.key === 'Escape') setActive(false); - } - - if (active) document.addEventListener('keydown', keyPress); - - return () => { - document.removeEventListener('keydown', keyPress); - }; - }, [active]); - - return ( -
- setActive(true)} - className='flex justify-center items-center text-xs border-black border-solid border-2 rounded-full h-5 w-5 hover:bg-gray-300'> - ícone de interrogação - - -
- {children} - -
-
+ const [active, setActive] = useState(false); + const { width } = useWindowDimensions(); + + useEffect(() => { + function keyPress(event: KeyboardEvent) { + if (event.key === 'Escape') setActive(false); + } + + if (active) document.addEventListener('keydown', keyPress); + + return () => { + document.removeEventListener('keydown', keyPress); + }; + }, [active]); + + return ( +
+ setActive(!active)} + className="flex justify-center items-center text-xs border-black border-solid border-2 rounded-full h-5 w-5 hover:bg-gray-300" + > + ícone de interrogação + + +
+ {children} +
- ); -} \ No newline at end of file +
+
+ ); +} diff --git a/web/app/components/Button.tsx b/web/app/components/Button.tsx index 01adbe8f..8c69ae7f 100644 --- a/web/app/components/Button.tsx +++ b/web/app/components/Button.tsx @@ -1,19 +1,27 @@ import { HTMLProps, LegacyRef } from 'react'; +import { twMerge } from 'tailwind-merge'; interface ButtonPropsType extends HTMLProps { - children: React.ReactNode; - innerRef?: LegacyRef | undefined + children: React.ReactNode; + innerRef?: LegacyRef | undefined; } -export default function Button({ children, innerRef, ...props }: ButtonPropsType) { - return ( - - ); -} \ No newline at end of file +export default function Button({ + children, + innerRef, + ...props +}: ButtonPropsType) { + return ( + + ); +} diff --git a/web/app/components/ClassInfo.tsx b/web/app/components/ClassInfo.tsx index 038dbbcb..a91da2d5 100644 --- a/web/app/components/ClassInfo.tsx +++ b/web/app/components/ClassInfo.tsx @@ -1,64 +1,81 @@ import { Fragment } from 'react'; import { ClassValueType } from '../contexts/SelectedClassesContext/types'; +import { FiInfo } from 'react-icons/fi'; import Tooltip from './AsideSchedulePopUp/Tooltip'; +import { + Dialog, + DialogContent, + DialogDescription, + DialogTitle, + DialogTrigger, +} from './ui/dialog'; interface ClassInfoPropsType { - currentClass: ClassValueType + currentClass: ClassValueType; } -export function generateSpecialDates(special_dates: Array>, days: Array) { - return special_dates.map((specialDate, index) => { - const day = specialDate[0]; - const start = parseInt(specialDate[1]) - 1; - const end = parseInt(specialDate[2]) - 1; +export function generateSpecialDates( + special_dates: Array>, + days: Array +) { + return special_dates.map((specialDate, index) => { + const day = specialDate[0]; + const start = parseInt(specialDate[1]) - 1; + const end = parseInt(specialDate[2]) - 1; - function make_days(start: number, end: number) { - return days.slice(start, end + 1).map((day, index) => -
- {day} -
- ); - } + function make_days(start: number, end: number) { + return days.slice(start, end + 1).map((day, index) => ( +
+ {day} +
+ )); + } - return ( - - - {day} - - {make_days(start, end)} - - ); - }); + return ( + + {day} + {make_days(start, end)} + + ); + }); } export default function ClassInfo({ currentClass }: ClassInfoPropsType) { - return ( -
-
- Sala: {currentClass.class.classroom} -
-
- Horários: {currentClass.class.schedule} - -
- Horários - {!currentClass.class.special_dates.length ? - currentClass.class.days.map((day, index) => -
- {day} -
- ) : generateSpecialDates( - currentClass.class.special_dates, - currentClass.class.days - )} -
-
-
-
- Professor: {currentClass.class.teachers[0]} {currentClass.class.teachers.length > 1 && 'e outros'} -
-
- ); -} \ No newline at end of file + return ( +
+
+ Sala:{' '} + {currentClass.class.classroom} +
+
+ Horários:{' '} + {currentClass.class.schedule} + + + + + + Horários + + {!currentClass.class.special_dates.length + ? currentClass.class.days.map((day, index) => ( +
{day}
+ )) + : generateSpecialDates( + currentClass.class.special_dates, + currentClass.class.days + )} +
+
+
+
+
+ Professor:{' '} + {currentClass.class.teachers[0]}{' '} + {currentClass.class.teachers.length > 1 && 'e outros'} +
+
+ ); +} diff --git a/web/app/components/DisciplineBox.tsx b/web/app/components/DisciplineBox.tsx index 19f01cf9..47f31bd4 100644 --- a/web/app/components/DisciplineBox.tsx +++ b/web/app/components/DisciplineBox.tsx @@ -8,40 +8,49 @@ import ClassInfo from './ClassInfo'; import deleteIcon from '../../public/icons/delete.jpg'; +import { FiTrash2 } from 'react-icons/fi'; + import _ from 'lodash'; interface DisciplineBoxPropsType { - currentClass: ClassValueType - discipline: { - id: number, - name: string, - code: string - } + currentClass: ClassValueType; + discipline: { + id: number; + name: string; + code: string; + }; } -export default function DisciplineBox({ currentClass, discipline }: DisciplineBoxPropsType) { - const { selectedClasses, setSelectedClasses } = useSelectedClasses(); - - function handleDeleteClass() { - const newSelectedClasses = _.cloneDeep(selectedClasses); - newSelectedClasses.get(discipline.id)?.delete(currentClass.class.id); - if (!newSelectedClasses.get(discipline.id)?.size) { - newSelectedClasses.delete(discipline.id); - } - setSelectedClasses(newSelectedClasses); +export default function DisciplineBox({ + currentClass, + discipline, +}: DisciplineBoxPropsType) { + const { selectedClasses, setSelectedClasses } = useSelectedClasses(); + + function handleDeleteClass() { + const newSelectedClasses = _.cloneDeep(selectedClasses); + newSelectedClasses.get(discipline.id)?.delete(currentClass.class.id); + if (!newSelectedClasses.get(discipline.id)?.size) { + newSelectedClasses.delete(discipline.id); } - - return ( - -
-
- Código: {discipline.code} -
- -
- -
- ); -} \ No newline at end of file + setSelectedClasses(newSelectedClasses); + } + + return ( + +
+
+ Código: {discipline.code} +
+ +
+ +
+ ); +} diff --git a/web/app/components/Modal/Dialog.tsx b/web/app/components/Modal/Dialog.tsx new file mode 100644 index 00000000..8568f059 --- /dev/null +++ b/web/app/components/Modal/Dialog.tsx @@ -0,0 +1,46 @@ +"use client"; + +import React, { createContext, useState } from "react"; + +interface ModalContextType { + activeModal: boolean; + setActiveModal: (value: boolean) => void; +} + +const ModalContext = createContext({ + activeModal: false, + setActiveModal: (value: boolean) => {}, +}); + +const ModalProvider = ({ children }) => { + const [activeModal, setActiveModal] = useState(false); + + return ( + + {children} + + ); +}; + +const DialogContent = React.forwardRef void }>( + { + children, + noExit, + setActiveModal, + } +) => { + return ( +
+
+ {children} + {!noExit && ( + + )} +
+
+ ); +}; + +export { ModalProvider, ModalContext }; diff --git a/web/app/components/ui/dialog.tsx b/web/app/components/ui/dialog.tsx new file mode 100644 index 00000000..3c6feb6a --- /dev/null +++ b/web/app/components/ui/dialog.tsx @@ -0,0 +1,135 @@ +'use client'; + +import * as React from 'react'; +import * as DialogPrimitive from '@radix-ui/react-dialog'; +import { X } from 'lucide-react'; + +import { cn } from '@/app/lib/utils'; + +const Dialog = DialogPrimitive.Root; + +const DialogTrigger = DialogPrimitive.Trigger; + +const DialogPortal = DialogPrimitive.Portal; + +const DialogClose = DialogPrimitive.Close; + +type DialogOverlayProps = { + showOverlay?: boolean; +} & React.ComponentPropsWithoutRef; + +const DialogOverlay = React.forwardRef< + React.ElementRef, + DialogOverlayProps +>(({ className, showOverlay = true, ...props }, ref) => ( + +)); +DialogOverlay.displayName = DialogPrimitive.Overlay.displayName; + +type DialogContentProps = React.ComponentPropsWithoutRef< + typeof DialogPrimitive.Content +> & { + showOverlay?: boolean; +}; + +const DialogContent = React.forwardRef< + React.ElementRef, + DialogContentProps +>(({ className, children, showOverlay, ...props }, ref) => ( + + + + {children} + + + Close + + + +)); +DialogContent.displayName = DialogPrimitive.Content.displayName; + +const DialogHeader = ({ + className, + ...props +}: React.HTMLAttributes) => ( +
+); +DialogHeader.displayName = 'DialogHeader'; + +const DialogFooter = ({ + className, + ...props +}: React.HTMLAttributes) => ( +
+); +DialogFooter.displayName = 'DialogFooter'; + +const DialogTitle = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +DialogTitle.displayName = DialogPrimitive.Title.displayName; + +const DialogDescription = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +DialogDescription.displayName = DialogPrimitive.Description.displayName; + +export { + Dialog, + DialogPortal, + DialogOverlay, + DialogClose, + DialogTrigger, + DialogContent, + DialogHeader, + DialogFooter, + DialogTitle, + DialogDescription, +}; diff --git a/web/app/components/ui/drawer.tsx b/web/app/components/ui/drawer.tsx new file mode 100644 index 00000000..bdae9da1 --- /dev/null +++ b/web/app/components/ui/drawer.tsx @@ -0,0 +1,130 @@ +'use client'; + +import * as React from 'react'; +import { Drawer as DrawerPrimitive } from 'vaul'; + +import { cn } from '@/app/lib/utils'; + +const Drawer = ({ + shouldScaleBackground = true, + ...props +}: React.ComponentProps) => ( + +); +Drawer.displayName = 'Drawer'; + +const DrawerTrigger = DrawerPrimitive.Trigger; + +const DrawerPortal = DrawerPrimitive.Portal; + +const DrawerClose = DrawerPrimitive.Close; + +type DialogOverlayProps = { + showOverlay?: boolean; +} & React.ComponentPropsWithoutRef; + +const DrawerOverlay = React.forwardRef< + React.ElementRef, + DialogOverlayProps +>(({ className, showOverlay = true, ...props }, ref) => ( + +)); +DrawerOverlay.displayName = DrawerPrimitive.Overlay.displayName; + +type DrawerContentProps = React.ComponentPropsWithoutRef< + typeof DrawerPrimitive.Content +> & { + showOverlay?: boolean; +}; + +const DrawerContent = React.forwardRef< + React.ElementRef, + DrawerContentProps +>(({ className, children, showOverlay, ...props }, ref) => ( + + + +
+ {children} + + +)); +DrawerContent.displayName = 'DrawerContent'; + +const DrawerHeader = ({ + className, + ...props +}: React.HTMLAttributes) => ( +
+); +DrawerHeader.displayName = 'DrawerHeader'; + +const DrawerFooter = ({ + className, + ...props +}: React.HTMLAttributes) => ( +
+); +DrawerFooter.displayName = 'DrawerFooter'; + +const DrawerTitle = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +DrawerTitle.displayName = DrawerPrimitive.Title.displayName; + +const DrawerDescription = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +DrawerDescription.displayName = DrawerPrimitive.Description.displayName; + +export { + Drawer, + DrawerPortal, + DrawerOverlay, + DrawerTrigger, + DrawerClose, + DrawerContent, + DrawerHeader, + DrawerFooter, + DrawerTitle, + DrawerDescription, +}; diff --git a/web/app/globals.css b/web/app/globals.css index c97228e3..30f1a06d 100644 --- a/web/app/globals.css +++ b/web/app/globals.css @@ -4,6 +4,7 @@ @tailwind components; @tailwind utilities; + :root { --main-green: hsl(149, 80%, 38%); --main-white: hsl(0, 0%, 96%); @@ -42,4 +43,72 @@ body::-moz-scrollbar-thumb { body::-moz-scrollbar-track { background-color: var(--main-white); -} \ No newline at end of file +} + + + +@layer base { + :root { + --background: 0 0% 100%; + --foreground: 222.2 84% 4.9%; + --card: 0 0% 100%; + --card-foreground: 222.2 84% 4.9%; + --popover: 0 0% 100%; + --popover-foreground: 222.2 84% 4.9%; + --primary: 222.2 47.4% 11.2%; + --primary-foreground: 210 40% 98%; + --secondary: 210 40% 96.1%; + --secondary-foreground: 222.2 47.4% 11.2%; + --muted: 210 40% 96.1%; + --muted-foreground: 215.4 16.3% 46.9%; + --accent: 210 40% 96.1%; + --accent-foreground: 222.2 47.4% 11.2%; + --destructive: 0 84.2% 60.2%; + --destructive-foreground: 210 40% 98%; + --border: 214.3 31.8% 91.4%; + --input: 214.3 31.8% 91.4%; + --ring: 222.2 84% 4.9%; + --radius: 0.5rem; + --chart-1: 12 76% 61%; + --chart-2: 173 58% 39%; + --chart-3: 197 37% 24%; + --chart-4: 43 74% 66%; + --chart-5: 27 87% 67%; + } + + .dark { + --background: 222.2 84% 4.9%; + --foreground: 210 40% 98%; + --card: 222.2 84% 4.9%; + --card-foreground: 210 40% 98%; + --popover: 222.2 84% 4.9%; + --popover-foreground: 210 40% 98%; + --primary: 210 40% 98%; + --primary-foreground: 222.2 47.4% 11.2%; + --secondary: 217.2 32.6% 17.5%; + --secondary-foreground: 210 40% 98%; + --muted: 217.2 32.6% 17.5%; + --muted-foreground: 215 20.2% 65.1%; + --accent: 217.2 32.6% 17.5%; + --accent-foreground: 210 40% 98%; + --destructive: 0 62.8% 30.6%; + --destructive-foreground: 210 40% 98%; + --border: 217.2 32.6% 17.5%; + --input: 217.2 32.6% 17.5%; + --ring: 212.7 26.8% 83.9%; + --chart-1: 220 70% 50%; + --chart-2: 160 60% 45%; + --chart-3: 30 80% 55%; + --chart-4: 280 65% 60%; + --chart-5: 340 75% 55%; + } +} + +/* @layer base { + * { + @apply border-border; + } + body { + @apply bg-background text-foreground; + } +} */ \ No newline at end of file diff --git a/web/app/schedules/home/components/AddDisciplineButton.tsx b/web/app/schedules/home/components/AddDisciplineButton.tsx index 7a73656c..dbf4c8bf 100644 --- a/web/app/schedules/home/components/AddDisciplineButton.tsx +++ b/web/app/schedules/home/components/AddDisciplineButton.tsx @@ -1,26 +1,18 @@ -import { MutableRefObject } from 'react'; - import Button from '@/app/components/Button'; -import useShowPopUpContent from '@/app/hooks/useShowPopUpContent'; import useWindowDimensions from '@/app/hooks/useWindowDimensions'; -interface AddDisciplineButtonPropsType { - buttonAddDisciplineRef: MutableRefObject; -} - -export default function AddDisciplineButton(props: AddDisciplineButtonPropsType) { - const { breakHeighPoint } = useWindowDimensions(); - const { showPopUpContent, setShowPopUpContent } = useShowPopUpContent(); +export default function AddDisciplineButton() { + const { breakHeighPoint } = useWindowDimensions(); - return ( -
- -
- ); -} \ No newline at end of file + return ( +
+ +
+ ); +} diff --git a/web/app/schedules/home/page.tsx b/web/app/schedules/home/page.tsx index 9bf8cddc..f8db0842 100644 --- a/web/app/schedules/home/page.tsx +++ b/web/app/schedules/home/page.tsx @@ -14,58 +14,84 @@ import ClickOutsideHandler from './handlers/ClickOutsideHandler'; import ShowPopUpContentContextProvider from '@/app/contexts/ShowPopUpContentContext'; import { ClassValueType } from '@/app/contexts/SelectedClassesContext/types'; +import { + Drawer, + DrawerContent, + DrawerOverlay, + DrawerTrigger, +} from '@/app/components/ui/drawer'; +import { ScheduleDrawer } from '@/app/components/AsideSchedulePopUp/ScheduleDrawer'; +import { ScheduleDrawerContent } from '@/app/components/AsideSchedulePopUp/ScheduleDrawerContent'; -function DisciplineBlockJSX({ ...props }: { - index: number, - cls: ClassValueType, - classesToShow: ClassValueType[] +function DisciplineBlockJSX({ + ...props +}: { + index: number; + cls: ClassValueType; + classesToShow: ClassValueType[]; }) { - let classAppearance; - for (let indexInner = 0; indexInner < props.classesToShow.length; indexInner++) { - if (props.classesToShow[indexInner].discipline.code === props.cls.discipline.code) { - classAppearance = indexInner; - break; - } + let classAppearance; + for ( + let indexInner = 0; + indexInner < props.classesToShow.length; + indexInner++ + ) { + if ( + props.classesToShow[indexInner].discipline.code === + props.cls.discipline.code + ) { + classAppearance = indexInner; + break; } + } - return ( - - {props.index === classAppearance && - - {props.cls.discipline.name} - - } - - - ); + return ( + + {props.index === classAppearance && ( + + {props.cls.discipline.name} + + )} + + + ); } export default function Home() { - const { classesToShow, setClassesToShow } = useClassesToShow(); - const { classesChange, selectedClasses } = useSelectedClasses(); + const { classesToShow, setClassesToShow } = useClassesToShow(); + const { classesChange, selectedClasses } = useSelectedClasses(); - const divAddClassRef = useRef(null), buttonAddDisciplineRef = useRef(null); + const divAddClassRef = useRef(null), + buttonAddDisciplineRef = useRef(null); - useEffect(() => { - const newClasses = new Array(); - selectedClasses.forEach((cls) => cls.forEach(value => newClasses.push(value))); + useEffect(() => { + const newClasses = new Array(); + selectedClasses.forEach((cls) => + cls.forEach((value) => newClasses.push(value)) + ); - setClassesToShow(newClasses); - }, [classesChange, selectedClasses, setClassesToShow]); + setClassesToShow(newClasses); + }, [classesChange, selectedClasses, setClassesToShow]); - return ( - - - -
- {classesToShow.length ? classesToShow.map((cls, index) => { - return DisciplineBlockJSX({ index, cls, classesToShow }); - }) : 'Nenhuma disciplina escolhida no momento'} -
+ return ( + <> + +
+ {classesToShow.length + ? classesToShow.map((cls, index) => { + return DisciplineBlockJSX({ index, cls, classesToShow }); + }) + : 'Nenhuma disciplina escolhida no momento'} +
- - -
-
- ); -} \ No newline at end of file + + + ); +} diff --git a/web/app/schedules/layout.tsx b/web/app/schedules/layout.tsx index 8cefdd2b..e9ab3629 100644 --- a/web/app/schedules/layout.tsx +++ b/web/app/schedules/layout.tsx @@ -17,117 +17,179 @@ import infoIcon from '@/public/icons/info.jpg'; import googleIcon from '@/public/icons/google.jpg'; import scheduleIcon from '@/public/icons/schedule.jpg'; import profileIcon from '@/public/icons/profile.jpg'; - -function calculatePositionOfBlob(node: any, width: number, footerWidth: number) { - const infos = node.getBoundingClientRect(); - const intX = Math.round(infos.x - ((width - footerWidth) / 2)); - const intWidth = Math.round(infos.width); - - return { x: intX, width: intWidth }; +import { + RiMenuLine, + RiCalendarLine, + RiUserLine, + RiInformation2Line, +} from 'react-icons/ri'; +import { FiHome, FiInfo, FiUser } from 'react-icons/fi'; + +function calculatePositionOfBlob( + node: any, + width: number, + footerWidth: number +) { + const infos = node.getBoundingClientRect(); + const intX = Math.round(infos.x - (width - footerWidth) / 2); + const intWidth = Math.round(infos.width); + + return { x: intX, width: intWidth }; } function LogoReturnButton() { - const router = useRouter(); - const { user } = useUser(); - - return ( - user.is_anonymous && - - ); + const router = useRouter(); + const { user } = useUser(); + + return ( + user.is_anonymous && ( + + ) + ); } -function footerRefCallback(node: any, width: number | undefined, setFooterWidth: (width: number) => void) { - if (width && node) { - const intWidth = Math.round(node.getBoundingClientRect().width); - setFooterWidth(intWidth); - } +function footerRefCallback( + node: any, + width: number | undefined, + setFooterWidth: (width: number) => void +) { + if (width && node) { + const intWidth = Math.round(node.getBoundingClientRect().width); + setFooterWidth(intWidth); + } } interface AsideRefCallbackPropsType { - node: any, - path: string, - width: number | undefined, - footerWidth: number, - setCurrentBlobDimensions: (position: { x: number, width: number }) => void + node: any; + path: string; + width: number | undefined; + footerWidth: number; + setCurrentBlobDimensions: (position: { x: number; width: number }) => void; } function asideRefCallback(props: AsideRefCallbackPropsType) { - const { node, path, width, footerWidth } = props; - if (width && node && node.name === path) { - const position = calculatePositionOfBlob(node, width, footerWidth); - props.setCurrentBlobDimensions(position); - } + const { node, path, width, footerWidth } = props; + if (width && node && node.name === path) { + const position = calculatePositionOfBlob(node, width, footerWidth); + props.setCurrentBlobDimensions(position); + } } function AsideButtonsJSX() { - const { user } = useUser(); - - const { width } = useWindowDimensions(); - const [footerWidth, setFooterWidth] = useState(0); - const onFooterRefChange = useCallback((node: any) => { - footerRefCallback(node, width, setFooterWidth); - }, [width]); - - const router = useRouter(); - const path = usePathname().split('/')[2]; - - const [currentBlobDimensions, setCurrentBlobDimensions] = useState({ x: 0, width: 0 }); - const onRefChange = useCallback((node: any) => { - const props = { node, path, width, footerWidth, setCurrentBlobDimensions }; - asideRefCallback(props); - }, [path, width, footerWidth]); - - return ( -
-
- router.push('/schedules/home')} /> - router.push('/schedules/mygrades')} /> - {user.is_anonymous ? null : router.push('/schedules/profile')} />} - router.push('/schedules/info')} /> -
- ); + const { user } = useUser(); + + const { width } = useWindowDimensions(); + const [footerWidth, setFooterWidth] = useState(0); + const onFooterRefChange = useCallback( + (node: any) => { + footerRefCallback(node, width, setFooterWidth); + }, + [width] + ); + + const router = useRouter(); + const path = usePathname().split('/')[2]; + + const [currentBlobDimensions, setCurrentBlobDimensions] = useState({ + x: 0, + width: 0, + }); + const onRefChange = useCallback( + (node: any) => { + const props = { + node, + path, + width, + footerWidth, + setCurrentBlobDimensions, + }; + asideRefCallback(props); + }, + [path, width, footerWidth] + ); + + return ( +
+
+ } + onClick={() => router.push('/schedules/home')} + /> + } + onClick={() => router.push('/schedules/mygrades')} + /> + {user.is_anonymous ? null : ( + } + onClick={() => router.push('/schedules/profile')} + /> + )} + } + onClick={() => router.push('/schedules/info')} + /> +
+ ); } function LayoutJSX({ children }: { children: React.ReactNode }) { - const { breakHeighPoint } = useWindowDimensions(); - const { isLoading } = useUser(); - - return ( - <> - - {!isLoading ? -
- {children} -
: - - } - - - - ); + const { breakHeighPoint } = useWindowDimensions(); + const { isLoading } = useUser(); + + return ( + <> + + {!isLoading ? ( +
+ {children} +
+ ) : ( + + )} + + + + ); } export default function SchedulesLayout({ - children, + children, }: { - children: React.ReactNode + children: React.ReactNode; }) { - - return ( - - - {children} - - - ); + return ( + + {children} + + ); } diff --git a/web/components.json b/web/components.json new file mode 100644 index 00000000..e997d58b --- /dev/null +++ b/web/components.json @@ -0,0 +1,17 @@ +{ + "$schema": "https://ui.shadcn.com/schema.json", + "style": "default", + "rsc": true, + "tsx": true, + "tailwind": { + "config": "tailwind.config.ts", + "css": "app/globals.css", + "baseColor": "slate", + "cssVariables": true, + "prefix": "" + }, + "aliases": { + "components": "@/app/components", + "utils": "@/app/lib/utils" + } +} \ No newline at end of file diff --git a/web/package-lock.json b/web/package-lock.json index 6c1479d5..6e40c097 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -8,15 +8,23 @@ "name": "web", "version": "0.1.0", "dependencies": { + "@radix-ui/react-dialog": "^1.1.1", "@types/lodash": "^4.14.202", "axios": "^1.6.2", + "class-variance-authority": "^0.7.0", + "clsx": "^2.1.1", "jspdf": "^2.5.1", "lodash": "^4.17.21", + "lucide-react": "^0.424.0", "next": "14.1.1", "react": "^18", "react-dom": "^18", "react-hot-toast": "^2.4.1", - "sharp": "^0.33.0" + "react-icons": "^5.2.1", + "sharp": "^0.33.0", + "tailwind-merge": "^2.4.0", + "tailwindcss-animate": "^1.0.7", + "vaul": "^0.9.1" }, "devDependencies": { "@types/node": "^20", @@ -43,7 +51,6 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", - "dev": true, "engines": { "node": ">=10" }, @@ -595,7 +602,6 @@ "version": "0.3.3", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", - "dev": true, "dependencies": { "@jridgewell/set-array": "^1.0.1", "@jridgewell/sourcemap-codec": "^1.4.10", @@ -609,7 +615,6 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", - "dev": true, "engines": { "node": ">=6.0.0" } @@ -618,7 +623,6 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", - "dev": true, "engines": { "node": ">=6.0.0" } @@ -626,14 +630,12 @@ "node_modules/@jridgewell/sourcemap-codec": { "version": "1.4.15", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", - "dev": true + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==" }, "node_modules/@jridgewell/trace-mapping": { "version": "0.3.20", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz", "integrity": "sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==", - "dev": true, "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" @@ -792,7 +794,6 @@ "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" @@ -805,7 +806,6 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true, "engines": { "node": ">= 8" } @@ -814,7 +814,6 @@ "version": "1.2.8", "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" @@ -823,6 +822,302 @@ "node": ">= 8" } }, + "node_modules/@radix-ui/primitive": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.0.tgz", + "integrity": "sha512-4Z8dn6Upk0qk4P74xBhZ6Hd/w0mPEzOOLxy4xiPXOXqjF7jZS0VAKk7/x/H6FyY2zCkYJqePf1G5KmkmNJ4RBA==" + }, + "node_modules/@radix-ui/react-compose-refs": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.0.tgz", + "integrity": "sha512-b4inOtiaOnYf9KWyO3jAeeCG6FeyfY6ldiEPanbUjWd+xIk5wZeHa8yVwmrJ2vderhu/BQvzCrJI0lHd+wIiqw==", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-context": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.0.tgz", + "integrity": "sha512-OKrckBy+sMEgYM/sMmqmErVn0kZqrHPJze+Ql3DzYsDDp0hl0L62nx/2122/Bvps1qz645jlcu2tD9lrRSdf8A==", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dialog": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.1.tgz", + "integrity": "sha512-zysS+iU4YP3STKNS6USvFVqI4qqx8EpiwmT5TuCApVEBca+eRCbONi4EgzfNSuVnOXvC5UPHHMjs8RXO6DH9Bg==", + "dependencies": { + "@radix-ui/primitive": "1.1.0", + "@radix-ui/react-compose-refs": "1.1.0", + "@radix-ui/react-context": "1.1.0", + "@radix-ui/react-dismissable-layer": "1.1.0", + "@radix-ui/react-focus-guards": "1.1.0", + "@radix-ui/react-focus-scope": "1.1.0", + "@radix-ui/react-id": "1.1.0", + "@radix-ui/react-portal": "1.1.1", + "@radix-ui/react-presence": "1.1.0", + "@radix-ui/react-primitive": "2.0.0", + "@radix-ui/react-slot": "1.1.0", + "@radix-ui/react-use-controllable-state": "1.1.0", + "aria-hidden": "^1.1.1", + "react-remove-scroll": "2.5.7" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dismissable-layer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.0.tgz", + "integrity": "sha512-/UovfmmXGptwGcBQawLzvn2jOfM0t4z3/uKffoBlj724+n3FvBbZ7M0aaBOmkp6pqFYpO4yx8tSVJjx3Fl2jig==", + "dependencies": { + "@radix-ui/primitive": "1.1.0", + "@radix-ui/react-compose-refs": "1.1.0", + "@radix-ui/react-primitive": "2.0.0", + "@radix-ui/react-use-callback-ref": "1.1.0", + "@radix-ui/react-use-escape-keydown": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-focus-guards": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.0.tgz", + "integrity": "sha512-w6XZNUPVv6xCpZUqb/yN9DL6auvpGX3C/ee6Hdi16v2UUy25HV2Q5bcflsiDyT/g5RwbPQ/GIT1vLkeRb+ITBw==", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-focus-scope": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.0.tgz", + "integrity": "sha512-200UD8zylvEyL8Bx+z76RJnASR2gRMuxlgFCPAe/Q/679a/r0eK3MBVYMb7vZODZcffZBdob1EGnky78xmVvcA==", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.0", + "@radix-ui/react-primitive": "2.0.0", + "@radix-ui/react-use-callback-ref": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-id": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.0.tgz", + "integrity": "sha512-EJUrI8yYh7WOjNOqpoJaf1jlFIH2LvtgAl+YcFqNCa+4hj64ZXmPkAKOFs/ukjz3byN6bdb/AVUqHkI8/uWWMA==", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-portal": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.1.tgz", + "integrity": "sha512-A3UtLk85UtqhzFqtoC8Q0KvR2GbXF3mtPgACSazajqq6A41mEQgo53iPzY4i6BwDxlIFqWIhiQ2G729n+2aw/g==", + "dependencies": { + "@radix-ui/react-primitive": "2.0.0", + "@radix-ui/react-use-layout-effect": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-presence": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.0.tgz", + "integrity": "sha512-Gq6wuRN/asf9H/E/VzdKoUtT8GC9PQc9z40/vEr0VCJ4u5XvvhWIrSsCB6vD2/cH7ugTdSfYq9fLJCcM00acrQ==", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.0", + "@radix-ui/react-use-layout-effect": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-primitive": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.0.0.tgz", + "integrity": "sha512-ZSpFm0/uHa8zTvKBDjLFWLo8dkr4MBsiDLz0g3gMUwqgLHz9rTaRRGYDgvZPtBJgYCBKXkS9fzmoySgr8CO6Cw==", + "dependencies": { + "@radix-ui/react-slot": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-slot": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.0.tgz", + "integrity": "sha512-FUCf5XMfmW4dtYl69pdS4DbxKy8nj4M7SafBgPllysxmdachynNflAdp/gCsnYWNDnge6tI9onzMp5ARYc1KNw==", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-callback-ref": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.0.tgz", + "integrity": "sha512-CasTfvsy+frcFkbXtSJ2Zu9JHpN8TYKxkgJGWbjiZhFivxaeW7rMeZt7QELGVLaYVfFMsKHjb7Ak0nMEe+2Vfw==", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-controllable-state": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.1.0.tgz", + "integrity": "sha512-MtfMVJiSr2NjzS0Aa90NPTnvTSg6C/JLCV7ma0W6+OMV78vd8OyRpID+Ng9LxzsPbLeuBnWBA1Nq30AtBIDChw==", + "dependencies": { + "@radix-ui/react-use-callback-ref": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-escape-keydown": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.0.tgz", + "integrity": "sha512-L7vwWlR1kTTQ3oh7g1O0CBF3YCyyTj8NmhLR+phShpyA50HCfBFKVJTpshm9PzLiKmehsrQzTYTpX9HvmC9rhw==", + "dependencies": { + "@radix-ui/react-use-callback-ref": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-layout-effect": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.0.tgz", + "integrity": "sha512-+FPE0rOdziWSrH9athwI1R0HDVbWlEhd+FR+aSDk4uWGmSJ9Z54sdZVDQPZAinJhJXwfT+qnj969mCsT2gfm5w==", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@rushstack/eslint-patch": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.5.1.tgz", @@ -861,7 +1156,7 @@ "version": "15.7.10", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.10.tgz", "integrity": "sha512-mxSnDQxPqsZxmeShFH+uwQ4kO4gcJcGahjjMFeLbKE95IAZiiZyiEepGZjtXJ7hN/yfu0bu9xN2ajcU0JcxX6A==", - "dev": true + "devOptional": true }, "node_modules/@types/raf": { "version": "3.4.3", @@ -873,7 +1168,7 @@ "version": "18.2.37", "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.37.tgz", "integrity": "sha512-RGAYMi2bhRgEXT3f4B92WTohopH6bIXw05FuGlmJEnv/omEn190+QYEIYxIAuIBdKgboYYdVved2p1AxZVQnaw==", - "dev": true, + "devOptional": true, "dependencies": { "@types/prop-types": "*", "@types/scheduler": "*", @@ -884,7 +1179,7 @@ "version": "18.2.15", "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.15.tgz", "integrity": "sha512-HWMdW+7r7MR5+PZqJF6YFNSCtjz1T0dsvo/f1BV6HkV+6erD/nA7wd9NM00KVG83zf2nJ7uATPO9ttdIPvi3gg==", - "dev": true, + "devOptional": true, "dependencies": { "@types/react": "*" } @@ -893,7 +1188,7 @@ "version": "0.16.6", "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.6.tgz", "integrity": "sha512-Vlktnchmkylvc9SnwwwozTv04L/e1NykF5vgoQ0XTmI8DD+wxfjQuHuvHS3p0r2jz2x2ghPs2h1FVeDirIteWA==", - "dev": true + "devOptional": true }, "node_modules/@typescript-eslint/parser": { "version": "6.11.0", @@ -1067,14 +1362,12 @@ "node_modules/any-promise": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", - "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", - "dev": true + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==" }, "node_modules/anymatch": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dev": true, "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" @@ -1086,8 +1379,7 @@ "node_modules/arg": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", - "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", - "dev": true + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==" }, "node_modules/argparse": { "version": "2.0.1", @@ -1095,6 +1387,17 @@ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true }, + "node_modules/aria-hidden": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.4.tgz", + "integrity": "sha512-y+CcFFwelSXpLZk/7fMB2mUbGtX9lKycf1MWJ7CaTIERyitVlyQx6C+sxcROU2BAJ24OiZyK+8wj2i8AlBoS3A==", + "dependencies": { + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/aria-query": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", @@ -1345,8 +1648,7 @@ "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, "node_modules/base64-arraybuffer": { "version": "1.0.2", @@ -1361,7 +1663,6 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", - "dev": true, "engines": { "node": ">=8" } @@ -1370,7 +1671,6 @@ "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -1380,7 +1680,6 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "dev": true, "dependencies": { "fill-range": "^7.1.1" }, @@ -1469,7 +1768,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", - "dev": true, "engines": { "node": ">= 6" } @@ -1538,7 +1836,6 @@ "version": "3.5.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", - "dev": true, "funding": [ { "type": "individual", @@ -1565,7 +1862,6 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, "dependencies": { "is-glob": "^4.0.1" }, @@ -1573,11 +1869,38 @@ "node": ">= 6" } }, + "node_modules/class-variance-authority": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.7.0.tgz", + "integrity": "sha512-jFI8IQw4hczaL4ALINxqLEXQbWcNjoSkloa4IaufXCJr6QawJyw7tuRysRsrE8w2p/4gGaxKIt/hX3qz/IbD1A==", + "dependencies": { + "clsx": "2.0.0" + }, + "funding": { + "url": "https://joebell.co.uk" + } + }, + "node_modules/class-variance-authority/node_modules/clsx": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.0.0.tgz", + "integrity": "sha512-rQ1+kcj+ttHG0MKVGBUXwayCCF1oh39BF5COIpRzuCEv8Mwjv0XucrI2ExNTOn9IlLifGClWQcU9BrZORvtw6Q==", + "engines": { + "node": ">=6" + } + }, "node_modules/client-only": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==" }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "engines": { + "node": ">=6" + } + }, "node_modules/color": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", @@ -1630,7 +1953,6 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", - "dev": true, "engines": { "node": ">= 6" } @@ -1638,8 +1960,7 @@ "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" }, "node_modules/core-js": { "version": "3.34.0", @@ -1679,7 +2000,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", - "dev": true, "bin": { "cssesc": "bin/cssesc" }, @@ -1777,11 +2097,15 @@ "node": ">=8" } }, + "node_modules/detect-node-es": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz", + "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==" + }, "node_modules/didyoumean": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", - "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", - "dev": true + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==" }, "node_modules/dir-glob": { "version": "3.0.1", @@ -1798,8 +2122,7 @@ "node_modules/dlv": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", - "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", - "dev": true + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==" }, "node_modules/doctrine": { "version": "3.0.0", @@ -2400,7 +2723,6 @@ "version": "3.3.2", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", - "dev": true, "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", @@ -2416,7 +2738,6 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, "dependencies": { "is-glob": "^4.0.1" }, @@ -2440,7 +2761,6 @@ "version": "1.15.0", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", - "dev": true, "dependencies": { "reusify": "^1.0.4" } @@ -2466,7 +2786,6 @@ "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "dev": true, "dependencies": { "to-regex-range": "^5.0.1" }, @@ -2567,14 +2886,12 @@ "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, "hasInstallScript": true, "optional": true, "os": [ @@ -2588,7 +2905,6 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "dev": true, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -2635,6 +2951,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/get-nonce": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-nonce/-/get-nonce-1.0.1.tgz", + "integrity": "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==", + "engines": { + "node": ">=6" + } + }, "node_modules/get-symbol-description": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", @@ -2687,7 +3011,6 @@ "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, "dependencies": { "is-glob": "^4.0.3" }, @@ -2849,7 +3172,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", - "dev": true, "dependencies": { "function-bind": "^1.1.2" }, @@ -2908,7 +3230,6 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "dev": true, "dependencies": { "once": "^1.3.0", "wrappy": "1" @@ -2917,8 +3238,7 @@ "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, "node_modules/internal-slot": { "version": "1.0.6", @@ -2934,6 +3254,14 @@ "node": ">= 0.4" } }, + "node_modules/invariant": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "dependencies": { + "loose-envify": "^1.0.0" + } + }, "node_modules/is-array-buffer": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz", @@ -2984,7 +3312,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, "dependencies": { "binary-extensions": "^2.0.0" }, @@ -3024,7 +3351,6 @@ "version": "2.13.1", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", - "dev": true, "dependencies": { "hasown": "^2.0.0" }, @@ -3051,7 +3377,6 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -3087,7 +3412,6 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, "dependencies": { "is-extglob": "^2.1.1" }, @@ -3120,7 +3444,6 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, "engines": { "node": ">=0.12.0" } @@ -3294,7 +3617,6 @@ "version": "1.21.0", "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.0.tgz", "integrity": "sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q==", - "dev": true, "bin": { "jiti": "bin/jiti.js" } @@ -3422,7 +3744,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==", - "dev": true, "engines": { "node": ">=10" } @@ -3430,8 +3751,7 @@ "node_modules/lines-and-columns": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "dev": true + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" }, "node_modules/locate-path": { "version": "6.0.0", @@ -3481,11 +3801,18 @@ "node": ">=10" } }, + "node_modules/lucide-react": { + "version": "0.424.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.424.0.tgz", + "integrity": "sha512-x2Nj2aytk1iOyHqt4hKenfVlySq0rYxNeEf8hE0o+Yh0iE36Rqz0rkngVdv2uQtjZ70LAE73eeplhhptYt9x4Q==", + "peerDependencies": { + "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0-rc" + } + }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true, "engines": { "node": ">= 8" } @@ -3494,7 +3821,6 @@ "version": "4.0.5", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", - "dev": true, "dependencies": { "braces": "^3.0.2", "picomatch": "^2.3.1" @@ -3526,7 +3852,6 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, "dependencies": { "brace-expansion": "^1.1.7" }, @@ -3553,7 +3878,6 @@ "version": "2.7.0", "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", - "dev": true, "dependencies": { "any-promise": "^1.0.0", "object-assign": "^4.0.1", @@ -3638,7 +3962,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -3656,7 +3979,6 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -3665,7 +3987,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", - "dev": true, "engines": { "node": ">= 6" } @@ -3783,7 +4104,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, "dependencies": { "wrappy": "1" } @@ -3860,7 +4180,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -3877,8 +4196,7 @@ "node_modules/path-parse": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" }, "node_modules/path-type": { "version": "4.0.0", @@ -3904,7 +4222,6 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, "engines": { "node": ">=8.6" }, @@ -3916,7 +4233,6 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -3925,7 +4241,6 @@ "version": "4.0.6", "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", - "dev": true, "engines": { "node": ">= 6" } @@ -3961,7 +4276,6 @@ "version": "15.1.0", "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", - "dev": true, "dependencies": { "postcss-value-parser": "^4.0.0", "read-cache": "^1.0.0", @@ -3978,7 +4292,6 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz", "integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==", - "dev": true, "dependencies": { "camelcase-css": "^2.0.1" }, @@ -3997,7 +4310,6 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.1.tgz", "integrity": "sha512-vEJIc8RdiBRu3oRAI0ymerOn+7rPuMvRXslTvZUKZonDHFIczxztIyJ1urxM1x9JXEikvpWWTUUqal5j/8QgvA==", - "dev": true, "dependencies": { "lilconfig": "^2.0.5", "yaml": "^2.1.1" @@ -4026,7 +4338,6 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.0.1.tgz", "integrity": "sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ==", - "dev": true, "dependencies": { "postcss-selector-parser": "^6.0.11" }, @@ -4045,7 +4356,6 @@ "version": "6.0.13", "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.13.tgz", "integrity": "sha512-EaV1Gl4mUEV4ddhDnv/xtj7sxwrwxdetHdWUGnT4VJQf+4d05v6lHYZr8N573k5Z0BViss7BDhfWtKS3+sfAqQ==", - "dev": true, "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -4057,8 +4367,7 @@ "node_modules/postcss-value-parser": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", - "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", - "dev": true + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" }, "node_modules/prelude-ls": { "version": "1.2.1", @@ -4098,7 +4407,6 @@ "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true, "funding": [ { "type": "github", @@ -4161,17 +4469,91 @@ "react-dom": ">=16" } }, + "node_modules/react-icons": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.2.1.tgz", + "integrity": "sha512-zdbW5GstTzXaVKvGSyTaBalt7HSfuK5ovrzlpyiWHAFXndXTdd/1hdDHI4xBM1Mn7YriT6aqESucFl9kEXzrdw==", + "peerDependencies": { + "react": "*" + } + }, "node_modules/react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", "dev": true }, + "node_modules/react-remove-scroll": { + "version": "2.5.7", + "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.5.7.tgz", + "integrity": "sha512-FnrTWO4L7/Bhhf3CYBNArEG/yROV0tKmTv7/3h9QCFvH6sndeFf1wPqOcbFVu5VAulS5dV1wGT3GZZ/1GawqiA==", + "dependencies": { + "react-remove-scroll-bar": "^2.3.4", + "react-style-singleton": "^2.2.1", + "tslib": "^2.1.0", + "use-callback-ref": "^1.3.0", + "use-sidecar": "^1.1.2" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/react-remove-scroll-bar": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.6.tgz", + "integrity": "sha512-DtSYaao4mBmX+HDo5YWYdBWQwYIQQshUV/dVxFxK+KM26Wjwp1gZ6rv6OC3oujI6Bfu6Xyg3TwK533AQutsn/g==", + "dependencies": { + "react-style-singleton": "^2.2.1", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/react-style-singleton": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.1.tgz", + "integrity": "sha512-ZWj0fHEMyWkHzKYUr2Bs/4zU6XLmq9HsgBURm7g5pAVfyn49DgUiNgY2d4lXRlYSiCif9YBGpQleewkcqddc7g==", + "dependencies": { + "get-nonce": "^1.0.0", + "invariant": "^2.2.4", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/read-cache": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", - "dev": true, "dependencies": { "pify": "^2.3.0" } @@ -4180,7 +4562,6 @@ "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, "dependencies": { "picomatch": "^2.2.1" }, @@ -4234,7 +4615,6 @@ "version": "1.22.8", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", - "dev": true, "dependencies": { "is-core-module": "^2.13.0", "path-parse": "^1.0.7", @@ -4269,7 +4649,6 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "dev": true, "engines": { "iojs": ">=1.0.0", "node": ">=0.10.0" @@ -4303,7 +4682,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, "funding": [ { "type": "github", @@ -4645,7 +5023,6 @@ "version": "3.34.0", "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.34.0.tgz", "integrity": "sha512-70/LQEZ07TEcxiU2dz51FKaE6hCTWC6vr7FOk3Gr0U60C3shtAN+H+BFr9XlYe5xqf3RA8nrc+VIwzCfnxuXJw==", - "dev": true, "dependencies": { "@jridgewell/gen-mapping": "^0.3.2", "commander": "^4.0.0", @@ -4667,7 +5044,6 @@ "version": "7.1.6", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "dev": true, "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -4699,7 +5075,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true, "engines": { "node": ">= 0.4" }, @@ -4716,11 +5091,19 @@ "node": ">=12.0.0" } }, + "node_modules/tailwind-merge": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-2.4.0.tgz", + "integrity": "sha512-49AwoOQNKdqKPd9CViyH5wJoSKsCDjUlzL8DxuGp3P1FsGY36NJDAa18jLZcaHAUUuTj+JB8IAo8zWgBNvBF7A==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/dcastil" + } + }, "node_modules/tailwindcss": { "version": "3.3.5", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.3.5.tgz", "integrity": "sha512-5SEZU4J7pxZgSkv7FP1zY8i2TIAOooNZ1e/OGtxIEv6GltpoiXUqWvLy89+a10qYTB1N5Ifkuw9lqQkN9sscvA==", - "dev": true, "dependencies": { "@alloc/quick-lru": "^5.2.0", "arg": "^5.0.2", @@ -4753,6 +5136,14 @@ "node": ">=14.0.0" } }, + "node_modules/tailwindcss-animate": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/tailwindcss-animate/-/tailwindcss-animate-1.0.7.tgz", + "integrity": "sha512-bl6mpH3T7I3UFxuvDEXLxy/VuFxBk5bbzplh7tXI68mwMokNYd1t9qPBHlnyTwfa4JGC4zP516I1hYYtQ/vspA==", + "peerDependencies": { + "tailwindcss": ">=3.0.0 || insiders" + } + }, "node_modules/tapable": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", @@ -4781,7 +5172,6 @@ "version": "3.3.1", "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", - "dev": true, "dependencies": { "any-promise": "^1.0.0" } @@ -4790,7 +5180,6 @@ "version": "1.6.0", "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", - "dev": true, "dependencies": { "thenify": ">= 3.1.0 < 4" }, @@ -4802,7 +5191,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, "dependencies": { "is-number": "^7.0.0" }, @@ -4825,8 +5213,7 @@ "node_modules/ts-interface-checker": { "version": "0.1.13", "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", - "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", - "dev": true + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==" }, "node_modules/tsconfig-paths": { "version": "3.14.2", @@ -5007,11 +5394,51 @@ "punycode": "^2.1.0" } }, + "node_modules/use-callback-ref": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.2.tgz", + "integrity": "sha512-elOQwe6Q8gqZgDA8mrh44qRTQqpIHDcZ3hXTLjBe1i4ph8XpNJnO+aQf3NaG+lriLopI4HMx9VjQLfPQ6vhnoA==", + "dependencies": { + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/use-sidecar": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.2.tgz", + "integrity": "sha512-epTbsLuzZ7lPClpz2TyryBfztm7m+28DlEv2ZCQ3MDr5ssiwyOwGH/e5F9CkfWjJ1t4clvI58yF822/GUkjjhw==", + "dependencies": { + "detect-node-es": "^1.1.0", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "^16.9.0 || ^17.0.0 || ^18.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "dev": true + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" }, "node_modules/utrie": { "version": "1.0.2", @@ -5022,6 +5449,18 @@ "base64-arraybuffer": "^1.0.2" } }, + "node_modules/vaul": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/vaul/-/vaul-0.9.1.tgz", + "integrity": "sha512-fAhd7i4RNMinx+WEm6pF3nOl78DFkAazcN04ElLPFF9BMCNGbY/kou8UMhIcicm0rJCNePJP0Yyza60gGOD0Jw==", + "dependencies": { + "@radix-ui/react-dialog": "^1.0.4" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -5116,8 +5555,7 @@ "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" }, "node_modules/yallist": { "version": "4.0.0", @@ -5128,7 +5566,6 @@ "version": "2.3.4", "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.4.tgz", "integrity": "sha512-8aAvwVUSHpfEqTQ4w/KMlf3HcRdt50E5ODIQJBw1fQ5RL34xabzxtUlzTXVqc4rkZsPbvrXKWnABCD7kWSmocA==", - "dev": true, "engines": { "node": ">= 14" } diff --git a/web/package.json b/web/package.json index 70f69fe3..d65870d4 100644 --- a/web/package.json +++ b/web/package.json @@ -9,15 +9,23 @@ "lint": "next lint" }, "dependencies": { + "@radix-ui/react-dialog": "^1.1.1", "@types/lodash": "^4.14.202", "axios": "^1.6.2", + "class-variance-authority": "^0.7.0", + "clsx": "^2.1.1", "jspdf": "^2.5.1", "lodash": "^4.17.21", + "lucide-react": "^0.424.0", "next": "14.1.1", "react": "^18", "react-dom": "^18", "react-hot-toast": "^2.4.1", - "sharp": "^0.33.0" + "react-icons": "^5.2.1", + "sharp": "^0.33.0", + "tailwind-merge": "^2.4.0", + "tailwindcss-animate": "^1.0.7", + "vaul": "^0.9.1" }, "devDependencies": { "@types/node": "^20", diff --git a/web/tailwind.config.ts b/web/tailwind.config.ts index 00420ff4..11a1149a 100644 --- a/web/tailwind.config.ts +++ b/web/tailwind.config.ts @@ -1,22 +1,30 @@ import type { Config } from 'tailwindcss'; -const config: Config = { +const config = { + darkMode: ['class'], content: [ - './pages/**/*.{js,ts,jsx,tsx,mdx}', - './components/**/*.{js,ts,jsx,tsx,mdx}', - './app/**/*.{js,ts,jsx,tsx,mdx}', + './pages/**/*.{ts,tsx}', + './components/**/*.{ts,tsx}', + './app/**/*.{ts,tsx}', + './src/**/*.{ts,tsx}', ], + prefix: '', theme: { + container: { + center: true, + padding: '2rem', + screens: { + '2xl': '1400px', + }, + }, + extend: { backgroundImage: { 'gradient-radial': 'radial-gradient(var(--tw-gradient-stops))', 'gradient-conic': 'conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))', }, - fontFamily: { - sans: ['var(--font-poppins)'], - mono: ['var(--font-chivo-mono)'], - }, + colors: { primary: '#13AF5E', secondary: '#4080F4', @@ -24,15 +32,72 @@ const config: Config = { primary: '#F6F6F6', secondary: '#E9E9E9', tertiary: '#D9D9D9', - } + }, + border: 'hsl(var(--border))', + input: 'hsl(var(--input))', + ring: 'hsl(var(--ring))', + background: 'hsl(var(--background))', + foreground: 'hsl(var(--foreground))', + // primary: { + // DEFAULT: 'hsl(var(--primary))', + // foreground: 'hsl(var(--primary-foreground))', + // }, + // secondary: { + // DEFAULT: 'hsl(var(--secondary))', + // foreground: 'hsl(var(--secondary-foreground))', + // }, + destructive: { + DEFAULT: 'hsl(var(--destructive))', + foreground: 'hsl(var(--destructive-foreground))', + }, + muted: { + DEFAULT: 'hsl(var(--muted))', + foreground: 'hsl(var(--muted-foreground))', + }, + accent: { + DEFAULT: 'hsl(var(--accent))', + foreground: 'hsl(var(--accent-foreground))', + }, + popover: { + DEFAULT: 'hsl(var(--popover))', + foreground: 'hsl(var(--popover-foreground))', + }, + card: { + DEFAULT: 'hsl(var(--card))', + foreground: 'hsl(var(--card-foreground))', + }, + }, + borderRadius: { + lg: 'var(--radius)', + md: 'calc(var(--radius) - 2px)', + sm: 'calc(var(--radius) - 4px)', + }, + fontFamily: { + sans: ['var(--font-poppins)'], + mono: ['var(--font-chivo-mono)'], + }, + keyframes: { + 'accordion-down': { + from: { height: '0' }, + to: { height: 'var(--radix-accordion-content-height)' }, + }, + 'accordion-up': { + from: { height: 'var(--radix-accordion-content-height)' }, + to: { height: '0' }, + }, + }, + animation: { + 'accordion-down': 'accordion-down 0.2s ease-out', + 'accordion-up': 'accordion-up 0.2s ease-out', }, screens: { flat: { - raw: '(max-height: 365px)' - } - } + raw: '(max-height: 365px)', + }, + }, }, }, - plugins: [], -}; + plugins: [require('tailwindcss-animate')], +} satisfies Config; + export default config; From 7a7ac1e15682ffd9fea992105a259f4e7dcbb017 Mon Sep 17 00:00:00 2001 From: David William Date: Thu, 8 Aug 2024 15:00:17 -0300 Subject: [PATCH 02/16] revert: changes in api & scripts page at commit 349fb67 --- api/.env-e | 29 ----------------------------- docker-compose.yml | 4 ---- scripts/config.sh | 2 +- scripts/env.sh | 2 +- 4 files changed, 2 insertions(+), 35 deletions(-) delete mode 100644 api/.env-e diff --git a/api/.env-e b/api/.env-e deleted file mode 100644 index a2d1b575..00000000 --- a/api/.env-e +++ /dev/null @@ -1,29 +0,0 @@ -# Configuração Django -# Gerar secret key: -# python3 -c 'from django.core.management.utils import get_random_secret_key; print(get_random_secret_key())' -DJANGO_SECRET_KEY="s0KORJPRaFEh06Z8OOhqO3Xu6UXAaNSweoB4YEc9YcIrIehESiWaK6jL4fR99k9s" -SETTINGS_FILE_PATH="core.settings.dev" - -# Banco de Dados -DB_ENGINE="django.db.backends.postgresql" -DB_NAME="postgres" -DB_USERNAME="suagradeunb" -DB_PASSWORD="suagradeunb" -DB_HOSTNAME="locahost" -DB_PORT=5432 - -# PostgreSQL -POSTGRES_DB="postgres" -POSTGRES_USER="suagradeunb" -POSTGRES_PASSWORD="suagradeunb" - -# Redis -REDIS_CACHE_LOCATION="redis://localhost:6379/1" - -# Credenciais de acesso ao admin -ADMIN_NAME="admin" -ADMIN_PASS="admin" -ADMIN_EMAIL="admin@gmail.com" - -# Google OAuth2 Mock -GOOGLE_OAUTH2_MOCK_TOKEN="your_google_oauth2_mock_token" diff --git a/docker-compose.yml b/docker-compose.yml index bed72758..67b51be6 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -6,14 +6,10 @@ services: - postgres_data:/var/lib/postgresql/data/ env_file: - ./api/.env - ports: - - 5432:5432 redis: image: redis:alpine container_name: rediscache - ports: - - 6379:6379 api: restart: unless-stopped diff --git a/scripts/config.sh b/scripts/config.sh index 43503f97..7f33f36d 100644 --- a/scripts/config.sh +++ b/scripts/config.sh @@ -1,3 +1,3 @@ #!/bin/bash -sed -i -e "s/your_google_oauth2_mock_token/$(python3 -c 'import string; import random; print("".join(random.SystemRandom().choice(string.ascii_uppercase + string.ascii_lowercase + string.digits) for _ in range(64)))')/g" ./api/.env +sed -i "s/your_google_oauth2_mock_token/$(python3 -c 'import string; import random; print("".join(random.SystemRandom().choice(string.ascii_uppercase + string.ascii_lowercase + string.digits) for _ in range(64)))')/g" ./api/.env diff --git a/scripts/env.sh b/scripts/env.sh index dbf2a7b6..54bc6e25 100644 --- a/scripts/env.sh +++ b/scripts/env.sh @@ -1,3 +1,3 @@ #!/bin/bash -sed -i -e "s/your_secret_key/$(python3 -c 'import string; import random; print("".join(random.SystemRandom().choice(string.ascii_uppercase + string.ascii_lowercase + string.digits) for _ in range(64)))')/g" ./api/.env +sed -i "s/your_secret_key/$(python3 -c 'import string; import random; print("".join(random.SystemRandom().choice(string.ascii_uppercase + string.ascii_lowercase + string.digits) for _ in range(64)))')/g" ./api/.env From 7d22ee3e8606a491c9597533dfdf259383396b2b Mon Sep 17 00:00:00 2001 From: David William Date: Thu, 8 Aug 2024 15:17:46 -0300 Subject: [PATCH 03/16] feat: delete unsed aside schedule pop up files after refactory --- .../AsideSchedulePopUp/AsideSchedulePopUp.tsx | 99 ------------------- .../AsideSchedulePopUp/ClassInfoBox.tsx | 18 +--- web/app/components/ui/dialog.tsx | 2 +- web/app/components/ui/drawer.tsx | 2 +- web/app/contexts/ShowPopUpContentContext.tsx | 22 ----- web/app/hooks/useShowPopUpContent.ts | 10 -- .../home/handlers/ClickOutsideHandler.tsx | 36 ------- web/app/schedules/home/page.tsx | 18 ---- web/app/utils/utils.ts | 6 ++ 9 files changed, 9 insertions(+), 204 deletions(-) delete mode 100644 web/app/components/AsideSchedulePopUp/AsideSchedulePopUp.tsx delete mode 100644 web/app/contexts/ShowPopUpContentContext.tsx delete mode 100644 web/app/hooks/useShowPopUpContent.ts delete mode 100644 web/app/schedules/home/handlers/ClickOutsideHandler.tsx create mode 100644 web/app/utils/utils.ts diff --git a/web/app/components/AsideSchedulePopUp/AsideSchedulePopUp.tsx b/web/app/components/AsideSchedulePopUp/AsideSchedulePopUp.tsx deleted file mode 100644 index 5d374159..00000000 --- a/web/app/components/AsideSchedulePopUp/AsideSchedulePopUp.tsx +++ /dev/null @@ -1,99 +0,0 @@ -import { useEffect, useState } from 'react'; -import useSelectedClasses from '@/app/hooks/useSelectedClasses'; -import useShowPopUpContent from '@/app/hooks/useShowPopUpContent'; - -import { ClassType, DisciplineType } from '@/app/utils/api/searchDiscipline'; -import { ClassValueType } from '@/app/contexts/SelectedClassesContext/types'; - -import DisciplineOptionForm from './Form/DisciplineOptionForm'; -import DisciplineFragment from './DisciplineFragment'; - -import { AsideSchedulePopUpJSXPropsType, AsideSchedulePopUpPropsType, CheckDisciplineObjPropsType } from './types/types'; - -import _ from 'lodash'; - -function checkDisciplineObj(props: CheckDisciplineObjPropsType) { - const { disciplineObj, newSelectedClasses, classId, disciplineId, newClassInfo } = props; - - if (disciplineObj.has(classId)) { - disciplineObj.delete(classId); - - if (!disciplineObj.size) newSelectedClasses.delete(disciplineId); - } else disciplineObj.set(classId, newClassInfo); -} - -function createNewClassInfo(props: { cls: ClassType, discipline: DisciplineType }) { - const { cls, discipline } = props; - - return { - class: cls, - discipline: { - id: discipline.id, - name: discipline.name, - code: discipline.code - } - }; -} - -function AsideSchedulePopUpJSX(props: AsideSchedulePopUpJSXPropsType) { - const { showPopUpContent, searchedDisciplineInfos, setSearchedDisciplineInfos, handleSelectClass } = props; - - return ( -
-
- {showPopUpContent && -
- -
- {searchedDisciplineInfos.map((discipline, index) => - - )} -
-
- } -
-
- ); -} - -export default function AsideSchedulePopUp(props: AsideSchedulePopUpPropsType) { - const { showPopUpContent } = useShowPopUpContent(); - const { selectedClasses, setSelectedClasses } = useSelectedClasses(); - const [searchedDisciplineInfos, setSearchedDisciplineInfos] = useState>([]); - - useEffect(() => { - if (!showPopUpContent) setSearchedDisciplineInfos([]); - }, [showPopUpContent, setSearchedDisciplineInfos]); - - function handleSelectClass(discipline: DisciplineType, cls: ClassType) { - const disciplineId = discipline.id; - const classId = cls.id; - - const newSelectedClasses = _.cloneDeep(selectedClasses); - const disciplineObj = newSelectedClasses.get(disciplineId); - - const newClassInfo = createNewClassInfo({ cls, discipline }); - - if (disciplineObj) checkDisciplineObj({ disciplineObj, newSelectedClasses, classId, disciplineId, newClassInfo }); - else { - const newDiscipline = new Map(); - newDiscipline.set(classId, newClassInfo); - - newSelectedClasses.set(disciplineId, newDiscipline); - } - - setSelectedClasses(newSelectedClasses); - } - - return AsideSchedulePopUpJSX({ - divAddClassRef: props.divAddClassRef, - showPopUpContent, searchedDisciplineInfos, setSearchedDisciplineInfos, - handleSelectClass - }); -} \ No newline at end of file diff --git a/web/app/components/AsideSchedulePopUp/ClassInfoBox.tsx b/web/app/components/AsideSchedulePopUp/ClassInfoBox.tsx index 7479b27a..ac6c148e 100644 --- a/web/app/components/AsideSchedulePopUp/ClassInfoBox.tsx +++ b/web/app/components/AsideSchedulePopUp/ClassInfoBox.tsx @@ -53,23 +53,7 @@ export default function ClassInfoBox({ } className="hover:cursor-pointer col-start-7 flex justify-center items-center" > - {selected ? ( - - ) : ( - - // ícone remover matéria - // ícone adicionar matéria - )} + {selected ? : }
); diff --git a/web/app/components/ui/dialog.tsx b/web/app/components/ui/dialog.tsx index 3c6feb6a..1fd4571b 100644 --- a/web/app/components/ui/dialog.tsx +++ b/web/app/components/ui/dialog.tsx @@ -4,7 +4,7 @@ import * as React from 'react'; import * as DialogPrimitive from '@radix-ui/react-dialog'; import { X } from 'lucide-react'; -import { cn } from '@/app/lib/utils'; +import { cn } from '@/app/utils/utils'; const Dialog = DialogPrimitive.Root; diff --git a/web/app/components/ui/drawer.tsx b/web/app/components/ui/drawer.tsx index bdae9da1..54244d66 100644 --- a/web/app/components/ui/drawer.tsx +++ b/web/app/components/ui/drawer.tsx @@ -3,7 +3,7 @@ import * as React from 'react'; import { Drawer as DrawerPrimitive } from 'vaul'; -import { cn } from '@/app/lib/utils'; +import { cn } from '@/app/utils/utils'; const Drawer = ({ shouldScaleBackground = true, diff --git a/web/app/contexts/ShowPopUpContentContext.tsx b/web/app/contexts/ShowPopUpContentContext.tsx deleted file mode 100644 index 8cdf56b5..00000000 --- a/web/app/contexts/ShowPopUpContentContext.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import { createContext, useState } from 'react'; - -interface ShowPopUpContentContextType { - showPopUpContent: boolean; - setShowPopUpContent: React.Dispatch>; -} - -interface ShowPopUpContentContextProviderPropsType { - children: React.ReactNode; -} - -export const ShowPopUpContentContext = createContext({} as ShowPopUpContentContextType); - -export default function ShowPopUpContentContextProvider({ children, ...props }: ShowPopUpContentContextProviderPropsType) { - const [showPopUpContent, setShowPopUpContent] = useState(false); - - return ( - - {children} - - ); -} \ No newline at end of file diff --git a/web/app/hooks/useShowPopUpContent.ts b/web/app/hooks/useShowPopUpContent.ts deleted file mode 100644 index 97b2f149..00000000 --- a/web/app/hooks/useShowPopUpContent.ts +++ /dev/null @@ -1,10 +0,0 @@ -'use client'; - -import { useContext } from 'react'; -import { ShowPopUpContentContext } from '../contexts/ShowPopUpContentContext'; - -export default function useShowPopUpContent() { - const value = useContext(ShowPopUpContentContext); - - return value; -} \ No newline at end of file diff --git a/web/app/schedules/home/handlers/ClickOutsideHandler.tsx b/web/app/schedules/home/handlers/ClickOutsideHandler.tsx deleted file mode 100644 index 502263ac..00000000 --- a/web/app/schedules/home/handlers/ClickOutsideHandler.tsx +++ /dev/null @@ -1,36 +0,0 @@ -'use client'; - -import { useEffect } from 'react'; - -import useShowPopUpContent from '@/app/hooks/useShowPopUpContent'; - -interface ClickOutsideHandlerPropsType { - children: React.ReactNode; - refs: { - divAddClassRef: React.MutableRefObject; - buttonAddDisciplineRef: React.MutableRefObject; - } -} - -export default function ClickOutsideHandler({ children, ...props }: ClickOutsideHandlerPropsType) { - const { setShowPopUpContent } = useShowPopUpContent(); - const { divAddClassRef, buttonAddDisciplineRef } = props.refs; - - useEffect(() => { - const handleClickOutside = (event: Event) => { - const element: Node = event.target as Node; - - if (divAddClassRef.current && buttonAddDisciplineRef.current) { - const checkButtonAddDiscipline: boolean = buttonAddDisciplineRef.current == element; - const checkDivAddClass: boolean = (divAddClassRef.current as HTMLElement).contains(element); - - if (!checkButtonAddDiscipline && !checkDivAddClass) setShowPopUpContent(false); - } - }; - document.addEventListener('click', handleClickOutside); - - return () => document.removeEventListener('click', handleClickOutside); - }, [buttonAddDisciplineRef, divAddClassRef, setShowPopUpContent]); - - return children; -} \ No newline at end of file diff --git a/web/app/schedules/home/page.tsx b/web/app/schedules/home/page.tsx index f8db0842..d542c024 100644 --- a/web/app/schedules/home/page.tsx +++ b/web/app/schedules/home/page.tsx @@ -3,25 +3,10 @@ import { Fragment, useEffect, useRef } from 'react'; import useSelectedClasses from '@/app/hooks/useSelectedClasses'; import useClassesToShow from '@/app/hooks/useClassesToShow'; - import DisciplineBox from '@/app/components/DisciplineBox'; -import AsideSchedulePopUp from '@/app/components/AsideSchedulePopUp/AsideSchedulePopUp'; import GenerateScheduleButton from './components/GenerateScheduleButton'; -import AddDisciplineButton from './components/AddDisciplineButton'; - -import ClickOutsideHandler from './handlers/ClickOutsideHandler'; - -import ShowPopUpContentContextProvider from '@/app/contexts/ShowPopUpContentContext'; - import { ClassValueType } from '@/app/contexts/SelectedClassesContext/types'; -import { - Drawer, - DrawerContent, - DrawerOverlay, - DrawerTrigger, -} from '@/app/components/ui/drawer'; import { ScheduleDrawer } from '@/app/components/AsideSchedulePopUp/ScheduleDrawer'; -import { ScheduleDrawerContent } from '@/app/components/AsideSchedulePopUp/ScheduleDrawerContent'; function DisciplineBlockJSX({ ...props @@ -64,9 +49,6 @@ export default function Home() { const { classesToShow, setClassesToShow } = useClassesToShow(); const { classesChange, selectedClasses } = useSelectedClasses(); - const divAddClassRef = useRef(null), - buttonAddDisciplineRef = useRef(null); - useEffect(() => { const newClasses = new Array(); selectedClasses.forEach((cls) => diff --git a/web/app/utils/utils.ts b/web/app/utils/utils.ts new file mode 100644 index 00000000..9ad0df42 --- /dev/null +++ b/web/app/utils/utils.ts @@ -0,0 +1,6 @@ +import { type ClassValue, clsx } from 'clsx'; +import { twMerge } from 'tailwind-merge'; + +export function cn(...inputs: ClassValue[]) { + return twMerge(clsx(inputs)); +} From d9d0f665c2317dc67c12c00d0fa3e38db82c8635 Mon Sep 17 00:00:00 2001 From: David William Date: Thu, 8 Aug 2024 20:13:23 -0300 Subject: [PATCH 04/16] feat(refactory): generate schedule dialog --- web/app/components/AsideButton.tsx | 4 - .../AsideSchedulePopUp/ClassInfoBox.tsx | 2 +- .../AsideSchedulePopUp/DisciplineFragment.tsx | 119 +++-- .../Form/DisciplineOptionForm.tsx | 173 ++++--- .../AsideSchedulePopUp/ScheduleDrawer.tsx | 3 +- .../ScheduleDrawerContent.tsx | 2 +- web/app/components/DisciplineBox.tsx | 15 +- web/app/components/Modal/Dialog.tsx | 46 -- .../SchedulePreview/SchedulePreview.tsx | 451 +++++++++++------- web/app/components/ui/dialog.tsx | 4 +- web/app/components/ui/drawer.tsx | 12 +- web/app/components/ui/input.tsx | 36 ++ web/app/components/ui/select.tsx | 160 +++++++ web/app/components/ui/tooltip.tsx | 30 ++ web/app/globals.css | 91 ++-- web/app/layout.tsx | 9 +- web/app/page.tsx | 22 +- .../components/GenerateScheduleButton.tsx | 206 ++++---- web/app/schedules/home/page.tsx | 2 +- web/app/schedules/layout.tsx | 13 +- web/app/schedules/profile/page.tsx | 138 +++--- web/components.json | 2 +- web/package-lock.json | 283 +++++++++++ web/package.json | 2 + 24 files changed, 1245 insertions(+), 580 deletions(-) delete mode 100644 web/app/components/Modal/Dialog.tsx create mode 100644 web/app/components/ui/input.tsx create mode 100644 web/app/components/ui/select.tsx create mode 100644 web/app/components/ui/tooltip.tsx diff --git a/web/app/components/AsideButton.tsx b/web/app/components/AsideButton.tsx index 351dcbfd..ab0d1ed6 100644 --- a/web/app/components/AsideButton.tsx +++ b/web/app/components/AsideButton.tsx @@ -23,10 +23,6 @@ export default function AsideButton({ name={pageName} > {image} - {/* {`ícone */} ); } diff --git a/web/app/components/AsideSchedulePopUp/ClassInfoBox.tsx b/web/app/components/AsideSchedulePopUp/ClassInfoBox.tsx index ac6c148e..579eaef4 100644 --- a/web/app/components/AsideSchedulePopUp/ClassInfoBox.tsx +++ b/web/app/components/AsideSchedulePopUp/ClassInfoBox.tsx @@ -33,7 +33,7 @@ export default function ClassInfoBox({
diff --git a/web/app/components/AsideSchedulePopUp/DisciplineFragment.tsx b/web/app/components/AsideSchedulePopUp/DisciplineFragment.tsx index 71f9abbb..9f970f68 100644 --- a/web/app/components/AsideSchedulePopUp/DisciplineFragment.tsx +++ b/web/app/components/AsideSchedulePopUp/DisciplineFragment.tsx @@ -6,69 +6,82 @@ import { ClassType, DisciplineType } from '@/app/utils/api/searchDiscipline'; import expand_more from '@/public/icons/expand_more.png'; import expand_less from '@/public/icons/expand_less.png'; +import { FiChevronDown, FiChevronUp } from 'react-icons/fi'; interface DisciplineFragmentPropsType { - index: number, - discipline: DisciplineType, - disciplineInfos: { - searchedDisciplineInfos: Array, - setSearchedDisciplineInfos: React.Dispatch> - } - handleSelectClass: (discipline: DisciplineType, cls: ClassType) => void + index: number; + discipline: DisciplineType; + disciplineInfos: { + searchedDisciplineInfos: Array; + setSearchedDisciplineInfos: React.Dispatch< + React.SetStateAction + >; + }; + handleSelectClass: (discipline: DisciplineType, cls: ClassType) => void; } -function DisciplineFragmentJSX({ handleDisciplineToggle, ...props }: { - handleDisciplineToggle: (index: number) => void, - props: DisciplineFragmentPropsType +function DisciplineFragmentJSX({ + handleDisciplineToggle, + ...props +}: { + handleDisciplineToggle: (index: number) => void; + props: DisciplineFragmentPropsType; }) { - const { index, discipline, handleSelectClass } = props.props; + const { index, discipline, handleSelectClass } = props.props; - return ( - - - {discipline.expanded && -
- {discipline.classes.map((cls, index) => - handleSelectClass(discipline, cls)} - /> - )} -
- } -
- ); + return ( + + + {discipline.expanded && ( +
+ {discipline.classes.map((cls, index) => ( + handleSelectClass(discipline, cls)} + /> + ))} +
+ )} +
+ ); } export default function DisciplineFragment(props: DisciplineFragmentPropsType) { - const { searchedDisciplineInfos, setSearchedDisciplineInfos } = props.disciplineInfos; + const { searchedDisciplineInfos, setSearchedDisciplineInfos } = + props.disciplineInfos; - function handleDisciplineToggle(index: number) { - let newInfo: Array = []; - searchedDisciplineInfos.forEach((discipline, number) => { - if (number != index) newInfo.push({ - ...discipline, - expanded: false - }); - else newInfo.push({ - ...discipline, - expanded: !discipline.expanded - }); + function handleDisciplineToggle(index: number) { + let newInfo: Array = []; + searchedDisciplineInfos.forEach((discipline, number) => { + if (number != index) + newInfo.push({ + ...discipline, + expanded: false, + }); + else + newInfo.push({ + ...discipline, + expanded: !discipline.expanded, }); + }); - setSearchedDisciplineInfos(newInfo); - } + setSearchedDisciplineInfos(newInfo); + } - return DisciplineFragmentJSX({ handleDisciplineToggle, props }); -} \ No newline at end of file + return DisciplineFragmentJSX({ handleDisciplineToggle, props }); +} diff --git a/web/app/components/AsideSchedulePopUp/Form/DisciplineOptionForm.tsx b/web/app/components/AsideSchedulePopUp/Form/DisciplineOptionForm.tsx index 016c614b..9a2815fe 100644 --- a/web/app/components/AsideSchedulePopUp/Form/DisciplineOptionForm.tsx +++ b/web/app/components/AsideSchedulePopUp/Form/DisciplineOptionForm.tsx @@ -1,89 +1,138 @@ -import { ChangeEvent, Dispatch, SetStateAction, useEffect, useState } from 'react'; +import { + ChangeEvent, + Dispatch, + SetStateAction, + useEffect, + useState, +} from 'react'; import useYearPeriod from '@/app/hooks/useYearPeriod'; import useSelectedClasses from '@/app/hooks/useSelectedClasses'; import { errorToast } from '@/app/utils/toast'; -import { DisciplineOptionFormPropsType, FormType, defaultFormData } from '../types/types'; +import { + DisciplineOptionFormPropsType, + FormType, + defaultFormData, +} from '../types/types'; import { DisciplineType } from '@/app/utils/api/searchDiscipline'; import InputForm from './InputForm'; +import { + Select, + SelectContent, + SelectGroup, + SelectItem, + SelectTrigger, + SelectValue, +} from '../../ui/select'; interface FormPropsType { - form: FormType, - disableDefault: boolean, - setInfos: Dispatch>>, - handleYearAndPeriodChange: (event: ChangeEvent) => void, + form: FormType; + disableDefault: boolean; + setInfos: Dispatch>>; + handleYearAndPeriodChange: (event: ChangeEvent) => void; } function Form(props: FormPropsType) { - const { formData, setFormData } = props.form; - const { disableDefault, handleYearAndPeriodChange } = props; + const { formData, setFormData } = props.form; + const { disableDefault, handleYearAndPeriodChange } = props; - const { periods } = useYearPeriod(); + const { periods } = useYearPeriod(); - return ( - <> -
- Ano/Período - -
-
- Matéria - -
- - ); + return ( + <> +
+ Ano/Período + +
+
+ Matéria + +
+ + ); } function getYearAndPeriod(text: string) { - const year = text.split('/')[0] || defaultFormData.year; - const period = text.split('/')[1] || defaultFormData.period; - - return { year, period }; + const year = text.split('/')[0] || defaultFormData.year; + const period = text.split('/')[1] || defaultFormData.period; + + return { year, period }; } -function handleChangeYearAndPeriod(text: string, currentYearPeriod: string, selectedClasses: any, handleSetYearPeriod: () => void) { - if (currentYearPeriod && selectedClasses && currentYearPeriod != text) { - errorToast('Há disciplinas selecionadas de outro período, não pode haver mistura!'); - } else handleSetYearPeriod(); +function handleChangeYearAndPeriod( + text: string, + currentYearPeriod: string, + selectedClasses: any, + handleSetYearPeriod: () => void +) { + if (currentYearPeriod && selectedClasses && currentYearPeriod != text) { + errorToast( + 'Há disciplinas selecionadas de outro período, não pode haver mistura!' + ); + } else handleSetYearPeriod(); } -export default function DisciplineOptionForm(props: DisciplineOptionFormPropsType) { - const { selectedClasses, currentYearPeriod, setCurrentYearPeriod } = useSelectedClasses(); - const [disableDefault, setDisableDefault] = useState(false); - const [formData, setFormData] = useState(defaultFormData); +export default function DisciplineOptionForm( + props: DisciplineOptionFormPropsType +) { + const { selectedClasses, currentYearPeriod, setCurrentYearPeriod } = + useSelectedClasses(); + const [disableDefault, setDisableDefault] = useState(false); + const [formData, setFormData] = useState(defaultFormData); - useEffect(() => { - if (!disableDefault && formData == defaultFormData && selectedClasses.size) { - const { year, period } = getYearAndPeriod(currentYearPeriod); - setFormData({ ...formData, year: year, period: period }); - setDisableDefault(true); - } - }, [disableDefault, currentYearPeriod, formData, selectedClasses]); + useEffect(() => { + if ( + !disableDefault && + formData == defaultFormData && + selectedClasses.size + ) { + const { year, period } = getYearAndPeriod(currentYearPeriod); + setFormData({ ...formData, year: year, period: period }); + setDisableDefault(true); + } + }, [disableDefault, currentYearPeriod, formData, selectedClasses]); - function handleYearAndPeriodChange(event: ChangeEvent) { - const text = event.target.value.trim(); - const { year, period } = getYearAndPeriod(text); + function handleYearAndPeriodChange(event: ChangeEvent) { + const text = event.target.value.trim(); + const { year, period } = getYearAndPeriod(text); - if (year && period) { - const handleSetYearPeriod = () => { - setFormData({ ...formData, year: year, period: period }); - props.setInfos([]); - setCurrentYearPeriod(text); - setDisableDefault(true); - }; - handleChangeYearAndPeriod(text, currentYearPeriod, selectedClasses.size, handleSetYearPeriod); - } + if (year && period) { + const handleSetYearPeriod = () => { + setFormData({ ...formData, year: year, period: period }); + props.setInfos([]); + setCurrentYearPeriod(text); + setDisableDefault(true); + }; + handleChangeYearAndPeriod( + text, + currentYearPeriod, + selectedClasses.size, + handleSetYearPeriod + ); } + } - return ; -} \ No newline at end of file + return ( + + ); +} diff --git a/web/app/components/AsideSchedulePopUp/ScheduleDrawer.tsx b/web/app/components/AsideSchedulePopUp/ScheduleDrawer.tsx index c75ef826..703f3ab5 100644 --- a/web/app/components/AsideSchedulePopUp/ScheduleDrawer.tsx +++ b/web/app/components/AsideSchedulePopUp/ScheduleDrawer.tsx @@ -15,8 +15,9 @@ export const ScheduleDrawer = () => { diff --git a/web/app/components/AsideSchedulePopUp/ScheduleDrawerContent.tsx b/web/app/components/AsideSchedulePopUp/ScheduleDrawerContent.tsx index 97cfadb1..1827edbc 100644 --- a/web/app/components/AsideSchedulePopUp/ScheduleDrawerContent.tsx +++ b/web/app/components/AsideSchedulePopUp/ScheduleDrawerContent.tsx @@ -42,7 +42,7 @@ export const ScheduleDrawerContent = () => { } return ( -
+
diff --git a/web/app/components/DisciplineBox.tsx b/web/app/components/DisciplineBox.tsx index 47f31bd4..cbab3582 100644 --- a/web/app/components/DisciplineBox.tsx +++ b/web/app/components/DisciplineBox.tsx @@ -1,17 +1,8 @@ -import Image from 'next/image'; - import { ClassValueType } from '../contexts/SelectedClassesContext/types'; - import useSelectedClasses from '../hooks/useSelectedClasses'; - import ClassInfo from './ClassInfo'; - -import deleteIcon from '../../public/icons/delete.jpg'; - import { FiTrash2 } from 'react-icons/fi'; - import _ from 'lodash'; - interface DisciplineBoxPropsType { currentClass: ClassValueType; discipline: { @@ -44,12 +35,8 @@ export default function DisciplineBox({
- ); diff --git a/web/app/components/Modal/Dialog.tsx b/web/app/components/Modal/Dialog.tsx deleted file mode 100644 index 8568f059..00000000 --- a/web/app/components/Modal/Dialog.tsx +++ /dev/null @@ -1,46 +0,0 @@ -"use client"; - -import React, { createContext, useState } from "react"; - -interface ModalContextType { - activeModal: boolean; - setActiveModal: (value: boolean) => void; -} - -const ModalContext = createContext({ - activeModal: false, - setActiveModal: (value: boolean) => {}, -}); - -const ModalProvider = ({ children }) => { - const [activeModal, setActiveModal] = useState(false); - - return ( - - {children} - - ); -}; - -const DialogContent = React.forwardRef void }>( - { - children, - noExit, - setActiveModal, - } -) => { - return ( -
-
- {children} - {!noExit && ( - - )} -
-
- ); -}; - -export { ModalProvider, ModalContext }; diff --git a/web/app/components/SchedulePreview/SchedulePreview.tsx b/web/app/components/SchedulePreview/SchedulePreview.tsx index b805258d..320e556a 100644 --- a/web/app/components/SchedulePreview/SchedulePreview.tsx +++ b/web/app/components/SchedulePreview/SchedulePreview.tsx @@ -4,7 +4,10 @@ import { useEffect, useState } from 'react'; import useSchedules from '@/app/hooks/useSchedules'; import useUser from '@/app/hooks/useUser'; -import { CloudScheduleType, ScheduleClassType } from '@/app/contexts/SchedulesContext'; +import { + CloudScheduleType, + ScheduleClassType, +} from '@/app/contexts/SchedulesContext'; import Image from 'next/image'; import Modal from '../Modal/Modal'; @@ -22,226 +25,312 @@ import { errorToast, successToast } from '@/app/utils/toast'; import jsPDF from 'jspdf'; import { AxiosError } from 'axios'; +import { FiCloud, FiDownload, FiTrash2, FiUploadCloud } from 'react-icons/fi'; +import { Tooltip, TooltipContent, TooltipTrigger } from '../ui/tooltip'; -const commonError = () => errorToast('Houve um erro na atualização das grades!'); +const commonError = () => + errorToast('Houve um erro na atualização das grades!'); function DeleteButton({ - setActiveDeleteModal, + setActiveDeleteModal, }: { - setActiveDeleteModal: (value: boolean) => void; + setActiveDeleteModal: (value: boolean) => void; }) { - return ( + return ( + + - ); + + + Deletar + + + ); } function DeleteModalHandler(props: { - deleteModal: { - activeDeleteModal: boolean; - setActiveDeleteModal: (value: boolean) => void; - } - isCloud?: boolean; - index: number; - deleteHandler: { - handleDeleteCloud: () => Promise; - handleDeleteLocal: () => void; - } + deleteModal: { + activeDeleteModal: boolean; + setActiveDeleteModal: (value: boolean) => void; + }; + isCloud?: boolean; + index: number; + deleteHandler: { + handleDeleteCloud: () => Promise; + handleDeleteLocal: () => void; + }; }) { - async function handleDelete() { - if (!props.isCloud) props.deleteHandler.handleDeleteLocal(); - else await props.deleteHandler.handleDeleteCloud(); - props.deleteModal.setActiveDeleteModal(false); - } + async function handleDelete() { + if (!props.isCloud) props.deleteHandler.handleDeleteLocal(); + else await props.deleteHandler.handleDeleteCloud(); + props.deleteModal.setActiveDeleteModal(false); + } - return props.deleteModal.activeDeleteModal && - -
-

A grade será deletada para sempre, tem certeza?

-
- - -
-
-
; + return ( + props.deleteModal.activeDeleteModal && ( + +
+

+ A grade será deletada para sempre, tem certeza? +

+
+ + +
+
+
+ ) + ); } function handleDate(created_at: string) { - const date = new Date(created_at); + const date = new Date(created_at); - const currentDay = date.getDate().toString(); - const currentMonth = months[date.getMonth()]; - const currentWeekDay = days[date.getDay()]; + const currentDay = date.getDate().toString(); + const currentMonth = months[date.getMonth()]; + const currentWeekDay = days[date.getDay()]; - return `${currentWeekDay}, ${currentDay} de ${currentMonth}`; + return `${currentWeekDay}, ${currentDay} de ${currentMonth}`; } function BottomPart(props: { - schedules: { - localSchedule?: Array; - cloudSchedule?: CloudScheduleType; - } - index: number; - position: number; - isCloud?: boolean; - handleDelete: () => void; - setters: { - setActiveScheduleModal: (value: boolean) => void; - setActiveDeleteModal: (value: boolean) => void; - setToDownload: (value: boolean) => void; - } + schedules: { + localSchedule?: Array; + cloudSchedule?: CloudScheduleType; + }; + index: number; + position: number; + isCloud?: boolean; + handleDelete: () => void; + setters: { + setActiveScheduleModal: (value: boolean) => void; + setActiveDeleteModal: (value: boolean) => void; + setToDownload: (value: boolean) => void; + }; }) { - const { user } = useUser(); - - const [changeDate, setChangeDate] = useState(''); - - useEffect(() => { - if (props.isCloud && props.schedules.cloudSchedule?.created_at) { - setChangeDate(handleDate(props.schedules.cloudSchedule.created_at)); - } - }, [props.isCloud, props.schedules.cloudSchedule?.created_at]); - - return ( -
-
- Grade {props.position}
- {props.isCloud && changeDate && {changeDate}} -
-
- {!props.isCloud && !user.is_anonymous && } - - -
-
- ); + const { user } = useUser(); + + const [changeDate, setChangeDate] = useState(''); + + useEffect(() => { + if (props.isCloud && props.schedules.cloudSchedule?.created_at) { + setChangeDate(handleDate(props.schedules.cloudSchedule.created_at)); + } + }, [props.isCloud, props.schedules.cloudSchedule?.created_at]); + + return ( +
+
+

+ Grade {props.position} +

+ {props.isCloud && changeDate && ( +

{changeDate}

+ )} +
+
+ {!props.isCloud && !user.is_anonymous && ( + + )} + + + + + + Baixar + + + +
+
+ ); } -function UploadToCloudButton({ ...props }: { - schedules: { - localSchedule?: Array; - }; - handleDelete: () => void; +function UploadToCloudButton({ + ...props +}: { + schedules: { + localSchedule?: Array; + }; + handleDelete: () => void; }) { - const { user } = useUser(); - const { setCloudSchedules } = useSchedules(); - - function handleSuccessToSave() { - getSchedules(user.access).then(response => { - props.handleDelete(); - setCloudSchedules(response.data); - }).catch(() => commonError()); - successToast('Grade salva com sucesso!'); - } + const { user } = useUser(); + const { setCloudSchedules } = useSchedules(); - function handleErrorToSave(axiosError: AxiosError) { - if (axiosError.response) { - const data = axiosError.response.data as { errors: string }; - errorToast(data.errors); - } - } + function handleSuccessToSave() { + getSchedules(user.access) + .then((response) => { + props.handleDelete(); + setCloudSchedules(response.data); + }) + .catch(() => commonError()); + successToast('Grade salva com sucesso!'); + } - /** - * Tenta salvar a grade na nuvem, se der certo, atualiza as grades locais. - * Caso contrário, exibe errorToast com mensagem retornada pela API. - */ - async function handleUploadToCloud() { - saveSchedule(props.schedules.localSchedule, user.access).then(response => { - if (response.status == 201) handleSuccessToSave(); - }).catch((error: any) => handleErrorToSave(error)); + function handleErrorToSave(axiosError: AxiosError) { + if (axiosError.response) { + const data = axiosError.response.data as { errors: string }; + errorToast(data.errors); } + } + + /** + * Tenta salvar a grade na nuvem, se der certo, atualiza as grades locais. + * Caso contrário, exibe errorToast com mensagem retornada pela API. + */ + async function handleUploadToCloud() { + saveSchedule(props.schedules.localSchedule, user.access) + .then((response) => { + if (response.status == 201) handleSuccessToSave(); + }) + .catch((error: any) => handleErrorToSave(error)); + } - return ( + return ( + + - ); + + + Salvar na nuvem + + + ); } function handleDownloadPDF(isCloud: boolean, index: number) { - const doc = document.getElementById('download-content')!; - - const pdfManager = new jsPDF('l', 'pt', 'a4'); - pdfManager.html(doc, { - callback: function (doc) { - doc.save(`schedule-${isCloud ? 'cloud' : 'local'}-${index + 1}.pdf`); - }, - x: 0, y: 0, - width: 1150, windowWidth: 2000, - }); + const doc = document.getElementById('download-content')!; + + const pdfManager = new jsPDF('l', 'pt', 'a4'); + pdfManager.html(doc, { + callback: function (doc) { + doc.save(`schedule-${isCloud ? 'cloud' : 'local'}-${index + 1}.pdf`); + }, + x: 0, + y: 0, + width: 1150, + windowWidth: 2000, + }); } -export default function SchedulePreview({ localSchedule, cloudSchedule, index, position, isCloud = false }: { - localSchedule?: Array; - cloudSchedule?: CloudScheduleType; - index: number; - position: number; - isCloud?: boolean; +export default function SchedulePreview({ + localSchedule, + cloudSchedule, + index, + position, + isCloud = false, +}: { + localSchedule?: Array; + cloudSchedule?: CloudScheduleType; + index: number; + position: number; + isCloud?: boolean; }) { - const { user } = useUser(); - const { localSchedules, setCloudSchedules, setLocalSchedules } = useSchedules(); + const { user } = useUser(); + const { localSchedules, setCloudSchedules, setLocalSchedules } = + useSchedules(); - const [toDownload, setToDownload] = useState(false); - const [activeScheduleModal, setActiveScheduleModal] = useState(false); - const [activeDeleteModal, setActiveDeleteModal] = useState(false); + const [toDownload, setToDownload] = useState(false); + const [activeScheduleModal, setActiveScheduleModal] = useState(false); + const [activeDeleteModal, setActiveDeleteModal] = useState(false); - async function handleDeleteCloud() { - const response = await deleteSchedule(cloudSchedule?.id, user.access); + async function handleDeleteCloud() { + const response = await deleteSchedule(cloudSchedule?.id, user.access); - if (response.status == 204) { - getSchedules(user.access).then(response => { - setCloudSchedules(response.data); - }).catch(() => commonError()); - } else errorToast('Não foi possível deletar a grade na nuvem!'); - } + if (response.status == 204) { + getSchedules(user.access) + .then((response) => { + setCloudSchedules(response.data); + }) + .catch(() => commonError()); + } else errorToast('Não foi possível deletar a grade na nuvem!'); + } + + function handleDeleteLocal() { + const newLocalSchedules = [...localSchedules]; + newLocalSchedules.splice(index, 1); + setLocalSchedules(newLocalSchedules, false); + } - function handleDeleteLocal() { - const newLocalSchedules = [...localSchedules]; - newLocalSchedules.splice(index, 1); - setLocalSchedules(newLocalSchedules, false); + useEffect(() => { + if (toDownload && activeScheduleModal) { + setTimeout(() => { + handleDownloadPDF(isCloud, index); + setActiveScheduleModal(false); + setToDownload(false); + }, 75); } + }, [toDownload, activeScheduleModal, isCloud, index]); - useEffect(() => { - if (toDownload && activeScheduleModal) { - setTimeout(() => { - handleDownloadPDF(isCloud, index); - setActiveScheduleModal(false); - setToDownload(false); - }, 75); - } - }, [toDownload, activeScheduleModal, isCloud, index]); - - return ( - <> -
-
{ - if (!activeScheduleModal) setActiveScheduleModal(true); - }} - className='flex justify-center items-center bg-snow-tertiary h-48 rounded-3xl'> - - {activeScheduleModal && - - - - } -
- -
- - - ); + return ( + <> +
+
{ + if (!activeScheduleModal) setActiveScheduleModal(true); + }} + className="flex justify-center items-center bg-snow-tertiary h-48 rounded-3xl" + > + + {activeScheduleModal && ( + + + + )} +
+ +
+ + + ); } diff --git a/web/app/components/ui/dialog.tsx b/web/app/components/ui/dialog.tsx index 1fd4571b..a1687211 100644 --- a/web/app/components/ui/dialog.tsx +++ b/web/app/components/ui/dialog.tsx @@ -26,10 +26,10 @@ const DialogOverlay = React.forwardRef< ref={ref} className={cn( 'fixed inset-0 z-50 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0', - className, { 'bg-black/80': showOverlay, - } + }, + className )} {...props} /> diff --git a/web/app/components/ui/drawer.tsx b/web/app/components/ui/drawer.tsx index 54244d66..a5402150 100644 --- a/web/app/components/ui/drawer.tsx +++ b/web/app/components/ui/drawer.tsx @@ -22,13 +22,13 @@ const DrawerPortal = DrawerPrimitive.Portal; const DrawerClose = DrawerPrimitive.Close; -type DialogOverlayProps = { +type DrawerOverlayProps = { showOverlay?: boolean; } & React.ComponentPropsWithoutRef; const DrawerOverlay = React.forwardRef< React.ElementRef, - DialogOverlayProps + DrawerOverlayProps >(({ className, showOverlay = true, ...props }, ref) => ( & { showOverlay?: boolean; + hideDragIndicator?: boolean; }; const DrawerContent = React.forwardRef< React.ElementRef, DrawerContentProps ->(({ className, children, showOverlay, ...props }, ref) => ( +>(({ className, children, showOverlay, hideDragIndicator, ...props }, ref) => ( -
+ {!hideDragIndicator && ( +
+ )} + {children} diff --git a/web/app/components/ui/input.tsx b/web/app/components/ui/input.tsx new file mode 100644 index 00000000..5df89608 --- /dev/null +++ b/web/app/components/ui/input.tsx @@ -0,0 +1,36 @@ +import * as React from 'react'; +import { cn } from '@/app/utils/utils'; + +const InputRoot = ({ + children, +}: { + children: JSX.Element | JSX.Element[]; +}): JSX.Element => { + return ( +
+ {children} +
+ ); +}; + +export interface InputProps + extends React.InputHTMLAttributes {} + +const Input = React.forwardRef( + ({ className, ...props }, ref) => { + return ( + + + + ); + } +); + +Input.displayName = 'Input'; diff --git a/web/app/components/ui/select.tsx b/web/app/components/ui/select.tsx new file mode 100644 index 00000000..de60acf6 --- /dev/null +++ b/web/app/components/ui/select.tsx @@ -0,0 +1,160 @@ +'use client'; + +import * as React from 'react'; +import * as SelectPrimitive from '@radix-ui/react-select'; +import { Check, ChevronDown, ChevronUp } from 'lucide-react'; + +import { cn } from '@/app/utils/utils'; + +const Select = SelectPrimitive.Root; + +const SelectGroup = SelectPrimitive.Group; + +const SelectValue = SelectPrimitive.Value; + +const SelectTrigger = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + span]:line-clamp-1', + className + )} + {...props} + > + {children} + + + + +)); +SelectTrigger.displayName = SelectPrimitive.Trigger.displayName; + +const SelectScrollUpButton = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + + + +)); +SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName; + +const SelectScrollDownButton = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + + + +)); +SelectScrollDownButton.displayName = + SelectPrimitive.ScrollDownButton.displayName; + +const SelectContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, position = 'popper', ...props }, ref) => ( + + + + + {children} + + + + +)); +SelectContent.displayName = SelectPrimitive.Content.displayName; + +const SelectLabel = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +SelectLabel.displayName = SelectPrimitive.Label.displayName; + +const SelectItem = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + + + + + + + + {children} + +)); +SelectItem.displayName = SelectPrimitive.Item.displayName; + +const SelectSeparator = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +SelectSeparator.displayName = SelectPrimitive.Separator.displayName; + +export { + Select, + SelectGroup, + SelectValue, + SelectTrigger, + SelectContent, + SelectLabel, + SelectItem, + SelectSeparator, + SelectScrollUpButton, + SelectScrollDownButton, +}; diff --git a/web/app/components/ui/tooltip.tsx b/web/app/components/ui/tooltip.tsx new file mode 100644 index 00000000..adcb5d0d --- /dev/null +++ b/web/app/components/ui/tooltip.tsx @@ -0,0 +1,30 @@ +'use client'; + +import * as React from 'react'; +import * as TooltipPrimitive from '@radix-ui/react-tooltip'; + +import { cn } from '@/app/utils/utils'; + +const TooltipProvider = TooltipPrimitive.Provider; + +const Tooltip = TooltipPrimitive.Root; + +const TooltipTrigger = TooltipPrimitive.Trigger; + +const TooltipContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, sideOffset = 4, ...props }, ref) => ( + +)); +TooltipContent.displayName = TooltipPrimitive.Content.displayName; + +export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider }; diff --git a/web/app/globals.css b/web/app/globals.css index 30f1a06d..7681a8e9 100644 --- a/web/app/globals.css +++ b/web/app/globals.css @@ -4,49 +4,6 @@ @tailwind components; @tailwind utilities; - -:root { - --main-green: hsl(149, 80%, 38%); - --main-white: hsl(0, 0%, 96%); -} - -* { - box-sizing: border-box; - padding: 0; - margin: 0; -} - -::-webkit-scrollbar { - width: 4px; - height: 4px; -} - -::-webkit-scrollbar-track { - height: 4px; - background: var(--main-white); -} - -::-webkit-scrollbar-thumb { - background: var(--main-green); - border-radius: 5px; -} - -body { - scrollbar-width: thin; - scrollbar-color: var(--main-green) var(--main-white); -} - -body::-moz-scrollbar-thumb { - background-color: var(--main-green); - border-radius: 5px; -} - -body::-moz-scrollbar-track { - background-color: var(--main-white); -} - - - @layer base { :root { --background: 0 0% 100%; @@ -74,6 +31,8 @@ body::-moz-scrollbar-track { --chart-3: 197 37% 24%; --chart-4: 43 74% 66%; --chart-5: 27 87% 67%; + --main-green: 149, 80%, 38%; + --main-white: 0, 0%, 96%; } .dark { @@ -101,9 +60,55 @@ body::-moz-scrollbar-track { --chart-3: 30 80% 55%; --chart-4: 280 65% 60%; --chart-5: 340 75% 55%; + --main-green: 149, 80%, 38%; + --main-white: 0, 0%, 96%; } } + +/* +:root { + --main-green: hsl(149, 80%, 38%); + --main-white: hsl(0, 0%, 96%); +} */ + +* { + box-sizing: border-box; + padding: 0; + margin: 0; +} + +::-webkit-scrollbar { + width: 4px; + height: 4px; +} + +::-webkit-scrollbar-track { + height: 4px; + background: var(--main-white); +} + +::-webkit-scrollbar-thumb { + background: hsl(var(--main-green)); + border-radius: 5px; +} + +body { + scrollbar-width: thin; + scrollbar-color: hsl(var(--main-green)) hsl(var(--main-white)); +} + +body::-moz-scrollbar-thumb { + background-color: hsl(var(--main-green)); + border-radius: 5px; +} + +body::-moz-scrollbar-track { + background-color: hsl(var(--main-white)); +} + + + /* @layer base { * { @apply border-border; diff --git a/web/app/layout.tsx b/web/app/layout.tsx index ea886a33..46bc77a7 100644 --- a/web/app/layout.tsx +++ b/web/app/layout.tsx @@ -10,6 +10,7 @@ import SelectedClassesContextProvider from './contexts/SelectedClassesContext/Se import SchedulesContextProvider from './contexts/SchedulesContext'; import { Toaster } from 'react-hot-toast'; +import { TooltipProvider } from './components/ui/tooltip'; export const poppins = Poppins({ weight: ['100', '200', '300', '400', '500', '600', '700', '800', '900'], @@ -33,18 +34,20 @@ export const metadata: Metadata = { export default function RootLayout({ children, }: { - children: React.ReactNode + children: React.ReactNode; }) { return ( - + - {children} + {children} diff --git a/web/app/page.tsx b/web/app/page.tsx index 8b9d95fd..d4cd620f 100644 --- a/web/app/page.tsx +++ b/web/app/page.tsx @@ -14,23 +14,29 @@ export default function Welcome() { if (isLoading) return ; return ( -
+
-
+
- Bem-vindos ao -

+ Bem-vindos ao +

Sua Grade UnB

-
-

A ferramenta definitiva para os alunos da Universidade de Brasília planejarem suas trajetórias acadêmicas!

+
+

+ A ferramenta definitiva para os alunos da Universidade de Brasília + planejarem suas trajetórias acadêmicas! +

-
+
- + Continuar como anônimo
diff --git a/web/app/schedules/home/components/GenerateScheduleButton.tsx b/web/app/schedules/home/components/GenerateScheduleButton.tsx index b43887e1..1854ea7d 100644 --- a/web/app/schedules/home/components/GenerateScheduleButton.tsx +++ b/web/app/schedules/home/components/GenerateScheduleButton.tsx @@ -8,104 +8,142 @@ import useSchedules from '@/app/hooks/useSchedules'; import useUser from '@/app/hooks/useUser'; import Button from '@/app/components/Button'; -import Modal from '@/app/components/Modal/Modal'; -import { ScheduleAPIType, ScheduleClassType } from '@/app/contexts/SchedulesContext'; +import { + ScheduleAPIType, + ScheduleClassType, +} from '@/app/contexts/SchedulesContext'; -import generateSchedule, { EachFieldNumber } from '@/app/utils/api/generateSchedule'; +import generateSchedule, { + EachFieldNumber, +} from '@/app/utils/api/generateSchedule'; import { errorToast } from '@/app/utils/toast'; +import { + Dialog, + DialogClose, + DialogContent, + DialogFooter, + DialogTitle, + DialogTrigger, +} from '@/app/components/ui/dialog'; -function PreferenceOrder({ setPreference }: { - setPreference: (preference: EachFieldNumber) => void +function PreferenceOrder({ + setPreference, +}: { + setPreference: (preference: EachFieldNumber) => void; }) { - return ( - - ); + return ( + + ); } export default function GenerateScheduleButton() { - const router = useRouter(); + const router = useRouter(); - const { setLoading } = useUser(); - const { setLocalSchedules } = useSchedules(); - const { selectedClasses } = useSelectedClasses(); - const { classesToShow } = useClassesToShow(); + const { setLoading } = useUser(); + const { setLocalSchedules } = useSchedules(); + const { selectedClasses } = useSelectedClasses(); + const { classesToShow } = useClassesToShow(); - const [activeModal, setActiveModal] = useState(false); - const [morningPreference, setMorningPreference] = useState(1); - const [afternoonPreference, setAfternoonPreference] = useState(1); - const [nightPreference, setNightPreference] = useState(1); + const [morningPreference, setMorningPreference] = + useState(1); + const [afternoonPreference, setAfternoonPreference] = + useState(1); + const [nightPreference, setNightPreference] = useState(1); - const createSchedule = () => { - let classes_id = new Array(); - selectedClasses.forEach((_class) => { - Array.from(_class.keys()).forEach((key) => { - classes_id.push(key); - }); - }); + const createSchedule = () => { + let classes_id = new Array(); + selectedClasses.forEach((_class) => { + Array.from(_class.keys()).forEach((key) => { + classes_id.push(key); + }); + }); - setLoading(true); - generateSchedule(classes_id, [morningPreference, afternoonPreference, nightPreference]).then((response) => { - if (response.status === 200) { - const data = response.data as ScheduleAPIType; - const schedules = data.schedules as Array; + setLoading(true); + generateSchedule(classes_id, [ + morningPreference, + afternoonPreference, + nightPreference, + ]).then((response) => { + if (response.status === 200) { + const data = response.data as ScheduleAPIType; + const schedules = data.schedules as Array; - if (!schedules.length) { - errorToast(data.message, {centered: data.message.split('\n').length == 1}); - setLoading(false); - } else { - setLocalSchedules(schedules); - router.replace('/schedules/mygrades'); - setTimeout(() => { - setLoading(false); - }, 500); - } - } else { - errorToast('Não foi possível gerar as grades, tente novamente mais tarde!'); - setLoading(false); - } - }); - }; + if (!schedules.length) { + errorToast(data.message, { + centered: data.message.split('\n').length == 1, + }); + setLoading(false); + } else { + setLocalSchedules(schedules); + router.replace('/schedules/mygrades'); + setTimeout(() => { + setLoading(false); + }, 500); + } + } else { + errorToast( + 'Não foi possível gerar as grades, tente novamente mais tarde!' + ); + setLoading(false); + } + }); + }; - return ( - <> - {activeModal && - -
-

Período de preferência

+ return ( + <> + + +
+ +
+
-
-
- Manhã - Tarde - Noite -
-
- - - -
-
+ + Período de preferência -
- - -
-
-
- } -
- +
+
+
+ Manhã + Tarde + Noite +
+
+ + + +
- - ); -} \ No newline at end of file +
+ + +
+ + + + +
+
+ + + + ); +} diff --git a/web/app/schedules/home/page.tsx b/web/app/schedules/home/page.tsx index d542c024..71fb7791 100644 --- a/web/app/schedules/home/page.tsx +++ b/web/app/schedules/home/page.tsx @@ -1,6 +1,6 @@ 'use client'; -import { Fragment, useEffect, useRef } from 'react'; +import { Fragment, useEffect } from 'react'; import useSelectedClasses from '@/app/hooks/useSelectedClasses'; import useClassesToShow from '@/app/hooks/useClassesToShow'; import DisciplineBox from '@/app/components/DisciplineBox'; diff --git a/web/app/schedules/layout.tsx b/web/app/schedules/layout.tsx index e9ab3629..5b8b6a72 100644 --- a/web/app/schedules/layout.tsx +++ b/web/app/schedules/layout.tsx @@ -4,25 +4,14 @@ import { usePathname, useRouter } from 'next/navigation'; import { useCallback, useState } from 'react'; import useUser from '@/app/hooks/useUser'; import useWindowDimensions from '../hooks/useWindowDimensions'; - import Image from 'next/image'; import Button from '../components/Button'; import AsideButton from '../components/AsideButton'; import Protected from '../components/Protected'; import InfoHeader from '../components/InfoHeader'; import { LoadingScreen } from '../components/LoadingScreen'; - -import homeIcon from '@/public/icons/home.jpg'; -import infoIcon from '@/public/icons/info.jpg'; import googleIcon from '@/public/icons/google.jpg'; -import scheduleIcon from '@/public/icons/schedule.jpg'; -import profileIcon from '@/public/icons/profile.jpg'; -import { - RiMenuLine, - RiCalendarLine, - RiUserLine, - RiInformation2Line, -} from 'react-icons/ri'; +import { RiCalendarLine } from 'react-icons/ri'; import { FiHome, FiInfo, FiUser } from 'react-icons/fi'; function calculatePositionOfBlob( diff --git a/web/app/schedules/profile/page.tsx b/web/app/schedules/profile/page.tsx index f978fa28..c7c173d4 100644 --- a/web/app/schedules/profile/page.tsx +++ b/web/app/schedules/profile/page.tsx @@ -19,69 +19,89 @@ import handleLogout from '@/app/utils/api/logout'; import useSchedules from '@/app/hooks/useSchedules'; export default function Profile() { - const [activeModal, setActiveModal] = useState(false); + const [activeModal, setActiveModal] = useState(false); - const { setClassesToShow } = useClassesToShow(); - const { setSelectedClasses } = useSelectedClasses(); - const { localSchedules, setLocalSchedules, setCloudSchedules } = useSchedules(); + const { setClassesToShow } = useClassesToShow(); + const { setSelectedClasses } = useSelectedClasses(); + const { localSchedules, setLocalSchedules, setCloudSchedules } = + useSchedules(); - const userContext = useUser(); - const { user } = userContext; - const router = useRouter(); + const userContext = useUser(); + const { user } = userContext; + const router = useRouter(); - function handleLogoutAndRedirect() { - setClassesToShow(new Array()); - setSelectedClasses(new Map()); - setLocalSchedules(new Array(), false); - setCloudSchedules(new Array()); - handleLogout({ userContext, router }); - } + function handleLogoutAndRedirect() { + setClassesToShow(new Array()); + setSelectedClasses(new Map()); + setLocalSchedules(new Array(), false); + setCloudSchedules(new Array()); + handleLogout({ userContext, router }); + } - return ( - <> - {activeModal && - -
-

Você tem grades não salvas na nuvem e vai perdê-las!

-
-

Tem certeza que quer sair?

-
- - -
-
-
-
- } - Pessoa marcando datas em um calendário com borda azul -
- Foto de perfil do usuário -
-
- - +
- - ); -} \ No newline at end of file +
+ + )} + Pessoa marcando datas em um calendário com borda azul + +
+ Foto de perfil do usuário +
+
+ + +
+ + ); +} diff --git a/web/components.json b/web/components.json index e997d58b..c671be76 100644 --- a/web/components.json +++ b/web/components.json @@ -12,6 +12,6 @@ }, "aliases": { "components": "@/app/components", - "utils": "@/app/lib/utils" + "utils": "@/app/utils/utils" } } \ No newline at end of file diff --git a/web/package-lock.json b/web/package-lock.json index 6e40c097..6263cd03 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -9,6 +9,8 @@ "version": "0.1.0", "dependencies": { "@radix-ui/react-dialog": "^1.1.1", + "@radix-ui/react-select": "^2.1.1", + "@radix-ui/react-tooltip": "^1.1.2", "@types/lodash": "^4.14.202", "axios": "^1.6.2", "class-variance-authority": "^0.7.0", @@ -134,6 +136,40 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, + "node_modules/@floating-ui/core": { + "version": "1.6.7", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.7.tgz", + "integrity": "sha512-yDzVT/Lm101nQ5TCVeK65LtdN7Tj4Qpr9RTXJ2vPFLqtLxwOrpoxAHAJI8J3yYWUc40J0BDBheaitK5SJmno2g==", + "dependencies": { + "@floating-ui/utils": "^0.2.7" + } + }, + "node_modules/@floating-ui/dom": { + "version": "1.6.10", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.10.tgz", + "integrity": "sha512-fskgCFv8J8OamCmyun8MfjB1Olfn+uZKjOKZ0vhYF3gRmEUXcGOjxWL8bBr7i4kIuPZ2KD2S3EUIOxnjC8kl2A==", + "dependencies": { + "@floating-ui/core": "^1.6.0", + "@floating-ui/utils": "^0.2.7" + } + }, + "node_modules/@floating-ui/react-dom": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.1.tgz", + "integrity": "sha512-4h84MJt3CHrtG18mGsXuLCHMrug49d7DFkU0RMIyshRveBeyV2hmV/pDaF2Uxtu8kgq5r46llp5E5FQiR0K2Yg==", + "dependencies": { + "@floating-ui/dom": "^1.0.0" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@floating-ui/utils": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.7.tgz", + "integrity": "sha512-X8R8Oj771YRl/w+c1HqAC1szL8zWQRwFvgDwT129k9ACdBoud/+/rX9V0qiMl6LWUdP9voC2nDVZYPMQQsb6eA==" + }, "node_modules/@humanwhocodes/config-array": { "version": "0.11.13", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.13.tgz", @@ -822,11 +858,63 @@ "node": ">= 8" } }, + "node_modules/@radix-ui/number": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/number/-/number-1.1.0.tgz", + "integrity": "sha512-V3gRzhVNU1ldS5XhAPTom1fOIo4ccrjjJgmE+LI2h/WaFpHmx0MQApT+KZHnx8abG6Avtfcz4WoEciMnpFT3HQ==" + }, "node_modules/@radix-ui/primitive": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.0.tgz", "integrity": "sha512-4Z8dn6Upk0qk4P74xBhZ6Hd/w0mPEzOOLxy4xiPXOXqjF7jZS0VAKk7/x/H6FyY2zCkYJqePf1G5KmkmNJ4RBA==" }, + "node_modules/@radix-ui/react-arrow": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.0.tgz", + "integrity": "sha512-FmlW1rCg7hBpEBwFbjHwCW6AmWLQM6g/v0Sn8XbP9NvmSZ2San1FpQeyPtufzOMSIx7Y4dzjlHoifhp+7NkZhw==", + "dependencies": { + "@radix-ui/react-primitive": "2.0.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-collection": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.0.tgz", + "integrity": "sha512-GZsZslMJEyo1VKm5L1ZJY8tGDxZNPAoUeQUIbKeJfoi7Q4kmig5AsgLMYYuyYbfjd8fBmFORAIwYAkXMnXZgZw==", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.0", + "@radix-ui/react-context": "1.1.0", + "@radix-ui/react-primitive": "2.0.0", + "@radix-ui/react-slot": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-compose-refs": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.0.tgz", @@ -890,6 +978,20 @@ } } }, + "node_modules/@radix-ui/react-direction": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.0.tgz", + "integrity": "sha512-BUuBvgThEiAXh2DWu93XsT+a3aWrGqolGlqqw5VU1kG7p/ZH2cuDlM1sRLNnY3QcBS69UIz2mcKhMxDsdewhjg==", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-dismissable-layer": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.0.tgz", @@ -971,6 +1073,37 @@ } } }, + "node_modules/@radix-ui/react-popper": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.0.tgz", + "integrity": "sha512-ZnRMshKF43aBxVWPWvbj21+7TQCvhuULWJ4gNIKYpRlQt5xGRhLx66tMp8pya2UkGHTSlhpXwmjqltDYHhw7Vg==", + "dependencies": { + "@floating-ui/react-dom": "^2.0.0", + "@radix-ui/react-arrow": "1.1.0", + "@radix-ui/react-compose-refs": "1.1.0", + "@radix-ui/react-context": "1.1.0", + "@radix-ui/react-primitive": "2.0.0", + "@radix-ui/react-use-callback-ref": "1.1.0", + "@radix-ui/react-use-layout-effect": "1.1.0", + "@radix-ui/react-use-rect": "1.1.0", + "@radix-ui/react-use-size": "1.1.0", + "@radix-ui/rect": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-portal": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.1.tgz", @@ -1039,6 +1172,48 @@ } } }, + "node_modules/@radix-ui/react-select": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-select/-/react-select-2.1.1.tgz", + "integrity": "sha512-8iRDfyLtzxlprOo9IicnzvpsO1wNCkuwzzCM+Z5Rb5tNOpCdMvcc2AkzX0Fz+Tz9v6NJ5B/7EEgyZveo4FBRfQ==", + "dependencies": { + "@radix-ui/number": "1.1.0", + "@radix-ui/primitive": "1.1.0", + "@radix-ui/react-collection": "1.1.0", + "@radix-ui/react-compose-refs": "1.1.0", + "@radix-ui/react-context": "1.1.0", + "@radix-ui/react-direction": "1.1.0", + "@radix-ui/react-dismissable-layer": "1.1.0", + "@radix-ui/react-focus-guards": "1.1.0", + "@radix-ui/react-focus-scope": "1.1.0", + "@radix-ui/react-id": "1.1.0", + "@radix-ui/react-popper": "1.2.0", + "@radix-ui/react-portal": "1.1.1", + "@radix-ui/react-primitive": "2.0.0", + "@radix-ui/react-slot": "1.1.0", + "@radix-ui/react-use-callback-ref": "1.1.0", + "@radix-ui/react-use-controllable-state": "1.1.0", + "@radix-ui/react-use-layout-effect": "1.1.0", + "@radix-ui/react-use-previous": "1.1.0", + "@radix-ui/react-visually-hidden": "1.1.0", + "aria-hidden": "^1.1.1", + "react-remove-scroll": "2.5.7" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-slot": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.0.tgz", @@ -1056,6 +1231,39 @@ } } }, + "node_modules/@radix-ui/react-tooltip": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-tooltip/-/react-tooltip-1.1.2.tgz", + "integrity": "sha512-9XRsLwe6Yb9B/tlnYCPVUd/TFS4J7HuOZW345DCeC6vKIxQGMZdx21RK4VoZauPD5frgkXTYVS5y90L+3YBn4w==", + "dependencies": { + "@radix-ui/primitive": "1.1.0", + "@radix-ui/react-compose-refs": "1.1.0", + "@radix-ui/react-context": "1.1.0", + "@radix-ui/react-dismissable-layer": "1.1.0", + "@radix-ui/react-id": "1.1.0", + "@radix-ui/react-popper": "1.2.0", + "@radix-ui/react-portal": "1.1.1", + "@radix-ui/react-presence": "1.1.0", + "@radix-ui/react-primitive": "2.0.0", + "@radix-ui/react-slot": "1.1.0", + "@radix-ui/react-use-controllable-state": "1.1.0", + "@radix-ui/react-visually-hidden": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-use-callback-ref": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.0.tgz", @@ -1118,6 +1326,81 @@ } } }, + "node_modules/@radix-ui/react-use-previous": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-previous/-/react-use-previous-1.1.0.tgz", + "integrity": "sha512-Z/e78qg2YFnnXcW88A4JmTtm4ADckLno6F7OXotmkQfeuCVaKuYzqAATPhVzl3delXE7CxIV8shofPn3jPc5Og==", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-rect": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.1.0.tgz", + "integrity": "sha512-0Fmkebhr6PiseyZlYAOtLS+nb7jLmpqTrJyv61Pe68MKYW6OWdRE2kI70TaYY27u7H0lajqM3hSMMLFq18Z7nQ==", + "dependencies": { + "@radix-ui/rect": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-size": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.1.0.tgz", + "integrity": "sha512-XW3/vWuIXHa+2Uwcc2ABSfcCledmXhhQPlGbfcRXbiUQI5Icjcg19BGCZVKKInYbvUCut/ufbbLLPFC5cbb1hw==", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-visually-hidden": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.1.0.tgz", + "integrity": "sha512-N8MDZqtgCgG5S3aV60INAB475osJousYpZ4cTJ2cFbMpdHS5Y6loLTH8LPtkj2QN0x93J30HT/M3qJXM0+lyeQ==", + "dependencies": { + "@radix-ui/react-primitive": "2.0.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/rect": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.1.0.tgz", + "integrity": "sha512-A9+lCBZoaMJlVKcRBz2YByCG+Cp2t6nAnMnNba+XiWxnj6r4JUFqfsgwocMBZU9LPtdxC6wB56ySYpc7LQIoJg==" + }, "node_modules/@rushstack/eslint-patch": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.5.1.tgz", diff --git a/web/package.json b/web/package.json index d65870d4..1dbef60f 100644 --- a/web/package.json +++ b/web/package.json @@ -10,6 +10,8 @@ }, "dependencies": { "@radix-ui/react-dialog": "^1.1.1", + "@radix-ui/react-select": "^2.1.1", + "@radix-ui/react-tooltip": "^1.1.2", "@types/lodash": "^4.14.202", "axios": "^1.6.2", "class-variance-authority": "^0.7.0", From 63d5d4e9ab77bd19f15160a9317d382e7cb1f248 Mon Sep 17 00:00:00 2001 From: David William Date: Tue, 13 Aug 2024 21:22:13 -0300 Subject: [PATCH 05/16] feat: refactor schedule preview page; nested scroll bug fix into schedule popup drawer --- .../AsideSchedulePopUp/ScheduleDrawer.tsx | 7 +- .../ScheduleDrawerContent.tsx | 5 +- .../SchedulePreview/SchedulePreview.tsx | 355 ++++++++---------- web/app/components/ui/dialog.tsx | 2 +- web/app/layout.tsx | 2 +- web/app/schedules/layout.tsx | 5 +- web/app/schedules/mygrades/page.tsx | 108 ++++-- web/app/utils/moment.ts | 6 + web/package-lock.json | 9 + web/package.json | 1 + 10 files changed, 240 insertions(+), 260 deletions(-) create mode 100644 web/app/utils/moment.ts diff --git a/web/app/components/AsideSchedulePopUp/ScheduleDrawer.tsx b/web/app/components/AsideSchedulePopUp/ScheduleDrawer.tsx index 703f3ab5..35c44280 100644 --- a/web/app/components/AsideSchedulePopUp/ScheduleDrawer.tsx +++ b/web/app/components/AsideSchedulePopUp/ScheduleDrawer.tsx @@ -1,10 +1,5 @@ import AddDisciplineButton from '@/app/schedules/home/components/AddDisciplineButton'; -import { - Drawer, - DrawerContent, - DrawerTitle, - DrawerTrigger, -} from '../ui/drawer'; +import { Drawer, DrawerContent, DrawerTrigger } from '../ui/drawer'; import { ScheduleDrawerContent } from './ScheduleDrawerContent'; export const ScheduleDrawer = () => { diff --git a/web/app/components/AsideSchedulePopUp/ScheduleDrawerContent.tsx b/web/app/components/AsideSchedulePopUp/ScheduleDrawerContent.tsx index 1827edbc..5e99218b 100644 --- a/web/app/components/AsideSchedulePopUp/ScheduleDrawerContent.tsx +++ b/web/app/components/AsideSchedulePopUp/ScheduleDrawerContent.tsx @@ -45,7 +45,10 @@ export const ScheduleDrawerContent = () => {
-
+
{searchedDisciplines.map((discipline, index) => ( - errorToast('Houve um erro na atualização das grades!'); - -function DeleteButton({ - setActiveDeleteModal, -}: { - setActiveDeleteModal: (value: boolean) => void; -}) { - return ( - - - - - - Deletar - - - ); +interface GenericSchedule { + id?: number; + created_at: string; + classes: Array; + type: 'local' | 'cloud'; } -function DeleteModalHandler(props: { - deleteModal: { - activeDeleteModal: boolean; - setActiveDeleteModal: (value: boolean) => void; - }; - isCloud?: boolean; +export default function SchedulePreview({ + schedule, + index, + position, +}: { + schedule: GenericSchedule; index: number; - deleteHandler: { - handleDeleteCloud: () => Promise; - handleDeleteLocal: () => void; - }; + position: number; }) { + const { user } = useUser(); + const { localSchedules, setCloudSchedules, setLocalSchedules } = + useSchedules(); + + const isCloud = schedule.type === 'cloud'; + + const scheduleId = `download-content-${position}-${ + isCloud ? 'cloud' : 'local' + }`; + + async function handleDeleteCloud() { + const response = await deleteSchedule(schedule?.id, user.access); + + if (response.status == 204) { + getSchedules(user.access) + .then((response) => { + setCloudSchedules(response.data); + }) + .catch(() => commonError()); + } else errorToast('Não foi possível deletar a grade na nuvem!'); + } + + function handleDeleteLocal() { + const newLocalSchedules = [...localSchedules]; + newLocalSchedules.splice(index, 1); + setLocalSchedules(newLocalSchedules, false); + } + async function handleDelete() { - if (!props.isCloud) props.deleteHandler.handleDeleteLocal(); - else await props.deleteHandler.handleDeleteCloud(); - props.deleteModal.setActiveDeleteModal(false); + if (isCloud) await handleDeleteCloud(); + else handleDeleteLocal(); } return ( - props.deleteModal.activeDeleteModal && ( - -
-

- A grade será deletada para sempre, tem certeza? -

-
- - -
+ <> +
+
+
- - ) + + +
+ +
+
+ + + +
+ +
+ ); } -function handleDate(created_at: string) { - const date = new Date(created_at); - - const currentDay = date.getDate().toString(); - const currentMonth = months[date.getMonth()]; - const currentWeekDay = days[date.getDay()]; - - return `${currentWeekDay}, ${currentDay} de ${currentMonth}`; -} - -function BottomPart(props: { - schedules: { - localSchedule?: Array; - cloudSchedule?: CloudScheduleType; - }; - index: number; +function ActionButtons({ + schedule, + handleDelete, + position, +}: { + schedule: GenericSchedule; + handleDelete: () => Promise; position: number; - isCloud?: boolean; - handleDelete: () => void; - setters: { - setActiveScheduleModal: (value: boolean) => void; - setActiveDeleteModal: (value: boolean) => void; - setToDownload: (value: boolean) => void; - }; }) { const { user } = useUser(); - - const [changeDate, setChangeDate] = useState(''); - - useEffect(() => { - if (props.isCloud && props.schedules.cloudSchedule?.created_at) { - setChangeDate(handleDate(props.schedules.cloudSchedule.created_at)); - } - }, [props.isCloud, props.schedules.cloudSchedule?.created_at]); + const isCloud = schedule.type === 'cloud'; return (

- Grade {props.position} + Grade {position}

- {props.isCloud && changeDate && ( -

{changeDate}

+ {isCloud && schedule.created_at && ( +

+ {moment(schedule.created_at).format('dddd, DD [de] MMMM, YYYY')} +

)}
- {!props.isCloud && !user.is_anonymous && ( + {!isCloud && !user.is_anonymous && ( )} - + + + + Deletar + + + + + Deletar grade +
+

+ A grade será deletada para sempre, tem certeza? +

+ +
+ + + + + + +
+
+
+
+
); } +const commonError = () => + errorToast('Houve um erro na atualização das grades!'); + function UploadToCloudButton({ ...props }: { @@ -199,10 +232,6 @@ function UploadToCloudButton({ } } - /** - * Tenta salvar a grade na nuvem, se der certo, atualiza as grades locais. - * Caso contrário, exibe errorToast com mensagem retornada pela API. - */ async function handleUploadToCloud() { saveSchedule(props.schedules.localSchedule, user.access) .then((response) => { @@ -213,7 +242,7 @@ function UploadToCloudButton({ return ( - + @@ -226,7 +255,12 @@ function UploadToCloudButton({ } function handleDownloadPDF(isCloud: boolean, index: number) { - const doc = document.getElementById('download-content')!; + const doc = document.getElementById( + `download-content-${index}-${isCloud ? 'cloud' : 'local'}` + ); + console.log(doc, `download-content-${index}-${isCloud ? 'cloud' : 'local'}`); + + if (!doc) return; const pdfManager = new jsPDF('l', 'pt', 'a4'); pdfManager.html(doc, { @@ -239,98 +273,3 @@ function handleDownloadPDF(isCloud: boolean, index: number) { windowWidth: 2000, }); } - -export default function SchedulePreview({ - localSchedule, - cloudSchedule, - index, - position, - isCloud = false, -}: { - localSchedule?: Array; - cloudSchedule?: CloudScheduleType; - index: number; - position: number; - isCloud?: boolean; -}) { - const { user } = useUser(); - const { localSchedules, setCloudSchedules, setLocalSchedules } = - useSchedules(); - - const [toDownload, setToDownload] = useState(false); - const [activeScheduleModal, setActiveScheduleModal] = useState(false); - const [activeDeleteModal, setActiveDeleteModal] = useState(false); - - async function handleDeleteCloud() { - const response = await deleteSchedule(cloudSchedule?.id, user.access); - - if (response.status == 204) { - getSchedules(user.access) - .then((response) => { - setCloudSchedules(response.data); - }) - .catch(() => commonError()); - } else errorToast('Não foi possível deletar a grade na nuvem!'); - } - - function handleDeleteLocal() { - const newLocalSchedules = [...localSchedules]; - newLocalSchedules.splice(index, 1); - setLocalSchedules(newLocalSchedules, false); - } - - useEffect(() => { - if (toDownload && activeScheduleModal) { - setTimeout(() => { - handleDownloadPDF(isCloud, index); - setActiveScheduleModal(false); - setToDownload(false); - }, 75); - } - }, [toDownload, activeScheduleModal, isCloud, index]); - - return ( - <> -
-
{ - if (!activeScheduleModal) setActiveScheduleModal(true); - }} - className="flex justify-center items-center bg-snow-tertiary h-48 rounded-3xl" - > - - {activeScheduleModal && ( - - - - )} -
- -
- - - ); -} diff --git a/web/app/components/ui/dialog.tsx b/web/app/components/ui/dialog.tsx index a1687211..d99d5952 100644 --- a/web/app/components/ui/dialog.tsx +++ b/web/app/components/ui/dialog.tsx @@ -51,7 +51,7 @@ const DialogContent = React.forwardRef< diff --git a/web/app/schedules/layout.tsx b/web/app/schedules/layout.tsx index 5b8b6a72..d555b2fb 100644 --- a/web/app/schedules/layout.tsx +++ b/web/app/schedules/layout.tsx @@ -1,7 +1,7 @@ 'use client'; import { usePathname, useRouter } from 'next/navigation'; -import { useCallback, useState } from 'react'; +import { useCallback, useLayoutEffect, useState } from 'react'; import useUser from '@/app/hooks/useUser'; import useWindowDimensions from '../hooks/useWindowDimensions'; import Image from 'next/image'; @@ -95,6 +95,7 @@ function AsideButtonsJSX() { x: 0, width: 0, }); + const isUserAnonymous = !user.is_anonymous; const onRefChange = useCallback( (node: any) => { const props = { @@ -106,7 +107,7 @@ function AsideButtonsJSX() { }; asideRefCallback(props); }, - [path, width, footerWidth] + [path, width, footerWidth, isUserAnonymous] ); return ( diff --git a/web/app/schedules/mygrades/page.tsx b/web/app/schedules/mygrades/page.tsx index af809bfe..9eb77d8d 100644 --- a/web/app/schedules/mygrades/page.tsx +++ b/web/app/schedules/mygrades/page.tsx @@ -5,51 +5,77 @@ import useSchedules from '@/app/hooks/useSchedules'; import SchedulePreview from '@/app/components/SchedulePreview/SchedulePreview'; function RenderLocalSchedules() { - const { localSchedules } = useSchedules(); - if (!localSchedules.length) return null; - - return ( - <> -

Grades locais

-
- {localSchedules.map((schedule, index) => ( - schedule && - ))} -
- - - ); + const { localSchedules } = useSchedules(); + if (!localSchedules.length) return null; + + return ( + <> +

Grades locais

+
+ {localSchedules.map( + (schedule, index) => + schedule && ( + + ) + )} +
+ + ); } function RenderCloudSchedules() { - const { cloudSchedules } = useSchedules(); - if (!cloudSchedules.length) return null; - - return ( - <> -

Grades nuvem

-
- {cloudSchedules.map((schedule, index) => ( - schedule && - ))} -
- - ); + const { cloudSchedules } = useSchedules(); + if (!cloudSchedules.length) return null; + + return ( + <> +

+ Grades nuvem +

+
+ {cloudSchedules.map( + (schedule, index) => + schedule && ( + + ) + )} +
+ + ); } export default function MyGrades() { - const { localSchedules, cloudSchedules } = useSchedules(); - - return ( -
-

Suas Grades

- - - {localSchedules.length === 0 && cloudSchedules.length === 0 && ( -
- Você ainda não possui nenhuma grade. -
- )} + const { localSchedules, cloudSchedules } = useSchedules(); + + return ( +
+

Suas Grades

+ + + {localSchedules.length === 0 && cloudSchedules.length === 0 && ( +
+ Você ainda não possui nenhuma grade.
- ); -} \ No newline at end of file + )} +
+ ); +} diff --git a/web/app/utils/moment.ts b/web/app/utils/moment.ts new file mode 100644 index 00000000..88921520 --- /dev/null +++ b/web/app/utils/moment.ts @@ -0,0 +1,6 @@ +import moment from 'moment'; +import 'moment/locale/pt-br'; + +moment.locale('pt-br'); + +export { moment }; diff --git a/web/package-lock.json b/web/package-lock.json index 6263cd03..6db6d60c 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -18,6 +18,7 @@ "jspdf": "^2.5.1", "lodash": "^4.17.21", "lucide-react": "^0.424.0", + "moment": "^2.30.1", "next": "14.1.1", "react": "^18", "react-dom": "^18", @@ -4151,6 +4152,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "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==", + "engines": { + "node": "*" + } + }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", diff --git a/web/package.json b/web/package.json index 1dbef60f..5d9c1d15 100644 --- a/web/package.json +++ b/web/package.json @@ -19,6 +19,7 @@ "jspdf": "^2.5.1", "lodash": "^4.17.21", "lucide-react": "^0.424.0", + "moment": "^2.30.1", "next": "14.1.1", "react": "^18", "react-dom": "^18", From 889d408e781edd7318d5a0d47087db18809e4f30 Mon Sep 17 00:00:00 2001 From: David William Date: Sun, 18 Aug 2024 00:20:58 -0300 Subject: [PATCH 06/16] feat: refactor select into generate schedule preference order --- .../components/GenerateScheduleButton.tsx | 31 +++++++++++++------ 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/web/app/schedules/home/components/GenerateScheduleButton.tsx b/web/app/schedules/home/components/GenerateScheduleButton.tsx index 1854ea7d..ee98f72d 100644 --- a/web/app/schedules/home/components/GenerateScheduleButton.tsx +++ b/web/app/schedules/home/components/GenerateScheduleButton.tsx @@ -26,6 +26,13 @@ import { DialogTitle, DialogTrigger, } from '@/app/components/ui/dialog'; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from '@/app/components/ui/select'; function PreferenceOrder({ setPreference, @@ -33,17 +40,23 @@ function PreferenceOrder({ setPreference: (preference: EachFieldNumber) => void; }) { return ( - { + const preference = parseInt(value) as EachFieldNumber; setPreference(preference); }} + defaultValue="1" > - - - - + + + + + + Mínima + Média + Máxima + + ); } @@ -103,7 +116,7 @@ export default function GenerateScheduleButton() { return ( <> - +
diff --git a/web/app/components/AsideSchedulePopUp/ClassInfoBox.tsx b/web/app/components/AsideSchedulePopUp/ClassInfoBox.tsx index 579eaef4..fd9be2cf 100644 --- a/web/app/components/AsideSchedulePopUp/ClassInfoBox.tsx +++ b/web/app/components/AsideSchedulePopUp/ClassInfoBox.tsx @@ -1,12 +1,7 @@ import { ClassType, DisciplineType } from '@/app/utils/api/searchDiscipline'; import { HTMLProps, MouseEventHandler, useEffect, useState } from 'react'; - import useSelectedClasses from '@/app/hooks/useSelectedClasses'; import ClassInfo from '../ClassInfo'; -import Image from 'next/image'; - -import addIcon from '@/public/icons/add.jpg'; -import removeIcon from '@/public/icons/remove.jpg'; import { twMerge } from 'tailwind-merge'; import { FiPlus, FiMinus } from 'react-icons/fi'; @@ -51,6 +46,7 @@ export default function ClassInfoBox({ onClick={ props.onClick as MouseEventHandler | undefined } + aria-label="Selecionar/Desselecionar aula" className="hover:cursor-pointer col-start-7 flex justify-center items-center" > {selected ? : } diff --git a/web/app/components/AsideSchedulePopUp/DisciplineFragment.tsx b/web/app/components/AsideSchedulePopUp/DisciplineFragment.tsx index 9f970f68..843d0ddc 100644 --- a/web/app/components/AsideSchedulePopUp/DisciplineFragment.tsx +++ b/web/app/components/AsideSchedulePopUp/DisciplineFragment.tsx @@ -1,11 +1,6 @@ import { Fragment } from 'react'; -import Image from 'next/image'; import ClassInfoBox from './ClassInfoBox'; - import { ClassType, DisciplineType } from '@/app/utils/api/searchDiscipline'; - -import expand_more from '@/public/icons/expand_more.png'; -import expand_less from '@/public/icons/expand_less.png'; import { FiChevronDown, FiChevronUp } from 'react-icons/fi'; interface DisciplineFragmentPropsType { @@ -33,6 +28,7 @@ function DisciplineFragmentJSX({ ); diff --git a/web/app/components/AsideSchedulePopUp/ScheduleDrawerContent.tsx b/web/app/components/AsideSchedulePopUp/ScheduleDrawerContent.tsx index 5e99218b..b53c83ba 100644 --- a/web/app/components/AsideSchedulePopUp/ScheduleDrawerContent.tsx +++ b/web/app/components/AsideSchedulePopUp/ScheduleDrawerContent.tsx @@ -1,4 +1,4 @@ -import { useEffect, useState } from 'react'; +import { useState } from 'react'; import DisciplineOptionForm from './Form/DisciplineOptionForm'; import { ClassType, DisciplineType } from '@/app/utils/api/searchDiscipline'; import useSelectedClasses from '@/app/hooks/useSelectedClasses'; diff --git a/web/app/components/AsideSchedulePopUp/Tooltip.tsx b/web/app/components/AsideSchedulePopUp/Tooltip.tsx deleted file mode 100644 index 7e71bc42..00000000 --- a/web/app/components/AsideSchedulePopUp/Tooltip.tsx +++ /dev/null @@ -1,69 +0,0 @@ -'use client'; - -import styles from '@/app/styles/tooltip.module.css'; - -import Image from 'next/image'; - -import useWindowDimensions from '../../hooks/useWindowDimensions'; -import { useEffect, useState } from 'react'; - -import questionIcon from '@/public/icons/question.jpg'; -import closeIcon from '@/public/icons/close.jpg'; - -interface TooltipPropsType { - children: React.ReactNode; -} - -export const isMobile = (width?: number) => { - return width && width <= 768; -}; - -export default function Tooltip({ children }: TooltipPropsType) { - const [active, setActive] = useState(false); - const { width } = useWindowDimensions(); - - useEffect(() => { - function keyPress(event: KeyboardEvent) { - if (event.key === 'Escape') setActive(false); - } - - if (active) document.addEventListener('keydown', keyPress); - - return () => { - document.removeEventListener('keydown', keyPress); - }; - }, [active]); - - return ( -
- setActive(!active)} - className="flex justify-center items-center text-xs border-black border-solid border-2 rounded-full h-5 w-5 hover:bg-gray-300" - > - ícone de interrogação - - -
- {children} - -
-
-
- ); -} diff --git a/web/app/components/ClassInfo.tsx b/web/app/components/ClassInfo.tsx index a91da2d5..7a8cf4df 100644 --- a/web/app/components/ClassInfo.tsx +++ b/web/app/components/ClassInfo.tsx @@ -3,7 +3,6 @@ import { Fragment } from 'react'; import { ClassValueType } from '../contexts/SelectedClassesContext/types'; import { FiInfo } from 'react-icons/fi'; -import Tooltip from './AsideSchedulePopUp/Tooltip'; import { Dialog, DialogContent, @@ -53,7 +52,7 @@ export default function ClassInfo({ currentClass }: ClassInfoPropsType) { Horários:{' '} {currentClass.class.schedule} - + diff --git a/web/app/components/DisciplineBox.tsx b/web/app/components/DisciplineBox.tsx index cbab3582..0cc32a01 100644 --- a/web/app/components/DisciplineBox.tsx +++ b/web/app/components/DisciplineBox.tsx @@ -35,7 +35,7 @@ export default function DisciplineBox({
- diff --git a/web/app/components/ui/input.tsx b/web/app/components/ui/input.tsx deleted file mode 100644 index 5df89608..00000000 --- a/web/app/components/ui/input.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import * as React from 'react'; -import { cn } from '@/app/utils/utils'; - -const InputRoot = ({ - children, -}: { - children: JSX.Element | JSX.Element[]; -}): JSX.Element => { - return ( -
- {children} -
- ); -}; - -export interface InputProps - extends React.InputHTMLAttributes {} - -const Input = React.forwardRef( - ({ className, ...props }, ref) => { - return ( - - - - ); - } -); - -Input.displayName = 'Input'; diff --git a/web/app/schedules/layout.tsx b/web/app/schedules/layout.tsx index d555b2fb..dbb40f9c 100644 --- a/web/app/schedules/layout.tsx +++ b/web/app/schedules/layout.tsx @@ -1,7 +1,7 @@ 'use client'; import { usePathname, useRouter } from 'next/navigation'; -import { useCallback, useLayoutEffect, useState } from 'react'; +import { useCallback, useState } from 'react'; import useUser from '@/app/hooks/useUser'; import useWindowDimensions from '../hooks/useWindowDimensions'; import Image from 'next/image'; @@ -107,6 +107,7 @@ function AsideButtonsJSX() { }; asideRefCallback(props); }, + // eslint-disable-next-line react-hooks/exhaustive-deps [path, width, footerWidth, isUserAnonymous] ); diff --git a/web/package-lock.json b/web/package-lock.json index 6db6d60c..c8c88916 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -36,6 +36,8 @@ "autoprefixer": "^10.0.1", "eslint": "^8", "eslint-config-next": "14.0.2", + "eslint-plugin-jsx-a11y": "^6.9.0", + "eslint-plugin-unused-imports": "^3.0.0", "postcss": "^8", "tailwindcss": "^3.3.0", "typescript": "^5" @@ -1683,37 +1685,41 @@ } }, "node_modules/aria-query": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", - "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.1.3.tgz", + "integrity": "sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==", "dev": true, "dependencies": { - "dequal": "^2.0.3" + "deep-equal": "^2.0.5" } }, "node_modules/array-buffer-byte-length": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz", - "integrity": "sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz", + "integrity": "sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "is-array-buffer": "^3.0.1" + "call-bind": "^1.0.5", + "is-array-buffer": "^3.0.4" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/array-includes": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.7.tgz", - "integrity": "sha512-dlcsNBIiWhPkHdOEEKnehA+RNUWDc4UqFtnIXU4uuYDPtA4LDkr7qip2p0VvFAEXNDr0yWZ9PJyIRiGjRLQzwQ==", + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.8.tgz", + "integrity": "sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "get-intrinsic": "^1.2.1", + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.4", "is-string": "^1.0.7" }, "engines": { @@ -1801,17 +1807,18 @@ } }, "node_modules/arraybuffer.prototype.slice": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.2.tgz", - "integrity": "sha512-yMBKppFur/fbHu9/6USUe03bZ4knMYiwFBcyiaXB8Go0qNehwX6inYPzK9U0NeQvGxKthcmHcaR8P5MStSRBAw==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.3.tgz", + "integrity": "sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==", "dev": true, "dependencies": { - "array-buffer-byte-length": "^1.0.0", - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "get-intrinsic": "^1.2.1", - "is-array-buffer": "^3.0.2", + "array-buffer-byte-length": "^1.0.1", + "call-bind": "^1.0.5", + "define-properties": "^1.2.1", + "es-abstract": "^1.22.3", + "es-errors": "^1.2.1", + "get-intrinsic": "^1.2.3", + "is-array-buffer": "^3.0.4", "is-shared-array-buffer": "^1.0.2" }, "engines": { @@ -1827,15 +1834,6 @@ "integrity": "sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==", "dev": true }, - "node_modules/asynciterator.prototype": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/asynciterator.prototype/-/asynciterator.prototype-1.0.0.tgz", - "integrity": "sha512-wwHYEIS0Q80f5mosx3L/dfG5t5rjEa9Ft51GTaNt862EnpyGHpgz2RkZvLPp1oF5TnAiTohkEKVEu8pQPJI7Vg==", - "dev": true, - "dependencies": { - "has-symbols": "^1.0.3" - } - }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -1890,10 +1888,13 @@ } }, "node_modules/available-typed-arrays": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", - "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", "dev": true, + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, "engines": { "node": ">= 0.4" }, @@ -1902,9 +1903,9 @@ } }, "node_modules/axe-core": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.7.0.tgz", - "integrity": "sha512-M0JtH+hlOL5pLQwHOLNYZaXuhqmvS8oExsqB1SBYgA4Dk7u/xx+YdGHXaK5pyUfed5mYXdlYiphWq3G8cRi5JQ==", + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.10.0.tgz", + "integrity": "sha512-Mr2ZakwQ7XUAjp7pAwQWRhhK8mQQ6JAaNWSjmjxil0R8BPioMtQsTLOolGYkji1rcL++3dCqZA3zWqpT+9Ew6g==", "dev": true, "engines": { "node": ">=4" @@ -1921,12 +1922,12 @@ } }, "node_modules/axobject-query": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.2.1.tgz", - "integrity": "sha512-jsyHu61e6N4Vbz/v18DHwWYKK0bSWLqn47eeDSKPB7m8tqMHF9YJ+mhIk2lVteyZrY8tnSj/jHOv4YiTCuCJgg==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.1.1.tgz", + "integrity": "sha512-goKlv8DZrK9hUh975fnHzhNIO4jUnFCfv/dszV5VwUGDFjI6vQ2VwoyjYjYNEbBE8AH87TduWP5uyDR1D+Iteg==", "dev": true, "dependencies": { - "dequal": "^2.0.3" + "deep-equal": "^2.0.5" } }, "node_modules/balanced-match": { @@ -2026,14 +2027,19 @@ } }, "node_modules/call-bind": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.5.tgz", - "integrity": "sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", + "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", "dev": true, "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.1", - "set-function-length": "^1.1.1" + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -2302,6 +2308,57 @@ "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==", "dev": true }, + "node_modules/data-view-buffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.1.tgz", + "integrity": "sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/data-view-byte-length": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.1.tgz", + "integrity": "sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/data-view-byte-offset": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.0.tgz", + "integrity": "sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -2319,6 +2376,38 @@ } } }, + "node_modules/deep-equal": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.2.3.tgz", + "integrity": "sha512-ZIwpnevOurS8bpT4192sqAowWM76JDKSHYzMLty3BZGSswgq6pBaH3DhCSW5xVAZICZyKdOBPjwww5wfgT/6PA==", + "dev": true, + "dependencies": { + "array-buffer-byte-length": "^1.0.0", + "call-bind": "^1.0.5", + "es-get-iterator": "^1.1.3", + "get-intrinsic": "^1.2.2", + "is-arguments": "^1.1.1", + "is-array-buffer": "^3.0.2", + "is-date-object": "^1.0.5", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.2", + "isarray": "^2.0.5", + "object-is": "^1.1.5", + "object-keys": "^1.1.1", + "object.assign": "^4.1.4", + "regexp.prototype.flags": "^1.5.1", + "side-channel": "^1.0.4", + "which-boxed-primitive": "^1.0.2", + "which-collection": "^1.0.1", + "which-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", @@ -2326,17 +2415,20 @@ "dev": true }, "node_modules/define-data-property": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.1.tgz", - "integrity": "sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", "dev": true, "dependencies": { - "get-intrinsic": "^1.2.1", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.0" + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" }, "engines": { "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/define-properties": { @@ -2364,15 +2456,6 @@ "node": ">=0.4.0" } }, - "node_modules/dequal": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", - "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, "node_modules/detect-libc": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.2.tgz", @@ -2452,50 +2535,57 @@ } }, "node_modules/es-abstract": { - "version": "1.22.3", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.3.tgz", - "integrity": "sha512-eiiY8HQeYfYH2Con2berK+To6GrK2RxbPawDkGq4UiCQQfZHb6wX9qQqkbpPqaxQFcl8d9QzZqo0tGE0VcrdwA==", - "dev": true, - "dependencies": { - "array-buffer-byte-length": "^1.0.0", - "arraybuffer.prototype.slice": "^1.0.2", - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.5", - "es-set-tostringtag": "^2.0.1", + "version": "1.23.3", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.3.tgz", + "integrity": "sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==", + "dev": true, + "dependencies": { + "array-buffer-byte-length": "^1.0.1", + "arraybuffer.prototype.slice": "^1.0.3", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.7", + "data-view-buffer": "^1.0.1", + "data-view-byte-length": "^1.0.1", + "data-view-byte-offset": "^1.0.0", + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "es-set-tostringtag": "^2.0.3", "es-to-primitive": "^1.2.1", "function.prototype.name": "^1.1.6", - "get-intrinsic": "^1.2.2", - "get-symbol-description": "^1.0.0", + "get-intrinsic": "^1.2.4", + "get-symbol-description": "^1.0.2", "globalthis": "^1.0.3", "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.0", - "has-proto": "^1.0.1", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.0.3", "has-symbols": "^1.0.3", - "hasown": "^2.0.0", - "internal-slot": "^1.0.5", - "is-array-buffer": "^3.0.2", + "hasown": "^2.0.2", + "internal-slot": "^1.0.7", + "is-array-buffer": "^3.0.4", "is-callable": "^1.2.7", - "is-negative-zero": "^2.0.2", + "is-data-view": "^1.0.1", + "is-negative-zero": "^2.0.3", "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.2", + "is-shared-array-buffer": "^1.0.3", "is-string": "^1.0.7", - "is-typed-array": "^1.1.12", + "is-typed-array": "^1.1.13", "is-weakref": "^1.0.2", "object-inspect": "^1.13.1", "object-keys": "^1.1.1", - "object.assign": "^4.1.4", - "regexp.prototype.flags": "^1.5.1", - "safe-array-concat": "^1.0.1", - "safe-regex-test": "^1.0.0", - "string.prototype.trim": "^1.2.8", - "string.prototype.trimend": "^1.0.7", - "string.prototype.trimstart": "^1.0.7", - "typed-array-buffer": "^1.0.0", - "typed-array-byte-length": "^1.0.0", - "typed-array-byte-offset": "^1.0.0", - "typed-array-length": "^1.0.4", + "object.assign": "^4.1.5", + "regexp.prototype.flags": "^1.5.2", + "safe-array-concat": "^1.1.2", + "safe-regex-test": "^1.0.3", + "string.prototype.trim": "^1.2.9", + "string.prototype.trimend": "^1.0.8", + "string.prototype.trimstart": "^1.0.8", + "typed-array-buffer": "^1.0.2", + "typed-array-byte-length": "^1.0.1", + "typed-array-byte-offset": "^1.0.2", + "typed-array-length": "^1.0.6", "unbox-primitive": "^1.0.2", - "which-typed-array": "^1.1.13" + "which-typed-array": "^1.1.15" }, "engines": { "node": ">= 0.4" @@ -2504,37 +2594,93 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/es-iterator-helpers": { - "version": "1.0.15", - "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.0.15.tgz", - "integrity": "sha512-GhoY8uYqd6iwUl2kgjTm4CZAf6oo5mHK7BPqx3rKgx893YSsy0LGHV6gfqqQvZt/8xM8xeOnfXBCfqclMKkJ5g==", + "node_modules/es-define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", + "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-get-iterator": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.3.tgz", + "integrity": "sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==", "dev": true, "dependencies": { - "asynciterator.prototype": "^1.0.0", "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.3", + "has-symbols": "^1.0.3", + "is-arguments": "^1.1.1", + "is-map": "^2.0.2", + "is-set": "^2.0.2", + "is-string": "^1.0.7", + "isarray": "^2.0.5", + "stop-iteration-iterator": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-iterator-helpers": { + "version": "1.0.19", + "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.0.19.tgz", + "integrity": "sha512-zoMwbCcH5hwUkKJkT8kDIBZSz9I6mVG//+lDCinLCGov4+r7NIy0ld8o03M0cJxl2spVf6ESYVS6/gpIfq1FFw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", "define-properties": "^1.2.1", - "es-abstract": "^1.22.1", - "es-set-tostringtag": "^2.0.1", - "function-bind": "^1.1.1", - "get-intrinsic": "^1.2.1", + "es-abstract": "^1.23.3", + "es-errors": "^1.3.0", + "es-set-tostringtag": "^2.0.3", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", "globalthis": "^1.0.3", - "has-property-descriptors": "^1.0.0", - "has-proto": "^1.0.1", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.0.3", "has-symbols": "^1.0.3", - "internal-slot": "^1.0.5", + "internal-slot": "^1.0.7", "iterator.prototype": "^1.1.2", - "safe-array-concat": "^1.0.1" + "safe-array-concat": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz", + "integrity": "sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" } }, "node_modules/es-set-tostringtag": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.2.tgz", - "integrity": "sha512-BuDyupZt65P9D2D2vA/zqcI3G5xRsklm5N3xCwuiy+/vKy8i0ifdsQP1sLgO4tZDSCaQUSnmC48khknGMV3D2Q==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz", + "integrity": "sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==", "dev": true, "dependencies": { - "get-intrinsic": "^1.2.2", - "has-tostringtag": "^1.0.0", - "hasown": "^2.0.0" + "get-intrinsic": "^1.2.4", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.1" }, "engines": { "node": ">= 0.4" @@ -2801,27 +2947,27 @@ } }, "node_modules/eslint-plugin-jsx-a11y": { - "version": "6.8.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.8.0.tgz", - "integrity": "sha512-Hdh937BS3KdwwbBaKd5+PLCOmYY6U4f2h9Z2ktwtNKvIdIEu137rjYbcb9ApSbVJfWxANNuiKTD/9tOKjK9qOA==", + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.9.0.tgz", + "integrity": "sha512-nOFOCaJG2pYqORjK19lqPqxMO/JpvdCZdPtNdxY3kvom3jTvkAbOvQvD8wuD0G8BYR0IGAGYDlzqWJOh/ybn2g==", "dev": true, "dependencies": { - "@babel/runtime": "^7.23.2", - "aria-query": "^5.3.0", - "array-includes": "^3.1.7", + "aria-query": "~5.1.3", + "array-includes": "^3.1.8", "array.prototype.flatmap": "^1.3.2", "ast-types-flow": "^0.0.8", - "axe-core": "=4.7.0", - "axobject-query": "^3.2.1", + "axe-core": "^4.9.1", + "axobject-query": "~3.1.1", "damerau-levenshtein": "^1.0.8", "emoji-regex": "^9.2.2", - "es-iterator-helpers": "^1.0.15", - "hasown": "^2.0.0", + "es-iterator-helpers": "^1.0.19", + "hasown": "^2.0.2", "jsx-ast-utils": "^3.3.5", "language-tags": "^1.0.9", "minimatch": "^3.1.2", - "object.entries": "^1.1.7", - "object.fromentries": "^2.0.7" + "object.fromentries": "^2.0.8", + "safe-regex-test": "^1.0.3", + "string.prototype.includes": "^2.0.0" }, "engines": { "node": ">=4.0" @@ -2910,6 +3056,36 @@ "semver": "bin/semver.js" } }, + "node_modules/eslint-plugin-unused-imports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-unused-imports/-/eslint-plugin-unused-imports-3.0.0.tgz", + "integrity": "sha512-sduiswLJfZHeeBJ+MQaG+xYzSWdRXoSw61DpU13mzWumCkR0ufD0HmO4kdNokjrkluMHpj/7PJeN35pgbhW3kw==", + "dev": true, + "dependencies": { + "eslint-rule-composer": "^0.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "@typescript-eslint/eslint-plugin": "^6.0.0", + "eslint": "^8.0.0" + }, + "peerDependenciesMeta": { + "@typescript-eslint/eslint-plugin": { + "optional": true + } + } + }, + "node_modules/eslint-rule-composer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/eslint-rule-composer/-/eslint-rule-composer-0.3.0.tgz", + "integrity": "sha512-bt+Sh8CtDmn2OajxvNO+BX7Wn4CIWMpTRm3MaiKPCQcnnlm0CS2mhui6QaoeQugs+3Kj2ESKEEGJUdVafwhiCg==", + "dev": true, + "engines": { + "node": ">=4.0.0" + } + }, "node_modules/eslint-scope": { "version": "7.2.2", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", @@ -3221,16 +3397,20 @@ } }, "node_modules/get-intrinsic": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.2.tgz", - "integrity": "sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", + "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", "dev": true, "dependencies": { + "es-errors": "^1.3.0", "function-bind": "^1.1.2", "has-proto": "^1.0.1", "has-symbols": "^1.0.3", "hasown": "^2.0.0" }, + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -3244,13 +3424,14 @@ } }, "node_modules/get-symbol-description": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", - "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.2.tgz", + "integrity": "sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.1" + "call-bind": "^1.0.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4" }, "engines": { "node": ">= 0.4" @@ -3402,21 +3583,21 @@ } }, "node_modules/has-property-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz", - "integrity": "sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", "dev": true, "dependencies": { - "get-intrinsic": "^1.2.2" + "es-define-property": "^1.0.0" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/has-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", - "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", + "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", "dev": true, "engines": { "node": ">= 0.4" @@ -3438,12 +3619,12 @@ } }, "node_modules/has-tostringtag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", - "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", "dev": true, "dependencies": { - "has-symbols": "^1.0.2" + "has-symbols": "^1.0.3" }, "engines": { "node": ">= 0.4" @@ -3453,9 +3634,9 @@ } }, "node_modules/hasown": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", - "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", "dependencies": { "function-bind": "^1.1.2" }, @@ -3525,12 +3706,12 @@ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, "node_modules/internal-slot": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.6.tgz", - "integrity": "sha512-Xj6dv+PsbtwyPpEflsejS+oIZxmMlV44zAhG479uYu89MsjcYOhCFnNyKrkJrihbsiasQyY0afoCl/9BLR65bg==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.7.tgz", + "integrity": "sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==", "dev": true, "dependencies": { - "get-intrinsic": "^1.2.2", + "es-errors": "^1.3.0", "hasown": "^2.0.0", "side-channel": "^1.0.4" }, @@ -3546,15 +3727,33 @@ "loose-envify": "^1.0.0" } }, + "node_modules/is-arguments": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", + "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-array-buffer": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz", - "integrity": "sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.4.tgz", + "integrity": "sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==", "dev": true, "dependencies": { "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.0", - "is-typed-array": "^1.1.10" + "get-intrinsic": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -3642,6 +3841,21 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-data-view": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.1.tgz", + "integrity": "sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==", + "dev": true, + "dependencies": { + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-date-object": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", @@ -3713,9 +3927,9 @@ } }, "node_modules/is-negative-zero": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", - "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", + "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", "dev": true, "engines": { "node": ">= 0.4" @@ -3782,12 +3996,15 @@ } }, "node_modules/is-shared-array-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", - "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.3.tgz", + "integrity": "sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==", "dev": true, "dependencies": { - "call-bind": "^1.0.2" + "call-bind": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -3824,12 +4041,12 @@ } }, "node_modules/is-typed-array": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.12.tgz", - "integrity": "sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg==", + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.13.tgz", + "integrity": "sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==", "dev": true, "dependencies": { - "which-typed-array": "^1.1.11" + "which-typed-array": "^1.1.14" }, "engines": { "node": ">= 0.4" @@ -4292,6 +4509,22 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/object-is": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.6.tgz", + "integrity": "sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/object-keys": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", @@ -4302,13 +4535,13 @@ } }, "node_modules/object.assign": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", - "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.5.tgz", + "integrity": "sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", + "call-bind": "^1.0.5", + "define-properties": "^1.2.1", "has-symbols": "^1.0.3", "object-keys": "^1.1.1" }, @@ -4334,14 +4567,15 @@ } }, "node_modules/object.fromentries": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.7.tgz", - "integrity": "sha512-UPbPHML6sL8PI/mOqPwsH4G6iyXcCGzLin8KvEPenOZN5lpCNBZZQ+V62vdjB1mQHrmqGQt5/OJzemUA+KJmEA==", + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz", + "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0" }, "engines": { "node": ">= 0.4" @@ -4537,6 +4771,15 @@ "node": ">= 6" } }, + "node_modules/possible-typed-array-names": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", + "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/postcss": { "version": "8.4.31", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", @@ -4887,14 +5130,15 @@ "integrity": "sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==" }, "node_modules/regexp.prototype.flags": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.1.tgz", - "integrity": "sha512-sy6TXMN+hnP/wMy+ISxg3krXx7BAtWVO4UouuCN/ziM9UEne0euamVNafDfvC83bRNr95y0V5iijeDQFUNpvrg==", + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz", + "integrity": "sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "set-function-name": "^2.0.0" + "call-bind": "^1.0.6", + "define-properties": "^1.2.1", + "es-errors": "^1.3.0", + "set-function-name": "^2.0.1" }, "engines": { "node": ">= 0.4" @@ -4993,13 +5237,13 @@ } }, "node_modules/safe-array-concat": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.0.1.tgz", - "integrity": "sha512-6XbUAseYE2KtOuGueyeobCySj9L4+66Tn6KQMOPQJrAJEowYKW/YR/MGJZl7FdydUdaFu4LYyDZjxf4/Nmo23Q==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.2.tgz", + "integrity": "sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.1", + "call-bind": "^1.0.7", + "get-intrinsic": "^1.2.4", "has-symbols": "^1.0.3", "isarray": "^2.0.5" }, @@ -5011,15 +5255,18 @@ } }, "node_modules/safe-regex-test": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz", - "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.3.tgz", + "integrity": "sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.3", + "call-bind": "^1.0.6", + "es-errors": "^1.3.0", "is-regex": "^1.1.4" }, + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -5047,15 +5294,17 @@ } }, "node_modules/set-function-length": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.1.1.tgz", - "integrity": "sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", "dev": true, "dependencies": { - "define-data-property": "^1.1.1", - "get-intrinsic": "^1.2.1", + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.0" + "has-property-descriptors": "^1.0.2" }, "engines": { "node": ">= 0.4" @@ -5183,6 +5432,18 @@ "node": ">=0.1.14" } }, + "node_modules/stop-iteration-iterator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz", + "integrity": "sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ==", + "dev": true, + "dependencies": { + "internal-slot": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/streamsearch": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", @@ -5191,6 +5452,16 @@ "node": ">=10.0.0" } }, + "node_modules/string.prototype.includes": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/string.prototype.includes/-/string.prototype.includes-2.0.0.tgz", + "integrity": "sha512-E34CkBgyeqNDcrbU76cDjL5JLcVrtSdYq0MEh/B10r17pRP4ciHLwTgnuLV8Ay6cgEMLkcBkFCKyFZ43YldYzg==", + "dev": true, + "dependencies": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5" + } + }, "node_modules/string.prototype.matchall": { "version": "4.0.10", "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.10.tgz", @@ -5212,14 +5483,15 @@ } }, "node_modules/string.prototype.trim": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.8.tgz", - "integrity": "sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ==", + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz", + "integrity": "sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.0", + "es-object-atoms": "^1.0.0" }, "engines": { "node": ">= 0.4" @@ -5229,28 +5501,31 @@ } }, "node_modules/string.prototype.trimend": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.7.tgz", - "integrity": "sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA==", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.8.tgz", + "integrity": "sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/string.prototype.trimstart": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.7.tgz", - "integrity": "sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg==", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", + "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -5549,29 +5824,30 @@ } }, "node_modules/typed-array-buffer": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.0.tgz", - "integrity": "sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz", + "integrity": "sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.1", - "is-typed-array": "^1.1.10" + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.13" }, "engines": { "node": ">= 0.4" } }, "node_modules/typed-array-byte-length": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.0.tgz", - "integrity": "sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.1.tgz", + "integrity": "sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", + "call-bind": "^1.0.7", "for-each": "^0.3.3", - "has-proto": "^1.0.1", - "is-typed-array": "^1.1.10" + "gopd": "^1.0.1", + "has-proto": "^1.0.3", + "is-typed-array": "^1.1.13" }, "engines": { "node": ">= 0.4" @@ -5581,16 +5857,17 @@ } }, "node_modules/typed-array-byte-offset": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.0.tgz", - "integrity": "sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.2.tgz", + "integrity": "sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==", "dev": true, "dependencies": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.7", "for-each": "^0.3.3", - "has-proto": "^1.0.1", - "is-typed-array": "^1.1.10" + "gopd": "^1.0.1", + "has-proto": "^1.0.3", + "is-typed-array": "^1.1.13" }, "engines": { "node": ">= 0.4" @@ -5600,14 +5877,20 @@ } }, "node_modules/typed-array-length": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz", - "integrity": "sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.6.tgz", + "integrity": "sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", + "call-bind": "^1.0.7", "for-each": "^0.3.3", - "is-typed-array": "^1.1.9" + "gopd": "^1.0.1", + "has-proto": "^1.0.3", + "is-typed-array": "^1.1.13", + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -5826,16 +6109,16 @@ } }, "node_modules/which-typed-array": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.13.tgz", - "integrity": "sha512-P5Nra0qjSncduVPEAr7xhoF5guty49ArDTwzJ/yNuPIbZppyRxFQsRCWrocxIY+CnMVG+qfbU2FmDKyvSGClow==", + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.15.tgz", + "integrity": "sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==", "dev": true, "dependencies": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.4", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.7", "for-each": "^0.3.3", "gopd": "^1.0.1", - "has-tostringtag": "^1.0.0" + "has-tostringtag": "^1.0.2" }, "engines": { "node": ">= 0.4" diff --git a/web/package.json b/web/package.json index 5d9c1d15..3b876368 100644 --- a/web/package.json +++ b/web/package.json @@ -37,6 +37,8 @@ "autoprefixer": "^10.0.1", "eslint": "^8", "eslint-config-next": "14.0.2", + "eslint-plugin-jsx-a11y": "^6.9.0", + "eslint-plugin-unused-imports": "^3.0.0", "postcss": "^8", "tailwindcss": "^3.3.0", "typescript": "^5" From acb4d081c831a60c4753d89fdb159001de375279 Mon Sep 17 00:00:00 2001 From: David William Date: Sun, 18 Aug 2024 00:28:21 -0300 Subject: [PATCH 08/16] feat: refactor profile logout modal --- web/app/components/Modal/Modal.tsx | 30 ---------- web/app/schedules/profile/page.tsx | 90 ++++++++++++++++-------------- 2 files changed, 49 insertions(+), 71 deletions(-) delete mode 100644 web/app/components/Modal/Modal.tsx diff --git a/web/app/components/Modal/Modal.tsx b/web/app/components/Modal/Modal.tsx deleted file mode 100644 index 2fdaee97..00000000 --- a/web/app/components/Modal/Modal.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import Image from 'next/image'; - -import closeIcon from '@/public/icons/close.jpg'; - -interface ModalPropsType { - children: React.ReactNode; - setActiveModal: (active: boolean) => void; - noExit?: boolean; -} - -export default function Modal({ children, setActiveModal, noExit }: ModalPropsType) { - return ( -
-
- {children} - {!noExit && - - } -
-
- ); -} \ No newline at end of file diff --git a/web/app/schedules/profile/page.tsx b/web/app/schedules/profile/page.tsx index c7c173d4..215f1725 100644 --- a/web/app/schedules/profile/page.tsx +++ b/web/app/schedules/profile/page.tsx @@ -1,7 +1,6 @@ 'use client'; import { useRouter } from 'next/navigation'; -import { useState } from 'react'; import useUser from '@/app/hooks/useUser'; import useClassesToShow from '@/app/hooks/useClassesToShow'; @@ -12,15 +11,19 @@ import defaultProfile from '@/public/profile.svg'; import Image from 'next/image'; import Button from '@/app/components/Button'; -import Modal from '@/app/components/Modal/Modal'; import signInWithGoogle from '@/app/utils/signInWithGoogle'; import handleLogout from '@/app/utils/api/logout'; import useSchedules from '@/app/hooks/useSchedules'; +import { + Dialog, + DialogClose, + DialogContent, + DialogTitle, + DialogTrigger, +} from '@/app/components/ui/dialog'; export default function Profile() { - const [activeModal, setActiveModal] = useState(false); - const { setClassesToShow } = useClassesToShow(); const { setSelectedClasses } = useSelectedClasses(); const { localSchedules, setLocalSchedules, setCloudSchedules } = @@ -40,34 +43,6 @@ export default function Profile() { return ( <> - {activeModal && ( - -
-

- Você tem grades não salvas na nuvem e vai perdê-las! -

-
-

- Tem certeza que quer sair? -

-
- - -
-
-
-
- )} Trocar de conta - + + {localSchedules.length ? ( + + + + + + Tem certeza que quer sair? +
+

+ Você tem grades não salvas na nuvem e vai perdê-las! +

+ +
+
+ + + + + + +
+
+
+
+
+ ) : ( + + )}
); From b28383a237b25e554bd287fcf162ca5dca8c11cf Mon Sep 17 00:00:00 2001 From: David William Date: Sun, 18 Aug 2024 00:32:34 -0300 Subject: [PATCH 09/16] feat: fix schedule dialog mouse closes bug; refactor period select --- .../Form/DisciplineOptionForm.tsx | 59 ++-- web/app/components/Schedule/Schedule.tsx | 297 +++++++++++------- .../SchedulePreview/SchedulePreview.tsx | 18 +- web/app/components/ui/dialog.tsx | 52 +-- web/app/components/ui/select.tsx | 72 ++--- web/app/schedules/info/page.tsx | 197 +++++++----- 6 files changed, 404 insertions(+), 291 deletions(-) diff --git a/web/app/components/AsideSchedulePopUp/Form/DisciplineOptionForm.tsx b/web/app/components/AsideSchedulePopUp/Form/DisciplineOptionForm.tsx index 9a2815fe..42aec00b 100644 --- a/web/app/components/AsideSchedulePopUp/Form/DisciplineOptionForm.tsx +++ b/web/app/components/AsideSchedulePopUp/Form/DisciplineOptionForm.tsx @@ -1,10 +1,4 @@ -import { - ChangeEvent, - Dispatch, - SetStateAction, - useEffect, - useState, -} from 'react'; +import { Dispatch, SetStateAction, useEffect, useState } from 'react'; import useYearPeriod from '@/app/hooks/useYearPeriod'; import useSelectedClasses from '@/app/hooks/useSelectedClasses'; @@ -21,7 +15,6 @@ import InputForm from './InputForm'; import { Select, SelectContent, - SelectGroup, SelectItem, SelectTrigger, SelectValue, @@ -29,14 +22,13 @@ import { interface FormPropsType { form: FormType; - disableDefault: boolean; setInfos: Dispatch>>; - handleYearAndPeriodChange: (event: ChangeEvent) => void; + handleYearAndPeriodChange: (value: string) => void; } function Form(props: FormPropsType) { const { formData, setFormData } = props.form; - const { disableDefault, handleYearAndPeriodChange } = props; + const { handleYearAndPeriodChange } = props; const { periods } = useYearPeriod(); @@ -44,20 +36,23 @@ function Form(props: FormPropsType) { <>
Ano/Período - { + handleYearAndPeriodChange(value); + }} > - - {periods['year/period'].map((item, index) => ( - - ))} - + + + + + {periods['year/period'].map((item, index) => ( + + {item} + + ))} + +
Matéria @@ -92,23 +87,17 @@ export default function DisciplineOptionForm( ) { const { selectedClasses, currentYearPeriod, setCurrentYearPeriod } = useSelectedClasses(); - const [disableDefault, setDisableDefault] = useState(false); const [formData, setFormData] = useState(defaultFormData); useEffect(() => { - if ( - !disableDefault && - formData == defaultFormData && - selectedClasses.size - ) { + if (formData == defaultFormData && selectedClasses.size) { const { year, period } = getYearAndPeriod(currentYearPeriod); setFormData({ ...formData, year: year, period: period }); - setDisableDefault(true); } - }, [disableDefault, currentYearPeriod, formData, selectedClasses]); + }, [currentYearPeriod, formData, selectedClasses]); - function handleYearAndPeriodChange(event: ChangeEvent) { - const text = event.target.value.trim(); + function handleYearAndPeriodChange(value: string) { + const text = value.trim(); const { year, period } = getYearAndPeriod(text); if (year && period) { @@ -116,7 +105,6 @@ export default function DisciplineOptionForm( setFormData({ ...formData, year: year, period: period }); props.setInfos([]); setCurrentYearPeriod(text); - setDisableDefault(true); }; handleChangeYearAndPeriod( text, @@ -130,7 +118,6 @@ export default function DisciplineOptionForm( return (
diff --git a/web/app/components/Schedule/Schedule.tsx b/web/app/components/Schedule/Schedule.tsx index f81feaff..d8bbfd9f 100644 --- a/web/app/components/Schedule/Schedule.tsx +++ b/web/app/components/Schedule/Schedule.tsx @@ -3,139 +3,202 @@ import { ScheduleClassType } from '@/app/contexts/SchedulesContext'; import { HTMLProps, useEffect, useState } from 'react'; import { generateSpecialDates } from '../ClassInfo'; -import Tooltip from '../AsideSchedulePopUp/Tooltip'; +import { + Dialog, + DialogContent, + DialogDescription, + DialogTitle, + DialogTrigger, +} from '../ui/dialog'; +import { FiInfo } from 'react-icons/fi'; interface SchedulePropsType extends HTMLProps { - schedules?: Array; - preview?: boolean; - toDownload?: boolean; + schedules?: Array; + preview?: boolean; + toDownload?: boolean; } -export default function Schedule({ schedules, preview, toDownload, ...props }: SchedulePropsType) { - const [currentSchedule, setCurrentSchedule] = useState>>(new Array(6).fill(new Array(15).fill(null))); - const uniqueTeachers = new Set(); +export default function Schedule({ + schedules, + preview, + toDownload, + ...props +}: SchedulePropsType) { + const [currentSchedule, setCurrentSchedule] = useState< + Array> + >(new Array(6).fill(new Array(15).fill(null))); + const uniqueTeachers = new Set(); - const days = ['Seg', 'Ter', 'Qua', 'Qui', 'Sex', 'Sáb']; - const times = [ - '08:00 - 08:55', '08:55 - 09:50', '10:00 - 10:55', - '10:55 - 11:50', '12:00 - 12:55', '12:55 - 13:50', - '14:00 - 14:55', '14:55 - 15:50', '16:00 - 16:55', - '16:55 - 17:50', '18:00 - 18:55', '19:00 - 19:50', - '19:50 - 20:40', '20:50 - 21:40', '21:40 - 22:30' - ]; + const days = ['Seg', 'Ter', 'Qua', 'Qui', 'Sex', 'Sáb']; + const times = [ + '08:00 - 08:55', + '08:55 - 09:50', + '10:00 - 10:55', + '10:55 - 11:50', + '12:00 - 12:55', + '12:55 - 13:50', + '14:00 - 14:55', + '14:55 - 15:50', + '16:00 - 16:55', + '16:55 - 17:50', + '18:00 - 18:55', + '19:00 - 19:50', + '19:50 - 20:40', + '20:50 - 21:40', + '21:40 - 22:30', + ]; - useEffect(() => { - const baseSchedule = new Array(6); - for (let i = 0; i < baseSchedule.length; i++) { - baseSchedule[i] = new Array(15).fill(null); - } + useEffect(() => { + const baseSchedule = new Array(6); + for (let i = 0; i < baseSchedule.length; i++) { + baseSchedule[i] = new Array(15).fill(null); + } - function addClassToSchedule(scheduleStructure: ScheduleClassType) { - const { schedule } = scheduleStructure; - const splitSchedule = schedule.split(' '); + function addClassToSchedule(scheduleStructure: ScheduleClassType) { + const { schedule } = scheduleStructure; + const splitSchedule = schedule.split(' '); - splitSchedule.forEach((piece) => { - const regex = /^(\d+)([MNT])(\d+)$/; - const match = piece.match(regex); - if (match) { - const [, day, period, time] = match; + splitSchedule.forEach((piece) => { + const regex = /^(\d+)([MNT])(\d+)$/; + const match = piece.match(regex); + if (match) { + const [, day, period, time] = match; - for (let i = 0; i < day.length; i++) { - const x = parseInt(day[i]) - 2; - for (let j = 0; j < time.length; j++) { - let y = parseInt(time[j]) - 1; + for (let i = 0; i < day.length; i++) { + const x = parseInt(day[i]) - 2; + for (let j = 0; j < time.length; j++) { + let y = parseInt(time[j]) - 1; - if (period === 'T') y += 5; - else if (period === 'N') y += 11; + if (period === 'T') y += 5; + else if (period === 'N') y += 11; - baseSchedule[x][y] = scheduleStructure; - } - } - } - }); + baseSchedule[x][y] = scheduleStructure; + } + } } + }); + } - if (schedules) { - schedules.forEach((schedule) => addClassToSchedule(schedule)); - setCurrentSchedule(baseSchedule); - } - }, [schedules]); + if (schedules) { + schedules.forEach((schedule) => addClassToSchedule(schedule)); + setCurrentSchedule(baseSchedule); + } + }, [schedules]); - return ( + return ( +
+
+
+
+
+ {days.map((day, index) => ( +
+

{day}

+
+ ))} +
+
+
+ {times.map((time, timeIndex) => ( +
+
+

{time}

+
+
+ {days.map((day, dayIndex) => ( +
+ {currentSchedule[dayIndex] && + currentSchedule[dayIndex][timeIndex] && + currentSchedule[dayIndex][timeIndex].discipline.code} +
+ ))} +
+
+ ))} +
+
+ {!preview && (
-
-
-
-
- {days.map((day, index) => -
-

{day}

-
- )} -
-
-
- {times.map((time, timeIndex) => -
-
-

{time}

-
-
- {days.map((day, dayIndex) => -
- {currentSchedule[dayIndex] && - currentSchedule[dayIndex][timeIndex] && - currentSchedule[dayIndex][timeIndex].discipline.code} -
- )} -
-
- )} -
-
- {!preview && -
-
    - {schedules && schedules.map((schedule, index) => { - return ( -
    -
  • - - {schedule.discipline.code} - - {schedule.discipline.name} - ({schedule.classroom}) -
  • - PROFESSORES: -
      - {schedule.teachers.map((teacher, index) => { - if (uniqueTeachers.has(teacher)) return null; +
        + {schedules && + schedules.map((schedule, index) => { + return ( +
        +
      • + + {schedule.discipline.code} + {' '} + - {schedule.discipline.name} -{' '} + + ({schedule.classroom}) + +
      • + PROFESSORES: +
          + {schedule.teachers.map((teacher, index) => { + if (uniqueTeachers.has(teacher)) return null; - uniqueTeachers.add(teacher); - return ( -
        • -

          {teacher}

          -
        • - ); - })} -
        - {schedule.special_dates.length ? - <> - DATAS: - {!toDownload ? - {generateSpecialDates(schedule.special_dates, schedule.days)} - : generateSpecialDates(schedule.special_dates, schedule.days)} - : null - } - -
        - ); - })} + uniqueTeachers.add(teacher); + return ( +
      • +

        {teacher}

        +
      • + ); + })}
      -
    - } + {schedule.special_dates.length ? ( + <> + DATAS: + {!toDownload ? ( + + + + + + Horários + + {generateSpecialDates( + schedule.special_dates, + schedule.days + )} + + + + ) : ( + generateSpecialDates( + schedule.special_dates, + schedule.days + ) + )} + + ) : null} +
+ ); + })} +
- ); -} \ No newline at end of file + )} +
+ ); +} diff --git a/web/app/components/SchedulePreview/SchedulePreview.tsx b/web/app/components/SchedulePreview/SchedulePreview.tsx index a49cb7bf..2fa6fd14 100644 --- a/web/app/components/SchedulePreview/SchedulePreview.tsx +++ b/web/app/components/SchedulePreview/SchedulePreview.tsx @@ -6,7 +6,6 @@ import useUser from '@/app/hooks/useUser'; import { ScheduleClassType } from '@/app/contexts/SchedulesContext'; -import Modal from '../Modal/Modal'; import Schedule from '../Schedule/Schedule'; import Button from '../Button'; @@ -94,7 +93,10 @@ export default function SchedulePreview({
- + @@ -151,6 +153,7 @@ function ActionButtons({ onClick={() => { handleDownloadPDF(isCloud, position); }} + aria-label="Baixar grade" > @@ -162,10 +165,8 @@ function ActionButtons({ - - + + Deletar @@ -243,7 +244,10 @@ function UploadToCloudButton({ return ( - diff --git a/web/app/components/ui/dialog.tsx b/web/app/components/ui/dialog.tsx index d99d5952..b7dcefba 100644 --- a/web/app/components/ui/dialog.tsx +++ b/web/app/components/ui/dialog.tsx @@ -40,30 +40,44 @@ type DialogContentProps = React.ComponentPropsWithoutRef< typeof DialogPrimitive.Content > & { showOverlay?: boolean; + closeBtnPosition?: 'left' | 'right'; }; const DialogContent = React.forwardRef< React.ElementRef, DialogContentProps ->(({ className, children, showOverlay, ...props }, ref) => ( - - - - {children} - - - Close - - - -)); +>( + ( + { className, children, showOverlay, closeBtnPosition = 'right', ...props }, + ref + ) => ( + + + + {children} + + + Close + + + + ) +); DialogContent.displayName = DialogPrimitive.Content.displayName; const DialogHeader = ({ diff --git a/web/app/components/ui/select.tsx b/web/app/components/ui/select.tsx index de60acf6..6418ef93 100644 --- a/web/app/components/ui/select.tsx +++ b/web/app/components/ui/select.tsx @@ -1,16 +1,16 @@ -'use client'; +"use client" -import * as React from 'react'; -import * as SelectPrimitive from '@radix-ui/react-select'; -import { Check, ChevronDown, ChevronUp } from 'lucide-react'; +import * as React from "react" +import * as SelectPrimitive from "@radix-ui/react-select" +import { Check, ChevronDown, ChevronUp } from "lucide-react" -import { cn } from '@/app/utils/utils'; +import { cn } from "@/app/utils/utils" -const Select = SelectPrimitive.Root; +const Select = SelectPrimitive.Root -const SelectGroup = SelectPrimitive.Group; +const SelectGroup = SelectPrimitive.Group -const SelectValue = SelectPrimitive.Value; +const SelectValue = SelectPrimitive.Value const SelectTrigger = React.forwardRef< React.ElementRef, @@ -19,7 +19,7 @@ const SelectTrigger = React.forwardRef< span]:line-clamp-1', + "flex h-10 w-full items-center justify-between rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1", className )} {...props} @@ -29,8 +29,8 @@ const SelectTrigger = React.forwardRef< -)); -SelectTrigger.displayName = SelectPrimitive.Trigger.displayName; +)) +SelectTrigger.displayName = SelectPrimitive.Trigger.displayName const SelectScrollUpButton = React.forwardRef< React.ElementRef, @@ -39,15 +39,15 @@ const SelectScrollUpButton = React.forwardRef< -)); -SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName; +)) +SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName const SelectScrollDownButton = React.forwardRef< React.ElementRef, @@ -56,28 +56,28 @@ const SelectScrollDownButton = React.forwardRef< -)); +)) SelectScrollDownButton.displayName = - SelectPrimitive.ScrollDownButton.displayName; + SelectPrimitive.ScrollDownButton.displayName const SelectContent = React.forwardRef< React.ElementRef, React.ComponentPropsWithoutRef ->(({ className, children, position = 'popper', ...props }, ref) => ( +>(({ className, children, position = "popper", ...props }, ref) => ( {children} @@ -96,8 +96,8 @@ const SelectContent = React.forwardRef< -)); -SelectContent.displayName = SelectPrimitive.Content.displayName; +)) +SelectContent.displayName = SelectPrimitive.Content.displayName const SelectLabel = React.forwardRef< React.ElementRef, @@ -105,11 +105,11 @@ const SelectLabel = React.forwardRef< >(({ className, ...props }, ref) => ( -)); -SelectLabel.displayName = SelectPrimitive.Label.displayName; +)) +SelectLabel.displayName = SelectPrimitive.Label.displayName const SelectItem = React.forwardRef< React.ElementRef, @@ -118,7 +118,7 @@ const SelectItem = React.forwardRef< {children} -)); -SelectItem.displayName = SelectPrimitive.Item.displayName; +)) +SelectItem.displayName = SelectPrimitive.Item.displayName const SelectSeparator = React.forwardRef< React.ElementRef, @@ -140,11 +140,11 @@ const SelectSeparator = React.forwardRef< >(({ className, ...props }, ref) => ( -)); -SelectSeparator.displayName = SelectPrimitive.Separator.displayName; +)) +SelectSeparator.displayName = SelectPrimitive.Separator.displayName export { Select, @@ -157,4 +157,4 @@ export { SelectSeparator, SelectScrollUpButton, SelectScrollDownButton, -}; +} diff --git a/web/app/schedules/info/page.tsx b/web/app/schedules/info/page.tsx index 16c64ef3..c50d6d30 100644 --- a/web/app/schedules/info/page.tsx +++ b/web/app/schedules/info/page.tsx @@ -1,98 +1,143 @@ import Image from 'next/image'; import Link from 'next/link'; -function CollaboratorImage({ name, src }: { name: string, src: string }) { - return ( -
- {name} -
- - {`Foto - -
- - ); +function CollaboratorImage({ name, src }: { name: string; src: string }) { + return ( +
+ {name} +
+ + {`Foto + +
+ ); } const Team = () => { - return ( -
-
- - - -
-
- - - -
-
- ); + return ( +
+
+ + + +
+
+ + + +
+
+ ); }; function About() { - return ( - <> -

Sobre

-

- O Sua Grade UnB é um projeto em desenvolvimento da matéria de Métodos de Desenvolvimento de Software. - Seu propósito é proporcionar aos estudantes da Universidade de Brasília uma experiência simplificada e intuitiva na elaboração de suas grades horárias. - O projeto visa facilitar o processo de organização acadêmica, oferecendo uma ferramenta eficiente e amigável para a montagem de horários, otimizando assim a gestão do tempo dos alunos. -

- - ); + return ( + <> +

Sobre

+

+ O Sua Grade UnB é um projeto em desenvolvimento da matéria de{' '} + Métodos de Desenv`olvimento de Software. Seu propósito é + proporcionar aos estudantes da Universidade de Brasília uma experiência + simplificada e intuitiva na elaboração de suas grades horárias. O + projeto visa facilitar o processo de organização acadêmica, oferecendo + uma ferramenta eficiente e amigável para a montagem de horários, + otimizando assim a gestão do tempo dos alunos. +

+ + ); } function HowToUse() { - return ( - <> -

Como utilizar?

-

Na página inicial, clique no botão Buscar Matéria para selecionar as disciplinas desejadas para a sua grade. Escolha o ano/período e insira o nome da disciplina para poder optar por turmas potenciais na criação da grade, permitindo a escolha de até quatro turmas para cada disciplina.

-

Após fazer suas escolhas, clique no botão Gerar Grade e defina a prioridade dos turnos que melhor se adequam à sua rotina. Em seguida, basta escolher a grade mais adequada entre as opções geradas para realizar o download e/ou salvar as disciplinas escolhidas.

-

Compartilhe suas experiências e sugestões. O aplicativo está em constante desenvolvimento, e seu feedback é valioso para aprimorar a experiência de todos os usuários.

- - ); + return ( + <> +

Como utilizar?

+

+ Na página inicial, clique no botão Buscar Matéria para selecionar + as disciplinas desejadas para a sua grade. Escolha o ano/período e + insira o nome da disciplina para poder optar por turmas potenciais na + criação da grade, permitindo a escolha de até quatro turmas para cada + disciplina. +

+

+ Após fazer suas escolhas, clique no botão Gerar Grade e defina a + prioridade dos turnos que melhor se adequam à sua rotina. Em seguida, + basta escolher a grade mais adequada entre as opções geradas para + realizar o download e/ou salvar as disciplinas escolhidas. +

+

+ Compartilhe suas experiências e sugestões. O aplicativo está em + constante desenvolvimento, e seu feedback é valioso para aprimorar a + experiência de todos os usuários. +

+ + ); } function HowToContribute() { - return ( - <> -

Como contribuir?

-

- Se você é um programador e deseja contribuir com nosso projeto, basta clicar aqui para ter acesso a nossa documentação. Lá você encontrará todos os detalhes de como contribuir com novas funcionalidades e reportar possíveis erros. -

- - ); + return ( + <> +

Como contribuir?

+

+ Se você é um programador e deseja contribuir com nosso projeto, basta{' '} + + clicar aqui + {' '} + para ter acesso a nossa documentação. Lá você encontrará todos os + detalhes de como contribuir com novas funcionalidades e reportar + possíveis erros. +

+ + ); } function Collaborators() { - return ( - <> -

Colaboradores

- - - ); + return ( + <> +

Colaboradores

+ + + ); } export default function Info() { - - return ( -
-
-
- - - - -
-
+ return ( +
+
+
+ + + +
- ); -} \ No newline at end of file +
+
+ ); +} From c6c66ee34ffa5a9317eff9a91c1ef6cd163c99e4 Mon Sep 17 00:00:00 2001 From: David William Date: Sun, 18 Aug 2024 00:47:03 -0300 Subject: [PATCH 10/16] feat: add dialog margin mobile --- web/app/components/ui/dialog.tsx | 4 ++-- web/app/schedules/home/components/GenerateScheduleButton.tsx | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/web/app/components/ui/dialog.tsx b/web/app/components/ui/dialog.tsx index b7dcefba..de31a579 100644 --- a/web/app/components/ui/dialog.tsx +++ b/web/app/components/ui/dialog.tsx @@ -25,7 +25,7 @@ const DialogOverlay = React.forwardRef< - + Período de preferência
From f9a92106d047800b13edc2c928f85aab7e8b4b06 Mon Sep 17 00:00:00 2001 From: David William Date: Sun, 18 Aug 2024 00:49:45 -0300 Subject: [PATCH 11/16] fix: logout dialog confim condition and button sizes --- web/app/schedules/profile/page.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/web/app/schedules/profile/page.tsx b/web/app/schedules/profile/page.tsx index 215f1725..607151bc 100644 --- a/web/app/schedules/profile/page.tsx +++ b/web/app/schedules/profile/page.tsx @@ -83,14 +83,14 @@ export default function Profile() {
-
@@ -180,7 +180,7 @@ function ActionButtons({ A grade será deletada para sempre, tem certeza? -
+
diff --git a/web/app/schedules/profile/page.tsx b/web/app/schedules/profile/page.tsx index 607151bc..9547d94c 100644 --- a/web/app/schedules/profile/page.tsx +++ b/web/app/schedules/profile/page.tsx @@ -68,7 +68,7 @@ export default function Profile() { Trocar de conta - {localSchedules.length ? ( + {localSchedules.length > 0 ? ( From 6a3944f8c25d852d1e5e000880fe923ec87388a0 Mon Sep 17 00:00:00 2001 From: David William Date: Sun, 18 Aug 2024 00:58:56 -0300 Subject: [PATCH 13/16] fix: gramathical error at info page --- web/app/schedules/info/page.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/app/schedules/info/page.tsx b/web/app/schedules/info/page.tsx index c50d6d30..52aef4d9 100644 --- a/web/app/schedules/info/page.tsx +++ b/web/app/schedules/info/page.tsx @@ -60,7 +60,7 @@ function About() {

Sobre

O Sua Grade UnB é um projeto em desenvolvimento da matéria de{' '} - Métodos de Desenv`olvimento de Software. Seu propósito é + Métodos de Desenvolvimento de Software. Seu propósito é proporcionar aos estudantes da Universidade de Brasília uma experiência simplificada e intuitiva na elaboração de suas grades horárias. O projeto visa facilitar o processo de organização acadêmica, oferecendo From 73cab95a1df4352e0b4842fa10c1277ce6305c84 Mon Sep 17 00:00:00 2001 From: David William Date: Sun, 18 Aug 2024 14:34:45 -0300 Subject: [PATCH 14/16] fix: remove unsed console log --- web/app/components/SchedulePreview/SchedulePreview.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/web/app/components/SchedulePreview/SchedulePreview.tsx b/web/app/components/SchedulePreview/SchedulePreview.tsx index 11cdf9b3..66b26327 100644 --- a/web/app/components/SchedulePreview/SchedulePreview.tsx +++ b/web/app/components/SchedulePreview/SchedulePreview.tsx @@ -262,7 +262,6 @@ function handleDownloadPDF(isCloud: boolean, index: number) { const doc = document.getElementById( `download-content-${index}-${isCloud ? 'cloud' : 'local'}` ); - console.log(doc, `download-content-${index}-${isCloud ? 'cloud' : 'local'}`); if (!doc) return; From 7fcd76a3e9e85a832a057544a511d607746ae66d Mon Sep 17 00:00:00 2001 From: David William Date: Sun, 18 Aug 2024 14:46:24 -0300 Subject: [PATCH 15/16] merge: main recent changes into web/refactoring --- web/app/components/AsideSchedulePopUp/Form/InputForm.tsx | 4 ---- 1 file changed, 4 deletions(-) diff --git a/web/app/components/AsideSchedulePopUp/Form/InputForm.tsx b/web/app/components/AsideSchedulePopUp/Form/InputForm.tsx index 60a9f498..ba48c3f8 100644 --- a/web/app/components/AsideSchedulePopUp/Form/InputForm.tsx +++ b/web/app/components/AsideSchedulePopUp/Form/InputForm.tsx @@ -79,11 +79,7 @@ export default function InputForm(props: InputFormPropsType) { await handleDisciplineSearch(formData, props.setInfos); } -<<<<<<< HEAD return ( ); -======= - return ; ->>>>>>> origin/main } From bf6e34205bbed9ace103342b741666eec55e301d Mon Sep 17 00:00:00 2001 From: mateusvrs Date: Mon, 21 Oct 2024 09:54:51 -0300 Subject: [PATCH 16/16] fix: don't change year/period if already selected --- .../components/AsideSchedulePopUp/Form/DisciplineOptionForm.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/web/app/components/AsideSchedulePopUp/Form/DisciplineOptionForm.tsx b/web/app/components/AsideSchedulePopUp/Form/DisciplineOptionForm.tsx index 42aec00b..55a9cb24 100644 --- a/web/app/components/AsideSchedulePopUp/Form/DisciplineOptionForm.tsx +++ b/web/app/components/AsideSchedulePopUp/Form/DisciplineOptionForm.tsx @@ -38,6 +38,7 @@ function Form(props: FormPropsType) { Ano/Período