Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions .github/workflows/deploy-jmb-second-half-portal.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
name: Deploy JMB Second-Half Portal to Cloudflare

on:
push:
branches: [main, master]
paths:
- 'workers/jmb-second-half-portal/**'
workflow_dispatch:

jobs:
deploy:
runs-on: ubuntu-latest
name: Deploy jmb-second-half-portal
steps:
- uses: actions/checkout@v4

- name: Deploy to Cloudflare Workers
uses: cloudflare/wrangler-action@v3
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: bafa242dd95d3fdce72540d20accd0a2
workingDirectory: workers/jmb-second-half-portal
command: deploy

Check warning

Code scanning / CodeQL

Workflow does not contain permissions Medium

Actions job or workflow does not limit the permissions of the GITHUB_TOKEN. Consider setting an explicit permissions block, using the following as a minimal starting point: {contents: read}
Comment on lines +12 to +23
235 changes: 235 additions & 0 deletions workers/jmb-second-half-portal/portal.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,235 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>JMB — 2nd Test Schedule Portal</title>
<meta name="description" content="JMB Construction Clean Truck Check 2nd Test Schedule Portal for July–December 2026.">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=DM+Sans:wght@400;500;600;700&family=Source+Serif+4:opsz,wght@8..60,600;8..60,700&display=swap" rel="stylesheet">
<style>
:root {
--green: #169b62;
--green-dark: #0f7a4a;
--orange: #ff883e;
--ink: #1c2421;
--muted: #5c6b64;
--paper: #f3f7f5;
--card: #ffffff;
--line: #e4ece8;
}
* { box-sizing: border-box; }
body {
font-family: 'DM Sans', sans-serif;
background:
radial-gradient(ellipse at 10% 0%, rgba(22, 155, 98, 0.12), transparent 45%),
radial-gradient(ellipse at 90% 10%, rgba(255, 136, 62, 0.10), transparent 40%),
linear-gradient(180deg, #eef5f1 0%, var(--paper) 40%, #e8f0ec 100%);
color: var(--ink);
margin: 0;
padding: 28px 16px 48px;
min-height: 100vh;
}
.container {
max-width: 1100px;
margin: auto;
background: var(--card);
padding: 28px 28px 24px;
border-radius: 16px;
box-shadow: 0 12px 40px rgba(28, 36, 33, 0.08);
border: 1px solid rgba(22, 155, 98, 0.12);
border-top: 8px solid var(--green);
animation: rise 0.55s ease-out both;
}
@keyframes rise {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
header {
display: flex;
justify-content: space-between;
align-items: flex-start;
gap: 16px;
border-bottom: 1px solid var(--line);
padding-bottom: 20px;
margin-bottom: 22px;
}
h1 {
font-family: 'Source Serif 4', Georgia, serif;
color: var(--green-dark);
margin: 0;
font-size: clamp(1.35rem, 2.4vw, 1.85rem);
letter-spacing: -0.02em;
line-height: 1.2;
}
.sub {
margin: 8px 0 0;
color: var(--muted);
font-size: 0.95rem;
}
.brand {
font-weight: 700;
color: var(--orange);
font-size: 1.05rem;
letter-spacing: 0.04em;
text-align: right;
animation: pulseBrand 2.4s ease-in-out infinite;
}
.brand small {
display: block;
margin-top: 4px;
font-size: 0.72rem;
font-weight: 600;
color: var(--muted);
letter-spacing: 0.12em;
text-transform: uppercase;
}
@keyframes pulseBrand {
0%, 100% { opacity: 1; }
50% { opacity: 0.72; }
}
.table-wrap {
overflow-x: auto;
border-radius: 10px;
border: 1px solid var(--line);
}
table {
width: 100%;
border-collapse: collapse;
min-width: 760px;
}
th {
background-color: var(--green);
color: white;
text-align: left;
padding: 12px 14px;
font-size: 12px;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.04em;
}
td {
padding: 11px 14px;
border-bottom: 1px solid var(--line);
font-size: 14px;
}
.month-header td {
background-color: #f3faf6;
font-weight: 700;
color: var(--green-dark);
letter-spacing: 0.03em;
}
.status-pass {
color: var(--green-dark);
font-weight: 700;
}
.status-fail {
color: var(--orange);
font-weight: 700;
background: rgba(255, 136, 62, 0.12);
padding: 4px 8px;
border-radius: 4px;
}
tbody tr:not(.month-header):hover {
background-color: #f1faf6;
}
.vin {
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
font-size: 12.5px;
letter-spacing: 0.02em;
}
.footer-note {
margin-top: 24px;
font-size: 13px;
color: var(--muted);
background: #fff8f4;
padding: 14px 16px;
border-left: 4px solid var(--orange);
border-radius: 0 8px 8px 0;
line-height: 1.45;
}
.footer-note strong { color: var(--ink); }
@media (max-width: 640px) {
body { padding: 12px 10px 32px; }
.container { padding: 18px 14px; }
header { flex-direction: column; }
.brand { text-align: left; }
}
</style>
</head>
<body>
<div class="container">
<header>
<div>
<h1>JMB Clean Truck Check: 2nd Test Portal</h1>
<p class="sub">Schedule: July – December 2026 · 25 trucks</p>
</div>
<div class="brand">
JMB Construction
<small>Fleet Portal</small>
</div>
</header>

<div class="table-wrap">
<table>
<thead>
<tr>
<th>Month</th>
<th>Client</th>
<th>Truck #</th>
<th>VIN</th>
<th>Test</th>
<th>Last Result</th>
<th>Likely Mfr*</th>
</tr>
</thead>
<tbody>
<tr class="month-header"><td colspan="7">JULY 2026</td></tr>
<tr><td></td><td>JMB</td><td>95</td><td class="vin">1FVHCYCY8FHGE7317</td><td>OBD</td><td class="status-pass">Pass</td><td>Freightliner</td></tr>
<tr><td></td><td>JMB</td><td>116</td><td class="vin">2NPLLZ0X15M874818</td><td>OVI</td><td class="status-pass">Pass</td><td>Peterbilt (CA)</td></tr>
<tr><td></td><td>JMB</td><td>117</td><td class="vin">1NKDX4TX0EJ966975</td><td>OBD</td><td class="status-pass">Pass</td><td>Unconfirmed†</td></tr>
<tr><td></td><td>JMB</td><td>99A</td><td class="vin">1FVACWDT2CDBN8881</td><td>OVI</td><td class="status-pass">Pass</td><td>Freightliner</td></tr>

<tr class="month-header"><td colspan="7">AUGUST 2026</td></tr>
<tr><td></td><td>JMB</td><td>109</td><td class="vin">1FVACWDT3CHBM2780</td><td>OVI</td><td class="status-pass">Pass</td><td>Freightliner</td></tr>
<tr><td></td><td>JMB</td><td>110</td><td class="vin">1FVACWDT5CHBM2781</td><td>OVI</td><td class="status-pass">Pass</td><td>Freightliner</td></tr>
<tr><td></td><td>JMB</td><td>111</td><td class="vin">1FVACWDT7CHBM2782</td><td>OVI</td><td class="status-pass">Pass</td><td>Freightliner</td></tr>
<tr><td></td><td>JMB</td><td>39A</td><td class="vin">3ALACWDT1EDFK0314</td><td>OBD</td><td class="status-fail">Fail</td><td>Freightliner (MX)</td></tr>
<tr><td></td><td>JMB</td><td>47A</td><td class="vin">3ALACWDT1EDFK0300</td><td>OBD</td><td class="status-pass">Pass</td><td>Freightliner (MX)</td></tr>

<tr class="month-header"><td colspan="7">SEPTEMBER 2026</td></tr>
<tr><td></td><td>JMB</td><td>33</td><td class="vin">JALE5W16XC7900346</td><td>OVI</td><td class="status-pass">Pass</td><td>Isuzu</td></tr>
<tr><td></td><td>JMB</td><td>113</td><td class="vin">1FVACWDU5HHHX6804</td><td>OBD</td><td class="status-pass">Pass</td><td>Freightliner</td></tr>

<tr class="month-header"><td colspan="7">OCTOBER 2026</td></tr>
<tr><td></td><td>JMB</td><td>40A</td><td class="vin">1FVACWBS5CDBK0023</td><td>OVI</td><td class="status-pass">Pass</td><td>Freightliner</td></tr>
<tr><td></td><td>JMB</td><td>49A</td><td class="vin">1FDWF7DC3GDA02393</td><td>OBD</td><td class="status-pass">Pass</td><td>Ford</td></tr>
<tr><td></td><td>JMB</td><td>108A</td><td class="vin">3FRWF7FA5FV679560</td><td>OBD</td><td class="status-pass">Pass</td><td>Ford (MX)</td></tr>

<tr class="month-header"><td colspan="7">NOVEMBER 2026</td></tr>
<tr><td></td><td>JMB</td><td>96</td><td class="vin">1XKDD49X0DJ958752</td><td>OVI</td><td class="status-pass">Pass</td><td>Kenworth</td></tr>
<tr><td></td><td>JMB</td><td>100</td><td class="vin">1FVACWDT5CHBP7790</td><td>OVI</td><td class="status-pass">Pass</td><td>Freightliner</td></tr>
<tr><td></td><td>JMB</td><td>112</td><td class="vin">1FVACWDT2CHBM2785</td><td>OVI</td><td class="status-pass">Pass</td><td>Freightliner</td></tr>
<tr><td></td><td>JMB</td><td>115</td><td class="vin">1XPTP4EX5DD181499</td><td>OVI</td><td class="status-pass">Pass</td><td>Peterbilt</td></tr>
<tr><td></td><td>JMB</td><td>119</td><td class="vin">1XPCD40X1PD859833</td><td>OBD</td><td class="status-pass">Pass</td><td>Peterbilt</td></tr>
<tr><td></td><td>JMB</td><td>121</td><td class="vin">1NKZX4TX9MJ972021</td><td>OBD</td><td class="status-pass">Pass</td><td>Unconfirmed†</td></tr>
<tr><td></td><td>JMB</td><td>107A</td><td class="vin">1FDNF7DC0HDB09172</td><td>OBD</td><td class="status-pass">Pass</td><td>Ford</td></tr>

<tr class="month-header"><td colspan="7">DECEMBER 2026</td></tr>
<tr><td></td><td>JMB</td><td>120</td><td class="vin">1FVHCYCY4GHGY9831</td><td>OBD</td><td class="status-pass">Pass</td><td>Freightliner</td></tr>
<tr><td></td><td>JMB</td><td>23B</td><td class="vin">JALE5W168E7300847</td><td>OBD</td><td class="status-pass">Pass</td><td>Isuzu</td></tr>
<tr><td></td><td>JMB</td><td>44A</td><td class="vin">3ALACWDT3DDFJ0690</td><td>OVI</td><td class="status-pass">Pass</td><td>Freightliner (MX)</td></tr>
<tr><td></td><td>JMB</td><td>98A</td><td class="vin">1FVACXDT8CDBD8975</td><td>OVI</td><td class="status-pass">Pass</td><td>Freightliner</td></tr>
</tbody>
</table>
</div>

<div class="footer-note">
<strong>Logic Summary:</strong> Direct mapping for July–Sept due dates. 6-month extension applied for Feb–June due dates to fulfill the bi-annual testing requirement. Total trucks accounted: 25.<br>
<strong>*Likely Manufacturer</strong> is decoded from VIN WMI only — it is not legal ownership.<br>
<strong>†</strong> WMI <code>1NK</code> left unconfirmed pending NHTSA decoder check.
</div>
</div>
</body>
</html>
37 changes: 37 additions & 0 deletions workers/jmb-second-half-portal/worker.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// worker.js — JMB 2nd Test Schedule Portal
//
// Routes:
// GET / -> schedule portal HTML
// GET /api/health -> JSON health check
// * -> 404
//
// portal.html is bundled at build time via wrangler [[rules]] Text import.

import PORTAL_HTML from "./portal.html";

export default {
async fetch(request) {
const url = new URL(request.url);

if (url.pathname === "/api/health") {
return Response.json({
status: "ok",
service: "jmb-second-half-portal",
client: "JMB",
trucks: 25,
schedule: "2026-07..2026-12",
});
}

if (url.pathname === "/" || url.pathname === "/index.html") {
return new Response(PORTAL_HTML, {
headers: {
"content-type": "text/html; charset=utf-8",
"cache-control": "public, max-age=300",
},
});
}

return new Response("Not found", { status: 404 });
},
};
17 changes: 17 additions & 0 deletions workers/jmb-second-half-portal/wrangler.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
name = "jmb-second-half-portal"
main = "worker.js"
compatibility_date = "2026-03-01"
account_id = "bafa242dd95d3fdce72540d20accd0a2"

# Bundle portal.html as a string import.
[[rules]]
type = "Text"
globs = ["**/*.html"]
fallthrough = true

# workers.dev preview URL after first deploy:
# https://jmb-second-half-portal.silverbackai.workers.dev/
# Optional custom domain (uncomment + attach DNS when ready):
# routes = [
# { pattern = "jmb-portal.silverbackai.agency", custom_domain = true }
# ]