尚未偵測到更新。GitHub 發布版本後會顯示在這裡。
個人 GitHub 帳號下公開儲存庫的擁有者可以登入並驗證所有權,自行管理介紹,無需管理員核准。
登入並產生驗證檔案,在 30 分鐘內將其提交至此儲存庫的預設分支。其他分支或分叉儲存庫中的檔案無效。然後在下方檢查檔案,即可無需管理員核准直接管理專案。請保留檔案以維持管理權限。
如需登入,將前往登入頁面。登入後請回到此處,再次點擊所選的驗證按鈕。
在專案資料夾中執行 /nomorevibe verify,即可驗證所有權並自行管理介紹。
一分鐘完成驗證製作者的介紹僅有上面的一句話。 更多詳情請閱讀儲存庫 README。 儲存庫 README ↗
正在載入完整 README。
檢視完整 README ↗This file exists so anyone picking up this repo cold — a new person, or a new AI chat session — can be useful within a couple of minutes instead of asking the same six questions every time. Read it before touching anything.
subtradesoftware.com is a Next.js 14 App Router site for SubTrade Software
Ltd., Calgary, Alberta — field management software for trade subcontractors.
It lives in this repo, deploys itself to Vercel, and is maintained by Fabian.
1. Never publish the street address. The company location is written as "Calgary, Alberta" or "Calgary, AB" and nothing more — no street, no postal code — in the footer, in structured data, in blog posts, in schema markup, anywhere on the site or in anything generated from it. If you find a street address anywhere in this repo, that is a bug; remove it.
2. Hand Fabian the files. Do not drive his browser. When a change is requested, produce the finished file or files and deliver them for him to upload himself. Browser automation against GitHub or Vercel happens only when he explicitly says so in that specific conversation, and that permission expires with the task it was given for. It does not carry forward.
The Next.js project is not at the repo root. It is one level down:
subtrade-site/ <- the repo root (this README lives here)
└── subtrade-site/ <- the Next.js app. Everything below is inside this.
├── app/ <- App Router pages, one folder per URL
├── components/ <- shared React components
├── content/posts/ <- blog posts, as Markdown files
├── lib/ <- data and logic, no JSX
├── public/ <- images, served from /
├── next.config.mjs <- trailing slashes + every 301 redirect
└── package.json
Nearly every mistake a new session makes starts with forgetting the nested
folder. git clone then cd subtrade-site/subtrade-site before npm install.
To get the current code:
git clone https://github.com/fabian9511/subtrade-site.git
cd subtrade-site/subtrade-site
npm install
npm run dev
The repo is public, so cloning it needs no credentials and takes seconds. Do that rather than guessing at what a file contains.
Vercel watches main. Every commit to main deploys to production
automatically. There is no separate deploy step, no build command to run, no
dashboard button to press. Commit the file on GitHub and the live site updates
in one to two minutes.
Practical consequence: a commit is a publish. There is no staging environment, so check the build locally before handing over a file.
DNS points at Vercel (A @ → 216.198.79.1, CNAME www → *.vercel-dns-017.com).
Mail records are not managed here and must not be touched. The customer app at
portal.subtradesoftware.com is a separate system on AWS — this repo has
nothing to do with logins, and links to the product point at that hostname.
This is the part that used to require writing React. It does not any more.
subtrade-site/content/posts/TEMPLATE.md.progress-billing-holdback-canada.md publishes at
/blog/progress-billing-holdback-canada/. Lowercase, hyphens, no spaces.--- lines at the top. title,
description and date are the ones that matter; date must be
YYYY-MM-DD or the sort order breaks.subtrade-site/content/posts/ on GitHub and commit.The post page, the card on /blog/, the "On this page" sidebar and the
sitemap entry all generate themselves from that one file. No other file needs
editing to publish. TEMPLATE.md documents every supported piece of
formatting and is skipped by the build, so it never appears on the site.
Add draft: true to the front matter to keep a post in the repo but off the
live site.
Pictures. Upload a roughly 1200px-wide .webp to subtrade-site/public/,
name the file after what is in it, and point at it from the front matter with
image: "/that-filename.webp" plus a one-sentence imageAlt:. That one line
puts a hero image at the top of the article, a thumbnail on the blog index card
and a preview image on shared links, and feeds the BlogPosting structured
data. Posts without an image: render exactly as before and get a plain
branded panel on their index card, so the grid stays even. An optional
cardImage: overrides just the index thumbnail. TEMPLATE.md spells all of
this out.
The two original guides — the "ultimate guide" and the "best software"
comparison — are still hand-built React pages at their own root-level URLs.
That is deliberate: Google has those URLs indexed, and moving them under
/blog/ would throw away that history for nothing. They appear on the blog
index via legacyPosts in lib/posts.js. Leave them where they are.
Fabian edits this repo through GitHub's web interface. That works well for
source files and not at all for package-lock.json, so adding an npm package
is expensive and should be treated as a last resort.
This is why the Markdown renderer (lib/markdown.js), the YAML front-matter
parser (lib/posts.js) and the PDF writer (lib/holdbackPdf.js) are all
hand-rolled rather than pulled from npm. They are small, they are commented,
and they are the reason publishing works through a browser upload. Before
reaching for a library, check whether ninety lines of plain JavaScript will do
the job — for this site, it usually will.
| Thing | File |
|---|---|
| Markdown → HTML renderer | lib/markdown.js |
Reads content/posts/, parses front matter | lib/posts.js |
| Blog post page template | app/blog/[slug]/page.jsx |
| Blog index | app/blog/page.jsx |
| Canadian holdback rates and math | lib/holdback.js |
| US retainage rules, 50 states + DC | lib/retainage.js |
| The PDF export both calculators use | lib/holdbackPdf.js |
| Features, trades, comparisons, signup URL | lib/data.js |
| Tutorial library content | lib/tutorials.js |
| Every 301 redirect from the WordPress days | next.config.mjs |
| Sitemap (posts add themselves) | app/sitemap.js |
| All styling, one file | app/globals.css |
The two free calculators are at /construction-holdback-calculator/ (Canadian
statutory holdback, five provinces) and /construction-retainage-calculator/
(US retainage, all fifty states and DC). Both run entirely in the browser, need
no signup, and export a one-page PDF summary. They share their PDF writer and
their component shell, but the rules engines are deliberately separate because
Canadian holdback and US retainage are different legal animals.
Trailing slashes are on (trailingSlash: true). Every internal URL ends in
/. This matches the URLs the old WordPress site had indexed, so nothing that
Google knows about got redirected at launch. Do not turn this off.
This is not a static export. next.config.mjs uses async redirects(),
which output: 'export' does not support. So next build && next start is the
way to verify locally — npx serve out will not work because there is no out.
Pages are server components by default. Anything needing state or browser
APIs lives in components/ with 'use client' at the top. lib/posts.js
touches the filesystem and must only ever be imported by server components.
Styling is one file. app/globals.css is long and organised into commented
sections. There is no Tailwind, no CSS modules, no component library. Match the
existing section conventions rather than inventing a new pattern.
Mobile grid columns need minmax(0, 1fr), not 1fr. A plain 1fr refuses
to shrink below its content's intrinsic width, so one wide table can stretch
the entire page sideways on a phone. This has bitten the article layout once
already.
cd subtrade-site/subtrade-site
npx next build # must be clean
npx next start -p 4400 # then check the pages you touched
Worth checking every time:
curl -s localhost:4400/sitemap.xml lists any new URLNever run pkill -f next from a tool-driven shell. The pattern matches the
shell's own command line and it kills the process doing the killing.
It was a WordPress site on Hostinger. It was rebuilt as this Next.js app and
moved to Vercel with URL parity as the hard requirement — hence the trailing
slashes and the long redirect table in next.config.mjs, which maps every old
WordPress URL to its nearest surviving page. DNS was cut over to Vercel; mail
was left alone; the customer portal on AWS was never touched, so no user's login
was affected. The old Rank Math sitemap_index.xml was removed from Search
Console and the new sitemap.xml submitted in its place.
If a similar migration comes up for another site, that is the shape of it.