├── backend ├── public │ └── .gitkeep ├── .env.sample ├── api │ └── index.ts ├── vercel.json ├── src │ ├── interfaces │ │ ├── MessageResponse.ts │ │ └── ErrorResponse.ts │ ├── index.ts │ ├── api │ │ ├── emojis.ts │ │ └── index.ts │ ├── data │ │ ├── totalSource.ts │ │ ├── totalRatio.ts │ │ ├── totalUsers.ts │ │ ├── totalRevenue.ts │ │ ├── totalProducts.ts │ │ ├── totalVisit.ts │ │ ├── totalProfit.ts │ │ ├── totalRevenueByProducts.ts │ │ ├── topDeals.ts │ │ ├── products.ts │ │ ├── logs.ts │ │ ├── notes.ts │ │ ├── users.ts │ │ └── orders.ts │ ├── middlewares.ts │ └── app.ts ├── jest.config.js ├── tsconfig.json ├── test │ ├── app.test.ts │ └── api.test.ts ├── .eslintrc ├── LICENSE ├── .gitignore ├── package.json └── README.md ├── frontend ├── src │ ├── vite-env.d.ts │ ├── components │ │ ├── ToasterProvider.tsx │ │ ├── menu │ │ │ ├── Menu.tsx │ │ │ ├── MenuItem.tsx │ │ │ └── data.ts │ │ ├── Footer.tsx │ │ ├── topDealsBox │ │ │ ├── data.ts │ │ │ └── TopDealsBox.tsx │ │ ├── ChangesThemes.tsx │ │ ├── calendar │ │ │ ├── CalendarUtils.tsx │ │ │ └── CalendarBox.tsx │ │ ├── notes │ │ │ ├── NoteCard.tsx │ │ │ └── data.ts │ │ ├── DataTable.tsx │ │ ├── charts │ │ │ └── data.ts │ │ └── Navbar.tsx │ ├── contexts │ │ ├── ThemeContext.tsx │ │ ├── useTheme.ts │ │ └── ThemeProvider.tsx │ ├── main.tsx │ ├── pages │ │ ├── Calendar.tsx │ │ ├── Error.tsx │ │ ├── Logs.tsx │ │ ├── Products.tsx │ │ ├── Users.tsx │ │ ├── Home.tsx │ │ ├── Login.tsx │ │ ├── Charts.tsx │ │ ├── Orders.tsx │ │ ├── Posts.tsx │ │ ├── Product.tsx │ │ ├── Profile.tsx │ │ ├── User.tsx │ │ └── Notes.tsx │ ├── index.css │ ├── App.tsx │ ├── assets │ │ └── react.svg │ └── api │ │ └── ApiCollection.tsx ├── vercel.json ├── public │ ├── SEO-vector.png │ ├── corrugated-box.jpg │ ├── e-commerce-vector.png │ ├── marketing-vector.png │ ├── Portrait_Placeholder.png │ ├── communication-vector.png │ ├── productivity-vector.png │ ├── social-media-vector.png │ ├── icons8-microsoft.svg │ ├── React-icon.svg │ ├── icons8-google.svg │ ├── icons8-apple-black.svg │ ├── icons8-apple-white.svg │ └── vite.svg ├── postcss.config.js ├── vite.config.ts ├── tsconfig.node.json ├── .gitignore ├── index.html ├── tailwind.config.js ├── .eslintrc.cjs ├── tsconfig.json ├── README.md └── package.json ├── logo.jpg ├── preview-optimized.gif ├── LICENSE └── README.md /backend/public/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /backend/.env.sample: -------------------------------------------------------------------------------- 1 | NODE_ENV=development -------------------------------------------------------------------------------- /frontend/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /backend/api/index.ts: -------------------------------------------------------------------------------- 1 | import app from '../src/app'; 2 | 3 | export default app; -------------------------------------------------------------------------------- /logo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fransachmadhw/React-Admin-UI-V1/HEAD/logo.jpg -------------------------------------------------------------------------------- /backend/vercel.json: -------------------------------------------------------------------------------- 1 | { 2 | "rewrites": [{ "source": "/(.*)", "destination": "/api" }] 3 | } -------------------------------------------------------------------------------- /frontend/vercel.json: -------------------------------------------------------------------------------- 1 | { 2 | "rewrites": [{ "source": "/(.*)", "destination": "/" }] 3 | } 4 | -------------------------------------------------------------------------------- /preview-optimized.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fransachmadhw/React-Admin-UI-V1/HEAD/preview-optimized.gif -------------------------------------------------------------------------------- /backend/src/interfaces/MessageResponse.ts: -------------------------------------------------------------------------------- 1 | export default interface MessageResponse { 2 | message: string; 3 | } 4 | -------------------------------------------------------------------------------- /frontend/public/SEO-vector.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fransachmadhw/React-Admin-UI-V1/HEAD/frontend/public/SEO-vector.png -------------------------------------------------------------------------------- /frontend/postcss.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /frontend/public/corrugated-box.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fransachmadhw/React-Admin-UI-V1/HEAD/frontend/public/corrugated-box.jpg -------------------------------------------------------------------------------- /frontend/public/e-commerce-vector.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fransachmadhw/React-Admin-UI-V1/HEAD/frontend/public/e-commerce-vector.png -------------------------------------------------------------------------------- /frontend/public/marketing-vector.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fransachmadhw/React-Admin-UI-V1/HEAD/frontend/public/marketing-vector.png -------------------------------------------------------------------------------- /frontend/public/Portrait_Placeholder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fransachmadhw/React-Admin-UI-V1/HEAD/frontend/public/Portrait_Placeholder.png -------------------------------------------------------------------------------- /frontend/public/communication-vector.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fransachmadhw/React-Admin-UI-V1/HEAD/frontend/public/communication-vector.png -------------------------------------------------------------------------------- /frontend/public/productivity-vector.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fransachmadhw/React-Admin-UI-V1/HEAD/frontend/public/productivity-vector.png -------------------------------------------------------------------------------- /frontend/public/social-media-vector.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fransachmadhw/React-Admin-UI-V1/HEAD/frontend/public/social-media-vector.png -------------------------------------------------------------------------------- /backend/src/interfaces/ErrorResponse.ts: -------------------------------------------------------------------------------- 1 | import MessageResponse from './MessageResponse'; 2 | 3 | export default interface ErrorResponse extends MessageResponse { 4 | stack?: string; 5 | } -------------------------------------------------------------------------------- /frontend/src/components/ToasterProvider.tsx: -------------------------------------------------------------------------------- 1 | import { Toaster } from 'react-hot-toast'; 2 | 3 | const ToasterProvider = () => { 4 | return ; 5 | }; 6 | 7 | export default ToasterProvider; 8 | -------------------------------------------------------------------------------- /frontend/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react-swc' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()], 7 | }) 8 | -------------------------------------------------------------------------------- /backend/jest.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */ 2 | module.exports = { 3 | preset: 'ts-jest', 4 | testEnvironment: 'node', 5 | modulePathIgnorePatterns: ['/dist/'], 6 | }; -------------------------------------------------------------------------------- /backend/src/index.ts: -------------------------------------------------------------------------------- 1 | import app from './app'; 2 | 3 | const port = process.env.PORT || 5000; 4 | app.listen(port, () => { 5 | /* eslint-disable no-console */ 6 | console.log(`Listening: http://localhost:${port}`); 7 | /* eslint-enable no-console */ 8 | }); 9 | -------------------------------------------------------------------------------- /backend/src/api/emojis.ts: -------------------------------------------------------------------------------- 1 | import express from 'express'; 2 | 3 | const router = express.Router(); 4 | 5 | type EmojiResponse = string[]; 6 | 7 | router.get<{}, EmojiResponse>('/', (req, res) => { 8 | res.json(['😀', '😳', '🙄']); 9 | }); 10 | 11 | export default router; 12 | -------------------------------------------------------------------------------- /frontend/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "skipLibCheck": true, 5 | "module": "ESNext", 6 | "moduleResolution": "bundler", 7 | "allowSyntheticDefaultImports": true 8 | }, 9 | "include": ["vite.config.ts"] 10 | } 11 | -------------------------------------------------------------------------------- /frontend/src/contexts/ThemeContext.tsx: -------------------------------------------------------------------------------- 1 | import { createContext } from 'react'; 2 | 3 | interface ThemeContextType { 4 | theme: string; 5 | toggleTheme: () => void; 6 | } 7 | 8 | const ThemeContext = createContext( 9 | undefined 10 | ); 11 | 12 | export default ThemeContext; 13 | -------------------------------------------------------------------------------- /frontend/src/contexts/useTheme.ts: -------------------------------------------------------------------------------- 1 | import { useContext } from 'react'; 2 | import ThemeContext from './ThemeContext'; 3 | 4 | const useTheme = () => { 5 | const context = useContext(ThemeContext); 6 | if (!context) { 7 | throw new Error('useTheme must be used within a ThemeProvider'); 8 | } 9 | return context; 10 | }; 11 | 12 | export default useTheme; 13 | -------------------------------------------------------------------------------- /frontend/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /frontend/public/icons8-microsoft.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /backend/src/api/index.ts: -------------------------------------------------------------------------------- 1 | import express from 'express'; 2 | 3 | import MessageResponse from '../interfaces/MessageResponse'; 4 | import emojis from './emojis'; 5 | 6 | const router = express.Router(); 7 | 8 | router.get<{}, MessageResponse>('/', (req, res) => { 9 | res.json({ 10 | message: 'API - 👋🌎🌍🌏', 11 | }); 12 | }); 13 | 14 | router.use('/emojis', emojis); 15 | 16 | export default router; 17 | -------------------------------------------------------------------------------- /backend/src/data/totalSource.ts: -------------------------------------------------------------------------------- 1 | const totalSource = { 2 | title: 'Leads by Source', 3 | dataKey: 'value', 4 | chartPieData: [ 5 | { name: 'Mobile', value: 350, color: '#0088FE' }, 6 | { name: 'Desktop', value: 250, color: '#00C49F' }, 7 | { name: 'Laptop', value: 325, color: '#FFBB28' }, 8 | { name: 'Tablet', value: 75, color: '#FF8042' }, 9 | ], 10 | }; 11 | 12 | export default totalSource; 13 | -------------------------------------------------------------------------------- /frontend/public/React-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | React Logo 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /backend/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "dist", 4 | "sourceMap": true, 5 | "target": "esnext", 6 | "module": "commonjs", 7 | "esModuleInterop": true, 8 | "forceConsistentCasingInFileNames": true, 9 | "noImplicitAny": true, 10 | "strict": true, 11 | "skipLibCheck": true 12 | }, 13 | "include": [ 14 | "./api/*.ts", 15 | "./*.js", 16 | "src/**/*.ts", 17 | "test/**/*.ts", 18 | ], 19 | } 20 | -------------------------------------------------------------------------------- /frontend/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 10 | React Dashboard 11 | 12 | 13 |
14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /backend/src/data/totalRatio.ts: -------------------------------------------------------------------------------- 1 | const totalRatio = { 2 | color: 'gold', 3 | title: 'Total Ratio', 4 | number: '2.6', 5 | dataKey: 'ratio', 6 | percentage: 12, 7 | chartData: [ 8 | { name: 'Sun', ratio: 400 }, 9 | { name: 'Mon', ratio: 600 }, 10 | { name: 'Tue', ratio: 500 }, 11 | { name: 'Wed', ratio: 700 }, 12 | { name: 'Thu', ratio: 400 }, 13 | { name: 'Fri', ratio: 500 }, 14 | { name: 'Sat', ratio: 450 }, 15 | ], 16 | }; 17 | 18 | export default totalRatio; 19 | -------------------------------------------------------------------------------- /backend/src/data/totalUsers.ts: -------------------------------------------------------------------------------- 1 | const totalUsers = { 2 | color: '#8884d8', 3 | title: 'Total Users', 4 | number: '11.238', 5 | dataKey: 'users', 6 | percentage: 45, 7 | chartData: [ 8 | { name: 'Sun', users: 400 }, 9 | { name: 'Mon', users: 600 }, 10 | { name: 'Tue', users: 500 }, 11 | { name: 'Wed', users: 700 }, 12 | { name: 'Thu', users: 400 }, 13 | { name: 'Fri', users: 500 }, 14 | { name: 'Sat', users: 450 }, 15 | ], 16 | }; 17 | 18 | export default totalUsers; 19 | -------------------------------------------------------------------------------- /frontend/tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | export default { 3 | content: ['./index.html', './src/**/*.{js,ts,jsx,tsx}'], 4 | theme: { 5 | extend: { 6 | animation: { 7 | 'spin-slow': 'spin 7s linear infinite', 8 | }, 9 | screens: { 10 | '3xl': '2200px', 11 | }, 12 | }, 13 | }, 14 | plugins: [require('daisyui')], 15 | daisyui: { 16 | themes: ['light', 'dark'], 17 | }, 18 | darkMode: ['class', '[data-theme="dark"]'], 19 | }; 20 | -------------------------------------------------------------------------------- /backend/src/data/totalRevenue.ts: -------------------------------------------------------------------------------- 1 | const totalRevenue = { 2 | color: 'teal', 3 | title: 'Total Revenue', 4 | number: '$56.432', 5 | dataKey: 'revenue', 6 | percentage: -12, 7 | chartData: [ 8 | { name: 'Sun', revenue: 400 }, 9 | { name: 'Mon', revenue: 600 }, 10 | { name: 'Tue', revenue: 500 }, 11 | { name: 'Wed', revenue: 700 }, 12 | { name: 'Thu', revenue: 400 }, 13 | { name: 'Fri', revenue: 500 }, 14 | { name: 'Sat', revenue: 450 }, 15 | ], 16 | }; 17 | 18 | export default totalRevenue; 19 | -------------------------------------------------------------------------------- /frontend/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { browser: true, es2020: true }, 4 | extends: [ 5 | 'eslint:recommended', 6 | 'plugin:@typescript-eslint/recommended', 7 | 'plugin:react-hooks/recommended', 8 | ], 9 | ignorePatterns: ['dist', '.eslintrc.cjs'], 10 | parser: '@typescript-eslint/parser', 11 | plugins: ['react-refresh'], 12 | rules: { 13 | 'react-refresh/only-export-components': [ 14 | 'warn', 15 | { allowConstantExport: true }, 16 | ], 17 | }, 18 | } 19 | -------------------------------------------------------------------------------- /backend/src/data/totalProducts.ts: -------------------------------------------------------------------------------- 1 | const totalProducts = { 2 | color: 'skyblue', 3 | title: 'Total Products', 4 | number: '238', 5 | dataKey: 'products', 6 | percentage: 21, 7 | chartData: [ 8 | { name: 'Sun', products: 400 }, 9 | { name: 'Mon', products: 600 }, 10 | { name: 'Tue', products: 500 }, 11 | { name: 'Wed', products: 700 }, 12 | { name: 'Thu', products: 400 }, 13 | { name: 'Fri', products: 500 }, 14 | { name: 'Sat', products: 450 }, 15 | ], 16 | }; 17 | 18 | export default totalProducts; 19 | -------------------------------------------------------------------------------- /frontend/src/components/menu/Menu.tsx: -------------------------------------------------------------------------------- 1 | // import React from 'react'; 2 | import { menu } from './data'; 3 | import MenuItem from './MenuItem'; 4 | 5 | const Menu = () => { 6 | return ( 7 |
8 |
9 | {menu.map((item, index) => ( 10 | 15 | ))} 16 |
17 |
18 | ); 19 | }; 20 | 21 | export default Menu; 22 | -------------------------------------------------------------------------------- /frontend/src/main.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom/client'; 3 | import App from './App.tsx'; 4 | import './index.css'; 5 | import { 6 | QueryClient, 7 | QueryClientProvider, 8 | } from '@tanstack/react-query'; 9 | import ThemeProvider from './contexts/ThemeProvider.tsx'; 10 | 11 | const queryClient = new QueryClient(); 12 | 13 | ReactDOM.createRoot(document.getElementById('root')!).render( 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | ); 22 | -------------------------------------------------------------------------------- /frontend/src/components/Footer.tsx: -------------------------------------------------------------------------------- 1 | // import React from 'react'; 2 | import { DiReact } from 'react-icons/di'; 3 | 4 | const Footer = () => { 5 | return ( 6 |
7 | 8 | React Dashboard 9 | 10 |
11 | © Frans Admin Dashboard 12 | 13 |
14 |
15 | ); 16 | }; 17 | 18 | export default Footer; 19 | -------------------------------------------------------------------------------- /backend/src/data/totalVisit.ts: -------------------------------------------------------------------------------- 1 | const totalVisit = { 2 | title: 'Total Visit', 3 | color: '#FF8042', 4 | dataKey: 'visit', 5 | chartData: [ 6 | { 7 | name: 'Sun', 8 | visit: 4000, 9 | }, 10 | { 11 | name: 'Mon', 12 | visit: 3000, 13 | }, 14 | { 15 | name: 'Tue', 16 | visit: 2000, 17 | }, 18 | { 19 | name: 'Wed', 20 | visit: 2780, 21 | }, 22 | { 23 | name: 'Thu', 24 | visit: 1890, 25 | }, 26 | { 27 | name: 'Fri', 28 | visit: 2390, 29 | }, 30 | { 31 | name: 'Sat', 32 | visit: 3490, 33 | }, 34 | ], 35 | }; 36 | 37 | export default totalVisit; 38 | -------------------------------------------------------------------------------- /backend/src/data/totalProfit.ts: -------------------------------------------------------------------------------- 1 | const totalProfit = { 2 | title: 'Profit Earned', 3 | color: '#8884d8', 4 | dataKey: 'profit', 5 | chartData: [ 6 | { 7 | name: 'Sun', 8 | profit: 4000, 9 | }, 10 | { 11 | name: 'Mon', 12 | profit: 3000, 13 | }, 14 | { 15 | name: 'Tue', 16 | profit: 2000, 17 | }, 18 | { 19 | name: 'Wed', 20 | profit: 2780, 21 | }, 22 | { 23 | name: 'Thu', 24 | profit: 1890, 25 | }, 26 | { 27 | name: 'Fri', 28 | profit: 2390, 29 | }, 30 | { 31 | name: 'Sat', 32 | profit: 3490, 33 | }, 34 | ], 35 | }; 36 | 37 | export default totalProfit; 38 | -------------------------------------------------------------------------------- /backend/test/app.test.ts: -------------------------------------------------------------------------------- 1 | import request from 'supertest'; 2 | 3 | import app from '../src/app'; 4 | 5 | describe('app', () => { 6 | it('responds with a not found message', (done) => { 7 | request(app) 8 | .get('/what-is-this-even') 9 | .set('Accept', 'application/json') 10 | .expect('Content-Type', /json/) 11 | .expect(404, done); 12 | }); 13 | }); 14 | 15 | describe('GET /', () => { 16 | it('responds with a json message', (done) => { 17 | request(app) 18 | .get('/') 19 | .set('Accept', 'application/json') 20 | .expect('Content-Type', /json/) 21 | .expect(200, { 22 | message: '🦄🌈✨👋🌎🌍🌏✨🌈🦄', 23 | }, done); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /frontend/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "useDefineForClassFields": true, 5 | "lib": ["ES2020", "DOM", "DOM.Iterable"], 6 | "module": "ESNext", 7 | "skipLibCheck": true, 8 | 9 | /* Bundler mode */ 10 | "moduleResolution": "bundler", 11 | "allowImportingTsExtensions": true, 12 | "resolveJsonModule": true, 13 | "isolatedModules": true, 14 | "noEmit": true, 15 | "jsx": "react-jsx", 16 | 17 | /* Linting */ 18 | "strict": true, 19 | "noUnusedLocals": true, 20 | "noUnusedParameters": true, 21 | "noFallthroughCasesInSwitch": true 22 | }, 23 | "include": ["src"], 24 | "references": [{ "path": "./tsconfig.node.json" }] 25 | } 26 | -------------------------------------------------------------------------------- /backend/test/api.test.ts: -------------------------------------------------------------------------------- 1 | import request from 'supertest'; 2 | 3 | import app from '../src/app'; 4 | 5 | describe('GET /api/v1', () => { 6 | it('responds with a json message', (done) => { 7 | request(app) 8 | .get('/api/v1') 9 | .set('Accept', 'application/json') 10 | .expect('Content-Type', /json/) 11 | .expect(200, { 12 | message: 'API - 👋🌎🌍🌏', 13 | }, done); 14 | }); 15 | }); 16 | 17 | describe('GET /api/v1/emojis', () => { 18 | it('responds with a json message', (done) => { 19 | request(app) 20 | .get('/api/v1/emojis') 21 | .set('Accept', 'application/json') 22 | .expect('Content-Type', /json/) 23 | .expect(200, ['😀', '😳', '🙄'], done); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /frontend/src/pages/Calendar.tsx: -------------------------------------------------------------------------------- 1 | // import React from 'react'; 2 | 3 | import CalendarBox from '../components/calendar/CalendarBox'; 4 | 5 | const Calendar = () => { 6 | return ( 7 | // screen 8 |
9 | {/* container */} 10 |
11 | {/* heading */} 12 |

13 | Calendar 14 |

15 | {/* calendar container */} 16 |
17 | 18 |
19 |
20 |
21 | ); 22 | }; 23 | 24 | export default Calendar; 25 | -------------------------------------------------------------------------------- /backend/src/middlewares.ts: -------------------------------------------------------------------------------- 1 | import { NextFunction, Request, Response } from 'express'; 2 | 3 | import ErrorResponse from './interfaces/ErrorResponse'; 4 | 5 | export function notFound(req: Request, res: Response, next: NextFunction) { 6 | res.status(404); 7 | const error = new Error(`🔍 - Not Found - ${req.originalUrl}`); 8 | next(error); 9 | } 10 | 11 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 12 | export function errorHandler(err: Error, req: Request, res: Response, next: NextFunction) { 13 | const statusCode = res.statusCode !== 200 ? res.statusCode : 500; 14 | res.status(statusCode); 15 | res.json({ 16 | message: err.message, 17 | stack: process.env.NODE_ENV === 'production' ? '🥞' : err.stack, 18 | }); 19 | } 20 | -------------------------------------------------------------------------------- /frontend/src/contexts/ThemeProvider.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react'; 2 | import ThemeContext from './ThemeContext'; 3 | 4 | interface ThemeProviderProps { 5 | children: React.ReactNode; 6 | } 7 | 8 | const ThemeProvider: React.FC = ({ 9 | children, 10 | }) => { 11 | const [theme, setTheme] = useState('light'); 12 | 13 | const toggleTheme = () => { 14 | setTheme(theme === 'dark' ? 'light' : 'dark'); 15 | }; 16 | 17 | useEffect(() => { 18 | document.querySelector('html')?.setAttribute('data-theme', theme); 19 | }, [theme]); 20 | 21 | return ( 22 | 23 | {children} 24 | 25 | ); 26 | }; 27 | 28 | export default ThemeProvider; 29 | -------------------------------------------------------------------------------- /frontend/src/pages/Error.tsx: -------------------------------------------------------------------------------- 1 | import { useNavigate } from 'react-router-dom'; 2 | import { HiOutlineHome } from 'react-icons/hi2'; 3 | 4 | const Error = () => { 5 | const navigate = useNavigate(); 6 | 7 | return ( 8 |
9 |
10 |
11 |

12 | 🐔 Boom! Are you lost? 🙄 13 |

14 | 18 |
19 |
20 |
21 | ); 22 | }; 23 | 24 | export default Error; 25 | -------------------------------------------------------------------------------- /backend/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "env": { 4 | "jest": true 5 | }, 6 | "parser": "@typescript-eslint/parser", 7 | "parserOptions": { 8 | "project": [ 9 | "./tsconfig.json" 10 | ] 11 | }, 12 | "extends": "airbnb-typescript/base", 13 | "plugins": [ 14 | "import", 15 | "@typescript-eslint" 16 | ], 17 | "rules": { 18 | "comma-dangle": 0, 19 | "no-underscore-dangle": 0, 20 | "no-param-reassign": 0, 21 | "no-return-assign": 0, 22 | "camelcase": 0, 23 | "import/extensions": 0, 24 | "@typescript-eslint/no-redeclare": 0 25 | }, 26 | "settings": { 27 | "import/parsers": { 28 | "@typescript-eslint/parser": [ 29 | ".ts", 30 | ".tsx" 31 | ] 32 | }, 33 | "import/resolver": { 34 | "typescript": {} 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /frontend/public/icons8-google.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /frontend/public/icons8-apple-black.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /backend/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License Copyright (c) 2020 CJ R. 2 | 3 | Permission is hereby granted, free 4 | of charge, to any person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the Software without 6 | restriction, including without limitation the rights to use, copy, modify, merge, 7 | publish, distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to the 9 | following conditions: 10 | 11 | The above copyright notice and this permission notice 12 | (including the next paragraph) shall be included in all copies or substantial 13 | portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 16 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO 18 | EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR 19 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /frontend/public/icons8-apple-white.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /backend/src/data/totalRevenueByProducts.ts: -------------------------------------------------------------------------------- 1 | const totalRevenueByProducts = { 2 | title: 'Revenue by Products', 3 | dataKey: 'name', 4 | chartAreaData: [ 5 | { 6 | name: 'Sun', 7 | smartphones: 4000, 8 | consoles: 2400, 9 | laptops: 2400, 10 | others: 1000, 11 | }, 12 | { 13 | name: 'Mon', 14 | smartphones: 3000, 15 | consoles: 1398, 16 | laptops: 2210, 17 | others: 700, 18 | }, 19 | { 20 | name: 'Tue', 21 | smartphones: 2000, 22 | consoles: 9800, 23 | laptops: 2290, 24 | others: 675, 25 | }, 26 | { 27 | name: 'Wed', 28 | smartphones: 2780, 29 | consoles: 3908, 30 | laptops: 2000, 31 | others: 685, 32 | }, 33 | { 34 | name: 'Thu', 35 | smartphones: 1890, 36 | consoles: 4800, 37 | laptops: 2181, 38 | others: 675, 39 | }, 40 | { 41 | name: 'Fri', 42 | smartphones: 2390, 43 | consoles: 3800, 44 | laptops: 2500, 45 | others: 650, 46 | }, 47 | { 48 | name: 'Sat', 49 | smartphones: 3490, 50 | consoles: 4300, 51 | laptops: 2100, 52 | others: 1075, 53 | }, 54 | ], 55 | }; 56 | 57 | export default totalRevenueByProducts; 58 | -------------------------------------------------------------------------------- /backend/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (https://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # TypeScript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | 60 | # parcel-bundler cache (https://parceljs.org/) 61 | .cache 62 | 63 | # next.js build output 64 | .next 65 | 66 | # nuxt.js build output 67 | .nuxt 68 | 69 | # vuepress build output 70 | .vuepress/dist 71 | 72 | # Serverless directories 73 | .serverless 74 | 75 | dist -------------------------------------------------------------------------------- /frontend/README.md: -------------------------------------------------------------------------------- 1 | # React + TypeScript + Vite 2 | 3 | This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. 4 | 5 | Currently, two official plugins are available: 6 | 7 | - [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh 8 | - [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh 9 | 10 | ## Expanding the ESLint configuration 11 | 12 | If you are developing a production application, we recommend updating the configuration to enable type aware lint rules: 13 | 14 | - Configure the top-level `parserOptions` property like this: 15 | 16 | ```js 17 | export default { 18 | // other rules... 19 | parserOptions: { 20 | ecmaVersion: 'latest', 21 | sourceType: 'module', 22 | project: ['./tsconfig.json', './tsconfig.node.json'], 23 | tsconfigRootDir: __dirname, 24 | }, 25 | } 26 | ``` 27 | 28 | - Replace `plugin:@typescript-eslint/recommended` to `plugin:@typescript-eslint/recommended-type-checked` or `plugin:@typescript-eslint/strict-type-checked` 29 | - Optionally add `plugin:@typescript-eslint/stylistic-type-checked` 30 | - Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and add `plugin:react/recommended` & `plugin:react/jsx-runtime` to the `extends` list 31 | -------------------------------------------------------------------------------- /frontend/public/vite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /backend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react_admin_ui_v1_api", 3 | "version": "1.2.0", 4 | "description": "React Admin Dashboard API", 5 | "main": "src/index.ts", 6 | "scripts": { 7 | "start": "ts-node src/index.ts", 8 | "dev": "nodemon src/index.ts", 9 | "build": "tsc", 10 | "lint": "eslint --fix src test", 11 | "test": "jest", 12 | "typecheck": "tsc --noEmit", 13 | "vercel-build": "echo hello" 14 | }, 15 | "keywords": [], 16 | "author": "Frans", 17 | "repository": { 18 | "type": "git", 19 | "url": "https://github.com/fransachmadhw/react_admin_ui_v1.git" 20 | }, 21 | "license": "MIT", 22 | "dependencies": { 23 | "cors": "^2.8.5", 24 | "dotenv": "^16.3.1", 25 | "express": "^4.18.2", 26 | "helmet": "^7.1.0", 27 | "morgan": "^1.10.0" 28 | }, 29 | "devDependencies": { 30 | "@types/cors": "^2.8.17", 31 | "@types/express": "^4.17.21", 32 | "@types/jest": "^29.5.10", 33 | "@types/morgan": "^1.9.9", 34 | "@types/node": "^20.10.0", 35 | "@types/supertest": "^2.0.16", 36 | "@typescript-eslint/eslint-plugin": "^6.13.1", 37 | "@typescript-eslint/parser": "^6.13.1", 38 | "eslint": "^8.54.0", 39 | "eslint-config-airbnb-typescript": "^17.1.0", 40 | "eslint-import-resolver-typescript": "^3.6.1", 41 | "eslint-plugin-import": "^2.29.0", 42 | "jest": "^29.7.0", 43 | "nodemon": "^3.0.1", 44 | "supertest": "^6.3.3", 45 | "ts-jest": "^29.1.1", 46 | "ts-node": "^10.9.1", 47 | "typescript": "^5.3.2" 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2024, Frans AHW 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 11 | 2. Redistributions in binary form must reproduce the above copyright notice, 12 | this list of conditions and the following disclaimer in the documentation 13 | and/or other materials provided with the distribution. 14 | 15 | 3. Neither the name of the copyright holder nor the names of its 16 | contributors may be used to endorse or promote products derived from 17 | this software without specific prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 23 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 25 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 26 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 27 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | -------------------------------------------------------------------------------- /frontend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "frontend", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "tsc && vite build", 9 | "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", 10 | "preview": "vite preview" 11 | }, 12 | "dependencies": { 13 | "@emotion/react": "^11.11.3", 14 | "@emotion/styled": "^11.11.0", 15 | "@fullcalendar/core": "^6.1.11", 16 | "@fullcalendar/daygrid": "^6.1.11", 17 | "@fullcalendar/interaction": "^6.1.11", 18 | "@fullcalendar/list": "^6.1.11", 19 | "@fullcalendar/react": "^6.1.11", 20 | "@fullcalendar/timegrid": "^6.1.11", 21 | "@mui/material": "^5.15.7", 22 | "@mui/x-data-grid": "^6.19.2", 23 | "@tanstack/react-query": "^5.18.1", 24 | "axios": "^1.6.7", 25 | "react": "^18.2.0", 26 | "react-dom": "^18.2.0", 27 | "react-full-screen": "^1.1.1", 28 | "react-hot-toast": "^2.4.1", 29 | "react-icons": "^5.0.1", 30 | "react-router-dom": "^6.21.3", 31 | "recharts": "^2.11.0" 32 | }, 33 | "devDependencies": { 34 | "@types/react": "^18.2.43", 35 | "@types/react-dom": "^18.2.17", 36 | "@typescript-eslint/eslint-plugin": "^6.14.0", 37 | "@typescript-eslint/parser": "^6.14.0", 38 | "@vitejs/plugin-react-swc": "^3.5.0", 39 | "autoprefixer": "^10.4.17", 40 | "daisyui": "^4.6.1", 41 | "eslint": "^8.55.0", 42 | "eslint-plugin-react-hooks": "^4.6.0", 43 | "eslint-plugin-react-refresh": "^0.4.5", 44 | "postcss": "^8.4.33", 45 | "tailwindcss": "^3.4.1", 46 | "typescript": "^5.2.2", 47 | "vite": "^5.0.8" 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /frontend/src/components/topDealsBox/data.ts: -------------------------------------------------------------------------------- 1 | export const topDealUsers = [ 2 | { 3 | id: 1, 4 | img: 'https://images.pexels.com/photos/8405873/pexels-photo-8405873.jpeg?auto=compress&cs=tinysrgb&w=1600&lazy=load', 5 | username: 'Elva McDonald', 6 | email: 'elva@gmail.com', 7 | amount: '3.668', 8 | }, 9 | { 10 | id: 2, 11 | img: 'https://images.pexels.com/photos/774909/pexels-photo-774909.jpeg?auto=compress&cs=tinysrgb&w=1600', 12 | username: 'Linnie Nelson', 13 | email: 'linnie@gmail.com', 14 | amount: '3.256', 15 | }, 16 | { 17 | id: 3, 18 | img: 'https://images.pexels.com/photos/1222271/pexels-photo-1222271.jpeg?auto=compress&cs=tinysrgb&w=1600', 19 | username: 'Brent Reeves', 20 | email: 'brent@gmail.com', 21 | amount: '2.998', 22 | }, 23 | { 24 | id: 4, 25 | img: 'https://images.pexels.com/photos/733872/pexels-photo-733872.jpeg?auto=compress&cs=tinysrgb&w=1600', 26 | username: 'Adeline Watson', 27 | email: 'adeline@gmail.com', 28 | amount: '2.512', 29 | }, 30 | { 31 | id: 5, 32 | img: 'https://images.pexels.com/photos/91227/pexels-photo-91227.jpeg?auto=compress&cs=tinysrgb&w=1600', 33 | username: 'Juan Harrington', 34 | email: 'juan@gmail.com', 35 | amount: '2.134', 36 | }, 37 | { 38 | id: 6, 39 | img: 'https://images.pexels.com/photos/1681010/pexels-photo-1681010.jpeg?auto=compress&cs=tinysrgb&w=1600', 40 | username: 'Augusta McGee', 41 | email: 'augusta@gmail.com', 42 | amount: '1.932', 43 | }, 44 | { 45 | id: 7, 46 | img: 'https://images.pexels.com/photos/874158/pexels-photo-874158.jpeg?auto=compress&cs=tinysrgb&w=1600', 47 | username: 'Angel Thomas', 48 | email: 'angel@gmail.com', 49 | amount: '1.560', 50 | }, 51 | ]; 52 | -------------------------------------------------------------------------------- /backend/src/data/topDeals.ts: -------------------------------------------------------------------------------- 1 | const topDeals = [ 2 | { 3 | id: 1, 4 | img: 'https://images.pexels.com/photos/8405873/pexels-photo-8405873.jpeg?auto=compress&cs=tinysrgb&w=1600&lazy=load', 5 | username: 'Elva McDonald', 6 | email: 'elva@gmail.com', 7 | amount: '3.668', 8 | }, 9 | { 10 | id: 2, 11 | img: 'https://images.pexels.com/photos/774909/pexels-photo-774909.jpeg?auto=compress&cs=tinysrgb&w=1600', 12 | username: 'Linnie Nelson', 13 | email: 'linnie@gmail.com', 14 | amount: '3.256', 15 | }, 16 | { 17 | id: 3, 18 | img: 'https://images.pexels.com/photos/1222271/pexels-photo-1222271.jpeg?auto=compress&cs=tinysrgb&w=1600', 19 | username: 'Brent Reeves', 20 | email: 'brent@gmail.com', 21 | amount: '2.998', 22 | }, 23 | { 24 | id: 4, 25 | img: 'https://images.pexels.com/photos/733872/pexels-photo-733872.jpeg?auto=compress&cs=tinysrgb&w=1600', 26 | username: 'Adeline Watson', 27 | email: 'adeline@gmail.com', 28 | amount: '2.512', 29 | }, 30 | { 31 | id: 5, 32 | img: 'https://images.pexels.com/photos/91227/pexels-photo-91227.jpeg?auto=compress&cs=tinysrgb&w=1600', 33 | username: 'Juan Harrington', 34 | email: 'juan@gmail.com', 35 | amount: '2.134', 36 | }, 37 | { 38 | id: 6, 39 | img: 'https://images.pexels.com/photos/1681010/pexels-photo-1681010.jpeg?auto=compress&cs=tinysrgb&w=1600', 40 | username: 'Augusta McGee', 41 | email: 'augusta@gmail.com', 42 | amount: '1.932', 43 | }, 44 | { 45 | id: 7, 46 | img: 'https://images.pexels.com/photos/874158/pexels-photo-874158.jpeg?auto=compress&cs=tinysrgb&w=1600', 47 | username: 'Angel Thomas', 48 | email: 'angel@gmail.com', 49 | amount: '1.560', 50 | }, 51 | ]; 52 | 53 | export default topDeals; 54 | -------------------------------------------------------------------------------- /frontend/src/components/ChangesThemes.tsx: -------------------------------------------------------------------------------- 1 | // import React from 'react'; 2 | import useTheme from '../contexts/useTheme'; 3 | 4 | const ChangeThemes = () => { 5 | const { toggleTheme } = useTheme(); 6 | 7 | return ( 8 |
9 | 32 |
33 | ); 34 | }; 35 | 36 | export default ChangeThemes; 37 | -------------------------------------------------------------------------------- /backend/README.md: -------------------------------------------------------------------------------- 1 | # Express API Starter with Typescript 2 | 3 | How to use this template: 4 | 5 | ```sh 6 | npx create-express-api --typescript --directory my-api-name 7 | ``` 8 | 9 | Includes API Server utilities: 10 | 11 | * [morgan](https://www.npmjs.com/package/morgan) 12 | * HTTP request logger middleware for node.js 13 | * [helmet](https://www.npmjs.com/package/helmet) 14 | * Helmet helps you secure your Express apps by setting various HTTP headers. It's not a silver bullet, but it can help! 15 | * [dotenv](https://www.npmjs.com/package/dotenv) 16 | * Dotenv is a zero-dependency module that loads environment variables from a `.env` file into `process.env` 17 | * [cors](https://www.npmjs.com/package/cors) 18 | * CORS is a node.js package for providing a Connect/Express middleware that can be used to enable CORS with various options. 19 | 20 | Development utilities: 21 | 22 | * [typescript](https://www.npmjs.com/package/typescript) 23 | * TypeScript is a language for application-scale JavaScript. 24 | * [ts-node](https://www.npmjs.com/package/ts-node) 25 | * TypeScript execution and REPL for node.js, with source map and native ESM support. 26 | * [nodemon](https://www.npmjs.com/package/nodemon) 27 | * nodemon is a tool that helps develop node.js based applications by automatically restarting the node application when file changes in the directory are detected. 28 | * [eslint](https://www.npmjs.com/package/eslint) 29 | * ESLint is a tool for identifying and reporting on patterns found in ECMAScript/JavaScript code. 30 | * [typescript-eslint](https://typescript-eslint.io/) 31 | * Tooling which enables ESLint to support TypeScript. 32 | * [jest](https://www.npmjs.com/package/jest) 33 | * Jest is a delightful JavaScript Testing Framework with a focus on simplicity. 34 | * [supertest](https://www.npmjs.com/package/supertest) 35 | * HTTP assertions made easy via superagent. 36 | 37 | ## Setup 38 | 39 | ``` 40 | npm install 41 | ``` 42 | 43 | ## Lint 44 | 45 | ``` 46 | npm run lint 47 | ``` 48 | 49 | ## Test 50 | 51 | ``` 52 | npm run test 53 | ``` 54 | 55 | ## Development 56 | 57 | ``` 58 | npm run dev 59 | ``` 60 | -------------------------------------------------------------------------------- /frontend/src/components/menu/MenuItem.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { NavLink } from 'react-router-dom'; 3 | import { IconType } from 'react-icons'; 4 | 5 | interface MenuItemProps { 6 | onClick?: () => void; 7 | catalog: string; 8 | listItems: Array<{ 9 | isLink: boolean; 10 | url?: string; 11 | icon: IconType; 12 | label: string; 13 | onClick?: () => void; 14 | }>; 15 | } 16 | 17 | const MenuItem: React.FC = ({ 18 | onClick, 19 | catalog, 20 | listItems, 21 | }) => { 22 | return ( 23 |
24 | 25 | {catalog} 26 | 27 | {listItems.map((listItem, index) => { 28 | if (listItem.isLink) { 29 | return ( 30 | 35 | isActive 36 | ? 'btn 2xl:min-h-[52px] 3xl:min-h-[64px] btn-active btn-ghost btn-block justify-start' 37 | : 'btn 2xl:min-h-[52px] 3xl:min-h-[64px] btn-ghost btn-block justify-start' 38 | } 39 | > 40 | 41 | 42 | {listItem.label} 43 | 44 | 45 | ); 46 | } else { 47 | return ( 48 | 58 | ); 59 | } 60 | })} 61 |
62 | ); 63 | }; 64 | 65 | export default MenuItem; 66 | -------------------------------------------------------------------------------- /frontend/src/components/calendar/CalendarUtils.tsx: -------------------------------------------------------------------------------- 1 | import { EventInput } from '@fullcalendar/core'; 2 | 3 | let eventGuid = 0; 4 | const today = new Date(); 5 | const tomorrow = new Date(); 6 | const randomAja1 = new Date(); 7 | const randomAja2 = new Date(); 8 | const randomAja3 = new Date(); 9 | const randomAja4 = new Date(); 10 | const nextWeek = new Date(); 11 | 12 | tomorrow.setDate(today.getDate() + 1); 13 | randomAja1.setDate( 14 | today.getDate() + Math.floor(Math.random() * 8 + 1) 15 | ); 16 | randomAja2.setDate( 17 | today.getDate() + Math.floor(Math.random() * 12 + 1) 18 | ); 19 | randomAja3.setDate( 20 | today.getDate() + Math.floor(Math.random() * 16 + 1) 21 | ); 22 | randomAja4.setDate( 23 | today.getDate() + Math.floor(Math.random() * 16 + 16) 24 | ); 25 | nextWeek.setDate(today.getDate() + 7); 26 | 27 | const todayStr = new Date().toISOString().replace(/T.*$/, ''); // YYYY-MM-DD of today 28 | const tomorrowStr = tomorrow.toISOString().replace(/T.*$/, ''); 29 | const nextWeekStr = nextWeek.toISOString().replace(/T.*$/, ''); 30 | const randomStr1 = randomAja1.toISOString().replace(/T.*$/, ''); 31 | const randomStr2 = randomAja2.toISOString().replace(/T.*$/, ''); 32 | const randomStr3 = randomAja3.toISOString().replace(/T.*$/, ''); 33 | const randomStr4 = randomAja4.toISOString().replace(/T.*$/, ''); 34 | 35 | export const INITIAL_EVENTS: EventInput[] = [ 36 | { 37 | id: createEventId(), 38 | title: 'Hari ini ngapain hah? 🙃', 39 | start: todayStr, 40 | }, 41 | { 42 | id: createEventId(), 43 | title: 'Jirr kerja rodi 🤡', 44 | start: tomorrowStr + 'T19:00:00', 45 | }, 46 | { 47 | id: createEventId(), 48 | title: 'Waduh gawat, deadline! 😑', 49 | start: nextWeekStr + 'T20:00:00', 50 | }, 51 | { 52 | id: createEventId(), 53 | title: 'Ketemu client sombong 😠', 54 | start: randomStr1 + 'T09:00:00', 55 | }, 56 | { 57 | id: createEventId(), 58 | title: 'Surprise rekan kerja 🥳', 59 | start: randomStr2 + 'T12:00:00', 60 | }, 61 | { 62 | id: createEventId(), 63 | title: 'Business trip uhuyyy! 😎', 64 | start: randomStr3, 65 | end: randomStr4, 66 | }, 67 | ]; 68 | 69 | export function createEventId() { 70 | return String(eventGuid++); 71 | } 72 | -------------------------------------------------------------------------------- /frontend/src/components/notes/NoteCard.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | interface NoteCardProps { 4 | setNoteSelected: React.Dispatch>; 5 | selectedCard: { 6 | title: string; 7 | body: string; 8 | }; 9 | setSelectedCard: React.Dispatch< 10 | React.SetStateAction<{ title: string; body: string }> 11 | >; 12 | setTitleSelected: React.Dispatch>; 13 | setBodySelected: React.Dispatch>; 14 | setTopicSelected: React.Dispatch>; 15 | title: string; 16 | body: string; 17 | date: string; 18 | author: string; 19 | topic: string; 20 | } 21 | 22 | const NoteCard: React.FC = ({ 23 | setNoteSelected, 24 | selectedCard, 25 | setSelectedCard, 26 | setTitleSelected, 27 | setBodySelected, 28 | setTopicSelected, 29 | title, 30 | body, 31 | date, 32 | author, 33 | topic, 34 | }) => { 35 | return ( 36 |
{ 38 | setNoteSelected(true); 39 | setSelectedCard({ 40 | title: title, 41 | body: body, 42 | }); 43 | setTitleSelected(title); 44 | setBodySelected(body); 45 | setTopicSelected(topic); 46 | }} 47 | className={`w-full btn min-h-[auto] h-auto rounded-3xl border-none flex flex-col items-start text-start group bg-[#fafafa] hover:bg-primary hover:text-primary-content dark:bg-neutral dark:hover:bg-primary 48 | ${ 49 | selectedCard.title === title 50 | ? 'bg-primary text-primary-content dark:bg-primary dark:primary-content hover:text-primary-content dark:hover:text-primary-content' 51 | : 'bg-[#f7f7f7] hover:bg-primary hover:text-primary-content dark:bg-neutral dark:hover:bg-primary' 52 | } 53 | `} 54 | > 55 |
56 |

