This commit is contained in:
2026-01-30 17:20:52 +09:00
commit 21b6332c9c
459 changed files with 190743 additions and 0 deletions

View File

@@ -0,0 +1,614 @@
<!DOCTYPE html>
<html lang="ko">
<head>
<?php include __DIR__ . "/_head.php"; ?>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Q&A 상세보기</title>
<link rel="stylesheet" href="/kngil/css/qa/font-awesome.min.css?ver=2303229">
<!-- 디자인팀 작성 -->
<script src="/kngil/js/lib/jquery-3.6.1.min.js" type="text/javascript"></script>
<script src="/kngil/js/qa/jquery.mousewheel.min.js" type="text/javascript"></script>
<script src="https://cdn.jsdelivr.net/npm/gsap@3.12.5/dist/gsap.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/gsap@3.12.5/dist/ScrollTrigger.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/gsap@3.12.5/dist/ScrollToPlugin.min.js"></script>
<link rel="stylesheet" href="https://unpkg.com/aos@2.3.1/dist/aos.css" type="text/css"/>
<link rel="stylesheet" href="https://unpkg.com/lenis@1.1.9/dist/lenis.css">
<script src="https://unpkg.com/aos@2.3.1/dist/aos.js"></script>
<script src="https://unpkg.com/lenis@1.1.9/dist/lenis.min.js"></script>
</head>
<body>
<div class="wrap">
<?php
include __DIR__ . "/_header.php";
if (!$isLogin) {
echo "<script>
alert('로그인 후 문의 등록이 가능합니다.');
location.href = '/kngil/skin/qa_list.skin.php';
</script>";
exit;
}
$isLoggedIn = !empty($_SESSION['login']);
?>
<?php
include __DIR__ . "/pop_login.php";
include __DIR__ . "/pop_join.php";
include __DIR__ . "/pop_agreement.php";
include __DIR__ . "/pop_mypage01.php";
include __DIR__ . "/pop_mypage02.php";
include __DIR__ . "/pop_mypage03.php";
include __DIR__ . "/pop_password.php";
include __DIR__ . "/pop_privacy.php";
include __DIR__ . "/pop_search.php";
?>
<div class="container faq">
<section class="sub-header">
<div class="page-title">
<h2 data-aos="fade-down" data-aos-duration="1000">Q&A</h2>
<p class="sub-txt">KNGIL 관련 문의하기</p>
</div>
<ul class="sub-tab">
<li><a href="/kngil/skin/faq_list.skin.php">자주하는 질문(FAQ)</a></li>
<li class="on"><a href="/kngil/bbs/qa_list.php">문의하기(Q&A)</a></li>
<li><a href="https://939.co.kr/saman/" target="_blank">원격지원</a></li>
</ul>
</section>
<section class="sub-content">
<h3 class="sub-tit">문의하기(Q&A)</h3>
<article class="qa-detail">
<div class="qa-view">
<div class="qa-view-header">
<div class="title-area">
<!-- 왼쪽: 카테고리 + 제목 -->
<div class="title-left">
<span class="cate"><?=htmlspecialchars($post['category_label'])?></span>
<span class="title"><?=htmlspecialchars($post['title'])?></span>
</div>
<!-- 오른쪽: 상태 -->
<div class="title-right">
<?php if ($isSuperAdmin): ?>
<form method="post" action="/kngil/bbs/qa_status.php">
<input type="hidden" name="post_id" value="<?= $post['post_id'] ?>">
<select name="status" onchange="this.form.submit()" class="status-select status-<?= htmlspecialchars($post['stat_bc']) ?>">
<option value="new" <?= $post['stat_bc']==='new'?'selected':'' ?>>문의접수</option>
<option value="review" <?= $post['stat_bc']==='review'?'selected':'' ?>>문의검토</option>
<option value="deep" <?= $post['stat_bc']==='deep'?'selected':'' ?>>정밀검토</option>
<option value="patch" <?= $post['stat_bc']==='patch'?'selected':'' ?>>패치예정</option>
<option value="done" <?= $post['stat_bc']==='done'?'selected':'' ?>>답변완료</option>
</select>
</form>
<?php else: ?>
<div class="status"><?= htmlspecialchars($post['status_label']) ?></div>
<?php endif; ?>
</div>
</div>
<div class="user-info">
<div class="user-item">
<span>
<i class="fa fa-clock-o" aria-hidden="true"></i>
<?=htmlspecialchars($post['cdt_dt'])?>
</span>
</div>
<div class="user-item">
<!-- 글번호 -->
<span class="post">
<i class="fa fa-hashtag" aria-hidden="true"></i>
<?= htmlspecialchars($post['post_id']) ?>
</span>
<?php if ($post['category'] === 'notice'): ?>
<!-- ✅ 공지사항이면 "관리자"만 표시 -->
<span>
<i class="fa fa-user" aria-hidden="true"></i>
관리자
</span>
<?php else: ?>
<!-- 작성자 이메일 -->
<span>
<i class="fa fa-envelope-o" aria-hidden="true"></i>
<?= htmlspecialchars($post['email']) ?>
</span>
<!-- 회사 -->
<span>
<i class="fa fa-building-o" aria-hidden="true"></i>
<?= htmlspecialchars($post['co_nm'] ?? '') ?>
</span>
<!-- 부서 -->
<?php if (!empty($post['dept_nm'])): ?>
<span>
<i class="fa fa-sitemap" aria-hidden="true"></i>
<?= htmlspecialchars($post['dept_nm']) ?>
</span>
<?php endif; ?>
<!-- 작성자 이름 -->
<!-- <span>
<i class="fa fa-user" aria-hidden="true"></i>
<?= htmlspecialchars($post['display_name']) ?>
</span> -->
<!-- 관리자 전용 -->
<?php if ($isSuperAdmin): ?>
<?php if (!empty($post['tel_no'])): ?>
<span>
<i class="fa fa-phone" aria-hidden="true"></i>
<?= htmlspecialchars($post['tel_no']) ?>
</span>
<?php endif; ?>
<span>
<i class="fa fa-key" aria-hidden="true"></i>
Q&A : <?= htmlspecialchars($post['post_id']) ?>
</span>
<?php endif; ?>
<?php endif; ?>
</div>
</div>
</div>
<div class="content">
<?= $post['content'] ?>
</div>
</div>
<!-- 첨부파일 -->
<?php if (!empty($attachments)): ?>
<div class="attachments" style="margin-top:20px;">
<h3>첨부파일</h3>
<ul>
<?php foreach ($attachments as $file): ?>
<li>
<?php if (!empty($_SESSION['user']['userId'])): ?>
<!-- ✅ 로그인한 경우: 다운로드 가능 -->
<a href="<?= htmlspecialchars($file['save_path']) ?>" download>
<?= htmlspecialchars($file['ori_name']) ?>
</a>
<?php else: ?>
<!-- 🚫 비회원: 이름만 표시 (다운로드 막음) -->
<?= htmlspecialchars($file['ori_name']) ?> <span style="color:#888;"></span>
<?php endif; ?>
<small>
(<?= round($file['file_size'] / 1024, 1) ?> KB,
<?= htmlspecialchars(substr($file['uploaded_at'],0,16)) ?>)
</small>
</li>
<?php endforeach; ?>
</ul>
</div>
<?php endif; ?>
</article>
<div class="btn-wrap right">
<!-- 수정 버튼 (로그인 사용자 == 작성자 인 경우에만) -->
<?php if ($me === $_SESSION['login']['user_id'] ?? ''): ?>
<button class="btn-secondary" onclick="location.href='/kngil/bbs/qa_write.php?id=<?= $post['post_id'] ?>'">수정</button>
<button class="btn-secondary" id="btn-delete" data-status="<?= $post['stat_bc'] ?>" data-id="<?= $post['post_id'] ?>">삭제</button>
<?php endif; ?>
<!-- <button onclick="history.back()">닫기</button> -->
<button class="btn-primary" onclick="location.href='/kngil/bbs/qa_list.php'">
<i class="fa fa-list" aria-hidden="true"></i> 목록</button>
</div>
<article class="comment-section">
<h4 class="comment-title">댓글 (<span id="comment-count"><?= count($comments ?? []) ?></span>)</h4>
<div class="comment-wrap form-wrap">
<div id="comments" class="comment-list">
<?php foreach ($comments ?? [] as $c): ?>
<div class="comment" id="comment-<?= $c['comment_id'] ?>">
<div class="comment-body">
<!-- 댓글 내용 -->
<div class="comment-text">
<?= nl2br(htmlspecialchars($c['content'] ?? '')) ?>
</div>
<!-- (이미지 영역 구조 유지 아직 로직 안 붙여도 됨) -->
<?php if (!empty($c['images'])): ?>
<div class="comment-images" style="margin-top:6px; display:flex; gap:12px;">
<?php foreach ($c['images'] as $img): ?>
<div style="text-align:center;">
<img
src="<?= htmlspecialchars($img['thumb_path']) ?>"
onclick="window.open('<?= htmlspecialchars($img['file_path']) ?>', '_blank')"
style="width:140px; border-radius:6px; margin:4px; cursor:pointer;">
<div style="font-size:12px; color:#555; margin-top:4px; max-width:140px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;">
<?= htmlspecialchars($img['file_name']) ?>
</div>
</div>
<?php endforeach; ?>
</div>
<?php endif; ?>
<!-- 작성자 + 날짜 -->
<div class="comment-meta">
<span class="comment-author">
<strong><?= htmlspecialchars($c['user_nm'] ?? '') ?></strong>
</span>
<span class="comment-date">
<small><?= $c['cdt_dt'] ?? '' ?></small>
</span>
</div>
</div>
<!-- 수정 / 삭제 버튼 -->
<div class="comment-actions" style="text-align:right;">
<?php if ($isLoggedIn): ?>
<?php if (($_SESSION['login']['user_id'] ?? '') === ($c['commenter'] ?? '')): ?>
<button class="btn-edit" data-id="<?= $c['comment_id'] ?>">수정</button>
<button class="btn-delete" data-id="<?= $c['comment_id'] ?>">삭제</button>
<?php elseif ($isSuperAdmin): ?>
<button class="btn-delete" data-id="<?= $c['comment_id'] ?>">삭제</button>
<?php endif; ?>
<?php endif; ?>
</div>
<!-- 수정 모드 textarea (구조 유지) -->
<div class="comment-edit-box" style="display:none; margin-top:5px;">
<textarea class="comment-edit-input" rows="3"><?= htmlspecialchars($c['content'] ?? '') ?></textarea>
<div class="comment-edit-actions" style="text-align:right; margin-top:3px;">
<button class="btn-save-edit" data-id="<?= $c['comment_id'] ?>">저장</button>
<button class="btn-cancel-edit">취소</button>
</div>
</div>
</div>
<?php endforeach; ?>
</div>
<!-- 신규 댓글 입력 -->
<?php if ($isLoggedIn): ?>
<form id="commentForm" enctype="multipart/form-data">
<div class="comment-row">
<textarea id="newComment"
name="comment"
class="input-text"
rows="3"
placeholder="댓글을 입력하세요"></textarea>
<button type="submit" class="btn-save">저장</button>
</div>
<!-- 업로드 영역 (구조 유지) -->
<div class="upload-wrap">
<label for="commentImages" class="img-upload-btn">📷 이미지 첨부</label>
<input type="file" id="commentImages" name="images[]" accept="image/*" multiple>
<span id="fileName" class="file-name">선택된 파일 없음</span>
<div id="previewArea"></div>
</div>
</form>
<?php else: ?>
<p style="color:#888;">댓글 작성은 로그인 후 이용 가능합니다.</p>
<?php endif; ?>
</div>
</article>
</section>
</div> <!-- container END -->
<?php include __DIR__ . "/_footer.php"; ?>
</div> <!-- wrapper END -->
<script>
$(document).on('click', '#btn-delete', function (e) {
e.preventDefault();
const status = $(this).data('status');
const postId = $(this).data('id');
if (status === 'review' || status === 'done') {
alert("검토중이거나 답변완료 된 상태에서는 글을 삭제할 수 없습니다.\n관리자에게 문의해 주세요");
return;
}
if (!confirm("정말 삭제하시겠습니까?\n댓글과 첨부파일도 함께 삭제됩니다.")) {
return;
}
const form = document.createElement('form');
form.method = 'post';
form.action = "/kngil/bbs/qa_detail.php"; // ✅ 중요
const actionInput = document.createElement('input');
actionInput.type = 'hidden';
actionInput.name = 'action';
actionInput.value = 'delete';
const idInput = document.createElement('input');
idInput.type = 'hidden';
idInput.name = 'post_id';
idInput.value = postId;
form.appendChild(actionInput);
form.appendChild(idInput);
document.body.appendChild(form);
form.submit();
});
</script>
<script src="/kngil/js/index.js"></script>
<script src="/kngil/js/mypage.js"></script>
<script src="/kngil/js/join.js"></script>
<script type="module" src="/kngil/js/login.js"></script>
<script src="/kngil/js/login_sms.js"></script>
<script>
try{
AOS.init();
if (typeof Lenis !== 'undefined') {
const lenis = new Lenis();
lenis.on('scroll', ScrollTrigger.update);
gsap.ticker.add((time)=>{ lenis.raf(time * 1000) });
gsap.ticker.lagSmoothing(0);
window.lenis = lenis;
}
} catch(e){ console.error(e); }
</script>
<script>
//상태 색상 변경 동적 업데이트
document.addEventListener('DOMContentLoaded', () => {
document.querySelectorAll('.status-select').forEach(sel => {
sel.addEventListener('change', function() {
this.className = 'status-select status-' + this.value;
});
});
});
// ✅ 댓글 저장 (이미지 포함)
document.getElementById('commentForm')?.addEventListener('submit', async (e) => {
e.preventDefault();
const text = document.getElementById('newComment').value.trim();
const files = document.getElementById('commentImages').files;
if (!text && files.length === 0) {
alert('댓글 또는 이미지를 입력하세요.');
return;
}
const formData = new FormData();
formData.append('postId', <?= json_encode($post['post_id']) ?>);
formData.append('comment', text);
for (let i = 0; i < files.length; i++) {
formData.append('images[]', files[i]);
}
const res = await fetch('/kngil/bbs/qa_comment.php', {
method: 'POST',
body: formData,
credentials: 'include'
});
const json = await res.json();
if (json.status !== 'ok') {
alert(json.message || '저장 실패');
return;
}
// ✅ DOM 추가 (서버에서 이미지 URL 배열 반환한다고 가정)
const div = document.createElement('div');
div.className = 'comment';
div.id = "comment-" + json.comment_id;
const safeText = (json.comment_text || '').replace(/\r?\n/g, '<br>');
let imageHTML = "";
if (json.images && json.images.length > 0) {
imageHTML = `
<div class="comment-images"
style="margin-top:6px; display:flex; gap:12px; flex-wrap:wrap;">
${json.images
.map(
(img) => `
<div style="text-align:center; width:140px;">
<img
src="${img.thumb}"
onclick="window.open('${img.full}', '_blank')"
style="width:140px; height:auto; border-radius:6px; cursor:pointer;"
>
<div style="
font-size:12px;
color:#555;
margin-top:4px;
white-space:nowrap;
overflow:hidden;
text-overflow:ellipsis;
">
${img.name}
</div>
</div>
`
)
.join("")}
</div>`;
}
div.innerHTML = `
<div class="comment-body">
<div class="comment-text">${safeText}</div>
${imageHTML}
<div class="comment-meta">
<span class="comment-author"><strong>${json.user_name || json.login_id}</strong></span>
<span class="comment-date"><small>${json.created_at}</small></span>
</div>
</div>
`;
document.getElementById('comments').append(div);
document.getElementById('newComment').value = '';
document.getElementById('commentImages').value = '';
document.getElementById('previewArea').innerHTML = '';
});
// 댓글 수정 버튼
$(document).on('click', '.btn-edit', function() {
const id = $(this).data('id');
const $comment = $(this).closest('.comment');
const $text = $comment.find('.comment-text');
const original = $text.text().trim();
// input으로 교체
$text.replaceWith(`<textarea class="edit-input" data-id="${id}" rows="3">${original}</textarea>`);
$(this).text("저장").removeClass("btn-edit").addClass("btn-save-edit").attr("data-id", id);;
});
// 댓글 수정 저장
$(document).on('click', '.btn-save-edit', function() {
const id = $(this).data('id');
const $input = $('.edit-input[data-id="'+id+'"]');
const newText = $input.val().trim();
if (!newText) return alert("내용을 입력하세요");
console.log("update click", id, newText);
$.post("/kngil/bbs/qa_comment_update.php",
{ commentId:id, comment:newText },
function(res) {
if (res.status === 'ok') {
alert(res.message || "댓글이 수정되었습니다.");
$input.replaceWith(`<div class="comment-text">${newText}</div>`);
$('.btn-save-edit[data-id="'+id+'"]').text("수정")
.removeClass("btn-save-edit").addClass("btn-edit");
} else {
alert(res.message || "수정 실패");
}
}, "json");
});
document.addEventListener('click', function(e) {
// 수정 버튼 클릭 → textarea 열기
if (e.target.classList.contains('btn-edit')) {
const comment = e.target.closest('.comment');
comment.querySelector('.comment-text').style.display = 'none';
comment.querySelector('.comment-actions').style.display = 'none';
comment.querySelector('.comment-edit-box').style.display = 'block';
}
// 취소 버튼 → 원래 상태로
if (e.target.classList.contains('btn-cancel-edit')) {
const comment = e.target.closest('.comment');
comment.querySelector('.comment-text').style.display = 'inline';
comment.querySelector('.comment-actions').style.display = 'block';
comment.querySelector('.comment-edit-box').style.display = 'none';
}
// 저장 버튼 → AJAX로 수정 요청 (예시)
if (e.target.classList.contains('btn-save-edit')) {
const comment = e.target.closest('.comment');
const id = e.target.dataset.id;
const newText = comment.querySelector('.comment-edit-input').value;
// TODO: AJAX 호출해서 서버 업데이트
console.log("수정 저장:", id, newText);
// UI 갱신
comment.querySelector('.comment-text').textContent = newText;
comment.querySelector('.comment-text').style.display = 'inline';
comment.querySelector('.comment-actions').style.display = 'block';
comment.querySelector('.comment-edit-box').style.display = 'none';
}
});
// 댓글 삭제 버튼
$(document).on("click", ".btn-delete", function () {
if (!confirm("정말 삭제하시겠습니까?")) return;
var commentId = $(this).data("id");
$.ajax({
url: "/kngil/bbs/qa_comment_delete.php",
type: "POST",
data: { commentId: commentId },
dataType: "json",
success: function (res) {
if (res.status === "ok") {
// DOM에서도 삭제
$("#comment-" + commentId).fadeOut(300, function () {
$(this).remove();
});
// ✅ 댓글 수 감소
var countEl = $("#comment-count");
var current = parseInt(countEl.text(), 10);
if (current > 0) {
countEl.text(current - 1);
}
alert("댓글이 삭제되었습니다.");
} else {
alert(res.message || "삭제 실패");
}
},
error: function (xhr) {
alert("삭제 요청 에러: " + xhr.responseText);
}
});
});
</script>
</body>
</html>
<script>
// ✅ 댓글 이미지 미리보기
document.addEventListener("change", function (e) {
if (e.target.id === "commentImages") {
const preview = document.getElementById("previewArea");
preview.innerHTML = ""; // 초기화
[...e.target.files].forEach((file) => {
// ▶ 개별 박스 생성
const box = document.createElement("div");
box.style.width = "80px";
box.style.marginRight = "10px";
box.style.textAlign = "center";
box.style.display = "inline-block";
// ▶ 썸네일 이미지
const img = document.createElement("img");
img.src = URL.createObjectURL(file);
img.style.width = "80px";
img.style.height = "80px";
img.style.objectFit = "cover";
img.style.borderRadius = "6px";
// ▶ 파일명 (길면 ... 처리)
const name = document.createElement("div");
name.textContent = file.name;
name.style.marginTop = "4px";
name.style.whiteSpace = "nowrap";
name.style.overflow = "hidden";
name.style.textOverflow = "ellipsis";
name.style.width = "80px";
// ▶ 박스에 넣기
box.appendChild(img);
box.appendChild(name);
// ▶ preview 영역에 넣기
preview.appendChild(box);
});
}
});
</script>