Commit graph

35 commits

Author SHA1 Message Date
Vadym Samoilenko
7b6cfaa5c2 fix: wrap generateStaticParams in try/catch to survive build without DB
Some checks failed
Deploy to production / SSH deploy to VPS (push) Has been cancelled
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>
2026-02-23 22:01:00 +00:00
Vadym Samoilenko
0f8de6479d fix: drop --no-require-module from migrator (added in Node 22.12.0)
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>
2026-02-23 21:55:04 +00:00
Vadym Samoilenko
dc1b1efbab fix: use minimal Payload config in migrator to prevent OOM SIGKILL
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>
2026-02-23 21:46:29 +00:00
Vadym Samoilenko
1ba1349607 fix: use programmatic migrate script instead of pnpm payload migrate
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>
2026-02-23 21:37:29 +00:00
Vadym Samoilenko
96ab1c5da6 fix: run DB migrations via migrator service, not standalone app
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>
2026-02-23 21:31:52 +00:00
Vadym Samoilenko
3f6dfe36b1 feat: full CMS integration — connect all content to Payload admin panel
- 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>
2026-02-23 21:19:44 +00:00
Vadym Samoilenko
2966dfb518 ci: remove --pull to use Docker layer cache on server
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>
2026-02-23 18:36:48 +00:00
Vadym Samoilenko
f018a6e703 ci: fix SSH port to 1220, remove sudo (ubuntu in docker group)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-23 18:25:50 +00:00
Vadym Samoilenko
11826a63eb ci: add port 22 and sudo docker for ubuntu user
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-23 18:25:10 +00:00
Vadym Samoilenko
4958a6afc2 ci: add GitHub Actions CD workflow for auto-deploy to VPS
On push to main: SSH into server, git pull, docker compose build + up.
Uses SSH_HOST / SSH_USER / SSH_PRIVATE_KEY repo secrets.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-23 18:20:36 +00:00
Vadym Samoilenko
190581c80b feat: replace logo with official Axil Accountants brand mark
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>
2026-02-23 18:20:14 +00:00
Vadym Samoilenko
4ef45a311d fix: change contact form sender to info@ai-impress.com
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-23 15:46:32 +00:00
Vadym Samoilenko
338b47d4c1 feat: wire contact form to Resend email notifications
- 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>
2026-02-23 15:45:11 +00:00
Vadym Samoilenko
932b7143fe fix: fix ESLint hardcoded Mac path and enable allowImportingTsExtensions
- eslint.config.mjs: replace hardcoded absolute path with proper @eslint/eslintrc import
- tsconfig.json: add allowImportingTsExtensions for .ts import extensions in payload.config.ts

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-23 15:33:11 +00:00
Vadym Samoilenko
2b19626b68 feat: add initial DB migrations and sharp dependency
- Generate initial Payload CMS migration (all collections + globals)
- Add sharp for image resizing support
- Add .ts extensions to payload.config.ts imports (required for migrate:create with --disable-transpile)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-23 15:28:24 +00:00
Vadym Samoilenko
d34071f9f8 fix: switch from nginx to Traefik for routing and SSL
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>
2026-02-23 14:39:32 +00:00
Vadym Samoilenko
24b01bcba3 fix: start nginx if not active instead of reload
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-23 14:36:20 +00:00
Vadym Samoilenko
4e23350bfc fix: run migrations via Next.js instrumentation.ts at startup
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>
2026-02-23 14:14:18 +00:00
Vadym Samoilenko
1348188a6c fix: dedicated migrator stage for payload migrate
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>
2026-02-23 14:04:34 +00:00
Vadym Samoilenko
ecc2c94e5f fix: run migrations from build stage (has pnpm)
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>
2026-02-23 13:59:57 +00:00
Vadym Samoilenko
2c115bab99 fix: resolve all TypeScript build errors
- InteractiveMenu: block body on textRefs callback (void return)
- TextHoverEffect: remove stale @ts-expect-error (now unused)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-23 13:55:57 +00:00
Vadym Samoilenko
dcd6eb487e fix: ref callback return type in InteractiveMenu
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>
2026-02-23 13:53:24 +00:00
Vadym Samoilenko
239e6a4c43 fix: sync pnpm-lock.yaml with package.json
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-23 13:50:23 +00:00
Vadym Samoilenko
986bbd5a3c fix: gen_secret SIGPIPE crash with set -o pipefail
tr/head pipe causes SIGPIPE exit 141, killing script silently.
Wrap with set +o pipefail guard.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-23 13:46:01 +00:00
Vadym Samoilenko
fc4688cc6f feat: add production deploy script and docker-compose
- deploy.sh: idempotent deploy for Ubuntu server (Docker + Nginx + SSL)
- docker-compose.prod.yml: production compose (runner target, localhost-only port)
- .gitignore: exclude .env.production and .deploy-config

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-23 13:41:29 +00:00
Vadym Samoilenko
7bb25f9c59 feat: add courses page, real contact details, Courses to navigation
- 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>
2026-02-23 13:25:16 +00:00
Vadym Samoilenko
9d4bb7ad34 fix: add missing href to all broken buttons across site
- services/page.tsx: 'Book Free Consultation' bottom CTA → /contact
- services/[slug]/page.tsx: hero + final CTA 'Book Free Consultation' → /contact;
  fix nested <Link> inside secondary Button (invalid HTML) → href prop