{title}

57 |

66 | {body} 67 |

68 |
75 | {date} 76 | {author} 77 |
78 |
79 |
80 | ); 81 | }; 82 | 83 | export default NoteCard; 84 | -------------------------------------------------------------------------------- /frontend/src/components/menu/data.ts: -------------------------------------------------------------------------------- 1 | // import toast from 'react-hot-toast'; 2 | import { 3 | HiOutlineHome, 4 | HiOutlineUser, 5 | HiOutlineUsers, 6 | HiOutlineCube, 7 | HiOutlineClipboardDocumentList, 8 | HiOutlineDocumentChartBar, 9 | HiOutlinePencilSquare, 10 | HiOutlineCalendarDays, 11 | HiOutlinePresentationChartBar, 12 | HiOutlineDocumentText, 13 | HiOutlineArrowLeftOnRectangle, 14 | } from 'react-icons/hi2'; 15 | // import { IoSettingsOutline } from 'react-icons/io5'; 16 | 17 | export const menu = [ 18 | { 19 | catalog: 'main', 20 | listItems: [ 21 | { 22 | isLink: true, 23 | url: '/', 24 | icon: HiOutlineHome, 25 | label: 'homepage', 26 | }, 27 | { 28 | isLink: true, 29 | url: '/profile', 30 | icon: HiOutlineUser, 31 | label: 'profile', 32 | }, 33 | ], 34 | }, 35 | { 36 | catalog: 'lists', 37 | listItems: [ 38 | { 39 | isLink: true, 40 | url: '/users', 41 | icon: HiOutlineUsers, 42 | label: 'users', 43 | }, 44 | { 45 | isLink: true, 46 | url: '/products', 47 | icon: HiOutlineCube, 48 | label: 'products', 49 | }, 50 | { 51 | isLink: true, 52 | url: '/orders', 53 | icon: HiOutlineClipboardDocumentList, 54 | label: 'orders', 55 | }, 56 | { 57 | isLink: true, 58 | url: '/posts', 59 | icon: HiOutlineDocumentChartBar, 60 | label: 'posts', 61 | }, 62 | ], 63 | }, 64 | { 65 | catalog: 'general', 66 | listItems: [ 67 | { 68 | isLink: true, 69 | url: '/notes', 70 | icon: HiOutlinePencilSquare, 71 | label: 'notes', 72 | }, 73 | { 74 | isLink: true, 75 | url: '/calendar', 76 | icon: HiOutlineCalendarDays, 77 | label: 'calendar', 78 | }, 79 | ], 80 | }, 81 | { 82 | catalog: 'analytics', 83 | listItems: [ 84 | { 85 | isLink: true, 86 | url: '/charts', 87 | icon: HiOutlinePresentationChartBar, 88 | label: 'charts', 89 | }, 90 | { 91 | isLink: true, 92 | url: '/logs', 93 | icon: HiOutlineDocumentText, 94 | label: 'logs', 95 | }, 96 | ], 97 | }, 98 | { 99 | catalog: 'miscellaneous', 100 | listItems: [ 101 | // { 102 | // isLink: true, 103 | // url: '/settings', 104 | // icon: IoSettingsOutline, 105 | // label: 'settings', 106 | // }, 107 | { 108 | isLink: true, 109 | url: '/login', 110 | icon: HiOutlineArrowLeftOnRectangle, 111 | label: 'log out', 112 | }, 113 | ], 114 | }, 115 | ]; 116 | -------------------------------------------------------------------------------- /frontend/src/components/topDealsBox/TopDealsBox.tsx: -------------------------------------------------------------------------------- 1 | // import React from 'react' 2 | import toast from 'react-hot-toast'; 3 | // import { topDealUsers } from './data'; 4 | import { useQuery } from '@tanstack/react-query'; 5 | import { fetchTopDeals } from '../../api/ApiCollection'; 6 | 7 | interface topDealsUser { 8 | id: number; 9 | img: string; 10 | username: string; 11 | email: string; 12 | amount: string; 13 | } 14 | 15 | const TopDealsBox = () => { 16 | const tempTotalEntries = [1, 2, 3, 4, 5, 6, 7]; 17 | 18 | const { isLoading, isSuccess, data } = useQuery({ 19 | queryKey: ['topdeals'], 20 | queryFn: fetchTopDeals, 21 | }); 22 | 23 | return ( 24 |
25 | 26 | Top Deals 27 | 28 |
29 | {isLoading && 30 | tempTotalEntries.map((_item, index) => ( 31 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 | ))} 45 | {isSuccess && 46 | data.map((user: topDealsUser, index: number) => ( 47 | 71 | ))} 72 |
73 |
74 | ); 75 | }; 76 | 77 | export default TopDealsBox; 78 | -------------------------------------------------------------------------------- /backend/src/data/products.ts: -------------------------------------------------------------------------------- 1 | let products = [ 2 | { 3 | id: 1, 4 | img: 'https://store.sony.com.au/on/demandware.static/-/Sites-sony-master-catalog/default/dw1b537bbb/images/PLAYSTATION5W/PLAYSTATION5W.png', 5 | title: 'Playstation 5 Digital Edition', 6 | color: 'white', 7 | producer: 'Sony', 8 | price: '$250.99', 9 | createdAt: '01.02.2023', 10 | inStock: true, 11 | }, 12 | { 13 | id: 2, 14 | img: 'https://www.pngmart.com/files/6/Dell-Laptop-PNG-Image.png', 15 | title: 'Dell Laptop KR211822', 16 | color: 'black', 17 | producer: 'Dell', 18 | price: '$499.99', 19 | createdAt: '01.02.2023', 20 | inStock: true, 21 | }, 22 | { 23 | id: 3, 24 | img: 'http://images.samsung.com/is/image/samsung/uk-led-tv-hg40ed670ck-hg40ed670ckxxu-001-front', 25 | title: 'Samsung TV 4K SmartTV', 26 | color: 'gray', 27 | producer: 'Samsung', 28 | price: '$999.49', 29 | createdAt: '01.02.2023', 30 | inStock: true, 31 | }, 32 | { 33 | id: 4, 34 | img: 'https://raylo.imgix.net/iphone-14-blue.png', 35 | title: 'Apple Iphone 14 Pro Max', 36 | color: 'white', 37 | producer: 'Apple', 38 | price: '$799.49', 39 | createdAt: '01.02.2023', 40 | inStock: true, 41 | }, 42 | { 43 | id: 5, 44 | img: 'https://www.signify.com/b-dam/signify/en-aa/about/news/2020/20200903-movie-night-essentials-popcorn-ice-cream-and-the-new-philips-hue-play-gradient-lightstrip/packaging-lighstrip.png', 45 | title: 'Philips Hue Play Gradient', 46 | color: 'rainbow', 47 | producer: 'Philips', 48 | price: '$39.99', 49 | createdAt: '01.02.2023', 50 | }, 51 | { 52 | id: 6, 53 | img: 'https://www.smartworld.it/wp-content/uploads/2019/09/High_Resolution_PNG-MX-Master-3-LEFT-GRAPHITE.png', 54 | title: 'Logitech MX Master 3', 55 | color: 'black', 56 | producer: 'Logitech', 57 | price: '$59.49', 58 | createdAt: '01.02.2023', 59 | inStock: true, 60 | }, 61 | { 62 | id: 7, 63 | img: 'https://www.pngarts.com/files/7/Podcast-Mic-PNG-Picture.png', 64 | title: 'Rode Podcast Microphone', 65 | color: 'gray', 66 | producer: 'Rode', 67 | price: '$119.49', 68 | createdAt: '01.02.2023', 69 | }, 70 | { 71 | id: 8, 72 | img: 'https://5.imimg.com/data5/SW/VM/MY-5774620/toshiba-split-ac-2-ton-3-star-rated-ras-24s3ks-500x500.png', 73 | title: 'Toshiba Split AC 2', 74 | color: 'white', 75 | producer: 'Toshiba', 76 | price: '$899.99', 77 | createdAt: '01.02.2023', 78 | inStock: true, 79 | }, 80 | { 81 | id: 9, 82 | img: 'https://img.productz.com/review_image/102489/preview_sony-kdl-50w800b-50-inch-hdtv-review-superb-picture-102489.png', 83 | title: 'Sony Bravia KDL-47W805A', 84 | color: 'black', 85 | producer: 'Sony', 86 | price: '$970.49', 87 | createdAt: '01.02.2023', 88 | }, 89 | { 90 | id: 10, 91 | img: 'https://venturebeat.com/wp-content/uploads/2015/07/As_AO1-131_gray_nonglare_win10_03.png?fit=1338%2C1055&strip=all', 92 | title: 'Acer Laptop 16 KL-4804', 93 | color: 'black', 94 | producer: 'Acer', 95 | price: '$599.99', 96 | createdAt: '01.02.2023', 97 | inStock: true, 98 | }, 99 | ]; 100 | 101 | export default products; 102 | -------------------------------------------------------------------------------- /frontend/src/index.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:ital,wght@0,200;0,300;0,400;0,500;0,600;0,700;0,800;1,200;1,300;1,400;1,500;1,600;1,700;1,800&display=swap'); 2 | 3 | @tailwind base; 4 | @tailwind components; 5 | @tailwind utilities; 6 | 7 | :root { 8 | font-family: "Plus Jakarta Sans", sans-serif; 9 | margin: 0; 10 | padding: 0; 11 | overflow-x: hidden !important; 12 | overflow-y: auto !important; 13 | } 14 | 15 | :fullscreen::backdrop { 16 | background: none !important; 17 | overflow: visible; 18 | } 19 | 20 | :fullscreen::before & :fullscreen::after { 21 | overflow: visible; 22 | overflow: visible; 23 | } 24 | 25 | :fullscreen #rootContainer { 26 | overflow: auto !important; 27 | } 28 | 29 | :fullscreen body { 30 | overflow: auto !important; 31 | } 32 | 33 | :fullscreen { 34 | height: auto !important; 35 | overflow: auto !important; 36 | } 37 | 38 | /* Home */ 39 | .home .box { 40 | @apply rounded-lg xl:rounded-2xl overflow-visible border-2 border-base-300 dark:border-slate-700 p-4 xl:p-4 2xl:p-6 41 | } 42 | 43 | .dataGrid span, .dataGrid svg, .dataGrid button { 44 | @apply text-base-content 45 | } 46 | 47 | .dataGrid .MuiDataGrid-columnHeaders { 48 | @apply text-base-content border-base-content 49 | } 50 | 51 | .dataGrid .MuiDataGrid-virtualScroller { 52 | @apply text-base-content border-base-content 53 | } 54 | 55 | .dataGrid .MuiDataGrid-row .MuiDataGrid-cell .MuiSvgIcon-root { 56 | @apply text-base-content 57 | } 58 | 59 | .dataGrid .MuiDataGrid-footerContainer { 60 | @apply text-base-content border-base-content 61 | } 62 | 63 | .dataGrid .MuiDataGrid-footerContainer p { 64 | @apply text-base-content border-base-content 65 | } 66 | 67 | .dataGrid .MuiDataGrid-toolbarContainer { 68 | @apply flex-row-reverse 69 | } 70 | 71 | .dataGrid .MuiDataGrid-toolbarContainer input { 72 | @apply border-base-content text-base-content 73 | } 74 | 75 | .dataGrid .MuiDataGrid-toolbarContainer ::placeholder { 76 | @apply text-base-content 77 | } 78 | 79 | .modal-action> :not([hidden])~ :not([hidden]) { 80 | margin-right: 0 !important; 81 | margin-left: 0 !important; 82 | } 83 | 84 | .skeleton { 85 | @apply dark:bg-neutral 86 | } 87 | 88 | .fc .fc-header-toolbar .fc-toolbar-chunk { 89 | @apply text-[10px] xl:text-base 90 | } 91 | 92 | .fc .fc-header-toolbar .fc-toolbar-chunk .fc-button { 93 | @apply btn dark:btn-neutral 94 | } 95 | 96 | .fc .fc-header-toolbar .fc-toolbar-chunk .fc-button-group { 97 | @apply gap-2 98 | } 99 | 100 | .fc .fc-header-toolbar .fc-toolbar-chunk .fc-button.fc-button-primary { 101 | @apply border-none text-base-content 102 | } 103 | 104 | .fc .fc-header-toolbar .fc-toolbar-chunk .fc-button.fc-button-primary:disabled { 105 | @apply btn-disabled opacity-50 106 | } 107 | 108 | #singleUser #activities ul li { 109 | @apply list-none relative w-[1px] pt-[50px] bg-primary 110 | } 111 | 112 | #singleUser #activities ul li::after { 113 | content: ""; 114 | @apply absolute left-[50%] bottom-0 w-[10px] h-[10px] rounded-full bg-primary -translate-x-[50%] 115 | } 116 | 117 | #singleProduct #activities ul li { 118 | @apply list-none relative w-[1px] pt-[50px] bg-primary 119 | } 120 | 121 | #singleProduct #activities ul li::after { 122 | content: ""; 123 | @apply absolute left-[50%] bottom-0 w-[10px] h-[10px] rounded-full bg-primary -translate-x-[50%] 124 | } -------------------------------------------------------------------------------- /frontend/src/App.tsx: -------------------------------------------------------------------------------- 1 | // import React from 'react'; 2 | import { 3 | createBrowserRouter, 4 | RouterProvider, 5 | Outlet, 6 | ScrollRestoration, 7 | } from 'react-router-dom'; 8 | import Home from './pages/Home'; 9 | import Users from './pages/Users'; 10 | import Products from './pages/Products'; 11 | import Navbar from './components/Navbar'; 12 | import Footer from './components/Footer'; 13 | import Menu from './components/menu/Menu'; 14 | import Error from './pages/Error'; 15 | import Profile from './pages/Profile'; 16 | import Orders from './pages/Orders'; 17 | import Posts from './pages/Posts'; 18 | import Notes from './pages/Notes'; 19 | import Calendar from './pages/Calendar'; 20 | import Charts from './pages/Charts'; 21 | import Logs from './pages/Logs'; 22 | import ToasterProvider from './components/ToasterProvider'; 23 | import EditProfile from './pages/EditProfile'; 24 | import User from './pages/User'; 25 | import Product from './pages/Product'; 26 | import Login from './pages/Login'; 27 | 28 | function App() { 29 | const Layout = () => { 30 | return ( 31 |
35 | 36 | 37 |
38 | 39 |
40 |
41 | 42 |
43 |
44 | 45 |
46 |
47 |
48 |
49 |
50 | ); 51 | }; 52 | 53 | const router = createBrowserRouter([ 54 | { 55 | path: '/', 56 | element: , 57 | children: [ 58 | { 59 | path: '/', 60 | element: , 61 | }, 62 | { 63 | path: '/profile', 64 | element: , 65 | }, 66 | { 67 | path: '/profile/edit', 68 | element: , 69 | }, 70 | { 71 | path: '/users', 72 | element: , 73 | }, 74 | { 75 | path: '/users/:id', 76 | element: , 77 | }, 78 | { 79 | path: '/products', 80 | element: , 81 | }, 82 | { 83 | path: '/products/:id', 84 | element: , 85 | }, 86 | { 87 | path: '/orders', 88 | element: , 89 | }, 90 | { 91 | path: '/posts', 92 | element: , 93 | }, 94 | { 95 | path: '/notes', 96 | element: , 97 | }, 98 | { 99 | path: '/calendar', 100 | element: , 101 | }, 102 | { 103 | path: '/charts', 104 | element: , 105 | }, 106 | { 107 | path: '/logs', 108 | element: , 109 | }, 110 | ], 111 | errorElement: , 112 | }, 113 | { 114 | path: '/login', 115 | element: , 116 | }, 117 | ]); 118 | 119 | return ; 120 | } 121 | 122 | export default App; 123 | -------------------------------------------------------------------------------- /frontend/src/assets/react.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /backend/src/data/logs.ts: -------------------------------------------------------------------------------- 1 | const logs = [ 2 | { 3 | id: 1015, 4 | img: 'https://images.pexels.com/photos/8405873/pexels-photo-8405873.jpeg?auto=compress&cs=tinysrgb&w=1600&lazy=load', 5 | lastName: 'Griffin', 6 | firstName: 'Eric', 7 | email: 'griffin@gmail.com', 8 | role: 'Member', 9 | date: 'Mar 06, 2024', 10 | time: '2:29 pm', 11 | action: 'Updated profile picture and password', 12 | }, 13 | { 14 | id: 1014, 15 | img: 'https://images.pexels.com/photos/415829/pexels-photo-415829.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1', 16 | lastName: 'Rahmawati', 17 | firstName: 'Shania', 18 | email: 'shanirahma@gmail.com', 19 | role: 'Member', 20 | date: 'Feb 01, 2024', 21 | time: '11:05 am', 22 | action: 23 | 'Created a post `Exploring the Grand Canyon: A Journey Through Time`', 24 | }, 25 | { 26 | id: 1013, 27 | img: 'https://images.pexels.com/photos/761977/pexels-photo-761977.jpeg?auto=compress&cs=tinysrgb&w=1600', 28 | lastName: 'Cruz', 29 | firstName: 'Charlotte', 30 | email: 'charlotte@gmail.com', 31 | role: 'Member', 32 | date: 'Jan 27, 2024', 33 | time: '3:07 pm', 34 | action: 'Integrated account with Apple', 35 | }, 36 | { 37 | id: 1012, 38 | img: 'https://images.pexels.com/photos/460031/pexels-photo-460031.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1', 39 | lastName: 'Putra', 40 | firstName: 'Lasmanto', 41 | email: 'lasputra23@gmail.com', 42 | role: 'Member', 43 | date: 'Jan 26, 2024', 44 | time: '1:12 pm', 45 | action: 'Added Apple Iphone 14 Pro Max to wishlist', 46 | }, 47 | { 48 | id: 1011, 49 | img: 'https://images.pexels.com/photos/1102341/pexels-photo-1102341.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1', 50 | lastName: 'Puspita', 51 | firstName: 'Ika', 52 | email: 'puspaika98@gmail.com', 53 | role: 'Member', 54 | date: 'Jan 13, 2024', 55 | time: '8:54 am', 56 | action: 57 | 'Created a post `The Magic of Music: A Journey Through Sound`', 58 | }, 59 | { 60 | id: 1010, 61 | img: 'https://images.pexels.com/photos/774095/pexels-photo-774095.jpeg?auto=compress&cs=tinysrgb&w=1600', 62 | lastName: 'Williams', 63 | firstName: 'Mark', 64 | email: 'williams@hotmail.com', 65 | role: 'Member', 66 | date: 'Jan 1, 2024', 67 | time: '12:00 pm', 68 | action: 'Updated email and password', 69 | }, 70 | { 71 | id: 1009, 72 | img: 'https://images.pexels.com/photos/775358/pexels-photo-775358.jpeg?auto=compress&cs=tinysrgb&w=1600', 73 | lastName: 'Reid', 74 | firstName: 'Elnora', 75 | email: 'elnora@gmail.com', 76 | role: 'Member', 77 | date: 'Dec 27, 2023', 78 | time: '4:00 pm', 79 | action: 80 | 'Updated profile picture and integrated account with Google', 81 | }, 82 | { 83 | id: 1008, 84 | img: 'https://images.pexels.com/photos/91227/pexels-photo-91227.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1', 85 | lastName: 'Halim', 86 | firstName: 'Asnawi', 87 | email: 'halimasnawi@gmail.com', 88 | role: 'Member', 89 | date: 'Dec 17, 2023', 90 | time: '4:32 pm', 91 | action: 'Created a post `Healthy Living: Mind, Body, and Soul`', 92 | }, 93 | { 94 | id: 1007, 95 | img: 'https://images.pexels.com/photos/681644/pexels-photo-681644.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1', 96 | lastName: 'Pratiwi', 97 | firstName: 'Wulan', 98 | email: 'prawulan95@gmail.com', 99 | role: 'Member', 100 | date: 'Dec 3, 2023', 101 | time: '9:23 pm', 102 | action: 'Added Samsung TV 4K SmartTV to wishlist', 103 | }, 104 | { 105 | id: 1006, 106 | img: 'https://images.pexels.com/photos/681644/pexels-photo-681644.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1', 107 | lastName: 'Pratiwi', 108 | firstName: 'Wulan', 109 | email: 'prawulan95@gmail.com', 110 | role: 'Member', 111 | date: 'Dec 3, 2023', 112 | time: '9:17 pm', 113 | action: 'Added Polytron TV 2K SmartTV to wishlist', 114 | }, 115 | ]; 116 | 117 | export default logs; 118 | -------------------------------------------------------------------------------- /frontend/src/components/DataTable.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { 3 | DataGrid, 4 | GridColDef, 5 | // GridToolbarQuickFilter, 6 | GridToolbar, 7 | // GridValueGetterParams, 8 | } from '@mui/x-data-grid'; 9 | import { useNavigate } from 'react-router-dom'; 10 | import { 11 | HiOutlinePencilSquare, 12 | HiOutlineEye, 13 | HiOutlineTrash, 14 | } from 'react-icons/hi2'; 15 | import toast from 'react-hot-toast'; 16 | 17 | interface DataTableProps { 18 | columns: GridColDef[]; 19 | rows: object[]; 20 | slug: string; 21 | includeActionColumn: boolean; 22 | } 23 | 24 | const DataTable: React.FC = ({ 25 | columns, 26 | rows, 27 | slug, 28 | includeActionColumn, 29 | }) => { 30 | const navigate = useNavigate(); 31 | 32 | const actionColumn: GridColDef = { 33 | field: 'action', 34 | headerName: 'Action', 35 | minWidth: 200, 36 | flex: 1, 37 | renderCell: (params) => { 38 | return ( 39 |
40 | {/*
*/} 41 | 49 | 59 | 69 |
70 | ); 71 | }, 72 | }; 73 | 74 | if (includeActionColumn === true) { 75 | return ( 76 |
77 | 'auto'} 82 | initialState={{ 83 | pagination: { 84 | paginationModel: { 85 | pageSize: 10, 86 | }, 87 | }, 88 | }} 89 | slots={{ toolbar: GridToolbar }} 90 | slotProps={{ 91 | toolbar: { 92 | showQuickFilter: true, 93 | quickFilterProps: { debounceMs: 500 }, 94 | }, 95 | }} 96 | pageSizeOptions={[5]} 97 | checkboxSelection 98 | disableRowSelectionOnClick 99 | disableColumnFilter 100 | disableDensitySelector 101 | disableColumnSelector 102 | /> 103 |
104 | ); 105 | } else { 106 | return ( 107 |
108 | 'auto'} 113 | initialState={{ 114 | pagination: { 115 | paginationModel: { 116 | pageSize: 10, 117 | }, 118 | }, 119 | }} 120 | slots={{ toolbar: GridToolbar }} 121 | slotProps={{ 122 | toolbar: { 123 | showQuickFilter: true, 124 | quickFilterProps: { debounceMs: 500 }, 125 | }, 126 | }} 127 | pageSizeOptions={[5]} 128 | checkboxSelection 129 | disableRowSelectionOnClick 130 | disableColumnFilter 131 | disableDensitySelector 132 | disableColumnSelector 133 | /> 134 |
135 | ); 136 | } 137 | }; 138 | 139 | export default DataTable; 140 | -------------------------------------------------------------------------------- /frontend/src/components/notes/data.ts: -------------------------------------------------------------------------------- 1 | export const allNotes = [ 2 | { 3 | id: 1, 4 | title: 'The Importance of Product Descriptions', 5 | body: 'Crafting compelling product descriptions is crucial in e-commerce. It helps to highlight the features, benefits, and unique selling points of your products, ultimately enticing potential customers to make a purchase.', 6 | date: '2024-02-27', 7 | author: 'Frans AHW', 8 | }, 9 | { 10 | id: 2, 11 | title: 'Optimizing Images for E-commerce', 12 | body: 'High-quality images are essential for e-commerce success. Ensure your product images are clear, well-lit, and showcase the product from various angles. Additionally, optimize image sizes for faster page loading speeds.', 13 | date: '2024-02-27', 14 | author: 'Frans AHW', 15 | }, 16 | { 17 | id: 3, 18 | title: 'Effective Product Categorization Strategies', 19 | body: 'Organizing products into logical categories and subcategories simplifies navigation for customers, making it easier for them to find what they`re looking for. Consider factors like product type, usage, and target audience when creating your categorization structure.', 20 | date: '2024-02-27', 21 | author: 'Frans AHW', 22 | }, 23 | { 24 | id: 4, 25 | title: 'The Power of Customer Reviews', 26 | body: 'Customer reviews play a significant role in influencing purchasing decisions. Encourage satisfied customers to leave positive reviews and promptly address any negative feedback to maintain a positive brand image.', 27 | date: '2024-02-27', 28 | author: 'Frans AHW', 29 | }, 30 | { 31 | id: 5, 32 | title: 'Streamlining Checkout Processes', 33 | body: 'A streamlined checkout process is essential for reducing cart abandonment rates. Minimize the number of steps required to complete a purchase, offer guest checkout options, and provide multiple payment methods to accommodate diverse customer preferences.', 34 | date: '2024-02-27', 35 | author: 'Frans AHW', 36 | }, 37 | { 38 | id: 6, 39 | title: 'Harnessing the Power of Social Media Marketing', 40 | body: 'Social media platforms offer valuable opportunities for e-commerce businesses to connect with their target audience, build brand awareness, and drive sales. Develop a cohesive social media strategy that includes engaging content, influencer partnerships, and targeted advertising campaigns.', 41 | date: '2024-02-27', 42 | author: 'Frans AHW', 43 | }, 44 | { 45 | id: 7, 46 | title: 'Implementing Personalization in E-commerce', 47 | body: 'Personalization enhances the customer experience by delivering tailored product recommendations, customized offers, and relevant content based on individual preferences and browsing history. Leverage customer data and advanced analytics to implement effective personalization strategies.', 48 | date: '2024-02-27', 49 | author: 'Frans AHW', 50 | }, 51 | { 52 | id: 8, 53 | title: 'Utilizing Email Marketing for E-commerce Success', 54 | body: 'Email marketing remains a powerful tool for driving customer engagement and sales in e-commerce. Develop targeted email campaigns that deliver valuable content, promotional offers, and personalized recommendations to subscribers.', 55 | date: '2024-02-27', 56 | author: 'Frans AHW', 57 | }, 58 | { 59 | id: 9, 60 | title: 'Managing Inventory and Fulfillment Efficiently', 61 | body: 'Efficient inventory management and fulfillment processes are essential for maintaining product availability, minimizing stockouts, and delivering orders to customers in a timely manner. Implement inventory tracking systems and establish strategic partnerships with reliable suppliers and logistics providers.', 62 | date: '2024-02-27', 63 | author: 'Frans AHW', 64 | }, 65 | { 66 | id: 10, 67 | title: 'Embracing Mobile Commerce Trends', 68 | body: 'With the increasing prevalence of smartphones and mobile devices, optimizing your e-commerce platform for mobile users is paramount. Implement responsive design principles, streamline mobile checkout processes, and leverage mobile-specific marketing channels to capitalize on the growing trend of mobile commerce.', 69 | date: '2024-02-27', 70 | author: 'Frans AHW', 71 | }, 72 | ]; 73 | -------------------------------------------------------------------------------- /backend/src/data/notes.ts: -------------------------------------------------------------------------------- 1 | const notes = [ 2 | { 3 | id: 1, 4 | title: 'The Importance of Product Descriptions', 5 | body: 'Crafting compelling product descriptions is crucial in e-commerce. It helps to highlight the features, benefits, and unique selling points of your products, ultimately enticing potential customers to make a purchase.', 6 | date: '2024-02-27', 7 | author: 'Frans AHW', 8 | topic: 'e-commerce', 9 | }, 10 | { 11 | id: 2, 12 | title: 'Optimizing Images for E-commerce', 13 | body: 'High-quality images are essential for e-commerce success. Ensure your product images are clear, well-lit, and showcase the product from various angles. Additionally, optimize image sizes for faster page loading speeds.', 14 | date: '2024-02-27', 15 | author: 'Frans AHW', 16 | topic: 'e-commerce', 17 | }, 18 | { 19 | id: 3, 20 | title: 'Effective Product Categorization Strategies', 21 | body: "Organizing products into logical categories and subcategories simplifies navigation for customers, making it easier for them to find what they're looking for. Consider factors like product type, usage, and target audience when creating your categorization structure.", 22 | date: '2024-02-27', 23 | author: 'Frans AHW', 24 | topic: 'e-commerce', 25 | }, 26 | { 27 | id: 4, 28 | title: 'The Power of Customer Reviews', 29 | body: 'Customer reviews play a significant role in influencing purchasing decisions. Encourage satisfied customers to leave positive reviews and promptly address any negative feedback to maintain a positive brand image.', 30 | date: '2024-02-27', 31 | author: 'Frans AHW', 32 | topic: 'marketing', 33 | }, 34 | { 35 | id: 5, 36 | title: 'Streamlining Checkout Processes', 37 | body: 'A streamlined checkout process is essential for reducing cart abandonment rates. Minimize the number of steps required to complete a purchase, offer guest checkout options, and provide multiple payment methods to accommodate diverse customer preferences.', 38 | date: '2024-02-27', 39 | author: 'Frans AHW', 40 | topic: 'e-commerce', 41 | }, 42 | { 43 | id: 6, 44 | title: 'The Art of Storytelling in Marketing', 45 | body: 'Compelling storytelling can captivate audiences, evoke emotions, and establish strong connections with your brand. Incorporate storytelling techniques into your marketing campaigns to engage customers on a deeper level and differentiate your brand from competitors.', 46 | date: '2024-02-27', 47 | author: 'Frans AHW', 48 | topic: 'marketing', 49 | }, 50 | { 51 | id: 7, 52 | title: 'Maximizing Social Media Engagement', 53 | body: 'Social media platforms offer unparalleled opportunities for businesses to engage with their target audience, build brand awareness, and drive website traffic. Develop a cohesive social media strategy that incorporates engaging content, interactive features, and timely responses to audience interactions.', 54 | date: '2024-02-27', 55 | author: 'Frans AHW', 56 | topic: 'social-media', 57 | }, 58 | { 59 | id: 8, 60 | title: 'The Science of SEO: Ranking Strategies', 61 | body: "Search engine optimization (SEO) is essential for improving your website's visibility and attracting organic traffic from search engines. Stay updated on the latest SEO trends and algorithms, optimize your website structure and content, and build quality backlinks to enhance your search engine rankings.", 62 | date: '2024-02-27', 63 | author: 'Frans AHW', 64 | topic: 'SEO', 65 | }, 66 | { 67 | id: 9, 68 | title: 'Effective Time Management Techniques', 69 | body: 'Time management is crucial for maximizing productivity and achieving your personal and professional goals. Prioritize tasks, set realistic deadlines, minimize distractions, and leverage productivity tools and techniques to make the most of your time.', 70 | date: '2024-02-27', 71 | author: 'Frans AHW', 72 | topic: 'productivity', 73 | }, 74 | { 75 | id: 10, 76 | title: 'The Art of Negotiation: Essential Skills', 77 | body: 'Negotiation skills are valuable in both personal and professional contexts. Learn to communicate effectively, understand the needs and perspectives of others, seek mutually beneficial outcomes, and remain adaptable and open-minded during the negotiation process.', 78 | date: '2024-02-27', 79 | author: 'Frans AHW', 80 | topic: 'communication', 81 | }, 82 | ]; 83 | 84 | export default notes; 85 | -------------------------------------------------------------------------------- /frontend/src/pages/Logs.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { GridColDef } from '@mui/x-data-grid'; 3 | import DataTable from '../components/DataTable'; 4 | import { useQuery } from '@tanstack/react-query'; 5 | import toast from 'react-hot-toast'; 6 | import { fetchLogs } from '../api/ApiCollection'; 7 | 8 | const Logs = () => { 9 | const { isLoading, isError, isSuccess, data } = useQuery({ 10 | queryKey: ['all-logs'], 11 | queryFn: fetchLogs, 12 | }); 13 | 14 | const columns: GridColDef[] = [ 15 | { field: 'id', headerName: 'ID', width: 90 }, 16 | { 17 | field: 'firstName', 18 | headerName: 'Name', 19 | minWidth: 220, 20 | flex: 1, 21 | renderCell: (params) => { 22 | return ( 23 |
24 |
25 |
26 | user-picture 30 |
31 |
32 | 33 | {params.row.firstName} {params.row.lastName} 34 | 35 |
36 | ); 37 | }, 38 | }, 39 | { 40 | field: 'role', 41 | headerName: 'Role', 42 | minWidth: 100, 43 | type: 'string', 44 | flex: 1, 45 | }, 46 | { 47 | field: 'email', 48 | type: 'string', 49 | headerName: 'Email', 50 | minWidth: 200, 51 | flex: 1, 52 | }, 53 | { 54 | field: 'date', 55 | headerName: 'Date', 56 | minWidth: 120, 57 | type: 'string', 58 | flex: 1, 59 | }, 60 | { 61 | field: 'time', 62 | headerName: 'Time', 63 | minWidth: 100, 64 | type: 'string', 65 | flex: 1, 66 | }, 67 | { 68 | field: 'action', 69 | headerName: 'Action', 70 | minWidth: 350, 71 | type: 'string', 72 | flex: 1, 73 | renderCell: (params) => { 74 | return ( 75 |
76 | {params.row.action} 77 |
78 | ); 79 | }, 80 | }, 81 | ]; 82 | 83 | React.useEffect(() => { 84 | if (isLoading) { 85 | toast.loading('Loading...', { id: 'promiseLogs' }); 86 | } 87 | if (isError) { 88 | toast.error('Error while getting the data!', { 89 | id: 'promiseLogs', 90 | }); 91 | } 92 | if (isSuccess) { 93 | toast.success('Got the data successfully!', { 94 | id: 'promiseLogs', 95 | }); 96 | } 97 | }, [isError, isLoading, isSuccess]); 98 | return ( 99 | // screen 100 |
101 | {/* container */} 102 |
103 | {/* block 1 */} 104 |
105 |
106 |

107 | Logs 108 |

109 | {data && data.length > 0 && ( 110 | 111 | {data.length} Logs Found 112 | 113 | )} 114 |
115 |
116 | 117 | {/* table */} 118 | {isLoading ? ( 119 | 125 | ) : isSuccess ? ( 126 | 132 | ) : ( 133 | <> 134 | 140 |
141 | Error while getting the data! 142 |
143 | 144 | )} 145 |
146 |
147 | ); 148 | }; 149 | 150 | export default Logs; 151 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 |

