유저 조회 접속 상태 표시

유저 조회 kick 처리
This commit is contained in:
2025-04-02 18:02:11 +09:00
parent 73f8448b24
commit 2c693b2503
5 changed files with 161 additions and 65 deletions

View File

@@ -65,6 +65,20 @@ export const UserChangeAdminLevel = async (token, params) => {
} }
}; };
export const UserKick = async (token, params) => {
try {
const res = await Axios.put('/api/v1/users/user-kick', params, {
headers: { Authorization: `Bearer ${token}` },
});
return res.data;
} catch (e) {
if (e instanceof Error) {
throw new Error('UserKick Error', e);
}
}
};
// 아바타 조회 // 아바타 조회
export const UserAvatarView = async (token, guid) => { export const UserAvatarView = async (token, guid) => {
try { try {

View File

@@ -247,6 +247,26 @@ export const opSuccessType = [
{ value: false, name: '실패' }, { value: false, name: '실패' },
]; ];
export const opUserSessionType = [
{ value: true, name: '접속중' },
{ value: false, name: '미접속' },
];
export const opYNType = [
{ value: true, name: 'Y' },
{ value: false, name: 'N' },
];
export const opReadType = [
{ value: true, name: '확인' },
{ value: false, name: '미확인' },
];
export const opPickupType = [
{ value: true, name: '수령' },
{ value: false, name: '미수령' },
];
// export const logAction = [ // export const logAction = [
// { value: "None", name: "ALL" }, // { value: "None", name: "ALL" },
// { value: "AIChatDeleteCharacter", name: "NPC 삭제" }, // { value: "AIChatDeleteCharacter", name: "NPC 삭제" },

View File

@@ -1,11 +1,11 @@
import { useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import styled from 'styled-components'; import styled from 'styled-components';
import Profile from '../../assets/img/datamanage/img-profile.png'; import Profile from '../../assets/img/datamanage/img-profile.png';
import NicknameChangeModal from '../../components/DataManage/NicknameChangeModal'; import NicknameChangeModal from '../../components/DataManage/NicknameChangeModal';
import EditIcon from '../../assets/img/icon/icon-edit.png'; import EditIcon from '../../assets/img/icon/icon-edit.png';
import { UserChangeAdminLevel, UserInfoView } from '../../apis/Users'; import { UserChangeAdminLevel, UserInfoView, UserKick } from '../../apis/Users';
import { SelectInput } from '../../styles/Components'; import { SelectInput } from '../../styles/Components';
import { adminLevelType, authType, modalTypes } from '../../assets/data'; import { adminLevelType, authType, modalTypes } from '../../assets/data';
import DynamicModal from '../common/modal/DynamicModal'; import DynamicModal from '../common/modal/DynamicModal';
@@ -16,53 +16,89 @@ import { convertKTC } from '../../utils';
import { EditButton, ProfileWrapper, UserDefault, UserInfoTable } from '../../styles/ModuleComponents'; import { EditButton, ProfileWrapper, UserDefault, UserInfoTable } from '../../styles/ModuleComponents';
import { TableSkeleton } from '../Skeleton/TableSkeleton'; import { TableSkeleton } from '../Skeleton/TableSkeleton';
import { UserInfoSkeleton } from '../Skeleton/UserInfoSkeleton'; import { UserInfoSkeleton } from '../Skeleton/UserInfoSkeleton';
import { opUserSessionType } from '../../assets/data/options';
import Button from '../common/button/Button';
import { useModal } from '../../utils/hook';
import { InitData } from '../../apis/Data';
const UserDefaultInfo = ({ userInfo }) => { const UserDefaultInfo = ({ userInfo }) => {
const { t } = useTranslation(); const { t } = useTranslation();
const authInfo = useRecoilValue(authList); const authInfo = useRecoilValue(authList);
const token = sessionStorage.getItem('token');
const [pwPop, setPwPop] = useState('hidden'); const {
const [gmModal, setGmModal] = useState('hidden'); modalState,
handleModalView,
handleModalClose
} = useModal({
userKick: 'hidden',
gmLevelChange: 'hidden',
pwChange: 'hidden'
});
const [alertMsg, setAlertMsg] = useState('');
const [dataList, setDataList] = useState({}); const [dataList, setDataList] = useState({});
const [adminLevel, setAdminLevel] = useState('0'); const [adminLevel, setAdminLevel] = useState('0');
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
const handleClick = () => {
if (pwPop === 'hidden') setPwPop('view');
else setPwPop('hidden');
};
useEffect(() => { useEffect(() => {
fetchData(); fetchData();
}, [userInfo]); }, [userInfo]);
const fetchData = async () => { const fetchData = async () => {
const token = sessionStorage.getItem('token'); setLoading(true);
await UserInfoView(token, userInfo.guid).then(data => { await UserInfoView(token, userInfo.guid).then(data => {
setDataList(data); setDataList(data);
setLoading(false); setLoading(false);
}); });
}; };
const handleGMChange = (e) =>{ const handleSubmit = async (type, param = null) => {
setAdminLevel(e.target.value);
setGmModal('view');
}
const handleSubmit = async () => {
const token = sessionStorage.getItem('token');
let params = {}; let params = {};
params.guid = userInfo.guid;
params.admin_level = adminLevel;
await UserChangeAdminLevel(token, params); switch (type) {
case "gmLevelChangeSubmit":
setAdminLevel(param);
handleCancel(); handleModalView('gmLevelChange');
await fetchData(); break;
} case "userKickSubmit":
handleModalView('userKick');
break;
case "gmLevelChange":
setLoading(true);
const handleCancel = () => { params.guid = userInfo.guid;
setGmModal('hidden'); params.admin_level = adminLevel;
await UserChangeAdminLevel(token, params).then(data =>{
setAlertMsg(t('USER_GM_CHANGE_COMPLETE'))
}).catch(error => {
console.log(error);
}).finally(() => {
setLoading(false);
handleModalClose('gmLevelChange');
fetchData();
});
break;
case "userKick":
params.guid = userInfo.guid;
await UserKick(token, params).then((data) =>{
setAlertMsg(t('USER_KICK_COMPLETE'))
}).catch(error => {
console.log(error);
}).finally(() => {
setLoading(false);
handleModalClose('userKick');
fetchData();
});
break;
case "registComplete":
handleModalClose('registComplete');
break;
case "warning":
setAlertMsg('');
break;
}
} }
return ( return (
@@ -76,11 +112,11 @@ const UserDefaultInfo = ({ userInfo }) => {
<UserInfoTable $maxwidth="530px"> <UserInfoTable $maxwidth="530px">
<tbody> <tbody>
<tr> <tr>
<th>AID(GUID)</th> <th>GUID</th>
<td>{dataList.user_info && dataList.user_info.aid}</td> <td>{dataList.user_info && dataList.user_info.aid}</td>
</tr> </tr>
<tr> <tr>
<th>계정 ID</th> <th>Account ID</th>
<td>{dataList.user_info && dataList.user_info.user_id}</td> <td>{dataList.user_info && dataList.user_info.user_id}</td>
</tr> </tr>
<tr> <tr>
@@ -88,12 +124,11 @@ const UserDefaultInfo = ({ userInfo }) => {
<td>{dataList.user_info && dataList.user_info.nation}</td> <td>{dataList.user_info && dataList.user_info.nation}</td>
</tr> </tr>
<tr> <tr>
<th>멤버십</th> <th>접속상태</th>
<td>{dataList.user_info && dataList.user_info.membership}</td> <StatusCell>{dataList.user_session !== undefined && opUserSessionType.find(session => session.value === dataList.user_session)?.name}
</tr> {<Button theme={dataList.user_session ? "line" : "disable"} id={"user_session"} name="kick" text="KICK" handleClick={e => handleSubmit('userKickSubmit')} disabled={!dataList.user_session}/>}
<tr> {/*{<Button theme={dataList.user_session ? "line" : "disable"} id={"user_session"} name="kick" text="KICK" handleClick={e => handleSubmit('userKickSubmit')} />}*/}
<th>친구 추천코드</th> </StatusCell>
<td>{dataList.user_info && dataList.user_info.friend_code}</td>
</tr> </tr>
<tr> <tr>
<th>계정 생성일</th> <th>계정 생성일</th>
@@ -103,10 +138,7 @@ const UserDefaultInfo = ({ userInfo }) => {
</tr> </tr>
<tr> <tr>
<th>최근 접속일자</th> <th>최근 접속일자</th>
<td> <td>{dataList.user_info && convertKTC(dataList.user_info.access_dt)}</td>
{/*{dataList.user_info && String(new Date(new Date(dataList.user_info.access_dt).setHours(new Date(dataList.user_info.access_dt).getHours() + 9)).toLocaleString())}*/}
{dataList.user_info && convertKTC(dataList.user_info.access_dt)}
</td>
</tr> </tr>
<tr> <tr>
<th>최근 종료일자</th> <th>최근 종료일자</th>
@@ -121,7 +153,9 @@ const UserDefaultInfo = ({ userInfo }) => {
<tr> <tr>
<th>GM권한</th> <th>GM권한</th>
<td> <td>
<SelectInput value={dataList.user_info && dataList.user_info.admin_level} onChange={(e) => handleGMChange(e)} disabled={authInfo.auth_list && !authInfo.auth_list.some(auth => auth.id === authType.userSearchUpdate)} > <SelectInput value={dataList.user_info && dataList.user_info.admin_level}
onChange={e => handleSubmit('gmLevelChangeSubmit', e.target.value)}
disabled={authInfo.auth_list && !authInfo.auth_list.some(auth => auth.id === authType.userSearchUpdate)} >
{adminLevelType.map((data, index) => ( {adminLevelType.map((data, index) => (
<option key={index} value={data.value}> <option key={index} value={data.value}>
{data.name} {data.name}
@@ -149,7 +183,7 @@ const UserDefaultInfo = ({ userInfo }) => {
hidden={true} hidden={true}
onClick={e => { onClick={e => {
e.preventDefault(); e.preventDefault();
handleClick(); handleModalClose('pwChange');
}}></EditButton> }}></EditButton>
</td> </td>
</tr> </tr>
@@ -172,15 +206,36 @@ const UserDefaultInfo = ({ userInfo }) => {
</tbody> </tbody>
</UserInfoTable> </UserInfoTable>
</div> </div>
<NicknameChangeModal pwPop={pwPop} handleClick={handleClick} dataList={dataList} /> <NicknameChangeModal pwPop={modalState.pwChangeModal} handleClick={() => handleModalClose('pwChange')} dataList={dataList} />
<DynamicModal <DynamicModal
modalType={modalTypes.childOkCancel} modalType={modalTypes.confirmOkCancel}
view={gmModal} view={modalState.gmLevelChangeModal}
modalText={t('USER_GM_CHANGE')} modalText={t('USER_GM_CHANGE')}
handleCancel={handleCancel} handleSubmit={() => handleSubmit('gmLevelChange')}
handleSubmit={handleSubmit} handleCancel={() => handleModalClose('gmLevelChange')}
/>
<DynamicModal
modalType={modalTypes.confirmOkCancel}
view={modalState.userKickModal}
modalText={t('USER_KICK_CONFIRM')}
handleSubmit={() => handleSubmit('userKick')}
handleCancel={() => handleModalClose('userKick')}
/>
{/* 경고 모달 */}
<DynamicModal
modalType={modalTypes.completed}
view={alertMsg ? 'view' : 'hidden'}
modalText={alertMsg}
handleSubmit={() => setAlertMsg('')}
/> />
</> </>
); );
}; };
export default UserDefaultInfo; export default UserDefaultInfo;
const StatusCell = styled.td`
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px;
`;

View File

@@ -9,37 +9,41 @@ export const UserInfoSkeleton = () => {
<SkeletonImg width="200px" height="150px" /> <SkeletonImg width="200px" height="150px" />
</ProfileWrapper> </ProfileWrapper>
<UserInfoTable> <UserInfoTable>
<Skeleton width="530px" height="30px" /> <tbody>
<Skeleton width="530px" height="30px" /> {Array.from({ length: 10 }).map((_, index) => (
<Skeleton width="530px" height="30px" /> <tr key={index}>
<Skeleton width="530px" height="30px" /> <td>
<Skeleton width="530px" height="30px" /> <Skeleton width="530px" height="30px" />
<Skeleton width="530px" height="30px" /> </td>
<Skeleton width="530px" height="30px" /> </tr>
<Skeleton width="530px" height="30px" /> ))}
<Skeleton width="530px" height="30px" /> </tbody>
<Skeleton width="530px" height="30px" />
</UserInfoTable> </UserInfoTable>
</UserDefault> </UserDefault>
<UserInfoTable> <UserInfoTable>
<Skeleton width="750px" height="30px" /> <tbody>
<Skeleton width="750px" height="30px" /> {Array.from({ length: 4 }).map((_, index) => (
<Skeleton width="750px" height="30px" /> <tr key={index}>
<Skeleton width="750px" height="30px" /> <td>
<Skeleton width="750px" height="30px" />
</td>
</tr>
))}
</tbody>
</UserInfoTable> </UserInfoTable>
</> </>
) )
} }
const UserDefault = styled.div` const UserDefault = styled.div`
display: flex; display: flex;
gap: 40px; gap: 40px;
margin-bottom: 30px; margin-bottom: 30px;
justify-content: space-between; justify-content: space-between;
`; `;
const ProfileWrapper = styled.div` const ProfileWrapper = styled.div`
width: 150px; width: 150px;
height: 150px; height: 150px;
border-radius: 75px; border-radius: 75px;
overflow: hidden; overflow: hidden;
display: inline-flex; display: inline-flex;

View File

@@ -16,6 +16,9 @@ const resources = {
API_FAIL: '처리 중 오류가 발생하였습니다. 잠시 후 다시 한번 진행해 주세요. 오류가 지속될 경우, 담당자에게 문의해주세요.', API_FAIL: '처리 중 오류가 발생하였습니다. 잠시 후 다시 한번 진행해 주세요. 오류가 지속될 경우, 담당자에게 문의해주세요.',
USER_MAIL_DEL_CONFIRM: '해당 우편을 삭제하시겠습니까?', USER_MAIL_DEL_CONFIRM: '해당 우편을 삭제하시겠습니까?',
USER_GM_CHANGE: 'GM 권한을 변경하시겠습니까?', USER_GM_CHANGE: 'GM 권한을 변경하시겠습니까?',
USER_GM_CHANGE_COMPLETE: '권한변경을 완료하였습니다.',
USER_KICK_CONFIRM: '해당 유저를 Kick 하시겠습니까?',
USER_KICK_COMPLETE: '서버에 정상적으로 Kick을 요청하였습니다.',
DEL_COUNT_CHECK: '보유 개수 이상 삭제할 수 없습니다.', DEL_COUNT_CHECK: '보유 개수 이상 삭제할 수 없습니다.',
DEL_COUNT_CONFIRM: '삭제할 아이템 개수를 입력하세요.\n(보유 개수: {{count}})', DEL_COUNT_CONFIRM: '삭제할 아이템 개수를 입력하세요.\n(보유 개수: {{count}})',
CANCEL_CONFIRM: '취소하시겠습니까?\n취소 시 변경된 값은 초기화됩니다.', CANCEL_CONFIRM: '취소하시겠습니까?\n취소 시 변경된 값은 초기화됩니다.',