This commit is contained in:
2025-02-12 18:29:27 +09:00
commit 513ea114cc
290 changed files with 84274 additions and 0 deletions

3
.env.dev Normal file
View File

@@ -0,0 +1,3 @@
# .env stage
REACT_APP_API_URL=http://10.20.20.23:8080
REACT_APP_ENV=dev

3
.env.live Normal file
View File

@@ -0,0 +1,3 @@
# .env live
REACT_APP_API_URL=http://live-admintool.caliverse.io:8080
REACT_APP_ENV=live

3
.env.qa Normal file
View File

@@ -0,0 +1,3 @@
# .env stage
REACT_APP_API_URL=http://qa-admintool.caliverse.io:8080
REACT_APP_ENV=qa

3
.env.stage Normal file
View File

@@ -0,0 +1,3 @@
# .env stage
REACT_APP_API_URL=http://stage-admintool.caliverse.io:8080
REACT_APP_ENV=stage

34
.gitignore vendored Normal file
View File

@@ -0,0 +1,34 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
# testing
/coverage
# production
/build
# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
npm-debug.log*
yarn-debug.log*
yarn-error.log*
#.env
.env
/.idea/codeStyles/codeStyleConfig.xml
/.idea/codeStyles/Project.xml
/.idea/inspectionProfiles/Project_Default.xml
/.idea/.gitignore
/.idea/backofficefront.iml
/.idea/misc.xml
/.idea/modules.xml
/.idea/vcs.xml

39
.gitlab-ci.yml Normal file
View File

@@ -0,0 +1,39 @@
stages:
- build
- notify
before_script:
- apt-get update -qq && apt-get install
Build:
stage: build
cache:
paths:
- node_modules/
- .yarn/
policy: pull-push
tags:
- backoffice-front
before_script:
- export REACT_APP_API_URL="$REACT_APP_API_URL"
- env
- yarn config set cache-folder .yarn
- yarn install
script:
- pwd
- git config --global credential.helper cache
- git clone http://ci.cd_account:glpat-xmZekX15LbLQQdfYG2xD@44.227.18.113/ngle.dev/backofficefront.git
- CI='' yarn build
- rm -rf /application/backofficefront/build
- mv build/ /application/backofficefront/
- ls -al /application/backofficefront/
artifacts:
name: build
when: on_success
expire_in: 1 week
paths:
- build
only:
- dev

12
.prettierrc.json Normal file
View File

@@ -0,0 +1,12 @@
{
"printWidth": 200,
"tabWidth": 4,
"useTabs": true,
"semi": true,
"singleQuote": true,
"trailingComma": "all",
"bracketSpacing": true,
"jsxBracketSameLine": true,
"arrowParens": "avoid",
"endOfLine": "auto"
}

15
.vscode/launch.json vendored Normal file
View File

@@ -0,0 +1,15 @@
{
// IntelliSense를 사용하여 가능한 특성에 대해 알아보세요.
// 기존 특성에 대한 설명을 보려면 가리킵니다.
// 자세한 내용을 보려면 https://go.microsoft.com/fwlink/?linkid=830387을(를) 방문하세요.
"version": "0.2.0",
"configurations": [
{
"type": "chrome",
"request": "launch",
"name": "localhost에 대해 Chrome 시작",
"url": "http://localhost:3000",
"webRoot": "${workspaceFolder}"
}
]
}

16
Dockerfile.dev Normal file
View File

@@ -0,0 +1,16 @@
FROM node:20.11.0 as build
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build:dev
FROM nginx:alpine
COPY nginx_dev.conf /etc/nginx/conf.d/default.conf
COPY --from=build /app/build /usr/share/nginx/admintool
# port 8080
EXPOSE 8080
CMD ["nginx", "-g", "daemon off;"]

16
Dockerfile.live Normal file
View File

@@ -0,0 +1,16 @@
FROM node:20.11.0 as build
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build:live
FROM nginx:alpine
COPY nginx_live.conf /etc/nginx/conf.d/default.conf
COPY --from=build /app/build /usr/share/nginx/admintool
# port 8080
EXPOSE 8080
CMD ["nginx", "-g", "daemon off;"]

16
Dockerfile.qa Normal file
View File

