diff --git a/www/html/Admin/admin.css b/www/html/Admin/admin.css index e97eef1..d9a4c78 100644 --- a/www/html/Admin/admin.css +++ b/www/html/Admin/admin.css @@ -2758,6 +2758,36 @@ code { } /* ===== Evidence Cards admin panel ===== */ +.cm-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); + gap: 16px; + margin-top: 14px; +} +.cm-pick { + border: 1px solid rgba(124, 154, 255, 0.25); + border-radius: 12px; + padding: 12px; + background: rgba(20, 26, 48, 0.4); + display: flex; + flex-direction: column; + gap: 10px; +} +.cm-pick-label { + font-weight: 800; + font-size: 0.92rem; + color: #cdd6ff; +} +.cm-pick-prev { + width: 100%; + height: 200px; + object-fit: contain; + background: #0a0e1a; + border-radius: 8px; + border: 1px solid rgba(124, 154, 255, 0.18); +} +.cm-pick select { width: 100%; } + .ev-suspect-block { border: 1px solid rgba(124, 154, 255, 0.25); border-radius: 12px; diff --git a/www/html/Admin/admin.js b/www/html/Admin/admin.js index e89faab..67dbe85 100644 --- a/www/html/Admin/admin.js +++ b/www/html/Admin/admin.js @@ -4184,6 +4184,144 @@ if (saveBtn) saveBtn.addEventListener('click', saveEvidenceCardsPanel); })(); + /* ===== รูปคดี & Cutscene (LobbyA → LobbyB) ===== */ + var CASE_MEDIA_API = '/Game/api/case-media'; + var CASE_MEDIA_IMAGES_API = '/Game/api/case-media-images'; + var CASE_MEDIA_COUNT = 15; + var caseMediaData = null; + var caseMediaImages = null; // { case:[...], cutscene:[...] } + var caseMediaCurrent = '1'; + + function caseMediaPopulateSelect() { + var sel = el('case-media-case'); + if (!sel || sel.options.length) return; + for (var i = 1; i <= CASE_MEDIA_COUNT; i++) { + var o = document.createElement('option'); + o.value = String(i); o.textContent = 'คดี ' + i; + sel.appendChild(o); + } + } + + function loadCaseMediaPanel() { + caseMediaPopulateSelect(); + if (caseMediaImages == null) { + fetch(CASE_MEDIA_IMAGES_API + '?_=' + Date.now(), { credentials: 'include', cache: 'no-store' }) + .then(function (r) { return r.json(); }) + .then(function (j) { caseMediaImages = (j && j.images) || { case: [], cutscene: [] }; renderCaseMedia(caseMediaCurrent); }) + .catch(function () { caseMediaImages = { case: [], cutscene: [] }; }); + } + fetch(CASE_MEDIA_API + '?_=' + Date.now(), { credentials: 'include', cache: 'no-store' }) + .then(function (r) { return r.json(); }) + .then(function (j) { + caseMediaData = (j && j.cases) ? j : { cases: {} }; + var sel = el('case-media-case'); + if (sel) caseMediaCurrent = sel.value || '1'; + renderCaseMedia(caseMediaCurrent); + }) + .catch(function (e) { + setMsg('case-media-msg', (e.message || 'โหลดไม่ได้') + ' — เช็คว่า Node รัน Game/server.js และ /Game/api/case-media ไม่ 404', 'error'); + }); + } + + function caseMediaGetCase(cid) { + if (!caseMediaData) caseMediaData = { cases: {} }; + if (!caseMediaData.cases) caseMediaData.cases = {}; + var n = parseInt(cid, 10) || 1; + if (!caseMediaData.cases[cid]) caseMediaData.cases[cid] = {}; + var c = caseMediaData.cases[cid]; + if (typeof c.name !== 'string') c.name = 'คดีที่ ' + n; + if (typeof c.art !== 'string') c.art = '/Main-Lobby/IMAGE/case/case-' + n + '.png'; + if (typeof c.detail !== 'string') c.detail = '/Main-Lobby/IMAGE/case/case-detail-' + n + '.png'; + if (!Array.isArray(c.story)) c.story = []; + if (typeof c.story[0] !== 'string') c.story[0] = '/Main-Lobby/IMAGE/cutscene/case-' + n + '-story-1.png'; + if (typeof c.story[1] !== 'string') c.story[1] = '/Main-Lobby/IMAGE/cutscene/case-' + n + '-story-2.png'; + return c; + } + + /** ตัวเลือกรูป (dropdown + พรีวิว) — kind = 'case' | 'cutscene' */ + function caseMediaImagePicker(label, kind, current, onChange) { + var wrap = document.createElement('div'); + wrap.className = 'cm-pick'; + var lab = document.createElement('div'); lab.className = 'cm-pick-label'; lab.textContent = label; + var prev = document.createElement('img'); prev.className = 'cm-pick-prev'; prev.alt = ''; prev.src = current; + prev.onerror = function () { this.style.opacity = '0.25'; }; + var sel = document.createElement('select'); sel.className = 'admin-inp-num'; + var list = (caseMediaImages && caseMediaImages[kind]) ? caseMediaImages[kind].slice() : []; + if (current && list.indexOf(current) < 0) list.unshift(current); + list.forEach(function (u) { + var o = document.createElement('option'); + o.value = u; o.textContent = u.split('/').pop(); + if (u === current) o.selected = true; + sel.appendChild(o); + }); + sel.addEventListener('change', function () { prev.src = sel.value; onChange(sel.value); }); + wrap.appendChild(lab); wrap.appendChild(prev); wrap.appendChild(sel); + return wrap; + } + + function renderCaseMedia(cid) { + caseMediaCurrent = cid; + var tag = el('case-media-set-tag'); + if (tag) tag.textContent = 'กำลังแก้: คดี ' + cid; + var root = el('case-media-root'); + if (!root) return; + var c = caseMediaGetCase(cid); + root.innerHTML = ''; + + var nameLabel = document.createElement('label'); + nameLabel.className = 'admin-field'; + nameLabel.textContent = 'ชื่อคดี'; + var nameInp = document.createElement('input'); + nameInp.type = 'text'; nameInp.maxLength = 80; nameInp.value = c.name || ''; + nameInp.addEventListener('input', function () { c.name = nameInp.value; }); + nameLabel.appendChild(nameInp); + root.appendChild(nameLabel); + + var grid = document.createElement('div'); + grid.className = 'cm-grid'; + grid.appendChild(caseMediaImagePicker('รูปเลือกคดี (การ์ด)', 'case', c.art, function (v) { c.art = v; })); + grid.appendChild(caseMediaImagePicker('รูปรายละเอียดคดี', 'case', c.detail, function (v) { c.detail = v; })); + grid.appendChild(caseMediaImagePicker('Cutscene รูปที่ 1', 'cutscene', c.story[0], function (v) { c.story[0] = v; })); + grid.appendChild(caseMediaImagePicker('Cutscene รูปที่ 2', 'cutscene', c.story[1], function (v) { c.story[1] = v; })); + root.appendChild(grid); + } + + function saveCaseMediaPanel() { + if (!caseMediaData) return; + var btn = el('btn-case-media-save'); + if (btn) btn.disabled = true; + setMsg('case-media-msg', 'กำลังบันทึก…', ''); + fetch(CASE_MEDIA_API, { + method: 'PUT', + credentials: 'include', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(caseMediaData), + }).then(function (r) { + return r.text().then(function (t) { + var j = {}; + try { j = t ? JSON.parse(t) : {}; } catch (e) { /* ignore */ } + if (!r.ok || !j.ok) throw new Error((j && j.error) || ('HTTP ' + r.status)); + return j; + }); + }).then(function (j) { + if (j && j.cases) caseMediaData = { cases: j.cases }; + renderCaseMedia(caseMediaCurrent); + setMsg('case-media-msg', 'บันทึกแล้ว — มีผลในเกมเมื่อรีเฟรช LobbyA/LobbyB', 'ok'); + }).catch(function (e) { + setMsg('case-media-msg', e.message || 'บันทึกไม่ได้', 'error'); + }).then(function () { + if (btn) btn.disabled = false; + }); + } + + (function bindCaseMediaPanel() { + caseMediaPopulateSelect(); + var caseSel = el('case-media-case'); + if (caseSel) caseSel.addEventListener('change', function () { renderCaseMedia(caseSel.value || '1'); }); + var saveBtn = el('btn-case-media-save'); + if (saveBtn) saveBtn.addEventListener('click', saveCaseMediaPanel); + })(); + function setTab(name) { document.querySelectorAll('.tab').forEach(function (t) { var on = t.getAttribute('data-tab') === name; @@ -4212,6 +4350,7 @@ if (name === 'quiz') loadQuizSettingsPanel(); if (name === 'special-quiz') loadSpecialQuizPanel(); if (name === 'evidence-cards') loadEvidenceCardsPanel(); + if (name === 'case-media') loadCaseMediaPanel(); if (name === 'quiz-carry') loadQuizCarryPanel(); if (name === 'quiz-battle') loadQbBattlePanel(); if (name === 'jump-survive') loadJumpSurviveTimingPanel(); diff --git a/www/html/Admin/index.html b/www/html/Admin/index.html index 68ed63e..a2b9606 100644 --- a/www/html/Admin/index.html +++ b/www/html/Admin/index.html @@ -10,7 +10,7 @@ - + @@ -84,6 +84,7 @@ + @@ -332,6 +333,28 @@

+ +