add dashboard

This commit is contained in:
nochill 2023-08-18 07:34:30 +07:00
parent 988fb33b09
commit 07c4421987
34 changed files with 2071 additions and 34 deletions

View File

@ -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"

View File

@ -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 (
<>
<Provider store={store}>
<PersistGate persistor={persistore}>
<RouterProvider router={router} />
<Routes>
<Route index element={<Login />} />
<Route element={<DefaultLayout />}>
{routes.map(({ path, name, element}) => (
<>
<Route
path={path}
id={name}
element={element}
/>
</>
))}
</Route>
</Routes>
</PersistGate>
</Provider>
</>
)
}

View File

@ -0,0 +1,5 @@
import { LOGOUT } from "../constants/action_type";
export const logout = () => ({
type: LOGOUT
});

1
src/actions/index.js Normal file
View File

@ -0,0 +1 @@
export * from './AuthAction';

View File

@ -0,0 +1,18 @@
import { classNames } from "../../../../utils";
const PaginationDefaultButton = ({ children, className, ...rest }) => {
return (
<button
type="button"
className={classNames(
"relative inline-flex items-center px-4 py-2 border border-gray-300 text-sm font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50",
className
)}
{...rest}
>
{children}
</button>
);
}
export default PaginationDefaultButton;

View File

@ -0,0 +1,18 @@
import { classNames } from "../../../../utils";
const PaginationPageButton = ({ children, className, ...rest }) => {
return (
<button
type="button"
className={classNames(
"relative inline-flex items-center px-2 py-2 border border-gray-300 bg-white text-sm font-medium text-gray-500 hover:bg-gray-50",
className
)}
{...rest}
>
{children}
</button>
);
}
export default PaginationPageButton;

View File

@ -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) => (
<div key={option}>
{option}: &nbsp;
<select
value={state[option] as string}
onChange={({ target: { value } }) =>
setState((old) => ({
...old,
[option]:
typeof options[option][0] === "boolean"
? value === "true"
: value,
}))
}
>
{options[option].map((d: any) => (
<option value={d as string} key={d.toString()}>
{d.toString()}
</option>
))}
</select>
<br />
</div>
));
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,
};
}),
};
}

View File

@ -0,0 +1,8 @@
table tr:nth-child(odd) td{
background-color: #f5f6fa;
}
.tab-filter:hover {
border-bottom: 3px solid black;
color: #055274 !important;
}

View File

@ -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 (
<>
<GlobalFilter
preGlobalFilteredRows={preGlobalFilteredRows}
globalFilter={state.globalFilter}
setGlobalFilter={setGlobalFilter}
tabFilterData={tabFilterData}
onSelectedTabFilter={(role_name) => setSelectedRole(role_name)}
selectedRole={selectedRole}
/>
<div className="mt-2 flex flex-col">
<div className="-my-2 overflow-x-auto -mx-4 sm:-mx-6 lg:-mx-8">
<div className="py-2 align-middle inline-block min-w-full sm:px-6 lg:px-8">
<div className="shadow overflow-hidden rounded">
<table {...getTableProps()} className="min-w-full divide-y divide-gray">
<thead className="bg-primary">
{headerGroups.map((headerGroup) => (
<tr style={{ padding: 10 }} {...headerGroup.getHeaderGroupProps()}>
{headerGroup.headers.map((column) => (
<th className="p-2" style={{ color: 'white', textAlign: 'left' }} {...column.getHeaderProps()}>{column.render("Header")}</th>
))}
<th style={{ color: 'white', textAlign: 'left', width: '5%' }}>Action</th>
</tr>
))}
</thead>
<tbody {...getTableBodyProps()}>
{row_data.map((row, i) => {
prepareRow(row);
return (
<tr {...row.getRowProps()}>
{row.cells.map((cell) => {
return (
<td className="p-2"{...cell.getCellProps()}>
{cell.column.id === 'rowNumber' ? i + 1 : cell.render('Cell')}
</td>
);
})}
<td className="flex flex-row py-3 gap-2 px-3">
<button className="px-4 py-1 rounded" style={{ border: '1px solid black', fontSize: 14 }}>
Edit
</button>
<button className='bg-red text-white px-4 py-1 rounded' style={{ fontSize: 14}}>
Delete
</button>
</td>
</tr>
);
})}
</tbody>
</table>
</div>
</div>
</div>
</div>
<div className="py-3 flex items-center justify-between">
<div className="flex-1 flex justify-between sm:hidden">
<PaginationDefaultButton onClick={() => previousPage()} disabled={!canPreviousPage}>Previous</PaginationDefaultButton>
<PaginationDefaultButton onClick={() => nextPage()} disabled={!canNextPage}>Next</PaginationDefaultButton>
</div>
<div className="hidden sm:flex-1 sm:flex sm:items-center sm:justify-between">
<div className="flex gap-x-2 items-baseline">
<span className="text-sm text-gray-700">
Page <span className="font-medium">{state.pageIndex + 1}</span> of <span className="font-medium">{pageOptions.length}</span>
</span>
<label>
<span className="sr-only">Items Per Page</span>
<select
className="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50"
value={state.pageSize}
onChange={e => {
setPageSize(Number(e.target.value))
}}
>
{[5, 10, 20].map(pageSize => (
<option key={pageSize} value={pageSize}>
Show {pageSize}
</option>
))}
</select>
</label>
</div>
<div>
<nav className="relative z-0 inline-flex rounded-md shadow-sm -space-x-px" aria-label="Pagination">
<PaginationPageButton
className="rounded-l-md"
onClick={() => gotoPage(0)}
disabled={!canPreviousPage}
>
<span className="sr-only">First</span>
<ChevronDoubleLeftIcon className="h-5 w-5 text-gray-400" aria-hidden="true" />
</PaginationPageButton>
<PaginationPageButton
onClick={() => previousPage()}
disabled={!canPreviousPage}
>
<span className="sr-only">Previous</span>
<ChevronLeftIcon className="h-5 w-5 text-gray-400" aria-hidden="true" />
</PaginationPageButton>
<PaginationPageButton
onClick={() => nextPage()}
disabled={!canNextPage
}>
<span className="sr-only">Next</span>
<ChevronRightIcon className="h-5 w-5 text-gray-400" aria-hidden="true" />
</PaginationPageButton>
<PaginationPageButton
className="rounded-r-md"
onClick={() => gotoPage(pageCount - 1)}
disabled={!canNextPage}
>
<span className="sr-only">Last</span>
<ChevronDoubleRightIcon className="h-5 w-5 text-gray-400" aria-hidden="true" />
</PaginationPageButton>
</nav>
</div>
</div>
</div>
</>
);
}
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 (
<>
<div className="bg-gray p-3 mb-4">
<div class="relative text-gray-600 focus-within:text-gray-400 flex flex-row">
<span class="absolute inset-y-0 left-0 flex items-center pl-2">
<button type="submit" class="p-1 focus:outline-none focus:shadow-outline">
<svg fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" viewBox="0 0 24 24" class="w-4 h-4"><path d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"></path></svg>
</button>
</span>
<input
type="search"
name="q"
className="py-2 text-sm text-black bg-gray-900 rounded-md pl-10 focus:outline-none focus:bg-white focus:text-gray-900"
style={{ width: '90% ' }}
placeholder={`Enter Keyword ...`}
value={value}
onChange={e => {
setValue(e.target.value);
onChange(e.target.value)
}}
autocomplete="off"
/>
<button className='ml-4 bg-white rounded justify-center hover:bg-opacity-70 font-medium px-4 py-3 flex text-black' style={{ borderColor: 'black', borderWidth: 1 }}>
<svg xmlns="http://www.w3.org/2000/svg" className="mr-3" style={{ alignSelf: 'center' }} fill="black" height="18" viewBox="0 -960 960 960" width="18"><path d="M440-160q-17 0-28.5-11.5T400-200v-240L161-745q-14-17-4-36t31-19h584q21 0 31 19t-4 36L560-440v240q0 17-11.5 28.5T520-160h-80Zm40-276 240-304H240l240 304Zm0 0Z" /></svg>
Filters
</button>
</div>
</div>
<div className='flex mb-4' style={{ borderBottomWidth: 1 }}>
{tabsFilter.map((filter) => (
<ul>
<a onClick={() => onSelectedTabFilter(filter)} href='#' className='mr-4 tab-filter' style={selectedRole === filter ? { borderBottom: '3px solid black', color: '#055274' } : { color: '#7fa8b9' }}>{filter}</a>
</ul>
))}
</div>
</>
)
}
export default DataTable;

