Migrate HickLab3D dashboard and add production auto deployment
Deploy HickLab3D Dashboard / Deploy to production (push) Failing after 19s

This commit is contained in:
Hickmeister
2026-10-10 18:51:23 +01:00
parent 8410a50f26
commit 88dd00551f
974 changed files with 14609 additions and 162573 deletions
+25
View File
@@ -0,0 +1,25 @@
<IfModule mod_rewrite.c>
<IfModule mod_negotiation.c>
Options -MultiViews -Indexes
</IfModule>
RewriteEngine On
# Handle Authorization Header
RewriteCond %{HTTP:Authorization} .
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
# Handle X-XSRF-Token Header
RewriteCond %{HTTP:x-xsrf-token} .
RewriteRule .* - [E=HTTP_X_XSRF_TOKEN:%{HTTP:X-XSRF-Token}]
# Redirect Trailing Slashes If Not A Folder...
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} (.+)/$
RewriteRule ^ %1 [L,R=301]
# Send Requests To Front Controller...
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php [L]
</IfModule>
-196
View File
@@ -1,196 +0,0 @@
<?php include '../src/session_check.php';
checkUserRole(['admin']);
?>
<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 -->
<!--breadcrumb-->
<div class="page-breadcrumb d-none d-sm-flex align-items-center mb-3">
<div class="breadcrumb-title pe-3">Filament Settings</div>
<div class="ps-3">
<nav aria-label="breadcrumb">
<ol class="breadcrumb mb-0 p-0">
<li class="breadcrumb-item"><a href="index.php"><i class="bx bx-home-alt"></i></a>
</li>
<li class="breadcrumb-item active" aria-current="page">Add Filament</li>
</ol>
</nav>
</div>
</div>
<!--end breadcrumb-->
<div class="row">
<div class="col-lg-8 mx-auto">
<div class="card">
<div class="card-header px-4 py-3">
<h5 class="mb-0">Add New Filament to Track</h5>
</div>
<div class="card-body p-4">
<!-- Alert Banner (Hidden Initially) -->
<div id="alertBanner" class="alert d-none" role="alert"></div>
<form id="filamentForm">
<div class="row mb-3">
<label for="productName" class="col-sm-3 col-form-label">Filament Name</label>
<div class="col-sm-9">
<input type="text" class="form-control" id="productName" name="productName" placeholder="Brand / Type of Filament" required>
</div>
</div>
<div class="row mb-3">
<label for="productUrl" class="col-sm-3 col-form-label">Amazon URL</label>
<div class="col-sm-9">
<input type="url" class="form-control" id="productUrl" name="productUrl" placeholder="https://www.amazon.co.uk/example" required>
</div>
</div>
<div class="row">
<label class="col-sm-3 col-form-label"></label>
<div class="col-sm-9">
<div class="d-md-flex d-grid align-items-center gap-3">
<button type="submit" class="btn btn-primary px-4">Track Filament</button>
<button type="reset" class="btn btn-light px-4">Reset</button>
</div>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
<!-- jQuery & AJAX Script -->
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script>
$(document).ready(function() {
$('#filamentForm').on('submit', function(event) {
event.preventDefault(); // Prevent traditional form submission
// Collect form data
let formData = $(this).serialize();
// AJAX request to submit form
$.ajax({
type: 'POST',
url: '../src/filamentTracker/addFilament.php',
data: formData,
dataType: 'json',
success: function(response) {
let banner = $('#alertBanner');
if (response.status === 'success') {
banner.removeClass('d-none alert-danger')
.addClass('alert-success')
.text(response.message);
} else {
banner.removeClass('d-none alert-success')
.addClass('alert-danger')
.text(response.message);
}
},
error: function() {
$('#alertBanner').removeClass('d-none alert-success')
.addClass('alert-danger')
.text('An error occurred while adding the filament.');
}
});
});
});
</script>
<!--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-->
<footer class="page-footer">
<p class="mb-0">Copyright © 2024. All right reserved.</p>
</footer>
</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>
</body>
</html>
View File
+9
View File
@@ -0,0 +1,9 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1000 820">
<title>HickLab3D</title>
<rect width="1000" height="820" rx="180" fill="#e3f504"/>
<g fill="#111111">
<path d="M90 650 L90 240 L260 135 L260 290 L195 330 L195 425 L365 320 L365 155 L505 70 L505 520 L370 603 L370 450 L200 555 L200 715 Z"/>
<path d="M545 90 L680 10 L680 420 L910 560 L775 645 L545 505 Z"/>
<path d="M365 603 L505 520 L680 625 L545 710 Z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 451 B

