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 '';
// ====== DEBUG LOGGING (브라우저 콘솔) ======
echo '';
// ====== 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 '';
}
return ''
. '';
}
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';
?>