chart bug fix

This commit is contained in:
Naser Mansour 2025-12-27 20:26:53 +02:00
parent b69b4c8be2
commit a052266950
1 changed files with 164 additions and 187 deletions

View File

@ -375,7 +375,57 @@ new class extends Component
@else
<div
wire:ignore
x-data="trendsChart($wire.chartData)"
x-data="{
chart: null,
data: @js($chartData),
init() {
if (typeof Chart === 'undefined') return;
this.chart = new Chart(this.$refs.canvas, {
type: 'line',
data: {
labels: this.data.labels,
datasets: [
{
label: @js(__('admin_metrics.new_clients')),
data: this.data.newClients,
borderColor: '#D4AF37',
backgroundColor: 'rgba(212, 175, 55, 0.1)',
tension: 0.3,
fill: false,
},
{
label: @js(__('admin_metrics.consultations')),
data: this.data.consultations,
borderColor: '#0A1F44',
backgroundColor: 'rgba(10, 31, 68, 0.1)',
tension: 0.3,
fill: false,
}
]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
tooltip: { enabled: true },
legend: {
position: 'bottom',
rtl: document.dir === 'rtl',
}
},
scales: {
y: {
beginAtZero: true,
ticks: { precision: 0 }
}
}
}
});
},
destroy() {
if (this.chart) this.chart.destroy();
}
}"
class="h-64"
>
<canvas x-ref="canvas"></canvas>
@ -393,7 +443,50 @@ new class extends Component
@else
<div
wire:ignore
x-data="breakdownChart($wire.chartData.consultationBreakdown)"
x-data="{
chart: null,
data: @js($chartData['consultationBreakdown']),
init() {
if (typeof Chart === 'undefined') return;
const total = this.data.free + this.data.paid;
this.chart = new Chart(this.$refs.canvas, {
type: 'doughnut',
data: {
labels: [
@js(__('admin_metrics.free')) + ` (${this.data.free})`,
@js(__('admin_metrics.paid')) + ` (${this.data.paid})`
],
datasets: [{
data: [this.data.free, this.data.paid],
backgroundColor: ['#D4AF37', '#0A1F44'],
borderWidth: 0,
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
tooltip: {
callbacks: {
label: function(context) {
const value = context.parsed;
const percent = total > 0 ? Math.round((value / total) * 100) : 0;
return `${context.label}: ${percent}%`;
}
}
},
legend: {
position: 'bottom',
rtl: document.dir === 'rtl',
}
}
}
});
},
destroy() {
if (this.chart) this.chart.destroy();
}
}"
class="h-64"
>
<canvas x-ref="canvas"></canvas>
@ -411,7 +504,75 @@ new class extends Component
@else
<div
wire:ignore
x-data="noShowChart($wire.chartData)"
x-data="{
chart: null,
data: @js($chartData),
init() {
if (typeof Chart === 'undefined') return;
this.chart = new Chart(this.$refs.canvas, {
type: 'line',
data: {
labels: this.data.labels,
datasets: [{
label: @js(__('admin_metrics.noshow_rate_percent')),
data: this.data.noShowRates,
borderColor: '#E74C3C',
backgroundColor: 'rgba(231, 76, 60, 0.1)',
fill: true,
tension: 0.3,
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
tooltip: {
callbacks: {
label: function(context) {
return context.parsed.y + '%';
}
}
},
legend: {
position: 'bottom',
rtl: document.dir === 'rtl',
},
annotation: {
annotations: {
threshold: {
type: 'line',
yMin: 20,
yMax: 20,
borderColor: 'rgba(231, 76, 60, 0.5)',
borderWidth: 2,
borderDash: [5, 5],
label: {
display: true,
content: @js(__('admin_metrics.concerning_threshold')),
position: 'end'
}
}
}
}
},
scales: {
y: {
min: 0,
max: 100,
ticks: {
callback: function(value) {
return value + '%';
}
}
}
}
}
});
},
destroy() {
if (this.chart) this.chart.destroy();
}
}"
class="h-64"
>
<canvas x-ref="canvas"></canvas>
@ -448,188 +609,4 @@ new class extends Component
</div>
</div>
@script
<script>
// Ensure Chart.js is available
if (typeof Chart === 'undefined') {
console.error('Chart.js is not loaded');
}
Alpine.data('trendsChart', (data) => ({
chart: null,
init() {
if (typeof Chart === 'undefined') return;
this.chart = new Chart(this.$refs.canvas, {
type: 'line',
data: {
labels: data.labels,
datasets: [
{
label: @js(__('admin_metrics.new_clients')),
data: data.newClients,
borderColor: '#D4AF37',
backgroundColor: 'rgba(212, 175, 55, 0.1)',
tension: 0.3,
fill: false,
},
{
label: @js(__('admin_metrics.consultations')),
data: data.consultations,
borderColor: '#0A1F44',
backgroundColor: 'rgba(10, 31, 68, 0.1)',
tension: 0.3,
fill: false,
}
]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
tooltip: { enabled: true },
legend: {
position: 'bottom',
rtl: document.dir === 'rtl',
}
},
scales: {
y: {
beginAtZero: true,
ticks: { precision: 0 }
}
}
}
});
},
destroy() {
if (this.chart) {
this.chart.destroy();
}
}
}));
Alpine.data('breakdownChart', (data) => ({
chart: null,
init() {
if (typeof Chart === 'undefined') return;
const total = data.free + data.paid;
const freePercent = total > 0 ? Math.round((data.free / total) * 100) : 0;
const paidPercent = total > 0 ? Math.round((data.paid / total) * 100) : 0;
this.chart = new Chart(this.$refs.canvas, {
type: 'doughnut',
data: {
labels: [
@js(__('admin_metrics.free')) + ` (${data.free})`,
@js(__('admin_metrics.paid')) + ` (${data.paid})`
],
datasets: [{
data: [data.free, data.paid],
backgroundColor: ['#D4AF37', '#0A1F44'],
borderWidth: 0,
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
tooltip: {
callbacks: {
label: function(context) {
const value = context.parsed;
const percent = total > 0 ? Math.round((value / total) * 100) : 0;
return `${context.label}: ${percent}%`;
}
}
},
legend: {
position: 'bottom',
rtl: document.dir === 'rtl',
}
}
}
});
},
destroy() {
if (this.chart) {
this.chart.destroy();
}
}
}));
Alpine.data('noShowChart', (data) => ({
chart: null,
init() {
if (typeof Chart === 'undefined') return;
this.chart = new Chart(this.$refs.canvas, {
type: 'line',
data: {
labels: data.labels,
datasets: [{
label: @js(__('admin_metrics.noshow_rate_percent')),
data: data.noShowRates,
borderColor: '#E74C3C',
backgroundColor: 'rgba(231, 76, 60, 0.1)',
fill: true,
tension: 0.3,
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
tooltip: {
callbacks: {
label: function(context) {
return context.parsed.y + '%';
}
}
},
legend: {
position: 'bottom',
rtl: document.dir === 'rtl',
},
annotation: {
annotations: {
threshold: {
type: 'line',
yMin: 20,
yMax: 20,
borderColor: 'rgba(231, 76, 60, 0.5)',
borderWidth: 2,
borderDash: [5, 5],
label: {
display: true,
content: @js(__('admin_metrics.concerning_threshold')),
position: 'end'
}
}
}
}
},
scales: {
y: {
min: 0,
max: 100,
ticks: {
callback: function(value) {
return value + '%';
}
}
}
}
}
});
},
destroy() {
if (this.chart) {
this.chart.destroy();
}
}
}));
</script>
@endscript
</div>