제작 아이템 조회
랭킹 점수 관리
This commit is contained in:
@@ -13,7 +13,15 @@ import {
|
||||
LogView,
|
||||
} from './pages/UserManage';
|
||||
import { EconomicIndex, UserIndex } from './pages/IndexManage';
|
||||
import { LandInfoView, GameLogView, UserView, BusinessLogView, MetaItemView, RankManage } from './pages/DataManage';
|
||||
import {
|
||||
LandInfoView,
|
||||
GameLogView,
|
||||
UserView,
|
||||
BusinessLogView,
|
||||
MetaItemView,
|
||||
RankManage,
|
||||
MetaCraftingView,
|
||||
} from './pages/DataManage';
|
||||
import {
|
||||
Board,
|
||||
RewardEvent,
|
||||
@@ -62,6 +70,7 @@ const RouteInfo = () => {
|
||||
<Route path="gamelogview" element={<GameLogView />} />
|
||||
<Route path="businesslogview" element={<BusinessLogView />} />
|
||||
<Route path="itemdictionary" element={<MetaItemView />} />
|
||||
<Route path="craftdictionary" element={<MetaCraftingView />} />
|
||||
<Route path="rankmanage" element={<RankManage />} />
|
||||
</Route>
|
||||
<Route path="/servicemanage">
|
||||
|
||||
@@ -40,6 +40,43 @@ export const ItemDictionaryExport = async (token, params) => {
|
||||
}
|
||||
};
|
||||
|
||||
export const getCraftingDictionaryList = async (token, searchType, searchData, smallType, recipeType, order, size, currentPage) => {
|
||||
try {
|
||||
const response = await Axios.get(`/api/v1/dictionary/craft/list?search_type=${searchType}&search_data=${searchData}
|
||||
&small_type=${smallType}&recipe_type=${recipeType}
|
||||
&orderby=${order}&page_no=${currentPage}&page_size=${size}`, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
console.error('getCraftingDictionaryList API error:', error);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
export const CraftingDictionaryExport = async (token, params) => {
|
||||
try {
|
||||
await Axios.get(`/api/v1/dictionary/craft/excel-export?search_type=${params.search_type}&search_data=${params.search_data}
|
||||
&small_type=${params.small_type}&recipe_type=${params.recipe_type}
|
||||
&lang=${params.lang}&task_id=${params.taskId}`, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
responseType: 'blob'
|
||||
}).then(response => {
|
||||
responseFileDownload(response, {
|
||||
defaultFileName: 'craftingDictionary'
|
||||
});
|
||||
});
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('CraftingDictionaryExport Error', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const BrandView = async (token) => {
|
||||
try {
|
||||
const res = await Axios.get(
|
||||
|
||||
@@ -3,6 +3,7 @@ import menuBannerAPI from './menuBannerAPI.json';
|
||||
import historyAPI from './historyAPI.json';
|
||||
import eventAPI from './eventAPI.json';
|
||||
import rankingAPI from './rankingAPI.json';
|
||||
import metaCraftingAPI from './metaCraftingAPI.json';
|
||||
|
||||
export {
|
||||
itemAPI,
|
||||
@@ -10,4 +11,5 @@ export {
|
||||
historyAPI,
|
||||
eventAPI,
|
||||
rankingAPI,
|
||||
metaCraftingAPI
|
||||
};
|
||||
11
src/assets/data/apis/metaCraftingAPI.json
Normal file
11
src/assets/data/apis/metaCraftingAPI.json
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"baseUrl": "/api/v1/dictionary/craft",
|
||||
"endpoints": {
|
||||
"getCraftingDictionaryList": {
|
||||
"method": "GET",
|
||||
"url": "/list",
|
||||
"dataPath": "data",
|
||||
"paramFormat": "query"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -119,6 +119,14 @@ export const menuConfig = {
|
||||
view: true,
|
||||
authLevel: adminAuthLevel.NONE
|
||||
},
|
||||
craftdictionary: {
|
||||
title: '제작 아이템 조회',
|
||||
permissions: {
|
||||
read: authType.craftingDictionaryRead
|
||||
},
|
||||
view: true,
|
||||
authLevel: adminAuthLevel.NONE
|
||||
},
|
||||
rankmanage: {
|
||||
title: '랭킹 점수 관리',
|
||||
permissions: {
|
||||
|
||||
@@ -406,6 +406,19 @@ export const opItemRestore = [
|
||||
{ value: 'IMPOSSIBLE', name: '불가능' },
|
||||
];
|
||||
|
||||
export const opPropSmallType = [
|
||||
{ value: 'ALL', name: '전체' },
|
||||
{ value: 'FURNITURE', name: 'FURNITURE' },
|
||||
{ value: 'COOKING', name: 'COOKING' },
|
||||
{ value: 'CLOTHES', name: 'CLOTHES' },
|
||||
];
|
||||
|
||||
export const opPropRecipeType = [
|
||||
{ value: 'ALL', name: '전체' },
|
||||
{ value: 'Basic', name: '기본' },
|
||||
{ value: 'Add', name: '등록 필요' },
|
||||
];
|
||||
|
||||
export const opEquipType = [
|
||||
{ value: 0, name: '미장착' },
|
||||
{ value: 1, name: '의상장착' },
|
||||
|
||||
49
src/assets/data/pages/metaCraftingSearch.json
Normal file
49
src/assets/data/pages/metaCraftingSearch.json
Normal file
@@ -0,0 +1,49 @@
|
||||
{
|
||||
"initialSearchParams": {
|
||||
"search_type": "ID",
|
||||
"search_data": "",
|
||||
"small_type": "ALL",
|
||||
"recipe_type": "ALL",
|
||||
"orderBy": "DESC",
|
||||
"pageSize": 50,
|
||||
"currentPage": 1
|
||||
},
|
||||
|
||||
"searchFields": [
|
||||
{
|
||||
"type": "select",
|
||||
"id": "search_type",
|
||||
"optionsRef": "itemSearchType",
|
||||
"col": 1
|
||||
},
|
||||
{
|
||||
"type": "text",
|
||||
"id": "search_data",
|
||||
"placeholder": "아이템 입력",
|
||||
"width": "300px",
|
||||
"col": 1
|
||||
},
|
||||
{
|
||||
"type": "select",
|
||||
"id": "small_type",
|
||||
"label": "제작 분류",
|
||||
"optionsRef": "opPropSmallType",
|
||||
"col": 1
|
||||
},
|
||||
{
|
||||
"type": "select",
|
||||
"id": "recipe_type",
|
||||
"label": "레시피 필요 여부",
|
||||
"optionsRef": "opPropRecipeType",
|
||||
"col": 1
|
||||
}
|
||||
],
|
||||
|
||||
"apiInfo": {
|
||||
"endpointName": "getCraftingDictionaryList",
|
||||
"loadOnMount": true,
|
||||
"pageField": "page_no",
|
||||
"pageSizeField": "page_size",
|
||||
"orderField": "orderBy"
|
||||
}
|
||||
}
|
||||
@@ -61,6 +61,7 @@ export const authType = {
|
||||
worldEventRead: 59,
|
||||
worldEventUpdate: 60,
|
||||
worldEventDelete: 61,
|
||||
craftingDictionaryRead: 62,
|
||||
|
||||
|
||||
levelReader: 999,
|
||||
|
||||
@@ -193,6 +193,7 @@ const resources = {
|
||||
FILE_GAME_LOG_CURRENCY_ITEM: 'Caliverse_Game_Log_Currecy_Item',
|
||||
FILE_CURRENCY_INDEX: 'Caliverse_Currency_Index',
|
||||
FILE_DICTIONARY_ITEM: 'Caliverse_Dictionary_Item',
|
||||
FILE_DICTIONARY_CRAFTING: 'Caliverse_Dictionary_Crafting',
|
||||
//서버 에러메시지
|
||||
DYNAMODB_NOT_USER: '유저 정보를 확인해주세요.',
|
||||
NOT_USER: '유저 정보를 확인해주세요.',
|
||||
|
||||
208
src/pages/DataManage/MetaCraftingView.js
Normal file
208
src/pages/DataManage/MetaCraftingView.js
Normal file
@@ -0,0 +1,208 @@
|
||||
import React, { Fragment, useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
|
||||
import { AnimatedPageWrapper } from '../../components/common/Layout';
|
||||
import {
|
||||
Title,
|
||||
TableStyle,
|
||||
FormWrapper,
|
||||
TableWrapper,
|
||||
DownloadContainer, CircularProgressWrapper,
|
||||
} from '../../styles/Components';
|
||||
|
||||
import { withAuth } from '../../hooks/hook';
|
||||
import {
|
||||
authType,
|
||||
} from '../../assets/data';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import {
|
||||
AnimatedTabs,
|
||||
ViewTableInfo,
|
||||
} from '../../components/common';
|
||||
import { TableSkeleton } from '../../components/Skeleton/TableSkeleton';
|
||||
import CircularProgress from '../../components/common/CircularProgress';
|
||||
|
||||
import {
|
||||
INITIAL_PAGE_LIMIT, INITIAL_PAGE_SIZE,
|
||||
} from '../../assets/data/adminConstants';
|
||||
import ExcelExportButton from '../../components/common/button/ExcelExportButton';
|
||||
import Pagination from '../../components/common/Pagination/Pagination';
|
||||
import { CommonSearchBar } from '../../components/searchBar';
|
||||
import { languageNames } from '../../assets/data/types';
|
||||
import useCommonSearch from '../../hooks/useCommonSearch';
|
||||
import { CraftingDictionaryExport } from '../../apis';
|
||||
|
||||
const MetaCraftingView = () => {
|
||||
const token = sessionStorage.getItem('token');
|
||||
const { t } = useTranslation();
|
||||
const tableRef = useRef(null);
|
||||
|
||||
const [activeLanguage, setActiveLanguage] = useState('ko');
|
||||
|
||||
const [downloadState, setDownloadState] = useState({
|
||||
loading: false,
|
||||
progress: 0
|
||||
});
|
||||
|
||||
const {
|
||||
config,
|
||||
searchParams,
|
||||
data: dataList,
|
||||
handleSearch,
|
||||
handleReset,
|
||||
handleOrderByChange,
|
||||
updateSearchParams,
|
||||
loading,
|
||||
configLoaded,
|
||||
handlePageChange,
|
||||
handlePageSizeChange
|
||||
} = useCommonSearch("metaCraftingSearch");
|
||||
|
||||
useEffect(()=>{
|
||||
setDownloadState({
|
||||
loading: false,
|
||||
progress: 0
|
||||
});
|
||||
},[dataList]);
|
||||
|
||||
const tableHeaders = useMemo(() => {
|
||||
return [
|
||||
{ id: 'id', label: '제작 분류', width: '60px' },
|
||||
{ id: 'smallType', label: '프랍 그룹', width: '100px' },
|
||||
{ id: 'itemId', label: '제작 아이템 ID', width: '100px' },
|
||||
{ id: 'itemName', label: '제작 아이템명', width: '350px' },
|
||||
{ id: 'itemValue', label: '제작 아이템 개수', width: '80px' },
|
||||
{ id: 'recipeType', label: '레시피 필요 여부', width: '80px' },
|
||||
{ id: 'recipeId', label: '레시피 ID', width: '80px' },
|
||||
{ id: 'material', label: '제작 시 필요 재료'},
|
||||
{ id: 'attribute', label: '제작 시 필요 능력치' },
|
||||
{ id: 'craftingTime', label: '제작 시간', width: '80px' },
|
||||
{ id: 'beaconReduceTime', label: '비컨 사용 시 감소 시간', width: '100px' },
|
||||
{ id: 'beaconBonusItemId', label: '비컨 사용 시 획득 아이템ID', width: '100px' },
|
||||
{ id: 'maxCraftLimit', label: '1회당 제작 최대 가능수', width: '100px' },
|
||||
];
|
||||
}, []);
|
||||
|
||||
const renderTableForLanguage = useCallback((languageKey) => {
|
||||
// 해당 언어의 아이템 리스트 가져오기
|
||||
const languageItemList = dataList?.crafting_list?.[languageKey] || [];
|
||||
|
||||
return (
|
||||
<>
|
||||
<TableWrapper>
|
||||
<TableStyle ref={tableRef}>
|
||||
<thead>
|
||||
<tr>
|
||||
{tableHeaders.map(header => (
|
||||
<th key={header.id} width={header.width}>{header.label}</th>
|
||||
))}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{languageItemList?.map((item, index) => (
|
||||
<Fragment key={`${languageKey}-${index}`}>
|
||||
<tr>
|
||||
<td>{item.id}</td>
|
||||
<td>{item.type_small}</td>
|
||||
<td>{item.item_id}</td>
|
||||
<td>{item.item_name}</td>
|
||||
<td>{item.item_value}</td>
|
||||
<td>{item.recipe_type}</td>
|
||||
<td>{item.recipe_id}</td>
|
||||
<td>{item.material}</td>
|
||||
<td>{item.attribute}</td>
|
||||
<td>{item.crafting_time}</td>
|
||||
<td>{item.beacon_reduce_time}</td>
|
||||
<td>{item.beacon_bonus_item_id}</td>
|
||||
<td>{item.max_craft_limit}</td>
|
||||
</tr>
|
||||
</Fragment>
|
||||
))}
|
||||
</tbody>
|
||||
</TableStyle>
|
||||
</TableWrapper>
|
||||
{languageItemList.length > 0 &&
|
||||
<Pagination
|
||||
postsPerPage={searchParams.pageSize}
|
||||
totalPosts={dataList?.total_all}
|
||||
setCurrentPage={handlePageChange}
|
||||
currentPage={searchParams?.currentPage}
|
||||
pageLimit={INITIAL_PAGE_LIMIT}
|
||||
/>
|
||||
}
|
||||
</>
|
||||
);
|
||||
}, [dataList, loading, tableHeaders, searchParams, handlePageChange]);
|
||||
|
||||
// 언어별 탭 아이템 생성
|
||||
const tabItems = useMemo(() => {
|
||||
// 실제 데이터에서 사용 가능한 언어만 탭으로 생성
|
||||
const availableLanguages = dataList?.crafting_list ? Object.keys(dataList.crafting_list) : ['ko', 'en', 'ja'];
|
||||
|
||||
return availableLanguages.map(langKey => ({
|
||||
key: langKey,
|
||||
label: languageNames[langKey.charAt(0).toUpperCase() + langKey.slice(1)] || langKey.toUpperCase(),
|
||||
children: renderTableForLanguage(langKey)
|
||||
}));
|
||||
}, [dataList, renderTableForLanguage]);
|
||||
|
||||
|
||||
const handleTabChange = (key) => {
|
||||
setActiveLanguage(key);
|
||||
};
|
||||
|
||||
const excelParams = useMemo(() => ({
|
||||
...searchParams,
|
||||
lang: activeLanguage
|
||||
}), [searchParams, activeLanguage]);
|
||||
|
||||
return (
|
||||
<AnimatedPageWrapper>
|
||||
<Title>제작 아이템 조회</Title>
|
||||
<FormWrapper>
|
||||
<CommonSearchBar
|
||||
config={config}
|
||||
searchParams={searchParams}
|
||||
onSearch={(newParams, executeSearch = true) => {
|
||||
if (executeSearch) {
|
||||
handleSearch(newParams);
|
||||
} else {
|
||||
updateSearchParams(newParams);
|
||||
}
|
||||
}}
|
||||
onReset={handleReset}
|
||||
/>
|
||||
</FormWrapper>
|
||||
<ViewTableInfo orderType="asc" total={dataList?.total} total_all={dataList?.total_all} handleOrderBy={handleOrderByChange} handlePageSize={handlePageSizeChange}>
|
||||
<DownloadContainer>
|
||||
<ExcelExportButton
|
||||
functionName="CraftingDictionaryExport"
|
||||
params={excelParams}
|
||||
fileName={t('FILE_DICTIONARY_CRAFTING')}
|
||||
onLoadingChange={setDownloadState}
|
||||
dataSize={dataList?.total_all}
|
||||
/>
|
||||
{downloadState.loading && (
|
||||
<CircularProgressWrapper>
|
||||
<CircularProgress
|
||||
progress={downloadState.progress}
|
||||
size={36}
|
||||
progressColor="#4A90E2"
|
||||
/>
|
||||
</CircularProgressWrapper>
|
||||
)}
|
||||
</DownloadContainer>
|
||||
</ViewTableInfo>
|
||||
{
|
||||
loading ? <TableSkeleton width='100%' count={40} /> :
|
||||
<AnimatedTabs
|
||||
items={tabItems}
|
||||
activeKey={activeLanguage}
|
||||
onChange={handleTabChange}
|
||||
tabPosition="left"
|
||||
/>
|
||||
}
|
||||
</AnimatedPageWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
export default withAuth(authType.craftingDictionaryRead)(MetaCraftingView);
|
||||
102
src/pages/DataManage/RankManage.js
Normal file
102
src/pages/DataManage/RankManage.js
Normal file
@@ -0,0 +1,102 @@
|
||||
import React, { useState, useEffect, useMemo } from 'react';
|
||||
|
||||
import { Title } from '../../styles/Components';
|
||||
import { AnimatedPageWrapper } from '../../components/common/Layout'
|
||||
|
||||
import styled from 'styled-components';
|
||||
|
||||
import UserDefaultInfo from '../../components/DataManage/UserDefaultInfo';
|
||||
import UserAvatarInfo from '../../components/DataManage/UserAvatarInfo';
|
||||
import UserDressInfo from '../../components/DataManage/UserDressInfo';
|
||||
|
||||
import { authType } from '../../assets/data';
|
||||
import { withAuth } from '../../hooks/hook';
|
||||
import { TabRankManageList } from '../../assets/data/options';
|
||||
import {
|
||||
UserSearchBar,
|
||||
useUserSearch,
|
||||
} from '../../components/searchBar';
|
||||
import { AnimatedTabs } from '../../components/common';
|
||||
import { UserRankPioneerInfo } from '../../components/DataManage';
|
||||
|
||||
const RankManage = () => {
|
||||
const token = sessionStorage.getItem('token');
|
||||
|
||||
const [infoView, setInfoView] = useState('none');
|
||||
const [activeTab, setActiveTab] = useState('PIONEER');
|
||||
const [resultData, setResultData] = useState();
|
||||
|
||||
const {
|
||||
searchParams,
|
||||
loading: dataLoading,
|
||||
data,
|
||||
handleSearch,
|
||||
handleReset,
|
||||
updateSearchParams
|
||||
} = useUserSearch(token);
|
||||
|
||||
useEffect(() => {
|
||||
if(data) {
|
||||
setResultData(data);
|
||||
setInfoView('flex');
|
||||
} else {
|
||||
setInfoView('none');
|
||||
}
|
||||
}, [data])
|
||||
|
||||
const tabItems = useMemo(() => {
|
||||
return TabRankManageList.map(el => ({
|
||||
key: el.value,
|
||||
label: el.name,
|
||||
children: (() => {
|
||||
switch(el.value) {
|
||||
case 'PIONEER': return <UserRankPioneerInfo userInfo={resultData} />;
|
||||
case 'RUN_RACE': return <UserAvatarInfo userInfo={resultData} />;
|
||||
case 'BATTLE_OBJECT': return <UserDressInfo userInfo={resultData} />;
|
||||
default: return null;
|
||||
}
|
||||
})()
|
||||
}));
|
||||
}, [resultData]);
|
||||
|
||||
const handleTabChange = (key) => {
|
||||
setActiveTab(key);
|
||||
};
|
||||
|
||||
return (
|
||||
<AnimatedPageWrapper>
|
||||
<Title>랭킹 점수 관리</Title>
|
||||
<UserSearchBar
|
||||
searchParams={searchParams}
|
||||
onSearch={(newParams, executeSearch = true) => {
|
||||
if (executeSearch) {
|
||||
handleSearch(newParams);
|
||||
} else {
|
||||
updateSearchParams(newParams);
|
||||
setResultData();
|
||||
}
|
||||
}}
|
||||
onReset={handleReset}
|
||||
/>
|
||||
<UserWrapper display={infoView}>
|
||||
<AnimatedTabs
|
||||
items={tabItems}
|
||||
activeKey={activeTab}
|
||||
onChange={handleTabChange}
|
||||
tabPosition="center"
|
||||
/>
|
||||
</UserWrapper>
|
||||
</AnimatedPageWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
export default withAuth(authType.rankManagerRead)(RankManage);
|
||||
|
||||
const UserWrapper = styled.div`
|
||||
display: ${props => props.display};
|
||||
${props => props.display && `flex-flow:column;`}
|
||||
min-height: calc(100vh - 345px);
|
||||
overflow: hidden;
|
||||
padding-left: 40px;
|
||||
padding-right: 40px;
|
||||
`;
|
||||
@@ -1,5 +1,7 @@
|
||||
export { default as UserView } from './UserView';
|
||||
export { default as LandInfoView } from './LandInfoView';
|
||||
export { default as GameLogView } from './GameLogView';
|
||||
export { default as CryptView } from './CryptView';
|
||||
export { default as BusinessLogView} from './BusinessLogView';
|
||||
export { default as MetaItemView} from './MetaItemView';
|
||||
export { default as MetaCraftingView} from './MetaCraftingView';
|
||||
export { default as RankManage} from './RankManage';
|
||||
|
||||
Reference in New Issue
Block a user