First try at availability picker

This commit is contained in:
Miroslav Vasilev 2024-01-09 17:23:40 +02:00
parent a665c5e59d
commit 0dcf39b48a
18 changed files with 1038 additions and 58 deletions

View file

@ -13,8 +13,11 @@
"@emotion/react": "^11.11.3",
"@emotion/styled": "^11.11.0",
"@mui/material": "^5.15.3",
"@mui/x-date-pickers": "^6.18.7",
"dayjs": "^1.11.10",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-github-corner": "^2.5.0",
"react-router-dom": "^6.21.1"
},
"devDependencies": {

View file

@ -1,3 +1,15 @@
@import url('https://fonts.googleapis.com/css2?family=Bungee+Spice&display=swap');
.noselect {
-webkit-touch-callout: none; /* iOS Safari */
-webkit-user-select: none; /* Safari */
-khtml-user-select: none; /* Konqueror HTML */
-moz-user-select: none; /* Old versions of Firefox */
-ms-user-select: none; /* Internet Explorer/Edge */
user-select: none; /* Non-prefixed version, currently
supported by Chrome, Edge, Opera and Firefox */
}
#root {
max-width: 1280px;
margin: 0 auto;

View file

@ -0,0 +1,308 @@
import { Box, Card, Divider, Stack, Typography } from "@mui/material";
import dayjs, { Dayjs } from "dayjs";
import { useEffect, useState } from "react";
import * as utc from 'dayjs/plugin/utc';
import * as timezone from 'dayjs/plugin/timezone';
import * as localizedFormat from 'dayjs/plugin/localizedFormat';
import utils from "../utils";
import { EventTypes } from "../types/Event";
dayjs.extend(utc)
dayjs.extend(timezone)
dayjs.extend(localizedFormat)
type AvailabilityTime = {
fromTime: Dayjs,
toTime: Dayjs
}
type AvailabilityDay = {
forDate: Dayjs,
availableTimes: AvailabilityTime[]
}
const HALFHOUR_DISPLAY_HEIGHT: number = 15;
const DAY_DISPLAY_WIDTH: String = "150px";
export default function AvailabilityPicker(props: {
fromDate: Dayjs,
toDate: Dayjs,
eventType: String,
availabilityDurationInMinutes: number
}) {
const [days, setDays] = useState<AvailabilityDay[]>([]);
const [selectingAvailabilityForDay, setAvailabilityDayBeingSelectedFor] = useState<AvailabilityDay | null>(null);
const [currentAvailabilityTime, setAvailabilityTime] = useState<AvailabilityTime | null>(null);
useEffect(() => {
let localTimezone = dayjs.tz.guess();
let localFromDate = props.fromDate.tz(localTimezone);
let localToDate = props.toDate.tz(localTimezone);
switch (props.eventType) {
case EventTypes.SPECIFIC_DATE: {
createAvailabilitiesBasedOnInitialDate(localFromDate, 1);
break;
}
case EventTypes.DATE_RANGE: {
createAvailabilitiesBasedOnInitialDate(localFromDate, Math.abs(localFromDate.diff(localToDate, "day", false)));
break;
}
case EventTypes.DAY: {
createAvailabilitiesBasedOnUnspecifiedInitialDate(1, localTimezone);
break;
}
case EventTypes.WEEK: {
createAvailabilitiesBasedOnUnspecifiedInitialDate(7, localTimezone);
break;
}
}
}, [props]);
useEffect(() => {
console.log(days)
}, [days])
function createAvailabilitiesBasedOnUnspecifiedInitialDate(numberOfDays: number, tz: string) {
createAvailabilitiesBasedOnInitialDate(dayjs.tz("1970-01-05 00:00:00", tz), numberOfDays);
}
function createAvailabilitiesBasedOnInitialDate(date: Dayjs, numberOfDays: number) {
let availabilities: AvailabilityDay[] = [];
for (var i: number = 0; i < numberOfDays; i++) {
let availability: AvailabilityDay = {
forDate: date.add(i, "day").startOf("day"),
availableTimes: []
}
availabilities.push(availability);
}
setDays(availabilities);
}
function clearAvailabilityTimeSelection() {
setAvailabilityDayBeingSelectedFor(null);
setAvailabilityTime(null);
}
function beginAvailabilityTimeSelection(e: React.MouseEvent<HTMLDivElement, MouseEvent>, day: AvailabilityDay, startTime: Dayjs) {
setAvailabilityDayBeingSelectedFor(day);
setAvailabilityTime({
fromTime: startTime,
toTime: startTime
});
}
function finishAvailabilityTimeSelection(e: React.MouseEvent<HTMLDivElement, MouseEvent>, day: AvailabilityDay) {
if (currentAvailabilityTime === null) {
return;
}
day.availableTimes.push(currentAvailabilityTime);
setDays([...days])
clearAvailabilityTimeSelection();
}
function addTimeToAvailabilityTimeSelection(e: React.MouseEvent<HTMLDivElement, MouseEvent>, day: AvailabilityDay, time: Dayjs) {
if (e.buttons !== 1) {
return;
}
if (currentAvailabilityTime === null) {
return;
}
if (currentAvailabilityTime !== null && selectingAvailabilityForDay !== null && Math.abs(selectingAvailabilityForDay.forDate.diff(time, "day")) >= 1) {
clearAvailabilityTimeSelection();
return;
}
let currentFrom = currentAvailabilityTime.fromTime;
let currentTo = currentAvailabilityTime.toTime;
if (time.isBefore(currentFrom)) {
setAvailabilityTime({
fromTime: time,
toTime: currentTo
})
return;
}
if (time.isAfter(currentTo)) {
setAvailabilityTime({
fromTime: currentFrom,
toTime: time
})
return;
}
}
function currentAvailabilityTimeSelectionIncludes(time: Dayjs): boolean {
if (currentAvailabilityTime === null) {
return false;
}
if ((time.isAfter(currentAvailabilityTime.fromTime) && time.isBefore(currentAvailabilityTime.toTime)) || (time.isSame(currentAvailabilityTime.toTime) || time.isSame(currentAvailabilityTime.fromTime))) {
return true;
}
return false;
}
function isTimeIncludedInAnyAvailabilityPeriod(day: AvailabilityDay, time: Dayjs): boolean {
return day.availableTimes.some(t => t.fromTime.isBefore(time) && t.toTime.isAfter(time));
}
function isTimeBeginningOfAnyAvailabilityPeriod(day: AvailabilityDay, time: Dayjs): boolean {
return day.availableTimes.some(t => t.fromTime.isSame(time));
}
function isTimeEndingOfAnyAvailabilityPeriod(day: AvailabilityDay, time: Dayjs): boolean {
return day.availableTimes.some(t => t.toTime.isSame(time));
}
function generateDay(day: AvailabilityDay) {
const HOVER_COLOR: String = "#004455";
const HOUR_LIGHT_COLOR: String = "#002233";
const HOUR_DARK_COLOR: String = "#003344";
const HOUR_BORDER_COLOR: String = "#777";
const ACTIVE_COLOR: String = "#223300";
const CURRENTLY_SELECTED_COLOR: String = "#112200";
const HOUR_TEXT_COLOR: String = "#ddd";
const HALFHOUR_BORDER_COLOR: String = "#333";
let hours = [...Array<String>(24)].map((_, i) => {
let time = day.forDate.set("hour", i).set("minute", 0).set("second", 0);
return (
<Box
key={`${i}`}
sx={{
width: "100%",
borderBottom: 1,
borderColor: HOUR_BORDER_COLOR,
bgcolor: (i % 2 == 0) ? HOUR_LIGHT_COLOR : HOUR_DARK_COLOR,
":hover": {
bgcolor: HOVER_COLOR
}
}}
>
<Box
sx={{
width: "100%",
height: HALFHOUR_DISPLAY_HEIGHT,
borderBottom: 1,
borderColor: HALFHOUR_BORDER_COLOR,
":active": {
bgcolor: ACTIVE_COLOR
},
bgcolor: currentAvailabilityTimeSelectionIncludes(time) ? CURRENTLY_SELECTED_COLOR : "inherit"
}}
onMouseDown={(e) => beginAvailabilityTimeSelection(e, day, time)}
onMouseUp={(e) => finishAvailabilityTimeSelection(e, day)}
onMouseOver={(e) => addTimeToAvailabilityTimeSelection(e, day, time)}
>
<Typography
className={"noselect"}
textAlign={"left"}
fontSize={"0.65em"}
color={HOUR_TEXT_COLOR}
>
{ utils.formatTimeFromHourOfDay(i, 0) }
</Typography>
</Box>
<Box
sx={{
width: "100%",
height: HALFHOUR_DISPLAY_HEIGHT,
":active": {
bgcolor: ACTIVE_COLOR
},
bgcolor: currentAvailabilityTimeSelectionIncludes(time.set("minute", 30)) ? CURRENTLY_SELECTED_COLOR : "inherit"
}}
onMouseDown={(e) => beginAvailabilityTimeSelection(e, day, time.set("minute", 30))}
onMouseUp={(e) => finishAvailabilityTimeSelection(e, day)}
onMouseOver={(e) => addTimeToAvailabilityTimeSelection(e, day, time.set("minute", 30))}
>
</Box>
</Box>
);
})
return (
<Stack
key={day.forDate.format()}
direction="column"
sx={{
minWidth: DAY_DISPLAY_WIDTH,
width: DAY_DISPLAY_WIDTH
}}
overflow={"visible"}
>
<Card
sx={{
width: "100%",
height: "fit-content",
overflow: "visible"
}}
variant="outlined"
onMouseLeave={(e) => clearAvailabilityTimeSelection()}
>
<Box
sx={{ width: "100%" }}
padding={1}
>
{
(props.eventType === EventTypes.WEEK) &&
<Typography>
{ day.forDate.format("dddd") }
</Typography>
}
{
(props.eventType === EventTypes.DAY) &&
<Typography>
Any Day
</Typography>
}
{
(props.eventType === EventTypes.DATE_RANGE || props.eventType === EventTypes.SPECIFIC_DATE) &&
<Typography>
{ day.forDate.format("LL") }
</Typography>
}
</Box>
<Divider></Divider>
{hours}
</Card>
</Stack>
);
}
return (
<Stack
direction="row"
spacing={1}
justifyContent={"safe center"}
sx={{
width: "100%",
height: "auto",
maxHeight: "500px",
overflowY: "scroll",
overflowX: "scroll"
}}
>
{
days.map(a => generateDay(a))
}
</Stack>
);
}

View file

@ -0,0 +1,15 @@
import { Stack, Typography } from '@mui/material';
export default function Footer() {
return (
<Stack
sx={{ height: "50px" }}
direction="column"
justifyContent="center"
>
<Typography align="center">
Created by <a href="https://mvvasilev.dev">mvvasilev</a> | <a href="https://github.com/mvvasilev/findtheti.me">Github</a>
</Typography>
</Stack>
);
}

View file

@ -0,0 +1,30 @@
import { Stack, Typography, useTheme } from '@mui/material';
export default function Header() {
const theme = useTheme();
return (
<Stack
sx={{ height: "100px" }}
direction="column"
justifyContent="center"
>
<a href={window.location.origin}>
<Typography
align="center"
sx={{
fontFamily: "'Bungee Spice', sans-serif",
[theme.breakpoints.up("xs")]: {
fontSize: "2em"
},
[theme.breakpoints.up("sm")]: {
fontSize: "4em"
}
}}
>
findtheti.me
</Typography>
</a>
</Stack>
);
}

View file

@ -1,11 +1,82 @@
import { useEffect, useState } from "react";
import { useParams } from "react-router-dom";
import { Event, createEvent } from '../types/Event';
import Grid from '@mui/material/Unstable_Grid2'
import { Button, TextField, Typography } from "@mui/material";
import AvailabilityPicker from "../components/AvailabilityPicker";
import dayjs from "dayjs";
import utils from "../utils";
export default function ExistingEventPage() {
let { pasteId } = useParams();
let { eventId } = useParams();
console.log(pasteId);
const [event, setEvent] = useState<Event>(createEvent());
useEffect(() => {
fetch(`/api/events/${eventId}`)
.then(resp => resp.json())
.then(resp => setEvent({
name: resp.result?.name,
description: resp.result?.description,
fromDate: dayjs.utc(resp.result?.from_date),
toDate: dayjs.utc(resp.result?.to_date),
eventType: resp.result?.event_type,
snowflakeId: resp.result?.snowflake_id,
duration: resp.result?.duration
}));
}, [eventId]);
return (
<div />
<Grid container sx={{ p: 2 }} spacing={1}>
<Grid xs={12}>
<Typography>You've been invited to...</Typography>
</Grid>
<Grid xs={12}>
<Typography variant="h4">{ event.name }</Typography>
</Grid>
{
(event.description !== null) &&
<Grid xs={12}>
<Typography>{ event.description }</Typography>
</Grid>
}
<Grid xs={12}>
<Typography>
This event lasts for { utils.formatMinutesAsHoursMinutes(event.duration) }. When will you be available to attend?
</Typography>
</Grid>
<Grid xs={12}>
{
(event.fromDate !== null && event.toDate !== null && event.eventType !== null) &&
<AvailabilityPicker
fromDate={event.fromDate}
toDate={event.toDate}
eventType={event.eventType}
availabilityDurationInMinutes={event.duration}
/>
}
</Grid>
<Grid xs={0} md={3}></Grid>
<Grid xs={12} md={6} container spacing={1}>
<Grid xs={12} sm={9}>
<TextField
sx={{ width: "100%" }}
// TODO
// value={event.description}
// onChange={(e) => {
// event.description = e.target.value;
// setEvent({...event});
// }}
label="Your Name"
/>
</Grid>
<Grid xs={12} sm={3}>
<Button sx={{ width: "100%", height: "100%" }} variant="contained">
<Typography>Submit</Typography>
</Button>
</Grid>
</Grid>
<Grid xs={0} md={3}></Grid>
</Grid>
);
}

View file

@ -1,5 +1,210 @@
import { Alert, Button, MenuItem, Select, Slider, TextField, Typography } from '@mui/material';
import Grid from '@mui/material/Unstable_Grid2'
import { DateTimePicker } from '@mui/x-date-pickers';
import { useEffect, useState } from 'react';
import { useNavigate } from "react-router-dom"
import { Event, EventTypes, createEvent } from '../types/Event';
import utils from '../utils';
export default function NewEventPage() {
const navigate = useNavigate();
const [event, setEvent] = useState<Event>(createEvent());
const [isEventValid, setEventValid] = useState<Boolean>(false);
useEffect(() => {
validateEvent();
}, [event])
function validateEvent(): void {
console.log(event);
var valid: boolean = true;
valid &&= event.name && event.name !== "";
valid &&= event.eventType !== EventTypes.UNKNOWN || event.eventType !== null;
if (event.eventType === EventTypes.DATE_RANGE) {
valid &&= event.fromDate !== null;
valid &&= event.toDate !== null;
}
if (event.eventType === EventTypes.SPECIFIC_DATE) {
valid &&= event.fromDate !== null;
}
setEventValid(valid);
}
function saveEvent() {
fetch("/api/events", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
from_date: event.fromDate?.utc().format(),
to_date: event.toDate?.utc().format(),
name: event.name,
description: event.description,
event_type: event.eventType,
duration: event.duration
})
})
.then(resp => resp.json())
.then(resp => {
navigate(resp.result.snowflake_id)
})
}
return (
<div />
<Grid container>
<Grid xs={12} spacing={1}>
<h2>Create New Event</h2>
</Grid>
<Grid xs={0} sm={2} md={4}></Grid>
<Grid sx={{ p: 2 }} container spacing={1} xs={12} sm={8} md={4}>
<Grid xs={12}>
<TextField
sx={{ width: "100%" }}
value={event.name}
onChange={(e) => {
event.name = e.target.value;
setEvent({...event});
}}
label="I'm organizing a(n)..."
/>
</Grid>
<Grid xs={12}>
<TextField
sx={{ width: "100%" }}
value={event.description}
onChange={(e) => {
event.description = e.target.value;
setEvent({...event});
}}
label="More details... ( Optional )"
/>
</Grid>
<Grid xs={12}>
<Typography>
Duration
</Typography>
<Slider
sx={{ width: "90%" }}
step={30}
valueLabelDisplay="auto"
valueLabelFormat={(val) => utils.formatMinutesAsHoursMinutes(val)}
marks={
[
{
value: 30,
label: "30m"
},
{
value: 120,
label: "2h"
},
{
value: 240,
label: "4h"
},
{
value: 360,
label: "6h"
},
{
value: 480,
label: "8h"
}
]
}
min={30}
max={480}
value={event.duration}
onChange={(_, val) => {
event.duration = val as number;
setEvent({...event});
}}
/>
</Grid>
<Grid xs={12}>
<Select
sx={{ width: "100%" }}
value={event.eventType}
onChange={(e) => {
event.eventType = e.target.value;
setEvent({...event});
}}
>
<MenuItem value={EventTypes.UNKNOWN} disabled>Event Type</MenuItem>
<MenuItem value={EventTypes.SPECIFIC_DATE}>Exact Date</MenuItem>
<MenuItem value={EventTypes.DATE_RANGE}>Between</MenuItem>
<MenuItem value={EventTypes.DAY}>Daily</MenuItem>
<MenuItem value={EventTypes.WEEK}>Weekly</MenuItem>
</Select>
</Grid>
{
event.eventType == EventTypes.SPECIFIC_DATE &&
<Grid xs={12}>
<DateTimePicker
sx={{ width: "100%" }}
value={event.fromDate}
onChange={(value) => {
event.fromDate = value ?? null;
setEvent({...event});
}}
label="When"
/>
</Grid>
}
{
event.eventType == EventTypes.DATE_RANGE &&
<Grid xs={12} sm={6}>
<DateTimePicker
sx={{ width: "100%" }}
value={event.fromDate}
onChange={(value) => {
event.fromDate = value ?? null;
setEvent({...event});
}}
label="From"
/>
</Grid>
}
{
event.eventType == EventTypes.DATE_RANGE &&
<Grid xs={12} sm={6}>
<DateTimePicker
sx={{ width: "100%" }}
value={event.toDate}
onChange={(value) => {
event.toDate = value ?? null;
setEvent({...event});
}}
label="To"
/>
</Grid>
}
{
(event.eventType == EventTypes.DAY || event.eventType == EventTypes.WEEK || event.eventType == EventTypes.MONTH) &&
<Grid xs={12}>
<Alert severity={"info"}>
<Typography>Selecting the Day type will allow attendees to select their availability during an unspecified {event.eventType}</Typography>
</Alert>
</Grid>
}
<Grid xs={12}>
<Button
disabled={!isEventValid}
sx={{ width: "100%" }}
variant={"contained"}
onClick={saveEvent}
>
<Typography>Create</Typography>
</Button>
</Grid>
</Grid>
<Grid xs={0} sm={2} md={4}></Grid>
</Grid>
);
}
}

