Files
gh-christianlouis-docuelevate/frontend/templates/signup.html
T
copilot-swe-agent[bot] e518bce922 fix: merge main branch and renumber migration 037→040
Resolve all merge conflicts between our automation feature branch and
current main (v0.163.0, 920 commits ahead).

Conflicts resolved:
- app/api/__init__.py: add automation_router alongside main's new routers
  (classification_rules, qr_auth, sessions, system_reset)
- app/config.py: add main's new settings (dropbox_use_global_credentials,
  factory_reset_on_startup, enable_factory_reset)
- app/models.py: add main's new models (ClassificationRuleModel, UserSession,
  QRLoginChallenge, SharePoint integration type)
- app/utils/settings_service.py: merge automation_hooks_enabled with main's
  new metadata entries
- docs/API.md: merge automation API docs with main's classification rules docs
- docs/ConfigurationGuide.md: add factory reset settings
- tests/conftest.py: import both AutomationHook and new main models

Migration renumbered:
- 037_add_automation_hooks → 040_add_automation_hooks
- down_revision: 039_add_classification_rules (was 036_add_document_translation_fields)
- Chain: 036 → 037 → 038 → 039 → 040 (automation hooks)

For all non-automation files with conflicts, main's version was taken since
our branch did not modify those files (conflicts were from a stale prior merge).

Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com>
Agent-Logs-Url: https://github.com/christianlouis/DocuElevate/sessions/cb62f012-3b69-4415-835e-3857ce3e9f45
2026-03-20 23:54:04 +00:00

180 lines
9.3 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>DocuElevate - Create Account</title>
<link href="https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css"
integrity="sha512-DTOQO9RWCH3ppGqcWaEA1BIZOC6xxalwEsw9c2QQeAIftl+Vegovlnee1c9QX4TctnWMn13TZye+giMm8e2LwA=="
crossorigin="anonymous" referrerpolicy="no-referrer" />
<script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"></script>
</head>
<body class="bg-gray-100 min-h-screen flex items-center justify-center py-8">
<main class="bg-white rounded-lg shadow-lg p-8 max-w-md w-full" role="main">
<div class="flex justify-center mb-6">
<img src="/static/images/logo_writing.svg" alt="DocuElevate Logo" class="h-16">
</div>
<h1 class="text-2xl font-bold text-center text-gray-800 mb-2">Create your account</h1>
<p class="text-center text-gray-500 text-sm mb-6">Already have an account?
<a href="/login" class="text-blue-600 hover:text-blue-500 font-medium">Sign in</a>
</p>
<div
x-data="{
email: '',
username: '',
display_name: '',
password: '',
password_confirm: '',
loading: false,
error: '',
async submit() {
this.error = '';
if (this.username.length < 3 || this.username.length > 64) {
this.error = 'Username must be between 3 and 64 characters.';
return;
}
if (!/^[a-zA-Z0-9_-]+$/.test(this.username)) {
this.error = 'Username may only contain letters, numbers, hyphens, and underscores. Dots and other special characters are not allowed.';
return;
}
if (this.password !== this.password_confirm) {
this.error = 'Passwords do not match.';
return;
}
this.loading = true;
try {
const resp = await fetch('/api/auth/signup', {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-CSRF-Token': '{{ csrf_token }}' },
body: JSON.stringify({
email: this.email,
username: this.username,
display_name: this.display_name || null,
password: this.password,
password_confirm: this.password_confirm
})
});
if (resp.ok) {
const data = await resp.json();
if (data.email_verification_required) {
window.location.href = '/verify-email-sent';
} else {
window.location.href = '/login?message=Account+created+successfully.+You+can+now+log+in.';
}
} else {
const data = await resp.json();
const detail = data.detail;
if (Array.isArray(detail)) {
this.error = detail.map(e => e.msg || String(e)).join(' ') || 'Registration failed. Please try again.';
} else {
this.error = detail || 'Registration failed. Please try again.';
}
}
} catch(e) {
this.error = 'Network error. Please try again.';
} finally {
this.loading = false;
}
}
}"
>
<div x-show="error" x-cloak
class="bg-red-100 border-l-4 border-red-500 text-red-700 p-4 mb-6 rounded"
role="alert"
aria-live="polite">
<p x-text="error"></p>
</div>
<form @submit.prevent="submit" class="space-y-4" novalidate>
<div>
<label for="email" class="block text-sm font-medium text-gray-700">Email address <span aria-hidden="true" class="text-red-500">*</span></label>
<input
type="email" id="email" name="email" required autocomplete="email"
x-model="email"
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring focus:ring-blue-500 focus:ring-opacity-50"
aria-required="true"
style="min-height:44px;"
>
</div>
<div>
<label for="username" class="block text-sm font-medium text-gray-700">Username <span aria-hidden="true" class="text-red-500">*</span></label>
<input
type="text" id="username" name="username" required autocomplete="username"
x-model="username"
pattern="^[a-zA-Z0-9_-]+$"
minlength="3" maxlength="64"
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring focus:ring-blue-500 focus:ring-opacity-50"
aria-required="true"
aria-describedby="username-hint"
style="min-height:44px;"
>
<p id="username-hint" class="mt-1 text-xs text-gray-500">364 characters. Letters, numbers, hyphens and underscores only.</p>
</div>
<div>
<label for="display_name" class="block text-sm font-medium text-gray-700">Display name <span class="text-gray-400">(optional)</span></label>
<input
type="text" id="display_name" name="display_name" autocomplete="name"
x-model="display_name"
maxlength="255"
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring focus:ring-blue-500 focus:ring-opacity-50"
style="min-height:44px;"
>
</div>
<div>
<label for="password" class="block text-sm font-medium text-gray-700">Password <span aria-hidden="true" class="text-red-500">*</span></label>
<input
type="password" id="password" name="password" required autocomplete="new-password"
x-model="password"
minlength="8" maxlength="128"
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring focus:ring-blue-500 focus:ring-opacity-50"
aria-required="true"
aria-describedby="password-hint"
style="min-height:44px;"
>
<p id="password-hint" class="mt-1 text-xs text-gray-500">Minimum 8 characters.</p>
</div>
<div>
<label for="password_confirm" class="block text-sm font-medium text-gray-700">Confirm password <span aria-hidden="true" class="text-red-500">*</span></label>
<input
type="password" id="password_confirm" name="password_confirm" required autocomplete="new-password"
x-model="password_confirm"
minlength="8" maxlength="128"
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring focus:ring-blue-500 focus:ring-opacity-50"
aria-required="true"
style="min-height:44px;"
>
</div>
<button
type="submit"
:disabled="loading"
class="w-full flex justify-center py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 disabled:opacity-50"
style="min-height:44px;"
>
<span x-show="!loading">Create account</span>
<span x-show="loading" x-cloak>
<i class="fas fa-spinner fa-spin mr-2" aria-hidden="true"></i>Creating account&hellip;
</span>
</button>
</form>
</div>
<div class="mt-6 text-center">
<a href="/" class="text-sm font-medium text-blue-600 hover:text-blue-500">
Return to Home
</a>
</div>
</main>
<div class="fixed bottom-4 text-center w-full text-xs text-gray-500">
DocuElevate {{ app_version|default('', true) }}
</div>
</body>
</html>