-618
View File
@@ -1,618 +0,0 @@
<?php include '../src/session_check.php';
checkUserRole(['admin']);
?>
<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 -->
<!-- Start Drying Modal -->
<div class="modal fade" id="startDryingModal" tabindex="-1" aria-labelledby="startDryingModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="startDryingModalLabel">Start Drying</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<form id="dryerControlForm">
<div class="mb-3">
<label for="filamentPreset" class="form-label">Filament Preset</label>
<select class="form-select" id="filamentPreset" name="filamentPreset" required>
<option value="">Select Filament Type</option>
<option value="PLA" data-temp="50">PLA</option>
<option value="ABS" data-temp="70">ABS</option>
<option value="PETG" data-temp="65">PETG</option>
<option value="Nylon" data-temp="80">Nylon</option>
<option value="TPU" data-temp="45">TPU</option>
<option value="Custom" data-temp="">Custom</option>
</select>
</div>
<div class="mb-3">
<label for="dryingTime" class="form-label">Drying Time (Hours)</label>
<input type="number" class="form-control" id="dryingTime" name="dryingTime" min="1" max="24" required>
</div>
<div class="mb-3">
<label for="temperature" class="form-label">Drying Temperature (°C)</label>
<input type="number" class="form-control" id="temperature" name="temperature" min="30" max="100" required>
</div>
<div class="d-flex justify-content-between">
<button type="submit" class="btn btn-success">Start Drying</button>
</div>
</form>
</div>
</div>
</div>
</div>
<!-- Stop Drying Modal -->
<div class="modal fade" id="stopDryingModal" tabindex="-1" aria-labelledby="stopDryingModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="stopDryingModalLabel">Stop Drying</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<p>Are you sure you want to stop the drying process?</p>
<div class="d-flex justify-content-between">
<button type="button" class="btn btn-danger" id="confirmStopDrying">Yes, Stop Drying</button>
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
</div>
</div>
</div>
</div>
</div>
<!-- Page Content -->
<div class="container mt-4">
<div class="row">
<!-- Dryer Status Cards -->
<div class="col-lg-4">
<div class="card bg-danger radius-10 overflow-hidden">
<div class="card-body">
<div class="d-flex align-items-center">
<div>
<p class="mb-0">Dryer Status</p>
<h5 class="mb-0">STATUS</h5>
</div>
</div>
</div>
</div>
</div>
<div class="col-lg-4">
<div class="card bg-danger radius-10 overflow-hidden" id="heaterRelayStatus">
<div class="card-body">
<div class="d-flex align-items-center">
<div>
<p class="mb-0">Heater Status</p>
<h5 class="mb-0">STATUS</h5>
</div>
</div>
</div>
</div>
</div>
<div class="col-lg-4">
<div class="card bg-danger radius-10 overflow-hidden" id="fanRelayStatus">
<div class="card-body">
<div class="d-flex align-items-center">
<div>
<p class="mb-0">Fan Status</p>
<h5 class="mb-0">STATUS</h5>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="row">
<!-- Left Column - Charts -->
<div class="col-lg-7">
<div class="row">
<!-- Temperature Chart -->
<div class="col-lg-12 mb-0">
<div class="card radius-10 overflow-hidden">
<div class="card-body d-flex align-items-center justify-content-between">
<div>
<p class="mb-0 filament-name">Temp:</p>
<h5 class="mb-0" id="temperatureValue">--°C</h5>
</div>
</div>
<div id="tempChart"></div>
</div>
</div>
<!-- Humidity Chart -->
<div class="col-lg-12 mb-0">
<div class="card radius-10 overflow-hidden">
<div class="card-body d-flex align-items-center justify-content-between">
<div>
<p class="mb-0 filament-name">Humidity:</p>
<h5 class="mb-0" id="humidityValue">--%</h5>
</div>
</div>
<div id="humidityChart"></div>
</div>
</div>
</div>
</div>
<!-- Right Column - Dryer Control Card with Dropdown and Chart -->
<div class="col-lg-5">
<div class="card rounded-4 mb-0">
<div class="card-body">
<div class="d-flex align-items-start justify-content-between mb-0">
<div class="">
<h6 class="mb-0">Dryer Control</h6>
</div>
<div class="dropdown">
<a href="javascript:;" class="dropdown-toggle-nocaret more-options dropdown-toggle" data-bs-toggle="dropdown">
<i class='bx bx-dots-vertical-rounded'></i>
</a>
<ul class="dropdown-menu">
<li><a class="dropdown-item" href="javascript:;" data-bs-toggle="modal" data-bs-target="#startDryingModal">Start Drying</a></li>
<li><a class="dropdown-item" href="javascript:;" data-bs-toggle="modal" data-bs-target="#stopDryingModal">Stop Drying</a></li>
</ul>
</div>
</div>
<div class="chart-container2">
<div id="chartProgress"></div>
</div>
<div class="text-center">
<div class="">
<h4 id="dryingTimeDisplay" class="mb-1">Loadig..</h4>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- End Page Content -->
</div>
</div>
<!--end page 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 ESP32_IP = 'https://filamentdry.hickmeister.uk'; // Replace with your ESP32 IP
let tempChart, humidityChart;
let progressChart; // Declare a variable for the chart instance
const startDryingModal = new bootstrap.Modal(document.getElementById('startDryingModal'));
const stopDryingModal = new bootstrap.Modal(document.getElementById('stopDryingModal'));
// Fetch ESP32 Status Data
function fetchDryerStatus() {
fetch(`${ESP32_IP}/status`)
.then(response => {
if (!response.ok) {
throw new Error(`HTTP error! Status: ${response.status}`);
}
return response.json();
})
.then(data => {
if (data.dryerOn !== undefined) {
updateTemperatureCard(data);
updateHumidityCard(data);
updateStatusCards(data);
updateChartProgress(data); // Update chart for drying progress
} else {
console.error("Dryer offline or no data available");
showOfflineStatus();
}
})
.catch(error => {
console.error("Failed to fetch dryer status:", error);
showOfflineStatus();
});
}
// Control Dryer
function controlDryer(dryerOn, targetTemperature, dryingTimeInMinutes) {
const formData = new URLSearchParams();
formData.append("dryerOn", dryerOn);
formData.append("targetTemperature", targetTemperature);
formData.append("dryingTime", dryingTimeInMinutes);
fetch(`${ESP32_IP}/control`, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: formData.toString(),
})
.then(response => {
if (!response.ok) {
throw new Error(`HTTP error! Status: ${response.status}`);
}
return response.json();
})
.then(data => {
console.log('Dryer control updated:', data);
fetchDryerStatus(); // Refresh status after control update
if (dryerOn) {
startDryingModal.hide();
} else {
stopDryingModal.hide();
}
})
.catch(error => {
console.error('Failed to update dryer control:', error);
alert('Failed to update dryer control. Check your connection.');
});
}
// Handle Form Submission for Starting Drying
const dryerControlForm = document.getElementById("dryerControlForm");
dryerControlForm.addEventListener("submit", function (event) {
event.preventDefault();
// Collect form data
const formData = new FormData(dryerControlForm);
const dryerOn = true; // Always true when starting the dryer
const dryingTimeInHours = parseFloat(formData.get("dryingTime"));
const targetTemperature = parseFloat(formData.get("temperature"));
// Validate Input
if (isNaN(dryingTimeInHours) || isNaN(targetTemperature)) {
alert("Please complete all required fields.");
return;
}
// Convert drying time to minutes
const dryingTimeInMinutes = Math.round(dryingTimeInHours * 60);
// Control Dryer
controlDryer(dryerOn, targetTemperature, dryingTimeInMinutes);
});
// Handle Stop Dryer
document.getElementById("stopDryingModal").addEventListener("click", function () {
stopDryingModal.show();
});
document.getElementById("confirmStopDrying").addEventListener("click", function() {
controlDryer(false, 0, 0); // Turn off the dryer
});
// Auto-Fill Temperature Input Based on Filament Preset
const filamentPresetSelect = document.getElementById("filamentPreset");
const temperatureInput = document.getElementById("temperature");
filamentPresetSelect.addEventListener("change", function () {
const selectedOption = filamentPresetSelect.options[filamentPresetSelect.selectedIndex];
const presetTemperature = selectedOption.getAttribute("data-temp");
if (presetTemperature) {
temperatureInput.value = presetTemperature; // Auto-fill temperature input
} else {
temperatureInput.value = ""; // Clear the temperature input for "Custom"
}
});
// Update chart progress
function updateChartProgress(data) {
const chartProgressElement = document.getElementById("chartProgress");
const dryingTime = data.dryingTime || 0; // Total drying time in minutes
const remainingTime = data.remainingTime || 0; // Remaining time in minutes
// Convert remaining time and total drying time to h:mm format
const remainingTimeFormatted = convertToHoursAndMinutes(remainingTime);
const dryingTimeFormatted = convertToHoursAndMinutes(dryingTime);
const progressPercent = dryingTime > 0 ? ((dryingTime - remainingTime) / dryingTime) * 100 : 0; // Calculate the percentage of drying progress
// Initialize the chart if it doesn't exist yet
if (!progressChart) {
const options = {
series: [progressPercent],
chart: {
height: 385,
type: 'radialBar',
toolbar: {
show: false
}
},
plotOptions: {
radialBar: {
hollow: {
margin: 0,
size: '80%',
background: 'transparent',
},
track: {
background: 'rgba(0, 0, 0, 0.1)',
strokeWidth: '67%',
},
dataLabels: {
show: true,
name: {
offsetY: -10,
show: false,
},
value: {
offsetY: 10,
color: 'rgba(13, 160, 8, 0.82)',
fontSize: '20px',
show: true,
formatter: function () {
return remainingTimeFormatted; // Display remaining time in the center
}
}
}
}
},
fill: {
type: 'gradient',
gradient: {
shade: 'dark',
type: 'horizontal',
shadeIntensity: 02,
gradientToColors: ['#44FF00'],
opacityFrom: 1,
opacityTo: 1,
stops: [0, 100]
}
},
colors: ["#289600"],
stroke: {
lineCap: 'round'
},
labels: ['Drying Progress'],
};
progressChart = new ApexCharts(chartProgressElement, options);
progressChart.render();
} else {
progressChart.updateSeries([progressPercent]);
// Dynamically update the remaining time in the center
progressChart.updateOptions({
plotOptions: {
radialBar: {
dataLabels: {
value: {
formatter: function () {
return remainingTimeFormatted; // Dynamically update the remaining time
}
}
}
}
}
});
}
// Display drying time below the chart
const timeSetElement = document.getElementById('dryingTimeDisplay');
if (timeSetElement) {
timeSetElement.innerText = `Time Set: ${dryingTimeFormatted}`;
}
}
// Convert minutes to hours and minutes (e.g., 90 minutes -> 1h 30m)
function convertToHoursAndMinutes(minutes) {
const hours = Math.floor(minutes / 60);
const mins = minutes % 60;
return `${hours}h ${mins}m`;
}
// Update Temperature Card and Chart
function updateTemperatureCard(data) {
const temperature = data.temperature.toFixed(1); // Limit temperature to 1 decimal place
const tempHistory = data.tempHistory.map(temp => parseFloat(temp.toFixed(1))); // Limit history data
const timestamps = Array.from({ length: tempHistory.length }, (_, i) => `${i + 1}`);
document.getElementById('temperatureValue').innerText = `${temperature}°C`;
const options = {
series: [{ name: "Temperature", data: tempHistory }],
chart: {
type: 'area',
color: '#1df00a',
height: 150,
toolbar: { show: false },
sparkline: { enabled: true },
id: 'tempChart',
},
stroke: { curve: 'smooth' },
xaxis: { categories: timestamps, labels: { show: false } },
tooltip: {
theme: 'dark',
x: { show: false },
y: { formatter: value => `${value.toFixed(1)}°C` },
},
};
if (!tempChart) {
tempChart = new ApexCharts(document.querySelector("#tempChart"), options);
tempChart.render();
} else {
tempChart.updateSeries([{ data: tempHistory }]);
}
}
// Update Humidity Card and Chart
function updateHumidityCard(data) {
const humidity = data.humidity.toFixed(1);
const humidityHistory = data.humidityHistory || [];
const timestamps = Array.from({ length: humidityHistory.length }, (_, i) => `${i + 1}`);
document.getElementById('humidityValue').innerText = `${humidity}%`;
const options = {
series: [{ name: "Humidity", data: humidityHistory }],
chart: {
type: 'area',
height: 150,
toolbar: { show: false },
sparkline: { enabled: true },
id: 'humidityChart',
},
stroke: { curve: 'smooth' },
xaxis: { categories: timestamps, labels: { show: false } },
tooltip: {
theme: 'dark',
x: { show: false },
y: { formatter: value => `${value.toFixed(1)}%` },
},
};
if (!humidityChart) {
humidityChart = new ApexCharts(document.querySelector("#humidityChart"), options);
humidityChart.render();
} else {
humidityChart.updateSeries([{ data: humidityHistory }]);
}
}
// Update Status Cards (Dryer, Heater, Fan)
function updateStatusCards(data) {
const dryerStatusCard = document.querySelector('.card:nth-child(1)');
const heaterStatusCard = document.getElementById('heaterRelayStatus');
const fanStatusCard = document.getElementById('fanRelayStatus');
const dryerState = data.dryerOn ? "On" : "Off";
const fanRelayState = data.fanRelayState ? "On" : "Off";
const heaterRelayState = data.heaterRelayState ? "On" : "Off";
if (dryerStatusCard) {
dryerStatusCard.classList.toggle("bg-success", dryerState === "On");
dryerStatusCard.classList.toggle("bg-danger", dryerState === "Off");
dryerStatusCard.querySelector("h5").innerText = dryerState;
}
if (heaterStatusCard) {
heaterStatusCard.classList.toggle("bg-success", heaterRelayState === "On");
heaterStatusCard.classList.toggle("bg-danger", heaterRelayState === "Off");
heaterStatusCard.querySelector("h5").innerText = heaterRelayState;
}
if (fanStatusCard) {
fanStatusCard.classList.toggle("bg-success", fanRelayState === "On");
fanStatusCard.classList.toggle("bg-danger", fanRelayState === "Off");
fanStatusCard.querySelector("h5").innerText = fanRelayState;
}
}
// Show Offline Status
function showOfflineStatus() {
const dryerStatusCard = document.querySelector('.card:nth-child(1)');
const heaterStatusCard = document.getElementById('heaterRelayStatus');
const fanStatusCard = document.getElementById('fanRelayStatus');
if (dryerStatusCard) {
dryerStatusCard.classList.remove("bg-success");
dryerStatusCard.classList.add("bg-danger");
dryerStatusCard.querySelector("h5").innerText = "Offline";
}
if (heaterStatusCard) {
heaterStatusCard.classList.remove("bg-success");
heaterStatusCard.classList.add("bg-danger");
heaterStatusCard.querySelector("h5").innerText = "Offline";
}
if (fanStatusCard) {
fanStatusCard.classList.remove("bg-success");
fanStatusCard.classList.add("bg-danger");
fanStatusCard.querySelector("h5").innerText = "Offline";
}
}
// Fetch data every 10 seconds
fetchDryerStatus();
setInterval(fetchDryerStatus, 10000);
});
</script>
</body>
</html>
Binary file not shown.

