337 lines
13 KiB
PHP
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>
|