Files
edu/skin/myclass_list.php
T

1090 lines
50 KiB
PHP

<?php
require_once __DIR__ . '/../bbs/auth.php';
edu_require_login();
require_once __DIR__ . '/../bbs/db_conn.php';
$mode = isset($_GET['mode']) ? trim((string) $_GET['mode']) : 'learning';
$allowedModes = ['learning', 'progress', 'complete', 'extended'];
if (!in_array($mode, $allowedModes, true)) {
$mode = 'learning';
}
$activeGoalId = isset($_GET['goal']) ? (int) $_GET['goal'] : 1;
$completedGoalId = isset($_GET['completed_goal']) ? (int) $_GET['completed_goal'] : 1;
$activeGoalId = max(1, min(6, $activeGoalId));
$completedGoalId = max(1, min(6, $completedGoalId));
if ($mode === 'extended' && $activeGoalId === $completedGoalId) {
$activeGoalId = $completedGoalId === 1 ? 2 : 1;
}
$today = new DateTime('now', new DateTimeZone('Asia/Seoul'));
$currentYear = (int) $today->format('Y');
$currentMonth = (int) $today->format('m');
$currentQuarterNum = (int) ceil($currentMonth / 3);
$currentQuarterCode = sprintf('CA200Q%02d', $currentQuarterNum);
$memberId = (string)($_SESSION['member_id'] ?? '');
$sysCompCode = (string)($_SESSION['sys_comp_code'] ?? '');
$userActiveGoalRows = [];
$userActiveGoalCodes = [];
$userAllGoalRows = [];
$userAllGoalCodes = [];
try {
if ($memberId !== '' && $sysCompCode !== '') {
$stmtUserGoals = db_conn()->prepare(
"SELECT u.goal_code,
u.completed_date,
u.updated_at,
u.created_at,
u.is_active
FROM edu_user_learning_goals u
WHERE u.member_id = ?
AND u.sys_comp_code = ?
AND u.quarter = ?
AND (
u.is_active = '1'
OR (
u.completed_date IS NOT NULL
AND u.completed_date != '0000-00-00'
AND u.completed_date != '0000-00-00 00:00:00'
)
)
ORDER BY CASE
WHEN u.completed_date IS NULL
OR u.completed_date = '0000-00-00'
OR u.completed_date = '0000-00-00 00:00:00'
THEN 0
ELSE 1
END ASC,
u.updated_at DESC,
u.created_at DESC"
);
$stmtUserGoals->execute([$memberId, $sysCompCode, $currentQuarterCode]);
$userActiveGoalRows = $stmtUserGoals->fetchAll(PDO::FETCH_ASSOC);
$userActiveGoalCodes = array_values(array_unique(array_filter(array_map(function ($row) {
return trim((string)($row['goal_code'] ?? ''));
}, $userActiveGoalRows))));
$stmtAllUserGoals = db_conn()->prepare(
"SELECT u.goal_code,
u.quarter,
u.completed_date,
u.updated_at,
u.created_at,
u.is_active
FROM edu_user_learning_goals u
WHERE u.member_id = ?
AND u.sys_comp_code = ?
AND (
u.is_active = '1'
OR (
u.completed_date IS NOT NULL
AND u.completed_date != '0000-00-00'
AND u.completed_date != '0000-00-00 00:00:00'
)
)
ORDER BY u.quarter DESC,
CASE
WHEN u.completed_date IS NULL
OR u.completed_date = '0000-00-00'
OR u.completed_date = '0000-00-00 00:00:00'
THEN 0
ELSE 1
END ASC,
u.created_at ASC,
u.updated_at ASC"
);
$stmtAllUserGoals->execute([$memberId, $sysCompCode]);
$userAllGoalRows = $stmtAllUserGoals->fetchAll(PDO::FETCH_ASSOC);
usort($userAllGoalRows, function ($left, $right) {
$extractQuarterNum = function ($quarterCode) {
$quarterCode = trim((string)$quarterCode);
if (preg_match('/CA200Q(\d{2})/', $quarterCode, $match)) {
return (int)ltrim($match[1], '0');
}
return 0;
};
$isCompletedRow = function ($row) {
$completedDate = trim((string)($row['completed_date'] ?? ''));
return $completedDate !== ''
&& $completedDate !== '0000-00-00'
&& $completedDate !== '0000-00-00 00:00:00';
};
$leftQuarter = $extractQuarterNum($left['quarter'] ?? '');
$rightQuarter = $extractQuarterNum($right['quarter'] ?? '');
if ($leftQuarter !== $rightQuarter) {
return $rightQuarter <=> $leftQuarter;
}
$leftCompleted = $isCompletedRow($left);
$rightCompleted = $isCompletedRow($right);
if ($leftCompleted !== $rightCompleted) {
return $leftCompleted <=> $rightCompleted;
}
$leftCreatedAt = trim((string)($left['created_at'] ?? ''));
$rightCreatedAt = trim((string)($right['created_at'] ?? ''));
if ($leftCreatedAt !== $rightCreatedAt) {
return strcmp($leftCreatedAt, $rightCreatedAt);
}
$leftUpdatedAt = trim((string)($left['updated_at'] ?? ''));
$rightUpdatedAt = trim((string)($right['updated_at'] ?? ''));
return strcmp($leftUpdatedAt, $rightUpdatedAt);
});
$userAllGoalCodes = array_values(array_unique(array_filter(array_map(function ($row) {
return trim((string)($row['goal_code'] ?? ''));
}, $userAllGoalRows))));
}
} catch (Throwable $e) {
error_log('[myclass_list.php] active user goal fetch error: ' . $e->getMessage());
}
// 기본 목표 배열 (icon/gif/json은 고정, title/desc는 DB로 덮어씀)
$goals = [
['id' => 1, 'title' => '자기이해와 강점 찾기', 'desc' => '나의 성향, 가치관, 강점 등을 탐색하고 이해하며 자기인식을 높이는 과정', 'icon' => '01', 'gif' => 'ico_study_01.gif', 'json' => 'ico_study_01.json'],
['id' => 2, 'title' => '효과적인 의사소통 배우기', 'desc' => '상황에 맞는 말하기와 경청을 통해 타인과 원활하게 소통하는 방법을 익히는 과정', 'icon' => '02', 'gif' => 'ico_study_02.gif', 'json' => 'ico_study_02.json'],
['id' => 3, 'title' => '감정 조절과 자기관리', 'desc' => '다양한 감정을 인식하고 조절하여 안정적인 삶을 유지하는 자기관리 훈련 과정', 'icon' => '03', 'gif' => 'ico_study_03.gif', 'json' => 'ico_study_03.json'],
['id' => 4, 'title' => '비판적 사고와 문제 해결', 'desc' => '다양한 관점에서 생각하고 문제 상황을 논리적으로 해결하는 능력을 기르는 과정', 'icon' => '04', 'gif' => 'ico_study_04.gif', 'json' => 'ico_study_04.json'],
['id' => 5, 'title' => '협업과 팀워크 기르기', 'desc' => '타인과 협력하며 공동의 목표를 위해 함께 노력하는 태도와 기술을 기르는 과정', 'icon' => '05', 'gif' => 'ico_study_05.gif', 'json' => 'ico_study_05.json'],
['id' => 6, 'title' => '커리어 탐색과 역량 개발', 'desc' => '직무와 산업의 흐름을 이해하고, 자신의 커리어 방향성과 필요한 역량을 점검·계획하는 과정', 'icon' => '06', 'gif' => 'ico_study_06.gif', 'json' => 'ico_study_06.json'],
];
$quarterEndDates = [
'CA200Q01' => ['end' => '03-31', 'kr' => '3월 31일', 'num' => 1],
'CA200Q02' => ['end' => '06-30', 'kr' => '6월 30일', 'num' => 2],
'CA200Q03' => ['end' => '09-30', 'kr' => '9월 30일', 'num' => 3],
'CA200Q04' => ['end' => '12-31', 'kr' => '12월 31일', 'num' => 4],
];
$goalCodeById = [];
$goalQuarterById = [];
function myclass_list_fetch_quarter_goal_rows(PDO $conn, $currentYear, $currentQuarterCode) {
$orderSql = "ORDER BY CASE WHEN sort_order IS NULL OR sort_order = 0 THEN 999999 ELSE sort_order END ASC, goal_no ASC, goal_code ASC";
$stmt = $conn->prepare(
"SELECT goal_code,
title AS goal_title,
remarks AS goal_remarks,
quarter,
goal_no,
sort_order,
base_year
FROM edu_learning_goals
WHERE is_active = '1'
AND base_year = ?
AND quarter = ?
{$orderSql}"
);
$stmt->execute([(string)$currentYear, (string)$currentQuarterCode]);
$goalRows = $stmt->fetchAll(PDO::FETCH_ASSOC);
if (count($goalRows) < 6) {
$stmt = $conn->prepare(
"SELECT goal_code,
title AS goal_title,
remarks AS goal_remarks,
quarter,
goal_no,
sort_order,
base_year
FROM edu_learning_goals
WHERE is_active = '1'
AND quarter = ?
ORDER BY base_year DESC,
CASE WHEN sort_order IS NULL OR sort_order = 0 THEN 999999 ELSE sort_order END ASC,
goal_no ASC,
goal_code ASC
LIMIT 6"
);
$stmt->execute([(string)$currentQuarterCode]);
$goalRows = $stmt->fetchAll(PDO::FETCH_ASSOC);
}
return $goalRows;
}
function myclass_list_fetch_goal_rows_by_codes(PDO $conn, array $goalCodes) {
$goalCodes = array_values(array_unique(array_filter(array_map(function ($code) {
return trim((string)$code);
}, $goalCodes))));
if (empty($goalCodes)) {
return [];
}
$placeholders = implode(',', array_fill(0, count($goalCodes), '?'));
$stmt = $conn->prepare(
"SELECT goal_code,
title AS goal_title,
remarks AS goal_remarks,
quarter,
goal_no,
sort_order,
base_year
FROM edu_learning_goals
WHERE is_active = '1'
AND goal_code IN ($placeholders)"
);
$stmt->execute($goalCodes);
$rowsByCode = [];
foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {
$goalCode = trim((string)($row['goal_code'] ?? ''));
if ($goalCode !== '') {
$rowsByCode[$goalCode] = $row;
}
}
return $rowsByCode;
}
function myclass_list_resolve_goal_slot(array $goalRow) {
$sortOrder = (int)($goalRow['sort_order'] ?? 0);
if ($sortOrder >= 1 && $sortOrder <= 6) {
return $sortOrder;
}
$goalNo = (int)($goalRow['goal_no'] ?? 0);
if ($goalNo >= 1 && $goalNo <= 6) {
return $goalNo;
}
return 1;
}
// DB에서 현재 분기 목표 6개를 slot(1~6) 기준으로 고정 매핑한다.
try {
$goalRows = myclass_list_fetch_quarter_goal_rows(db_conn(), (string)$currentYear, (string)$currentQuarterCode);
$goalSlot = 1;
foreach ($goalRows as $goalRow) {
if ($goalSlot > 6) {
break;
}
$goalCode = trim((string)($goalRow['goal_code'] ?? ''));
if ($goalCode === '') {
continue;
}
$goalId = $goalSlot;
$goalCodeById[$goalId] = $goalCode;
$goalQuarterById[$goalId] = trim((string)($goalRow['quarter'] ?? '')) !== ''
? trim((string)($goalRow['quarter'] ?? ''))
: $currentQuarterCode;
$title = trim((string)($goalRow['goal_title'] ?? ''));
$desc = trim((string)($goalRow['goal_remarks'] ?? ''));
if ($title !== '') {
$goals[$goalId - 1]['title'] = $title;
}
if ($desc !== '') {
$goals[$goalId - 1]['desc'] = $desc;
}
$goalSlot++;
}
} catch (Throwable $e) {
error_log('[myclass_list.php] quarter goal fetch error: ' . $e->getMessage());
}
$books = [
['id' => 1, 'youtube' => 'KE_MeQZgnPM', 'title' => '조직도가 리셋된다!', 'sub' => '위계 중심 조직은\n더 이상 통하지 않습니다.', 'main' => '더 유연하게\n일할 방법', 'tag' => 'IT 테크', 'img' => 'img_book_01', 'postit' => ['일의 도구를 갖추고 효율을 높인다.', '반복 업무는 자동화로 줄이다.', '협업은 체계적인 방식으로!']],
['id' => 2, 'youtube' => 'a2l1uZfsRi0', 'title' => '혈당 스파이크', 'sub' => '식후 졸림은 의지 문제가\n아니라 혈당의 문제!', 'main' => '혈당을 안정시켜줄\n실생활 관리법', 'tag' => '웰니스', 'img' => 'img_book_02', 'postit' => ['식후 피로의 원인을 혈당에서 찾는다.', '탄수화물 섭취 점검', '생활습관으로 혈당 안정']],
['id' => 3, 'youtube' => 'IeF8r0ycgVg', 'title' => '제대로 쉬는 방법', 'sub' => '아무리 자도\n피곤한가요?', 'main' => '진짜 회복되는 쉼이\n무엇인지 알게됩니다.', 'tag' => '마인드셋', 'img' => 'img_book_03', 'postit' => ['쉬어도 피곤한 이유 이해', '회복을 위한 진짜 쉼 실천', '회복을 방해하는 습관 점검']],
['id' => 4, 'youtube' => 'KMZXMI0QPoA', 'title' => "'AI 도파민'의 바다에 빠진 이유", 'sub' => 'AI는 선택이 아닌\n생존의 도구', 'main' => 'AI는 도구가 아닌\n업무 파트너', 'tag' => 'IT 테크', 'img' => 'img_book_04', 'postit' => ['AI를 생존 도구가 아닌 성장 도구로 본다.', '반복 업무를 AI와 분담', 'AI를 도구가 아닌 협업 파트너로']],
['id' => 5, 'youtube' => 'CRKwszz6l2M', 'title' => '살찌고 망가진몸 되살리는 방법', 'sub' => '몸을 망쳤다면?\n다이어트 아닌 리셋이 답!', 'main' => '건강한 몸 되찾기', 'tag' => '웰니스', 'img' => 'img_book_05', 'postit' => ['단순 감량보다 건강 회복이 우선', '다이어트보다 나은 루틴을 만들기', '몸을 되살리는 작은 습관 실천']],
['id' => 6, 'youtube' => 'Gf5WoZ3BmgI', 'title' => '자기 관점의 힘', 'sub' => '당신의 삶을 바꾸는\n가장 강력한 무기?', 'main' => '자신만의 관점으로\n경쟁력을 키우는 방법', 'tag' => '리더십', 'img' => 'img_book_06', 'postit' => ['관점이 삶에 미치는 영향을 자각한다.', '나만의 강점과 가능성 정의', '더 넓은 시야로 삶을 바라본다.']],
];
// ====== 강제 완료 모드 전환: activeGoal의 completed_date가 있으면 $mode = 'complete' ======
// if (!empty($userActiveGoalRows) && !empty($goalCodeById)) {
// $goalIdByCode = array_flip($goalCodeById);
// foreach ($userActiveGoalRows as $row) {
// $goalCode = trim((string)($row['goal_code'] ?? ''));
// if ($goalCode === '' || !isset($goalIdByCode[$goalCode])) {
// continue;
// }
// $goalId = (int)$goalIdByCode[$goalCode];
// if ($goalId === $activeGoalId) {
// $completedDate = trim((string)($row['completed_date'] ?? ''));
// if ($completedDate !== '' && $completedDate !== '0000-00-00') {
// $mode = 'complete';
// }
// }
// }
// }
if (!empty($userActiveGoalRows) && !empty($goalCodeById)) {
$goalIdByCode = array_flip($goalCodeById);
$incompleteGoalIds = [];
$completedGoalIds = [];
$seenIncompleteGoalIds = [];
$seenCompletedGoalIds = [];
foreach ($userActiveGoalRows as $row) {
$goalCode = trim((string)($row['goal_code'] ?? ''));
if ($goalCode === '' || !isset($goalIdByCode[$goalCode])) {
continue;
}
$goalId = (int)$goalIdByCode[$goalCode];
$completedDate = trim((string)($row['completed_date'] ?? ''));
$isCompleted = (
$completedDate !== ''
&& $completedDate !== '0000-00-00'
&& $completedDate !== '0000-00-00 00:00:00'
);
if ($isCompleted) {
if (!isset($seenCompletedGoalIds[$goalId])) {
$completedGoalIds[] = $goalId;
$seenCompletedGoalIds[$goalId] = true;
}
} else {
if (!isset($seenIncompleteGoalIds[$goalId])) {
$incompleteGoalIds[] = $goalId;
$seenIncompleteGoalIds[$goalId] = true;
}
}
}
if (!empty($incompleteGoalIds)) {
$activeGoalId = (int)$incompleteGoalIds[0];
} elseif (!empty($completedGoalIds)) {
$activeGoalId = (int)$completedGoalIds[0];
}
if (!empty($completedGoalIds)) {
$completedGoalId = (int)$completedGoalIds[0];
} else {
$completedGoalId = $activeGoalId;
}
if (!empty($incompleteGoalIds) && !empty($completedGoalIds) && in_array($mode, ['learning', 'progress'], true)) {
$mode = 'extended';
}
}
function extract_video_id($value) {
$value = trim((string)$value);
if ($value === '') {
return '';
}
if (preg_match('~(?:v=|\.be/)([A-Za-z0-9_-]{11})~', $value, $m)) {
return $m[1];
}
if (preg_match('/^[A-Za-z0-9_-]{11}$/', $value)) {
return $value;
}
return '';
}
$booksByGoalId = [];
$booksByGoalCode = [];
$allContentIds = [];
// ====== 최종 완료 상태 체크: activeGoal의 completed_date가 있으면 mode를 'complete'로 확정 ======
if (!empty($userActiveGoalRows) && !empty($goalCodeById)) {
$goalIdByCode = array_flip($goalCodeById);
foreach ($userActiveGoalRows as $row) {
$goalCode = trim((string)($row['goal_code'] ?? ''));
if ($goalCode === '' || !isset($goalIdByCode[$goalCode])) {
continue;
}
$goalId = (int)$goalIdByCode[$goalCode];
if ($goalId === $activeGoalId) {
$completedDate = trim((string)($row['completed_date'] ?? ''));
if ($completedDate !== '' && $completedDate !== '0000-00-00') {
$mode = 'complete';
}
}
}
}
try {
$goalCodes = !empty($userAllGoalCodes)
? $userAllGoalCodes
: array_values(array_unique(array_filter($goalCodeById)));
if (count($goalCodes) > 0) {
$goalRowsByCodeForBooks = myclass_list_fetch_goal_rows_by_codes(db_conn(), $goalCodes);
$placeholders = implode(',', array_fill(0, count($goalCodes), '?'));
$quarterCodes = array_values(array_unique(array_filter(array_map(function ($row) {
return trim((string)($row['quarter'] ?? ''));
}, $userAllGoalRows))));
if (empty($quarterCodes)) {
$quarterCodes = [$currentQuarterCode];
}
$quarterPlaceholders = implode(',', array_fill(0, count($quarterCodes), '?'));
$stmtBooks = db_conn()->prepare(
"SELECT content_id,
goal_code,
category_group,
sort_order,
title,
description,
description1,
description2,
content_url,
thumbnail_url
FROM edu_contents
WHERE is_active = '1'
AND category_code = 'CA10001'
AND category_group IN ($quarterPlaceholders)
AND goal_code IN ($placeholders)
ORDER BY goal_code ASC, sort_order ASC, content_id ASC"
);
$stmtBooks->execute(array_merge($quarterCodes, $goalCodes));
$bookRows = $stmtBooks->fetchAll(PDO::FETCH_ASSOC);
// 1. 모든 content_id 수집
foreach ($bookRows as $bookRow) {
$cid = trim((string)($bookRow['content_id'] ?? ''));
if ($cid !== '') $allContentIds[] = $cid;
}
$memoMap = [];
if (!empty($allContentIds)) {
$in = implode(',', array_fill(0, count($allContentIds), '?'));
$stmtMemo = db_conn()->prepare(
"SELECT content_id, seq, title FROM edu_content_memos WHERE content_id IN ($in) AND (is_active = '1' OR is_active IS NULL) ORDER BY content_id ASC, seq ASC"
);
$stmtMemo->execute($allContentIds);
$memoRows = $stmtMemo->fetchAll(PDO::FETCH_ASSOC);
foreach ($memoRows as $m) {
$cid = (string)$m['content_id'];
if (!isset($memoMap[$cid])) $memoMap[$cid] = [];
$memoTitle = trim((string)($m['title'] ?? ''));
if ($memoTitle !== '') $memoMap[$cid][] = $memoTitle;
}
}
foreach ($bookRows as $bookRow) {
$goalCode = (string)($bookRow['goal_code'] ?? '');
if ($goalCode === '' || !isset($goalRowsByCodeForBooks[$goalCode])) {
continue;
}
$goalSlot = myclass_list_resolve_goal_slot($goalRowsByCodeForBooks[$goalCode]);
$contentUrl = trim((string)($bookRow['content_url'] ?? ''));
$videoId = extract_video_id($contentUrl);
$thumbnailUrl = trim((string)($bookRow['thumbnail_url'] ?? ''));
$sortOrder = (int)($bookRow['sort_order'] ?? 0);
if ($sortOrder < 1 || $sortOrder > 6) {
continue;
}
$templateBook = $books[$sortOrder - 1] ?? null;
if ($templateBook === null) {
continue;
}
if (!isset($booksByGoalId[$goalSlot])) {
$booksByGoalId[$goalSlot] = $books;
}
if (!isset($booksByGoalCode[$goalCode])) {
$booksByGoalCode[$goalCode] = $books;
}
$cid = (string)($bookRow['content_id'] ?? '');
$postitArr = isset($memoMap[$cid]) ? $memoMap[$cid] : (array)($templateBook['postit'] ?? []);
$bookPayload = [
'id' => (int)($templateBook['id'] ?? $sortOrder),
'content_id' => $cid,
'order' => $sortOrder,
'youtube' => $videoId !== '' ? $videoId : (string)($templateBook['youtube'] ?? ''),
'content_url' => $contentUrl,
'title' => trim((string)($bookRow['title'] ?? (string)($templateBook['title'] ?? ''))),
'description' => trim((string)($bookRow['description'] ?? '')),
'sub' => trim((string)($bookRow['description1'] ?? (string)($templateBook['sub'] ?? ''))),
'main' => trim((string)($bookRow['description2'] ?? (string)($templateBook['main'] ?? ''))),
'tag' => (string)($templateBook['tag'] ?? ''),
'img' => (string)($templateBook['img'] ?? 'img_book_01'),
'thumbnail' => $thumbnailUrl,
'postit' => $postitArr,
];
$booksByGoalId[$goalSlot][$sortOrder - 1] = $bookPayload;
$booksByGoalCode[$goalCode][$sortOrder - 1] = $bookPayload;
}
foreach ($booksByGoalId as $goalId => $goalBooks) {
if (is_array($goalBooks)) {
ksort($goalBooks);
$booksByGoalId[$goalId] = array_values($goalBooks);
}
}
foreach ($booksByGoalCode as $goalCode => $goalBooks) {
if (is_array($goalBooks)) {
ksort($goalBooks);
$booksByGoalCode[$goalCode] = array_values($goalBooks);
}
}
}
} catch (Throwable $e) {
error_log('[myclass_list.php] edu_contents fetch error: ' . $e->getMessage());
}
function get_goal_books($goalId, $booksByGoalId, $fallbackBooks) {
if (isset($booksByGoalId[$goalId]) && count($booksByGoalId[$goalId]) > 0) {
return $booksByGoalId[$goalId];
}
return $fallbackBooks;
}
function get_goal_books_by_code($goalCode, $goalSlot, $booksByGoalCode, $fallbackBooks) {
if ($goalCode !== '' && isset($booksByGoalCode[$goalCode]) && count($booksByGoalCode[$goalCode]) > 0) {
return $booksByGoalCode[$goalCode];
}
$fallbackGoalId = max(1, min(6, (int)$goalSlot));
return get_goal_books($fallbackGoalId, [], $fallbackBooks);
}
$activeGoal = $goals[$activeGoalId - 1];
$completedGoal = $goals[$completedGoalId - 1];
$activeGoalQuarter = $goalQuarterById[$activeGoal['id']] ?? $currentQuarterCode;
$quarterInfo = $quarterEndDates[$activeGoalQuarter] ?? ['end' => '12-31', 'kr' => '12월 31일', 'num' => 4];
$todayDateOnly = (clone $today)->setTime(0, 0, 0);
$targetDateStr = $currentYear . '-' . $quarterInfo['end'];
$targetDate = new DateTime($targetDateStr, new DateTimeZone('Asia/Seoul'));
$targetDate->setTime(0, 0, 0);
$daysInterval = $todayDateOnly->diff($targetDate);
$daysRemaining = max(0, (int) $daysInterval->format('%a'));
$daysDisplay = 'D-' . $daysRemaining;
$targetDateKr = $quarterInfo['kr'];
// ====== DEBUG LOGGING (브라우저 콘솔) ======
$debugRows = json_encode($userActiveGoalRows, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
$debugGoalCodeById = json_encode($goalCodeById, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
$debugGoalIdByCode = json_encode(isset($goalIdByCode) ? $goalIdByCode : [], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
$debugCompletedDate = '';
foreach ($userActiveGoalRows as $row) {
$goalCode = trim((string)($row['goal_code'] ?? ''));
if (isset($goalIdByCode[$goalCode]) && $goalIdByCode[$goalCode] == $activeGoalId) {
$debugCompletedDate = trim((string)($row['completed_date'] ?? ''));
break;
}
}
echo '<script>';
echo 'console.log("[myclass_list.php] DEBUG: $activeGoalId=' . $activeGoalId . ', $mode=' . $mode . '");';
echo 'console.log("userActiveGoalRows", ' . $debugRows . ');';
echo 'console.log("goalCodeById", ' . $debugGoalCodeById . ');';
echo 'console.log("goalIdByCode", ' . $debugGoalIdByCode . ');';
echo 'console.log("activeGoalId", ' . $activeGoalId . ');';
echo 'console.log("completedDate", "' . $debugCompletedDate . '");';
echo '</script>';
// ====== DEBUG LOGGING (브라우저 콘솔) ======
echo '<script>console.log("[myclass_list.php] DEBUG: $activeGoalId=' . $activeGoalId . ', $mode=' . $mode . '");</script>';
// ====== DEBUG LOGGING ======
error_log('[myclass_list.php] DEBUG: $activeGoalId=' . $activeGoalId . ', $mode=' . $mode);
// ========== DB에서 시청 완료된 영상의 content_id 목록 조회 ==========
// edu_learning_histories.completed_at 이 설정된 영상 = 다 본 영상 → 포스트잇 표시
$completedContentIds = [];
try {
$allContentIds = [];
foreach ($booksByGoalCode as $goalBooks) {
foreach ($goalBooks as $book) {
$cid = trim((string)($book['content_id'] ?? ''));
if ($cid !== '') {
$allContentIds[] = $cid;
}
}
}
$allContentIds = array_values(array_unique($allContentIds));
if (!empty($allContentIds) && $memberId !== '' && $sysCompCode !== '') {
$placeholders = implode(',', array_fill(0, count($allContentIds), '?'));
$stmtCompleted = db_conn()->prepare(
"SELECT content_id
FROM edu_learning_histories
WHERE member_id = ?
AND sys_comp_code = ?
AND content_id IN ($placeholders)
AND completed_at IS NOT NULL
AND completed_at != '0000-00-00 00:00:00'"
);
$stmtCompleted->execute(array_merge([$memberId, $sysCompCode], $allContentIds));
$completedContentIds = array_values(array_unique(array_filter(array_map(function ($value) {
return trim((string)$value);
}, array_column($stmtCompleted->fetchAll(PDO::FETCH_ASSOC), 'content_id')), function ($value) {
return $value !== '';
})));
}
} catch (Throwable $e) {
error_log('[myclass_list.php] completed histories fetch error: ' . $e->getMessage());
}
// ========== DB에서 한줄 소감문 작성된 영상의 content_id 목록 조회 ==========
$commentedContentIds = [];
try {
if (!empty($allContentIds) && $memberId !== '' && $sysCompCode !== '') {
$placeholders = implode(',', array_fill(0, count($allContentIds), '?'));
$stmtCommented = db_conn()->prepare(
"SELECT content_id
FROM edu_learning_histories
WHERE member_id = ?
AND sys_comp_code = ?
AND content_id IN ($placeholders)
AND comment IS NOT NULL
AND TRIM(comment) != ''"
);
$stmtCommented->execute(array_merge([$memberId, $sysCompCode], $allContentIds));
$commentedContentIds = array_values(array_unique(array_filter(array_map(function ($value) {
return trim((string)$value);
}, array_column($stmtCommented->fetchAll(PDO::FETCH_ASSOC), 'content_id')), function ($value) {
return $value !== '';
})));
}
} catch (Throwable $e) {
error_log('[myclass_list.php] commented histories fetch error: ' . $e->getMessage());
}
function render_goal_icon($goal, $completed = false) {
if ($completed) {
return '<img src="/img/myclass/ico_medal_completed.svg" alt="완독">';
}
return '<img class="lottie-fallback" src="/img/myclass/' . $goal['gif'] . '" alt="" aria-hidden="true" />'
. '<span class="lottie-icon lottie-' . $goal['icon'] . '" data-lottie-url="/img/myclass/' . $goal['json'] . '"></span>';
}
function render_book_item($book, $sectionKey, $completedContentIds, $commentedContentIds, $showProgressClass) {
$contentId = trim((string)($book['content_id'] ?? ''));
$isCompleted = $contentId !== '' && in_array($contentId, $completedContentIds, true);
$isCommentWritten = $contentId !== '' && in_array($contentId, $commentedContentIds, true);
$classes = ['books-item'];
if ($isCompleted) {
$classes[] = 'books-item-completed';
} elseif ($showProgressClass) {
$classes[] = 'books-item-progress';
}
$videoKey = trim((string)($book['content_id'] ?? ''));
if ($videoKey === '') {
$videoKey = (string)($book['id'] ?? '');
}
$bookId = $videoKey;
$bookTitle = htmlspecialchars((string)($book['title'] ?? ''), ENT_QUOTES, 'UTF-8');
$bookmarkId = 'like_book_' . $sectionKey . '_' . preg_replace('/[^A-Za-z0-9_-]/', '_', (string)$bookId);
$subText = str_replace('\\n', "\n", (string)($book['sub'] ?? ''));
$mainText = str_replace('\\n', "\n", (string)($book['main'] ?? ''));
$bookImg = (string)($book['img'] ?? 'img_book_01');
$bookThumbnail = trim((string)($book['thumbnail'] ?? ''));
$bookYoutube = (string)($book['youtube'] ?? '');
$coverDesktop = $bookThumbnail !== '' ? $bookThumbnail : '/img/myclass/' . $bookImg . '.png';
$coverMobile = $bookThumbnail !== '' ? $bookThumbnail : '/img/myclass/' . $bookImg . '_m.png';
$videoThumb = $bookThumbnail !== '' ? $bookThumbnail : 'https://img.youtube.com/vi/' . $bookYoutube . '/sddefault.jpg';
?>
<div class="<?php echo implode(' ', $classes); ?>" data-video-id="<?php echo htmlspecialchars((string)$videoKey, ENT_QUOTES, 'UTF-8'); ?>">
<div class="book-img-wrap" style="--book-img: url('<?php echo htmlspecialchars($coverDesktop, ENT_QUOTES, 'UTF-8'); ?>'); --book-img-m: url('<?php echo htmlspecialchars($coverMobile, ENT_QUOTES, 'UTF-8'); ?>')">
<span class="book-ico-pencil<?php echo $isCommentWritten ? ' book-ico-pencil-completed' : ''; ?>" aria-hidden="true" title="<?php echo $isCommentWritten ? '감상문 작성 완료' : '감상문 쓰기'; ?>"></span>
<picture class="book-img-picture">
<source media="(max-width: 1024px)" srcset="<?php echo htmlspecialchars($coverMobile, ENT_QUOTES, 'UTF-8'); ?>">
<img src="<?php echo htmlspecialchars($coverDesktop, ENT_QUOTES, 'UTF-8'); ?>" alt="추천 도서 이미지" class="book-img">
</picture>
</div>
<button type="button" class="book-info-btn" data-video-id="<?php echo htmlspecialchars((string)$videoKey, ENT_QUOTES, 'UTF-8'); ?>" aria-label="영상 상세 보기 - <?php echo $bookTitle; ?>">
<span class="book-info-wrap">
<div class="book-video-area">
<span class="book-video-thumb-wrap">
<img src="<?php echo htmlspecialchars($videoThumb, ENT_QUOTES, 'UTF-8'); ?>" alt="" class="book-video-thumb">
</span>
<label class="bookmark" for="<?php echo $bookmarkId; ?>" onclick="event.stopPropagation();"><input type="checkbox" id="<?php echo $bookmarkId; ?>" title="저장"></label>
</div>
<strong class="book-title"><?php echo $bookTitle; ?></strong>
<div class="book-desc">
<p class="book-desc-sub"><?php echo nl2br(htmlspecialchars($subText, ENT_QUOTES, 'UTF-8')); ?></p>
<p class="book-desc-main"><?php echo nl2br(htmlspecialchars($mainText, ENT_QUOTES, 'UTF-8')); ?></p>
</div>
<?php if ($isCompleted) : ?>
<div class="book-postit">
<strong class="book-postit-title">CHECK!</strong>
<ul class="book-postit-list">
<?php foreach (($book['postit'] ?? []) as $line) : ?>
<li><?php echo htmlspecialchars($line, ENT_QUOTES, 'UTF-8'); ?></li>
<?php endforeach; ?>
</ul>
</div>
<?php endif; ?>
<!-- <span class="book-tag"><?php echo htmlspecialchars((string)($book['tag'] ?? 'default'), ENT_QUOTES, 'UTF-8'); ?></span> -->
</span>
</button>
</div>
<?php
}
function render_section($goal, $quarterCode, $books, $completedContentIds, $commentedContentIds, $sectionKey, $completedSection = false, $showReset = true) {
global $currentYear;
$showProgressClass = !$completedSection && !empty($completedContentIds);
$quarterNum = 1;
if (preg_match('/CA200Q(\d{2})/', $quarterCode, $m)) {
$quarterNum = (int)ltrim($m[1], '0');
}
// 분기 숫자가 1~4가 아니면 1로 보정
if ($quarterNum < 1 || $quarterNum > 4) {
$quarterNum = 1;
}
?>
<div class="scroll-section scroll-section-reveal" data-quarter="<?php echo $quarterNum; ?>">
<aside class="scroll-section-sidebar">
<p class="sidebar-quarter">
<small><span><?php echo substr($currentYear, -2); ?></span>년</small>
<strong><?php echo $quarterNum; ?></strong>분기
</p>
<div class="quarter-select-wrap" aria-label="분기 선택"></div>
<a href="#bookshelf-section-<?php echo $sectionKey; ?>" class="sidebar-goal-item is-active" data-index="<?php echo $quarterNum - 1; ?>">
<span class="sidebar-goal-icon" aria-hidden="true"><?php echo render_goal_icon($goal, $completedSection); ?></span>
<h3 class="sidebar-goal-title"><?php echo htmlspecialchars($goal['title'], ENT_QUOTES, 'UTF-8'); ?></h3>
<p class="sidebar-goal-desc"><?php echo htmlspecialchars($goal['desc'], ENT_QUOTES, 'UTF-8'); ?></p>
<?php if ($showReset) : ?>
<button class="btn-reset-goal" type="button" data-reset-url="/skin/myclass.php?mode=additional&amp;completed_goal=<?php echo $goal['id']; ?>" onclick="event.preventDefault(); event.stopPropagation();">
<span class="ico-arrow-back" aria-hidden="true"></span> 목표 다시 선택
</button>
<?php endif; ?>
</a>
</aside>
<div class="scroll-section-content">
<section id="bookshelf-section-<?php echo $sectionKey; ?>" class="bookshelf-section" aria-label="<?php echo htmlspecialchars($goal['title'], ENT_QUOTES, 'UTF-8'); ?> 책장">
<div class="bookshelf">
<div class="books-list books-list--same-height js-video-cards-container" id="videoCardsContainer-<?php echo htmlspecialchars($sectionKey, ENT_QUOTES, 'UTF-8'); ?>" data-video-cards-container="true">
<?php foreach ($books as $book) {
render_book_item($book, $sectionKey, $completedContentIds, $commentedContentIds, $showProgressClass);
} ?>
</div>
<div class="shelf-plank">
<!-- <div class="shelf-tags">
<span><?php echo $quarterNum; ?>Q</span>
<ul>
<li>IT 테크</li>
<li>웰니스</li>
<li>마인드셋</li>
<li>리더십</li>
</ul>
</div> -->
</div>
</div>
</section>
</div>
</div>
<?php
}
?>
<!doctype html>
<html lang="ko">
<head>
<?php include(__DIR__ . '/_include/_head.php'); ?>
<link rel="stylesheet" type="text/css" href="/css/style.css" />
<style>
@media (min-width: 768px) {
.myclass-page.myclass-page-06 .myclass-content {
margin-top: 92px;
}
.myclass-page.myclass-page-06 .myclass-main {
padding-right: 76px;
box-sizing: border-box;
}
.myclass-page.myclass-page-06 .quarter-nav {
right: 14px;
padding-left: 16px;
}
.myclass-page.myclass-page-06 .bookshelf-section:not(.goal-slide-bookshelf):not(.books-list--no-hover) .books-item:hover {
transform: translateY(24px);
z-index: 5;
box-shadow: 0 -8px 24px rgba(255, 255, 255, 0.35), 0 8px 24px rgba(0, 0, 0, 0.12);
}
.myclass-page.myclass-page-06 .bookshelf-section:not(.goal-slide-bookshelf):not(.books-list--no-hover) .books-item:hover .book-video-area .book-video-thumb-wrap .book-video-thumb {
filter: grayscale(0%);
}
.myclass-page.myclass-page-06 .bookshelf-section:not(.goal-slide-bookshelf):not(.books-list--no-hover):has(.books-item:hover) .books-item:not(:hover) {
filter: brightness(0.6);
}
}
</style>
</head>
<body>
<div class="wrap myclass myclass-page">
<?php include(__DIR__ . '/_include/_header.php'); ?>
<div class="container">
<div class="myclass-wrap myclass-layout">
<div class="myclass-top-bar top-bar-absolute">
<div class="top-bar-left">
<!-- <div class="user-rank-area">
<div class="user-rank-area-inner">
<div class="user-profile">
<img src="/img/insight/profile.png" alt="프로필 이미지" />
</div>
<span class="rank-text">홍길동님 현재 <em>128</em>등 <span class="rank-change">(지난 주 대비 4↑)</span></span>
</div>
<div class="rank-toggle-wrap">
<button class="btn-rank-toggle" type="button" aria-label="순위 상세 보기" aria-expanded="false" aria-controls="rankListPopup">
<span class="ico-trophy" aria-hidden="true">
<img class="lottie-fallback" src="/img/myclass/ico_trophy.gif" alt="" aria-hidden="true" />
<span class="lottie-icon lottie-trophy" data-lottie-url="/img/myclass/ico_trophy.json"></span>
</span>
<span class="ico-chevron-down" aria-hidden="true"></span>
</button>
<div class="rank-list-popup" id="rankListPopup" role="region" aria-label="순위 목록" hidden>
<ul class="rank-list">
<li class="rank-item rank-1"><span class="rank-medal rank-gold" aria-hidden="true"><img src="/img/myclass/ico_medal_gold.png" alt="금메달 아이콘" /></span><span class="rank-name">박길동 <small>(바론컨설턴트)</small></span></li>
<li class="rank-item rank-2"><span class="rank-medal rank-silver" aria-hidden="true"><img src="/img/myclass/ico_medal_silver.png" alt="은메달 아이콘" /></span><span class="rank-name">홍길동 <small>(한맥기술)</small></span></li>
<li class="rank-item rank-3"><span class="rank-medal rank-bronze" aria-hidden="true"><img src="/img/myclass/ico_medal_bronze.png" alt="동메달 아이콘" /></span><span class="rank-name">김철수 <small>(삼안)</small></span></li>
<li class="rank-item rank-4"><span class="rank-num">4.</span><span class="rank-name">박철수 <small>(바른컨설턴트)</small></span></li>
<li class="rank-item rank-5"><span class="rank-num">5.</span><span class="rank-name">김영희 <small>(한맥기술)</small></span></li>
</ul>
</div>
</div>
</div> -->
</div>
<div class="page-intro" id="pageIntro">
<?php
// DB에서 현재 유저의 활성 목표 title 가져오기 (최신 분기 기준)
$activeGoalTitle = null;
if ($memberId !== '' && $sysCompCode !== '') {
try {
$stmtActiveGoal = db_conn()->prepare(
"SELECT g.title
FROM edu_user_learning_goals u
JOIN edu_learning_goals g ON u.goal_code = g.goal_code AND g.is_active = '1'
WHERE u.member_id = ?
AND u.sys_comp_code = ?
AND u.is_active = '1'
AND g.quarter = ?
ORDER BY u.completed_date IS NULL DESC, u.completed_date ASC, u.goal_code ASC
LIMIT 1"
);
$stmtActiveGoal->execute([$memberId, $sysCompCode, $currentQuarterCode]);
$activeGoalTitle = $stmtActiveGoal->fetchColumn();
} catch (Throwable $e) {
error_log('[myclass_list.php] active goal title fetch error: ' . $e->getMessage());
}
}
?>
<?php if ($mode === 'complete') : ?>
<div class="page-title">
<h3>축하합니다. <strong>[<?php echo htmlspecialchars($activeGoal['title'], ENT_QUOTES, 'UTF-8'); ?>]</strong>, 빛나는 책장을 완성했어요.</h3>
<p><em>다음 책장</em>을 열어볼까요?</p>
<button type="button" class="btn-next-growth" data-next-url="/skin/myclass.php?mode=completed&amp;completed_goal=<?php echo $activeGoal['id']; ?>">→ 다음 성장 이어가기</button>
</div>
<?php elseif ($mode === 'extended') : ?>
<div class="page-title page-title--goal">
<h3>두 번째 목표 <strong class="intro-goal-name">[<?php echo htmlspecialchars($activeGoal['title'], ENT_QUOTES, 'UTF-8'); ?>]</strong>로 새로운 책장이 열렸어요.</h3>
<p class="page-title-deadline">
<span class="deadline-highlight"><?php echo $daysDisplay; ?><small>(<?php echo $targetDateKr; ?>)</small></span>
<span class="deadline-body">까지 지금부터 또 하나의 지식을 쌓아보세요.</span>
</p>
</div>
<?php else : ?>
<div class="page-title page-title--goal">
<h3>
<?php if ($activeGoalTitle) : ?>
<strong class="intro-goal-name">[<?php echo htmlspecialchars($activeGoalTitle, ENT_QUOTES, 'UTF-8'); ?>]</strong>, 지금부터 하나씩 완독해보세요.
<?php else : ?>
<strong class="intro-goal-name">[<?php echo htmlspecialchars($activeGoal['title'], ENT_QUOTES, 'UTF-8'); ?>]</strong>, 지금부터 하나씩 완독해보세요.
<?php endif; ?>
</h3>
<p class="page-title-deadline">
<span class="deadline-highlight"><?php echo $daysDisplay; ?><small>(<?php echo $targetDateKr; ?>)</small></span>
<span class="deadline-body">나만의 지식을 채워갈 시간입니다.</span>
</p>
</div>
<?php endif; ?>
</div>
<div class="page-intro-mobile">
<div class="page-title page-title--goal">
<?php if ($mode === 'complete') : ?>
<h3>나만의 빛나는 책장을 완성했어요.</h3>
<button type="button" class="btn-next-growth" data-next-url="/skin/myclass.php?mode=completed&amp;completed_goal=<?php echo $activeGoal['id']; ?>">→ 다음 성장 이어가기</button>
<?php elseif ($mode === 'extended') : ?>
<p class="page-title-deadline">
<span class="deadline-highlight"><?php echo $daysDisplay; ?><small>(<?php echo $targetDateKr; ?>)</small></span>
<span class="deadline-body">까지 지금부터 또 하나의 지식을 쌓아보세요.</span>
</p>
<h3>두 번째 목표 <strong class="intro-goal-name">[<?php echo htmlspecialchars($activeGoal['title'], ENT_QUOTES, 'UTF-8'); ?>]</strong>로 새로운 책장이 열렸어요.</h3>
<?php else : ?>
<p class="page-title-deadline">
<span class="deadline-highlight"><?php echo $daysDisplay; ?><small>(<?php echo $targetDateKr; ?>)</small></span>
<span class="deadline-body">나만의 지식을 채워갈 시간입니다.</span>
</p>
<h3>
<?php if ($activeGoalTitle) : ?>
<strong class="intro-goal-name">[<?php echo htmlspecialchars($activeGoalTitle, ENT_QUOTES, 'UTF-8'); ?>]</strong>, 지금부터 하나씩 완독해보세요.
<?php else : ?>
<strong class="intro-goal-name">[<?php echo htmlspecialchars($activeGoal['title'], ENT_QUOTES, 'UTF-8'); ?>]</strong>, 지금부터 하나씩 완독해보세요.
<?php endif; ?>
</h3>
<?php endif; ?>
</div>
</div>
</div>
<nav class="quarter-nav" aria-label="분기 이동"></nav>
<div class="myclass-content">
<div class="myclass-main">
<div class="scroll-timeline" aria-hidden="true">
<div class="timeline-track">
<div class="timeline-progress-fill"></div>
</div>
</div>
<?php
$goalRowsByCode = myclass_list_fetch_goal_rows_by_codes(db_conn(), $userAllGoalCodes);
$renderedGoalSectionKeys = [];
foreach ($userAllGoalRows as $row) {
$goalCode = trim((string)($row['goal_code'] ?? ''));
$quarterCode = trim((string)($row['quarter'] ?? ''));
if ($goalCode === '' || $quarterCode === '' || !isset($goalRowsByCode[$goalCode])) {
continue;
}
$renderKey = $quarterCode . '|' . $goalCode;
if (isset($renderedGoalSectionKeys[$renderKey])) {
continue;
}
$renderedGoalSectionKeys[$renderKey] = true;
$goalRow = $goalRowsByCode[$goalCode];
$goalSlot = myclass_list_resolve_goal_slot($goalRow);
$baseGoal = $goals[$goalSlot - 1] ?? $goals[0];
$completedDate = trim((string)($row['completed_date'] ?? ''));
$isCompleted = (
$completedDate !== ''
&& $completedDate !== '0000-00-00'
&& $completedDate !== '0000-00-00 00:00:00'
);
$goal = [
'id' => $goalSlot,
'title' => trim((string)($goalRow['goal_title'] ?? '')) !== ''
? trim((string)($goalRow['goal_title'] ?? ''))
: (string)($baseGoal['title'] ?? ''),
'desc' => trim((string)($goalRow['goal_remarks'] ?? '')) !== ''
? trim((string)($goalRow['goal_remarks'] ?? ''))
: (string)($baseGoal['desc'] ?? ''),
'icon' => (string)($baseGoal['icon'] ?? '01'),
'gif' => (string)($baseGoal['gif'] ?? 'ico_study_01.gif'),
'json' => (string)($baseGoal['json'] ?? 'ico_study_01.json'),
];
$booksArr = get_goal_books_by_code($goalCode, $goalSlot, $booksByGoalCode, $books);
$sectionKey = ($isCompleted ? 'completed_' : 'active_') . preg_replace('/[^A-Za-z0-9_]/', '_', $quarterCode . '_' . $goalCode);
$showReset = !$isCompleted && $quarterCode === $currentQuarterCode;
render_section($goal, $quarterCode, $booksArr, $completedContentIds, $commentedContentIds, $sectionKey, $isCompleted, $showReset);
}
?>
</div>
</div>
</div>
</div>
</div>
<script>
<?php
$videoBooks = [];
$seenVideoIds = [];
foreach ($userAllGoalRows as $goalRow) {
$gCode = trim((string)($goalRow['goal_code'] ?? ''));
if ($gCode === '' || !isset($booksByGoalCode[$gCode])) {
continue;
}
foreach ($booksByGoalCode[$gCode] as $videoBook) {
$videoIdKey = trim((string)($videoBook['content_id'] ?? ''));
if ($videoIdKey === '') {
$videoIdKey = (string)($videoBook['id'] ?? '');
}
if ($videoIdKey === '' || isset($seenVideoIds[$videoIdKey])) {
continue;
}
$seenVideoIds[$videoIdKey] = true;
$videoBook['_goal_code'] = $gCode;
$videoBooks[] = $videoBook;
}
}
?>
window.MYCLASS_PAGE_VIDEOS = <?php echo json_encode(array_map(function ($book) use ($currentQuarterNum, $memberId, $sysCompCode, $completedContentIds, $commentedContentIds) {
$videoUrl = trim((string)($book['content_url'] ?? ''));
if ($videoUrl === '') {
$videoUrl = (string)($book['youtube'] ?? '');
}
$goalCode = trim((string)($book['_goal_code'] ?? ''));
$quarterLabel = $currentQuarterNum . '분기';
if ($goalCode !== '' && preg_match('/Q(\d{2})/', $goalCode, $m)) {
$quarterNum = (int)ltrim($m[1], '0');
if ($quarterNum >= 1 && $quarterNum <= 4) {
$quarterLabel = $quarterNum . '분기';
}
}
// DB에서 북마크 상태 조회
$bookmarkStatus = false;
$contentId = (string)($book['content_id'] ?? $book['id']);
$isCompleted = $contentId !== '' && in_array($contentId, $completedContentIds, true);
$isCommentWritten = $contentId !== '' && in_array($contentId, $commentedContentIds, true);
if ($memberId !== '' && $sysCompCode !== '' && $contentId !== '') {
try {
$stmtBookmark = db_conn()->prepare(
"SELECT is_active FROM edu_content_wishlist WHERE member_id = ? AND sys_comp_code = ? AND content_id = ? LIMIT 1"
);
$stmtBookmark->execute([$memberId, $sysCompCode, $contentId]);
$bookmarkRow = $stmtBookmark->fetch(PDO::FETCH_ASSOC);
if ($bookmarkRow && $bookmarkRow['is_active'] === '1') {
$bookmarkStatus = true;
}
} catch (Throwable $e) {
error_log('[myclass_list.php] bookmark fetch error: ' . $e->getMessage());
}
}
return [
'id' => $contentId,
'content_id' => $contentId,
'url' => $videoUrl,
'category' => '마이클래스',
'category_code' => 'CA10001',
'subcate' => $quarterLabel,
'title' => $book['title'],
'description' => (string)($book['description'] ?? ''),
'goal_code' => (string)($book['_goal_code'] ?? ''),
'type' => 'comment',
'bookmark' => $bookmarkStatus,
'completed' => $isCompleted,
'completed_at' => $isCompleted ? '1' : null,
'has_comment' => $isCommentWritten,
'postit' => array_values($book['postit'] ?? []),
'keywords' => [],
'gauge' => 0,
];
}, $videoBooks), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); ?>;
window.MYCLASS_COMPLETED_CONTENT_IDS = <?php echo json_encode(array_values($completedContentIds), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); ?>;
window.MYCLASS_COMMENTED_CONTENT_IDS = <?php echo json_encode(array_values($commentedContentIds), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); ?>;
</script>
<script src="/js/main/Videomodalmanager.js" defer></script>
<script src="/js/myclass_list.js" defer></script>
</body>
</html>