Files
edu/skin/onboarding.php
T

337 lines
13 KiB
PHP

<?php
require_once __DIR__ . "/../bbs/auth.php";
edu_require_login();
require_once __DIR__ . "/../bbs/db_conn.php";
$pdo = db_conn();
$memberId = (string)($_SESSION['member_id'] ?? '');
$sysCompCode = (string)($_SESSION['sys_comp_code'] ?? '');
if ($memberId === '' || $sysCompCode === '') {
http_response_code(401);
exit('로그인 정보가 유효하지 않습니다. 다시 로그인해 주세요.');
}
// 사용자 정보 및 join_date 조회
$stmtUser = $pdo->prepare("
SELECT name, rank_name, join_date, sys_comp_code
FROM edu_users
WHERE member_id = ?
AND sys_comp_code = ?
LIMIT 1
");
$stmtUser->execute([$memberId, $sysCompCode]);
$userRow = $stmtUser->fetch() ?: [];
$userName = $userRow['name'] ?? '사용자';
$userRank = $userRow['rank_name'] ?? '';
$sysCompCode = (string)($userRow['sys_comp_code'] ?? $sysCompCode);
// join_date 기준 14일 마감 계산
$today = new DateTime(date('Y-m-d'));
try {
$joinBase = !empty($userRow['join_date'])
? new DateTime(date('Y-m-d', strtotime($userRow['join_date'])))
: clone $today;
} catch (Exception $e) {
$joinBase = clone $today;
}
$deadline = (clone $joinBase)->modify('+14 days');
$dDay = ($today <= $deadline) ? (int)$today->diff($deadline)->days : 0;
$deadlineStr = $deadline->format('n월 j일');
?>
<!doctype html>
<html lang="ko">
<head>
<?php include(__DIR__ . "/_include/_head.php") ?>
<link rel="stylesheet" type="text/css" href="/css/main.css" />
<!-- <style>
/* puzzle-onboarding.js 미구현 동안 스크롤 임시 허용 */
html, body { overflow: auto !important; height: auto !important; }
.wrap.onboarding { position: static !important; overflow: visible !important; }
.wrap.onboarding .container { position: static !important; overflow: visible !important; }
</style> -->
</head>
<body>
<div class="wrap onboarding">
<?php include(__DIR__ . "/_include/_header.php") ?>
<!-- container -->
<div class="container">
<div class="puzzle-wrap">
<div class="page-title">
<h3>
<span><em><?= htmlspecialchars(trim($userName . ' ' . $userRank), ENT_QUOTES, 'UTF-8') ?></em>님,</span> <?= $dDay>0?'입사를 환영합니다.':''?>
</h3>
<p>
<em><?= $dDay>0?'D-'.$dDay:'' ?><small><?= $dDay>0?'('.htmlspecialchars($deadlineStr, ENT_QUOTES, 'UTF-8').')':'' ?></small><?= $dDay>0?'까지':''?> 필수 콘텐츠</em> 시청을
완료해주세요.
</p>
</div>
<div class="puzzle-area">
<div class="puzzle-board" id="puzzleBoard">
<!-- SVG가 여기에 동적으로 삽입됩니다 -->
<p class="img-box family">
<img src="/img/onboarding/img_obj_01.png" loading="lazy" />
</p>
<p class="img-box hanmac">
<img src="/img/onboarding/img_obj_02.png" loading="lazy" />
</p>
<p class="img-box value">
<img src="/img/onboarding/img_obj_03.png" loading="lazy" />
</p>
<p class="img-box company">
<img src="/img/onboarding/img_obj_04.png" loading="lazy" />
</p>
</div>
</div>
</div>
</div>
<!-- // container -->
</div>
<?php
// 1. 온보딩 콘텐츠 조회 (category_group 기준)
$onboardingGroups = [
'CA200O01', 'CA200O02', 'CA200O03', 'CA200O04', 'CA200O05',
'CA200O06', 'CA200O07', 'CA200O08', 'CA200O09', 'CA200O10',
];
$placeholders = implode(',', array_fill(0, count($onboardingGroups), '?'));
// edu_content_histories 테이블 존재 여부에 따라 쿼리 분기
$useContentHistories = true;
try {
$checkTable = $pdo->query("SHOW TABLES LIKE 'edu_content_histories'");
if ($checkTable->rowCount() === 0) {
$useContentHistories = false;
}
} catch (Exception $e) {
$useContentHistories = false;
}
if ($useContentHistories) {
try {
$stmt = $pdo->prepare("
SELECT
c.*,
COALESCE(lh.watch_tm, 0) AS watch_tm,
COALESCE(lh.content_tm, 0) AS content_tm,
lh.completed_at AS lh_completed_at,
CASE
WHEN ch.content_id IS NULL THEN 'none'
WHEN ch.completed_at IS NOT NULL THEN 'completed'
ELSE 'in_progress'
END AS learning_status
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_histories ch
ON ch.content_id = c.content_id
AND ch.member_id = ?
AND ch.sys_comp_code = ?
WHERE c.category_group IN ($placeholders)
ORDER BY FIELD(c.category_group, 'CA200O01','CA200O02','CA200O03','CA200O04','CA200O05','CA200O06','CA200O07','CA200O08','CA200O09','CA200O10'), COALESCE(NULLIF(c.sort_order, 0), 9999), c.content_id
");
$stmt->execute(array_merge([$memberId, $sysCompCode, $memberId, $sysCompCode], $onboardingGroups));
$rows = $stmt->fetchAll();
} catch (Exception $e) {
// edu_content_histories JOIN 실패 시 fallback
$useContentHistories = false;
}
}
if (!$useContentHistories) {
// fallback: edu_learning_histories만 사용
$stmt = $pdo->prepare("
SELECT
c.*,
COALESCE(lh.watch_tm, 0) AS watch_tm,
COALESCE(lh.content_tm, 0) AS content_tm,
lh.completed_at AS lh_completed_at,
CASE
WHEN lh.content_id IS NULL THEN 'none'
WHEN lh.completed_at IS NOT NULL THEN 'completed'
ELSE 'in_progress'
END AS learning_status
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 = ?
WHERE c.category_group IN ($placeholders)
ORDER BY FIELD(c.category_group, 'CA200O01','CA200O02','CA200O03','CA200O04','CA200O05','CA200O06','CA200O07','CA200O08','CA200O09','CA200O10'), COALESCE(NULLIF(c.sort_order, 0), 9999), c.content_id
");
$stmt->execute(array_merge([$memberId, $sysCompCode], $onboardingGroups));
$rows = $stmt->fetchAll();
}
// 2. category_group -> 퍼즐 피스/영역 매핑
// area: family(소개), hanmac(DX), value(가치), company(회사생활)
$categoryMap = [
'CA200O01' => ['pieceId' => 1, 'group' => 3, 'area' => 'value'],
'CA200O02' => ['pieceId' => 2, 'group' => 2, 'area' => 'hanmac'],
'CA200O03' => ['pieceId' => 3, 'group' => 2, 'area' => 'hanmac'],
'CA200O04' => ['pieceId' => 4, 'group' => 2, 'area' => 'hanmac'],
'CA200O05' => ['pieceId' => 5, 'group' => 2, 'area' => 'hanmac'],
'CA200O06' => ['pieceId' => 6, 'group' => 3, 'area' => 'value'],
'CA200O07' => ['pieceId' => 7, 'group' => 4, 'area' => 'company'],
'CA200O08' => ['pieceId' => 8, 'group' => 4, 'area' => 'company'],
'CA200O09' => ['pieceId' => 9, 'group' => 1, 'area' => 'family'],
'CA200O10' => ['pieceId' => 10, 'group' => 1, 'area' => 'family'],
];
// ✅ 온보딩 챕터 제목을 edu_codes 테이블에서 동적으로 로드
// 관리자가 edu_codes에서 code_name을 수정하면 자동으로 퍼즐 제목이 변경됨
$chapterNameMap = [];
try {
$stmtCodes = $pdo->prepare("
SELECT base_code, code_name
FROM edu_codes
WHERE group_code = 'CA200'
AND base_code IN ('CA200O01','CA200O02','CA200O03','CA200O04','CA200O05','CA200O06','CA200O07','CA200O08','CA200O09','CA200O10')
ORDER BY base_code
");
$stmtCodes->execute();
$codeRows = $stmtCodes->fetchAll();
foreach ($codeRows as $codeRow) {
$baseCode = (string)($codeRow['base_code'] ?? '');
$codeName = trim((string)($codeRow['code_name'] ?? ''));
if ($baseCode && $codeName) {
$chapterNameMap[$baseCode] = $codeName;
}
}
} catch (Exception $e) {
// 학습 부분은 진행되어야 하므로 조용히 실패 처리
error_log('[onboarding.php] edu_codes 조회 실패: ' . $e->getMessage());
}
// 3. 같은 category_group은 같은 모달(챕터)로 묶고 lessons에 1/2/3차시 누적
$chapterMap = [];
foreach ($rows as $idx => $row) {
$categoryGroup = $row['category_group'] ?? '';
$mapped = $categoryMap[$categoryGroup] ?? null;
// description 컬럼명 편차 대응
$description = trim((string)($row['description'] ?? $row['description1'] ?? $row['description_1'] ?? $row['content_desc'] ?? $row['content_desc1'] ?? $row['content_description'] ?? $row['content_description1'] ?? ''));
$description2 = trim((string)($row['description2'] ?? $row['description_2'] ?? $row['content_desc2'] ?? $row['content_description2'] ?? ''));
// 미정의 category_group은 뒤 순번으로 안전 처리
$pid = $mapped['pieceId'] ?? ($idx + 1);
$group = $mapped['group'] ?? 1;
$area = $mapped['area'] ?? 'family';
if (!isset($chapterMap[$categoryGroup])) {
$chapterMap[$categoryGroup] = [
'id' => 0, // 아래에서 순차 부여
// ✅ 챕터 제목은 고정된 chapterNameMap에서 가져옴 (콘텐츠 title이 아님)
'name' => $chapterNameMap[$categoryGroup] ?? str_replace('[온보딩] ', '', (string)$row['title']),
'pieceId' => $pid,
'type' => 'youtube',
'group' => $group,
'category_group' => $categoryGroup,
'area' => $area,
'lessons' => [],
];
}
$lessonNo = (int)($row['sort_order'] ?? 0);
if ($lessonNo <= 0) {
$lessonNo = count($chapterMap[$categoryGroup]['lessons']) + 1;
}
// learning_status: none(미진행), in_progress(학습중), completed(학습완료)
$learningStatus = $row['learning_status'] ?? 'none';
$chapterMap[$categoryGroup]['lessons'][] = [
'content_id' => $row['content_id'],
// 북마크/댓글은 edu_contents.content_id와 동일한 문자열 키를 사용한다.
'bookmark_content_id' => $row['content_id'],
'comment_content_id' => $row['content_id'],
'title' => $row['title'],
'label' => str_replace('[온보딩] ', '', $row['title']),
'description' => $description,
'description2' => $description2,
'url' => $row['content_url'],
'completed' => ($learningStatus === 'completed'),
'learning_status' => $learningStatus,
'watch_tm' => (int)$row['watch_tm'],
'content_tm' => (int)$row['content_tm'],
];
}
// 4. 북마크 상태 로드 (is_active: 1=표시, 0=미표시)
$bookmarkIds = [];
foreach ($chapterMap as $chapterItem) {
foreach (($chapterItem['lessons'] ?? []) as $lessonItem) {
$bookmarkId = trim((string)($lessonItem['bookmark_content_id'] ?? ''));
if ($bookmarkId !== '') {
$bookmarkIds[] = $bookmarkId;
}
}
}
$bookmarkIds = array_values(array_unique($bookmarkIds));
$wishlistMap = [];
if (!empty($bookmarkIds)) {
$wishlistPlaceholders = implode(',', array_fill(0, count($bookmarkIds), '?'));
$stmtWishlist = $pdo->prepare(
"SELECT content_id, is_active
FROM edu_content_wishlist
WHERE member_id = ?
AND sys_comp_code = ?
AND content_id IN ($wishlistPlaceholders)"
);
$stmtWishlist->execute(array_merge([$memberId, $sysCompCode], $bookmarkIds));
$wishlistRows = $stmtWishlist->fetchAll();
foreach ($wishlistRows as $wishlistRow) {
$wishlistMap[(string)$wishlistRow['content_id']] = ((string)($wishlistRow['is_active'] ?? '0') === '1');
}
}
foreach ($chapterMap as &$chapterItem) {
foreach ($chapterItem['lessons'] as &$lessonItem) {
$bookmarkId = trim((string)($lessonItem['bookmark_content_id'] ?? ''));
$lessonItem['is_bookmarked'] = ($bookmarkId !== '' && !empty($wishlistMap[$bookmarkId]));
}
unset($lessonItem);
}
unset($chapterItem);
// category_group 정의 순서대로 챕터 정렬 + id 재부여
$puzzleData = [];
foreach ($onboardingGroups as $code) {
if (!isset($chapterMap[$code])) {
continue;
}
$chapterMap[$code]['id'] = count($puzzleData) + 1;
$puzzleData[] = $chapterMap[$code];
}
$puzzleJson = json_encode($puzzleData, JSON_UNESCAPED_UNICODE);
?>
<script>
// 1. CONFIG 설정
window.puzzleConfig = {
COMPLETION_MODE: "COMPLETED",
CURRENT_MEMBER_ID: <?= json_encode($memberId, JSON_UNESCAPED_UNICODE) ?>,
};
// 2. 챕터 기반 데이터 설정 (DB에서 동적 로드)
window.puzzleChapterData = <?= $puzzleJson ?>;
</script>
<!-- JavaScript -->
<script src="/js/puzzle-onboarding.js" defer></script>
</body>
</html>