Files
flatrender/CLAUDE.md
T
Soroush.Asadi 36e264f3e3 feat: admin API integration, LogoMark, settings page, i18n, RTL font, docs
- Wire admin API into homepage + templates page (ISR 60s, null fallback)
- Add src/lib/admin-api.ts with safeFetch helper
- Add adminProjectToTemplateItem + adminProjectToCatalogTemplate mappers
- Add LogoMark SVG component, replace Sparkles icon in Navbar/Footer/Sidebar
- Add public/favicon.svg (SVG brand mark)
- Rewrite opengraph-image.tsx with FlatRender branding
- Add RTL/Persian font cascade: unlayered [dir=rtl] block forces Vazirmatn
- Dashboard Settings page: Profile, Security, Billing, Notifications sections
- Add src/lib/supabase/client.ts browser client
- Admin API: GET /me, PATCH /profile, POST /change-password endpoints
- Admin API DTOs: AdminUserDto, UpdateProfileRequest, ChangePasswordRequest
- Admin UI Settings page with TanStack Query + mutations
- Add CLAUDE.md + README.md to both repos for new-machine onboarding
- Update PROJECT_MEMORY.md with session log
- Add appsettings.Development.json.example template
2026-05-27 09:06:51 +03:30

10 KiB

FlatRender — Claude Instructions

Read this entire file before touching any code. This is the single source of truth for the AI assistant working on this project.


🗂 Project Layout (two repos, work together)

D:\Projects\flatrender\          ← Next.js marketing + studio app  (THIS REPO)
D:\Projects\flatrender-admin\    ← Admin panel (separate repo)
  admin-api\                     ← .NET 10 ASP.NET Core Web API
  admin-ui\                      ← Vite + React + TypeScript SPA

Both repos must be open together — the Next.js app consumes admin-api's public endpoints.


🚀 How to Run

Next.js app

cd D:\Projects\flatrender
npm run dev          # → http://localhost:3000
npm run render-worker  # optional — video render worker on :3355

Admin API (.NET 10)

cd D:\Projects\flatrender-admin\admin-api
# First time: copy appsettings.Development.json.example → appsettings.Development.json
# Fill in Postgres + MinIO credentials, then:
dotnet run           # → http://localhost:5000
# Scalar API docs:   http://localhost:5000 (root)

Admin UI (React SPA)

cd D:\Projects\flatrender-admin\admin-ui
npm run dev          # → http://localhost:5173

First-time admin seed

POST http://localhost:5000/api/auth/seed
Body: { "email": "admin@example.com", "password": "YourPassword123" }
Only works when zero admin users exist.

🛠 Tech Stack

Layer Technology
Framework Next.js 15 App Router, TypeScript
Styling Tailwind CSS v3, shadcn/ui, Framer Motion
Canvas React-Konva (Video Studio + Image Editor)
State Zustand (studio-store.ts, image-editor-store.ts)
Auth + DB Supabase (@supabase/ssr)
Payments Stripe
i18n next-intl (fa = default/RTL, en = /en/ prefix)
Fonts Vazirmatn (Persian RTL), Plus Jakarta Sans + Inter (English)
Video (browser) ffmpeg.wasm in Web Worker
Video (server) nexrender + After Effects
Admin backend .NET 10 ASP.NET Core, EF Core 9 + Npgsql (Supabase Postgres)
Admin storage MinIO (S3-compatible)
Admin auth JWT Bearer, BCrypt
Admin UI Vite + React + TypeScript + Tailwind + TanStack Query

🌐 i18n / Locale Routing

  • Persian (fa) — default, no URL prefix, RTL, Vazirmatn font
  • English (en) — at /en/ prefix, LTR, Plus Jakarta Sans + Inter

Config: src/i18n/routing.ts
Messages: messages/fa.json and messages/en.json
Both files must have identical keys — add to both when adding new strings.
Components use useTranslations("namespace") hook.

RTL font rule: globals.css has a [dir="rtl"] block that forces Vazirmatn on all elements — do not fight it with utility classes.


🔑 Environment Variables

Copy .env.example.env.local and fill in:

# Admin API (optional — hardcoded fallback when not set)
ADMIN_API_URL=http://localhost:5000

# Supabase — REQUIRED for auth/dashboard/studio
NEXT_PUBLIC_SUPABASE_URL=
NEXT_PUBLIC_SUPABASE_ANON_KEY=
SUPABASE_SERVICE_ROLE_KEY=

# Stripe — required for payments
STRIPE_SECRET_KEY=
STRIPE_WEBHOOK_SECRET=
STRIPE_PRICE_PRO_MONTHLY=
STRIPE_PRICE_PRO_ANNUAL=
STRIPE_PRICE_BUSINESS_MONTHLY=
STRIPE_PRICE_BUSINESS_ANNUAL=

# Image background removal
REMOVE_BG_API_KEY=

# Video rendering (RENDER_MOCK=true → skip real rendering in dev)
RENDER_MOCK=true
RENDER_WORKER_URL=http://localhost:3355
NEXRENDER_BINARY=
NEXRENDER_TEMPLATE_SRC=

📁 Key File Map

