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

View File

@@ -0,0 +1,312 @@
import { useEffect, useState } from 'react';
import { styled } from 'styled-components';
import Button from '../../components/common/button/Button';
import { CurrencyIndexExport, CurrencyIndexView } from '../../apis';
import { SelectInput, TableStyle, TableInfo, ListOption, InputLabel } from '../../styles/Components';
import CreditSeacrhBar from '../../components/IndexManage/CreditSearchBar';
import { uniqBy } from 'lodash';
import { sumBy } from 'lodash';
const CreditContent = () => {
const token = sessionStorage.getItem('token');
let d = new Date();
const START_DATE = new Date(new Date(d.setDate(d.getDate() - 1)).setHours(0, 0, 0, 0));
const END_DATE = new Date();
const CURRENCY_LIST = [
{ "key": "Gold", "name": "골드" },
{ "key": "Sapphire", "name": "사파이어" },
{ "key": "Calium", "name": "칼리움" },
{ "key": "Onyxium", "name": "오닉시움" }
];
const [sendDate, setSendDate] = useState(START_DATE);
const [finishDate, setFinishDate] = useState(END_DATE);
const [currencyType, setCurrencyType] = useState('Gold');
const [currencyText, setCurrencyText] = useState('골드');
const [dataList, setDataList] = useState([]);
const [routeData, setRouteData] = useState([]);
useEffect(() => {
fetchData(sendDate, finishDate, currencyType);
}, [currencyType]);
const fetchData = async (startDate, endDate) => {
const newStartDate = new Date(startDate);
const newEndDate = new Date(endDate);
const startDateToLocal =
newStartDate.getFullYear() +
'-' +
(newStartDate.getMonth() + 1 < 9 ? '0' + (newStartDate.getMonth() + 1) : newStartDate.getMonth() + 1) +
'-' +
(newStartDate.getDate() < 9 ? '0' + newStartDate.getDate() : newStartDate.getDate());
const endDateToLocal =
newEndDate.getFullYear() +
'-' +
(newEndDate.getMonth() + 1 < 9 ? '0' + (newEndDate.getMonth() + 1) : newEndDate.getMonth() + 1) +
'-' +
(newEndDate.getDate() < 9 ? '0' + newEndDate.getDate() : newEndDate.getDate());
setDataList(await CurrencyIndexView(token, startDateToLocal, endDateToLocal, currencyType));
setSendDate(startDateToLocal);
setFinishDate(endDateToLocal);
setRoutArray(await CurrencyIndexView(token, startDateToLocal, endDateToLocal, currencyType));
};
const handleCurrencyChange = e => {
let value = e.target.value;
setCurrencyType(value);
CURRENCY_LIST.filter(data => data.key === value).map(data => {
setCurrencyText(data.name);
});
};
//route data
const setRoutArray = async (data) => {
let routeArray = [];
let routeAcqArr = [];
let routeConArr = [];
//생산량 route
data.list && data.list[0].daily_data.filter(routeData => routeData.delta_type === 'ACQUIRE').map(routeData => {
routeData.data.map(routeData => {
if(!routeAcqArr.includes(routeData.route) ){
routeAcqArr.push(routeData.route);
}
})
});
routeArray.ACQUIRE = routeAcqArr;
//소진량 route
data.list && data.list[0].daily_data.filter(routeData => routeData.delta_type === 'CONSUME').map(routeData => {
routeData.data.map(routeData => {
if(!routeConArr.includes(routeData.route) ){
routeConArr.push(routeData.route);
}
})
});
routeArray.CONSUME = routeConArr;
setRouteData(routeArray);
};
// 엑셀 다운로드
const handleXlsxExport = () => {
const fileName = 'Caliverse_Credit_Index.xlsx';
CurrencyIndexExport(token, fileName, sendDate, finishDate, currencyType);
};
return (
<>
<CreditSeacrhBar fetchData={fetchData} />
<TableInfo2>
<SelectInput onChange={handleCurrencyChange}>
{CURRENCY_LIST.map((item, index) => (
<option value={item.key} key={index}>
{item.name}
</option>
))}
</SelectInput>
<ListOption>
<Button theme="line" text="엑셀 다운로드" handleClick={handleXlsxExport} />
</ListOption>
</TableInfo2>
<TableWrapper>
<EconomicTable>
<thead>
<tr>
<th colSpan="2" className="text-center" width="300">
Product
</th>
{dataList.list && uniqBy(dataList.list, 'date').map(data =>
<th width="160" key={data.date}>{data.date}</th>
)}
</tr>
</thead>
<tbody>
<tr>
<TableTitle colSpan="2">(Total) {currencyText} 생산량</TableTitle>
{dataList.list &&
dataList.list.map((data) =>
(data.total).filter(totalData => data.date === totalData.date && totalData.delta_type === 'ACQUIRE')
.map((totalData, index) => (
<TableData key={index}
$state={totalData.dif !== "" && totalData.dif !== "Infinity" ? "danger" : ""}>
{totalData.quantity}
{
totalData.dif !== "" && totalData.dif !== "Infinity"
? (<span>({totalData.dif})</span>)
: ("")
}
</TableData>
)
))
}
</tr>
<tr>
<TableTitle colSpan="2">(Total) {currencyText} 소진량</TableTitle>
{dataList.list &&
dataList.list.map((data) =>
(data.total).filter(totalData => data.date === totalData.date && totalData.delta_type === 'CONSUME')
.map((totalData, index) => (
<TableData key={index}
$state={totalData.dif !== "" && totalData.dif !== "Infinity" ? "danger" : ""}>
{totalData.quantity}
{
totalData.dif !== "" && totalData.dif !== "Infinity"
? (<span>({totalData.dif})</span>)
: ("")
}
</TableData>
)
))
}
</tr>
<tr>
<TableTitle colSpan="2">(Total) {currencyText} 보유량</TableTitle>
{dataList.list &&
dataList.list.map((data, index) => (
<TableData key={index}>
{sumBy(
data.total.filter(totalData => data.date === totalData.date && totalData.delta_type === 'ACQUIRE'),
'quantity',
) -
sumBy(
data.total.filter(totalData => data.date === totalData.date && totalData.delta_type === 'CONSUME'),
'quantity',
)}
</TableData>
))
}
</tr>
{/* 획득 GET */}
{
routeData.ACQUIRE && routeData.ACQUIRE.map((route, index) => (
<tr key={index}>
<TableTitle>GET</TableTitle>
<TableTitle>{route}</TableTitle>
{dataList.list &&
dataList.list.map((data) =>
data.daily_data.filter(dailyData => data.date === dailyData.date && dailyData.delta_type === 'ACQUIRE')
.map(dailyData => (dailyData.data).filter(routeData => data.date === routeData.date && routeData.route === route)
.map((routeData, i) => (
<TableData key={i} data={routeData.date}>{routeData.quantity}</TableData>
)))
)
}
</tr>
))
}
{/* 소진 USE CONSUME */}
{
routeData.CONSUME && routeData.CONSUME.map((route, index) => (
<tr key={index}>
<TableTitle>USE</TableTitle>
<TableTitle>{route}</TableTitle>
{dataList.list &&
dataList.list.map((data) =>
data.daily_data.filter(dailyData => data.date === dailyData.date && dailyData.delta_type === 'CONSUME')
.map(dailyData => (dailyData.data).filter(routeData => data.date === routeData.date && routeData.route === route)
.map((routeData, i) => (
<TableData key={i} data={routeData.date}>{routeData.quantity}</TableData>
)))
)
}
</tr>
))
}
</tbody>
</EconomicTable>
</TableWrapper>
</>
);
};
export default CreditContent;
const TableWrapper = styled.div`
width: 100%;
min-width: 680px;
overflow: auto;
&::-webkit-scrollbar {
height: 4px;
}
&::-webkit-scrollbar-thumb {
background: #666666;
}
&::-webkit-scrollbar-track {
background: #d9d9d9;
}
${TableStyle} {
width: 100%;
min-width: 900px;
th {
&.cell-nru {
background: #f0f0f0;
border-left: 1px solid #aaa;
border-right: 1px solid #aaa;
}
}
td {
&.blank {
background: #f9f9f9;
}
&.cell-nru {
background: #fafafa;
border-left: 1px solid #aaa;
border-right: 1px solid #aaa;
}
}
}
`;
const TableInfo2 = styled(TableInfo)`
justify-content: space-between;
${InputLabel} {
font-size: 12px;
}
${SelectInput} {
width: auto;
min-width: 100px;
height: 24px;
}
`;
const TableDate = styled.th`
color: ${props => (props.$state === 'danger' ? '#d60000' : '#2c2c2c')};
`;
const TableData = styled.td`
background: ${props => (props.$state === 'danger' ? '#d60000' : props.$state === 'blank' ? '#F9F9F9' : 'transparent')};
color: ${props => (props.$state === 'danger' ? '#fff' : '#2c2c2c')};
`;
const perData = styled.span`
display: ${props => (props.$view === 'hidden' ? 'none' : 'block')};
`;
const TableTitle = styled.td`
text-align: center;
`;
const EconomicTable = styled(TableStyle)`
${TableData} {
text-align: left;
}
tbody {
tr:nth-child(1),
tr:nth-child(2),
tr:nth-child(3) {
background: #f5fcff;
}
}
`;

View File

@@ -0,0 +1,113 @@
import { useState } from 'react';
import { styled } from 'styled-components';
import Button from '../../components/common/button/Button';
import DatePickerComponent from '../common/Date/DatePickerComponent';
import 'react-datepicker/dist/react-datepicker.css';
import { FormWrapper, InputLabel, TextInput, SelectInput, BtnWrapper, InputGroup, DatePickerWrapper } from '../../styles/Components';
const CreditSeacrhBar = ({ fetchData }) => {
let d = new Date();
const START_DATE = new Date(new Date(d.setDate(d.getDate() - 1)).setHours(0, 0, 0, 0));
const END_DATE = new Date();
const [resultData, setResultData] = useState({
send_dt: START_DATE,
end_dt: END_DATE,
});
// 발송 날짜 세팅 로직
const handleSelectedDate = data => {
const sendDate = new Date(data);
const resultSendData = new Date(sendDate.getFullYear(), sendDate.getMonth(), sendDate.getDate());
setResultData({ ...resultData, send_dt: resultSendData });
};
// 발송 날짜 세팅 로직
const handleEndDate = data => {
const endDate = new Date(data);
const resultSendData = new Date(endDate.getFullYear(), endDate.getMonth(), endDate.getDate());
setResultData({ ...resultData, end_dt: resultSendData });
};
const handleReset = e => {
e.preventDefault();
setResultData({ send_dt: START_DATE, end_dt: END_DATE });
fetchData(START_DATE, END_DATE);
};
return (
<>
<FormWrapper>
<SearchbarStyle>
<SearchItem>
<InputLabel>집계 기간</InputLabel>
<InputGroup>
<DatePickerWrapper>
<DatePickerComponent
name="시작 일자" selectedDate={resultData.send_dt}
handleSelectedDate={data => handleSelectedDate(data)}
maxDate={new Date()} />
<span>~</span>
<DatePickerComponent
name="종료 일자" selectedDate={resultData.end_dt}
handleSelectedDate={data => handleEndDate(data)}
pastDate = {resultData.send_dt}
maxDate={new Date()} />
</DatePickerWrapper>
</InputGroup>
</SearchItem>
<BtnWrapper $gap="8px">
<Button
theme="reset"
handleClick={e => {
handleReset(e);
}}
/>
<Button
theme="search"
text="집계"
handleClick={e => {
e.preventDefault();
fetchData(resultData.send_dt, resultData.end_dt);
}}
/>
</BtnWrapper>
</SearchbarStyle>
</FormWrapper>
</>
);
};
export default CreditSeacrhBar;
const SearchbarStyle = styled.div`
width: 100%;
display: flex;
flex-wrap: wrap;
gap: 20px 0;
font-size: 14px;
padding: 20px;
border-top: 1px solid #000;
border-bottom: 1px solid #000;
margin-bottom: 40px;
`;
const SearchItem = styled.div`
display: flex;
align-items: center;
gap: 20px;
margin-right: 50px;
${TextInput}, ${SelectInput} {
height: 35px;
}
${TextInput} {
padding: 0 10px;
max-width: 400px;
}
`;

