Marwan Alwali 0a037d3d9d update
2025-09-01 11:26:11 +03:00

1214 lines
44 KiB
HTML

{% extends 'base.html' %}
{% load static %}
{% block title %}{% if project %}Edit Project{% else %}New Project{% endif %} - Quality Management{% endblock %}
{% block css %}
<link href="{% static 'plugins/select2/dist/css/select2.min.css' %}" rel="stylesheet" />
<link href="{% static 'plugins/bootstrap-datepicker/dist/css/bootstrap-datepicker.min.css' %}" rel="stylesheet" />
<style>
.form-header {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
border-radius: 0.5rem;
padding: 2rem;
margin-bottom: 2rem;
}
.form-layout {
display: grid;
grid-template-columns: 2fr 1fr;
gap: 2rem;
margin-bottom: 2rem;
}
.form-main {
display: flex;
flex-direction: column;
gap: 2rem;
}
.form-sidebar {
display: flex;
flex-direction: column;
gap: 1.5rem;
}
.form-section {
background: white;
border: 1px solid #dee2e6;
border-radius: 0.5rem;
overflow: hidden;
}
.section-header {
background: #f8f9fa;
border-bottom: 1px solid #dee2e6;
padding: 1rem 1.5rem;
font-weight: 600;
color: #495057;
display: flex;
align-items: center;
gap: 0.5rem;
}
.section-content {
padding: 1.5rem;
}
.form-row {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 1rem;
margin-bottom: 1rem;
}
.form-row.single {
grid-template-columns: 1fr;
}
.form-group {
margin-bottom: 1rem;
}
.form-label {
font-weight: 600;
color: #495057;
margin-bottom: 0.5rem;
}
.form-label.required::after {
content: ' *';
color: #dc3545;
}
.form-control, .form-select {
border: 1px solid #dee2e6;
border-radius: 0.375rem;
padding: 0.75rem;
transition: all 0.2s;
}
.form-control:focus, .form-select:focus {
border-color: #007bff;
box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);
}
.form-text {
font-size: 0.875rem;
color: #6c757d;
margin-top: 0.25rem;
}
.priority-selector {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 0.5rem;
}
.priority-option {
padding: 0.75rem;
border: 2px solid #dee2e6;
border-radius: 0.375rem;
text-align: center;
cursor: pointer;
transition: all 0.2s;
background: white;
}
.priority-option:hover {
border-color: #007bff;
}
.priority-option.selected {
border-color: #007bff;
background: #e3f2fd;
color: #1976d2;
}
.priority-low.selected { border-color: #28a745; background: #e8f5e8; color: #2e7d32; }
.priority-medium.selected { border-color: #ffc107; background: #fff3e0; color: #f57c00; }
.priority-high.selected { border-color: #dc3545; background: #ffebee; color: #d32f2f; }
.priority-critical.selected { border-color: #6f42c1; background: #f3e5f5; color: #7b1fa2; }
.status-selector {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
gap: 0.5rem;
}
.status-option {
padding: 0.75rem;
border: 2px solid #dee2e6;
border-radius: 0.375rem;
text-align: center;
cursor: pointer;
transition: all 0.2s;
background: white;
font-size: 0.875rem;
}
.status-option:hover {
border-color: #007bff;
}
.status-option.selected {
border-color: #007bff;
background: #e3f2fd;
color: #1976d2;
}
.team-selector {
border: 1px solid #dee2e6;
border-radius: 0.375rem;
max-height: 200px;
overflow-y: auto;
}
.team-member {
display: flex;
align-items: center;
gap: 0.75rem;
padding: 0.75rem;
border-bottom: 1px solid #f0f0f0;
cursor: pointer;
transition: background 0.2s;
}
.team-member:hover {
background: #f8f9fa;
}
.team-member:last-child {
border-bottom: none;
}
.team-member.selected {
background: #e3f2fd;
}
.member-avatar {
width: 32px;
height: 32px;
border-radius: 50%;
background: #007bff;
color: white;
display: flex;
align-items: center;
justify-content: center;
font-size: 0.75rem;
font-weight: 600;
flex-shrink: 0;
}
.member-info {
flex: 1;
}
.member-name {
font-weight: 600;
color: #495057;
margin-bottom: 0.25rem;
}
.member-role {
font-size: 0.75rem;
color: #6c757d;
}
.milestone-item {
border: 1px solid #dee2e6;
border-radius: 0.375rem;
padding: 1rem;
margin-bottom: 1rem;
background: white;
}
.milestone-header {
display: flex;
justify-content: between;
align-items: center;
margin-bottom: 0.75rem;
}
.milestone-title {
font-weight: 600;
color: #495057;
}
.milestone-actions {
display: flex;
gap: 0.25rem;
}
.btn-icon {
width: 32px;
height: 32px;
border: 1px solid #dee2e6;
border-radius: 0.25rem;
background: white;
color: #6c757d;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: all 0.2s;
}
.btn-icon:hover {
border-color: #007bff;
color: #007bff;
}
.btn-icon.danger:hover {
border-color: #dc3545;
color: #dc3545;
}
.milestone-form {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 1rem;
}
.deliverable-item {
border: 1px solid #dee2e6;
border-radius: 0.375rem;
padding: 1rem;
margin-bottom: 1rem;
background: white;
}
.deliverable-header {
display: flex;
justify-content: between;
align-items: start;
margin-bottom: 0.75rem;
}
.deliverable-title {
font-weight: 600;
color: #495057;
margin-bottom: 0.25rem;
}
.deliverable-type {
font-size: 0.75rem;
color: #6c757d;
}
.deliverable-form {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 1rem;
}
.progress-slider {
margin: 1rem 0;
}
.progress-value {
text-align: center;
font-size: 1.25rem;
font-weight: bold;
color: #007bff;
margin-bottom: 0.5rem;
}
.progress-bar-container {
background: #e9ecef;
border-radius: 0.5rem;
height: 8px;
overflow: hidden;
margin-bottom: 0.5rem;
}
.progress-bar {
background: linear-gradient(90deg, #007bff, #0056b3);
height: 100%;
transition: width 0.3s ease;
border-radius: 0.5rem;
}
.form-actions {
background: white;
border: 1px solid #dee2e6;
border-radius: 0.5rem;
padding: 1.5rem;
display: flex;
justify-content: between;
align-items: center;
gap: 1rem;
}
.btn-group {
display: flex;
gap: 0.5rem;
}
.auto-save-indicator {
display: flex;
align-items: center;
gap: 0.5rem;
color: #6c757d;
font-size: 0.875rem;
}
.auto-save-indicator.saving {
color: #ffc107;
}
.auto-save-indicator.saved {
color: #28a745;
}
.validation-errors {
background: #f8d7da;
border: 1px solid #f5c6cb;
border-radius: 0.375rem;
padding: 1rem;
margin-bottom: 1rem;
}
.validation-errors ul {
margin: 0;
padding-left: 1.5rem;
}
.validation-errors li {
color: #721c24;
}
@media (max-width: 1200px) {
.form-layout {
grid-template-columns: 1fr;
gap: 1.5rem;
}
.form-sidebar {
order: -1;
}
}
@media (max-width: 768px) {
.form-header {
padding: 1.5rem;
}
.form-row {
grid-template-columns: 1fr;
}
.priority-selector {
grid-template-columns: repeat(2, 1fr);
}
.status-selector {
grid-template-columns: 1fr;
}
.milestone-form, .deliverable-form {
grid-template-columns: 1fr;
}
.form-actions {
flex-direction: column;
align-items: stretch;
}
.btn-group {
justify-content: center;
}
}
@media print {
.form-sidebar, .form-actions {
display: none !important;
}
.form-layout {
grid-template-columns: 1fr;
}
.section-header {
background: none;
border-bottom: 2px solid #000;
color: #000;
}
}
</style>
{% endblock %}
{% block content %}
<div id="content" class="app-content">
<!-- Page Header -->
<div class="d-flex align-items-center mb-3">
<div>
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="{% url 'core:dashboard' %}">Dashboard</a></li>
<li class="breadcrumb-item"><a href="{% url 'quality:dashboard' %}">Quality</a></li>
<li class="breadcrumb-item"><a href="{% url 'quality:improvement_project_list' %}">Projects</a></li>
<li class="breadcrumb-item active">{% if project %}Edit{% else %}New{% endif %} Project</li>
</ol>
<h1 class="page-header mb-0">
<i class="fas fa-project-diagram me-2"></i>{% if project %}Edit Project{% else %}New Quality Project{% endif %}
</h1>
</div>
<div class="ms-auto">
<a href="{% url 'quality:improvement_project_list' %}" class="btn btn-outline-secondary">
<i class="fas fa-arrow-left me-1"></i>Back to Projects
</a>
</div>
</div>
<!-- Form Header -->
<div class="form-header">
<div class="row align-items-center">
<div class="col-md-8">
<h2 class="mb-2">{% if project %}{{ project.name }}{% else %}Create New Quality Project{% endif %}</h2>
<p class="mb-0">{% if project %}Update project details and settings{% else %}Define project scope, objectives, and team assignments{% endif %}</p>
</div>
<div class="col-md-4 text-md-end">
<div class="auto-save-indicator" id="auto-save-status">
<i class="fas fa-save"></i>
<span>Auto-save enabled</span>
</div>
</div>
</div>
</div>
<form method="post" id="project-form" enctype="multipart/form-data">
{% csrf_token %}
<!-- Validation Errors -->
{% if form.errors %}
<div class="validation-errors">
<h6><i class="fas fa-exclamation-triangle me-2"></i>Please correct the following errors:</h6>
<ul>
{% for field, errors in form.errors.items %}
{% for error in errors %}
<li>{{ field|title }}: {{ error }}</li>
{% endfor %}
{% endfor %}
</ul>
</div>
{% endif %}
<div class="form-layout">
<!-- Main Form Content -->
<div class="form-main">
<!-- Basic Information -->
<div class="form-section">
<div class="section-header">
<i class="fas fa-info-circle"></i>
Basic Information
</div>
<div class="section-content">
<div class="form-row single">
<div class="form-group">
<label class="form-label required">Project Name</label>
<input type="text" class="form-control" name="name"
value="{{ form.name.value|default:'' }}"
placeholder="Enter project name..." required>
<div class="form-text">A clear, descriptive name for the quality project</div>
</div>
</div>
<div class="form-row">
<div class="form-group">
<label class="form-label">Project ID</label>
<input type="text" class="form-control" name="project_id"
value="{{ form.project_id.value|default:'' }}"
placeholder="Auto-generated if left blank">
<div class="form-text">Unique identifier for the project</div>
</div>
<div class="form-group">
<label class="form-label required">Department</label>
<select class="form-select" name="department" required>
<option value="">Select Department</option>
{% for dept in departments %}
<option value="{{ dept.id }}" {% if form.department.value == dept.id %}selected{% endif %}>
{{ dept.name }}
</option>
{% endfor %}
</select>
</div>
</div>
<div class="form-row single">
<div class="form-group">
<label class="form-label required">Description</label>
<textarea class="form-control" name="description" rows="4"
placeholder="Describe the project objectives, scope, and expected outcomes..." required>{{ form.description.value|default:'' }}</textarea>
<div class="form-text">Detailed description of project goals and scope</div>
</div>
</div>
</div>
</div>
<!-- Project Settings -->
<div class="form-section">
<div class="section-header">
<i class="fas fa-cog"></i>
Project Settings
</div>
<div class="section-content">
<div class="form-group">
<label class="form-label required">Priority Level</label>
<div class="priority-selector">
<div class="priority-option priority-low" data-priority="low">
<i class="fas fa-arrow-down mb-2"></i>
<div>Low</div>
</div>
<div class="priority-option priority-medium" data-priority="medium">
<i class="fas fa-minus mb-2"></i>
<div>Medium</div>
</div>
<div class="priority-option priority-high" data-priority="high">
<i class="fas fa-arrow-up mb-2"></i>
<div>High</div>
</div>
<div class="priority-option priority-critical" data-priority="critical">
<i class="fas fa-exclamation-triangle mb-2"></i>
<div>Critical</div>
</div>
</div>
<input type="hidden" name="priority" id="priority-input" value="{{ form.priority.value|default:'medium' }}">
</div>
<div class="form-group">
<label class="form-label required">Project Status</label>
<div class="status-selector">
<div class="status-option" data-status="planning">
<i class="fas fa-clipboard-list mb-1"></i>
<div>Planning</div>
</div>
<div class="status-option" data-status="active">
<i class="fas fa-play mb-1"></i>
<div>Active</div>
</div>
<div class="status-option" data-status="on-hold">
<i class="fas fa-pause mb-1"></i>
<div>On Hold</div>
</div>
<div class="status-option" data-status="completed">
<i class="fas fa-check mb-1"></i>
<div>Completed</div>
</div>
<div class="status-option" data-status="cancelled">
<i class="fas fa-times mb-1"></i>
<div>Cancelled</div>
</div>
</div>
<input type="hidden" name="status" id="status-input" value="{{ form.status.value|default:'planning' }}">
</div>
<div class="form-row">
<div class="form-group">
<label class="form-label">Start Date</label>
<input type="date" class="form-control" name="start_date"
value="{{ form.start_date.value|date:'Y-m-d'|default:'' }}">
</div>
<div class="form-group">
<label class="form-label">End Date</label>
<input type="date" class="form-control" name="end_date"
value="{{ form.end_date.value|date:'Y-m-d'|default:'' }}">
</div>
</div>
<div class="form-row">
<div class="form-group">
<label class="form-label">Budget</label>
<div class="input-group">
<span class="input-group-text">$</span>
<input type="number" class="form-control" name="budget"
value="{{ form.budget.value|default:'' }}"
placeholder="0.00" step="0.01" min="0">
</div>
</div>
<div class="form-group">
<label class="form-label">Project Manager</label>
<select class="form-select" name="manager">
<option value="">Select Manager</option>
{% for manager in managers %}
<option value="{{ manager.id }}" {% if form.manager.value == manager.id %}selected{% endif %}>
{{ manager.get_full_name }}
</option>
{% endfor %}
</select>
</div>
</div>
</div>
</div>
<!-- Progress Tracking -->
{% if project %}
<div class="form-section">
<div class="section-header">
<i class="fas fa-chart-line"></i>
Progress Tracking
</div>
<div class="section-content">
<div class="form-group">
<label class="form-label">Project Progress</label>
<div class="progress-slider">
<div class="progress-value" id="progress-value">{{ form.progress.value|default:0 }}%</div>
<div class="progress-bar-container">
<div class="progress-bar" id="progress-bar" style="width: {{ form.progress.value|default:0 }}%;"></div>
</div>
<input type="range" class="form-range" name="progress"
value="{{ form.progress.value|default:0 }}"
min="0" max="100" step="5" id="progress-slider">
</div>
<div class="form-text">Adjust the slider to reflect current project completion percentage</div>
</div>
</div>
</div>
{% endif %}
<!-- Milestones -->
<div class="form-section">
<div class="section-header">
<i class="fas fa-flag-checkered"></i>
Project Milestones
<div class="ms-auto">
<button type="button" class="btn btn-outline-primary btn-sm" onclick="addMilestone()">
<i class="fas fa-plus me-1"></i>Add Milestone
</button>
</div>
</div>
<div class="section-content">
<div id="milestones-container">
{% for milestone in project.milestones.all %}
<div class="milestone-item" data-milestone-id="{{ milestone.id }}">
<div class="milestone-header">
<div class="milestone-title">{{ milestone.name }}</div>
<div class="milestone-actions">
<button type="button" class="btn-icon" onclick="editMilestone(this)">
<i class="fas fa-edit"></i>
</button>
<button type="button" class="btn-icon danger" onclick="removeMilestone(this)">
<i class="fas fa-trash"></i>
</button>
</div>
</div>
<div class="milestone-form" style="display: none;">
<input type="text" class="form-control" placeholder="Milestone name" value="{{ milestone.name }}">
<input type="date" class="form-control" value="{{ milestone.due_date|date:'Y-m-d' }}">
<textarea class="form-control" placeholder="Description" rows="2">{{ milestone.description }}</textarea>
<select class="form-select">
<option value="">Assign to...</option>
{% for member in team_members %}
<option value="{{ member.id }}" {% if milestone.assignee_id == member.id %}selected{% endif %}>
{{ member.get_full_name }}
</option>
{% endfor %}
</select>
</div>
</div>
{% empty %}
<div class="text-center text-muted py-3">
<i class="fas fa-flag-checkered fa-2x mb-2"></i>
<p>No milestones defined yet. Add milestones to track project progress.</p>
</div>
{% endfor %}
</div>
</div>
</div>
<!-- Deliverables -->
<div class="form-section">
<div class="section-header">
<i class="fas fa-box"></i>
Project Deliverables
<div class="ms-auto">
<button type="button" class="btn btn-outline-primary btn-sm" onclick="addDeliverable()">
<i class="fas fa-plus me-1"></i>Add Deliverable
</button>
</div>
</div>
<div class="section-content">
<div id="deliverables-container">
{% for deliverable in project.deliverables.all %}
<div class="deliverable-item" data-deliverable-id="{{ deliverable.id }}">
<div class="deliverable-header">
<div>
<div class="deliverable-title">{{ deliverable.name }}</div>
<div class="deliverable-type">{{ deliverable.get_type_display }}</div>
</div>
<div class="milestone-actions">
<button type="button" class="btn-icon" onclick="editDeliverable(this)">
<i class="fas fa-edit"></i>
</button>
<button type="button" class="btn-icon danger" onclick="removeDeliverable(this)">
<i class="fas fa-trash"></i>
</button>
</div>
</div>
<div class="deliverable-form" style="display: none;">
<input type="text" class="form-control" placeholder="Deliverable name" value="{{ deliverable.name }}">
<select class="form-select">
<option value="document" {% if deliverable.type == 'document' %}selected{% endif %}>Document</option>
<option value="report" {% if deliverable.type == 'report' %}selected{% endif %}>Report</option>
<option value="presentation" {% if deliverable.type == 'presentation' %}selected{% endif %}>Presentation</option>
<option value="software" {% if deliverable.type == 'software' %}selected{% endif %}>Software</option>
<option value="other" {% if deliverable.type == 'other' %}selected{% endif %}>Other</option>
</select>
<textarea class="form-control" placeholder="Description" rows="2">{{ deliverable.description }}</textarea>
<input type="date" class="form-control" value="{{ deliverable.due_date|date:'Y-m-d' }}">
</div>
</div>
{% empty %}
<div class="text-center text-muted py-3">
<i class="fas fa-box fa-2x mb-2"></i>
<p>No deliverables defined yet. Add deliverables to track project outputs.</p>
</div>
{% endfor %}
</div>
</div>
</div>
</div>
<!-- Sidebar -->
<div class="form-sidebar">
<!-- Team Selection -->
<div class="form-section">
<div class="section-header">
<i class="fas fa-users"></i>
Team Members
</div>
<div class="section-content">
<div class="team-selector" id="team-selector">
{% for user in available_users %}
<div class="team-member" data-user-id="{{ user.id }}">
<div class="form-check">
<input class="form-check-input" type="checkbox" name="team_members"
value="{{ user.id }}" id="team-{{ user.id }}"
{% if user in project.team_members.all %}checked{% endif %}>
</div>
<div class="member-avatar">
{{ user.first_name.0|upper }}{{ user.last_name.0|upper }}
</div>
<div class="member-info">
<div class="member-name">{{ user.get_full_name }}</div>
<div class="member-role">{{ user.profile.role|default:"Staff" }}</div>
</div>
</div>
{% endfor %}
</div>
<div class="form-text">Select team members who will work on this project</div>
</div>
</div>
<!-- Project Categories -->
<div class="form-section">
<div class="section-header">
<i class="fas fa-tags"></i>
Categories & Tags
</div>
<div class="section-content">
<div class="form-group">
<label class="form-label">Project Category</label>
<select class="form-select" name="category">
<option value="">Select Category</option>
<option value="process-improvement" {% if form.category.value == 'process-improvement' %}selected{% endif %}>Process Improvement</option>
<option value="compliance" {% if form.category.value == 'compliance' %}selected{% endif %}>Compliance</option>
<option value="safety" {% if form.category.value == 'safety' %}selected{% endif %}>Safety</option>
<option value="training" {% if form.category.value == 'training' %}selected{% endif %}>Training</option>
<option value="technology" {% if form.category.value == 'technology' %}selected{% endif %}>Technology</option>
<option value="research" {% if form.category.value == 'research' %}selected{% endif %}>Research</option>
</select>
</div>
<div class="form-group">
<label class="form-label">Tags</label>
<input type="text" class="form-control" name="tags"
value="{{ form.tags.value|default:'' }}"
placeholder="Enter tags separated by commas">
<div class="form-text">Use tags to categorize and search projects</div>
</div>
</div>
</div>
<!-- Risk Assessment -->
<div class="form-section">
<div class="section-header">
<i class="fas fa-exclamation-triangle"></i>
Risk Assessment
</div>
<div class="section-content">
<div class="form-group">
<label class="form-label">Overall Risk Level</label>
<select class="form-select" name="risk_level">
<option value="low" {% if form.risk_level.value == 'low' %}selected{% endif %}>Low Risk</option>
<option value="medium" {% if form.risk_level.value == 'medium' %}selected{% endif %}>Medium Risk</option>
<option value="high" {% if form.risk_level.value == 'high' %}selected{% endif %}>High Risk</option>
</select>
</div>
<div class="form-group">
<label class="form-label">Risk Notes</label>
<textarea class="form-control" name="risk_notes" rows="3"
placeholder="Describe potential risks and mitigation strategies...">{{ form.risk_notes.value|default:'' }}</textarea>
</div>
</div>
</div>
<!-- Additional Information -->
<div class="form-section">
<div class="section-header">
<i class="fas fa-info"></i>
Additional Info
</div>
<div class="section-content">
<div class="form-group">
<label class="form-label">Success Criteria</label>
<textarea class="form-control" name="success_criteria" rows="3"
placeholder="Define what success looks like for this project...">{{ form.success_criteria.value|default:'' }}</textarea>
</div>
<div class="form-group">
<label class="form-label">Notes</label>
<textarea class="form-control" name="notes" rows="3"
placeholder="Additional notes or comments...">{{ form.notes.value|default:'' }}</textarea>
</div>
</div>
</div>
</div>
</div>
<!-- Form Actions -->
<div class="form-actions">
<div class="auto-save-indicator" id="auto-save-indicator">
<i class="fas fa-save"></i>
<span>All changes saved</span>
</div>
<div class="btn-group">
<a href="{% url 'quality:improvement_project_list' %}" class="btn btn-outline-secondary">
<i class="fas fa-times me-1"></i>Cancel
</a>
<button type="button" class="btn btn-outline-info" onclick="saveDraft()">
<i class="fas fa-save me-1"></i>Save Draft
</button>
<button type="submit" class="btn btn-primary">
<i class="fas fa-check me-1"></i>{% if project %}Update Project{% else %}Create Project{% endif %}
</button>
</div>
</div>
</form>
</div>
{% endblock %}
{% block js %}
<script src="{% static 'plugins/select2/dist/js/select2.min.js' %}"></script>
<script src="{% static 'plugins/bootstrap-datepicker/dist/js/bootstrap-datepicker.min.js' %}"></script>
<script>
let autoSaveTimer;
let milestoneCounter = 0;
let deliverableCounter = 0;
$(document).ready(function() {
// Initialize form
initializeForm();
// Set up auto-save
setupAutoSave();
// Initialize priority and status selectors
initializePrioritySelector();
initializeStatusSelector();
// Initialize progress slider
initializeProgressSlider();
// Set initial values
setInitialValues();
});
function initializeForm() {
// Initialize Select2 for dropdowns
$('.form-select').select2({
width: '100%',
placeholder: function() {
return $(this).data('placeholder') || 'Select an option...';
}
});
// Initialize date pickers
$('input[type="date"]').datepicker({
format: 'yyyy-mm-dd',
autoclose: true,
todayHighlight: true
});
}
function setupAutoSave() {
// Auto-save form data every 30 seconds
$('#project-form input, #project-form textarea, #project-form select').on('input change', function() {
clearTimeout(autoSaveTimer);
$('#auto-save-indicator').removeClass('saved').addClass('saving');
$('#auto-save-indicator span').text('Saving...');
autoSaveTimer = setTimeout(function() {
saveDraft();
}, 2000);
});
}
function initializePrioritySelector() {
$('.priority-option').click(function() {
$('.priority-option').removeClass('selected');
$(this).addClass('selected');
$('#priority-input').val($(this).data('priority'));
});
}
function initializeStatusSelector() {
$('.status-option').click(function() {
$('.status-option').removeClass('selected');
$(this).addClass('selected');
$('#status-input').val($(this).data('status'));
});
}
function initializeProgressSlider() {
$('#progress-slider').on('input', function() {
const value = $(this).val();
$('#progress-value').text(value + '%');
$('#progress-bar').css('width', value + '%');
});
}
function setInitialValues() {
// Set initial priority
const initialPriority = $('#priority-input').val();
$(`.priority-option[data-priority="${initialPriority}"]`).addClass('selected');
// Set initial status
const initialStatus = $('#status-input').val();
$(`.status-option[data-status="${initialStatus}"]`).addClass('selected');
}
function addMilestone() {
milestoneCounter++;
const milestoneHtml = `
<div class="milestone-item" data-milestone-id="new-${milestoneCounter}">
<div class="milestone-header">
<div class="milestone-title">New Milestone</div>
<div class="milestone-actions">
<button type="button" class="btn-icon" onclick="editMilestone(this)">
<i class="fas fa-edit"></i>
</button>
<button type="button" class="btn-icon danger" onclick="removeMilestone(this)">
<i class="fas fa-trash"></i>
</button>
</div>
</div>
<div class="milestone-form">
<input type="text" class="form-control" placeholder="Milestone name" value="New Milestone">
<input type="date" class="form-control">
<textarea class="form-control" placeholder="Description" rows="2"></textarea>
<select class="form-select">
<option value="">Assign to...</option>
{% for member in team_members %}
<option value="{{ member.id }}">{{ member.get_full_name }}</option>
{% endfor %}
</select>
</div>
</div>
`;
$('#milestones-container').append(milestoneHtml);
editMilestone($('#milestones-container .milestone-item:last .btn-icon:first')[0]);
}
function editMilestone(button) {
const milestoneItem = $(button).closest('.milestone-item');
const form = milestoneItem.find('.milestone-form');
const header = milestoneItem.find('.milestone-header');
if (form.is(':visible')) {
// Save changes
const name = form.find('input[type="text"]').val();
header.find('.milestone-title').text(name || 'Unnamed Milestone');
form.hide();
$(button).html('<i class="fas fa-edit"></i>');
} else {
// Show form
form.show();
$(button).html('<i class="fas fa-save"></i>');
}
}
function removeMilestone(button) {
if (confirm('Remove this milestone?')) {
$(button).closest('.milestone-item').remove();
}
}
function addDeliverable() {
deliverableCounter++;
const deliverableHtml = `
<div class="deliverable-item" data-deliverable-id="new-${deliverableCounter}">
<div class="deliverable-header">
<div>
<div class="deliverable-title">New Deliverable</div>
<div class="deliverable-type">Document</div>
</div>
<div class="milestone-actions">
<button type="button" class="btn-icon" onclick="editDeliverable(this)">
<i class="fas fa-edit"></i>
</button>
<button type="button" class="btn-icon danger" onclick="removeDeliverable(this)">
<i class="fas fa-trash"></i>
</button>
</div>
</div>
<div class="deliverable-form">
<input type="text" class="form-control" placeholder="Deliverable name" value="New Deliverable">
<select class="form-select">
<option value="document">Document</option>
<option value="report">Report</option>
<option value="presentation">Presentation</option>
<option value="software">Software</option>
<option value="other">Other</option>
</select>
<textarea class="form-control" placeholder="Description" rows="2"></textarea>
<input type="date" class="form-control">
</div>
</div>
`;
$('#deliverables-container').append(deliverableHtml);
editDeliverable($('#deliverables-container .deliverable-item:last .btn-icon:first')[0]);
}
function editDeliverable(button) {
const deliverableItem = $(button).closest('.deliverable-item');
const form = deliverableItem.find('.deliverable-form');
const header = deliverableItem.find('.deliverable-header');
if (form.is(':visible')) {
// Save changes
const name = form.find('input[type="text"]').val();
const type = form.find('select').val();
header.find('.deliverable-title').text(name || 'Unnamed Deliverable');
header.find('.deliverable-type').text(type.charAt(0).toUpperCase() + type.slice(1));
form.hide();
$(button).html('<i class="fas fa-edit"></i>');
} else {
// Show form
form.show();
$(button).html('<i class="fas fa-save"></i>');
}
}
function removeDeliverable(button) {
if (confirm('Remove this deliverable?')) {
$(button).closest('.deliverable-item').remove();
}
}
function saveDraft() {
const formData = new FormData($('#project-form')[0]);
formData.append('save_draft', 'true');
fetch(window.location.href, {
method: 'POST',
headers: {
'X-CSRFToken': document.querySelector('[name=csrfmiddlewaretoken]').value
},
body: formData
})
.then(response => response.json())
.then(data => {
if (data.success) {
$('#auto-save-indicator').removeClass('saving').addClass('saved');
$('#auto-save-indicator span').text('Draft saved');
setTimeout(() => {
$('#auto-save-indicator').removeClass('saved');
$('#auto-save-indicator span').text('Auto-save enabled');
}, 3000);
}
})
.catch(error => {
console.error('Auto-save error:', error);
$('#auto-save-indicator').removeClass('saving');
$('#auto-save-indicator span').text('Save failed');
});
}
// Team member selection
$('#team-selector .team-member').click(function(e) {
if (e.target.type !== 'checkbox') {
const checkbox = $(this).find('input[type="checkbox"]');
checkbox.prop('checked', !checkbox.prop('checked'));
}
$(this).toggleClass('selected', $(this).find('input[type="checkbox"]').prop('checked'));
});
// Form validation
$('#project-form').on('submit', function(e) {
let isValid = true;
const errors = [];
// Validate required fields
if (!$('input[name="name"]').val().trim()) {
errors.push('Project name is required');
isValid = false;
}
if (!$('textarea[name="description"]').val().trim()) {
errors.push('Project description is required');
isValid = false;
}
if (!$('select[name="department"]').val()) {
errors.push('Department selection is required');
isValid = false;
}
// Validate date range
const startDate = new Date($('input[name="start_date"]').val());
const endDate = new Date($('input[name="end_date"]').val());
if (startDate && endDate && startDate >= endDate) {
errors.push('End date must be after start date');
isValid = false;
}
if (!isValid) {
e.preventDefault();
showAlert('Please correct the following errors:\n' + errors.join('\n'), 'danger');
}
});
function showAlert(message, type) {
const alertDiv = document.createElement('div');
alertDiv.className = `alert alert-${type} alert-dismissible fade show position-fixed`;
alertDiv.style.cssText = 'top: 20px; right: 20px; z-index: 1060; min-width: 300px;';
alertDiv.innerHTML = `
${message}
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
`;
document.body.appendChild(alertDiv);
setTimeout(() => {
if (alertDiv.parentNode) {
alertDiv.remove();
}
}, 5000);
}
</script>
{% endblock %}