
- Updated `.env` and added `.env.test` for environment variables. - Introduced API documentation in `API_DOCUMENTATION.md`. - Added authentication setup guide in `AUTHENTICATION_SETUP.md`. - Implemented user authentication with JWT and email verification. - Created new routes for user management and form submissions. - Added middleware for API key authentication and error handling. - Set up Redis for rate limiting and notifications. - Removed obsolete files and configurations related to the previous Rust implementation.
177 lines
5.0 KiB
Plaintext
177 lines
5.0 KiB
Plaintext
<div class="header-bar">
|
|
<h1>
|
|
Submissions for <span style="font-weight: normal"><%= formName %></span>
|
|
</h1>
|
|
<div>
|
|
<a href="/dashboard" class="btn btn-secondary">Back to My Forms</a>
|
|
<a href="/dashboard/submissions/<%= formUuid %>/export" class="btn"
|
|
>Export CSV</a
|
|
>
|
|
</div>
|
|
</div>
|
|
|
|
<% if (submissions.length === 0) { %>
|
|
<div
|
|
style="
|
|
padding: 1rem;
|
|
background-color: #e9ecef;
|
|
border-radius: 0.25rem;
|
|
text-align: center;
|
|
">
|
|
No submissions yet for this form.
|
|
</div>
|
|
<% } else { %> <% submissions.forEach(submission => { %>
|
|
<div
|
|
style="
|
|
border: 1px solid #ddd;
|
|
border-radius: 0.25rem;
|
|
margin-bottom: 1rem;
|
|
background-color: white;
|
|
">
|
|
<div style="padding: 1rem">
|
|
<div
|
|
style="
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: flex-start;
|
|
margin-bottom: 0.5rem;
|
|
">
|
|
<div>
|
|
<h6 style="margin: 0 0 0.25rem 0; color: #6c757d">
|
|
Submitted: <%= new Date(submission.submitted_at).toLocaleString() %>
|
|
</h6>
|
|
<h6 style="margin: 0; color: #6c757d">
|
|
IP: <%= submission.ip_address %>
|
|
</h6>
|
|
</div>
|
|
<form
|
|
action="/dashboard/submissions/delete/<%= submission.id %>"
|
|
method="POST"
|
|
style="display: inline"
|
|
onsubmit="return confirm('Are you sure you want to delete this submission?');">
|
|
<input
|
|
type="hidden"
|
|
name="formUuidForRedirect"
|
|
value="<%= formUuid %>" />
|
|
<button
|
|
type="submit"
|
|
class="btn btn-secondary"
|
|
style="background-color: #dc3545; border-color: #dc3545">
|
|
Delete
|
|
</button>
|
|
</form>
|
|
</div>
|
|
<hr style="margin-top: 0.5rem; margin-bottom: 1rem" />
|
|
<div>
|
|
<% let data = {}; try { data = JSON.parse(submission.data); } catch (e) {
|
|
console.error("Failed to parse submission data:", submission.data); data =
|
|
{ "error": "Could not parse submission data" }; } %> <%
|
|
Object.entries(data).forEach(([key, value]) => { %> <% if (key !==
|
|
'honeypot_field' && key !== '_thankyou') { %>
|
|
<div style="margin-bottom: 0.5rem">
|
|
<strong
|
|
style="display: inline-block; min-width: 120px; vertical-align: top"
|
|
><%= key %>:</strong
|
|
>
|
|
<span style="white-space: pre-wrap; word-break: break-all"
|
|
><%= value %></span
|
|
>
|
|
</div>
|
|
<% } %> <% }); %>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<% }); %>
|
|
|
|
<!-- Pagination -->
|
|
<% if (pagination.totalPages > 1) { %>
|
|
<nav
|
|
aria-label="Submissions pagination"
|
|
style="margin-top: 2rem; margin-bottom: 2rem">
|
|
<ul
|
|
style="
|
|
display: flex;
|
|
justify-content: center;
|
|
padding-left: 0;
|
|
list-style: none;
|
|
">
|
|
<% if (pagination.currentPage > 1) { %>
|
|
<li style="margin: 0 0.25rem">
|
|
<a
|
|
href="/dashboard/submissions/<%= formUuid %>?page=<%= pagination.currentPage - 1 %>&limit=<%= pagination.limit %>"
|
|
class="btn btn-secondary"
|
|
>Previous</a
|
|
>
|
|
</li>
|
|
<% } else { %>
|
|
<li style="margin: 0 0.25rem">
|
|
<span
|
|
class="btn btn-secondary"
|
|
style="opacity: 0.65; pointer-events: none"
|
|
>Previous</span
|
|
>
|
|
</li>
|
|
<% } %> <% const maxPagesToShow = 5; let startPage = Math.max(1,
|
|
pagination.currentPage - Math.floor(maxPagesToShow / 2)); let endPage =
|
|
Math.min(pagination.totalPages, startPage + maxPagesToShow - 1); if (endPage
|
|
- startPage + 1 < maxPagesToShow) { startPage = Math.max(1, endPage -
|
|
maxPagesToShow + 1); } %> <% if (startPage > 1) { %>
|
|
<li style="margin: 0 0.25rem">
|
|
<a
|
|
href="/dashboard/submissions/<%= formUuid %>?page=1&limit=<%= pagination.limit %>"
|
|
class="btn btn-secondary"
|
|
>1</a
|
|
>
|
|
</li>
|
|
<% if (startPage > 2) { %>
|
|
<li style="margin: 0 0.25rem">
|
|
<span class="btn btn-secondary" style="pointer-events: none">...</span>
|
|
</li>
|
|
<% } %> <% } %> <% for(let i = startPage; i <= endPage; i++) { %>
|
|
<li style="margin: 0 0.25rem">
|
|
<a
|
|
href="/dashboard/submissions/<%= formUuid %>?page=<%= i %>&limit=<%= pagination.limit %>"
|
|
class="btn <%= i === pagination.currentPage ? '' : 'btn-secondary' %>"
|
|
><%= i %></a
|
|
>
|
|
</li>
|
|
<% } %> <% if (endPage < pagination.totalPages) { %> <% if (endPage <
|
|
pagination.totalPages - 1) { %>
|
|
<li style="margin: 0 0.25rem">
|
|
<span class="btn btn-secondary" style="pointer-events: none">...</span>
|
|
</li>
|
|
<% } %>
|
|
<li style="margin: 0 0.25rem">
|
|
<a
|
|
href="/dashboard/submissions/<%= formUuid %>?page=<%= pagination.totalPages %>&limit=<%= pagination.limit %>"
|
|
class="btn btn-secondary"
|
|
><%= pagination.totalPages %></a
|
|
>
|
|
</li>
|
|
<% } %> <% if (pagination.currentPage < pagination.totalPages) { %>
|
|
<li style="margin: 0 0.25rem">
|
|
<a
|
|
href="/dashboard/submissions/<%= formUuid %>?page=<%= pagination.currentPage + 1 %>&limit=<%= pagination.limit %>"
|
|
class="btn btn-secondary"
|
|
>Next</a
|
|
>
|
|
</li>
|
|
<% } else { %>
|
|
<li style="margin: 0 0.25rem">
|
|
<span
|
|
class="btn btn-secondary"
|
|
style="opacity: 0.65; pointer-events: none"
|
|
>Next</span
|
|
>
|
|
</li>
|
|
<% } %>
|
|
</ul>
|
|
</nav>
|
|
<div style="text-align: center; color: #6c757d">
|
|
Showing <%= (pagination.currentPage - 1) * pagination.limit + 1 %> to <%=
|
|
Math.min(pagination.currentPage * pagination.limit,
|
|
pagination.totalSubmissions) %> of <%= pagination.totalSubmissions %>
|
|
submissions
|
|
</div>
|
|
<% } %> <% } %>
|