feat: Sprint 3 frontend dashboards with Alpine.js + Jinja2
- Login page with JWT auth and role-based redirect - CS Dashboard: KPI cards, priority breakdown, recent tickets - FM Dashboard: tech workload, aging analysis, emergency alerts - CEO Dashboard: executive KPIs, charts, risk indicators - All Issues: filterable/sortable ticket table with pagination - Create Issue: form with category tree, property/unit selector, photo uploads - Issue Detail: full timeline, photo gallery, SLA status, action modals - Role-based nav bar: CS/FM/Executive links adapt to user role - Base template: shared Alpine.js app state, toast notifications, loading overlay
This commit is contained in:
@@ -0,0 +1,74 @@
|
||||
{% 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">
|
||||
<div class="mx-auto w-16 h-16 bg-denya-100 rounded-full flex items-center justify-center mb-4">
|
||||
<svg class="w-8 h-8 text-denya-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16m14 0h2m-2 0h-5m-9 0H3m2 0h5M9 7h1m-1 4h1m4-4h1m-1 4h1m-5 10v-5a1 1 0 011-1h2a1 1 0 011 1v5m-4 0h4"/>
|
||||
</svg>
|
||||
</div>
|
||||
<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 %}
|
||||
Reference in New Issue
Block a user