Overview
ddsite uses JSON Schema to define page content. Each template renders the same JSON structure with different visual styles.
For AI: When generating content, output valid JSON that matches the schema below. The frontend will render it using the site's selected template.
Key Concepts
- Site: A website belonging to a tenant, has one template
- Page: Content stored as JSON (draft_json / published_json)
- Template: React components that render JSON into HTML
- Section: Reusable content blocks (hero, about, services, etc.)
Architecture
┌─────────────────────────────────────────────────────────┐
│ ddsite Flow │
├─────────────────────────────────────────────────────────┤
│ │
│ User Input ──→ AI Generate ──→ JSON Schema ──→ DB │
│ │ │ │ │ │
│ "我是美甲店" Claude API { hero: {...} } pages │
│ table │
│ │
│ Template ←── JSON Data ←── API ←── Frontend Request │
│ │ │ │
│ restaurant/ published_json │
│ hotel/ │
│ salon/ │
│ │
└─────────────────────────────────────────────────────────┘
JSON Schema Structure
Every page follows this base structure:
{
"meta": {
"title": "頁面標題 - 用於 SEO",
"description": "頁面描述 - 用於 SEO",
"keywords": ["關鍵字1", "關鍵字2"]
},
"hero": {
"title": "主標題",
"subtitle": "副標題",
"backgroundImage": "https://...",
"ctaText": "立即預約",
"ctaLink": "#contact"
},
"sections": [
{
"type": "about",
"props": { ... }
},
{
"type": "services",
"props": { ... }
}
],
"footer": {
"copyright": "© 2024 店名",
"links": []
}
}
Field Descriptions
| Field | Type | Status | Description |
|---|---|---|---|
meta |
Object | Optional | SEO metadata |
hero |
Object | Required | Hero section at top of page |
sections |
Array | Required | Array of content sections |
footer |
Object | Optional | Footer content |
Component Types
Hero Component
{
"hero": {
"title": "string", // Main heading
"subtitle": "string", // Subheading
"backgroundImage": "string", // URL or gradient
"backgroundOverlay": 0.5, // 0-1 darkness
"ctaText": "string", // Button text
"ctaLink": "string", // Button link
"alignment": "center" // left | center | right
}
}
Navigation Component
{
"nav": {
"logo": "string", // Logo URL or text
"links": [
{ "label": "首頁", "href": "/" },
{ "label": "服務", "href": "#services" },
{ "label": "聯絡", "href": "#contact" }
],
"style": "fixed" // fixed | static
}
}
Section Types
About Section
{
"type": "about",
"props": {
"title": "關於我們",
"content": "店家介紹文字...",
"image": "https://...",
"imagePosition": "right" // left | right
}
}
Services Section
{
"type": "services",
"props": {
"title": "服務項目",
"subtitle": "我們提供以下專業服務",
"items": [
{
"icon": "💅", // Emoji or icon name
"title": "美甲服務",
"description": "專業美甲設計",
"price": "NT$800 起",
"image": "https://..."
}
],
"columns": 3 // 2 | 3 | 4
}
}
Gallery Section
{
"type": "gallery",
"props": {
"title": "作品集",
"images": [
{
"url": "https://...",
"alt": "作品描述",
"caption": "可選標題"
}
],
"layout": "grid" // grid | masonry | carousel
}
}
Testimonials Section
{
"type": "testimonials",
"props": {
"title": "客戶評價",
"items": [
{
"content": "服務很棒!",
"author": "王小姐",
"rating": 5,
"avatar": "https://..."
}
]
}
}
Contact Section
{
"type": "contact",
"props": {
"title": "聯絡我們",
"phone": "02-1234-5678",
"email": "info@example.com",
"address": "台北市...",
"hours": "週一至週五 10:00-20:00",
"mapEmbed": "https://maps.google.com/...",
"showForm": true,
"formFields": ["name", "email", "phone", "message"]
}
}
Features Section
{
"type": "features",
"props": {
"title": "為什麼選擇我們",
"items": [
{
"icon": "⭐",
"title": "專業認證",
"description": "10年以上經驗"
}
]
}
}
CTA Section
{
"type": "cta",
"props": {
"title": "立即預約",
"subtitle": "享受專屬優惠",
"buttonText": "線上預約",
"buttonLink": "#contact",
"background": "gradient" // gradient | image | solid
}
}
Pricing Section
{
"type": "pricing",
"props": {
"title": "價目表",
"categories": [
{
"name": "基礎服務",
"items": [
{ "name": "單色美甲", "price": "NT$500" },
{ "name": "漸層美甲", "price": "NT$800" }
]
}
]
}
}
FAQ Section
{
"type": "faq",
"props": {
"title": "常見問題",
"items": [
{
"question": "需要預約嗎?",
"answer": "建議事先預約以確保服務時段。"
}
]
}
}
Creating a Template
File Structure
shared/templates/
├── base/ # Base components (shared)
│ ├── Hero.tsx
│ ├── Section.tsx
│ └── Footer.tsx
├── restaurant/ # Restaurant template
│ ├── index.tsx # Template entry
│ ├── styles.css
│ └── components/
│ ├── MenuSection.tsx
│ └── ReservationCTA.tsx
├── hotel/ # Hotel template
│ ├── index.tsx
│ └── components/
│ ├── RoomCard.tsx
│ └── AmenitiesGrid.tsx
└── salon/ # Salon template
└── ...
Template Entry Point
// shared/templates/salon/index.tsx
import { PageSchema } from '@/types/schema';
import { Hero } from '../base/Hero';
import { SectionRenderer } from '../base/Section';
interface Props {
data: PageSchema;
}
export default function SalonTemplate({ data }: Props) {
return (
<div className="salon-template">
<Hero {...data.hero} theme="elegant" />
{data.sections.map((section, i) => (
<SectionRenderer
key={i}
section={section}
theme="salon"
/>
))}
<Footer {...data.footer} />
</div>
);
}
Template Registration
// shared/templates/index.ts
import RestaurantTemplate from './restaurant';
import HotelTemplate from './hotel';
import SalonTemplate from './salon';
export const templates = {
restaurant: RestaurantTemplate,
hotel: HotelTemplate,
salon: SalonTemplate,
} as const;
export type TemplateName = keyof typeof templates;
Complete Examples
Nail Salon Page
{
"meta": {
"title": "粉紅指尖美甲工作室 | 專業美甲服務",
"description": "台北最專業的美甲工作室,提供凝膠美甲、光療指甲等服務"
},
"hero": {
"title": "粉紅指尖",
"subtitle": "讓指尖綻放專屬於你的美麗",
"backgroundImage": "https://images.unsplash.com/photo-nail-salon",
"ctaText": "立即預約",
"ctaLink": "#contact"
},
"sections": [
{
"type": "about",
"props": {
"title": "關於我們",
"content": "粉紅指尖成立於2018年,我們相信每個人都值得擁有美麗的指尖...",
"image": "https://...",
"imagePosition": "right"
}
},
{
"type": "services",
"props": {
"title": "服務項目",
"items": [
{
"icon": "💅",
"title": "凝膠美甲",
"description": "持久亮麗的凝膠指甲",
"price": "NT$800 起"
},
{
"icon": "✨",
"title": "光療指甲",
"description": "快速固化,自然光澤",
"price": "NT$1,000 起"
},
{
"icon": "💎",
"title": "指甲彩繪",
"description": "客製化藝術設計",
"price": "NT$1,200 起"
}
],
"columns": 3
}
},
{
"type": "gallery",
"props": {
"title": "作品集",
"images": [
{ "url": "https://...", "alt": "法式美甲" },
{ "url": "https://...", "alt": "漸層設計" },
{ "url": "https://...", "alt": "季節限定款" }
],
"layout": "grid"
}
},
{
"type": "testimonials",
"props": {
"title": "客戶好評",
"items": [
{
"content": "技術超棒,而且環境很舒適!",
"author": "Amy",
"rating": 5
}
]
}
},
{
"type": "contact",
"props": {
"title": "預約諮詢",
"phone": "02-2345-6789",
"address": "台北市大安區...",
"hours": "週二至週日 11:00-20:00",
"showForm": true
}
}
],
"footer": {
"copyright": "© 2024 粉紅指尖美甲工作室"
}
}
Hotel Page
{
"meta": {
"title": "山水渡假飯店 | 宜蘭頂級住宿",
"description": "座落於宜蘭山水之間的頂級渡假飯店"
},
"hero": {
"title": "山水渡假飯店",
"subtitle": "在自然中找到寧靜",
"backgroundImage": "https://...",
"backgroundOverlay": 0.4,
"ctaText": "查看房型",
"ctaLink": "#rooms"
},
"sections": [
{
"type": "about",
"props": {
"title": "關於山水",
"content": "山水渡假飯店座落於宜蘭礁溪,擁有絕美山景與溫泉設施..."
}
},
{
"type": "rooms",
"props": {
"title": "精選房型",
"items": [
{
"name": "山景雙人房",
"description": "180度山景視野",
"price": "NT$4,800/晚",
"image": "https://...",
"amenities": ["免費WiFi", "山景陽台", "迷你吧"]
},
{
"name": "溫泉套房",
"description": "私人湯屋",
"price": "NT$6,800/晚",
"image": "https://...",
"amenities": ["私人溫泉", "King Size床", "客廳"]
}
]
}
},
{
"type": "features",
"props": {
"title": "飯店設施",
"items": [
{ "icon": "♨️", "title": "溫泉SPA", "description": "天然碳酸氫鈉泉" },
{ "icon": "🍽️", "title": "景觀餐廳", "description": "在地食材料理" },
{ "icon": "🏊", "title": "無邊際泳池", "description": "俯瞰蘭陽平原" }
]
}
},
{
"type": "contact",
"props": {
"title": "訂房資訊",
"phone": "03-988-1234",
"email": "booking@shanshui.com",
"address": "宜蘭縣礁溪鄉...",
"mapEmbed": "https://maps.google.com/..."
}
}
]
}
Real Estate Agent Page (個人品牌)
{
"meta": {
"title": "陳建宏 | 信義房屋資深經理 | 大安區房產專家",
"description": "專注大安區房產20年,為您找到理想的家"
},
"hero": {
"title": "陳建宏",
"subtitle": "您的房產投資最佳夥伴",
"backgroundImage": "https://images.unsplash.com/photo-1560518883-ce09059eeffa?w=1920",
"backgroundOverlay": 0.5,
"ctaText": "立即諮詢",
"ctaLink": "#contact"
},
"sections": [
{
"type": "about",
"props": {
"title": "關於我",
"content": "我是陳建宏,從事房地產業已超過20年。專注於台北市大安區、信義區的中高端住宅市場,累積成交超過500件案件,成交總額突破50億。我相信每一位客戶都值得最專業的服務,無論是首購族還是投資客,我都會以同樣的熱忱協助您找到最適合的房產。",
"image": "https://images.unsplash.com/photo-1560250097-0b93528c311a?w=800",
"imagePosition": "right"
}
},
{
"type": "features",
"props": {
"title": "我的優勢",
"items": [
{
"icon": "🏆",
"title": "20年經驗",
"description": "深耕大安區房產市場,熟悉每一條街道"
},
{
"icon": "📊",
"title": "500+ 成交",
"description": "豐富的談判經驗,為您爭取最佳價格"
},
{
"icon": "🤝",
"title": "誠信服務",
"description": "透明公開,不隱瞞任何屋況資訊"
},
{
"icon": "📱",
"title": "全天候服務",
"description": "隨時為您解答房產相關問題"
}
]
}
},
{
"type": "services",
"props": {
"title": "服務項目",
"items": [
{
"icon": "🏠",
"title": "買屋服務",
"description": "根據您的需求,精準配對理想物件,從看屋到交屋全程陪同",
"image": "https://images.unsplash.com/photo-1600596542815-ffad4c1539a9?w=600"
},
{
"icon": "💰",
"title": "賣屋服務",
"description": "專業估價、精美攝影、多平台曝光,讓您的房產快速成交",
"image": "https://images.unsplash.com/photo-1600585154340-be6161a56a0c?w=600"
},
{
"icon": "📈",
"title": "投資諮詢",
"description": "分析市場趨勢,提供置產規劃建議,最大化投資報酬",
"image": "https://images.unsplash.com/photo-1551836022-d5d88e9218df?w=600"
}
],
"columns": 3
}
},
{
"type": "gallery",
"props": {
"title": "近期成交案例",
"images": [
{
"url": "https://images.unsplash.com/photo-1600607687939-ce8a6c25118c?w=800",
"alt": "大安區電梯大樓",
"caption": "大安區|電梯3房|成交價 3,280萬"
},
{
"url": "https://images.unsplash.com/photo-1600566753190-17f0baa2a6c3?w=800",
"alt": "信義區豪宅",
"caption": "信義區|景觀4房|成交價 6,800萬"
},
{
"url": "https://images.unsplash.com/photo-1600585154526-990dced4db0d?w=800",
"alt": "中正區公寓",
"caption": "中正區|翻新2房|成交價 1,680萬"
},
{
"url": "https://images.unsplash.com/photo-1600573472591-ee6c563aaec9?w=800",
"alt": "松山區新成屋",
"caption": "松山區|新成屋|成交價 4,200萬"
}
],
"layout": "grid"
}
},
{
"type": "testimonials",
"props": {
"title": "客戶見證",
"items": [
{
"content": "建宏經理非常專業,幫我們找到了心目中理想的房子,整個過程都很順利。從看屋到簽約只花了一個月!",
"author": "林先生 (首購族)",
"rating": 5
},
{
"content": "賣房的過程比我想像中快很多,建宏幫我把房子拍得很漂亮,兩週就有人出價,最後成交價還超過我的預期。",
"author": "王太太 (屋主)",
"rating": 5
},
{
"content": "已經跟建宏經理合作買過三間房了,每次都很滿意。他對市場的判斷很準確,是我信任的房產顧問。",
"author": "張董 (投資客)",
"rating": 5
}
]
}
},
{
"type": "cta",
"props": {
"title": "想買房或賣房?",
"subtitle": "免費諮詢,沒有壓力",
"buttonText": "LINE 我聊聊",
"buttonLink": "https://line.me/ti/p/xxxxx",
"background": "gradient"
}
},
{
"type": "contact",
"props": {
"title": "聯絡方式",
"phone": "0912-345-678",
"email": "chen.realtor@example.com",
"address": "台北市大安區忠孝東路四段100號",
"hours": "週一至週日 09:00-21:00",
"showForm": true,
"formFields": ["name", "phone", "message"],
"socialLinks": [
{ "platform": "LINE", "url": "https://line.me/ti/p/xxxxx" },
{ "platform": "Facebook", "url": "https://facebook.com/chen.realtor" }
]
}
}
],
"footer": {
"copyright": "© 2024 陳建宏 | 信義房屋大安店",
"links": [
{ "label": "隱私權政策", "href": "/privacy" }
]
}
}
Best Practices
For AI Content Generation
- Always output valid JSON - use proper escaping for quotes
- Keep text concise - hero subtitles under 50 characters
- Use Traditional Chinese (繁體中文) for Taiwan market
- Include realistic placeholder images from Unsplash
- Add 3-6 sections for a complete page
- Always include contact section with business hours
Image Guidelines
- Hero images: 1920x1080 or 16:9 ratio
- Gallery images: 800x600 or 4:3 ratio
- Service icons: Use emoji or icon names
- Use HTTPS URLs only
Section Order Recommendation
- Hero (required)
- About / Introduction
- Services / Products
- Gallery / Portfolio
- Testimonials / Reviews
- Pricing (if applicable)
- FAQ (optional)
- CTA (optional)
- Contact (required)
Avoid:
- Duplicate section types (except gallery)
- Empty arrays or null values
- External scripts or iframes (except maps)
- Hardcoded colors in content (use template theme)
API Reference
Get Page Data
GET /api/public/sites/{siteSlug}/pages/{pageSlug}
Response:
{
"id": 1,
"slug": "home",
"title": "首頁",
"publishedJson": { ... },
"site": {
"name": "店名",
"slug": "site-slug",
"template": "salon"
}
}
Update Page (Admin)
PATCH /api/sites/{siteId}/pages/{pageId}
Authorization: Bearer {token}
Body:
{
"draftJson": { ... }
}
Publish Page
POST /api/sites/{siteId}/pages/{pageId}/publish
Authorization: Bearer {token}
// Copies draftJson to publishedJson
AI Generate Content
POST /api/chatbot/generate
Authorization: Bearer {token}
Body:
{
"siteId": 1,
"prompt": "我是一間美甲店,叫做粉紅指尖,位於台北大安區..."
}
Response:
{
"generatedJson": { ... },
"previewUrl": "https://..."
}
TypeScript Type Definitions
// types/schema.ts
interface PageSchema {
meta?: MetaData;
hero: HeroSection;
sections: Section[];
footer?: FooterData;
}
interface MetaData {
title: string;
description?: string;
keywords?: string[];
}
interface HeroSection {
title: string;
subtitle?: string;
backgroundImage?: string;
backgroundOverlay?: number;
ctaText?: string;
ctaLink?: string;
alignment?: 'left' | 'center' | 'right';
}
interface Section {
type: SectionType;
props: Record<string, any>;
}
type SectionType =
| 'about'
| 'services'
| 'gallery'
| 'testimonials'
| 'contact'
| 'features'
| 'cta'
| 'pricing'
| 'faq'
| 'rooms' // Hotel specific
| 'menu'; // Restaurant specific
interface FooterData {
copyright?: string;
links?: { label: string; href: string }[];
socials?: { platform: string; url: string }[];
}