초기화

This commit is contained in:
2026-07-15 18:37:19 +09:00
commit 94abc5461d
1268 changed files with 380198 additions and 0 deletions
+409
View File
@@ -0,0 +1,409 @@
// ChemiFactory MES - Customer Management Frontend Logic
const API_BASE = "/customers";
let allCustomers = [];
let selectedCustomerId = null;
document.addEventListener("DOMContentLoaded", () => {
initEvents();
fetchCustomers();
});
// APIs Integration
async function fetchCustomers() {
try {
const res = await fetch(`${API_BASE}/`);
if (!res.ok) throw new Error("Failed to fetch customers");
allCustomers = await res.json();
renderCustomerList(allCustomers);
} catch (err) {
console.error(err);
showListError();
}
}
async function showCustomerDetail(id) {
try {
selectedCustomerId = id;
const res = await fetch(`${API_BASE}/${id}`);
if (!res.ok) throw new Error("Failed to fetch customer details");
const data = await res.json();
renderCustomerDetail(data);
} catch (err) {
console.error(err);
alert("고객사 정보를 상세히 불러오지 못했습니다.");
}
}
// Render Lists
function renderCustomerList(list) {
const tbody = document.getElementById("customer-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(cust => {
const tr = document.createElement("tr");
if (selectedCustomerId === cust.id) tr.className = "active-row";
tr.innerHTML = `
<td><strong>${cust.name_kr}</strong></td>
<td>${cust.contact_number || "-"}</td>
<td>${cust.business_registration_number || "-"}</td>
`;
tr.addEventListener("click", () => {
document.querySelectorAll("#customer-list-body tr").forEach(el => el.classList.remove("active-row"));
tr.classList.add("active-row");
showCustomerDetail(cust.id);
});
tbody.appendChild(tr);
});
}
function showListError() {
const tbody = document.getElementById("customer-list-body");
tbody.innerHTML = `<tr><td colspan="3" style="text-align:center; color:var(--accent-red); padding:30px 0;">데이터 통신에 실패했습니다. DB 작동을 확인하십시오.</td></tr>`;
}
function renderCustomerDetail(data) {
// Show details card, hide placeholder
document.getElementById("customer-placeholder-card").style.display = "none";
document.getElementById("customer-detail-card").style.display = "block";
const cust = data.customer;
document.getElementById("detail-name-kr").innerText = cust.name_kr;
document.getElementById("detail-name-en").innerText = cust.name_en || "";
document.getElementById("detail-brn").innerText = cust.business_registration_number || "-";
document.getElementById("detail-contact").innerText = cust.contact_number || "-";
document.getElementById("detail-address").innerText = cust.address || "-";
// Bind Contacts
renderContactsList(data.contacts || []);
// Bind Sales Activities
renderActivitiesList(data.salesActivities || []);
}
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, '&quot;')})">수정</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, '&quot;')})">수정</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-customer").addEventListener("input", (e) => {
const query = e.target.value.toLowerCase();
const filtered = allCustomers.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))
);
renderCustomerList(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 = ["customer-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 Customer
document.getElementById("btn-add-customer").addEventListener("click", () => {
document.getElementById("customer-form").reset();
document.getElementById("customer-id").value = "";
document.getElementById("customer-modal-title").innerText = "고객사 등록";
document.getElementById("customer-modal").style.display = "flex";
});
document.getElementById("customer-form").addEventListener("submit", handleCustomerSubmit);
// Edit & Delete Customer
document.getElementById("btn-edit-customer").addEventListener("click", () => {
const activeCust = allCustomers.find(c => c.id === selectedCustomerId);
if (!activeCust) return;
document.getElementById("customer-id").value = activeCust.id;
document.getElementById("cust-name-kr").value = activeCust.name_kr;
document.getElementById("cust-name-en").value = activeCust.name_en || "";
document.getElementById("cust-brn").value = activeCust.business_registration_number || "";
document.getElementById("cust-contact").value = activeCust.contact_number || "";
document.getElementById("cust-address").value = activeCust.address || "";
document.getElementById("customer-modal-title").innerText = "고객사 정보 수정";
document.getElementById("customer-modal").style.display = "flex";
});
document.getElementById("btn-delete-customer").addEventListener("click", handleDeleteCustomer);
// 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 handleCustomerSubmit(e) {
e.preventDefault();
const id = document.getElementById("customer-id").value;
const payload = {
name_kr: document.getElementById("cust-name-kr").value,
name_en: document.getElementById("cust-name-en").value || null,
business_registration_number: document.getElementById("cust-brn").value || null,
contact_number: document.getElementById("cust-contact").value || null,
address: document.getElementById("cust-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("customer-modal").style.display = "none";
await fetchCustomers();
if (id) {
showCustomerDetail(parseInt(id));
} else {
showCustomerDetail(result.id);
}
} catch (err) {
console.error(err);
alert("고객사 정보를 저장하는 중 오류가 발생했습니다.");
}
}
async function handleDeleteCustomer() {
if (!selectedCustomerId) return;
if (!confirm("이 고객사를 정말 삭제하시겠습니까?\n해당 거래처의 연락처 및 활동 일지 정보가 영구 삭제됩니다.")) return;
try {
const res = await fetch(`${API_BASE}/${selectedCustomerId}`, {
method: "DELETE"
});
if (!res.ok) throw new Error("Failed to delete customer");
selectedCustomerId = null;
document.getElementById("customer-detail-card").style.display = "none";
document.getElementById("customer-placeholder-card").style.display = "flex";
await fetchCustomers();
} 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 = {
customer_id: selectedCustomerId,
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";
showCustomerDetail(selectedCustomerId);
} 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");
showCustomerDetail(selectedCustomerId);
} 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 = {
customer_id: selectedCustomerId,
contact_id: null, // Optional in back-end
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";
showCustomerDetail(selectedCustomerId);
} 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");
showCustomerDetail(selectedCustomerId);
} catch (err) {
console.error(err);
alert("일지 삭제에 실패했습니다.");
}
};