3 | 4 | Logo 5 | 6 |

7 | 8 |
9 |

10 | GitHub license 11 | version 12 | GitHub stars 13 | language 14 | forks 15 |

16 |
17 | 18 | [React Admin UI](https://react-admin-ui-v1.vercel.app/) is a beautiful and open-source **Dashboard User Interface Prototype** built with TypeScript and React based. Surprisingly, this is my first time building a User Interface prototype with a bit complex components. So, my goal is IT agencies or even individual developers could use this prototype to brings insight for their future projects. 19 | 20 | Have a look at the preview of [React Admin UI](https://react-admin-ui-v1.vercel.app/) for a comprehensive list of prototype's features, core values and use cases. 21 | 22 |
23 |

24 | preview 25 |

26 |
27 | 28 | This repository contains the **core system of React Admin UI Prototype**, splitted into two different directories. Backend is for the JSON API (It is already configured for Vercel deployment), and Frontend is for the whole User Interface prototype. 29 | 30 | ## 💎  Features and Consist of 31 | 32 | - ⚡️ React 18 TypeScript with Vite 33 | - 🎯 Declarative Routing with React Router v6 34 | - 📋 Seamless Data Fetching with React Query v5 35 | - ✨ Optimized Icons with React Icons v5 36 | - 🎨 Tailwind CSS v3 as the Styling Foundation 37 | - 👓 Daisy UI v4 as the Base Design System 38 | - 🕶 Material UI v5 for optimized Data Grid 39 | - 📊 Beautiful Charts with Recharts v2 40 | - 🤯 And many more... 41 | 42 | ## 🚀  Installation and How to use 43 | 44 | See below for a quickstart installation and usage examples. 45 | 46 |
47 | Backend 48 | 49 | Install all dependencies listed in `package.json` inside backend directory. 50 | 51 | ```bash 52 | cd backend 53 | ``` 54 | 55 | ```bash 56 | npm install 57 | ``` 58 | 59 | By default, I already deployed the API to run in Vercel environment. The live API can be accessed from [https://react-admin-ui-v1-api.vercel.app/](https://react-admin-ui-v1-api.vercel.app/). However, in case you would like to configure the backend by yourself, you can run below. 60 | 61 | ```bash 62 | nodemon ./src/index.ts 63 | ``` 64 | 65 | And the API can be accessed locally from [http://localhost:5000](http://localhost:5000). 66 | 67 |
68 | 69 |
70 | Frontend 71 | 72 | Install all dependencies listed in `package.json` inside frontend directory. 73 | 74 | ```bash 75 | cd frontend 76 | ``` 77 | 78 | ```bash 79 | npm install 80 | ``` 81 | 82 | ```bash 83 | npm run dev 84 | ``` 85 | 86 | If you would like to change the default API endpoint, you can go to [ApiCollection.tsx](/frontend/src/api/ApiCollection.tsx). 87 | 88 | And then, the app can be accessed from [http://localhost:5173/](http://localhost:5173/). 89 | 90 |
91 | 92 | ## 📫  Have a question? Would like to chat? Ran into a problem? 93 | 94 | Obviously you can always **reach out to me directly** via a formal approach such as [Email](mailto:franswinata6@gmail.com) or [LinkedIn](https://www.linkedin.com/in/fransachmadhw/). 95 | 96 | ## 🤝  Found a bug? Suggesting a specific feature? 97 | 98 | Feel free to **file a new issue** with a respective title and description on the the [fransachmadhw/react_admin_ui_v1](https://github.com/fransachmadhw/react_admin_ui_v1/issues) repository. If you already found a solution to your problem, **we would love to review your pull request**! 99 | 100 | ## ✅  Requirements 101 | 102 | React Admin UI requires a **Node version higher or equal to 20.11.0 LTS**. Have a look at the `dependencies` and `devDependencies` section in the _package.json_ inside [backend](/backend/package.json) and [frontend](/frontend/package.json) to find the **current list of the requirements** of React Admin UI. 103 | 104 | ## 📘  License 105 | 106 | React Admin Dashboard UI Prototype is released under the terms of the [BSD-3-Clause](LICENSE). 107 | -------------------------------------------------------------------------------- /backend/src/app.ts: -------------------------------------------------------------------------------- 1 | import express from 'express'; 2 | import morgan from 'morgan'; 3 | import helmet from 'helmet'; 4 | import cors from 'cors'; 5 | 6 | import * as middlewares from './middlewares'; 7 | import api from './api'; 8 | // import MessageResponse from './interfaces/MessageResponse'; 9 | import users from './data/users'; 10 | import products from './data/products'; 11 | import orders from './data/orders'; 12 | import posts from './data/posts'; 13 | import topDeals from './data/topDeals'; 14 | import totalUsers from './data/totalUsers'; 15 | import totalProducts from './data/totalProducts'; 16 | import totalRatio from './data/totalRatio'; 17 | import totalRevenue from './data/totalRevenue'; 18 | import totalVisit from './data/totalVisit'; 19 | import totalProfit from './data/totalProfit'; 20 | import totalSource from './data/totalSource'; 21 | import totalRevenueByProducts from './data/totalRevenueByProducts'; 22 | import notes from './data/notes'; 23 | import logs from './data/logs'; 24 | 25 | require('dotenv').config(); 26 | 27 | const app = express(); 28 | 29 | app.use(morgan('dev')); 30 | app.use(helmet()); 31 | app.use(cors()); 32 | app.use(express.json()); 33 | 34 | app.get('/', (req, res) => { 35 | const navigationLinks = { 36 | user: '/users', 37 | user1: '/users/:1', 38 | products: '/products', 39 | products1: '/products/1', 40 | orders: '/orders', 41 | posts: '/posts', 42 | notes: '/notes', 43 | logs: '/logs', 44 | }; 45 | 46 | let htmlResponse = 47 | '

🦄🌈✨ React Dashboard Admin V1 API ✨🌈🦄

'; 48 | htmlResponse += '
    '; 49 | for (const [route, url] of Object.entries(navigationLinks)) { 50 | htmlResponse += `
  • ${route}
  • `; 51 | } 52 | htmlResponse += '
'; 53 | 54 | // Send HTML response 55 | res.send(htmlResponse); 56 | }); 57 | 58 | // GET USERS 59 | app.get('/users', (req, res) => { 60 | res.json(users); 61 | }); 62 | 63 | // GET A USER 64 | app.get('/users/:id', (req, res) => { 65 | const user = users.find( 66 | // eslint-disable-next-line @typescript-eslint/comma-dangle 67 | (usera) => usera.id === parseInt(req.params.id) 68 | ); 69 | 70 | res.json(user); 71 | }); 72 | 73 | // GET PRODUCTS 74 | app.get('/products', (req, res) => { 75 | res.json(products); 76 | }); 77 | 78 | // GET A PRODUCT 79 | app.get('/products/:id', (req, res) => { 80 | const product = products.find( 81 | // eslint-disable-next-line @typescript-eslint/comma-dangle 82 | (producta) => producta.id === parseInt(req.params.id) 83 | ); 84 | 85 | res.json(product); 86 | }); 87 | 88 | // GET ORDERS 89 | app.get('/orders', (req, res) => { 90 | res.json(orders); 91 | }); 92 | 93 | // GET POSTS 94 | app.get('/posts', (req, res) => { 95 | res.json(posts); 96 | }); 97 | 98 | // GET TOP DEAL USERS 99 | app.get('/topdeals', (req, res) => { 100 | res.json(topDeals); 101 | }); 102 | 103 | // GET TOTAL USERS 104 | app.get('/totalusers', (req, res) => { 105 | res.json(totalUsers); 106 | }); 107 | 108 | // GET TOTAL PRODUCTS 109 | app.get('/totalproducts', (req, res) => { 110 | res.json(totalProducts); 111 | }); 112 | 113 | // GET TOTAL RATIO 114 | app.get('/totalratio', (req, res) => { 115 | res.json(totalRatio); 116 | }); 117 | 118 | // GET TOTAL REVENUE 119 | app.get('/totalrevenue', (req, res) => { 120 | res.json(totalRevenue); 121 | }); 122 | 123 | // GET TOTAL SOURCE 124 | app.get('/totalsource', (req, res) => { 125 | res.json(totalSource); 126 | }); 127 | 128 | // GET TOTAL VISIT 129 | app.get('/totalvisit', (req, res) => { 130 | res.json(totalVisit); 131 | }); 132 | 133 | // GET TOTAL PROFIT 134 | app.get('/totalprofit', (req, res) => { 135 | res.json(totalProfit); 136 | }); 137 | 138 | // GET TOTAL REVENUE BY PRODUCTS 139 | app.get('/totalrevenue-by-product', (req, res) => { 140 | res.json(totalRevenueByProducts); 141 | }); 142 | 143 | // GET NOTES 144 | app.get('/notes', (req, res) => { 145 | const { q } = req.query; 146 | const keys = ['title', 'body']; 147 | 148 | interface Note { 149 | id: number; 150 | title: string; 151 | body: string; 152 | date: string; 153 | author: string; 154 | } 155 | 156 | const search = (data: Note[]) => { 157 | return data.filter((item) => 158 | keys.some((key) => 159 | (item as any)[key].toLowerCase().includes(q as string) 160 | ) 161 | ); 162 | }; 163 | 164 | q 165 | ? res.json(search(notes).slice(0, 10)) 166 | : res.json(notes.slice(0, 10)); 167 | }); 168 | 169 | // GET LOGS 170 | app.get('/logs', (req, res) => { 171 | res.json(logs); 172 | }); 173 | 174 | app.use('/api/v1', api); 175 | 176 | app.use(middlewares.notFound); 177 | app.use(middlewares.errorHandler); 178 | 179 | export default app; 180 | -------------------------------------------------------------------------------- /backend/src/data/users.ts: -------------------------------------------------------------------------------- 1 | let users = [ 2 | { 3 | id: 1, 4 | img: 'https://images.pexels.com/photos/8405873/pexels-photo-8405873.jpeg?auto=compress&cs=tinysrgb&w=1600&lazy=load', 5 | lastName: 'Hubbard', 6 | firstName: 'Eula', 7 | email: 'hubbard@gmail.com', 8 | phone: '423 452 729', 9 | createdAt: '05.07.2023', 10 | verified: true, 11 | }, 12 | { 13 | id: 2, 14 | img: 'https://images.pexels.com/photos/1181519/pexels-photo-1181519.jpeg?auto=compress&cs=tinysrgb&w=1600', 15 | lastName: 'Manning', 16 | firstName: 'Stella', 17 | email: 'manning@gmail.com', 18 | phone: '422 426 715', 19 | createdAt: '03.07.2023', 20 | verified: true, 21 | }, 22 | { 23 | id: 3, 24 | img: 'https://images.pexels.com/photos/1587009/pexels-photo-1587009.jpeg?auto=compress&cs=tinysrgb&w=1600', 25 | lastName: 'Greer', 26 | firstName: 'Mary', 27 | email: 'greer@hottmail.com', 28 | phone: '563 632 325', 29 | createdAt: '02.07.2023', 30 | verified: true, 31 | }, 32 | { 33 | id: 4, 34 | img: 'https://images.pexels.com/photos/871495/pexels-photo-871495.jpeg?auto=compress&cs=tinysrgb&w=1600', 35 | lastName: 'Williamson', 36 | firstName: 'Mildred', 37 | email: 'williamson@gmail.com', 38 | phone: '534 522 125', 39 | createdAt: '12.06.2023', 40 | verified: true, 41 | }, 42 | { 43 | id: 5, 44 | img: 'https://images.pexels.com/photos/1758144/pexels-photo-1758144.jpeg?auto=compress&cs=tinysrgb&w=1600', 45 | lastName: 'Gross', 46 | firstName: 'Jose', 47 | email: 'gobtagbes@yahoo.com', 48 | phone: '462 252 624', 49 | createdAt: '10.06.2023', 50 | }, 51 | { 52 | id: 6, 53 | img: 'https://images.pexels.com/photos/769745/pexels-photo-769745.jpeg?auto=compress&cs=tinysrgb&w=1600', 54 | lastName: 'Sharp', 55 | firstName: 'Jeremy', 56 | email: 'vulca.eder@mail.com', 57 | phone: '735 523 563', 58 | createdAt: '11.05.2023', 59 | verified: true, 60 | }, 61 | { 62 | id: 7, 63 | img: 'https://images.pexels.com/photos/1043474/pexels-photo-1043474.jpeg?auto=compress&cs=tinysrgb&w=1600', 64 | lastName: 'Lowe', 65 | firstName: 'Christina', 66 | email: 'reso.bilic@gmail.com', 67 | phone: '235 734 574', 68 | createdAt: '04.05.2023', 69 | }, 70 | { 71 | id: 8, 72 | img: 'https://images.pexels.com/photos/428364/pexels-photo-428364.jpeg?auto=compress&cs=tinysrgb&w=1600', 73 | lastName: 'Dean', 74 | firstName: 'Garrett', 75 | email: 'codaic@mail.com', 76 | phone: '377 346 834', 77 | createdAt: '08.04.2023', 78 | verified: true, 79 | }, 80 | { 81 | id: 9, 82 | img: 'https://images.pexels.com/photos/1181686/pexels-photo-1181686.jpeg?auto=compress&cs=tinysrgb&w=1600', 83 | lastName: 'Parsons', 84 | firstName: 'Leah', 85 | email: 'parsons@gmail.com', 86 | phone: '745 677 345', 87 | createdAt: '04.04.2023', 88 | }, 89 | { 90 | id: 10, 91 | img: 'https://images.pexels.com/photos/775358/pexels-photo-775358.jpeg?auto=compress&cs=tinysrgb&w=1600', 92 | lastName: 'Reid', 93 | firstName: 'Elnora', 94 | email: 'elnora@gmail.com', 95 | phone: '763 345 346', 96 | createdAt: '01.04.2023', 97 | verified: true, 98 | }, 99 | { 100 | id: 11, 101 | img: 'https://images.pexels.com/photos/762020/pexels-photo-762020.jpeg?auto=compress&cs=tinysrgb&w=1600', 102 | lastName: 'Dunn', 103 | firstName: 'Gertrude', 104 | email: 'gertrude@gmail.com', 105 | phone: '124 456 789', 106 | createdAt: '05.04.2023', 107 | verified: true, 108 | }, 109 | { 110 | id: 12, 111 | img: 'https://images.pexels.com/photos/774095/pexels-photo-774095.jpeg?auto=compress&cs=tinysrgb&w=1600', 112 | lastName: 'Williams', 113 | firstName: 'Mark', 114 | email: 'williams@hotmail.com', 115 | phone: '626 235 345', 116 | createdAt: '01.03.2023', 117 | }, 118 | { 119 | id: 13, 120 | img: 'https://images.pexels.com/photos/761977/pexels-photo-761977.jpeg?auto=compress&cs=tinysrgb&w=1600', 121 | lastName: 'Cruz', 122 | firstName: 'Charlotte', 123 | email: 'charlotte@gmail.com', 124 | phone: '673 547 343', 125 | createdAt: '03.02.2023', 126 | }, 127 | { 128 | id: 14, 129 | img: 'https://images.pexels.com/photos/927022/pexels-photo-927022.jpeg?auto=compress&cs=tinysrgb&w=1600', 130 | lastName: 'Harper', 131 | firstName: 'Sara', 132 | email: 'harper@hotmail.com', 133 | phone: '734 234 234', 134 | createdAt: '01.02.2023', 135 | }, 136 | { 137 | id: 15, 138 | img: 'https://images.pexels.com/photos/8405873/pexels-photo-8405873.jpeg?auto=compress&cs=tinysrgb&w=1600&lazy=load', 139 | lastName: 'Griffin', 140 | firstName: 'Eric', 141 | email: 'griffin@gmail.com', 142 | phone: '763 234 235', 143 | createdAt: '01.01.2023', 144 | }, 145 | ]; 146 | 147 | export default users; 148 | -------------------------------------------------------------------------------- /frontend/src/pages/Products.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { GridColDef } from '@mui/x-data-grid'; 3 | import DataTable from '../components/DataTable'; 4 | import { fetchProducts } from '../api/ApiCollection'; 5 | import { useQuery } from '@tanstack/react-query'; 6 | import toast from 'react-hot-toast'; 7 | import AddData from '../components/AddData'; 8 | 9 | const Products = () => { 10 | const [isOpen, setIsOpen] = React.useState(false); 11 | const { isLoading, isError, isSuccess, data } = useQuery({ 12 | queryKey: ['allproducts'], 13 | queryFn: fetchProducts, 14 | }); 15 | 16 | const columns: GridColDef[] = [ 17 | { field: 'id', headerName: 'ID', width: 90 }, 18 | { 19 | field: 'img', 20 | headerName: 'Product', 21 | minWidth: 300, 22 | flex: 1, 23 | renderCell: (params) => { 24 | return ( 25 |
26 |
27 | product-picture 32 |
33 | 34 | {params.row.title} 35 | 36 |
37 | ); 38 | }, 39 | }, 40 | // { 41 | // field: 'title', 42 | // type: 'string', 43 | // headerName: 'Title', 44 | // width: 250, 45 | // }, 46 | { 47 | field: 'color', 48 | type: 'string', 49 | headerName: 'Color', 50 | minWidth: 100, 51 | flex: 1, 52 | }, 53 | { 54 | field: 'price', 55 | type: 'string', 56 | headerName: 'Price', 57 | minWidth: 100, 58 | flex: 1, 59 | }, 60 | { 61 | field: 'producer', 62 | headerName: 'Producer', 63 | type: 'string', 64 | minWidth: 100, 65 | flex: 1, 66 | }, 67 | { 68 | field: 'createdAt', 69 | headerName: 'Created At', 70 | minWidth: 100, 71 | type: 'string', 72 | flex: 1, 73 | }, 74 | { 75 | field: 'inStock', 76 | headerName: 'In Stock', 77 | minWidth: 80, 78 | type: 'boolean', 79 | flex: 1, 80 | }, 81 | ]; 82 | 83 | React.useEffect(() => { 84 | if (isLoading) { 85 | toast.loading('Loading...', { id: 'promiseProducts' }); 86 | } 87 | if (isError) { 88 | toast.error('Error while getting the data!', { 89 | id: 'promiseProducts', 90 | }); 91 | } 92 | if (isSuccess) { 93 | toast.success('Got the data successfully!', { 94 | id: 'promiseProducts', 95 | }); 96 | } 97 | }, [isError, isLoading, isSuccess]); 98 | 99 | return ( 100 |
101 |
102 |
103 |
104 |

105 | Products 106 |

107 | {data && data.length > 0 && ( 108 | 109 | {data.length} Products Found 110 | 111 | )} 112 |
113 | 121 |
122 | 123 | {isLoading ? ( 124 | 130 | ) : isSuccess ? ( 131 | 137 | ) : ( 138 | <> 139 | 145 |
146 | Error while getting the data! 147 |
148 | 149 | )} 150 | 151 | {isOpen && ( 152 | 157 | )} 158 |
159 |
160 | ); 161 | }; 162 | 163 | export default Products; 164 | -------------------------------------------------------------------------------- /frontend/src/pages/Users.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { GridColDef } from '@mui/x-data-grid'; 3 | import DataTable from '../components/DataTable'; 4 | import { fetchUsers } from '../api/ApiCollection'; 5 | import { useQuery } from '@tanstack/react-query'; 6 | import toast from 'react-hot-toast'; 7 | import AddData from '../components/AddData'; 8 | 9 | const Users = () => { 10 | const [isOpen, setIsOpen] = React.useState(false); 11 | const { isLoading, isError, isSuccess, data } = useQuery({ 12 | queryKey: ['allusers'], 13 | queryFn: fetchUsers, 14 | }); 15 | 16 | const columns: GridColDef[] = [ 17 | { field: 'id', headerName: 'ID', width: 90 }, 18 | { 19 | field: 'firstName', 20 | headerName: 'Name', 21 | minWidth: 220, 22 | flex: 1, 23 | renderCell: (params) => { 24 | return ( 25 |
26 |
27 |
28 | user-picture 32 |
33 |
34 | 35 | {params.row.firstName} {params.row.lastName} 36 | 37 |
38 | ); 39 | }, 40 | }, 41 | { 42 | field: 'email', 43 | type: 'string', 44 | headerName: 'Email', 45 | minWidth: 200, 46 | flex: 1, 47 | }, 48 | { 49 | field: 'phone', 50 | type: 'string', 51 | headerName: 'Phone', 52 | minWidth: 120, 53 | flex: 1, 54 | }, 55 | { 56 | field: 'createdAt', 57 | headerName: 'Created At', 58 | minWidth: 100, 59 | type: 'string', 60 | flex: 1, 61 | }, 62 | // { 63 | // field: 'fullName', 64 | // headerName: 'Full name', 65 | // description: 66 | // 'This column has a value getter and is not sortable.', 67 | // sortable: false, 68 | // width: 160, 69 | // valueGetter: (params: GridValueGetterParams) => 70 | // `${params.row.firstName || ''} ${params.row.lastName || ''}`, 71 | // }, 72 | { 73 | field: 'verified', 74 | headerName: 'Verified', 75 | width: 80, 76 | type: 'boolean', 77 | flex: 1, 78 | }, 79 | ]; 80 | 81 | React.useEffect(() => { 82 | if (isLoading) { 83 | toast.loading('Loading...', { id: 'promiseUsers' }); 84 | } 85 | if (isError) { 86 | toast.error('Error while getting the data!', { 87 | id: 'promiseUsers', 88 | }); 89 | } 90 | if (isSuccess) { 91 | toast.success('Got the data successfully!', { 92 | id: 'promiseUsers', 93 | }); 94 | } 95 | }, [isError, isLoading, isSuccess]); 96 | 97 | return ( 98 |
99 |
100 |
101 |
102 |

103 | Users 104 |

105 | {data && data.length > 0 && ( 106 | 107 | {data.length} Users Found 108 | 109 | )} 110 |
111 | 119 |
120 | {isLoading ? ( 121 | 127 | ) : isSuccess ? ( 128 | 134 | ) : ( 135 | <> 136 | 142 |
143 | Error while getting the data! 144 |
145 | 146 | )} 147 | 148 | {isOpen && ( 149 | 154 | )} 155 |
156 |
157 | ); 158 | }; 159 | 160 | export default Users; 161 | -------------------------------------------------------------------------------- /frontend/src/components/charts/data.ts: -------------------------------------------------------------------------------- 1 | import { 2 | MdGroup, 3 | MdInventory2, 4 | MdAssessment, 5 | MdSwapHorizontalCircle, 6 | } from 'react-icons/md'; 7 | 8 | export const totalUsers = { 9 | color: '#8884d8', 10 | IconBox: MdGroup, 11 | title: 'Total Users', 12 | number: '11.238', 13 | dataKey: 'users', 14 | percentage: 45, 15 | chartData: [ 16 | { name: 'Sun', users: 400 }, 17 | { name: 'Mon', users: 600 }, 18 | { name: 'Tue', users: 500 }, 19 | { name: 'Wed', users: 700 }, 20 | { name: 'Thu', users: 400 }, 21 | { name: 'Fri', users: 500 }, 22 | { name: 'Sat', users: 450 }, 23 | ], 24 | }; 25 | 26 | export const totalProducts = { 27 | color: 'skyblue', 28 | IconBox: MdInventory2, 29 | title: 'Total Products', 30 | number: '238', 31 | dataKey: 'products', 32 | percentage: 21, 33 | chartData: [ 34 | { name: 'Sun', products: 400 }, 35 | { name: 'Mon', products: 600 }, 36 | { name: 'Tue', products: 500 }, 37 | { name: 'Wed', products: 700 }, 38 | { name: 'Thu', products: 400 }, 39 | { name: 'Fri', products: 500 }, 40 | { name: 'Sat', products: 450 }, 41 | ], 42 | }; 43 | export const totalRevenue = { 44 | color: 'teal', 45 | IconBox: MdAssessment, 46 | title: 'Total Revenue', 47 | number: '$56.432', 48 | dataKey: 'revenue', 49 | percentage: -12, 50 | chartData: [ 51 | { name: 'Sun', revenue: 400 }, 52 | { name: 'Mon', revenue: 600 }, 53 | { name: 'Tue', revenue: 500 }, 54 | { name: 'Wed', revenue: 700 }, 55 | { name: 'Thu', revenue: 400 }, 56 | { name: 'Fri', revenue: 500 }, 57 | { name: 'Sat', revenue: 450 }, 58 | ], 59 | }; 60 | export const totalRatio = { 61 | color: 'gold', 62 | IconBox: MdSwapHorizontalCircle, 63 | title: 'Total Ratio', 64 | number: '2.6', 65 | dataKey: 'ratio', 66 | percentage: 12, 67 | chartData: [ 68 | { name: 'Sun', ratio: 400 }, 69 | { name: 'Mon', ratio: 600 }, 70 | { name: 'Tue', ratio: 500 }, 71 | { name: 'Wed', ratio: 700 }, 72 | { name: 'Thu', ratio: 400 }, 73 | { name: 'Fri', ratio: 500 }, 74 | { name: 'Sat', ratio: 450 }, 75 | ], 76 | }; 77 | 78 | export const totalVisit = { 79 | title: 'Total Visit', 80 | color: '#FF8042', 81 | dataKey: 'visit', 82 | chartData: [ 83 | { 84 | name: 'Sun', 85 | visit: 4000, 86 | }, 87 | { 88 | name: 'Mon', 89 | visit: 3000, 90 | }, 91 | { 92 | name: 'Tue', 93 | visit: 2000, 94 | }, 95 | { 96 | name: 'Wed', 97 | visit: 2780, 98 | }, 99 | { 100 | name: 'Thu', 101 | visit: 1890, 102 | }, 103 | { 104 | name: 'Fri', 105 | visit: 2390, 106 | }, 107 | { 108 | name: 'Sat', 109 | visit: 3490, 110 | }, 111 | ], 112 | }; 113 | 114 | export const totalProfit = { 115 | title: 'Profit Earned', 116 | color: '#8884d8', 117 | dataKey: 'profit', 118 | chartData: [ 119 | { 120 | name: 'Sun', 121 | profit: 4000, 122 | }, 123 | { 124 | name: 'Mon', 125 | profit: 3000, 126 | }, 127 | { 128 | name: 'Tue', 129 | profit: 2000, 130 | }, 131 | { 132 | name: 'Wed', 133 | profit: 2780, 134 | }, 135 | { 136 | name: 'Thu', 137 | profit: 1890, 138 | }, 139 | { 140 | name: 'Fri', 141 | profit: 2390, 142 | }, 143 | { 144 | name: 'Sat', 145 | profit: 3490, 146 | }, 147 | ], 148 | }; 149 | 150 | export const totalSource = { 151 | title: 'Leads by Source', 152 | // color: '#8884d8', 153 | dataKey: 'value', 154 | chartPieData: [ 155 | { name: 'Mobile', value: 350, color: '#0088FE' }, 156 | { name: 'Desktop', value: 250, color: '#00C49F' }, 157 | { name: 'Laptop', value: 325, color: '#FFBB28' }, 158 | { name: 'Tablet', value: 75, color: '#FF8042' }, 159 | ], 160 | }; 161 | 162 | export const totalRevenueByProducts = { 163 | title: 'Revenue by Products', 164 | // color: '#8884d8', 165 | dataKey: 'name', 166 | chartAreaData: [ 167 | { 168 | name: 'Sun', 169 | smartphones: 4000, 170 | consoles: 2400, 171 | laptops: 2400, 172 | others: 1000, 173 | }, 174 | { 175 | name: 'Mon', 176 | smartphones: 3000, 177 | consoles: 1398, 178 | laptops: 2210, 179 | others: 700, 180 | }, 181 | { 182 | name: 'Tue', 183 | smartphones: 2000, 184 | consoles: 9800, 185 | laptops: 2290, 186 | others: 675, 187 | }, 188 | { 189 | name: 'Wed', 190 | smartphones: 2780, 191 | consoles: 3908, 192 | laptops: 2000, 193 | others: 685, 194 | }, 195 | { 196 | name: 'Thu', 197 | smartphones: 1890, 198 | consoles: 4800, 199 | laptops: 2181, 200 | others: 675, 201 | }, 202 | { 203 | name: 'Fri', 204 | smartphones: 2390, 205 | consoles: 3800, 206 | laptops: 2500, 207 | others: 650, 208 | }, 209 | { 210 | name: 'Sat', 211 | smartphones: 3490, 212 | consoles: 4300, 213 | laptops: 2100, 214 | others: 1075, 215 | }, 216 | ], 217 | }; 218 | -------------------------------------------------------------------------------- /backend/src/data/orders.ts: -------------------------------------------------------------------------------- 1 | const orders = [ 2 | { 3 | id: 1, 4 | product: 5 | 'https://venturebeat.com/wp-content/uploads/2015/07/As_AO1-131_gray_nonglare_win10_03.png?fit=1338%2C1055&strip=all', 6 | title: 'Acer Laptop 16 KL-4804', 7 | profile: 8 | 'https://images.pexels.com/photos/3785079/pexels-photo-3785079.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1', 9 | recipient: 'Teddy Firgantoro', 10 | address: 'Jln. Gatot Subroto No. 620, Palu', 11 | date: '01.02.2024', 12 | total: '$599.99', 13 | status: 'Pending', 14 | }, 15 | { 16 | id: 2, 17 | product: 18 | 'https://img.productz.com/review_image/102489/preview_sony-kdl-50w800b-50-inch-hdtv-review-superb-picture-102489.png', 19 | title: 'Sony Bravia KDL-47W805A', 20 | profile: 21 | 'https://images.pexels.com/photos/761977/pexels-photo-761977.jpeg?auto=compress&cs=tinysrgb&w=1600', 22 | recipient: 'Laila Tari Puspasari', 23 | address: 'Jln. Jakarta No. 468, Malang', 24 | date: '25.01.2024', 25 | total: '$879.99', 26 | status: 'Dispatch', 27 | }, 28 | { 29 | id: 3, 30 | product: 31 | 'https://www.smartworld.it/wp-content/uploads/2019/09/High_Resolution_PNG-MX-Master-3-LEFT-GRAPHITE.png', 32 | title: 'Logitech MX Master 3', 33 | profile: 34 | 'https://images.pexels.com/photos/1547971/pexels-photo-1547971.jpeg', 35 | recipient: 'Kezia Yulia', 36 | address: 'Jr. Qrisdoren No. 731, Semarang', 37 | date: '03.02.2024', 38 | total: '$879.99', 39 | status: 'Cancelled', 40 | }, 41 | { 42 | id: 4, 43 | product: 44 | 'https://www.signify.com/b-dam/signify/en-aa/about/news/2020/20200903-movie-night-essentials-popcorn-ice-cream-and-the-new-philips-hue-play-gradient-lightstrip/packaging-lighstrip.png', 45 | title: 'Philips Hue Play Gradient', 46 | profile: 47 | 'https://images.pexels.com/photos/428364/pexels-photo-428364.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1', 48 | recipient: 'Bambang Najmudin', 49 | address: 'Jr. Supono No. 770, DI Yogyakarta', 50 | date: '01.02.2024', 51 | total: '$45.99', 52 | status: 'Completed', 53 | }, 54 | { 55 | id: 5, 56 | product: 57 | 'http://images.samsung.com/is/image/samsung/uk-led-tv-hg40ed670ck-hg40ed670ckxxu-001-front', 58 | title: 'Samsung TV 4K SmartTV', 59 | profile: 60 | 'https://images.pexels.com/photos/681644/pexels-photo-681644.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1', 61 | recipient: 'Wulan Pratiwi', 62 | address: 'Psr. Surapati No. 982, Denpasar', 63 | date: '01.02.2024', 64 | total: '$549.99', 65 | status: 'Dispatch', 66 | }, 67 | { 68 | id: 6, 69 | product: 70 | 'https://store.sony.com.au/on/demandware.static/-/Sites-sony-master-catalog/default/dw1b537bbb/images/PLAYSTATION5W/PLAYSTATION5W.png', 71 | title: 'Playstation 5 Digital Edition', 72 | profile: 73 | 'https://images.pexels.com/photos/623305/pexels-photo-623305.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1', 74 | recipient: 'Darimin Permadi', 75 | address: 'Jln. Ekonomi No. 524, Palangka Raya', 76 | date: '15.01.2024', 77 | total: '$759.99', 78 | status: 'Completed', 79 | }, 80 | { 81 | id: 7, 82 | product: 83 | 'https://www.pngmart.com/files/6/Dell-Laptop-PNG-Image.png', 84 | title: 'Dell Laptop KR211822', 85 | profile: 86 | 'https://images.pexels.com/photos/3361154/pexels-photo-3361154.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1', 87 | recipient: 'Salsabila Mutia Novitasari', 88 | address: 'Jln. Pasteur No. 981, Jakarta Barat', 89 | date: '28.01.2024', 90 | total: '$499.99', 91 | status: 'Completed', 92 | }, 93 | { 94 | id: 8, 95 | product: 'https://raylo.imgix.net/iphone-14-blue.png', 96 | title: 'Apple Iphone 14 Pro Max', 97 | profile: 98 | 'https://images.pexels.com/photos/460031/pexels-photo-460031.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1', 99 | recipient: 'Lasmanto Putra', 100 | address: 'Ki. Bakti No. 754, Semarang', 101 | date: '08.02.2024', 102 | total: '$799.49', 103 | status: 'Pending', 104 | }, 105 | { 106 | id: 9, 107 | product: 108 | 'https://www.smartworld.it/wp-content/uploads/2019/09/High_Resolution_PNG-MX-Master-3-LEFT-GRAPHITE.png', 109 | title: 'Logitech MX Master 3', 110 | profile: 111 | 'https://images.pexels.com/photos/1083855/pexels-photo-1083855.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1', 112 | recipient: 'Cawisadi Hardiansyah', 113 | address: 'Ds. Sutarto No. 418, Surakarta', 114 | date: '10.02.2024', 115 | total: '$79.49', 116 | status: 'Cancelled', 117 | }, 118 | { 119 | id: 10, 120 | product: 121 | 'http://images.samsung.com/is/image/samsung/uk-led-tv-hg40ed670ck-hg40ed670ckxxu-001-front', 122 | title: 'Samsung TV 4K SmartTV', 123 | profile: 124 | 'https://images.pexels.com/photos/1832326/pexels-photo-1832326.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1', 125 | recipient: 'Amelia Utami', 126 | address: 'Jr. Bank Dagang Negara No. 893, Denpasar', 127 | date: '14.02.2024', 128 | total: '$679.49', 129 | status: 'Dispatch', 130 | }, 131 | ]; 132 | 133 | export default orders; 134 | -------------------------------------------------------------------------------- /frontend/src/pages/Home.tsx: -------------------------------------------------------------------------------- 1 | // import React from 'react'; 2 | import TopDealsBox from '../components/topDealsBox/TopDealsBox'; 3 | import ChartBox from '../components/charts/ChartBox'; 4 | import { useQuery } from '@tanstack/react-query'; 5 | import { 6 | MdGroup, 7 | MdInventory2, 8 | MdAssessment, 9 | MdSwapHorizontalCircle, 10 | } from 'react-icons/md'; 11 | import { 12 | fetchTotalProducts, 13 | fetchTotalProfit, 14 | fetchTotalRatio, 15 | fetchTotalRevenue, 16 | fetchTotalRevenueByProducts, 17 | fetchTotalSource, 18 | fetchTotalUsers, 19 | fetchTotalVisit, 20 | } from '../api/ApiCollection'; 21 | 22 | const Home = () => { 23 | const queryGetTotalUsers = useQuery({ 24 | queryKey: ['totalusers'], 25 | queryFn: fetchTotalUsers, 26 | }); 27 | 28 | const queryGetTotalProducts = useQuery({ 29 | queryKey: ['totalproducts'], 30 | queryFn: fetchTotalProducts, 31 | }); 32 | 33 | const queryGetTotalRatio = useQuery({ 34 | queryKey: ['totalratio'], 35 | queryFn: fetchTotalRatio, 36 | }); 37 | 38 | const queryGetTotalRevenue = useQuery({ 39 | queryKey: ['totalrevenue'], 40 | queryFn: fetchTotalRevenue, 41 | }); 42 | 43 | const queryGetTotalSource = useQuery({ 44 | queryKey: ['totalsource'], 45 | queryFn: fetchTotalSource, 46 | }); 47 | 48 | const queryGetTotalRevenueByProducts = useQuery({ 49 | queryKey: ['totalrevenue-by-products'], 50 | queryFn: fetchTotalRevenueByProducts, 51 | }); 52 | 53 | const queryGetTotalVisit = useQuery({ 54 | queryKey: ['totalvisit'], 55 | queryFn: fetchTotalVisit, 56 | }); 57 | 58 | const queryGetTotalProfit = useQuery({ 59 | queryKey: ['totalprofit'], 60 | queryFn: fetchTotalProfit, 61 | }); 62 | 63 | return ( 64 | // screen 65 |
66 | {/* grid */} 67 |
68 |
69 | 70 |
71 |
72 | 80 |
81 |
82 | 90 |
91 |
92 | 99 |
100 |
101 | 109 |
110 |
111 | 119 |
120 |
121 | 128 |
129 |
130 | 137 |
138 |
139 | 146 |
147 |
148 |
149 | ); 150 | }; 151 | 152 | export default Home; 153 | -------------------------------------------------------------------------------- /frontend/src/pages/Login.tsx: -------------------------------------------------------------------------------- 1 | // import React from 'react'; 2 | import ChangeThemes from '../components/ChangesThemes'; 3 | import { DiReact } from 'react-icons/di'; 4 | import { useNavigate } from 'react-router-dom'; 5 | 6 | const Login = () => { 7 | const navigate = useNavigate(); 8 | return ( 9 | // screen 10 |
11 | {/* container */} 12 |
13 | {/* theme */} 14 |
15 | 16 |
17 |
18 |
19 | 20 | 21 | React Dashboard 22 | 23 |
24 | 25 | Hello, 👋 Welcome Back! 26 | 27 |
28 | 44 | 63 |
64 |
65 | 75 |
76 | 80 | Forgot Password? 81 | 82 |
83 |
navigate('/')} 85 | className="btn btn-block btn-primary" 86 | > 87 | Log In 88 |
89 |
OR
90 |
91 | 98 | 105 | 117 |
118 |
119 |
120 |
121 |
122 | ); 123 | }; 124 | 125 | export default Login; 126 | -------------------------------------------------------------------------------- /frontend/src/components/calendar/CalendarBox.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { 3 | EventApi, 4 | DateSelectArg, 5 | EventClickArg, 6 | EventContentArg, 7 | formatDate, 8 | } from '@fullcalendar/core'; 9 | import FullCalendar from '@fullcalendar/react'; 10 | import dayGridPlugin from '@fullcalendar/daygrid'; 11 | import timeGridPlugin from '@fullcalendar/timegrid'; 12 | import interactionPlugin from '@fullcalendar/interaction'; 13 | // import listPlugin from '@fullcalendar/list'; 14 | import { INITIAL_EVENTS, createEventId } from './CalendarUtils'; 15 | 16 | // interface CalendarBoxProps { 17 | // weekendsVisible: boolean; 18 | // currentEvents: EventApi[]; 19 | // } 20 | 21 | const CalendarBox: React.FC = () => { 22 | const [currentEvents, setCurrentEvents] = React.useState< 23 | EventApi[] 24 | >([]); 25 | 26 | const handleDateSelect = (selectInfo: DateSelectArg) => { 27 | const title = prompt('Please enter a new title for your event'); 28 | const calendarApi = selectInfo.view.calendar; 29 | 30 | calendarApi.unselect(); // clear date selection 31 | 32 | if (title) { 33 | calendarApi.addEvent({ 34 | id: createEventId(), 35 | title, 36 | start: selectInfo.startStr, 37 | end: selectInfo.endStr, 38 | allDay: selectInfo.allDay, 39 | }); 40 | } 41 | }; 42 | 43 | const handleEventClick = (clickInfo: EventClickArg) => { 44 | if ( 45 | confirm( 46 | `Are you sure you want to delete the event '${clickInfo.event.title}'` 47 | ) 48 | ) { 49 | clickInfo.event.remove(); 50 | } 51 | }; 52 | 53 | const handleEvents = (events: EventApi[]) => { 54 | setCurrentEvents(events); 55 | }; 56 | 57 | const renderEventContent = (eventContent: EventContentArg) => { 58 | return ( 59 |
60 | {eventContent.timeText} 61 | {eventContent.event.title} 62 |
63 | ); 64 | }; 65 | 66 | const renderSidebarEvent = (event: EventApi) => { 67 | return ( 68 |
72 |
73 | 74 | {formatDate(event.start!, { 75 | year: 'numeric', 76 | month: 'short', 77 | day: 'numeric', 78 | })} 79 | 80 | {event.end && ( 81 |
until
82 | )} 83 | 84 | {formatDate(event.end!, { 85 | year: 'numeric', 86 | month: 'short', 87 | day: 'numeric', 88 | })} 89 | 90 |
91 |
{event.title}
92 |
93 | ); 94 | }; 95 | 96 | return ( 97 |
98 | {/* sidebar */} 99 |
100 | {/* heading */} 101 |

Events

102 |
103 | {currentEvents.map(renderSidebarEvent)} 104 |
105 |
106 | {currentEvents.map((event) => ( 107 |
111 |
112 | 113 | {formatDate(event.start!, { 114 | year: 'numeric', 115 | month: 'short', 116 | day: 'numeric', 117 | })} 118 | 119 | {event.end && ( 120 |
until
121 | )} 122 | 123 | {formatDate(event.end!, { 124 | year: 'numeric', 125 | month: 'short', 126 | day: 'numeric', 127 | })} 128 | 129 |
130 |
{event.title}
131 |
132 | ))} 133 |
134 |
135 | 136 | {/* calendar */} 137 |
138 | 162 |
163 |
164 | ); 165 | }; 166 | 167 | export default CalendarBox; 168 | -------------------------------------------------------------------------------- /frontend/src/components/Navbar.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Link, useNavigate } from 'react-router-dom'; 3 | import { HiBars3CenterLeft } from 'react-icons/hi2'; 4 | import { DiReact } from 'react-icons/di'; 5 | import { HiSearch, HiOutlineBell } from 'react-icons/hi'; 6 | import { RxEnterFullScreen, RxExitFullScreen } from 'react-icons/rx'; 7 | import ChangeThemes from './ChangesThemes'; 8 | import toast from 'react-hot-toast'; 9 | import { menu } from './menu/data'; 10 | import MenuItem from './menu/MenuItem'; 11 | 12 | const Navbar = () => { 13 | const [isFullScreen, setIsFullScreen] = React.useState(true); 14 | const element = document.getElementById('root'); 15 | 16 | const [isDrawerOpen, setDrawerOpen] = React.useState(false); 17 | const toggleDrawer = () => setDrawerOpen(!isDrawerOpen); 18 | 19 | const toggleFullScreen = () => { 20 | setIsFullScreen((prev) => !prev); 21 | }; 22 | 23 | const navigate = useNavigate(); 24 | 25 | React.useEffect(() => { 26 | if (isFullScreen) { 27 | document.exitFullscreen(); 28 | } else { 29 | element?.requestFullscreen({ navigationUI: 'auto' }); 30 | } 31 | }, [element, isFullScreen]); 32 | 33 | return ( 34 | // navbar screen 35 |
36 | {/* container */} 37 |
38 | {/* for mobile */} 39 |
40 | 47 |
48 | 54 |
55 |
56 | 61 |
62 | 66 | 67 | 68 | React Dashboard 69 | 70 | 71 | {menu.map((item, index) => ( 72 | 78 | ))} 79 |
80 |
81 |
82 | 83 | {/* navbar logo */} 84 | 85 | 86 | 87 | React Dashboard 88 | 89 | 90 |
91 | 92 | {/* navbar items to right */} 93 |
94 | {/* search */} 95 | 105 | 106 | {/* fullscreen */} 107 | 117 | 118 | {/* notification */} 119 | 129 | 130 | {/* theme */} 131 |
132 | 133 |
134 | 135 | {/* avatar dropdown */} 136 |
137 |
142 |
143 | foto-cowok-ganteng 147 |
148 |
149 |
    153 | 154 |
  • 155 | My Profile 156 |
  • 157 | 158 |
  • navigate('/login')}> 159 | Log Out 160 |
  • 161 |
162 |
163 |
164 |
165 | ); 166 | }; 167 | 168 | export default Navbar; 169 | -------------------------------------------------------------------------------- /frontend/src/pages/Charts.tsx: -------------------------------------------------------------------------------- 1 | // import React from 'react'; 2 | import { 3 | LineChart, 4 | Line, 5 | PieChart, 6 | Pie, 7 | Cell, 8 | AreaChart, 9 | Area, 10 | BarChart, 11 | Bar, 12 | // XAxis, 13 | // YAxis, 14 | // CartesianGrid, 15 | ResponsiveContainer, 16 | } from 'recharts'; 17 | 18 | const Charts = () => { 19 | const dataLine = [ 20 | { 21 | name: 'Page A', 22 | uv: 4000, 23 | pv: 2400, 24 | amt: 2400, 25 | }, 26 | { 27 | name: 'Page B', 28 | uv: 3000, 29 | pv: 1398, 30 | amt: 2210, 31 | }, 32 | { 33 | name: 'Page C', 34 | uv: 2000, 35 | pv: 9800, 36 | amt: 2290, 37 | }, 38 | { 39 | name: 'Page D', 40 | uv: 2780, 41 | pv: 3908, 42 | amt: 2000, 43 | }, 44 | { 45 | name: 'Page E', 46 | uv: 1890, 47 | pv: 4800, 48 | amt: 2181, 49 | }, 50 | { 51 | name: 'Page F', 52 | uv: 2390, 53 | pv: 3800, 54 | amt: 2500, 55 | }, 56 | { 57 | name: 'Page G', 58 | uv: 3490, 59 | pv: 4300, 60 | amt: 2100, 61 | }, 62 | ]; 63 | 64 | const dataPie = [ 65 | { name: 'Group A', value: 400 }, 66 | { name: 'Group B', value: 300 }, 67 | { name: 'Group C', value: 300 }, 68 | { name: 'Group D', value: 200 }, 69 | ]; 70 | const COLORS = ['#0088FE', '#00C49F', '#FFBB28', '#FF8042']; 71 | 72 | const dataArea = [ 73 | { 74 | name: 'Page A', 75 | uv: 4000, 76 | pv: 2400, 77 | amt: 2400, 78 | }, 79 | { 80 | name: 'Page B', 81 | uv: 3000, 82 | pv: 1398, 83 | amt: 2210, 84 | }, 85 | { 86 | name: 'Page C', 87 | uv: 2000, 88 | pv: 9800, 89 | amt: 2290, 90 | }, 91 | { 92 | name: 'Page D', 93 | uv: 2780, 94 | pv: 3908, 95 | amt: 2000, 96 | }, 97 | { 98 | name: 'Page E', 99 | uv: 1890, 100 | pv: 4800, 101 | amt: 2181, 102 | }, 103 | { 104 | name: 'Page F', 105 | uv: 2390, 106 | pv: 3800, 107 | amt: 2500, 108 | }, 109 | { 110 | name: 'Page G', 111 | uv: 3490, 112 | pv: 4300, 113 | amt: 2100, 114 | }, 115 | ]; 116 | 117 | const dataBar = [ 118 | { 119 | name: 'Page A', 120 | uv: 4000, 121 | pv: 2400, 122 | amt: 2400, 123 | }, 124 | { 125 | name: 'Page B', 126 | uv: 3000, 127 | pv: 1398, 128 | amt: 2210, 129 | }, 130 | { 131 | name: 'Page C', 132 | uv: 2000, 133 | pv: 9800, 134 | amt: 2290, 135 | }, 136 | { 137 | name: 'Page D', 138 | uv: 2780, 139 | pv: 3908, 140 | amt: 2000, 141 | }, 142 | { 143 | name: 'Page E', 144 | uv: 1890, 145 | pv: 4800, 146 | amt: 2181, 147 | }, 148 | { 149 | name: 'Page F', 150 | uv: 2390, 151 | pv: 3800, 152 | amt: 2500, 153 | }, 154 | { 155 | name: 'Page G', 156 | uv: 3490, 157 | pv: 4300, 158 | amt: 2100, 159 | }, 160 | ]; 161 | 162 | return ( 163 | // screen 164 |
165 | {/* container */} 166 |
167 | {/* heading */} 168 |

169 | Charts 170 |

171 | {/* grid */} 172 |
173 |
174 | 175 | 176 | 182 | 183 | 184 |
185 |
186 | 187 | 188 | 196 | {dataPie.map((_entry, index) => ( 197 | 201 | ))} 202 | 203 | 204 | 205 |
206 |
207 | 208 | 209 | {/* */} 210 | {/* */} 211 | {/* */} 212 | {/* */} 213 | 220 | 227 | 234 | 235 | 236 |
237 |
238 | 239 | 240 | 241 | 242 | 243 |
244 |
245 |
246 |
247 | ); 248 | }; 249 | 250 | export default Charts; 251 | -------------------------------------------------------------------------------- /frontend/src/api/ApiCollection.tsx: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | 3 | // GET TOP DEALS 4 | export const fetchTopDeals = async () => { 5 | const response = await axios 6 | .get('https://react-admin-ui-v1-api.vercel.app/topdeals') 7 | .then((res) => { 8 | console.log('axios get:', res.data); 9 | return res.data; 10 | }) 11 | .catch((err) => { 12 | console.log(err); 13 | throw err; 14 | }); 15 | 16 | return response; 17 | }; 18 | 19 | // GET TOTAL USERS 20 | export const fetchTotalUsers = async () => { 21 | const response = await axios 22 | .get('https://react-admin-ui-v1-api.vercel.app/totalusers') 23 | .then((res) => { 24 | console.log('axios get:', res.data); 25 | return res.data; 26 | }) 27 | .catch((err) => { 28 | console.log(err); 29 | throw err; 30 | }); 31 | 32 | return response; 33 | }; 34 | 35 | // GET TOTAL PRODUCTS 36 | export const fetchTotalProducts = async () => { 37 | const response = await axios 38 | .get('https://react-admin-ui-v1-api.vercel.app/totalproducts') 39 | .then((res) => { 40 | console.log('axios get:', res.data); 41 | return res.data; 42 | }) 43 | .catch((err) => { 44 | console.log(err); 45 | throw err; 46 | }); 47 | 48 | return response; 49 | }; 50 | 51 | // GET TOTAL RATIO 52 | export const fetchTotalRatio = async () => { 53 | const response = await axios 54 | .get('https://react-admin-ui-v1-api.vercel.app/totalratio') 55 | .then((res) => { 56 | console.log('axios get:', res.data); 57 | return res.data; 58 | }) 59 | .catch((err) => { 60 | console.log(err); 61 | throw err; 62 | }); 63 | 64 | return response; 65 | }; 66 | 67 | // GET TOTAL REVENUE 68 | export const fetchTotalRevenue = async () => { 69 | const response = await axios 70 | .get('https://react-admin-ui-v1-api.vercel.app/totalrevenue') 71 | .then((res) => { 72 | console.log('axios get:', res.data); 73 | return res.data; 74 | }) 75 | .catch((err) => { 76 | console.log(err); 77 | throw err; 78 | }); 79 | 80 | return response; 81 | }; 82 | 83 | // GET TOTAL SOURCE 84 | export const fetchTotalSource = async () => { 85 | const response = await axios 86 | .get('https://react-admin-ui-v1-api.vercel.app/totalsource') 87 | .then((res) => { 88 | console.log('axios get:', res.data); 89 | return res.data; 90 | }) 91 | .catch((err) => { 92 | console.log(err); 93 | throw err; 94 | }); 95 | 96 | return response; 97 | }; 98 | 99 | // GET TOTAL VISIT 100 | export const fetchTotalVisit = async () => { 101 | const response = await axios 102 | .get('https://react-admin-ui-v1-api.vercel.app/totalvisit') 103 | .then((res) => { 104 | console.log('axios get:', res.data); 105 | return res.data; 106 | }) 107 | .catch((err) => { 108 | console.log(err); 109 | throw err; 110 | }); 111 | 112 | return response; 113 | }; 114 | 115 | // GET TOTAL REVENUE BY PRODUCTS 116 | export const fetchTotalRevenueByProducts = async () => { 117 | const response = await axios 118 | .get( 119 | 'https://react-admin-ui-v1-api.vercel.app/totalrevenue-by-product' 120 | ) 121 | .then((res) => { 122 | console.log('axios get:', res.data); 123 | return res.data; 124 | }) 125 | .catch((err) => { 126 | console.log(err); 127 | throw err; 128 | }); 129 | 130 | return response; 131 | }; 132 | 133 | // GET TOTAL PROFIT 134 | export const fetchTotalProfit = async () => { 135 | const response = await axios 136 | .get('https://react-admin-ui-v1-api.vercel.app/totalprofit') 137 | .then((res) => { 138 | console.log('axios get:', res.data); 139 | return res.data; 140 | }) 141 | .catch((err) => { 142 | console.log(err); 143 | throw err; 144 | }); 145 | 146 | return response; 147 | }; 148 | 149 | // GET ALL USERS 150 | export const fetchUsers = async () => { 151 | const response = await axios 152 | .get('https://react-admin-ui-v1-api.vercel.app/users') 153 | .then((res) => { 154 | console.log('axios get:', res.data); 155 | return res.data; 156 | }) 157 | .catch((err) => { 158 | console.log(err); 159 | throw err; 160 | }); 161 | 162 | return response; 163 | }; 164 | 165 | // GET SINGLE USER 166 | export const fetchSingleUser = async (id: string) => { 167 | const response = await axios 168 | .get(`https://react-admin-ui-v1-api.vercel.app/users/${id}`) 169 | .then((res) => { 170 | console.log('axios get:', res.data); 171 | return res.data; 172 | }) 173 | .catch((err) => { 174 | console.log(err); 175 | throw err; 176 | }); 177 | 178 | return response; 179 | }; 180 | 181 | // GET ALL PRODUCTS 182 | export const fetchProducts = async () => { 183 | const response = await axios 184 | .get('https://react-admin-ui-v1-api.vercel.app/products') 185 | .then((res) => { 186 | console.log('axios get:', res.data); 187 | return res.data; 188 | }) 189 | .catch((err) => { 190 | console.log(err); 191 | throw err; 192 | }); 193 | 194 | return response; 195 | }; 196 | 197 | // GET SINGLE PRODUCT 198 | export const fetchSingleProduct = async (id: string) => { 199 | const response = await axios 200 | .get(`https://react-admin-ui-v1-api.vercel.app/products/${id}`) 201 | .then((res) => { 202 | console.log('axios get:', res.data); 203 | return res.data; 204 | }) 205 | .catch((err) => { 206 | console.log(err); 207 | throw err; 208 | }); 209 | 210 | return response; 211 | }; 212 | 213 | // GET ALL ORDERS 214 | export const fetchOrders = async () => { 215 | const response = await axios 216 | .get('https://react-admin-ui-v1-api.vercel.app/orders') 217 | .then((res) => { 218 | console.log('axios get:', res.data); 219 | return res.data; 220 | }) 221 | .catch((err) => { 222 | console.log(err); 223 | throw err; 224 | }); 225 | 226 | return response; 227 | }; 228 | 229 | // GET ALL POSTS 230 | export const fetchPosts = async () => { 231 | const response = await axios 232 | .get('https://react-admin-ui-v1-api.vercel.app/posts') 233 | .then((res) => { 234 | console.log('axios get:', res.data); 235 | return res.data; 236 | }) 237 | .catch((err) => { 238 | console.log(err); 239 | throw err; 240 | }); 241 | 242 | return response; 243 | }; 244 | 245 | // GET ALL NOTES 246 | export const fetchNotes = async () => { 247 | const response = await axios 248 | .get(`https://react-admin-ui-v1-api.vercel.app/notes?q=`) 249 | .then((res) => { 250 | console.log('axios get:', res.data); 251 | return res.data; 252 | }) 253 | .catch((err) => { 254 | console.log(err); 255 | throw err; 256 | }); 257 | 258 | return response; 259 | }; 260 | 261 | // GET ALL LOGS 262 | export const fetchLogs = async () => { 263 | const response = await axios 264 | .get(`https://react-admin-ui-v1-api.vercel.app/logs`) 265 | .then((res) => { 266 | console.log('axios get:', res.data); 267 | return res.data; 268 | }) 269 | .catch((err) => { 270 | console.log(err); 271 | throw err; 272 | }); 273 | 274 | return response; 275 | }; 276 | -------------------------------------------------------------------------------- /frontend/src/pages/Orders.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { GridColDef } from '@mui/x-data-grid'; 3 | import DataTable from '../components/DataTable'; 4 | import { useQuery } from '@tanstack/react-query'; 5 | import toast from 'react-hot-toast'; 6 | // import AddData from '../components/AddData'; 7 | import { fetchOrders } from '../api/ApiCollection'; 8 | 9 | const Orders = () => { 10 | // const [isOpen, setIsOpen] = React.useState(false); 11 | const { isLoading, isError, isSuccess, data } = useQuery({ 12 | queryKey: ['allorders'], 13 | queryFn: fetchOrders, 14 | }); 15 | 16 | const columns: GridColDef[] = [ 17 | { field: 'id', headerName: 'ID', width: 90 }, 18 | { 19 | field: 'title', 20 | headerName: 'Product', 21 | minWidth: 300, 22 | flex: 1, 23 | renderCell: (params) => { 24 | return ( 25 |
26 |
27 | orders-picture 32 |
33 | 34 | {params.row.title} 35 | 36 |
37 | ); 38 | }, 39 | }, 40 | { 41 | field: 'address', 42 | type: 'string', 43 | headerName: 'Address', 44 | minWidth: 320, 45 | flex: 1, 46 | }, 47 | { 48 | field: 'recipient', 49 | headerName: 'Recipient', 50 | minWidth: 250, 51 | flex: 1, 52 | renderCell: (params) => { 53 | return ( 54 |
55 |
56 |
57 | user-picture 63 |
64 |
65 | 66 | {params.row.recipient} 67 | 68 |
69 | ); 70 | }, 71 | }, 72 | { 73 | field: 'date', 74 | headerName: 'Date', 75 | minWidth: 100, 76 | type: 'string', 77 | flex: 1, 78 | }, 79 | { 80 | field: 'total', 81 | headerName: 'Total', 82 | minWidth: 100, 83 | type: 'string', 84 | flex: 1, 85 | }, 86 | { 87 | field: 'status', 88 | headerName: 'Status', 89 | minWidth: 120, 90 | flex: 1, 91 | renderCell: (params) => { 92 | if (params.row.status == 'Pending') { 93 | return ( 94 |
95 |
96 |
97 | {params.row.status} 98 |
99 |
100 | ); 101 | } else if (params.row.status == 'Dispatch') { 102 | return ( 103 |
104 |
105 |
106 | {params.row.status} 107 |
108 |
109 | ); 110 | } else if (params.row.status == 'Cancelled') { 111 | return ( 112 |
113 |
114 |
115 | {params.row.status} 116 |
117 |
118 | ); 119 | } else if (params.row.status == 'Completed') { 120 | return ( 121 |
122 |
123 |
124 | {params.row.status} 125 |
126 |
127 | ); 128 | } else { 129 | return ( 130 |
131 |
132 | 133 | Unknown 134 | 135 |
136 | ); 137 | } 138 | }, 139 | }, 140 | ]; 141 | 142 | React.useEffect(() => { 143 | if (isLoading) { 144 | toast.loading('Loading...', { id: 'promiseOrders' }); 145 | } 146 | if (isError) { 147 | toast.error('Error while getting the data!', { 148 | id: 'promiseOrders', 149 | }); 150 | } 151 | if (isSuccess) { 152 | toast.success('Got the data successfully!', { 153 | id: 'promiseOrders', 154 | }); 155 | } 156 | }, [isError, isLoading, isSuccess]); 157 | 158 | return ( 159 |
160 |
161 |
162 |
163 |

164 | Orders 165 |

166 | {data && data.length > 0 && ( 167 | 168 | {data.length} Orders Found 169 | 170 | )} 171 |
172 | {/* */} 180 |
181 | {isLoading ? ( 182 | 188 | ) : isSuccess ? ( 189 | 195 | ) : ( 196 | <> 197 | 203 |
204 | Error while getting the data! 205 |
206 | 207 | )} 208 | 209 | {/* {isOpen && ( 210 | 215 | )} */} 216 |
217 |
218 | ); 219 | }; 220 | 221 | export default Orders; 222 | -------------------------------------------------------------------------------- /frontend/src/pages/Posts.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { GridColDef } from '@mui/x-data-grid'; 3 | import DataTable from '../components/DataTable'; 4 | import { useQuery } from '@tanstack/react-query'; 5 | import toast from 'react-hot-toast'; 6 | // import AddData from '../components/AddData'; 7 | import { fetchPosts } from '../api/ApiCollection'; 8 | import { 9 | HiOutlineGlobeAmericas, 10 | HiOutlineLockClosed, 11 | } from 'react-icons/hi2'; 12 | 13 | const Posts = () => { 14 | // const [isOpen, setIsOpen] = React.useState(false); 15 | const { isLoading, isError, isSuccess, data } = useQuery({ 16 | queryKey: ['allorders'], 17 | queryFn: fetchPosts, 18 | }); 19 | 20 | const columns: GridColDef[] = [ 21 | { field: 'id', headerName: 'ID', minWidth: 90 }, 22 | { 23 | field: 'title', 24 | headerName: 'Title', 25 | minWidth: 500, 26 | flex: 1, 27 | renderCell: (params) => { 28 | return ( 29 |
30 |
31 | thumbnail-picture 39 |
40 |
41 |
42 | 43 | {params.row.title} 44 | 45 |
46 |
47 |

