2025-08-12 13:33:25 +03:00

748 lines
34 KiB
HTML

{% extends "base.html" %}
{% load static %}
{% block title %}{% if object %}Edit{% else %}New{% endif %} Risk Assessment{% endblock %}
{% block content %}
<div class="d-flex align-items-center mb-3">
<div>
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="{% url 'quality:dashboard' %}">Quality</a></li>
<li class="breadcrumb-item"><a href="{% url 'quality:risk_assessment_list' %}">Risk Assessments</a></li>
<li class="breadcrumb-item active">{% if object %}Edit{% else %}New{% endif %}</li>
</ol>
<h1 class="page-header mb-0">{% if object %}Edit{% else %}New{% endif %} Risk Assessment</h1>
</div>
<div class="ms-auto">
<a href="{% url 'quality:risk_assessment_list' %}" class="btn btn-secondary">
<i class="fas fa-arrow-left me-2"></i>Back to List
</a>
</div>
</div>
<form method="post" id="riskAssessmentForm">
{% csrf_token %}
<div class="row">
<div class="col-xl-8">
<!-- Basic Information -->
<div class="card mb-4">
<div class="card-header">
<h4 class="card-title">
<i class="fas fa-info-circle me-2"></i>
Basic Information
</h4>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-8">
<div class="mb-3">
<label for="title" class="form-label">
<strong>Assessment Title *</strong>
</label>
<input type="text"
class="form-control"
id="title"
name="title"
value="{{ object.title|default:"" }}"
required
placeholder="Enter a descriptive title for this risk assessment">
<div class="form-text">
Provide a clear, descriptive title that identifies the risk being assessed.
</div>
</div>
</div>
<div class="col-md-4">
<div class="mb-3">
<label for="assessment_id" class="form-label">
<strong>Assessment ID</strong>
</label>
<input type="text"
class="form-control"
id="assessment_id"
name="assessment_id"
value="{{ object.assessment_id|default:"" }}"
placeholder="Auto-generated">
<div class="form-text">
Leave blank for auto-generation.
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-4">
<div class="mb-3">
<label for="risk_category" class="form-label">
<strong>Risk Category *</strong>
</label>
<select class="form-select" id="risk_category" name="risk_category" required>
<option value="">Select category</option>
<option value="clinical" {% if object and object.risk_category == 'clinical' %}selected{% endif %}>Clinical</option>
<option value="operational" {% if object and object.risk_category == 'operational' %}selected{% endif %}>Operational</option>
<option value="financial" {% if object and object.risk_category == 'financial' %}selected{% endif %}>Financial</option>
<option value="regulatory" {% if object and object.risk_category == 'regulatory' %}selected{% endif %}>Regulatory</option>
<option value="technology" {% if object and object.risk_category == 'technology' %}selected{% endif %}>Technology</option>
<option value="environmental" {% if object and object.risk_category == 'environmental' %}selected{% endif %}>Environmental</option>
<option value="security" {% if object and object.risk_category == 'security' %}selected{% endif %}>Security</option>
<option value="reputation" {% if object and object.risk_category == 'reputation' %}selected{% endif %}>Reputation</option>
</select>
</div>
</div>
<div class="col-md-4">
<div class="mb-3">
<label for="department" class="form-label">
<strong>Department</strong>
</label>
<select class="form-select" id="department" name="department">
<option value="">All Departments</option>
{% for dept in departments %}
<option value="{{ dept.pk }}" {% if object and object.department.pk == dept.pk %}selected{% endif %}>
{{ dept.name }}
</option>
{% endfor %}
</select>
</div>
</div>
<div class="col-md-4">
<div class="mb-3">
<label for="priority" class="form-label">
<strong>Priority *</strong>
</label>
<select class="form-select" id="priority" name="priority" required>
<option value="">Select priority</option>
<option value="low" {% if object and object.priority == 'low' %}selected{% endif %}>Low</option>
<option value="medium" {% if object and object.priority == 'medium' %}selected{% endif %}>Medium</option>
<option value="high" {% if object and object.priority == 'high' %}selected{% endif %}>High</option>
<option value="critical" {% if object and object.priority == 'critical' %}selected{% endif %}>Critical</option>
</select>
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="mb-3">
<label for="assessment_date" class="form-label">
<strong>Assessment Date *</strong>
</label>
<input type="date"
class="form-control"
id="assessment_date"
name="assessment_date"
value="{{ object.assessment_date|date:"Y-m-d"|default:"" }}"
required>
</div>
</div>
<div class="col-md-6">
<div class="mb-3">
<label for="review_date" class="form-label">
<strong>Next Review Date</strong>
</label>
<input type="date"
class="form-control"
id="review_date"
name="review_date"
value="{{ object.review_date|date:"Y-m-d"|default:"" }}">
<div class="form-text">
When should this assessment be reviewed again?
</div>
</div>
</div>
</div>
<div class="mb-3">
<label for="description" class="form-label">
<strong>Risk Description *</strong>
</label>
<textarea class="form-control"
id="description"
name="description"
rows="4"
required
placeholder="Describe the risk in detail, including potential causes and consequences...">{{ object.description|default:"" }}</textarea>
<div class="form-text">
Provide a comprehensive description of the risk, including what could go wrong and why.
</div>
</div>
</div>
</div>
<!-- Risk Factors -->
<div class="card mb-4">
<div class="card-header">
<div class="d-flex justify-content-between align-items-center">
<h4 class="card-title mb-0">
<i class="fas fa-exclamation-triangle me-2"></i>
Risk Factors
</h4>
<button type="button" class="btn btn-sm btn-primary" onclick="addRiskFactor()">
<i class="fas fa-plus me-1"></i>Add Factor
</button>
</div>
</div>
<div class="card-body">
<div id="riskFactors">
<!-- Risk factors will be dynamically added here -->
</div>
<div class="text-center text-muted py-3" id="noFactorsMessage">
<i class="fas fa-exclamation-triangle fa-2x mb-2"></i>
<p>No risk factors added yet. Click "Add Factor" to begin.</p>
</div>
</div>
</div>
<!-- Mitigation Strategies -->
<div class="card mb-4">
<div class="card-header">
<h4 class="card-title">
<i class="fas fa-shield-alt me-2"></i>
Mitigation Strategies
</h4>
</div>
<div class="card-body">
<div class="mb-3">
<label for="mitigation_strategies" class="form-label">
<strong>Current Mitigation Strategies</strong>
</label>
<textarea class="form-control"
id="mitigation_strategies"
name="mitigation_strategies"
rows="4"
placeholder="Describe existing controls and mitigation measures...">{{ object.mitigation_strategies|default:"" }}</textarea>
<div class="form-text">
List current controls, procedures, and measures in place to mitigate this risk.
</div>
</div>
<div class="mb-3">
<label for="implementation_plan" class="form-label">
<strong>Implementation Plan</strong>
</label>
<textarea class="form-control"
id="implementation_plan"
name="implementation_plan"
rows="4"
placeholder="Describe the plan for implementing additional mitigation measures...">{{ object.implementation_plan|default:"" }}</textarea>
<div class="form-text">
Outline steps to implement new or improved risk mitigation measures.
</div>
</div>
<div class="mb-3">
<label for="monitoring_plan" class="form-label">
<strong>Monitoring Plan</strong>
</label>
<textarea class="form-control"
id="monitoring_plan"
name="monitoring_plan"
rows="3"
placeholder="Describe how this risk will be monitored and measured...">{{ object.monitoring_plan|default:"" }}</textarea>
<div class="form-text">
How will you monitor the effectiveness of mitigation strategies?
</div>
</div>
</div>
</div>
<!-- Additional Information -->
<div class="card mb-4">
<div class="card-header">
<h4 class="card-title">
<i class="fas fa-cogs me-2"></i>
Additional Information
</h4>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-6">
<div class="mb-3">
<label for="regulatory_requirements" class="form-label">
<strong>Regulatory Requirements</strong>
</label>
<textarea class="form-control"
id="regulatory_requirements"
name="regulatory_requirements"
rows="3"
placeholder="List relevant regulatory requirements...">{{ object.regulatory_requirements|default:"" }}</textarea>
</div>
</div>
<div class="col-md-6">
<div class="mb-3">
<label for="stakeholders" class="form-label">
<strong>Key Stakeholders</strong>
</label>
<textarea class="form-control"
id="stakeholders"
name="stakeholders"
rows="3"
placeholder="List key stakeholders and their roles...">{{ object.stakeholders|default:"" }}</textarea>
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="mb-3">
<label for="resources_required" class="form-label">
<strong>Resources Required</strong>
</label>
<textarea class="form-control"
id="resources_required"
name="resources_required"
rows="3"
placeholder="List resources needed for mitigation...">{{ object.resources_required|default:"" }}</textarea>
</div>
</div>
<div class="col-md-6">
<div class="mb-3">
<label for="success_criteria" class="form-label">
<strong>Success Criteria</strong>
</label>
<textarea class="form-control"
id="success_criteria"
name="success_criteria"
rows="3"
placeholder="Define success criteria for risk mitigation...">{{ object.success_criteria|default:"" }}</textarea>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="col-xl-4">
<!-- Risk Matrix -->
<div class="card mb-4">
<div class="card-header">
<h5 class="card-title">
<i class="fas fa-th me-2"></i>
Risk Matrix
</h5>
</div>
<div class="card-body">
<div class="risk-matrix">
<table class="table table-bordered text-center">
<thead>
<tr>
<th></th>
<th colspan="5">Likelihood</th>
</tr>
<tr>
<th>Impact</th>
<th>1</th>
<th>2</th>
<th>3</th>
<th>4</th>
<th>5</th>
</tr>
</thead>
<tbody>
{% for i in "54321" %}
<tr>
<th>{{ i }}</th>
{% for j in "12345" %}
<td class="risk-cell risk-{{ i|add:j|floatformat:0 }}"
data-risk="{{ i|mul:j }}"
onclick="selectRiskLevel({{ i }}, {{ j }})">
{{ i|mul:j }}
</td>
{% endfor %}
</tr>
{% endfor %}
</tbody>
</table>
<div class="mt-2">
<small class="text-muted">
Click on a cell to set the overall risk level.
</small>
</div>
</div>
</div>
</div>
<!-- Risk Score Summary -->
<div class="card mb-4">
<div class="card-header">
<h5 class="card-title">
<i class="fas fa-calculator me-2"></i>
Risk Score Summary
</h5>
</div>
<div class="card-body">
<div class="mb-3">
<label for="overall_risk_score" class="form-label">
<strong>Overall Risk Score</strong>
</label>
<input type="number"
class="form-control"
id="overall_risk_score"
name="overall_risk_score"
value="{{ object.overall_risk_score|default:"" }}"
min="1"
max="25"
readonly>
<div class="form-text">
Calculated from risk factors or matrix selection.
</div>
</div>
<div class="mb-3">
<label for="risk_level" class="form-label">
<strong>Risk Level</strong>
</label>
<select class="form-select" id="risk_level" name="risk_level">
<option value="">Select level</option>
<option value="very_low" {% if object and object.risk_level == 'very_low' %}selected{% endif %}>Very Low</option>
<option value="low" {% if object and object.risk_level == 'low' %}selected{% endif %}>Low</option>
<option value="medium" {% if object and object.risk_level == 'medium' %}selected{% endif %}>Medium</option>
<option value="high" {% if object and object.risk_level == 'high' %}selected{% endif %}>High</option>
<option value="very_high" {% if object and object.risk_level == 'very_high' %}selected{% endif %}>Very High</option>
</select>
</div>
<div class="alert alert-info">
<h6 class="alert-heading">Risk Level Guidelines</h6>
<ul class="mb-0 small">
<li><strong>1-4:</strong> Very Low - Minimal impact</li>
<li><strong>5-9:</strong> Low - Minor impact</li>
<li><strong>10-14:</strong> Medium - Moderate impact</li>
<li><strong>15-19:</strong> High - Significant impact</li>
<li><strong>20-25:</strong> Very High - Severe impact</li>
</ul>
</div>
</div>
</div>
<!-- Assessment Status -->
<div class="card mb-4">
<div class="card-header">
<h5 class="card-title">
<i class="fas fa-flag me-2"></i>
Assessment Status
</h5>
</div>
<div class="card-body">
<div class="mb-3">
<label for="status" class="form-label">
<strong>Status</strong>
</label>
<select class="form-select" id="status" name="status">
<option value="draft" {% if not object or object.status == 'draft' %}selected{% endif %}>Draft</option>
<option value="in_review" {% if object and object.status == 'in_review' %}selected{% endif %}>In Review</option>
<option value="approved" {% if object and object.status == 'approved' %}selected{% endif %}>Approved</option>
<option value="implemented" {% if object and object.status == 'implemented' %}selected{% endif %}>Implemented</option>
<option value="archived" {% if object and object.status == 'archived' %}selected{% endif %}>Archived</option>
</select>
</div>
<div class="form-check mb-3">
<input class="form-check-input"
type="checkbox"
id="requires_approval"
name="requires_approval"
{% if not object or object.requires_approval %}checked{% endif %}>
<label class="form-check-label" for="requires_approval">
Requires management approval
</label>
</div>
<div class="form-check mb-3">
<input class="form-check-input"
type="checkbox"
id="is_confidential"
name="is_confidential"
{% if object and object.is_confidential %}checked{% endif %}>
<label class="form-check-label" for="is_confidential">
Confidential assessment
</label>
</div>
</div>
</div>
<!-- Quick Actions -->
<div class="card">
<div class="card-header">
<h5 class="card-title">
<i class="fas fa-bolt me-2"></i>
Quick Actions
</h5>
</div>
<div class="card-body">
<div class="d-grid gap-2">
<button type="button" class="btn btn-outline-primary" onclick="calculateRiskScore()">
<i class="fas fa-calculator me-2"></i>Calculate Risk Score
</button>
<button type="button" class="btn btn-outline-secondary" onclick="previewAssessment()">
<i class="fas fa-eye me-2"></i>Preview Assessment
</button>
<button type="button" class="btn btn-outline-info" onclick="saveAsDraft()">
<i class="fas fa-save me-2"></i>Save as Draft
</button>
</div>
</div>
</div>
</div>
</div>
<div class="d-flex justify-content-between mt-4">
<a href="{% url 'quality:risk_assessment_list' %}" class="btn btn-secondary">
<i class="fas fa-times me-2"></i>Cancel
</a>
<div>
<button type="button" class="btn btn-outline-primary me-2" onclick="saveAsDraft()">
<i class="fas fa-save me-2"></i>Save as Draft
</button>
<button type="submit" class="btn btn-primary">
<i class="fas fa-check me-2"></i>{% if object %}Update{% else %}Create{% endif %} Assessment
</button>
</div>
</div>
</form>
<!-- Risk Factor Template -->
<template id="riskFactorTemplate">
<div class="card mb-3 risk-factor-item">
<div class="card-header">
<div class="d-flex justify-content-between align-items-center">
<h6 class="mb-0">Risk Factor</h6>
<button type="button" class="btn btn-sm btn-outline-danger" onclick="removeRiskFactor(this)">
<i class="fas fa-trash"></i>
</button>
</div>
</div>
<div class="card-body">
<div class="mb-3">
<label class="form-label">Factor Name</label>
<input type="text" class="form-control factor-name" placeholder="Enter factor name">
</div>
<div class="mb-3">
<label class="form-label">Description</label>
<textarea class="form-control factor-description" rows="2" placeholder="Describe this risk factor"></textarea>
</div>
<div class="row">
<div class="col-md-4">
<label class="form-label">Likelihood (1-5)</label>
<select class="form-select factor-likelihood" onchange="updateFactorScore(this)">
<option value="">Select</option>
<option value="1">1 - Very Unlikely</option>
<option value="2">2 - Unlikely</option>
<option value="3">3 - Possible</option>
<option value="4">4 - Likely</option>
<option value="5">5 - Very Likely</option>
</select>
</div>
<div class="col-md-4">
<label class="form-label">Impact (1-5)</label>
<select class="form-select factor-impact" onchange="updateFactorScore(this)">
<option value="">Select</option>
<option value="1">1 - Minimal</option>
<option value="2">2 - Minor</option>
<option value="3">3 - Moderate</option>
<option value="4">4 - Major</option>
<option value="5">5 - Severe</option>
</select>
</div>
<div class="col-md-4">
<label class="form-label">Risk Score</label>
<input type="text" class="form-control factor-score" readonly placeholder="Auto-calculated">
</div>
</div>
</div>
</div>
</template>
<script>
let riskFactorCount = 0;
document.addEventListener('DOMContentLoaded', function() {
// Initialize form
updateNoFactorsMessage();
// Set default assessment date to today
if (!document.getElementById('assessment_date').value) {
document.getElementById('assessment_date').value = new Date().toISOString().split('T')[0];
}
});
function addRiskFactor() {
const template = document.getElementById('riskFactorTemplate');
const clone = template.content.cloneNode(true);
riskFactorCount++;
document.getElementById('riskFactors').appendChild(clone);
updateNoFactorsMessage();
}
function removeRiskFactor(button) {
button.closest('.risk-factor-item').remove();
updateNoFactorsMessage();
calculateRiskScore();
}
function updateNoFactorsMessage() {
const factors = document.querySelectorAll('.risk-factor-item');
const message = document.getElementById('noFactorsMessage');
if (factors.length === 0) {
message.style.display = 'block';
} else {
message.style.display = 'none';
}
}
function updateFactorScore(element) {
const factorItem = element.closest('.risk-factor-item');
const likelihood = factorItem.querySelector('.factor-likelihood').value;
const impact = factorItem.querySelector('.factor-impact').value;
const scoreField = factorItem.querySelector('.factor-score');
if (likelihood && impact) {
const score = parseInt(likelihood) * parseInt(impact);
scoreField.value = score;
// Update score styling
scoreField.className = 'form-control factor-score';
if (score >= 15) {
scoreField.classList.add('bg-danger', 'text-white');
} else if (score >= 10) {
scoreField.classList.add('bg-warning');
} else if (score >= 5) {
scoreField.classList.add('bg-info', 'text-white');
} else {
scoreField.classList.add('bg-success', 'text-white');
}
} else {
scoreField.value = '';
scoreField.className = 'form-control factor-score';
}
calculateRiskScore();
}
function calculateRiskScore() {
const factorScores = document.querySelectorAll('.factor-score');
let totalScore = 0;
let factorCount = 0;
factorScores.forEach(score => {
if (score.value) {
totalScore += parseInt(score.value);
factorCount++;
}
});
if (factorCount > 0) {
const averageScore = Math.round(totalScore / factorCount);
document.getElementById('overall_risk_score').value = averageScore;
updateRiskLevel(averageScore);
}
}
function updateRiskLevel(score) {
const riskLevelSelect = document.getElementById('risk_level');
if (score >= 20) {
riskLevelSelect.value = 'very_high';
} else if (score >= 15) {
riskLevelSelect.value = 'high';
} else if (score >= 10) {
riskLevelSelect.value = 'medium';
} else if (score >= 5) {
riskLevelSelect.value = 'low';
} else {
riskLevelSelect.value = 'very_low';
}
}
function selectRiskLevel(impact, likelihood) {
const score = impact * likelihood;
document.getElementById('overall_risk_score').value = score;
updateRiskLevel(score);
// Highlight selected cell
document.querySelectorAll('.risk-cell').forEach(cell => {
cell.classList.remove('selected');
});
event.target.classList.add('selected');
}
function previewAssessment() {
const title = document.getElementById('title').value;
const category = document.getElementById('risk_category').value;
const priority = document.getElementById('priority').value;
const score = document.getElementById('overall_risk_score').value;
if (!title) {
alert('Please enter an assessment title first.');
return;
}
const preview = `
Risk Assessment Preview:
Title: ${title}
Category: ${category || 'Not specified'}
Priority: ${priority || 'Not specified'}
Risk Score: ${score || 'Not calculated'}
This is a preview of how the assessment will appear.
`;
alert(preview);
}
function saveAsDraft() {
document.getElementById('status').value = 'draft';
document.getElementById('riskAssessmentForm').submit();
}
// Form validation
document.getElementById('riskAssessmentForm').addEventListener('submit', function(e) {
const title = document.getElementById('title').value;
const category = document.getElementById('risk_category').value;
const description = document.getElementById('description').value;
if (!title || !category || !description) {
e.preventDefault();
alert('Please fill in all required fields (Title, Category, Description).');
return false;
}
return true;
});
</script>
<style>
.risk-matrix .risk-cell {
width: 40px;
height: 40px;
font-weight: bold;
cursor: pointer;
transition: all 0.2s;
}
.risk-matrix .risk-cell:hover {
transform: scale(1.1);
box-shadow: 0 2px 4px rgba(0,0,0,0.2);
}
.risk-matrix .risk-cell.selected {
border: 3px solid #007bff;
box-shadow: 0 0 10px rgba(0,123,255,0.5);
}
.risk-cell.risk-1, .risk-cell.risk-2 { background-color: #d4edda; color: #155724; }
.risk-cell.risk-3, .risk-cell.risk-4, .risk-cell.risk-6 { background-color: #fff3cd; color: #856404; }
.risk-cell.risk-5, .risk-cell.risk-8, .risk-cell.risk-9, .risk-cell.risk-10 { background-color: #f8d7da; color: #721c24; }
.risk-cell.risk-12, .risk-cell.risk-15, .risk-cell.risk-16, .risk-cell.risk-20, .risk-cell.risk-25 { background-color: #721c24; color: white; }
.risk-factor-item {
border-left: 4px solid #007bff;
}
</style>
{% endblock %}