[UPDT] DASHBOARD: Main dashboard design updates

This commit is contained in:
Horilla
2025-06-25 15:58:35 +05:30
parent 293e2f3ca9
commit 8ef3a032b2
11 changed files with 1720 additions and 916 deletions

View File

@@ -3,11 +3,10 @@ from typing import Any
from django.utils.decorators import method_decorator
from django.utils.translation import gettext_lazy as _
from horilla_views.cbv_methods import login_required, permission_required
from horilla_views.generic.cbv.views import HorillaDetailedView, HorillaListView
from asset.filters import AssetFilter
from asset.models import Asset
from horilla_views.cbv_methods import login_required, permission_required
from horilla_views.generic.cbv.views import HorillaDetailedView, HorillaListView
@method_decorator(login_required, name="dispatch")

View File

@@ -33,4 +33,4 @@
<ion-icon name="trash-outline" role="img" class="md hydrated" aria-label="trash outline"></ion-icon>
</button>
</form>
{% endif %}
{% endif %}

View File

@@ -24,4 +24,4 @@
</button>
</form>
<!-- End of Delete Form -->
{% endif %}
{% endif %}

View File

@@ -54,7 +54,7 @@ class DashboardOfflineEmployees(HorillaListView):
style="width:80px !important;"
""",
}
records_per_page = 7
records_per_page = 5
show_toggle_form = False
bulk_select_option = False
@@ -85,7 +85,7 @@ class DashboardOnlineEmployees(HorillaListView):
return queryset
columns = [
("Employee", "employee_id__get_full_name", "employee_id__get_avatar"),
("Employee", "get_full_name", "get_avatar"),
("Work Status", "get_custom_forecasted_info_col"),
]

File diff suppressed because it is too large Load Diff

View File

@@ -1,41 +1,41 @@
$(document).ready(function () {
function employeeChart(dataSet, labels) {
const data = {
labels: labels,
datasets: dataSet,
};
// Create chart using the Chart.js library
window["myChart"] = {};
if (document.getElementById("totalEmployees")) {
const ctx = document.getElementById("totalEmployees").getContext("2d");
employeeChart = new Chart(ctx, {
type: "doughnut",
data: data,
options: {
responsive: true,
maintainAspectRatio: false,
onClick: (e, activeEls) => {
let datasetIndex = activeEls[0].datasetIndex;
let dataIndex = activeEls[0].index;
let datasetLabel = e.chart.data.datasets[datasetIndex].label;
let value = e.chart.data.datasets[datasetIndex].data[dataIndex];
let label = e.chart.data.labels[dataIndex];
var active = "False";
if (label.toLowerCase() == "active") {
active = "True";
}
localStorage.removeItem("savedFilters");
window.location.href = "/employee/employee-view?is_active=" + active;
},
},
plugins: [
{
afterRender: (chart) => emptyChart(chart),
},
],
});
}
}
// function employeeChart(dataSet, labels) {
// const data = {
// labels: labels,
// datasets: dataSet,
// };
// // Create chart using the Chart.js library
// window["myChart"] = {};
// if (document.getElementById("totalEmployees")) {
// const ctx = document.getElementById("totalEmployees").getContext("2d");
// employeeChart = new Chart(ctx, {
// type: "doughnut",
// data: data,
// options: {
// responsive: true,
// maintainAspectRatio: false,
// onClick: (e, activeEls) => {
// let datasetIndex = activeEls[0].datasetIndex;
// let dataIndex = activeEls[0].index;
// let datasetLabel = e.chart.data.datasets[datasetIndex].label;
// let value = e.chart.data.datasets[datasetIndex].data[dataIndex];
// let label = e.chart.data.labels[dataIndex];
// var active = "False";
// if (label.toLowerCase() == "active") {
// active = "True";
// }
// localStorage.removeItem("savedFilters");
// window.location.href = "/employee/employee-view?is_active=" + active;
// },
// },
// plugins: [
// {
// afterRender: (chart) => emptyChart(chart),
// },
// ],
// });
// }
// }
// function genderChart(dataSet, labels) {
// const data = {
@@ -111,6 +111,131 @@ $(document).ready(function () {
// }
// }
function employeeChart(dataSet, labels) {
$(document).ready(function () {
const ctx = document.getElementById("totalEmployees")?.getContext("2d");
if (!ctx) return;
const values = dataSet[0].data;
const colors = [
"#34d399", // Active - green
"#f87171", // Inactive - red
];
const visibility = Array(labels.length).fill(true);
// Create chart instance
const employeeChartInstance = new Chart(ctx, {
type: "doughnut",
data: {
labels: labels,
datasets: [
{
...dataSet[0],
backgroundColor: colors.slice(0, labels.length),
borderWidth: 0,
borderRadius: 10,
hoverOffset: 8,
},
],
},
options: {
cutout: "70%",
responsive: true,
maintainAspectRatio: false,
onClick: function (e, activeEls) {
if (!activeEls.length) return;
const dataIndex = activeEls[0].index;
const label = labels[dataIndex];
let active = label.toLowerCase() === "active" ? "True" : "False";
localStorage.removeItem("savedFilters");
window.location.href = "/employee/employee-view?is_active=" + active;
},
plugins: {
legend: { display: false },
tooltip: {
backgroundColor: "#111827",
bodyColor: "#f3f4f6",
borderColor: "#e5e7eb",
borderWidth: 1,
},
},
},
plugins: [
{
id: "centerText",
afterDraw(chart) {
const { width, height, ctx } = chart;
ctx.save();
const total = chart.data.datasets[0].data.reduce(
(sum, val) => sum + val,
0
);
ctx.font = "bold 22px sans-serif";
ctx.fillStyle = "#374151";
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.fillText(total, width / 2, height / 2 - 5);
ctx.font = "15px sans-serif";
ctx.fillStyle = "#9ca3af";
ctx.fillText("Total", width / 2, height / 2 + 20);
ctx.restore();
},
},
{
afterRender: (chart) => {
if (typeof emptyChart === "function") {
emptyChart(chart);
}
},
},
],
});
// 🧩 Custom Legend Generation
const $legendContainer = $("#employeeChartLegend"); // Make sure to have this element in DOM
$legendContainer.empty();
labels.forEach((label, index) => {
const color = colors[index % colors.length];
const $item = $(`
<div style="display: flex; align-items: center; margin-bottom: 6px; cursor: pointer;">
<span style="display: inline-block; width: 12px; height: 12px; border-radius: 50%; background-color: ${color}; margin-right: 8px;"></span>
<span class="legend-label">${label}</span>
</div>
`);
$legendContainer.append($item);
$item.on("click", function () {
visibility[index] = !visibility[index];
employeeChartInstance.data.datasets[0].data = values.map((val, i) =>
visibility[i] ? val : 0
);
const $dot = $(this).find("span").first();
const $label = $(this).find(".legend-label");
if (visibility[index]) {
$dot.css("opacity", "1");
$label.css("text-decoration", "none");
} else {
$dot.css("opacity", "0.4");
$label.css("text-decoration", "line-through");
}
employeeChartInstance.update();
});
});
});
}
function genderChart(dataSet, labels) {
const centerImage = new Image();
@@ -200,8 +325,6 @@ $(document).ready(function () {
}
}
function departmentChart(dataSet, labels) {
$(document).ready(function () {
const ctx = $("#departmentChart")[0]?.getContext("2d");
@@ -324,8 +447,6 @@ $(document).ready(function () {
});
}
$.ajax({
url: "/employee/dashboard-employee",
type: "GET",

View File

@@ -1814,6 +1814,17 @@ ul.errorlist ul.errorlist li {
background: #00d6ff1f !important;
}
.oh-recuritment_tag {
background-color: hsl(0, 75%, 97%);
color: hsl(1, 100%, 61%);
padding: 0.4rem 0.6rem;
border-radius: 0.2rem;
margin-right: 0.2rem;
font-size: 0.8rem;
height: fit-content;
}
/* SIDEBAR */
.sidebarModal {

View File

@@ -25,4 +25,3 @@
{% endwith %}
{% endwith %}
{% endwith %}

View File

@@ -89,8 +89,8 @@
<div class="w-full overflow-x-auto overflow-y-auto" id="shiftRequestApprove" hx-get="{% url 'dashboard-shift-request' %}" hx-trigger="load">
</div>
</div>
{% comment %} {% if not 'gender_chart' in charts %} {% endcomment %}
{% comment %} {% if 'gender_chart'|feature_is_accessible:request or perms.employee.view_employee or request.user|is_reportingmanager %} {% endcomment %}
{% if not 'gender_chart' in charts %}
{% if 'gender_chart'|feature_is_accessible:request or perms.employee.view_employee or request.user|is_reportingmanager %}
<div class="xl:col-span-3 md:col-span-6 col-span-12 bg-white p-6 rounded-md shadow-card">
<div class="flex justify-between items-center mb-2">
<h3 class="text-md font-semibold">{% trans "Gender Chart" %}</h3>
@@ -101,8 +101,8 @@
</div>
</div>
</div>
{% comment %} {% endif %} {% endcomment %}
{% comment %} {% endif %} {% endcomment %}
{% endif %}
{% endif %}
<div class="xl:col-span-3 md:col-span-6 col-span-12 bg-white p-6 rounded-md shadow-card">
<div class="flex justify-between items-center mb-4">
@@ -202,9 +202,8 @@
</div>
<!-- Recent Activities -->
{% comment %} {% if "pms"|app_installed and not 'key_result_status' in charts %} {% endcomment %}
{% comment %} {% if perms.pms.view_employeekeyresult or request.user|is_reportingmanager %} {% endcomment %}
{% if "pms"|app_installed and not 'key_result_status' in charts %}
{% if perms.pms.view_employeekeyresult or request.user|is_reportingmanager %}
<div class="xl:col-span-3 md:col-span-5 col-span-12 bg-white p-6 rounded-md shadow-card" style="width:98%;">
<div class="flex justify-between items-center mb-2">
<h3 class="text-md font-semibold">{% trans "Key Result Status" %}</h3>
@@ -213,8 +212,8 @@
<canvas id="keyResultChart" style="cursor: pointer"></canvas>
</div>
</div>
{% comment %} {% endif %} {% endcomment %}
{% comment %} {% endif %} {% endcomment %}
{% endif %}
{% endif %}
<div class="xl:col-span-5 md:col-span-6 col-span-12 bg-white p-6 rounded-md shadow-card">
<div class="flex justify-between items-center mb-2">
@@ -253,280 +252,226 @@
{% endif %}
{% endif %}
{% if "leave"|app_installed %}
{% if perms.leave.view_leaverequest or request.user|is_reportingmanager %}
<div class="xl:col-span-3 md:col-span-4 col-span-12 bg-white p-6 rounded-md shadow-card">
<div class="flex justify-between items-center mb-2">
<h3 class="text-md font-semibold">{% trans "On Leave" %}</h3>
</div>
<div class="flex-col items-center justify-center" hx-get="{% url 'employee-leave' %}" hx-trigger="load">
</div>
</div>
{% endif %}
{% endif %}
<div class="xl:col-span-5 md:col-span-6 col-span-12 bg-white p-6 rounded-md shadow-card">
<div class="flex justify-between items-center mb-2">
<h3 class="text-md font-semibold">Offline Employees</h3>
</div>
<div class="overflow-hidden overflow-x-auto">
<table class="w-full">
<thead>
<tr class="border-b border-[#ececec]">
<th class="text-sm font-medium text-left p-3 pl-0">
<span class="flex w-max">Employee</span>
</th>
<th class="text-sm font-medium text-left p-3 pl-0">
<span class="flex w-max">Mail</span>
</th>
</tr>
</thead>
<tbody>
<tr class="border-b border-[#ececec]">
<td class="text-sm text-left p-3 pl-0 text-[#666]">
<div class="flex gap-3 items-center w-max">
<img src="/static/horilla_theme/assets/icons/user1.png" alt="" width="30" /><span>Richard Dom</span>
<button class="cursor-pointer px-2.5 py-1 bg-[#00c49025] text-[#00C490] rounded-sm text-xs">
Expected working
</button>
</div>
</td>
<td class="text-xs text-left p-3 pl-0 text-[#666]">
<button class="cursor-pointer">
<img src="/static/horilla_theme/assets/img/icons/email.svg" alt="" width="25" />
</button>
</td>
</tr>
<tr class="border-b border-[#ececec]">
<td class="text-sm text-left p-3 pl-0 text-[#666]">
<div class="flex gap-3 items-center w-max">
<img src="/static/horilla_theme/assets/icons/user1.png" alt="" width="30" /><span>Richard Dom</span>
<button class="cursor-pointer px-2.5 py-1 bg-[#00c49025] text-[#00C490] rounded-sm text-xs">
Expected working
</button>
</div>
</td>
<td class="text-xs text-left p-3 pl-0 text-[#666]">
<button class="cursor-pointer">
<img src="/static/horilla_theme/assets/img/icons/email.svg" alt="" width="25" />
</button>
</td>
</tr>
<tr class="border-b border-[#ececec]">
<td class="text-sm text-left p-3 pl-0 text-[#666]">
<div class="flex gap-3 items-center w-max">
<img src="/static/horilla_theme/assets/icons/user1.png" alt="" width="30" /><span>Richard Dom</span>
<button class="cursor-pointer px-2.5 py-1 bg-[#00c49025] text-[#00C490] rounded-sm text-xs">
Expected working
</button>
</div>
</td>
<td class="text-xs text-left p-3 pl-0 text-[#666]">
<button class="cursor-pointer">
<img src="/static/horilla_theme/assets/img/icons/email.svg" alt="" width="25" />
</button>
</td>
</tr>
<tr class="border-b border-[#ececec]">
<td class="text-sm text-left p-3 pl-0 text-[#666]">
<div class="flex gap-3 items-center w-max">
<img src="/static/horilla_theme/assets/icons/user1.png" alt="" width="30" /><span>Richard Dom</span>
<button class="cursor-pointer px-2.5 py-1 bg-[#00c49025] text-[#00C490] rounded-sm text-xs">
Expected working
</button>
</div>
</td>
<td class="text-xs text-left p-3 pl-0 text-[#666]">
<button class="cursor-pointer">
<img src="/static/horilla_theme/assets/img/icons/email.svg" alt="" width="25" />
</button>
</td>
</tr>
<tr class="border-b border-[#ececec]">
<td class="text-sm text-left p-3 pl-0 text-[#666]">
<div class="flex gap-3 items-center w-max">
<img src="/static/horilla_theme/assets/icons/user1.png" alt="" width="30" /><span>Richard Dom</span>
<button class="cursor-pointer px-2.5 py-1 bg-[#00c49025] text-[#00C490] rounded-sm text-xs">
Expected working
</button>
</div>
</td>
<td class="text-xs text-left p-3 pl-0 text-[#666]">
<button class="cursor-pointer">
<img src="/static/horilla_theme/assets/img/icons/email.svg" alt="" width="25" />
</button>
</td>
</tr>
<tr class="border-b border-[#ececec]">
<td class="text-sm text-left p-3 pl-0 text-[#666]">
<div class="flex gap-3 items-center w-max">
<img src="/static/horilla_theme/assets/icons/user1.png" alt="" width="30" /><span>Richard Dom</span>
<button class="cursor-pointer px-2.5 py-1 bg-[#00c49025] text-[#00C490] rounded-sm text-xs">
Expected working
</button>
</div>
</td>
<td class="text-xs text-left p-3 pl-0 text-[#666]">
<button class="cursor-pointer">
<img src="/static/horilla_theme/assets/img/icons/email.svg" alt="" width="25" />
</button>
</td>
</tr>
</tbody>
</table>
</div>
</div>
{% if "asset"|app_installed and not 'asset_request_approve' in charts %}
{% if perms.asset.change_assetrequest or request.user|is_reportingmanager %}
<div class="xl:col-span-4 md:col-span-12 col-span-12 bg-white p-6 rounded-md shadow-card">
<div class="flex justify-between items-center mb-2">
<h3 class="text-md font-semibold">{% trans "Asset Requests To Approve" %}</h3>
{% if perms.asset.change_assetrequest or request.user|is_reportingmanager %}
<div class="xl:col-span-7 md:col-span-8 col-span-12 bg-white p-6 rounded-md shadow-card" >
<div class="flex justify-between items-center mb-2">
<h3 class="text-md font-semibold">{% trans "Asset Requests To Approve" %}</h3>
</div>
<div class="flex flex-col items-center justify-center" hx-get='{% url "dashboard-asset-request-approve" %}' hx-trigger="load">
{% comment %} <img src="/static/horilla_theme/assets/img/no-records.svg" alt="" width="300" class="mb-4" />
<p class="text-[#666]">No records available at the moment</p> {% endcomment %}
</div>
</div>
<div class="flex flex-col items-center justify-center h-full" hx-get='{% url "dashboard-asset-request-approve" %}' hx-trigger="load">
{% comment %} <img src="/static/horilla_theme/assets/img/no-records.svg" alt="" width="300" class="mb-4" />
<p class="text-[#666]">No records available at the moment</p> {% endcomment %}
</div>
</div>
{% endif %}
{% endif %}
{% endif %}
<div class="xl:col-span-5 md:col-span-6 col-span-12 bg-white p-6 rounded-md shadow-card">
<div class="flex justify-between items-center mb-2">
<h3 class="text-md font-semibold">Overtime TO Approve</h3>
{% if "attendance"|app_installed %}
{% if perms.employee.view_employee or request.user|is_reportingmanager %}
{% if not 'online_employees' in charts %}
<div class="xl:col-span-4 md:col-span-6 col-span-12 bg-white p-6 rounded-md shadow-card">
<div class="flex justify-between items-center mb-2">
<h3 class="text-md font-semibold">{% trans "Online Employees" %}</h3>
</div>
<div class="flex-col items-center justify-center h-[400px]" hx-get="{% url 'not-out-yet' %}" hx-trigger="load">
</div>
</div>
{% endif %}
{% if not 'offline_employees' in charts %}
<div class="xl:col-span-5 md:col-span-7 col-span-12 bg-white p-6 rounded-md shadow-card">
<div class="flex justify-between items-center mb-2">
<h3 class="text-md font-semibold">{% trans "Offline Employees" %}</h3>
</div>
<div class="flex-col items-center justify-center" hx-get="{% url 'not-in-yet' %}" hx-trigger="load">
</div>
</div>
{% endif %}
{% endif %}
{% if not 'overtime_approve' in charts %}
<div class="xl:col-span-5 md:col-span-7 col-span-12 bg-white p-6 rounded-md shadow-card">
<div class="flex justify-between items-center mb-2">
<h3 class="text-md font-semibold">{% trans "Overtime To Approve" %}</h3>
</div>
<div class="flex-col items-center justify-center" hx-get="{% url 'dashboard-overtime-approve' %}" hx-trigger="load">
</div>
</div>
{% endif %}
{% endif %}
<div class="xl:col-span-4 md:col-span-6 col-span-12 bg-white p-6 rounded-md shadow-card">
<div class="flex justify-between items-center mb-4">
<h3 class="text-md font-semibold">
{% trans "Attendance Analytic" %}
</h3>
</div>
<div class="overflow-hidden overflow-x-auto">
<table class="w-full">
<thead>
<tr class="border-b border-[#ececec]">
<th class="text-sm font-medium text-left p-3 pl-0">
<span class="flex w-max">Employee</span>
</th>
<th class="text-sm font-medium text-left p-3 pl-0">
<span class="flex w-max">Overtime</span>
</th>
<th class="text-sm font-medium text-left p-3 pl-0">
<span class="flex w-max">Action</span>
</th>
</tr>
</thead>
<tbody>
<tr class="border-b border-[#ececec]">
<td class="text-sm text-left p-3 pl-0 text-[#666]">
<div class="flex gap-3 items-center w-max">
<img src="/static/horilla_theme/assets/icons/user1.png" alt="" width="30" /><span>Richard Dom</span>
</div>
</td>
<td class="text-sm text-left p-3 pl-0 text-[#666]">
10:45
</td>
<td class="text-sm text-left p-3 pl-0 text-[#666]">
<div class="flex gap-1">
<button class="cursor-pointer px-2 py-1 text-xs bg-[#07bd072e] text-[#00c700] rounded-sm w-6 h-6 flex items-center justify-center">
<i class="fa-solid fa-check"></i>
</button>
<button class="cursor-pointer px-2 py-1 text-xs bg-[#ff000024] text-[red] rounded-sm w-6 h-6 flex items-center justify-center">
<i class="fa-solid fa-xmark"></i>
</button>
</div>
</td>
</tr>
<tr class="border-b border-[#ececec]">
<td class="text-sm text-left p-3 pl-0 text-[#666]">
<div class="flex gap-3 items-center w-max">
<img src="/static/horilla_theme/assets/icons/user1.png" alt="" width="30" /><span>Richard Dom</span>
</div>
</td>
<td class="text-sm text-left p-3 pl-0 text-[#666]">
10:45
</td>
<td class="text-sm text-left p-3 pl-0 text-[#666]">
<div class="flex gap-1">
<button class="cursor-pointer px-2 py-1 text-xs bg-[#07bd072e] text-[#00c700] rounded-sm w-6 h-6 flex items-center justify-center">
<i class="fa-solid fa-check"></i>
</button>
<button class="cursor-pointer px-2 py-1 text-xs bg-[#ff000024] text-[red] rounded-sm w-6 h-6 flex items-center justify-center">
<i class="fa-solid fa-xmark"></i>
</button>
</div>
</td>
</tr>
<tr class="border-b border-[#ececec]">
<td class="text-sm text-left p-3 pl-0 text-[#666]">
<div class="flex gap-3 items-center w-max">
<img src="/static/horilla_theme/assets/icons/user1.png" alt="" width="30" /><span>Richard Dom</span>
</div>
</td>
<td class="text-sm text-left p-3 pl-0 text-[#666]">
10:45
</td>
<td class="text-sm text-left p-3 pl-0 text-[#666]">
<div class="flex gap-1">
<button class="cursor-pointer px-2 py-1 text-xs bg-[#07bd072e] text-[#00c700] rounded-sm w-6 h-6 flex items-center justify-center">
<i class="fa-solid fa-check"></i>
</button>
<button class="cursor-pointer px-2 py-1 text-xs bg-[#ff000024] text-[red] rounded-sm w-6 h-6 flex items-center justify-center">
<i class="fa-solid fa-xmark"></i>
</button>
</div>
</td>
</tr>
<tr class="border-b border-[#ececec]">
<td class="text-sm text-left p-3 pl-0 text-[#666]">
<div class="flex gap-3 items-center w-max">
<img src="/static/horilla_theme/assets/icons/user1.png" alt="" width="30" /><span>Richard Dom</span>
</div>
</td>
<td class="text-sm text-left p-3 pl-0 text-[#666]">
10:45
</td>
<td class="text-sm text-left p-3 pl-0 text-[#666]">
<div class="flex gap-1">
<button class="cursor-pointer px-2 py-1 text-xs bg-[#07bd072e] text-[#00c700] rounded-sm w-6 h-6 flex items-center justify-center">
<i class="fa-solid fa-check"></i>
</button>
<button class="cursor-pointer px-2 py-1 text-xs bg-[#ff000024] text-[red] rounded-sm w-6 h-6 flex items-center justify-center">
<i class="fa-solid fa-xmark"></i>
</button>
</div>
</td>
</tr>
<tr class="border-b border-[#ececec]">
<td class="text-sm text-left p-3 pl-0 text-[#666]">
<div class="flex gap-3 items-center w-max">
<img src="/static/horilla_theme/assets/icons/user1.png" alt="" width="30" /><span>Richard Dom</span>
</div>
</td>
<td class="text-sm text-left p-3 pl-0 text-[#666]">
10:45
</td>
<td class="text-sm text-left p-3 pl-0 text-[#666]">
<div class="flex gap-1">
<button class="cursor-pointer px-2 py-1 text-xs bg-[#07bd072e] text-[#00c700] rounded-sm w-6 h-6 flex items-center justify-center">
<i class="fa-solid fa-check"></i>
</button>
<button class="cursor-pointer px-2 py-1 text-xs bg-[#ff000024] text-[red] rounded-sm w-6 h-6 flex items-center justify-center">
<i class="fa-solid fa-xmark"></i>
</button>
</div>
</td>
</tr>
<tr class="border-b border-[#ececec]">
<td class="text-sm text-left p-3 pl-0 text-[#666]">
<div class="flex gap-3 items-center w-max">
<img src="/static/horilla_theme/assets/icons/user1.png" alt="" width="30" /><span>Richard Dom</span>
</div>
</td>
<td class="text-sm text-left p-3 pl-0 text-[#666]">
10:45
</td>
<td class="text-sm text-left p-3 pl-0 text-[#666]">
<div class="flex gap-1">
<button class="cursor-pointer px-2 py-1 text-xs bg-[#07bd072e] text-[#00c700] rounded-sm w-6 h-6 flex items-center justify-center">
<i class="fa-solid fa-check"></i>
</button>
<button class="cursor-pointer px-2 py-1 text-xs bg-[#ff000024] text-[red] rounded-sm w-6 h-6 flex items-center justify-center">
<i class="fa-solid fa-xmark"></i>
</button>
</div>
</td>
</tr>
</tbody>
</table>
<div class="flex gap-2 justify-between items-center mb-4">
<select id="type" class="oh-select" name="type" onchange="changeView(this)" style="
width: 150px;
padding: 5px;
color: #5e5c5c;
border-radius: 4px;
border: 1px solid #d1d5db;
">
<option value="day">{% trans "Day" %}</option>
<option value="weekly">{% trans "Weekly" %}</option>
<option value="monthly">{% trans "Monthly" %}</option>
<option value="date_range">{% trans "Date range" %}</option>
</select>
<input type="date" id="attendance_month" onchange="changeMonth()" class="oh-select" style="
width: 150px;
padding: 5px;
color: #5e5c5c;
border-radius: 4px;
border: 1px solid #d1d5db;
" />
</div>
<div class="w-full max-w-xs mx-auto">
<div class="relative">
<canvas id="dailyAnalytic"></canvas>
</div>
</div>
</div>
<div class="xl:col-span-4 md:col-span-6 col-span-12 bg-white p-6 rounded-md shadow-card">
<div class="flex justify-between items-center mb-2">
<h3 class="text-md font-semibold">{% trans "Hours Chart" %}</h3>
<input type="month" class="mb-2 float-end pointer oh-select"
id="hourAccountMonth" onchange="dynamicMonth($(this))"
style="
width: 150px;
padding: 5px;
color: #5e5c5c;
border-radius: 4px;
border: 1px solid #d1d5db;
" />
</div>
<div class="w-full max-w-xs mx-auto">
<div class="relative">
<canvas id="pendingHoursCanvas"></canvas>
</div>
</div>
</div>
{% if "leave"|app_installed and not 'overall_leave_chart' in charts %}
{% if perms.leave.view_leaverequest %}
<div class="xl:col-span-3 md:col-span-6 col-span-12 bg-white p-6 rounded-md shadow-card">
<div class="flex justify-between items-center mb-2">
<h3 class="text-md font-semibold">{% trans "Overall Leave" %}</h3>
<select class="oh-select oh-select--sm float-end w-[110px]" name="" id="overAllLeaveSelect" >
<option value="today" selected>{% trans "Today" %}</option>
<option value="week">{% trans "This Week" %}</option>
<option value="month">{% trans "This Month" %}</option>
<option value="year">{% trans "This Year" %}</option>
</select>
</div>
<div class="w-full max-w-xs mx-auto">
<div class="relative">
<canvas id="overAllLeave"></canvas>
</div>
</div>
</div>
{% endif %}
{% endif %}
{% if "leave"|app_installed %}
{% if not 'leave_request_approve' in charts %}
{% if perms.leave.change_leaverequest or request.user|is_reportingmanager %}
<div class="xl:col-span-5 md:col-span-7 col-span-12 bg-white p-6 rounded-md shadow-card">
<div class="flex justify-between items-center mb-2">
<h3 class="text-md font-semibold">{% trans "Leave Requests To Approve" %}</h3>
</div>
<div class="flex-col items-center justify-center" hx-get="{% url 'leave-request-and-approve' %}" hx-trigger="load">
</div>
</div>
{% endif %}
{% endif %}
{% if not 'leave_allocation_approve' in charts %}
{% if perms.leave.change_leaveallocationrequest or request.user|is_reportingmanager %}
<div class="xl:col-span-6 md:col-span-8 col-span-12 bg-white p-6 rounded-md shadow-card">
<div class="flex justify-between items-center mb-2">
<h3 class="text-md font-semibold">{% trans "Leave Allocation Request To Approve" %}</h3>
</div>
<div class="flex-col items-center justify-center" hx-get="{% url 'leave-allocation-approve' %}" hx-trigger="load">
</div>
</div>
{% endif %}
{% endif %}
{% endif%}
<div class="xl:col-span-3 md:col-span-6 col-span-12 bg-white p-6 rounded-md shadow-card">
<div class="flex justify-between items-center mb-2">
<h3 class="text-md font-semibold">{% trans "Employees Chart" %}</h3>
</div>
<div class="w-full max-w-xs mx-auto">
<div class="relative">
<canvas id="totalEmployees"></canvas>
</div>
<div id="employeeChartLegend" class="mt-4 flex justify-center flex-wrap gap-3 text-xs"
></div>
</div>
</div>
{% if "pms"|app_installed %}
{% if not 'feedback_answer' in charts %}
<div class="xl:col-span-6 md:col-span-8 col-span-12 bg-white p-6 rounded-md shadow-card">
<div class="flex justify-between items-center mb-2">
<h3 class="text-md font-semibold">{% trans "Feedback To Answers" %}</h3>
</div>
<div class="flex-col items-center justify-center" hx-get="{% url 'dashboard-feedback-answer' %}" hx-trigger="load">
</div>
</div>
{% endif %}
{% if not 'feedback_status' in charts %}
{% if perms.pms.view_feedback or request.user|is_reportingmanager %}
<div class="xl:col-span-3 md:col-span-6 col-span-12 bg-white p-6 rounded-md shadow-card">
<div class="flex justify-between items-center mb-2">
<h3 class="text-md font-semibold">{% trans "Feedback Status" %}</h3>
</div>
<div class="w-full max-w-xs mx-auto">
<div class="relative">
<canvas id="feedbackChart"></canvas>
</div>
<div id="feedbackChartLegend" class="mt-4 flex justify-center flex-wrap gap-3 text-xs"
></div>
</div>
</div>
{% endif %}
{% endif %}
{% if not 'objective_status' in charts %}
<div class="xl:col-span-3 md:col-span-6 col-span-12 bg-white p-6 rounded-md shadow-card">
<div class="flex justify-between items-center mb-2">
<h3 class="text-md font-semibold">{% trans "Objective Status" %}</h3>
</div>
<div class="w-full max-w-xs mx-auto">
<div class="relative">
<canvas id="objectiveChart"></canvas>
</div>
<div id="feedbackChartLegend" class="mt-4 flex justify-center flex-wrap gap-3 text-xs"
></div>
</div>
</div>
{% endif %}
{% endif %}
</div>
<script src="{% static 'dashboard/employeeChart.js' %}"></script>
@@ -545,3 +490,14 @@
<!-- onboarding dashboard -->
<script src="{% static 'dashboard/onboardChart.js' %}"></script>
{% endif %}
{% if "attendance"|app_installed %}
{% if perms.attendance.view_attendance or request.user|is_reportingmanager %}
<script src="{% static 'dashboard/attendanceChart.js' %}"></script>
{% endif %}
{% endif %}
{% if "leave"|app_installed %}
<script src="{% static 'dashboard/onLeave.js' %}"></script>
<script src="{% static 'dashboard/leaveChart.js' %}"></script>
{% endif %}

View File

@@ -1,67 +1,168 @@
$(document).ready(function () {
//Todays leave count department wise chart
if (document.getElementById("overAllLeave")){
var myChart1 = document.getElementById("overAllLeave").getContext("2d");
var overAllLeave = new Chart(myChart1, {
let overAllLeaveChart = null;
function renderOverAllLeaveChart(data, labels) {
const ctx = document.getElementById("overAllLeave")?.getContext("2d");
if (!ctx) return;
const dataset = [{
label: "Leave count",
data: data,
backgroundColor: ["#cfe9ff", "#ffc9de", "#e6ccff"], // Customize as needed
borderWidth: 0,
}];
if (overAllLeaveChart) {
overAllLeaveChart.data.labels = labels;
overAllLeaveChart.data.datasets = dataset;
overAllLeaveChart.update();
return;
}
overAllLeaveChart = new Chart(ctx, {
type: "doughnut",
data: {
labels: [],
datasets: [
{
label: "Leave count",
data: [],
backgroundColor: null,
},
],
labels: labels,
datasets: dataset,
},
options: {
cutout: "70%",
responsive: true,
maintainAspectRatio: false,
onClick: (e, activeEls) => {
let datasetIndex = activeEls[0].datasetIndex;
let dataIndex = activeEls[0].index;
let datasetLabel = e.chart.data.datasets[datasetIndex].label;
let value = e.chart.data.datasets[datasetIndex].data[dataIndex];
let label = e.chart.data.labels[dataIndex];
params =`?department_name=${label}&overall_leave=${$("#overAllLeaveSelect").val()}`;
window.location.href = "/leave/request-view" + params;
if (activeEls.length > 0) {
const datasetIndex = activeEls[0].datasetIndex;
const dataIndex = activeEls[0].index;
const label = e.chart.data.labels[dataIndex];
const selected = $("#overAllLeaveSelect").val();
const params = `?department_name=${label}&overall_leave=${selected}`;
window.location.href = "/leave/request-view" + params;
}
},
plugins: {
legend: {
position: "bottom",
labels: {
usePointStyle: true,
pointStyle: "circle",
padding: 20,
font: {
size: 12,
},
color: "#000",
},
},
tooltip: {
padding: 10,
cornerRadius: 4,
backgroundColor: "#333",
titleColor: "#fff",
bodyColor: "#fff",
callbacks: {
label: function (context) {
return context.parsed;
},
},
},
},
},
plugins: [
{
afterRender: (chart) => emptyChart(chart),
afterRender: (chart) => {
if (typeof emptyChart === "function") {
emptyChart(chart);
}
},
},
],
});
}
$.ajax({
type: "GET",
url: "/leave/overall-leave?overall_leave=today",
dataType: "json",
success: function (response) {
if (overAllLeave){
overAllLeave.data.labels = response.labels;
overAllLeave.data.datasets[0].data = response.data;
overAllLeave.data.datasets[0].backgroundColor = null;
overAllLeave.update();
}
},
});
$(document).on("change", "#overAllLeaveSelect", function () {
var selected = $(this).val();
function fetchOverAllLeaveData(overallLeaveType = "today") {
$.ajax({
type: "GET",
url: `/leave/overall-leave?overall_leave=${selected}`,
url: `/leave/overall-leave?overall_leave=${overallLeaveType}`,
dataType: "json",
success: function (response) {
overAllLeave.data.labels = response.labels;
overAllLeave.data.datasets[0].data = response.data;
overAllLeave.data.datasets[0].backgroundColor = null;
overAllLeave.update();
renderOverAllLeaveChart(response.data, response.labels);
},
});
});
}
//Today leave employees chart
// Initial chart load
fetchOverAllLeaveData();
// Dropdown change event
$(document).on("change", "#overAllLeaveSelect", function () {
fetchOverAllLeaveData($(this).val());
});
});
// $(document).ready(function () {
// //Todays leave count department wise chart
// if (document.getElementById("overAllLeave")){
// var myChart1 = document.getElementById("overAllLeave").getContext("2d");
// var overAllLeave = new Chart(myChart1, {
// type: "doughnut",
// data: {
// labels: [],
// datasets: [
// {
// label: "Leave count",
// data: [],
// backgroundColor: null,
// },
// ],
// },
// options: {
// responsive: true,
// maintainAspectRatio: false,
// onClick: (e, activeEls) => {
// let datasetIndex = activeEls[0].datasetIndex;
// let dataIndex = activeEls[0].index;
// let datasetLabel = e.chart.data.datasets[datasetIndex].label;
// let value = e.chart.data.datasets[datasetIndex].data[dataIndex];
// let label = e.chart.data.labels[dataIndex];
// params =`?department_name=${label}&overall_leave=${$("#overAllLeaveSelect").val()}`;
// window.location.href = "/leave/request-view" + params;
// },
// },
// plugins: [
// {
// afterRender: (chart) => emptyChart(chart),
// },
// ],
// });
// }
// $.ajax({
// type: "GET",
// url: "/leave/overall-leave?overall_leave=today",
// dataType: "json",
// success: function (response) {
// if (overAllLeave){
// overAllLeave.data.labels = response.labels;
// overAllLeave.data.datasets[0].data = response.data;
// overAllLeave.data.datasets[0].backgroundColor = null;
// overAllLeave.update();
// }
// },
// });
// $(document).on("change", "#overAllLeaveSelect", function () {
// var selected = $(this).val();
// $.ajax({
// type: "GET",
// url: `/leave/overall-leave?overall_leave=${selected}`,
// dataType: "json",
// success: function (response) {
// overAllLeave.data.labels = response.labels;
// overAllLeave.data.datasets[0].data = response.data;
// overAllLeave.data.datasets[0].backgroundColor = null;
// overAllLeave.update();
// },
// });
// });
// //Today leave employees chart
// });

View File

@@ -1,99 +1,215 @@
$(document).ready(function() {
const objectiveChart = document.getElementById("objectiveChart");
// data dictionary
var objectiveStatusData = {
labels: [],
datasets: [
{
label: "objective",
data: [],
backgroundColor: ["#8de5b3", "#f0a8a6", "#8ed1f7", "#f8e08e", "#c2c7cc"],
hoverOffset: 3,
},
],
message:"",
};
if (document.getElementById("objectiveChart")) {
const ctx = document.getElementById("objectiveChart")?.getContext("2d");
// chart constructor
if (objectiveChart != null) {
var objectiveStatusChart = new Chart(objectiveChart, {
type: "doughnut",
data: objectiveStatusData,
options: {
let objectiveChartInstance = null;
function renderObjectiveChart(dataSet, labels) {
const updatedDataSet = dataSet.map((ds) => ({
...ds,
backgroundColor: ["#f8e08e", "#c2c7cc","#8de5b3", "#f0a8a6", "#8ed1f7"],
borderWidth: 0,
}));
if (objectiveChartInstance) {
objectiveChartInstance.destroy();
}
objectiveChartInstance = new Chart(ctx, {
type: "doughnut",
data: {
labels: labels,
datasets: updatedDataSet,
},
options: {
cutout: "70%",
responsive: true,
maintainAspectRatio:false,
maintainAspectRatio: false,
onClick: (e, activeEls) => {
let datasetIndex = activeEls[0].datasetIndex;
let dataIndex = activeEls[0].index;
let datasetLabel = e.chart.data.datasets[datasetIndex].label;
let value = e.chart.data.datasets[datasetIndex].data[dataIndex];
let label = e.chart.data.labels[dataIndex];
let params = "?status=" + label + "&archive=false" + "&dashboard=True";
if (!activeEls.length) return;
const dataIndex = activeEls[0].index;
const label = labels[dataIndex];
const params = `?status=${label}&archive=false&dashboard=True`;
$.ajax({
url: "/pms/objective-dashboard-view" + params,
type: "GET",
headers: {
"X-Requested-With": "XMLHttpRequest",
},
success: (response) => {
$("#dashboard").html(response);
},
error: (error) => {
console.log("Error", error);
},
url: "/pms/objective-dashboard-view" + params,
type: "GET",
headers: { "X-Requested-With": "XMLHttpRequest" },
success: (response) => {
$("#dashboard").html(response);
$("#back_button").removeClass("d-none");
},
error: (error) => {
console.error("Error loading dashboard:", error);
},
});
$("#back_button").removeClass("d-none");
},
plugins: {
legend: {
position: "bottom",
labels: {
usePointStyle: true,
pointStyle: "circle",
padding: 20,
font: { size: 12 },
color: "#000",
},
},
tooltip: {
padding: 10,
cornerRadius: 4,
backgroundColor: "#333",
titleColor: "#fff",
bodyColor: "#fff",
callbacks: {
label: function (context) {
return context.parsed;
},
},
},
},
},
plugins: [
{
afterRender: (chart) => {
if (typeof emptyChart === "function") {
emptyChart(chart);
}
},
},
],
});
}
// Update chart data and redraw
function updateObjectiveChart(data) {
const dataset = [{
label: "Objective",
data: data.objective_value,
}];
renderObjectiveChart(dataset, data.objective_label);
}
// Initial fetch
$.ajax({
url: "/pms/dashboard-objective-status",
type: "GET",
dataType: "json",
headers: { "X-Requested-With": "XMLHttpRequest" },
success: (response) => {
updateObjectiveChart(response);
},
plugins: [{
afterRender: (chart)=>emptyChart(chart)
}],
});
}
error: (error) => {
console.error("Error loading objective chart data:", error);
},
});
function objectiveStatusDataUpdate(data) {
objectiveStatusData.labels = data.objective_label;
objectiveStatusData.datasets[0].data = data.objective_value;
objectiveStatusData.message = data.message;
if (objectiveStatusChart){
objectiveStatusChart.update();
// Optional chart type toggle (pie/bar/doughnut etc.)
$("#objective-status-chart").click(function () {
const types = ["bar", "doughnut", "pie", "line"];
const current = types.indexOf(objectiveChartInstance.config.type);
const nextType = types[(current + 1) % types.length];
objectiveChartInstance.config.type = nextType;
objectiveChartInstance.update();
});
}
}
// const objectiveChart = document.getElementById("objectiveChart");
// // data dictionary
// var objectiveStatusData = {
// labels: [],
// datasets: [
// {
// label: "objective",
// data: [],
// backgroundColor: ["#8de5b3", "#f0a8a6", "#8ed1f7", "#f8e08e", "#c2c7cc"],
// hoverOffset: 3,
// },
// ],
// message:"",
// };
$.ajax({
url: "/pms/dashboard-objective-status",
type: "GET",
dataType: "json",
headers: {
"X-Requested-With": "XMLHttpRequest",
},
success: (response) => {
objectiveStatusDataUpdate(response);
},
error: (error) => {
console.log("Error", error);
},
});
// // chart constructor
// if (objectiveChart != null) {
// var objectiveStatusChart = new Chart(objectiveChart, {
// type: "doughnut",
// data: objectiveStatusData,
// options: {
// responsive: true,
// maintainAspectRatio:false,
// onClick: (e, activeEls) => {
// let datasetIndex = activeEls[0].datasetIndex;
// let dataIndex = activeEls[0].index;
// let datasetLabel = e.chart.data.datasets[datasetIndex].label;
// let value = e.chart.data.datasets[datasetIndex].data[dataIndex];
// let label = e.chart.data.labels[dataIndex];
// let params = "?status=" + label + "&archive=false" + "&dashboard=True";
// chart change
$("#objective-status-chart").click(function (e) {
var chartType = objectiveStatusChart.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";
}
objectiveStatusChart.config.type = chartType;
if (objectiveStatusChart){
objectiveStatusChart.update();
}
// $.ajax({
// url: "/pms/objective-dashboard-view" + params,
// type: "GET",
// headers: {
// "X-Requested-With": "XMLHttpRequest",
// },
// success: (response) => {
// $("#dashboard").html(response);
// },
// error: (error) => {
// console.log("Error", error);
// },
// });
// $("#back_button").removeClass("d-none");
// },
// },
// plugins: [{
// afterRender: (chart)=>emptyChart(chart)
// }],
// });
// }
});
// function objectiveStatusDataUpdate(data) {
// objectiveStatusData.labels = data.objective_label;
// objectiveStatusData.datasets[0].data = data.objective_value;
// objectiveStatusData.message = data.message;
// if (objectiveStatusChart){
// objectiveStatusChart.update();
// }
// }
// $.ajax({
// url: "/pms/dashboard-objective-status",
// type: "GET",
// dataType: "json",
// headers: {
// "X-Requested-With": "XMLHttpRequest",
// },
// success: (response) => {
// objectiveStatusDataUpdate(response);
// },
// error: (error) => {
// console.log("Error", error);
// },
// });
// // chart change
// $("#objective-status-chart").click(function (e) {
// var chartType = objectiveStatusChart.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";
// }
// objectiveStatusChart.config.type = chartType;
// if (objectiveStatusChart){
// objectiveStatusChart.update();
// }
// });
// objecitve chart section end
@@ -281,85 +397,221 @@ $("#key-result-status-chart").click(() => {
// key result chart section
const feedbackStatusChartCtx = document.getElementById("feedbackChart");
// const feedbackStatusChartCtx = document.getElementById("feedbackChart");
// data dictionary
var feedbackStatusData = {
labels: [],
datasets: [
{
label: "Feedback",
data: [],
backgroundColor: ["#8de5b3", "#f0a8a6", "#8ed1f7", "#f8e08e", "#c2c7cc"],
hoverOffset: 3,
// // data dictionary
// var feedbackStatusData = {
// labels: [],
// datasets: [
// {
// label: "Feedback",
// data: [],
// backgroundColor: ["#8de5b3", "#f0a8a6", "#8ed1f7", "#f8e08e", "#c2c7cc"],
// hoverOffset: 3,
// },
// ],
// message:"",
// };
// // chart constructor
// if (feedbackStatusChartCtx != null) {
// var feedbackStatusChart = new Chart(feedbackStatusChartCtx, {
// type: "pie",
// data: feedbackStatusData,
// options: {
// responsive: true,
// maintainAspectRatio:false,
// onClick: (e, activeEls) => {
// let datasetIndex = activeEls[0].datasetIndex;
// let dataIndex = activeEls[0].index;
// let datasetLabel = e.chart.data.datasets[datasetIndex].label;
// let value = e.chart.data.datasets[datasetIndex].data[dataIndex];
// let label = e.chart.data.labels[dataIndex];
// let params = "?status=" + label + "&archive=false";
// window.location.href = "/pms/feedback-view" + params;
// },
// },
// plugins: [{
// afterRender: (chart)=>emptyChart(chart)
// }],
// });
// }
// function feedbackStatusDataUpdate(data) {
// feedbackStatusData.labels = data.feedback_label;
// feedbackStatusData.datasets[0].data = data.feedback_value;
// feedbackStatusData.message = data.message;
// if (feedbackStatusChart){
// feedbackStatusChart.update();
// }
// }
// $.ajax({
// url: "/pms/dashboard-feedback-status",
// type: "GET",
// dataType: "json",
// headers: {
// "X-Requested-With": "XMLHttpRequest",
// },
// success: (response) => {
// feedbackStatusDataUpdate(response);
// },
// error: (error) => {
// console.log("Error", error);
// },
// });
// // chart change
// $("#feedback-status-chart").click(function (e) {
// var chartType = feedbackStatusChart.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";
// }
// feedbackStatusChart.config.type = chartType;
// if (feedbackStatusChart){
// feedbackStatusChart.update();
// }
// });
if (document.getElementById("feedbackChart")) {
const ctx = document.getElementById("feedbackChart")?.getContext("2d");
let feedbackChartInstance = null;
let visibility = [];
function renderFeedbackChart(dataSet, labels) {
const values = dataSet[0].data;
const colors = [
"#93c5fd",
"#facc15",
"#f87171",
"#ddd6fe",
"#a5b4fc",
"#d1d5db",
];
visibility = Array(labels.length).fill(true);
// Destroy previous chart if it exists
if (feedbackChartInstance) {
feedbackChartInstance.destroy();
}
feedbackChartInstance = new Chart(ctx, {
type: "doughnut",
data: {
labels: labels,
datasets: [
{
...dataSet[0],
backgroundColor: colors.slice(0, labels.length),
borderWidth: 0,
borderRadius: 10,
hoverOffset: 8,
},
],
},
],
message:"",
};
// chart constructor
if (feedbackStatusChartCtx != null) {
var feedbackStatusChart = new Chart(feedbackStatusChartCtx, {
type: "pie",
data: feedbackStatusData,
options: {
responsive: true,
maintainAspectRatio:false,
onClick: (e, activeEls) => {
let datasetIndex = activeEls[0].datasetIndex;
let dataIndex = activeEls[0].index;
let datasetLabel = e.chart.data.datasets[datasetIndex].label;
let value = e.chart.data.datasets[datasetIndex].data[dataIndex];
let label = e.chart.data.labels[dataIndex];
let params = "?status=" + label + "&archive=false";
window.location.href = "/pms/feedback-view" + params;
cutout: "70%",
responsive: true,
maintainAspectRatio: false,
onClick: function (e, activeEls) {
if (!activeEls.length) return;
const dataIndex = activeEls[0].index;
const label = labels[dataIndex];
const params = "?status=" + label + "&archive=false";
window.location.href = "/pms/feedback-view" + params;
},
plugins: {
legend: { display: false },
tooltip: {
backgroundColor: "#111827",
bodyColor: "#f3f4f6",
borderColor: "#e5e7eb",
borderWidth: 1,
},
},
plugins: [{
afterRender: (chart)=>emptyChart(chart)
}],
},
plugins: [
{
afterRender: (chart) => {
if (typeof emptyChart === "function") {
emptyChart(chart);
}
},
},
],
});
}
function feedbackStatusDataUpdate(data) {
feedbackStatusData.labels = data.feedback_label;
feedbackStatusData.datasets[0].data = data.feedback_value;
feedbackStatusData.message = data.message;
if (feedbackStatusChart){
feedbackStatusChart.update();
// 🧩 Custom Legend Generation
const $legendContainer = $("#feedbackChartLegend");
$legendContainer.empty();
labels.forEach((label, index) => {
const color = colors[index % colors.length];
const $item = $(`
<div style="display: flex; align-items: center; margin-bottom: 6px; cursor: pointer;">
<span style="display: inline-block; width: 12px; height: 12px; border-radius: 50%; background-color: ${color}; margin-right: 8px;"></span>
<span class="legend-label">${label}</span>
</div>
`);
$legendContainer.append($item);
$item.on("click", function () {
visibility[index] = !visibility[index];
feedbackChartInstance.data.datasets[0].data = values.map((val, i) =>
visibility[i] ? val : 0
);
const $dot = $(this).find("span").first();
const $label = $(this).find(".legend-label");
if (visibility[index]) {
$dot.css("opacity", "1");
$label.css("text-decoration", "none");
} else {
$dot.css("opacity", "0.4");
$label.css("text-decoration", "line-through");
}
feedbackChartInstance.update();
});
});
}
}
$.ajax({
url: "/pms/dashboard-feedback-status",
type: "GET",
dataType: "json",
headers: {
// 🔄 Load data via AJAX
function fetchFeedbackData() {
$.ajax({
url: "/pms/dashboard-feedback-status",
type: "GET",
dataType: "json",
headers: {
"X-Requested-With": "XMLHttpRequest",
},
success: (response) => {
feedbackStatusDataUpdate(response);
},
error: (error) => {
console.log("Error", error);
},
});
},
success: function (response) {
const dataset = [
{
label: "Feedback",
data: response.feedback_value,
},
];
renderFeedbackChart(dataset, response.feedback_label);
},
error: function (error) {
console.error("Error loading feedback chart:", error);
},
});
}
fetchFeedbackData();
}
// chart change
$("#feedback-status-chart").click(function (e) {
var chartType = feedbackStatusChart.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";
}
feedbackStatusChart.config.type = chartType;
if (feedbackStatusChart){
feedbackStatusChart.update();
}
});
});