View File

@@ -0,0 +1,110 @@
import { useEffect, useState } from 'react';
import Button from '../../components/common/button/Button';
import { TableStyle, TableInfo, ListOption, IndexTableWrap } from '../../styles/Components';
import { DailySearchBar } from '../../components/IndexManage/index';
import { DailyActiveUserExport, DailyActiveUserView } from '../../apis';
const PlayTimeContent = () => {
const token = sessionStorage.getItem('token');
let d = new Date();
const START_DATE = new Date(new Date(d.setDate(d.getDate() - 1)).setHours(0, 0, 0, 0));
const END_DATE = new Date();
const [dataList, setDataList] = useState([]);
const [resultData, setResultData] = useState([]);
const [sendDate, setSendDate] = useState(START_DATE);
const [finishDate, setFinishDate] = useState(END_DATE);
useEffect(() => {
fetchData(START_DATE, END_DATE);
}, []);
// DAU 데이터
const fetchData = async (startDate, endDate) => {
const startDateToLocal =
startDate.getFullYear() +
'-' +
(startDate.getMonth() + 1 < 9 ? '0' + (startDate.getMonth() + 1) : startDate.getMonth() + 1) +
'-' +
(startDate.getDate() < 9 ? '0' + startDate.getDate() : startDate.getDate());
const endDateToLocal =
endDate.getFullYear() +
'-' +
(endDate.getMonth() + 1 < 9 ? '0' + (endDate.getMonth() + 1) : endDate.getMonth() + 1) +
'-' +
(endDate.getDate() < 9 ? '0' + endDate.getDate() : endDate.getDate());
// await DailyActiveUserView(token, startDateToLocal, endDateToLocal).then(data => {
// console.log(data);
// setDataList(data);
// });
setSendDate(startDateToLocal);
setFinishDate(endDateToLocal);
};
// 검색 함수
const handleSearch = (send_dt, end_dt) => {
fetchData(send_dt, end_dt);
};
// 엑셀 다운로드
const handleXlsxExport = () => {
const fileName = 'Caliverse_Dau.xlsx';
DailyActiveUserExport(token, fileName, sendDate, finishDate);
};
return (
<>
<DailySearchBar setResultData={setResultData} resultData={resultData} handleSearch={handleSearch} fetchData={fetchData} />
<TableInfo>
<ListOption>
<Button theme="line" text="엑셀 다운로드" handleClick={handleXlsxExport} />
</ListOption>
</TableInfo>
<IndexTableWrap>
<TableStyle>
<caption></caption>
<thead>
<tr>
<th rowSpan="1" width="45">
일자
</th>
<th colSpan="1" width="30">
DAU
</th>
{/*<th colSpan="1" width="30">*/}
{/* DALC*/}
{/*</th>*/}
<th colSpan="1" width="30">
DGLC
</th>
{/*<th colSpan="1" width="30">*/}
{/* MaxAU*/}
{/*</th>*/}
</tr>
</thead>
<tbody>
{dataList && (dataList || []).map((data, index) => (
<tr key={index}>
<td>{data.date}</td>
<td>{data.dau}</td>
{/*<td>{data.dalc}</td>*/}
<td>{data.dglc}</td>
{/*<td>{data.maxAu}</td>*/}
</tr>
))}
</tbody>
</TableStyle>
</IndexTableWrap>
</>
);
};
export default PlayTimeContent;

View File

@@ -0,0 +1,170 @@
import { useEffect, useState } from 'react';
import { userTotalIndex } from '../../apis';
import { styled } from 'styled-components';
import TitleArrow from '../../assets/img/icon/icon-title.png';
import UpIcon from '../../assets/img/icon/icon-up.png';
import DownIcon from '../../assets/img/icon/icon-down.png';
const DailyDashBoard = ({ content }) => {
const [boardState, setBoardState] = useState('active');
const [totalData, setTotalData] = useState([]);
const handleBoard = () => {
if (boardState === 'active') {
setBoardState('inactive');
} else {
setBoardState('active');
}
};
useEffect(() => {
// 이용자 지표 총계 불러오기
fetchData();
}, []);
const fetchData = async () => {
const token = sessionStorage.getItem('token');
await userTotalIndex(token).then(data => {
console.log(data);
setTotalData(data.dashboard);
});
};
return (
<DailyBoardWrapper>
{totalData &&
<DailyBoard>
<BoardTitle onClick={handleBoard} $state={boardState}>
Daily Dashboard
</BoardTitle>
<BoardInfo $state={boardState}>
<BoxWrapper>
<InfoItem>
<InfoTitle>DAU</InfoTitle>
<InfoValue>
{totalData.dau && totalData.dau.count}
<span>({totalData.dau && totalData.dau.dif}%)</span>
<InfoArrow $state={totalData.dau && totalData.dau.updown === '=' ? null : totalData.dau && totalData.dau.updown}></InfoArrow>
</InfoValue>
</InfoItem>
<InfoItem>
<InfoTitle>NRU</InfoTitle>
<InfoValue>
{totalData.nru && totalData.nru.count}
<span>({totalData.nru && totalData.nru.dif}%)</span>
<InfoArrow $state={totalData.nru && totalData.nru.updown === '=' ? null : totalData.nru && totalData.nru.updown}></InfoArrow>
</InfoValue>
</InfoItem>
<InfoItem>
<InfoTitle>PU</InfoTitle>
<InfoValue>
{totalData.pu && totalData.pu.count}
<span>({totalData.pu && totalData.pu.dif}%)</span>
<InfoArrow $state={totalData.pu && totalData.pu.updown === '=' ? null : totalData.pu && totalData.pu.updown}></InfoArrow>
</InfoValue>
</InfoItem>
<InfoItem>
<InfoTitle>MCU</InfoTitle>
<InfoValue>
{totalData.mcu && totalData.mcu.count}
<span>({totalData.mcu && totalData.mcu.dif}%)</span>
<InfoArrow $state={totalData.mcu && totalData.mcu.updown === '=' ? null : totalData.mcu && totalData.mcu.updown}></InfoArrow>
</InfoValue>
</InfoItem>
</BoxWrapper>
<InfoNotice>집계 기간 : {totalData.date && totalData.date} 00 ~ 24 (집계는 당일 기준 하루 데이터를 사용합니다)</InfoNotice>
</BoardInfo>
</DailyBoard>
}
</DailyBoardWrapper>
);
};
export default DailyDashBoard;
const DailyBoardWrapper = styled.div`
padding-top: 20px;
border-top: 1px solid #000;
`;
const DailyBoard = styled.div`
background: #f6f6f6;
border-radius: 10px;
margin-bottom: 20px;
`;
const BoardTitle = styled.div`
font-size: 24px;
font-weight: 600;
line-height: 52px;
padding: 0 10px;
cursor: pointer;
&:after {
content: '';
display: inline-block;
width: 11px;
height: 52px;
margin-left: 10px;
background: url(${TitleArrow}) 50% 50% no-repeat;
position: absolute;
transform: ${props => (props.$state === 'active' ? 'rotate(0)' : 'rotate(180deg)')};
}
`;
const BoardInfo = styled.div`
padding: 20px;
border-top: 1px solid #d9d9d9;
display: ${props => (props.$state === 'active' ? 'block' : 'none')};
`;
const BoxWrapper = styled.div`
display: flex;
flex-wrap: wrap;
justify-content: space-between;
`;
const InfoItem = styled.div`
width: 24%;
background: #fff;
padding: 15px 20px;
display: flex;
flex-wrap: wrap;
justify-content: space-between;
align-items: center;
border-radius: 15px;
`;
const InfoTitle = styled.div`
width: 42px;
font-size: 20px;
font-weight: 600;
`;
const InfoValue = styled.div`
display: inline-flex;
flex-wrap: wrap;
margin: 5px 0;
gap: 5px 0;
align-items: center;
font-size: 18px;
font-weight: 600;
span {
font-size: 14px;
font-weight: 400;
display: inline-block;
margin: 0 5px;
}
`;
const InfoArrow = styled.div`
width: 12px;
height: 6px;
background: url(${props => (props.$state === null ? '' : props.$state === 'UP' ? UpIcon : DownIcon)}) 50% 50% no-repeat;
`;
const InfoNotice = styled.div`
font-size: 12px;
text-align: right;
color: #999;
margin-top: 10px;
`;

View File

@@ -0,0 +1,111 @@
import { Fragment, useEffect, useState } from 'react';
import Button from '../common/button/Button';
import { TableStyle, TableInfo, ListOption, IndexTableWrap } from '../../styles/Components';
import { DailySearchBar } from './index';
import { DailyMedalView } from '../../apis';
const DailyMedalContent = () => {
const token = sessionStorage.getItem('token');
let d = new Date();
const START_DATE = new Date(new Date(d.setDate(d.getDate() - 1)).setHours(0, 0, 0, 0));
const END_DATE = new Date();
const [dataList, setDataList] = useState([]);
const [resultData, setResultData] = useState([]);
const [sendDate, setSendDate] = useState(START_DATE);
const [finishDate, setFinishDate] = useState(END_DATE);
useEffect(() => {
fetchData(START_DATE, END_DATE);
}, []);
const fetchData = async (startDate, endDate) => {
const startDateToLocal =
startDate.getFullYear() +
'-' +
(startDate.getMonth() + 1 < 9 ? '0' + (startDate.getMonth() + 1) : startDate.getMonth() + 1) +
'-' +
(startDate.getDate() < 9 ? '0' + startDate.getDate() : startDate.getDate());
const endDateToLocal =
endDate.getFullYear() +
'-' +
(endDate.getMonth() + 1 < 9 ? '0' + (endDate.getMonth() + 1) : endDate.getMonth() + 1) +
'-' +
(endDate.getDate() < 9 ? '0' + endDate.getDate() : endDate.getDate());
setDataList(await DailyMedalView(token, startDateToLocal, endDateToLocal));
setSendDate(startDateToLocal);
setFinishDate(endDateToLocal);
};
// 검색 함수
const handleSearch = (send_dt, end_dt) => {
fetchData(send_dt, end_dt);
};
// 엑셀 다운로드
const handleXlsxExport = () => {
const fileName = 'Caliverse_Daily_Medal.xlsx';
//DailyActiveUserExport(token, fileName, sendDate, finishDate);
};
return (
<>
<DailySearchBar setResultData={setResultData} resultData={resultData} handleSearch={handleSearch} fetchData={fetchData} />
<TableInfo>
<ListOption>
<Button theme="line" text="엑셀 다운로드" handleClick={handleXlsxExport} />
</ListOption>
</TableInfo>
<IndexTableWrap>
<TableStyle>
<caption></caption>
<thead >
<tr>
<th rowSpan="1" width="20">
일자
</th>
<th colSpan="1" width="30">
UserID
</th>
<th colSpan="1" width="30">
닉네임
</th>
<th colSpan="1" width="30">
Item ID
</th>
<th colSpan="1" width="30">
획득량
</th>
</tr>
</thead>
<tbody>
{(dataList || []).map((data, index) => (
<tr key={index}>
<td>{data.date}</td>
<td>{data.dau}</td>
<td>{data.dalc}</td>
<td>{data.dglc}</td>
<td>{data.maxAu}</td>
{Array.from({ length: 24 }, (_, i) => (
<td key={i}>{data['h' + i]}</td>
))}
</tr>
))}
</tbody>
</TableStyle>
</IndexTableWrap>
</>
);
};
export default DailyMedalContent;

