[UPDT] DASHBOARDS: Updated dashboard designs
This commit is contained in:
@@ -515,6 +515,31 @@ class AssetAssignment(HorillaModel):
|
||||
date_col=date_col,
|
||||
)
|
||||
|
||||
def get_asset_of_offboarding_employee(self):
|
||||
url = f"{reverse('asset-request-allocation-view')}?assigned_to_employee_id={self.assigned_to_employee_id.id}"
|
||||
return url
|
||||
|
||||
def get_send_mail_employee_link(self):
|
||||
if not self.assigned_to_employee_id:
|
||||
return ""
|
||||
url = reverse(
|
||||
"send-mail-employee", kwargs={"emp_id": self.assigned_to_employee_id.id}
|
||||
)
|
||||
title = _("Send Mail")
|
||||
html = f"""
|
||||
<a
|
||||
onclick="event.stopPropagation()"
|
||||
hx-get="{url}"
|
||||
data-toggle="oh-modal-toggle"
|
||||
data-target="#sendMailModal"
|
||||
title="{title}"
|
||||
hx-target="#mail-content"
|
||||
>
|
||||
<ion-icon name="mail-outline"></ion-icon>
|
||||
</a>
|
||||
"""
|
||||
return format_html(html)
|
||||
|
||||
|
||||
class AssetRequest(HorillaModel):
|
||||
"""
|
||||
|
||||
@@ -4,8 +4,10 @@ This page handles the cbv methods for existing process
|
||||
|
||||
import re
|
||||
from datetime import datetime, timedelta
|
||||
from typing import Any
|
||||
|
||||
from django import forms
|
||||
from django.apps import apps
|
||||
from django.contrib import messages
|
||||
from django.http import HttpResponse
|
||||
from django.urls import reverse, reverse_lazy
|
||||
@@ -14,6 +16,7 @@ from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from base.context_processors import intial_notice_period
|
||||
from base.methods import eval_validate
|
||||
from horilla.methods import get_horilla_model_class
|
||||
from horilla_views.cbv_methods import login_required, permission_required
|
||||
from horilla_views.generic.cbv.pipeline import Pipeline
|
||||
from horilla_views.generic.cbv.views import (
|
||||
@@ -619,3 +622,132 @@ class OffboardingEmployeeList(HorillaListView):
|
||||
employee_id__in=instance_ids, task_id=pk
|
||||
).update(status=status)
|
||||
return response
|
||||
|
||||
|
||||
@method_decorator(login_required, name="dispatch")
|
||||
class DashboardTaskListview(HorillaListView):
|
||||
"""
|
||||
For dashboard task status table
|
||||
"""
|
||||
|
||||
# view_id = "view-container"
|
||||
|
||||
model = OffboardingEmployee
|
||||
filter_class = PipelineEmployeeFilter
|
||||
bulk_select_option = False
|
||||
view_id = "dashboard_task_status"
|
||||
|
||||
def get_queryset(self):
|
||||
"""
|
||||
Returns a filtered queryset of records assigned to a specific employee
|
||||
"""
|
||||
|
||||
qs = OffboardingEmployee.objects.entire()
|
||||
queryset = super().get_queryset(queryset=qs)
|
||||
return queryset
|
||||
|
||||
columns = [
|
||||
("Employee", "employee_id", "employee_id__get_avatar"),
|
||||
("Stage", "stage_id"),
|
||||
("Task Status", "get_task_status_col"),
|
||||
]
|
||||
|
||||
|
||||
if apps.is_installed("asset"):
|
||||
|
||||
@method_decorator(login_required, name="dispatch")
|
||||
@method_decorator(
|
||||
any_manager_can_enter("offboarding.view_offboarding"), name="dispatch"
|
||||
)
|
||||
class DashboardNotReturndAsssets(HorillaListView):
|
||||
"""
|
||||
For dashboard task status table
|
||||
"""
|
||||
|
||||
# view_id = "view-container"
|
||||
AssetAssignment = get_horilla_model_class(
|
||||
app_label="asset", model="assetassignment"
|
||||
)
|
||||
model = AssetAssignment
|
||||
bulk_select_option = False
|
||||
view_id = "dashboard_task_status"
|
||||
|
||||
def get_queryset(self):
|
||||
"""
|
||||
Returns a filtered queryset of records assigned to a specific employee
|
||||
"""
|
||||
|
||||
offboarding_employees = OffboardingEmployee.objects.entire().values_list(
|
||||
"employee_id__id", flat=True
|
||||
)
|
||||
qs = self.model.objects.entire().filter(
|
||||
return_status__isnull=True,
|
||||
assigned_to_employee_id__in=offboarding_employees,
|
||||
)
|
||||
queryset = (
|
||||
super().get_queryset().filter(id__in=qs.values_list("id", flat=True))
|
||||
)
|
||||
return queryset
|
||||
|
||||
columns = [
|
||||
(
|
||||
"Employee",
|
||||
"assigned_to_employee_id__get_full_name",
|
||||
"assigned_to_employee_id__get_avatar",
|
||||
),
|
||||
("Asset", "asset_id__asset_name"),
|
||||
("Reminder", "get_send_mail_employee_link"),
|
||||
]
|
||||
|
||||
row_attrs = """
|
||||
onclick="
|
||||
localStorage.setItem('activeTabAsset','#tab_2');
|
||||
window.location.href='{get_asset_of_offboarding_employee}'"
|
||||
"""
|
||||
|
||||
|
||||
if apps.is_installed("pms"):
|
||||
|
||||
@method_decorator(login_required, name="dispatch")
|
||||
@method_decorator(
|
||||
any_manager_can_enter("offboarding.view_offboarding"), name="dispatch"
|
||||
)
|
||||
class DashboardFeedbackView(HorillaListView):
|
||||
"""
|
||||
For dashboard task status table
|
||||
"""
|
||||
|
||||
# view_id = "view-container"
|
||||
Feedback = get_horilla_model_class(app_label="pms", model="feedback")
|
||||
model = Feedback
|
||||
bulk_select_option = False
|
||||
view_id = "dashboard_task_status"
|
||||
|
||||
def get_queryset(self):
|
||||
"""
|
||||
Returns a filtered queryset of records assigned to a specific employee
|
||||
"""
|
||||
|
||||
offboarding_employees = OffboardingEmployee.objects.entire().values_list(
|
||||
"employee_id__id", "notice_period_starts"
|
||||
)
|
||||
if offboarding_employees:
|
||||
id_list, date_list = map(list, zip(*offboarding_employees))
|
||||
else:
|
||||
id_list, date_list = [], []
|
||||
|
||||
qs = (
|
||||
self.model.objects.entire()
|
||||
.filter(employee_id__in=id_list)
|
||||
.exclude(status="Closed")
|
||||
)
|
||||
queryset = (
|
||||
super().get_queryset().filter(id__in=qs.values_list("id", flat=True))
|
||||
)
|
||||
return queryset
|
||||
|
||||
columns = [
|
||||
("Employee", "employee_id__get_full_name", "employee_id__get_avatar"),
|
||||
("Feedback", "review_cycle"),
|
||||
("Status", "status"),
|
||||
]
|
||||
|
||||
@@ -11,29 +11,76 @@ $(document).ready(function () {
|
||||
if (departmentChart) {
|
||||
departmentChart.destroy();
|
||||
}
|
||||
|
||||
// Extract colors and create visibility array for dynamic datasets
|
||||
const colors = ['#a5b4fc', '#fca5a5', '#fdba74', '#34d399', '#fbbf24', '#c084fc', '#fb7185'];
|
||||
const visibility = data.datasets.map(() => true);
|
||||
|
||||
// Apply the new styling to datasets
|
||||
const styledDatasets = data.datasets.map((dataset, index) => ({
|
||||
...dataset,
|
||||
backgroundColor: colors[index % colors.length],
|
||||
borderRadius: 20,
|
||||
barPercentage: 0.6,
|
||||
categoryPercentage: 0.6
|
||||
}));
|
||||
|
||||
departmentChart = new Chart(ctx, {
|
||||
type: "bar",
|
||||
data: {
|
||||
labels: data.labels,
|
||||
datasets: data.datasets,
|
||||
datasets: styledDatasets
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
plugins: {
|
||||
legend: { display: false },
|
||||
tooltip: { enabled: true }
|
||||
},
|
||||
scales: {
|
||||
x: {
|
||||
stacked: true,
|
||||
ticks: { display: true },
|
||||
grid: { display: false },
|
||||
border: { display: true, color: '#d1d5db' }
|
||||
},
|
||||
y: {
|
||||
stacked: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
plugins: [
|
||||
{
|
||||
afterRender: (chart) => emptyChart(chart),
|
||||
},
|
||||
],
|
||||
beginAtZero: true,
|
||||
ticks: { stepSize: 10 },
|
||||
grid: { drawBorder: false, color: '#e5e7eb' }
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Create clickable legend dynamically for multiple datasets
|
||||
const legendContainer = $("#departmentLegend");
|
||||
legendContainer.empty(); // Clear existing legend
|
||||
|
||||
data.datasets.forEach((dataset, i) => {
|
||||
const item = $(`
|
||||
<div class="flex items-center gap-2 cursor-pointer legend-item" data-index="${i}">
|
||||
<span class="w-4 h-4 rounded-full inline-block legend-dot" style="background:${colors[i % colors.length]}; transition: 0.3s;"></span>
|
||||
<span class="legend-text">${dataset.label}</span>
|
||||
</div>
|
||||
`);
|
||||
|
||||
item.on('click', function () {
|
||||
const index = parseInt($(this).data('index'));
|
||||
visibility[index] = !visibility[index];
|
||||
|
||||
// Use Chart.js built-in visibility toggle
|
||||
departmentChart.setDatasetVisibility(index, visibility[index]);
|
||||
departmentChart.update();
|
||||
|
||||
// Update legend visuals
|
||||
const dot = $(this).find('.legend-dot');
|
||||
const text = $(this).find('.legend-text');
|
||||
dot.css('opacity', visibility[index] ? '1' : '0.4');
|
||||
text.css('text-decoration', visibility[index] ? 'none' : 'line-through');
|
||||
});
|
||||
|
||||
legendContainer.append(item);
|
||||
});
|
||||
},
|
||||
error: function (xhr, status, error) {
|
||||
@@ -51,30 +98,88 @@ $(document).ready(function () {
|
||||
if (joinChart) {
|
||||
joinChart.destroy();
|
||||
}
|
||||
|
||||
// Colors for join chart
|
||||
const colors = ['#a5b4fc', '#fca5a5', '#fdba74', '#34d399', '#fbbf24', '#c084fc', '#fb7185'];
|
||||
|
||||
// Style the dataset based on chart type
|
||||
const styledDataset = {
|
||||
label: "Employees",
|
||||
data: data.items,
|
||||
backgroundColor: type === 'line' ? '#a5b4fc' : colors.slice(0, data.items.length),
|
||||
borderColor: type === 'line' ? '#a5b4fc' : undefined,
|
||||
borderWidth: type === 'line' ? 2 : undefined,
|
||||
borderRadius: (type === 'bar') ? 20 : undefined,
|
||||
barPercentage: (type === 'bar') ? 0.6 : undefined,
|
||||
categoryPercentage: (type === 'bar') ? 0.6 : undefined,
|
||||
fill: type === 'line' ? false : undefined,
|
||||
tension: type === 'line' ? 0.4 : undefined
|
||||
};
|
||||
|
||||
joinChart = new Chart(ctx, {
|
||||
type: type,
|
||||
data: {
|
||||
labels: data.labels,
|
||||
datasets: [
|
||||
{
|
||||
label: "Employees",
|
||||
data: data.items,
|
||||
},
|
||||
],
|
||||
datasets: [styledDataset],
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
},
|
||||
plugins: [
|
||||
{
|
||||
afterRender: (chart) => emptyChart(chart),
|
||||
plugins: {
|
||||
// Disable default legend for all chart types
|
||||
legend: { display: false },
|
||||
tooltip: { enabled: true }
|
||||
},
|
||||
],
|
||||
scales: (type === 'pie' || type === 'doughnut') ? {} : {
|
||||
x: {
|
||||
ticks: { display: true },
|
||||
grid: { display: false },
|
||||
border: { display: true, color: '#d1d5db' }
|
||||
},
|
||||
y: {
|
||||
beginAtZero: true,
|
||||
ticks: { stepSize: 10 },
|
||||
grid: { drawBorder: false, color: '#e5e7eb' }
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Create custom legend for all chart types
|
||||
const joinLegendContainer = $("#joinLegend");
|
||||
joinLegendContainer.empty(); // Clear existing legend
|
||||
|
||||
data.labels.forEach((label, i) => {
|
||||
const item = $(`
|
||||
<div class="flex items-center gap-2 cursor-pointer legend-item" data-index="${i}">
|
||||
<span class="w-4 h-4 rounded-full inline-block legend-dot" style="background:${colors[i % colors.length]}; transition: 0.3s;"></span>
|
||||
<span class="legend-text">${label}</span>
|
||||
</div>
|
||||
`);
|
||||
|
||||
item.on('click', function () {
|
||||
const index = parseInt($(this).data('index'));
|
||||
const currentData = [...joinChart.data.datasets[0].data];
|
||||
const originalValue = data.items[index];
|
||||
|
||||
// Toggle visibility by setting data to 0 or original value
|
||||
currentData[index] = currentData[index] === 0 ? originalValue : 0;
|
||||
joinChart.data.datasets[0].data = currentData;
|
||||
joinChart.update();
|
||||
|
||||
// Update legend visuals
|
||||
const dot = $(this).find('.legend-dot');
|
||||
const text = $(this).find('.legend-text');
|
||||
const isVisible = currentData[index] !== 0;
|
||||
dot.css('opacity', isVisible ? '1' : '0.4');
|
||||
text.css('text-decoration', isVisible ? 'none' : 'line-through');
|
||||
});
|
||||
|
||||
joinLegendContainer.append(item);
|
||||
});
|
||||
},
|
||||
error: function (xhr, status, error) {
|
||||
console.error("Error fetching department chart data:", error);
|
||||
console.error("Error fetching join chart data:", error);
|
||||
},
|
||||
});
|
||||
};
|
||||
@@ -93,6 +198,105 @@ $(document).ready(function () {
|
||||
join_chart(chartType);
|
||||
});
|
||||
|
||||
department_chart("pie");
|
||||
department_chart();
|
||||
join_chart("bar");
|
||||
});
|
||||
|
||||
// $(document).ready(function () {
|
||||
// var departmentChart;
|
||||
// var joinChart;
|
||||
|
||||
// var department_chart = () => {
|
||||
// $.ajax({
|
||||
// url: "/offboarding/dashboard-department-chart",
|
||||
// type: "GET",
|
||||
// success: function (data) {
|
||||
// var ctx = $("#departmentChart");
|
||||
// if (departmentChart) {
|
||||
// departmentChart.destroy();
|
||||
// }
|
||||
// departmentChart = new Chart(ctx, {
|
||||
// type: "bar",
|
||||
// data: {
|
||||
// labels: data.labels,
|
||||
// datasets: data.datasets,
|
||||
// },
|
||||
// options: {
|
||||
// responsive: true,
|
||||
// maintainAspectRatio: false,
|
||||
// scales: {
|
||||
// x: {
|
||||
// stacked: true,
|
||||
// },
|
||||
// y: {
|
||||
// stacked: true,
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
// plugins: [
|
||||
// {
|
||||
// afterRender: (chart) => emptyChart(chart),
|
||||
// },
|
||||
// ],
|
||||
// });
|
||||
// },
|
||||
// error: function (xhr, status, error) {
|
||||
// console.error("Error fetching department chart data:", error);
|
||||
// },
|
||||
// });
|
||||
// };
|
||||
|
||||
// var join_chart = (type) => {
|
||||
// $.ajax({
|
||||
// url: "/offboarding/dashboard-join-chart",
|
||||
// type: "GET",
|
||||
// success: function (data) {
|
||||
// var ctx = $("#joinChart");
|
||||
// if (joinChart) {
|
||||
// joinChart.destroy();
|
||||
// }
|
||||
// joinChart = new Chart(ctx, {
|
||||
// type: type,
|
||||
// data: {
|
||||
// labels: data.labels,
|
||||
// datasets: [
|
||||
// {
|
||||
// label: "Employees",
|
||||
// data: data.items,
|
||||
// },
|
||||
// ],
|
||||
// },
|
||||
// options: {
|
||||
// responsive: true,
|
||||
// maintainAspectRatio: false,
|
||||
// },
|
||||
// plugins: [
|
||||
// {
|
||||
// afterRender: (chart) => emptyChart(chart),
|
||||
// },
|
||||
// ],
|
||||
// });
|
||||
// },
|
||||
// error: function (xhr, status, error) {
|
||||
// console.error("Error fetching department chart data:", error);
|
||||
// },
|
||||
// });
|
||||
// };
|
||||
|
||||
// $("#joinChartChange").click(function (e) {
|
||||
// var chartType = joinChart.config.type;
|
||||
// if (chartType === "line") {
|
||||
// chartType = "bar";
|
||||
// } else if (chartType === "bar") {
|
||||
// chartType = "doughnut";
|
||||
// } else if (chartType === "doughnut") {
|
||||
// chartType = "pie";
|
||||
// } else if (chartType === "pie") {
|
||||
// chartType = "line";
|
||||
// }
|
||||
// join_chart(chartType);
|
||||
// });
|
||||
|
||||
// department_chart("pie");
|
||||
// join_chart("bar");
|
||||
// });
|
||||
|
||||
@@ -256,6 +256,11 @@ urlpatterns = [
|
||||
views.dashboard_join_chart,
|
||||
name="dashboard-join-chart",
|
||||
),
|
||||
path(
|
||||
"list-dashboard-task-status/",
|
||||
exit_process.DashboardTaskListview.as_view(),
|
||||
name="list-dashboard-task-status",
|
||||
),
|
||||
]
|
||||
|
||||
if apps.is_installed("asset"):
|
||||
@@ -265,8 +270,14 @@ if apps.is_installed("asset"):
|
||||
views.dashboard_asset_table,
|
||||
name="dashboard-asset-table",
|
||||
),
|
||||
path(
|
||||
"dashboard-asset-table-cbv",
|
||||
exit_process.DashboardNotReturndAsssets.as_view(),
|
||||
name="dashboard-asset-table-cbv",
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
if apps.is_installed("pms"):
|
||||
urlpatterns += [
|
||||
path(
|
||||
@@ -274,4 +285,9 @@ if apps.is_installed("pms"):
|
||||
views.dashboard_feedback_table,
|
||||
name="dashboard-feedback-table",
|
||||
),
|
||||
path(
|
||||
"dashboard-feedback-table-cbv/",
|
||||
exit_process.DashboardFeedbackView.as_view(),
|
||||
name="dashboard-feedback-table-cbv",
|
||||
),
|
||||
]
|
||||
|
||||
@@ -34,7 +34,7 @@ $(document).ready(function(){
|
||||
backgroundColor: dataset.backgroundColor || defaultColors[index % defaultColors.length],
|
||||
borderRadius: 20,
|
||||
barPercentage: 0.9,
|
||||
categoryPercentage: 0.9
|
||||
categoryPercentage: 0.6
|
||||
}));
|
||||
|
||||
const data = {
|
||||
@@ -59,8 +59,7 @@ $(document).ready(function(){
|
||||
scales: {
|
||||
y: {
|
||||
beginAtZero: true,
|
||||
max: Math.max(...dataSet.flatMap(set => set.data)) + 20,
|
||||
ticks: { stepSize: 20 },
|
||||
ticks: { stepSize: 5 },
|
||||
grid: { drawBorder: false, color: '#e5e7eb' }
|
||||
},
|
||||
x: {
|
||||
@@ -167,7 +166,7 @@ $(document).ready(function(){
|
||||
backgroundColor: dataset.backgroundColor || defaultColors[index % defaultColors.length],
|
||||
borderRadius: 20,
|
||||
barPercentage: 0.9,
|
||||
categoryPercentage: 0.9
|
||||
categoryPercentage: 0.6
|
||||
}));
|
||||
|
||||
const data = {
|
||||
@@ -192,8 +191,7 @@ $(document).ready(function(){
|
||||
scales: {
|
||||
y: {
|
||||
beginAtZero: true,
|
||||
max: Math.max(...dataSet.flatMap(set => set.data)) + 20,
|
||||
ticks: { stepSize: 20 },
|
||||
ticks: { stepSize: 5 },
|
||||
grid: { drawBorder: false, color: '#e5e7eb' }
|
||||
},
|
||||
x: {
|
||||
|
||||
Reference in New Issue
Block a user