// 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 = `
| 등록된 공급사가 없습니다. |
`;
return;
}
list.forEach(supp => {
const tr = document.createElement("tr");
if (selectedSupplierId === supp.id) tr.className = "active-row";
tr.innerHTML = `
${supp.name_kr} |
${supp.contact_number || "-"} |
${supp.business_registration_number || "-"} |
`;
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 = `| 데이터 통신에 실패했습니다. DB 작동을 확인하십시오. |
`;
}
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 = `| 등록된 담당자가 없습니다. |
`;
return;
}
contacts.forEach(c => {
const tr = document.createElement("tr");
tr.innerHTML = `
${c.name} |
${c.position || "-"} ${c.work_location ? `(${c.work_location})` : ""} |
${c.phone_number || "-"} |
${c.email || "-"} |
|
`;
tbody.appendChild(tr);
});
}
function renderActivitiesList(activities) {
const tbody = document.getElementById("activity-list-body");
tbody.innerHTML = "";
if (activities.length === 0) {
tbody.innerHTML = `| 매입 활동 내역이 없습니다. |
`;
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 = `
${act.activity_date} |
${act.activity_type} |
${act.memo} |
|
`;
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("일지 삭제에 실패했습니다.");
}
};