import { AfterViewInit, Component, OnInit, ViewChild, ViewEncapsulation, ElementRef
} from '@angular/core';
import { Chart } from 'chart.js/auto';
import { DashboardService } from './service/dashboard.service';
import { PurchaseOrderService } from '../core-module/purchase-order/purchase-
order.service';
import { UserRoleService } from '../core-module/user-role/user-role.service';
declare var anychart: any;
@Component({
selector: 'app-dashboard',
templateUrl: './dashboard.component.html',
styleUrls: ['./dashboard.component.scss'],
encapsulation: ViewEncapsulation.None,
})
export class DashboardComponent implements AfterViewInit {
@ViewChild('canvasContainer', { static: false }) canvasContainer!: ElementRef;
@ViewChild('canvasContainer1', { static: false }) canvasContainer1!: ElementRef;
@ViewChild('canvasContainer2', { static: false }) canvasContainer2!: ElementRef;
@ViewChild('canvasContainer3', { static: false }) canvasContainer3!: ElementRef;
@ViewChild('canvasContainer4', { static: false }) canvasContainer4!: ElementRef;
@ViewChild('canvasContainer5', { static: false }) canvasContainer5!: ElementRef;
companyId: any;
employeeLoginId: any;
rfpData: any;
negotiation: any;
grn: any;
PurchaseOrder: any;
Invoice: any;
loading: boolean = false;
poinvoicegraphdata: any[] = [];
po_id: any;
poInvoice_id: any;
chartOptions: any;
poInvoicelistData: any[] = [];
polistData: any[] = [];
extrapodata: any;
extrapoinvoicedata: any;
constructor(
private readonly dashboard_service: DashboardService,
private poservice: PurchaseOrderService,
private userRoleService: UserRoleService,
) {
}
ngOnInit() {
const storedData = localStorage.getItem('user_details') ?? "{}";
const userDetails = JSON.parse(storedData);
if (userDetails && userDetails.companyId) {
this.companyId = userDetails.companyId;
this.employeeLoginId = userDetails.employeeId;
}
// First get the PO list and invoice data to properly initialize po_id and
poInvoice_id
this.getPOListById();
this.getAllInvoiceData();
this.getAllDashboardCounts(this.employeeLoginId, this.companyId);
// We'll call getAllPoDataInvoiceDataByGraph after we have valid IDs
}
getPOListById() {
this.poservice.getPOLIstById(this.employeeLoginId).subscribe((res: any) => {
if (res.body && res.body.data) {
this.polistData = res.body.data;
// Make sure we're getting a valid po_id (first one from the list if it's
an array)
if (Array.isArray(this.polistData) && this.polistData.length > 0) {
this.po_id = this.polistData[0].po_id;
}
this.checkAndLoadGraph(); // Try to load graph after getting PO ID
}
});
}
getAllInvoiceData() {
this.userRoleService.getAllInvoiceData(this.employeeLoginId).subscribe((res:
any) => {
if (res.body && res.body.data) {
this.poInvoicelistData = res.body.data;
// Make sure we're getting a valid poInvoice_id (first one from the list if
it's an array)
if (Array.isArray(this.poInvoicelistData) && this.poInvoicelistData.length
> 0) {
this.poInvoice_id = this.poInvoicelistData[0].poInvoice_id;
}
this.checkAndLoadGraph(); // Try to load graph after getting Invoice ID
}
});
}
// Helper method to call the graph API once we have both IDs
checkAndLoadGraph() {
if (this.employeeLoginId && this.po_id !== undefined && this.poInvoice_id !==
undefined) {
console.log('Loading graph with:', this.employeeLoginId, this.po_id,
this.poInvoice_id);
this.getAllPoDataInvoiceDataByGraph(this.employeeLoginId, this.po_id,
this.poInvoice_id);
}
}
getAllDashboardCounts(empid: any, company_id: any) {
if (empid && company_id) {
this.dashboard_service.getAllDashboardCounts(empid,
company_id).subscribe((res: any) => {
if (res.body) {
this.rfpData = res.body.counts.rfpCount;
this.negotiation = res.body.counts.negotiationCount;
this.grn = res.body.counts.grnCount;
this.PurchaseOrder = res.body.counts.poCount;
this.Invoice = res.body.counts.invoiceCount;
}
}, (err: any) => {
})
}
}
async ngAfterViewInit(): Promise<void> {
await this.loadAnyChartScript();
this.drawChart();
// Give enough delay to allow CanvasJS to inject watermark
setTimeout(() => {
const observer = new MutationObserver((mutations) => {
this.removeCanvasJSWatermark();
});
// Start observing the chart container for changes
observer.observe(this.canvasContainer.nativeElement, {
childList: true,
subtree: true
});
const anchorTags1 =
this.canvasContainer1.nativeElement.querySelectorAll('a');
anchorTags1.forEach((a: HTMLAnchorElement) => {
if (a.href.includes('canvasjs.com')) {
a.style.display = 'none';
}
});
const anchorTags2 =
this.canvasContainer2.nativeElement.querySelectorAll('a');
anchorTags2.forEach((a: HTMLAnchorElement) => {
a.style.display = 'none';
});
const anchorTags3 =
this.canvasContainer3.nativeElement.querySelectorAll('a');
anchorTags3.forEach((a: HTMLAnchorElement) => {
if (a.href.includes('canvasjs.com')) {
a.style.display = 'none';
}
});
const anchorTags4 =
this.canvasContainer4.nativeElement.querySelectorAll('a');
anchorTags4.forEach((a: HTMLAnchorElement) => {
if (a.href.includes('canvasjs.com')) {
a.style.display = 'none';
}
});
const anchorTags5 =
this.canvasContainer5.nativeElement.querySelectorAll('a');
anchorTags5.forEach((a: HTMLAnchorElement) => {
if (a.href.includes('canvasjs.com')) {
a.style.display = 'none';
}
});
}, 1000); // Adjust delay if needed
}
// Current Year Monthly Transactions start//
getAllPoDataInvoiceDataByGraph(empId: number, poId: number, poInvoiceId: number)
{
this.dashboard_service.getAllPoDataInvoiceDataByGraph(empId, poId, poInvoiceId)
.subscribe(
(res: any) => {
if (res.body && res.body.result) {
const result = res.body.result;
let poitemData: any[] = [];
let invoiceData: any[] = [];
if (Array.isArray(result)) {
result.forEach(item => {
if (item.poitemData && Array.isArray(item.poitemData)) {
poitemData = poitemData.concat(item.poitemData);
}
if (item.data && Array.isArray(item.data)) {
invoiceData = invoiceData.concat(item.data);
}
});
} else {
poitemData = result.poitemData || [];
invoiceData = result.data || [];
}
this.extrapodata = poitemData;
this.extrapoinvoicedata = invoiceData;
if (!Array.isArray(this.extrapodata) || !
Array.isArray(this.extrapoinvoicedata)) {
console.error('Invalid data structure for chart');
return;
}
const purchasePoints = this.extrapodata.map((item: any) => {
if (!item || !item.createdAt) {
console.warn('Invalid PO item:', item);
return null;
}
return {
x: new Date(item.createdAt),
y: Number(item.priceIncludingCharges) || 0
};
}).filter((point): point is { x: Date, y: number } => point !== null);
const invoicePoints = this.extrapoinvoicedata.map((item: any) => {
if (!item) {
console.warn('Invalid invoice item: null or undefined');
return null;
}
const dateValue = item.createdAt || item.Date;
if (!dateValue) {
console.warn('Invoice item missing date property:', item);
return null;
}
const amount = Number(item.partiallyPaid);
if (isNaN(amount)) {
console.warn('Invalid partiallyPaid value:', item.partiallyPaid);
return null;
}
return {
x: new Date(dateValue),
y: amount
};
}).filter((point): point is { x: Date, y: number } => point !== null);
if (purchasePoints.length === 0 && invoicePoints.length === 0) {
this.chartOptions = {
title: {
text: "No data available for selected parameters"
}
};
return;
}
let minDate = new Date();
if (purchasePoints.length > 0) {
const earliestPurchaseDate = new
Date(Math.min(...purchasePoints.map(p => p.x.getTime())));
minDate = earliestPurchaseDate;
}
if (invoicePoints.length > 0) {
const earliestInvoiceDate = new Date(Math.min(...invoicePoints.map(p
=> p.x.getTime())));
if (earliestInvoiceDate < minDate) {
minDate = earliestInvoiceDate;
}
}
minDate.setDate(minDate.getDate() - 5);
this.chartOptions = {
animationEnabled: true,
title: {
text: "Current Year Monthly Transactions"
},
axisX: {
interval: 10,
intervalType: "day",
title: "Date & Time",
valueFormatString: "DD MMM HH:mm",
labelAngle: -45,
labelFontColor: "rgb(0,75,141)",
minimum: minDate
},
axisY: {
title: "Amount in Rupees (₹)",
interlacedColor: "#EBF2FA",
tickColor: "azure",
titleFontColor: "#4f81bc",
valueFormatString: "#,###.##",
includeZero: true
},
legend: {
verticalAlign: "bottom",
horizontalAlign: "center",
cursor: "pointer",
itemclick: (e: any) => {
e.dataSeries.visible = !e.dataSeries.visible;
e.chart.render();
}
},
data: [
{
type: "area",
name: "Purchase Order",
showInLegend: true,
markerSize: 5,
dataPoints: purchasePoints
},
{
type: "area",
name: "Invoice",
showInLegend: true,
markerSize: 5,
dataPoints: invoicePoints
}
]
};
setTimeout(() => {
this.chartOptions = { ...this.chartOptions };
}, 100);
} else {
console.error('Invalid response format:', res);
}
},
(err: any) => console.error("Graph load error:", err)
);
}
// Current Year Monthly Transactions end//
// Current Year Spent Analysis start//
chartOptions1 = {
animationEnabled: true,
title: {
text: "Current Year Spent Analysis"
},
axisX: {
labelAngle: 180
},
axisY: {
title: "Amount in Rupees (₹)",
interlacedColor: "#EBF2FA",
tickColor: "azure",
titleFontColor: "#4f81bc",
valueFormatString: "#(₹)",
includeZero: true
},
toolTip: {
shared: true
},
legend: {
cursor: "pointer",
itemclick: function (e: any) {
if (typeof (e.dataSeries.visible) === "undefined" || e.dataSeries.visible)
{
e.dataSeries.visible = false;
}
else {
e.dataSeries.visible = true;
}
e.chart.render();
}
},
dataPointWidth: 50,
data: [{
type: "column",
name: "Created",
legendText: "Created",
showInLegend: true,
dataPoints: [
{ label: "Purchase Request", y: 262 },
{ label: "Purchase Order", y: 211 },
{ label: "Invoice", y: 175 },
{ label: "GRN", y: 137 },
]
}, {
type: "column",
name: "Approved",
legendText: "Approved",
axisYType: "secondary",
showInLegend: true,
dataPoints: [
{ label: "Purchase Request", y: 11.15 },
{ label: "Purchase Order", y: 2.5 },
{ label: "Invoice", y: 3.6 },
{ label: "GRN", y: 4.2 },
]
}]
}
loadAnyChartScript(): Promise<void> {
return new Promise((resolve, reject) => {
const script = document.createElement('script');
script.src = 'https://cdn.anychart.com/releases/8.11.0/js/anychart-
bundle.min.js';
script.onload = () => resolve();
script.onerror = () => reject('Failed to load AnyChart');
document.body.appendChild(script);
});
}
removeCanvasJSWatermark(): void {
const anchorTags = document.querySelectorAll('a[href*="canvasjs.com"]');
anchorTags.forEach((a: any) => {
a.style.display = 'none';
});
const creditsElements = document.querySelectorAll('.canvasjs-chart-credit');
creditsElements.forEach((el: any) => {
el.style.display = 'none';
});
}
drawChart(): void {
const data = [
{
name: 'IT Hardware',
value: 18741194
}
];
const anychart = (window as any).anychart;
const chart = anychart.treeMap(data, 'as-tree');
chart.title('Spend By Category Level 1');
// Create linear color scale
const colorScale = anychart.scales.linearColor();
colorScale.colors(['#00BCD4', '#8BC34A', '#FFEB3B']);
colorScale.ticks().interval(0.25);
// Apply color scale to chart
chart.colorScale(colorScale);
// ✅ Enable color range display (the gradient bar at the bottom)
const colorRange = chart.colorRange();
colorRange.enabled(true); // Make it visible
colorRange.colorLineSize(20); // Adjust thickness if needed
colorRange.marker().size(10); // Optional: size of marker/triangle
colorRange.labels().fontSize(12); // Optional: label size
// Tooltip
chart.tooltip().format('{%name}\n{%value}');
// Set the container and draw
chart.container('treemapContainer');
chart.draw();
}
//Spend By Category Level 1 graph start //
//Spend By Suppllier start//
chartOptions2 = {
title: {
text: "Spend By Supplier"
},
axisX: {
labelAngle: -25,
labelFontColor: "rgb(0,75,141)",
},
axisY: {
title: "Vendor Details",
interlacedColor: "#EBF2FA",
tickColor: "azure",
titleFontColor: "#4f81bc",
includeZero: true
},
animationEnabled: true,
dataPointWidth: 25,
data: [{
type: "column",
dataPoints: [
{ label: "RELIANCE RETAIL LIMITED", y: 3052 },
{ label: "G-SOFT SOLUTIONS", y: 2063 },
{ label: "ELITE MINDZ", y: 1060 },
{ label: "TECH INDIA", y: 380 },
]
}]
}
//Spend By Suppllier end//
//Spend By Region start//
chartOptions3 = {
title: {
text: "Spend By Region"
},
animationEnabled: true,
axisX: {
labelAngle: -25,
labelFontColor: "rgb(0,75,141)",
},
axisY: {
suffix: "M",
title: "Values",
interlacedColor: "#EBF2FA",
tickColor: "azure",
titleFontColor: "#4f81bc",
includeZero: true
},
toolTip: {
content: "{label} Year 2025: {y}"
},
data: [{
type: "bar",
indexLabel: "{y}",
yValueFormatString: "#,###M",
dataPoints: [
{ label: "Africa", y: 15 },
{ label: "Mena", y: 20 },
{ label: "South Asia", y: 24 },
{ label: "East Asia", y: 29 },
{ label: "Europe", y: 73 },
{ label: "America", y: 87 }
]
}]
}
//Spend By Region end//
//Spend By Contract start//
chartOptions4 = {
animationEnabled: true,
title: {
text: "Spend By Contract",
fontFamily: "Arial",
fontSize: 18,
fontWeight: "normal"
},
data: [{
type: "doughnut",
startAngle: 75,
indexLabelFontSize: 12,
indexLabelFontFamily: "Arial",
indexLabelFontColor: "#333",
indexLabelLineColor: "#888",
indexLabelPlacement: "outside",
radius: "90%",
innerRadius: "55%",
explodeOnClick: false,
indexLabel: "{name}",
indexLabelLineThickness: 1,
showInLegend: false,
dataPoints: [
{ y: 10, name: "Non Contracted", color: "#D3D3D3" },
{ y: 90, name: "Contracted", color: "#7cb5ec" }
],
// Add border to the doughnut
borderColor: "#EAEAEA",
borderThickness: 5
}],
backgroundColor: "transparent",
toolTip: {
enabled: true,
animationEnabled: true,
shared: false,
contentFormatter: function (e: any) {
return e.entries[0].dataPoint.name + "<br/>" +
"• Medals: " + e.entries[0].dataPoint.y.toLocaleString("en-US",
{ minimumFractionDigits: 2, maximumFractionDigits: 2 });
},
borderColor: "#CCCCCC",
cornerRadius: 4,
backgroundColor: "white"
}
}
//Spend By Contract end//
}