메뉴 앤트디자인 메뉴로 교체

헤더 Breadcrumb 추가, profile 수정
This commit is contained in:
2025-07-07 14:25:02 +09:00
parent 0d8fb7b327
commit c4099c0cf0
34 changed files with 359 additions and 408 deletions

View File

@@ -12,5 +12,6 @@ export const STORAGE_MAIL_COPY = 'copyMailData';
export const STORAGE_BUSINESS_LOG_SEARCH = 'businessLogSearchParam';
export const STORAGE_GAME_LOG_CURRENCY_SEARCH = 'gameLogCurrencySearchParam';
export const LOG_ACTION_FAIL_CALIUM_ECHO = 'FailCaliumEchoSystem';
export const BATTLE_EVENT_OPERATION_TIME_WAIT_SECONDS = 300;
export { INITIAL_PAGE_SIZE, INITIAL_CURRENT_PAGE, INITIAL_PAGE_LIMIT };

View File

@@ -103,14 +103,14 @@ export const menuConfig = {
view: false,
authLevel: adminAuthLevel.NONE
},
cryptview: {
title: '크립토 조회',
permissions: {
read: authType.cryptoRead
},
view: false,
authLevel: adminAuthLevel.NONE
},
// cryptview: {
// title: '크립토 조회',
// permissions: {
// read: authType.cryptoRead
// },
// view: false,
// authLevel: adminAuthLevel.NONE
// },
businesslogview: {
title: '비즈니스 로그 조회',
permissions: {

View File

@@ -1,5 +1,5 @@
import { NavLink, useNavigate } from 'react-router-dom';
import arrowIcon from '../../../assets/img/icon/icon-tab.png';
import { NavLink, useNavigate, useLocation } from 'react-router-dom';
import { ConfigProvider, Menu, theme } from 'antd';
import styled from 'styled-components';
import { useRecoilValue } from 'recoil';
import { authList } from '../../../store/authList';
@@ -7,7 +7,6 @@ import Modal from '../modal/Modal';
import { BtnWrapper, ButtonClose, ModalText } from '../../../styles/Components';
import { useEffect, useState } from 'react';
import Button from '../button/Button';
import { useLocation } from 'react-router-dom';
import { AuthInfo } from '../../../apis';
import { getMenuConfig } from '../../../utils';
import { adminAuthLevel } from '../../../assets/data/types';
@@ -15,47 +14,55 @@ import { adminAuthLevel } from '../../../assets/data/types';
const Navi = () => {
const token = sessionStorage.getItem('token');
const userInfo = useRecoilValue(authList);
const menu = getMenuConfig(userInfo);
const [modalClose, setModalClose] = useState('hidden');
const [logoutModalClose, setLogoutModalClose] = useState('hidden');
const menuConfig = getMenuConfig(userInfo);
const location = useLocation();
const navigate = useNavigate();
const [modalClose, setModalClose] = useState('hidden');
const [logoutModalClose, setLogoutModalClose] = useState('hidden');
const [openKeys, setOpenKeys] = useState([]);
const [selectedKeys, setSelectedKeys] = useState([]);
// 현재 경로에 따라 선택된 메뉴와 열린 서브메뉴 설정
useEffect(() => {
const path = location.pathname.split('/');
if (path.length > 1) {
// 첫 번째 경로(예: /usermanage)를 기반으로 openKeys 설정
setOpenKeys([path[1]]);
// 전체 경로(예: /usermanage/adminview)를 기반으로 selectedKeys 설정
if (path.length > 2) {
setSelectedKeys([`${path[1]}/${path[2]}`]);
} else {
setSelectedKeys([path[1]]);
}
}
}, [location.pathname]);
const handleToken = async () => {
const tokenStatus = await AuthInfo(token);
tokenStatus.message === '잘못된 타입의 토큰입니다.' && setLogoutModalClose('view');
if (tokenStatus.message === '잘못된 타입의 토큰입니다.') {
setLogoutModalClose('view');
}
};
useEffect(() => {
handleToken();
}, [token]);
const handleTopMenu = e => {
e.preventDefault();
e.target.classList.toggle('active');
// 메뉴 아이템 클릭 핸들러
const handleMenuClick = ({ key }) => {
handleToken();
};
const handleLink = e => {
let topActive = document.querySelectorAll('nav .active');
let currentTopMenu = e.target.closest('ul').previousSibling;
for (let i = 0; i < topActive.length; i++) {
if (topActive[i] !== currentTopMenu) {
topActive[i].classList.remove('active');
}
}
handleToken();
// 서브메뉴 열기/닫기 핸들러
const handleOpenChange = (keys) => {
setOpenKeys(keys);
};
// 등록 완료 모달
const handleModalClose = () => {
if (modalClose === 'hidden') {
setModalClose('view');
} else {
setModalClose('hidden');
}
setModalClose(modalClose === 'hidden' ? 'view' : 'hidden');
};
// 로그아웃 안내 모달
@@ -65,7 +72,6 @@ const Navi = () => {
} else {
setLogoutModalClose('hidden');
sessionStorage.removeItem('token');
navigate('/');
}
};
@@ -79,41 +85,56 @@ const Navi = () => {
default:
return submenu.authLevel === adminAuthLevel.NONE && userInfo.auth_list && userInfo.auth_list.some(auth => auth.id === submenu.id);
}
}
};
const getMenuItems = () => {
return menuConfig
.filter(item => item.access)
.map(item => ({
key: item.link.substring(1),
label: item.title,
children: item.submenu.map(submenu => ({
key: `${item.link.substring(1)}/${submenu.link.split('/').pop()}`,
label: (
<MenuItemLink
to={isClickable(submenu) ? submenu.link : location.pathname}
$isclickable={isClickable(submenu) ? 'true' : 'false'}
onClick={(e) => {
if (!isClickable(submenu)) {
e.preventDefault();
handleModalClose();
}
}}
>
{submenu.title}
</MenuItemLink>
),
disabled: !isClickable(submenu)
}))
}));
};
return (
<>
<nav>
<ul>
{menu.map((item, idx) => {
return (
<li key={idx}>
{item.access && (
<TopMenu to={item.link} onClick={handleTopMenu}>
{item.title}
</TopMenu>
)}
<SubMenu>
{item.submenu && userInfo &&
item.submenu.map((submenu, idx) => {
return (
<SubMenuItem key={idx} $isclickable={isClickable(submenu) ? 'true' : 'false'}>
<NavLink
to={isClickable(submenu) ? submenu.link : location.pathname}
onClick={e => {
isClickable(submenu) ? handleLink(e) : handleModalClose();
}}>
{submenu.title}
</NavLink>
</SubMenuItem>
);
})}
</SubMenu>
</li>
);
})}
</ul>
</nav>
<StyledNavWrapper>
<ConfigProvider
theme={{
algorithm: theme.darkAlgorithm,
}}
>
<StyledMenu
theme="dark"
mode="inline"
openKeys={openKeys}
selectedKeys={selectedKeys}
onOpenChange={handleOpenChange}
onClick={handleMenuClick}
items={getMenuItems()}
multiple={true}
/>
</ConfigProvider>
</StyledNavWrapper>
{/* 접근 불가 모달 */}
<Modal min="440px" $padding="40px" $bgcolor="transparent" $view={modalClose}>
<BtnWrapper $justify="flex-end">
@@ -145,61 +166,15 @@ const Navi = () => {
export default Navi;
const TopMenu = styled(NavLink)`
padding: 16px 30px;
width: 100%;
text-align: left;
border-bottom: 1px solid #888;
position: relative;
color: #fff;
const StyledNavWrapper = styled.div`
`;
&:before {
content: '';
display: block;
width: 12px;
height: 12px;
position: absolute;
right: 30px;
top: 50%;
transform: translate(0, -50%);
background: url('${arrowIcon}') -12px 0 no-repeat;
}
&:hover,
const StyledMenu = styled(Menu)`
`;
const MenuItemLink = styled(NavLink)`
&.active {
background: #444;
font-weight: ${props => (props.$isclickable === 'false' ? 400 : 600)};
}
&.active ~ ul {
display: block;
}
&.active:before {
background: url('${arrowIcon}') 0 0 no-repeat;
}
`;
const SubMenu = styled.ul`
display: none;
`;
const SubMenuItem = styled.li`
background: #eee;
border-bottom: 1px solid #ccc;
color: #2c2c2c;
a {
width: 100%;
padding: 16px 30px;
color: ${props => (props.$isclickable === 'false' ? '#818181' : '#2c2c2c')};
text-align: left;
&:hover,
&.active {
color: ${props => (props.$isclickable === 'false' ? '#818181' : '#2c2c2c')};
font-weight: ${props => (props.$isclickable === 'false' ? 400 : 600)};
}
}
`;
const BackGround = styled.div`
background: #eee2;
width: 100%;
height: 100%;
z-index: 100;
`;
`;

View File

@@ -1,20 +1,24 @@
import { useState, useEffect } from 'react';
import UserIcon from '../../../assets/img/icon/icon-profile.png';
import { Layout, Avatar, Button as AntButton, Typography, Tooltip, Breadcrumb } from 'antd';
import { UserOutlined, LogoutOutlined, HomeOutlined } from '@ant-design/icons'
import styled from 'styled-components';
import Modal from '../modal/Modal';
import CloseIcon from '../../../assets/img/icon/icon-close.png';
import Button from '../../common/button/Button';
import { useRecoilState } from 'recoil';
import { Link, useNavigate } from 'react-router-dom';
import { Link, useNavigate, useLocation } from 'react-router-dom';
import { AuthLogout, AuthInfo } from '../../../apis';
import { BtnWrapper, ModalText } from '../../../styles/Components';
import { authList } from '../../../store/authList';
import { alertTypes } from '../../../assets/data/types';
import { useAlert } from '../../../context/AlertProvider';
import { menuConfig } from '../../../assets/data/menuConfig';
const { Header } = Layout;
const { Text } = Typography;
const Profile = () => {
const location = useLocation();
const { showModal } = useAlert();
const [infoData, setInfoData] = useRecoilState(authList);
const [errorModal, setErrorModal] = useState('hidden');
const navigate = useNavigate();
@@ -40,91 +44,124 @@ const Profile = () => {
// 필수값 입력 모달창
const handleErrorModal = () => {
if (errorModal === 'hidden') {
setErrorModal('view');
} else {
setErrorModal('hidden');
}
showModal('USER_LOGOUT_CONFIRM', {
type: alertTypes.confirm,
onConfirm: () => handleLogout()
});
};
// 카테고리별 첫 번째 아이템 링크 찾기
const getFirstItemLink = (categoryKey) => {
const category = menuConfig[categoryKey];
if (!category || !category.items) return `/${categoryKey}`;
// 첫 번째 visible 아이템 찾기
const firstVisibleItem = Object.entries(category.items)
.find(([_, item]) => item.view !== false);
if (!firstVisibleItem) return `/${categoryKey}`;
return `/${categoryKey}/${firstVisibleItem[0]}`;
};
const pathSnippets = location.pathname.split('/').filter(i => i);
const breadcrumbItems = [
{
title: <Link to="/"><HomeOutlined /></Link>,
}
];
if (pathSnippets.length > 0) {
// 첫 번째 경로 (메인 카테고리)
const mainCategory = pathSnippets[0];
if (menuConfig[mainCategory]) {
const firstItemLink = getFirstItemLink(mainCategory);
breadcrumbItems.push({
title: <Link to={firstItemLink}>{menuConfig[mainCategory].title}</Link>
});
// 두 번째 경로 (서브 카테고리)
if (pathSnippets.length > 1) {
const subCategory = pathSnippets[1];
if (menuConfig[mainCategory].items[subCategory]) {
breadcrumbItems.push({
title: menuConfig[mainCategory].items[subCategory].title
});
}
}
}
}
return (
<>
<ProfileWrapper>
<UserWrapper>{infoData.name && <Username>{infoData.name.length > 20 ? infoData.name.slice(0, 20) + '...' : infoData.name}</Username>}</UserWrapper>
<Link>
<LogoutBtn onClick={handleErrorModal}>로그아웃</LogoutBtn>
</Link>
</ProfileWrapper>
{/* 로그아웃 확인 모달 */}
<Modal min="440px" $padding="40px" $bgcolor="transparent" $view={errorModal}>
<BtnWrapper $justify="flex-end">
<ButtonClose onClick={handleErrorModal} />
</BtnWrapper>
<ModalText $align="center">
로그아웃 하시겠습니까?
<br />
(로그아웃 저장되지 않은 값은 초기화 됩니다.)
</ModalText>
<BtnWrapper $gap="10px">
<Button text="취소" theme="line" size="large" width="100%" handleClick={handleErrorModal} />
<Button text="확인" theme="primary" type="submit" size="large" width="100%" handleClick={handleLogout} />
</BtnWrapper>
</Modal>
<StyledHeader>
<StyledBreadcrumb items={breadcrumbItems} />
<ProfileContainer>
<StyledAvatar
size={32}
icon={<UserOutlined />}
/>
{infoData.name &&
<StyledUsername>
{infoData.name.length > 20 ? infoData.name.slice(0, 20) + '...' : infoData.name}
</StyledUsername>
}
<Tooltip title="로그아웃">
<StyledLogoutButton
type="text"
icon={<LogoutOutlined />}
onClick={handleErrorModal}
/>
</Tooltip>
</ProfileContainer>
</StyledHeader>
</>
);
};
export default Profile;
const ProfileWrapper = styled.div`
background: #f6f6f6;
padding: 20px;
const StyledHeader = styled(Header)`
background: #f6f6f6;
padding: 0 20px;
display: flex;
justify-content: space-between;
align-items: center;
height: 64px;
`;
const StyledBreadcrumb = styled(Breadcrumb)`
font-size: 15px;
font-weight: 600;
`;
const ProfileContainer = styled.div`
display: flex;
flex-wrap: wrap;
gap: 30px;
word-break: break-all;
justify-content: flex-end;
align-items: center;
gap: 12px;
`;
const LogoutBtn = styled.button`
color: #2c2c2c;
line-height: 1;
border-bottom: 0.5px solid #2c2c2c;
font-size: 13px;
font-weight: 300;
border-radius: 0;
letter-spacing: 0;
width: max-content;
height: max-content;
const StyledAvatar = styled(Avatar)`
background-color: #1677ff;
`;
const UserWrapper = styled.div`
padding-left: 35px;
position: relative;
font-size: 18px;
display: flex;
&:before {
background: url('${UserIcon}') 50% 50% no-repeat;
width: 24px;
height: 24px;
content: '';
display: block;
position: absolute;
left: 0;
top: 50%;
transform: translate(0, -50%);
}
const StyledUsername = styled(Text)`
font-weight: 600;
font-size: 18px;
color: rgba(0, 0, 0, 0.85);
`;
const Username = styled.div`
font-weight: 700;
padding-right: 3px;
`;
const StyledLogoutButton = styled(AntButton)`
color: rgba(0, 0, 0, 0.45);
transition: color 0.3s;
font-size: 18px;
&:hover {
color: #1677ff;
background: transparent;
}
const ButtonClose = styled.button`
width: 16px;
height: 16px;
background: url(${CloseIcon}) 50% 50% no-repeat;
`;

View File

@@ -0,0 +1,41 @@
import React from 'react';
import { motion } from 'framer-motion';
const pageVariants = {
initial: {
opacity: 0,
x: 20
},
animate: {
opacity: 1,
x: 0,
transition: {
duration: 0.3,
ease: "easeInOut"
}
},
exit: {
opacity: 0,
x: -20,
transition: {
duration: 0.2,
ease: "easeInOut"
}
}
};
const AnimatedPageWrapper = ({ children }) => {
return (
<motion.div
initial="initial"
animate="animate"
exit="exit"
variants={pageVariants}
style={{ width: '100%', height: '100%' }}
>
{children}
</motion.div>
);
};
export default AnimatedPageWrapper;

View File

@@ -1,5 +1,8 @@
import Layout from './Layout';
import LoginLayout from './LoginLayout';
import MainLayout from './MainLayout';
import AnimatedPageWrapper from './AnimatedPageWrapper';
import DetailGrid from './DetailGrid';
import DetailLayout from './DetailLayout';
export { Layout, LoginLayout, MainLayout };
export { Layout, LoginLayout, MainLayout, AnimatedPageWrapper, DetailGrid, DetailLayout };

View File

@@ -5,6 +5,7 @@ const resources = {
ko: {
translation: {
//메시지
USER_LOGOUT_CONFIRM: '로그아웃 하시겠습니까?\n(로그아웃 시 저장되지 않은 값은 초기화 됩니다.)',
NULL_MSG: '필수값을 입력해주세요.',
DATE_KTC: '* UTC+9 한국시간 기준으로 설정 (UTC+0 자동 반영처리)',
NOT_ITEM: '존재하지 않는 아이템코드입니다.',

View File

@@ -1,5 +1,6 @@
import React, { Fragment, useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { AnimatedPageWrapper } from '../../components/common/Layout'
import {
Title,
TableStyle,
@@ -174,7 +175,7 @@ const BusinessLogView = () => {
// }
return (
<>
<AnimatedPageWrapper>
<Title>비즈니스 로그 조회</Title>
<FormWrapper>
<BusinessLogSearchBar
@@ -271,7 +272,7 @@ const BusinessLogView = () => {
<TopButton />
</>
}
</>
</AnimatedPageWrapper>
);
};

View File

@@ -1,128 +0,0 @@
import { styled } from 'styled-components';
import { Link } from 'react-router-dom';
import { Fragment, useState } from 'react';
import { Title, TableStyle, BtnWrapper, ButtonClose, ModalText } from '../../styles/Components';
import LandSearchBar from '../../components/searchBar/LandSearchBar';
import Button from '../../components/common/button/Button';
import QuestDetailModal from '../../components/DataManage/QuestDetailModal';
import LandDetailModal from '../../components/DataManage/LandDetailModal';
import Modal from '../../components/common/modal/Modal';
import { useNavigate } from 'react-router-dom';
import { authList } from '../../store/authList';
import { useRecoilValue } from 'recoil';
const ContentsView = () => {
const navigate = useNavigate();
const userInfo = useRecoilValue(authList);
const [detailPop, setDetailPop] = useState('hidden');
const mokupData = [
{
landId: '2000515223',
ownerNick: '칼리버스F',
ownerId: '23d59e868ca342198f6a653d957914a5',
lockInDate: '2023-08-11 15:32:07',
landUrl: '0x5765eB84ab55369f430DdA0d0C2b443FB9372DB3',
},
];
const handleClick = () => {
if (detailPop === 'hidden') setDetailPop('view');
else setDetailPop('hidden');
};
return (
<>
{userInfo.auth_list && !userInfo.auth_list.some(auth => auth.id === 13) ? (
<Modal min="440px" $padding="40px" $bgcolor="transparent" $view={'view'}>
<BtnWrapper $justify="flex-end">
<ButtonClose onClick={() => navigate(-1)} />
</BtnWrapper>
<ModalText $align="center">
해당 메뉴에 대한 조회 권한이 없습니다.
<br />
권한 등급을 변경 다시 이용해주세요.
</ModalText>
<BtnWrapper $gap="10px">
<Button text="확인" theme="primary" type="submit" size="large" width="100%" handleClick={() => navigate(-1)} />
</BtnWrapper>
</Modal>
) : (
<>
<Title>랜드 정보 조회</Title>
<LandSearchBar />
<TableWrapper>
<TableStyle>
<thead>
<tr>
<th width="150">랜드 ID</th>
<th>소유자 아바타명</th>
<th>소유쟈 GUID</th>
<th width="200">Lock IN 처리 일자</th>
<th>랜드 URL</th>
<th width="150">상세보기</th>
</tr>
</thead>
<tbody>
{mokupData.map((data, index) => (
<Fragment key={index}>
<tr>
<td>{data.landId}</td>
<td>{data.ownerNick}</td>
<td>{data.ownerId}</td>
<td>{new Date(data.lockInDate).toLocaleString()}</td>
<td>
<LandLink to={data.landUrl}>{data.landUrl}</LandLink>
</td>
<td>
<Button theme="line" text="상세보기" handleClick={handleClick} />
</td>
</tr>
</Fragment>
))}
</tbody>
</TableStyle>
</TableWrapper>
<LandDetailModal detailPop={detailPop} handleClick={handleClick} />
</>
)}
</>
);
};
export default ContentsView;
const TableWrapper = styled.div`
overflow: auto;
border-top: 1px solid #000;
&::-webkit-scrollbar {
height: 4px;
}
&::-webkit-scrollbar-thumb {
background: #666666;
}
&::-webkit-scrollbar-track {
background: #d9d9d9;
}
thead {
th {
position: sticky;
top: 0;
z-index: 10;
}
}
${TableStyle} {
min-width: 1000px;
th {
position: sticky;
top: 0;
}
}
`;
const LandLink = styled(Link)`
color: #61a2d0;
text-decoration: underline;
`;

View File

@@ -1,5 +1,6 @@
import { Fragment, useEffect, useState } from 'react';
import { AnimatedPageWrapper } from '../../components/common/Layout'
import Button from '../../components/common/button/Button';
import Pagination from '../../components/common/Pagination/Pagination';
@@ -119,7 +120,7 @@ const GameLogView = () => {
};
return (
<>
<AnimatedPageWrapper>
<Title>게임 로그 조회</Title>
<TabScroll>
<TabWrapper>
@@ -137,7 +138,7 @@ const GameLogView = () => {
{/*{activeTab === 'ITEM' && <ItemLogContent />}*/}
<CurrencyLogContent active={activeTab === 'CURRENCY'} />
{/*{activeTab === 'TRADE' && <TradeLogContent />}*/}
</>
</AnimatedPageWrapper>
);
};

View File

@@ -1,6 +1,7 @@
import { styled } from 'styled-components';
import { Link } from 'react-router-dom';
import React, { Fragment, useRef, useState } from 'react';
import { AnimatedPageWrapper } from '../../components/common/Layout'
import {
Title,
@@ -158,7 +159,7 @@ const LandInfoView = () => {
}
return (
<>
<AnimatedPageWrapper>
<Title>랜드 정보 조회</Title>
<FormWrapper>
<LandInfoSearchBar
@@ -240,7 +241,7 @@ const LandInfoView = () => {
content={detailData}
setDetailData={setDetailData}
/>
</>
</AnimatedPageWrapper>
);
};

View File

@@ -1,6 +1,7 @@
import { useState, Fragment } from 'react';
import { TabScroll, Title } from '../../styles/Components';
import { AnimatedPageWrapper } from '../../components/common/Layout'
import styled from 'styled-components';
@@ -39,7 +40,7 @@ const UserView = () => {
{userInfo.auth_list && !userInfo.auth_list.some(auth => auth.id === authType.userSearchRead) ? (
<AuthModal />
) : (
<>
<AnimatedPageWrapper>
<Title>유저조회</Title>
<UserViewSearchBar setInfoView={setInfoView} resultData={resultData} handleTab={handleTab} setResultData={setResultData} />
<UserWrapper display={infoView}>
@@ -68,7 +69,7 @@ const UserView = () => {
{activeContent === '클레임' && <UserClaimInfo userInfo={resultData && resultData} />}
</UserTabInfo>
</UserWrapper>
</>
</AnimatedPageWrapper>
)}
</>
);

View File

@@ -1,5 +1,6 @@
import { useState } from 'react';
import { Link } from 'react-router-dom';
import { AnimatedPageWrapper } from '../../components/common/Layout'
import Button from '../../components/common/button/Button';
@@ -29,7 +30,7 @@ const EconomicIndex = () => {
setActiveTab(content);
};
return (
<>
<AnimatedPageWrapper>
<Title>경제 지표</Title>
<TabScroll>
<TabWrapper>
@@ -49,7 +50,7 @@ const EconomicIndex = () => {
{/*{activeTab === 'item' && <ItemContent />}*/}
{/*{activeTab === 'instance' && <InstanceContent />}*/}
{/*{activeTab === 'deco' && <DecoContent />}*/}
</>
</AnimatedPageWrapper>
);
};

View File

@@ -2,6 +2,7 @@ import { Fragment, useEffect, useState } from 'react';
import { styled } from 'styled-components';
import { Link, useNavigate } from 'react-router-dom';
import { useRecoilValue } from 'recoil';
import { AnimatedPageWrapper } from '../../components/common/Layout';
import Modal from '../../components/common/modal/Modal';
import Button from '../../components/common/button/Button';
@@ -14,6 +15,7 @@ import { userIndexView, userTotalIndex } from '../../apis';
import { UserContent, SegmentContent, PlayTimeContent, RetentionContent, DailyActiveUserContent, DailyMedalContent } from '../../components/IndexManage/index';
import AuthModal from '../../components/common/modal/AuthModal';
import { authType } from '../../assets/data';
import { withAuth } from '../../hooks/hook';
const UserIndex = () => {
const token = sessionStorage.getItem('token');
@@ -37,41 +39,35 @@ const UserIndex = () => {
];
return (
<>
{userInfo.auth_list && !userInfo.auth_list.some(auth => auth.id === authType.userIndicatorsRead) ? (
<AuthModal/>
) : (
<>
<Title>유저 지표</Title>
<TabWrapper>
{TabList.map((el, idx) => {
return (
<TabItem
key={idx}
$state={el.title === activeTab ? 'active' : 'unactive'}
onClick={(e) => handleTab(e, el.title)}
>
{el.title}
</TabItem>
);
})}
</TabWrapper>
<AnimatedPageWrapper>
<Title>유저 지표</Title>
<TabWrapper>
{TabList.map((el, idx) => {
return (
<TabItem
key={idx}
$state={el.title === activeTab ? 'active' : 'unactive'}
onClick={(e) => handleTab(e, el.title)}
>
{el.title}
</TabItem>
);
})}
</TabWrapper>
{/*{activeTab === 'DAU' && <DailyActiveUserContent />}*/}
{activeTab === '이용자 지표' && <UserContent />}
{activeTab === 'Retention' && <RetentionContent />}
{activeTab === 'Segment' && <SegmentContent />}
{activeTab === '플레이타임' && <PlayTimeContent />}
{/*{activeTab === '메달' && <DailyMedalContent />}*/}
</>
)}
</>
{/*{activeTab === 'DAU' && <DailyActiveUserContent />}*/}
{activeTab === '이용자 지표' && <UserContent />}
{activeTab === 'Retention' && <RetentionContent />}
{activeTab === 'Segment' && <SegmentContent />}
{activeTab === '플레이타임' && <PlayTimeContent />}
{/*{activeTab === '메달' && <DailyMedalContent />}*/}
</AnimatedPageWrapper>
);
};
export default UserIndex;
export default withAuth(authType.userIndicatorsRead)(UserIndex);
const TabItem = styled(Link)`
display: inline-flex;

View File

@@ -48,6 +48,7 @@ import { useLoading } from '../../context/LoadingProvider';
import LogDetailModal from '../../components/common/modal/LogDetailModal';
import { historyTables } from '../../assets/data/data';
import { LogHistory } from '../../apis';
import { AnimatedPageWrapper } from '../../components/common/Layout';
const BattleEvent = () => {
const token = sessionStorage.getItem('token');
@@ -255,7 +256,7 @@ const BattleEvent = () => {
}
return (
<>
<AnimatedPageWrapper>
<Title>전투시스템 타임 스케줄러</Title>
<FormWrapper>
<BattleEventSearchBar
@@ -381,7 +382,7 @@ const BattleEvent = () => {
title="히스토리"
/>
</>
</AnimatedPageWrapper>
)
};

View File

@@ -36,6 +36,7 @@ import { useLoading } from '../../context/LoadingProvider';
import LogDetailModal from '../../components/common/modal/LogDetailModal';
import { historyTables } from '../../assets/data/data';
import { LogHistory } from '../../apis';
import { AnimatedPageWrapper } from '../../components/common/Layout';
const Board = () => {
const token = sessionStorage.getItem('token');
@@ -127,7 +128,7 @@ const Board = () => {
}
return (
<>
<AnimatedPageWrapper>
<Title>인게임 메시지</Title>
<TableInfo>
<ListOption>
@@ -217,7 +218,7 @@ const Board = () => {
title="히스토리"
/>
</TableWrapper>
</>
</AnimatedPageWrapper>
);
};

View File

@@ -35,6 +35,7 @@ import { alertTypes } from '../../assets/data/types';
import useCommonSearchOld from '../../hooks/useCommonSearchOld';
import { historyTables } from '../../assets/data/data';
import LogDetailModal from '../../components/common/modal/LogDetailModal';
import {AnimatedPageWrapper} from '../../components/common/Layout';
const Event = () => {
const token = sessionStorage.getItem('token');
@@ -158,7 +159,7 @@ const Event = () => {
{userInfo.auth_list && !userInfo.auth_list.some(auth => auth.id === authType.eventRead) ? (
<AuthModal />
) : (
<>
<AnimatedPageWrapper>
<Title>출석 보상 이벤트 관리</Title>
<FormWrapper>
<CommonSearchBar
@@ -281,7 +282,7 @@ const Event = () => {
<ModalSubText $color={deleteDesc.length > 29 ? 'red' : '#666'}>* 최대 등록 가능 글자수 ({deleteDesc.length}/30)</ModalSubText>
</ModalInputItem>
</DynamicModal>
</>
</AnimatedPageWrapper>
)}
</>
);

View File

@@ -33,6 +33,7 @@ import { timeDiffMinute } from '../../utils';
import { useAlert } from '../../context/AlertProvider';
import { alertTypes, currencyCodeTypes } from '../../assets/data/types';
import { useLoading } from '../../context/LoadingProvider';
import { AnimatedPageWrapper } from '../../components/common/Layout';
const EventRegist = () => {
const navigate = useNavigate();
@@ -275,7 +276,7 @@ const EventRegist = () => {
};
return (
<>
<AnimatedPageWrapper>
{userInfo.auth_list && !userInfo.auth_list.some(auth => auth.id === authType.eventUpdate) ? (
<AuthModal/>
) : (
@@ -458,7 +459,7 @@ const EventRegist = () => {
</BtnWrapper>
</>
)}
</>
</AnimatedPageWrapper>
);
};

View File

@@ -14,6 +14,7 @@ import { useLoading } from '../../context/LoadingProvider';
import useEnhancedCommonSearch from '../../hooks/useEnhancedCommonSearch';
import CustomConfirmModal from '../../components/common/modal/CustomConfirmModal';
import { useTranslation } from 'react-i18next';
import { AnimatedPageWrapper } from '../../components/common/Layout';
const Items = () => {
const token = sessionStorage.getItem('token');
@@ -155,7 +156,7 @@ const Items = () => {
}
return (
<>
<AnimatedPageWrapper>
<Title>아이템 관리</Title>
<FormWrapper>
<CommonSearchBar
@@ -199,7 +200,7 @@ const Items = () => {
handleCancel={() => handleModalClose('delete')}
handleClose={() => handleModalClose('delete')}
/>
</>
</AnimatedPageWrapper>
);
};

View File

@@ -36,6 +36,7 @@ import { alertTypes } from '../../assets/data/types';
import { useAlert } from '../../context/AlertProvider';
import LogDetailModal from '../../components/common/modal/LogDetailModal';
import { historyTables } from '../../assets/data/data';
import { AnimatedPageWrapper } from '../../components/common/Layout';
const LandAuction = () => {
const token = sessionStorage.getItem('token');
@@ -161,7 +162,7 @@ const LandAuction = () => {
}
return (
<>
<AnimatedPageWrapper>
<Title>랜드 경매 관리</Title>
<FormWrapper>
<LandAuctionSearchBar
@@ -277,7 +278,7 @@ const LandAuction = () => {
title="히스토리"
/>
</>
</AnimatedPageWrapper>
)
};

View File

@@ -34,6 +34,7 @@ import { useLoading } from '../../context/LoadingProvider';
import useCommonSearchOld from '../../hooks/useCommonSearchOld';
import LogDetailModal from '../../components/common/modal/LogDetailModal';
import { historyTables } from '../../assets/data/data';
import { AnimatedPageWrapper } from '../../components/common/Layout';
const Mail = () => {
const token = sessionStorage.getItem('token');
@@ -134,7 +135,7 @@ const Mail = () => {
}
return (
<>
<AnimatedPageWrapper>
<Title>우편 조회 발송 관리</Title>
<FormWrapper>
<CommonSearchBar
@@ -238,7 +239,7 @@ const Mail = () => {
title="히스토리"
/>
</>
</AnimatedPageWrapper>
);
};

View File

@@ -15,6 +15,7 @@ import {
Textarea,
SearchBarAlert,
} from '../../styles/Components';
import { AnimatedPageWrapper } from '../../components/common/Layout'
import IconDelete from '../../assets/img/icon/icon-delete.png';
import CloseIcon from '../../assets/img/icon/icon-close.png';
@@ -351,7 +352,7 @@ const MailRegist = () => {
{userInfo.auth_list && !userInfo.auth_list.some(auth => auth.id === authType.mailUpdate) ? (
<AuthModal />
) : (
<>
<AnimatedPageWrapper>
<Title>우편 등록</Title>
<RegistGroup>
@@ -637,7 +638,7 @@ const MailRegist = () => {
handleClick={() => handleSubmit('submit')}
/>
</BtnWrapper>
</>
</AnimatedPageWrapper>
)}
</>
);

View File

@@ -22,6 +22,7 @@ import useEnhancedCommonSearch from '../../hooks/useEnhancedCommonSearch';
import MenuBannerDetailModal from '../../components/modal/MenuBannerDetailModal';
import { historyTables } from '../../assets/data/data';
import LogDetailModal from '../../components/common/modal/LogDetailModal';
import { AnimatedPageWrapper } from '../../components/common/Layout';
const MenuBanner = () => {
const tableRef = useRef(null);
@@ -121,7 +122,7 @@ const MenuBanner = () => {
};
return (
<>
<AnimatedPageWrapper>
<Title>메뉴 배너 관리</Title>
{/* 조회조건 */}
@@ -192,7 +193,7 @@ const MenuBanner = () => {
title="히스토리"
/>
</>
</AnimatedPageWrapper>
)
};

View File

@@ -9,6 +9,7 @@ import {
BtnWrapper,
SearchBarAlert,
} from '../../styles/Components';
import { AnimatedPageWrapper } from '../../components/common/Layout';
import { useNavigate } from 'react-router-dom';
import { MenuBannerSingleRegist } from '../../apis';
@@ -258,7 +259,7 @@ const MenuBannerRegist = () => {
{userInfo.auth_list && !userInfo.auth_list.some(auth => auth.id === authType.eventUpdate) ? (
<AuthModal/>
) : (
<>
<AnimatedPageWrapper>
<Title>메뉴배너 등록</Title>
<RegistGroup>
<FormRowGroup>
@@ -365,7 +366,7 @@ const MenuBannerRegist = () => {
})}
/>
</BtnWrapper>
</>
</AnimatedPageWrapper>
)}
</>
);

View File

@@ -1,8 +1,9 @@
import { Fragment, useEffect, useState } from 'react';
import styled from 'styled-components';
import { AnimatedPageWrapper } from '../../components/common/Layout';
import CheckBox from '../../components/common/input/CheckBox';
import styled from 'styled-components';
import { Title, FormWrapper, TableInfo, ListCount, ListOption, TableStyle, SelectInput, TableWrapper, ButtonClose, ModalText, BtnWrapper } from '../../styles/Components';
import Button from '../../components/common/button/Button';
@@ -242,7 +243,7 @@ const ReportList = () => {
{userInfo.auth_list && !userInfo.auth_list.some(auth => auth.id === authType.reportRead) ? (
<AuthModal/>
) : (
<>
<AnimatedPageWrapper>
<Title>신고내역 조회 답변</Title>
<ReportListSummary />
<FormWrapper>
@@ -331,7 +332,7 @@ const ReportList = () => {
<Button text="확인" theme="primary" type="submit" size="large" width="100%" handleClick={handleConfirmeModalClose} />
</BtnWrapper>
</Modal>
</>
</AnimatedPageWrapper>
)}
</>
);

View File

@@ -1,6 +1,7 @@
import React, { useState, Fragment } from 'react';
import { Title, FormWrapper, TextInput } from '../../styles/Components';
import { useNavigate } from 'react-router-dom';
import { AnimatedPageWrapper } from '../../components/common/Layout';
import {
CommonSearchBar,
UserBlockDetailModal,
@@ -130,7 +131,7 @@ const UserBlock = () => {
};
return (
<>
<AnimatedPageWrapper>
<Title>이용자 제재 조회 등록</Title>
<FormWrapper>
<CommonSearchBar
@@ -209,7 +210,7 @@ const UserBlock = () => {
<ModalSubText $color={deleteDesc.length > 29 ? 'red' : '#666'}>* 최대 등록 가능 글자수 ({deleteDesc.length}/30)</ModalSubText>
</ModalInputItem>
</DynamicModal>
</>
</AnimatedPageWrapper>
);
};

View File

@@ -1,5 +1,7 @@
import React, { useState, useEffect, useRef } from 'react';
import {AnimatedPageWrapper} from '../../components/common/Layout'
import Button from '../../components/common/button/Button';
import RadioInput from '../../components/common/input/Radio';
@@ -267,7 +269,7 @@ const UserBlockRegist = () => {
{userInfo.auth_list && !userInfo.auth_list.some(auth => auth.id === authType.blackListUpdate) ? (
<AuthModal/>
) : (
<>
<AnimatedPageWrapper>
<Title>이용자 제재 등록</Title>
<div>
@@ -563,7 +565,7 @@ const UserBlockRegist = () => {
/>
</BtnWrapper>
</BtnWrapper>
</>
</AnimatedPageWrapper>
)}
</>
);

View File

@@ -1,5 +1,6 @@
import { Fragment, useEffect, useState } from 'react';
import { AnimatedPageWrapper } from '../../components/common/Layout'
import CheckBox from '../../components/common/input/CheckBox';
import { Title, FormWrapper, SelectInput, BtnWrapper, TableInfo, ListCount, ListOption, TableStyle, State, ButtonClose, ModalText } from '../../styles/Components';
import Button from '../../components/common/button/Button';
@@ -292,7 +293,7 @@ function AdminView() {
{userInfo.auth_list && !userInfo.auth_list.some(auth => auth.id === 1) ? (
<AuthModal />
) : (
<>
<AnimatedPageWrapper>
<Title>운영자 조회</Title>
<FormWrapper action="" $flow="row">
<AdminViewSearchBar handleSearch={handleSearch} groupList={groupList} setResultData={setSearchData} setCurrentPage={setCurrentPage} />
@@ -492,7 +493,7 @@ function AdminView() {
<Button text="확인" theme="primary" type="submit" size="large" width="100%" handleClick={handlePasswordInitialize} />
</BtnWrapper>
</Modal>
</>
</AnimatedPageWrapper>
)}
</>
);

View File

@@ -1,5 +1,6 @@
import CheckBox from '../../components/common/input/CheckBox';
import { AnimatedPageWrapper } from '../../components/common/Layout'
import Modal from '../../components/common/modal/Modal';
import { Title, FormWrapper, SelectInput, TableInfo, ListCount, ListOption, TableStyle, BtnWrapper, ButtonClose, ModalText } from '../../styles/Components';
import Button from '../../components/common/button/Button';
@@ -168,7 +169,7 @@ const AuthSetting = () => {
{userInfo.auth_list && !userInfo.auth_list.some(auth => auth.id === authType.authoritySettingRead) ? (
<AuthModal />
) : (
<>
<AnimatedPageWrapper>
<Title>권한 설정</Title>
{userInfo.auth_list && userInfo.auth_list.some(auth => auth.id === authType.authoritySettingUpdate) && (
<FormWrapper action="" $flow="row">
@@ -270,7 +271,7 @@ const AuthSetting = () => {
/>
</BtnWrapper>
</Modal>
</>
</AnimatedPageWrapper>
)}
</>
);

View File

@@ -2,6 +2,7 @@ import { useLocation, useNavigate } from 'react-router-dom';
import { useCallback, useEffect, useMemo, useState } from 'react';
import { useRecoilValue } from 'recoil';
import { useTranslation } from 'react-i18next';
import { AnimatedPageWrapper } from '../../components/common/Layout';
import { authList } from '../../store/authList';
import { authType, modalTypes } from '../../assets/data';
@@ -100,7 +101,7 @@ const AuthSettingUpdate = () => {
{userInfo.auth_list && !userInfo.auth_list.some(auth => auth.id === authType.authoritySettingUpdate) ? (
<AuthModal />
) : (
<>
<AnimatedPageWrapper>
<Title>권한 설정</Title>
<FormWrapper $flow="column">
<TableStyle>
@@ -148,7 +149,7 @@ const AuthSettingUpdate = () => {
</BtnWrapper>
</FormWrapper>
</>
</AnimatedPageWrapper>
)}
</>
);

View File

@@ -3,6 +3,7 @@ import { useRecoilValue } from 'recoil';
import { useTranslation } from 'react-i18next';
import 'react-datepicker/dist/react-datepicker.css';
import { AnimatedPageWrapper } from '../../components/common/Layout'
import { authList } from '../../store/authList';
import {
authType,
@@ -136,7 +137,7 @@ const CaliumRequest = () => {
}
return (
<>
<AnimatedPageWrapper>
<Title>칼리움 사용 수량 요청</Title>
<FormWrapper>
<CommonSearchBar
@@ -250,7 +251,7 @@ const CaliumRequest = () => {
title="히스토리"
/>
</>
</AnimatedPageWrapper>
);
};

View File

@@ -1,6 +1,7 @@
import React, { useState, Fragment, useMemo } from 'react';
import Button from '../../components/common/button/Button';
import Loading from '../../components/common/Loading';
import { AnimatedPageWrapper } from '../../components/common/Layout'
import {
Title,
@@ -139,7 +140,7 @@ const DataInitView = () => {
}
return (
<>
<AnimatedPageWrapper>
<Title>데이터 초기화</Title>
<MessageWrapper>
<FormRowGroup>
@@ -227,7 +228,7 @@ const DataInitView = () => {
handleCancel={() => handleModalClose('registConfirm')}
/>
<TopButton />
</>
</AnimatedPageWrapper>
);
};

View File

@@ -1,5 +1,6 @@
import React, { Fragment, useCallback, useState } from 'react';
import { AnimatedPageWrapper } from '../../components/common/Layout'
import { CommonSearchBar } from '../../components/ServiceManage';
import { Title, FormWrapper } from '../../styles/Components';
import { authType } from '../../assets/data';
@@ -51,7 +52,7 @@ const LogView = () => {
};
return (
<>
<AnimatedPageWrapper>
<Title>사용 이력 조회</Title>
{/* 조회조건 */}
@@ -99,7 +100,7 @@ const LogView = () => {
title="상세정보"
/>
</>
</AnimatedPageWrapper>
);
};

View File

@@ -27,7 +27,7 @@ export const Container = styled.div`
export const HeaderContainer = styled.div`
width: 280px;
flex: 0 0 280px;
background: #666666;
background: #141414;
min-height: 100vh;
align-self: stretch;
`;