Compare commits
8 Commits
d5451f51b9
...
main
Author | SHA1 | Date | |
---|---|---|---|
Sambo Chea | 9d14fe60f0 | ||
Sambo Chea | 3d4e0e7664 | ||
Sambo Chea | 025ff76a20 | ||
Sambo Chea | a50437a27b | ||
Sambo Chea | 85c81f5e2a | ||
Sambo Chea | 26451a4291 | ||
Sambo Chea | 371b4fad15 | ||
Sambo Chea | 4c0fe442cd |
|
@ -0,0 +1,7 @@
|
|||
.vscode/
|
||||
node_modules/
|
||||
npm-debug.log
|
||||
dist/
|
||||
build/
|
||||
test/
|
||||
yarn-error.log
|
|
@ -0,0 +1,4 @@
|
|||
node_modules
|
||||
build
|
||||
dist
|
||||
coverage
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"trailingComma": "es5",
|
||||
"tabWidth": 4,
|
||||
"semi": false,
|
||||
"singleQuote": true
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||
"editor.formatOnSave": true,
|
||||
"[dockerfile]": {
|
||||
"editor.defaultFormatter": "ms-azuretools.vscode-docker"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
# Build application
|
||||
FROM cubetiq/calpine-node AS builder
|
||||
|
||||
RUN apk update && \
|
||||
# For build commit hash in "process.env.COMMIT_ID"
|
||||
apk add git && \
|
||||
apk add tzdata && \
|
||||
cp /usr/share/zoneinfo/Asia/Phnom_Penh /etc/localtime && \
|
||||
echo "Asia/Phnom_Penh" > /etc/timezone
|
||||
|
||||
WORKDIR /app
|
||||
COPY package.json ./
|
||||
# Set custom registry for npm registry (from cubetiq local server)
|
||||
RUN yarn config set registry https://r.ctdn.net
|
||||
RUN yarn
|
||||
COPY . .
|
||||
RUN yarn build
|
||||
|
||||
# Clean up unused packages
|
||||
RUN apk del tzdata && \
|
||||
apk del git
|
||||
|
||||
# Build production image
|
||||
FROM nginx:alpine
|
||||
LABEL maintainer="sombochea@cubetiqs.com"
|
||||
|
||||
WORKDIR /usr/share/nginx/html
|
||||
COPY --from=builder /app/build/ /usr/share/nginx/html
|
||||
COPY --from=builder /app/docker/nginx.conf /etc/nginx/conf.d
|
||||
RUN rm /etc/nginx/conf.d/default.conf
|
||||
EXPOSE 80
|
|
@ -1,10 +1,14 @@
|
|||
# CUBETIQ React App
|
||||
|
||||
CUBETIQ React App Template for general use in react application.
|
||||
|
||||
# Libraries
|
||||
|
||||
- Create React App: 4.0.3
|
||||
- React: 17.0.0
|
||||
- TypeScript: 4.1.2
|
||||
- Craco: 6.1.2 | Craco less: 1.17.1
|
||||
|
||||
# Contributors
|
||||
- Sambo Chea <sombochea@cubetiqs.com>
|
||||
|
||||
- Sambo Chea <sombochea@cubetiqs.com>
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
const path = require('path')
|
||||
const webpack = require('webpack')
|
||||
const npmPackage = require('./package.json')
|
||||
const { v4: uuidv4 } = require('uuid')
|
||||
const { execSync } = require('child_process')
|
||||
|
||||
// Get the last commit id/log
|
||||
const gitFetchCommitIdCommand = 'git rev-parse HEAD'
|
||||
|
||||
// Execute the command
|
||||
const fetchGitCommitId = () => {
|
||||
try {
|
||||
return execSync(gitFetchCommitIdCommand).toString().trim()
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
return '-1'
|
||||
}
|
||||
}
|
||||
|
||||
// get current date (build date time)
|
||||
const today = new Date()
|
||||
|
||||
// get date as version suffix: 10112021
|
||||
const dateVersion = `${today.getDay()}${today.getMonth()}${today.getFullYear()}`
|
||||
|
||||
module.exports = {
|
||||
webpack: {
|
||||
alias: {
|
||||
'@': path.resolve(__dirname, 'src/'),
|
||||
},
|
||||
plugins: {
|
||||
add: [
|
||||
new webpack.DefinePlugin({
|
||||
'process.env.PACKAGE_NAME': `"${npmPackage.name}"`,
|
||||
'process.env.PACKAGE_VERSION': `"${dateVersion}"`,
|
||||
'process.env.BUILD_NUMBER': `"${uuidv4()}"`,
|
||||
'process.env.BUILD_DATE': `"${today.toLocaleString()}"`,
|
||||
'process.env.COMMIT_ID': `"${fetchGitCommitId()}"`,
|
||||
}),
|
||||
],
|
||||
},
|
||||
},
|
||||
jest: {
|
||||
configure: {
|
||||
moduleNameMapper: {
|
||||
'^@(.*)$': '<rootDir>/src$1',
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
server {
|
||||
listen 80;
|
||||
# serve static files
|
||||
location ~ ^/(images|javascript|js|css|flash|media|static)/ {
|
||||
root /usr/share/nginx/html;
|
||||
expires 30d;
|
||||
}
|
||||
|
||||
# serve default location
|
||||
location / {
|
||||
root /usr/share/nginx/html;
|
||||
index index.html index.htm;
|
||||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
|
||||
error_page 500 502 503 504 /50x.html;
|
||||
|
||||
location = /50x.html {
|
||||
root /usr/share/nginx/html;
|
||||
}
|
||||
}
|
42
package.json
42
package.json
|
@ -1,27 +1,28 @@
|
|||
{
|
||||
"name": "cubetiq-react-app",
|
||||
"version": "0.1.0",
|
||||
"author": {
|
||||
"name": "Sambo Chea",
|
||||
"email": "sombochea@cubetiqs.com",
|
||||
"url": "https://github.com/SomboChea"
|
||||
},
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
"url": "https://git.cubetiqs.com/CUBETIQ/cubetiq-react-app.git"
|
||||
},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@testing-library/jest-dom": "^5.11.4",
|
||||
"@testing-library/react": "^11.1.0",
|
||||
"@testing-library/user-event": "^12.1.10",
|
||||
"@types/jest": "^26.0.15",
|
||||
"@types/node": "^12.0.0",
|
||||
"@types/react": "^17.0.0",
|
||||
"@types/react-dom": "^17.0.0",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-router-dom": "^5.2.0",
|
||||
"react-scripts": "4.0.3",
|
||||
"typescript": "^4.1.2",
|
||||
"web-vitals": "^1.0.1"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "react-scripts start",
|
||||
"build": "react-scripts build",
|
||||
"test": "react-scripts test",
|
||||
"eject": "react-scripts eject"
|
||||
"start": "craco start",
|
||||
"build": "craco build",
|
||||
"test": "craco test",
|
||||
"eject": "react-scripts eject",
|
||||
"format": "prettier --write ."
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": [
|
||||
|
@ -40,5 +41,20 @@
|
|||
"last 1 firefox version",
|
||||
"last 1 safari version"
|
||||
]
|
||||
},
|
||||
"devDependencies": {
|
||||
"@craco/craco": "^6.4.3",
|
||||
"@testing-library/jest-dom": "^5.11.4",
|
||||
"@testing-library/react": "^11.1.0",
|
||||
"@testing-library/user-event": "^12.1.10",
|
||||
"@types/jest": "^26.0.15",
|
||||
"@types/node": "^12.0.0",
|
||||
"@types/react": "^17.0.0",
|
||||
"@types/react-dom": "^17.0.0",
|
||||
"@types/react-router-dom": "^5.1.7",
|
||||
"craco-less": "^2.0.0",
|
||||
"prettier": "^2.3.1",
|
||||
"react-scripts": "5.0.0",
|
||||
"typescript": "^4.5.4"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,20 +1,18 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<meta name="description" content="%REACT_APP_DESCRIPTION%" />
|
||||
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
|
||||
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
|
||||
<title>%REACT_APP_NAME%</title>
|
||||
</head>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<meta name="description" content="%REACT_APP_DESCRIPTION%" />
|
||||
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
|
||||
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
|
||||
<title>%REACT_APP_NAME%</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
<div id="root"></div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
<div id="root"></div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
#!/bin/sh -e
|
||||
|
||||
echo "Building docker image..."
|
||||
docker build . -t myreactapp:latest
|
||||
|
||||
echo "Running docker image..."
|
||||
docker run --cpus=".1" --memory="5m" --rm -it -p 3003:3000 myreactapp:latest
|
||||
|
||||
# Now you can access the app at http://localhost:3003
|
10
src/App.tsx
10
src/App.tsx
|
@ -1,10 +0,0 @@
|
|||
import React from 'react';
|
||||
import './App.css';
|
||||
|
||||
export default function App() {
|
||||
return (
|
||||
<div className="App">
|
||||
<h1>CUBETIQ</h1>
|
||||
</div>
|
||||
);
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
export const APP_NAME = process.env.PACKAGE_NAME
|
||||
export const APP_VERSION = process.env.PACKAGE_VERSION
|
||||
export const APP_BUILD_NUMBER = process.env.BUILD_NUMBER
|
||||
export const APP_BUILD_DATE = process.env.BUILD_DATE
|
||||
export const APP_COMMIT_ID = process.env.COMMIT_ID
|
|
@ -1,3 +1,3 @@
|
|||
.App {
|
||||
text-align: center;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
import AppProvider from '@/provider/AppProvider'
|
||||
import RouterView from '../routes'
|
||||
import './App.less'
|
||||
|
||||
const App = () => {
|
||||
return (
|
||||
<AppProvider><RouterView /></AppProvider>
|
||||
)
|
||||
}
|
||||
|
||||
export default App
|
|
@ -0,0 +1,5 @@
|
|||
export const AppConfig = {
|
||||
APP_NAME: process.env.REACT_APP_NAME,
|
||||
|
||||
CLIENT_TOKEN_KEY: "client_token",
|
||||
}
|
|
@ -0,0 +1,131 @@
|
|||
import { AppConfig } from '@/config'
|
||||
import { printError, printInfo } from '@/utils/log_util'
|
||||
import { getStorage, setStorage } from '@/utils/ls_util'
|
||||
import React, { useContext, useCallback, useReducer, useEffect } from 'react'
|
||||
|
||||
interface AuthContextState {
|
||||
login: (args: { username: string; password: string }) => Promise<void>
|
||||
state: {
|
||||
user: {
|
||||
username: string
|
||||
userId: string
|
||||
} | null
|
||||
}
|
||||
getToken: () => string | undefined | null
|
||||
logout: () => void
|
||||
isLogin: () => boolean
|
||||
}
|
||||
|
||||
enum AuthActionType {
|
||||
'LOGIN' = 'LOGIN',
|
||||
'LOGOUT' = 'LOGOUT',
|
||||
}
|
||||
|
||||
const AuthReducer: (
|
||||
state: AuthContextState['state'],
|
||||
action: {
|
||||
type: AuthActionType
|
||||
payload?: any
|
||||
}
|
||||
) => AuthContextState['state'] = (state, action) => {
|
||||
switch (action.type) {
|
||||
case AuthActionType.LOGIN:
|
||||
return {
|
||||
...state,
|
||||
user: action.payload,
|
||||
}
|
||||
case AuthActionType.LOGOUT:
|
||||
return {
|
||||
...state,
|
||||
user: null,
|
||||
}
|
||||
default:
|
||||
return state
|
||||
}
|
||||
}
|
||||
|
||||
const AuthContext = React.createContext<AuthContextState>({
|
||||
login: async () => {},
|
||||
state: {
|
||||
user: null,
|
||||
},
|
||||
getToken: () => {
|
||||
return undefined
|
||||
},
|
||||
logout: () => {},
|
||||
isLogin: () => { return false },
|
||||
})
|
||||
|
||||
const AuthProvider: React.FC = (props) => {
|
||||
const [state, dispatch] = useReducer(AuthReducer, {
|
||||
user: null,
|
||||
})
|
||||
|
||||
const login = async (args: { username: string; password: string }) => {
|
||||
const res = {
|
||||
data: {
|
||||
token: 'DEMO_HAH',
|
||||
},
|
||||
}
|
||||
|
||||
setStorage(AppConfig.CLIENT_TOKEN_KEY, res.data.token)
|
||||
await verify()
|
||||
dispatch({
|
||||
type: AuthActionType.LOGIN,
|
||||
payload: args,
|
||||
})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
const doVerify = async () => {
|
||||
const token = getStorage(AppConfig.CLIENT_TOKEN_KEY)
|
||||
const userAuthInfo = {}
|
||||
|
||||
printInfo("User details =>", token, userAuthInfo)
|
||||
}
|
||||
|
||||
doVerify()
|
||||
}, [])
|
||||
|
||||
const verify = async () => {
|
||||
const token = getStorage(AppConfig.CLIENT_TOKEN_KEY)
|
||||
printError('Verify not implemented with token: ', token)
|
||||
const userAuthInfo = {}
|
||||
return userAuthInfo
|
||||
}
|
||||
|
||||
const logout = useCallback(() => {
|
||||
setStorage(AppConfig.CLIENT_TOKEN_KEY, undefined)
|
||||
dispatch({
|
||||
type: AuthActionType.LOGOUT,
|
||||
})
|
||||
}, [])
|
||||
|
||||
const getToken = () => getStorage(AppConfig.CLIENT_TOKEN_KEY)
|
||||
|
||||
const isLogin = (): boolean => {
|
||||
return getToken() != null
|
||||
}
|
||||
|
||||
return (
|
||||
<AuthContext.Provider
|
||||
value={{
|
||||
login,
|
||||
state,
|
||||
logout,
|
||||
isLogin,
|
||||
getToken,
|
||||
}}
|
||||
>
|
||||
{props.children}
|
||||
</AuthContext.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
export const useAuthContext = () => {
|
||||
return useContext(AuthContext)
|
||||
}
|
||||
|
||||
export default AuthProvider
|
|
@ -5,4 +5,4 @@ body {
|
|||
sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
}
|
|
@ -1,17 +1,17 @@
|
|||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import './index.css';
|
||||
import App from './App';
|
||||
import reportWebVitals from './reportWebVitals';
|
||||
import React from 'react'
|
||||
import ReactDOM from 'react-dom'
|
||||
import './index.less'
|
||||
import App from '@/app/App'
|
||||
import reportWebVitals from './reportWebVitals'
|
||||
|
||||
ReactDOM.render(
|
||||
<React.StrictMode>
|
||||
<App />
|
||||
</React.StrictMode>,
|
||||
document.getElementById('root')
|
||||
);
|
||||
)
|
||||
|
||||
// If you want to start measuring performance in your app, pass a function
|
||||
// to log results (for example: reportWebVitals(console.log))
|
||||
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
|
||||
reportWebVitals();
|
||||
reportWebVitals()
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
.about-title {
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
import './index.less'
|
||||
|
||||
export default function About() {
|
||||
return (
|
||||
<div>
|
||||
<h1 className="about-title">About Us</h1>
|
||||
</div>
|
||||
)
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
export default function AccessDenied() {
|
||||
return (
|
||||
<div>
|
||||
<h1>Access denied!</h1>
|
||||
</div>
|
||||
)
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
export default function NotFound() {
|
||||
return (
|
||||
<div>
|
||||
<h1>Not found!</h1>
|
||||
</div>
|
||||
)
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
.home-title {
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
font-size: large;
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
import './index.less'
|
||||
|
||||
export default function Home() {
|
||||
return (
|
||||
<div>
|
||||
<h1 className="home-title">Home</h1>
|
||||
</div>
|
||||
)
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
import {
|
||||
APP_BUILD_DATE,
|
||||
APP_BUILD_NUMBER,
|
||||
APP_COMMIT_ID,
|
||||
APP_NAME,
|
||||
APP_VERSION,
|
||||
} from '@/app.config'
|
||||
import { RouteTypes } from '@/routes/types'
|
||||
import './index.less'
|
||||
|
||||
export default function Info() {
|
||||
return (
|
||||
<div>
|
||||
<h1>App Info</h1>
|
||||
<p>App name: {APP_NAME}</p>
|
||||
<p>App version: {APP_VERSION}</p>
|
||||
<p>App build number: {APP_BUILD_NUMBER}</p>
|
||||
<p>App build date: {APP_BUILD_DATE}</p>
|
||||
<p> App commit id: {APP_COMMIT_ID}</p>
|
||||
|
||||
<br />
|
||||
<a href={RouteTypes.RESET}>Reset</a>
|
||||
</div>
|
||||
)
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
.login-title {
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
import './index.less'
|
||||
|
||||
export default function Login() {
|
||||
return (
|
||||
<div>
|
||||
<h1 className="login-title">Login</h1>
|
||||
</div>
|
||||
)
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
export default function Profile() {
|
||||
return (
|
||||
<div>
|
||||
<h1>Profile</h1>
|
||||
</div>
|
||||
)
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
import { RouteTypes } from '@/routes/types'
|
||||
import { clearStorage } from '@/utils/ls_util'
|
||||
import { useEffect } from 'react'
|
||||
import { Redirect } from 'react-router'
|
||||
|
||||
const Reset = () => {
|
||||
const clearAllCaches = () => {
|
||||
caches.keys().then((cacheNames) => {
|
||||
cacheNames.forEach((cacheName) => {
|
||||
caches.delete(cacheName).then((r) => console.log('Caches cleared', r))
|
||||
})
|
||||
})
|
||||
}
|
||||
useEffect(() => {
|
||||
clearStorage()
|
||||
sessionStorage.clear()
|
||||
clearAllCaches()
|
||||
}, [])
|
||||
|
||||
return <Redirect to={RouteTypes.HOME} />
|
||||
}
|
||||
|
||||
export default Reset
|
|
@ -0,0 +1,5 @@
|
|||
const AppProvider = (props: any) => {
|
||||
return <>{props.children}</>
|
||||
}
|
||||
|
||||
export default AppProvider
|
|
@ -1,15 +1,15 @@
|
|||
import { ReportHandler } from 'web-vitals';
|
||||
import { ReportHandler } from 'web-vitals'
|
||||
|
||||
const reportWebVitals = (onPerfEntry?: ReportHandler) => {
|
||||
if (onPerfEntry && onPerfEntry instanceof Function) {
|
||||
import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
|
||||
getCLS(onPerfEntry);
|
||||
getFID(onPerfEntry);
|
||||
getFCP(onPerfEntry);
|
||||
getLCP(onPerfEntry);
|
||||
getTTFB(onPerfEntry);
|
||||
});
|
||||
getCLS(onPerfEntry)
|
||||
getFID(onPerfEntry)
|
||||
getFCP(onPerfEntry)
|
||||
getLCP(onPerfEntry)
|
||||
getTTFB(onPerfEntry)
|
||||
})
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export default reportWebVitals;
|
||||
export default reportWebVitals
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
import { useAuthContext } from '@/context/AuthContext'
|
||||
import AccessDenied from '@/pages/Error/403'
|
||||
import { Route } from 'react-router-dom'
|
||||
|
||||
const AuthRoute = (props: any) => {
|
||||
const { component, ...rest } = props
|
||||
const { isLogin } = useAuthContext()
|
||||
|
||||
if (!isLogin()) {
|
||||
return (<AccessDenied />)
|
||||
}
|
||||
|
||||
return (
|
||||
<Route
|
||||
{...rest}
|
||||
render={component}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export default AuthRoute
|
|
@ -0,0 +1,36 @@
|
|||
import AuthProvider from '@/context/AuthContext'
|
||||
import { BrowserRouter as Router, Switch, Route } from 'react-router-dom'
|
||||
import AuthRoute from './AuthRoute'
|
||||
import { routes } from './routes'
|
||||
|
||||
const RouterView = () => {
|
||||
return (
|
||||
<Router>
|
||||
<Switch>
|
||||
{routes.map((route) => {
|
||||
const { withAuthority } = route
|
||||
if (withAuthority) {
|
||||
return (
|
||||
<AuthProvider key={route.key}>
|
||||
<AuthRoute {...route} />
|
||||
</AuthProvider>
|
||||
)
|
||||
} else {
|
||||
return (
|
||||
<Route
|
||||
key={route.key}
|
||||
exact={route.exact}
|
||||
component={route.component}
|
||||
children={route.children}
|
||||
location={route.location}
|
||||
path={route.path}
|
||||
/>
|
||||
)
|
||||
}
|
||||
})}
|
||||
</Switch>
|
||||
</Router>
|
||||
)
|
||||
}
|
||||
|
||||
export default RouterView
|
|
@ -0,0 +1,27 @@
|
|||
import { RouteProps } from 'react-router-dom'
|
||||
|
||||
export interface Authority {
|
||||
authority?: string | undefined
|
||||
strict?: boolean | undefined
|
||||
with?: Authority | undefined
|
||||
}
|
||||
|
||||
export interface AuthorityProps {
|
||||
withAuthority?: boolean | undefined
|
||||
authorities?: Authority | string[] | string | undefined
|
||||
strictAuthority?: boolean | undefined
|
||||
}
|
||||
|
||||
export interface CustomRouteProps extends RouteProps, AuthorityProps {
|
||||
key: string
|
||||
}
|
||||
|
||||
export interface MenuProps {
|
||||
icon: any
|
||||
label?: string | undefined
|
||||
}
|
||||
|
||||
export interface SideSubMenuProps extends MenuProps {
|
||||
key?: string | undefined
|
||||
subMenus: SideSubMenuProps[]
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
import About from '@/pages/About'
|
||||
import NotFound from '@/pages/Error/404'
|
||||
import Home from '@/pages/Home'
|
||||
import Info from '@/pages/Info'
|
||||
import Login from '@/pages/Login'
|
||||
import Profile from '@/pages/Profile'
|
||||
import Reset from '@/pages/Reset'
|
||||
import { CustomRouteProps } from '@/routes/interfaces'
|
||||
import { RouteTypes } from '@/routes/types'
|
||||
|
||||
const routes: CustomRouteProps[] = [
|
||||
// Auth
|
||||
{
|
||||
key: 'login',
|
||||
path: RouteTypes.LOGIN,
|
||||
component: () => <Login />,
|
||||
},
|
||||
{
|
||||
key: 'home',
|
||||
exact: true,
|
||||
path: RouteTypes.HOME,
|
||||
component: () => <Home />,
|
||||
},
|
||||
{
|
||||
key: 'about',
|
||||
path: RouteTypes.ABOUT,
|
||||
component: () => <About />,
|
||||
},
|
||||
{
|
||||
exact: true,
|
||||
key: 'info',
|
||||
path: RouteTypes.INFO,
|
||||
component: () => <Info />,
|
||||
},
|
||||
{
|
||||
exact: true,
|
||||
key: 'reset',
|
||||
path: RouteTypes.RESET,
|
||||
component: () => <Reset />,
|
||||
},
|
||||
{
|
||||
exact: true,
|
||||
key: 'profile',
|
||||
path: RouteTypes.PROFILE,
|
||||
component: () => <Profile />,
|
||||
withAuthority: true,
|
||||
},
|
||||
|
||||
// Errors
|
||||
{
|
||||
key: 'notfound',
|
||||
path: RouteTypes.ERROR_404,
|
||||
component: () => <NotFound />,
|
||||
},
|
||||
]
|
||||
|
||||
export { routes }
|
|
@ -0,0 +1,17 @@
|
|||
const RouteTypes = {
|
||||
HOME: "/",
|
||||
ABOUT: "/about",
|
||||
PROFILE: "/profile",
|
||||
INFO: "/info",
|
||||
RESET: "/reset",
|
||||
|
||||
// Auth
|
||||
LOGIN: '/login',
|
||||
|
||||
// Errors
|
||||
ERROR_404: "**",
|
||||
}
|
||||
|
||||
export {
|
||||
RouteTypes,
|
||||
}
|
|
@ -2,4 +2,4 @@
|
|||
// allows you to do things like:
|
||||
// expect(element).toHaveTextContent(/react/i)
|
||||
// learn more: https://github.com/testing-library/jest-dom
|
||||
import '@testing-library/jest-dom';
|
||||
import '@testing-library/jest-dom'
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
export const printInfo = (message?: any, ...opts: any[]) => console.log(message, opts)
|
||||
export const printError = (message?: any, ...opts: any[]) => console.error(message, opts)
|
||||
export const printWarn = (message?: any, ...opts: any[]) => console.warn(message, opts)
|
|
@ -0,0 +1,11 @@
|
|||
export const setStorage = (key: string, value?: any) => {
|
||||
localStorage.setItem(key, JSON.stringify(value))
|
||||
}
|
||||
|
||||
export const getStorage = (key: string, defaultValue?: string): (string | undefined | null) => {
|
||||
return localStorage.getItem(key) ?? defaultValue
|
||||
}
|
||||
|
||||
export const clearStorage = () => {
|
||||
return localStorage.clear()
|
||||
}
|
|
@ -20,6 +20,7 @@
|
|||
"noEmit": true,
|
||||
"jsx": "react-jsx"
|
||||
},
|
||||
"extends": "./tsconfig.paths.json",
|
||||
"include": [
|
||||
"src"
|
||||
]
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"baseUrl": "./",
|
||||
"paths": {
|
||||
"@/*": ["src/*"]
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user