emile 1 month ago
parent a1ebd69efc
commit 64a831f2be

@ -2,12 +2,12 @@
{% load static %} {% load static %}
{% block content %} {% block content %}
<link href="https://cdn.jsdelivr.net/npm/tom-select@2.4.3/dist/css/tom-select.css" rel="stylesheet" /> <link href="https://cdn.jsdelivr.net/npm/tom-select@2.4.3/dist/css/tom-select.css" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/tom-select@2.4.3/dist/js/tom-select.complete.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/tom-select@2.4.3/dist/js/tom-select.complete.min.js"></script>
<div class="h-fit w-full flex flex-col items gap-5 rounded-xl bg-gray-50 shadow-md p-5"> <div class="h-fit w-full flex flex-col items gap-5 rounded-xl bg-gray-50 shadow-md p-5">
<div class="flex flex-row gap-3 justify-between items-center"> <div class="flex flex-row gap-3 justify-between items-center">
<h1 class="text-2xl text-secondosiblue text-center font-semibold">Projects Dashboard</h1> <h1 class="font-semibold text-xl text-primary">Projects Dashboard</h1>
</div> </div>
<form method="get" id="filter-form"> <form method="get" id="filter-form">
@ -17,20 +17,20 @@
<p>From:</p> <p>From:</p>
<input name="start_date" id="start-date" type="date" <input name="start_date" id="start-date" type="date"
value="{{ start_date }}" value="{{ start_date }}"
class="w-full border rounded-md outline-none focus:border-primary p-2" /> class="w-full border rounded-md outline-none focus:border-primary p-2">
</div> </div>
<div class="w-full max-w-[300px] flex flex-row gap-2 items-center"> <div class="w-full max-w-[300px] flex flex-row gap-2 items-center">
<p>To:</p> <p>To:</p>
<input name="end_date" id="end-date" type="date" <input name="end_date" id="end-date" type="date"
value="{{ end_date }}" value="{{ end_date }}"
class="w-full border rounded-md outline-none focus:border-primary p-2" /> class="w-full border rounded-md outline-none focus:border-primary p-2">
</div> </div>
</div> </div>
<div class="w-full flex flex-row flex-wrap gap-5"> <div class="w-full flex flex-row flex-wrap gap-5">
<div class="w-full flex flex-1 flex-col items-center"> <div class="w-full flex flex-1 flex-col items-center">
<p class="font-medium text-secondary">Projects</p> <p class="font-medium text-secondary">Projects</p>
<select name="projects" id="projects" class="w-full h-full rounded-md z-10 confirm-exit-input" multiple> <select name="projects" id="projects" class="w-full h-full rounded-md z-10" multiple>
{% for project in projects %} {% for project in projects %}
<option value="{{ project.id }}" <option value="{{ project.id }}"
{% if project.id in selected_projects %}selected{% endif %}> {% if project.id in selected_projects %}selected{% endif %}>
@ -42,11 +42,11 @@
<div class="w-full flex-1 flex flex-col items-center"> <div class="w-full flex-1 flex flex-col items-center">
<p class="font-medium text-secondary">Staff</p> <p class="font-medium text-secondary">Staff</p>
<select name="staff" id="staff" class="w-full h-full rounded-md z-10 confirm-exit-input" multiple> <select name="staff" id="staff" class="w-full h-full rounded-md z-10" multiple>
{% for staff in staffs %} {% for staff in staffs %}
<option value="{{ staff.id }}" <option value="{{ staff.id }}"
{% if staff.id in selected_staff %}selected{% endif %}> {% if staff.id in selected_staff %}selected{% endif %}>
{{ staff.user.first_name }} {{ staff.user.last_name }} {{ staff.name }}
</option> </option>
{% endfor %} {% endfor %}
</select> </select>
@ -59,23 +59,53 @@
</div> </div>
</form> </form>
<div class="w-full flex flex-col gap-10 justify-center items-center"> <div class="w-full overflow-x-auto">
<table class="table-auto w-full"> <table class="min-w-full divide-y divide-gray-200">
<thead> <thead class="bg-gray-50">
<tr> <tr>
<th class="px-6 py-3 text-sm font-medium text-gray-500 uppercase border-r border-gray-300 whitespace-nowrap">Project</th> <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Project</th>
<th class="px-6 py-3 text-sm font-medium text-gray-500 uppercase border-r border-gray-300 whitespace-nowrap">Status</th> <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Task</th>
<th class="px-6 py-3 text-sm font-medium text-gray-500 uppercase border-r border-gray-300 whitespace-nowrap">Date</th> <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Point</th>
<th class="px-6 py-3 text-sm font-medium text-gray-500 uppercase border-r border-gray-300 whitespace-nowrap">Staff</th> <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Status</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Assigned To</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Time Spent</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Last Activity</th>
</tr> </tr>
</thead> </thead>
<tbody id="status-table-body"> <tbody class="bg-white divide-y divide-gray-200">
{% for task in tasks %} {% for point in points %}
<tr> <tr>
<td class="px-6 py-4 text-center text-sm border-r border-gray-300">{{ task.project.name }}</td> <td class="px-6 py-4 whitespace-nowrap">{{ point.task.project.name }}</td>
<td class="px-6 py-4 text-center text-sm border-r border-gray-300">{{ task.status }}</td> <td class="px-6 py-4 whitespace-nowrap">{{ point.task.name }}</td>
<td class="px-6 py-4 text-center text-sm border-r border-gray-300">{{ task.start_date }}</td> <td class="px-6 py-4 whitespace-nowrap">{{ point.text }}</td>
<td class="px-6 py-4 text-center text-sm border-r border-gray-300">{{ task.assigned_to.user.first_name }} {{ task.assigned_to.user.last_name }}</td> <td class="px-6 py-4 whitespace-nowrap">
<span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full
{% if point.status == 'Completed' %}bg-green-100 text-green-800
{% elif point.status == 'Working On' %}bg-blue-100 text-blue-800
{% elif point.status == 'Paused' %}bg-yellow-100 text-yellow-800
{% else %}bg-red-100 text-red-800{% endif %}">
{{ point.status }}
</span>
</td>
<td class="px-6 py-4 whitespace-nowrap">{{ point.task.assigned_to.name }}</td>
<td class="px-6 py-4 whitespace-nowrap">
{% with point.total_activity_time as time %}
{{ time.0 }}h {{ time.1 }}m {{ time.2 }}s
{% endwith %}
</td>
<td class="px-6 py-4 whitespace-nowrap">
{% with point.pointactivity_set.last as last_activity %}
{% if last_activity %}
{{ last_activity.end_time|date:"Y-m-d H:i" }}
{% else %}
No activity
{% endif %}
{% endwith %}
</td>
</tr>
{% empty %}
<tr>
<td colspan="7" class="px-6 py-4 text-center">No points found with the current filters</td>
</tr> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>

