feat(admin): implement unified submission and newsletter management

Introduce a centralized system to manage all website form submissions and newsletter subscriptions.
- Add `Submission` and `NewsletterSubscription` models with MongoDB schema validation
- Implement `submissionController` and `newsletterSubscriptionController` for CRUD operations and filtering
- Create a unified admin UI for reviewing submissions across different sources (home, request, contact, partnership, newsletter)
- Add database migration scripts for creating collections and indexes
- Refactor partnership inquiry forms to use a fixed field structure
- Update admin navigation and server CORS settings to support PATCH requests
This commit is contained in:
Tống Thành Đạt
2026-04-24 17:11:23 +07:00
parent caf38e8750
commit f6f2e0e03b
18 changed files with 1534 additions and 54 deletions
@@ -97,12 +97,6 @@
renderPartners();
});
document.getElementById("addInquiryFieldBtn")?.addEventListener("click", function () {
state.inquiryForm.fields.push(getDefaultInquiryField());
ensurePartnershipIds();
renderInquiryFields();
});
document.getElementById("resetPartnershipsForm")?.addEventListener("click", function () {
window.location.reload();
});
@@ -378,7 +372,7 @@
input.value = field[key] || "";
input.addEventListener("input", function () {
field[key] = input.value;
if (key === "label") {
if (key === "label") {
title.textContent = input.value || `${inquiryFieldFields.label?.label || "Field"} ${index + 1}`;
}
if (key === "type") {
@@ -401,11 +395,6 @@
}
});
node.querySelector("[data-remove-item]").addEventListener("click", function () {
state.inquiryForm.fields.splice(index, 1);
renderInquiryFields();
});
node.querySelector("[data-add-option]").addEventListener("click", function () {
field.options = Array.isArray(field.options) ? field.options : [];
field.options.push("");
@@ -444,7 +433,6 @@
inquiryFieldsList.appendChild(node);
});
initSortable(inquiryFieldsList, state.inquiryForm.fields, renderInquiryFields, '[data-item="inquiry-field"]');
}
function initSortable(container, list, rerender, draggableSelector) {