hospital-management/templates/analytics/dashboard_widget_form.html
2025-08-12 13:33:25 +03:00

661 lines
33 KiB
HTML

{% extends 'base.html' %}
{% load static %}
{% block title %}{% if widget %}Edit Widget{% else %}Create Widget{% 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 widget %}Edit Widget{% else %}Create New Widget{% endif %}</h4>
<h6>{% if widget %}Update widget configuration{% else %}Add a new widget to your dashboard{% endif %}</h6>
</div>
<div class="page-btn">
<a href="{% url 'analytics:dashboard_widget_list' dashboard.pk|default:1 %}" class="btn btn-secondary">
<i class="fas fa-arrow-left me-1"></i>Back to Widgets
</a>
</div>
</div>
</div>
</div>
<form method="post" id="widgetForm">
{% csrf_token %}
<div class="row">
<!-- Widget Configuration -->
<div class="col-lg-8">
<!-- Basic Information -->
<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="title" class="form-label">Widget Title <span class="text-danger">*</span></label>
<input type="text" class="form-control" id="title" name="title"
value="{{ widget.title|default:'' }}"
placeholder="Enter widget title" required>
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label for="widget_type" class="form-label">Widget Type <span class="text-danger">*</span></label>
<select class="form-select" id="widget_type" name="widget_type" required onchange="updateWidgetOptions()">
<option value="">Select widget type</option>
<option value="CHART" {% if widget.widget_type == 'CHART' %}selected{% endif %}>Chart</option>
<option value="TABLE" {% if widget.widget_type == 'TABLE' %}selected{% endif %}>Table</option>
<option value="METRIC" {% if widget.widget_type == 'METRIC' %}selected{% endif %}>Metric/KPI</option>
<option value="GAUGE" {% if widget.widget_type == 'GAUGE' %}selected{% endif %}>Gauge</option>
<option value="MAP" {% if widget.widget_type == 'MAP' %}selected{% endif %}>Map</option>
<option value="TEXT" {% if widget.widget_type == 'TEXT' %}selected{% endif %}>Text</option>
<option value="CUSTOM" {% if widget.widget_type == 'CUSTOM' %}selected{% endif %}>Custom</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 what this widget displays">{{ widget.description|default:'' }}</textarea>
</div>
</div>
</div>
<!-- Chart Configuration -->
<div class="card" id="chartConfig" style="display: none;">
<div class="card-header">
<h5 class="card-title">
<i class="fas fa-chart-line me-2"></i>Chart Configuration
</h5>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-6">
<div class="form-group">
<label for="chart_type" class="form-label">Chart Type</label>
<select class="form-select" id="chart_type" name="chart_type">
<option value="LINE" {% if widget.chart_type == 'LINE' %}selected{% endif %}>Line Chart</option>
<option value="BAR" {% if widget.chart_type == 'BAR' %}selected{% endif %}>Bar Chart</option>
<option value="PIE" {% if widget.chart_type == 'PIE' %}selected{% endif %}>Pie Chart</option>
<option value="AREA" {% if widget.chart_type == 'AREA' %}selected{% endif %}>Area Chart</option>
<option value="SCATTER" {% if widget.chart_type == 'SCATTER' %}selected{% endif %}>Scatter Plot</option>
<option value="DOUGHNUT" {% if widget.chart_type == 'DOUGHNUT' %}selected{% endif %}>Doughnut Chart</option>
</select>
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label for="color_scheme" class="form-label">Color Scheme</label>
<select class="form-select" id="color_scheme" name="color_scheme">
<option value="default">Default</option>
<option value="blue">Blue Tones</option>
<option value="green">Green Tones</option>
<option value="red">Red Tones</option>
<option value="purple">Purple Tones</option>
<option value="rainbow">Rainbow</option>
</select>
</div>
</div>
</div>
<div class="row">
<div class="col-md-4">
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" id="show_legend" name="show_legend"
{% if widget.show_legend or not widget %}checked{% endif %}>
<label class="form-check-label" for="show_legend">Show Legend</label>
</div>
</div>
<div class="col-md-4">
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" id="show_grid" name="show_grid"
{% if widget.show_grid %}checked{% endif %}>
<label class="form-check-label" for="show_grid">Show Grid</label>
</div>
</div>
<div class="col-md-4">
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" id="enable_zoom" name="enable_zoom"
{% if widget.enable_zoom %}checked{% endif %}>
<label class="form-check-label" for="enable_zoom">Enable Zoom</label>
</div>
</div>
</div>
</div>
</div>
<!-- Data Configuration -->
<div class="card">
<div class="card-header">
<h5 class="card-title">
<i class="fas fa-database me-2"></i>Data Configuration
</h5>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-6">
<div class="form-group">
<label for="data_source" class="form-label">Data Source <span class="text-danger">*</span></label>
<select class="form-select" id="data_source" name="data_source" required onchange="updateMetrics()">
<option value="">Select data source</option>
<option value="patient_data" {% if widget.data_source == 'patient_data' %}selected{% endif %}>Patient Data</option>
<option value="financial_data" {% if widget.data_source == 'financial_data' %}selected{% endif %}>Financial Data</option>
<option value="clinical_data" {% if widget.data_source == 'clinical_data' %}selected{% endif %}>Clinical Data</option>
<option value="operational_data" {% if widget.data_source == 'operational_data' %}selected{% endif %}>Operational Data</option>
<option value="quality_data" {% if widget.data_source == 'quality_data' %}selected{% endif %}>Quality Data</option>
<option value="hr_data" {% if widget.data_source == 'hr_data' %}selected{% endif %}>HR Data</option>
</select>
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label for="metric" class="form-label">Primary Metric <span class="text-danger">*</span></label>
<select class="form-select" id="metric" name="metric" required>
<option value="">Select metric</option>
<!-- Options will be populated based on data source -->
</select>
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="form-group">
<label for="time_period" class="form-label">Time Period</label>
<select class="form-select" id="time_period" name="time_period">
<option value="7d" {% if widget.time_period == '7d' %}selected{% endif %}>Last 7 days</option>
<option value="30d" {% if widget.time_period == '30d' or not widget %}selected{% endif %}>Last 30 days</option>
<option value="90d" {% if widget.time_period == '90d' %}selected{% endif %}>Last 90 days</option>
<option value="6m" {% if widget.time_period == '6m' %}selected{% endif %}>Last 6 months</option>
<option value="1y" {% if widget.time_period == '1y' %}selected{% endif %}>Last year</option>
<option value="custom" {% if widget.time_period == 'custom' %}selected{% endif %}>Custom Range</option>
</select>
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label for="refresh_interval" class="form-label">Refresh Interval</label>
<select class="form-select" id="refresh_interval" name="refresh_interval">
<option value="60" {% if widget.refresh_interval == 60 %}selected{% endif %}>1 minute</option>
<option value="300" {% if widget.refresh_interval == 300 or not widget %}selected{% endif %}>5 minutes</option>
<option value="600" {% if widget.refresh_interval == 600 %}selected{% endif %}>10 minutes</option>
<option value="900" {% if widget.refresh_interval == 900 %}selected{% endif %}>15 minutes</option>
<option value="1800" {% if widget.refresh_interval == 1800 %}selected{% endif %}>30 minutes</option>
<option value="3600" {% if widget.refresh_interval == 3600 %}selected{% endif %}>1 hour</option>
</select>
</div>
</div>
</div>
<div class="form-group" id="customDateRange" style="display: none;">
<label class="form-label">Custom Date Range</label>
<div class="row">
<div class="col-md-6">
<input type="date" class="form-control" id="start_date" name="start_date"
value="{{ widget.start_date|default:'' }}">
</div>
<div class="col-md-6">
<input type="date" class="form-control" id="end_date" name="end_date"
value="{{ widget.end_date|default:'' }}">
</div>
</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-3">
<div class="form-group">
<label for="grid_x" class="form-label">Column Start</label>
<select class="form-select" id="grid_x" name="grid_x">
{% for i in "123456789012"|make_list %}
<option value="{{ forloop.counter }}" {% if widget.grid_x == forloop.counter %}selected{% endif %}>{{ forloop.counter }}</option>
{% endfor %}
</select>
</div>
</div>
<div class="col-md-3">
<div class="form-group">
<label for="grid_y" class="form-label">Row Start</label>
<select class="form-select" id="grid_y" name="grid_y">
{% for i in "123456"|make_list %}
<option value="{{ forloop.counter }}" {% if widget.grid_y == forloop.counter %}selected{% endif %}>{{ forloop.counter }}</option>
{% endfor %}
</select>
</div>
</div>
<div class="col-md-3">
<div class="form-group">
<label for="grid_width" class="form-label">Width (columns)</label>
<select class="form-select" id="grid_width" name="grid_width">
{% for i in "123456789012"|make_list %}
<option value="{{ forloop.counter }}" {% if widget.grid_width == forloop.counter or forloop.counter == 6 and not widget %}selected{% endif %}>{{ forloop.counter }}</option>
{% endfor %}
</select>
</div>
</div>
<div class="col-md-3">
<div class="form-group">
<label for="grid_height" class="form-label">Height (rows)</label>
<select class="form-select" id="grid_height" name="grid_height">
{% for i in "123456"|make_list %}
<option value="{{ forloop.counter }}" {% if widget.grid_height == forloop.counter or forloop.counter == 3 and not widget %}selected{% endif %}>{{ forloop.counter }}</option>
{% endfor %}
</select>
</div>
</div>
</div>
<div class="grid-preview-container">
<label class="form-label">Position Preview</label>
<div class="grid-preview" id="gridPreview">
<!-- Grid will be generated by JavaScript -->
</div>
</div>
</div>
</div>
</div>
<!-- Sidebar -->
<div class="col-lg-4">
<!-- Widget Preview -->
<div class="card">
<div class="card-header">
<h5 class="card-title">
<i class="fas fa-eye me-2"></i>Widget Preview
</h5>
</div>
<div class="card-body">
<div class="widget-preview" id="widgetPreview">
<div class="preview-placeholder">
<i class="fas fa-chart-line fa-3x text-muted"></i>
<p class="text-muted mt-2">Select widget type to see preview</p>
</div>
</div>
</div>
</div>
<!-- Widget Settings -->
<div class="card">
<div class="card-header">
<h5 class="card-title">
<i class="fas fa-cog me-2"></i>Widget Settings
</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 widget.is_active or not widget %}checked{% endif %}>
<label class="form-check-label" for="is_active">Active Widget</label>
</div>
<div class="form-text">Inactive widgets are hidden from the dashboard</div>
</div>
<div class="form-group">
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" id="allow_export" name="allow_export"
{% if widget.allow_export or not widget %}checked{% endif %}>
<label class="form-check-label" for="allow_export">Allow Export</label>
</div>
<div class="form-text">Users can export widget data</div>
</div>
<div class="form-group">
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" id="allow_drilldown" name="allow_drilldown"
{% if widget.allow_drilldown %}checked{% endif %}>
<label class="form-check-label" for="allow_drilldown">Allow Drill-down</label>
</div>
<div class="form-text">Users can click for detailed views</div>
</div>
<div class="form-group">
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" id="real_time" name="real_time"
{% if widget.real_time %}checked{% endif %}>
<label class="form-check-label" for="real_time">Real-time Updates</label>
</div>
<div class="form-text">Widget updates in real-time</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 widget %}Update Widget{% else %}Create Widget{% endif %}
</button>
<button type="button" class="btn btn-outline-secondary" onclick="previewWidget()">
<i class="fas fa-eye me-1"></i>Preview Widget
</button>
<a href="{% url 'analytics:dashboard_widget_list' dashboard.pk|default:1 %}" 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
updateWidgetOptions();
updateGridPreview();
// Add event listeners
document.getElementById('time_period').addEventListener('change', handleTimePeriodChange);
// Grid position listeners
['grid_x', 'grid_y', 'grid_width', 'grid_height'].forEach(id => {
document.getElementById(id).addEventListener('change', updateGridPreview);
});
// Form change listeners for preview
const formInputs = document.querySelectorAll('#widgetForm input, #widgetForm select, #widgetForm textarea');
formInputs.forEach(input => {
input.addEventListener('change', updatePreview);
});
});
function updateWidgetOptions() {
const widgetType = document.getElementById('widget_type').value;
const chartConfig = document.getElementById('chartConfig');
if (widgetType === 'CHART') {
chartConfig.style.display = 'block';
} else {
chartConfig.style.display = 'none';
}
updatePreview();
}
function updateMetrics() {
const dataSource = document.getElementById('data_source').value;
const metricSelect = document.getElementById('metric');
// Clear existing options
metricSelect.innerHTML = '<option value="">Select metric</option>';
const metrics = {
'patient_data': [
{ value: 'patient_count', text: 'Patient Count' },
{ value: 'new_patients', text: 'New Patients' },
{ value: 'patient_satisfaction', text: 'Patient Satisfaction' },
{ value: 'readmission_rate', text: 'Readmission Rate' }
],
'financial_data': [
{ value: 'revenue', text: 'Revenue' },
{ value: 'expenses', text: 'Expenses' },
{ value: 'profit_margin', text: 'Profit Margin' },
{ value: 'billing_amount', text: 'Billing Amount' }
],
'clinical_data': [
{ value: 'lab_results', text: 'Lab Results' },
{ value: 'procedures', text: 'Procedures' },
{ value: 'medications', text: 'Medications' },
{ value: 'diagnoses', text: 'Diagnoses' }
],
'operational_data': [
{ value: 'bed_occupancy', text: 'Bed Occupancy' },
{ value: 'staff_utilization', text: 'Staff Utilization' },
{ value: 'equipment_usage', text: 'Equipment Usage' },
{ value: 'wait_times', text: 'Wait Times' }
],
'quality_data': [
{ value: 'quality_score', text: 'Quality Score' },
{ value: 'incident_rate', text: 'Incident Rate' },
{ value: 'compliance_rate', text: 'Compliance Rate' },
{ value: 'safety_metrics', text: 'Safety Metrics' }
],
'hr_data': [
{ value: 'staff_count', text: 'Staff Count' },
{ value: 'turnover_rate', text: 'Turnover Rate' },
{ value: 'training_completion', text: 'Training Completion' },
{ value: 'performance_scores', text: 'Performance Scores' }
]
};
if (metrics[dataSource]) {
metrics[dataSource].forEach(metric => {
const option = document.createElement('option');
option.value = metric.value;
option.textContent = metric.text;
metricSelect.appendChild(option);
});
}
}
function handleTimePeriodChange() {
const timePeriod = document.getElementById('time_period').value;
const customDateRange = document.getElementById('customDateRange');
if (timePeriod === 'custom') {
customDateRange.style.display = 'block';
} else {
customDateRange.style.display = 'none';
}
}
function updateGridPreview() {
const gridX = parseInt(document.getElementById('grid_x').value) || 1;
const gridY = parseInt(document.getElementById('grid_y').value) || 1;
const gridWidth = parseInt(document.getElementById('grid_width').value) || 6;
const gridHeight = parseInt(document.getElementById('grid_height').value) || 3;
const preview = document.getElementById('gridPreview');
preview.innerHTML = '';
// Create 12x6 grid
for (let row = 1; row <= 6; row++) {
for (let col = 1; col <= 12; col++) {
const cell = document.createElement('div');
cell.className = 'grid-cell';
// Check if this cell is part of the widget
if (row >= gridY && row < gridY + gridHeight &&
col >= gridX && col < gridX + gridWidth) {
cell.classList.add('selected');
}
preview.appendChild(cell);
}
}
}
function updatePreview() {
const widgetType = document.getElementById('widget_type').value;
const title = document.getElementById('title').value || 'Widget Title';
const preview = document.getElementById('widgetPreview');
if (!widgetType) {
preview.innerHTML = `
<div class="preview-placeholder">
<i class="fas fa-chart-line fa-3x text-muted"></i>
<p class="text-muted mt-2">Select widget type to see preview</p>
</div>
`;
return;
}
const icons = {
'CHART': 'fas fa-chart-line',
'TABLE': 'fas fa-table',
'METRIC': 'fas fa-calculator',
'GAUGE': 'fas fa-tachometer-alt',
'MAP': 'fas fa-map',
'TEXT': 'fas fa-font',
'CUSTOM': 'fas fa-cog'
};
const colors = {
'CHART': 'text-primary',
'TABLE': 'text-info',
'METRIC': 'text-success',
'GAUGE': 'text-warning',
'MAP': 'text-danger',
'TEXT': 'text-secondary',
'CUSTOM': 'text-dark'
};
preview.innerHTML = `
<div class="preview-widget">
<div class="preview-header">
<h6>${title}</h6>
</div>
<div class="preview-content">
<i class="${icons[widgetType]} fa-3x ${colors[widgetType]}"></i>
<p class="mt-2 text-muted">${widgetType} Widget</p>
</div>
</div>
`;
}
function previewWidget() {
const formData = new FormData(document.getElementById('widgetForm'));
const widgetData = Object.fromEntries(formData.entries());
alert('Opening widget preview...\n\nWidget: ' + widgetData.title + '\nType: ' + widgetData.widget_type);
}
// Form validation
document.getElementById('widgetForm').addEventListener('submit', function(e) {
const title = document.getElementById('title').value.trim();
const widgetType = document.getElementById('widget_type').value;
const dataSource = document.getElementById('data_source').value;
const metric = document.getElementById('metric').value;
if (!title || !widgetType || !dataSource || !metric) {
e.preventDefault();
alert('Please fill in all required fields');
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 delay (in real app, handled by form submission)
setTimeout(() => {
submitBtn.innerHTML = originalText;
submitBtn.disabled = false;
}, 2000);
});
</script>
<style>
.grid-preview-container {
margin-top: 20px;
}
.grid-preview {
display: grid;
grid-template-columns: repeat(12, 1fr);
grid-template-rows: repeat(6, 25px);
gap: 2px;
background: #f8f9fa;
padding: 15px;
border-radius: 8px;
border: 1px solid #e9ecef;
}
.grid-cell {
background: #e9ecef;
border-radius: 3px;
transition: all 0.3s ease;
}
.grid-cell.selected {
background: #007bff;
}
.widget-preview {
min-height: 200px;
border: 1px solid #e9ecef;
border-radius: 8px;
background: #f8f9fa;
display: flex;
align-items: center;
justify-content: center;
}
.preview-placeholder {
text-align: center;
padding: 40px 20px;
}
.preview-widget {
text-align: center;
padding: 20px;
background: white;
border-radius: 8px;
width: 100%;
margin: 10px;
}
.preview-header {
border-bottom: 1px solid #e9ecef;
padding-bottom: 10px;
margin-bottom: 15px;
}
.preview-header h6 {
margin: 0;
font-weight: 600;
color: #2c3e50;
}
.preview-content {
padding: 20px 0;
}
@media (max-width: 768px) {
.grid-preview {
grid-template-columns: repeat(6, 1fr);
grid-template-rows: repeat(8, 20px);
}
.preview-widget {
margin: 5px;
padding: 15px;
}
.preview-content {
padding: 15px 0;
}
.preview-content i {
font-size: 2rem !important;
}
}
</style>
{% endblock %}