737 lines
27 KiB
PHP
737 lines
27 KiB
PHP
<?php
|
|
// phpcs:ignoreFile
|
|
|
|
if (!isset($requiredGrowerId)) {
|
|
http_response_code(400);
|
|
echo 'Grower dashboard misconfiguration.';
|
|
exit();
|
|
}
|
|
|
|
$rootDir = dirname(__DIR__);
|
|
|
|
require_once $rootDir . '/grower-session.php';
|
|
require_once $rootDir . '/inc/dbconfig.php';
|
|
require_once $rootDir . '/inc/opendb.php';
|
|
require_once $rootDir . '/inc/auth.php';
|
|
require_once $rootDir . '/inc/grower_helpers.php';
|
|
|
|
function lasuca_env($key, $fallback = null)
|
|
{
|
|
$value = getenv($key);
|
|
|
|
if ($value === false || $value === null || $value === '') {
|
|
return $fallback;
|
|
}
|
|
|
|
return $value;
|
|
}
|
|
|
|
function format_metric_value($value, $decimals = 0, $unit = '')
|
|
{
|
|
if (!is_numeric($value)) {
|
|
return '—';
|
|
}
|
|
|
|
$formatted = number_format((float) $value, $decimals);
|
|
|
|
if ($unit !== '') {
|
|
$formatted .= ' ' . $unit;
|
|
}
|
|
|
|
return $formatted;
|
|
}
|
|
|
|
function number_or_dash($value, $decimals = 0)
|
|
{
|
|
if (!is_numeric($value)) {
|
|
return '—';
|
|
}
|
|
|
|
return number_format((float) $value, $decimals);
|
|
}
|
|
|
|
function format_datetime_label($value, $format = 'g:i A')
|
|
{
|
|
if ($value instanceof DateTime) {
|
|
return $value->format($format);
|
|
}
|
|
|
|
if ($value === null || $value === '') {
|
|
return '—';
|
|
}
|
|
|
|
$timestamp = strtotime((string) $value);
|
|
|
|
if ($timestamp === false) {
|
|
return '—';
|
|
}
|
|
|
|
return date($format, $timestamp);
|
|
}
|
|
|
|
function calculate_net_weight($scaleWeight, $grossWeight)
|
|
{
|
|
if (!is_numeric($scaleWeight) || !is_numeric($grossWeight)) {
|
|
return 0.0;
|
|
}
|
|
|
|
$scale = (float) $scaleWeight;
|
|
$gross = (float) $grossWeight;
|
|
|
|
if ($scale > 100000 && $scale > $gross) {
|
|
return $scale - $gross;
|
|
}
|
|
|
|
return 0.0;
|
|
}
|
|
|
|
function parse_boolean_value($value)
|
|
{
|
|
if (is_bool($value)) {
|
|
return $value;
|
|
}
|
|
|
|
if (is_numeric($value)) {
|
|
return ((int) $value) === 1;
|
|
}
|
|
|
|
if (is_string($value)) {
|
|
$normalized = strtolower(trim($value));
|
|
return in_array($normalized, array('1', 'true', 'yes', 'y'), true);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
function capture_metric_from_include($path)
|
|
{
|
|
if (!file_exists($path)) {
|
|
return null;
|
|
}
|
|
|
|
ob_start();
|
|
$result = @include $path;
|
|
$output = trim(ob_get_clean());
|
|
|
|
if ($result === false || $output === '') {
|
|
return null;
|
|
}
|
|
|
|
if (!preg_match('/-?\d+(?:\.\d+)?/', $output, $matches)) {
|
|
return null;
|
|
}
|
|
|
|
return (float) $matches[0];
|
|
}
|
|
|
|
function fetch_factory_metrics($rootDir)
|
|
{
|
|
$metrics = array(
|
|
'current_rate' => null,
|
|
'avg_15_min' => null,
|
|
'tons_today' => null,
|
|
'tons_prev' => null,
|
|
'run_hours_today' => null,
|
|
'run_hours_prev' => null,
|
|
'updated_at_raw' => null,
|
|
'error' => null
|
|
);
|
|
|
|
if (!class_exists('mysqli')) {
|
|
$metrics['error'] = 'Factory data currently unavailable.';
|
|
return $metrics;
|
|
}
|
|
|
|
$itemsPath = $rootDir . '/inc/items.php';
|
|
|
|
if (!file_exists($itemsPath)) {
|
|
$metrics['error'] = 'Factory data currently unavailable.';
|
|
return $metrics;
|
|
}
|
|
|
|
$value = array();
|
|
$rounded = array();
|
|
$time = array();
|
|
|
|
require $itemsPath;
|
|
|
|
$hasTonsToday = false;
|
|
$tonsToday = 0.0;
|
|
|
|
if (isset($value['CANETOT'])) {
|
|
$tonsToday += (float) $value['CANETOT'];
|
|
$hasTonsToday = true;
|
|
}
|
|
|
|
if (isset($value['W TONS GROUND'])) {
|
|
$tonsToday += (float) $value['W TONS GROUND'];
|
|
$hasTonsToday = true;
|
|
}
|
|
|
|
$metrics['tons_today'] = $hasTonsToday ? $tonsToday : null;
|
|
|
|
$hasTonsPrev = false;
|
|
$tonsPrev = 0.0;
|
|
|
|
if (isset($value['PREVTONS'])) {
|
|
$tonsPrev += (float) $value['PREVTONS'];
|
|
$hasTonsPrev = true;
|
|
}
|
|
|
|
if (isset($value['WPREVGROUND'])) {
|
|
$tonsPrev += (float) $value['WPREVGROUND'];
|
|
$hasTonsPrev = true;
|
|
}
|
|
|
|
$metrics['tons_prev'] = $hasTonsPrev ? $tonsPrev : null;
|
|
|
|
if (isset($rounded['RUNHRSTODAY'])) {
|
|
$metrics['run_hours_today'] = (float) $rounded['RUNHRSTODAY'];
|
|
}
|
|
|
|
if (isset($value['PREVTIME'])) {
|
|
$metrics['run_hours_prev'] = (float) $value['PREVTIME'];
|
|
}
|
|
|
|
$timestampCandidates = array('CANETOT', 'RUNHRSTODAY', 'PREVTONS', 'PREVTIME');
|
|
|
|
foreach ($timestampCandidates as $candidate) {
|
|
if (isset($time[$candidate]) && $time[$candidate] !== null && $time[$candidate] !== '') {
|
|
$metrics['updated_at_raw'] = $time[$candidate];
|
|
break;
|
|
}
|
|
}
|
|
|
|
$stableratePath = $rootDir . '/inc/stablerate30.php';
|
|
$metrics['current_rate'] = capture_metric_from_include($stableratePath);
|
|
|
|
$avgPath = $rootDir . '/inc/east15minavg.php';
|
|
$metrics['avg_15_min'] = capture_metric_from_include($avgPath);
|
|
|
|
if ($metrics['current_rate'] === null) {
|
|
$hasRate = false;
|
|
$rateSum = 0.0;
|
|
|
|
if (isset($value['E TONS PER HOUR'])) {
|
|
$rateSum += (float) $value['E TONS PER HOUR'];
|
|
$hasRate = true;
|
|
}
|
|
|
|
if (isset($value['W RATE'])) {
|
|
$rateSum += (float) $value['W RATE'];
|
|
$hasRate = true;
|
|
}
|
|
|
|
if ($hasRate) {
|
|
$metrics['current_rate'] = $rateSum;
|
|
}
|
|
}
|
|
|
|
if ($metrics['avg_15_min'] === null) {
|
|
$hasAvg = false;
|
|
$avgSum = 0.0;
|
|
|
|
if (isset($rounded['E 15 MIN AVG'])) {
|
|
$avgSum += (float) $rounded['E 15 MIN AVG'];
|
|
$hasAvg = true;
|
|
}
|
|
|
|
if (isset($rounded['W 15 MIN AVG'])) {
|
|
$avgSum += (float) $rounded['W 15 MIN AVG'];
|
|
$hasAvg = true;
|
|
}
|
|
|
|
if ($hasAvg) {
|
|
$metrics['avg_15_min'] = $avgSum;
|
|
}
|
|
}
|
|
|
|
if ($metrics['current_rate'] === null &&
|
|
$metrics['avg_15_min'] === null &&
|
|
$metrics['tons_today'] === null &&
|
|
$metrics['run_hours_today'] === null) {
|
|
$metrics['error'] = 'Factory data currently unavailable.';
|
|
}
|
|
|
|
return $metrics;
|
|
}
|
|
|
|
function connect_scale_db()
|
|
{
|
|
if (!function_exists('sqlsrv_connect')) {
|
|
return null;
|
|
}
|
|
|
|
$server = lasuca_env('SQLSRV_HOST', 'CBM2K12\\SQLEXPRESS');
|
|
$database = lasuca_env('SQLSRV_DATABASE', 'SugarCaneScale');
|
|
$user = lasuca_env('SQLSRV_USERNAME', 'cbmclient');
|
|
$password = lasuca_env('SQLSRV_PASSWORD', 'ascbm2k');
|
|
|
|
$connectionInfo = array(
|
|
'Database' => $database,
|
|
'UID' => $user,
|
|
'PWD' => $password,
|
|
'ReturnDatesAsStrings' => true,
|
|
'CharacterSet' => 'UTF-8'
|
|
);
|
|
|
|
$link = @call_user_func('sqlsrv_connect', $server, $connectionInfo);
|
|
|
|
if ($link === false) {
|
|
return null;
|
|
}
|
|
|
|
return $link;
|
|
}
|
|
|
|
function fetch_today_load_summary($connection, $growerId)
|
|
{
|
|
if (!function_exists('sqlsrv_query')) {
|
|
return array();
|
|
}
|
|
|
|
$sql = "SELECT COUNT(*) AS load_count,
|
|
SUM(Tons) AS total_tons,
|
|
SUM(GrossWt) AS total_gross,
|
|
SUM(TareWt) AS total_tare,
|
|
SUM(CASE WHEN ScaleWt > 100000 AND ScaleWt > GrossWt THEN ScaleWt - GrossWt ELSE 0 END) AS total_net,
|
|
MAX(DateOut) AS last_load_time
|
|
FROM LoadData
|
|
WHERE FarmerId_Fk = ? AND CONVERT(date, DateOut) = CONVERT(date, GETDATE())";
|
|
|
|
$params = array((int) $growerId);
|
|
$stmt = call_user_func('sqlsrv_query', $connection, $sql, $params);
|
|
|
|
if ($stmt === false) {
|
|
return array();
|
|
}
|
|
|
|
$fetchAssoc = defined('SQLSRV_FETCH_ASSOC') ? constant('SQLSRV_FETCH_ASSOC') : 2;
|
|
$row = call_user_func('sqlsrv_fetch_array', $stmt, $fetchAssoc);
|
|
|
|
if (function_exists('sqlsrv_free_stmt')) {
|
|
call_user_func('sqlsrv_free_stmt', $stmt);
|
|
}
|
|
|
|
if (!$row) {
|
|
return array();
|
|
}
|
|
|
|
$loadCount = isset($row['load_count']) ? (int) $row['load_count'] : 0;
|
|
|
|
$summary = array(
|
|
'load_count' => $loadCount,
|
|
'total_tons' => isset($row['total_tons']) ? (float) $row['total_tons'] : 0.0,
|
|
'total_gross' => isset($row['total_gross']) ? (float) $row['total_gross'] : 0.0,
|
|
'total_tare' => isset($row['total_tare']) ? (float) $row['total_tare'] : 0.0,
|
|
'total_net' => isset($row['total_net']) ? (float) $row['total_net'] : 0.0,
|
|
'last_load_time' => isset($row['last_load_time']) ? $row['last_load_time'] : null
|
|
);
|
|
|
|
if ($loadCount > 0) {
|
|
$summary['avg_tons'] = $summary['total_tons'] / $loadCount;
|
|
$summary['avg_gross'] = $summary['total_gross'] / $loadCount;
|
|
$summary['avg_tare'] = $summary['total_tare'] / $loadCount;
|
|
$summary['avg_net'] = $summary['total_net'] / $loadCount;
|
|
} else {
|
|
$summary['avg_tons'] = 0.0;
|
|
$summary['avg_gross'] = 0.0;
|
|
$summary['avg_tare'] = 0.0;
|
|
$summary['avg_net'] = 0.0;
|
|
}
|
|
|
|
return $summary;
|
|
}
|
|
|
|
function fetch_today_loads($connection, $growerId, $limit = 25)
|
|
{
|
|
if (!function_exists('sqlsrv_query')) {
|
|
return array();
|
|
}
|
|
|
|
$limit = max(1, (int) $limit);
|
|
|
|
$sql = "SELECT TOP {$limit}
|
|
LoadId_Pk,
|
|
VehicleId_Fk,
|
|
TractId_Fk,
|
|
Tons,
|
|
TareWt,
|
|
GrossWt,
|
|
ScaleWt,
|
|
Parked,
|
|
DateOut
|
|
FROM LoadData
|
|
WHERE FarmerId_Fk = ? AND CONVERT(date, DateOut) = CONVERT(date, GETDATE())
|
|
ORDER BY DateOut DESC, LoadId_Pk DESC";
|
|
|
|
$params = array((int) $growerId);
|
|
$stmt = call_user_func('sqlsrv_query', $connection, $sql, $params);
|
|
|
|
if ($stmt === false) {
|
|
return array();
|
|
}
|
|
|
|
$loads = array();
|
|
$fetchAssoc = defined('SQLSRV_FETCH_ASSOC') ? constant('SQLSRV_FETCH_ASSOC') : 2;
|
|
|
|
while ($row = call_user_func('sqlsrv_fetch_array', $stmt, $fetchAssoc)) {
|
|
$loads[] = array(
|
|
'load_id' => isset($row['LoadId_Pk']) ? (int) $row['LoadId_Pk'] : null,
|
|
'vehicle' => isset($row['VehicleId_Fk']) ? (string) $row['VehicleId_Fk'] : '',
|
|
'tract' => isset($row['TractId_Fk']) ? (string) $row['TractId_Fk'] : '',
|
|
'tons' => isset($row['Tons']) ? (float) $row['Tons'] : null,
|
|
'tare_weight' => isset($row['TareWt']) ? (float) $row['TareWt'] : null,
|
|
'gross_weight' => isset($row['GrossWt']) ? (float) $row['GrossWt'] : null,
|
|
'net_weight' => calculate_net_weight(isset($row['ScaleWt']) ? $row['ScaleWt'] : null, isset($row['GrossWt']) ? $row['GrossWt'] : null),
|
|
'date_out' => isset($row['DateOut']) ? $row['DateOut'] : null,
|
|
'parked' => parse_boolean_value(isset($row['Parked']) ? $row['Parked'] : null)
|
|
);
|
|
}
|
|
|
|
if (function_exists('sqlsrv_free_stmt')) {
|
|
call_user_func('sqlsrv_free_stmt', $stmt);
|
|
}
|
|
|
|
return $loads;
|
|
}
|
|
|
|
$username = isset($_SESSION['myusername']) ? $_SESSION['myusername'] : '';
|
|
$growerId = isset($_SESSION['growerid']) ? (string) $_SESSION['growerid'] : '';
|
|
|
|
if ($growerId !== (string) $requiredGrowerId) {
|
|
header('Location: /grower-login.php');
|
|
exit();
|
|
}
|
|
|
|
$member = auth_find_member($username);
|
|
$memberData = grower_member_defaults($member);
|
|
$recentFiles = grower_recent_files($username, 5);
|
|
|
|
$displayName = '';
|
|
if (is_array($member)) {
|
|
$first = isset($member['firstname']) ? trim((string) $member['firstname']) : '';
|
|
$last = isset($member['lastname']) ? trim((string) $member['lastname']) : '';
|
|
$displayName = trim($first . ' ' . $last);
|
|
|
|
if ($displayName === '' && isset($member['growername'])) {
|
|
$displayName = trim((string) $member['growername']);
|
|
}
|
|
}
|
|
|
|
if ($displayName === '') {
|
|
$displayName = 'Grower ' . (string) $growerId;
|
|
}
|
|
|
|
$factoryMetrics = fetch_factory_metrics($rootDir);
|
|
$scaleConnection = connect_scale_db();
|
|
|
|
$todayLoadSummary = array();
|
|
$todayLoads = array();
|
|
|
|
if ($scaleConnection) {
|
|
$todayLoadSummary = fetch_today_load_summary($scaleConnection, $growerId);
|
|
$todayLoads = fetch_today_loads($scaleConnection, $growerId, 25);
|
|
|
|
if (function_exists('sqlsrv_close')) {
|
|
call_user_func('sqlsrv_close', $scaleConnection);
|
|
}
|
|
}
|
|
|
|
require_once $rootDir . '/inc/closedb.php';
|
|
|
|
$loadSummaryCount = isset($todayLoadSummary['load_count']) ? (int) $todayLoadSummary['load_count'] : 0;
|
|
|
|
if ($loadSummaryCount > 0) {
|
|
$totalTonsText = number_or_dash(isset($todayLoadSummary['total_tons']) ? $todayLoadSummary['total_tons'] : null, 1);
|
|
if ($totalTonsText === '—') {
|
|
$heroSummaryText = sprintf(
|
|
'We\'ve logged %s %s today. Keep scrolling for mill stats and the live load list.',
|
|
number_format($loadSummaryCount),
|
|
$loadSummaryCount === 1 ? 'load' : 'loads'
|
|
);
|
|
} else {
|
|
$heroSummaryText = sprintf(
|
|
'We\'ve logged %s %s so far today totaling %s tons. Keep scrolling for mill stats and the live load list.',
|
|
number_format($loadSummaryCount),
|
|
$loadSummaryCount === 1 ? 'load' : 'loads',
|
|
$totalTonsText
|
|
);
|
|
}
|
|
} else {
|
|
$heroSummaryText = 'Your loads will appear below as soon as the first ticket clears the scale today. In the meantime, check your recent PDFs and mill performance snapshots.';
|
|
}
|
|
|
|
$heroFootnoteParts = array();
|
|
|
|
if (empty($factoryMetrics['error'])) {
|
|
$factoryUpdatedLabel = format_datetime_label(isset($factoryMetrics['updated_at_raw']) ? $factoryMetrics['updated_at_raw'] : null, 'g:i A');
|
|
if ($factoryUpdatedLabel !== '—') {
|
|
$heroFootnoteParts[] = 'Factory snapshot updated ' . $factoryUpdatedLabel;
|
|
}
|
|
} else {
|
|
$factoryUpdatedLabel = '—';
|
|
}
|
|
|
|
if ($loadSummaryCount > 0) {
|
|
$lastLoadSource = isset($todayLoadSummary['last_load_time']) ? $todayLoadSummary['last_load_time'] : null;
|
|
if ($lastLoadSource === null && !empty($todayLoads)) {
|
|
$lastLoadSource = $todayLoads[0]['date_out'];
|
|
}
|
|
|
|
$lastLoadLabel = format_datetime_label($lastLoadSource, 'g:i A');
|
|
|
|
if ($lastLoadLabel !== '—') {
|
|
$heroFootnoteParts[] = 'Last load recorded at ' . $lastLoadLabel;
|
|
}
|
|
} else {
|
|
$lastLoadLabel = '';
|
|
}
|
|
|
|
$lastLoginLabel = grower_format_datetime($memberData['last_login_at']);
|
|
if ($lastLoginLabel !== 'Never') {
|
|
$heroFootnoteParts[] = 'Last login ' . $lastLoginLabel;
|
|
}
|
|
|
|
$heroFootnote = '';
|
|
if (!empty($heroFootnoteParts)) {
|
|
$heroFootnote = implode(' · ', $heroFootnoteParts);
|
|
}
|
|
|
|
if (empty($factoryMetrics['error']) && (!isset($factoryUpdatedLabel) || $factoryUpdatedLabel === '—')) {
|
|
$factoryUpdatedLabel = 'Live reading';
|
|
}
|
|
|
|
$heroFootnoteText = $heroFootnote !== '' ? $heroFootnote : 'Data refreshes every few minutes · Last check ' . date('g:i A');
|
|
?>
|
|
<!DOCTYPE html>
|
|
<html lang="en" dir="ltr">
|
|
<head>
|
|
<meta charset="utf-8" />
|
|
<title>Grower Dashboard</title>
|
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css" />
|
|
<link rel="stylesheet" href="/new/css/styles.css" />
|
|
<link rel="stylesheet" href="/new/css/lasuca-theme.css" />
|
|
<link rel="stylesheet" href="/new/css/grower-dashboard.css" />
|
|
</head>
|
|
<body class="lasuca-theme theme-dark">
|
|
<nav class="navbar navbar-expand-lg navbar-dark">
|
|
<div class="container">
|
|
<a class="navbar-brand" href="/grower-dashboard.php">
|
|
<img src="/images/logo2.png" alt="LASUCA logo" />
|
|
<span>Grower Portal</span>
|
|
</a>
|
|
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#growerNav" aria-controls="growerNav" aria-expanded="false" aria-label="Toggle navigation">
|
|
<span class="navbar-toggler-icon"></span>
|
|
</button>
|
|
<div class="collapse navbar-collapse" id="growerNav">
|
|
<ul class="navbar-nav ms-auto mb-2 mb-lg-0">
|
|
<li class="nav-item"><a class="nav-link" href="/home.php">Home</a></li>
|
|
<li class="nav-item"><a class="nav-link active" href="/grower-dashboard.php">Dashboard</a></li>
|
|
<li class="nav-item"><a class="nav-link" href="/grower-files.php">Daily Reports</a></li>
|
|
<li class="nav-item"><a class="nav-link" href="/grower-account.php">Manage Account</a></li>
|
|
<li class="nav-item"><a class="nav-link" href="/grower-logout.php">Log Out</a></li>
|
|
<?php require $rootDir . '/inc/theme-toggle.php'; ?>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</nav>
|
|
<main>
|
|
<header class="hero">
|
|
<div class="container">
|
|
<div class="hero-content">
|
|
<span class="badge">Welcome back, <?php echo htmlspecialchars($displayName, ENT_QUOTES, 'UTF-8'); ?></span>
|
|
<h1>Today’s field and factory pulse.</h1>
|
|
<p><?php echo htmlspecialchars($heroSummaryText, ENT_QUOTES, 'UTF-8'); ?></p>
|
|
<div class="hero-actions d-flex flex-column flex-sm-row gap-3">
|
|
<a class="btn btn-primary" href="/grower-files.php">Daily Ticket PDFs</a>
|
|
<a class="btn btn-outline-light" href="loads.php">View Full Load History</a>
|
|
</div>
|
|
<p class="mt-3 mb-0 text-white-50 small"><?php echo htmlspecialchars($heroFootnoteText, ENT_QUOTES, 'UTF-8'); ?></p>
|
|
</div>
|
|
</div>
|
|
</header>
|
|
|
|
<section class="data-glance">
|
|
<div class="container">
|
|
<div class="row g-4">
|
|
<div class="col-xl-4">
|
|
<div class="data-panel h-100">
|
|
<h3 class="mb-4">Today's Data</h3>
|
|
<div class="metric-grid">
|
|
<div class="metric-card">
|
|
<span class="metric-label">Grower ID</span>
|
|
<span class="metric-value"><?php echo htmlspecialchars((string) $growerId, ENT_QUOTES, 'UTF-8'); ?></span>
|
|
</div>
|
|
<div class="metric-card">
|
|
<span class="metric-label">Loads today</span>
|
|
<span class="metric-value"><?php echo number_format($loadSummaryCount); ?></span>
|
|
</div>
|
|
<div class="metric-card">
|
|
<span class="metric-label">Tons today</span>
|
|
<span class="metric-value"><?php echo htmlspecialchars(number_or_dash(isset($todayLoadSummary['total_tons']) ? $todayLoadSummary['total_tons'] : null, 1), ENT_QUOTES, 'UTF-8'); ?></span>
|
|
</div>
|
|
<div class="metric-card">
|
|
<span class="metric-label">Avg tons/load</span>
|
|
<span class="metric-value"><?php echo htmlspecialchars(number_or_dash(isset($todayLoadSummary['avg_tons']) ? $todayLoadSummary['avg_tons'] : null, 1), ENT_QUOTES, 'UTF-8'); ?></span>
|
|
</div>
|
|
<div class="metric-card">
|
|
<span class="metric-label">Last load</span>
|
|
<span class="metric-value"><?php echo htmlspecialchars($lastLoadLabel !== '' ? $lastLoadLabel : '—', ENT_QUOTES, 'UTF-8'); ?></span>
|
|
</div>
|
|
<div class="metric-card">
|
|
<span class="metric-label">Last login</span>
|
|
<span class="metric-value"><?php echo htmlspecialchars($lastLoginLabel, ENT_QUOTES, 'UTF-8'); ?></span>
|
|
</div>
|
|
</div>
|
|
<p class="metric-footnote mb-0">Updates as tickets clear the scale.</p>
|
|
</div>
|
|
</div>
|
|
<div class="col-xl-8">
|
|
<div class="row g-4">
|
|
<div class="col-md-6">
|
|
<div class="feature-card h-100">
|
|
<h3>Factory Data</h3>
|
|
<?php if (!empty($factoryMetrics['error'])): ?>
|
|
<p class="mb-0 text-muted">Mill metrics are offline right now. Please check back shortly.</p>
|
|
<?php else: ?>
|
|
<ul class="stat-list">
|
|
<li>
|
|
<span class="stat-label">Current grinding rate</span>
|
|
<span class="stat-value"><?php echo htmlspecialchars(format_metric_value(isset($factoryMetrics['current_rate']) ? $factoryMetrics['current_rate'] : null, 0, 'tons/hr'), ENT_QUOTES, 'UTF-8'); ?></span>
|
|
</li>
|
|
<li>
|
|
<span class="stat-label">15-minute average</span>
|
|
<span class="stat-value"><?php echo htmlspecialchars(format_metric_value(isset($factoryMetrics['avg_15_min']) ? $factoryMetrics['avg_15_min'] : null, 0, 'tons'), ENT_QUOTES, 'UTF-8'); ?></span>
|
|
</li>
|
|
<li>
|
|
<span class="stat-label">Tons ground today</span>
|
|
<span class="stat-value"><?php echo htmlspecialchars(format_metric_value(isset($factoryMetrics['tons_today']) ? $factoryMetrics['tons_today'] : null, 0, 'tons'), ENT_QUOTES, 'UTF-8'); ?></span>
|
|
</li>
|
|
<li>
|
|
<span class="stat-label">Previous day tons</span>
|
|
<span class="stat-value"><?php echo htmlspecialchars(format_metric_value(isset($factoryMetrics['tons_prev']) ? $factoryMetrics['tons_prev'] : null, 0, 'tons'), ENT_QUOTES, 'UTF-8'); ?></span>
|
|
</li>
|
|
<li>
|
|
<span class="stat-label">Run hours today</span>
|
|
<span class="stat-value"><?php echo htmlspecialchars(format_metric_value(isset($factoryMetrics['run_hours_today']) ? $factoryMetrics['run_hours_today'] : null, 1, 'hrs'), ENT_QUOTES, 'UTF-8'); ?></span>
|
|
</li>
|
|
<li>
|
|
<span class="stat-label">Previous day hours</span>
|
|
<span class="stat-value"><?php echo htmlspecialchars(format_metric_value(isset($factoryMetrics['run_hours_prev']) ? $factoryMetrics['run_hours_prev'] : null, 1, 'hrs'), ENT_QUOTES, 'UTF-8'); ?></span>
|
|
</li>
|
|
</ul>
|
|
<p class="small text-muted mt-3 mb-0"><?php echo htmlspecialchars('Last updated ' . $factoryUpdatedLabel, ENT_QUOTES, 'UTF-8'); ?></p>
|
|
<?php endif; ?>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<div class="feature-card h-100">
|
|
<h3>Recent files</h3>
|
|
<?php if (!empty($recentFiles)): ?>
|
|
<ul class="recent-files-list">
|
|
<?php foreach ($recentFiles as $file): ?>
|
|
<li>
|
|
<a href="<?php echo htmlspecialchars($file['path'], ENT_QUOTES, 'UTF-8'); ?>" target="_blank"><?php echo htmlspecialchars($file['name'], ENT_QUOTES, 'UTF-8'); ?></a>
|
|
<span class="file-meta"><?php echo htmlspecialchars(grower_format_datetime(date('Y-m-d H:i:s', $file['modified'])), ENT_QUOTES, 'UTF-8'); ?></span>
|
|
</li>
|
|
<?php endforeach; ?>
|
|
</ul>
|
|
<?php else: ?>
|
|
<p class="mb-3 text-muted">We haven’t spotted any recent files in your folder yet.</p>
|
|
<?php endif; ?>
|
|
<a class="btn btn-sm btn-outline-success mt-3" href="/grower-files.php">Open file browser</a>
|
|
</div>
|
|
</div>
|
|
<div class="col-12">
|
|
<div class="feature-card h-100">
|
|
<h3>Daily report links</h3>
|
|
<p class="mb-3">Jump straight to your most common folders. These links open in a new tab.</p>
|
|
<div class="row g-3">
|
|
<div class="col-md-6">
|
|
<a class="btn w-100 btn-outline-success" href="index.php">PDF Reports</a>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<a class="btn w-100 btn-outline-success" href="/grower-account.php">Manage account</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<section class="py-5">
|
|
<div class="container">
|
|
<div class="feature-card load-table">
|
|
<div class="d-flex flex-column flex-lg-row align-items-lg-center justify-content-between gap-2 mb-4">
|
|
<div>
|
|
<h3 class="mb-1">Today’s loads</h3>
|
|
<p class="mb-0 text-muted">Showing up to 25 of your most recent tickets. Full history lives in the load data view.</p>
|
|
</div>
|
|
<a class="btn btn-outline-success" href="loads.php">Open full load history</a>
|
|
</div>
|
|
<?php if (!empty($todayLoads)): ?>
|
|
<div class="table-responsive">
|
|
<table class="table align-middle mb-0">
|
|
<thead>
|
|
<tr>
|
|
<th scope="col">Load</th>
|
|
<th scope="col">Vehicle</th>
|
|
<th scope="col">Tract</th>
|
|
<th scope="col">Tons</th>
|
|
<th scope="col">Gross</th>
|
|
<th scope="col">Tare</th>
|
|
<th scope="col">Net</th>
|
|
<th scope="col">Parked</th>
|
|
<th scope="col">Time out</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<?php foreach ($todayLoads as $load): ?>
|
|
<tr>
|
|
<td><?php echo htmlspecialchars(number_format(isset($load['load_id']) ? $load['load_id'] : 0), ENT_QUOTES, 'UTF-8'); ?></td>
|
|
<td><?php echo htmlspecialchars($load['vehicle'], ENT_QUOTES, 'UTF-8'); ?></td>
|
|
<td><?php echo htmlspecialchars($load['tract'], ENT_QUOTES, 'UTF-8'); ?></td>
|
|
<td><?php echo htmlspecialchars(format_metric_value($load['tons'], 2), ENT_QUOTES, 'UTF-8'); ?></td>
|
|
<td><?php echo htmlspecialchars(format_metric_value($load['gross_weight'], 0), ENT_QUOTES, 'UTF-8'); ?></td>
|
|
<td><?php echo htmlspecialchars(format_metric_value($load['tare_weight'], 0), ENT_QUOTES, 'UTF-8'); ?></td>
|
|
<td><?php echo htmlspecialchars(format_metric_value($load['net_weight'], 0), ENT_QUOTES, 'UTF-8'); ?></td>
|
|
<td><?php echo $load['parked'] ? '<span class="badge bg-warning text-dark">Yes</span>' : '<span class="badge bg-success">No</span>'; ?></td>
|
|
<td>
|
|
<?php echo htmlspecialchars(format_datetime_label(isset($load['date_out']) ? $load['date_out'] : null, 'g:i A'), ENT_QUOTES, 'UTF-8'); ?>
|
|
<span class="load-time"><?php echo htmlspecialchars(format_datetime_label(isset($load['date_out']) ? $load['date_out'] : null, 'M j'), ENT_QUOTES, 'UTF-8'); ?></span>
|
|
</td>
|
|
</tr>
|
|
<?php endforeach; ?>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
<?php else: ?>
|
|
<p class="mb-0 text-muted">No loads have been recorded today yet. Once the first ticket clears the scale you’ll see it here instantly.</p>
|
|
<?php endif; ?>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
</main>
|
|
<footer class="lasuca-footer">
|
|
<div class="container">
|
|
<div class="row g-3 align-items-center">
|
|
<div class="col-md-6">
|
|
<strong>Louisiana Sugar Cane Cooperative</strong><br />
|
|
341 Sugar Mill Road · St. Martinville, LA 70582
|
|
</div>
|
|
<div class="col-md-6 text-md-end">
|
|
<small>© <?php echo date('Y'); ?> LASUCA. All rights reserved.</small>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</footer>
|
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/js/bootstrap.bundle.min.js"></script>
|
|
<script src="/new/js/scripts.js"></script>
|
|
</body>
|
|
</html>
|
|
}
|