| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291 |
- <?php
- require_once __DIR__ . '/../config/database.php';
- require_once __DIR__ . '/../lib/auth.php';
- if (session_status() === PHP_SESSION_NONE) {
- session_start();
- }
- requireLogin();
- $pageTitle = 'Dashboard';
- $siteName = 'Crop Monitor';
- include __DIR__ . '/../layouts/header.php';
- include __DIR__ . '/../layouts/navbar.php';
- ?>
- <div id="layoutSidenav">
- <div id="layoutSidenav_nav">
- <?php include __DIR__ . '/../layouts/sidebar.php'; ?>
- </div>
- <div id="layoutSidenav_content">
- <main>
- <div class="container-fluid px-4">
- <h1 class="mt-4"><?= htmlspecialchars($pageTitle, ENT_QUOTES, 'UTF-8') ?></h1>
- <ol class="breadcrumb mb-4">
- <li class="breadcrumb-item active">Dashboard</li>
- </ol>
- <!-- ── Summary cards ──────────────────────────────────────── -->
- <div class="row">
- <div class="col-xl-3 col-sm-6 mb-3">
- <div class="card text-white bg-primary o-hidden h-100">
- <div class="card-body">
- <div class="card-body-icon"><i class="fas fa-fw fa-comments"></i></div>
- <div class="me-5">26 New Messages!</div>
- </div>
- <a class="card-footer text-white clearfix small z-1" href="/dashboard/inbox.php">
- <span class="float-start">View Details</span>
- <span class="float-end"><i class="fas fa-angle-right"></i></span>
- </a>
- </div>
- </div>
- <div class="col-xl-3 col-sm-6 mb-3">
- <div class="card text-white bg-warning o-hidden h-100">
- <div class="card-body">
- <div class="card-body-icon"><i class="fas fa-fw fa-list"></i></div>
- <div class="me-5">11 New Tasks!</div>
- </div>
- <a class="card-footer text-white clearfix small z-1" href="#">
- <span class="float-start">View Details</span>
- <span class="float-end"><i class="fas fa-angle-right"></i></span>
- </a>
- </div>
- </div>
- <div class="col-xl-3 col-sm-6 mb-3">
- <div class="card text-white bg-success o-hidden h-100">
- <div class="card-body">
- <div class="card-body-icon"><i class="fas fa-fw fa-seedling"></i></div>
- <div class="me-5">New Soil Tests</div>
- </div>
- <a class="card-footer text-white clearfix small z-1"
- href="/dashboard/crop-analysis/soil-test-data/soil-test-data.php">
- <span class="float-start">View Details</span>
- <span class="float-end"><i class="fas fa-angle-right"></i></span>
- </a>
- </div>
- </div>
- <div class="col-xl-3 col-sm-6 mb-3">
- <div class="card text-white bg-danger o-hidden h-100">
- <div class="card-body">
- <div class="card-body-icon"><i class="fas fa-fw fa-life-ring"></i></div>
- <div class="me-5">13 New Tickets!</div>
- </div>
- <a class="card-footer text-white clearfix small z-1" href="#">
- <span class="float-start">View Details</span>
- <span class="float-end"><i class="fas fa-angle-right"></i></span>
- </a>
- </div>
- </div>
- </div><!-- /row: summary cards -->
- <!-- ── Recent Analysis heading ─────────────────────────────── -->
- <div class="row mb-3">
- <div class="col-md-12">
- <h5>Recent Analysis</h5>
- </div>
- </div>
- <!-- ── Weather widget + Calendar ──────────────────────────── -->
- <div class="row">
- <!-- Weather widget -->
- <div class="col-md-7">
- <!-- Skeleton shown while loading -->
- <div id="weather-loading" class="weather d-flex align-items-center justify-content-center" style="min-height:180px;">
- <div class="text-muted small"><span class="spinner-border spinner-border-sm me-2"></span>Loading weather…</div>
- </div>
- <!-- Populated by JS -->
- <div id="weather-widget" class="weather" style="display:none;">
- <div class="weather-top">
- <div class="weather-top-left">
- <div class="degree">
- <figure class="icons">
- <canvas id="wx-hero-canvas" width="64" height="64"></canvas>
- </figure>
- <span id="wx-temp">—</span>
- <div class="clearfix"></div>
- </div>
- <p>
- <?= date('l') ?>
- <label><?= date('j') ?></label><sup><?= date('S') ?></sup>
- <?= date('M') ?>
- </p>
- </div>
- <div class="weather-top-right">
- <p><i class="fa fa-map-marker"></i> <span id="wx-location">—</span></p>
- <label id="wx-condition">—</label>
- <div class="small text-muted mt-1">
- <span title="Humidity"><i class="fa fa-tint"></i> <span id="wx-humidity">—</span>%</span>
-
- <span title="Wind"><i class="fa fa-wind"></i> <span id="wx-wind">—</span> km/h</span>
-
- <span title="Rain now"><i class="fa fa-cloud-rain"></i> <span id="wx-rain">—</span> mm</span>
- </div>
- </div>
- <div class="clearfix"></div>
- </div>
- <!-- 7-day forecast (future days) -->
- <div class="weather-bottom" id="wx-forecast-row">
- <!-- filled by JS -->
- <div class="clearfix"></div>
- </div>
- </div>
- <!-- Past 7-day rainfall chart -->
- <div id="wx-rainfall-card" class="card mt-3" style="display:none;">
- <div class="card-header py-1 small fw-bold">Past 7 Days Rainfall (mm)</div>
- <div class="card-body py-2">
- <canvas id="wx-rainfall-chart" height="80"></canvas>
- </div>
- </div>
- <div id="weather-error" class="alert alert-warning mt-2 small" style="display:none;"></div>
- </div><!-- /col: weather -->
- <!-- Calendar widget -->
- <div class="col-md-5">
- <div class="cal1 cal_2">
- <div id="clndr"></div>
- </div>
- <link rel="stylesheet" href="/client-assets/css/clndr.css">
- <script src="/client-assets/js/underscore-min.js"></script>
- <script src="/client-assets/js/moment-2.2.1.js"></script>
- <script src="/client-assets/js/clndr.js"></script>
- <script src="/client-assets/js/site.js"></script>
- </div><!-- /col: calendar -->
- </div><!-- /row: weather + calendar -->
- <div class="content-bottom mt-4"></div>
- <div class="clearfix"></div>
- </div><!-- /container-fluid -->
- <script>
- (function () {
- 'use strict';
- var heroSkycons = null;
- var fcSkycons = null;
- var rainfallChart = null;
- function renderWeather(data) {
- // ── Current conditions ───────────────────────────────────────────────
- document.getElementById('wx-temp').textContent = data.current.temp + '°';
- document.getElementById('wx-condition').textContent = data.current.label;
- document.getElementById('wx-location').textContent = data.location;
- document.getElementById('wx-humidity').textContent = data.current.humidity;
- document.getElementById('wx-wind').textContent = data.current.wind;
- document.getElementById('wx-rain').textContent = data.current.rain;
- // Hero Skycon
- var heroCanvas = document.getElementById('wx-hero-canvas');
- if (!heroSkycons) {
- heroSkycons = new Skycons({ color: '#1ABC9C' });
- }
- heroSkycons.set(heroCanvas, data.current.icon);
- heroSkycons.play();
- // ── Forecast row (future days only, up to 7) ─────────────────────────
- var forecastRow = document.getElementById('wx-forecast-row');
- var futureDays = data.days.filter(function (d) { return !d.is_past && !d.is_today; }).slice(0, 5);
- var fcHtml = '';
- if (!fcSkycons) {
- fcSkycons = new Skycons({ color: '#999' });
- }
- futureDays.forEach(function (d, i) {
- var cid = 'wx-fc-' + i;
- fcHtml +=
- '<div class="weather-bottom1">' +
- '<div class="weather-head">' +
- '<h4>' + d.label + '</h4>' +
- '<figure class="icons"><canvas id="' + cid + '" width="58" height="58"></canvas></figure>' +
- '<h6>' + d.temp_max + '°</h6>' +
- '<div class="bottom-head">' +
- '<p>' + d.day_short + '</p>' +
- '<p>' + d.day_name + '</p>' +
- '</div>' +
- '</div>' +
- '</div>';
- });
- fcHtml += '<div class="clearfix"></div>';
- forecastRow.innerHTML = fcHtml;
- futureDays.forEach(function (d, i) {
- var el = document.getElementById('wx-fc-' + i);
- if (el) { fcSkycons.set(el, d.icon); }
- });
- fcSkycons.play();
- // ── Past 7-day rainfall chart ─────────────────────────────────────────
- var pastDays = data.days.filter(function (d) { return d.is_past; }).slice(-7);
- if (pastDays.length > 0) {
- var labels = pastDays.map(function (d) { return d.day_name + ' ' + d.day_short; });
- var values = pastDays.map(function (d) { return d.rain; });
- var ctx = document.getElementById('wx-rainfall-chart').getContext('2d');
- if (rainfallChart) { rainfallChart.destroy(); }
- rainfallChart = new Chart(ctx, {
- type: 'bar',
- data: {
- labels: labels,
- datasets: [{
- label: 'Rainfall (mm)',
- data: values,
- backgroundColor: 'rgba(54, 162, 235, 0.6)',
- borderColor: 'rgba(54, 162, 235, 1)',
- borderWidth: 1,
- }],
- },
- options: {
- responsive: true,
- plugins: { legend: { display: false } },
- scales: {
- y: { beginAtZero: true, ticks: { font: { size: 10 } } },
- x: { ticks: { font: { size: 10 } } },
- },
- },
- });
- document.getElementById('wx-rainfall-card').style.display = '';
- }
- // Show widget, hide skeleton
- document.getElementById('weather-loading').style.display = 'none';
- document.getElementById('weather-widget').style.display = '';
- }
- function loadWeather() {
- fetch('/api/weather.php')
- .then(function (r) {
- if (!r.ok) { throw new Error('HTTP ' + r.status); }
- return r.json();
- })
- .then(function (data) {
- if (data.error) { throw new Error(data.error); }
- renderWeather(data);
- })
- .catch(function (err) {
- document.getElementById('weather-loading').style.display = 'none';
- var errEl = document.getElementById('weather-error');
- errEl.textContent = 'Weather unavailable: ' + err.message;
- errEl.style.display = '';
- });
- }
- document.addEventListener('DOMContentLoaded', loadWeather);
- })();
- </script>
- <?php include __DIR__ . '/../layouts/footer.php'; ?>
|