대시보드 사용자 접속현황 그래프(6개월 전으로 해놓음)

thkim
유민형 2024-01-03 17:31:34 +09:00
parent 20a84b6d1e
commit 0e16df04a4
7 changed files with 559 additions and 17503 deletions

File diff suppressed because it is too large Load Diff

View File

@ -13,6 +13,7 @@
"react-loader-spinner": "^5.4.5",
"react-router-dom": "^6.4.0",
"react-scripts": "5.0.1",
"recharts": "^2.10.3",
"styled-components": "^6.0.9",
"web-vitals": "^2.1.4"
},

View File

@ -1,27 +1,29 @@
import React, { useState, useEffect, useCallback } from 'react';
import { Link, useLocation } from 'react-router-dom';
import React, {useState, useEffect, useCallback, PureComponent} from 'react';
import {Link, useLocation} from 'react-router-dom';
import {BarChart, Bar, XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer} from 'recharts';
import * as EgovNet from 'api/egovFetch';
import URL from 'constants/url';
import CODE from 'constants/code';
import { default as EgovLeftNav } from 'components/leftmenu/EgovLeftNavAdmin';
import {default as EgovLeftNav} from 'components/leftmenu/EgovLeftNavAdmin';
function EgovAdminScheduleList(props) {
console.group("EgovAdminScheduleList");
console.log("[Start] EgovAdminScheduleList ------------------------------");
console.log("EgovAdminScheduleList [props] : ", props);
const location = useLocation();
console.log("EgovAdminScheduleList [location] : ", location);
const DATE = new Date();
const TODAY = new Date(DATE.getFullYear(), DATE.getMonth(), DATE.getDate());
const [searchCondition, setSearchCondition] = useState(location.state?.searchCondition || { schdulSe: '', year: TODAY.getFullYear(), month: TODAY.getMonth(), date: TODAY.getDate() });
const [searchCondition, setSearchCondition] = useState(location.state?.searchCondition || {schdulSe: '', year: TODAY.getFullYear(), month: TODAY.getMonth(), date: TODAY.getDate()});
const [calendarTag, setCalendarTag] = useState([]);
const [scheduleList, setScheduleList] = useState([]);
const [dailyUserLogList, setDailyUserLogList] = useState([]);
const innerConsole = (...args) => {
console.log(...args);
@ -45,14 +47,14 @@ function EgovAdminScheduleList(props) {
if (target === CODE.DATE_MONTH) {
changedDate = new Date(searchCondition.year, searchCondition.month + amount, searchCondition.date);
}
setSearchCondition({ ...searchCondition, year: changedDate.getFullYear(), month: changedDate.getMonth(), date: changedDate.getDate() });
setSearchCondition({...searchCondition, year: changedDate.getFullYear(), month: changedDate.getMonth(), date: changedDate.getDate()});
}
const retrieveList = useCallback((srchcnd) => {
console.groupCollapsed("EgovAdminScheduleList.retrieveList()");
const retrieveListURL = '/schedule/month'+EgovNet.getQueryString(srchcnd);
const retrieveListURL = '/schedule/month' + EgovNet.getQueryString(srchcnd);
const requestOptions = {
method: "GET",
headers: {
@ -70,7 +72,7 @@ function EgovAdminScheduleList(props) {
}
);
console.groupEnd("EgovAdminScheduleList.retrieveList()");
},[]);
}, []);
const drawCalendar = () => {
console.groupCollapsed("EgovAdminScheduleList.drawCalendar()");
@ -138,7 +140,7 @@ function EgovAdminScheduleList(props) {
let mutCalendarTagList = [];
let keyIdx = 0;
//draw Calendar
monthArr.forEach((week, weekIdx) => {
console.log();
@ -151,7 +153,8 @@ function EgovAdminScheduleList(props) {
if (scheduleList.length > 0) {//
return (
<td key={keyIdx++}>
<Link to={{pathname: URL.ADMIN_SCHEDULE_CREATE}} state={{iUseDate : mutsUseYearMonth + sDate + "000000"}} className="day" key={keyIdx++}>{day}</Link><br />
<Link to={{pathname: URL.ADMIN_SCHEDULE_CREATE}} state={{iUseDate: mutsUseYearMonth + sDate + "000000"}} className="day"
key={keyIdx++}>{day}</Link><br/>
{
scheduleList.map((schedule, scheduleIdx) => {
let iBeginDate = Number(schedule.schdulBgnde.substring(0, 8));
@ -161,11 +164,11 @@ function EgovAdminScheduleList(props) {
if (iUseDate >= iBeginDate && iUseDate <= iEndDate) {
return (
<>
<Link to={{pathname: URL.ADMIN_SCHEDULE_DETAIL}}
state={{schdulId : schedule.schdulId}}
key={keyIdx++}>{schedule.schdulNm}
<Link to={{pathname: URL.ADMIN_SCHEDULE_DETAIL}}
state={{schdulId: schedule.schdulId}}
key={keyIdx++}>{schedule.schdulNm}
</Link>
<br />
<br/>
</>
);
} else return <></>
@ -176,7 +179,8 @@ function EgovAdminScheduleList(props) {
} else {//
return (
<td key={keyIdx++}>
<Link to={{pathname: URL.ADMIN_SCHEDULE_CREATE}} state={{iUseDate : mutsUseYearMonth + sDate + "000000"}} className="day" key={keyIdx++}>{day}</Link><br />
<Link to={{pathname: URL.ADMIN_SCHEDULE_CREATE}} state={{iUseDate: mutsUseYearMonth + sDate + "000000"}} className="day"
key={keyIdx++}>{day}</Link><br/>
</td>);
}
} else if (day === 0) {// /
@ -190,7 +194,7 @@ function EgovAdminScheduleList(props) {
console.groupEnd("EgovAdminScheduleList.drawCalendar()");
}
const Location = React.memo(function Location() {
const Location = React.memo(function Location() {
return (
<div className="location">
<ul>
@ -204,21 +208,123 @@ function EgovAdminScheduleList(props) {
useEffect(() => {
retrieveList(searchCondition);
// eslint-disable-next-line react-hooks/exhaustive-deps
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [searchCondition]);
useEffect(() => {
drawCalendar();
// eslint-disable-next-line react-hooks/exhaustive-deps
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [scheduleList]);
const getDailyUserLogList = useCallback(() => {
console.groupCollapsed("EgovAdminScheduleList.getDailyUserLogList()");
const dailyUserLogListURL = '/admin/dashboard/daily-user-log-list';
const requestOptions = {
method: "GET",
headers: {
'Content-type': 'application/json',
}
}
EgovNet.requestFetch(dailyUserLogListURL,
requestOptions,
(resp) => {
setDailyUserLogList(resp.result.dailyUserLogList);
console.log("@@@ : " + dailyUserLogList);
},
function (resp) {
console.log("err response : ", resp);
}
);
console.groupEnd("EgovAdminScheduleList.getDailyUserLogList()");
}, []);
useEffect(() => {
getDailyUserLogList();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
const data = dailyUserLogList.map(item => ({
logDt: item.logDt,
uv: item.mobileCnt,
"사용자 접속현황": item.logCnt,
amt: item.pcCnt,
}));
const getIntroOfPage = (label) => {
if (label === 'Page A') {
return "Page A is about men's clothing";
}
if (label === 'Page B') {
return "Page B is about women's dress";
}
if (label === 'Page C') {
return "Page C is about women's bag";
}
if (label === 'Page D') {
return 'Page D is about household goods';
}
if (label === 'Page E') {
return 'Page E is about food';
}
if (label === 'Page F') {
return 'Page F is about baby food';
}
return '';
};
const CustomTooltip = ({active, payload, label}) => {
if (active && payload && payload.length) {
return (
<div className="custom-tooltip">
<p className="desc">사용자 접속 현황</p>
<p className="label">{`${label} : ${payload[0].value}`}</p>
<p className="intro">{getIntroOfPage(label)}</p>
</div>
);
}
return null;
};
class Example extends PureComponent {
static demoUrl = 'https://codesandbox.io/s/tooltip-with-customized-content-lyxvs';
render() {
return (
<ResponsiveContainer width="100%" height="100%">
<BarChart
width={500}
height={300}
data={data}
margin={{
top: 5,
right: 30,
left: 20,
bottom: 5,
}}
>
<CartesianGrid strokeDasharray="3 3"/>
<XAxis dataKey="logDt"/>
<YAxis/>
<Tooltip content={<CustomTooltip/>}/>
<Legend/>
<Bar dataKey="사용자 접속현황" barSize={20} fill="#87CEFA"/>
</BarChart>
</ResponsiveContainer>
);
}
}
console.log("------------------------------EgovAdminScheduleList [End]");
console.groupEnd("EgovAdminScheduleList");
return (
<div className="container">
<div className="c_wrap">
{/* <!-- Location --> */}
<Location />
<Location/>
{/* <!--// Location --> */}
<div className="layout">
@ -235,15 +341,19 @@ function EgovAdminScheduleList(props) {
<h2 className="tit_2">일정관리</h2>
<div style={{width: 1000, height: 300}}>
<Example/>
</div>
{/* <!-- 검색조건 --> */}
<div className="condition">
<ul>
<li>
<label className="f_select" htmlFor="sel1">
<select name="schdulSe" id="sel1" title="조건"
onChange={e => {
setSearchCondition({ ...searchCondition, schdulSe: e.target.value });
}}
onChange={e => {
setSearchCondition({...searchCondition, schdulSe: e.target.value});
}}
>
<option value="">전체</option>
<option value="1">회의</option>
@ -256,28 +366,28 @@ function EgovAdminScheduleList(props) {
</li>
<li className="half L">
<button className="prev"
onClick={() => {
changeDate(CODE.DATE_YEAR, -1);
}}
onClick={() => {
changeDate(CODE.DATE_YEAR, -1);
}}
></button>
<span>{searchCondition.year}</span>
<button className="next"
onClick={() => {
changeDate(CODE.DATE_YEAR, 1);
}}
onClick={() => {
changeDate(CODE.DATE_YEAR, 1);
}}
></button>
</li>
<li className="half R">
<button className="prev"
onClick={() => {
changeDate(CODE.DATE_MONTH, -1);
}}
onClick={() => {
changeDate(CODE.DATE_MONTH, -1);
}}
></button>
<span>{(searchCondition.month + 1)}</span>
<button className="next"
onClick={() => {
changeDate(CODE.DATE_MONTH, 1);
}}
onClick={() => {
changeDate(CODE.DATE_MONTH, 1);
}}
></button>
</li>
</ul>
@ -287,18 +397,18 @@ function EgovAdminScheduleList(props) {
<div className="calendar_list">
<table>
<thead>
<tr>
<th></th>
<th></th>
<th></th>
<th></th>
<th></th>
<th></th>
<th></th>
</tr>
<tr>
<th></th>
<th></th>
<th></th>
<th></th>
<th></th>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
{calendarTag}
{calendarTag}
</tbody>
</table>
</div>

View File

@ -0,0 +1,55 @@
package com.dbnt.kcscbackend.admin.dashboard;
import com.dbnt.kcscbackend.admin.dashboard.service.AdminDashboardService;
import com.dbnt.kcscbackend.config.common.BaseController;
import com.dbnt.kcscbackend.config.common.ResultVO;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import java.time.LocalDate;
import java.util.HashMap;
import java.util.Map;
@RestController
@RequiredArgsConstructor
@RequestMapping("/admin/dashboard")
@Tag(name="AdminDashboardController", description = "사이트관리 대시보드")
public class AdminDashboardController extends BaseController {
private final AdminDashboardService adminDashboardService;
@Operation(
summary = "기본코드 그룹 조회",
description = "기본코드 그룹 조회",
tags = {"AdminConfigController"}
)
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "조회 성공"),
@ApiResponse(responseCode = "403", description = "인가된 사용자가 아님")
})
@RequestMapping(method = RequestMethod.GET, value = "/daily-user-log-list", consumes = MediaType.APPLICATION_JSON_VALUE)
public ResultVO getDailyUserLogList() throws Exception {
ResultVO resultVO = new ResultVO();
Map<String, Object> resultMap = new HashMap<>();
// 현재 날짜
// todo endDate 뒤에 .minus 지워야함
LocalDate endDate = LocalDate.now().minusMonths(6);
System.out.println("@@@ localdate : " + endDate);
// 3개월 전 날짜 계산
LocalDate startDate = endDate.minusMonths(3);
resultMap.put("dailyUserLogList", adminDashboardService.selectDailyUserLogList(startDate, endDate));
resultVO.setResult(resultMap);
return resultVO;
}
}

View File

@ -0,0 +1,36 @@
package com.dbnt.kcscbackend.admin.dashboard.entity;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.hibernate.annotations.DynamicInsert;
import org.hibernate.annotations.DynamicUpdate;
import javax.persistence.*;
import java.time.LocalDate;
@Getter
@Setter
@Entity
@NoArgsConstructor
@DynamicInsert
@DynamicUpdate
@Table(name = "tn_daily_user_log")
public class TnDailyUserLog {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "dul_seq")
private Long dulSeq;
@Column(name = "log_dt")
private LocalDate logDt;
@Column(name = "log_cnt")
private Integer logCnt;
@Column(name = "mobile_cnt")
private Integer mobileCnt;
@Column(name = "pc_cnt")
private Integer pcCnt;
}

View File

@ -0,0 +1,11 @@
package com.dbnt.kcscbackend.admin.dashboard.repository;
import com.dbnt.kcscbackend.admin.dashboard.entity.TnDailyUserLog;
import org.springframework.data.jpa.repository.JpaRepository;
import java.time.LocalDate;
import java.util.List;
public interface TnDailyUserLogRepository extends JpaRepository<TnDailyUserLog, Long> {
List<TnDailyUserLog> findByLogDtBetweenOrderByLogDt(LocalDate startDate, LocalDate endDate);
}

View File

@ -0,0 +1,20 @@
package com.dbnt.kcscbackend.admin.dashboard.service;
import com.dbnt.kcscbackend.admin.dashboard.entity.TnDailyUserLog;
import com.dbnt.kcscbackend.admin.dashboard.repository.TnDailyUserLogRepository;
import lombok.RequiredArgsConstructor;
import org.egovframe.rte.fdl.cmmn.EgovAbstractServiceImpl;
import org.springframework.stereotype.Service;
import java.time.LocalDate;
import java.util.List;
@Service
@RequiredArgsConstructor
public class AdminDashboardService extends EgovAbstractServiceImpl {
private final TnDailyUserLogRepository tnDailyUserLogRepository;
public List<TnDailyUserLog> selectDailyUserLogList(LocalDate startDate, LocalDate endDate) {
return tnDailyUserLogRepository.findByLogDtBetweenOrderByLogDt(startDate, endDate);
}
}