diff --git a/client/package-lock.json b/client/package-lock.json index b0682335f..7d6350f45 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -22,6 +22,7 @@ "dayjs": "1.11.13", "flag-icons": "7.3.2", "html2canvas": "^1.4.1", + "human-interval": "2.0.1", "i18next": "25.4.2", "joi": "17.13.3", "mui-color-input": "^6.0.0", @@ -50,6 +51,7 @@ "eslint-plugin-react-hooks": "^5.0.0", "eslint-plugin-react-refresh": "^0.4.6", "prettier": "^3.3.3", + "typescript": "5.9.3", "vite": "6.3.6" } }, @@ -4118,6 +4120,15 @@ "node": ">=8.0.0" } }, + "node_modules/human-interval": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/human-interval/-/human-interval-2.0.1.tgz", + "integrity": "sha512-r4Aotzf+OtKIGQCB3odUowy4GfUDTy3aTWTfLd7ZF2gBCy3XW3v/dJLRefZnOFFnjqs5B1TypvS8WarpBkYUNQ==", + "license": "MIT", + "dependencies": { + "numbered": "^1.1.0" + } + }, "node_modules/i18next": { "version": "25.4.2", "resolved": "https://registry.npmjs.org/i18next/-/i18next-25.4.2.tgz", @@ -4952,6 +4963,12 @@ "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", "license": "MIT" }, + "node_modules/numbered": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/numbered/-/numbered-1.1.0.tgz", + "integrity": "sha512-pv/ue2Odr7IfYOO0byC1KgBI10wo5YDauLhxY6/saNzAdAs0r1SotGCPzzCLNPL0xtrAwWRialLu23AAu9xO1g==", + "license": "MIT" + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -6354,6 +6371,20 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "devOptional": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, "node_modules/unbox-primitive": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", diff --git a/client/package.json b/client/package.json index 6661702f7..6d4858027 100644 --- a/client/package.json +++ b/client/package.json @@ -27,6 +27,7 @@ "dayjs": "1.11.13", "flag-icons": "7.3.2", "html2canvas": "^1.4.1", + "human-interval": "2.0.1", "i18next": "25.4.2", "joi": "17.13.3", "mui-color-input": "^6.0.0", @@ -63,6 +64,7 @@ "eslint-plugin-react-hooks": "^5.0.0", "eslint-plugin-react-refresh": "^0.4.6", "prettier": "^3.3.3", + "typescript": "5.9.3", "vite": "6.3.6" } } diff --git a/client/src/Components/v1/Inputs/Checkbox/index.css b/client/src/Components/v1/Inputs/Checkbox/index.css deleted file mode 100644 index e69de29bb..000000000 diff --git a/client/src/Components/v1/Inputs/Checkbox/index.jsx b/client/src/Components/v1/Inputs/Checkbox/index.jsx index 08af49d84..64f3cc3db 100644 --- a/client/src/Components/v1/Inputs/Checkbox/index.jsx +++ b/client/src/Components/v1/Inputs/Checkbox/index.jsx @@ -3,7 +3,6 @@ import { FormControlLabel, Checkbox as MuiCheckbox } from "@mui/material"; import { useTheme } from "@emotion/react"; import CheckboxOutline from "../../../../assets/icons/checkbox-outline.svg?react"; import CheckboxFilled from "../../../../assets/icons/checkbox-filled.svg?react"; -import "./index.css"; /** * Checkbox Component diff --git a/client/src/Components/v2/DesignElements/BasePage.tsx b/client/src/Components/v2/DesignElements/BasePage.tsx new file mode 100644 index 000000000..ad516f2c6 --- /dev/null +++ b/client/src/Components/v2/DesignElements/BasePage.tsx @@ -0,0 +1,24 @@ +import Stack from "@mui/material/Stack"; +import type { StackProps } from "@mui/material/Stack"; +import { useTheme } from "@mui/material/styles"; + +interface BasePageProps extends StackProps { + children: React.ReactNode; +} + +export const BasePage: React.FC = ({ + children, + ...props +}: { + children: React.ReactNode; +}) => { + const theme = useTheme(); + return ( + + {children} + + ); +}; diff --git a/client/src/Components/v2/DesignElements/SplitBox.tsx b/client/src/Components/v2/DesignElements/SplitBox.tsx new file mode 100644 index 000000000..f5a09d8f5 --- /dev/null +++ b/client/src/Components/v2/DesignElements/SplitBox.tsx @@ -0,0 +1,68 @@ +import { Fragment } from "react"; +import Stack from "@mui/material/Stack"; +import Box from "@mui/material/Box"; +import Typography from "@mui/material/Typography"; +import { useTheme } from "@mui/material/styles"; +import { useMediaQuery } from "@mui/material"; +export const SplitBox = ({ + left, + right, +}: { + left: React.ReactNode; + right: React.ReactNode; +}) => { + const theme = useTheme(); + const isSmall = useMediaQuery(theme.breakpoints.down("md")); + return ( + + + {left} + + + {right} + + + ); +}; + +export const ConfigBox = ({ + title, + subtitle, + rightContent, +}: { + title: string; + subtitle: string; + rightContent: React.ReactNode; +}) => { + return ( + + + {title} + + {subtitle} + + } + right={rightContent} + /> + ); +}; diff --git a/client/src/Components/v2/DesignElements/index.tsx b/client/src/Components/v2/DesignElements/index.tsx new file mode 100644 index 000000000..bc21144c0 --- /dev/null +++ b/client/src/Components/v2/DesignElements/index.tsx @@ -0,0 +1,2 @@ +export { SplitBox as HorizontalSplitBox, ConfigBox } from "./SplitBox"; +export { BasePage } from "./BasePage"; diff --git a/client/src/Components/v2/Inputs/AutoComplete.tsx b/client/src/Components/v2/Inputs/AutoComplete.tsx new file mode 100644 index 000000000..73afa365a --- /dev/null +++ b/client/src/Components/v2/Inputs/AutoComplete.tsx @@ -0,0 +1,48 @@ +import Autocomplete from "@mui/material/Autocomplete"; +import type { AutocompleteProps } from "@mui/material/Autocomplete"; +import { TextInput } from "@/Components/v2/Inputs/TextInput"; +import { CheckboxInput } from "@/Components/v2/Inputs/Checkbox"; +import ListItem from "@mui/material/ListItem"; +import { useTheme } from "@mui/material/styles"; + +type AutoCompleteInputProps = Omit< + AutocompleteProps, + "renderInput" +> & { + renderInput?: AutocompleteProps["renderInput"]; +}; + +export const AutoCompleteInput: React.FC = ({ ...props }) => { + const theme = useTheme(); + return ( + ( + + )} + getOptionKey={(option) => option._id} + renderTags={() => null} + renderOption={(props, option, { selected }) => { + const { key, ...optionProps } = props; + return ( + + + {option.name} + + ); + }} + sx={{ + "&.MuiAutocomplete-root .MuiAutocomplete-input": { + padding: `0 ${theme.spacing(5)}`, + }, + }} + /> + ); +}; diff --git a/client/src/Components/v2/Inputs/Checkbox.tsx b/client/src/Components/v2/Inputs/Checkbox.tsx new file mode 100644 index 000000000..bc2cedd03 --- /dev/null +++ b/client/src/Components/v2/Inputs/Checkbox.tsx @@ -0,0 +1,23 @@ +import Checkbox from "@mui/material/Checkbox"; +import type { CheckboxProps } from "@mui/material/Checkbox"; +import CheckboxOutline from "@/assets/icons/checkbox-outline.svg?react"; +import CheckboxFilled from "@/assets/icons/checkbox-filled.svg?react"; +import { useTheme } from "@mui/material/styles"; +type CheckboxInputProps = CheckboxProps & { + label?: string; +}; + +export const CheckboxInput: React.FC = ({ label, ...props }) => { + const theme = useTheme(); + return ( + } + checkedIcon={} + sx={{ + "&:hover": { backgroundColor: "transparent" }, + "& svg": { width: theme.spacing(8), height: theme.spacing(8) }, + }} + /> + ); +}; diff --git a/client/src/Components/v2/Inputs/RadioInput.tsx b/client/src/Components/v2/Inputs/RadioInput.tsx new file mode 100644 index 000000000..c108189f7 --- /dev/null +++ b/client/src/Components/v2/Inputs/RadioInput.tsx @@ -0,0 +1,62 @@ +import Radio from "@mui/material/Radio"; +import type { RadioProps } from "@mui/material/Radio"; +import { useTheme } from "@mui/material/styles"; +import RadioChecked from "@/assets/icons/radio-checked.svg?react"; +import FormControlLabel from "@mui/material/FormControlLabel"; +import Typography from "@mui/material/Typography"; + +interface RadioInputProps extends RadioProps {} + +export const RadioInput: React.FC = ({ ...props }) => { + const theme = useTheme(); + return ( + } + sx={{ + color: "transparent", + boxShadow: `inset 0 0 0 1px ${theme.palette.secondary.main}`, + "&:not(.Mui-checked)": { + boxShadow: `inset 0 0 0 1px ${theme.palette.primary.contrastText}70`, // Use theme text color for the outline + }, + mt: theme.spacing(0.5), + }} + /> + ); +}; + +export const RadioWithDescription: React.FC< + RadioInputProps & { label: string; description: string } +> = ({ label, description, ...props }) => { + const theme = useTheme(); + return ( + } + label={ + <> + {label} + + {description} + + + } + sx={{ + alignItems: "flex-start", + p: theme.spacing(2.5), + m: theme.spacing(-2.5), + borderRadius: theme.shape.borderRadius, + + "&:hover": { + backgroundColor: theme.palette.tertiary.main, + }, + "& .MuiButtonBase-root": { + p: 0, + mr: theme.spacing(6), + }, + }} + /> + ); +}; diff --git a/client/src/Components/v2/Inputs/Select.tsx b/client/src/Components/v2/Inputs/Select.tsx new file mode 100644 index 000000000..fb388b334 --- /dev/null +++ b/client/src/Components/v2/Inputs/Select.tsx @@ -0,0 +1,6 @@ +import Select from "@mui/material/Select"; +import type { SelectProps } from "@mui/material/Select"; + +export const SelectInput: React.FC = ({ ...props }) => { + return