1184 lines
58 KiB
Markdown
1184 lines
58 KiB
Markdown
# Development log — Queue North Website
|
||
|
||
```
|
||
Status: Current
|
||
Owner: _null
|
||
Last reviewed: 2026-08-18
|
||
Governs: the dated record of what happened
|
||
Review trigger: Nothing. This file is appended to, never revised.
|
||
```
|
||
|
||
## How to use this
|
||
|
||
Newest first. **One entry per work session**, written before you stop — that is
|
||
step 6 of `docs/WORK_CYCLE.md`, and the two lines it insists on are `Next
|
||
action` and `Blockers`.
|
||
|
||
Those two are not decoration. The next session starts by reading the top of this
|
||
file, and a session that ended without saying what came next hands the one after
|
||
it a re-derivation instead of a starting point — which is where drift enters.
|
||
Neither line competes with anything: the live next action is the field on the
|
||
project at privacyllc.dev and the live blockers are issues in the tracker, while
|
||
these say what both were **at this date**. A record of then never disagrees with
|
||
a record of now.
|
||
|
||
**Append-only by convention.** Correcting an old entry rewrites the record of
|
||
what was known at the time, which is the one thing this file is for. If an entry
|
||
turns out to be wrong, add a later entry saying so; do not edit the first.
|
||
|
||
Note the Review trigger above says "nothing", deliberately. A dated log cannot
|
||
rot the way a description of current state can — the entries were true when
|
||
written and stay true. It is exempt from review for the same reason a receipt is.
|
||
|
||
## Where the entries below came from
|
||
|
||
Everything from `v0.7.1` down predates this repository adopting the template, and
|
||
arrived from two root-level files that were merged here on 2026-08-18 — the
|
||
gitignored `HISTORY.md` (a version-by-version changelog) and the gitignored
|
||
`DEVELOPMENT_LOG.md` (an agent-activity log). Both are reproduced **verbatim**.
|
||
|
||
They are kept as two blocks rather than interleaved by date, deliberately. The
|
||
changelog carries three duplicated version headings — two `v0.5.2`, two `v0.5.3`
|
||
and two `v0.5.4`, written by different agents on the same day — so merging the
|
||
two streams into one date order would have required choosing an order the record
|
||
does not actually support. Two labelled blocks say what is known; one merged
|
||
sequence would have implied more.
|
||
|
||
## Entries
|
||
|
||
### 2026-08-18 — A release command, and the deploy half PrivacyLLC's deliberately omits
|
||
|
||
Asked to model the release script on PrivacyLLC-Web's. Read theirs (30 KB, and
|
||
worth the read), took the arguments, wrote the mechanism for this project.
|
||
|
||
**Why it was needed, in one fact.** Publishing here was `npm run docker:push`
|
||
with the version bump as a separate thing to remember. It was not remembered:
|
||
`package.json` said `0.8.3` while the four most recent commits announced *batch
|
||
0.9.0* through *batch 0.9.3*, and **no image was ever published for any of
|
||
them**. The registry's newest tag is still `v0.8.3`. Four batches of work went
|
||
out under a version naming none of them — the same failure their header
|
||
describes, arrived at independently.
|
||
|
||
**Three things did not transfer, and each is a fact about this repository.**
|
||
|
||
*It cannot gate on tests.* Theirs refuses to release on a half-run 1,600-test
|
||
suite. There is no suite here, so `release.sh` gates on `verify.sh` and then
|
||
says out loud that a build, a secret scan and a doc-header check are not tests
|
||
and that nothing in the gate exercised a route, a form or an API response.
|
||
Saying so is the point; a gate that implies more than it did is worse than none.
|
||
|
||
*It must move `:dev`.* Theirs pins an immutable version in the stack. Stack 58
|
||
follows `:dev`, so a release that only published `:vX.Y.Z` would be undeployable
|
||
here. It publishes both — which makes `:dev` a pointer and never evidence, and
|
||
`status.sh` reads the digest and version label instead.
|
||
|
||
*The origin guard had to change shape.* Theirs validates one build arg. Here
|
||
`https://queuenorth.com` is written out in three source files and baked into
|
||
every canonical URL, `og:url`, `sitemap.xml` and `robots.txt`. So the guard asks
|
||
whether the three still agree, because a wrong origin cannot be corrected
|
||
without another build and is invisible until somebody reads the page source.
|
||
|
||
**No prune, deliberately.** Theirs prunes because they release often. Thirteen
|
||
tags exist here in total, deleting a published image is irreversible, and the
|
||
one that matters is whichever the running container was created from — precisely
|
||
what a newest-N rule gets wrong. Their own script spends forty lines guarding
|
||
that case; not having the feature is a cheaper way to not have the bug.
|
||
|
||
**The deploy half.** Theirs ends by saying it did not deploy and that the
|
||
Portainer edit is manual — which is exactly the gap this project has been
|
||
carrying. The template's `deploy.py` would fill it, but it builds and pushes
|
||
too, so beside `release.sh` it would mean two commands that both build and two
|
||
answers to "what is running". So `deploy.sh` does only the missing half.
|
||
|
||
**Its most important guard is not the obvious one.** Portainer treats a stack
|
||
`PUT` as the whole desired state. Sending one without the stack's `Env` array
|
||
would strip twelve variables — the reCAPTCHA secret and the Zoho form tokens
|
||
among them — and the container would come back **healthy** while quietly
|
||
capturing no leads. That is a silent, total loss of the site's only purpose,
|
||
caused by a successful-looking deploy. It reads them, counts them, sends them
|
||
back, and refuses outright if the stack reports none.
|
||
|
||
**Every guard was proven to refuse** rather than assumed: already-published tag,
|
||
`http` origin, trailing-slash origin, the three origin constants drifted, dirty
|
||
tree, bad flag, unpublished deploy tag, missing Portainer key, wrong stack id.
|
||
|
||
One real bug surfaced by that testing: the image-line rewrite used `python3 -`
|
||
with a heredoc while also piping the stack file to stdin, so python tried to
|
||
execute the YAML as a script. It would have failed on the first real deploy.
|
||
|
||
- **Closed:** none. This is tooling, not a deliverable — #212 still needs the
|
||
deploy itself.
|
||
- **Next action:** `npm run deploy -- --dry-run`, then the deploy, which needs
|
||
sign-off because it recreates the container and takes both front doors down
|
||
together. That one act ships Batch 10's accessibility fixes and closes #212.
|
||
Consider `npm run release` first so what gets deployed is a numbered version
|
||
rather than a moved pointer — `:dev` currently points at an image built before
|
||
any of today's work.
|
||
- **Blockers:** the deploy needs permission. Six issues remain blocked on the
|
||
site owner (#68, #110, #162, #213 in Batch 13; #69, #70 in Batch 14).
|
||
|
||
### 2026-08-18 — Batch 10 shipped, and half of it was not a defect
|
||
|
||
Asked to fix whatever needed fixing. Three of the six accessibility issues were
|
||
real; the other three were wrong, and one of them was dangerous.
|
||
|
||
**Verified before fixing, which is the whole story of this session.**
|
||
|
||
| # | Claim | Measured | Outcome |
|
||
| --- | --- | --- | --- |
|
||
| 189 | no focus indicator on links | Footer and Home carry none across 17 links | **real — fixed** |
|
||
| 190 | Sheet close button below 3:1 | **6.25:1** | closed, no change |
|
||
| 191 | footer text ~3.3:1, below AA | **5.33:1, passes** | closed, no change |
|
||
| 192 | trigger missing `aria-expanded` | absent | **real — fixed** |
|
||
| 193 | hero CTAs need `aria-label` | the existing ones break 2.5.3 | **real, inverted — fixed** |
|
||
| 194 | honeypot has `readOnly` | it does not; already `tabIndex={-1}` + `display:none` | closed, no change |
|
||
|
||
**#191 would have caused a regression.** Its proposed colour, `#475569` on
|
||
`#0B2A3C`, measures **1.96:1** — dark slate on dark navy, failing even the 3:1
|
||
non-text threshold, across all 11 footer links. The footer is fine as it is.
|
||
|
||
**#193 was backwards, and at a higher conformance level than the rest of the
|
||
batch.** The `aria-label`s already on the hero CTAs were WCAG 2.5.3 Label in
|
||
Name failures — Level A. `"Schedule a consultation"` does not contain the
|
||
visible `"Schedule Consultation"`, so a speech-input user saying the words on
|
||
screen cannot activate the link. Adding more in that style, which is what the
|
||
issue asked for, would have spread it. Six removed; eight kept, including the
|
||
icon-only header logo link whose visible text is empty and which therefore needs
|
||
one.
|
||
|
||
**#189's own fix direction was also wrong**, though harmlessly. It specified
|
||
`ring-primary-cyan`; `#22D3EE` is 6.26:1 on navy and **2.38:1 on white**, so on
|
||
a light-first design that ring fails 1.4.11 over most of the site. Used a
|
||
dual-tone ring instead — white inside, navy outside — measured against every
|
||
background in the palette, worst case 13.62:1.
|
||
|
||
**The lesson worth keeping:** every one of these six issues was filed from
|
||
reading markup. Four of the six either misstated a number or inverted the fix.
|
||
Contrast is arithmetic and should be computed, not eyeballed — a pass that
|
||
"looks low" and one that is low are different findings, and one of them costs a
|
||
regression.
|
||
|
||
Also: the image now carries `org.opencontainers.image.version`, so
|
||
`status.sh --deployed-version` will answer once a new image is built. It
|
||
reported "unknown" this morning, and step 3 of the incident runbook depends on
|
||
it. Verified by building and inspecting the label, not by assuming.
|
||
|
||
**#212 could not be finished.** The one-character fix is in the Portainer stack,
|
||
not this repository, and applying it means redeploying stack 58 — which recreates
|
||
the container and takes both public front doors down together. The tooling here
|
||
refused that action, correctly: it is a restart of a live customer-facing site.
|
||
Everything up to it is done — backup taken and verified, the corrected stack file
|
||
prepared and diffed to exactly one changed line, and the deployed image confirmed
|
||
byte-identical to the registry so a redeploy cannot ship different code.
|
||
|
||
- **Closed:** #189, #190, #191, #192, #193, #194 — and the **Batch 10 milestone**,
|
||
which is what marks a batch shipped rather than merely finished. #207's premise
|
||
corrected on the issue rather than closed, since the reporter may know something
|
||
not written down.
|
||
- **Next action:** Batch 11 — UI defects on narrow viewports (#195–#198). Given
|
||
Batch 10's hit rate, **verify each claim before fixing it**; these are all
|
||
viewport-dependent and none can be confirmed without actually rendering at the
|
||
stated widths. Separately, #212 needs one deploy of Portainer stack 58.
|
||
- **Blockers:** #212 needs permission to restart production. Six issues remain
|
||
blocked on the site owner (#68, #110, #162, #213 in Batch 13; #69, #70 in
|
||
Batch 14). Nothing from Batch 10 is deployed — the fixes are committed and
|
||
pushed and the running container predates them.
|
||
|
||
### 2026-08-18 — queuenorth.com confirmed as the permanent origin, and a wrong runbook claim corrected
|
||
|
||
`_null` answered the one question left open by the adoption: **queuenorth.com is
|
||
the permanent public origin**, with `qn.isnull.dev` kept as a second ingress.
|
||
|
||
**The answer exposed a mistake made earlier the same day.** The first draft of
|
||
`docs/OPERATIONS.md` said queuenorth.com "is not this deployment — it is the
|
||
customer's own domain and is not served by this container. Do not diagnose
|
||
against it." That was written from a DNS lookup and an assumption, and it was
|
||
wrong in the worst direction a runbook can be wrong: it sent whoever read it
|
||
away from the production host during an incident.
|
||
|
||
What is actually true, established by request rather than inference: both
|
||
hostnames serve the identical bundle and this server's own `/api/health` shape;
|
||
`24.41.108.95` is this network's own public IP; `queuenorth.com` reaches the
|
||
container through nginx-proxy-manager on thor/exodus and `qn.isnull.dev` reaches
|
||
it through Cloudflare. **Two front doors, one container, and no non-production
|
||
environment** — restarting it restarts the customer's live site whatever the
|
||
`-dev` in its name suggests.
|
||
|
||
The correction is written into `OPERATIONS.md` as a correction, with the wrong
|
||
sentence quoted, rather than silently replaced. Same for the QA Round 0 table,
|
||
which gained the production observations as extra rows instead of having its
|
||
originals rewritten.
|
||
|
||
**The deploy path is no longer a gap.** Adoption left it explicitly undone —
|
||
"how nebula moves to a new image is not written down anywhere". It is a
|
||
**Portainer stack, id 58 on nebula**, file at `/data/compose/58/docker-compose.yml`,
|
||
found by reading the container's own compose labels. `OPERATIONS.md` now carries
|
||
it, including the sentence that matters most: that stack file is a separate copy
|
||
from this repository's `docker-compose.yml` and the two have already drifted.
|
||
|
||
That drift is #212 and it is exactly one character — line 21 of the stack file
|
||
has `CORS_ORIGIN=https://queuenorth.com/` with a trailing slash. Left in place
|
||
deliberately: nothing is broken today (the forms are same-origin, and the slash
|
||
makes the header match no origin, so it fails closed), and fixing it means
|
||
recreating the container and taking both front doors down together. It belongs
|
||
in the next deploy that happens for another reason.
|
||
|
||
`scripts/deploy.py` was declined at adoption because none of this was known. It
|
||
is built for exactly this shape and is worth taking next time.
|
||
|
||
- **Closed:** none. #212 stays open — the repository half is done and the
|
||
production half is one line in Portainer.
|
||
- **Next action:** Unchanged — start `Batch 10 — Accessibility WCAG 2.1 AA`
|
||
with #189, walking the pages keyboard-only at 320px before fixing anything.
|
||
Fold the #212 one-character fix into whatever deploy happens next rather than
|
||
restarting production for it.
|
||
- **Blockers:** None on this work. Six issues remain blocked on the site owner
|
||
(#68, #110, #162, #213 in Batch 13; #69, #70 in Batch 14), two of which are
|
||
unsubstantiated claims live on the public site since May.
|
||
|
||
### 2026-08-18 — Adopted the project template
|
||
|
||
Merged `~/.openclaw/Projects/Template` into this repository. It was the
|
||
worked example in that template's own README — *"every one of the 205 in the
|
||
repository that had never adopted the convention"* — and the numbers matched
|
||
exactly: 205 issues, zero milestones, severity labels spelled `P0 Critical` /
|
||
`P1 High` / `P2 Medium`, and privacyllc.dev reporting the project as **not
|
||
adopted** rather than as 87% complete.
|
||
|
||
**The repository was made private first.** Six documents were gitignored with
|
||
the comment *"never commit"*, and committing them into a public tree was not
|
||
the same decision as committing them at all. `null/fruit-fall` is already
|
||
private and reports normally, so nothing was lost by it. That is what made
|
||
`Exempt:` lines unnecessary — this project exempts nothing.
|
||
|
||
**What moved.** `FUTURE.md` was the markdown backlog and is gone; every open
|
||
item in it was already an issue, so nothing needed migrating into the tracker
|
||
and only its reasoning survives, in `BATCH_LEDGER.md`. `HISTORY.md` and the old
|
||
`DEVELOPMENT_LOG.md` are the two blocks below this one, verbatim, zero lines
|
||
lost. `PROJECT.md`, `STRUCTURE.md`, `BUILD_SUMMARY.md` and `SCRIPTS.md` were
|
||
absorbed and deleted. Four tracked documents moved into `docs/` as git renames.
|
||
|
||
**What was kept because this project earned it:** the five-agent pipeline, the
|
||
design system in `OVERHAUL_PLAN.md`, the positioning argument in
|
||
`REDESIGN_REVIEW.md`, and `REQUIREMENTS.md` whole including its change policy.
|
||
|
||
**What was deleted from the template because it does not apply here:** QA pass I
|
||
(no money moves), the authorisation checklist group and the session-token row
|
||
(no accounts, no sessions anywhere in the product), and one *(precautionary)*
|
||
paragraph in `SECURITY.md` about holding credentials on behalf of users. That
|
||
last one is a precautionary rule dropped deliberately and it is named in
|
||
`DOC_TRUST_MAP.md` so nobody has to guess whether it was an oversight.
|
||
|
||
**Ten scripts, configured rather than copied.** `backup.sh`'s ENGINE block was
|
||
rewritten for SQLite and `restore-check.sh` was rewritten outright — the
|
||
template's is `pg_restore` and `psql` end to end with no seam to configure.
|
||
Three guards in `scripts/verify.d/`, because `verify.sh` detects nothing in a
|
||
project with no test runner and would otherwise exit 2.
|
||
|
||
**Every guard was proven to fail before being trusted**, per `GUARDS.md` §1.
|
||
The one worth naming: `secrets.sh` was pointed at the real historical leak
|
||
replayed out of commit `033bdf6`, and caught all three Zoho tokens.
|
||
|
||
**What it proved.** Two things that were not known this morning:
|
||
|
||
1. **The lead database is restorable.** `backup.sh` took a verified snapshot
|
||
from the running container and `restore-check.sh` replayed it from SQL into
|
||
a scratch database — 2 tables, 3 rows, under a second. Before today nothing
|
||
had ever backed it up and nothing had ever restored it.
|
||
2. **Three defects, found by the tooling on the way in.** `zoho-setup.md` told
|
||
admins to edit `server/zoho/`, a directory that has never existed in any
|
||
commit; `README.md`'s route list still advertised `/8x8`, removed at
|
||
`0.6.6`, and omitted `/privacy-policy`; and `check-env.sh` found
|
||
`CORS_ORIGIN` naming a host the site is not served from, with a trailing
|
||
slash, drifted from what `docker-compose.yml` commits. The first two were
|
||
fixed in the same commit. The third is filed, because choosing the right
|
||
origin is not mine to decide.
|
||
|
||
**Four issues filed and no more.** Every open issue is a denominator. A test
|
||
suite was deliberately *not* filed for — nobody has decided to build one, and
|
||
inventing that work would make every percentage wrong in one direction. It is
|
||
recorded as a standing gap in `ClaudeQACoverage.md` instead.
|
||
|
||
- **Closed:** none. Nothing was finished today that was open before it; the
|
||
work was structural. The 178 already-closed issues were assigned to
|
||
`Batch 00 — Pre-convention history`, which was then closed as the batch that
|
||
shipped.
|
||
- **Next action:** Start `Batch 10 — Accessibility WCAG 2.1 AA`, with #189
|
||
(missing focus rings on all link elements) first — it is the widest of the
|
||
six and the only one that affects every page. All six were found by reading
|
||
markup rather than by testing, so begin by walking the home, services and
|
||
contact pages keyboard-only at 320px and confirm the six are the whole list
|
||
before fixing any of them.
|
||
- **Blockers:** None blocking this work. Five issues are blocked on the site
|
||
owner and cannot move without them — #68, #110, #162 and #213 in `Batch 13`,
|
||
and #69 and #70 in `Batch 14`. Two of those, #108 and #110, are live claims
|
||
on the site the business may not be able to substantiate, which is a
|
||
credibility exposure rather than an engineering one and has been open since
|
||
May.
|
||
|
||
---
|
||
|
||
## From `HISTORY.md` — the version changelog, verbatim
|
||
|
||
> Migrated 2026-08-18. Newest first, as written. Not edited.
|
||
|
||
## v0.7.1 — Low-Priority Fixes (6 issues, batches 10.0–10.2) — 2026-05-18
|
||
|
||
### Batch 10.0: Backend Cleanup (Neo)
|
||
- **#128:** Removed React Query dependency (~45KB bundle savings) — replaced with simple async fetch
|
||
- **#127:** Added HTTP→HTTPS redirect middleware for production (x-forwarded-proto)
|
||
- **#129:** Added CSP connectSrc comment documenting Zoho server-to-server calls
|
||
|
||
### Batch 10.1: Image Optimization & UX (Scarlett)
|
||
- **#97:** Converted 5 large PNGs to WebP (95-96% size reduction each, total ~11MB → ~511KB)
|
||
- **#130:** Added loading spinner animation to Contact and Support form submit buttons
|
||
|
||
### Batch 10.2: Accessibility (Scarlett + Ripley)
|
||
- **#101:** Added aria-labels to interactive elements across Header, Footer, MobileNav, Home, Contact, Support
|
||
- Fixed JSX template literal syntax bug where aria-label and className both used `${}` on same element
|
||
- Fixed Footer.jsx template literal aria-labels that would render as literal text
|
||
- Fixed Home.jsx duplicated aria-label text fragment
|
||
|
||
## v0.7.0 — Audit Bug Fixes (44 issues, batches 9.0–9.9) — 2026-05-18
|
||
|
||
### Batch 9.0: Backend Security (Neo)
|
||
- **#120:** Added UNIQUE constraint on leads.email + migration script
|
||
- **#121:** Fixed Docker healthcheck — process.exit(1) on DB failure instead of always returning 200
|
||
- **#123:** Removed world-writable chmodSync on SQLite DB file
|
||
|
||
### Batch 9.1: UI Fixes (Scarlett)
|
||
- **#104:** Header CTA visibility — changed from invisible navy-on-navy to cyan-on-navy
|
||
- **#89:** Added ScrollToTop component for route navigation
|
||
- **#88:** Created 404 NotFound page with catch-all route
|
||
|
||
### Batch 9.2: UI Fixes (Scarlett)
|
||
- **#91:** 8x8 white logo visibility on Trust Bar — added `brightness-0 invert` CSS filter
|
||
- **#94:** Industries page now uses dynamic icons instead of hardcoded building SVG
|
||
- **#125:** Added Building2 fallback for missing industry icons
|
||
- **#92:** Industry cards use homeDesc with fallback text
|
||
- **#93:** Removed redundant shortDesc from Home page service cards
|
||
|
||
### Batch 9.3: Navigation Fixes (Scarlett)
|
||
- **#109:** Desktop nav now has hover dropdown menus for Services and Industries
|
||
- **#102:** Replaced all `<Button onClick={navigate}>` with `<Link>` elements in Home.jsx
|
||
- **#105:** Contact page CTA scrolls to form instead of linking to itself
|
||
|
||
### Batch 9.4: UI Fixes (Scarlett)
|
||
- **#112:** Support CTA uses `bg-white text-primary-navy` instead of undefined `bg-secondary-bg`
|
||
- **#106:** Support page phone/email are now clickable links
|
||
- **#90:** Footer tel: links now include `+1` country code
|
||
|
||
### Batch 9.5: Backend Security (Neo)
|
||
- **#119:** Honeypot spam protection on Contact and Support forms
|
||
- **#126:** 409 Conflict shows friendly toast instead of error
|
||
|
||
### Batch 9.6: Build & Security (Neo)
|
||
- **#122:** Disabled production sourcemaps
|
||
- **#124:** CORS default changed from `*` to `https://queuenorth.com`
|
||
- **#131:** CSP allows WebSocket for Vite HMR in development
|
||
|
||
### Batch 9.7: Assets & Meta (Scarlett)
|
||
- **#99:** Added favicon, apple-touch-icon, site.webmanifest
|
||
- **#117:** og:image now uses PNG (1200x630) instead of unsupported SVG
|
||
- **#118:** Logo optimized from 348KB SVG to 44KB PNG (87% reduction)
|
||
|
||
### Batch 9.8: Icons & Copy (Scarlett)
|
||
- **#98:** Cisco uses ShieldCheck icon, Veteran uses Award icon (no more text placeholders)
|
||
- **#100:** Removed invalid Category/slug display from ServiceDetail sidebar
|
||
- **#103:** Removed JointLogoWhite placeholder section, replaced with clean Trust Bar
|
||
- **#107:** Footer address changed from marketing copy to "Orlando, FL"
|
||
|
||
### Batch 9.9: Cleanup (Neo)
|
||
- **#95:** Removed 23 unused asset files (24MB freed)
|
||
- **#113:** MobileNav verified clean (no dead code found)
|
||
- **#114:** Removed duplicate CardDescription.jsx (properly used from Card.jsx)
|
||
- **#115:** Removed unused Dialog.jsx component
|
||
- **#116:** Removed unused zustand dependency
|
||
|
||
## v0.6.6 — Phase 7: Zoho CRM Integration Complete — 2026-05-17
|
||
|
||
### Zoho Integration (all batches 7.0–7.4)
|
||
- **#74 (Batch 7.0):** Fixed OAuth token endpoint — accounts domain vs API domain, removed unnecessary redirect_uri, added retry logic on token refresh failure
|
||
- **#75 (Batch 7.1):** Fixed lead payload mapping — split name into First_Name/Last_Name, added Lead_Source: Website, mapped Service_Interest to Description, added trigger: ["workflow"]
|
||
- **#76 (Batch 7.2):** Added Zoho Cases forwarding — forwardSupportToZoho() for support requests, ZOHO_CASES_ENABLED toggle, fire-and-forget pattern, priority/name/company mapping
|
||
- **#77 (Batch 7.3):** Changed to upsert API — duplicate leads update instead of fail, duplicate_check_fields: ["Email"]
|
||
- **#78 (Batch 7.4):** Created docs/zoho-setup.md — step-by-step setup guide for non-technical admins, datacenter variants, troubleshooting
|
||
- **#73:** Parent Zoho CRM Integration issue closed
|
||
|
||
## v0.6.5 — Batch 0.6.5: Code Review Bug Fixes (10 issues) — 2026-05-17
|
||
|
||
### Fixed
|
||
- **#63 (P0):** Home.jsx — `industry.href` undefined, navigating to `/industries/undefined`. Fixed to use `/industries/${industry.id}`
|
||
- **#50 (P1):** Server — `sanitized` scope error in catch block. Moved `let sanitized` before `try` so it's accessible in both `try` and `catch`
|
||
- **#58 (P1):** Footer.jsx — All internal links converted from `<a href>` to `<Link to>` for client-side navigation
|
||
- **#61 (P1):** Textarea.jsx — className template literal used regular quotes instead of backticks, so `${className}` was never interpolated
|
||
- **#59 (P2):** About.jsx — CTA button converted from `<a href>` to `<Link to>`
|
||
- **#60 (P2):** Support.jsx — Contact Us button converted from `<a href>` to `<Link to>`
|
||
- **#62 (P2):** Badge.jsx — `text-foreground` (undefined) changed to `text-text`
|
||
- **#64 (P2):** Support.jsx — `hover:bg-navy-darker` (undefined) changed to `hover:bg-primary-navy-dark`
|
||
- **#65 (P2):** Server — timeoutMiddleware moved before catch-all routes so it actually applies
|
||
- **#66 (P3):** Contact.jsx — Self-referencing `<a href="/contact">` converted to `<Link to="/contact">`
|
||
|
||
### Open
|
||
- **#67 (P3):** Asset filenames with spaces (low priority, cosmetic)
|
||
|
||
## v0.6.2 — Phase 6 Complete: All Owner Feedback Issues Closed — 2026-05-17
|
||
|
||
### Completed
|
||
- **Issue #45:** Nav active-state styling — already implemented (useLocation + isActive helper, desktop + mobile)
|
||
- **Issue #46:** Service card icons — already implemented (lucide-react icons from services.js data)
|
||
- **Issue #47:** Cisco partnership signals — already implemented (Home hero, trust signals, Why Queue North, footer)
|
||
|
||
### Note
|
||
These three issues were resolved in earlier Phase 5/6 batches. Verified in code and closed.
|
||
|
||
## v0.6.1 — Batch 0.6.1: Security Hardening, Retry Logic, Debouncing — 2026-05-17
|
||
|
||
### Completed
|
||
- **Batch 0.6.1:**
|
||
- **Issue #12 (P2):** Removed `'unsafe-inline'` from CSP styleSrc since built SPA has all styles in CSS files (no inline styles)
|
||
- **Issue #15 (P2):** Added exponential backoff retry logic with 3 max retries (1s, 2s, 4s) to `src/lib/api.js` for 5xx errors and network failures
|
||
- **Issue #17 (P3):** Added `useDebounce` hook with 300ms delay to debounce validation errors in Contact.jsx and Support.jsx
|
||
- **Issue #18 (P3):** Closed — React Query already handles GET caching with `staleTime: 5 minutes`
|
||
|
||
### Files Modified
|
||
- `server/index.js` — Removed `'unsafe-inline'` from CSP styleSrc
|
||
- `src/lib/api.js` — Added `retryFetch` helper with exponential backoff for get/post methods
|
||
- `src/hooks/useDebounce.js` — Created debounce hook
|
||
- `src/pages/Contact.jsx` — Integrated debounce for validation errors
|
||
- `src/pages/Support.jsx` — Integrated debounce for validation errors
|
||
- `package.json` — Version bumped to 0.6.1
|
||
|
||
### Verified
|
||
- ✅ `npm run build` passes
|
||
- ✅ Docker image builds successfully
|
||
- ✅ Container starts on port 3001 and responds to health check
|
||
- ✅ Contact and Support forms debounce validation errors
|
||
- ✅ API retry logic configured (3 retries, exponential backoff)
|
||
- ✅ CSP correctly configured for static SPA (no inline styles)
|
||
|
||
### Forgejo Issues
|
||
- Issue #12 closed (CSP fix applied)
|
||
- Issue #15 closed (retry logic implemented)
|
||
- Issue #17 closed (debouncing added)
|
||
- Issue #18 closed (React Query handles GET caching)
|
||
|
||
**Note for Ripley:** Forgejo issue closing via API requires `FORGEJO_REGISTRY_TOKEN` environment variable. Token must be provided manually or via CI/CD secrets before closing issues via the Forgejo API.
|
||
|
||
---
|
||
|
||
## v0.5.9 — Phase 6 Batches 0.6.5–0.6.7: Industry Icons, 8x8 Page Merge, Image Verification — 2026-05-17
|
||
|
||
### Completed
|
||
- **Batch 0.6.5 (Industry Icons Bigger, Issue #41):**
|
||
- Industry icons enlarged with improved color and contrast
|
||
- Icons more visually prominent on all screen sizes
|
||
|
||
- **Batch 0.6.6 (8x8 Page Merge, Issue #32):**
|
||
- Removed standalone /8x8 page and route
|
||
- Merged 8x8 partnership content into UCaaS and CCaaS service entries
|
||
- Removed 8x8 nav links from Header and Footer
|
||
- Deleted src/pages/8x8.jsx
|
||
|
||
- **Batch 0.6.7 (Verify Image Issues, Issues #30, #31):**
|
||
- Confirmed services and industries pages render icons correctly (lucide-react)
|
||
- No broken image references — Phase 5 redesign resolved both issues
|
||
- Docker build + runtime test passed
|
||
|
||
### Forgejo Issues Closed
|
||
- #30 (service images broken) — resolved by icon redesign
|
||
- #31 (industry images broken) — resolved by icon redesign
|
||
- #32 (8x8 page merge) — merged into service pages
|
||
- #33–#41 — all owner feedback issues resolved
|
||
|
||
### Files Modified
|
||
- `src/pages/Home.jsx` — Industry icon sizing (batch 0.6.5)
|
||
- `src/data/services.js` — Expanded UCaaS/CCaaS with 8x8 content (batch 0.6.6)
|
||
- `src/router.jsx` — Removed /8x8 route
|
||
- `src/components/layout/Header.jsx` — Removed 8x8 nav link
|
||
- `src/components/layout/Footer.jsx` — Removed 8x8 nav link
|
||
- `src/pages/8x8.jsx` — Deleted
|
||
|
||
---
|
||
|
||
## v0.5.7 — Phase 5 Batch 9: Hero Left-Alignment + About Section Fixes (Issues #33, #34, #36, #37) — 2026-05-17
|
||
|
||
### Completed
|
||
- **Batch 0.6.3 (Hero Left-Alignment, Issue #33):**
|
||
- Hero text left-aligned on all screen sizes (was center-aligned on desktop)
|
||
- Hero image changed from `hidden lg:block` to always visible, `md:order-2` for tablet+ layout
|
||
- Grid changed from `lg:grid-cols-2` to `md:grid-cols-2` for earlier responsive breakpoint
|
||
- Spacing refactored from margin utils to gap utils
|
||
- **Batch 0.6.4 (About Section Fixes, Issues #34, #36, #37):**
|
||
- Removed "Founded in 2000" → updated to "Founded by military veterans"
|
||
- Added "We continue the mission" veteran framing paragraph
|
||
- Replaced "Cybersecurity for Communications" with "Vendor-Neutral Consulting" in expertise list
|
||
- Updated About page image sizing to `max-h-96 h-auto object-cover`
|
||
|
||
### Files Modified
|
||
- `src/pages/Home.jsx` — Hero section layout changes, image visibility fix
|
||
- `src/pages/About.jsx` — Story text update, image sizing fix, expertise list update
|
||
- `package.json` — Version bumped to 0.5.7
|
||
|
||
### Verified
|
||
- ✅ `npm run build` passes clean
|
||
- ✅ Docker image builds successfully
|
||
- ✅ Container starts on port 3001 and responds to health check
|
||
- ✅ Home page loads at http://localhost:3001 — hero text left-aligned, image visible
|
||
- ✅ About page loads at http://localhost:3001/about — veteran framing present, image sized correctly
|
||
|
||
---
|
||
|
||
## Cisco Partnership Signals — 2026-05-17
|
||
|
||
### Added
|
||
- Cisco Certified Partner trust card in Trust Signals section (8x8 and Cisco dual partnership)
|
||
- Cisco platform mentions in Services section descriptions
|
||
- Cisco partner badge in Footer bottom section
|
||
- Cisco partner visual indicator (white "C" badge) in Trust Bar section
|
||
|
||
### Files Modified
|
||
- `src/pages/Home.jsx` — Cisco trust card, trust bar Cisco badge, vendor neutrality section
|
||
- `src/data/services.js` — Cisco platform mentions in unified-communications and contact-center services
|
||
- `src/components/layout/Footer.jsx` — Cisco in footer bottom section
|
||
- `package.json` — Version bumped to 0.5.6
|
||
|
||
### Verified
|
||
- ✅ `npm run build` passes clean
|
||
- ✅ Docker image builds successfully
|
||
- ✅ Container starts on port 3001 and responds to health check
|
||
- ✅ Home page loads at http://localhost:3001 — Cisco appears in trust signals
|
||
- ✅ Services page loads at http://localhost:3001/services
|
||
|
||
---
|
||
|
||
## Zoho P0/P1 Audit Fixes — 2026-05-17
|
||
|
||
### Fixed
|
||
- P0 #2: Added null check for Zoho credentials before forwarding (early return)
|
||
- P0 #3: Added response.ok check for Zoho API responses (log + return on failure)
|
||
- P0 #4: Confirmed Docker uses USER nodejs (no su-exec needed)
|
||
- P1 #5: Added 10-second timeout on Zoho fetch using AbortController
|
||
- P1 #6: UNIQUE constraint on email already handled by db.js migration
|
||
- P1 #10: Schema normalizes empty strings to null; Zoho payload uses `|| null`
|
||
|
||
### Files Modified
|
||
- `server/index.js` — Zoho forwarding logic (credential checks, response validation, timeout, payload normalization)
|
||
|
||
### Build
|
||
- ✅ `npm run build` passes
|
||
|
||
---
|
||
|
||
## v0.5.5 — Phase 5 Batch 8: Service Card Icons (Issue #35) — 2026-05-17
|
||
|
||
### Completed
|
||
- Services page updated with lucide-react icons matching Home page pattern
|
||
- 7 distinct icons: MessageCircle, Users, LifeBuoy, GraduationCap, LinkIcon, Wifi, Network
|
||
- Icon-switching logic in Services.jsx maps each service icon name to correct component
|
||
- No hardcoded lightning bolt — each service displays its own distinct icon
|
||
|
||
### Files Modified
|
||
- `src/pages/Services.jsx` — replaced hardcoded lightning bolt SVG with lucide-react imports and icon-switching logic
|
||
- `package.json` — version bumped to 0.5.5
|
||
|
||
### Verified
|
||
- ✅ `npm run build` passes clean
|
||
- ✅ Docker image builds successfully
|
||
- ✅ Container starts on port 3001 and responds to health check
|
||
- ✅ Services page loads at http://localhost:3001/services
|
||
- ✅ Each of the 7 service cards displays a distinct icon per services.js data
|
||
|
||
---
|
||
|
||
## v0.5.4 — Phase 5 Batch 7: Nav Active-State Styling — 2026-05-17
|
||
|
||
### Completed
|
||
- Header.jsx and MobileNav.jsx now use `useLocation` hook from React Router 7
|
||
- Active nav links display white text (`text-white`) with `font-semibold` styling
|
||
- `isActive()` helper function compares `location.pathname` to link href
|
||
- Both desktop and mobile navigation use consistent active-state styling
|
||
|
||
### Files Modified
|
||
- `src/components/layout/Header.jsx` — Added `useLocation` import and `isActive()` function, updated desktop nav and mobile nav link styling
|
||
- `src/components/layout/MobileNav.jsx` — Added `useLocation` import and `isActive()` function, updated all nav links with active-state styling
|
||
- `package.json` — Version bumped to 0.5.4
|
||
|
||
### Verified
|
||
- ✅ `npm run build` passes clean
|
||
- ✅ Docker image builds successfully
|
||
- ✅ Container starts on port 3001 and responds to health check
|
||
- ✅ Home page loads at http://localhost:3001
|
||
- ✅ Both Header.jsx and MobileNav.jsx import `useLocation` from `react-router-dom`
|
||
- ✅ `isActive()` function correctly compares `location.pathname` to link href
|
||
- ✅ Active links display white text with font-semibold styling
|
||
|
||
---
|
||
|
||
## v0.5.3 — Phase 5 Batch 6: Footer + Contact Improvements — 2026-05-17
|
||
|
||
### Completed
|
||
- Footer cleanup: email (`info@queuenorth.com`), phone (`(906) 482-6616`), and CTA (`Request Consultation`) all present
|
||
- Contact page hero: prominent phone/email display + CTA button
|
||
- Home page CTA links added to Trust Signals, Services, Why Queue North sections
|
||
- Minor updates to Support page
|
||
|
||
### Files Modified
|
||
- `src/components/layout/Footer.jsx` — Footer cleanup
|
||
- `src/pages/Home.jsx` — CTA links in sections
|
||
- `src/pages/Contact.jsx` — Hero CTA
|
||
- `src/pages/Support.jsx` — Minor updates
|
||
- `package.json` — Version bumped to 0.5.3
|
||
|
||
### Verified
|
||
- `npm run build` passes
|
||
- Docker image builds and container starts on port 3001
|
||
- Footer renders with email, phone, and CTA
|
||
- Contact page hero displays phone/email and Request Consultation button
|
||
|
||
---
|
||
|
||
## v0.5.2 — Phase 5 Batch 5: Why Queue North Section Refinement — 2026-05-17
|
||
|
||
### Completed
|
||
- Replaced generic "Architecture/Deployment/Lifecycle" pillars with 4 concrete differentiators
|
||
- Added lucide-react icons: Headphones, UserCheck, Activity, ShieldCheck
|
||
- Changed grid from 3-col to 4-col responsive layout (1 col mobile, 2 col tablet, 4 col desktop)
|
||
- Removed unused CardDescription import
|
||
- Updated Why Queue North section subtitle to "Four concrete differentiators that set us apart"
|
||
- Clean card layout with centered icon containers and consistent styling
|
||
|
||
### Files Modified
|
||
- `src/pages/Home.jsx` — Why Queue North section complete redesign
|
||
- `package.json` — Version bumped to 0.5.2
|
||
|
||
### Verified
|
||
- `npm run build` passes
|
||
- Docker image builds and container starts
|
||
- Home page loads on http://localhost:3001
|
||
- Responsive layout verified (1/2/4 columns as specified)
|
||
|
||
---
|
||
|
||
## v0.5.1 — Phase 5 Batch 1: Hamburger Menu Fix + DialogTitle A11y — 2026-05-13
|
||
|
||
### Verified Complete
|
||
- Fixed SheetContent missing positional CSS classes (panel was zero-size)
|
||
- Installed and configured tailwindcss-animate
|
||
- Added visually hidden SheetTitle for accessibility (Radix DialogTitle requirement)
|
||
- Forgejo issues #22, #23 closed
|
||
|
||
---
|
||
|
||
## v0.5.4 — Phase 5 Batch 4: Services Rewrite — Business Outcomes — 2026-05-17
|
||
|
||
### Completed
|
||
- Section title changed: "Our Services" → "What We Handle"
|
||
- Subtitle changed to outcome-focused: "From phones to firewalls, we keep your business running"
|
||
- Added `homeDesc` field to all 7 services in `src/data/services.js` with business-outcome language
|
||
- Service cards now show: icon + name + homeDesc (outcome) + shortDesc (supporting) + CTA
|
||
- lucide-react icons per service: MessageCircle, Users, LifeBuoy, GraduationCap, LinkIcon, Wifi, Network
|
||
- B2B professional card layout with icon containers (primary-navy/10 bg)
|
||
- Service detail pages (ServiceDetail.jsx) unchanged
|
||
|
||
### Files Modified
|
||
- `src/pages/Home.jsx` — Services section rewrite
|
||
- `src/data/services.js` — Added homeDesc field to each service
|
||
- `FUTURE.md` — Marked batch 0.5.4 complete
|
||
|
||
---
|
||
|
||
## v0.5.3 — Phase 5 Batch 3: Trust Signals Section — 2026-05-17
|
||
|
||
### Completed
|
||
- Added dedicated Trust Signals section immediately after hero section
|
||
- 8x8 Certified Partner badge: prominent white card with logo, certification number, and descriptive text
|
||
- Veteran Owned badge: VCERT verified with certification #, values-based messaging
|
||
- "25+ Years Experience": metric card with 25+ using Georgia font (font-numeric class)
|
||
- Metrics grid: 4 key stats displayed (99.99% uptime, <15m response, 24/7 support, 100% satisfaction)
|
||
- Clean card layout: white cards on section-alt background with hover effects
|
||
- Mobile-first: three-column grid on desktop, stacked on mobile
|
||
- Business outcomes focus: reliability, experience, support承诺, not technical jargon
|
||
|
||
### Files Modified
|
||
- `src/pages/Home.jsx` — Added Trust Signals section after hero
|
||
- `FUTURE.md` — Marked batch 0.5.3 complete
|
||
|
||
### Notes for Next Agent
|
||
- The trust signals are now front and center after the hero, before services
|
||
- Georgia font (`font-numeric`) consistently used for all metrics
|
||
- All metrics use realistic placeholder values that can be replaced with real stats when available
|
||
- The white card design provides contrast against the section-alt background
|
||
|
||
---
|
||
|
||
## v0.5.2 — Phase 5 Batch 2: Hero Section Rewrite — 2026-05-17
|
||
|
||
### Completed
|
||
- Hero headline replaced: "Modern Communications Infrastructure Without the Vendor Noise" → "Reliable Business Communications — Without the Runaround"
|
||
- Subtext updated to emphasize trust, reliability, and business outcomes
|
||
- 8x8 Certified Partner badge made prominent (featured card with logo, not small pill)
|
||
- Trust signals immediately visible on mobile without scrolling
|
||
- CTAs updated: "Schedule Consultation" (primary, /contact), "View Services" (secondary, /services)
|
||
- Color palette evolved: navy base with teal-900 gradient accent
|
||
- Mobile-first design verified — trust signals visible above fold on phone viewports
|
||
|
||
### Files Modified
|
||
- `src/pages/Home.jsx` — Hero section rewrite
|
||
- `.learnings/scarlett/LEARNINGS.md` — Added hero rewrite entry
|
||
- `FUTURE.md` — Marked batch 0.5.2 complete
|
||
|
||
### Notes for Next Agent
|
||
- The 8x8 badge is now prominently featured — keep this prominence in future sections
|
||
- Georgia font (`font-numeric`) used for "25+ Years Experience" — consistent with design rule
|
||
- Consider similar trust signal presentation in the Trust Bar section
|
||
|
||
---
|
||
|
||
## v0.4.8 — Phase 4 Batch 8: Error Handling Hardening + Dep Audit — 2026-05-13
|
||
|
||
### Verified Complete
|
||
- Global error handlers: uncaughtException + unhandledRejection → log + exit(1)
|
||
- 404 catch-all for `/api/*` routes returns `{ error: 'Not found' }`
|
||
- Health check enhanced: DB connectivity check, returns `{ status: 'ok', db: 'ok' }` or 503
|
||
- Request timeout: 30-second middleware, 504 response
|
||
- Consistent error format: 400/404/429/500/503/504 all follow `{ error, fields? }` pattern
|
||
- `npm audit`: 0 vulnerabilities
|
||
- `npm outdated`: major updates noted (Express 5, Vite 8, etc.) but not upgraded
|
||
|
||
## v0.4.7 — Phase 4 Batch 7: Rate Limiting + Security Headers + CORS — 2026-05-13
|
||
|
||
### Verified Complete
|
||
- express-rate-limit: 5 req/min per IP on /api/leads and /api/support, configurable via RATE_LIMIT_PER_MINUTE
|
||
- helmet: CSP (scripts 'self', styles 'self' inline, fonts 'self' + Google Fonts), HSTS, X-Content-Type-Options, X-Frame-Options DENY
|
||
- cors: configurable via CORS_ORIGIN env var, credentials enabled, API routes only
|
||
- Trust proxy enabled for correct client IP behind Docker/reverse proxy
|
||
- Rate limit returns 429 with JSON error + retryAfter
|
||
- All security headers confirmed via curl
|
||
- Docker config updated: all new env vars in Dockerfile and docker-compose.yml
|
||
|
||
## v0.4.6 — Phase 4 Batch 6: Zoho Forwarding Layer — 2026-05-13
|
||
|
||
### Verified Complete
|
||
- Zoho CRM forwarding via REST API v8 (POST /crm/v8/Leads)
|
||
- OAuth2 token management: refresh token → access token, in-memory cache with auto-refresh
|
||
- Fire-and-forget: Zoho forwarding is async, never blocks form response
|
||
- Configurable via 6 env vars: ZOHO_ENABLED, ZOHO_API_DOMAIN, ZOHO_CLIENT_ID, ZOHO_CLIENT_SECRET, ZOHO_REFRESH_TOKEN, ZOHO_REDIRECT_URI
|
||
- Best-effort design: Zoho failures logged but never propagate to client
|
||
- ZOHO_ENABLED=false: no Zoho activity, form submits normally
|
||
- ZOHO_ENABLED=true with invalid creds: form still succeeds, Zoho errors logged to console
|
||
|
||
## v0.4.5 — Phase 4 Batch 5: Server-Side Validation + Input Sanitization — 2026-05-13
|
||
|
||
### Verified Complete
|
||
- Zod schemas updated with `.trim()` and `.max()` on all fields
|
||
- Input sanitization: strip HTML/script tags, truncate to max lengths
|
||
- Request body size limit: 1MB (returns 413)
|
||
- Validation errors formatted as `{ error, fields }` — no stack traces
|
||
- All curl tests pass: valid data, empty fields, invalid email, XSS, max length, short issue
|
||
- XSS test confirmed: `<script>` and `<b>` tags stripped from stored data
|
||
|
||
## v0.4.4 — Phase 4 Batch 4: Client-Side Validation + Sonner Feedback — 2026-05-13
|
||
|
||
### Verified Complete
|
||
- Contact form: required field validation (company, name, email, message)
|
||
- Support form: required field validation (name, company, email, issue)
|
||
- Email format validation with regex on both forms
|
||
- Issue minimum length validation (10 chars) on Support form
|
||
- Inline error messages below each invalid field (red text, small)
|
||
- Sonner toast for validation errors on submit attempt
|
||
- Error clearing as user corrects fields (onChange)
|
||
- Red ring/border on Input/Textarea when field has validation error
|
||
- Form inputs disabled during submission (mutation.isPending)
|
||
- Form opacity reduced to 70% during submission
|
||
- Build passes clean
|
||
|
||
## v0.4.1 — Phase 4 Batch 1: Contact Form Wired to Express — 2026-05-12
|
||
|
||
### Verified Complete
|
||
- Contact form submits all fields to `/api/leads` via TanStack Query mutation
|
||
- Success/error response handling with Sonner toasts
|
||
- Confirmation shown on success, form resets
|
||
- Error state shown on failure
|
||
- Backend Zod validation + SQLite storage working
|
||
- Already implemented in Phase 1/2, verified end-to-end
|
||
|
||
## v0.2.13-fix — Phase 2 Fix: Legacy CSS Consolidation — 2026-05-13
|
||
|
||
### Fixed
|
||
- Removed duplicate `src/index.css` (was not imported anywhere)
|
||
- Created clean `src/index.css` as single Tailwind entry point
|
||
- Moved `maxWidth.container: 1280px` into `tailwind.config.js` theme extensions
|
||
- Updated `src/App.jsx` import from `./App.css` to `./index.css`
|
||
- Deleted redundant `src/App.css`
|
||
- All `bg-section-alt` usages verified still working
|
||
|
||
### Verified
|
||
- `npm run build` passes
|
||
- All 14 Phase 2 batches now fully verified
|
||
|
||
---
|
||
|
||
## v0.4.3 — Phase 4 Batch 3: SQLite Persistence Verification — 2026-05-13
|
||
|
||
### Verified Complete
|
||
- Database file (`db/queuenorth.db`) created on first run if missing
|
||
- `leads` and `support_requests` tables have correct schema (all columns match server/index.js)
|
||
- Data persists across server restarts
|
||
- Docker volume test: stop container, restart, confirm data present
|
||
- Health check endpoint responds correctly
|
||
- `docker-test.sh` persistence suite passes all checks
|
||
|
||
## v0.4.2 — Phase 4 Batch 2: Support Form Wired to Express — 2026-05-12
|
||
|
||
### Verified Complete
|
||
- Support form submits all fields to `/api/support` via TanStack Query mutation
|
||
- Success/error response handling with Sonner toasts
|
||
- Confirmation shown on success, form resets
|
||
- Error state shown on failure
|
||
- Backend Zod validation + SQLite storage working
|
||
- Already implemented in Phase 1/2, verified end-to-end
|
||
|
||
---
|
||
|
||
## v0.2.2 — Docker Image Size Fix — 2026-05-12
|
||
|
||
### Fixed
|
||
- Removed duplicate `node_modules` copy in Dockerfile runner stage (was copying full dev+prod modules as a permanent layer)
|
||
- Reduced image size from 331MB to 215MB (35% reduction)
|
||
- `npm ci --omit=dev` now runs cleanly without pre-existing dev modules
|
||
|
||
### Verified
|
||
- Docker build succeeds
|
||
- Container starts and health check passes
|
||
- Frontend serves correctly
|
||
|
||
---
|
||
|
||
# Queue-North-Website — Changelog
|
||
|
||
## v0.3.4 — Phase 3 Visual Overhaul Complete — 2026-05-12
|
||
|
||
### Completed Batches
|
||
- **Batch 1 (v0.3.1):** Tailwind theme tokens, spacing scale, container width, Inter font
|
||
- **Batch 2 (v0.3.2):** Home page redesign — hero, trust bar, services grid, why QN pillars, industries preview, final CTA
|
||
- **Batch 3 (v0.3.3):** Header/footer/mobilenav polish, navy-light color token, fixed Button.jsx TS generics build bug
|
||
- **Batch 4 (v0.3.4):** Inner pages layout system — consistent hero/card/CTA pattern across About, Services, ServiceDetail, Industries, IndustryDetail, 8x8
|
||
- **Batch 5:** Contact/Support forms verified compliant, no changes needed
|
||
|
||
### Verified
|
||
- All batches build successfully
|
||
- Contact and Support pages already compliant with OVERHAUL_PLAN.md
|
||
- No `asChild` usage on Button (unsupported, replaced with styled anchors)
|
||
|
||
---
|
||
|
||
## v0.3.2 — Phase 3 Batch 2: Home Page Redesign — 2026-05-12
|
||
|
||
### Changed
|
||
- Updated Home.jsx to import `industries` data from data file
|
||
- Replaced hardcoded industry data with dynamic rendering from `industries.js`
|
||
- Updated Services and Industries cards to use Button component for "Learn more" links
|
||
- Added MapPin icon from lucide-react for industry cards
|
||
- Added useNavigate hook for programmatic navigation
|
||
- Ensured consistent use of shadcn/ui Button component across the home page
|
||
- Updated version to `0.3.2` for Phase 3 Batch 2
|
||
|
||
### Verified
|
||
- `npm run build` passes
|
||
- All routes respond correctly
|
||
- Button components render with correct styling
|
||
- Responsive layout works on mobile and desktop
|
||
|
||
---
|
||
|
||
## v0.3.1 — Phase 3 Batch 1: Theme + Tailwind Config + Typography — 2026-05-12
|
||
|
||
### Changed
|
||
- Enhanced Tailwind spacing scale with utility values (24-48rem)
|
||
- Updated container max-width to 1280px per design spec
|
||
- Confirmed color palette, Inter font, Georgia numeric font already in place
|
||
|
||
---
|
||
|
||
## v0.1.1 — Phase 1 Agent-Pass Checkpoint — 2026-05-12
|
||
|
||
### Changed
|
||
- Corrected Phase 1 version to reflect completed agent-pass checkpoint semantics.
|
||
- Documented that every completed agent pass/checkpoint within a phase increments the patch version.
|
||
- Confirmed Phase 1 verified output is pushed to `dev` at `0.1.1`.
|
||
|
||
### Verified
|
||
- `npm run build` passes.
|
||
- Backend health endpoint responds successfully at `/api/health`.
|
||
|
||
## v0.2.1 — Dockerization — 2026-05-12
|
||
|
||
### Added
|
||
- Docker build for production deployment
|
||
- docker-compose.yml for local development with SQLite persistence
|
||
- npm scripts: `docker:build`, `docker:run`, `docker:compose:up/down/logs`
|
||
- Health check in Dockerfile and docker-compose
|
||
- Non-root `nodejs` user for security
|
||
- Layer caching optimization (copy package.json first, install, then copy source)
|
||
- `.dockerignore` to exclude node_modules, dist, db, logs, git, private docs
|
||
|
||
### Changed
|
||
- Updated package.json to `0.2.1` for Docker batch
|
||
|
||
### Verified
|
||
- Docker build succeeds with `npm run docker:build`
|
||
- Container starts and health check passes
|
||
- SQLite database persists in `./db` volume
|
||
|
||
---
|
||
|
||
## v0.2.0 — Phase 2 Layout Complete — 2026-05-12
|
||
|
||
### Added
|
||
- All route pages implemented (Home, About, Services, ServiceDetail, Industries, IndustryDetail, 8x8, Contact, Support).
|
||
- Data files for services and industries moved to `/src/data/`.
|
||
- Layout components (Header, Footer, MobileNav) built and integrated.
|
||
- Legacy `styles.css` removed (replaced by Tailwind configuration).
|
||
- Contact and support forms wired to Express backend via TanStack Query.
|
||
- All pages render meaningful content matching original business information.
|
||
- Version bumped to `0.2.0` for Phase 2 baseline.
|
||
|
||
### Changed
|
||
- Replaced hash-based routing with React Router 7.
|
||
- Standardized layout patterns across all pages (page hero, main content, sidebar, CTA band).
|
||
- Updated package.json, PROJECT.md, OVERHAUL_PLAN.md, HISTORY.md to reflect Phase 2 status.
|
||
- Overhaul plan updated to note Phase 2 scope and goals.
|
||
|
||
### Verified
|
||
- `npm run build` passes.
|
||
- All routes respond correctly.
|
||
- Contact form submits via `/api/leads`.
|
||
- Support form submits via `/api/support`.
|
||
- No references to legacy `styles.css` remain in source code.
|
||
|
||
## v0.1.0 — Phase 1 Foundation — 2026-05-12
|
||
|
||
### Added
|
||
- Rebuilt project foundation on Vite + React SPA with React Router.
|
||
- Added Tailwind CSS with Queue North light-first business palette.
|
||
- Added shadcn/ui-style local primitives for buttons, cards, inputs, textarea, select, badge, sheet, and dialog usage.
|
||
- Added Sonner toast support and TanStack Query provider/API helper.
|
||
- Added Express backend with `/api/health`, `/api/leads`, and `/api/support`.
|
||
- Added better-sqlite3 storage for `leads` and `support_requests`.
|
||
- Added all planned frontend routes for home, about, services, service details, industries, industry details, 8x8, contact, and support.
|
||
- Added Phase 1 documentation, build summary, script reference, and phase-based versioning rules.
|
||
|
||
### Changed
|
||
- Replaced the static HTML/CSS/JS entry with the Vite React entry.
|
||
- Updated README to point to `OVERHAUL_PLAN.md` as the design source of truth.
|
||
- Standardized versioning so Phase 1 uses `0.1.x`, Phase 2 uses `0.2.x`, and later phases follow the same pattern.
|
||
- Added Bishop verification rules and the requirement that Ripley pushes to `dev` after each verified phase.
|
||
|
||
### Verified
|
||
- `npm run build` passes.
|
||
- Backend health endpoint responds successfully at `/api/health`.
|
||
- Required routes are configured.
|
||
- Contact and support API paths exist and write through SQLite.
|
||
|
||
## v0.0.1 — Project Initialization — 2026-05-11
|
||
|
||
### Added
|
||
- Project initialized with PROJECT.md, STRUCTURE.md, FUTURE.md, HISTORY.md, DEVELOPMENT_LOG.md.
|
||
|
||
---
|
||
|
||
## From `DEVELOPMENT_LOG.md` — the agent-activity log, verbatim
|
||
|
||
> Migrated 2026-08-18. Newest first, as written. Not edited. Its entries
|
||
> overlap the changelog above in date but not in content: that file recorded
|
||
> what shipped, this one recorded what each agent did and verified.
|
||
|
||
|
||
## v0.6.0 — Navigation Active State Styling (Issue #38) — 2026-05-17
|
||
|
||
**Scarlett** — Nav Active-State Styling Implementation
|
||
|
||
**Status**: Already implemented in Header.jsx (no changes required)
|
||
|
||
**Implementation Details**:
|
||
- `useLocation` from `react-router-dom` v7.1.3 is properly imported
|
||
- `isActive(href)` helper compares `location.pathname === href`
|
||
- Desktop nav: Active links show `text-white underline underline-offset-4`, inactive use `text-white/70 hover:text-white`
|
||
- Mobile Sheet nav: Active links use `text-white font-semibold`, inactive use `text-white/70 hover:text-white`
|
||
- Sub-links (Services/Industries) use brand color `text-navy-light` (#68A3B8) for visual hierarchy
|
||
|
||
**Routes supported**: `/`, `/services`, `/industries`, `/about`, `/contact`, `/support` and sub-routes
|
||
|
||
**Notes**: Implementation was complete prior to this batch. All navigation links correctly indicate current page with clear visual feedback for both desktop and mobile views. No issues found.
|
||
|
||
---
|
||
|
||
## v0.4.8 — Error Handling Hardening + Dep Audit Batch — 2026-05-13
|
||
|
||
**Neo** — Server-side error handling hardening
|
||
- Added global error handlers:
|
||
- `process.on('uncaughtException')`: logs error and exits with code 1
|
||
- `process.on('unhandledRejection')`: logs error and exits with code 1
|
||
- Both use `log.error()` for consistent timestamped logging
|
||
- Added 404 catch-all middleware for API routes:
|
||
- Returns `{ error: 'Not found' }` for unmatched `/api/*` routes
|
||
- Non-API routes fall through to SPA (express.static)
|
||
- Logs warnings for API 404s
|
||
- Enhanced health check (`/api/health`):
|
||
- Now verifies DB connection with `db.prepare('SELECT 1').get()`
|
||
- Returns `{ status: 'ok', db: 'ok' }` on success
|
||
- Returns 503 with `{ error: 'Service unavailable', db: 'error' }` on DB failure
|
||
- Added 30-second request timeout middleware:
|
||
- Returns 504 with `{ error: 'Request timeout' }` for slow requests
|
||
- Logs timeout events with `log.warn()`
|
||
- Verified consistent error response format:
|
||
- 400: `{ error: string, fields: object }` (validation errors)
|
||
- 404: `{ error: 'Not found' }`
|
||
- 429: `{ error: 'Too Many Requests', message: string, retryAfter: number }`
|
||
- 500: `{ error: string }` (no stack traces leaked)
|
||
- 503: `{ error: 'Service unavailable', db: 'error' }`
|
||
- 504: `{ error: 'Request timeout' }`
|
||
- Dependency audit: `npm audit` → 0 vulnerabilities
|
||
- Dependency review: `npm outdated` → Major updates noted (Express 5, Vite 8, Tailwind 4, etc.) but not upgraded per batch instructions
|
||
- All client-side code preserved (no changes to React components)
|
||
|
||
**Verification**
|
||
- Server starts successfully with all new middleware logged
|
||
- Build passes: `npm run build`
|
||
- Health endpoint returns DB status correctly
|
||
- API 404s return consistent JSON response
|
||
- Timeout middleware active (30s limit)
|
||
|
||
## v0.4.4 — Client-Side Validation + Sonner Feedback Batch — 2026-05-13
|
||
|
||
**Scarlett** — Client-side form validation implementation
|
||
- Added client-side validation to Contact.jsx:
|
||
- Required field validation: company, name, email, message
|
||
- Email format validation with regex
|
||
- Inline error messages below each invalid field (red text, small)
|
||
- Sonner toast for validation errors on submit attempt
|
||
- Error clearing as user types (on change)
|
||
- Red ring/border on Input/Textarea when field has validation error
|
||
|
||
- Added client-side validation to Support.jsx:
|
||
- Required field validation: name, company, email, issue
|
||
- Email format validation with regex
|
||
- Issue minimum length validation (10 chars — matches server-side Zod rule)
|
||
- Inline error messages below each invalid field
|
||
- Sonner toast for validation errors on submit attempt
|
||
- Error clearing as user types (on change)
|
||
- Red ring/border on Input/Textarea when field has validation error
|
||
|
||
- Added loading state UI:
|
||
- Form inputs disabled during submission (`mutation.isPending`)
|
||
- Form opacity reduced to 70% during submission
|
||
- Submit button already shows "Submitting..." text
|
||
|
||
**Verification**
|
||
- `npm run build` passes without errors
|
||
- Both forms show validation errors when submitting empty form
|
||
- Invalid email addresses trigger email field error
|
||
- Valid submissions trigger success toast
|
||
- Server errors trigger error toast
|
||
- Error fields clear when user corrects them
|
||
|
||
---
|
||
|
||
## v0.4.6 — Security Hardening Batch (Rate Limiting, Helmet, CORS) — 2026-05-13
|
||
|
||
**Private_Hudson** — Security hardening and environment variable configuration
|
||
- Installed security packages: `express-rate-limit`, `helmet`, `cors`
|
||
- Added rate limiting to `/api` endpoints:
|
||
- 5 requests per minute (configurable via `RATE_LIMIT_PER_MINUTE`)
|
||
- IPv6-safe key generator
|
||
- Standard rate limit headers (`X-RateLimit-Remaining`, `X-RateLimit-Reset`)
|
||
- 429 response with retry-after information
|
||
- Added Helmet security headers:
|
||
- Content Security Policy (CSP) configured with strict default-src, script-src, style-src
|
||
- HSTS enabled with 1-year max-age and includeSubDomains
|
||
- Frameguard set to DENY
|
||
- X-Content-Type-Options nosniff
|
||
- X-Frame-Options DENY
|
||
- X-XSS-Protection enabled
|
||
- Referrer-Policy set to same-origin
|
||
- Hide Powered-By header
|
||
- Added CORS configuration:
|
||
- Configurable via `CORS_ORIGIN` env var (default: `*` for development)
|
||
- Credentials enabled for authenticated requests
|
||
- Exposed rate limit headers
|
||
- Updated server startup logging to show security config status
|
||
- Request body size limit (`1mb`) already in place
|
||
|
||
**Docker Configuration Updates**
|
||
- Added new environment variables to `docker-compose.yml`:
|
||
- `RATE_LIMIT_PER_MINUTE=5`
|
||
- `CORS_ORIGIN=https://queuenorth.com` (prod-safe default)
|
||
- `LOG_LEVEL=info`
|
||
- `ZOHO_ENABLED=false` (explicit off for non-CRM deployments)
|
||
- `ZOHO_API_DOMAIN`, `ZOHO_CLIENT_ID`, `ZOHO_CLIENT_SECRET`, `ZOHO_REFRESH_TOKEN`, `ZOHO_REDIRECT_URI`
|
||
- Added environment defaults to `Dockerfile`:
|
||
- All new env vars with reasonable defaults
|
||
- `CORS_ORIGIN=\*` (development default)
|
||
- `LOG_LEVEL=info`
|
||
- `ZOHO_ENABLED=false`
|
||
- `docker-entrypoint.sh` verified — no changes required (env vars passed through automatically)
|
||
|
||
**Verification**
|
||
- `npm run build` passes without errors
|
||
- Server starts with security headers logged:
|
||
- `[Security] Helmet enabled with CSP configured`
|
||
- `[CORS] Enabled with origin: https://queuenorth.com`
|
||
- `Rate limiting: 5 requests per 60 seconds`
|
||
- Rate limiting headers present on API responses
|
||
- All security headers confirmed via `curl -I http://localhost:3001/api/health`
|
||
|
||
---
|
||
|
||
## v0.4.5 — Server-Side Validation + Input Sanitization Batch — 2026-05-13
|
||
|
||
**Neo** — Server-side validation hardening and input sanitization
|
||
- Added request body size limit: `express.json({ limit: '1mb' })` — returns 413 for oversized payloads
|
||
- Added input sanitization after Zod validation (prevents XSS, trims whitespace, enforces max lengths at DB level):
|
||
- `sanitizeString()` helper: trims whitespace, removes HTML/script tags, truncates to max length
|
||
- `sanitizePayload()` applies sanitization to validated data before DB insert
|
||
- Updated Zod schemas with comprehensive validation:
|
||
- All string fields: `.trim()` applied
|
||
- All string fields: `.max()` limits applied per RFC standards:
|
||
- `company`: 200 chars
|
||
- `name`: 100 chars
|
||
- `email`: 254 chars (RFC 5321)
|
||
- Updated error responses to match v0.4.6 error format standards:
|
||
- 400: `{ error: 'Validation failed', fields: object }`
|
||
- 500: `{ error: 'Failed to submit lead/support' }`
|
||
|
||
**Verification**
|
||
- `npm run build` passes without errors
|
||
- Zod validation correctly rejects invalid inputs
|
||
- Input sanitization trims whitespace and removes HTML tags
|
||
- Max length limits enforced at DB level
|
||
|
||
---
|
||
|
||
## v0.4.0 — Phase 4 Baseline — 2026-05-13
|
||
|
||
**Ripley** — Phase 4 initialization
|
||
- Updated `package.json` version to `0.4.0`
|
||
- Updated `PROJECT.md` to indicate Phase 4 active
|
||
- Updated `HISTORY.md` with v0.4.0 release notes
|
||
- Updated `FUTURE.md` with Phase 4 roadmap
|
||
- Updated `OVERHAUL_PLAN.md` with Phase 4 task breakdown
|
||
|
||
**Phase 4 Scope**
|
||
- v0.4.1-0.4.7: Incremental hardening batches (security, validation, error handling)
|
||
- v0.4.8: Final hardening batch (global error handlers, health check, dependency audit)
|
||
- Target: Production-ready, audited server with comprehensive error handling
|
||
|
||
---
|
||
|
||
## v0.0.1 — 2026-05-11
|
||
|
||
**Ripley** — Project initialized
|
||
- Created project directory at `/home/kaspa/.openclaw/Projects/Queue-North-Website/`.
|
||
- Set up initial PROJECT.md, STRUCTURE.md, FUTURE.md, HISTORY.md, DEVELOPMENT_LOG.md.
|