410 lines
16 KiB
JavaScript
410 lines
16 KiB
JavaScript
// ChemiFactory MES - Supplier Management Frontend Logic
|
|
const API_BASE = "/suppliers";
|
|
|
|
let allSuppliers = [];
|
|
let selectedSupplierId = null;
|
|
|
|
document.addEventListener("DOMContentLoaded", () => {
|
|
initEvents();
|
|
fetchSuppliers();
|
|
});
|
|
|
|
// APIs Integration
|
|
async function fetchSuppliers() {
|
|
try {
|
|
const res = await fetch(`${API_BASE}/`);
|
|
if (!res.ok) throw new Error("Failed to fetch suppliers");
|
|
allSuppliers = await res.json();
|
|
renderSupplierList(allSuppliers);
|
|
} catch (err) {
|
|
console.error(err);
|
|
showListError();
|
|
}
|
|
}
|
|
|
|
async function showSupplierDetail(id) {
|
|
try {
|
|
selectedSupplierId = id;
|
|
const res = await fetch(`${API_BASE}/${id}`);
|
|
if (!res.ok) throw new Error("Failed to fetch supplier details");
|
|
const data = await res.json();
|
|
renderSupplierDetail(data);
|
|
} catch (err) {
|
|
console.error(err);
|
|
alert("공급사 정보를 상세히 불러오지 못했습니다.");
|
|
}
|
|
}
|
|
|
|
// Render Lists
|
|
function renderSupplierList(list) {
|
|
const tbody = document.getElementById("supplier-list-body");
|
|
tbody.innerHTML = "";
|
|
|
|
if (list.length === 0) {
|
|
tbody.innerHTML = `<tr><td colspan="3" style="text-align:center; color:var(--text-muted); padding:30px 0;">등록된 공급사가 없습니다.</td></tr>`;
|
|
return;
|
|
}
|
|
|
|
list.forEach(supp => {
|
|
const tr = document.createElement("tr");
|
|
if (selectedSupplierId === supp.id) tr.className = "active-row";
|
|
tr.innerHTML = `
|
|
<td><strong>${supp.name_kr}</strong></td>
|
|
<td>${supp.contact_number || "-"}</td>
|
|
<td>${supp.business_registration_number || "-"}</td>
|
|
`;
|
|
tr.addEventListener("click", () => {
|
|
document.querySelectorAll("#supplier-list-body tr").forEach(el => el.classList.remove("active-row"));
|
|
tr.classList.add("active-row");
|
|
showSupplierDetail(supp.id);
|
|
});
|
|
tbody.appendChild(tr);
|
|
});
|
|
}
|
|
|
|
function showListError() {
|
|
const tbody = document.getElementById("supplier-list-body");
|
|
tbody.innerHTML = `<tr><td colspan="3" style="text-align:center; color:var(--accent-red); padding:30px 0;">데이터 통신에 실패했습니다. DB 작동을 확인하십시오.</td></tr>`;
|
|
}
|
|
|
|
function renderSupplierDetail(data) {
|
|
// Show details card, hide placeholder
|
|
document.getElementById("supplier-placeholder-card").style.display = "none";
|
|
document.getElementById("supplier-detail-card").style.display = "block";
|
|
|
|
const supp = data.supplier;
|
|
document.getElementById("detail-name-kr").innerText = supp.name_kr;
|
|
document.getElementById("detail-name-en").innerText = supp.name_en || "";
|
|
document.getElementById("detail-brn").innerText = supp.business_registration_number || "-";
|
|
document.getElementById("detail-contact").innerText = supp.contact_number || "-";
|
|
document.getElementById("detail-address").innerText = supp.address || "-";
|
|
|
|
// Bind Contacts
|
|
renderContactsList(data.contacts || []);
|
|
|
|
// Bind Purchase Activities
|
|
renderActivitiesList(data.purchaseActivities || []);
|
|
}
|
|
|
|
function renderContactsList(contacts) {
|
|
const tbody = document.getElementById("contact-list-body");
|
|
tbody.innerHTML = "";
|
|
|
|
if (contacts.length === 0) {
|
|
tbody.innerHTML = `<tr><td colspan="5" style="text-align:center; color:var(--text-muted); padding:20px 0;">등록된 담당자가 없습니다.</td></tr>`;
|
|
return;
|
|
}
|
|
|
|
contacts.forEach(c => {
|
|
const tr = document.createElement("tr");
|
|
tr.innerHTML = `
|
|
<td><strong>${c.name}</strong></td>
|
|
<td>${c.position || "-"} ${c.work_location ? `(${c.work_location})` : ""}</td>
|
|
<td>${c.phone_number || "-"}</td>
|
|
<td>${c.email || "-"}</td>
|
|
<td>
|
|
<button class="btn btn-secondary" style="padding: 2px 6px; font-size:11px;" onclick="openEditContactModal(${JSON.stringify(c).replace(/"/g, '"')})">수정</button>
|
|
<button class="btn btn-danger" style="padding: 2px 6px; font-size:11px;" onclick="deleteContact(${c.id})">삭제</button>
|
|
</td>
|
|
`;
|
|
tbody.appendChild(tr);
|
|
});
|
|
}
|
|
|
|
function renderActivitiesList(activities) {
|
|
const tbody = document.getElementById("activity-list-body");
|
|
tbody.innerHTML = "";
|
|
|
|
if (activities.length === 0) {
|
|
tbody.innerHTML = `<tr><td colspan="4" style="text-align:center; color:var(--text-muted); padding:20px 0;">매입 활동 내역이 없습니다.</td></tr>`;
|
|
return;
|
|
}
|
|
|
|
// Sort by date descending
|
|
activities.sort((a, b) => new Date(b.activity_date) - new Date(a.activity_date));
|
|
|
|
activities.forEach(act => {
|
|
const tr = document.createElement("tr");
|
|
tr.innerHTML = `
|
|
<td>${act.activity_date}</td>
|
|
<td><span class="badge badge-online">${act.activity_type}</span></td>
|
|
<td style="max-width: 250px; white-wrap: wrap;">${act.memo}</td>
|
|
<td>
|
|
<button class="btn btn-secondary" style="padding: 2px 6px; font-size:11px;" onclick="openEditActivityModal(${JSON.stringify(act).replace(/"/g, '"')})">수정</button>
|
|
<button class="btn btn-danger" style="padding: 2px 6px; font-size:11px;" onclick="deleteActivity(${act.id})">삭제</button>
|
|
</td>
|
|
`;
|
|
tbody.appendChild(tr);
|
|
});
|
|
}
|
|
|
|
// Event Bindings
|
|
function initEvents() {
|
|
// Search Filter
|
|
document.getElementById("search-supplier").addEventListener("input", (e) => {
|
|
const query = e.target.value.toLowerCase();
|
|
const filtered = allSuppliers.filter(c =>
|
|
c.name_kr.toLowerCase().includes(query) ||
|
|
(c.name_en && c.name_en.toLowerCase().includes(query)) ||
|
|
(c.business_registration_number && c.business_registration_number.includes(query))
|
|
);
|
|
renderSupplierList(filtered);
|
|
});
|
|
|
|
// Tab Toggle
|
|
document.querySelectorAll(".tab-btn").forEach(btn => {
|
|
btn.addEventListener("click", () => {
|
|
document.querySelectorAll(".tab-btn").forEach(b => b.classList.remove("active"));
|
|
document.querySelectorAll(".tab-content").forEach(tc => tc.classList.remove("active"));
|
|
|
|
btn.classList.add("active");
|
|
document.getElementById(btn.dataset.tab).classList.add("active");
|
|
});
|
|
});
|
|
|
|
// Modals Control
|
|
const modals = ["supplier-modal", "contact-modal", "activity-modal"];
|
|
modals.forEach(mId => {
|
|
const modal = document.getElementById(mId);
|
|
modal.querySelectorAll(".close-btn, .close-modal-btn").forEach(btn => {
|
|
btn.addEventListener("click", () => modal.style.display = "none");
|
|
});
|
|
});
|
|
|
|
// Add Supplier
|
|
document.getElementById("btn-add-supplier").addEventListener("click", () => {
|
|
document.getElementById("supplier-form").reset();
|
|
document.getElementById("supplier-id").value = "";
|
|
document.getElementById("supplier-modal-title").innerText = "공급사 등록";
|
|
document.getElementById("supplier-modal").style.display = "flex";
|
|
});
|
|
|
|
document.getElementById("supplier-form").addEventListener("submit", handleSupplierSubmit);
|
|
|
|
// Edit & Delete Supplier
|
|
document.getElementById("btn-edit-supplier").addEventListener("click", () => {
|
|
const activeSupp = allSuppliers.find(c => c.id === selectedSupplierId);
|
|
if (!activeSupp) return;
|
|
|
|
document.getElementById("supplier-id").value = activeSupp.id;
|
|
document.getElementById("supp-name-kr").value = activeSupp.name_kr;
|
|
document.getElementById("supp-name-en").value = activeSupp.name_en || "";
|
|
document.getElementById("supp-brn").value = activeSupp.business_registration_number || "";
|
|
document.getElementById("supp-contact").value = activeSupp.contact_number || "";
|
|
document.getElementById("supp-address").value = activeSupp.address || "";
|
|
|
|
document.getElementById("supplier-modal-title").innerText = "공급사 정보 수정";
|
|
document.getElementById("supplier-modal").style.display = "flex";
|
|
});
|
|
|
|
document.getElementById("btn-delete-supplier").addEventListener("click", handleDeleteSupplier);
|
|
|
|
// Add Contact
|
|
document.getElementById("btn-add-contact").addEventListener("click", () => {
|
|
document.getElementById("contact-form").reset();
|
|
document.getElementById("contact-id").value = "";
|
|
document.getElementById("contact-modal-title").innerText = "담당자 등록";
|
|
document.getElementById("contact-modal").style.display = "flex";
|
|
});
|
|
document.getElementById("contact-form").addEventListener("submit", handleContactSubmit);
|
|
|
|
// Add Activity
|
|
document.getElementById("btn-add-activity").addEventListener("click", () => {
|
|
document.getElementById("activity-form").reset();
|
|
document.getElementById("activity-id").value = "";
|
|
document.getElementById("act-date").value = new Date().toISOString().substring(0, 10);
|
|
document.getElementById("activity-modal-title").innerText = "매입 일지 등록";
|
|
document.getElementById("activity-modal").style.display = "flex";
|
|
});
|
|
document.getElementById("activity-form").addEventListener("submit", handleActivitySubmit);
|
|
}
|
|
|
|
// Form Handlers
|
|
async function handleSupplierSubmit(e) {
|
|
e.preventDefault();
|
|
const id = document.getElementById("supplier-id").value;
|
|
const payload = {
|
|
name_kr: document.getElementById("supp-name-kr").value,
|
|
name_en: document.getElementById("supp-name-en").value || null,
|
|
business_registration_number: document.getElementById("supp-brn").value || null,
|
|
contact_number: document.getElementById("supp-contact").value || null,
|
|
address: document.getElementById("supp-address").value || null
|
|
};
|
|
|
|
try {
|
|
let res;
|
|
if (id) {
|
|
// Update
|
|
res = await fetch(`${API_BASE}/${id}`, {
|
|
method: "PUT",
|
|
headers: { "Content-Type": "application/json" },
|
|
body: JSON.stringify(payload)
|
|
});
|
|
} else {
|
|
// Create
|
|
res = await fetch(`${API_BASE}/`, {
|
|
method: "POST",
|
|
headers: { "Content-Type": "application/json" },
|
|
body: JSON.stringify(payload)
|
|
});
|
|
}
|
|
|
|
if (!res.ok) throw new Error("API error occurred");
|
|
const result = await res.json();
|
|
|
|
document.getElementById("supplier-modal").style.display = "none";
|
|
await fetchSuppliers();
|
|
|
|
if (id) {
|
|
showSupplierDetail(parseInt(id));
|
|
} else {
|
|
showSupplierDetail(result.id);
|
|
}
|
|
} catch (err) {
|
|
console.error(err);
|
|
alert("공급사 정보를 저장하는 중 오류가 발생했습니다.");
|
|
}
|
|
}
|
|
|
|
async function handleDeleteSupplier() {
|
|
if (!selectedSupplierId) return;
|
|
if (!confirm("이 공급사를 정말 삭제하시겠습니까?\n해당 공급사의 연락처 및 매입 일지 정보가 영구 삭제됩니다.")) return;
|
|
|
|
try {
|
|
const res = await fetch(`${API_BASE}/${selectedSupplierId}`, {
|
|
method: "DELETE"
|
|
});
|
|
if (!res.ok) throw new Error("Failed to delete supplier");
|
|
|
|
selectedSupplierId = null;
|
|
document.getElementById("supplier-detail-card").style.display = "none";
|
|
document.getElementById("supplier-placeholder-card").style.display = "flex";
|
|
|
|
await fetchSuppliers();
|
|
} catch (err) {
|
|
console.error(err);
|
|
alert("공급사 삭제에 실패했습니다.");
|
|
}
|
|
}
|
|
|
|
// Contact Handlers
|
|
window.openEditContactModal = function(contact) {
|
|
document.getElementById("contact-id").value = contact.id;
|
|
document.getElementById("cont-name").value = contact.name;
|
|
document.getElementById("cont-position").value = contact.position || "";
|
|
document.getElementById("cont-phone").value = contact.phone_number || "";
|
|
document.getElementById("cont-email").value = contact.email || "";
|
|
document.getElementById("cont-location").value = contact.work_location || "";
|
|
|
|
document.getElementById("contact-modal-title").innerText = "담당자 정보 수정";
|
|
document.getElementById("contact-modal").style.display = "flex";
|
|
};
|
|
|
|
async function handleContactSubmit(e) {
|
|
e.preventDefault();
|
|
const id = document.getElementById("contact-id").value;
|
|
const payload = {
|
|
supplier_id: selectedSupplierId,
|
|
name: document.getElementById("cont-name").value,
|
|
position: document.getElementById("cont-position").value || null,
|
|
phone_number: document.getElementById("cont-phone").value || null,
|
|
email: document.getElementById("cont-email").value || null,
|
|
work_location: document.getElementById("cont-location").value || null
|
|
};
|
|
|
|
try {
|
|
let res;
|
|
if (id) {
|
|
res = await fetch(`${API_BASE}/contacts/${id}`, {
|
|
method: "PUT",
|
|
headers: { "Content-Type": "application/json" },
|
|
body: JSON.stringify(payload)
|
|
});
|
|
} else {
|
|
res = await fetch(`${API_BASE}/contacts`, {
|
|
method: "POST",
|
|
headers: { "Content-Type": "application/json" },
|
|
body: JSON.stringify(payload)
|
|
});
|
|
}
|
|
|
|
if (!res.ok) throw new Error("Contact save API error");
|
|
document.getElementById("contact-modal").style.display = "none";
|
|
showSupplierDetail(selectedSupplierId);
|
|
} catch (err) {
|
|
console.error(err);
|
|
alert("담당자 저장에 실패했습니다.");
|
|
}
|
|
}
|
|
|
|
window.deleteContact = async function(contactId) {
|
|
if (!confirm("이 담당자 정보를 정말 삭제하시겠습니까?")) return;
|
|
try {
|
|
const res = await fetch(`${API_BASE}/contacts/${contactId}`, { method: "DELETE" });
|
|
if (!res.ok) throw new Error("Failed to delete contact");
|
|
showSupplierDetail(selectedSupplierId);
|
|
} catch (err) {
|
|
console.error(err);
|
|
alert("담당자 삭제에 실패했습니다.");
|
|
}
|
|
};
|
|
|
|
// Activity Handlers
|
|
window.openEditActivityModal = function(act) {
|
|
document.getElementById("activity-id").value = act.id;
|
|
document.getElementById("act-date").value = act.activity_date;
|
|
document.getElementById("act-type").value = act.activity_type;
|
|
document.getElementById("act-memo").value = act.memo || "";
|
|
|
|
document.getElementById("activity-modal-title").innerText = "매입 일지 수정";
|
|
document.getElementById("activity-modal").style.display = "flex";
|
|
};
|
|
|
|
async function handleActivitySubmit(e) {
|
|
e.preventDefault();
|
|
const id = document.getElementById("activity-id").value;
|
|
const payload = {
|
|
supplier_id: selectedSupplierId,
|
|
contact_id: null,
|
|
activity_date: document.getElementById("act-date").value,
|
|
activity_type: document.getElementById("act-type").value,
|
|
memo: document.getElementById("act-memo").value
|
|
};
|
|
|
|
try {
|
|
let res;
|
|
if (id) {
|
|
res = await fetch(`${API_BASE}/activities/${id}`, {
|
|
method: "PUT",
|
|
headers: { "Content-Type": "application/json" },
|
|
body: JSON.stringify(payload)
|
|
});
|
|
} else {
|
|
res = await fetch(`${API_BASE}/activities`, {
|
|
method: "POST",
|
|
headers: { "Content-Type": "application/json" },
|
|
body: JSON.stringify(payload)
|
|
});
|
|
}
|
|
|
|
if (!res.ok) throw new Error("Activity save API error");
|
|
document.getElementById("activity-modal").style.display = "none";
|
|
showSupplierDetail(selectedSupplierId);
|
|
} catch (err) {
|
|
console.error(err);
|
|
alert("일지 저장에 실패했습니다.");
|
|
}
|
|
}
|
|
|
|
window.deleteActivity = async function(actId) {
|
|
if (!confirm("이 매입 일지를 삭제하시겠습니까?")) return;
|
|
try {
|
|
const res = await fetch(`${API_BASE}/activities/${actId}`, { method: "DELETE" });
|
|
if (!res.ok) throw new Error("Failed to delete activity");
|
|
showSupplierDetail(selectedSupplierId);
|
|
} catch (err) {
|
|
console.error(err);
|
|
alert("일지 삭제에 실패했습니다.");
|
|
}
|
|
};
|