792 lines
39 KiB
HTML
792 lines
39 KiB
HTML
{% extends 'base.html' %}
|
|
{% load static %}
|
|
|
|
{% block title %}{% if dashboard %}Edit Dashboard{% else %}Create Dashboard{% endif %} - Hospital Management{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="content">
|
|
<div class="container-fluid">
|
|
<!-- Page Header -->
|
|
<div class="row">
|
|
<div class="col-12">
|
|
<div class="page-header">
|
|
<div class="page-title">
|
|
<h4>{% if dashboard %}Edit Dashboard{% else %}Create New Dashboard{% endif %}</h4>
|
|
<h6>{% if dashboard %}Update dashboard configuration and settings{% else %}Set up a new analytics dashboard{% endif %}</h6>
|
|
</div>
|
|
<div class="page-btn">
|
|
<a href="{% url 'analytics:dashboard_list' %}" class="btn btn-secondary">
|
|
<i class="fas fa-arrow-left me-1"></i>Back to Dashboards
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Dashboard Form -->
|
|
<form method="post" id="dashboardForm">
|
|
{% csrf_token %}
|
|
<div class="row">
|
|
<!-- Basic Information -->
|
|
<div class="col-lg-8">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h5 class="card-title">
|
|
<i class="fas fa-info-circle me-2"></i>Basic Information
|
|
</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="row">
|
|
<div class="col-md-6">
|
|
<div class="form-group">
|
|
<label for="name" class="form-label">Dashboard Name <span class="text-danger">*</span></label>
|
|
<input type="text" class="form-control" id="name" name="name"
|
|
value="{{ dashboard.name|default:'' }}"
|
|
placeholder="Enter dashboard name" required>
|
|
<div class="form-text">Choose a descriptive name for your dashboard</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<div class="form-group">
|
|
<label for="dashboard_type" class="form-label">Dashboard Type <span class="text-danger">*</span></label>
|
|
<select class="form-select" id="dashboard_type" name="dashboard_type" required>
|
|
<option value="">Select dashboard type</option>
|
|
<option value="EXECUTIVE" {% if dashboard.dashboard_type == 'EXECUTIVE' %}selected{% endif %}>Executive Dashboard</option>
|
|
<option value="CLINICAL" {% if dashboard.dashboard_type == 'CLINICAL' %}selected{% endif %}>Clinical Dashboard</option>
|
|
<option value="OPERATIONAL" {% if dashboard.dashboard_type == 'OPERATIONAL' %}selected{% endif %}>Operational Dashboard</option>
|
|
<option value="FINANCIAL" {% if dashboard.dashboard_type == 'FINANCIAL' %}selected{% endif %}>Financial Dashboard</option>
|
|
<option value="QUALITY" {% if dashboard.dashboard_type == 'QUALITY' %}selected{% endif %}>Quality Dashboard</option>
|
|
<option value="PATIENT" {% if dashboard.dashboard_type == 'PATIENT' %}selected{% endif %}>Patient Dashboard</option>
|
|
<option value="PROVIDER" {% if dashboard.dashboard_type == 'PROVIDER' %}selected{% endif %}>Provider Dashboard</option>
|
|
<option value="DEPARTMENT" {% if dashboard.dashboard_type == 'DEPARTMENT' %}selected{% endif %}>Department Dashboard</option>
|
|
<option value="CUSTOM" {% if dashboard.dashboard_type == 'CUSTOM' %}selected{% endif %}>Custom Dashboard</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label for="description" class="form-label">Description</label>
|
|
<textarea class="form-control" id="description" name="description" rows="3"
|
|
placeholder="Describe the purpose and content of this dashboard">{{ dashboard.description|default:'' }}</textarea>
|
|
<div class="form-text">Provide a brief description of what this dashboard displays</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Layout Configuration -->
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h5 class="card-title">
|
|
<i class="fas fa-th-large me-2"></i>Layout Configuration
|
|
</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="row">
|
|
<div class="col-md-6">
|
|
<div class="form-group">
|
|
<label for="refresh_interval" class="form-label">Refresh Interval (seconds)</label>
|
|
<select class="form-select" id="refresh_interval" name="refresh_interval">
|
|
<option value="60" {% if dashboard.refresh_interval == 60 %}selected{% endif %}>1 minute</option>
|
|
<option value="300" {% if dashboard.refresh_interval == 300 or not dashboard %}selected{% endif %}>5 minutes</option>
|
|
<option value="600" {% if dashboard.refresh_interval == 600 %}selected{% endif %}>10 minutes</option>
|
|
<option value="900" {% if dashboard.refresh_interval == 900 %}selected{% endif %}>15 minutes</option>
|
|
<option value="1800" {% if dashboard.refresh_interval == 1800 %}selected{% endif %}>30 minutes</option>
|
|
<option value="3600" {% if dashboard.refresh_interval == 3600 %}selected{% endif %}>1 hour</option>
|
|
</select>
|
|
<div class="form-text">How often should the dashboard data refresh automatically</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<div class="form-group">
|
|
<label for="grid_columns" class="form-label">Grid Columns</label>
|
|
<select class="form-select" id="grid_columns" name="grid_columns">
|
|
<option value="12" selected>12 columns (default)</option>
|
|
<option value="8">8 columns</option>
|
|
<option value="6">6 columns</option>
|
|
<option value="4">4 columns</option>
|
|
</select>
|
|
<div class="form-text">Number of columns in the dashboard grid</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label class="form-label">Layout Options</label>
|
|
<div class="layout-options">
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="checkbox" id="auto_arrange" name="auto_arrange"
|
|
{% if dashboard.layout_config.auto_arrange %}checked{% endif %}>
|
|
<label class="form-check-label" for="auto_arrange">
|
|
Auto-arrange widgets
|
|
</label>
|
|
</div>
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="checkbox" id="responsive_layout" name="responsive_layout"
|
|
{% if dashboard.layout_config.responsive_layout or not dashboard %}checked{% endif %}>
|
|
<label class="form-check-label" for="responsive_layout">
|
|
Responsive layout (mobile-friendly)
|
|
</label>
|
|
</div>
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="checkbox" id="compact_mode" name="compact_mode"
|
|
{% if dashboard.layout_config.compact_mode %}checked{% endif %}>
|
|
<label class="form-check-label" for="compact_mode">
|
|
Compact mode (smaller widgets)
|
|
</label>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Theme Selection -->
|
|
<div class="form-group">
|
|
<label class="form-label">Dashboard Theme</label>
|
|
<div class="theme-selection">
|
|
<div class="row">
|
|
<div class="col-md-4">
|
|
<div class="theme-option" data-theme="light">
|
|
<div class="theme-preview light-theme">
|
|
<div class="theme-header"></div>
|
|
<div class="theme-content">
|
|
<div class="theme-widget"></div>
|
|
<div class="theme-widget"></div>
|
|
</div>
|
|
</div>
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="radio" name="theme" id="theme_light" value="light"
|
|
{% if dashboard.layout_config.theme == 'light' or not dashboard %}checked{% endif %}>
|
|
<label class="form-check-label" for="theme_light">Light Theme</label>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<div class="theme-option" data-theme="dark">
|
|
<div class="theme-preview dark-theme">
|
|
<div class="theme-header"></div>
|
|
<div class="theme-content">
|
|
<div class="theme-widget"></div>
|
|
<div class="theme-widget"></div>
|
|
</div>
|
|
</div>
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="radio" name="theme" id="theme_dark" value="dark"
|
|
{% if dashboard.layout_config.theme == 'dark' %}checked{% endif %}>
|
|
<label class="form-check-label" for="theme_dark">Dark Theme</label>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<div class="theme-option" data-theme="auto">
|
|
<div class="theme-preview auto-theme">
|
|
<div class="theme-header"></div>
|
|
<div class="theme-content">
|
|
<div class="theme-widget"></div>
|
|
<div class="theme-widget"></div>
|
|
</div>
|
|
</div>
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="radio" name="theme" id="theme_auto" value="auto"
|
|
{% if dashboard.layout_config.theme == 'auto' %}checked{% endif %}>
|
|
<label class="form-check-label" for="theme_auto">Auto (System)</label>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Data Sources -->
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h5 class="card-title">
|
|
<i class="fas fa-database me-2"></i>Data Sources
|
|
</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="form-group">
|
|
<label class="form-label">Available Data Sources</label>
|
|
<div class="data-sources-list">
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="checkbox" id="ds_patient_data" name="data_sources" value="patient_data" checked>
|
|
<label class="form-check-label" for="ds_patient_data">
|
|
<div class="data-source-item">
|
|
<div class="ds-icon">
|
|
<i class="fas fa-users text-primary"></i>
|
|
</div>
|
|
<div class="ds-info">
|
|
<h6>Patient Data</h6>
|
|
<small class="text-muted">Demographics, visits, outcomes</small>
|
|
</div>
|
|
<div class="ds-status">
|
|
<span class="badge bg-success">Active</span>
|
|
</div>
|
|
</div>
|
|
</label>
|
|
</div>
|
|
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="checkbox" id="ds_financial_data" name="data_sources" value="financial_data" checked>
|
|
<label class="form-check-label" for="ds_financial_data">
|
|
<div class="data-source-item">
|
|
<div class="ds-icon">
|
|
<i class="fas fa-dollar-sign text-success"></i>
|
|
</div>
|
|
<div class="ds-info">
|
|
<h6>Financial Data</h6>
|
|
<small class="text-muted">Revenue, billing, costs</small>
|
|
</div>
|
|
<div class="ds-status">
|
|
<span class="badge bg-success">Active</span>
|
|
</div>
|
|
</div>
|
|
</label>
|
|
</div>
|
|
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="checkbox" id="ds_clinical_data" name="data_sources" value="clinical_data">
|
|
<label class="form-check-label" for="ds_clinical_data">
|
|
<div class="data-source-item">
|
|
<div class="ds-icon">
|
|
<i class="fas fa-heartbeat text-danger"></i>
|
|
</div>
|
|
<div class="ds-info">
|
|
<h6>Clinical Data</h6>
|
|
<small class="text-muted">Lab results, procedures, medications</small>
|
|
</div>
|
|
<div class="ds-status">
|
|
<span class="badge bg-success">Active</span>
|
|
</div>
|
|
</div>
|
|
</label>
|
|
</div>
|
|
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="checkbox" id="ds_operational_data" name="data_sources" value="operational_data">
|
|
<label class="form-check-label" for="ds_operational_data">
|
|
<div class="data-source-item">
|
|
<div class="ds-icon">
|
|
<i class="fas fa-cogs text-secondary"></i>
|
|
</div>
|
|
<div class="ds-info">
|
|
<h6>Operational Data</h6>
|
|
<small class="text-muted">Staffing, capacity, workflows</small>
|
|
</div>
|
|
<div class="ds-status">
|
|
<span class="badge bg-warning">Limited</span>
|
|
</div>
|
|
</div>
|
|
</label>
|
|
</div>
|
|
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="checkbox" id="ds_quality_data" name="data_sources" value="quality_data">
|
|
<label class="form-check-label" for="ds_quality_data">
|
|
<div class="data-source-item">
|
|
<div class="ds-icon">
|
|
<i class="fas fa-star text-warning"></i>
|
|
</div>
|
|
<div class="ds-info">
|
|
<h6>Quality Data</h6>
|
|
<small class="text-muted">Metrics, incidents, compliance</small>
|
|
</div>
|
|
<div class="ds-status">
|
|
<span class="badge bg-success">Active</span>
|
|
</div>
|
|
</div>
|
|
</label>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="alert alert-info">
|
|
<i class="fas fa-info-circle me-2"></i>
|
|
<strong>Note:</strong> Selected data sources will be available for widgets in this dashboard. You can modify this selection later.
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Sidebar Settings -->
|
|
<div class="col-lg-4">
|
|
<!-- Access Control -->
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h5 class="card-title">
|
|
<i class="fas fa-lock me-2"></i>Access Control
|
|
</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="form-group">
|
|
<label class="form-label">Visibility</label>
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="radio" name="is_public" id="visibility_private" value="false"
|
|
{% if not dashboard.is_public or not dashboard %}checked{% endif %}>
|
|
<label class="form-check-label" for="visibility_private">
|
|
<i class="fas fa-lock me-2"></i>Private
|
|
<small class="d-block text-muted">Only you can access this dashboard</small>
|
|
</label>
|
|
</div>
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="radio" name="is_public" id="visibility_shared" value="shared"
|
|
{% if dashboard.is_public %}checked{% endif %}>
|
|
<label class="form-check-label" for="visibility_shared">
|
|
<i class="fas fa-users me-2"></i>Shared
|
|
<small class="d-block text-muted">Share with specific users or roles</small>
|
|
</label>
|
|
</div>
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="radio" name="is_public" id="visibility_public" value="true">
|
|
<label class="form-check-label" for="visibility_public">
|
|
<i class="fas fa-globe me-2"></i>Public
|
|
<small class="d-block text-muted">All users can access this dashboard</small>
|
|
</label>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="form-group" id="sharedUsersGroup" style="display: none;">
|
|
<label for="allowed_users" class="form-label">Shared Users</label>
|
|
<select class="form-select" id="allowed_users" name="allowed_users" multiple>
|
|
<option value="1">Dr. Sarah Johnson</option>
|
|
<option value="2">Dr. Michael Chen</option>
|
|
<option value="3">Nurse Manager Lisa Davis</option>
|
|
<option value="4">Admin John Smith</option>
|
|
<option value="5">Dr. Emily Rodriguez</option>
|
|
</select>
|
|
<div class="form-text">Select users who can access this dashboard</div>
|
|
</div>
|
|
|
|
<div class="form-group" id="sharedRolesGroup" style="display: none;">
|
|
<label for="allowed_roles" class="form-label">Shared Roles</label>
|
|
<div class="role-checkboxes">
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="checkbox" id="role_admin" name="allowed_roles" value="admin">
|
|
<label class="form-check-label" for="role_admin">Administrators</label>
|
|
</div>
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="checkbox" id="role_doctor" name="allowed_roles" value="doctor">
|
|
<label class="form-check-label" for="role_doctor">Doctors</label>
|
|
</div>
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="checkbox" id="role_nurse" name="allowed_roles" value="nurse">
|
|
<label class="form-check-label" for="role_nurse">Nurses</label>
|
|
</div>
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="checkbox" id="role_manager" name="allowed_roles" value="manager">
|
|
<label class="form-check-label" for="role_manager">Managers</label>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Dashboard Status -->
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h5 class="card-title">
|
|
<i class="fas fa-toggle-on me-2"></i>Dashboard Status
|
|
</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="form-group">
|
|
<div class="form-check form-switch">
|
|
<input class="form-check-input" type="checkbox" id="is_active" name="is_active"
|
|
{% if dashboard.is_active or not dashboard %}checked{% endif %}>
|
|
<label class="form-check-label" for="is_active">
|
|
Active Dashboard
|
|
</label>
|
|
</div>
|
|
<div class="form-text">Inactive dashboards are hidden from users</div>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<div class="form-check form-switch">
|
|
<input class="form-check-input" type="checkbox" id="is_default" name="is_default"
|
|
{% if dashboard.is_default %}checked{% endif %}>
|
|
<label class="form-check-label" for="is_default">
|
|
Default Dashboard
|
|
</label>
|
|
</div>
|
|
<div class="form-text">Users will see this dashboard by default</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Preview -->
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h5 class="card-title">
|
|
<i class="fas fa-eye me-2"></i>Preview
|
|
</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="dashboard-preview">
|
|
<div class="preview-header">
|
|
<h6 id="previewTitle">Dashboard Name</h6>
|
|
<span class="badge bg-primary" id="previewType">Executive</span>
|
|
</div>
|
|
<div class="preview-grid">
|
|
<div class="preview-widget"></div>
|
|
<div class="preview-widget"></div>
|
|
<div class="preview-widget"></div>
|
|
<div class="preview-widget"></div>
|
|
</div>
|
|
<div class="preview-footer">
|
|
<small class="text-muted">
|
|
<i class="fas fa-clock me-1"></i>
|
|
<span id="previewRefresh">5 min refresh</span>
|
|
</small>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Form Actions -->
|
|
<div class="card">
|
|
<div class="card-body">
|
|
<div class="d-grid gap-2">
|
|
<button type="submit" class="btn btn-primary">
|
|
<i class="fas fa-save me-1"></i>
|
|
{% if dashboard %}Update Dashboard{% else %}Create Dashboard{% endif %}
|
|
</button>
|
|
<button type="button" class="btn btn-outline-secondary" onclick="previewDashboard()">
|
|
<i class="fas fa-eye me-1"></i>Preview Dashboard
|
|
</button>
|
|
<a href="{% url 'analytics:dashboard_list' %}" class="btn btn-outline-danger">
|
|
<i class="fas fa-times me-1"></i>Cancel
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
// Initialize form
|
|
initializeForm();
|
|
|
|
// Update preview when form changes
|
|
const formInputs = document.querySelectorAll('#dashboardForm input, #dashboardForm select, #dashboardForm textarea');
|
|
formInputs.forEach(input => {
|
|
input.addEventListener('change', updatePreview);
|
|
input.addEventListener('input', updatePreview);
|
|
});
|
|
|
|
// Handle visibility changes
|
|
const visibilityRadios = document.querySelectorAll('input[name="is_public"]');
|
|
visibilityRadios.forEach(radio => {
|
|
radio.addEventListener('change', handleVisibilityChange);
|
|
});
|
|
|
|
// Initial preview update
|
|
updatePreview();
|
|
});
|
|
|
|
function initializeForm() {
|
|
// Initialize select2 for multi-select
|
|
if (typeof $ !== 'undefined' && $.fn.select2) {
|
|
$('#allowed_users').select2({
|
|
placeholder: 'Select users...',
|
|
allowClear: true
|
|
});
|
|
}
|
|
|
|
// Handle visibility on load
|
|
handleVisibilityChange();
|
|
}
|
|
|
|
function updatePreview() {
|
|
const name = document.getElementById('name').value || 'Dashboard Name';
|
|
const type = document.getElementById('dashboard_type').value || 'EXECUTIVE';
|
|
const refreshInterval = document.getElementById('refresh_interval').value || '300';
|
|
|
|
// Update preview title
|
|
document.getElementById('previewTitle').textContent = name;
|
|
|
|
// Update preview type
|
|
const typeDisplay = document.querySelector(`option[value="${type}"]`)?.textContent || 'Executive';
|
|
document.getElementById('previewType').textContent = typeDisplay.replace(' Dashboard', '');
|
|
|
|
// Update preview refresh
|
|
const refreshMinutes = Math.floor(refreshInterval / 60);
|
|
document.getElementById('previewRefresh').textContent = `${refreshMinutes} min refresh`;
|
|
|
|
// Update type badge color
|
|
const typeBadge = document.getElementById('previewType');
|
|
typeBadge.className = 'badge ' + getTypeColor(type);
|
|
}
|
|
|
|
function getTypeColor(type) {
|
|
const colors = {
|
|
'EXECUTIVE': 'bg-primary',
|
|
'CLINICAL': 'bg-success',
|
|
'OPERATIONAL': 'bg-secondary',
|
|
'FINANCIAL': 'bg-warning',
|
|
'QUALITY': 'bg-info',
|
|
'PATIENT': 'bg-danger',
|
|
'PROVIDER': 'bg-dark',
|
|
'DEPARTMENT': 'bg-light text-dark',
|
|
'CUSTOM': 'bg-purple'
|
|
};
|
|
return colors[type] || 'bg-primary';
|
|
}
|
|
|
|
function handleVisibilityChange() {
|
|
const selectedVisibility = document.querySelector('input[name="is_public"]:checked')?.value;
|
|
const sharedUsersGroup = document.getElementById('sharedUsersGroup');
|
|
const sharedRolesGroup = document.getElementById('sharedRolesGroup');
|
|
|
|
if (selectedVisibility === 'shared') {
|
|
sharedUsersGroup.style.display = 'block';
|
|
sharedRolesGroup.style.display = 'block';
|
|
} else {
|
|
sharedUsersGroup.style.display = 'none';
|
|
sharedRolesGroup.style.display = 'none';
|
|
}
|
|
}
|
|
|
|
function previewDashboard() {
|
|
// Collect form data
|
|
const formData = new FormData(document.getElementById('dashboardForm'));
|
|
const dashboardData = Object.fromEntries(formData.entries());
|
|
|
|
// Open preview in new window/modal
|
|
alert('Opening dashboard preview...\n\nDashboard: ' + dashboardData.name + '\nType: ' + dashboardData.dashboard_type);
|
|
}
|
|
|
|
// Form validation
|
|
document.getElementById('dashboardForm').addEventListener('submit', function(e) {
|
|
const name = document.getElementById('name').value.trim();
|
|
const type = document.getElementById('dashboard_type').value;
|
|
|
|
if (!name) {
|
|
e.preventDefault();
|
|
alert('Please enter a dashboard name');
|
|
document.getElementById('name').focus();
|
|
return;
|
|
}
|
|
|
|
if (!type) {
|
|
e.preventDefault();
|
|
alert('Please select a dashboard type');
|
|
document.getElementById('dashboard_type').focus();
|
|
return;
|
|
}
|
|
|
|
// Show loading state
|
|
const submitBtn = document.querySelector('button[type="submit"]');
|
|
const originalText = submitBtn.innerHTML;
|
|
submitBtn.innerHTML = '<i class="fas fa-spinner fa-spin me-1"></i>Saving...';
|
|
submitBtn.disabled = true;
|
|
|
|
// Re-enable after a delay (in real app, this would be handled by form submission)
|
|
setTimeout(() => {
|
|
submitBtn.innerHTML = originalText;
|
|
submitBtn.disabled = false;
|
|
}, 2000);
|
|
});
|
|
</script>
|
|
|
|
<style>
|
|
.layout-options .form-check {
|
|
margin-bottom: 10px;
|
|
}
|
|
|
|
.theme-selection {
|
|
margin-top: 10px;
|
|
}
|
|
|
|
.theme-option {
|
|
text-align: center;
|
|
margin-bottom: 15px;
|
|
}
|
|
|
|
.theme-preview {
|
|
width: 100%;
|
|
height: 80px;
|
|
border-radius: 8px;
|
|
margin-bottom: 10px;
|
|
position: relative;
|
|
overflow: hidden;
|
|
border: 2px solid transparent;
|
|
cursor: pointer;
|
|
transition: all 0.3s ease;
|
|
}
|
|
|
|
.theme-preview:hover {
|
|
transform: translateY(-2px);
|
|
box-shadow: 0 4px 15px rgba(0,0,0,0.1);
|
|
}
|
|
|
|
.theme-option input[type="radio"]:checked + label .theme-preview {
|
|
border-color: #007bff;
|
|
}
|
|
|
|
.light-theme {
|
|
background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
|
|
}
|
|
|
|
.dark-theme {
|
|
background: linear-gradient(135deg, #343a40 0%, #212529 100%);
|
|
}
|
|
|
|
.auto-theme {
|
|
background: linear-gradient(135deg, #f8f9fa 0%, #343a40 50%, #212529 100%);
|
|
}
|
|
|
|
.theme-header {
|
|
height: 15px;
|
|
background: rgba(0,0,0,0.1);
|
|
margin-bottom: 5px;
|
|
}
|
|
|
|
.theme-content {
|
|
display: flex;
|
|
gap: 5px;
|
|
padding: 0 10px;
|
|
}
|
|
|
|
.theme-widget {
|
|
flex: 1;
|
|
height: 40px;
|
|
background: rgba(0,0,0,0.05);
|
|
border-radius: 4px;
|
|
}
|
|
|
|
.dark-theme .theme-header,
|
|
.dark-theme .theme-widget {
|
|
background: rgba(255,255,255,0.1);
|
|
}
|
|
|
|
.data-sources-list .form-check {
|
|
margin-bottom: 15px;
|
|
}
|
|
|
|
.data-source-item {
|
|
display: flex;
|
|
align-items: center;
|
|
padding: 15px;
|
|
border: 1px solid #e9ecef;
|
|
border-radius: 8px;
|
|
background: #f8f9fa;
|
|
transition: all 0.3s ease;
|
|
}
|
|
|
|
.data-source-item:hover {
|
|
background: #e9ecef;
|
|
}
|
|
|
|
.form-check-input:checked + .form-check-label .data-source-item {
|
|
background: #e7f3ff;
|
|
border-color: #007bff;
|
|
}
|
|
|
|
.ds-icon {
|
|
width: 40px;
|
|
height: 40px;
|
|
border-radius: 50%;
|
|
background: white;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
margin-right: 15px;
|
|
font-size: 1.2rem;
|
|
}
|
|
|
|
.ds-info {
|
|
flex: 1;
|
|
}
|
|
|
|
.ds-info h6 {
|
|
margin: 0;
|
|
font-weight: 600;
|
|
color: #2c3e50;
|
|
}
|
|
|
|
.ds-status {
|
|
margin-left: 15px;
|
|
}
|
|
|
|
.role-checkboxes .form-check {
|
|
margin-bottom: 8px;
|
|
}
|
|
|
|
.dashboard-preview {
|
|
border: 1px solid #e9ecef;
|
|
border-radius: 8px;
|
|
padding: 15px;
|
|
background: #f8f9fa;
|
|
}
|
|
|
|
.preview-header {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
margin-bottom: 15px;
|
|
padding-bottom: 10px;
|
|
border-bottom: 1px solid #dee2e6;
|
|
}
|
|
|
|
.preview-header h6 {
|
|
margin: 0;
|
|
font-weight: 600;
|
|
color: #2c3e50;
|
|
}
|
|
|
|
.preview-grid {
|
|
display: grid;
|
|
grid-template-columns: 1fr 1fr;
|
|
gap: 8px;
|
|
margin-bottom: 15px;
|
|
}
|
|
|
|
.preview-widget {
|
|
height: 30px;
|
|
background: white;
|
|
border-radius: 4px;
|
|
border: 1px solid #dee2e6;
|
|
}
|
|
|
|
.preview-footer {
|
|
text-align: center;
|
|
padding-top: 10px;
|
|
border-top: 1px solid #dee2e6;
|
|
}
|
|
|
|
@media (max-width: 768px) {
|
|
.theme-selection .row {
|
|
flex-direction: column;
|
|
}
|
|
|
|
.theme-option {
|
|
margin-bottom: 20px;
|
|
}
|
|
|
|
.data-source-item {
|
|
flex-direction: column;
|
|
text-align: center;
|
|
}
|
|
|
|
.ds-icon {
|
|
margin-right: 0;
|
|
margin-bottom: 10px;
|
|
}
|
|
|
|
.ds-status {
|
|
margin-left: 0;
|
|
margin-top: 10px;
|
|
}
|
|
|
|
.preview-grid {
|
|
grid-template-columns: 1fr;
|
|
}
|
|
}
|
|
</style>
|
|
{% endblock %}
|
|
|