Added base timetable
This commit is contained in:
parent
3d209968ce
commit
6f3f651294
|
|
@ -11,6 +11,7 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@emotion/react": "^11.11.4",
|
"@emotion/react": "^11.11.4",
|
||||||
"@mantine/core": "^7.10.2",
|
"@mantine/core": "^7.10.2",
|
||||||
|
"@mantine/dates": "^7.10.2",
|
||||||
"@mantine/form": "^7.10.2",
|
"@mantine/form": "^7.10.2",
|
||||||
"@mantine/hooks": "^7.10.2",
|
"@mantine/hooks": "^7.10.2",
|
||||||
"@mantine/notifications": "^7.10.2",
|
"@mantine/notifications": "^7.10.2",
|
||||||
|
|
@ -19,6 +20,7 @@
|
||||||
"@tanstack/react-table": "^8.17.3",
|
"@tanstack/react-table": "^8.17.3",
|
||||||
"backend": "workspace:*",
|
"backend": "workspace:*",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
|
"dayjs": "^1.11.11",
|
||||||
"hono": "^4.4.6",
|
"hono": "^4.4.6",
|
||||||
"mantine-form-zod-resolver": "^1.1.0",
|
"mantine-form-zod-resolver": "^1.1.0",
|
||||||
"react": "^18.3.1",
|
"react": "^18.3.1",
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ import { routeTree } from "./routeTree.gen";
|
||||||
|
|
||||||
import "@mantine/core/styles.css";
|
import "@mantine/core/styles.css";
|
||||||
import "@mantine/notifications/styles.css";
|
import "@mantine/notifications/styles.css";
|
||||||
|
import "@mantine/dates/styles.css";
|
||||||
import { AuthProvider } from "./contexts/AuthContext";
|
import { AuthProvider } from "./contexts/AuthContext";
|
||||||
|
|
||||||
const queryClient = new QueryClient();
|
const queryClient = new QueryClient();
|
||||||
|
|
|
||||||
95
apps/frontend/src/components/Timetable/DayColumn.tsx
Normal file
95
apps/frontend/src/components/Timetable/DayColumn.tsx
Normal file
|
|
@ -0,0 +1,95 @@
|
||||||
|
import dayjs from "dayjs";
|
||||||
|
import isoWeek from "dayjs/plugin/isoWeek";
|
||||||
|
import customParseFormat from "dayjs/plugin/customParseFormat";
|
||||||
|
import Event from "./types/Event";
|
||||||
|
|
||||||
|
dayjs.extend(isoWeek);
|
||||||
|
dayjs.extend(customParseFormat);
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
day: dayjs.Dayjs;
|
||||||
|
startTime: dayjs.Dayjs;
|
||||||
|
endTime: dayjs.Dayjs;
|
||||||
|
events: Event[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function DayColumn({ day, startTime, endTime, events }: Props) {
|
||||||
|
const isToday = day.isSame(dayjs(), "day");
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="basis-[14.3%] flex flex-col border-r border-t border-b border-gray-100">
|
||||||
|
{/* Column Header */}
|
||||||
|
<div className="flex flex-col h-20 p-2 relative">
|
||||||
|
{isToday && (
|
||||||
|
<div className="w-full h-1 bg-primary-500 top-0 left-0 absolute" />
|
||||||
|
)}
|
||||||
|
<p className="text-2xl font-bold">{day.date()}</p>
|
||||||
|
<p>{day.format("dddd")}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Hour rows */}
|
||||||
|
{[...new Array(endTime.diff(startTime, "h"))].map((_, i) => {
|
||||||
|
const currentDateTime = day.hour(startTime.hour() + i);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
key={i}
|
||||||
|
className="border-t h-20 hover:bg-gray-100 flex pr-1.5 gap-1 relative"
|
||||||
|
>
|
||||||
|
{/* <div className="absolute top-1 right-1 text-sm text-gray-500 rounded-full size-6 bg-gray-100 flex items-center justify-center">
|
||||||
|
5
|
||||||
|
</div> */}
|
||||||
|
{events
|
||||||
|
.filter((event) => {
|
||||||
|
// return event.start.isSame(
|
||||||
|
// startTime.add(i, "h"),
|
||||||
|
// "hour"
|
||||||
|
// );
|
||||||
|
|
||||||
|
return (
|
||||||
|
currentDateTime.isSame(
|
||||||
|
event.start,
|
||||||
|
"hour"
|
||||||
|
) ||
|
||||||
|
(currentDateTime.isAfter(event.start) &&
|
||||||
|
currentDateTime.isBefore(event.end))
|
||||||
|
);
|
||||||
|
})
|
||||||
|
.map((event, i) =>
|
||||||
|
currentDateTime.isSame(event.start, "hour") ? (
|
||||||
|
<div
|
||||||
|
key={i}
|
||||||
|
className="bg-primary-100 rounded-sm text-sm text-left pl-1 text-primary-800 font-medium w-full z-10 relative"
|
||||||
|
style={{
|
||||||
|
minHeight: "min-content",
|
||||||
|
|
||||||
|
// The height is calculated from the duration of the event in minutes, converted to a percentage of an hour,
|
||||||
|
// plus an additional number of pixels equivalent to the number of hours in the event duration
|
||||||
|
height: `calc(${
|
||||||
|
(event.end.diff(
|
||||||
|
event.start,
|
||||||
|
"minute"
|
||||||
|
) *
|
||||||
|
100) /
|
||||||
|
60
|
||||||
|
}% + ${event.end.diff(event.start, "hour")}px)`,
|
||||||
|
|
||||||
|
// The top position is calculated from the start minute of the event, converted to a percentage of an hour
|
||||||
|
top: `${(event.start.minute() * 100) / 60}%`,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{event.title}
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="w-full"></div>
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
{/* {Math.random() < 0.1 && (
|
||||||
|
<div className="bg-purple-200/80 rounded-md w-full h-full"></div>
|
||||||
|
)} */}
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
43
apps/frontend/src/components/Timetable/HourColumn.tsx
Normal file
43
apps/frontend/src/components/Timetable/HourColumn.tsx
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
import dayjs from "dayjs";
|
||||||
|
import isoWeek from "dayjs/plugin/isoWeek";
|
||||||
|
import customParseFormat from "dayjs/plugin/customParseFormat";
|
||||||
|
import { useMemo } from "react";
|
||||||
|
|
||||||
|
dayjs.extend(isoWeek);
|
||||||
|
dayjs.extend(customParseFormat);
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
startTime: dayjs.Dayjs;
|
||||||
|
endTime: dayjs.Dayjs;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function HourColumn({ startTime, endTime }: Props) {
|
||||||
|
const hoursArray = useMemo(() => {
|
||||||
|
const arr: dayjs.Dayjs[] = [];
|
||||||
|
let currentTime = startTime;
|
||||||
|
|
||||||
|
while (currentTime.isBefore(endTime)) {
|
||||||
|
arr.push(currentTime);
|
||||||
|
currentTime = currentTime.add(1, "hour");
|
||||||
|
}
|
||||||
|
|
||||||
|
return arr;
|
||||||
|
}, [startTime, endTime]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col border w-12">
|
||||||
|
{/* Column Header */}
|
||||||
|
<div className="flex flex-col h-20"></div>
|
||||||
|
|
||||||
|
{/* Hour Rows */}
|
||||||
|
{hoursArray.map((h) => (
|
||||||
|
<div
|
||||||
|
key={h.format("HH:mm")}
|
||||||
|
className="border-t h-20 flex items-center justify-center font-medium text-xs text-gray-500"
|
||||||
|
>
|
||||||
|
{h.format("hA").toLowerCase()}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
98
apps/frontend/src/components/Timetable/Timetable.tsx
Normal file
98
apps/frontend/src/components/Timetable/Timetable.tsx
Normal file
|
|
@ -0,0 +1,98 @@
|
||||||
|
import dayjs from "dayjs";
|
||||||
|
import { useMemo, useState } from "react";
|
||||||
|
|
||||||
|
import isoWeek from "dayjs/plugin/isoWeek";
|
||||||
|
import customParseFormat from "dayjs/plugin/customParseFormat";
|
||||||
|
import HourColumn from "./HourColumn";
|
||||||
|
import DayColumn from "./DayColumn";
|
||||||
|
import { TbChevronLeft, TbChevronRight } from "react-icons/tb";
|
||||||
|
import Event from "./types/Event";
|
||||||
|
import WeekPicker from "./WeekPicker";
|
||||||
|
|
||||||
|
dayjs.extend(isoWeek);
|
||||||
|
dayjs.extend(customParseFormat);
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
events: Event[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function Timetable({ events }: Props) {
|
||||||
|
const [currentDate, setCurrentDate] = useState(dayjs());
|
||||||
|
|
||||||
|
const startTime = dayjs("08:00", "HH:mm");
|
||||||
|
const endTime = dayjs("18:00", "HH:mm");
|
||||||
|
|
||||||
|
const weekDays = useMemo(() => {
|
||||||
|
const startOfWeek = currentDate.startOf("isoWeek");
|
||||||
|
|
||||||
|
return [...new Array(7)].map((_, i) => startOfWeek.add(i, "day"));
|
||||||
|
}, [currentDate]);
|
||||||
|
|
||||||
|
const eventPerDay = useMemo(() => {
|
||||||
|
const startOfWeek = currentDate.startOf("isoWeek");
|
||||||
|
|
||||||
|
return [...new Array(7)].map((_, i) => {
|
||||||
|
const currentDateIteration = startOfWeek.add(i, "day");
|
||||||
|
|
||||||
|
return events.filter((event) => {
|
||||||
|
return (
|
||||||
|
event.start.isSame(currentDateIteration, "day") ||
|
||||||
|
event.end.isSame(currentDateIteration, "day")
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}, [currentDate, events]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="w-full h-full flex flex-col gap-4">
|
||||||
|
{/* Header */}
|
||||||
|
<div className="flex justify-between items-center">
|
||||||
|
{/* Left */}
|
||||||
|
<div className="flex gap-8">
|
||||||
|
<button
|
||||||
|
className="flex items-center border border-gray-900 font-medium px-2 py-1 rounded-md"
|
||||||
|
onClick={() => setCurrentDate(dayjs())}
|
||||||
|
>
|
||||||
|
Today
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<div className="flex gap-2">
|
||||||
|
<button
|
||||||
|
onClick={() =>
|
||||||
|
setCurrentDate(currentDate.subtract(1, "week"))
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<TbChevronLeft />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() =>
|
||||||
|
setCurrentDate(currentDate.add(1, "week"))
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<TbChevronRight />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<WeekPicker
|
||||||
|
currentDate={currentDate}
|
||||||
|
onChange={(date) => setCurrentDate(dayjs(date))}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/* The Table */}
|
||||||
|
<div className="flex">
|
||||||
|
{/* Columns */}
|
||||||
|
<HourColumn startTime={startTime} endTime={endTime} />
|
||||||
|
{weekDays.map((day, i) => (
|
||||||
|
<DayColumn
|
||||||
|
key={day.format()}
|
||||||
|
day={day}
|
||||||
|
events={eventPerDay[i]}
|
||||||
|
startTime={startTime}
|
||||||
|
endTime={endTime}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
73
apps/frontend/src/components/Timetable/WeekPicker.tsx
Normal file
73
apps/frontend/src/components/Timetable/WeekPicker.tsx
Normal file
|
|
@ -0,0 +1,73 @@
|
||||||
|
import { Popover } from "@mantine/core";
|
||||||
|
import { DatePicker } from "@mantine/dates";
|
||||||
|
import dayjs from "dayjs";
|
||||||
|
import { useState } from "react";
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
currentDate: dayjs.Dayjs;
|
||||||
|
onChange: (date: Date) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getDay(date: Date) {
|
||||||
|
const day = date.getDay();
|
||||||
|
return day === 0 ? 6 : day - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
function startOfWeek(date: Date) {
|
||||||
|
return new Date(
|
||||||
|
date.getFullYear(),
|
||||||
|
date.getMonth(),
|
||||||
|
date.getDate() - getDay(date) - 1
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function endOfWeek(date: Date) {
|
||||||
|
return dayjs(
|
||||||
|
new Date(
|
||||||
|
date.getFullYear(),
|
||||||
|
date.getMonth(),
|
||||||
|
date.getDate() + (6 - getDay(date))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.endOf("date")
|
||||||
|
.toDate();
|
||||||
|
}
|
||||||
|
|
||||||
|
function isInWeekRange(date: Date, value: Date | null) {
|
||||||
|
return value
|
||||||
|
? dayjs(date).isBefore(endOfWeek(value)) &&
|
||||||
|
dayjs(date).isAfter(startOfWeek(value))
|
||||||
|
: false;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function WeekPicker({ currentDate, onChange }: Props) {
|
||||||
|
const [hovered, setHovered] = useState<Date | null>(null);
|
||||||
|
const [value, setValue] = useState<Date | null>(null);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Popover>
|
||||||
|
<Popover.Target>
|
||||||
|
<button>{currentDate.format("MMMM YYYY")}</button>
|
||||||
|
</Popover.Target>
|
||||||
|
<Popover.Dropdown>
|
||||||
|
<DatePicker
|
||||||
|
getDayProps={(date) => {
|
||||||
|
const isHovered = isInWeekRange(date, hovered);
|
||||||
|
const isSelected = isInWeekRange(date, value);
|
||||||
|
const isInRange = isHovered || isSelected;
|
||||||
|
return {
|
||||||
|
onMouseEnter: () => setHovered(date),
|
||||||
|
onMouseLeave: () => setHovered(null),
|
||||||
|
inRange: isInRange,
|
||||||
|
firstInRange: isInRange && date.getDay() === 1,
|
||||||
|
lastInRange: isInRange && date.getDay() === 0,
|
||||||
|
selected: isSelected,
|
||||||
|
onClick: () => setValue(date),
|
||||||
|
};
|
||||||
|
}}
|
||||||
|
onChange={(date) => onChange(date ?? new Date())}
|
||||||
|
/>
|
||||||
|
</Popover.Dropdown>
|
||||||
|
</Popover>
|
||||||
|
);
|
||||||
|
}
|
||||||
3
apps/frontend/src/components/Timetable/index.ts
Normal file
3
apps/frontend/src/components/Timetable/index.ts
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
import Timetable from "./Timetable";
|
||||||
|
|
||||||
|
export default Timetable;
|
||||||
7
apps/frontend/src/components/Timetable/types/Event.d.ts
vendored
Normal file
7
apps/frontend/src/components/Timetable/types/Event.d.ts
vendored
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
type Event = {
|
||||||
|
title: string;
|
||||||
|
start: dayjs.Dayjs;
|
||||||
|
end: dayjs.Dayjs;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Event;
|
||||||
|
|
@ -3,6 +3,7 @@ import ReactDOM from "react-dom/client";
|
||||||
import App from "./App.tsx";
|
import App from "./App.tsx";
|
||||||
import "./index.css";
|
import "./index.css";
|
||||||
import "./styles/tailwind.css";
|
import "./styles/tailwind.css";
|
||||||
|
import "./styles/fonts/manrope.css";
|
||||||
|
|
||||||
ReactDOM.createRoot(document.getElementById("root")!).render(
|
ReactDOM.createRoot(document.getElementById("root")!).render(
|
||||||
<React.StrictMode>
|
<React.StrictMode>
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ import { createFileRoute } from '@tanstack/react-router'
|
||||||
import { Route as rootRoute } from './routes/__root'
|
import { Route as rootRoute } from './routes/__root'
|
||||||
import { Route as DashboardLayoutImport } from './routes/_dashboardLayout'
|
import { Route as DashboardLayoutImport } from './routes/_dashboardLayout'
|
||||||
import { Route as DashboardLayoutUsersIndexImport } from './routes/_dashboardLayout/users/index'
|
import { Route as DashboardLayoutUsersIndexImport } from './routes/_dashboardLayout/users/index'
|
||||||
|
import { Route as DashboardLayoutTimetableIndexImport } from './routes/_dashboardLayout/timetable/index'
|
||||||
import { Route as DashboardLayoutDashboardIndexImport } from './routes/_dashboardLayout/dashboard/index'
|
import { Route as DashboardLayoutDashboardIndexImport } from './routes/_dashboardLayout/dashboard/index'
|
||||||
|
|
||||||
// Create Virtual Routes
|
// Create Virtual Routes
|
||||||
|
|
@ -52,6 +53,12 @@ const DashboardLayoutUsersIndexRoute = DashboardLayoutUsersIndexImport.update({
|
||||||
import('./routes/_dashboardLayout/users/index.lazy').then((d) => d.Route),
|
import('./routes/_dashboardLayout/users/index.lazy').then((d) => d.Route),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const DashboardLayoutTimetableIndexRoute =
|
||||||
|
DashboardLayoutTimetableIndexImport.update({
|
||||||
|
path: '/timetable/',
|
||||||
|
getParentRoute: () => DashboardLayoutRoute,
|
||||||
|
} as any)
|
||||||
|
|
||||||
const DashboardLayoutDashboardIndexRoute =
|
const DashboardLayoutDashboardIndexRoute =
|
||||||
DashboardLayoutDashboardIndexImport.update({
|
DashboardLayoutDashboardIndexImport.update({
|
||||||
path: '/dashboard/',
|
path: '/dashboard/',
|
||||||
|
|
@ -97,6 +104,13 @@ declare module '@tanstack/react-router' {
|
||||||
preLoaderRoute: typeof DashboardLayoutDashboardIndexImport
|
preLoaderRoute: typeof DashboardLayoutDashboardIndexImport
|
||||||
parentRoute: typeof DashboardLayoutImport
|
parentRoute: typeof DashboardLayoutImport
|
||||||
}
|
}
|
||||||
|
'/_dashboardLayout/timetable/': {
|
||||||
|
id: '/_dashboardLayout/timetable/'
|
||||||
|
path: '/timetable'
|
||||||
|
fullPath: '/timetable'
|
||||||
|
preLoaderRoute: typeof DashboardLayoutTimetableIndexImport
|
||||||
|
parentRoute: typeof DashboardLayoutImport
|
||||||
|
}
|
||||||
'/_dashboardLayout/users/': {
|
'/_dashboardLayout/users/': {
|
||||||
id: '/_dashboardLayout/users/'
|
id: '/_dashboardLayout/users/'
|
||||||
path: '/users'
|
path: '/users'
|
||||||
|
|
@ -113,6 +127,7 @@ export const routeTree = rootRoute.addChildren({
|
||||||
IndexLazyRoute,
|
IndexLazyRoute,
|
||||||
DashboardLayoutRoute: DashboardLayoutRoute.addChildren({
|
DashboardLayoutRoute: DashboardLayoutRoute.addChildren({
|
||||||
DashboardLayoutDashboardIndexRoute,
|
DashboardLayoutDashboardIndexRoute,
|
||||||
|
DashboardLayoutTimetableIndexRoute,
|
||||||
DashboardLayoutUsersIndexRoute,
|
DashboardLayoutUsersIndexRoute,
|
||||||
}),
|
}),
|
||||||
LoginIndexLazyRoute,
|
LoginIndexLazyRoute,
|
||||||
|
|
@ -140,6 +155,7 @@ export const routeTree = rootRoute.addChildren({
|
||||||
"filePath": "_dashboardLayout.tsx",
|
"filePath": "_dashboardLayout.tsx",
|
||||||
"children": [
|
"children": [
|
||||||
"/_dashboardLayout/dashboard/",
|
"/_dashboardLayout/dashboard/",
|
||||||
|
"/_dashboardLayout/timetable/",
|
||||||
"/_dashboardLayout/users/"
|
"/_dashboardLayout/users/"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
@ -153,6 +169,10 @@ export const routeTree = rootRoute.addChildren({
|
||||||
"filePath": "_dashboardLayout/dashboard/index.tsx",
|
"filePath": "_dashboardLayout/dashboard/index.tsx",
|
||||||
"parent": "/_dashboardLayout"
|
"parent": "/_dashboardLayout"
|
||||||
},
|
},
|
||||||
|
"/_dashboardLayout/timetable/": {
|
||||||
|
"filePath": "_dashboardLayout/timetable/index.tsx",
|
||||||
|
"parent": "/_dashboardLayout"
|
||||||
|
},
|
||||||
"/_dashboardLayout/users/": {
|
"/_dashboardLayout/users/": {
|
||||||
"filePath": "_dashboardLayout/users/index.tsx",
|
"filePath": "_dashboardLayout/users/index.tsx",
|
||||||
"parent": "/_dashboardLayout"
|
"parent": "/_dashboardLayout"
|
||||||
|
|
|
||||||
|
|
@ -8,9 +8,9 @@ interface RouteContext {
|
||||||
|
|
||||||
export const Route = createRootRouteWithContext<RouteContext>()({
|
export const Route = createRootRouteWithContext<RouteContext>()({
|
||||||
component: () => (
|
component: () => (
|
||||||
<>
|
<div className="font-manrope">
|
||||||
<Outlet />
|
<Outlet />
|
||||||
<TanStackRouterDevtools />
|
<TanStackRouterDevtools />
|
||||||
</>
|
</div>
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
import Timetable from "@/components/Timetable";
|
||||||
|
import { Card } from "@mantine/core";
|
||||||
|
import { createFileRoute } from "@tanstack/react-router";
|
||||||
|
import dayjs from "dayjs";
|
||||||
|
|
||||||
|
export const Route = createFileRoute("/_dashboardLayout/timetable/")({
|
||||||
|
component: TimetablePage,
|
||||||
|
});
|
||||||
|
|
||||||
|
const events = [
|
||||||
|
{
|
||||||
|
title: "Hehe 1",
|
||||||
|
start: dayjs("24 June 2024 09:00", "DD MMMM YYYY HH:mm"),
|
||||||
|
end: dayjs("24 June 2024 11:45", "DD MMMM YYYY HH:mm"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Hehe 2",
|
||||||
|
start: dayjs("24 June 2024 09:30", "DD MMMM YYYY HH:mm"),
|
||||||
|
end: dayjs("24 June 2024 10:00", "DD MMMM YYYY HH:mm"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Hehe 3",
|
||||||
|
start: dayjs("24 June 2024 10:30", "DD MMMM YYYY HH:mm"),
|
||||||
|
end: dayjs("24 June 2024 11:00", "DD MMMM YYYY HH:mm"),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export default function TimetablePage() {
|
||||||
|
console.log(events);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card>
|
||||||
|
<Timetable events={events} />
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { createFileRoute, useNavigate } from "@tanstack/react-router";
|
import { createLazyFileRoute, useNavigate } from "@tanstack/react-router";
|
||||||
import { useMutation } from "@tanstack/react-query";
|
import { useMutation } from "@tanstack/react-query";
|
||||||
import {
|
import {
|
||||||
Paper,
|
Paper,
|
||||||
|
|
@ -17,7 +17,7 @@ import { zodResolver } from "mantine-form-zod-resolver";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import useAuth from "@/hooks/useAuth";
|
import useAuth from "@/hooks/useAuth";
|
||||||
|
|
||||||
export const Route = createFileRoute("/login/")({
|
export const Route = createLazyFileRoute("/login/")({
|
||||||
component: LoginPage,
|
component: LoginPage,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
import useAuth from "@/hooks/useAuth";
|
import useAuth from "@/hooks/useAuth";
|
||||||
import { useQueryClient } from "@tanstack/react-query";
|
import { useQueryClient } from "@tanstack/react-query";
|
||||||
import { createFileRoute, useNavigate } from "@tanstack/react-router";
|
import { createLazyFileRoute, useNavigate } from "@tanstack/react-router";
|
||||||
import { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
|
|
||||||
export const Route = createFileRoute("/logout/")({
|
export const Route = createLazyFileRoute("/logout/")({
|
||||||
component: LogoutPage,
|
component: LogoutPage,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
6
apps/frontend/src/styles/fonts/manrope.css
Normal file
6
apps/frontend/src/styles/fonts/manrope.css
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
@import url("https://fonts.googleapis.com/css2?family=Manrope:wght@200..800&display=swap");
|
||||||
|
|
||||||
|
.font-manrope {
|
||||||
|
font-family: "Manrope", sans-serif;
|
||||||
|
font-optical-sizing: auto;
|
||||||
|
}
|
||||||
6
apps/frontend/src/styles/fonts/plus-jakarta-sans.css
Normal file
6
apps/frontend/src/styles/fonts/plus-jakarta-sans.css
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
@import url("https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:ital,wght@0,200..800;1,200..800&display=swap");
|
||||||
|
|
||||||
|
.font-plus-jakarta-sans {
|
||||||
|
font-family: "Plus Jakarta Sans", sans-serif;
|
||||||
|
font-optical-sizing: auto;
|
||||||
|
}
|
||||||
|
|
@ -1,8 +1,14 @@
|
||||||
|
import colors from "tailwindcss/colors";
|
||||||
|
|
||||||
/** @type {import('tailwindcss').Config} */
|
/** @type {import('tailwindcss').Config} */
|
||||||
export default {
|
export default {
|
||||||
content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"],
|
content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"],
|
||||||
theme: {
|
theme: {
|
||||||
extend: {},
|
extend: {
|
||||||
|
colors: {
|
||||||
|
primary: colors.blue,
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
plugins: [],
|
plugins: [],
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -90,6 +90,9 @@ importers:
|
||||||
'@mantine/core':
|
'@mantine/core':
|
||||||
specifier: ^7.10.2
|
specifier: ^7.10.2
|
||||||
version: 7.10.2(@mantine/hooks@7.10.2(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
version: 7.10.2(@mantine/hooks@7.10.2(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||||
|
'@mantine/dates':
|
||||||
|
specifier: ^7.10.2
|
||||||
|
version: 7.10.2(@mantine/core@7.10.2(@mantine/hooks@7.10.2(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mantine/hooks@7.10.2(react@18.3.1))(dayjs@1.11.11)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||||
'@mantine/form':
|
'@mantine/form':
|
||||||
specifier: ^7.10.2
|
specifier: ^7.10.2
|
||||||
version: 7.10.2(react@18.3.1)
|
version: 7.10.2(react@18.3.1)
|
||||||
|
|
@ -114,6 +117,9 @@ importers:
|
||||||
clsx:
|
clsx:
|
||||||
specifier: ^2.1.1
|
specifier: ^2.1.1
|
||||||
version: 2.1.1
|
version: 2.1.1
|
||||||
|
dayjs:
|
||||||
|
specifier: ^1.11.11
|
||||||
|
version: 1.11.11
|
||||||
hono:
|
hono:
|
||||||
specifier: ^4.4.6
|
specifier: ^4.4.6
|
||||||
version: 4.4.6
|
version: 4.4.6
|
||||||
|
|
@ -1338,6 +1344,15 @@ packages:
|
||||||
react: ^18.2.0
|
react: ^18.2.0
|
||||||
react-dom: ^18.2.0
|
react-dom: ^18.2.0
|
||||||
|
|
||||||
|
'@mantine/dates@7.10.2':
|
||||||
|
resolution: {integrity: sha512-3YwrQ7UzwnKq07wS9/N10jkMHtTlOZI7TM9uRo4M2HPzw8d9w9IN21qAnVDkOCfATWzxiINcQEtICTdtDHhMFg==}
|
||||||
|
peerDependencies:
|
||||||
|
'@mantine/core': 7.10.2
|
||||||
|
'@mantine/hooks': 7.10.2
|
||||||
|
dayjs: '>=1.0.0'
|
||||||
|
react: ^18.2.0
|
||||||
|
react-dom: ^18.2.0
|
||||||
|
|
||||||
'@mantine/form@7.10.2':
|
'@mantine/form@7.10.2':
|
||||||
resolution: {integrity: sha512-OlXQ04orkwQO+AEeA4OihYtfxpaoK/LC1r2/nnUQmChG/GO1X9MoEW8oTQYKyYDIpQc8+lHhos4gl9dEF5YAWw==}
|
resolution: {integrity: sha512-OlXQ04orkwQO+AEeA4OihYtfxpaoK/LC1r2/nnUQmChG/GO1X9MoEW8oTQYKyYDIpQc8+lHhos4gl9dEF5YAWw==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
|
|
@ -2467,6 +2482,9 @@ packages:
|
||||||
resolution: {integrity: sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==}
|
resolution: {integrity: sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==}
|
||||||
engines: {node: '>=0.11'}
|
engines: {node: '>=0.11'}
|
||||||
|
|
||||||
|
dayjs@1.11.11:
|
||||||
|
resolution: {integrity: sha512-okzr3f11N6WuqYtZSvm+F776mB41wRZMhKP+hc34YdW+KmtYYK9iqvHSwo2k9FEH3fhGXvOPV6yz2IcSrfRUDg==}
|
||||||
|
|
||||||
debug@3.2.7:
|
debug@3.2.7:
|
||||||
resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==}
|
resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
|
|
@ -6317,6 +6335,15 @@ snapshots:
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- '@types/react'
|
- '@types/react'
|
||||||
|
|
||||||
|
'@mantine/dates@7.10.2(@mantine/core@7.10.2(@mantine/hooks@7.10.2(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mantine/hooks@7.10.2(react@18.3.1))(dayjs@1.11.11)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
|
||||||
|
dependencies:
|
||||||
|
'@mantine/core': 7.10.2(@mantine/hooks@7.10.2(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||||
|
'@mantine/hooks': 7.10.2(react@18.3.1)
|
||||||
|
clsx: 2.1.1
|
||||||
|
dayjs: 1.11.11
|
||||||
|
react: 18.3.1
|
||||||
|
react-dom: 18.3.1(react@18.3.1)
|
||||||
|
|
||||||
'@mantine/form@7.10.2(react@18.3.1)':
|
'@mantine/form@7.10.2(react@18.3.1)':
|
||||||
dependencies:
|
dependencies:
|
||||||
fast-deep-equal: 3.1.3
|
fast-deep-equal: 3.1.3
|
||||||
|
|
@ -7683,6 +7710,8 @@ snapshots:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/runtime': 7.24.7
|
'@babel/runtime': 7.24.7
|
||||||
|
|
||||||
|
dayjs@1.11.11: {}
|
||||||
|
|
||||||
debug@3.2.7:
|
debug@3.2.7:
|
||||||
dependencies:
|
dependencies:
|
||||||
ms: 2.1.3
|
ms: 2.1.3
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user