soil-analysis.php 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517
  1. <?php
  2. /**
  3. * dashboard/crop-analysis/soil-test-data/soil-analysis.php
  4. *
  5. * Soil Analysis Results Display Page
  6. */
  7. require_once __DIR__.'/../../../config/database.php';
  8. require_once __DIR__.'/../../../lib/auth.php';
  9. require_once __DIR__.'/../../../lib/validation.php';
  10. require_once __DIR__.'/../../../lib/soil_calculations.php';
  11. if (session_status() === PHP_SESSION_NONE) {
  12. session_start();
  13. }
  14. requireLogin();
  15. $record_id = (float)($_GET['rid'] ?? 0);
  16. $rand_id = (float)($_GET['rand'] ?? 0);
  17. $client_id = (int) ($_GET['cid'] ?? 0);
  18. if (!$record_id || !$rand_id) {
  19. die('Invalid request parameters');
  20. }
  21. try {
  22. $pdo = getDBConnection();
  23. // Load soil record
  24. $stmt = $pdo->prepare("SELECT * FROM soil_records WHERE id = ? AND rand = ?");
  25. $stmt->execute([$record_id, $rand_id]);
  26. $row = $stmt->fetch(PDO::FETCH_ASSOC);
  27. if (!$row) {
  28. die('Soil record not found');
  29. }
  30. // Load matching specifications row for this soil type
  31. $spec = [];
  32. if (!empty($row['soil_type'])) {
  33. $stmtSpec = $pdo->prepare("SELECT * FROM soil_specifications WHERE soil_type = ? LIMIT 1");
  34. $stmtSpec->execute([$row['soil_type']]);
  35. $spec = $stmtSpec->fetch(PDO::FETCH_ASSOC) ?: [];
  36. }
  37. } catch (PDOException $e) {
  38. error_log("Database error in soil-analysis.php: " . $e->getMessage());
  39. die('Database error occurred');
  40. }
  41. // ── Helper: render one analysis row ───────────────────────────────────────
  42. /**
  43. * Renders a <tr> for a single nutrient with three progress-bar columns.
  44. *
  45. * Parameters ($p keys):
  46. * element string Column name in soil_records
  47. * sbl string Chemical symbol (may contain HTML, e.g. "NO<sub>3</sub>-N")
  48. * nutrient string Display name (may contain HTML)
  49. * min string Column in soil_records, numeric literal, or '' for no bar
  50. * max string Column in soil_records, 'soil_type' (special), numeric literal, or ''
  51. * type string Unit label (ppm, %, mS/cm …)
  52. * text string Value cell alignment: c|r|l
  53. * rec_text string Recommended cell alignment: c|r|l
  54. * recV string Recommended display: 'n'=none, 'ph'='6.4', 'max'=max only, else=min–max
  55. * decimal int Decimal places for value
  56. * graph string CSS class for progress-bar colour
  57. */
  58. function soilRow(array $row, array $spec, array $p): void
  59. {
  60. $element = $p['element'] ?? '';
  61. $sbl = $p['sbl'] ?? '';
  62. $nutrient = $p['nutrient'] ?? '';
  63. $minParam = $p['min'] ?? '';
  64. $maxParam = $p['max'] ?? '';
  65. $type = $p['type'] ?? '';
  66. $text = $p['text'] ?? 'c';
  67. $recText = $p['rec_text'] ?? 'c';
  68. $recV = $p['recV'] ?? '';
  69. $decimal = (int)($p['decimal'] ?? 2);
  70. $graph = $p['graph'] ?? '';
  71. $label = ($sbl !== '') ? $sbl . ' - ' . $nutrient : $nutrient;
  72. $rawVal = $row[$element] ?? null;
  73. $value = ($rawVal !== null && $rawVal !== '') ? (float)$rawVal : 0.0;
  74. $valueFmt = number_format($value, $decimal, '.', '') . ($type !== '' ? ' ' . $type : '');
  75. // Resolve min
  76. $min = 0.0;
  77. if ($minParam !== '') {
  78. if (is_numeric($minParam)) {
  79. $min = (float)$minParam;
  80. } elseif (isset($row[$minParam]) && $row[$minParam] !== '') {
  81. $min = (float)$row[$minParam];
  82. } elseif (isset($spec[$minParam]) && $spec[$minParam] !== '') {
  83. $min = (float)$spec[$minParam];
  84. }
  85. } elseif (isset($spec[$element]) && $spec[$element] !== '') {
  86. $min = (float)$spec[$element] / 2; // fallback: half the spec average
  87. }
  88. // Resolve max + recommended display label
  89. $max = 0.0;
  90. $maxLabel = '';
  91. if ($maxParam === 'soil_type') {
  92. $st = strtolower($row['soil_type'] ?? '');
  93. $maxLabel = match($st) {
  94. 'light' => 'Light Soil',
  95. 'medium' => 'Medium Soil',
  96. 'heavy' => 'Heavy Soil',
  97. default => htmlspecialchars($row['soil_type'] ?? '', ENT_QUOTES, 'UTF-8'),
  98. };
  99. } elseif ($maxParam !== '') {
  100. if (is_numeric($maxParam)) {
  101. $max = (float)$maxParam;
  102. } elseif (isset($row[$maxParam]) && $row[$maxParam] !== '') {
  103. $max = (float)$row[$maxParam];
  104. } elseif (isset($spec[$maxParam]) && $spec[$maxParam] !== '') {
  105. $max = (float)$spec[$maxParam];
  106. }
  107. } elseif (isset($spec[$element]) && $spec[$element] !== '') {
  108. $max = (float)$spec[$element] * 2; // fallback: double the spec average
  109. }
  110. // Recommended cell text
  111. $measurement = ($type !== '') ? ' ' . $type : '';
  112. if ($maxParam === 'soil_type') {
  113. $recommended = $maxLabel;
  114. } elseif ($recV === 'n') {
  115. $recommended = '';
  116. } elseif ($recV === 'ph') {
  117. $recommended = '6.4';
  118. } elseif ($recV === 'max') {
  119. $recommended = number_format($max, $decimal, '.', '') . $measurement;
  120. } else {
  121. $recommended = number_format($min, $decimal, '.', '') . ' - ' . number_format($max, $decimal, '.', '') . $measurement;
  122. }
  123. $alignVal = match($text) { 'r' => 'text-right', 'l' => 'text-left', default => 'text-center' };
  124. $alignRec = match($recText) { 'r' => 'text-right', 'l' => 'text-left', default => 'text-center' };
  125. // Bar calculations (replicates original int-cast logic)
  126. $c_min = $min - ($max - $min);
  127. $c_max = $max + ($max - $min);
  128. $hasValue = ($rawVal !== null && $rawVal !== '' && $rawVal !== '0');
  129. // First bar (deficit zone: c_min → min)
  130. if (!$hasValue || (int)($c_min - $min) == 0) {
  131. $fb = 0;
  132. } else {
  133. $fb = (int)($c_min - $value) / (int)($c_min - $min) * 100;
  134. }
  135. $fb = !$hasValue ? 0 : ($fb > 100 ? 100 : ($fb < 0 ? 2 : $fb));
  136. // Second bar (ideal zone: min → max)
  137. if (!$hasValue || (int)($min - $max) == 0) {
  138. $sb = 0;
  139. } else {
  140. $sb = (int)($min - $value) / (int)($min - $max) * 100;
  141. }
  142. $sbp = ($fb < 100) ? 0 : ($sb < 0 ? 0 : ($sb > 101 ? 100 : $sb));
  143. // Third bar (excess zone: max → c_max)
  144. if (!$hasValue || (int)($max - $c_max) == 0) {
  145. $tb = 0;
  146. } else {
  147. $tb = (int)($max - $value) / (int)($max - $c_max) * 100;
  148. }
  149. $tbp = ($sb < 100) ? 0 : ($tb < 0 ? 0 : ($tb > 101 ? 100 : $tb));
  150. echo "<tr class='sub-chart'>\n";
  151. echo " <td class='text-left border-left text-capitalize pl-2'>{$label}</td>\n";
  152. echo " <td class='{$alignRec} border-left px-3'>{$recommended}</td>\n";
  153. echo " <td class='{$alignVal} border-left nutrient-balance px-3'>{$valueFmt}</td>\n";
  154. echo " <td class='text-center border-left graph-border'><div class='progress'><div class='progress-bar {$graph}' style='width:{$fb}%'></div></div></td>\n";
  155. echo " <td class='text-center border-left graph-border'><div class='progress'><div class='progress-bar {$graph}' style='width:{$sbp}%'></div></div></td>\n";
  156. echo " <td class='text-center border-left border-right graph-border'><div class='progress'><div class='progress-bar {$graph}' style='width:{$tbp}%'></div></div></td>\n";
  157. echo "</tr>\n";
  158. }
  159. // ── Helper: render a ratio row ─────────────────────────────────────────────
  160. /**
  161. * Renders a <tr> for a calculated ratio (element ÷ elementTwo).
  162. *
  163. * Parameters ($p keys):
  164. * element string Numerator column in soil_records
  165. * elementTwo string Denominator column in soil_records
  166. * sbl string Chemical symbol
  167. * nutrient string Display name
  168. * rec string Column in soil_specifications for the recommended ratio
  169. * type string Unit suffix (e.g. ':1')
  170. * rec_text string Recommended alignment: c|r|l
  171. * decimal int Decimal places
  172. * graph string CSS class for progress-bar colour
  173. */
  174. function soilRatio(array $row, array $spec, array $p): void
  175. {
  176. $element = $p['element'] ?? '';
  177. $element2 = $p['elementTwo'] ?? '';
  178. $sbl = $p['sbl'] ?? '';
  179. $nutrient = $p['nutrient'] ?? '';
  180. $rec = $p['rec'] ?? '';
  181. $type = $p['type'] ?? '';
  182. $recText = $p['rec_text'] ?? 'c';
  183. $decimal = (int)($p['decimal'] ?? 1);
  184. $graph = $p['graph'] ?? '';
  185. $label = ($sbl !== '') ? $sbl . ' - ' . $nutrient : $nutrient;
  186. $val1 = isset($row[$element]) && $row[$element] !== '' ? (float)$row[$element] : 0.0;
  187. $val2 = isset($row[$element2]) && $row[$element2] !== '' ? (float)$row[$element2] : 0.0;
  188. $ratio = ($val2 != 0) ? $val1 / $val2 : 0.0;
  189. $valueFmt = number_format($ratio, $decimal, '.', '') . ($type !== '' ? ' ' . $type : '');
  190. $recommended = (isset($spec[$rec]) && $spec[$rec] !== '') ? htmlspecialchars($spec[$rec], ENT_QUOTES, 'UTF-8') : '';
  191. $alignRec = match($recText) { 'r' => 'text-right', 'l' => 'text-left', default => 'text-center' };
  192. echo "<tr class='sub-chart'>\n";
  193. echo " <td class='text-left border-left text-capitalize pl-2'>{$label}</td>\n";
  194. echo " <td class='{$alignRec} border-left px-3'>{$recommended}</td>\n";
  195. echo " <td class='text-center border-left nutrient-balance px-3'>{$valueFmt}</td>\n";
  196. echo " <td class='text-center border-left graph-border'><div class='progress'><div class='progress-bar {$graph}' style='width:0%'></div></div></td>\n";
  197. echo " <td class='text-center border-left graph-border'><div class='progress'><div class='progress-bar {$graph}' style='width:0%'></div></div></td>\n";
  198. echo " <td class='text-center border-left border-right graph-border'><div class='progress'><div class='progress-bar {$graph}' style='width:0%'></div></div></td>\n";
  199. echo "</tr>\n";
  200. }
  201. // ── Page setup ─────────────────────────────────────────────────────────────
  202. $client = htmlspecialchars($row['client_name'] ?? '', ENT_QUOTES, 'UTF-8');
  203. $address = htmlspecialchars($row['site_address'] ?? '', ENT_QUOTES, 'UTF-8');
  204. $state = htmlspecialchars($row['state_postcode'] ?? '', ENT_QUOTES, 'UTF-8');
  205. $email = htmlspecialchars($row['email'] ?? '', ENT_QUOTES, 'UTF-8');
  206. $labNo = htmlspecialchars($row['lab_no'] ?? '', ENT_QUOTES, 'UTF-8');
  207. $sampleDate = htmlspecialchars($row['date_sampled'] ?? '', ENT_QUOTES, 'UTF-8');
  208. $sample = htmlspecialchars($row['site_id'] ?? '', ENT_QUOTES, 'UTF-8');
  209. $crop = htmlspecialchars($row['sample_id'] ?? '', ENT_QUOTES, 'UTF-8');
  210. $today = date('jS F Y');
  211. $pageTitle = 'Soil Analysis Results' . ($client !== '' ? ' - ' . $client : '');
  212. include __DIR__.'/../../../layouts/header.php';
  213. ?>
  214. <link rel="stylesheet" href="client-assets/home/css/graphPrint.css" media="print">
  215. <style>
  216. .progress { border-radius: 0 !important; }
  217. </style>
  218. <div class="container" id="content">
  219. <div class="row mb-2">
  220. <div class="col-md-3">
  221. <img class="img-fluid" src="client-assets/images/crop-monitor.png" alt="Crop Monitor">
  222. </div>
  223. </div>
  224. <table class="title">
  225. <tbody>
  226. <tr>
  227. <th class="col-20"></th><th class="col-20"></th><th class="col-20"></th>
  228. <th class="col-20"></th><th class="col-20"></th>
  229. </tr>
  230. <tr>
  231. <td class="right"><b>DATE:</b></td>
  232. <td class="left"><?= $today ?></td>
  233. <td></td>
  234. <td class="right"><b>SAMPLE ID:</b></td>
  235. <td class="left"><?= $sample ?></td>
  236. </tr>
  237. <tr>
  238. <td class="right"><b>CLIENT:</b></td>
  239. <td class="left"><?= $client ?></td>
  240. <td></td>
  241. <td class="right"><b>DATE SAMPLED:</b></td>
  242. <td class="left"><?= $sampleDate ?></td>
  243. </tr>
  244. <tr>
  245. <td class="right"><b>ADDRESS:</b></td>
  246. <td class="left"><?= $address ?></td>
  247. <td></td>
  248. <td class="right"><b>LAB NUMBER:</b></td>
  249. <td class="left"><?= $labNo ?></td>
  250. </tr>
  251. <tr>
  252. <td class="right"></td>
  253. <td class="left"><?= $state ?></td>
  254. <td></td>
  255. <td class="right"><b>CROP:</b></td>
  256. <td class="left"><?= $crop ?></td>
  257. </tr>
  258. <tr>
  259. <td class="right"></td>
  260. <td class="left"><?= $email ?></td>
  261. <td></td><td></td><td></td>
  262. </tr>
  263. </tbody>
  264. </table>
  265. <div class="d-print-none">
  266. <div class="row p-2">
  267. <div class="col">
  268. <button type="button" class="btn btn-primary" onclick="generateGraph()">
  269. <i class="fas fa-chart-bar"></i> Generate Graph
  270. </button>
  271. </div>
  272. <div class="col">
  273. <div class="form-status-holder"></div>
  274. </div>
  275. </div>
  276. </div>
  277. <div class="row">
  278. <div class="col-md-12 text-center fw-bold h4">Soil Analysis Summary</div>
  279. </div>
  280. <table class="chart">
  281. <tbody>
  282. <!-- ── CHART HEADER ──────────────────────────────────────────── -->
  283. <tr class="chart-header">
  284. <th colspan="3" class="text-center col-md-6 border-left border-right border-top">ELEMENT</th>
  285. <th colspan="3" class="text-center col-md-6 border-right border-top">STATUS</th>
  286. </tr>
  287. <tr class="chart-header-sub">
  288. <th class="text-center col-18 border-left"></th>
  289. <th class="text-center col-15">DESIRED</th>
  290. <th class="text-center col-15">FOUND</th>
  291. <th class="text-center col-16 stripe-1">LIGHT</th>
  292. <th class="text-center col-16 stripe-1">MEDIUM</th>
  293. <th class="text-center col-16 border-right stripe-1">HEAVY</th>
  294. </tr>
  295. <tr>
  296. <td class="border-left"></td>
  297. <td class="border-left"></td>
  298. <td class="border-left nutrient-balance"></td>
  299. <td class="border-left"></td>
  300. <td class="border-left"></td>
  301. <td class="border-left border-right"></td>
  302. </tr>
  303. <?php
  304. soilRow($row, $spec, ['element'=>'cec', 'nutrient'=>'CEC', 'recV'=>'n', 'decimal'=>2, 'graph'=>'lightorangeGraph']);
  305. soilRow($row, $spec, ['element'=>'tec', 'nutrient'=>'TEC', 'max'=>'soil_type', 'recV'=>'max', 'rec_text'=>'c', 'decimal'=>2, 'graph'=>'lightorangeGraph']);
  306. ?>
  307. <tr class="chart-header-sub">
  308. <th class="text-center col-18 border-left text-dark bg-white"></th>
  309. <th class="text-center col-15 border-left text-dark bg-white"></th>
  310. <th class="text-center col-15 border-left nutrient-balance"></th>
  311. <th class="text-center col-16 border-left stripe-1">DEFICIT</th>
  312. <th class="text-center col-16 stripe-1">IDEAL</th>
  313. <th class="text-center col-16 border-right stripe-1">HIGH</th>
  314. </tr>
  315. <?php
  316. soilRow($row, $spec, ['element'=>'ph_h2o', 'nutrient'=>'pH-level (H20)', 'type'=>'pH', 'recV'=>'ph', 'decimal'=>1, 'graph'=>'lightorangeGraph']);
  317. soilRow($row, $spec, ['element'=>'ph_cacl2', 'nutrient'=>'pH-level (CaCl2)', 'type'=>'pH', 'recV'=>'n', 'decimal'=>1, 'graph'=>'lightorangeGraph']);
  318. soilRow($row, $spec, ['element'=>'ec', 'nutrient'=>'Conductivity (EC)','type'=>'mS/cm', 'decimal'=>2, 'graph'=>'lightorangeGraph']);
  319. soilRow($row, $spec, ['element'=>'ocarbon', 'nutrient'=>'Organic Carbon', 'type'=>'%', 'decimal'=>1, 'graph'=>'lightorangeGraph']);
  320. soilRow($row, $spec, ['element'=>'omatter', 'nutrient'=>'Organic Matter', 'type'=>'%', 'decimal'=>1, 'graph'=>'lightorangeGraph']);
  321. ?>
  322. <!-- ── MAJOR ELEMENTS ─────────────────────────────────────────── -->
  323. <tr class="chart-header-sub">
  324. <th colspan="3" class="col-16 border-left text-center lightgreen">MAJOR ELEMENTS</th>
  325. <th class="text-center col-16 stripe-1"></th>
  326. <th class="text-center col-16 stripe-1"></th>
  327. <th class="text-center col-16 border-right stripe-1"></th>
  328. </tr>
  329. <?php
  330. soilRow($row, $spec, ['element'=>'NO3_N', 'sbl'=>'NO<sub>3</sub>-N', 'nutrient'=>'Nitrate <small class="d-print-none">Nitrogen</small>', 'min'=>'10', 'max'=>'20', 'type'=>'ppm', 'text'=>'c', 'rec_text'=>'r', 'decimal'=>0, 'graph'=>'lightgreenGraph']);
  331. soilRow($row, $spec, ['element'=>'NH3_N', 'sbl'=>'NH<sub>3</sub>-N', 'nutrient'=>'Ammonium <small class="d-print-none">Nitrogen</small>', 'type'=>'ppm', 'text'=>'c', 'rec_text'=>'r', 'decimal'=>0, 'graph'=>'lightgreenGraph']);
  332. // p_mehlick, p_bray2, p_morgan excluded (commented in original)
  333. soilRow($row, $spec, ['element'=>'p_colwell', 'sbl'=>'P', 'nutrient'=>'Phosphate <small>(colwell)</small>', 'type'=>'ppm', 'text'=>'c', 'rec_text'=>'r', 'decimal'=>0, 'graph'=>'lightgreenGraph']);
  334. soilRow($row, $spec, ['element'=>'BS_ca_ppm', 'sbl'=>'Ca', 'nutrient'=>'Calcium', 'min'=>'ca_ppm_min', 'max'=>'ca_ppm_max', 'type'=>'ppm', 'text'=>'c', 'rec_text'=>'r', 'recV'=>'max', 'decimal'=>0, 'graph'=>'lightgreenGraph']);
  335. soilRow($row, $spec, ['element'=>'BS_mg_ppm', 'sbl'=>'Mg', 'nutrient'=>'Magnesium', 'min'=>'mg_ppm_min', 'max'=>'mg_ppm_max', 'type'=>'ppm', 'text'=>'c', 'rec_text'=>'r', 'recV'=>'max', 'decimal'=>0, 'graph'=>'lightgreenGraph']);
  336. soilRow($row, $spec, ['element'=>'BS_k_ppm', 'sbl'=>'K', 'nutrient'=>'Potassium', 'min'=>'k_ppm_min', 'max'=>'k_ppm_max', 'type'=>'ppm', 'text'=>'c', 'rec_text'=>'r', 'recV'=>'max', 'decimal'=>0, 'graph'=>'lightgreenGraph']);
  337. soilRow($row, $spec, ['element'=>'BS_na_ppm', 'sbl'=>'Na', 'nutrient'=>'Sodium', 'min'=>'na_ppm_min', 'max'=>'na_ppm_max', 'type'=>'ppm', 'text'=>'c', 'rec_text'=>'r', 'recV'=>'max', 'decimal'=>0, 'graph'=>'lightgreenGraph']);
  338. ?>
  339. <!-- ── TRACE ELEMENTS ─────────────────────────────────────────── -->
  340. <tr class="chart-header-sub">
  341. <th colspan="3" class="col-16 border-left text-center lightred">TRACE ELEMENTS</th>
  342. <th class="text-center col-16 stripe-1"></th>
  343. <th class="text-center col-16 stripe-1"></th>
  344. <th class="text-center col-16 border-right stripe-1"></th>
  345. </tr>
  346. <?php
  347. soilRow($row, $spec, ['element'=>'s_morgan', 'sbl'=>'S', 'nutrient'=>'Sulfur', 'type'=>'ppm', 'text'=>'c', 'rec_text'=>'r', 'decimal'=>2, 'graph'=>'lightredGraph']);
  348. soilRow($row, $spec, ['element'=>'b_cacl2', 'sbl'=>'B', 'nutrient'=>'Boron', 'type'=>'ppm', 'text'=>'c', 'rec_text'=>'r', 'decimal'=>2, 'graph'=>'lightredGraph']);
  349. soilRow($row, $spec, ['element'=>'mn_dtpa', 'sbl'=>'Mn', 'nutrient'=>'Manganese', 'type'=>'ppm', 'text'=>'c', 'rec_text'=>'r', 'decimal'=>2, 'graph'=>'lightredGraph']);
  350. soilRow($row, $spec, ['element'=>'cu_dtpa', 'sbl'=>'Cu', 'nutrient'=>'Copper', 'type'=>'ppm', 'text'=>'c', 'rec_text'=>'r', 'decimal'=>2, 'graph'=>'lightredGraph']);
  351. soilRow($row, $spec, ['element'=>'zn_dtpa', 'sbl'=>'Zn', 'nutrient'=>'Zinc', 'type'=>'ppm', 'text'=>'c', 'rec_text'=>'r', 'decimal'=>2, 'graph'=>'lightredGraph']);
  352. soilRow($row, $spec, ['element'=>'fe_dtpa', 'sbl'=>'Fe', 'nutrient'=>'Iron', 'type'=>'ppm', 'text'=>'c', 'rec_text'=>'r', 'decimal'=>2, 'graph'=>'lightredGraph']);
  353. soilRow($row, $spec, ['element'=>'al', 'sbl'=>'Al', 'nutrient'=>'Aluminium', 'type'=>'ppm', 'text'=>'c', 'rec_text'=>'r', 'decimal'=>2, 'graph'=>'lightredGraph']);
  354. soilRow($row, $spec, ['element'=>'sl_cacl2', 'sbl'=>'Si', 'nutrient'=>'Silicon', 'type'=>'ppm', 'text'=>'c', 'rec_text'=>'r', 'decimal'=>2, 'graph'=>'lightredGraph']);
  355. ?>
  356. <!-- ── BASE SATURATION ────────────────────────────────────────── -->
  357. <tr class="chart-header-sub">
  358. <th colspan="3" class="col-16 border-left text-center lightpurple">BASE SATURATION</th>
  359. <th class="text-center col-16 stripe-1"></th>
  360. <th class="text-center col-16 stripe-1"></th>
  361. <th class="text-center col-16 border-right stripe-1"></th>
  362. </tr>
  363. <?php
  364. soilRow($row, $spec, ['element'=>'BS_ca2', 'sbl'=>'Ca', 'nutrient'=>'Calcium', 'min'=>'cabs_min', 'max'=>'cabs_max', 'type'=>'%', 'text'=>'c', 'rec_text'=>'r', 'recV'=>'max', 'decimal'=>2, 'graph'=>'lightpurpleGraph']);
  365. soilRow($row, $spec, ['element'=>'BS_mg2', 'sbl'=>'Mg', 'nutrient'=>'Magnesium', 'min'=>'mgbs_min', 'max'=>'mgbs_max', 'type'=>'%', 'text'=>'c', 'rec_text'=>'r', 'recV'=>'max', 'decimal'=>2, 'graph'=>'lightpurpleGraph']);
  366. soilRow($row, $spec, ['element'=>'BS_k', 'sbl'=>'K', 'nutrient'=>'Potassium', 'min'=>'kbs_min', 'max'=>'kbs_max', 'type'=>'%', 'text'=>'c', 'rec_text'=>'r', 'recV'=>'max', 'decimal'=>2, 'graph'=>'lightpurpleGraph']);
  367. soilRow($row, $spec, ['element'=>'BS_na', 'sbl'=>'Na', 'nutrient'=>'Sodium', 'min'=>'nabs_min', 'max'=>'nabs_max', 'type'=>'%', 'text'=>'c', 'rec_text'=>'r', 'recV'=>'max', 'decimal'=>2, 'graph'=>'lightpurpleGraph']);
  368. soilRow($row, $spec, ['element'=>'BS_ob', 'nutrient'=>'Other Bases', 'max'=>'ob_rec', 'type'=>'%', 'text'=>'c', 'rec_text'=>'r', 'recV'=>'max', 'decimal'=>2, 'graph'=>'lightpurpleGraph']);
  369. soilRow($row, $spec, ['element'=>'BS_h', 'nutrient'=>'Hydrogen', 'max'=>'h_rec', 'type'=>'%', 'text'=>'c', 'rec_text'=>'r', 'recV'=>'max', 'decimal'=>2, 'graph'=>'lightpurpleGraph']);
  370. ?>
  371. <!-- ── SOLUBLE MORGAN 2 EXTRACT ───────────────────────────────── -->
  372. <tr class="chart-header-sub">
  373. <th colspan="3" class="col-16 border-left text-center lightgrey">SOLUBLE MORGAN 2 EXTRACT</th>
  374. <th class="text-center col-16 stripe-1"></th>
  375. <th class="text-center col-16 stripe-1"></th>
  376. <th class="text-center col-16 border-right stripe-1"></th>
  377. </tr>
  378. <?php
  379. soilRow($row, $spec, ['element'=>'s_morgan', 'sbl'=>'Ca', 'nutrient'=>'Calcium', 'type'=>'%', 'text'=>'c', 'rec_text'=>'r', 'decimal'=>0, 'graph'=>'lightgreyGraph']);
  380. soilRow($row, $spec, ['element'=>'b_cacl2', 'sbl'=>'Mg', 'nutrient'=>'Magnesium', 'type'=>'%', 'text'=>'c', 'rec_text'=>'r', 'decimal'=>0, 'graph'=>'lightgreyGraph']);
  381. soilRow($row, $spec, ['element'=>'mn_dtpa', 'sbl'=>'K', 'nutrient'=>'Potassium', 'type'=>'%', 'text'=>'c', 'rec_text'=>'r', 'decimal'=>0, 'graph'=>'lightgreyGraph']);
  382. ?>
  383. <!-- ── ADDITIONAL DATA ────────────────────────────────────────── -->
  384. <tr class="chart-header-sub">
  385. <th colspan="3" class="col-16 border-left text-center lightgrey">ADDITIONAL DATA</th>
  386. <th class="text-center col-16 stripe-1">LOW</th>
  387. <th class="text-center col-16 stripe-1">IDEAL</th>
  388. <th class="text-center col-16 border-right stripe-1">EXCELLENT</th>
  389. </tr>
  390. <?php
  391. soilRow($row, $spec, ['element'=>'s_morgan', 'sbl'=>'Ca', 'nutrient'=>'Calcium', 'type'=>'%', 'text'=>'c', 'rec_text'=>'r', 'decimal'=>0, 'graph'=>'lightgreyGraph']);
  392. ?>
  393. <!-- ── RATIOS ─────────────────────────────────────────────────── -->
  394. <tr class="chart-header-sub">
  395. <th colspan="3" class="col-16 border-left text-center lightblue">RATIOS</th>
  396. <th class="text-center col-16 stripe-1"></th>
  397. <th class="text-center col-16 stripe-1"></th>
  398. <th class="text-center col-16 border-right stripe-1"></th>
  399. </tr>
  400. <?php
  401. soilRatio($row, $spec, ['element'=>'ca_mehlick3', 'elementTwo'=>'mg_mehlick3', 'rec'=>'ca_mg_ratio', 'nutrient'=>'Ca:Mg Ratio', 'rec_text'=>'r', 'decimal'=>1, 'graph'=>'lightblueGraph']);
  402. soilRow ($row, $spec, ['element'=>'NH3_N', 'nutrient'=>'Total Nitrogen', 'type'=>'%', 'text'=>'c', 'rec_text'=>'r', 'decimal'=>1, 'graph'=>'lightblueGraph']);
  403. soilRow ($row, $spec, ['element'=>'ocarbon', 'nutrient'=>'Total Carbon', 'type'=>'%', 'text'=>'c', 'rec_text'=>'r', 'decimal'=>1, 'graph'=>'lightblueGraph']);
  404. soilRatio($row, $spec, ['element'=>'ocarbon', 'elementTwo'=>'NO3_N', 'rec'=>'c_n_ratio', 'nutrient'=>'C:N Ratio', 'type'=>':1', 'rec_text'=>'r', 'decimal'=>1, 'graph'=>'lightblueGraph']);
  405. ?>
  406. <tr>
  407. <td class="border-bottom border-left"></td>
  408. <td class="border-bottom border-left"></td>
  409. <td class="border-bottom border-left nutrient-balance"></td>
  410. <td class="border-bottom border-left"></td>
  411. <td class="border-bottom border-left"></td>
  412. <td class="border-bottom border-left border-right"></td>
  413. </tr>
  414. </tbody>
  415. </table>
  416. <footer class="py-4 bg-light mt-auto">
  417. <div class="container-fluid px-4">
  418. <div class="d-flex align-items-center justify-content-between small">
  419. <div class="text-muted">&copy; <?= date('Y') ?> Crop Management Platform. All Rights Reserved.</div>
  420. <div>
  421. <a href="/privacy-policy">Privacy Policy</a> &middot;
  422. <a href="/terms">Terms &amp; Conditions</a>
  423. </div>
  424. </div>
  425. </div>
  426. </footer>
  427. </div><!-- /.container -->
  428. <?php include __DIR__.'/../../../layouts/footer.php'; ?>
  429. <script>
  430. function generateGraph() {
  431. alert('Graph generation functionality will be implemented here.');
  432. }
  433. $(document).ready(function () {
  434. var timeoutId;
  435. $('form textarea, form input').on('input propertychange change', function () {
  436. clearTimeout(timeoutId);
  437. timeoutId = setTimeout(saveToDB, 1000);
  438. });
  439. function saveToDB() {
  440. var form = $('.report-form');
  441. $.ajax({
  442. url: '/controllers/save_soil_analysis.php',
  443. type: 'POST',
  444. data: form.serialize(),
  445. beforeSend: function () { $('.form-status-holder').html('Saving...'); },
  446. success: function () {
  447. var d = new Date();
  448. $('.form-status-holder').html('Saved! Last: ' + d.toLocaleTimeString());
  449. }
  450. });
  451. }
  452. $('.report-form').submit(function (e) {
  453. saveToDB();
  454. e.preventDefault();
  455. });
  456. });
  457. </script>