860 lines
43 KiB
HTML
860 lines
43 KiB
HTML
{% extends 'base.html' %}
|
|
{% load static %}
|
|
|
|
{% block title %}Pharmacy Workflow - Pharmacy{% 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>Pharmacy Workflow Dashboard</h4>
|
|
<h6>Real-time workflow management and prescription processing overview</h6>
|
|
</div>
|
|
<div class="page-btn">
|
|
<div class="btn-group">
|
|
<button type="button" class="btn btn-primary" onclick="refreshWorkflow()">
|
|
<i class="fa fa-sync"></i> Refresh
|
|
</button>
|
|
<button type="button" class="btn btn-info" data-bs-toggle="modal" data-bs-target="#workflowSettingsModal">
|
|
<i class="fa fa-cog"></i> Settings
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Workflow Overview -->
|
|
<div class="row">
|
|
<div class="col-xl-3 col-sm-6 col-12">
|
|
<div class="card">
|
|
<div class="card-body">
|
|
<div class="dash-widget-header">
|
|
<span class="dash-widget-icon text-primary border-primary">
|
|
<i class="fa fa-inbox"></i>
|
|
</span>
|
|
<div class="dash-count">
|
|
<h3>{{ workflow_stats.pending_prescriptions|default:24 }}</h3>
|
|
</div>
|
|
</div>
|
|
<div class="dash-widget-info">
|
|
<h6 class="text-muted">Pending Prescriptions</h6>
|
|
<div class="progress progress-sm">
|
|
<div class="progress-bar bg-primary" style="width: {{ workflow_stats.pending_percentage|default:60 }}%"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-xl-3 col-sm-6 col-12">
|
|
<div class="card">
|
|
<div class="card-body">
|
|
<div class="dash-widget-header">
|
|
<span class="dash-widget-icon text-warning border-warning">
|
|
<i class="fa fa-clock"></i>
|
|
</span>
|
|
<div class="dash-count">
|
|
<h3>{{ workflow_stats.in_progress|default:18 }}</h3>
|
|
</div>
|
|
</div>
|
|
<div class="dash-widget-info">
|
|
<h6 class="text-muted">In Progress</h6>
|
|
<div class="progress progress-sm">
|
|
<div class="progress-bar bg-warning" style="width: {{ workflow_stats.progress_percentage|default:45 }}%"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-xl-3 col-sm-6 col-12">
|
|
<div class="card">
|
|
<div class="card-body">
|
|
<div class="dash-widget-header">
|
|
<span class="dash-widget-icon text-success border-success">
|
|
<i class="fa fa-check-circle"></i>
|
|
</span>
|
|
<div class="dash-count">
|
|
<h3>{{ workflow_stats.ready_for_pickup|default:12 }}</h3>
|
|
</div>
|
|
</div>
|
|
<div class="dash-widget-info">
|
|
<h6 class="text-muted">Ready for Pickup</h6>
|
|
<div class="progress progress-sm">
|
|
<div class="progress-bar bg-success" style="width: {{ workflow_stats.ready_percentage|default:30 }}%"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-xl-3 col-sm-6 col-12">
|
|
<div class="card">
|
|
<div class="card-body">
|
|
<div class="dash-widget-header">
|
|
<span class="dash-widget-icon text-info border-info">
|
|
<i class="fa fa-users"></i>
|
|
</span>
|
|
<div class="dash-count">
|
|
<h3>{{ workflow_stats.active_staff|default:8 }}</h3>
|
|
</div>
|
|
</div>
|
|
<div class="dash-widget-info">
|
|
<h6 class="text-muted">Active Staff</h6>
|
|
<div class="progress progress-sm">
|
|
<div class="progress-bar bg-info" style="width: 80%"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Workflow Stages -->
|
|
<div class="row">
|
|
<div class="col-12">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h5 class="card-title">Prescription Workflow Pipeline</h5>
|
|
<div class="card-tools">
|
|
<span class="badge bg-info">Last updated: {{ last_updated|date:"H:i" }}</span>
|
|
</div>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="workflow-pipeline">
|
|
<div class="row">
|
|
<!-- Stage 1: Received -->
|
|
<div class="col-md-2">
|
|
<div class="workflow-stage">
|
|
<div class="stage-header bg-primary text-white">
|
|
<h6 class="mb-0">
|
|
<i class="fa fa-inbox"></i> Received
|
|
<span class="badge bg-light text-dark ms-2">{{ stages.received.count|default:8 }}</span>
|
|
</h6>
|
|
</div>
|
|
<div class="stage-content">
|
|
{% for prescription in stages.received.prescriptions %}
|
|
<div class="prescription-card mb-2" data-prescription-id="{{ prescription.id }}">
|
|
<div class="card-sm">
|
|
<div class="card-body p-2">
|
|
<div class="d-flex justify-content-between">
|
|
<small><strong>{{ prescription.prescription_number }}</strong></small>
|
|
<span class="badge bg-{{ prescription.priority_color }}">{{ prescription.priority|slice:":1"|upper }}</span>
|
|
</div>
|
|
<div class="text-muted small">
|
|
{{ prescription.patient.get_full_name|truncatechars:20 }}<br>
|
|
{{ prescription.medication.name|truncatechars:15 }}
|
|
</div>
|
|
<div class="text-muted small">
|
|
{{ prescription.time_received|timesince }} ago
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% empty %}
|
|
<div class="text-center text-muted py-3">
|
|
<i class="fa fa-check-circle"></i><br>
|
|
<small>No prescriptions</small>
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Stage 2: Data Entry -->
|
|
<div class="col-md-2">
|
|
<div class="workflow-stage">
|
|
<div class="stage-header bg-info text-white">
|
|
<h6 class="mb-0">
|
|
<i class="fa fa-keyboard"></i> Data Entry
|
|
<span class="badge bg-light text-dark ms-2">{{ stages.data_entry.count|default:6 }}</span>
|
|
</h6>
|
|
</div>
|
|
<div class="stage-content">
|
|
{% for prescription in stages.data_entry.prescriptions %}
|
|
<div class="prescription-card mb-2" data-prescription-id="{{ prescription.id }}">
|
|
<div class="card-sm">
|
|
<div class="card-body p-2">
|
|
<div class="d-flex justify-content-between">
|
|
<small><strong>{{ prescription.prescription_number }}</strong></small>
|
|
<span class="badge bg-{{ prescription.priority_color }}">{{ prescription.priority|slice:":1"|upper }}</span>
|
|
</div>
|
|
<div class="text-muted small">
|
|
{{ prescription.patient.get_full_name|truncatechars:20 }}<br>
|
|
{{ prescription.medication.name|truncatechars:15 }}
|
|
</div>
|
|
<div class="text-muted small">
|
|
Assigned: {{ prescription.assigned_technician.first_name|default:"Unassigned" }}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% empty %}
|
|
<div class="text-center text-muted py-3">
|
|
<i class="fa fa-check-circle"></i><br>
|
|
<small>No prescriptions</small>
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Stage 3: Verification -->
|
|
<div class="col-md-2">
|
|
<div class="workflow-stage">
|
|
<div class="stage-header bg-warning text-dark">
|
|
<h6 class="mb-0">
|
|
<i class="fa fa-clipboard-check"></i> Verification
|
|
<span class="badge bg-light text-dark ms-2">{{ stages.verification.count|default:5 }}</span>
|
|
</h6>
|
|
</div>
|
|
<div class="stage-content">
|
|
{% for prescription in stages.verification.prescriptions %}
|
|
<div class="prescription-card mb-2" data-prescription-id="{{ prescription.id }}">
|
|
<div class="card-sm">
|
|
<div class="card-body p-2">
|
|
<div class="d-flex justify-content-between">
|
|
<small><strong>{{ prescription.prescription_number }}</strong></small>
|
|
<span class="badge bg-{{ prescription.priority_color }}">{{ prescription.priority|slice:":1"|upper }}</span>
|
|
</div>
|
|
<div class="text-muted small">
|
|
{{ prescription.patient.get_full_name|truncatechars:20 }}<br>
|
|
{{ prescription.medication.name|truncatechars:15 }}
|
|
</div>
|
|
<div class="text-muted small">
|
|
Pharmacist: {{ prescription.assigned_pharmacist.first_name|default:"Unassigned" }}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% empty %}
|
|
<div class="text-center text-muted py-3">
|
|
<i class="fa fa-check-circle"></i><br>
|
|
<small>No prescriptions</small>
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Stage 4: Filling -->
|
|
<div class="col-md-2">
|
|
<div class="workflow-stage">
|
|
<div class="stage-header bg-secondary text-white">
|
|
<h6 class="mb-0">
|
|
<i class="fa fa-pills"></i> Filling
|
|
<span class="badge bg-light text-dark ms-2">{{ stages.filling.count|default:4 }}</span>
|
|
</h6>
|
|
</div>
|
|
<div class="stage-content">
|
|
{% for prescription in stages.filling.prescriptions %}
|
|
<div class="prescription-card mb-2" data-prescription-id="{{ prescription.id }}">
|
|
<div class="card-sm">
|
|
<div class="card-body p-2">
|
|
<div class="d-flex justify-content-between">
|
|
<small><strong>{{ prescription.prescription_number }}</strong></small>
|
|
<span class="badge bg-{{ prescription.priority_color }}">{{ prescription.priority|slice:":1"|upper }}</span>
|
|
</div>
|
|
<div class="text-muted small">
|
|
{{ prescription.patient.get_full_name|truncatechars:20 }}<br>
|
|
{{ prescription.medication.name|truncatechars:15 }}
|
|
</div>
|
|
<div class="text-muted small">
|
|
Technician: {{ prescription.filling_technician.first_name|default:"Unassigned" }}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% empty %}
|
|
<div class="text-center text-muted py-3">
|
|
<i class="fa fa-check-circle"></i><br>
|
|
<small>No prescriptions</small>
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Stage 5: Final Check -->
|
|
<div class="col-md-2">
|
|
<div class="workflow-stage">
|
|
<div class="stage-header bg-dark text-white">
|
|
<h6 class="mb-0">
|
|
<i class="fa fa-search"></i> Final Check
|
|
<span class="badge bg-light text-dark ms-2">{{ stages.final_check.count|default:3 }}</span>
|
|
</h6>
|
|
</div>
|
|
<div class="stage-content">
|
|
{% for prescription in stages.final_check.prescriptions %}
|
|
<div class="prescription-card mb-2" data-prescription-id="{{ prescription.id }}">
|
|
<div class="card-sm">
|
|
<div class="card-body p-2">
|
|
<div class="d-flex justify-content-between">
|
|
<small><strong>{{ prescription.prescription_number }}</strong></small>
|
|
<span class="badge bg-{{ prescription.priority_color }}">{{ prescription.priority|slice:":1"|upper }}</span>
|
|
</div>
|
|
<div class="text-muted small">
|
|
{{ prescription.patient.get_full_name|truncatechars:20 }}<br>
|
|
{{ prescription.medication.name|truncatechars:15 }}
|
|
</div>
|
|
<div class="text-muted small">
|
|
Pharmacist: {{ prescription.checking_pharmacist.first_name|default:"Unassigned" }}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% empty %}
|
|
<div class="text-center text-muted py-3">
|
|
<i class="fa fa-check-circle"></i><br>
|
|
<small>No prescriptions</small>
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Stage 6: Ready -->
|
|
<div class="col-md-2">
|
|
<div class="workflow-stage">
|
|
<div class="stage-header bg-success text-white">
|
|
<h6 class="mb-0">
|
|
<i class="fa fa-check-circle"></i> Ready
|
|
<span class="badge bg-light text-dark ms-2">{{ stages.ready.count|default:12 }}</span>
|
|
</h6>
|
|
</div>
|
|
<div class="stage-content">
|
|
{% for prescription in stages.ready.prescriptions %}
|
|
<div class="prescription-card mb-2" data-prescription-id="{{ prescription.id }}">
|
|
<div class="card-sm">
|
|
<div class="card-body p-2">
|
|
<div class="d-flex justify-content-between">
|
|
<small><strong>{{ prescription.prescription_number }}</strong></small>
|
|
<span class="badge bg-{{ prescription.priority_color }}">{{ prescription.priority|slice:":1"|upper }}</span>
|
|
</div>
|
|
<div class="text-muted small">
|
|
{{ prescription.patient.get_full_name|truncatechars:20 }}<br>
|
|
{{ prescription.medication.name|truncatechars:15 }}
|
|
</div>
|
|
<div class="text-muted small">
|
|
Ready: {{ prescription.ready_time|timesince }} ago
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% empty %}
|
|
<div class="text-center text-muted py-3">
|
|
<i class="fa fa-check-circle"></i><br>
|
|
<small>No prescriptions</small>
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Staff Workload -->
|
|
<div class="row">
|
|
<div class="col-md-8">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h5 class="card-title">Staff Workload Distribution</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="table-responsive">
|
|
<table class="table table-striped">
|
|
<thead>
|
|
<tr>
|
|
<th>Staff Member</th>
|
|
<th>Role</th>
|
|
<th>Current Tasks</th>
|
|
<th>Completed Today</th>
|
|
<th>Average Time</th>
|
|
<th>Status</th>
|
|
<th>Actions</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for staff in staff_workload %}
|
|
<tr>
|
|
<td>
|
|
<div class="d-flex align-items-center">
|
|
<div class="avatar avatar-sm me-2">
|
|
<span class="avatar-title bg-{{ staff.status_color }} text-white">
|
|
{{ staff.first_name|slice:":1" }}{{ staff.last_name|slice:":1" }}
|
|
</span>
|
|
</div>
|
|
{{ staff.get_full_name }}
|
|
</div>
|
|
</td>
|
|
<td>{{ staff.role|title }}</td>
|
|
<td>
|
|
<span class="badge bg-primary">{{ staff.current_tasks|default:0 }}</span>
|
|
</td>
|
|
<td>{{ staff.completed_today|default:0 }}</td>
|
|
<td>{{ staff.average_time|default:"--" }} min</td>
|
|
<td>
|
|
<span class="badge bg-{{ staff.status_color }}">{{ staff.status|title }}</span>
|
|
</td>
|
|
<td>
|
|
<div class="btn-group">
|
|
<button type="button" class="btn btn-sm btn-outline-primary"
|
|
onclick="assignTask({{ staff.id }})">
|
|
<i class="fa fa-plus"></i>
|
|
</button>
|
|
<button type="button" class="btn btn-sm btn-outline-info"
|
|
onclick="viewWorkload({{ staff.id }})">
|
|
<i class="fa fa-eye"></i>
|
|
</button>
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
{% empty %}
|
|
<tr>
|
|
<td colspan="7" class="text-center text-muted">No staff members on duty</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-4">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h5 class="card-title">Workflow Metrics</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="metric-item mb-3">
|
|
<div class="d-flex justify-content-between">
|
|
<span>Average Processing Time</span>
|
|
<strong>{{ metrics.avg_processing_time|default:45 }} min</strong>
|
|
</div>
|
|
<div class="progress progress-sm">
|
|
<div class="progress-bar bg-info" style="width: 75%"></div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="metric-item mb-3">
|
|
<div class="d-flex justify-content-between">
|
|
<span>Throughput Today</span>
|
|
<strong>{{ metrics.throughput_today|default:156 }} Rx</strong>
|
|
</div>
|
|
<div class="progress progress-sm">
|
|
<div class="progress-bar bg-success" style="width: 85%"></div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="metric-item mb-3">
|
|
<div class="d-flex justify-content-between">
|
|
<span>Error Rate</span>
|
|
<strong>{{ metrics.error_rate|default:0.2 }}%</strong>
|
|
</div>
|
|
<div class="progress progress-sm">
|
|
<div class="progress-bar bg-success" style="width: 95%"></div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="metric-item mb-3">
|
|
<div class="d-flex justify-content-between">
|
|
<span>Customer Wait Time</span>
|
|
<strong>{{ metrics.wait_time|default:12 }} min</strong>
|
|
</div>
|
|
<div class="progress progress-sm">
|
|
<div class="progress-bar bg-warning" style="width: 60%"></div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="metric-item">
|
|
<div class="d-flex justify-content-between">
|
|
<span>Staff Utilization</span>
|
|
<strong>{{ metrics.staff_utilization|default:78 }}%</strong>
|
|
</div>
|
|
<div class="progress progress-sm">
|
|
<div class="progress-bar bg-primary" style="width: 78%"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Alerts and Notifications -->
|
|
<div class="card mt-3">
|
|
<div class="card-header">
|
|
<h5 class="card-title">Workflow Alerts</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
{% for alert in workflow_alerts %}
|
|
<div class="alert alert-{{ alert.type }} alert-sm mb-2">
|
|
<div class="d-flex justify-content-between">
|
|
<span><i class="fa fa-{{ alert.icon }}"></i> {{ alert.message }}</span>
|
|
<small>{{ alert.time|timesince }} ago</small>
|
|
</div>
|
|
</div>
|
|
{% empty %}
|
|
<div class="text-center text-muted">
|
|
<i class="fa fa-check-circle fa-2x mb-2"></i>
|
|
<p>No active alerts</p>
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Recent Activity -->
|
|
<div class="row">
|
|
<div class="col-12">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h5 class="card-title">Recent Workflow Activity</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="table-responsive">
|
|
<table class="table table-sm">
|
|
<thead>
|
|
<tr>
|
|
<th>Time</th>
|
|
<th>Prescription</th>
|
|
<th>Patient</th>
|
|
<th>Action</th>
|
|
<th>Staff</th>
|
|
<th>Stage</th>
|
|
<th>Duration</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for activity in recent_activities %}
|
|
<tr>
|
|
<td>{{ activity.timestamp|date:"H:i" }}</td>
|
|
<td>{{ activity.prescription_number }}</td>
|
|
<td>{{ activity.patient_name }}</td>
|
|
<td>{{ activity.action|title }}</td>
|
|
<td>{{ activity.staff_member }}</td>
|
|
<td>
|
|
<span class="badge bg-{{ activity.stage_color }}">{{ activity.stage|title }}</span>
|
|
</td>
|
|
<td>{{ activity.duration|default:"--" }}</td>
|
|
</tr>
|
|
{% empty %}
|
|
<tr>
|
|
<td colspan="7" class="text-center text-muted">No recent activity</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Workflow Settings Modal -->
|
|
<div class="modal fade" id="workflowSettingsModal" tabindex="-1">
|
|
<div class="modal-dialog">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title">Workflow Settings</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
|
</div>
|
|
<form id="workflowSettingsForm">
|
|
<div class="modal-body">
|
|
<div class="mb-3">
|
|
<label class="form-label">Auto-refresh Interval</label>
|
|
<select name="refresh_interval" class="form-select">
|
|
<option value="30">30 seconds</option>
|
|
<option value="60" selected>1 minute</option>
|
|
<option value="120">2 minutes</option>
|
|
<option value="300">5 minutes</option>
|
|
<option value="0">Manual only</option>
|
|
</select>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label class="form-label">Priority Display</label>
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="checkbox" name="show_priority" id="showPriority" checked>
|
|
<label class="form-check-label" for="showPriority">
|
|
Show priority indicators
|
|
</label>
|
|
</div>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label class="form-label">Sound Notifications</label>
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="checkbox" name="sound_alerts" id="soundAlerts">
|
|
<label class="form-check-label" for="soundAlerts">
|
|
Enable sound alerts for urgent prescriptions
|
|
</label>
|
|
</div>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label class="form-label">Workflow View</label>
|
|
<select name="workflow_view" class="form-select">
|
|
<option value="pipeline" selected>Pipeline View</option>
|
|
<option value="list">List View</option>
|
|
<option value="kanban">Kanban Board</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
|
|
<button type="submit" class="btn btn-primary">Save Settings</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
// Auto-refresh functionality
|
|
let refreshInterval = 60000; // 1 minute default
|
|
let refreshTimer;
|
|
|
|
function startAutoRefresh() {
|
|
if (refreshInterval > 0) {
|
|
refreshTimer = setInterval(refreshWorkflow, refreshInterval);
|
|
}
|
|
}
|
|
|
|
function stopAutoRefresh() {
|
|
if (refreshTimer) {
|
|
clearInterval(refreshTimer);
|
|
}
|
|
}
|
|
|
|
// Start auto-refresh
|
|
startAutoRefresh();
|
|
|
|
// Prescription card click handlers
|
|
document.querySelectorAll('.prescription-card').forEach(card => {
|
|
card.addEventListener('click', function() {
|
|
const prescriptionId = this.getAttribute('data-prescription-id');
|
|
showPrescriptionDetails(prescriptionId);
|
|
});
|
|
});
|
|
|
|
// Workflow settings form handler
|
|
document.getElementById('workflowSettingsForm').addEventListener('submit', function(e) {
|
|
e.preventDefault();
|
|
|
|
const formData = new FormData(this);
|
|
const newInterval = parseInt(formData.get('refresh_interval')) * 1000;
|
|
|
|
// Update refresh interval
|
|
refreshInterval = newInterval;
|
|
stopAutoRefresh();
|
|
startAutoRefresh();
|
|
|
|
// Close modal
|
|
bootstrap.Modal.getInstance(document.getElementById('workflowSettingsModal')).hide();
|
|
|
|
alert('Workflow settings updated successfully!');
|
|
});
|
|
|
|
// Drag and drop functionality for workflow stages
|
|
enableDragAndDrop();
|
|
});
|
|
|
|
function refreshWorkflow() {
|
|
console.log('Refreshing workflow data...');
|
|
|
|
// Show refresh indicator
|
|
const refreshBtn = document.querySelector('button[onclick="refreshWorkflow()"]');
|
|
const originalContent = refreshBtn.innerHTML;
|
|
refreshBtn.innerHTML = '<i class="fa fa-spinner fa-spin"></i> Refreshing...';
|
|
refreshBtn.disabled = true;
|
|
|
|
// Simulate API call
|
|
setTimeout(() => {
|
|
// In real implementation, this would fetch updated data
|
|
location.reload();
|
|
}, 1000);
|
|
}
|
|
|
|
function assignTask(staffId) {
|
|
// Handle task assignment
|
|
const taskType = prompt('Select task type:\n1. Data Entry\n2. Verification\n3. Filling\n4. Final Check\n\nEnter number:');
|
|
|
|
if (taskType && ['1', '2', '3', '4'].includes(taskType)) {
|
|
const taskTypes = {
|
|
'1': 'Data Entry',
|
|
'2': 'Verification',
|
|
'3': 'Filling',
|
|
'4': 'Final Check'
|
|
};
|
|
|
|
console.log(`Assigning ${taskTypes[taskType]} task to staff ${staffId}`);
|
|
alert(`${taskTypes[taskType]} task assigned successfully!`);
|
|
}
|
|
}
|
|
|
|
function viewWorkload(staffId) {
|
|
// Handle workload viewing
|
|
console.log(`Viewing workload for staff ${staffId}`);
|
|
alert('Workload details would be displayed here.');
|
|
}
|
|
|
|
function showPrescriptionDetails(prescriptionId) {
|
|
// Handle prescription details display
|
|
console.log(`Showing details for prescription ${prescriptionId}`);
|
|
|
|
// In real implementation, this would open a modal or navigate to details page
|
|
if (confirm('View prescription details?')) {
|
|
window.open(`/pharmacy/prescription/${prescriptionId}/`, '_blank');
|
|
}
|
|
}
|
|
|
|
function enableDragAndDrop() {
|
|
// Enable drag and drop for prescription cards
|
|
const prescriptionCards = document.querySelectorAll('.prescription-card');
|
|
const workflowStages = document.querySelectorAll('.workflow-stage .stage-content');
|
|
|
|
prescriptionCards.forEach(card => {
|
|
card.draggable = true;
|
|
card.addEventListener('dragstart', function(e) {
|
|
e.dataTransfer.setData('text/plain', this.getAttribute('data-prescription-id'));
|
|
this.style.opacity = '0.5';
|
|
});
|
|
|
|
card.addEventListener('dragend', function(e) {
|
|
this.style.opacity = '1';
|
|
});
|
|
});
|
|
|
|
workflowStages.forEach(stage => {
|
|
stage.addEventListener('dragover', function(e) {
|
|
e.preventDefault();
|
|
this.style.backgroundColor = '#f8f9fa';
|
|
});
|
|
|
|
stage.addEventListener('dragleave', function(e) {
|
|
this.style.backgroundColor = '';
|
|
});
|
|
|
|
stage.addEventListener('drop', function(e) {
|
|
e.preventDefault();
|
|
this.style.backgroundColor = '';
|
|
|
|
const prescriptionId = e.dataTransfer.getData('text/plain');
|
|
const newStage = this.closest('.workflow-stage').querySelector('.stage-header h6').textContent.trim().split(' ')[1];
|
|
|
|
if (confirm(`Move prescription ${prescriptionId} to ${newStage} stage?`)) {
|
|
// Handle stage change
|
|
console.log(`Moving prescription ${prescriptionId} to ${newStage}`);
|
|
|
|
// In real implementation, make API call to update prescription stage
|
|
alert('Prescription moved successfully!');
|
|
refreshWorkflow();
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
// Real-time updates simulation
|
|
function simulateRealTimeUpdates() {
|
|
setInterval(() => {
|
|
// Simulate new prescription arrival
|
|
if (Math.random() < 0.1) { // 10% chance every interval
|
|
console.log('New prescription received');
|
|
|
|
// Update received count
|
|
const receivedBadge = document.querySelector('.workflow-stage:first-child .badge');
|
|
if (receivedBadge) {
|
|
const currentCount = parseInt(receivedBadge.textContent);
|
|
receivedBadge.textContent = currentCount + 1;
|
|
}
|
|
}
|
|
}, 30000); // Check every 30 seconds
|
|
}
|
|
|
|
// Start real-time updates simulation
|
|
simulateRealTimeUpdates();
|
|
</script>
|
|
|
|
<style>
|
|
.workflow-pipeline {
|
|
min-height: 400px;
|
|
}
|
|
|
|
.workflow-stage {
|
|
height: 100%;
|
|
border: 1px solid #dee2e6;
|
|
border-radius: 0.375rem;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.stage-header {
|
|
padding: 0.75rem;
|
|
border-bottom: 1px solid rgba(255,255,255,0.2);
|
|
}
|
|
|
|
.stage-content {
|
|
padding: 0.5rem;
|
|
height: 350px;
|
|
overflow-y: auto;
|
|
}
|
|
|
|
.prescription-card {
|
|
cursor: pointer;
|
|
transition: transform 0.2s;
|
|
}
|
|
|
|
.prescription-card:hover {
|
|
transform: translateY(-2px);
|
|
}
|
|
|
|
.prescription-card .card-sm {
|
|
border: 1px solid #dee2e6;
|
|
border-radius: 0.25rem;
|
|
transition: border-color 0.2s;
|
|
}
|
|
|
|
.prescription-card:hover .card-sm {
|
|
border-color: #007bff;
|
|
}
|
|
|
|
.metric-item {
|
|
padding: 0.5rem 0;
|
|
}
|
|
|
|
.avatar {
|
|
width: 32px;
|
|
height: 32px;
|
|
border-radius: 50%;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
}
|
|
|
|
.avatar-title {
|
|
font-size: 0.75rem;
|
|
font-weight: bold;
|
|
}
|
|
|
|
.alert-sm {
|
|
padding: 0.5rem;
|
|
font-size: 0.875rem;
|
|
}
|
|
|
|
@media (max-width: 768px) {
|
|
.workflow-pipeline .col-md-2 {
|
|
margin-bottom: 1rem;
|
|
}
|
|
|
|
.stage-content {
|
|
height: 200px;
|
|
}
|
|
}
|
|
</style>
|
|
{% endblock %}
|
|
|