View File

@ -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 (
<header className="sticky top-0 z-999 flex w-full bg-white drop-shadow-1 dark:bg-boxdark dark:drop-shadow-none">
<div className="flex flex-grow items-center py-4 px-4 shadow-2 md:px-6 2xl:px-11">
<div className="flex items-center gap-2 sm:gap-4 lg:hidden">
{/* <!-- Hamburger Toggle BTN --> */}
<button
aria-controls="sidebar"
onClick={(e) => {
e.stopPropagation();
props.setSidebarOpen(!props.sidebarOpen);
}}
className="z-99999 block rounded-sm border border-stroke bg-white p-1.5 shadow-sm dark:border-strokedark dark:bg-boxdark lg:hidden"
>
<span className="relative block h-5.5 w-5.5 cursor-pointer">
<span className="du-block absolute right-0 h-full w-full">
<span
className={`relative top-0 left-0 my-1 block h-0.5 w-0 rounded-sm bg-black delay-[0] duration-200 ease-in-out dark:bg-white ${
!props.sidebarOpen && '!w-full delay-300'
}`}
></span>
<span
className={`relative top-0 left-0 my-1 block h-0.5 w-0 rounded-sm bg-black delay-150 duration-200 ease-in-out dark:bg-white ${
!props.sidebarOpen && 'delay-400 !w-full'
}`}
></span>
<span
className={`relative top-0 left-0 my-1 block h-0.5 w-0 rounded-sm bg-black delay-200 duration-200 ease-in-out dark:bg-white ${
!props.sidebarOpen && '!w-full delay-500'
}`}
></span>
</span>
<span className="absolute right-0 h-full w-full rotate-45">
<span
className={`absolute left-2.5 top-0 block h-full w-0.5 rounded-sm bg-black delay-300 duration-200 ease-in-out dark:bg-white ${
!props.sidebarOpen && '!h-0 !delay-[0]'
}`}
></span>
<span
className={`delay-400 absolute left-0 top-2.5 block h-0.5 w-full rounded-sm bg-black duration-200 ease-in-out dark:bg-white ${
!props.sidebarOpen && '!h-0 !delay-200'
}`}
></span>
</span>
</span>
</button>
{/* <!-- Hamburger Toggle BTN --> */}
{/* <Link className="block flex-shrink-0 lg:hidden" to="/">
<img src={Logo} alt="Logo" />
</Link> */}
</div>
<div className="ml-4 flex-1">
<h1>Loan market / {props.routeName}</h1>
</div>
<button onClick={() => handleLogoutClick()}>
<h1>logout</h1>
</button>
{/* <div className="flex items-center gap-3 2xsm:gap-7">
<ul className="flex items-center gap-2 2xsm:gap-4">
<!-- Dark Mode Toggler -->
<DarkModeSwitcher />
<!-- Dark Mode Toggler -->
<!-- Notification Menu Area -->
<DropdownNotification />
<!-- Notification Menu Area -->
<!-- Chat Notification Area -->
<DropdownMessage />
<!-- Chat Notification Area -->
</ul>
<!-- User Area -->
<DropdownUser />
<!-- User Area -->
</div> */}
</div>
</header>
);
};
export default Header;

View File

@ -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 (
<div className="mt-4 mb-4">
<div className="flex flex-row justify-between">
<p style={labelStyles}>{props.label}</p>
<p style={labelStyles}>{props.completed} %</p>
</div>
<div style={containerStyles}>
<div style={fillerStyles}>
</div>
</div>
</div>
);
};
export default ProgressBar;

View File

