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) => (
+ {column.render("Header")} |
+ ))}
+ Action |
+
+ ))}
+
+
+ {row_data.map((row, i) => {
+ prepareRow(row);
+ return (
+
+ {row.cells.map((cell) => {
+ return (
+
+ {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 (
+
+ );
+};
+
+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
+
+
+
+
+
+
+
+
Data & analytics
+
Show updates of post
+
+ {/* container */}
+
+ {/* 2 grid */}
+
+ {/* inside 2 grid */}
+
+ {/* header */}
+
+
+
+
Belum diproses
+
9123
+
+
+
+
+
+
+
+
Sudah diproses
+
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 = () => {