src/
├── app/[locale]/
│   ├── page.tsx                      ← Homepage (async, fetches admin projects)
│   ├── video-maker/page.tsx          ← Video Maker landing
│   ├── image-maker/page.tsx          ← Image Maker landing
│   ├── templates/page.tsx            ← Templates browser (async, fetches admin projects)
│   ├── pricing/page.tsx              ← Pricing page
│   ├── auth/page.tsx                 ← Sign in / Sign up (Supabase)
│   ├── dashboard/
│   │   ├── layout.tsx                ← Auth guard, DashboardShell
│   │   ├── page.tsx                  ← Projects grid
│   │   └── settings/page.tsx         ← Settings (Profile, Security, Billing, Notifications)
│   └── studio/
│       ├── video/new/page.tsx        ← New project onboarding
│       ├── video/[projectId]/page.tsx ← Video Creation Studio
│       ├── image/[projectId]/page.tsx ← Image Editor
│       └── trimmer/page.tsx          ← Video Trimmer
│
├── components/
│   ├── layout/
│   │   ├── Navbar.tsx                ← Sticky nav, dropdowns, mobile sheet
│   │   ├── Footer.tsx                ← 4-column dark footer
│   │   └── SiteChrome.tsx            ← Wraps pages in Navbar+Footer (skips dashboard/studio)
│   ├── ui/
│   │   └── LogoMark.tsx              ← Inline SVG brand icon (used everywhere)
│   ├── sections/                     ← Landing page sections
│   │   ├── TemplateGallery.tsx       ← Accepts adminItems prop from server
│   │   └── template-gallery-data.ts  ← Hardcoded fallback + TemplateItem type
│   ├── dashboard/
│   │   └── settings/                 ← Settings sub-components
│   │       ├── SettingsProfile.tsx
│   │       ├── SettingsSecurity.tsx
│   │       ├── SettingsBilling.tsx
│   │       └── SettingsNotifications.tsx
│   ├── studio/                       ← Video Studio (Konva canvas)
│   ├── image-editor/                 ← Image Editor (Konva canvas)
│   └── templates/                    ← Templates page components
│
├── lib/
│   ├── admin-api.ts                  ← fetchCategories, fetchProjects, fetchProject
│   ├── supabase/
│   │   ├── server.ts                 ← Server-side Supabase client
│   │   ├── client.ts                 ← Browser-side Supabase client
│   │   └── middleware.ts             ← Session refresh
│   ├── studio-store.ts               ← Video Studio Zustand store
│   ├── image-editor-store.ts         ← Image Editor Zustand store
│   ├── video-templates-catalog.ts    ← Hardcoded + admin-to-catalog mapper
│   ├── plans.ts                      ← PlanId type, Stripe price helpers
│   └── profiles.ts                   ← getUserProfile (reads Supabase profiles table)
│
├── i18n/
│   ├── routing.ts                    ← locales: ["fa","en"], defaultLocale: "fa"
│   └── request.ts                    ← getRequestConfig (loads messages JSON)
│
└── middleware.ts                     ← next-intl + Supabase session

messages/
├── fa.json                           ← Persian translations (default locale)
└── en.json                           ← English translations

public/
└── favicon.svg                       ← SVG favicon (blue rounded square + play icon)

supabase/migrations/
├── 001_profiles.sql
├── 002_render_jobs.sql
└── 003_projects.sql

server/
├── render-worker.ts                  ← HTTP server port 3355
├── render-job-processor.ts
└── nexrender-job-builder.ts

🔗 Admin API Integration

The Next.js app fetches from the admin API server-side with ISR (60-second revalidation).

src/lib/admin-api.ts exports:

  • fetchCategories(type?)AdminCategory[]
  • fetchProjects(opts?)AdminProjectsResponse
  • fetchProject(slug)AdminProject | null
  • isAdminApiAvailable()boolean

Fallback behaviour: If ADMIN_API_URL is not set or the service is unreachable, all functions return empty arrays / null — the app works standalone with hardcoded data.

Wired pages:

  • app/[locale]/page.tsx → fetches 8 projects → passes adminItems to <TemplateGallery>
  • app/[locale]/templates/page.tsx → fetches 100 video projects → converts to VideoCatalogTemplate[] → passes initialCatalog to <VideoTemplatesPageContent>

🗄 Database (Supabase)

Run migrations in order in Supabase SQL Editor:

  1. supabase/migrations/001_profiles.sql — profiles (plan, billing_period, stripe IDs)
  2. supabase/migrations/002_render_jobs.sql — render_jobs
  3. supabase/migrations/003_projects.sql — projects + scene_data

What Is Complete

  • All marketing pages (Home, Video Maker, Image Maker, Pricing, Templates)
  • Full Video Creation Studio (Konva canvas, timeline, layers, transitions, undo/redo)
  • Full Image Editor (Konva canvas, filters, crop, adjustments, layers)
  • Video Trimmer (ffmpeg.wasm, frame strip, export)
  • Auth (Supabase, email/password, Google OAuth)
  • Dashboard (project grid, plan badge, settings page)
  • Stripe checkout + webhook for plan upgrades
  • Admin API backend (full CRUD for categories, projects, media + public endpoints)
  • Admin UI (dashboard, projects, categories, media library, settings)
  • i18n for all public pages (fa + en)
  • Logo brand mark (inline SVG LogoMark component)
  • RTL font (Vazirmatn forced via [dir="rtl"] CSS)

What Still Needs Setup (credentials required)

Item Action needed
Supabase Create project → get URL + anon key + service key → add to .env.local → run 3 SQL migrations
Stripe Create products with 4 prices → add price IDs to .env.local
Admin API DB Fill appsettings.Development.json with real Postgres connection string
MinIO Run MinIO locally or use S3 → update admin appsettings.Development.json
Real template assets Upload via admin panel → auto-appears on website
Video rendering Set RENDER_MOCK=false + NEXRENDER_BINARY path

📝 Coding Conventions

  • Server components for data fetching; "use client" for interactivity
  • All new translation strings go in both messages/fa.json AND messages/en.json
  • Never hardcode English strings in components — use useTranslations()
  • Logo: always use <LogoMark size={36} /> — never the old <Sparkles> icon
  • Admin API calls in page.tsx only (server-side), never in client components
  • TypeScript strict — run npx tsc --noEmit before committing
  • Tailwind only — no inline styles except Framer Motion animations