-
Notifications
You must be signed in to change notification settings - Fork 1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: new timetable page #12
base: dev
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
import { API } from '@/api/api'; | ||
import { TimetableEvent } from '@/api/users/getDailyTimetable'; | ||
import { TimetableCreateEntryRequestDto } from '@/api/timetable/timetable.interface'; | ||
|
||
export function createTimetableEvent(api: API, start: Date, end: Date, location: string, groups: string[]) { | ||
return api | ||
.post<TimetableCreateEntryRequestDto, Omit<TimetableEvent, 'column' | 'id'>>('/timetable/current', { | ||
firstRepetitionDate: start, | ||
repetitions: 1, | ||
duration: end.getTime() - start.getTime(), | ||
groups: [], | ||
location, | ||
}) | ||
.toPromise(); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
import { useAPI } from '@/api/api'; | ||
import { useEffect, useState } from 'react'; | ||
import { StatusCodes } from 'http-status-codes'; | ||
|
||
export function useGroups() { | ||
const api = useAPI(); | ||
const [groups, setGroups] = useState<string[]>([]); | ||
useEffect(() => { | ||
api.get<string[]>('/timetable/current/groups/').on(StatusCodes.OK, setGroups); | ||
}, []); | ||
return groups; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import { useAPI } from '@/api/api'; | ||
import { StatusCodes } from 'http-status-codes'; | ||
import { useEffect, useState } from 'react'; | ||
import { TimetableEvent } from '@/api/users/getDailyTimetable'; | ||
|
||
export function useTimetableEvents(firstDay: Date, numberOfDays: number): TimetableEvent[] { | ||
const api = useAPI(); | ||
const [events, setEvents] = useState<TimetableEvent[]>([]); | ||
useEffect(() => { | ||
api | ||
.get< | ||
TimetableEvent[] | ||
>(`/timetable/current/${numberOfDays}/${firstDay.getDate()}/${firstDay.getMonth() + 1}/${firstDay.getFullYear()}/`) | ||
.on(StatusCodes.OK, setEvents); | ||
}, [firstDay, numberOfDays]); | ||
return events; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
export interface TimetableCreateEntryRequestDto { | ||
location: string; | ||
duration: number; | ||
firstRepetitionDate: Date; | ||
repetitionFrequency?: number; | ||
repetitions?: number; | ||
groups: string[]; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
'use client'; | ||
import styles from './styles.module.scss'; | ||
import { useEffect, useState } from 'react'; | ||
import { useAPI } from '@/api/api'; | ||
import { DAY_LENGTH, roundToStartOfDay } from '@/utils/utils'; | ||
import { TimetableDay } from '@/components/timetable/TimetableDay'; | ||
import { format } from 'date-fns'; | ||
import Button from '@/components/UI/Button'; | ||
import { useTimetableEvents } from '@/api/timetable/getTimetableEvents'; | ||
import { createTimetableEvent } from '@/api/timetable/createTimetableEvent'; | ||
|
||
export default function TimetablePage() { | ||
const [firstDay, setFirstDay] = useState(roundToStartOfDay(new Date())); | ||
const [numberOfDays, setNumberOfDays] = useState(7); | ||
const events = useTimetableEvents(firstDay, numberOfDays); | ||
const [eventIndicesPerDay, setEventIndicesPerDay] = useState([] as number[][]); | ||
const api = useAPI(); | ||
useEffect(() => { | ||
const offset = numberOfDays === 7 ? -(firstDay.getDay() === 0 ? 6 : firstDay.getDay() - 1) : 0; | ||
setFirstDay(new Date(firstDay.getTime() + offset * DAY_LENGTH)); // I verified, there is no problem with changing the clocks :) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Logique que ça change pas si tu changes l'horloge (puisqu'on travaille avec des timestamps - donc relatifs au début de l'époque Unix en UTC) Le changement de fuseau horaire pourrait poser des soucis mais pas à cet endroit là : imagine tu regardes un emploi du temps avec des heures françaises alors que t'es sur un autre fuseau horaire, qu'est ce que ça donne ? En l'occurrence (j'ai pas vérifié, je dis ça en lisant le code), vu que Je ne sais pas si ce qui est pertinent entre le fait de s'adapter au fuseau horaire local ou de proposer un edt "fixe" :
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. De mémoire j'avais eu un ptit traumatisme avec les fuseaux horaires dans cette PR >.< Je pense que ce point ça peut être un truc pour plus tard, je pense que c'est le genre de détail sur lequel faut pas forcément s'attarder sinon on aura jamais fini (on pourra toujours faire mieux). Par contre ouais, en vrai ça peut être sympa de tenir une liste. Flemme de le faire ce soir tho, je le fais demain si j'y pense |
||
}, [numberOfDays]); | ||
useEffect(() => { | ||
const newEventsPerDay = new Array(numberOfDays).fill(undefined).map(() => []) as number[][]; | ||
for (let i = 0; i < events.length; i++) { | ||
const event = events[i]; | ||
const startDayIndex = Math.floor((roundToStartOfDay(event.start).getTime() - firstDay.getTime()) / DAY_LENGTH); | ||
const endDayIndex = Math.floor((roundToStartOfDay(event.end).getTime() - firstDay.getTime()) / DAY_LENGTH); | ||
for (let j = startDayIndex; j < endDayIndex; j++) { | ||
newEventsPerDay[j].push(i); | ||
} | ||
} | ||
setEventIndicesPerDay(newEventsPerDay); | ||
}, [events, firstDay, numberOfDays]); | ||
|
||
return ( | ||
<div className={styles.timetablePage}> | ||
<h1>Timetable</h1> | ||
<div className={styles.settings}> | ||
<div className={styles.range}> | ||
<Button noStyle onClick={() => setFirstDay(new Date(firstDay.getTime() - DAY_LENGTH * numberOfDays))}> | ||
{'<'} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. En vrai pourquoi ne pas utiliser une icône LeftChevron ? (idem symétriquement pour RightChevron) |
||
</Button> | ||
<p> | ||
{format(firstDay, `d MMMM yyyy`)}{' '} | ||
au{' '} | ||
{format(new Date(firstDay.getTime() + (numberOfDays - 1) * DAY_LENGTH), `d MMMM yyyy`)} | ||
</p> | ||
<Button noStyle onClick={() => setFirstDay(new Date(firstDay.getTime() + DAY_LENGTH * numberOfDays))}> | ||
{'>'} | ||
</Button> | ||
</div> | ||
<div className={styles.rangeLength}> | ||
<Button noStyle onClick={() => setNumberOfDays(1)}> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Le style est ptet à revoir je pense 😅 |
||
1 jour | ||
</Button> | ||
<Button noStyle onClick={() => setNumberOfDays(3)}> | ||
3 jours | ||
</Button> | ||
<Button noStyle onClick={() => setNumberOfDays(7)}> | ||
7 jours | ||
</Button> | ||
</div> | ||
</div> | ||
<div className={styles.timetable}> | ||
{eventIndicesPerDay.map((eventIndices, i) => ( | ||
<div key={i} className={styles.day}> | ||
<h2 className={styles.dayTitle}> | ||
{format(new Date(firstDay.getTime() + i * DAY_LENGTH), `ccc d`)} | ||
</h2> | ||
<TimetableDay | ||
events={eventIndices.map((i) => events[i])} | ||
day={new Date(firstDay.getTime() + i * DAY_LENGTH)} | ||
className={styles.dayTimetable} | ||
onClickOnEmptySlot={ | ||
(time) => createTimetableEvent(api, time, new Date(time.getTime() + 3_600_000), 'a random place', []) // TODO : It's missing an interface to fill-in the info woopsyyy | ||
} | ||
/> | ||
</div> | ||
))} | ||
</div> | ||
</div> | ||
); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
.timetablePage { | ||
height: 100%; | ||
display: flex; | ||
flex-direction: column; | ||
gap: 2rem; | ||
|
||
.settings { | ||
display: flex; | ||
justify-content: space-between; | ||
align-items: center; | ||
margin-left: 3rem; | ||
margin-right: 1.5rem; | ||
|
||
.rangeLength { | ||
> button { | ||
margin: 0 10px; | ||
&:first-child { | ||
margin-left: 0; | ||
} | ||
&:last-child { | ||
margin-right: 0; | ||
} | ||
} | ||
} | ||
|
||
.range { | ||
display: flex; | ||
justify-content: space-between; | ||
align-items: center; | ||
width: 40%; | ||
align-self: center; | ||
|
||
& > p { | ||
margin: auto 0; | ||
font-size: 1.2rem; | ||
} | ||
|
||
& > button { | ||
font-size: 2rem; | ||
} | ||
} | ||
} | ||
|
||
.timetable { | ||
display: flex; | ||
bottom: 0; | ||
position: relative; | ||
height: auto; | ||
gap: 10px; | ||
flex-grow: 1; | ||
|
||
.day { | ||
flex-grow: 1; | ||
|
||
.dayTitle { | ||
text-align: center; | ||
} | ||
|
||
.dayTimetable { | ||
height: 100%; | ||
} | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
@import '@/variables.scss'; | ||
|
||
.timetableDay { | ||
flex-grow: 1; | ||
display: flex; | ||
gap: 10px; | ||
|
||
.hours { | ||
display: flex; | ||
flex-direction: column; | ||
justify-content: space-between; | ||
height: 100%; | ||
overflow-y: hidden; // The page would become scrollable for 1 pixel, probably due to a computing imprecision somewhere in the interpretation of the CSS. | ||
|
||
> div { | ||
flex-grow: 1; | ||
text-align: center; | ||
display: flex; | ||
align-items: center; | ||
justify-content: flex-end; | ||
} | ||
} | ||
|
||
.events { | ||
display: flex; | ||
flex-direction: column; | ||
justify-content: space-between; | ||
height: 100%; | ||
position: relative; | ||
flex-grow: 1; | ||
overflow-y: hidden; // The page would become scrollable for 1 pixel, probably due to a computing imprecision somewhere in the interpretation of the CSS. | ||
|
||
.timeSeparator { | ||
width: 100%; | ||
flex-grow: 1; | ||
position: relative; | ||
|
||
&:after { | ||
content: ''; | ||
position: absolute; | ||
width: 100%; | ||
height: 1px; | ||
background-color: $ung-dark-grey; | ||
top: calc(50% - 1px); | ||
left: 0; | ||
} | ||
} | ||
|
||
.event { | ||
position: absolute; | ||
background-color: red; | ||
width: 100%; | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Peut être plus simple à écrire comme ça ?
Ps : peut être commenter et préciser que le hook sert à se recaler sur le début d'une semaine ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Je sais pas si c'est plus lisible tho