컴포넌트 관련 변경

이미지 업로드 한글명칭 불가 처리
유저 조회 부분 수정
This commit is contained in:
2025-09-15 16:37:46 +09:00
parent b01c5cd410
commit 4407fdc6b6
11 changed files with 484 additions and 521 deletions

View File

@@ -12,7 +12,7 @@ export const UserView = async (token, searchType, searchKey) => {
{ headers: { Authorization: `Bearer ${token}` } },
);
return res.data.data.result;
return res.data;
} catch (e) {
if (e instanceof Error) {
throw new Error('UserView Error', e);

View File

@@ -93,12 +93,6 @@ const UserInfoTable = styled.table`
font-size: 13px;
border-radius: 15px;
overflow: hidden;
tr:first-child {
th,
td {
border-top: 0;
}
}
th,
td {
height: 36px;

View File

@@ -4,7 +4,7 @@ import { useState, useEffect } from 'react';
import { UserTattooView } from '../../apis/Users';
import { TableSkeleton } from '../Skeleton/TableSkeleton';
const UserTatttooInfo = ({ userInfo }) => {
const UserTattooInfo = ({ userInfo }) => {
const [dataList, setDataList] = useState();
const [loading, setLoading] = useState(true);
@@ -62,7 +62,7 @@ const UserTatttooInfo = ({ userInfo }) => {
);
};
export default UserTatttooInfo;
export default UserTattooInfo;
const UserDefaultTable = styled.table`
border: 1px solid #e8eaec;

View File

@@ -32,6 +32,18 @@ const ImageUploadBtn = ({ disabled,
if (!file) return;
const koreanRegex = /[ㄱ-ㅎ|ㅏ-ㅣ|가-힣]/;
if (koreanRegex.test(file.name)) {
showToast('FILE_KOREAN_NAME_WARNING', {
type: alertTypes.warning
});
if (document.querySelector('#fileinput')) {
document.querySelector('#fileinput').value = '';
}
onFileDelete();
return;
}
// 이미지 파일 확장자 체크
const fileExt = file.name.split('.').pop().toLowerCase();
if (fileExt !== 'png' && fileExt !== 'jpg' && fileExt !== 'jpeg') {

View File

@@ -1,79 +1,29 @@
import React from 'react';
import DatePickerComponent from './DatePickerComponent';
import { DatePickerWrapper } from '../../../styles/Components';
import {
FormRowGroup,
FormLabel,
DateContainer,
DateTimeWrapper,
DateTimeGroup,
} from '../../../styles/ModuleComponents';
import { useTranslation } from 'react-i18next';
import { DatePicker } from 'antd';
import dayjs from 'dayjs';
const { RangePicker } = DatePicker;
const DateRangePicker = ({
label,
startDate,
endDate,
onStartDateChange,
onEndDateChange,
pastDate = new Date(),
disabled,
startLabel = '시작 일자',
endLabel = '종료 일자',
setAlert,
value,
onChange,
format,
showTime = true,
size = 'middle',
...props
}) => {
const { t } = useTranslation();
const handleStartDate = (date) => {
const newDate = new Date(date);
onStartDateChange(newDate);
};
const handleEndDate = (date) => {
let newDate = new Date(date);
if (startDate && newDate < startDate) {
setAlert(t('DATE_START_DIFF_END'));
newDate = new Date(startDate);
}
onEndDateChange(newDate);
};
return (
<FormRowGroup>
<FormLabel>{label}</FormLabel>
<DateTimeWrapper>
<DateTimeGroup>
<DateContainer>
<DatePickerWrapper>
<DatePickerComponent
name={startLabel}
handleSelectedDate={handleStartDate}
selectedDate={startDate}
pastDate={pastDate}
disabled={disabled}
/>
</DatePickerWrapper>
</DateContainer>
</DateTimeGroup>
<DateTimeGroup>
<DateContainer>
<DatePickerWrapper>
<DatePickerComponent
name={endLabel}
handleSelectedDate={handleEndDate}
selectedDate={endDate}
pastDate={pastDate}
disabled={disabled}
/>
</DatePickerWrapper>
</DateContainer>
</DateTimeGroup>
</DateTimeWrapper>
</FormRowGroup>
<RangePicker
showTime={showTime}
value={value ? [dayjs(value[0]), dayjs(value[1])] : [null, null]}
format={format || 'YYYY-MM-DD HH:mm:ss'}
onChange={onChange}
placeholder={['시작 일시', '종료 일시']}
size={size}
allowClear={false}
{...props}
/>
);
};
export default DateRangePicker;

View File

@@ -0,0 +1,205 @@
import { NavLink, useNavigate } from 'react-router-dom';
import arrowIcon from '../../../assets/img/icon/icon-tab.png';
import styled from 'styled-components';
import { useRecoilValue } from 'recoil';
import { authList } from '../../../store/authList';
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';
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 location = useLocation();
const navigate = useNavigate();
const handleToken = async () => {
const tokenStatus = await AuthInfo(token);
tokenStatus.message === '잘못된 타입의 토큰입니다.' && setLogoutModalClose('view');
};
useEffect(() => {
handleToken();
}, [token]);
const handleTopMenu = e => {
e.preventDefault();
e.target.classList.toggle('active');
};
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 handleModalClose = () => {
if (modalClose === 'hidden') {
setModalClose('view');
} else {
setModalClose('hidden');
}
};
// 로그아웃 안내 모달
const handleConfirmClose = () => {
if (logoutModalClose === 'hidden') {
setLogoutModalClose('view');
} else {
setLogoutModalClose('hidden');
sessionStorage.removeItem('token');
navigate('/');
}
};
const isClickable = (submenu) => {
switch (userInfo.auth_level_type) {
case adminAuthLevel.DEVELOPER:
case adminAuthLevel.READER:
case adminAuthLevel.MASTER:
return true;
default:
return submenu.authLevel === adminAuthLevel.NONE && userInfo.auth_list && userInfo.auth_list.some(auth => auth.id === submenu.id);
}
}
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>
{/* 접근 불가 모달 */}
<Modal min="440px" $padding="40px" $bgcolor="transparent" $view={modalClose}>
<BtnWrapper $justify="flex-end">
<ButtonClose onClick={handleModalClose} />
</BtnWrapper>
<ModalText $align="center">
해당 메뉴에 대한 조회 권한이 없습니다.
<br />
권한 등급을 변경 다시 이용해주세요.
</ModalText>
<BtnWrapper $gap="10px">
<Button text="확인" theme="primary" type="submit" size="large" width="100%" handleClick={handleModalClose} />
</BtnWrapper>
</Modal>
{/* 로그아웃 안내 모달 */}
<Modal min="440px" $padding="40px" $bgcolor="transparent" $view={logoutModalClose}>
<BtnWrapper $justify="flex-end">
<ButtonClose onClick={handleConfirmClose} />
</BtnWrapper>
<ModalText $align="center">로그아웃 되었습니다.</ModalText>
<BtnWrapper $gap="10px">
<Button text="확인" theme="primary" type="submit" size="large" width="100%" handleClick={handleConfirmClose} />
</BtnWrapper>
</Modal>
</>
);
};
export default Navi;
const TopMenu = styled(NavLink)`
padding: 16px 30px;
width: 100%;
text-align: left;
border-bottom: 1px solid #888;
position: relative;
color: #fff;
&: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,
&.active {
background: #444;
}
&.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

@@ -26,7 +26,7 @@ const SearchBarLayout = ({ firstColumnData, secondColumnData, filter, direction,
</SearchRow>
)}
{isSearch &&
<SearchRow>
<SearchRow direction={direction}>
<BtnWrapper $gap="8px">
<Button theme="search" text="검색" handleClick={handleSubmit} type="button" />
<Button theme="reset" handleClick={onReset} type="button" />

View File

@@ -1,53 +0,0 @@
import React, { useState, useEffect } from 'react';
import styled from 'styled-components';
const DotsButton = styled.button`
width: 24px;
height: 24px;
border-radius: 50%;
background-color: #f0f0f0;
border: none;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
cursor: pointer;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
transition: all 0.2s ease;
&:hover {
background-color: #e0e0e0;
}
/* 점 스타일링 */
.dot {
width: 3px;
height: 3px;
border-radius: 50%;
background-color: #333;
margin: 2px 0;
}
`;
const VerticalDotsButton = ({ text, type = 'button', errorMessage, handleClick, size, width, height, borderColor, disabled, name }) => {
return (
<DotsButton
onSubmit={e => e.preventDefault()}
type={type}
disabled={disabled}
onClick={handleClick}
size={size}
bordercolor={borderColor}
width={width}
height={height}
name={name}
>
<div className="dot"></div>
<div className="dot"></div>
<div className="dot"></div>
</DotsButton>
);
};
export default VerticalDotsButton;

View File

@@ -1,231 +0,0 @@
import { useEffect, useRef, useState } from 'react';
import styled from 'styled-components';
const AIMessageInput = ({ onSendMessage }) => {
const [isOpen, setIsOpen] = useState(false);
const [message, setMessage] = useState('');
const [isSending, setIsSending] = useState(false);
const textareaRef = useRef(null);
const modalRef = useRef(null);
// 텍스트 영역 높이 자동 조절
useEffect(() => {
if (textareaRef.current && isOpen) {
textareaRef.current.style.height = 'auto';
textareaRef.current.style.height = `${Math.min(textareaRef.current.scrollHeight, 200)}px`;
}
}, [message, isOpen]);
// 모달 외부 클릭시 닫기
useEffect(() => {
const handleClickOutside = (event) => {
if (modalRef.current && !modalRef.current.contains(event.target)) {
closeModal();
}
};
if (isOpen) {
document.addEventListener('mousedown', handleClickOutside);
}
return () => {
document.removeEventListener('mousedown', handleClickOutside);
};
}, [isOpen]);
// 모달 열기
const openModal = () => {
setIsOpen(true);
// 모달이 열린 후 텍스트 영역에 포커스
setTimeout(() => {
if (textareaRef.current) {
textareaRef.current.focus();
}
}, 100);
};
// 모달 닫기
const closeModal = () => {
setIsOpen(false);
setMessage('');
};
// 메시지 전송 처리
const handleSendMessage = () => {
if (message.trim() && !isSending) {
setIsSending(true);
// 메시지 전송 처리
if (onSendMessage) {
onSendMessage(message);
}
// 입력 초기화 및 상태 업데이트
setMessage('');
setIsSending(false);
// 모달 닫기
closeModal();
}
};
// 엔터 키 처리 (Shift+Enter 줄바꿈, Enter 전송)
const handleKeyDown = (e) => {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault();
handleSendMessage();
}
};
return (
<>
{/* 메뉴 버튼 */}
<MenuButton onClick={openModal}>
<div className="dot"></div>
<div className="dot"></div>
<div className="dot"></div>
</MenuButton>
{/* 모달 오버레이 */}
<ModalOverlay isOpen={isOpen}>
<InputContainer ref={modalRef} isOpen={isOpen}>
<MessageInput
ref={textareaRef}
value={message}
onChange={(e) => setMessage(e.target.value)}
onKeyDown={handleKeyDown}
placeholder="메시지를 입력하세요..."
rows={1}
/>
<SendButton
onClick={handleSendMessage}
disabled={!message.trim() || isSending}
>
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path d="M2.01 21L23 12 2.01 3 2 10l15 2-15 2z" />
</svg>
</SendButton>
</InputContainer>
</ModalOverlay>
</>
);
};
export default AIMessageInput;
const ModalOverlay = styled.div`
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.5);
display: ${props => props.isOpen ? 'flex' : 'none'};
justify-content: center;
align-items: center;
z-index: 1000;
`;
// 메인 컨테이너
const InputContainer = styled.div`
width: 90%;
max-width: 600px;
border: 1px solid #e0e0e0;
border-radius: 12px;
background-color: #ffffff;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
overflow: hidden;
position: relative;
animation: ${props => props.isOpen ? 'slideUp 0.3s ease-out' : 'none'};
@keyframes slideUp {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
`;
// 메시지 입력 영역
const MessageInput = styled.textarea`
width: 100%;
min-height: 60px;
max-height: 200px;
padding: 16px 60px 16px 16px;
border: none;
outline: none;
resize: none;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
font-size: 16px;
line-height: 1.5;
background: transparent;
&::placeholder {
color: #9e9ea7;
}
`;
// 전송 버튼
const SendButton = styled.button`
position: absolute;
bottom: 12px;
right: 12px;
width: 38px;
height: 38px;
border-radius: 50%;
background-color: #5436DA;
color: white;
border: none;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
transition: all 0.2s ease;
&:hover {
background-color: #4527D0;
}
&:disabled {
background-color: #DADCE0;
cursor: not-allowed;
}
svg {
width: 18px;
height: 18px;
fill: white;
}
`;
// 메뉴 버튼 (세로 점 세개)
const MenuButton = styled.button`
width: 40px;
height: 40px;
border-radius: 50%;
background-color: #f0f0f0;
border: none;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
cursor: pointer;
transition: all 0.2s ease;
&:hover {
background-color: #e0e0e0;
}
.dot {
width: 4px;
height: 4px;
border-radius: 50%;
background-color: #666;
margin: 2px 0;
}
`;

View File

@@ -1,57 +1,124 @@
import { styled } from 'styled-components';
import Button from '../common/button/Button';
import { TextInput, SelectInput, InputGroup } from '../../styles/Components';
import { SearchBarLayout } from '../common/SearchBar';
import { useCallback, useState } from 'react';
import {
userSearchType2,
} from '../../assets/data/options';
import { useAlert } from '../../context/AlertProvider';
import { alertTypes } from '../../assets/data/types';
import { UserView } from '../../apis';
import { FormWrapper, InputLabel, TextInput, SelectInput, BtnWrapper } from '../../styles/Components';
export const useUserSearch = (token) => {
const {showToast} = useAlert();
const UserSearchBar = () => {
return (
<>
<FormWrapper>
<SearchbarStyle>
<SearchItem>
<InputLabel>유저 조회</InputLabel>
<TextInput type="text" width="300px" placeholder="조회 대상 유저의 GUID를 입력하세요." />
</SearchItem>
<BtnWrapper $gap="8px">
<Button theme="reset" />
<Button
theme="primary"
text="검색"
handleClick={e => {
e.preventDefault();
}}
/>
</BtnWrapper>
</SearchbarStyle>
</FormWrapper>
</>
);
const [searchParams, setSearchParams] = useState({
search_type: 'GUID',
search_data: ''
});
const [loading, setLoading] = useState(false);
const [data, setData] = useState(null);
const fetchData = useCallback(async (params) => {
if (!token) return;
try {
setLoading(true);
const result = await UserView(
token,
params.search_type,
params.search_data
);
if(result.result === "NOT_USER"){
showToast(result.result, {type: alertTypes.warning});
return;
}
setData(result.data.result);
return result.data.result;
} catch (error) {
showToast('error', {type: alertTypes.error});
throw error;
} finally {
setLoading(false);
}
}, [token]);
const updateSearchParams = useCallback((newParams) => {
setSearchParams(prev => ({
...prev,
...newParams
}));
}, []);
const handleSearch = useCallback(async (newParams = {}, executeSearch = true) => {
const updatedParams = {
...searchParams,
...newParams
};
updateSearchParams(updatedParams);
if (executeSearch) {
return await fetchData(updatedParams);
}
return null;
}, [searchParams, fetchData]);
const handleReset = useCallback(async () => {
const now = new Date();
now.setDate(now.getDate() - 1);
const resetParams = {
search_type: 'GUID',
search_data: ''
};
setSearchParams(resetParams);
return await fetchData(resetParams);
}, [fetchData]);
return {
searchParams,
loading,
data,
handleSearch,
handleReset,
updateSearchParams
};
};
export default UserSearchBar;
const UserSearchBar = ({ searchParams, onSearch, onReset }) => {
const handleSubmit = event => {
event.preventDefault();
const SearchbarStyle = styled.div`
width: 100%;
display: flex;
flex-wrap: wrap;
gap: 20px 0;
font-size: 14px;
padding: 20px;
border-top: 1px solid #000;
border-bottom: 1px solid #000;
margin-bottom: 40px;
`;
const SearchItem = styled.div`
display: flex;
align-items: center;
gap: 20px;
margin-right: 50px;
onSearch(searchParams, true);
};
${TextInput}, ${SelectInput} {
height: 35px;
}
${TextInput} {
padding: 0 10px;
max-width: 400px;
}
`;
const searchList = [
<>
<InputGroup>
<SelectInput value={searchParams.search_type} onChange={e => onSearch({search_type: e.target.value }, false)}>
{userSearchType2.map((data, index) => (
<option key={index} value={data.value}>
{data.name}
</option>
))}
</SelectInput>
<TextInput
type="text"
placeholder={searchParams.search_type === 'GUID' ? 'GUID 입력' : searchParams.search_type === 'ACCOUNT' ? 'ACCOUNT 입력' : '닉네임 입력'}
value={searchParams.search_data}
width="260px"
onChange={e => onSearch({ search_data: e.target.value }, false)}
onKeyDown={e => {
if (e.key === 'Enter') {
e.preventDefault();
onSearch({ search_data: e.target.value }, true);
}
}}
/>
</InputGroup>
</>
];
return <SearchBarLayout firstColumnData={searchList} onReset={onReset} handleSubmit={handleSubmit} />;
};
export default UserSearchBar;

View File

@@ -1,130 +1,149 @@
import { useState, Fragment } from 'react';
import React, { useState, useEffect, useMemo } from 'react';
import { TabScroll, Title } from '../../styles/Components';
import { TabItem, TabScroll, TabWrapper, Title } from '../../styles/Components';
import { AnimatedPageWrapper } from '../../components/common/Layout'
import styled from 'styled-components';
import UserViewSearchBar from '../../components/searchBar/UserViewSearchBar';
import UserDefaultInfo from '../../components/DataManage/UserDefaultInfo';
import UserAvatarInfo from '../../components/DataManage/UserAvatarInfo';
import UserDressInfo from '../../components/DataManage/UserDressInfo';
import UserToolInfo from '../../components/DataManage/UserToolInfo';
import UserInventoryInfo from '../../components/DataManage/UserInventoryInfo';
import UserMailInfo from '../../components/DataManage/UserMailInfo';
import UserMyHomeInfo from '../../components/DataManage/UserMyHomeInfo';
import UserFriendInfo from '../../components/DataManage/UserFriendInfo';
import UserTatttooInfo from '../../components/DataManage/UserTattooInfo';
import UserQuestInfo from '../../components/DataManage/UserQuestInfo';
import UserClaimInfo from '../../components/DataManage/UserClaimInfo';
import {
UserDefaultInfo,
UserAvatarInfo,
UserDressInfo,
UserToolInfo,
UserInventoryInfo,
UserMailInfo,
UserMyHomeInfo,
UserFriendInfo,
UserTattooInfo,
UserQuestInfo,
UserClaimInfo
} from '../../components/DataManage';
import { useNavigate } from 'react-router-dom';
import { authList } from '../../store/authList';
import { useRecoilValue } from 'recoil';
import AuthModal from '../../components/common/modal/AuthModal';
import { authType, TabUserList } from '../../assets/data';
import { UserSearchBar, useUserSearch } from '../../components/searchBar';
import { withAuth } from '../../hooks/hook';
import { AnimatedTabs } from '../../components/common';
const UserView = () => {
const userInfo = useRecoilValue(authList);
const token = sessionStorage.getItem('token');
const [infoView, setInfoView] = useState('none');
const [activeContent, setActiveContent] = useState('기본정보');
const [resultData, setResultData] = useState([]);
const [activeTab, setActiveTab] = useState('BASIC');
const [resultData, setResultData] = useState();
const handleTab = title => {
setActiveContent(title);
const {
searchParams,
loading: dataLoading,
data,
handleSearch,
handleReset,
updateSearchParams
} = useUserSearch(token);
useEffect(() => {
if(data) {
setResultData(data);
setInfoView('flex');
} else {
setInfoView('none');
}
}, [data])
const tabItems = useMemo(() => {
return TabUserList.map(el => ({
key: el.value,
label: el.name,
children: (() => {
switch(el.value) {
case 'BASIC': return <UserDefaultInfo userInfo={resultData} />;
case 'AVATAR': return <UserAvatarInfo userInfo={resultData} />;
case 'CLOTH': return <UserDressInfo userInfo={resultData} />;
case 'TOOL': return <UserToolInfo userInfo={resultData} />;
case 'INVENTORY': return <UserInventoryInfo userInfo={resultData} />;
case 'MAIL': return <UserMailInfo userInfo={resultData} />;
case 'MYHOME': return <UserMyHomeInfo userInfo={resultData} />;
case 'FRIEND': return <UserFriendInfo userInfo={resultData} />;
case 'TATTOO': return <UserTattooInfo userInfo={resultData} />;
case 'QUEST': return <UserQuestInfo userInfo={resultData} />;
case 'CLAIM': return <UserClaimInfo userInfo={resultData} />;
default: return null;
}
})()
}));
}, [resultData]);
const handleTabChange = (key) => {
setActiveTab(key);
};
// const handleTab = (e, content) => {
// e.preventDefault();
// setActiveTab(content);
// };
return (
<>
{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}>
<TabScroll>
<UserTabWrapper>
{TabUserList.map((el, idx) => {
return (
<UserTab key={idx} $state={el.title === activeContent ? 'active' : 'unactive'} onClick={() => handleTab(el.title)}>
{el.title}
</UserTab>
);
})}
</UserTabWrapper>
</TabScroll>
<UserTabInfo>
{activeContent === '기본정보' && <UserDefaultInfo userInfo={resultData && resultData} />}
{activeContent === '아바타' && <UserAvatarInfo userInfo={resultData && resultData} />}
{activeContent === '의상' && <UserDressInfo userInfo={resultData && resultData} />}
{activeContent === '도구' && <UserToolInfo userInfo={resultData && resultData} />}
{activeContent === '인벤토리' && <UserInventoryInfo userInfo={resultData && resultData} />}
{activeContent === '우편' && <UserMailInfo userInfo={resultData && resultData} />}
{activeContent === '마이홈' && <UserMyHomeInfo userInfo={resultData && resultData} />}
{activeContent === '친구목록' && <UserFriendInfo userInfo={resultData && resultData} />}
{activeContent === '타투' && <UserTatttooInfo userInfo={resultData && resultData} />}
{activeContent === '퀘스트' && <UserQuestInfo userInfo={resultData && resultData} />}
{activeContent === '클레임' && <UserClaimInfo userInfo={resultData && resultData} />}
</UserTabInfo>
</UserWrapper>
</AnimatedPageWrapper>
)}
</>
<AnimatedPageWrapper>
<Title>유저조회</Title>
<UserSearchBar
searchParams={searchParams}
onSearch={(newParams, executeSearch = true) => {
if (executeSearch) {
handleSearch(newParams);
} else {
updateSearchParams(newParams);
setResultData();
}
}}
onReset={handleReset}
/>
<UserWrapper display={infoView}>
<AnimatedTabs
items={tabItems}
activeKey={activeTab}
onChange={handleTabChange}
tabPosition="center"
/>
{/*<TabScroll>*/}
{/* <TabWrapper>*/}
{/* {TabUserList.map((el, idx) => {*/}
{/* return (*/}
{/* <li key={idx}>*/}
{/* <TabItem $state={activeTab === el.value ? 'active' : 'none'} onClick={e => handleTab(e, el.value)}>*/}
{/* {el.name}*/}
{/* </TabItem>*/}
{/* </li>*/}
{/* )*/}
{/* })}*/}
{/* </TabWrapper>*/}
{/*</TabScroll>*/}
{/*<UserTabInfo>*/}
{/* {activeTab === 'BASIC' && <UserDefaultInfo userInfo={resultData && resultData} />}*/}
{/* {activeTab === 'AVATAR' && <UserAvatarInfo userInfo={resultData && resultData} />}*/}
{/* {activeTab === 'CLOTH' && <UserDressInfo userInfo={resultData && resultData} />}*/}
{/* {activeTab === 'TOOL' && <UserToolInfo userInfo={resultData && resultData} />}*/}
{/* {activeTab === 'INVENTORY' && <UserInventoryInfo userInfo={resultData && resultData} />}*/}
{/* {activeTab === 'MAIL' && <UserMailInfo userInfo={resultData && resultData} />}*/}
{/* {activeTab === 'MYHOME' && <UserMyHomeInfo userInfo={resultData && resultData} />}*/}
{/* {activeTab === 'FRIEND' && <UserFriendInfo userInfo={resultData && resultData} />}*/}
{/* {activeTab === 'TATTOO' && <UserTattooInfo userInfo={resultData && resultData} />}*/}
{/* {activeTab === 'QUEST' && <UserQuestInfo userInfo={resultData && resultData} />}*/}
{/* {activeTab === 'CLAIM' && <UserClaimInfo userInfo={resultData && resultData} />}*/}
{/*</UserTabInfo>*/}
</UserWrapper>
</AnimatedPageWrapper>
);
};
export default UserView;
export default withAuth(authType.userSearchRead)(UserView);
const UserTab = styled.li`
display: flex;
width: 100%;
max-width: 120px;
height: 35px;
align-items: center;
justify-content: center;
border-top-left-radius: 5px;
border-top-right-radius: 5px;
color: #888;
text-align: center;
cursor: pointer;
&:hover {
font-weight: 700;
color: #fff;
background: #888;
}
${props =>
props.$state === 'active' &&
`font-weight: 700;
color: #fff;
background: #888;`}
`;
const UserTabWrapper = styled.ul`
border-bottom: 1px solid #888888;
display: flex;
background: #fff;
`;
const UserWrapper = styled.div`
display: ${props => props.display};
${props => props.display && `flex-flow:column;`}
min-height: calc(100vh - 345px);
overflow: hidden;
background: #f9f9f9;
`;
const UserTabInfo = styled.div`
padding: 50px;
overflow: auto;
&::-webkit-scrollbar {
width: 4px;
}
&::-webkit-scrollbar-thumb {
background: #666666;
}
&::-webkit-scrollbar-track {
background: #d9d9d9;
}
`;
padding-left: 40px;
padding-right: 40px;
//background: #f9f9f9;
`;