@@ -0,0 +1,16 @@
FROM node:20.11.0 as build
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build:qa
FROM nginx:alpine
COPY nginx_qa.conf /etc/nginx/conf.d/default.conf
COPY --from=build /app/build /usr/share/nginx/admintool
# port 8080
EXPOSE 8080
CMD ["nginx", "-g", "daemon off;"]

16
Dockerfile.stage Normal file
View File

@@ -0,0 +1,16 @@
FROM node:20.11.0 as build
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build:stage
FROM nginx:alpine
COPY nginx_stage.conf /etc/nginx/conf.d/default.conf
COPY --from=build /app/build /usr/share/nginx/admintool
# port 8080
EXPOSE 8080
CMD ["nginx", "-g", "daemon off;"]

34
Jenkinsfile.dev Normal file
View File

@@ -0,0 +1,34 @@
pipeline {
agent any
environment {
DOCKER_IMAGE = "caliverse/admintool-front-dev"
DOCKER_TAG = "1.0.0"
DOCKER_TAG_PRE = "1.0.0"
FILE_NAME = 'admintool_front-dev.tar'
DOCKER_NAME = 'admintool-front-dev'
DOCKER_PORT = '8080'
DOCKERFILE_NAME = 'Dockerfile.dev'
}
stages {
stage('Docker Build & Deploy') {
steps {
sh 'docker stop $DOCKER_NAME || true'
sh 'docker rm $DOCKER_NAME || true'
sh 'docker rmi $DOCKER_IMAGE:$DOCKER_TAG_PRE || true' //이전 이미지 삭제
sh 'docker rmi $DOCKER_IMAGE:$DOCKER_TAG || true'
echo 'Docker Image Create'
sh 'docker build -f $DOCKERFILE_NAME -t $DOCKER_IMAGE:$DOCKER_TAG .' //Dockerfile 기준으로 이미지 생성
sh 'docker run -d \
-p $DOCKER_PORT:$DOCKER_PORT\
--name $DOCKER_NAME \
--restart=always \
-v /home/admintool/admintool-front/log:/var/log/nginx \
-e TZ=\${TZ:-Asia/Seoul} \
$DOCKER_IMAGE:$DOCKER_TAG'
echo 'Docker Container Create Complete'
}
}
}
}

88
Jenkinsfile.live Normal file
View File

@@ -0,0 +1,88 @@
pipeline {
agent any
environment {
DOCKER_IMAGE = "caliverse/admintool-front-live"
DOCKER_TAG = "1.0.0"
DOCKER_TAG_PRE = "1.0.0"
FILE_NAME = 'admintool_front_live.tar'
DOCKER_NAME = 'admintool-front-live'
DOCKER_PORT = '8080'
DOCKERFILE_NAME = 'Dockerfile.live'
}
stages {
stage('Docker Build') {
steps {
sh 'docker rmi $DOCKER_IMAGE:$DOCKER_TAG_PRE || true' //이전 이미지 삭제
sh 'docker build -f $DOCKERFILE_NAME -t $DOCKER_IMAGE:$DOCKER_TAG .' //Dockerfile 기준으로 이미지 생성
echo 'Docker Image Create'
sh 'docker save -o $FILE_NAME $DOCKER_IMAGE:$DOCKER_TAG' //이미지 .tar 로 생성
echo 'Docker Image > .tar File Create'
script {
def todayDate = sh(script: "date +%Y-%m-%d", returnStdout: true).trim()
def fileName = "${FILE_NAME}_${todayDate}.tar"
sh "cp ${FILE_NAME} ${fileName}" // 이름 변경
}
}
}
stage('Transfer Docker Image') {
steps {
// aws .tar transfer
sshPublisher(
publishers: [
sshPublisherDesc(
configName: 'live-frontend',
transfers: [
sshTransfer(
sourceFiles: "${FILE_NAME}",
remoteDirectory: '',
execCommand: """
echo '.tar Transfer Complete'
""",
execTimeout: 120000
)
],
usePromotionTimestamp: false,
verbose: true
)
]
)
}
}
stage('Deploy to Remote'){
steps{
// aws command
sshPublisher(
publishers: [
sshPublisherDesc(
configName: 'live-frontend',
transfers: [
sshTransfer(
execCommand: """
docker stop ${DOCKER_NAME} || true &&
docker rm ${DOCKER_NAME} || true &&
docker rmi ${DOCKER_IMAGE}:${DOCKER_TAG_PRE} || true &&
docker load -i ${FILE_NAME} &&
docker run -d \
-p ${DOCKER_PORT}:${DOCKER_PORT} \
--name ${DOCKER_NAME} \
--restart=always \
-v ./admintool/log:/var/log/nginx \
-e TZ=\${TZ:-Asia/Seoul} \
${DOCKER_IMAGE}:${DOCKER_TAG} &&
rm ${FILE_NAME}
echo 'Docker Container Create Complete'
""",
execTimeout: 120000
)
],
usePromotionTimestamp: false,
verbose: true
)
]
)
}
}
}
}

