| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255 |
- <?php
- /**
- * dashboard/crop-analysis/soil-test-data/soil-recommendations.php
- *
- * Soil Specifications / Recommendations page.
- * Displays and allows inline editing of soil_specifications reference ranges
- * for the current user.
- */
- require_once __DIR__ . '/../../../config/database.php';
- require_once __DIR__ . '/../../../lib/auth.php';
- require_once __DIR__ . '/../../../lib/csrf.php';
- requireLogin();
- // URL params retained for back-link context
- $record_id = (int)($_GET['rid'] ?? 0);
- $rand_id = $_GET['rand'] ?? '';
- $client_id = (int)($_GET['cid'] ?? 0);
- // Columns not shown in the editable table
- $hiddenCols = ['id', 'modx_user_id', 'crop', 'tec', 'paramag', 'texture', 'gravel', 'colour'];
- try {
- $pdo = getDBConnection();
- $userId = getCurrentUserId();
- $stmt = $pdo->prepare('SELECT * FROM soil_specifications WHERE modx_user_id = ?');
- $stmt->execute([$userId]);
- $specs = $stmt->fetchAll(PDO::FETCH_ASSOC);
- $allColumns = $specs ? array_keys($specs[0]) : [];
- $visibleColumns = array_values(array_diff($allColumns, $hiddenCols));
- } catch (PDOException $e) {
- error_log('Database error in soil-recommendations.php: ' . $e->getMessage());
- die('Database error occurred');
- }
- $pageTitle = 'Soil Recommendations';
- $siteName = 'Crop Monitor';
- $csrfToken = generateCsrfToken();
- include __DIR__ . '/../../../layouts/header.php';
- include __DIR__ . '/../../../layouts/navbar.php';
- include __DIR__ . '/../../../layouts/sidebar.php';
- ?>
- <div class="container-fluid px-4" id="content">
- <div class="d-flex align-items-center justify-content-between mt-4 mb-3">
- <h1 class="h3 mb-0">Soil Recommendations</h1>
- <?php if ($record_id && $rand_id): ?>
- <a href="/dashboard/crop-analysis/soil-test-data/soil-analysis.php?rid=<?= $record_id ?>&rand=<?= urlencode($rand_id) ?>&cid=<?= $client_id ?>"
- class="btn btn-outline-secondary btn-sm">
- ← Back to Analysis
- </a>
- <?php endif; ?>
- </div>
- <p class="text-muted mb-3">
- Variables used in Soil Analysis recommendation programs.
- Click any value in the table to edit it — changes save automatically.
- </p>
- <?php if (empty($specs)): ?>
- <div class="alert alert-info">No soil specification records found for your account.</div>
- <?php else: ?>
- <div class="card mb-4">
- <div class="card-header d-flex justify-content-between align-items-center">
- <span>Specification Ranges</span>
- <button type="button" class="btn btn-primary btn-sm"
- data-bs-toggle="modal" data-bs-target="#addProductModal">
- + Add Soil Recommendation
- </button>
- </div>
- <div class="card-body p-0">
- <div class="table-responsive">
- <table class="table table-sm table-striped table-hover table-bordered mb-0">
- <thead class="table-dark">
- <tr>
- <?php foreach ($visibleColumns as $col): ?>
- <th class="text-center text-capitalize text-nowrap px-2">
- <?= htmlspecialchars(str_replace('_', ' ', $col), ENT_QUOTES, 'UTF-8') ?>
- </th>
- <?php endforeach; ?>
- </tr>
- </thead>
- <tbody>
- <?php foreach ($specs as $spec): ?>
- <tr>
- <?php foreach ($visibleColumns as $col):
- $raw = $spec[$col] ?? '';
- if ($raw === '' || $raw === 'N/A') {
- $display = '0.0';
- } elseif (is_numeric($raw)) {
- $display = number_format((float)$raw, 2, '.', '');
- } else {
- $display = $raw;
- }
- ?>
- <td class="text-center spec-cell px-2"
- contenteditable="true"
- data-id="<?= (int)$spec['id'] ?>"
- data-col="<?= htmlspecialchars($col, ENT_QUOTES, 'UTF-8') ?>">
- <?= htmlspecialchars($display, ENT_QUOTES, 'UTF-8') ?>
- </td>
- <?php endforeach; ?>
- </tr>
- <?php endforeach; ?>
- </tbody>
- </table>
- </div>
- </div>
- </div>
- <div id="save-status" class="text-muted small mb-3" style="min-height:1.25rem;"></div>
- <?php endif; ?>
- </div><!-- /.container-fluid -->
- <!-- ── Add Product Modal ─────────────────────────────────────────────────── -->
- <div class="modal fade" id="addProductModal" tabindex="-1"
- aria-labelledby="addProductModalLabel" aria-hidden="true">
- <div class="modal-dialog">
- <div class="modal-content">
- <div class="modal-header">
- <h5 class="modal-title" id="addProductModalLabel">Add New Product</h5>
- <button type="button" class="btn-close"
- data-bs-dismiss="modal" aria-label="Close"></button>
- </div>
- <form method="post" action="/controllers/newProductSubmit.php">
- <input type="hidden" name="csrf_token"
- value="<?= htmlspecialchars($csrfToken, ENT_QUOTES, 'UTF-8') ?>">
- <div class="modal-body">
- <div class="row g-2">
- <div class="col-md-8">
- <label class="form-label" for="name">Product Name</label>
- <input type="text" class="form-control" name="name"
- id="name" placeholder="Product Name" required>
- </div>
- <div class="col-md-4">
- <label class="form-label" for="chemical">Chemical Symbol</label>
- <input type="text" class="form-control" name="chemical"
- id="chemical" placeholder="e.g. CaSO4">
- </div>
- </div>
- <hr class="my-3">
- <p class="text-muted small mb-2">Nutrient composition (%)</p>
- <div class="row g-2">
- <?php
- $nutrients = [
- 'N' => 'Nitrogen',
- 'P' => 'Phosphorus',
- 'K' => 'Potassium',
- 'Na' => 'Sodium',
- 'Ca' => 'Calcium',
- 'Mg' => 'Magnesium',
- 'B' => 'Boron',
- 'Zn' => 'Zinc',
- 'Cu' => 'Copper',
- 'Mn' => 'Manganese',
- 'Fe' => 'Iron',
- 'Co' => 'Cobalt',
- 'Mo' => 'Molybdenum',
- ];
- foreach ($nutrients as $key => $label):
- ?>
- <div class="col-6 col-md-4">
- <label class="form-label small" for="field_<?= $key ?>">
- <?= htmlspecialchars($label, ENT_QUOTES, 'UTF-8') ?> — <?= htmlspecialchars($key, ENT_QUOTES, 'UTF-8') ?>
- </label>
- <input type="number" step="any" min="0"
- class="form-control form-control-sm"
- name="<?= htmlspecialchars($key, ENT_QUOTES, 'UTF-8') ?>"
- id="field_<?= $key ?>"
- placeholder="0">
- </div>
- <?php endforeach; ?>
- </div>
- </div>
- <div class="modal-footer">
- <button type="button" class="btn btn-secondary"
- data-bs-dismiss="modal">Close</button>
- <button type="submit" class="btn btn-primary">Save Product</button>
- </div>
- </form>
- </div>
- </div>
- </div>
- <script>
- (function () {
- const CSRF = <?= json_encode($csrfToken) ?>;
- const status = document.getElementById('save-status');
- let timer = null;
- let active = null;
- function setStatus(msg, cls) {
- status.textContent = msg;
- status.className = 'text-' + cls + ' small mb-3';
- }
- document.querySelectorAll('.spec-cell').forEach(function (cell) {
- cell.addEventListener('focus', function () {
- this.style.background = '#d1f0d1';
- });
- cell.addEventListener('blur', function () {
- this.style.background = '';
- const el = this;
- const id = el.dataset.id;
- const col = el.dataset.col;
- const val = el.textContent.trim();
- clearTimeout(timer);
- timer = setTimeout(function () {
- active = el;
- setStatus('Saving…', 'secondary');
- fetch('/controllers/updateSoilSpecification.php', {
- method: 'POST',
- headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
- body: new URLSearchParams({
- csrf_token: CSRF,
- spec_id: id,
- column: col,
- value: val,
- }),
- })
- .then(function (r) { return r.json(); })
- .then(function (data) {
- if (data.success) {
- var d = new Date();
- setStatus('Saved — ' + d.toLocaleTimeString(), 'success');
- } else {
- setStatus('Error: ' + (data.error || 'save failed'), 'danger');
- }
- })
- .catch(function () {
- setStatus('Network error — change not saved', 'danger');
- });
- }, 600);
- });
- });
- })();
- </script>
- <?php include __DIR__ . '/../../../layouts/footer.php'; ?>
|