water-analysis-pdf.php 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290
  1. <?php
  2. /**
  3. * dashboard/crop-analysis/water-test-data/water-analysis-pdf.php
  4. *
  5. * Water quality analysis results display page.
  6. * Supports normal browser access (session login) and headless Chrome (ptoken).
  7. */
  8. require_once __DIR__ . '/../../../config/database.php';
  9. require_once __DIR__ . '/../../../lib/auth.php';
  10. require_once __DIR__ . '/../../../lib/print_auth.php';
  11. if (session_status() === PHP_SESSION_NONE) {
  12. session_start();
  13. }
  14. $recordId = (int) ($_GET['rid'] ?? 0);
  15. $randId = trim( $_GET['rand'] ?? '');
  16. $clientId = (int) ($_GET['cid'] ?? 0);
  17. $printMode = isset($_GET['print']) || isset($_GET['ptoken']);
  18. if (!$recordId || $randId === '') {
  19. http_response_code(400);
  20. die('Invalid request parameters');
  21. }
  22. $chromeAccess = authenticatePrintPage($recordId, $randId);
  23. $pdo = getDBConnection();
  24. $userId = $chromeAccess ? null : getCurrentUserId();
  25. $row = null;
  26. $specs = [];
  27. $stmt = $pdo->prepare('SELECT * FROM water_records WHERE id = ? AND rand = ? LIMIT 1');
  28. $stmt->execute([$recordId, $randId]);
  29. $row = $stmt->fetch(PDO::FETCH_ASSOC);
  30. if ($row) {
  31. $type = $row['analysis_type'] ?? '';
  32. if ($type !== '') {
  33. $stmtSpec = $pdo->prepare('SELECT * FROM water_specifications WHERE type = ? LIMIT 1');
  34. $stmtSpec->execute([$type]);
  35. $specs = $stmtSpec->fetch(PDO::FETCH_ASSOC) ?: [];
  36. }
  37. }
  38. $h = fn($v) => htmlspecialchars((string)($v ?? ''), ENT_QUOTES, 'UTF-8');
  39. function waterBar(float $found, float $min, float $max): string {
  40. if ($max <= 0) return '<td colspan="3" class="text-muted text-center small">N/A</td>';
  41. if ($found < $min) {
  42. return '<td class="text-center"><div class="progress"><div class="progress-bar bg-danger" style="width:100%"></div></div></td>'
  43. . '<td></td><td></td>';
  44. }
  45. if ($found > $max) {
  46. return '<td></td><td></td>'
  47. . '<td class="text-center"><div class="progress"><div class="progress-bar bg-warning" style="width:100%"></div></div></td>';
  48. }
  49. $pct = ($max > $min) ? round(min(100, ($found - $min) / ($max - $min) * 100)) : 50;
  50. return '<td></td>'
  51. . '<td class="text-center"><div class="progress"><div class="progress-bar bg-success" style="width:' . $pct . '%"></div></div></td>'
  52. . '<td></td>';
  53. }
  54. $today = date('jS F Y');
  55. $pageTitle = 'Water Analysis' . (!empty($row['client_name']) ? ' — ' . $row['client_name'] : '');
  56. $siteName = 'Crop Monitor';
  57. if (!$printMode) {
  58. include __DIR__ . '/../../../layouts/header.php';
  59. }
  60. ?>
  61. <link rel="stylesheet" href="/client-assets/home/css/graphPrint.css" media="print">
  62. <style>
  63. @media print {
  64. @page { size: A4 portrait; }
  65. body { min-width: 992px !important; }
  66. .container { min-width: 992px !important; }
  67. }
  68. .progress { border-radius: 0 !important; }
  69. .lightgreen { background: #d4edda !important; color: #155724 !important; }
  70. .lightred { background: #f8d7da !important; color: #721c24 !important; }
  71. .lightblue { background: #cce5ff !important; color: #004085 !important; }
  72. .lightpurple { background: #e2d9f3 !important; color: #432874 !important; }
  73. .stripe-1 { background: #f8f9fa; }
  74. .border-left { border-left: 1px solid #dee2e6; }
  75. .border-right { border-right: 1px solid #dee2e6; }
  76. .border-bottom { border-bottom: 1px solid #dee2e6; }
  77. .border-top { border-top: 1px solid #dee2e6; }
  78. table.chart { width: 100%; border-collapse: collapse; }
  79. table.chart th, table.chart td { padding: 3px 6px; font-size: 0.85rem; }
  80. .chart-header th { background: #343a40; color: white; }
  81. .chart-header-sub th { background: #6c757d; color: white; }
  82. </style>
  83. <div class="container" id="content">
  84. <?php if (!$row): ?>
  85. <div class="alert alert-danger mt-4">Record not found or access denied.</div>
  86. <?php else: ?>
  87. <!-- ── Header ──────────────────────────────────────────────────────────── -->
  88. <div class="row mb-2 mt-3">
  89. <div class="col-md-3">
  90. <img class="img-fluid" src="/client-assets/images/crop-monitor.png" alt="Crop Monitor">
  91. </div>
  92. </div>
  93. <table class="title w-100 mb-3 small">
  94. <tbody>
  95. <tr>
  96. <td class="text-end fw-bold text-nowrap">DATE:</td>
  97. <td><?= $h($today) ?></td>
  98. <td></td>
  99. <td class="text-end fw-bold text-nowrap">SAMPLE ID:</td>
  100. <td><?= $h($row['sample_id']) ?></td>
  101. </tr>
  102. <tr>
  103. <td class="text-end fw-bold text-nowrap">CLIENT:</td>
  104. <td><?= $h($row['client_name']) ?></td>
  105. <td></td>
  106. <td class="text-end fw-bold text-nowrap">DATE SAMPLED:</td>
  107. <td><?= $h($row['date_sampled']) ?></td>
  108. </tr>
  109. <tr>
  110. <td class="text-end fw-bold text-nowrap">SITE ID:</td>
  111. <td><?= $h($row['site_id']) ?></td>
  112. <td></td>
  113. <td class="text-end fw-bold text-nowrap">LAB NUMBER:</td>
  114. <td><?= $h($row['lab_no']) ?></td>
  115. </tr>
  116. <tr>
  117. <td class="text-end fw-bold text-nowrap">ANALYSIS TYPE:</td>
  118. <td><?= $h($row['analysis_type']) ?></td>
  119. <td></td>
  120. <td></td>
  121. <td></td>
  122. </tr>
  123. </tbody>
  124. </table>
  125. <!-- ── Action buttons ──────────────────────────────────────────────────── -->
  126. <?php if (!$printMode): ?>
  127. <div class="d-print-none mb-3 d-flex gap-2 flex-wrap">
  128. <a href="/dashboard/crop-analysis/water-test-data/water-report.php?rid=<?= $recordId ?>&rand=<?= urlencode($randId) ?>&cid=<?= $clientId ?>"
  129. class="btn btn-outline-primary btn-sm">
  130. <i class="fas fa-file-alt me-1"></i>View Report
  131. </a>
  132. <a href="/pdf-files/headlessChrome_pdf.php?type=water-analysis&rid=<?= $recordId ?>&rand=<?= urlencode($randId) ?>&cid=<?= $clientId ?>"
  133. class="btn btn-outline-secondary btn-sm">
  134. <i class="fas fa-file-pdf me-1"></i>PDF — Analysis
  135. </a>
  136. <a href="/pdf-files/headlessChrome_pdf.php?type=water&rid=<?= $recordId ?>&rand=<?= urlencode($randId) ?>&cid=<?= $clientId ?>"
  137. class="btn btn-success btn-sm">
  138. <i class="fas fa-file-pdf me-1"></i>PDF — Analysis &amp; Report
  139. </a>
  140. </div>
  141. <?php endif; ?>
  142. <div class="row">
  143. <div class="col-md-12 text-center fw-bold h4">Water Quality Analysis Results</div>
  144. </div>
  145. <table class="chart">
  146. <tbody>
  147. <tr class="chart-header">
  148. <th colspan="3" class="text-center border-left border-right border-top">ELEMENT</th>
  149. <th colspan="3" class="text-center border-right border-top">STATUS</th>
  150. </tr>
  151. <tr class="chart-header-sub">
  152. <th class="text-center border-left">Parameter</th>
  153. <th class="text-center">Desired</th>
  154. <th class="text-center">Found</th>
  155. <th class="text-center stripe-1">Deficit</th>
  156. <th class="text-center stripe-1">Ideal</th>
  157. <th class="text-center border-right stripe-1">High</th>
  158. </tr>
  159. <tr>
  160. <td class="border-left"></td><td></td><td></td>
  161. <td></td><td></td><td class="border-right"></td>
  162. </tr>
  163. <!-- General Parameters -->
  164. <tr class="chart-header-sub">
  165. <th colspan="6" class="border-left text-center lightblue">GENERAL PARAMETERS</th>
  166. </tr>
  167. <?php
  168. $generalParams = [
  169. ['ph', 'pH', '', 3],
  170. ['cond_dsm', 'Conductivity', 'dS/m', 3],
  171. ['hco3-', 'Bicarbonate (HCO₃)', 'ppm', 2],
  172. ];
  173. foreach ($generalParams as [$col, $label, $unit, $dp]):
  174. $found = (float)($row[$col] ?? 0);
  175. $min = (float)($specs[$col . '_min'] ?? 0);
  176. $max = (float)($specs[$col . '_max'] ?? 0);
  177. $desired = ($min > 0 || $max > 0) ? number_format($min, $dp) . '–' . number_format($max, $dp) : '—';
  178. $display = $unit ? $label . ' (' . $unit . ')' : $label;
  179. ?>
  180. <tr>
  181. <td class="border-left"><?= $h($display) ?></td>
  182. <td class="text-center"><?= $h($desired) ?></td>
  183. <td class="text-center"><?= $found > 0 ? number_format($found, $dp) : '—' ?></td>
  184. <?= waterBar($found, $min, $max) ?>
  185. </tr>
  186. <?php endforeach; ?>
  187. <tr><td colspan="6" class="border-left"></td></tr>
  188. <!-- Major Elements -->
  189. <tr class="chart-header-sub">
  190. <th colspan="6" class="border-left text-center lightgreen">MAJOR ELEMENTS (ppm)</th>
  191. </tr>
  192. <?php
  193. $majorElements = [
  194. ['nh4', 'Ammonium Nitrogen (NH₄)', 'ppm', 2],
  195. ['no3', 'Nitrate Nitrogen (NO₃)', 'ppm', 2],
  196. ['p', 'Phosphorus', 'ppm', 2],
  197. ['k', 'Potassium', 'ppm', 1],
  198. ['s', 'Sulphur', 'ppm', 2],
  199. ['ca', 'Calcium', 'ppm', 1],
  200. ['mg', 'Magnesium', 'ppm', 1],
  201. ['na', 'Sodium', 'ppm', 1],
  202. ];
  203. foreach ($majorElements as [$col, $label, $unit, $dp]):
  204. $found = (float)($row[$col] ?? 0);
  205. $min = (float)($specs[$col . '_min'] ?? 0);
  206. $max = (float)($specs[$col . '_max'] ?? 0);
  207. $desired = ($min > 0 || $max > 0) ? number_format($min, $dp) . '–' . number_format($max, $dp) : '—';
  208. ?>
  209. <tr>
  210. <td class="border-left"><?= $h($label) ?> (<?= $h($unit) ?>)</td>
  211. <td class="text-center"><?= $h($desired) ?></td>
  212. <td class="text-center"><?= $found > 0 ? number_format($found, $dp) : '—' ?></td>
  213. <?= waterBar($found, $min, $max) ?>
  214. </tr>
  215. <?php endforeach; ?>
  216. <tr><td colspan="6" class="border-left"></td></tr>
  217. <!-- Trace Elements -->
  218. <tr class="chart-header-sub">
  219. <th colspan="6" class="border-left text-center lightred">TRACE ELEMENTS (ppm)</th>
  220. </tr>
  221. <?php
  222. $traceElements = [
  223. ['fe', 'Iron', 'ppm', 3],
  224. ['mn', 'Manganese', 'ppm', 3],
  225. ['zn', 'Zinc', 'ppm', 3],
  226. ['cu', 'Copper', 'ppm', 3],
  227. ['b', 'Boron', 'ppm', 3],
  228. ['m', 'Molybdenum', 'ppm', 3],
  229. ['co', 'Cobalt', 'ppm', 3],
  230. ];
  231. foreach ($traceElements as [$col, $label, $unit, $dp]):
  232. $found = (float)($row[$col] ?? 0);
  233. $min = (float)($specs[$col . '_min'] ?? 0);
  234. $max = (float)($specs[$col . '_max'] ?? 0);
  235. $desired = ($min > 0 || $max > 0) ? number_format($min, $dp) . '–' . number_format($max, $dp) : '—';
  236. ?>
  237. <tr>
  238. <td class="border-left"><?= $h($label) ?> (<?= $h($unit) ?>)</td>
  239. <td class="text-center"><?= $h($desired) ?></td>
  240. <td class="text-center"><?= $found > 0 ? number_format($found, $dp) : '—' ?></td>
  241. <?= waterBar($found, $min, $max) ?>
  242. </tr>
  243. <?php endforeach; ?>
  244. <tr>
  245. <td class="border-bottom border-left"></td>
  246. <td class="border-bottom"></td>
  247. <td class="border-bottom"></td>
  248. <td class="border-bottom"></td>
  249. <td class="border-bottom"></td>
  250. <td class="border-bottom border-right"></td>
  251. </tr>
  252. </tbody>
  253. </table>
  254. <div class="mt-4 small text-muted">
  255. <p><i class="fa fa-tint" style="color:#007bff"></i> Water analysis results should be interpreted in the context of intended use (irrigation, drinking, stock water).</p>
  256. <p><i class="fa fa-tint" style="color:#007bff"></i> Talk to your qualified consultant to make a plan for correction or maintenance of the found parameter levels.</p>
  257. <p class="fst-italic" style="font-size:9px;">Any recommendations provided by Crop Monitor are advice only. We are not paid consultants and accept no responsibility for any of our suggestions.</p>
  258. </div>
  259. <?php endif; ?>
  260. </div><!-- /.container -->