83
Jenkinsfile.qa Normal file
View File

@@ -0,0 +1,83 @@
pipeline {
agent any
environment {
DOCKER_IMAGE = "caliverse/admintool-front-qa"
DOCKER_TAG = "1.0.0"
DOCKER_TAG_PRE = "1.0.0"
FILE_NAME = 'admintool_front-qa.tar'
DOCKER_NAME = 'admintool-front-qa'
DOCKER_PORT = '8080'
DOCKERFILE_NAME = 'Dockerfile.qa'
}
stages {
stage('Docker Build') {
steps {
sh 'docker rmi $DOCKER_IMAGE:$DOCKER_TAG_PRE || true' //이전 이미지 삭제
sh 'docker build -f $DOCKERFILE_NAME -t $DOCKER_IMAGE:$DOCKER_TAG .' //Dockerfile 기준으로 이미지 생성
echo 'Docker Image Create'
sh 'docker save -o $FILE_NAME $DOCKER_IMAGE:$DOCKER_TAG' //이미지 .tar 로 생성
echo 'Docker Image > .tar File Create'
}
}
stage('Transfer Docker Image') {
steps {
// aws .tar transfer
sshPublisher(
publishers: [
sshPublisherDesc(
configName: 'qa-frontend',
transfers: [
sshTransfer(
sourceFiles: "${FILE_NAME}",
remoteDirectory: '',
execCommand: """
echo '.tar Transfer Complete'
""",
execTimeout: 120000
)
],
usePromotionTimestamp: false,
verbose: true
)
]
)
}
}
stage('Deploy to Remote'){
steps{
// aws command
sshPublisher(
publishers: [
sshPublisherDesc(
configName: 'qa-frontend',
transfers: [
sshTransfer(
execCommand: """
docker stop ${DOCKER_NAME} || true &&
docker rm ${DOCKER_NAME} || true &&
docker rmi ${DOCKER_IMAGE}:${DOCKER_TAG_PRE} || true &&
docker load -i ${FILE_NAME} &&
docker run -d \
-p ${DOCKER_PORT}:${DOCKER_PORT} \
--name ${DOCKER_NAME} \
--restart=always \
-v ./admintool/log:/var/log/nginx \
-e TZ=\${TZ:-Asia/Seoul} \
${DOCKER_IMAGE}:${DOCKER_TAG} &&
rm ${FILE_NAME}
echo 'Docker Container Create Complete'
""",
execTimeout: 120000
)
],
usePromotionTimestamp: false,
verbose: true
)
]
)
}
}
}
}

88
Jenkinsfile.stage Normal file
View File