@ -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 (
<aside
ref={sidebar}
className={`absolute left-0 top-0 z-9999 flex h-screen w-72.5 flex-col overflow-y-hidden bg-black duration-300 ease-linear dark:bg-boxdark lg:static lg:translate-x-0 ${
sidebarOpen ? 'translate-x-0' : '-translate-x-full'
}`}
>
{/* <!-- SIDEBAR HEADER --> */}
<div className="flex items-center justify-between gap-2 px-6 py-5.5 lg:py-6.5">
{/* <NavLink to="/">
<img src={Logo} alt="Logo" />
</NavLink> */}
<button
ref={trigger}
onClick={() => setSidebarOpen(!sidebarOpen)}
aria-controls="sidebar"
aria-expanded={sidebarOpen}
className="block lg:hidden"
>
<svg
style={{ fill: 'white'}}
width="20"
height="18"
viewBox="0 0 20 18"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M19 8.175H2.98748L9.36248 1.6875C9.69998 1.35 9.69998 0.825 9.36248 0.4875C9.02498 0.15 8.49998 0.15 8.16248 0.4875L0.399976 8.3625C0.0624756 8.7 0.0624756 9.225 0.399976 9.5625L8.16248 17.4375C8.31248 17.5875 8.53748 17.7 8.76248 17.7C8.98748 17.7 9.17498 17.625 9.36248 17.475C9.69998 17.1375 9.69998 16.6125 9.36248 16.275L3.02498 9.8625H19C19.45 9.8625 19.825 9.4875 19.825 9.0375C19.825 8.55 19.45 8.175 19 8.175Z"
/>
</svg>
</button>
</div>
{/* <!-- SIDEBAR HEADER --> */}
<div className="no-scrollbar flex flex-col overflow-y-auto duration-300 ease-linear">
{/* <!-- Sidebar Menu --> */}
<div className="mt-5 py-4 px-4 lg:px-6">
<h3 className="ml-4 text-sm font-semibold text-white">{auth.name}</h3>
<h3 className="mb-4 ml-4 text-sm font-semibold text-bodydark2">{auth.role_name}</h3>
</div>
<nav className="py-4 px-4 lg:px-6">
{/* <!-- Menu Group --> */}
<div>
<h3 className="mb-4 ml-4 text-sm font-semibold text-bodydark2">
MENU
</h3>
<ul className="mb-6 flex flex-col gap-1.5">
{/* <!-- Menu Item Calendar --> */}
<li>
<NavLink
to="/dashboard"
className={`group relative flex items-center gap-2.5 rounded-sm py-2 px-4 font-medium text-bodydark1 duration-300 ease-in-out hover:bg-graydark dark:hover:bg-meta-4 ${
pathname.includes('dashboard') &&
'bg-graydark dark:bg-meta-4'
}`}
>
<svg
className="fill-current"
width="18"
height="18"
viewBox="0 -960 960 960"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M220-180h150v-250h220v250h150v-390L480-765 220-570v390Zm-60 60v-480l320-240 320 240v480H530v-250H430v250H160Zm320-353Z"
fill=""
/>
</svg>
Dashboard
</NavLink>
</li>
<li>
<NavLink
to="/users"
className={`group relative flex items-center gap-2.5 rounded-sm py-2 px-4 font-medium text-bodydark1 duration-300 ease-in-out hover:bg-graydark dark:hover:bg-meta-4 ${
pathname.includes('user') && 'bg-graydark dark:bg-meta-4'
}`}
>
<svg
className="fill-current"
width="18"
height="18"
viewBox="0 0 18 18"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M9.0002 7.79065C11.0814 7.79065 12.7689 6.1594 12.7689 4.1344C12.7689 2.1094 11.0814 0.478149 9.0002 0.478149C6.91895 0.478149 5.23145 2.1094 5.23145 4.1344C5.23145 6.1594 6.91895 7.79065 9.0002 7.79065ZM9.0002 1.7719C10.3783 1.7719 11.5033 2.84065 11.5033 4.16252C11.5033 5.4844 10.3783 6.55315 9.0002 6.55315C7.62207 6.55315 6.49707 5.4844 6.49707 4.16252C6.49707 2.84065 7.62207 1.7719 9.0002 1.7719Z"
fill=""
/>
<path
d="M10.8283 9.05627H7.17207C4.16269 9.05627 1.71582 11.5313 1.71582 14.5406V16.875C1.71582 17.2125 1.99707 17.5219 2.3627 17.5219C2.72832 17.5219 3.00957 17.2407 3.00957 16.875V14.5406C3.00957 12.2344 4.89394 10.3219 7.22832 10.3219H10.8564C13.1627 10.3219 15.0752 12.2063 15.0752 14.5406V16.875C15.0752 17.2125 15.3564 17.5219 15.7221 17.5219C16.0877 17.5219 16.3689 17.2407 16.3689 16.875V14.5406C16.2846 11.5313 13.8377 9.05627 10.8283 9.05627Z"
fill=""
/>
</svg>
User management
</NavLink>
</li>
<SidebarLinkGroup
activeCondition={
pathname === '/news' || pathname.includes('news')
}
>
{(handleClick, open) => {
return (
<React.Fragment>
<NavLink
to="news"
className={`group relative flex items-center gap-2.5 rounded-sm py-2 px-4 font-medium text-bodydark1 duration-300 ease-in-out hover:bg-graydark dark:hover:bg-meta-4 ${
(pathname === '/' ||
pathname.includes('news')) &&
'bg-graydark dark:bg-meta-4'
}`}
onClick={(e) => {
e.preventDefault();
sidebarExpanded
? handleClick()
: setSidebarExpanded(true);
}}
>
<svg
className="fill-current"
width="18"
height="18"
viewBox="0 -960 960 960"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M167-120q-21 5-36.5-10.5T120-167l40-191 198 198-191 40Zm191-40L160-358l473-473q17-17 42-17t42 17l114 114q17 17 17 42t-17 42L358-160Zm317-628L233-346l113 113 442-442-113-113Z"
fill=''
/>
</svg>
News
<svg
className={`absolute right-4 top-1/2 -translate-y-1/2 fill-current ${
open && 'rotate-180'
}`}
width="20"
height="20"
viewBox="0 0 20 20"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M4.41107 6.9107C4.73651 6.58527 5.26414 6.58527 5.58958 6.9107L10.0003 11.3214L14.4111 6.91071C14.7365 6.58527 15.2641 6.58527 15.5896 6.91071C15.915 7.23614 15.915 7.76378 15.5896 8.08922L10.5896 13.0892C10.2641 13.4147 9.73651 13.4147 9.41107 13.0892L4.41107 8.08922C4.08563 7.76378 4.08563 7.23614 4.41107 6.9107Z"
fill=""
/>
</svg>
</NavLink>
{/* <!-- Dropdown Menu Start --> */}
<div
className={`translate transform overflow-hidden ${
open && 'hidden'
}`}
>
<ul className="mt-4 mb-5.5 flex flex-col gap-2.5 pl-6">
<li>
<NavLink
to="/news"
className={({ isActive }) =>
'group relative flex items-center gap-2.5 rounded-md px-4 font-medium text-bodydark2 duration-300 ease-in-out hover:text-white ' +
(isActive && '!text-white')
}
>
Add news
</NavLink>
</li>
</ul>
</div>
{/* <!-- Dropdown Menu End --> */}
</React.Fragment>
);
}}
</SidebarLinkGroup>
</ul>
</div>
</nav>
{/* <!-- Sidebar Menu --> */}
</div>
</aside>
);
};
export default Sidebar;

View File