@ -911,7 +911,6 @@ def prospecting_lists(request):
from django.utils.dateparse import parse_date from django.utils.dateparse import parse_date
from django.shortcuts import render from django.shortcuts import render
@staff_login_required @staff_login_required
def projects_dashboard(request): def projects_dashboard(request):
# Get all projects and staff for the dropdowns # Get all projects and staff for the dropdowns
@ -935,22 +934,24 @@ def projects_dashboard(request):
start_date = parse_date(start_date_str) if start_date_str else None start_date = parse_date(start_date_str) if start_date_str else None
end_date = parse_date(end_date_str) if end_date_str else None end_date = parse_date(end_date_str) if end_date_str else None
# Filter tasks based on parameters # Filter points based on parameters
tasks = Task.objects.all() points = Point.objects.select_related('task__project', 'task__assigned_to').prefetch_related('pointactivity_set')
if selected_project_ids: if selected_project_ids:
tasks = tasks.filter(project__id__in=selected_project_ids) points = points.filter(task__project__id__in=selected_project_ids)
if selected_staff_ids: if selected_staff_ids:
tasks = tasks.filter(assigned_to__id__in=selected_staff_ids) points = points.filter(task__assigned_to__id__in=selected_staff_ids)
if start_date and end_date: if start_date and end_date:
tasks = tasks.filter(end_date__range=[start_date, end_date]) points = points.filter(
pointactivity__end_time__date__range=[start_date, end_date]
).distinct()
context = { context = {
'projects': all_projects, 'projects': all_projects,
'staffs': all_staff, # Changed from 'staff' to match your template 'staffs': all_staff,
'tasks': tasks, 'points': points,
'selected_projects': [int(id) for id in selected_project_ids if id], 'selected_projects': [int(id) for id in selected_project_ids if id],
'selected_staff': [int(id) for id in selected_staff_ids if id], 'selected_staff': [int(id) for id in selected_staff_ids if id],
'start_date': start_date_str if start_date else '', 'start_date': start_date_str if start_date else '',

@ -1,61 +1,61 @@
document.addEventListener('DOMContentLoaded', function () { // document.addEventListener('DOMContentLoaded', function () {
// Initialize TomSelect for Projects // // Initialize TomSelect for Projects
new TomSelect('#projects', { // new TomSelect('#projects', {
create: false, // Disable creating new options // create: false, // Disable creating new options
sortField: 'text', // sortField: 'text',
onChange: function(values) { // onChange: function(values) {
updateTasks(); // Update tasks on value change // updateTasks(); // Update tasks on value change
} // }
}); // });
//
// Initialize TomSelect for Staff // // Initialize TomSelect for Staff
new TomSelect('#staff', { // new TomSelect('#staff', {
create: false, // create: false,
sortField: 'text', // sortField: 'text',
onChange: function(values) { // onChange: function(values) {
updateTasks(); // Update tasks on value change // updateTasks(); // Update tasks on value change
} // }
}); // });
//
// Update tasks based on filters // // Update tasks based on filters
function updateTasks() { // function updateTasks() {
const projectIds = Array.from(document.getElementById('projects').selectedOptions).map(option => option.value); // const projectIds = Array.from(document.getElementById('projects').selectedOptions).map(option => option.value);
const staffIds = Array.from(document.getElementById('staff').selectedOptions).map(option => option.value); // const staffIds = Array.from(document.getElementById('staff').selectedOptions).map(option => option.value);
const startDate = document.getElementById('start-date').value; // const startDate = document.getElementById('start-date').value;
const endDate = document.getElementById('end-date').value; // const endDate = document.getElementById('end-date').value;
//
// Construct the query parameters without brackets around the projectIds // // Construct the query parameters without brackets around the projectIds
let queryParams = `?projects=${projectIds.join(',')}&staff=${staffIds.join(',')}&start_date=${startDate}&end_date=${endDate}`; // let queryParams = `?projects=${projectIds.join(',')}&staff=${staffIds.join(',')}&start_date=${startDate}&end_date=${endDate}`;
//
// Perform AJAX request // // Perform AJAX request
fetch(`/projects-dashboard/${queryParams}`, { // fetch(`/projects-dashboard/${queryParams}`, {
method: 'GET', // method: 'GET',
headers: { // headers: {
'X-Requested-With': 'XMLHttpRequest', // 'X-Requested-With': 'XMLHttpRequest',
}, // },
}) // })
.then(response => response.json()) // .then(response => response.json())
.then(data => { // .then(data => {
// Update table body with new data // // Update table body with new data
const tbody = document.getElementById('status-table-body'); // const tbody = document.getElementById('status-table-body');
tbody.innerHTML = ''; // Clear the existing table rows // tbody.innerHTML = ''; // Clear the existing table rows
data.tasks_data.forEach(task => { // data.tasks_data.forEach(task => {
const tr = document.createElement('tr'); // const tr = document.createElement('tr');
tr.innerHTML = ` // tr.innerHTML = `
<td>${task.project_name}</td> // <td>${task.project_name}</td>
<td>${task.status}</td> // <td>${task.status}</td>
<td>${task.start_date}</td> // <td>${task.start_date}</td>
<td>${task.staff}</td> // <td>${task.staff}</td>
`; // `;
tbody.appendChild(tr); // tbody.appendChild(tr);
}); // });
}) // })
.catch(error => { // .catch(error => {
console.error('Error updating tasks:', error); // console.error('Error updating tasks:', error);
}); // });
} // }
//
// Trigger update when date range changes // // Trigger update when date range changes
document.getElementById('start-date').addEventListener('change', updateTasks); // document.getElementById('start-date').addEventListener('change', updateTasks);
document.getElementById('end-date').addEventListener('change', updateTasks); // document.getElementById('end-date').addEventListener('change', updateTasks);
}); // });

Loading…
Cancel
Save