Date: 2026-03-28 Status: Approved Author: Roberto + Claude Code
Migrate ai4managers.net from Astro to SvelteKit, then add a full blog system (ported from mha-public-site) for publishing BoFu content that feeds the Content Intelligence Flywheel.
| Sub-project | Scope | Talla | Depends on |
|---|---|---|---|
| A: Landing Migration | Rewrite Astro landing in SvelteKit. Same visual identity. | M | — |
| B: Blog System | Port blog from MHA. Supabase + ProseMirror + admin. | M | A |
Sequential execution: A first, then B on top.
Rewrite ai4managers.net landing page from Astro 5 to SvelteKit 2, maintaining identical visual design (obsidian/champagne/ivory theme, GSAP animations, same sections).
| Component | Technology |
|---|---|
| Framework | SvelteKit 2 + Svelte 5 (runes) |
| CSS | TailwindCSS 4 |
| UI Components | Skeleton UI 2 (or Svelte 5 compatible successor) |
| Animations | GSAP 3.14 |
| Fonts | Inter, Playfair Display, JetBrains Mono (Google Fonts) |
| Deployment | Vercel (adapter-auto) |
| Domain | ai4managers.net |
| Astro Source | Svelte Target | Notes |
|---|---|---|
Layout.astro |
+layout.svelte |
SEO meta, OG tags, JSON-LD Organization schema |
Navbar.astro |
Navbar.svelte |
Add "Blog" link (for sub-project B) |
Hero.astro |
Hero.svelte |
GSAP entrance animations |
Features.astro |
Features.svelte |
"Programa" section |
Philosophy.astro |
Philosophy.svelte |
|
Protocol.astro |
Protocol.svelte |
"Proceso" section |
FAQ.astro |
FAQ.svelte |
Accordion |
Footer.astro |
Footer.svelte |
|
global.css |
Tailwind config + app.css |
Theme tokens: obsidian, champagne, ivory, slate |
index.astro |
+page.svelte |
Compose all sections |
--obsidian: #0a0a0a; /* bg principal */
--champagne: #c9a96e; /* acento dorado */
--ivory: #f5f0e8; /* texto claro */
--slate: #1a1a2e; /* superficie secundaria */
| Route | Type | Content |
|---|---|---|
/ |
SSG (prerender) | Landing page |
/blog |
SSR | Blog listing (sub-project B) |
/blog/[slug] |
SSR | Article (sub-project B) |
prerender = true for landing pageAdd a full blog system to ai4managers.net/blog, ported from mha-public-site (MedicalHubAssist). Admin panel with ProseMirror editor, draft/publish workflow, image upload. Spanish only.
Repository: github.com/aguirrerjg/mha-public-site
Key directories to port from:
src/lib/server/db/ — Drizzle schema + CRUD queriessrc/lib/components/blog/ — Article, PlainText, RichText editorssrc/lib/editor/ — ProseMirror schemas, keymap, input rulessrc/routes/api/ — create/update/delete article, upload assetsrc/routes/[...lang]/blog/ — blog listing + article pagessrc/routes/[...lang]/admin/ — login/logout| Component | Technology |
|---|---|
| Database | PostgreSQL via Supabase (new project) |
| ORM | Drizzle ORM |
| Auth | Supabase Auth (@supabase/ssr for SvelteKit 2) |
| Rich Text Editor | ProseMirror (adapted to Svelte 5) |
| Image Storage | ImageKit |
| Image Crop | cropperjs |
Table: articles
CREATE TABLE articles (
article_id SERIAL PRIMARY KEY,
slug TEXT UNIQUE NOT NULL,
created_at TIMESTAMP DEFAULT NOW(),
published_at TIMESTAMP, -- null = draft
updated_at TIMESTAMP DEFAULT NOW(),
thumbnail TEXT, -- ImageKit URL
thumbnail_id TEXT -- ImageKit file ID
);
Table: article_translations
CREATE TABLE article_translations (
translation_id SERIAL PRIMARY KEY,
article_id INTEGER REFERENCES articles(article_id) ON DELETE CASCADE,
language_code VARCHAR(2) DEFAULT 'es',
slug TEXT UNIQUE NOT NULL,
title TEXT NOT NULL,
teaser TEXT,
content TEXT, -- HTML from ProseMirror
created_at TIMESTAMP DEFAULT NOW(),
published_at TIMESTAMP,
updated_at TIMESTAMP DEFAULT NOW()
);
Table: profiles
CREATE TABLE profiles (
id UUID PRIMARY KEY REFERENCES auth.users(id),
email TEXT UNIQUE,
is_admin BOOLEAN DEFAULT FALSE
);
/admin/blog-loginsignInWithPassword()@supabase/ssr (cookies, SvelteKit 2 compatible)profiles.is_admin/admin/blog-logoutPort from MHA with Svelte 5 runes adaptation:
PlainText.svelte — single-line editor for title/teaser (use $state instead of let)RichText.svelte — multi-line editor for content bodyEditorToolbar.svelte — Save/Cancel barUploadImageModal.svelte — Crop + resize + upload to ImageKit| Route | Method | Auth Required | Purpose |
|---|---|---|---|
/api/create-article |
POST | Yes | Create article + translation |
/api/update-article |
POST | Yes | Update article/translation |
/api/delete-article |
POST | Yes | Delete article + translations + ImageKit asset |
/api/upload-asset |
PUT | Yes | Upload image to ImageKit |
| Route | Auth | Content |
|---|---|---|
/blog |
Public | Article listing (only published). Admin sees drafts too. |
/blog/[slug] |
Public | Article view. Admin sees edit/delete buttons. |
/blog/new |
Admin only | New article editor |
ArticleTeaser cards (thumbnail, title, teaser, date)published_at IS NOT NULLpublished_at DESC/api/upload-asset → ImageKit<img> node in ProseMirror/ai4managers-blog/images/<title> = article title + " | AI4Managers"<meta name="description"> = teaser/blog/inteligencia-artificial-en-los-negociossitemap.xml that includes:/ (landing)/blog (listing)/blog/[slug] for each published article/{lang}/ prefix — Spanish only)@supabase/ssr instead of @supabase/auth-helpers-sveltekit (SvelteKit 2 compatible)# Supabase (new project for AI4Managers)
PUBLIC_SUPABASE_URL=https://xxx.supabase.co
PUBLIC_SUPABASE_ANON_KEY=eyJ...
DATABASE_URL=postgresql://...
# ImageKit
IMAGEKIT_PUBLIC_KEY=public_xxx
IMAGEKIT_PRIVATE_KEY=private_xxx
PUBLIC_IMAGEKIT_URL_ENDPOINT=https://ik.imagekit.io/xxx
# Vercel (auto-configured)
/blog shows list of published articlessitemap.xml includes all published articles