@ -0,0 +1,16 @@
import { useState } from 'react';
const SidebarLinkGroup = ({
children,
activeCondition,
}) => {
const [open, setOpen] = useState(activeCondition);
const handleClick = () => {
setOpen(!open);
};
return <li>{children(handleClick, open)}</li>;
};
export default SidebarLinkGroup;

View File

@ -0,0 +1,5 @@
#chart {
width: '100px' !important;
height: '100px' !important;
position: 'relative' !important;
}

View File

@ -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<typeof data[number]["data"][number]>
>(
() => ({
getValue: (datum) => datum.primary as unknown as Date,
show: false,
}),
[]
);
const secondaryAxes = React.useMemo<
AxisOptions<typeof data[number]["data"][number]>[]
>(
() => [
{
getValue: (datum) => datum.secondary,
show: false,
showDatumElements: false,
},
],
[]
);
return (
<>
<Chart
options={{
data,
primaryAxis,
secondaryAxes,
}}
/>
</>
);
}

17
src/components/index.js Normal file
View File

@ -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
}

View File

@ -1,2 +1 @@
export const LOGIN = 'LOGIN';
export const LOGOUT = 'LOGOUT';

View File

@ -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"
}

View File

@ -1,6 +1,6 @@
import { createSlice, current } from "@reduxjs/toolkit";
import { createSlice } from "@reduxjs/toolkit";
const initialState = []
const initialState = {}
const authSlice = createSlice({
name: 'auth',

View File

@ -1,3 +1,9 @@
@tailwind base;
@tailwind component;
@tailwind components;
@tailwind utilities;
@layer base {
body {
@apply bg-whiten relative z-1;
}
}

View File

@ -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 (
<div className="dark:bg-boxdark-2 dark:text-bodydark">
{/* <!-- ===== Page Wrapper Start ===== --> */}
<div className="flex h-screen overflow-hidden">
{/* <!-- ===== Sidebar Start ===== --> */}
<Sidebar sidebarOpen={sidebarOpen} setSidebarOpen={setSidebarOpen} />
{/* <!-- ===== Sidebar End ===== --> */}
{/* <!-- ===== Content Area Start ===== --> */}
<div className="relative flex flex-1 flex-col overflow-y-auto overflow-x-hidden">
{/* <!-- ===== Header Start ===== --> */}
<Header sidebarOpen={sidebarOpen} setSidebarOpen={setSidebarOpen} routeName={routeName} />
{/* <!-- ===== Header End ===== --> */}
{/* <!-- ===== Main Content Start ===== --> */}
<main>
<div className="mx-auto max-w-screen-2xl p-4 md:p-6 2xl:p-10">
<Outlet />
</div>
</main>
{/* <!-- ===== Main Content End ===== --> */}
</div>
{/* <!-- ===== Content Area End ===== --> */}
</div>
{/* <!-- ===== Page Wrapper End ===== --> */}
</div>
);
};
export default DefaultLayout;

5
src/layouts/index.jsx Normal file
View File

@ -0,0 +1,5 @@
import DefaultLayout from "./DefaultLayout";
export {
DefaultLayout,
}

View File

@ -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(
<React.StrictMode>
<App />
<Router>
<App />
</Router>
</React.StrictMode>,
)

View File

@ -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;
}
}

View File

