478 lines
13 KiB
PHP
478 lines
13 KiB
PHP
<?php
|
|
$pdo = new PDO("mysql:host=localhost;dbname=egbim;charset=utf8mb4","egbim","baron3840!!");
|
|
|
|
$rows = $pdo->query("
|
|
SELECT
|
|
s.schedule_date,
|
|
s.time_period,
|
|
s.content,
|
|
s.emp_no,
|
|
m.emp_name,
|
|
c.client_name
|
|
FROM sales_schedules s
|
|
LEFT JOIN sales_members m ON s.emp_no = m.emp_no
|
|
LEFT JOIN sales_clients c ON s.client_code = c.client_code
|
|
")->fetchAll(PDO::FETCH_ASSOC);
|
|
|
|
// 사번 매핑
|
|
$tagMap = [
|
|
"j01201" => "권",
|
|
"223070" => "염",
|
|
"m21430" => "김",
|
|
"b21367" => "윤",
|
|
];
|
|
|
|
$eventsData = [];
|
|
|
|
foreach ($rows as $r) {
|
|
$date = $r["schedule_date"];
|
|
$empNo = strtolower($r["emp_no"]);
|
|
|
|
if (!isset($eventsData[$date])) {
|
|
$eventsData[$date] = [];
|
|
}
|
|
|
|
$time = trim($r["time_period"]); // 공백 제거
|
|
$time = str_replace(["\r", "\n", "\t"], "", $time); // 숨은 문자 제거
|
|
|
|
$eventsData[$date][] = [
|
|
"time" => $time, // ⭐ 핵심
|
|
"title" => $r["content"],
|
|
"tags" => [ $tagMap[$empNo] ?? "" ]
|
|
];
|
|
}
|
|
// JS로 전달
|
|
echo "<script>var eventsData = " . json_encode($eventsData, JSON_UNESCAPED_UNICODE) . ";</script>";
|
|
?>
|
|
|
|
<div class="main-weekly weekly" id="weekly">
|
|
<div class="weekly-grid">
|
|
<div class="weekly-header">
|
|
<div class="weekly-title">
|
|
<p>주간 영업 계획</p>
|
|
<div class="week-navigation">
|
|
<button class="week-nav-btn" id="prevWeek" title="이전 주">
|
|
<svg
|
|
width="20"
|
|
height="20"
|
|
viewBox="0 0 24 24"
|
|
fill="none"
|
|
stroke="#424242"
|
|
stroke-width="2"
|
|
>
|
|
<path d="M15 18l-6-6 6-6" />
|
|
</svg>
|
|
</button>
|
|
<h3 id="weekTitle">11월 5주차</h3>
|
|
<button class="week-nav-btn" id="nextWeek" title="다음 주">
|
|
<svg
|
|
width="20"
|
|
height="20"
|
|
viewBox="0 0 24 24"
|
|
fill="none"
|
|
stroke="#424242"
|
|
stroke-width="2"
|
|
>
|
|
<path d="M9 18l6-6-6-6" />
|
|
</svg>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<ul class="person-list">
|
|
<li>
|
|
<span class="badge-green">권</span><span class="name">권혁진 <small>수석</small></span>
|
|
</li>
|
|
<li>
|
|
<span class="badge-purple">염</span><span class="name">염승호 <small>수석</small></span>
|
|
</li>
|
|
<li>
|
|
<span class="badge-teal">김</span><span class="name">김지영 <small>선임</small></span>
|
|
</li>
|
|
<li>
|
|
<span class="badge-black">윤</span><span class="name">윤준수 <small>선임</small></span>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
<div class="schedule" id="scheduleContainer"></div>
|
|
</div>
|
|
<div class="main-issue">
|
|
<div class="issue-header">
|
|
<h3>주요 이슈 사항</h3>
|
|
</div>
|
|
<div class="issue">
|
|
<ul class="issue-list">
|
|
<li>주요 이슈 A</li>
|
|
<li>주요 이슈 B</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<script src="./assets/js/schedule-manager.min.js"></script>
|
|
<!-- <script src="./assets/js/events-data.min.js"></script> -->
|
|
<script>
|
|
const personOrder = {
|
|
"권": 1,
|
|
"염": 2,
|
|
"김": 3,
|
|
"윤": 4,
|
|
};
|
|
|
|
function isMorning(event) {
|
|
return event.time === "오전";
|
|
}
|
|
|
|
function isAfternoon(event) {
|
|
return event.time === "오후";
|
|
}
|
|
|
|
// 태그 매핑
|
|
const tagMapping = {
|
|
권: "green",
|
|
염: "purple",
|
|
김: "teal",
|
|
윤: "black",
|
|
};
|
|
|
|
(function() {
|
|
'use strict';
|
|
|
|
// ⭐ scheduleManager를 상단에 선언 (초기화는 나중에)
|
|
let scheduleManager;
|
|
async function loadIssueText() {
|
|
|
|
// ⭐ 현재 주간 달 기준으로 변경
|
|
const year = currentWeekStart.getFullYear();
|
|
const month = String(currentWeekStart.getMonth() + 1).padStart(2, "0");
|
|
const ym = `${year}-${month}`;
|
|
|
|
let res = await fetch(`/egbim/bbs/sales_issue.php?action=get&issue_month=${ym}`);
|
|
let json = await res.json();
|
|
|
|
const issue = json.issue_text ?? "";
|
|
|
|
const ul = document.querySelector(".issue-list");
|
|
ul.innerHTML = "";
|
|
|
|
if (issue.trim() === "") {
|
|
ul.innerHTML = "<li>이번 달 주요 이슈가 없습니다.</li>";
|
|
return;
|
|
}
|
|
|
|
issue.split("\n").forEach(line => {
|
|
if (line.trim() !== "") {
|
|
ul.innerHTML += `<li>${line}</li>`;
|
|
}
|
|
});
|
|
}
|
|
|
|
|
|
// 현재 날짜에서 이번 주 월요일 구하기
|
|
function getThisMonday() {
|
|
const today = new Date();
|
|
const day = today.getDay(); // 0(일) ~ 6(토)
|
|
const diff = day === 0 ? -6 : 1 - day; // 일요일이면 -6, 아니면 1-day
|
|
|
|
const monday = new Date(today);
|
|
monday.setDate(today.getDate() + diff);
|
|
monday.setHours(0, 0, 0, 0); // 시간 초기화
|
|
|
|
return monday;
|
|
}
|
|
|
|
// 현재 주차 상태
|
|
let currentWeekStart = getThisMonday();
|
|
|
|
// 주차 타이틀 업데이트
|
|
function updateWeekTitle() {
|
|
const weekStart = new Date(currentWeekStart);
|
|
const month = weekStart.getMonth() + 1;
|
|
const startDate = weekStart.getDate();
|
|
|
|
// 해당 월의 몇 번째 주인지 계산
|
|
const firstDayOfMonth = new Date(
|
|
weekStart.getFullYear(),
|
|
weekStart.getMonth(),
|
|
1,
|
|
);
|
|
|
|
const firstDayOfWeek = firstDayOfMonth.getDay() < 2 ? 7 : firstDayOfMonth.getDay();
|
|
const offset = firstDayOfWeek - 1;
|
|
|
|
//const weekOfMonth = Math.ceil((startDate + firstDayOfMonth.getDay()) / 7);
|
|
const weekOfMonth = Math.floor((startDate + offset) / 7);
|
|
|
|
document.getElementById("weekTitle").textContent =
|
|
`${month}월 ${weekOfMonth}주차`;
|
|
}
|
|
|
|
// 요일 이름 가져오기
|
|
function getDayName(dayIndex) {
|
|
const days = ["일", "월", "화", "수", "목", "금", "토"];
|
|
return days[dayIndex];
|
|
}
|
|
|
|
|
|
// 주간 일정 렌더링
|
|
function renderWeeklySchedule() {
|
|
const container = document.getElementById("scheduleContainer");
|
|
container.innerHTML = "";
|
|
|
|
// 월요일부터 금요일까지 (5일)
|
|
for (let i = 0; i < 5; i++) {
|
|
const currentDate = new Date(currentWeekStart);
|
|
currentDate.setDate(currentWeekStart.getDate() + i);
|
|
|
|
const year = currentDate.getFullYear();
|
|
const month = String(currentDate.getMonth() + 1).padStart(2, "0");
|
|
const day = String(currentDate.getDate()).padStart(2, "0");
|
|
const dateKey = `${year}-${month}-${day}`;
|
|
|
|
const dayOfWeek = getDayName(currentDate.getDay());
|
|
const dayEvents = eventsData[dateKey] || [];
|
|
|
|
dayEvents.sort((a, b) => {
|
|
const aTag = a.tags[0] ?? "";
|
|
const bTag = b.tags[0] ?? "";
|
|
|
|
return (personOrder[aTag] || 99) - (personOrder[bTag] || 99);
|
|
});
|
|
|
|
// 카드 생성
|
|
const dayCard = document.createElement("div");
|
|
dayCard.className = "day-card";
|
|
dayCard.dataset.date = dateKey;
|
|
|
|
let morningHTML = "";
|
|
let afternoonHTML = "";
|
|
|
|
dayEvents.forEach((event) => {
|
|
console.log(
|
|
dateKey,
|
|
event.time,
|
|
JSON.stringify(event.time),
|
|
isMorning(event),
|
|
isAfternoon(event)
|
|
);
|
|
});
|
|
|
|
dayEvents.forEach((event) => {
|
|
const tagsHTML = event.tags
|
|
.map(tag => `<span class="badge-${tagMapping[tag]}">${tag}</span>`)
|
|
.join("");
|
|
|
|
const itemHTML = `
|
|
<li>
|
|
<a href="#" class="schedule-item">
|
|
${tagsHTML}
|
|
${event.title}
|
|
</a>
|
|
</li>
|
|
`;
|
|
|
|
if (isMorning(event)) {
|
|
morningHTML += itemHTML;
|
|
}
|
|
|
|
if (isAfternoon(event)) {
|
|
afternoonHTML += itemHTML;
|
|
}
|
|
});
|
|
|
|
let dayHTML = `
|
|
<div class="day-title">${day}일 <span>(${dayOfWeek})</span></div>
|
|
|
|
<div class="day">
|
|
<dl>
|
|
<dt>오전</dt>
|
|
<dd class="day-content">
|
|
${morningHTML}
|
|
</dd>
|
|
</dl>
|
|
|
|
<dl>
|
|
<dt>오후</dt>
|
|
<dd class="day-content">
|
|
${afternoonHTML}
|
|
</dd>
|
|
</dl>
|
|
</div>
|
|
`;
|
|
|
|
dayCard.innerHTML = dayHTML;
|
|
container.appendChild(dayCard);
|
|
}
|
|
|
|
updateWeekTitle();
|
|
if (scheduleManager && typeof scheduleManager.initialize === "function") {
|
|
scheduleManager.initialize();
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 이벤트 리스너 등록 함수
|
|
function setupEventListeners() {
|
|
const prevWeekBtn = document.getElementById("prevWeek");
|
|
const nextWeekBtn = document.getElementById("nextWeek");
|
|
|
|
// 이전 주로 이동
|
|
if (prevWeekBtn) {
|
|
prevWeekBtn.addEventListener("click", () => {
|
|
currentWeekStart.setDate(currentWeekStart.getDate() - 7);
|
|
renderWeeklySchedule();
|
|
loadIssueText();
|
|
});
|
|
}
|
|
|
|
// 다음 주로 이동
|
|
if (nextWeekBtn) {
|
|
nextWeekBtn.addEventListener("click", () => {
|
|
currentWeekStart.setDate(currentWeekStart.getDate() + 7);
|
|
renderWeeklySchedule();
|
|
loadIssueText();
|
|
});
|
|
}
|
|
}
|
|
// ⭐ 초기화 함수
|
|
function init() {
|
|
// ScheduleManager가 존재하는지 확인
|
|
if (typeof ScheduleManager !== 'undefined') {
|
|
scheduleManager = new ScheduleManager();
|
|
scheduleManager.initialize();
|
|
}
|
|
|
|
// 이벤트 리스너 등록
|
|
setupEventListeners();
|
|
|
|
// 초기 렌더링
|
|
renderWeeklySchedule();
|
|
loadIssueText();
|
|
}
|
|
|
|
// ⭐ DOM 로드 완료 후 실행
|
|
if (document.readyState === 'loading') {
|
|
document.addEventListener('DOMContentLoaded', init);
|
|
} else {
|
|
init();
|
|
}
|
|
})();
|
|
</script>
|
|
|
|
<script>
|
|
let isCalendarLoaded = false;
|
|
// 모달 열기
|
|
function openModal() {
|
|
const existingModal = document.getElementById("scheduleModal");
|
|
|
|
// 기존 모달이 있으면 제거
|
|
if (existingModal) {
|
|
existingModal.remove();
|
|
}
|
|
|
|
// 항상 새로 로드 (데이터 최신화)
|
|
fetchScheduleDetail();
|
|
}
|
|
|
|
// 모달 닫기
|
|
function closeModal() {
|
|
const modal = document.getElementById("scheduleModal");
|
|
modal.classList.add("closing");
|
|
isCalendarLoaded = false;
|
|
setTimeout(() => {
|
|
modal.remove();
|
|
}, 400);
|
|
}
|
|
|
|
// AJAX로 calendar.html 페이지 가져오기
|
|
let calendarScriptsLoaded = false;
|
|
|
|
function fetchScheduleDetail() {
|
|
const weekly = document.getElementById("weekly");
|
|
|
|
fetch(`./html/main_sub/calendar.php`)
|
|
.then((response) => response.text())
|
|
.then((html) => {
|
|
// 기존 모달 제거
|
|
const existingModal = document.getElementById("scheduleModal");
|
|
if (existingModal) existingModal.remove();
|
|
|
|
// 새로운 모달 삽입
|
|
const parser = new DOMParser();
|
|
const doc = parser.parseFromString(html, "text/html");
|
|
|
|
// ⭐ body의 자식 노드들을 추가
|
|
doc.body.childNodes.forEach(node => {
|
|
if (node.nodeType === Node.ELEMENT_NODE || node.nodeType === Node.TEXT_NODE) {
|
|
weekly.appendChild(node.cloneNode(true));
|
|
}
|
|
});
|
|
|
|
// ⭐ 스크립트 처리 개선
|
|
const scripts = doc.querySelectorAll("script");
|
|
|
|
if (!calendarScriptsLoaded) {
|
|
// 첫 번째 로딩: 모든 스크립트 실행
|
|
scripts.forEach(script => {
|
|
const newScript = document.createElement("script");
|
|
if (script.src) {
|
|
newScript.src = script.src;
|
|
} else {
|
|
newScript.textContent = script.textContent;
|
|
}
|
|
document.body.appendChild(newScript);
|
|
});
|
|
calendarScriptsLoaded = true;
|
|
} else {
|
|
// ⭐ 두 번째 이후: 인라인 스크립트만 재실행
|
|
scripts.forEach(script => {
|
|
if (!script.src) {
|
|
// src가 없는 인라인 스크립트만 실행
|
|
try {
|
|
eval(script.textContent);
|
|
} catch (error) {
|
|
console.error('Script execution error:', error);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
// ⭐ 모달이 추가된 후 초기화 함수 명시적 호출
|
|
setTimeout(() => {
|
|
if (typeof initializeCalendar === "function") {
|
|
initializeCalendar();
|
|
}
|
|
if (typeof renderCalendar === "function") {
|
|
renderCalendar();
|
|
}
|
|
if (typeof checkOverflow === "function") {
|
|
checkOverflow();
|
|
}
|
|
if (typeof bindToggleEvents === "function") {
|
|
bindToggleEvents();
|
|
}
|
|
}, 100);
|
|
})
|
|
.catch(error => {
|
|
console.error('Calendar loading error:', error);
|
|
});
|
|
}
|
|
|
|
// 일정 항목 클릭 이벤트
|
|
document.addEventListener("DOMContentLoaded", function () {
|
|
document.addEventListener('click', function(e) {
|
|
if (e.target.closest('.schedule-item')) {
|
|
e.preventDefault();
|
|
const scheduleItem = e.target.closest('.schedule-item');
|
|
const scheduleId = scheduleItem.getAttribute("data-id");
|
|
openModal(scheduleId);
|
|
}
|
|
});
|
|
});
|
|
|
|
</script>
|