formies/views/dashboard.ejs
Mohamad.Elsena 2927013a6d Update environment configuration, add API documentation, and implement user authentication system
- 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.
2025-05-28 11:18:35 +02:00

357 lines
12 KiB
Plaintext

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>User Dashboard - Formies</title>
<!-- Basic styling - replace with a proper CSS framework or custom styles later -->
<style>
body {
font-family: sans-serif;
margin: 0;
background-color: #f4f7f6;
}
.navbar {
background-color: #333;
color: white;
padding: 1rem;
display: flex;
justify-content: space-between;
align-items: center;
}
.navbar a {
color: white;
text-decoration: none;
margin-left: 1rem;
}
.navbar .logo {
font-size: 1.5rem;
font-weight: bold;
}
.container {
padding: 2rem;
}
.header-bar {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 1.5rem;
}
.header-bar h1 {
margin: 0;
}
.btn {
background-color: #007bff;
color: white;
padding: 0.5rem 1rem;
text-decoration: none;
border-radius: 0.25rem;
border: none;
cursor: pointer;
}
.btn-secondary {
background-color: #6c757d;
}
/* Add more styles as needed */
</style>
</head>
<body>
<nav class="navbar">
<div class="logo"><a href="/dashboard">Formies</a></div>
<div>
<a href="/dashboard">My Forms</a>
<a href="/dashboard/create-form">Create New Form</a>
<a href="/dashboard/api-keys">API Keys</a>
<!-- Placeholder for Account Settings -->
<a href="#">Hi, <%= user ? user.email : 'User' %> &#9662;</a>
</div>
</nav>
<div class="container">
<!-- Main content will be injected or decided by the route -->
<% if (view === 'my_forms') { %>
<div class="header-bar">
<h1>My Forms</h1>
<a href="/dashboard/create-form" class="btn">+ Create New Form</a>
</div>
<%- include('partials/_forms_table', { forms: forms, appUrl: appUrl }) %>
<% } else if (view === 'create_form') { %>
<h1>Create New Form</h1>
<form action="/dashboard/forms/create" method="POST">
<div>
<label for="formName">Form Name:</label>
<input
type="text"
id="formName"
name="formName"
value="<%= typeof formNameValue !== 'undefined' ? formNameValue : 'Untitled Form' %>"
required />
</div>
<button type="submit" class="btn">Create Form</button>
<% if (typeof error !== 'undefined' && error) { %>
<p style="color: red; margin-top: 1rem"><%= error %></p>
<% } %>
</form>
<% } else if (view === 'form_submissions') { %> <%-
include('partials/_submissions_view', { submissions: submissions,
formUuid: formUuid, formName: formName, pagination: pagination, appUrl:
appUrl }) %> <% } else if (view === 'account_settings') { %>
<h1>Account Settings</h1>
<p>Account settings will be here.</p>
<% } else if (view === 'form_settings') { %>
<div class="header-bar">
<h1>
Settings for <span style="font-weight: normal"><%= formName %></span>
</h1>
<a href="/dashboard" class="btn btn-secondary">Back to My Forms</a>
</div>
<% if (typeof successMessage !== 'undefined' && successMessage) { %>
<div
style="
background-color: #d4edda;
color: #155724;
padding: 0.75rem 1.25rem;
margin-bottom: 1rem;
border: 1px solid #c3e6cb;
border-radius: 0.25rem;
">
<%= successMessage %>
</div>
<% } %> <% if (typeof errorMessage !== 'undefined' && errorMessage) { %>
<div
style="
background-color: #f8d7da;
color: #721c24;
padding: 0.75rem 1.25rem;
margin-bottom: 1rem;
border: 1px solid #f5c6cb;
border-radius: 0.25rem;
">
<%= errorMessage %>
</div>
<% } %>
<form
action="/dashboard/forms/<%= formUuid %>/settings/update"
method="POST"
style="
background-color: white;
padding: 1.5rem;
border-radius: 0.25rem;
border: 1px solid #ddd;
margin-bottom: 2rem;
">
<h3 style="margin-top: 0; margin-bottom: 1rem">General Settings</h3>
<div style="margin-bottom: 1rem">
<label
for="formNameInput"
style="display: block; margin-bottom: 0.5rem"
>Form Name:</label
>
<input
type="text"
id="formNameInput"
name="formName"
value="<%= currentFormName %>"
required
style="
width: 100%;
padding: 0.5rem;
border: 1px solid #ccc;
border-radius: 0.25rem;
" />
</div>
<h3 style="margin-top: 2rem; margin-bottom: 1rem">Email Notifications</h3>
<div style="margin-bottom: 1rem">
<input type="checkbox" id="emailNotificationsEnabled"
name="emailNotificationsEnabled" value="on" <%=
currentEmailNotificationsEnabled ? 'checked' : '' %>
style="margin-right: 0.5rem;">
<label for="emailNotificationsEnabled"
>Enable Email Notifications for new submissions</label
>
</div>
<div style="margin-bottom: 1rem">
<label
for="notificationEmailAddress"
style="display: block; margin-bottom: 0.5rem"
>Notification Email Address:</label
>
<input
type="email"
id="notificationEmailAddress"
name="notificationEmailAddress"
value="<%= currentNotificationEmailAddress || '' %>"
placeholder="Defaults to your account email (<%= user.email %>)"
style="
width: 100%;
padding: 0.5rem;
border: 1px solid #ccc;
border-radius: 0.25rem;
" />
<small style="display: block; margin-top: 0.25rem; color: #6c757d"
>If left blank, notifications will be sent to your account email:
<%= user.email %></small
>
</div>
<h3 style="margin-top: 2rem; margin-bottom: 1rem">Spam Protection</h3>
<div style="margin-bottom: 1rem">
<input type="checkbox" id="recaptchaEnabled"
name="recaptchaEnabled" value="on" <%=
currentRecaptchaEnabled ? 'checked' : '' %>
style="margin-right: 0.5rem;">
<label for="recaptchaEnabled"
>Enable Google reCAPTCHA v2 ("I'm not a robot")</label
>
<small style="display: block; margin-top: 0.25rem; color: #6c757d"
>Uses the globally configured site and secret keys. Ensure these are set in your server's .env file.</small
>
</div>
<h3 style="margin-top: 2rem; margin-bottom: 1rem">Thank You Page</h3>
<div style="margin-bottom: 1rem;">
<label for="thankYouUrl" style="display: block; margin-bottom: 0.5rem;">Thank You URL (Optional):</label>
<input
type="url"
id="thankYouUrl"
name="thankYouUrl"
value="<%= currentThankYouUrl || '' %>"
placeholder="e.g., https://example.com/thank-you"
style="
width: 100%;
padding: 0.5rem;
border: 1px solid #ccc;
border-radius: 0.25rem;
" />
</div>
<div style="margin-bottom: 1rem;">
<label for="thankYouMessage" style="display: block; margin-bottom: 0.5rem;">Custom Thank You Message (Optional):</label>
<textarea
id="thankYouMessage"
name="thankYouMessage"
rows="3"
placeholder="e.g., Thanks for your submission!"
style="
width: 100%;
padding: 0.5rem;
border: 1px solid #ccc;
border-radius: 0.25rem;
"><%= currentThankYouMessage || '' %></textarea>
<small style="display: block; margin-top: 0.25rem; color: #6c757d;">
If a "Thank You URL" is provided, it will be used. Otherwise, this custom message will be shown. If both are blank, a default message is used.
</small>
</div>
<h3 style="margin-top: 2rem; margin-bottom: 1rem">Allowed Domains</h3>
<div style="margin-bottom: 1rem;">
<label for="allowedDomains" style="display: block; margin-bottom: 0.5rem;">Allowed Domains:</label>
<textarea
id="allowedDomains"
name="allowedDomains"
rows="3"
placeholder="e.g., example.com, app.example.com"
style="
width: 100%;
padding: 0.5rem;
border: 1px solid #ccc;
border-radius: 0.25rem;
"><%= currentAllowedDomains || '' %></textarea>
<small style="display: block; margin-top: 0.25rem; color: #6c757d;">
Comma-separated list of domains. Leave blank to allow submissions from any domain.
</small>
</div>
<button type="submit" class="btn" style="margin-top: 1.5rem;">Save All Settings</button>
</form>
<% } else if (view === 'api_keys') { %>
<div class="header-bar">
<h1>API Keys</h1>
</div>
<% if (typeof successMessage !== 'undefined' && successMessage) { %>
<div style="background-color: #d4edda; color: #155724; padding: 0.75rem 1.25rem; margin-bottom: 1rem; border: 1px solid #c3e6cb; border-radius: 0.25rem;">
<%= successMessage %>
</div>
<% } %>
<% if (typeof errorMessage !== 'undefined' && errorMessage) { %>
<div style="background-color: #f8d7da; color: #721c24; padding: 0.75rem 1.25rem; margin-bottom: 1rem; border: 1px solid #f5c6cb; border-radius: 0.25rem;">
<%= errorMessage %>
</div>
<% } %>
<% if (typeof newlyGeneratedApiKey !== 'undefined' && newlyGeneratedApiKey) { %>
<div style="background-color: #fff3cd; color: #856404; padding: 1rem; margin-bottom: 1.5rem; border: 1px solid #ffeeba; border-radius: 0.25rem;">
<h3 style="margin-top:0;">New API Key Generated: <%= newlyGeneratedApiKeyName %></h3>
<p><strong>Important:</strong> This is the only time you will see this API key. Copy it now and store it securely.</p>
<pre style="background-color: #e9ecef; padding: 0.5rem; border-radius: 0.25rem; word-break: break-all;"><code id="newApiKey"><%= newlyGeneratedApiKey %></code></pre>
<button onclick="copyApiKeyToClipboard()" class="btn btn-secondary" style="margin-top:0.5rem;">Copy to Clipboard</button>
</div>
<% } %>
<div style="background-color: white; padding: 1.5rem; border-radius: 0.25rem; border: 1px solid #ddd; margin-bottom: 2rem;">
<h3 style="margin-top: 0; margin-bottom: 1rem">Generate New API Key</h3>
<form action="/dashboard/api-keys/generate" method="POST">
<div style="margin-bottom: 1rem;">
<label for="keyName" style="display: block; margin-bottom: 0.5rem;">Key Name (for your reference):</label>
<input type="text" id="keyName" name="keyName" required style="width: 100%; padding: 0.5rem; border: 1px solid #ccc; border-radius: 0.25rem;">
</div>
<button type="submit" class="btn">Generate Key</button>
</form>
</div>
<div style="background-color: white; padding: 1.5rem; border-radius: 0.25rem; border: 1px solid #ddd;">
<h3 style="margin-top: 0; margin-bottom: 1rem">Your API Keys</h3>
<% if (apiKeys && apiKeys.length > 0) { %>
<table style="width: 100%; border-collapse: collapse;">
<thead>
<tr style="text-align: left; border-bottom: 1px solid #dee2e6;">
<th style="padding: 0.75rem;">Name</th>
<th style="padding: 0.75rem;">Identifier (Prefix)</th>
<th style="padding: 0.75rem;">Created At</th>
<th style="padding: 0.75rem;">Last Used</th>
<th style="padding: 0.75rem;">Actions</th>
</tr>
</thead>
<tbody>
<% apiKeys.forEach(key => { %>
<tr style="border-bottom: 1px solid #f1f1f1;">
<td style="padding: 0.75rem;"><%= key.key_name %></td>
<td style="padding: 0.75rem;"><%= key.api_key_identifier %></td>
<td style="padding: 0.75rem;"><%= new Date(key.created_at).toLocaleDateString() %></td>
<td style="padding: 0.75rem;"><%= key.last_used_at ? new Date(key.last_used_at).toLocaleDateString() : 'Never' %></td>
<td style="padding: 0.75rem;">
<form action="/dashboard/api-keys/<%= key.uuid %>/revoke" method="POST" onsubmit="return confirm('Are you sure you want to revoke this API key? This cannot be undone.');" style="display: inline;">
<button type="submit" class="btn btn-secondary" style="background-color: #dc3545;">Revoke</button>
</form>
</td>
</tr>
<% }) %>
</tbody>
</table>
<% } else { %>
<p>You have not generated any API keys yet.</p>
<% } %>
</div>
<% } %>
</div>
<script>
// Client-side JS for dynamic interactions will go here
function copyApiKeyToClipboard() {
const apiKeyText = document.getElementById('newApiKey').innerText;
navigator.clipboard.writeText(apiKeyText).then(() => {
alert('API Key copied to clipboard!');
}).catch(err => {
console.error('Failed to copy API key: ', err);
alert('Failed to copy API key. Please copy it manually.');
});
}
</script>
</body>
</html>