During Docker build the `db` host doesn't exist in the build network,
so any attempt to query Postgres in generateStaticParams fails. With the
try/catch, blog pages fall back to [] (rendered on demand) and service
pages fall back to hardcoded SERVICES_FALLBACK slugs so static pages
are still pre-generated even without a live DB.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Exit code 9 from Node.js means "Invalid argument", not SIGKILL/OOM.
--no-require-module was added in Node 22.12.0 — the cached node:22-slim
image on the VPS predates that release so Node rejects the flag immediately.
The minimal migrate.ts doesn't load lexical/form-builder, so there are no
TLA/ESM issues that required the flag in the first place.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The migrator was importing the full payload.config.ts which loads lexical
editor + form-builder plugin — very heavy modules that caused the process
to be SIGKILL'd (exit 9) on the VPS.
The new migrate.ts builds an inline minimal config: only the postgres
adapter + explicit migrationDir. No editor, no plugins, no
collections/globals needed to run raw SQL migrations. This should fit
comfortably within VPS memory limits.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
pnpm payload migrate exits 1 in the migrator container likely due to TLA
issues with Lexical packages (same issue seen with migrate:create).
Replace with a small src/scripts/migrate.ts that calls payload.db.migrate()
programmatically, using the same NODE_OPTIONS approach that works for seed.ts:
NODE_OPTIONS="--experimental-strip-types --no-require-module"
Also add migrator log output to the CD workflow for easier debugging.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
In Next.js standalone output (output: 'standalone') the src/migrations/
directory is not present on the filesystem, so payload.db.migrate()
could not discover migration files → home_page table was never created
→ getHomePage() threw a DB error → Server Components render failed.
Fix:
- Add `migrator` service to docker-compose.prod.yml (builds from the
`migrator` Dockerfile stage which has the full src/ tree)
- migrator runs `pnpm payload migrate` before app starts
- app depends_on migrator: service_completed_successfully
- Remove payload.db.migrate() from instrumentation.ts (migrator handles it)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Connect homepage, blog, services, header, footer to Payload CMS via local API
- Add HomePage global with 7 editorial sections (hero, painPoints, solution, whyAxil, audience, process, finalCta)
- Add ServerHeader/ServerFooter async wrappers with unstable_cache + tag-based ISR revalidation
- Rewrite blog/[slug] and services/[slug] pages to fetch from CMS with fallbacks
- Add seed script (src/lib/seed.ts) to populate all collections and globals from hardcoded defaults
- Restructure app into (site)/ route group to fix Payload admin hydration conflicts
- Make root layout a passthrough; (site)/layout.tsx owns html/body/fonts
- Restrict user creation/update/delete to admin role only
- Fix migration imports: type MigrateUpArgs/Down from @payloadcms/db-postgres
- Wrap revalidateTag dynamic imports in try/catch for seed/CLI compatibility
- Skip migrations in dev mode (Payload handles schema push automatically)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Subsequent deploys will only rebuild changed layers (src code).
Node modules layer is cached as long as package.json is unchanged.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Swaps logo-axil.png for logo-axil.jpg (official logo with blue A
lettermark, green bar chart icon and AXIL ACCOUNTANTS wordmark).
Updates Header and Footer references accordingly.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add Server Action (actions.ts) sending to info@ and anastasia@axilaccountants.co.uk
- Add ContactForm client component with useActionState, loading and success states
- Replace static HTML form (action="#") with live ContactForm component
- Add @eslint/eslintrc as direct devDependency (fix ESLint resolution on Node.js 25)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Server uses Traefik (traefik-public network) with Cloudflare DNS
cert resolver. Nginx not needed. Add Traefik labels to app service,
connect to traefik-public + internal networks, remove nginx/certbot.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
tsx v4 + Node.js 22 ESM does not handle extensionless .ts imports.
instrumentation.ts uses compiled/bundled code — no resolution issues.
Migrations run automatically before first request on every app start.
Removes external migrator container approach entirely.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Build stage runs pnpm build which changes ESM resolution context.
New migrator stage has deps+src only — clean env for tsx/payload migrate.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Runner stage is minimal — no pnpm. Add migrator service using
build target which has full node_modules + pnpm.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Block body prevents implicit return of HTMLButtonElement | null,
fixing TS error in stricter React ref type checking.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- New /courses page with 5 course cards (Interview Ready, Self
Assessment, Payroll, QuickBooks, Xero) with thumbnail images,
pricing, includes checklists, and enrol CTAs
- Added course images to public/courses/ and Assets/
- Contact page: updated email, phone (07440 594192), address
(Suite 29 Beaufort Court E14 9XL), hours, and added Courses
option to the "I'm interested in" select field
- Header: added Courses to desktop nav and mobile dock
(GraduationCap icon) between Services and Blog
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- services/page.tsx: remove duplicate 'Talk to us' (both went to /contact);
fix 'Book Free Consultation' missing href; swap to BeamButton for consistency
- about/page.tsx: remove 'Contact Us' button duplicating BeamButton on same page;
remove now-unused Button import
- contact/page.tsx: replace "directly in our calendar" with call-back copy
since no calendar integration exists yet
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- ProcessSection: 'Get started' href /services → /contact
- BlogPreviewSection: fix invalid <Link> nested inside <Button> (was <button><a>)
- ContactPage: 'Book free consultation' sidebar button now href="#contact-form"
with matching id on the form element (was unclickable — no href)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace all hardcoded hex color values in components with Tailwind CSS
classes or CSS custom properties matching the design system tokens.
Affected files: Footer, TestimonialsColumn, HeroSection, StarIcon,
TextHoverEffect, and all page-level radial-gradient backgrounds.
Also fix eslint.config.mjs to use FlatCompat for eslint-config-next
legacy config compatibility with ESLint 9 flat config format, and
resolve pre-existing react/no-unescaped-entities errors in JSX files.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>