1288 lines
50 KiB
PHP
1288 lines
50 KiB
PHP
|
|
<?php
|
|
require_once __DIR__ . '/../bbs/auth.php';
|
|
edu_require_login();
|
|
|
|
if (session_status() === PHP_SESSION_NONE) {
|
|
session_start();
|
|
}
|
|
require_once __DIR__ . '/../bbs/db_conn.php';
|
|
$pdoBiz = db_conn();
|
|
$pdoBiz->exec("SET NAMES 'utf8mb4'");
|
|
|
|
$bizMemberId = (string)($_SESSION['member_id'] ?? '');
|
|
$bizSysCompCode = (string)($_SESSION['sys_comp_code'] ?? '');
|
|
$bizLoggedIn = $bizMemberId !== '';
|
|
|
|
// 선택된 월 (기본값: 현재 월)
|
|
$selectedMonth = $_GET['month'] ?? date('Y-m');
|
|
if (!preg_match('/^\d{4}-\d{2}$/', $selectedMonth)) {
|
|
$selectedMonth = date('Y-m');
|
|
}
|
|
$selYear = substr($selectedMonth, 0, 4);
|
|
$selMonth = ltrim(substr($selectedMonth, 5, 2), '0');
|
|
|
|
// ── 비즈트렌드 컨텐츠 조회 ──
|
|
$bizData = [];
|
|
try {
|
|
$sql = "
|
|
SELECT
|
|
c.content_id,
|
|
c.title,
|
|
c.description,
|
|
c.description1,
|
|
c.description2,
|
|
c.content_url,
|
|
c.start_date,
|
|
c.category_group,
|
|
c.image_name,
|
|
c.issue_type_code,
|
|
CASE WHEN c.issue_type_code IS NOT NULL AND c.issue_type_code != ''
|
|
THEN fn_get_code_name(c.issue_type_code)
|
|
ELSE NULL END AS badge_name
|
|
";
|
|
|
|
if ($bizLoggedIn) {
|
|
$sql .= ",
|
|
lh.watch_tm,
|
|
lh.content_tm,
|
|
CASE WHEN cw.content_id IS NOT NULL AND cw.is_active = '1' THEN 1 ELSE 0 END AS is_liked
|
|
FROM edu_contents c
|
|
LEFT JOIN edu_learning_histories lh
|
|
ON lh.content_id = c.content_id
|
|
AND lh.member_id = ?
|
|
AND lh.sys_comp_code = ?
|
|
LEFT JOIN edu_content_wishlist cw
|
|
ON cw.content_id = c.content_id
|
|
AND cw.member_id = ?
|
|
AND cw.sys_comp_code = ?
|
|
AND cw.is_active = '1'
|
|
";
|
|
} else {
|
|
$sql .= ",
|
|
NULL AS watch_tm,
|
|
NULL AS content_tm,
|
|
0 AS is_liked
|
|
FROM edu_contents c
|
|
";
|
|
}
|
|
|
|
$sql .= "
|
|
WHERE c.category_code = 'CA10006'
|
|
AND c.is_active = 1
|
|
AND c.start_date IS NOT NULL
|
|
AND c.start_date <= CURDATE()
|
|
ORDER BY c.start_date ASC, c.category_group ASC
|
|
";
|
|
|
|
$stmt = $pdoBiz->prepare($sql);
|
|
if ($bizLoggedIn) {
|
|
$stmt->execute([$bizMemberId, $bizSysCompCode, $bizMemberId, $bizSysCompCode]);
|
|
} else {
|
|
$stmt->execute([]);
|
|
}
|
|
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
|
|
|
foreach ($rows as $row) {
|
|
$dateKey = $row['start_date'];
|
|
|
|
// YouTube videoId 추출
|
|
$videoId = '';
|
|
$url = $row['content_url'] ?? '';
|
|
if (preg_match('/(?:youtube\.com\/watch\?v=|youtu\.be\/|youtube\.com\/embed\/)([a-zA-Z0-9_-]+)/', $url, $m)) {
|
|
$videoId = $m[1];
|
|
} else {
|
|
$videoId = $url;
|
|
}
|
|
|
|
// description, description2 → list 배열
|
|
$list = [];
|
|
if (!empty($row['description1'])) {
|
|
$lines = array_filter(array_map('trim', preg_split('/[\r\n]+/', $row['description1'])));
|
|
$list = array_merge($list, array_values($lines));
|
|
}
|
|
if (!empty($row['description2'])) {
|
|
$lines = array_filter(array_map('trim', preg_split('/[\r\n]+/', $row['description2'])));
|
|
$list = array_merge($list, array_values($lines));
|
|
}
|
|
|
|
// gauge (시청 진행률)
|
|
$gauge = null;
|
|
$watchTm = (int)($row['watch_tm'] ?? 0);
|
|
$contentTm = (int)($row['content_tm'] ?? 0);
|
|
if ($contentTm > 0 && $watchTm > 0) {
|
|
$gauge = min(100, round($watchTm / $contentTm * 100));
|
|
}
|
|
|
|
// 이미지 경로 (edu/uploads/biztrend/ 폴더 기준)
|
|
$imageData = null;
|
|
if (!empty($row['image_name'])) {
|
|
$imageData = '/uploads/biztrend/' . $row['image_name'];
|
|
}
|
|
|
|
$entry = [
|
|
'contentId' => $row['content_id'] ?? '',
|
|
'title' => $row['title'] ?? '',
|
|
'list' => $list,
|
|
'videoId' => $videoId,
|
|
'contentUrl' => $row['content_url'] ?? '',
|
|
'description'=> $row['description'] ?? '',
|
|
'gauge' => $gauge,
|
|
'badge' => $row['badge_name'] ?: null,
|
|
'liked' => (bool)($row['is_liked'] ?? false),
|
|
'image' => $imageData,
|
|
'watchTm' => (int)($row['watch_tm'] ?? 0),
|
|
'contentTm' => (int)($row['content_tm'] ?? 0)
|
|
];
|
|
|
|
if (!isset($bizData[$dateKey])) {
|
|
$bizData[$dateKey] = [];
|
|
}
|
|
$bizData[$dateKey][] = $entry;
|
|
}
|
|
} catch (Exception $e) {
|
|
$bizData = [];
|
|
$bizError = $e->getMessage();
|
|
}
|
|
|
|
// ── 추천강의: 최근 업로드된 비즈트렌드 영상 8개 ──
|
|
$recentBizData = [];
|
|
try {
|
|
$recentSql = "
|
|
SELECT
|
|
c.content_id,
|
|
c.title,
|
|
c.description,
|
|
c.content_url,
|
|
c.start_date,
|
|
c.image_name
|
|
";
|
|
if ($bizLoggedIn) {
|
|
$recentSql .= ",
|
|
lh.watch_tm,
|
|
lh.content_tm
|
|
FROM edu_contents c
|
|
LEFT JOIN edu_learning_histories lh
|
|
ON lh.content_id = c.content_id
|
|
AND lh.member_id = ?
|
|
AND lh.sys_comp_code = ?
|
|
";
|
|
} else {
|
|
$recentSql .= ",
|
|
NULL AS watch_tm,
|
|
NULL AS content_tm
|
|
FROM edu_contents c
|
|
";
|
|
}
|
|
$recentSql .= "
|
|
WHERE c.category_code = 'CA10006'
|
|
AND c.is_active = 1
|
|
AND c.start_date <= CURDATE()
|
|
ORDER BY c.start_date DESC
|
|
LIMIT 8
|
|
";
|
|
$recentStmt = $pdoBiz->prepare($recentSql);
|
|
if ($bizLoggedIn) {
|
|
$recentStmt->execute([$bizMemberId, $bizSysCompCode]);
|
|
} else {
|
|
$recentStmt->execute([]);
|
|
}
|
|
$recentRows = $recentStmt->fetchAll(PDO::FETCH_ASSOC);
|
|
|
|
foreach ($recentRows as $row) {
|
|
$videoId = '';
|
|
$url = $row['content_url'] ?? '';
|
|
if (preg_match('/(?:youtube\.com\/watch\?v=|youtu\.be\/|youtube\.com\/embed\/)([a-zA-Z0-9_-]+)/', $url, $m)) {
|
|
$videoId = $m[1];
|
|
} else {
|
|
$videoId = $url;
|
|
}
|
|
$imageData = null;
|
|
if (!empty($row['image_name'])) {
|
|
$imageData = '/uploads/biztrend/' . $row['image_name'];
|
|
}
|
|
$recentBizData[] = [
|
|
'contentId' => $row['content_id'] ?? '',
|
|
'title' => $row['title'] ?? '',
|
|
'videoId' => $videoId,
|
|
'contentUrl' => $row['content_url'] ?? '',
|
|
'description'=> $row['description'] ?? '',
|
|
'image' => $imageData,
|
|
'watchTm' => (int)($row['watch_tm'] ?? 0),
|
|
'contentTm' => (int)($row['content_tm'] ?? 0),
|
|
];
|
|
}
|
|
} catch (Exception $e) {
|
|
$recentBizData = [];
|
|
}
|
|
?>
|
|
<!doctype html>
|
|
<html lang="ko">
|
|
<head>
|
|
<?php include(__DIR__ . "/_include/_head.php") ?>
|
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
<link href="https://fonts.googleapis.com/css2?family=Caveat:wght@700&display=swap" rel="stylesheet">
|
|
</head>
|
|
<body>
|
|
<div class="wrap biztrend">
|
|
<?php include(__DIR__ . "/_include/_header.php") ?>
|
|
|
|
<!-- ===================== -->
|
|
<!-- 컨테이너 -->
|
|
<!-- ===================== -->
|
|
<div class="container">
|
|
<div class="biztrend-inner">
|
|
<!-- 페이지 헤더: 브레드크럼 + 제목 -->
|
|
<div class="page-header">
|
|
<ul class="breadcrumb" aria-label="breadcrumb">
|
|
<li><a href="./index.php">홈</a></li>
|
|
<li><a href="./biztrend.php">비즈트렌드</a></li>
|
|
<li><span class="current">성공예감&별책부록</span></li>
|
|
</ul>
|
|
<div class="page-title">
|
|
<h3>성공예감<span>&</span>별책부록</h3>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ===================== -->
|
|
<!-- 비즈트렌드 메인 섹션 -->
|
|
<!-- ===================== -->
|
|
<section class="biztrend-wrap">
|
|
<div class="biztrend-inner">
|
|
<!-- 헤더: 소개 + 연·월 선택 -->
|
|
<div class="biztrend-header">
|
|
<div class="biztrend-sub-title">
|
|
<div class="biztrend-photo">
|
|
<img src="/img/biztrend/img_profile.png" alt="">
|
|
</div>
|
|
<p class="biztrend-intro-desc">
|
|
KBS 제1라디오 '성공예감&별책부록'을 통해 <br>
|
|
<em>경제 정보를 쉽게 접하고, 생활 속 경제 흐름</em>을 알아보세요.
|
|
</p>
|
|
</div>
|
|
|
|
<!-- 연·월 선택기 -->
|
|
<div class="article-grid-date">
|
|
<button type="button" class="date-select-trigger datepicker-trigger" id="datepicker-trigger" title="연도·월 선택" aria-label="연도·월 선택" aria-haspopup="listbox" aria-expanded="false">
|
|
<span class="date-select-label">
|
|
<span class="date-year"><span class="date-select-year"><?= $selYear ?></span>년</span>
|
|
<span class="date-month"><em class="date-select-month"><?= $selMonth ?></em>월</span>
|
|
</span>
|
|
<span class="date-select-icon" aria-hidden="true"></span>
|
|
</button>
|
|
<input type="hidden" class="date-select-value" value="<?= htmlspecialchars($selectedMonth, ENT_QUOTES, 'UTF-8') ?>">
|
|
|
|
<!-- 날짜피커 드롭다운 (버튼 바로 아래 노출) -->
|
|
<div class="datepicker-dropdown" id="datepicker-dropdown" role="listbox" aria-hidden="true">
|
|
<div class="datepicker-dropdown-inner">
|
|
<div class="datepicker-wheels">
|
|
<div class="datepicker-wheel-wrap">
|
|
<span class="datepicker-wheel-label">연도</span>
|
|
<div class="datepicker-wheel" aria-label="연도 선택">
|
|
<div class="datepicker-wheel-track" data-wheel="year"></div>
|
|
</div>
|
|
</div>
|
|
<div class="datepicker-wheel-wrap">
|
|
<span class="datepicker-wheel-label">월</span>
|
|
<div class="datepicker-wheel" aria-label="월 선택">
|
|
<div class="datepicker-wheel-track" data-wheel="month"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="datepicker-actions">
|
|
<button type="button" class="btn-datepicker-cancel">취소</button>
|
|
<button type="button" class="btn-datepicker-confirm">확인</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="biztrend-auto">
|
|
|
|
<!-- ===================== -->
|
|
<!-- 기사 달력 그리드 -->
|
|
<!-- 월~토 6열, 각 날짜당 1개 카드 -->
|
|
<!-- ===================== -->
|
|
<div class="article-grid">
|
|
<div class="article-grid-inner">
|
|
<div class="article-grid-head">
|
|
<ul class="article-grid-days" aria-label="요일">
|
|
<li>월</li>
|
|
<li>화</li>
|
|
<li>수</li>
|
|
<li>목</li>
|
|
<li>금</li>
|
|
<li>토</li>
|
|
</ul>
|
|
</div>
|
|
<div class="article-grid-body">
|
|
<!-- JS(biztrend.js)에서 현재 달 기준으로 동적 생성 -->
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ============================================ -->
|
|
<!-- 영상 모달 댓글 스타일 -->
|
|
<!-- ============================================ -->
|
|
<style>
|
|
/* #biztrendVideoModal .comment-list { list-style: none; margin: 0; padding: 0; }
|
|
#biztrendVideoModal .comment-list li { padding: 12px 0; border-bottom: 1px solid rgba(255,255,255,.08); }
|
|
#biztrendVideoModal .comment-list li.empty { text-align: center; color: #888; padding: 20px 0; border-bottom: none; }
|
|
#biztrendVideoModal .comment-list .comment-header { display: flex; align-items: center; gap: 6px; margin-bottom: 6px; }
|
|
#biztrendVideoModal .comment-list .comment-header .user-icon { width: 20px; height: 20px; border-radius: 50%; background: #555; display: flex; align-items: center; justify-content: center; flex-shrink: 0; }
|
|
#biztrendVideoModal .comment-list .comment-header .user-icon svg { width: 12px; height: 12px; fill: #ccc; }
|
|
#biztrendVideoModal .comment-list .comment-header .comment-author { font-size: 13px; color: #ccc; }
|
|
#biztrendVideoModal .comment-list .comment-content { font-size: 14px; color: #fff; line-height: 1.5; margin-bottom: 6px; word-break: break-word; text-align: left; }
|
|
#biztrendVideoModal .comment-list .comment-body { display: flex; align-items: flex-start; justify-content: space-between; gap: 8px; margin-bottom: 6px; }
|
|
#biztrendVideoModal .comment-list .comment-body .comment-content { flex: 1; min-width: 0; margin-bottom: 0; }
|
|
#biztrendVideoModal .comment-list .comment-body .comment-actions { flex-shrink: 0; align-self: center; }
|
|
#biztrendVideoModal .comment-list .comment-footer { display: flex; align-items: center; }
|
|
#biztrendVideoModal .comment-list .comment-date { font-size: 12px; color: #888; }
|
|
#biztrendVideoModal .comment-list .comment-actions { display: flex; gap: 4px; }
|
|
#biztrendVideoModal .comment-list .comment-actions button { padding: 3px 8px; font-size: 12px; border: 1px solid #555; border-radius: 3px; background: #333; color: #ddd; cursor: pointer; white-space: nowrap; }
|
|
#biztrendVideoModal .comment-list .comment-actions button:hover { background: #555; color: #fff; }
|
|
#biztrendVideoModal .video-list { flex: 1; overflow-y: auto; min-height: 0; }
|
|
#biztrendVideoModal .video-list ul { list-style: none; margin: 0; padding: 0; } */
|
|
|
|
/* 관련영상 목록 스크롤 */
|
|
#biztrendVideoModal .video-side .video-list {
|
|
overflow: hidden;
|
|
}
|
|
|
|
#biztrendVideoModal #bizRecommendedList {
|
|
max-height: none;
|
|
overflow-y: auto;
|
|
overflow-x: hidden;
|
|
overscroll-behavior: contain;
|
|
padding-right: 4px;
|
|
}
|
|
|
|
#biztrendVideoModal #bizRecommendedList::-webkit-scrollbar {
|
|
width: 6px;
|
|
}
|
|
|
|
#biztrendVideoModal #bizRecommendedList::-webkit-scrollbar-thumb {
|
|
background: rgba(255, 255, 255, 0.35);
|
|
border-radius: 999px;
|
|
}
|
|
|
|
#biztrendVideoModal #bizRecommendedList::-webkit-scrollbar-track {
|
|
background: rgba(255, 255, 255, 0.08);
|
|
}
|
|
|
|
/* 1024px 이하: 댓글 패널(슬라이드업) 토글 */
|
|
@media (max-width: 1024px) {
|
|
#biztrendVideoModal .comment-wrap {
|
|
position: fixed;
|
|
left: 0;
|
|
right: 0;
|
|
bottom: 0;
|
|
width: 100%;
|
|
max-height: min(70vh, 520px);
|
|
z-index: 1002;
|
|
transform: translateY(100%);
|
|
transition: transform 220ms ease;
|
|
}
|
|
|
|
#biztrendVideoModal.is-comment-open .comment-wrap {
|
|
transform: translateY(0);
|
|
}
|
|
}
|
|
</style>
|
|
|
|
<!-- ============================================ -->
|
|
<!-- 영상 모달 -->
|
|
<!-- ============================================ -->
|
|
<div class="modal video" id="biztrendVideoModal" style="display:none">
|
|
<div class="modal-content">
|
|
<div class="modal-body">
|
|
<div class="video-contents">
|
|
<div class="video-area">
|
|
<div class="video-box">
|
|
<div id="bizVideoPlayer"></div>
|
|
</div>
|
|
</div>
|
|
<div class="video-info">
|
|
<div class="tit-box">
|
|
<div class="meta">
|
|
<span id="bizModalCategoryName"></span>
|
|
<em id="bizModalSubCategory"></em>
|
|
</div>
|
|
<h3 id="bizModalTitle"></h3>
|
|
<div class="tit-right">
|
|
<button class="btn-comment"><i class="ico-comment"></i>0</button>
|
|
<label class="bookmark" for="biztrend-bookmark-chk">
|
|
<input id="biztrend-bookmark-chk" type="checkbox" value="" />
|
|
<span>북마크</span>
|
|
</label>
|
|
</div>
|
|
</div>
|
|
<div class="desc" id="bizModalDesc"></div>
|
|
</div>
|
|
</div>
|
|
<div class="video-side">
|
|
<div class="video-header">
|
|
<h5 class="tit">관련영상</h5>
|
|
<!-- <span class="badge" id="bizModalBadge">비즈트렌드</span> -->
|
|
<span class="close">×</span>
|
|
</div>
|
|
<div class="video-list">
|
|
<ul id="bizRecommendedList"></ul>
|
|
</div>
|
|
<div class="comment-wrap">
|
|
<!-- 🔥 드래그 리사이저 추가 -->
|
|
<div class="comment-resizer">
|
|
<div class="resizer-handle"></div>
|
|
</div>
|
|
<div class="comment-list-wrap">
|
|
<ul class="comment-list">
|
|
<li class="empty">등록된 댓글이 없습니다.</li>
|
|
</ul>
|
|
</div>
|
|
<div class="comment-box">
|
|
<textarea placeholder="댓글을 작성해주세요"></textarea>
|
|
<div class="btn-area">
|
|
<button class="btn-cancel" disabled>취소</button>
|
|
<button class="btn-save" disabled>등록</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script src="/js/biztrend.js?ver=1.1" defer></script>
|
|
<script id="biztrend-data" type="application/json"><?= json_encode($bizData, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ?></script>
|
|
<script id="biztrend-recent" type="application/json"><?= json_encode($recentBizData, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ?></script>
|
|
<?php if (!empty($bizError)): ?>
|
|
<script>console.error('[biztrend] DB Error:', <?= json_encode($bizError) ?>);</script>
|
|
<?php endif; ?>
|
|
<?php if (empty($bizData)): ?>
|
|
<script>console.warn('[biztrend] No data loaded for month:', <?= json_encode($selectedMonth) ?>);</script>
|
|
<?php endif; ?>
|
|
|
|
<!-- ============================================ -->
|
|
<!-- 영상 모달 JS -->
|
|
<!-- ============================================ -->
|
|
<script>
|
|
(function () {
|
|
var API_BASE = '/bbs/api';
|
|
var $modal = $('#biztrendVideoModal');
|
|
var currentContentId = null;
|
|
var editingCommentId = null;
|
|
var commentToggleCleanup = null;
|
|
|
|
// ── YouTube IFrame API 시청 추적 변수 ──
|
|
var ytPlayer = null;
|
|
var trackingInterval = null;
|
|
var lastSaveTime = 0;
|
|
var lastCurrentTime = 0;
|
|
|
|
// YouTube IFrame API 스크립트 로드
|
|
if (!document.getElementById('youtube-iframe-api')) {
|
|
var ytScript = document.createElement('script');
|
|
ytScript.id = 'youtube-iframe-api';
|
|
ytScript.src = 'https://www.youtube.com/iframe_api';
|
|
ytScript.async = true;
|
|
document.head.appendChild(ytScript);
|
|
}
|
|
|
|
// ── YouTube 비디오 ID 추출 ──
|
|
function extractVideoId(url) {
|
|
if (!url) return '';
|
|
var videoId = url;
|
|
if (url.indexOf('youtube.com/watch') > -1) {
|
|
try { videoId = new URL(url).searchParams.get('v') || url; } catch (e) {}
|
|
} else if (url.indexOf('youtu.be/') > -1) {
|
|
videoId = url.split('youtu.be/')[1].split(/[?&#]/)[0];
|
|
} else if (url.indexOf('youtube.com/embed/') > -1) {
|
|
videoId = url.split('youtube.com/embed/')[1].split(/[?&#]/)[0];
|
|
}
|
|
if (videoId && videoId.indexOf('/') === -1 && videoId.indexOf('.') === -1) {
|
|
return videoId;
|
|
}
|
|
return '';
|
|
}
|
|
|
|
// ── YT.Player 생성 ──
|
|
function initYTPlayer(videoId, startSeconds) {
|
|
destroyYTPlayer();
|
|
startSeconds = Math.floor(startSeconds || 0);
|
|
|
|
var createPlayer = function () {
|
|
ytPlayer = new YT.Player('bizVideoPlayer', {
|
|
height: '100%',
|
|
width: '100%',
|
|
videoId: videoId,
|
|
playerVars: {
|
|
autoplay: 1, controls: 1, rel: 0,
|
|
modestbranding: 1, enablejsapi: 1, start: startSeconds
|
|
},
|
|
events: {
|
|
'onReady': function (event) {
|
|
if (event.target && typeof event.target.playVideo === 'function') {
|
|
event.target.playVideo();
|
|
}
|
|
},
|
|
'onStateChange': function (event) { handlePlayerStateChange(event); },
|
|
'onError': function (event) { console.error('[Biztrend] Player Error:', event.data); }
|
|
}
|
|
});
|
|
};
|
|
|
|
if (typeof YT !== 'undefined' && YT.Player && typeof YT.Player === 'function') {
|
|
createPlayer();
|
|
} else {
|
|
var checkYt = setInterval(function () {
|
|
if (typeof YT !== 'undefined' && YT.Player && typeof YT.Player === 'function') {
|
|
clearInterval(checkYt);
|
|
createPlayer();
|
|
}
|
|
}, 500);
|
|
setTimeout(function () { clearInterval(checkYt); }, 10000);
|
|
}
|
|
}
|
|
|
|
// ── 플레이어 파괴 ──
|
|
function destroyYTPlayer() {
|
|
stopTracking();
|
|
if (ytPlayer && typeof ytPlayer.destroy === 'function') {
|
|
try { ytPlayer.destroy(); } catch (e) {}
|
|
}
|
|
ytPlayer = null;
|
|
var $box = $modal.find('.video-box');
|
|
if (!$box.find('#bizVideoPlayer').length) {
|
|
$box.html('<div id="bizVideoPlayer"></div>');
|
|
}
|
|
}
|
|
|
|
// ── 플레이어 상태 변경 핸들러 ──
|
|
function handlePlayerStateChange(event) {
|
|
if (event.data === YT.PlayerState.PLAYING) {
|
|
startTracking();
|
|
} else {
|
|
stopTracking();
|
|
if (event.data === YT.PlayerState.PAUSED || event.data === YT.PlayerState.ENDED) {
|
|
saveLearningProgress(Math.floor(lastSaveTime), 'N');
|
|
lastSaveTime = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
// ── 시청 추적 시작 (1초 주기) ──
|
|
function startTracking() {
|
|
if (trackingInterval) return;
|
|
lastSaveTime = 0;
|
|
trackingInterval = setInterval(function () {
|
|
if (!ytPlayer || typeof ytPlayer.getCurrentTime !== 'function') return;
|
|
var playerState = (typeof ytPlayer.getPlayerState === 'function')
|
|
? ytPlayer.getPlayerState() : null;
|
|
if (playerState !== YT.PlayerState.PLAYING) return;
|
|
var currentTime = ytPlayer.getCurrentTime();
|
|
var diff = currentTime - lastCurrentTime;
|
|
if (Math.abs(diff) <= 5) { lastSaveTime += 1; }
|
|
lastCurrentTime = currentTime;
|
|
if (lastSaveTime >= 30) {
|
|
saveLearningProgress(lastSaveTime);
|
|
lastSaveTime = 0;
|
|
}
|
|
}, 1000);
|
|
}
|
|
|
|
// ── 시청 추적 중지 ──
|
|
function stopTracking() {
|
|
if (trackingInterval) { clearInterval(trackingInterval); trackingInterval = null; }
|
|
}
|
|
|
|
// ── 서버에 학습 진행상황 저장 ──
|
|
function saveLearningProgress(heartbeatSeconds, isWatching) {
|
|
if (!ytPlayer || !currentContentId) return;
|
|
if (typeof ytPlayer.getCurrentTime !== 'function') return;
|
|
var currentTime = Math.floor(ytPlayer.getCurrentTime());
|
|
var duration = Math.floor(ytPlayer.getDuration());
|
|
var incrementalWatch = Math.max(0, Math.floor(heartbeatSeconds || 0));
|
|
var params = new URLSearchParams({
|
|
content_id: String(currentContentId),
|
|
watch_tm: String(Math.max(0, currentTime)),
|
|
content_tm: String(Math.max(0, duration)),
|
|
all_tm_increment: String(incrementalWatch),
|
|
is_watching: isWatching === 'N' ? 'N' : 'Y'
|
|
});
|
|
fetch(API_BASE + '/save_learning.php', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' },
|
|
body: params.toString(),
|
|
keepalive: true
|
|
}).then(function (r) { return r.json(); }).then(function (result) {
|
|
if (!result.success) console.error('[Biztrend] Save Learning Failed:', result.message);
|
|
}).catch(function (e) {
|
|
console.error('[Biztrend] Save Learning Error:', e);
|
|
});
|
|
}
|
|
|
|
// ── 추천강의용 최근 비즈트렌드 영상 목록 ──
|
|
var allBizEntries = [];
|
|
(function () {
|
|
try {
|
|
var el = document.getElementById('biztrend-recent');
|
|
if (el) {
|
|
allBizEntries = JSON.parse(el.textContent || '[]');
|
|
}
|
|
} catch (e) { console.error('[Biztrend] Parse recent entries error:', e); }
|
|
})();
|
|
|
|
function syncAllBizEntriesBookmark(contentId, isBookmarked) {
|
|
var cid = String(contentId || '');
|
|
if (!cid) return;
|
|
|
|
allBizEntries.forEach(function (entry) {
|
|
if (String(entry.contentId || '') !== cid) return;
|
|
entry.liked = !!isBookmarked;
|
|
entry.bookmark = !!isBookmarked;
|
|
entry.is_liked = !!isBookmarked;
|
|
entry.is_bookmarked = !!isBookmarked;
|
|
});
|
|
}
|
|
|
|
function syncCardBookmarkByContentId(contentId, isBookmarked) {
|
|
var cid = String(contentId || '');
|
|
if (!cid) return;
|
|
|
|
// data-content-id가 어느 레벨에 붙어 있어도 북마크 UI를 최대한 폭넓게 동기화한다.
|
|
var checked = !!isBookmarked;
|
|
var $targets = $([
|
|
'[data-content-id="' + cid + '"]',
|
|
'[data-content_id="' + cid + '"]',
|
|
'[data-id="' + cid + '"]',
|
|
'[data-video-id="' + cid + '"]',
|
|
'[data-video_id="' + cid + '"]',
|
|
'[data-cid="' + cid + '"]'
|
|
].join(','));
|
|
if (!$targets.length) return;
|
|
|
|
$targets.each(function () {
|
|
var $target = $(this);
|
|
// 하루 카드 전체가 아니라 해당 영상 블록(article-card-content)만 동기화한다.
|
|
var $scope = $target.closest('.article-card-content');
|
|
if (!$scope.length) {
|
|
$scope = $target;
|
|
}
|
|
|
|
$scope.find('.article-card-like').toggleClass('is-active', checked).attr('aria-label', checked ? '좋아요 취소' : '좋아요');
|
|
$scope.find('.bookmark input[type="checkbox"], .bookmark input[type="radio"]').prop('checked', checked);
|
|
$scope.find('.bookmark').toggleClass('is-active', checked).attr('aria-pressed', checked ? 'true' : 'false');
|
|
$scope.attr('data-bookmarked', checked ? '1' : '0');
|
|
$scope.attr('data-liked', checked ? '1' : '0');
|
|
$scope.attr('data-is-liked', checked ? '1' : '0');
|
|
$scope.attr('data-is-bookmarked', checked ? '1' : '0');
|
|
|
|
$target.find('.article-card-like').toggleClass('is-active', checked).attr('aria-label', checked ? '좋아요 취소' : '좋아요');
|
|
$target.find('.bookmark input[type="checkbox"], .bookmark input[type="radio"]').prop('checked', checked);
|
|
$target.find('.bookmark').toggleClass('is-active', checked).attr('aria-pressed', checked ? 'true' : 'false');
|
|
$target.attr('data-bookmarked', checked ? '1' : '0');
|
|
$target.attr('data-liked', checked ? '1' : '0');
|
|
$target.attr('data-is-liked', checked ? '1' : '0');
|
|
$target.attr('data-is-bookmarked', checked ? '1' : '0');
|
|
});
|
|
}
|
|
|
|
function syncWatchTimeByContentId(contentId, watchTm) {
|
|
var cid = String(contentId || '');
|
|
if (!cid) return;
|
|
|
|
$('[data-content-id="' + cid + '"]').attr('data-watch-tm', watchTm);
|
|
}
|
|
|
|
function applyBookmarkState(contentId, isBookmarked) {
|
|
var cid = String(contentId || '');
|
|
if (!cid) return;
|
|
|
|
syncCardBookmarkByContentId(cid, isBookmarked);
|
|
syncAllBizEntriesBookmark(cid, isBookmarked);
|
|
|
|
// 렌더러가 script JSON 또는 전역 배열을 재사용하는 경우를 대비해 상태를 같이 갱신한다.
|
|
try {
|
|
var dataEl = document.getElementById('biztrend-data');
|
|
if (dataEl) {
|
|
var parsed = JSON.parse(dataEl.textContent || '{}');
|
|
Object.keys(parsed).forEach(function (k) {
|
|
var arr = parsed[k];
|
|
if (!Array.isArray(arr)) return;
|
|
arr.forEach(function (entry) {
|
|
if (String(entry && entry.contentId || '') !== cid) return;
|
|
entry.liked = !!isBookmarked;
|
|
entry.bookmark = !!isBookmarked;
|
|
entry.is_liked = !!isBookmarked;
|
|
entry.is_bookmarked = !!isBookmarked;
|
|
});
|
|
});
|
|
dataEl.textContent = JSON.stringify(parsed);
|
|
}
|
|
} catch (e) {}
|
|
|
|
['articleData', 'biztrendData', 'bizData'].forEach(function (name) {
|
|
var ref = window[name];
|
|
if (!ref) return;
|
|
if (Array.isArray(ref)) {
|
|
ref.forEach(function (entry) {
|
|
if (String(entry && entry.contentId || entry && entry.content_id || '') !== cid) return;
|
|
entry.liked = !!isBookmarked;
|
|
entry.bookmark = !!isBookmarked;
|
|
entry.is_liked = !!isBookmarked;
|
|
entry.is_bookmarked = !!isBookmarked;
|
|
});
|
|
return;
|
|
}
|
|
if (typeof ref === 'object') {
|
|
Object.keys(ref).forEach(function (k) {
|
|
var arr = ref[k];
|
|
if (!Array.isArray(arr)) return;
|
|
arr.forEach(function (entry) {
|
|
if (String(entry && entry.contentId || entry && entry.content_id || '') !== cid) return;
|
|
entry.liked = !!isBookmarked;
|
|
entry.bookmark = !!isBookmarked;
|
|
entry.is_liked = !!isBookmarked;
|
|
entry.is_bookmarked = !!isBookmarked;
|
|
});
|
|
});
|
|
}
|
|
});
|
|
|
|
if (String(currentContentId || '') === cid) {
|
|
$modal.find('#biztrend-bookmark-chk').prop('checked', !!isBookmarked);
|
|
}
|
|
}
|
|
|
|
function adjustRecommendedListHeight() {
|
|
if (!$modal.is(':visible')) return;
|
|
|
|
var $videoListWrap = $modal.find('.video-list');
|
|
var $list = $modal.find('#bizRecommendedList');
|
|
var $commentWrap = $modal.find('.comment-wrap');
|
|
if (!$videoListWrap.length || !$list.length || !$commentWrap.length) return;
|
|
|
|
var listTop = $videoListWrap.offset().top;
|
|
var commentTop = $commentWrap.offset().top;
|
|
if (!Number.isFinite(listTop) || !Number.isFinite(commentTop)) return;
|
|
|
|
var availableHeight = Math.floor(commentTop - listTop - 8);
|
|
if (availableHeight < 120) availableHeight = 120;
|
|
|
|
$list.css('max-height', availableHeight + 'px');
|
|
}
|
|
|
|
// ── 1024px 이하: btn-comment → comment-wrap 토글 (insight.php 패턴) ──
|
|
function setupCommentToggle() {
|
|
if (typeof commentToggleCleanup === 'function') return;
|
|
|
|
var $btnComment = $modal.find('.btn-comment').first();
|
|
var $videoSide = $modal.find('.video-side').first();
|
|
var $commentWrap = $modal.find('.comment-wrap').first();
|
|
|
|
if (!$btnComment.length || !$videoSide.length || !$commentWrap.length) return;
|
|
|
|
var mediaQuery = window.matchMedia('(max-width: 1024px)');
|
|
|
|
function isMobileView() {
|
|
return mediaQuery.matches;
|
|
}
|
|
|
|
function openComment() {
|
|
$modal.addClass('is-comment-open');
|
|
}
|
|
|
|
function closeComment() {
|
|
$modal.removeClass('is-comment-open');
|
|
}
|
|
|
|
function clearMobileStyles() {
|
|
var el = $commentWrap.get(0);
|
|
if (el && el.style) el.style.cssText = '';
|
|
var listWrap = $commentWrap.find('.comment-list-wrap').get(0);
|
|
if (listWrap && listWrap.style) listWrap.style.cssText = '';
|
|
}
|
|
|
|
function restoreToVideoSide() {
|
|
closeComment();
|
|
if ($commentWrap.parent().is($modal)) {
|
|
$videoSide.append($commentWrap);
|
|
}
|
|
clearMobileStyles();
|
|
}
|
|
|
|
function moveToModal() {
|
|
if (!$commentWrap.parent().is($modal)) {
|
|
$modal.append($commentWrap);
|
|
}
|
|
}
|
|
|
|
function handleResize() {
|
|
if (isMobileView()) {
|
|
moveToModal();
|
|
} else {
|
|
restoreToVideoSide();
|
|
}
|
|
}
|
|
|
|
if (isMobileView()) {
|
|
moveToModal();
|
|
} else {
|
|
restoreToVideoSide();
|
|
}
|
|
|
|
if (typeof mediaQuery.addEventListener === 'function') {
|
|
mediaQuery.addEventListener('change', handleResize);
|
|
} else if (typeof mediaQuery.addListener === 'function') {
|
|
mediaQuery.addListener(handleResize);
|
|
}
|
|
|
|
var onBtnClick = function (e) {
|
|
e.stopPropagation();
|
|
if (!isMobileView()) return;
|
|
if ($modal.hasClass('is-comment-open')) closeComment();
|
|
else openComment();
|
|
};
|
|
$btnComment.on('click', onBtnClick);
|
|
|
|
var onResizerClick = function () {
|
|
if (isMobileView()) closeComment();
|
|
};
|
|
$commentWrap.on('click', '.comment-resizer', onResizerClick);
|
|
|
|
commentToggleCleanup = function () {
|
|
try {
|
|
$btnComment.off('click', onBtnClick);
|
|
$commentWrap.off('click', '.comment-resizer', onResizerClick);
|
|
if (typeof mediaQuery.removeEventListener === 'function') {
|
|
mediaQuery.removeEventListener('change', handleResize);
|
|
} else if (typeof mediaQuery.removeListener === 'function') {
|
|
mediaQuery.removeListener(handleResize);
|
|
}
|
|
} catch (e) {}
|
|
|
|
restoreToVideoSide();
|
|
commentToggleCleanup = null;
|
|
};
|
|
}
|
|
|
|
// ── 모달 열기 ──
|
|
function openModal(info) {
|
|
currentContentId = info.contentId;
|
|
editingCommentId = null;
|
|
lastSaveTime = 0;
|
|
lastCurrentTime = Math.floor(info.watchTm || 0);
|
|
|
|
var videoId = extractVideoId(info.videoUrl);
|
|
if (videoId) { initYTPlayer(videoId, info.watchTm || 0); }
|
|
|
|
// 서버에서 최신 watch_tm 조회 후 재생 위치 보정
|
|
$.ajax({
|
|
url: '/bbs/myclass/api/get_video_time.php',
|
|
type: 'POST',
|
|
contentType: 'application/json',
|
|
data: JSON.stringify({ content_id: info.contentId }),
|
|
dataType: 'json',
|
|
success: function(res) {
|
|
if (res.success && res.watch_tm > 0) {
|
|
var serverWatchTm = parseInt(res.watch_tm, 10);
|
|
if (ytPlayer && typeof ytPlayer.seekTo === 'function' && serverWatchTm > (info.watchTm || 0)) {
|
|
ytPlayer.seekTo(serverWatchTm, true);
|
|
lastCurrentTime = serverWatchTm;
|
|
}
|
|
syncWatchTimeByContentId(info.contentId, serverWatchTm);
|
|
}
|
|
}
|
|
});
|
|
|
|
$modal.find('#bizModalTitle').text(info.title);
|
|
var descHtml = $('<div>').text(info.description || '').html().replace(/\n/g, '<br>');
|
|
$modal.find('#bizModalDesc').html(descHtml);
|
|
$modal.find('#bizModalCategoryName').text(info.categoryName || '비즈트렌드');
|
|
$modal.find('#bizModalSubCategory').text(info.subCategory || '');
|
|
$modal.find('#bizModalBadge').text('비즈트렌드');
|
|
|
|
buildRecommendedList(info.contentId);
|
|
loadComments(info.contentId);
|
|
resetCommentForm();
|
|
|
|
// 북마크 상태 동기화
|
|
syncBookmark(info.contentId, info.bookmark);
|
|
|
|
$modal.show();
|
|
$('body').css('overflow', 'hidden');
|
|
|
|
// 모바일 댓글 토글 세팅(1회 바인딩)
|
|
setupCommentToggle();
|
|
|
|
setTimeout(adjustRecommendedListHeight, 0);
|
|
}
|
|
|
|
// ── 모달 닫기 ──
|
|
function closeModal() {
|
|
stopTracking();
|
|
if (ytPlayer && currentContentId && typeof ytPlayer.getCurrentTime === 'function') {
|
|
var currentTime = Math.floor(ytPlayer.getCurrentTime());
|
|
syncWatchTimeByContentId(currentContentId, currentTime);
|
|
saveLearningProgress(Math.floor(lastSaveTime), 'N');
|
|
}
|
|
lastSaveTime = 0;
|
|
destroyYTPlayer();
|
|
$modal.hide();
|
|
$modal.removeClass('is-comment-open');
|
|
$('body').css('overflow', '');
|
|
currentContentId = null;
|
|
editingCommentId = null;
|
|
|
|
if (typeof commentToggleCleanup === 'function') {
|
|
commentToggleCleanup();
|
|
}
|
|
}
|
|
|
|
$modal.on('click', '.close', closeModal);
|
|
$modal.on('click', function (e) { if ($(e.target).hasClass('modal')) closeModal(); });
|
|
$(document).on('keydown', function (e) {
|
|
if (e.key === 'Escape' && $modal.is(':visible')) closeModal();
|
|
});
|
|
|
|
// ── 북마크 동기화 ──
|
|
function syncBookmark(contentId, isBookmarked) {
|
|
var $chk = $modal.find('.bookmark input[type="checkbox"]');
|
|
if (!$chk.length) return;
|
|
if (isBookmarked !== undefined) {
|
|
$chk.prop('checked', !!isBookmarked);
|
|
} else {
|
|
$.ajax({
|
|
url: '/bbs/myclass/api/get_video_time.php',
|
|
type: 'POST',
|
|
contentType: 'application/json',
|
|
data: JSON.stringify({ content_id: contentId }),
|
|
dataType: 'json',
|
|
success: function(res) {
|
|
if (res.success) {
|
|
$chk.prop('checked', !!res.is_bookmarked);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
// ── 북마크 토글 이벤트 ──
|
|
$modal.on('change', '.bookmark input[type="checkbox"]', function () {
|
|
var $chk = $(this);
|
|
var isActive = $chk.is(':checked') ? '1' : '0';
|
|
if (!currentContentId) return;
|
|
$chk.prop('disabled', true);
|
|
$.ajax({
|
|
url: '/bbs/api/save_wishlist.php',
|
|
type: 'POST',
|
|
data: { content_id: String(currentContentId), is_active: isActive },
|
|
dataType: 'json',
|
|
success: function (res) {
|
|
if (!res || res.success !== true) {
|
|
$chk.prop('checked', !$chk.is(':checked'));
|
|
alert(res?.message || '북마크 저장에 실패했습니다.');
|
|
} else {
|
|
var isBookmarked = (isActive === '1');
|
|
applyBookmarkState(currentContentId, isBookmarked);
|
|
|
|
// biztrend.js의 articleData도 즉시 동기화해 모달 재오픈 시 상태가 유지되도록 한다.
|
|
if (typeof window.dispatchEvent === 'function' && typeof window.CustomEvent === 'function') {
|
|
var detail = {
|
|
contentId: String(currentContentId),
|
|
content_id: String(currentContentId),
|
|
isBookmarked: isBookmarked,
|
|
is_bookmarked: isBookmarked,
|
|
isActive: isBookmarked ? '1' : '0'
|
|
};
|
|
window.dispatchEvent(new CustomEvent('biztrend:bookmark-changed', { detail: detail }));
|
|
window.dispatchEvent(new CustomEvent('bookmark-changed', { detail: detail }));
|
|
window.dispatchEvent(new CustomEvent('wishlist:changed', { detail: detail }));
|
|
$(document).trigger('biztrend:bookmark-changed', [detail]);
|
|
$(document).trigger('bookmark-changed', [detail]);
|
|
$(document).trigger('wishlist:changed', [detail]);
|
|
}
|
|
}
|
|
},
|
|
error: function () {
|
|
$chk.prop('checked', !$chk.is(':checked'));
|
|
alert('북마크 저장 중 오류가 발생했습니다.');
|
|
},
|
|
complete: function () {
|
|
$chk.prop('disabled', false);
|
|
}
|
|
});
|
|
});
|
|
|
|
$(window).on('biztrend:bookmark-changed', function (e) {
|
|
var detail = e.originalEvent && e.originalEvent.detail ? e.originalEvent.detail : null;
|
|
if (!detail) return;
|
|
|
|
var cid = String(detail.contentId || '');
|
|
if (!cid) return;
|
|
|
|
applyBookmarkState(cid, !!detail.isBookmarked);
|
|
});
|
|
|
|
$(document).on('biztrend:bookmark-changed', function (e, detail) {
|
|
if (!detail || typeof detail !== 'object') return;
|
|
|
|
var cid = String(detail.contentId || '');
|
|
if (!cid) return;
|
|
|
|
applyBookmarkState(cid, !!detail.isBookmarked);
|
|
});
|
|
|
|
// ── 추천영상 리스트 구성 (비즈트렌드 영상) ──
|
|
function buildRecommendedList(excludeContentId) {
|
|
var $list = $modal.find('#bizRecommendedList');
|
|
$list.empty();
|
|
var count = 0;
|
|
|
|
allBizEntries.forEach(function (entry) {
|
|
if (count >= 8) return;
|
|
var cid = entry.contentId || '';
|
|
if (!cid || String(cid) === String(excludeContentId)) return;
|
|
|
|
var title = entry.title || '';
|
|
var videoUrl = entry.contentUrl || '';
|
|
var desc = entry.description || '';
|
|
var thumbSrc = entry.image || (entry.videoId ? 'https://img.youtube.com/vi/' + entry.videoId + '/sddefault.jpg' : '/img/video/img_thumb_01.png');
|
|
var wTm = entry.watchTm || 0;
|
|
var cTm = entry.contentTm || 0;
|
|
|
|
var li =
|
|
'<li>' +
|
|
'<a href="#" class="list" ' +
|
|
'data-content-id="' + cid + '" ' +
|
|
'data-content-url="' + (videoUrl).replace(/"/g, '"') + '" ' +
|
|
'data-title="' + title.replace(/"/g, '"') + '" ' +
|
|
'data-description="' + desc.replace(/"/g, '"') + '" ' +
|
|
'data-watch-tm="' + wTm + '" ' +
|
|
'data-content-tm="' + cTm + '">' +
|
|
'<div class="thumb"><img src="' + thumbSrc + '" alt="" /></div>' +
|
|
'<div class="txt-box">' +
|
|
'<div class="category leader">비즈트렌드</div>' +
|
|
'<div class="title">' + title + '</div>' +
|
|
'</div>' +
|
|
'</a>' +
|
|
'</li>';
|
|
$list.append(li);
|
|
count++;
|
|
});
|
|
|
|
if (count === 0) {
|
|
$list.html('<li class="empty">추천 영상이 없습니다.</li>');
|
|
}
|
|
|
|
setTimeout(adjustRecommendedListHeight, 0);
|
|
}
|
|
|
|
$(window).on('resize', function () {
|
|
if ($modal.is(':visible')) {
|
|
adjustRecommendedListHeight();
|
|
}
|
|
});
|
|
|
|
// ── 추천영상 클릭 → 영상 전환 ──
|
|
$modal.on('click', '#bizRecommendedList .list', function (e) {
|
|
e.preventDefault();
|
|
var $a = $(this);
|
|
stopTracking();
|
|
if (ytPlayer && currentContentId && typeof ytPlayer.getCurrentTime === 'function') {
|
|
saveLearningProgress(Math.floor(lastSaveTime), 'N');
|
|
}
|
|
lastSaveTime = 0;
|
|
openModal({
|
|
contentId: $a.data('content-id'),
|
|
videoUrl: $a.data('content-url') || '',
|
|
title: $a.data('title') || $a.find('.title').text().trim(),
|
|
description: $a.data('description') || '',
|
|
categoryName: '비즈트렌드',
|
|
subCategory: '',
|
|
watchTm: parseInt($a.data('watch-tm') || '0', 10),
|
|
contentTm: parseInt($a.data('content-tm') || '0', 10)
|
|
});
|
|
});
|
|
|
|
// ══════════════════════════════════════════════
|
|
// 댓글 CRUD
|
|
// ══════════════════════════════════════════════
|
|
|
|
function loadComments(contentId) {
|
|
var $commentList = $modal.find('.comment-list');
|
|
if (!contentId) return;
|
|
$.getJSON(API_BASE + '/get_comments.php', { content_id: contentId })
|
|
.done(function (data) {
|
|
if (data.success && Array.isArray(data.data)) {
|
|
renderComments(data.data);
|
|
} else {
|
|
$commentList.html('<li class="empty">등록된 댓글이 없습니다.</li>');
|
|
}
|
|
})
|
|
.fail(function () {
|
|
$commentList.html('<li class="empty">댓글을 불러올 수 없습니다.</li>');
|
|
});
|
|
}
|
|
|
|
function renderComments(comments) {
|
|
var $commentList = $modal.find('.comment-list');
|
|
if (!comments.length) {
|
|
$commentList.html('<li class="empty">등록된 댓글이 없습니다.</li>');
|
|
return;
|
|
}
|
|
var userIconSvg = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 12c2.7 0 4.8-2.1 4.8-4.8S14.7 2.4 12 2.4 7.2 4.5 7.2 7.2 9.3 12 12 12zm0 2.4c-3.2 0-9.6 1.6-9.6 4.8v2.4h19.2v-2.4c0-3.2-6.4-4.8-9.6-4.8z"/></svg>';
|
|
// var html = comments.map(function (c) {
|
|
// var safeComment = (c.comment || '').replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/\n/g, '<br>');
|
|
// var safeName = (c.member_name || '익명').replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
|
// var buttons = c.is_author
|
|
// ? '<div class="comment-actions">' +
|
|
// '<button type="button" class="btn-edit" data-id="' + c.id + '">수정</button>' +
|
|
// '<button type="button" class="btn-delete" data-id="' + c.id + '">삭제</button>' +
|
|
// '</div>'
|
|
// : '';
|
|
// return '<li data-comment-id="' + c.id + '">' +
|
|
// '<div class="comment-header">' +
|
|
// '<span class="user-icon">' + userIconSvg + '</span>' +
|
|
// '<span class="comment-author">' + safeName + '</span>' +
|
|
// '</div>' +
|
|
// '<div class="comment-body">' +
|
|
// '<div class="comment-content">' + safeComment + '</div>' +
|
|
// buttons +
|
|
// '</div>' +
|
|
// '</li>';
|
|
// }).join('');
|
|
var html = comments.map(function (c) {
|
|
var safeComment = (c.comment || '')
|
|
.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
|
var safeName = (c.member_name || '익명')
|
|
.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
|
var actions = c.is_author
|
|
? '<div class="actions">' +
|
|
'<button type="button" class="btn-edit-comment" data-id="' + c.id + '">수정</button>' +
|
|
'<button type="button" class="btn-del-comment" data-id="' + c.id + '">삭제</button>' +
|
|
'</div>'
|
|
: '';
|
|
|
|
return '<li data-id="' + c.id + '">' +
|
|
'<div class="comment-info">' +
|
|
'<div class="photo"><img src="' + (c.profile_image || '/img/ico/ico_user.svg') + '" onerror="this.src=\'/img/ico/ico_user.svg\'" /></div>' +
|
|
'<div class="user-comment">' +
|
|
'<span class="user-name">' + safeName + '</span>' +
|
|
'<textarea class="user-text" disabled>' + safeComment + '</textarea>' +
|
|
'</div>' +
|
|
actions +
|
|
'</div>' +
|
|
'</li>';
|
|
}).join('');
|
|
$commentList.html(html);
|
|
adjustUserTextHeights();
|
|
}
|
|
|
|
// ── 댓글 textarea 높이 자동 조정 ──
|
|
function adjustUserTextHeights() {
|
|
$modal.find('.user-text').each(function () {
|
|
this.style.setProperty('overflow-y', 'hidden', 'important');
|
|
this.style.setProperty('overflow-x', 'hidden', 'important');
|
|
this.style.setProperty('max-height', 'none', 'important');
|
|
this.style.setProperty('height', '0px', 'important');
|
|
var h = this.scrollHeight;
|
|
this.style.setProperty('height', (h > 0 ? h : 40) + 'px', 'important');
|
|
});
|
|
}
|
|
|
|
$modal.on('click', '.btn-edit-comment', function () {
|
|
var $li = $(this).closest('li');
|
|
var content = ($li.find('.user-text').val() || '').trim();
|
|
editingCommentId = $(this).data('id');
|
|
var $textarea = $modal.find('.comment-box textarea');
|
|
$textarea.val(content).focus();
|
|
$modal.find('.btn-cancel').prop('disabled', false);
|
|
$modal.find('.btn-save').prop('disabled', false).text('수정');
|
|
});
|
|
|
|
$modal.on('click', '.btn-del-comment', function () {
|
|
if (!confirm('댓글을 삭제하시겠습니까?')) return;
|
|
var commentId = $(this).data('id');
|
|
$.ajax({
|
|
url: API_BASE + '/delete_comment.php',
|
|
type: 'POST',
|
|
contentType: 'application/json',
|
|
data: JSON.stringify({ id: commentId }),
|
|
success: function (data) {
|
|
if (data.success) { loadComments(currentContentId); }
|
|
else { alert(data.message || '삭제에 실패했습니다.'); }
|
|
},
|
|
error: function () { alert('삭제 중 오류가 발생했습니다.'); }
|
|
});
|
|
});
|
|
|
|
$modal.on('click', '.comment-box .btn-save', function () {
|
|
var $textarea = $modal.find('.comment-box textarea');
|
|
var comment = $textarea.val().trim();
|
|
if (!comment || !currentContentId) return;
|
|
if (comment.length > COMMENT_MAX_LENGTH) {
|
|
$textarea.val($textarea.val().slice(0, COMMENT_MAX_LENGTH));
|
|
alert('댓글은 200자까지 입력할 수 있습니다.');
|
|
return;
|
|
}
|
|
var payload = { content_id: currentContentId, comment: comment };
|
|
if (editingCommentId) payload.id = editingCommentId;
|
|
$.ajax({
|
|
url: API_BASE + '/save_comment.php',
|
|
type: 'POST',
|
|
contentType: 'application/json',
|
|
data: JSON.stringify(payload),
|
|
success: function (data) {
|
|
if (data.success) { resetCommentForm(); loadComments(currentContentId); }
|
|
else { alert(data.message || '저장에 실패했습니다.'); }
|
|
},
|
|
error: function () { alert('저장 중 오류가 발생했습니다.'); }
|
|
});
|
|
});
|
|
|
|
$modal.on('click', '.comment-box .btn-cancel', function () { resetCommentForm(); });
|
|
|
|
var COMMENT_MAX_LENGTH = 200;
|
|
var _lastLengthAlertAt = 0;
|
|
|
|
$modal.on('input', '.comment-box textarea', function () {
|
|
var $ta = $(this);
|
|
if ($ta.val().length > COMMENT_MAX_LENGTH) {
|
|
$ta.val($ta.val().slice(0, COMMENT_MAX_LENGTH));
|
|
var now = Date.now();
|
|
if (now - _lastLengthAlertAt > 800) {
|
|
_lastLengthAlertAt = now;
|
|
alert('댓글은 200자까지 입력할 수 있습니다.');
|
|
}
|
|
}
|
|
var hasText = $ta.val().trim().length > 0;
|
|
$modal.find('.btn-cancel').prop('disabled', !hasText);
|
|
$modal.find('.btn-save').prop('disabled', !hasText);
|
|
});
|
|
|
|
function resetCommentForm() {
|
|
var $ta = $modal.find('.comment-box textarea');
|
|
$ta.val('');
|
|
$ta.attr('maxlength', COMMENT_MAX_LENGTH);
|
|
$modal.find('.btn-cancel').prop('disabled', true);
|
|
$modal.find('.btn-save').prop('disabled', true).text('등록');
|
|
editingCommentId = null;
|
|
}
|
|
|
|
// biztrend.js에서 호출할 수 있도록 전역 노출
|
|
window._biztrendOpenModal = openModal;
|
|
})();
|
|
</script>
|
|
</body>
|
|
</html>
|