View file

@ -1,5 +1,15 @@
import { ThemeProvider } from "@emotion/react";
import { Box, CssBaseline, createTheme } from "@mui/material";
import { CssBaseline, Divider, Paper, createTheme } from "@mui/material";
import Grid from '@mui/material/Unstable_Grid2'
import GithubCorner from "react-github-corner";
import Header from "../components/Header";
import Footer from "../components/Footer";
import { LocalizationProvider } from "@mui/x-date-pickers";
import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'
import dayjs from "dayjs";
import * as utc from 'dayjs/plugin/utc';
dayjs.extend(utc);
const theme = createTheme({
palette: {
@ -12,21 +22,51 @@ export default function RootLayout(props: { children: React.ReactNode }) {
<ThemeProvider theme={theme}>
<CssBaseline/>
<Box
<GithubCorner
href={"https://github.com/mvvasilev/findtheti.me"}
bannerColor="#FD6C6C"
octoColor="inherit"
size={80}
direction="right"
/>
<Paper
component="main"
sx={{
position: "absolute",
top: "0",
top: 0,
left: "50%",
transform: "translate(-50%, 0)",
width: {
xs: "100%",
lg: "1000px"
},
[theme.breakpoints.up('lg')]: {
top: "25px",
width: "1000px"
}
}}
>
{props.children}
</Box>
<LocalizationProvider dateAdapter={AdapterDayjs}>
<Grid container>
<Grid xs={12}>
<Header />
</Grid>
<Grid xs={12}>
<Divider></Divider>
</Grid>
<Grid xs={12}>
{props.children}
</Grid>
<Grid xs={12}>
<Divider></Divider>
</Grid>
<Grid xs={12}>
<Footer />
</Grid>
</Grid>
</LocalizationProvider>
</Paper>
</ThemeProvider>
);
};

View file

@ -0,0 +1,32 @@
import { Dayjs } from "dayjs";
export type Event = {
snowflakeId: String,
name: String,
description: String,
fromDate: null | Dayjs,
toDate: null | Dayjs,
eventType: String,
duration: number
};
export const EventTypes = {
UNKNOWN: "Unknown",
SPECIFIC_DATE: "SpecificDate",
DATE_RANGE: "DateRange",
DAY: "Day",
WEEK: "Week",
MONTH: "Month" // Unsupported atm
};
export function createEvent(): Event {
return {
snowflakeId: "",
name: "",
description: "",
fromDate: null,
toDate: null,
eventType: EventTypes.UNKNOWN,
duration: 30
};
}

30
frontend/src/utils.ts Normal file
View file

@ -0,0 +1,30 @@
import dayjs from "dayjs";
import * as duration from 'dayjs/plugin/duration';
dayjs.extend(duration)
const utils = {
toHoursAndMinutes: (totalMinutes: number): { hours: number, minutes: number } => {
const hours = Math.floor(totalMinutes / 60);
const minutes = totalMinutes % 60;
return { hours, minutes };
},
formatMinutesAsHoursMinutes: (val: number): String => {
let { hours, minutes } = utils.toHoursAndMinutes(val);
if (hours > 0) {
return `${hours}h ${minutes}m`;
} else {
return `${minutes}m`;
}
},
zeroPad: (num: number, places: number): String => {
return String(num).padStart(places, '0');
},
formatTimeFromHourOfDay: (hourOfDay: number, minutes: number): String => {
return dayjs.duration({ hours: hourOfDay, minutes: minutes }).format('HH:mm');
}
}
export default utils;

View file

@ -6,6 +6,15 @@ import { fileURLToPath, URL } from 'node:url'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [react()],
server: {
proxy: {
"/api": {
target: "http://localhost:8080",
changeOrigin: true,
secure: false,
},
}
},
resolve: {
alias: [
{ find: '@', replacement: fileURLToPath(new URL('./src', import.meta.url)) }

View file

@ -28,7 +28,7 @@
resolved "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.23.5.tgz"
integrity sha512-uU27kfDRlhfKl+w1U6vp16IuvSLtjAxdArVXPa9BvLkrr7CYIsxH5adpHObeAGY/41+syctUWOZ140a2Rvkgjw==
"@babel/core@^7.0.0", "@babel/core@^7.0.0-0", "@babel/core@^7.23.5":
"@babel/core@^7.23.5":
version "7.23.7"
resolved "https://registry.npmjs.org/@babel/core/-/core-7.23.7.tgz"
integrity sha512-+UpDgowcmqe36d4NwqvKsyPMlOLNGMsfMmQ5WGCu+siCe3t3dfe9njrzGfdN4qq+bcNUt0+Vw6haRxBOycs4dw==
@ -186,6 +186,13 @@
dependencies:
regenerator-runtime "^0.14.0"
"@babel/runtime@^7.23.2":
version "7.23.8"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.23.8.tgz#8ee6fe1ac47add7122902f257b8ddf55c898f650"
integrity sha512-Y7KbAP984rn1VGMbGqKmBLio9V7y5Je9GvU4rQPCPinCyNfUcToxIXl06d59URp/F3LwinvODxab5N/G6qggkw==
dependencies:
regenerator-runtime "^0.14.0"
"@babel/template@^7.22.15":
version "7.22.15"
resolved "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz"
@ -265,7 +272,7 @@
resolved "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.1.tgz"
integrity sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA==
"@emotion/react@^11.0.0-rc.0", "@emotion/react@^11.11.3", "@emotion/react@^11.4.1", "@emotion/react@^11.5.0":
"@emotion/react@^11.11.3":
version "11.11.3"
resolved "https://registry.npmjs.org/@emotion/react/-/react-11.11.3.tgz"
integrity sha512-Cnn0kuq4DoONOMcnoVsTOR8E+AdnKFf//6kUWc4LCdnxj31pZWn7rIULd6Y7/Js1PiPHzn7SKCM9vB/jBni8eA==
@ -295,7 +302,7 @@
resolved "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.2.2.tgz"
integrity sha512-0QBtGvaqtWi+nx6doRwDdBIzhNdZrXUppvTM4dtZZWEGTXL/XE/yJxLMGlDT1Gt+UHH5IX1n+jkXyytE/av7OA==
"@emotion/styled@^11.11.0", "@emotion/styled@^11.3.0":
"@emotion/styled@^11.11.0":
version "11.11.0"
resolved "https://registry.npmjs.org/@emotion/styled/-/styled-11.11.0.tgz"
integrity sha512-hM5Nnvu9P3midq5aaXj4I+lnSfNi7Pmd4EWk1fOZ3pxookaQTNew6bp4JaCBYM4HVFZF9g7UjJmsUmC2JlxOng==
@ -327,11 +334,121 @@
resolved "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.3.1.tgz"
integrity sha512-EsBwpc7hBUJWAsNPBmJy4hxWx12v6bshQsldrVmjxJoc3isbxhOrF2IcCpaXxfvq03NwkI7sbsOLXbYuqF/8Ww==
"@esbuild/aix-ppc64@0.19.11":
version "0.19.11"
resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.19.11.tgz#2acd20be6d4f0458bc8c784103495ff24f13b1d3"
integrity sha512-FnzU0LyE3ySQk7UntJO4+qIiQgI7KoODnZg5xzXIrFJlKd2P2gwHsHY4927xj9y5PJmJSzULiUCWmv7iWnNa7g==
"@esbuild/android-arm64@0.19.11":
version "0.19.11"
resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.19.11.tgz#b45d000017385c9051a4f03e17078abb935be220"
integrity sha512-aiu7K/5JnLj//KOnOfEZ0D90obUkRzDMyqd/wNAUQ34m4YUPVhRZpnqKV9uqDGxT7cToSDnIHsGooyIczu9T+Q==
"@esbuild/android-arm@0.19.11":
version "0.19.11"
resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.19.11.tgz#f46f55414e1c3614ac682b29977792131238164c"
integrity sha512-5OVapq0ClabvKvQ58Bws8+wkLCV+Rxg7tUVbo9xu034Nm536QTII4YzhaFriQ7rMrorfnFKUsArD2lqKbFY4vw==
"@esbuild/android-x64@0.19.11":
version "0.19.11"
resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.19.11.tgz#bfc01e91740b82011ef503c48f548950824922b2"
integrity sha512-eccxjlfGw43WYoY9QgB82SgGgDbibcqyDTlk3l3C0jOVHKxrjdc9CTwDUQd0vkvYg5um0OH+GpxYvp39r+IPOg==
"@esbuild/darwin-arm64@0.19.11":
version "0.19.11"
resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.19.11.tgz#533fb7f5a08c37121d82c66198263dcc1bed29bf"
integrity sha512-ETp87DRWuSt9KdDVkqSoKoLFHYTrkyz2+65fj9nfXsaV3bMhTCjtQfw3y+um88vGRKRiF7erPrh/ZuIdLUIVxQ==
"@esbuild/darwin-x64@0.19.11":
version "0.19.11"
resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.19.11.tgz#62f3819eff7e4ddc656b7c6815a31cf9a1e7d98e"
integrity sha512-fkFUiS6IUK9WYUO/+22omwetaSNl5/A8giXvQlcinLIjVkxwTLSktbF5f/kJMftM2MJp9+fXqZ5ezS7+SALp4g==
"@esbuild/freebsd-arm64@0.19.11":
version "0.19.11"
resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.11.tgz#d478b4195aa3ca44160272dab85ef8baf4175b4a"
integrity sha512-lhoSp5K6bxKRNdXUtHoNc5HhbXVCS8V0iZmDvyWvYq9S5WSfTIHU2UGjcGt7UeS6iEYp9eeymIl5mJBn0yiuxA==
"@esbuild/freebsd-x64@0.19.11":
version "0.19.11"
resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.19.11.tgz#7bdcc1917409178257ca6a1a27fe06e797ec18a2"
integrity sha512-JkUqn44AffGXitVI6/AbQdoYAq0TEullFdqcMY/PCUZ36xJ9ZJRtQabzMA+Vi7r78+25ZIBosLTOKnUXBSi1Kw==
"@esbuild/linux-arm64@0.19.11":
version "0.19.11"
resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.19.11.tgz#58ad4ff11685fcc735d7ff4ca759ab18fcfe4545"
integrity sha512-LneLg3ypEeveBSMuoa0kwMpCGmpu8XQUh+mL8XXwoYZ6Be2qBnVtcDI5azSvh7vioMDhoJFZzp9GWp9IWpYoUg==
"@esbuild/linux-arm@0.19.11":
version "0.19.11"
resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.19.11.tgz#ce82246d873b5534d34de1e5c1b33026f35e60e3"
integrity sha512-3CRkr9+vCV2XJbjwgzjPtO8T0SZUmRZla+UL1jw+XqHZPkPgZiyWvbDvl9rqAN8Zl7qJF0O/9ycMtjU67HN9/Q==
"@esbuild/linux-ia32@0.19.11":
version "0.19.11"
resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.19.11.tgz#cbae1f313209affc74b80f4390c4c35c6ab83fa4"
integrity sha512-caHy++CsD8Bgq2V5CodbJjFPEiDPq8JJmBdeyZ8GWVQMjRD0sU548nNdwPNvKjVpamYYVL40AORekgfIubwHoA==
"@esbuild/linux-loong64@0.19.11":
version "0.19.11"
resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.19.11.tgz#5f32aead1c3ec8f4cccdb7ed08b166224d4e9121"
integrity sha512-ppZSSLVpPrwHccvC6nQVZaSHlFsvCQyjnvirnVjbKSHuE5N24Yl8F3UwYUUR1UEPaFObGD2tSvVKbvR+uT1Nrg==
"@esbuild/linux-mips64el@0.19.11":
version "0.19.11"
resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.19.11.tgz#38eecf1cbb8c36a616261de858b3c10d03419af9"
integrity sha512-B5x9j0OgjG+v1dF2DkH34lr+7Gmv0kzX6/V0afF41FkPMMqaQ77pH7CrhWeR22aEeHKaeZVtZ6yFwlxOKPVFyg==
"@esbuild/linux-ppc64@0.19.11":
version "0.19.11"
resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.19.11.tgz#9c5725a94e6ec15b93195e5a6afb821628afd912"
integrity sha512-MHrZYLeCG8vXblMetWyttkdVRjQlQUb/oMgBNurVEnhj4YWOr4G5lmBfZjHYQHHN0g6yDmCAQRR8MUHldvvRDA==
"@esbuild/linux-riscv64@0.19.11":
version "0.19.11"
resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.19.11.tgz#2dc4486d474a2a62bbe5870522a9a600e2acb916"
integrity sha512-f3DY++t94uVg141dozDu4CCUkYW+09rWtaWfnb3bqe4w5NqmZd6nPVBm+qbz7WaHZCoqXqHz5p6CM6qv3qnSSQ==
"@esbuild/linux-s390x@0.19.11":
version "0.19.11"
resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.19.11.tgz#4ad8567df48f7dd4c71ec5b1753b6f37561a65a8"
integrity sha512-A5xdUoyWJHMMlcSMcPGVLzYzpcY8QP1RtYzX5/bS4dvjBGVxdhuiYyFwp7z74ocV7WDc0n1harxmpq2ePOjI0Q==
"@esbuild/linux-x64@0.19.11":
version "0.19.11"
resolved "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.11.tgz"
integrity sha512-grbyMlVCvJSfxFQUndw5mCtWs5LO1gUlwP4CDi4iJBbVpZcqLVT29FxgGuBJGSzyOxotFG4LoO5X+M1350zmPA==
"@esbuild/netbsd-x64@0.19.11":
version "0.19.11"
resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.19.11.tgz#d633c09492a1721377f3bccedb2d821b911e813d"
integrity sha512-13jvrQZJc3P230OhU8xgwUnDeuC/9egsjTkXN49b3GcS5BKvJqZn86aGM8W9pd14Kd+u7HuFBMVtrNGhh6fHEQ==
"@esbuild/openbsd-x64@0.19.11":
version "0.19.11"
resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.19.11.tgz#17388c76e2f01125bf831a68c03a7ffccb65d1a2"
integrity sha512-ysyOGZuTp6SNKPE11INDUeFVVQFrhcNDVUgSQVDzqsqX38DjhPEPATpid04LCoUr2WXhQTEZ8ct/EgJCUDpyNw==
"@esbuild/sunos-x64@0.19.11":
version "0.19.11"
resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.19.11.tgz#e320636f00bb9f4fdf3a80e548cb743370d41767"
integrity sha512-Hf+Sad9nVwvtxy4DXCZQqLpgmRTQqyFyhT3bZ4F2XlJCjxGmRFF0Shwn9rzhOYRB61w9VMXUkxlBy56dk9JJiQ==
"@esbuild/win32-arm64@0.19.11":
version "0.19.11"
resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.19.11.tgz#c778b45a496e90b6fc373e2a2bb072f1441fe0ee"
integrity sha512-0P58Sbi0LctOMOQbpEOvOL44Ne0sqbS0XWHMvvrg6NE5jQ1xguCSSw9jQeUk2lfrXYsKDdOe6K+oZiwKPilYPQ==
"@esbuild/win32-ia32@0.19.11":
version "0.19.11"
resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.19.11.tgz#481a65fee2e5cce74ec44823e6b09ecedcc5194c"
integrity sha512-6YOrWS+sDJDmshdBIQU+Uoyh7pQKrdykdefC1avn76ss5c+RN6gut3LZA4E2cH5xUEp5/cA0+YxRaVtRAb0xBg==
"@esbuild/win32-x64@0.19.11":
version "0.19.11"
resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.19.11.tgz#a5d300008960bb39677c46bf16f53ec70d8dee04"
integrity sha512-vfkhltrjCAb603XaFhqhAF4LGDi2M4OrCRrFusyQ+iTLQ/o60QQXxc9cZC/FFpihBI9N1Grn6SMKVJ4KP7Fuiw==
"@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.4.0":
version "4.4.0"
resolved "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz"
@ -442,7 +559,7 @@
"@jridgewell/resolve-uri" "^3.1.0"
"@jridgewell/sourcemap-codec" "^1.4.14"
"@mui/base@5.0.0-beta.30":
"@mui/base@5.0.0-beta.30", "@mui/base@^5.0.0-beta.22":
version "5.0.0-beta.30"
resolved "https://registry.npmjs.org/@mui/base/-/base-5.0.0-beta.30.tgz"
integrity sha512-dc38W4W3K42atE9nSaOeoJ7/x9wGIfawdwC/UmMxMLlZ1iSsITQ8dQJaTATCbn98YvYPINK/EH541YA5enQIPQ==
@ -516,7 +633,7 @@
resolved "https://registry.npmjs.org/@mui/types/-/types-7.2.12.tgz"
integrity sha512-3kaHiNm9khCAo0pVe0RenketDSFoZGAlVZ4zDjB/QNZV0XiCj+sh1zkX0VVhQPgYJDlBEzAag+MHJ1tU3vf0Zw==
"@mui/utils@^5.15.3":
"@mui/utils@^5.14.16", "@mui/utils@^5.15.3":
version "5.15.3"
resolved "https://registry.npmjs.org/@mui/utils/-/utils-5.15.3.tgz"
integrity sha512-mT3LiSt9tZWCdx1pl7q4Q5tNo6gdZbvJel286ZHGuj6LQQXjWNAh8qiF9d+LogvNUI+D7eLkTnj605d1zoazfg==
@ -526,6 +643,19 @@
prop-types "^15.8.1"
react-is "^18.2.0"
"@mui/x-date-pickers@^6.18.7":
version "6.18.7"
resolved "https://registry.yarnpkg.com/@mui/x-date-pickers/-/x-date-pickers-6.18.7.tgz#6b00163c77dc450c11b44a479baf62541e6f8b36"
integrity sha512-4NoapaCT3jvEk2cuAUjG0ReZvTEk1i4dGDz94Gt1Oc08GuC1AuzYRwCR1/1tdmbDynwkR8ilkKL6AyS3NL1H4A==
dependencies:
"@babel/runtime" "^7.23.2"
"@mui/base" "^5.0.0-beta.22"
"@mui/utils" "^5.14.16"
"@types/react-transition-group" "^4.4.8"
clsx "^2.0.0"
prop-types "^15.8.1"
react-transition-group "^4.4.5"
"@nodelib/fs.scandir@2.1.5":
version "2.1.5"
resolved "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz"
@ -534,7 +664,7 @@
"@nodelib/fs.stat" "2.0.5"
run-parallel "^1.1.9"
"@nodelib/fs.stat@^2.0.2", "@nodelib/fs.stat@2.0.5":
"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2":
version "2.0.5"
resolved "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz"
integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==
@ -557,6 +687,46 @@
resolved "https://registry.npmjs.org/@remix-run/router/-/router-1.14.1.tgz"
integrity sha512-Qg4DMQsfPNAs88rb2xkdk03N3bjK4jgX5fR24eHCTR9q6PrhZQZ4UJBPzCHJkIpTRN1UKxx2DzjZmnC+7Lj0Ow==
"@rollup/rollup-android-arm-eabi@4.9.4":
version "4.9.4"
resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.9.4.tgz#b1094962742c1a0349587040bc06185e2a667c9b"
integrity sha512-ub/SN3yWqIv5CWiAZPHVS1DloyZsJbtXmX4HxUTIpS0BHm9pW5iYBo2mIZi+hE3AeiTzHz33blwSnhdUo+9NpA==
"@rollup/rollup-android-arm64@4.9.4":
version "4.9.4"
resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.9.4.tgz#96eb86fb549e05b187f2ad06f51d191a23cb385a"
integrity sha512-ehcBrOR5XTl0W0t2WxfTyHCR/3Cq2jfb+I4W+Ch8Y9b5G+vbAecVv0Fx/J1QKktOrgUYsIKxWAKgIpvw56IFNA==
"@rollup/rollup-darwin-arm64@4.9.4":
version "4.9.4"
resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.9.4.tgz#2456630c007cc5905cb368acb9ff9fc04b2d37be"
integrity sha512-1fzh1lWExwSTWy8vJPnNbNM02WZDS8AW3McEOb7wW+nPChLKf3WG2aG7fhaUmfX5FKw9zhsF5+MBwArGyNM7NA==
"@rollup/rollup-darwin-x64@4.9.4":
version "4.9.4"
resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.9.4.tgz#97742214fc7dfd47a0f74efba6f5ae264e29c70c"
integrity sha512-Gc6cukkF38RcYQ6uPdiXi70JB0f29CwcQ7+r4QpfNpQFVHXRd0DfWFidoGxjSx1DwOETM97JPz1RXL5ISSB0pA==
"@rollup/rollup-linux-arm-gnueabihf@4.9.4":
version "4.9.4"
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.9.4.tgz#cd933e61d6f689c9cdefde424beafbd92cfe58e2"
integrity sha512-g21RTeFzoTl8GxosHbnQZ0/JkuFIB13C3T7Y0HtKzOXmoHhewLbVTFBQZu+z5m9STH6FZ7L/oPgU4Nm5ErN2fw==
"@rollup/rollup-linux-arm64-gnu@4.9.4":
version "4.9.4"
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.9.4.tgz#33b09bf462f1837afc1e02a1b352af6b510c78a6"
integrity sha512-TVYVWD/SYwWzGGnbfTkrNpdE4HON46orgMNHCivlXmlsSGQOx/OHHYiQcMIOx38/GWgwr/po2LBn7wypkWw/Mg==
"@rollup/rollup-linux-arm64-musl@4.9.4":
version "4.9.4"
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.9.4.tgz#50257fb248832c2308064e3764a16273b6ee4615"
integrity sha512-XcKvuendwizYYhFxpvQ3xVpzje2HHImzg33wL9zvxtj77HvPStbSGI9czrdbfrf8DGMcNNReH9pVZv8qejAQ5A==
"@rollup/rollup-linux-riscv64-gnu@4.9.4":
version "4.9.4"
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.9.4.tgz#09589e4e1a073cf56f6249b77eb6c9a8e9b613a8"
integrity sha512-LFHS/8Q+I9YA0yVETyjonMJ3UA+DczeBd/MqNEzsGSTdNvSJa1OJZcSH8GiXLvcizgp9AlHs2walqRcqzjOi3A==
"@rollup/rollup-linux-x64-gnu@4.9.4":
version "4.9.4"
resolved "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.9.4.tgz"
@ -567,6 +737,21 @@
resolved "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.9.4.tgz"
integrity sha512-RoaYxjdHQ5TPjaPrLsfKqR3pakMr3JGqZ+jZM0zP2IkDtsGa4CqYaWSfQmZVgFUCgLrTnzX+cnHS3nfl+kB6ZQ==
"@rollup/rollup-win32-arm64-msvc@4.9.4":
version "4.9.4"
resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.9.4.tgz#95957067eb107f571da1d81939f017d37b4958d3"
integrity sha512-T8Q3XHV+Jjf5e49B4EAaLKV74BbX7/qYBRQ8Wop/+TyyU0k+vSjiLVSHNWdVd1goMjZcbhDmYZUYW5RFqkBNHQ==
"@rollup/rollup-win32-ia32-msvc@4.9.4":
version "4.9.4"
resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.9.4.tgz#71b6facad976db527863f698692c6964c0b6e10e"
integrity sha512-z+JQ7JirDUHAsMecVydnBPWLwJjbppU+7LZjffGf+Jvrxq+dVjIE7By163Sc9DKc3ADSU50qPVw0KonBS+a+HQ==
"@rollup/rollup-win32-x64-msvc@4.9.4":
version "4.9.4"
resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.9.4.tgz#16295ccae354707c9bc6842906bdeaad4f3ba7a5"
integrity sha512-LfdGXCV9rdEify1oxlN9eamvDSjv9md9ZVMAbNHA87xqIfFCxImxan9qZ8+Un54iK2nnqPlbnSi4R54ONtbWBw==
"@types/babel__core@^7.20.5":
version "7.20.5"
resolved "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz"
@ -610,7 +795,7 @@
resolved "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz"
integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==
"@types/node@^18.0.0 || >=20.0.0", "@types/node@^20.10.7":
"@types/node@^20.10.7":
version "20.10.7"
resolved "https://registry.npmjs.org/@types/node/-/node-20.10.7.tgz"
integrity sha512-fRbIKb8C/Y2lXxB5eVMj4IU7xpdox0Lh8bUPEdtLysaylsml1hOOx1+STloRs/B9nf7C6kPRmmg/V7aQW7usNg==
@ -634,14 +819,14 @@
dependencies:
"@types/react" "*"
"@types/react-transition-group@^4.4.10":
"@types/react-transition-group@^4.4.10", "@types/react-transition-group@^4.4.8":
version "4.4.10"
resolved "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.10.tgz"
integrity sha512-hT/+s0VQs2ojCX823m60m5f0sL5idt9SO6Tj6Dg+rdphGPIeJbJ6CxvBYkgkGKrYeDjvIpKTR38UzmtHJOGW3Q==
dependencies:
"@types/react" "*"
"@types/react@*", "@types/react@^17.0.0 || ^18.0.0", "@types/react@^18.2.43":
"@types/react@*", "@types/react@^18.2.43":
version "18.2.47"
resolved "https://registry.npmjs.org/@types/react/-/react-18.2.47.tgz"
integrity sha512-xquNkkOirwyCgoClNk85BjP+aqnIS+ckAJ8i37gAbDs14jfW/J23f2GItAf33oiUPQnqNMALiFeoM9Y5mbjpVQ==
@ -677,7 +862,7 @@
semver "^7.5.4"
ts-api-utils "^1.0.1"
"@typescript-eslint/parser@^6.0.0 || ^6.0.0-alpha", "@typescript-eslint/parser@^6.14.0":
"@typescript-eslint/parser@^6.14.0":
version "6.18.0"
resolved "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.18.0.tgz"
integrity sha512-v6uR68SFvqhNQT41frCMCQpsP+5vySy6IdgjlzUWoo7ALCnpaWYcz/Ij2k4L8cEsL0wkvOviCMpjmtRtHNOKzA==
@ -767,7 +952,7 @@ acorn-jsx@^5.3.2:
resolved "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz"
integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==
"acorn@^6.0.0 || ^7.0.0 || ^8.0.0", acorn@^8.9.0:
acorn@^8.9.0:
version "8.11.3"
resolved "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz"
integrity sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==
@ -847,7 +1032,7 @@ braces@^3.0.2:
dependencies:
fill-range "^7.0.1"
browserslist@^4.22.2, "browserslist@>= 4.21.0":
browserslist@^4.22.2:
version "4.22.2"
resolved "https://registry.npmjs.org/browserslist/-/browserslist-4.22.2.tgz"
integrity sha512-0UgcrvQmBDvZHFGdYUehrCNIazki7/lUP3kkoi/r3YB2amZbFM9J43ZRkJTXBUZK4gmx56+Sqk9+Vs9mwZx9+A==
@ -903,16 +1088,16 @@ color-convert@^2.0.1:
dependencies:
color-name "~1.1.4"
color-name@~1.1.4:
version "1.1.4"
resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz"
integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
color-name@1.1.3:
version "1.1.3"
resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz"
integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==
color-name@~1.1.4:
version "1.1.4"
resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz"
integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
concat-map@0.0.1:
version "0.0.1"
resolved "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz"
@ -953,6 +1138,11 @@ csstype@^3.0.2, csstype@^3.1.2:
resolved "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz"
integrity sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==
dayjs@^1.11.10:
version "1.11.10"
resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.10.tgz#68acea85317a6e164457d6d6947564029a6a16a0"
integrity sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ==
debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4:
version "4.3.4"
resolved "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz"
@ -1066,7 +1256,7 @@ eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4
resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz"
integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==
"eslint@^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0", "eslint@^6.0.0 || ^7.0.0 || >=8.0.0", "eslint@^7.0.0 || ^8.0.0", eslint@^8.55.0, eslint@>=7:
eslint@^8.55.0:
version "8.56.0"
resolved "https://registry.npmjs.org/eslint/-/eslint-8.56.0.tgz"
integrity sha512-Go19xM6T9puCOWntie1/P997aXxFsOi37JIHRWI514Hc6ZnaHGKY9xFhrU65RT6CcBEzZoGG1e6Nq+DT04ZtZQ==
@ -1222,6 +1412,11 @@ fs.realpath@^1.0.0:
resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz"
integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==
fsevents@~2.3.2, fsevents@~2.3.3:
version "2.3.3"
resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6"
integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==
function-bind@^1.1.2:
version "1.1.2"
resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz"
@ -1489,13 +1684,6 @@ micromatch@^4.0.4:
braces "^3.0.2"
picomatch "^2.3.1"
minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2:
version "3.1.2"
resolved "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz"
integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==
dependencies:
brace-expansion "^1.1.7"
minimatch@9.0.3:
version "9.0.3"
resolved "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz"
@ -1503,6 +1691,13 @@ minimatch@9.0.3:
dependencies:
brace-expansion "^2.0.1"
minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2:
version "3.1.2"
resolved "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz"
integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==
dependencies:
brace-expansion "^1.1.7"
ms@2.1.2:
version "2.1.2"
resolved "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz"
@ -1646,7 +1841,7 @@ queue-microtask@^1.2.2:
resolved "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz"
integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==
"react-dom@^17.0.0 || ^18.0.0", react-dom@^18.2.0, react-dom@>=16.6.0, react-dom@>=16.8, react-dom@>=16.8.0:
react-dom@^18.2.0:
version "18.2.0"
resolved "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz"
integrity sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==
@ -1654,6 +1849,11 @@ queue-microtask@^1.2.2:
loose-envify "^1.1.0"
scheduler "^0.23.0"
react-github-corner@^2.5.0:
version "2.5.0"
resolved "https://registry.yarnpkg.com/react-github-corner/-/react-github-corner-2.5.0.tgz#e350d0c69f69c075bc0f1d2a6f1df6ee91da31f2"
integrity sha512-ofds9l6n61LJc6ML+jSE6W9ZSQvATcMR9evnHPXua1oDYj289HKODnVqFUB/g2a4ieBjDHw416iHP3MjqnU76Q==
react-is@^16.13.1, react-is@^16.7.0:
version "16.13.1"
resolved "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz"
@ -1694,7 +1894,7 @@ react-transition-group@^4.4.5:
loose-envify "^1.4.0"
prop-types "^15.6.2"
"react@^17.0.0 || ^18.0.0", react@^18.2.0, react@>=16.6.0, react@>=16.8, react@>=16.8.0:
react@^18.2.0:
version "18.2.0"
resolved "https://registry.npmjs.org/react/-/react-18.2.0.tgz"
integrity sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==
@ -1877,7 +2077,7 @@ type-fest@^0.20.2:
resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz"
integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==
typescript@^5.2.2, typescript@>=4.2.0:
typescript@^5.2.2:
version "5.3.3"
resolved "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz"
integrity sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==
@ -1902,7 +2102,7 @@ uri-js@^4.2.2:
dependencies:
punycode "^2.1.0"
"vite@^4.2.0 || ^5.0.0", vite@^5.0.8:
vite@^5.0.8:
version "5.0.11"
resolved "https://registry.npmjs.org/vite/-/vite-5.0.11.tgz"
integrity sha512-XBMnDjZcNAw/G1gEiskiM1v6yzM4GE5aMGvhWTlHAYYhxb7S3/V1s3m2LDHa8Vh6yIWYYB0iJwsEaS523c4oYA==

View file

@ -0,0 +1,11 @@
ALTER TABLE events.event
DROP COLUMN IF EXISTS from_date;
ALTER TABLE events.event
DROP COLUMN IF EXISTS to_date;
ALTER TABLE events.event
ADD COLUMN IF NOT EXISTS from_date TIMESTAMP NULL;
ALTER TABLE events.event
ADD COLUMN IF NOT EXISTS to_date TIMESTAMP NULL;

View file

@ -0,0 +1,2 @@
ALTER TABLE events.event
ADD COLUMN IF NOT EXISTS duration INTEGER NOT NULL DEFAULT (60);

View file

@ -40,8 +40,8 @@ pub(crate) async fn insert_event_and_fetch_id(
) -> Result<i64, sqlx::Error> {
sqlx::query_scalar!(
r#"
INSERT INTO events.event (snowflake_id, name, description, from_date, to_date, event_type)
VALUES ($1, $2, $3, $4, $5, $6)
INSERT INTO events.event (snowflake_id, name, description, from_date, to_date, event_type, duration)
VALUES ($1, $2, $3, $4, $5, $6, $7)
RETURNING id
"#,
event.snowflake_id,
@ -49,7 +49,8 @@ pub(crate) async fn insert_event_and_fetch_id(
event.description,
event.from_date,
event.to_date,
event.event_type.to_string()
event.event_type.to_string(),
event.duration
)
.fetch_one(&mut **txn)
.await

View file

@ -18,21 +18,23 @@ use crate::{
#[derive(Deserialize)]
pub struct CreateEventDto {
from_date: DateTime<Utc>,
to_date: DateTime<Utc>,
from_date: Option<DateTime<Utc>>,
to_date: Option<DateTime<Utc>>,
name: String,
description: Option<String>,
event_type: String,
duration: i32
}
#[derive(Serialize)]
pub struct EventDto {
snowflake_id: String,
from_date: DateTime<Utc>,
to_date: DateTime<Utc>,
from_date: Option<DateTime<Utc>>,
to_date: Option<DateTime<Utc>>,
name: String,
description: Option<String>,
event_type: String,
duration: i32
}
#[derive(Deserialize)]
@ -92,9 +94,10 @@ pub async fn create_event(
snowflake_id: uid,
name: dto.name,
description: dto.description,
from_date: dto.from_date.naive_utc(),
to_date: dto.to_date.naive_utc(),
from_date: dto.from_date.map(|d| d.naive_utc()),
to_date: dto.to_date.map(|d| d.naive_utc()),
event_type,
duration: dto.duration
},
)
.await?;
@ -103,11 +106,12 @@ pub async fn create_event(
Ok(EventDto {
snowflake_id: event.snowflake_id,
from_date: Utc.from_utc_datetime(&event.from_date),
to_date: Utc.from_utc_datetime(&event.to_date),
from_date: event.from_date.map(|d| Utc.from_utc_datetime(&d)),
to_date: event.to_date.map(|d| Utc.from_utc_datetime(&d)),
name: event.name,
description: event.description,
event_type: event.event_type.to_string(),
duration: event.duration
})
})
})
@ -132,11 +136,12 @@ pub async fn fetch_event(
Ok(EventDto {
snowflake_id: event.snowflake_id,
from_date: Utc.from_utc_datetime(&event.from_date),
to_date: Utc.from_utc_datetime(&event.to_date),
from_date: event.from_date.map(|d| Utc.from_utc_datetime(&d)),
to_date: event.to_date.map(|d| Utc.from_utc_datetime(&d)),
name: event.name,
description: event.description,
event_type: event.event_type.to_string(),
duration: event.duration
})
})
})

View file

@ -7,9 +7,10 @@ pub(crate) struct Event {
pub snowflake_id: String,
pub name: String,
pub description: Option<String>,
pub from_date: NaiveDateTime,
pub to_date: NaiveDateTime,
pub from_date: Option<NaiveDateTime>,
pub to_date: Option<NaiveDateTime>,
pub event_type: EventType,
pub duration: i32
}
#[derive(Debug)]

View file

@ -16,10 +16,15 @@ async fn main() {
let api_routes = api::routes().await.expect("Unable to create api routes");
let routes = Router::new()
.nest("/api", api_routes)
.nest_service("/", ServeDir::new("./frontend/dist"))
.fallback_service(ServeDir::new("./frontend/dist"));
let mut routes = Router::new()
.nest("/api", api_routes);
// If in release mod, serve static files
if !cfg!(debug_assertions) {
routes = routes.nest_service("/", ServeDir::new("./frontend/dist"))
.fallback_service(ServeDir::new("./frontend/dist"));
}
let addr = SocketAddr::from(([127, 0, 0, 1], 8080));