From 07c44219873b4d40fcac34eef327d9757ba0c108 Mon Sep 17 00:00:00 2001 From: nochill Date: Fri, 18 Aug 2023 07:34:30 +0700 Subject: [PATCH] add dashboard --- package.json | 4 + src/App.jsx | 24 +- src/actions/AuthAction.js | 5 + src/actions/index.js | 1 + .../Button/Pagination/DefaultButton/index.jsx | 18 ++ .../Button/Pagination/PageButton/index.jsx | 18 ++ src/components/ChartConfig/index.tsx | 240 +++++++++++++++++ src/components/DataTable/index.css | 8 + src/components/DataTable/index.jsx | 236 ++++++++++++++++ src/components/Header/index.jsx | 98 +++++++ src/components/ProgressBar/index.jsx | 39 +++ src/components/Sidebar/index.jsx | 253 ++++++++++++++++++ src/components/SidebarLinkGroup/index.jsx | 16 ++ src/components/SparkChart/index.css | 5 + src/components/SparkChart/index.tsx | 46 ++++ src/components/index.js | 17 ++ src/constants/action_type.js | 1 - src/datas/users.json | 206 ++++++++++++++ src/features/auth/authSlice.js | 4 +- src/index.css | 10 +- src/layouts/DefaultLayout/index.jsx | 40 +++ src/layouts/index.jsx | 5 + src/main.jsx | 5 +- src/pages/Dashboard/index.css | 14 + src/pages/Dashboard/index.jsx | 201 +++++++++++++- src/pages/Login/index.jsx | 21 +- src/pages/News/index.jsx | 58 ++++ src/pages/User/index.jsx | 58 ++++ src/pages/index.js | 4 + src/routes/index.jsx | 22 +- src/utils/common.js | 7 + src/utils/index.js | 6 + tailwind.config.js | 219 ++++++++++++++- yarn.lock | 196 +++++++++++++- 34 files changed, 2071 insertions(+), 34 deletions(-) create mode 100644 src/actions/AuthAction.js create mode 100644 src/actions/index.js create mode 100644 src/components/Button/Pagination/DefaultButton/index.jsx create mode 100644 src/components/Button/Pagination/PageButton/index.jsx create mode 100644 src/components/ChartConfig/index.tsx create mode 100644 src/components/DataTable/index.css create mode 100644 src/components/DataTable/index.jsx create mode 100644 src/components/Header/index.jsx create mode 100644 src/components/ProgressBar/index.jsx create mode 100644 src/components/Sidebar/index.jsx create mode 100644 src/components/SidebarLinkGroup/index.jsx create mode 100644 src/components/SparkChart/index.css create mode 100644 src/components/SparkChart/index.tsx create mode 100644 src/components/index.js create mode 100644 src/layouts/DefaultLayout/index.jsx create mode 100644 src/layouts/index.jsx create mode 100644 src/pages/Dashboard/index.css create mode 100644 src/pages/News/index.jsx create mode 100644 src/pages/User/index.jsx create mode 100644 src/utils/common.js create mode 100644 src/utils/index.js diff --git a/package.json b/package.json index 659ecf9..166faa3 100644 --- a/package.json +++ b/package.json @@ -10,11 +10,15 @@ "preview": "vite preview" }, "dependencies": { + "@heroicons/react": "v1", "@reduxjs/toolkit": "^1.9.5", + "moment": "^2.29.4", "react": "^18.2.0", + "react-charts": "^3.0.0-beta.55", "react-dom": "^18.2.0", "react-redux": "^8.1.2", "react-router-dom": "^6.15.0", + "react-table": "^7.8.0", "redux-persist": "^6.0.0", "redux-thunk": "^2.4.2", "validator": "^13.11.0" diff --git a/src/App.jsx b/src/App.jsx index c26321e..6bc99da 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,18 +1,36 @@ -import { RouterProvider, createBrowserRouter } from 'react-router-dom'; +import { Route, Routes, createBrowserRouter, useNavigate } from 'react-router-dom'; +import 'regenerator-runtime/runtime'; import routes from './routes'; import { Provider } from 'react-redux'; import { PersistGate } from 'redux-persist/integration/react'; import { persistore, store } from './stores/configureStore'; +import { Login } from './pages'; +import { DefaultLayout } from './layouts'; -const router = createBrowserRouter(routes) +// const router = createBrowserRouter(routes) function App() { return ( + <> - + + } /> + }> + {routes.map(({ path, name, element}) => ( + <> + + + ))} + + + ) } diff --git a/src/actions/AuthAction.js b/src/actions/AuthAction.js new file mode 100644 index 0000000..6bdcabf --- /dev/null +++ b/src/actions/AuthAction.js @@ -0,0 +1,5 @@ +import { LOGOUT } from "../constants/action_type"; + +export const logout = () => ({ + type: LOGOUT +}); \ No newline at end of file diff --git a/src/actions/index.js b/src/actions/index.js new file mode 100644 index 0000000..a525e7a --- /dev/null +++ b/src/actions/index.js @@ -0,0 +1 @@ +export * from './AuthAction'; \ No newline at end of file diff --git a/src/components/Button/Pagination/DefaultButton/index.jsx b/src/components/Button/Pagination/DefaultButton/index.jsx new file mode 100644 index 0000000..d68d614 --- /dev/null +++ b/src/components/Button/Pagination/DefaultButton/index.jsx @@ -0,0 +1,18 @@ +import { classNames } from "../../../../utils"; + +const PaginationDefaultButton = ({ children, className, ...rest }) => { + return ( + + ); +} + +export default PaginationDefaultButton; \ No newline at end of file diff --git a/src/components/Button/Pagination/PageButton/index.jsx b/src/components/Button/Pagination/PageButton/index.jsx new file mode 100644 index 0000000..fb2a308 --- /dev/null +++ b/src/components/Button/Pagination/PageButton/index.jsx @@ -0,0 +1,18 @@ +import { classNames } from "../../../../utils"; + +const PaginationPageButton = ({ children, className, ...rest }) => { + return ( + + ); +} + +export default PaginationPageButton; \ No newline at end of file diff --git a/src/components/ChartConfig/index.tsx b/src/components/ChartConfig/index.tsx new file mode 100644 index 0000000..0a2219e --- /dev/null +++ b/src/components/ChartConfig/index.tsx @@ -0,0 +1,240 @@ +import React from "react"; + +// + +const options = { + elementType: ["line", "area", "bar"], + primaryAxisType: ["linear", "time", "log", "band"], + secondaryAxisType: ["linear", "time", "log", "band"], + primaryAxisPosition: ["top", "left", "right", "bottom"], + secondaryAxisPosition: ["top", "left", "right", "bottom"], + secondaryAxisStack: [true, false], + primaryAxisShow: [true, false], + secondaryAxisShow: [true, false], + interactionMode: ["primary", "closest"], + tooltipGroupingMode: ["single", "primary", "secondary", "series"], + tooltipAnchor: [ + "closest", + "top", + "bottom", + "left", + "right", + "center", + "gridTop", + "gridBottom", + "gridLeft", + "gridRight", + "gridCenter", + "pointer", + ], + tooltipAlign: [ + "auto", + "top", + "bottom", + "left", + "right", + "topLeft", + "topRight", + "bottomLeft", + "bottomRight", + "center", + ], + snapCursor: [true, false], +} as const; + +type DataType = "time" | "ordinal" | "linear"; +type ElementType = typeof options["elementType"][number]; +type PrimaryAxisType = typeof options["primaryAxisType"][number]; +type SecondaryAxisType = typeof options["secondaryAxisType"][number]; +type PrimaryAxisPosition = typeof options["primaryAxisPosition"][number]; +type SecondaryAxisPosition = typeof options["secondaryAxisPosition"][number]; +type TooltipAnchor = typeof options["tooltipAnchor"][number]; +type TooltipAlign = typeof options["tooltipAlign"][number]; +type InteractionMode = typeof options["interactionMode"][number]; +type TooltipGroupingMode = typeof options["tooltipGroupingMode"][number]; + +const optionKeys = Object.keys(options) as (keyof typeof options)[]; + +export default function useChartConfig({ + series, + datums = 10, + useR, + show = [], + count = 1, + + resizable = true, + canRandomize = true, + dataType = "time", + elementType = "line", + primaryAxisType = "time", + secondaryAxisType = "linear", + primaryAxisPosition = "bottom", + secondaryAxisPosition = "left", + primaryAxisStack = false, + secondaryAxisStack = true, + primaryAxisShow = true, + secondaryAxisShow = true, + tooltipAnchor = "closest", + tooltipAlign = "auto", + interactionMode = "primary", + tooltipGroupingMode = "primary", + snapCursor = true, +}: { + series: number; + datums?: number; + useR?: boolean; + show?: (keyof typeof options)[]; + count?: number; + resizable?: boolean; + canRandomize?: boolean; + dataType?: DataType; + elementType?: ElementType; + primaryAxisType?: PrimaryAxisType; + secondaryAxisType?: SecondaryAxisType; + primaryAxisPosition?: PrimaryAxisPosition; + secondaryAxisPosition?: SecondaryAxisPosition; + primaryAxisStack?: boolean; + secondaryAxisStack?: boolean; + primaryAxisShow?: boolean; + secondaryAxisShow?: boolean; + tooltipAnchor?: TooltipAnchor; + tooltipAlign?: TooltipAlign; + interactionMode?: InteractionMode; + tooltipGroupingMode?: TooltipGroupingMode; + snapCursor?: boolean; +}) { + const [state, setState] = React.useState({ + count, + resizable, + canRandomize, + dataType, + elementType, + primaryAxisType, + secondaryAxisType, + primaryAxisPosition, + secondaryAxisPosition, + primaryAxisStack, + secondaryAxisStack, + primaryAxisShow, + secondaryAxisShow, + tooltipAnchor, + tooltipAlign, + interactionMode, + tooltipGroupingMode, + snapCursor, + datums, + data: makeDataFrom(dataType, series, datums, useR), + }); + + React.useEffect(() => { + setState((old) => ({ + ...old, + data: makeDataFrom(dataType, series, datums, useR), + })); + }, [count, dataType, datums, series, useR]); + + const Options = optionKeys + .filter((option) => show.indexOf(option) > -1) + .map((option) => ( +
+ {option}:   + +
+
+ )); + + return { + ...state, + Options, + }; +} + +function makeDataFrom( + dataType: DataType, + series: number, + datums: number, + useR?: boolean +) { + return [ + ...new Array(series || Math.max(Math.round(Math.random() * 5), 1)), + ].map((d, i) => makeSeries(i, dataType, datums, useR)); +} + +function makeSeries( + i: number, + dataType: DataType, + datums: number, + useR?: boolean +) { + const start = 0; + const startDate = new Date(); + // startDate.setFullYear(2020); + startDate.setUTCHours(0); + startDate.setUTCMinutes(0); + startDate.setUTCSeconds(0); + startDate.setUTCMilliseconds(0); + // const length = 5 + Math.round(Math.random() * 15) + const length = datums; + const min = 0; + const max = 100; + const rMin = 2; + const rMax = 20; + const nullChance = 0; + return { + label: `Series ${i + 1}`, + data: [...new Array(length)].map((_, i) => { + let x; + + if (dataType === "ordinal") { + x = `Ordinal Group ${start + i}`; + } else if (dataType === "time") { + x = new Date(startDate.getTime() + 60 * 1000 * 60 * 24 * i); + } else if (dataType === "linear") { + x = + Math.random() < nullChance + ? null + : min + Math.round(Math.random() * (max - min)); + } else { + x = start + i; + } + + const distribution = 1.1; + + const y = + Math.random() < nullChance + ? null + : min + Math.round(Math.random() * (max - min)); + + const r = !useR + ? undefined + : rMax - + Math.floor( + Math.log(Math.random() * (distribution ** rMax - rMin) + rMin) / + Math.log(distribution) + ); + + return { + primary: x, + secondary: y, + radius: r, + }; + }), + }; +} diff --git a/src/components/DataTable/index.css b/src/components/DataTable/index.css new file mode 100644 index 0000000..0c945c9 --- /dev/null +++ b/src/components/DataTable/index.css @@ -0,0 +1,8 @@ +table tr:nth-child(odd) td{ + background-color: #f5f6fa; +} + +.tab-filter:hover { + border-bottom: 3px solid black; + color: #055274 !important; +} \ No newline at end of file diff --git a/src/components/DataTable/index.jsx b/src/components/DataTable/index.jsx new file mode 100644 index 0000000..cb71caa --- /dev/null +++ b/src/components/DataTable/index.jsx @@ -0,0 +1,236 @@ +import React, { useState } from "react"; +import { + useAsyncDebounce, + useTable, + useGlobalFilter, + useFilters, + usePagination +} from "react-table"; +import { ChevronDoubleLeftIcon, ChevronDoubleRightIcon, ChevronLeftIcon, ChevronRightIcon } from "@heroicons/react/solid"; + +import "./index.css"; +import PaginationDefaultButton from "../Button/Pagination/DefaultButton"; +import PaginationPageButton from "../Button/Pagination/PageButton"; + + +const DataTable = ({ columns, data, tabFilterData }) => { + + const [selectedRole, setSelectedRole] = useState('All') + + // Use the state and functions returned from useTable to build your UI + const { + getTableProps, + getTableBodyProps, + headerGroups, + rows, + + page, // Instead of using 'rows', we'll use page, + // which has only the rows for the active page + + // The rest of these things are super handy, too ;) + canPreviousPage, + canNextPage, + pageOptions, + pageCount, + gotoPage, + nextPage, + previousPage, + setPageSize, + + + prepareRow, + state, + preGlobalFilteredRows, + setGlobalFilter + } = + useTable({ + columns, + data, + }, + useGlobalFilter, + usePagination + ); + + let row_data = page; + + if (selectedRole !== 'All') { + row_data = page.filter(x => x.values.role_name === selectedRole || x.values.type === selectedRole); + } + + + // Render the UI for your table + return ( + <> + setSelectedRole(role_name)} + selectedRole={selectedRole} + /> +
+
+
+
+ + + {headerGroups.map((headerGroup) => ( + + {headerGroup.headers.map((column) => ( + + ))} + + + ))} + + + {row_data.map((row, i) => { + prepareRow(row); + return ( + + {row.cells.map((cell) => { + return ( + + ); + })} + + + ); + })} + +
{column.render("Header")}Action
+ {cell.column.id === 'rowNumber' ? i + 1 : cell.render('Cell')} + + + +
+
+
+
+
+
+
+ previousPage()} disabled={!canPreviousPage}>Previous + nextPage()} disabled={!canNextPage}>Next +
+
+
+ + Page {state.pageIndex + 1} of {pageOptions.length} + + +
+
+ +
+
+
+ + ); +} + +const GlobalFilter = ({ + globalFilter, + setGlobalFilter, + tabFilterData, + onSelectedTabFilter, + selectedRole +}) => { + const [value, setValue] = React.useState(globalFilter) + const onChange = useAsyncDebounce(value => { + setGlobalFilter(value || undefined) + }, 200) + + const tabsFilter = ['All', ...tabFilterData]; + + return ( + <> +
+
+ + + + { + setValue(e.target.value); + onChange(e.target.value) + }} + autocomplete="off" + /> + +
+
+
+ {tabsFilter.map((filter) => ( + + ))} +
+ + ) +} + +export default DataTable; \ No newline at end of file diff --git a/src/components/Header/index.jsx b/src/components/Header/index.jsx new file mode 100644 index 0000000..d5774bc --- /dev/null +++ b/src/components/Header/index.jsx @@ -0,0 +1,98 @@ +import { useDispatch } from 'react-redux'; +import { logout } from '../../actions'; + +const Header = (props) => { + + const dispatch = useDispatch(); + + const handleLogoutClick = () => { + dispatch(logout()) + navigate("/") + } + + return ( +
+
+
+ {/* */} + + {/* */} + + {/* + Logo + */} +
+ +
+

Loan market / {props.routeName}

+
+ + + + + {/*
+
    + + + + + + + + + + + +
+ + + + +
*/} +
+
+ ); +}; + +export default Header; \ No newline at end of file diff --git a/src/components/ProgressBar/index.jsx b/src/components/ProgressBar/index.jsx new file mode 100644 index 0000000..755679c --- /dev/null +++ b/src/components/ProgressBar/index.jsx @@ -0,0 +1,39 @@ +const ProgressBar = (props) => { + const { bgcolor, completed } = props; + + const containerStyles = { + height: 5, + width: '100%', + backgroundColor: "#e0e0de", + borderRadius: 50, + } + + const fillerStyles = { + height: '100%', + width: `${completed}%`, + backgroundColor: bgcolor, + borderRadius: 'inherit', + textAlign: 'right' + } + + const labelStyles = { + padding: 5, + color: 'black', + fontWeight: 'bold' + } + + return ( +
+
+

{props.label}

+

{props.completed} %

+
+
+
+
+
+
+ ); +}; + +export default ProgressBar; \ No newline at end of file diff --git a/src/components/Sidebar/index.jsx b/src/components/Sidebar/index.jsx new file mode 100644 index 0000000..d4aa230 --- /dev/null +++ b/src/components/Sidebar/index.jsx @@ -0,0 +1,253 @@ +import React, { useEffect, useRef, useState } from 'react'; +import { NavLink, useLocation } from 'react-router-dom'; +// import Logo from './vite.svg'; +import SidebarLinkGroup from '../SidebarLinkGroup'; +import { useSelector } from 'react-redux'; + +// interface SidebarProps { +// sidebarOpen: boolean; +// setSidebarOpen: (arg: boolean) => void; +// } + +const Sidebar = ({ sidebarOpen, setSidebarOpen }) => { + const location = useLocation(); + const { pathname } = location; + + const auth = useSelector(state => state.auth) + + const trigger = useRef(null); + const sidebar = useRef(null); + + const storedSidebarExpanded = localStorage.getItem('sidebar-expanded'); + const [sidebarExpanded, setSidebarExpanded] = useState( + storedSidebarExpanded === null ? false : storedSidebarExpanded === 'true' + ); + + // close on click outside + useEffect(() => { + const clickHandler = ({ target }) => { + if (!sidebar.current || !trigger.current) return; + if ( + !sidebarOpen || + sidebar.current.contains(target) || + trigger.current.contains(target) + ) + return; + setSidebarOpen(false); + }; + document.addEventListener('click', clickHandler); + return () => document.removeEventListener('click', clickHandler); + }); + + // close if the esc key is pressed + useEffect(() => { + const keyHandler = ({ keyCode }) => { + if (!sidebarOpen || keyCode !== 27) return; + setSidebarOpen(false); + }; + document.addEventListener('keydown', keyHandler); + return () => document.removeEventListener('keydown', keyHandler); + }); + + useEffect(() => { + localStorage.setItem('sidebar-expanded', sidebarExpanded.toString()); + if (sidebarExpanded) { + document.querySelector('body')?.classList.add('sidebar-expanded'); + } else { + document.querySelector('body')?.classList.remove('sidebar-expanded'); + } + }, [sidebarExpanded]); + + return ( + + ); +}; + +export default Sidebar; \ No newline at end of file diff --git a/src/components/SidebarLinkGroup/index.jsx b/src/components/SidebarLinkGroup/index.jsx new file mode 100644 index 0000000..06fa402 --- /dev/null +++ b/src/components/SidebarLinkGroup/index.jsx @@ -0,0 +1,16 @@ +import { useState } from 'react'; + +const SidebarLinkGroup = ({ + children, + activeCondition, +}) => { + const [open, setOpen] = useState(activeCondition); + + const handleClick = () => { + setOpen(!open); + }; + + return
  • {children(handleClick, open)}
  • ; +}; + +export default SidebarLinkGroup; \ No newline at end of file diff --git a/src/components/SparkChart/index.css b/src/components/SparkChart/index.css new file mode 100644 index 0000000..03b2839 --- /dev/null +++ b/src/components/SparkChart/index.css @@ -0,0 +1,5 @@ +#chart { + width: '100px' !important; + height: '100px' !important; + position: 'relative' !important; +} \ No newline at end of file diff --git a/src/components/SparkChart/index.tsx b/src/components/SparkChart/index.tsx new file mode 100644 index 0000000..0093b36 --- /dev/null +++ b/src/components/SparkChart/index.tsx @@ -0,0 +1,46 @@ +import useDemoConfig from "../ChartConfig"; +import React from "react"; +import { AxisOptions, Chart } from "react-charts"; +import './index.css' + +export default function SparkChart() { + const { data } = useDemoConfig({ + series: 1, + dataType: "time", + }); + + const primaryAxis = React.useMemo< + AxisOptions + >( + () => ({ + getValue: (datum) => datum.primary as unknown as Date, + show: false, + }), + [] + ); + + const secondaryAxes = React.useMemo< + AxisOptions[] + >( + () => [ + { + getValue: (datum) => datum.secondary, + show: false, + showDatumElements: false, + }, + ], + [] + ); + + return ( + <> + + + ); +} diff --git a/src/components/index.js b/src/components/index.js new file mode 100644 index 0000000..3e88de6 --- /dev/null +++ b/src/components/index.js @@ -0,0 +1,17 @@ +import Sidebar from "./Sidebar"; +import Header from "./Header"; +import ProgressBar from "./ProgressBar"; +import SparkChart from "./SparkChart"; +import DataTable from "./DataTable"; +import PaginationDefaultButton from "./Button/Pagination/DefaultButton"; +import PaginationPageButton from "./Button/Pagination/PageButton"; + +export { + Sidebar, + Header, + ProgressBar, + SparkChart, + DataTable, + PaginationDefaultButton, + PaginationPageButton +} \ No newline at end of file diff --git a/src/constants/action_type.js b/src/constants/action_type.js index e7b86b2..f2228ff 100644 --- a/src/constants/action_type.js +++ b/src/constants/action_type.js @@ -1,2 +1 @@ -export const LOGIN = 'LOGIN'; export const LOGOUT = 'LOGOUT'; \ No newline at end of file diff --git a/src/datas/users.json b/src/datas/users.json index 9d9ca51..80f6ec3 100644 --- a/src/datas/users.json +++ b/src/datas/users.json @@ -1,19 +1,225 @@ [ { + "name": "superadmin", "email": "superadmin@superadmin.com", "password": "12345", + "phone": "180121312", "role_id": "1", "role_name": "Super admin" }, { + "name": "author", "email": "author@author.com", "password": "12345", + "phone": "180123142", "role_id": "2", "role_name": "Author" }, { + "name": "admin", "email": "admin@admin.com", "password": "12345", + "phone": "1818230123", + "role_id": "3", + "role_name": "Admin" + }, + { + "name": "user1", + "email": "user1@example.com", + "password": "password1", + "phone": "1234567891", + "role_id": "3", + "role_name": "Admin" + }, + { + "name": "user2", + "email": "user2@example.com", + "password": "password2", + "phone": "2345678912", + "role_id": "3", + "role_name": "Admin" + }, + { + "name": "user3", + "email": "user3@example.com", + "password": "password3", + "phone": "3456789123", + "role_id": "3", + "role_name": "Admin" + }, + { + "name": "user4", + "email": "user4@example.com", + "password": "password4", + "phone": "4567891234", + "role_id": "3", + "role_name": "Admin" + }, + { + "name": "user5", + "email": "user5@example.com", + "password": "password5", + "phone": "5678912345", + "role_id": "3", + "role_name": "Admin" + }, + { + "name": "user6", + "email": "user6@example.com", + "password": "password6", + "phone": "6789123456", + "role_id": "3", + "role_name": "Admin" + }, + { + "name": "user7", + "email": "user7@example.com", + "password": "password7", + "phone": "7891234567", + "role_id": "3", + "role_name": "Admin" + }, + { + "name": "user8", + "email": "user8@example.com", + "password": "password8", + "phone": "8912345678", + "role_id": "3", + "role_name": "Admin" + }, + { + "name": "user9", + "email": "user9@example.com", + "password": "password9", + "phone": "9123456789", + "role_id": "3", + "role_name": "Admin" + }, + { + "name": "user10", + "email": "user10@example.com", + "password": "password10", + "phone": "1234567890", + "role_id": "3", + "role_name": "Admin" + }, + { + "name": "user11", + "email": "user11@example.com", + "password": "password11", + "phone": "2345678901", + "role_id": "3", + "role_name": "Admin" + }, + { + "name": "user12", + "email": "user12@example.com", + "password": "password12", + "phone": "3456789012", + "role_id": "3", + "role_name": "Admin" + }, + { + "name": "user13", + "email": "user13@example.com", + "password": "password13", + "phone": "4567890123", + "role_id": "3", + "role_name": "Admin" + }, + { + "name": "user14", + "email": "user14@example.com", + "password": "password14", + "phone": "5678901234", + "role_id": "3", + "role_name": "Admin" + }, + { + "name": "user15", + "email": "user15@example.com", + "password": "password15", + "phone": "6789012345", + "role_id": "3", + "role_name": "Admin" + }, + { + "name": "user16", + "email": "user16@example.com", + "password": "password16", + "phone": "7890123456", + "role_id": "3", + "role_name": "Admin" + }, + { + "name": "user17", + "email": "user17@example.com", + "password": "password17", + "phone": "8901234567", + "role_id": "3", + "role_name": "Admin" + }, + { + "name": "user18", + "email": "user18@example.com", + "password": "password18", + "phone": "9012345678", + "role_id": "3", + "role_name": "Admin" + }, + { + "name": "user19", + "email": "user19@example.com", + "password": "password19", + "phone": "0123456789", + "role_id": "3", + "role_name": "Admin" + }, + { + "name": "user20", + "email": "user20@example.com", + "password": "password20", + "phone": "9876543210", + "role_id": "3", + "role_name": "Admin" + }, + { + "name": "user21", + "email": "user21@example.com", + "password": "password21", + "phone": "8765432109", + "role_id": "3", + "role_name": "Admin" + }, + { + "name": "user22", + "email": "user22@example.com", + "password": "password22", + "phone": "7654321098", + "role_id": "3", + "role_name": "Admin" + }, + { + "name": "user23", + "email": "user23@example.com", + "password": "password23", + "phone": "6543210987", + "role_id": "3", + "role_name": "Admin" + }, + { + "name": "user24", + "email": "user24@example.com", + "password": "password24", + "phone": "5432109876", + "role_id": "3", + "role_name": "Admin" + }, + { + "name": "user25", + "email": "user25@example.com", + "password": "password25", + "phone": "4321098765", "role_id": "3", "role_name": "Admin" } diff --git a/src/features/auth/authSlice.js b/src/features/auth/authSlice.js index 85794f7..0896949 100644 --- a/src/features/auth/authSlice.js +++ b/src/features/auth/authSlice.js @@ -1,6 +1,6 @@ -import { createSlice, current } from "@reduxjs/toolkit"; +import { createSlice } from "@reduxjs/toolkit"; -const initialState = [] +const initialState = {} const authSlice = createSlice({ name: 'auth', diff --git a/src/index.css b/src/index.css index 750edf7..c5bae7c 100644 --- a/src/index.css +++ b/src/index.css @@ -1,3 +1,9 @@ @tailwind base; -@tailwind component; -@tailwind utilities; \ No newline at end of file +@tailwind components; +@tailwind utilities; + +@layer base { + body { + @apply bg-whiten relative z-1; + } +} \ No newline at end of file diff --git a/src/layouts/DefaultLayout/index.jsx b/src/layouts/DefaultLayout/index.jsx new file mode 100644 index 0000000..d597ca3 --- /dev/null +++ b/src/layouts/DefaultLayout/index.jsx @@ -0,0 +1,40 @@ +import { useState } from 'react'; +import { Header, Sidebar } from '../../components'; +import { Outlet, useLocation } from 'react-router-dom'; +import { getRouteName } from '../../utils'; + +const DefaultLayout = () => { + const [sidebarOpen, setSidebarOpen] = useState(false); + const routeName = getRouteName(useLocation().pathname); + + + return ( +
    + {/* */} +
    + {/* */} + + {/* */} + + {/* */} +
    + {/* */} +
    + {/* */} + + {/* */} +
    +
    + +
    +
    + {/* */} +
    + {/* */} +
    + {/* */} +
    + ); +}; + +export default DefaultLayout; \ No newline at end of file diff --git a/src/layouts/index.jsx b/src/layouts/index.jsx new file mode 100644 index 0000000..ebc5fd2 --- /dev/null +++ b/src/layouts/index.jsx @@ -0,0 +1,5 @@ +import DefaultLayout from "./DefaultLayout"; + +export { + DefaultLayout, +} \ No newline at end of file diff --git a/src/main.jsx b/src/main.jsx index 54b39dd..a6838ce 100644 --- a/src/main.jsx +++ b/src/main.jsx @@ -2,9 +2,12 @@ import React from 'react' import ReactDOM from 'react-dom/client' import App from './App.jsx' import './index.css' +import { BrowserRouter as Router} from 'react-router-dom' ReactDOM.createRoot(document.getElementById('root')).render( - + + + , ) diff --git a/src/pages/Dashboard/index.css b/src/pages/Dashboard/index.css new file mode 100644 index 0000000..28aab05 --- /dev/null +++ b/src/pages/Dashboard/index.css @@ -0,0 +1,14 @@ +@media only screen and (max-width:1268px) { + .box { + margin-bottom: 10px; /* Or required space */ + display: block; + } + .box-center { + align-self: center; + width: 210px; + } + .big-box { + display: block; + margin: 0 10px; + } +} \ No newline at end of file diff --git a/src/pages/Dashboard/index.jsx b/src/pages/Dashboard/index.jsx index 19fb638..abd2868 100644 --- a/src/pages/Dashboard/index.jsx +++ b/src/pages/Dashboard/index.jsx @@ -1,7 +1,206 @@ +import React from 'react'; +import { useDispatch, useSelector } from 'react-redux'; +import { logout } from '../../actions'; +import { useNavigate } from 'react-router-dom'; +import data from '../.././datas/dashboard.json'; +import './index.css'; +import { ProgressBar, SparkChart } from '../../components'; + const Dashboard = () => { + const dispatch = useDispatch(); + const navigate = useNavigate(); + const auth = useSelector(state => state.auth) + const handleOnClick = () => { + dispatch(logout()) + navigate("/") + } return ( <> -

    Dashboard

    +
    +
    +

    Dashboard

    +

    User quantity

    +
    + { auth.role_id != 2 && + + } +
    +
    +
    +
    +
    + +
    +

    {data.users_data.total_users}

    +

    User

    +
    +
    +
    +
    +
    + +
    +

    {data.users_data.total_users}

    +

    User

    +
    +
    +
    +
    +
    + +
    +

    {data.users_data.total_users}

    +

    User

    +
    +
    +
    +
    +
    + +
    +
    +

    Admin office

    +

    4

    +
    +
    +
    +
    + +
    +
    +

    Admin office

    +

    4

    +
    +
    +
    +
    + +
    +
    +

    Admin office

    +

    4

    +
    +
    +
    +
    +
    +
    + +
    +
    +

    Admin office

    +

    4

    +
    +
    +
    +
    + +
    +
    +

    Admin office

    +

    4

    +
    +
    +
    +
    + +
    +
    +

    Admin office

    +

    4

    +
    +
    +
    +
    +
    +
    + +
    +
    +

    Admin office

    +

    4

    +
    +
    +
    +
    + +
    +
    +

    Admin office

    +

    4

    +
    +
    +
    +
    + +
    +
    +

    Admin office

    +

    4

    +
    +
    +
    +
    +
    +

    Data & analytics

    +

    Show updates of post

    +
    + {/* container */} +
    + {/* 2 grid */} +
    + {/* inside 2 grid */} +
    + {/* header */} +
    +
    +
    +

    Belum diproses

    +

    9123

    +
    +
    + +
    +
    +
    +
    +

    Sudah diproses

    +

    9123

    +
    +
    + +
    +
    +
    +
    + + +
    +
    +
    +
    +
    +
    +
    +

    Customer

    +

    9123

    +
    +
    + +
    +
    +
    +
    +
    + + +
    +
    +
    +
    ) } diff --git a/src/pages/Login/index.jsx b/src/pages/Login/index.jsx index 35bdd47..c4447d0 100644 --- a/src/pages/Login/index.jsx +++ b/src/pages/Login/index.jsx @@ -1,9 +1,14 @@ -import { useState } from "react"; +import { useEffect, useState } from "react"; import user from "../../datas/users.json"; import { useDispatch, useSelector } from "react-redux"; import { authAdded } from "../../features/auth/authSlice"; +import { useNavigate } from "react-router-dom"; const Login = () => { + const dispatch = useDispatch(); + const auth = useSelector(state => state.auth); + const navigate = useNavigate(); + const [authForm, setAuthForm] = useState({ email: '', password: '' @@ -14,9 +19,6 @@ const Login = () => { message: '' }); - const dispatch = useDispatch(); - const auth = useSelector(state => state.auth); - const handleAuthForm = (e) => { if(err.err) { setErr({ ...err, err: false}) @@ -42,9 +44,16 @@ const Login = () => { return; } dispatch(authAdded({current_user})); - console.log(auth); + navigate("/dashboard") } + useEffect(() => { + if(!auth == {}) { + console.log("BRUHH", auth); + navigate("/dashboard") + } + }) + return ( <>
    @@ -95,7 +104,7 @@ const Login = () => {
    +
    +
    +
    + +
    +
    + + ) +} + +export default News; \ No newline at end of file diff --git a/src/pages/User/index.jsx b/src/pages/User/index.jsx new file mode 100644 index 0000000..af8593c --- /dev/null +++ b/src/pages/User/index.jsx @@ -0,0 +1,58 @@ +import { useMemo } from 'react'; +import data from '../../datas/users.json'; +import { DataTable } from '../../components'; + +const User = () => { + + let roleTemp = []; + data.map(({ role_name }) => { + roleTemp.push(role_name) + }) + + const role_set = new Set([...roleTemp]); + const role = [...role_set]; + + const columns = useMemo( + () => [ + { + Header: () => No, + id: 'rowNumber', + width: 30, + }, + { + Header: "Name", + accessor: 'name' + }, + { + Header: "Phone", + accessor: 'phone' + }, + { + Header: "Email", + accessor: 'email' + }, + { + Header: "Role", + accessor: "role_name" + } + ] + ) + + return ( + <> +
    + +
    +
    +
    + +
    +
    + + ) +} + +export default User; \ No newline at end of file diff --git a/src/pages/index.js b/src/pages/index.js index 8df7dad..38f3896 100644 --- a/src/pages/index.js +++ b/src/pages/index.js @@ -1,9 +1,13 @@ import Login from './Login'; import Notfound404 from './Notfound404'; import Dashboard from './Dashboard'; +import User from './User'; +import News from './News'; export { Login, Notfound404, Dashboard, + User, + News } \ No newline at end of file diff --git a/src/routes/index.jsx b/src/routes/index.jsx index 8fff3d6..ea36280 100644 --- a/src/routes/index.jsx +++ b/src/routes/index.jsx @@ -1,18 +1,24 @@ import { - Dashboard, - Login, - Notfound404 + Dashboard, + User, + News } from "../pages"; const routes = [ { - path: "/", - element: , - errorElement: + path: "/dashboard", + name: "Dashboard", + element: }, { - path: "/dashboard", - element: + path: "/users", + name: "Users", + element: + }, + { + path: "/news", + name: "News", + element: } ] diff --git a/src/utils/common.js b/src/utils/common.js new file mode 100644 index 0000000..c4bdb8c --- /dev/null +++ b/src/utils/common.js @@ -0,0 +1,7 @@ +export const getRouteName = (pathName) => { + return pathName.replace(/\/(.)/, (_, firstLetter) => firstLetter.toUpperCase()); +} + +export const classNames = (...classes) => { + return classes.filter(Boolean).join(" "); +} \ No newline at end of file diff --git a/src/utils/index.js b/src/utils/index.js new file mode 100644 index 0000000..e303b5b --- /dev/null +++ b/src/utils/index.js @@ -0,0 +1,6 @@ +import { getRouteName, classNames} from './common'; + +export { + getRouteName, + classNames, +} \ No newline at end of file diff --git a/tailwind.config.js b/tailwind.config.js index 9815354..d143567 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -1,14 +1,215 @@ +const defaultTheme = require('tailwindcss/defaultTheme') + /** @type {import('tailwindcss').Config} */ -export default { - content: [ - "./index.html", - "./src/**/*.{js,jsx,ts,tsx}", - ], +module.exports = { + content: ['./index.html', './src/**/*.{js,ts,jsx,tsx}'], + darkMode: 'class', theme: { - extend: {}, + fontFamily: { + satoshi: ['Satoshi', 'sans-serif'], + }, + colors: { + current: 'currentColor', + transparent: 'transparent', + white: '#FFFFFF', + black: '#1C2434', + body: '#64748B', + bodydark: '#AEB7C0', + bodydark1: '#DEE4EE', + bodydark2: '#8A99AF', + primary: '#022b3a', + stroke: '#E2E8F0', + gray: '#EFF4FB', + graydark: '#333A48', + whiten: '#F1F5F9', + whiter: '#F5F7FD', + boxdark: '#24303F', + 'boxdark-2': '#1A222C', + strokedark: '#2E3A47', + red: '#dc3545' + }, + screens: { + '2xsm': '375px', + xsm: '425px', + '3xl': '2000px', + ...defaultTheme.screens, + }, + extend: { + fontSize: { + 'title-xxl': ['44px', '55px'], + 'title-xl': ['36px', '45px'], + 'title-xl2': ['33px', '45px'], + 'title-lg': ['28px', '35px'], + 'title-md': ['24px', '30px'], + 'title-md2': ['26px', '30px'], + 'title-sm': ['20px', '26px'], + 'title-xsm': ['18px', '24px'], + }, + spacing: { + 4.5: '1.125rem', + 5.5: '1.375rem', + 6.5: '1.625rem', + 7.5: '1.875rem', + 8.5: '2.125rem', + 9.5: '2.375rem', + 10.5: '2.625rem', + 11: '2.75rem', + 11.5: '2.875rem', + 12.5: '3.125rem', + 13: '3.25rem', + 13.5: '3.375rem', + 14: '3.5rem', + 14.5: '3.625rem', + 15: '3.75rem', + 15.5: '3.875rem', + 16: '4rem', + 16.5: '4.125rem', + 17: '4.25rem', + 17.5: '4.375rem', + 18: '4.5rem', + 18.5: '4.625rem', + 19: '4.75rem', + 19.5: '4.875rem', + 21: '5.25rem', + 21.5: '5.375rem', + 22: '5.5rem', + 22.5: '5.625rem', + 24.5: '6.125rem', + 25: '6.25rem', + 25.5: '6.375rem', + 26: '6.5rem', + 27: '6.75rem', + 27.5: '6.875rem', + 29: '7.25rem', + 29.5: '7.375rem', + 30: '7.5rem', + 31: '7.75rem', + 32.5: '8.125rem', + 34: '8.5rem', + 34.5: '8.625rem', + 35: '8.75rem', + 36.5: '9.125rem', + 37.5: '9.375rem', + 39: '9.75rem', + 39.5: '9.875rem', + 40: '10rem', + 42.5: '10.625rem', + 44: '11rem', + 45: '11.25rem', + 46: '11.5rem', + 47.5: '11.875rem', + 49: '12.25rem', + 50: '12.5rem', + 52: '13rem', + 52.5: '13.125rem', + 54: '13.5rem', + 54.5: '13.625rem', + 55: '13.75rem', + 55.5: '13.875rem', + 59: '14.75rem', + 60: '15rem', + 62.5: '15.625rem', + 65: '16.25rem', + 67: '16.75rem', + 67.5: '16.875rem', + 70: '17.5rem', + 72.5: '18.125rem', + 73: '18.25rem', + 75: '18.75rem', + 90: '22.5rem', + 94: '23.5rem', + 95: '23.75rem', + 100: '25rem', + 115: '28.75rem', + 125: '31.25rem', + 132.5: '33.125rem', + 150: '37.5rem', + 171.5: '42.875rem', + 180: '45rem', + 187.5: '46.875rem', + 203: '50.75rem', + 230: '57.5rem', + 242.5: '60.625rem', + }, + maxWidth: { + 2.5: '0.625rem', + 3: '0.75rem', + 4: '1rem', + 11: '2.75rem', + 13: '3.25rem', + 14: '3.5rem', + 15: '3.75rem', + 22.5: '5.625rem', + 25: '6.25rem', + 30: '7.5rem', + 34: '8.5rem', + 35: '8.75rem', + 40: '10rem', + 42.5: '10.625rem', + 44: '11rem', + 45: '11.25rem', + 70: '17.5rem', + 90: '22.5rem', + 94: '23.5rem', + 125: '31.25rem', + 132.5: '33.125rem', + 142.5: '35.625rem', + 150: '37.5rem', + 180: '45rem', + 203: '50.75rem', + 230: '57.5rem', + 242.5: '60.625rem', + 270: '67.5rem', + 280: '70rem', + 292.5: '73.125rem', + }, + maxHeight: { + 35: '8.75rem', + 70: '17.5rem', + 90: '22.5rem', + 550: '34.375rem', + 300: '18.75rem', + }, + minWidth: { + 22.5: '5.625rem', + 42.5: '10.625rem', + 47.5: '11.875rem', + 75: '18.75rem', + }, + zIndex: { + 999999: '999999', + 99999: '99999', + 9999: '9999', + 999: '999', + 99: '99', + 9: '9', + 1: '1', + }, + opacity: { + 65: '.65', + }, + borderWidth: { + 6: '6px', + }, + boxShadow: { + default: '0px 8px 13px -3px rgba(0, 0, 0, 0.07)', + card: '0px 1px 3px rgba(0, 0, 0, 0.12)', + 'card-2': '0px 1px 2px rgba(0, 0, 0, 0.05)', + switcher: + '0px 2px 4px rgba(0, 0, 0, 0.2), inset 0px 2px 2px #FFFFFF, inset 0px -1px 1px rgba(0, 0, 0, 0.1)', + 'switch-1': '0px 0px 5px rgba(0, 0, 0, 0.15)', + 1: '0px 1px 3px rgba(0, 0, 0, 0.08)', + 2: '0px 1px 4px rgba(0, 0, 0, 0.12)', + 3: '0px 1px 5px rgba(0, 0, 0, 0.14)', + 4: '0px 4px 10px rgba(0, 0, 0, 0.12)', + 5: '0px 1px 1px rgba(0, 0, 0, 0.15)', + 6: '0px 3px 15px rgba(0, 0, 0, 0.1)', + 7: '-5px 0 0 #313D4A, 5px 0 0 #313D4A', + 8: '1px 0 0 #313D4A, -1px 0 0 #313D4A, 0 1px 0 #313D4A, 0 -1px 0 #313D4A, 0 3px 13px rgb(0 0 0 / 8%)', + }, + }, }, plugins: [ - require('@tailwindcss/forms'), + require('@tailwindcss/forms') ], -} - +} \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index eec43ab..cbb4a3b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -184,7 +184,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.22.5" -"@babel/runtime@^7.12.1", "@babel/runtime@^7.9.2": +"@babel/runtime@^7.12.1", "@babel/runtime@^7.14.6", "@babel/runtime@^7.9.2": version "7.22.10" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.22.10.tgz#ae3e9631fd947cb7e3610d3e9d8fef5f76696682" integrity sha512-21t/fkKLMZI4pqP2wlmsQAWnYW1PDyKyyUV4vCi+B25ydmdaYTKXPwCj0BzSUnZf4seIiYvSA3jcZ3gdsMFkLQ== @@ -367,6 +367,11 @@ resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.47.0.tgz#5478fdf443ff8158f9de171c704ae45308696c7d" integrity sha512-P6omY1zv5MItm93kLM8s2vr1HICJH8v0dvddDhysbIuZ+vcjOHg5Zbkf1mTkcmi2JA9oBG2anOkRnW8WJTS8Og== +"@heroicons/react@v1": + version "1.0.6" + resolved "https://registry.yarnpkg.com/@heroicons/react/-/react-1.0.6.tgz#35dd26987228b39ef2316db3b1245c42eb19e324" + integrity sha512-JJCXydOFWMDpCP4q13iEplA503MQO3xLoZiKum+955ZCtHINWnx26CUxVxxFQu/uLb4LW3ge15ZpzIkXKkJ8oQ== + "@humanwhocodes/config-array@^0.11.10": version "0.11.10" resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.10.tgz#5a3ffe32cc9306365fb3fd572596cd602d5e12d2" @@ -461,6 +466,35 @@ dependencies: mini-svg-data-uri "^1.2.3" +"@types/d3-array@^3.0.1": + version "3.0.5" + resolved "https://registry.yarnpkg.com/@types/d3-array/-/d3-array-3.0.5.tgz#857c1afffd3f51319bbc5b301956aca68acaa7b8" + integrity sha512-Qk7fpJ6qFp+26VeQ47WY0mkwXaiq8+76RJcncDEfMc2ocRzXLO67bLFRNI4OX1aGBoPzsM5Y2T+/m1pldOgD+A== + +"@types/d3-path@*": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@types/d3-path/-/d3-path-3.0.0.tgz#939e3a784ae4f80b1fde8098b91af1776ff1312b" + integrity sha512-0g/A+mZXgFkQxN3HniRDbXMN79K3CdTpLsevj+PXiTcb2hVyvkZUBg37StmgCQkaD84cUJ4uaDAWq7UJOQy2Tg== + +"@types/d3-scale@^4.0.1": + version "4.0.3" + resolved "https://registry.yarnpkg.com/@types/d3-scale/-/d3-scale-4.0.3.tgz#7a5780e934e52b6f63ad9c24b105e33dd58102b5" + integrity sha512-PATBiMCpvHJSMtZAMEhc2WyL+hnzarKzI6wAHYjhsonjWJYGq5BXTzQjv4l8m2jO183/4wZ90rKvSeT7o72xNQ== + dependencies: + "@types/d3-time" "*" + +"@types/d3-shape@^3.0.1": + version "3.1.1" + resolved "https://registry.yarnpkg.com/@types/d3-shape/-/d3-shape-3.1.1.tgz#15cc497751dac31192d7aef4e67a8d2c62354b95" + integrity sha512-6Uh86YFF7LGg4PQkuO2oG6EMBRLuW9cbavUW46zkIO5kuS2PfTqo2o9SkgtQzguBHbLgNnU90UNsITpsX1My+A== + dependencies: + "@types/d3-path" "*" + +"@types/d3-time@*": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@types/d3-time/-/d3-time-3.0.0.tgz#e1ac0f3e9e195135361fa1a1d62f795d87e6e819" + integrity sha512-sZLCdHvBUcNby1cB6Fd3ZBrABbjz3v1Vm90nysCQ6Vt7vd6e/h9Lt7SiJUoEX0l4Dzc7P5llKyhqSi1ycSf1Hg== + "@types/hoist-non-react-statics@^3.3.1": version "3.3.1" resolved "https://registry.yarnpkg.com/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz#1124aafe5118cb591977aeb1ceaaed1070eb039f" @@ -474,6 +508,18 @@ resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.5.tgz#5f19d2b85a98e9558036f6a3cacc8819420f05cf" integrity sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w== +"@types/raf@^3.4.0": + version "3.4.0" + resolved "https://registry.yarnpkg.com/@types/raf/-/raf-3.4.0.tgz#2b72cbd55405e071f1c4d29992638e022b20acc2" + integrity sha512-taW5/WYqo36N7V39oYyHP9Ipfd5pNFvGTIQsNGj86xV88YQ7GnI30/yMfKDF7Zgin0m3e+ikX88FvImnK4RjGw== + +"@types/react-dom@^17.0.9": + version "17.0.20" + resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-17.0.20.tgz#e0c8901469d732b36d8473b40b679ad899da1b53" + integrity sha512-4pzIjSxDueZZ90F52mU3aPoogkHIoSIDG+oQ+wQK7Cy2B9S+MvOqY0uEA/qawKz381qrEDkvpwyt8Bm31I8sbA== + dependencies: + "@types/react" "^17" + "@types/react-dom@^18.2.7": version "18.2.7" resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.2.7.tgz#67222a08c0a6ae0a0da33c3532348277c70abb63" @@ -490,6 +536,15 @@ "@types/scheduler" "*" csstype "^3.0.2" +"@types/react@^17", "@types/react@^17.0.14": + version "17.0.64" + resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.64.tgz#468162c66c33ddb4548eb1a0e36682028d9e9a62" + integrity sha512-IlgbX/vglDTwrCRgad6fTCzOT+D/5C0xwuvrzfuqfhg9gJrkFqAGADpUFlEtqbrP1IEo9QLSbo41MaFfoIu9Aw== + dependencies: + "@types/prop-types" "*" + "@types/scheduler" "*" + csstype "^3.0.2" + "@types/scheduler@*": version "0.16.3" resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.3.tgz#cef09e3ec9af1d63d2a6cc5b383a737e24e6dcf5" @@ -799,6 +854,95 @@ csstype@^3.0.2: resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.2.tgz#1d4bf9d572f11c14031f0436e1c10bc1f571f50b" integrity sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ== +d3-array@2, d3-array@^2.12.1, d3-array@^2.3.0: + version "2.12.1" + resolved "https://registry.yarnpkg.com/d3-array/-/d3-array-2.12.1.tgz#e20b41aafcdffdf5d50928004ececf815a465e81" + integrity sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ== + dependencies: + internmap "^1.0.0" + +"d3-array@2 - 3": + version "3.2.4" + resolved "https://registry.yarnpkg.com/d3-array/-/d3-array-3.2.4.tgz#15fec33b237f97ac5d7c986dc77da273a8ed0bb5" + integrity sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg== + dependencies: + internmap "1 - 2" + +"d3-color@1 - 2": + version "2.0.0" + resolved "https://registry.yarnpkg.com/d3-color/-/d3-color-2.0.0.tgz#8d625cab42ed9b8f601a1760a389f7ea9189d62e" + integrity sha512-SPXi0TSKPD4g9tw0NMZFnR95XVgUZiBH+uUTqQuDu1OsE2zomHU7ho0FISciaPvosimixwHFl3WHLGabv6dDgQ== + +d3-delaunay@5.3.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/d3-delaunay/-/d3-delaunay-5.3.0.tgz#b47f05c38f854a4e7b3cea80e0bb12e57398772d" + integrity sha512-amALSrOllWVLaHTnDLHwMIiz0d1bBu9gZXd1FiLfXf8sHcX9jrcj81TVZOqD4UX7MgBZZ07c8GxzEgBpJqc74w== + dependencies: + delaunator "4" + +"d3-format@1 - 2": + version "2.0.0" + resolved "https://registry.yarnpkg.com/d3-format/-/d3-format-2.0.0.tgz#a10bcc0f986c372b729ba447382413aabf5b0767" + integrity sha512-Ab3S6XuE/Q+flY96HXT0jOXcM4EAClYFnRGY5zsjRGNy6qCYrQsMffs7cV5Q9xejb35zxW5hf/guKw34kvIKsA== + +"d3-interpolate@1.2.0 - 2": + version "2.0.1" + resolved "https://registry.yarnpkg.com/d3-interpolate/-/d3-interpolate-2.0.1.tgz#98be499cfb8a3b94d4ff616900501a64abc91163" + integrity sha512-c5UhwwTs/yybcmTpAVqwSFl6vrQ8JZJoT5F7xNFK9pymv5C0Ymcc9/LIJHtYIggg/yS9YHw8i8O8tgb9pupjeQ== + dependencies: + d3-color "1 - 2" + +"d3-path@1 - 2": + version "2.0.0" + resolved "https://registry.yarnpkg.com/d3-path/-/d3-path-2.0.0.tgz#55d86ac131a0548adae241eebfb56b4582dd09d8" + integrity sha512-ZwZQxKhBnv9yHaiWd6ZU4x5BtCQ7pXszEV9CU6kRgwIQVQGLMv1oiL4M+MK/n79sYzsj+gcgpPQSctJUsLN7fA== + +d3-scale@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/d3-scale/-/d3-scale-3.3.0.tgz#28c600b29f47e5b9cd2df9749c206727966203f3" + integrity sha512-1JGp44NQCt5d1g+Yy+GeOnZP7xHo0ii8zsQp6PGzd+C1/dl0KGsp9A7Mxwp+1D1o4unbTTxVdU/ZOIEBoeZPbQ== + dependencies: + d3-array "^2.3.0" + d3-format "1 - 2" + d3-interpolate "1.2.0 - 2" + d3-time "^2.1.1" + d3-time-format "2 - 3" + +d3-shape@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/d3-shape/-/d3-shape-2.1.0.tgz#3b6a82ccafbc45de55b57fcf956c584ded3b666f" + integrity sha512-PnjUqfM2PpskbSLTJvAzp2Wv4CZsnAgTfcVRTwW03QR3MkXF8Uo7B1y/lWkAsmbKwuecto++4NlsYcvYpXpTHA== + dependencies: + d3-path "1 - 2" + +"d3-time-format@2 - 3": + version "3.0.0" + resolved "https://registry.yarnpkg.com/d3-time-format/-/d3-time-format-3.0.0.tgz#df8056c83659e01f20ac5da5fdeae7c08d5f1bb6" + integrity sha512-UXJh6EKsHBTjopVqZBhFysQcoXSv/5yLONZvkQ5Kk3qbwiUYkdX17Xa1PT6U1ZWXGGfB1ey5L8dKMlFq2DO0Ag== + dependencies: + d3-time "1 - 2" + +d3-time-format@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/d3-time-format/-/d3-time-format-4.1.0.tgz#7ab5257a5041d11ecb4fe70a5c7d16a195bb408a" + integrity sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg== + dependencies: + d3-time "1 - 3" + +"d3-time@1 - 2", d3-time@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/d3-time/-/d3-time-2.1.1.tgz#e9d8a8a88691f4548e68ca085e5ff956724a6682" + integrity sha512-/eIQe/eR4kCQwq7yxi7z4c6qEXf2IYGcjoWB5OOQy4Tq9Uv39/947qlDcN2TLkiTzQWzvnsuYPB9TrWaNfipKQ== + dependencies: + d3-array "2" + +"d3-time@1 - 3": + version "3.1.0" + resolved "https://registry.yarnpkg.com/d3-time/-/d3-time-3.1.0.tgz#9310db56e992e3c0175e1ef385e545e48a9bb5c7" + integrity sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q== + dependencies: + d3-array "2 - 3" + debug@^4.1.0, debug@^4.1.1, debug@^4.3.2: version "4.3.4" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" @@ -819,6 +963,11 @@ define-properties@^1.1.3, define-properties@^1.1.4, define-properties@^1.2.0: has-property-descriptors "^1.0.0" object-keys "^1.1.1" +delaunator@4: + version "4.0.1" + resolved "https://registry.yarnpkg.com/delaunator/-/delaunator-4.0.1.tgz#3d779687f57919a7a418f8ab947d3bddb6846957" + integrity sha512-WNPWi1IRKZfCt/qIDMfERkDp93+iZEmOxN2yy4Jg+Xhv8SLk2UTqqbe1sfiipn0and9QrE914/ihdx82Y/Giag== + didyoumean@^1.2.2: version "1.2.2" resolved "https://registry.yarnpkg.com/didyoumean/-/didyoumean-1.2.2.tgz#989346ffe9e839b4555ecf5666edea0d3e8ad037" @@ -1381,6 +1530,16 @@ internal-slot@^1.0.3, internal-slot@^1.0.5: has "^1.0.3" side-channel "^1.0.4" +"internmap@1 - 2": + version "2.0.3" + resolved "https://registry.yarnpkg.com/internmap/-/internmap-2.0.3.tgz#6685f23755e43c524e251d29cbc97248e3061009" + integrity sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg== + +internmap@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/internmap/-/internmap-1.0.1.tgz#0017cc8a3b99605f0302f2b198d272e015e5df95" + integrity sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw== + is-array-buffer@^3.0.1, is-array-buffer@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/is-array-buffer/-/is-array-buffer-3.0.2.tgz#f2653ced8412081638ecb0ebbd0c41c6e0aecbbe" @@ -1634,6 +1793,11 @@ minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: dependencies: brace-expansion "^1.1.7" +moment@^2.29.4: + version "2.29.4" + resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.4.tgz#3dbe052889fe7c1b2ed966fcb3a77328964ef108" + integrity sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w== + ms@2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" @@ -1895,6 +2059,26 @@ queue-microtask@^1.2.2: resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== +react-charts@^3.0.0-beta.55: + version "3.0.0-beta.55" + resolved "https://registry.yarnpkg.com/react-charts/-/react-charts-3.0.0-beta.55.tgz#07b91432f18bd739030cb742bcde915a62760633" + integrity sha512-PuPGoK/3B4SgmnANqh1+biuJAN/iYAvLO/juLKJ2dtF0+Wzfa0KarGbgAij2o8P83bAn9bZnu46RCevNvYwOjQ== + dependencies: + "@babel/runtime" "^7.14.6" + "@types/d3-array" "^3.0.1" + "@types/d3-scale" "^4.0.1" + "@types/d3-shape" "^3.0.1" + "@types/raf" "^3.4.0" + "@types/react" "^17.0.14" + "@types/react-dom" "^17.0.9" + d3-array "^2.12.1" + d3-delaunay "5.3.0" + d3-scale "^3.3.0" + d3-shape "^2.1.0" + d3-time "^2.1.1" + d3-time-format "^4.1.0" + ts-toolbelt "^9.6.0" + react-dom@^18.2.0: version "18.2.0" resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.2.0.tgz#22aaf38708db2674ed9ada224ca4aa708d821e3d" @@ -1945,6 +2129,11 @@ react-router@6.15.0: dependencies: "@remix-run/router" "1.8.0" +react-table@^7.8.0: + version "7.8.0" + resolved "https://registry.yarnpkg.com/react-table/-/react-table-7.8.0.tgz#07858c01c1718c09f7f1aed7034fcfd7bda907d2" + integrity sha512-hNaz4ygkZO4bESeFfnfOft73iBUj8K5oKi1EcSHPAibEydfsX2MyU6Z8KCr3mv3C9Kqqh71U+DhZkFvibbnPbA== + react@^18.2.0: version "18.2.0" resolved "https://registry.yarnpkg.com/react/-/react-18.2.0.tgz#555bd98592883255fa00de14f1151a917b5d77d5" @@ -2257,6 +2446,11 @@ ts-interface-checker@^0.1.9: resolved "https://registry.yarnpkg.com/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz#784fd3d679722bc103b1b4b8030bcddb5db2a699" integrity sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA== +ts-toolbelt@^9.6.0: + version "9.6.0" + resolved "https://registry.yarnpkg.com/ts-toolbelt/-/ts-toolbelt-9.6.0.tgz#50a25426cfed500d4a09bd1b3afb6f28879edfd5" + integrity sha512-nsZd8ZeNUzukXPlJmTBwUAuABDe/9qtVDelJeT/qW0ow3ZS3BsQJtNkan1802aM9Uf68/Y8ljw86Hu0h5IUW3w== + type-check@^0.4.0, type-check@~0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1"