update-css

This commit is contained in:
Marwan Alwali 2026-01-06 17:33:52 +03:00
parent cfe83f50e0
commit 4ed30c94c8
17 changed files with 2380 additions and 164 deletions

View File

@ -44,12 +44,19 @@ class OnboardingService:
user.set_unusable_password()
user.save()
# Log creation
# Log creation (only store simple data, not objects)
log_metadata = {
'email': user.email,
'first_name': user.first_name,
'last_name': user.last_name,
'hospital_id': str(user.hospital_id) if user.hospital_id else None,
'department_id': str(user.department_id) if user.department_id else None,
}
UserProvisionalLog.objects.create(
user=user,
event_type='created',
description=f"Provisional user created",
metadata=user_data
metadata=log_metadata
)
return user

View File

@ -196,6 +196,9 @@ def provisional_user_list(request):
user_data = serializer.validated_data.copy()
roles = request.POST.getlist('roles', [])
# Remove roles from user_data (not a User model field)
user_data.pop('roles', None)
# Create provisional user
user = OnboardingService.create_provisional_user(user_data)
@ -221,14 +224,29 @@ def provisional_user_list(request):
is_provisional=True
).select_related('hospital', 'department').order_by('-created_at')
# Calculate statistics
total_count = provisional_users.count()
completed_count = provisional_users.filter(acknowledgement_completed_at__isnull=False).count()
in_progress_count = total_count - completed_count
# Get available roles
from .models import Role
roles = Role.objects.all()
# Get hospitals and departments for the form
from apps.organizations.models import Hospital, Department
hospitals = Hospital.objects.filter(status='active').order_by('name')
departments = Department.objects.filter(status='active').order_by('name')
context = {
'page_title': 'Provisional Users',
'provisional_users': provisional_users,
'roles': roles,
'hospitals': hospitals,
'departments': departments,
'total_count': total_count,
'completed_count': completed_count,
'in_progress_count': in_progress_count,
}
return render(request, 'accounts/onboarding/provisional_list.html', context)
@ -253,6 +271,12 @@ def provisional_user_progress(request, user_id):
is_acknowledged=True
).select_related('checklist_item')
# Create a lookup dict: checklist_item_id -> acknowledged_at timestamp
acknowledged_timestamps = {}
for ack in acknowledged_items:
if ack.checklist_item_id:
acknowledged_timestamps[ack.checklist_item_id] = ack.acknowledged_at
# Get logs
from .models import UserProvisionalLog
logs = UserProvisionalLog.objects.filter(
@ -265,15 +289,25 @@ def provisional_user_progress(request, user_id):
checklist_item__is_required=True
).count()
progress_percentage = int((acknowledged_count / total_items) * 100) if total_items > 0 else 0
remaining_count = total_items - acknowledged_count
# Attach acknowledged_at timestamp to each checklist item
checklist_items_with_timestamps = []
for item in checklist_items:
item_dict = {
'item': item,
'acknowledged_at': acknowledged_timestamps.get(item.id),
}
checklist_items_with_timestamps.append(item_dict)
context = {
'page_title': f'Onboarding Progress - {user.email}',
'user': user,
'checklist_items': checklist_items,
'acknowledged_items': acknowledged_items,
'checklist_items': checklist_items_with_timestamps,
'logs': logs,
'total_items': total_items,
'acknowledged_count': acknowledged_count,
'remaining_count': remaining_count,
'progress_percentage': progress_percentage,
}
return render(request, 'accounts/onboarding/progress_detail.html', context)

View File