@@ -0,0 +1,88 @@
pipeline {
agent any
environment {
DOCKER_IMAGE = "caliverse/admintool-front-stage"
DOCKER_TAG = "1.0.0"
DOCKER_TAG_PRE = "1.0.0"
FILE_NAME = 'admintool_front-stage.tar'
DOCKER_NAME = 'admintool-front-stage'
DOCKER_PORT = '8080'
DOCKERFILE_NAME = 'Dockerfile.stage'
}
stages {
stage('Docker Build') {
steps {
sh 'docker rmi $DOCKER_IMAGE:$DOCKER_TAG_PRE || true' //이전 이미지 삭제
sh 'docker build -f $DOCKERFILE_NAME -t $DOCKER_IMAGE:$DOCKER_TAG .' //Dockerfile 기준으로 이미지 생성
echo 'Docker Image Create'
sh 'docker save -o $FILE_NAME $DOCKER_IMAGE:$DOCKER_TAG' //이미지 .tar 로 생성
echo 'Docker Image > .tar File Create'
script {
def todayDate = sh(script: "date +%Y-%m-%d", returnStdout: true).trim()
def fileName = "${FILE_NAME}_${todayDate}.tar"
sh "cp ${FILE_NAME} ${fileName}" // 이름 변경
}
}
}
stage('Transfer Docker Image') {
steps {
// aws .tar transfer
sshPublisher(
publishers: [
sshPublisherDesc(
configName: 'stage-frontend',
transfers: [
sshTransfer(
sourceFiles: "${FILE_NAME}",
remoteDirectory: '',
execCommand: """
echo '.tar Transfer Complete'
""",
execTimeout: 120000
)
],
usePromotionTimestamp: false,
verbose: true
)
]
)
}
}
stage('Deploy to Remote'){
steps{
// aws command
sshPublisher(
publishers: [
sshPublisherDesc(
configName: 'stage-frontend',
transfers: [
sshTransfer(
execCommand: """
docker stop ${DOCKER_NAME} || true &&
docker rm ${DOCKER_NAME} || true &&
docker rmi ${DOCKER_IMAGE}:${DOCKER_TAG_PRE} || true &&
docker load -i ${FILE_NAME} &&
docker run -d \
-p ${DOCKER_PORT}:${DOCKER_PORT} \
--name ${DOCKER_NAME} \
--restart=always \
-v ./admintool/log:/var/log/nginx \
-e TZ=\${TZ:-Asia/Seoul} \
${DOCKER_IMAGE}:${DOCKER_TAG} &&
rm ${FILE_NAME}
echo 'Docker Container Create Complete'
""",
execTimeout: 120000
)
],
usePromotionTimestamp: false,
verbose: true
)
]
)
}
}
}
}

70
README.md Normal file
View File