48 | {params.row.desc} 49 |

50 |
51 |
52 |
53 | ); 54 | }, 55 | }, 56 | { 57 | field: 'tags', 58 | // type: 'string', 59 | headerName: 'Tags', 60 | minWidth: 250, 61 | flex: 1, 62 | renderCell: (params) => { 63 | return ( 64 |
65 | {params.row.tags && 66 | params.row.tags.map((tag: string, index: number) => ( 67 |
71 | {tag} 72 |
73 | ))} 74 |
75 | ); 76 | }, 77 | }, 78 | { 79 | field: 'author', 80 | headerName: 'Author', 81 | minWidth: 220, 82 | flex: 1, 83 | renderCell: (params) => { 84 | return ( 85 |
86 |
87 |
88 | user-picture 94 |
95 |
96 | 97 | {params.row.author} 98 | 99 |
100 | ); 101 | }, 102 | }, 103 | { 104 | field: 'visibility', 105 | headerName: 'Visibility', 106 | minWidth: 100, 107 | flex: 1, 108 | renderCell: (params) => { 109 | if (params.row.visibility == 'Public') { 110 | return ( 111 |
112 | 113 | 114 | {params.row.visibility} 115 | 116 |
117 | ); 118 | } else if (params.row.visibility == 'Private') { 119 | return ( 120 |
121 | 122 | 123 | {params.row.visibility} 124 | 125 |
126 | ); 127 | } else { 128 | return Unknown; 129 | } 130 | }, 131 | }, 132 | { 133 | field: 'date', 134 | type: 'string', 135 | headerName: 'Date', 136 | minWidth: 100, 137 | }, 138 | { 139 | field: 'views', 140 | type: 'number', 141 | headerName: 'Views', 142 | minWidth: 120, 143 | }, 144 | { 145 | field: 'comments', 146 | type: 'number', 147 | headerName: 'Comments', 148 | minWidth: 120, 149 | renderCell: (params) => { 150 | return ( 151 |
152 | {params.row.comments && params.row.comments.length} 153 |
154 | ); 155 | }, 156 | }, 157 | { 158 | field: 'likes', 159 | type: 'number', 160 | headerName: 'Likes', 161 | minWidth: 80, 162 | }, 163 | ]; 164 | 165 | React.useEffect(() => { 166 | if (isLoading) { 167 | toast.loading('Loading...', { id: 'promisePosts' }); 168 | } 169 | if (isError) { 170 | toast.error('Error while getting the data!', { 171 | id: 'promisePosts', 172 | }); 173 | } 174 | if (isSuccess) { 175 | toast.success('Got the data successfully!', { 176 | id: 'promisePosts', 177 | }); 178 | } 179 | }, [isError, isLoading, isSuccess]); 180 | 181 | return ( 182 |
183 |
184 |
185 |
186 |

187 | Posts 188 |

189 | {data && data.length > 0 && ( 190 | 191 | {data.length} Posts Found 192 | 193 | )} 194 |
195 | {/* */} 203 |
204 | {isLoading ? ( 205 | 211 | ) : isSuccess ? ( 212 | 218 | ) : ( 219 | <> 220 | 226 |
227 | Error while getting the data! 228 |
229 | 230 | )} 231 | 232 | {/* {isOpen && ( 233 | 238 | )} */} 239 |
240 |
241 | ); 242 | }; 243 | 244 | export default Posts; 245 | -------------------------------------------------------------------------------- /frontend/src/pages/Product.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { useParams } from 'react-router-dom'; 3 | import toast from 'react-hot-toast'; 4 | import { useQuery } from '@tanstack/react-query'; 5 | import { fetchSingleProduct } from '../api/ApiCollection'; 6 | import { 7 | LineChart, 8 | Line, 9 | XAxis, 10 | YAxis, 11 | Tooltip, 12 | Legend, 13 | ResponsiveContainer, 14 | } from 'recharts'; 15 | 16 | const Product = () => { 17 | const tempEntries: number[] = [1, 2, 3, 4, 5]; 18 | const dataLine = [ 19 | { 20 | name: 'Jan', 21 | purchased: 4000, 22 | wishlisted: 2400, 23 | amt: 2400, 24 | }, 25 | { 26 | name: 'Feb', 27 | purchased: 3000, 28 | wishlisted: 1398, 29 | amt: 2210, 30 | }, 31 | { 32 | name: 'Mar', 33 | purchased: 2000, 34 | wishlisted: 9800, 35 | amt: 2290, 36 | }, 37 | { 38 | name: 'Apr', 39 | purchased: 2780, 40 | wishlisted: 3908, 41 | amt: 2000, 42 | }, 43 | { 44 | name: 'May', 45 | purchased: 1890, 46 | wishlisted: 4800, 47 | amt: 2181, 48 | }, 49 | { 50 | name: 'Jun', 51 | purchased: 2390, 52 | wishlisted: 3800, 53 | amt: 2500, 54 | }, 55 | { 56 | name: 'Jul', 57 | purchased: 3490, 58 | wishlisted: 4300, 59 | amt: 2100, 60 | }, 61 | ]; 62 | 63 | // const [user, setUser] = React.useState(); 64 | const { id } = useParams(); 65 | // const navigate = useNavigate(); 66 | 67 | const { isLoading, isError, data, isSuccess } = useQuery({ 68 | queryKey: ['user', id], 69 | queryFn: () => fetchSingleProduct(id || ''), 70 | }); 71 | 72 | React.useEffect(() => { 73 | if (isLoading) { 74 | toast.loading('Loading...', { id: 'promiseRead' }); 75 | } 76 | if (isError) { 77 | toast.error('Error while getting the data!', { 78 | id: 'promiseRead', 79 | }); 80 | } 81 | if (isSuccess) { 82 | toast.success('Read the data successfully!', { 83 | id: 'promiseRead', 84 | }); 85 | } 86 | }, [isError, isLoading, isSuccess]); 87 | 88 | return ( 89 | // screen 90 |
91 | {/* container */} 92 |
93 | {/* column 1 */} 94 |
95 | {/* product block */} 96 |
97 | {/* photo block */} 98 |
99 |
100 |
101 | {isLoading ? ( 102 |
103 | ) : isSuccess ? ( 104 |
105 | avatar 110 |
111 | ) : ( 112 | '' 113 | )} 114 |
115 |
116 | {isLoading ? ( 117 |
118 | ) : isSuccess ? ( 119 |

120 | {data.title} 121 |

122 | ) : ( 123 |
124 | )} 125 | 126 | Exclusive 127 | 128 |
129 |
130 |
131 | {/* detail block */} 132 |
133 | {isLoading ? ( 134 |
135 | ) : isSuccess ? ( 136 |
137 | {/* column 1 */} 138 |
139 | Product ID 140 | Color 141 | Price 142 | Producer 143 | Status 144 |
145 | {/* column 2 */} 146 |
147 | {data.id} 148 | 149 | {data.color} 150 | 151 | 152 | {data.price} 153 | 154 | 155 | {data.producer} 156 | 157 | 158 | {data.inStock ? 'In stock' : 'Out of stock'} 159 | 160 |
161 |
162 | ) : ( 163 |
164 | )} 165 |
166 |
167 | {/* divider */} 168 |
169 | {/* chart */} 170 | {isLoading ? ( 171 |
172 | ) : isSuccess ? ( 173 |
174 | 175 | 176 | 177 | 178 | 179 | 180 | 185 | 190 | 191 | 192 |
193 | ) : ( 194 |
195 | )} 196 |
197 | {/* column 2 */} 198 |
202 |

203 | Latest Activities 204 |

205 | {isLoading && 206 | tempEntries.map((index: number) => ( 207 |
211 | ))} 212 | {isSuccess && ( 213 |
    214 |
  • 215 |
    216 | Frans AHW purchased {data.title} 217 | 3 days ago 218 |
    219 |
  • 220 |
  • 221 |
    222 | 223 | Kurt Cobain added {data.title} into wishlist 224 | 225 | 1 week ago 226 |
    227 |
  • 228 |
  • 229 |
    230 | Mary Jane purchased {data.title} 231 | 2 weeks ago 232 |
    233 |
  • 234 |
  • 235 |
    236 | 237 | Jose Rose added {data.title} into wishlist 238 | 239 | 3 weeks ago 240 |
    241 |
  • 242 |
  • 243 |
    244 | James Deane purchased {data.title} 245 | 1 month ago 246 |
    247 |
  • 248 |
249 | )} 250 | {isError && 251 | tempEntries.map((index: number) => ( 252 |
256 | ))} 257 |
258 |
259 |
260 | ); 261 | }; 262 | 263 | export default Product; 264 | -------------------------------------------------------------------------------- /frontend/src/pages/Profile.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import toast from 'react-hot-toast'; 3 | import { HiOutlinePencil, HiOutlineTrash } from 'react-icons/hi2'; 4 | import { useNavigate } from 'react-router-dom'; 5 | 6 | const Profile = () => { 7 | const modalDelete = React.useRef(null); 8 | const navigate = useNavigate(); 9 | 10 | return ( 11 | // screen 12 |
13 | {/* container */} 14 |
15 | {/* block 1 */} 16 |
17 |

18 | My Profile 19 |

20 | 26 |
27 | {/* block 2 */} 28 |
29 |
30 |
31 | foto-cowok-ganteng 35 |
36 |
37 |
38 |

39 | Frans AHW 40 |

41 | Supervisor 42 |
43 |
44 | {/* block 3 */} 45 |
46 | {/* heading */} 47 |
48 |

49 | Basic Information 50 |

51 |
52 |
53 | {/* grid */} 54 |
55 | {/* column 1 */} 56 |
57 | {/* column 1 label */} 58 |
59 | First Name* 60 | Last Name* 61 | Nickname 62 |
63 | {/* column 1 text */} 64 |
65 | Frans 66 | AHW 67 | Frans 68 |
69 |
70 | {/* column 2 */} 71 |
72 | {/* column 2 label */} 73 |
74 | Email* 75 | Phone 76 | Address 77 |
78 | {/* column 2 text */} 79 |
80 | 81 | franswinata6@gmail.com 82 | 83 | 081-234-5678 84 | 85 | Suite 948 Jl. Gajahmada No. 91, Malang, SM 74810 86 | 87 |
88 |
89 | {/* column 3 */} 90 |
91 | {/* column 3 label */} 92 |
93 | Password 94 |
95 | {/* column 3 text */} 96 |
97 | 98 | Change Password 99 | 100 |
101 |
102 |
103 |
104 | {/* block 4 */} 105 |
106 | {/* heading */} 107 |
108 |
109 |

110 | Account Integrations 111 |

112 |
113 |
114 | 115 | Authorize faster and easier with your external service 116 | account. 117 | 118 |
119 | {/* services block */} 120 |
121 | {/* column 1 */} 122 |
123 | 140 |
141 | google 146 | 147 | Connected with Google 148 | 149 |
150 | 172 |
173 | {/* column 2 */} 174 |
175 | 176 | 186 | 187 |
188 |
189 |
190 | {/* block 5 */} 191 |
192 | 199 | 204 |
205 |

206 | Action Confirmation! 207 |

208 |

209 | Do you want to delete your account? 210 |

211 |
212 | 222 |
223 | {/* if there is a button in form, it will close the modal */} 224 | 227 |
228 |
229 |
230 |
231 |
232 |
233 |
234 | ); 235 | }; 236 | 237 | export default Profile; 238 | -------------------------------------------------------------------------------- /frontend/src/pages/User.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { useParams } from 'react-router-dom'; 3 | import toast from 'react-hot-toast'; 4 | import { useQuery } from '@tanstack/react-query'; 5 | import { fetchSingleUser } from '../api/ApiCollection'; 6 | import { 7 | LineChart, 8 | Line, 9 | XAxis, 10 | YAxis, 11 | Tooltip, 12 | Legend, 13 | ResponsiveContainer, 14 | } from 'recharts'; 15 | 16 | const User = () => { 17 | const tempEntries: number[] = [1, 2, 3, 4, 5]; 18 | const dataLine = [ 19 | { 20 | name: 'Jan', 21 | purchased: 4000, 22 | wishlisted: 2400, 23 | amt: 2400, 24 | }, 25 | { 26 | name: 'Feb', 27 | purchased: 3000, 28 | wishlisted: 1398, 29 | amt: 2210, 30 | }, 31 | { 32 | name: 'Mar', 33 | purchased: 2000, 34 | wishlisted: 9800, 35 | amt: 2290, 36 | }, 37 | { 38 | name: 'Apr', 39 | purchased: 2780, 40 | wishlisted: 3908, 41 | amt: 2000, 42 | }, 43 | { 44 | name: 'May', 45 | purchased: 1890, 46 | wishlisted: 4800, 47 | amt: 2181, 48 | }, 49 | { 50 | name: 'Jun', 51 | purchased: 2390, 52 | wishlisted: 3800, 53 | amt: 2500, 54 | }, 55 | { 56 | name: 'Jul', 57 | purchased: 3490, 58 | wishlisted: 4300, 59 | amt: 2100, 60 | }, 61 | ]; 62 | 63 | // const [user, setUser] = React.useState(); 64 | const { id } = useParams(); 65 | // const navigate = useNavigate(); 66 | 67 | const { isLoading, isError, data, isSuccess } = useQuery({ 68 | queryKey: ['user', id], 69 | queryFn: () => fetchSingleUser(id || ''), 70 | }); 71 | 72 | React.useEffect(() => { 73 | if (isLoading) { 74 | toast.loading('Loading...', { id: 'promiseRead' }); 75 | } 76 | if (isError) { 77 | toast.error('Error while getting the data!', { 78 | id: 'promiseRead', 79 | }); 80 | } 81 | if (isSuccess) { 82 | toast.success('Read the data successfully!', { 83 | id: 'promiseRead', 84 | }); 85 | } 86 | }, [isError, isLoading, isSuccess]); 87 | 88 | return ( 89 | // screen 90 |
91 | {/* container */} 92 |
93 | {/* column 1 */} 94 |
95 | {/* profile block */} 96 |
97 | {/* photo block */} 98 |
99 |
100 |
101 | {isLoading ? ( 102 |
103 | ) : isSuccess ? ( 104 |
105 | avatar 106 |
107 | ) : ( 108 | '' 109 | )} 110 |
111 |
112 | {isLoading ? ( 113 |
114 | ) : isSuccess ? ( 115 |

116 | {data.firstName} {data.lastName} 117 |

118 | ) : ( 119 |
120 | )} 121 | 122 | Member 123 | 124 |
125 |
126 |
127 | {/* detail block */} 128 |
129 | {isLoading ? ( 130 |
131 | ) : isSuccess ? ( 132 |
133 | {/* column 1 */} 134 |
135 | First Name 136 | Last Name 137 | Email 138 | Phone 139 | Status 140 |
141 | {/* column 2 */} 142 |
143 | 144 | {data.firstName} 145 | 146 | 147 | {data.lastName} 148 | 149 | 150 | {data.email} 151 | 152 | 153 | {data.phone} 154 | 155 | 156 | {data.verified ? 'Verified' : 'Not Verified'} 157 | 158 |
159 |
160 | ) : ( 161 |
162 | )} 163 |
164 |
165 | {/* divider */} 166 |
167 | {/* chart */} 168 | {isLoading ? ( 169 |
170 | ) : isSuccess ? ( 171 |
172 | 173 | 174 | 175 | 176 | 177 | 178 | 183 | 188 | 189 | 190 |
191 | ) : ( 192 |
193 | )} 194 |
195 | {/* column 2 */} 196 |
200 |

201 | Latest Activities 202 |

203 | {isLoading && 204 | tempEntries.map((index: number) => ( 205 |
209 | ))} 210 | {isSuccess && ( 211 |
    212 |
  • 213 |
    214 | 215 | {data.firstName} {data.lastName} purchased 216 | Playstation 5 Digital Edition 217 | 218 | 3 days ago 219 |
    220 |
  • 221 |
  • 222 |
    223 | 224 | {data.firstName} {data.lastName} added 3 items 225 | into wishlist 226 | 227 | 1 week ago 228 |
    229 |
  • 230 |
  • 231 |
    232 | 233 | {data.firstName} {data.lastName} purchased Samsung 234 | 4K UHD SmartTV 235 | 236 | 2 weeks ago 237 |
    238 |
  • 239 |
  • 240 |
    241 | 242 | {data.firstName} {data.lastName} commented a post 243 | 244 | 3 weeks ago 245 |
    246 |
  • 247 |
  • 248 |
    249 | 250 | {data.firstName} {data.lastName} added 1 item into 251 | wishlist 252 | 253 | 1 month ago 254 |
    255 |
  • 256 |
257 | )} 258 | {isError && 259 | tempEntries.map((index: number) => ( 260 |
264 | ))} 265 |
266 |
267 |
268 | ); 269 | }; 270 | 271 | export default User; 272 | -------------------------------------------------------------------------------- /frontend/src/pages/Notes.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import axios from 'axios'; 3 | import { useQuery } from '@tanstack/react-query'; 4 | import toast from 'react-hot-toast'; 5 | 6 | import NoteCard from '../components/notes/NoteCard'; 7 | import { fetchNotes } from '../api/ApiCollection'; 8 | import { HiOutlineXMark } from 'react-icons/hi2'; 9 | // import { allNotes } from '../components/notes/data'; 10 | 11 | interface Note { 12 | id: number; 13 | title: string; 14 | body: string; 15 | date: string; 16 | author: string; 17 | topic: string; 18 | } 19 | 20 | const Notes = () => { 21 | const [allNotes, setAllNotes] = React.useState([]); 22 | const [searchQuery, setSearchQuery] = React.useState(''); 23 | 24 | const [noteSelected, setNoteSelected] = React.useState(false); 25 | const [selectedCard, setSelectedCard] = React.useState({ 26 | title: '', 27 | body: '', 28 | }); 29 | const [titleSelected, setTitleSelected] = React.useState(''); 30 | const [bodySelected, setBodySelected] = React.useState(''); 31 | const [topicSelected, setTopicSelected] = React.useState(''); 32 | 33 | const tempTotalEntries = [1, 2, 3, 4, 5, 6, 7]; 34 | 35 | const { data, isLoading, isError, isSuccess } = useQuery({ 36 | queryKey: ['notes'], 37 | queryFn: fetchNotes, 38 | }); 39 | 40 | React.useEffect(() => { 41 | if (isLoading) { 42 | toast.loading('Loading...', { id: 'promiseNotes' }); 43 | } 44 | if (isError) { 45 | toast.error('Error while getting the data!', { 46 | id: 'promiseNotes', 47 | }); 48 | } 49 | if (isSuccess) { 50 | toast.success('Got the data successfully!', { 51 | id: 'promiseNotes', 52 | }); 53 | } 54 | }, [isError, isLoading, isSuccess]); 55 | 56 | React.useEffect(() => { 57 | console.log('Ini datanya:', data); 58 | setAllNotes(data); 59 | // console.log('Ini notes nya:', allNotes); 60 | }, [data]); 61 | 62 | React.useEffect(() => { 63 | const fetchSpesificNote = async () => { 64 | const res = await axios.get( 65 | `https://react-admin-ui-v1-api.vercel.app/notes?q=${searchQuery}` 66 | ); 67 | setAllNotes(res.data); 68 | }; 69 | 70 | if (searchQuery.length === 0 || searchQuery.length > 2) { 71 | fetchSpesificNote(); 72 | } 73 | }, [searchQuery]); 74 | 75 | return ( 76 | // screen 77 |
78 | {/* container */} 79 |
80 | {/* grid */} 81 |
82 | {/* column 1 */} 83 |
84 | {/* heading */} 85 |
86 |

