Added MUI to Main Page

This commit is contained in:
Thastertyn committed 2025-06-22 22:26:36 +02:00
1 parent c4d7ad5b5d
commit 2204ffeece
16 files changed
+319 -107

No files matched your search

+2
View File
@@ -13,6 +13,8 @@
"@emotion/react": "^11.14.0", "@emotion/react": "^11.14.0",
"@emotion/styled": "^11.14.0", "@emotion/styled": "^11.14.0",
"@mui/material": "^7.1.1", "@mui/material": "^7.1.1",
"@mui/x-date-pickers": "^8.5.3",
"dayjs": "^1.11.13",
"react": "^19.1.0", "react": "^19.1.0",
"react-dom": "^19.1.0", "react-dom": "^19.1.0",
"react-hook-form": "^7.58.1", "react-hook-form": "^7.58.1",
+90
View File
@@ -17,6 +17,12 @@ importers:
'@mui/material': '@mui/material':
specifier: ^7.1.1 specifier: ^7.1.1
version: 7.1.1(@emotion/react@11.14.0(@types/react@19.1.8)(react@19.1.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.1.8)(react@19.1.0))(@types/react@19.1.8)(react@19.1.0))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) version: 7.1.1(@emotion/react@11.14.0(@types/react@19.1.8)(react@19.1.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.1.8)(react@19.1.0))(@types/react@19.1.8)(react@19.1.0))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
'@mui/x-date-pickers':
specifier: ^8.5.3
version: 8.5.3(@emotion/react@11.14.0(@types/react@19.1.8)(react@19.1.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.1.8)(react@19.1.0))(@types/react@19.1.8)(react@19.1.0))(@mui/material@7.1.1(@emotion/react@11.14.0(@types/react@19.1.8)(react@19.1.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.1.8)(react@19.1.0))(@types/react@19.1.8)(react@19.1.0))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(@mui/system@7.1.1(@emotion/react@11.14.0(@types/react@19.1.8)(react@19.1.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.1.8)(react@19.1.0))(@types/react@19.1.8)(react@19.1.0))(@types/react@19.1.8)(react@19.1.0))(@types/react@19.1.8)(dayjs@1.11.13)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
dayjs:
specifier: ^1.11.13
version: 1.11.13
react: react:
specifier: ^19.1.0 specifier: ^19.1.0
version: 19.1.0 version: 19.1.0
@@ -471,6 +477,50 @@ packages:
'@types/react': '@types/react':
optional: true optional: true
'@mui/x-date-pickers@8.5.3':
resolution: {integrity: sha512-rToJOgIfVN/mG8QPX5RSPcP7QdnNN4yk+37fBztVUnmR7mhiWdUNSHlazGpnRGRqDdr4lXRkJbTjtJLviWkhKg==}
engines: {node: '>=14.0.0'}
peerDependencies:
'@emotion/react': ^11.9.0
'@emotion/styled': ^11.8.1
'@mui/material': ^5.15.14 || ^6.0.0 || ^7.0.0
'@mui/system': ^5.15.14 || ^6.0.0 || ^7.0.0
date-fns: ^2.25.0 || ^3.2.0 || ^4.0.0
date-fns-jalali: ^2.13.0-0 || ^3.2.0-0 || ^4.0.0-0
dayjs: ^1.10.7
luxon: ^3.0.2
moment: ^2.29.4
moment-hijri: ^2.1.2 || ^3.0.0
moment-jalaali: ^0.7.4 || ^0.8.0 || ^0.9.0 || ^0.10.0
react: ^17.0.0 || ^18.0.0 || ^19.0.0
react-dom: ^17.0.0 || ^18.0.0 || ^19.0.0
peerDependenciesMeta:
'@emotion/react':
optional: true
'@emotion/styled':
optional: true
date-fns:
optional: true
date-fns-jalali:
optional: true
dayjs:
optional: true
luxon:
optional: true
moment:
optional: true
moment-hijri:
optional: true
moment-jalaali:
optional: true
'@mui/x-internals@8.5.3':
resolution: {integrity: sha512-ImCg4E3DT3XoDIZO0pNCbB7iw14N+YCFY3J1V28POwCD7P2f3HSIz4jwzM006oYxI6bqeE6LMfpdPRDW6s6dQw==}
engines: {node: '>=14.0.0'}
peerDependencies:
'@mui/system': ^5.15.14 || ^6.0.0 || ^7.0.0
react: ^17.0.0 || ^18.0.0 || ^19.0.0
'@nodelib/fs.scandir@2.1.5': '@nodelib/fs.scandir@2.1.5':
resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
engines: {node: '>= 8'} engines: {node: '>= 8'}
@@ -833,6 +883,9 @@ packages:
csstype@3.1.3: csstype@3.1.3:
resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
dayjs@1.11.13:
resolution: {integrity: sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==}
debug@4.4.1: debug@4.4.1:
resolution: {integrity: sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==} resolution: {integrity: sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==}
engines: {node: '>=6.0'} engines: {node: '>=6.0'}
@@ -1222,6 +1275,9 @@ packages:
resolution: {integrity: sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==} resolution: {integrity: sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==}
engines: {node: '>=0.10.0'} engines: {node: '>=0.10.0'}
reselect@5.1.1:
resolution: {integrity: sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==}
resolve-from@4.0.0: resolve-from@4.0.0:
resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==}
engines: {node: '>=4'} engines: {node: '>=4'}
@@ -1746,6 +1802,36 @@ snapshots:
optionalDependencies: optionalDependencies:
'@types/react': 19.1.8 '@types/react': 19.1.8
'@mui/x-date-pickers@8.5.3(@emotion/react@11.14.0(@types/react@19.1.8)(react@19.1.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.1.8)(react@19.1.0))(@types/react@19.1.8)(react@19.1.0))(@mui/material@7.1.1(@emotion/react@11.14.0(@types/react@19.1.8)(react@19.1.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.1.8)(react@19.1.0))(@types/react@19.1.8)(react@19.1.0))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(@mui/system@7.1.1(@emotion/react@11.14.0(@types/react@19.1.8)(react@19.1.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.1.8)(react@19.1.0))(@types/react@19.1.8)(react@19.1.0))(@types/react@19.1.8)(react@19.1.0))(@types/react@19.1.8)(dayjs@1.11.13)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)':
dependencies:
'@babel/runtime': 7.27.6
'@mui/material': 7.1.1(@emotion/react@11.14.0(@types/react@19.1.8)(react@19.1.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.1.8)(react@19.1.0))(@types/react@19.1.8)(react@19.1.0))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
'@mui/system': 7.1.1(@emotion/react@11.14.0(@types/react@19.1.8)(react@19.1.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.1.8)(react@19.1.0))(@types/react@19.1.8)(react@19.1.0))(@types/react@19.1.8)(react@19.1.0)
'@mui/utils': 7.1.1(@types/react@19.1.8)(react@19.1.0)
'@mui/x-internals': 8.5.3(@mui/system@7.1.1(@emotion/react@11.14.0(@types/react@19.1.8)(react@19.1.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.1.8)(react@19.1.0))(@types/react@19.1.8)(react@19.1.0))(@types/react@19.1.8)(react@19.1.0))(@types/react@19.1.8)(react@19.1.0)
'@types/react-transition-group': 4.4.12(@types/react@19.1.8)
clsx: 2.1.1
prop-types: 15.8.1
react: 19.1.0
react-dom: 19.1.0(react@19.1.0)
react-transition-group: 4.4.5(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
optionalDependencies:
'@emotion/react': 11.14.0(@types/react@19.1.8)(react@19.1.0)
'@emotion/styled': 11.14.0(@emotion/react@11.14.0(@types/react@19.1.8)(react@19.1.0))(@types/react@19.1.8)(react@19.1.0)
dayjs: 1.11.13
transitivePeerDependencies:
- '@types/react'
'@mui/x-internals@8.5.3(@mui/system@7.1.1(@emotion/react@11.14.0(@types/react@19.1.8)(react@19.1.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.1.8)(react@19.1.0))(@types/react@19.1.8)(react@19.1.0))(@types/react@19.1.8)(react@19.1.0))(@types/react@19.1.8)(react@19.1.0)':
dependencies:
'@babel/runtime': 7.27.6
'@mui/system': 7.1.1(@emotion/react@11.14.0(@types/react@19.1.8)(react@19.1.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.1.8)(react@19.1.0))(@types/react@19.1.8)(react@19.1.0))(@types/react@19.1.8)(react@19.1.0)
'@mui/utils': 7.1.1(@types/react@19.1.8)(react@19.1.0)
react: 19.1.0
reselect: 5.1.1
transitivePeerDependencies:
- '@types/react'
'@nodelib/fs.scandir@2.1.5': '@nodelib/fs.scandir@2.1.5':
dependencies: dependencies:
'@nodelib/fs.stat': 2.0.5 '@nodelib/fs.stat': 2.0.5
@@ -2073,6 +2159,8 @@ snapshots:
csstype@3.1.3: {} csstype@3.1.3: {}
dayjs@1.11.13: {}
debug@4.4.1: debug@4.4.1:
dependencies: dependencies:
ms: 2.1.3 ms: 2.1.3
@@ -2453,6 +2541,8 @@ snapshots:
react@19.1.0: {} react@19.1.0: {}
reselect@5.1.1: {}
resolve-from@4.0.0: {} resolve-from@4.0.0: {}
resolve@1.22.10: resolve@1.22.10:
+4 -38
View File
@@ -1,42 +1,8 @@
#root { #root {
max-width: 1280px; margin: 0 auto;
margin: 0 auto; text-align: center;
padding: 2rem;
text-align: center;
} }
.logo { body {
height: 6em; background-color: #f0f0f0;
padding: 1.5em;
will-change: filter;
transition: filter 300ms;
}
.logo:hover {
filter: drop-shadow(0 0 2em #646cffaa);
}
.logo.react:hover {
filter: drop-shadow(0 0 2em #61dafbaa);
}
@keyframes logo-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
@media (prefers-reduced-motion: no-preference) {
a:nth-of-type(2) .logo {
animation: logo-spin infinite 20s linear;
}
}
.card {
padding: 2em;
}
.read-the-docs {
color: #888;
} }
+20 -14
View File
@@ -1,23 +1,29 @@
import "./App.css"; import { LocalizationProvider } from "@mui/x-date-pickers";
import { AdapterDayjs } from "@mui/x-date-pickers/AdapterDayjs";
import { BrowserRouter, Route, Routes } from "react-router-dom"; import { BrowserRouter, Route, Routes } from "react-router-dom";
import MainPage from "./pages/MainPage";
import TaskPage from "./pages/TaskPage"; import "./App.css";
import NotFound from "./pages/NotFound";
import { UserContextProvider } from "./contexts/UserContextProvider";
import { TaskContextProvider } from "./contexts/TaskContextProvider"; import { TaskContextProvider } from "./contexts/TaskContextProvider";
import { UserContextProvider } from "./contexts/UserContextProvider";
import MainPage from "./pages/MainPage";
import NotFound from "./pages/NotFound";
import TaskPage from "./pages/TaskPage";
function App() { function App() {
return ( return (
<BrowserRouter> <BrowserRouter>
<UserContextProvider> <LocalizationProvider dateAdapter={AdapterDayjs}>
<TaskContextProvider> <UserContextProvider>
<Routes> <TaskContextProvider>
<Route path="/" element={<MainPage />} /> <Routes>
<Route path="/task/:taskId" element={<TaskPage />} /> <Route path="/" element={<MainPage />} />
<Route path="*" element={<NotFound />} /> <Route path="/task/:taskId" element={<TaskPage />} />
</Routes> <Route path="*" element={<NotFound />} />
</TaskContextProvider> </Routes>
</UserContextProvider> </TaskContextProvider>
</UserContextProvider>
</LocalizationProvider>
</BrowserRouter> </BrowserRouter>
); );
} }
+26 -5
View File
@@ -3,6 +3,7 @@ import type { Task } from "../utils/types";
import { UserContext } from "../contexts/userContext"; import { UserContext } from "../contexts/userContext";
import { useContext } from "react"; import { useContext } from "react";
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
import { Grid, ListItem, Typography } from "@mui/material";
type TaskComponentProps = { type TaskComponentProps = {
task: Task; task: Task;
@@ -22,11 +23,31 @@ const TaskComponent: React.FC<TaskComponentProps> = ({ task }) => {
} }
return ( return (
<div onClick={() => navigate(`/task/${task.id}`)}> <ListItem
<span>{task.text}</span> className="task-list-item"
<span>Assigned to: {assignedToUser}</span> onClick={() => navigate(`/task/${task.id}`)}
<input type="checkbox" checked={task.finished} disabled={true} /> >
</div> <Grid container spacing={2} alignItems="center" width={"100%"}>
<Grid size={4}>
<Typography variant="body1" fontWeight="bold">
{task.text}
</Typography>
</Grid>
<Grid size={4}>
<Typography variant="body2" color="text.secondary" align="left">
Assigned to: {assignedToUser}
</Typography>
</Grid>
<Grid size={3}>
<Typography variant="body1" color="text.secondary">
Due: {task.due}
</Typography>
</Grid>
<Grid size={1}>
<input type="checkbox" checked={task.finished} disabled={true} />
</Grid>
</Grid>
</ListItem>
); );
}; };
+86 -14
View File
@@ -1,21 +1,34 @@
import React from "react"; import React from "react";
import { useForm } from "react-hook-form"; import { Controller, useForm } from "react-hook-form";
import type { Task } from "../utils/types"; import type { Task } from "../utils/types";
import { useRef } from "react"; import { useRef } from "react";
import { useContext } from "react"; import { useContext } from "react";
import { UserContext } from "../contexts/userContext"; import { UserContext } from "../contexts/userContext";
import { TaskContext } from "../contexts/taskContext"; import { TaskContext } from "../contexts/taskContext";
import {
Autocomplete,
Button,
Card,
CardHeader,
TextField,
} from "@mui/material";
import { DatePicker } from "@mui/x-date-pickers/DatePicker";
import { validateDueDate } from "../utils/validation";
import type { Dayjs } from "dayjs";
type FormValues = { type FormValues = {
text: string; text: string;
assignedTo: number; assignedTo: number;
dueDate: Dayjs;
}; };
const TaskForm: React.FC = () => { const TaskForm: React.FC = () => {
const { const {
register, register,
handleSubmit, handleSubmit,
// formState: { errors: formErrors } control,
reset,
formState: { errors: formErrors },
} = useForm<FormValues>(); } = useForm<FormValues>();
const { users, loading, error } = useContext(UserContext); const { users, loading, error } = useContext(UserContext);
@@ -26,17 +39,18 @@ const TaskForm: React.FC = () => {
function addTask(data: FormValues) { function addTask(data: FormValues) {
const currentIdMaxId = Math.max(...tasks.map((task) => task.id)) + 1; const currentIdMaxId = Math.max(...tasks.map((task) => task.id)) + 1;
const newId = Math.max(currentIdMaxId, 1); const newId = Math.max(currentIdMaxId, 1);
console.log("NEW ID: ", newId);
const newTask: Task = { const newTask: Task = {
id: newId, id: newId,
assignedTo: Number(data.assignedTo), assignedTo: Number(data.assignedTo),
due: data.dueDate.format("YYYY-MM-DD"),
text: data.text, text: data.text,
createdAt: creationDate, createdAt: creationDate,
finished: false, finished: false,
}; };
setTasks((prev) => [...prev, newTask]); setTasks((prev) => [...prev, newTask]);
reset();
} }
// TODO make a skeleton // TODO make a skeleton
@@ -46,17 +60,75 @@ const TaskForm: React.FC = () => {
if (users == null) return <div>{error}</div>; if (users == null) return <div>{error}</div>;
return ( return (
<form onSubmit={handleSubmit(addTask)}> <Card>
<input {...register("text", { required: true, minLength: 2 })} /> <CardHeader title="Create new task" />
<select {...register("assignedTo", { required: true })}> <form className="task-form" onSubmit={handleSubmit(addTask)}>
{users.map((user) => ( <TextField
<option key={user.id} value={user.id}> id="outlined-basic"
{user.name} label="Task text"
</option> variant="outlined"
))} error={!!formErrors.text}
</select> helperText={formErrors.text?.message}
<input type="submit" value="Submit" /> {...register("text", {
</form> required: "Task text is required",
minLength: {
value: 2,
message: "Must be at least 2 characters",
},
})}
/>
<Controller
name="assignedTo"
control={control}
rules={{ required: "Assignee is required" }}
render={({ field }) => (
<Autocomplete
disablePortal
options={users.map((user) => ({
label: user.name,
id: user.id,
}))}
getOptionLabel={(option) => option.label}
onChange={(_, data) => field.onChange(data?.id || "")}
renderInput={(params) => (
<TextField
{...params}
label="Assign To"
error={!!formErrors.assignedTo}
helperText={formErrors.assignedTo?.message}
/>
)}
/>
)}
/>
<Controller
name="dueDate"
control={control}
rules={{
required: "Due date is required",
validate: validateDueDate,
}}
render={({ field }) => (
<DatePicker
label="Due Date"
onChange={(date) => field.onChange(date)}
slotProps={{
textField: {
fullWidth: true,
error: !!formErrors.dueDate,
helperText: formErrors.dueDate?.message,
},
}}
/>
)}
/>
<Button type="submit" variant="outlined">
Add
</Button>
</form>
</Card>
); );
}; };
+15 -6
View File
@@ -1,17 +1,26 @@
import React, { useContext } from "react"; import React, { useContext } from "react";
import TaskComponent from "./Task"; import TaskComponent from "./Task";
import { TaskContext } from "../contexts/taskContext"; import { TaskContext } from "../contexts/taskContext";
import { Card, List } from "@mui/material";
const TaskList: React.FC = () => { const TaskList: React.FC = () => {
const { tasks } = useContext(TaskContext); const { tasks } = useContext(TaskContext);
if (tasks.length === 0) return <div>No tasks yet...</div>; return (
<div className="task-list">
return tasks.map((task, index) => ( <Card>
<div key={index}> {tasks.length === 0 ? (
<TaskComponent task={task} /> <p>No tasks yet...</p>
) : (
<List>
{tasks.map((task, index) => (
<TaskComponent key={index} task={task} />
))}
</List>
)}
</Card>
</div> </div>
)); );
}; };
export default TaskList; export default TaskList;
+2
View File
@@ -8,6 +8,8 @@ export const UserContextProvider = ({
}: Readonly<{ children: React.ReactNode }>) => { }: Readonly<{ children: React.ReactNode }>) => {
const userProps = useUsers(); const userProps = useUsers();
if(userProps.users == null && !userProps.loading) return <div>Failed to load users</div>;
return ( return (
<UserContext.Provider value={userProps}> <UserContext.Provider value={userProps}>
{children} {children}
+25
View File
@@ -0,0 +1,25 @@
.task-form {
padding: 10px;
display: flex;
flex-direction: row;
gap: 10px;
width: 50vw;
max-width: 1000px;
justify-content: stretch;
}
.task-form > * {
flex: 1;
}
.task-list {
margin-top: 20px;
}
.task-list-item {
cursor: pointer;
}
.task-list-item:hover {
background-color: #f0f0f0;
}
+2 -2
View File
@@ -23,8 +23,8 @@ export function useUsers() {
const data = parseUserData(await response.json()); const data = parseUserData(await response.json());
setUsers(data); setUsers(data);
setError(null); setError(null);
} catch(error) { } catch (error) {
console.error(error) console.error(error);
setError("An error occurred during data fetching."); setError("An error occurred during data fetching.");
} }
setLoading(false); setLoading(false);
+1
View File
@@ -1,6 +1,7 @@
import React from "react"; import React from "react";
import TaskForm from "../components/TaskForm"; import TaskForm from "../components/TaskForm";
import TaskList from "../components/TaskList"; import TaskList from "../components/TaskList";
import "@/css/MainPage.css";
const MainPage: React.FC = () => { const MainPage: React.FC = () => {
return ( return (
+1 -1
View File
@@ -1,7 +1,7 @@
import React from "react"; import React from "react";
const NotFound: React.FC = () => { const NotFound: React.FC = () => {
return <div>Not found</div>; return <div>Page not found</div>;
}; };
export default NotFound; export default NotFound;
+1
View File
@@ -10,6 +10,7 @@ export type Task = {
finished: boolean; finished: boolean;
assignedTo: number; // User ID assignedTo: number; // User ID
createdAt: React.RefObject<Date>; createdAt: React.RefObject<Date>;
due: string;
text: string; text: string;
}; };
+11
View File
@@ -0,0 +1,11 @@
import type { Dayjs } from "dayjs";
export function validateDueDate(value: Dayjs): string | boolean {
const selected = new Date(value.format("YYYY-MM-DD"));
const now = new Date();
now.setHours(0, 0, 0, 0);
if (selected < now) return "Due date cannot be in the past";
return true;
}
+23 -23
View File
@@ -1,27 +1,27 @@
{ {
"compilerOptions": { "compilerOptions": {
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
"target": "ES2020", "target": "ES2020",
"useDefineForClassFields": true, "useDefineForClassFields": true,
"lib": ["ES2020", "DOM", "DOM.Iterable"], "lib": ["ES2020", "DOM", "DOM.Iterable"],
"module": "ESNext", "module": "ESNext",
"skipLibCheck": true, "skipLibCheck": true,
/* Bundler mode */ /* Bundler mode */
"moduleResolution": "bundler", "moduleResolution": "bundler",
"allowImportingTsExtensions": true, "allowImportingTsExtensions": true,
"verbatimModuleSyntax": true, "verbatimModuleSyntax": true,
"moduleDetection": "force", "moduleDetection": "force",
"noEmit": true, "noEmit": true,
"jsx": "react-jsx", "jsx": "react-jsx",
/* Linting */ /* Linting */
"strict": true, "strict": true,
"noUnusedLocals": true, "noUnusedLocals": true,
"noUnusedParameters": true, "noUnusedParameters": true,
"erasableSyntaxOnly": true, "erasableSyntaxOnly": true,
"noFallthroughCasesInSwitch": true, "noFallthroughCasesInSwitch": true,
"noUncheckedSideEffectImports": true "noUncheckedSideEffectImports": true
}, },
"include": ["src"] "include": ["src"]
} }
+10 -4
View File
@@ -1,7 +1,13 @@
import { defineConfig } from 'vite' import { defineConfig } from "vite";
import react from '@vitejs/plugin-react-swc' import react from "@vitejs/plugin-react-swc";
import path from 'path';
// https://vite.dev/config/ // https://vite.dev/config/
export default defineConfig({ export default defineConfig({
plugins: [react()], plugins: [react()],
}) resolve: {
alias: {
"@": path.resolve(__dirname, "./src"),
},
},
});