236 lines
12 KiB
HTML
236 lines
12 KiB
HTML
{% extends 'layouts/base.html' %}
|
|
{% load i18n %}
|
|
|
|
{% block title %}{{ page_title }} - PX360{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="container py-4">
|
|
<!-- Header -->
|
|
<div class="d-flex justify-content-between align-items-center mb-4">
|
|
<div>
|
|
<h4 class="mb-1">
|
|
<i class="fas fa-users-cog text-primary me-2"></i>{% trans "Bulk Invite Users" %}
|
|
</h4>
|
|
<p class="text-muted mb-0">{% trans "Invite multiple users at once using a CSV file" %}</p>
|
|
</div>
|
|
<a href="{% url 'accounts:provisional-user-list' %}" class="btn btn-outline-secondary">
|
|
<i class="fas fa-arrow-left me-1"></i>{% trans "Back to Users" %}
|
|
</a>
|
|
</div>
|
|
|
|
<div class="row g-4">
|
|
<!-- CSV Upload Form -->
|
|
<div class="col-lg-8">
|
|
<div class="card">
|
|
<div class="card-header bg-white">
|
|
<h5 class="card-title mb-0">
|
|
<i class="fas fa-upload text-primary me-2"></i>{% trans "Upload CSV File" %}
|
|
</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<form method="post" enctype="multipart/form-data">
|
|
{% csrf_token %}
|
|
|
|
<div class="mb-4">
|
|
<label for="csv_file" class="form-label fw-semibold">{% trans "Select CSV File" %}</label>
|
|
<input type="file" class="form-control" id="csv_file" name="csv_file" accept=".csv" required>
|
|
<div class="form-text">
|
|
{% trans "Upload a CSV file with the required columns. Maximum 500 users per upload." %}
|
|
</div>
|
|
</div>
|
|
|
|
<div class="alert alert-info">
|
|
<h6 class="alert-heading">
|
|
<i class="fas fa-info-circle me-1"></i>{% trans "CSV Format Requirements" %}
|
|
</h6>
|
|
<p class="mb-2">{% trans "Your CSV file must include these columns:" %}</p>
|
|
<ul class="mb-2">
|
|
<li><code>email</code> - {% trans "User's email address (required)" %}</li>
|
|
<li><code>first_name</code> - {% trans "First name (required)" %}</li>
|
|
<li><code>last_name</code> - {% trans "Last name (required)" %}</li>
|
|
<li><code>role</code> - {% trans "Role name, e.g., 'Staff', 'Physician' (required)" %}</li>
|
|
<li><code>hospital_id</code> - {% trans "Hospital UUID (required)" %}</li>
|
|
<li><code>department_id</code> - {% trans "Department UUID (optional)" %}</li>
|
|
</ul>
|
|
<p class="mb-0">
|
|
<a href="data:text/csv;charset=utf-8,email,first_name,last_name,role,hospital_id,department_id\nuser@example.com,John,Doe,Staff,hospital-uuid-here,dept-uuid-here"
|
|
download="bulk_invite_template.csv"
|
|
class="alert-link">
|
|
<i class="fas fa-download me-1"></i>{% trans "Download Template CSV" %}
|
|
</a>
|
|
</p>
|
|
</div>
|
|
|
|
<div class="d-flex gap-2">
|
|
<button type="submit" class="btn btn-primary">
|
|
<i class="fas fa-paper-plane me-1"></i>{% trans "Send Invitations" %}
|
|
</button>
|
|
<a href="{% url 'accounts:provisional-user-list' %}" class="btn btn-outline-secondary">
|
|
{% trans "Cancel" %}
|
|
</a>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
{% if results.total > 0 %}
|
|
<!-- Results Section -->
|
|
<div class="card mt-4">
|
|
<div class="card-header bg-white">
|
|
<h5 class="card-title mb-0">
|
|
<i class="fas fa-clipboard-list text-primary me-2"></i>{% trans "Import Results" %}
|
|
</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="row g-3 mb-4">
|
|
<div class="col-md-4">
|
|
<div class="border rounded p-3 text-center">
|
|
<h3 class="text-primary mb-0">{{ results.total }}</h3>
|
|
<small class="text-muted">{% trans "Total Processed" %}</small>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<div class="border rounded p-3 text-center bg-success bg-opacity-10">
|
|
<h3 class="text-success mb-0">{{ results.success|length }}</h3>
|
|
<small class="text-success">{% trans "Successfully Invited" %}</small>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<div class="border rounded p-3 text-center {% if results.errors %}bg-danger bg-opacity-10{% endif %}">
|
|
<h3 class="{% if results.errors %}text-danger{% else %}text-muted{% endif %} mb-0">{{ results.errors|length }}</h3>
|
|
<small class="{% if results.errors %}text-danger{% else %}text-muted{% endif %}">{% trans "Failed" %}</small>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{% if results.success %}
|
|
<h6 class="fw-semibold text-success mb-2">
|
|
<i class="fas fa-check-circle me-1"></i>{% trans "Successfully Invited" %} ({{ results.success|length }})
|
|
</h6>
|
|
<div class="table-responsive mb-4">
|
|
<table class="table table-sm table-hover">
|
|
<thead class="table-light">
|
|
<tr>
|
|
<th>{% trans "Name" %}</th>
|
|
<th>{% trans "Email" %}</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for user in results.success %}
|
|
<tr>
|
|
<td>{{ user.name }}</td>
|
|
<td><code>{{ user.email }}</code></td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
{% endif %}
|
|
|
|
{% if results.errors %}
|
|
<h6 class="fw-semibold text-danger mb-2">
|
|
<i class="fas fa-exclamation-circle me-1"></i>{% trans "Failed Imports" %} ({{ results.errors|length }})
|
|
</h6>
|
|
<div class="table-responsive">
|
|
<table class="table table-sm table-hover">
|
|
<thead class="table-light">
|
|
<tr>
|
|
<th>{% trans "Row" %}</th>
|
|
<th>{% trans "Email" %}</th>
|
|
<th>{% trans "Error" %}</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for error in results.errors %}
|
|
<tr class="table-danger">
|
|
<td>{{ error.row }}</td>
|
|
<td><code>{{ error.email }}</code></td>
|
|
<td>{{ error.error }}</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<!-- Sidebar Info -->
|
|
<div class="col-lg-4">
|
|
<div class="card mb-4">
|
|
<div class="card-header bg-white">
|
|
<h6 class="card-title mb-0">
|
|
<i class="fas fa-info-circle text-info me-2"></i>{% trans "Available Roles" %}
|
|
</h6>
|
|
</div>
|
|
<div class="card-body">
|
|
<ul class="list-group list-group-flush">
|
|
{% for role in roles %}
|
|
<li class="list-group-item d-flex justify-content-between align-items-center px-0">
|
|
{{ role.name }}
|
|
<code class="small">{{ role.name }}</code>
|
|
</li>
|
|
{% endfor %}
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="card mb-4">
|
|
<div class="card-header bg-white">
|
|
<h6 class="card-title mb-0">
|
|
<i class="fas fa-hospital text-primary me-2"></i>{% trans "Hospitals" %}
|
|
</h6>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="small text-muted mb-2">{% trans "Use these IDs in your CSV:" %}</div>
|
|
<ul class="list-group list-group-flush">
|
|
{% for hospital in hospitals %}
|
|
<li class="list-group-item px-0 py-2">
|
|
<div class="d-flex justify-content-between">
|
|
<span class="small">{{ hospital.name|truncatechars:25 }}</span>
|
|
<code class="small text-muted" style="font-size: 0.7rem;">{{ hospital.id }}</code>
|
|
</div>
|
|
</li>
|
|
{% endfor %}
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="card">
|
|
<div class="card-header bg-white">
|
|
<h6 class="card-title mb-0">
|
|
<i class="fas fa-lightbulb text-warning me-2"></i>{% trans "Tips" %}
|
|
</h6>
|
|
</div>
|
|
<div class="card-body">
|
|
<ul class="list-unstyled small mb-0">
|
|
<li class="mb-2">
|
|
<i class="fas fa-check text-success me-2"></i>
|
|
{% trans "Verify email addresses before uploading" %}
|
|
</li>
|
|
<li class="mb-2">
|
|
<i class="fas fa-check text-success me-2"></i>
|
|
{% trans "Maximum 500 users per CSV file" %}
|
|
</li>
|
|
<li class="mb-2">
|
|
<i class="fas fa-check text-success me-2"></i>
|
|
{% trans "Duplicate emails will be skipped" %}
|
|
</li>
|
|
<li class="mb-2">
|
|
<i class="fas fa-check text-success me-2"></i>
|
|
{% trans "Invitation emails are sent automatically" %}
|
|
</li>
|
|
<li>
|
|
<i class="fas fa-check text-success me-2"></i>
|
|
{% trans "Users have 7 days to complete onboarding" %}
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endblock %}
|