init
38
src/App.css
Normal file
@@ -0,0 +1,38 @@
|
||||
.App {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.App-logo {
|
||||
height: 40vmin;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
.App-logo {
|
||||
animation: App-logo-spin infinite 20s linear;
|
||||
}
|
||||
}
|
||||
|
||||
.App-header {
|
||||
background-color: #282c34;
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: calc(10px + 2vmin);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.App-link {
|
||||
color: #61dafb;
|
||||
}
|
||||
|
||||
@keyframes App-logo-spin {
|
||||
from {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
25
src/App.js
Normal file
@@ -0,0 +1,25 @@
|
||||
import { BrowserRouter, Link } from 'react-router-dom';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import GlobalStyles from './styles/GlobalStyles';
|
||||
import RouteInfo from './RouteInfo';
|
||||
|
||||
import './i18n';
|
||||
|
||||
function App() {
|
||||
const isToken = sessionStorage.getItem('token') ? true : false;
|
||||
|
||||
return (
|
||||
<BrowserRouter>
|
||||
<GlobalStyles />
|
||||
<ControllLink>{isToken ? <Link to="/main" /> : <Link to="/fail" />}</ControllLink>
|
||||
<RouteInfo />
|
||||
</BrowserRouter>
|
||||
);
|
||||
}
|
||||
|
||||
export default App;
|
||||
|
||||
const ControllLink = styled.div`
|
||||
position: absolute;
|
||||
`;
|
||||
8
src/App.test.js
Normal file
@@ -0,0 +1,8 @@
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import App from './App';
|
||||
|
||||
test('renders learn react link', () => {
|
||||
render(<App />);
|
||||
const linkElement = screen.getByText(/learn react/i);
|
||||
expect(linkElement).toBeInTheDocument();
|
||||
});
|
||||
82
src/RouteInfo.js
Normal file
@@ -0,0 +1,82 @@
|
||||
import { Route, Routes } from 'react-router-dom';
|
||||
import { Layout, LoginLayout, MainLayout } from './components/common/Layout';
|
||||
import LoginBg from './assets/img/login-bg.png';
|
||||
import { Login } from './pages/Login';
|
||||
import LoginFail from './pages/LoginFail';
|
||||
import { AccountEdit, AccountRegist, PasswordReset } from './pages/Account';
|
||||
import Main from './pages/Main';
|
||||
import {
|
||||
AdminView,
|
||||
AuthSetting,
|
||||
AuthSettingUpdate,
|
||||
CaliumRequest,
|
||||
LogView,
|
||||
} from './pages/UserManage';
|
||||
import { EconomicIndex, UserIndex } from './pages/IndexManage';
|
||||
import { LandView, CryptView, GameLogView, UserView } from './pages/DataManage';
|
||||
import {
|
||||
Board,
|
||||
Event,
|
||||
EventRegist,
|
||||
Items,
|
||||
Mail,
|
||||
MailRegist,
|
||||
ReportList,
|
||||
UserBlock,
|
||||
UserBlockRegist,
|
||||
WhiteList,
|
||||
LandAuction,
|
||||
BattleEvent
|
||||
} from './pages/ServiceManage';
|
||||
|
||||
const RouteInfo = () => {
|
||||
return (
|
||||
<Routes>
|
||||
<Route element={<LoginLayout $bgimg={LoginBg} $padding="50px" />}>
|
||||
<Route path="/" element={<Login />} />
|
||||
<Route path="/fail" element={<LoginFail />} />
|
||||
<Route path="/account/regist" element={<AccountRegist />} />
|
||||
<Route path="/account/pwdreset" element={<PasswordReset />} />
|
||||
<Route path="/account/edit" element={<AccountEdit />} />
|
||||
</Route>
|
||||
<Route element={<MainLayout />}>
|
||||
<Route path="/main" element={<Main />} />
|
||||
</Route>
|
||||
<Route element={<Layout />}>
|
||||
<Route path="/usermanage/">
|
||||
<Route path="adminview" element={<AdminView />} />
|
||||
<Route path="logview" element={<LogView />} />
|
||||
<Route path="authsetting" element={<AuthSetting />} />
|
||||
<Route path="authsetting/:id" element={<AuthSettingUpdate />} />
|
||||
<Route path="caliumrequest" element={<CaliumRequest />} />
|
||||
</Route>
|
||||
<Route path="/indexmanage">
|
||||
<Route path="userindex" element={<UserIndex />} />
|
||||
<Route path="economicindex" element={<EconomicIndex />} />
|
||||
</Route>
|
||||
<Route path="/datamanage">
|
||||
<Route path="userview" element={<UserView />} />
|
||||
<Route path="landview" element={<LandView />} />
|
||||
<Route path="gamelogview" element={<GameLogView />} />
|
||||
<Route path="cryptview" element={<CryptView />} />
|
||||
</Route>
|
||||
<Route path="/servicemanage">
|
||||
<Route path="board" element={<Board />} />
|
||||
<Route path="whitelist" element={<WhiteList />} />
|
||||
<Route path="mail" element={<Mail />} />
|
||||
<Route path="mail/mailregist" element={<MailRegist />} />
|
||||
<Route path="userblock" element={<UserBlock />} />
|
||||
<Route path="userblock/userblockregist" element={<UserBlockRegist />} />
|
||||
<Route path="reportlist" element={<ReportList />} />
|
||||
<Route path="items" element={<Items />} />
|
||||
<Route path="event" element={<Event />} />
|
||||
<Route path="event/eventregist" element={<EventRegist />} />
|
||||
<Route path="landauction" element={<LandAuction />} />
|
||||
<Route path="battleevent" element={<BattleEvent />} />
|
||||
</Route>
|
||||
</Route>
|
||||
</Routes>
|
||||
)
|
||||
}
|
||||
|
||||
export default RouteInfo;
|
||||
79
src/RouteInfo.js.bak
Normal file
@@ -0,0 +1,79 @@
|
||||
import { Route, Routes } from 'react-router-dom';
|
||||
import { Layout, LoginLayout, MainLayout } from './components/common/Layout';
|
||||
import LoginBg from './assets/img/login-bg.png';
|
||||
import { Login } from './pages/Login';
|
||||
import LoginFail from './pages/LoginFail';
|
||||
import { AccountEdit, AccountRegist, PasswordReset } from './pages/Account';
|
||||
import Main from './pages/Main';
|
||||
import {
|
||||
AdminView,
|
||||
AuthSetting,
|
||||
AuthSettingUpdate,
|
||||
CaliumRequest,
|
||||
CaliumRequestRegist,
|
||||
LogView,
|
||||
} from './pages/UserManage';
|
||||
import { EconomicIndex, UserIndex } from './pages/IndexManage';
|
||||
import { ContentsView, CryptView, GameLogView, UserView } from './pages/DataManage';
|
||||
import {
|
||||
Board,
|
||||
Event,
|
||||
EventRegist,
|
||||
Items,
|
||||
Mail,
|
||||
MailRegist,
|
||||
ReportList,
|
||||
UserBlock,
|
||||
UserBlockRegist,
|
||||
WhiteList,
|
||||
} from './pages/ServiceManage';
|
||||
|
||||
const RouteInfo = () => {
|
||||
return (
|
||||
<Routes>
|
||||
<Route element={<LoginLayout $bgimg={LoginBg} $padding="50px" />}>
|
||||
<Route path="/" element={<Login />} />
|
||||
<Route path="/fail" element={<LoginFail />} />
|
||||
<Route path="/account/regist" element={<AccountRegist />} />
|
||||
<Route path="/account/pwdreset" element={<PasswordReset />} />
|
||||
<Route path="/account/edit" element={<AccountEdit />} />
|
||||
</Route>
|
||||
<Route element={<MainLayout />}>
|
||||
<Route path="/main" element={<Main />} />
|
||||
</Route>
|
||||
<Route element={<Layout />}>
|
||||
<Route path="/usermanage/">
|
||||
<Route path="adminview" element={<AdminView />} />
|
||||
<Route path="logview" element={<LogView />} />
|
||||
<Route path="authsetting" element={<AuthSetting />} />
|
||||
<Route path="authsetting/:id" element={<AuthSettingUpdate />} />
|
||||
<Route path="caliumrequest" element={<CaliumRequest />} />
|
||||
</Route>
|
||||
<Route path="/indexmanage">
|
||||
<Route path="userindex" element={<UserIndex />} />
|
||||
<Route path="economicindex" element={<EconomicIndex />} />
|
||||
</Route>
|
||||
<Route path="/datamanage">
|
||||
<Route path="userview" element={<UserView />} />
|
||||
<Route path="contentsview" element={<ContentsView />} />
|
||||
<Route path="gamelogview" element={<GameLogView />} />
|
||||
<Route path="cryptview" element={<CryptView />} />
|
||||
</Route>
|
||||
<Route path="/servicemanage">
|
||||
<Route path="board" element={<Board />} />
|
||||
<Route path="whitelist" element={<WhiteList />} />
|
||||
<Route path="mail" element={<Mail />} />
|
||||
<Route path="mail/mailregist" element={<MailRegist />} />
|
||||
<Route path="userblock" element={<UserBlock />} />
|
||||
<Route path="userblock/userblockregist" element={<UserBlockRegist />} />
|
||||
<Route path="reportlist" element={<ReportList />} />
|
||||
<Route path="items" element={<Items />} />
|
||||
<Route path="event" element={<Event />} />
|
||||
<Route path="event/eventregist" element={<EventRegist />} />
|
||||
</Route>
|
||||
</Route>
|
||||
</Routes>
|
||||
)
|
||||
}
|
||||
|
||||
export default RouteInfo;
|
||||
100
src/apis/Admin.js
Normal file
@@ -0,0 +1,100 @@
|
||||
//사용자 관리 - 사용자 조회 api 연결
|
||||
|
||||
import { Axios } from '../utils';
|
||||
|
||||
// 운영자 조회
|
||||
export const AdminViewList = async (token, searchType, searchKey, groupType, joinCheck, orderBy, size, currentPage) => {
|
||||
try {
|
||||
const res = await Axios.get(
|
||||
`/api/v1/admin/list?search_type=${searchType ? searchType : 'NAME'}&search_key=${searchKey ? searchKey : ''}&group_id=${groupType ? groupType : ''}&join_check=${joinCheck}&orderby=${
|
||||
orderBy ? orderBy : 'DESC'
|
||||
}&page_no=${currentPage}&page_size=${size}`,
|
||||
{
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
},
|
||||
);
|
||||
|
||||
|
||||
return res.data.data;
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('AdminViewList Error', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const AdminViewGroupList = async token => {
|
||||
try {
|
||||
const res = await Axios.get('/api/v1/admin/group-list', {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
});
|
||||
|
||||
return res.data.data.group_list;
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('AdminViewGroupList Error', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const AdminLoginApprove = async (token, params) => {
|
||||
try {
|
||||
const res = await Axios.patch(
|
||||
'/api/v1/admin',
|
||||
{ list: params },
|
||||
{
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
},
|
||||
);
|
||||
return res;
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('AdminLoginApprove Error', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const AdminChangeGroup = async (token, params) => {
|
||||
try {
|
||||
const res = await Axios.put(
|
||||
'/api/v1/admin',
|
||||
{ list: params },
|
||||
{
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
},
|
||||
);
|
||||
return res;
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('AdminChangeGroup Error', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const AdminDeleteUser = async (token, params) => {
|
||||
try {
|
||||
const res = await Axios.delete('/api/v1/admin', {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
data: { list: params },
|
||||
});
|
||||
return res;
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('AdminDeleteUser Error', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const AdminChangePw = async (token, params) => {
|
||||
try {
|
||||
const res = await Axios.post('/api/v1/admin/init-password', params, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
});
|
||||
|
||||
return res.data;
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('AdminChangePw Error', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
79
src/apis/Auth.js
Normal file
@@ -0,0 +1,79 @@
|
||||
//인증 관련 api 연결
|
||||
|
||||
import { Axios } from '../utils';
|
||||
|
||||
export const AuthInfo = async token => {
|
||||
try {
|
||||
const res = await Axios.get('/api/v1/admin/info', {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
});
|
||||
|
||||
return res.data.data;
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('AuthInfo', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const AuthLogin = async params => {
|
||||
try {
|
||||
const res = await Axios.post('/api/v1/auth/login', params);
|
||||
|
||||
return res.data;
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('AuthLogin Error', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const AuthRegist = async data => {
|
||||
try {
|
||||
const res = await Axios.post('/api/v1/auth/register', {
|
||||
email: data.userid,
|
||||
name: data.username,
|
||||
password: data.password,
|
||||
});
|
||||
|
||||
return res.data;
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
e.response.data.message.map(message => alert(message));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const AuthLogout = async token => {
|
||||
try {
|
||||
const res = await Axios.post('/api/v1/auth/logout', {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
});
|
||||
return res;
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('AuthLogout Error', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const AuthEdit = async (data, token) => {
|
||||
try {
|
||||
const res = await Axios.patch(
|
||||
'/api/v1/admin/change-password',
|
||||
{
|
||||
password: data.password,
|
||||
new_password: data.newPassword,
|
||||
},
|
||||
{
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
},
|
||||
);
|
||||
|
||||
return res;
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('AuthEdit Error', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
117
src/apis/Battle.js
Normal file
@@ -0,0 +1,117 @@
|
||||
//운영서비스 관리 - 전투시스템 api 연결
|
||||
|
||||
import { Axios } from '../utils';
|
||||
|
||||
// 전투시스템 리스트 조회
|
||||
export const BattleEventView = async (token, landType, landData, userType, userData, landSize, status, startDate, endDate, order, size, currentPage) => {
|
||||
try {
|
||||
const res = await Axios.get(
|
||||
`/api/v1/battle/event/list?land_type=${landType}&land_data=${landData}&user_type=${userType}&user_data=${userData}&land_size=${landSize}&status=${status}&start_dt=${startDate}&end_dt=${endDate}&orderby=${order}&page_no=${currentPage}
|
||||
&page_size=${size}`,
|
||||
{
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
},
|
||||
);
|
||||
|
||||
return res.data.data;
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('BattleEventView Error', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 전투시스템 상세보기
|
||||
export const BattleEventDetailView = async (token, id) => {
|
||||
try {
|
||||
const res = await Axios.get(`/api/v1/battle/event/detail/${id}`, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
});
|
||||
|
||||
return res.data.data;
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('BattleEventDetailView Error', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 전투시스템 등록
|
||||
export const BattleEventSingleRegist = async (token, params) => {
|
||||
try {
|
||||
const res = await Axios.post(`/api/v1/battle/event`, params, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
});
|
||||
|
||||
return res.data;
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('BattleEventSingleRegist Error', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 전투시스템 수정
|
||||
export const BattleEventModify = async (token, id, params) => {
|
||||
try {
|
||||
const res = await Axios.put(`/api/v1/battle/event/${id}`, params, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
});
|
||||
|
||||
return res.data;
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('BattleEventModify Error', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 전투시스템 삭제
|
||||
export const BattleEventDelete = async (token, params, id) => {
|
||||
try {
|
||||
const res = await Axios.delete(`/api/v1/battle/event/delete`, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
data: { list: params },
|
||||
});
|
||||
|
||||
return res.data;
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('BattleEventDelete Error', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const BattleConfigView = async (token) => {
|
||||
try {
|
||||
const res = await Axios.get(
|
||||
`/api/v1/battle/config/list`,
|
||||
{
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
},
|
||||
);
|
||||
|
||||
return res.data.data.battle_config_list;
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('BattleConfigView Error', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const BattleRewardView = async (token) => {
|
||||
try {
|
||||
const res = await Axios.get(
|
||||
`/api/v1/battle/reward/list`,
|
||||
{
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
},
|
||||
);
|
||||
|
||||
return res.data.data.battle_reward_list;
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('BattleRewardView Error', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
121
src/apis/BlackList.js
Normal file
@@ -0,0 +1,121 @@
|
||||
//운영서비스 관리 - 이용자 제재 리스트 api 연결
|
||||
|
||||
import { Axios } from '../utils';
|
||||
|
||||
// 블랙리스트 조회
|
||||
export const BlackListView = async (token, searchType, data, email, status, sanctions, period, order, size, currentPage) => {
|
||||
try {
|
||||
const res = await Axios.get(
|
||||
`/api/v1/black-list/list?search_type=${searchType ? searchType : ''}
|
||||
&search_key=${data ? data : ''}
|
||||
&email=${email ? email : ''}
|
||||
&status=${status ? status : ''}
|
||||
&sanctions=${sanctions ? sanctions : ''}
|
||||
&period=${period ? period : ''}
|
||||
&orderby=${order}
|
||||
&page_no=${currentPage}
|
||||
&page_size=${size}
|
||||
`,
|
||||
{
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
},
|
||||
);
|
||||
|
||||
return res.data.data;
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 블랙 리스트 상세 조회
|
||||
export const BlackListDetail = async (token, id) => {
|
||||
try {
|
||||
const res = await Axios.get(`api/v1/black-list/detail/${id}`, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
});
|
||||
|
||||
return res.data.data.detail;
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('BlackListDetail', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 제재 삭제
|
||||
export const BlackListDelete = async (token, params) => {
|
||||
try {
|
||||
const res = await Axios.delete(`api/v1/black-list`, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
data: { list: params },
|
||||
});
|
||||
|
||||
return res;
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('BlackListDelete', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 제재 등록
|
||||
export const BlackListRegist = async (token, params) => {
|
||||
try {
|
||||
const res = await Axios.post(`/api/v1/black-list`, params, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
});
|
||||
|
||||
return res;
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('BlacklistRegist', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// 엑셀 업로드
|
||||
export const BlackListMultipleUpload = async (token, file) => {
|
||||
const exelFile = new FormData()
|
||||
exelFile.append('file', file)
|
||||
try {
|
||||
const res = await Axios.post(`/api/v1/black-list/excel-upload`, exelFile, {
|
||||
headers: {
|
||||
"Content-Type": "multipart/form-data",
|
||||
Authorization: `Bearer ${token}`
|
||||
},
|
||||
});
|
||||
|
||||
return res.data.data.list;
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('BlacklistRegist', e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const BlackListExcelDown = async (token, filename) => {
|
||||
try {
|
||||
await Axios.get(`/api/v1/black-list/excel-down?file=${filename}`, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
responseType: 'blob',
|
||||
}).then(response => {
|
||||
const href = URL.createObjectURL(response.data);
|
||||
|
||||
const link = document.createElement('a');
|
||||
link.href = href;
|
||||
link.setAttribute('download', `${filename}`);
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
|
||||
document.body.removeChild(link);
|
||||
URL.revokeObjectURL(href);
|
||||
});
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('BlacklistExcelDown Error', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
82
src/apis/Calium.js
Normal file
@@ -0,0 +1,82 @@
|
||||
//운영자 관리 - 칼리움 api 연결
|
||||
|
||||
import { Axios } from '../utils';
|
||||
|
||||
// 칼리움 요청 리스트 조회
|
||||
export const CaliumRequestView = async (token, content, status, startDate, endDate, order, size, currentPage) => {
|
||||
try {
|
||||
const res = await Axios.get(
|
||||
`/api/v1/calium/list?content=${content}&status=${status}&start_dt=${startDate}&end_dt=${endDate}&orderby=${order}&page_no=${currentPage}
|
||||
&page_size=${size}`,
|
||||
{
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
},
|
||||
);
|
||||
|
||||
return res.data.data;
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('CaliumRequestView Error', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 칼리움 상세보기
|
||||
export const CaliumDetailView = async (token, id) => {
|
||||
try {
|
||||
const res = await Axios.get(`/api/v1/calium/detail/${id}`, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
});
|
||||
|
||||
return res.data.data.detail;
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('CaliumDetailView Error', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 칼리움 요청 등록
|
||||
export const CaliumRequestRegist = async (token, params) => {
|
||||
try {
|
||||
const res = await Axios.post(`/api/v1/calium`, params, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
});
|
||||
|
||||
return res;
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('CaliumRequestRegist Error', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 칼리움 충적
|
||||
export const CaliumCharge = async (token, params) => {
|
||||
try {
|
||||
const res = await Axios.post(`/api/v1/calium/charge`, params, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
});
|
||||
|
||||
return res.data;
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('CaliumCharge Error', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 칼리움 인출 가능 수량
|
||||
export const CaliumLimitCount = async (token) => {
|
||||
try {
|
||||
const res = await Axios.get(`/api/v1/calium/limit`, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
});
|
||||
|
||||
return res.data.data.withdrawable_info;
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('CaliumLimitCount Error', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
98
src/apis/Event.js
Normal file
@@ -0,0 +1,98 @@
|
||||
//운영서비스 관리 - 이벤트 api 연결
|
||||
|
||||
import { Axios } from '../utils';
|
||||
|
||||
// 이벤트 리스트 조회
|
||||
export const EventView = async (token, title, content, status, startDate, endDate, order, size, currentPage) => {
|
||||
try {
|
||||
const res = await Axios.get(
|
||||
`/api/v1/event/list?title=${title}&content=${content}&status=${status}&start_dt=${startDate}&end_dt=${endDate}&orderby=${order}&page_no=${currentPage}
|
||||
&page_size=${size}`,
|
||||
{
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
},
|
||||
);
|
||||
|
||||
return res.data.data;
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('EventView Error', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 이벤트 상세보기
|
||||
export const EventDetailView = async (token, id) => {
|
||||
try {
|
||||
const res = await Axios.get(`/api/v1/event/detail/${id}`, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
});
|
||||
|
||||
return res.data.data.detail;
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('EventDetailView Error', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 이벤트 등록
|
||||
export const EventSingleRegist = async (token, params) => {
|
||||
try {
|
||||
const res = await Axios.post(`/api/v1/event`, params, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
});
|
||||
|
||||
return res;
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('EventSingleRegist Error', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 우편 수정
|
||||
export const EventModify = async (token, id, params) => {
|
||||
try {
|
||||
const res = await Axios.put(`/api/v1/event/${id}`, params, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
});
|
||||
|
||||
return res.data.data.list;
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('EventModify Error', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 우편 삭제
|
||||
export const EventDelete = async (token, params, id) => {
|
||||
try {
|
||||
const res = await Axios.delete(`/api/v1/event/delete`, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
data: { list: params },
|
||||
});
|
||||
|
||||
return res.data.data.list;
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('EventDelete Error', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 이벤트 우편 아이템 확인
|
||||
export const EventIsItem = async (token, params) => {
|
||||
try {
|
||||
const res = await Axios.post(`/api/v1/event/item`, params, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
});
|
||||
|
||||
return res;
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('EventIsItem Error', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
82
src/apis/Group.js
Normal file
@@ -0,0 +1,82 @@
|
||||
//사용자 관리 - 그룹 조회 api 연결
|
||||
|
||||
import { Axios } from '../utils';
|
||||
|
||||
// 그룹 권한 조회
|
||||
export const GroupViewList = async (token, orderBy, size, currentPage) => {
|
||||
try {
|
||||
const res = await Axios.get(`/api/v1/groups/list?orderby=${orderBy ? orderBy : 'DESC'}
|
||||
&page_no=${size}&page_size=${currentPage}`, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
});
|
||||
|
||||
return res.data.data;
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('GroupViewList Error', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 그룹 상세 권한 조회
|
||||
export const GroupDetailViewList = async (token, id) => {
|
||||
try {
|
||||
const res = await Axios.get(`/api/v1/groups/detail/${id}`, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
});
|
||||
|
||||
return res.data.data;
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('GroupViewList Error', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 권한 그룹 등록
|
||||
export const GroupRegist = async (token, params) => {
|
||||
try {
|
||||
const res = await Axios.post(`/api/v1/groups`, params, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
});
|
||||
return res;
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
// e.response.data.message.map(message => alert(message));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 권한 그룹 선택 삭제
|
||||
export const GroupDelete = async (token, params) => {
|
||||
try {
|
||||
const res = await Axios.delete('/api/v1/groups', {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
data: { list: params },
|
||||
});
|
||||
|
||||
return res;
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('GroupDelete', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 권한 수정
|
||||
export const GroupModify = async (token, id, params) => {
|
||||
try {
|
||||
const res = await Axios.put(
|
||||
`/api/v1/groups/${id}`,
|
||||
{ list: params },
|
||||
{
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
},
|
||||
);
|
||||
return res;
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('GroupModify Error', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
36
src/apis/History.js
Normal file
@@ -0,0 +1,36 @@
|
||||
//사용자 관리 - 로그조회 api 연결
|
||||
|
||||
import { Axios } from '../utils';
|
||||
|
||||
export const LogViewList = async (token, searchType, searchKey, historyType, startDt, endDt, orderBy, size, currentPage) => {
|
||||
try {
|
||||
const res = await Axios.get(
|
||||
`/api/v1/history/list?search_type=${searchType ? searchType : 'NAME'}&search_key=${searchKey ? searchKey : ''}&history_type=${historyType ? historyType : ''}&start_dt=${
|
||||
startDt ? startDt : ''
|
||||
}&end_dt=${endDt ? endDt : ''}&orderby=${orderBy ? orderBy : 'DESC'}&page_no=${currentPage}&page_size=${size}`,
|
||||
{
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
},
|
||||
);
|
||||
|
||||
return res.data.data;
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('LogViewList Error', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const LogviewDetail = async (token, id) => {
|
||||
try {
|
||||
const res = await Axios.get(`/api/v1/history/detail/${id}`, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
});
|
||||
|
||||
return res.data.data.content;
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('LogViewDetail', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
456
src/apis/Indicators.js
Normal file
@@ -0,0 +1,456 @@
|
||||
//지표 관리 - 유저 지표, 경제 지표 api
|
||||
|
||||
import { Axios } from '../utils';
|
||||
|
||||
// 1. 유저 지표
|
||||
// 2. 경제 지표
|
||||
|
||||
// 1. 유저 지표
|
||||
|
||||
// 이용자 지표 조회
|
||||
export const userIndexView = async (token, sendDate, endDate) => {
|
||||
try {
|
||||
const res = await Axios.get(`/api/v1/indicators/user/list?start_dt=${sendDate}&end_dt=${endDate}`, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
});
|
||||
|
||||
return res.data.data.user_statistics_list;
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('MailView Error', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 이용자 지표 총계
|
||||
export const userTotalIndex = async token => {
|
||||
try {
|
||||
const res = await Axios.get(`/api/v1/indicators/user/total`, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
});
|
||||
return res.data.data;
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('userTotalIndex', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 유저 지표 다운로드
|
||||
export const userIndexExport = async (token, filename, sendDate, endDate) => {
|
||||
try {
|
||||
await Axios.get(`/api/v1/indicators/user/excel-down?file=${filename}&start_dt=${sendDate}&end_dt=${endDate}`, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
responseType: 'blob',
|
||||
}).then(response => {
|
||||
const href = URL.createObjectURL(response.data);
|
||||
|
||||
const link = document.createElement('a');
|
||||
link.href = href;
|
||||
link.setAttribute('download', `${filename}`);
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
|
||||
document.body.removeChild(link);
|
||||
URL.revokeObjectURL(href);
|
||||
});
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('userIndexExport Error', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Retention
|
||||
export const RetentionIndexView = async (token, start_dt, end_dt) => {
|
||||
try {
|
||||
const res = await Axios.get(`/api/v1/indicators/retention/list?start_dt=${start_dt}&end_dt=${end_dt}`, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
});
|
||||
|
||||
return res.data.data;
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('RetentionIndexView Error', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Retention 다운로드
|
||||
export const RetentionIndexExport = async (token, filename, sendDate, endDate) => {
|
||||
try {
|
||||
await Axios.get(`/api/v1/indicators/retention/excel-down?${filename}&start_dt=${sendDate}&end_dt=${endDate}`, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
responseType: 'blob',
|
||||
}).then(response => {
|
||||
const href = URL.createObjectURL(response.data);
|
||||
|
||||
const link = document.createElement('a');
|
||||
link.href = href;
|
||||
link.setAttribute('download', `${filename}`);
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
|
||||
document.body.removeChild(link);
|
||||
URL.revokeObjectURL(href);
|
||||
});
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('RetentionIndexExport Error', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Segment
|
||||
export const SegmentIndexView = async (token, search_dt) => {
|
||||
try {
|
||||
const res = await Axios.get(`/api/v1/indicators/segment/list?search_dt=${search_dt}`, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
});
|
||||
|
||||
return res.data.data;
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('SegmentIndexView Error', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Segment 다운로드
|
||||
export const SegmentIndexExport = async (token, filename, endDate) => {
|
||||
try {
|
||||
await Axios.get(`/api/v1/indicators/segment/excel-down?${filename}&search_dt=${endDate}`, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
responseType: 'blob',
|
||||
}).then(response => {
|
||||
const href = URL.createObjectURL(response.data);
|
||||
|
||||
const link = document.createElement('a');
|
||||
link.href = href;
|
||||
link.setAttribute('download', `${filename}`);
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
|
||||
document.body.removeChild(link);
|
||||
URL.revokeObjectURL(href);
|
||||
});
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('SegmentIndexExport Error', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Playtime
|
||||
export const PlaytimeIndexView = async (token, start_dt, end_dt) => {
|
||||
try {
|
||||
const res = await Axios.get(`/api/v1/indicators/playtime/list?start_dt=${start_dt}&end_dt=${end_dt}`, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
});
|
||||
|
||||
return res.data.data;
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('PlaytimeIndexView Error', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Playtime 다운로드
|
||||
export const PlaytimeIndexExport = async (token, filename, sendDate, endDate) => {
|
||||
try {
|
||||
await Axios.get(`/api/v1/indicators/playtime/excel-down?file=${filename}&start_dt=${sendDate}&end_dt=${endDate}`, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
responseType: 'blob',
|
||||
}).then(response => {
|
||||
const href = URL.createObjectURL(response.data);
|
||||
|
||||
const link = document.createElement('a');
|
||||
link.href = href;
|
||||
link.setAttribute('download', `${filename}`);
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
|
||||
document.body.removeChild(link);
|
||||
URL.revokeObjectURL(href);
|
||||
});
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('PlaytimeIndexExport Error', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 2. 경제 지표
|
||||
|
||||
// 재화 조회 (currency)
|
||||
export const CurrencyIndexView = async (token, start_dt, end_dt, currency_type) => {
|
||||
try {
|
||||
const res = await Axios.get(`/api/v1/indicators/currency/use?start_dt=${start_dt}&end_dt=${end_dt}¤cy_type=${currency_type}`, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
});
|
||||
|
||||
return res.data.data;
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('currencyIndexView Error', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 재화 지표 다운로드
|
||||
export const CurrencyIndexExport = async (token, filename, sendDate, endDate, currencyType) => {
|
||||
try {
|
||||
await Axios.get(`/api/v1/indicators/currency/excel-down?file=${filename}&start_dt=${sendDate}&end_dt=${endDate}¤cy_type=${currencyType}`, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
responseType: 'blob',
|
||||
}).then(response => {
|
||||
const href = URL.createObjectURL(response.data);
|
||||
|
||||
const link = document.createElement('a');
|
||||
link.href = href;
|
||||
link.setAttribute('download', `${filename}`);
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
|
||||
document.body.removeChild(link);
|
||||
URL.revokeObjectURL(href);
|
||||
});
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('CurrencyIndexExport Error', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// VBP
|
||||
export const VbpIndexView = async (token, start_dt, end_dt) => {
|
||||
try {
|
||||
const res = await Axios.get(`/api/v1/indicators/currency/vbp?start_dt=${start_dt}&end_dt=${end_dt}`, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
});
|
||||
|
||||
return res.data.data;
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('VbpIndexView Error', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// VBP 다운로드
|
||||
export const VBPIndexExport = async (token, filename, sendDate, endDate) => {
|
||||
try {
|
||||
await Axios.get(`/api/v1/indicators/currency/excel-down?file=${filename}&start_dt=${sendDate}&end_dt=${endDate}`, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
responseType: 'blob',
|
||||
}).then(response => {
|
||||
const href = URL.createObjectURL(response.data);
|
||||
|
||||
const link = document.createElement('a');
|
||||
link.href = href;
|
||||
link.setAttribute('download', `${filename}`);
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
|
||||
document.body.removeChild(link);
|
||||
URL.revokeObjectURL(href);
|
||||
});
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('VBPIndexExport Error', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Item
|
||||
export const ItemIndexView = async (token, start_dt, end_dt) => {
|
||||
try {
|
||||
const res = await Axios.get(`/api/v1/indicators/currency/item?start_dt=${start_dt}&end_dt=${end_dt}`, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
});
|
||||
|
||||
return res.data.data;
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('ItemIndexView Error', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Item 다운로드
|
||||
export const ItemIndexExport = async (token, filename, sendDate, endDate) => {
|
||||
try {
|
||||
await Axios.get(`/api/v1/indicators/currency/excel-down?file=${filename}&start_dt=${sendDate}&end_dt=${endDate}`, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
responseType: 'blob',
|
||||
}).then(response => {
|
||||
const href = URL.createObjectURL(response.data);
|
||||
|
||||
const link = document.createElement('a');
|
||||
link.href = href;
|
||||
link.setAttribute('download', `${filename}`);
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
|
||||
document.body.removeChild(link);
|
||||
URL.revokeObjectURL(href);
|
||||
});
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('ItemIndexExport Error', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Instance
|
||||
export const InstanceIndexView = async (token, data, start_dt, end_dt) => {
|
||||
try {
|
||||
const res = await Axios.get(
|
||||
`/api/v1/indicators/currency/instance?search_key=${data ? data : ''}
|
||||
&start_dt=${start_dt}&end_dt=${end_dt}`,
|
||||
{
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
},
|
||||
);
|
||||
|
||||
return res.data.data;
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('InstanceIndexView Error', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Instance 다운로드
|
||||
export const InstanceIndexExport = async (token, filename, data, sendDate, endDate) => {
|
||||
try {
|
||||
await Axios.get(
|
||||
`/api/v1/indicators/currency/excel-down?file=${filename}&search_key=${data ? data : ''}
|
||||
&start_dt=${sendDate}&end_dt=${endDate}`,
|
||||
{
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
responseType: 'blob',
|
||||
},
|
||||
).then(response => {
|
||||
const href = URL.createObjectURL(response.data);
|
||||
|
||||
const link = document.createElement('a');
|
||||
link.href = href;
|
||||
link.setAttribute('download', `${filename}`);
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
|
||||
document.body.removeChild(link);
|
||||
URL.revokeObjectURL(href);
|
||||
});
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('InstanceIndexExport Error', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Clothes
|
||||
export const ClothesIndexView = async (token, data, start_dt, end_dt) => {
|
||||
try {
|
||||
const res = await Axios.get(`/api/v1/indicators/currency/clothes?search_key=${data ? data : ''}&start_dt=${start_dt}&end_dt=${end_dt}`, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
});
|
||||
|
||||
return res.data.data;
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('ClothesIndexView Error', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Clothes 다운로드
|
||||
export const ClothesIndexExport = async (token, filename, data, sendDate, endDate) => {
|
||||
try {
|
||||
await Axios.get(
|
||||
`/api/v1/indicators/currency/excel-down?file=${filename}&search_key=${data ? data : ''}
|
||||
&start_dt=${sendDate}&end_dt=${endDate}`,
|
||||
{
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
responseType: 'blob',
|
||||
},
|
||||
).then(response => {
|
||||
const href = URL.createObjectURL(response.data);
|
||||
|
||||
const link = document.createElement('a');
|
||||
link.href = href;
|
||||
link.setAttribute('download', `${filename}`);
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
|
||||
document.body.removeChild(link);
|
||||
URL.revokeObjectURL(href);
|
||||
});
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('ClothesIndexExport Error', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// DAU
|
||||
export const DailyActiveUserView = async (token, start_dt, end_dt) => {
|
||||
try {
|
||||
const res = await Axios.get(`/api/v1/indicators/dau/list?start_dt=${start_dt}&end_dt=${end_dt}`, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
});
|
||||
|
||||
return res.data.data.dau_list;
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('DailyActiveUserView Error', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
// DAU 다운로드
|
||||
export const DailyActiveUserExport = async (token, filename, sendDate, endDate) => {
|
||||
try {
|
||||
await Axios.get(`/api/v1/indicators/dau/excel-down?file=${filename}&start_dt=${sendDate}&end_dt=${endDate}`, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
responseType: 'blob',
|
||||
}).then(response => {
|
||||
const href = URL.createObjectURL(response.data);
|
||||
|
||||
const link = document.createElement('a');
|
||||
link.href = href;
|
||||
link.setAttribute('download', `${filename}`);
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
|
||||
document.body.removeChild(link);
|
||||
URL.revokeObjectURL(href);
|
||||
});
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('PlaytimeIndexExport Error', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
// Daily Medal
|
||||
export const DailyMedalView = async (token, start_dt, end_dt) => {
|
||||
try {
|
||||
const res = await Axios.get(`/api/v1/indicators/daily-medal/list?start_dt=${start_dt}&end_dt=${end_dt}`, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
});
|
||||
|
||||
return res.data.data.daily_medal_list;
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('DailyMedalView Error', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
27
src/apis/Item.js
Normal file
@@ -0,0 +1,27 @@
|
||||
//운영서비스 관리 - 아이템 리스트 api 연결
|
||||
|
||||
import { Axios } from '../utils';
|
||||
|
||||
//아이템 리스트 조회
|
||||
export const ItemListView = async (token, searchType, data, status, restore, order, size, currentPage) => {
|
||||
try {
|
||||
const res = await Axios.get(
|
||||
`/api/v1/items/list?search_type=${searchType ? searchType : ''}
|
||||
&search_key=${data ? data : ''}
|
||||
&orderby=${order}
|
||||
&page_no=${currentPage}
|
||||
&page_size=${size}
|
||||
`,
|
||||
{
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
},
|
||||
);
|
||||
// console.log(res.data.data);
|
||||
|
||||
return res.data.data;
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
|
||||
}
|
||||
}
|
||||
};
|
||||
131
src/apis/Land.js
Normal file
@@ -0,0 +1,131 @@
|
||||
//운영서비스 관리 - 랜드 경매 api 연결
|
||||
|
||||
import { Axios } from '../utils';
|
||||
|
||||
// 랜드 경매 리스트 조회
|
||||
export const LandAuctionView = async (token, landType, landData, userType, userData, landSize, status, startDate, endDate, order, size, currentPage) => {
|
||||
try {
|
||||
const res = await Axios.get(
|
||||
`/api/v1/land/auction/list?land_type=${landType}&land_data=${landData}&user_type=${userType}&user_data=${userData}&land_size=${landSize}&status=${status}&start_dt=${startDate}&end_dt=${endDate}&orderby=${order}&page_no=${currentPage}
|
||||
&page_size=${size}`,
|
||||
{
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
},
|
||||
);
|
||||
|
||||
return res.data.data;
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('LandAuctionView Error', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 랜드 경매 상세보기
|
||||
export const LandAuctionDetailView = async (token, id) => {
|
||||
try {
|
||||
const res = await Axios.get(`/api/v1/land/auction/detail/${id}`, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
});
|
||||
|
||||
return res.data.data;
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('LandAuctionDetailView Error', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 랜드 경매 등록
|
||||
export const LandAuctionSingleRegist = async (token, params) => {
|
||||
try {
|
||||
const res = await Axios.post(`/api/v1/land/auction`, params, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
});
|
||||
|
||||
return res.data;
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('LandAuctionSingleRegist Error', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 랜드 경매 수정
|
||||
export const LandAuctionModify = async (token, id, params) => {
|
||||
try {
|
||||
const res = await Axios.put(`/api/v1/land/auction/${id}`, params, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
});
|
||||
|
||||
return res.data;
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('LandAuctionModify Error', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 랜드 경매 삭제
|
||||
export const LandAuctionDelete = async (token, params, id) => {
|
||||
try {
|
||||
const res = await Axios.delete(`/api/v1/land/auction/delete`, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
data: { list: params },
|
||||
});
|
||||
|
||||
return res.data;
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('LandAuctionDelete Error', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const LandView = async (token) => {
|
||||
try {
|
||||
const res = await Axios.get(
|
||||
`/api/v1/land/list`,
|
||||
{
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
},
|
||||
);
|
||||
|
||||
return res.data.data.land_list;
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('LandView Error', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const BuildingView = async (token) => {
|
||||
try {
|
||||
const res = await Axios.get(
|
||||
`/api/v1/land/building/list`,
|
||||
{
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
},
|
||||
);
|
||||
|
||||
return res.data.data.building_list;
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('LandView Error', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const LandDetailView = async (token, id) => {
|
||||
try {
|
||||
const res = await Axios.get(`/api/v1/land/detail/${id}`, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
});
|
||||
|
||||
return res.data.data.detail;
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('LandDetailView Error', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
142
src/apis/Mail.js
Normal file
@@ -0,0 +1,142 @@
|
||||
//운영서비스 관리 - 메일 api 연결
|
||||
|
||||
import { Axios } from '../utils';
|
||||
|
||||
// 메일 리스트 조회
|
||||
export const MailView = async (token, mailTitle, content, sendType, sendStatus, mailType, receiveType, sendDate, endDate, order, size, currentPage) => {
|
||||
try {
|
||||
const res = await Axios.get(
|
||||
`/api/v1/mail/list?mail_title=${mailTitle}&content=${content}&send_type=${sendType}&send_status=${sendStatus}&mail_type=${mailType}&receive_type=${receiveType}&start_dt=${sendDate}&end_dt=${endDate}&orderby=${order}&page_no=${currentPage}
|
||||
&page_size=${size}`,
|
||||
{
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
},
|
||||
);
|
||||
|
||||
return res.data.data;
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('MailView Error', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 메일 상세보기
|
||||
export const MailDetailView = async (token, id) => {
|
||||
try {
|
||||
const res = await Axios.get(`/api/v1/mail/detail/${id}`, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
});
|
||||
|
||||
return res.data.data.detail;
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('MailDetailView Error', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 우편 단일 등록
|
||||
export const MailSingleRegist = async (token, params) => {
|
||||
try {
|
||||
const res = await Axios.post(`/api/v1/mail`, params, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
});
|
||||
|
||||
return res;
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('MailRegist Error', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 우편 수정
|
||||
export const MailModify = async (token, id, params) => {
|
||||
try {
|
||||
const res = await Axios.put(`/api/v1/mail/${id}`, params, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
});
|
||||
|
||||
return res.data.data.list;
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('MailModify Error', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 우편 삭제
|
||||
export const MailDelete = async (token, params, id) => {
|
||||
try {
|
||||
const res = await Axios.delete(`/api/v1/mail/delete`, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
data: { list: params },
|
||||
});
|
||||
|
||||
return res.data.data.list;
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('MailDelete Error', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 우편 다운로드
|
||||
export const MailExcelDown = async (token, filename) => {
|
||||
try {
|
||||
await Axios.get(`/api/v1/mail/excel-down?file=${filename}`, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
responseType: 'blob',
|
||||
}).then(response => {
|
||||
const href = URL.createObjectURL(response.data);
|
||||
|
||||
const link = document.createElement('a');
|
||||
link.href = href;
|
||||
link.setAttribute('download', `${filename}`);
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
|
||||
document.body.removeChild(link);
|
||||
URL.revokeObjectURL(href);
|
||||
});
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('MailExcelDown Error', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 우편 업로드
|
||||
export const MailMultiRegsit = async (token, file) => {
|
||||
const exelFile = new FormData();
|
||||
exelFile.append('file', file);
|
||||
try {
|
||||
const res = await Axios.post(`/api/v1/mail/excel-upload`, exelFile, {
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data',
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
});
|
||||
return res;
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('MailMultiRegsit', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 우편 아이템 확인
|
||||
export const MailIsItem = async (token, params) => {
|
||||
try {
|
||||
const res = await Axios.post(`/api/v1/mail/item`, params, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
});
|
||||
|
||||
return res;
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('MailItemCheck Error', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
69
src/apis/Notice.js
Normal file
@@ -0,0 +1,69 @@
|
||||
//운영 서비스 관리 - 인게임 메세지 api 연결
|
||||
|
||||
import { Axios } from '../utils';
|
||||
|
||||
export const NoticeListView = async token => {
|
||||
try {
|
||||
const res = await Axios.get(`/api/v1/notice/list`, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
});
|
||||
return res.data.data.list;
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('NoticeList', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const NoticeDetailView = async (token, id) => {
|
||||
try {
|
||||
const res = await Axios.get(`/api/v1/notice/detail/${id}`, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
});
|
||||
return res.data.data;
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('NoticeDetailView Error', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const NoticeRegist = async (token, params) => {
|
||||
try {
|
||||
const res = await Axios.post('/api/v1/notice', params, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
});
|
||||
return res;
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('NoticeRegist Error', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const NoticeModify = async (token, id, params) => {
|
||||
try {
|
||||
const res = await Axios.put(`/api/v1/notice/${id}`, params, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
});
|
||||
return res;
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('NoticeModify Error', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const NoticeDelete = async (token, params) => {
|
||||
try {
|
||||
const res = await Axios.delete('/api/v1/notice', {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
data: { list: params },
|
||||
});
|
||||
return res;
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('NoticeDelete Error', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
75
src/apis/Report.js
Normal file
@@ -0,0 +1,75 @@
|
||||
//운영 서비스 관리 - 신고내역 api 연결
|
||||
|
||||
import { Axios } from '../utils';
|
||||
|
||||
// 신고내역 전체 조회
|
||||
export const ReportTotalView = async (token, startDate, endDate) => {
|
||||
try {
|
||||
const res = await Axios.get(`/api/v1/report/total?start_dt=${startDate}&end_dt=${endDate}`, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
});
|
||||
return res.data.data;
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('ReportTotalView', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 신고내역 리스트 조회
|
||||
export const ReportListView = async (token, startDate, endDate, reportType, status, searchType, searchKey, order, size, currentPage) => {
|
||||
try {
|
||||
const res = await Axios.get(
|
||||
`/api/v1/report/list?start_dt=${startDate}&end_dt=${endDate}&report_type=${reportType}&orderby=${order}&page_no=${currentPage}&page_size=${size}&status=${status}&search_type=${searchType}&search_key=${searchKey}`,
|
||||
{
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
},
|
||||
);
|
||||
return res.data.data;
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('ReportListView', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
// 신고내역 상세조회
|
||||
export const ReportListDetailView = async (token, pk, sk) => {
|
||||
try {
|
||||
const res = await Axios.get(`/api/v1/report/report-detail?pk=${pk}&sk=${sk}`, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
});
|
||||
return res.data.data.report;
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('ReportListDetailView', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 신고내역 답변 작성
|
||||
export const RepostReplyMessage = async (token, params) => {
|
||||
try {
|
||||
const res = await Axios.post('/api/v1/report/reply', params, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
});
|
||||
return res;
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('RepostReplyMessage Error', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 신고내역 답변 조회
|
||||
export const ReportReplyDetail = async (token, pk, sk) => {
|
||||
try {
|
||||
const res = await Axios.get(`/api/v1/report/reply-detail?pk=${pk}&sk=${sk}`, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
});
|
||||
return res.data.data.reply;
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('ReportReplyDetail', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
264
src/apis/Users.js
Normal file
@@ -0,0 +1,264 @@
|
||||
//운영 정보 관리 - 유저 조회 api 연결
|
||||
|
||||
import { Axios } from '../utils';
|
||||
|
||||
// 유저 조회
|
||||
export const UserView = async (token, searchType, searchKey) => {
|
||||
try {
|
||||
const res = await Axios.get(
|
||||
`/api/v1/users/find-users?
|
||||
search_type=${searchType ? searchType : 'NAME'}
|
||||
&search_key=${searchKey ? searchKey : ''}`,
|
||||
{ headers: { Authorization: `Bearer ${token}` } },
|
||||
);
|
||||
|
||||
return res.data.data.result;
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('UserView Error', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 기본 정보 조회
|
||||
export const UserInfoView = async (token, guid) => {
|
||||
try {
|
||||
const res = await Axios.get(`/api/v1/users/basicinfo?guid=${guid}`, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
});
|
||||
|
||||
return res.data.data;
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('UserInfoView Error', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 닉네임 변경
|
||||
export const UserChangeNickName = async (token, params) => {
|
||||
try {
|
||||
const res = await Axios.put('/api/v1/users/change-nickname', params, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
});
|
||||
|
||||
return res;
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('UserChangeNickName Error', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// GM 권한 변경
|
||||
export const UserChangeAdminLevel = async (token, params) => {
|
||||
try {
|
||||
const res = await Axios.put('/api/v1/users/change-level', params, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
});
|
||||
|
||||
return res;
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('UserChangeGMType Error', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 아바타 조회
|
||||
export const UserAvatarView = async (token, guid) => {
|
||||
try {
|
||||
const res = await Axios.get(`/api/v1/users/avatarinfo?guid=${guid}`, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
});
|
||||
|
||||
return res.data.data;
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('UserAvatarView Error', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 의상 조회
|
||||
export const UserClothView = async (token, guid) => {
|
||||
try {
|
||||
const res = await Axios.get(`/api/v1/users/clothinfo?guid=${guid}`, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
});
|
||||
|
||||
return res.data.data;
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('UserClothView Error', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 도구 조회
|
||||
export const UserToolView = async (token, guid) => {
|
||||
try {
|
||||
const res = await Axios.get(`/api/v1/users/toolslot?guid=${guid}`, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
});
|
||||
|
||||
return res.data.data;
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('UserToolView Error', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 인벤토리 조회
|
||||
export const UserInventoryView = async (token, guid) => {
|
||||
try {
|
||||
const res = await Axios.get(`/api/v1/users/inventory?guid=${guid}`, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
});
|
||||
|
||||
return res.data.data;
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('UserInventoryView Error', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 인벤토리 아이템 삭제
|
||||
export const UserInventoryItemDelete = async (token, params) => {
|
||||
try {
|
||||
const res = await Axios.delete(`/api/v1/users/inventory/delete/item`, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
data: params,
|
||||
});
|
||||
|
||||
return res.data;
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('UserInventoryItemDelete Error', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 타투 조회
|
||||
export const UserTattooView = async (token, guid) => {
|
||||
try {
|
||||
const res = await Axios.get(`/api/v1/users/tattoo?guid=${guid}`, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
});
|
||||
|
||||
return res.data.data;
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('UserTattooView Error', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 퀘스트 조회
|
||||
export const UserQuestView = async (token, guid) => {
|
||||
try {
|
||||
const res = await Axios.get(`/api/v1/users/quest?guid=${guid}`, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
});
|
||||
|
||||
return res.data.data;
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('UserQuestView Error', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 친구목록 조회
|
||||
export const UserFriendListView = async (token, guid) => {
|
||||
try {
|
||||
const res = await Axios.get(`/api/v1/users/friendlist?guid=${guid}`, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
});
|
||||
|
||||
return res.data.data;
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('UserFriendListView Error', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 우편 조회
|
||||
export const UserMailView = async (token, guid, option) => {
|
||||
try {
|
||||
const res = await Axios.get(`/api/v1/users/mail?guid=${guid}&type=${option}`, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
});
|
||||
|
||||
return res.data.data;
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('UserMailView Error', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 우편 삭제
|
||||
export const UserMailDelete = async (token, params) => {
|
||||
try {
|
||||
const res = await Axios.delete(`/api/v1/users/mail/delete`, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
data: params,
|
||||
});
|
||||
|
||||
return res.data;
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('UserMailView Error', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 우편 아이템 삭제
|
||||
export const UserMailItemDelete = async (token, params) => {
|
||||
try {
|
||||
const res = await Axios.delete(`/api/v1/users/mail/delete/item`, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
data: params,
|
||||
});
|
||||
|
||||
return res.data;
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('UserMailItemDelete Error', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 유저 우편 상세 정보 조회
|
||||
export const UserMailDetailView = async (token, id) => {
|
||||
try {
|
||||
const res = await Axios.get(`/api/v1/users/mail/${id}}`, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
});
|
||||
|
||||
return res.data.data;
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('UserMailDetailView Error', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 마이홈 조회
|
||||
export const UserMyhomeView = async (token, guid) => {
|
||||
try {
|
||||
const res = await Axios.get(`/api/v1/users/myhome?guid=${guid}`, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
});
|
||||
|
||||
return res.data.data;
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('UserMyhomeView Error', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
134
src/apis/WhiteList.js
Normal file
@@ -0,0 +1,134 @@
|
||||
//운영서비스 관리 - 화이트 리스트 api 연결
|
||||
|
||||
import { Axios } from '../utils';
|
||||
|
||||
export const WhiteListData = async token => {
|
||||
try {
|
||||
const res = await Axios.get(`/api/v1/white-list/list`, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
});
|
||||
|
||||
return res.data.data.list;
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('whiteList Error', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 선택 삭제
|
||||
export const WhiteListDelete = async (token, params) => {
|
||||
try {
|
||||
const res = await Axios.delete(`/api/v1/white-list`, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
data: { list: params },
|
||||
});
|
||||
|
||||
return res;
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('WhiteListDelete', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 선택 승인
|
||||
export const WhiteListAllow = async (token, params) => {
|
||||
try {
|
||||
const res = await Axios.patch(
|
||||
`/api/v1/white-list`,
|
||||
{ list: params },
|
||||
{
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
},
|
||||
);
|
||||
return res;
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('WhiteListAllow', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 화이트 리스트 등록 (단일)
|
||||
export const WhiteListRegist = async (token, params) => {
|
||||
try {
|
||||
const res = await Axios.post(`/api/v1/white-list`, params, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
});
|
||||
return res;
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('WhiteListRegist', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 화이트리스트 엑셀 업로더
|
||||
export const WhiteListExelUpload = async (token, file) => {
|
||||
const exelFile = new FormData();
|
||||
exelFile.append('file', file);
|
||||
try {
|
||||
const res = await Axios.post(`/api/v1/white-list/excel-upload`, exelFile, {
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data',
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
});
|
||||
|
||||
return res;
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('WhiteListExelUpload', e);
|
||||
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 화이트 리스트 등록(복수) -> 등록하는 것임
|
||||
export const WhiteListMultiRegsit = async (token, file) => {
|
||||
const exelFile = new FormData();
|
||||
exelFile.append('file', file);
|
||||
try {
|
||||
const res = await Axios.post(`/api/v1/white-list/multiPost`, exelFile, {
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data',
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
});
|
||||
|
||||
return res;
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
throw new Error('WhiteListMultiRegsit', e);
|
||||
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 엑셀 다운로드
|
||||
export const WhiteListExport = async (token, fileName) => {
|
||||
try{
|
||||
await Axios.get(`/api/v1/white-list/excelDownLoad`, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
responseType: 'blob',
|
||||
}).then(response => {
|
||||
const href = URL.createObjectURL(response.data);
|
||||
|
||||
const link = document.createElement('a');
|
||||
const fileName = 'Caliverse_whitelist.xlsx';
|
||||
link.href = href;
|
||||
link.setAttribute('download', `${fileName}`);
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
|
||||
document.body.removeChild(link);
|
||||
URL.revokeObjectURL(href);
|
||||
})
|
||||
|
||||
}catch(e) {
|
||||
if(e instanceof Error) {
|
||||
throw new Error('WhiteListExport Error', e);
|
||||
}
|
||||
}
|
||||
};
|
||||
14
src/apis/index.js
Normal file
@@ -0,0 +1,14 @@
|
||||
export * from './Admin';
|
||||
export * from './Auth';
|
||||
export * from './Group';
|
||||
export * from './History';
|
||||
export * from './Mail';
|
||||
export * from './Notice';
|
||||
export * from './WhiteList';
|
||||
export * from './BlackList';
|
||||
export * from './Users';
|
||||
export * from './Indicators';
|
||||
export * from './Item';
|
||||
export * from './Event';
|
||||
export * from './Calium';
|
||||
export * from './Land';
|
||||
1
src/assets/data/HourList.js
Normal file
@@ -0,0 +1 @@
|
||||
export const HourList = ['00', '01', '02', '03', '04', '05', '06', '07', '08', '09', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20', '21', '22', '23'];
|
||||
62
src/assets/data/MinuteList.js
Normal file
@@ -0,0 +1,62 @@
|
||||
export const MinuteList = [
|
||||
'00',
|
||||
'01',
|
||||
'02',
|
||||
'03',
|
||||
'04',
|
||||
'05',
|
||||
'06',
|
||||
'07',
|
||||
'08',
|
||||
'09',
|
||||
'10',
|
||||
'11',
|
||||
'12',
|
||||
'13',
|
||||
'14',
|
||||
'15',
|
||||
'16',
|
||||
'17',
|
||||
'18',
|
||||
'19',
|
||||
'20',
|
||||
'21',
|
||||
'22',
|
||||
'23',
|
||||
'24',
|
||||
'25',
|
||||
'26',
|
||||
'27',
|
||||
'28',
|
||||
'29',
|
||||
'30',
|
||||
'31',
|
||||
'32',
|
||||
'33',
|
||||
'34',
|
||||
'35',
|
||||
'36',
|
||||
'37',
|
||||
'38',
|
||||
'39',
|
||||
'40',
|
||||
'41',
|
||||
'42',
|
||||
'43',
|
||||
'44',
|
||||
'45',
|
||||
'46',
|
||||
'47',
|
||||
'48',
|
||||
'49',
|
||||
'50',
|
||||
'51',
|
||||
'52',
|
||||
'53',
|
||||
'54',
|
||||
'55',
|
||||
'56',
|
||||
'57',
|
||||
'58',
|
||||
'59',
|
||||
];
|
||||
8
src/assets/data/adminConstants.js
Normal file
@@ -0,0 +1,8 @@
|
||||
const INITIAL_PAGE_SIZE = 50;
|
||||
const INITIAL_CURRENT_PAGE = 1;
|
||||
const INITIAL_PAGE_LIMIT = 10;
|
||||
export const TYPE_REGISTRY = 'regist';
|
||||
export const TYPE_MODIFY = 'modify';
|
||||
export const NONE = 'NONE';
|
||||
|
||||
export { INITIAL_PAGE_SIZE, INITIAL_CURRENT_PAGE, INITIAL_PAGE_LIMIT };
|
||||
53
src/assets/data/applicator.json
Normal file
@@ -0,0 +1,53 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2019-09/schema",
|
||||
"$id": "https://json-schema.org/draft/2019-09/meta/applicator",
|
||||
"$vocabulary": {
|
||||
"https://json-schema.org/draft/2019-09/vocab/applicator": true
|
||||
},
|
||||
"$recursiveAnchor": true,
|
||||
|
||||
"title": "Applicator vocabulary meta-schema",
|
||||
"type": ["object", "boolean"],
|
||||
"properties": {
|
||||
"additionalItems": {"$recursiveRef": "#"},
|
||||
"unevaluatedItems": {"$recursiveRef": "#"},
|
||||
"items": {
|
||||
"anyOf": [{"$recursiveRef": "#"}, {"$ref": "#/$defs/schemaArray"}]
|
||||
},
|
||||
"contains": {"$recursiveRef": "#"},
|
||||
"additionalProperties": {"$recursiveRef": "#"},
|
||||
"unevaluatedProperties": {"$recursiveRef": "#"},
|
||||
"properties": {
|
||||
"type": "object",
|
||||
"additionalProperties": {"$recursiveRef": "#"},
|
||||
"default": {}
|
||||
},
|
||||
"patternProperties": {
|
||||
"type": "object",
|
||||
"additionalProperties": {"$recursiveRef": "#"},
|
||||
"propertyNames": {"format": "regex"},
|
||||
"default": {}
|
||||
},
|
||||
"dependentSchemas": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"$recursiveRef": "#"
|
||||
}
|
||||
},
|
||||
"propertyNames": {"$recursiveRef": "#"},
|
||||
"if": {"$recursiveRef": "#"},
|
||||
"then": {"$recursiveRef": "#"},
|
||||
"else": {"$recursiveRef": "#"},
|
||||
"allOf": {"$ref": "#/$defs/schemaArray"},
|
||||
"anyOf": {"$ref": "#/$defs/schemaArray"},
|
||||
"oneOf": {"$ref": "#/$defs/schemaArray"},
|
||||
"not": {"$recursiveRef": "#"}
|
||||
},
|
||||
"$defs": {
|
||||
"schemaArray": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"items": {"$recursiveRef": "#"}
|
||||
}
|
||||
}
|
||||
}
|
||||
64
src/assets/data/data.js
Normal file
@@ -0,0 +1,64 @@
|
||||
export const benItems = [
|
||||
"19010003",
|
||||
"19010001",
|
||||
"19010002",
|
||||
"19010005"
|
||||
];
|
||||
|
||||
export const HourList = ['00', '01', '02', '03', '04', '05', '06', '07', '08', '09', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20', '21', '22', '23'];
|
||||
|
||||
export const MinuteList = [
|
||||
'00', '01', '02', '03', '04', '05', '06', '07', '08', '09',
|
||||
'10', '11', '12', '13', '14', '15', '16', '17', '18', '19',
|
||||
'20', '21', '22', '23', '24', '25', '26', '27', '28', '29',
|
||||
'30', '31', '32', '33', '34', '35', '36', '37', '38', '39',
|
||||
'40', '41', '42', '43', '44', '45', '46', '47', '48', '49',
|
||||
'50', '51', '52', '53', '54', '55', '56', '57', '58', '59',
|
||||
];
|
||||
|
||||
export const months = ['01', '02', '03', '04', '05', '06', '07', '08', '09', '10', '11', '12'];
|
||||
|
||||
export const caliumRequestInitData = {
|
||||
dept: '',
|
||||
count: 0,
|
||||
content: '',
|
||||
}
|
||||
|
||||
export const STATUS_STYLES = {
|
||||
COMPLETE: {
|
||||
background: '#58AB62',
|
||||
color: 'white'
|
||||
},
|
||||
WAIT: {
|
||||
background: '#DEBB46',
|
||||
color: 'black'
|
||||
},
|
||||
FAIL: {
|
||||
background: '#D33B27',
|
||||
color: 'white'
|
||||
},
|
||||
FINISH: {
|
||||
background: '#D9D9D9',
|
||||
color: 'black'
|
||||
},
|
||||
REJECT: {
|
||||
background: '#D33B27',
|
||||
color: 'white'
|
||||
},
|
||||
CANCEL: {
|
||||
background: '#D33B27',
|
||||
color: 'white'
|
||||
},
|
||||
RESV_START: {
|
||||
background: '#58AB62',
|
||||
color: 'white'
|
||||
},
|
||||
AUCTION_START: {
|
||||
background: '#4287f5',
|
||||
color: 'white'
|
||||
},
|
||||
AUCTION_END: {
|
||||
background: '#A37FB8',
|
||||
color: 'white'
|
||||
},
|
||||
};
|
||||
23
src/assets/data/index.js
Normal file
@@ -0,0 +1,23 @@
|
||||
export {authType, ivenTabType, modalTypes, TabList, tattooSlot, commonStatus, ViewTitleCountType, landAuctionStatusType} from './types'
|
||||
export {
|
||||
mailSendType,
|
||||
mailType,
|
||||
mailSendStatus,
|
||||
mailReceiveType,
|
||||
adminLevelType,
|
||||
logOption,
|
||||
eventStatus,
|
||||
wellType,
|
||||
blockStatus,
|
||||
blockSanctions,
|
||||
blockPeriod,
|
||||
blockType,
|
||||
caliumStatus,
|
||||
landSize,
|
||||
userSearchType,
|
||||
landAuctionStatus,
|
||||
landSearchType,
|
||||
CurrencyType,
|
||||
languageType
|
||||
} from './options'
|
||||
export {benItems, MinuteList, HourList, caliumRequestInitData, STATUS_STYLES, months} from './data'
|
||||
150
src/assets/data/menuConfig.js
Normal file
@@ -0,0 +1,150 @@
|
||||
import { authType } from './types';
|
||||
|
||||
export const menuConfig = {
|
||||
usermanage: {
|
||||
title: '운영자 관리',
|
||||
items: {
|
||||
adminview: {
|
||||
title: '운영자 조회',
|
||||
permissions: {
|
||||
read: authType.adminSearchRead,
|
||||
confirm: authType.adminSearchConfirm,
|
||||
update: authType.adminSearchUpdate,
|
||||
delete: authType.adminSearchDelete
|
||||
}
|
||||
},
|
||||
logview: {
|
||||
title: '사용 이력 조회',
|
||||
permissions: {
|
||||
read: authType.adminLogSearchRead
|
||||
}
|
||||
},
|
||||
authsetting: {
|
||||
title: '권한 설정',
|
||||
permissions: {
|
||||
read: authType.authoritySettingRead,
|
||||
update: authType.authoritySettingUpdate,
|
||||
delete: authType.authoritySettingDelete
|
||||
}
|
||||
},
|
||||
caliumrequest: {
|
||||
title: '칼리움 요청',
|
||||
permissions: {
|
||||
read: authType.caliumRequestRead,
|
||||
update: authType.caliumRequestUpdate
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
indexmanage: {
|
||||
title: '지표 관리',
|
||||
items: {
|
||||
userindex: {
|
||||
title: '유저 지표',
|
||||
permissions: {
|
||||
read: authType.userIndicatorsRead
|
||||
}
|
||||
},
|
||||
economicindex: {
|
||||
title: '경제 지표',
|
||||
permissions: {
|
||||
read: authType.economicIndicatorsRead
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
datamanage: {
|
||||
title: '운영 정보 관리',
|
||||
items: {
|
||||
userview: {
|
||||
title: '유저 조회',
|
||||
permissions: {
|
||||
read: authType.userSearchRead,
|
||||
update: authType.userSearchUpdate,
|
||||
delete: authType.userSearchDelete
|
||||
}
|
||||
},
|
||||
landview: {
|
||||
title: '랜드 조회',
|
||||
permissions: {
|
||||
read: authType.landRead,
|
||||
update: authType.landUpdate,
|
||||
delete: authType.landDelete
|
||||
}
|
||||
},
|
||||
gamelogview: {
|
||||
title: '게임 로그 조회',
|
||||
permissions: {
|
||||
read: authType.gameLogRead
|
||||
}
|
||||
},
|
||||
cryptview: {
|
||||
title: '크립토 조회',
|
||||
permissions: {
|
||||
read: authType.cryptoRead
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
servicemanage: {
|
||||
title: '운영 서비스 관리',
|
||||
items: {
|
||||
board: {
|
||||
title: '인게임 메시지',
|
||||
permissions: {
|
||||
read: authType.inGameRead,
|
||||
update: authType.inGameUpdate,
|
||||
delete: authType.inGameDelete
|
||||
}
|
||||
},
|
||||
mail: {
|
||||
title: '우편',
|
||||
permissions: {
|
||||
read: authType.mailRead,
|
||||
update: authType.mailUpdate,
|
||||
delete: authType.mailDelete
|
||||
}
|
||||
},
|
||||
userblock: {
|
||||
title: '이용자 제재',
|
||||
permissions: {
|
||||
read: authType.blackListRead,
|
||||
update: authType.blackListUpdate,
|
||||
delete: authType.blackListDelete
|
||||
}
|
||||
},
|
||||
reportlist: {
|
||||
title: '신고내역',
|
||||
permissions: {
|
||||
read: authType.reportRead,
|
||||
update: authType.reportUpdate,
|
||||
delete: authType.reportDelete
|
||||
}
|
||||
},
|
||||
event: {
|
||||
title: '보상 이벤트 관리',
|
||||
permissions: {
|
||||
read: authType.eventRead,
|
||||
update: authType.eventUpdate,
|
||||
delete: authType.eventDelete
|
||||
}
|
||||
},
|
||||
landauction: {
|
||||
title: '랜드 경매 관리',
|
||||
permissions: {
|
||||
read: authType.landAuctionRead,
|
||||
update: authType.landAuctionUpdate,
|
||||
delete: authType.landAuctionDelete
|
||||
}
|
||||
},
|
||||
battleevent: {
|
||||
title: '전투시스템 타입 스케줄러',
|
||||
permissions: {
|
||||
read: authType.battleEventRead,
|
||||
update: authType.battleEventUpdate,
|
||||
delete: authType.battleEventDelete
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
};
|
||||
198
src/assets/data/options.js
Normal file
@@ -0,0 +1,198 @@
|
||||
export const languageType = [
|
||||
{ value: 'KO', name: '한국어' },
|
||||
{ value: 'EN', name: '영어' },
|
||||
{ value: 'JA', name: '일본어' },
|
||||
];
|
||||
|
||||
export const mailSendType = [
|
||||
{ value: 'ALL', name: '전체' },
|
||||
{ value: 'RESERVE_SEND', name: '예약 발송' },
|
||||
{ value: 'DIRECT_SEND', name: '즉시 발송' },
|
||||
];
|
||||
|
||||
export const mailSendStatus = [
|
||||
{ value: 'ALL', name: '전체' },
|
||||
{ value: 'WAIT', name: '대기' },
|
||||
{ value: 'FINISH', name: '완료' },
|
||||
{ value: 'FAIL', name: '실패' },
|
||||
{ value: 'RUNNING', name: '전송중' },
|
||||
];
|
||||
|
||||
export const mailType = [
|
||||
{ value: 'ALL', name: '전체' },
|
||||
{ value: 'SYSTEM_GUID', name: '시스템 안내' },
|
||||
{ value: 'INSPECTION_COMPENSATION', name: '점검 보상' },
|
||||
{ value: 'RECOVER_COMPENSATION', name: '복구 보상' },
|
||||
{ value: 'EVENT_COMPENSATION', name: '이벤트 보상' },
|
||||
];
|
||||
|
||||
export const mailReceiveType = [
|
||||
{ value: 'ALL', name: '전체' },
|
||||
{ value: 'SINGLE', name: '단일' },
|
||||
{ value: 'MULTIPLE', name: '복수' },
|
||||
];
|
||||
|
||||
export const adminLevelType = [
|
||||
{ value: '0', name: '없음' },
|
||||
{ value: '1', name: 'GM' },
|
||||
{ value: '2', name: 'Super GM' },
|
||||
{ value: '3', name: 'Developer' },
|
||||
]
|
||||
|
||||
export const eventStatus = [
|
||||
{ value: 'ALL', name: '전체' },
|
||||
{ value: 'RUNNING', name: '진행중' },
|
||||
{ value: 'WAIT', name: '대기' },
|
||||
{ value: 'FINISH', name: '완료' },
|
||||
{ value: 'FAIL', name: '실패' },
|
||||
{ value: 'DELETE', name: '삭제' },
|
||||
];
|
||||
|
||||
export const caliumStatus = [
|
||||
{ value: 'ALL', name: '전체' },
|
||||
{ value: 'WAIT', name: '대기중' },
|
||||
{ value: 'COMPLETE', name: '승인완료' },
|
||||
{ value: 'FINISH', name: '충전완료' },
|
||||
{ value: 'REJECT', name: '반려' },
|
||||
{ value: 'FAIL', name: '실패' },
|
||||
];
|
||||
|
||||
export const landAuctionStatus = [
|
||||
{ value: 'ALL', name: '전체' },
|
||||
{ value: 'WAIT', name: '등록완료' },
|
||||
{ value: 'RESV_START', name: '예약시작' },
|
||||
{ value: 'AUCTION_START', name: '경매시작' },
|
||||
{ value: 'AUCTION_END', name: '경매완료' },
|
||||
{ value: 'STL_START', name: '정산시작' },
|
||||
{ value: 'STL_END', name: '정산완료' },
|
||||
{ value: 'CANCEL', name: '취소' },
|
||||
{ value: 'FAIL', name: '실패' },
|
||||
];
|
||||
|
||||
export const wellType = [
|
||||
{ value: '19010001', name: '골드' },
|
||||
{ value: '19010002', name: '사파이어' },
|
||||
{ value: '19010005', name: '루비' },
|
||||
];
|
||||
|
||||
export const logOption = [
|
||||
{ value: 'LOGIN_PERMITTED', name: '로그인 승인' },
|
||||
{ value: 'ADMIN_INFO_UPDATE', name: '운영자 정보 수정' },
|
||||
{ value: 'ADMIN_INFO_DELETE', name: '운영자 정보 삭제' },
|
||||
{ value: 'PASSWORD_INIT', name: '비밀번호 초기화' },
|
||||
{ value: 'USER_INFO_UPDATE', name: '유저 정보 변경' },
|
||||
{ value: 'GROUP_AUTH_UPDATE', name: '그룹 권한 수정' },
|
||||
{ value: 'GROUP_DELETE', name: '그룹 삭제' },
|
||||
{ value: 'NOTICE_DELETE', name: '인게임 메시지 삭제' },
|
||||
{ value: 'MAIL_ADD', name: '우편 등록' },
|
||||
{ value: 'MAIL_UPDATE', name: '우편 수정' },
|
||||
{ value: 'MAIL_DELETE', name: '우편 삭제' },
|
||||
{ value: 'MAIL_SEND_FAIL', name: '우편 전송 실패' },
|
||||
{ value: 'MAIL_SEND', name: '우편 전송' },
|
||||
{ value: 'NOTICE_ADD', name: '공지사항 등록' },
|
||||
{ value: 'NOTICE_UPDATE', name: '공지사항 수정' },
|
||||
{ value: 'NOTICE_DELETE', name: '공지사항 삭제' },
|
||||
{ value: 'NOTICE_SEND_FAIL', name: '공지사항 전송 실패' },
|
||||
{ value: 'WHITELIST_DELETE', name: '화이트리스트 삭제' },
|
||||
{ value: 'BLACKLIST_DELETE', name: '유저 제재 삭제' },
|
||||
{ value: 'REPORT_DELETE', name: '신고내역 삭제' },
|
||||
{ value: 'SCHEDULE_MAIL_FAIL', name: '메일 스케줄 실패' },
|
||||
{ value: 'SCHEDULE_NOTICE_FAIL', name: '공지 스케줄 실패' },
|
||||
{ value: 'USER_MAIL_DELETE', name: '유저 메일 삭제' },
|
||||
{ value: 'USER_ITEM_DELETE', name: '유저 아이템 삭제' },
|
||||
{ value: 'ITEM_DELETE', name: '아이템 삭제' },
|
||||
{ value: 'MAIL_ITEM_DELETE', name: '아이템 삭제' },
|
||||
{ value: 'MAIL_ITEM_UPDATE', name: '아이템 삭제' },
|
||||
{ value: 'INVENTORY_ITEM_DELETE', name: '우편 아이템 삭제' },
|
||||
{ value: 'INVENTORY_ITEM_UPDATE', name: '우편 아이템 수정' },
|
||||
{ value: 'EVENT_ADD', name: '이벤트 등록' },
|
||||
{ value: 'EVENT_UPDATE', name: '이벤트 수정' },
|
||||
{ value: 'EVENT_DELETE', name: '이벤트 삭제' },
|
||||
];
|
||||
|
||||
export const blockStatus = [
|
||||
{ value: 'ALL', name: '전체' },
|
||||
{ value: 'INPROGRESS', name: '제재중' },
|
||||
{ value: 'EXPIRATION', name: '기간만료' },
|
||||
{ value: 'WAIT', name: '대기 중' },
|
||||
{ value: 'FAIL', name: '실패' },
|
||||
];
|
||||
|
||||
export const blockSanctions = [
|
||||
{ value: 'ALL', name: '전체' },
|
||||
{ value: 'Bad_Behavior', name: '비매너 행위' },
|
||||
{ value: 'Inappropriate_Name', name: '불건전 이름 사용' },
|
||||
{ value: 'Cash_Transaction', name: '현금거래 행위' },
|
||||
{ value: 'Game_Interference', name: '게임 진행 방해' },
|
||||
{ value: 'Service_Interference', name: '운영서비스 방해' },
|
||||
{ value: 'Account_Impersonation', name: '계정도용' },
|
||||
{ value: 'Bug_Abuse', name: '버그/어뷰징' },
|
||||
{ value: 'Illegal_Program', name: '불법프로그램 사용' },
|
||||
{ value: 'Personal_Info_Leak', name: '개인정보 유출' },
|
||||
{ value: 'Admin_Impersonation', name: '운영자 사칭' },
|
||||
];
|
||||
|
||||
export const blockPeriod = [
|
||||
{ value: 'ALL', name: '전체' },
|
||||
// { value: 'WARNING', name: '경고' },
|
||||
{ value: 'D1', name: '1일' },
|
||||
{ value: 'D3', name: '3일' },
|
||||
{ value: 'D7', name: '7일' },
|
||||
{ value: 'D15', name: '15일' },
|
||||
{ value: 'D30', name: '30일' },
|
||||
{ value: 'PERMANENT', name: '영구정지' },
|
||||
];
|
||||
|
||||
export const userSearchType = [
|
||||
{ value: 'GUID', name: 'GUID' },
|
||||
{ value: 'NAME', name: '닉네임' },
|
||||
];
|
||||
|
||||
export const landSearchType = [
|
||||
{ value: 'ID', name: '랜드ID' },
|
||||
{ value: 'NAME', name: '랜드명' },
|
||||
];
|
||||
|
||||
export const blockType = [
|
||||
{ value: '', name: '선택' },
|
||||
{ value: 'Access_Restrictions', name: '접근 제한' },
|
||||
// { value: 'Chatting_Restrictions', name: '채팅 제한' },
|
||||
];
|
||||
|
||||
export const landSize = [
|
||||
{ value: 'ALL', name: '전체' },
|
||||
{value: 'SMALL', name: '소형'},
|
||||
{value: 'MEDIUM', name: '중형'},
|
||||
{value: 'LARGE', name: '대형'},
|
||||
{value: 'GIANT', name: '초대형'},
|
||||
]
|
||||
|
||||
export const CurrencyType = [
|
||||
{value: 'Gold', name: '골드' },
|
||||
{value: 'Sapphire', name: '사파이어' },
|
||||
{value: 'Calium', name: '칼리움' },
|
||||
{value: 'Beam', name: 'Beam' },
|
||||
{value: 'Ruby', name: '루비' }
|
||||
]
|
||||
|
||||
export const battleEventStatus = [
|
||||
{ value: 'ALL', name: '전체' },
|
||||
{ value: 'WAIT', name: '중단' },
|
||||
{ value: 'REGISTER', name: '예약완료' },
|
||||
{ value: 'CANCEL', name: '예약취소' },
|
||||
{ value: 'END', name: '종료' },
|
||||
{ value: 'RUNNING', name: '진행중' },
|
||||
{ value: 'FAIL', name: '실패' },
|
||||
];
|
||||
|
||||
export const battleRepeatType = [
|
||||
{ value: 'NONE', name: '없음' },
|
||||
{ value: 'DAY', name: 'Day' },
|
||||
{ value: 'SUNDAY', name: 'Week-일' },
|
||||
{ value: 'MONDAY', name: 'Week-월' },
|
||||
{ value: 'TUESDAY', name: 'Week-화' },
|
||||
{ value: 'WEDNESDAY', name: 'Week-수' },
|
||||
{ value: 'THURSDAY', name: 'Week-목' },
|
||||
{ value: 'FRIDAY', name: 'Week-금' },
|
||||
{ value: 'SATURDAY', name: 'Week-토' },
|
||||
];
|
||||
119
src/assets/data/types.js
Normal file
@@ -0,0 +1,119 @@
|
||||
export const authType = {
|
||||
adminSearchRead: 1,
|
||||
adminSearchConfirm: 2,
|
||||
adminSearchUpdate: 3,
|
||||
adminSearchDelete: 4,
|
||||
adminLogSearchRead: 5,
|
||||
authoritySettingRead: 6,
|
||||
authoritySettingUpdate: 7,
|
||||
authoritySettingDelete: 8,
|
||||
userIndicatorsRead: 9,
|
||||
economicIndicatorsRead: 10,
|
||||
userSearchRead: 11,
|
||||
userSearchUpdate: 12,
|
||||
landRead: 13,
|
||||
gameLogRead: 14,
|
||||
cryptoRead: 15,
|
||||
inGameRead: 16,
|
||||
inGameUpdate: 17,
|
||||
inGameDelete: 18,
|
||||
whiteListRead: 19,
|
||||
whiteListConfirm: 20,
|
||||
whiteListUpdate: 21,
|
||||
mailRead: 22,
|
||||
mailUpdate: 23,
|
||||
blackListRead: 24,
|
||||
blackListUpdate: 25,
|
||||
reportRead: 26,
|
||||
reportUpdate: 27,
|
||||
whiteListDelete: 28,
|
||||
mailDelete: 29,
|
||||
blackListDelete: 30,
|
||||
reportDelete: 31,
|
||||
itemRead: 32,
|
||||
itemUpdate: 33,
|
||||
itemDelete: 34,
|
||||
userSearchDelete: 35,
|
||||
eventRead: 36,
|
||||
eventUpdate: 37,
|
||||
eventDelete: 38,
|
||||
caliumRequestRead: 39,
|
||||
caliumRequestUpdate: 40,
|
||||
landAuctionRead: 41,
|
||||
landAuctionUpdate: 42,
|
||||
landAuctionDelete: 43,
|
||||
landUpdate: 44,
|
||||
landDelete: 45,
|
||||
battleEventRead: 46,
|
||||
battleEventUpdate: 47,
|
||||
battleEventDelete: 48
|
||||
};
|
||||
|
||||
export const TabList = [
|
||||
{ title: '기본정보' },
|
||||
{ title: '아바타' },
|
||||
{ title: '의상' },
|
||||
{ title: '도구' },
|
||||
{ title: '인벤토리' },
|
||||
{ title: '우편' },
|
||||
{ title: '마이홈' },
|
||||
{ title: '친구목록' },
|
||||
{ title: '타투' },
|
||||
{ title: '퀘스트' },
|
||||
// { title: '클레임' },
|
||||
];
|
||||
|
||||
export const ivenTabType = {
|
||||
CLOTH: "cloth",
|
||||
PROP: "prop",
|
||||
BEAUTY: "beauty",
|
||||
TATTOO: "tattoo",
|
||||
CURRENCY: "currency",
|
||||
ETC: "etc"
|
||||
};
|
||||
|
||||
export const modalTypes = {
|
||||
confirmOkCancel: "confirmOkCancel",
|
||||
completed: "completed",
|
||||
childOkCancel: "childOkCancel",
|
||||
}
|
||||
|
||||
export const tattooSlot = {
|
||||
0: "미장착",
|
||||
1: "가슴",
|
||||
2: "왼팔",
|
||||
3: "오른팔"
|
||||
}
|
||||
|
||||
export const commonStatus = {
|
||||
running: "RUNNING",
|
||||
wait: "WAIT",
|
||||
finish: "FINISH",
|
||||
fail: "FAIL",
|
||||
delete: "DELETE",
|
||||
reject: "REJECT",
|
||||
complete: "COMPLETE",
|
||||
}
|
||||
|
||||
export const ViewTitleCountType = {
|
||||
total: "total",
|
||||
calium: "calium",
|
||||
}
|
||||
|
||||
export const landAuctionStatusType = {
|
||||
wait: "WAIT",
|
||||
resv_start: "RESV_START",
|
||||
auction_start: "AUCTION_START",
|
||||
auction_end: "AUCTION_END",
|
||||
stl_end: "STL_END",
|
||||
fail: "FAIL",
|
||||
cancel: "CANCEL",
|
||||
}
|
||||
|
||||
export const battleEventStatusType = {
|
||||
wait: "WAIT",
|
||||
register: "REGISTER",
|
||||
end: "END",
|
||||
fail: "FAIL",
|
||||
cancel: "CANCEL",
|
||||
}
|
||||
BIN
src/assets/img/datamanage/img-profile.png
Normal file
|
After Width: | Height: | Size: 3.2 KiB |
BIN
src/assets/img/icon/icon-add.png
Normal file
|
After Width: | Height: | Size: 366 B |
BIN
src/assets/img/icon/icon-arrow.png
Normal file
|
After Width: | Height: | Size: 221 B |
BIN
src/assets/img/icon/icon-chk.png
Normal file
|
After Width: | Height: | Size: 523 B |
BIN
src/assets/img/icon/icon-close.png
Normal file
|
After Width: | Height: | Size: 248 B |
BIN
src/assets/img/icon/icon-date.png
Normal file
|
After Width: | Height: | Size: 479 B |
BIN
src/assets/img/icon/icon-delete.png
Normal file
|
After Width: | Height: | Size: 232 B |
BIN
src/assets/img/icon/icon-down.png
Normal file
|
After Width: | Height: | Size: 243 B |
BIN
src/assets/img/icon/icon-edit.png
Normal file
|
After Width: | Height: | Size: 381 B |
BIN
src/assets/img/icon/icon-pagination.png
Normal file
|
After Width: | Height: | Size: 941 B |
BIN
src/assets/img/icon/icon-profile.png
Normal file
|
After Width: | Height: | Size: 679 B |
BIN
src/assets/img/icon/icon-radio.png
Normal file
|
After Width: | Height: | Size: 410 B |
BIN
src/assets/img/icon/icon-reset.png
Normal file
|
After Width: | Height: | Size: 419 B |
BIN
src/assets/img/icon/icon-select.png
Normal file
|
After Width: | Height: | Size: 260 B |
BIN
src/assets/img/icon/icon-select2.png
Normal file
|
After Width: | Height: | Size: 252 B |
BIN
src/assets/img/icon/icon-tab.png
Normal file
|
After Width: | Height: | Size: 244 B |
BIN
src/assets/img/icon/icon-title.png
Normal file
|
After Width: | Height: | Size: 249 B |
BIN
src/assets/img/icon/icon-up.png
Normal file
|
After Width: | Height: | Size: 218 B |
1
src/assets/img/img.js
Normal file
@@ -0,0 +1 @@
|
||||
// 이미지 및 기타 common 파일을 관리하는 폴더입니다. 이 파일은 삭제하셔도 됩니다.
|
||||
BIN
src/assets/img/login-bg.png
Normal file
|
After Width: | Height: | Size: 1002 KiB |
BIN
src/assets/img/logo-main.png
Normal file
|
After Width: | Height: | Size: 3.3 KiB |
54
src/assets/img/logo-white.svg
Normal file
@@ -0,0 +1,54 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="205.252" height="18.971" viewBox="0 0 205.252 18.971">
|
||||
<defs>
|
||||
<clipPath id="clip-path">
|
||||
<rect id="사각형_21" data-name="사각형 21" width="205.252" height="18.97" fill="none"/>
|
||||
</clipPath>
|
||||
<clipPath id="clip-path-2">
|
||||
<rect id="사각형_20" data-name="사각형 20" width="205.252" height="18.971" fill="none"/>
|
||||
</clipPath>
|
||||
<clipPath id="clip-path-3">
|
||||
<rect id="사각형_15" data-name="사각형 15" height="0.018" fill="none"/>
|
||||
</clipPath>
|
||||
<clipPath id="clip-path-4">
|
||||
<rect id="사각형_18" data-name="사각형 18" width="0.014" fill="none"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
<g id="logo" clip-path="url(#clip-path)">
|
||||
<g id="그룹_26" data-name="그룹 26">
|
||||
<g id="그룹_25" data-name="그룹 25" clip-path="url(#clip-path-2)">
|
||||
<g id="그룹_18" data-name="그룹 18" transform="translate(52.923 14.805)" opacity="0.49">
|
||||
<g id="그룹_17" data-name="그룹 17">
|
||||
<g id="그룹_16" data-name="그룹 16" clip-path="url(#clip-path-3)">
|
||||
<path id="패스_33" data-name="패스 33" d="M124.094,34.722h0" transform="translate(-124.094 -34.713)" fill="#fff"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<path id="패스_34" data-name="패스 34" d="M437.325,34.705a.138.138,0,0,0,0,.014s0,.009,0,.013Z" transform="translate(-250.815 -19.903)" fill="#fff"/>
|
||||
<path id="패스_35" data-name="패스 35" d="M3.253,4.727v6.247a1.561,1.561,0,0,0,1.561,1.539H18.87v3.145H4.815A4.812,4.812,0,0,1,0,10.974V4.727A4.8,4.8,0,0,1,4.815.043H18.87V3.166H4.815A1.581,1.581,0,0,0,3.253,4.727" transform="translate(0 -0.025)" fill="#fff"/>
|
||||
<path id="패스_36" data-name="패스 36" d="M79.687,15.659H76.449L73.78,10.974H63.228l-2.666,4.685h-3.24L65.35,1.56A2.483,2.483,0,0,1,67.6.043H69.41A2.483,2.483,0,0,1,71.66,1.56ZM72,7.851,69.5,3.444a.507.507,0,0,0-.452-.277H67.963a.508.508,0,0,0-.452.277L65,7.851Z" transform="translate(-32.875 -0.025)" fill="#fff"/>
|
||||
<rect id="사각형_17" data-name="사각형 17" width="3.111" height="15.591" transform="translate(76.816 0.036)" fill="#fff"/>
|
||||
<g id="그룹_21" data-name="그룹 21" transform="translate(56.501 15.634)" opacity="0.49">
|
||||
<g id="그룹_20" data-name="그룹 20">
|
||||
<g id="그룹_19" data-name="그룹 19" clip-path="url(#clip-path-4)">
|
||||
<path id="패스_37" data-name="패스 37" d="M132.489,36.657h0" transform="translate(-132.483 -36.657)" fill="#fff"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g id="그룹_24" data-name="그룹 24" transform="translate(52.923 12.04)" opacity="0.49">
|
||||
<g id="그룹_23" data-name="그룹 23">
|
||||
<g id="그룹_22" data-name="그룹 22" clip-path="url(#clip-path-3)">
|
||||
<path id="패스_38" data-name="패스 38" d="M124.094,28.238h0" transform="translate(-124.094 -28.229)" fill="#fff"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<path id="패스_39" data-name="패스 39" d="M130,12.428h-2.691a1.211,1.211,0,0,1-1.211-1.211V.071h-3.245V12.1a3.584,3.584,0,0,0,3.578,3.575h15.264V12.427Z" transform="translate(-70.455 -0.041)" fill="#fff"/>
|
||||
<line id="선_1" data-name="선 1" x1="3.227" transform="translate(153.249 15.622)" fill="#fff"/>
|
||||
<path id="패스_40" data-name="패스 40" d="M338.858,10.089a3.566,3.566,0,0,0,2.6-3.2V3.4A3.584,3.584,0,0,0,337.979.053H322.564v15.6h3.11V10.233h10.019l.427.75,2.668,4.683h3.245ZM325.675,7.1V3.18h10.951A1.578,1.578,0,0,1,338.2,4.758v.775a1.577,1.577,0,0,1-1.577,1.578H325.675Z" transform="translate(-184.998 -0.03)" fill="#fff"/>
|
||||
<path id="패스_41" data-name="패스 41" d="M395.4,6.291H384.737a1.551,1.551,0,1,1,.039-3.1h14.062V.053H383.419A3.583,3.583,0,0,0,379.937,3.4V6.075a1.688,1.688,0,0,0,.026.213,3.575,3.575,0,0,0,3.416,3.132h10.663a1.551,1.551,0,1,1-.039,3.1H379.941v3.127h15.415a3.584,3.584,0,0,0,3.482-3.344V9.632a1.688,1.688,0,0,0-.026-.213A3.575,3.575,0,0,0,395.4,6.291" transform="translate(-217.903 -0.03)" fill="#fff"/>
|
||||
<path id="패스_42" data-name="패스 42" d="M270.271,3.164H284.3V3.132h0V0H269.09a3.523,3.523,0,0,0-3.5,3.132h.01a3.622,3.622,0,0,0-.035.47V12.02a3.622,3.622,0,0,0,.035.47h-.01a3.523,3.523,0,0,0,3.5,3.132h15.216V12.491h0V12.46H270.271a1.6,1.6,0,0,1-1.6-1.6V9.328h15.631V6.292H268.675V4.76a1.6,1.6,0,0,1,1.6-1.6" transform="translate(-152.307)" fill="#fff"/>
|
||||
<path id="패스_43" data-name="패스 43" d="M219.551,0h0L211.6,1.839,203.665,0h-3.143V8.638A1.8,1.8,0,0,0,201.15,10l10.457,8.966L222.065,10a1.8,1.8,0,0,0,.628-1.366V0ZM203.665.332l7.783,1.8V14.79l-7.783-6.672Zm8.1,14.458V2.131l7.783-1.8V8.118Z" transform="translate(-115.003 0.001)" fill="#fff"/>
|
||||
<path id="패스_44" data-name="패스 44" d="M442.031,3.164h14.032V3.132h0V0H440.85a3.523,3.523,0,0,0-3.5,3.132h.01a3.622,3.622,0,0,0-.035.47V12.02a3.633,3.633,0,0,0,.035.47h-.01a3.523,3.523,0,0,0,3.5,3.132h15.216V12.491h0V12.46H442.031a1.6,1.6,0,0,1-1.6-1.6V9.328h15.631V6.292H440.435V4.76a1.6,1.6,0,0,1,1.6-1.6" transform="translate(-250.815)" fill="#fff"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 5.1 KiB |
BIN
src/assets/img/logo.png
Normal file
|
After Width: | Height: | Size: 3.9 KiB |
198
src/components/DataManage/CreditLogSearchBar.js
Normal file
@@ -0,0 +1,198 @@
|
||||
import { styled } from 'styled-components';
|
||||
import { useState } from 'react';
|
||||
|
||||
import Button from '../../components/common/button/Button';
|
||||
|
||||
import DatePicker, { registerLocale } from 'react-datepicker';
|
||||
import 'react-datepicker/dist/react-datepicker.css';
|
||||
import { getMonth, getYear } from 'date-fns';
|
||||
import range from 'lodash/range';
|
||||
|
||||
import { TextInput, SelectInput, DatePickerWrapper, InputLabel, BtnWrapper } from '../../styles/Components';
|
||||
|
||||
const GoodsLogSearchBar = () => {
|
||||
const [startDate, setStartDate] = useState(new Date());
|
||||
const [endDate, setEndDate] = useState(new Date());
|
||||
const [selectData, setSelectData] = useState('default');
|
||||
const years = range(1990, getYear(new Date()) + 1, 1);
|
||||
const months = ['01', '02', '03', '04', '05', '06', '07', '08', '09', '10', '11', '12'];
|
||||
|
||||
const handleChange = e => {
|
||||
setSelectData(e.target.value);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<SearchbarStyle2>
|
||||
<SearchRow>
|
||||
<SearchItem>
|
||||
<InputLabel>조회 기간</InputLabel>
|
||||
<DatePickerWrapper>
|
||||
<InputGroup>
|
||||
<DatePicker
|
||||
selected={startDate}
|
||||
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>
|
||||
)}
|
||||
/>
|
||||
<SelectInput>
|
||||
<option value="">00</option>
|
||||
<option value="">01</option>
|
||||
</SelectInput>
|
||||
<SelectInput>
|
||||
<option value="">00</option>
|
||||
<option value="">01</option>
|
||||
</SelectInput>
|
||||
</InputGroup>
|
||||
<span>~</span>
|
||||
<InputGroup>
|
||||
<DatePicker
|
||||
selected={endDate}
|
||||
onChange={date => setEndDate(date)}
|
||||
className="datepicker"
|
||||
placeholderText="검색기간 선택"
|
||||
calendarClassName="calendar"
|
||||
dateFormat="yyyy - MM - dd"
|
||||
minDate = {startDate}
|
||||
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>
|
||||
)}
|
||||
/>
|
||||
<SelectInput>
|
||||
<option value="">00</option>
|
||||
<option value="">01</option>
|
||||
</SelectInput>
|
||||
<SelectInput>
|
||||
<option value="">00</option>
|
||||
<option value="">01</option>
|
||||
</SelectInput>
|
||||
</InputGroup>
|
||||
</DatePickerWrapper>
|
||||
</SearchItem>
|
||||
</SearchRow>
|
||||
<SearchRow>
|
||||
<SearchItem>
|
||||
<InputLabel>조회 대상</InputLabel>
|
||||
<TextInput type="text" placeholder="조회 대상 유저의 GUID를 입력하세요." width="600px" />
|
||||
</SearchItem>
|
||||
<BtnWrapper $gap="8px">
|
||||
<Button theme="reset" />
|
||||
<Button theme="gray" text="검색" />
|
||||
</BtnWrapper>
|
||||
</SearchRow>
|
||||
</SearchbarStyle2>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default GoodsLogSearchBar;
|
||||
|
||||
const SearchbarStyle = styled.div`
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 20px 0;
|
||||
font-size: 14px;
|
||||
padding: 20px;
|
||||
border-top: 1px solid #000;
|
||||
border-bottom: 1px solid #000;
|
||||
margin-bottom: 40px;
|
||||
`;
|
||||
const SearchbarStyle2 = styled(SearchbarStyle)`
|
||||
flex-flow: column;
|
||||
gap: 20px;
|
||||
`;
|
||||
|
||||
const SearchRow = styled.div`
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 20px 0;
|
||||
`;
|
||||
|
||||
const InputGroup = styled.div`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
`;
|
||||
|
||||
const SearchItem = styled.div`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 20px;
|
||||
margin-right: 50px;
|
||||
|
||||
${TextInput}, ${SelectInput} {
|
||||
height: 35px;
|
||||
}
|
||||
${TextInput} {
|
||||
padding: 0 10px;
|
||||
max-width: 400px;
|
||||
}
|
||||
`;
|
||||
219
src/components/DataManage/ItemLogSearchBar.js
Normal file
@@ -0,0 +1,219 @@
|
||||
import { useState } from 'react';
|
||||
import { styled } from 'styled-components';
|
||||
|
||||
import RadioInput from '../common/input/Radio';
|
||||
import Button from '../common/button/Button';
|
||||
|
||||
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 { TextInput, SelectInput, DatePickerWrapper, InputLabel, BtnWrapper } from '../../styles/Components';
|
||||
|
||||
const ItemLogSearchBar = () => {
|
||||
const [startDate, setStartDate] = useState(new Date());
|
||||
const [endDate, setEndDate] = useState(new Date());
|
||||
const [selectData, setSelectData] = useState('default');
|
||||
const years = range(1990, getYear(new Date()) + 1, 1);
|
||||
const months = ['01', '02', '03', '04', '05', '06', '07', '08', '09', '10', '11', '12'];
|
||||
|
||||
const handleChange = e => {
|
||||
setSelectData(e.target.value);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<SearchbarStyle2>
|
||||
<SearchRow>
|
||||
<RadioGroup>
|
||||
<RadioInput label="기본 조회" id="single" name="receiver" value="default" fontWeight="600" checked={selectData === 'default'} handleChange={handleChange} />
|
||||
<RadioInput label="아이템 소유자 추적" id="multi" name="receiver" value="item" fontWeight="600" checked={selectData === 'item'} handleChange={handleChange} />
|
||||
</RadioGroup>
|
||||
</SearchRow>
|
||||
<SearchRow>
|
||||
<SearchItem>
|
||||
<InputLabel>조회 기간</InputLabel>
|
||||
<DatePickerWrapper>
|
||||
<InputGroup>
|
||||
<DatePicker
|
||||
selected={startDate}
|
||||
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>
|
||||
)}
|
||||
/>
|
||||
<SelectInput>
|
||||
<option value="">00</option>
|
||||
<option value="">01</option>
|
||||
</SelectInput>
|
||||
<SelectInput>
|
||||
<option value="">00</option>
|
||||
<option value="">01</option>
|
||||
</SelectInput>
|
||||
</InputGroup>
|
||||
<span>~</span>
|
||||
<InputGroup>
|
||||
<DatePicker
|
||||
selected={endDate}
|
||||
onChange={date => setEndDate(date)}
|
||||
className="datepicker"
|
||||
placeholderText="검색기간 선택"
|
||||
calendarClassName="calendar"
|
||||
dateFormat="yyyy - MM - dd"
|
||||
minDate = {startDate}
|
||||
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>
|
||||
)}
|
||||
/>
|
||||
<SelectInput>
|
||||
<option value="">00</option>
|
||||
<option value="">01</option>
|
||||
</SelectInput>
|
||||
<SelectInput>
|
||||
<option value="">00</option>
|
||||
<option value="">01</option>
|
||||
</SelectInput>
|
||||
</InputGroup>
|
||||
</DatePickerWrapper>
|
||||
</SearchItem>
|
||||
</SearchRow>
|
||||
<SearchRow>
|
||||
{selectData === 'default' ? (
|
||||
<SearchItem>
|
||||
<InputLabel>조회 대상</InputLabel>
|
||||
<TextInput type="text" placeholder="조회 대상 유저의 GUID를 입력하세요." width="600px" />
|
||||
</SearchItem>
|
||||
) : (
|
||||
<SearchItem>
|
||||
<InputLabel>아이템 ID</InputLabel>
|
||||
<TextInput type="text" placeholder="아이템의 GUID를 입력하세요." width="600px" />
|
||||
</SearchItem>
|
||||
)}
|
||||
|
||||
<BtnWrapper $gap="8px">
|
||||
<Button theme="reset" />
|
||||
<Button theme="gray" text="검색" />
|
||||
</BtnWrapper>
|
||||
</SearchRow>
|
||||
</SearchbarStyle2>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default ItemLogSearchBar;
|
||||
|
||||
const SearchbarStyle = styled.div`
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 20px 0;
|
||||
font-size: 14px;
|
||||
padding: 20px;
|
||||
border-top: 1px solid #000;
|
||||
border-bottom: 1px solid #000;
|
||||
margin-bottom: 40px;
|
||||
`;
|
||||
const SearchbarStyle2 = styled(SearchbarStyle)`
|
||||
flex-flow: column;
|
||||
gap: 20px;
|
||||
`;
|
||||
|
||||
const SearchRow = styled.div`
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 20px 0;
|
||||
`;
|
||||
|
||||
const InputGroup = styled.div`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
`;
|
||||
|
||||
const RadioGroup = styled(InputGroup)`
|
||||
gap: 30px;
|
||||
height: 35px;
|
||||
`;
|
||||
|
||||
const SearchItem = styled.div`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 20px;
|
||||
margin-right: 50px;
|
||||
|
||||
${TextInput}, ${SelectInput} {
|
||||
height: 35px;
|
||||
}
|
||||
${TextInput} {
|
||||
padding: 0 10px;
|
||||
max-width: 400px;
|
||||
}
|
||||
`;
|
||||
79
src/components/DataManage/LandDetailModal.js
Normal file
@@ -0,0 +1,79 @@
|
||||
import { styled } from 'styled-components';
|
||||
|
||||
import { Title } from '../../styles/Components';
|
||||
import { BtnWrapper, TableStyle } from '../../styles/Components';
|
||||
import Button from '../../components/common/button/Button';
|
||||
import Modal from '../../components/common/modal/Modal';
|
||||
import { Fragment } from 'react';
|
||||
|
||||
const LandDetailModal = ({ detailPop, handleClick }) => {
|
||||
const landlist = [
|
||||
{ floor: '1', instanceId: 'ad31230001' },
|
||||
{ floor: '2', instanceId: 'ad31230001' },
|
||||
];
|
||||
return (
|
||||
<>
|
||||
<Modal $view={detailPop} min="480px">
|
||||
<Title $align="center">랜드 상세정보</Title>
|
||||
<TableWrapper>
|
||||
<TableStyle>
|
||||
<thead>
|
||||
<tr>
|
||||
<th width="100">층 정보</th>
|
||||
<th>인스턴스 연결 정보</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{landlist.map((el, index) => {
|
||||
return (
|
||||
<Fragment key={index}>
|
||||
<tr>
|
||||
<td>{el.floor}</td>
|
||||
<InstanceData>{el.instanceId}</InstanceData>
|
||||
</tr>
|
||||
</Fragment>
|
||||
);
|
||||
})}
|
||||
</tbody>
|
||||
</TableStyle>
|
||||
</TableWrapper>
|
||||
<BtnWrapper2 $justify="center">
|
||||
<Button
|
||||
theme="line"
|
||||
text="확인"
|
||||
handleClick={e => {
|
||||
e.preventDefault();
|
||||
handleClick();
|
||||
}}
|
||||
/>
|
||||
</BtnWrapper2>
|
||||
</Modal>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default LandDetailModal;
|
||||
|
||||
const InstanceData = styled.td``;
|
||||
|
||||
const TableWrapper = styled.div`
|
||||
max-height: 50vh;
|
||||
max-width: 600px;
|
||||
overflow: auto;
|
||||
${InstanceData} {
|
||||
text-align: left;
|
||||
}
|
||||
&::-webkit-scrollbar {
|
||||
width: 4px;
|
||||
}
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background: #666666;
|
||||
}
|
||||
&::-webkit-scrollbar-track {
|
||||
background: #d9d9d9;
|
||||
}
|
||||
`;
|
||||
|
||||
const BtnWrapper2 = styled(BtnWrapper)`
|
||||
margin-top: 30px;
|
||||
`;
|
||||
57
src/components/DataManage/LandSearchBar.js
Normal file
@@ -0,0 +1,57 @@
|
||||
import { styled } from 'styled-components';
|
||||
import Button from '../../components/common/button/Button';
|
||||
|
||||
import { FormWrapper, InputLabel, TextInput, SelectInput, BtnWrapper } from '../../styles/Components';
|
||||
|
||||
const LandSearchBar = () => {
|
||||
return (
|
||||
<>
|
||||
<FormWrapper>
|
||||
<SearchbarStyle>
|
||||
<SearchItem>
|
||||
<InputLabel>랜드 조회</InputLabel>
|
||||
<TextInput type="text" width="300px" placeholder="랜드 ID를 입력하세요." />
|
||||
</SearchItem>
|
||||
<BtnWrapper $gap="8px">
|
||||
<Button theme="reset" />
|
||||
<Button
|
||||
theme="gray"
|
||||
text="검색"
|
||||
handleClick={e => {
|
||||
e.preventDefault();
|
||||
}}
|
||||
/>
|
||||
</BtnWrapper>
|
||||
</SearchbarStyle>
|
||||
</FormWrapper>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default LandSearchBar;
|
||||
|
||||
const SearchbarStyle = styled.div`
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 20px 0;
|
||||
font-size: 14px;
|
||||
padding: 20px;
|
||||
border-top: 1px solid #000;
|
||||
border-bottom: 1px solid #000;
|
||||
margin-bottom: 40px;
|
||||
`;
|
||||
const SearchItem = styled.div`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 20px;
|
||||
margin-right: 50px;
|
||||
|
||||
${TextInput}, ${SelectInput} {
|
||||
height: 35px;
|
||||
}
|
||||
${TextInput} {
|
||||
padding: 0 10px;
|
||||
max-width: 400px;
|
||||
}
|
||||
`;
|
||||
141
src/components/DataManage/MailDetailModal.js
Normal file
@@ -0,0 +1,141 @@
|
||||
import { styled } from 'styled-components';
|
||||
import Button from '../../components/common/button/Button';
|
||||
import Modal from '../common/modal/Modal';
|
||||
|
||||
import { TextInput, BtnWrapper} from '../../styles/Components';
|
||||
import { Textarea, Title } from '../../styles/Components';
|
||||
import CDivider from '../common/CDivider';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import IconDelete from '../../assets/img/icon/icon-delete.png';
|
||||
|
||||
const MailDetailModal = ({ mailModal, handleClick, setDetail, content, handleDelete, handleItemDelete, authDelete }) => {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<>
|
||||
<Modal $view={mailModal}>
|
||||
<Title $align="center">우편 상세 정보</Title>
|
||||
<MailDetailTable>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th width="120">제목</th>
|
||||
<td>
|
||||
<TextInput defaultValue={content.title}></TextInput>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>내용</th>
|
||||
<td>
|
||||
<Textarea defaultValue={content.content}></Textarea>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>아이템 첨부</th>
|
||||
<td>
|
||||
<div>
|
||||
{content.item_list && (
|
||||
<ItemList>
|
||||
{content &&
|
||||
content.item_list.map((data, index) => {
|
||||
return (
|
||||
<Item key={index}>
|
||||
<span>
|
||||
{data.item_name}({data.count})
|
||||
</span>
|
||||
<BtnDelete onClick={() => handleItemDelete(data)} disabled={!authDelete}></BtnDelete>
|
||||
{content && content.is_reserve === false}
|
||||
</Item>
|
||||
);
|
||||
})}
|
||||
</ItemList>
|
||||
)}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</MailDetailTable>
|
||||
<BtnWrapper $justify="center">
|
||||
<Button
|
||||
theme={authDelete ? "line" : "disable"}
|
||||
text="삭제"
|
||||
handleClick={handleDelete}
|
||||
disabled={!authDelete}
|
||||
/>
|
||||
<CDivider />
|
||||
<Button
|
||||
theme="line"
|
||||
text="확인"
|
||||
handleClick={e => {
|
||||
e.preventDefault();
|
||||
handleClick();
|
||||
setDetail({ title: '', content: '', item_list: [] });
|
||||
}}
|
||||
/>
|
||||
</BtnWrapper>
|
||||
</Modal>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default MailDetailModal;
|
||||
|
||||
const MailDetailTable = styled.table`
|
||||
max-width: 800px;
|
||||
margin-bottom: 30px;
|
||||
tr:first-child {
|
||||
th,
|
||||
td {
|
||||
border-top: 1px solid #000;
|
||||
}
|
||||
}
|
||||
th {
|
||||
font-weight: 700;
|
||||
vertical-align: top;
|
||||
line-height: 30px;
|
||||
}
|
||||
th,
|
||||
td {
|
||||
padding: 15px;
|
||||
border-bottom: 1px solid #d9d9d9;
|
||||
}
|
||||
tr:last-child {
|
||||
th,
|
||||
td {
|
||||
border-bottom: 1px solid #000;
|
||||
}
|
||||
}
|
||||
td {
|
||||
textarea {
|
||||
border: 1px solid #e0e0e0;
|
||||
width: 100%;
|
||||
border-radius: 5px;
|
||||
min-height: 200px;
|
||||
&::-webkit-scrollbar {
|
||||
width: 4px;
|
||||
}
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background: #666666;
|
||||
}
|
||||
&::-webkit-scrollbar-track {
|
||||
background: #d9d9d9;
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const ItemList = styled.ul`
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
padding: 10px;
|
||||
flex-wrap: wrap;
|
||||
`;
|
||||
|
||||
const Item = styled.li`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
`;
|
||||
|
||||
const BtnDelete = styled.button`
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
background: url(${IconDelete}) 50% 50% no-repeat;
|
||||
`;
|
||||
129
src/components/DataManage/NicknameChangeModal.js
Normal file
@@ -0,0 +1,129 @@
|
||||
import { styled } from 'styled-components';
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
import { Title } from '../../styles/Components';
|
||||
import { TextInput, BtnWrapper, ButtonClose, ModalText } from '../../styles/Components';
|
||||
import Button from '../../components/common/button/Button';
|
||||
import Modal from '../../components/common/modal/Modal';
|
||||
import { UserChangeNickName } from '../../apis';
|
||||
|
||||
const NicknameChangeModal = ({ pwPop, handleClick, dataList }) => {
|
||||
let nickName = dataList.char_info && dataList.char_info.character_name;
|
||||
const [modifyModal, setModifyModal] = useState('hidden');
|
||||
const [completeModal, setCompleteModal] = useState('hidden');
|
||||
const [completeText, setCompleteText] = useState('');
|
||||
|
||||
const [resultData, setResultData] = useState({
|
||||
guid: '',
|
||||
nickname: '',
|
||||
new_nickname: '',
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
setResultData({ ...resultData, guid: dataList.user_info && dataList.user_info.aid, nickname: dataList.char_info && dataList.char_info.character_name });
|
||||
}, [dataList]);
|
||||
|
||||
// 수정 모달창
|
||||
const handleModifyModal = () => {
|
||||
if (modifyModal === 'hidden') {
|
||||
setModifyModal('view');
|
||||
} else {
|
||||
setModifyModal('hidden');
|
||||
}
|
||||
};
|
||||
|
||||
// 완료 모달창
|
||||
const handleCompleteModal = () => {
|
||||
if (completeModal === 'hidden') {
|
||||
setCompleteModal('view');
|
||||
} else {
|
||||
setCompleteModal('hidden');
|
||||
|
||||
handleClick();
|
||||
completeText === '변경이 완료되었습니다.' && window.location.reload();
|
||||
}
|
||||
};
|
||||
|
||||
// 수정
|
||||
const handleModifyNotice = async () => {
|
||||
const token = sessionStorage.getItem('token');
|
||||
|
||||
const message = await UserChangeNickName(token, resultData);
|
||||
|
||||
// console.log(message);
|
||||
message.data.data.message !== '수정 하였습니다.' ? setCompleteText('변경 닉네임이 이미 존재합니다.\n다시 시도해주세요.') : setCompleteText('변경이 완료되었습니다.');
|
||||
|
||||
handleCompleteModal();
|
||||
handleModifyModal();
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Modal $view={pwPop} min="480px">
|
||||
<Title $align="center">닉네임 변경</Title>
|
||||
<PwSetTable>
|
||||
<colgroup>
|
||||
<col width="120" />
|
||||
<col />
|
||||
</colgroup>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>기존 닉네임</th>
|
||||
<td>{nickName}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>변경 닉네임</th>
|
||||
<td>
|
||||
<TextInput placeholder="닉네임을 입력하세요." onChange={e => setResultData({ ...resultData, new_nickname: e.target.value })} maxLength={12} />
|
||||
<ChangeNotice>* 최대 12글자까지 가능합니다.</ChangeNotice>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</PwSetTable>
|
||||
<BtnWrapper $justify="center" $gap="10px">
|
||||
<Button theme="line" text="취소" handleClick={handleClick} />
|
||||
<Button theme="primary" text="변경하기" handleClick={handleModifyModal} />
|
||||
</BtnWrapper>
|
||||
</Modal>
|
||||
|
||||
{/* 확인 모달 */}
|
||||
<Modal min="440px" $padding="40px" $bgcolor="transparent" $view={modifyModal}>
|
||||
<BtnWrapper $justify="flex-end">
|
||||
<ButtonClose onClick={handleModifyModal} />
|
||||
</BtnWrapper>
|
||||
<ModalText $align="center">닉네임을 변경하시겠습니까?</ModalText>
|
||||
<BtnWrapper $gap="10px">
|
||||
<Button text="취소" theme="line" size="large" width="100%" handleClick={handleModifyModal} />
|
||||
<Button text="확인" theme="primary" type="submit" size="large" width="100%" handleClick={handleModifyNotice} />
|
||||
</BtnWrapper>
|
||||
</Modal>
|
||||
{/* 완료 모달 */}
|
||||
<Modal min="440px" $padding="40px" $bgcolor="transparent" $view={completeModal}>
|
||||
<BtnWrapper $justify="flex-end">
|
||||
<ButtonClose onClick={handleCompleteModal} />
|
||||
</BtnWrapper>
|
||||
<ModalText $align="center">{completeText}</ModalText>
|
||||
<BtnWrapper $gap="10px">
|
||||
<Button text="확인" theme="primary" type="submit" size="large" width="100%" handleClick={handleCompleteModal} />
|
||||
</BtnWrapper>
|
||||
</Modal>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default NicknameChangeModal;
|
||||
const PwSetTable = styled.table`
|
||||
width: 400px;
|
||||
margin: 30px 0;
|
||||
th,
|
||||
td {
|
||||
padding: 10px 0;
|
||||
}
|
||||
`;
|
||||
const ChangeNotice = styled.span`
|
||||
font-size: 12px;
|
||||
font-weight: 300;
|
||||
color: ${props => props.$color || '#999'};
|
||||
margin-top: 10px;
|
||||
display: block;
|
||||
`;
|
||||
80
src/components/DataManage/QuestDetailModal.js
Normal file
@@ -0,0 +1,80 @@
|
||||
import { styled } from 'styled-components';
|
||||
|
||||
import { Title } from '../../styles/Components';
|
||||
import { BtnWrapper, TableStyle } from '../../styles/Components';
|
||||
import Button from '../../components/common/button/Button';
|
||||
import Modal from '../../components/common/modal/Modal';
|
||||
import { useEffect, useState, Fragment } from 'react';
|
||||
|
||||
const QuestDetailModal = ({ detailPop, handleClick, detailQuest }) => {
|
||||
const [detailList, setDetailList] = useState([])
|
||||
|
||||
useEffect(() => {
|
||||
Array.isArray(detailQuest) && setDetailList(detailQuest)
|
||||
|
||||
}, [detailQuest])
|
||||
// const questlist = [{ taskNo: detailQuest.task_no, taskName: detailQuest.quest_name, counter: detailQuest.counter, state: detailQuest.status }];
|
||||
return (
|
||||
<>
|
||||
<Modal $view={detailPop} min="480px">
|
||||
<Title $align="center">퀘스트 상세정보</Title>
|
||||
<TableWrapper>
|
||||
<TableStyle>
|
||||
<thead>
|
||||
<tr>
|
||||
<th width="80">Task No</th>
|
||||
<th>Task Name</th>
|
||||
<th width="120">Counter</th>
|
||||
<th width="120">State</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{detailList && detailList.map((el, index) => {
|
||||
return (
|
||||
<Fragment key={index}>
|
||||
<tr>
|
||||
<td>{el.task_no}</td>
|
||||
<td>{el.quest_name}</td>
|
||||
<td>{el.counter}</td>
|
||||
<td>{el.status}</td>
|
||||
</tr>
|
||||
</Fragment>
|
||||
);
|
||||
})}
|
||||
</tbody>
|
||||
</TableStyle>
|
||||
</TableWrapper>
|
||||
<BtnWrapper2 $justify="center">
|
||||
<Button
|
||||
theme="line"
|
||||
text="확인"
|
||||
handleClick={e => {
|
||||
e.preventDefault();
|
||||
handleClick();
|
||||
}}
|
||||
/>
|
||||
</BtnWrapper2>
|
||||
</Modal>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default QuestDetailModal;
|
||||
|
||||
const TableWrapper = styled.div`
|
||||
max-height: 50vh;
|
||||
overflow: auto;
|
||||
&::-webkit-scrollbar {
|
||||
width: 4px;
|
||||
}
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background: #666666;
|
||||
}
|
||||
&::-webkit-scrollbar-track {
|
||||
background: #d9d9d9;
|
||||
}
|
||||
`;
|
||||
|
||||
const BtnWrapper2 = styled(BtnWrapper)`
|
||||
margin-top: 30px;
|
||||
`;
|
||||
198
src/components/DataManage/TradeLogSearchBar.js
Normal file
@@ -0,0 +1,198 @@
|
||||
import { styled } from 'styled-components';
|
||||
import { useState } from 'react';
|
||||
|
||||
import Button from '../../components/common/button/Button';
|
||||
|
||||
import DatePicker, { registerLocale } from 'react-datepicker';
|
||||
import 'react-datepicker/dist/react-datepicker.css';
|
||||
import { getMonth, getYear } from 'date-fns';
|
||||
import range from 'lodash/range';
|
||||
|
||||
import { TextInput, SelectInput, DatePickerWrapper, InputLabel, BtnWrapper } from '../../styles/Components';
|
||||
|
||||
const TradeLogSerchBar = () => {
|
||||
const [startDate, setStartDate] = useState(new Date());
|
||||
const [endDate, setEndDate] = useState(new Date());
|
||||
const [selectData, setSelectData] = useState('default');
|
||||
const years = range(1990, getYear(new Date()) + 1, 1);
|
||||
const months = ['01', '02', '03', '04', '05', '06', '07', '08', '09', '10', '11', '12'];
|
||||
|
||||
const handleChange = e => {
|
||||
setSelectData(e.target.value);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<SearchbarStyle2>
|
||||
<SearchRow>
|
||||
<SearchItem>
|
||||
<InputLabel>조회 기간</InputLabel>
|
||||
<DatePickerWrapper>
|
||||
<InputGroup>
|
||||
<DatePicker
|
||||
selected={startDate}
|
||||
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>
|
||||
)}
|
||||
/>
|
||||
<SelectInput>
|
||||
<option value="">00</option>
|
||||
<option value="">01</option>
|
||||
</SelectInput>
|
||||
<SelectInput>
|
||||
<option value="">00</option>
|
||||
<option value="">01</option>
|
||||
</SelectInput>
|
||||
</InputGroup>
|
||||
<span>~</span>
|
||||
<InputGroup>
|
||||
<DatePicker
|
||||
selected={endDate}
|
||||
onChange={date => setEndDate(date)}
|
||||
className="datepicker"
|
||||
placeholderText="검색기간 선택"
|
||||
calendarClassName="calendar"
|
||||
dateFormat="yyyy - MM - dd"
|
||||
minDate = {startDate}
|
||||
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>
|
||||
)}
|
||||
/>
|
||||
<SelectInput>
|
||||
<option value="">00</option>
|
||||
<option value="">01</option>
|
||||
</SelectInput>
|
||||
<SelectInput>
|
||||
<option value="">00</option>
|
||||
<option value="">01</option>
|
||||
</SelectInput>
|
||||
</InputGroup>
|
||||
</DatePickerWrapper>
|
||||
</SearchItem>
|
||||
</SearchRow>
|
||||
<SearchRow>
|
||||
<SearchItem>
|
||||
<InputLabel>조회 대상</InputLabel>
|
||||
<TextInput type="text" placeholder="조회 대상 유저의 GUID를 입력하세요." width="600px" />
|
||||
</SearchItem>
|
||||
<BtnWrapper $gap="8px">
|
||||
<Button theme="reset" />
|
||||
<Button theme="gray" text="검색" />
|
||||
</BtnWrapper>
|
||||
</SearchRow>
|
||||
</SearchbarStyle2>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default TradeLogSerchBar;
|
||||
|
||||
const SearchbarStyle = styled.div`
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 20px 0;
|
||||
font-size: 14px;
|
||||
padding: 20px;
|
||||
border-top: 1px solid #000;
|
||||
border-bottom: 1px solid #000;
|
||||
margin-bottom: 40px;
|
||||
`;
|
||||
const SearchbarStyle2 = styled(SearchbarStyle)`
|
||||
flex-flow: column;
|
||||
gap: 20px;
|
||||
`;
|
||||
|
||||
const SearchRow = styled.div`
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 20px 0;
|
||||
`;
|
||||
|
||||
const InputGroup = styled.div`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
`;
|
||||
|
||||
const SearchItem = styled.div`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 20px;
|
||||
margin-right: 50px;
|
||||
|
||||
${TextInput}, ${SelectInput} {
|
||||
height: 35px;
|
||||
}
|
||||
${TextInput} {
|
||||
padding: 0 10px;
|
||||
max-width: 400px;
|
||||
}
|
||||
`;
|
||||
672
src/components/DataManage/UserAvatarInfo.js
Normal file
@@ -0,0 +1,672 @@
|
||||
import styled from 'styled-components';
|
||||
import { useState, useEffect } from 'react';
|
||||
import { UserAvatarView } from '../../apis/Users';
|
||||
import { TableSkeleton } from '../Skeleton/TableSkeleton';
|
||||
|
||||
const UserAvatarInfo = ({ userInfo }) => {
|
||||
const [dataList, setDataList] = useState();
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
fetchData();
|
||||
}, []);
|
||||
|
||||
const fetchData = async () => {
|
||||
const token = sessionStorage.getItem('token');
|
||||
await UserAvatarView(token, userInfo.guid).then(data => {
|
||||
setDataList(data);
|
||||
setLoading(false);
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
loading ? <TableSkeleton width='25%' count={20}/> :
|
||||
<>
|
||||
<Notice>* 아바타 항목의 모든 항목은 ID 및 코드로 노출됩니다.</Notice>
|
||||
<AvatarWrapper>
|
||||
<UserInfoTable $maxwidth="600px">
|
||||
<colgroup>
|
||||
<col width="120" />
|
||||
<col width="30%" />
|
||||
<col width="120" />
|
||||
<col width="30%" />
|
||||
</colgroup>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>아바타 ID</th>
|
||||
<td colSpan="3">{dataList && dataList.avatar_info.character_id}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>기본 외형</th>
|
||||
<td colSpan="3">{dataList && dataList.avatar_info.basicstyle}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>체형 타입</th>
|
||||
<td colSpan="3">{dataList && dataList.avatar_info.bodyshape}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>헤어스타일</th>
|
||||
<td>{dataList && dataList.avatar_info.hairstyle}</td>
|
||||
<th>헤어 컬러</th>
|
||||
<td>{dataList && dataList.avatar_info.haircolor}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>나이</th>
|
||||
<td colSpan="3">{dataList && dataList.avatar_info.age}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</UserInfoTable>
|
||||
|
||||
<UserFaceInfoTable $maxwidth="600px">
|
||||
<colgroup>
|
||||
<col width="120" />
|
||||
<col width="100%" />
|
||||
</colgroup>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>얼굴 윤곽</th>
|
||||
<td>
|
||||
<UserFaceDetailInfoTable>
|
||||
<colgroup>
|
||||
<col width="120" />
|
||||
<col width="100%" />
|
||||
</colgroup>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>얼굴 길이</th>
|
||||
<td>{dataList && dataList.avatar_info.facesCustomizing[0]}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>이마 돌출</th>
|
||||
<td>{dataList && dataList.avatar_info.facesCustomizing[1]}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>볼 라인</th>
|
||||
<td>{dataList && dataList.avatar_info.facesCustomizing[2]}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>광대 좌우</th>
|
||||
<td>{dataList && dataList.avatar_info.facesCustomizing[3]}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>광대 상하</th>
|
||||
<td>{dataList && dataList.avatar_info.facesCustomizing[4]}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>광대 돌출</th>
|
||||
<td>{dataList && dataList.avatar_info.facesCustomizing[5]}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</UserFaceDetailInfoTable>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</UserFaceInfoTable>
|
||||
<UserFaceInfoTable $maxwidth="600px">
|
||||
<colgroup>
|
||||
<col width="120" />
|
||||
<col width="100%" />
|
||||
</colgroup>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>턱</th>
|
||||
<td>
|
||||
<UserFaceDetailInfoTable>
|
||||
<colgroup>
|
||||
<col width="120" />
|
||||
<col width="100%" />
|
||||
</colgroup>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>앞턱 넓이 좌우</th>
|
||||
<td>{dataList && dataList.avatar_info.facesCustomizing[6]}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>앞턱 상하</th>
|
||||
<td>{dataList && dataList.avatar_info.facesCustomizing[7]}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>앞턱 전후</th>
|
||||
<td>{dataList && dataList.avatar_info.facesCustomizing[8]}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>뒷턱 돌출</th>
|
||||
<td>{dataList && dataList.avatar_info.facesCustomizing[9]}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>뒷턱 상하</th>
|
||||
<td>{dataList && dataList.avatar_info.facesCustomizing[10]}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>뒷턱 넓이 좌우</th>
|
||||
<td>{dataList && dataList.avatar_info.facesCustomizing[11]}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</UserFaceDetailInfoTable>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</UserFaceInfoTable>
|
||||
<UserFaceInfoTable $maxwidth="600px">
|
||||
<colgroup>
|
||||
<col width="120" />
|
||||
<col width="100%" />
|
||||
</colgroup>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>눈</th>
|
||||
<td>
|
||||
<UserFaceDetailInfoTable>
|
||||
<colgroup>
|
||||
<col width="120" />
|
||||
<col width="100%" />
|
||||
</colgroup>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>눈 사이 간격</th>
|
||||
<td>{dataList && dataList.avatar_info.facesCustomizing[12]}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>눈 높이 상하</th>
|
||||
<td>{dataList && dataList.avatar_info.facesCustomizing[13]}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>눈 각도</th>
|
||||
<td>{dataList && dataList.avatar_info.facesCustomizing[14]}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>눈 크기</th>
|
||||
<td>{dataList && dataList.avatar_info.facesCustomizing[15]}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</UserFaceDetailInfoTable>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</UserFaceInfoTable>
|
||||
<UserFaceInfoTable $maxwidth="600px">
|
||||
<colgroup>
|
||||
<col width="120" />
|
||||
<col width="100%" />
|
||||
</colgroup>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>눈썹</th>
|
||||
<td>
|
||||
<UserFaceDetailInfoTable>
|
||||
<colgroup>
|
||||
<col width="120" />
|
||||
<col width="100%" />
|
||||
</colgroup>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>눈썹뼈 돌출</th>
|
||||
<td>{dataList && dataList.avatar_info.facesCustomizing[16]}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>넓이</th>
|
||||
<td>{dataList && dataList.avatar_info.facesCustomizing[17]}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>높이</th>
|
||||
<td>{dataList && dataList.avatar_info.facesCustomizing[18]}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>각도</th>
|
||||
<td>{dataList && dataList.avatar_info.facesCustomizing[19]}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>두께</th>
|
||||
<td>{dataList && dataList.avatar_info.facesCustomizing[20]}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</UserFaceDetailInfoTable>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</UserFaceInfoTable>
|
||||
<UserFaceInfoTable $maxwidth="600px">
|
||||
<colgroup>
|
||||
<col width="120" />
|
||||
<col width="100%" />
|
||||
</colgroup>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>코</th>
|
||||
<td>
|
||||
<UserFaceDetailInfoTable>
|
||||
<colgroup>
|
||||
<col width="120" />
|
||||
<col width="100%" />
|
||||
</colgroup>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>코 높이 상하</th>
|
||||
<td>{dataList && dataList.avatar_info.facesCustomizing[21]}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>콧대 돌출</th>
|
||||
<td>{dataList && dataList.avatar_info.facesCustomizing[22]}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>코볼</th>
|
||||
<td>{dataList && dataList.avatar_info.facesCustomizing[23]}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</UserFaceDetailInfoTable>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</UserFaceInfoTable>
|
||||
|
||||
<UserFaceInfoTable $maxwidth="600px">
|
||||
<colgroup>
|
||||
<col width="120" />
|
||||
<col width="100%" />
|
||||
</colgroup>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>입</th>
|
||||
<td>
|
||||
<UserFaceDetailInfoTable>
|
||||
<colgroup>
|
||||
<col width="120" />
|
||||
<col width="100%" />
|
||||
</colgroup>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>상하</th>
|
||||
<td>{dataList && dataList.avatar_info.facesCustomizing[24]}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>넓이 좌우</th>
|
||||
<td>{dataList && dataList.avatar_info.facesCustomizing[25]}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>윗입술 두께</th>
|
||||
<td>{dataList && dataList.avatar_info.facesCustomizing[26]}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>아랫입술 두께</th>
|
||||
<td>{dataList && dataList.avatar_info.facesCustomizing[27]}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>윗입술 넓이</th>
|
||||
<td>{dataList && dataList.avatar_info.facesCustomizing[28]}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>아랫입술 넓이</th>
|
||||
<td>{dataList && dataList.avatar_info.facesCustomizing[29]}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</UserFaceDetailInfoTable>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</UserFaceInfoTable>
|
||||
<UserFaceInfoTable $maxwidth="600px">
|
||||
<colgroup>
|
||||
<col width="120" />
|
||||
<col width="100%" />
|
||||
</colgroup>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>귀</th>
|
||||
<td>
|
||||
<UserFaceDetailInfoTable>
|
||||
<colgroup>
|
||||
<col width="120" />
|
||||
<col width="100%" />
|
||||
</colgroup>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>뾰족귀(엘프귀)</th>
|
||||
<td>{dataList && dataList.avatar_info.facesCustomizing[30]}</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<th>귓볼(부처귀)</th>
|
||||
<td>{dataList && dataList.avatar_info.facesCustomizing[31]}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>귀 각도</th>
|
||||
<td>{dataList && dataList.avatar_info.facesCustomizing[32]}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</UserFaceDetailInfoTable>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</UserFaceInfoTable>
|
||||
<UserFaceInfoTable $maxwidth="600px">
|
||||
<colgroup>
|
||||
<col width="120" />
|
||||
<col width="100%" />
|
||||
</colgroup>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>헤어 루트컬러</th>
|
||||
<td>
|
||||
<UserFaceDetailInfoTable>
|
||||
<colgroup>
|
||||
<col width="120" />
|
||||
<col width="100%" />
|
||||
</colgroup>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>RED</th>
|
||||
<td>{dataList && dataList.avatar_info.facesCustomizing[33]}</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<th>GREEN</th>
|
||||
<td>{dataList && dataList.avatar_info.facesCustomizing[34]}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>BLUE</th>
|
||||
<td>{dataList && dataList.avatar_info.facesCustomizing[35]}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</UserFaceDetailInfoTable>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</UserFaceInfoTable>
|
||||
<UserFaceInfoTable $maxwidth="600px">
|
||||
<colgroup>
|
||||
<col width="120" />
|
||||
<col width="100%" />
|
||||
</colgroup>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>헤어 팁 컬러</th>
|
||||
<td>
|
||||
<UserFaceDetailInfoTable>
|
||||
<colgroup>
|
||||
<col width="120" />
|
||||
<col width="100%" />
|
||||
</colgroup>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>RED</th>
|
||||
<td>{dataList && dataList.avatar_info.facesCustomizing[36]}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>GREEN</th>
|
||||
<td>{dataList && dataList.avatar_info.facesCustomizing[37]}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>BLUE</th>
|
||||
<td>{dataList && dataList.avatar_info.facesCustomizing[38]}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</UserFaceDetailInfoTable>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</UserFaceInfoTable>
|
||||
<UserFaceInfoTable $maxwidth="600px">
|
||||
<colgroup>
|
||||
<col width="120" />
|
||||
<col width="100%" />
|
||||
</colgroup>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>헤어 컬러</th>
|
||||
<td>
|
||||
<UserFaceDetailInfoTable>
|
||||
<colgroup>
|
||||
<col width="120" />
|
||||
<col width="100%" />
|
||||
</colgroup>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>RED</th>
|
||||
<td>{dataList && dataList.avatar_info.facesCustomizing[39]}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>GREEN</th>
|
||||
<td>{dataList && dataList.avatar_info.facesCustomizing[40]}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>BLUE</th>
|
||||
<td>{dataList && dataList.avatar_info.facesCustomizing[41]}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>CONTRAST</th>
|
||||
<td>{dataList && dataList.avatar_info.facesCustomizing[42]}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</UserFaceDetailInfoTable>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</UserFaceInfoTable>
|
||||
<UserFaceInfoTable $maxwidth="600px">
|
||||
<colgroup>
|
||||
<col width="120" />
|
||||
<col width="100%" />
|
||||
</colgroup>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>눈썹 컬러</th>
|
||||
<td>
|
||||
<UserFaceDetailInfoTable>
|
||||
<colgroup>
|
||||
<col width="120" />
|
||||
<col width="100%" />
|
||||
</colgroup>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>RED</th>
|
||||
<td>{dataList && dataList.avatar_info.facesCustomizing[43]}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>GREEN</th>
|
||||
<td>{dataList && dataList.avatar_info.facesCustomizing[44]}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>BLUE</th>
|
||||
<td>{dataList && dataList.avatar_info.facesCustomizing[45]}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>ALPHA</th>
|
||||
<td>{dataList && dataList.avatar_info.facesCustomizing[46]}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</UserFaceDetailInfoTable>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</UserFaceInfoTable>
|
||||
<UserFaceInfoTable $maxwidth="600px">
|
||||
<colgroup>
|
||||
<col width="120" />
|
||||
<col width="100%" />
|
||||
</colgroup>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>홍채 컬러</th>
|
||||
<td>
|
||||
<UserFaceDetailInfoTable>
|
||||
<colgroup>
|
||||
<col width="120" />
|
||||
<col width="100%" />
|
||||
</colgroup>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>RED</th>
|
||||
<td>{dataList && dataList.avatar_info.facesCustomizing[47]}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>GREEN</th>
|
||||
<td>{dataList && dataList.avatar_info.facesCustomizing[48]}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>BLUE</th>
|
||||
<td>{dataList && dataList.avatar_info.facesCustomizing[49]}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>ALPHA</th>
|
||||
<td>{dataList && dataList.avatar_info.facesCustomizing[50]}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</UserFaceDetailInfoTable>
|
||||
</td>
|
||||
|
||||
</tr>
|
||||
</tbody>
|
||||
</UserFaceInfoTable>
|
||||
{/*
|
||||
|
||||
|
||||
|
||||
<UserFaceInfoTable $maxwidth="600">
|
||||
<colgroup>
|
||||
<col width="120" />
|
||||
<col width="40%" />
|
||||
<col width="60%" />
|
||||
</colgroup>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>홍채 컬러</th>
|
||||
<tr>
|
||||
<th>RED</th>
|
||||
<td>{dataList && dataList.avatar_info.facesCustomizing[47]}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>GREEN</th>
|
||||
<td>{dataList && dataList.avatar_info.facesCustomizing[48]}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>BLUE</th>
|
||||
<td>{dataList && dataList.avatar_info.facesCustomizing[49]}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>ALPHA</th>
|
||||
<td>{dataList && dataList.avatar_info.facesCustomizing[50]}</td>
|
||||
</tr>
|
||||
</tr>
|
||||
</tbody>
|
||||
</UserFaceInfoTable>
|
||||
*/}
|
||||
</AvatarWrapper>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default UserAvatarInfo;
|
||||
|
||||
const Notice = styled.span`
|
||||
font-size: 13px;
|
||||
font-weight: 300;
|
||||
margin-bottom: 10px;
|
||||
display: block;
|
||||
`;
|
||||
|
||||
const UserInfoTable = styled.table`
|
||||
width: 100%;
|
||||
max-width: ${props => props.$maxwidth || 'auto'};
|
||||
font-size: 13px;
|
||||
border-radius: 15px;
|
||||
overflow: hidden;
|
||||
tr:first-child {
|
||||
th,
|
||||
td {
|
||||
border-top: 0;
|
||||
}
|
||||
}
|
||||
th,
|
||||
td {
|
||||
height: 36px;
|
||||
vertical-align: middle;
|
||||
border-top: 1px solid #d9d9d9;
|
||||
}
|
||||
th {
|
||||
width: 120px;
|
||||
background: #888;
|
||||
color: #fff;
|
||||
font-weight: 700;
|
||||
}
|
||||
td {
|
||||
background: #fff;
|
||||
padding: 0 20px;
|
||||
}
|
||||
`;
|
||||
|
||||
/*
|
||||
width: 100%;
|
||||
max-width: ${props => props.$maxwidth || 'auto'};
|
||||
*/
|
||||
|
||||
|
||||
const UserFaceInfoTable = styled.table`
|
||||
|
||||
max-width: ${props => props.$maxwidth || 'auto'};
|
||||
font-size: 13px;
|
||||
border-radius: 15px;
|
||||
overflow: hidden;
|
||||
tr:first-child {
|
||||
th,
|
||||
td {
|
||||
border-top: 0;
|
||||
}
|
||||
}
|
||||
th {
|
||||
height: 36px;
|
||||
vertical-align: middle;
|
||||
border-right: 1px solid #d9d9d9;
|
||||
border-bottom: 1px solid #d9d9d9;
|
||||
width: 120px;
|
||||
background: #888;
|
||||
color: #fff;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
`;
|
||||
|
||||
const UserFaceDetailInfoTable = styled.table`
|
||||
|
||||
max-width: ${props => props.$maxwidth || 'auto'};
|
||||
font-size: 13px;
|
||||
border-right-radius: 15px;
|
||||
overflow: hidden;
|
||||
tr:first-child {
|
||||
th,
|
||||
td {
|
||||
border-top: 0;
|
||||
}
|
||||
}
|
||||
tr:last-child {
|
||||
td {
|
||||
border-bottom: 0;
|
||||
}
|
||||
}
|
||||
th {
|
||||
height: 36px;
|
||||
vertical-align: middle;
|
||||
border-right: 1px solid #d9d9d9;
|
||||
border-bottom: 1px solid #d9d9d9;
|
||||
width: 120px;
|
||||
background: #888;
|
||||
color: #fff;
|
||||
font-weight: 700;
|
||||
}
|
||||
td {
|
||||
vertical-align: middle;
|
||||
border-bottom: 1px solid #d9d9d9;
|
||||
background: #fff;
|
||||
padding: 0 20px;
|
||||
}
|
||||
`;
|
||||
|
||||
|
||||
|
||||
const AvatarWrapper = styled.div`
|
||||
${UserInfoTable}:first-child {
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
${UserFaceInfoTable} {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
`;
|
||||
130
src/components/DataManage/UserClaimInfo.js
Normal file
@@ -0,0 +1,130 @@
|
||||
import { Fragment } from 'react';
|
||||
|
||||
import styled from 'styled-components';
|
||||
|
||||
const UserClaimInfo = () => {
|
||||
const ListData = [
|
||||
{ count: '1', state1: '수령', receipt1: '2023-08-11 09:00', state2: '수령', receipt2: '2023-08-11 09:00' },
|
||||
{ count: '2', state1: '수령가능', receipt1: '', state2: '수령가능', receipt2: '' },
|
||||
{ count: '3', state1: '수령불가', receipt1: '', state2: '수령불가', receipt2: '' },
|
||||
];
|
||||
return (
|
||||
<>
|
||||
<NoContent>진행중인 클레임이 없습니다.</NoContent>
|
||||
<UserTableWrapper>
|
||||
<ClaimTable>
|
||||
<colgroup>
|
||||
<col width="80" />
|
||||
<col width="170" />
|
||||
<col width="170" />
|
||||
<col width="170" />
|
||||
<col width="170" />
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>분류</th>
|
||||
<th colSpan="2">everyone</th>
|
||||
<th colSpan="2">member</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>회차</th>
|
||||
<th>상태</th>
|
||||
<th>수령시간</th>
|
||||
<th>상태</th>
|
||||
<th>수령시간</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{ListData.map((el, idx) => {
|
||||
return (
|
||||
<Fragment key={idx}>
|
||||
<tr>
|
||||
<td>{el.count}</td>
|
||||
<td>{el.state1}</td>
|
||||
<td>{el.receipt1}</td>
|
||||
<td>{el.state2}</td>
|
||||
<td>{el.receipt2}</td>
|
||||
</tr>
|
||||
</Fragment>
|
||||
);
|
||||
})}
|
||||
</tbody>
|
||||
</ClaimTable>
|
||||
</UserTableWrapper>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default UserClaimInfo;
|
||||
|
||||
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;
|
||||
}
|
||||
`;
|
||||
|
||||
const QuestTable = styled(UserDefaultTable)`
|
||||
tbody {
|
||||
td {
|
||||
padding: 9px 12px;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const UserTableWrapper = styled.div`
|
||||
width: 100%;
|
||||
overflow: auto;
|
||||
margin-bottom: 40px;
|
||||
${UserDefaultTable}, ${QuestTable} {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
&::-webkit-scrollbar {
|
||||
height: 4px;
|
||||
}
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background: #666666;
|
||||
}
|
||||
&::-webkit-scrollbar-track {
|
||||
background: #d9d9d9;
|
||||
}
|
||||
`;
|
||||
|
||||
const NoContent = styled.div`
|
||||
padding: 70px;
|
||||
text-align: center;
|
||||
font-size: 18px;
|
||||
`;
|
||||
|
||||
const ClaimTable = styled(UserDefaultTable)`
|
||||
thead {
|
||||
th {
|
||||
padding: 9px 12px;
|
||||
}
|
||||
tr:first-child {
|
||||
th {
|
||||
background: #d9d9d9;
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
186
src/components/DataManage/UserDefaultInfo.js
Normal file
@@ -0,0 +1,186 @@
|
||||
import { 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 { SelectInput } from '../../styles/Components';
|
||||
import { adminLevelType, authType, modalTypes } from '../../assets/data';
|
||||
import DynamicModal from '../common/modal/DynamicModal';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { authList } from '../../store/authList';
|
||||
import { convertKTC } from '../../utils';
|
||||
import { EditButton, ProfileWrapper, UserDefault, UserInfoTable } from '../../styles/ModuleComponents';
|
||||
import { TableSkeleton } from '../Skeleton/TableSkeleton';
|
||||
import { UserInfoSkeleton } from '../Skeleton/UserInfoSkeleton';
|
||||
|
||||
const UserDefaultInfo = ({ userInfo }) => {
|
||||
const { t } = useTranslation();
|
||||
const authInfo = useRecoilValue(authList);
|
||||
|
||||
const [pwPop, setPwPop] = useState('hidden');
|
||||
const [gmModal, setGmModal] = useState('hidden');
|
||||
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');
|
||||
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');
|
||||
let params = {};
|
||||
params.guid = userInfo.guid;
|
||||
params.admin_level = adminLevel;
|
||||
|
||||
await UserChangeAdminLevel(token, params);
|
||||
|
||||
handleCancel();
|
||||
await fetchData();
|
||||
}
|
||||
|
||||
const handleCancel = () => {
|
||||
setGmModal('hidden');
|
||||
}
|
||||
|
||||
return (
|
||||
loading ? <UserInfoSkeleton /> :
|
||||
<>
|
||||
<div>
|
||||
<UserDefault>
|
||||
<ProfileWrapper>
|
||||
<img src={Profile} alt="" />
|
||||
</ProfileWrapper>
|
||||
<UserInfoTable $maxwidth="530px">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>AID(GUID)</th>
|
||||
<td>{dataList.user_info && dataList.user_info.aid}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>계정 ID</th>
|
||||
<td>{dataList.user_info && dataList.user_info.user_id}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>국가</th>
|
||||
<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>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>계정 생성일</th>
|
||||
<td>
|
||||
{dataList.user_info && convertKTC(dataList.user_info.create_dt)}
|
||||
</td>
|
||||
</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>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>최근 종료일자</th>
|
||||
<td>{dataList.user_info && convertKTC(dataList.user_info.end_dt)}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>전자지갑 URL</th>
|
||||
<td>
|
||||
<Link>{dataList.user_info && dataList.user_info.wallet_url}</Link>
|
||||
</td>
|
||||
</tr>
|
||||
<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)} >
|
||||
{adminLevelType.map((data, index) => (
|
||||
<option key={index} value={data.value}>
|
||||
{data.name}
|
||||
</option>
|
||||
))}
|
||||
</SelectInput>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</UserInfoTable>
|
||||
</UserDefault>
|
||||
<UserInfoTable $maxwidth="720px">
|
||||
<colgroup>
|
||||
<col />
|
||||
<col />
|
||||
<col width="120" />
|
||||
<col />
|
||||
</colgroup>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>캐릭터 아바타명</th>
|
||||
<td colSpan="3">
|
||||
{dataList.char_info && dataList.char_info.character_name}
|
||||
<EditButton
|
||||
hidden={true}
|
||||
onClick={e => {
|
||||
e.preventDefault();
|
||||
handleClick();
|
||||
}}></EditButton>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>시즌 패스 레벨</th>
|
||||
<td colSpan="3">{dataList.char_info && dataList.char_info.level}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>골드</th>
|
||||
<td>{dataList.char_info && dataList.char_info.gold_cali}</td>
|
||||
<th>사파이어</th>
|
||||
<td>{dataList.char_info && dataList.char_info.blue_cali}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>칼리움</th>
|
||||
<td>{dataList.char_info && dataList.char_info.red_cali}</td>
|
||||
<th>루비</th>
|
||||
<td>{dataList.char_info && dataList.char_info.black_cali}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</UserInfoTable>
|
||||
</div>
|
||||
<NicknameChangeModal pwPop={pwPop} handleClick={handleClick} dataList={dataList} />
|
||||
<DynamicModal
|
||||
modalType={modalTypes.childOkCancel}
|
||||
view={gmModal}
|
||||
modalText={t('USER_GM_CHANGE')}
|
||||
handleCancel={handleCancel}
|
||||
handleSubmit={handleSubmit}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
export default UserDefaultInfo;
|
||||
125
src/components/DataManage/UserDressInfo.js
Normal file
@@ -0,0 +1,125 @@
|
||||
import styled from 'styled-components';
|
||||
import { useState, useEffect } from 'react';
|
||||
import { UserClothView } from '../../apis/Users';
|
||||
import { TableSkeleton } from '../Skeleton/TableSkeleton';
|
||||
|
||||
const UserDressInfo = ({ userInfo }) => {
|
||||
const [dataList, setDataList] = useState();
|
||||
const [rowData, setRowData] = useState([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
fetchData();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
setRowData([
|
||||
{ slotType : "상의", smallType: '셔츠', itemNo : dataList?.cloth_info?.cloth_shirt?.cloth || 0, itemName: dataList?.cloth_info?.cloth_shirt?.clothName || null},
|
||||
{ slotType : "상의", smallType: '드레스', itemNo : dataList?.cloth_info?.cloth_dress?.cloth || 0, itemName: dataList?.cloth_info?.cloth_dress?.clothName || null},
|
||||
{ slotType : "외투", smallType: '외투', itemNo : dataList?.cloth_info?.cloth_outer?.cloth || 0, itemName: dataList?.cloth_info?.cloth_outer?.clothName || null},
|
||||
{ slotType : "하의", smallType: '바지', itemNo : dataList?.cloth_info?.cloth_pants?.cloth || 0, itemName: dataList?.cloth_info?.cloth_pants?.clothName || null},
|
||||
{ slotType : "장갑", smallType: '장갑', itemNo : dataList?.cloth_info?.cloth_gloves?.cloth || 0, itemName: dataList?.cloth_info?.cloth_gloves?.clothName || null},
|
||||
{ slotType : "장갑", smallType: '반지', itemNo : dataList?.cloth_info?.cloth_ring?.cloth || 0, itemName: dataList?.cloth_info?.cloth_ring?.clothName || null},
|
||||
{ slotType : "장갑", smallType: '팔찌', itemNo : dataList?.cloth_info?.cloth_bracelet?.cloth || 0, itemName: dataList?.cloth_info?.cloth_bracelet?.clothName || null},
|
||||
{ slotType : "가방", smallType: '가방', itemNo : dataList?.cloth_info?.cloth_bag?.cloth || 0, itemName: dataList?.cloth_info?.cloth_bag?.clothName || null},
|
||||
{ slotType : "가방", smallType: '배낭', itemNo : dataList?.cloth_info?.cloth_backpack?.cloth || 0, itemName: dataList?.cloth_info?.cloth_backpack?.clothName || null},
|
||||
{ slotType : "머리 장식", smallType: '모자', itemNo : dataList?.cloth_info?.cloth_cap?.cloth || 0, itemName: dataList?.cloth_info?.cloth_cap?.clothName || null},
|
||||
{ slotType : "얼굴 장식", smallType: '얼굴 장식', itemNo : dataList?.cloth_info?.cloth_mask?.cloth || 0, itemName: dataList?.cloth_info?.cloth_mask?.clothName || null},
|
||||
{ slotType : "얼굴 장식", smallType: '안경', itemNo : dataList?.cloth_info?.cloth_glasses?.cloth || 0, itemName: dataList?.cloth_info?.cloth_glasses?.clothName || null},
|
||||
{ slotType : "귀걸이", smallType: '귀걸이', itemNo : dataList?.cloth_info?.cloth_earring?.cloth || 0, itemName: dataList?.cloth_info?.cloth_earring?.clothName || null},
|
||||
{ slotType : "목걸이", smallType: '목걸이', itemNo : dataList?.cloth_info?.cloth_necklace?.cloth || 0, itemName: dataList?.cloth_info?.cloth_necklace?.clothName || null},
|
||||
{ slotType : "신발", smallType: '신발', itemNo : dataList?.cloth_info?.cloth_shoes?.cloth || 0, itemName: dataList?.cloth_info?.cloth_shoes?.clothName || null},
|
||||
{ slotType : "양말", smallType: '양말', itemNo : dataList?.cloth_info?.cloth_socks?.cloth || 0, itemName: dataList?.cloth_info?.cloth_socks?.clothName || null},
|
||||
{ slotType : "양말", smallType: '발찌', itemNo : dataList?.cloth_info?.cloth_anklet?.cloth || 0, itemName: dataList?.cloth_info?.cloth_anklet?.clothName || null}
|
||||
])
|
||||
}, [dataList])
|
||||
|
||||
const fetchData = async () => {
|
||||
const token = sessionStorage.getItem('token');
|
||||
await UserClothView(token, userInfo.guid).then(data => {
|
||||
setDataList(data);
|
||||
setLoading(false);
|
||||
});
|
||||
};
|
||||
|
||||
const groupedData = rowData.reduce((acc, el) => {
|
||||
const key = el.slotType;
|
||||
if (!acc[key]) {
|
||||
acc[key] = [];
|
||||
}
|
||||
acc[key].push(el);
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
return (
|
||||
loading ? <TableSkeleton width='30%' count={15} /> :
|
||||
<>
|
||||
<DressWrapper>
|
||||
<UserInfoTable $maxwidth="670px">
|
||||
<colgroup>
|
||||
<col width="100" />
|
||||
<col width="120" />
|
||||
<col width="30%" />
|
||||
<col width="50%" />
|
||||
</colgroup>
|
||||
<tbody>
|
||||
{Object.keys(groupedData).map((key) => {
|
||||
const rows = groupedData[key];
|
||||
return rows.map((el, idx) => (
|
||||
<tr key={`${key}-${idx}`}>
|
||||
{idx === 0 && (
|
||||
<th rowSpan={rows.length}>{el.slotType}</th>
|
||||
)}
|
||||
<th>{el.smallType}</th>
|
||||
<td>{el.itemNo}</td>
|
||||
<td>{el.itemName}</td>
|
||||
</tr>
|
||||
));
|
||||
})}
|
||||
</tbody>
|
||||
</UserInfoTable>
|
||||
</DressWrapper>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default UserDressInfo;
|
||||
|
||||
const UserInfoTable = styled.table`
|
||||
width: 100%;
|
||||
max-width: ${props => props.$maxwidth || 'auto'};
|
||||
font-size: 13px;
|
||||
border-radius: 15px;
|
||||
overflow: hidden;
|
||||
tr:first-child {
|
||||
th,
|
||||
td {
|
||||
border-top: 0;
|
||||
}
|
||||
}
|
||||
th,
|
||||
td {
|
||||
height: 36px;
|
||||
vertical-align: middle;
|
||||
border-top: 1px solid #d9d9d9;
|
||||
border-right: 1px solid #d9d9d9;
|
||||
}
|
||||
th {
|
||||
width: 120px;
|
||||
background: #888;
|
||||
color: #fff;
|
||||
font-weight: 700;
|
||||
}
|
||||
td {
|
||||
background: #fff;
|
||||
padding: 0 20px;
|
||||
}
|
||||
`;
|
||||
|
||||
const DressWrapper = styled.div`
|
||||
${UserInfoTable} {
|
||||
td {
|
||||
border-left: 1px solid #d9d9d9;
|
||||
}
|
||||
}
|
||||
`;
|
||||
194
src/components/DataManage/UserFriendInfo.js
Normal file
@@ -0,0 +1,194 @@
|
||||
import { useState, Fragment, useEffect } from 'react';
|
||||
import { SelectInput } from '../../styles/Components';
|
||||
import { UserTableWrapper, SelectWrapper, RequestTabWrapper, RequestTab, UserDefaultTable } from '../../styles/ModuleComponents';
|
||||
import { UserFriendListView } from '../../apis';
|
||||
import { convertKTC } from '../../utils';
|
||||
import { TableSkeleton } from '../Skeleton/TableSkeleton';
|
||||
|
||||
const UserFriendInfo = ({ userInfo }) => {
|
||||
const [activeSection, setActiveSection] = useState('list');
|
||||
const [activeRequest, setActiveRequest] = useState('received');
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
const [dataList, setDataList] = useState([]);
|
||||
|
||||
useEffect(() => {
|
||||
fetchData();
|
||||
}, []);
|
||||
|
||||
const fetchData = async () => {
|
||||
const token = sessionStorage.getItem('token');
|
||||
await UserFriendListView(token, userInfo.guid).then(data => {
|
||||
setDataList(data);
|
||||
setLoading(false);
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
loading ? <TableSkeleton /> :
|
||||
<>
|
||||
<SelectWrapper>
|
||||
<SelectInput
|
||||
onChange={e => {
|
||||
setActiveSection(e.target.value);
|
||||
}}>
|
||||
<option value="list">친구 목록</option>
|
||||
<option value="request">친구 요청</option>
|
||||
<option value="block">차단 목록</option>
|
||||
</SelectInput>
|
||||
{activeSection === 'request' && (
|
||||
<RequestTabWrapper>
|
||||
<RequestTab
|
||||
$state={activeRequest === 'received' ? 'active' : 'inactive'}
|
||||
onClick={e => {
|
||||
e.preventDefault();
|
||||
setActiveRequest('received');
|
||||
}}>
|
||||
받은 요청
|
||||
</RequestTab>
|
||||
<RequestTab
|
||||
$state={activeRequest === 'sended' ? 'active' : 'inactive'}
|
||||
onClick={e => {
|
||||
e.preventDefault();
|
||||
setActiveRequest('sended');
|
||||
}}>
|
||||
보낸 요청
|
||||
</RequestTab>
|
||||
</RequestTabWrapper>
|
||||
)}
|
||||
</SelectWrapper>
|
||||
{activeSection === 'list' && (
|
||||
<>
|
||||
<UserTableWrapper>
|
||||
<UserDefaultTable>
|
||||
<thead>
|
||||
<tr>
|
||||
<th width="70">번호</th>
|
||||
<th width="200">친구 닉네임</th>
|
||||
<th width="300">친구 GUID</th>
|
||||
<th width="100">국가(언어)</th>
|
||||
<th width="160">친구 수락일</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{dataList && dataList.friend_list &&
|
||||
dataList.friend_list.map((data, idx) => (
|
||||
<Fragment key={idx}>
|
||||
<tr>
|
||||
<td>{data.row_num}</td>
|
||||
<td>{data.friend_name}</td>
|
||||
<td>{data.friend_guid}</td>
|
||||
<td>{data.language}</td>
|
||||
<td>{data.receive_dt && convertKTC(data.receive_dt)}</td>
|
||||
</tr>
|
||||
</Fragment>
|
||||
))}
|
||||
</tbody>
|
||||
</UserDefaultTable>
|
||||
</UserTableWrapper>
|
||||
</>
|
||||
)}
|
||||
{activeSection === 'request' && (
|
||||
<>
|
||||
{activeRequest === 'received' && (
|
||||
<>
|
||||
<UserDefaultTable>
|
||||
<thead>
|
||||
<tr>
|
||||
<th width="70">번호</th>
|
||||
<th width="200">요청자 닉네임</th>
|
||||
<th width="300">요청자 GUID</th>
|
||||
<th width="100">국가(언어)</th>
|
||||
<th width="160">받은 일자</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{dataList && dataList.friend_receive_list && dataList.friend_receive_list.map((data, idx) => {
|
||||
return (
|
||||
<Fragment key={idx}>
|
||||
<tr>
|
||||
<td>{data.row_num}</td>
|
||||
<td>{data.friend_name}</td>
|
||||
<td>{data.friend_guid}</td>
|
||||
<td>{data.language}</td>
|
||||
<td>{data.receive_dt && convertKTC(data.receive_dt)}</td>
|
||||
</tr>
|
||||
</Fragment>
|
||||
);
|
||||
})}
|
||||
</tbody>
|
||||
</UserDefaultTable>
|
||||
</>
|
||||
)}
|
||||
{activeRequest === 'sended' && (
|
||||
<>
|
||||
<UserTableWrapper>
|
||||
<UserDefaultTable>
|
||||
<thead>
|
||||
<tr>
|
||||
<th width="70">번호</th>
|
||||
<th width="200">요청 대상자 닉네임</th>
|
||||
<th width="300">요청 대상자 GUID</th>
|
||||
<th width="100">국가(언어)</th>
|
||||
<th width="160">요청일</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{dataList && dataList.friend_send_list && dataList.friend_send_list.map((data, idx) => {
|
||||
return (
|
||||
<Fragment key={idx}>
|
||||
<tr>
|
||||
<td>{data.row_num}</td>
|
||||
<td>{data.friend_name}</td>
|
||||
<td>{data.friend_guid}</td>
|
||||
<td>{data.language}</td>
|
||||
<td>{data.receive_dt && convertKTC(data.receive_dt)}</td>
|
||||
</tr>
|
||||
</Fragment>
|
||||
);
|
||||
})}
|
||||
</tbody>
|
||||
</UserDefaultTable>
|
||||
</UserTableWrapper>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
{activeSection === 'block' && (
|
||||
<>
|
||||
<UserTableWrapper>
|
||||
<UserDefaultTable>
|
||||
<thead>
|
||||
<tr>
|
||||
<th width="70">번호</th>
|
||||
<th width="200">차단 닉네임</th>
|
||||
<th width="300">차단 대상 GUID</th>
|
||||
<th width="100">국가(언어)</th>
|
||||
<th width="160">차단 일자</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{dataList && dataList.friend_block_list && dataList.friend_block_list.map((data, idx) => {
|
||||
return (
|
||||
<Fragment key={idx}>
|
||||
<tr>
|
||||
<td>{data.row_num}</td>
|
||||
<td>{data.friend_name}</td>
|
||||
<td>{data.friend_guid}</td>
|
||||
<td>{data.language}</td>
|
||||
<td>{data.receive_dt && convertKTC(data.receive_dt)}</td>
|
||||
</tr>
|
||||
</Fragment>
|
||||
);
|
||||
})}
|
||||
</tbody>
|
||||
</UserDefaultTable>
|
||||
</UserTableWrapper>
|
||||
</>
|
||||
)}
|
||||
{/*{loading && <Loading/>}*/}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default UserFriendInfo;
|
||||
363
src/components/DataManage/UserInventoryInfo.js
Normal file
@@ -0,0 +1,363 @@
|
||||
import styled from 'styled-components';
|
||||
import { useState, useEffect } from 'react';
|
||||
import { UserInventoryItemDelete, UserInventoryView } from '../../apis/Users';
|
||||
import Button from '../common/button/Button';
|
||||
import ConfirmModal from '../common/modal/ConfirmModal';
|
||||
import CompletedModal from '../common/modal/CompletedModal';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { InputItem, SelectInput, TextInput } from '../../styles/Components';
|
||||
import CustomConfirmModal from '../common/modal/CustomConfirmModal';
|
||||
import { ivenTabType } from '../../assets/data';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { authList } from '../../store/authList';
|
||||
import { TableSkeleton } from '../Skeleton/TableSkeleton';
|
||||
import { InfoSubTitle, UserDefaultTable, UserTableWrapper } from '../../styles/ModuleComponents';
|
||||
|
||||
const UserInventoryInfo = ({ userInfo }) => {
|
||||
const { t } = useTranslation();
|
||||
const authInfo = useRecoilValue(authList);
|
||||
|
||||
const [dataList, setDataList] = useState();
|
||||
const [itemCount, setItemCount] = useState('');
|
||||
const [selected, setSelected] = useState({});
|
||||
const [itemUpdateCount, setItemUpdateCount] = useState('1');
|
||||
const [deleteConfirmModal, setDeleteConfirmModal] = useState('hidden');
|
||||
const [deleteSubmitModal, setDeleteSubmitModal] = useState('hidden');
|
||||
const [deleteCompleteModal, setDeleteCompleteModal] = useState('hidden');
|
||||
const [authDelete, setAuthDelete] = useState(false);
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
fetchData();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
setAuthDelete(authInfo.auth_list.some(auth => auth.id === 35));
|
||||
}, [authInfo]);
|
||||
|
||||
const fetchData = async () => {
|
||||
const token = sessionStorage.getItem('token');
|
||||
await UserInventoryView(token, userInfo.guid).then(data => {
|
||||
setDataList(data);
|
||||
setLoading(false);
|
||||
});
|
||||
};
|
||||
|
||||
const handleDeleteConfirmModal = (data, type) =>{
|
||||
setItemCount(data.count);
|
||||
let row;
|
||||
switch(type){
|
||||
case ivenTabType.CLOTH:
|
||||
row = dataList.inventory_list.cloth.find(item => item.item_guid === data.item_guid);
|
||||
row.type = ivenTabType.CLOTH;
|
||||
break;
|
||||
case ivenTabType.PROP:
|
||||
row = dataList.inventory_list.prop.find(item => item.item_guid === data.item_guid);
|
||||
row.type = ivenTabType.PROP;
|
||||
break;
|
||||
case ivenTabType.BEAUTY:
|
||||
row = dataList.inventory_list.beauty.find(item => item.item_guid === data.item_guid);
|
||||
row.type = ivenTabType.BEAUTY;
|
||||
break;
|
||||
case ivenTabType.TATTOO:
|
||||
row = dataList.inventory_list.tattoo.find(item => item.item_guid === data.item_guid);
|
||||
row.type = ivenTabType.TATTOO;
|
||||
break;
|
||||
case ivenTabType.CURRENCY:
|
||||
row = dataList.inventory_list.currency.find(item => item.item_guid === data.item_guid);
|
||||
row.type = ivenTabType.CURRENCY;
|
||||
break;
|
||||
default:
|
||||
row = dataList.inventory_list.etc.find(item => item.item_guid === data.item_guid);
|
||||
row.type = ivenTabType.ETC;
|
||||
break;
|
||||
}
|
||||
setSelected(row);
|
||||
setDeleteConfirmModal('view');
|
||||
}
|
||||
|
||||
// 개수 입력 모달 hidden
|
||||
const handleDeleteConfirmClose = () => {
|
||||
setDeleteConfirmModal('hidden');
|
||||
setItemUpdateCount(1);
|
||||
setSelected({});
|
||||
}
|
||||
|
||||
// 삭제 모달 hidden
|
||||
const handleDeleteClose = () => {
|
||||
setDeleteSubmitModal('hidden');
|
||||
}
|
||||
|
||||
const handleDeleteConfirm = () => {
|
||||
setDeleteSubmitModal('view');
|
||||
}
|
||||
|
||||
// 삭제 처리
|
||||
const handleDeleteSubmit = async () => {
|
||||
let params = {}
|
||||
params.guid = userInfo.guid;
|
||||
params.item_guid = selected.item_guid;
|
||||
params.current_cnt = selected.count;
|
||||
params.cnt = itemUpdateCount;
|
||||
const token = sessionStorage.getItem('token');
|
||||
const result = await UserInventoryItemDelete(token, params);
|
||||
if(result.result === "SUCCESS"){
|
||||
//성공시 아이템 삭제 or 개수 차감
|
||||
if(selected.count <= itemUpdateCount){
|
||||
switch (selected.type) {
|
||||
case ivenTabType.CLOTH:
|
||||
dataList.inventory_list.cloth.splice(dataList.inventory_list.cloth.findIndex(item => item.item_guid === selected.item_guid), 1);
|
||||
break;
|
||||
case ivenTabType.PROP:
|
||||
dataList.inventory_list.prop.splice(dataList.inventory_list.prop.findIndex(item => item.item_guid === selected.item_guid), 1);
|
||||
break;
|
||||
case ivenTabType.BEAUTY:
|
||||
dataList.inventory_list.beauty.splice(dataList.inventory_list.beauty.findIndex(item => item.item_guid === selected.item_guid), 1);
|
||||
break;
|
||||
case ivenTabType.TATTOO:
|
||||
dataList.inventory_list.tattoo.splice(dataList.inventory_list.tattoo.findIndex(item => item.item_guid === selected.item_guid), 1);
|
||||
break;
|
||||
case ivenTabType.CURRENCY:
|
||||
dataList.inventory_list.currency.splice(dataList.inventory_list.currency.findIndex(item => item.item_guid === selected.item_guid), 1);
|
||||
break;
|
||||
default:
|
||||
dataList.inventory_list.etc.splice(dataList.inventory_list.etc.findIndex(item => item.item_guid === selected.item_guid), 1);
|
||||
break;
|
||||
}
|
||||
}else{
|
||||
selected.count = selected.count - itemUpdateCount;
|
||||
}
|
||||
}
|
||||
handleDeleteClose();
|
||||
handleDeleteConfirmClose();
|
||||
setDeleteCompleteModal('view')
|
||||
}
|
||||
|
||||
const handleDeleteComplete = () => {
|
||||
setDeleteCompleteModal('hidden');
|
||||
}
|
||||
|
||||
const handleItemCount = e => {
|
||||
if (e.target.value === '0' || e.target.value === '-0') {
|
||||
setItemUpdateCount('1');
|
||||
e.target.value = '1';
|
||||
} else if (e.target.value < 0) {
|
||||
let plusNum = Math.abs(e.target.value);
|
||||
setItemUpdateCount(plusNum);
|
||||
} else if(e.target.value > itemCount){
|
||||
alert(t('DEL_COUNT_CHECK'));
|
||||
setItemUpdateCount(itemCount);
|
||||
} else {
|
||||
setItemUpdateCount(e.target.value);
|
||||
}
|
||||
};
|
||||
|
||||
const ConfirmChild = () => {
|
||||
return(
|
||||
<InputItem>
|
||||
<p>{t('DEL_COUNT_CONFIRM', {count: itemCount})}</p>
|
||||
<TextInput placeholder="수량" type="number" value={itemUpdateCount} onChange={e => handleItemCount(e)} width="200px" />
|
||||
</InputItem>
|
||||
);
|
||||
}
|
||||
|
||||
const InvenTable = ({invenList, title, type}) => {
|
||||
return (
|
||||
<>
|
||||
<InfoSubTitle>{title}</InfoSubTitle>
|
||||
<UserTableWrapper>
|
||||
<UserDefaultTable>
|
||||
<thead>
|
||||
<tr>
|
||||
<th width="80">no.</th>
|
||||
<th width="120">ID</th>
|
||||
<th width="50%">아이템</th>
|
||||
<th width="100">보유개수</th>
|
||||
<th width="60">삭제</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{invenList.map((el, idx) => {
|
||||
return (
|
||||
<tr key={idx}>
|
||||
<td>{idx + 1}</td>
|
||||
<td>{el.item_id}</td>
|
||||
<td>{el.item_name}</td>
|
||||
<td>{el.count}</td>
|
||||
<td>
|
||||
<Button theme={authDelete ? "line" : "disable"} id={el.item_guid} name="single" text="삭제" handleClick={e => {handleDeleteConfirmModal(el, type)}} disabled={!authDelete}/>
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
})}
|
||||
</tbody>
|
||||
</UserDefaultTable>
|
||||
</UserTableWrapper>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
loading ? <TableSkeleton count={15}/> :
|
||||
<>
|
||||
{dataList && dataList.inventory_list && <InvenTable title="의상 탭" type={ivenTabType.CLOTH} invenList={dataList.inventory_list.cloth} />}
|
||||
{dataList && dataList.inventory_list && <InvenTable title="소품 탭" type={ivenTabType.PROP} invenList={dataList.inventory_list.prop} />}
|
||||
{dataList && dataList.inventory_list && <InvenTable title="미용 탭" type={ivenTabType.BEAUTY} invenList={dataList.inventory_list.beauty} />}
|
||||
{dataList && dataList.inventory_list && <InvenTable title="타투 탭" type={ivenTabType.TATTOO} invenList={dataList.inventory_list.tattoo} />}
|
||||
{dataList && dataList.inventory_list && <InvenTable title="재화 탭" type={ivenTabType.CURRENCY} invenList={dataList.inventory_list.currency} />}
|
||||
{dataList && dataList.inventory_list && <InvenTable title="기타 탭" type={ivenTabType.ETC} invenList={dataList.inventory_list.etc} />}
|
||||
<CustomConfirmModal
|
||||
ChildView={ConfirmChild}
|
||||
view={deleteConfirmModal}
|
||||
handleSubmit={handleDeleteConfirm}
|
||||
handleCancel={handleDeleteConfirmClose}
|
||||
handleClose={handleDeleteConfirmClose}
|
||||
/>
|
||||
<ConfirmModal
|
||||
modalText={t('DEL_CONFIRM')}
|
||||
view={deleteSubmitModal}
|
||||
handleSubmit={handleDeleteSubmit}
|
||||
handleCancel={handleDeleteClose}
|
||||
handleClose={handleDeleteClose}
|
||||
/>
|
||||
<CompletedModal
|
||||
view={deleteCompleteModal}
|
||||
modalText={t('DEL_COMPLETE')}
|
||||
handleComplete={handleDeleteComplete}
|
||||
/>
|
||||
{/* <InfoSubTitle>의상 탭</InfoSubTitle>
|
||||
<UserTableWrapper>
|
||||
<UserDefaultTable>
|
||||
<thead>
|
||||
<tr>
|
||||
<th width="80">no.</th>
|
||||
<th width="120">ID</th>
|
||||
<th width="50%">아이템</th>
|
||||
<th width="100">보유개수</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{dataList &&
|
||||
dataList.inventory_list.cloth.map((el, idx) => {
|
||||
return (
|
||||
<tr key={idx}>
|
||||
<td>{idx + 1}</td>
|
||||
<td>{el.item_id}</td>
|
||||
<td>{el.item_name}</td>
|
||||
<td>{el.count}</td>
|
||||
</tr>
|
||||
);
|
||||
})}
|
||||
</tbody>
|
||||
</UserDefaultTable>
|
||||
</UserTableWrapper>
|
||||
<InfoSubTitle>소품 탭</InfoSubTitle>
|
||||
<UserTableWrapper>
|
||||
<UserDefaultTable>
|
||||
<thead>
|
||||
<tr>
|
||||
<th width="80">no.</th>
|
||||
<th width="120">ID</th>
|
||||
<th width="50%">아이템</th>
|
||||
<th width="100">보유개수</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{dataList &&
|
||||
dataList.inventory_list.prop.map((el, idx) => {
|
||||
return (
|
||||
<tr key={idx}>
|
||||
<td>{idx + 1}</td>
|
||||
<td>{el.item_id}</td>
|
||||
<td>{el.item_name}</td>
|
||||
<td>{el.count}</td>
|
||||
</tr>
|
||||
);
|
||||
})}
|
||||
</tbody>
|
||||
</UserDefaultTable>
|
||||
</UserTableWrapper>
|
||||
<InfoSubTitle>미용 탭</InfoSubTitle>
|
||||
<UserTableWrapper>
|
||||
<UserDefaultTable>
|
||||
<thead>
|
||||
<tr>
|
||||
<th width="80">no.</th>
|
||||
<th width="120">ID</th>
|
||||
<th width="50%">아이템</th>
|
||||
<th width="100">보유개수</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{dataList &&
|
||||
dataList.inventory_list.beauty.map((el, idx) => {
|
||||
return (
|
||||
<tr key={idx}>
|
||||
<td>{idx + 1}</td>
|
||||
<td>{el.item_id}</td>
|
||||
<td>{el.item_name}</td>
|
||||
<td>{el.count}</td>
|
||||
</tr>
|
||||
);
|
||||
})}
|
||||
</tbody>
|
||||
</UserDefaultTable>
|
||||
</UserTableWrapper>
|
||||
<InfoSubTitle>타투 탭</InfoSubTitle>
|
||||
<UserTableWrapper>
|
||||
<UserDefaultTable>
|
||||
<thead>
|
||||
<tr>
|
||||
<th width="80">no.</th>
|
||||
<th width="120">ID</th>
|
||||
<th width="50%">아이템</th>
|
||||
<th width="100">보유개수</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{dataList &&
|
||||
dataList.inventory_list.tattoo.map((el, idx) => {
|
||||
return (
|
||||
<tr key={idx}>
|
||||
<td>{idx + 1}</td>
|
||||
<td>{el.item_id}</td>
|
||||
<td>{el.item_name}</td>
|
||||
<td>{el.count}</td>
|
||||
</tr>
|
||||
);
|
||||
})}
|
||||
</tbody>
|
||||
</UserDefaultTable>
|
||||
</UserTableWrapper>
|
||||
<InfoSubTitle>기타 탭</InfoSubTitle>
|
||||
<UserTableWrapper>
|
||||
<UserDefaultTable>
|
||||
<thead>
|
||||
<tr>
|
||||
<th width="80">슬롯 no.</th>
|
||||
<th width="320">ID</th>
|
||||
<th width="50%">아이템</th>
|
||||
<th width="100">보유개수</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{dataList &&
|
||||
dataList.inventory_list.etc.map((el, idx) => {
|
||||
return (
|
||||
<tr key={idx}>
|
||||
<td>{idx + 1}</td>
|
||||
<td>{el.item_id}</td>
|
||||
<td>{el.item_name}</td>
|
||||
<td>{el.count}</td>
|
||||
</tr>
|
||||
);
|
||||
})}
|
||||
</tbody>
|
||||
</UserDefaultTable>
|
||||
</UserTableWrapper> */}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default UserInventoryInfo;
|
||||
|
||||
398
src/components/DataManage/UserMailInfo.js
Normal file
@@ -0,0 +1,398 @@
|
||||
import { useState, Fragment, useEffect } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
import styled from 'styled-components';
|
||||
|
||||
import CheckBox from '../../components/common/input/CheckBox';
|
||||
import MailDetailModal from '../../components/DataManage/MailDetailModal';
|
||||
import { SelectInput, TextInput } from '../../styles/Components';
|
||||
import { UserMailDelete, UserMailItemDelete, UserMailView } from '../../apis';
|
||||
import ConfirmModal from '../common/modal/ConfirmModal';
|
||||
import CompletedModal from '../common/modal/CompletedModal';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import CustomConfirmModal from '../common/modal/CustomConfirmModal';
|
||||
import DynamicModal from '../common/modal/DynamicModal';
|
||||
import { authType, ivenTabType } from '../../assets/data';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { authList } from '../../store/authList';
|
||||
import { convertKTC } from '../../utils';
|
||||
import { TableSkeleton } from '../Skeleton/TableSkeleton';
|
||||
|
||||
const UserMailInfo = ({ userInfo }) => {
|
||||
const token = sessionStorage.getItem('token');
|
||||
const { t } = useTranslation();
|
||||
const authInfo = useRecoilValue(authList);
|
||||
|
||||
//데이터 리스트
|
||||
const [dataList, setDataList] = useState([]);
|
||||
// 받은 우편, 보낸 우편
|
||||
const [option, setOption] = useState('RECEIVE');
|
||||
// 상세 정보
|
||||
const [detail, setDetail] = useState({ title: '', content: '', item_list: [], mail_guid: '' });
|
||||
const [deleteSelected, setDeleteSelected] = useState({});
|
||||
const [itemUpdateCount, setItemUpdateCount] = useState('1');
|
||||
const [authDelete, setAuthDelete] = useState(false);
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
const [modalState, setModalState] = useState({
|
||||
detailModal: 'hidden',
|
||||
deleteItemModal: 'hidden',
|
||||
deleteSubmitModal: 'hidden',
|
||||
deleteCompleteModal: 'hidden',
|
||||
deleteItemCompleteModal: 'hidden'
|
||||
});
|
||||
|
||||
// 받은 우편, 보낸 우편 option 에 따른 data fetch
|
||||
useEffect(() => {
|
||||
fetchData(option);
|
||||
}, [option]);
|
||||
|
||||
useEffect(() => {
|
||||
setAuthDelete(authInfo.auth_list.some(auth => auth.id === authType.userSearchDelete));
|
||||
}, [authInfo]);
|
||||
|
||||
const fetchData = async option => {
|
||||
await UserMailView(token, userInfo.guid, option).then(data =>{
|
||||
setDataList(data);
|
||||
setLoading(false);
|
||||
});
|
||||
};
|
||||
|
||||
// 상세 모달 value 세팅
|
||||
const handleDetail = (title, content, itmeList, mail_guid) => {
|
||||
setDetail({ title: title, content: content, item_list: itmeList, mail_guid: mail_guid });
|
||||
};
|
||||
|
||||
// 우편 아이템 삭제 개수 처리
|
||||
const handleItemCount = e => {
|
||||
if (e.target.value === '0' || e.target.value === '-0') {
|
||||
setItemUpdateCount('1');
|
||||
e.target.value = '1';
|
||||
} else if (e.target.value < 0) {
|
||||
let plusNum = Math.abs(e.target.value);
|
||||
setItemUpdateCount(plusNum);
|
||||
} else if(e.target.value > deleteSelected.count){
|
||||
alert(t('DEL_COUNT_CHECK'));
|
||||
setItemUpdateCount(deleteSelected.count);
|
||||
} else {
|
||||
setItemUpdateCount(e.target.value);
|
||||
}
|
||||
};
|
||||
|
||||
const handleModalView = (type) => {
|
||||
setModalState((prevState) => ({
|
||||
...prevState,
|
||||
[`${type}Modal`]: 'view',
|
||||
}));
|
||||
}
|
||||
|
||||
const handleModalClose = (type) => {
|
||||
setModalState((prevState) => ({
|
||||
...prevState,
|
||||
[`${type}Modal`]: 'hidden',
|
||||
}));
|
||||
}
|
||||
|
||||
const handleModalSubmit = async (type, param = null) => {
|
||||
let params;
|
||||
let result;
|
||||
switch (type) {
|
||||
case "detail":
|
||||
handleModalView('deleteSubmit');
|
||||
break;
|
||||
case "deleteItem":
|
||||
setDeleteSelected(param);
|
||||
handleModalView('deleteItem');
|
||||
break;
|
||||
case "deleteSubmit":
|
||||
params = {}
|
||||
params.type = option;
|
||||
params.guid = userInfo.guid;
|
||||
params.mail_guid = detail.mail_guid;
|
||||
|
||||
result = await UserMailDelete(token, params);
|
||||
|
||||
handleModalClose('deleteSubmit');
|
||||
handleModalView('deleteComplete');
|
||||
break;
|
||||
case "deleteItemSubmit":
|
||||
params = {}
|
||||
params.type = option;
|
||||
params.guid = userInfo.guid;
|
||||
params.mail_guid = detail.mail_guid;
|
||||
params.item_id = deleteSelected.item_id;
|
||||
params.parrent_count = deleteSelected.count;
|
||||
params.count = itemUpdateCount;
|
||||
|
||||
result = await UserMailItemDelete(token, params);
|
||||
if(result.result === "SUCCESS"){
|
||||
if(deleteSelected.count <= itemUpdateCount){
|
||||
const item_idx = detail.item_list.findIndex(item => item.item_id === deleteSelected.item_id);
|
||||
if(item_idx >= 0) {
|
||||
detail.item_list.splice(item_idx, 1);
|
||||
}
|
||||
}else{
|
||||
deleteSelected.count = deleteSelected.count - itemUpdateCount;
|
||||
}
|
||||
}
|
||||
handleModalClose('deleteItem');
|
||||
handleModalView('deleteItemComplete');
|
||||
break;
|
||||
case "deleteComplete":
|
||||
handleModalClose('deleteComplete');
|
||||
handleModalClose('detail');
|
||||
// const idx = dataList.mail_list.findIndex(mail => mail.mail_guid === detail.mail_guid);
|
||||
// if(idx >= 0) {
|
||||
// dataList.mail_list.splice(idx, 1);
|
||||
// }
|
||||
fetchData(option);
|
||||
break;
|
||||
case "deleteItemComplete":
|
||||
handleModalClose('deleteItemComplete');
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const ConfirmChild = () => {
|
||||
return(
|
||||
<InputItem>
|
||||
<p>{t('DEL_COUNT_CONFIRM', {count: deleteSelected.count})}</p>
|
||||
<TextInput placeholder="수량" type="number" value={itemUpdateCount} onChange={e => handleItemCount(e)} width="200px" />
|
||||
</InputItem>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
loading ? <TableSkeleton count={10}/> :
|
||||
<>
|
||||
<SelectWrapper>
|
||||
<SelectInput
|
||||
value={option}
|
||||
onChange={e => {
|
||||
setOption(e.target.value);
|
||||
}}>
|
||||
<option value="RECEIVE">받은 우편</option>
|
||||
<option value="SEND">보낸 우편</option>
|
||||
</SelectInput>
|
||||
</SelectWrapper>
|
||||
{option === 'RECEIVE' && (
|
||||
<>
|
||||
{/* <CheckWrapper>
|
||||
<CheckBox id="viewDelReceiveMail" label="삭제 우편 보기" />
|
||||
</CheckWrapper> */}
|
||||
<UserTableWrapper>
|
||||
<UserDefaultTable>
|
||||
<thead>
|
||||
<tr>
|
||||
<th width="170">수신일자</th>
|
||||
<th width="200">발송자</th>
|
||||
<th width="300">우편 제목</th>
|
||||
<th width="80">상태</th>
|
||||
<th width="100">첨부 아이템</th>
|
||||
<th width="80">수령</th>
|
||||
<th width="80">시스템 우편</th>
|
||||
{/* <th width="170">수령 일자</th> */}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{dataList.mail_list &&
|
||||
dataList.mail_list.map((mail, idx) => {
|
||||
return (
|
||||
<tr key={idx}>
|
||||
<td>{convertKTC(mail.create_time)}</td>
|
||||
<td>{mail.sender_nickname}</td>
|
||||
<td>
|
||||
<MailLink
|
||||
onClick={e => {
|
||||
e.preventDefault();
|
||||
handleModalView('detail');
|
||||
handleDetail(mail.title, mail.content, mail.item_list, mail.mail_guid);
|
||||
}}>
|
||||
{mail.title}
|
||||
</MailLink>
|
||||
</td>
|
||||
<td>{mail.status === true ? '확인' : '미확인'}</td>
|
||||
<td>{mail.item_list.length > 0 ? 'Y' : 'N'}</td>
|
||||
<td>{mail.is_get_item === true ? '수령함' : '미수령함'}</td>
|
||||
<td>{mail.is_system_mail === true ? 'Y' : 'N'}</td>
|
||||
{/* <td>
|
||||
{mail.is_get_item_dt && String(new Date(new Date(mail.is_get_item_dt).setHours(new Date(mail.is_get_item_dt).getHours() + 9)).toLocaleString())}
|
||||
</td> */}
|
||||
</tr>
|
||||
);
|
||||
})}
|
||||
</tbody>
|
||||
</UserDefaultTable>
|
||||
</UserTableWrapper>
|
||||
</>
|
||||
)}
|
||||
{option === 'SEND' && (
|
||||
<>
|
||||
{/* <CheckWrapper>
|
||||
<CheckBox id="viewDelSendMail" label="삭제 우편 보기" />
|
||||
</CheckWrapper> */}
|
||||
<UserTableWrapper>
|
||||
<UserDefaultTable>
|
||||
<thead>
|
||||
<tr>
|
||||
<th width="170">발송 일자</th>
|
||||
<th width="200">수신자</th>
|
||||
<th width="300">우편 제목</th>
|
||||
<th width="120">첨부 아이템</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{dataList.mail_list &&
|
||||
dataList.mail_list.map((mail, idx) => {
|
||||
return (
|
||||
<tr key={idx}>
|
||||
<td>{convertKTC(mail.create_time)}</td>
|
||||
<td>{mail.receiver_nickname}</td>
|
||||
<td>
|
||||
<MailLink
|
||||
onClick={e => {
|
||||
e.preventDefault();
|
||||
handleModalView('detail');
|
||||
handleDetail(mail.title, mail.content, mail.item_list, mail.mail_guid);
|
||||
}}>
|
||||
{mail.title}
|
||||
</MailLink>
|
||||
</td>
|
||||
<td>{mail.item_list.length > 0 ? 'Y' : 'N'}</td>
|
||||
</tr>
|
||||
);
|
||||
})}
|
||||
</tbody>
|
||||
</UserDefaultTable>
|
||||
</UserTableWrapper>
|
||||
</>
|
||||
)}
|
||||
{/*상세*/}
|
||||
<MailDetailModal
|
||||
mailModal={modalState.detailModal}
|
||||
handleClick={() => handleModalClose('detail')}
|
||||
setDetail={setDetail}
|
||||
content={detail}
|
||||
handleDelete={() => handleModalSubmit('detail')}
|
||||
handleItemDelete={(param) => handleModalSubmit('deleteItem', param)}
|
||||
authDelete={authDelete}
|
||||
/>
|
||||
{/*메일 삭제 모달*/}
|
||||
<ConfirmModal
|
||||
modalText={t('USER_MAIL_DEL_CONFIRM')}
|
||||
view={modalState.deleteSubmitModal}
|
||||
handleSubmit={() => handleModalSubmit('deleteSubmit')}
|
||||
handleCancel={() => handleModalClose('deleteSubmit')}
|
||||
handleClose={() => handleModalClose('deleteSubmit')}
|
||||
/>
|
||||
<CompletedModal
|
||||
view={modalState.deleteCompleteModal}
|
||||
modalText={t('DEL_COMPLETE')}
|
||||
handleComplete={() => handleModalSubmit('deleteComplete')}
|
||||
/>
|
||||
{/*메일 아이템 삭제 모달*/}
|
||||
<CustomConfirmModal
|
||||
ChildView={ConfirmChild}
|
||||
view={modalState.deleteItemModal}
|
||||
handleSubmit={() => handleModalSubmit('deleteItemSubmit')}
|
||||
handleCancel={() => handleModalClose('deleteItem')}
|
||||
handleClose={() => handleModalClose('deleteItem')}
|
||||
/>
|
||||
<CompletedModal
|
||||
view={modalState.deleteItemCompleteModal}
|
||||
modalText={t('DEL_ITEM_COMPLETE')}
|
||||
handleComplete={() => handleModalSubmit('deleteItemComplete')}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default UserMailInfo;
|
||||
|
||||
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;
|
||||
}
|
||||
`;
|
||||
|
||||
const QuestTable = styled(UserDefaultTable)`
|
||||
tbody {
|
||||
td {
|
||||
padding: 9px 12px;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const UserTableWrapper = styled.div`
|
||||
width: 100%;
|
||||
overflow: auto;
|
||||
margin-bottom: 40px;
|
||||
${UserDefaultTable}, ${QuestTable} {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
&::-webkit-scrollbar {
|
||||
height: 4px;
|
||||
}
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background: #666666;
|
||||
}
|
||||
&::-webkit-scrollbar-track {
|
||||
background: #d9d9d9;
|
||||
}
|
||||
`;
|
||||
|
||||
const MailLink = styled.div`
|
||||
color: #61a2d0;
|
||||
text-decoration: underline;
|
||||
`;
|
||||
|
||||
const SelectWrapper = styled.div`
|
||||
select {
|
||||
height: 30px;
|
||||
}
|
||||
margin-bottom: 10px;
|
||||
`;
|
||||
const CheckWrapper = styled.div`
|
||||
text-align: right;
|
||||
padding: 10px;
|
||||
margin-top: -40px;
|
||||
height: 30px;
|
||||
margin-bottom: 10px;
|
||||
font-size: 14px;
|
||||
font-weight: 700;
|
||||
`;
|
||||
|
||||
const InputItem = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center; /* 수평 중앙 정렬 */
|
||||
gap: 10px;
|
||||
${TextInput} {
|
||||
height: 35px;
|
||||
font-size: 14px;
|
||||
width: 100px;
|
||||
padding: 10px 20px;
|
||||
}
|
||||
`;
|
||||
70
src/components/DataManage/UserMyHomeInfo.js
Normal file
@@ -0,0 +1,70 @@
|
||||
import styled from 'styled-components';
|
||||
import Button from '../common/button/Button';
|
||||
import { InfoSubTitle, UserDefaultTable, UserInfoTable, UserTableWrapper } from '../../styles/ModuleComponents';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { authList } from '../../store/authList';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { TableSkeleton } from '../Skeleton/TableSkeleton';
|
||||
import { UserInventoryView, UserMyhomeView } from '../../apis';
|
||||
|
||||
const UserMyHomeInfo = ({ userInfo }) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const [dataList, setDataList] = useState();
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
fetchData();
|
||||
}, []);
|
||||
|
||||
const fetchData = async () => {
|
||||
const token = sessionStorage.getItem('token');
|
||||
await UserMyhomeView(token, userInfo.guid).then(data => {
|
||||
setDataList(data.myhome_info);
|
||||
setLoading(false);
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
loading ? <TableSkeleton count={15}/> :
|
||||
dataList &&
|
||||
<>
|
||||
<UserInfoTable $maxwidth="700px">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>마이 홈명</th>
|
||||
<td>{dataList.myhome_name}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</UserInfoTable>
|
||||
<InfoSubTitle top='30px'>배치 소품</InfoSubTitle>
|
||||
<UserTableWrapper>
|
||||
<UserDefaultTable>
|
||||
<thead>
|
||||
<tr>
|
||||
<th width="80">no.</th>
|
||||
<th width="120">아이템ID</th>
|
||||
<th width="50%">아이템명</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{dataList.prop_list && dataList.prop_list.map((el, idx) => {
|
||||
return (
|
||||
<tr key={idx}>
|
||||
<td>{idx + 1}</td>
|
||||
<td>{el.item_id}</td>
|
||||
<td>{el.item_name}</td>
|
||||
</tr>
|
||||
);
|
||||
})}
|
||||
</tbody>
|
||||
</UserDefaultTable>
|
||||
</UserTableWrapper>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default UserMyHomeInfo;
|
||||
|
||||
|
||||
130
src/components/DataManage/UserQuestInfo.js
Normal file
@@ -0,0 +1,130 @@
|
||||
import { useState, useEffect, Fragment } from 'react';
|
||||
|
||||
import styled from 'styled-components';
|
||||
import Button from '../../components/common/button/Button';
|
||||
import QuestDetailModal from '../../components/DataManage/QuestDetailModal';
|
||||
import { UserQuestView } from '../../apis/Users';
|
||||
import { convertKTC } from '../../utils';
|
||||
import { TableSkeleton } from '../Skeleton/TableSkeleton';
|
||||
|
||||
const UserQuestInfo = ({ userInfo }) => {
|
||||
const [detailPop, setDetailPop] = useState('hidden');
|
||||
const [dataList, setDataList] = useState({});
|
||||
const [detailQuest, setDetailQuest] = useState({});
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
fetchData();
|
||||
}, []);
|
||||
|
||||
const fetchData = async () => {
|
||||
const token = sessionStorage.getItem('token');
|
||||
await UserQuestView(token, userInfo.guid).then(data => {
|
||||
setDataList(data);
|
||||
setLoading(false);
|
||||
});
|
||||
};
|
||||
|
||||
const handleClick = data => {
|
||||
if (detailPop === 'hidden') {
|
||||
setDetailPop('view');
|
||||
setDetailQuest(data.detailQuest);
|
||||
} else setDetailPop('hidden');
|
||||
};
|
||||
|
||||
return (
|
||||
loading ? <TableSkeleton /> :
|
||||
<>
|
||||
<UserTableWrapper>
|
||||
<QuestTable>
|
||||
<thead>
|
||||
<tr>
|
||||
<th width="80">퀘스트 ID</th>
|
||||
<th width="40%">퀘스트명</th>
|
||||
<th width="100">타입</th>
|
||||
<th width="80">상태</th>
|
||||
<th width="150">수령 시간</th>
|
||||
<th width="150">완료 시간</th>
|
||||
<th width="80">상세보기</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{dataList.quest_list &&
|
||||
dataList.quest_list.map((el, index) => {
|
||||
return (
|
||||
<Fragment key={index}>
|
||||
<tr>
|
||||
<td>{el.quest_id}</td>
|
||||
<td>{el.quest_name}</td>
|
||||
<td>{el.quest_type}</td>
|
||||
<td>{el.status}</td>
|
||||
<td>{convertKTC(el.quest_assign_time, false)}</td>
|
||||
<td>{convertKTC(el.quest_complete_time, false)}</td>
|
||||
<td>
|
||||
<Button text="상세보기" theme="line" handleClick={() => handleClick(el)} />
|
||||
</td>
|
||||
</tr>
|
||||
</Fragment>
|
||||
);
|
||||
})}
|
||||
</tbody>
|
||||
</QuestTable>
|
||||
</UserTableWrapper>
|
||||
<QuestDetailModal detailPop={detailPop} handleClick={handleClick} detailQuest={detailQuest} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
export default UserQuestInfo;
|
||||
|
||||
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;
|
||||
}
|
||||
`;
|
||||
|
||||
const QuestTable = styled(UserDefaultTable)`
|
||||
tbody {
|
||||
td {
|
||||
padding: 9px 12px;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const UserTableWrapper = styled.div`
|
||||
width: 100%;
|
||||
overflow: auto;
|
||||
margin-bottom: 40px;
|
||||
${UserDefaultTable}, ${QuestTable} {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
&::-webkit-scrollbar {
|
||||
height: 4px;
|
||||
}
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background: #666666;
|
||||
}
|
||||
&::-webkit-scrollbar-track {
|
||||
background: #d9d9d9;
|
||||
}
|
||||
`;
|
||||
57
src/components/DataManage/UserSearchBar.js
Normal file
@@ -0,0 +1,57 @@
|
||||
import { styled } from 'styled-components';
|
||||
import Button from '../../components/common/button/Button';
|
||||
|
||||
import { FormWrapper, InputLabel, TextInput, SelectInput, BtnWrapper } from '../../styles/Components';
|
||||
|
||||
const UserSearchBar = () => {
|
||||
return (
|
||||
<>
|
||||
<FormWrapper>
|
||||
<SearchbarStyle>
|
||||
<SearchItem>
|
||||
<InputLabel>유저 조회</InputLabel>
|
||||
<TextInput type="text" width="300px" placeholder="조회 대상 유저의 GUID를 입력하세요." />
|
||||
</SearchItem>
|
||||
<BtnWrapper $gap="8px">
|
||||
<Button theme="reset" />
|
||||
<Button
|
||||
theme="primary"
|
||||
text="검색"
|
||||
handleClick={e => {
|
||||
e.preventDefault();
|
||||
}}
|
||||
/>
|
||||
</BtnWrapper>
|
||||
</SearchbarStyle>
|
||||
</FormWrapper>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default UserSearchBar;
|
||||
|
||||
const SearchbarStyle = styled.div`
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 20px 0;
|
||||
font-size: 14px;
|
||||
padding: 20px;
|
||||
border-top: 1px solid #000;
|
||||
border-bottom: 1px solid #000;
|
||||
margin-bottom: 40px;
|
||||
`;
|
||||
const SearchItem = styled.div`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 20px;
|
||||
margin-right: 50px;
|
||||
|
||||
${TextInput}, ${SelectInput} {
|
||||
height: 35px;
|
||||
}
|
||||
${TextInput} {
|
||||
padding: 0 10px;
|
||||
max-width: 400px;
|
||||
}
|
||||
`;
|
||||
115
src/components/DataManage/UserTattooInfo.js
Normal file
@@ -0,0 +1,115 @@
|
||||
import { Fragment } from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { useState, useEffect } from 'react';
|
||||
import { UserTattooView } from '../../apis/Users';
|
||||
import { TableSkeleton } from '../Skeleton/TableSkeleton';
|
||||
|
||||
const UserTatttooInfo = ({ userInfo }) => {
|
||||
const [dataList, setDataList] = useState();
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
fetchData();
|
||||
}, []);
|
||||
|
||||
const fetchData = async () => {
|
||||
const token = sessionStorage.getItem('token');
|
||||
await UserTattooView(token, userInfo.guid).then(data => {
|
||||
setDataList(data);
|
||||
setLoading(false);
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
loading ? <TableSkeleton count={10}/> :
|
||||
<>
|
||||
<UserTableWrapper>
|
||||
<UserDefaultTable>
|
||||
<thead>
|
||||
<tr>
|
||||
<th width="100">장착슬롯</th>
|
||||
<th width="150">타투 ID</th>
|
||||
<th width="200">타투명</th>
|
||||
{/*<th width="100">등급</th>*/}
|
||||
<th width="100">레벨</th>
|
||||
{/*<th width="300">속성 및 수치</th>*/}
|
||||
{/*<th width="200">세트 효과</th>*/}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{dataList && dataList.tattoo_list &&
|
||||
dataList.tattoo_list.map((el, idx) => {
|
||||
return (
|
||||
<Fragment key={idx}>
|
||||
<tr>
|
||||
<td>{el.slot}</td>
|
||||
<td>{el.item_id}</td>
|
||||
<td>{el.item_name}</td>
|
||||
{/*<td>{el.grade}</td>*/}
|
||||
<td>{el.level}</td>
|
||||
{/*<td>{el.property}</td>*/}
|
||||
{/*<td>{el.setEffect}</td>*/}
|
||||
</tr>
|
||||
</Fragment>
|
||||
);
|
||||
})}
|
||||
</tbody>
|
||||
</UserDefaultTable>
|
||||
</UserTableWrapper>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default UserTatttooInfo;
|
||||
|
||||
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;
|
||||
}
|
||||
`;
|
||||
|
||||
const QuestTable = styled(UserDefaultTable)`
|
||||
tbody {
|
||||
td {
|
||||
padding: 9px 12px;
|
||||
}
|
||||
}
|
||||
`;
|
||||
const UserTableWrapper = styled.div`
|
||||
width: 100%;
|
||||
overflow: auto;
|
||||
margin-bottom: 40px;
|
||||
${UserDefaultTable}, ${QuestTable} {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
&::-webkit-scrollbar {
|
||||
height: 4px;
|
||||
}
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background: #666666;
|
||||
}
|
||||
&::-webkit-scrollbar-track {
|
||||
background: #d9d9d9;
|
||||
}
|
||||
`;
|
||||
98
src/components/DataManage/UserToolInfo.js
Normal file
@@ -0,0 +1,98 @@
|
||||
import styled from 'styled-components';
|
||||
import { useState, useEffect } from 'react';
|
||||
import { UserToolView } from '../../apis/Users';
|
||||
import { TableSkeleton } from '../Skeleton/TableSkeleton';
|
||||
|
||||
const UserToolInfo = ({ userInfo }) => {
|
||||
const [dataList, setDataList] = useState();
|
||||
const [rowData, setRowData] = useState([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
fetchData();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if(dataList && dataList.slot_list)
|
||||
setRowData([
|
||||
{ title: '1번 슬롯', itemNo: dataList.slot_list.slot1?.tool_id, itemName: dataList?.slot_list.slot1?.tool_name },
|
||||
{ title: '2번 슬롯', itemNo: dataList.slot_list.slot2?.tool_id, itemName: dataList?.slot_list.slot2?.tool_name },
|
||||
{ title: '3번 슬롯', itemNo: dataList.slot_list.slot3?.tool_id, itemName: dataList?.slot_list.slot3?.tool_name },
|
||||
{ title: '4번 슬롯', itemNo: dataList.slot_list.slot4?.tool_id, itemName: dataList?.slot_list.slot4?.tool_name },
|
||||
])
|
||||
}, [dataList])
|
||||
|
||||
const fetchData = async () => {
|
||||
const token = sessionStorage.getItem('token');
|
||||
await UserToolView(token, userInfo.guid).then(data => {
|
||||
setDataList(data);
|
||||
setLoading(false);
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
loading ? <TableSkeleton width='30%' count={4} /> :
|
||||
<>
|
||||
<ToolWrapper>
|
||||
<UserInfoTable $maxwidth="570px">
|
||||
<colgroup>
|
||||
<col width="120" />
|
||||
<col width="30%" />
|
||||
<col width="70%" />
|
||||
</colgroup>
|
||||
<tbody>
|
||||
{rowData && rowData.map((el, idx) => {
|
||||
return (
|
||||
<tr key={idx}>
|
||||
<th>{el.title}</th>
|
||||
<td>{el.itemNo}</td>
|
||||
<td>{el.itemName}</td>
|
||||
</tr>
|
||||
);
|
||||
})}
|
||||
</tbody>
|
||||
</UserInfoTable>
|
||||
</ToolWrapper>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default UserToolInfo;
|
||||
|
||||
const UserInfoTable = styled.table`
|
||||
width: 100%;
|
||||
max-width: ${props => props.$maxwidth || 'auto'};
|
||||
font-size: 13px;
|
||||
border-radius: 15px;
|
||||
overflow: hidden;
|
||||
tr:first-child {
|
||||
th,
|
||||
td {
|
||||
border-top: 0;
|
||||
}
|
||||
}
|
||||
th,
|
||||
td {
|
||||
height: 36px;
|
||||
vertical-align: middle;
|
||||
border-top: 1px solid #d9d9d9;
|
||||
}
|
||||
th {
|
||||
width: 120px;
|
||||
background: #888;
|
||||
color: #fff;
|
||||
font-weight: 700;
|
||||
}
|
||||
td {
|
||||
background: #fff;
|
||||
padding: 0 20px;
|
||||
}
|
||||
`;
|
||||
|
||||
const ToolWrapper = styled.div`
|
||||
${UserInfoTable} {
|
||||
td {
|
||||
border-left: 1px solid #d9d9d9;
|
||||
}
|
||||
}
|
||||
`;
|
||||
216
src/components/DataManage/UserViewSearchBar.js
Normal file
@@ -0,0 +1,216 @@
|
||||
import { styled } from 'styled-components';
|
||||
import { useState, useEffect } from 'react';
|
||||
import { TextInput, SelectInput, InputLabel, FormWrapper, BtnWrapper, ButtonClose } from '../../styles/Components';
|
||||
import Button from '../../components/common/button/Button';
|
||||
import Modal from '../../components/common/modal/Modal';
|
||||
import { UserView } from '../../apis';
|
||||
|
||||
const UserViewSearchBar = ({ setInfoView, handleTab, setResultData, resultData }) => {
|
||||
const [modalState, setModalState] = useState('hidden');
|
||||
const [form, setForm] = useState('');
|
||||
const [isNullValue, setIsNullValue] = useState(false);
|
||||
const [message, setMessage] = useState('필수값을 입력해주세요.');
|
||||
const [dataLength, setDataLength] = useState(false);
|
||||
const [dataList, setDataList] = useState([]);
|
||||
const [searchData, setSearchData] = useState({
|
||||
searchType: 'DEFAULT',
|
||||
searchKey: '',
|
||||
});
|
||||
|
||||
// console.log(dataList)
|
||||
|
||||
const searchType = [
|
||||
{ value: 'DEFAULT', name: '선택' },
|
||||
{ value: 'NAME', name: '아바타명' },
|
||||
{ value: 'GUID', name: 'GUID' },
|
||||
{ value: 'ACCOUNT', name: 'Account ID' },
|
||||
{ value: 'FRIEND_CODE', name: '친구코드' },
|
||||
];
|
||||
|
||||
const fetchData = async (searchType, searchKey) => {
|
||||
const token = sessionStorage.getItem('token');
|
||||
setDataList(await UserView(token, searchType, searchKey));
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
dataList && setDataLength(Object.values(dataList).length);
|
||||
}, [dataList]);
|
||||
|
||||
const handleSearch = (searchType, searchKey) => {
|
||||
fetchData(searchType, searchKey);
|
||||
};
|
||||
|
||||
const handleReset = (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
setSearchData({
|
||||
searchType: 'DEFAULT',
|
||||
searchKey: '',
|
||||
});
|
||||
setInfoView('none');
|
||||
window.location.reload();
|
||||
};
|
||||
|
||||
const handleRegistModalClose = (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
if (searchData.searchKey.length === 0 || searchData.searchType === 'DEFAULT') {
|
||||
setMessage(searchData.searchType === 'DEFAULT' ? '항목을 선택해주세요.' : '필수값을 입력해주세요.');
|
||||
setIsNullValue(true);
|
||||
} else if (searchData.searchKey && modalState === 'hidden') {
|
||||
setIsNullValue(false);
|
||||
|
||||
handleSearch(searchData.searchType, searchData.searchKey);
|
||||
setModalState('view');
|
||||
} else {
|
||||
setModalState('hidden');
|
||||
}
|
||||
};
|
||||
|
||||
const handleModal = () => {
|
||||
if (modalState === 'hidden') {
|
||||
setModalState('view');
|
||||
} else {
|
||||
setInfoView('flex');
|
||||
handleTab('기본정보');
|
||||
setModalState('hidden');
|
||||
}
|
||||
};
|
||||
|
||||
const handleModalClose = () => {
|
||||
if (modalState === 'hidden') {
|
||||
setModalState('view');
|
||||
} else {
|
||||
setModalState('hidden');
|
||||
}
|
||||
};
|
||||
|
||||
const handleGuid = data => {
|
||||
// setResultData(data.guid);
|
||||
setResultData(data);
|
||||
handleModal();
|
||||
};
|
||||
|
||||
|
||||
|
||||
return (
|
||||
<>
|
||||
<FormWrapper onSubmit={e => handleRegistModalClose(e)}>
|
||||
<SearchbarStyle>
|
||||
<SearchItem>
|
||||
<InputLabel>유저 조회</InputLabel>
|
||||
<InputGroup>
|
||||
<SelectInput
|
||||
value={searchData.searchType}
|
||||
onChange={e => { setSearchData({ ...searchData, searchType: e.target.value }); setDataList([])}}
|
||||
>
|
||||
{searchType.map((data, index) => (
|
||||
<option key={index} value={data.value} disabled={data.value === 'FRIEND_CODE' ? true : false}>
|
||||
{data.name}
|
||||
</option>
|
||||
))}
|
||||
</SelectInput>
|
||||
<TextInput
|
||||
type="text"
|
||||
width="300px"
|
||||
value={searchData.searchKey}
|
||||
onChange={e => setSearchData({ ...searchData, searchKey: e.target.value })}
|
||||
disabled={searchData.searchType === 'DEFAULT' ? true : false}
|
||||
/>
|
||||
</InputGroup>
|
||||
</SearchItem>
|
||||
<BtnWrapper $gap="8px">
|
||||
<Button type="button" theme="reset" handleClick={e => handleReset(e)} />
|
||||
<Button type="button"
|
||||
theme="search" text="검색"
|
||||
handleClick={e => handleRegistModalClose(e)}
|
||||
/>
|
||||
{isNullValue && <SearchBarAlert>{message}</SearchBarAlert>}
|
||||
</BtnWrapper>
|
||||
</SearchbarStyle>
|
||||
</FormWrapper>
|
||||
<Modal $view={modalState} $bgcolor="transparent" min="600px">
|
||||
<BtnWrapper $justify="space-between">
|
||||
<PopTitle className="title">검색 대상 선택</PopTitle>
|
||||
<ButtonClose onClick={handleModalClose} />
|
||||
</BtnWrapper>
|
||||
<PopCategory>{searchType.map(data => data.value === searchData.searchType && data.name)}</PopCategory>
|
||||
<PopResult>
|
||||
|
||||
{dataLength === 0 ? (
|
||||
<ResultItem>일치하는 조회 대상이 없습니다.</ResultItem>
|
||||
) : searchData.searchType === 'NAME' ? (
|
||||
<ResultItem onClick={() => handleGuid(dataList)}>{dataList.nickname && dataList.nickname}</ResultItem>
|
||||
) : (
|
||||
<ResultItem onClick={() => handleGuid(dataList)}>{dataList.guid && dataList.guid}</ResultItem>
|
||||
)}
|
||||
</PopResult>
|
||||
</Modal>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default UserViewSearchBar;
|
||||
|
||||
const SearchbarStyle = styled.div`
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 20px 0;
|
||||
font-size: 14px;
|
||||
padding: 20px;
|
||||
border-top: 1px solid #000;
|
||||
border-bottom: 1px solid #000;
|
||||
margin-bottom: 40px;
|
||||
`;
|
||||
|
||||
const SearchItem = styled.div`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 20px;
|
||||
margin-right: 50px;
|
||||
|
||||
${TextInput}, ${SelectInput} {
|
||||
height: 35px;
|
||||
}
|
||||
${TextInput} {
|
||||
padding: 0 10px;
|
||||
max-width: 400px;
|
||||
}
|
||||
`;
|
||||
|
||||
const InputGroup = styled.div`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
`;
|
||||
|
||||
const PopTitle = styled.div`
|
||||
font-weight: 700;
|
||||
margin-bottom: 10px;
|
||||
font-size: 18px;
|
||||
`;
|
||||
const PopCategory = styled.div`
|
||||
background: #818181;
|
||||
color: #fff;
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
padding: 5px 10px;
|
||||
`;
|
||||
const PopResult = styled.div`
|
||||
border-bottom: 1px solid #000;
|
||||
`;
|
||||
const ResultItem = styled.div`
|
||||
padding: 4px 10px;
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
background: #f6f6f6;
|
||||
}
|
||||
`;
|
||||
|
||||
const SearchBarAlert = styled.div`
|
||||
width: 100%;
|
||||
color: #d60000;
|
||||
margin: auto;
|
||||
padding-left: 15px;
|
||||
`;
|
||||
312
src/components/IndexManage/CreditContent.js
Normal file
@@ -0,0 +1,312 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { styled } from 'styled-components';
|
||||
import Button from '../../components/common/button/Button';
|
||||
|
||||
import { CurrencyIndexExport, CurrencyIndexView } from '../../apis';
|
||||
import { SelectInput, TableStyle, TableInfo, ListOption, InputLabel } from '../../styles/Components';
|
||||
|
||||
import CreditSeacrhBar from '../../components/IndexManage/CreditSearchBar';
|
||||
import { uniqBy } from 'lodash';
|
||||
import { sumBy } from 'lodash';
|
||||
|
||||
const CreditContent = () => {
|
||||
const token = sessionStorage.getItem('token');
|
||||
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 CURRENCY_LIST = [
|
||||
{ "key": "Gold", "name": "골드" },
|
||||
{ "key": "Sapphire", "name": "사파이어" },
|
||||
{ "key": "Calium", "name": "칼리움" },
|
||||
{ "key": "Onyxium", "name": "오닉시움" }
|
||||
];
|
||||
|
||||
const [sendDate, setSendDate] = useState(START_DATE);
|
||||
const [finishDate, setFinishDate] = useState(END_DATE);
|
||||
const [currencyType, setCurrencyType] = useState('Gold');
|
||||
const [currencyText, setCurrencyText] = useState('골드');
|
||||
|
||||
const [dataList, setDataList] = useState([]);
|
||||
const [routeData, setRouteData] = useState([]);
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
fetchData(sendDate, finishDate, currencyType);
|
||||
}, [currencyType]);
|
||||
|
||||
const fetchData = async (startDate, endDate) => {
|
||||
const newStartDate = new Date(startDate);
|
||||
const newEndDate = new Date(endDate);
|
||||
|
||||
const startDateToLocal =
|
||||
newStartDate.getFullYear() +
|
||||
'-' +
|
||||
(newStartDate.getMonth() + 1 < 9 ? '0' + (newStartDate.getMonth() + 1) : newStartDate.getMonth() + 1) +
|
||||
'-' +
|
||||
(newStartDate.getDate() < 9 ? '0' + newStartDate.getDate() : newStartDate.getDate());
|
||||
|
||||
const endDateToLocal =
|
||||
newEndDate.getFullYear() +
|
||||
'-' +
|
||||
(newEndDate.getMonth() + 1 < 9 ? '0' + (newEndDate.getMonth() + 1) : newEndDate.getMonth() + 1) +
|
||||
'-' +
|
||||
(newEndDate.getDate() < 9 ? '0' + newEndDate.getDate() : newEndDate.getDate());
|
||||
|
||||
setDataList(await CurrencyIndexView(token, startDateToLocal, endDateToLocal, currencyType));
|
||||
|
||||
|
||||
setSendDate(startDateToLocal);
|
||||
setFinishDate(endDateToLocal);
|
||||
setRoutArray(await CurrencyIndexView(token, startDateToLocal, endDateToLocal, currencyType));
|
||||
};
|
||||
|
||||
const handleCurrencyChange = e => {
|
||||
let value = e.target.value;
|
||||
setCurrencyType(value);
|
||||
CURRENCY_LIST.filter(data => data.key === value).map(data => {
|
||||
setCurrencyText(data.name);
|
||||
});
|
||||
};
|
||||
|
||||
//route data
|
||||
const setRoutArray = async (data) => {
|
||||
let routeArray = [];
|
||||
let routeAcqArr = [];
|
||||
let routeConArr = [];
|
||||
|
||||
//생산량 route
|
||||
data.list && data.list[0].daily_data.filter(routeData => routeData.delta_type === 'ACQUIRE').map(routeData => {
|
||||
routeData.data.map(routeData => {
|
||||
if(!routeAcqArr.includes(routeData.route) ){
|
||||
routeAcqArr.push(routeData.route);
|
||||
}
|
||||
})
|
||||
});
|
||||
routeArray.ACQUIRE = routeAcqArr;
|
||||
|
||||
//소진량 route
|
||||
data.list && data.list[0].daily_data.filter(routeData => routeData.delta_type === 'CONSUME').map(routeData => {
|
||||
routeData.data.map(routeData => {
|
||||
if(!routeConArr.includes(routeData.route) ){
|
||||
routeConArr.push(routeData.route);
|
||||
}
|
||||
})
|
||||
});
|
||||
routeArray.CONSUME = routeConArr;
|
||||
|
||||
setRouteData(routeArray);
|
||||
};
|
||||
|
||||
// 엑셀 다운로드
|
||||
const handleXlsxExport = () => {
|
||||
const fileName = 'Caliverse_Credit_Index.xlsx';
|
||||
CurrencyIndexExport(token, fileName, sendDate, finishDate, currencyType);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
<CreditSeacrhBar fetchData={fetchData} />
|
||||
<TableInfo2>
|
||||
<SelectInput onChange={handleCurrencyChange}>
|
||||
{CURRENCY_LIST.map((item, index) => (
|
||||
<option value={item.key} key={index}>
|
||||
{item.name}
|
||||
</option>
|
||||
))}
|
||||
</SelectInput>
|
||||
<ListOption>
|
||||
<Button theme="line" text="엑셀 다운로드" handleClick={handleXlsxExport} />
|
||||
</ListOption>
|
||||
</TableInfo2>
|
||||
<TableWrapper>
|
||||
<EconomicTable>
|
||||
<thead>
|
||||
<tr>
|
||||
<th colSpan="2" className="text-center" width="300">
|
||||
Product
|
||||
</th>
|
||||
{dataList.list && uniqBy(dataList.list, 'date').map(data =>
|
||||
<th width="160" key={data.date}>{data.date}</th>
|
||||
)}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<TableTitle colSpan="2">(Total) {currencyText} 생산량</TableTitle>
|
||||
{dataList.list &&
|
||||
dataList.list.map((data) =>
|
||||
(data.total).filter(totalData => data.date === totalData.date && totalData.delta_type === 'ACQUIRE')
|
||||
.map((totalData, index) => (
|
||||
<TableData key={index}
|
||||
$state={totalData.dif !== "" && totalData.dif !== "Infinity" ? "danger" : ""}>
|
||||
{totalData.quantity}
|
||||
{
|
||||
totalData.dif !== "" && totalData.dif !== "Infinity"
|
||||
? (<span>({totalData.dif})</span>)
|
||||
: ("")
|
||||
}
|
||||
</TableData>
|
||||
)
|
||||
))
|
||||
}
|
||||
</tr>
|
||||
<tr>
|
||||
<TableTitle colSpan="2">(Total) {currencyText} 소진량</TableTitle>
|
||||
{dataList.list &&
|
||||
dataList.list.map((data) =>
|
||||
(data.total).filter(totalData => data.date === totalData.date && totalData.delta_type === 'CONSUME')
|
||||
.map((totalData, index) => (
|
||||
<TableData key={index}
|
||||
$state={totalData.dif !== "" && totalData.dif !== "Infinity" ? "danger" : ""}>
|
||||
{totalData.quantity}
|
||||
{
|
||||
totalData.dif !== "" && totalData.dif !== "Infinity"
|
||||
? (<span>({totalData.dif})</span>)
|
||||
: ("")
|
||||
}
|
||||
</TableData>
|
||||
)
|
||||
))
|
||||
}
|
||||
</tr>
|
||||
<tr>
|
||||
<TableTitle colSpan="2">(Total) {currencyText} 보유량</TableTitle>
|
||||
{dataList.list &&
|
||||
dataList.list.map((data, index) => (
|
||||
<TableData key={index}>
|
||||
{sumBy(
|
||||
data.total.filter(totalData => data.date === totalData.date && totalData.delta_type === 'ACQUIRE'),
|
||||
'quantity',
|
||||
) -
|
||||
sumBy(
|
||||
data.total.filter(totalData => data.date === totalData.date && totalData.delta_type === 'CONSUME'),
|
||||
'quantity',
|
||||
)}
|
||||
</TableData>
|
||||
))
|
||||
}
|
||||
</tr>
|
||||
{/* 획득 GET */}
|
||||
{
|
||||
routeData.ACQUIRE && routeData.ACQUIRE.map((route, index) => (
|
||||
<tr key={index}>
|
||||
<TableTitle>GET</TableTitle>
|
||||
<TableTitle>{route}</TableTitle>
|
||||
{dataList.list &&
|
||||
dataList.list.map((data) =>
|
||||
data.daily_data.filter(dailyData => data.date === dailyData.date && dailyData.delta_type === 'ACQUIRE')
|
||||
.map(dailyData => (dailyData.data).filter(routeData => data.date === routeData.date && routeData.route === route)
|
||||
.map((routeData, i) => (
|
||||
<TableData key={i} data={routeData.date}>{routeData.quantity}</TableData>
|
||||
)))
|
||||
)
|
||||
}
|
||||
</tr>
|
||||
))
|
||||
}
|
||||
{/* 소진 USE CONSUME */}
|
||||
{
|
||||
routeData.CONSUME && routeData.CONSUME.map((route, index) => (
|
||||
<tr key={index}>
|
||||
<TableTitle>USE</TableTitle>
|
||||
<TableTitle>{route}</TableTitle>
|
||||
{dataList.list &&
|
||||
dataList.list.map((data) =>
|
||||
data.daily_data.filter(dailyData => data.date === dailyData.date && dailyData.delta_type === 'CONSUME')
|
||||
.map(dailyData => (dailyData.data).filter(routeData => data.date === routeData.date && routeData.route === route)
|
||||
.map((routeData, i) => (
|
||||
<TableData key={i} data={routeData.date}>{routeData.quantity}</TableData>
|
||||
)))
|
||||
)
|
||||
}
|
||||
</tr>
|
||||
))
|
||||
}
|
||||
</tbody>
|
||||
</EconomicTable>
|
||||
</TableWrapper>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default CreditContent;
|
||||
|
||||
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: 900px;
|
||||
th {
|
||||
&.cell-nru {
|
||||
background: #f0f0f0;
|
||||
border-left: 1px solid #aaa;
|
||||
border-right: 1px solid #aaa;
|
||||
}
|
||||
}
|
||||
td {
|
||||
&.blank {
|
||||
background: #f9f9f9;
|
||||
}
|
||||
&.cell-nru {
|
||||
background: #fafafa;
|
||||
border-left: 1px solid #aaa;
|
||||
border-right: 1px solid #aaa;
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const TableInfo2 = styled(TableInfo)`
|
||||
justify-content: space-between;
|
||||
${InputLabel} {
|
||||
font-size: 12px;
|
||||
}
|
||||
${SelectInput} {
|
||||
width: auto;
|
||||
min-width: 100px;
|
||||
height: 24px;
|
||||
}
|
||||
`;
|
||||
|
||||
const TableDate = styled.th`
|
||||
color: ${props => (props.$state === 'danger' ? '#d60000' : '#2c2c2c')};
|
||||
`;
|
||||
|
||||
const TableData = styled.td`
|
||||
|
||||
background: ${props => (props.$state === 'danger' ? '#d60000' : props.$state === 'blank' ? '#F9F9F9' : 'transparent')};
|
||||
color: ${props => (props.$state === 'danger' ? '#fff' : '#2c2c2c')};
|
||||
`;
|
||||
|
||||
const perData = styled.span`
|
||||
display: ${props => (props.$view === 'hidden' ? 'none' : 'block')};
|
||||
`;
|
||||
|
||||
const TableTitle = styled.td`
|
||||
text-align: center;
|
||||
`;
|
||||
|
||||
const EconomicTable = styled(TableStyle)`
|
||||
${TableData} {
|
||||
text-align: left;
|
||||
}
|
||||
tbody {
|
||||
tr:nth-child(1),
|
||||
tr:nth-child(2),
|
||||
tr:nth-child(3) {
|
||||
background: #f5fcff;
|
||||
}
|
||||
}
|
||||
`;
|
||||
113
src/components/IndexManage/CreditSearchBar.js
Normal file
@@ -0,0 +1,113 @@
|
||||
import { useState } from 'react';
|
||||
import { styled } from 'styled-components';
|
||||
import Button from '../../components/common/button/Button';
|
||||
|
||||
import DatePickerComponent from '../common/Date/DatePickerComponent';
|
||||
import 'react-datepicker/dist/react-datepicker.css';
|
||||
|
||||
import { FormWrapper, InputLabel, TextInput, SelectInput, BtnWrapper, InputGroup, DatePickerWrapper } from '../../styles/Components';
|
||||
|
||||
const CreditSeacrhBar = ({ fetchData }) => {
|
||||
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 [resultData, setResultData] = useState({
|
||||
send_dt: START_DATE,
|
||||
end_dt: END_DATE,
|
||||
});
|
||||
|
||||
// 발송 날짜 세팅 로직
|
||||
const handleSelectedDate = data => {
|
||||
const sendDate = new Date(data);
|
||||
const resultSendData = new Date(sendDate.getFullYear(), sendDate.getMonth(), sendDate.getDate());
|
||||
|
||||
setResultData({ ...resultData, send_dt: resultSendData });
|
||||
};
|
||||
|
||||
// 발송 날짜 세팅 로직
|
||||
const handleEndDate = data => {
|
||||
const endDate = new Date(data);
|
||||
const resultSendData = new Date(endDate.getFullYear(), endDate.getMonth(), endDate.getDate());
|
||||
|
||||
setResultData({ ...resultData, end_dt: resultSendData });
|
||||
};
|
||||
|
||||
const handleReset = e => {
|
||||
e.preventDefault();
|
||||
setResultData({ send_dt: START_DATE, end_dt: END_DATE });
|
||||
|
||||
fetchData(START_DATE, END_DATE);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<FormWrapper>
|
||||
<SearchbarStyle>
|
||||
<SearchItem>
|
||||
<InputLabel>집계 기간</InputLabel>
|
||||
<InputGroup>
|
||||
<DatePickerWrapper>
|
||||
<DatePickerComponent
|
||||
name="시작 일자" selectedDate={resultData.send_dt}
|
||||
handleSelectedDate={data => handleSelectedDate(data)}
|
||||
maxDate={new Date()} />
|
||||
<span>~</span>
|
||||
<DatePickerComponent
|
||||
name="종료 일자" selectedDate={resultData.end_dt}
|
||||
handleSelectedDate={data => handleEndDate(data)}
|
||||
pastDate = {resultData.send_dt}
|
||||
maxDate={new Date()} />
|
||||
</DatePickerWrapper>
|
||||
</InputGroup>
|
||||
</SearchItem>
|
||||
<BtnWrapper $gap="8px">
|
||||
<Button
|
||||
theme="reset"
|
||||
handleClick={e => {
|
||||
handleReset(e);
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
theme="search"
|
||||
text="집계"
|
||||
handleClick={e => {
|
||||
e.preventDefault();
|
||||
fetchData(resultData.send_dt, resultData.end_dt);
|
||||
}}
|
||||
/>
|
||||
</BtnWrapper>
|
||||
</SearchbarStyle>
|
||||
</FormWrapper>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default CreditSeacrhBar;
|
||||
|
||||
const SearchbarStyle = styled.div`
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 20px 0;
|
||||
font-size: 14px;
|
||||
padding: 20px;
|
||||
border-top: 1px solid #000;
|
||||
border-bottom: 1px solid #000;
|
||||
margin-bottom: 40px;
|
||||
`;
|
||||
|
||||
const SearchItem = styled.div`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 20px;
|
||||
margin-right: 50px;
|
||||
|
||||
${TextInput}, ${SelectInput} {
|
||||
height: 35px;
|
||||
}
|
||||
${TextInput} {
|
||||
padding: 0 10px;
|
||||
max-width: 400px;
|
||||
}
|
||||
`;
|
||||
110
src/components/IndexManage/DailyActiveUserContent.js
Normal file
@@ -0,0 +1,110 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
import Button from '../../components/common/button/Button';
|
||||
|
||||
import { TableStyle, TableInfo, ListOption, IndexTableWrap } from '../../styles/Components';
|
||||
import { DailySearchBar } from '../../components/IndexManage/index';
|
||||
import { DailyActiveUserExport, DailyActiveUserView } from '../../apis';
|
||||
|
||||
const PlayTimeContent = () => {
|
||||
const token = sessionStorage.getItem('token');
|
||||
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 [dataList, setDataList] = useState([]);
|
||||
const [resultData, setResultData] = useState([]);
|
||||
|
||||
const [sendDate, setSendDate] = useState(START_DATE);
|
||||
const [finishDate, setFinishDate] = useState(END_DATE);
|
||||
|
||||
useEffect(() => {
|
||||
fetchData(START_DATE, END_DATE);
|
||||
}, []);
|
||||
|
||||
// DAU 데이터
|
||||
const fetchData = async (startDate, endDate) => {
|
||||
const startDateToLocal =
|
||||
startDate.getFullYear() +
|
||||
'-' +
|
||||
(startDate.getMonth() + 1 < 9 ? '0' + (startDate.getMonth() + 1) : startDate.getMonth() + 1) +
|
||||
'-' +
|
||||
(startDate.getDate() < 9 ? '0' + startDate.getDate() : startDate.getDate());
|
||||
|
||||
const endDateToLocal =
|
||||
endDate.getFullYear() +
|
||||
'-' +
|
||||
(endDate.getMonth() + 1 < 9 ? '0' + (endDate.getMonth() + 1) : endDate.getMonth() + 1) +
|
||||
'-' +
|
||||
(endDate.getDate() < 9 ? '0' + endDate.getDate() : endDate.getDate());
|
||||
|
||||
// await DailyActiveUserView(token, startDateToLocal, endDateToLocal).then(data => {
|
||||
// console.log(data);
|
||||
// setDataList(data);
|
||||
// });
|
||||
|
||||
setSendDate(startDateToLocal);
|
||||
setFinishDate(endDateToLocal);
|
||||
};
|
||||
|
||||
// 검색 함수
|
||||
const handleSearch = (send_dt, end_dt) => {
|
||||
fetchData(send_dt, end_dt);
|
||||
};
|
||||
|
||||
// 엑셀 다운로드
|
||||
const handleXlsxExport = () => {
|
||||
const fileName = 'Caliverse_Dau.xlsx';
|
||||
DailyActiveUserExport(token, fileName, sendDate, finishDate);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<DailySearchBar setResultData={setResultData} resultData={resultData} handleSearch={handleSearch} fetchData={fetchData} />
|
||||
<TableInfo>
|
||||
<ListOption>
|
||||
<Button theme="line" text="엑셀 다운로드" handleClick={handleXlsxExport} />
|
||||
</ListOption>
|
||||
</TableInfo>
|
||||
<IndexTableWrap>
|
||||
<TableStyle>
|
||||
<caption></caption>
|
||||
<thead>
|
||||
<tr>
|
||||
<th rowSpan="1" width="45">
|
||||
일자
|
||||
</th>
|
||||
<th colSpan="1" width="30">
|
||||
DAU
|
||||
</th>
|
||||
{/*<th colSpan="1" width="30">*/}
|
||||
{/* DALC*/}
|
||||
{/*</th>*/}
|
||||
<th colSpan="1" width="30">
|
||||
DGLC
|
||||
</th>
|
||||
{/*<th colSpan="1" width="30">*/}
|
||||
{/* MaxAU*/}
|
||||
{/*</th>*/}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
{dataList && (dataList || []).map((data, index) => (
|
||||
<tr key={index}>
|
||||
<td>{data.date}</td>
|
||||
<td>{data.dau}</td>
|
||||
{/*<td>{data.dalc}</td>*/}
|
||||
<td>{data.dglc}</td>
|
||||
{/*<td>{data.maxAu}</td>*/}
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</TableStyle>
|
||||
</IndexTableWrap>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default PlayTimeContent;
|
||||
|
||||
170
src/components/IndexManage/DailyDashBoard.js
Normal file
@@ -0,0 +1,170 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { userTotalIndex } from '../../apis';
|
||||
|
||||
import { styled } from 'styled-components';
|
||||
import TitleArrow from '../../assets/img/icon/icon-title.png';
|
||||
import UpIcon from '../../assets/img/icon/icon-up.png';
|
||||
import DownIcon from '../../assets/img/icon/icon-down.png';
|
||||
|
||||
const DailyDashBoard = ({ content }) => {
|
||||
const [boardState, setBoardState] = useState('active');
|
||||
const [totalData, setTotalData] = useState([]);
|
||||
|
||||
const handleBoard = () => {
|
||||
if (boardState === 'active') {
|
||||
setBoardState('inactive');
|
||||
} else {
|
||||
setBoardState('active');
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
// 이용자 지표 총계 불러오기
|
||||
fetchData();
|
||||
}, []);
|
||||
|
||||
const fetchData = async () => {
|
||||
const token = sessionStorage.getItem('token');
|
||||
await userTotalIndex(token).then(data => {
|
||||
console.log(data);
|
||||
setTotalData(data.dashboard);
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<DailyBoardWrapper>
|
||||
{totalData &&
|
||||
<DailyBoard>
|
||||
<BoardTitle onClick={handleBoard} $state={boardState}>
|
||||
Daily Dashboard
|
||||
</BoardTitle>
|
||||
<BoardInfo $state={boardState}>
|
||||
<BoxWrapper>
|
||||
<InfoItem>
|
||||
<InfoTitle>DAU</InfoTitle>
|
||||
<InfoValue>
|
||||
{totalData.dau && totalData.dau.count}
|
||||
<span>({totalData.dau && totalData.dau.dif}%)</span>
|
||||
<InfoArrow $state={totalData.dau && totalData.dau.updown === '=' ? null : totalData.dau && totalData.dau.updown}></InfoArrow>
|
||||
</InfoValue>
|
||||
</InfoItem>
|
||||
<InfoItem>
|
||||
<InfoTitle>NRU</InfoTitle>
|
||||
<InfoValue>
|
||||
{totalData.nru && totalData.nru.count}
|
||||
<span>({totalData.nru && totalData.nru.dif}%)</span>
|
||||
<InfoArrow $state={totalData.nru && totalData.nru.updown === '=' ? null : totalData.nru && totalData.nru.updown}></InfoArrow>
|
||||
</InfoValue>
|
||||
</InfoItem>
|
||||
<InfoItem>
|
||||
<InfoTitle>PU</InfoTitle>
|
||||
<InfoValue>
|
||||
{totalData.pu && totalData.pu.count}
|
||||
<span>({totalData.pu && totalData.pu.dif}%)</span>
|
||||
<InfoArrow $state={totalData.pu && totalData.pu.updown === '=' ? null : totalData.pu && totalData.pu.updown}></InfoArrow>
|
||||
</InfoValue>
|
||||
</InfoItem>
|
||||
<InfoItem>
|
||||
<InfoTitle>MCU</InfoTitle>
|
||||
<InfoValue>
|
||||
{totalData.mcu && totalData.mcu.count}
|
||||
<span>({totalData.mcu && totalData.mcu.dif}%)</span>
|
||||
<InfoArrow $state={totalData.mcu && totalData.mcu.updown === '=' ? null : totalData.mcu && totalData.mcu.updown}></InfoArrow>
|
||||
</InfoValue>
|
||||
</InfoItem>
|
||||
</BoxWrapper>
|
||||
<InfoNotice>집계 기간 : {totalData.date && totalData.date} 00시 ~ 24시 (집계는 당일 기준 하루 전 데이터를 사용합니다)</InfoNotice>
|
||||
</BoardInfo>
|
||||
</DailyBoard>
|
||||
}
|
||||
</DailyBoardWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
export default DailyDashBoard;
|
||||
|
||||
const DailyBoardWrapper = styled.div`
|
||||
padding-top: 20px;
|
||||
border-top: 1px solid #000;
|
||||
`;
|
||||
|
||||
const DailyBoard = styled.div`
|
||||
background: #f6f6f6;
|
||||
border-radius: 10px;
|
||||
margin-bottom: 20px;
|
||||
`;
|
||||
|
||||
const BoardTitle = styled.div`
|
||||
font-size: 24px;
|
||||
font-weight: 600;
|
||||
line-height: 52px;
|
||||
padding: 0 10px;
|
||||
cursor: pointer;
|
||||
&:after {
|
||||
content: '';
|
||||
display: inline-block;
|
||||
width: 11px;
|
||||
height: 52px;
|
||||
margin-left: 10px;
|
||||
background: url(${TitleArrow}) 50% 50% no-repeat;
|
||||
position: absolute;
|
||||
transform: ${props => (props.$state === 'active' ? 'rotate(0)' : 'rotate(180deg)')};
|
||||
}
|
||||
`;
|
||||
|
||||
const BoardInfo = styled.div`
|
||||
padding: 20px;
|
||||
border-top: 1px solid #d9d9d9;
|
||||
display: ${props => (props.$state === 'active' ? 'block' : 'none')};
|
||||
`;
|
||||
|
||||
const BoxWrapper = styled.div`
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
`;
|
||||
|
||||
const InfoItem = styled.div`
|
||||
width: 24%;
|
||||
background: #fff;
|
||||
padding: 15px 20px;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
border-radius: 15px;
|
||||
`;
|
||||
|
||||
const InfoTitle = styled.div`
|
||||
width: 42px;
|
||||
font-size: 20px;
|
||||
font-weight: 600;
|
||||
`;
|
||||
|
||||
const InfoValue = styled.div`
|
||||
display: inline-flex;
|
||||
flex-wrap: wrap;
|
||||
margin: 5px 0;
|
||||
gap: 5px 0;
|
||||
align-items: center;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
span {
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
display: inline-block;
|
||||
margin: 0 5px;
|
||||
}
|
||||
`;
|
||||
|
||||
const InfoArrow = styled.div`
|
||||
width: 12px;
|
||||
height: 6px;
|
||||
background: url(${props => (props.$state === null ? '' : props.$state === 'UP' ? UpIcon : DownIcon)}) 50% 50% no-repeat;
|
||||
`;
|
||||
const InfoNotice = styled.div`
|
||||
font-size: 12px;
|
||||
text-align: right;
|
||||
color: #999;
|
||||
margin-top: 10px;
|
||||
`;
|
||||
111
src/components/IndexManage/DailyMedalContent.js
Normal file
@@ -0,0 +1,111 @@
|
||||
import { Fragment, useEffect, useState } from 'react';
|
||||
|
||||
import Button from '../common/button/Button';
|
||||
|
||||
import { TableStyle, TableInfo, ListOption, IndexTableWrap } from '../../styles/Components';
|
||||
import { DailySearchBar } from './index';
|
||||
import { DailyMedalView } from '../../apis';
|
||||
|
||||
const DailyMedalContent = () => {
|
||||
const token = sessionStorage.getItem('token');
|
||||
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 [dataList, setDataList] = useState([]);
|
||||
const [resultData, setResultData] = useState([]);
|
||||
|
||||
const [sendDate, setSendDate] = useState(START_DATE);
|
||||
const [finishDate, setFinishDate] = useState(END_DATE);
|
||||
|
||||
useEffect(() => {
|
||||
fetchData(START_DATE, END_DATE);
|
||||
}, []);
|
||||
|
||||
const fetchData = async (startDate, endDate) => {
|
||||
const startDateToLocal =
|
||||
startDate.getFullYear() +
|
||||
'-' +
|
||||
(startDate.getMonth() + 1 < 9 ? '0' + (startDate.getMonth() + 1) : startDate.getMonth() + 1) +
|
||||
'-' +
|
||||
(startDate.getDate() < 9 ? '0' + startDate.getDate() : startDate.getDate());
|
||||
|
||||
const endDateToLocal =
|
||||
endDate.getFullYear() +
|
||||
'-' +
|
||||
(endDate.getMonth() + 1 < 9 ? '0' + (endDate.getMonth() + 1) : endDate.getMonth() + 1) +
|
||||
'-' +
|
||||
(endDate.getDate() < 9 ? '0' + endDate.getDate() : endDate.getDate());
|
||||
|
||||
setDataList(await DailyMedalView(token, startDateToLocal, endDateToLocal));
|
||||
|
||||
setSendDate(startDateToLocal);
|
||||
setFinishDate(endDateToLocal);
|
||||
};
|
||||
|
||||
// 검색 함수
|
||||
const handleSearch = (send_dt, end_dt) => {
|
||||
fetchData(send_dt, end_dt);
|
||||
};
|
||||
|
||||
// 엑셀 다운로드
|
||||
const handleXlsxExport = () => {
|
||||
const fileName = 'Caliverse_Daily_Medal.xlsx';
|
||||
//DailyActiveUserExport(token, fileName, sendDate, finishDate);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<DailySearchBar setResultData={setResultData} resultData={resultData} handleSearch={handleSearch} fetchData={fetchData} />
|
||||
<TableInfo>
|
||||
<ListOption>
|
||||
<Button theme="line" text="엑셀 다운로드" handleClick={handleXlsxExport} />
|
||||
</ListOption>
|
||||
</TableInfo>
|
||||
<IndexTableWrap>
|
||||
<TableStyle>
|
||||
<caption></caption>
|
||||
<thead >
|
||||
<tr>
|
||||
<th rowSpan="1" width="20">
|
||||
일자
|
||||
</th>
|
||||
<th colSpan="1" width="30">
|
||||
UserID
|
||||
</th>
|
||||
<th colSpan="1" width="30">
|
||||
닉네임
|
||||
</th>
|
||||
<th colSpan="1" width="30">
|
||||
Item ID
|
||||
</th>
|
||||
<th colSpan="1" width="30">
|
||||
획득량
|
||||
</th>
|
||||
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
{(dataList || []).map((data, index) => (
|
||||
<tr key={index}>
|
||||
<td>{data.date}</td>
|
||||
<td>{data.dau}</td>
|
||||
<td>{data.dalc}</td>
|
||||
<td>{data.dglc}</td>
|
||||
<td>{data.maxAu}</td>
|
||||
{Array.from({ length: 24 }, (_, i) => (
|
||||
<td key={i}>{data['h' + i]}</td>
|
||||
))}
|
||||
</tr>
|
||||
))}
|
||||
|
||||
</tbody>
|
||||
</TableStyle>
|
||||
</IndexTableWrap>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default DailyMedalContent;
|
||||
|
||||
112
src/components/IndexManage/DailySearchBar.js
Normal file
@@ -0,0 +1,112 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { styled } from 'styled-components';
|
||||
import Button from '../common/button/Button';
|
||||
|
||||
import 'react-datepicker/dist/react-datepicker.css';
|
||||
import DatePickerComponent from '../common/Date/DatePickerComponent';
|
||||
|
||||
import { FormWrapper, InputLabel, TextInput, SelectInput, BtnWrapper, InputGroup, DatePickerWrapper } from '../../styles/Components';
|
||||
|
||||
const DailySearchBar = ({ resultData, setResultData, handleSearch, fetchData }) => {
|
||||
// 초기 날짜 세팅
|
||||
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();
|
||||
|
||||
// resultData에 임의 날짜 넣어주기
|
||||
useEffect(() => {
|
||||
setResultData({
|
||||
send_dt: START_DATE,
|
||||
end_dt: END_DATE,
|
||||
});
|
||||
}, []);
|
||||
|
||||
const handleSelectedDate = data => {
|
||||
const sendDate = new Date(data);
|
||||
const resultSendData = new Date(sendDate.getFullYear(), sendDate.getMonth(), sendDate.getDate());
|
||||
|
||||
setResultData({ ...resultData, send_dt: resultSendData });
|
||||
};
|
||||
|
||||
const handleEndDate = data => {
|
||||
const endDate = new Date(data);
|
||||
const resultSendData = new Date(endDate.getFullYear(), endDate.getMonth(), endDate.getDate());
|
||||
|
||||
setResultData({ ...resultData, end_dt: resultSendData });
|
||||
};
|
||||
|
||||
const handleReset = e => {
|
||||
e.preventDefault();
|
||||
setResultData({ send_dt: START_DATE, end_dt: END_DATE });
|
||||
|
||||
fetchData(START_DATE, END_DATE);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<FormWrapper>
|
||||
<SearchbarStyle>
|
||||
<SearchItem>
|
||||
<InputLabel>집계 기준일</InputLabel>
|
||||
<InputGroup>
|
||||
<DatePickerWrapper>
|
||||
<DatePickerComponent
|
||||
name="시작 일자"
|
||||
selectedDate={resultData.send_dt}
|
||||
handleSelectedDate={data => handleSelectedDate(data)}
|
||||
maxDate={new Date()} />
|
||||
<span>~</span>
|
||||
<DatePickerComponent
|
||||
name="종료 일자"
|
||||
selectedDate={resultData.end_dt}
|
||||
handleSelectedDate={data => handleEndDate(data)}
|
||||
pastDate = {resultData.send_dt}
|
||||
maxDate={new Date()} />
|
||||
</DatePickerWrapper>
|
||||
</InputGroup>
|
||||
</SearchItem>
|
||||
<BtnWrapper $gap="8px">
|
||||
<Button theme="reset" handleClick={handleReset} />
|
||||
<Button
|
||||
theme="search"
|
||||
text="검색"
|
||||
handleClick={e => {
|
||||
e.preventDefault();
|
||||
handleSearch(resultData.send_dt, resultData.end_dt);
|
||||
}}
|
||||
/>
|
||||
</BtnWrapper>
|
||||
</SearchbarStyle>
|
||||
</FormWrapper>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default DailySearchBar;
|
||||
|
||||
const SearchbarStyle = styled.div`
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 20px 0;
|
||||
font-size: 14px;
|
||||
padding: 20px;
|
||||
border-top: 1px solid #000;
|
||||
border-bottom: 1px solid #000;
|
||||
margin-bottom: 40px;
|
||||
`;
|
||||
|
||||
const SearchItem = styled.div`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 20px;
|
||||
margin-right: 50px;
|
||||
|
||||
${TextInput}, ${SelectInput} {
|
||||
height: 35px;
|
||||
}
|
||||
${TextInput} {
|
||||
padding: 0 10px;
|
||||
max-width: 400px;
|
||||
}
|
||||
`;
|
||||
166
src/components/IndexManage/DecoContent.js
Normal file
@@ -0,0 +1,166 @@
|
||||
import { styled } from 'styled-components';
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
import DecoSearchBar from '../../components/IndexManage/DecoSearchBar';
|
||||
import Button from '../../components/common/button/Button';
|
||||
|
||||
import { TableStyle, TableInfo, ListOption } from '../../styles/Components';
|
||||
import { ClothesIndexExport, ClothesIndexView } from '../../apis';
|
||||
|
||||
const DecoContent = () => {
|
||||
const token = sessionStorage.getItem('token');
|
||||
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 [searchKey, setSearchKey] = useState('');
|
||||
const [sendDate, setSendDate] = useState(START_DATE);
|
||||
const [finishDate, setFinishDate] = useState(END_DATE);
|
||||
|
||||
const [dataList, setDataList] = useState([]);
|
||||
useEffect(() => {
|
||||
fetchData('', START_DATE, END_DATE);
|
||||
}, []);
|
||||
|
||||
// console.log(dataList);
|
||||
|
||||
const fetchData = async (data, startDate, endDate) => {
|
||||
const startDateToLocal =
|
||||
startDate.getFullYear() +
|
||||
'-' +
|
||||
(startDate.getMonth() + 1 < 9 ? '0' + (startDate.getMonth() + 1) : startDate.getMonth() + 1) +
|
||||
'-' +
|
||||
(startDate.getDate() < 9 ? '0' + startDate.getDate() : startDate.getDate());
|
||||
|
||||
const endDateToLocal =
|
||||
endDate.getFullYear() +
|
||||
'-' +
|
||||
(endDate.getMonth() + 1 < 9 ? '0' + (endDate.getMonth() + 1) : endDate.getMonth() + 1) +
|
||||
'-' +
|
||||
(endDate.getDate() < 9 ? '0' + endDate.getDate() : endDate.getDate());
|
||||
|
||||
setDataList(await ClothesIndexView(token, data, startDateToLocal, endDateToLocal));
|
||||
|
||||
setSearchKey(data);
|
||||
setSendDate(startDateToLocal);
|
||||
setFinishDate(endDateToLocal);
|
||||
};
|
||||
|
||||
// 엑셀 다운로드
|
||||
const handleXlsxExport = () => {
|
||||
const fileName = 'Caliverse_Deco_Index.xlsx';
|
||||
ClothesIndexExport(token, fileName, searchKey, sendDate, finishDate);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<DecoSearchBar fetchData={fetchData} />
|
||||
<TableInfo>
|
||||
<ListOption>
|
||||
<Button theme="line" text="엑셀 다운로드" handleClick={handleXlsxExport} />
|
||||
</ListOption>
|
||||
</TableInfo>
|
||||
<TableWrapper>
|
||||
<EconomicTable2>
|
||||
<thead>
|
||||
<tr>
|
||||
<th width="240">일자</th>
|
||||
<th rowSpan="2" width="160">
|
||||
2023-08-07
|
||||
</th>
|
||||
<th rowSpan="2" width="160">
|
||||
2023-08-08
|
||||
</th>
|
||||
<th rowSpan="2" width="160">
|
||||
2023-08-09
|
||||
</th>
|
||||
<th rowSpan="2" width="160">
|
||||
2023-08-10
|
||||
</th>
|
||||
<th rowSpan="2" width="160"></th>
|
||||
<th rowSpan="2" width="160"></th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>대상</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<TableTitle>(인스턴스 ID) 방문자 수</TableTitle>
|
||||
<TableData>95</TableData>
|
||||
<TableData>240</TableData>
|
||||
<TableData>240</TableData>
|
||||
<TableData>240</TableData>
|
||||
<TableData $state="blank"></TableData>
|
||||
<TableData $state="blank"></TableData>
|
||||
</tr>
|
||||
{/* {mokupData.map((data, index) => (
|
||||
<Fragment key={index}>
|
||||
<tr>
|
||||
<td>{data.date}</td>
|
||||
<td>{data.name}</td>
|
||||
<td>{data.trader}</td>
|
||||
<td>{data.id}</td>
|
||||
<td>{data.key}</td>
|
||||
</tr>
|
||||
</Fragment>
|
||||
))} */}
|
||||
</tbody>
|
||||
</EconomicTable2>
|
||||
</TableWrapper>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default DecoContent;
|
||||
|
||||
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: 900px;
|
||||
th {
|
||||
&.cell-nru {
|
||||
background: #f0f0f0;
|
||||
border-left: 1px solid #aaa;
|
||||
border-right: 1px solid #aaa;
|
||||
}
|
||||
}
|
||||
td {
|
||||
&.blank {
|
||||
background: #f9f9f9;
|
||||
}
|
||||
&.cell-nru {
|
||||
background: #fafafa;
|
||||
border-left: 1px solid #aaa;
|
||||
border-right: 1px solid #aaa;
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const TableData = styled.td`
|
||||
background: ${props => (props.$state === 'danger' ? '#d60000' : props.$state === 'blank' ? '#F9F9F9' : 'transparent')};
|
||||
color: ${props => (props.$state === 'danger' ? '#fff' : '#2c2c2c')};
|
||||
`;
|
||||
|
||||
const TableTitle = styled.td`
|
||||
text-align: center;
|
||||
`;
|
||||
|
||||
const EconomicTable2 = styled(TableStyle)`
|
||||
${TableData} {
|
||||
text-align: left;
|
||||
}
|
||||
`;
|
||||
116
src/components/IndexManage/DecoSearchBar.js
Normal file
@@ -0,0 +1,116 @@
|
||||
import { useState } from 'react';
|
||||
import { styled } from 'styled-components';
|
||||
import Button from '../../components/common/button/Button';
|
||||
|
||||
import DatePickerComponent from '../common/Date/DatePickerComponent';
|
||||
|
||||
import { FormWrapper, InputLabel, TextInput, SelectInput, BtnWrapper, InputGroup, DatePickerWrapper } from '../../styles/Components';
|
||||
|
||||
const DecoSearchBar = ({ fetchData }) => {
|
||||
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 [resultData, setResultData] = useState({
|
||||
search_key: '',
|
||||
send_dt: START_DATE,
|
||||
end_dt: END_DATE,
|
||||
});
|
||||
|
||||
// 발송 날짜 세팅 로직
|
||||
const handleSelectedDate = data => {
|
||||
const sendDate = new Date(data);
|
||||
const resultSendData = new Date(sendDate.getFullYear(), sendDate.getMonth(), sendDate.getDate());
|
||||
|
||||
setResultData({ ...resultData, send_dt: resultSendData });
|
||||
};
|
||||
|
||||
// 발송 날짜 세팅 로직
|
||||
const handleEndDate = data => {
|
||||
const endDate = new Date(data);
|
||||
const resultSendData = new Date(endDate.getFullYear(), endDate.getMonth(), endDate.getDate());
|
||||
|
||||
setResultData({ ...resultData, end_dt: resultSendData });
|
||||
};
|
||||
|
||||
const handleReset = e => {
|
||||
e.preventDefault();
|
||||
setResultData({ search_key: '', send_dt: START_DATE, end_dt: END_DATE });
|
||||
|
||||
fetchData('', START_DATE, END_DATE);
|
||||
};
|
||||
return (
|
||||
<>
|
||||
<FormWrapper>
|
||||
<SearchbarStyle>
|
||||
<SearchItem>
|
||||
<InputLabel>아이템 ID</InputLabel>
|
||||
<TextInput type="text" placeholder="ID 입력" onChange={e => setResultData({ ...resultData, search_key: e.target.value })} />
|
||||
</SearchItem>
|
||||
<SearchItem>
|
||||
<InputLabel>집계 기간</InputLabel>
|
||||
<InputGroup>
|
||||
<DatePickerWrapper>
|
||||
<DatePickerComponent
|
||||
name="시작 일자" selectedDate={resultData.send_dt}
|
||||
handleSelectedDate={data => handleSelectedDate(data)}
|
||||
maxDate={new Date()} />
|
||||
<span>~</span>
|
||||
<DatePickerComponent
|
||||
name="종료 일자" selectedDate={resultData.end_dt}
|
||||
handleSelectedDate={data => handleEndDate(data)}
|
||||
pastDate = {resultData.send_dt}
|
||||
maxDate={new Date()} />
|
||||
</DatePickerWrapper>
|
||||
</InputGroup>
|
||||
</SearchItem>
|
||||
<BtnWrapper $gap="8px">
|
||||
<Button
|
||||
theme="reset"
|
||||
handleClick={e => {
|
||||
handleReset(e);
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
theme="search"
|
||||
text="집계"
|
||||
handleClick={e => {
|
||||
e.preventDefault();
|
||||
fetchData(resultData.search_key, resultData.send_dt, resultData.end_dt);
|
||||
}}
|
||||
/>
|
||||
</BtnWrapper>
|
||||
</SearchbarStyle>
|
||||
</FormWrapper>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default DecoSearchBar;
|
||||
|
||||
const SearchbarStyle = styled.div`
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 20px 0;
|
||||
font-size: 14px;
|
||||
padding: 20px;
|
||||
border-top: 1px solid #000;
|
||||
border-bottom: 1px solid #000;
|
||||
margin-bottom: 40px;
|
||||
`;
|
||||
|
||||
const SearchItem = styled.div`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 20px;
|
||||
margin-right: 50px;
|
||||
|
||||
${TextInput}, ${SelectInput} {
|
||||
height: 35px;
|
||||
}
|
||||
${TextInput} {
|
||||
padding: 0 10px;
|
||||
max-width: 400px;
|
||||
}
|
||||
`;
|
||||
166
src/components/IndexManage/InstanceContent.js
Normal file
@@ -0,0 +1,166 @@
|
||||
import { styled } from 'styled-components';
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
import { TableStyle, TableInfo, ListOption } from '../../styles/Components';
|
||||
|
||||
import Button from '../../components/common/button/Button';
|
||||
import InstanceSearchBar from '../../components/IndexManage/InstanceSearchBar';
|
||||
import { InstanceIndexExport, InstanceIndexView } from '../../apis';
|
||||
|
||||
const InstanceContent = () => {
|
||||
const token = sessionStorage.getItem('token');
|
||||
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 [searchKey, setSearchKey] = useState('');
|
||||
const [sendDate, setSendDate] = useState(START_DATE);
|
||||
const [finishDate, setFinishDate] = useState(END_DATE);
|
||||
|
||||
const [dataList, setDataList] = useState([]);
|
||||
useEffect(() => {
|
||||
fetchData('', START_DATE, END_DATE);
|
||||
}, []);
|
||||
|
||||
// console.log(dataList);
|
||||
|
||||
const fetchData = async (data, startDate, endDate) => {
|
||||
const startDateToLocal =
|
||||
startDate.getFullYear() +
|
||||
'-' +
|
||||
(startDate.getMonth() + 1 < 9 ? '0' + (startDate.getMonth() + 1) : startDate.getMonth() + 1) +
|
||||
'-' +
|
||||
(startDate.getDate() < 9 ? '0' + startDate.getDate() : startDate.getDate());
|
||||
|
||||
const endDateToLocal =
|
||||
endDate.getFullYear() +
|
||||
'-' +
|
||||
(endDate.getMonth() + 1 < 9 ? '0' + (endDate.getMonth() + 1) : endDate.getMonth() + 1) +
|
||||
'-' +
|
||||
(endDate.getDate() < 9 ? '0' + endDate.getDate() : endDate.getDate());
|
||||
|
||||
setDataList(await InstanceIndexView(token, data, startDateToLocal, endDateToLocal));
|
||||
|
||||
setSearchKey(data);
|
||||
setSendDate(startDateToLocal);
|
||||
setFinishDate(endDateToLocal);
|
||||
};
|
||||
|
||||
// 엑셀 다운로드
|
||||
const handleXlsxExport = () => {
|
||||
const fileName = 'Caliverse_Instance_Index.xlsx';
|
||||
InstanceIndexExport(token, fileName, searchKey, sendDate, finishDate);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<InstanceSearchBar fetchData={fetchData} />
|
||||
<TableInfo>
|
||||
<ListOption>
|
||||
<Button theme="line" text="엑셀 다운로드" handleClick={handleXlsxExport} />
|
||||
</ListOption>
|
||||
</TableInfo>
|
||||
<TableWrapper>
|
||||
<EconomicTable2>
|
||||
<thead>
|
||||
<tr>
|
||||
<th width="240">일자</th>
|
||||
<th rowSpan="2" width="160">
|
||||
2023-08-07
|
||||
</th>
|
||||
<th rowSpan="2" width="160">
|
||||
2023-08-08
|
||||
</th>
|
||||
<th rowSpan="2" width="160">
|
||||
2023-08-09
|
||||
</th>
|
||||
<th rowSpan="2" width="160">
|
||||
2023-08-10
|
||||
</th>
|
||||
<th rowSpan="2" width="160"></th>
|
||||
<th rowSpan="2" width="160"></th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>대상</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<TableTitle>(인스턴스 ID) 방문자 수</TableTitle>
|
||||
<TableData>95</TableData>
|
||||
<TableData>240</TableData>
|
||||
<TableData>240</TableData>
|
||||
<TableData>240</TableData>
|
||||
<TableData $state="blank"></TableData>
|
||||
<TableData $state="blank"></TableData>
|
||||
</tr>
|
||||
{/* {mokupData.map((data, index) => (
|
||||
<Fragment key={index}>
|
||||
<tr>
|
||||
<td>{data.date}</td>
|
||||
<td>{data.name}</td>
|
||||
<td>{data.trader}</td>
|
||||
<td>{data.id}</td>
|
||||
<td>{data.key}</td>
|
||||
</tr>
|
||||
</Fragment>
|
||||
))} */}
|
||||
</tbody>
|
||||
</EconomicTable2>
|
||||
</TableWrapper>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default InstanceContent;
|
||||
|
||||
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: 900px;
|
||||
th {
|
||||
&.cell-nru {
|
||||
background: #f0f0f0;
|
||||
border-left: 1px solid #aaa;
|
||||
border-right: 1px solid #aaa;
|
||||
}
|
||||
}
|
||||
td {
|
||||
&.blank {
|
||||
background: #f9f9f9;
|
||||
}
|
||||
&.cell-nru {
|
||||
background: #fafafa;
|
||||
border-left: 1px solid #aaa;
|
||||
border-right: 1px solid #aaa;
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const TableData = styled.td`
|
||||
background: ${props => (props.$state === 'danger' ? '#d60000' : props.$state === 'blank' ? '#F9F9F9' : 'transparent')};
|
||||
color: ${props => (props.$state === 'danger' ? '#fff' : '#2c2c2c')};
|
||||
`;
|
||||
|
||||
const TableTitle = styled.td`
|
||||
text-align: center;
|
||||
`;
|
||||
|
||||
const EconomicTable2 = styled(TableStyle)`
|
||||
${TableData} {
|
||||
text-align: left;
|
||||
}
|
||||
`;
|
||||
117
src/components/IndexManage/InstanceSearchBar.js
Normal file
@@ -0,0 +1,117 @@
|
||||
import { useState } from 'react';
|
||||
import { styled } from 'styled-components';
|
||||
import Button from '../../components/common/button/Button';
|
||||
|
||||
import DatePickerComponent from '../common/Date/DatePickerComponent';
|
||||
|
||||
import { FormWrapper, InputLabel, TextInput, SelectInput, BtnWrapper, InputGroup, DatePickerWrapper } from '../../styles/Components';
|
||||
|
||||
const InstanceSearchBar = ({ fetchData }) => {
|
||||
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 [resultData, setResultData] = useState({
|
||||
search_key: '',
|
||||
send_dt: START_DATE,
|
||||
end_dt: END_DATE,
|
||||
});
|
||||
|
||||
// 발송 날짜 세팅 로직
|
||||
const handleSelectedDate = data => {
|
||||
const sendDate = new Date(data);
|
||||
const resultSendData = new Date(sendDate.getFullYear(), sendDate.getMonth(), sendDate.getDate());
|
||||
|
||||
setResultData({ ...resultData, send_dt: resultSendData });
|
||||
};
|
||||
|
||||
// 발송 날짜 세팅 로직
|
||||
const handleEndDate = data => {
|
||||
const endDate = new Date(data);
|
||||
const resultSendData = new Date(endDate.getFullYear(), endDate.getMonth(), endDate.getDate());
|
||||
|
||||
setResultData({ ...resultData, end_dt: resultSendData });
|
||||
};
|
||||
|
||||
const handleReset = e => {
|
||||
e.preventDefault();
|
||||
setResultData({ search_key: '', send_dt: START_DATE, end_dt: END_DATE });
|
||||
|
||||
fetchData('', START_DATE, END_DATE);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<FormWrapper>
|
||||
<SearchbarStyle>
|
||||
<SearchItem>
|
||||
<InputLabel>인스턴스 ID</InputLabel>
|
||||
<TextInput type="text" placeholder="ID 입력" onChange={e => setResultData({ ...resultData, search_key: e.target.value })} />
|
||||
</SearchItem>
|
||||
<SearchItem>
|
||||
<InputLabel>집계 기간</InputLabel>
|
||||
<InputGroup>
|
||||
<DatePickerWrapper>
|
||||
<DatePickerComponent
|
||||
name="시작 일자" selectedDate={resultData.send_dt}
|
||||
handleSelectedDate={data => handleSelectedDate(data)}
|
||||
maxDate={new Date()} />
|
||||
<span>~</span>
|
||||
<DatePickerComponent
|
||||
name="종료 일자" selectedDate={resultData.end_dt}
|
||||
handleSelectedDate={data => handleEndDate(data)}
|
||||
pastDate = {resultData.send_dt}
|
||||
maxDate={new Date()} />
|
||||
</DatePickerWrapper>
|
||||
</InputGroup>
|
||||
</SearchItem>
|
||||
<BtnWrapper $gap="8px">
|
||||
<Button
|
||||
theme="reset"
|
||||
handleClick={e => {
|
||||
handleReset(e);
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
theme="search"
|
||||
text="집계"
|
||||
handleClick={e => {
|
||||
e.preventDefault();
|
||||
fetchData(resultData.search_key, resultData.send_dt, resultData.end_dt);
|
||||
}}
|
||||
/>
|
||||
</BtnWrapper>
|
||||
</SearchbarStyle>
|
||||
</FormWrapper>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default InstanceSearchBar;
|
||||
|
||||
const SearchbarStyle = styled.div`
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 20px 0;
|
||||
font-size: 14px;
|
||||
padding: 20px;
|
||||
border-top: 1px solid #000;
|
||||
border-bottom: 1px solid #000;
|
||||
margin-bottom: 40px;
|
||||
`;
|
||||
|
||||
const SearchItem = styled.div`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 20px;
|
||||
margin-right: 50px;
|
||||
|
||||
${TextInput}, ${SelectInput} {
|
||||
height: 35px;
|
||||
}
|
||||
${TextInput} {
|
||||
padding: 0 10px;
|
||||
max-width: 400px;
|
||||
}
|
||||
`;
|
||||
241
src/components/IndexManage/ItemContent.js
Normal file
@@ -0,0 +1,241 @@
|
||||
import { styled } from 'styled-components';
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
import Button from '../../components/common/button/Button';
|
||||
|
||||
import { SelectInput, TableStyle, TableInfo, ListOption, InputLabel, InputGroup } from '../../styles/Components';
|
||||
|
||||
import ItemSearchBar from '../../components/IndexManage/ItemSearchBar';
|
||||
import { ItemIndexExport, ItemIndexView } from '../../apis';
|
||||
|
||||
const ItemContent = () => {
|
||||
const token = sessionStorage.getItem('token');
|
||||
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 [sendDate, setSendDate] = useState(START_DATE);
|
||||
const [finishDate, setFinishDate] = useState(END_DATE);
|
||||
|
||||
const [dataList, setDataList] = useState([]);
|
||||
useEffect(() => {
|
||||
fetchData(START_DATE, END_DATE);
|
||||
}, []);
|
||||
|
||||
// console.log(dataList);
|
||||
|
||||
const fetchData = async (startDate, endDate) => {
|
||||
const startDateToLocal =
|
||||
startDate.getFullYear() +
|
||||
'-' +
|
||||
(startDate.getMonth() + 1 < 9 ? '0' + (startDate.getMonth() + 1) : startDate.getMonth() + 1) +
|
||||
'-' +
|
||||
(startDate.getDate() < 9 ? '0' + startDate.getDate() : startDate.getDate());
|
||||
|
||||
const endDateToLocal =
|
||||
endDate.getFullYear() +
|
||||
'-' +
|
||||
(endDate.getMonth() + 1 < 9 ? '0' + (endDate.getMonth() + 1) : endDate.getMonth() + 1) +
|
||||
'-' +
|
||||
(endDate.getDate() < 9 ? '0' + endDate.getDate() : endDate.getDate());
|
||||
|
||||
setDataList(await ItemIndexView(token, startDateToLocal, endDateToLocal));
|
||||
|
||||
setSendDate(startDateToLocal);
|
||||
setFinishDate(endDateToLocal);
|
||||
};
|
||||
|
||||
// 엑셀 다운로드
|
||||
const handleXlsxExport = () => {
|
||||
const fileName = 'Caliverse_Item_Index.xlsx';
|
||||
ItemIndexExport(token, fileName, sendDate, finishDate);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<ItemSearchBar fetchData={fetchData} />
|
||||
<TableInfo2>
|
||||
<InputGroup>
|
||||
<InputLabel>아이템 코드</InputLabel>
|
||||
<SelectInput>
|
||||
<option value="">15100001</option>
|
||||
<option value="">15100001</option>
|
||||
<option value="">15100001</option>
|
||||
<option value="">15100001</option>
|
||||
</SelectInput>
|
||||
</InputGroup>
|
||||
<ListOption>
|
||||
<Button theme="line" text="엑셀 다운로드" handleClick={handleXlsxExport} />
|
||||
</ListOption>
|
||||
</TableInfo2>
|
||||
<TableWrapper>
|
||||
<EconomicTable>
|
||||
<thead>
|
||||
<tr>
|
||||
<th colSpan="2" className="text-center" width="300">
|
||||
Product
|
||||
</th>
|
||||
<th width="160">2023-08-07</th>
|
||||
<th width="160">2023-08-08</th>
|
||||
<th width="160">2023-08-09</th>
|
||||
<th width="160">2023-08-10</th>
|
||||
<th width="160">2023-08-11</th>
|
||||
<th width="160">2023-08-12</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<TableTitle colSpan="2">(Total) 15100001 생산량</TableTitle>
|
||||
<TableData>240</TableData>
|
||||
<TableData>240</TableData>
|
||||
<TableData>240</TableData>
|
||||
<TableData>240</TableData>
|
||||
<TableData $state="blank"></TableData>
|
||||
<TableData $state="blank"></TableData>
|
||||
</tr>
|
||||
<tr>
|
||||
<TableTitle colSpan="2">(Total) 15100001 소진량</TableTitle>
|
||||
<TableData>110</TableData>
|
||||
<TableData>110</TableData>
|
||||
<TableData>110</TableData>
|
||||
<TableData>110</TableData>
|
||||
<TableData $state="blank"></TableData>
|
||||
<TableData $state="blank"></TableData>
|
||||
</tr>
|
||||
<tr>
|
||||
<TableTitle colSpan="2">(Total) 15100001 보유량</TableTitle>
|
||||
<TableData>3750</TableData>
|
||||
<TableData>3750</TableData>
|
||||
<TableData>3750</TableData>
|
||||
<TableData>3750</TableData>
|
||||
<TableData $state="blank"></TableData>
|
||||
<TableData $state="blank"></TableData>
|
||||
</tr>
|
||||
<tr>
|
||||
<TableTitle rowSpan="2">GET</TableTitle>
|
||||
<TableTitle>퀘스트 보상</TableTitle>
|
||||
<TableData>70</TableData>
|
||||
<TableData>70</TableData>
|
||||
<TableData>70</TableData>
|
||||
<TableData>70</TableData>
|
||||
<TableData $state="blank"></TableData>
|
||||
<TableData $state="blank"></TableData>
|
||||
</tr>
|
||||
<tr>
|
||||
<TableTitle>시즌패스 보상</TableTitle>
|
||||
<TableData>70</TableData>
|
||||
<TableData>70</TableData>
|
||||
<TableData>70</TableData>
|
||||
<TableData>70</TableData>
|
||||
<TableData $state="blank"></TableData>
|
||||
<TableData $state="blank"></TableData>
|
||||
</tr>
|
||||
<tr>
|
||||
<TableTitle rowSpan="2">USE</TableTitle>
|
||||
<TableTitle>퀘스트 보상</TableTitle>
|
||||
<TableData>50</TableData>
|
||||
<TableData>50</TableData>
|
||||
<TableData>50</TableData>
|
||||
<TableData>50</TableData>
|
||||
<TableData $state="blank"></TableData>
|
||||
<TableData $state="blank"></TableData>
|
||||
</tr>
|
||||
<tr>
|
||||
<TableTitle>시즌패스 보상</TableTitle>
|
||||
<TableData>50</TableData>
|
||||
<TableData>50</TableData>
|
||||
<TableData>50</TableData>
|
||||
<TableData>50</TableData>
|
||||
<TableData $state="blank"></TableData>
|
||||
<TableData $state="blank"></TableData>
|
||||
</tr>
|
||||
|
||||
{/* {mokupData.map((data, index) => (
|
||||
<Fragment key={index}>
|
||||
<tr>
|
||||
<td>{data.date}</td>
|
||||
<td>{data.name}</td>
|
||||
<td>{data.trader}</td>
|
||||
<td>{data.id}</td>
|
||||
<td>{data.key}</td>
|
||||
</tr>
|
||||
</Fragment>
|
||||
))} */}
|
||||
</tbody>
|
||||
</EconomicTable>
|
||||
</TableWrapper>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default ItemContent;
|
||||
|
||||
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: 900px;
|
||||
th {
|
||||
&.cell-nru {
|
||||
background: #f0f0f0;
|
||||
border-left: 1px solid #aaa;
|
||||
border-right: 1px solid #aaa;
|
||||
}
|
||||
}
|
||||
td {
|
||||
&.blank {
|
||||
background: #f9f9f9;
|
||||
}
|
||||
&.cell-nru {
|
||||
background: #fafafa;
|
||||
border-left: 1px solid #aaa;
|
||||
border-right: 1px solid #aaa;
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const TableInfo2 = styled(TableInfo)`
|
||||
justify-content: space-between;
|
||||
${InputLabel} {
|
||||
font-size: 12px;
|
||||
}
|
||||
${SelectInput} {
|
||||
width: auto;
|
||||
min-width: 100px;
|
||||
height: 24px;
|
||||
}
|
||||
`;
|
||||
|
||||
const TableData = styled.td`
|
||||
background: ${props => (props.$state === 'danger' ? '#d60000' : props.$state === 'blank' ? '#F9F9F9' : 'transparent')};
|
||||
color: ${props => (props.$state === 'danger' ? '#fff' : '#2c2c2c')};
|
||||
`;
|
||||
|
||||
const TableTitle = styled.td`
|
||||
text-align: center;
|
||||
`;
|
||||
|
||||
const EconomicTable = styled(TableStyle)`
|
||||
${TableData} {
|
||||
text-align: left;
|
||||
}
|
||||
tbody {
|
||||
tr:nth-child(1),
|
||||
tr:nth-child(2),
|
||||
tr:nth-child(3) {
|
||||
background: #f5fcff;
|
||||
}
|
||||
}
|
||||
`;
|
||||
111
src/components/IndexManage/ItemSearchBar.js
Normal file
@@ -0,0 +1,111 @@
|
||||
import { useState } from 'react';
|
||||
import { styled } from 'styled-components';
|
||||
import Button from '../../components/common/button/Button';
|
||||
import DatePickerComponent from '../common/Date/DatePickerComponent';
|
||||
|
||||
import { FormWrapper, InputLabel, TextInput, SelectInput, BtnWrapper, InputGroup, DatePickerWrapper } from '../../styles/Components';
|
||||
|
||||
const ItemSearchBar = ({ fetchData }) => {
|
||||
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 [resultData, setResultData] = useState({
|
||||
send_dt: START_DATE,
|
||||
end_dt: END_DATE,
|
||||
});
|
||||
|
||||
// 발송 날짜 세팅 로직
|
||||
const handleSelectedDate = data => {
|
||||
const sendDate = new Date(data);
|
||||
const resultSendData = new Date(sendDate.getFullYear(), sendDate.getMonth(), sendDate.getDate());
|
||||
|
||||
setResultData({ ...resultData, send_dt: resultSendData });
|
||||
};
|
||||
|
||||
// 발송 날짜 세팅 로직
|
||||
const handleEndDate = data => {
|
||||
const endDate = new Date(data);
|
||||
const resultSendData = new Date(endDate.getFullYear(), endDate.getMonth(), endDate.getDate());
|
||||
|
||||
setResultData({ ...resultData, end_dt: resultSendData });
|
||||
};
|
||||
|
||||
const handleReset = e => {
|
||||
e.preventDefault();
|
||||
setResultData({ send_dt: START_DATE, end_dt: END_DATE });
|
||||
|
||||
fetchData(START_DATE, END_DATE);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<FormWrapper>
|
||||
<SearchbarStyle>
|
||||
<SearchItem>
|
||||
<InputLabel>집계 기간</InputLabel>
|
||||
<InputGroup>
|
||||
<DatePickerWrapper>
|
||||
<DatePickerComponent
|
||||
name="시작 일자" selectedDate={resultData.send_dt}
|
||||
handleSelectedDate={data => handleSelectedDate(data)}
|
||||
maxDate={new Date()} />
|
||||
<span>~</span>
|
||||
<DatePickerComponent
|
||||
name="종료 일자" selectedDate={resultData.end_dt}
|
||||
handleSelectedDate={data => handleEndDate(data)}
|
||||
pastDate = {resultData.send_dt}
|
||||
maxDate={new Date()} />
|
||||
</DatePickerWrapper>
|
||||
</InputGroup>
|
||||
</SearchItem>
|
||||
<BtnWrapper $gap="8px">
|
||||
<Button
|
||||
theme="reset"
|
||||
handleClick={e => {
|
||||
handleReset(e);
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
theme="search"
|
||||
text="집계"
|
||||
handleClick={e => {
|
||||
e.preventDefault();
|
||||
fetchData(resultData.send_dt, resultData.end_dt);
|
||||
}}
|
||||
/>
|
||||
</BtnWrapper>
|
||||
</SearchbarStyle>
|
||||
</FormWrapper>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default ItemSearchBar;
|
||||
|
||||
const SearchbarStyle = styled.div`
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 20px 0;
|
||||
font-size: 14px;
|
||||
padding: 20px;
|
||||
border-top: 1px solid #000;
|
||||
border-bottom: 1px solid #000;
|
||||
margin-bottom: 40px;
|
||||
`;
|
||||
|
||||
const SearchItem = styled.div`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 20px;
|
||||
margin-right: 50px;
|
||||
|
||||
${TextInput}, ${SelectInput} {
|
||||
height: 35px;
|
||||
}
|
||||
${TextInput} {
|
||||
padding: 0 10px;
|
||||
max-width: 400px;
|
||||
}
|
||||
`;
|
||||
112
src/components/IndexManage/PlayTimeContent.js
Normal file
@@ -0,0 +1,112 @@
|
||||
import { Fragment, useEffect, useState } from 'react';
|
||||
|
||||
import Button from '../../components/common/button/Button';
|
||||
|
||||
import { TableStyle, TableInfo, ListOption, IndexTableWrap } from '../../styles/Components';
|
||||
import { PlayTimeSearchBar } from '../../components/IndexManage/index';
|
||||
import { PlaytimeIndexExport, PlaytimeIndexView } from '../../apis';
|
||||
|
||||
const PlayTimeContent = () => {
|
||||
const token = sessionStorage.getItem('token');
|
||||
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 [dataList, setDataList] = useState([]);
|
||||
const [resultData, setResultData] = useState([]);
|
||||
|
||||
const [sendDate, setSendDate] = useState(START_DATE);
|
||||
const [finishDate, setFinishDate] = useState(END_DATE);
|
||||
|
||||
useEffect(() => {
|
||||
fetchData(START_DATE, END_DATE);
|
||||
}, []);
|
||||
|
||||
// 이용자 지표 데이터
|
||||
const fetchData = async (startDate, endDate) => {
|
||||
const startDateToLocal =
|
||||
startDate.getFullYear() +
|
||||
'-' +
|
||||
(startDate.getMonth() + 1 < 9 ? '0' + (startDate.getMonth() + 1) : startDate.getMonth() + 1) +
|
||||
'-' +
|
||||
(startDate.getDate() < 9 ? '0' + startDate.getDate() : startDate.getDate());
|
||||
|
||||
const endDateToLocal =
|
||||
endDate.getFullYear() +
|
||||
'-' +
|
||||
(endDate.getMonth() + 1 < 9 ? '0' + (endDate.getMonth() + 1) : endDate.getMonth() + 1) +
|
||||
'-' +
|
||||
(endDate.getDate() < 9 ? '0' + endDate.getDate() : endDate.getDate());
|
||||
|
||||
setDataList(await PlaytimeIndexView(token, startDateToLocal, endDateToLocal));
|
||||
|
||||
setSendDate(startDateToLocal);
|
||||
setFinishDate(endDateToLocal);
|
||||
};
|
||||
|
||||
// 검색 함수
|
||||
const handleSearch = (send_dt, end_dt) => {
|
||||
fetchData(send_dt, end_dt);
|
||||
};
|
||||
|
||||
// 엑셀 다운로드
|
||||
const handleXlsxExport = () => {
|
||||
const fileName = 'Caliverse_PlayTime_Index.xlsx';
|
||||
PlaytimeIndexExport(token, fileName, sendDate, finishDate);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<PlayTimeSearchBar setResultData={setResultData} resultData={resultData} handleSearch={handleSearch} fetchData={fetchData} />
|
||||
<TableInfo>
|
||||
<ListOption>
|
||||
<Button theme="line" text="엑셀 다운로드" handleClick={handleXlsxExport} />
|
||||
</ListOption>
|
||||
</TableInfo>
|
||||
<IndexTableWrap>
|
||||
<TableStyle>
|
||||
<caption></caption>
|
||||
<thead>
|
||||
<tr>
|
||||
<th rowSpan="2" width="140">
|
||||
일자
|
||||
</th>
|
||||
<th colSpan="4" width="520">
|
||||
유저수
|
||||
</th>
|
||||
<th rowSpan="2" width="160">
|
||||
총 누적 플레이타임(분)
|
||||
</th>
|
||||
<th rowSpan="2" width="160">
|
||||
1인당 평균 플레이타임(분)
|
||||
</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>30분 이내</th>
|
||||
<th>30분 ~ 1시간</th>
|
||||
<th>1시간 ~ 3시간</th>
|
||||
<th>3시간 이상</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{dataList.playtime &&
|
||||
dataList.playtime.map(time => (
|
||||
<tr key={time.date}>
|
||||
<td>{time.date}</td>
|
||||
{time.user_cnt.map((cnt, index) => (
|
||||
<td className="text-left" key={index}>
|
||||
{cnt}
|
||||
</td>
|
||||
))}
|
||||
<td className="text-left">{time.total_time}</td>
|
||||
<td className="text-left">{time.average_time}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</TableStyle>
|
||||
</IndexTableWrap>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default PlayTimeContent;
|
||||
122
src/components/IndexManage/PlayTimeSearchBar.js
Normal file
@@ -0,0 +1,122 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { styled } from 'styled-components';
|
||||
import Button from '../../components/common/button/Button';
|
||||
|
||||
import 'react-datepicker/dist/react-datepicker.css';
|
||||
import DatePickerComponent from '../common/Date/DatePickerComponent';
|
||||
|
||||
import { FormWrapper, InputLabel, TextInput, SelectInput, BtnWrapper, InputGroup, DatePickerWrapper } from '../../styles/Components';
|
||||
|
||||
const PlayTimeSearchBar = ({ resultData, setResultData, handleSearch, fetchData }) => {
|
||||
// 초기 날짜 세팅
|
||||
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();
|
||||
|
||||
// resultData에 임의 날짜 넣어주기
|
||||
useEffect(() => {
|
||||
setResultData({
|
||||
send_dt: START_DATE,
|
||||
end_dt: END_DATE,
|
||||
});
|
||||
}, []);
|
||||
|
||||
const searchType = [
|
||||
{ name: '전체', value: 'ALL' },
|
||||
{ name: '한국어', value: 'KR' },
|
||||
{ name: '영어', value: 'EN' },
|
||||
{ name: '일본어', value: 'JP' },
|
||||
{ name: '태국어', value: 'TH' },
|
||||
];
|
||||
|
||||
// 발송 날짜 세팅 로직
|
||||
const handleSelectedDate = data => {
|
||||
const sendDate = new Date(data);
|
||||
const resultSendData = new Date(sendDate.getFullYear(), sendDate.getMonth(), sendDate.getDate());
|
||||
|
||||
setResultData({ ...resultData, send_dt: resultSendData });
|
||||
};
|
||||
|
||||
// 발송 날짜 세팅 로직
|
||||
const handleEndDate = data => {
|
||||
const endDate = new Date(data);
|
||||
const resultSendData = new Date(endDate.getFullYear(), endDate.getMonth(), endDate.getDate());
|
||||
|
||||
setResultData({ ...resultData, end_dt: resultSendData });
|
||||
};
|
||||
|
||||
const handleReset = e => {
|
||||
e.preventDefault();
|
||||
setResultData({ send_dt: START_DATE, end_dt: END_DATE });
|
||||
|
||||
fetchData(START_DATE, END_DATE);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<FormWrapper>
|
||||
<SearchbarStyle>
|
||||
<SearchItem>
|
||||
<InputLabel>집계 기준일</InputLabel>
|
||||
<InputGroup>
|
||||
<DatePickerWrapper>
|
||||
<DatePickerComponent
|
||||
name="시작 일자"
|
||||
selectedDate={resultData.send_dt}
|
||||
handleSelectedDate={data => handleSelectedDate(data)}
|
||||
maxDate={new Date()} />
|
||||
<span>~</span>
|
||||
<DatePickerComponent
|
||||
name="종료 일자"
|
||||
selectedDate={resultData.end_dt}
|
||||
handleSelectedDate={data => handleEndDate(data)}
|
||||
pastDate = {resultData.send_dt}
|
||||
maxDate={new Date()} />
|
||||
</DatePickerWrapper>
|
||||
</InputGroup>
|
||||
</SearchItem>
|
||||
<BtnWrapper $gap="8px">
|
||||
<Button theme="reset" handleClick={handleReset} />
|
||||
<Button
|
||||
theme="search"
|
||||
text="검색"
|
||||
handleClick={e => {
|
||||
e.preventDefault();
|
||||
handleSearch(resultData.send_dt, resultData.end_dt);
|
||||
}}
|
||||
/>
|
||||
</BtnWrapper>
|
||||
</SearchbarStyle>
|
||||
</FormWrapper>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default PlayTimeSearchBar;
|
||||
|
||||
const SearchbarStyle = styled.div`
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 20px 0;
|
||||
font-size: 14px;
|
||||
padding: 20px;
|
||||
border-top: 1px solid #000;
|
||||
border-bottom: 1px solid #000;
|
||||
margin-bottom: 40px;
|
||||
`;
|
||||
|
||||
const SearchItem = styled.div`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 20px;
|
||||
margin-right: 50px;
|
||||
|
||||
${TextInput}, ${SelectInput} {
|
||||
height: 35px;
|
||||
}
|
||||
${TextInput} {
|
||||
padding: 0 10px;
|
||||
max-width: 400px;
|
||||
}
|
||||
`;
|
||||
109
src/components/IndexManage/RetentionContent.js
Normal file
@@ -0,0 +1,109 @@
|
||||
import { Fragment, useEffect, useState } from 'react';
|
||||
|
||||
import Button from '../../components/common/button/Button';
|
||||
|
||||
import { TableStyle, TableInfo, ListOption, IndexTableWrap } from '../../styles/Components';
|
||||
import { RetentionSearchBar } from '../../components/IndexManage/index';
|
||||
import { RetentionIndexExport, RetentionIndexView } from '../../apis';
|
||||
|
||||
const RetentionContent = () => {
|
||||
const token = sessionStorage.getItem('token');
|
||||
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(new Date(d.setDate(d.getDate() - 1)).setHours(24, 0, 0, 0));
|
||||
|
||||
const [dataList, setDataList] = useState([]);
|
||||
const [resultData, setResultData] = useState([]);
|
||||
const [retentionData, setRetention] = useState(1);
|
||||
|
||||
const [sendDate, setSendDate] = useState(START_DATE);
|
||||
const [finishDate, setFinishDate] = useState(END_DATE);
|
||||
const [excelBtn, setExcelBtn] = useState(true); //true 시 비활성화
|
||||
|
||||
useEffect(() => {
|
||||
fetchData(START_DATE, END_DATE);
|
||||
}, []);
|
||||
|
||||
// Retention 지표 데이터
|
||||
const fetchData = async (startDate, endDate) => {
|
||||
const startDateToLocal =
|
||||
startDate.getFullYear() +
|
||||
'-' +
|
||||
(startDate.getMonth() + 1 < 9 ? '0' + (startDate.getMonth() + 1) : startDate.getMonth() + 1) +
|
||||
'-' +
|
||||
(startDate.getDate() < 9 ? '0' + startDate.getDate() : startDate.getDate());
|
||||
|
||||
const endDateToLocal =
|
||||
endDate.getFullYear() +
|
||||
'-' +
|
||||
(endDate.getMonth() + 1 < 9 ? '0' + (endDate.getMonth() + 1) : endDate.getMonth() + 1) +
|
||||
'-' +
|
||||
(endDate.getDate() < 9 ? '0' + endDate.getDate() : endDate.getDate());
|
||||
|
||||
setDataList(await RetentionIndexView(token, startDateToLocal, endDateToLocal));
|
||||
|
||||
console.log(dataList);
|
||||
|
||||
setSendDate(startDateToLocal);
|
||||
setFinishDate(endDateToLocal);
|
||||
};
|
||||
|
||||
// 검색 함수
|
||||
const handleSearch = (send_dt, end_dt) => {
|
||||
fetchData(send_dt, end_dt);
|
||||
setRetention(resultData.retention);
|
||||
};
|
||||
|
||||
// 엑셀 다운로드
|
||||
const handleXlsxExport = () => {
|
||||
const fileName = 'Caliverse_Retention_Index.xlsx';
|
||||
|
||||
if(!excelBtn){
|
||||
RetentionIndexExport(token, fileName, sendDate, finishDate);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<RetentionSearchBar setResultData={setResultData} resultData={resultData}
|
||||
handleSearch={handleSearch} fetchData={fetchData} setRetention={setRetention} setExcelBtn={setExcelBtn} />
|
||||
<TableInfo>
|
||||
<ListOption>
|
||||
<Button
|
||||
theme={excelBtn === true ? "disable" : "line"}
|
||||
text="엑셀 다운로드"
|
||||
disabled={handleXlsxExport}
|
||||
handleClick={handleXlsxExport} />
|
||||
</ListOption>
|
||||
</TableInfo>
|
||||
<IndexTableWrap>
|
||||
<TableStyle>
|
||||
<caption></caption>
|
||||
<thead>
|
||||
<tr>
|
||||
{/* <th width="100">국가</th> */}
|
||||
<th width="150">일자</th>
|
||||
<th className="cell-nru">NRU</th>
|
||||
{[...Array(Number(retentionData))].map((value, index) => {
|
||||
return <th key={index}>{`D+${index + 1}`}</th>;
|
||||
})}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{dataList.retention &&
|
||||
dataList.retention.map(data => (
|
||||
<tr className="cell-nru-th" key={data.date}>
|
||||
<td>{data.date}</td>
|
||||
{data['d-day'].map((day, index) => (
|
||||
<td key={index}>{day.dif}</td>
|
||||
))}
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</TableStyle>
|
||||
</IndexTableWrap>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default RetentionContent;
|
||||
166
src/components/IndexManage/RetentionSearchBar.js
Normal file
@@ -0,0 +1,166 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { styled } from 'styled-components';
|
||||
import Button from '../../components/common/button/Button';
|
||||
import DatePickerComponent from '../common/Date/DatePickerComponent';
|
||||
|
||||
import { FormWrapper, InputLabel, TextInput, SelectInput, BtnWrapper, InputGroup, DatePickerWrapper, AlertText } from '../../styles/Components';
|
||||
|
||||
const RetentionSearchBar = ({ resultData, setResultData, handleSearch, fetchData, setRetention, setExcelBtn }) => {
|
||||
// 초기 날짜 세팅
|
||||
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(new Date(d.setDate(d.getDate() - 1)).setHours(24, 0, 0, 0));
|
||||
const [errorMessage, setErrorMessage] = useState('');
|
||||
const [period, setPeriod] = useState(0);
|
||||
|
||||
// resultData에 임의 날짜 넣어주기
|
||||
useEffect(() => {
|
||||
setResultData({
|
||||
send_dt: START_DATE,
|
||||
end_dt: '',
|
||||
retention: 0,
|
||||
});
|
||||
}, []);
|
||||
|
||||
// 발송 날짜 세팅 로직
|
||||
const handleSelectedDate = data => {
|
||||
const sendDate = new Date(data);
|
||||
const resultSendData = new Date(sendDate.getFullYear(), sendDate.getMonth(), sendDate.getDate());
|
||||
|
||||
const resultEndDate = new Date(resultSendData);
|
||||
resultEndDate.setDate(resultEndDate.getDate() + Number(resultData.retention));
|
||||
|
||||
setResultData({ ...resultData, send_dt: resultSendData, end_dt: resultEndDate });
|
||||
};
|
||||
|
||||
// // 발송 날짜 세팅 로직
|
||||
// const handleEndDate = data => {
|
||||
// const endDate = new Date(data);
|
||||
// const resultSendData = new Date(endDate.getFullYear(), endDate.getMonth(), endDate.getDate());
|
||||
|
||||
// setResultData({ ...resultData, end_dt: resultSendData });
|
||||
// };
|
||||
|
||||
// Retention 세팅 로직
|
||||
const handleRetention = e => {
|
||||
const value = e.target.value;
|
||||
|
||||
const resultEndDate = new Date(resultData.send_dt);
|
||||
resultEndDate.setDate(resultEndDate.getDate() + Number(value));
|
||||
|
||||
setResultData({ ...resultData, end_dt: resultEndDate, retention: value });
|
||||
setPeriod(value);
|
||||
};
|
||||
|
||||
//Retention 범위 선택 후 disable 처리 로직
|
||||
const handleSearchBtn = e => {
|
||||
e.preventDefault();
|
||||
|
||||
if(period == 0) {
|
||||
setErrorMessage("필수값을 선택하세요.");
|
||||
return false;
|
||||
} else {
|
||||
setErrorMessage("");
|
||||
setExcelBtn(false); //활성화
|
||||
handleSearch(resultData.send_dt, resultData.end_dt);
|
||||
}
|
||||
}
|
||||
|
||||
const handleReset = e => {
|
||||
e.preventDefault();
|
||||
setResultData({ send_dt: START_DATE, end_dt: '', retention: 0 });
|
||||
setRetention(1);
|
||||
setErrorMessage("");
|
||||
setPeriod(1);
|
||||
setExcelBtn(true); //비활성화
|
||||
|
||||
fetchData(START_DATE, END_DATE);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<FormWrapper>
|
||||
<SearchbarStyle>
|
||||
<SearchItem>
|
||||
<InputLabel>집계 기준일</InputLabel>
|
||||
<InputGroup>
|
||||
<DatePickerWrapper>
|
||||
<DatePickerComponent
|
||||
name="시작 일자" selectedDate={resultData.send_dt}
|
||||
handleSelectedDate={data => handleSelectedDate(data)}
|
||||
maxDate={new Date()} />
|
||||
<span>~</span>
|
||||
<DatePickerComponent
|
||||
name="종료 일자"
|
||||
selectedDate={resultData.end_dt}
|
||||
maxDate={new Date()}
|
||||
readOnly={true}
|
||||
disabled={true}
|
||||
type="retention" />
|
||||
</DatePickerWrapper>
|
||||
</InputGroup>
|
||||
</SearchItem>
|
||||
<SearchItem>
|
||||
<InputLabel>Retention 범위</InputLabel>
|
||||
<SelectInput
|
||||
onChange={e => handleRetention(e)} value={resultData.retention}>
|
||||
<option value={0}>선택</option>
|
||||
<option value={1}>D+1</option>
|
||||
<option value={7}>D+7</option>
|
||||
<option value={30}>D+30</option>
|
||||
</SelectInput>
|
||||
</SearchItem>
|
||||
{/* 기획 보류 */}
|
||||
{/* <SearchItem>
|
||||
<InputLabel>조회 국가</InputLabel>
|
||||
<SelectInput>
|
||||
<option value="">ALL</option>
|
||||
<option value="">KR</option>
|
||||
<option value="">EN</option>
|
||||
<option value="">JP</option>
|
||||
<option value="">TH</option>
|
||||
</SelectInput>
|
||||
</SearchItem> */}
|
||||
<BtnWrapper $gap="8px">
|
||||
<Button theme="reset" handleClick={handleReset} />
|
||||
<Button
|
||||
theme="search"
|
||||
text="검색"
|
||||
handleClick={handleSearchBtn}
|
||||
/>
|
||||
<AlertText>{errorMessage}</AlertText>
|
||||
</BtnWrapper>
|
||||
</SearchbarStyle>
|
||||
</FormWrapper>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default RetentionSearchBar;
|
||||
|
||||
const SearchbarStyle = styled.div`
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 20px 0;
|
||||
font-size: 14px;
|
||||
padding: 20px;
|
||||
border-top: 1px solid #000;
|
||||
border-bottom: 1px solid #000;
|
||||
margin-bottom: 40px;
|
||||
`;
|
||||
|
||||
const SearchItem = styled.div`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 20px;
|
||||
margin-right: 50px;
|
||||
|
||||
${TextInput}, ${SelectInput} {
|
||||
height: 35px;
|
||||
}
|
||||
${TextInput} {
|
||||
padding: 0 10px;
|
||||
max-width: 400px;
|
||||
}
|
||||
`;
|
||||
90
src/components/IndexManage/SegmentContent.js
Normal file
@@ -0,0 +1,90 @@
|
||||
import { Fragment, useEffect, useState } from 'react';
|
||||
|
||||
import Button from '../../components/common/button/Button';
|
||||
|
||||
import { TableStyle, TableInfo, ListOption, IndexTableWrap } from '../../styles/Components';
|
||||
import { SegmentSearchBar } from '../../components/IndexManage/index';
|
||||
import { SegmentIndexExport, SegmentIndexView } from '../../apis';
|
||||
|
||||
const SegmentContent = () => {
|
||||
const token = sessionStorage.getItem('token');
|
||||
const END_DATE = new Date();
|
||||
|
||||
const [dataList, setDataList] = useState([]);
|
||||
const [resultData, setResultData] = useState([]);
|
||||
|
||||
const [finishDate, setFinishDate] = useState(END_DATE);
|
||||
|
||||
useEffect(() => {
|
||||
fetchData(END_DATE);
|
||||
}, []);
|
||||
|
||||
// Retention 지표 데이터
|
||||
const fetchData = async endDate => {
|
||||
const endDateToLocal =
|
||||
endDate.getFullYear() +
|
||||
'-' +
|
||||
(endDate.getMonth() + 1 < 9 ? '0' + (endDate.getMonth() + 1) : endDate.getMonth() + 1) +
|
||||
'-' +
|
||||
(endDate.getDate() < 9 ? '0' + endDate.getDate() : endDate.getDate());
|
||||
|
||||
setDataList(await SegmentIndexView(token, endDateToLocal));
|
||||
setFinishDate(endDateToLocal);
|
||||
};
|
||||
|
||||
// 검색 함수
|
||||
const handleSearch = end_dt => {
|
||||
fetchData(end_dt);
|
||||
};
|
||||
|
||||
// 엑셀 다운로드
|
||||
const handleXlsxExport = () => {
|
||||
const fileName = 'Caliverse_Segment_Index.xlsx';
|
||||
SegmentIndexExport(token, fileName, finishDate);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<SegmentSearchBar setResultData={setResultData} resultData={resultData} handleSearch={handleSearch} fetchData={fetchData} />
|
||||
<TableInfo>
|
||||
<ListOption>
|
||||
<Button theme="line" text="엑셀 다운로드" handleClick={handleXlsxExport} />
|
||||
</ListOption>
|
||||
</TableInfo>
|
||||
<IndexTableWrap>
|
||||
<TableStyle>
|
||||
<caption></caption>
|
||||
<thead>
|
||||
<tr>
|
||||
<th colSpan="1" width="200">
|
||||
{dataList && dataList.start_dt} ~ {dataList && dataList.end_dt}
|
||||
</th>
|
||||
<th colSpan="2" width="400">
|
||||
KIP
|
||||
</th>
|
||||
</tr>
|
||||
<tr>
|
||||
{/* <th>국가</th> */}
|
||||
<th>세그먼트 분류</th>
|
||||
<th>AU</th>
|
||||
<th>AU Percentage by User Segment (%)</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{dataList && dataList.segment &&
|
||||
dataList.segment.map((segment, index) => (
|
||||
<tr key={index}>
|
||||
{/* <td rowSpan="6">TH</td> */}
|
||||
<td>{segment.type}</td>
|
||||
<td>{segment.au}</td>
|
||||
<td>{segment.dif}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</TableStyle>
|
||||
</IndexTableWrap>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default SegmentContent;
|
||||
123
src/components/IndexManage/SegmentSearchBar.js
Normal file
@@ -0,0 +1,123 @@
|
||||
import { useEffect } from 'react';
|
||||
import { styled } from 'styled-components';
|
||||
import Button from '../../components/common/button/Button';
|
||||
import DatePickerComponent from '../common/Date/DatePickerComponent';
|
||||
|
||||
import { FormWrapper, InputLabel, TextInput, SelectInput, BtnWrapper, InputGroup, DatePickerWrapper } from '../../styles/Components';
|
||||
|
||||
const SegmentSearchBar = ({ resultData, setResultData, handleSearch, fetchData }) => {
|
||||
// 초기 날짜 세팅
|
||||
const END_DATE = new Date();
|
||||
|
||||
// resultData에 임의 날짜 넣어주기
|
||||
useEffect(() => {
|
||||
setResultData({
|
||||
search_dt: END_DATE,
|
||||
});
|
||||
}, []);
|
||||
|
||||
// 발송 날짜 세팅 로직
|
||||
const handleSelectedDate = data => {
|
||||
const sendDate = new Date(data);
|
||||
const resultSendData = new Date(sendDate.getFullYear(), sendDate.getMonth(), sendDate.getDate());
|
||||
|
||||
setResultData({ search_dt: resultSendData });
|
||||
};
|
||||
|
||||
// 발송 날짜 세팅 로직
|
||||
|
||||
const handleReset = e => {
|
||||
e.preventDefault();
|
||||
setResultData({ search_dt: END_DATE });
|
||||
|
||||
fetchData(END_DATE);
|
||||
};
|
||||
|
||||
// console.log(resultData);
|
||||
|
||||
return (
|
||||
<>
|
||||
<FormWrapper>
|
||||
<SearchbarStyle2>
|
||||
<SearchRow>
|
||||
<SearchItem>
|
||||
<InputLabel>집계 기준일</InputLabel>
|
||||
<InputGroup>
|
||||
<DatePickerWrapper>
|
||||
<DatePickerComponent name="집계 기준일" selectedDate={resultData.search_dt} handleSelectedDate={data => handleSelectedDate(data)} maxDate={new Date()} />
|
||||
</DatePickerWrapper>
|
||||
</InputGroup>
|
||||
</SearchItem>
|
||||
{/* 기획 보류 */}
|
||||
{/* <SearchItem>
|
||||
<InputLabel>조회 국가</InputLabel>
|
||||
<SelectInput>
|
||||
<option value="">ALL</option>
|
||||
<option value="">KR</option>
|
||||
<option value="">EN</option>
|
||||
<option value="">JP</option>
|
||||
<option value="">TH</option>
|
||||
</SelectInput>
|
||||
</SearchItem> */}
|
||||
<BtnWrapper $gap="8px">
|
||||
<Button theme="reset" handleClick={handleReset} />
|
||||
<Button
|
||||
theme="search"
|
||||
text="검색"
|
||||
handleClick={e => {
|
||||
e.preventDefault();
|
||||
handleSearch(resultData.search_dt);
|
||||
}}
|
||||
/>
|
||||
</BtnWrapper>
|
||||
</SearchRow>
|
||||
<SearchNoti>※ 집계 기준일 기준 -100일까지의 Segment 유저 분포를 집계합니다.</SearchNoti>
|
||||
</SearchbarStyle2>
|
||||
</FormWrapper>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default SegmentSearchBar;
|
||||
|
||||
const SearchbarStyle = styled.div`
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 20px 0;
|
||||
font-size: 14px;
|
||||
padding: 20px;
|
||||
border-top: 1px solid #000;
|
||||
border-bottom: 1px solid #000;
|
||||
margin-bottom: 40px;
|
||||
`;
|
||||
|
||||
const SearchbarStyle2 = styled(SearchbarStyle)`
|
||||
flex-flow: column;
|
||||
gap: 10px;
|
||||
`;
|
||||
|
||||
const SearchRow = styled.div`
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 20px 0;
|
||||
`;
|
||||
const SearchItem = styled.div`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 20px;
|
||||
margin-right: 50px;
|
||||
|
||||
${TextInput}, ${SelectInput} {
|
||||
height: 35px;
|
||||
}
|
||||
${TextInput} {
|
||||
padding: 0 10px;
|
||||
max-width: 400px;
|
||||
}
|
||||
`;
|
||||
const SearchNoti = styled.div`
|
||||
font-size: 12px;
|
||||
color: #999;
|
||||
letter-spacing: 0.5px;
|
||||
`;
|
||||
133
src/components/IndexManage/UserContent.js
Normal file
@@ -0,0 +1,133 @@
|
||||
import { Fragment, useEffect, useRef, useState } from 'react';
|
||||
|
||||
import Button from '../../components/common/button/Button';
|
||||
|
||||
import { Title, TableStyle, TableInfo, ListOption, IndexTableWrap } from '../../styles/Components';
|
||||
import { UserIndexSearchBar, DailyDashBoard } from '../../components/IndexManage/index';
|
||||
|
||||
import { userIndexView, userIndexExport } from '../../apis';
|
||||
import Loading from '../common/Loading';
|
||||
import { ExcelDownButton } from '../common';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { formatStringDate } from '../../utils';
|
||||
|
||||
const UserContent = () => {
|
||||
const token = sessionStorage.getItem('token');
|
||||
const { t } = useTranslation();
|
||||
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 [loading, setLoading] = useState(false);
|
||||
|
||||
const tableRef = useRef(null);
|
||||
|
||||
const [dataList, setDataList] = useState([]);
|
||||
const [resultData, setResultData] = useState([]);
|
||||
|
||||
// const [sendDate, setSendDate] = useState(START_DATE);
|
||||
// const [finishDate, setFinishDate] = useState(END_DATE);
|
||||
|
||||
const headers = [
|
||||
{key: 'date', label: '일자'},
|
||||
{key: 'nru', label: 'NRU'},
|
||||
{key: 'ugqCreate', label: '일자'},
|
||||
{key: 'dglc', label: '일자'},
|
||||
{key: 'dau', label: '일자'},
|
||||
{key: 'mcu', label: '일자'},
|
||||
{key: 'date', label: '일자'},
|
||||
{key: 'date', label: '일자'},
|
||||
]
|
||||
|
||||
useEffect(() => {
|
||||
fetchData(START_DATE, END_DATE);
|
||||
}, []);
|
||||
|
||||
// 이용자 지표 데이터
|
||||
const fetchData = async (startDate, endDate) => {
|
||||
setLoading(true);
|
||||
|
||||
const startDateToLocal = formatStringDate(startDate);
|
||||
const endDateToLocal = formatStringDate(endDate);
|
||||
|
||||
await userIndexView(token, startDateToLocal, endDateToLocal).then(data => {
|
||||
setDataList(data);
|
||||
setLoading(false);
|
||||
});
|
||||
|
||||
// setSendDate(startDateToLocal);
|
||||
// setFinishDate(endDateToLocal);
|
||||
};
|
||||
|
||||
// 검색 함수
|
||||
const handleSearch = (send_dt, end_dt) => {
|
||||
fetchData(send_dt, end_dt);
|
||||
};
|
||||
|
||||
// 엑셀 다운로드
|
||||
// const handleXlsxExport = () => {
|
||||
// const fileName = 'Caliverse_User_Index.xlsx';
|
||||
// userIndexExport(token, fileName, sendDate, finishDate);
|
||||
// };
|
||||
|
||||
return (
|
||||
<>
|
||||
<DailyDashBoard />
|
||||
<UserIndexSearchBar setResultData={setResultData} resultData={resultData} handleSearch={handleSearch} fetchData={fetchData} />
|
||||
<TableInfo>
|
||||
<ListOption>
|
||||
<ExcelDownButton tableRef={tableRef} fileName={t('FILE_INDEX_USER_CONTENT')} />
|
||||
</ListOption>
|
||||
</TableInfo>
|
||||
<IndexTableWrap>
|
||||
<TableStyle ref={tableRef}>
|
||||
<caption></caption>
|
||||
<thead>
|
||||
<tr>
|
||||
{/*<th width="100">국가</th>*/}
|
||||
<th width="120">일자</th>
|
||||
<th width="100">NRU</th>
|
||||
<th width="100">UGQ(생성)</th>
|
||||
<th width="100">DGLC</th>
|
||||
<th width="100">DAU</th>
|
||||
<th width="100">MCU</th>
|
||||
<th width="100">총 Playtime(HR)</th>
|
||||
<th width="100">평균 Playtime</th>
|
||||
<th width="100">WAU</th>
|
||||
<th width="100">MAU</th>
|
||||
<th width="100">ServerCount</th>
|
||||
<th width="100">DB Read Count</th>
|
||||
<th width="100">DB Write Count</th>
|
||||
<th width="100">DB Sum</th>
|
||||
{/*<th width="200">PU</th>*/}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{dataList &&
|
||||
dataList.map((data, index) => (
|
||||
<tr key={index}>
|
||||
{/*<td rowSpan={data.length}>ALL</td>*/}
|
||||
<td>{data.date}</td>
|
||||
<td>{data.nru}</td>
|
||||
<td>{data.ugqCreate}</td>
|
||||
<td>{data.dglc}</td>
|
||||
<td>{data.dau}</td>
|
||||
<td>{data.mcu}</td>
|
||||
<td>{Math.ceil(data.playtime / 3600) || 0}</td>
|
||||
<td>{Math.round(((data.playtime / 3600) / data.dau) * 100) / 100 || 0}</td>
|
||||
<td>{data.wau}</td>
|
||||
<td>{data.mau}</td>
|
||||
<td>{data.serverCount}</td>
|
||||
<td>{data.readCapacity}</td>
|
||||
<td>{data.writeCapacity}</td>
|
||||
<td>{data.readCapacity + data.writeCapacity}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</TableStyle>
|
||||
</IndexTableWrap>
|
||||
{loading && <Loading/>}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default UserContent;
|
||||
135
src/components/IndexManage/UserIndexSearchBar.js
Normal file
@@ -0,0 +1,135 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { styled } from 'styled-components';
|
||||
import Button from '../../components/common/button/Button';
|
||||
|
||||
import 'react-datepicker/dist/react-datepicker.css';
|
||||
import DatePickerComponent from '../common/Date/DatePickerComponent';
|
||||
|
||||
import { FormWrapper, InputLabel, TextInput, SelectInput, BtnWrapper, InputGroup, DatePickerWrapper } from '../../styles/Components';
|
||||
|
||||
const UserIndexSearchBar = ({ resultData, setResultData, handleSearch, fetchData }) => {
|
||||
// 초기 날짜 세팅
|
||||
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();
|
||||
|
||||
// resultData에 임의 날짜 넣어주기
|
||||
useEffect(() => {
|
||||
setResultData({
|
||||
send_dt: START_DATE,
|
||||
end_dt: END_DATE,
|
||||
});
|
||||
}, []);
|
||||
|
||||
const searchType = [
|
||||
{ name: '전체', value: 'ALL' },
|
||||
{ name: '한국어', value: 'KR' },
|
||||
{ name: '영어', value: 'EN' },
|
||||
{ name: '일본어', value: 'JP' },
|
||||
{ name: '태국어', value: 'TH' },
|
||||
];
|
||||
|
||||
// 발송 날짜 세팅 로직
|
||||
const handleSelectedDate = data => {
|
||||
const sendDate = new Date(data);
|
||||
const resultSendData = new Date(sendDate.getFullYear(), sendDate.getMonth(), sendDate.getDate());
|
||||
|
||||
setResultData({ ...resultData, send_dt: resultSendData });
|
||||
};
|
||||
|
||||
// 발송 날짜 세팅 로직
|
||||
const handleEndDate = data => {
|
||||
const endDate = new Date(data);
|
||||
const resultSendData = new Date(endDate.getFullYear(), endDate.getMonth(), endDate.getDate());
|
||||
|
||||
setResultData({ ...resultData, end_dt: resultSendData });
|
||||
};
|
||||
|
||||
const handleReset = e => {
|
||||
e.preventDefault();
|
||||
setResultData({ send_dt: START_DATE, end_dt: END_DATE });
|
||||
|
||||
fetchData(START_DATE, END_DATE);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<FormWrapper>
|
||||
<SearchbarStyle>
|
||||
<SearchItem>
|
||||
<InputLabel>집계 기간</InputLabel>
|
||||
<InputGroup>
|
||||
<DatePickerWrapper>
|
||||
<DatePickerComponent
|
||||
name="시작 일자"
|
||||
selectedDate={resultData.send_dt}
|
||||
handleSelectedDate={data => handleSelectedDate(data)}
|
||||
maxDate={new Date()} />
|
||||
<span>~</span>
|
||||
<DatePickerComponent
|
||||
name="종료 일자"
|
||||
selectedDate={resultData.end_dt}
|
||||
handleSelectedDate={data => handleEndDate(data)}
|
||||
pastDate = {resultData.send_dt}
|
||||
maxDate={new Date()} />
|
||||
</DatePickerWrapper>
|
||||
</InputGroup>
|
||||
</SearchItem>
|
||||
{/* 기획 보류 */}
|
||||
{/* <SearchItem>
|
||||
<InputLabel>조회 국가</InputLabel>
|
||||
<SelectInput
|
||||
// onChange={e => setResultData({ ...resultData, searchType: e.target.value })}
|
||||
>
|
||||
{searchType.map((option, index) => (
|
||||
<option key={index} value={option.value}>
|
||||
{option.name}
|
||||
</option>
|
||||
))}
|
||||
</SelectInput>
|
||||
</SearchItem> */}
|
||||
<BtnWrapper $gap="8px">
|
||||
<Button theme="reset" handleClick={handleReset} />
|
||||
<Button
|
||||
theme="search"
|
||||
text="검색"
|
||||
handleClick={e => {
|
||||
e.preventDefault();
|
||||
handleSearch(resultData.send_dt, resultData.end_dt);
|
||||
}}
|
||||
/>
|
||||
</BtnWrapper>
|
||||
</SearchbarStyle>
|
||||
</FormWrapper>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default UserIndexSearchBar;
|
||||
|
||||
const SearchbarStyle = styled.div`
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 20px 0;
|
||||
font-size: 14px;
|
||||
padding: 20px;
|
||||
border-top: 1px solid #000;
|
||||
border-bottom: 1px solid #000;
|
||||
margin-bottom: 40px;
|
||||
`;
|
||||
|
||||
const SearchItem = styled.div`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 20px;
|
||||
margin-right: 50px;
|
||||
|
||||
${TextInput}, ${SelectInput} {
|
||||
height: 35px;
|
||||
}
|
||||
${TextInput} {
|
||||
padding: 0 10px;
|
||||
max-width: 400px;
|
||||
}
|
||||
`;
|
||||
219
src/components/IndexManage/VBPContent.js
Normal file
@@ -0,0 +1,219 @@
|
||||
import { styled } from 'styled-components';
|
||||
import { useState, useEffect } from 'react';
|
||||
|
||||
import { TableStyle, TableInfo, ListOption } from '../../styles/Components';
|
||||
|
||||
import Button from '../../components/common/button/Button';
|
||||
import VBPSearchBar from '../../components/IndexManage/VBPSearchBar';
|
||||
import { VBPIndexExport, VbpIndexView } from '../../apis';
|
||||
|
||||
const VBPContent = () => {
|
||||
const token = sessionStorage.getItem('token');
|
||||
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 [sendDate, setSendDate] = useState(START_DATE);
|
||||
const [finishDate, setFinishDate] = useState(END_DATE);
|
||||
|
||||
const [dataList, setDataList] = useState([]);
|
||||
useEffect(() => {
|
||||
fetchData(START_DATE, END_DATE);
|
||||
}, []);
|
||||
|
||||
// console.log(dataList);
|
||||
|
||||
const fetchData = async (startDate, endDate) => {
|
||||
const startDateToLocal =
|
||||
startDate.getFullYear() +
|
||||
'-' +
|
||||
(startDate.getMonth() + 1 < 9 ? '0' + (startDate.getMonth() + 1) : startDate.getMonth() + 1) +
|
||||
'-' +
|
||||
(startDate.getDate() < 9 ? '0' + startDate.getDate() : startDate.getDate());
|
||||
|
||||
const endDateToLocal =
|
||||
endDate.getFullYear() +
|
||||
'-' +
|
||||
(endDate.getMonth() + 1 < 9 ? '0' + (endDate.getMonth() + 1) : endDate.getMonth() + 1) +
|
||||
'-' +
|
||||
(endDate.getDate() < 9 ? '0' + endDate.getDate() : endDate.getDate());
|
||||
|
||||
setDataList(await VbpIndexView(token, startDateToLocal, endDateToLocal));
|
||||
|
||||
setSendDate(startDateToLocal);
|
||||
setFinishDate(endDateToLocal);
|
||||
};
|
||||
|
||||
// 엑셀 다운로드
|
||||
const handleXlsxExport = () => {
|
||||
const fileName = 'Caliverse_VBP_Index.xlsx';
|
||||
VBPIndexExport(token, fileName, sendDate, finishDate);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<VBPSearchBar fetchData={fetchData} />
|
||||
<TableInfo>
|
||||
<ListOption>
|
||||
<Button theme="line" text="엑셀 다운로드" handleClick={handleXlsxExport} />
|
||||
</ListOption>
|
||||
</TableInfo>
|
||||
<TableWrapper>
|
||||
<EconomicTable>
|
||||
<thead>
|
||||
<tr>
|
||||
<th colSpan="2" className="text-center" width="300">
|
||||
Product
|
||||
</th>
|
||||
<th width="160">2023-08-07</th>
|
||||
<th width="160">2023-08-08</th>
|
||||
<th width="160">2023-08-09</th>
|
||||
<th width="160">2023-08-10</th>
|
||||
<th width="160">2023-08-11</th>
|
||||
<th width="160">2023-08-12</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<TableTitle colSpan="2">(Total) VBP 생산량</TableTitle>
|
||||
<TableData>500000</TableData>
|
||||
<TableData>500000</TableData>
|
||||
<TableData>500000</TableData>
|
||||
<TableData>500000</TableData>
|
||||
<TableData>500000</TableData>
|
||||
<TableData>500000</TableData>
|
||||
</tr>
|
||||
<tr>
|
||||
<TableTitle colSpan="2">(Total) VBP 소진량</TableTitle>
|
||||
<TableData>490000</TableData>
|
||||
<TableData>490000</TableData>
|
||||
<TableData>490000</TableData>
|
||||
<TableData>490000</TableData>
|
||||
<TableData>490000</TableData>
|
||||
<TableData>490000</TableData>
|
||||
</tr>
|
||||
<tr>
|
||||
<TableTitle colSpan="2">(Total) VBP 보유량</TableTitle>
|
||||
<TableData>3.2M</TableData>
|
||||
<TableData>3.3M</TableData>
|
||||
<TableData>3.3M</TableData>
|
||||
<TableData>3.4M</TableData>
|
||||
<TableData>3.5M</TableData>
|
||||
<TableData>3.5M</TableData>
|
||||
</tr>
|
||||
<tr>
|
||||
<TableTitle rowSpan="2">GET</TableTitle>
|
||||
<TableTitle>퀘스트 보상</TableTitle>
|
||||
<TableData>150000</TableData>
|
||||
<TableData>150000</TableData>
|
||||
<TableData>150000</TableData>
|
||||
<TableData>150000</TableData>
|
||||
<TableData>150000</TableData>
|
||||
<TableData>150000</TableData>
|
||||
</tr>
|
||||
<tr>
|
||||
<TableTitle>시즌패스 보상</TableTitle>
|
||||
<TableData>150000</TableData>
|
||||
<TableData>150000</TableData>
|
||||
<TableData>150000</TableData>
|
||||
<TableData>150000</TableData>
|
||||
<TableData>150000</TableData>
|
||||
<TableData>150000</TableData>
|
||||
</tr>
|
||||
<tr>
|
||||
<TableTitle rowSpan="2">USE</TableTitle>
|
||||
<TableTitle>퀘스트 보상</TableTitle>
|
||||
<TableData>150000</TableData>
|
||||
<TableData>150000</TableData>
|
||||
<TableData>150000</TableData>
|
||||
<TableData>150000</TableData>
|
||||
<TableData>150000</TableData>
|
||||
<TableData>150000</TableData>
|
||||
</tr>
|
||||
<tr>
|
||||
<TableTitle>시즌패스 보상</TableTitle>
|
||||
<TableData>150000</TableData>
|
||||
<TableData>150000</TableData>
|
||||
<TableData>150000</TableData>
|
||||
<TableData>150000</TableData>
|
||||
<TableData>150000</TableData>
|
||||
<TableData>150000</TableData>
|
||||
</tr>
|
||||
|
||||
{/* {mokupData.map((data, index) => (
|
||||
<Fragment key={index}>
|
||||
<tr>
|
||||
<td>{data.date}</td>
|
||||
<td>{data.name}</td>
|
||||
<td>{data.trader}</td>
|
||||
<td>{data.id}</td>
|
||||
<td>{data.key}</td>
|
||||
</tr>
|
||||
</Fragment>
|
||||
))} */}
|
||||
</tbody>
|
||||
</EconomicTable>
|
||||
</TableWrapper>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default VBPContent;
|
||||
|
||||
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: 900px;
|
||||
th {
|
||||
&.cell-nru {
|
||||
background: #f0f0f0;
|
||||
border-left: 1px solid #aaa;
|
||||
border-right: 1px solid #aaa;
|
||||
}
|
||||
}
|
||||
td {
|
||||
&.blank {
|
||||
background: #f9f9f9;
|
||||
}
|
||||
&.cell-nru {
|
||||
background: #fafafa;
|
||||
border-left: 1px solid #aaa;
|
||||
border-right: 1px solid #aaa;
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const TableData = styled.td`
|
||||
background: ${props => (props.$state === 'danger' ? '#d60000' : props.$state === 'blank' ? '#F9F9F9' : 'transparent')};
|
||||
color: ${props => (props.$state === 'danger' ? '#fff' : '#2c2c2c')};
|
||||
`;
|
||||
|
||||
const TableTitle = styled.td`
|
||||
text-align: center;
|
||||
`;
|
||||
|
||||
const EconomicTable = styled(TableStyle)`
|
||||
${TableData} {
|
||||
text-align: left;
|
||||
}
|
||||
tbody {
|
||||
tr:nth-child(1),
|
||||
tr:nth-child(2),
|
||||
tr:nth-child(3) {
|
||||
background: #f5fcff;
|
||||
}
|
||||
}
|
||||
`;
|
||||
112
src/components/IndexManage/VBPSearchBar.js
Normal file
@@ -0,0 +1,112 @@
|
||||
import { useState } from 'react';
|
||||
import { styled } from 'styled-components';
|
||||
import Button from '../../components/common/button/Button';
|
||||
|
||||
import DatePickerComponent from '../common/Date/DatePickerComponent';
|
||||
|
||||
import { FormWrapper, InputLabel, TextInput, SelectInput, BtnWrapper, InputGroup, DatePickerWrapper } from '../../styles/Components';
|
||||
|
||||
const VBPSearchBar = ({ fetchData }) => {
|
||||
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 [resultData, setResultData] = useState({
|
||||
send_dt: START_DATE,
|
||||
end_dt: END_DATE,
|
||||
});
|
||||
|
||||
// 발송 날짜 세팅 로직
|
||||
const handleSelectedDate = data => {
|
||||
const sendDate = new Date(data);
|
||||
const resultSendData = new Date(sendDate.getFullYear(), sendDate.getMonth(), sendDate.getDate());
|
||||
|
||||
setResultData({ ...resultData, send_dt: resultSendData });
|
||||
};
|
||||
|
||||
// 발송 날짜 세팅 로직
|
||||
const handleEndDate = data => {
|
||||
const endDate = new Date(data);
|
||||
const resultSendData = new Date(endDate.getFullYear(), endDate.getMonth(), endDate.getDate());
|
||||
|
||||
setResultData({ ...resultData, end_dt: resultSendData });
|
||||
};
|
||||
|
||||
const handleReset = e => {
|
||||
e.preventDefault();
|
||||
setResultData({ send_dt: START_DATE, end_dt: END_DATE });
|
||||
|
||||
fetchData(START_DATE, END_DATE);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<FormWrapper>
|
||||
<SearchbarStyle>
|
||||
<SearchItem>
|
||||
<InputLabel>집계 기간</InputLabel>
|
||||
<InputGroup>
|
||||
<DatePickerWrapper>
|
||||
<DatePickerComponent
|
||||
name="시작 일자" selectedDate={resultData.send_dt}
|
||||
handleSelectedDate={data => handleSelectedDate(data)}
|
||||
maxDate={new Date()} />
|
||||
<span>~</span>
|
||||
<DatePickerComponent
|
||||
name="종료 일자" selectedDate={resultData.end_dt}
|
||||
handleSelectedDate={data => handleEndDate(data)}
|
||||
pastDate = {resultData.send_dt}
|
||||
maxDate={new Date()} />
|
||||
</DatePickerWrapper>
|
||||
</InputGroup>
|
||||
</SearchItem>
|
||||
<BtnWrapper $gap="8px">
|
||||
<Button
|
||||
theme="reset"
|
||||
handleClick={e => {
|
||||
handleReset(e);
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
theme="search"
|
||||
text="집계"
|
||||
handleClick={e => {
|
||||
e.preventDefault();
|
||||
fetchData(resultData.send_dt, resultData.end_dt);
|
||||
}}
|
||||
/>
|
||||
</BtnWrapper>
|
||||
</SearchbarStyle>
|
||||
</FormWrapper>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default VBPSearchBar;
|
||||
|
||||
const SearchbarStyle = styled.div`
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 20px 0;
|
||||
font-size: 14px;
|
||||
padding: 20px;
|
||||
border-top: 1px solid #000;
|
||||
border-bottom: 1px solid #000;
|
||||
margin-bottom: 40px;
|
||||
`;
|
||||
|
||||
const SearchItem = styled.div`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 20px;
|
||||
margin-right: 50px;
|
||||
|
||||
${TextInput}, ${SelectInput} {
|
||||
height: 35px;
|
||||
}
|
||||
${TextInput} {
|
||||
padding: 0 10px;
|
||||
max-width: 400px;
|
||||
}
|
||||
`;
|
||||
27
src/components/IndexManage/index.js
Normal file
@@ -0,0 +1,27 @@
|
||||
import UserIndexSearchBar from "./UserIndexSearchBar";
|
||||
import RetentionSearchBar from "./RetentionSearchBar";
|
||||
import SegmentSearchBar from "./SegmentSearchBar";
|
||||
import DailyDashBoard from "./DailyDashBoard";
|
||||
import PlayTimeSearchBar from "./PlayTimeSearchBar";
|
||||
import UserContent from "./UserContent";
|
||||
import PlayTimeContent from "./PlayTimeContent";
|
||||
import RetentionContent from "./RetentionContent";
|
||||
import SegmentContent from "./SegmentContent";
|
||||
import DailyActiveUserContent from "./DailyActiveUserContent";
|
||||
import DailyMedalContent from "./DailyMedalContent";
|
||||
import DailySearchBar from "./DailySearchBar";
|
||||
|
||||
export {
|
||||
UserIndexSearchBar,
|
||||
RetentionSearchBar,
|
||||
SegmentSearchBar,
|
||||
DailyDashBoard,
|
||||
PlayTimeSearchBar,
|
||||
UserContent,
|
||||
SegmentContent,
|
||||
RetentionContent,
|
||||
PlayTimeContent,
|
||||
DailySearchBar,
|
||||
DailyActiveUserContent,
|
||||
DailyMedalContent,
|
||||
};
|
||||
13
src/components/JsonTest.js
Normal file
@@ -0,0 +1,13 @@
|
||||
import ReactJson from 'react-json-view';
|
||||
|
||||
import json from '../assets/data/applicator.json';
|
||||
|
||||
const JsonTest = () => {
|
||||
return (
|
||||
<>
|
||||
<ReactJson src={json} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default JsonTest;
|
||||