@ -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 (
<>
<h1>Dashboard</h1>
<div className='mb-6 flex flex-col-2 gap-3 flex-row justify-between'>
<div>
<h1>Dashboard</h1>
<h3>User quantity</h3>
</div>
{ auth.role_id != 2 &&
<button className='bg-primary rounded justify-center hover:bg-opacity-70 text-gray font-medium px-4 py-3 flex'>
Go to CRM
<svg xmlns="http://www.w3.org/2000/svg" className="ml-3" style={{ alignSelf: 'center' }} fill="white" height="18" viewBox="0 -960 960 960" width="18"><path d="M686-450H160v-60h526L438-758l42-42 320 320-320 320-42-42 248-248Z" /></svg>
</button>
}
</div>
<div className='grid lg:grid-cols-3 md:grid-cols-3 sm:grid-cols-2 xl:grid-cols-6 gap-5'>
<div class="flex justify-center">
<div class="bg-white rounded py-5 px-15 text-center">
<div className='bg-black justify-center py-5 px-5 rounded shadow-lg mt-10'>
<svg xmlns="http://www.w3.org/2000/svg" fill="white" height="48" viewBox="0 -960 960 960" width="48"><path d="M222-255q63-44 125-67.5T480-346q71 0 133.5 23.5T739-255q44-54 62.5-109T820-480q0-145-97.5-242.5T480-820q-145 0-242.5 97.5T140-480q0 61 19 116t63 109Zm257.814-195Q422-450 382.5-489.686q-39.5-39.686-39.5-97.5t39.686-97.314q39.686-39.5 97.5-39.5t97.314 39.686q39.5 39.686 39.5 97.5T577.314-489.5q-39.686 39.5-97.5 39.5Zm.654 370Q398-80 325-111.5q-73-31.5-127.5-86t-86-127.266Q80-397.532 80-480.266T111.5-635.5q31.5-72.5 86-127t127.266-86q72.766-31.5 155.5-31.5T635.5-848.5q72.5 31.5 127 86t86 127.032q31.5 72.532 31.5 155T848.5-325q-31.5 73-86 127.5t-127.032 86q-72.532 31.5-155 31.5ZM480-140q55 0 107.5-16T691-212q-51-36-104-55t-107-19q-54 0-107 19t-104 55q51 40 103.5 56T480-140Zm0-370q34 0 55.5-21.5T557-587q0-34-21.5-55.5T480-664q-34 0-55.5 21.5T403-587q0 34 21.5 55.5T480-510Zm0-77Zm0 374Z" /></svg>
</div>
<h1 className='mt-15'>{data.users_data.total_users}</h1>
<h1 className='mb-10'>User</h1>
</div>
</div>
<div class="flex justify-center">
<div class="bg-white rounded py-5 px-15 text-center">
<div className='bg-black justify-center py-5 px-5 rounded shadow-lg mt-10'>
<svg xmlns="http://www.w3.org/2000/svg" fill="white" height="48" viewBox="0 -960 960 960" width="48"><path d="M222-255q63-44 125-67.5T480-346q71 0 133.5 23.5T739-255q44-54 62.5-109T820-480q0-145-97.5-242.5T480-820q-145 0-242.5 97.5T140-480q0 61 19 116t63 109Zm257.814-195Q422-450 382.5-489.686q-39.5-39.686-39.5-97.5t39.686-97.314q39.686-39.5 97.5-39.5t97.314 39.686q39.5 39.686 39.5 97.5T577.314-489.5q-39.686 39.5-97.5 39.5Zm.654 370Q398-80 325-111.5q-73-31.5-127.5-86t-86-127.266Q80-397.532 80-480.266T111.5-635.5q31.5-72.5 86-127t127.266-86q72.766-31.5 155.5-31.5T635.5-848.5q72.5 31.5 127 86t86 127.032q31.5 72.532 31.5 155T848.5-325q-31.5 73-86 127.5t-127.032 86q-72.532 31.5-155 31.5ZM480-140q55 0 107.5-16T691-212q-51-36-104-55t-107-19q-54 0-107 19t-104 55q51 40 103.5 56T480-140Zm0-370q34 0 55.5-21.5T557-587q0-34-21.5-55.5T480-664q-34 0-55.5 21.5T403-587q0 34 21.5 55.5T480-510Zm0-77Zm0 374Z" /></svg>
</div>
<h1 className='mt-15'>{data.users_data.total_users}</h1>
<h1 className='mb-10'>User</h1>
</div>
</div>
<div class="flex justify-center">
<div class="bg-white rounded py-5 px-15 text-center">
<div className='bg-black justify-center py-5 px-5 rounded shadow-lg mt-10'>
<svg xmlns="http://www.w3.org/2000/svg" fill="white" height="48" viewBox="0 -960 960 960" width="48"><path d="M222-255q63-44 125-67.5T480-346q71 0 133.5 23.5T739-255q44-54 62.5-109T820-480q0-145-97.5-242.5T480-820q-145 0-242.5 97.5T140-480q0 61 19 116t63 109Zm257.814-195Q422-450 382.5-489.686q-39.5-39.686-39.5-97.5t39.686-97.314q39.686-39.5 97.5-39.5t97.314 39.686q39.5 39.686 39.5 97.5T577.314-489.5q-39.686 39.5-97.5 39.5Zm.654 370Q398-80 325-111.5q-73-31.5-127.5-86t-86-127.266Q80-397.532 80-480.266T111.5-635.5q31.5-72.5 86-127t127.266-86q72.766-31.5 155.5-31.5T635.5-848.5q72.5 31.5 127 86t86 127.032q31.5 72.532 31.5 155T848.5-325q-31.5 73-86 127.5t-127.032 86q-72.532 31.5-155 31.5ZM480-140q55 0 107.5-16T691-212q-51-36-104-55t-107-19q-54 0-107 19t-104 55q51 40 103.5 56T480-140Zm0-370q34 0 55.5-21.5T557-587q0-34-21.5-55.5T480-664q-34 0-55.5 21.5T403-587q0 34 21.5 55.5T480-510Zm0-77Zm0 374Z" /></svg>
</div>
<h1 className='mt-15'>{data.users_data.total_users}</h1>
<h1 className='mb-10'>User</h1>
</div>
</div>
<div className='justify-between flex flex-col'>
<div className='bg-white text-right px-3 py-4 box-center box' style={{ display: 'flex', justifyContent: 'space-between' }}>
<div class="bg-black rounded px-2 py-2">
<svg xmlns="http://www.w3.org/2000/svg" fill="white" height="30" viewBox="0 -960 960 960" width="30"><path d="M222-255q63-44 125-67.5T480-346q71 0 133.5 23.5T739-255q44-54 62.5-109T820-480q0-145-97.5-242.5T480-820q-145 0-242.5 97.5T140-480q0 61 19 116t63 109Zm257.814-195Q422-450 382.5-489.686q-39.5-39.686-39.5-97.5t39.686-97.314q39.686-39.5 97.5-39.5t97.314 39.686q39.5 39.686 39.5 97.5T577.314-489.5q-39.686 39.5-97.5 39.5Zm.654 370Q398-80 325-111.5q-73-31.5-127.5-86t-86-127.266Q80-397.532 80-480.266T111.5-635.5q31.5-72.5 86-127t127.266-86q72.766-31.5 155.5-31.5T635.5-848.5q72.5 31.5 127 86t86 127.032q31.5 72.532 31.5 155T848.5-325q-31.5 73-86 127.5t-127.032 86q-72.532 31.5-155 31.5ZM480-140q55 0 107.5-16T691-212q-51-36-104-55t-107-19q-54 0-107 19t-104 55q51 40 103.5 56T480-140Zm0-370q34 0 55.5-21.5T557-587q0-34-21.5-55.5T480-664q-34 0-55.5 21.5T403-587q0 34 21.5 55.5T480-510Zm0-77Zm0 374Z" /></svg>
</div>
<div>
<p>Admin office</p>
<p>4</p>
</div>
</div>
<div className='bg-white text-right px-3 py-4 box-center box' style={{ display: 'flex', justifyContent: 'space-between' }}>
<div class="bg-black rounded px-2 py-2">
<svg xmlns="http://www.w3.org/2000/svg" fill="white" height="30" viewBox="0 -960 960 960" width="30"><path d="M222-255q63-44 125-67.5T480-346q71 0 133.5 23.5T739-255q44-54 62.5-109T820-480q0-145-97.5-242.5T480-820q-145 0-242.5 97.5T140-480q0 61 19 116t63 109Zm257.814-195Q422-450 382.5-489.686q-39.5-39.686-39.5-97.5t39.686-97.314q39.686-39.5 97.5-39.5t97.314 39.686q39.5 39.686 39.5 97.5T577.314-489.5q-39.686 39.5-97.5 39.5Zm.654 370Q398-80 325-111.5q-73-31.5-127.5-86t-86-127.266Q80-397.532 80-480.266T111.5-635.5q31.5-72.5 86-127t127.266-86q72.766-31.5 155.5-31.5T635.5-848.5q72.5 31.5 127 86t86 127.032q31.5 72.532 31.5 155T848.5-325q-31.5 73-86 127.5t-127.032 86q-72.532 31.5-155 31.5ZM480-140q55 0 107.5-16T691-212q-51-36-104-55t-107-19q-54 0-107 19t-104 55q51 40 103.5 56T480-140Zm0-370q34 0 55.5-21.5T557-587q0-34-21.5-55.5T480-664q-34 0-55.5 21.5T403-587q0 34 21.5 55.5T480-510Zm0-77Zm0 374Z" /></svg>
</div>
<div >
<p>Admin office</p>
<p>4</p>
</div>
</div>
<div className='bg-white text-right px-3 py-4 box-center' style={{ display: 'flex', justifyContent: 'space-between' }}>
<div class="bg-black rounded px-2 py-2">
<svg xmlns="http://www.w3.org/2000/svg" fill="white" height="30" viewBox="0 -960 960 960" width="30"><path d="M222-255q63-44 125-67.5T480-346q71 0 133.5 23.5T739-255q44-54 62.5-109T820-480q0-145-97.5-242.5T480-820q-145 0-242.5 97.5T140-480q0 61 19 116t63 109Zm257.814-195Q422-450 382.5-489.686q-39.5-39.686-39.5-97.5t39.686-97.314q39.686-39.5 97.5-39.5t97.314 39.686q39.5 39.686 39.5 97.5T577.314-489.5q-39.686 39.5-97.5 39.5Zm.654 370Q398-80 325-111.5q-73-31.5-127.5-86t-86-127.266Q80-397.532 80-480.266T111.5-635.5q31.5-72.5 86-127t127.266-86q72.766-31.5 155.5-31.5T635.5-848.5q72.5 31.5 127 86t86 127.032q31.5 72.532 31.5 155T848.5-325q-31.5 73-86 127.5t-127.032 86q-72.532 31.5-155 31.5ZM480-140q55 0 107.5-16T691-212q-51-36-104-55t-107-19q-54 0-107 19t-104 55q51 40 103.5 56T480-140Zm0-370q34 0 55.5-21.5T557-587q0-34-21.5-55.5T480-664q-34 0-55.5 21.5T403-587q0 34 21.5 55.5T480-510Zm0-77Zm0 374Z" /></svg>
</div>
<div >
<p>Admin office</p>
<p>4</p>
</div>
</div>
</div>
<div className='justify-between flex flex-col'>
<div className='bg-white text-right px-3 py-4 box-center box' style={{ display: 'flex', justifyContent: 'space-between' }}>
<div class="bg-black rounded px-2 py-2">
<svg xmlns="http://www.w3.org/2000/svg" fill="white" height="30" viewBox="0 -960 960 960" width="30"><path d="M222-255q63-44 125-67.5T480-346q71 0 133.5 23.5T739-255q44-54 62.5-109T820-480q0-145-97.5-242.5T480-820q-145 0-242.5 97.5T140-480q0 61 19 116t63 109Zm257.814-195Q422-450 382.5-489.686q-39.5-39.686-39.5-97.5t39.686-97.314q39.686-39.5 97.5-39.5t97.314 39.686q39.5 39.686 39.5 97.5T577.314-489.5q-39.686 39.5-97.5 39.5Zm.654 370Q398-80 325-111.5q-73-31.5-127.5-86t-86-127.266Q80-397.532 80-480.266T111.5-635.5q31.5-72.5 86-127t127.266-86q72.766-31.5 155.5-31.5T635.5-848.5q72.5 31.5 127 86t86 127.032q31.5 72.532 31.5 155T848.5-325q-31.5 73-86 127.5t-127.032 86q-72.532 31.5-155 31.5ZM480-140q55 0 107.5-16T691-212q-51-36-104-55t-107-19q-54 0-107 19t-104 55q51 40 103.5 56T480-140Zm0-370q34 0 55.5-21.5T557-587q0-34-21.5-55.5T480-664q-34 0-55.5 21.5T403-587q0 34 21.5 55.5T480-510Zm0-77Zm0 374Z" /></svg>
</div>
<div>
<p>Admin office</p>
<p>4</p>
</div>
</div>
<div className='bg-white text-right px-3 py-4 box-center box' style={{ display: 'flex', justifyContent: 'space-between' }}>
<div class="bg-black rounded px-2 py-2">
<svg xmlns="http://www.w3.org/2000/svg" fill="white" height="30" viewBox="0 -960 960 960" width="30"><path d="M222-255q63-44 125-67.5T480-346q71 0 133.5 23.5T739-255q44-54 62.5-109T820-480q0-145-97.5-242.5T480-820q-145 0-242.5 97.5T140-480q0 61 19 116t63 109Zm257.814-195Q422-450 382.5-489.686q-39.5-39.686-39.5-97.5t39.686-97.314q39.686-39.5 97.5-39.5t97.314 39.686q39.5 39.686 39.5 97.5T577.314-489.5q-39.686 39.5-97.5 39.5Zm.654 370Q398-80 325-111.5q-73-31.5-127.5-86t-86-127.266Q80-397.532 80-480.266T111.5-635.5q31.5-72.5 86-127t127.266-86q72.766-31.5 155.5-31.5T635.5-848.5q72.5 31.5 127 86t86 127.032q31.5 72.532 31.5 155T848.5-325q-31.5 73-86 127.5t-127.032 86q-72.532 31.5-155 31.5ZM480-140q55 0 107.5-16T691-212q-51-36-104-55t-107-19q-54 0-107 19t-104 55q51 40 103.5 56T480-140Zm0-370q34 0 55.5-21.5T557-587q0-34-21.5-55.5T480-664q-34 0-55.5 21.5T403-587q0 34 21.5 55.5T480-510Zm0-77Zm0 374Z" /></svg>
</div>
<div >
<p>Admin office</p>
<p>4</p>
</div>
</div>
<div className='bg-white text-right px-3 py-4 box-center' style={{ display: 'flex', justifyContent: 'space-between' }}>
<div class="bg-black rounded px-2 py-2">
<svg xmlns="http://www.w3.org/2000/svg" fill="white" height="30" viewBox="0 -960 960 960" width="30"><path d="M222-255q63-44 125-67.5T480-346q71 0 133.5 23.5T739-255q44-54 62.5-109T820-480q0-145-97.5-242.5T480-820q-145 0-242.5 97.5T140-480q0 61 19 116t63 109Zm257.814-195Q422-450 382.5-489.686q-39.5-39.686-39.5-97.5t39.686-97.314q39.686-39.5 97.5-39.5t97.314 39.686q39.5 39.686 39.5 97.5T577.314-489.5q-39.686 39.5-97.5 39.5Zm.654 370Q398-80 325-111.5q-73-31.5-127.5-86t-86-127.266Q80-397.532 80-480.266T111.5-635.5q31.5-72.5 86-127t127.266-86q72.766-31.5 155.5-31.5T635.5-848.5q72.5 31.5 127 86t86 127.032q31.5 72.532 31.5 155T848.5-325q-31.5 73-86 127.5t-127.032 86q-72.532 31.5-155 31.5ZM480-140q55 0 107.5-16T691-212q-51-36-104-55t-107-19q-54 0-107 19t-104 55q51 40 103.5 56T480-140Zm0-370q34 0 55.5-21.5T557-587q0-34-21.5-55.5T480-664q-34 0-55.5 21.5T403-587q0 34 21.5 55.5T480-510Zm0-77Zm0 374Z" /></svg>
</div>
<div >
<p>Admin office</p>
<p>4</p>
</div>
</div>
</div>
<div className='justify-between flex flex-col'>
<div className='bg-white text-right px-3 py-4 box-center box' style={{ display: 'flex', justifyContent: 'space-between' }}>
<div class="bg-black rounded px-2 py-2">
<svg xmlns="http://www.w3.org/2000/svg" fill="white" height="30" viewBox="0 -960 960 960" width="30"><path d="M222-255q63-44 125-67.5T480-346q71 0 133.5 23.5T739-255q44-54 62.5-109T820-480q0-145-97.5-242.5T480-820q-145 0-242.5 97.5T140-480q0 61 19 116t63 109Zm257.814-195Q422-450 382.5-489.686q-39.5-39.686-39.5-97.5t39.686-97.314q39.686-39.5 97.5-39.5t97.314 39.686q39.5 39.686 39.5 97.5T577.314-489.5q-39.686 39.5-97.5 39.5Zm.654 370Q398-80 325-111.5q-73-31.5-127.5-86t-86-127.266Q80-397.532 80-480.266T111.5-635.5q31.5-72.5 86-127t127.266-86q72.766-31.5 155.5-31.5T635.5-848.5q72.5 31.5 127 86t86 127.032q31.5 72.532 31.5 155T848.5-325q-31.5 73-86 127.5t-127.032 86q-72.532 31.5-155 31.5ZM480-140q55 0 107.5-16T691-212q-51-36-104-55t-107-19q-54 0-107 19t-104 55q51 40 103.5 56T480-140Zm0-370q34 0 55.5-21.5T557-587q0-34-21.5-55.5T480-664q-34 0-55.5 21.5T403-587q0 34 21.5 55.5T480-510Zm0-77Zm0 374Z" /></svg>
</div>
<div>
<p>Admin office</p>
<p>4</p>
</div>
</div>
<div className='bg-white text-right px-3 py-4 box-center box' style={{ display: 'flex', justifyContent: 'space-between' }}>
<div class="bg-black rounded px-2 py-2">
<svg xmlns="http://www.w3.org/2000/svg" fill="white" height="30" viewBox="0 -960 960 960" width="30"><path d="M222-255q63-44 125-67.5T480-346q71 0 133.5 23.5T739-255q44-54 62.5-109T820-480q0-145-97.5-242.5T480-820q-145 0-242.5 97.5T140-480q0 61 19 116t63 109Zm257.814-195Q422-450 382.5-489.686q-39.5-39.686-39.5-97.5t39.686-97.314q39.686-39.5 97.5-39.5t97.314 39.686q39.5 39.686 39.5 97.5T577.314-489.5q-39.686 39.5-97.5 39.5Zm.654 370Q398-80 325-111.5q-73-31.5-127.5-86t-86-127.266Q80-397.532 80-480.266T111.5-635.5q31.5-72.5 86-127t127.266-86q72.766-31.5 155.5-31.5T635.5-848.5q72.5 31.5 127 86t86 127.032q31.5 72.532 31.5 155T848.5-325q-31.5 73-86 127.5t-127.032 86q-72.532 31.5-155 31.5ZM480-140q55 0 107.5-16T691-212q-51-36-104-55t-107-19q-54 0-107 19t-104 55q51 40 103.5 56T480-140Zm0-370q34 0 55.5-21.5T557-587q0-34-21.5-55.5T480-664q-34 0-55.5 21.5T403-587q0 34 21.5 55.5T480-510Zm0-77Zm0 374Z" /></svg>
</div>
<div >
<p>Admin office</p>
<p>4</p>
</div>
</div>
<div className='bg-white text-right px-3 py-4 box-center' style={{ display: 'flex', justifyContent: 'space-between' }}>
<div class="bg-black rounded px-2 py-2">
<svg xmlns="http://www.w3.org/2000/svg" fill="white" height="30" viewBox="0 -960 960 960" width="30"><path d="M222-255q63-44 125-67.5T480-346q71 0 133.5 23.5T739-255q44-54 62.5-109T820-480q0-145-97.5-242.5T480-820q-145 0-242.5 97.5T140-480q0 61 19 116t63 109Zm257.814-195Q422-450 382.5-489.686q-39.5-39.686-39.5-97.5t39.686-97.314q39.686-39.5 97.5-39.5t97.314 39.686q39.5 39.686 39.5 97.5T577.314-489.5q-39.686 39.5-97.5 39.5Zm.654 370Q398-80 325-111.5q-73-31.5-127.5-86t-86-127.266Q80-397.532 80-480.266T111.5-635.5q31.5-72.5 86-127t127.266-86q72.766-31.5 155.5-31.5T635.5-848.5q72.5 31.5 127 86t86 127.032q31.5 72.532 31.5 155T848.5-325q-31.5 73-86 127.5t-127.032 86q-72.532 31.5-155 31.5ZM480-140q55 0 107.5-16T691-212q-51-36-104-55t-107-19q-54 0-107 19t-104 55q51 40 103.5 56T480-140Zm0-370q34 0 55.5-21.5T557-587q0-34-21.5-55.5T480-664q-34 0-55.5 21.5T403-587q0 34 21.5 55.5T480-510Zm0-77Zm0 374Z" /></svg>
</div>
<div >
<p>Admin office</p>
<p>4</p>
</div>
</div>
</div>
</div>
<div className='mb-5 mt-15'>
<h2>Data & analytics</h2>
<p>Show updates of post</p>
</div>
{/* container */}
<div className='bg-white px-3 py-3 rounded'>
{/* 2 grid */}
<div className="grid lg:grid-cols-2 gap-10">
{/* inside 2 grid */}
<div>
{/* header */}
<div className='grid lg:grid-cols-2 py-2 gap-10' style={{ borderBottomWidth: 1, borderColor: '#ededed'}}>
<div className='flex flex-row px-2 justify-between' style={{ borderLeftWidth: 4, borderRadius: 4, borderColor: '#68c0d9'}}>
<div className='flex flex-col flex'>
<p>Belum diproses</p>
<p>9123</p>
</div>
<div style={{ width: '100px'}}>
<SparkChart />
</div>
</div>
<div className='flex flex-row justify-between px-2' style={{ borderLeftWidth: 4, borderRadius: 4, borderColor: '#f76c6b'}}>
<div className='flex flex-col flex'>
<p>Sudah diproses</p>
<p>9123</p>
</div>
<div style={{ width: '100px'}}>
<SparkChart />
</div>
</div>
</div>
<div className='pl-4'>
<ProgressBar label="Belum diproses" completed="43" bgcolor='#68c0d9'/>
<ProgressBar label="Sudah diproses" completed="37" bgcolor="#f76c6b"/>
</div>
</div>
<div>
<div className="py-2" style={{ borderBottomWidth: 1, borderColor: '#ededed'}}>
<div>
<div className='flex flex-row px-2' style={{ borderLeftWidth: 4, borderRadius: 4, borderColor: '#fcc00c'}}>
<div className='flex flex-col flex mr-10'>
<p>Customer</p>
<p>9123</p>
</div>
<div style={{ width: '150px'}}>
<SparkChart />
</div>
</div>
</div>
</div>
<div className='pl-4'>
<ProgressBar label="Pria" completed="43" bgcolor='#fcc00c'/>
<ProgressBar label="Wanita diproses" completed="37" bgcolor="#fcc00c"/>
</div>
</div>
</div>
</div>
</>
)
}