- blog/page.tsx: 'Subscribe to newsletter' → /contact (no newsletter service yet)
- blog/[slug]/page.tsx: inline 'Book Free Consultation' CTA → /contact

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-23 13:03:32 +00:00
Vadym Samoilenko
e4fcc5b9c8 fix: add missing href to 'Get your quote' button on services page
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-23 13:00:40 +00:00
Vadym Samoilenko
946c6a44ab fix: remove duplicate CTAs and calendar reference
- 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>
2026-02-23 12:55:29 +00:00
Vadym Samoilenko
28b999b1ae fix: align all CTAs with concept — every CTA leads to contact form
- 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>
2026-02-23 12:51:57 +00:00
Vadym Samoilenko
7cd698d6d6 feat: UI polish — SolutionSection stacked cards, BeamButton CTA, Footer redesign
- SolutionSection: replace flat cards with interactive stacked card layout
  (skew, grayscale, hover lift, click-to-expand with z-50 + skew reset)
- BeamButton: new component with framer-motion SVG beam animation along
  pill border (emerald→blue gradient); variants dark/light
- HeroSection + FinalCTASection: primary CTA switched to BeamButton
- Footer: replace programmatic SVG logo with logo-axil.png via next/image;
  add AImpress LTD credit link; increase hero text height
- TextHoverEffect: fix viewBox (920×100), use SVG-unit fontSize, split
  AXIL (blue #1B9AD6) / ACCOUNTANTS (emerald #3CC68A) via tspan

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-23 12:30:50 +00:00
Vadym Samoilenko
83a8878f4a feat: redesign HeroSection to 3-column MinimalistHero layout
- HeroSection: 3-col layout (copy | concentric circles+dashboard | display headline)
  - 'use client' + framer-motion entrance animations (slide in from sides, scale centre)
  - DashboardPreview inline component (compact portal mockup)
  - Two floating stat mini-cards (Avg Tax Saved, Response Time)
  - Mobile: stacked layout, right headline column hidden, H1 in left column
- ContainerScroll: simplified — removed 72rem scroll container and scroll transforms;
  now plain layout wrapper with CSS fadeInUp entrance
- Header: logo size increased h-10 → h-13 (40px → 52px)
- fix: escape apostrophes in ProcessSection, SolutionSection, TestimonialsSection
- fix: remove unused customSize param from SpotlightCard
- docs: update CONTEXT_HANDOVER.md with session 4 changes

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-22 21:20:42 +00:00
Vadym Samoilenko
8db56af042 refactor: replace hardcoded hex colors with design system tokens
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>
2026-02-22 20:36:50 +00:00
Vadym Samoilenko
9785c0997e docs: mark Feature 1 tasks as complete in implementation plan
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 22:44:54 +00:00
Vadym Samoilenko
e373c2b46c feat: project setup & repository (Feature 1)
- Next.js 16.1.6 with App Router, TypeScript strict, Tailwind CSS v4
- ESLint 9, Prettier with Tailwind class sorting plugin
- Husky + lint-staged pre-commit hooks
- Multi-stage Dockerfile (dev/build/production) with pnpm + Node 20
- docker-compose.yml with hot reload + PostgreSQL 17
- src/ directory structure (components, lib, hooks, types, payload)
- .env.example template with all required variables
- standalone output mode for production Docker builds

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 22:43:53 +00:00