Skip to content

Commit 0141db1

Browse files
authored
Merge pull request #4 from cfboy/claude/evaluate-cms-options-Q82zd
feat: integrate Sanity CMS and Netlify deployment
2 parents 26bea4c + aeb0fc5 commit 0141db1

81 files changed

Lines changed: 11891 additions & 2755 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.env.example

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# Sanity CMS
2+
VITE_SANITY_PROJECT_ID=your-project-id
3+
VITE_SANITY_DATASET=production
4+
5+
# Sanity Studio (used by sanity.config.ts)
6+
SANITY_STUDIO_PROJECT_ID=your-project-id
7+
SANITY_STUDIO_DATASET=production
8+
SANITY_TOKEN=your-sanity-token

.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,10 @@ node_modules
22
dist
33
*.local
44

5+
# Environment variables (contains secrets)
6+
.env
7+
.env.local
8+
59
# macOS
610
.DS_Store
11+
.sanity/

docs/cms-evaluation.md

Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,211 @@
1+
# CMS Strategy Evaluation for MasterSpace
2+
3+
## Context
4+
5+
MasterSpace is a fully static React SPA (React 19 + Vite 7 + Tailwind v4) deployed on GitHub Pages. All content is hardcoded: portfolio data in TypeScript components with 60+ static image imports, text in i18n JSON files (`src/i18n/es.json`, `en.json`). Adding a new portfolio project requires editing TypeScript source code, adding image imports, updating both translation files, committing to git, and rebuilding — a developer-only workflow.
6+
7+
**Goal**: $0 cost, best possible implementation, non-technical editors can manage content.
8+
9+
---
10+
11+
## Final Recommendation: Sanity CMS (Free Tier)
12+
13+
After evaluating all options, **Sanity CMS** is the best fit given the user's priorities:
14+
- $0 cost (free tier: 20 users, 10K docs, 10 GB storage, 10 GB bandwidth)
15+
- Best editorial UX of all options — polished, modern admin UI
16+
- Built-in image CDN with automatic optimization (eliminates the need for `sharp` build scripts)
17+
- Native i18n with field-level localization
18+
- Non-technical editors log in with email/Google — no GitHub account needed
19+
- Hosting on Netlify (free tier, 100 GB bandwidth, allows commercial use)
20+
21+
### Why Not Decap CMS?
22+
23+
Decap is cheaper/simpler but has significant UX limitations:
24+
- Basic form-based UI (built in 2017 with React, carries technical debt)
25+
- Slow loading (~1.5s per page, individual API calls per content piece)
26+
- No live preview of content
27+
- Requires Netlify Identity setup for non-GitHub auth (adds complexity)
28+
- Image management is basic — no automatic optimization
29+
- Bilingual content setup is awkward (manual schema config per locale)
30+
- Community is fragmenting (users migrating to Sveltia CMS)
31+
32+
For non-technical editors adding construction portfolio projects with photos, Sanity's Studio provides a substantially better experience.
33+
34+
### Why Not Vercel?
35+
36+
**Vercel's free (Hobby) tier prohibits commercial use.** Since MasterSpace is a business website, this is a violation of their terms. **Netlify's free tier allows commercial use** and provides 100 GB bandwidth/month, built-in CI/CD, and native Decap/Identity integration.
37+
38+
---
39+
40+
## Detailed Research
41+
42+
### 1. Editorial UX Comparison
43+
44+
**Sanity Studio:**
45+
- Polished, modern React-based admin panel (customizable)
46+
- Real-time collaboration — multiple editors can work simultaneously
47+
- Drag-and-drop, rich text editor, image hotspot/crop tools
48+
- Custom input components and dashboard widgets
49+
- AI Assist feature for content generation (Spring 2025 release)
50+
- Schema defined as code — developer builds the editorial interface once, editors use it forever
51+
- Access at a separate URL: `masterspace.sanity.studio` (free subdomain)
52+
53+
**Decap CMS:**
54+
- Functional but dated form-based UI
55+
- Three tabs: Content, Workflow, Media
56+
- 16 built-in widgets (string, image, list, markdown, etc.)
57+
- No live preview, no real-time collaboration
58+
- Editorial workflow with draft/review/publish states
59+
- Admin panel at `masterspacellc.com/admin`
60+
- Slow — makes individual REST API calls per content piece
61+
62+
**For a construction firm adding project portfolios**: Sanity wins decisively. The image handling alone (hotspot cropping, automatic responsive sizes via CDN) is worth the setup effort for a photo-heavy site.
63+
64+
### 2. Hosting Implications
65+
66+
| Feature | GitHub Pages (current) | Netlify (recommended) | Vercel |
67+
|---------|----------------------|----------------------|--------|
68+
| **Cost** | $0 | $0 | $0 (Hobby) |
69+
| **Commercial use** | Yes | Yes | **No** (violates ToS) |
70+
| **Bandwidth** | Unlimited (soft) | 100 GB/mo | 100 GB/mo |
71+
| **Build pipeline** | Manual (or GitHub Actions) | Built-in CI/CD | Built-in CI/CD |
72+
| **Serverless functions** | No | Yes (125K inv/mo) | Yes |
73+
| **CDN** | Basic | Global CDN | Global Edge |
74+
| **Identity/Auth** | No | Netlify Identity (free, 5 users) | No |
75+
| **Form handling** | No | Built-in (100 submissions/mo) | No |
76+
77+
**Recommended: Netlify Free Tier**
78+
- Automated builds on git push (no GitHub Actions needed)
79+
- Netlify Identity for CMS auth if using Decap (not needed with Sanity)
80+
- Built-in form handling could replace the Formspree/EmailJS TODO in the contact form
81+
- Deploy previews for every PR
82+
- Migration: change DNS from GitHub Pages to Netlify, connect repo — ~15 min
83+
84+
### 3. Migration Effort
85+
86+
**Phase 1: Sanity Setup (1-2 days)**
87+
- Install `@sanity/client` and `sanity` packages
88+
- Define content schemas: Project, Service, Testimonial, Certification
89+
- Configure i18n with field-level localization (ES/EN)
90+
- Deploy Sanity Studio to `masterspace.sanity.studio` (free)
91+
- Migrate existing content: 4 projects, 6 services, 3 testimonials, 3 certifications
92+
93+
**Phase 2: Image Migration (1 day)**
94+
- Upload 55+ images to Sanity's asset pipeline
95+
- Each image gets automatic CDN delivery with on-the-fly optimization
96+
- Remove `src/assets/projects/` directory (saves ~25 MB from repo)
97+
- No need for `sharp` build scripts — Sanity handles optimization
98+
99+
**Phase 3: Component Refactoring (2-3 days)**
100+
- `src/components/sections/portfolio.tsx` — replace 35 static imports with Sanity query + CDN image URLs
101+
- `src/components/sections/services.tsx` — replace 27 static imports similarly
102+
- `src/components/sections/testimonials.tsx` — pull from Sanity
103+
- `src/components/sections/certifications.tsx` — pull from Sanity
104+
- Add a data fetching layer (fetch at build time or runtime with SWR/React Query)
105+
- Update i18n: portfolio/service text comes from Sanity instead of JSON files
106+
107+
**Phase 4: Hosting Migration (30 min)**
108+
- Connect repo to Netlify
109+
- Configure build command: `pnpm build`
110+
- Set environment variable: `SANITY_PROJECT_ID`
111+
- Update DNS (CNAME from GitHub Pages to Netlify)
112+
113+
**Phase 5: Contact Form (30 min)**
114+
- Replace the TODO in `contact.tsx` with Netlify Forms (built-in, free, 100 submissions/mo)
115+
116+
**Total estimated effort: 4-6 days of development**
117+
118+
### 4. Cost Projections
119+
120+
**Sanity Free Tier Limits:**
121+
122+
| Resource | Free Limit | MasterSpace Usage (current) | At 20 projects | At 50 projects |
123+
|----------|-----------|---------------------------|----------------|----------------|
124+
| Documents | 10,000 | ~15 (4 projects + services + testimonials) | ~50 | ~120 |
125+
| API requests | 200,000/mo | ~5,000/mo (low traffic site) | ~10,000/mo | ~25,000/mo |
126+
| Assets storage | 10 GB | ~25 MB (55 images) | ~100 MB | ~250 MB |
127+
| Bandwidth | 10 GB | ~2-5 GB/mo estimate | ~5-8 GB/mo | ~8-15 GB/mo |
128+
| Users | 20 | 1-2 editors | 2-3 editors | 3-5 editors |
129+
130+
**Verdict: Free tier is more than sufficient for the foreseeable future.** Even at 50 projects, MasterSpace uses ~1.2% of the document limit, ~12.5% of API requests, ~2.5% of storage, and well under bandwidth limits. The site would need to become extremely high-traffic (100K+ monthly visitors) before approaching free tier limits.
131+
132+
**Netlify Free Tier:**
133+
134+
| Resource | Free Limit | MasterSpace Usage |
135+
|----------|-----------|-------------------|
136+
| Bandwidth | 100 GB/mo | ~5-10 GB/mo |
137+
| Build minutes | 300/mo | ~5 min/build x ~10 builds/mo = 50 min |
138+
| Forms | 100 submissions/mo | Sufficient for contact form |
139+
| Sites | Unlimited | 1 |
140+
141+
**Total projected cost: $0/month** for at least the next 2-3 years of growth.
142+
143+
**When costs would start**: If MasterSpace grows to 100K+ monthly visitors or 500+ high-res images, the Sanity Growth tier ($15/user/mo) might be needed. This is unlikely for a Puerto Rico construction firm's portfolio site.
144+
145+
---
146+
147+
## Implementation Plan
148+
149+
### Phase 1: Sanity CMS Setup
150+
- `npm create sanity@latest` — initialize Sanity project with schemas
151+
- Define schemas: `project.ts` (title, location, description, cover, album[], all with ES/EN), `service.ts`, `testimonial.ts`, `certification.ts`
152+
- Configure i18n: document-level internationalization with `@sanity/document-internationalization` plugin
153+
- Deploy Studio to `masterspace.sanity.studio`
154+
- Migrate existing content from JSON/TypeScript into Sanity
155+
156+
### Phase 2: Image Migration
157+
- Upload all images from `src/assets/projects/` to Sanity
158+
- Sanity CDN auto-generates optimized sizes (no `sharp` scripts needed)
159+
- Remove static image files from repo
160+
161+
### Phase 3: Component Refactoring
162+
- Add `@sanity/client` to the project
163+
- Create `src/lib/sanity.ts` — client config + query helpers
164+
- Refactor `portfolio.tsx`: fetch projects from Sanity, use CDN image URLs
165+
- Refactor `services.tsx`: same pattern
166+
- Refactor `testimonials.tsx`, `certifications.tsx`
167+
- Simplify i18n JSON files (remove sections now managed by Sanity)
168+
169+
### Phase 4: Hosting Migration
170+
- Connect GitHub repo to Netlify
171+
- Configure build: `pnpm build`, publish dir: `dist`
172+
- Set env vars: `VITE_SANITY_PROJECT_ID`, `VITE_SANITY_DATASET`
173+
- Update DNS: CNAME `masterspacellc.com` → Netlify
174+
175+
### Phase 5: Contact Form
176+
- Replace TODO in `contact.tsx` with Netlify Forms integration
177+
178+
---
179+
180+
## Key Files to Modify
181+
182+
| File | Change |
183+
|------|--------|
184+
| `src/components/sections/portfolio.tsx` | Replace 35 static imports with Sanity query + CDN URLs |
185+
| `src/components/sections/services.tsx` | Replace 27 static imports with Sanity data |
186+
| `src/components/sections/testimonials.tsx` | Fetch from Sanity |
187+
| `src/components/sections/certifications.tsx` | Fetch from Sanity |
188+
| `src/components/sections/contact.tsx` | Integrate Netlify Forms (replace Formspree TODO) |
189+
| `src/i18n/es.json`, `en.json` | Remove sections managed by Sanity |
190+
| `vite.config.ts` | Add Sanity env vars |
191+
| **New:** `src/lib/sanity.ts` | Sanity client config + GROQ queries |
192+
| **New:** `sanity/` directory | Sanity Studio schemas and config |
193+
| **New:** `netlify.toml` | Netlify build configuration |
194+
195+
## Verification
196+
197+
1. `pnpm build` — site builds without errors
198+
2. `pnpm dev` — all portfolio projects render with Sanity CDN images + bilingual text
199+
3. Access `masterspace.sanity.studio` — verify editors can add/edit projects, upload images, manage translations
200+
4. Test adding a new project via Sanity Studio — verify it appears on site after rebuild
201+
5. Test contact form submission via Netlify Forms
202+
6. Verify image CDN delivers optimized sizes at different viewports
203+
204+
## Sources
205+
206+
- [Sanity Pricing](https://www.sanity.io/pricing)
207+
- [Sanity Studio Features](https://www.sanity.io/studio)
208+
- [Decap CMS Editor Features](https://decapcms.org/features/editor/)
209+
- [Netlify vs Vercel Comparison](https://www.netlify.com/guides/netlify-vs-vercel/)
210+
- [Static Hosting Comparison](https://namastedev.com/blog/hosting-a-static-website-comparing-github-pages-netlify-and-vercel/)
211+
- [Sveltia CMS as Decap Alternative](https://dubasipavankumar.com/blog/sveltia-cms-migration-decap-replacement/)

netlify.toml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
[build]
2+
command = "pnpm build && pnpm sanity deploy -y || true"
3+
publish = "dist"
4+
5+
[build.environment]
6+
NODE_VERSION = "22"
7+
8+
# SPA fallback — all routes serve index.html
9+
[[redirects]]
10+
from = "/*"
11+
to = "/index.html"
12+
status = 200

package.json

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,15 @@
88
"build": "tsc -b && vite build",
99
"format": "prettier --write .",
1010
"lint": "eslint .",
11-
"preview": "vite preview"
11+
"preview": "vite preview",
12+
"sanity:dev": "sanity dev",
13+
"sanity:deploy": "sanity deploy",
14+
"sanity:migrate": "node scripts/migrate-to-sanity.mjs"
1215
},
1316
"dependencies": {
17+
"@sanity/client": "^7.17.0",
18+
"@sanity/icons": "^3.7.4",
19+
"@sanity/image-url": "^2.0.3",
1420
"@tailwindcss/vite": "^4.2.1",
1521
"class-variance-authority": "^0.7.1",
1622
"clsx": "^2.1.1",
@@ -23,12 +29,15 @@
2329
"react": "^19.2.0",
2430
"react-dom": "^19.2.0",
2531
"react-i18next": "^16.5.4",
32+
"sanity-plugin-icon-picker": "^4.0.0",
33+
"styled-components": "^6.3.11",
2634
"tailwind-merge": "^3.5.0",
2735
"tailwindcss": "^4.2.1",
2836
"yet-another-react-lightbox": "^3.29.1"
2937
},
3038
"devDependencies": {
3139
"@eslint/js": "^9.39.1",
40+
"@sanity/vision": "^5.16.0",
3241
"@trivago/prettier-plugin-sort-imports": "^6.0.2",
3342
"@types/node": "^24.10.1",
3443
"@types/react": "^19.2.7",
@@ -40,7 +49,7 @@
4049
"globals": "^16.5.0",
4150
"prettier": "^3.8.1",
4251
"prettier-plugin-tailwindcss": "^0.7.2",
43-
"sharp": "^0.34.5",
52+
"sanity": "^5.16.0",
4453
"typescript": "~5.9.3",
4554
"typescript-eslint": "^8.48.0",
4655
"vite": "^7.3.1",

0 commit comments

Comments
 (0)