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
@@ -0,0 +1,62 @@
<div class="modal fade" id="submissionDetailModal" tabindex="-1" aria-hidden="true">
<div class="modal-dialog modal-lg modal-dialog-scrollable" style="max-height: calc(100vh - 3.5rem);">
<div class="modal-content" style="max-height: calc(100vh - 3.5rem);">
<div class="modal-header">
<div>
<h5 class="modal-title mb-0">Submission Detail</h5>
<small class="text-muted" id="submissionDetailMeta"></small>
</div>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body" style="overflow-y: auto;">
<input type="hidden" id="submissionDetailId">
<div class="row g-3 mb-4">
<div class="col-md-6" data-submission-name-field>
<label class="form-label small text-muted" id="submissionDetailNameLabel">Name</label>
<div class="fw-semibold" id="submissionDetailName">-</div>
</div>
<%# Hidden by Partnerships submissions because that inquiry form has no email field. %>
<div class="col-md-6" data-submission-email-field>
<label class="form-label small text-muted" id="submissionDetailEmailLabel">Email</label>
<div><a id="submissionDetailEmail" href="#">-</a></div>
</div>
<%# Hidden by Partnerships submissions because that inquiry form has no phone field. %>
<div class="col-md-6" data-submission-phone-field>
<label class="form-label small text-muted" id="submissionDetailPhoneLabel">Phone</label>
<div id="submissionDetailPhone">-</div>
</div>
<div class="col-md-6">
<label class="form-label small text-muted" id="submissionDetailPageLabel">Page</label>
<div id="submissionDetailPage">-</div>
</div>
</div>
<div class="mb-4">
<label class="form-label small text-muted">Submitted Values</label>
<div class="border rounded p-3 bg-light" id="submissionPayload"></div>
</div>
<div class="row g-3">
<div class="col-md-5">
<label class="form-label">Status</label>
<select class="form-select" id="submissionDetailStatus">
<% statuses.forEach((status) => { %>
<option value="<%= status %>"><%= status.replace("_", " ") %></option>
<% }) %>
</select>
</div>
<div class="col-12">
<label class="form-label">Internal note</label>
<textarea class="form-control" id="submissionDetailNote" rows="4" maxlength="2000" placeholder="Add internal processing notes"></textarea>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
<button type="button" class="btn btn-primary" id="submissionSaveDetail">
<i class="fas fa-save me-1"></i>Save
</button>
</div>
</div>
</div>
</div>