View File

@@ -0,0 +1,112 @@
import { useEffect, useState } from 'react';
import { styled } from 'styled-components';
import Button from '../common/button/Button';
import 'react-datepicker/dist/react-datepicker.css';
import DatePickerComponent from '../common/Date/DatePickerComponent';
import { FormWrapper, InputLabel, TextInput, SelectInput, BtnWrapper, InputGroup, DatePickerWrapper } from '../../styles/Components';
const DailySearchBar = ({ resultData, setResultData, handleSearch, fetchData }) => {
// 초기 날짜 세팅
let d = new Date();
const START_DATE = new Date(new Date(d.setDate(d.getDate() - 1)).setHours(0, 0, 0, 0));
const END_DATE = new Date();
// resultData에 임의 날짜 넣어주기
useEffect(() => {
setResultData({
send_dt: START_DATE,
end_dt: END_DATE,
});
}, []);
const handleSelectedDate = data => {
const sendDate = new Date(data);
const resultSendData = new Date(sendDate.getFullYear(), sendDate.getMonth(), sendDate.getDate());
setResultData({ ...resultData, send_dt: resultSendData });
};
const handleEndDate = data => {
const endDate = new Date(data);
const resultSendData = new Date(endDate.getFullYear(), endDate.getMonth(), endDate.getDate());
setResultData({ ...resultData, end_dt: resultSendData });
};
const handleReset = e => {
e.preventDefault();
setResultData({ send_dt: START_DATE, end_dt: END_DATE });
fetchData(START_DATE, END_DATE);
};
return (
<>
<FormWrapper>
<SearchbarStyle>
<SearchItem>
<InputLabel>집계 기준일</InputLabel>
<InputGroup>
<DatePickerWrapper>
<DatePickerComponent
name="시작 일자"
selectedDate={resultData.send_dt}
handleSelectedDate={data => handleSelectedDate(data)}
maxDate={new Date()} />
<span>~</span>
<DatePickerComponent
name="종료 일자"
selectedDate={resultData.end_dt}
handleSelectedDate={data => handleEndDate(data)}
pastDate = {resultData.send_dt}
maxDate={new Date()} />
</DatePickerWrapper>
</InputGroup>
</SearchItem>
<BtnWrapper $gap="8px">
<Button theme="reset" handleClick={handleReset} />
<Button
theme="search"
text="검색"
handleClick={e => {
e.preventDefault();
handleSearch(resultData.send_dt, resultData.end_dt);
}}
/>
</BtnWrapper>
</SearchbarStyle>
</FormWrapper>
</>
);
};
export default DailySearchBar;
const SearchbarStyle = styled.div`
width: 100%;
display: flex;
flex-wrap: wrap;
gap: 20px 0;
font-size: 14px;
padding: 20px;
border-top: 1px solid #000;
border-bottom: 1px solid #000;
margin-bottom: 40px;
`;
const SearchItem = styled.div`
display: flex;
align-items: center;
gap: 20px;
margin-right: 50px;
${TextInput}, ${SelectInput} {
height: 35px;
}
${TextInput} {
padding: 0 10px;
max-width: 400px;
}
`;

View File

@@ -0,0 +1,166 @@
import { styled } from 'styled-components';
import { useEffect, useState } from 'react';
import DecoSearchBar from '../../components/IndexManage/DecoSearchBar';
import Button from '../../components/common/button/Button';
import { TableStyle, TableInfo, ListOption } from '../../styles/Components';
import { ClothesIndexExport, ClothesIndexView } from '../../apis';
const DecoContent = () => {
const token = sessionStorage.getItem('token');
let d = new Date();
const START_DATE = new Date(new Date(d.setDate(d.getDate() - 1)).setHours(0, 0, 0, 0));
const END_DATE = new Date();
const [searchKey, setSearchKey] = useState('');
const [sendDate, setSendDate] = useState(START_DATE);
const [finishDate, setFinishDate] = useState(END_DATE);
const [dataList, setDataList] = useState([]);
useEffect(() => {
fetchData('', START_DATE, END_DATE);
}, []);
// console.log(dataList);
const fetchData = async (data, startDate, endDate) => {
const startDateToLocal =
startDate.getFullYear() +
'-' +
(startDate.getMonth() + 1 < 9 ? '0' + (startDate.getMonth() + 1) : startDate.getMonth() + 1) +
'-' +
(startDate.getDate() < 9 ? '0' + startDate.getDate() : startDate.getDate());
const endDateToLocal =
endDate.getFullYear() +
'-' +
(endDate.getMonth() + 1 < 9 ? '0' + (endDate.getMonth() + 1) : endDate.getMonth() + 1) +
'-' +
(endDate.getDate() < 9 ? '0' + endDate.getDate() : endDate.getDate());
setDataList(await ClothesIndexView(token, data, startDateToLocal, endDateToLocal));
setSearchKey(data);
setSendDate(startDateToLocal);
setFinishDate(endDateToLocal);
};
// 엑셀 다운로드
const handleXlsxExport = () => {
const fileName = 'Caliverse_Deco_Index.xlsx';
ClothesIndexExport(token, fileName, searchKey, sendDate, finishDate);
};
return (
<>
<DecoSearchBar fetchData={fetchData} />
<TableInfo>
<ListOption>
<Button theme="line" text="엑셀 다운로드" handleClick={handleXlsxExport} />
</ListOption>
</TableInfo>
<TableWrapper>
<EconomicTable2>
<thead>
<tr>
<th width="240">일자</th>
<th rowSpan="2" width="160">
2023-08-07
</th>
<th rowSpan="2" width="160">
2023-08-08
</th>
<th rowSpan="2" width="160">
2023-08-09
</th>
<th rowSpan="2" width="160">
2023-08-10
</th>
<th rowSpan="2" width="160"></th>
<th rowSpan="2" width="160"></th>
</tr>
<tr>
<th>대상</th>
</tr>
</thead>
<tbody>
<tr>
<TableTitle>(인스턴스 ID) 방문자 </TableTitle>
<TableData>95</TableData>
<TableData>240</TableData>
<TableData>240</TableData>
<TableData>240</TableData>
<TableData $state="blank"></TableData>
<TableData $state="blank"></TableData>
</tr>
{/* {mokupData.map((data, index) => (
<Fragment key={index}>
<tr>
<td>{data.date}</td>
<td>{data.name}</td>
<td>{data.trader}</td>
<td>{data.id}</td>
<td>{data.key}</td>
</tr>
</Fragment>
))} */}
</tbody>
</EconomicTable2>
</TableWrapper>
</>
);
};
export default DecoContent;
const TableWrapper = styled.div`
width: 100%;
min-width: 680px;
overflow: auto;
&::-webkit-scrollbar {
height: 4px;
}
&::-webkit-scrollbar-thumb {
background: #666666;
}
&::-webkit-scrollbar-track {
background: #d9d9d9;
}
${TableStyle} {
width: 100%;
min-width: 900px;
th {
&.cell-nru {
background: #f0f0f0;
border-left: 1px solid #aaa;
border-right: 1px solid #aaa;
}
}
td {
&.blank {
background: #f9f9f9;
}
&.cell-nru {
background: #fafafa;
border-left: 1px solid #aaa;
border-right: 1px solid #aaa;
}
}
}
`;
const TableData = styled.td`
background: ${props => (props.$state === 'danger' ? '#d60000' : props.$state === 'blank' ? '#F9F9F9' : 'transparent')};
color: ${props => (props.$state === 'danger' ? '#fff' : '#2c2c2c')};
`;
const TableTitle = styled.td`
text-align: center;
`;
const EconomicTable2 = styled(TableStyle)`
${TableData} {
text-align: left;
}
`;

View File

@@ -0,0 +1,116 @@
import { useState } from 'react';
import { styled } from 'styled-components';
import Button from '../../components/common/button/Button';
import DatePickerComponent from '../common/Date/DatePickerComponent';
import { FormWrapper, InputLabel, TextInput, SelectInput, BtnWrapper, InputGroup, DatePickerWrapper } from '../../styles/Components';
const DecoSearchBar = ({ fetchData }) => {
let d = new Date();
const START_DATE = new Date(new Date(d.setDate(d.getDate() - 1)).setHours(0, 0, 0, 0));
const END_DATE = new Date();
const [resultData, setResultData] = useState({
search_key: '',
send_dt: START_DATE,
end_dt: END_DATE,
});
// 발송 날짜 세팅 로직
const handleSelectedDate = data => {
const sendDate = new Date(data);
const resultSendData = new Date(sendDate.getFullYear(), sendDate.getMonth(), sendDate.getDate());
setResultData({ ...resultData, send_dt: resultSendData });
};
// 발송 날짜 세팅 로직
const handleEndDate = data => {
const endDate = new Date(data);
const resultSendData = new Date(endDate.getFullYear(), endDate.getMonth(), endDate.getDate());
setResultData({ ...resultData, end_dt: resultSendData });
};
const handleReset = e => {
e.preventDefault();
setResultData({ search_key: '', send_dt: START_DATE, end_dt: END_DATE });
fetchData('', START_DATE, END_DATE);
};
return (
<>
<FormWrapper>
<SearchbarStyle>
<SearchItem>
<InputLabel>아이템 ID</InputLabel>
<TextInput type="text" placeholder="ID 입력" onChange={e => setResultData({ ...resultData, search_key: e.target.value })} />
</SearchItem>
<SearchItem>
<InputLabel>집계 기간</InputLabel>
<InputGroup>
<DatePickerWrapper>
<DatePickerComponent
name="시작 일자" selectedDate={resultData.send_dt}
handleSelectedDate={data => handleSelectedDate(data)}
maxDate={new Date()} />
<span>~</span>
<DatePickerComponent
name="종료 일자" selectedDate={resultData.end_dt}
handleSelectedDate={data => handleEndDate(data)}
pastDate = {resultData.send_dt}
maxDate={new Date()} />
</DatePickerWrapper>
</InputGroup>
</SearchItem>
<BtnWrapper $gap="8px">
<Button
theme="reset"
handleClick={e => {
handleReset(e);
}}
/>
<Button
theme="search"
text="집계"
handleClick={e => {
e.preventDefault();
fetchData(resultData.search_key, resultData.send_dt, resultData.end_dt);
}}
/>
</BtnWrapper>
</SearchbarStyle>
</FormWrapper>
</>
);
};
export default DecoSearchBar;
const SearchbarStyle = styled.div`
width: 100%;
display: flex;
flex-wrap: wrap;
gap: 20px 0;
font-size: 14px;
padding: 20px;
border-top: 1px solid #000;
border-bottom: 1px solid #000;
margin-bottom: 40px;
`;
const SearchItem = styled.div`
display: flex;
align-items: center;
gap: 20px;
margin-right: 50px;
${TextInput}, ${SelectInput} {
height: 35px;
}
${TextInput} {
padding: 0 10px;
max-width: 400px;
}
`;

