├── src ├── styles │ ├── _utils.scss │ ├── _font.scss │ ├── _size.scss │ ├── typography.scss │ ├── _color.scss │ ├── App.scss │ ├── _mixin.scss │ ├── _container.scss │ └── _reset.scss ├── features │ ├── home │ │ ├── components │ │ │ ├── Home.tsx │ │ │ └── tag │ │ │ │ ├── index.tsx │ │ │ │ └── style.scss │ │ ├── types │ │ │ └── index.ts │ │ ├── route.ts │ │ ├── style.scss │ │ └── index.tsx │ └── core │ │ ├── route.ts │ │ └── components │ │ └── login │ │ ├── index.tsx │ │ └── style.scss ├── layouts │ ├── main │ │ ├── style.scss │ │ └── index.tsx │ ├── index.tsx │ ├── footer │ │ ├── style.scss │ │ └── index.tsx │ ├── Auth.tsx │ └── header │ │ ├── index.tsx │ │ └── style.scss ├── locales │ ├── languages.ts │ ├── resources │ │ ├── footer.json │ │ ├── home.json │ │ └── index.ts │ └── i18n.ts ├── setupTests.ts ├── constants │ └── env.ts ├── apis │ ├── auth.ts │ └── todo.ts ├── components │ ├── page-loading │ │ ├── style.scss │ │ └── index.tsx │ ├── spinner │ │ ├── index.tsx │ │ └── style.scss │ ├── not-found │ │ ├── index.tsx │ │ └── style.scss │ ├── button │ │ ├── Button.test.tsx │ │ ├── index.tsx │ │ └── style.scss │ └── select │ │ ├── style.scss │ │ └── index.tsx ├── App.test.tsx ├── helpers │ ├── local-storage.ts │ ├── router.ts │ ├── axios.ts │ └── toast.ts ├── hooks │ ├── useShallowEqualSelector.tsx │ ├── useDidMountEffect.tsx │ ├── useOutsideClick.tsx │ ├── useSafeState.tsx │ └── useWindowSize.tsx ├── index.tsx ├── services │ ├── http-request.ts │ └── axios-base.ts ├── router │ └── index.ts ├── store │ ├── slices │ │ ├── appSlice.ts │ │ ├── layoutSlice.ts │ │ └── authSlice.ts │ └── index.ts ├── jest.config.js ├── App.tsx ├── __snapshots__ │ └── App.test.tsx.snap └── utils │ └── index.ts ├── .eslintignore ├── config ├── jest │ ├── cssMock.js │ ├── svgrMock.js │ └── routeMock.js ├── @types │ └── index.d.ts ├── paths.js └── env.js ├── .prettierignore ├── .env.development ├── public ├── favicon.ico ├── static │ └── images │ │ ├── banner.png │ │ ├── webpack.png │ │ ├── login-avatar.png │ │ └── icon │ │ ├── stack-overflow.svg │ │ ├── arrow-down.svg │ │ ├── vi.svg │ │ ├── linkedin.svg │ │ ├── view-details.svg │ │ ├── en.svg │ │ └── github.svg ├── index.html └── manifest.json ├── .vscode └── settings.json ├── .prettierrc ├── babel.config.js ├── pm2 └── prod.json ├── .gitignore ├── tsconfig.json ├── server.js ├── LICENSE ├── docs ├── index.html ├── README.md └── style.css ├── webpack ├── webpack.dev.js ├── webpack.common.js └── webpack.prod.js ├── README.md ├── .eslintrc.js └── package.json /src/styles/_utils.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | build -------------------------------------------------------------------------------- /src/features/home/components/Home.tsx: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /config/jest/cssMock.js: -------------------------------------------------------------------------------- 1 | module.exports = {}; 2 | -------------------------------------------------------------------------------- /src/styles/_font.scss: -------------------------------------------------------------------------------- 1 | html { 2 | font-family: system-ui; 3 | } 4 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | build/ 2 | node_modules/ 3 | package-lock.json 4 | yarn.lock -------------------------------------------------------------------------------- /.env.development: -------------------------------------------------------------------------------- 1 | NODE_ENV=development 2 | API_SERVER_URL=http://localhost:3000 3 | PORT=3000 -------------------------------------------------------------------------------- /config/jest/svgrMock.js: -------------------------------------------------------------------------------- 1 | export default 'SvgrURL'; 2 | export const ReactComponent = 'div'; 3 | -------------------------------------------------------------------------------- /src/layouts/main/style.scss: -------------------------------------------------------------------------------- 1 | #main { 2 | flex: 1 1 auto; 3 | // min-height: 70vh; 4 | } 5 | -------------------------------------------------------------------------------- /src/locales/languages.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | vi: 'Vietnamese', 3 | en: 'English', 4 | }; 5 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thaind97git/react-typescript-webpack/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /src/features/home/types/index.ts: -------------------------------------------------------------------------------- 1 | export interface ITag { 2 | color?: string; 3 | label: string; 4 | } 5 | -------------------------------------------------------------------------------- /public/static/images/banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thaind97git/react-typescript-webpack/HEAD/public/static/images/banner.png -------------------------------------------------------------------------------- /public/static/images/webpack.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thaind97git/react-typescript-webpack/HEAD/public/static/images/webpack.png -------------------------------------------------------------------------------- /src/styles/_size.scss: -------------------------------------------------------------------------------- 1 | $screenXL: 1920px; 2 | $screenLG: 1280px; 3 | $screenMD: 1024px; 4 | $screenSM: 768px; 5 | $screenXS: 375px; 6 | -------------------------------------------------------------------------------- /public/static/images/login-avatar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thaind97git/react-typescript-webpack/HEAD/public/static/images/login-avatar.png -------------------------------------------------------------------------------- /src/setupTests.ts: -------------------------------------------------------------------------------- 1 | import { configure } from 'enzyme'; 2 | import React16Adapter from 'enzyme-adapter-react-16'; 3 | 4 | configure({ adapter: new React16Adapter() }); 5 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "cSpell.words": [ 3 | "eslintignore", 4 | "huskyinstall", 5 | "prettierignore", 6 | "toastify", 7 | "todos" 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /src/styles/typography.scss: -------------------------------------------------------------------------------- 1 | @mixin typo($font-size, $font-weight, $line-height) { 2 | font-size: $font-size; 3 | font-weight: $font-weight; 4 | line-height: $line-height; 5 | } 6 | -------------------------------------------------------------------------------- /src/constants/env.ts: -------------------------------------------------------------------------------- 1 | const { NODE_ENV, API_SERVER_URL, PORT } = process.env; 2 | 3 | export default { 4 | NodeEnv: NODE_ENV, 5 | ApiServerUrl: API_SERVER_URL, 6 | Port: PORT, 7 | }; 8 | -------------------------------------------------------------------------------- /src/locales/resources/footer.json: -------------------------------------------------------------------------------- 1 | { 2 | "en": { 3 | "copy_right": "Copyright (c) 2021 by Aldenn" 4 | }, 5 | "vi": { 6 | "copy_right": "Bản quyền (c) 2021 bởi Aldenn" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 80, 3 | "tabWidth": 2, 4 | "useTabs": false, 5 | "semi": true, 6 | "singleQuote": true, 7 | "trailingComma": "all", 8 | "arrowParens": "avoid" 9 | } 10 | -------------------------------------------------------------------------------- /src/apis/auth.ts: -------------------------------------------------------------------------------- 1 | import { AxiosResponse } from 'axios'; 2 | import HttpRequest from '@/services/http-request'; 3 | 4 | export const getCurrentUser = async (): Promise => 5 | HttpRequest.get('/api/login/GetInfoToken'); 6 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = function (api) { 2 | api.cache(true); 3 | return { 4 | presets: ['@babel/preset-env', '@babel/preset-react'], 5 | plugins: ['@babel/plugin-transform-runtime'], 6 | sourceType: 'unambiguous', 7 | }; 8 | }; 9 | -------------------------------------------------------------------------------- /src/features/home/route.ts: -------------------------------------------------------------------------------- 1 | import { lazy } from 'react'; 2 | 3 | const Home = lazy(() => import('./index')); 4 | 5 | export default [ 6 | { 7 | name: 'home', 8 | path: '/', 9 | exact: true, 10 | component: Home, 11 | }, 12 | ]; 13 | -------------------------------------------------------------------------------- /src/components/page-loading/style.scss: -------------------------------------------------------------------------------- 1 | .page-loading { 2 | height: 100vh; 3 | width: 100%; 4 | position: fixed; 5 | top: 0px; 6 | left: 0px; 7 | top: 0px; 8 | bottom: 0px; 9 | z-index: 9999; 10 | background-color: rgba(255, 255, 255, 0.2); 11 | } 12 | -------------------------------------------------------------------------------- /pm2/prod.json: -------------------------------------------------------------------------------- 1 | { 2 | "apps": [ 3 | { 4 | "name": "react-webpack-typescript-starter", 5 | "cwd": "./", 6 | "kill_timeout": 3000, 7 | "restart_delay": 3000, 8 | "script": "npm", 9 | "args": "run start" 10 | } 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /src/styles/_color.scss: -------------------------------------------------------------------------------- 1 | // text color 2 | $primary-color: #257aa6; // primary color for all components 3 | $link-color: #1890ff; // link color 4 | $success-color: #52c41a; // success state color 5 | $warning-color: #faad14; // warning state color 6 | $error-color: #f5222d; // error state color 7 | $text-color: #333; // major text color 8 | -------------------------------------------------------------------------------- /src/features/core/route.ts: -------------------------------------------------------------------------------- 1 | import { lazy } from 'react'; 2 | 3 | const Login = lazy(() => import('./components/login/index')); 4 | 5 | export default [ 6 | { 7 | name: 'login', 8 | path: '/login', 9 | exact: true, 10 | layout: { 11 | header: false, 12 | footer: false, 13 | }, 14 | component: Login, 15 | }, 16 | ]; 17 | -------------------------------------------------------------------------------- /src/components/spinner/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const Spinner: React.FC = () => { 4 | return ( 5 |
6 |
7 |
8 |
9 |
10 | ); 11 | }; 12 | 13 | export default Spinner; 14 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | React Webpack Typescript Starter 6 | 7 | 8 | 9 |
10 | 11 | 12 | -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64", 8 | "type": "image/png" 9 | } 10 | ], 11 | "start_url": "./index.html", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /src/App.test.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { createRenderer } from 'react-test-renderer/shallow'; 3 | import App from './App'; 4 | 5 | const renderer = createRenderer(); 6 | 7 | it('renders without crashing', () => { 8 | renderer.render(); 9 | const renderedOutput = renderer.getRenderOutput(); 10 | expect(renderedOutput).toMatchSnapshot(); 11 | }); 12 | -------------------------------------------------------------------------------- /src/layouts/index.tsx: -------------------------------------------------------------------------------- 1 | import React, { Fragment } from 'react'; 2 | 3 | import Header from './header'; 4 | import Footer from './footer'; 5 | import Main from './main'; 6 | 7 | const Layout: React.FC = () => { 8 | return ( 9 | 10 |
11 |
12 |