Initial commit: 교육 프로젝트 배포
This commit is contained in:
@@ -0,0 +1,533 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/../bbs/auth.php';
|
||||
edu_require_login();
|
||||
|
||||
require_once __DIR__ . '/../bbs/db_conn.php';
|
||||
|
||||
$q = trim((string)($_GET['q'] ?? ''));
|
||||
$results = [];
|
||||
|
||||
function h(string $value): string
|
||||
{
|
||||
return htmlspecialchars($value, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
|
||||
}
|
||||
|
||||
function parse_youtube_id(string $raw): string
|
||||
{
|
||||
if (preg_match('/(?:v=|youtu\.be\/|youtube\.com\/embed\/)([A-Za-z0-9_-]{11})/', $raw, $m)) {
|
||||
return $m[1];
|
||||
}
|
||||
if (preg_match('/^[A-Za-z0-9_-]{11}$/', $raw)) {
|
||||
return $raw;
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
function make_thumb_url(string $thumb, string $contentUrl): string
|
||||
{
|
||||
$thumb = trim($thumb);
|
||||
if ($thumb !== '') {
|
||||
return $thumb;
|
||||
}
|
||||
|
||||
$videoId = parse_youtube_id(trim($contentUrl));
|
||||
if ($videoId !== '') {
|
||||
return 'https://img.youtube.com/vi/' . $videoId . '/sddefault.jpg';
|
||||
}
|
||||
|
||||
return '/img/video/img_thumb_01.png';
|
||||
}
|
||||
|
||||
function highlight_keyword(string $text, string $keyword): string
|
||||
{
|
||||
$escaped = h($text);
|
||||
$keyword = trim($keyword);
|
||||
if ($keyword === '') {
|
||||
return $escaped;
|
||||
}
|
||||
|
||||
$pattern = '/' . preg_quote(h($keyword), '/') . '/iu';
|
||||
return (string)preg_replace($pattern, '<span class="search-highlight">$0</span>', $escaped);
|
||||
}
|
||||
|
||||
try {
|
||||
$pdo = db_conn();
|
||||
|
||||
$memberId = trim((string)($_SESSION['member_id'] ?? ''));
|
||||
$sysCompCode = trim((string)($_SESSION['sys_comp_code'] ?? ''));
|
||||
|
||||
$sql = "
|
||||
SELECT
|
||||
c.content_id,
|
||||
c.title,
|
||||
c.description,
|
||||
c.description2,
|
||||
c.content_url,
|
||||
c.thumbnail_url,
|
||||
c.category_code,
|
||||
c.category_group,
|
||||
COALESCE(ec.code_name, c.category_code) AS category_name,
|
||||
COALESCE(vs.view_cnt, 0) AS view_cnt,
|
||||
CASE WHEN cw.content_id IS NOT NULL THEN 1 ELSE 0 END AS is_bookmarked
|
||||
FROM edu_contents c
|
||||
LEFT JOIN edu_codes ec
|
||||
ON ec.base_code = c.category_code
|
||||
AND ec.group_code = 'CA100'
|
||||
AND ec.is_active = 1
|
||||
LEFT JOIN (
|
||||
SELECT
|
||||
content_id,
|
||||
COUNT(DISTINCT member_id) AS view_cnt
|
||||
FROM edu_learning_histories
|
||||
GROUP BY content_id
|
||||
) vs
|
||||
ON vs.content_id = c.content_id
|
||||
LEFT JOIN edu_content_wishlist cw
|
||||
ON cw.member_id = :mid
|
||||
AND cw.sys_comp_code = :sc
|
||||
AND cw.is_active = 1
|
||||
AND cw.content_id = c.content_id
|
||||
WHERE c.is_active = 1
|
||||
AND (
|
||||
:kw = ''
|
||||
OR c.title LIKE :like_kw1
|
||||
OR c.description LIKE :like_kw2
|
||||
OR c.description2 LIKE :like_kw3
|
||||
)
|
||||
ORDER BY c.updated_at DESC, c.created_at DESC, c.content_id DESC
|
||||
LIMIT 300
|
||||
";
|
||||
|
||||
$stmt = $pdo->prepare($sql);
|
||||
$stmt->execute([
|
||||
':mid' => $memberId,
|
||||
':sc' => $sysCompCode,
|
||||
':kw' => $q,
|
||||
':like_kw1' => '%' . $q . '%',
|
||||
':like_kw2' => '%' . $q . '%',
|
||||
':like_kw3' => '%' . $q . '%',
|
||||
]);
|
||||
$results = $stmt->fetchAll(PDO::FETCH_ASSOC) ?: [];
|
||||
} catch (Throwable $e) {
|
||||
error_log('[search_result] ' . $e->getMessage());
|
||||
$results = [];
|
||||
}
|
||||
|
||||
$totalCount = count($results);
|
||||
|
||||
// 카테고리별 그룹화
|
||||
$groupedResults = [];
|
||||
$categoryMeta = []; // category_code => ['name' => string, 'count' => int]
|
||||
foreach ($results as $row) {
|
||||
$catCode = (string)($row['category_code'] ?? 'etc');
|
||||
$catName = (string)($row['category_name'] ?? $row['category_code'] ?? '기타');
|
||||
if (!isset($groupedResults[$catCode])) {
|
||||
$groupedResults[$catCode] = [];
|
||||
$categoryMeta[$catCode] = ['name' => $catName, 'count' => 0];
|
||||
}
|
||||
$groupedResults[$catCode][] = $row;
|
||||
$categoryMeta[$catCode]['count']++;
|
||||
}
|
||||
|
||||
$videosForJs = array_map(static function (array $row): array {
|
||||
$contentUrl = (string)($row['content_url'] ?? '');
|
||||
$thumb = make_thumb_url((string)($row['thumbnail_url'] ?? ''), $contentUrl);
|
||||
|
||||
return [
|
||||
'id' => (string)($row['content_id'] ?? ''),
|
||||
'content_id' => (string)($row['content_id'] ?? ''),
|
||||
'url' => $contentUrl,
|
||||
'content_url' => $contentUrl,
|
||||
'thumbnail' => $thumb,
|
||||
'category' => (string)($row['category_name'] ?? $row['category_code'] ?? ''),
|
||||
'category_name' => (string)($row['category_name'] ?? $row['category_code'] ?? ''),
|
||||
'category_code' => (string)($row['category_code'] ?? ''),
|
||||
'subcate' => (string)($row['category_group'] ?? ''),
|
||||
'title' => (string)($row['title'] ?? ''),
|
||||
'description' => trim((string)($row['description'] ?? '')),
|
||||
'description2' => trim((string)($row['description2'] ?? '')),
|
||||
'bookmark' => ((string)($row['is_bookmarked'] ?? '0') === '1'),
|
||||
'type' => 'main',
|
||||
'keywords' => [],
|
||||
'watch_tm' => 0,
|
||||
'content_tm' => 0,
|
||||
'all_tm' => 0,
|
||||
'view_cnt' => (int)($row['view_cnt'] ?? 0),
|
||||
];
|
||||
}, $results);
|
||||
|
||||
$videosJson = json_encode($videosForJs, JSON_UNESCAPED_UNICODE | JSON_HEX_TAG | JSON_HEX_AMP);
|
||||
?>
|
||||
<!doctype html>
|
||||
<!-- dynamic-search-result-v2 -->
|
||||
<html lang="ko">
|
||||
<head>
|
||||
<?php include(__DIR__ . '/_include/_head.php') ?>
|
||||
<link rel="stylesheet" type="text/css" href="/css/main.css" />
|
||||
<style>
|
||||
.search-result .search-highlight {
|
||||
color: #ff7a00;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.search-result .item-desc-wrapper {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.search-result .item-view-count {
|
||||
margin-left: auto;
|
||||
font-size: 13px;
|
||||
color: #6f6f6f;
|
||||
line-height: 1.5;
|
||||
white-space: nowrap;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="wrap search-result">
|
||||
<?php include(__DIR__ . '/_include/_header.php') ?>
|
||||
<div class="container">
|
||||
<div class="search-result-wrap">
|
||||
<div class="page-header">
|
||||
<div class="page-title">
|
||||
<h3 class="blind">검색결과</h3>
|
||||
<p class="search-summary">
|
||||
'<?php echo h($q); ?>'에 대한 검색결과가 <em class="search-count"><?php echo (int)$totalCount; ?></em>건 있습니다.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php if ($q === ''): ?>
|
||||
<p style="padding: 20px 0;">검색어를 입력해주세요.</p>
|
||||
<?php elseif ($totalCount === 0): ?>
|
||||
<p style="padding: 20px 0;">검색 결과가 없습니다.</p>
|
||||
<?php else: ?>
|
||||
|
||||
<!-- 필터 탭 -->
|
||||
<div class="filter-tabs">
|
||||
<button class="filter-tab active" data-filter="all">
|
||||
전체 <span class="count"><?php echo (int)$totalCount; ?></span>
|
||||
</button>
|
||||
<?php foreach ($categoryMeta as $catCode => $meta): ?>
|
||||
<button class="filter-tab" data-filter="<?php echo h($catCode); ?>">
|
||||
<?php echo h($meta['name']); ?> <span class="count"><?php echo (int)$meta['count']; ?></span>
|
||||
</button>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
|
||||
<!-- 검색 결과 섹션 -->
|
||||
<div class="search-sections" id="videoCardsContainer">
|
||||
|
||||
<!-- [전체] 평면 목록: 카테고리 구분 없이 전체 결과를 순서대로 출력 -->
|
||||
<section class="content-section" data-section="all">
|
||||
<div class="section-header">
|
||||
<h3 class="section-title">전체 <span class="section-count"><?php echo (int)$totalCount; ?></span></h3>
|
||||
<!-- <div class="list-options">
|
||||
<div class="select-wrap">
|
||||
<select class="select-sort" title="정렬">
|
||||
<option>조회수</option>
|
||||
<option selected>업데이트</option>
|
||||
<option>내가본컨텐츠</option>
|
||||
<option>안본컨텐츠</option>
|
||||
</select>
|
||||
</div>
|
||||
</div> -->
|
||||
</div>
|
||||
<ul class="video-grid">
|
||||
<?php foreach ($results as $idx => $row):
|
||||
$id = (string)($row['content_id'] ?? '');
|
||||
$title = (string)($row['title'] ?? '');
|
||||
$description = trim((string)($row['description'] ?? ''));
|
||||
$description2 = trim((string)($row['description2'] ?? ''));
|
||||
$thumb = make_thumb_url((string)($row['thumbnail_url'] ?? ''), (string)($row['content_url'] ?? ''));
|
||||
$category = (string)($row['category_name'] ?? $row['category_code'] ?? '');
|
||||
$bookmark = ((string)($row['is_bookmarked'] ?? '0') === '1');
|
||||
$viewCnt = (int)($row['view_cnt'] ?? 0);
|
||||
$bookmarkId = 'like_chk_all_' . ($idx + 1);
|
||||
$titleHighlighted = highlight_keyword($title, $q);
|
||||
$descHighlighted = highlight_keyword($description, $q);
|
||||
$desc2Highlighted = highlight_keyword($description2, $q);
|
||||
?>
|
||||
<li class="video-item">
|
||||
<a href="#" class="card-link card" data-video-id="<?php echo h($id); ?>">
|
||||
<label class="bookmark" for="<?php echo h($bookmarkId); ?>" onclick="event.stopPropagation();"><input type="checkbox" id="<?php echo h($bookmarkId); ?>" <?php echo $bookmark ? 'checked' : ''; ?> title="좋아요"></label>
|
||||
<div class="item-thumb">
|
||||
<img src="<?php echo h($thumb); ?>" alt="" />
|
||||
</div>
|
||||
<div class="item-info">
|
||||
<strong class="item-title"><?php echo $titleHighlighted; ?></strong>
|
||||
<?php if ($description !== '' || $description2 !== ''): ?>
|
||||
<div class="item-desc" style="color:#6f6f6f; font-size:13px; line-height:1.5;">
|
||||
<?php if ($description !== ''): ?><p class="desc-1"><?php echo $descHighlighted; ?></p><?php endif; ?>
|
||||
<?php if ($description2 !== ''): ?><p class="desc-2" style="margin-top:2px;"><?php echo $desc2Highlighted; ?></p><?php endif; ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<div class="tag-list item-desc-wrapper">
|
||||
<span class="tag"><?php echo h($category); ?></span>
|
||||
<span class="item-view-count">시청수 <?php echo (int)$viewCnt; ?> 회</span>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
<?php endforeach; ?>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<!-- [카테고리별] 섹션: 기본 hidden, 특정 탭 선택 시 표시 -->
|
||||
<?php
|
||||
$globalIdx = 0;
|
||||
foreach ($groupedResults as $catCode => $catRows):
|
||||
$catName = $categoryMeta[$catCode]['name'];
|
||||
$catCount = $categoryMeta[$catCode]['count'];
|
||||
?>
|
||||
<section class="content-section" data-section="<?php echo h($catCode); ?>" style="display:none;">
|
||||
<div class="section-header">
|
||||
<h3 class="section-title"><?php echo h($catName); ?> <span class="section-count"><?php echo (int)$catCount; ?></span></h3>
|
||||
<!-- <div class="list-options">
|
||||
<div class="select-wrap">
|
||||
<select class="select-sort" title="정렬">
|
||||
<option>조회수</option>
|
||||
<option selected>업데이트</option>
|
||||
<option>내가본컨텐츠</option>
|
||||
<option>안본컨텐츠</option>
|
||||
</select>
|
||||
</div>
|
||||
</div> -->
|
||||
</div>
|
||||
<ul class="video-grid">
|
||||
<?php foreach ($catRows as $row):
|
||||
$globalIdx++;
|
||||
$id = (string)($row['content_id'] ?? '');
|
||||
$title = (string)($row['title'] ?? '');
|
||||
$description = trim((string)($row['description'] ?? ''));
|
||||
$description2 = trim((string)($row['description2'] ?? ''));
|
||||
$thumb = make_thumb_url((string)($row['thumbnail_url'] ?? ''), (string)($row['content_url'] ?? ''));
|
||||
$category = (string)($row['category_name'] ?? $row['category_code'] ?? '');
|
||||
$bookmark = ((string)($row['is_bookmarked'] ?? '0') === '1');
|
||||
$viewCnt = (int)($row['view_cnt'] ?? 0);
|
||||
$bookmarkId = 'like_chk_search_' . $globalIdx;
|
||||
$titleHighlighted = highlight_keyword($title, $q);
|
||||
$descHighlighted = highlight_keyword($description, $q);
|
||||
$desc2Highlighted = highlight_keyword($description2, $q);
|
||||
?>
|
||||
<li class="video-item">
|
||||
<a href="#" class="card-link card" data-video-id="<?php echo h($id); ?>">
|
||||
<label class="bookmark" for="<?php echo h($bookmarkId); ?>" onclick="event.stopPropagation();"><input type="checkbox" id="<?php echo h($bookmarkId); ?>" <?php echo $bookmark ? 'checked' : ''; ?> title="좋아요"></label>
|
||||
<div class="item-thumb">
|
||||
<img src="<?php echo h($thumb); ?>" alt="" />
|
||||
</div>
|
||||
<div class="item-info">
|
||||
<strong class="item-title"><?php echo $titleHighlighted; ?></strong>
|
||||
<?php if ($description !== '' || $description2 !== ''): ?>
|
||||
<div class="item-desc" style="color:#6f6f6f; font-size:13px; line-height:1.5;">
|
||||
<?php if ($description !== ''): ?><p class="desc-1"><?php echo $descHighlighted; ?></p><?php endif; ?>
|
||||
<?php if ($description2 !== ''): ?><p class="desc-2" style="margin-top:2px;"><?php echo $desc2Highlighted; ?></p><?php endif; ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<div class="tag-list item-desc-wrapper">
|
||||
<span class="tag"><?php echo h($category); ?></span>
|
||||
<span class="item-view-count">시청수 <?php echo (int)$viewCnt; ?> 회</span>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
<?php endforeach; ?>
|
||||
</ul>
|
||||
</section>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="/js/learning/config.js" defer></script>
|
||||
<script src="/js/learning/markers.js" defer></script>
|
||||
<script src="/js/learning/modal.js" defer></script>
|
||||
<script src="/js/bridges/learning-modal-bridge.js" defer></script>
|
||||
<script>
|
||||
window.puzzleConfig = {
|
||||
...(window.puzzleConfig || {}),
|
||||
COMPLETION_MODE: 'COMPLETED',
|
||||
CURRENT_MEMBER_ID: <?php echo json_encode($memberId, JSON_UNESCAPED_UNICODE | JSON_HEX_TAG | JSON_HEX_AMP); ?>,
|
||||
};
|
||||
</script>
|
||||
<script src="/js/puzzle-onboarding.js" defer></script>
|
||||
<script src="/js/bridges/onboarding-modal-bridge.js" defer></script>
|
||||
<script src="/js/main/Videomodalmanager.js" defer></script>
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
const currentVideos = <?= $videosJson ?: '[]' ?>;
|
||||
const container = document.getElementById('videoCardsContainer');
|
||||
|
||||
// ── 모달 초기화 ──
|
||||
let modalManager = null;
|
||||
if (container && Array.isArray(currentVideos) && currentVideos.length > 0 && typeof VideoModalManager !== 'undefined') {
|
||||
modalManager = new VideoModalManager({ videos: currentVideos });
|
||||
}
|
||||
|
||||
// ── 필터 탭 ──
|
||||
const filterTabs = document.querySelectorAll('.filter-tab');
|
||||
filterTabs.forEach(function (tab) {
|
||||
tab.addEventListener('click', function () {
|
||||
filterTabs.forEach(function (t) { t.classList.remove('active'); });
|
||||
this.classList.add('active');
|
||||
|
||||
const filter = this.dataset.filter;
|
||||
document.querySelectorAll('.content-section').forEach(function (section) {
|
||||
const sec = section.dataset.section;
|
||||
if (filter === 'all') {
|
||||
// 전체 탭: all 섹션만 표시, 카테고리 섹션 모두 숨김
|
||||
section.style.display = (sec === 'all') ? 'block' : 'none';
|
||||
} else {
|
||||
// 카테고리 탭: all 섹션 숨기고 해당 카테고리만 표시
|
||||
section.style.display = (sec === filter) ? 'block' : 'none';
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// ── 북마크 ──
|
||||
if (!container) return;
|
||||
|
||||
async function saveWishlist(videoId, isActive) {
|
||||
const params = new URLSearchParams({
|
||||
content_id: String(videoId || ''),
|
||||
is_active: isActive ? '1' : '0',
|
||||
});
|
||||
const res = await fetch('/bbs/api/save_wishlist.php', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' },
|
||||
body: params.toString(),
|
||||
});
|
||||
return res.json();
|
||||
}
|
||||
|
||||
function setBookmarkState(videoId, isBookmarked) {
|
||||
const id = String(videoId || '');
|
||||
currentVideos.forEach(function (video) {
|
||||
if (String(video?.id ?? '') === id) { video.bookmark = !!isBookmarked; }
|
||||
});
|
||||
container.querySelectorAll('.card[data-video-id]').forEach(function (cardEl) {
|
||||
if (String(cardEl.getAttribute('data-video-id') || '') !== id) return;
|
||||
const checkbox = cardEl.querySelector('.bookmark input[type="checkbox"]');
|
||||
if (checkbox) checkbox.checked = !!isBookmarked;
|
||||
});
|
||||
if (modalManager && modalManager.config) { modalManager.config.videos = currentVideos; }
|
||||
}
|
||||
|
||||
function getVideoDataById(videoId) {
|
||||
const id = String(videoId || '');
|
||||
return currentVideos.find(function (v) {
|
||||
return String(v?.id ?? '') === id || String(v?.content_id ?? '') === id;
|
||||
}) || null;
|
||||
}
|
||||
|
||||
function toBridgeInfo(videoData) {
|
||||
const contentId = String(videoData?.content_id ?? videoData?.id ?? '');
|
||||
const description = [
|
||||
String(videoData?.description || '').trim(),
|
||||
String(videoData?.description2 || '').trim(),
|
||||
].filter(Boolean).join('\n');
|
||||
|
||||
return {
|
||||
contentId: contentId,
|
||||
videoUrl: String(videoData?.content_url ?? videoData?.url ?? ''),
|
||||
title: String(videoData?.title ?? ''),
|
||||
description: description,
|
||||
categoryName: String(videoData?.category_name ?? videoData?.category ?? ''),
|
||||
subCategory: String(videoData?.subcate ?? ''),
|
||||
watchTm: Number(videoData?.watch_tm || 0),
|
||||
contentTm: Number(videoData?.content_tm || 0),
|
||||
bookmark: !!videoData?.bookmark,
|
||||
};
|
||||
}
|
||||
|
||||
async function openSearchResultVideoByCategory(videoData) {
|
||||
const categoryCode = String(videoData?.category_code || '').toUpperCase();
|
||||
const bridgeInfo = toBridgeInfo(videoData);
|
||||
|
||||
// 브리지가 준비된 카테고리부터 우선 적용한다.
|
||||
if (categoryCode === 'CA10006' && typeof window._biztrendOpenModal === 'function') {
|
||||
window._biztrendOpenModal(bridgeInfo);
|
||||
return;
|
||||
}
|
||||
|
||||
if (categoryCode === 'CA10005' && typeof window._insightOpenModal === 'function') {
|
||||
window._insightOpenModal(bridgeInfo);
|
||||
return;
|
||||
}
|
||||
|
||||
if (categoryCode === 'CA10004' && typeof window._leadershipOpenModal === 'function') {
|
||||
window._leadershipOpenModal(bridgeInfo);
|
||||
return;
|
||||
}
|
||||
|
||||
if (categoryCode === 'CA10003' && typeof window._learningOpenModal === 'function') {
|
||||
window._learningOpenModal(bridgeInfo);
|
||||
return;
|
||||
}
|
||||
|
||||
if (categoryCode === 'CA10002' && typeof window._onboardingOpenModal === 'function') {
|
||||
window._onboardingOpenModal(bridgeInfo);
|
||||
return;
|
||||
}
|
||||
|
||||
// 기본값: 기존 공통 모달 흐름 유지
|
||||
if (modalManager && typeof modalManager.openVideo === 'function') {
|
||||
await modalManager.openVideo(String(videoData?.id ?? videoData?.content_id ?? ''));
|
||||
}
|
||||
}
|
||||
|
||||
container.addEventListener('click', function (e) {
|
||||
if (e.target.closest('.bookmark')) { e.stopPropagation(); return; }
|
||||
|
||||
const card = e.target.closest('.card[data-video-id]');
|
||||
if (!card) return;
|
||||
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
const videoId = String(card.getAttribute('data-video-id') || '');
|
||||
if (!videoId) return;
|
||||
|
||||
const videoData = getVideoDataById(videoId);
|
||||
if (!videoData) return;
|
||||
|
||||
openSearchResultVideoByCategory(videoData).catch(function (err) {
|
||||
console.warn('[search_result] open by category failed:', err?.message || err);
|
||||
});
|
||||
});
|
||||
|
||||
container.addEventListener('change', async function (e) {
|
||||
const checkbox = e.target.closest('input[type="checkbox"]');
|
||||
if (!checkbox || !checkbox.closest('.bookmark')) return;
|
||||
|
||||
const card = checkbox.closest('.card[data-video-id]');
|
||||
const videoId = card?.getAttribute('data-video-id');
|
||||
if (!videoId) return;
|
||||
|
||||
const nextState = !!checkbox.checked;
|
||||
checkbox.disabled = true;
|
||||
try {
|
||||
const result = await saveWishlist(videoId, nextState);
|
||||
if (!result || !result.success) { checkbox.checked = !nextState; return; }
|
||||
setBookmarkState(videoId, nextState);
|
||||
} catch (err) {
|
||||
checkbox.checked = !nextState;
|
||||
console.warn('[search wishlist]', err?.message || err);
|
||||
} finally {
|
||||
checkbox.disabled = false;
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user