mirror of
https://github.com/Telecominfraproject/wlan-cloud-ui-library.git
synced 2025-11-01 03:08:08 +00:00
init
This commit is contained in:
29
.babelrc
Normal file
29
.babelrc
Normal file
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"presets": [
|
||||
[
|
||||
"@babel/preset-env",
|
||||
{
|
||||
"modules": false
|
||||
}
|
||||
],
|
||||
"@babel/preset-react"
|
||||
],
|
||||
"plugins": [
|
||||
"@babel/plugin-proposal-class-properties",
|
||||
["import", { "libraryName": "antd", "libraryDirectory": "es", "style": true }] // `style: true` for less
|
||||
],
|
||||
"env": {
|
||||
"production": {
|
||||
"only": ["app"],
|
||||
"plugins": [
|
||||
"lodash",
|
||||
"transform-react-remove-prop-types",
|
||||
"@babel/plugin-transform-react-inline-elements",
|
||||
"@babel/plugin-transform-react-constant-elements"
|
||||
]
|
||||
},
|
||||
"test": {
|
||||
"plugins": ["@babel/plugin-transform-modules-commonjs", "dynamic-import-node"]
|
||||
}
|
||||
}
|
||||
}
|
||||
19
.editorconfig
Normal file
19
.editorconfig
Normal file
@@ -0,0 +1,19 @@
|
||||
# http://editorconfig.org
|
||||
root = true
|
||||
[*]
|
||||
charset = utf-8
|
||||
end_of_line = lf
|
||||
indent_size = 2
|
||||
indent_style = space
|
||||
insert_final_newline = true
|
||||
max_line_length = 100
|
||||
trim_trailing_whitespace = true
|
||||
[*.md]
|
||||
max_line_length = 0
|
||||
trim_trailing_whitespace = false
|
||||
[{Makefile,**.mk}]
|
||||
# Use tabs for indentation (Makefiles require tabs)
|
||||
indent_style = tab
|
||||
[*.scss]
|
||||
indent_size = 2
|
||||
indent_style = space
|
||||
40
.eslintrc
Normal file
40
.eslintrc
Normal file
@@ -0,0 +1,40 @@
|
||||
{
|
||||
"extends": ["airbnb", "prettier", "prettier/react"],
|
||||
"plugins": ["prettier"],
|
||||
"rules": {
|
||||
"react/jsx-filename-extension": [
|
||||
1,
|
||||
{
|
||||
"extensions": [".js", ".jsx"]
|
||||
}
|
||||
],
|
||||
"react/prop-types": 0,
|
||||
"no-underscore-dangle": 0,
|
||||
"import/imports-first": ["error", "absolute-first"],
|
||||
"import/newline-after-import": "error",
|
||||
"import/prefer-default-export": 0,
|
||||
"react/jsx-props-no-spreading": 0,
|
||||
"semi": "error"
|
||||
},
|
||||
"globals": {
|
||||
"window": true,
|
||||
"document": true,
|
||||
"localStorage": true,
|
||||
"FormData": true,
|
||||
"FileReader": true,
|
||||
"Blob": true,
|
||||
"navigator": true,
|
||||
"Headers": true,
|
||||
"Request": true,
|
||||
"fetch": true
|
||||
},
|
||||
"parser": "babel-eslint",
|
||||
"settings": {
|
||||
"import/resolver": {
|
||||
"node": {
|
||||
"extensions": [".js", ".jsx", ".ts", ".tsx"],
|
||||
"moduleDirectory": ["node_modules", "app"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
23
.gitignore
vendored
Normal file
23
.gitignore
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
/.pnp
|
||||
.pnp.js
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# production
|
||||
/dist
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
4
.prettierrc
Normal file
4
.prettierrc
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"singleQuote": true,
|
||||
"trailingComma": "es5"
|
||||
}
|
||||
49
app/components/GlobalHeader/GlobalHeader.module.scss
Normal file
49
app/components/GlobalHeader/GlobalHeader.module.scss
Normal file
@@ -0,0 +1,49 @@
|
||||
@import 'styles/variables';
|
||||
|
||||
.GlobalHeader {
|
||||
height: $header-height;
|
||||
left: $sidebar-width;
|
||||
padding: 0;
|
||||
position: fixed;
|
||||
display: flex;
|
||||
right: 0;
|
||||
z-index: 2000;
|
||||
|
||||
&.collapsed {
|
||||
left: $sidebar-collapsed-width;
|
||||
}
|
||||
&.mobile {
|
||||
left: 0;
|
||||
}
|
||||
|
||||
:global(.ant-row) {
|
||||
margin-bottom: 10px;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.LogoContainer {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 0 0 0 24px;
|
||||
|
||||
img {
|
||||
border-radius: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.MenuIcon {
|
||||
cursor: pointer;
|
||||
font-size: 18px;
|
||||
line-height: 64px;
|
||||
padding: 0 24px;
|
||||
}
|
||||
|
||||
.RightMenu {
|
||||
margin-left: auto;
|
||||
}
|
||||
87
app/components/GlobalHeader/index.js
Normal file
87
app/components/GlobalHeader/index.js
Normal file
@@ -0,0 +1,87 @@
|
||||
import React, { useState } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { Layout, Popover, Row } from 'antd';
|
||||
import { MenuUnfoldOutlined, MenuFoldOutlined, SettingOutlined } from '@ant-design/icons';
|
||||
|
||||
import styles from './GlobalHeader.module.scss';
|
||||
|
||||
const { Header } = Layout;
|
||||
|
||||
const GlobalHeader = ({ collapsed, onMenuButtonClick, isMobile, logoMobile }) => {
|
||||
const [popoverVisible, setPopoverVisible] = useState(false);
|
||||
|
||||
const hidePopover = () => {
|
||||
setPopoverVisible(false);
|
||||
};
|
||||
|
||||
const handleVisibleChange = visible => {
|
||||
setPopoverVisible(visible);
|
||||
};
|
||||
|
||||
const userOptions = (
|
||||
<>
|
||||
<Row>
|
||||
<Link onClick={hidePopover} to="/accounts/customers/view">
|
||||
Profile
|
||||
</Link>
|
||||
</Row>
|
||||
<Row>
|
||||
<Link onClick={hidePopover} to="/account">
|
||||
Users
|
||||
</Link>
|
||||
</Row>
|
||||
<Row>
|
||||
<Link onClick={hidePopover} to="/accounts">
|
||||
Advanced
|
||||
</Link>
|
||||
</Row>
|
||||
<Row>
|
||||
<Link onClick={hidePopover} to="/accounts/customersxw">
|
||||
Rules Preference
|
||||
</Link>
|
||||
</Row>
|
||||
</>
|
||||
);
|
||||
|
||||
return (
|
||||
<Header
|
||||
className={`${styles.GlobalHeader} ${collapsed ? styles.collapsed : ''} ${
|
||||
isMobile ? styles.mobile : ''
|
||||
}`}
|
||||
>
|
||||
{isMobile && [
|
||||
<Link className={styles.LogoContainer} to="/" key="mobileLogo">
|
||||
<img src={logoMobile} alt="logo" width="32" />
|
||||
</Link>,
|
||||
]}
|
||||
{collapsed ? (
|
||||
<MenuUnfoldOutlined className={styles.MenuIcon} onClick={onMenuButtonClick} />
|
||||
) : (
|
||||
<MenuFoldOutlined className={styles.MenuIcon} onClick={onMenuButtonClick} />
|
||||
)}
|
||||
<div className={styles.RightMenu}>
|
||||
<Popover
|
||||
content={userOptions}
|
||||
trigger="click"
|
||||
getPopupContainer={e => e.parentElement}
|
||||
visible={popoverVisible}
|
||||
onVisibleChange={handleVisibleChange}
|
||||
placement="bottomRight"
|
||||
arrowPointAtCenter
|
||||
>
|
||||
<SettingOutlined className={styles.MenuIcon} />
|
||||
</Popover>
|
||||
</div>
|
||||
</Header>
|
||||
);
|
||||
};
|
||||
|
||||
GlobalHeader.propTypes = {
|
||||
collapsed: PropTypes.bool.isRequired,
|
||||
onMenuButtonClick: PropTypes.func.isRequired,
|
||||
isMobile: PropTypes.bool.isRequired,
|
||||
logoMobile: PropTypes.string.isRequired
|
||||
};
|
||||
|
||||
export default GlobalHeader;
|
||||
37
app/components/SideMenu/Sider.module.scss
Normal file
37
app/components/SideMenu/Sider.module.scss
Normal file
@@ -0,0 +1,37 @@
|
||||
@import 'styles/variables';
|
||||
|
||||
.Sider {
|
||||
height: 100vh;
|
||||
left: 0;
|
||||
position: fixed;
|
||||
|
||||
&.Mobile {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
&.collapsed {
|
||||
.Logo {
|
||||
width: 32px;
|
||||
}
|
||||
}
|
||||
|
||||
.TopArea {
|
||||
height: $header-height;
|
||||
}
|
||||
|
||||
.LogoContainer {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.Logo {
|
||||
width: 200px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.MenuIcon {
|
||||
margin-right: 10px!important;
|
||||
}
|
||||
}
|
||||
271
app/components/SideMenu/index.js
Normal file
271
app/components/SideMenu/index.js
Normal file
@@ -0,0 +1,271 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { Layout, Menu, Drawer } from 'antd';
|
||||
import {
|
||||
DashboardOutlined,
|
||||
ProfileOutlined,
|
||||
AreaChartOutlined,
|
||||
MobileOutlined,
|
||||
ApiOutlined,
|
||||
NotificationOutlined,
|
||||
CheckCircleOutlined,
|
||||
SettingOutlined,
|
||||
TeamOutlined,
|
||||
LogoutOutlined,
|
||||
} from '@ant-design/icons';
|
||||
|
||||
import styles from './Sider.module.scss';
|
||||
|
||||
const { Sider } = Layout;
|
||||
const { SubMenu, Item } = Menu;
|
||||
|
||||
const ACCOUNTS = 'accounts';
|
||||
const NETWORK = 'network';
|
||||
const CONFIGURATION = 'configuration';
|
||||
const INSIGHTS = 'insights';
|
||||
const SYSTEM = 'system';
|
||||
const HISTORY = 'history';
|
||||
const rootSubmenuKeys = [ACCOUNTS, NETWORK, CONFIGURATION, INSIGHTS, SYSTEM, HISTORY];
|
||||
|
||||
const SideMenu = ({
|
||||
locationState,
|
||||
collapsed,
|
||||
isMobile,
|
||||
logo,
|
||||
logoMobile,
|
||||
onMenuButtonClick,
|
||||
onMenuItemClick,
|
||||
onLogout,
|
||||
}) => {
|
||||
const [openKeys, setOpenKeys] = useState([]);
|
||||
|
||||
useEffect(() => {
|
||||
setOpenKeys([]);
|
||||
}, [collapsed]);
|
||||
|
||||
const enocMenuItems = [
|
||||
{
|
||||
key: 'dashboard',
|
||||
icon: <DashboardOutlined className={styles.MenuIcon} />,
|
||||
path: '/',
|
||||
text: 'Dashboard',
|
||||
onClick: onMenuItemClick,
|
||||
},
|
||||
{
|
||||
key: 'profiles',
|
||||
icon: <ProfileOutlined className={styles.MenuIcon} />,
|
||||
path: '/profiles',
|
||||
text: 'Profiles',
|
||||
onClick: onMenuItemClick,
|
||||
},
|
||||
{
|
||||
key: 'reports',
|
||||
icon: <AreaChartOutlined className={styles.MenuIcon} />,
|
||||
path: '/analytics/qoe',
|
||||
text: 'Insights',
|
||||
onClick: onMenuItemClick,
|
||||
},
|
||||
{
|
||||
key: 'client-devices',
|
||||
icon: <MobileOutlined className={styles.MenuIcon} />,
|
||||
path: '/network/client-devices',
|
||||
text: 'Client Devices',
|
||||
onClick: onMenuItemClick,
|
||||
},
|
||||
{
|
||||
key: 'network-elements',
|
||||
icon: <ApiOutlined className={styles.MenuIcon} />,
|
||||
path: '/network/elements',
|
||||
text: 'Network Elements',
|
||||
onClick: onMenuItemClick,
|
||||
},
|
||||
{
|
||||
key: 'alarms',
|
||||
icon: <NotificationOutlined className={styles.MenuIcon} />,
|
||||
path: '/network/alarms',
|
||||
text: 'Alarms',
|
||||
onClick: onMenuItemClick,
|
||||
},
|
||||
{
|
||||
key: 'recommendations',
|
||||
icon: <CheckCircleOutlined className={styles.MenuIcon} />,
|
||||
path: '/recommendations',
|
||||
text: 'Recommendations',
|
||||
onClick: onMenuItemClick,
|
||||
},
|
||||
{
|
||||
key: 'settings',
|
||||
icon: <SettingOutlined className={styles.MenuIcon} />,
|
||||
path: '/settings',
|
||||
text: 'Settings',
|
||||
onClick: onMenuItemClick,
|
||||
},
|
||||
{
|
||||
key: ACCOUNTS,
|
||||
icon: <TeamOutlined className={styles.MenuIcon} />,
|
||||
text: 'Customers',
|
||||
path: '/accounts/customers',
|
||||
onClick: onMenuItemClick,
|
||||
},
|
||||
];
|
||||
|
||||
const commonMenuItems = [
|
||||
{
|
||||
key: 'logout',
|
||||
icon: <LogoutOutlined className={styles.MenuIcon} />,
|
||||
path: '/signout',
|
||||
text: 'Sign Out',
|
||||
onClick: onLogout,
|
||||
},
|
||||
];
|
||||
|
||||
const onOpenChange = keys => {
|
||||
const latestOpenKey = keys.find(key => !openKeys.includes(key));
|
||||
|
||||
if (rootSubmenuKeys.indexOf(latestOpenKey) === -1) {
|
||||
setOpenKeys(keys);
|
||||
} else {
|
||||
setOpenKeys(latestOpenKey ? [latestOpenKey] : []);
|
||||
}
|
||||
};
|
||||
|
||||
const getMenu = (config = { items: [] }, defaultSelectedKeys = []) => {
|
||||
const items = [];
|
||||
|
||||
let keys = [];
|
||||
let selectedKeys = [...defaultSelectedKeys];
|
||||
|
||||
config.items.forEach(item => {
|
||||
if (item && item.key) {
|
||||
if (item.children) {
|
||||
const subMenu = getMenu({ items: item.children, parent: item });
|
||||
|
||||
if (subMenu.selectedKeys && subMenu.selectedKeys.length) {
|
||||
selectedKeys = [...selectedKeys, ...subMenu.selectedKeys];
|
||||
}
|
||||
if (subMenu.openKeys && subMenu.openKeys.length) {
|
||||
keys = [...keys, ...subMenu.openKeys];
|
||||
}
|
||||
|
||||
items.push(
|
||||
<SubMenu
|
||||
key={item.key}
|
||||
title={
|
||||
<span>
|
||||
{item.icon}
|
||||
<span>{item.text}</span>
|
||||
</span>
|
||||
}
|
||||
>
|
||||
{subMenu.items}
|
||||
</SubMenu>
|
||||
);
|
||||
} else {
|
||||
const ItemComponent = item.Component || Item;
|
||||
|
||||
let LinkComponent = ({ ...restP }) => (
|
||||
<Link
|
||||
// preserveParams={this.getPreservedParams(item.path, locationState)}
|
||||
{...restP}
|
||||
/>
|
||||
);
|
||||
|
||||
if (item.LinkComponent) {
|
||||
LinkComponent = item.LinkComponent;
|
||||
}
|
||||
|
||||
const path = locationState.pathname;
|
||||
const pathAndHash = `${path}${locationState.hash}`; // for hash routing
|
||||
|
||||
if (path.startsWith(item.path) || pathAndHash.startsWith(item.path)) {
|
||||
if (config.parent) {
|
||||
keys.push(config.parent.key);
|
||||
}
|
||||
selectedKeys.push(item.key.toString());
|
||||
}
|
||||
|
||||
items.push(
|
||||
<ItemComponent key={item.key} className="ant-menu-item">
|
||||
<LinkComponent onClick={item.onClick} to={item.path}>
|
||||
{item.icon}
|
||||
<span>{item.text}</span>
|
||||
</LinkComponent>
|
||||
</ItemComponent>
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
items,
|
||||
selectedKeys,
|
||||
keys,
|
||||
};
|
||||
};
|
||||
|
||||
const menuConfig = {
|
||||
items: [...enocMenuItems, ...commonMenuItems],
|
||||
};
|
||||
|
||||
const menu = getMenu(menuConfig);
|
||||
|
||||
const sider = (
|
||||
<Sider
|
||||
collapsed={isMobile ? false : collapsed}
|
||||
width="234px"
|
||||
collapsedWidth="80px"
|
||||
breakpoint="lg"
|
||||
className={`${styles.Sider} ${collapsed ? styles.collapsed : ''} ${
|
||||
isMobile ? styles.Mobile : ''
|
||||
}`}
|
||||
>
|
||||
<div className={styles.TopArea}>
|
||||
<Link className={styles.LogoContainer} to="/">
|
||||
<img className={styles.Logo} alt="ConnectUs" src={collapsed ? logoMobile : logo} />
|
||||
</Link>
|
||||
</div>
|
||||
<Menu
|
||||
className="sidemenu"
|
||||
selectedKeys={menu.selectedKeys}
|
||||
defaultOpenKeys={menu.openKeys}
|
||||
onOpenChange={onOpenChange}
|
||||
mode="inline"
|
||||
theme="dark"
|
||||
>
|
||||
{menu.items}
|
||||
</Menu>
|
||||
</Sider>
|
||||
);
|
||||
|
||||
if (isMobile) {
|
||||
return (
|
||||
<Drawer
|
||||
zIndex={9999}
|
||||
placement="left"
|
||||
closable={false}
|
||||
visible={!collapsed}
|
||||
onClose={onMenuButtonClick}
|
||||
bodyStyle={{ padding: 0 }}
|
||||
width={256}
|
||||
>
|
||||
{sider}
|
||||
</Drawer>
|
||||
);
|
||||
}
|
||||
|
||||
return sider;
|
||||
};
|
||||
|
||||
SideMenu.propTypes = {
|
||||
locationState: PropTypes.instanceOf(Object).isRequired,
|
||||
collapsed: PropTypes.bool.isRequired,
|
||||
isMobile: PropTypes.bool.isRequired,
|
||||
logo: PropTypes.string.isRequired,
|
||||
logoMobile: PropTypes.string.isRequired,
|
||||
onMenuButtonClick: PropTypes.func.isRequired,
|
||||
onMenuItemClick: PropTypes.func.isRequired,
|
||||
onLogout: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export default SideMenu;
|
||||
5
app/containers/Dashboard/index.js
Normal file
5
app/containers/Dashboard/index.js
Normal file
@@ -0,0 +1,5 @@
|
||||
import React from 'react';
|
||||
|
||||
const Dashboard = () => <h1>Dashboard</h1>;
|
||||
|
||||
export default Dashboard;
|
||||
22
app/containers/MasterLayout/MasterLayout.module.scss
Normal file
22
app/containers/MasterLayout/MasterLayout.module.scss
Normal file
@@ -0,0 +1,22 @@
|
||||
@import 'styles/variables';
|
||||
|
||||
.MainLayout {
|
||||
height: 100vh;
|
||||
margin-left: $sidebar-width;
|
||||
overflow: auto;
|
||||
|
||||
&.collapsed {
|
||||
margin-left: $sidebar-collapsed-width;
|
||||
}
|
||||
&.mobile {
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.Content {
|
||||
margin-top: $header-height;
|
||||
}
|
||||
|
||||
.Footer {
|
||||
text-align: center;
|
||||
}
|
||||
103
app/containers/MasterLayout/index.js
Normal file
103
app/containers/MasterLayout/index.js
Normal file
@@ -0,0 +1,103 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Layout } from 'antd';
|
||||
|
||||
import GlobalHeader from 'components/GlobalHeader';
|
||||
import SideMenu from 'components/SideMenu';
|
||||
|
||||
import styles from './MasterLayout.module.scss';
|
||||
|
||||
const { Content, Footer } = Layout;
|
||||
|
||||
const MasterLayout = ({ children, logo, logoMobile, locationState, onLogout }) => {
|
||||
const [isCollapsed, setIsCollapsed] = useState(false);
|
||||
const [isMobile, setIsMobile] = useState(false);
|
||||
const [screenSize, setScreenSize] = useState('lg');
|
||||
|
||||
const currentYear = new Date().getFullYear();
|
||||
|
||||
const handleResize = () => {
|
||||
const width = window.innerWidth;
|
||||
|
||||
if (width < 768 && screenSize !== 'sm') {
|
||||
setIsCollapsed(true);
|
||||
setIsMobile(true);
|
||||
setScreenSize('sm');
|
||||
} else if (width >= 768 && width < 992 && screenSize !== 'md') {
|
||||
setIsCollapsed(true);
|
||||
setIsMobile(false);
|
||||
setScreenSize('md');
|
||||
} else if (width >= 992 && screenSize !== 'lg') {
|
||||
setIsCollapsed(false);
|
||||
setIsMobile(false);
|
||||
setScreenSize('lg');
|
||||
}
|
||||
};
|
||||
|
||||
const handleMenuToggle = () => {
|
||||
setIsCollapsed(!isCollapsed);
|
||||
};
|
||||
|
||||
const handleMenuItemClick = () => {
|
||||
if (isMobile === true) {
|
||||
setIsCollapsed(true);
|
||||
}
|
||||
};
|
||||
|
||||
const handleLogout = () => {
|
||||
onLogout();
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
handleResize();
|
||||
|
||||
window.addEventListener('resize', handleResize);
|
||||
return () => window.removeEventListener('resize', handleResize);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
window.addEventListener('resize', handleResize);
|
||||
return () => window.removeEventListener('resize', handleResize);
|
||||
}, [screenSize]);
|
||||
|
||||
return (
|
||||
<Layout>
|
||||
<SideMenu
|
||||
locationState={locationState}
|
||||
collapsed={isCollapsed}
|
||||
isMobile={isMobile}
|
||||
logo={logo}
|
||||
logoMobile={logoMobile}
|
||||
onMenuButtonClick={handleMenuToggle}
|
||||
onMenuItemClick={handleMenuItemClick}
|
||||
onLogout={handleLogout}
|
||||
/>
|
||||
<Layout
|
||||
className={`${styles.MainLayout} ${isCollapsed ? styles.collapsed : ''} ${
|
||||
isMobile ? styles.mobile : ''
|
||||
}`}
|
||||
>
|
||||
<GlobalHeader
|
||||
collapsed={isCollapsed}
|
||||
isMobile={isMobile}
|
||||
logoMobile={logoMobile}
|
||||
onMenuButtonClick={handleMenuToggle}
|
||||
/>
|
||||
<Content className={styles.Content}>{children}</Content>
|
||||
<Footer className={styles.Footer}>
|
||||
Copyright © {currentYear} ConnectUs Inc. All Rights Reserved.
|
||||
</Footer>
|
||||
</Layout>
|
||||
</Layout>
|
||||
);
|
||||
};
|
||||
|
||||
MasterLayout.propTypes = {
|
||||
children: PropTypes.node.isRequired,
|
||||
logo: PropTypes.string.isRequired,
|
||||
logoMobile: PropTypes.string.isRequired,
|
||||
onLogout: PropTypes.func.isRequired,
|
||||
locationState: PropTypes.instanceOf(Object).isRequired,
|
||||
};
|
||||
|
||||
export default MasterLayout;
|
||||
3
app/styles/index.scss
Normal file
3
app/styles/index.scss
Normal file
@@ -0,0 +1,3 @@
|
||||
body {
|
||||
font-family: 'Lato', 'Helvetica Neue', Helvetica, Arial, sans-serif;
|
||||
}
|
||||
4
app/styles/variables.scss
Normal file
4
app/styles/variables.scss
Normal file
@@ -0,0 +1,4 @@
|
||||
$sidebar-width: 234px;
|
||||
$sidebar-collapsed-width: 80px;
|
||||
|
||||
$header-height: 64px;
|
||||
31
jest.config.js
Executable file
31
jest.config.js
Executable file
@@ -0,0 +1,31 @@
|
||||
module.exports = {
|
||||
collectCoverageFrom: [
|
||||
'app/**/*.{js,jsx}',
|
||||
'!app/**/*.test.{js,jsx}',
|
||||
'!app/*/RbGenerated*/*.{js,jsx}',
|
||||
'!app/app.js',
|
||||
'!app/global-styles.js',
|
||||
'!app/*/*/Loadable.{js,jsx}',
|
||||
],
|
||||
coverageThreshold: {
|
||||
global: {
|
||||
statements: 98,
|
||||
branches: 91,
|
||||
functions: 98,
|
||||
lines: 98,
|
||||
},
|
||||
},
|
||||
moduleDirectories: ['node_modules', 'app'],
|
||||
moduleNameMapper: {
|
||||
'.*\\.(css|less|styl|scss|sass)$': '<rootDir>/internals/mocks/cssModule.js',
|
||||
'.*\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$':
|
||||
'<rootDir>/internals/mocks/image.js',
|
||||
},
|
||||
setupFilesAfterEnv: [
|
||||
'<rootDir>/internals/testing/test-bundler.js',
|
||||
'react-testing-library/cleanup-after-each',
|
||||
],
|
||||
setupFiles: ['raf/polyfill'],
|
||||
testRegex: 'tests/.*\\.test\\.js$',
|
||||
snapshotSerializers: [],
|
||||
};
|
||||
11
jsconfig.json
Normal file
11
jsconfig.json
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES6",
|
||||
"module": "commonjs",
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"baseUrl": "./",
|
||||
"paths": {
|
||||
"~/*": ["./app/*"]
|
||||
}
|
||||
}
|
||||
}
|
||||
13625
package-lock.json
generated
Normal file
13625
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
80
package.json
Normal file
80
package.json
Normal file
@@ -0,0 +1,80 @@
|
||||
{
|
||||
"name": "cu-ui",
|
||||
"version": "0.1.0",
|
||||
"author": "ConnectUs",
|
||||
"description": "",
|
||||
"engines": {
|
||||
"npm": ">=5",
|
||||
"node": ">=8"
|
||||
},
|
||||
"scripts": {
|
||||
"format": "prettier --write \"app/**/*.js\"",
|
||||
"eslint-fix": "eslint --fix \"app/**/*.js\"",
|
||||
"build": "webpack --mode production"
|
||||
},
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@ant-design/icons": "^4.0.2",
|
||||
"antd": "^4.0.2",
|
||||
"clean-webpack-plugin": "^3.0.0",
|
||||
"connected-react-router": "^6.7.0",
|
||||
"history": "^4.10.1",
|
||||
"prop-types": "^15.7.2",
|
||||
"react": "^16.13.0",
|
||||
"react-dom": "^16.13.0",
|
||||
"react-router-dom": "^5.1.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.8.7",
|
||||
"@babel/plugin-proposal-class-properties": "^7.8.3",
|
||||
"@babel/preset-env": "^7.8.7",
|
||||
"@babel/preset-react": "^7.8.3",
|
||||
"babel-core": "^6.26.3",
|
||||
"babel-eslint": "^10.1.0",
|
||||
"babel-loader": "^8.0.6",
|
||||
"babel-plugin-root-import": "^6.4.1",
|
||||
"babel-polyfill": "^6.26.0",
|
||||
"css-loader": "^3.4.2",
|
||||
"eslint": "^6.8.0",
|
||||
"eslint-config-airbnb": "^18.0.1",
|
||||
"eslint-config-prettier": "^6.10.0",
|
||||
"eslint-config-react": "^1.1.7",
|
||||
"eslint-loader": "^3.0.3",
|
||||
"eslint-plugin-import": "^2.20.1",
|
||||
"eslint-plugin-jsx-a11y": "^6.2.3",
|
||||
"eslint-plugin-prettier": "^3.1.2",
|
||||
"eslint-plugin-react": "^7.19.0",
|
||||
"eslint-plugin-react-hooks": "^2.5.0",
|
||||
"husky": "^4.2.3",
|
||||
"less": "^3.11.1",
|
||||
"less-loader": "^5.0.0",
|
||||
"lint-staged": "^10.0.8",
|
||||
"node-sass": "^4.13.1",
|
||||
"prettier": "^1.19.1",
|
||||
"pretty-quick": "^2.0.1",
|
||||
"sass-loader": "^8.0.2",
|
||||
"style-loader": "^1.1.3",
|
||||
"uglifyjs-webpack-plugin": "^2.2.0",
|
||||
"webpack": "^4.42.0",
|
||||
"webpack-cli": "^3.3.11",
|
||||
"webpack-dev-server": "^3.10.3"
|
||||
},
|
||||
"precommit": "NODE_ENV=production lint-staged",
|
||||
"browserslist": [
|
||||
"last 2 versions",
|
||||
"> 1%",
|
||||
"IE 10"
|
||||
],
|
||||
"lint-staged": {
|
||||
"*.{js,jsx}": [
|
||||
"pretty-quick --staged",
|
||||
"eslint app/ --fix",
|
||||
"git add"
|
||||
]
|
||||
},
|
||||
"husky": {
|
||||
"hooks": {
|
||||
"pre-commit": "lint-staged"
|
||||
}
|
||||
}
|
||||
}
|
||||
80
webpack.config.js
Normal file
80
webpack.config.js
Normal file
@@ -0,0 +1,80 @@
|
||||
const path = require('path');
|
||||
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
|
||||
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
|
||||
|
||||
module.exports = {
|
||||
mode: process.env.NODE_ENV,
|
||||
entry: ['babel-polyfill', './app/index.js'],
|
||||
output: {
|
||||
path: path.join(__dirname, 'dist'),
|
||||
publicPath: '/',
|
||||
filename: 'bundle.js',
|
||||
},
|
||||
devtool: 'inline-source-map',
|
||||
devServer: {
|
||||
port: 3000,
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.(js|jsx)$/,
|
||||
exclude: /node_modules/,
|
||||
use: ['babel-loader', 'eslint-loader'],
|
||||
},
|
||||
{
|
||||
// Preprocess 3rd party .css files located in node_modules
|
||||
test: /\.css$/,
|
||||
include: /node_modules/,
|
||||
use: ['style-loader', 'css-loader'],
|
||||
},
|
||||
{
|
||||
test: /\.less$/,
|
||||
use: [
|
||||
{
|
||||
loader: 'style-loader',
|
||||
},
|
||||
{
|
||||
loader: 'css-loader',
|
||||
},
|
||||
{
|
||||
loader: 'less-loader',
|
||||
options: {
|
||||
javascriptEnabled: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
test: /\.(css|scss)$/,
|
||||
exclude: /node_modules/,
|
||||
use: [
|
||||
{
|
||||
loader: 'style-loader',
|
||||
},
|
||||
{
|
||||
loader: 'css-loader',
|
||||
options: {
|
||||
modules: {
|
||||
localIdentName: '[name]__[local]___[hash:base64:5]',
|
||||
},
|
||||
sourceMap: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
loader: 'sass-loader',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
optimization: {
|
||||
minimizer: [new UglifyJsPlugin()],
|
||||
},
|
||||
resolve: {
|
||||
modules: ['node_modules', path.resolve(`${__dirname}/app`)],
|
||||
alias: {
|
||||
app: path.resolve(`${__dirname}/app`),
|
||||
},
|
||||
},
|
||||
plugins: [new CleanWebpackPlugin()],
|
||||
};
|
||||
Reference in New Issue
Block a user