Files
egbim_homepage/egbim/bbs/sales_schedules.skin.php
2026-07-23 16:15:57 +09:00

346 lines
11 KiB
PHP

<?php
include __DIR__ . '/layout_sales.php';
sales_layout_start("영업 스케줄표");
?>
<!-- w2ui CSS / JS -->
<link rel="stylesheet" href="https://rawcdn.githack.com/vitmalina/w2ui/master/dist/w2ui.min.css">
<script src="https://rawcdn.githack.com/vitmalina/w2ui/master/dist/w2ui.min.js"></script>
<h2 class="text-2xl font-bold mb-3">영업 스케줄표</h2>
<!-- 월 선택 -->
<div class="flex gap-2 mb-4">
<select id="sel-year" class="border p-1">
<?php
for ($y=date("Y")-1; $y<=date("Y")+2; $y++) {
echo "<option value='$y' ".($y==date("Y")?"selected":"").">$y 년</option>";
}
?>
</select>
<select id="sel-month" class="border p-1">
<?php
for ($m=1;$m<=12;$m++) {
$mm = sprintf("%02d",$m);
echo "<option value='$mm' ".($mm==date("m")?"selected":"").">$mm 월</option>";
}
?>
</select>
<button id="btn-load" class="bg-blue-600 text-white px-3 py-1 rounded">불러오기</button>
</div>
<div id="month-container"></div>
<script>
let allSchedules = [];
let employees = [];
let weekGroups = [];
/* =========================================================
평일(월~금) 기준 주차 생성
========================================================= */
function generateWeeks(year, month) {
let lastDay = new Date(year, month, 0).getDate();
let businessDays = [];
for (let d=1; d<=lastDay; d++) {
let date = new Date(`${year}-${month}-${String(d).padStart(2,'0')}`);
let dow = date.getDay();
if (dow >= 1 && dow <= 5) {
businessDays.push({
day: d,
dow: ["","월","화","수","목","금"][dow],
full: `${year}-${month}-${String(d).padStart(2,'0')}`
});
}
}
let weeks = [];
for (let i=0; i<businessDays.length; i+=5) {
weeks.push(businessDays.slice(i, i+5));
}
return weeks;
}
/* =========================================================
월 전체 로드
========================================================= */
async function loadMonth() {
let year = document.getElementById("sel-year").value;
let month = document.getElementById("sel-month").value;
let res = await fetch(`/egbim1/bbs/sales_schedules.php?action=month&year=${year}&month=${month}`);
let json = await res.json();
employees = json.employees;
allSchedules = json.schedules;
weekGroups = generateWeeks(year, month);
renderMonth();
}
/* =========================================================
월 → 주차 렌더링
========================================================= */
function renderMonth() {
let container = document.getElementById("month-container");
container.innerHTML = "";
weekGroups.forEach((week, idx) => {
container.insertAdjacentHTML("beforeend", `
<div class="font-bold text-lg mt-6 mb-2">
${idx+1}주차 : ${week[0].day}일 ~ ${week.at(-1).day}일
</div>
<div id="week-grid-${idx}" style="width:100%;height:360px;"></div>
`);
renderWeekGrid(week, idx);
});
}
/* =========================================================
주차 Grid
========================================================= */
function renderWeekGrid(week, idx) {
let columns = [
{ field: "emp_name", text: "담당자", size: "120px", frozen: true }
];
week.forEach(d => {
columns.push({
field: "day_" + d.day,
text: `${d.day}일(${d.dow})`,
size: "260px",
render: rec => rec["day_" + d.day] || ""
});
});
let records = employees.map(emp => {
let row = {
recid: emp.emp_no + "_" + idx,
emp_name: emp.emp_name,
emp_no: emp.emp_no
};
week.forEach(d => {
let items = allSchedules.filter(s =>
s.emp_no === emp.emp_no && s.schedule_date === d.full
);
row["day_" + d.day] =
items
.map(it => `
<div style="margin-bottom:4px;">
• ${it.content}
<div style="font-size:11px;color:#777;">${it.client_code}</div>
<div style="font-size:11px;color:#0a0;">${it.status}</div>
</div>`)
.join("");
});
return row;
});
new w2grid({
name: `grid_week_${idx}`,
box: `#week-grid-${idx}`,
show: { toolbar: false, footer: false },
columns,
records,
/* 셀 클릭 → 일정 팝업 */
onClick(event) {
let rec = this.get(event.recid);
if (!rec) return;
let col = this.columns[event.column];
if (!col || !col.field) return;
if (!col.field.startsWith("day_")) return;
let dayNum = col.field.replace("day_", "");
let fullDate = week.find(w => w.day == dayNum).full;
openSchedulePopup(rec.emp_no, rec.emp_name, fullDate);
}
});
}
/* =========================================================
SweetAlert2 일정 리스트 팝업
========================================================= */
function openSchedulePopup(emp_no, emp_name, date) {
let items = allSchedules.filter(s => s.emp_no == emp_no && s.schedule_date == date);
let html = `
<button onclick="addSchedule('${emp_no}','${date}')"
class="swal2-confirm swal2-styled"
style="margin-bottom:12px;">+ 신규 일정 추가</button>
<hr style="margin:10px 0;">
${
items.length
? items.map(it => `
<div style="padding:10px; border:1px solid #ddd; border-radius:6px; margin-bottom:10px; text-align:left;">
<b>${it.content}</b>
<div style="font-size:12px;color:#777">${it.client_code}</div>
<div style="font-size:12px;margin-top:4px;">상태: ${it.status}</div>
<button onclick="editSchedule(${it.seq_no})"
class="swal2-confirm swal2-styled" style="background:#facc15; margin-top:6px;">수정</button>
<button onclick="deleteSchedule(${it.seq_no})"
class="swal2-cancel swal2-styled" style="background:#ef4444; margin-top:6px; margin-left:6px;">삭제</button>
</div>
`).join("")
: "<div style='color:#888;'>등록된 일정이 없습니다.</div>"
}
`;
Swal.fire({
title: `${date}<br>${emp_name}`,
html: html,
width: 450,
showConfirmButton: false
});
}
/* =========================================================
신규 일정 추가 (SweetAlert2)
========================================================= */
window.addSchedule = function(emp_no, date) {
Swal.fire({
title: "신규 일정 추가",
html: `
<label>상태</label><br>
<input type="radio" name="s_status" value="예정" checked> 예정
<input type="radio" name="s_status" value="완료"> 완료
<br><br>
<label>거래처 코드</label>
<input id="s-client" class="swal2-input">
<label>내용</label>
<textarea id="s-content" class="swal2-textarea"></textarea>
`,
showCancelButton: true,
confirmButtonText: '등록'
}).then(async r => {
if (!r.isConfirmed) return;
let status = document.querySelector('input[name="s_status"]:checked').value;
let res = await fetch("/egbim1/bbs/sales_schedules.php", {
method: "POST",
headers: {"Content-Type":"application/x-www-form-urlencoded"},
body: new URLSearchParams({
action: "insert",
emp_no,
schedule_date: date,
client_code: document.getElementById("s-client").value,
content: document.getElementById("s-content").value,
status
})
});
let j = await res.json();
if (j.status === "ok") {
Swal.close();
loadMonth();
}
});
};
/* =========================================================
일정 수정
========================================================= */
window.editSchedule = function(seq_no) {
let item = allSchedules.find(s => s.seq_no == seq_no);
Swal.fire({
title: "일정 수정",
html: `
<label>상태</label><br>
<input type="radio" name="e_status" value="예정" ${item.status=="예정"?"checked":""}> 예정
<input type="radio" name="e_status" value="완료" ${item.status=="완료"?"checked":""}> 완료
<br><br>
<label>거래처 코드</label>
<input id="e-client" class="swal2-input" value="${item.client_code}">
<label>내용</label>
<textarea id="e-content" class="swal2-textarea">${item.content}</textarea>
`,
showCancelButton: true,
confirmButtonText: '저장'
}).then(async r => {
if (!r.isConfirmed) return;
let status = document.querySelector('input[name="e_status"]:checked').value;
let res = await fetch("/egbim1/bbs/sales_schedules.php", {
method:"POST",
headers:{"Content-Type":"application/x-www-form-urlencoded"},
body:new URLSearchParams({
action: "update",
seq_no,
content: document.getElementById("e-content").value,
client_code: document.getElementById("e-client").value,
status
})
});
let j = await res.json();
if (j.status === "ok") {
Swal.close();
loadMonth();
}
});
};
/* =========================================================
일정 삭제
========================================================= */
window.deleteSchedule = function(seq_no) {
Swal.fire({
title: "삭제하시겠습니까?",
icon: "warning",
showCancelButton: true,
confirmButtonText: "삭제"
}).then(async r => {
if (!r.isConfirmed) return;
let res = await fetch("/egbim1/bbs/sales_schedules.php", {
method:"POST",
headers:{"Content-Type":"application/x-www-form-urlencoded"},
body:new URLSearchParams({
action:"delete",
seq_no
})
});
let j = await res.json();
if (j.status === "ok") {
Swal.close();
loadMonth();
}
});
};
/* 초기 로드 */
document.getElementById("btn-load").onclick = loadMonth;
loadMonth();
</script>
<?php sales_layout_end(); ?>