WhatsApp demo path (relay #748): - WHATSAPP_DEMO_TO config under the WhatsApp section (env-based, .env-only; .env.example keeps an empty placeholder; real numbers never enter source). - build_demo_webhook_payload() in app/routers/whatsapp.py builds the Meta demo payload from it (fails closed when unset), so the webhook round trip logs from_number = demo number (surfaces in GET /api/whatsapp/mock-log) and the auto-reply targets the same number. - tests/test_whatsapp_demo_number.py: default empty + never committed in tracked files, payload builder from/to, 200/403/401 gates unchanged. Branding (logo-assets-v1, sha256-verified, same-origin app/static/branding): - Login header uses h96 full lockup; logged-in topbar (base.html) uses h48 on a light chip (logo ink is ~2:1 vs the dark nav); favicons 32x32 + 16x16 in <head>. img-src 'self' data: blob: already allows /static/branding/*. - tests/test_branding_assets.py: page placement + same-origin serving + CSP. - AGENTS.md synced.
73 lines
3.6 KiB
HTML
73 lines
3.6 KiB
HTML
{% extends "base.html" %}
|
|
{% block content %}
|
|
<div class="min-h-[80vh] flex items-center justify-center">
|
|
<div class="w-full max-w-md" x-data="loginForm()">
|
|
<div class="bg-white rounded-2xl shadow-lg p-8">
|
|
<div class="text-center mb-8">
|
|
<!-- Denya Developers full lockup (same-origin app/static/branding) -->
|
|
<img src="/static/branding/denya-logo-h96.png" alt="Denya Developers"
|
|
class="mx-auto mb-4 h-24 w-auto">
|
|
<h1 class="text-2xl font-bold text-gray-900">Denya OneCare</h1>
|
|
<p class="text-gray-500 mt-1">Sign in to your dashboard</p>
|
|
</div>
|
|
|
|
<form @submit.prevent="submitLogin" class="space-y-5">
|
|
<div>
|
|
<label class="block text-sm font-medium text-gray-700 mb-1">Email</label>
|
|
<input type="email" x-model="email" required
|
|
class="w-full px-4 py-2.5 rounded-lg border border-gray-300 focus:ring-2 focus:ring-denya-500 focus:border-transparent outline-none transition"
|
|
placeholder="you@example.com">
|
|
</div>
|
|
<div>
|
|
<label class="block text-sm font-medium text-gray-700 mb-1">Password</label>
|
|
<input type="password" x-model="password" required
|
|
class="w-full px-4 py-2.5 rounded-lg border border-gray-300 focus:ring-2 focus:ring-denya-500 focus:border-transparent outline-none transition"
|
|
placeholder="••••••••">
|
|
</div>
|
|
<div x-show="error" class="text-red-600 text-sm bg-red-50 p-3 rounded-lg" x-text="error"></div>
|
|
<button type="submit" :disabled="submitting"
|
|
class="w-full py-2.5 px-4 bg-denya-600 hover:bg-denya-700 text-white font-medium rounded-lg transition disabled:opacity-50 disabled:cursor-not-allowed flex items-center justify-center space-x-2">
|
|
<svg x-show="submitting" class="animate-spin h-5 w-5" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
|
|
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
|
|
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z"></path>
|
|
</svg>
|
|
<span x-text="submitting ? 'Signing in...' : 'Sign In'"></span>
|
|
</button>
|
|
</form>
|
|
|
|
<div class="mt-6 pt-6 border-t border-gray-200">
|
|
<p class="text-xs text-gray-400 text-center">
|
|
<strong>Demo Accounts:</strong><br>
|
|
bella@denya.com / denya123 — CS Rep<br>
|
|
nicholas@denya.com / denya123 — FM Dispatcher<br>
|
|
scott@denya.com / denya123 — CEO<br>
|
|
jerome@denya.com / denya123 — Admin
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
function loginForm() {
|
|
return {
|
|
email: '',
|
|
password: '',
|
|
error: '',
|
|
submitting: false,
|
|
async submitLogin() {
|
|
this.error = '';
|
|
this.submitting = true;
|
|
try {
|
|
await app().login(this.email, this.password);
|
|
} catch (e) {
|
|
this.error = e.message || 'Invalid credentials. Please try again.';
|
|
} finally {
|
|
this.submitting = false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
</script>
|
|
{% endblock %}
|