This commit is contained in:
2025-02-12 18:29:27 +09:00
commit 513ea114cc
290 changed files with 84274 additions and 0 deletions

View File

@@ -0,0 +1,168 @@
import { useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { useForm } from 'react-hook-form';
import { yupResolver } from '@hookform/resolvers/yup';
import { PasswordValidation } from './PasswordValidation';
import styled from 'styled-components';
import { FormBox, AccountTitle, FormWrapper, BtnWrapper, ModalText } from '../../styles/Components';
import Button from '../../components/common/button/Button';
import Modal from '../../components/common/modal/Modal';
import CloseIcon from '../../assets/img/icon/icon-close.png';
import { AccountEditItem } from '../../components/account';
import { AuthEdit } from '../../apis';
const AccountEdit = () => {
const [completeModal, setCompleteModal] = useState('hidden');
const navigate = useNavigate();
const [confirmText, setConfirmText] = useState('');
const onSubmitData = async data => {
const token = sessionStorage.getItem('token');
const message = await AuthEdit(data, token);
message.data.data.message !== '수정 하였습니다.' ? setConfirmText(message.data.data.message) : setConfirmText('변경이 완료되었습니다.');
setForm('');
handleCompleteModal();
};
const {
register,
formState: { errors },
watch,
handleSubmit,
} = useForm({
resolver: yupResolver(PasswordValidation),
mode: 'onChange',
});
const values = watch();
const [form, setForm] = useState({
newPassword: '',
passwordConfirm: '',
});
const { newPassword, passwordConfirm } = form;
const onChange = e => {
const nextForm = {
...form,
[e.target.name]: e.target.value,
};
setForm(nextForm);
};
// console.log(form);
const AccountEditData = [
{
title: '신규 비밀번호',
name: 'newPassword',
placeholder: '영어/숫자/특수문자 조합 가능(8~20자)',
value: newPassword,
},
{
title: '비밀번호 확인',
name: 'passwordConfirm',
placeholder: '비밀번호 재입력',
value: passwordConfirm,
},
];
// 완료 모달창
const handleCompleteModal = () => {
if (completeModal === 'hidden') {
setCompleteModal('view');
} else {
setCompleteModal('hidden');
if (confirmText === '변경이 완료되었습니다.') {
sessionStorage.removeItem('token');
navigate('/');
}
}
};
// 취소버튼
const handleSessionout = e => {
e.preventDefault();
sessionStorage.removeItem('token');
navigate('/');
};
return (
<>
<FormBox>
<AccountTitle>비밀번호 재설정</AccountTitle>
<FormWrapper action="" $flow="column" onSubmit={handleSubmit(onSubmitData)}>
{/* 비밀번호 만료 안내에서 비밀번호 변경 시 현재 비밀번호 입력 항목이 보입니다. */}
{AccountEditData.map((data, index) => (
<AccountEditItem
key={index}
title={data.title}
name={data.name}
placeholder={data.placeholder}
alertText={data.alertText}
value={data.value}
onChange={onChange}
register={register}
errors={errors}
/>
))}
<PwBtnWrap $justify="space-between" $gap="14px">
<Button
theme="line"
size="large"
bordercolor="#e4e4e4"
text="취소"
handleClick={e => {
handleSessionout(e);
}}
width="100%"
/>
<Button
size="large"
width="100%"
text="완료"
type="submit"
bordercolor="#e4e4e4"
theme={!values.passwordConfirm || errors.newPassword || errors.passwordConfirm ? 'disable' : 'primary'}
isDisabled={values.password && values.newPassword && values.passwordConfirm ? false : true}
/>
</PwBtnWrap>
</FormWrapper>
</FormBox>
{/* 완료 모달 */}
<Modal min="440px" $padding="40px" $bgcolor="transparent" $view={completeModal}>
<BtnWrapper $justify="flex-end">
<ButtonClose onClick={handleCompleteModal} />
</BtnWrapper>
<ModalText $align="center">{confirmText}</ModalText>
<BtnWrapper $gap="10px">
<Button text="확인" theme="primary" type="submit" size="large" width="100%" handleClick={handleCompleteModal} />
</BtnWrapper>
</Modal>
</>
);
};
export default AccountEdit;
const PwBtnWrap = styled(BtnWrapper)`
margin-top: 25px;
`;
const ButtonClose = styled.button`
width: 16px;
height: 16px;
background: url(${CloseIcon}) 50% 50% no-repeat;
`;

View File

@@ -0,0 +1,187 @@
import { useState, useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
import { useForm } from 'react-hook-form';
import { yupResolver } from '@hookform/resolvers/yup';
import { RegistValidation } from './RegistValidation';
import styled from 'styled-components';
import { FormBox, AccountTitle, FormWrapper, BtnWrapper } from '../../styles/Components';
import Button from '../../components/common/button/Button';
import { AccountInputItem } from '../../components/account';
import Modal from '../../components/common/modal/Modal';
import CloseUrl from '../../assets/img/icon/icon-close.png';
import { AuthRegist } from '../../apis';
const AccountRegist = () => {
const navigate = useNavigate();
const [stateModal, setStateModal] = useState('hidden');
const [submitText, setSubmitText] = useState('');
const [doubleSubmitFlag, setDoubleSubmitFlag] = useState(false);
const [isSuccess, setIsSuccess] = useState(false)
const handleModal = () => {
if (stateModal === 'hidden') {
setStateModal('view');
} else {
// submitText === '회원가입이 완료되었습니다. \n 관리자 로그인 권한 승인 까지 \n 대기해주세요.' ? navigate('/') : setStateModal('hidden'); //2024.08.02 승인 제외에 따른 변경
isSuccess ? navigate('/') : setStateModal('hidden');
setDoubleSubmitFlag(false);
}
};
const onSubmitData = async data => {
setDoubleSubmitFlag(true);
const result = await AuthRegist(data);
setSubmitText(
// result.data.access_token && result.data.access_token === '저장 하였습니다.' ? '회원가입이 완료되었습니다. \n 관리자 로그인 권한 승인 까지 \n 대기해주세요.' : result.data.message, //2024.08.02 승인 제외에 따른 메시지 변경
result.data.access_token && result.data.access_token === '저장 하였습니다.' ? '회원가입이 완료되었습니다.' : result.data.message,
);
setIsSuccess(result.data.access_token && result.data.access_token === '저장 하였습니다.' ? true : false);
handleModal();
};
// console.log(doubleSubmitFlag);
const {
register,
formState: { errors },
watch,
handleSubmit,
} = useForm({
resolver: yupResolver(RegistValidation),
mode: 'onChange',
});
const [form, setForm] = useState({
username: '',
userid: '',
password: '',
confirmPassword: '',
});
const { username, userid, password, confirmPassword } = form;
const values = watch();
const onChange = e => {
const nextForm = {
...form,
[e.target.name]: e.target.value,
};
setForm(nextForm);
};
const AccountRegistData = [
{
title: '이름',
type: 'text',
name: 'username',
placeholder: '한글, 영문 입력 가능',
value: username,
},
{
title: 'ID(메일주소)',
type: 'text',
name: 'userid',
placeholder: 'sample@lotte.net',
value: userid,
},
{
title: '비밀번호',
type: 'password',
name: 'password',
placeholder: '영어/숫자/특수문자 조합 가능(8~20자)',
value: password,
length: '20',
},
{
title: '비밀번호 확인',
type: 'password',
name: 'confirmPassword',
placeholder: '비밀번호 확인',
value: confirmPassword,
length: '20',
},
];
return (
<>
<FormBox>
<AccountTitle>회원가입</AccountTitle>
<FormWrapper $flow="column" onSubmit={doubleSubmitFlag || handleSubmit(onSubmitData)}>
{AccountRegistData.map((data, index) => (
<AccountInputItem
key={index}
title={data.title}
type={data.type}
name={data.name}
placeholder={data.placeholder}
alertText={data.alertText}
value={data.value}
onChange={onChange}
register={register}
errors={errors}
length={data.length && data.length}
/>
))}
<JoinBtnWrapper $justify="space-between" $gap="14px">
<Button
theme="line"
size="large"
text="취소"
width="100%"
handleClick={e => {
e.preventDefault();
navigate('/');
}}
bordercolor="#e4e4e4"
/>
<Button
errorMessage={doubleSubmitFlag === true}
size="large"
text="가입하기"
type="submit"
width="100%"
theme={!values.confirmPassword || errors.username || errors.userid || errors.password || errors.confirmPassword ? 'disable' : 'primary'}
/>
</JoinBtnWrapper>
</FormWrapper>
</FormBox>
<Modal $view={stateModal} min="440px" $padding="40px">
<BtnWrapper $justify="flex-end">
<ButtonClose onClick={handleModal} />
</BtnWrapper>
<ModalText>{submitText}</ModalText>
<Button text="확인" theme="primary" size="large" width="100%" handleClick={handleModal} />
</Modal>
</>
);
};
export default AccountRegist;
const JoinBtnWrapper = styled(BtnWrapper)`
margin-top: 25px;
justify-content: space-between;
gap: 14px;
`;
const ModalText = styled.div`
font-size: 20px;
line-height: 30px;
font-weight: 600;
padding: 10px 0 40px;
`;
const ButtonClose = styled.button`
width: 16px;
height: 16px;
background: url(${CloseUrl}) 50% 50% no-repeat;
`;

View File

@@ -0,0 +1,111 @@
import { useState, useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
import { useForm } from 'react-hook-form';
import { yupResolver } from '@hookform/resolvers/yup';
import { RegistValidation } from './RegistValidation';
import { FormBox, AccountTitle, FormWrapper, BtnWrapper } from '../../styles/Components';
import { AccountInputItem } from '../../components/account';
import Button from '../../components/common/button/Button';
import styled from 'styled-components';
const PasswordReset = () => {
const navigate = useNavigate();
const [stateModal, setStateModal] = useState('hidden');
const [submitText, setSubmitText] = useState('');
const [doubleSubmitFlag, setDoubleSubmitFlag] = useState(false);
const [isSuccess, setIsSuccess] = useState(false)
const {
register,
formState: { errors },
watch,
handleSubmit,
} = useForm({
resolver: yupResolver(RegistValidation),
mode: 'onChange',
});
const [form, setForm] = useState({
userid: '',
username: ''
});
const { userid, username } = form;
const onChange = e => {
const nextForm = {
...form,
[e.target.name]: e.target.value,
};
setForm(nextForm);
};
const AccountRegistData = [
{
title: '이름',
type: 'text',
name: 'username',
placeholder: '한글, 영문 입력 가능',
value: username,
},
{
title: 'ID(메일주소)',
type: 'text',
name: 'userid',
placeholder: '가입한 메일주소를 입력해주세요.(sample@lotte.net)',
value: userid,
}
];
return (
<>
<FormBox>
<AccountTitle>비밀번호 찾기</AccountTitle>
{AccountRegistData.map((data, index) => (
<AccountInputItem
key={index}
title={data.title}
type={data.type}
name={data.name}
placeholder={data.placeholder}
alertText={data.alertText}
value={data.value}
onChange={onChange}
register={register}
errors={errors}
length={data.length && data.length}
/>
))}
<PwdBtnWrapper $justify="space-between" $gap="14px">
<Button
theme="line"
size="large"
text="취소"
width="100%"
handleClick={e => {
e.preventDefault();
navigate('/');
}}
bordercolor="#e4e4e4"
/>
<Button
errorMessage={doubleSubmitFlag === true}
size="large"
text="비밀번호 찾기"
type="submit"
width="100%"
theme={errors.username || errors.userid || errors.password || errors.confirmPassword ? 'disable' : 'primary'}
/>
</PwdBtnWrapper>
</FormBox>
</>
);
}
export default PasswordReset;
const PwdBtnWrapper = styled(BtnWrapper)`
margin-top: 25px;
justify-content: space-between;
gap: 14px;
`;

View File

@@ -0,0 +1,11 @@
import * as yup from "yup";
export const PasswordValidation = yup.object({
newPassword: yup.string()
.min(8, '8~20자로 입력하십시오.')
.max(20, '8~20자로 입력하십시오.')
.required('비밀번호를 입력해주세요.'),
passwordConfirm: yup.string()
.oneOf([yup.ref('newPassword'), null], '비밀번호가 일치하지 않습니다.')
.required('비밀번호를 한번 더 입력해주세요.'),
});

View File

@@ -0,0 +1,17 @@
import * as yup from 'yup';
export const RegistValidation = yup.object({
username: yup.string().matches(/^[가-힣a-zA-Z]+$/, '한글, 영문만 입력이 가능합니다.'),
userid: yup.string().matches(/^[0-9a-zA-Z]([-_.]?[0-9a-zA-Z])*@[0-9a-zA-Z]([-_.]?[0-9a-zA-Z])*.[a-zA-Z]{2,3}$/i, '메일주소가 올바르지 않습니다.'),
password: yup
.string()
.min(8, '8~20자로 입력하십시오.')
.max(20, '8~20자로 입력하십시오.')
.oneOf([yup.ref('password'), null], '현재 비밀번호가 일치하지 않습니다.'),
confirmPassword: yup
.string()
.min(8, '8~20자로 입력하십시오.')
.max(20, '8~20자로 입력하십시오.')
.oneOf([yup.ref('password'), null], '비밀번호가 일치하지 않습니다.')
.required('비밀번호를 한번 더 입력해주세요.'),
});

View File

@@ -0,0 +1,3 @@
export { default as AccountRegist } from './AccountRegist';
export { default as AccountEdit } from './AccountEdit';
export { default as PasswordReset } from './PasswordReset';

View File

@@ -0,0 +1,128 @@
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/DataManage/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

@@ -0,0 +1,111 @@
import { styled } from 'styled-components';
import { Fragment, useState } from 'react';
import { Title, TableStyle, ButtonClose, ModalText, BtnWrapper } from '../../styles/Components';
import UserSearchBar from '../../components/DataManage/UserSearchBar';
import Modal from '../../components/common/modal/Modal';
import Button from '../../components/common/button/Button';
import { useNavigate } from 'react-router-dom';
import { authList } from '../../store/authList';
import { useRecoilValue } from 'recoil';
const CryptView = () => {
const navigate = useNavigate();
const userInfo = useRecoilValue(authList);
const mokupData = [
{
getDate: '2023-08-06 18:50:03',
getLocation: '유저 거래',
itemName: '빛나는 가죽 장갑',
serialNo: 'Serial_number',
itemCode: 'Item_code',
tradeKey: 'User_trade_key',
},
];
return (
<>
{userInfo.auth_list && !userInfo.auth_list.some(auth => auth.id === 15) ? (
<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>
<UserSearchBar />
<TableWrapper>
<TableStyle>
<thead>
<tr>
<th width="170">획득일자</th>
<th width="170">획득처</th>
<th width="200">아이템명</th>
<th width="250">시리얼 넘버</th>
<th width="250">고유 코드</th>
<th width="250">거래 key</th>
</tr>
</thead>
<tbody>
{mokupData.map((data, index) => (
<Fragment key={index}>
<tr>
<td>{new Date(data.getDate).toLocaleString()}</td>
<td>{data.getLocation}</td>
<td>{data.itemName}</td>
<td>{data.serialNo}</td>
<td>{data.itemCode}</td>
<td>{data.tradeKey}</td>
</tr>
</Fragment>
))}
</tbody>
</TableStyle>
</TableWrapper>
</>
)}
</>
);
};
export default CryptView;
const TableWrapper = styled.div`
max-height: calc(100vh - 287px);
overflow: auto;
border-top: 1px solid #000;
&::-webkit-scrollbar {
width: 4px;
height: 4px;
}
&::-webkit-scrollbar-thumb {
background: #666666;
}
&::-webkit-scrollbar-track {
background: #d9d9d9;
}
thead {
th {
position: sticky;
top: 0;
z-index: 10;
}
}
${TableStyle} {
th {
position: sticky;
top: 0;
}
}
`;

View File

@@ -0,0 +1,389 @@
import { Fragment, useState } from 'react';
import { Link } from 'react-router-dom';
import Button from '../../components/common/button/Button';
import Pagination from '../../components/common/Pagination/Pagination';
import DatePicker, { registerLocale } from 'react-datepicker';
import { ko } from 'date-fns/esm/locale';
import 'react-datepicker/dist/react-datepicker.css';
import { getMonth, getYear } from 'date-fns';
import range from 'lodash/range';
import { Title, SelectInput, BtnWrapper, TableStyle, TableInfo, ListCount, ListOption, ButtonClose, ModalText } from '../../styles/Components';
import { styled } from 'styled-components';
import ItemLogSearchBar from '../../components/DataManage/ItemLogSearchBar';
import GoodsLogSearchBar from '../../components/DataManage/CreditLogSearchBar';
import TradeLogSerchBar from '../../components/DataManage/TradeLogSearchBar';
import Modal from '../../components/common/modal/Modal';
import { useNavigate } from 'react-router-dom';
import { authList } from '../../store/authList';
import { useRecoilValue } from 'recoil';
registerLocale('ko', ko);
const ItemLogContent = () => {
const mokupData = [
{
date: '2023-08-05 12:11:32',
name: '칼리버스',
id: '16CD2ECD-4798-46CE-9B6B-F952CF11F196',
action: '획득',
route: '아이템 제작',
itemName: 'Item_name',
serialNumber: 'Serial_number',
itemCode: 'Item_code',
count: 1,
key: 'User_trade_key',
},
];
return (
<>
<ItemLogSearchBar />
<TableInfo>
<ListCount> : 117 / 000 </ListCount>
<ListOption>
<SelectInput name="" id="" className="input-select">
<option value="up">오름차순</option>
<option value="down">내림차순</option>
</SelectInput>
<SelectInput name="" id="" className="input-select">
<option value="up">50</option>
<option value="down">100</option>
</SelectInput>
<Button theme="line" text="엑셀 다운로드" />
</ListOption>
</TableInfo>
<TableWrapper>
<TableStyle>
<caption></caption>
<thead>
<tr>
<th width="150">일자</th>
<th width="200">아바타명</th>
<th width="300">GUID</th>
<th width="100">액션</th>
<th width="150">획득/소진경로</th>
<th width="200">아이템명</th>
<th width="200">시리얼 넘버</th>
<th width="200">고유코드</th>
<th width="80">개수</th>
<th width="300">거래 key</th>
</tr>
</thead>
<tbody>
{mokupData.map((data, index) => (
<Fragment key={index}>
<tr>
<td>{new Date(data.date).toLocaleString()}</td>
<td>{data.name}</td>
<td>{data.id}</td>
<td>{data.action}</td>
<td>{data.route}</td>
<td>{data.itemName}e</td>
<td>{data.serialNumber}</td>
<td>{data.itemCode}</td>
<td>{data.count}</td>
<td>{data.key}</td>
</tr>
</Fragment>
))}
</tbody>
</TableStyle>
</TableWrapper>
<Pagination />
</>
);
};
const GoodsLogContent = () => {
const mokupData = [
{
date: '2023-08-05 12:11:32',
name: '홍길동',
id: '16CD2ECD-4798-46CE-9B6B-F952CF11F196',
gold: '99800',
blue: '400',
black: '500',
red: '500',
action: '소진',
location: '유저 거래',
goldchange: '-',
bluechange: '-100',
blackchange: '-',
redchange: '-',
key: 'User_trade_key',
},
];
return (
<>
<GoodsLogSearchBar />
<TableInfo>
<ListCount> : 117 / 000 </ListCount>
<ListOption>
<SelectInput name="" id="" className="input-select">
<option value="up">오름차순</option>
<option value="down">내림차순</option>
</SelectInput>
<SelectInput name="" id="" className="input-select">
<option value="up">50</option>
<option value="down">100</option>
</SelectInput>
<Button theme="line" text="엑셀 다운로드" />
</ListOption>
</TableInfo>
<TableWrapper>
<TableStyle>
<caption></caption>
<thead>
<tr>
<th width="150">일자</th>
<th width="200">아바타명</th>
<th width="300">GUID</th>
<th width="100">
(Total)
<br />
골드
</th>
<th width="100">
(Total)
<br />
사파이어
</th>
<th width="100">
(Total)
<br />
칼리움
</th>
<th width="100">
(Total)
<br />
오닉시움
</th>
<th width="80">액션</th>
<th width="100">획득 / 소진처</th>
<th width="100">
(변화량)
<br />
골드
</th>
<th width="100">
(변화량)
<br />
사파이어
</th>
<th width="100">
(변화량)
<br />
칼리움
</th>
<th width="100">
(변화량)
<br />
오닉시움
</th>
<th width="300">거래 Key</th>
</tr>
</thead>
<tbody>
{mokupData.map((data, index) => (
<Fragment key={index}>
<tr>
<td>{new Date(data.date).toLocaleString()}</td>
<td>{data.name}</td>
<td>{data.id}</td>
<td>{data.gold}</td>
<td>{data.blue}</td>
<td>{data.black}</td>
<td>{data.red}</td>
<td>{data.action}</td>
<td>{data.location}</td>
<td>{data.goldchange}</td>
{/* 변화량 0보다 작을 경우 StateDecrease 적용 */}
<td>
<StateDecrease>{data.bluechange}</StateDecrease>
</td>
<td>{data.blackchange}</td>
<td>{data.redchange}</td>
<td>{data.key}</td>
</tr>
</Fragment>
))}
</tbody>
</TableStyle>
</TableWrapper>
<Pagination />
</>
);
};
const TradeLogContent = () => {
const mokupData = [
{
date: '2023-08-05 12:11:32',
name: '홍길동',
trader: '칼리버스',
id: '16CD2ECD-4798-46CE-9B6B-F952CF11F196',
key: 'User_trade_key',
},
];
return (
<>
<TradeLogSerchBar />
<TableInfo>
<ListCount>
: 117 / <span>000</span>
</ListCount>
<ListOption>
<SelectInput name="" id="" className="input-select">
<option value="up">오름차순</option>
<option value="down">내림차순</option>
</SelectInput>
<SelectInput name="" id="" className="input-select">
<option value="up">50</option>
<option value="down">100</option>
</SelectInput>
<Button theme="line" text="엑셀 다운로드" />
</ListOption>
</TableInfo>
<TableWrapper>
<TableStyle>
<caption></caption>
<thead>
<tr>
<th width="150">일자</th>
<th width="150">조회 아바타명</th>
<th width="150">거래 대상 아바타명</th>
<th width="300">거래 대상 GUID</th>
<th width="300">거래 Key</th>
</tr>
</thead>
<tbody>
{mokupData.map((data, index) => (
<Fragment key={index}>
<tr>
<td>{new Date(data.date).toLocaleString()}</td>
<td>{data.name}</td>
<td>{data.trader}</td>
<td>{data.id}</td>
<td>{data.key}</td>
</tr>
</Fragment>
))}
</tbody>
</TableStyle>
</TableWrapper>
<Pagination />
</>
);
};
const GameLogView = () => {
const navigate = useNavigate();
const userInfo = useRecoilValue(authList);
const [activeTab, setActiveTab] = useState('itemlog');
const handleTab = (e, content) => {
e.preventDefault();
setActiveTab(content);
};
return (
<>
{userInfo.auth_list && !userInfo.auth_list.some(auth => auth.id === 14) ? (
<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>
<TabWrapper>
<li>
<TabItem $state={activeTab === 'itemlog' ? 'active' : 'none'} onClick={e => handleTab(e, 'itemlog')}>
아이템 로그
</TabItem>
</li>
<li>
<TabItem $state={activeTab === 'goodslog' ? 'active' : 'none'} onClick={e => handleTab(e, 'goodslog')}>
재화 로그
</TabItem>
</li>
<li>
<TabItem $state={activeTab === 'tradelog' ? 'active' : 'none'} onClick={e => handleTab(e, 'tradelog')}>
거래 로그
</TabItem>
</li>
</TabWrapper>
{activeTab === 'itemlog' && <ItemLogContent />}
{activeTab === 'goodslog' && <GoodsLogContent />}
{activeTab === 'tradelog' && <TradeLogContent />}
</>
)}
</>
);
};
export default GameLogView;
const TabItem = styled(Link)`
display: inline-flex;
width: 120px;
height: 30px;
justify-content: center;
align-items: center;
background: #f9f9f9;
border-left: 1px solid #d9d9d9;
&:hover {
background: #888;
color: #fff;
}
${props =>
props.$state === 'active' &&
`
background: #888;
color: #fff;`}
`;
const TabWrapper = styled.ul`
display: flex;
li:first-child {
${TabItem} {
border-left: 0;
}
}
`;
const TableWrapper = styled.div`
width: 100%;
min-width: 680px;
overflow: auto;
&::-webkit-scrollbar {
height: 4px;
}
&::-webkit-scrollbar-thumb {
background: #666666;
}
&::-webkit-scrollbar-track {
background: #d9d9d9;
}
${TableStyle} {
width: 100%;
min-width: max-content;
}
`;
const StateDecrease = styled.span`
color: #d60000;
`;

View File

@@ -0,0 +1,238 @@
import { styled } from 'styled-components';
import { Link } from 'react-router-dom';
import { Fragment, useRef, useState } from 'react';
import { Title, TableStyle, BtnWrapper, ButtonClose, ModalText, FormWrapper } from '../../styles/Components';
import LandSearchBar from '../../components/DataManage/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';
import { useDataFetch, useModal, useTable, withAuth } from '../../utils/hook';
import { authType, landAuctionStatusType } from '../../assets/data';
import { useLandAuctionSearch } from '../../components/ServiceManage/searchBar/LandAuctionSearchBar';
import { INITIAL_PAGE_SIZE } from '../../assets/data/adminConstants';
import { LandAuctionDelete, LandAuctionDetailView } from '../../apis';
import { convertKTC, timeDiffMinute } from '../../utils';
import { useTranslation } from 'react-i18next';
import { ExcelDownButton, ViewTableInfo } from '../../components/common';
import { LandAuctionSearchBar } from '../../components/ServiceManage';
const LandView = () => {
const token = sessionStorage.getItem('token');
const navigate = useNavigate();
const userInfo = useRecoilValue(authList);
const { t } = useTranslation();
const tableRef = useRef(null);
const [detailData, setDetailData] = useState({});
const {
modalState,
handleModalView,
handleModalClose
} = useModal({
detail: 'hidden',
deleteConfirm: 'hidden',
deleteComplete: 'hidden'
});
const [alertMsg, setAlertMsg] = useState('');
const [modalType, setModalType] = useState('regist');
const {
searchParams,
data: dataList,
handleSearch,
handleReset,
handlePageChange,
handlePageSizeChange,
handleOrderByChange,
updateSearchParams
} = useLandAuctionSearch(token, INITIAL_PAGE_SIZE);
const {
selectedRows,
handleSelectRow,
isRowSelected
} = useTable(dataList?.auction_list || [], {mode: 'single'});
const {
data: landData
} = useDataFetch(() => LandView(token), [token]);
const handleModalSubmit = async (type, param = null) => {
switch (type) {
// case "regist":
// setModalType('regist');
// handleModalView('detail');
// break;
// case "detail":
// await LandAuctionDetailView(token, param).then(data => {
// setDetailData(data.auction_detail);
// setModalType('modify');
// handleModalView('detail');
// });
// break;
// case "delete":
// const date_check = selectedRows.every(row => {
// const timeDiff = timeDiffMinute(convertKTC(row.auction_start_dt), (new Date));
// return timeDiff < 3;
// });
// if(date_check){
// setAlertMsg(t('LAND_AUCTION_DELETE_DATE_WARNING'));
// return;
// }
// if(selectedRows[0].status === landAuctionStatusType.auction_start || selectedRows[0].status === landAuctionStatusType.stl_end){
// setAlertMsg(t('LAND_AUCTION_DELETE_STATUS_WARNING'));
// return;
// }
// handleModalView('deleteConfirm');
// break;
// case "deleteConfirm":
// let list = [];
// let isChecked = false;
//
// selectedRows.map(data => {
// // const row = dataList.list.find(row => row.id === Number(data.id));
// // if(row.status !== commonStatus.wait) isChecked = true;
// list.push({
// id: data.id,
// });
// });
//
// if(isChecked) {
// setAlertMsg(t('LAND_AUCTION_WARNING_DELETE'))
// handleModalClose('deleteConfirm');
// return;
// }
//
// await LandAuctionDelete(token, list).then(data => {
// handleModalClose('deleteConfirm');
// if(data.result === "SUCCESS") {
// handleModalView('deleteComplete');
// }else{
// setAlertMsg(t('DELETE_FAIL'));
// }
// }).catch(reason => {
// setAlertMsg(t('API_FAIL'));
// });
//
// break;
// case "deleteComplete":
// handleModalClose('deleteComplete');
// // fetchData(option);
// window.location.reload();
// break;
// case "warning":
// setAlertMsg('')
// break;
}
}
return (
<>
<Title>랜드 조회</Title>
<FormWrapper>
<LandAuctionSearchBar
searchParams={searchParams}
onSearch={(newParams, executeSearch = true) => {
if (executeSearch) {
handleSearch(newParams);
} else {
updateSearchParams(newParams);
}
}}
onReset={handleReset}
/>
</FormWrapper>
<ViewTableInfo total={dataList?.total} total_all={dataList?.total_all} handleOrderBy={handleOrderByChange} handlePageSize={handlePageSizeChange}>
<ExcelDownButton tableRef={tableRef} fileName={t('FILE_LAND_AUCTION')} />
{userInfo.auth_list?.some(auth => auth.id === authType.landDelete) && (
<Button theme={selectedRows.length === 0 ? 'disable' : 'line'} text="선택 삭제" handleClick={() => handleModalSubmit('delete')} />
)}
{userInfo.auth_list?.some(auth => auth.id === authType.landUpdate) && (
<Button
theme={selectedRows.length === 0 ? 'disable' : 'line'}
text="소유권 변경"
type="button"
handleClick={e => handleModalSubmit('regist')}
/>
)}
</ViewTableInfo>
<TableWrapper>
<TableStyle ref={tableRef}>
<thead>
<tr>
<th width="150">랜드 ID</th>
<th>랜드 이름</th>
<th>랜드 상태</th>
<th>카테고리</th>
<th>랜드 크기</th>
<th>보유시작일</th>
<th width="150">상세보기</th>
</tr>
</thead>
<tbody>
{dataList.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 withAuth(authType.landRead)(LandView);
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

@@ -0,0 +1,162 @@
import { useState, Fragment } from 'react';
import { Title, BtnWrapper, ButtonClose, ModalText } from '../../styles/Components';
import styled from 'styled-components';
import UserViewSearchBar from '../../components/DataManage/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 Modal from '../../components/common/modal/Modal';
import Button from '../../components/common/button/Button';
import { useNavigate } from 'react-router-dom';
import { authList } from '../../store/authList';
import { useRecoilValue } from 'recoil';
import AuthModal from '../../components/common/modal/AuthModal';
import { authType, TabList } from '../../assets/data';
const UserView = () => {
const navigate = useNavigate();
const userInfo = useRecoilValue(authList);
const [infoView, setInfoView] = useState('none');
const [activeContent, setActiveContent] = useState('기본정보');
const [resultData, setResultData] = useState([]);
const handleTab = title => {
setActiveContent(title);
};
return (
<>
{userInfo.auth_list && !userInfo.auth_list.some(auth => auth.id === authType.userSearchRead) ? (
<AuthModal />
) : (
<>
<Title>유저조회</Title>
<UserViewSearchBar setInfoView={setInfoView} resultData={resultData} handleTab={handleTab} setResultData={setResultData} />
<UserWrapper display={infoView}>
<TabScroll>
<UserTabWrapper>
{TabList.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>
</>
)}
</>
);
};
export default 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 TabScroll = styled.div`
width: 100%;
overflow: auto;
`;
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;
}
`;
const UserDefaultTable = styled.table`
border: 1px solid #e8eaec;
border-top: 1px solid #000;
font-size: 14px;
margin-bottom: 40px;
th {
background: #efefef;
font-weight: 700;
}
th,
td {
padding: 12px;
text-align: center;
border-left: 1px solid #e8eaec;
vertical-align: middle;
}
td {
background: #fff;
border-bottom: 1px solid #e8eaec;
word-break: break-all;
}
button {
height: 24px;
font-size: 13px;
}
`;

View File

@@ -0,0 +1,4 @@
export { default as UserView } from './UserView';
export { default as LandView } from './LandView';
export { default as GameLogView } from './GameLogView';
export { default as CryptView } from './CryptView';

View File

@@ -0,0 +1,114 @@
import { useState } from 'react';
import { Link } from 'react-router-dom';
import Button from '../../components/common/button/Button';
import { Title, BtnWrapper, ButtonClose, ModalText } from '../../styles/Components';
import { styled } from 'styled-components';
import Modal from '../../components/common/modal/Modal';
import { useNavigate } from 'react-router-dom';
import { authList } from '../../store/authList';
import { useRecoilValue } from 'recoil';
import CreditContent from '../../components/IndexManage/CreditContent';
import VBPContent from '../../components/IndexManage/VBPContent';
import ItemContent from '../../components/IndexManage/ItemContent';
import InstanceContent from '../../components/IndexManage/InstanceContent';
import DecoContent from '../../components/IndexManage/DecoContent';
const EconomicIndex = () => {
const navigate = useNavigate();
const userInfo = useRecoilValue(authList);
const [activeTab, setActiveTab] = useState('credit');
const handleTab = (e, content) => {
e.preventDefault();
setActiveTab(content);
};
return (
<>
{userInfo.auth_list && !userInfo.auth_list.some(auth => auth.id === 10) ? (
<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>
<TabWrapper>
<li>
<TabItem $state={activeTab === 'credit' ? 'active' : 'none'} onClick={e => handleTab(e, 'credit')}>
재화
</TabItem>
</li>
<li>
<TabItem $state={activeTab === 'vbp' ? 'active' : 'none'} onClick={e => handleTab(e, 'vbp')}>
VBP
</TabItem>
</li>
<li>
<TabItem $state={activeTab === 'item' ? 'active' : 'none'} onClick={e => handleTab(e, 'item')}>
아이템
</TabItem>
</li>
<li>
<TabItem $state={activeTab === 'instance' ? 'active' : 'none'} onClick={e => handleTab(e, 'instance')}>
인스턴스
</TabItem>
</li>
<li>
<TabItem $state={activeTab === 'deco' ? 'active' : 'none'} onClick={e => handleTab(e, 'deco')}>
의상 / 타투
</TabItem>
</li>
</TabWrapper>
{activeTab === 'credit' && <CreditContent />}
{activeTab === 'vbp' && <VBPContent />}
{activeTab === 'item' && <ItemContent />}
{activeTab === 'instance' && <InstanceContent />}
{activeTab === 'deco' && <DecoContent />}
</>
)}
</>
);
};
export default EconomicIndex;
const TabItem = styled(Link)`
display: inline-flex;
width: 120px;
height: 30px;
justify-content: center;
align-items: center;
background: #f9f9f9;
border-left: 1px solid #d9d9d9;
&:hover {
background: #888;
color: #fff;
}
${props =>
props.$state === 'active' &&
`background: #888;
color: #fff;`}
`;
const TabWrapper = styled.ul`
display: flex;
li:first-child {
${TabItem} {
border-left: 0;
}
}
`;

View File

@@ -0,0 +1,103 @@
import { Fragment, useEffect, useState } from 'react';
import { styled } from 'styled-components';
import { Link, useNavigate } from 'react-router-dom';
import { useRecoilValue } from 'recoil';
import Modal from '../../components/common/modal/Modal';
import Button from '../../components/common/button/Button';
import { Title, BtnWrapper, ButtonClose, ModalText } from '../../styles/Components';
import { authList } from '../../store/authList';
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';
const UserIndex = () => {
const token = sessionStorage.getItem('token');
const navigate = useNavigate();
const userInfo = useRecoilValue(authList);
const [activeTab, setActiveTab] = useState('이용자 지표');
const handleTab = (e, content) => {
// e.preventDefault();
setActiveTab(content);
};
const TabList = [
{ title: '이용자 지표' },
// { title: 'Retention' },
// { title: 'Segment' },
// { title: '플레이타임' },
// { title: 'DAU' },
// { title: '메달' },
];
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>
{/*{activeTab === 'DAU' && <DailyActiveUserContent />}*/}
{activeTab === '이용자 지표' && <UserContent />}
{activeTab === 'Retention' && <RetentionContent />}
{activeTab === 'Segment' && <SegmentContent />}
{activeTab === '플레이타임' && <PlayTimeContent />}
{/*{activeTab === '메달' && <DailyMedalContent />}*/}
</>
)}
</>
);
};
export default UserIndex;
const TabItem = styled(Link)`
display: inline-flex;
width: 120px;
height: 30px;
justify-content: center;
align-items: center;
background: #f9f9f9;
border-left: 1px solid #d9d9d9;
&:hover {
background: #888;
color: #fff;
}
${props =>
props.$state === 'active' &&
`
background: #888;
color: #fff;`}
`;
const TabWrapper = styled.ul`
display: flex;
li:first-child {
${TabItem} {
border-left: 0;
}
}
`;

View File

@@ -0,0 +1,2 @@
export { default as UserIndex } from './UserIndex';
export { default as EconomicIndex } from './EconomicIndex';

77
src/pages/Login/Login.js Normal file
View File

@@ -0,0 +1,77 @@
import styled from 'styled-components';
import { Link, Navigate } from 'react-router-dom';
import { LoginForm, LoginInfo } from '../../components/login';
import LogoUrl from '../../assets/img/logo.png';
const Login = () => {
const isToken = sessionStorage.getItem('token') ? true : false;
return (
<>
{isToken ? (
<Navigate to="/main" replace={true} />
) : (
<FormBox>
<Logo src={LogoUrl} alt="caliverse" />
<LoginTitle>ADMIN LOGIN</LoginTitle>
<LoginForm />
<JoinButton to="/account/regist">회원가입</JoinButton>
{/* <Divider>|</Divider>
<PwdButton to="/account/pwdreset">비밀번호 찾기</PwdButton> */}
<LoginInfo />
</FormBox>
)}
</>
);
};
export default Login;
const Divider = styled.span`
margin: 0 8px;
color: RGB(217,217,217);
padding: 4px 0;
`;
const PwdButton = styled(Link)`
text-decoration: underline;
font-size: 14px;
margin: 28px 0;
border-radius: 0;
color: #8d8d8d;
`;
const JoinButton = styled(Link)`
text-decoration: underline;
font-size: 14px;
margin: 28px 0;
border-radius: 0;
color: #8d8d8d;
`;
const FormBox = styled.div`
background: #fff;
width: 486px;
padding: 75px 45px;
text-align: center;
border-radius: 30px;
`;
const Logo = styled.img`
display: block;
margin: auto;
`;
const LoginTitle = styled.span`
background: #ececec;
border-radius: 50px;
color: #adadad;
margin-top: 40px;
font-size: 12px;
display: inline-block;
width: 110px;
padding: 5px 10px;
letter-spacing: 1px;
`;

1
src/pages/Login/index.js Normal file
View File

@@ -0,0 +1 @@
export { default as Login } from './Login';

182
src/pages/LoginFail.js Normal file
View File

@@ -0,0 +1,182 @@
import { useState } from 'react';
import { Link } from 'react-router-dom';
import Button from '../components/common/button/Button';
import styled from 'styled-components';
import { TextInput, BtnWrapper, ButtonClose, Title } from '../styles/Components';
import LogoUrl from '../assets/img/logo.png';
import Modal from '../components/common/modal/Modal';
const LoginFail = () => {
const [userId, setUserId] = useState('');
const [userPw, setUserPw] = useState('');
const [stateModal, setStateModal] = useState('hidden');
const handleModal = () => {
if (stateModal === 'hidden') {
setStateModal('view');
} else {
setStateModal('hidden');
}
};
return (
<>
<FormBox>
<Logo src={LogoUrl} alt="caliverse" />
<LoginTitle>ADMIN LOGIN</LoginTitle>
<FormWrapper action="" $flow="column">
<InputWrapper>
<TextInput fontSize="15px" $padding="16px 20px" name="userid" placeholder="ID를 입력하세요(test@test.co.kr)" value={userId} onChange={e => setUserId(e.target.value)} />
<TextInput fontSize="15px" $padding="16px 20px" name="password" id="userpw" placeholder="비밀번호를 입력하세요" value={userPw} onChange={e => setUserPw(e.target.value)} />
</InputWrapper>
<Button
type="submit"
theme="primary"
size="large"
text="로그인"
width="100%"
handleClick={e => {
e.preventDefault();
//window.location.href = '/account/edit';
handleModal();
}}
/>
</FormWrapper>
<JoinButton to="/account/regist">회원가입</JoinButton>
<LoginInfo>
<LoginInfoItem>- 관리자 승인된 계정으로 접속하실 있습니다.</LoginInfoItem>
<LoginInfoItem>
- 로그인 오류 비밀번호 재설정은 <Link to="mailto:admin@caliverse.co.kr">admin@caliverse.co.kr</Link> .
</LoginInfoItem>
</LoginInfo>
</FormBox>
<Modal $view={stateModal} min="440px" $padding="40px">
<BtnWrapper $justify="flex-end">
<ButtonClose onClick={handleModal} />
</BtnWrapper>
<Title $align="center">로그인 실패</Title>
<NoticeSummary $maxwidth="400px">
<table>
<tbody>
<tr>
<th>사유</th>
<td>비밀번호 기간 만료</td>
</tr>
<tr>
<th>만료일</th>
<td>2023-07-24</td>
</tr>
</tbody>
</table>
</NoticeSummary>
<NoticeDetail $align="center">
비밀번호 기간 갱신은 관리자 메일을 통해
<br />
문의해주시기 바랍니다.
<br />
<br />
관리자 문의 : caliverse@caliverse.com
</NoticeDetail>
<Button
text="확인"
theme="line"
handleClick={e => {
e.preventDefault();
window.location.href = '/account/edit';
}}
/>
</Modal>
</>
);
};
export default LoginFail;
const JoinButton = styled(Link)`
text-decoration: underline;
font-size: 14px;
margin: 28px 0;
border-radius: 0;
`;
const FormBox = styled.div`
background: #fff;
width: 486px;
padding: 75px 45px;
text-align: center;
border-radius: 30px;
color: #8d8d8d;
`;
const Logo = styled.img`
display: block;
margin: auto;
`;
const LoginTitle = styled.span`
background: #ececec;
border-radius: 50px;
color: #adadad;
margin-top: 40px;
font-size: 12px;
display: inline-block;
width: 110px;
padding: 5px 10px;
letter-spacing: 1px;
`;
const FormWrapper = styled.form`
margin-top: 40px;
`;
const InputWrapper = styled.div`
display: flex;
flex-flow: column;
gap: 6px;
margin-bottom: 20px;
`;
const LoginInfo = styled.ul`
text-align: left;
`;
const LoginInfoItem = styled.li`
width: 100%;
font-size: 14px;
line-height: 1.6;
word-break: keep-all;
padding-left: 10px;
text-indent: -10px;
a {
color: #333;
font-weight: 500;
display: inline-block;
word-break: break-all;
border-radius: 0;
text-indent: 0;
}
`;
const NoticeSummary = styled.div`
border-top: 1px solid #000;
border-bottom: 1px solid #000;
padding: 20px 40px;
margin-bottom: 20px;
max-width: ${props => props.$maxwidth || 'auto'};
table {
th {
text-align: left;
font-weight: 600;
}
th,
td {
padding: 8px 0;
}
}
`;
const NoticeDetail = styled.div`
background: #f6f6f6;
padding: 30px 20px;
margin-bottom: 20px;
line-height: 2;
text-align: ${props => props.$align || 'left'};
`;

100
src/pages/Main.js Normal file
View File

@@ -0,0 +1,100 @@
import { styled } from 'styled-components';
import { useState, useEffect } from 'react';
import MainLogo from '../assets/img/logo-main.png';
import PasswordAlertModal from '../components/account/PasswordAlertModal';
import { AuthInfo } from '../apis/Auth';
import { Navigate } from 'react-router-dom';
const Main = () => {
const token = sessionStorage.getItem('token');
const [passwordModal, setPasswordModal] = useState('hidden');
const homeVisited = localStorage.getItem('homeVisited');
useEffect(() => {
if (homeVisited) {
new Date(homeVisited) < new Date() && handleExpiredPassword();
} else {
handleExpiredPassword();
}
}, []); // 비밀번호 만료 알림 모달 날짜 계산
const handleExpiredPassword = async () => {
const today = new Date();
const guideDate = new Date(today);
guideDate.setDate(today.getDate() + 30);
const userInfo = await AuthInfo(token);
userInfo && new Date(userInfo.expiredDt) < new Date(guideDate) && handlePasswordModal();
};
// 비밀번호 재설정 모달창
const handlePasswordModal = () => {
if (passwordModal === 'hidden') {
setPasswordModal('view');
} else {
setPasswordModal('hidden');
}
};
return (
<>
{token ? (
<>
<MainWrapper>
<IntroBox>
<span>Welcome To</span>
<img src={MainLogo} alt="" />
<div>운영툴 관리 메뉴를 선택해주세요</div>
</IntroBox>
</MainWrapper>
{/* 비빌번호 만료 안내 */}
<PasswordAlertModal stateModal={passwordModal} handleModal={handlePasswordModal} />
</>
) : (
<Navigate to="/" replace={true} />
)}
</>
);
};
export default Main;
const MainWrapper = styled.div`
width: 100%;
height: calc(100vh - 58px);
background: rgba(246, 246, 246, 0.5);
display: flex;
align-items: center;
justify-content: center;
position: relative;
&:before {
content: '';
display: block;
width: 400px;
height: 510px;
/* background: #fff; */
position: absolute;
}
`;
const IntroBox = styled.div`
position: relative;
display: flex;
flex-flow: column;
align-items: center;
font-weight: 600;
font-size: 18px;
color: #666;
span {
font-size: 30px;
font-weight: 500;
letter-spacing: 10px;
color: #d9d9d9;
display: inline-block;
margin-bottom: 30px;
}
img {
margin-bottom: 80px;
}
`;

View File

@@ -0,0 +1,281 @@
import { useState, Fragment, useRef } from 'react';
import { useRecoilValue } from 'recoil';
import { useTranslation } from 'react-i18next';
import 'react-datepicker/dist/react-datepicker.css';
import {
BattleConfigView,
BattleEventDelete,
BattleEventDetailView,
BattleEventView,
BattleRewardView,
} from '../../apis/Battle';
import { authList } from '../../store/authList';
import {
authType,
modalTypes,
landAuctionStatusType,
landAuctionStatus,
landSize,
caliumStatus, commonStatus,
} from '../../assets/data';
import { Title, FormWrapper, TableStyle, TableWrapper} from '../../styles/Components';
import {
CheckBox,
Button,
DynamicModal,
Pagination,
ViewTableInfo, ExcelDownButton,
} from '../../components/common';
import { convertKTC, timeDiffMinute } from '../../utils';
import { BattleEventModal, LandAuctionModal, LandAuctionSearchBar } from '../../components/ServiceManage';
import { INITIAL_PAGE_SIZE, INITIAL_PAGE_LIMIT } from '../../assets/data/adminConstants';
import { useDataFetch, useModal, useTable, withAuth } from '../../utils/hook';
import { useLandAuctionSearch } from '../../components/ServiceManage/searchBar/LandAuctionSearchBar';
import { StatusWapper, StatusLabel } from '../../styles/ModuleComponents';
import { battleEventStatus, battleRepeatType } from '../../assets/data/options';
import BattleEventSearchBar, {
useBattleEventSearch,
} from '../../components/ServiceManage/searchBar/BattleEventSearchBar';
import { getTimeOnly } from '../../utils/date';
const BattleEvent = () => {
const token = sessionStorage.getItem('token');
const userInfo = useRecoilValue(authList);
const { t } = useTranslation();
const tableRef = useRef(null);
const [detailData, setDetailData] = useState({});
const {
modalState,
handleModalView,
handleModalClose
} = useModal({
detail: 'hidden',
deleteConfirm: 'hidden',
deleteComplete: 'hidden'
});
const [alertMsg, setAlertMsg] = useState('');
const [modalType, setModalType] = useState('regist');
const {
searchParams,
data: dataList,
handleSearch,
handleReset,
handlePageChange,
handlePageSizeChange,
handleOrderByChange,
updateSearchParams
} = useBattleEventSearch(token, INITIAL_PAGE_SIZE);
const {
selectedRows,
handleSelectRow,
isRowSelected
} = useTable(dataList?.event_list || [], {mode: 'single'});
const {
data: battleConfigData
} = useDataFetch(() => BattleConfigView(token), [token]);
const {
data: battleRewardData
} = useDataFetch(() => BattleRewardView(token), [token]);
const handleModalSubmit = async (type, param = null) => {
switch (type) {
case "regist":
setModalType('regist');
handleModalView('detail');
break;
case "detail":
await BattleEventDetailView(token, param).then(data => {
setDetailData(data.event_detail);
setModalType('modify');
handleModalView('detail');
});
break;
case "delete":
const date_check = selectedRows.every(row => {
const timeDiff = timeDiffMinute(convertKTC(row.auction_start_dt), (new Date));
return timeDiff < 3;
});
if(date_check){
setAlertMsg(t('LAND_AUCTION_DELETE_DATE_WARNING'));
return;
}
if(selectedRows[0].status === landAuctionStatusType.auction_start || selectedRows[0].status === landAuctionStatusType.stl_end){
setAlertMsg(t('LAND_AUCTION_DELETE_STATUS_WARNING'));
return;
}
handleModalView('deleteConfirm');
break;
case "deleteConfirm":
let list = [];
let isChecked = false;
selectedRows.map(data => {
// const row = dataList.list.find(row => row.id === Number(data.id));
// if(row.status !== commonStatus.wait) isChecked = true;
list.push({
id: data.id,
});
});
if(isChecked) {
setAlertMsg(t('LAND_AUCTION_WARNING_DELETE'))
handleModalClose('deleteConfirm');
return;
}
await BattleEventDelete(token, list).then(data => {
handleModalClose('deleteConfirm');
if(data.result === "SUCCESS") {
handleModalView('deleteComplete');
}else if(data.result === "ERROR_AUCTION_STATUS_IMPOSSIBLE"){
setAlertMsg(t('LAND_AUCTION_ERROR_DELETE_STATUS'));
}else{
setAlertMsg(t('DELETE_FAIL'));
}
}).catch(reason => {
setAlertMsg(t('API_FAIL'));
});
break;
case "deleteComplete":
handleModalClose('deleteComplete');
// fetchData(option);
window.location.reload();
break;
case "warning":
setAlertMsg('')
break;
}
}
return (
<>
<Title>전투시스템 타임 스케줄러</Title>
<FormWrapper>
<BattleEventSearchBar
searchParams={searchParams}
onSearch={(newParams, executeSearch = true) => {
if (executeSearch) {
handleSearch(newParams);
} else {
updateSearchParams(newParams);
}
}}
onReset={handleReset}
/>
</FormWrapper>
<ViewTableInfo total={dataList?.total} total_all={dataList?.total_all} handleOrderBy={handleOrderByChange} handlePageSize={handlePageSizeChange}>
<ExcelDownButton tableRef={tableRef} fileName={t('FILE_BATTLE_EVENT')} />
{userInfo.auth_list?.some(auth => auth.id === authType.battleEventDelete) && (
<Button theme={selectedRows.length === 0 ? 'disable' : 'line'} text="이벤트 중단" handleClick={() => handleModalSubmit('delete')} />
)}
{userInfo.auth_list?.some(auth => auth.id === authType.battleEventUpdate) && (
<Button
theme="primary"
text="이벤트 등록"
type="button"
handleClick={e => handleModalSubmit('regist')}
/>
)}
</ViewTableInfo>
<TableWrapper>
<TableStyle ref={tableRef}>
<caption></caption>
<thead>
<tr>
<th width="40"></th>
<th width="90">그룹</th>
<th width="60">이벤트 ID</th>
<th width="150">이벤트명</th>
<th width="80">반복</th>
<th width="120">이벤트 시작시간</th>
<th width="120">이벤트 종료시간</th>
<th width="90">이벤트 상태</th>
<th width="90">라운드 시간</th>
<th width="80">라운드 </th>
<th width="80">핫타임</th>
<th width="200">기간 시작일</th>
<th width="200">기간 종료일</th>
<th width="100">확인 / 수정</th>
<th width="150">히스토리</th>
</tr>
</thead>
<tbody>
{dataList?.event_list?.map(battle => (
<tr key={battle.row_num}>
<td>
<CheckBox name={'select'} id={battle.id}
setData={(e) => handleSelectRow(e, battle)}
checked={isRowSelected(battle.id)} />
</td>
<td>{battle.group_id}</td>
<td>{battle.id}</td>
<td>{battle.event_name}</td>
<StatusWapper>
<StatusLabel $status={battle.repeat_type}>
{battleRepeatType.find(data => data.value === battle.repeat_type).name}
</StatusLabel>
</StatusWapper>
<td>{getTimeOnly(battle.event_start_dt)}</td>
<td>{battle.end_time}</td>
<StatusWapper>
<StatusLabel $status={battle.status}>
{battleEventStatus.find(data => data.value === battle.status).name}
</StatusLabel>
</StatusWapper>
<td>{battle.round_time}</td>
<td>{battle.round_count}</td>
<td>{battle.hot_time}</td>
<td>{convertKTC(battle.event_start_dt)}</td>
<td>{convertKTC(battle.event_end_dt)}</td>
<td>
<Button theme="line" text="상세보기"
handleClick={e => handleModalSubmit('detail', battle.id)} />
</td>
<td>{battle.update_by}</td>
</tr>
))}
</tbody>
</TableStyle>
</TableWrapper>
<Pagination postsPerPage={searchParams.pageSize} totalPosts={dataList?.total_all} setCurrentPage={handlePageChange} currentPage={searchParams.currentPage} pageLimit={INITIAL_PAGE_LIMIT} />
{/*상세*/}
<BattleEventModal modalType={modalType} detailView={modalState.detailModal} handleDetailView={() => handleModalClose('detail')} content={detailData} setDetailData={setDetailData} configData={battleConfigData} rewardData={battleRewardData} />
{/*삭제 확인*/}
<DynamicModal
modalType={modalTypes.confirmOkCancel}
view={modalState.deleteConfirmModal}
handleCancel={() => handleModalClose('deleteConfirm')}
handleSubmit={() => handleModalSubmit('deleteConfirm')}
modalText={t('LAND_AUCTION_SELECT_DELETE')}
/>
{/*삭제 완료*/}
<DynamicModal
modalType={modalTypes.completed}
view={modalState.deleteCompleteModal}
handleSubmit={() => handleModalSubmit('deleteComplete')}
modalText={t('DEL_COMPLETE')}
/>
{/* 경고 모달 */}
<DynamicModal
modalType={modalTypes.completed}
view={alertMsg ? 'view' : 'hidden'}
modalText={alertMsg}
handleSubmit={() => handleModalSubmit('warning')}
/>
</>
)
};
export default withAuth(authType.battleEventRead)(BattleEvent);

View File

@@ -0,0 +1,265 @@
import { Fragment, useState } from 'react';
import { Link } from 'react-router-dom';
import { useEffect } from 'react';
import Button from '../../components/common/button/Button';
import CheckBox from '../../components/common/input/CheckBox';
import Modal from '../../components/common/modal/Modal';
import styled from 'styled-components';
import {
Title,
TableWrapper,
BtnWrapper,
TableInfo,
ListOption,
TableStyle,
ButtonClose,
ModalText,
DetailMessage,
} from '../../styles/Components';
import { NoticeDelete, NoticeDetailView, NoticeListView } from '../../apis/Notice';
import { BoardInfoModal, BoardRegistModal } from '../../components/ServiceManage';
import { authList } from '../../store/authList';
import { useRecoilValue } from 'recoil';
import { useNavigate } from 'react-router-dom';
import { convertKTC } from '../../utils';
import AuthModal from '../../components/common/modal/AuthModal';
import { authType } from '../../assets/data';
const Board = () => {
const navigate = useNavigate();
const token = sessionStorage.getItem('token');
const userInfo = useRecoilValue(authList);
const [isCopyData, setIsCopyData] = useState(false);
const [dataList, setDataList] = useState([]);
const [detailView, setDetailView] = useState('hidden');
const [registView, setRegistView] = useState('hidden');
const [detailData, setDetailData] = useState('');
const [detailId, setDetailId] = useState('');
const [selectedRow, setSelectedRow] = useState([]);
const [deleteModalClose, setDeleteModalClose] = useState('hidden');
const [confirmModalClose, setConfirmModalClose] = useState('hidden');
const message_type = [
{ value: 'CHATTING', name: '채팅 타입' },
{ value: 'CHATTING_TOAST', name: '채팅 + 토스트' },
];
const sendStatus = [
{ value: 'WAIT', name: '대기' },
{ value: 'RUNNING', name: '송출중' },
{ value: 'FINISH', name: '완료' },
{ value: 'FAIL', name: '실패' },
];
const fetchData = async () => {
setDataList(await NoticeListView(token));
};
useEffect(() => {
fetchData();
}, []);
// 체크박스 선택 리스트
const handleSelectCheckBox = e => {
let list = [...selectedRow];
if (e.target.checked) {
list.push(e.target.id);
setSelectedRow(list);
} else {
const filterList = list.filter(data => e.target.id !== data);
setSelectedRow(filterList);
}
};
// 전체 선택 구현
const handleAllSelect = () => {
let list = [];
if (document.getElementById('check-all').checked === true) {
dataList.map((data, index) => {
document.getElementsByName('select')[index].checked = true;
list.push(String(data.id));
});
} else if (document.getElementById('check-all').checked === false) {
for (let i = 0; i < dataList.length; i++) {
dataList.map((data, index) => (document.getElementsByName('select')[index].checked = false));
list = [];
}
}
setSelectedRow(list);
};
// 선택 삭제 함수
const handleSelectedDelete = () => {
let list = [];
selectedRow.map(data =>
list.push({
message_id: data,
}),
);
NoticeDelete(token, list);
handleDeleteModalClose();
handleConfirmeModalClose();
};
// 선택 삭제 모달
const handleDeleteModalClose = () => {
if (selectedRow.length !== 0 && deleteModalClose === 'hidden') {
setDeleteModalClose('view');
} else {
setDeleteModalClose('hidden');
}
};
// 삭제, 승인, 저장 확인 모달창
const handleConfirmeModalClose = () => {
if (confirmModalClose === 'hidden') {
setConfirmModalClose('view');
} else {
setConfirmModalClose('hidden');
window.location.reload();
}
};
const handleDetailModal = async (e, id) => {
setDetailData(await NoticeDetailView(token, id));
setDetailId(id);
e.preventDefault();
setDetailView('view');
};
const handleCountSelectedRow = () => {
return dataList && document.querySelectorAll('input[name="select"]:checked').length === dataList.length;
};
return (
<>
{userInfo.auth_list && !userInfo.auth_list.some(auth => auth.id === authType.inGameRead) ? (
<AuthModal/>
) : (
<>
<Title>인게임 메시지</Title>
<TableInfo>
<ListOption>
{userInfo.auth_list && userInfo.auth_list.some(auth => auth.id === authType.inGameDelete) && (
<Button theme={selectedRow.length === 0 ? 'disable' : 'line'} text="선택 삭제" handleClick={handleDeleteModalClose} />
)}
{userInfo.auth_list && userInfo.auth_list.some(auth => auth.id === authType.inGameUpdate) && (
<Button
theme="primary"
text="메시지 등록"
value="2"
handleClick={e => {
e.preventDefault();
setRegistView('view');
}}
/>
)}
</ListOption>
</TableInfo>
<TableWrapper>
<TableStyle>
<caption></caption>
<thead>
<tr>
<th width="40">
<CheckBox id="check-all" handleCheck={handleAllSelect} checked={handleCountSelectedRow()} />
</th>
<th width="80">번호</th>
<th width="100">채팅 타입</th>
<th width="200">송출 일자</th>
<th>메시지</th>
<th width="80">송출 횟수</th>
<th width="100">송출 상태</th>
<th width="120">등록자</th>
<th width="200">등록자(이메일주소)</th>
<th width="200">등록일자</th>
</tr>
</thead>
<tbody>
{dataList &&
dataList.map(notice => (
<Fragment key={notice.id}>
<tr>
<td>
<CheckBox name={'select'} id={notice.id} setData={e => handleSelectCheckBox(e)} />
</td>
<td>{notice.row_num}</td>
<td>{message_type.map(item => item.value === notice.message_type && item.name)}</td>
<td>{convertKTC(notice.send_dt)}</td>
<td>
<DetailMessage onClick={e => handleDetailModal(e, notice.id)}>
{notice.content.length > 20 ? notice.content.slice(0, 20) + '...' : notice.content || ''}
</DetailMessage>
</td>
<td>
{/*{notice.send_cnt} / {notice.repeat_cnt}*/}
{notice.send_cnt}
</td>
<td>
{sendStatus.map(data => data.value === notice.send_status && data.name)}
</td>
<td>{notice.create_name}</td>
<td>{notice.create_by}</td>
<td>{convertKTC(notice.create_dt)}</td>
</tr>
</Fragment>
))}
</tbody>
</TableStyle>
{/* 인게임 메세지 정보 */}
<BoardInfoModal
detailView={detailView}
setDetailView={setDetailView}
content={detailData}
id={detailId}
setIsCopyData={setIsCopyData}
openRegistModal={setRegistView}
userInfo={userInfo}
/>
{/* 인게임 메세지 등록 */}
<BoardRegistModal registView={registView} setRegistView={setRegistView} copyData={isCopyData ? detailData : ''} setIsCopyData={setIsCopyData} userInfo={userInfo} />
</TableWrapper>
{/* 선택 삭제 모달 */}
<Modal min="440px" $padding="40px" $bgcolor="transparent" $view={deleteModalClose}>
<BtnWrapper $justify="flex-end">
<ButtonClose onClick={handleDeleteModalClose} />
</BtnWrapper>
<ModalText $align="center">
선택된 인게임 메세지를 삭제하시겠습니까?
<br />
삭제 설정 정보가 제거됩니다.
</ModalText>
<BtnWrapper $gap="10px">
<Button text="취소" theme="line" size="large" width="100%" handleClick={handleDeleteModalClose} />
<Button text="확인" theme="primary" type="submit" size="large" width="100%" handleClick={handleSelectedDelete} />
</BtnWrapper>
</Modal>
{/* 확인 모달 */}
<Modal min="440px" $padding="40px" $bgcolor="transparent" $view={confirmModalClose}>
<BtnWrapper $justify="flex-end">
<ButtonClose onClick={handleConfirmeModalClose} />
</BtnWrapper>
<ModalText $align="center">삭제가 완료되었습니다.</ModalText>
<BtnWrapper $gap="10px">
<Button text="확인" theme="primary" type="submit" size="large" width="100%" handleClick={handleConfirmeModalClose} />
</BtnWrapper>
</Modal>
</>
)}
</>
);
};
export default Board;

View File

@@ -0,0 +1,339 @@
import { useState, useEffect, Fragment, memo } from 'react';
import { useNavigate } from 'react-router-dom';
import { useRecoilValue } from 'recoil';
import { useTranslation } from 'react-i18next';
import {
EventDelete,
EventDetailView,
EventView,
} from '../../apis';
import { authList } from '../../store/authList';
import { authType, commonStatus, modalTypes, eventStatus } from '../../assets/data';
import { Title, FormWrapper, TableStyle, MailTitle, TableWrapper, TextInput, InputItem } from '../../styles/Components';
import CheckBox from '../../components/common/input/CheckBox';
import Button from '../../components/common/button/Button';
import EventDetailModal from '../../components/ServiceManage/modal/EventDetailModal';
import Pagination from '../../components/common/Pagination/Pagination';
import 'react-datepicker/dist/react-datepicker.css';
import DynamicModal from '../../components/common/modal/DynamicModal';
import AuthModal from '../../components/common/modal/AuthModal';
import ViewTableInfo from '../../components/common/Table/ViewTableInfo';
import { convertKTC, timeDiffMinute } from '../../utils';
import EventListSearchBar from '../../components/ServiceManage/searchBar/EventListSearchBar';
import CustomConfirmModal from '../../components/common/modal/CustomConfirmModal';
import { ModalInputItem, ModalSubText, RegistInputItem, RegistNotice, SubText } from '../../styles/ModuleComponents';
const Event = () => {
const token = sessionStorage.getItem('token');
const userInfo = useRecoilValue(authList);
const { t } = useTranslation();
const navigate = useNavigate();
const [currentPage, setCurrentPage] = useState(1);
const [pageSize, setPageSize] = useState(50);
const [dataList, setDataList] = useState([]);
const [detailData, setDetailData] = useState('');
const [selectedRow, setSelectedRow] = useState([]);
const [searchData, setSearchData] = useState({
title: '',
content: '',
status: 'ALL',
startDate: '',
endDate: '',
});
const [orderBy, setOrderBy] = useState('DESC');
const [modalState, setModalState] = useState({
detailModal: 'hidden',
deleteConfirmModal: 'hidden',
deleteCompleteModal: 'hidden',
});
const [alertMsg, setAlertMsg] = useState('');
const [deleteDesc, setDeleteDesc] = useState('');
useEffect(() => {
fetchData('', '', 'ALL', '', '');
setSelectedRow([]);
}, [currentPage]);
// 리스트 조회
const fetchData = async (title, content, status, startDate, endDate, order, size) => {
setDataList(
await EventView(
token,
title,
content,
status,
startDate && new Date(startDate).toISOString(),
endDate && new Date(endDate).toISOString(),
order ? order : orderBy,
size ? size : pageSize,
currentPage,
),
);
};
// 검색 함수
const handleSearch = (title, content, status, startDate, endDate) => {
fetchData(title, content, status, startDate && new Date(startDate).toISOString(), endDate && new Date(endDate).toISOString());
};
// 오름차순 내림차순
const handleOrderBy = e => {
const order = e.target.value;
setOrderBy(order);
fetchData(
searchData.title,
searchData.content,
searchData.status,
searchData.startDate && new Date(searchData.startDate).toISOString(),
searchData.endDate && new Date(searchData.endDate).toISOString(),
order,
pageSize,
);
};
const handlePageSize = e => {
const size = e.target.value;
setPageSize(size);
setCurrentPage(1);
fetchData(
searchData.title,
searchData.content,
searchData.status,
searchData.startDate && new Date(searchData.startDate).toISOString(),
searchData.endDate && new Date(searchData.endDate).toISOString(),
orderBy,
size,
1,
);
};
// 상세보기 호출
const handleDetailModal = async (e, id) => {
await EventDetailView(token, id).then(data => {
setDetailData(data);
});
e.preventDefault();
handleModalView('detail');
};
// 체크박스 선택 리스트
const handleSelectCheckBox = (e, event) => {
let list = [...selectedRow];
if (e.target.checked) {
list.push(event);
setSelectedRow(list);
} else {
const filterList = list.filter(data => e.target.id !== data);
setSelectedRow(filterList);
}
};
// 전체 선택 구현
const handleAllSelect = () => {
let list = [];
if (document.getElementById('check-all').checked === true) {
dataList.list.map((data, index) => {
document.getElementsByName('select')[index].checked = true;
list.push(String(data.id));
});
} else if (document.getElementById('check-all').checked === false) {
for (let i = 0; i < dataList.list.length; i++) {
dataList.list.map((data, index) => (document.getElementsByName('select')[index].checked = false));
list = [];
}
}
setSelectedRow(list);
};
const handleModalView = (type) => {
setModalState((prevState) => ({
...prevState,
[`${type}Modal`]: 'view',
}));
}
const handleModalClose = (type) => {
setModalState((prevState) => ({
...prevState,
[`${type}Modal`]: 'hidden',
}));
}
const handleModalSubmit = async (type, param = null) => {
switch (type) {
case "delete":
const delete_check = selectedRow.every(row => {
const timeDiff = timeDiffMinute(convertKTC(row.start_dt), (new Date));
return row.add_flag || (timeDiff < 30);
});
if(delete_check){
setAlertMsg(t('EVENT_TIME_LIMIT_UPDATE'));
return;
}
handleModalView('deleteConfirm');
break;
case "deleteConfirm":
let list = [];
let isChecked = false;
selectedRow.map(data => {
const row = dataList.list.find(row => row.id === Number(data.id));
if(row.status !== commonStatus.wait) isChecked = true;
list.push({
id: data.id,
delete_desc: deleteDesc
});
});
if(isChecked) {
setAlertMsg(t('EVENT_WARNING_DELETE'))
handleModalClose('deleteConfirm');
return;
}
EventDelete(token, list);
handleModalClose('deleteConfirm');
handleModalView('deleteComplete');
break;
case "deleteComplete":
handleModalClose('deleteComplete');
// fetchData(option);
window.location.reload();
break;
case "warning":
setAlertMsg('')
break;
}
}
const handleCountSelectedRow = () => {
return currentPage > (dataList && dataList.total) / pageSize ? selectedRow.length === dataList.total % pageSize : selectedRow.length === Number(pageSize);
};
return (
<>
{userInfo.auth_list && !userInfo.auth_list.some(auth => auth.id === authType.eventRead) ? (
<AuthModal />
) : (
<>
<Title>출석 보상 이벤트 관리</Title>
<FormWrapper>
<EventListSearchBar handleSearch={handleSearch} setResultData={setSearchData} />
</FormWrapper>
<ViewTableInfo total={dataList.total} total_all={dataList.total_all} handleOrderBy={handleOrderBy} handlePageSize={handlePageSize}>
{userInfo.auth_list && userInfo.auth_list.some(auth => auth.id === authType.eventDelete) && (
<Button theme={selectedRow.length === 0 ? 'disable' : 'line'} text="선택 삭제" handleClick={() => handleModalSubmit('delete')} />
)}
{userInfo.auth_list && userInfo.auth_list.some(auth => auth.id === authType.eventUpdate) && (
<Button
theme="primary"
text="이벤트 등록"
type="button"
handleClick={e => {
e.preventDefault();
navigate('/servicemanage/event/eventregist');
}}
/>
)}
</ViewTableInfo>
<TableWrapper>
<TableStyle>
<caption></caption>
<thead>
<tr>
<th width="40">
<CheckBox id="check-all" handleCheck={handleAllSelect} checked={handleCountSelectedRow()} />
</th>
<th width="80">번호</th>
<th width="100">이벤트 상태</th>
<th width="210">시작 일시</th>
<th width="210">종료 일시</th>
<th>우편 제목</th>
<th width="110">확인 / 수정</th>
<th width="200">등록자(처리자)</th>
</tr>
</thead>
<tbody>
{dataList.list &&
dataList.list.map(event => (
<Fragment key={event.row_num}>
<tr>
<td>
<CheckBox name={'select'} id={event.id} setData={e => handleSelectCheckBox(e, event)} />
</td>
<td>{event.row_num}</td>
<td>{eventStatus.map(data => data.value === event.status && data.name)}</td>
<td>{convertKTC(event.start_dt)}</td>
<td>{convertKTC(event.end_dt)}</td>
<MailTitle>{event.title}</MailTitle>
<td>
<Button theme="line" text="상세보기" handleClick={e => handleDetailModal(e, event.id)} />
</td>
<td>{event.create_by}</td>
</tr>
</Fragment>
))}
</tbody>
</TableStyle>
</TableWrapper>
<Pagination postsPerPage={pageSize} totalPosts={dataList && dataList.total_all} setCurrentPage={setCurrentPage} currentPage={currentPage} pageLimit={10} />
{/*상세*/}
<EventDetailModal detailView={modalState.detailModal} handleDetailView={() => handleModalClose('detail')} content={detailData} setDetailData={setDetailData}/>
{/*삭제 확인*/}
<DynamicModal
modalType={modalTypes.childOkCancel}
view={modalState.deleteConfirmModal}
handleCancel={() => handleModalClose('deleteConfirm')}
handleSubmit={() => handleModalSubmit('deleteConfirm')}
>
<ModalInputItem>
{t('EVENT_SELECT_DELETE')}
<RegistInputItem>
<TextInput
placeholder="사유 입력"
maxLength="30"
value={deleteDesc}
onChange={e => {
if (e.target.value.length > 30) return;
setDeleteDesc(e.target.value.trimStart())
}}
/>
</RegistInputItem>
<ModalSubText $color={deleteDesc.length > 29 ? 'red' : '#666'}>* 최대 등록 가능 글자수 ({deleteDesc.length}/30)</ModalSubText>
</ModalInputItem>
</DynamicModal>
{/*삭제 완료*/}
<DynamicModal
modalType={modalTypes.completed}
view={modalState.deleteCompleteModal}
handleSubmit={() => handleModalSubmit('deleteComplete')}
modalText={t('DEL_COMPLETE')}
/>
{/* 경고 모달 */}
<DynamicModal
modalType={modalTypes.completed}
view={alertMsg ? 'view' : 'hidden'}
modalText={alertMsg}
handleSubmit={() => handleModalSubmit('warning')}
/>
</>
)}
</>
);
};
export default Event;

View File

@@ -0,0 +1,519 @@
import { useState, Fragment, useEffect } from 'react';
import Button from '../../components/common/button/Button';
import Loading from '../../components/common/Loading';
import {
Title,
BtnWrapper,
TextInput,
SelectInput,
Label,
InputLabel,
Textarea,
SearchBarAlert,
} from '../../styles/Components';
import { useNavigate } from 'react-router-dom';
import { EventIsItem, EventSingleRegist } from '../../apis';
import { authList } from '../../store/authList';
import { useRecoilValue } from 'recoil';
import { useTranslation } from 'react-i18next';
import {
AppendRegistBox, AppendRegistTable, AreaBtnClose,
BtnDelete,
Item,
ItemList, LangArea, ModalInputItem, ModalItem, ModalItemList, ModalSubText, RegistGroup,
RegistInputItem,
RegistInputRow, RegistNotice, RegistTable,
} from '../../styles/ModuleComponents';
import AuthModal from '../../components/common/modal/AuthModal';
import { authType, benItems, modalTypes, wellType } from '../../assets/data';
import DynamicModal from '../../components/common/modal/DynamicModal';
import DateTimeInput from '../../components/common/input/DateTimeInput';
import { timeDiffMinute } from '../../utils';
const EventRegist = () => {
const navigate = useNavigate();
const userInfo = useRecoilValue(authList);
const { t } = useTranslation();
const token = sessionStorage.getItem('token');
const [loading, setLoading] = useState(false); // 로딩 창
const [modalState, setModalState] = useState({
cancelModal: 'hidden',
registConfirmModal: 'hidden',
registCompleteModal: 'hidden',
}); // 모달 관리
const [item, setItem] = useState(''); // 아이템 값
const [itemCount, setItemCount] = useState(''); // 아이템 개수
const [resource, setResource] = useState('19010001'); // 자원 값
const [resourceCount, setResourceCount] = useState(''); // 자원 개수
const [isNullValue, setIsNullValue] = useState(false); // 데이터 값 체크
const [btnValidation, setBtnValidation] = useState(false); // 입력창 버튼
const [itemCheckMsg, setItemCheckMsg] = useState('');
const [alertMsg, setAlertMsg] = useState('');
const [time, setTime] = useState({
start_hour: '00',
start_min: '00',
end_hour: '00',
end_min: '00',
}); //시간 정보
const [resultData, setResultData] = useState({
is_reserve: false,
start_dt: '',
end_dt: '',
event_type: 'ATTD',
mail_list: [
{
title: '',
content: '',
language: 'KO',
},
{
title: '',
content: '',
language: 'EN',
},
{
title: '',
content: '',
language: 'JA',
}
],
item_list: [],
guid: '',
}); //데이터 정보
useEffect(() => {
if (checkCondition()) {
setIsNullValue(false);
} else {
setIsNullValue(true);
}
}, [resultData]);
useEffect(() => {
setItemCheckMsg('');
}, [item]);
const combineDateTime = (date, hour, min) => {
if (!date) return null;
return new Date(date.getFullYear(), date.getMonth(), date.getDate(), hour, min);
};
// 날짜 처리
const handleDateChange = (data, type) => {
const date = new Date(data);
setResultData({
...resultData,
[`${type}_dt`]: combineDateTime(date, time[`${type}_hour`], time[`${type}_min`]),
});
};
// 시간 처리
const handleTimeChange = (e, type) => {
const { id, value } = e.target;
const newTime = { ...time, [`${type}_${id}`]: value };
setTime(newTime);
const date = resultData[`${type}_dt`] ? resultData[`${type}_dt`] : new Date();
setResultData({
...resultData,
[`${type}_dt`]: combineDateTime(date, newTime[`${type}_hour`], newTime[`${type}_min`]),
});
};
// 아이템 수량 숫자 체크
const handleItemCount = e => {
if (e.target.value === '0' || e.target.value === '-0') {
setItemCount('1');
e.target.value = '1';
} else if (e.target.value < 0) {
let plusNum = Math.abs(e.target.value);
setItemCount(plusNum);
} else {
setItemCount(e.target.value);
}
};
// 아이템 추가
const handleItemList = async () => {
if(benItems.includes(item)){
setAlertMsg(t('MAIL_ITEM_ADD_BEN'))
return;
}
if(item.length === 0 || itemCount.length === 0) return;
const token = sessionStorage.getItem('token');
const result = await EventIsItem(token, {item: item});
if(result.data.result === "ERROR"){
setItemCheckMsg(t('NOT_ITEM'));
return;
}
const itemIndex = resultData.item_list.findIndex((data) => data.item === item);
if (itemIndex !== -1) {
setItemCheckMsg(t('MAIL_ITEM_ADD_DUPL'));
return;
}
const newItem = { item: item, item_cnt: itemCount, item_name: result.data.data.item_info.item_name };
resultData.item_list.push(newItem);
setItem('');
setItemCount('');
};
// 추가된 아이템 삭제
const onItemRemove = id => {
let filterList = resultData.item_list && resultData.item_list.filter(item => item !== resultData.item_list[id]);
setResultData({ ...resultData, item_list: filterList });
};
// 입력창 삭제
const onLangDelete = language => {
let filterList = resultData.mail_list && resultData.mail_list.filter(el => el.language !== language);
if (filterList.length === 1) {
setBtnValidation(true);
} else {
setBtnValidation(false);
}
setResultData({ ...resultData, mail_list: filterList });
};
// 자원 수량 숫자 체크
const handleResourceCount = e => {
if (e.target.value === '0' || e.target.value === '-0') {
setResourceCount('1');
e.target.value = '1';
} else if (e.target.value < 0) {
let plusNum = Math.abs(e.target.value);
setResourceCount(plusNum);
} else {
setResourceCount(e.target.value);
}
};
// 자원 추가
const handleResourceList = (e) => {
if(resource.length === 0 || resourceCount.length === 0) return;
const itemIndex = resultData.item_list.findIndex(
(item) => item.item === resource
);
if (itemIndex !== -1) {
const item_cnt = resultData.item_list[itemIndex].item_cnt;
resultData.item_list[itemIndex].item_cnt = Number(item_cnt) + Number(resourceCount);
} else {
const name = wellType.find(well => well.value === resource).name;
const newItem = { item: resource, item_cnt: resourceCount, item_name: name };
resultData.item_list.push(newItem);
}
setResourceCount('');
};
const handleModalView = (type) => {
setModalState((prevState) => ({
...prevState,
[`${type}Modal`]: 'view',
}));
}
const handleModalClose = (type) => {
setModalState((prevState) => ({
...prevState,
[`${type}Modal`]: 'hidden',
}));
}
const handleSubmit = async (type, param = null) => {
switch (type) {
case "submit":
if (!checkCondition()) return;
const timeDiff = timeDiffMinute(resultData.start_dt, (new Date))
if(timeDiff < 60) {
setAlertMsg(t('EVENT_TIME_LIMIT_ADD'));
return;
}
handleModalView('registConfirm');
break;
case "cancel":
handleModalView('deleteSubmit');
break;
case "registConfirm":
setLoading(true);
const result = await EventSingleRegist(token, resultData);
setLoading(false);
handleModalClose('registConfirm');
handleModalView('registComplete');
break;
case "registComplete":
handleModalClose('registComplete');
navigate('/servicemanage/event');
break;
case "warning":
setAlertMsg('');
break;
}
}
const checkCondition = () => {
return (
resultData.mail_list.every(data => data.content !== '' && data.title !== '') &&
(resultData.start_dt.length !== 0) &&
(resultData.end_dt.length !== 0)
);
};
return (
<>
{userInfo.auth_list && !userInfo.auth_list.some(auth => auth.id === authType.eventUpdate) ? (
<AuthModal/>
) : (
<>
<Title>이벤트 등록</Title>
<RegistGroup>
<RegistInputRow>
<RegistInputItem>
<InputLabel>이벤트 타입</InputLabel>
<SelectInput onChange={e => setResultData({ ...resultData, event_type: e.target.value })} value={resultData.event_type}>
<option value="ATTD">출석 이벤트</option>
</SelectInput>
</RegistInputItem>
<DateTimeInput
title="시작 시간"
dateName="시작 일자"
selectedDate={resultData.start_dt}
handleSelectedDate={data => handleDateChange(data, 'start')}
onChange={e => handleTimeChange(e, 'start')}
/>
<DateTimeInput
title="종료 시간"
dateName="종료 일자"
selectedDate={resultData.end_dt}
handleSelectedDate={data => handleDateChange(data, 'end')}
onChange={e => handleTimeChange(e, 'end')}
/>
</RegistInputRow>
</RegistGroup>
{resultData.mail_list.map((data, idx) => {
return (
<Fragment key={idx}>
<AppendRegistBox>
<LangArea>
언어 : {data.language}
{btnValidation === false ? (
<AreaBtnClose
onClick={e => {
e.preventDefault();
onLangDelete(data.language);
}}
/>
) : (
<AreaBtnClose opacity="10%" />
)}
</LangArea>
<RegistTable>
<tbody>
<tr>
<th width="120">
<Label>제목</Label>
</th>
<td>
<RegistInputItem>
<TextInput
placeholder="우편 제목 입력"
maxLength="30"
id={data.language}
value={data.title}
onChange={e => {
if (e.target.value.length > 30) {
return;
}
let list = [...resultData.mail_list];
let findIndex = resultData.mail_list && resultData.mail_list.findIndex(item => item.language === e.target.id);
list[findIndex].title = e.target.value.trimStart();
setResultData({ ...resultData, mail_list: list });
}}
/>
</RegistInputItem>
<RegistNotice $color={data.title.length > 29 ? 'red' : '#666'}>* 최대 등록 가능 글자수 ({data.title.length}/30)</RegistNotice>
</td>
</tr>
<tr>
<th>
<Label>내용</Label>
</th>
<td>
<Textarea
maxLength="2000"
value={data.content}
id={data.language}
onChange={e => {
if (e.target.value.length > 2000) {
return;
}
let list = [...resultData.mail_list];
let findIndex = resultData.mail_list && resultData.mail_list.findIndex(item => item.language === e.target.id);
list[findIndex].content = e.target.value.trimStart();
setResultData({ ...resultData, mail_list: list });
}}
/>
<RegistNotice $color={data.content.length > 1999 ? 'red' : '#666'}>* 최대 등록 가능 글자수 ({data.content.length}/2000)</RegistNotice>
</td>
</tr>
</tbody>
</RegistTable>
</AppendRegistBox>
</Fragment>
);
})}
<AppendRegistBox>
<AppendRegistTable>
<tbody>
<tr>
<th width="120">
<Label>아이템 첨부</Label>
</th>
<td>
<RegistInputItem>
<TextInput placeholder="Item Meta id 입력" value={item} onChange={e => setItem(e.target.value.trimStart())} />
<TextInput placeholder="수량" type="number" value={itemCount} onChange={e => handleItemCount(e)} width="100px" />
<Button text="추가" theme={itemCount.length === 0 || item.length === 0 ? 'disable' : 'search'} handleClick={handleItemList} width="100px" height="35px" />
{itemCheckMsg && <SearchBarAlert>{itemCheckMsg}</SearchBarAlert>}
</RegistInputItem>
</td>
</tr>
<tr>
<th width="120">
<Label>자원 첨부</Label>
</th>
<td>
<RegistInputItem>
<SelectInput onChange={e => setResource(e.target.value)} value={resource}>
{wellType.map((data, index) => (
<option key={index} value={data.value}>
{data.name}
</option>
))}
</SelectInput>
<TextInput placeholder="수량" type="number" value={resourceCount} onChange={e => handleResourceCount(e)} width="200px" />
<Button text="추가" theme={resourceCount.length === 0 || resource.length === 0 ? 'disable' : 'search'} handleClick={handleResourceList} width="100px" height="35px" />
</RegistInputItem>
<div>
{resultData.item_list && (
<ItemList>
{resultData.item_list.map((data, index) => {
return (
<Item key={index}>
<span>
{data.item_name}[{data.item}] ({data.item_cnt})
</span>
<BtnDelete onClick={() => onItemRemove(index)}></BtnDelete>
</Item>
);
})}
</ItemList>
)}
</div>
</td>
</tr>
</tbody>
</AppendRegistTable>
</AppendRegistBox>
{isNullValue && (
<SearchBarAlert $align="right" $padding="0 0 15px">
{t('NULL_MSG')}
</SearchBarAlert>
)}
<BtnWrapper $justify="flex-end" $gap="10px">
<Button text="취소" theme="line" handleClick={() => handleModalView('cancel')} />
<Button
type="submit"
text="등록"
theme={checkCondition() ? 'primary' : 'disable'}
handleClick={() => handleSubmit('submit')}
/>
</BtnWrapper>
{/* 등록 모달 */}
<DynamicModal
modalType={modalTypes.confirmOkCancel}
view={modalState.registConfirmModal}
modalText={t('EVENT_REGIST_CONFIRM')}
handleSubmit={() => handleSubmit('registConfirm')}
handleCancel={() => handleModalClose('registConfirm')}
/>
<DynamicModal
modalType={modalTypes.childOkCancel}
view={modalState.registConfirmModal}
handleCancel={() => handleModalClose('registConfirm')}
handleSubmit={() => handleSubmit('registConfirm')}
>
<ModalItem>
{t('EVENT_REGIST_CONFIRM')}
{resultData.item_list && (
<ModalItemList>
{resultData.item_list.map((data, index) => {
return (
<Item key={index}>
<span>
{data.item_name} {data.item_cnt.toLocaleString()}
</span>
</Item>
);
})}
</ModalItemList>
)}
</ModalItem>
</DynamicModal>
{/* 완료 모달 */}
<DynamicModal
modalType={modalTypes.completed}
view={modalState.registCompleteModal}
modalText={t('REGIST_COMPLTE')}
handleSubmit={() => handleSubmit('registComplete')}
/>
{/* 취소 모달 */}
<DynamicModal
modalType={modalTypes.confirmOkCancel}
view={modalState.cancelModal}
modalText={t('EVENT_REGIST_CANCEL')}
handleCancel={() => handleModalClose('cancel')}
handleSubmit={() => handleSubmit('cancel')}
/>
{/* 경고 모달 */}
<DynamicModal
modalType={modalTypes.completed}
view={alertMsg ? 'view' : 'hidden'}
modalText={alertMsg}
handleSubmit={() => handleSubmit('warning')}
/>
{loading && <Loading/>}
</>
)}
</>
);
};
export default EventRegist;

View File

@@ -0,0 +1,252 @@
import { useState, Fragment, useEffect } from 'react';
import Modal from '../../components/common/modal/Modal';
import Button from '../../components/common/button/Button';
import styled from 'styled-components';
import { Title, FormWrapper, TableStyle, TableWrapper, BtnWrapper, ButtonClose, ModalText } from '../../styles/Components';
import { useNavigate } from 'react-router-dom';
import { ItemsSearchBar } from '../../components/ServiceManage';
import { ItemListView } from '../../apis';
import { authList } from '../../store/authList';
import { useRecoilValue } from 'recoil';
import AuthModal from '../../components/common/modal/AuthModal';
import { authType } from '../../assets/data';
const Items = () => {
const token = sessionStorage.getItem('token');
const userInfo = useRecoilValue(authList);
const navigate = useNavigate();
// 데이터 조회 관련
const [dataList, setDataList] = useState([]);
const [selectedData, setSelectedData] = useState([]);
const [requestValue, setRequestValue] = useState([]);
// 모달 관련 변수
const [confirmDelModal, setConfirmDelModal] = useState('hidden');
const [completeDelModal, setCompleteDelModal] = useState('hidden');
const [confirmRestoreModal, setConfirmRestoreModal] = useState('hidden');
const [completeRestoreModal, setCompleteRestoreModal] = useState('hidden');
// 검색 관련 변수
const [searchData, setSearchData] = useState({
searchType: 'GUID',
searchKey: '',
status: 'ALL',
sanctions: 'ALL',
period: 'ALL',
});
const status = [
{ value: 'ALL', name: '상태' },
{ value: 'ACTIVE', name: '활성' },
{ value: 'DEACTIVE', name: '비활성' },
];
const restore = [
{ value: 'ALL', name: '복구' },
{ value: 'POSSIBLE', name: '가능' },
{ value: 'IMPOSSIBLE', name: '불가능' },
];
const fetchData = async (searchType, data, status, restore) => {
setDataList(await ItemListView(token, searchType, data, status, restore));
};
// 검색 기능
const handleSearch = (searchType, data, status, restore) => {
fetchData(searchType, data, status, restore);
};
// 삭제 여부 모달
const handleConfirmeDelModalClose = () => {
if (confirmDelModal === 'hidden') {
setConfirmDelModal('view');
} else {
setConfirmDelModal('hidden');
setRequestValue([]);
}
};
// 복구 여부 모달
const handleConfirmeRestoreModalClose = () => {
if (confirmRestoreModal === 'hidden') {
setConfirmRestoreModal('view');
} else {
setConfirmRestoreModal('hidden');
setRequestValue([]);
}
};
// 삭제 모달창
const handleConfirmDeleteModal = data => {
handleConfirmeDelModalClose();
};
// 복구 모달창
const handleConfirmRestoreModal = data => {
handleConfirmeRestoreModalClose();
};
// 삭제 완료 확인 모달
const handleCompleteDelModal = () => {
if (completeDelModal === 'hidden') {
setCompleteDelModal('view');
} else {
setCompleteDelModal('hidden');
setRequestValue([]);
handleConfirmeDelModalClose();
window.location.reload();
}
};
// 복구 완료 확인 모달
const handleCompleteRestoreModal = () => {
if (completeRestoreModal === 'hidden') {
setCompleteRestoreModal('view');
} else {
setCompleteRestoreModal('hidden');
setRequestValue([]);
handleConfirmeRestoreModalClose();
window.location.reload();
}
};
// 삭제 기능 구현
const handleDelete = () => {
let list = [];
list.push({ id: selectedData });
// BlackListDelete(token, list);
handleCompleteDelModal();
};
// 복구 기능
const handleRestore = () => {
let list = [];
list.push({ id: selectedData});
//api 호출
handleCompleteRestoreModal();
}
return (
<>
{userInfo.auth_list && !userInfo.auth_list.some(auth => auth.id === authType.itemRead) ? (
<AuthModal/>
) : (
<>
<Title>아이템 복구 삭제</Title>
<FormWrapper>
<ItemsSearchBar handleSearch={handleSearch} setResultData={setSearchData} />
</FormWrapper>
<TableWrapper>
<TableStyle>
<caption></caption>
<thead>
<tr>
<th width="80">번호</th>
<th width="20%">아이템명</th>
<th width="20%">아이템 ID</th>
<th width="80">상태</th>
<th width="100">생성 날짜</th>
<th width="80">복구 가능 여부</th>
{userInfo.auth_list && userInfo.auth_list.some(auth => auth.id === 30) && <th width="100">액션</th>}
</tr>
</thead>
<tbody>
{dataList.list &&
dataList.list.map(data => (
<Fragment key={data.id}>
<tr>
<td>{data.row_num}</td>
<td>{data.item_nm}</td>
<td>{data.item_id}</td>
<td>{status.map(item => item.value === data.status && item.name)}</td>
<td>{new Date(data.create_by).toLocaleString()}</td>
<td>{restore.map(item => item.value === data.restore && item.name)}</td>
{userInfo.auth_list && userInfo.auth_list.some(auth => auth.id === 30) && (
<td>
<ButtonGroup>
<Button theme="line" id={data.id+"restore"} name="single" text="복구" handleClick={() => handleConfirmRestoreModal(data)} />
<Divider>/</Divider>
<Button theme="line" id={data.id+"delete"} name="single" text="삭제" handleClick={() => handleConfirmDeleteModal(data)} />
</ButtonGroup>
</td>
)}
</tr>
</Fragment>
))}
</tbody>
</TableStyle>
</TableWrapper>
{/* 삭제 확인 모달 */}
<Modal min="440px" $padding="40px" $bgcolor="transparent" $view={confirmDelModal}>
<BtnWrapper $justify="flex-end">
<ButtonClose onClick={handleConfirmeDelModalClose} />
</BtnWrapper>
<ModalText $align="center">
아이템을 삭제하시겠습니까?<br />* 한번 삭제한 아이템은 다시 복구할 없습니다.
</ModalText>
<BtnWrapper $gap="10px">
<Button text="취소" theme="line" size="large" width="100%" handleClick={handleConfirmeDelModalClose} />
<Button text="확인" theme="primary" type="submit" size="large" width="100%" handleClick={handleDelete} />
</BtnWrapper>
</Modal>
{/* 삭제 완료 모달 */}
<Modal min="440px" $padding="40px" $bgcolor="transparent" $view={completeDelModal}>
<BtnWrapper $justify="flex-end">
<ButtonClose onClick={handleCompleteDelModal} />
</BtnWrapper>
<ModalText $align="center">삭제가 완료되었습니다.</ModalText>
<BtnWrapper $gap="10px">
<Button text="확인" theme="primary" type="submit" size="large" width="100%" handleClick={handleCompleteDelModal} />
</BtnWrapper>
</Modal>
{/* 복구 확인 모달 */}
<Modal min="440px" $padding="40px" $bgcolor="transparent" $view={confirmRestoreModal}>
<BtnWrapper $justify="flex-end">
<ButtonClose onClick={handleConfirmeRestoreModalClose} />
</BtnWrapper>
<ModalText $align="center">
아이템을 복구하시겠습니까?
</ModalText>
<BtnWrapper $gap="10px">
<Button text="취소" theme="line" size="large" width="100%" handleClick={handleConfirmeRestoreModalClose} />
<Button text="확인" theme="primary" type="submit" size="large" width="100%" handleClick={handleDelete} />
</BtnWrapper>
</Modal>
{/* 복구 완료 모달 */}
<Modal min="440px" $padding="40px" $bgcolor="transparent" $view={completeRestoreModal}>
<BtnWrapper $justify="flex-end">
<ButtonClose onClick={handleCompleteRestoreModal} />
</BtnWrapper>
<ModalText $align="center">복구가 완료되었습니다.</ModalText>
<BtnWrapper $gap="10px">
<Button text="확인" theme="primary" type="submit" size="large" width="100%" handleClick={handleCompleteRestoreModal} />
</BtnWrapper>
</Modal>
</>
)}
</>
);
};
export default Items;
const ButtonGroup = styled.div`
display: 'flex',
alignItems: 'center'
`
const Divider = styled.span`
margin: 0 8px;
color: RGB(200,200,200);
padding: 4px 0;
`;

View File

@@ -0,0 +1,275 @@
import { useState, Fragment, useRef } from 'react';
import { useRecoilValue } from 'recoil';
import { useTranslation } from 'react-i18next';
import 'react-datepicker/dist/react-datepicker.css';
import {
BuildingView,
LandAuctionDelete,
LandAuctionDetailView, LandView,
} from '../../apis';
import { authList } from '../../store/authList';
import {
authType,
modalTypes,
landAuctionStatusType,
landAuctionStatus,
landSize,
caliumStatus, commonStatus,
} from '../../assets/data';
import { Title, FormWrapper, TableStyle, TableWrapper} from '../../styles/Components';
import {
CheckBox,
Button,
DynamicModal,
Pagination,
ViewTableInfo, ExcelDownButton,
} from '../../components/common';
import { convertKTC, timeDiffMinute } from '../../utils';
import { LandAuctionModal, LandAuctionSearchBar } from '../../components/ServiceManage';
import { INITIAL_PAGE_SIZE, INITIAL_PAGE_LIMIT } from '../../assets/data/adminConstants';
import { useDataFetch, useModal, useTable, withAuth } from '../../utils/hook';
import { useLandAuctionSearch } from '../../components/ServiceManage/searchBar/LandAuctionSearchBar';
import { StatusWapper, ChargeBtn, StatusLabel } from '../../styles/ModuleComponents';
const LandAuction = () => {
const token = sessionStorage.getItem('token');
const userInfo = useRecoilValue(authList);
const { t } = useTranslation();
const tableRef = useRef(null);
const [detailData, setDetailData] = useState({});
const {
modalState,
handleModalView,
handleModalClose
} = useModal({
detail: 'hidden',
deleteConfirm: 'hidden',
deleteComplete: 'hidden'
});
const [alertMsg, setAlertMsg] = useState('');
const [modalType, setModalType] = useState('regist');
const {
searchParams,
data: dataList,
handleSearch,
handleReset,
handlePageChange,
handlePageSizeChange,
handleOrderByChange,
updateSearchParams
} = useLandAuctionSearch(token, INITIAL_PAGE_SIZE);
const {
selectedRows,
handleSelectRow,
isRowSelected
} = useTable(dataList?.auction_list || [], {mode: 'single'});
const {
data: landData
} = useDataFetch(() => LandView(token), [token]);
const {
data: buildingData
} = useDataFetch(() => BuildingView(token), [token]);
const handleModalSubmit = async (type, param = null) => {
switch (type) {
case "regist":
setModalType('regist');
handleModalView('detail');
break;
case "detail":
await LandAuctionDetailView(token, param).then(data => {
setDetailData(data.auction_detail);
setModalType('modify');
handleModalView('detail');
});
break;
case "delete":
const date_check = selectedRows.every(row => {
const timeDiff = timeDiffMinute(convertKTC(row.auction_start_dt), (new Date));
return timeDiff < 3;
});
if(date_check){
setAlertMsg(t('LAND_AUCTION_DELETE_DATE_WARNING'));
return;
}
if(selectedRows[0].status === landAuctionStatusType.auction_start || selectedRows[0].status === landAuctionStatusType.stl_end){
setAlertMsg(t('LAND_AUCTION_DELETE_STATUS_WARNING'));
return;
}
handleModalView('deleteConfirm');
break;
case "deleteConfirm":
let list = [];
let isChecked = false;
selectedRows.map(data => {
// const row = dataList.list.find(row => row.id === Number(data.id));
// if(row.status !== commonStatus.wait) isChecked = true;
list.push({
id: data.id,
});
});
if(isChecked) {
setAlertMsg(t('LAND_AUCTION_WARNING_DELETE'))
handleModalClose('deleteConfirm');
return;
}
await LandAuctionDelete(token, list).then(data => {
handleModalClose('deleteConfirm');
if(data.result === "SUCCESS") {
handleModalView('deleteComplete');
}else if(data.result === "ERROR_AUCTION_STATUS_IMPOSSIBLE"){
setAlertMsg(t('LAND_AUCTION_ERROR_DELETE_STATUS'));
}else{
setAlertMsg(t('DELETE_FAIL'));
}
}).catch(reason => {
setAlertMsg(t('API_FAIL'));
});
break;
case "deleteComplete":
handleModalClose('deleteComplete');
// fetchData(option);
window.location.reload();
break;
case "warning":
setAlertMsg('')
break;
}
}
return (
<>
<Title>랜드 경매 관리</Title>
<FormWrapper>
<LandAuctionSearchBar
searchParams={searchParams}
onSearch={(newParams, executeSearch = true) => {
if (executeSearch) {
handleSearch(newParams);
} else {
updateSearchParams(newParams);
}
}}
onReset={handleReset}
/>
</FormWrapper>
<ViewTableInfo total={dataList?.total} total_all={dataList?.total_all} handleOrderBy={handleOrderByChange} handlePageSize={handlePageSizeChange}>
<ExcelDownButton tableRef={tableRef} fileName={t('FILE_LAND_AUCTION')} />
{userInfo.auth_list?.some(auth => auth.id === authType.landAuctionDelete) && (
<Button theme={selectedRows.length === 0 ? 'disable' : 'line'} text="선택 취소" handleClick={() => handleModalSubmit('delete')} />
)}
{userInfo.auth_list?.some(auth => auth.id === authType.landAuctionUpdate) && (
<Button
theme="primary"
text="경매 등록"
type="button"
handleClick={e => handleModalSubmit('regist')}
/>
)}
</ViewTableInfo>
<TableWrapper>
<TableStyle ref={tableRef}>
<caption></caption>
<thead>
<tr>
<th width="40"></th>
{/*<th width="80">번호</th>*/}
<th width="90">경매 상태</th>
<th width="80">랜드 크기</th>
<th width="200">랜드 이름</th>
<th width="90">인스턴스 </th>
{/*<th width="100">경매 재화</th>*/}
<th width="90">경매 시작가</th>
<th width="200">예약 시작일</th>
<th width="200">예약 종료일</th>
<th width="200">경매 시작일</th>
<th width="200">경매 종료일</th>
<th width="200">낙찰 정산일</th>
<th width="90">낙찰 정산가</th>
<th width="100">낙찰자</th>
<th width="100">확인 / 수정</th>
<th width="150">히스토리</th>
</tr>
</thead>
<tbody>
{dataList?.auction_list?.map(auction => (
<tr key={auction.row_num}>
<td>
<CheckBox name={'select'} id={auction.id}
setData={(e) => handleSelectRow(e, auction)}
checked={isRowSelected(auction.id)} />
</td>
{/*<td>{event.row_num}</td>*/}
<StatusWapper>
<StatusLabel $status={auction.status}>
{landAuctionStatus.find(data => data.value === auction.status).name}
</StatusLabel>
</StatusWapper>
{/*<td>{landAuctionStatus.find(data => data.value === auction.status)?.name}</td>*/}
<td>{auction.land_size}</td>
<td>{auction.land_name}</td>
<td>{auction.land_socket}</td>
{/*<td>{auction.currency_type}</td>*/}
<td>{auction.start_price}</td>
<td>{convertKTC(auction.resv_start_dt)}</td>
<td>{convertKTC(auction.resv_end_dt)}</td>
<td>{convertKTC(auction.auction_start_dt)}</td>
<td>{convertKTC(auction.auction_end_dt)}</td>
<td>{convertKTC(auction.close_end_dt)}</td>
<td>{auction.close_price}</td>
<td>{auction.bidder_nickname}</td>
<td>
<Button theme="line" text="상세보기"
handleClick={e => handleModalSubmit('detail', auction.id)} />
</td>
<td>{auction.update_by}</td>
</tr>
))}
</tbody>
</TableStyle>
</TableWrapper>
<Pagination postsPerPage={searchParams.pageSize} totalPosts={dataList?.total_all} setCurrentPage={handlePageChange} currentPage={searchParams.currentPage} pageLimit={INITIAL_PAGE_LIMIT} />
{/*상세*/}
<LandAuctionModal modalType={modalType} detailView={modalState.detailModal} handleDetailView={() => handleModalClose('detail')} content={detailData} setDetailData={setDetailData} landData={landData} buildingData={buildingData} />
{/*삭제 확인*/}
<DynamicModal
modalType={modalTypes.confirmOkCancel}
view={modalState.deleteConfirmModal}
handleCancel={() => handleModalClose('deleteConfirm')}
handleSubmit={() => handleModalSubmit('deleteConfirm')}
modalText={t('LAND_AUCTION_SELECT_DELETE')}
/>
{/*삭제 완료*/}
<DynamicModal
modalType={modalTypes.completed}
view={modalState.deleteCompleteModal}
handleSubmit={() => handleModalSubmit('deleteComplete')}
modalText={t('DEL_COMPLETE')}
/>
{/* 경고 모달 */}
<DynamicModal
modalType={modalTypes.completed}
view={alertMsg ? 'view' : 'hidden'}
modalText={alertMsg}
handleSubmit={() => handleModalSubmit('warning')}
/>
</>
)
};
export default withAuth(authType.landAuctionRead)(LandAuction);

View File

@@ -0,0 +1,331 @@
import styled from 'styled-components';
import { useState, useEffect, Fragment } from 'react';
import CheckBox from '../../components/common/input/CheckBox';
import Modal from '../../components/common/modal/Modal';
import { MailDelete, MailDetailView, MailView } from '../../apis';
import { Title, FormWrapper, TableInfo, ListCount, ListOption, TableStyle, SelectInput, MailTitle, TableWrapper, BtnWrapper, ButtonClose, ModalText } from '../../styles/Components';
import Button from '../../components/common/button/Button';
import 'react-datepicker/dist/react-datepicker.css';
import { useNavigate } from 'react-router-dom';
import MailDetailModal from '../../components/ServiceManage/modal/MailDetailModal';
import MailListSearchBar from '../../components/ServiceManage/searchBar/MailListSearchBar';
import Pagination from '../../components/common/Pagination/Pagination';
import { authList } from '../../store/authList';
import { useRecoilValue } from 'recoil';
import { authType, mailReceiveType, mailSendStatus, mailSendType, mailType } from '../../assets/data';
import AuthModal from '../../components/common/modal/AuthModal';
import ViewTableInfo from '../../components/common/Table/ViewTableInfo';
import { convertKTC} from '../../utils';
const Mail = () => {
const token = sessionStorage.getItem('token');
const userInfo = useRecoilValue(authList);
const navigate = useNavigate();
const [currentPage, setCurrentPage] = useState(1);
const [pageSize, setPageSize] = useState(50);
const [dataList, setDataList] = useState([]);
const [detailView, setDetailView] = useState('hidden');
const [detailData, setDetailData] = useState('');
const [selectedRow, setSelectedRow] = useState([]);
const [searchData, setSearchData] = useState({
mailTitle: '',
content: '',
sendType: 'ALL',
sendStatus: 'ALL',
mailType: 'ALL',
receiveType: 'ALL',
sendDate: '',
endDate: '',
});
const [orderBy, setOrderBy] = useState('DESC');
const [deleteModalClose, setDeleteModalClose] = useState('hidden');
const [confirmModalClose, setConfirmModalClose] = useState('hidden');
// console.log(dataList);
useEffect(() => {
fetchData('', '', 'ALL', 'ALL', 'ALL', 'ALL', '', '');
setSelectedRow([]);
}, [currentPage]);
// 리스트 조회
const fetchData = async (mailTitle, content, sendType, sendStatus, mailType, receiveType, sendDate, endDate, order, size) => {
setDataList(
await MailView(
token,
mailTitle,
content,
sendType,
sendStatus,
mailType,
receiveType,
sendDate && new Date(sendDate).toISOString(),
endDate && new Date(endDate).toISOString(),
order ? order : orderBy,
size ? size : pageSize,
currentPage,
),
);
};
// 검색 함수
const handleSearch = (mailTitle, content, sendType, sendStatus, mailType, receiveType, sendDate, endDate) => {
fetchData(mailTitle, content, sendType, sendStatus, mailType, receiveType, sendDate && new Date(sendDate).toISOString(), endDate && new Date(endDate).toISOString());
};
// 오름차순 내림차순
const handleOrderBy = e => {
const order = e.target.value;
setOrderBy(order);
fetchData(
searchData.mailTitle,
searchData.content,
searchData.sendType,
searchData.sendStatus,
searchData.mailType,
searchData.receiveType,
searchData.sendDate && new Date(searchData.sendDate).toISOString(),
searchData.endDate && new Date(searchData.endDate).toISOString(),
order,
pageSize,
);
};
const handlePageSize = e => {
const size = e.target.value;
setPageSize(size);
setCurrentPage(1);
fetchData(
searchData.mailTitle,
searchData.content,
searchData.sendType,
searchData.sendStatus,
searchData.mailType,
searchData.receiveType,
searchData.sendDate && new Date(searchData.sendDate).toISOString(),
searchData.endDate && new Date(searchData.endDate).toISOString(),
orderBy,
size,
1,
);
};
const handleDetailView = () => {
if (detailView === 'hidden') setDetailView('view');
else setDetailView('hidden');
};
const handleDetailModal = async (e, id) => {
setDetailData(await MailDetailView(token, id));
e.preventDefault();
handleDetailView();
};
// 체크박스 선택 리스트
const handleSelectCheckBox = e => {
let list = [...selectedRow];
if (e.target.checked) {
list.push(e.target.id);
setSelectedRow(list);
} else {
const filterList = list.filter(data => e.target.id !== data);
setSelectedRow(filterList);
}
};
// 전체 선택 구현
const handleAllSelect = () => {
let list = [];
if (document.getElementById('check-all').checked === true) {
dataList.list.map((data, index) => {
document.getElementsByName('select')[index].checked = true;
list.push(String(data.id));
});
} else if (document.getElementById('check-all').checked === false) {
for (let i = 0; i < dataList.list.length; i++) {
dataList.list.map((data, index) => (document.getElementsByName('select')[index].checked = false));
list = [];
}
}
setSelectedRow(list);
};
// 선택 삭제 함수
const handleSelectedDelete = () => {
let list = [];
let isChecked = false;
selectedRow.map(data => {
const row = dataList.list.find(row => row.id === Number(data));
if(row.send_status === "FINISH" || row.send_status === "RUNNING") isChecked = true;
list.push({
id: data,
});
});
if(isChecked) {
alert("발송 완료한 우편은 삭제할 수 없습니다.")
handleDeleteModalClose();
return;
}
MailDelete(token, list);
handleDeleteModalClose();
handleConfirmeModalClose();
};
// 선택 삭제 모달
const handleDeleteModalClose = () => {
if (selectedRow.length !== 0 && deleteModalClose === 'hidden') {
setDeleteModalClose('view');
} else {
setDeleteModalClose('hidden');
}
};
// 삭제, 승인, 저장 확인 모달창
const handleConfirmeModalClose = () => {
if (confirmModalClose === 'hidden') {
setConfirmModalClose('view');
} else {
setConfirmModalClose('hidden');
window.location.reload();
}
};
const handleCountSelectedRow = () => {
return currentPage > (dataList && dataList.total) / pageSize ? selectedRow.length === dataList.total % pageSize : selectedRow.length === Number(pageSize);
};
return (
<>
{userInfo.auth_list && !userInfo.auth_list.some(auth => auth.id === authType.mailRead) ? (
<AuthModal/>
) : (
<>
<Title>우편 조회 발송 관리</Title>
<FormWrapper>
<MailListSearchBar handleSearch={handleSearch} setResultData={setSearchData} />
</FormWrapper>
<ViewTableInfo total={dataList.total} total_all={dataList.total_all} handleOrderBy={handleOrderBy} handlePageSize={handlePageSize}>
{userInfo.auth_list && userInfo.auth_list.some(auth => auth.id === authType.mailDelete) && (
<Button theme={selectedRow.length === 0 ? 'disable' : 'line'} text="선택 삭제" handleClick={handleDeleteModalClose} />
)}
{userInfo.auth_list && userInfo.auth_list.some(auth => auth.id === authType.mailUpdate) && (
<Button
theme="primary"
text="우편 등록"
type="button"
handleClick={e => {
e.preventDefault();
navigate('/servicemanage/mail/mailregist');
}}
/>
)}
</ViewTableInfo>
<TableWrapper>
<TableStyle>
<caption></caption>
<thead>
<tr>
<th width="40">
<CheckBox id="check-all" handleCheck={handleAllSelect} checked={handleCountSelectedRow()} />
</th>
<th width="80">번호</th>
<th width="210">등록 일시</th>
<th width="210">발송 일시</th>
<th width="100">발송 방식</th>
<th width="100">발송 상태</th>
<th width="100">우편 타입</th>
<th width="100">수신 대상</th>
<th>우편 제목</th>
<th width="110">확인 / 수정</th>
<th width="200">등록자(처리자)</th>
</tr>
</thead>
<tbody>
{dataList.list &&
dataList.list.map(mail => (
<Fragment key={mail.row_num}>
<tr>
<td>
<CheckBox name={'select'} id={mail.id} setData={e => handleSelectCheckBox(e)} />
</td>
<td>{mail.row_num}</td>
<td>{convertKTC(mail.create_dt)}</td>
<td>{convertKTC(mail.send_dt)}</td>
<td>{mailSendType.map(data => data.value === mail.send_type && data.name)}</td>
<td>
{mail.send_status === 'FAIL' ? (
<ListState>{mailSendStatus.map(data => data.value === mail.send_status && data.name)}</ListState>
) : (
mailSendStatus.map(data => data.value === mail.send_status && data.name)
)}
</td>
<td>{mailType.map(data => data.value === mail.mail_type && data.name)}</td>
<td>{mailReceiveType.map(data => data.value === mail.receive_type && data.name)}</td>
<MailTitle>{mail.title}</MailTitle>
<td>
<Button theme="line" text="상세보기" handleClick={e => handleDetailModal(e, mail.id)} />
</td>
<td>{mail.create_by}</td>
</tr>
</Fragment>
))}
</tbody>
</TableStyle>
</TableWrapper>
<Pagination postsPerPage={pageSize} totalPosts={dataList && dataList.total_all} setCurrentPage={setCurrentPage} currentPage={currentPage} pageLimit={10} />
<MailDetailModal detailView={detailView} handleDetailView={handleDetailView} content={detailData} />
{/* 선택 삭제 모달 */}
<Modal min="440px" $padding="40px" $bgcolor="transparent" $view={deleteModalClose}>
<BtnWrapper $justify="flex-end">
<ButtonClose onClick={handleDeleteModalClose} />
</BtnWrapper>
<ModalText $align="center">
선택된 우편을 삭제하시겠습니까?
<br />
삭제 설정 정보가 제거됩니다.
</ModalText>
<BtnWrapper $gap="10px">
<Button text="취소" theme="line" size="large" width="100%" handleClick={handleDeleteModalClose} />
<Button text="확인" theme="primary" type="submit" size="large" width="100%" handleClick={handleSelectedDelete} />
</BtnWrapper>
</Modal>
{/* 확인 모달 */}
<Modal min="440px" $padding="40px" $bgcolor="transparent" $view={confirmModalClose}>
<BtnWrapper $justify="flex-end">
<ButtonClose onClick={handleConfirmeModalClose} />
</BtnWrapper>
<ModalText $align="center">삭제가 완료되었습니다.</ModalText>
<BtnWrapper $gap="10px">
<Button text="확인" theme="primary" type="submit" size="large" width="100%" handleClick={handleConfirmeModalClose} />
</BtnWrapper>
</Modal>
</>
)}
</>
);
};
export default Mail;
const ListState = styled.span`
color: #d60000;
`;

View File

@@ -0,0 +1,765 @@
import { useState, Fragment } from 'react';
import Button from '../../components/common/button/Button';
import RadioInput from '../../components/common/input/Radio';
import CheckBox from '../../components/common/input/CheckBox';
import Loading from '../../components/common/Loading';
import styled from 'styled-components';
import { Title, BtnWrapper, TextInput, SelectInput, Label, InputLabel, DatePickerWrapper, Textarea, ModalText, ButtonClose, SearchBarAlert, AlertText } from '../../styles/Components';
import IconDelete from '../../assets/img/icon/icon-delete.png';
import CloseIcon from '../../assets/img/icon/icon-close.png';
import { HourList, MinuteList, wellType } from '../../assets/data';
import { useNavigate } from 'react-router-dom';
import MailRegistUploadBtn from '../../components/ServiceManage/MailRegistUploadBtn';
import DatePickerComponent from '../../components/common/Date/DatePickerComponent';
import Modal from '../../components/common/modal/Modal';
import { MailIsItem, MailMultiRegsit, MailSingleRegist } from '../../apis';
import { authList } from '../../store/authList';
import { useRecoilValue } from 'recoil';
import { useTranslation } from 'react-i18next';
import { MailReceiver, RegistInputRow } from '../../styles/ModuleComponents';
import AuthModal from '../../components/common/modal/AuthModal';
import { authType } from '../../assets/data';
const MailRegist = () => {
const navigate = useNavigate();
const userInfo = useRecoilValue(authList);
const { t } = useTranslation();
const environment = process.env.REACT_APP_ENV;
const [doubleSubmitFlag, setDoubleSubmitFlag] = useState(false);
const [sendHour, setSendHour] = useState('00');
const [sendMin, setSendMin] = useState('00');
const [item, setItem] = useState('');
const [itemCount, setItemCount] = useState('');
const [resource, setResource] = useState('19010001');
const [resourceCount, setResourceCount] = useState(0);
const [cancelModalClose, setCancelModalClose] = useState('hidden');
const [submitModal, setSubmitModal] = useState('hidden');
const [completeModal, setCompleteModal] = useState('hidden');
const [isNullValue, setIsNullValue] = useState(false);
const [btnValidation, setBtnValidation] = useState(false);
const [excelFile, setExcelFile] = useState(null);
const [excelName, setExcelName] = useState(null);
const [isItemNullValue, setIsItemNullValue] = useState(false);
const [isResourceNullValue, setIsResourceNullValue] = useState(false);
const [confirmText, setConfirmText] = useState('');
const [alertMessage, setAlertMessage] = useState('');
const [loading, setLoading] = useState(false);
const [resultData, setResultData] = useState({
is_reserve: false,
send_dt: '',
mail_type: 'SELECT',
receive_type: 'SINGLE',
user_type: 'GUID',
mail_list: [
{
title: '',
content: '',
language: 'KO',
},
{
title: '',
content: '',
language: 'EN',
},
{
title: '',
content: '',
language: 'JA',
}
],
item_list: [],
resource_list: [],
guid: '',
});
const benItems = [
"19010003"
];
// 아이템 수량 숫자 체크
const handleItemCount = e => {
if (e.target.value === '0' || e.target.value === '-0') {
setItemCount('1');
e.target.value = '1';
} else if (e.target.value < 0) {
let plusNum = Math.abs(e.target.value);
setItemCount(plusNum);
} else {
setItemCount(e.target.value);
}
};
// 아이템 추가
const handleItemList = async () => {
if(benItems.includes(item) && environment === "live"){
alert(t('MAIL_ITEM_ADD_BEN'))
return;
}
item.length === 0 || itemCount.length === 0 ? setIsItemNullValue(true) : setIsItemNullValue(false);
const token = sessionStorage.getItem('token');
const result = await MailIsItem(token, {item: item});
if(result.data.result === "ERROR"){
alert(result.data.data.message);
return;
}
if (item.length === '' || itemCount.length === 0 || itemCount <= 0) {
setIsItemNullValue(true);
} else if (item.length !== 0) {
setIsItemNullValue(false);
const itemIndex = resultData.item_list.findIndex(
(item) => item.item === resource
);
if (itemIndex !== -1) {
alert(t('MAIL_ITEM_ADD_DUPL'));
return;
}
const newItem = { item: item, item_cnt: itemCount, item_name: result.data.data.item_info.item_name };
resultData.item_list.push(newItem);
setItem('');
setItemCount('');
}
};
const onItemRemove = id => {
let filterList = resultData.item_list && resultData.item_list.filter(item => item !== resultData.item_list[id]);
setResultData({ ...resultData, item_list: filterList });
};
// 입력창 삭제
const onLangDelete = language => {
let filterList = resultData.mail_list && resultData.mail_list.filter(el => el.language !== language);
if (filterList.length === 1) {
setBtnValidation(true);
} else {
setBtnValidation(false);
}
setResultData({ ...resultData, mail_list: filterList });
};
// 자원 수량 숫자 체크
const handleResourceCount = e => {
if (e.target.value === '0' || e.target.value === '-0') {
setResourceCount('1');
e.target.value = '1';
} else if (e.target.value < 0) {
let plusNum = Math.abs(e.target.value);
setResourceCount(plusNum);
} else {
setResourceCount(e.target.value);
}
};
// 자원 추가
const handleResourceList = () => {
resourceCount.length === 0 ? setIsItemNullValue(true) : setIsItemNullValue(false);
if (resourceCount.length === 0 || resourceCount <= 0) {
setIsItemNullValue(true);
} else {
setIsItemNullValue(false);
const itemIndex = resultData.item_list.findIndex(
(item) => item.item === resource
);
if (itemIndex !== -1) {
const item_cnt = resultData.item_list[itemIndex].item_cnt;
resultData.item_list[itemIndex].item_cnt = Number(item_cnt) + Number(resourceCount);
} else {
const name = wellType.find(well => well.value === resource).name;
const newItem = { item: resource, item_cnt: resourceCount, item_name: name };
resultData.item_list.push(newItem);
}
setResourceCount('');
}
};
const onResourceRemove = id => {
let filterList = resultData.resource_list && resultData.resource_list.filter(item => item !== resultData.resource_list[id]);
setResultData({ ...resultData, resource_list: filterList });
};
// 발송 날짜 세팅 로직
const handleSelectedDate = data => {
const sendDate = new Date(data);
const resultSendData = new Date(sendDate.getFullYear(), sendDate.getMonth(), sendDate.getDate(), sendHour, sendMin);
setResultData({ ...resultData, send_dt: resultSendData });
};
// 발송 시간 세팅 로직
const handleSendTime = e => {
let sendDate = '';
if (resultData.send_dt.length === 0 || typeof resultData.send_dt.length != 'undefined') {
sendDate = new Date();
} else {
sendDate = new Date(resultData.send_dt);
}
if (e.target.id === 'hour') setSendHour(e.target.value);
else if (e.target.id === 'min') setSendMin(e.target.value);
const result = new Date(sendDate.getFullYear(), sendDate.getMonth(), sendDate.getDate(), e.target.id === 'hour' ? e.target.value : sendHour, e.target.id === 'min' ? e.target.value : sendMin);
setResultData({ ...resultData, send_dt: result });
};
const handleSubmitModal = () => {
if (
resultData.mail_list.map(data => data.content === '' || data.title === '').includes(true) ||
(resultData.receive_type === 'MULTIPLE' ? excelFile === null : resultData.guid === '') ||
(resultData.is_reserve && resultData.send_dt.length === 0) ||
resultData.mail_type === 'SELECT' ||
alertMessage
) {
setIsNullValue(true);
} else if (submitModal === 'hidden') {
setIsNullValue(false);
setSubmitModal('view');
} else {
setSubmitModal('hidden');
}
};
// 취소 모달
const handleCancelModalClose = () => {
if (cancelModalClose === 'hidden') {
setCancelModalClose('view');
} else {
setCancelModalClose('hidden');
setConfirmText(t('MAIL_CANCEL'));
}
};
// 완료 모달창
const handleCompleteModal = () => {
if (completeModal === 'hidden') {
setCompleteModal('view');
} else {
setCompleteModal('hidden');
navigate('/servicemanage/mail');
}
};
const handleRegistMail = async () => {
setLoading(true);
const token = sessionStorage.getItem('token');
const message = await MailSingleRegist(token, resultData);
if (message.data.data.message === t('EXCEL_SELECT')) {
setConfirmText('파일 내 중복된 유저 정보가 있습니다. \n 파일을 다시 확인 후 이용해주세요.');
} else if (message.data.data.message === '저장 하였습니다.') {
setConfirmText('우편이 정상 등록되었습니다.');
} else {
setConfirmText(message.data.data.message);
}
setLoading(false);
handleCompleteModal();
handleSubmitModal();
};
const handleSingleBtn = () => {
setResultData({ ...resultData, guid: '' });
delete resultData.file_name;
document.querySelector('#fileinput').value = '';
setExcelFile(null);
};
const handleMultiBtn = () => {
delete resultData.guid;
};
// console.log('alertMessage', alertMessage);
// console.log('resultData: ', resultData);
return (
<>
{userInfo.auth_list && !userInfo.auth_list.some(auth => auth.id === authType.mailUpdate) ? (
<AuthModal/>
) : (
<>
<Title>우편 등록</Title>
<RegistGroup>
<RegistInputRow>
<CheckBox
label="예약 발송"
id="reserve"
checked={resultData.is_reserve}
setData={e => setResultData({ ...resultData, is_reserve: e.target.checked, send_dt: new Date() })}
/>
<InputItem>
{resultData.is_reserve && (
<>
<InputLabel>발송 시간</InputLabel>
<InputGroup>
<DatePickerWrapper>
<DatePickerComponent name="시작 일자" selectedDate={resultData.send_dt} handleSelectedDate={data => handleSelectedDate(data)} pastDate={new Date()} />
</DatePickerWrapper>
<SelectInput onChange={e => handleSendTime(e)} id="hour">
{HourList.map(hour => (
<option value={hour} key={hour}>
{hour}
</option>
))}
</SelectInput>
<SelectInput onChange={e => handleSendTime(e)} id="min">
{MinuteList.map(min => (
<option value={min} key={min}>
{min}
</option>
))}
</SelectInput>
</InputGroup>
</>
)}
</InputItem>
<InputItem>
<InputLabel>우편 타입</InputLabel>
<SelectInput onChange={e => setResultData({ ...resultData, mail_type: e.target.value })} value={resultData.mail_type}>
<option value="SELECT">타입 선택</option>
<option value="SYSTEM_GUID">시스템 안내</option>
<option value="INSPECTION_COMPENSATION">점검 보상</option>
<option value="RECOVER_COMPENSATION">복구 보상</option>
<option value="EVENT_COMPENSATION">이벤트 보상</option>
</SelectInput>
</InputItem>
</RegistInputRow>
<MailNotice>* 예약 발송 미선택 등록과 함께 우편이 즉시 발송됩니다.</MailNotice>
<MailReceiver>
<InputItem>
<InputLabel>수신대상</InputLabel>
<InputItem>
<SelectInput onChange={e => setResultData({ ...resultData, user_type: e.target.value })} value={resultData.user_type} disabled={resultData.receive_type !== 'SINGLE'}>
<option value="GUID">GUID</option>
<option value="NICKNAME">아바타명</option>
<option value="EMAIL">이메일</option>
</SelectInput>
</InputItem>
<div>
<InputGroup>
<RadioInput
label="단일"
id="SINGLE"
name="receiver"
value="SINGLE"
fontWeight="600"
checked={resultData.receive_type === 'SINGLE'}
handleChange={e => setResultData({ ...resultData, receive_type: e.target.id })}
handleClick={handleSingleBtn}
/>
<TextInput
placeholder={resultData.user_type === "GUID" ? "GUID 입력" : resultData.user_type === "NICKNAME" ? "아바타명 입력" : "이메일 입력"}
disabled={resultData.receive_type !== 'SINGLE'}
onChange={e => setResultData({ ...resultData, guid: e.target.value })}
value={resultData.receive_type === 'SINGLE' && resultData.guid ? resultData.guid : ''}
/>
</InputGroup>
<InputGroup>
<RadioInput
label="복수"
id="MULTIPLE"
name="receiver"
value="MULTIPLE"
fontWeight="600"
checked={resultData.receive_type === 'MULTIPLE'}
handleChange={e => setResultData({ ...resultData, receive_type: e.target.id })}
handleClick={handleMultiBtn}
/>
<MailRegistUploadBtn
disabled={resultData.receive_type !== 'MULTIPLE'}
setResultData={setResultData}
resultData={resultData}
excelFile={excelFile}
alertMessage={alertMessage}
setAlertMessage={setAlertMessage}
setExcelFile={setExcelFile}
excelName={excelName}
setExcelName={setExcelName}
/>
</InputGroup>
</div>
</InputItem>
</MailReceiver>
</RegistGroup>
{resultData.mail_list.map((data, idx) => {
return (
<Fragment key={idx}>
<MailRegistBox>
<LangArea>
언어 : {data.language}
{btnValidation === false ? (
<BtnClose
onClick={e => {
e.preventDefault();
onLangDelete(data.language);
}}
/>
) : (
<BtnClose opacity="10%" />
)}
</LangArea>
<MailRegistTable>
<tbody>
<tr>
<th width="120">
<Label>제목</Label>
</th>
<td>
<InputItem>
<TextInput
placeholder="우편 제목 입력"
maxLength="30"
id={data.language}
value={data.title}
onChange={e => {
if (e.target.value.length > 30) {
return;
}
let list = [...resultData.mail_list];
let findIndex = resultData.mail_list && resultData.mail_list.findIndex(item => item.language === e.target.id);
list[findIndex].title = e.target.value.trimStart();
setResultData({ ...resultData, mail_list: list });
}}
/>
</InputItem>
<MailNotice $color={data.title.length > 29 ? 'red' : '#666'}>* 최대 등록 가능 글자수 ({data.title.length}/30)</MailNotice>
</td>
</tr>
<tr>
<th>
<Label>내용</Label>
</th>
<td>
<Textarea
maxLength="2000"
value={data.content}
id={data.language}
onChange={e => {
if (e.target.value.length > 2000) {
return;
}
let list = [...resultData.mail_list];
let findIndex = resultData.mail_list && resultData.mail_list.findIndex(item => item.language === e.target.id);
list[findIndex].content = e.target.value.trimStart();
setResultData({ ...resultData, mail_list: list });
}}
/>
<MailNotice $color={data.content.length > 1999 ? 'red' : '#666'}>* 최대 등록 가능 글자수 ({data.content.length}/2000)</MailNotice>
</td>
</tr>
</tbody>
</MailRegistTable>
</MailRegistBox>
</Fragment>
);
})}
<MailRegistBox>
<MailRegistTable>
<tbody>
<tr>
<th width="120">
<Label>아이템 첨부</Label>
</th>
<td>
<InputItem>
<TextInput placeholder="Item Meta id 입력" value={item} onChange={e => setItem(e.target.value.trimStart())} />
<TextInput placeholder="수량" type="number" value={itemCount} onChange={e => handleItemCount(e)} width="100px" />
<Button text="추가" theme={itemCount.length === 0 || item.length === 0 ? 'disable' : 'search'} handleClick={handleItemList} width="100px" height="35px" />
</InputItem>
{/* {isItemNullValue && <SearchBarAlert $marginTop="15px">필수값을 입력해주세요.</SearchBarAlert>}
<div>
{resultData.item_list && (
<ItemList>
{resultData.item_list.map((data, index) => {
return (
<Item key={index}>
<span>
{data.item}({data.item_cnt})
</span>
<BtnDelete onClick={() => onItemRemove(index)}></BtnDelete>
</Item>
);
})}
</ItemList>
)}
</div> */}
</td>
</tr>
<tr>
<th width="120">
<Label>자원 첨부</Label>
</th>
<td>
<InputItem>
<SelectInput onChange={e => setResource(e.target.value)} value={resource}>
<option value="19010001">골드</option>
<option value="19010002">사파이어</option>
<option value="19010005">루비</option>
</SelectInput>
<TextInput placeholder="수량" type="number" value={resourceCount} onChange={e => handleResourceCount(e)} width="200px" />
<Button text="추가" theme={resourceCount.length === 0 || resource.length === 0 ? 'disable' : 'search'} handleClick={handleResourceList} width="100px" height="35px" />
</InputItem>
{isItemNullValue && <SearchBarAlert $marginTop="15px">필수값을 입력해주세요.</SearchBarAlert>}
<div>
{resultData.item_list && (
<ItemList>
{resultData.item_list.map((data, index) => {
return (
<Item key={index}>
<span>
{data.item_name}[{data.item}] ({data.item_cnt})
</span>
<BtnDelete onClick={() => onItemRemove(index)}></BtnDelete>
</Item>
);
})}
</ItemList>
)}
</div>
</td>
</tr>
</tbody>
</MailRegistTable>
</MailRegistBox>
{isNullValue && (
<SearchBarAlert $align="right" $padding="0 0 15px">
필수값을 입력해주세요.
</SearchBarAlert>
)}
<BtnWrapper $justify="flex-end" $gap="10px">
<Button text="취소" theme="line" handleClick={handleCancelModalClose} />
<Button
type="submit"
text="등록"
theme={
resultData.mail_list.map(data => data.content === '' || data.title === '').includes(true) ||
(resultData.receive_type === 'MULTIPLE' ? excelFile === null : resultData.guid === '') ||
(resultData.is_reserve && resultData.send_dt.length === 0) ||
resultData.mail_type === 'SELECT' ||
alertMessage
? 'disable'
: 'primary'
}
handleClick={handleSubmitModal}
/>
</BtnWrapper>
{/* 등록 모달 */}
<Modal min="440px" $padding="40px" $bgcolor="transparent" $view={submitModal}>
<BtnWrapper $justify="flex-end">
<ButtonClose onClick={handleSubmitModal} />
</BtnWrapper>
<ModalText $align="center">우편을 등록하시겠습니까?</ModalText>
<BtnWrapper $gap="10px">
<Button text="취소" theme="line" size="large" width="100%" handleClick={handleSubmitModal} />
<Button
text="확인"
theme="primary"
type="submit"
size="large"
width="100%"
handleClick={() => {
doubleSubmitFlag || handleRegistMail();
setDoubleSubmitFlag(true);
}}
/>
</BtnWrapper>
</Modal>
{/* 완료 모달 */}
<Modal min="440px" $padding="40px" $bgcolor="transparent" $view={completeModal}>
<BtnWrapper $justify="flex-end">
<ButtonClose onClick={handleCompleteModal} />
</BtnWrapper>
<ModalText $align="center">{confirmText}</ModalText>
<BtnWrapper $gap="10px">
<Button text="확인" theme="primary" type="submit" size="large" width="100%" handleClick={handleCompleteModal} />
</BtnWrapper>
</Modal>
{/* 취소 모달 */}
<Modal min="440px" $padding="40px" $bgcolor="transparent" $view={cancelModalClose}>
<BtnWrapper $justify="flex-end">
<ButtonClose onClick={handleCancelModalClose} />
</BtnWrapper>
<ModalText $align="center">
우편 등록을 취소하시겠습니까?
<br />
취소 설정된 값은 반영되지 않습니다.
</ModalText>
<BtnWrapper $gap="10px">
<Button text="취소" theme="line" size="large" width="100%" handleClick={handleCancelModalClose} />
<Button
text="확인"
theme="primary"
type="submit"
size="large"
width="100%"
handleClick={() => {
handleCancelModalClose();
handleCompleteModal();
}}
/>
</BtnWrapper>
</Modal>
{loading && <Loading/>}
</>
)}
</>
);
};
export default MailRegist;
const InputRow = styled.div`
display: flex;
flex-wrap: wrap;
gap: 10px 50px;
`;
const InputItem = styled.div`
display: flex;
align-items: center;
gap: 20px;
${TextInput},${SelectInput} {
height: 35px;
font-size: 14px;
}
${TextInput} {
padding: 0 20px;
}
${SelectInput} {
width: max-content;
}
`;
const InputGroup = styled.div`
display: flex;
gap: 5px;
align-items: center;
`;
const MailNotice = styled.span`
font-size: 12px;
font-weight: 300;
color: ${props => props.$color || '#999'};
margin-top: 10px;
display: block;
`;
const RegistGroup = styled.div`
display: flex;
width: 100%;
flex-flow: column;
padding: 20px;
border-top: 1px solid #000;
border-bottom: 1px solid #000;
font-size: 14px;
margin-bottom: 40px;
${MailNotice} {
margin-bottom: 20px;
}
`;
const BtnClose = styled.button`
width: 16px;
height: 16px;
background: url(${CloseIcon}) 50% 50% no-repeat;
position: absolute;
top: 50%;
transform: translate(0, -50%);
right: 20px;
opacity: ${props => props.opacity};
`;
const LangArea = styled.div`
background: #f9f9f9;
padding: 10px 20px;
font-size: 14px;
font-weight: 600;
position: relative;
`;
const MailRegistBox = styled.div`
margin-bottom: 20px;
border-top: 1px solid #999;
border-bottom: 1px solid #999;
`;
const MailRegistTable = styled.table`
th {
vertical-align: top;
line-height: 35px;
}
th,
td {
padding: 15px 0;
border-bottom: 1px solid #f6f6f6;
}
td {
${InputItem} {
gap: 5px;
}
${TextInput} {
max-width: 600px;
padding: 0 15px;
}
${Textarea} {
width: 100%;
border: 1px solid #d9d9d9;
border-radius: 5px;
height: 255px;
padding: 15px;
&:focus {
border: 1px solid #2c2c2c;
}
}
}
`;
const ItemList = styled.ul`
display: flex;
flex-wrap: wrap;
gap: 20px;
padding: 10px 20px;
`;
const Item = styled.li`
display: flex;
gap: 5px;
align-items: center;
`;
const BtnDelete = styled.button`
width: 12px;
height: 12px;
background: url(${IconDelete}) 50% 50% no-repeat;
`;

View File

@@ -0,0 +1,344 @@
import { Fragment, useEffect, useState } from 'react';
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';
import { ReportListDetailModal, ReportListAnswerModal, ReportListSearchBar, ReportListSummary } from '../../components/ServiceManage';
import Modal from '../../components/common/modal/Modal';
import { useNavigate } from 'react-router-dom';
import { authList } from '../../store/authList';
import { useRecoilValue } from 'recoil';
import { ReportListView, ReportListDetailView, ReportReplyDetail } from '../../apis/Report';
import Pagination from '../../components/common/Pagination/Pagination';
import { convertKTC } from '../../utils';
import AuthModal from '../../components/common/modal/AuthModal';
import { authType } from '../../assets/data';
import ViewTableInfo from '../../components/common/Table/ViewTableInfo';
const reportType = [
{ value: 'ALL', name: '전체' },
{ value: 'UNMANNERED_ACT', name: '비매너 행위' },
{ value: 'USE_UNHEALTHY_NAMES', name: '불건전 이름 사용' },
{ value: 'CASH_TRADING', name: '현금거래 행위' },
{ value: 'INTERFERENCE_GAME', name: '게임 진행 방해' },
{ value: 'INTERFERENCE_SERVICE', name: '운영서비스 방해' },
{ value: 'ACCOUNT_EXPLOITATION', name: '계정도용' },
{ value: 'BUG_ABUSING', name: '버그/어뷰징' },
{ value: 'USE_HACK', name: '불법프로그램 사용' },
{ value: 'LEAK_PERSONAL_INFO', name: '개인정보 유출' },
{ value: 'PRETENDING_GM', name: '운영자 사칭' },
];
const ReportList = () => {
const navigate = useNavigate();
const token = sessionStorage.getItem('token');
const userInfo = useRecoilValue(authList);
// 권한 설정용
let deleteAuth = userInfo.auth_list && userInfo.auth_list.some(auth => auth.id === 31); // 삭제 권한
let replyAuth = userInfo.auth_list && userInfo.auth_list.some(auth => auth.id === 31); // 답변 권한
let d = new Date();
const START_DATE = new Date(new Date(d.setDate(d.getDate() - 1)).setHours(0, 0, 0, 0));
const END_DATE = new Date();
// 페이징용
const [currentPage, setCurrentPage] = useState(1);
const [pageSize, setPageSize] = useState(50);
const [selectedRow, setSelectedRow] = useState([]);
const [searchData, setSearchData] = useState({
startDate: START_DATE,
endDate: END_DATE,
reportType: 'ALL',
status: 'ALL',
searchType: 'ALL',
searchKey: '',
});
const [orderBy, setOrderBy] = useState('DESC');
const [detailView, setDetailView] = useState('hidden');
const [answerView, setAnswerView] = useState('hidden');
const [dataList, setDataList] = useState([]);
// 신고내역 상세 조회용
const [detailData, setDetailData] = useState([]);
// 신고답변 상세 조회용
const [replyData, setReplyData] = useState([]);
const [pkId, setPkId] = useState('');
const [skId, setSkId] = useState('');
const [deleteModalClose, setDeleteModalClose] = useState('hidden');
const [confirmModalClose, setConfirmModalClose] = useState('hidden');
useEffect(() => {
fetchData(START_DATE, END_DATE, 'ALL', 'ALL', 'ALL', '');
}, [currentPage]);
const fetchData = async (startDate, endDate, reportType, status, searchType, searchKey, order, size) => {
await ReportListView(
token,
startDate && new Date(startDate).toISOString().split('.')[0],
endDate && new Date(endDate).toISOString().split('.')[0],
reportType && reportType,
status && status,
searchType && searchType,
searchKey && searchKey,
order ? order : orderBy,
size ? size : pageSize,
currentPage,
).then(data => {
setDataList(data);
})
};
const handlePageSize = e => {
const size = e.target.value;
setPageSize(size);
setCurrentPage(1);
fetchData(searchData.startDate, searchData.endDate, searchData.reportType, searchData.status, searchData.searchType, searchData.searchKey, orderBy, size, 1);
};
const handleDetail = async (e, pk, sk) => {
const pkData = encodeURIComponent(pk);
const skData = encodeURIComponent(sk);
e.preventDefault();
setPkId(pk);
setSkId(sk);
setDetailData(await ReportListDetailView(token, pkData, skData));
handleDetailView();
};
// 상세 정보 모달 띄우기
const handleDetailView = () => {
if (detailView === 'hidden') {
setDetailView('block');
} else {
setDetailView('hidden');
}
};
// 신고 답변 불러오기
const handleReply = async e => {
const pk = encodeURIComponent(pkId);
const sk = encodeURIComponent(skId);
setReplyData(await ReportReplyDetail(token, pk, sk));
handleReplyView();
};
// 답변 모달 띄우기
const handleReplyView = () => {
if (answerView === 'hidden') {
setDetailView('hidden');
setAnswerView('block');
} else {
setAnswerView('hidden');
setDetailView('block');
}
};
// 검색 함수
const handleSearch = (startDate, endDate, reportType, status, searchType, searchKey) => {
fetchData(startDate && new Date(startDate).toISOString(), endDate && new Date(endDate).toISOString(), reportType, status, searchType, searchKey, orderBy, pageSize);
};
// 오름차순 내림차순
const handleOrderBy = e => {
const order = e.target.value;
setOrderBy(order);
fetchData(
searchData.startDate && new Date(searchData.startDate).toISOString().split('.')[0],
searchData.endDate && new Date(searchData.endDate).toISOString().split('.')[0],
searchData.reportType,
searchData.status,
searchData.searchKey,
order,
pageSize,
);
};
// 선택 삭제 함수
const handleSelectedDelete = () => {
let list = [];
selectedRow.map(data =>
list.push({
id: data,
}),
);
// ReportDelete(token, list);
handleDeleteModalClose();
handleConfirmeModalClose();
};
// 선택 삭제 모달
const handleDeleteModalClose = () => {
if (selectedRow.length !== 0 && deleteModalClose === 'hidden') {
setDeleteModalClose('view');
} else {
setDeleteModalClose('hidden');
}
};
// 삭제, 승인, 저장 확인 모달창
const handleConfirmeModalClose = () => {
if (confirmModalClose === 'hidden') {
setConfirmModalClose('view');
} else {
setConfirmModalClose('hidden');
window.location.reload();
}
};
// 체크박스 선택 리스트
const handleSelectCheckBox = e => {
let list = [...selectedRow];
if (e.target.checked) {
list.push(e.target.id);
setSelectedRow(list);
} else {
const filterList = list.filter(data => e.target.id !== data);
setSelectedRow(filterList);
}
};
// 전체 선택 구현
const handleAllSelect = () => {
let list = [];
if (document.getElementById('check-all').checked === true) {
dataList.list &&
dataList.list.map((data, index) => {
document.getElementsByName('select')[index].checked = true;
list.push(String(data.id));
});
} else if (document.getElementById('check-all').checked === false) {
for (let i = 0; i < dataList.list.length; i++) {
dataList.list && dataList.list.map((data, index) => (document.getElementsByName('select')[index].checked = false));
list = [];
}
}
setSelectedRow(list);
};
const handleCountSelectedRow = () => {
return currentPage > (dataList && dataList.total) / pageSize ? selectedRow.length === dataList.total % pageSize : selectedRow.length === Number(pageSize);
};
return (
<>
{userInfo.auth_list && !userInfo.auth_list.some(auth => auth.id === authType.reportRead) ? (
<AuthModal/>
) : (
<>
<Title>신고내역 조회 답변</Title>
<ReportListSummary />
<FormWrapper>
<ReportListSearchBar handleSearch={handleSearch} setResultData={setSearchData} />
</FormWrapper>
<ViewTableInfo handlePageSize={handlePageSize} handleOrderBy={handleOrderBy} total={dataList.total} total_all={dataList.total_all}>
{deleteAuth && <Button theme={'disable'} text="선택 삭제" />}
</ViewTableInfo>
<TableWrapper>
<TableStyle>
<caption></caption>
<thead>
<tr>
<th width="40">
<CheckBox id="check-all" handleCheck={handleAllSelect} checked={handleCountSelectedRow()} />
</th>
<th width="80">번호</th>
<th width="180">신고일자</th>
<th width="200">신고자</th>
<th width="15%">신고 유형</th>
<th width="110">확인 / 답변</th>
<th width="90">상태</th>
<th width="180">해결 일자</th>
<th width="15%">담당자(이메일주소)</th>
{deleteAuth && <th width="80">삭제</th>}
</tr>
</thead>
<tbody>
{dataList.list &&
dataList.list.map((report, index) => (
<Fragment key={index}>
<tr>
<td>
<CheckBox name={'select'} id={report.row_num} setData={e => handleSelectCheckBox(e)} />
</td>
<td>{report.row_num}</td>
<td>{convertKTC(report.create_time, false)}</td>
<td>{report.reporter_nickname}</td>
<td>{reportType.map(data => data.value === report.report_type && data.name)}</td>
<td>
<Button theme="line" text="상세보기" handleClick={e => handleDetail(e, report.pk, report.sk)} />
</td>
<td>{report.state === 'UNRESOLVED' ? <ListState>미해결</ListState> : ''}</td>
<td>
{report.resolution_time && convertKTC(report.resolution_time, false)}
</td>
<td>{report.manager_email}</td>
{deleteAuth && (
<td>
<Button theme="line" text="삭제" />
</td>
)}
</tr>
</Fragment>
))}
</tbody>
</TableStyle>
</TableWrapper>
<Pagination postsPerPage={pageSize} totalPosts={dataList && dataList.total_all} setCurrentPage={setCurrentPage} currentPage={currentPage} pageLimit={10} />
<ReportListDetailModal detailView={detailView} handleReply={handleReply} handleDetailView={handleDetailView} detailData={detailData} replyData={replyData} replyAuth={replyAuth} />
<ReportListAnswerModal answerView={answerView} setAnswerView={setAnswerView} detailData={detailData} replyData={replyData} pkId={pkId} skId={skId} />
{/* 선택 삭제 모달 */}
<Modal min="440px" $padding="40px" $bgcolor="transparent" $view={deleteModalClose}>
<BtnWrapper $justify="flex-end">
<ButtonClose onClick={handleDeleteModalClose} />
</BtnWrapper>
<ModalText $align="center">
선택된 우편을 삭제하시겠습니까?
<br />
삭제 설정 정보가 제거됩니다.
</ModalText>
<BtnWrapper $gap="10px">
<Button text="취소" theme="line" size="large" width="100%" handleClick={handleDeleteModalClose} />
<Button text="확인" theme="primary" type="submit" size="large" width="100%" handleClick={handleSelectedDelete} />
</BtnWrapper>
</Modal>
{/* 확인 모달 */}
<Modal min="440px" $padding="40px" $bgcolor="transparent" $view={confirmModalClose}>
<BtnWrapper $justify="flex-end">
<ButtonClose onClick={handleConfirmeModalClose} />
</BtnWrapper>
<ModalText $align="center">삭제가 완료되었습니다.</ModalText>
<BtnWrapper $gap="10px">
<Button text="확인" theme="primary" type="submit" size="large" width="100%" handleClick={handleConfirmeModalClose} />
</BtnWrapper>
</Modal>
</>
)}
</>
);
};
export default ReportList;
const ListState = styled.span`
color: #d60000;
`;

View File

@@ -0,0 +1,339 @@
import { useState, Fragment, useEffect } from 'react';
import CheckBox from '../../components/common/input/CheckBox';
import Modal from '../../components/common/modal/Modal';
import Button from '../../components/common/button/Button';
import styled from 'styled-components';
import { Title, FormWrapper, TableInfo, ListCount, ListOption, TableStyle, SelectInput, TableWrapper, BtnWrapper, ButtonClose, ModalText } from '../../styles/Components';
import { useNavigate } from 'react-router-dom';
import { UserBlockDetailModal, UserBlockSearchBar } from '../../components/ServiceManage/';
import { BlackListView, BlackListDetail, BlackListDelete } from '../../apis';
import Pagination from '../../components/common/Pagination/Pagination';
import { authList } from '../../store/authList';
import { useRecoilValue } from 'recoil';
import { authType, blockPeriod, blockSanctions, blockStatus } from '../../assets/data';
import AuthModal from '../../components/common/modal/AuthModal';
const UserBlock = () => {
const token = sessionStorage.getItem('token');
const userInfo = useRecoilValue(authList);
const navigate = useNavigate();
// 페이지 네이션 관련
const [currentPage, setCurrentPage] = useState(1);
const [pageSize, setPageSize] = useState(50);
// 데이터 조회 관련
const [dataList, setDataList] = useState([]);
const [detailView, setDetailView] = useState('hidden');
const [selectedRow, setSelectedRow] = useState([]);
const [selectedData, setSelectedData] = useState([]);
const [detail, setDetail] = useState([]);
const [requestValue, setRequestValue] = useState([]);
// 모달 관련 변수
const [confirmModal, setConfirmModal] = useState('hidden');
const [completeModal, setCompleteModal] = useState('hidden');
// 검색 관련 변수
const [orderBy, setOrderBy] = useState('DESC');
const [searchData, setSearchData] = useState({
searchType: 'GUID',
searchKey: '',
status: 'ALL',
sanctions: 'ALL',
period: 'ALL',
});
// 등록하기 모달 연결
const handleDetail = async dataValue => {
if (detailView === 'hidden') {
setDetailView('block');
} else {
setDetailView('hidden');
setDetail([]);
}
setSelectedData(dataValue);
const id = dataValue.id;
if (id) {
setDetail(await BlackListDetail(token, id));
}
};
const fetchData = async (searchType, data, email, status, sanctions, period, order, size) => {
setDataList(await BlackListView(token, searchType, data, email, status, sanctions, period, order ? order : orderBy, size ? size : pageSize, currentPage));
};
// 검색 기능
const handleSearch = (searchType, data, email, status, sanctions, period) => {
fetchData(searchType, data, email, status, sanctions, period);
};
// 배열 정렬
const handleOrderBy = e => {
const order = e.target.value;
setOrderBy(order);
fetchData(searchData.searchType, searchData.searchKey, searchData.email, searchData.status, searchData.sanctions, searchData.period, order, pageSize);
};
const handlePageSize = e => {
const size = e.target.value;
setPageSize(size);
setCurrentPage(1);
fetchData(searchData.searchType, searchData.searchKey, searchData.email, searchData.status, searchData.sanctions, searchData.period, orderBy, size, 1);
};
useEffect(() => {
fetchData(searchData.searchType, searchData.searchKey, searchData.email, searchData.status, searchData.sanctions, searchData.period, orderBy, pageSize);
setSelectedRow([]);
}, [currentPage]);
// 전체 선택
const handleAllSelect = () => {
const checkAll = document.getElementById('check-all');
let list = [];
if (checkAll.checked === true) {
dataList.list.map((data, index) => {
document.getElementsByName('select')[index].checked = true;
list.push(String(data.id));
});
} else if (checkAll.checked === false) {
for (let i = 0; i < dataList.list.length; i++) {
dataList.list.map((data, index) => (document.getElementsByName('select')[index].checked = false));
list = [];
}
}
setSelectedRow(list);
};
// 일부 선택
const handleSelectCheckBox = e => {
let list = [...selectedRow];
if (e.target.checked) {
list.push(e.target.id);
setSelectedRow(list);
} else {
const filterList = list.filter(data => e.target.id !== data);
setSelectedRow(filterList);
}
};
// 삭제 여부 모달
const handleConfirmeModalClose = () => {
if (confirmModal === 'hidden') {
setConfirmModal('view');
} else {
setConfirmModal('hidden');
setRequestValue([]);
}
};
// 선택, 복수 삭제 모달창
const handleConfirmModal = data => {
if (data.id) {
setSelectedData(data.id);
if (selectedData) {
setRequestValue(data.id);
}
} else if (selectedRow) {
setRequestValue(selectedRow);
}
handleConfirmeModalClose();
};
// 완료 확인 모달
const handleCompleteModal = () => {
if (completeModal === 'hidden') {
setCompleteModal('view');
} else {
setCompleteModal('hidden');
setRequestValue([]);
handleConfirmeModalClose();
window.location.reload();
}
};
// 삭제 기능 구현
const handleDelete = () => {
let list = [];
if (requestValue === selectedRow) {
selectedRow.map(data =>
list.push({
id: data,
}),
);
BlackListDelete(token, list);
// console.log(list);
// console.log(selectedRow);
handleCompleteModal();
} else if (requestValue !== selectedRow) {
list.push({ id: selectedData });
// console.log(list);
BlackListDelete(token, list);
handleCompleteModal();
}
};
const handleCountSelectedRow = () => {
return currentPage > (dataList && dataList.total) / pageSize ? selectedRow.length === dataList.total % pageSize : selectedRow.length === Number(pageSize);
};
return (
<>
{userInfo.auth_list && !userInfo.auth_list.some(auth => auth.id === authType.blackListRead) ? (
<AuthModal/>
) : (
<>
<Title>이용자 제재 조회 등록</Title>
<FormWrapper>
<UserBlockSearchBar handleSearch={handleSearch} setResultData={setSearchData} />
</FormWrapper>
<TableInfo>
<ListCount>
: {dataList && dataList.total} / {dataList && dataList.total_all}
</ListCount>
<ListOption>
<SelectInput
name=""
id=""
className="input-select"
onChange={e => {
handleOrderBy(e);
}}>
<option value="DESC">내림차순</option>
<option value="ASC">오름차순</option>
</SelectInput>
<SelectInput
name=""
id=""
onChange={e => {
handlePageSize(e);
}}
className="input-select">
<option value="50">50</option>
<option value="100">100</option>
</SelectInput>
{userInfo.auth_list && userInfo.auth_list.some(auth => auth.id === 30) && (
<Button theme={selectedRow.length === 0 ? 'disable' : 'line'} type="submit" text="선택 삭제" id={selectedRow} handleClick={() => handleConfirmModal(selectedRow)} />
)}
{userInfo.auth_list && userInfo.auth_list.some(auth => auth.id === 25) && (
<Button
theme="primary"
type="submit"
text="제재 등록"
handleClick={e => {
e.preventDefault();
navigate('/servicemanage/userblock/userblockregist');
}}
/>
)}
</ListOption>
</TableInfo>
<TableWrapper>
<TableStyle>
<caption></caption>
<thead>
<tr>
<th width="40">
<CheckBox id="check-all" handleCheck={handleAllSelect} checked={handleCountSelectedRow()} />
</th>
<th width="80">번호</th>
<th width="20%">GUID</th>
<th width="20%">아바타명</th>
<th width="100">상태</th>
<th width="100">제재 기간</th>
<th width="250">제재 사유</th>
<th width="150">등록자</th>
<th width="110">상세정보</th>
{userInfo.auth_list && userInfo.auth_list.some(auth => auth.id === 30) && <th width="80">삭제</th>}
</tr>
</thead>
<tbody>
{dataList.list &&
dataList.list.map(data => (
<Fragment key={data.id}>
<tr>
<td>
<CheckBox name={'select'} id={data.id} setData={e => handleSelectCheckBox(e)} />
</td>
<td>{data.row_num}</td>
<td>{data.guid}</td>
<td>{data.nickname}</td>
{data.status === 'INPROGRESS' ? (
<td>
<ListState>{blockStatus.map(item => item.value === data.status && item.name)}</ListState>
</td>
) : (
<td>{blockStatus.map(item => item.value === data.status && item.name)}</td>
)}
<td>{blockPeriod.map(item => item.value === data.period && item.name)}</td>
<td>{blockSanctions.map(item => item.value === data.sanctions && item.name)}</td>
<td>{data.create_by}</td>
<td>
<Button theme="line" text="상세보기" handleClick={() => handleDetail(data)} />
</td>
{userInfo.auth_list && userInfo.auth_list.some(auth => auth.id === 30) && (
<td>
<Button theme="line" id={data.id} name="single" text="삭제" handleClick={() => handleConfirmModal(data)} />
</td>
)}
</tr>
</Fragment>
))}
</tbody>
</TableStyle>
</TableWrapper>
<Pagination postsPerPage={pageSize} totalPosts={dataList && dataList.total_all} setCurrentPage={setCurrentPage} currentPage={currentPage} pageLimit={10} />
<UserBlockDetailModal stateModal={detailView} data={detail} handleModal={handleDetail} />
{/* 선택 삭제 모달 */}
<Modal min="440px" $padding="40px" $bgcolor="transparent" $view={confirmModal}>
<BtnWrapper $justify="flex-end">
<ButtonClose onClick={handleConfirmeModalClose} />
</BtnWrapper>
<ModalText $align="center">
제재 대상을 삭제하시겠습니까?
<br />
삭제 제재가 해제됩니다.
</ModalText>
<BtnWrapper $gap="10px">
<Button text="취소" theme="line" size="large" width="100%" handleClick={handleConfirmeModalClose} />
<Button text="확인" theme="primary" type="submit" size="large" width="100%" handleClick={handleDelete} />
</BtnWrapper>
</Modal>
{/* 완료창 모달 */}
{/* 삭제 완료 모달 */}
<Modal min="440px" $padding="40px" $bgcolor="transparent" $view={completeModal}>
<BtnWrapper $justify="flex-end">
<ButtonClose onClick={handleCompleteModal} />
</BtnWrapper>
<ModalText $align="center">삭제가 완료되었습니다.</ModalText>
<BtnWrapper $gap="10px">
<Button text="확인" theme="primary" type="submit" size="large" width="100%" handleClick={handleCompleteModal} />
</BtnWrapper>
</Modal>
</>
)}
</>
);
};
export default UserBlock;
const ListState = styled.span`
color: #d60000;
`;

View File

@@ -0,0 +1,686 @@
import { useState, useEffect, useRef } from 'react';
import Button from '../../components/common/button/Button';
import RadioInput from '../../components/common/input/Radio';
import styled from 'styled-components';
import { Title, BtnWrapper, TextInput, SelectInput, DatePickerWrapper, ButtonClose, ModalText } from '../../styles/Components';
import Modal from '../../components/common/modal/Modal';
import DatePickerComponent from '../../components/common/Date/DatePickerComponent';
import { blockPeriod, blockSanctions, blockType, HourList, MinuteList } from '../../assets/data';
import DatePicker, { registerLocale } from 'react-datepicker';
import { ko } from 'date-fns/esm/locale';
import 'react-datepicker/dist/react-datepicker.css';
import { getMonth, getYear, setHours } from 'date-fns';
import range from 'lodash/range';
import { useNavigate } from 'react-router-dom';
import UserBlockUploadBtn from '../../components/ServiceManage/UserBlockUploadBtn';
import { BlackListRegist, BlackListMultipleUpload } from '../../apis';
import { authList } from '../../store/authList';
import { useRecoilValue } from 'recoil';
registerLocale('ko', ko);
const UserBlockRegist = () => {
const token = sessionStorage.getItem('token');
const navigate = useNavigate();
const userInfo = useRecoilValue(authList);
//시간 관련 변수
const [sendHour, setSendHour] = useState('00');
const [sendMin, setSendMin] = useState('00');
const [endHour, setEndHour] = useState('00');
const [endMin, setEndMin] = useState('00');
const [selectData, setSelectData] = useState('single');
const [selectSingle, setSelectSingle] = useState(false);
const [selectMulti, setSelectMulti] = useState(true);
const [startDate, setStartDate] = useState(null);
const years = range(1990, getYear(new Date()) + 1, 1);
const months = ['01', '02', '03', '04', '05', '06', '07', '08', '09', '10', '11', '12'];
const [registModalClose, setRegistModalClose] = useState('hidden');
const [completeModalClose, setCompleteModalClose] = useState('hidden');
const [isNullValue, setIsNullValue] = useState(false);
const [guidList, setGuidList] = useState([]);
const [typeError, setTypeError] = useState(true);
const [resultData, setResultData] = useState({
list: { guid: '' },
type: '',
sanctions: '',
period: '',
start_dt: '',
end_dt: '',
});
const [confirmText, setConfirmText] = useState('');
const handleChange = e => {
setSelectData(e.target.value);
handleReset();
};
const handleReset = () => {
setResultData({
list: { guid: '' },
type: '',
sanctions: '',
period: '',
start_dt: '',
end_dt: '',
});
};
const [previewModal, setPreviewModal] = useState('hidden');
const handlePreview = e => {
e.preventDefault();
previewModal === 'hidden' ? setPreviewModal('view') : setPreviewModal('hidden');
};
useEffect(() => {
if (selectData === 'single') {
setSelectSingle(false);
setSelectMulti(true);
if (resultData.period === 'WARNING') {
setEndHour('01');
} else {
setEndHour('00');
}
} else if (selectData === 'multi') {
setSelectSingle(true);
setSelectMulti(false);
if (resultData.period === 'WARNING') {
setEndHour('01');
} else {
setEndHour('00');
}
}
let list = [];
if (guidList) {
guidList.map(data =>
list.push({
guid: data.guid,
nickname: data.nickname,
}),
);
setResultData({ ...resultData, list: list });
}
}, [selectData, guidList]);
// 제재 시간 세팅 로직
const handleSendTime = e => {
let sendDate = new Date(resultData.start_dt);
if (resultData.start_dt.length === 0) {
sendDate = new Date();
} else {
sendDate = new Date(resultData.start_dt);
}
const result = new Date(sendDate.getFullYear(), sendDate.getMonth(), sendDate.getDate(), e.target.id === 'hour' ? e.target.value : sendHour, e.target.id === 'min' ? e.target.value : sendMin);
const blockDay = resultData.period.slice(1) > 0 ? Number(resultData.period.slice(1)) + 1 : 0;
const permanentDay = 2999 - sendDate.getFullYear();
if (e.target.id === 'hour') setSendHour(e.target.value);
else if (e.target.id === 'min') setSendMin(e.target.value);
if (e.target.id === 'hour' && resultData.period === 'WARNING') {
let targetHour = String(e.target.value);
let plusHour = Number(e.target.value) + 1;
if (targetHour < 9) {
setEndHour('0' + String(plusHour));
} else {
setEndHour(plusHour);
}
} else if (resultData.period !== 'WARNING' && e.target.id === 'hour') {
setEndHour(e.target.value);
}
if (resultData.period === 'WARNING') {
const EndResult = new Date(
sendDate.getFullYear(),
sendDate.getMonth(),
sendDate.getDate() + blockDay,
e.target.id === 'hour' ? e.target.value : sendHour,
e.target.id === 'min' ? e.target.value : sendMin + 60,
);
setResultData({ ...resultData, start_dt: result, end_dt: EndResult });
} else if (resultData.period === 'PERMANENT') {
const EndResult = new Date(
sendDate.getFullYear() + permanentDay,
sendDate.getMonth(),
sendDate.getDate() + blockDay,
e.target.id === 'hour' ? e.target.value : sendHour,
e.target.id === 'min' ? e.target.value : sendMin,
);
setResultData({ ...resultData, start_dt: result, end_dt: EndResult });
} else if (blockDay > 0) {
const EndResult = new Date(
sendDate.getFullYear(),
sendDate.getMonth(),
sendDate.getDate() + blockDay,
e.target.id === 'hour' ? e.target.value : sendHour,
e.target.id === 'min' ? e.target.value : sendMin,
);
setResultData({ ...resultData, start_dt: result, end_dt: EndResult });
}
};
const handleSelectedDate = data => {
setSendHour('00');
setSendMin('00');
setEndHour('00');
const sendDate = new Date(data);
const blockDay = Number(resultData.period.slice(1));
const permanentDay = 2999 - sendDate.getFullYear();
if (endHour === '00' && resultData.period === 'WARNING') {
setEndHour('01');
}
const resultSendData = new Date(sendDate.getFullYear(), sendDate.getMonth(), sendDate.getDate(), sendHour, sendMin);
if (blockDay > 0) {
const resultEndData = new Date(sendDate.getFullYear(), sendDate.getMonth(), sendDate.getDate() + blockDay, sendHour, sendMin);
setResultData({ ...resultData, start_dt: resultSendData, end_dt: resultEndData });
} else if (resultData.period === 'PERMANENT') {
const resultEndData = new Date(sendDate.getFullYear() + permanentDay, sendDate.getMonth(), sendDate.getDate(), sendHour, sendMin);
setResultData({ ...resultData, start_dt: resultSendData, end_dt: resultEndData });
} else if (resultData.period === 'WARNING') {
const resultEndData = new Date(sendDate.getFullYear(), sendDate.getMonth(), sendDate.getDate(), sendHour, sendMin + 60);
setResultData({ ...resultData, start_dt: resultSendData, end_dt: resultEndData });
}
};
// 기간, 시간 초기화
const setTimeValue = e => {
setResultData({ ...resultData, period: e.target.value, start_dt: '', end_dt: '' });
setSendMin('00');
setSendHour('00');
if (resultData.period === 'WARNING') setEndHour('01');
};
// 등록 확인 모달
let notNull = resultData.list.length > 0 && resultData.type && resultData.period && resultData.sanctions && resultData.start_dt && resultData.end_dt;
const handleRegistModalClose = () => {
if(selectData === 'multi'){
const guidChk = guidList.some(guid => guid.validate === false);
if(guidChk) {
alert("유효성 체크가 통과되지 않은 항목이 존재합니다. \r\n수정 후 재등록 해주세요.")
return;
}
}
if (notNull) {
registModalClose === 'hidden' ? setRegistModalClose('view') : setRegistModalClose('hidden');
setIsNullValue(false);
} else {
setIsNullValue(true);
}
};
// 등록 완료 모달
const handleCompleteModalClose = () => {
if (completeModalClose === 'hidden') {
setCompleteModalClose('view');
} else {
setCompleteModalClose('hidden');
handleReset();
handleRegistModalClose();
navigate('/servicemanage/userblock');
window.location.reload();
}
};
// 등록 api 연동
const handleRegist = async () => {
const message = await BlackListRegist(token, resultData);
message.data.data.message === '등록 하였습니다.' ?
setConfirmText('등록이 완료되었습니다.') :
message.data.data.message === 'admindb_exit_error' ?
setConfirmText("이미 등록된 유저입니다.") : setConfirmText(message.data.data.message);
handleRegistModalClose();
handleCompleteModalClose();
};
// console.log('DataList: ', resultData);
// console.log('resultData.period :', resultData.period);
// console.log(resultData.period.length);
return (
<>
{userInfo.auth_list && !userInfo.auth_list.some(auth => auth.id === 25) ? (
<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>
<div>
<RadioInput id="single" value="single" label="단일 등록" name="blockregist" fontSize="20px" fontWeight="500" checked={selectData === 'single'} handleChange={handleChange} />
<RegistGroup>
<InputItem>
<Label>제재 대상</Label>
<TextInput
placeholder="GUID 입력"
width="400px"
disabled={selectSingle}
value={resultData.list.guid}
onChange={e => {
let list = [];
list.push({ guid: e.target.value });
setResultData({ ...resultData, list });
}}
/>
</InputItem>
<InputRow>
<InputItem>
<Label>제재 방식</Label>
<SelectInput value={resultData.type} onChange={e => setResultData({ ...resultData, type: e.target.value })} disabled={selectSingle}>
{blockType.map((data, index) => (
<option key={index} value={data.value}>
{selectData === 'single' ? data.name : '선택'}
</option>
))}
</SelectInput>
</InputItem>
<InputItem>
<Label>제재 기간</Label>
<SelectInput value={resultData.period} onChange={e => setTimeValue(e)} disabled={selectSingle}>
{blockPeriod.map((data, index) => (
<option key={index} value={data.value}>
{selectData === 'single' ? data.name : '선택'}
</option>
))}
</SelectInput>
</InputItem>
<InputItem>
<Label>제재 사유</Label>
<SelectInput value={resultData.sanctions} onChange={e => setResultData({ ...resultData, sanctions: e.target.value })} disabled={selectSingle}>
{blockSanctions.map((data, index) => (
<option key={index} value={data.value}>
{selectData === 'single' ? data.name : '선택'}
</option>
))}
</SelectInput>
</InputItem>
</InputRow>
<InputRow>
<InputItem>
<Label>시작 일시</Label>
<InputGroup>
<DatePickerWrapper>
<DatePickerComponent
name="시작 일자"
handleSelectedDate={data => handleSelectedDate(data)}
selectedDate={resultData.start_dt}
disabled={selectSingle || !resultData.period}
pastDate={new Date()}
/>
</DatePickerWrapper>
<SelectInput onChange={e => handleSendTime(e)} id="hour" disabled={selectSingle || !resultData.period} value={sendHour}>
{HourList.map(hour => (
<option value={hour} key={hour}>
{hour}
</option>
))}
</SelectInput>
<SelectInput onChange={e => handleSendTime(e)} id="min" disabled={selectSingle || !resultData.period} value={sendMin}>
{MinuteList.map(min => (
<option value={min} key={min}>
{min}
</option>
))}
</SelectInput>
</InputGroup>
</InputItem>
<InputItem>
<Label>만료 일시</Label>
<InputGroup>
<DatePickerWrapper>
<DatePicker
disabled
selected={selectData === 'single' ? resultData.end_dt : ''}
onChange={date => setStartDate(date)}
className="datepicker"
placeholderText="만료일자"
calendarClassName="calendar"
dateFormat="yyyy - MM - dd"
locale="ko"
renderCustomHeader={({ date, changeYear, changeMonth, decreaseMonth, increaseMonth, prevMonthButtonDisabled, nextMonthButtonDisabled }) => (
<div className="calendar-top">
<button
className="btn-prev"
onClick={e => {
e.preventDefault();
decreaseMonth();
}}
disabled={prevMonthButtonDisabled}></button>
<select value={getYear(date)} onChange={({ target: { value } }) => changeYear(value)}>
{years.map(option => (
<option key={option} value={option}>
{option}
</option>
))}
</select>
.
<select value={months[getMonth(date)]} onChange={({ target: { value } }) => changeMonth(months.indexOf(value))}>
{months.map(option => (
<option key={option} value={option}>
{option}
</option>
))}
</select>
<button
className="btn-next"
onClick={e => {
e.preventDefault();
increaseMonth();
}}
disabled={nextMonthButtonDisabled}></button>
</div>
)}
/>
</DatePickerWrapper>
<SelectInput disabled>
<option value="">{selectData === 'single' && resultData.start_dt ? endHour : '00'}</option>
</SelectInput>
<SelectInput disabled>
<option value="">{selectData === 'single' && resultData.start_dt ? sendMin : '00'}</option>
</SelectInput>
</InputGroup>
</InputItem>
</InputRow>
</RegistGroup>
</div>
<div>
<RadioInput id="multi" value="multi" label="복수 등록" name="blockregist" fontSize="20px" fontWeight="500" checked={selectData === 'multi'} handleChange={handleChange} />
<RegistGroup>
<InputItem>
<UserBlockUploadBtn
disabled={selectMulti}
setResultData={setResultData}
resultData={resultData}
guidList={guidList}
setGuidList={setGuidList}
typeError={typeError}
setTypeError={setTypeError}
/>
</InputItem>
<InputRow>
<InputItem>
<Label>제재 방식</Label>
<SelectInput value={resultData.type} onChange={e => setResultData({ ...resultData, type: e.target.value })} disabled={selectMulti}>
{blockType.map((data, index) => (
<option key={index} value={data.value}>
{selectData === 'multi' ? data.name : '선택'}
</option>
))}
</SelectInput>
</InputItem>
<InputItem>
<Label>제재 기간</Label>
<SelectInput value={resultData.period} onChange={e => setTimeValue(e)} disabled={selectMulti}>
{blockPeriod.map((data, index) => (
<option key={index} value={data.value}>
{selectData === 'multi' ? data.name : '선택'}
</option>
))}
</SelectInput>
</InputItem>
<InputItem>
<Label>제재 사유</Label>
<SelectInput value={resultData.sanctions} onChange={e => setResultData({ ...resultData, sanctions: e.target.value })} disabled={selectMulti}>
{blockSanctions.map((data, index) => (
<option key={index} value={data.value}>
{selectData === 'multi' ? data.name : '선택'}
</option>
))}
</SelectInput>
</InputItem>
</InputRow>
<InputRow>
<InputItem>
<Label>시작 일시</Label>
<InputGroup>
<DatePickerWrapper>
<DatePickerComponent
name="시작 일자"
handleSelectedDate={data => handleSelectedDate(data)}
selectedDate={resultData.start_dt}
disabled={selectMulti || !resultData.period}
pastDate={new Date()}
/>
</DatePickerWrapper>
<SelectInput onChange={e => handleSendTime(e)} id="hour" disabled={selectMulti || !resultData.period} value={sendHour}>
{HourList.map(hour => (
<option value={hour} key={hour}>
{hour}
</option>
))}
</SelectInput>
<SelectInput onChange={e => handleSendTime(e)} id="min" disabled={selectMulti || !resultData.period} value={sendMin}>
{MinuteList.map(min => (
<option value={min} key={min}>
{min}
</option>
))}
</SelectInput>
</InputGroup>
</InputItem>
<InputItem>
<Label>만료 일시</Label>
<InputGroup>
<DatePickerWrapper>
<DatePicker
disabled
selected={selectData === 'multi' ? resultData.end_dt : ''}
onChange={date => setStartDate(date)}
className="datepicker"
placeholderText="만료일자"
calendarClassName="calendar"
dateFormat="yyyy - MM - dd"
locale="ko"
renderCustomHeader={({ date, changeYear, changeMonth, decreaseMonth, increaseMonth, prevMonthButtonDisabled, nextMonthButtonDisabled }) => (
<div className="calendar-top">
<button
className="btn-prev"
onClick={e => {
e.preventDefault();
decreaseMonth();
}}
disabled={prevMonthButtonDisabled}></button>
<select value={getYear(date)} onChange={({ target: { value } }) => changeYear(value)}>
{years.map(option => (
<option key={option} value={option}>
{option}
</option>
))}
</select>
.
<select value={months[getMonth(date)]} onChange={({ target: { value } }) => changeMonth(months.indexOf(value))}>
{months.map(option => (
<option key={option} value={option}>
{option}
</option>
))}
</select>
<button
className="btn-next"
onClick={e => {
e.preventDefault();
increaseMonth();
}}
disabled={nextMonthButtonDisabled}></button>
</div>
)}
/>
</DatePickerWrapper>
<SelectInput disabled>
<option value="">{selectData === 'multi' ? endHour : '00'}</option>
</SelectInput>
<SelectInput disabled>
<option value="">{selectData === 'multi' ? sendMin : '00'}</option>
</SelectInput>
</InputGroup>
</InputItem>
</InputRow>
</RegistGroup>
</div>
{isNullValue && <SearchBarAlert>설정값을 모두 입력해주세요.</SearchBarAlert>}
<BtnWrapper $justify="flex-end" $gap="10px">
<Button
text="취소"
theme="line"
handleClick={e => {
e.preventDefault();
navigate('/servicemanage/userblock');
}}
/>
<Button type="submit" text="등록" name="등록버튼" handleClick={handleRegistModalClose} theme={notNull ? 'primary' : 'disable'} />
</BtnWrapper>
{/* 일괄등록 모달창 */}
<Modal min="440px" $padding="40px" $bgcolor="transparent" $view={registModalClose}>
<BtnWrapper $justify="flex-end">
<ButtonClose onClick={handleRegistModalClose} />
</BtnWrapper>
<ModalText $align="center">
이용자 제재 명단에
<br />
등록하시겠습니까?
</ModalText>
<BtnWrapper $gap="10px">
<Button text="취소" theme="line" size="large" width="100%" handleClick={handleRegistModalClose} />
<Button text="확인" theme="primary" type="submit" size="large" width="100%" handleClick={handleRegist} />
</BtnWrapper>
</Modal>
{/* 등록 완료 모달 */}
<Modal min="440px" $padding="40px" $bgcolor="transparent" $view={completeModalClose}>
<BtnWrapper $justify="flex-end">
<ButtonClose onClick={handleCompleteModalClose} />
</BtnWrapper>
<ModalText $align="center">{confirmText}</ModalText>
<BtnWrapper $gap="10px">
<Button text="확인" theme="primary" type="submit" size="large" width="100%" handleClick={handleCompleteModalClose} />
</BtnWrapper>
</Modal>
</>
)}
</>
);
};
export default UserBlockRegist;
const RegistGroup = styled.div`
display: flex;
width: 100%;
flex-flow: column;
gap: 20px;
padding: 20px 0;
border-top: 1px solid #000;
border-bottom: 1px solid #000;
font-size: 14px;
margin-bottom: 40px;
`;
const InputRow = styled.div`
display: flex;
flex-wrap: wrap;
gap: 20px 50px;
`;
const InputItem = styled.div`
display: inline-flex;
align-items: center;
${TextInput},${SelectInput} {
height: 35px;
font-size: 14px;
}
`;
const Label = styled.div`
display: block;
min-width: 120px;
text-align: center;
`;
const InputGroup = styled.div`
display: flex;
gap: 5px;
button {
height: 35px;
}
`;
const TableWrapper = styled.div`
margin-top: 5px;
margin-bottom: 30px;
max-height: 324px;
overflow: auto;
&::-webkit-scrollbar {
width: 4px;
}
&::-webkit-scrollbar-thumb {
background: #666666;
}
&::-webkit-scrollbar-track {
background: #d9d9d9;
}
`;
const Count = styled.div`
font-size: 14px;
`;
const FileInput = styled(TextInput)`
height: 35px;
border: 1px solid #e0e0e0;
border-radius: 5px;
width: 300px;
padding: 0 15px;
line-height: 35px;
font-size: 14px;
cursor: pointer;
&::file-selector-button {
display: none;
}
`;
const SearchBarAlert = styled.div`
display: ${props => (props.$view === 'hidden' ? 'none' : 'flex')};
justify-content: right;
width: 100%;
padding-bottom: 20px;
color: #d60000;
`;

View File

@@ -0,0 +1,507 @@
import { useState, useEffect, Fragment, useCallback } from 'react';
import { SearchBar } from '../../components/common/SearchBar';
import CheckBox from '../../components/common/input/CheckBox';
import Modal from '../../components/common/modal/Modal';
import { Title, FormWrapper, TableInfo, ListTitle, ListOption, TableStyle, BtnWrapper, ButtonClose, ModalText } from '../../styles/Components';
import Button from '../../components/common/button/Button';
import { WhiteListSearchBar } from '../../components/ServiceManage';
import { styled } from 'styled-components';
import { WhiteListData, WhiteListDelete, WhiteListRegist, WhiteListAllow, WhiteListExport } from '../../apis/WhiteList';
import { authList } from '../../store/authList';
import { useRecoilValue } from 'recoil';
import { useNavigate } from 'react-router-dom';
import AuthModal from '../../components/common/modal/AuthModal';
import { authType } from '../../assets/data';
const WhiteList = () => {
const navigate = useNavigate();
const token = sessionStorage.getItem('token');
const userInfo = useRecoilValue(authList);
const [resultData, setResultData] = useState({ guid: '' });
const [doubleSubmitFlag, setDoubleSubmitFlag] = useState(false);
const [selectedRow, setSelectedRow] = useState([]);
const [selectedId, setSelectedId] = useState([]);
const [dataList, setDataList] = useState([]);
const [deleteModalClose, setDeleteModalClose] = useState('hidden');
const [confirmModalClose, setConfirmModalClose] = useState('hidden');
const [deleteUserClose, setDeleteUserClose] = useState('hidden');
const [completeModalClose, setCompleteModalClose] = useState('hidden');
const [registModalClose, setRegistModalClose] = useState('hidden');
const [allowModalClose, setAllowModalClose] = useState('hidden');
const [confirmAllowClose, setConfirmAllowClose] = useState('hidden');
const [userAllowClose, setUserAllowClose] = useState('hidden');
const [isNullValue, setIsNullValue] = useState(false);
const [confirmText, setConfirmText] = useState('');
const [approveAble, setApproveAble] = useState(false);
const fetchData = async () => {
setDataList(await WhiteListData(token));
};
useEffect(() => {
fetchData();
}, []);
useEffect(() => {
handleApproveCheck();
}, [selectedRow]);
// 전체 선택
const handleAllSelect = () => {
const checkAll = document.getElementById('check-all');
let list = [];
if (checkAll.checked === true) {
dataList.map((data, index) => {
document.getElementsByName('select')[index].checked = true;
list.push(String(data.id));
});
} else if (checkAll.checked === false) {
for (let i = 0; i < dataList.length; i++) {
dataList.map((data, index) => (document.getElementsByName('select')[index].checked = false));
list = [];
}
}
setSelectedRow(list);
};
// 일부 선택
const handleSelectCheckBox = e => {
let list = [...selectedRow];
if (e.target.checked) {
list.push(e.target.id);
setSelectedRow(list);
} else {
const filterList = list.filter(data => e.target.id !== data);
setSelectedRow(filterList);
}
};
// 선택 승인 유효성 검사
const approveCheck = e => {
let approveList = [];
dataList && dataList.map(data => data.status === 'PERMITTED' && approveList.push(data.id));
return approveList;
};
const handleApproveCheck = () => {
let list = [];
let approveList = approveCheck();
selectedRow.map(data => list.push(Number(data)));
setApproveAble(approveList.filter(row => list.includes(row)).length === 0);
};
// 선택 삭제 모달창
const handleDeleteModalClose = () => {
if (selectedRow.length !== 0) {
if (deleteModalClose === 'hidden') {
setDeleteModalClose('view');
} else {
setDeleteModalClose('hidden');
}
}
};
// 삭제 확인 모달창
const handleConfirmModalClose = () => {
if (confirmModalClose === 'hidden') {
setConfirmModalClose('view');
} else {
setConfirmModalClose('hidden');
window.location.reload();
}
};
// 선택 삭제 버튼
const handleSelectedDelete = () => {
let list = [];
selectedRow.map(data =>
list.push({
id: data,
}),
);
WhiteListDelete(token, list);
handleDeleteModalClose();
handleConfirmModalClose();
};
// 개별 삭제 모달
const handleUserDeleteModalClose = dataValue => {
if (deleteUserClose === 'hidden') {
setDeleteUserClose('view');
} else {
setDeleteUserClose('hidden');
}
if (dataValue) {
setSelectedId(dataValue.id);
}
};
// 개별 삭제 버튼
const handleDelete = () => {
let list = [];
list.push({ id: selectedId });
WhiteListDelete(token, list);
handleUserDeleteModalClose();
handleConfirmModalClose();
setSelectedId([]);
};
// 등록 확인 모달
const handleRegistModalClose = () => {
if (resultData.guid.length === 0) {
setIsNullValue(true);
} else if (registModalClose === 'hidden') {
setRegistModalClose('view');
setIsNullValue(false);
} else {
setRegistModalClose('hidden');
}
};
// console.log(resultData.guid.length)
// 등록 완료 모달
const handleCompleteModalClose = () => {
if (completeModalClose === 'hidden') {
setCompleteModalClose('view');
} else {
setCompleteModalClose('hidden');
window.location.reload();
}
};
// 화이트 리스트 등록하기
const handleSubmit = async () => {
const message = await WhiteListRegist(token, resultData);
if (message.data.data.message === '등록 하였습니다.') setConfirmText('등록이 완료되었습니다.');
else if (message.data.data.message === 'admindb_exit_error') setConfirmText('해당 유저가 이미 등록되어있습니다. \n 확인 후 다시 이용해주세요.');
else if (message.data.data.message === '중복된 유저 정보가 있습니다.') setConfirmText('파일 내 중복된 유저 정보가 있습니다. \n 파일을 다시 확인 후 이용해주세요.');
else setConfirmText(message.data.data.message);
handleRegistModalClose();
handleCompleteModalClose();
};
// 선택 승인 모달창
const handleAllowModalClose = () => {
if (selectedRow.length !== 0) {
if (allowModalClose === 'hidden') {
setAllowModalClose('view');
} else {
setAllowModalClose('hidden');
}
}
};
// 승인 완료 모달창
const handleConfirmAllowClose = () => {
if (confirmAllowClose === 'hidden') {
setConfirmAllowClose('view');
} else {
setConfirmModalClose('hidden');
window.location.reload();
}
};
// 일괄 승인
const handleAllow = () => {
let list = [];
selectedRow.map(data =>
list.push({
id: data,
}),
);
WhiteListAllow(token, list);
handleAllowModalClose();
handleConfirmAllowClose();
};
// 개별 승인
const handleUserAllowModalClose = dataValue => {
if (userAllowClose === 'hidden') {
setUserAllowClose('view');
} else {
setUserAllowClose('hidden');
}
if (dataValue) {
setSelectedId(dataValue.id);
}
};
// 개별 승인 버튼
const handleUserAllow = () => {
let list = [];
list.push({ id: selectedId });
WhiteListAllow(token, list);
handleUserAllowModalClose();
handleConfirmAllowClose();
setSelectedId([]);
};
// 엑셀 다운로드
const handleXlsxExport = () => {
const fileName = 'Caliverse_whitelist.xlsx';
WhiteListExport(token, fileName);
};
const handleCountSelectedRow = () => {
return dataList && document.querySelectorAll('input[name="select"]:checked').length === dataList.length;
};
return (
<>
{userInfo.auth_list && !userInfo.auth_list.some(auth => auth.id === authType.whiteListRead) ? (
<AuthModal />
) : (
<>
<Title>화이트리스트 등록/수정</Title>
{userInfo.auth_list && userInfo.auth_list.some(auth => auth.id === 21) && (
<FormWrapper>
<WhiteListSearchBar handleRegistModalClose={handleRegistModalClose} isNullValue={isNullValue} resultData={resultData} setResultData={setResultData} />
</FormWrapper>
)}
<TableInfo>
<ListTitle>화이트리스트 명단</ListTitle>
<ListOption>
<Button theme="line" type="file" text="엑셀 다운로드" handleClick={handleXlsxExport} />
{userInfo.auth_list && userInfo.auth_list.some(auth => auth.id === 28) && (
<Button theme={selectedRow.length === 0 ? 'disable' : 'line'} type="submit" text="선택 삭제" handleClick={handleDeleteModalClose} />
)}
{userInfo.auth_list && userInfo.auth_list.some(auth => auth.id === 20) && (
<Button
theme={selectedRow.length === 0 || !approveAble ? 'disable' : 'line'}
type="submit"
text="선택 승인"
errorMessage={!approveAble}
handleClick={handleAllowModalClose}
/>
)}
</ListOption>
</TableInfo>
<TableWrapper>
<TableStyle>
<caption></caption>
<thead>
<tr>
<th width="40">
<CheckBox id="check-all" handleCheck={handleAllSelect} checked={handleCountSelectedRow()} />
</th>
<th width="80">번호</th>
<th width="40%">GUID</th>
<th width="25%">닉네임</th>
<th width="25%">등록자(이메일주소)</th>
<th width="100">승인</th>
{userInfo.auth_list && userInfo.auth_list.some(auth => auth.id === 28) && <th width="100">삭제</th>}
</tr>
</thead>
<tbody>
{dataList &&
dataList.map(data => (
<Fragment key={data.guid}>
<tr>
<td>
<CheckBox name={'select'} id={data.id} setData={e => handleSelectCheckBox(e)} handleCheck={handleApproveCheck} />
</td>
<td>{data.row_num}</td>
<td>{data.guid}</td>
<td>{data.nickname}</td>
<td>{data.create_by}</td>
{/* 승인 상태 */}
{data.status !== 'REJECT' ? (
<td>승인</td>
) : userInfo.auth_list && userInfo.auth_list.some(auth => auth.id === 20) ? (
<td>
<Button theme="line" text="승인" id={data.id} handleClick={() => handleUserAllowModalClose(data)} />
</td>
) : (
<td>대기 </td>
)}
{userInfo.auth_list && userInfo.auth_list.some(auth => auth.id === 28) && (
<td>
<Button theme="line" text="삭제" id={data.id} handleClick={() => handleUserDeleteModalClose(data)} />
</td>
)}
</tr>
</Fragment>
))}
</tbody>
</TableStyle>
</TableWrapper>
{/* 선택삭제 모달 */}
<Modal min="440px" $padding="40px" $bgcolor="transparent" $view={deleteModalClose}>
<BtnWrapper $justify="flex-end">
<ButtonClose onClick={handleDeleteModalClose} />
</BtnWrapper>
<ModalText $align="center">
선택된 대상을 화이트리스트 유저에서
<br />
삭제하시겠습니까?
</ModalText>
<BtnWrapper $gap="10px">
<Button text="취소" theme="line" size="large" width="100%" handleClick={handleDeleteModalClose} />
<Button text="확인" theme="primary" type="submit" size="large" width="100%" handleClick={handleSelectedDelete} />
</BtnWrapper>
</Modal>
{/* 등록 모달 */}
<Modal min="440px" $padding="40px" $bgcolor="transparent" $view={registModalClose}>
<BtnWrapper $="flex-end">
<ButtonClose onClick={handleRegistModalClose} />
</BtnWrapper>
<ModalText $align="center">
화이트리스트 명단에
<br />
등록하시겠습니까?
</ModalText>
<BtnWrapper $gap="10px">
<Button text="취소" theme="line" size="large" width="100%" handleClick={handleRegistModalClose} />
<Button
text="확인"
theme="primary"
type="submit"
size="large"
width="100%"
handleClick={() => {
doubleSubmitFlag || handleSubmit();
setDoubleSubmitFlag(true);
}}
/>
</BtnWrapper>
</Modal>
{/* 개별 삭제 모달 */}
<Modal min="440px" $padding="40px" $bgcolor="transparent" $view={deleteUserClose}>
<BtnWrapper $justify="flex-end">
<ButtonClose onClick={handleUserDeleteModalClose} />
</BtnWrapper>
<ModalText $align="center">
화이트리스트 유저를
<br />
삭제하시겠습니까?
</ModalText>
<BtnWrapper $gap="10px">
<Button text="취소" theme="line" size="large" width="100%" handleClick={handleUserDeleteModalClose} />
<Button text="확인" theme="primary" type="submit" size="large" width="100%" handleClick={handleDelete} />
</BtnWrapper>
</Modal>
{/* 승인 모달 */}
<Modal min="440px" $padding="40px" $bgcolor="transparent" $view={allowModalClose}>
<BtnWrapper $justify="flex-end">
<ButtonClose onClick={handleAllowModalClose} />
</BtnWrapper>
<ModalText $align="center">
선택된 대상을 화이트리스트 유저로
<br />
승인하시겠습니까?
</ModalText>
<BtnWrapper $gap="10px">
<Button text="취소" theme="line" size="large" width="100%" handleClick={handleAllowModalClose} />
<Button text="확인" theme="primary" type="submit" size="large" width="100%" handleClick={handleAllow} />
</BtnWrapper>
</Modal>
{/* 개별 승인 모달 */}
<Modal min="440px" $padding="40px" $bgcolor="transparent" $view={userAllowClose}>
<BtnWrapper $justify="flex-end">
<ButtonClose onClick={handleUserAllowModalClose} />
</BtnWrapper>
<ModalText $align="center">
선택된 대상을 화이트리스트 유저로
<br />
승인하시겠습니까?
</ModalText>
<BtnWrapper $gap="10px">
<Button text="취소" theme="line" size="large" width="100%" handleClick={handleUserAllowModalClose} />
<Button text="확인" theme="primary" type="submit" size="large" width="100%" handleClick={handleUserAllow} />
</BtnWrapper>
</Modal>
{/* 완료 모달 */}
{/* 등록 완료 모달 */}
<Modal min="440px" $padding="40px" $bgcolor="transparent" $view={completeModalClose}>
<BtnWrapper $justify="flex-end">
<ButtonClose onClick={handleCompleteModalClose} />
</BtnWrapper>
<ModalText $align="center">{confirmText}</ModalText>
<BtnWrapper $gap="10px">
<Button text="확인" theme="primary" type="submit" size="large" width="100%" handleClick={handleCompleteModalClose} />
</BtnWrapper>
</Modal>
{/* 승인 완료 모달 */}
<Modal min="440px" $padding="40px" $bgcolor="transparent" $view={confirmAllowClose}>
<BtnWrapper $justify="flex-end">
<ButtonClose onClick={handleConfirmAllowClose} />
</BtnWrapper>
<ModalText $align="center">승인이 완료되었습니다.</ModalText>
<BtnWrapper $gap="10px">
<Button text="확인" theme="primary" type="submit" size="large" width="100%" handleClick={handleConfirmAllowClose} />
</BtnWrapper>
</Modal>
{/* 삭제 확인 모달 */}
<Modal min="440px" $padding="40px" $bgcolor="transparent" $view={confirmModalClose}>
<BtnWrapper $justify="flex-end">
<ButtonClose onClick={handleConfirmModalClose} />
</BtnWrapper>
<ModalText $align="center">삭제가 완료되었습니다.</ModalText>
<BtnWrapper $gap="10px">
<Button text="확인" theme="primary" type="submit" size="large" width="100%" handleClick={handleConfirmModalClose} />
</BtnWrapper>
</Modal>
</>
)}
</>
);
};
export default WhiteList;
const TableWrapper = styled.div`
height: calc(100vh - 383px);
border-top: 1px solid #000;
overflow: auto;
&::-webkit-scrollbar {
width: 4px;
}
&::-webkit-scrollbar-thumb {
background: #666666;
}
&::-webkit-scrollbar-track {
background: #d9d9d9;
}
thead {
th {
position: sticky;
top: 0;
z-index: 10;
}
}
`;
const StatusRed = styled.td`
color: #d60000;
`;

View File

@@ -0,0 +1,12 @@
export { default as Board } from './Board';
export { default as Mail } from './Mail';
export { default as MailRegist } from './MailRegist';
export { default as ReportList } from './ReportList';
export { default as UserBlock } from './UserBlock';
export { default as UserBlockRegist } from './UserBlockRegist';
export { default as WhiteList } from './WhiteList';
export { default as Items } from './Items';
export { default as Event } from './Event';
export { default as EventRegist } from './EventRegist';
export { default as LandAuction} from './LandAuction'
export { default as BattleEvent} from './BattleEvent'

View File

@@ -0,0 +1,501 @@
import { Fragment, useEffect, useState } from 'react';
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';
import Pagination from '../../components/common/Pagination/Pagination';
import Modal from '../../components/common/modal/Modal';
import { AdminViewList, AdminViewGroupList, AdminLoginApprove, AdminDeleteUser, AdminChangeGroup, AdminChangePw } from '../../apis/Admin';
import AdminViewSearchBar from '../../components/UserManage/AdminViewSearchBar';
import { useNavigate } from 'react-router-dom';
import { authList } from '../../store/authList';
import { useRecoilValue } from 'recoil';
import AuthModal from '../../components/common/modal/AuthModal';
function AdminView() {
const token = sessionStorage.getItem('token');
const userInfo = useRecoilValue(authList);
const navigate = useNavigate();
const [currentPage, setCurrentPage] = useState(1);
const [dataList, setDataList] = useState([]);
const [groupList, setGroupList] = useState([]);
const [selectedRow, setSelectedRow] = useState([]);
const [selectedGroup, setSelectedGroup] = useState([]);
const [searchData, setSearchData] = useState({});
const [selectedEmail, setSelectedEmail] = useState('');
const [orderBy, setOrderBy] = useState('DESC');
const [deleteModalClose, setDeleteModalClose] = useState('hidden');
const [approveModalClose, setApproveModalClose] = useState('hidden');
const [saveModalClose, setSaveModalClose] = useState('hidden');
const [confirmModalClose, setConfirmModalClose] = useState('hidden');
const [confirmText, setConfirmText] = useState('');
const [singleConfirmText, setSingleConfirmText] = useState('');
const [singleModalClose, setSingleModalClose] = useState('hidden');
const [initialModalClose, setInitialModalClose] = useState('hidden');
const [approveAble, setApproveAble] = useState(false);
const [pageSize, setPageSize] = useState('50');
// 그룹 권한 조회 후 전체 리스트 불러오기 && 페이징
useEffect(() => {
fetchGroupData();
setSelectedRow([]);
}, [currentPage]);
useEffect(() => {
handleApproveCheck();
}, [selectedRow]);
// 선택 삭제 모달
const handleDeleteModalClose = () => {
if (selectedRow.length !== 0 && deleteModalClose === 'hidden') {
setDeleteModalClose('view');
} else {
setDeleteModalClose('hidden');
setConfirmText('삭제가');
}
};
// 선택 승인 모달
const handleApproveModalClose = () => {
if (selectedRow.length !== 0 && approveModalClose === 'hidden' && approveAble) {
setApproveModalClose('view');
} else {
setApproveModalClose('hidden');
setConfirmText('승인이');
}
};
// 저장 모달
const handleSaveModalClose = () => {
if (selectedGroup.length !== 0 && saveModalClose === 'hidden') {
setSaveModalClose('view');
} else {
setSaveModalClose('hidden');
setConfirmText('저장이');
}
};
// 삭제, 승인, 저장 확인 모달창
const handleConfirmeModalClose = () => {
if (confirmModalClose === 'hidden') {
setConfirmModalClose('view');
} else {
setConfirmModalClose('hidden');
window.location.reload();
}
};
// 단일 승인 or 반려 모달
const handleSingleModalClose = () => {
if (singleModalClose === 'hidden') {
setSingleModalClose('view');
} else {
setSingleModalClose('hidden');
window.location.reload();
}
};
// 비밀번호 초기화 모달
const handleInitialModalClose = () => {
if (initialModalClose === 'hidden') {
setInitialModalClose('view');
} else {
setInitialModalClose('hidden');
}
};
// 리스트 조회
const fetchData = async (searchType, searchKey, groupType, joinCheck, order, size) => {
setDataList(await AdminViewList(token, searchType, searchKey, groupType, joinCheck, order ? order : orderBy, size ? size : pageSize, currentPage));
};
// 그룹 조회
const fetchGroupData = async () => {
setGroupList(await AdminViewGroupList(token));
fetchData(searchData.searchOption, searchData.data, searchData.authorityOption, searchData.joinCheck, orderBy, pageSize);
};
// 검색 함수
const handleSearch = (searchType, searchKey, groupType, joinCheck) => {
fetchData(searchType, searchKey, groupType, joinCheck);
};
// 오름차순 내림차순
const handleOrderBy = e => {
const order = e.target.value;
setOrderBy(order);
fetchData(searchData.searchOption, searchData.data, searchData.authorityOption, searchData.joinCheck, order, pageSize);
};
// 페이징
const handlePageSize = e => {
const size = e.target.value;
setPageSize(size);
setCurrentPage(1);
fetchData(searchData.searchOption, searchData.data, searchData.authorityOption, searchData.joinCheck, orderBy, size, 1);
};
// 사용자 승인
const handleApprove = userEmail => {
let list = [];
list.push({
email: userEmail,
is_approve: 'APPROVE',
});
AdminLoginApprove(token, list);
};
// 사옹자 반려
const handleReject = userEmail => {
let list = [];
list.push({
email: userEmail,
is_approve: 'REJECT',
});
AdminLoginApprove(token, list);
};
// 체크박스 선택 리스트
const handleSelectCheckBox = e => {
let list = [...selectedRow];
if (e.target.checked) {
list.push(e.target.id);
setSelectedRow(list);
} else {
const filterList = list.filter(data => e.target.id !== data);
setSelectedRow(filterList);
}
};
// 승인버튼 유효성 검사
const approveCheck = () => {
let list = [];
dataList.list && dataList.list.map(data => data.status === 'ROLE_NOT_PERMITTED' && list.push(data.email));
return list;
};
// 승인 버튼 validation
const handleApproveCheck = () => {
const list = approveCheck();
setApproveAble(list.filter(row => selectedRow.includes(row)).length === selectedRow.length);
};
// 변경된 권한 등급 리스트
const handleGroupGrade = e => {
let list = [...selectedGroup];
const filterList = list.filter(data => e.target.id !== data.email);
filterList.push({ email: e.target.id, group_id: e.target.value });
setSelectedGroup(filterList);
};
// 선택 승인 함수
const handleSelectedApprove = () => {
selectedRow.map(data => handleApprove(data));
handleApproveModalClose();
handleConfirmeModalClose();
};
// 선택 삭제 함수
const handleSelectedDelete = () => {
let list = [];
selectedRow.map(data =>
list.push({
email: data,
}),
);
AdminDeleteUser(token, list);
handleDeleteModalClose();
handleConfirmeModalClose();
};
// 단일 승인 함수
const handleSingleApprove = data => {
handleApprove(data);
setSingleConfirmText('승인이 완료되었습니다.');
handleSingleModalClose();
};
// 선택 반려 함수
const handleSingleReject = data => {
handleReject(data);
setSingleConfirmText('승인이 반려되었습니다.');
handleSingleModalClose();
};
// 수정된 권한 등급 저장 함수
const handleSelectedGroupSave = () => {
AdminChangeGroup(token, selectedGroup);
handleSaveModalClose();
handleConfirmeModalClose();
};
// 비밀번호 저장 함수
const handleResetPw = data => {
setSelectedEmail(data);
handleInitialModalClose();
};
const handlePasswordInitialize = () => {
AdminChangePw(token, { email: selectedEmail });
// console.log(selectedEmail);
setConfirmText('비밀번호 초기화가');
handleInitialModalClose();
handleConfirmeModalClose();
};
// 전체 선택 구현
const handleAllSelect = () => {
let list = [];
if (document.getElementById('check-all').checked === true) {
dataList.list.map((data, index) => {
document.getElementsByName('select')[index].checked = true;
list.push(data.email);
});
} else if (document.getElementById('check-all').checked === false) {
for (let i = 0; i < dataList.list.length; i++) {
dataList.list.map((data, index) => (document.getElementsByName('select')[index].checked = false));
list = [];
}
}
setSelectedRow(list);
};
const handleCountSelectedRow = () => {
return currentPage > (dataList && dataList.total) / pageSize ? selectedRow.length === dataList.total % pageSize : selectedRow.length === Number(pageSize);
};
return (
<>
{userInfo.auth_list && !userInfo.auth_list.some(auth => auth.id === 1) ? (
<AuthModal />
) : (
<>
<Title>운영자 조회</Title>
<FormWrapper action="" $flow="row">
<AdminViewSearchBar handleSearch={handleSearch} groupList={groupList} setResultData={setSearchData} setCurrentPage={setCurrentPage} />
</FormWrapper>
<TableInfo>
<ListCount>
: {dataList && dataList.total} / {dataList && dataList.total_all}
</ListCount>
<ListOption>
<SelectInput className="input-select" onChange={e => handleOrderBy(e)}>
<option value="DESC">내림차순</option>
<option value="ASC">오름차순</option>
</SelectInput>
<SelectInput className="input-select" onChange={e => handlePageSize(e)}>
<option value="50">50</option>
<option value="100">100</option>
</SelectInput>
{userInfo.auth_list && userInfo.auth_list.some(auth => auth.id === 4) && (
<Button theme={selectedRow.length === 0 ? 'disable' : 'line'} text="선택 삭제" handleClick={handleDeleteModalClose} />
)}
{userInfo.auth_list && userInfo.auth_list.some(auth => auth.id === 2) && (
<Button theme={selectedRow.length === 0 || !approveAble ? 'disable' : 'line'} text="선택 승인" handleClick={handleApproveModalClose} />
)}
{userInfo.auth_list && userInfo.auth_list.some(auth => auth.id === 3) && <Button theme="primary" text="저장" handleClick={handleSaveModalClose} />}
</ListOption>
</TableInfo>
<TableStyle>
<caption></caption>
<thead>
<tr>
<th width="40">
<CheckBox id="check-all" handleCheck={handleAllSelect} checked={handleCountSelectedRow()} />
</th>
<th width="80">번호</th>
<th width="200">이름</th>
<th>ID (이메일 주소)</th>
<th width="150">로그인 승인 상태</th>
<th width="20%">권한 등급</th>
{userInfo.auth_list && userInfo.auth_list.some(auth => auth.id === 3) && <th width="120">비밀번호 초기화</th>}
</tr>
</thead>
<tbody>
{dataList.list &&
dataList.list.map(user => (
<Fragment key={user.id}>
<tr>
<td>
<CheckBox
name={'select'}
id={user.email}
setData={e => {
handleSelectCheckBox(e);
}}
handleCheck={handleApproveCheck}
/>
</td>
<td>{user.row_num}</td>
<td>{user.name}</td>
<td>{user.email}</td>
{user.status !== 'ROLE_NOT_PERMITTED' ? (
user.status === 'PERMITTED' || user.status === 'INIT' ? (
<td>
<span>승인완료</span>
</td>
) : (
<td>
<State color="#d60000">신청 반려</State>
</td>
)
) : userInfo.auth_list && userInfo.auth_list.some(auth => auth.id === 2) ? (
<td>
<BtnWrapper $gap="5px" $justify="center">
<Button theme="primary" text="승인" handleClick={() => handleSingleApprove(user.email)} />
<Button theme="line" text="불가" handleClick={() => handleSingleReject(user.email)} />
</BtnWrapper>
</td>
) : (
<td>
<State color="gray">승인 대기 </State>
</td>
)}
{userInfo.auth_list && userInfo.auth_list.some(auth => auth.id === 3) ? (
<td>
<SelectInput id={user.email} defaultValue={!user.group_id ? '선택' : user.group_id} onChange={e => handleGroupGrade(e)}>
{user.group_id || (
<option key={'선택'} value={'선택'} disabled>
선택
</option>
)}
{groupList &&
groupList.map(group => (
<option key={group.group_id} value={group.group_id}>
{group.group_nm}
</option>
))}
</SelectInput>
</td>
) : (
<td>
<SelectInput id={user.email} defaultValue={user.group_id} disabled color="gray">
{groupList &&
groupList.map(group => (
<option key={group.group_id} value={group.group_id}>
{group.group_nm}
</option>
))}
</SelectInput>
</td>
)}
{userInfo.auth_list && userInfo.auth_list.some(auth => auth.id === 3) && (
<td>{(user.status === 'PERMITTED' || user.status === 'INIT') && <Button theme="reset" handleClick={() => handleResetPw(user.email)} />}</td>
)}
</tr>
</Fragment>
))}
</tbody>
</TableStyle>
<Pagination postsPerPage={pageSize} totalPosts={dataList && dataList.total_all} setCurrentPage={setCurrentPage} currentPage={currentPage} pageLimit={10} />
{/* 선택 삭제 모달 */}
<Modal min="440px" $padding="40px" $bgcolor="transparent" $view={deleteModalClose}>
<BtnWrapper $justify="flex-end">
<ButtonClose onClick={handleDeleteModalClose} />
</BtnWrapper>
<ModalText $align="center">
선택된 운영자를 삭제하시겠습니까?
<br />
삭제 계정 정보가 제거됩니다.
</ModalText>
<BtnWrapper $gap="10px">
<Button text="취소" theme="line" size="large" width="100%" handleClick={handleDeleteModalClose} />
<Button text="확인" theme="primary" type="submit" size="large" width="100%" handleClick={handleSelectedDelete} />
</BtnWrapper>
</Modal>
{/* 선택 승인 모달 */}
<Modal min="440px" $padding="40px" $bgcolor="transparent" $view={approveModalClose}>
<BtnWrapper $justify="flex-end">
<ButtonClose onClick={handleApproveModalClose} />
</BtnWrapper>
<ModalText $align="center">
선택된 운영자 로그인을
<br />
승인하시겠습니까?
</ModalText>
<BtnWrapper $gap="10px">
<Button text="취소" theme="line" size="large" width="100%" handleClick={handleApproveModalClose} />
<Button text="확인" theme="primary" type="submit" size="large" width="100%" handleClick={handleSelectedApprove} />
</BtnWrapper>
</Modal>
{/* 저장 모달 */}
<Modal min="440px" $padding="40px" $bgcolor="transparent" $view={saveModalClose}>
<BtnWrapper $justify="flex-end">
<ButtonClose onClick={handleSaveModalClose} />
</BtnWrapper>
<ModalText $align="center">
운영자 정보 수정사항을
<br />
저장하시겠습니까?
</ModalText>
<BtnWrapper $gap="10px">
<Button text="취소" theme="line" size="large" width="100%" handleClick={handleSaveModalClose} />
<Button text="확인" theme="primary" type="submit" size="large" width="100%" handleClick={handleSelectedGroupSave} />
</BtnWrapper>
</Modal>
{/* 확인 모달 */}
<Modal min="440px" $padding="40px" $bgcolor="transparent" $view={confirmModalClose}>
<BtnWrapper $justify="flex-end">
<ButtonClose onClick={handleConfirmeModalClose} />
</BtnWrapper>
<ModalText $align="center">{confirmText} 완료되었습니다.</ModalText>
<BtnWrapper $gap="10px">
<Button text="확인" theme="primary" type="submit" size="large" width="100%" handleClick={handleConfirmeModalClose} />
</BtnWrapper>
</Modal>
{/* 단일 승인 or 반려 모달 */}
<Modal min="440px" $padding="40px" $bgcolor="transparent" $view={singleModalClose}>
<BtnWrapper $justify="flex-end">
<ButtonClose onClick={handleSingleModalClose} />
</BtnWrapper>
<ModalText $align="center">{singleConfirmText}</ModalText>
<BtnWrapper $gap="10px">
<Button text="확인" theme="primary" type="submit" size="large" width="100%" handleClick={handleSingleModalClose} />
</BtnWrapper>
</Modal>
{/* 비밀번호 초기화 모달 */}
<Modal min="440px" $padding="40px" $bgcolor="transparent" $view={initialModalClose}>
<BtnWrapper $justify="flex-end">
<ButtonClose onClick={handleInitialModalClose} />
</BtnWrapper>
<ModalText $align="center">비밀번호를 초기화하시겠습니까?</ModalText>
<BtnWrapper $gap="10px">
<Button text="취소" theme="line" size="large" width="100%" handleClick={handleInitialModalClose} />
<Button text="확인" theme="primary" type="submit" size="large" width="100%" handleClick={handlePasswordInitialize} />
</BtnWrapper>
</Modal>
</>
)}
</>
);
}
export default AdminView;

View File

@@ -0,0 +1,279 @@
import CheckBox from '../../components/common/input/CheckBox';
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';
import { useNavigate } from 'react-router-dom';
import { Fragment, useState, useEffect } from 'react';
import { GroupViewList, GroupDelete, GroupRegist } from '../../apis/Group';
import AuthRegistBar from '../../components/UserManage/AuthRegistBar';
import Pagination from '../../components/common/Pagination/Pagination';
import { authList } from '../../store/authList';
import { useRecoilValue } from 'recoil';
import AuthModal from '../../components/common/modal/AuthModal';
import { useTranslation } from 'react-i18next';
import { authType } from '../../assets/data';
import ViewTableInfo from '../../components/common/Table/ViewTableInfo';
const AuthSetting = () => {
const token = sessionStorage.getItem('token');
const userInfo = useRecoilValue(authList);
const { t } = useTranslation();
const navigate = useNavigate();
const [doubleSubmitFlag, setDoubleSubmitFlag] = useState(false);
const [dataList, setDataList] = useState([]);
const [selectedRow, setSelectedRow] = useState([]);
const [deleteModalClose, setDeleteModalClose] = useState('hidden');
const [registModalClose, setRegistModalClose] = useState('hidden');
const [confirmModalClose, setConfirmModalClose] = useState('hidden');
const [isNullValue, setIsNullValue] = useState(false);
const [confirmText, setConfirmText] = useState();
const [orderBy, setOrderBy] = useState('DESC');
const [pageSize, setPageSize] = useState('50');
const [currentPage, setCurrentPage] = useState(1);
const [registData, setRegistData] = useState({
group_nm: '',
description: '',
});
const fetchData = async (order, size) => {
setDataList(await GroupViewList(token, order ? order : orderBy, currentPage, size ? size : pageSize, currentPage));
};
useEffect(() => {
fetchData(orderBy, pageSize);
setSelectedRow([]);
}, [currentPage]);
// 권한 등록 폼 제출
const handleGroupSubmit = async () => {
const message = await GroupRegist(token, registData);
handleRegistModalClose();
handleConfirmeModalClose();
message.data.data.message !== '저장 하였습니다.' ? setConfirmText(message.data.data.message) : setConfirmText('등록이 완료되었습니다.');
};
const handleAllSelect = () => {
let list = [];
if (document.getElementById('check-all').checked === true) {
dataList.group_list.map((data, index) => {
document.getElementsByName('select')[index].checked = true;
list.push(String(data.group_id));
});
} else if (document.getElementById('check-all').checked === false) {
for (let i = 0; i < dataList.group_list.length; i++) {
dataList.group_list.map((data, index) => (document.getElementsByName('select')[index].checked = false));
list = [];
}
}
setSelectedRow(list);
};
const handleSelectCheckBox = e => {
let list = [...selectedRow];
if (e.target.checked) {
list.push(e.target.id);
setSelectedRow(list);
} else {
const filterList = list.filter(data => e.target.id !== data);
setSelectedRow(filterList);
}
};
// 삭제 확인 모달창
const handleDeleteModalClose = () => {
if (selectedRow.length !== 0 && deleteModalClose === 'hidden') {
setDeleteModalClose('view');
setConfirmText('삭제가 완료되었습니다.');
} else {
setDeleteModalClose('hidden');
}
};
// 등록 확인 모달창
const handleRegistModalClose = () => {
if (registData.group_nm.length === 0) {
// if(registData.group_nm.length === 0) {
// setNullMessage("필수값을 입력해주세요.")
// } else if(registData.group_nm.charAt(0) === " "){
// setNullMessage("텍스트를 공백으로 시작할 수 없습니다.")
// }
setIsNullValue(true);
} else if (registModalClose === 'hidden') {
setRegistModalClose('view');
setConfirmText('등록이 완료되었습니다.');
setIsNullValue(false);
} else {
setRegistModalClose('hidden');
}
};
//삭제, 등록 완료 모달창
const handleConfirmeModalClose = () => {
if (confirmModalClose === 'hidden') {
setConfirmModalClose('view');
} else {
setConfirmModalClose('hidden');
window.location.reload();
}
};
// 삭제 버튼
const handleSelectedDelete = () => {
let list = [];
selectedRow.map(data =>
list.push({
group_id: data,
}),
);
GroupDelete(token, list);
handleDeleteModalClose();
handleConfirmeModalClose();
};
// 배열 정렬
const handleOrderBy = e => {
const order = e.target.value;
setOrderBy(order);
fetchData(order, pageSize);
};
const handlePageSize = e => {
const size = e.target.value;
setPageSize(size);
setCurrentPage(1);
fetchData(orderBy, size, 1);
};
const handleCountSelectedRow = () => {
return currentPage > (dataList && dataList.total) / pageSize ? selectedRow.length === dataList.total % pageSize : selectedRow.length === Number(pageSize);
};
return (
<>
{userInfo.auth_list && !userInfo.auth_list.some(auth => auth.id === authType.authoritySettingRead) ? (
<AuthModal />
) : (
<>
<Title>권한 설정</Title>
{userInfo.auth_list && userInfo.auth_list.some(auth => auth.id === authType.authoritySettingUpdate) && (
<FormWrapper action="" $flow="row">
<AuthRegistBar handleRegistModalClose={handleRegistModalClose} registData={registData} setRegistData={setRegistData} $isNullValue={isNullValue} nullMessage={t('NULL_MSG')} />
</FormWrapper>
)}
<ViewTableInfo total={dataList.total} total_all={dataList.total_all} handleOrderBy={handleOrderBy} handlePageSize={handlePageSize}>
{userInfo.auth_list && userInfo.auth_list.some(auth => auth.id === authType.authoritySettingDelete) && (
<Button theme={selectedRow.length === 0 ? 'disable' : 'line'} text="선택 삭제" handleClick={handleDeleteModalClose} />
)}
</ViewTableInfo>
<TableStyle>
<caption></caption>
<thead>
<tr>
<th width="40">
<CheckBox id="check-all" handleCheck={handleAllSelect} checked={handleCountSelectedRow()} />
</th>
<th width="80">번호</th>
<th width="20%">관리자 그룹명</th>
<th width="150">그룹 인원</th>
<th>설명</th>
{userInfo.auth_list && userInfo.auth_list.some(auth => auth.id === authType.authoritySettingUpdate) ? <th width="120">권한 설정</th> : ''}
</tr>
</thead>
<tbody>
{dataList.group_list &&
dataList.group_list.map(data => (
<Fragment key={data.row_num}>
<tr>
<td>
<CheckBox name={'select'} id={data.group_id} setData={e => handleSelectCheckBox(e)} />
</td>
<td>{data.row_num}</td>
<td>{data.group_nm}</td>
<td>{data.member_cnt}</td>
<td>{data.description}</td>
{userInfo.auth_list && userInfo.auth_list.some(auth => auth.id === authType.authoritySettingUpdate) && (
<td>
<Button
theme="line"
handleClick={e => {
e.preventDefault();
navigate(`/usermanage/authsetting/${data.group_id}`);
}}
text="수정"
/>
</td>
)}
</tr>
</Fragment>
))}
</tbody>
</TableStyle>
<Pagination postsPerPage={pageSize} totalPosts={dataList && dataList.total_all} setCurrentPage={setCurrentPage} currentPage={currentPage} pageLimit={10} />
{/* 선택 삭제 모달 */}
<Modal min="440px" $padding="40px" $bgcolor="transparent" $view={deleteModalClose}>
<BtnWrapper $justify="flex-end">
<ButtonClose onClick={handleDeleteModalClose} />
</BtnWrapper>
<ModalText $align="center">
선택된 관리자 그룹을 삭제하시겠습니까?
<br />
삭제 관리자 그룹 정보가 제거됩니다.
</ModalText>
<BtnWrapper $gap="10px">
<Button text="취소" theme="line" size="large" width="100%" handleClick={handleDeleteModalClose} />
<Button text="확인" theme="primary" type="submit" size="large" width="100%" handleClick={handleSelectedDelete} />
</BtnWrapper>
</Modal>
{/* 삭제 확인 모달 */}
<Modal min="440px" $padding="40px" $bgcolor="transparent" $view={confirmModalClose}>
<BtnWrapper $justify="flex-end">
<ButtonClose onClick={handleConfirmeModalClose} />
</BtnWrapper>
<ModalText $align="center">{confirmText}</ModalText>
<BtnWrapper $gap="10px">
<Button text="확인" theme="primary" type="submit" size="large" width="100%" handleClick={handleConfirmeModalClose} />
</BtnWrapper>
</Modal>
{/* 등록 확인 모달 */}
<Modal min="440px" $padding="40px" $bgcolor="transparent" $view={registModalClose}>
<BtnWrapper $justify="flex-end">
<ButtonClose onClick={e => handleRegistModalClose(e)} />
</BtnWrapper>
<ModalText $align="center">신규 그룹을 등록하시겠습니까?</ModalText>
<BtnWrapper $gap="10px">
<Button text="취소" theme="line" size="large" width="100%" handleClick={handleRegistModalClose} />
<Button
text="확인"
theme="primary"
type="submit"
size="large"
width="100%"
handleClick={() => {
doubleSubmitFlag || handleGroupSubmit();
setDoubleSubmitFlag(true);
}}
/>
</BtnWrapper>
</Modal>
</>
)}
</>
);
};
export default AuthSetting;

View File

@@ -0,0 +1,250 @@
import { useLocation, useNavigate } from 'react-router-dom';
import { useCallback, useEffect, useMemo, useState } from 'react';
import { useRecoilValue } from 'recoil';
import { useTranslation } from 'react-i18next';
import { authList } from '../../store/authList';
import { authType, modalTypes } from '../../assets/data';
import { menuConfig } from '../../assets/data/menuConfig';
import { Title, FormWrapper, BtnWrapper, TableStyle } from '../../styles/Components';
import { GroupDetailViewList, GroupModify } from '../../apis';
import Button from '../../components/common/button/Button';
import AuthModal from '../../components/common/modal/AuthModal';
import DynamicModal from '../../components/common/modal/DynamicModal';
import { AuthGroupRows } from '../../components/UserManage';
const AuthSettingUpdate = () => {
const location = useLocation();
const navigate = useNavigate();
const userInfo = useRecoilValue(authList);
const { t } = useTranslation();
const id = location.pathname.split('/')[3];
const [msg, setMsg] = useState('');
const {
authGroups,
selectedPermissions,
modalState,
setSelectedPermissions,
setModalState
} = useAuthSetting(id);
if (!userInfo.auth_list?.some(auth => auth.id === authType.authoritySettingUpdate)) {
return <AuthModal />;
}
const handlePermissionChange = (groupId, permissionId) => {
setSelectedPermissions(prev => {
const newPermissions = { ...prev };
if (permissionId === 'all') {
const group = authGroups.find(g => g.id === groupId);
const allPermissions = group.items.flatMap(item =>
Object.values(item.permissions)
);
newPermissions[groupId] = allPermissions;
} else if (permissionId === 'none') {
newPermissions[groupId] = [];
} else {
const currentGroupPermissions = newPermissions[groupId] || [];
if (currentGroupPermissions.includes(Number(permissionId))) {
newPermissions[groupId] = currentGroupPermissions.filter(id =>
id !== Number(permissionId)
);
} else {
newPermissions[groupId] = [...currentGroupPermissions, Number(permissionId)];
}
}
return newPermissions;
});
};
const handleModalView = (type) => {
setModalState((prevState) => ({
...prevState,
[`${type}Modal`]: 'view',
}));
}
const handleModalClose = (type) => {
setModalState((prevState) => ({
...prevState,
[`${type}Modal`]: 'hidden',
}));
}
const handleSubmit = async (type, param = null) => {
switch (type) {
case "cancelConfirm":
setMsg(t('CANCEL_COMPLETED'));
handleModalClose('cancelConfirm');
handleModalView('complete');
break;
case "registConfirm":
const token = sessionStorage.getItem('token');
const resultList = Object.values(selectedPermissions)
.flat()
.map(permissionId => ({ auth_id: permissionId }));
await GroupModify(token, id, resultList);
setMsg(t('SAVE_COMPLETED'));
handleModalClose('registConfirm');
handleModalView('complete');
break;
case "complete":
handleModalClose('complete');
setMsg('');
navigate('/usermanage/authsetting');
// window.location.reload();
break;
}
}
return (
<>
{userInfo.auth_list && !userInfo.auth_list.some(auth => auth.id === authType.authoritySettingUpdate) ? (
<AuthModal />
) : (
<>
<Title>권한 설정</Title>
<FormWrapper $flow="column">
<TableStyle>
<thead>
<tr>
<th width="40"></th>
<th></th>
<th>메뉴</th>
<th>조회</th>
<th>승인</th>
<th>등록/수정</th>
<th>삭제</th>
</tr>
</thead>
<tbody>
{authGroups.map((group) => (
<AuthGroupRows
key={group.id}
group={group}
selectedPermissions={selectedPermissions[group.id] || []}
onPermissionChange={handlePermissionChange}
/>
))}
</tbody>
</TableStyle>
<BtnWrapper $justify="flex-end" $gap="5px">
<Button
theme="line"
handleClick={() => handleModalView('cancelConfirm')}
text="취소"
/>
<Button
theme="primary"
handleClick={() => handleModalView('registConfirm')}
text="저장"
/>
</BtnWrapper>
</FormWrapper>
<DynamicModal
modalType={modalTypes.confirmOkCancel}
view={modalState.cancelConfirmModal}
modalText={t('CANCEL_CONFIRM')}
handleSubmit={() => handleSubmit('cancelConfirm')}
handleCancel={() => handleModalClose('cancelConfirm')}
/>
<DynamicModal
modalType={modalTypes.confirmOkCancel}
view={modalState.registConfirmModal}
modalText={t('SAVE_CONFIRM')}
handleSubmit={() => handleSubmit('registConfirm')}
handleCancel={() => handleModalClose('registConfirm')}
/>
<DynamicModal
modalType={modalTypes.completed}
view={modalState.completeModal}
modalText={msg}
handleSubmit={() => handleSubmit('complete')}
/>
</>
)}
</>
);
};
const useAuthSetting = (initialId) => {
const token = sessionStorage.getItem('token');
const [selectedPermissions, setSelectedPermissions] = useState({});
const [modalState, setModalState] = useState({
cancelConfirmModal: 'hidden',
registConfirmModal: 'hidden',
completeModal: 'hidden',
});
// menuConfig를 기반으로 권한 그룹 구조화
const authGroups = useMemo(() => {
return Object.entries(menuConfig).map(([key, section]) => ({
id: key,
title: section.title,
items: Object.entries(section.items).map(([itemKey, item]) => ({
id: itemKey,
title: item.title,
permissions: item.permissions
}))
}));
}, []);
// 권한 ID로 그룹과 아이템 찾기
const findPermissionLocation = useCallback((permissionId) => {
for (const [groupKey, group] of Object.entries(menuConfig)) {
for (const [itemKey, item] of Object.entries(group.items)) {
if (Object.values(item.permissions).includes(permissionId)) {
return { groupKey, itemKey };
}
}
}
return null;
}, []);
// API 데이터를 menuConfig 구조에 맞게 변환
const formatPermissionsData = useCallback((apiData) => {
return apiData.auth_list.reduce((acc, auth) => {
const location = findPermissionLocation(auth.id);
if (location) {
if (!acc[location.groupKey]) {
acc[location.groupKey] = [];
}
acc[location.groupKey].push(auth.id);
}
return acc;
}, {});
}, [findPermissionLocation]);
useEffect(() => {
const fetchInitialData = async () => {
const data = await GroupDetailViewList(token, initialId);
const formattedPermissions = formatPermissionsData(data);
setSelectedPermissions(formattedPermissions);
};
fetchInitialData();
}, [initialId, formatPermissionsData]);
return {
authGroups,
selectedPermissions,
modalState,
setSelectedPermissions,
setModalState
};
};
export default AuthSettingUpdate;

View File

@@ -0,0 +1,678 @@
import AuthCheckBox from '../../components/common/input/AuthCheckBox';
import CheckBox from '../../components/common/input/CheckBox';
import { Title, FormWrapper, BtnWrapper, TableStyle, ButtonClose, ModalText } from '../../styles/Components';
import Button from '../../components/common/button/Button';
import { useLocation, useNavigate } from 'react-router-dom';
import { useEffect, useState } from 'react';
import { GroupDetailViewList, GroupModify } from '../../apis';
import Modal from '../../components/common/modal/Modal';
import { authList } from '../../store/authList';
import { useRecoilValue } from 'recoil';
import AuthModal from '../../components/common/modal/AuthModal';
import { authType } from '../../assets/data';
const AuthSettingUpdate = () => {
const navigate = useNavigate();
const location = useLocation();
const userInfo = useRecoilValue(authList);
const id = location.pathname.split('/')[3];
const token = sessionStorage.getItem('token');
const [dataList, setDataList] = useState([]);
const [selectedRow, setSelectedRow] = useState({ group1: [], group2: [], group3: [], group4: [] });
const [cancelModalClose, setCancelModalClose] = useState('hidden');
const [saveModalClose, setSaveModalClose] = useState('hidden');
const [confirmModalClose, setConfirmModalClose] = useState('hidden');
const [confirmText, setConfirmText] = useState('');
const [authRow, setAuthRow] = useState([]);
const AdminViewList = ['2', '3', '4'];
const AuthSettingList = ['7', '8'];
const UserViewList = ['12', '35'];
const BoardList = ['17', '18'];
const WhiteList = ['20', '21', '28'];
const MailList = ['23', '29'];
const ReportList = ['25', '31'];
const UserBlockList = ['27', '30']; //DB상 report가 31이고 blocklist가 30인데 추후 테스트 필요해보인다
const ItemList = ['33', '34'];
const EventList = ['37', '38'];
const CaliumRequest = ['40'];
const LandAuctionList = ['41', '42', '43'];
useEffect(() => {
fetchData();
}, []);
useEffect(() => {
selectedRow && setAuthRow(selectedRow);
}, [selectedRow]);
const AdminView = authRow.group1 && authRow.group1.filter(data => AdminViewList.includes(data)).length > 0;
const AuthSettingView = authRow.group1 && authRow.group1.filter(data => AuthSettingList.includes(data)).length > 0;
const CaliumRequestView = authRow.group1 && authRow.group1.filter(data => CaliumRequest.includes(data)).length > 0;
const UserView = authRow.group3 && authRow.group3.filter(data => UserViewList.includes(data)).length > 0;
const BoardView = authRow.group4 && authRow.group4.filter(data => BoardList.includes(data)).length > 0;
const WhiteView = authRow.group4 && authRow.group4.filter(data => WhiteList.includes(data)).length > 0;
const MailView = authRow.group4 && authRow.group4.filter(data => MailList.includes(data)).length > 0;
const ReportView = authRow.group4 && authRow.group4.filter(data => ReportList.includes(data)).length > 0;
const BlockView = authRow.group4 && authRow.group4.filter(data => UserBlockList.includes(data)).length > 0;
const ItemView = authRow.group4 && authRow.group4.filter(data => ItemList.includes(data)).length > 0;
const EventView = authRow.group4 && authRow.group4.filter(data => EventList.includes(data)).length > 0;
const LandAuctionView = authRow.group4 && authRow.group4.filter(data => LandAuctionList.includes(data)).length > 0;
const fetchData = async () => {
setDataList(await GroupDetailViewList(token, id));
};
useEffect(() => {
const Group1List = dataList.auth_list && dataList.auth_list.filter(data => data.id >= 1 && data.id <= 8
|| (data.id >= authType.caliumRequestRead && data.id <= authType.caliumRequestUpdate)).map(group => String(group.id));
const Group2List = dataList.auth_list && dataList.auth_list.filter(data => data.id >= 9 && data.id <= 10).map(group => String(group.id));
const Group3List = dataList.auth_list && dataList.auth_list.filter(data => (data.id >= 11 && data.id <= 15)
|| data.id === 35).map(group => String(group.id));
const Group4List = dataList.auth_list && dataList.auth_list.filter(data => (data.id >= 16 && data.id <= 34)
|| (data.id >= 36 && data.id <= 38)
|| (data.id >= authType.landAuctionRead && data.id <= authType.landAuctionDelete) ).map(group => String(group.id));
setSelectedRow({ group1: Group1List, group2: Group2List, group3: Group3List, group4: Group4List });
}, [dataList]);
// 전체 선택 구현
const handlGroupSelect = order => {
let list = [];
const groupAllCheck = document.getElementById('group' + order);
const groupData = document.getElementsByName('group' + order);
if (groupAllCheck.checked === true) {
// console.log(groupData.length);
for (let i = 0; i < groupData.length; i++) {
const data = document.getElementsByName('group' + order)[i];
data.checked = true;
list.push(data.id);
}
} else if (groupAllCheck.checked === false) {
for (let i = 0; i < groupData.length; i++) {
const data = document.getElementsByName('group' + order)[i];
data.checked = false;
list = [];
}
}
setSelectedRow({ ...selectedRow, ['group' + order]: list });
};
const handleSelectedAuthSave = () => {
let strArr1 = Object.values(selectedRow[`group1`]);
let strArr2 = Object.values(selectedRow[`group2`]);
let strArr3 = Object.values(selectedRow[`group3`]);
let strArr4 = Object.values(selectedRow[`group4`]);
let resultList = [];
const newArr = [...strArr1, ...strArr2, ...strArr3, ...strArr4];
newArr.map(data => resultList.push({ auth_id: data }));
GroupModify(token, id, resultList);
handleSaveModalClose();
handleConfirmeModalClose();
};
const handleExistedId = (id, name) => {
return selectedRow[name] && selectedRow[name].includes(String(id));
};
// 취소 모달
const handleCancelModalClose = () => {
if (cancelModalClose === 'hidden') {
setCancelModalClose('view');
} else {
setCancelModalClose('hidden');
setConfirmText('취소가');
}
};
// 저장 모달
const handleSaveModalClose = () => {
if (saveModalClose === 'hidden') {
setSaveModalClose('view');
} else {
setSaveModalClose('hidden');
setConfirmText('저장이');
}
};
// 확인 모달창
const handleConfirmeModalClose = () => {
if (confirmModalClose === 'hidden') {
setConfirmModalClose('view');
} else {
setConfirmModalClose('hidden');
navigate('/usermanage/authsetting');
window.location.reload();
}
};
const handleCountSelectedRow = (name, count) => {
switch (name) {
case 'group1':
return selectedRow.group1 && selectedRow.group1.filter(data => data >= 1 && data <= 8).length > 0;
case 'group2':
return selectedRow.group2 && selectedRow.group2.filter(data => data >= 9 && data <= 10).length > 0;
case 'group3':
return selectedRow.group3 && selectedRow.group3.filter(data => (data >= 11 && data <= 15)).length > 0;
case 'group4':
return selectedRow.group4 && selectedRow.group4.filter(data => (data >= 16 && data <= 38) || (data >= 41 && data <= 43)).length > 0;
default:
}
};
function killEvent(e) {
e.stopPropagation();
e.preventDefault();
}
// 체크박스 선택 리스트
const handleSelectAuthCheckBox = e => {
let list = selectedRow[e.target.name];
if (e.target.checked) {
list.push(e.target.id);
if (AdminViewList.includes(e.target.id)) list.includes('1') || list.push('1');
else if (AuthSettingList.includes(e.target.id)) list.includes('6') || list.push('6');
else if (UserViewList.includes(e.target.id)) list.includes('11') || list.push('11');
else if (BoardList.includes(e.target.id)) list.includes('16') || list.push('16');
else if (WhiteList.includes(e.target.id)) list.includes('19') || list.push('19');
else if (MailList.includes(e.target.id)) list.includes('22') || list.push('22');
else if (ReportList.includes(e.target.id)) list.includes('24') || list.push('24');
else if (UserBlockList.includes(e.target.id)) list.includes('26') || list.push('26');
else if (ItemList.includes(e.target.id)) list.includes('32') || list.push('32');
else if (EventList.includes(e.target.id)) list.includes('36') || list.push('36');
else if (CaliumRequest.includes(e.target.id)) list.includes('39') || list.push('39');
else if (CaliumRequest.includes(e.target.id)) list.includes('41') || list.push('41');
setSelectedRow({ ...selectedRow, [e.target.name]: list });
} else {
const filterList = list.filter(data => e.target.id !== data);
setSelectedRow({ ...selectedRow, [e.target.name]: filterList });
}
};
return (
<>
{userInfo.auth_list && !userInfo.auth_list.some(auth => auth.id === authType.authoritySettingUpdate) ? (
<AuthModal />
) : (
<>
<Title>권한 설정</Title>
<FormWrapper $flow="column">
<TableStyle>
<caption></caption>
<thead>
<tr>
<th width="40"></th>
<th></th>
<th>메뉴</th>
<th>조회</th>
<th>승인</th>
<th>등록 / 수정</th>
<th>삭제</th>
</tr>
</thead>
<tbody>
<tr>
<td rowSpan="4">
<AuthCheckBox id="group1" handleCheck={() => handlGroupSelect(1)}
checked={handleCountSelectedRow('group1', 8)} />
</td>
<td rowSpan="4">운영자 관리</td>
<td>운영자 조회</td>
<td>
<AuthCheckBox id="1" name="group1"
checked={handleExistedId(authType.adminSearchRead, 'group1') || AdminView}
setData={handleSelectAuthCheckBox} disabled={AdminView} />
</td>
<td>
<AuthCheckBox id="2" name="group1"
checked={handleExistedId(authType.adminSearchConfirm, 'group1')}
setData={handleSelectAuthCheckBox} />
</td>
<td>
<AuthCheckBox id="3" name="group1"
checked={handleExistedId(authType.adminSearchUpdate, 'group1')}
setData={handleSelectAuthCheckBox} />
</td>
<td>
<AuthCheckBox id="4" name="group1"
checked={handleExistedId(authType.adminSearchDelete, 'group1')}
setData={handleSelectAuthCheckBox} />
</td>
</tr>
<tr>
<td>사용 이력 조회</td>
<td>
<AuthCheckBox id="5" name="group1"
checked={handleExistedId(authType.adminLogSearchRead, 'group1')}
setData={handleSelectAuthCheckBox} />
</td>
<td>
<CheckBox disabled={true} />
</td>
<td>
<CheckBox disabled={true} />
</td>
<td>
<CheckBox disabled={true} />
</td>
</tr>
<tr>
<td>권한 설정</td>
<td>
<AuthCheckBox id="6" name="group1"
checked={handleExistedId(authType.authoritySettingRead, 'group1') || AuthSettingView}
disabled={AuthSettingView} setData={handleSelectAuthCheckBox} />
</td>
<td>
<CheckBox disabled={true} />
</td>
<td>
<AuthCheckBox id="7" name="group1"
checked={handleExistedId(authType.authoritySettingUpdate, 'group1')}
setData={handleSelectAuthCheckBox} />
</td>
<td>
<AuthCheckBox id="8" name="group1"
checked={handleExistedId(authType.authoritySettingDelete, 'group1')}
setData={handleSelectAuthCheckBox} />
</td>
</tr>
<tr>
<td>칼리움 요청</td>
<td>
<AuthCheckBox id="39" name="group1"
checked={handleExistedId(authType.caliumRequestRead, 'group1') || CaliumRequestView}
disabled={CaliumRequestView} setData={handleSelectAuthCheckBox} />
</td>
<td>
<CheckBox disabled={true} />
</td>
<td>
<AuthCheckBox id="40" name="group1"
checked={handleExistedId(authType.caliumRequestUpdate, 'group1')}
setData={handleSelectAuthCheckBox} />
</td>
<td>
<CheckBox disabled={true} />
</td>
</tr>
<tr className="table-line">
<td rowSpan="2">
<AuthCheckBox id="group2" handleCheck={() => handlGroupSelect(2)}
checked={handleCountSelectedRow('group2', 2)} />
</td>
<td rowSpan="2">지표 관리</td>
<td>유저 지표</td>
<td>
<AuthCheckBox id="9" name="group2"
checked={handleExistedId(authType.userIndicatorsRead, 'group2')}
setData={handleSelectAuthCheckBox} />
</td>
<td>
<CheckBox disabled={true} />
</td>
<td>
<CheckBox disabled={true} />
</td>
<td>
<CheckBox disabled={true} />
</td>
</tr>
<tr>
<td>경제 지표</td>
<td>
<AuthCheckBox id="10" name="group2"
checked={handleExistedId(authType.economicIndicatorsRead, 'group2')}
setData={handleSelectAuthCheckBox} />
</td>
<td>
<CheckBox disabled={true} />
</td>
<td>
<CheckBox disabled={true} />
</td>
<td>
<CheckBox disabled={true} />
</td>
</tr>
<tr className="table-line">
<td rowSpan="4">
<AuthCheckBox id="group3" handleCheck={() => handlGroupSelect(3)}
checked={handleCountSelectedRow('group3', 5)} />
</td>
<td rowSpan="4">운영 정보 관리</td>
<td>유저 조회</td>
<td>
<AuthCheckBox id="11" name="group3"
checked={handleExistedId(authType.userSearchRead, 'group3') || UserView}
disabled={UserView} setData={handleSelectAuthCheckBox} />
</td>
<td>
<CheckBox disabled={true} />
</td>
<td>
<AuthCheckBox id="12" name="group3"
checked={handleExistedId(authType.userSearchUpdate, 'group3')}
setData={handleSelectAuthCheckBox} />
</td>
<td>
<AuthCheckBox id="35" name="group3"
checked={handleExistedId(authType.userSearchDelete, 'group3')}
setData={handleSelectAuthCheckBox} />
</td>
</tr>
<tr>
<td>컨텐츠 조회</td>
<td>
<AuthCheckBox id="13" name="group3"
checked={handleExistedId(authType.contentSearchRead, 'group3')}
setData={handleSelectAuthCheckBox} />
</td>
<td>
<CheckBox disabled={true} />
</td>
<td>
<CheckBox disabled={true} />
</td>
<td>
<CheckBox disabled={true} />
</td>
</tr>
<tr>
<td>게임 로그 조회</td>
<td>
<AuthCheckBox id="14" name="group3"
checked={handleExistedId(authType.gameLogRead, 'group3')}
setData={handleSelectAuthCheckBox} />
</td>
<td>
<CheckBox disabled={true} />
</td>
<td>
<CheckBox disabled={true} />
</td>
<td>
<CheckBox disabled={true} />
</td>
</tr>
<tr>
<td>크립토 조회</td>
<td>
<AuthCheckBox id="15" name="group3"
checked={handleExistedId(authType.cryptoRead, 'group3')}
setData={handleSelectAuthCheckBox} />
</td>
<td>
<CheckBox disabled={true} />
</td>
<td>
<CheckBox disabled={true} />
</td>
<td>
<CheckBox disabled={true} />
</td>
</tr>
<tr className="table-line">
<td rowSpan="8">
<AuthCheckBox id="group4" handleCheck={() => handlGroupSelect(4)}
checked={handleCountSelectedRow('group4', 25)} />
</td>
<td rowSpan="8">운영 서비스 관리</td>
<td>인게임 메시지</td>
<td>
<AuthCheckBox id="16" name="group4"
checked={handleExistedId(authType.inGameRead, 'group4') || BoardView}
disabled={BoardView} setData={handleSelectAuthCheckBox} />
</td>
<td>
<CheckBox disabled={true} />
</td>
<td>
<AuthCheckBox id="17" name="group4"
checked={handleExistedId(authType.inGameUpdate, 'group4')}
setData={handleSelectAuthCheckBox} />
</td>
<td>
<AuthCheckBox id="18" name="group4"
checked={handleExistedId(authType.inGameDelete, 'group4')}
setData={handleSelectAuthCheckBox} />
</td>
</tr>
<tr>
<td>화이트리스트</td>
<td>
<AuthCheckBox id="19" name="group4"
checked={handleExistedId(authType.whiteListRead, 'group4') || WhiteView}
disabled={WhiteView} setData={handleSelectAuthCheckBox} />
</td>
<td>
<AuthCheckBox id="20" name="group4"
checked={handleExistedId(authType.whiteListConfirm, 'group4')}
setData={handleSelectAuthCheckBox} />
</td>
<td>
<AuthCheckBox id="21" name="group4"
checked={handleExistedId(authType.whiteListUpdate, 'group4')}
setData={handleSelectAuthCheckBox} />
</td>
<td>
<AuthCheckBox id="28" name="group4"
checked={handleExistedId(authType.whiteListDelete, 'group4')}
setData={handleSelectAuthCheckBox} />
</td>
</tr>
<tr>
<td>우편</td>
<td>
<AuthCheckBox id="22" name="group4"
checked={handleExistedId(authType.mailRead, 'group4') || MailView}
disabled={MailView} setData={handleSelectAuthCheckBox} />
</td>
<td>
<CheckBox disabled={true} />
</td>
<td>
<AuthCheckBox id="23" name="group4"
checked={handleExistedId(authType.mailUpdate, 'group4')}
setData={handleSelectAuthCheckBox} />
</td>
<td>
<AuthCheckBox id="29" name="group4"
checked={handleExistedId(authType.mailDelete, 'group4')}
setData={handleSelectAuthCheckBox} />
</td>
</tr>
<tr>
<td>이용자 제재</td>
<td>
<AuthCheckBox id="24" name="group4"
checked={handleExistedId(authType.blackListRead, 'group4') || ReportView}
disabled={ReportView} setData={handleSelectAuthCheckBox} />
</td>
<td>
<CheckBox disabled={true} />
</td>
<td>
<AuthCheckBox id="25" name="group4"
checked={handleExistedId(authType.blackListUpdate, 'group4')}
setData={handleSelectAuthCheckBox} />
</td>
<td>
<AuthCheckBox id="30" name="group4"
checked={handleExistedId(authType.blackListDelete, 'group4')}
setData={handleSelectAuthCheckBox} />
</td>
</tr>
<tr>
<td>신고내역</td>
<td>
<AuthCheckBox id="26" name="group4"
checked={handleExistedId(authType.reportRead, 'group4') || BlockView}
disabled={BlockView} setData={handleSelectAuthCheckBox} />
</td>
<td>
<CheckBox disabled={true} />
</td>
<td>
<AuthCheckBox id="27" name="group4"
checked={handleExistedId(authType.reportUpdate, 'group4')}
setData={handleSelectAuthCheckBox} />
</td>
<td>
<AuthCheckBox id="31" name="group4"
checked={handleExistedId(authType.reportDelete, 'group4')}
setData={handleSelectAuthCheckBox} />
</td>
</tr>
<tr>
<td>아이템 복구 삭제</td>
<td>
<AuthCheckBox id="32" name="group4"
checked={handleExistedId(authType.itemRead, 'group4') || ItemView}
disabled={ItemView} setData={handleSelectAuthCheckBox} />
</td>
<td>
<CheckBox disabled={true} />
</td>
<td>
<AuthCheckBox id="33" name="group4"
checked={handleExistedId(authType.itemUpdate, 'group4')}
setData={handleSelectAuthCheckBox} />
</td>
<td>
<AuthCheckBox id="34" name="group4"
checked={handleExistedId(authType.itemDelete, 'group4')}
setData={handleSelectAuthCheckBox} />
</td>
</tr>
<tr>
<td>보상 이벤트 관리</td>
<td>
<AuthCheckBox id="36" name="group4"
checked={handleExistedId(authType.eventRead, 'group4')}
disabled={EventView} setData={handleSelectAuthCheckBox} />
</td>
<td>
<CheckBox disabled={true} />
</td>
<td>
<AuthCheckBox id="37" name="group4"
checked={handleExistedId(authType.eventUpdate, 'group4')}
setData={handleSelectAuthCheckBox} />
</td>
<td>
<AuthCheckBox id="38" name="group4"
checked={handleExistedId(authType.eventDelete, 'group4')}
setData={handleSelectAuthCheckBox} />
</td>
</tr>
<tr>
<td>랜드 경매 관리</td>
<td>
<AuthCheckBox id="41" name="group4"
checked={handleExistedId(authType.landAuctionRead, 'group4')}
disabled={LandAuctionView} setData={handleSelectAuthCheckBox} />
</td>
<td>
<CheckBox disabled={true} />
</td>
<td>
<AuthCheckBox id="42" name="group4"
checked={handleExistedId(authType.landAuctionUpdate, 'group4')}
setData={handleSelectAuthCheckBox} />
</td>
<td>
<AuthCheckBox id="43" name="group4"
checked={handleExistedId(authType.landAuctionDelete, 'group4')}
setData={handleSelectAuthCheckBox} />
</td>
</tr>
</tbody>
</TableStyle>
<BtnWrapper $justify="flex-end" $gap="5px">
<Button
theme="line"
handleClick={e => {
e.preventDefault();
handleCancelModalClose();
}}
text="취소"
/>
<Button
type="submit"
theme="primary"
text="저장"
handleClick={e => {
e.preventDefault();
handleSaveModalClose();
}}
/>
</BtnWrapper>
</FormWrapper>
{/* 취소 모달 */}
<Modal min="440px" $padding="40px" $bgcolor="transparent" $view={cancelModalClose}>
<BtnWrapper $justify="flex-end">
<ButtonClose onClick={handleCancelModalClose} />
</BtnWrapper>
<ModalText $align="center">
취소하시겠습니까?
<br />
취소 변경된 값은 초기화됩니다.
</ModalText>
<BtnWrapper $gap="10px">
<Button text="취소" theme="line" size="large" width="100%" handleClick={handleCancelModalClose} />
<Button
text="확인"
theme="primary"
type="submit"
size="large"
width="100%"
handleClick={() => {
handleCancelModalClose();
handleConfirmeModalClose();
}}
/>
</BtnWrapper>
</Modal>
{/* 저장 모달 */}
<Modal min="440px" $padding="40px" $bgcolor="transparent" $view={saveModalClose}>
<BtnWrapper $justify="flex-end">
<ButtonClose onClick={handleSaveModalClose} />
</BtnWrapper>
<ModalText $align="center">저장하시겠습니까? </ModalText>
<BtnWrapper $gap="10px">
<Button text="취소" theme="line" size="large" width="100%" handleClick={handleSaveModalClose} />
<Button text="확인" theme="primary" type="submit" size="large" width="100%" handleClick={handleSelectedAuthSave} />
</BtnWrapper>
</Modal>
{/* 확인 모달 */}
<Modal min="440px" $padding="40px" $bgcolor="transparent" $view={confirmModalClose}>
<BtnWrapper $justify="flex-end">
<ButtonClose onClick={handleConfirmeModalClose} />
</BtnWrapper>
<ModalText $align="center">{confirmText} 완료되었습니다.</ModalText>
<BtnWrapper $gap="10px">
<Button text="확인" theme="primary" type="submit" size="large" width="100%" handleClick={handleConfirmeModalClose} />
</BtnWrapper>
</Modal>
</>
)}
</>
);
};
export default AuthSettingUpdate;

View File

@@ -0,0 +1,277 @@
import { useState, useEffect, Fragment, useRef } from 'react';
import { useRecoilValue } from 'recoil';
import { useTranslation } from 'react-i18next';
import 'react-datepicker/dist/react-datepicker.css';
import { authList } from '../../store/authList';
import {
authType,
commonStatus,
modalTypes,
ViewTitleCountType,
caliumStatus
} from '../../assets/data';
import { Title, FormWrapper, TableStyle, TableWrapper, PopupMessage } from '../../styles/Components';
import {
StatusWapper,
ChargeBtn,
StatusLabel,
} from '../../styles/ModuleComponents';
import {Button, ExcelDownButton, Pagination, DynamicModal, ViewTableInfo, Loading} from '../../components/common';
import { convertKTC, truncateText } from '../../utils';
import { CaliumRequestRegistModal, CaliumRequestSearchBar } from '../../components/UserManage';
import { CaliumCharge, CaliumRequestView } from '../../apis';
import { withAuth } from '../../utils/hook';
import { convertEndDateToISO, convertStartDateToISO } from '../../utils/date';
const CaliumRequest = () => {
const token = sessionStorage.getItem('token');
const userInfo = useRecoilValue(authList);
const { t } = useTranslation();
const tableRef = useRef(null);
const [currentPage, setCurrentPage] = useState(1);
const [pageSize, setPageSize] = useState(50);
const [dataList, setDataList] = useState([]);
const [detailData, setDetailData] = useState('');
const [loading, setLoading] = useState(false);
const [alertMsg, setAlertMsg] = useState('');
const [selectCharge, setSelectCharge] = useState({});
const [searchData, setSearchData] = useState({
content: '',
status: 'ALL',
startDate: '',
endDate: '',
});
const [orderBy, setOrderBy] = useState('DESC');
const [modalState, setModalState] = useState({
detailModal: 'hidden',
registerModal: 'hidden',
chargedCompleteModal: 'hidden',
chargedConfirmModal: 'hidden'
});
useEffect(() => {
fetchData('', 'ALL', '', '');
}, [currentPage]);
// 리스트 조회
const fetchData = async (content, status, startDate, endDate, order, size) => {
setDataList(
await CaliumRequestView(
token,
content,
status,
startDate && convertStartDateToISO(startDate),
endDate && convertEndDateToISO(endDate),
order ? order : orderBy,
size ? size : pageSize,
currentPage,
),
);
};
// 검색 함수
const handleSearch = (content, status, startDate, endDate) => {
fetchData(content, status, startDate, endDate);
};
// 오름차순 내림차순
const handleOrderBy = e => {
const order = e.target.value;
setOrderBy(order);
fetchData(
searchData.content,
searchData.status,
searchData.startDate,
searchData.endDate,
order,
pageSize,
);
};
const handlePageSize = e => {
const size = e.target.value;
setPageSize(size);
setCurrentPage(1);
fetchData(
searchData.content,
searchData.status,
searchData.startDate,
searchData.endDate,
orderBy,
size,
1,
);
};
// 상세보기 호출
// const handleDetailModal = async (e, id) => {
// await EventDetailView(token, id).then(data => {
// setDetailData(data);
// });
//
// e.preventDefault();
// handleModalView('detail');
// };
const handleModalView = (type) => {
setModalState((prevState) => ({
...prevState,
[`${type}Modal`]: 'view',
}));
}
const handleModalClose = (type) => {
setModalState((prevState) => ({
...prevState,
[`${type}Modal`]: 'hidden',
}));
}
const handleSubmit = async (type, param = null) => {
switch (type) {
case "detail":
setDetailData(param);
handleModalView('detail');
break;
case "chargedConfirm":
setSelectCharge({id: param.id, count: param.count});
handleModalView('chargedConfirm');
break;
case "charged":
handleModalClose('chargedConfirm');
setLoading(true);
await CaliumCharge(token, selectCharge).then(data => {
setLoading(false);
if(data.result === "SUCCESS") {
setAlertMsg(t('CHARGE_COMPLTED'));
handleModalView('chargedComplete');
}else if(data.result === "ERROR_CALIUM_FINISH") {
setAlertMsg(t('CHARGE_FINISH_FAIL'));
handleModalView('chargedComplete');
}else{
setAlertMsg(t('CHARGE_FAIL'));
handleModalView('chargedComplete');
}
setSelectCharge({});
});
break;
case "chargedComplete":
handleModalClose('chargedComplete');
await fetchData(
searchData.content,
searchData.status,
searchData.startDate,
searchData.endDate
);
break;
}
}
return (
<>
<Title>칼리움 사용 수량 요청</Title>
<FormWrapper>
<CaliumRequestSearchBar handleSearch={handleSearch} setResultData={setSearchData} />
</FormWrapper>
<ViewTableInfo total={dataList.total} total_all={dataList.total_all} handleOrderBy={handleOrderBy} handlePageSize={handlePageSize} countType={ViewTitleCountType.calium}>
<ExcelDownButton tableRef={tableRef} fileName={t('FILE_CALIUM_REQUEST')} />
{userInfo.auth_list && userInfo.auth_list.some(auth => auth.id === authType.eventUpdate) && (
<Button
theme="primary"
text="요청 등록"
type="button"
handleClick={e => {
e.preventDefault();
handleModalView('register');
}}
/>
)}
</ViewTableInfo>
<TableWrapper>
<TableStyle ref={tableRef}>
<caption></caption>
<thead>
<tr>
{/*<th width="80">번호</th>*/}
<th width="210">요청 일시</th>
<th width="140">요청 부서</th>
<th width="100">요청자</th>
<th>요청 사유</th>
<th width="80">요청수량</th>
<th width="160">진행상태</th>
<th width="210">최종 처리일시</th>
</tr>
</thead>
<tbody>
{dataList.list &&
dataList.list.map(calium => (
<Fragment key={calium.row_num}>
<tr>
{/*<td>{calium.row_num}</td>*/}
<td>{convertKTC(calium.create_dt)}</td>
<td>{calium.dept}</td>
<td>{calium.create_by}</td>
<td>
<PopupMessage onClick={e => handleSubmit('detail', calium.content)}>
{truncateText(calium.content)}
</PopupMessage>
</td>
<td>{calium.count}</td>
<StatusWapper>
<StatusLabel $status={calium.status}>{caliumStatus.map(data => data.value === calium.status && data.name)}</StatusLabel>
{calium.status === commonStatus.complete && userInfo.auth_list?.some(auth => auth.id === authType.eventUpdate) && calium.create_by === userInfo.name &&
<ChargeBtn onClick={() => handleSubmit('chargedConfirm', {id: calium.id, count: calium.count})}>충전</ChargeBtn>
}
</StatusWapper>
<td>{convertKTC(calium.state_time)}</td>
</tr>
</Fragment>
))}
</tbody>
</TableStyle>
</TableWrapper>
<Pagination postsPerPage={pageSize} totalPosts={dataList && dataList.total_all} setCurrentPage={setCurrentPage} currentPage={currentPage} pageLimit={10} />
{/*상세*/}
{/*<EventDetailModal detailView={modalState.detailModal} handleDetailView={() => handleModalClose('detail')} content={detailData} setDetailData={setDetailData}/>*/}
<CaliumRequestRegistModal registView={modalState.registerModal} setRegistView={() => handleModalClose('register')} userInfo={userInfo} />
<DynamicModal
modalType={modalTypes.confirmOkCancel}
view={modalState.chargedConfirmModal}
handleCancel={() => handleModalClose('chargedConfirm')}
handleSubmit={() => handleSubmit('charged')}
modalText={t('CALIUM_CHARGE_CONFIRM')}
/>
<DynamicModal
modalType={modalTypes.completed}
view={modalState.chargedCompleteModal}
modalText={alertMsg}
handleSubmit={() => handleSubmit('chargedComplete')}
/>
<DynamicModal
modalType={modalTypes.completed}
view={modalState.chargedCompleteModal}
modalText={alertMsg}
handleSubmit={() => handleSubmit('chargedComplete')}
/>
<DynamicModal
modalType={modalTypes.completed}
view={modalState.detailModal}
modalText={detailData}
handleSubmit={() => handleModalClose('detail')}
/>
{loading && <Loading/>}
</>
);
};
export default withAuth(authType.caliumRequestRead)(CaliumRequest);

View File

@@ -0,0 +1,170 @@
import { Fragment, useState, useEffect } from 'react';
import LogViewSearchBar from '../../components/UserManage/LogViewSearchBar';
import { Title, FormWrapper, SelectInput, TableInfo, ListCount, ListOption, TableStyle, BtnWrapper, ButtonClose, ModalText } from '../../styles/Components';
import Button from '../../components/common/button/Button';
import Pagination from '../../components/common/Pagination/Pagination';
import Modal from '../../components/common/modal/Modal';
import LogViewModal from '../../components/UserManage/LogViewModal';
import { LogViewList } from '../../apis';
import { useNavigate } from 'react-router-dom';
import { authList } from '../../store/authList';
import { useRecoilValue } from 'recoil';
import { logOption } from '../../assets/data';
import { convertKTC } from '../../utils';
const LogView = () => {
const navigate = useNavigate();
const userInfo = useRecoilValue(authList);
const [currentPage, setCurrentPage] = useState(1);
const [stateModal, setStateModal] = useState('hidden');
const [dataList, setDataList] = useState([]);
const [detailData, setDetailData] = useState('');
const [searchData, setSearchData] = useState({});
const [orderBy, setOrderBy] = useState('DESC');
const [pageSize, setPageSize] = useState('50');
const handleButtonClick = content => {
setStateModal('view');
setDetailData(content);
};
const handleModal = () => {
if (stateModal === 'hidden') {
setStateModal('view');
} else {
setStateModal('hidden');
}
};
const handleOrderBy = e => {
const order = e.target.value;
setOrderBy(order);
fetchData(
searchData.searchOption,
searchData.data,
searchData.logOption,
searchData.startDate && new Date(searchData.startDate).toISOString(),
searchData.endDate && new Date(searchData.endDate).toISOString(),
order,
pageSize,
);
};
const handlePageSize = e => {
const size = e.target.value;
setPageSize(size);
setCurrentPage(1);
fetchData(
searchData.searchOption,
searchData.data,
searchData.logOption,
searchData.startDate && new Date(searchData.startDate).toISOString(),
searchData.endDate && new Date(searchData.endDate).toISOString(),
orderBy,
size,
1,
);
};
const fetchData = async (searchType, searchKey, historyType, startDt, endDt, order, size) => {
const token = sessionStorage.getItem('token');
setDataList(await LogViewList(token, searchType, searchKey, historyType, startDt, endDt, order ? order : orderBy, size ? size : pageSize, currentPage));
};
useEffect(() => {
fetchData(
searchData.searchOption,
searchData.data,
searchData.logOption,
searchData.startDate && new Date(searchData.startDate).toISOString(),
searchData.endDate && new Date(searchData.endDate).toISOString(),
orderBy,
pageSize,
);
}, [currentPage]);
const handleSearch = (searchType, searchKey, historyType, startDt, endDt) => {
fetchData(searchType, searchKey, historyType, startDt, endDt);
};
return (
<>
{userInfo.auth_list && !userInfo.auth_list.some(auth => auth.id === 5) ? (
<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>
<FormWrapper action="" $flow="row">
<LogViewSearchBar handleSearch={handleSearch} resultData={setSearchData} />
</FormWrapper>
<TableInfo>
<ListCount>
: {dataList && dataList.total} / {dataList && dataList.total_all}
</ListCount>
<ListOption>
<SelectInput className="input-select" onChange={e => handleOrderBy(e)}>
<option value="DESC">최신일자</option>
<option value="ASC">오래된순</option>
</SelectInput>
<SelectInput className="input-select" onChange={e => handlePageSize(e)}>
<option value="50">50</option>
<option value="100">100</option>
</SelectInput>
</ListOption>
</TableInfo>
<TableStyle>
<caption></caption>
<thead>
<tr>
<th width="200">일시</th>
<th width="200">이름</th>
<th width="50%">ID</th>
<th width="30%">사용 이력</th>
{/* <th width="25%">상세 이력</th> */}
<th width="150">상세 보기</th>
</tr>
</thead>
<tbody>
{dataList.list &&
dataList.list.map((history, index) => (
<Fragment key={index}>
<tr>
<td>{convertKTC(history.create_dt)}</td>
<td>{history.name}</td>
<td>{history.mail}</td>
<td>{logOption.map(data => data.value === history.history_type && data.name)}</td>
<td>
<Button theme="line" text="JSON INFO" handleClick={() => handleButtonClick(history.content)} />
</td>
</tr>
</Fragment>
))}
</tbody>
</TableStyle>
<Pagination postsPerPage={pageSize} totalPosts={dataList && dataList.total_all} setCurrentPage={setCurrentPage} currentPage={currentPage} pageLimit={10} />
<LogViewModal stateModal={stateModal} handleModal={handleModal} data={detailData} />
</>
)}
</>
);
};
export default LogView;

View File

@@ -0,0 +1,6 @@
export { default as AdminView } from './AdminView';
export { default as LogView } from './LogView';
export { default as AuthSetting } from './AuthSetting';
export { default as AuthSettingUpdate } from './AuthSettingUpdate';
export { default as CaliumRequest} from './CaliumRequest';
export { default as CaliumRequestRegist} from '../../components/UserManage/CaliumRequestRegistModal';

View File

@@ -0,0 +1,44 @@
import React from 'react';
import { styled } from 'styled-components';
const Button = (props) => {
const {
isActive,
isDisabled,
backColor,
color,
} = props;
return (
<>
<Block
active={isActive}
disabled={isDisabled}
backColor={backColor}
color={color}
>확인</Block>
</>
);
};
export default Button;
const Block = styled.button `
width: 130px;
padding: 13px;
font-size: 13px;
border: none;
outline: none;
cursor: pointer;
font-weight: bold;
color: ${({color})=> color};
background-color: ${(props) => props.backColor};
`;
// 확인 버튼 불 들어왔을 때
// const Block = styled.button `
// color: #E1E3ED;
// background-color: #00A6B5;
// `;

View File

@@ -0,0 +1,35 @@
import React from 'react';
import { styled } from 'styled-components';
const Input = ({ placeholder, inputPw, inputType, register,id }) => {
return (
<Wrap>
<InputBox
placeholder={placeholder}
pw={inputPw}
type="password"
id={id}
{...register(id)}
/>
</Wrap>
);
};
export default Input;
const Wrap = styled.div`
& input:focus {
border-color: #00626B;
outline: none;
}
`;
const InputBox = styled.input`
height: 45px;
width: 300px;
border: 1px solid #E1E3ED;
padding-left: 20px;
color: #2C2C2C;
font-size: 16px;
`;

View File

@@ -0,0 +1,28 @@
import * as Yup from 'yup';
export const SignUpValidationSchema = Yup.object({
email: Yup.string()
.matches(/^[^@\s]+@[^@\s]+\.[^@\s]+$/, '이메일 형식에 맞지 않습니다.')
.required('이메일을 입력해주세요.'),
password: Yup.string()
.matches(
/^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d]{6,10}$/,
'6~10자의 영문, 숫자를 조합해서 입력하세요.',
)
.min(6, '6글자 이상 10글자 이하로 입력해주세요.')
.max(10, '6글자 이상 10글자 이하로 입력해주세요.')
.required('비밀번호를 입력해주세요.'),
passwordConfirm: Yup.string()
.oneOf([Yup.ref('password'), null], '비밀번호가 다릅니다.')
.required('비밀번호를 한번 더 입력해주세요.'),
phone: Yup.string()
.matches(/^[0-9]{11}$/i, '번호는 01012345678형태로 입력해주세요')
.required('휴대폰 번호를 입력해주세요'),
name: Yup.string()
.matches(/^[가-힣]{2,5}$/, '한글로 입력해주세요.')
.min(2, '2글자 이상 5글자 이하로 입력해주세요')
.max(4, '2글자 이상 5글자 이하로 입력해주세요')
.required('이름을 입력해주세요.'),
code: Yup.string().length(6, '코드의 길이가 다릅니다.').required('인증코드를 입력해주세요.'),
privacy: Yup.boolean().oneOf([true], '회원가입을 위해 약관에 동의해주세요.'),
});

View File

@@ -0,0 +1,111 @@
import React from 'react';
import Input from './Input';
import Button from './Button';
import { styled } from 'styled-components';
import { useState } from 'react';
import { useForm } from 'react-hook-form';
import Validation from './Validation';
const Regist = () => {
const {
register,
setValue,
handleSubmit,
formState: { errors },
watch,
} = useForm({
resolver: yupResolver(validation),
});
const values = watch();
return (
<>
<form>
<Wrap>
<Title>비밀번호 재설정</Title>
<InputArea>
<InputBlock>
<InputTitle>비밀번호</InputTitle>
<Input
id="pw1"
// onChange={e=> onChangePW(e)}
register={register}
/>
<InfoText>영어/숫자/특수문자만 가능합니다. (8~20 이내로)</InfoText>
</InputBlock>
<InputBlock>
<InputTitle>비밀번호 확인</InputTitle>
<Input
// onChange={e=> onChangePW(e)}
id="pw2"
register={register}
/>
<ErrorText>비밀번호를 다시 확인해주세요.</ErrorText>
</InputBlock>
<ButtonArea>
<Button
backColor={values.pw1 && values.pw2 ? '#00A6B5' : '#E1E3ED'}
color={values.pw1 && values.pw2 ? '#E1E3ED' : '#00A6B5'}
isDisabled={values.pw1 && values.pw2 ? false : true}
/>
</ButtonArea>
</InputArea>
</Wrap>
</form>
</>
);
};
export default Regist;
const Wrap = styled.div`
text-align: center;
margin: 15%;
`;
const Title = styled.h1`
font-size: 32px;
line-height: 40px;
color: #00626b;
`;
const InputArea = styled.div`
display: grid;
justify-content: center;
`;
const InputTitle = styled.p`
font-size: 16px;
`;
const InputBlock = styled.div`
width: 320px;
font-size: 16px;
text-align: left;
`;
const InfoText = styled.p`
margin-left: 10px;
font-size: 13px;
text-align: left;
color: #818181;
`;
const ErrorText = styled.p`
margin-left: 10px;
font-size: 13px;
text-align: left;
color: #fe565e;
`;
const ButtonArea = styled.div`
display: flex;
justify-content: right;
margin-top: 20px;
`;