View File

@ -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 (
<>
<div className="flex min-h-full flex-1 flex-col justify-center px-6 py-12 lg:px-8">
@ -95,7 +104,7 @@ const Login = () => {
</div>
<div>
<button
className="flex w-full justify-center rounded-md bg-indigo-600 px-3 py-1.5 text-sm font-semibold leading-6 text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
className="flex w-full justify-center rounded-md bg-primary px-3 py-1.5 text-sm font-semibold leading-6 text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
type="submit"
>
Sign in

58
src/pages/News/index.jsx Normal file
View File

@ -0,0 +1,58 @@
import { useMemo } from 'react';
import { DataTable } from '../../components';
import data from '../../datas/articles.json';
import moment from 'moment';
const News = () => {
let roleTemp = [];
data.map(({ type }) => {
roleTemp.push(type)
})
const proccessed_data = data.map((obj, i) => ({ ...obj, publish_date: moment(obj.publish_date).format('DD MMMM YYYY')}))
const type_set = new Set([...roleTemp]);
const type = [...type_set];
const columns = useMemo(
() => [
{
Header: () => <span>No</span>,
id: 'rowNumber',
width: 30,
},
{
Header: "Type",
accessor: 'type'
},
{
Header: "Title",
accessor: 'title'
},
{
Header: "Publish",
accessor: 'publish_date'
},
]
)
return (
<>
<div className="flex flex-row justify-end">
<button className='bg-primary rounded justify-center hover:bg-opacity-70 text-gray font-medium px-4 py-3 flex'>
<svg xmlns="http://www.w3.org/2000/svg" className="mr-3" style={{ alignSelf: 'center' }} fill="white" height="18" viewBox="0 -960 960 960" width="18"><path d="M730-400v-130H600v-60h130v-130h60v130h130v60H790v130h-60Zm-370-81q-66 0-108-42t-42-108q0-66 42-108t108-42q66 0 108 42t42 108q0 66-42 108t-108 42ZM40-160v-94q0-35 17.5-63.5T108-360q75-33 133.338-46.5 58.339-13.5 118.5-13.5Q420-420 478-406.5 536-393 611-360q33 15 51 43t18 63v94H40Zm60-60h520v-34q0-16-9-30.5T587-306q-71-33-120-43.5T360-360q-58 0-107.5 10.5T132-306q-15 7-23.5 21.5T100-254v34Zm260-321q39 0 64.5-25.5T450-631q0-39-25.5-64.5T360-721q-39 0-64.5 25.5T270-631q0 39 25.5 64.5T360-541Zm0-90Zm0 411Z" /></svg>
Create News
</button>
</div>
<div className="p-4 bg-white mt-8 rounded">
<div className='mt-4'>
<DataTable columns={columns} data={proccessed_data} tabFilterData={type} />
</div>
</div>
</>
)
}
export default News;

58
src/pages/User/index.jsx Normal file
View File

@ -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: () => <span>No</span>,
id: 'rowNumber',
width: 30,
},
{
Header: "Name",
accessor: 'name'
},
{
Header: "Phone",
accessor: 'phone'
},
{
Header: "Email",
accessor: 'email'
},
{
Header: "Role",
accessor: "role_name"
}
]
)
return (
<>
<div className="flex flex-row justify-end">
<button className='bg-primary rounded justify-center hover:bg-opacity-70 text-gray font-medium px-4 py-3 flex'>
<svg xmlns="http://www.w3.org/2000/svg" className="mr-3" style={{ alignSelf: 'center' }} fill="white" height="18" viewBox="0 -960 960 960" width="18"><path d="M730-400v-130H600v-60h130v-130h60v130h130v60H790v130h-60Zm-370-81q-66 0-108-42t-42-108q0-66 42-108t108-42q66 0 108 42t42 108q0 66-42 108t-108 42ZM40-160v-94q0-35 17.5-63.5T108-360q75-33 133.338-46.5 58.339-13.5 118.5-13.5Q420-420 478-406.5 536-393 611-360q33 15 51 43t18 63v94H40Zm60-60h520v-34q0-16-9-30.5T587-306q-71-33-120-43.5T360-360q-58 0-107.5 10.5T132-306q-15 7-23.5 21.5T100-254v34Zm260-321q39 0 64.5-25.5T450-631q0-39-25.5-64.5T360-721q-39 0-64.5 25.5T270-631q0 39 25.5 64.5T360-541Zm0-90Zm0 411Z" /></svg>
Add User
</button>
</div>
<div className="p-4 bg-white mt-8 rounded">
<div className='mt-4'>
<DataTable columns={columns} data={data} tabFilterData={role} />
</div>
</div>
</>
)
}
export default User;

View File

@ -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
}

View File

@ -1,18 +1,24 @@
import {
Dashboard,
Login,
Notfound404
User,
News
} from "../pages";
const routes = [
{
path: "/",
element: <Login />,
errorElement: <Notfound404 />
path: "/dashboard",
name: "Dashboard",
element: <Dashboard />
},
{
path: "/dashboard",
element: <Dashboard />
path: "/users",
name: "Users",
element: <User />
},
{
path: "/news",
name: "News",
element: <News />
}
]

7
src/utils/common.js Normal file
View File

@ -0,0 +1,7 @@
export const getRouteName = (pathName) => {
return pathName.replace(/\/(.)/, (_, firstLetter) => firstLetter.toUpperCase());
}
export const classNames = (...classes) => {
return classes.filter(Boolean).join(" ");
}

6
src/utils/index.js Normal file
View File

@ -0,0 +1,6 @@
import { getRouteName, classNames} from './common';
export {
getRouteName,
classNames,
}

View File

@ -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')
],
}

196
yarn.lock
View File

@ -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"