Add layout
This commit is contained in:
15
src/layout/LayoutBreadcrumb.jsx
Normal file
15
src/layout/LayoutBreadcrumb.jsx
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import React, { createContext, useContext, useState } from 'react';
|
||||||
|
|
||||||
|
const BreadcrumbContext = createContext();
|
||||||
|
|
||||||
|
export const BreadcrumbProvider = ({ children }) => {
|
||||||
|
const [breadcrumbItems, setBreadcrumbItems] = useState([]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<BreadcrumbContext.Provider value={{ breadcrumbItems, setBreadcrumbItems }}>
|
||||||
|
{children}
|
||||||
|
</BreadcrumbContext.Provider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useBreadcrumb = () => useContext(BreadcrumbContext);
|
||||||
18
src/layout/LayoutFooter.jsx
Normal file
18
src/layout/LayoutFooter.jsx
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import { Layout } from 'antd'
|
||||||
|
import Link from 'antd/es/typography/Link';
|
||||||
|
|
||||||
|
const { Footer } = Layout;
|
||||||
|
const LayoutFooter = () => {
|
||||||
|
return (
|
||||||
|
<Footer
|
||||||
|
style={{
|
||||||
|
textAlign: 'center',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{/* SYPIU GGCP */}
|
||||||
|
</Footer>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default LayoutFooter
|
||||||
119
src/layout/LayoutHeader.jsx
Normal file
119
src/layout/LayoutHeader.jsx
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { Layout, theme, Space, Typography, Breadcrumb, Button } from 'antd';
|
||||||
|
import { UserOutlined } from '@ant-design/icons';
|
||||||
|
import handleLogOut from '../Utils/Auth/Logout';
|
||||||
|
import { useBreadcrumb } from './LayoutBreadcrumb';
|
||||||
|
import { decryptData } from '../components/Global/Formatter';
|
||||||
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
|
||||||
|
const { Link, Text } = Typography;
|
||||||
|
const { Header } = Layout;
|
||||||
|
|
||||||
|
const LayoutHeader = () => {
|
||||||
|
const { breadcrumbItems } = useBreadcrumb();
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const {
|
||||||
|
token: { colorBgContainer, colorBorder, colorText },
|
||||||
|
} = theme.useToken();
|
||||||
|
|
||||||
|
// Ambil data user dari localStorage dan dekripsi
|
||||||
|
const sessionData = localStorage.getItem('session');
|
||||||
|
const userData = sessionData ? decryptData(sessionData) : null;
|
||||||
|
// console.log(userData);
|
||||||
|
|
||||||
|
const roleName = userData?.user?.approval || userData?.user?.partner_name || 'Guest';
|
||||||
|
|
||||||
|
const userName = userData?.user?.name || userData?.user?.username || 'User';
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Header
|
||||||
|
style={{
|
||||||
|
background: colorBgContainer,
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
alignItems: 'center',
|
||||||
|
flexWrap: 'wrap',
|
||||||
|
rowGap: 10,
|
||||||
|
paddingTop:15,
|
||||||
|
paddingBottom: 20,
|
||||||
|
paddingLeft: 24,
|
||||||
|
paddingRight: 24,
|
||||||
|
minHeight: 100,
|
||||||
|
boxSizing: 'border-box',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
flexWrap: 'wrap',
|
||||||
|
gap: 12,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Text
|
||||||
|
style={{
|
||||||
|
color: colorText,
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: 'bold',
|
||||||
|
whiteSpace: 'nowrap',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Login AS {roleName}
|
||||||
|
</Text>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
flexWrap: 'wrap',
|
||||||
|
gap: 10,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
background: '#f5f5f5',
|
||||||
|
border: `1px solid ${colorBorder}`,
|
||||||
|
borderRadius: 6,
|
||||||
|
padding: '4px 12px',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<UserOutlined style={{ fontSize: 16, color: colorText }} />
|
||||||
|
<Text style={{ marginLeft: 8, color: colorText }} strong>
|
||||||
|
{userName}
|
||||||
|
</Text>
|
||||||
|
</Button>
|
||||||
|
<Link
|
||||||
|
onClick={() => {
|
||||||
|
handleLogOut();
|
||||||
|
navigate('/signin');
|
||||||
|
}}
|
||||||
|
aria-label="Log out from the application"
|
||||||
|
style={{
|
||||||
|
color: colorText,
|
||||||
|
whiteSpace: 'nowrap',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Logout
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</Header>
|
||||||
|
|
||||||
|
<div style={{ width: '100%', maxWidth: '50%', textAlign: 'left' }}>
|
||||||
|
<Breadcrumb
|
||||||
|
style={{
|
||||||
|
marginLeft: '20px',
|
||||||
|
marginTop: '20px',
|
||||||
|
marginBottom: '10px',
|
||||||
|
}}
|
||||||
|
items={breadcrumbItems || []}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default LayoutHeader;
|
||||||
69
src/layout/LayoutLogo.jsx
Normal file
69
src/layout/LayoutLogo.jsx
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
import { Image } from 'antd';
|
||||||
|
import logoPiu from '../assets/freepik/LOGOPIU.png';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
const LayoutLogo = () => {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
margin: '1rem auto',
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
backgroundColor: '#001529',
|
||||||
|
padding: '1rem',
|
||||||
|
borderRadius: '1rem',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
background: 'radial-gradient(circle at center, rgba(255,255,255,0.95), rgba(220,220,220,0.5))',
|
||||||
|
borderRadius: '50%',
|
||||||
|
width: 160,
|
||||||
|
height: 160,
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
boxShadow: '0 6px 20px rgba(0, 0, 0, 0.2)',
|
||||||
|
position: 'relative',
|
||||||
|
overflow: 'hidden'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{/* Ring sebelum logo utama */}
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
border: '5px solid #ffffffff',
|
||||||
|
borderRadius: '50%',
|
||||||
|
width: 160,
|
||||||
|
height: 160,
|
||||||
|
position: 'absolute',
|
||||||
|
zIndex: 1
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
borderRadius: '10px',
|
||||||
|
padding: '4px',
|
||||||
|
mixBlendMode: 'normal',
|
||||||
|
opacity: 1,
|
||||||
|
zIndex: 2,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Image
|
||||||
|
src={logoPiu}
|
||||||
|
alt="logo"
|
||||||
|
width={140}
|
||||||
|
height={100}
|
||||||
|
preview={false}
|
||||||
|
style={{
|
||||||
|
filter: 'drop-shadow(0 0 3px rgba(0, 0, 0, 0.2))',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default LayoutLogo;
|
||||||
110
src/layout/LayoutMenu.jsx
Normal file
110
src/layout/LayoutMenu.jsx
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
import React, { useState } from 'react';
|
||||||
|
import { Link } from 'react-router-dom';
|
||||||
|
import { Menu, Typography, Image } from 'antd';
|
||||||
|
import { getSessionData } from '../components/Global/Formatter';
|
||||||
|
import { HomeOutlined,
|
||||||
|
DatabaseOutlined,
|
||||||
|
SettingOutlined,
|
||||||
|
UserOutlined,
|
||||||
|
AntDesignOutlined,
|
||||||
|
ShoppingCartOutlined,
|
||||||
|
ShoppingOutlined,
|
||||||
|
HistoryOutlined,
|
||||||
|
DollarOutlined,
|
||||||
|
RollbackOutlined,
|
||||||
|
ProductOutlined
|
||||||
|
} from '@ant-design/icons';
|
||||||
|
|
||||||
|
const { Text } = Typography;
|
||||||
|
|
||||||
|
const allItems = [
|
||||||
|
{
|
||||||
|
key: 'home',
|
||||||
|
icon: <HomeOutlined style={{fontSize:'19px'}} />,
|
||||||
|
label: <Link to="/dashboard/home" className='fontMenus'>Home</Link>,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'master',
|
||||||
|
icon: <DatabaseOutlined style={{fontSize:'19px'}} />,
|
||||||
|
label: 'Master',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
key: 'master-device',
|
||||||
|
icon: <DatabaseOutlined style={{fontSize:'19px'}} />,
|
||||||
|
label: <Link to="/master/device">Device</Link>,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const LayoutMenu = () => {
|
||||||
|
const [stateOpenKeys, setStateOpenKeys] = useState(['home']);
|
||||||
|
|
||||||
|
const getLevelKeys = items1 => {
|
||||||
|
const key = {};
|
||||||
|
const func = (items2, level = 1) => {
|
||||||
|
items2.forEach(item => {
|
||||||
|
if (item.key) {
|
||||||
|
key[item.key] = level;
|
||||||
|
}
|
||||||
|
if (item.children) {
|
||||||
|
func(item.children, level + 1);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
func(items1);
|
||||||
|
return key;
|
||||||
|
};
|
||||||
|
|
||||||
|
const levelKeys = getLevelKeys(allItems);
|
||||||
|
|
||||||
|
const onOpenChange = openKeys => {
|
||||||
|
const currentOpenKey = openKeys.find(key => stateOpenKeys.indexOf(key) === -1);
|
||||||
|
if (currentOpenKey !== undefined) {
|
||||||
|
const repeatIndex = openKeys.filter(key => key !== currentOpenKey).findIndex(key => levelKeys[key] === levelKeys[currentOpenKey]);
|
||||||
|
setStateOpenKeys(
|
||||||
|
openKeys.filter((_, index) => index !== repeatIndex).filter(key => levelKeys[key] <= levelKeys[currentOpenKey]),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
setStateOpenKeys(openKeys);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const session = getSessionData();
|
||||||
|
const isAdmin = session?.user?.user_id;
|
||||||
|
|
||||||
|
const karyawan = ()=>{
|
||||||
|
return allItems.filter(
|
||||||
|
item => item.key !== 'setting'
|
||||||
|
// tambahkan menu jika terdapat menu yang di sembunyikan dari user karyawan
|
||||||
|
// && item.key !== 'master'
|
||||||
|
// && item.key !== 'master'
|
||||||
|
).map(item=>{
|
||||||
|
if(item.key === 'master'){
|
||||||
|
return{
|
||||||
|
...item,
|
||||||
|
// buka command dibawah jika terdapat sub menu yang di sembunyikan
|
||||||
|
// children: item.children.filter(
|
||||||
|
// child => child.key !== 'master-product'
|
||||||
|
// tambahkan menu jika terdapat menu yang di sembunyikan dari user karyawan
|
||||||
|
// && child.key !== 'master-service'
|
||||||
|
// )
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return item;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
const items = isAdmin === 1 ? allItems : karyawan();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Menu
|
||||||
|
theme="dark"
|
||||||
|
mode="inline"
|
||||||
|
items={items}
|
||||||
|
defaultSelectedKeys={['home']}
|
||||||
|
openKeys={stateOpenKeys}
|
||||||
|
onOpenChange={onOpenChange}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
export default LayoutMenu;
|
||||||
25
src/layout/LayoutSidebar.jsx
Normal file
25
src/layout/LayoutSidebar.jsx
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import { Layout } from 'antd';
|
||||||
|
import LayoutLogo from './LayoutLogo';
|
||||||
|
import LayoutMenu from './LayoutMenu';
|
||||||
|
|
||||||
|
const { Sider } = Layout;
|
||||||
|
const LayoutSidebar = () => {
|
||||||
|
return (
|
||||||
|
<Sider width={300}
|
||||||
|
breakpoint="lg"
|
||||||
|
collapsedWidth="0"
|
||||||
|
onBreakpoint={(broken) => {
|
||||||
|
// console.log(broken);
|
||||||
|
}}
|
||||||
|
onCollapse={(collapsed, type) => {
|
||||||
|
// console.log(collapsed, type);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<LayoutLogo />
|
||||||
|
<LayoutMenu />
|
||||||
|
</Sider>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default LayoutSidebar
|
||||||
46
src/layout/MainLayout.jsx
Normal file
46
src/layout/MainLayout.jsx
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { Layout, theme } from 'antd';
|
||||||
|
import LayoutFooter from './LayoutFooter';
|
||||||
|
import LayoutHeader from './LayoutHeader';
|
||||||
|
import LayoutSidebar from './LayoutSidebar';
|
||||||
|
|
||||||
|
|
||||||
|
const { Content } = Layout;
|
||||||
|
|
||||||
|
const MainLayout = ({ children }) => {
|
||||||
|
const {
|
||||||
|
token: { colorBgContainer, borderRadiusLG },
|
||||||
|
} = theme.useToken();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Layout style={{ height: '100vh' }}>
|
||||||
|
<LayoutSidebar />
|
||||||
|
<Layout
|
||||||
|
style={{
|
||||||
|
overflow: 'auto',
|
||||||
|
|
||||||
|
}}>
|
||||||
|
<LayoutHeader />
|
||||||
|
<Content
|
||||||
|
style={{
|
||||||
|
margin: '24px 16px 0',
|
||||||
|
flex: '1 0 auto',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{/* <div
|
||||||
|
style={{
|
||||||
|
padding: 24,
|
||||||
|
minHeight: '100%',
|
||||||
|
background: colorBgContainer,
|
||||||
|
borderRadius: borderRadiusLG,
|
||||||
|
}}
|
||||||
|
> */}
|
||||||
|
{children}
|
||||||
|
{/* </div> */}
|
||||||
|
</Content>
|
||||||
|
{/* <LayoutFooter /> */}
|
||||||
|
</Layout>
|
||||||
|
</Layout>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
export default MainLayout;
|
||||||
Reference in New Issue
Block a user