diff --git a/package.json b/package.json index 1f21a62..331e23c 100644 --- a/package.json +++ b/package.json @@ -3,12 +3,13 @@ "version": "0.1.0", "private": true, "dependencies": { + "@ant-design/icons": "^4.7.0", "@craco/craco": "^6.4.3", "@reduxjs/toolkit": "^1.8.1", "@testing-library/jest-dom": "^4.2.4", "@testing-library/react": "^9.5.0", "@testing-library/user-event": "^7.2.1", - "antd": "^4.19.5", + "antd": "^4.19.2", "aws-amplify": "^4.3.19", "axios": "^0.26.1", "craco-less": "^2.0.0", @@ -20,6 +21,7 @@ "react-scripts": "5.0.0", "redux-saga": "^1.1.3", "styled-components": "^5.3.5", + "tailwindcss": "^3.0.24", "typescript": "^4.1.6" }, "scripts": { diff --git a/src/App.tsx b/src/App.tsx index 28cdb6a..8cbf528 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,3 +1,5 @@ +import { Card } from "antd"; +import Layout from "components/Layout"; import { logout, selectUserSelector } from "containers/Auth/authSlice"; import withAuth from "helpers/withAuth"; import React, { useEffect } from "react"; @@ -15,12 +17,10 @@ function App() { }, []); return ( -
- Hello World, {user?.username}, - -
+ + Hello World + ); } export default withAuth(App); - diff --git a/src/components/Auth/LoginForm.tsx b/src/components/Auth/LoginForm.tsx index d86430c..25a9ecb 100644 --- a/src/components/Auth/LoginForm.tsx +++ b/src/components/Auth/LoginForm.tsx @@ -3,10 +3,11 @@ import React from "react"; import styled from "styled-components"; interface IProps { + loading?: boolean; onFinish: any; } -export default function LoginForm({ onFinish }: IProps) { +export default function LoginForm({ onFinish, loading }: IProps) { return (

B2B Admin Portal

@@ -24,7 +25,7 @@ export default function LoginForm({ onFinish }: IProps) { rules={[ { required: true, - message: "Required field", + message: "Please input required field", }, ]} > @@ -35,13 +36,13 @@ export default function LoginForm({ onFinish }: IProps) { rules={[ { required: true, - message: "Required field", + message: "Please input required field", }, ]} > - @@ -60,9 +61,14 @@ const FormContainer = styled.div` justify-content: center; align-items: center; flex-direction: column; + + h1 { + font-weight: bold; + } `; const FormContent = styled(Card)` + border: 0; max-width: 90%; - width: 350px; + width: 400px; `; diff --git a/src/components/Layout/index.tsx b/src/components/Layout/index.tsx new file mode 100644 index 0000000..12cb283 --- /dev/null +++ b/src/components/Layout/index.tsx @@ -0,0 +1,100 @@ +import { Button, Menu } from "antd"; +import { logout, selectUserSelector } from "containers/Auth/authSlice"; +import { useMemo, useState } from "react"; +import { + RiGroupLine, + RiHome3Line, + RiMenuFoldLine, + RiMenuUnfoldLine, + RiUserLine, +} from "react-icons/ri"; +import { useDispatch, useSelector } from "react-redux"; +import { Link, useLocation } from "react-router-dom"; + +import { + StyledContent, + StyledHeader, + StyledLayout, + StyledSider, +} from "./style"; + +interface IProps { + children?: any; +} + +const Layout = (props: IProps) => { + const [collapsed, setCollapsed] = useState(false); + const user = useSelector(selectUserSelector); + const dispatch = useDispatch(); + const location = useLocation(); + + const toggle = () => { + setCollapsed(!collapsed); + }; + + const openKeys = useMemo(() => { + const list = location.pathname.split("/").filter(Boolean); + + return list.map((item, index) => `${item}-submenu`); + }, [location.pathname]); + + return ( + + +
+ + }> + Home + + } + title="Users" + > + }> + Users + + }> + Groups + + + + + + + + } + > + setVisible(false)} + > +
+ + + + + + + + + + + ); +} + +export default withAuth(Group); diff --git a/src/containers/User/index.tsx b/src/containers/User/index.tsx new file mode 100644 index 0000000..157aa73 --- /dev/null +++ b/src/containers/User/index.tsx @@ -0,0 +1,9 @@ +import Layout from "components/Layout"; +import withAuth from "helpers/withAuth"; +import React from "react"; + +function User() { + return User; +} + +export default withAuth(User); diff --git a/src/index.less b/src/index.less index 902372e..cea5584 100644 --- a/src/index.less +++ b/src/index.less @@ -1,11 +1,29 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; @import "~antd/dist/antd.less"; @font-family: "Roboto", sans-serif; -@padding-lg: 30px; -@padding-md: 24px; +// vertical paddings +@padding-lg: 30px; // containers +@padding-md: 24px; // small containers and buttons +@padding-sm: 18px; // Form controls and items +@padding-xs: 12px; // small items +@padding-xss: 8px; // more small + +// vertical margins +@margin-lg: 30px; // containers +@margin-md: 24px; // small containers and buttons +@margin-sm: 18px; // Form controls and items +@margin-xs: 12px; // small items +@margin-xss: 8px; // more small + @border-radius-base: 5px; @primary-color: #1f6dff; @info-color: #20063b; @success-color: #4ebc5e; @warning-color: #ffb427; @error-color: #ff3341; +@height-base: 40px; +@height-lg: 42px; +@height-sm: 32px; diff --git a/src/index.tsx b/src/index.tsx index 683af54..8676e76 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -4,6 +4,7 @@ import { Auth } from "aws-amplify"; import config from "aws-exports"; import React from "react"; import ReactDOM from "react-dom"; +import { createRoot } from "react-dom/client"; import { Provider } from "react-redux"; import AppRouter from "routes"; import { store } from "store"; diff --git a/src/react-app-env.d.ts b/src/react-app-env.d.ts index 6431bc5..a2b5c95 100644 --- a/src/react-app-env.d.ts +++ b/src/react-app-env.d.ts @@ -1 +1,6 @@ /// +declare module "react-dom/client" { + // typing module default export as `any` will allow you to access its members without compiler warning + var createRoot: any; + export { createRoot }; +} diff --git a/src/routes/index.tsx b/src/routes/index.tsx index aabfd57..97bef92 100644 --- a/src/routes/index.tsx +++ b/src/routes/index.tsx @@ -4,14 +4,20 @@ import { BrowserRouter as Router, Route, Routes } from "react-router-dom"; const App = React.lazy(() => import("App")); const Login = React.lazy(() => import("containers/Auth/Login")); +const User = React.lazy(() => import("containers/User")); +const Group = React.lazy(() => import("containers/User/Group")); function AppRouter() { return ( }> - } /> - } /> + + } /> + } /> + + } /> + } /> @@ -19,4 +25,3 @@ function AppRouter() { } export default AppRouter; - diff --git a/src/styles/globalStyle.ts b/src/styles/globalStyle.ts index ca37aac..6319bd8 100644 --- a/src/styles/globalStyle.ts +++ b/src/styles/globalStyle.ts @@ -1,3 +1,13 @@ import { createGlobalStyle } from "styled-components"; -export const GlobalStyle = createGlobalStyle``; +export const GlobalStyle = createGlobalStyle` + html, body{ + margin: 0; + padding: 0; + } + + #root{ + min-height: 100vh; + height: 100%; + } +`; diff --git a/tailwind.config.js b/tailwind.config.js new file mode 100644 index 0000000..a4e483b --- /dev/null +++ b/tailwind.config.js @@ -0,0 +1,7 @@ +module.exports = { + content: ["./src/**/*.{js,ts,jsx,tsx}"], + theme: { + extend: {}, + }, + plugins: [], +}; diff --git a/yarn.lock b/yarn.lock index 7679156..eca8061 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4626,10 +4626,10 @@ ansi-styles@^5.0.0: resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b" integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA== -antd@^4.19.5: - version "4.19.5" - resolved "https://registry.yarnpkg.com/antd/-/antd-4.19.5.tgz#38d08f3e1391a7a69c2ca76f50968bb12ec2ac93" - integrity sha512-C4H/VJqlVO5iMvHZyiV27R8SbPs4jsOKCGPhDXIHUry/RnUCbMmVeQaPRfUIxSI1NbqDflsuQfevPtz1svyIlg== +antd@4.19.2: + version "4.19.2" + resolved "https://registry.yarnpkg.com/antd/-/antd-4.19.2.tgz#5438478aedf61bf670784611cba077387ff0f1b3" + integrity sha512-qXM6d6hFsT5rZJB4TRTHl32dixu1IHmPD0QLzG5YuUpzecv6QoTr1ZMp2/I1MMCb1+n99yQ5gFx7U677AbL5hw== dependencies: "@ant-design/colors" "^6.0.0" "@ant-design/icons" "^4.7.0" @@ -4647,7 +4647,7 @@ antd@^4.19.5: rc-dialog "~8.6.0" rc-drawer "~4.4.2" rc-dropdown "~3.3.2" - rc-field-form "~1.25.0" + rc-field-form "~1.24.0" rc-image "~5.2.5" rc-input "~0.0.1-alpha.5" rc-input-number "~7.3.0" @@ -8708,6 +8708,11 @@ object-hash@^2.2.0: resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-2.2.0.tgz#5ad518581eefc443bd763472b8ff2e9c2c0d54a5" integrity sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw== +object-hash@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-3.0.0.tgz#73f97f753e7baffc0e2cc9d6e079079744ac82e9" + integrity sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw== + object-inspect@^1.12.0, object-inspect@^1.9.0: version "1.12.0" resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.0.tgz#6e2c120e868fd1fd18cb4f18c31741d0d6e776f0" @@ -9241,7 +9246,7 @@ postcss-lab-function@^4.1.2: "@csstools/postcss-progressive-custom-properties" "^1.1.0" postcss-value-parser "^4.2.0" -postcss-load-config@^3.1.0: +postcss-load-config@^3.1.0, postcss-load-config@^3.1.4: version "3.1.4" resolved "https://registry.yarnpkg.com/postcss-load-config/-/postcss-load-config-3.1.4.tgz#1ab2571faf84bb078877e1d07905eabe9ebda855" integrity sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg== @@ -9581,7 +9586,7 @@ postcss@^7.0.35: picocolors "^0.2.1" source-map "^0.6.1" -postcss@^8.3.5, postcss@^8.4.4, postcss@^8.4.6, postcss@^8.4.7: +postcss@^8.3.5, postcss@^8.4.12, postcss@^8.4.4, postcss@^8.4.6, postcss@^8.4.7: version "8.4.12" resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.12.tgz#1e7de78733b28970fa4743f7da6f3763648b1905" integrity sha512-lg6eITwYe9v6Hr5CncVbK70SoioNQIq81nsaG86ev5hAidQvmOeETBqs7jm43K2F5/Ley3ytDtriImV6TpNiSg== @@ -9840,10 +9845,10 @@ rc-dropdown@~3.3.2: rc-trigger "^5.0.4" rc-util "^5.17.0" -rc-field-form@~1.25.0: - version "1.25.0" - resolved "https://registry.yarnpkg.com/rc-field-form/-/rc-field-form-1.25.0.tgz#5497252e67e2eb5daca6513ba4255edc67cf5896" - integrity sha512-mOl0YTgIw7d9EyNySgqw6HlchOt5AGliIRWEIaTt8kP+LHjXBYqOQ+tzVUH2ALren6G568UJicQ2SeMFpfCWHQ== +rc-field-form@~1.24.0: + version "1.24.0" + resolved "https://registry.yarnpkg.com/rc-field-form/-/rc-field-form-1.24.0.tgz#2510a5c34713831ddcb412d4560be9057fc0dc5e" + integrity sha512-5beNBU5gEyi8YRYyqbTWSu5hO0jZQN0AWpY3U7TcllUKrDLcZZdRXuAOpyxJQcttWFs+UAFsbcRAUtnOGBjl7w== dependencies: "@babel/runtime" "^7.8.4" async-validator "^4.0.2" @@ -11204,6 +11209,33 @@ tailwindcss@^3.0.2: quick-lru "^5.1.1" resolve "^1.22.0" +tailwindcss@^3.0.24: + version "3.0.24" + resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-3.0.24.tgz#22e31e801a44a78a1d9a81ecc52e13b69d85704d" + integrity sha512-H3uMmZNWzG6aqmg9q07ZIRNIawoiEcNFKDfL+YzOPuPsXuDXxJxB9icqzLgdzKNwjG3SAro2h9SYav8ewXNgig== + dependencies: + arg "^5.0.1" + chokidar "^3.5.3" + color-name "^1.1.4" + detective "^5.2.0" + didyoumean "^1.2.2" + dlv "^1.1.3" + fast-glob "^3.2.11" + glob-parent "^6.0.2" + is-glob "^4.0.3" + lilconfig "^2.0.5" + normalize-path "^3.0.0" + object-hash "^3.0.0" + picocolors "^1.0.0" + postcss "^8.4.12" + postcss-js "^4.0.0" + postcss-load-config "^3.1.4" + postcss-nested "5.0.6" + postcss-selector-parser "^6.0.10" + postcss-value-parser "^4.2.0" + quick-lru "^5.1.1" + resolve "^1.22.0" + tapable@^1.0.0: version "1.1.3" resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.1.3.tgz#a1fccc06b58db61fd7a45da2da44f5f3a3e67ba2"