View File

@@ -0,0 +1,166 @@
import { styled } from 'styled-components';
import { useEffect, useState } from 'react';
import { TableStyle, TableInfo, ListOption } from '../../styles/Components';
import Button from '../../components/common/button/Button';
import InstanceSearchBar from '../../components/IndexManage/InstanceSearchBar';
import { InstanceIndexExport, InstanceIndexView } from '../../apis';
const InstanceContent = () => {
const token = sessionStorage.getItem('token');
let d = new Date();
const START_DATE = new Date(new Date(d.setDate(d.getDate() - 1)).setHours(0, 0, 0, 0));
const END_DATE = new Date();
const [searchKey, setSearchKey] = useState('');
const [sendDate, setSendDate] = useState(START_DATE);
const [finishDate, setFinishDate] = useState(END_DATE);
const [dataList, setDataList] = useState([]);
useEffect(() => {
fetchData('', START_DATE, END_DATE);
}, []);
// console.log(dataList);
const fetchData = async (data, startDate, endDate) => {
const startDateToLocal =
startDate.getFullYear() +
'-' +
(startDate.getMonth() + 1 < 9 ? '0' + (startDate.getMonth() + 1) : startDate.getMonth() + 1) +
'-' +
(startDate.getDate() < 9 ? '0' + startDate.getDate() : startDate.getDate());
const endDateToLocal =
endDate.getFullYear() +
'-' +
(endDate.getMonth() + 1 < 9 ? '0' + (endDate.getMonth() + 1) : endDate.getMonth() + 1) +
'-' +
(endDate.getDate() < 9 ? '0' + endDate.getDate() : endDate.getDate());
setDataList(await InstanceIndexView(token, data, startDateToLocal, endDateToLocal));
setSearchKey(data);
setSendDate(startDateToLocal);
setFinishDate(endDateToLocal);
};
// 엑셀 다운로드
const handleXlsxExport = () => {
const fileName = 'Caliverse_Instance_Index.xlsx';
InstanceIndexExport(token, fileName, searchKey, sendDate, finishDate);
};
return (
<>
<InstanceSearchBar fetchData={fetchData} />
<TableInfo>
<ListOption>
<Button theme="line" text="엑셀 다운로드" handleClick={handleXlsxExport} />
</ListOption>
</TableInfo>
<TableWrapper>
<EconomicTable2>
<thead>
<tr>
<th width="240">일자</th>
<th rowSpan="2" width="160">
2023-08-07
</th>
<th rowSpan="2" width="160">
2023-08-08
</th>
<th rowSpan="2" width="160">
2023-08-09
</th>
<th rowSpan="2" width="160">
2023-08-10
</th>
<th rowSpan="2" width="160"></th>
<th rowSpan="2" width="160"></th>
</tr>
<tr>
<th>대상</th>
</tr>
</thead>
<tbody>
<tr>
<TableTitle>(인스턴스 ID) 방문자 </TableTitle>
<TableData>95</TableData>
<TableData>240</TableData>
<TableData>240</TableData>
<TableData>240</TableData>
<TableData $state="blank"></TableData>
<TableData $state="blank"></TableData>
</tr>
{/* {mokupData.map((data, index) => (
<Fragment key={index}>
<tr>
<td>{data.date}</td>
<td>{data.name}</td>
<td>{data.trader}</td>
<td>{data.id}</td>
<td>{data.key}</td>
</tr>
</Fragment>
))} */}
</tbody>
</EconomicTable2>
</TableWrapper>
</>
);
};
export default InstanceContent;
const TableWrapper = styled.div`
width: 100%;
min-width: 680px;
overflow: auto;
&::-webkit-scrollbar {
height: 4px;
}
&::-webkit-scrollbar-thumb {
background: #666666;
}
&::-webkit-scrollbar-track {
background: #d9d9d9;
}
${TableStyle} {
width: 100%;
min-width: 900px;
th {
&.cell-nru {
background: #f0f0f0;
border-left: 1px solid #aaa;
border-right: 1px solid #aaa;
}
}
td {
&.blank {
background: #f9f9f9;
}
&.cell-nru {
background: #fafafa;
border-left: 1px solid #aaa;
border-right: 1px solid #aaa;
}
}
}
`;
const TableData = styled.td`
background: ${props => (props.$state === 'danger' ? '#d60000' : props.$state === 'blank' ? '#F9F9F9' : 'transparent')};
color: ${props => (props.$state === 'danger' ? '#fff' : '#2c2c2c')};
`;
const TableTitle = styled.td`
text-align: center;
`;
const EconomicTable2 = styled(TableStyle)`
${TableData} {
text-align: left;
}
`;

View File

@@ -0,0 +1,117 @@
import { useState } from 'react';
import { styled } from 'styled-components';
import Button from '../../components/common/button/Button';
import DatePickerComponent from '../common/Date/DatePickerComponent';
import { FormWrapper, InputLabel, TextInput, SelectInput, BtnWrapper, InputGroup, DatePickerWrapper } from '../../styles/Components';
const InstanceSearchBar = ({ fetchData }) => {
let d = new Date();
const START_DATE = new Date(new Date(d.setDate(d.getDate() - 1)).setHours(0, 0, 0, 0));
const END_DATE = new Date();
const [resultData, setResultData] = useState({
search_key: '',
send_dt: START_DATE,
end_dt: END_DATE,
});
// 발송 날짜 세팅 로직
const handleSelectedDate = data => {
const sendDate = new Date(data);
const resultSendData = new Date(sendDate.getFullYear(), sendDate.getMonth(), sendDate.getDate());
setResultData({ ...resultData, send_dt: resultSendData });
};
// 발송 날짜 세팅 로직
const handleEndDate = data => {
const endDate = new Date(data);
const resultSendData = new Date(endDate.getFullYear(), endDate.getMonth(), endDate.getDate());
setResultData({ ...resultData, end_dt: resultSendData });
};
const handleReset = e => {
e.preventDefault();
setResultData({ search_key: '', send_dt: START_DATE, end_dt: END_DATE });
fetchData('', START_DATE, END_DATE);
};
return (
<>
<FormWrapper>
<SearchbarStyle>
<SearchItem>
<InputLabel>인스턴스 ID</InputLabel>
<TextInput type="text" placeholder="ID 입력" onChange={e => setResultData({ ...resultData, search_key: e.target.value })} />
</SearchItem>
<SearchItem>
<InputLabel>집계 기간</InputLabel>
<InputGroup>
<DatePickerWrapper>
<DatePickerComponent
name="시작 일자" selectedDate={resultData.send_dt}
handleSelectedDate={data => handleSelectedDate(data)}
maxDate={new Date()} />
<span>~</span>
<DatePickerComponent
name="종료 일자" selectedDate={resultData.end_dt}
handleSelectedDate={data => handleEndDate(data)}
pastDate = {resultData.send_dt}
maxDate={new Date()} />
</DatePickerWrapper>
</InputGroup>
</SearchItem>
<BtnWrapper $gap="8px">
<Button
theme="reset"
handleClick={e => {
handleReset(e);
}}
/>
<Button
theme="search"
text="집계"
handleClick={e => {
e.preventDefault();
fetchData(resultData.search_key, resultData.send_dt, resultData.end_dt);
}}
/>
</BtnWrapper>
</SearchbarStyle>
</FormWrapper>
</>
);
};
export default InstanceSearchBar;
const SearchbarStyle = styled.div`
width: 100%;
display: flex;
flex-wrap: wrap;
gap: 20px 0;
font-size: 14px;
padding: 20px;
border-top: 1px solid #000;
border-bottom: 1px solid #000;
margin-bottom: 40px;
`;
const SearchItem = styled.div`
display: flex;
align-items: center;
gap: 20px;
margin-right: 50px;
${TextInput}, ${SelectInput} {
height: 35px;
}
${TextInput} {
padding: 0 10px;
max-width: 400px;
}
`;

View File

