diff --git a/resources/js/metrics.js b/resources/js/metrics.js
index e4f222d..102c938 100644
--- a/resources/js/metrics.js
+++ b/resources/js/metrics.js
@@ -1,84 +1,94 @@
-document.addEventListener('DOMContentLoaded', function () {
- const fetchData = async (url) => {
- const response = await fetch(url);
- return response.json();
- };
+let weeklyUploadersChart = null;
- const removeLoadingElement = (spinnerId) => {
- const spinner = document.getElementById(spinnerId);
- if (spinner) {
- spinner.remove();
- }
- };
+const formatDateToYYYYMMDD = (date) => {
+ return date.toISOString().split('T')[0];
+};
- const renderSchedulerDetails = async () => {
- const data = await fetchData('/metrics/scrapy-schedulers');
- removeLoadingElement('schedulerChartsSkeleton');
- const container = document.getElementById('schedulerChartsContainer');
- container.innerHTML = ''; // Clear loading skeleton
+const getStartOfWeek = (date) => {
+ const d = new Date(date);
+ const day = d.getDay();
+ const diff = d.getDate() - day + (day === 0 ? -6 : 1);
+ return new Date(d.setDate(diff));
+};
- const enabledWithStats = [];
- const enabledWithoutStats = [];
- const disabledWithStats = [];
- const disabledWithoutStats = [];
+const fetchData = async (url) => {
+ const response = await fetch(url);
+ return response.json();
+};
- data.forEach(scheduler => {
- if (scheduler.is_scheduler_disabled) {
- if (scheduler.last_run_state) {
- disabledWithStats.push(scheduler);
- } else {
- disabledWithoutStats.push(scheduler);
- }
+const removeLoadingElement = (spinnerId) => {
+ const spinner = document.getElementById(spinnerId);
+ if (spinner) {
+ spinner.remove();
+ }
+};
+
+const renderSchedulerDetails = async () => {
+ const data = await fetchData('/metrics/scrapy-schedulers');
+ removeLoadingElement('schedulerChartsSkeleton');
+ const container = document.getElementById('schedulerChartsContainer');
+ container.innerHTML = ''; // Clear loading skeleton
+
+ const enabledWithStats = [];
+ const enabledWithoutStats = [];
+ const disabledWithStats = [];
+ const disabledWithoutStats = [];
+
+ data.forEach(scheduler => {
+ if (scheduler.is_scheduler_disabled) {
+ if (scheduler.last_run_state) {
+ disabledWithStats.push(scheduler);
} else {
- if (scheduler.last_run_state) {
- enabledWithStats.push(scheduler);
+ disabledWithoutStats.push(scheduler);
+ }
+ } else {
+ if (scheduler.last_run_state) {
+ enabledWithStats.push(scheduler);
+ } else {
+ enabledWithoutStats.push(scheduler);
+ }
+ }
+ });
+
+ const createSchedulerSection = (schedulers, sectionTitle) => {
+ const section = document.createElement('div');
+ section.className = 'scheduler-section';
+ const sectionTitleElem = document.createElement('h5');
+ sectionTitleElem.textContent = sectionTitle;
+ section.appendChild(sectionTitleElem);
+
+ let row;
+ schedulers.forEach((scheduler, index) => {
+ if (index % 3 === 0) {
+ row = document.createElement('div');
+ row.className = 'row';
+ section.appendChild(row);
+ }
+
+ const schedulerCard = document.createElement('div');
+ schedulerCard.className = 'col-lg-4 col-md-6 col-sm-12 mb-4';
+
+ let cardClass = 'card-scheduler ';
+ if (scheduler.is_scheduler_disabled) {
+ cardClass += 'disabled';
+ } else if (scheduler.last_run_state === null) {
+ cardClass += 'enabled';
+ } else {
+ const logCounts = {
+ info: scheduler.last_run_state.log_count_info, warning: scheduler.last_run_state.log_count_warning, error: scheduler.last_run_state.log_count_error
+ };
+
+ const maxLogCount = Math.max(logCounts.info, logCounts.warning, logCounts.error);
+ if (maxLogCount === logCounts.error) {
+ cardClass += 'log-error';
+ } else if (maxLogCount === logCounts.warning) {
+ cardClass += 'log-warning';
} else {
- enabledWithoutStats.push(scheduler);
+ cardClass += 'log-info';
}
}
- });
- const createSchedulerSection = (schedulers, sectionTitle) => {
- const section = document.createElement('div');
- section.className = 'scheduler-section';
- const sectionTitleElem = document.createElement('h5');
- sectionTitleElem.textContent = sectionTitle;
- section.appendChild(sectionTitleElem);
-
- let row;
- schedulers.forEach((scheduler, index) => {
- if (index % 3 === 0) {
- row = document.createElement('div');
- row.className = 'row';
- section.appendChild(row);
- }
-
- const schedulerCard = document.createElement('div');
- schedulerCard.className = 'col-lg-4 col-md-6 col-sm-12 mb-4';
-
- let cardClass = 'card-scheduler ';
- if (scheduler.is_scheduler_disabled) {
- cardClass += 'disabled';
- } else if (scheduler.last_run_state === null) {
- cardClass += 'enabled';
- } else {
- const logCounts = {
- info: scheduler.last_run_state.log_count_info,
- warning: scheduler.last_run_state.log_count_warning,
- error: scheduler.last_run_state.log_count_error
- };
-
- const maxLogCount = Math.max(logCounts.info, logCounts.warning, logCounts.error);
- if (maxLogCount === logCounts.error) {
- cardClass += 'log-error';
- } else if (maxLogCount === logCounts.warning) {
- cardClass += 'log-warning';
- } else {
- cardClass += 'log-info';
- }
- }
-
- const lastRunState = scheduler.last_run_state ? `
+ const lastRunState = scheduler.last_run_state ? `
Items Scraped: ${scheduler.last_run_state.item_scraped_count}
Items Dropped: ${scheduler.last_run_state.item_dropped_count}
Info: ${scheduler.last_run_state.log_count_info}
@@ -86,7 +96,7 @@ document.addEventListener('DOMContentLoaded', function () {
Error: ${scheduler.last_run_state.log_count_error}
` : '';
- const cardContent = `
+ const cardContent = `
${scheduler.name}
Last Run: ${scheduler.time_since_last_run}
@@ -94,397 +104,335 @@ document.addEventListener('DOMContentLoaded', function () {
${lastRunState}
`;
- schedulerCard.innerHTML = cardContent;
- row.appendChild(schedulerCard);
- });
+ schedulerCard.innerHTML = cardContent;
+ row.appendChild(schedulerCard);
+ });
- container.appendChild(section);
- };
-
- if (enabledWithStats.length > 0) {
- createSchedulerSection(enabledWithStats, 'Enabled Schedulers with Stats');
- }
- if (enabledWithoutStats.length > 0) {
- createSchedulerSection(enabledWithoutStats, 'Enabled Schedulers without Stats');
- }
- if (disabledWithStats.length > 0) {
- createSchedulerSection(disabledWithStats, 'Disabled Schedulers with Stats');
- }
- if (disabledWithoutStats.length > 0) {
- createSchedulerSection(disabledWithoutStats, 'Disabled Schedulers without Stats');
- }
+ container.appendChild(section);
};
- const renderMetadataCountsChart = async () => {
- const data = await fetchData('/metrics/metadata');
- removeLoadingElement('metadataCountsSkeleton');
- const ctx = document.getElementById('metadataCountsChart').getContext('2d');
- new Chart(ctx, {
- type: 'pie',
- data: {
- labels: ['Movies', 'Series', 'TV Channels'],
- datasets: [{
- label: 'Metadata Counts',
- data: [data.movies, data.series, data.tv_channels],
- backgroundColor: [
- 'rgba(153, 102, 255, 0.6)',
- 'rgba(54, 162, 235, 0.6)',
- 'rgba(75, 192, 192, 0.6)'
- ],
- borderColor: [
- 'rgba(153, 102, 255, 1)',
- 'rgba(54, 162, 235, 1)',
- 'rgba(75, 192, 192, 1)'
- ],
- }]
- },
- options: {
- responsive: true,
- maintainAspectRatio: false,
- plugins: {
- legend: {
- display: false
- },
- datalabels: {
- formatter: (value, context) => {
- return context.chart.data.labels[context.dataIndex] + ': ' + value.toLocaleString();
- },
- color: '#fff',
- anchor: 'center',
- align: 'center',
- offset: 0,
- borderRadius: 4,
- backgroundColor: (context) => {
- return context.dataset.backgroundColor;
- },
- font: {
- weight: 'bold'
- }
+ if (enabledWithStats.length > 0) {
+ createSchedulerSection(enabledWithStats, 'Enabled Schedulers with Stats');
+ }
+ if (enabledWithoutStats.length > 0) {
+ createSchedulerSection(enabledWithoutStats, 'Enabled Schedulers without Stats');
+ }
+ if (disabledWithStats.length > 0) {
+ createSchedulerSection(disabledWithStats, 'Disabled Schedulers with Stats');
+ }
+ if (disabledWithoutStats.length > 0) {
+ createSchedulerSection(disabledWithoutStats, 'Disabled Schedulers without Stats');
+ }
+};
+
+const renderMetadataCountsChart = async () => {
+ const data = await fetchData('/metrics/metadata');
+ removeLoadingElement('metadataCountsSkeleton');
+ const ctx = document.getElementById('metadataCountsChart').getContext('2d');
+ new Chart(ctx, {
+ type: 'pie', data: {
+ labels: ['Movies', 'Series', 'TV Channels'], datasets: [{
+ label: 'Metadata Counts',
+ data: [data.movies, data.series, data.tv_channels],
+ backgroundColor: ['rgba(153, 102, 255, 0.6)', 'rgba(54, 162, 235, 0.6)', 'rgba(75, 192, 192, 0.6)'],
+ borderColor: ['rgba(153, 102, 255, 1)', 'rgba(54, 162, 235, 1)', 'rgba(75, 192, 192, 1)'],
+ }]
+ }, options: {
+ responsive: true, maintainAspectRatio: false, plugins: {
+ legend: {
+ display: false
+ }, datalabels: {
+ formatter: (value, context) => {
+ return context.chart.data.labels[context.dataIndex] + ': ' + value.toLocaleString();
+ }, color: '#fff', anchor: 'center', align: 'center', offset: 0, borderRadius: 4, backgroundColor: (context) => {
+ return context.dataset.backgroundColor;
+ }, font: {
+ weight: 'bold'
}
}
}
- });
- };
+ }
+ });
+};
- const renderTotalTorrentsCount = async () => {
- const data = await fetchData('/metrics/torrents');
- removeLoadingElement('totalTorrentsSkeleton');
- const totalTorrentsValue = document.getElementById('totalTorrentsValue');
- totalTorrentsValue.textContent = `${data.total_torrents.toLocaleString()} (${data.total_torrents_readable})`;
- };
+const renderTotalTorrentsCount = async () => {
+ const data = await fetchData('/metrics/torrents');
+ removeLoadingElement('totalTorrentsSkeleton');
+ const totalTorrentsValue = document.getElementById('totalTorrentsValue');
+ totalTorrentsValue.textContent = `${data.total_torrents.toLocaleString()} (${data.total_torrents_readable})`;
+};
- const renderTorrentSourcesChart = async () => {
- const data = await fetchData('/metrics/torrents/sources');
- removeLoadingElement('torrentSourcesSkeleton');
- const ctx = document.getElementById('torrentSourcesChart').getContext('2d');
- new Chart(ctx, {
- type: 'bar',
- data: {
- datasets: data.map(source => {
- return {
- label: source.name,
- data: [{x: source.name, y: source.count}],
- };
- }),
- },
- options: {
- grouped: false,
- responsive: true,
- maintainAspectRatio: false,
- scales: {
- y: {
- beginAtZero: true,
- display: false
- },
- x: {
- ticks: {
- color: '#fff',
- }
- }
- },
- layout: {
- padding: {
- top: 50 // Add padding to prevent label cutoff
- }
- },
- plugins: {
- legend: {
- display: false
- },
- tooltip: {
- callbacks: {
- label: function (context) {
- return `${context.raw.y.toLocaleString()} Torrents`;
- }
- }
- },
- datalabels: {
- align: 'end',
- anchor: 'end',
- rotation: 0,
+const renderTorrentSourcesChart = async () => {
+ const data = await fetchData('/metrics/torrents/sources');
+ removeLoadingElement('torrentSourcesSkeleton');
+ const ctx = document.getElementById('torrentSourcesChart').getContext('2d');
+ new Chart(ctx, {
+ type: 'bar', data: {
+ datasets: data.map(source => {
+ return {
+ label: source.name, data: [{x: source.name, y: source.count}],
+ };
+ }),
+ }, options: {
+ grouped: false, responsive: true, maintainAspectRatio: false, scales: {
+ y: {
+ beginAtZero: true, display: false
+ }, x: {
+ ticks: {
color: '#fff',
- formatter: (value, context) => {
- return value.y.toLocaleString();
- },
- font: {
- weight: 'bold'
- },
+ }
+ }
+ }, layout: {
+ padding: {
+ top: 50 // Add padding to prevent label cutoff
+ }
+ }, plugins: {
+ legend: {
+ display: false
+ }, tooltip: {
+ callbacks: {
+ label: function (context) {
+ return `${context.raw.y.toLocaleString()} Torrents`;
+ }
+ }
+ }, datalabels: {
+ align: 'end', anchor: 'end', rotation: 0, color: '#fff', formatter: (value, context) => {
+ return value.y.toLocaleString();
+ }, font: {
+ weight: 'bold'
+ },
+ }
+ }
+ }
+ });
+};
+
+const renderTorrentUploadersChart = async () => {
+ const data = await fetchData('/metrics/torrents/uploaders');
+ removeLoadingElement('torrentUploadersSkeleton');
+ const ctx = document.getElementById('torrentUploadersChart').getContext('2d');
+
+ const sortedData = data.sort((a, b) => b.count - a.count);
+
+ new Chart(ctx, {
+ type: 'bar', data: {
+ labels: sortedData.map(item => item.name), datasets: [{
+ data: sortedData.map(item => item.count), backgroundColor: 'rgba(75, 192, 192, 0.6)', borderColor: 'rgba(75, 192, 192, 1)', borderWidth: 1
+ }]
+ }, options: {
+ indexAxis: 'y', responsive: true, maintainAspectRatio: false, layout: {
+ padding: {
+ right: 50 // Add padding to prevent label cutoff
+ }
+ }, plugins: {
+ legend: {
+ display: false
+ }, tooltip: {
+ callbacks: {
+ label: function (context) {
+ return `${context.raw.toLocaleString()} Torrents`;
+ }
+ }
+ }, datalabels: {
+ color: '#fff', anchor: 'end', align: 'end', formatter: (value) => value.toLocaleString(), font: {
+ weight: 'bold'
+ }
+ }
+ }, scales: {
+ x: {
+ beginAtZero: true, grid: {
+ color: 'rgba(255, 255, 255, 0.1)'
+ }, ticks: {
+ color: '#fff'
+ }
+ }, y: {
+ grid: {
+ display: false
+ }, ticks: {
+ color: '#fff'
}
}
}
- });
- };
+ }
+ });
+};
- const renderTorrentUploadersChart = async () => {
- const data = await fetchData('/metrics/torrents/uploaders');
- removeLoadingElement('torrentUploadersSkeleton');
- const ctx = document.getElementById('torrentUploadersChart').getContext('2d');
+const renderDebridCacheMetrics = async () => {
+ const data = await fetchData('/metrics/debrid-cache');
+ removeLoadingElement('debridChartSkeleton');
- const sortedData = data.sort((a, b) => b.count - a.count);
+ // Create the bar chart for service comparison
+ const ctx = document.getElementById('debridCacheChart').getContext('2d');
- new Chart(ctx, {
- type: 'bar',
- data: {
- labels: sortedData.map(item => item.name),
- datasets: [{
- data: sortedData.map(item => item.count),
- backgroundColor: 'rgba(75, 192, 192, 0.6)',
- borderColor: 'rgba(75, 192, 192, 1)',
- borderWidth: 1
- }]
- },
- options: {
- indexAxis: 'y',
- responsive: true,
- maintainAspectRatio: false,
- layout: {
- padding: {
- right: 50 // Add padding to prevent label cutoff
+ // Prepare data for the chart
+ const services = Object.keys(data.services);
+ const cachedTorrents = services.map(service => data.services[service].cached_torrents);
+
+ new Chart(ctx, {
+ type: 'bar', data: {
+ labels: services, datasets: [{
+ label: 'Cached Torrents', data: cachedTorrents, backgroundColor: 'rgba(54, 162, 235, 0.6)', borderColor: 'rgba(54, 162, 235, 1)', borderWidth: 1
+ }]
+ }, options: {
+ responsive: true, maintainAspectRatio: false, plugins: {
+ legend: {
+ display: false
+ }, tooltip: {
+ callbacks: {
+ label: function (context) {
+ return `Cached Torrents: ${context.raw.toLocaleString()}`;
+ }
}
- },
- plugins: {
- legend: {
+ }, datalabels: {
+ color: '#fff', anchor: 'end', align: 'end', formatter: (value) => value.toLocaleString(), font: {
+ weight: 'bold'
+ }
+ }
+ }, scales: {
+ y: {
+ beginAtZero: true, title: {
+ display: true, text: 'Number of Cached Torrents', color: '#fff'
+ }, ticks: {
+ color: '#fff'
+ }, grid: {
+ color: 'rgba(255, 255, 255, 0.1)'
+ }
+ }, x: {
+ ticks: {
+ color: '#fff'
+ }, grid: {
display: false
- },
- tooltip: {
- callbacks: {
- label: function (context) {
- return `${context.raw.toLocaleString()} Torrents`;
- }
- }
- },
- datalabels: {
- color: '#fff',
- anchor: 'end',
- align: 'end',
- formatter: (value) => value.toLocaleString(),
- font: {
- weight: 'bold'
- }
- }
- },
- scales: {
- x: {
- beginAtZero: true,
- grid: {
- color: 'rgba(255, 255, 255, 0.1)'
- },
- ticks: {
- color: '#fff'
- }
- },
- y: {
- grid: {
- display: false
- },
- ticks: {
- color: '#fff'
- }
}
}
}
- });
- };
+ }
+ });
+};
- const renderWeeklyUploadersChart = async () => {
- const data = await fetchData('/metrics/torrents/uploaders/weekly');
- removeLoadingElement('weeklyUploadersSkeleton');
+
+const renderWeeklyUploadersChart = async (selectedDate = null) => {
+ if (!selectedDate) {
+ selectedDate = new Date();
+ }
+
+ const startOfWeek = getStartOfWeek(selectedDate);
+ const formattedDate = formatDateToYYYYMMDD(startOfWeek);
+
+ try {
+ // Show loading state
+ document.getElementById('weeklyUploadersSkeleton').style.display = 'block';
+ if (weeklyUploadersChart) {
+ weeklyUploadersChart.destroy();
+ }
+
+ const data = await fetchData(`/metrics/torrents/uploaders/weekly/${formattedDate}`);
+
+ if (data.error) {
+ console.error('Error fetching data:', data.error);
+ return;
+ }
// Update date range display
const weekStart = new Date(data.week_start);
- const weekEnd = new Date(weekStart);
- weekEnd.setDate(weekEnd.getDate() + 6);
+ const weekEnd = new Date(data.week_end);
const dateRangeElement = document.getElementById('weekDateRange');
dateRangeElement.innerHTML = `
-
- ${weekStart.toLocaleDateString()} - ${weekEnd.toLocaleDateString()}
-
- `;
+
+ ${weekStart.toLocaleDateString()} - ${weekEnd.toLocaleDateString()}
+
+ `;
+
+ // Update week selector value
+ document.getElementById('weekSelector').value = formattedDate;
const ctx = document.getElementById('weeklyUploadersChart').getContext('2d');
- new Chart(ctx, {
- type: 'bar',
- data: {
- labels: data.uploaders.map(uploader => uploader.name),
- datasets: [{
- data: data.uploaders.map(uploader => uploader.count),
- backgroundColor: 'rgba(147, 112, 219, 0.6)',
- borderColor: 'rgba(147, 112, 219, 1)',
- borderWidth: 1
+ weeklyUploadersChart = new Chart(ctx, {
+ type: 'bar', data: {
+ labels: data.uploaders.map(uploader => uploader.name), datasets: [{
+ data: data.uploaders.map(uploader => uploader.count), backgroundColor: 'rgba(147, 112, 219, 0.6)', borderColor: 'rgba(147, 112, 219, 1)', borderWidth: 1
}]
- },
- options: {
- indexAxis: 'y',
- responsive: true,
- maintainAspectRatio: false,
- layout: {
+ }, options: {
+ indexAxis: 'y', responsive: true, maintainAspectRatio: false, layout: {
padding: {
right: 50
}
- },
- plugins: {
+ }, plugins: {
legend: {
display: false
- },
- tooltip: {
+ }, tooltip: {
callbacks: {
label: function (context) {
const uploader = data.uploaders[context.dataIndex];
const lastUpload = new Date(uploader.latest_upload);
- return [
- `Torrents: ${context.raw}`,
- `Last Upload: ${lastUpload.toLocaleDateString()}`
- ];
+ return [`Torrents: ${context.raw}`, `Last Upload: ${lastUpload.toLocaleDateString()}`];
}
}
- },
- datalabels: {
- color: '#fff',
- anchor: 'end',
- align: 'right',
- formatter: (value) => value,
- font: {
- weight: 'bold',
- size: 12
- },
- padding: {
+ }, datalabels: {
+ color: '#fff', anchor: 'end', align: 'right', formatter: (value) => value, font: {
+ weight: 'bold', size: 12
+ }, padding: {
right: 6
}
}
- },
- scales: {
+ }, scales: {
x: {
- beginAtZero: true,
- grid: {
+ beginAtZero: true, grid: {
color: 'rgba(255, 255, 255, 0.1)'
- },
- ticks: {
- color: '#fff',
- callback: function (value) {
+ }, ticks: {
+ color: '#fff', callback: function (value) {
return value.toLocaleString();
- },
- padding: 10 // Add padding to axis ticks
- },
- // Ensure the axis extends a bit beyond the max value
- suggestedMax: function (context) {
- const max = Math.max(...context.chart.data.datasets[0].data);
- return max * 1.1; // Extend 10% beyond the max value
- }
- },
- y: {
- grid: {
- display: false,
- borderWidth: 0
- },
- ticks: {
- color: '#fff',
- font: {
- size: 12
- },
- padding: 10
- }
- }
- }
- }
- });
- };
-
- const renderDebridCacheMetrics = async () => {
- const data = await fetchData('/metrics/debrid-cache');
- removeLoadingElement('debridChartSkeleton');
-
- // Create the bar chart for service comparison
- const ctx = document.getElementById('debridCacheChart').getContext('2d');
-
- // Prepare data for the chart
- const services = Object.keys(data.services);
- const cachedTorrents = services.map(service => data.services[service].cached_torrents);
-
- new Chart(ctx, {
- type: 'bar',
- data: {
- labels: services,
- datasets: [{
- label: 'Cached Torrents',
- data: cachedTorrents,
- backgroundColor: 'rgba(54, 162, 235, 0.6)',
- borderColor: 'rgba(54, 162, 235, 1)',
- borderWidth: 1
- }]
- },
- options: {
- responsive: true,
- maintainAspectRatio: false,
- plugins: {
- legend: {
- display: false
- },
- tooltip: {
- callbacks: {
- label: function (context) {
- return `Cached Torrents: ${context.raw.toLocaleString()}`;
}
}
- },
- datalabels: {
- color: '#fff',
- anchor: 'end',
- align: 'end',
- formatter: (value) => value.toLocaleString(),
- font: {
- weight: 'bold'
- }
- }
- },
- scales: {
- y: {
- beginAtZero: true,
- title: {
- display: true,
- text: 'Number of Cached Torrents',
- color: '#fff'
- },
- ticks: {
- color: '#fff'
- },
- grid: {
- color: 'rgba(255, 255, 255, 0.1)'
- }
- },
- x: {
- ticks: {
- color: '#fff'
- },
+ }, y: {
grid: {
display: false
+ }, ticks: {
+ color: '#fff'
}
}
}
}
});
- };
+ } finally {
+ // Hide loading state
+ document.getElementById('weeklyUploadersSkeleton').style.display = 'none';
+ }
+};
+
+document.addEventListener('DOMContentLoaded', function () {
+ const weekSelector = document.getElementById('weekSelector');
+ const prevWeekBtn = document.getElementById('prevWeek');
+ const nextWeekBtn = document.getElementById('nextWeek');
+
+ // Set initial date to start of current week
+ const currentDate = new Date();
+ const startOfWeek = getStartOfWeek(currentDate);
+ weekSelector.value = formatDateToYYYYMMDD(startOfWeek);
+
+ // Event listeners for navigation
+ weekSelector.addEventListener('change', (e) => {
+ renderWeeklyUploadersChart(new Date(e.target.value));
+ });
+
+ prevWeekBtn.addEventListener('click', () => {
+ const currentDate = new Date(weekSelector.value);
+ const prevWeek = new Date(currentDate.setDate(currentDate.getDate() - 7));
+ renderWeeklyUploadersChart(prevWeek);
+ });
+
+ nextWeekBtn.addEventListener('click', () => {
+ const currentDate = new Date(weekSelector.value);
+ const nextWeek = new Date(currentDate.setDate(currentDate.getDate() + 7));
+ renderWeeklyUploadersChart(nextWeek);
+ });
+
+ // Initial render
+ renderWeeklyUploadersChart();
+});
+
+document.addEventListener('DOMContentLoaded', function () {
const initCharts = () => {
renderSchedulerDetails();
@@ -492,7 +440,6 @@ document.addEventListener('DOMContentLoaded', function () {
renderTotalTorrentsCount();
renderTorrentSourcesChart();
renderTorrentUploadersChart();
- renderWeeklyUploadersChart();
renderDebridCacheMetrics();
};