초기화
This commit is contained in:
@@ -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, '"')})">수정</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-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("일지 삭제에 실패했습니다.");
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user