@@ -0,0 +1,70 @@
# Getting Started with Create React App
This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
## Available Scripts
In the project directory, you can run:
### `yarn start`
Runs the app in the development mode.\
Open [http://localhost:3000](http://localhost:3000) to view it in your browser.
The page will reload when you make changes.\
You may also see any lint errors in the console.
### `yarn test`
Launches the test runner in the interactive watch mode.\
See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
### `yarn build`
Builds the app for production to the `build` folder.\
It correctly bundles React in production mode and optimizes the build for the best performance.
The build is minified and the filenames include the hashes.\
Your app is ready to be deployed!
See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
### `yarn eject`
**Note: this is a one-way operation. Once you `eject`, you can't go back!**
If you aren't satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you're on your own.
You don't have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn't feel obligated to use this feature. However we understand that this tool wouldn't be useful if you couldn't customize it when you are ready for it.
## Learn More
You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
To learn React, check out the [React documentation](https://reactjs.org/).
### Code Splitting
This section has moved here: [https://facebook.github.io/create-react-app/docs/code-splitting](https://facebook.github.io/create-react-app/docs/code-splitting)
### Analyzing the Bundle Size
This section has moved here: [https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size](https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size)
### Making a Progressive Web App
This section has moved here: [https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app](https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app)
### Advanced Configuration
This section has moved here: [https://facebook.github.io/create-react-app/docs/advanced-configuration](https://facebook.github.io/create-react-app/docs/advanced-configuration)
### Deployment
This section has moved here: [https://facebook.github.io/create-react-app/docs/deployment](https://facebook.github.io/create-react-app/docs/deployment)
### `yarn build` fails to minify
This section has moved here: [https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify](https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify)

25
nginx_dev.conf Normal file
View File

@@ -0,0 +1,25 @@
server {
listen 8080;
listen [::]:8080;
server_name localhost;
location / {
root /usr/share/nginx/admintool;
index index.html index.htm;
try_files $uri $uri/ /index.html;
}
# api reverse proxy
location /api/ {
proxy_pass http://10.20.20.23:23450;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/admintool;
}
}

25
nginx_live.conf Normal file
View File

@@ -0,0 +1,25 @@
server {
listen 8080;
listen [::]:8080;
server_name localhost;
location / {
root /usr/share/nginx/admintool;
index index.html index.htm;
try_files $uri $uri/ /index.html;
}
# api reverse proxy
location /api/ {
proxy_pass http://172.20.168.216:23450;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/admintool;
}
}

25
nginx_qa.conf Normal file
View File

@@ -0,0 +1,25 @@
server {
listen 8080;
listen [::]:8080;
server_name localhost;
location / {
root /usr/share/nginx/admintool;
index index.html index.htm;
try_files $uri $uri/ /index.html;
}
# api reverse proxy
location /api/ {
proxy_pass http://172.40.129.180:23450;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/admintool;
}
}

25
nginx_stage.conf Normal file
View File

@@ -0,0 +1,25 @@
server {
listen 8080;
listen [::]:8080;
server_name localhost;
location / {
root /usr/share/nginx/admintool;
index index.html index.htm;
try_files $uri $uri/ /index.html;
}
# api reverse proxy
location /api/ {
proxy_pass http://172.30.148.164:23450;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/admintool;
}
}

34857
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

62
package.json Normal file
View File

@@ -0,0 +1,62 @@
{
"name": "backofficefront",
"version": "0.1.0",
"private": true,
"dependencies": {
"@hookform/resolvers": "^3.2.0",
"@testing-library/jest-dom": "^5.14.1",
"@testing-library/react": "^13.0.0",
"@testing-library/user-event": "^13.2.1",
"axios": "^1.4.0",
"date-fns": "^2.30.0",
"dotenv-cli": "^7.4.2",
"i18next": "^23.15.1",
"lodash": "^4.17.21",
"react": "^18.2.0",
"react-calendar": "^4.4.0",
"react-datepicker": "^4.16.0",
"react-dom": "^18.2.0",
"react-hook-form": "^7.45.4",
"react-i18next": "^15.0.2",
"react-router-dom": "^6.14.1",
"react-scripts": "5.0.1",
"react-table": "^7.8.0",
"recoil": "^0.7.7",
"styled-components": "^6.0.4",
"styled-reset": "^4.5.1",
"web-vitals": "^2.1.0",
"xlsx": "^0.18.5",
"xlsx-js-style": "^1.2.0",
"yup": "^1.2.0"
},
"scripts": {
"start": "dotenv -e .env.local react-scripts start",
"build:stage": "dotenv -e .env.stage react-scripts build",
"build:live": "dotenv -e .env.live react-scripts build",
"build:dev": "dotenv -e .env.dev react-scripts build",
"build:qa": "dotenv -e .env.qa react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
},
"devDependencies": {
"@babel/plugin-proposal-private-property-in-object": "^7.21.11"
}
}

BIN
public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

29
public/index.html Normal file
View File

@@ -0,0 +1,29 @@
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta name="description" content="Web site created using create-react-app" />
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<!--
manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>CALIVERSE</title>
</head>
<body>
<div id="root"></div>
</body>
</html>

BIN
public/loading.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 176 KiB

BIN
public/logo192.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

BIN
public/logo512.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

25
public/manifest.json Normal file
View File

@@ -0,0 +1,25 @@
{
"short_name": "React App",
"name": "Create React App Sample",
"icons": [
{
"src": "favicon.ico",
"sizes": "64x64 32x32 24x24 16x16",
"type": "image/x-icon"
},
{
"src": "logo192.png",
"type": "image/png",
"sizes": "192x192"
},
{
"src": "logo512.png",
"type": "image/png",
"sizes": "512x512"
}
],
"start_url": ".",
"display": "standalone",
"theme_color": "#000000",
"background_color": "#ffffff"
}

3
public/robots.txt Normal file
View File

@@ -0,0 +1,3 @@
# https://www.robotstxt.org/robotstxt.html
User-agent: *
Disallow:

38
src/App.css Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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}&currency_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}&currency_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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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';

View 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'];

View 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',
];

View 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 };

View 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
View 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
View 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'

View 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
View 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
View 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",
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 366 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 221 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 523 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 248 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 479 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 232 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 243 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 381 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 941 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 679 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 410 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 419 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 260 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 252 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 244 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 249 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 218 B

1
src/assets/img/img.js Normal file
View File

@@ -0,0 +1 @@
// 이미지 및 기타 common 파일을 관리하는 폴더입니다. 이 파일은 삭제하셔도 됩니다.

BIN
src/assets/img/login-bg.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1002 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

View 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;
}
`;

View 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;
}
`;

View 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;
`;

View 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;
}
`;

View 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;
`;

View 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;
`;

View 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;
`;

View 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;
}
`;

View 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;
}
`;

View 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;
}
}
}
`;

View 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;

View 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;
}
}
`;

View 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;

View 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;

View 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;
}
`;

View 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;

Some files were not shown because too many files have changed in this diff Show More