Files
contact.allucanget.biz/templates/admin_submissions.html
zwitschi 56840ac313
All checks were successful
CI / test (3.11) (push) Successful in 9m26s
CI / build-image (push) Successful in 49s
feat(newsletter): Add subscription confirmation email functionality
- Implemented `send_subscription_confirmation` function to send a confirmation email upon subscription.
- Added a default HTML template for the confirmation email in settings.
- Updated the newsletter management page to handle subscription and unsubscription actions.
- Created new templates for embedding contact and newsletter forms.
- Added styles for the new templates and unified CSS styles across the application.
- Updated tests to reflect changes in the newsletter management API endpoints.
2025-10-30 12:38:26 +01:00

304 lines
8.9 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>
<link rel="stylesheet" href="/static/css/styles.css" />
<style>
body {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
.filters {
background: #f8f9fa;
padding: 15px;
border-radius: 5px;
margin-bottom: 20px;
}
.filters form {
display: flex;
gap: 10px;
flex-wrap: wrap;
align-items: end;
}
</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>