@ -48,7 +48,7 @@ urlpatterns = [
# Provisional User Management
path('onboarding/provisional/', provisional_user_list, name='provisional-user-list'),
path('onboarding/provisional/<int:user_id>/progress/', provisional_user_progress, name='provisional-user-progress'),
path('onboarding/provisional/<uuid:user_id>/progress/', provisional_user_progress, name='provisional-user-progress'),
# Acknowledgement Management
path('onboarding/content/', acknowledgement_content_list, name='acknowledgement-content-list'),

View File

@ -12,6 +12,7 @@ def sidebar_counts(request):
- Active complaints
- Pending feedback
- Open PX actions
- Provisional users (PX Admin only)
"""
if not request.user.is_authenticated:
return {}
@ -33,6 +34,12 @@ def sidebar_counts(request):
action_count = PXAction.objects.filter(
status__in=['open', 'in_progress']
).count()
# Count provisional users for PX Admin
from apps.accounts.models import User
provisional_user_count = User.objects.filter(
is_provisional=True,
acknowledgement_completed=False
).count()
elif user.hospital:
complaint_count = Complaint.objects.filter(
hospital=user.hospital,
@ -46,13 +53,16 @@ def sidebar_counts(request):
hospital=user.hospital,
status__in=['open', 'in_progress']
).count()
provisional_user_count = 0
else:
complaint_count = 0
feedback_count = 0
action_count = 0
provisional_user_count = 0
return {
'complaint_count': complaint_count,
'feedback_count': feedback_count,
'action_count': action_count,
'provisional_user_count': provisional_user_count,
}

View File

@ -154,7 +154,13 @@ LOCALE_PATHS = [
# https://docs.djangoproject.com/en/5.0/howto/static-files/
STATIC_URL = '/static/'
STATIC_ROOT = BASE_DIR / 'staticfiles'
STATICFILES_DIRS = [BASE_DIR / 'static']
STATICFILES_DIRS = [
BASE_DIR / 'static',
]
# Media files
MEDIA_URL = '/media/'
MEDIA_ROOT = BASE_DIR / 'media'
# WhiteNoise configuration
STORAGES = {
@ -166,9 +172,7 @@ STORAGES = {
},
}
# Media files
MEDIA_URL = '/media/'
MEDIA_ROOT = BASE_DIR / 'media'
# Default primary key field type
# https://docs.djangoproject.com/en/5.0/ref/settings/#default-auto-field

View File

@ -0,0 +1,314 @@
# Al Hammadi Hospital Theme - PX360 Implementation Guide
## Overview
This document describes the Al Hammadi Hospital theme implementation for the PX360 Patient Experience Management System. The theme reflects the visual identity of Al Hammadi Hospital (https://alhammadi.med.sa).
## Color Palette
### Primary Colors
| Color Name | Hex Code | CSS Variable | Usage |
|------------|----------|--------------|-------|
| Primary Teal | `#0097a7` | `--hh-primary` | Main brand color, buttons, links |
| Primary Dark | `#00838f` | `--hh-primary-dark` | Hover states, gradients |
| Primary Light | `#4dd0e1` | `--hh-primary-light` | Info badges, highlights |
| Primary Lighter | `#b2ebf2` | `--hh-primary-lighter` | Progress bar backgrounds |
| Primary BG | `rgba(0, 151, 167, 0.1)` | `--hh-primary-bg` | Soft backgrounds, hover states |
### Secondary Colors
| Color Name | Hex Code | CSS Variable | Usage |
|------------|----------|--------------|-------|
| Secondary Blue | `#1a237e` | `--hh-secondary` | Secondary buttons, accents |
| Secondary Dark | `#0d1642` | `--hh-secondary-dark` | Hover states |
| Secondary Light | `#283593` | `--hh-secondary-light` | Gradients |
### Accent Colors
| Color Name | Hex Code | CSS Variable | Usage |
|------------|----------|--------------|-------|
| Red Accent | `#c62828` | `--hh-accent` | Danger, alerts, logo crescent |
| Red Light | `#d32f2f` | `--hh-accent-light` | Gradients |
| Red Dark | `#b71c1c` | `--hh-accent-dark` | Hover states |
### Semantic Colors
| Color Name | Hex Code | CSS Variable | Usage |
|------------|----------|--------------|-------|
| Success | `#00897b` | `--hh-success` | Success states, positive indicators |
| Warning | `#f9a825` | `--hh-warning` | Warning states, caution indicators |
| Danger | `#c62828` | `--hh-danger` | Error states, critical alerts |
| Info | `#0097a7` | `--hh-info` | Information, tips |
### Text Colors
| Color Name | Hex Code | CSS Variable | Usage |
|------------|----------|--------------|-------|
| Dark Text | `#263238` | `--hh-text-dark` | Headings, primary text |
| Muted Text | `#607d8b` | `--hh-text-muted` | Secondary text, labels |
| Light Text | `#90a4ae` | `--hh-text-light` | Placeholder text |
### Background Colors
| Color Name | Hex Code | CSS Variable | Usage |
|------------|----------|--------------|-------|
| Light BG | `#f5f7fa` | `--hh-bg-light` | Page background |
| White BG | `#ffffff` | `--hh-bg-white` | Cards, panels |
| Border | `#e0e6ed` | `--hh-border` | Borders, dividers |
## Typography
### Font Families
- **English**: Open Sans (Google Fonts)
- **Arabic**: Cairo (Google Fonts)
### Font Weights
- Light: 300
- Regular: 400
- Medium: 500
- Semi-Bold: 600
- Bold: 700
## Component Styling
### Sidebar
The sidebar uses a teal gradient background with the Al Hammadi brand colors:
```css
background: linear-gradient(180deg, var(--hh-primary-dark) 0%, var(--hh-primary) 50%, var(--hh-primary-dark) 100%);
```
- Active items have a red accent border (matching the logo crescent)
- Badges use theme colors for different states
### Cards
Cards feature:
- No border (border: none)
- Subtle shadow (--hh-shadow-sm)
- Hover effect with increased shadow
- Teal gradient headers for primary cards
### Buttons
| Button Type | Background | Border | Text |
|-------------|------------|--------|------|
| Primary | Teal | Teal | White |
| Secondary | Dark Blue | Dark Blue | White |
| Danger | Red | Red | White |
| Success | Teal-Green | Teal-Green | White |
| Warning | Gold | Gold | Dark |
| Soft Primary | Light Teal BG | None | Teal |
### Tables
- Header: Light gray background with uppercase text
- Rows: Hover effect with light teal background
- Striped: Alternating very light teal
### Forms
- Focus state: Teal border with light teal shadow
- Checkboxes/Radio: Teal when checked
- Select2: Bootstrap 5 theme with teal accents
### Modals
- Header: Teal gradient background
- Close button: White (inverted)
- Footer: Light border
### Alerts
All alerts use soft backgrounds with matching text colors:
- Primary/Info: Light teal background
- Success: Light green background
- Danger: Light red background
- Warning: Light gold background
## Utility Classes
### Text Colors
- `.text-teal` - Primary teal
- `.text-teal-dark` - Dark teal
- `.text-red` - Red accent
- `.text-blue` - Secondary blue
### Background Colors
- `.bg-teal` - Primary teal
- `.bg-teal-light` - Light teal (10% opacity)
- `.bg-red` - Red accent
- `.bg-blue` - Secondary blue
### Gradients
- `.bg-gradient-teal` - Teal gradient
- `.bg-gradient-red` - Red gradient
- `.bg-gradient-blue` - Blue gradient
### Effects
- `.hover-lift` - Lift effect on hover
### Stat Card Icons
- `.stat-icon.bg-teal` - Teal icon background
- `.stat-icon.bg-red` - Red icon background
- `.stat-icon.bg-blue` - Blue icon background
- `.stat-icon.bg-green` - Green icon background
### Avatars
- `.avatar-teal` - Teal avatar
- `.avatar-red` - Red avatar
- `.avatar-blue` - Blue avatar
### Badges (Soft)
- `.badge-soft-primary` - Soft teal badge
- `.badge-soft-danger` - Soft red badge
- `.badge-soft-success` - Soft green badge
- `.badge-soft-warning` - Soft gold badge
## RTL Support
The theme includes full RTL (Right-to-Left) support for Arabic:
- Sidebar moves to right side
- Border accents switch sides
- Icon margins adjust
- Timeline reverses direction
- All spacing and alignment adapts
## Responsive Design
### Breakpoints
- Mobile: < 576px
- Tablet: 576px - 991px
- Desktop: ≥ 992px
### Mobile Behavior
- Sidebar collapses off-screen
- Topbar spans full width
- Main content removes margins
- Stat cards adjust sizing
## Usage Examples
### Creating a Stat Card
```html
<div class="card stat-card">
<div class="card-body">
<div class="stat-icon bg-teal">
<i class="bi bi-people"></i>
</div>
<p class="stat-label">Total Users</p>
<h3 class="stat-value">1,234</h3>
<p class="stat-trend up">
<i class="bi bi-arrow-up"></i> 12% from last month
</p>
</div>
</div>
```
### Creating a Teal Header Card
```html
<div class="card">
<div class="card-header bg-teal">
<h5 class="card-title text-white mb-0">Card Title</h5>
</div>
<div class="card-body">
Content here...
</div>
</div>
```
### Using Soft Buttons
```html
<button class="btn btn-soft-primary">Primary Action</button>
<button class="btn btn-soft-danger">Delete</button>
<button class="btn btn-soft-success">Approve</button>
```
### Using Soft Badges
```html
<span class="badge badge-soft-primary">Active</span>
<span class="badge badge-soft-danger">Overdue</span>
<span class="badge badge-soft-success">Completed</span>
<span class="badge badge-soft-warning">Pending</span>
```
## ApexCharts Theme Colors
When using ApexCharts, use these colors for consistency:
```javascript
const chartColors = {
primary: '#0097a7',
secondary: '#1a237e',
success: '#00897b',
danger: '#c62828',
warning: '#f9a825',
info: '#4dd0e1'
};
// Example chart options
const options = {
colors: [chartColors.primary, chartColors.success, chartColors.warning, chartColors.danger],
// ... other options
};
```
## File Structure
```
templates/
├── layouts/
│ ├── base.html # Main template with theme CSS
│ └── partials/
│ ├── sidebar.html # Sidebar navigation
│ ├── topbar.html # Top navigation bar
│ └── ...
docs/
└── ALHAMMADI_THEME_GUIDE.md # This file
```
## Browser Support
The theme supports:
- Chrome (latest)
- Firefox (latest)
- Safari (latest)
- Edge (latest)
- Mobile browsers (iOS Safari, Chrome for Android)
## Maintenance
### Updating Colors
To update the color palette, modify the CSS variables in `:root` section of `templates/layouts/base.html`.
### Adding New Components
When adding new components:
1. Use existing CSS variables for colors
2. Follow the established naming conventions
3. Include RTL support
4. Test on mobile devices
## Version History
| Version | Date | Changes |
|---------|------|---------|
| 1.0.0 | January 2026 | Initial Al Hammadi theme implementation |
---
**Theme Based On**: Al Hammadi Hospital Website (https://alhammadi.med.sa)
**Implemented For**: PX360 Patient Experience Management System
**Last Updated**: January 2026

Binary file not shown.

After

Width:  |  Height:  |  Size: 104 KiB

BIN
static/img/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

View File

@ -0,0 +1,169 @@
{% extends "layouts/base.html" %}
{% load i18n %}
{% block title %}{% trans "Acknowledgement Checklist Items" %}{% endblock %}
{% block content %}
<div class="container-fluid py-4">
<!-- Page Header -->
<div class="row mb-4">
<div class="col-12">
<div class="d-flex justify-content-between align-items-center">
<div>
<h1 class="h3 mb-2">
<i class="fas fa-tasks me-2"></i>
{% trans "Checklist Items Management" %}
</h1>
<p class="text-muted mb-0">
{% trans "Manage acknowledgement checklist items" %}
</p>
</div>
<button type="button" class="btn btn-primary">
<i class="fas fa-plus me-2"></i>
{% trans "Add Checklist Item" %}
</button>
</div>
</div>
</div>
<!-- Checklist Items List -->
<div class="card shadow-sm">
<div class="card-header bg-white py-3">
<div class="d-flex justify-content-between align-items-center">
<h5 class="mb-0">
<i class="fas fa-list me-2"></i>
{% trans "Checklist Items" %}
</h5>
<div class="input-group" style="max-width: 300px;">
<input type="text" class="form-control" id="searchInput" placeholder="{% trans 'Search items...' %}">
<button class="btn btn-outline-secondary" type="button">
<i class="fas fa-search"></i>
</button>
</div>
</div>
</div>
<div class="card-body p-0">
<div class="table-responsive">
<table class="table table-hover mb-0 align-middle" id="itemsTable">
<thead class="table-light">
<tr>
<th>{% trans "Item Text" %}</th>
<th>{% trans "Role" %}</th>
<th>{% trans "Linked Content" %}</th>
<th>{% trans "Required" %}</th>
<th>{% trans "Order" %}</th>
<th>{% trans "Status" %}</th>
<th>{% trans "Created" %}</th>
<th>{% trans "Actions" %}</th>
</tr>
</thead>
<tbody>
{% for item in checklist_items %}
<tr>
<td>
<strong>{{ item.text_en }}</strong>
{% if item.code %}
<span class="badge bg-light text-muted ms-2">{{ item.code }}</span>
{% endif %}
{% if item.description_en %}
<p class="small text-muted mb-0 mt-1">{{ item.description_en }}</p>
{% endif %}
</td>
<td>
{% if item.role %}
<span class="badge bg-info text-dark">
{{ item.get_role_display }}
</span>
{% else %}
<span class="badge bg-secondary">{% trans "All Roles" %}</span>
{% endif %}
</td>
<td>
{% if item.content %}
<span class="badge bg-light text-dark">
{{ item.content.title_en }}
</span>
{% else %}
<span class="text-muted">-</span>
{% endif %}
</td>
<td class="text-center">
{% if item.is_required %}
<span class="badge bg-danger">
<i class="fas fa-exclamation-circle me-1"></i>
{% trans "Yes" %}
</span>
{% else %}
<span class="badge bg-secondary">{% trans "No" %}</span>
{% endif %}
</td>
<td>{{ item.order }}</td>
<td>
{% if item.is_active %}
<span class="badge bg-success">
<i class="fas fa-check me-1"></i>
{% trans "Active" %}
</span>
{% else %}
<span class="badge bg-secondary">
<i class="fas fa-times me-1"></i>
{% trans "Inactive" %}
</span>
{% endif %}
</td>
<td>
<small>{{ item.created_at|date:"M d, Y" }}</small>
</td>
<td>
<div class="btn-group" role="group">
<button class="btn btn-sm btn-outline-primary" title="{% trans 'Edit' %}">
<i class="fas fa-edit"></i>
</button>
<button class="btn btn-sm btn-outline-danger" title="{% trans 'Delete' %}">
<i class="fas fa-trash"></i>
</button>
</div>
</td>
</tr>
{% empty %}
<tr>
<td colspan="8" class="text-center py-5">
<i class="fas fa-clipboard-list fa-3x text-muted mb-3"></i>
<p class="text-muted mb-0">
{% trans "No checklist items found" %}
</p>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
</div>
<script>
// Search functionality
document.getElementById('searchInput').addEventListener('keyup', function() {
const searchValue = this.value.toLowerCase();
const table = document.getElementById('itemsTable');
const rows = table.getElementsByTagName('tr');
for (let i = 1; i < rows.length; i++) {
const row = rows[i];
const cells = row.getElementsByTagName('td');
let found = false;
for (let j = 0; j < cells.length; j++) {
const cellText = cells[j].textContent.toLowerCase();
if (cellText.includes(searchValue)) {
found = true;
break;
}
}
row.style.display = found ? '' : 'none';
}
});
</script>
{% endblock %}

View File

@ -0,0 +1,153 @@
{% extends "layouts/base.html" %}
{% load i18n %}
{% block title %}{% trans "Acknowledgement Content" %}{% endblock %}
{% block content %}
<div class="container-fluid py-4">
<!-- Page Header -->
<div class="row mb-4">
<div class="col-12">
<div class="d-flex justify-content-between align-items-center">
<div>
<h1 class="h3 mb-2">
<i class="fas fa-book me-2"></i>
{% trans "Acknowledgement Content Management" %}
</h1>
<p class="text-muted mb-0">
{% trans "Manage educational content for onboarding wizard" %}
</p>
</div>
<button type="button" class="btn btn-primary">
<i class="fas fa-plus me-2"></i>
{% trans "Add Content" %}
</button>
</div>
</div>
</div>
<!-- Content List -->
<div class="card shadow-sm">
<div class="card-header bg-white py-3">
<div class="d-flex justify-content-between align-items-center">
<h5 class="mb-0">
<i class="fas fa-list me-2"></i>
{% trans "Content Sections" %}
</h5>
<div class="input-group" style="max-width: 300px;">
<input type="text" class="form-control" id="searchInput" placeholder="{% trans 'Search content...' %}">
<button class="btn btn-outline-secondary" type="button">
<i class="fas fa-search"></i>
</button>
</div>
</div>
</div>
<div class="card-body p-0">
<div class="table-responsive">
<table class="table table-hover mb-0 align-middle" id="contentTable">
<thead class="table-light">
<tr>
<th width="50">{% trans "Icon" %}</th>
<th>{% trans "Title" %}</th>
<th>{% trans "Role" %}</th>
<th>{% trans "Order" %}</th>
<th>{% trans "Status" %}</th>
<th>{% trans "Created" %}</th>
<th>{% trans "Actions" %}</th>
</tr>
</thead>
<tbody>
{% for content in content_list %}
<tr>
<td class="text-center">
{% if content.icon %}
<i class="fas {{ content.icon }} fa-lg text-{{ content.color|default:'primary' }}"></i>
{% else %}
<i class="fas fa-file-alt fa-lg text-muted"></i>
{% endif %}
</td>
<td>
<strong>{{ content.title_en }}</strong>
{% if content.code %}
<span class="badge bg-light text-muted ms-2">{{ content.code }}</span>
{% endif %}
</td>
<td>
{% if content.role %}
<span class="badge bg-info text-dark">
{{ content.get_role_display }}
</span>
{% else %}
<span class="badge bg-secondary">{% trans "All Roles" %}</span>
{% endif %}
</td>
<td>{{ content.order }}</td>
<td>
{% if content.is_active %}
<span class="badge bg-success">
<i class="fas fa-check me-1"></i>
{% trans "Active" %}
</span>
{% else %}
<span class="badge bg-secondary">
<i class="fas fa-times me-1"></i>
{% trans "Inactive" %}
</span>
{% endif %}
</td>
<td>
<small>{{ content.created_at|date:"M d, Y" }}</small>
</td>
<td>
<div class="btn-group" role="group">
<button class="btn btn-sm btn-outline-primary" title="{% trans 'Edit' %}">
<i class="fas fa-edit"></i>
</button>
<button class="btn btn-sm btn-outline-danger" title="{% trans 'Delete' %}">
<i class="fas fa-trash"></i>
</button>
</div>
</td>
</tr>
{% empty %}
<tr>
<td colspan="7" class="text-center py-5">
<i class="fas fa-folder-open fa-3x text-muted mb-3"></i>
<p class="text-muted mb-0">
{% trans "No content found" %}
</p>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
</div>
<script>
// Search functionality
document.getElementById('searchInput').addEventListener('keyup', function() {
const searchValue = this.value.toLowerCase();
const table = document.getElementById('contentTable');
const rows = table.getElementsByTagName('tr');
for (let i = 1; i < rows.length; i++) {
const row = rows[i];
const cells = row.getElementsByTagName('td');
let found = false;
for (let j = 0; j < cells.length; j++) {
const cellText = cells[j].textContent.toLowerCase();
if (cellText.includes(searchValue)) {
found = true;
break;
}
}
row.style.display = found ? '' : 'none';
}
});
</script>
{% endblock %}

View File

@ -0,0 +1,249 @@
{% extends "layouts/base.html" %}
{% load i18n %}
{% block title %}{% trans page_title %}{% endblock %}
{% block content %}
<div class="container-fluid py-4">
<!-- Page Header -->
<div class="row mb-4">
<div class="col-12">
<div class="d-flex justify-content-between align-items-center">
<div>
<a href="{% url 'accounts:provisional-user-list' %}" class="text-muted text-decoration-none mb-2 d-inline-block">
<i class="fas fa-arrow-left me-1"></i>
{% trans "Back to Provisional Users" %}
</a>
<h1 class="h3 mb-2">
<i class="fas fa-chart-line me-2"></i>
{% trans "Onboarding Progress" %}
</h1>
<p class="text-muted mb-0">{{ user.email }}</p>
</div>
</div>
</div>
</div>
<!-- Progress Overview -->
<div class="row mb-4">
<div class="col-md-6">
<div class="card mb-3">
<div class="card-body">
<h6 class="text-muted mb-3">{% trans "Overall Progress" %}</h6>
<div class="mb-3">
<div class="d-flex justify-content-between mb-2">
<span class="text-muted">{% trans "Required Items" %}</span>
<span class="fw-bold">{{ acknowledged_count }} / {{ total_items }}</span>
</div>
<div class="progress" style="height: 20px;">
<div class="progress-bar bg-success" role="progressbar"
style="width: {{ progress_percentage }}%;"
aria-valuenow="{{ progress_percentage }}"
aria-valuemin="0"
aria-valuemax="100">
{{ progress_percentage }}%
</div>
</div>
</div>
{% if progress_percentage == 100 %}
<div class="alert alert-success mb-0">
<i class="fas fa-check-circle me-2"></i>
{% trans "All required acknowledgements completed!" %}
</div>
{% else %}
<div class="alert alert-info mb-0">
<i class="fas fa-info-circle me-2"></i>
<span class="text-muted">{{ remaining_count }} {% trans "items remaining" %}</span>
</div>
{% endif %}
</div>
</div>
</div>
<div class="col-md-6">
<div class="card mb-3">
<div class="card-body">
<h6 class="text-muted mb-3">{% trans "User Details" %}</h6>
<div class="mb-2">
<strong>{% trans "Name:" %}</strong> {{ user.get_full_name }}
</div>
<div class="mb-2">
<strong>{% trans "Email:" %}</strong> {{ user.email }}
</div>
<div class="mb-2">
<strong>{% trans "Hospital:" %}</strong> {{ user.hospital.name|default:"-" }}
</div>
<div class="mb-2">
<strong>{% trans "Department:" %}</strong> {{ user.department.name|default:"-" }}
</div>
<div class="mb-2">
<strong>{% trans "Roles:" %}</strong>
{% for group in user.groups.all %}
<span class="badge bg-secondary me-1">{{ group.name }}</span>
{% empty %}
<span class="text-muted">-</span>
{% endfor %}
</div>
</div>
</div>
</div>
</div>
<!-- Checklist Items -->
<div class="row mb-4">
<div class="col-12">
<div class="card">
<div class="card-header bg-white py-3">
<h5 class="mb-0">
<i class="fas fa-tasks me-2"></i>
{% trans "Acknowledgement Checklist" %}
</h5>
</div>
<div class="card-body">
<div class="table-responsive">
<table class="table table-hover mb-0">
<thead class="table-light">
<tr>
<th width="50">{% trans "Status" %}</th>
<th>{% trans "Item" %}</th>
<th>{% trans "Role" %}</th>
<th>{% trans "Required" %}</th>
<th>{% trans "Acknowledged At" %}</th>
</tr>
</thead>
<tbody>
{% for checklist_item in checklist_items %}
<tr>
<td class="text-center">
{% if checklist_item.acknowledged_at %}
<i class="fas fa-check-circle text-success fa-lg"></i>
{% else %}
<i class="far fa-circle text-muted fa-lg"></i>
{% endif %}
</td>
<td>
<strong>{{ checklist_item.item.text_en }}</strong>
{% if checklist_item.item.description_en %}
<p class="small text-muted mb-0 mt-1">{{ checklist_item.item.description_en }}</p>
{% endif %}
</td>
<td>
<span class="badge bg-info text-dark">
{% if checklist_item.item.role %}{{ checklist_item.item.role }}{% else %}All Roles{% endif %}
</span>
</td>
<td class="text-center">
{% if checklist_item.item.is_required %}
<span class="badge bg-danger">{% trans "Yes" %}</span>
{% else %}
<span class="badge bg-secondary">{% trans "No" %}</span>
{% endif %}
</td>
<td>
{% if checklist_item.acknowledged_at %}
<small>{{ checklist_item.acknowledged_at|date:"M d, Y H:i" }}</small>
{% else %}
<span class="text-muted">-</span>
{% endif %}
</td>
</tr>
{% empty %}
<tr>
<td colspan="5" class="text-center py-4">
<p class="text-muted mb-0">
{% trans "No checklist items found for this user's role" %}
</p>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<!-- Activity Log -->
<div class="row">
<div class="col-12">
<div class="card">
<div class="card-header bg-white py-3">
<h5 class="mb-0">
<i class="fas fa-history me-2"></i>
{% trans "Activity Log" %}
</h5>
</div>
<div class="card-body">
<div class="timeline">
{% for log in logs %}
<div class="timeline-item mb-3">
<div class="d-flex">
<div class="flex-shrink-0">
<div class="timeline-marker bg-primary">
<i class="fas fa-circle"></i>
</div>
</div>
<div class="flex-grow-1 ms-3">
<div class="card">
<div class="card-body">
<div class="d-flex justify-content-between align-items-start mb-2">
<h6 class="mb-0">{{ log.get_event_type_display }}</h6>
<small class="text-muted">{{ log.created_at|date:"M d, Y H:i" }}</small>
</div>
<p class="mb-0 text-muted">{{ log.description }}</p>
{% if log.ip_address %}
<small class="text-muted">
<i class="fas fa-map-marker-alt me-1"></i>
{{ log.ip_address }}
</small>
{% endif %}
</div>
</div>
</div>
</div>
</div>
{% empty %}
<div class="text-center py-4">
<i class="fas fa-history fa-2x text-muted mb-2"></i>
<p class="text-muted mb-0">
{% trans "No activity recorded yet" %}
</p>
</div>
{% endfor %}
</div>
</div>
</div>
</div>
</div>
</div>
<style>
.timeline {
position: relative;
padding-left: 20px;
}
.timeline-item {
position: relative;
}
.timeline-marker {
width: 40px;
height: 40px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: 0.875rem;
}
</style>
{% comment %}
Template filter to get event type icon
{% endcomment %}
<script>
// Any interactive functionality can be added here
</script>
{% endblock %}

View File

@ -0,0 +1,338 @@
{% extends "layouts/base.html" %}
{% load i18n %}
{% block title %}{% trans "Provisional Users" %}{% endblock %}
{% block content %}
<div class="container-fluid py-4">
<!-- Page Header -->
<div class="row mb-4">
<div class="col-12">
<div class="d-flex justify-content-between align-items-center">
<div>
<h1 class="h3 mb-2">
<i class="fas fa-users-cog me-2"></i>
{% trans "Provisional Users Management" %}
</h1>
<p class="text-muted mb-0">
{% trans "Manage and monitor provisional user onboarding" %}
</p>
</div>
<button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#createUserModal">
<i class="fas fa-plus me-2"></i>
{% trans "Create Provisional User" %}
</button>
</div>
</div>
</div>
<!-- Statistics Cards -->
<div class="row mb-4">
<div class="col-md-4">
<div class="card border-primary mb-3">
<div class="card-body">
<div class="d-flex align-items-center">
<div class="flex-shrink-0">
<i class="fas fa-user-clock fa-2x text-primary"></i>
</div>
<div class="flex-grow-1 ms-3">
<h6 class="text-muted mb-1">{% trans "Total Provisional" %}</h6>
<h3 class="mb-0">{{ total_count }}</h3>
</div>
</div>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card border-success mb-3">
<div class="card-body">
<div class="d-flex align-items-center">
<div class="flex-shrink-0">
<i class="fas fa-check-circle fa-2x text-success"></i>
</div>
<div class="flex-grow-1 ms-3">
<h6 class="text-muted mb-1">{% trans "Completed" %}</h6>
<h3 class="mb-0">{{ completed_count }}</h3>
</div>
</div>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card border-warning mb-3">
<div class="card-body">
<div class="d-flex align-items-center">
<div class="flex-shrink-0">
<i class="fas fa-hourglass-half fa-2x text-warning"></i>
</div>
<div class="flex-grow-1 ms-3">
<h6 class="text-muted mb-1">{% trans "In Progress" %}</h6>
<h3 class="mb-0">{{ in_progress_count }}</h3>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Users List -->
<div class="card shadow-sm">
<div class="card-header bg-white py-3">
<div class="d-flex justify-content-between align-items-center">
<h5 class="mb-0">
<i class="fas fa-list me-2"></i>
{% trans "Provisional Users List" %}
</h5>
<div class="input-group" style="max-width: 300px;">
<input type="text" class="form-control" id="searchInput" placeholder="{% trans 'Search users...' %}">
<button class="btn btn-outline-secondary" type="button">
<i class="fas fa-search"></i>
</button>
</div>
</div>
</div>
<div class="card-body p-0">
<div class="table-responsive">
<table class="table table-hover mb-0 align-middle" id="usersTable">
<thead class="table-light">
<tr>
<th>{% trans "User" %}</th>
<th>{% trans "Hospital" %}</th>
<th>{% trans "Department" %}</th>
<th>{% trans "Roles" %}</th>
<th>{% trans "Status" %}</th>
<th>{% trans "Created" %}</th>
<th>{% trans "Actions" %}</th>
</tr>
</thead>
<tbody>
{% for user in provisional_users %}
<tr>
<td>
<div class="d-flex align-items-center">
<div class="flex-shrink-0">
<div class="avatar-circle bg-primary text-white">
{{ user.email|first|upper }}
</div>
</div>
<div class="flex-grow-1 ms-3">
<h6 class="mb-0">{{ user.email }}</h6>
<small class="text-muted">{{ user.first_name }} {{ user.last_name }}</small>
</div>
</div>
</td>
<td>{{ user.hospital.name|default:"-" }}</td>
<td>{{ user.department.name|default:"-" }}</td>
<td>
{% for group in user.groups.all %}
<span class="badge bg-secondary me-1">{{ group.name }}</span>
{% empty %}
<span class="text-muted">-</span>
{% endfor %}
</td>
<td>
{% if user.acknowledgement_completed_at %}
<span class="badge bg-success">
<i class="fas fa-check me-1"></i>
{% trans "Completed" %}
</span>
{% else %}
<span class="badge bg-warning">
<i class="fas fa-spinner me-1"></i>
{% trans "In Progress" %}
</span>
{% endif %}
</td>
<td>
<small>{{ user.created_at|date:"M d, Y" }}</small>
</td>
<td>
<a href="{% url 'accounts:provisional-user-progress' user.id %}"
class="btn btn-sm btn-outline-primary"
title="{% trans 'View Progress' %}">
<i class="bi bi-activity"></i>
</a>
<button class="btn btn-sm btn-outline-danger"
onclick="resendInvitation({{ user.id }})"
title="{% trans 'Resend Invitation' %}">
<i class="bi bi-send"></i>
</button>
</td>
</tr>
{% empty %}
<tr>
<td colspan="7" class="text-center py-5">
<i class="fas fa-user-slash fa-3x text-muted mb-3"></i>
<p class="text-muted mb-0">
{% trans "No provisional users found" %}
</p>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
</div>
<!-- Create User Modal -->
<div class="modal fade" id="createUserModal" tabindex="-1" aria-labelledby="createUserModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="createUserModalLabel">
<i class="fas fa-user-plus me-2"></i>
{% trans "Create Provisional User" %}
</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<form method="post">
{% csrf_token %}
<div class="modal-body">
<div class="row">
<div class="col-md-6 mb-3">
<label for="email" class="form-label">
{% trans "Email Address" %}
<span class="text-danger">*</span>
</label>
<input type="email" class="form-control" id="email" name="email" required>
</div>
<div class="col-md-6 mb-3">
<label for="phone" class="form-label">{% trans "Phone Number" %}</label>
<input type="tel" class="form-control" id="phone" name="phone">
</div>
</div>
<div class="row">
<div class="col-md-6 mb-3">
<label for="first_name" class="form-label">
{% trans "First Name" %}
<span class="text-danger">*</span>
</label>
<input type="text" class="form-control" id="first_name" name="first_name" required>
</div>
<div class="col-md-6 mb-3">
<label for="last_name" class="form-label">
{% trans "Last Name" %}
<span class="text-danger">*</span>
</label>
<input type="text" class="form-control" id="last_name" name="last_name" required>
</div>
</div>
<div class="row">
<div class="col-md-6 mb-3">
<label for="hospital" class="form-label">{% trans "Hospital" %}</label>
<select class="form-select" id="hospital" name="hospital">
<option value="">{% trans "Select Hospital" %}</option>
{% for hospital in hospitals %}
<option value="{{ hospital.id }}">{{ hospital.name }}</option>
{% endfor %}
</select>
</div>
<div class="col-md-6 mb-3">
<label for="department" class="form-label">{% trans "Department" %}</label>
<select class="form-select" id="department" name="department">
<option value="">{% trans "Select Department" %}</option>
{% for department in departments %}
<option value="{{ department.id }}">{{ department.name }}</option>
{% endfor %}
</select>
</div>
</div>
<div class="mb-3">
<label class="form-label">{% trans "Roles" %}</label>
<div class="row">
{% for role in roles %}
<div class="col-md-6 mb-2">
<div class="form-check">
<input class="form-check-input" type="checkbox"
name="roles" id="role_{{ role.id }}"
value="{{ role.name }}">
<label class="form-check-label" for="role_{{ role.id }}">
{{ role.name }}
</label>
</div>
</div>
{% endfor %}
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">
{% trans "Cancel" %}
</button>
<button type="submit" class="btn btn-primary">
<i class="fas fa-paper-plane me-2"></i>
{% trans "Create & Send Invitation" %}
</button>
</div>
</form>
</div>
</div>
</div>
<style>
.avatar-circle {
width: 40px;
height: 40px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
font-size: 1.2rem;
}
</style>
<script>
// Search functionality
document.getElementById('searchInput').addEventListener('keyup', function() {
const searchValue = this.value.toLowerCase();
const table = document.getElementById('usersTable');
const rows = table.getElementsByTagName('tr');
for (let i = 1; i < rows.length; i++) {
const row = rows[i];
const cells = row.getElementsByTagName('td');
let found = false;
for (let j = 0; j < cells.length; j++) {
const cellText = cells[j].textContent.toLowerCase();
if (cellText.includes(searchValue)) {
found = true;
break;
}
}
row.style.display = found ? '' : 'none';
}
});
function resendInvitation(userId) {
if (confirm('{% trans "Are you sure you want to resend the invitation email?" %}')) {
fetch(`/accounts/onboarding/provisional/${userId}/resend/`, {
method: 'POST',
headers: {
'X-CSRFToken': document.querySelector('[name=csrfmiddlewaretoken]').value,
'Content-Type': 'application/json',
},
})
.then(response => response.json())
.then(data => {
if (data.success) {
alert('{% trans "Invitation sent successfully!" %}');
} else {
alert('{% trans "Failed to send invitation." %}');
}
})
.catch(error => {
console.error('Error:', error);
alert('{% trans "An error occurred. Please try again." %}');
});
}
}
</script>
{% endblock %}

View File

@ -5,28 +5,37 @@
{% block extra_css %}
<style>
/* Al Hammadi Theme - Command Center Styles */
.kpi-card {
transition: transform 0.2s ease-in-out, box-shadow 0.2s ease-in-out;
border-left: 4px solid var(--hh-primary);
}
.kpi-card:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
transform: translateY(-3px);
box-shadow: var(--hh-shadow-lg);
}
.kpi-card.border-left-primary { border-left-color: var(--hh-primary); }
.kpi-card.border-left-warning { border-left-color: var(--hh-warning); }
.kpi-card.border-left-danger { border-left-color: var(--hh-accent); }
.kpi-card.border-left-success { border-left-color: var(--hh-success); }
.kpi-card.border-left-info { border-left-color: var(--hh-primary-light); }
.kpi-card.border-left-secondary { border-left-color: var(--hh-secondary); }
.kpi-value {
font-size: 2rem;
font-weight: 700;
}
.kpi-trend-up {
color: #dc3545;
color: var(--hh-accent);
}
.kpi-trend-down {
color: #198754;
color: var(--hh-success);
}
.chart-container {
min-height: 350px;
}
.filter-panel {
background: #f8f9fa;
background: var(--hh-bg-light);
border-radius: 8px;
padding: 20px;
}
@ -39,7 +48,7 @@
left: 0;
right: 0;
bottom: 0;
background: rgba(255,255,255,0.8);
background: rgba(255,255,255,0.9);
display: none;
justify-content: center;
align-items: center;
@ -48,10 +57,36 @@
.loading-overlay.active {
display: flex;
}
.loading-overlay .spinner-border {
color: var(--hh-primary);
width: 3rem;
height: 3rem;
}
.physician-row:hover {
background-color: #f8f9fa;
background-color: var(--hh-primary-bg);
cursor: pointer;
}
/* KPI Text Colors */
.text-primary { color: var(--hh-primary) !important; }
.text-warning { color: var(--hh-warning) !important; }
.text-danger { color: var(--hh-accent) !important; }
.text-success { color: var(--hh-success) !important; }
.text-info { color: var(--hh-primary-light) !important; }
.text-secondary { color: var(--hh-secondary) !important; }
/* Card Header Styling */
.card-header h6 {
color: var(--hh-text-dark);
}
/* Table Styling */
.table thead th {
background: var(--hh-bg-light);
color: var(--hh-text-dark);
font-weight: 600;
border-bottom: 2px solid var(--hh-border);
}
</style>
{% endblock %}
@ -632,6 +667,9 @@ function renderChart(elementId, chartData, chartType) {
charts[elementId].destroy();
}
// Al Hammadi Theme Colors for Charts
const hhChartColors = ['#0097a7', '#00897b', '#f9a825', '#c62828', '#1a237e', '#4dd0e1'];
const options = {
series: chartData.series || [],
chart: {
@ -639,10 +677,11 @@ function renderChart(elementId, chartData, chartType) {
height: 350,
toolbar: {
show: true
}
},
fontFamily: 'Open Sans, Cairo, sans-serif'
},
labels: chartData.labels || [],
colors: ['#4472C4', '#4BC0C0', '#FF6384', '#36A2EB', '#FFCE56', '#9966FF'],
colors: hhChartColors,
dataLabels: {
enabled: chartType === 'donut'
},

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,4 @@
{% load i18n %}
{% load i18n static%}
<div class="sidebar">
<!-- Brand -->
<div class="sidebar-brand">
@ -260,10 +260,34 @@
{% if user.is_px_admin %}
<li class="nav-item">
<a class="nav-link {% if 'config' in request.path %}active{% endif %}"
href="{% url 'config:dashboard' %}">
data-bs-toggle="collapse"
href="#settingsMenu"
role="button"
aria-expanded="{% if 'config' in request.path %}true{% else %}false{% endif %}"
aria-controls="settingsMenu">
<i class="bi bi-gear"></i>
{% trans "Configuration" %}
{% trans "Settings" %}
<i class="bi bi-chevron-down ms-auto"></i>
</a>
<div class="collapse {% if 'config' in request.path %}show{% endif %}" id="settingsMenu">
<ul class="nav flex-column ms-3">
<li class="nav-item">
<a class="nav-link {% if request.resolver_match.url_name == 'config_dashboard' %}active{% endif %}"
href="{% url 'config:dashboard' %}">
<i class="bi bi-sliders"></i>
{% trans "Configuration" %}
</a>
</li>
<li class="nav-item">
<a class="nav-link {% if 'onboarding' in request.path %}active{% endif %}"
href="{% url 'accounts:provisional-user-list' %}">
<i class="bi bi-person-plus"></i>
{% trans "Onboarding" %}
<span class="badge bg-info">{{ provisional_user_count|default:0 }}</span>
</a>
</li>
</ul>
</div>
</li>
{% endif %}
</ul>

View File

@ -1,28 +1,29 @@
{% load i18n %}
{% load i18n static%}
<div class="topbar d-flex align-items-center px-4">
<!-- Mobile Menu Toggle -->
<button class="btn btn-link d-md-none me-3" type="button" onclick="document.querySelector('.sidebar').classList.toggle('show')">
<button class="btn btn-link text-teal d-lg-none me-3" type="button" onclick="document.querySelector('.sidebar').classList.toggle('show')">
<i class="bi bi-list fs-4"></i>
</button>
<!-- Page Title -->
<div class="flex-grow-1">
<h5 class="mb-0">{% block page_title %}{% trans "Dashboard" %}{% endblock %}</h5>
<img src="{% static 'img/logo.png' %}" height="50">
{# <h5 class="mb-0 text-teal-dark">{% block page_title %}{% trans "Dashboard" %}{% endblock %}</h5>#}
</div>
<!-- Search -->
<div class="me-3 d-none d-md-block">
<div class="input-group" style="width: 300px;">
<span class="input-group-text bg-white border-end-0">
<i class="bi bi-search"></i>
<span class="input-group-text bg-white border-end-0" style="border-color: var(--hh-border);">
<i class="bi bi-search text-teal"></i>
</span>
<input type="text" class="form-control border-start-0" placeholder="{% trans 'Search...' %}">
<input type="text" class="form-control border-start-0" placeholder="{% trans 'Search...' %}" style="border-color: var(--hh-border);">
</div>
</div>
<!-- Notifications -->
<div class="dropdown me-3">
<button class="btn btn-link position-relative p-0" type="button" data-bs-toggle="dropdown" style="line-height: 1;">
<button class="btn btn-link position-relative p-0 text-teal" type="button" data-bs-toggle="dropdown" style="line-height: 1;">
<i class="bi bi-bell fs-5"></i>
{% if notification_count|default:0 > 0 %}
<span class="position-absolute top-0 start-100 translate-middle badge rounded-pill bg-danger" style="font-size: 0.65rem;">
@ -30,25 +31,44 @@
</span>
{% endif %}
</button>
<ul class="dropdown-menu dropdown-menu-end" style="width: 300px;">
<li class="dropdown-header">{% trans "Notifications" %}</li>
<ul class="dropdown-menu dropdown-menu-end" style="width: 320px;">
<li class="dropdown-header d-flex justify-content-between align-items-center">
<span class="fw-semibold">{% trans "Notifications" %}</span>
<a href="#" class="text-teal small">{% trans "View All" %}</a>
</li>
<li><hr class="dropdown-divider"></li>
<li><a class="dropdown-item" href="#">{% trans "No new notifications" %}</a></li>
<li>
<a class="dropdown-item py-2" href="#">
<div class="d-flex align-items-start">
<div class="avatar avatar-sm avatar-teal me-2">
<i class="bi bi-bell"></i>
</div>
<div class="flex-grow-1">
<p class="mb-0 small">{% trans "No new notifications" %}</p>
<small class="text-muted">{% trans "You're all caught up!" %}</small>
</div>
</div>
</a>
</li>
</ul>
</div>
<!-- Language Toggle -->
<div class="dropdown me-3">
<button class="btn btn-link" type="button" data-bs-toggle="dropdown">
<button class="btn btn-link text-teal" type="button" data-bs-toggle="dropdown">
<i class="bi bi-translate fs-5"></i>
</button>
<ul class="dropdown-menu dropdown-menu-end">
<li class="dropdown-header">{% trans "Select Language" %}</li>
<li><hr class="dropdown-divider"></li>
<li>
<form action="{% url 'set_language' %}" method="post" style="display: inline;">
{% csrf_token %}
<input name="next" type="hidden" value="{{ request.get_full_path }}">
<input type="hidden" name="language" value="en">
<button type="submit" class="dropdown-item">English</button>
<button type="submit" class="dropdown-item d-flex align-items-center">
<span class="me-2">🇺🇸</span> English
</button>
</form>
</li>
<li>
@ -56,7 +76,9 @@
{% csrf_token %}
<input name="next" type="hidden" value="{{ request.get_full_path }}">
<input type="hidden" name="language" value="ar">
<button type="submit" class="dropdown-item">العربية</button>
<button type="submit" class="dropdown-item d-flex align-items-center">
<span class="me-2">🇸🇦</span> العربية
</button>
</form>
</li>
</ul>
@ -64,21 +86,32 @@
<!-- User Menu -->
<div class="dropdown">
<button class="btn btn-link d-flex align-items-center" type="button" data-bs-toggle="dropdown">
<button class="btn btn-link d-flex align-items-center text-decoration-none" type="button" data-bs-toggle="dropdown">
<div class="me-2 text-end d-none d-md-block">
<div class="fw-semibold">{{ user.get_full_name|default:user.username }}</div>
<small class="text-muted">{{ user.get_role_names.0|default:"User" }}</small>
</div>
<div class="rounded-circle bg-primary text-white d-flex align-items-center justify-content-center"
style="width: 40px; height: 40px;">
{{ user.first_name.0|default:user.username.0|upper }}
<div class="fw-semibold" style="color: var(--hh-text-dark);">{{ user.get_full_name|default:user.username }}</div>
<small style="color: var(--hh-text-muted);">{{ user.get_role_names.0|default:"User" }}</small>
</div>
{# <div class="avatar avatar-teal">#}
{# {{ user.first_name.0|default:user.username.0|upper }}#}
{# </div>#}
</button>
<ul class="dropdown-menu dropdown-menu-end">
<li><a class="dropdown-item" href="#"><i class="bi bi-person me-2"></i>{% trans "Profile" %}</a></li>
<li><a class="dropdown-item" href="#"><i class="bi bi-gear me-2"></i>{% trans "Settings" %}</a></li>
<li class="dropdown-header">
<div class="d-flex align-items-center">
{# <div class="avatar avatar-teal me-2">#}
{# {{ user.first_name.0|default:user.username.0|upper }}#}
{# </div>#}
<div>
<div class="fw-semibold">{{ user.get_full_name|default:user.username }}</div>
<small class="text-muted">{{ user.email }}</small>
</div>
</div>
</li>
<li><hr class="dropdown-divider"></li>
<li><a class="dropdown-item" href="{% url 'admin:logout' %}"><i class="bi bi-box-arrow-right me-2"></i>{% trans "Logout" %}</a></li>
<li><a class="dropdown-item" href="#"><i class="bi bi-person me-2 text-teal"></i>{% trans "Profile" %}</a></li>
<li><a class="dropdown-item" href="#"><i class="bi bi-gear me-2 text-teal"></i>{% trans "Settings" %}</a></li>
<li><hr class="dropdown-divider"></li>
<li><a class="dropdown-item text-danger" href="{% url 'admin:logout' %}"><i class="bi bi-box-arrow-right me-2"></i>{% trans "Logout" %}</a></li>
</ul>
</div>
</div>

View File

@ -193,16 +193,16 @@
</td>
<td>
{% if entry.trend == 'up' %}
<span class="badge bg-success">
<i class="bi bi-arrow-up"></i> {% trans "Up" %}
</span>
<span class="text-success ">
<i class="bi bi-arrow-up text-success"></i> {% trans "Up" %}
{# </span>#}
{% elif entry.trend == 'down' %}
<span class="badge bg-danger">
<i class="bi bi-arrow-down"></i> {% trans "Down" %}
<span class="text-danger">
<i class="bi bi-arrow-down text-danger"></i> {% trans "Down" %}
</span>
{% else %}
<span class="badge bg-secondary">
<i class="bi bi-dash"></i> {% trans "Stable" %}
<span class="text-primary">
<i class="bi bi-dash text-secondary"></i> {% trans "Stable" %}
</span>
{% endif %}
</td>