@@ -0,0 +1,241 @@
import { styled } from 'styled-components';
import { useEffect, useState } from 'react';
import Button from '../../components/common/button/Button';
import { SelectInput, TableStyle, TableInfo, ListOption, InputLabel, InputGroup } from '../../styles/Components';
import ItemSearchBar from '../../components/IndexManage/ItemSearchBar';
import { ItemIndexExport, ItemIndexView } from '../../apis';
const ItemContent = () => {
const token = sessionStorage.getItem('token');
let d = new Date();
const START_DATE = new Date(new Date(d.setDate(d.getDate() - 1)).setHours(0, 0, 0, 0));
const END_DATE = new Date();
const [sendDate, setSendDate] = useState(START_DATE);
const [finishDate, setFinishDate] = useState(END_DATE);
const [dataList, setDataList] = useState([]);
useEffect(() => {
fetchData(START_DATE, END_DATE);
}, []);
// console.log(dataList);
const fetchData = async (startDate, endDate) => {
const startDateToLocal =
startDate.getFullYear() +
'-' +
(startDate.getMonth() + 1 < 9 ? '0' + (startDate.getMonth() + 1) : startDate.getMonth() + 1) +
'-' +
(startDate.getDate() < 9 ? '0' + startDate.getDate() : startDate.getDate());
const endDateToLocal =
endDate.getFullYear() +
'-' +
(endDate.getMonth() + 1 < 9 ? '0' + (endDate.getMonth() + 1) : endDate.getMonth() + 1) +
'-' +
(endDate.getDate() < 9 ? '0' + endDate.getDate() : endDate.getDate());
setDataList(await ItemIndexView(token, startDateToLocal, endDateToLocal));
setSendDate(startDateToLocal);
setFinishDate(endDateToLocal);
};
// 엑셀 다운로드
const handleXlsxExport = () => {
const fileName = 'Caliverse_Item_Index.xlsx';
ItemIndexExport(token, fileName, sendDate, finishDate);
};
return (
<>
<ItemSearchBar fetchData={fetchData} />
<TableInfo2>
<InputGroup>
<InputLabel>아이템 코드</InputLabel>
<SelectInput>
<option value="">15100001</option>
<option value="">15100001</option>
<option value="">15100001</option>
<option value="">15100001</option>
</SelectInput>
</InputGroup>
<ListOption>
<Button theme="line" text="엑셀 다운로드" handleClick={handleXlsxExport} />
</ListOption>
</TableInfo2>
<TableWrapper>
<EconomicTable>
<thead>
<tr>
<th colSpan="2" className="text-center" width="300">
Product
</th>
<th width="160">2023-08-07</th>
<th width="160">2023-08-08</th>
<th width="160">2023-08-09</th>
<th width="160">2023-08-10</th>
<th width="160">2023-08-11</th>
<th width="160">2023-08-12</th>
</tr>
</thead>
<tbody>
<tr>
<TableTitle colSpan="2">(Total) 15100001 생산량</TableTitle>
<TableData>240</TableData>
<TableData>240</TableData>
<TableData>240</TableData>
<TableData>240</TableData>
<TableData $state="blank"></TableData>
<TableData $state="blank"></TableData>
</tr>
<tr>
<TableTitle colSpan="2">(Total) 15100001 소진량</TableTitle>
<TableData>110</TableData>
<TableData>110</TableData>
<TableData>110</TableData>
<TableData>110</TableData>
<TableData $state="blank"></TableData>
<TableData $state="blank"></TableData>
</tr>
<tr>
<TableTitle colSpan="2">(Total) 15100001 보유량</TableTitle>
<TableData>3750</TableData>
<TableData>3750</TableData>
<TableData>3750</TableData>
<TableData>3750</TableData>
<TableData $state="blank"></TableData>
<TableData $state="blank"></TableData>
</tr>
<tr>
<TableTitle rowSpan="2">GET</TableTitle>
<TableTitle>퀘스트 보상</TableTitle>
<TableData>70</TableData>
<TableData>70</TableData>
<TableData>70</TableData>
<TableData>70</TableData>
<TableData $state="blank"></TableData>
<TableData $state="blank"></TableData>
</tr>
<tr>
<TableTitle>시즌패스 보상</TableTitle>
<TableData>70</TableData>
<TableData>70</TableData>
<TableData>70</TableData>
<TableData>70</TableData>
<TableData $state="blank"></TableData>
<TableData $state="blank"></TableData>
</tr>
<tr>
<TableTitle rowSpan="2">USE</TableTitle>
<TableTitle>퀘스트 보상</TableTitle>
<TableData>50</TableData>
<TableData>50</TableData>
<TableData>50</TableData>
<TableData>50</TableData>
<TableData $state="blank"></TableData>
<TableData $state="blank"></TableData>
</tr>
<tr>
<TableTitle>시즌패스 보상</TableTitle>
<TableData>50</TableData>
<TableData>50</TableData>
<TableData>50</TableData>
<TableData>50</TableData>
<TableData $state="blank"></TableData>
<TableData $state="blank"></TableData>
</tr>
{/* {mokupData.map((data, index) => (
<Fragment key={index}>
<tr>
<td>{data.date}</td>
<td>{data.name}</td>
<td>{data.trader}</td>
<td>{data.id}</td>
<td>{data.key}</td>
</tr>
</Fragment>
))} */}
</tbody>
</EconomicTable>
</TableWrapper>
</>
);
};
export default ItemContent;
const TableWrapper = styled.div`
width: 100%;
min-width: 680px;
overflow: auto;
&::-webkit-scrollbar {
height: 4px;
}
&::-webkit-scrollbar-thumb {
background: #666666;
}
&::-webkit-scrollbar-track {
background: #d9d9d9;
}
${TableStyle} {
width: 100%;
min-width: 900px;
th {
&.cell-nru {
background: #f0f0f0;
border-left: 1px solid #aaa;
border-right: 1px solid #aaa;
}
}
td {
&.blank {
background: #f9f9f9;
}
&.cell-nru {
background: #fafafa;
border-left: 1px solid #aaa;
border-right: 1px solid #aaa;
}
}
}
`;
const TableInfo2 = styled(TableInfo)`
justify-content: space-between;
${InputLabel} {
font-size: 12px;
}
${SelectInput} {
width: auto;
min-width: 100px;
height: 24px;
}
`;
const TableData = styled.td`
background: ${props => (props.$state === 'danger' ? '#d60000' : props.$state === 'blank' ? '#F9F9F9' : 'transparent')};
color: ${props => (props.$state === 'danger' ? '#fff' : '#2c2c2c')};
`;
const TableTitle = styled.td`
text-align: center;
`;
const EconomicTable = styled(TableStyle)`
${TableData} {
text-align: left;
}
tbody {
tr:nth-child(1),
tr:nth-child(2),
tr:nth-child(3) {
background: #f5fcff;
}
}
`;

View File

@@ -0,0 +1,111 @@
import { useState } from 'react';
import { styled } from 'styled-components';
import Button from '../../components/common/button/Button';
import DatePickerComponent from '../common/Date/DatePickerComponent';
import { FormWrapper, InputLabel, TextInput, SelectInput, BtnWrapper, InputGroup, DatePickerWrapper } from '../../styles/Components';
const ItemSearchBar = ({ fetchData }) => {
let d = new Date();
const START_DATE = new Date(new Date(d.setDate(d.getDate() - 1)).setHours(0, 0, 0, 0));
const END_DATE = new Date();
const [resultData, setResultData] = useState({
send_dt: START_DATE,
end_dt: END_DATE,
});
// 발송 날짜 세팅 로직
const handleSelectedDate = data => {
const sendDate = new Date(data);
const resultSendData = new Date(sendDate.getFullYear(), sendDate.getMonth(), sendDate.getDate());
setResultData({ ...resultData, send_dt: resultSendData });
};
// 발송 날짜 세팅 로직
const handleEndDate = data => {
const endDate = new Date(data);
const resultSendData = new Date(endDate.getFullYear(), endDate.getMonth(), endDate.getDate());
setResultData({ ...resultData, end_dt: resultSendData });
};
const handleReset = e => {
e.preventDefault();
setResultData({ send_dt: START_DATE, end_dt: END_DATE });
fetchData(START_DATE, END_DATE);
};
return (
<>
<FormWrapper>
<SearchbarStyle>
<SearchItem>
<InputLabel>집계 기간</InputLabel>
<InputGroup>
<DatePickerWrapper>
<DatePickerComponent
name="시작 일자" selectedDate={resultData.send_dt}
handleSelectedDate={data => handleSelectedDate(data)}
maxDate={new Date()} />
<span>~</span>
<DatePickerComponent
name="종료 일자" selectedDate={resultData.end_dt}
handleSelectedDate={data => handleEndDate(data)}
pastDate = {resultData.send_dt}
maxDate={new Date()} />
</DatePickerWrapper>
</InputGroup>
</SearchItem>
<BtnWrapper $gap="8px">
<Button
theme="reset"
handleClick={e => {
handleReset(e);
}}
/>
<Button
theme="search"
text="집계"
handleClick={e => {
e.preventDefault();
fetchData(resultData.send_dt, resultData.end_dt);
}}
/>
</BtnWrapper>
</SearchbarStyle>
</FormWrapper>
</>
);
};
export default ItemSearchBar;
const SearchbarStyle = styled.div`
width: 100%;
display: flex;
flex-wrap: wrap;
gap: 20px 0;
font-size: 14px;
padding: 20px;
border-top: 1px solid #000;
border-bottom: 1px solid #000;
margin-bottom: 40px;
`;
const SearchItem = styled.div`
display: flex;
align-items: center;
gap: 20px;
margin-right: 50px;
${TextInput}, ${SelectInput} {
height: 35px;
}
${TextInput} {
padding: 0 10px;
max-width: 400px;
}
`;

View File

@@ -0,0 +1,112 @@
import { Fragment, useEffect, useState } from 'react';
import Button from '../../components/common/button/Button';
import { TableStyle, TableInfo, ListOption, IndexTableWrap } from '../../styles/Components';
import { PlayTimeSearchBar } from '../../components/IndexManage/index';
import { PlaytimeIndexExport, PlaytimeIndexView } from '../../apis';
const PlayTimeContent = () => {
const token = sessionStorage.getItem('token');
let d = new Date();
const START_DATE = new Date(new Date(d.setDate(d.getDate() - 1)).setHours(0, 0, 0, 0));
const END_DATE = new Date();
const [dataList, setDataList] = useState([]);
const [resultData, setResultData] = useState([]);
const [sendDate, setSendDate] = useState(START_DATE);
const [finishDate, setFinishDate] = useState(END_DATE);
useEffect(() => {
fetchData(START_DATE, END_DATE);
}, []);
// 이용자 지표 데이터
const fetchData = async (startDate, endDate) => {
const startDateToLocal =
startDate.getFullYear() +
'-' +
(startDate.getMonth() + 1 < 9 ? '0' + (startDate.getMonth() + 1) : startDate.getMonth() + 1) +
'-' +
(startDate.getDate() < 9 ? '0' + startDate.getDate() : startDate.getDate());
const endDateToLocal =
endDate.getFullYear() +
'-' +
(endDate.getMonth() + 1 < 9 ? '0' + (endDate.getMonth() + 1) : endDate.getMonth() + 1) +
'-' +
(endDate.getDate() < 9 ? '0' + endDate.getDate() : endDate.getDate());
setDataList(await PlaytimeIndexView(token, startDateToLocal, endDateToLocal));
setSendDate(startDateToLocal);
setFinishDate(endDateToLocal);
};
// 검색 함수
const handleSearch = (send_dt, end_dt) => {
fetchData(send_dt, end_dt);
};
// 엑셀 다운로드
const handleXlsxExport = () => {
const fileName = 'Caliverse_PlayTime_Index.xlsx';
PlaytimeIndexExport(token, fileName, sendDate, finishDate);
};
return (
<>
<PlayTimeSearchBar setResultData={setResultData} resultData={resultData} handleSearch={handleSearch} fetchData={fetchData} />
<TableInfo>
<ListOption>
<Button theme="line" text="엑셀 다운로드" handleClick={handleXlsxExport} />
</ListOption>
</TableInfo>
<IndexTableWrap>
<TableStyle>
<caption></caption>
<thead>
<tr>
<th rowSpan="2" width="140">
일자
</th>
<th colSpan="4" width="520">
유저수
</th>
<th rowSpan="2" width="160">
누적 플레이타임()
</th>
<th rowSpan="2" width="160">
1인당 평균 플레이타임()
</th>
</tr>
<tr>
<th>30 이내</th>
<th>30 ~ 1시간</th>
<th>1시간 ~ 3시간</th>
<th>3시간 이상</th>
</tr>
</thead>
<tbody>
{dataList.playtime &&
dataList.playtime.map(time => (
<tr key={time.date}>
<td>{time.date}</td>
{time.user_cnt.map((cnt, index) => (
<td className="text-left" key={index}>
{cnt}
</td>
))}
<td className="text-left">{time.total_time}</td>
<td className="text-left">{time.average_time}</td>
</tr>
))}
</tbody>
</TableStyle>
</IndexTableWrap>
</>
);
};
export default PlayTimeContent;

View File

