최초 커밋
This commit is contained in:
@@ -0,0 +1,350 @@
|
||||
<?php
|
||||
include __DIR__ . '/layout_sales.php';
|
||||
sales_layout_start("영업 스케줄표");
|
||||
?>
|
||||
|
||||
<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 type="module">
|
||||
import { w2grid,w2popup } from "https://cdn.jsdeliver.net/npm/w2ui@2.0.0/dist/w2ui.es6.min.js";
|
||||
|
||||
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(`/egbim/bbs/sales_schedules.php?action=month&year=${year}&month=${month}`);
|
||||
let json = await res.json();
|
||||
|
||||
if (json.status !== "ok") return;
|
||||
|
||||
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; // ← 헤더 클릭 오류 방지
|
||||
|
||||
let field = col.field;
|
||||
if (!field.startsWith("day_")) return;
|
||||
|
||||
let dayNum = field.replace("day_", "");
|
||||
let fullDate = week.find(w => w.day == dayNum).full;
|
||||
|
||||
openSchedulePopup(rec.emp_no, rec.emp_name, fullDate);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/* =========================================================
|
||||
일정 리스트 팝업
|
||||
========================================================= */
|
||||
function openSchedulePopup(emp_no, emp_name, date) {
|
||||
|
||||
let items = allSchedules.filter(s => s.emp_no == emp_no && s.schedule_date == date);
|
||||
|
||||
let html = `
|
||||
<div class="p-3">
|
||||
<div class="mb-2 font-bold">${date} - ${emp_name}</div>
|
||||
|
||||
<button class="bg-blue-600 text-white px-3 py-1 rounded mb-3"
|
||||
onclick="window.addSchedule('${emp_no}','${date}')">+ 신규 일정 추가</button>
|
||||
|
||||
${items.map(it => `
|
||||
<div class="border rounded p-2 mb-2">
|
||||
<div><b>${it.content}</b></div>
|
||||
<div class="text-xs text-gray-600">${it.client_code}</div>
|
||||
<div class="text-xs mt-1">상태: ${it.status}</div>
|
||||
|
||||
<button onclick="window.editSchedule(${it.seq_no})"
|
||||
class="bg-yellow-500 text-white px-2 py-1 rounded mt-2">수정</button>
|
||||
|
||||
<button onclick="window.deleteSchedule(${it.seq_no})"
|
||||
class="bg-red-600 text-white px-2 py-1 rounded mt-2 ml-2">삭제</button>
|
||||
</div>
|
||||
`).join("")}
|
||||
</div>
|
||||
`;
|
||||
|
||||
w2popup.open({
|
||||
title: `${date} - 일정`,
|
||||
body: html,
|
||||
width: 420,
|
||||
height: 500
|
||||
});
|
||||
}
|
||||
|
||||
/* =========================================================
|
||||
신규 일정 추가
|
||||
========================================================= */
|
||||
window.addSchedule = function(emp_no, date) {
|
||||
|
||||
let html = `
|
||||
<div class="p-3">
|
||||
<label>내용</label>
|
||||
<input id="s-content" class="border p-2 w-full mb-2">
|
||||
|
||||
<label>거래처 코드</label>
|
||||
<input id="s-client" class="border p-2 w-full mb-2">
|
||||
|
||||
<label>상태</label>
|
||||
<select id="s-status" class="border p-2 w-full mb-3">
|
||||
<option value="예정">예정</option>
|
||||
<option value="완료">완료</option>
|
||||
</select>
|
||||
|
||||
<button class="bg-blue-600 text-white px-3 py-2 rounded w-full"
|
||||
onclick="window.saveNew('${emp_no}','${date}')">등록</button>
|
||||
</div>
|
||||
`;
|
||||
|
||||
w2popup.open({
|
||||
title: "신규 일정 추가",
|
||||
body: html,
|
||||
width: 400,
|
||||
height: 350
|
||||
});
|
||||
};
|
||||
|
||||
window.saveNew = async function(emp_no, date) {
|
||||
|
||||
let res = await fetch("/egbim/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: document.getElementById("s-status").value
|
||||
})
|
||||
});
|
||||
|
||||
let json = await res.json();
|
||||
if (json.status === "ok") {
|
||||
w2popup.close();
|
||||
loadMonth();
|
||||
}
|
||||
};
|
||||
|
||||
/* =========================================================
|
||||
일정 수정
|
||||
========================================================= */
|
||||
window.editSchedule = async function(seq_no) {
|
||||
|
||||
let item = allSchedules.find(s => s.seq_no == seq_no);
|
||||
|
||||
let html = `
|
||||
<div class="p-3">
|
||||
<label>내용</label>
|
||||
<input id="e-content" class="border p-2 w-full mb-2" value="${item.content}">
|
||||
|
||||
<label>거래처 코드</label>
|
||||
<input id="e-client" class="border p-2 w-full mb-2" value="${item.client_code}">
|
||||
|
||||
<label>상태</label>
|
||||
<select id="e-status" class="border p-2 w-full mb-3">
|
||||
<option value="예정" ${item.status=="예정"?"selected":""}>예정</option>
|
||||
<option value="완료" ${item.status=="완료"?"selected":""}>완료</option>
|
||||
</select>
|
||||
|
||||
<button class="bg-blue-600 text-white px-3 py-2 rounded w-full"
|
||||
onclick="window.saveEdit(${seq_no})">저장</button>
|
||||
</div>
|
||||
`;
|
||||
|
||||
w2popup.open({
|
||||
title: "일정 수정",
|
||||
body: html,
|
||||
width: 400,
|
||||
height: 350
|
||||
});
|
||||
};
|
||||
|
||||
window.saveEdit = async function(seq_no) {
|
||||
|
||||
let res = await fetch("/egbim/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: document.getElementById("e-status").value
|
||||
})
|
||||
});
|
||||
|
||||
let json = await res.json();
|
||||
if (json.status === "ok") {
|
||||
w2popup.close();
|
||||
loadMonth();
|
||||
}
|
||||
};
|
||||
|
||||
/* =========================================================
|
||||
일정 삭제
|
||||
========================================================= */
|
||||
window.deleteSchedule = async function(seq_no) {
|
||||
if (!confirm("삭제하시겠습니까?")) return;
|
||||
|
||||
let res = await fetch("/egbim/bbs/sales_schedules.php", {
|
||||
method: "POST",
|
||||
headers: {"Content-Type":"application/x-www-form-urlencoded"},
|
||||
body: new URLSearchParams({
|
||||
action: "delete",
|
||||
seq_no
|
||||
})
|
||||
});
|
||||
|
||||
let json = await res.json();
|
||||
if (json.status === "ok") {
|
||||
w2popup.close();
|
||||
loadMonth();
|
||||
}
|
||||
};
|
||||
|
||||
/* 초기 로드 */
|
||||
document.getElementById("btn-load").onclick = loadMonth;
|
||||
loadMonth();
|
||||
|
||||
</script>
|
||||
|
||||
<?php sales_layout_end(); ?>
|
||||
Reference in New Issue
Block a user