HEX
Server: Apache/2
System: Linux sv156.hostsevenplus.com 4.18.0-553.126.1.el8_10.x86_64 #1 SMP Thu May 28 06:44:09 EDT 2026 x86_64
User: npaaenew (1145)
PHP: 8.5.0
Disabled: exec,system,passthru,shell_exec,proc_close,proc_open,dl,popen,show_source,posix_kill,posix_mkfifo,posix_getpwuid,posix_setpgid,posix_setsid,posix_setuid,posix_setgid,posix_seteuid,posix_setegid,posix_uname
Upload Files
File: /home/npaaenew/public_html/wp-content/plugins/formlayer-pro/assets/js/reports-chart.js
jQuery(document).ready(function($){
	/* Reports Dashboard Charts & Logic */
	let trend_chart = null,
	form_chart = null,
	device_chart = null,
	_reports_raw_data = null,
	_reports_trend_mode = 'daily';

	function load_reports_data() {
		let form_id = $('#formlayer-reports-filter-form').val();
		let days = $('#formlayer-reports-filter-range').val();

		let $container = $('.formlayer-reports-view');
		if (!$container.length) return;

		$.post(formlayer_admin.ajax_url, {
			action: 'formlayer_get_reports_data',
			nonce: formlayer_admin.nonce,
			form_id: form_id,
			days: days
		}, function(response) {
			if (response.success) {
				let data = response.data;
				_reports_raw_data = data;

				animate_number_counter('#reports-stat-total', data.total_entries);
				animate_number_counter('#reports-stat-read', data.read_count);
				animate_number_counter('#reports-stat-unread', data.unread_count);
				$('#reports-stat-conversion').text(data.conversion_rate + '%');

				render_trend_chart(data.time_data, parseInt(days), _reports_trend_mode);

				if (form_id == 0) {
					$('#reports-chart-form-card').show();
					render_form_bar_chart(data.form_data);
				} else {
					$('#reports-chart-form-card').hide();
				}

				render_device_chart(data.device_data);
				render_heatmap(data.heatmap_data);
				render_country_list(data.country_data);
				render_form_table(data.form_data);
			} else {
				show_toast('Error loading reports data', 'error');
			}
		});
	}

	function animate_number_counter(selector, target) {
		let $el = $(selector);
		if (!$el.length) return;
		let current = parseInt($el.text()) || 0;
		if (current === target) return;
		
		$({ countNum: current }).animate({ countNum: target }, {
			duration: 600,
			easing: 'swing',
			step: function() {
				$el.text(Math.floor(this.countNum));
			},
			complete: function() {
				$el.text(this.countNum);
			}
		});
	}

	function render_trend_chart(time_data, days, mode) {
		let canvas = document.getElementById('reports-chart-trend');
		if (!canvas) return;
		if (trend_chart) { trend_chart.destroy(); }

		mode = mode || 'daily';
		let labels = [];
		let dataValues = [];

		if (mode === 'daily') {
			if (days > 0) {
				for (let i = days - 1; i >= 0; i--) {
					let d = new Date();
					d.setDate(d.getDate() - i);
					let dateStr = d.toISOString().split('T')[0];
					labels.push(d.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }));
					dataValues.push(time_data[dateStr] || 0);
				}
			} else {
				Object.keys(time_data).sort().forEach(dateStr => {
					let d = new Date(dateStr);
					labels.push(d.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }));
					dataValues.push(time_data[dateStr]);
				});
			}
		} else if (mode === 'weekly') {
			let weekly = {};
			Object.keys(time_data).sort().forEach(dateStr => {
				let d = new Date(dateStr);
				let yr = d.getFullYear();
				let wk = Math.ceil(((d - new Date(yr, 0, 1)) / 86400000 + 1) / 7);
				let key = yr + '-W' + String(wk).padStart(2, '0');
				weekly[key] = (weekly[key] || 0) + (time_data[dateStr] || 0);
			});
			Object.keys(weekly).sort().forEach(k => { labels.push(k); dataValues.push(weekly[k]); });
		} else if (mode === 'monthly') {
			let monthly = {};
			Object.keys(time_data).sort().forEach(dateStr => {
				let key = dateStr.substring(0, 7);
				monthly[key] = (monthly[key] || 0) + (time_data[dateStr] || 0);
			});
			Object.keys(monthly).sort().forEach(k => {
				let [yr, mo] = k.split('-');
				let d = new Date(yr, parseInt(mo) - 1, 1);
				labels.push(d.toLocaleDateString('en-US', { month: 'short', year: 'numeric' }));
				dataValues.push(monthly[k]);
			});
		}

		let ctx = canvas.getContext('2d');
		let gradient = ctx.createLinearGradient(0, 0, 0, 260);
		gradient.addColorStop(0, 'rgba(85, 37, 214, 0.25)');
		gradient.addColorStop(1, 'rgba(85, 37, 214, 0.0)');

		trend_chart = new Chart(ctx, {
			type: 'line',
			data: {
				labels: labels,
				datasets: [{
					label: 'Submissions',
					data: dataValues,
					borderColor: '#5525d6',
					borderWidth: 3,
					backgroundColor: gradient,
					fill: true,
					tension: 0.4,
					pointBackgroundColor: '#5525d6',
					pointHoverRadius: 7,
					pointHoverBackgroundColor: '#ffffff',
					pointHoverBorderColor: '#5525d6',
					pointHoverBorderWidth: 3
				}]
			},
			options: {
				responsive: true,
				maintainAspectRatio: false,
				plugins: {
					legend: { display: false },
					tooltip: {
						backgroundColor: '#1e293b',
						padding: 12,
						titleFont: { size: 13, weight: '600' },
						bodyFont: { size: 13 },
						cornerRadius: 8,
						displayColors: false
					}
				},
				scales: {
					x: {
						grid: { display: false },
						ticks: { 
							color: '#64748b', 
							font: { size: 11 },
							autoSkip: true,
							maxTicksLimit: 10,
							maxRotation: 0,
							minRotation: 0
						}
					},
					y: {
						grid: { color: '#f1f5f9', borderDash: [5, 5] },
						ticks: { 
							color: '#64748b', 
							font: { size: 11 },
							precision: 0
						},
						beginAtZero: true
					}
				}
			}
		});
	}

	function render_form_bar_chart(form_data) {
		let canvas = document.getElementById('reports-chart-form');
		if (!canvas) return;
		if (form_chart) { form_chart.destroy(); }
		if (!form_data || form_data.length === 0) return;

		let labels = form_data.map(d => d.label);
		let values = form_data.map(d => d.value);
		let ctx = canvas.getContext('2d');
		form_chart = new Chart(ctx, {
			type: 'bar',
			data: {
				labels: labels,
				datasets: [{
					label: 'Submissions',
					data: values,
					backgroundColor: '#5525d6',
					borderRadius: 6,
					maxBarThickness: 30
				}]
			},
			options: {
				responsive: true,
				maintainAspectRatio: false,
				plugins: {
					legend: { display: false },
					tooltip: { backgroundColor: '#1e293b', padding: 12, cornerRadius: 8 }
				},
				scales: {
					x: { grid: { display: false }, ticks: { color: '#64748b', font: { size: 11 } } },
					y: { grid: { color: '#f1f5f9' }, ticks: { color: '#64748b', precision: 0 }, beginAtZero: true }
				}
			}
		});
	}

	function render_device_chart(device_data) {
		let canvas = document.getElementById('reports-chart-device');
		if (!canvas) return;
		if (device_chart) { device_chart.destroy(); }

		let labels = device_data.length ? device_data.map(d => d.label) : ['No Data'];
		let values = device_data.length ? device_data.map(d => d.value) : [1];
		let colors = ['#5525d6', '#10b981', '#f59e0b'];
		let ctx = canvas.getContext('2d');
		device_chart = new Chart(ctx, {
			type: 'doughnut',
			data: {
				labels: labels,
				datasets: [{ data: values, backgroundColor: colors.slice(0, labels.length), borderWidth: 3, borderColor: '#fff' }]
			},
			options: {
				responsive: true, maintainAspectRatio: false, cutout: '68%',
				plugins: {
					legend: { display: false },
					tooltip: { backgroundColor: '#1e293b', padding: 12, cornerRadius: 8 }
				}
			}
		});

		let total = values.reduce((a, b) => a + b, 0);
		let $legend = $('#reports-device-legend').empty();
		labels.forEach((lbl, i) => {
			let pct = total > 0 ? Math.round((values[i] / total) * 100) : 0;
			$legend.append(`<div class="formlayer-device-legend-item"><span class="formlayer-device-dot" style="background:${colors[i] || '#94a3b8'}"></span><span>${lbl}</span><strong>${pct}%</strong></div>`);
		});
	}

	function render_heatmap(heatmap_data) {
		let $wrap = $('#reports-heatmap-container').empty();
		if (!$wrap.length) return;

		let days = ['Sun','Mon','Tue','Wed','Thu','Fri','Sat'],
		hours = [0,3,6,9,12,15,18,21],
		grid = {},
		maxVal = 0;

		(heatmap_data || []).forEach(h => {
			let dow = h.dow - 1; // convert 1-7 to 0-6
			let hr = Math.floor(h.hour / 3) * 3;
			let key = dow + '_' + hr;
			grid[key] = (grid[key] || 0) + h.count;
			if (grid[key] > maxVal) maxVal = grid[key];
		});

		let html = '<div class="formlayer-heatmap-grid">';
		html += '<div class="formlayer-heatmap-row formlayer-heatmap-header"><div class="formlayer-heatmap-day-label"></div>';
		hours.forEach(h => { html += `<div class="formlayer-heatmap-hour-label">${h}:00</div>`; });
		html += '</div>';

		days.forEach((day, di) => {
			html += `<div class="formlayer-heatmap-row"><div class="formlayer-heatmap-day-label">${day}</div>`;
			hours.forEach(h => {
				let val = grid[di + '_' + h] || 0;
				let intensity = maxVal > 0 ? val / maxVal : 0;
				let alpha = (0.08 + intensity * 0.92).toFixed(2);
				let bg = val > 0 ? `rgba(85,37,214,${alpha})` : '#f1f5f9';
				let color = intensity > 0.5 ? '#fff' : '#475569';
				html += `<div class="formlayer-heatmap-cell" style="background:${bg};color:${color}" title="${day} ${h}:00 — ${val} submissions">${val > 0 ? val : ''}</div>`;
			});
			html += '</div>';
		});
		html += '</div>';
		$wrap.html(html);
	}

	function render_country_list(country_data) {
		let $wrap = $('#reports-country-list').empty();
		if (!$wrap.length) return;

		if (!country_data || country_data.length === 0) {
			$wrap.html('<p style="color:#94a3b8;font-size:13px;">No country data available.</p>');
			return;
		}

		let max = country_data[0].value;
		let html = '';
		country_data.forEach(c => {
			let pct = max > 0 ? Math.round((c.value / max) * 100) : 0;
			html += `<div class="formlayer-country-row">
				<span class="formlayer-country-name">${c.label}</span>
				<div class="formlayer-country-bar-wrap"><div class="formlayer-country-bar" style="width:${pct}%"></div></div>
				<span class="formlayer-country-count">${c.value}</span>
			</div>`;
		});
		$wrap.html(html);
	}

	function render_form_table(form_data) {
		let $tbody = $('#reports-form-table-body').empty();
		if (!$tbody.length) return;

		if (!form_data || form_data.length === 0) {
			$tbody.html('<tr><td colspan="5" style="text-align:center;padding:30px;color:#94a3b8;">No form data available.</td></tr>');
			return;
		}

		form_data.forEach(f => {
			let conv = f.conversion || 0;
			let barColor = conv >= 15 ? '#10b981' : conv >= 8 ? '#f59e0b' : '#5525d6';
			$tbody.append(`<tr>
				<td style="font-weight:600;">${f.label}</td>
				<td>${f.views || 0}</td>
				<td>${f.value}</td>
				<td><strong style="color:${barColor}">${conv}%</strong></td>
				<td><div class="formlayer-conv-bar-wrap"><div class="formlayer-conv-bar" style="width:${Math.min(conv,100)}%;background:${barColor}"></div></div></td>
			</tr>`);
		});
	}

	function handle_reports_tab_activation() {
		let hash = window.location.hash.trim().replace('#', '').split('/')[0];
		if (hash === 'reports') {
			load_reports_data();
		}
	}

	$(window).on('hashchange', handle_reports_tab_activation);
	setTimeout(handle_reports_tab_activation, 150);

	$('#formlayer-reports-filter-form, #formlayer-reports-filter-range').on('change', function(){
		load_reports_data();
	});

	$('.formlayer-trend-tab').on('click', function(){
		$('.formlayer-trend-tab').removeClass('active');
		$(this).addClass('active');
		_reports_trend_mode = $(this).data('mode');
		if (_reports_raw_data) {
			let days = parseInt($('#formlayer-reports-filter-range').val());
			render_trend_chart(_reports_raw_data.time_data, days, _reports_trend_mode);
		}
	});
});