438 lines
12 KiB
HTML
438 lines
12 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8" />
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
<title>Contact Submissions</title>
|
|
<style>
|
|
body {
|
|
font-family: Arial, sans-serif;
|
|
margin: 20px;
|
|
max-width: 1200px;
|
|
margin: 0 auto;
|
|
padding: 20px;
|
|
}
|
|
h1 {
|
|
color: #333;
|
|
margin-bottom: 20px;
|
|
}
|
|
.nav {
|
|
margin-bottom: 20px;
|
|
}
|
|
.nav a {
|
|
color: #007bff;
|
|
text-decoration: none;
|
|
margin-right: 20px;
|
|
}
|
|
.nav a:hover {
|
|
text-decoration: underline;
|
|
}
|
|
.filters {
|
|
background: #f8f9fa;
|
|
padding: 15px;
|
|
border-radius: 5px;
|
|
margin-bottom: 20px;
|
|
}
|
|
.filters form {
|
|
display: flex;
|
|
gap: 10px;
|
|
flex-wrap: wrap;
|
|
align-items: end;
|
|
}
|
|
.filters label {
|
|
display: block;
|
|
margin-bottom: 5px;
|
|
font-weight: bold;
|
|
}
|
|
.filters input,
|
|
.filters select {
|
|
padding: 8px;
|
|
border: 1px solid #ccc;
|
|
border-radius: 4px;
|
|
}
|
|
.filters button {
|
|
padding: 8px 15px;
|
|
background: #007bff;
|
|
color: white;
|
|
border: none;
|
|
border-radius: 4px;
|
|
cursor: pointer;
|
|
}
|
|
.filters button:hover {
|
|
background: #0056b3;
|
|
}
|
|
.filters .clear-btn {
|
|
background: #6c757d;
|
|
}
|
|
.filters .clear-btn:hover {
|
|
background: #545b62;
|
|
}
|
|
table {
|
|
width: 100%;
|
|
border-collapse: collapse;
|
|
margin-top: 20px;
|
|
}
|
|
th,
|
|
td {
|
|
padding: 12px;
|
|
text-align: left;
|
|
border-bottom: 1px solid #ddd;
|
|
}
|
|
th {
|
|
background-color: #f8f9fa;
|
|
font-weight: bold;
|
|
cursor: pointer;
|
|
}
|
|
th:hover {
|
|
background-color: #e9ecef;
|
|
}
|
|
th.sort-asc::after {
|
|
content: " ↑";
|
|
}
|
|
th.sort-desc::after {
|
|
content: " ↓";
|
|
}
|
|
tr:hover {
|
|
background-color: #f5f5f5;
|
|
}
|
|
.message {
|
|
padding: 8px;
|
|
margin: 10px 0;
|
|
border-radius: 4px;
|
|
}
|
|
.message.success {
|
|
background-color: #d4edda;
|
|
color: #155724;
|
|
border: 1px solid #c3e6cb;
|
|
}
|
|
.message.error {
|
|
background-color: #f8d7da;
|
|
color: #721c24;
|
|
border: 1px solid #f5c6cb;
|
|
}
|
|
.pagination {
|
|
margin-top: 20px;
|
|
text-align: center;
|
|
}
|
|
.pagination button {
|
|
padding: 8px 12px;
|
|
margin: 0 2px;
|
|
border: 1px solid #ddd;
|
|
background: white;
|
|
cursor: pointer;
|
|
}
|
|
.pagination button:hover {
|
|
background: #f8f9fa;
|
|
}
|
|
.pagination button.active {
|
|
background: #007bff;
|
|
color: white;
|
|
border-color: #007bff;
|
|
}
|
|
.pagination button:disabled {
|
|
opacity: 0.5;
|
|
cursor: not-allowed;
|
|
}
|
|
.delete-btn {
|
|
background: #dc3545;
|
|
color: white;
|
|
border: none;
|
|
padding: 4px 8px;
|
|
border-radius: 3px;
|
|
cursor: pointer;
|
|
}
|
|
.delete-btn:hover {
|
|
background: #c82333;
|
|
}
|
|
.loading {
|
|
text-align: center;
|
|
padding: 20px;
|
|
}
|
|
.no-data {
|
|
text-align: center;
|
|
padding: 40px;
|
|
color: #666;
|
|
}
|
|
.submission-details {
|
|
max-width: 300px;
|
|
word-wrap: break-word;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="nav">
|
|
<a href="/admin/">Dashboard</a>
|
|
<a href="/admin/settings">Settings</a>
|
|
<a href="{{ url_for('auth.logout') }}">Logout</a>
|
|
</div>
|
|
|
|
<h1>Contact Form Submissions</h1>
|
|
|
|
<div id="message"></div>
|
|
|
|
<div class="filters">
|
|
<form id="filterForm">
|
|
<div>
|
|
<label for="email">Email Filter:</label>
|
|
<input
|
|
type="text"
|
|
id="email"
|
|
name="email"
|
|
placeholder="Filter by email"
|
|
/>
|
|
</div>
|
|
<div>
|
|
<label for="date_from">Date From:</label>
|
|
<input type="date" id="date_from" name="date_from" />
|
|
</div>
|
|
<div>
|
|
<label for="date_to">Date To:</label>
|
|
<input type="date" id="date_to" name="date_to" />
|
|
</div>
|
|
<div>
|
|
<label for="per_page">Items per page:</label>
|
|
<select id="per_page" name="per_page">
|
|
<option value="25">25</option>
|
|
<option value="50" selected>50</option>
|
|
<option value="100">100</option>
|
|
</select>
|
|
</div>
|
|
<button type="submit">Apply Filters</button>
|
|
<button type="button" class="clear-btn" onclick="clearFilters()">
|
|
Clear
|
|
</button>
|
|
</form>
|
|
</div>
|
|
|
|
<div id="loading" class="loading" style="display: none">Loading...</div>
|
|
|
|
<table id="submissionsTable">
|
|
<thead>
|
|
<tr>
|
|
<th data-sort="id">ID</th>
|
|
<th data-sort="name">Name</th>
|
|
<th data-sort="email">Email</th>
|
|
<th data-sort="company">Company</th>
|
|
<th>Message</th>
|
|
<th data-sort="created_at">Date</th>
|
|
<th>Actions</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="submissionsBody">
|
|
<tr>
|
|
<td colspan="7" class="no-data">Loading submissions...</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
|
|
<div class="pagination" id="pagination"></div>
|
|
|
|
<script>
|
|
let currentPage = 1;
|
|
let currentSortBy = "created_at";
|
|
let currentSortOrder = "desc";
|
|
|
|
// Load submissions on page load
|
|
document.addEventListener("DOMContentLoaded", function () {
|
|
loadSubmissions();
|
|
});
|
|
|
|
// Handle filter form submission
|
|
document
|
|
.getElementById("filterForm")
|
|
.addEventListener("submit", function (e) {
|
|
e.preventDefault();
|
|
currentPage = 1;
|
|
loadSubmissions();
|
|
});
|
|
|
|
// Handle table header sorting
|
|
document.querySelectorAll("th[data-sort]").forEach((header) => {
|
|
header.addEventListener("click", function () {
|
|
const sortBy = this.dataset.sort;
|
|
if (currentSortBy === sortBy) {
|
|
currentSortOrder = currentSortOrder === "asc" ? "desc" : "asc";
|
|
} else {
|
|
currentSortBy = sortBy;
|
|
currentSortOrder = "asc";
|
|
}
|
|
currentPage = 1;
|
|
loadSubmissions();
|
|
});
|
|
});
|
|
|
|
function clearFilters() {
|
|
document.getElementById("email").value = "";
|
|
document.getElementById("date_from").value = "";
|
|
document.getElementById("date_to").value = "";
|
|
document.getElementById("per_page").value = "50";
|
|
currentPage = 1;
|
|
currentSortBy = "created_at";
|
|
currentSortOrder = "desc";
|
|
loadSubmissions();
|
|
}
|
|
|
|
function loadSubmissions() {
|
|
const loading = document.getElementById("loading");
|
|
const table = document.getElementById("submissionsTable");
|
|
|
|
loading.style.display = "block";
|
|
table.style.opacity = "0.5";
|
|
|
|
const params = new URLSearchParams({
|
|
page: currentPage,
|
|
per_page: document.getElementById("per_page").value,
|
|
sort_by: currentSortBy,
|
|
sort_order: currentSortOrder,
|
|
email: document.getElementById("email").value,
|
|
date_from: document.getElementById("date_from").value,
|
|
date_to: document.getElementById("date_to").value,
|
|
});
|
|
|
|
fetch(`/api/contact?${params}`)
|
|
.then((response) => response.json())
|
|
.then((data) => {
|
|
if (data.status === "ok") {
|
|
displaySubmissions(data.submissions);
|
|
displayPagination(data.pagination);
|
|
} else {
|
|
showMessage(
|
|
"Error loading submissions: " +
|
|
(data.message || "Unknown error"),
|
|
"error"
|
|
);
|
|
}
|
|
})
|
|
.catch((error) => {
|
|
console.error("Error:", error);
|
|
showMessage("Error loading submissions", "error");
|
|
})
|
|
.finally(() => {
|
|
loading.style.display = "none";
|
|
table.style.opacity = "1";
|
|
});
|
|
}
|
|
|
|
function displaySubmissions(submissions) {
|
|
const tbody = document.getElementById("submissionsBody");
|
|
|
|
if (submissions.length === 0) {
|
|
tbody.innerHTML =
|
|
'<tr><td colspan="7" class="no-data">No submissions found</td></tr>';
|
|
return;
|
|
}
|
|
|
|
tbody.innerHTML = submissions
|
|
.map(
|
|
(submission) => `
|
|
<tr>
|
|
<td>${submission.id}</td>
|
|
<td>${escapeHtml(submission.name)}</td>
|
|
<td>${escapeHtml(submission.email)}</td>
|
|
<td>${escapeHtml(submission.company || "")}</td>
|
|
<td class="submission-details">${escapeHtml(
|
|
submission.message
|
|
)}</td>
|
|
<td>${new Date(submission.created_at).toLocaleString()}</td>
|
|
<td><button class="delete-btn" onclick="deleteSubmission(${
|
|
submission.id
|
|
})">Delete</button></td>
|
|
</tr>
|
|
`
|
|
)
|
|
.join("");
|
|
}
|
|
|
|
function displayPagination(pagination) {
|
|
const paginationDiv = document.getElementById("pagination");
|
|
|
|
if (pagination.pages <= 1) {
|
|
paginationDiv.innerHTML = "";
|
|
return;
|
|
}
|
|
|
|
let buttons = [];
|
|
|
|
// Previous button
|
|
buttons.push(
|
|
`<button ${
|
|
pagination.page <= 1 ? "disabled" : ""
|
|
} onclick="changePage(${pagination.page - 1})">Previous</button>`
|
|
);
|
|
|
|
// Page numbers
|
|
const startPage = Math.max(1, pagination.page - 2);
|
|
const endPage = Math.min(pagination.pages, pagination.page + 2);
|
|
|
|
for (let i = startPage; i <= endPage; i++) {
|
|
buttons.push(
|
|
`<button class="${
|
|
i === pagination.page ? "active" : ""
|
|
}" onclick="changePage(${i})">${i}</button>`
|
|
);
|
|
}
|
|
|
|
// Next button
|
|
buttons.push(
|
|
`<button ${
|
|
pagination.page >= pagination.pages ? "disabled" : ""
|
|
} onclick="changePage(${pagination.page + 1})">Next</button>`
|
|
);
|
|
|
|
paginationDiv.innerHTML = buttons.join("");
|
|
}
|
|
|
|
function changePage(page) {
|
|
currentPage = page;
|
|
loadSubmissions();
|
|
window.scrollTo(0, 0);
|
|
}
|
|
|
|
function deleteSubmission(id) {
|
|
if (!confirm("Are you sure you want to delete this submission?")) {
|
|
return;
|
|
}
|
|
|
|
fetch(`/api/contact/${id}`, {
|
|
method: "DELETE",
|
|
})
|
|
.then((response) => response.json())
|
|
.then((data) => {
|
|
if (data.status === "ok") {
|
|
showMessage("Submission deleted successfully", "success");
|
|
loadSubmissions(); // Reload the current page
|
|
} else {
|
|
showMessage(
|
|
"Error deleting submission: " +
|
|
(data.message || "Unknown error"),
|
|
"error"
|
|
);
|
|
}
|
|
})
|
|
.catch((error) => {
|
|
console.error("Error:", error);
|
|
showMessage("Error deleting submission", "error");
|
|
});
|
|
}
|
|
|
|
function showMessage(text, type) {
|
|
const messageDiv = document.getElementById("message");
|
|
messageDiv.className = `message ${type}`;
|
|
messageDiv.textContent = text;
|
|
messageDiv.style.display = "block";
|
|
|
|
// Auto-hide after 5 seconds
|
|
setTimeout(() => {
|
|
messageDiv.style.display = "none";
|
|
}, 5000);
|
|
}
|
|
|
|
function escapeHtml(text) {
|
|
const div = document.createElement("div");
|
|
div.textContent = text;
|
|
return div.innerHTML;
|
|
}
|
|
</script>
|
|
</body>
|
|
</html>
|