Files
2026-07-02 15:48:24 -04:00

203 lines
9.4 KiB
HTML

{% extends "base.html" %}
{% block title %}Create User{% endblock %}
{% block page_title %}User Management{% endblock %}
{% block head %}
<link rel="stylesheet" href="{{ url_for('static', filename='css/password-strength.css') }}"/>
{% endblock %}
{% block content %}
<div class="row justify-content-center">
<div class="col-lg-7">
<div class="mb-3">
<a href="{{ url_for('admin.users') }}" style="font-size:13px;color:var(--accent);">
<i class="bi bi-arrow-left me-1"></i>Back to Users
</a>
</div>
<div class="card">
<div class="card-header">
<i class="bi bi-person-plus me-2"></i>Create New User
</div>
<div class="card-body">
<form method="POST" novalidate>
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}"/>
<!-- ── Identity ─────────────────────────────────────────────────── -->
<div style="font-size:11px;font-weight:700;letter-spacing:1.2px;text-transform:uppercase;color:var(--muted);margin-bottom:14px;">
Identity
</div>
<div class="row g-3 mb-4">
<div class="col-md-6">
<label class="form-label">Full Name *</label>
<input type="text" class="form-control" name="full_name"
value="{{ form.get('full_name', '') }}" required
placeholder="Jane Smith"/>
</div>
<div class="col-md-6">
<label class="form-label">Username *</label>
<input type="text" class="form-control" name="username"
value="{{ form.get('username', '') }}" required
placeholder="jsmith"
pattern="[a-zA-Z0-9_\-]+"
title="Letters, numbers, underscores and hyphens only"/>
</div>
<div class="col-12">
<label class="form-label">Work Email *</label>
<input type="email" class="form-control" name="email"
value="{{ form.get('email', '') }}" required
placeholder="jane.smith@company.com"/>
</div>
<div class="col-md-6">
<label class="form-label">Department</label>
<input type="text" class="form-control" name="department"
value="{{ form.get('department', '') }}"
placeholder="Finance"/>
</div>
<div class="col-md-6">
<label class="form-label">Phone</label>
<input type="text" class="form-control" name="phone"
value="{{ form.get('phone', '') }}"
placeholder="+1 555 000 0000"/>
</div>
</div>
<!-- ── Role & Status ────────────────────────────────────────────── -->
<div style="font-size:11px;font-weight:700;letter-spacing:1.2px;text-transform:uppercase;color:var(--muted);margin-bottom:14px;padding-top:4px;border-top:1px solid var(--border);">
Role &amp; Status
</div>
<div class="row g-3 mb-4">
<div class="col-md-6">
<label class="form-label">Role *</label>
<select class="form-select" name="role">
{% for r in roles %}
<option value="{{ r }}"
{% if form.get('role', 'employee') == r %}selected{% endif %}>
{{ r.replace('_', ' ').title() }}
</option>
{% endfor %}
</select>
<div style="font-size:11px;color:var(--muted);margin-top:5px;">
<strong>Employee</strong> — can submit &amp; track own tickets.<br>
<strong>IT Staff</strong> — can manage all tickets and KB.<br>
<strong>Admin</strong> — full access including user management.
</div>
</div>
<div class="col-md-6 d-flex align-items-start pt-4">
<label style="display:flex;align-items:center;gap:10px;cursor:pointer;margin-top:10px;">
<input type="checkbox" name="is_active" value="1"
style="accent-color:var(--accent);width:17px;height:17px;"
{% if form.get('is_active', '1') != '0' %}checked{% endif %}/>
<span style="font-size:14px;font-weight:500;">Account Active</span>
</label>
</div>
</div>
<!-- ── Password ─────────────────────────────────────────────────── -->
<div style="font-size:11px;font-weight:700;letter-spacing:1.2px;text-transform:uppercase;color:var(--muted);margin-bottom:14px;padding-top:4px;border-top:1px solid var(--border);">
Password
</div>
<div class="row g-3 mb-4">
<div class="col-md-6">
<label class="form-label">Password *</label>
<div class="pw-wrap">
<input type="password" class="form-control pw-input" name="password"
id="pw-field" required minlength="8" maxlength="128"
placeholder="Min. 8 characters"
autocomplete="new-password"/>
<button type="button" class="pw-toggle" id="pw-field-toggle"
title="Show / hide password" tabindex="-1">
<i class="bi bi-eye"></i>
</button>
</div>
</div>
<div class="col-md-6">
<label class="form-label">Confirm Password *</label>
<div class="pw-wrap">
<input type="password" class="form-control pw-input" name="confirm_password"
id="pw-field2" required minlength="8" maxlength="128"
placeholder="Repeat password"
autocomplete="new-password"/>
<button type="button" class="pw-toggle" id="pw-field2-toggle"
title="Show / hide password" tabindex="-1">
<i class="bi bi-eye"></i>
</button>
</div>
<div class="pw-match" id="pw-match-msg" aria-live="polite" aria-atomic="true"></div>
</div>
<div class="col-12">
<!-- Strength bar -->
<div class="pw-strength-wrap" id="pw-strength-wrap" style="display:none;">
<div class="pw-strength-track">
<div class="pw-strength-fill" id="pw-strength-bar"></div>
</div>
<div class="pw-strength-label" id="pw-strength-label"></div>
</div>
<!-- Rule checklist — mirrors server-side validate_password() rules -->
<div class="pw-rules" id="pw-rules-box" style="display:none;" aria-live="polite" aria-atomic="true">
<div class="pw-rule unmet" id="pw-rule-len">
<span class="ri"><i class="bi bi-circle"></i></span>
<span>8+ characters</span>
</div>
<div class="pw-rule unmet" id="pw-rule-upper">
<span class="ri"><i class="bi bi-circle"></i></span>
<span>Uppercase letter</span>
</div>
<div class="pw-rule unmet" id="pw-rule-digit">
<span class="ri"><i class="bi bi-circle"></i></span>
<span>Number</span>
</div>
<div class="pw-rule unmet" id="pw-rule-special">
<span class="ri"><i class="bi bi-circle"></i></span>
<span>Special character</span>
</div>
</div>
</div>
</div>
<!-- ── Actions ──────────────────────────────────────────────────── -->
<div class="d-flex gap-2 pt-2" style="border-top:1px solid var(--border);">
<button type="submit" class="btn btn-primary">
<i class="bi bi-person-check me-2"></i>Create User
</button>
<a href="{{ url_for('admin.users') }}" class="btn btn-secondary">Cancel</a>
</div>
</form>
</div>
</div>
</div>
</div>
{% endblock %}
{% block scripts %}
<script src="{{ url_for('static', filename='js/password-strength.js') }}"></script>
<script>
const pwStrength = PasswordStrength.init({
password : 'pw-field',
confirm : 'pw-field2',
strengthBar : 'pw-strength-bar',
strengthLabel: 'pw-strength-label',
strengthWrap : 'pw-strength-wrap',
rulesWrap : 'pw-rules-box',
rules: { len: 'pw-rule-len', upper: 'pw-rule-upper', digit: 'pw-rule-digit', special: 'pw-rule-special' },
matchMsg : 'pw-match-msg',
toggles: [
{ input: 'pw-field', button: 'pw-field-toggle' },
{ input: 'pw-field2', button: 'pw-field2-toggle' },
],
});
// ── Block submit on a real mismatch (server re-validates regardless) ──────────
document.querySelector('form').addEventListener('submit', function (e) {
const pw = document.getElementById('pw-field').value;
const pw2 = document.getElementById('pw-field2').value;
if (pw !== pw2) {
e.preventDefault();
pwStrength.renderMatch();
}
});
</script>
{% endblock %}