upload
This commit is contained in:
24
.gitignore
vendored
Normal file
24
.gitignore
vendored
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
pnpm-debug.log*
|
||||||
|
lerna-debug.log*
|
||||||
|
|
||||||
|
node_modules
|
||||||
|
dist
|
||||||
|
dist-ssr
|
||||||
|
*.local
|
||||||
|
|
||||||
|
# Editor directories and files
|
||||||
|
.vscode/*
|
||||||
|
!.vscode/extensions.json
|
||||||
|
.idea
|
||||||
|
.DS_Store
|
||||||
|
*.suo
|
||||||
|
*.ntvs*
|
||||||
|
*.njsproj
|
||||||
|
*.sln
|
||||||
|
*.sw?
|
||||||
2
README.md
Normal file
2
README.md
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
## Dự Án React (chưa có api, redux)
|
||||||
|
## Có đa ngôn ngữ
|
||||||
16
abc.js
Normal file
16
abc.js
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
let targetEL = document.querySelector(".icon-listing")
|
||||||
|
|
||||||
|
let arrayI = targetEL.querySelectorAll("i")
|
||||||
|
|
||||||
|
let result = []
|
||||||
|
for (let i = 0; i < arrayI.length; i++) {
|
||||||
|
|
||||||
|
let item = ""
|
||||||
|
for (let j = 0; j < arrayI[i].classList.length; j++) {
|
||||||
|
item += `${arrayI[i].classList[j]} `
|
||||||
|
}
|
||||||
|
|
||||||
|
result.push(item)
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(result)
|
||||||
23
eslint.config.js
Normal file
23
eslint.config.js
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import js from '@eslint/js'
|
||||||
|
import globals from 'globals'
|
||||||
|
import reactHooks from 'eslint-plugin-react-hooks'
|
||||||
|
import reactRefresh from 'eslint-plugin-react-refresh'
|
||||||
|
import tseslint from 'typescript-eslint'
|
||||||
|
import { defineConfig, globalIgnores } from 'eslint/config'
|
||||||
|
|
||||||
|
export default defineConfig([
|
||||||
|
globalIgnores(['dist']),
|
||||||
|
{
|
||||||
|
files: ['**/*.{ts,tsx}'],
|
||||||
|
extends: [
|
||||||
|
js.configs.recommended,
|
||||||
|
tseslint.configs.recommended,
|
||||||
|
reactHooks.configs['recommended-latest'],
|
||||||
|
reactRefresh.configs.vite,
|
||||||
|
],
|
||||||
|
languageOptions: {
|
||||||
|
ecmaVersion: 2020,
|
||||||
|
globals: globals.browser,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
])
|
||||||
14
index.html
Normal file
14
index.html
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>TMDT</title>
|
||||||
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/7.0.1/css/all.min.css" integrity="sha512-2SwdPD6INVrV/lHTZbO2nodKhrnDdJK9/kg2XD1r9uGqPo1cUbujc+IYdlYdEErWNu69gVcYgdxlmVmzTWnetw==" crossorigin="anonymous" referrerpolicy="no-referrer" />
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="root"></div>
|
||||||
|
<script type="module" src="/src/main.tsx"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
4586
package-lock.json
generated
Normal file
4586
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
36
package.json
Normal file
36
package.json
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
{
|
||||||
|
"name": "react_router_dom",
|
||||||
|
"private": true,
|
||||||
|
"version": "0.0.0",
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "vite",
|
||||||
|
"build": "tsc -b && vite build",
|
||||||
|
"lint": "eslint .",
|
||||||
|
"preview": "vite preview"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@ant-design/icons": "^5.6.1",
|
||||||
|
"@ant-design/v5-patch-for-react-19": "^1.0.3",
|
||||||
|
"antd": "^5.27.4",
|
||||||
|
"i18next": "^25.5.2",
|
||||||
|
"react": "^19.1.1",
|
||||||
|
"react-dom": "^19.1.1",
|
||||||
|
"react-i18next": "^15.7.3",
|
||||||
|
"react-router": "^7.9.1"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@eslint/js": "^9.36.0",
|
||||||
|
"@types/react": "^19.1.13",
|
||||||
|
"@types/react-dom": "^19.1.9",
|
||||||
|
"@vitejs/plugin-react-swc": "^4.1.0",
|
||||||
|
"eslint": "^9.36.0",
|
||||||
|
"eslint-plugin-react-hooks": "^5.2.0",
|
||||||
|
"eslint-plugin-react-refresh": "^0.4.20",
|
||||||
|
"globals": "^16.4.0",
|
||||||
|
"sass": "^1.93.1",
|
||||||
|
"typescript": "~5.8.3",
|
||||||
|
"typescript-eslint": "^8.44.0",
|
||||||
|
"vite": "^7.1.7"
|
||||||
|
}
|
||||||
|
}
|
||||||
1
public/vite.svg
Normal file
1
public/vite.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
|
||||||
|
After Width: | Height: | Size: 1.5 KiB |
53
src/App.tsx
Normal file
53
src/App.tsx
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import RouteConfig from './RouteConfig'
|
||||||
|
import { UserRole, type User } from './interfaces/user.interface'
|
||||||
|
import type { Category } from './interfaces/cateogry.interface'
|
||||||
|
|
||||||
|
|
||||||
|
export default function App() {
|
||||||
|
const mockUserData: User[] = [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
fullName: "Lưu Hoàng Xuân Nguyên",
|
||||||
|
age: 31,
|
||||||
|
phone: "+84329577177",
|
||||||
|
userName: "user1",
|
||||||
|
password: "123",
|
||||||
|
email: "user@abc.com",
|
||||||
|
role: UserRole.USER,
|
||||||
|
isActive: true
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
fullName: "nguyễn Thanh bình Phước",
|
||||||
|
age: 30,
|
||||||
|
phone: "+84329577177",
|
||||||
|
userName: "admin",
|
||||||
|
password: "123",
|
||||||
|
email: "admin@abc.com",
|
||||||
|
role: UserRole.ADMIN,
|
||||||
|
isActive: true
|
||||||
|
}
|
||||||
|
|
||||||
|
]
|
||||||
|
|
||||||
|
if(!localStorage.getItem("userList")) {
|
||||||
|
localStorage.setItem("userList", JSON.stringify(mockUserData))
|
||||||
|
}
|
||||||
|
|
||||||
|
const mockCategoryData: Category[] = [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
title: "Điện thoại",
|
||||||
|
isActive: true,
|
||||||
|
icon: "fa-solid fa-circle-user"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
if(!localStorage.getItem("categoryList")) {
|
||||||
|
localStorage.setItem("categoryList", JSON.stringify(mockCategoryData))
|
||||||
|
}
|
||||||
|
|
||||||
|
return <RouteConfig/>
|
||||||
|
}
|
||||||
28
src/RouteConfig.tsx
Normal file
28
src/RouteConfig.tsx
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import { Route, Routes } from 'react-router'
|
||||||
|
import { Admin } from './pages/admin/Admin'
|
||||||
|
import UserManagement from './pages/admin/user-management/UserManagement'
|
||||||
|
import ProductManagement from './pages/admin/product-management/ProductManagement'
|
||||||
|
import CategoryManagement from './pages/admin/category-management/CategoryManagement'
|
||||||
|
import Home from './pages/user/Home/Home'
|
||||||
|
import Auth from './pages/admin/auth/Auth'
|
||||||
|
import About from './pages/user/Home/About/About'
|
||||||
|
import CollectionDetail from './pages/user/Collection/CollectionDetail'
|
||||||
|
|
||||||
|
export default function RouteConfig() {
|
||||||
|
return (
|
||||||
|
<Routes>
|
||||||
|
<Route path='*' element={<Home />}>
|
||||||
|
<Route path='about' element={<About />}></Route>
|
||||||
|
<Route path='collections/:slugCollection' element={<CollectionDetail />}></Route>
|
||||||
|
</Route>
|
||||||
|
<Route path='admin' element={<Auth>
|
||||||
|
<Admin />
|
||||||
|
</Auth>}>
|
||||||
|
<Route path='user' element={<UserManagement />}></Route>
|
||||||
|
<Route path='product' element={<ProductManagement />}></Route>
|
||||||
|
<Route path='category' element={<CategoryManagement />}></Route>
|
||||||
|
</Route>
|
||||||
|
</Routes>
|
||||||
|
)
|
||||||
|
}
|
||||||
BIN
src/assets/img/banner.png
Normal file
BIN
src/assets/img/banner.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 83 KiB |
BIN
src/assets/img/logo.png
Normal file
BIN
src/assets/img/logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 19 KiB |
31
src/i18n/en.json
Normal file
31
src/i18n/en.json
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
{
|
||||||
|
"Welcome to React": "Welcome to React and react-i18next",
|
||||||
|
"user-management": "User Management",
|
||||||
|
"product-management": "Product Management",
|
||||||
|
"category-management": "Category Management",
|
||||||
|
"user-id": "User Id",
|
||||||
|
"fullname": "Fullname",
|
||||||
|
"age": "Age",
|
||||||
|
"phone-number": "Phone Number",
|
||||||
|
"tools": "tools",
|
||||||
|
"block": "Block",
|
||||||
|
"hello-friend": "Hello, Friend!",
|
||||||
|
"enter-your-personal-details-and-start-journey-with-us": "Enter your personal details and start journey with us",
|
||||||
|
"welcome-back": "Welcome Back!",
|
||||||
|
"to-keep-connected-with-us-please-login-with-your-personal-info": "To keep connected with us please login with your personal info",
|
||||||
|
"sign-in": "Sign In",
|
||||||
|
"forgot-your-password": "Forgot your password?",
|
||||||
|
"or-use-your-account": "or use your account",
|
||||||
|
"sign-in-0": "Sign in",
|
||||||
|
"logout": "Logout",
|
||||||
|
"password-incorrect": "Password incorrect",
|
||||||
|
"ban-khong-co-quyen-truy-cap": " You do not have access",
|
||||||
|
"user-not-found": "User not found",
|
||||||
|
"username": "Username",
|
||||||
|
"password": "Password",
|
||||||
|
"username-0": "Username",
|
||||||
|
"email": "Email",
|
||||||
|
"role": "Role",
|
||||||
|
"active": "Active",
|
||||||
|
"unlock": "Unlock"
|
||||||
|
}
|
||||||
26
src/i18n/i18n.setup.ts
Normal file
26
src/i18n/i18n.setup.ts
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
import i18n from "i18next";
|
||||||
|
import { initReactI18next } from "react-i18next";
|
||||||
|
import en from './en.json'
|
||||||
|
import vi from './vi.json'
|
||||||
|
import ja from './ja.json'
|
||||||
|
|
||||||
|
const resources = {
|
||||||
|
en: {
|
||||||
|
translation: en
|
||||||
|
},
|
||||||
|
vi: {
|
||||||
|
translation: vi
|
||||||
|
},
|
||||||
|
ja: {
|
||||||
|
translation: ja
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
i18n
|
||||||
|
.use(initReactI18next)
|
||||||
|
.init({
|
||||||
|
resources,
|
||||||
|
lng: localStorage.getItem("lng") || "vi"
|
||||||
|
});
|
||||||
|
|
||||||
|
export default i18n;
|
||||||
27
src/i18n/ja.json
Normal file
27
src/i18n/ja.json
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
{
|
||||||
|
"category-management": "カテゴリ管理",
|
||||||
|
"product-management": "製品管理",
|
||||||
|
"user-id": "ユーザーID",
|
||||||
|
"fullname": "フルネーム",
|
||||||
|
"age": "年",
|
||||||
|
"phone-number": "電話番号",
|
||||||
|
"tools": "ツール",
|
||||||
|
"block": "ブロック",
|
||||||
|
"hello-friend": "こんにちは、友達!",
|
||||||
|
"enter-your-personal-details-and-start-journey-with-us": "あなたの個人的な詳細を入力して、私たちと一緒に旅を始めてください",
|
||||||
|
"welcome-back": "おかえり!",
|
||||||
|
"to-keep-connected-with-us-please-login-with-your-personal-info": "私たちとつながり続けるには、あなたの個人情報にログインしてください",
|
||||||
|
"sign-in": "サインイン",
|
||||||
|
"forgot-your-password": "パスワードをお忘れですか?",
|
||||||
|
"or-use-your-account": "またはアカウントを使用します",
|
||||||
|
"logout": "ログアウト",
|
||||||
|
"password-incorrect": "パスワードが正しくありません",
|
||||||
|
"ban-khong-co-quyen-truy-cap": "アクセスできません",
|
||||||
|
"user-not-found": "ユーザーが見つかりません",
|
||||||
|
"username": "ユーザー名",
|
||||||
|
"password": "パスワード",
|
||||||
|
"email": "メール",
|
||||||
|
"role": "役割",
|
||||||
|
"active": "アクティブ",
|
||||||
|
"unlock": "ロックを解除します"
|
||||||
|
}
|
||||||
29
src/i18n/vi.json
Normal file
29
src/i18n/vi.json
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
{
|
||||||
|
"Welcome to React": "Xin Chào Đến Với React",
|
||||||
|
"user-management": "Quản lý người dùng",
|
||||||
|
"product-management": "Quản lý sản phẩm",
|
||||||
|
"category-management": "Quản lý danh mục",
|
||||||
|
"user-id": "ID người dùng",
|
||||||
|
"fullname": "Tên đầy đủ",
|
||||||
|
"age": "Tuổi",
|
||||||
|
"phone-number": "Số điện thoại",
|
||||||
|
"tools": "công cụ",
|
||||||
|
"block": "Khóa",
|
||||||
|
"hello-friend": "Chào bạn!",
|
||||||
|
"enter-your-personal-details-and-start-journey-with-us": "Nhập chi tiết cá nhân của bạn và bắt đầu hành trình với chúng tôi",
|
||||||
|
"welcome-back": "Chào mừng trở lại!",
|
||||||
|
"to-keep-connected-with-us-please-login-with-your-personal-info": "Để giữ kết nối với chúng tôi, vui lòng đăng nhập với thông tin cá nhân của bạn",
|
||||||
|
"sign-in": "Đăng nhập",
|
||||||
|
"forgot-your-password": "Quên mật khẩu của bạn?",
|
||||||
|
"or-use-your-account": "hoặc sử dụng tài khoản của bạn",
|
||||||
|
"logout": "Đăng xuất",
|
||||||
|
"password-incorrect": "Mật khẩu không chính xác",
|
||||||
|
"ban-khong-co-quyen-truy-cap": "Bạn không có quyền truy cập",
|
||||||
|
"user-not-found": "Người dùng không tìm thấy",
|
||||||
|
"username": "Tên người dùng",
|
||||||
|
"password": "Mật khẩu",
|
||||||
|
"email": "E-mail",
|
||||||
|
"role": "Vai trò",
|
||||||
|
"active": "Tích cực",
|
||||||
|
"unlock": "Mở khóa"
|
||||||
|
}
|
||||||
8
src/interfaces/cateogry.interface.ts
Normal file
8
src/interfaces/cateogry.interface.ts
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
import type React from "react"
|
||||||
|
|
||||||
|
export interface Category {
|
||||||
|
id: number
|
||||||
|
title: string
|
||||||
|
icon: string
|
||||||
|
isActive: boolean
|
||||||
|
}
|
||||||
15
src/interfaces/user.interface.ts
Normal file
15
src/interfaces/user.interface.ts
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
export enum UserRole {
|
||||||
|
ADMIN="ADMIN",
|
||||||
|
USER="USER"
|
||||||
|
}
|
||||||
|
export interface User {
|
||||||
|
id: number
|
||||||
|
fullName: string
|
||||||
|
age: number
|
||||||
|
phone: string
|
||||||
|
userName: string
|
||||||
|
password: string
|
||||||
|
email: string
|
||||||
|
role: UserRole
|
||||||
|
isActive: boolean
|
||||||
|
}
|
||||||
5
src/main.scss
Normal file
5
src/main.scss
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
* {
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
13
src/main.tsx
Normal file
13
src/main.tsx
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import { createRoot } from 'react-dom/client'
|
||||||
|
import App from './App.tsx'
|
||||||
|
import { BrowserRouter } from 'react-router'
|
||||||
|
import './main.scss'
|
||||||
|
import './i18n/i18n.setup.ts'
|
||||||
|
import '@ant-design/v5-patch-for-react-19';
|
||||||
|
|
||||||
|
|
||||||
|
createRoot(document.getElementById('root')!).render(
|
||||||
|
<BrowserRouter>
|
||||||
|
<App />
|
||||||
|
</BrowserRouter>
|
||||||
|
)
|
||||||
34
src/pages/admin/Admin.tsx
Normal file
34
src/pages/admin/Admin.tsx
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
import React, { useState } from 'react';
|
||||||
|
import { Layout, theme } from 'antd';
|
||||||
|
import Slider from './components/Slider';
|
||||||
|
import HeaderCom from './components/Header';
|
||||||
|
import { Outlet } from 'react-router';
|
||||||
|
|
||||||
|
const { Content } = Layout;
|
||||||
|
|
||||||
|
export const Admin: React.FC = () => {
|
||||||
|
const [collapsed, setCollapsed] = useState(false);
|
||||||
|
const {
|
||||||
|
token: { colorBgContainer, borderRadiusLG },
|
||||||
|
} = theme.useToken();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Layout>
|
||||||
|
<Slider collapsed={collapsed} />
|
||||||
|
<Layout>
|
||||||
|
<HeaderCom collapsed={collapsed} setCollapsed={setCollapsed} />
|
||||||
|
<Content
|
||||||
|
style={{
|
||||||
|
margin: '24px 16px',
|
||||||
|
padding: 24,
|
||||||
|
minHeight: 280,
|
||||||
|
background: colorBgContainer,
|
||||||
|
borderRadius: borderRadiusLG,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Outlet/>
|
||||||
|
</Content>
|
||||||
|
</Layout>
|
||||||
|
</Layout>
|
||||||
|
);
|
||||||
|
};
|
||||||
112
src/pages/admin/auth/Auth.tsx
Normal file
112
src/pages/admin/auth/Auth.tsx
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
import { type ReactNode } from 'react'
|
||||||
|
import './auth.scss'
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { UserRole, type User } from '../../../interfaces/user.interface';
|
||||||
|
import { message } from 'antd';
|
||||||
|
export default function Auth({ children }: {
|
||||||
|
children: ReactNode;
|
||||||
|
}) {
|
||||||
|
const {t} = useTranslation()
|
||||||
|
let isLogin = localStorage.getItem("userLogin")
|
||||||
|
|
||||||
|
function loginHandle(data: {
|
||||||
|
userName: string,
|
||||||
|
password: string
|
||||||
|
}) {
|
||||||
|
let userList: User[] = JSON.parse(localStorage.getItem("userList"))
|
||||||
|
|
||||||
|
let user = userList.find(item => item.userName == data.userName)
|
||||||
|
|
||||||
|
if(user) {
|
||||||
|
if(user.password != data.password) {
|
||||||
|
message.error(t('password-incorrect'))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if(user.role != UserRole.ADMIN) {
|
||||||
|
message.error(t('ban-khong-co-quyen-truy-cap'))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
localStorage.setItem("userLogin", JSON.stringify(user))
|
||||||
|
window.location.reload()
|
||||||
|
}else {
|
||||||
|
message.error(t('user-not-found'))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isLogin) {
|
||||||
|
return <>{children}</>
|
||||||
|
} else {
|
||||||
|
return <div className='admin_login_page'>
|
||||||
|
<div className="container" id="container">
|
||||||
|
{/* <div className="form-container sign-up-container">
|
||||||
|
<form action="#">
|
||||||
|
<h1>Create Account</h1>
|
||||||
|
<div className="social-container">
|
||||||
|
<a href="#" className="social">
|
||||||
|
<i className="fab fa-facebook-f" />
|
||||||
|
</a>
|
||||||
|
<a href="#" className="social">
|
||||||
|
<i className="fab fa-google-plus-g" />
|
||||||
|
</a>
|
||||||
|
<a href="#" className="social">
|
||||||
|
<i className="fab fa-linkedin-in" />
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<span>or use your email for registration</span>
|
||||||
|
<input type="text" placeholder="Name" />
|
||||||
|
<input type="email" placeholder="Email" />
|
||||||
|
<input type="password" placeholder="Password" />
|
||||||
|
<button>Sign Up</button>
|
||||||
|
</form>
|
||||||
|
</div> */}
|
||||||
|
<div className="form-container sign-in-container">
|
||||||
|
<form onSubmit={(e) => {
|
||||||
|
e.preventDefault()
|
||||||
|
loginHandle({
|
||||||
|
userName: (e.target as any).userName.value,
|
||||||
|
password: (e.target as any).password.value,
|
||||||
|
})
|
||||||
|
}}>
|
||||||
|
<h1>{t('sign-in')}</h1>
|
||||||
|
<div className="social-container">
|
||||||
|
<a href="#" className="social">
|
||||||
|
<i className="fab fa-facebook-f" />
|
||||||
|
</a>
|
||||||
|
<a href="#" className="social">
|
||||||
|
<i className="fab fa-google-plus-g" />
|
||||||
|
</a>
|
||||||
|
<a href="#" className="social">
|
||||||
|
<i className="fab fa-linkedin-in" />
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<span>{t('or-use-your-account')}</span>
|
||||||
|
<input type="text" name='userName' placeholder={t('username')} />
|
||||||
|
<input type="password" name='password' placeholder={t('password')} />
|
||||||
|
<a href="#">{t('forgot-your-password')}</a>
|
||||||
|
<button>{t('sign-in')}</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div className="overlay-container">
|
||||||
|
<div className="overlay">
|
||||||
|
<div className="overlay-panel overlay-left">
|
||||||
|
<h1>{t('welcome-back')}</h1>
|
||||||
|
<p>{t('to-keep-connected-with-us-please-login-with-your-personal-info')}</p>
|
||||||
|
<button className="ghost" id="signIn">
|
||||||
|
{t('sign-in')}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div className="overlay-panel overlay-right">
|
||||||
|
<h1>{t('hello-friend')}</h1>
|
||||||
|
<p>{t('enter-your-personal-details-and-start-journey-with-us')}</p>
|
||||||
|
{/* <button className="ghost" id="signUp">
|
||||||
|
Sign Up
|
||||||
|
</button> */}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
258
src/pages/admin/auth/auth.scss
Normal file
258
src/pages/admin/auth/auth.scss
Normal file
@@ -0,0 +1,258 @@
|
|||||||
|
@import url('https://fonts.googleapis.com/css?family=Montserrat:400,800');
|
||||||
|
|
||||||
|
.admin_login_page {
|
||||||
|
* {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
background: #f6f5f7;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
flex-direction: column;
|
||||||
|
font-family: 'Montserrat', sans-serif;
|
||||||
|
height: 100vh;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-weight: bold;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 100;
|
||||||
|
line-height: 20px;
|
||||||
|
letter-spacing: 0.5px;
|
||||||
|
margin: 20px 0 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
span {
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: #333;
|
||||||
|
font-size: 14px;
|
||||||
|
text-decoration: none;
|
||||||
|
margin: 15px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
border-radius: 20px;
|
||||||
|
border: 1px solid #FF4B2B;
|
||||||
|
background-color: #FF4B2B;
|
||||||
|
color: #FFFFFF;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: bold;
|
||||||
|
padding: 12px 45px;
|
||||||
|
letter-spacing: 1px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
transition: transform 80ms ease-in;
|
||||||
|
}
|
||||||
|
|
||||||
|
button:active {
|
||||||
|
transform: scale(0.95);
|
||||||
|
}
|
||||||
|
|
||||||
|
button:focus {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
button.ghost {
|
||||||
|
background-color: transparent;
|
||||||
|
border-color: #FFFFFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
form {
|
||||||
|
background-color: #FFFFFF;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
flex-direction: column;
|
||||||
|
padding: 0 50px;
|
||||||
|
height: 100%;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
input {
|
||||||
|
background-color: #eee;
|
||||||
|
border: none;
|
||||||
|
padding: 12px 15px;
|
||||||
|
margin: 8px 0;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
background-color: #fff;
|
||||||
|
border-radius: 10px;
|
||||||
|
box-shadow: 0 14px 28px rgba(0, 0, 0, 0.25),
|
||||||
|
0 10px 10px rgba(0, 0, 0, 0.22);
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
width: 768px;
|
||||||
|
max-width: 100%;
|
||||||
|
min-height: 480px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-container {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
height: 100%;
|
||||||
|
transition: all 0.6s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sign-in-container {
|
||||||
|
left: 0;
|
||||||
|
width: 50%;
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container.right-panel-active .sign-in-container {
|
||||||
|
transform: translateX(100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sign-up-container {
|
||||||
|
left: 0;
|
||||||
|
width: 50%;
|
||||||
|
opacity: 0;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container.right-panel-active .sign-up-container {
|
||||||
|
transform: translateX(100%);
|
||||||
|
opacity: 1;
|
||||||
|
z-index: 5;
|
||||||
|
animation: show 0.6s;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes show {
|
||||||
|
|
||||||
|
0%,
|
||||||
|
49.99% {
|
||||||
|
opacity: 0;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
50%,
|
||||||
|
100% {
|
||||||
|
opacity: 1;
|
||||||
|
z-index: 5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.overlay-container {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 50%;
|
||||||
|
width: 50%;
|
||||||
|
height: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
transition: transform 0.6s ease-in-out;
|
||||||
|
z-index: 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container.right-panel-active .overlay-container {
|
||||||
|
transform: translateX(-100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.overlay {
|
||||||
|
background: #FF416C;
|
||||||
|
background: -webkit-linear-gradient(to right, #FF4B2B, #FF416C);
|
||||||
|
background: linear-gradient(to right, #FF4B2B, #FF416C);
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-size: cover;
|
||||||
|
background-position: 0 0;
|
||||||
|
color: #FFFFFF;
|
||||||
|
position: relative;
|
||||||
|
left: -100%;
|
||||||
|
height: 100%;
|
||||||
|
width: 200%;
|
||||||
|
transform: translateX(0);
|
||||||
|
transition: transform 0.6s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container.right-panel-active .overlay {
|
||||||
|
transform: translateX(50%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.overlay-panel {
|
||||||
|
position: absolute;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
flex-direction: column;
|
||||||
|
padding: 0 40px;
|
||||||
|
text-align: center;
|
||||||
|
top: 0;
|
||||||
|
height: 100%;
|
||||||
|
width: 50%;
|
||||||
|
transform: translateX(0);
|
||||||
|
transition: transform 0.6s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.overlay-left {
|
||||||
|
transform: translateX(-20%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.container.right-panel-active .overlay-left {
|
||||||
|
transform: translateX(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.overlay-right {
|
||||||
|
right: 0;
|
||||||
|
transform: translateX(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.container.right-panel-active .overlay-right {
|
||||||
|
transform: translateX(20%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.social-container {
|
||||||
|
margin: 20px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.social-container a {
|
||||||
|
border: 1px solid #DDDDDD;
|
||||||
|
border-radius: 50%;
|
||||||
|
display: inline-flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
margin: 0 5px;
|
||||||
|
height: 40px;
|
||||||
|
width: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
footer {
|
||||||
|
background-color: #222;
|
||||||
|
color: #fff;
|
||||||
|
font-size: 14px;
|
||||||
|
bottom: 0;
|
||||||
|
position: fixed;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
text-align: center;
|
||||||
|
z-index: 999;
|
||||||
|
}
|
||||||
|
|
||||||
|
footer p {
|
||||||
|
margin: 10px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
footer i {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
|
||||||
|
footer a {
|
||||||
|
color: #3c97bf;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
55
src/pages/admin/category-management/CategoryManagement.tsx
Normal file
55
src/pages/admin/category-management/CategoryManagement.tsx
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
import React, { useState } from 'react'
|
||||||
|
import { IconData } from './iconData'
|
||||||
|
import type { Category } from '../../../interfaces/cateogry.interface'
|
||||||
|
import { Button, Space, Table } from 'antd'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
|
||||||
|
export default function CategoryManagement() {
|
||||||
|
|
||||||
|
const [categoryList, setCategoryList] = useState<Category[]>(JSON.parse(localStorage.getItem("categoryList") || "[]"))
|
||||||
|
|
||||||
|
console.log('categoryList', categoryList[0].icon)
|
||||||
|
const {t} = useTranslation()
|
||||||
|
|
||||||
|
const columns = [
|
||||||
|
{
|
||||||
|
title: 'Id',
|
||||||
|
dataIndex: 'id',
|
||||||
|
key: 'id',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Title',
|
||||||
|
dataIndex: 'title',
|
||||||
|
key: 'title',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Icon',
|
||||||
|
key: 'address',
|
||||||
|
render: (_: any, record: Category) => (
|
||||||
|
<Space size="middle">
|
||||||
|
<i className={record.icon}></i>
|
||||||
|
</Space>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Status',
|
||||||
|
key: 'isActive',
|
||||||
|
render: (_: any, record: Category) => (
|
||||||
|
<Space size="middle">
|
||||||
|
{
|
||||||
|
record.isActive ?
|
||||||
|
<Button color="danger" variant="solid">{t('block')}</Button> :
|
||||||
|
<Button color="cyan" variant="solid" >{t('unlock')}</Button>
|
||||||
|
}
|
||||||
|
</Space>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<h1>CategoryManagement</h1>
|
||||||
|
<Table dataSource={categoryList} columns={columns} />;
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
export default function FormAdd() {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
FormAdd
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
182
src/pages/admin/category-management/iconData.ts
Normal file
182
src/pages/admin/category-management/iconData.ts
Normal file
@@ -0,0 +1,182 @@
|
|||||||
|
export const IconData = [
|
||||||
|
"fa-classic fa-solid fa-house ",
|
||||||
|
"fa-classic fa-solid fa-circle-user ",
|
||||||
|
"fa-classic fa-solid fa-image ",
|
||||||
|
"fa-classic fa-solid fa-file ",
|
||||||
|
"fa-classic fa-solid fa-camera ",
|
||||||
|
"fa-classic fa-solid fa-calendar ",
|
||||||
|
"fa-classic fa-solid fa-cloud ",
|
||||||
|
"fa-classic fa-solid fa-alarm-clock ",
|
||||||
|
"fa-classic fa-solid fa-truck ",
|
||||||
|
"fa-classic fa-solid fa-thumbs-up ",
|
||||||
|
"fa-classic fa-solid fa-face-smile ",
|
||||||
|
"fa-classic fa-solid fa-headphones ",
|
||||||
|
"fa-classic fa-solid fa-bell ",
|
||||||
|
"fa-classic fa-solid fa-user ",
|
||||||
|
"fa-classic fa-solid fa-comment ",
|
||||||
|
"fa-classic fa-solid fa-envelope ",
|
||||||
|
"fa-classic fa-solid fa-globe ",
|
||||||
|
"fa-classic fa-solid fa-trophy ",
|
||||||
|
"fa-classic fa-solid fa-eye ",
|
||||||
|
"fa-classic fa-solid fa-inbox ",
|
||||||
|
"fa-classic fa-solid fa-print ",
|
||||||
|
"fa-classic fa-solid fa-suitcase ",
|
||||||
|
"fa-classic fa-solid fa-volume ",
|
||||||
|
"fa-classic fa-solid fa-magnifying-glass ",
|
||||||
|
"fa-classic fa-solid fa-check ",
|
||||||
|
"fa-classic fa-solid fa-download ",
|
||||||
|
"fa-classic fa-solid fa-font-awesome ",
|
||||||
|
"fa-classic fa-solid fa-web-awesome ",
|
||||||
|
"fa-classic fa-solid fa-phone ",
|
||||||
|
"fa-classic fa-solid fa-bars ",
|
||||||
|
"fa-classic fa-solid fa-star ",
|
||||||
|
"fa-classic fa-solid fa-location-dot ",
|
||||||
|
"fa-classic fa-solid fa-music ",
|
||||||
|
"fa-classic fa-solid fa-wand-magic-sparkles ",
|
||||||
|
"fa-classic fa-solid fa-face-awesome ",
|
||||||
|
"fa-classic fa-solid fa-heart ",
|
||||||
|
"fa-classic fa-solid fa-arrow-right ",
|
||||||
|
"fa-classic fa-solid fa-circle-xmark ",
|
||||||
|
"fa-classic fa-solid fa-bomb ",
|
||||||
|
"fa-classic fa-solid fa-poo ",
|
||||||
|
"fa-classic fa-solid fa-camera-retro ",
|
||||||
|
"fa-classic fa-solid fa-xmark ",
|
||||||
|
"fa-classic fa-solid fa-caret-up ",
|
||||||
|
"fa-classic fa-solid fa-truck-fast ",
|
||||||
|
"fa-classic fa-solid fa-pen-nib ",
|
||||||
|
"fa-classic fa-solid fa-arrow-up ",
|
||||||
|
"fa-classic fa-solid fa-hippo ",
|
||||||
|
"fa-classic fa-solid fa-calendar-days ",
|
||||||
|
"fa-classic fa-solid fa-paperclip ",
|
||||||
|
"fa-classic fa-solid fa-shield-halved ",
|
||||||
|
"fa-classic fa-solid fa-cart-shopping ",
|
||||||
|
"fa-classic fa-solid fa-clipboard ",
|
||||||
|
"fa-classic fa-solid fa-filter ",
|
||||||
|
"fa-classic fa-solid fa-circle-info ",
|
||||||
|
"fa-classic fa-solid fa-arrow-up-from-bracket ",
|
||||||
|
"fa-classic fa-solid fa-bolt ",
|
||||||
|
"fa-classic fa-solid fa-raygun ",
|
||||||
|
"fa-classic fa-solid fa-car ",
|
||||||
|
"fa-classic fa-solid fa-ghost ",
|
||||||
|
"fa-classic fa-solid fa-mug-hot ",
|
||||||
|
"fa-classic fa-solid fa-rocket-launch ",
|
||||||
|
"fa-classic fa-solid fa-pen ",
|
||||||
|
"fa-classic fa-solid fa-umbrella ",
|
||||||
|
"fa-classic fa-solid fa-gift ",
|
||||||
|
"fa-classic fa-solid fa-film ",
|
||||||
|
"fa-classic fa-solid fa-grid-2 ",
|
||||||
|
"fa-classic fa-solid fa-list ",
|
||||||
|
"fa-classic fa-solid fa-gear ",
|
||||||
|
"fa-classic fa-solid fa-trash ",
|
||||||
|
"fa-classic fa-solid fa-circle-up ",
|
||||||
|
"fa-classic fa-solid fa-circle-down ",
|
||||||
|
"fa-classic fa-solid fa-rotate-right ",
|
||||||
|
"fa-classic fa-solid fa-sparkles ",
|
||||||
|
"fa-classic fa-solid fa-lock ",
|
||||||
|
"fa-classic fa-solid fa-barcode ",
|
||||||
|
"fa-classic fa-solid fa-tag ",
|
||||||
|
"fa-classic fa-solid fa-book ",
|
||||||
|
"fa-classic fa-solid fa-bookmark ",
|
||||||
|
"fa-classic fa-solid fa-font ",
|
||||||
|
"fa-classic fa-solid fa-video ",
|
||||||
|
"fa-classic fa-solid fa-circle-half-stroke ",
|
||||||
|
"fa-classic fa-solid fa-droplet ",
|
||||||
|
"fa-classic fa-solid fa-pen-to-square ",
|
||||||
|
"fa-classic fa-solid fa-share-from-square ",
|
||||||
|
"fa-classic fa-solid fa-plus ",
|
||||||
|
"fa-classic fa-solid fa-minus ",
|
||||||
|
"fa-classic fa-solid fa-share ",
|
||||||
|
"fa-classic fa-solid fa-circle-exclamation ",
|
||||||
|
"fa-classic fa-solid fa-fire ",
|
||||||
|
"fa-classic fa-solid fa-eye-slash ",
|
||||||
|
"fa-classic fa-solid fa-plane ",
|
||||||
|
"fa-classic fa-solid fa-magnet ",
|
||||||
|
"fa-classic fa-solid fa-hand ",
|
||||||
|
"fa-classic fa-solid fa-folder ",
|
||||||
|
"fa-classic fa-solid fa-folder-open ",
|
||||||
|
"fa-classic fa-solid fa-money-bill ",
|
||||||
|
"fa-classic fa-solid fa-thumbs-down ",
|
||||||
|
"fa-classic fa-solid fa-comments ",
|
||||||
|
"fa-classic fa-solid fa-grill-hot ",
|
||||||
|
"fa-classic fa-solid fa-lemon ",
|
||||||
|
"fa-classic fa-solid fa-key ",
|
||||||
|
"fa-classic fa-solid fa-thumbtack ",
|
||||||
|
"fa-classic fa-solid fa-gears ",
|
||||||
|
"fa-classic fa-solid fa-paper-plane ",
|
||||||
|
"fa-classic fa-solid fa-code ",
|
||||||
|
"fa-classic fa-solid fa-text ",
|
||||||
|
"fa-classic fa-solid fa-arrow-down-to-line ",
|
||||||
|
"fa-classic fa-solid fa-city ",
|
||||||
|
"fa-classic fa-solid fa-ticket ",
|
||||||
|
"fa-classic fa-solid fa-tree ",
|
||||||
|
"fa-classic fa-solid fa-wifi ",
|
||||||
|
"fa-classic fa-solid fa-window ",
|
||||||
|
"fa-classic fa-solid fa-burger-soda ",
|
||||||
|
"fa-classic fa-solid fa-camera-movie ",
|
||||||
|
"fa-classic fa-solid fa-paint-roller ",
|
||||||
|
"fa-classic fa-solid fa-bicycle ",
|
||||||
|
"fa-classic fa-solid fa-sliders ",
|
||||||
|
"fa-classic fa-solid fa-brush ",
|
||||||
|
"fa-classic fa-solid fa-hashtag ",
|
||||||
|
"fa-classic fa-solid fa-megaphone ",
|
||||||
|
"fa-classic fa-solid fa-flask ",
|
||||||
|
"fa-classic fa-solid fa-briefcase ",
|
||||||
|
"fa-classic fa-solid fa-compass ",
|
||||||
|
"fa-classic fa-solid fa-dumpster-fire ",
|
||||||
|
"fa-classic fa-solid fa-person ",
|
||||||
|
"fa-classic fa-solid fa-person-dress ",
|
||||||
|
"fa-classic fa-solid fa-balloons ",
|
||||||
|
"fa-classic fa-solid fa-address-book ",
|
||||||
|
"fa-classic fa-solid fa-bath ",
|
||||||
|
"fa-classic fa-solid fa-handshake ",
|
||||||
|
"fa-classic fa-solid fa-snowflake ",
|
||||||
|
"fa-classic fa-solid fa-right-to-bracket ",
|
||||||
|
"fa-classic fa-solid fa-earth-americas ",
|
||||||
|
"fa-classic fa-solid fa-cloud-arrow-up ",
|
||||||
|
"fa-classic fa-solid fa-binoculars ",
|
||||||
|
"fa-classic fa-solid fa-palette ",
|
||||||
|
"fa-classic fa-solid fa-layer-group ",
|
||||||
|
"fa-classic fa-solid fa-brackets-curly ",
|
||||||
|
"fa-classic fa-solid fa-users ",
|
||||||
|
"fa-classic fa-solid fa-gamepad ",
|
||||||
|
"fa-classic fa-solid fa-business-time ",
|
||||||
|
"fa-classic fa-solid fa-feather ",
|
||||||
|
"fa-classic fa-solid fa-sun ",
|
||||||
|
"fa-classic fa-solid fa-computer-classic ",
|
||||||
|
"fa-classic fa-solid fa-link ",
|
||||||
|
"fa-classic fa-solid fa-pen-fancy ",
|
||||||
|
"fa-classic fa-solid fa-badge-check ",
|
||||||
|
"fa-classic fa-solid fa-fish ",
|
||||||
|
"fa-classic fa-solid fa-joystick ",
|
||||||
|
"fa-classic fa-solid fa-play-pause ",
|
||||||
|
"fa-classic fa-solid fa-bug ",
|
||||||
|
"fa-classic fa-solid fa-shop ",
|
||||||
|
"fa-classic fa-solid fa-mug-saucer ",
|
||||||
|
"fa-classic fa-solid fa-planet-ringed ",
|
||||||
|
"fa-classic fa-solid fa-landmark ",
|
||||||
|
"fa-classic fa-solid fa-campfire ",
|
||||||
|
"fa-classic fa-solid fa-poo-storm ",
|
||||||
|
"fa-classic fa-solid fa-chart-simple ",
|
||||||
|
"fa-classic fa-solid fa-shirt ",
|
||||||
|
"fa-classic fa-solid fa-files ",
|
||||||
|
"fa-classic fa-solid fa-dolphin ",
|
||||||
|
"fa-classic fa-solid fa-anchor ",
|
||||||
|
"fa-classic fa-solid fa-quote-left ",
|
||||||
|
"fa-classic fa-solid fa-atom-simple ",
|
||||||
|
"fa-classic fa-solid fa-bag-shopping ",
|
||||||
|
"fa-classic fa-solid fa-bed-front ",
|
||||||
|
"fa-classic fa-solid fa-people-simple ",
|
||||||
|
"fa-classic fa-solid fa-gauge ",
|
||||||
|
"fa-classic fa-solid fa-signal-bars ",
|
||||||
|
"fa-classic fa-solid fa-code-compare ",
|
||||||
|
"fa-classic fa-solid fa-user-secret ",
|
||||||
|
"fa-classic fa-solid fa-stethoscope ",
|
||||||
|
"fa-classic fa-solid fa-car-side ",
|
||||||
|
"fa-classic fa-solid fa-hand-holding-heart ",
|
||||||
|
"fa-classic fa-solid fa-ufo ",
|
||||||
|
"fa-classic fa-solid fa-alien-8bit ",
|
||||||
|
"fa-classic fa-solid fa-block-question ",
|
||||||
|
"fa-classic fa-solid fa-aperture ",
|
||||||
|
"fa-classic fa-solid fa-bird ",
|
||||||
|
"fa-classic fa-solid fa-strawberry "
|
||||||
|
]
|
||||||
39
src/pages/admin/components/Header.tsx
Normal file
39
src/pages/admin/components/Header.tsx
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
import { MenuFoldOutlined, MenuUnfoldOutlined } from '@ant-design/icons';
|
||||||
|
import { Button, Layout, theme } from 'antd';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
const { Header } = Layout;
|
||||||
|
|
||||||
|
export default function HeaderCom({ collapsed, setCollapsed }: { collapsed: boolean, setCollapsed: any }) {
|
||||||
|
const {
|
||||||
|
token: { colorBgContainer },
|
||||||
|
} = theme.useToken();
|
||||||
|
const { t,i18n } = useTranslation()
|
||||||
|
return (
|
||||||
|
<Header style={{ padding: 0, background: colorBgContainer }}>
|
||||||
|
<Button
|
||||||
|
type="text"
|
||||||
|
icon={collapsed ? <MenuUnfoldOutlined /> : <MenuFoldOutlined />}
|
||||||
|
onClick={() => setCollapsed(!collapsed)}
|
||||||
|
style={{
|
||||||
|
fontSize: '16px',
|
||||||
|
width: 64,
|
||||||
|
height: 64,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<select onChange={(e) => {
|
||||||
|
localStorage.setItem("lng", e.target.value)
|
||||||
|
i18n.changeLanguage(e.target.value)
|
||||||
|
}}>
|
||||||
|
<option value="vi">Tiếng Việt</option>
|
||||||
|
<option value="en">Tiếng Anh</option>
|
||||||
|
<option value="ja">Tiếng Nhật</option>
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<Button onClick={() => {
|
||||||
|
localStorage.removeItem("userLogin")
|
||||||
|
window.location.reload()
|
||||||
|
}}>{t('logout')}</Button>
|
||||||
|
</Header>
|
||||||
|
)
|
||||||
|
}
|
||||||
45
src/pages/admin/components/Slider.tsx
Normal file
45
src/pages/admin/components/Slider.tsx
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
import { UploadOutlined, UserOutlined, VideoCameraOutlined } from '@ant-design/icons'
|
||||||
|
import { Menu } from 'antd'
|
||||||
|
import Sider from 'antd/es/layout/Sider'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import { useNavigate } from 'react-router'
|
||||||
|
|
||||||
|
export default function Slider({ collapsed }: { collapsed: boolean }) {
|
||||||
|
|
||||||
|
const { t } = useTranslation()
|
||||||
|
const navigate = useNavigate()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Sider style={{
|
||||||
|
height: "100vh"
|
||||||
|
}} trigger={null} collapsible collapsed={collapsed}>
|
||||||
|
<div className="demo-logo-vertical" />
|
||||||
|
<Menu
|
||||||
|
theme="dark"
|
||||||
|
mode="inline"
|
||||||
|
defaultSelectedKeys={['1']}
|
||||||
|
items={[
|
||||||
|
{
|
||||||
|
key: 'user',
|
||||||
|
icon: <UserOutlined />,
|
||||||
|
label: t("Quản lý người dùng"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'category',
|
||||||
|
icon: <VideoCameraOutlined />,
|
||||||
|
label: t('category-management'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'product',
|
||||||
|
icon: <VideoCameraOutlined />,
|
||||||
|
label: t('product-management'),
|
||||||
|
},
|
||||||
|
|
||||||
|
]}
|
||||||
|
onClick={(e) => {
|
||||||
|
navigate(e.key)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Sider>
|
||||||
|
)
|
||||||
|
}
|
||||||
9
src/pages/admin/product-management/ProductManagement.tsx
Normal file
9
src/pages/admin/product-management/ProductManagement.tsx
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
export default function ProductManagement() {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
ProductManagement
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
103
src/pages/admin/user-management/UserManagement.tsx
Normal file
103
src/pages/admin/user-management/UserManagement.tsx
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
import React, { useEffect, useState } from 'react'
|
||||||
|
import { UserRole, type User } from '../../../interfaces/user.interface'
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { Button, Space, Table } from 'antd';
|
||||||
|
|
||||||
|
export default function UserManagement() {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
const [userList, setUserList] = useState<User[]>(JSON.parse(localStorage.getItem("userList") || "[]"))
|
||||||
|
|
||||||
|
const columns = [
|
||||||
|
{
|
||||||
|
title: t('user-id'),
|
||||||
|
dataIndex: 'id',
|
||||||
|
key: 'id',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('fullname'),
|
||||||
|
dataIndex: 'fullName',
|
||||||
|
key: 'fullName',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('age'),
|
||||||
|
dataIndex: 'age',
|
||||||
|
key: 'age',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('phone-number'),
|
||||||
|
dataIndex: 'phone',
|
||||||
|
key: 'phone',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('username'),
|
||||||
|
dataIndex: 'userName',
|
||||||
|
key: 'userName',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('email'),
|
||||||
|
dataIndex: 'email',
|
||||||
|
key: 'email',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('role'),
|
||||||
|
dataIndex: 'role',
|
||||||
|
key: 'role',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('active'),
|
||||||
|
key: 'isActive',
|
||||||
|
render: (_: any, record: User) => (
|
||||||
|
<Space size="middle">
|
||||||
|
{
|
||||||
|
record.role != UserRole.ADMIN && (record.isActive ?
|
||||||
|
<Button color="danger" variant="solid" onClick={() => [
|
||||||
|
setUserList(userList.map((user) => {
|
||||||
|
if(user.id == record.id) {
|
||||||
|
return {
|
||||||
|
...record,
|
||||||
|
isActive: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return user
|
||||||
|
}))
|
||||||
|
]}>{t('block')}</Button> :
|
||||||
|
<Button color="cyan" variant="solid" onClick={() => [
|
||||||
|
setUserList(userList.map((user) => {
|
||||||
|
if(user.id == record.id) {
|
||||||
|
return {
|
||||||
|
...record,
|
||||||
|
isActive: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return user
|
||||||
|
}))
|
||||||
|
]}>{t('unlock')}</Button>)
|
||||||
|
}
|
||||||
|
</Space>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: t('tools'),
|
||||||
|
key: 'tools',
|
||||||
|
render: (_: any, record: User) => (
|
||||||
|
<Space size="middle">
|
||||||
|
<Button onClick={() => [
|
||||||
|
console.log("record", record)
|
||||||
|
]}>Test</Button>
|
||||||
|
</Space>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
localStorage.setItem("userList", JSON.stringify(userList))
|
||||||
|
}, [userList])
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<p>{t("user-management")}</p>
|
||||||
|
<Table dataSource={userList.slice().sort((userA, userB) => {
|
||||||
|
return (userB.role == UserRole.ADMIN ? 1 : 0) - (userA.role == UserRole.ADMIN ? 1 : 0)
|
||||||
|
})} columns={columns} />;
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
11
src/pages/user/Collection/CollectionDetail.tsx
Normal file
11
src/pages/user/Collection/CollectionDetail.tsx
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import { useParams } from 'react-router'
|
||||||
|
|
||||||
|
export default function CollectionDetail() {
|
||||||
|
const {slugCollection} = useParams()
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
Collection = {slugCollection}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
9
src/pages/user/Home/About/About.tsx
Normal file
9
src/pages/user/Home/About/About.tsx
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
export default function About() {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
Về Chúng Tôi
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
20
src/pages/user/Home/Home.tsx
Normal file
20
src/pages/user/Home/Home.tsx
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import HomeHeader from './components/HomeHeader'
|
||||||
|
import HomeFooter from './components/HomeFooter'
|
||||||
|
import './home.scss'
|
||||||
|
import HomeBanner from './components/HomeBanner'
|
||||||
|
import { Outlet } from 'react-router'
|
||||||
|
export default function Home() {
|
||||||
|
return (
|
||||||
|
<div className="home_page">
|
||||||
|
<HomeBanner/>
|
||||||
|
<HomeHeader/>
|
||||||
|
<div className='container'>
|
||||||
|
<div className='content'>
|
||||||
|
<Outlet/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<HomeFooter/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
12
src/pages/user/Home/components/HomeBanner.tsx
Normal file
12
src/pages/user/Home/components/HomeBanner.tsx
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import banner from '../../../../assets/img/banner.png'
|
||||||
|
export default function HomeBanner() {
|
||||||
|
return (
|
||||||
|
<div className='home_banner'>
|
||||||
|
<div style={{
|
||||||
|
backgroundImage: `url(${banner})`
|
||||||
|
}} className='content'>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
11
src/pages/user/Home/components/HomeFooter.tsx
Normal file
11
src/pages/user/Home/components/HomeFooter.tsx
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
export default function HomeFooter() {
|
||||||
|
return (
|
||||||
|
<footer>
|
||||||
|
<div className='content'>
|
||||||
|
footer
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
)
|
||||||
|
}
|
||||||
124
src/pages/user/Home/components/HomeHeader.scss
Normal file
124
src/pages/user/Home/components/HomeHeader.scss
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
header {
|
||||||
|
background-color: white;
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
|
||||||
|
.content {
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
|
||||||
|
img {
|
||||||
|
height: 60px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-btn {
|
||||||
|
color: red;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.media_box {
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
font-size: 10px;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
i {
|
||||||
|
font-size: 2em;
|
||||||
|
color: red;
|
||||||
|
margin-right: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
i {
|
||||||
|
margin-right: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.text_box {
|
||||||
|
font-size: 1.5em;
|
||||||
|
color: red;
|
||||||
|
|
||||||
|
p {
|
||||||
|
&:first-child {}
|
||||||
|
|
||||||
|
&:last-child {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.cart_count {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 30%;
|
||||||
|
width: 1.5em;
|
||||||
|
height: 1.5em;
|
||||||
|
font-size: 1em;
|
||||||
|
background-color: yellow;
|
||||||
|
color: black;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
border-radius: 50%;
|
||||||
|
border: 1px solid red;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.user_box {
|
||||||
|
width: 100px;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.login {}
|
||||||
|
|
||||||
|
.unlogin {
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
.unlogin-content {
|
||||||
|
background-color: rgba(220, 218, 218, 0.612);
|
||||||
|
padding: 2px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
font-size: 10px;
|
||||||
|
cursor: pointer;
|
||||||
|
border-radius: 5px;
|
||||||
|
|
||||||
|
i {
|
||||||
|
font-size: 2em;
|
||||||
|
color: red;
|
||||||
|
margin-right: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
i {
|
||||||
|
margin-right: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.text_box {
|
||||||
|
font-size: 1.5em;
|
||||||
|
color: red;
|
||||||
|
|
||||||
|
p {
|
||||||
|
&:first-child {}
|
||||||
|
|
||||||
|
&:last-child {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
83
src/pages/user/Home/components/HomeHeader.tsx
Normal file
83
src/pages/user/Home/components/HomeHeader.tsx
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
import React, { useState } from 'react'
|
||||||
|
import './HomeHeader.scss'
|
||||||
|
import logo from '../../../../assets/img/logo.png'
|
||||||
|
import Search from 'antd/es/input/Search'
|
||||||
|
import { useNavigate } from 'react-router'
|
||||||
|
export default function HomeHeader() {
|
||||||
|
const [isLogin, setIsLogin] = useState(false)
|
||||||
|
const navigate = useNavigate()
|
||||||
|
const menu = [
|
||||||
|
{
|
||||||
|
iconClass: "fa-solid fa-headphones",
|
||||||
|
text: ["Hotline", "1900.5301"],
|
||||||
|
path: "/hotline"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
iconClass: "fa-solid fa-location-dot",
|
||||||
|
text: ["Hệ thống", "Showroom"],
|
||||||
|
path: "/about"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
iconClass: "fa-solid fa-clipboard",
|
||||||
|
text: ["Tra cứu", "Đơn hàng"],
|
||||||
|
path: "/order-history"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
return (
|
||||||
|
<header>
|
||||||
|
<div className='content'>
|
||||||
|
<img onClick={() => {
|
||||||
|
navigate("/")
|
||||||
|
}} src={logo} />
|
||||||
|
<i className="fa-solid fa-bars menu-btn"></i>
|
||||||
|
<Search placeholder="input search text" style={{ width: 200 }} />
|
||||||
|
<div className='media_box'>
|
||||||
|
{
|
||||||
|
menu.map((item) => {
|
||||||
|
return (
|
||||||
|
<div onClick={() => {
|
||||||
|
navigate(item.path)
|
||||||
|
}} className='item'>
|
||||||
|
<i className={item.iconClass}></i>
|
||||||
|
<div className='text_box'>
|
||||||
|
<p>{item.text[0]}</p>
|
||||||
|
<p>{item.text[1]}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
<div className='item'>
|
||||||
|
<i className="fa-solid fa-cart-shopping"></i>
|
||||||
|
<p className='cart_count'>0</p>
|
||||||
|
<div className='text_box'>
|
||||||
|
<p>Giỏ</p>
|
||||||
|
<p>Hàng</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className='user_box'>
|
||||||
|
{
|
||||||
|
isLogin ? (
|
||||||
|
<div className='login'>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className='unlogin'>
|
||||||
|
<div className='unlogin-content'>
|
||||||
|
<i className="fa-solid fa-user"></i>
|
||||||
|
<div className='text_box'>
|
||||||
|
<p>Đăng</p>
|
||||||
|
<p>Nhập</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
)
|
||||||
|
}
|
||||||
54
src/pages/user/Home/home.scss
Normal file
54
src/pages/user/Home/home.scss
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
$widthContent: 1080px;
|
||||||
|
|
||||||
|
.home_page {
|
||||||
|
.home_banner {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
background-color: #C30100;
|
||||||
|
.content {
|
||||||
|
width: $widthContent;
|
||||||
|
}
|
||||||
|
|
||||||
|
height: 60px;
|
||||||
|
}
|
||||||
|
|
||||||
|
header {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
.content {
|
||||||
|
width: $widthContent;
|
||||||
|
}
|
||||||
|
|
||||||
|
height: 60px;
|
||||||
|
|
||||||
|
border-bottom: 1px solid black;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
.content {
|
||||||
|
width: $widthContent;
|
||||||
|
}
|
||||||
|
|
||||||
|
height: calc(100vh - 60px);
|
||||||
|
}
|
||||||
|
|
||||||
|
footer {
|
||||||
|
width: 100%;
|
||||||
|
height: 120px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
.content {
|
||||||
|
width: $widthContent;
|
||||||
|
}
|
||||||
|
|
||||||
|
background-color: yellow;
|
||||||
|
}
|
||||||
|
}
|
||||||
1
src/vite-env.d.ts
vendored
Normal file
1
src/vite-env.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/// <reference types="vite/client" />
|
||||||
27
tsconfig.app.json
Normal file
27
tsconfig.app.json
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
|
||||||
|
"target": "ES2022",
|
||||||
|
"useDefineForClassFields": false,
|
||||||
|
"lib": ["ES2022", "DOM", "DOM.Iterable"],
|
||||||
|
"module": "ESNext",
|
||||||
|
"skipLibCheck": true,
|
||||||
|
|
||||||
|
/* Bundler mode */
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"allowImportingTsExtensions": true,
|
||||||
|
"verbatimModuleSyntax": true,
|
||||||
|
"moduleDetection": "force",
|
||||||
|
"noEmit": true,
|
||||||
|
"jsx": "react-jsx",
|
||||||
|
|
||||||
|
/* Linting */
|
||||||
|
"strict": false,
|
||||||
|
"noUnusedLocals": false,
|
||||||
|
"noUnusedParameters": false,
|
||||||
|
"erasableSyntaxOnly": false,
|
||||||
|
"noFallthroughCasesInSwitch": false,
|
||||||
|
"noUncheckedSideEffectImports": false
|
||||||
|
},
|
||||||
|
"include": ["src"]
|
||||||
|
}
|
||||||
7
tsconfig.json
Normal file
7
tsconfig.json
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"files": [],
|
||||||
|
"references": [
|
||||||
|
{ "path": "./tsconfig.app.json" },
|
||||||
|
{ "path": "./tsconfig.node.json" }
|
||||||
|
]
|
||||||
|
}
|
||||||
25
tsconfig.node.json
Normal file
25
tsconfig.node.json
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
|
||||||
|
"target": "ES2023",
|
||||||
|
"lib": ["ES2023"],
|
||||||
|
"module": "ESNext",
|
||||||
|
"skipLibCheck": true,
|
||||||
|
|
||||||
|
/* Bundler mode */
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"allowImportingTsExtensions": true,
|
||||||
|
"verbatimModuleSyntax": true,
|
||||||
|
"moduleDetection": "force",
|
||||||
|
"noEmit": true,
|
||||||
|
|
||||||
|
/* Linting */
|
||||||
|
"strict": false,
|
||||||
|
"noUnusedLocals": false,
|
||||||
|
"noUnusedParameters": false,
|
||||||
|
"erasableSyntaxOnly": false,
|
||||||
|
"noFallthroughCasesInSwitch": false,
|
||||||
|
"noUncheckedSideEffectImports": false
|
||||||
|
},
|
||||||
|
"include": ["vite.config.ts"]
|
||||||
|
}
|
||||||
7
vite.config.ts
Normal file
7
vite.config.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import { defineConfig } from 'vite'
|
||||||
|
import react from '@vitejs/plugin-react-swc'
|
||||||
|
|
||||||
|
// https://vite.dev/config/
|
||||||
|
export default defineConfig({
|
||||||
|
plugins: [react()],
|
||||||
|
})
|
||||||
Reference in New Issue
Block a user