After

Width:  |  Height:  |  Size: 703 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 405 KiB

+12
View File
@@ -0,0 +1,12 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1000 820">
<title>HL3D HL Touchmark</title>
<desc>Single-colour simplified HL monogram for 3D-print embossing, debossing and touchmarking.</desc>
<g fill="#111111">
<!-- H: heavy angular form -->
<path d="M90 650 L90 240 L260 135 L260 290 L195 330 L195 425 L365 320 L365 155 L505 70 L505 520 L370 603 L370 450 L200 555 L200 715 Z"/>
<!-- L: interlocking lower-right form -->
<path d="M545 90 L680 10 L680 420 L910 560 L775 645 L545 505 Z"/>
<!-- lower bridge / foot to echo the dimensional mark while remaining a simple solid touchmark -->
<path d="M365 603 L505 520 L680 625 L545 710 Z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 687 B

+14 -227
View File
@@ -1,233 +1,20 @@
<?php include '../src/session_check.php'; ?>
<?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>
use Illuminate\Foundation\Application;
use Illuminate\Http\Request;
<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">
<div class="">
<div class="">
<div class="row g-4">
<div class="col-12 col-xl-4">
<div class="card rounded-4 mb-0">
<div class="card-body">
<div class="hstack align-items-center gap-3">
<div class="mb-1 widgets-icons bg-light-success text-success rounded-3 d-flex align-items-center justify-content-center">
<i class='bx bx-printer'></i>
</div>
<hr class="vr">
<div class="">
<h4 class="mb-0 d-flex align-items-center gap-2">$84,256 <span class="dash-lable d-flex align-items-center gap-1 rounded mb-0 bg-light-danger text-danger bg-opacity-10"><i class='bx bx-up-arrow-alt' ></i>8.6%</span></h4>
<p class="mb-0">Total Income</p>
</div>
</div>
<div id="chart1"></div>
</div>
</div>
</div>
<div class="col-12 col-xl-2">
<div class="card rounded-4 mb-0">
<div class="card-body">
<div class="d-flex align-items-start justify-content-between mb-0">
<div class="">
<h6 class="mb-0">Active Users</h6>
</div>
<div class="dropdown">
<a href="javascript:;" class="dropdown-toggle-nocaret more-options dropdown-toggle"
data-bs-toggle="dropdown">
<i class='bx bx-dots-vertical-rounded'></i>
</a>
<ul class="dropdown-menu">
<li><a class="dropdown-item" href="javascript:;">Action</a></li>
<li><a class="dropdown-item" href="javascript:;">Another action</a></li>
<li><a class="dropdown-item" href="javascript:;">Something else here</a></li>
</ul>
</div>
</div>
<div class="chart-container2">
<div id="chart2"></div>
</div>
<div class="text-center">
<div class="">
<h4 class="mb-1">42.5K</h4>
</div>
<p class="mb-0"><span class="text-success">+37.5K</span> from last month</p>
</div>
</div>
</div>
</div>
<div class="col-12 col-xl-4">
<div class="card rounded-4 mb-0">
<div class="card-body">
<div class="d-flex align-items-start justify-content-between mb-3">
<div class="">
<h6 class="mb-0">Sales & Views</h6>
</div>
<div class="dropdown">
<a href="javascript:;" class="dropdown-toggle-nocaret more-options dropdown-toggle"
data-bs-toggle="dropdown">
<i class='bx bx-dots-vertical-rounded'></i>
</a>
<ul class="dropdown-menu">
<li><a class="dropdown-item" href="javascript:;">Action</a></li>
<li><a class="dropdown-item" href="javascript:;">Another action</a></li>
<li><a class="dropdown-item" href="javascript:;">Something else here</a></li>
</ul>
</div>
</div>
<div id="chart3"></div>
</div>
</div>
</div>
<div class="col-12 col-xl-2">
<div class="card rounded-4 mb-0 overflow-hidden mb-0">
<div class="card-body">
<div class="d-flex align-items-start justify-content-between mb-1">
<div class="">
<h4 class="mb-0">87.4K</h4>
<p class="mb-0">Total Clicks</p>
</div>
<div class="dropdown">
<a href="javascript:;" class="dropdown-toggle-nocaret more-options dropdown-toggle"
data-bs-toggle="dropdown">
<i class='bx bx-dots-vertical-rounded'></i>
</a>
<ul class="dropdown-menu">
<li><a class="dropdown-item" href="javascript:;">Action</a></li>
<li><a class="dropdown-item" href="javascript:;">Another action</a></li>
<li><a class="dropdown-item" href="javascript:;">Something else here</a></li>
</ul>
</div>
</div>
</div>
<div class="chart-container">
<div id="chart4"></div>
</div>
</div>
</div>
<div class="col-12 col-xl-4">
<div class="card border-0 rounded-4 shadow-none mb-0 bg-transparent mb-0">
<div class="card-body p-0">
<div class="d-flex flex-column gap-4">
<div class="card rounded-4 mb-0">
<div class="card-body">
<div class="d-flex align-items-center">
<div>
<p class="mb-0 text-secondary">Total Accounts</p>
<h4 class="my-1">58.4K</h4>
<p class="mb-0 font-13 text-success">+14% Since last Month</p>
</div>
<div class="widgets-icons bg-light-danger text-danger ms-auto"><i class='bx bxs-group'></i></div>
</div>
<div id="chart8"></div>
</div>
</div>
<div class="card rounded-4">
<div class="card-body">
<div class="d-flex align-items-center">
<div>
<p class="mb-0 text-secondary">Bounce Rate</p>
<h4 class="my-1">36.7%</h4>
<p class="mb-0 font-13 text-success">+17% Since last Month</p>
</div>
<div class="widgets-icons bg-light-primary text-primary ms-auto"><i class='bx bx-bar-chart-alt-2'></i></div>
</div>
<div id="chart9"></div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div><!--end page content -->
</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-->
<footer class="page-footer">
<p class="mb-0">Copyright © 2024. All right reserved.</p>
</footer>
</div>
<!--end wrapper-->
define('LARAVEL_START', microtime(true));
// Determine if the application is in maintenance mode...
if (file_exists($maintenance = __DIR__.'/../storage/framework/maintenance.php')) {
require $maintenance;
}
<!-- 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 -->
// Register the Composer autoloader...
require __DIR__.'/../vendor/autoload.php';
<!-- 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>
// Bootstrap Laravel and handle the request...
/** @var Application $app */
$app = require_once __DIR__.'/../bootstrap/app.php';
<script src="../assets/js/index.js"></script>
<script src="../assets/plugins/peity/jquery.peity.min.js"></script>
<script>
$(".data-attributes span").peity("donut")
</script>
</body>
</html>
$app->handleRequest(Request::capture());
-98
View File
@@ -1,98 +0,0 @@
<!doctype html>
<html lang="en" data-bs-theme="dark">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" href="../assets/images/favicon-32x32.png" type="image/png">
<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">
<link href="../assets/css/pace.min.css" rel="stylesheet">
<script src="../assets/js/pace.min.js"></script>
<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">
<!-- 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">
<link href="../assets/css/icons.css" rel="stylesheet">
<title>TOD - Admin Dashboard</title>
</head>
<body>
<div class="wrapper">
<div class="section-authentication-cover">
<div class="row g-0">
<div class="col-12 col-xl-7 col-xxl-8 auth-cover-left d-none d-xl-flex align-items-center justify-content-center">
<div class="card shadow-none bg-transparent rounded-0">
<div class="card-body">
<img src="../assets/images/login-images/login-cover.svg" class="img-fluid" width="650" alt=""/>
</div>
</div>
</div>
<div class="col-12 col-xl-5 col-xxl-4 auth-cover-right align-items-center justify-content-center">
<div class="card rounded-0 m-3 shadow-none bg-transparent">
<div class="card-body p-sm-5">
<div class="text-center mb-4">
<img src="../assets/images/logo-icon.png" width="60" alt="">
<h5>TechOdyssey Designs Dashboard</h5>
<p>Please log in to your account</p>
</div>
<form id="loginForm" class="row g-3">
<div class="col-12">
<label for="inputEmailAddress" class="form-label">Username</label>
<input type="input" class="form-control" name="username" id="inputEmailAddress" required>
</div>
<div class="col-12">
<label for="inputChoosePassword" class="form-label">Password</label>
<div class="input-group" id="show_hide_password">
<input type="password" class="form-control" name="password" id="inputChoosePassword" required>
<a href="javascript:;" class="input-group-text bg-transparent"><i class="bx bx-hide"></i></a>
</div>
</div>
<div class="col-12 d-grid">
<button type="submit" class="btn btn-primary">Sign in</button>
</div>
<div class="col-12 text-danger text-center mt-2" id="loginMessage"></div>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
<script src="../assets/js/bootstrap.bundle.min.js"></script>
<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>
$(document).ready(function () {
$('#loginForm').on('submit', function (e) {
e.preventDefault();
const formData = $(this).serialize();
$.ajax({
url: '../src/login.php',
type: 'POST',
data: formData,
success: function (response) {
if (response.trim() === 'success') {
window.location.href = 'index.php';
} else {
$('#loginMessage').text(response);
}
}
});
});
});
</script>
<script src="../assets/js/app.js"></script>
</body>
</html>
-7
View File
@@ -1,7 +0,0 @@
<?php
session_start();
session_unset();
session_destroy();
header("Location: login.php");
exit();
?>
-211
View File
@@ -1,211 +0,0 @@
<?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 -->
<!--breadcrumb-->
<div class="page-breadcrumb d-none d-sm-flex align-items-center mb-3">
<div class="breadcrumb-title pe-3">User Settings</div>
<div class="ps-3">
<nav aria-label="breadcrumb">
<ol class="breadcrumb mb-0 p-0">
<li class="breadcrumb-item"><a href="index.php"><i class="bx bx-home-alt"></i></a>
</li>
<li class="breadcrumb-item active" aria-current="page">Add Printer</li>
</ol>
</nav>
</div>
</div>
<!--end breadcrumb-->
<div class="row">
<div class="col-lg-8 mx-auto">
<div class="card">
<div class="card-header px-4 py-3">
<h5 class="mb-0">Add New Printer</h5>
</div>
<div class="card-body p-4">
<!-- Alert Banner (Hidden Initially) -->
<div id="alertBanner" class="alert d-none" role="alert"></div>
<form id="printerForm">
<div class="row mb-3">
<label for="printerName" class="col-sm-3 col-form-label">Printer Name</label>
<div class="col-sm-9">
<input type="text" class="form-control" id="printerName" name="printerName" placeholder="Bambu X1 or P1P" required>
</div>
</div>
<div class="row mb-3">
<label for="printerIp" class="col-sm-3 col-form-label">Printer IP</label>
<div class="col-sm-9">
<input type="text" class="form-control" id="printerIp" name="printerIp" placeholder="e.g., 192.168.1.100" required>
</div>
</div>
<div class="row mb-3">
<label for="serialNumber" class="col-sm-3 col-form-label">Serial Number</label>
<div class="col-sm-9">
<input type="text" class="form-control" id="serialNumber" name="serialNumber" placeholder="e.g., X1SN123456" required>
</div>
</div>
<div class="row mb-3">
<label for="accessCode" class="col-sm-3 col-form-label">Printer Access Code</label>
<div class="col-sm-9">
<input type="password" class="form-control" id="accessCode" name="accessCode" placeholder="Enter Printer Access Code" required>
</div>
</div>
<div class="row">
<label class="col-sm-3 col-form-label"></label>
<div class="col-sm-9">
<div class="d-md-flex d-grid align-items-center gap-3">
<button type="submit" class="btn btn-primary px-4">Add Printer</button>
<button type="reset" class="btn btn-light px-4">Reset</button>
</div>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
<!-- jQuery & AJAX Script -->
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script>
$(document).ready(function() {
$('#printerForm').on('submit', function(event) {
event.preventDefault(); // Prevent traditional form submission
// Collect form data
let formData = $(this).serialize();
// AJAX request to submit form
$.ajax({
type: 'POST',
url: '../src/printers/addPrinter.php',
data: formData,
dataType: 'json',
success: function(response) {
let banner = $('#alertBanner');
if (response.status === 'success') {
banner.removeClass('d-none alert-danger')
.addClass('alert-success')
.text(response.message);
} else {
banner.removeClass('d-none alert-success')
.addClass('alert-danger')
.text(response.message);
}
},
error: function() {
$('#alertBanner').removeClass('d-none alert-success')
.addClass('alert-danger')
.text('An error occurred while adding the printer.');
}
});
});
});
</script>
<!--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-->
<footer class="page-footer">
<p class="mb-0">Copyright © 2024. All right reserved.</p>
</footer>
</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>
</body>
</html>
+2
View File
@@ -0,0 +1,2 @@
User-agent: *
Disallow:
-265
View File
@@ -1,265 +0,0 @@
<?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">
<div class="">
<div class="">
<!--start page content -->
<div class="row g-4">
<!-- Account Information Card -->
<div class="col-12 col-xl-6">
<div class="card rounded-4 mb-0 shadow-sm">
<div class="card-body">
<div class="d-flex align-items-center gap-3">
<div class="widgets-icons bg-light-success text-success rounded-circle d-flex align-items-center justify-content-center p-3">
<i class='bx bxs-user fs-3'></i>
</div>
<div id="accountInfo" class="ms-3">
<h5 class="mb-0 fw-bold">Loading...</h5>
<p class="text-muted mb-0">Account Information</p>
</div>
</div>
</div>
</div>
</div>
<!-- Prepay Balance Card -->
<div class="col-12 col-xl-6">
<div class="card rounded-4 mb-0 shadow-sm">
<div class="card-body">
<div class="d-flex align-items-center gap-3">
<div class="widgets-icons bg-light-info text-info rounded-circle d-flex align-items-center justify-content-center p-3">
<i class='bx bx-wallet fs-3'></i>
</div>
<div id="prepayBalance" class="ms-3">
<h5 class="mb-0 fw-bold">Loading...</h5>
<p class="text-muted mb-0">Prepay Balance</p>
</div>
</div>
</div>
</div>
</div>
<!-- Orders Table -->
<div class="col-12 col-xl-12">
<div class="card rounded-4 mb-0 shadow-sm">
<div class="card-body">
<h5 class="card-title fw-bold mb-3">Orders</h5>
<div id="ordersDetail" class="table-responsive">
<table class="table table-hover align-middle">
<thead class="table-dark">
<tr>
<th>Order ID</th>
<th>Courier</th>
<th>Service</th>
<th>Tracking Status</th>
<th>Estimated Delivery</th>
<th>Recipient</th>
</tr>
</thead>
<tbody>
<tr>
<td colspan="6" class="text-center text-muted">Loading orders...</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<!--end page content -->
</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).ready(function () {
// Fetch account information via AJAX
function fetchAccountInfo() {
$.ajax({
url: '../src/shipping/get_account_info.php',
type: 'POST',
dataType: 'json',
success: function (response) {
if (response.success) {
const data = response.data;
let html = `
<h5 class="mb-0 fw-bold">${data.Forename || 'N/A'} ${data.Surname || ''}</h5>
<p class="text-muted mb-0">Email: ${data.Email || 'N/A'}</p>
<p class="text-muted mb-0">Member Since: ${data.MemberSince || 'N/A'}</p>
`;
$('#accountInfo').html(html);
} else {
$('#accountInfo').html('<p class="text-danger">Error fetching account information.</p>');
}
},
error: function () {
$('#accountInfo').html('<p class="text-danger">Error loading account information.</p>');
}
});
}
// Fetch prepay balance via AJAX
function fetchPrepayBalance() {
$.ajax({
url: '../src/shipping/get_prepay_balance.php',
type: 'POST',
dataType: 'json',
success: function (response) {
if (response.success) {
const data = response.data;
const balance = data.Balance || 0;
$('#prepayBalance').html(`
<h5 class="mb-0 fw-bold">£ ${balance}</h5>
<p class="text-muted mb-0">Prepay Balance</p>
`);
} else {
$('#prepayBalance').html('<p class="text-danger">Error fetching balance.</p>');
}
},
error: function () {
$('#prepayBalance').html('<p class="text-danger">Error loading balance.</p>');
}
});
}
// Fetch order details via AJAX
function fetchOrdersDetail() {
$.ajax({
url: '../src/shipping/get_orders_detail.php',
type: 'POST',
dataType: 'json',
success: function (response) {
if (response.success) {
const orders = response.data.Orders || [];
let html = '';
if (orders.length === 0) {
html = '<tr><td colspan="6" class="text-center text-muted">No orders found.</td></tr>';
} else {
orders.forEach(order => {
const trackingStatus = order.Tracking?.Delivered ?
'<span class="badge bg-success">Delivered</span>' :
'<span class="badge bg-warning text-dark">In Transit</span>';
html += `
<tr>
<td>${order.OrderLineId}</td>
<td>${order.Courier}</td>
<td>${order.Service}</td>
<td>${trackingStatus}</td>
<td>${order.EstimatedDeliveryDate ? new Date(order.EstimatedDeliveryDate).toLocaleString() : 'N/A'}</td>
<td>${order.DeliveryAddress?.ContactName || 'N/A'}</td>
</tr>
`;
});
}
$('#ordersDetail tbody').html(html);
} else {
$('#ordersDetail tbody').html('<tr><td colspan="6" class="text-center text-danger">Error fetching orders.</td></tr>');
}
},
error: function () {
$('#ordersDetail tbody').html('<tr><td colspan="6" class="text-center text-danger">Error loading orders.</td></tr>');
}
});
}
// Trigger all fetches on page load
fetchAccountInfo();
fetchPrepayBalance();
fetchOrdersDetail();
});
</script>
</body>
</html>
-319
View File
@@ -1,319 +0,0 @@
<?php include '../src/session_check.php';
checkUserRole(['admin']);
?>
<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 mt-4">
<h2 class="text-center mb-4">User Management</h2>
<div class="d-flex justify-content-between mb-3">
<button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#createUserModal">
<i class="fas fa-user-plus"></i> Create New User
</button>
</div>
<div class="table-responsive">
<table class="table table-striped table-hover table-bordered align-middle">
<thead class="table-dark">
<tr>
<th>ID</th>
<th>Username</th>
<th>Email</th>
<th>Role</th>
<th>Actions</th>
</tr>
</thead>
<tbody id="userTableBody">
<!-- Rows dynamically populated -->
</tbody>
</table>
</div>
<!-- Create User Modal -->
<div class="modal fade" id="createUserModal" tabindex="-1" aria-labelledby="createUserModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="createUserModalLabel">Create New User</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<form id="createUserForm">
<div class="mb-3">
<label for="username" class="form-label">Username</label>
<input type="text" class="form-control" id="username" required>
</div>
<div class="mb-3">
<label for="email" class="form-label">Email</label>
<input type="email" class="form-control" id="email" required>
</div>
<div class="mb-3">
<label for="role" class="form-label">Role</label>
<select id="role" class="form-select" required>
<option value="">Select Role</option>
<option value="admin">Admin</option>
<option value="user">User</option>
</select>
</div>
<div class="mb-3">
<label for="password" class="form-label">Password</label>
<input type="password" class="form-control" id="password" required>
</div>
<button type="submit" class="btn btn-primary w-100">Create User</button>
</form>
</div>
</div>
</div>
</div>
<!-- Reset Password Modal -->
<div class="modal fade" id="resetPasswordModal" tabindex="-1" aria-labelledby="resetPasswordModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="resetPasswordModalLabel">Reset Password</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<form id="resetPasswordForm">
<p>Enter a new password for <span id="resetUserName"></span>:</p>
<div class="mb-3">
<label for="newPassword" class="form-label">New Password</label>
<input type="password" class="form-control" id="newPassword" required>
</div>
<button type="submit" class="btn btn-warning w-100">Reset Password</button>
</form>
</div>
</div>
</div>
</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-->
<footer class="page-footer">
<p class="mb-0">Copyright © 2024. All right reserved.</p>
</footer>
</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).ready(function () {
const apiUrl = '../src/userMananagementService.php';
// Load users on page load
fetchUsers();
// Create new user form submission
$('#createUserForm').submit(function (e) {
e.preventDefault();
const username = $('#username').val();
const email = $('#email').val();
const role = $('#role').val();
const password = $('#password').val();
$.ajax({
url: apiUrl,
method: 'POST',
dataType: 'json',
data: JSON.stringify({ username, email, role, password }),
contentType: 'application/json',
success: function () {
alert('User created successfully!');
fetchUsers();
$('#createUserModal').modal('hide');
$('#createUserForm')[0].reset();
},
error: function (xhr) {
alert('Error creating user: ' + xhr.responseText);
},
});
});
// Reset password form submission
$('#resetPasswordForm').submit(function (e) {
e.preventDefault();
const userId = $('#resetPasswordModal').data('userId');
const password = $('#newPassword').val();
$.ajax({
url: `${apiUrl}?reset-password=${userId}`,
method: 'POST',
dataType: 'json',
data: JSON.stringify({ password }),
contentType: 'application/json',
success: function () {
alert('Password reset successfully!');
$('#resetPasswordModal').modal('hide');
$('#resetPasswordForm')[0].reset();
},
error: function (xhr) {
alert('Error resetting password: ' + xhr.responseText);
},
});
});
});
// Fetch users from the backend
function fetchUsers() {
const apiUrl = '../src/userMananagementService.php';
$.ajax({
url: apiUrl,
method: 'GET',
dataType: 'json',
success: function (users) {
const tableBody = $('#userTableBody');
tableBody.empty();
users.forEach(function (user) {
const row = `
<tr>
<td>${user.id}</td>
<td>${user.username}</td>
<td>${user.email}</td>
<td>${user.role}</td>
<td>
<button class="btn btn-warning btn-sm" onclick="openResetPasswordModal(${user.id}, '${user.username}')">
<i class="fas fa-key"></i> Reset Password
</button>
<button class="btn btn-danger btn-sm" onclick="deleteUser(${user.id})">
<i class="fas fa-trash"></i> Delete
</button>
</td>
</tr>
`;
tableBody.append(row);
});
},
error: function (xhr) {
alert('Error fetching users: ' + xhr.responseText);
},
});
}
// Open the reset password modal
function openResetPasswordModal(userId, username) {
$('#resetPasswordModal').data('userId', userId);
$('#resetUserName').text(username);
$('#resetPasswordModal').modal('show');
}
// Delete user
function deleteUser(userId) {
const apiUrl = '../src/userMananagementService.php';
if (confirm('Are you sure you want to delete this user?')) {
$.ajax({
url: `${apiUrl}?delete-user=${userId}`,
method: 'POST',
success: function () {
alert('User deleted successfully!');
fetchUsers();
},
error: function (xhr) {
alert('Error deleting user: ' + xhr.responseText);
},
});
}
}
</script>
</body>
</html>
-283
View File
@@ -1,283 +0,0 @@
<?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);
let latestPrice = prices[prices.length - 1] || 0;
// Adjust price for voucher
const currentDiscount = filamentData.currentDiscount || {};
const voucher = currentDiscount.voucher || {};
if (voucher.value > 0) {
if (voucher.type === 'percentage') {
latestPrice -= (latestPrice * voucher.value) / 100; // Apply percentage voucher
} else if (voucher.type === 'fixed') {
latestPrice -= voucher.value; // Subtract fixed voucher value
}
}
// Ensure price doesn't go below zero
latestPrice = Math.max(latestPrice, 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 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.toFixed(2)} ${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>
-249
View File
@@ -1,249 +0,0 @@
<?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 -->
<!-- Breadcrumb -->
<div class="page-breadcrumb d-none d-sm-flex align-items-center mb-3">
<div class="breadcrumb-title pe-3">Printer Dashboard</div>
<div class="ps-3">
<nav aria-label="breadcrumb">
<ol class="breadcrumb mb-0 p-0">
<li class="breadcrumb-item"><a href="index.php"><i class="bx bx-home-alt"></i></a></li>
<li class="breadcrumb-item active" aria-current="page">Your Printers</li>
</ol>
</nav>
</div>
<div class="ms-auto">
<div class="btn-group">
<button type="button" class="btn btn-primary dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">
Refresh Interval: <span id="selectedIntervalText">60s</span>
</button>
<ul class="dropdown-menu dropdown-menu-end" id="intervalDropdown">
<li><a class="dropdown-item set-interval" data-interval="5000" href="#">5 seconds</a></li>
<li><a class="dropdown-item set-interval" data-interval="10000" href="#">10 seconds</a></li>
<li><a class="dropdown-item set-interval" data-interval="30000" href="#">30 seconds</a></li>
<li><a class="dropdown-item set-interval" data-interval="60000" href="#">60 seconds</a></li>
<li><a class="dropdown-item set-interval" data-interval="off" href="#">Off</a></li>
</ul>
</div>
</div>
</div>
<!-- End Breadcrumb -->
<div class="container">
<div class="main-body">
<div id="alertContainer" class="container mt-3">
<!-- Alerts will be dynamically inserted here -->
</div>
<div class="row" id="printerContainer">
<!-- Printer cards will be dynamically inserted here -->
</div>
</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).ready(function() {
let refreshInterval = 60000; // Default interval of 10s
let intervalHandler;
// Load printers asynchronously
async function loadPrinters() {
try {
const response = await $.ajax({
url: '../src/printers/getPrinters.php',
method: 'GET',
dataType: 'json',
cache: false
});
let container = $('#printerContainer');
if (response.status === 'success' && response.data.length > 0) {
response.data.forEach(function(printer) {
let existingCard = container.find(`.printer-card[data-serial="${printer.serialNumber}"]`);
let trayHTML = '';
printer.trays.forEach(tray => {
let trayColor = `#${tray.color.slice(0, 6)}`;
trayHTML += `
<div class="tray-slot text-center">
<div class="tray-circle" style="background-color: ${trayColor}; width: 40px; height: 40px; border-radius: 50%;"></div>
<p>Slot ${parseInt(tray.id) + 1}</p>
</div>`;
});
let printerCard = `
<div class="col-lg-4 printer-card" data-serial="${printer.serialNumber}">
<div class="card mb-4">
<div class="card-body text-center">
<img src="../assets/images/printer-icon.png" alt="Printer" class="rounded-circle p-1" width="110">
<div class="mt-3">
<h4>${printer.printerName}</h4>
<p class="text-secondary mb-1">Current Job: <span>${printer.jobName}</span></p>
<p class="text-muted font-size-sm">
Bed Temp: <span>${printer.bedTemp}°C</span> |
Nozzle Temp: <span>${printer.nozzleTemp}°C</span>
</p>
</div>
<div class="d-flex justify-content-center gap-4 mt-3">
${trayHTML}
</div>
<div class="mt-4 w-100">
<label for="humidityBar${printer.serialNumber}" class="form-label">AMS Humidity</label>
<div class="progress" style="height: 20px;">
<div id="humidityBar${printer.serialNumber}" class="progress-bar" role="progressbar"
style="width: ${printer.humidity * 20}%;"
aria-valuemin="1"
aria-valuemax="5">
${printer.humidity}/5
</div>
</div>
</div>
</div>
</div>
</div>`;
if (existingCard.length) {
existingCard.replaceWith(printerCard);
} else {
container.append(printerCard);
}
});
}
} catch (error) {
console.log('Failed to load printers:', error);
}
}
// Polling function
function startPolling(interval) {
clearInterval(intervalHandler);
if (interval !== 'off') {
intervalHandler = setInterval(loadPrinters, interval);
}
}
// Handle refresh dropdown
$('.set-interval').on('click', function(e) {
e.preventDefault();
let selectedInterval = $(this).data('interval');
if (selectedInterval === 'off') {
clearInterval(intervalHandler);
console.log("Auto-refresh turned off.");
} else {
refreshInterval = parseInt(selectedInterval);
startPolling(refreshInterval);
console.log(`Refresh interval set to ${refreshInterval / 1000} seconds.`);
}
$('#selectedIntervalText').text(selectedInterval === 'off' ? 'Off' : `${selectedInterval / 1000}s`);
});
loadPrinters();
startPolling(refreshInterval); // Start polling immediately
});
</script>
</body>
</html>