랜드 소유권 변경 예약 처리 및 취소 처리

This commit is contained in:
2025-03-13 14:45:31 +09:00
parent 3efd663f0d
commit 5d9b6871fb
7 changed files with 160 additions and 81 deletions

View File

@@ -130,6 +130,22 @@ export const LandAuctionDelete = async (token, params, id) => {
} }
}; };
// 랜드 소유권 변경 예약 삭제
export const LandOwnerChangesDelete = async (token, params) => {
try {
const res = await Axios.delete(`/api/v1/land/change/delete`, {
headers: { Authorization: `Bearer ${token}` },
data: params,
});
return res.data;
} catch (e) {
if (e instanceof Error) {
throw new Error('LandAuctionDelete Error', e);
}
}
};
export const LandView = async (token) => { export const LandView = async (token) => {
try { try {
const res = await Axios.get( const res = await Axios.get(

View File

@@ -213,7 +213,18 @@ export const opLandOwnedType = [
]; ];
export const opLandCategoryType = [ export const opLandCategoryType = [
{ value: 'ALL', name: '전체' },
{ value: 'public', name: '공공 임대형' }, { value: 'public', name: '공공 임대형' },
{ value: 'auction', name: '경매' }, { value: 'auction', name: '경매' },
{ value: 'event', name: '이벤트 지급' }, { value: 'event', name: '이벤트 지급' },
];
export const opLandInfoStatusType = [
{ value: 'ALL', name: '전체' },
{ value: 'NONE', name: '' },
{ value: 'OWNED', name: '지급 완료' },
{ value: 'AUCTION_END', name: '경매 완료' },
{ value: 'RESV_START', name: '경매 예정' },
{ value: 'WAIT', name: '경매 대기' },
{ value: 'AUCTION_START', name: '경매 진행' },
]; ];

View File

@@ -28,7 +28,7 @@ import { BattleEventModify, BattleEventSingleRegist } from '../../../apis/Battle
import { battleEventStatusType } from '../../../assets/data/types'; import { battleEventStatusType } from '../../../assets/data/types';
import { isValidDayRange } from '../../../utils/date'; import { isValidDayRange } from '../../../utils/date';
import CheckBox from '../../common/input/CheckBox'; import CheckBox from '../../common/input/CheckBox';
import { LandOwnedChangesRegist, UserInfoView } from '../../../apis'; import { LandOwnedChangesRegist, LandOwnerChangesDelete, UserInfoView } from '../../../apis';
const OwnerChangeModal = ({ modalType, detailView, handleDetailView, content, setDetailData }) => { const OwnerChangeModal = ({ modalType, detailView, handleDetailView, content, setDetailData }) => {
const { t } = useTranslation(); const { t } = useTranslation();
@@ -52,12 +52,24 @@ const OwnerChangeModal = ({ modalType, detailView, handleDetailView, content, se
useEffect(() => { useEffect(() => {
if(content && Object.keys(content).length > 0){ if(content && Object.keys(content).length > 0){
const ownerChanges = content.owner_changes;
let changes_info;
if(ownerChanges && ownerChanges.length > 0){
changes_info = ownerChanges.filter(item => item.status === 'WAIT').reduce((maxItem, current) => {
return (!maxItem || current.id > maxItem.id ) ? current : maxItem;
}, null);
}
setResultData({ setResultData({
...resultData, ...resultData,
land_id: content.land_id, land_id: content.land_id,
land_name: content.land_name, land_name: content.land_name,
building_id: content.building_id, building_id: content.building_id,
building_name: content.building_name, building_name: content.building_name,
is_reserve: changes_info?.is_reserve || false,
reservation_dt: (changes_info && convertKTCDate(changes_info.reservation_dt)) || new Date(),
user_guid: changes_info?.user_guid || '',
user_name: changes_info?.user_name || '',
id: changes_info?.id || null
}); });
} }
}, [modalType, content]); }, [modalType, content]);
@@ -114,8 +126,6 @@ const OwnerChangeModal = ({ modalType, detailView, handleDetailView, content, se
case "submit": case "submit":
if (!checkCondition()) return; if (!checkCondition()) return;
handleModalView('registConfirm'); handleModalView('registConfirm');
break; break;
case "cancel": case "cancel":
@@ -126,6 +136,7 @@ const OwnerChangeModal = ({ modalType, detailView, handleDetailView, content, se
handleReset(); handleReset();
break; break;
case "user": case "user":
if(isView()) return;
const guid = resultData.user_guid; const guid = resultData.user_guid;
if(!guid || guid.length !== 32){ if(!guid || guid.length !== 32){
setAlertMsg(t('WARNING_GUID_CHECK')) setAlertMsg(t('WARNING_GUID_CHECK'))
@@ -148,24 +159,52 @@ const OwnerChangeModal = ({ modalType, detailView, handleDetailView, content, se
case "registConfirm": case "registConfirm":
setLoading(true); setLoading(true);
await LandOwnedChangesRegist(token, resultData).then(data => { if(isView()){
console.log(resultData);
setLoading(false); setLoading(false);
handleModalClose('registConfirm'); handleModalClose('registConfirm');
if(data.result === "SUCCESS") {
handleModalView('registComplete'); const resvDt = resultData.reservation_dt;
}else if(data.result === "GUID_CHECK"){ const now = new Date();
setAlertMsg(t('WARNING_GUID_CHECK')); if(resvDt < now){
}else{ setAlertMsg(t('LAND_OWNED_CHANGES_DELETE_TIME_WARNING'));
setAlertMsg(t('REGIST_FAIL')); handleReset();
return;
} }
}).catch(reason => {
setAlertMsg(t('API_FAIL')); await LandOwnerChangesDelete(token, resultData).then(data => {
}); setLoading(false);
handleModalClose('registConfirm');
if(data.result === "SUCCESS") {
handleModalView('registComplete');
}else if(data.result === "ERROR_LAND_OWNER_CHANGES_RESERVATION"){
setAlertMsg(t('LAND_OWNED_CHANGES_DELETE_STATUS_WARNING'));
}else{
setAlertMsg(t('DELETE_FAIL'));
}
}).catch(reason => {
setAlertMsg(t('API_FAIL'));
});
}else{
await LandOwnedChangesRegist(token, resultData).then(data => {
setLoading(false);
handleModalClose('registConfirm');
if(data.result === "SUCCESS") {
handleModalView('registComplete');
}else if(data.result === "GUID_CHECK"){
setAlertMsg(t('WARNING_GUID_CHECK'));
}else{
setAlertMsg(t('REGIST_FAIL'));
}
}).catch(reason => {
setAlertMsg(t('API_FAIL'));
});
}
break; break;
case "registComplete": case "registComplete":
handleModalClose('registComplete'); handleModalClose('registComplete');
handleReset(); handleReset();
window.location.reload();
break; break;
case "warning": case "warning":
setAlertMsg(''); setAlertMsg('');
@@ -186,21 +225,14 @@ const OwnerChangeModal = ({ modalType, detailView, handleDetailView, content, se
const isView = (label) => { const isView = (label) => {
switch (label) { switch (label) {
case "modify": case "modify":
return modalType === TYPE_MODIFY && (content?.status === battleEventStatusType.stop); return modalType === TYPE_MODIFY;
case "start_dt":
case "repeat":
case "registry": case "registry":
return modalType === TYPE_REGISTRY return modalType === TYPE_REGISTRY;
case "end_dt": case "reservation_dt":
case "group": case "user":
case "name": return modalType === TYPE_REGISTRY || (modalType === TYPE_MODIFY &&resultData?.id === null);
case "config":
case "reward":
case "round":
case "hot":
return modalType === TYPE_REGISTRY || (modalType === TYPE_MODIFY &&(content?.status === battleEventStatusType.stop));
default: default:
return modalType === TYPE_MODIFY && (content?.status !== battleEventStatusType.stop); return modalType === TYPE_MODIFY && resultData?.id !== null;
} }
} }
@@ -210,14 +242,15 @@ const OwnerChangeModal = ({ modalType, detailView, handleDetailView, content, se
<Title $align="center">소유권 변경</Title> <Title $align="center">소유권 변경</Title>
<MessageWrapper> <MessageWrapper>
<FormRowGroup> <FormRowGroup>
{/*<FormItemGroup>*/} <FormItemGroup>
{/* <CheckBox*/} <CheckBox
{/* label="예약"*/} label="예약"
{/* id="reserve"*/} id="reserve"
{/* checked={resultData.is_reserve}*/} checked={resultData.is_reserve}
{/* setData={e => setResultData({ ...resultData, is_reserve: e.target.checked, reservation_dt: new Date() })}*/} setData={e => setResultData({ ...resultData, is_reserve: e.target.checked, reservation_dt: new Date() })}
{/* />*/} disabled={!isView('user')}
{/*</FormItemGroup>*/} />
</FormItemGroup>
{resultData.is_reserve && ( {resultData.is_reserve && (
<> <>
<SingleDatePicker <SingleDatePicker
@@ -244,12 +277,16 @@ const OwnerChangeModal = ({ modalType, detailView, handleDetailView, content, se
width='150px' width='150px'
value={resultData?.land_id} value={resultData?.land_id}
readOnly={true} readOnly={true}
handleClick={() => handleSubmit('user')}
disabled={!isView('user')}
/> />
<FormInput <FormInput
type="text" type="text"
width='250px' width='250px'
value={resultData?.land_name} value={resultData?.land_name}
readOnly={true} readOnly={true}
handleClick={() => handleSubmit('user')}
disabled={!isView('user')}
/> />
</FormRowGroup> </FormRowGroup>
<FormRowGroup> <FormRowGroup>
@@ -260,10 +297,11 @@ const OwnerChangeModal = ({ modalType, detailView, handleDetailView, content, se
width='300px' width='300px'
value={resultData?.user_guid} value={resultData?.user_guid}
onChange={e => setResultData({ ...resultData, user_guid: e.target.value })} onChange={e => setResultData({ ...resultData, user_guid: e.target.value })}
disabled={!isView('user')}
/> />
<Button <Button
text="확인" text="확인"
theme="primary" theme={!isView() ? 'primary' : 'disable'}
type="submit" type="submit"
size="large" size="large"
width="100px" width="100px"
@@ -277,6 +315,7 @@ const OwnerChangeModal = ({ modalType, detailView, handleDetailView, content, se
width='300px' width='300px'
value={resultData?.user_name} value={resultData?.user_name}
readOnly={true} readOnly={true}
disabled={!isView('user')}
/> />
</FormRowGroup> </FormRowGroup>
{!isView() && isNullValue && <SearchBarAlert $marginTop="25px" $align="right">{t('REQUIRED_VALUE_CHECK')}</SearchBarAlert>} {!isView() && isNullValue && <SearchBarAlert $marginTop="25px" $align="right">{t('REQUIRED_VALUE_CHECK')}</SearchBarAlert>}
@@ -288,8 +327,8 @@ const OwnerChangeModal = ({ modalType, detailView, handleDetailView, content, se
<Button text="취소" theme="line" handleClick={() => handleSubmit('cancel')} /> <Button text="취소" theme="line" handleClick={() => handleSubmit('cancel')} />
<Button <Button
type="submit" type="submit"
text={isView('modify') ? "수정" : "등록"} text={isView() ? "예약 삭제": "등록"}
name="등록버튼" name={isView() ? "삭제버튼": "등록버튼"}
theme={ theme={
checkCondition() checkCondition()
? 'primary' ? 'primary'
@@ -306,7 +345,7 @@ const OwnerChangeModal = ({ modalType, detailView, handleDetailView, content, se
<DynamicModal <DynamicModal
modalType={modalTypes.confirmOkCancel} modalType={modalTypes.confirmOkCancel}
view={modalState.registConfirmModal} view={modalState.registConfirmModal}
modalText={isView('modify') ? t('BATTLE_EVENT_UPDATE_CONFIRM') : t('BATTLE_EVENT_REGIST_CONFIRM')} modalText={isView() ? t('LAND_OWNED_CHANGES_SELECT_DELETE') : t('LAND_OWNED_CHANGES_REGIST_CONFIRM')}
handleSubmit={() => handleSubmit('registConfirm')} handleSubmit={() => handleSubmit('registConfirm')}
handleCancel={() => handleModalClose('registConfirm')} handleCancel={() => handleModalClose('registConfirm')}
/> />
@@ -314,7 +353,7 @@ const OwnerChangeModal = ({ modalType, detailView, handleDetailView, content, se
<DynamicModal <DynamicModal
modalType={modalTypes.completed} modalType={modalTypes.completed}
view={modalState.registCompleteModal} view={modalState.registCompleteModal}
modalText={isView('modify') ? t('UPDATE_COMPLETED') : t('REGIST_COMPLTE')} modalText={isView() ? t('CANCEL_COMPLETED') : t('REGIST_COMPLTE')}
handleSubmit={() => handleSubmit('registComplete')} handleSubmit={() => handleSubmit('registComplete')}
/> />
{/* 취소 모달 */} {/* 취소 모달 */}

View File

@@ -3,7 +3,8 @@ import Button from '../../common/button/Button';
import { SearchBarLayout, SearchPeriod } from '../../common/SearchBar'; import { SearchBarLayout, SearchPeriod } from '../../common/SearchBar';
import { useCallback, useEffect, useState } from 'react'; import { useCallback, useEffect, useState } from 'react';
import { LandAuctionView, LandInfoData } from '../../../apis'; import { LandAuctionView, LandInfoData } from '../../../apis';
import { landAuctionStatus, landSearchType, landSize } from '../../../assets/data'; import { landAuctionStatus, landSearchType, landSize, opLandCategoryType } from '../../../assets/data';
import { opLandInfoStatusType } from '../../../assets/data/options';
export const useLandInfoSearch = (token, initialPageSize) => { export const useLandInfoSearch = (token, initialPageSize) => {
const [searchParams, setSearchParams] = useState({ const [searchParams, setSearchParams] = useState({
@@ -157,7 +158,7 @@ const LandInfoSearchBar = ({ searchParams, onSearch, onReset }) => {
<> <>
<InputLabel>랜드상태</InputLabel> <InputLabel>랜드상태</InputLabel>
<SelectInput value={searchParams.status} onChange={e => onSearch({ status: e.target.value }, false)} > <SelectInput value={searchParams.status} onChange={e => onSearch({ status: e.target.value }, false)} >
{landAuctionStatus.map((data, index) => ( {opLandInfoStatusType.map((data, index) => (
<option key={index} value={data.value}> <option key={index} value={data.value}>
{data.name} {data.name}
</option> </option>
@@ -170,7 +171,7 @@ const LandInfoSearchBar = ({ searchParams, onSearch, onReset }) => {
<> <>
<InputLabel>카테고리</InputLabel> <InputLabel>카테고리</InputLabel>
<SelectInput value={searchParams.category} onChange={e => onSearch({ category: e.target.value }, false)}> <SelectInput value={searchParams.category} onChange={e => onSearch({ category: e.target.value }, false)}>
{landAuctionStatus.map((data, index) => ( {opLandCategoryType.map((data, index) => (
<option key={index} value={data.value}> <option key={index} value={data.value}>
{data.name} {data.name}
</option> </option>

View File

@@ -39,6 +39,10 @@ const resources = {
WARNING_TYPE_CHECK: '타입을 확인해주세요.', WARNING_TYPE_CHECK: '타입을 확인해주세요.',
//랜드 //랜드
LAND_OWNED_CHANGES_WARNING: "해당 랜드는 소유권 변경이 불가능합니다.", LAND_OWNED_CHANGES_WARNING: "해당 랜드는 소유권 변경이 불가능합니다.",
LAND_OWNED_CHANGES_REGIST_CONFIRM: "랜드 소유권 변경을 등록하시겠습니까?",
LAND_OWNED_CHANGES_SELECT_DELETE: "랜드 소유권 변경 예약을 취소하시겠습니까?",
LAND_OWNED_CHANGES_DELETE_TIME_WARNING: "예약시간이 지나 취소할 수 없습니다.",
LAND_OWNED_CHANGES_DELETE_STATUS_WARNING: "소유권 변경 예약을 취소할 수 없는 상태입니다.",
LAND_AUCTION_SELECT_DELETE: "선택된 경매를 삭제하시겠습니까?", LAND_AUCTION_SELECT_DELETE: "선택된 경매를 삭제하시겠습니까?",
LAND_AUCTION_WARNING_DELETE: "대기 상태의 경매만 삭제할 수 있습니다.", LAND_AUCTION_WARNING_DELETE: "대기 상태의 경매만 삭제할 수 있습니다.",
LAND_AUCTION_MODAL_STATUS_WARNING: "경매 시작일시 이후에는 변경이 불가합니다.", LAND_AUCTION_MODAL_STATUS_WARNING: "경매 시작일시 이후에는 변경이 불가합니다.",

View File

@@ -2,7 +2,15 @@ import { styled } from 'styled-components';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import React, { Fragment, useRef, useState } from 'react'; import React, { Fragment, useRef, useState } from 'react';
import { Title, TableStyle, BtnWrapper, ButtonClose, ModalText, FormWrapper } from '../../styles/Components'; import {
Title,
TableStyle,
BtnWrapper,
ButtonClose,
ModalText,
FormWrapper,
TableWrapper,
} from '../../styles/Components';
import Button from '../../components/common/button/Button'; import Button from '../../components/common/button/Button';
@@ -19,7 +27,7 @@ import {
opLandOwnedType, opLandOwnedType,
} from '../../assets/data'; } from '../../assets/data';
import { useLandAuctionSearch } from '../../components/ServiceManage/searchBar/LandAuctionSearchBar'; import { useLandAuctionSearch } from '../../components/ServiceManage/searchBar/LandAuctionSearchBar';
import { INITIAL_PAGE_LIMIT, INITIAL_PAGE_SIZE } from '../../assets/data/adminConstants'; import { INITIAL_PAGE_LIMIT, INITIAL_PAGE_SIZE, TYPE_MODIFY } from '../../assets/data/adminConstants';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { CheckBox, DynamicModal, ExcelDownButton, Pagination, ViewTableInfo } from '../../components/common'; import { CheckBox, DynamicModal, ExcelDownButton, Pagination, ViewTableInfo } from '../../components/common';
import { LandAuctionSearchBar } from '../../components/ServiceManage'; import { LandAuctionSearchBar } from '../../components/ServiceManage';
@@ -29,6 +37,7 @@ import Loading from '../../components/common/Loading';
import { TableSkeleton } from '../../components/Skeleton/TableSkeleton'; import { TableSkeleton } from '../../components/Skeleton/TableSkeleton';
import OwnerChangeModal from '../../components/ServiceManage/modal/OwnerChangeModal'; import OwnerChangeModal from '../../components/ServiceManage/modal/OwnerChangeModal';
import { StatusLabel, StatusWapper } from '../../styles/ModuleComponents'; import { StatusLabel, StatusWapper } from '../../styles/ModuleComponents';
import { opLandInfoStatusType } from '../../assets/data/options';
const LandInfoView = () => { const LandInfoView = () => {
const token = sessionStorage.getItem('token'); const token = sessionStorage.getItem('token');
@@ -82,6 +91,10 @@ const LandInfoView = () => {
setAlertMsg(t('LAND_OWNED_CHANGES_WARNING')) setAlertMsg(t('LAND_OWNED_CHANGES_WARNING'))
return; return;
} }
const owner_changes = selectRow.owner_changes;
if(owner_changes.length > 0){
setModalType(TYPE_MODIFY);
}
setDetailData(selectRow); setDetailData(selectRow);
handleModalView('detail'); handleModalView('detail');
break; break;
@@ -147,7 +160,6 @@ const LandInfoView = () => {
break; break;
} }
} }
console.log(dataList?.land_info_list)
return ( return (
<> <>
@@ -205,12 +217,7 @@ const LandInfoView = () => {
</td> </td>
<td>{data.land_id}</td> <td>{data.land_id}</td>
<td>{data.land_name}</td> <td>{data.land_name}</td>
<td>{data.status}</td> <td>{opLandInfoStatusType.find(option => option.value === data.status)?.name}</td>
{/*<StatusWapper>*/}
{/* <StatusLabel $status={data.status}>*/}
{/* {landAuctionStatus.find(option => option.value === data.status)?.name}*/}
{/* </StatusLabel>*/}
{/*</StatusWapper>*/}
<td>{opLandCategoryType.find(option => option.value === data.category)?.name}</td> <td>{opLandCategoryType.find(option => option.value === data.category)?.name}</td>
<td>{landSize.find(option => option.value === data.land_size)?.name}</td> <td>{landSize.find(option => option.value === data.land_size)?.name}</td>
<td>{data.socket}</td> <td>{data.socket}</td>
@@ -229,7 +236,7 @@ const LandInfoView = () => {
<Pagination postsPerPage={searchParams.pageSize} totalPosts={dataList?.total_all} setCurrentPage={handlePageChange} currentPage={searchParams.currentPage} pageLimit={INITIAL_PAGE_LIMIT} /> <Pagination postsPerPage={searchParams.pageSize} totalPosts={dataList?.total_all} setCurrentPage={handlePageChange} currentPage={searchParams.currentPage} pageLimit={INITIAL_PAGE_LIMIT} />
<OwnerChangeModal modalType={modalType} detailView={modalState.detailModal} handleDetailView={() => handleModalClose('detail')} content={detailData} setDetailData={setDetailData} /> <OwnerChangeModal modalType={modalType} detailView={modalState.detailModal} handleDetailView={() => {handleModalClose('detail');handleSearch()}} content={detailData} setDetailData={setDetailData} />
<DynamicModal <DynamicModal
modalType={modalTypes.completed} modalType={modalTypes.completed}
@@ -243,33 +250,34 @@ const LandInfoView = () => {
export default withAuth(authType.landRead)(LandInfoView); export default withAuth(authType.landRead)(LandInfoView);
const TableWrapper = styled.div` // const TableWrapper = styled.div`
overflow: auto; // overflow: auto;
border-top: 1px solid #000; // border-top: 1px solid #000;
&::-webkit-scrollbar { // z-index: 1;
height: 4px; // &::-webkit-scrollbar {
} // height: 4px;
&::-webkit-scrollbar-thumb { // }
background: #666666; // &::-webkit-scrollbar-thumb {
} // background: #666666;
&::-webkit-scrollbar-track { // }
background: #d9d9d9; // &::-webkit-scrollbar-track {
} // background: #d9d9d9;
thead { // }
th { // thead {
position: sticky; // th {
top: 0; // position: sticky;
z-index: 10; // top: 0;
} // z-index: 10;
} // }
${TableStyle} { // }
min-width: 1000px; // ${TableStyle} {
th { // min-width: 1000px;
position: sticky; // th {
top: 0; // position: sticky;
} // top: 0;
} // }
`; // }
// `;
const LandLink = styled(Link)` const LandLink = styled(Link)`
color: #61a2d0; color: #61a2d0;

View File

@@ -91,8 +91,8 @@ export const useTable = (tableData = [], options = {mode: 'multi'}) => {
tableDataRef.current[index]?.id !== item.id tableDataRef.current[index]?.id !== item.id
); );
setSelectedRows([]);
if (hasDataChanged) { if (hasDataChanged) {
setSelectedRows([]);
tableDataRef.current = tableData; tableDataRef.current = tableData;
} }
}, [tableData]); }, [tableData]);