| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153 |
- <?php
- require_once __DIR__ . '/db.php';
- function h(?string $s): string {
- return htmlspecialchars($s ?? '', ENT_QUOTES, 'UTF-8');
- }
- // Render the job table for a given audience.
- // $audience: 'ICG' or 'vendor'
- // $vendor_id: required when $audience === 'vendor'
- function render_jobs_table(string $audience, ?int $vendor_id = null): string {
- $pdo = db();
- // Filter matches the original: open jobs OR anything touched in the last 4 days.
- $where = "(j.status != 'Received') OR (j.updated_at > datetime('now', '-4 days'))";
- $params = [];
- if ($audience === 'vendor') {
- $where = "j.vendor_id = ? AND ($where)";
- $params[] = $vendor_id;
- }
- $sql = "
- SELECT j.*, v.slug AS vendor_slug, v.name AS vendor_name,
- CASE WHEN j.due_date IS NULL THEN ''
- ELSE CAST(CAST(strftime('%m', j.due_date) AS INTEGER) AS TEXT) || '-' ||
- CAST(CAST(strftime('%d', j.due_date) AS INTEGER) AS TEXT)
- END AS due_short
- FROM jobs j
- JOIN vendors v ON v.id = j.vendor_id
- WHERE $where
- ORDER BY (j.status = '') DESC,
- (j.status = 'Finished') DESC,
- (j.status = 'Shipped') DESC,
- (j.ack = 'new') DESC,
- j.due_date IS NULL,
- j.due_date ASC,
- j.job
- ";
- $stmt = $pdo->prepare($sql);
- $stmt->execute($params);
- $rows = $stmt->fetchAll();
- ob_start();
- ?>
- <table class="jobs">
- <thead>
- <tr>
- <th>Job</th>
- <th>Material</th>
- <th>Description</th>
- <th class="right">Qty</th>
- <th class="center">Due</th>
- <?php if ($audience === 'ICG'): ?><th>Vendor</th><?php endif; ?>
- <th class="center">Ack</th>
- <th class="center">Status</th>
- <th class="center">Action</th>
- </tr>
- </thead>
- <tbody>
- <?php if (!$rows): ?>
- <tr><td colspan="<?= $audience === 'ICG' ? 9 : 8 ?>" class="empty">No active jobs.</td></tr>
- <?php endif; ?>
- <?php foreach ($rows as $r):
- $editable = ($audience === 'ICG');
- $jobClass = strlen($r['job']) > 9 ? 'smaller' : '';
- $statusClass = $r['status'] !== '' ? 'status-' . strtolower($r['status']) : '';
- $ackClass = $r['ack'] === 'new' ? 'ack-new' : '';
- ?>
- <tr data-job-id="<?= (int) $r['id'] ?>" class="<?= h($statusClass) ?>">
- <td class="<?= h($jobClass) ?>"><?= render_field($r, 'job', $editable) ?></td>
- <td><?= render_field($r, 'material', $editable) ?></td>
- <td class="center"><?= render_field($r, 'description', $editable) ?></td>
- <td class="right"><?= render_field($r, 'qty', $editable) ?></td>
- <td class="center"><?= render_due($r, $editable) ?></td>
- <?php if ($audience === 'ICG'): ?><td><?= h($r['vendor_name']) ?></td><?php endif; ?>
- <td class="center <?= h($ackClass) ?>"><?= $r['ack'] === 'new' ? 'NEW' : '' ?></td>
- <td class="center"><?= h($r['status']) ?></td>
- <td class="center actions"><?= render_actions($r, $audience) ?></td>
- </tr>
- <?php endforeach; ?>
- </tbody>
- </table>
- <?php
- return ob_get_clean();
- }
- function render_field(array $r, string $col, bool $editable): string {
- $val = (string) ($r[$col] ?? '');
- if (!$editable) {
- return h($val);
- }
- return '<span class="editable" data-column="' . h($col) . '" tabindex="0">'
- . ($val === '' ? '—' : h($val))
- . '</span>';
- }
- function render_due(array $r, bool $editable): string {
- $display = $r['due_short'] !== '' ? h($r['due_short']) : '—';
- if (!$editable) return $display;
- return '<span class="editable" data-column="due_date" data-raw="' . h($r['due_date']) . '" tabindex="0">'
- . $display . '</span>';
- }
- function render_actions(array $r, string $audience): string {
- $btns = [];
- if ($audience === 'vendor') {
- if ($r['ack'] === 'new') {
- $btns[] = '<button class="btn btn-ack" data-action="acknowledge">Acknowledge</button>';
- }
- if ($r['status'] === '') {
- $btns[] = '<button class="btn btn-finish" data-action="mark_finished">Mark Finished</button>';
- } elseif ($r['status'] === 'Finished') {
- $btns[] = '<button class="btn btn-ship" data-action="mark_shipped">Mark Shipped</button>';
- }
- } else { // ICG
- if ($r['status'] === 'Shipped') {
- $btns[] = '<button class="btn btn-receive" data-action="mark_received">Mark Received</button>';
- }
- if ($r['status'] === 'Received') {
- $btns[] = '<span class="received">Received</span>';
- }
- }
- return implode(' ', $btns);
- }
- function render_messages(int $vendor_id, ?int $since_id = null): string {
- $pdo = db();
- if ($since_id !== null) {
- $stmt = $pdo->prepare('SELECT * FROM messages WHERE vendor_id = ? AND id > ? ORDER BY id ASC');
- $stmt->execute([$vendor_id, $since_id]);
- } else {
- $stmt = $pdo->prepare('SELECT * FROM messages WHERE vendor_id = ? ORDER BY id ASC');
- $stmt->execute([$vendor_id]);
- }
- $rows = $stmt->fetchAll();
- ob_start();
- foreach ($rows as $m) {
- $klass = $m['author'] === 'ICG' ? 'msg msg-icg' : 'msg msg-vendor';
- echo '<div class="' . $klass . '" data-msg-id="' . (int)$m['id'] . '">';
- echo '<span class="msg-author">' . h($m['author']) . '</span>';
- echo '<span class="msg-time">' . h($m['posted_at']) . '</span>';
- echo '<div class="msg-body">' . nl2br(h($m['body'])) . '</div>';
- echo '</div>';
- }
- return ob_get_clean();
- }
- function max_message_id(int $vendor_id): int {
- $stmt = db()->prepare('SELECT COALESCE(MAX(id), 0) FROM messages WHERE vendor_id = ?');
- $stmt->execute([$vendor_id]);
- return (int) $stmt->fetchColumn();
- }
|