@@ -0,0 +1,122 @@
import { useEffect, useState } from 'react';
import { styled } from 'styled-components';
import Button from '../../components/common/button/Button';
import 'react-datepicker/dist/react-datepicker.css';
import DatePickerComponent from '../common/Date/DatePickerComponent';
import { FormWrapper, InputLabel, TextInput, SelectInput, BtnWrapper, InputGroup, DatePickerWrapper } from '../../styles/Components';
const PlayTimeSearchBar = ({ resultData, setResultData, handleSearch, fetchData }) => {
// 초기 날짜 세팅
let d = new Date();
const START_DATE = new Date(new Date(d.setDate(d.getDate() - 1)).setHours(0, 0, 0, 0));
const END_DATE = new Date();
// resultData에 임의 날짜 넣어주기
useEffect(() => {
setResultData({
send_dt: START_DATE,
end_dt: END_DATE,
});
}, []);
const searchType = [
{ name: '전체', value: 'ALL' },
{ name: '한국어', value: 'KR' },
{ name: '영어', value: 'EN' },
{ name: '일본어', value: 'JP' },
{ name: '태국어', value: 'TH' },
];
// 발송 날짜 세팅 로직
const handleSelectedDate = data => {
const sendDate = new Date(data);
const resultSendData = new Date(sendDate.getFullYear(), sendDate.getMonth(), sendDate.getDate());
setResultData({ ...resultData, send_dt: resultSendData });
};
// 발송 날짜 세팅 로직
const handleEndDate = data => {
const endDate = new Date(data);
const resultSendData = new Date(endDate.getFullYear(), endDate.getMonth(), endDate.getDate());
setResultData({ ...resultData, end_dt: resultSendData });
};
const handleReset = e => {
e.preventDefault();
setResultData({ send_dt: START_DATE, end_dt: END_DATE });
fetchData(START_DATE, END_DATE);
};
return (
<>
<FormWrapper>
<SearchbarStyle>
<SearchItem>
<InputLabel>집계 기준일</InputLabel>
<InputGroup>
<DatePickerWrapper>
<DatePickerComponent
name="시작 일자"
selectedDate={resultData.send_dt}
handleSelectedDate={data => handleSelectedDate(data)}
maxDate={new Date()} />
<span>~</span>
<DatePickerComponent
name="종료 일자"
selectedDate={resultData.end_dt}
handleSelectedDate={data => handleEndDate(data)}
pastDate = {resultData.send_dt}
maxDate={new Date()} />
</DatePickerWrapper>
</InputGroup>
</SearchItem>
<BtnWrapper $gap="8px">
<Button theme="reset" handleClick={handleReset} />
<Button
theme="search"
text="검색"
handleClick={e => {
e.preventDefault();
handleSearch(resultData.send_dt, resultData.end_dt);
}}
/>
</BtnWrapper>
</SearchbarStyle>
</FormWrapper>
</>
);
};
export default PlayTimeSearchBar;
const SearchbarStyle = styled.div`
width: 100%;
display: flex;
flex-wrap: wrap;
gap: 20px 0;
font-size: 14px;
padding: 20px;
border-top: 1px solid #000;
border-bottom: 1px solid #000;
margin-bottom: 40px;
`;
const SearchItem = styled.div`
display: flex;
align-items: center;
gap: 20px;
margin-right: 50px;
${TextInput}, ${SelectInput} {
height: 35px;
}
${TextInput} {
padding: 0 10px;
max-width: 400px;
}
`;

View File

@@ -0,0 +1,109 @@
import { Fragment, useEffect, useState } from 'react';
import Button from '../../components/common/button/Button';
import { TableStyle, TableInfo, ListOption, IndexTableWrap } from '../../styles/Components';
import { RetentionSearchBar } from '../../components/IndexManage/index';
import { RetentionIndexExport, RetentionIndexView } from '../../apis';
const RetentionContent = () => {
const token = sessionStorage.getItem('token');
let d = new Date();
const START_DATE = new Date(new Date(d.setDate(d.getDate() - 1)).setHours(0, 0, 0, 0));
const END_DATE = new Date(new Date(d.setDate(d.getDate() - 1)).setHours(24, 0, 0, 0));
const [dataList, setDataList] = useState([]);
const [resultData, setResultData] = useState([]);
const [retentionData, setRetention] = useState(1);
const [sendDate, setSendDate] = useState(START_DATE);
const [finishDate, setFinishDate] = useState(END_DATE);
const [excelBtn, setExcelBtn] = useState(true); //true 시 비활성화
useEffect(() => {
fetchData(START_DATE, END_DATE);
}, []);
// Retention 지표 데이터
const fetchData = async (startDate, endDate) => {
const startDateToLocal =
startDate.getFullYear() +
'-' +
(startDate.getMonth() + 1 < 9 ? '0' + (startDate.getMonth() + 1) : startDate.getMonth() + 1) +
'-' +
(startDate.getDate() < 9 ? '0' + startDate.getDate() : startDate.getDate());
const endDateToLocal =
endDate.getFullYear() +
'-' +
(endDate.getMonth() + 1 < 9 ? '0' + (endDate.getMonth() + 1) : endDate.getMonth() + 1) +
'-' +
(endDate.getDate() < 9 ? '0' + endDate.getDate() : endDate.getDate());
setDataList(await RetentionIndexView(token, startDateToLocal, endDateToLocal));
console.log(dataList);
setSendDate(startDateToLocal);
setFinishDate(endDateToLocal);
};
// 검색 함수
const handleSearch = (send_dt, end_dt) => {
fetchData(send_dt, end_dt);
setRetention(resultData.retention);
};
// 엑셀 다운로드
const handleXlsxExport = () => {
const fileName = 'Caliverse_Retention_Index.xlsx';
if(!excelBtn){
RetentionIndexExport(token, fileName, sendDate, finishDate);
}
};
return (
<>
<RetentionSearchBar setResultData={setResultData} resultData={resultData}
handleSearch={handleSearch} fetchData={fetchData} setRetention={setRetention} setExcelBtn={setExcelBtn} />
<TableInfo>
<ListOption>
<Button
theme={excelBtn === true ? "disable" : "line"}
text="엑셀 다운로드"
disabled={handleXlsxExport}
handleClick={handleXlsxExport} />
</ListOption>
</TableInfo>
<IndexTableWrap>
<TableStyle>
<caption></caption>
<thead>
<tr>
{/* <th width="100">국가</th> */}
<th width="150">일자</th>
<th className="cell-nru">NRU</th>
{[...Array(Number(retentionData))].map((value, index) => {
return <th key={index}>{`D+${index + 1}`}</th>;
})}
</tr>
</thead>
<tbody>
{dataList.retention &&
dataList.retention.map(data => (
<tr className="cell-nru-th" key={data.date}>
<td>{data.date}</td>
{data['d-day'].map((day, index) => (
<td key={index}>{day.dif}</td>
))}
</tr>
))}
</tbody>
</TableStyle>
</IndexTableWrap>
</>
);
};
export default RetentionContent;

View File

@@ -0,0 +1,166 @@
import { useEffect, useState } from 'react';
import { styled } from 'styled-components';
import Button from '../../components/common/button/Button';
import DatePickerComponent from '../common/Date/DatePickerComponent';
import { FormWrapper, InputLabel, TextInput, SelectInput, BtnWrapper, InputGroup, DatePickerWrapper, AlertText } from '../../styles/Components';
const RetentionSearchBar = ({ resultData, setResultData, handleSearch, fetchData, setRetention, setExcelBtn }) => {
// 초기 날짜 세팅
let d = new Date();
const START_DATE = new Date(new Date(d.setDate(d.getDate() - 1)).setHours(0, 0, 0, 0));
const END_DATE = new Date(new Date(d.setDate(d.getDate() - 1)).setHours(24, 0, 0, 0));
const [errorMessage, setErrorMessage] = useState('');
const [period, setPeriod] = useState(0);
// resultData에 임의 날짜 넣어주기
useEffect(() => {
setResultData({
send_dt: START_DATE,
end_dt: '',
retention: 0,
});
}, []);
// 발송 날짜 세팅 로직
const handleSelectedDate = data => {
const sendDate = new Date(data);
const resultSendData = new Date(sendDate.getFullYear(), sendDate.getMonth(), sendDate.getDate());
const resultEndDate = new Date(resultSendData);
resultEndDate.setDate(resultEndDate.getDate() + Number(resultData.retention));
setResultData({ ...resultData, send_dt: resultSendData, end_dt: resultEndDate });
};
// // 발송 날짜 세팅 로직
// const handleEndDate = data => {
// const endDate = new Date(data);
// const resultSendData = new Date(endDate.getFullYear(), endDate.getMonth(), endDate.getDate());
// setResultData({ ...resultData, end_dt: resultSendData });
// };
// Retention 세팅 로직
const handleRetention = e => {
const value = e.target.value;
const resultEndDate = new Date(resultData.send_dt);
resultEndDate.setDate(resultEndDate.getDate() + Number(value));
setResultData({ ...resultData, end_dt: resultEndDate, retention: value });
setPeriod(value);
};
//Retention 범위 선택 후 disable 처리 로직
const handleSearchBtn = e => {
e.preventDefault();
if(period == 0) {
setErrorMessage("필수값을 선택하세요.");
return false;
} else {
setErrorMessage("");
setExcelBtn(false); //활성화
handleSearch(resultData.send_dt, resultData.end_dt);
}
}
const handleReset = e => {
e.preventDefault();
setResultData({ send_dt: START_DATE, end_dt: '', retention: 0 });
setRetention(1);
setErrorMessage("");
setPeriod(1);
setExcelBtn(true); //비활성화
fetchData(START_DATE, END_DATE);
};
return (
<>
<FormWrapper>
<SearchbarStyle>
<SearchItem>
<InputLabel>집계 기준일</InputLabel>
<InputGroup>
<DatePickerWrapper>
<DatePickerComponent
name="시작 일자" selectedDate={resultData.send_dt}
handleSelectedDate={data => handleSelectedDate(data)}
maxDate={new Date()} />
<span>~</span>
<DatePickerComponent
name="종료 일자"
selectedDate={resultData.end_dt}
maxDate={new Date()}
readOnly={true}
disabled={true}
type="retention" />
</DatePickerWrapper>
</InputGroup>
</SearchItem>
<SearchItem>
<InputLabel>Retention 범위</InputLabel>
<SelectInput
onChange={e => handleRetention(e)} value={resultData.retention}>
<option value={0}>선택</option>
<option value={1}>D+1</option>
<option value={7}>D+7</option>
<option value={30}>D+30</option>
</SelectInput>
</SearchItem>
{/* 기획 보류 */}
{/* <SearchItem>
<InputLabel>조회 국가</InputLabel>
<SelectInput>
<option value="">ALL</option>
<option value="">KR</option>
<option value="">EN</option>
<option value="">JP</option>
<option value="">TH</option>
</SelectInput>
</SearchItem> */}
<BtnWrapper $gap="8px">
<Button theme="reset" handleClick={handleReset} />
<Button
theme="search"
text="검색"
handleClick={handleSearchBtn}
/>
<AlertText>{errorMessage}</AlertText>
</BtnWrapper>
</SearchbarStyle>
</FormWrapper>
</>
);
};
export default RetentionSearchBar;
const SearchbarStyle = styled.div`
width: 100%;
display: flex;
flex-wrap: wrap;
gap: 20px 0;
font-size: 14px;
padding: 20px;
border-top: 1px solid #000;
border-bottom: 1px solid #000;
margin-bottom: 40px;
`;
const SearchItem = styled.div`
display: flex;
align-items: center;
gap: 20px;
margin-right: 50px;
${TextInput}, ${SelectInput} {
height: 35px;
}
${TextInput} {
padding: 0 10px;
max-width: 400px;
}
`;

View File

