271 lines
11 KiB
PHP
271 lines
11 KiB
PHP
<?php include '../src/session_check.php'; ?>
|
|
|
|
<html lang="en" data-bs-theme="dark">
|
|
<head>
|
|
<!-- Required meta tags -->
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<!--favicon-->
|
|
<link rel="icon" href="../assets/images/favicon-32x32.png" type="image/png">
|
|
<!--plugins-->
|
|
<link href="../assets/plugins/vectormap/jquery-jvectormap-2.0.2.css" rel="stylesheet">
|
|
<link href="../assets/plugins/simplebar/css/simplebar.css" rel="stylesheet">
|
|
<link href="../assets/plugins/perfect-scrollbar/css/perfect-scrollbar.css" rel="stylesheet">
|
|
<link href="../assets/plugins/metismenu/css/metisMenu.min.css" rel="stylesheet">
|
|
<!-- loader-->
|
|
<link href="../assets/css/pace.min.css" rel="stylesheet"/>
|
|
<script src="../assets/js/pace.min.js"></script>
|
|
<!-- Bootstrap CSS -->
|
|
<link href="../assets/css/bootstrap.min.css" rel="stylesheet">
|
|
<link href="../assets/css/bootstrap-extended.css" rel="stylesheet">
|
|
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@400;500&display=swap" rel="stylesheet">
|
|
|
|
<link href="../assets/sass/app.css" rel="stylesheet">
|
|
<link href="../assets/css/icons.css" rel="stylesheet">
|
|
<link href='https://unpkg.com/boxicons@2.1.4/css/boxicons.min.css' rel='stylesheet'>
|
|
<!-- Theme Style CSS -->
|
|
<link rel="stylesheet" href="../assets/sass/dark-theme.css">
|
|
<link rel="stylesheet" href="../assets/sass/semi-dark.css">
|
|
<link rel="stylesheet" href="../assets/sass/bordered-theme.css">
|
|
|
|
<title>TOD Dashboard</title>
|
|
</head>
|
|
|
|
<body>
|
|
<!--wrapper-->
|
|
<div class="wrapper">
|
|
<!--sidebar wrapper -->
|
|
<?php include '../src/nav.php'; ?>
|
|
<!--end sidebar wrapper -->
|
|
<!--start header -->
|
|
<?php include '../src/header.php'; ?>
|
|
<!--end header -->
|
|
|
|
<!--start page wrapper -->
|
|
<div class="page-wrapper">
|
|
<div class="page-content">
|
|
<!--start page content -->
|
|
|
|
|
|
|
|
<div class="container">
|
|
<div class="row" id="filamentCardContainer">
|
|
<!-- Cards will be inserted here dynamically -->
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
|
<!--end page content -->
|
|
</div>
|
|
</div>
|
|
<!--end page wrapper -->
|
|
|
|
<!--start overlay-->
|
|
<div class="overlay mobile-toggle-icon"></div>
|
|
<!--end overlay-->
|
|
<!--Start Back To Top Button-->
|
|
<a href="javaScript:;" class="back-to-top"><i class='bx bxs-up-arrow-alt'></i></a>
|
|
<!--End Back To Top Button-->
|
|
</div>
|
|
<!--end wrapper-->
|
|
|
|
|
|
<!-- search modal -->
|
|
<div class="modal" id="SearchModal" tabindex="-1">
|
|
<div class="modal-dialog modal-dialog-centered modal-dialog-scrollable modal-fullscreen-md-down">
|
|
<div class="modal-content">
|
|
<div class="modal-header gap-2">
|
|
<div class="position-relative popup-search w-100">
|
|
<input class="form-control form-control-lg ps-5 border border-3 border-primary" type="search" placeholder="Search">
|
|
<span class="position-absolute top-50 search-show ms-3 translate-middle-y start-0 top-50 fs-4"><i class='bx bx-search'></i></span>
|
|
</div>
|
|
<button type="button" class="btn-close d-md-none" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<!-- end search modal -->
|
|
|
|
<!-- Bootstrap JS -->
|
|
<script src="../assets/js/bootstrap.bundle.min.js"></script>
|
|
<!--plugins-->
|
|
<script src="../assets/js/jquery.min.js"></script>
|
|
<script src="../assets/plugins/simplebar/js/simplebar.min.js"></script>
|
|
<script src="../assets/plugins/metismenu/js/metisMenu.min.js"></script>
|
|
<script src="../assets/plugins/perfect-scrollbar/js/perfect-scrollbar.js"></script>
|
|
<script src="../assets/plugins/apexcharts-bundle/js/apexcharts.min.js"></script>
|
|
<!--app JS-->
|
|
<script src="../assets/js/app.js"></script>
|
|
|
|
<script src="../assets/js/index.js"></script>
|
|
<script src="../assets/plugins/peity/jquery.peity.min.js"></script>
|
|
<script>
|
|
$(".data-attributes span").peity("donut")
|
|
</script>
|
|
|
|
<script>
|
|
document.addEventListener("DOMContentLoaded", function () {
|
|
const container = document.getElementById('filamentCardContainer');
|
|
|
|
// Function to generate random colors
|
|
function getRandomColor() {
|
|
const letters = '0123456789ABCDEF';
|
|
let color = '#';
|
|
for (let i = 0; i < 6; i++) {
|
|
color += letters[Math.floor(Math.random() * 16)];
|
|
}
|
|
return color;
|
|
}
|
|
|
|
// Skeleton Loader Template
|
|
function showSkeletonLoader() {
|
|
container.innerHTML = `
|
|
<div class="col-lg-4 mb-1">
|
|
<div class="card radius-10 overflow-hidden skeleton-loader" style="height: 200px;">
|
|
<div class="card-body d-flex align-items-center justify-content-between">
|
|
<div>
|
|
<div class="skeleton-text mb-2" style="width: 70px; height: 20px;"></div>
|
|
<div class="skeleton-text" style="width: 50px; height: 30px;"></div>
|
|
</div>
|
|
<div class="skeleton-button" style="width: 40px; height: 40px;"></div>
|
|
</div>
|
|
<div class="skeleton-chart" style="height: 120px;"></div>
|
|
</div>
|
|
</div>
|
|
`;
|
|
}
|
|
|
|
fetch('../src/filamentTracker/getFilamentPrices.php')
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
if (data.status === 'success') {
|
|
container.innerHTML = ''; // Clear Skeleton
|
|
|
|
Object.keys(data.data).forEach(filament => {
|
|
const filamentData = data.data[filament];
|
|
const prices = filamentData.prices.map(entry => parseFloat(entry.price));
|
|
const timestamps = filamentData.prices.map(entry => entry.recordedAt);
|
|
const latestPrice = prices[prices.length - 1] || 0;
|
|
|
|
// Find the last distinct price (where the price actually changed)
|
|
let lastDistinctPrice = latestPrice;
|
|
for (let i = prices.length - 2; i >= 0; i--) {
|
|
if (prices[i] !== latestPrice) {
|
|
lastDistinctPrice = prices[i];
|
|
break;
|
|
}
|
|
}
|
|
|
|
const priceDifference = latestPrice - lastDistinctPrice;
|
|
|
|
// Price Change Indicator
|
|
let priceChangeIndicator;
|
|
if (priceDifference > 0) {
|
|
priceChangeIndicator = `<span class="text-danger ms-4">
|
|
<i class="bx bx-up-arrow-alt"></i> +£${priceDifference.toFixed(2)}</span>`;
|
|
} else if (priceDifference < 0) {
|
|
priceChangeIndicator = `<span class="text-success ms-4">
|
|
<i class="bx bx-down-arrow-alt"></i> -£${Math.abs(priceDifference).toFixed(2)}</span>`;
|
|
} else {
|
|
priceChangeIndicator = `<span class="text-muted ms-4">
|
|
<i class="bx bx-minus"></i> £0.00</span>`;
|
|
}
|
|
|
|
const amazonUrl = filamentData.amazonUrl || '#';
|
|
|
|
// Extract discount details
|
|
const currentDiscount = filamentData.currentDiscount || {};
|
|
const voucher = currentDiscount.voucher || {};
|
|
const discount = currentDiscount.discount || {};
|
|
|
|
let discountText = '';
|
|
if (voucher.value > 0) {
|
|
discountText = voucher.type === 'percentage'
|
|
? `-${voucher.value}% Voucher`
|
|
: `-£${voucher.value} Voucher`;
|
|
} else if (discount.value > 0) {
|
|
discountText = discount.type === 'percentage'
|
|
? `-${discount.value}%`
|
|
: `-£${discount.value}`;
|
|
}
|
|
|
|
const chartId = `chart-${filament.replace(/\s+/g, '-')}`;
|
|
const chartColor = getRandomColor();
|
|
|
|
// Create Card HTML
|
|
const cardHTML = `
|
|
<div class="col-lg-4 mb-1">
|
|
<div class="card radius-10 overflow-hidden">
|
|
<div class="card-body d-flex align-items-center justify-content-between">
|
|
<div>
|
|
<a href="${amazonUrl}" target="_blank" class="mb-0 filament-name">${filament}</a>
|
|
<h5 class="mb-0">
|
|
£${latestPrice} ${priceChangeIndicator}
|
|
</h5>
|
|
</div>
|
|
<div>
|
|
${
|
|
discountText
|
|
? `<span class="text-success fw-bold" style="font-size: 1rem;">
|
|
${discountText}
|
|
</span>`
|
|
: ''
|
|
}
|
|
</div>
|
|
</div>
|
|
<div id="${chartId}"></div>
|
|
</div>
|
|
</div>
|
|
`;
|
|
|
|
container.insertAdjacentHTML('beforeend', cardHTML);
|
|
|
|
// Chart Rendering
|
|
const chartOptions = {
|
|
series: [{ name: "Price", data: prices }],
|
|
chart: {
|
|
type: "area",
|
|
height: 110,
|
|
toolbar: { show: false },
|
|
zoom: { enabled: false },
|
|
sparkline: { enabled: true },
|
|
},
|
|
markers: { size: 0 },
|
|
dataLabels: { enabled: false },
|
|
stroke: { show: true, width: 2.4, curve: "smooth" },
|
|
colors: [chartColor],
|
|
xaxis: { categories: timestamps, labels: { show: false } },
|
|
fill: { opacity: 1 },
|
|
tooltip: {
|
|
theme: "dark",
|
|
y: {
|
|
formatter: function (value, { dataPointIndex }) {
|
|
const date = new Date(timestamps[dataPointIndex]);
|
|
return `£${value} - ${date.toLocaleDateString()}`;
|
|
},
|
|
},
|
|
},
|
|
};
|
|
|
|
const chartElement = document.getElementById(chartId);
|
|
if (chartElement) {
|
|
const chart = new ApexCharts(chartElement, chartOptions);
|
|
chart.render();
|
|
}
|
|
});
|
|
} else {
|
|
container.innerHTML = '<p class="text-center">No filament data available.</p>';
|
|
}
|
|
})
|
|
.catch(error => {
|
|
console.error("Error fetching filament data:", error);
|
|
});
|
|
|
|
showSkeletonLoader();
|
|
});
|
|
</script>
|
|
|
|
</body>
|
|
|
|
</html>
|