유저 조회 접속 상태 표시

유저 조회 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) => {
try {

View File

@@ -247,6 +247,26 @@ export const opSuccessType = [
{ 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 = [
// { value: "None", name: "ALL" },
// { 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 styled from 'styled-components';
import Profile from '../../assets/img/datamanage/img-profile.png';
import NicknameChangeModal from '../../components/DataManage/NicknameChangeModal';
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 { adminLevelType, authType, modalTypes } from '../../assets/data';
import DynamicModal from '../common/modal/DynamicModal';
@@ -16,53 +16,89 @@ import { convertKTC } from '../../utils';
import { EditButton, ProfileWrapper, UserDefault, UserInfoTable } from '../../styles/ModuleComponents';
import { TableSkeleton } from '../Skeleton/TableSkeleton';
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 { t } = useTranslation();
const authInfo = useRecoilValue(authList);
const [pwPop, setPwPop] = useState('hidden');
const [gmModal, setGmModal] = useState('hidden');
const token = sessionStorage.getItem('token');
const {
modalState,
handleModalView,
handleModalClose
} = useModal({
userKick: 'hidden',
gmLevelChange: 'hidden',
pwChange: 'hidden'
});
const [alertMsg, setAlertMsg] = useState('');
const [dataList, setDataList] = useState({});
const [adminLevel, setAdminLevel] = useState('0');
const [loading, setLoading] = useState(true);
const handleClick = () => {
if (pwPop === 'hidden') setPwPop('view');
else setPwPop('hidden');
};
useEffect(() => {
fetchData();
}, [userInfo]);
const fetchData = async () => {
const token = sessionStorage.getItem('token');
setLoading(true);
await UserInfoView(token, userInfo.guid).then(data => {
setDataList(data);
setLoading(false);
});
};
const handleGMChange = (e) =>{
setAdminLevel(e.target.value);
setGmModal('view');
}
const handleSubmit = async () => {
const token = sessionStorage.getItem('token');
const handleSubmit = async (type, param = null) => {
let params = {};
params.guid = userInfo.guid;
params.admin_level = adminLevel;
await UserChangeAdminLevel(token, params);
switch (type) {
case "gmLevelChangeSubmit":
setAdminLevel(param);
handleCancel();
await fetchData();
}
handleModalView('gmLevelChange');
break;
case "userKickSubmit":
handleModalView('userKick');
break;
case "gmLevelChange":
setLoading(true);
const handleCancel = () => {
setGmModal('hidden');
params.guid = userInfo.guid;
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 (
@@ -76,11 +112,11 @@ const UserDefaultInfo = ({ userInfo }) => {
<UserInfoTable $maxwidth="530px">
<tbody>
<tr>
<th>AID(GUID)</th>
<th>GUID</th>
<td>{dataList.user_info && dataList.user_info.aid}</td>
</tr>
<tr>
<th>계정 ID</th>
<th>Account ID</th>
<td>{dataList.user_info && dataList.user_info.user_id}</td>
</tr>
<tr>
@@ -88,12 +124,11 @@ const UserDefaultInfo = ({ userInfo }) => {
<td>{dataList.user_info && dataList.user_info.nation}</td>
</tr>
<tr>
<th>멤버십</th>
<td>{dataList.user_info && dataList.user_info.membership}</td>
</tr>
<tr>
<th>친구 추천코드</th>
<td>{dataList.user_info && dataList.user_info.friend_code}</td>
<th>접속상태</th>
<StatusCell>{dataList.user_session !== undefined && opUserSessionType.find(session => session.value === dataList.user_session)?.name}
{<Button theme={dataList.user_session ? "line" : "disable"} id={"user_session"} name="kick" text="KICK" handleClick={e => handleSubmit('userKickSubmit')} disabled={!dataList.user_session}/>}
{/*{<Button theme={dataList.user_session ? "line" : "disable"} id={"user_session"} name="kick" text="KICK" handleClick={e => handleSubmit('userKickSubmit')} />}*/}
</StatusCell>
</tr>
<tr>
<th>계정 생성일</th>
@@ -103,10 +138,7 @@ const UserDefaultInfo = ({ userInfo }) => {
</tr>
<tr>
<th>최근 접속일자</th>
<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>
<td>{dataList.user_info && convertKTC(dataList.user_info.access_dt)}</td>
</tr>
<tr>
<th>최근 종료일자</th>
@@ -121,7 +153,9 @@ const UserDefaultInfo = ({ userInfo }) => {
<tr>
<th>GM권한</th>
<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) => (
<option key={index} value={data.value}>
{data.name}
@@ -149,7 +183,7 @@ const UserDefaultInfo = ({ userInfo }) => {
hidden={true}
onClick={e => {
e.preventDefault();
handleClick();
handleModalClose('pwChange');
}}></EditButton>
</td>
</tr>
@@ -172,15 +206,36 @@ const UserDefaultInfo = ({ userInfo }) => {
</tbody>
</UserInfoTable>
</div>
<NicknameChangeModal pwPop={pwPop} handleClick={handleClick} dataList={dataList} />
<NicknameChangeModal pwPop={modalState.pwChangeModal} handleClick={() => handleModalClose('pwChange')} dataList={dataList} />
<DynamicModal
modalType={modalTypes.childOkCancel}
view={gmModal}
modalType={modalTypes.confirmOkCancel}
view={modalState.gmLevelChangeModal}
modalText={t('USER_GM_CHANGE')}
handleCancel={handleCancel}
handleSubmit={handleSubmit}
handleSubmit={() => handleSubmit('gmLevelChange')}
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" />
</ProfileWrapper>
<UserInfoTable>
<Skeleton width="530px" height="30px" />
<Skeleton width="530px" height="30px" />
<Skeleton width="530px" height="30px" />
<Skeleton width="530px" height="30px" />
<Skeleton width="530px" height="30px" />
<Skeleton width="530px" height="30px" />
<Skeleton width="530px" height="30px" />
<Skeleton width="530px" height="30px" />
<Skeleton width="530px" height="30px" />
<Skeleton width="530px" height="30px" />
<tbody>
{Array.from({ length: 10 }).map((_, index) => (
<tr key={index}>
<td>
<Skeleton width="530px" height="30px" />
</td>
</tr>
))}
</tbody>
</UserInfoTable>
</UserDefault>
<UserInfoTable>
<Skeleton width="750px" height="30px" />
<Skeleton width="750px" height="30px" />
<Skeleton width="750px" height="30px" />
<Skeleton width="750px" height="30px" />
<tbody>
{Array.from({ length: 4 }).map((_, index) => (
<tr key={index}>
<td>
<Skeleton width="750px" height="30px" />
</td>
</tr>
))}
</tbody>
</UserInfoTable>
</>
)
}
const UserDefault = styled.div`
display: flex;
gap: 40px;
margin-bottom: 30px;
display: flex;
gap: 40px;
margin-bottom: 30px;
justify-content: space-between;
`;
const ProfileWrapper = styled.div`
width: 150px;
height: 150px;
width: 150px;
height: 150px;
border-radius: 75px;
overflow: hidden;
display: inline-flex;

View File

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