The goal
The company ran its public site on WordPress + Elementor with no meta titles or descriptions, and ran its operations (projects, contractors, customers, employees, union tables) across Google Sheets. The goal was a coded, redesigned Hebrew site that search engines and AI answer engines can read, plus an admin that becomes the foundation of a wider company management system.
The solution
A single Nuxt 4 + TypeScript codebase: an SSR Hebrew RTL public site, an admin SPA under /admin and a Nitro API over MongoDB, deployed on Vercel. A migration script pulls the WordPress pages, posts and media through the WP REST API, strips the Elementor markup into content blocks and keeps every Hebrew URL path 1:1 for a lossless domain move. The admin covers CMS pages, leads, customers, contractors, projects, work logs, employee diaries, no-code custom tables, a Google Sheets importer with dry-run and rollback, reminders, roles and permissions, an audit log with restore, and an error log.
Highlights
- One codebase replaces both the WordPress site and the operational Google Sheets
- Legacy Hebrew URLs preserved 1:1 for a lossless domain move
- Every change is audited and can be restored from the admin
- 0 axe WCAG A/AA violations across 50 page views in the internal accessibility audit
- CI runs lint, typecheck, unit tests and API tests against the production build
The challenge
Moving off WordPress without losing search rankings, while the live WordPress site keeps running on the domain.
The migration keeps every nested Hebrew URL identical and records a redirect for anything that changes. The new build runs on its own deployment with a global noindex switch (SITE_INDEXABLE=false), so it never competes with the live site as duplicate content until the domain moves.
Bringing loosely structured Google Sheets into a database without a one-way, all-or-nothing import.
Every imported record carries its source (sheet, tab, row key, import run). The importer previews rows, maps columns to an existing module or a new custom table, reports errors per row in a dry-run, upserts on commit and can roll back a whole run, so it can be re-run to stay in sync until the cut-over. It also reads dates written inside free text.
One system holds customer, contractor and employee personal data and is used by people with different responsibilities.
The server is the source of truth: every admin handler checks a resource:action permission loaded fresh from the database, a role can only grant permissions its author holds, diary visibility is scoped per role and user, and every create, update, delete and login lands in an audit log that can restore or revert. Login adds an emailed 2FA code and a lockout stored in MongoDB so it works on serverless.
Reminders must fire reliably on a serverless host where the cron may run once a day or every hour.
Each run claims a rule's next run time with a compare-and-set, missed slots collapse into the latest one, and a delivery ledger stops the same item reaching the same recipient twice in a period. The schedule and routing logic is pure and unit-tested.
What was delivered
- Redesigned Nuxt 4 public site (SSR, Hebrew RTL)
- Admin system with CMS and operations modules
- WordPress migration script with dry-run
- Google Sheets importer with rollback
- Automated test suites (unit, API, browser) and GitHub Actions CI
- Accessibility menu, accessibility statement script and audit report
- README with setup, security model and operating procedures
Results
15+
Admin modules
40
Pages audited for accessibility
0
axe WCAG A/AA violations
Hebrew RTL
Primary language
What it taught me
- Tagging every imported row with its source is what makes a spreadsheet migration reversible and repeatable
- Keeping the new site noindexed on its own deployment lets the whole rebuild happen with zero risk to the live site's rankings