@@ -0,0 +1,90 @@
import { Fragment, useEffect, useState } from 'react';
import Button from '../../components/common/button/Button';
import { TableStyle, TableInfo, ListOption, IndexTableWrap } from '../../styles/Components';
import { SegmentSearchBar } from '../../components/IndexManage/index';
import { SegmentIndexExport, SegmentIndexView } from '../../apis';
const SegmentContent = () => {
const token = sessionStorage.getItem('token');
const END_DATE = new Date();
const [dataList, setDataList] = useState([]);
const [resultData, setResultData] = useState([]);
const [finishDate, setFinishDate] = useState(END_DATE);
useEffect(() => {
fetchData(END_DATE);
}, []);
// Retention 지표 데이터
const fetchData = async endDate => {
const endDateToLocal =
endDate.getFullYear() +
'-' +
(endDate.getMonth() + 1 < 9 ? '0' + (endDate.getMonth() + 1) : endDate.getMonth() + 1) +
'-' +
(endDate.getDate() < 9 ? '0' + endDate.getDate() : endDate.getDate());
setDataList(await SegmentIndexView(token, endDateToLocal));
setFinishDate(endDateToLocal);
};
// 검색 함수
const handleSearch = end_dt => {
fetchData(end_dt);
};
// 엑셀 다운로드
const handleXlsxExport = () => {
const fileName = 'Caliverse_Segment_Index.xlsx';
SegmentIndexExport(token, fileName, finishDate);
};
return (
<>
<SegmentSearchBar setResultData={setResultData} resultData={resultData} handleSearch={handleSearch} fetchData={fetchData} />
<TableInfo>
<ListOption>
<Button theme="line" text="엑셀 다운로드" handleClick={handleXlsxExport} />
</ListOption>
</TableInfo>
<IndexTableWrap>
<TableStyle>
<caption></caption>
<thead>
<tr>
<th colSpan="1" width="200">
{dataList && dataList.start_dt} ~ {dataList && dataList.end_dt}
</th>
<th colSpan="2" width="400">
KIP
</th>
</tr>
<tr>
{/* <th>국가</th> */}
<th>세그먼트 분류</th>
<th>AU</th>
<th>AU Percentage by User Segment (%)</th>
</tr>
</thead>
<tbody>
{dataList && dataList.segment &&
dataList.segment.map((segment, index) => (
<tr key={index}>
{/* <td rowSpan="6">TH</td> */}
<td>{segment.type}</td>
<td>{segment.au}</td>
<td>{segment.dif}</td>
</tr>
))}
</tbody>
</TableStyle>
</IndexTableWrap>
</>
);
};
export default SegmentContent;

View File

@@ -0,0 +1,123 @@
import { useEffect } from 'react';
import { styled } from 'styled-components';
import Button from '../../components/common/button/Button';
import DatePickerComponent from '../common/Date/DatePickerComponent';
import { FormWrapper, InputLabel, TextInput, SelectInput, BtnWrapper, InputGroup, DatePickerWrapper } from '../../styles/Components';
const SegmentSearchBar = ({ resultData, setResultData, handleSearch, fetchData }) => {
// 초기 날짜 세팅
const END_DATE = new Date();
// resultData에 임의 날짜 넣어주기
useEffect(() => {
setResultData({
search_dt: END_DATE,
});
}, []);
// 발송 날짜 세팅 로직
const handleSelectedDate = data => {
const sendDate = new Date(data);
const resultSendData = new Date(sendDate.getFullYear(), sendDate.getMonth(), sendDate.getDate());
setResultData({ search_dt: resultSendData });
};
// 발송 날짜 세팅 로직
const handleReset = e => {
e.preventDefault();
setResultData({ search_dt: END_DATE });
fetchData(END_DATE);
};
// console.log(resultData);
return (
<>
<FormWrapper>
<SearchbarStyle2>
<SearchRow>
<SearchItem>
<InputLabel>집계 기준일</InputLabel>
<InputGroup>
<DatePickerWrapper>
<DatePickerComponent name="집계 기준일" selectedDate={resultData.search_dt} handleSelectedDate={data => handleSelectedDate(data)} maxDate={new Date()} />
</DatePickerWrapper>
</InputGroup>
</SearchItem>
{/* 기획 보류 */}
{/* <SearchItem>
<InputLabel>조회 국가</InputLabel>
<SelectInput>
<option value="">ALL</option>
<option value="">KR</option>
<option value="">EN</option>
<option value="">JP</option>
<option value="">TH</option>
</SelectInput>
</SearchItem> */}
<BtnWrapper $gap="8px">
<Button theme="reset" handleClick={handleReset} />
<Button
theme="search"
text="검색"
handleClick={e => {
e.preventDefault();
handleSearch(resultData.search_dt);
}}
/>
</BtnWrapper>
</SearchRow>
<SearchNoti> 집계 기준일 기준 -100일까지의 Segment 유저 분포를 집계합니다.</SearchNoti>
</SearchbarStyle2>
</FormWrapper>
</>
);
};
export default SegmentSearchBar;
const SearchbarStyle = styled.div`
width: 100%;
display: flex;
flex-wrap: wrap;
gap: 20px 0;
font-size: 14px;
padding: 20px;
border-top: 1px solid #000;
border-bottom: 1px solid #000;
margin-bottom: 40px;
`;
const SearchbarStyle2 = styled(SearchbarStyle)`
flex-flow: column;
gap: 10px;
`;
const SearchRow = styled.div`
display: flex;
flex-wrap: wrap;
gap: 20px 0;
`;
const SearchItem = styled.div`
display: flex;
align-items: center;
gap: 20px;
margin-right: 50px;
${TextInput}, ${SelectInput} {
height: 35px;
}
${TextInput} {
padding: 0 10px;
max-width: 400px;
}
`;
const SearchNoti = styled.div`
font-size: 12px;
color: #999;
letter-spacing: 0.5px;
`;

View File

@@ -0,0 +1,133 @@
import { Fragment, useEffect, useRef, useState } from 'react';
import Button from '../../components/common/button/Button';
import { Title, TableStyle, TableInfo, ListOption, IndexTableWrap } from '../../styles/Components';
import { UserIndexSearchBar, DailyDashBoard } from '../../components/IndexManage/index';
import { userIndexView, userIndexExport } from '../../apis';
import Loading from '../common/Loading';
import { ExcelDownButton } from '../common';
import { useTranslation } from 'react-i18next';
import { formatStringDate } from '../../utils';
const UserContent = () => {
const token = sessionStorage.getItem('token');
const { t } = useTranslation();
let d = new Date();
const START_DATE = new Date(new Date(d.setDate(d.getDate() - 1)).setHours(0, 0, 0, 0));
const END_DATE = new Date();
const [loading, setLoading] = useState(false);
const tableRef = useRef(null);
const [dataList, setDataList] = useState([]);
const [resultData, setResultData] = useState([]);
// const [sendDate, setSendDate] = useState(START_DATE);
// const [finishDate, setFinishDate] = useState(END_DATE);
const headers = [
{key: 'date', label: '일자'},
{key: 'nru', label: 'NRU'},
{key: 'ugqCreate', label: '일자'},
{key: 'dglc', label: '일자'},
{key: 'dau', label: '일자'},
{key: 'mcu', label: '일자'},
{key: 'date', label: '일자'},
{key: 'date', label: '일자'},
]
useEffect(() => {
fetchData(START_DATE, END_DATE);
}, []);
// 이용자 지표 데이터
const fetchData = async (startDate, endDate) => {
setLoading(true);
const startDateToLocal = formatStringDate(startDate);
const endDateToLocal = formatStringDate(endDate);
await userIndexView(token, startDateToLocal, endDateToLocal).then(data => {
setDataList(data);
setLoading(false);
});
// setSendDate(startDateToLocal);
// setFinishDate(endDateToLocal);
};
// 검색 함수
const handleSearch = (send_dt, end_dt) => {
fetchData(send_dt, end_dt);
};
// 엑셀 다운로드
// const handleXlsxExport = () => {
// const fileName = 'Caliverse_User_Index.xlsx';
// userIndexExport(token, fileName, sendDate, finishDate);
// };
return (
<>
<DailyDashBoard />
<UserIndexSearchBar setResultData={setResultData} resultData={resultData} handleSearch={handleSearch} fetchData={fetchData} />
<TableInfo>
<ListOption>
<ExcelDownButton tableRef={tableRef} fileName={t('FILE_INDEX_USER_CONTENT')} />
</ListOption>
</TableInfo>
<IndexTableWrap>
<TableStyle ref={tableRef}>
<caption></caption>
<thead>
<tr>
{/*<th width="100">국가</th>*/}
<th width="120">일자</th>
<th width="100">NRU</th>
<th width="100">UGQ(생성)</th>
<th width="100">DGLC</th>
<th width="100">DAU</th>
<th width="100">MCU</th>
<th width="100"> Playtime(HR)</th>
<th width="100">평균 Playtime</th>
<th width="100">WAU</th>
<th width="100">MAU</th>
<th width="100">ServerCount</th>
<th width="100">DB Read Count</th>
<th width="100">DB Write Count</th>
<th width="100">DB Sum</th>
{/*<th width="200">PU</th>*/}
</tr>
</thead>
<tbody>
{dataList &&
dataList.map((data, index) => (
<tr key={index}>
{/*<td rowSpan={data.length}>ALL</td>*/}
<td>{data.date}</td>
<td>{data.nru}</td>
<td>{data.ugqCreate}</td>
<td>{data.dglc}</td>
<td>{data.dau}</td>
<td>{data.mcu}</td>
<td>{Math.ceil(data.playtime / 3600) || 0}</td>
<td>{Math.round(((data.playtime / 3600) / data.dau) * 100) / 100 || 0}</td>
<td>{data.wau}</td>
<td>{data.mau}</td>
<td>{data.serverCount}</td>
<td>{data.readCapacity}</td>
<td>{data.writeCapacity}</td>
<td>{data.readCapacity + data.writeCapacity}</td>
</tr>
))}
</tbody>
</TableStyle>
</IndexTableWrap>
{loading && <Loading/>}
</>
);
};
export default UserContent;

View File

@@ -0,0 +1,135 @@
import { useEffect, useState } from 'react';
import { styled } from 'styled-components';
import Button from '../../components/common/button/Button';
import 'react-datepicker/dist/react-datepicker.css';
import DatePickerComponent from '../common/Date/DatePickerComponent';
import { FormWrapper, InputLabel, TextInput, SelectInput, BtnWrapper, InputGroup, DatePickerWrapper } from '../../styles/Components';
const UserIndexSearchBar = ({ resultData, setResultData, handleSearch, fetchData }) => {
// 초기 날짜 세팅
let d = new Date();
const START_DATE = new Date(new Date(d.setDate(d.getDate() - 1)).setHours(0, 0, 0, 0));
const END_DATE = new Date();
// resultData에 임의 날짜 넣어주기
useEffect(() => {
setResultData({
send_dt: START_DATE,
end_dt: END_DATE,
});
}, []);
const searchType = [
{ name: '전체', value: 'ALL' },
{ name: '한국어', value: 'KR' },
{ name: '영어', value: 'EN' },
{ name: '일본어', value: 'JP' },
{ name: '태국어', value: 'TH' },
];
// 발송 날짜 세팅 로직
const handleSelectedDate = data => {
const sendDate = new Date(data);
const resultSendData = new Date(sendDate.getFullYear(), sendDate.getMonth(), sendDate.getDate());
setResultData({ ...resultData, send_dt: resultSendData });
};
// 발송 날짜 세팅 로직
const handleEndDate = data => {
const endDate = new Date(data);
const resultSendData = new Date(endDate.getFullYear(), endDate.getMonth(), endDate.getDate());
setResultData({ ...resultData, end_dt: resultSendData });
};
const handleReset = e => {
e.preventDefault();
setResultData({ send_dt: START_DATE, end_dt: END_DATE });
fetchData(START_DATE, END_DATE);
};
return (
<>
<FormWrapper>
<SearchbarStyle>
<SearchItem>
<InputLabel>집계 기간</InputLabel>
<InputGroup>
<DatePickerWrapper>
<DatePickerComponent
name="시작 일자"
selectedDate={resultData.send_dt}
handleSelectedDate={data => handleSelectedDate(data)}
maxDate={new Date()} />
<span>~</span>
<DatePickerComponent
name="종료 일자"
selectedDate={resultData.end_dt}
handleSelectedDate={data => handleEndDate(data)}
pastDate = {resultData.send_dt}
maxDate={new Date()} />
</DatePickerWrapper>
</InputGroup>
</SearchItem>
{/* 기획 보류 */}
{/* <SearchItem>
<InputLabel>조회 국가</InputLabel>
<SelectInput
// onChange={e => setResultData({ ...resultData, searchType: e.target.value })}
>
{searchType.map((option, index) => (
<option key={index} value={option.value}>
{option.name}
</option>
))}
</SelectInput>
</SearchItem> */}
<BtnWrapper $gap="8px">
<Button theme="reset" handleClick={handleReset} />
<Button
theme="search"
text="검색"
handleClick={e => {
e.preventDefault();
handleSearch(resultData.send_dt, resultData.end_dt);
}}
/>
</BtnWrapper>
</SearchbarStyle>
</FormWrapper>
</>
);
};
export default UserIndexSearchBar;
const SearchbarStyle = styled.div`
width: 100%;
display: flex;
flex-wrap: wrap;
gap: 20px 0;
font-size: 14px;
padding: 20px;
border-top: 1px solid #000;
border-bottom: 1px solid #000;
margin-bottom: 40px;
`;
const SearchItem = styled.div`
display: flex;
align-items: center;
gap: 20px;
margin-right: 50px;
${TextInput}, ${SelectInput} {
height: 35px;
}
${TextInput} {
padding: 0 10px;
max-width: 400px;
}
`;