87 | Notes 88 |

89 | 90 |
91 | 92 | {/* Search Box */} 93 |
94 | 99 | setSearchQuery(e.target.value.toLowerCase()) 100 | } 101 | /> 102 |
103 | 104 | {/* listed notes */} 105 | {isLoading ? ( 106 | tempTotalEntries.map((index: number) => ( 107 |
111 | )) 112 | ) : isSuccess ? ( 113 | allNotes && 114 | allNotes.map((note: Note, index: number) => ( 115 | 129 | )) 130 | ) : ( 131 |
132 | Error while getting the data! 133 |
134 | )} 135 |
136 | 137 | {/* column 2 */} 138 |
139 | {/* content */} 140 | {!noteSelected ? ( 141 |
142 |

Please select one note

143 |
144 | ) : ( 145 |
146 |
147 | {topicSelected == 'e-commerce' && ( 148 | vector 153 | )} 154 | {topicSelected == 'marketing' && ( 155 | vector 160 | )} 161 | {topicSelected == 'social-media' && ( 162 | vector 167 | )} 168 | {topicSelected == 'SEO' && ( 169 | vector 174 | )} 175 | {topicSelected == 'productivity' && ( 176 | vector 181 | )} 182 | {topicSelected == 'communication' && ( 183 | vector 188 | )} 189 | {topicSelected == '' && ( 190 | vector 195 | )} 196 |
197 |

198 | {titleSelected} 199 |

200 |

201 | {bodySelected} 202 |

203 |
204 | )} 205 |
206 |
207 | {/* mobile only */} 208 |
{ 210 | setNoteSelected(false); 211 | setSelectedCard({ 212 | title: '', 213 | body: '', 214 | }); 215 | }} 216 | className={`w-screen h-screen left-0 bottom-0 fixed z-[99] flex items-end transition-all duration-[2s] bg-black/50 217 | ${ 218 | noteSelected 219 | ? 'opacity-100 inline-flex xl:hidden' 220 | : 'opacity-0 hidden' 221 | }`} 222 | > 223 |
227 | 239 |
240 | {topicSelected == 'e-commerce' && ( 241 | vector 246 | )} 247 | {topicSelected == 'marketing' && ( 248 | vector 253 | )} 254 | {topicSelected == 'social-media' && ( 255 | vector 260 | )} 261 | {topicSelected == 'SEO' && ( 262 | vector 267 | )} 268 | {topicSelected == 'productivity' && ( 269 | vector 274 | )} 275 | {topicSelected == 'communication' && ( 276 | vector 281 | )} 282 | {topicSelected == '' && ( 283 | vector 288 | )} 289 |
290 |

291 | {titleSelected} 292 |

293 |

294 | {bodySelected} 295 |

296 |
297 |
298 |
299 |
300 | ); 301 | }; 302 | 303 | export default Notes; 304 | --------------------------------------------------------------------------------