init
This commit is contained in:
312
src/components/IndexManage/CreditContent.js
Normal file
312
src/components/IndexManage/CreditContent.js
Normal file
@@ -0,0 +1,312 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { styled } from 'styled-components';
|
||||
import Button from '../../components/common/button/Button';
|
||||
|
||||
import { CurrencyIndexExport, CurrencyIndexView } from '../../apis';
|
||||
import { SelectInput, TableStyle, TableInfo, ListOption, InputLabel } from '../../styles/Components';
|
||||
|
||||
import CreditSeacrhBar from '../../components/IndexManage/CreditSearchBar';
|
||||
import { uniqBy } from 'lodash';
|
||||
import { sumBy } from 'lodash';
|
||||
|
||||
const CreditContent = () => {
|
||||
const token = sessionStorage.getItem('token');
|
||||
let d = new Date();
|
||||
const START_DATE = new Date(new Date(d.setDate(d.getDate() - 1)).setHours(0, 0, 0, 0));
|
||||
const END_DATE = new Date();
|
||||
const CURRENCY_LIST = [
|
||||
{ "key": "Gold", "name": "골드" },
|
||||
{ "key": "Sapphire", "name": "사파이어" },
|
||||
{ "key": "Calium", "name": "칼리움" },
|
||||
{ "key": "Onyxium", "name": "오닉시움" }
|
||||
];
|
||||
|
||||
const [sendDate, setSendDate] = useState(START_DATE);
|
||||
const [finishDate, setFinishDate] = useState(END_DATE);
|
||||
const [currencyType, setCurrencyType] = useState('Gold');
|
||||
const [currencyText, setCurrencyText] = useState('골드');
|
||||
|
||||
const [dataList, setDataList] = useState([]);
|
||||
const [routeData, setRouteData] = useState([]);
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
fetchData(sendDate, finishDate, currencyType);
|
||||
}, [currencyType]);
|
||||
|
||||
const fetchData = async (startDate, endDate) => {
|
||||
const newStartDate = new Date(startDate);
|
||||
const newEndDate = new Date(endDate);
|
||||
|
||||
const startDateToLocal =
|
||||
newStartDate.getFullYear() +
|
||||
'-' +
|
||||
(newStartDate.getMonth() + 1 < 9 ? '0' + (newStartDate.getMonth() + 1) : newStartDate.getMonth() + 1) +
|
||||
'-' +
|
||||
(newStartDate.getDate() < 9 ? '0' + newStartDate.getDate() : newStartDate.getDate());
|
||||
|
||||
const endDateToLocal =
|
||||
newEndDate.getFullYear() +
|
||||
'-' +
|
||||
(newEndDate.getMonth() + 1 < 9 ? '0' + (newEndDate.getMonth() + 1) : newEndDate.getMonth() + 1) +
|
||||
'-' +
|
||||
(newEndDate.getDate() < 9 ? '0' + newEndDate.getDate() : newEndDate.getDate());
|
||||
|
||||
setDataList(await CurrencyIndexView(token, startDateToLocal, endDateToLocal, currencyType));
|
||||
|
||||
|
||||
setSendDate(startDateToLocal);
|
||||
setFinishDate(endDateToLocal);
|
||||
setRoutArray(await CurrencyIndexView(token, startDateToLocal, endDateToLocal, currencyType));
|
||||
};
|
||||
|
||||
const handleCurrencyChange = e => {
|
||||
let value = e.target.value;
|
||||
setCurrencyType(value);
|
||||
CURRENCY_LIST.filter(data => data.key === value).map(data => {
|
||||
setCurrencyText(data.name);
|
||||
});
|
||||
};
|
||||
|
||||
//route data
|
||||
const setRoutArray = async (data) => {
|
||||
let routeArray = [];
|
||||
let routeAcqArr = [];
|
||||
let routeConArr = [];
|
||||
|
||||
//생산량 route
|
||||
data.list && data.list[0].daily_data.filter(routeData => routeData.delta_type === 'ACQUIRE').map(routeData => {
|
||||
routeData.data.map(routeData => {
|
||||
if(!routeAcqArr.includes(routeData.route) ){
|
||||
routeAcqArr.push(routeData.route);
|
||||
}
|
||||
})
|
||||
});
|
||||
routeArray.ACQUIRE = routeAcqArr;
|
||||
|
||||
//소진량 route
|
||||
data.list && data.list[0].daily_data.filter(routeData => routeData.delta_type === 'CONSUME').map(routeData => {
|
||||
routeData.data.map(routeData => {
|
||||
if(!routeConArr.includes(routeData.route) ){
|
||||
routeConArr.push(routeData.route);
|
||||
}
|
||||
})
|
||||
});
|
||||
routeArray.CONSUME = routeConArr;
|
||||
|
||||
setRouteData(routeArray);
|
||||
};
|
||||
|
||||
// 엑셀 다운로드
|
||||
const handleXlsxExport = () => {
|
||||
const fileName = 'Caliverse_Credit_Index.xlsx';
|
||||
CurrencyIndexExport(token, fileName, sendDate, finishDate, currencyType);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
<CreditSeacrhBar fetchData={fetchData} />
|
||||
<TableInfo2>
|
||||
<SelectInput onChange={handleCurrencyChange}>
|
||||
{CURRENCY_LIST.map((item, index) => (
|
||||
<option value={item.key} key={index}>
|
||||
{item.name}
|
||||
</option>
|
||||
))}
|
||||
</SelectInput>
|
||||
<ListOption>
|
||||
<Button theme="line" text="엑셀 다운로드" handleClick={handleXlsxExport} />
|
||||
</ListOption>
|
||||
</TableInfo2>
|
||||
<TableWrapper>
|
||||
<EconomicTable>
|
||||
<thead>
|
||||
<tr>
|
||||
<th colSpan="2" className="text-center" width="300">
|
||||
Product
|
||||
</th>
|
||||
{dataList.list && uniqBy(dataList.list, 'date').map(data =>
|
||||
<th width="160" key={data.date}>{data.date}</th>
|
||||
)}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<TableTitle colSpan="2">(Total) {currencyText} 생산량</TableTitle>
|
||||
{dataList.list &&
|
||||
dataList.list.map((data) =>
|
||||
(data.total).filter(totalData => data.date === totalData.date && totalData.delta_type === 'ACQUIRE')
|
||||
.map((totalData, index) => (
|
||||
<TableData key={index}
|
||||
$state={totalData.dif !== "" && totalData.dif !== "Infinity" ? "danger" : ""}>
|
||||
{totalData.quantity}
|
||||
{
|
||||
totalData.dif !== "" && totalData.dif !== "Infinity"
|
||||
? (<span>({totalData.dif})</span>)
|
||||
: ("")
|
||||
}
|
||||
</TableData>
|
||||
)
|
||||
))
|
||||
}
|
||||
</tr>
|
||||
<tr>
|
||||
<TableTitle colSpan="2">(Total) {currencyText} 소진량</TableTitle>
|
||||
{dataList.list &&
|
||||
dataList.list.map((data) =>
|
||||
(data.total).filter(totalData => data.date === totalData.date && totalData.delta_type === 'CONSUME')
|
||||
.map((totalData, index) => (
|
||||
<TableData key={index}
|
||||
$state={totalData.dif !== "" && totalData.dif !== "Infinity" ? "danger" : ""}>
|
||||
{totalData.quantity}
|
||||
{
|
||||
totalData.dif !== "" && totalData.dif !== "Infinity"
|
||||
? (<span>({totalData.dif})</span>)
|
||||
: ("")
|
||||
}
|
||||
</TableData>
|
||||
)
|
||||
))
|
||||
}
|
||||
</tr>
|
||||
<tr>
|
||||
<TableTitle colSpan="2">(Total) {currencyText} 보유량</TableTitle>
|
||||
{dataList.list &&
|
||||
dataList.list.map((data, index) => (
|
||||
<TableData key={index}>
|
||||
{sumBy(
|
||||
data.total.filter(totalData => data.date === totalData.date && totalData.delta_type === 'ACQUIRE'),
|
||||
'quantity',
|
||||
) -
|
||||
sumBy(
|
||||
data.total.filter(totalData => data.date === totalData.date && totalData.delta_type === 'CONSUME'),
|
||||
'quantity',
|
||||
)}
|
||||
</TableData>
|
||||
))
|
||||
}
|
||||
</tr>
|
||||
{/* 획득 GET */}
|
||||
{
|
||||
routeData.ACQUIRE && routeData.ACQUIRE.map((route, index) => (
|
||||
<tr key={index}>
|
||||
<TableTitle>GET</TableTitle>
|
||||
<TableTitle>{route}</TableTitle>
|
||||
{dataList.list &&
|
||||
dataList.list.map((data) =>
|
||||
data.daily_data.filter(dailyData => data.date === dailyData.date && dailyData.delta_type === 'ACQUIRE')
|
||||
.map(dailyData => (dailyData.data).filter(routeData => data.date === routeData.date && routeData.route === route)
|
||||
.map((routeData, i) => (
|
||||
<TableData key={i} data={routeData.date}>{routeData.quantity}</TableData>
|
||||
)))
|
||||
)
|
||||
}
|
||||
</tr>
|
||||
))
|
||||
}
|
||||
{/* 소진 USE CONSUME */}
|
||||
{
|
||||
routeData.CONSUME && routeData.CONSUME.map((route, index) => (
|
||||
<tr key={index}>
|
||||
<TableTitle>USE</TableTitle>
|
||||
<TableTitle>{route}</TableTitle>
|
||||
{dataList.list &&
|
||||
dataList.list.map((data) =>
|
||||
data.daily_data.filter(dailyData => data.date === dailyData.date && dailyData.delta_type === 'CONSUME')
|
||||
.map(dailyData => (dailyData.data).filter(routeData => data.date === routeData.date && routeData.route === route)
|
||||
.map((routeData, i) => (
|
||||
<TableData key={i} data={routeData.date}>{routeData.quantity}</TableData>
|
||||
)))
|
||||
)
|
||||
}
|
||||
</tr>
|
||||
))
|
||||
}
|
||||
</tbody>
|
||||
</EconomicTable>
|
||||
</TableWrapper>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default CreditContent;
|
||||
|
||||
const TableWrapper = styled.div`
|
||||
width: 100%;
|
||||
min-width: 680px;
|
||||
overflow: auto;
|
||||
&::-webkit-scrollbar {
|
||||
height: 4px;
|
||||
}
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background: #666666;
|
||||
}
|
||||
&::-webkit-scrollbar-track {
|
||||
background: #d9d9d9;
|
||||
}
|
||||
${TableStyle} {
|
||||
width: 100%;
|
||||
min-width: 900px;
|
||||
th {
|
||||
&.cell-nru {
|
||||
background: #f0f0f0;
|
||||
border-left: 1px solid #aaa;
|
||||
border-right: 1px solid #aaa;
|
||||
}
|
||||
}
|
||||
td {
|
||||
&.blank {
|
||||
background: #f9f9f9;
|
||||
}
|
||||
&.cell-nru {
|
||||
background: #fafafa;
|
||||
border-left: 1px solid #aaa;
|
||||
border-right: 1px solid #aaa;
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const TableInfo2 = styled(TableInfo)`
|
||||
justify-content: space-between;
|
||||
${InputLabel} {
|
||||
font-size: 12px;
|
||||
}
|
||||
${SelectInput} {
|
||||
width: auto;
|
||||
min-width: 100px;
|
||||
height: 24px;
|
||||
}
|
||||
`;
|
||||
|
||||
const TableDate = styled.th`
|
||||
color: ${props => (props.$state === 'danger' ? '#d60000' : '#2c2c2c')};
|
||||
`;
|
||||
|
||||
const TableData = styled.td`
|
||||
|
||||
background: ${props => (props.$state === 'danger' ? '#d60000' : props.$state === 'blank' ? '#F9F9F9' : 'transparent')};
|
||||
color: ${props => (props.$state === 'danger' ? '#fff' : '#2c2c2c')};
|
||||
`;
|
||||
|
||||
const perData = styled.span`
|
||||
display: ${props => (props.$view === 'hidden' ? 'none' : 'block')};
|
||||
`;
|
||||
|
||||
const TableTitle = styled.td`
|
||||
text-align: center;
|
||||
`;
|
||||
|
||||
const EconomicTable = styled(TableStyle)`
|
||||
${TableData} {
|
||||
text-align: left;
|
||||
}
|
||||
tbody {
|
||||
tr:nth-child(1),
|
||||
tr:nth-child(2),
|
||||
tr:nth-child(3) {
|
||||
background: #f5fcff;
|
||||
}
|
||||
}
|
||||
`;
|
||||
113
src/components/IndexManage/CreditSearchBar.js
Normal file
113
src/components/IndexManage/CreditSearchBar.js
Normal file
@@ -0,0 +1,113 @@
|
||||
import { useState } from 'react';
|
||||
import { styled } from 'styled-components';
|
||||
import Button from '../../components/common/button/Button';
|
||||
|
||||
import DatePickerComponent from '../common/Date/DatePickerComponent';
|
||||
import 'react-datepicker/dist/react-datepicker.css';
|
||||
|
||||
import { FormWrapper, InputLabel, TextInput, SelectInput, BtnWrapper, InputGroup, DatePickerWrapper } from '../../styles/Components';
|
||||
|
||||
const CreditSeacrhBar = ({ fetchData }) => {
|
||||
let d = new Date();
|
||||
const START_DATE = new Date(new Date(d.setDate(d.getDate() - 1)).setHours(0, 0, 0, 0));
|
||||
const END_DATE = new Date();
|
||||
|
||||
const [resultData, setResultData] = useState({
|
||||
send_dt: START_DATE,
|
||||
end_dt: END_DATE,
|
||||
});
|
||||
|
||||
// 발송 날짜 세팅 로직
|
||||
const handleSelectedDate = data => {
|
||||
const sendDate = new Date(data);
|
||||
const resultSendData = new Date(sendDate.getFullYear(), sendDate.getMonth(), sendDate.getDate());
|
||||
|
||||
setResultData({ ...resultData, send_dt: resultSendData });
|
||||
};
|
||||
|
||||
// 발송 날짜 세팅 로직
|
||||
const handleEndDate = data => {
|
||||
const endDate = new Date(data);
|
||||
const resultSendData = new Date(endDate.getFullYear(), endDate.getMonth(), endDate.getDate());
|
||||
|
||||
setResultData({ ...resultData, end_dt: resultSendData });
|
||||
};
|
||||
|
||||
const handleReset = e => {
|
||||
e.preventDefault();
|
||||
setResultData({ send_dt: START_DATE, end_dt: END_DATE });
|
||||
|
||||
fetchData(START_DATE, END_DATE);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<FormWrapper>
|
||||
<SearchbarStyle>
|
||||
<SearchItem>
|
||||
<InputLabel>집계 기간</InputLabel>
|
||||
<InputGroup>
|
||||
<DatePickerWrapper>
|
||||
<DatePickerComponent
|
||||
name="시작 일자" selectedDate={resultData.send_dt}
|
||||
handleSelectedDate={data => handleSelectedDate(data)}
|
||||
maxDate={new Date()} />
|
||||
<span>~</span>
|
||||
<DatePickerComponent
|
||||
name="종료 일자" selectedDate={resultData.end_dt}
|
||||
handleSelectedDate={data => handleEndDate(data)}
|
||||
pastDate = {resultData.send_dt}
|
||||
maxDate={new Date()} />
|
||||
</DatePickerWrapper>
|
||||
</InputGroup>
|
||||
</SearchItem>
|
||||
<BtnWrapper $gap="8px">
|
||||
<Button
|
||||
theme="reset"
|
||||
handleClick={e => {
|
||||
handleReset(e);
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
theme="search"
|
||||
text="집계"
|
||||
handleClick={e => {
|
||||
e.preventDefault();
|
||||
fetchData(resultData.send_dt, resultData.end_dt);
|
||||
}}
|
||||
/>
|
||||
</BtnWrapper>
|
||||
</SearchbarStyle>
|
||||
</FormWrapper>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default CreditSeacrhBar;
|
||||
|
||||
const SearchbarStyle = styled.div`
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 20px 0;
|
||||
font-size: 14px;
|
||||
padding: 20px;
|
||||
border-top: 1px solid #000;
|
||||
border-bottom: 1px solid #000;
|
||||
margin-bottom: 40px;
|
||||
`;
|
||||
|
||||
const SearchItem = styled.div`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 20px;
|
||||
margin-right: 50px;
|
||||
|
||||
${TextInput}, ${SelectInput} {
|
||||
height: 35px;
|
||||
}
|
||||
${TextInput} {
|
||||
padding: 0 10px;
|
||||
max-width: 400px;
|
||||
}
|
||||
`;
|
||||
110
src/components/IndexManage/DailyActiveUserContent.js
Normal file
110
src/components/IndexManage/DailyActiveUserContent.js
Normal file
@@ -0,0 +1,110 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
import Button from '../../components/common/button/Button';
|
||||
|
||||
import { TableStyle, TableInfo, ListOption, IndexTableWrap } from '../../styles/Components';
|
||||
import { DailySearchBar } from '../../components/IndexManage/index';
|
||||
import { DailyActiveUserExport, DailyActiveUserView } from '../../apis';
|
||||
|
||||
const PlayTimeContent = () => {
|
||||
const token = sessionStorage.getItem('token');
|
||||
let d = new Date();
|
||||
const START_DATE = new Date(new Date(d.setDate(d.getDate() - 1)).setHours(0, 0, 0, 0));
|
||||
const END_DATE = new Date();
|
||||
|
||||
const [dataList, setDataList] = useState([]);
|
||||
const [resultData, setResultData] = useState([]);
|
||||
|
||||
const [sendDate, setSendDate] = useState(START_DATE);
|
||||
const [finishDate, setFinishDate] = useState(END_DATE);
|
||||
|
||||
useEffect(() => {
|
||||
fetchData(START_DATE, END_DATE);
|
||||
}, []);
|
||||
|
||||
// DAU 데이터
|
||||
const fetchData = async (startDate, endDate) => {
|
||||
const startDateToLocal =
|
||||
startDate.getFullYear() +
|
||||
'-' +
|
||||
(startDate.getMonth() + 1 < 9 ? '0' + (startDate.getMonth() + 1) : startDate.getMonth() + 1) +
|
||||
'-' +
|
||||
(startDate.getDate() < 9 ? '0' + startDate.getDate() : startDate.getDate());
|
||||
|
||||
const endDateToLocal =
|
||||
endDate.getFullYear() +
|
||||
'-' +
|
||||
(endDate.getMonth() + 1 < 9 ? '0' + (endDate.getMonth() + 1) : endDate.getMonth() + 1) +
|
||||
'-' +
|
||||
(endDate.getDate() < 9 ? '0' + endDate.getDate() : endDate.getDate());
|
||||
|
||||
// await DailyActiveUserView(token, startDateToLocal, endDateToLocal).then(data => {
|
||||
// console.log(data);
|
||||
// setDataList(data);
|
||||
// });
|
||||
|
||||
setSendDate(startDateToLocal);
|
||||
setFinishDate(endDateToLocal);
|
||||
};
|
||||
|
||||
// 검색 함수
|
||||
const handleSearch = (send_dt, end_dt) => {
|
||||
fetchData(send_dt, end_dt);
|
||||
};
|
||||
|
||||
// 엑셀 다운로드
|
||||
const handleXlsxExport = () => {
|
||||
const fileName = 'Caliverse_Dau.xlsx';
|
||||
DailyActiveUserExport(token, fileName, sendDate, finishDate);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<DailySearchBar setResultData={setResultData} resultData={resultData} handleSearch={handleSearch} fetchData={fetchData} />
|
||||
<TableInfo>
|
||||
<ListOption>
|
||||
<Button theme="line" text="엑셀 다운로드" handleClick={handleXlsxExport} />
|
||||
</ListOption>
|
||||
</TableInfo>
|
||||
<IndexTableWrap>
|
||||
<TableStyle>
|
||||
<caption></caption>
|
||||
<thead>
|
||||
<tr>
|
||||
<th rowSpan="1" width="45">
|
||||
일자
|
||||
</th>
|
||||
<th colSpan="1" width="30">
|
||||
DAU
|
||||
</th>
|
||||
{/*<th colSpan="1" width="30">*/}
|
||||
{/* DALC*/}
|
||||
{/*</th>*/}
|
||||
<th colSpan="1" width="30">
|
||||
DGLC
|
||||
</th>
|
||||
{/*<th colSpan="1" width="30">*/}
|
||||
{/* MaxAU*/}
|
||||
{/*</th>*/}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
{dataList && (dataList || []).map((data, index) => (
|
||||
<tr key={index}>
|
||||
<td>{data.date}</td>
|
||||
<td>{data.dau}</td>
|
||||
{/*<td>{data.dalc}</td>*/}
|
||||
<td>{data.dglc}</td>
|
||||
{/*<td>{data.maxAu}</td>*/}
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</TableStyle>
|
||||
</IndexTableWrap>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default PlayTimeContent;
|
||||
|
||||
170
src/components/IndexManage/DailyDashBoard.js
Normal file
170
src/components/IndexManage/DailyDashBoard.js
Normal file
@@ -0,0 +1,170 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { userTotalIndex } from '../../apis';
|
||||
|
||||
import { styled } from 'styled-components';
|
||||
import TitleArrow from '../../assets/img/icon/icon-title.png';
|
||||
import UpIcon from '../../assets/img/icon/icon-up.png';
|
||||
import DownIcon from '../../assets/img/icon/icon-down.png';
|
||||
|
||||
const DailyDashBoard = ({ content }) => {
|
||||
const [boardState, setBoardState] = useState('active');
|
||||
const [totalData, setTotalData] = useState([]);
|
||||
|
||||
const handleBoard = () => {
|
||||
if (boardState === 'active') {
|
||||
setBoardState('inactive');
|
||||
} else {
|
||||
setBoardState('active');
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
// 이용자 지표 총계 불러오기
|
||||
fetchData();
|
||||
}, []);
|
||||
|
||||
const fetchData = async () => {
|
||||
const token = sessionStorage.getItem('token');
|
||||
await userTotalIndex(token).then(data => {
|
||||
console.log(data);
|
||||
setTotalData(data.dashboard);
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<DailyBoardWrapper>
|
||||
{totalData &&
|
||||
<DailyBoard>
|
||||
<BoardTitle onClick={handleBoard} $state={boardState}>
|
||||
Daily Dashboard
|
||||
</BoardTitle>
|
||||
<BoardInfo $state={boardState}>
|
||||
<BoxWrapper>
|
||||
<InfoItem>
|
||||
<InfoTitle>DAU</InfoTitle>
|
||||
<InfoValue>
|
||||
{totalData.dau && totalData.dau.count}
|
||||
<span>({totalData.dau && totalData.dau.dif}%)</span>
|
||||
<InfoArrow $state={totalData.dau && totalData.dau.updown === '=' ? null : totalData.dau && totalData.dau.updown}></InfoArrow>
|
||||
</InfoValue>
|
||||
</InfoItem>
|
||||
<InfoItem>
|
||||
<InfoTitle>NRU</InfoTitle>
|
||||
<InfoValue>
|
||||
{totalData.nru && totalData.nru.count}
|
||||
<span>({totalData.nru && totalData.nru.dif}%)</span>
|
||||
<InfoArrow $state={totalData.nru && totalData.nru.updown === '=' ? null : totalData.nru && totalData.nru.updown}></InfoArrow>
|
||||
</InfoValue>
|
||||
</InfoItem>
|
||||
<InfoItem>
|
||||
<InfoTitle>PU</InfoTitle>
|
||||
<InfoValue>
|
||||
{totalData.pu && totalData.pu.count}
|
||||
<span>({totalData.pu && totalData.pu.dif}%)</span>
|
||||
<InfoArrow $state={totalData.pu && totalData.pu.updown === '=' ? null : totalData.pu && totalData.pu.updown}></InfoArrow>
|
||||
</InfoValue>
|
||||
</InfoItem>
|
||||
<InfoItem>
|
||||
<InfoTitle>MCU</InfoTitle>
|
||||
<InfoValue>
|
||||
{totalData.mcu && totalData.mcu.count}
|
||||
<span>({totalData.mcu && totalData.mcu.dif}%)</span>
|
||||
<InfoArrow $state={totalData.mcu && totalData.mcu.updown === '=' ? null : totalData.mcu && totalData.mcu.updown}></InfoArrow>
|
||||
</InfoValue>
|
||||
</InfoItem>
|
||||
</BoxWrapper>
|
||||
<InfoNotice>집계 기간 : {totalData.date && totalData.date} 00시 ~ 24시 (집계는 당일 기준 하루 전 데이터를 사용합니다)</InfoNotice>
|
||||
</BoardInfo>
|
||||
</DailyBoard>
|
||||
}
|
||||
</DailyBoardWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
export default DailyDashBoard;
|
||||
|
||||
const DailyBoardWrapper = styled.div`
|
||||
padding-top: 20px;
|
||||
border-top: 1px solid #000;
|
||||
`;
|
||||
|
||||
const DailyBoard = styled.div`
|
||||
background: #f6f6f6;
|
||||
border-radius: 10px;
|
||||
margin-bottom: 20px;
|
||||
`;
|
||||
|
||||
const BoardTitle = styled.div`
|
||||
font-size: 24px;
|
||||
font-weight: 600;
|
||||
line-height: 52px;
|
||||
padding: 0 10px;
|
||||
cursor: pointer;
|
||||
&:after {
|
||||
content: '';
|
||||
display: inline-block;
|
||||
width: 11px;
|
||||
height: 52px;
|
||||
margin-left: 10px;
|
||||
background: url(${TitleArrow}) 50% 50% no-repeat;
|
||||
position: absolute;
|
||||
transform: ${props => (props.$state === 'active' ? 'rotate(0)' : 'rotate(180deg)')};
|
||||
}
|
||||
`;
|
||||
|
||||
const BoardInfo = styled.div`
|
||||
padding: 20px;
|
||||
border-top: 1px solid #d9d9d9;
|
||||
display: ${props => (props.$state === 'active' ? 'block' : 'none')};
|
||||
`;
|
||||
|
||||
const BoxWrapper = styled.div`
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
`;
|
||||
|
||||
const InfoItem = styled.div`
|
||||
width: 24%;
|
||||
background: #fff;
|
||||
padding: 15px 20px;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
border-radius: 15px;
|
||||
`;
|
||||
|
||||
const InfoTitle = styled.div`
|
||||
width: 42px;
|
||||
font-size: 20px;
|
||||
font-weight: 600;
|
||||
`;
|
||||
|
||||
const InfoValue = styled.div`
|
||||
display: inline-flex;
|
||||
flex-wrap: wrap;
|
||||
margin: 5px 0;
|
||||
gap: 5px 0;
|
||||
align-items: center;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
span {
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
display: inline-block;
|
||||
margin: 0 5px;
|
||||
}
|
||||
`;
|
||||
|
||||
const InfoArrow = styled.div`
|
||||
width: 12px;
|
||||
height: 6px;
|
||||
background: url(${props => (props.$state === null ? '' : props.$state === 'UP' ? UpIcon : DownIcon)}) 50% 50% no-repeat;
|
||||
`;
|
||||
const InfoNotice = styled.div`
|
||||
font-size: 12px;
|
||||
text-align: right;
|
||||
color: #999;
|
||||
margin-top: 10px;
|
||||
`;
|
||||
111
src/components/IndexManage/DailyMedalContent.js
Normal file
111
src/components/IndexManage/DailyMedalContent.js
Normal file
@@ -0,0 +1,111 @@
|
||||
import { Fragment, useEffect, useState } from 'react';
|
||||
|
||||
import Button from '../common/button/Button';
|
||||
|
||||
import { TableStyle, TableInfo, ListOption, IndexTableWrap } from '../../styles/Components';
|
||||
import { DailySearchBar } from './index';
|
||||
import { DailyMedalView } from '../../apis';
|
||||
|
||||
const DailyMedalContent = () => {
|
||||
const token = sessionStorage.getItem('token');
|
||||
let d = new Date();
|
||||
const START_DATE = new Date(new Date(d.setDate(d.getDate() - 1)).setHours(0, 0, 0, 0));
|
||||
const END_DATE = new Date();
|
||||
|
||||
const [dataList, setDataList] = useState([]);
|
||||
const [resultData, setResultData] = useState([]);
|
||||
|
||||
const [sendDate, setSendDate] = useState(START_DATE);
|
||||
const [finishDate, setFinishDate] = useState(END_DATE);
|
||||
|
||||
useEffect(() => {
|
||||
fetchData(START_DATE, END_DATE);
|
||||
}, []);
|
||||
|
||||
const fetchData = async (startDate, endDate) => {
|
||||
const startDateToLocal =
|
||||
startDate.getFullYear() +
|
||||
'-' +
|
||||
(startDate.getMonth() + 1 < 9 ? '0' + (startDate.getMonth() + 1) : startDate.getMonth() + 1) +
|
||||
'-' +
|
||||
(startDate.getDate() < 9 ? '0' + startDate.getDate() : startDate.getDate());
|
||||
|
||||
const endDateToLocal =
|
||||
endDate.getFullYear() +
|
||||
'-' +
|
||||
(endDate.getMonth() + 1 < 9 ? '0' + (endDate.getMonth() + 1) : endDate.getMonth() + 1) +
|
||||
'-' +
|
||||
(endDate.getDate() < 9 ? '0' + endDate.getDate() : endDate.getDate());
|
||||
|
||||
setDataList(await DailyMedalView(token, startDateToLocal, endDateToLocal));
|
||||
|
||||
setSendDate(startDateToLocal);
|
||||
setFinishDate(endDateToLocal);
|
||||
};
|
||||
|
||||
// 검색 함수
|
||||
const handleSearch = (send_dt, end_dt) => {
|
||||
fetchData(send_dt, end_dt);
|
||||
};
|
||||
|
||||
// 엑셀 다운로드
|
||||
const handleXlsxExport = () => {
|
||||
const fileName = 'Caliverse_Daily_Medal.xlsx';
|
||||
//DailyActiveUserExport(token, fileName, sendDate, finishDate);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<DailySearchBar setResultData={setResultData} resultData={resultData} handleSearch={handleSearch} fetchData={fetchData} />
|
||||
<TableInfo>
|
||||
<ListOption>
|
||||
<Button theme="line" text="엑셀 다운로드" handleClick={handleXlsxExport} />
|
||||
</ListOption>
|
||||
</TableInfo>
|
||||
<IndexTableWrap>
|
||||
<TableStyle>
|
||||
<caption></caption>
|
||||
<thead >
|
||||
<tr>
|
||||
<th rowSpan="1" width="20">
|
||||
일자
|
||||
</th>
|
||||
<th colSpan="1" width="30">
|
||||
UserID
|
||||
</th>
|
||||
<th colSpan="1" width="30">
|
||||
닉네임
|
||||
</th>
|
||||
<th colSpan="1" width="30">
|
||||
Item ID
|
||||
</th>
|
||||
<th colSpan="1" width="30">
|
||||
획득량
|
||||
</th>
|
||||
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
{(dataList || []).map((data, index) => (
|
||||
<tr key={index}>
|
||||
<td>{data.date}</td>
|
||||
<td>{data.dau}</td>
|
||||
<td>{data.dalc}</td>
|
||||
<td>{data.dglc}</td>
|
||||
<td>{data.maxAu}</td>
|
||||
{Array.from({ length: 24 }, (_, i) => (
|
||||
<td key={i}>{data['h' + i]}</td>
|
||||
))}
|
||||
</tr>
|
||||
))}
|
||||
|
||||
</tbody>
|
||||
</TableStyle>
|
||||
</IndexTableWrap>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default DailyMedalContent;
|
||||
|
||||
112
src/components/IndexManage/DailySearchBar.js
Normal file
112
src/components/IndexManage/DailySearchBar.js
Normal file
@@ -0,0 +1,112 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { styled } from 'styled-components';
|
||||
import Button from '../common/button/Button';
|
||||
|
||||
import 'react-datepicker/dist/react-datepicker.css';
|
||||
import DatePickerComponent from '../common/Date/DatePickerComponent';
|
||||
|
||||
import { FormWrapper, InputLabel, TextInput, SelectInput, BtnWrapper, InputGroup, DatePickerWrapper } from '../../styles/Components';
|
||||
|
||||
const DailySearchBar = ({ resultData, setResultData, handleSearch, fetchData }) => {
|
||||
// 초기 날짜 세팅
|
||||
let d = new Date();
|
||||
const START_DATE = new Date(new Date(d.setDate(d.getDate() - 1)).setHours(0, 0, 0, 0));
|
||||
const END_DATE = new Date();
|
||||
|
||||
// resultData에 임의 날짜 넣어주기
|
||||
useEffect(() => {
|
||||
setResultData({
|
||||
send_dt: START_DATE,
|
||||
end_dt: END_DATE,
|
||||
});
|
||||
}, []);
|
||||
|
||||
const handleSelectedDate = data => {
|
||||
const sendDate = new Date(data);
|
||||
const resultSendData = new Date(sendDate.getFullYear(), sendDate.getMonth(), sendDate.getDate());
|
||||
|
||||
setResultData({ ...resultData, send_dt: resultSendData });
|
||||
};
|
||||
|
||||
const handleEndDate = data => {
|
||||
const endDate = new Date(data);
|
||||
const resultSendData = new Date(endDate.getFullYear(), endDate.getMonth(), endDate.getDate());
|
||||
|
||||
setResultData({ ...resultData, end_dt: resultSendData });
|
||||
};
|
||||
|
||||
const handleReset = e => {
|
||||
e.preventDefault();
|
||||
setResultData({ send_dt: START_DATE, end_dt: END_DATE });
|
||||
|
||||
fetchData(START_DATE, END_DATE);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<FormWrapper>
|
||||
<SearchbarStyle>
|
||||
<SearchItem>
|
||||
<InputLabel>집계 기준일</InputLabel>
|
||||
<InputGroup>
|
||||
<DatePickerWrapper>
|
||||
<DatePickerComponent
|
||||
name="시작 일자"
|
||||
selectedDate={resultData.send_dt}
|
||||
handleSelectedDate={data => handleSelectedDate(data)}
|
||||
maxDate={new Date()} />
|
||||
<span>~</span>
|
||||
<DatePickerComponent
|
||||
name="종료 일자"
|
||||
selectedDate={resultData.end_dt}
|
||||
handleSelectedDate={data => handleEndDate(data)}
|
||||
pastDate = {resultData.send_dt}
|
||||
maxDate={new Date()} />
|
||||
</DatePickerWrapper>
|
||||
</InputGroup>
|
||||
</SearchItem>
|
||||
<BtnWrapper $gap="8px">
|
||||
<Button theme="reset" handleClick={handleReset} />
|
||||
<Button
|
||||
theme="search"
|
||||
text="검색"
|
||||
handleClick={e => {
|
||||
e.preventDefault();
|
||||
handleSearch(resultData.send_dt, resultData.end_dt);
|
||||
}}
|
||||
/>
|
||||
</BtnWrapper>
|
||||
</SearchbarStyle>
|
||||
</FormWrapper>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default DailySearchBar;
|
||||
|
||||
const SearchbarStyle = styled.div`
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 20px 0;
|
||||
font-size: 14px;
|
||||
padding: 20px;
|
||||
border-top: 1px solid #000;
|
||||
border-bottom: 1px solid #000;
|
||||
margin-bottom: 40px;
|
||||
`;
|
||||
|
||||
const SearchItem = styled.div`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 20px;
|
||||
margin-right: 50px;
|
||||
|
||||
${TextInput}, ${SelectInput} {
|
||||
height: 35px;
|
||||
}
|
||||
${TextInput} {
|
||||
padding: 0 10px;
|
||||
max-width: 400px;
|
||||
}
|
||||
`;
|
||||
166
src/components/IndexManage/DecoContent.js
Normal file
166
src/components/IndexManage/DecoContent.js
Normal file
@@ -0,0 +1,166 @@
|
||||
import { styled } from 'styled-components';
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
import DecoSearchBar from '../../components/IndexManage/DecoSearchBar';
|
||||
import Button from '../../components/common/button/Button';
|
||||
|
||||
import { TableStyle, TableInfo, ListOption } from '../../styles/Components';
|
||||
import { ClothesIndexExport, ClothesIndexView } from '../../apis';
|
||||
|
||||
const DecoContent = () => {
|
||||
const token = sessionStorage.getItem('token');
|
||||
let d = new Date();
|
||||
const START_DATE = new Date(new Date(d.setDate(d.getDate() - 1)).setHours(0, 0, 0, 0));
|
||||
const END_DATE = new Date();
|
||||
|
||||
const [searchKey, setSearchKey] = useState('');
|
||||
const [sendDate, setSendDate] = useState(START_DATE);
|
||||
const [finishDate, setFinishDate] = useState(END_DATE);
|
||||
|
||||
const [dataList, setDataList] = useState([]);
|
||||
useEffect(() => {
|
||||
fetchData('', START_DATE, END_DATE);
|
||||
}, []);
|
||||
|
||||
// console.log(dataList);
|
||||
|
||||
const fetchData = async (data, startDate, endDate) => {
|
||||
const startDateToLocal =
|
||||
startDate.getFullYear() +
|
||||
'-' +
|
||||
(startDate.getMonth() + 1 < 9 ? '0' + (startDate.getMonth() + 1) : startDate.getMonth() + 1) +
|
||||
'-' +
|
||||
(startDate.getDate() < 9 ? '0' + startDate.getDate() : startDate.getDate());
|
||||
|
||||
const endDateToLocal =
|
||||
endDate.getFullYear() +
|
||||
'-' +
|
||||
(endDate.getMonth() + 1 < 9 ? '0' + (endDate.getMonth() + 1) : endDate.getMonth() + 1) +
|
||||
'-' +
|
||||
(endDate.getDate() < 9 ? '0' + endDate.getDate() : endDate.getDate());
|
||||
|
||||
setDataList(await ClothesIndexView(token, data, startDateToLocal, endDateToLocal));
|
||||
|
||||
setSearchKey(data);
|
||||
setSendDate(startDateToLocal);
|
||||
setFinishDate(endDateToLocal);
|
||||
};
|
||||
|
||||
// 엑셀 다운로드
|
||||
const handleXlsxExport = () => {
|
||||
const fileName = 'Caliverse_Deco_Index.xlsx';
|
||||
ClothesIndexExport(token, fileName, searchKey, sendDate, finishDate);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<DecoSearchBar fetchData={fetchData} />
|
||||
<TableInfo>
|
||||
<ListOption>
|
||||
<Button theme="line" text="엑셀 다운로드" handleClick={handleXlsxExport} />
|
||||
</ListOption>
|
||||
</TableInfo>
|
||||
<TableWrapper>
|
||||
<EconomicTable2>
|
||||
<thead>
|
||||
<tr>
|
||||
<th width="240">일자</th>
|
||||
<th rowSpan="2" width="160">
|
||||
2023-08-07
|
||||
</th>
|
||||
<th rowSpan="2" width="160">
|
||||
2023-08-08
|
||||
</th>
|
||||
<th rowSpan="2" width="160">
|
||||
2023-08-09
|
||||
</th>
|
||||
<th rowSpan="2" width="160">
|
||||
2023-08-10
|
||||
</th>
|
||||
<th rowSpan="2" width="160"></th>
|
||||
<th rowSpan="2" width="160"></th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>대상</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<TableTitle>(인스턴스 ID) 방문자 수</TableTitle>
|
||||
<TableData>95</TableData>
|
||||
<TableData>240</TableData>
|
||||
<TableData>240</TableData>
|
||||
<TableData>240</TableData>
|
||||
<TableData $state="blank"></TableData>
|
||||
<TableData $state="blank"></TableData>
|
||||
</tr>
|
||||
{/* {mokupData.map((data, index) => (
|
||||
<Fragment key={index}>
|
||||
<tr>
|
||||
<td>{data.date}</td>
|
||||
<td>{data.name}</td>
|
||||
<td>{data.trader}</td>
|
||||
<td>{data.id}</td>
|
||||
<td>{data.key}</td>
|
||||
</tr>
|
||||
</Fragment>
|
||||
))} */}
|
||||
</tbody>
|
||||
</EconomicTable2>
|
||||
</TableWrapper>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default DecoContent;
|
||||
|
||||
const TableWrapper = styled.div`
|
||||
width: 100%;
|
||||
min-width: 680px;
|
||||
overflow: auto;
|
||||
&::-webkit-scrollbar {
|
||||
height: 4px;
|
||||
}
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background: #666666;
|
||||
}
|
||||
&::-webkit-scrollbar-track {
|
||||
background: #d9d9d9;
|
||||
}
|
||||
${TableStyle} {
|
||||
width: 100%;
|
||||
min-width: 900px;
|
||||
th {
|
||||
&.cell-nru {
|
||||
background: #f0f0f0;
|
||||
border-left: 1px solid #aaa;
|
||||
border-right: 1px solid #aaa;
|
||||
}
|
||||
}
|
||||
td {
|
||||
&.blank {
|
||||
background: #f9f9f9;
|
||||
}
|
||||
&.cell-nru {
|
||||
background: #fafafa;
|
||||
border-left: 1px solid #aaa;
|
||||
border-right: 1px solid #aaa;
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const TableData = styled.td`
|
||||
background: ${props => (props.$state === 'danger' ? '#d60000' : props.$state === 'blank' ? '#F9F9F9' : 'transparent')};
|
||||
color: ${props => (props.$state === 'danger' ? '#fff' : '#2c2c2c')};
|
||||
`;
|
||||
|
||||
const TableTitle = styled.td`
|
||||
text-align: center;
|
||||
`;
|
||||
|
||||
const EconomicTable2 = styled(TableStyle)`
|
||||
${TableData} {
|
||||
text-align: left;
|
||||
}
|
||||
`;
|
||||
116
src/components/IndexManage/DecoSearchBar.js
Normal file
116
src/components/IndexManage/DecoSearchBar.js
Normal file
@@ -0,0 +1,116 @@
|
||||
import { useState } from 'react';
|
||||
import { styled } from 'styled-components';
|
||||
import Button from '../../components/common/button/Button';
|
||||
|
||||
import DatePickerComponent from '../common/Date/DatePickerComponent';
|
||||
|
||||
import { FormWrapper, InputLabel, TextInput, SelectInput, BtnWrapper, InputGroup, DatePickerWrapper } from '../../styles/Components';
|
||||
|
||||
const DecoSearchBar = ({ fetchData }) => {
|
||||
let d = new Date();
|
||||
const START_DATE = new Date(new Date(d.setDate(d.getDate() - 1)).setHours(0, 0, 0, 0));
|
||||
const END_DATE = new Date();
|
||||
|
||||
const [resultData, setResultData] = useState({
|
||||
search_key: '',
|
||||
send_dt: START_DATE,
|
||||
end_dt: END_DATE,
|
||||
});
|
||||
|
||||
// 발송 날짜 세팅 로직
|
||||
const handleSelectedDate = data => {
|
||||
const sendDate = new Date(data);
|
||||
const resultSendData = new Date(sendDate.getFullYear(), sendDate.getMonth(), sendDate.getDate());
|
||||
|
||||
setResultData({ ...resultData, send_dt: resultSendData });
|
||||
};
|
||||
|
||||
// 발송 날짜 세팅 로직
|
||||
const handleEndDate = data => {
|
||||
const endDate = new Date(data);
|
||||
const resultSendData = new Date(endDate.getFullYear(), endDate.getMonth(), endDate.getDate());
|
||||
|
||||
setResultData({ ...resultData, end_dt: resultSendData });
|
||||
};
|
||||
|
||||
const handleReset = e => {
|
||||
e.preventDefault();
|
||||
setResultData({ search_key: '', send_dt: START_DATE, end_dt: END_DATE });
|
||||
|
||||
fetchData('', START_DATE, END_DATE);
|
||||
};
|
||||
return (
|
||||
<>
|
||||
<FormWrapper>
|
||||
<SearchbarStyle>
|
||||
<SearchItem>
|
||||
<InputLabel>아이템 ID</InputLabel>
|
||||
<TextInput type="text" placeholder="ID 입력" onChange={e => setResultData({ ...resultData, search_key: e.target.value })} />
|
||||
</SearchItem>
|
||||
<SearchItem>
|
||||
<InputLabel>집계 기간</InputLabel>
|
||||
<InputGroup>
|
||||
<DatePickerWrapper>
|
||||
<DatePickerComponent
|
||||
name="시작 일자" selectedDate={resultData.send_dt}
|
||||
handleSelectedDate={data => handleSelectedDate(data)}
|
||||
maxDate={new Date()} />
|
||||
<span>~</span>
|
||||
<DatePickerComponent
|
||||
name="종료 일자" selectedDate={resultData.end_dt}
|
||||
handleSelectedDate={data => handleEndDate(data)}
|
||||
pastDate = {resultData.send_dt}
|
||||
maxDate={new Date()} />
|
||||
</DatePickerWrapper>
|
||||
</InputGroup>
|
||||
</SearchItem>
|
||||
<BtnWrapper $gap="8px">
|
||||
<Button
|
||||
theme="reset"
|
||||
handleClick={e => {
|
||||
handleReset(e);
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
theme="search"
|
||||
text="집계"
|
||||
handleClick={e => {
|
||||
e.preventDefault();
|
||||
fetchData(resultData.search_key, resultData.send_dt, resultData.end_dt);
|
||||
}}
|
||||
/>
|
||||
</BtnWrapper>
|
||||
</SearchbarStyle>
|
||||
</FormWrapper>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default DecoSearchBar;
|
||||
|
||||
const SearchbarStyle = styled.div`
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 20px 0;
|
||||
font-size: 14px;
|
||||
padding: 20px;
|
||||
border-top: 1px solid #000;
|
||||
border-bottom: 1px solid #000;
|
||||
margin-bottom: 40px;
|
||||
`;
|
||||
|
||||
const SearchItem = styled.div`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 20px;
|
||||
margin-right: 50px;
|
||||
|
||||
${TextInput}, ${SelectInput} {
|
||||
height: 35px;
|
||||
}
|
||||
${TextInput} {
|
||||
padding: 0 10px;
|
||||
max-width: 400px;
|
||||
}
|
||||
`;
|
||||
166
src/components/IndexManage/InstanceContent.js
Normal file
166
src/components/IndexManage/InstanceContent.js
Normal file
@@ -0,0 +1,166 @@
|
||||
import { styled } from 'styled-components';
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
import { TableStyle, TableInfo, ListOption } from '../../styles/Components';
|
||||
|
||||
import Button from '../../components/common/button/Button';
|
||||
import InstanceSearchBar from '../../components/IndexManage/InstanceSearchBar';
|
||||
import { InstanceIndexExport, InstanceIndexView } from '../../apis';
|
||||
|
||||
const InstanceContent = () => {
|
||||
const token = sessionStorage.getItem('token');
|
||||
let d = new Date();
|
||||
const START_DATE = new Date(new Date(d.setDate(d.getDate() - 1)).setHours(0, 0, 0, 0));
|
||||
const END_DATE = new Date();
|
||||
|
||||
const [searchKey, setSearchKey] = useState('');
|
||||
const [sendDate, setSendDate] = useState(START_DATE);
|
||||
const [finishDate, setFinishDate] = useState(END_DATE);
|
||||
|
||||
const [dataList, setDataList] = useState([]);
|
||||
useEffect(() => {
|
||||
fetchData('', START_DATE, END_DATE);
|
||||
}, []);
|
||||
|
||||
// console.log(dataList);
|
||||
|
||||
const fetchData = async (data, startDate, endDate) => {
|
||||
const startDateToLocal =
|
||||
startDate.getFullYear() +
|
||||
'-' +
|
||||
(startDate.getMonth() + 1 < 9 ? '0' + (startDate.getMonth() + 1) : startDate.getMonth() + 1) +
|
||||
'-' +
|
||||
(startDate.getDate() < 9 ? '0' + startDate.getDate() : startDate.getDate());
|
||||
|
||||
const endDateToLocal =
|
||||
endDate.getFullYear() +
|
||||
'-' +
|
||||
(endDate.getMonth() + 1 < 9 ? '0' + (endDate.getMonth() + 1) : endDate.getMonth() + 1) +
|
||||
'-' +
|
||||
(endDate.getDate() < 9 ? '0' + endDate.getDate() : endDate.getDate());
|
||||
|
||||
setDataList(await InstanceIndexView(token, data, startDateToLocal, endDateToLocal));
|
||||
|
||||
setSearchKey(data);
|
||||
setSendDate(startDateToLocal);
|
||||
setFinishDate(endDateToLocal);
|
||||
};
|
||||
|
||||
// 엑셀 다운로드
|
||||
const handleXlsxExport = () => {
|
||||
const fileName = 'Caliverse_Instance_Index.xlsx';
|
||||
InstanceIndexExport(token, fileName, searchKey, sendDate, finishDate);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<InstanceSearchBar fetchData={fetchData} />
|
||||
<TableInfo>
|
||||
<ListOption>
|
||||
<Button theme="line" text="엑셀 다운로드" handleClick={handleXlsxExport} />
|
||||
</ListOption>
|
||||
</TableInfo>
|
||||
<TableWrapper>
|
||||
<EconomicTable2>
|
||||
<thead>
|
||||
<tr>
|
||||
<th width="240">일자</th>
|
||||
<th rowSpan="2" width="160">
|
||||
2023-08-07
|
||||
</th>
|
||||
<th rowSpan="2" width="160">
|
||||
2023-08-08
|
||||
</th>
|
||||
<th rowSpan="2" width="160">
|
||||
2023-08-09
|
||||
</th>
|
||||
<th rowSpan="2" width="160">
|
||||
2023-08-10
|
||||
</th>
|
||||
<th rowSpan="2" width="160"></th>
|
||||
<th rowSpan="2" width="160"></th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>대상</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<TableTitle>(인스턴스 ID) 방문자 수</TableTitle>
|
||||
<TableData>95</TableData>
|
||||
<TableData>240</TableData>
|
||||
<TableData>240</TableData>
|
||||
<TableData>240</TableData>
|
||||
<TableData $state="blank"></TableData>
|
||||
<TableData $state="blank"></TableData>
|
||||
</tr>
|
||||
{/* {mokupData.map((data, index) => (
|
||||
<Fragment key={index}>
|
||||
<tr>
|
||||
<td>{data.date}</td>
|
||||
<td>{data.name}</td>
|
||||
<td>{data.trader}</td>
|
||||
<td>{data.id}</td>
|
||||
<td>{data.key}</td>
|
||||
</tr>
|
||||
</Fragment>
|
||||
))} */}
|
||||
</tbody>
|
||||
</EconomicTable2>
|
||||
</TableWrapper>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default InstanceContent;
|
||||
|
||||
const TableWrapper = styled.div`
|
||||
width: 100%;
|
||||
min-width: 680px;
|
||||
overflow: auto;
|
||||
&::-webkit-scrollbar {
|
||||
height: 4px;
|
||||
}
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background: #666666;
|
||||
}
|
||||
&::-webkit-scrollbar-track {
|
||||
background: #d9d9d9;
|
||||
}
|
||||
${TableStyle} {
|
||||
width: 100%;
|
||||
min-width: 900px;
|
||||
th {
|
||||
&.cell-nru {
|
||||
background: #f0f0f0;
|
||||
border-left: 1px solid #aaa;
|
||||
border-right: 1px solid #aaa;
|
||||
}
|
||||
}
|
||||
td {
|
||||
&.blank {
|
||||
background: #f9f9f9;
|
||||
}
|
||||
&.cell-nru {
|
||||
background: #fafafa;
|
||||
border-left: 1px solid #aaa;
|
||||
border-right: 1px solid #aaa;
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const TableData = styled.td`
|
||||
background: ${props => (props.$state === 'danger' ? '#d60000' : props.$state === 'blank' ? '#F9F9F9' : 'transparent')};
|
||||
color: ${props => (props.$state === 'danger' ? '#fff' : '#2c2c2c')};
|
||||
`;
|
||||
|
||||
const TableTitle = styled.td`
|
||||
text-align: center;
|
||||
`;
|
||||
|
||||
const EconomicTable2 = styled(TableStyle)`
|
||||
${TableData} {
|
||||
text-align: left;
|
||||
}
|
||||
`;
|
||||
117
src/components/IndexManage/InstanceSearchBar.js
Normal file
117
src/components/IndexManage/InstanceSearchBar.js
Normal file
@@ -0,0 +1,117 @@
|
||||
import { useState } from 'react';
|
||||
import { styled } from 'styled-components';
|
||||
import Button from '../../components/common/button/Button';
|
||||
|
||||
import DatePickerComponent from '../common/Date/DatePickerComponent';
|
||||
|
||||
import { FormWrapper, InputLabel, TextInput, SelectInput, BtnWrapper, InputGroup, DatePickerWrapper } from '../../styles/Components';
|
||||
|
||||
const InstanceSearchBar = ({ fetchData }) => {
|
||||
let d = new Date();
|
||||
const START_DATE = new Date(new Date(d.setDate(d.getDate() - 1)).setHours(0, 0, 0, 0));
|
||||
const END_DATE = new Date();
|
||||
|
||||
const [resultData, setResultData] = useState({
|
||||
search_key: '',
|
||||
send_dt: START_DATE,
|
||||
end_dt: END_DATE,
|
||||
});
|
||||
|
||||
// 발송 날짜 세팅 로직
|
||||
const handleSelectedDate = data => {
|
||||
const sendDate = new Date(data);
|
||||
const resultSendData = new Date(sendDate.getFullYear(), sendDate.getMonth(), sendDate.getDate());
|
||||
|
||||
setResultData({ ...resultData, send_dt: resultSendData });
|
||||
};
|
||||
|
||||
// 발송 날짜 세팅 로직
|
||||
const handleEndDate = data => {
|
||||
const endDate = new Date(data);
|
||||
const resultSendData = new Date(endDate.getFullYear(), endDate.getMonth(), endDate.getDate());
|
||||
|
||||
setResultData({ ...resultData, end_dt: resultSendData });
|
||||
};
|
||||
|
||||
const handleReset = e => {
|
||||
e.preventDefault();
|
||||
setResultData({ search_key: '', send_dt: START_DATE, end_dt: END_DATE });
|
||||
|
||||
fetchData('', START_DATE, END_DATE);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<FormWrapper>
|
||||
<SearchbarStyle>
|
||||
<SearchItem>
|
||||
<InputLabel>인스턴스 ID</InputLabel>
|
||||
<TextInput type="text" placeholder="ID 입력" onChange={e => setResultData({ ...resultData, search_key: e.target.value })} />
|
||||
</SearchItem>
|
||||
<SearchItem>
|
||||
<InputLabel>집계 기간</InputLabel>
|
||||
<InputGroup>
|
||||
<DatePickerWrapper>
|
||||
<DatePickerComponent
|
||||
name="시작 일자" selectedDate={resultData.send_dt}
|
||||
handleSelectedDate={data => handleSelectedDate(data)}
|
||||
maxDate={new Date()} />
|
||||
<span>~</span>
|
||||
<DatePickerComponent
|
||||
name="종료 일자" selectedDate={resultData.end_dt}
|
||||
handleSelectedDate={data => handleEndDate(data)}
|
||||
pastDate = {resultData.send_dt}
|
||||
maxDate={new Date()} />
|
||||
</DatePickerWrapper>
|
||||
</InputGroup>
|
||||
</SearchItem>
|
||||
<BtnWrapper $gap="8px">
|
||||
<Button
|
||||
theme="reset"
|
||||
handleClick={e => {
|
||||
handleReset(e);
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
theme="search"
|
||||
text="집계"
|
||||
handleClick={e => {
|
||||
e.preventDefault();
|
||||
fetchData(resultData.search_key, resultData.send_dt, resultData.end_dt);
|
||||
}}
|
||||
/>
|
||||
</BtnWrapper>
|
||||
</SearchbarStyle>
|
||||
</FormWrapper>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default InstanceSearchBar;
|
||||
|
||||
const SearchbarStyle = styled.div`
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 20px 0;
|
||||
font-size: 14px;
|
||||
padding: 20px;
|
||||
border-top: 1px solid #000;
|
||||
border-bottom: 1px solid #000;
|
||||
margin-bottom: 40px;
|
||||
`;
|
||||
|
||||
const SearchItem = styled.div`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 20px;
|
||||
margin-right: 50px;
|
||||
|
||||
${TextInput}, ${SelectInput} {
|
||||
height: 35px;
|
||||
}
|
||||
${TextInput} {
|
||||
padding: 0 10px;
|
||||
max-width: 400px;
|
||||
}
|
||||
`;
|
||||
241
src/components/IndexManage/ItemContent.js
Normal file
241
src/components/IndexManage/ItemContent.js
Normal file
@@ -0,0 +1,241 @@
|
||||
import { styled } from 'styled-components';
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
import Button from '../../components/common/button/Button';
|
||||
|
||||
import { SelectInput, TableStyle, TableInfo, ListOption, InputLabel, InputGroup } from '../../styles/Components';
|
||||
|
||||
import ItemSearchBar from '../../components/IndexManage/ItemSearchBar';
|
||||
import { ItemIndexExport, ItemIndexView } from '../../apis';
|
||||
|
||||
const ItemContent = () => {
|
||||
const token = sessionStorage.getItem('token');
|
||||
let d = new Date();
|
||||
const START_DATE = new Date(new Date(d.setDate(d.getDate() - 1)).setHours(0, 0, 0, 0));
|
||||
const END_DATE = new Date();
|
||||
|
||||
const [sendDate, setSendDate] = useState(START_DATE);
|
||||
const [finishDate, setFinishDate] = useState(END_DATE);
|
||||
|
||||
const [dataList, setDataList] = useState([]);
|
||||
useEffect(() => {
|
||||
fetchData(START_DATE, END_DATE);
|
||||
}, []);
|
||||
|
||||
// console.log(dataList);
|
||||
|
||||
const fetchData = async (startDate, endDate) => {
|
||||
const startDateToLocal =
|
||||
startDate.getFullYear() +
|
||||
'-' +
|
||||
(startDate.getMonth() + 1 < 9 ? '0' + (startDate.getMonth() + 1) : startDate.getMonth() + 1) +
|
||||
'-' +
|
||||
(startDate.getDate() < 9 ? '0' + startDate.getDate() : startDate.getDate());
|
||||
|
||||
const endDateToLocal =
|
||||
endDate.getFullYear() +
|
||||
'-' +
|
||||
(endDate.getMonth() + 1 < 9 ? '0' + (endDate.getMonth() + 1) : endDate.getMonth() + 1) +
|
||||
'-' +
|
||||
(endDate.getDate() < 9 ? '0' + endDate.getDate() : endDate.getDate());
|
||||
|
||||
setDataList(await ItemIndexView(token, startDateToLocal, endDateToLocal));
|
||||
|
||||
setSendDate(startDateToLocal);
|
||||
setFinishDate(endDateToLocal);
|
||||
};
|
||||
|
||||
// 엑셀 다운로드
|
||||
const handleXlsxExport = () => {
|
||||
const fileName = 'Caliverse_Item_Index.xlsx';
|
||||
ItemIndexExport(token, fileName, sendDate, finishDate);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<ItemSearchBar fetchData={fetchData} />
|
||||
<TableInfo2>
|
||||
<InputGroup>
|
||||
<InputLabel>아이템 코드</InputLabel>
|
||||
<SelectInput>
|
||||
<option value="">15100001</option>
|
||||
<option value="">15100001</option>
|
||||
<option value="">15100001</option>
|
||||
<option value="">15100001</option>
|
||||
</SelectInput>
|
||||
</InputGroup>
|
||||
<ListOption>
|
||||
<Button theme="line" text="엑셀 다운로드" handleClick={handleXlsxExport} />
|
||||
</ListOption>
|
||||
</TableInfo2>
|
||||
<TableWrapper>
|
||||
<EconomicTable>
|
||||
<thead>
|
||||
<tr>
|
||||
<th colSpan="2" className="text-center" width="300">
|
||||
Product
|
||||
</th>
|
||||
<th width="160">2023-08-07</th>
|
||||
<th width="160">2023-08-08</th>
|
||||
<th width="160">2023-08-09</th>
|
||||
<th width="160">2023-08-10</th>
|
||||
<th width="160">2023-08-11</th>
|
||||
<th width="160">2023-08-12</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<TableTitle colSpan="2">(Total) 15100001 생산량</TableTitle>
|
||||
<TableData>240</TableData>
|
||||
<TableData>240</TableData>
|
||||
<TableData>240</TableData>
|
||||
<TableData>240</TableData>
|
||||
<TableData $state="blank"></TableData>
|
||||
<TableData $state="blank"></TableData>
|
||||
</tr>
|
||||
<tr>
|
||||
<TableTitle colSpan="2">(Total) 15100001 소진량</TableTitle>
|
||||
<TableData>110</TableData>
|
||||
<TableData>110</TableData>
|
||||
<TableData>110</TableData>
|
||||
<TableData>110</TableData>
|
||||
<TableData $state="blank"></TableData>
|
||||
<TableData $state="blank"></TableData>
|
||||
</tr>
|
||||
<tr>
|
||||
<TableTitle colSpan="2">(Total) 15100001 보유량</TableTitle>
|
||||
<TableData>3750</TableData>
|
||||
<TableData>3750</TableData>
|
||||
<TableData>3750</TableData>
|
||||
<TableData>3750</TableData>
|
||||
<TableData $state="blank"></TableData>
|
||||
<TableData $state="blank"></TableData>
|
||||
</tr>
|
||||
<tr>
|
||||
<TableTitle rowSpan="2">GET</TableTitle>
|
||||
<TableTitle>퀘스트 보상</TableTitle>
|
||||
<TableData>70</TableData>
|
||||
<TableData>70</TableData>
|
||||
<TableData>70</TableData>
|
||||
<TableData>70</TableData>
|
||||
<TableData $state="blank"></TableData>
|
||||
<TableData $state="blank"></TableData>
|
||||
</tr>
|
||||
<tr>
|
||||
<TableTitle>시즌패스 보상</TableTitle>
|
||||
<TableData>70</TableData>
|
||||
<TableData>70</TableData>
|
||||
<TableData>70</TableData>
|
||||
<TableData>70</TableData>
|
||||
<TableData $state="blank"></TableData>
|
||||
<TableData $state="blank"></TableData>
|
||||
</tr>
|
||||
<tr>
|
||||
<TableTitle rowSpan="2">USE</TableTitle>
|
||||
<TableTitle>퀘스트 보상</TableTitle>
|
||||
<TableData>50</TableData>
|
||||
<TableData>50</TableData>
|
||||
<TableData>50</TableData>
|
||||
<TableData>50</TableData>
|
||||
<TableData $state="blank"></TableData>
|
||||
<TableData $state="blank"></TableData>
|
||||
</tr>
|
||||
<tr>
|
||||
<TableTitle>시즌패스 보상</TableTitle>
|
||||
<TableData>50</TableData>
|
||||
<TableData>50</TableData>
|
||||
<TableData>50</TableData>
|
||||
<TableData>50</TableData>
|
||||
<TableData $state="blank"></TableData>
|
||||
<TableData $state="blank"></TableData>
|
||||
</tr>
|
||||
|
||||
{/* {mokupData.map((data, index) => (
|
||||
<Fragment key={index}>
|
||||
<tr>
|
||||
<td>{data.date}</td>
|
||||
<td>{data.name}</td>
|
||||
<td>{data.trader}</td>
|
||||
<td>{data.id}</td>
|
||||
<td>{data.key}</td>
|
||||
</tr>
|
||||
</Fragment>
|
||||
))} */}
|
||||
</tbody>
|
||||
</EconomicTable>
|
||||
</TableWrapper>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default ItemContent;
|
||||
|
||||
const TableWrapper = styled.div`
|
||||
width: 100%;
|
||||
min-width: 680px;
|
||||
overflow: auto;
|
||||
&::-webkit-scrollbar {
|
||||
height: 4px;
|
||||
}
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background: #666666;
|
||||
}
|
||||
&::-webkit-scrollbar-track {
|
||||
background: #d9d9d9;
|
||||
}
|
||||
${TableStyle} {
|
||||
width: 100%;
|
||||
min-width: 900px;
|
||||
th {
|
||||
&.cell-nru {
|
||||
background: #f0f0f0;
|
||||
border-left: 1px solid #aaa;
|
||||
border-right: 1px solid #aaa;
|
||||
}
|
||||
}
|
||||
td {
|
||||
&.blank {
|
||||
background: #f9f9f9;
|
||||
}
|
||||
&.cell-nru {
|
||||
background: #fafafa;
|
||||
border-left: 1px solid #aaa;
|
||||
border-right: 1px solid #aaa;
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const TableInfo2 = styled(TableInfo)`
|
||||
justify-content: space-between;
|
||||
${InputLabel} {
|
||||
font-size: 12px;
|
||||
}
|
||||
${SelectInput} {
|
||||
width: auto;
|
||||
min-width: 100px;
|
||||
height: 24px;
|
||||
}
|
||||
`;
|
||||
|
||||
const TableData = styled.td`
|
||||
background: ${props => (props.$state === 'danger' ? '#d60000' : props.$state === 'blank' ? '#F9F9F9' : 'transparent')};
|
||||
color: ${props => (props.$state === 'danger' ? '#fff' : '#2c2c2c')};
|
||||
`;
|
||||
|
||||
const TableTitle = styled.td`
|
||||
text-align: center;
|
||||
`;
|
||||
|
||||
const EconomicTable = styled(TableStyle)`
|
||||
${TableData} {
|
||||
text-align: left;
|
||||
}
|
||||
tbody {
|
||||
tr:nth-child(1),
|
||||
tr:nth-child(2),
|
||||
tr:nth-child(3) {
|
||||
background: #f5fcff;
|
||||
}
|
||||
}
|
||||
`;
|
||||
111
src/components/IndexManage/ItemSearchBar.js
Normal file
111
src/components/IndexManage/ItemSearchBar.js
Normal file
@@ -0,0 +1,111 @@
|
||||
import { useState } from 'react';
|
||||
import { styled } from 'styled-components';
|
||||
import Button from '../../components/common/button/Button';
|
||||
import DatePickerComponent from '../common/Date/DatePickerComponent';
|
||||
|
||||
import { FormWrapper, InputLabel, TextInput, SelectInput, BtnWrapper, InputGroup, DatePickerWrapper } from '../../styles/Components';
|
||||
|
||||
const ItemSearchBar = ({ fetchData }) => {
|
||||
let d = new Date();
|
||||
const START_DATE = new Date(new Date(d.setDate(d.getDate() - 1)).setHours(0, 0, 0, 0));
|
||||
const END_DATE = new Date();
|
||||
|
||||
const [resultData, setResultData] = useState({
|
||||
send_dt: START_DATE,
|
||||
end_dt: END_DATE,
|
||||
});
|
||||
|
||||
// 발송 날짜 세팅 로직
|
||||
const handleSelectedDate = data => {
|
||||
const sendDate = new Date(data);
|
||||
const resultSendData = new Date(sendDate.getFullYear(), sendDate.getMonth(), sendDate.getDate());
|
||||
|
||||
setResultData({ ...resultData, send_dt: resultSendData });
|
||||
};
|
||||
|
||||
// 발송 날짜 세팅 로직
|
||||
const handleEndDate = data => {
|
||||
const endDate = new Date(data);
|
||||
const resultSendData = new Date(endDate.getFullYear(), endDate.getMonth(), endDate.getDate());
|
||||
|
||||
setResultData({ ...resultData, end_dt: resultSendData });
|
||||
};
|
||||
|
||||
const handleReset = e => {
|
||||
e.preventDefault();
|
||||
setResultData({ send_dt: START_DATE, end_dt: END_DATE });
|
||||
|
||||
fetchData(START_DATE, END_DATE);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<FormWrapper>
|
||||
<SearchbarStyle>
|
||||
<SearchItem>
|
||||
<InputLabel>집계 기간</InputLabel>
|
||||
<InputGroup>
|
||||
<DatePickerWrapper>
|
||||
<DatePickerComponent
|
||||
name="시작 일자" selectedDate={resultData.send_dt}
|
||||
handleSelectedDate={data => handleSelectedDate(data)}
|
||||
maxDate={new Date()} />
|
||||
<span>~</span>
|
||||
<DatePickerComponent
|
||||
name="종료 일자" selectedDate={resultData.end_dt}
|
||||
handleSelectedDate={data => handleEndDate(data)}
|
||||
pastDate = {resultData.send_dt}
|
||||
maxDate={new Date()} />
|
||||
</DatePickerWrapper>
|
||||
</InputGroup>
|
||||
</SearchItem>
|
||||
<BtnWrapper $gap="8px">
|
||||
<Button
|
||||
theme="reset"
|
||||
handleClick={e => {
|
||||
handleReset(e);
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
theme="search"
|
||||
text="집계"
|
||||
handleClick={e => {
|
||||
e.preventDefault();
|
||||
fetchData(resultData.send_dt, resultData.end_dt);
|
||||
}}
|
||||
/>
|
||||
</BtnWrapper>
|
||||
</SearchbarStyle>
|
||||
</FormWrapper>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default ItemSearchBar;
|
||||
|
||||
const SearchbarStyle = styled.div`
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 20px 0;
|
||||
font-size: 14px;
|
||||
padding: 20px;
|
||||
border-top: 1px solid #000;
|
||||
border-bottom: 1px solid #000;
|
||||
margin-bottom: 40px;
|
||||
`;
|
||||
|
||||
const SearchItem = styled.div`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 20px;
|
||||
margin-right: 50px;
|
||||
|
||||
${TextInput}, ${SelectInput} {
|
||||
height: 35px;
|
||||
}
|
||||
${TextInput} {
|
||||
padding: 0 10px;
|
||||
max-width: 400px;
|
||||
}
|
||||
`;
|
||||
112
src/components/IndexManage/PlayTimeContent.js
Normal file
112
src/components/IndexManage/PlayTimeContent.js
Normal file
@@ -0,0 +1,112 @@
|
||||
import { Fragment, useEffect, useState } from 'react';
|
||||
|
||||
import Button from '../../components/common/button/Button';
|
||||
|
||||
import { TableStyle, TableInfo, ListOption, IndexTableWrap } from '../../styles/Components';
|
||||
import { PlayTimeSearchBar } from '../../components/IndexManage/index';
|
||||
import { PlaytimeIndexExport, PlaytimeIndexView } from '../../apis';
|
||||
|
||||
const PlayTimeContent = () => {
|
||||
const token = sessionStorage.getItem('token');
|
||||
let d = new Date();
|
||||
const START_DATE = new Date(new Date(d.setDate(d.getDate() - 1)).setHours(0, 0, 0, 0));
|
||||
const END_DATE = new Date();
|
||||
|
||||
const [dataList, setDataList] = useState([]);
|
||||
const [resultData, setResultData] = useState([]);
|
||||
|
||||
const [sendDate, setSendDate] = useState(START_DATE);
|
||||
const [finishDate, setFinishDate] = useState(END_DATE);
|
||||
|
||||
useEffect(() => {
|
||||
fetchData(START_DATE, END_DATE);
|
||||
}, []);
|
||||
|
||||
// 이용자 지표 데이터
|
||||
const fetchData = async (startDate, endDate) => {
|
||||
const startDateToLocal =
|
||||
startDate.getFullYear() +
|
||||
'-' +
|
||||
(startDate.getMonth() + 1 < 9 ? '0' + (startDate.getMonth() + 1) : startDate.getMonth() + 1) +
|
||||
'-' +
|
||||
(startDate.getDate() < 9 ? '0' + startDate.getDate() : startDate.getDate());
|
||||
|
||||
const endDateToLocal =
|
||||
endDate.getFullYear() +
|
||||
'-' +
|
||||
(endDate.getMonth() + 1 < 9 ? '0' + (endDate.getMonth() + 1) : endDate.getMonth() + 1) +
|
||||
'-' +
|
||||
(endDate.getDate() < 9 ? '0' + endDate.getDate() : endDate.getDate());
|
||||
|
||||
setDataList(await PlaytimeIndexView(token, startDateToLocal, endDateToLocal));
|
||||
|
||||
setSendDate(startDateToLocal);
|
||||
setFinishDate(endDateToLocal);
|
||||
};
|
||||
|
||||
// 검색 함수
|
||||
const handleSearch = (send_dt, end_dt) => {
|
||||
fetchData(send_dt, end_dt);
|
||||
};
|
||||
|
||||
// 엑셀 다운로드
|
||||
const handleXlsxExport = () => {
|
||||
const fileName = 'Caliverse_PlayTime_Index.xlsx';
|
||||
PlaytimeIndexExport(token, fileName, sendDate, finishDate);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<PlayTimeSearchBar setResultData={setResultData} resultData={resultData} handleSearch={handleSearch} fetchData={fetchData} />
|
||||
<TableInfo>
|
||||
<ListOption>
|
||||
<Button theme="line" text="엑셀 다운로드" handleClick={handleXlsxExport} />
|
||||
</ListOption>
|
||||
</TableInfo>
|
||||
<IndexTableWrap>
|
||||
<TableStyle>
|
||||
<caption></caption>
|
||||
<thead>
|
||||
<tr>
|
||||
<th rowSpan="2" width="140">
|
||||
일자
|
||||
</th>
|
||||
<th colSpan="4" width="520">
|
||||
유저수
|
||||
</th>
|
||||
<th rowSpan="2" width="160">
|
||||
총 누적 플레이타임(분)
|
||||
</th>
|
||||
<th rowSpan="2" width="160">
|
||||
1인당 평균 플레이타임(분)
|
||||
</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>30분 이내</th>
|
||||
<th>30분 ~ 1시간</th>
|
||||
<th>1시간 ~ 3시간</th>
|
||||
<th>3시간 이상</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{dataList.playtime &&
|
||||
dataList.playtime.map(time => (
|
||||
<tr key={time.date}>
|
||||
<td>{time.date}</td>
|
||||
{time.user_cnt.map((cnt, index) => (
|
||||
<td className="text-left" key={index}>
|
||||
{cnt}
|
||||
</td>
|
||||
))}
|
||||
<td className="text-left">{time.total_time}</td>
|
||||
<td className="text-left">{time.average_time}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</TableStyle>
|
||||
</IndexTableWrap>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default PlayTimeContent;
|
||||
122
src/components/IndexManage/PlayTimeSearchBar.js
Normal file
122
src/components/IndexManage/PlayTimeSearchBar.js
Normal file
@@ -0,0 +1,122 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { styled } from 'styled-components';
|
||||
import Button from '../../components/common/button/Button';
|
||||
|
||||
import 'react-datepicker/dist/react-datepicker.css';
|
||||
import DatePickerComponent from '../common/Date/DatePickerComponent';
|
||||
|
||||
import { FormWrapper, InputLabel, TextInput, SelectInput, BtnWrapper, InputGroup, DatePickerWrapper } from '../../styles/Components';
|
||||
|
||||
const PlayTimeSearchBar = ({ resultData, setResultData, handleSearch, fetchData }) => {
|
||||
// 초기 날짜 세팅
|
||||
let d = new Date();
|
||||
const START_DATE = new Date(new Date(d.setDate(d.getDate() - 1)).setHours(0, 0, 0, 0));
|
||||
const END_DATE = new Date();
|
||||
|
||||
// resultData에 임의 날짜 넣어주기
|
||||
useEffect(() => {
|
||||
setResultData({
|
||||
send_dt: START_DATE,
|
||||
end_dt: END_DATE,
|
||||
});
|
||||
}, []);
|
||||
|
||||
const searchType = [
|
||||
{ name: '전체', value: 'ALL' },
|
||||
{ name: '한국어', value: 'KR' },
|
||||
{ name: '영어', value: 'EN' },
|
||||
{ name: '일본어', value: 'JP' },
|
||||
{ name: '태국어', value: 'TH' },
|
||||
];
|
||||
|
||||
// 발송 날짜 세팅 로직
|
||||
const handleSelectedDate = data => {
|
||||
const sendDate = new Date(data);
|
||||
const resultSendData = new Date(sendDate.getFullYear(), sendDate.getMonth(), sendDate.getDate());
|
||||
|
||||
setResultData({ ...resultData, send_dt: resultSendData });
|
||||
};
|
||||
|
||||
// 발송 날짜 세팅 로직
|
||||
const handleEndDate = data => {
|
||||
const endDate = new Date(data);
|
||||
const resultSendData = new Date(endDate.getFullYear(), endDate.getMonth(), endDate.getDate());
|
||||
|
||||
setResultData({ ...resultData, end_dt: resultSendData });
|
||||
};
|
||||
|
||||
const handleReset = e => {
|
||||
e.preventDefault();
|
||||
setResultData({ send_dt: START_DATE, end_dt: END_DATE });
|
||||
|
||||
fetchData(START_DATE, END_DATE);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<FormWrapper>
|
||||
<SearchbarStyle>
|
||||
<SearchItem>
|
||||
<InputLabel>집계 기준일</InputLabel>
|
||||
<InputGroup>
|
||||
<DatePickerWrapper>
|
||||
<DatePickerComponent
|
||||
name="시작 일자"
|
||||
selectedDate={resultData.send_dt}
|
||||
handleSelectedDate={data => handleSelectedDate(data)}
|
||||
maxDate={new Date()} />
|
||||
<span>~</span>
|
||||
<DatePickerComponent
|
||||
name="종료 일자"
|
||||
selectedDate={resultData.end_dt}
|
||||
handleSelectedDate={data => handleEndDate(data)}
|
||||
pastDate = {resultData.send_dt}
|
||||
maxDate={new Date()} />
|
||||
</DatePickerWrapper>
|
||||
</InputGroup>
|
||||
</SearchItem>
|
||||
<BtnWrapper $gap="8px">
|
||||
<Button theme="reset" handleClick={handleReset} />
|
||||
<Button
|
||||
theme="search"
|
||||
text="검색"
|
||||
handleClick={e => {
|
||||
e.preventDefault();
|
||||
handleSearch(resultData.send_dt, resultData.end_dt);
|
||||
}}
|
||||
/>
|
||||
</BtnWrapper>
|
||||
</SearchbarStyle>
|
||||
</FormWrapper>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default PlayTimeSearchBar;
|
||||
|
||||
const SearchbarStyle = styled.div`
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 20px 0;
|
||||
font-size: 14px;
|
||||
padding: 20px;
|
||||
border-top: 1px solid #000;
|
||||
border-bottom: 1px solid #000;
|
||||
margin-bottom: 40px;
|
||||
`;
|
||||
|
||||
const SearchItem = styled.div`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 20px;
|
||||
margin-right: 50px;
|
||||
|
||||
${TextInput}, ${SelectInput} {
|
||||
height: 35px;
|
||||
}
|
||||
${TextInput} {
|
||||
padding: 0 10px;
|
||||
max-width: 400px;
|
||||
}
|
||||
`;
|
||||
109
src/components/IndexManage/RetentionContent.js
Normal file
109
src/components/IndexManage/RetentionContent.js
Normal file
@@ -0,0 +1,109 @@
|
||||
import { Fragment, useEffect, useState } from 'react';
|
||||
|
||||
import Button from '../../components/common/button/Button';
|
||||
|
||||
import { TableStyle, TableInfo, ListOption, IndexTableWrap } from '../../styles/Components';
|
||||
import { RetentionSearchBar } from '../../components/IndexManage/index';
|
||||
import { RetentionIndexExport, RetentionIndexView } from '../../apis';
|
||||
|
||||
const RetentionContent = () => {
|
||||
const token = sessionStorage.getItem('token');
|
||||
let d = new Date();
|
||||
const START_DATE = new Date(new Date(d.setDate(d.getDate() - 1)).setHours(0, 0, 0, 0));
|
||||
const END_DATE = new Date(new Date(d.setDate(d.getDate() - 1)).setHours(24, 0, 0, 0));
|
||||
|
||||
const [dataList, setDataList] = useState([]);
|
||||
const [resultData, setResultData] = useState([]);
|
||||
const [retentionData, setRetention] = useState(1);
|
||||
|
||||
const [sendDate, setSendDate] = useState(START_DATE);
|
||||
const [finishDate, setFinishDate] = useState(END_DATE);
|
||||
const [excelBtn, setExcelBtn] = useState(true); //true 시 비활성화
|
||||
|
||||
useEffect(() => {
|
||||
fetchData(START_DATE, END_DATE);
|
||||
}, []);
|
||||
|
||||
// Retention 지표 데이터
|
||||
const fetchData = async (startDate, endDate) => {
|
||||
const startDateToLocal =
|
||||
startDate.getFullYear() +
|
||||
'-' +
|
||||
(startDate.getMonth() + 1 < 9 ? '0' + (startDate.getMonth() + 1) : startDate.getMonth() + 1) +
|
||||
'-' +
|
||||
(startDate.getDate() < 9 ? '0' + startDate.getDate() : startDate.getDate());
|
||||
|
||||
const endDateToLocal =
|
||||
endDate.getFullYear() +
|
||||
'-' +
|
||||
(endDate.getMonth() + 1 < 9 ? '0' + (endDate.getMonth() + 1) : endDate.getMonth() + 1) +
|
||||
'-' +
|
||||
(endDate.getDate() < 9 ? '0' + endDate.getDate() : endDate.getDate());
|
||||
|
||||
setDataList(await RetentionIndexView(token, startDateToLocal, endDateToLocal));
|
||||
|
||||
console.log(dataList);
|
||||
|
||||
setSendDate(startDateToLocal);
|
||||
setFinishDate(endDateToLocal);
|
||||
};
|
||||
|
||||
// 검색 함수
|
||||
const handleSearch = (send_dt, end_dt) => {
|
||||
fetchData(send_dt, end_dt);
|
||||
setRetention(resultData.retention);
|
||||
};
|
||||
|
||||
// 엑셀 다운로드
|
||||
const handleXlsxExport = () => {
|
||||
const fileName = 'Caliverse_Retention_Index.xlsx';
|
||||
|
||||
if(!excelBtn){
|
||||
RetentionIndexExport(token, fileName, sendDate, finishDate);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<RetentionSearchBar setResultData={setResultData} resultData={resultData}
|
||||
handleSearch={handleSearch} fetchData={fetchData} setRetention={setRetention} setExcelBtn={setExcelBtn} />
|
||||
<TableInfo>
|
||||
<ListOption>
|
||||
<Button
|
||||
theme={excelBtn === true ? "disable" : "line"}
|
||||
text="엑셀 다운로드"
|
||||
disabled={handleXlsxExport}
|
||||
handleClick={handleXlsxExport} />
|
||||
</ListOption>
|
||||
</TableInfo>
|
||||
<IndexTableWrap>
|
||||
<TableStyle>
|
||||
<caption></caption>
|
||||
<thead>
|
||||
<tr>
|
||||
{/* <th width="100">국가</th> */}
|
||||
<th width="150">일자</th>
|
||||
<th className="cell-nru">NRU</th>
|
||||
{[...Array(Number(retentionData))].map((value, index) => {
|
||||
return <th key={index}>{`D+${index + 1}`}</th>;
|
||||
})}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{dataList.retention &&
|
||||
dataList.retention.map(data => (
|
||||
<tr className="cell-nru-th" key={data.date}>
|
||||
<td>{data.date}</td>
|
||||
{data['d-day'].map((day, index) => (
|
||||
<td key={index}>{day.dif}</td>
|
||||
))}
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</TableStyle>
|
||||
</IndexTableWrap>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default RetentionContent;
|
||||
166
src/components/IndexManage/RetentionSearchBar.js
Normal file
166
src/components/IndexManage/RetentionSearchBar.js
Normal file
@@ -0,0 +1,166 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { styled } from 'styled-components';
|
||||
import Button from '../../components/common/button/Button';
|
||||
import DatePickerComponent from '../common/Date/DatePickerComponent';
|
||||
|
||||
import { FormWrapper, InputLabel, TextInput, SelectInput, BtnWrapper, InputGroup, DatePickerWrapper, AlertText } from '../../styles/Components';
|
||||
|
||||
const RetentionSearchBar = ({ resultData, setResultData, handleSearch, fetchData, setRetention, setExcelBtn }) => {
|
||||
// 초기 날짜 세팅
|
||||
let d = new Date();
|
||||
const START_DATE = new Date(new Date(d.setDate(d.getDate() - 1)).setHours(0, 0, 0, 0));
|
||||
const END_DATE = new Date(new Date(d.setDate(d.getDate() - 1)).setHours(24, 0, 0, 0));
|
||||
const [errorMessage, setErrorMessage] = useState('');
|
||||
const [period, setPeriod] = useState(0);
|
||||
|
||||
// resultData에 임의 날짜 넣어주기
|
||||
useEffect(() => {
|
||||
setResultData({
|
||||
send_dt: START_DATE,
|
||||
end_dt: '',
|
||||
retention: 0,
|
||||
});
|
||||
}, []);
|
||||
|
||||
// 발송 날짜 세팅 로직
|
||||
const handleSelectedDate = data => {
|
||||
const sendDate = new Date(data);
|
||||
const resultSendData = new Date(sendDate.getFullYear(), sendDate.getMonth(), sendDate.getDate());
|
||||
|
||||
const resultEndDate = new Date(resultSendData);
|
||||
resultEndDate.setDate(resultEndDate.getDate() + Number(resultData.retention));
|
||||
|
||||
setResultData({ ...resultData, send_dt: resultSendData, end_dt: resultEndDate });
|
||||
};
|
||||
|
||||
// // 발송 날짜 세팅 로직
|
||||
// const handleEndDate = data => {
|
||||
// const endDate = new Date(data);
|
||||
// const resultSendData = new Date(endDate.getFullYear(), endDate.getMonth(), endDate.getDate());
|
||||
|
||||
// setResultData({ ...resultData, end_dt: resultSendData });
|
||||
// };
|
||||
|
||||
// Retention 세팅 로직
|
||||
const handleRetention = e => {
|
||||
const value = e.target.value;
|
||||
|
||||
const resultEndDate = new Date(resultData.send_dt);
|
||||
resultEndDate.setDate(resultEndDate.getDate() + Number(value));
|
||||
|
||||
setResultData({ ...resultData, end_dt: resultEndDate, retention: value });
|
||||
setPeriod(value);
|
||||
};
|
||||
|
||||
//Retention 범위 선택 후 disable 처리 로직
|
||||
const handleSearchBtn = e => {
|
||||
e.preventDefault();
|
||||
|
||||
if(period == 0) {
|
||||
setErrorMessage("필수값을 선택하세요.");
|
||||
return false;
|
||||
} else {
|
||||
setErrorMessage("");
|
||||
setExcelBtn(false); //활성화
|
||||
handleSearch(resultData.send_dt, resultData.end_dt);
|
||||
}
|
||||
}
|
||||
|
||||
const handleReset = e => {
|
||||
e.preventDefault();
|
||||
setResultData({ send_dt: START_DATE, end_dt: '', retention: 0 });
|
||||
setRetention(1);
|
||||
setErrorMessage("");
|
||||
setPeriod(1);
|
||||
setExcelBtn(true); //비활성화
|
||||
|
||||
fetchData(START_DATE, END_DATE);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<FormWrapper>
|
||||
<SearchbarStyle>
|
||||
<SearchItem>
|
||||
<InputLabel>집계 기준일</InputLabel>
|
||||
<InputGroup>
|
||||
<DatePickerWrapper>
|
||||
<DatePickerComponent
|
||||
name="시작 일자" selectedDate={resultData.send_dt}
|
||||
handleSelectedDate={data => handleSelectedDate(data)}
|
||||
maxDate={new Date()} />
|
||||
<span>~</span>
|
||||
<DatePickerComponent
|
||||
name="종료 일자"
|
||||
selectedDate={resultData.end_dt}
|
||||
maxDate={new Date()}
|
||||
readOnly={true}
|
||||
disabled={true}
|
||||
type="retention" />
|
||||
</DatePickerWrapper>
|
||||
</InputGroup>
|
||||
</SearchItem>
|
||||
<SearchItem>
|
||||
<InputLabel>Retention 범위</InputLabel>
|
||||
<SelectInput
|
||||
onChange={e => handleRetention(e)} value={resultData.retention}>
|
||||
<option value={0}>선택</option>
|
||||
<option value={1}>D+1</option>
|
||||
<option value={7}>D+7</option>
|
||||
<option value={30}>D+30</option>
|
||||
</SelectInput>
|
||||
</SearchItem>
|
||||
{/* 기획 보류 */}
|
||||
{/* <SearchItem>
|
||||
<InputLabel>조회 국가</InputLabel>
|
||||
<SelectInput>
|
||||
<option value="">ALL</option>
|
||||
<option value="">KR</option>
|
||||
<option value="">EN</option>
|
||||
<option value="">JP</option>
|
||||
<option value="">TH</option>
|
||||
</SelectInput>
|
||||
</SearchItem> */}
|
||||
<BtnWrapper $gap="8px">
|
||||
<Button theme="reset" handleClick={handleReset} />
|
||||
<Button
|
||||
theme="search"
|
||||
text="검색"
|
||||
handleClick={handleSearchBtn}
|
||||
/>
|
||||
<AlertText>{errorMessage}</AlertText>
|
||||
</BtnWrapper>
|
||||
</SearchbarStyle>
|
||||
</FormWrapper>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default RetentionSearchBar;
|
||||
|
||||
const SearchbarStyle = styled.div`
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 20px 0;
|
||||
font-size: 14px;
|
||||
padding: 20px;
|
||||
border-top: 1px solid #000;
|
||||
border-bottom: 1px solid #000;
|
||||
margin-bottom: 40px;
|
||||
`;
|
||||
|
||||
const SearchItem = styled.div`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 20px;
|
||||
margin-right: 50px;
|
||||
|
||||
${TextInput}, ${SelectInput} {
|
||||
height: 35px;
|
||||
}
|
||||
${TextInput} {
|
||||
padding: 0 10px;
|
||||
max-width: 400px;
|
||||
}
|
||||
`;
|
||||
90
src/components/IndexManage/SegmentContent.js
Normal file
90
src/components/IndexManage/SegmentContent.js
Normal file
@@ -0,0 +1,90 @@
|
||||
import { Fragment, useEffect, useState } from 'react';
|
||||
|
||||
import Button from '../../components/common/button/Button';
|
||||
|
||||
import { TableStyle, TableInfo, ListOption, IndexTableWrap } from '../../styles/Components';
|
||||
import { SegmentSearchBar } from '../../components/IndexManage/index';
|
||||
import { SegmentIndexExport, SegmentIndexView } from '../../apis';
|
||||
|
||||
const SegmentContent = () => {
|
||||
const token = sessionStorage.getItem('token');
|
||||
const END_DATE = new Date();
|
||||
|
||||
const [dataList, setDataList] = useState([]);
|
||||
const [resultData, setResultData] = useState([]);
|
||||
|
||||
const [finishDate, setFinishDate] = useState(END_DATE);
|
||||
|
||||
useEffect(() => {
|
||||
fetchData(END_DATE);
|
||||
}, []);
|
||||
|
||||
// Retention 지표 데이터
|
||||
const fetchData = async endDate => {
|
||||
const endDateToLocal =
|
||||
endDate.getFullYear() +
|
||||
'-' +
|
||||
(endDate.getMonth() + 1 < 9 ? '0' + (endDate.getMonth() + 1) : endDate.getMonth() + 1) +
|
||||
'-' +
|
||||
(endDate.getDate() < 9 ? '0' + endDate.getDate() : endDate.getDate());
|
||||
|
||||
setDataList(await SegmentIndexView(token, endDateToLocal));
|
||||
setFinishDate(endDateToLocal);
|
||||
};
|
||||
|
||||
// 검색 함수
|
||||
const handleSearch = end_dt => {
|
||||
fetchData(end_dt);
|
||||
};
|
||||
|
||||
// 엑셀 다운로드
|
||||
const handleXlsxExport = () => {
|
||||
const fileName = 'Caliverse_Segment_Index.xlsx';
|
||||
SegmentIndexExport(token, fileName, finishDate);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<SegmentSearchBar setResultData={setResultData} resultData={resultData} handleSearch={handleSearch} fetchData={fetchData} />
|
||||
<TableInfo>
|
||||
<ListOption>
|
||||
<Button theme="line" text="엑셀 다운로드" handleClick={handleXlsxExport} />
|
||||
</ListOption>
|
||||
</TableInfo>
|
||||
<IndexTableWrap>
|
||||
<TableStyle>
|
||||
<caption></caption>
|
||||
<thead>
|
||||
<tr>
|
||||
<th colSpan="1" width="200">
|
||||
{dataList && dataList.start_dt} ~ {dataList && dataList.end_dt}
|
||||
</th>
|
||||
<th colSpan="2" width="400">
|
||||
KIP
|
||||
</th>
|
||||
</tr>
|
||||
<tr>
|
||||
{/* <th>국가</th> */}
|
||||
<th>세그먼트 분류</th>
|
||||
<th>AU</th>
|
||||
<th>AU Percentage by User Segment (%)</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{dataList && dataList.segment &&
|
||||
dataList.segment.map((segment, index) => (
|
||||
<tr key={index}>
|
||||
{/* <td rowSpan="6">TH</td> */}
|
||||
<td>{segment.type}</td>
|
||||
<td>{segment.au}</td>
|
||||
<td>{segment.dif}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</TableStyle>
|
||||
</IndexTableWrap>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default SegmentContent;
|
||||
123
src/components/IndexManage/SegmentSearchBar.js
Normal file
123
src/components/IndexManage/SegmentSearchBar.js
Normal file
@@ -0,0 +1,123 @@
|
||||
import { useEffect } from 'react';
|
||||
import { styled } from 'styled-components';
|
||||
import Button from '../../components/common/button/Button';
|
||||
import DatePickerComponent from '../common/Date/DatePickerComponent';
|
||||
|
||||
import { FormWrapper, InputLabel, TextInput, SelectInput, BtnWrapper, InputGroup, DatePickerWrapper } from '../../styles/Components';
|
||||
|
||||
const SegmentSearchBar = ({ resultData, setResultData, handleSearch, fetchData }) => {
|
||||
// 초기 날짜 세팅
|
||||
const END_DATE = new Date();
|
||||
|
||||
// resultData에 임의 날짜 넣어주기
|
||||
useEffect(() => {
|
||||
setResultData({
|
||||
search_dt: END_DATE,
|
||||
});
|
||||
}, []);
|
||||
|
||||
// 발송 날짜 세팅 로직
|
||||
const handleSelectedDate = data => {
|
||||
const sendDate = new Date(data);
|
||||
const resultSendData = new Date(sendDate.getFullYear(), sendDate.getMonth(), sendDate.getDate());
|
||||
|
||||
setResultData({ search_dt: resultSendData });
|
||||
};
|
||||
|
||||
// 발송 날짜 세팅 로직
|
||||
|
||||
const handleReset = e => {
|
||||
e.preventDefault();
|
||||
setResultData({ search_dt: END_DATE });
|
||||
|
||||
fetchData(END_DATE);
|
||||
};
|
||||
|
||||
// console.log(resultData);
|
||||
|
||||
return (
|
||||
<>
|
||||
<FormWrapper>
|
||||
<SearchbarStyle2>
|
||||
<SearchRow>
|
||||
<SearchItem>
|
||||
<InputLabel>집계 기준일</InputLabel>
|
||||
<InputGroup>
|
||||
<DatePickerWrapper>
|
||||
<DatePickerComponent name="집계 기준일" selectedDate={resultData.search_dt} handleSelectedDate={data => handleSelectedDate(data)} maxDate={new Date()} />
|
||||
</DatePickerWrapper>
|
||||
</InputGroup>
|
||||
</SearchItem>
|
||||
{/* 기획 보류 */}
|
||||
{/* <SearchItem>
|
||||
<InputLabel>조회 국가</InputLabel>
|
||||
<SelectInput>
|
||||
<option value="">ALL</option>
|
||||
<option value="">KR</option>
|
||||
<option value="">EN</option>
|
||||
<option value="">JP</option>
|
||||
<option value="">TH</option>
|
||||
</SelectInput>
|
||||
</SearchItem> */}
|
||||
<BtnWrapper $gap="8px">
|
||||
<Button theme="reset" handleClick={handleReset} />
|
||||
<Button
|
||||
theme="search"
|
||||
text="검색"
|
||||
handleClick={e => {
|
||||
e.preventDefault();
|
||||
handleSearch(resultData.search_dt);
|
||||
}}
|
||||
/>
|
||||
</BtnWrapper>
|
||||
</SearchRow>
|
||||
<SearchNoti>※ 집계 기준일 기준 -100일까지의 Segment 유저 분포를 집계합니다.</SearchNoti>
|
||||
</SearchbarStyle2>
|
||||
</FormWrapper>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default SegmentSearchBar;
|
||||
|
||||
const SearchbarStyle = styled.div`
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 20px 0;
|
||||
font-size: 14px;
|
||||
padding: 20px;
|
||||
border-top: 1px solid #000;
|
||||
border-bottom: 1px solid #000;
|
||||
margin-bottom: 40px;
|
||||
`;
|
||||
|
||||
const SearchbarStyle2 = styled(SearchbarStyle)`
|
||||
flex-flow: column;
|
||||
gap: 10px;
|
||||
`;
|
||||
|
||||
const SearchRow = styled.div`
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 20px 0;
|
||||
`;
|
||||
const SearchItem = styled.div`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 20px;
|
||||
margin-right: 50px;
|
||||
|
||||
${TextInput}, ${SelectInput} {
|
||||
height: 35px;
|
||||
}
|
||||
${TextInput} {
|
||||
padding: 0 10px;
|
||||
max-width: 400px;
|
||||
}
|
||||
`;
|
||||
const SearchNoti = styled.div`
|
||||
font-size: 12px;
|
||||
color: #999;
|
||||
letter-spacing: 0.5px;
|
||||
`;
|
||||
133
src/components/IndexManage/UserContent.js
Normal file
133
src/components/IndexManage/UserContent.js
Normal file
@@ -0,0 +1,133 @@
|
||||
import { Fragment, useEffect, useRef, useState } from 'react';
|
||||
|
||||
import Button from '../../components/common/button/Button';
|
||||
|
||||
import { Title, TableStyle, TableInfo, ListOption, IndexTableWrap } from '../../styles/Components';
|
||||
import { UserIndexSearchBar, DailyDashBoard } from '../../components/IndexManage/index';
|
||||
|
||||
import { userIndexView, userIndexExport } from '../../apis';
|
||||
import Loading from '../common/Loading';
|
||||
import { ExcelDownButton } from '../common';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { formatStringDate } from '../../utils';
|
||||
|
||||
const UserContent = () => {
|
||||
const token = sessionStorage.getItem('token');
|
||||
const { t } = useTranslation();
|
||||
let d = new Date();
|
||||
const START_DATE = new Date(new Date(d.setDate(d.getDate() - 1)).setHours(0, 0, 0, 0));
|
||||
const END_DATE = new Date();
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
const tableRef = useRef(null);
|
||||
|
||||
const [dataList, setDataList] = useState([]);
|
||||
const [resultData, setResultData] = useState([]);
|
||||
|
||||
// const [sendDate, setSendDate] = useState(START_DATE);
|
||||
// const [finishDate, setFinishDate] = useState(END_DATE);
|
||||
|
||||
const headers = [
|
||||
{key: 'date', label: '일자'},
|
||||
{key: 'nru', label: 'NRU'},
|
||||
{key: 'ugqCreate', label: '일자'},
|
||||
{key: 'dglc', label: '일자'},
|
||||
{key: 'dau', label: '일자'},
|
||||
{key: 'mcu', label: '일자'},
|
||||
{key: 'date', label: '일자'},
|
||||
{key: 'date', label: '일자'},
|
||||
]
|
||||
|
||||
useEffect(() => {
|
||||
fetchData(START_DATE, END_DATE);
|
||||
}, []);
|
||||
|
||||
// 이용자 지표 데이터
|
||||
const fetchData = async (startDate, endDate) => {
|
||||
setLoading(true);
|
||||
|
||||
const startDateToLocal = formatStringDate(startDate);
|
||||
const endDateToLocal = formatStringDate(endDate);
|
||||
|
||||
await userIndexView(token, startDateToLocal, endDateToLocal).then(data => {
|
||||
setDataList(data);
|
||||
setLoading(false);
|
||||
});
|
||||
|
||||
// setSendDate(startDateToLocal);
|
||||
// setFinishDate(endDateToLocal);
|
||||
};
|
||||
|
||||
// 검색 함수
|
||||
const handleSearch = (send_dt, end_dt) => {
|
||||
fetchData(send_dt, end_dt);
|
||||
};
|
||||
|
||||
// 엑셀 다운로드
|
||||
// const handleXlsxExport = () => {
|
||||
// const fileName = 'Caliverse_User_Index.xlsx';
|
||||
// userIndexExport(token, fileName, sendDate, finishDate);
|
||||
// };
|
||||
|
||||
return (
|
||||
<>
|
||||
<DailyDashBoard />
|
||||
<UserIndexSearchBar setResultData={setResultData} resultData={resultData} handleSearch={handleSearch} fetchData={fetchData} />
|
||||
<TableInfo>
|
||||
<ListOption>
|
||||
<ExcelDownButton tableRef={tableRef} fileName={t('FILE_INDEX_USER_CONTENT')} />
|
||||
</ListOption>
|
||||
</TableInfo>
|
||||
<IndexTableWrap>
|
||||
<TableStyle ref={tableRef}>
|
||||
<caption></caption>
|
||||
<thead>
|
||||
<tr>
|
||||
{/*<th width="100">국가</th>*/}
|
||||
<th width="120">일자</th>
|
||||
<th width="100">NRU</th>
|
||||
<th width="100">UGQ(생성)</th>
|
||||
<th width="100">DGLC</th>
|
||||
<th width="100">DAU</th>
|
||||
<th width="100">MCU</th>
|
||||
<th width="100">총 Playtime(HR)</th>
|
||||
<th width="100">평균 Playtime</th>
|
||||
<th width="100">WAU</th>
|
||||
<th width="100">MAU</th>
|
||||
<th width="100">ServerCount</th>
|
||||
<th width="100">DB Read Count</th>
|
||||
<th width="100">DB Write Count</th>
|
||||
<th width="100">DB Sum</th>
|
||||
{/*<th width="200">PU</th>*/}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{dataList &&
|
||||
dataList.map((data, index) => (
|
||||
<tr key={index}>
|
||||
{/*<td rowSpan={data.length}>ALL</td>*/}
|
||||
<td>{data.date}</td>
|
||||
<td>{data.nru}</td>
|
||||
<td>{data.ugqCreate}</td>
|
||||
<td>{data.dglc}</td>
|
||||
<td>{data.dau}</td>
|
||||
<td>{data.mcu}</td>
|
||||
<td>{Math.ceil(data.playtime / 3600) || 0}</td>
|
||||
<td>{Math.round(((data.playtime / 3600) / data.dau) * 100) / 100 || 0}</td>
|
||||
<td>{data.wau}</td>
|
||||
<td>{data.mau}</td>
|
||||
<td>{data.serverCount}</td>
|
||||
<td>{data.readCapacity}</td>
|
||||
<td>{data.writeCapacity}</td>
|
||||
<td>{data.readCapacity + data.writeCapacity}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</TableStyle>
|
||||
</IndexTableWrap>
|
||||
{loading && <Loading/>}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default UserContent;
|
||||
135
src/components/IndexManage/UserIndexSearchBar.js
Normal file
135
src/components/IndexManage/UserIndexSearchBar.js
Normal file
@@ -0,0 +1,135 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { styled } from 'styled-components';
|
||||
import Button from '../../components/common/button/Button';
|
||||
|
||||
import 'react-datepicker/dist/react-datepicker.css';
|
||||
import DatePickerComponent from '../common/Date/DatePickerComponent';
|
||||
|
||||
import { FormWrapper, InputLabel, TextInput, SelectInput, BtnWrapper, InputGroup, DatePickerWrapper } from '../../styles/Components';
|
||||
|
||||
const UserIndexSearchBar = ({ resultData, setResultData, handleSearch, fetchData }) => {
|
||||
// 초기 날짜 세팅
|
||||
let d = new Date();
|
||||
const START_DATE = new Date(new Date(d.setDate(d.getDate() - 1)).setHours(0, 0, 0, 0));
|
||||
const END_DATE = new Date();
|
||||
|
||||
// resultData에 임의 날짜 넣어주기
|
||||
useEffect(() => {
|
||||
setResultData({
|
||||
send_dt: START_DATE,
|
||||
end_dt: END_DATE,
|
||||
});
|
||||
}, []);
|
||||
|
||||
const searchType = [
|
||||
{ name: '전체', value: 'ALL' },
|
||||
{ name: '한국어', value: 'KR' },
|
||||
{ name: '영어', value: 'EN' },
|
||||
{ name: '일본어', value: 'JP' },
|
||||
{ name: '태국어', value: 'TH' },
|
||||
];
|
||||
|
||||
// 발송 날짜 세팅 로직
|
||||
const handleSelectedDate = data => {
|
||||
const sendDate = new Date(data);
|
||||
const resultSendData = new Date(sendDate.getFullYear(), sendDate.getMonth(), sendDate.getDate());
|
||||
|
||||
setResultData({ ...resultData, send_dt: resultSendData });
|
||||
};
|
||||
|
||||
// 발송 날짜 세팅 로직
|
||||
const handleEndDate = data => {
|
||||
const endDate = new Date(data);
|
||||
const resultSendData = new Date(endDate.getFullYear(), endDate.getMonth(), endDate.getDate());
|
||||
|
||||
setResultData({ ...resultData, end_dt: resultSendData });
|
||||
};
|
||||
|
||||
const handleReset = e => {
|
||||
e.preventDefault();
|
||||
setResultData({ send_dt: START_DATE, end_dt: END_DATE });
|
||||
|
||||
fetchData(START_DATE, END_DATE);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<FormWrapper>
|
||||
<SearchbarStyle>
|
||||
<SearchItem>
|
||||
<InputLabel>집계 기간</InputLabel>
|
||||
<InputGroup>
|
||||
<DatePickerWrapper>
|
||||
<DatePickerComponent
|
||||
name="시작 일자"
|
||||
selectedDate={resultData.send_dt}
|
||||
handleSelectedDate={data => handleSelectedDate(data)}
|
||||
maxDate={new Date()} />
|
||||
<span>~</span>
|
||||
<DatePickerComponent
|
||||
name="종료 일자"
|
||||
selectedDate={resultData.end_dt}
|
||||
handleSelectedDate={data => handleEndDate(data)}
|
||||
pastDate = {resultData.send_dt}
|
||||
maxDate={new Date()} />
|
||||
</DatePickerWrapper>
|
||||
</InputGroup>
|
||||
</SearchItem>
|
||||
{/* 기획 보류 */}
|
||||
{/* <SearchItem>
|
||||
<InputLabel>조회 국가</InputLabel>
|
||||
<SelectInput
|
||||
// onChange={e => setResultData({ ...resultData, searchType: e.target.value })}
|
||||
>
|
||||
{searchType.map((option, index) => (
|
||||
<option key={index} value={option.value}>
|
||||
{option.name}
|
||||
</option>
|
||||
))}
|
||||
</SelectInput>
|
||||
</SearchItem> */}
|
||||
<BtnWrapper $gap="8px">
|
||||
<Button theme="reset" handleClick={handleReset} />
|
||||
<Button
|
||||
theme="search"
|
||||
text="검색"
|
||||
handleClick={e => {
|
||||
e.preventDefault();
|
||||
handleSearch(resultData.send_dt, resultData.end_dt);
|
||||
}}
|
||||
/>
|
||||
</BtnWrapper>
|
||||
</SearchbarStyle>
|
||||
</FormWrapper>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default UserIndexSearchBar;
|
||||
|
||||
const SearchbarStyle = styled.div`
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 20px 0;
|
||||
font-size: 14px;
|
||||
padding: 20px;
|
||||
border-top: 1px solid #000;
|
||||
border-bottom: 1px solid #000;
|
||||
margin-bottom: 40px;
|
||||
`;
|
||||
|
||||
const SearchItem = styled.div`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 20px;
|
||||
margin-right: 50px;
|
||||
|
||||
${TextInput}, ${SelectInput} {
|
||||
height: 35px;
|
||||
}
|
||||
${TextInput} {
|
||||
padding: 0 10px;
|
||||
max-width: 400px;
|
||||
}
|
||||
`;
|
||||
219
src/components/IndexManage/VBPContent.js
Normal file
219
src/components/IndexManage/VBPContent.js
Normal file
@@ -0,0 +1,219 @@
|
||||
import { styled } from 'styled-components';
|
||||
import { useState, useEffect } from 'react';
|
||||
|
||||
import { TableStyle, TableInfo, ListOption } from '../../styles/Components';
|
||||
|
||||
import Button from '../../components/common/button/Button';
|
||||
import VBPSearchBar from '../../components/IndexManage/VBPSearchBar';
|
||||
import { VBPIndexExport, VbpIndexView } from '../../apis';
|
||||
|
||||
const VBPContent = () => {
|
||||
const token = sessionStorage.getItem('token');
|
||||
let d = new Date();
|
||||
const START_DATE = new Date(new Date(d.setDate(d.getDate() - 1)).setHours(0, 0, 0, 0));
|
||||
const END_DATE = new Date();
|
||||
|
||||
const [sendDate, setSendDate] = useState(START_DATE);
|
||||
const [finishDate, setFinishDate] = useState(END_DATE);
|
||||
|
||||
const [dataList, setDataList] = useState([]);
|
||||
useEffect(() => {
|
||||
fetchData(START_DATE, END_DATE);
|
||||
}, []);
|
||||
|
||||
// console.log(dataList);
|
||||
|
||||
const fetchData = async (startDate, endDate) => {
|
||||
const startDateToLocal =
|
||||
startDate.getFullYear() +
|
||||
'-' +
|
||||
(startDate.getMonth() + 1 < 9 ? '0' + (startDate.getMonth() + 1) : startDate.getMonth() + 1) +
|
||||
'-' +
|
||||
(startDate.getDate() < 9 ? '0' + startDate.getDate() : startDate.getDate());
|
||||
|
||||
const endDateToLocal =
|
||||
endDate.getFullYear() +
|
||||
'-' +
|
||||
(endDate.getMonth() + 1 < 9 ? '0' + (endDate.getMonth() + 1) : endDate.getMonth() + 1) +
|
||||
'-' +
|
||||
(endDate.getDate() < 9 ? '0' + endDate.getDate() : endDate.getDate());
|
||||
|
||||
setDataList(await VbpIndexView(token, startDateToLocal, endDateToLocal));
|
||||
|
||||
setSendDate(startDateToLocal);
|
||||
setFinishDate(endDateToLocal);
|
||||
};
|
||||
|
||||
// 엑셀 다운로드
|
||||
const handleXlsxExport = () => {
|
||||
const fileName = 'Caliverse_VBP_Index.xlsx';
|
||||
VBPIndexExport(token, fileName, sendDate, finishDate);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<VBPSearchBar fetchData={fetchData} />
|
||||
<TableInfo>
|
||||
<ListOption>
|
||||
<Button theme="line" text="엑셀 다운로드" handleClick={handleXlsxExport} />
|
||||
</ListOption>
|
||||
</TableInfo>
|
||||
<TableWrapper>
|
||||
<EconomicTable>
|
||||
<thead>
|
||||
<tr>
|
||||
<th colSpan="2" className="text-center" width="300">
|
||||
Product
|
||||
</th>
|
||||
<th width="160">2023-08-07</th>
|
||||
<th width="160">2023-08-08</th>
|
||||
<th width="160">2023-08-09</th>
|
||||
<th width="160">2023-08-10</th>
|
||||
<th width="160">2023-08-11</th>
|
||||
<th width="160">2023-08-12</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<TableTitle colSpan="2">(Total) VBP 생산량</TableTitle>
|
||||
<TableData>500000</TableData>
|
||||
<TableData>500000</TableData>
|
||||
<TableData>500000</TableData>
|
||||
<TableData>500000</TableData>
|
||||
<TableData>500000</TableData>
|
||||
<TableData>500000</TableData>
|
||||
</tr>
|
||||
<tr>
|
||||
<TableTitle colSpan="2">(Total) VBP 소진량</TableTitle>
|
||||
<TableData>490000</TableData>
|
||||
<TableData>490000</TableData>
|
||||
<TableData>490000</TableData>
|
||||
<TableData>490000</TableData>
|
||||
<TableData>490000</TableData>
|
||||
<TableData>490000</TableData>
|
||||
</tr>
|
||||
<tr>
|
||||
<TableTitle colSpan="2">(Total) VBP 보유량</TableTitle>
|
||||
<TableData>3.2M</TableData>
|
||||
<TableData>3.3M</TableData>
|
||||
<TableData>3.3M</TableData>
|
||||
<TableData>3.4M</TableData>
|
||||
<TableData>3.5M</TableData>
|
||||
<TableData>3.5M</TableData>
|
||||
</tr>
|
||||
<tr>
|
||||
<TableTitle rowSpan="2">GET</TableTitle>
|
||||
<TableTitle>퀘스트 보상</TableTitle>
|
||||
<TableData>150000</TableData>
|
||||
<TableData>150000</TableData>
|
||||
<TableData>150000</TableData>
|
||||
<TableData>150000</TableData>
|
||||
<TableData>150000</TableData>
|
||||
<TableData>150000</TableData>
|
||||
</tr>
|
||||
<tr>
|
||||
<TableTitle>시즌패스 보상</TableTitle>
|
||||
<TableData>150000</TableData>
|
||||
<TableData>150000</TableData>
|
||||
<TableData>150000</TableData>
|
||||
<TableData>150000</TableData>
|
||||
<TableData>150000</TableData>
|
||||
<TableData>150000</TableData>
|
||||
</tr>
|
||||
<tr>
|
||||
<TableTitle rowSpan="2">USE</TableTitle>
|
||||
<TableTitle>퀘스트 보상</TableTitle>
|
||||
<TableData>150000</TableData>
|
||||
<TableData>150000</TableData>
|
||||
<TableData>150000</TableData>
|
||||
<TableData>150000</TableData>
|
||||
<TableData>150000</TableData>
|
||||
<TableData>150000</TableData>
|
||||
</tr>
|
||||
<tr>
|
||||
<TableTitle>시즌패스 보상</TableTitle>
|
||||
<TableData>150000</TableData>
|
||||
<TableData>150000</TableData>
|
||||
<TableData>150000</TableData>
|
||||
<TableData>150000</TableData>
|
||||
<TableData>150000</TableData>
|
||||
<TableData>150000</TableData>
|
||||
</tr>
|
||||
|
||||
{/* {mokupData.map((data, index) => (
|
||||
<Fragment key={index}>
|
||||
<tr>
|
||||
<td>{data.date}</td>
|
||||
<td>{data.name}</td>
|
||||
<td>{data.trader}</td>
|
||||
<td>{data.id}</td>
|
||||
<td>{data.key}</td>
|
||||
</tr>
|
||||
</Fragment>
|
||||
))} */}
|
||||
</tbody>
|
||||
</EconomicTable>
|
||||
</TableWrapper>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default VBPContent;
|
||||
|
||||
const TableWrapper = styled.div`
|
||||
width: 100%;
|
||||
min-width: 680px;
|
||||
overflow: auto;
|
||||
&::-webkit-scrollbar {
|
||||
height: 4px;
|
||||
}
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background: #666666;
|
||||
}
|
||||
&::-webkit-scrollbar-track {
|
||||
background: #d9d9d9;
|
||||
}
|
||||
${TableStyle} {
|
||||
width: 100%;
|
||||
min-width: 900px;
|
||||
th {
|
||||
&.cell-nru {
|
||||
background: #f0f0f0;
|
||||
border-left: 1px solid #aaa;
|
||||
border-right: 1px solid #aaa;
|
||||
}
|
||||
}
|
||||
td {
|
||||
&.blank {
|
||||
background: #f9f9f9;
|
||||
}
|
||||
&.cell-nru {
|
||||
background: #fafafa;
|
||||
border-left: 1px solid #aaa;
|
||||
border-right: 1px solid #aaa;
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const TableData = styled.td`
|
||||
background: ${props => (props.$state === 'danger' ? '#d60000' : props.$state === 'blank' ? '#F9F9F9' : 'transparent')};
|
||||
color: ${props => (props.$state === 'danger' ? '#fff' : '#2c2c2c')};
|
||||
`;
|
||||
|
||||
const TableTitle = styled.td`
|
||||
text-align: center;
|
||||
`;
|
||||
|
||||
const EconomicTable = styled(TableStyle)`
|
||||
${TableData} {
|
||||
text-align: left;
|
||||
}
|
||||
tbody {
|
||||
tr:nth-child(1),
|
||||
tr:nth-child(2),
|
||||
tr:nth-child(3) {
|
||||
background: #f5fcff;
|
||||
}
|
||||
}
|
||||
`;
|
||||
112
src/components/IndexManage/VBPSearchBar.js
Normal file
112
src/components/IndexManage/VBPSearchBar.js
Normal file
@@ -0,0 +1,112 @@
|
||||
import { useState } from 'react';
|
||||
import { styled } from 'styled-components';
|
||||
import Button from '../../components/common/button/Button';
|
||||
|
||||
import DatePickerComponent from '../common/Date/DatePickerComponent';
|
||||
|
||||
import { FormWrapper, InputLabel, TextInput, SelectInput, BtnWrapper, InputGroup, DatePickerWrapper } from '../../styles/Components';
|
||||
|
||||
const VBPSearchBar = ({ fetchData }) => {
|
||||
let d = new Date();
|
||||
const START_DATE = new Date(new Date(d.setDate(d.getDate() - 1)).setHours(0, 0, 0, 0));
|
||||
const END_DATE = new Date();
|
||||
|
||||
const [resultData, setResultData] = useState({
|
||||
send_dt: START_DATE,
|
||||
end_dt: END_DATE,
|
||||
});
|
||||
|
||||
// 발송 날짜 세팅 로직
|
||||
const handleSelectedDate = data => {
|
||||
const sendDate = new Date(data);
|
||||
const resultSendData = new Date(sendDate.getFullYear(), sendDate.getMonth(), sendDate.getDate());
|
||||
|
||||
setResultData({ ...resultData, send_dt: resultSendData });
|
||||
};
|
||||
|
||||
// 발송 날짜 세팅 로직
|
||||
const handleEndDate = data => {
|
||||
const endDate = new Date(data);
|
||||
const resultSendData = new Date(endDate.getFullYear(), endDate.getMonth(), endDate.getDate());
|
||||
|
||||
setResultData({ ...resultData, end_dt: resultSendData });
|
||||
};
|
||||
|
||||
const handleReset = e => {
|
||||
e.preventDefault();
|
||||
setResultData({ send_dt: START_DATE, end_dt: END_DATE });
|
||||
|
||||
fetchData(START_DATE, END_DATE);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<FormWrapper>
|
||||
<SearchbarStyle>
|
||||
<SearchItem>
|
||||
<InputLabel>집계 기간</InputLabel>
|
||||
<InputGroup>
|
||||
<DatePickerWrapper>
|
||||
<DatePickerComponent
|
||||
name="시작 일자" selectedDate={resultData.send_dt}
|
||||
handleSelectedDate={data => handleSelectedDate(data)}
|
||||
maxDate={new Date()} />
|
||||
<span>~</span>
|
||||
<DatePickerComponent
|
||||
name="종료 일자" selectedDate={resultData.end_dt}
|
||||
handleSelectedDate={data => handleEndDate(data)}
|
||||
pastDate = {resultData.send_dt}
|
||||
maxDate={new Date()} />
|
||||
</DatePickerWrapper>
|
||||
</InputGroup>
|
||||
</SearchItem>
|
||||
<BtnWrapper $gap="8px">
|
||||
<Button
|
||||
theme="reset"
|
||||
handleClick={e => {
|
||||
handleReset(e);
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
theme="search"
|
||||
text="집계"
|
||||
handleClick={e => {
|
||||
e.preventDefault();
|
||||
fetchData(resultData.send_dt, resultData.end_dt);
|
||||
}}
|
||||
/>
|
||||
</BtnWrapper>
|
||||
</SearchbarStyle>
|
||||
</FormWrapper>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default VBPSearchBar;
|
||||
|
||||
const SearchbarStyle = styled.div`
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 20px 0;
|
||||
font-size: 14px;
|
||||
padding: 20px;
|
||||
border-top: 1px solid #000;
|
||||
border-bottom: 1px solid #000;
|
||||
margin-bottom: 40px;
|
||||
`;
|
||||
|
||||
const SearchItem = styled.div`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 20px;
|
||||
margin-right: 50px;
|
||||
|
||||
${TextInput}, ${SelectInput} {
|
||||
height: 35px;
|
||||
}
|
||||
${TextInput} {
|
||||
padding: 0 10px;
|
||||
max-width: 400px;
|
||||
}
|
||||
`;
|
||||
27
src/components/IndexManage/index.js
Normal file
27
src/components/IndexManage/index.js
Normal file
@@ -0,0 +1,27 @@
|
||||
import UserIndexSearchBar from "./UserIndexSearchBar";
|
||||
import RetentionSearchBar from "./RetentionSearchBar";
|
||||
import SegmentSearchBar from "./SegmentSearchBar";
|
||||
import DailyDashBoard from "./DailyDashBoard";
|
||||
import PlayTimeSearchBar from "./PlayTimeSearchBar";
|
||||
import UserContent from "./UserContent";
|
||||
import PlayTimeContent from "./PlayTimeContent";
|
||||
import RetentionContent from "./RetentionContent";
|
||||
import SegmentContent from "./SegmentContent";
|
||||
import DailyActiveUserContent from "./DailyActiveUserContent";
|
||||
import DailyMedalContent from "./DailyMedalContent";
|
||||
import DailySearchBar from "./DailySearchBar";
|
||||
|
||||
export {
|
||||
UserIndexSearchBar,
|
||||
RetentionSearchBar,
|
||||
SegmentSearchBar,
|
||||
DailyDashBoard,
|
||||
PlayTimeSearchBar,
|
||||
UserContent,
|
||||
SegmentContent,
|
||||
RetentionContent,
|
||||
PlayTimeContent,
|
||||
DailySearchBar,
|
||||
DailyActiveUserContent,
|
||||
DailyMedalContent,
|
||||
};
|
||||
Reference in New Issue
Block a user