View File

@@ -0,0 +1,219 @@
import { styled } from 'styled-components';
import { useState, useEffect } from 'react';
import { TableStyle, TableInfo, ListOption } from '../../styles/Components';
import Button from '../../components/common/button/Button';
import VBPSearchBar from '../../components/IndexManage/VBPSearchBar';
import { VBPIndexExport, VbpIndexView } from '../../apis';
const VBPContent = () => {
const token = sessionStorage.getItem('token');
let d = new Date();
const START_DATE = new Date(new Date(d.setDate(d.getDate() - 1)).setHours(0, 0, 0, 0));
const END_DATE = new Date();
const [sendDate, setSendDate] = useState(START_DATE);
const [finishDate, setFinishDate] = useState(END_DATE);
const [dataList, setDataList] = useState([]);
useEffect(() => {
fetchData(START_DATE, END_DATE);
}, []);
// console.log(dataList);
const fetchData = async (startDate, endDate) => {
const startDateToLocal =
startDate.getFullYear() +
'-' +
(startDate.getMonth() + 1 < 9 ? '0' + (startDate.getMonth() + 1) : startDate.getMonth() + 1) +
'-' +
(startDate.getDate() < 9 ? '0' + startDate.getDate() : startDate.getDate());
const endDateToLocal =
endDate.getFullYear() +
'-' +
(endDate.getMonth() + 1 < 9 ? '0' + (endDate.getMonth() + 1) : endDate.getMonth() + 1) +
'-' +
(endDate.getDate() < 9 ? '0' + endDate.getDate() : endDate.getDate());
setDataList(await VbpIndexView(token, startDateToLocal, endDateToLocal));
setSendDate(startDateToLocal);
setFinishDate(endDateToLocal);
};
// 엑셀 다운로드
const handleXlsxExport = () => {
const fileName = 'Caliverse_VBP_Index.xlsx';
VBPIndexExport(token, fileName, sendDate, finishDate);
};
return (
<>
<VBPSearchBar fetchData={fetchData} />
<TableInfo>
<ListOption>
<Button theme="line" text="엑셀 다운로드" handleClick={handleXlsxExport} />
</ListOption>
</TableInfo>
<TableWrapper>
<EconomicTable>
<thead>
<tr>
<th colSpan="2" className="text-center" width="300">
Product
</th>
<th width="160">2023-08-07</th>
<th width="160">2023-08-08</th>
<th width="160">2023-08-09</th>
<th width="160">2023-08-10</th>
<th width="160">2023-08-11</th>
<th width="160">2023-08-12</th>
</tr>
</thead>
<tbody>
<tr>
<TableTitle colSpan="2">(Total) VBP 생산량</TableTitle>
<TableData>500000</TableData>
<TableData>500000</TableData>
<TableData>500000</TableData>
<TableData>500000</TableData>
<TableData>500000</TableData>
<TableData>500000</TableData>
</tr>
<tr>
<TableTitle colSpan="2">(Total) VBP 소진량</TableTitle>
<TableData>490000</TableData>
<TableData>490000</TableData>
<TableData>490000</TableData>
<TableData>490000</TableData>
<TableData>490000</TableData>
<TableData>490000</TableData>
</tr>
<tr>
<TableTitle colSpan="2">(Total) VBP 보유량</TableTitle>
<TableData>3.2M</TableData>
<TableData>3.3M</TableData>
<TableData>3.3M</TableData>
<TableData>3.4M</TableData>
<TableData>3.5M</TableData>
<TableData>3.5M</TableData>
</tr>
<tr>
<TableTitle rowSpan="2">GET</TableTitle>
<TableTitle>퀘스트 보상</TableTitle>
<TableData>150000</TableData>
<TableData>150000</TableData>
<TableData>150000</TableData>
<TableData>150000</TableData>
<TableData>150000</TableData>
<TableData>150000</TableData>
</tr>
<tr>
<TableTitle>시즌패스 보상</TableTitle>
<TableData>150000</TableData>
<TableData>150000</TableData>
<TableData>150000</TableData>
<TableData>150000</TableData>
<TableData>150000</TableData>
<TableData>150000</TableData>
</tr>
<tr>
<TableTitle rowSpan="2">USE</TableTitle>
<TableTitle>퀘스트 보상</TableTitle>
<TableData>150000</TableData>
<TableData>150000</TableData>
<TableData>150000</TableData>
<TableData>150000</TableData>
<TableData>150000</TableData>
<TableData>150000</TableData>
</tr>
<tr>
<TableTitle>시즌패스 보상</TableTitle>
<TableData>150000</TableData>
<TableData>150000</TableData>
<TableData>150000</TableData>
<TableData>150000</TableData>
<TableData>150000</TableData>
<TableData>150000</TableData>
</tr>
{/* {mokupData.map((data, index) => (
<Fragment key={index}>
<tr>
<td>{data.date}</td>
<td>{data.name}</td>
<td>{data.trader}</td>
<td>{data.id}</td>
<td>{data.key}</td>
</tr>
</Fragment>
))} */}
</tbody>
</EconomicTable>
</TableWrapper>
</>
);
};
export default VBPContent;
const TableWrapper = styled.div`
width: 100%;
min-width: 680px;
overflow: auto;
&::-webkit-scrollbar {
height: 4px;
}
&::-webkit-scrollbar-thumb {
background: #666666;
}
&::-webkit-scrollbar-track {
background: #d9d9d9;
}
${TableStyle} {
width: 100%;
min-width: 900px;
th {
&.cell-nru {
background: #f0f0f0;
border-left: 1px solid #aaa;
border-right: 1px solid #aaa;
}
}
td {
&.blank {
background: #f9f9f9;
}
&.cell-nru {
background: #fafafa;
border-left: 1px solid #aaa;
border-right: 1px solid #aaa;
}
}
}
`;
const TableData = styled.td`
background: ${props => (props.$state === 'danger' ? '#d60000' : props.$state === 'blank' ? '#F9F9F9' : 'transparent')};
color: ${props => (props.$state === 'danger' ? '#fff' : '#2c2c2c')};
`;
const TableTitle = styled.td`
text-align: center;
`;
const EconomicTable = styled(TableStyle)`
${TableData} {
text-align: left;
}
tbody {
tr:nth-child(1),
tr:nth-child(2),
tr:nth-child(3) {
background: #f5fcff;
}
}
`;

View File

@@ -0,0 +1,112 @@
import { useState } from 'react';
import { styled } from 'styled-components';
import Button from '../../components/common/button/Button';
import DatePickerComponent from '../common/Date/DatePickerComponent';
import { FormWrapper, InputLabel, TextInput, SelectInput, BtnWrapper, InputGroup, DatePickerWrapper } from '../../styles/Components';
const VBPSearchBar = ({ fetchData }) => {
let d = new Date();
const START_DATE = new Date(new Date(d.setDate(d.getDate() - 1)).setHours(0, 0, 0, 0));
const END_DATE = new Date();
const [resultData, setResultData] = useState({
send_dt: START_DATE,
end_dt: END_DATE,
});
// 발송 날짜 세팅 로직
const handleSelectedDate = data => {
const sendDate = new Date(data);
const resultSendData = new Date(sendDate.getFullYear(), sendDate.getMonth(), sendDate.getDate());
setResultData({ ...resultData, send_dt: resultSendData });
};
// 발송 날짜 세팅 로직
const handleEndDate = data => {
const endDate = new Date(data);
const resultSendData = new Date(endDate.getFullYear(), endDate.getMonth(), endDate.getDate());
setResultData({ ...resultData, end_dt: resultSendData });
};
const handleReset = e => {
e.preventDefault();
setResultData({ send_dt: START_DATE, end_dt: END_DATE });
fetchData(START_DATE, END_DATE);
};
return (
<>
<FormWrapper>
<SearchbarStyle>
<SearchItem>
<InputLabel>집계 기간</InputLabel>
<InputGroup>
<DatePickerWrapper>
<DatePickerComponent
name="시작 일자" selectedDate={resultData.send_dt}
handleSelectedDate={data => handleSelectedDate(data)}
maxDate={new Date()} />
<span>~</span>
<DatePickerComponent
name="종료 일자" selectedDate={resultData.end_dt}
handleSelectedDate={data => handleEndDate(data)}
pastDate = {resultData.send_dt}
maxDate={new Date()} />
</DatePickerWrapper>
</InputGroup>
</SearchItem>
<BtnWrapper $gap="8px">
<Button
theme="reset"
handleClick={e => {
handleReset(e);
}}
/>
<Button
theme="search"
text="집계"
handleClick={e => {
e.preventDefault();
fetchData(resultData.send_dt, resultData.end_dt);
}}
/>
</BtnWrapper>
</SearchbarStyle>
</FormWrapper>
</>
);
};
export default VBPSearchBar;
const SearchbarStyle = styled.div`
width: 100%;
display: flex;
flex-wrap: wrap;
gap: 20px 0;
font-size: 14px;
padding: 20px;
border-top: 1px solid #000;
border-bottom: 1px solid #000;
margin-bottom: 40px;
`;
const SearchItem = styled.div`
display: flex;
align-items: center;
gap: 20px;
margin-right: 50px;
${TextInput}, ${SelectInput} {
height: 35px;
}
${TextInput} {
padding: 0 10px;
max-width: 400px;
}
`;

View File

@@ -0,0 +1,27 @@
import UserIndexSearchBar from "./UserIndexSearchBar";
import RetentionSearchBar from "./RetentionSearchBar";
import SegmentSearchBar from "./SegmentSearchBar";
import DailyDashBoard from "./DailyDashBoard";
import PlayTimeSearchBar from "./PlayTimeSearchBar";
import UserContent from "./UserContent";
import PlayTimeContent from "./PlayTimeContent";
import RetentionContent from "./RetentionContent";
import SegmentContent from "./SegmentContent";
import DailyActiveUserContent from "./DailyActiveUserContent";
import DailyMedalContent from "./DailyMedalContent";
import DailySearchBar from "./DailySearchBar";
export {
UserIndexSearchBar,
RetentionSearchBar,
SegmentSearchBar,
DailyDashBoard,
PlayTimeSearchBar,
UserContent,
SegmentContent,
RetentionContent,
PlayTimeContent,
DailySearchBar,
DailyActiveUserContent,
DailyMedalContent,
};