first commit 2
This commit is contained in:
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
define('G5_IS_ADMIN', true);
|
||||
define('G5_IS_ADMIN', true);
|
||||
include_once('../../common.php');
|
||||
|
||||
if (!defined('_GNUBOARD_')) exit;
|
||||
include_once(G5_ADMIN_PATH.'/admin.lib.php');
|
||||
|
||||
//if (isset($token)) {
|
||||
// $token = @htmlspecialchars(strip_tags($token), ENT_QUOTES);
|
||||
//}
|
||||
//
|
||||
//run_event('admin_common');
|
||||
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
if (!defined('_GNUBOARD_')) exit;
|
||||
|
||||
// 710번대 최상위 메뉴 '통계관리'를 정의합니다.
|
||||
// 💡 [수정] 통계관리 메뉴 링크 수정
|
||||
$menu['menu720'][] = array('720100', '통계관리', G5_ADMIN_URL . '/statistics/index.php', 'statistics');
|
||||
$menu['menu720'][] = array('720200', '통계현황', G5_ADMIN_URL . '/statistics/visit_list.php', 'st_visit'); // 상세 통계 페이지로 변경
|
||||
//$menu['menu720'][] = array('720300', '접속통계', G5_ADMIN_URL . '/statistics/index.php', 'st_visit_summary'); // 요약 페이지
|
||||
$menu['menu720'][] = array('720400', '광고현황', G5_ADMIN_URL . '/statistics/banner.php', 'st_keyword');
|
||||
$menu['menu720'][] = array('720500', '개시판통계', G5_ADMIN_URL . '/statistics/board.php', 'st_board');
|
||||
$menu['menu720'][] = array('720600', '솔루션 설치', G5_ADMIN_URL . '/statistics/install.php', 'install');
|
||||
?>
|
||||
@@ -0,0 +1,240 @@
|
||||
<?php
|
||||
include_once('./_common.php');
|
||||
|
||||
//if (!isset($_SERVER['HTTP_X_REQUESTED_WITH']) || strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) != 'xmlhttprequest') {
|
||||
// die(json_encode(['error' => '잘못된 접근입니다.']));
|
||||
//}
|
||||
|
||||
$type = isset($_GET['type']) ? preg_replace('/[^a-z_]/i', '', $_GET['type']) : 'time';
|
||||
$start_day = isset($_GET['start_day']) ? $_GET['start_day'] : G5_TIME_YMD;
|
||||
$end_day = isset($_GET['end_day']) ? $_GET['end_day'] : G5_TIME_YMD;
|
||||
|
||||
$result = [
|
||||
'type' => 'bar',
|
||||
'title' => '',
|
||||
'labels' => [],
|
||||
'datasets' => [],
|
||||
'list' => [],
|
||||
];
|
||||
|
||||
$data = [];
|
||||
$sql = '';
|
||||
$sql_common = " from {$g5['visit_table']} ";
|
||||
$sql_search = " where vi_date between '{$start_day}' and '{$end_day}' ";
|
||||
|
||||
// 배너 테이블 존재 여부 확인
|
||||
$banner_table = 'rb_banner';//isset($g5['banner_table']) ? $g5['banner_table'] : 'g5_shop_banner';
|
||||
|
||||
switch($type) {
|
||||
// --- 접속 통계 (기존 코드 유지) ---
|
||||
case 'time':
|
||||
$result['title'] = '시간별 접속 현황';
|
||||
$sql = " SELECT SUBSTRING(vi_time, 1, 2) as `key`, count(vi_id) as `cnt` {$sql_common} {$sql_search} GROUP BY `key` ORDER BY `key` ";
|
||||
break;
|
||||
case 'week':
|
||||
$result['title'] = '요일별 접속 현황';
|
||||
$sql = " SELECT WEEKDAY(vi_date) as `key`, count(vi_id) as `cnt` {$sql_common} {$sql_search} GROUP BY `key` ORDER BY `key` ";
|
||||
break;
|
||||
case 'date':
|
||||
$result['title'] = '일별 접속 현황';
|
||||
$result['type'] = 'line';
|
||||
$sql = " SELECT vi_date as `key`, count(vi_id) as `cnt` {$sql_common} {$sql_search} GROUP BY `key` ORDER BY `key` ";
|
||||
break;
|
||||
case 'month':
|
||||
$result['title'] = '월별 접속 현황';
|
||||
$result['type'] = 'line';
|
||||
$sql = " SELECT SUBSTRING(vi_date, 1, 7) as `key`, count(vi_id) as `cnt` {$sql_common} {$sql_search} GROUP BY `key` ORDER BY `key` ";
|
||||
break;
|
||||
case 'domain':
|
||||
$result['title'] = '접속전 도메인 TOP 50';
|
||||
$sql = " SELECT vi_referer as `key`, count(vi_id) as `cnt` {$sql_common} {$sql_search} AND vi_referer != '' GROUP BY `key` ORDER BY `cnt` DESC LIMIT 50";
|
||||
break;
|
||||
case 'ip':
|
||||
$result['title'] = '접속 IP TOP 50';
|
||||
$sql = " SELECT vi_ip as `key`, count(vi_id) as `cnt` {$sql_common} {$sql_search} GROUP BY `key` ORDER BY `cnt` DESC LIMIT 50";
|
||||
break;
|
||||
case 'browser':
|
||||
$result['title'] = '브라우저별 접속 현황';
|
||||
$result['type'] = 'pie';
|
||||
$sql = " SELECT vi_browser as `key`, count(vi_id) as `cnt` {$sql_common} {$sql_search} GROUP BY `key` ORDER BY `cnt` DESC LIMIT 20";
|
||||
break;
|
||||
case 'os':
|
||||
$result['title'] = '운영체제별 접속 현황';
|
||||
$result['type'] = 'pie';
|
||||
$sql = " SELECT vi_os as `key`, count(vi_id) as `cnt` {$sql_common} {$sql_search} GROUP BY `key` ORDER BY `cnt` DESC LIMIT 20";
|
||||
break;
|
||||
|
||||
// --- 게시물 통계 (수정된 부분) ---
|
||||
case 'board_view':
|
||||
$result['title'] = '게시판별 총 조회수';
|
||||
// 💡 [수정] 모든 게시판을 순회하며 실제 wr_hit 합계 계산 (성능 최적화)
|
||||
$boards = sql_query("SELECT bo_table, bo_subject FROM {$g5['board_table']} ORDER BY bo_count_write DESC");
|
||||
$board_stats = [];
|
||||
while($row = sql_fetch_array($boards)) {
|
||||
$write_table = $g5['write_prefix'] . $row['bo_table'];
|
||||
$chk = sql_fetch("SHOW TABLES LIKE '{$write_table}'");
|
||||
if ($chk) {
|
||||
$stat = sql_fetch("SELECT SUM(wr_hit) as cnt FROM {$write_table}");
|
||||
if ($stat['cnt'] > 0) {
|
||||
$board_stats[] = ['key' => $row['bo_subject'], 'cnt' => $stat['cnt']];
|
||||
}
|
||||
}
|
||||
}
|
||||
// 조회수 내림차순 정렬
|
||||
usort($board_stats, function($a, $b) { return $b['cnt'] - $a['cnt']; });
|
||||
$board_stats = array_slice($board_stats, 0, 20); // 상위 20개만
|
||||
|
||||
// SQL 실행 결과를 흉내내어 데이터 처리 로직으로 넘김
|
||||
$data_source = $board_stats;
|
||||
break;
|
||||
|
||||
case 'board_write':
|
||||
$result['title'] = '게시판별 총 게시물 수';
|
||||
$sql = " SELECT bo_subject as `key`, bo_count_write as `cnt` FROM {$g5['board_table']} ORDER BY `cnt` DESC LIMIT 20";
|
||||
break;
|
||||
case 'board_comment':
|
||||
$result['title'] = '게시판별 총 댓글 수';
|
||||
$sql = " SELECT bo_subject as `key`, bo_count_comment as `cnt` FROM {$g5['board_table']} ORDER BY `cnt` DESC LIMIT 20";
|
||||
break;
|
||||
|
||||
case 'post_view':
|
||||
$result['title'] = '게시물별 조회수 TOP 20';
|
||||
// 💡 [수정] 모든 게시판을 순회하며 상위 게시물 수집 (성능 최적화)
|
||||
$boards = sql_query("SELECT bo_table FROM {$g5['board_table']}");
|
||||
$all_posts = [];
|
||||
while($row = sql_fetch_array($boards)) {
|
||||
$write_table = $g5['write_prefix'] . $row['bo_table'];
|
||||
$chk = sql_fetch("SHOW TABLES LIKE '{$write_table}'");
|
||||
if ($chk) {
|
||||
// 각 게시판에서 상위 20개씩 가져옴
|
||||
$res = sql_query("SELECT wr_subject as `key`, wr_hit as `cnt` FROM {$write_table} WHERE wr_is_comment = 0 ORDER BY wr_hit DESC LIMIT 20");
|
||||
while($post = sql_fetch_array($res)) {
|
||||
$all_posts[] = $post;
|
||||
}
|
||||
}
|
||||
}
|
||||
// 전체에서 다시 정렬하여 상위 20개 추출
|
||||
usort($all_posts, function($a, $b) { return $b['cnt'] - $a['cnt']; });
|
||||
$data_source = array_slice($all_posts, 0, 20);
|
||||
break;
|
||||
|
||||
case 'post_comment':
|
||||
$result['title'] = '게시물별 댓글 수 TOP 20';
|
||||
// 💡 [수정] 모든 게시판을 순회하며 상위 게시물 수집
|
||||
$boards = sql_query("SELECT bo_table FROM {$g5['board_table']}");
|
||||
$all_posts = [];
|
||||
while($row = sql_fetch_array($boards)) {
|
||||
$write_table = $g5['write_prefix'] . $row['bo_table'];
|
||||
$chk = sql_fetch("SHOW TABLES LIKE '{$write_table}'");
|
||||
if ($chk) {
|
||||
$res = sql_query("SELECT wr_subject as `key`, wr_comment as `cnt` FROM {$write_table} WHERE wr_is_comment = 0 ORDER BY wr_comment DESC LIMIT 20");
|
||||
while($post = sql_fetch_array($res)) {
|
||||
$all_posts[] = $post;
|
||||
}
|
||||
}
|
||||
}
|
||||
usort($all_posts, function($a, $b) { return $b['cnt'] - $a['cnt']; });
|
||||
$data_source = array_slice($all_posts, 0, 20);
|
||||
break;
|
||||
|
||||
// --- 배너 통계 (기존 코드 유지) ---
|
||||
case 'position':
|
||||
$result['title'] = '배너 위치별 클릭수';
|
||||
$result['type'] = 'pie';
|
||||
$chk = sql_fetch("SHOW TABLES LIKE '{$banner_table}'");
|
||||
if ($chk) {
|
||||
$sql = " SELECT bn_position as `key`, SUM(bn_hit) as `cnt` FROM {$banner_table} GROUP BY `key` ORDER BY `cnt` DESC ";
|
||||
}
|
||||
break;
|
||||
case 'each':
|
||||
$result['title'] = '개별 배너 클릭수 TOP 20';
|
||||
$chk = sql_fetch("SHOW TABLES LIKE '{$banner_table}'");
|
||||
if ($chk) {
|
||||
$sql = " SELECT bn_alt as `key`, bn_hit as `cnt` FROM {$banner_table} ORDER BY `cnt` DESC LIMIT 20";
|
||||
}
|
||||
break;
|
||||
|
||||
// --- 검색어 통계 (기존 코드 유지) ---
|
||||
case 'keyword_today':
|
||||
case 'keyword_yesterday':
|
||||
case 'keyword_week':
|
||||
case 'keyword_month':
|
||||
$result['title'] = '검색어 순위';
|
||||
$from = $to = '';
|
||||
if($type === 'keyword_today') { $from = $to = G5_TIME_YMD; }
|
||||
if($type === 'keyword_yesterday') { $from = $to = date('Y-m-d', strtotime('-1 day')); }
|
||||
if($type === 'keyword_week') { $from = date('Y-m-d', strtotime('-6 day')); $to = G5_TIME_YMD; }
|
||||
if($type === 'keyword_month') { $from = date('Y-m-01'); $to = G5_TIME_YMD; }
|
||||
$sql = " SELECT pp_word as `key`, pp_count as `cnt` FROM {$g5['popular_table']} WHERE pp_date BETWEEN '{$from}' AND '{$to}' GROUP BY pp_word ORDER BY `cnt` DESC LIMIT 50";
|
||||
break;
|
||||
}
|
||||
|
||||
// 데이터 처리 공통 로직
|
||||
if (isset($data_source)) {
|
||||
// PHP 배열로 데이터가 준비된 경우 (게시판 통계 등)
|
||||
$sum_count = 0;
|
||||
$max = 0;
|
||||
foreach($data_source as $row) {
|
||||
$sum_count += $row['cnt'];
|
||||
if ($row['cnt'] > $max) $max = $row['cnt'];
|
||||
}
|
||||
foreach($data_source as $row) {
|
||||
$key = $row['key'] ?: '알수없음';
|
||||
$result['labels'][] = $key;
|
||||
$data[] = $row['cnt'];
|
||||
$rate = ($sum_count > 0) ? $row['cnt'] / $sum_count * 100 : 0;
|
||||
$bar_width = ($max > 0) ? $row['cnt'] / $max * 100 : 0;
|
||||
$result['list'][] = [
|
||||
'key' => $key,
|
||||
'count' => number_format($row['cnt']),
|
||||
'rate' => number_format($rate, 1),
|
||||
'bar_width' => $bar_width
|
||||
];
|
||||
}
|
||||
} else if ($sql) {
|
||||
// SQL 쿼리로 데이터가 준비된 경우 (접속 통계 등)
|
||||
$res = sql_query($sql, false);
|
||||
if ($res) {
|
||||
$sum_count = 0;
|
||||
$max = 0;
|
||||
$temp_list = [];
|
||||
while($row = sql_fetch_array($res)) {
|
||||
$temp_list[] = $row;
|
||||
$sum_count += $row['cnt'];
|
||||
if ($row['cnt'] > $max) $max = $row['cnt'];
|
||||
}
|
||||
foreach($temp_list as $row) {
|
||||
$key = $row['key'] ?: '알수없음';
|
||||
if ($type == 'week') {
|
||||
$week_name = ['월', '화', '수', '목', '금', '토', '일'];
|
||||
$key = isset($week_name[$row['key']]) ? $week_name[$row['key']] : '알수없음';
|
||||
}
|
||||
$result['labels'][] = $key;
|
||||
$data[] = $row['cnt'];
|
||||
$rate = ($sum_count > 0) ? $row['cnt'] / $sum_count * 100 : 0;
|
||||
$bar_width = ($max > 0) ? $row['cnt'] / $max * 100 : 0;
|
||||
$result['list'][] = [
|
||||
'key' => $key,
|
||||
'count' => number_format($row['cnt']),
|
||||
'rate' => number_format($rate, 1),
|
||||
'bar_width' => $bar_width
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$dataset_item = ['label' => '수', 'data' => $data];
|
||||
if (in_array($result['type'], ['pie', 'doughnut', 'radar'])) {
|
||||
$colors = [
|
||||
'rgba(255, 99, 132, 0.7)', 'rgba(54, 162, 235, 0.7)', 'rgba(255, 206, 86, 0.7)',
|
||||
'rgba(75, 192, 192, 0.7)', 'rgba(153, 102, 255, 0.7)', 'rgba(255, 159, 64, 0.7)'
|
||||
];
|
||||
$dataset_item['backgroundColor'] = array_slice($colors, 0, count($data));
|
||||
} else {
|
||||
$dataset_item['backgroundColor'] = 'rgba(54, 162, 235, 0.5)';
|
||||
}
|
||||
|
||||
$result['datasets'][] = $dataset_item;
|
||||
|
||||
echo json_encode($result);
|
||||
?>
|
||||
@@ -0,0 +1,140 @@
|
||||
<?php
|
||||
$sub_menu = '720400';
|
||||
include_once('./_common.php');
|
||||
auth_check_menu($auth, $sub_menu, "r");
|
||||
|
||||
$g5['title'] = "배너 통계";
|
||||
include_once(G5_ADMIN_PATH.'/admin.head.php');
|
||||
|
||||
$type = isset($_GET['type']) ? preg_replace('/[^a-z]/i', '', $_GET['type']) : 'position';
|
||||
?>
|
||||
|
||||
<link rel="stylesheet" href="statistics.css?ver=<?php echo G5_CSS_VER; ?>">
|
||||
|
||||
<div class="notbox">
|
||||
<p>위치별, 배너별 노출수와 클릭수를 시각적으로 분석할 수 있습니다.</p>
|
||||
</div>
|
||||
|
||||
<dl class="srchb lnb4_col bg2_col">
|
||||
<table class="bg_col">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="ctlt bno">통계분류</td>
|
||||
<td class="wbg pl7">
|
||||
<ul class="List">
|
||||
<li><a href="./banner.php?type=position" class="<?php echo ($type == 'position') ? 'btn_ov01' : 'btn_ov02'; ?>">위치별 통계</a></li>
|
||||
<li><a href="./banner.php?type=each" class="<?php echo ($type == 'each') ? 'btn_ov01' : 'btn_ov02'; ?>">개별 배너 통계</a></li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</dl>
|
||||
|
||||
<div class="pt5" style="display: none;">
|
||||
<dl class="srchb lnb4_col bg2_col">
|
||||
<form name="statisticsSearchFrm" method="GET" id="statisticsSearchFrm">
|
||||
<input type="hidden" name="type" value="<?php echo $type; ?>">
|
||||
<dl class="tc pd7 wbg">
|
||||
<input name="start_day" type="date" class="frm_input" id="start_day" value="<?php echo date('Y-m-d'); ?>"> ~
|
||||
<input name="end_day" type="date" class="frm_input" id="end_day" value="<?php echo date('Y-m-d'); ?>">
|
||||
<a class="btn btn_03 set_day" data-date="today">오늘</a>
|
||||
<a class="btn btn_03 set_day" data-date="week">이번주</a>
|
||||
<a class="btn btn_03 set_day" data-date="month">이번달</a>
|
||||
<a class="btn btn_03 set_day" data-date="7day">1주일</a>
|
||||
<a class="btn btn_03 set_day" data-date="15day">15일</a>
|
||||
<a class="btn btn_03 set_day" data-date="30day">1개월</a>
|
||||
<a class="btn btn_03 set_day" data-date="60day">3개월</a>
|
||||
<a class="btn btn_03 set_day" data-date="120day">6개월</a>
|
||||
|
||||
<button type="button" id="btn_search" class="btn_submit btn">검색</button>
|
||||
</dl>
|
||||
</form>
|
||||
</dl>
|
||||
</div>
|
||||
|
||||
<div id="chart-type-selector" class="local_sch01 local_sch">
|
||||
<strong>차트 모양</strong>
|
||||
<a class="btn btn_03 chart-btn active" data-chart-type="bar">세로막대</a>
|
||||
<a class="btn btn_03 chart-btn" data-chart-type="horizontalBar">가로막대</a>
|
||||
<a class="btn btn_03 chart-btn" data-chart-type="line">선그래프</a>
|
||||
<a class="btn btn_03 chart-btn" data-chart-type="pie">원형</a>
|
||||
<a class="btn btn_03 chart-btn" data-chart-type="doughnut">도넛</a>
|
||||
<a class="btn btn_03 chart-btn" data-chart-type="radar">레이더</a>
|
||||
</div>
|
||||
|
||||
<div class="chart-container">
|
||||
<canvas id="myChart"></canvas>
|
||||
</div>
|
||||
|
||||
<!-- 💡 [핵심 수정] 이미지 경로 수정 -->
|
||||
<dl class="ntlt lnb_col"><img src="img/bul_10.png" class="t"> 상세 데이터</dl>
|
||||
<table width="100%" class="access ttlt tf">
|
||||
<thead>
|
||||
<tr class="bg">
|
||||
<th>항목</th>
|
||||
<th>그래프</th>
|
||||
<th>클릭수</th>
|
||||
<th>비율(%)</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="data-table-body">
|
||||
<!-- 데이터가 여기에 동적으로 추가됩니다. -->
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||
<script src="statistics.js?ver=<?php echo G5_JS_VER; ?>"></script>
|
||||
|
||||
<script src="https://code.jquery.com/ui/1.13.2/jquery-ui.js"></script>
|
||||
|
||||
<script>
|
||||
$(function(){
|
||||
$(".hasDatepicker").datepicker({
|
||||
dateFormat: "yy-mm-dd"
|
||||
});
|
||||
|
||||
$('.set_day').click(function(){
|
||||
var term = $(this).data('date');
|
||||
var today = new Date();
|
||||
var start_day, end_day;
|
||||
|
||||
end_day = $.datepicker.formatDate('yy-mm-dd', today);
|
||||
|
||||
switch(term) {
|
||||
case 'today':
|
||||
start_day = end_day;
|
||||
break;
|
||||
case 'week':
|
||||
today.setDate(today.getDate() - today.getDay() + (today.getDay() === 0 ? -6 : 1));
|
||||
start_day = $.datepicker.formatDate('yy-mm-dd', today);
|
||||
break;
|
||||
case 'month':
|
||||
start_day = $.datepicker.formatDate('yy-mm-01', today);
|
||||
break;
|
||||
default:
|
||||
var days = parseInt(term.replace('day', ''));
|
||||
today.setDate(today.getDate() - (days - 1));
|
||||
start_day = $.datepicker.formatDate('yy-mm-dd', today);
|
||||
break;
|
||||
}
|
||||
|
||||
$('#start_day').val(start_day);
|
||||
$('#end_day').val(end_day);
|
||||
|
||||
// 💡 [핵심 수정] 선택된 버튼 스타일 적용
|
||||
$('.set_day').removeClass('active');
|
||||
$(this).addClass('active');
|
||||
});
|
||||
// 차트 종류 변경 버튼 이벤트
|
||||
$('.chart-btn').click(function(){
|
||||
var chartType = $(this).data('chart-type');
|
||||
// createChart(chartType);
|
||||
$('.chart-btn').removeClass('active');
|
||||
$(this).addClass('active');
|
||||
});
|
||||
});
|
||||
</script>
|
||||
<?php
|
||||
include_once(G5_ADMIN_PATH.'/admin.tail.php');
|
||||
?>
|
||||
@@ -0,0 +1,145 @@
|
||||
<?php
|
||||
$sub_menu = '720500'; // 💡 [변경] 게시판 통계 메뉴 코드
|
||||
include_once('./_common.php');
|
||||
auth_check_menu($auth, $sub_menu, "r");
|
||||
|
||||
$g5['title'] = "게시판 통계";
|
||||
include_once(G5_ADMIN_PATH.'/admin.head.php');
|
||||
|
||||
$type = isset($_GET['type']) ? preg_replace('/[^a-z_]/i', '', $_GET['type']) : 'board_view'; // 💡 [변경] 기본값
|
||||
?>
|
||||
|
||||
<!-- 💡 [공통] CSS 파일 로드 -->
|
||||
<link rel="stylesheet" href="statistics.css?ver=<?php echo G5_CSS_VER; ?>">
|
||||
|
||||
<div class="notbox">
|
||||
<p>게시판별, 게시물별 조회수와 활동 현황을 시각적으로 분석할 수 있습니다.</p>
|
||||
</div>
|
||||
|
||||
<dl class="srchb lnb4_col bg2_col">
|
||||
<table class="bg_col">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="ctlt bno">통계분류</td>
|
||||
<td class="wbg pl7">
|
||||
<ul class="List">
|
||||
<!-- 💡 [변경] 게시판 통계용 탭 메뉴 -->
|
||||
<li><a href="./board.php?type=board_view" class="<?php echo ($type == 'board_view') ? 'btn_ov01' : 'btn_ov02'; ?>">게시판별 조회수</a></li>
|
||||
<li><a href="./board.php?type=board_write" class="<?php echo ($type == 'board_write') ? 'btn_ov01' : 'btn_ov02'; ?>">게시판별 게시물 수</a></li>
|
||||
<li><a href="./board.php?type=board_comment" class="<?php echo ($type == 'board_comment') ? 'btn_ov01' : 'btn_ov02'; ?>">게시판별 댓글 수</a></li>
|
||||
<li><a href="./board.php?type=post_view" class="<?php echo ($type == 'post_view') ? 'btn_ov01' : 'btn_ov02'; ?>">게시물별 조회수 TOP 20</a></li>
|
||||
<li><a href="./board.php?type=post_comment" class="<?php echo ($type == 'post_comment') ? 'btn_ov01' : 'btn_ov02'; ?>">게시물별 댓글 수 TOP 20</a></li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</dl>
|
||||
|
||||
|
||||
<div class="pt5">
|
||||
<dl class="srchb lnb4_col bg2_col">
|
||||
<form name="statisticsSearchFrm" method="GET" id="statisticsSearchFrm">
|
||||
<input type="hidden" name="type" value="<?php echo $type; ?>">
|
||||
<dl class="tc pd7 wbg">
|
||||
<input name="start_day" type="date" class="frm_input" id="start_day" value="<?php echo date('Y-m-d'); ?>"> ~
|
||||
<input name="end_day" type="date" class="frm_input" id="end_day" value="<?php echo date('Y-m-d'); ?>">
|
||||
<a class="btn btn_03 set_day" data-date="today">오늘</a>
|
||||
<a class="btn btn_03 set_day" data-date="week">이번주</a>
|
||||
<a class="btn btn_03 set_day" data-date="month">이번달</a>
|
||||
<a class="btn btn_03 set_day" data-date="7day">1주일</a>
|
||||
<a class="btn btn_03 set_day" data-date="15day">15일</a>
|
||||
<a class="btn btn_03 set_day" data-date="30day">1개월</a>
|
||||
<a class="btn btn_03 set_day" data-date="60day">3개월</a>
|
||||
<a class="btn btn_03 set_day" data-date="120day">6개월</a>
|
||||
|
||||
<button type="button" id="btn_search" class="btn_submit btn">검색</button>
|
||||
</dl>
|
||||
</form>
|
||||
</dl>
|
||||
</div>
|
||||
|
||||
<div id="chart-type-selector" class="local_sch01 local_sch">
|
||||
<strong>차트 모양</strong>
|
||||
<a class="btn btn_03 chart-btn active" data-chart-type="bar">세로막대</a>
|
||||
<a class="btn btn_03 chart-btn" data-chart-type="horizontalBar">가로막대</a>
|
||||
<a class="btn btn_03 chart-btn" data-chart-type="line">선그래프</a>
|
||||
<a class="btn btn_03 chart-btn" data-chart-type="pie">원형</a>
|
||||
<a class="btn btn_03 chart-btn" data-chart-type="doughnut">도넛</a>
|
||||
<a class="btn btn_03 chart-btn" data-chart-type="radar">레이더</a>
|
||||
</div>
|
||||
|
||||
<div class="chart-container">
|
||||
<canvas id="myChart"></canvas>
|
||||
</div>
|
||||
|
||||
<dl class="ntlt lnb_col"><img src="img/bul_10.png" class="t"> 상세 데이터</dl>
|
||||
<table width="100%" class="access ttlt tf">
|
||||
<thead>
|
||||
<tr class="bg">
|
||||
<th>항목</th>
|
||||
<th>그래프</th>
|
||||
<th>수</th>
|
||||
<th>비율(%)</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="data-table-body">
|
||||
<!-- 데이터가 여기에 동적으로 추가됩니다. -->
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||
<script src="statistics.js?ver=<?php echo G5_JS_VER; ?>"></script>
|
||||
|
||||
<script src="https://code.jquery.com/ui/1.13.2/jquery-ui.js"></script>
|
||||
|
||||
<script>
|
||||
$(function(){
|
||||
$(".hasDatepicker").datepicker({
|
||||
dateFormat: "yy-mm-dd"
|
||||
});
|
||||
|
||||
$('.set_day').click(function(){
|
||||
var term = $(this).data('date');
|
||||
var today = new Date();
|
||||
var start_day, end_day;
|
||||
|
||||
end_day = $.datepicker.formatDate('yy-mm-dd', today);
|
||||
|
||||
switch(term) {
|
||||
case 'today':
|
||||
start_day = end_day;
|
||||
break;
|
||||
case 'week':
|
||||
today.setDate(today.getDate() - today.getDay() + (today.getDay() === 0 ? -6 : 1));
|
||||
start_day = $.datepicker.formatDate('yy-mm-dd', today);
|
||||
break;
|
||||
case 'month':
|
||||
start_day = $.datepicker.formatDate('yy-mm-01', today);
|
||||
break;
|
||||
default:
|
||||
var days = parseInt(term.replace('day', ''));
|
||||
today.setDate(today.getDate() - (days - 1));
|
||||
start_day = $.datepicker.formatDate('yy-mm-dd', today);
|
||||
break;
|
||||
}
|
||||
|
||||
$('#start_day').val(start_day);
|
||||
$('#end_day').val(end_day);
|
||||
|
||||
// 💡 [핵심 수정] 선택된 버튼 스타일 적용
|
||||
$('.set_day').removeClass('active');
|
||||
$(this).addClass('active');
|
||||
});
|
||||
// 차트 종류 변경 버튼 이벤트
|
||||
$('.chart-btn').click(function(){
|
||||
var chartType = $(this).data('chart-type');
|
||||
// createChart(chartType);
|
||||
$('.chart-btn').removeClass('active');
|
||||
$(this).addClass('active');
|
||||
});
|
||||
});
|
||||
</script>
|
||||
<?php
|
||||
include_once(G5_ADMIN_PATH.'/admin.tail.php');
|
||||
?>
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 210 B |
Binary file not shown.
|
After Width: | Height: | Size: 24 KiB |
@@ -0,0 +1,283 @@
|
||||
<?php
|
||||
$sub_menu = '720100';
|
||||
include_once('./_common.php');
|
||||
auth_check_menu($auth, $sub_menu, "r");
|
||||
|
||||
$g5['title'] = "접속통계";
|
||||
include_once(G5_ADMIN_PATH.'/admin.head.php');
|
||||
|
||||
//--- 데이터 처리 시작 ---//
|
||||
|
||||
$type = isset($_GET['type']) ? preg_replace('/[^a-z]/i', '', $_GET['type']) : 'summary';
|
||||
|
||||
// 날짜 검색
|
||||
$start_day = isset($_GET['start_day']) && preg_match("/^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])$/", $_GET['start_day']) ? $_GET['start_day'] : G5_TIME_YMD;
|
||||
$end_day = isset($_GET['end_day']) && preg_match("/^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])$/", $_GET['end_day']) ? $_GET['end_day'] : G5_TIME_YMD;
|
||||
|
||||
|
||||
$list = [];
|
||||
$sum_count = 0;
|
||||
$max = 0;
|
||||
$chart_data = ['labels' => [], 'datasets' => [['data' => []]]];
|
||||
$page_title = '';
|
||||
$chart_type = 'bar';
|
||||
|
||||
$sql_common = " from {$g5['visit_table']} ";
|
||||
$sql_search = " where vi_date between '{$start_day}' and '{$end_day}' ";
|
||||
|
||||
switch ($type) {
|
||||
case 'time':
|
||||
$page_title = '시간별 통계';
|
||||
$sql = " select SUBSTRING(vi_time, 1, 2) as `key`, count(vi_id) as `cnt` {$sql_common} {$sql_search} group by `key` order by `key` ";
|
||||
break;
|
||||
case 'week':
|
||||
$page_title = '요일별 통계';
|
||||
$sql = " select weekday(vi_date) as `key`, count(vi_id) as `cnt` {$sql_common} {$sql_search} group by `key` order by `key` ";
|
||||
break;
|
||||
case 'date':
|
||||
$page_title = '일별 통계';
|
||||
$chart_type = 'line';
|
||||
$sql = " select vi_date as `key`, count(vi_id) as `cnt` {$sql_common} {$sql_search} group by `key` order by `key` ";
|
||||
break;
|
||||
case 'month':
|
||||
$page_title = '월별 통계';
|
||||
$chart_type = 'line';
|
||||
$sql = " select SUBSTRING(vi_date, 1, 7) as `key`, count(vi_id) as `cnt` {$sql_common} {$sql_search} group by `key` order by `key` ";
|
||||
break;
|
||||
case 'browser':
|
||||
$page_title = '접속 브라우저';
|
||||
$chart_type = 'pie';
|
||||
$sql = " select vi_browser as `key`, count(vi_id) as `cnt` {$sql_common} {$sql_search} group by `key` order by `cnt` desc limit 10";
|
||||
break;
|
||||
case 'os':
|
||||
$page_title = '접속 OS';
|
||||
$chart_type = 'pie';
|
||||
$sql = " select vi_os as `key`, count(vi_id) as `cnt` {$sql_common} {$sql_search} group by `key` order by `cnt` desc limit 10";
|
||||
break;
|
||||
// ... (다른 케이스들)
|
||||
default:
|
||||
$page_title = '접속통계 요약';
|
||||
break;
|
||||
}
|
||||
|
||||
if ($type !== 'summary' && $sql) {
|
||||
$result = sql_query($sql);
|
||||
while($row = sql_fetch_array($result)) {
|
||||
$list[] = $row;
|
||||
$sum_count += $row['cnt'];
|
||||
if ($row['cnt'] > $max) $max = $row['cnt'];
|
||||
}
|
||||
}
|
||||
//--- 데이터 처리 끝 ---//
|
||||
?>
|
||||
|
||||
<link rel="stylesheet" href="statistics.css?ver=<?php echo G5_CSS_VER; ?>">
|
||||
<link rel="stylesheet" href="//code.jquery.com/ui/1.13.2/themes/base/jquery-ui.css">
|
||||
<script src="https://code.jquery.com/ui/1.13.2/jquery-ui.js"></script>
|
||||
|
||||
<div class="notbox">
|
||||
<p>시간 흐름(시간/일간/월간)에 따른 방문자 현황 등을 한눈에 볼 수 있습니다.<br>
|
||||
하루 하루 나타나는 데이터를 출력하여 모아 놓으면, 아주 소중한 사이트 운영가이드책이 될 수 있습니다.</p>
|
||||
</div>
|
||||
|
||||
<dl class="srchb lnb4_col bg2_col">
|
||||
<table class="bg_col">
|
||||
<tbody><tr>
|
||||
<td class="ctlt bno">통계분류</td>
|
||||
<td class="wbg pl7">
|
||||
<ul class="List">
|
||||
<li><a href="./" class="<?php echo ($type == 'summary') ? 'btn_ov01' : 'btn_ov02'; ?>">접속통계</a></li>
|
||||
<li><a href="./?type=time" class="<?php echo ($type == 'time') ? 'btn_ov01' : 'btn_ov02'; ?>">시간별 통계</a></li>
|
||||
<li><a href="./?type=week" class="<?php echo ($type == 'week') ? 'btn_ov01' : 'btn_ov02'; ?>">요일별 통계</a></li>
|
||||
<li><a href="./?type=date" class="<?php echo ($type == 'date') ? 'btn_ov01' : 'btn_ov02'; ?>">일별 통계</a></li>
|
||||
<li><a href="./?type=month" class="<?php echo ($type == 'month') ? 'btn_ov01' : 'btn_ov02'; ?>">월별 통계</a></li>
|
||||
<li><a href="./?type=domain" class="<?php echo ($type == 'domain') ? 'btn_ov01' : 'btn_ov02'; ?>">접속전 도메인</a></li>
|
||||
<li><a href="./?type=ip" class="<?php echo ($type == 'ip') ? 'btn_ov01' : 'btn_ov02'; ?>">접속 IP</a></li>
|
||||
<li><a href="./?type=browser" class="<?php echo ($type == 'browser') ? 'btn_ov01' : 'btn_ov02'; ?>">접속 브라우저</a></li>
|
||||
<li><a href="./?type=os" class="<?php echo ($type == 'os') ? 'btn_ov01' : 'btn_ov02'; ?>">접속 OS</a></li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody></table>
|
||||
</dl>
|
||||
|
||||
<?php if ($type !== 'summary'): ?>
|
||||
<div class="pt5">
|
||||
<dl class="srchb lnb4_col bg2_col">
|
||||
<form name="statisticsSearchFrm" method="GET" id="statisticsSearchFrm">
|
||||
<input type="hidden" name="type" value="<?php echo $type; ?>">
|
||||
<dl class="tc pd7 wbg">
|
||||
<input name="start_day" type="date" class="frm_input hasDatepicker" id="start_day" value="<?php echo $start_day; ?>"> ~
|
||||
<input name="end_day" type="date" class="frm_input hasDatepicker" id="end_day" value="<?php echo $end_day; ?>">
|
||||
<a class="btn btn_03 set_day" data-date="today">오늘</a>
|
||||
<a class="btn btn_03 set_day" data-date="week">이번주</a>
|
||||
<a class="btn btn_03 set_day" data-date="month">이번달</a>
|
||||
<a class="btn btn_03 set_day" data-date="7day">1주일</a>
|
||||
<a class="btn btn_03 set_day" data-date="15day">15일</a>
|
||||
<a class="btn btn_03 set_day" data-date="30day">1개월</a>
|
||||
<a class="btn btn_03 set_day" data-date="60day">3개월</a>
|
||||
<a class="btn btn_03 set_day" data-date="120day">6개월</a>
|
||||
|
||||
<button type="submit" class="btn_submit btn">검색</button>
|
||||
<button type="button" class="btn" onclick="$('#statisticsSearchFrm').get(0).reset();">초기화</button>
|
||||
</dl>
|
||||
</form>
|
||||
</dl>
|
||||
</div>
|
||||
|
||||
<!-- 💡 [핵심 추가] 차트 종류 선택 UI -->
|
||||
<div id="chart-type-selector" class="local_sch01 local_sch">
|
||||
<strong>차트 모양</strong>
|
||||
<a class="btn btn_03 chart-btn active" data-chart-type="bar">세로막대</a>
|
||||
<a class="btn btn_03 chart-btn" data-chart-type="line">선그래프</a>
|
||||
<a class="btn btn_03 chart-btn" data-chart-type="pie">원형</a>
|
||||
<a class="btn btn_03 chart-btn" data-chart-type="doughnut">도넛</a>
|
||||
<a class="btn btn_03 chart-btn" data-chart-type="radar">레이더</a>
|
||||
</div>
|
||||
|
||||
<dl class="ntlt lnb_col"><img src="img/bul_10.png" class="t"> <?php echo $page_title; ?></dl>
|
||||
<div class="chart-container">
|
||||
<canvas id="myChart"></canvas>
|
||||
</div>
|
||||
|
||||
|
||||
<table width="100%" class="access ttlt tf">
|
||||
<colgroup>
|
||||
<col width="200">
|
||||
<col>
|
||||
<col width="100">
|
||||
<col width="100">
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr class="bg">
|
||||
<th>항목</th>
|
||||
<th>그래프</th>
|
||||
<th>방문자수</th>
|
||||
<th>비율(%)</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php
|
||||
if (count($list)) {
|
||||
foreach ($list as $row) {
|
||||
$key = $row['key'] ?: '알수없음';
|
||||
if ($type == 'week') {
|
||||
$week_name = ['월', '화', '수', '목', '금', '토', '일'];
|
||||
$key = isset($week_name[$row['key']]) ? $week_name[$row['key']] : '알수없음';
|
||||
}
|
||||
$rate = ($sum_count > 0) ? $row['cnt'] / $sum_count * 100 : 0;
|
||||
$bar_width = ($max > 0) ? $row['cnt'] / $max * 100 : 0;
|
||||
|
||||
$chart_data['labels'][] = $key;
|
||||
$chart_data['datasets'][0]['data'][] = $row['cnt'];
|
||||
?>
|
||||
<tr>
|
||||
<td class="ct"><?php echo $key; ?></td>
|
||||
<td><div class="graph"><span class="bar" style="width:<?php echo $bar_width; ?>%"></span></div></td>
|
||||
<td class="rt"><?php echo number_format($row['cnt']); ?></td>
|
||||
<td class="rt"><?php echo number_format($rate, 1); ?></td>
|
||||
</tr>
|
||||
<?php
|
||||
}
|
||||
} else {
|
||||
echo '<tr><td colspan="4" class="empty_table">데이터가 없습니다.</td></tr>';
|
||||
}
|
||||
?>
|
||||
</tbody>
|
||||
</table>
|
||||
<?php endif; ?>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||
<script>
|
||||
$(function(){
|
||||
$(".hasDatepicker").datepicker({
|
||||
dateFormat: "yy-mm-dd"
|
||||
});
|
||||
|
||||
$('.set_day').click(function(){
|
||||
var term = $(this).data('date');
|
||||
var today = new Date();
|
||||
var start_day, end_day;
|
||||
|
||||
end_day = $.datepicker.formatDate('yy-mm-dd', today);
|
||||
|
||||
switch(term) {
|
||||
case 'today':
|
||||
start_day = end_day;
|
||||
break;
|
||||
case 'week':
|
||||
today.setDate(today.getDate() - today.getDay() + (today.getDay() === 0 ? -6 : 1));
|
||||
start_day = $.datepicker.formatDate('yy-mm-dd', today);
|
||||
break;
|
||||
case 'month':
|
||||
start_day = $.datepicker.formatDate('yy-mm-01', today);
|
||||
break;
|
||||
default:
|
||||
var days = parseInt(term.replace('day', ''));
|
||||
today.setDate(today.getDate() - (days - 1));
|
||||
start_day = $.datepicker.formatDate('yy-mm-dd', today);
|
||||
break;
|
||||
}
|
||||
|
||||
$('#start_day').val(start_day);
|
||||
$('#end_day').val(end_day);
|
||||
|
||||
// 💡 [핵심 수정] 선택된 버튼 스타일 적용
|
||||
$('.set_day').removeClass('active');
|
||||
$(this).addClass('active');
|
||||
});
|
||||
|
||||
// 💡 [핵심 수정] 차트 생성 및 변경 로직
|
||||
var chartData = <?php echo json_encode($chart_data); ?>;
|
||||
var defaultChartType = '<?php echo $chart_type; ?>';
|
||||
var ctx = document.getElementById('myChart');
|
||||
var myChart;
|
||||
|
||||
function createChart(type) {
|
||||
if (myChart) {
|
||||
myChart.destroy();
|
||||
}
|
||||
if(ctx && chartData.labels.length > 0) {
|
||||
|
||||
// 💡 [핵심 수정] 원형/도넛 차트용 색상 배열 생성
|
||||
if (['pie', 'doughnut', 'radar'].includes(type)) {
|
||||
const colors = [
|
||||
'rgba(255, 99, 132, 0.5)', 'rgba(54, 162, 235, 0.5)', 'rgba(255, 206, 86, 0.5)',
|
||||
'rgba(75, 192, 192, 0.5)', 'rgba(153, 102, 255, 0.5)', 'rgba(255, 159, 64, 0.5)',
|
||||
'rgba(255, 99, 132, 0.8)', 'rgba(54, 162, 235, 0.8)', 'rgba(255, 206, 86, 0.8)',
|
||||
'rgba(75, 192, 192, 0.8)'
|
||||
];
|
||||
chartData.datasets[0].backgroundColor = colors;
|
||||
} else {
|
||||
chartData.datasets[0].backgroundColor = 'rgba(54, 162, 235, 0.5)';
|
||||
}
|
||||
|
||||
myChart = new Chart(ctx, {
|
||||
type: type,
|
||||
data: {
|
||||
labels: chartData.labels,
|
||||
datasets: chartData.datasets
|
||||
},
|
||||
options: {
|
||||
scales: { y: { beginAtZero: true } }
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 초기 차트 생성
|
||||
createChart('bar');
|
||||
|
||||
// 차트 종류 변경 버튼 이벤트
|
||||
$('.chart-btn').click(function(){
|
||||
var chartType = $(this).data('chart-type');
|
||||
createChart(chartType);
|
||||
$('.chart-btn').removeClass('active');
|
||||
$(this).addClass('active');
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<?php
|
||||
include_once(G5_ADMIN_PATH.'/admin.tail.php');
|
||||
?>
|
||||
@@ -0,0 +1,115 @@
|
||||
<?php
|
||||
$sub_menu = '850640';
|
||||
include_once('./_common.php');
|
||||
//include_once(__DIR__ . '/lib/SchemaManager.class.php');
|
||||
|
||||
if (!$is_admin) {
|
||||
alert('관리자만 접근할 수 있습니다.');
|
||||
}
|
||||
|
||||
$g5['title'] = '통계 솔루션 설치';
|
||||
include_once(G5_ADMIN_PATH . '/admin.head.php');
|
||||
|
||||
$install_result = null;
|
||||
$delete_result = null;
|
||||
$action = $_POST['action'] ?? '';
|
||||
|
||||
if ($action === 'install') {
|
||||
check_admin_token();
|
||||
try {
|
||||
$menu_msg = create_admin_menu_file();
|
||||
$install_result = ['menu' => $menu_msg];
|
||||
} catch (Exception $e) {
|
||||
$install_result['errors'][] = '설치 중 심각한 오류 발생: ' . $e->getMessage();
|
||||
}
|
||||
} else if ($action === 'delete') {
|
||||
check_admin_token();
|
||||
$delete_result = ['tables' => [], 'menu' => ''];
|
||||
$menu_file = G5_ADMIN_PATH . '/admin.menu720.statistics.php';
|
||||
if (file_exists($menu_file)) {
|
||||
if (@unlink($menu_file)) {
|
||||
$delete_result['menu'] = '메뉴 파일 삭제 성공';
|
||||
} else {
|
||||
$delete_result['menu'] = '메뉴 파일 삭제 실패 (권한 확인 필요)';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function create_admin_menu_file() {
|
||||
$source_file = __DIR__ . '/admin.menu720.statistics.php';
|
||||
$target_file = G5_ADMIN_PATH . '/admin.menu720.statistics.php';
|
||||
if (!file_exists($source_file)) return "실패 (메뉴 원본 파일 없음)";
|
||||
if (file_exists($target_file)) return "성공 (이미 존재함)";
|
||||
if (@copy($source_file, $target_file)) return "성공";
|
||||
return "실패 (파일 복사 오류)";
|
||||
}
|
||||
|
||||
$menu_file = G5_ADMIN_PATH . '/admin.menu720.statistics.php';
|
||||
$is_installed = file_exists($menu_file);
|
||||
?>
|
||||
|
||||
<style>
|
||||
.install-container { max-width: 800px; margin: 20px auto; padding: 20px; background: #fff; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); }
|
||||
.install-header { text-align: center; margin-bottom: 30px; padding-bottom: 20px; border-bottom: 2px solid #AA20FF; }
|
||||
.install-header h1 { color: #AA20FF; margin-bottom: 10px; }
|
||||
.feature-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 20px; margin: 30px 0; }
|
||||
.feature-card { padding: 20px; border: 1px solid #e0e0e0; border-radius: 8px; text-align: center; }
|
||||
.feature-card i { font-size: 2em; color: #AA20FF; margin-bottom: 10px; }
|
||||
.status-table { width: 100%; border-collapse: collapse; margin: 20px 0; }
|
||||
.status-table th, .status-table td { padding: 12px; text-align: left; border-bottom: 1px solid #ddd; }
|
||||
.status-table th { background-color: #f8f9fa; font-weight: bold; }
|
||||
.status-ok { color: #28a745; font-weight: bold; }
|
||||
.status-missing { color: #dc3545; font-weight: bold; }
|
||||
.install-btn { display: block; width: 200px; margin: 30px auto; padding: 15px 30px; background: #AA20FF; color: white; text-align: center; text-decoration: none; border-radius: 5px; font-size: 16px; font-weight: bold; border: none; cursor: pointer; transition: background-color 0.3s; }
|
||||
.install-btn:hover { background: #8A1ACC; color: white; }
|
||||
.install-btn:disabled { background: #ccc; cursor: not-allowed; }
|
||||
.alert { padding: 15px; margin: 20px 0; border-radius: 5px; }
|
||||
.alert-success { background-color: #d4edda; border: 1px solid #c3e6cb; color: #155724; }
|
||||
.alert-info { background-color: #d1ecf1; border: 1px solid #bee5eb; color: #0c5460; }
|
||||
.alert-danger { background-color: #f8d7da; border: 1px solid #f5c6cb; color: #721c24; }
|
||||
.btn-secondary { background: #6c757d; color: white; border-color: #6c757d; padding: 5px 10px; border-radius: 4px; text-decoration: none; }
|
||||
.btn-secondary:hover { background: #5a6268; }
|
||||
.btn-danger { background: #dc3545; color: white; border: none; padding: 10px 15px; border-radius: 4px; cursor: pointer; }
|
||||
.btn-danger:hover { background: #c82333; }
|
||||
.button-group { display: flex; justify-content: center; align-items: center; gap: 10px; }
|
||||
</style>
|
||||
|
||||
<div class="install-container">
|
||||
<div class="install-header">
|
||||
<h1><i class="fa fa-calendar-check"></i> 상담 예약 시스템</h1>
|
||||
<p>전문적인 상담 예약 관리 및 일정 관리 솔루션</p>
|
||||
</div>
|
||||
|
||||
<?php if ($install_result): ?>
|
||||
<div class="alert alert-success"><h4><i class="fa fa-check-circle"></i> 설치 작업 완료</h4><p>데이터베이스 및 기본 설정 설치가 완료되었습니다.</p><p><a href="./consultant_list.php" class="btn btn-primary">상담 예약 관리로 이동</a></p></div>
|
||||
<?php elseif ($delete_result): ?>
|
||||
<div class="alert alert-danger"><h4><i class="fa fa-trash"></i> 삭제 작업 완료</h4><p>솔루션 관련 데이터와 파일이 삭제되었습니다.</p><ul><?php foreach($delete_result['tables'] as $tbl) echo "<li>{$tbl} 테이블 삭제됨</li>"; ?><li><?php echo $delete_result['menu']; ?></li></ul></div>
|
||||
<?php elseif ($is_installed): ?>
|
||||
<div class="alert alert-success"><h4><i class="fa fa-check-circle"></i> 설치 완료</h4><p>상담 예약 시스템이 이미 설치되어 있습니다.</p><p><a href="./consultant_list.php" class="btn btn-primary">상담 예약 관리로 이동</a></p></div>
|
||||
<?php else: ?>
|
||||
<div class="alert alert-info"><h4><i class="fa fa-info-circle"></i> 설치 필요</h4><p>상담 예약 시스템을 사용하기 위해 설치가 필요합니다.</p></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<h3><i class="fa fa-database"></i> 설치 상태</h3>
|
||||
<?php if (!$is_installed): ?>
|
||||
<form method="post" onsubmit="return confirm('솔루션을 설치하시겠습니까?');">
|
||||
<input type="hidden" name="action" value="install">
|
||||
<input type="hidden" name="token" value="<?php echo get_token(); ?>">
|
||||
<button type="submit" class="install-btn"><i class="fa fa-download"></i> 솔루션 설치하기</button>
|
||||
</form>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($is_installed && !$install_result && !$delete_result): ?>
|
||||
<div class="button-group" style="text-align: center; margin-top: 20px;">
|
||||
<form method="post" onsubmit="return confirm('정말로 솔루션을 삭제하시겠습니까? 모든 관련 데이터와 파일이 영구적으로 삭제됩니다.');">
|
||||
<input type="hidden" name="action" value="delete">
|
||||
<input type="hidden" name="token" value="<?php echo get_token(); ?>">
|
||||
<button type="submit" class="btn-danger"><i class="fa fa-trash"></i> 솔루션 삭제하기</button>
|
||||
</form>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<?php
|
||||
include_once(G5_ADMIN_PATH . '/admin.tail.php');
|
||||
?>
|
||||
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
$sub_menu = '720300';
|
||||
include_once('./_common.php');
|
||||
auth_check_menu($auth, $sub_menu, "r");
|
||||
|
||||
$g5['title'] = "검색어통계";
|
||||
include_once(G5_ADMIN_PATH.'/admin.head.php');
|
||||
?>
|
||||
|
||||
<link rel="stylesheet" href="statistics.css?ver=<?php echo G5_CSS_VER; ?>">
|
||||
|
||||
<div class="local_sch01 local_sch">
|
||||
<ul class="List">
|
||||
<li><a href="./keyword.php?type=today" class="btn_ov01">오늘</a></li>
|
||||
<li><a href="./keyword.php?type=yesterday" class="btn_ov02">어제</a></li>
|
||||
<li><a href="./keyword.php?type=week" class="btn_ov02">이번주</a></li>
|
||||
<li><a href="./keyword.php?type=month" class="btn_ov02">이번달</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div id="chart-type-selector" class="local_sch01 local_sch">
|
||||
<strong class="m-r-10">차트 모양</strong>
|
||||
<button type="button" class="btn_ov02" data-chart-type="bar">세로막대</button>
|
||||
<button type="button" class="btn_ov02" data-chart-type="horizontalBar">가로막대</button>
|
||||
<button type="button" class="btn_ov02" data-chart-type="line">선</button>
|
||||
</div>
|
||||
|
||||
<div class="chart-container">
|
||||
<canvas id="myChart"></canvas>
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||
<script src="statistics.js?ver=<?php echo G5_JS_VER; ?>"></script>
|
||||
|
||||
<?php
|
||||
include_once(G5_ADMIN_PATH.'/admin.tail.php');
|
||||
?>
|
||||
@@ -0,0 +1,80 @@
|
||||
.notbox { border:1px solid #e9e9e9; background:#f7f7f7; padding:10px; text-align:center; margin-bottom:10px; }
|
||||
.srchb { border:1px solid #e9e9e9; margin-bottom:10px; }
|
||||
.srchb table { width:100%; }
|
||||
.srchb td { padding:5px; }
|
||||
.srchb .ctlt { background:#f7f7f7; text-align:center; font-weight:bold; }
|
||||
.srchb .wbg { background:#fff; }
|
||||
|
||||
/* 💡 [핵심 수정] 통계 분류 탭 메뉴 스타일 복원 */
|
||||
.srchb .List {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
gap: 5px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.srchb .List li a {
|
||||
display: block;
|
||||
padding: 5px 10px;
|
||||
border: 1px solid #ddd;
|
||||
background: #f8f9fa;
|
||||
color: #333;
|
||||
text-decoration: none;
|
||||
}
|
||||
.srchb .List li a:hover {
|
||||
background: #e9ecef;
|
||||
}
|
||||
.srchb .List li a.btn_ov01 {
|
||||
background: #333;
|
||||
color: #fff;
|
||||
border-color: #000;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* 날짜 설정 및 차트 종류 버튼 스타일 */
|
||||
.set_day,
|
||||
#chart-type-selector .btn_03 {
|
||||
cursor: pointer !important;
|
||||
border: 1px solid #ccc;
|
||||
padding: 5px 10px;
|
||||
background: #f8f9fa;
|
||||
border-radius: 4px;
|
||||
text-decoration: none;
|
||||
color: #333;
|
||||
display: inline-block;
|
||||
}
|
||||
.set_day:hover,
|
||||
#chart-type-selector .btn_03:hover {
|
||||
background: #e9ecef;
|
||||
}
|
||||
.set_day.active,
|
||||
#chart-type-selector .btn_03.active {
|
||||
background: #333;
|
||||
color: #fff;
|
||||
border-color: #000;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.ntlt { font-weight:bold; margin:15px 0 5px; }
|
||||
|
||||
.ttlt { width:100%; border-collapse:collapse; border-top:2px solid #999; }
|
||||
.ttlt th, .ttlt td { padding:8px; border:1px solid #e9e9e9; }
|
||||
.ttlt thead tr { background:#f7f7f7; }
|
||||
.ttlt .rt { text-align:right; }
|
||||
.ttlt .ct { text-align:center; }
|
||||
|
||||
.graph { background:#eee; width:100%; height:15px; border-radius: 3px; overflow: hidden;}
|
||||
.graph .bar { display:block; background:#627ab8; height:100%; text-align:right; color:#fff; padding-right:5px; }
|
||||
|
||||
.chart-container {
|
||||
width: 100%;
|
||||
max-width: 1000px;
|
||||
margin: 30px auto;
|
||||
padding: 20px;
|
||||
background: #fff;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 5px rgba(0,0,0,0.05);
|
||||
}
|
||||
.empty-chart { text-align:center; padding: 50px; }
|
||||
@@ -0,0 +1,142 @@
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const ctx = document.getElementById('myChart');
|
||||
if (!ctx) return;
|
||||
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
let type = urlParams.get('type');
|
||||
|
||||
const currentPage = window.location.pathname.split('/').pop();
|
||||
if (!type) {
|
||||
if (currentPage.includes('visit_list.php')) type = 'time';
|
||||
else if (currentPage.includes('keyword.php')) type = 'today';
|
||||
else if (currentPage.includes('board.php')) type = 'board_view';
|
||||
else if (currentPage.includes('banner.php')) type = 'position';
|
||||
else type = 'summary';
|
||||
}
|
||||
|
||||
let myChart;
|
||||
let currentChartData;
|
||||
|
||||
function createChart(chartType, chartData) {
|
||||
if (myChart) {
|
||||
myChart.destroy();
|
||||
}
|
||||
|
||||
const isHorizontal = chartType === 'horizontalBar';
|
||||
const finalChartType = isHorizontal ? 'bar' : chartType;
|
||||
|
||||
const options = {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
plugins: {
|
||||
legend: { position: 'top' },
|
||||
title: { display: true, text: chartData.title }
|
||||
},
|
||||
indexAxis: isHorizontal ? 'y' : 'x',
|
||||
};
|
||||
|
||||
if (['pie', 'doughnut', 'radar'].includes(chartType)) {
|
||||
const colors = [
|
||||
'rgba(255, 99, 132, 0.5)', 'rgba(54, 162, 235, 0.5)', 'rgba(255, 206, 86, 0.5)',
|
||||
'rgba(75, 192, 192, 0.5)', 'rgba(153, 102, 255, 0.5)', 'rgba(255, 159, 64, 0.5)',
|
||||
'rgba(255, 99, 132, 0.8)', 'rgba(54, 162, 235, 0.8)', 'rgba(255, 206, 86, 0.8)',
|
||||
'rgba(75, 192, 192, 0.8)'
|
||||
];
|
||||
chartData.datasets[0].backgroundColor = colors;
|
||||
} else {
|
||||
chartData.datasets[0].backgroundColor = 'rgba(54, 162, 235, 0.5)';
|
||||
}
|
||||
|
||||
myChart = new Chart(ctx, {
|
||||
type: finalChartType,
|
||||
data: {
|
||||
labels: chartData.labels,
|
||||
datasets: chartData.datasets
|
||||
},
|
||||
options: options
|
||||
});
|
||||
}
|
||||
|
||||
function fetchData(newType, startDate, endDate) {
|
||||
const requestType = newType || type;
|
||||
|
||||
let url = `ajax.data.php?type=${requestType}`;
|
||||
if (startDate) url += `&start_day=${startDate}`;
|
||||
if (endDate) url += `&end_day=${endDate}`;
|
||||
|
||||
fetch(url)
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
const chartContainer = document.querySelector('.chart-container');
|
||||
const dataTableBody = document.getElementById('data-table-body');
|
||||
|
||||
if (data.labels && data.labels.length > 0) {
|
||||
chartContainer.style.display = 'block';
|
||||
currentChartData = data;
|
||||
const initialChartType = data.type || 'bar';
|
||||
createChart(initialChartType, currentChartData);
|
||||
|
||||
// 테이블 데이터 생성
|
||||
if (dataTableBody) {
|
||||
let tableHtml = '';
|
||||
data.list.forEach(item => {
|
||||
tableHtml += `<tr>
|
||||
<td class="ct">${item.key}</td>
|
||||
<td><div class="graph"><span class="bar" style="width:${item.bar_width}%"></span></div></td>
|
||||
<td class="rt">${item.count}</td>
|
||||
<td class="rt">${item.rate}</td>
|
||||
</tr>`;
|
||||
});
|
||||
dataTableBody.innerHTML = tableHtml;
|
||||
}
|
||||
|
||||
} else {
|
||||
if(myChart) myChart.destroy();
|
||||
chartContainer.innerHTML = '<div class="empty-chart" style="text-align:center; padding: 50px;">표시할 데이터가 없습니다.</div>';
|
||||
if(dataTableBody) dataTableBody.innerHTML = '<tr><td colspan="4" class="empty_table">데이터가 없습니다.</td></tr>';
|
||||
}
|
||||
})
|
||||
.catch(error => console.error('Error fetching chart data:', error));
|
||||
}
|
||||
|
||||
$('#chart-type-selector').on('click', 'a.chart-btn', function(e) {
|
||||
e.preventDefault();
|
||||
const newChartType = $(this).data('chart-type');
|
||||
if (newChartType && currentChartData) {
|
||||
createChart(newChartType, currentChartData);
|
||||
$('.chart-btn').removeClass('active');
|
||||
$(this).addClass('active');
|
||||
}
|
||||
});
|
||||
|
||||
function updateData() {
|
||||
const newType = new URLSearchParams(window.location.search).get('type') || type;
|
||||
const startDate = $('#start_day').val();
|
||||
const endDate = $('#end_day').val();
|
||||
fetchData(newType, startDate, endDate);
|
||||
}
|
||||
|
||||
$('.local_sch a').on('click', function(e) {
|
||||
e.preventDefault();
|
||||
const url = new URL(this.href);
|
||||
const newType = url.searchParams.get('type');
|
||||
|
||||
history.pushState({type: newType}, '', this.href);
|
||||
|
||||
$('.local_sch a').removeClass('btn_ov01').addClass('btn_ov02');
|
||||
$(this).removeClass('btn_ov02').addClass('btn_ov01');
|
||||
|
||||
$('input[name="type"]').val(newType);
|
||||
type = newType; // 현재 type 업데이트
|
||||
updateData();
|
||||
});
|
||||
|
||||
$('#btn_search').on('click', function() {
|
||||
updateData();
|
||||
});
|
||||
|
||||
// 초기 데이터 로드
|
||||
if (type !== 'summary') {
|
||||
updateData();
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,145 @@
|
||||
<?php
|
||||
$sub_menu = '720200';
|
||||
include_once('./_common.php');
|
||||
auth_check_menu($auth, $sub_menu, "r");
|
||||
|
||||
$g5['title'] = "상세 접속통계";
|
||||
include_once(G5_ADMIN_PATH.'/admin.head.php');
|
||||
|
||||
$type = isset($_GET['type']) ? preg_replace('/[^a-z]/i', '', $_GET['type']) : 'time';
|
||||
?>
|
||||
|
||||
<link rel="stylesheet" href="statistics.css?ver=<?php echo G5_CSS_VER; ?>">
|
||||
<!--<link rel="stylesheet" href="http://code.jquery.com/ui/1.13.2/themes/base/jquery-ui.css">-->
|
||||
|
||||
<div class="notbox">
|
||||
<p>차트를 통해 기간별, 유형별 방문자 현황을 시각적으로 분석할 수 있습니다.</p>
|
||||
</div>
|
||||
|
||||
<dl class="srchb lnb4_col bg2_col">
|
||||
<table class="bg_col">
|
||||
<tbody><tr>
|
||||
<td class="ctlt bno">통계분류</td>
|
||||
<td class="wbg pl7">
|
||||
<ul class="List">
|
||||
<li><a href="./" class="btn_ov02">요약</a></li>
|
||||
<li><a href="./visit_list.php?type=time" class="<?php echo ($type == 'time') ? 'btn_ov01' : 'btn_ov02'; ?>">시간별</a></li>
|
||||
<li><a href="./visit_list.php?type=week" class="<?php echo ($type == 'week') ? 'btn_ov01' : 'btn_ov02'; ?>">요일별</a></li>
|
||||
<li><a href="./visit_list.php?type=date" class="<?php echo ($type == 'date') ? 'btn_ov01' : 'btn_ov02'; ?>">일별</a></li>
|
||||
<li><a href="./visit_list.php?type=month" class="<?php echo ($type == 'month') ? 'btn_ov01' : 'btn_ov02'; ?>">월별</a></li>
|
||||
<li><a href="./visit_list.php?type=domain" class="<?php echo ($type == 'domain') ? 'btn_ov01' : 'btn_ov02'; ?>">도메인</a></li>
|
||||
<li><a href="./visit_list.php?type=ip" class="<?php echo ($type == 'ip') ? 'btn_ov01' : 'btn_ov02'; ?>">접속IP</a></li>
|
||||
<li><a href="./visit_list.php?type=browser" class="<?php echo ($type == 'browser') ? 'btn_ov01' : 'btn_ov02'; ?>">브라우저</a></li>
|
||||
<li><a href="./visit_list.php?type=os" class="<?php echo ($type == 'os') ? 'btn_ov01' : 'btn_ov02'; ?>">운영체제</a></li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody></table>
|
||||
</dl>
|
||||
|
||||
|
||||
<div class="pt5">
|
||||
<dl class="srchb lnb4_col bg2_col">
|
||||
<form name="statisticsSearchFrm" method="GET" id="statisticsSearchFrm">
|
||||
<input type="hidden" name="type" value="<?php echo $type; ?>">
|
||||
<dl class="tc pd7 wbg">
|
||||
<input name="start_day" type="date" class="frm_input" id="start_day" value="<?php echo date('Y-m-d'); ?>"> ~
|
||||
<input name="end_day" type="date" class="frm_input" id="end_day" value="<?php echo date('Y-m-d'); ?>">
|
||||
<a class="btn btn_03 set_day" data-date="today">오늘</a>
|
||||
<a class="btn btn_03 set_day" data-date="week">이번주</a>
|
||||
<a class="btn btn_03 set_day" data-date="month">이번달</a>
|
||||
<a class="btn btn_03 set_day" data-date="7day">1주일</a>
|
||||
<a class="btn btn_03 set_day" data-date="15day">15일</a>
|
||||
<a class="btn btn_03 set_day" data-date="30day">1개월</a>
|
||||
<a class="btn btn_03 set_day" data-date="60day">3개월</a>
|
||||
<a class="btn btn_03 set_day" data-date="120day">6개월</a>
|
||||
|
||||
<button type="button" id="btn_search" class="btn_submit btn">검색</button>
|
||||
</dl>
|
||||
</form>
|
||||
</dl>
|
||||
</div>
|
||||
|
||||
<div id="chart-type-selector" class="local_sch01 local_sch">
|
||||
<strong>차트 모양</strong>
|
||||
<a class="btn btn_03 chart-btn active" data-chart-type="bar">세로막대</a>
|
||||
<a class="btn btn_03 chart-btn" data-chart-type="line">선그래프</a>
|
||||
<a class="btn btn_03 chart-btn" data-chart-type="pie">원형</a>
|
||||
<a class="btn btn_03 chart-btn" data-chart-type="doughnut">도넛</a>
|
||||
<a class="btn btn_03 chart-btn" data-chart-type="radar">레이더</a>
|
||||
</div>
|
||||
|
||||
<div class="chart-container">
|
||||
<canvas id="myChart"></canvas>
|
||||
</div>
|
||||
|
||||
<dl class="ntlt lnb_col"><img src="img/bul_10.png" class="t"> 상세 데이터</dl>
|
||||
<table width="100%" class="access ttlt tf">
|
||||
<thead>
|
||||
<tr class="bg">
|
||||
<th>항목</th>
|
||||
<th>그래프</th>
|
||||
<th>방문자수</th>
|
||||
<th>비율(%)</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="data-table-body">
|
||||
<!-- 데이터가 여기에 동적으로 추가됩니다. -->
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||
<script src="statistics.js?ver=<?php echo G5_JS_VER; ?>"></script>
|
||||
|
||||
<script src="https://code.jquery.com/ui/1.13.2/jquery-ui.js"></script>
|
||||
<script>
|
||||
$(function(){
|
||||
$(".hasDatepicker").datepicker({
|
||||
dateFormat: "yy-mm-dd"
|
||||
});
|
||||
|
||||
$('.set_day').click(function(){
|
||||
var term = $(this).data('date');
|
||||
var today = new Date();
|
||||
var start_day, end_day;
|
||||
|
||||
end_day = $.datepicker.formatDate('yy-mm-dd', today);
|
||||
|
||||
switch(term) {
|
||||
case 'today':
|
||||
start_day = end_day;
|
||||
break;
|
||||
case 'week':
|
||||
today.setDate(today.getDate() - today.getDay() + (today.getDay() === 0 ? -6 : 1));
|
||||
start_day = $.datepicker.formatDate('yy-mm-dd', today);
|
||||
break;
|
||||
case 'month':
|
||||
start_day = $.datepicker.formatDate('yy-mm-01', today);
|
||||
break;
|
||||
default:
|
||||
var days = parseInt(term.replace('day', ''));
|
||||
today.setDate(today.getDate() - (days - 1));
|
||||
start_day = $.datepicker.formatDate('yy-mm-dd', today);
|
||||
break;
|
||||
}
|
||||
|
||||
$('#start_day').val(start_day);
|
||||
$('#end_day').val(end_day);
|
||||
|
||||
// 💡 [핵심 수정] 선택된 버튼 스타일 적용
|
||||
$('.set_day').removeClass('active');
|
||||
$(this).addClass('active');
|
||||
});
|
||||
// 차트 종류 변경 버튼 이벤트
|
||||
$('.chart-btn').click(function(){
|
||||
var chartType = $(this).data('chart-type');
|
||||
// createChart(chartType);
|
||||
$('.chart-btn').removeClass('active');
|
||||
$(this).addClass('active');
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<?php
|
||||
include_once(G5_ADMIN_PATH.'/admin.tail.php');
|
||||
?>
|
||||
Reference in New Issue
Block a user