@sellhapi/mcp 0.0.0-stage → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +142 -2
- package/dist/client.d.ts +33 -0
- package/dist/client.js +99 -0
- package/dist/hosted.d.ts +25 -0
- package/dist/hosted.js +234 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +54 -0
- package/dist/resources/build-rules.d.ts +5 -0
- package/dist/resources/build-rules.js +151 -0
- package/dist/resources/capability-patterns.d.ts +15 -0
- package/dist/resources/capability-patterns.js +339 -0
- package/dist/resources/design-direction-schema.d.ts +11 -0
- package/dist/resources/design-direction-schema.js +252 -0
- package/dist/resources/index.d.ts +9 -0
- package/dist/resources/index.js +55 -0
- package/dist/resources/runtime-hooks.d.ts +5 -0
- package/dist/resources/runtime-hooks.js +269 -0
- package/dist/resources/wireframe-schema.d.ts +8 -0
- package/dist/resources/wireframe-schema.js +220 -0
- package/dist/server.d.ts +8 -0
- package/dist/server.js +27 -0
- package/dist/tools/commerce.d.ts +9 -0
- package/dist/tools/commerce.js +86 -0
- package/dist/tools/content.d.ts +8 -0
- package/dist/tools/content.js +49 -0
- package/dist/tools/intelligence.d.ts +5 -0
- package/dist/tools/intelligence.js +39 -0
- package/dist/tools/studio.d.ts +5 -0
- package/dist/tools/studio.js +342 -0
- package/dist/util.d.ts +16 -0
- package/dist/util.js +21 -0
- package/package.json +36 -4
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* DesignDirection schema resource.
|
|
4
|
+
*
|
|
5
|
+
* Exposes the exact DesignDirection TypeScript interface + field documentation
|
|
6
|
+
* so AI clients can generate a valid direction JSON themselves (zero SellHapi
|
|
7
|
+
* Claude cost) and submit it via the apply_design_direction tool.
|
|
8
|
+
*
|
|
9
|
+
* Mirrors the interface in generate2.controller.ts. Keep in sync when that
|
|
10
|
+
* interface changes.
|
|
11
|
+
*/
|
|
12
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
+
exports.DESIGN_DIRECTION_SCHEMA = void 0;
|
|
14
|
+
exports.DESIGN_DIRECTION_SCHEMA = `
|
|
15
|
+
# SellHapi Design Direction Schema
|
|
16
|
+
|
|
17
|
+
Use this schema to generate a \`DesignDirection\` JSON object and submit it via
|
|
18
|
+
\`apply_design_direction\`. This path costs SellHapi nothing — your own LLM does
|
|
19
|
+
the work. Only fall back to \`set_design_direction\` if you cannot generate
|
|
20
|
+
structured JSON reliably.
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## Top-level object
|
|
25
|
+
|
|
26
|
+
\`\`\`typescript
|
|
27
|
+
{
|
|
28
|
+
businessType: string; // REQUIRED. e.g. "fashion", "bakery", "photography"
|
|
29
|
+
capabilityIds: string[]; // REQUIRED. Active capability IDs. See /api/mcp/capabilities.
|
|
30
|
+
capabilitySlots?: CapabilitySlot[]; // Optional. Richer slot-per-purpose model (see below).
|
|
31
|
+
pages: Page[]; // REQUIRED. Non-empty. One entry per storefront page.
|
|
32
|
+
visualDirection: VisualDirection; // REQUIRED. All visual tokens.
|
|
33
|
+
businessContext: BusinessContext; // REQUIRED. Business identity.
|
|
34
|
+
storefrontConfig?: StorefrontConfig; // Optional. Only for storefront-mode projects.
|
|
35
|
+
displayModes?: DisplayModes; // Optional. Product display preferences.
|
|
36
|
+
}
|
|
37
|
+
\`\`\`
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## Page
|
|
42
|
+
|
|
43
|
+
Each page the storefront will have. Minimum 1, typically 3–6.
|
|
44
|
+
|
|
45
|
+
\`\`\`typescript
|
|
46
|
+
{
|
|
47
|
+
slug: string; // URL-safe identifier. e.g. "home", "products", "about", "contact"
|
|
48
|
+
title: string; // Display name. e.g. "Home", "Shop", "About Us"
|
|
49
|
+
sections: string[]; // Section labels in order. e.g. ["hero", "product-grid", "testimonials", "newsletter"]
|
|
50
|
+
}
|
|
51
|
+
\`\`\`
|
|
52
|
+
|
|
53
|
+
**Section label conventions:**
|
|
54
|
+
- Use lowercase kebab-case
|
|
55
|
+
- Should describe content purpose: "hero", "featured-products", "services-list", "booking-cta",
|
|
56
|
+
"testimonials", "faq", "team", "newsletter", "about-story", "contact-form", "events-grid"
|
|
57
|
+
- Capability-specific sections should reference the capability:
|
|
58
|
+
"appointment-booking", "donation-campaigns", "membership-tiers", "events-list"
|
|
59
|
+
|
|
60
|
+
**Common page structures:**
|
|
61
|
+
|
|
62
|
+
Physical products store:
|
|
63
|
+
\`\`\`
|
|
64
|
+
home: ["hero", "featured-products", "collections", "testimonials", "newsletter"]
|
|
65
|
+
products: ["filter-bar", "product-grid", "pagination"]
|
|
66
|
+
about: ["brand-story", "values", "team"]
|
|
67
|
+
contact: ["contact-form", "location-map"]
|
|
68
|
+
\`\`\`
|
|
69
|
+
|
|
70
|
+
Service business:
|
|
71
|
+
\`\`\`
|
|
72
|
+
home: ["hero", "services-overview", "how-it-works", "testimonials", "booking-cta"]
|
|
73
|
+
services: ["services-list", "pricing"]
|
|
74
|
+
about: ["our-story", "team", "credentials"]
|
|
75
|
+
contact: ["contact-form", "faq"]
|
|
76
|
+
\`\`\`
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## VisualDirection
|
|
81
|
+
|
|
82
|
+
All fields except the optional ones are required.
|
|
83
|
+
|
|
84
|
+
\`\`\`typescript
|
|
85
|
+
{
|
|
86
|
+
// Descriptive aesthetic — free text. e.g. "Minimal luxury", "Vibrant street market"
|
|
87
|
+
style: string;
|
|
88
|
+
|
|
89
|
+
primaryColor: string; // Main brand color — hex. e.g. "#1a1a2e"
|
|
90
|
+
accentColor: string; // CTA / highlight color — hex. e.g. "#e94560"
|
|
91
|
+
backgroundColor: string; // Page background — hex. Can be dark, cream, off-white.
|
|
92
|
+
surfaceColor: string; // Card / panel background — hex.
|
|
93
|
+
textColor: string; // Primary text color — hex.
|
|
94
|
+
|
|
95
|
+
headingFont: string; // Google Fonts name for headings. e.g. "Playfair Display", "Space Grotesk"
|
|
96
|
+
bodyFont: string; // Google Fonts name for body copy. e.g. "Inter", "DM Sans"
|
|
97
|
+
fontStyle: string; // Description. e.g. "editorial serif headings with clean sans body"
|
|
98
|
+
|
|
99
|
+
// Enum values — use exactly as written:
|
|
100
|
+
cardStyle: "flat" | "soft-shadow" | "bordered" | "glassmorphism";
|
|
101
|
+
buttonStyle: "pill" | "rounded" | "sharp" | "outline-first";
|
|
102
|
+
visualEffect: "flat" | "subtle-gradient" | "glassmorphism" | "textured";
|
|
103
|
+
|
|
104
|
+
mood: string; // e.g. "sophisticated and aspirational", "warm and approachable"
|
|
105
|
+
|
|
106
|
+
// Optional — include when you have reference site analysis or strong design intent:
|
|
107
|
+
layoutPattern?: string; // e.g. "full-bleed hero, 3-col card grid, alternating split sections"
|
|
108
|
+
spacingRhythm?: string; // e.g. "generous whitespace, section padding 80–120px"
|
|
109
|
+
typographyScale?: string; // e.g. "oversized display ~72px, 17px body, tight heading tracking"
|
|
110
|
+
componentNotes?: string; // e.g. "rounded cards with drop shadow, pill category tags"
|
|
111
|
+
distinctiveElements?: string; // e.g. "full-bleed photography, sticky glassmorphic nav"
|
|
112
|
+
}
|
|
113
|
+
\`\`\`
|
|
114
|
+
|
|
115
|
+
**Color guidance:**
|
|
116
|
+
- All color fields must be valid hex strings (#rrggbb or #rgb)
|
|
117
|
+
- Ensure sufficient contrast: textColor vs backgroundColor ≥ 4.5:1 (WCAG AA)
|
|
118
|
+
- primaryColor and accentColor should be visually distinct
|
|
119
|
+
- surfaceColor is usually a slightly lighter/darker variant of backgroundColor
|
|
120
|
+
|
|
121
|
+
**Font guidance:**
|
|
122
|
+
- Use Google Fonts names exactly as they appear on fonts.google.com
|
|
123
|
+
- Pairing examples: "Playfair Display" + "Inter", "Space Grotesk" + "DM Sans",
|
|
124
|
+
"Syne" + "Outfit", "Cormorant Garamond" + "Jost"
|
|
125
|
+
|
|
126
|
+
---
|
|
127
|
+
|
|
128
|
+
## BusinessContext
|
|
129
|
+
|
|
130
|
+
\`\`\`typescript
|
|
131
|
+
{
|
|
132
|
+
name: string; // REQUIRED. Business name. e.g. "Adire Lagos"
|
|
133
|
+
tagline: string; // One-line value prop. e.g. "Authentic Nigerian fashion for the bold"
|
|
134
|
+
targetAudience: string; // e.g. "Nigerian professionals aged 25–40 who value craftsmanship"
|
|
135
|
+
uniqueSellingPoints: string[]; // 2–4 bullets. e.g. ["Handcrafted in Lagos", "Free delivery over ₦15,000"]
|
|
136
|
+
}
|
|
137
|
+
\`\`\`
|
|
138
|
+
|
|
139
|
+
---
|
|
140
|
+
|
|
141
|
+
## CapabilitySlot (optional, more expressive than capabilityIds alone)
|
|
142
|
+
|
|
143
|
+
\`\`\`typescript
|
|
144
|
+
{
|
|
145
|
+
purpose: string; // Human-readable job. e.g. "Let customers book sessions"
|
|
146
|
+
capability: string; // Selected capability ID. e.g. "appointments"
|
|
147
|
+
alternatives: string[]; // Valid swaps for this slot. Always include the selected capability.
|
|
148
|
+
}
|
|
149
|
+
\`\`\`
|
|
150
|
+
|
|
151
|
+
---
|
|
152
|
+
|
|
153
|
+
## StorefrontConfig (optional)
|
|
154
|
+
|
|
155
|
+
\`\`\`typescript
|
|
156
|
+
{
|
|
157
|
+
storeType: "fashion" | "beauty" | "food" | "electronics" | "general";
|
|
158
|
+
vendorOverrides: string[]; // Free-text additions. e.g. ["Feature collections on homepage"]
|
|
159
|
+
}
|
|
160
|
+
\`\`\`
|
|
161
|
+
|
|
162
|
+
---
|
|
163
|
+
|
|
164
|
+
## DisplayModes (optional — only for stores with physical_products)
|
|
165
|
+
|
|
166
|
+
\`\`\`typescript
|
|
167
|
+
{
|
|
168
|
+
productGrid: boolean; // Main product grid (useProducts)
|
|
169
|
+
collections: boolean; // Product collections (useProductCollections)
|
|
170
|
+
sections: boolean; // Category sections (useSections)
|
|
171
|
+
bundles: boolean; // Bundles & deals (useStorefrontBundles)
|
|
172
|
+
}
|
|
173
|
+
\`\`\`
|
|
174
|
+
|
|
175
|
+
---
|
|
176
|
+
|
|
177
|
+
## Full example — Lagos fashion store
|
|
178
|
+
|
|
179
|
+
\`\`\`json
|
|
180
|
+
{
|
|
181
|
+
"businessType": "fashion",
|
|
182
|
+
"capabilityIds": ["physical_products", "content", "customer_accounts"],
|
|
183
|
+
"pages": [
|
|
184
|
+
{
|
|
185
|
+
"slug": "home",
|
|
186
|
+
"title": "Home",
|
|
187
|
+
"sections": ["hero", "featured-products", "collections", "brand-story", "testimonials", "newsletter"]
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
"slug": "shop",
|
|
191
|
+
"title": "Shop",
|
|
192
|
+
"sections": ["filter-bar", "product-grid"]
|
|
193
|
+
},
|
|
194
|
+
{
|
|
195
|
+
"slug": "about",
|
|
196
|
+
"title": "About",
|
|
197
|
+
"sections": ["brand-story", "values", "team"]
|
|
198
|
+
},
|
|
199
|
+
{
|
|
200
|
+
"slug": "contact",
|
|
201
|
+
"title": "Contact",
|
|
202
|
+
"sections": ["contact-form", "faq"]
|
|
203
|
+
}
|
|
204
|
+
],
|
|
205
|
+
"visualDirection": {
|
|
206
|
+
"style": "Minimal luxury",
|
|
207
|
+
"primaryColor": "#1a1a2e",
|
|
208
|
+
"accentColor": "#c9a84c",
|
|
209
|
+
"backgroundColor": "#fafaf8",
|
|
210
|
+
"surfaceColor": "#ffffff",
|
|
211
|
+
"textColor": "#1a1a2e",
|
|
212
|
+
"headingFont": "Cormorant Garamond",
|
|
213
|
+
"bodyFont": "Inter",
|
|
214
|
+
"fontStyle": "editorial serif headings with clean sans body",
|
|
215
|
+
"cardStyle": "soft-shadow",
|
|
216
|
+
"buttonStyle": "sharp",
|
|
217
|
+
"visualEffect": "flat",
|
|
218
|
+
"mood": "sophisticated and aspirational",
|
|
219
|
+
"layoutPattern": "full-bleed hero, 2-col editorial split, 3-col product grid",
|
|
220
|
+
"spacingRhythm": "generous whitespace, 100–120px section padding",
|
|
221
|
+
"typographyScale": "oversized display 72px, 16px body, tight tracking on headings"
|
|
222
|
+
},
|
|
223
|
+
"businessContext": {
|
|
224
|
+
"name": "Adire Lagos",
|
|
225
|
+
"tagline": "Authentic Nigerian fashion for the bold",
|
|
226
|
+
"targetAudience": "Nigerian professionals aged 25–40 who value craftsmanship",
|
|
227
|
+
"uniqueSellingPoints": [
|
|
228
|
+
"Handcrafted adire fabric from Abeokuta",
|
|
229
|
+
"Free delivery over ₦15,000",
|
|
230
|
+
"30-day returns"
|
|
231
|
+
]
|
|
232
|
+
},
|
|
233
|
+
"storefrontConfig": {
|
|
234
|
+
"storeType": "fashion",
|
|
235
|
+
"vendorOverrides": []
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
\`\`\`
|
|
239
|
+
|
|
240
|
+
---
|
|
241
|
+
|
|
242
|
+
## Submission
|
|
243
|
+
|
|
244
|
+
Once you have generated the direction JSON and the user has reviewed it:
|
|
245
|
+
|
|
246
|
+
\`\`\`
|
|
247
|
+
apply_design_direction(projectId, direction)
|
|
248
|
+
\`\`\`
|
|
249
|
+
|
|
250
|
+
The server validates the shape, filters any unrecognised capabilityIds, and stores
|
|
251
|
+
the direction on the project. Status advances to "directing".
|
|
252
|
+
`;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP Resources — teach AI clients how to write valid SellHapi storefront code.
|
|
3
|
+
*
|
|
4
|
+
* These resources are automatically loaded by AI clients when they connect.
|
|
5
|
+
* They contain everything needed to generate React code that passes BuildGuard
|
|
6
|
+
* and correctly uses the @sellhapi/studio-runtime hooks.
|
|
7
|
+
*/
|
|
8
|
+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
9
|
+
export declare function registerResources(server: McpServer): void;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* MCP Resources — teach AI clients how to write valid SellHapi storefront code.
|
|
4
|
+
*
|
|
5
|
+
* These resources are automatically loaded by AI clients when they connect.
|
|
6
|
+
* They contain everything needed to generate React code that passes BuildGuard
|
|
7
|
+
* and correctly uses the @sellhapi/studio-runtime hooks.
|
|
8
|
+
*/
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.registerResources = registerResources;
|
|
11
|
+
const runtime_hooks_js_1 = require("./runtime-hooks.js");
|
|
12
|
+
const build_rules_js_1 = require("./build-rules.js");
|
|
13
|
+
const capability_patterns_js_1 = require("./capability-patterns.js");
|
|
14
|
+
const design_direction_schema_js_1 = require("./design-direction-schema.js");
|
|
15
|
+
const wireframe_schema_js_1 = require("./wireframe-schema.js");
|
|
16
|
+
function registerResources(server) {
|
|
17
|
+
// ── Runtime Hooks ──────────────────────────────────────────────────────────
|
|
18
|
+
server.registerResource("runtime-hooks", "sellhapi://runtime-hooks", {
|
|
19
|
+
description: "Complete reference for all @sellhapi/studio-runtime hooks — signatures, return shapes, and usage examples. Read this BEFORE generating any storefront code.",
|
|
20
|
+
mimeType: "text/plain",
|
|
21
|
+
}, async () => ({
|
|
22
|
+
contents: [{ uri: "sellhapi://runtime-hooks", text: runtime_hooks_js_1.RUNTIME_HOOKS, mimeType: "text/plain" }],
|
|
23
|
+
}));
|
|
24
|
+
// ── Build Rules ────────────────────────────────────────────────────────────
|
|
25
|
+
server.registerResource("build-rules", "sellhapi://build-rules", {
|
|
26
|
+
description: "BuildGuard validation rules — what code patterns are allowed and forbidden. Code that violates these rules will be rejected by push_code.",
|
|
27
|
+
mimeType: "text/plain",
|
|
28
|
+
}, async () => ({
|
|
29
|
+
contents: [{ uri: "sellhapi://build-rules", text: build_rules_js_1.BUILD_RULES, mimeType: "text/plain" }],
|
|
30
|
+
}));
|
|
31
|
+
// ── Design Direction Schema ────────────────────────────────────────────────
|
|
32
|
+
server.registerResource("design-direction-schema", "sellhapi://design-direction-schema", {
|
|
33
|
+
description: "Complete DesignDirection JSON schema with field documentation, enum values, font pairing guidance, and a full example. Read this before calling apply_design_direction — generate the direction object yourself (free) instead of using set_design_direction (costs Claude API credits).",
|
|
34
|
+
mimeType: "text/plain",
|
|
35
|
+
}, async () => ({
|
|
36
|
+
contents: [{ uri: "sellhapi://design-direction-schema", text: design_direction_schema_js_1.DESIGN_DIRECTION_SCHEMA, mimeType: "text/plain" }],
|
|
37
|
+
}));
|
|
38
|
+
// ── Wireframe Schema ───────────────────────────────────────────────────────
|
|
39
|
+
server.registerResource("wireframe-schema", "sellhapi://wireframe-schema", {
|
|
40
|
+
description: "Wireframe shape, section conventions, nav/footer rules, data-hook reference table, and user-presentation format. Read this before generating wireframes. The wireframe is the user's checkpoint — present each page to the user for approval before calling apply_wireframe.",
|
|
41
|
+
mimeType: "text/plain",
|
|
42
|
+
}, async () => ({
|
|
43
|
+
contents: [{ uri: "sellhapi://wireframe-schema", text: wireframe_schema_js_1.WIREFRAME_SCHEMA, mimeType: "text/plain" }],
|
|
44
|
+
}));
|
|
45
|
+
// ── Capability Patterns ────────────────────────────────────────────────────
|
|
46
|
+
// Fetched live from sell_api on each read — auto-generated from STUDIO_PROMPT_REGISTRY.
|
|
47
|
+
// Falls back to the bundled static copy if the API is unreachable.
|
|
48
|
+
server.registerResource("capability-patterns", "sellhapi://capability-patterns", {
|
|
49
|
+
description: "Per-capability data shapes, anti-patterns, hook checklists, and UX flows. Auto-generated from the live server registry — always up to date. Covers: products, digital products, services, appointments, events, donations, memberships, content, listings, customer auth.",
|
|
50
|
+
mimeType: "text/plain",
|
|
51
|
+
}, async () => {
|
|
52
|
+
const text = await (0, capability_patterns_js_1.buildCapabilityPatterns)();
|
|
53
|
+
return { contents: [{ uri: "sellhapi://capability-patterns", text, mimeType: "text/plain" }] };
|
|
54
|
+
});
|
|
55
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runtime hooks reference — extracted from @sellhapi/studio-runtime.
|
|
3
|
+
* This resource teaches AI clients the exact hook signatures and return shapes.
|
|
4
|
+
*/
|
|
5
|
+
export declare const RUNTIME_HOOKS = "\n# @sellhapi/studio-runtime \u2014 Hook Reference\n\nAll imports come from \"@sellhapi/studio-runtime\". No other network/data access is allowed.\n\n## IMPORTANT RULES\n- All prices are in kobo (divide by 100 for Naira display): product.priceKobo / 100\n- product.images is string[] \u2014 each element IS the URL. NEVER access .url on it.\n- Always include type: 'physical' | 'digital' when calling addItem()\n- If you use useCart/addItem, you MUST render <CartDrawer /> in App.tsx\n- FetchState<T> = { data: T; loading: boolean; error: string | null }\n\n---\n\n## ROUTING (required for multi-page storefronts)\n\nuseHashRouter()\n Returns: { route: string; params: Record<string, string>; navigate: (route: string) => void }\n Usage: const { route, navigate } = useHashRouter();\n Routes: \"#/\" (home), \"#/product/:id\", \"#/digital/:id\", \"#/services/:id\", etc.\n\n---\n\n## PHYSICAL PRODUCTS\n\nuseProducts(options?)\n Options: { limit?: number; categoryId?: string; collectionId?: string; sectionId?: string; sort?: \"newest\"|\"price_asc\"|\"price_desc\"; search?: string; ids?: string[] }\n Returns: FetchState<StoreProduct[]>\n StoreProduct: { id, name, priceKobo, compareAtPriceKobo, imageUrl, images: string[], category, type: \"physical\"|\"digital\", variants: StoreProductVariant[], inStock }\n\nuseProduct(productId: string)\n Returns: FetchState<StoreProductDetail | null>\n StoreProductDetail: extends StoreProduct + { description, variants: StoreProductVariant[] }\n StoreProductVariant: { id, size, color, priceKobo, compareAtPriceKobo, stock, imageUrl }\n\nuseCategories()\n Returns: FetchState<StoreCategory[]>\n StoreCategory: { name: string; count: number }\n\nuseProductCollections()\n Returns: FetchState<ProductCollection[]>\n ProductCollection: { id, name, coverImage: string | null }\n\nuseSections()\n Returns: FetchState<StoreSection[]>\nuseSection(sectionId: string)\n Returns: FetchState<StoreProduct[]>\n\n---\n\n## CART & CHECKOUT\n\nuseCart()\n Returns: {\n items: CartItem[]; count: number; totalKobo: number; savingsKobo: number; isEmpty: boolean;\n addItem: (item: { productId, name, priceKobo, imageUrl?, variantId?, variantLabel?, quantity?, type: \"physical\"|\"digital\" }) => void;\n removeItem: (productId, variantId?) => void;\n updateQty: (productId, quantity, variantId?) => void;\n clear: () => void;\n openCart: () => void; // MUST call after addItem \u2014 opens the cart drawer\n openCheckout: () => void;\n }\n\nCartDrawer \u2014 REQUIRED component if using cart. Import from \"@sellhapi/studio-runtime\".\n Usage: <CartDrawer /> in App.tsx root. Without it, openCart() does nothing.\n\nuseCartCheckout()\n Returns: { initiate: (customer: { name, email, phone, address? }, options?: { deliveryMethod?, shippingRateId?, discountCode?, note? }) => Promise<CommerceActionResult>; loading; error }\n CommerceActionResult: { paymentLink: string } \u2014 redirect to paymentLink\n\nuseProductCheckout()\n Returns: { initiate: (input: { productId, quantity, variantId?, customer: { name, email, phone, address? }, deliveryMethod? }) => Promise<CommerceActionResult>; loading; error }\n Use for single-product \"Buy Now\" flows only. For multi-product stores, use cart.\n\nuseShippingRates()\n Returns: FetchState<ShippingRate[]>\n ShippingRate: { id, name, amountKobo, estimatedDays }\n\nuseValidateDiscount()\n Returns: { validate: (code: string) => Promise<DiscountValidationResult>; validating; error }\n\nuseCheckoutConfig()\n Returns: FetchState<CheckoutConfig>\n CheckoutConfig: { deliveryEnabled, pickupEnabled, deliveryOnly, pickupOnly }\n\n---\n\n## DIGITAL PRODUCTS\n\nuseDigitalProducts(options?)\n Options: { limit?: number }\n Returns: FetchState<StoreProduct[]> (filtered to type=\"digital\")\n\nuseDigitalProduct(productId: string)\n Returns: FetchState<StoreProductDetail | null>\n\n---\n\n## SERVICES\n\nuseServiceListings(options?)\n Options: { limit?: number; category?: string }\n Returns: FetchState<StorefrontServiceListing[]>\n StorefrontServiceListing: { id, title, description, coverImage, category, priceKobo, availability, addOns: ServiceAddOn[], subServices: SubService[] }\n\nuseServiceListing(listingId: string)\n Returns: FetchState<StorefrontServiceListing | null>\n\nuseCheckoutService()\n Returns: { checkout: (input: { listingId, customer: { name, email, phone }, addOnIds?, subServiceIds? }) => Promise<{ paymentLink }>; loading; error }\n\nuseInitiateEngagement()\n Returns: { submit: (input: { listingId, customer: { name, email, phone }, message? }) => Promise<void>; submitting; submitted; error }\n\n---\n\n## APPOINTMENTS\n\nuseServices(options?)\n Options: { limit?: number }\n Returns: FetchState<StorefrontService[]>\n StorefrontService: { id, name, description, durationMinutes, priceKobo, imageUrl, category }\n\nuseAvailableDates(options)\n Options: { serviceId: string; month: string } (month format: \"YYYY-MM\")\n Returns: FetchState<string[]> \u2014 array of date strings \"YYYY-MM-DD\"\n\nuseAvailability(options)\n Options: { serviceId: string; date: string } (date format: \"YYYY-MM-DD\")\n Returns: FetchState<AvailabilitySlot[]>\n AvailabilitySlot: { id, startTime, endTime, status }\n\nuseBookAppointment()\n Returns: { initiate: (input: { serviceId, slotId, customer: { name, email, phone } }) => Promise<CommerceActionResult>; loading; error }\n\n---\n\n## EVENTS\n\nuseEvents(options?)\n Options: { limit?: number }\n Returns: FetchState<StorefrontEvent[]>\n StorefrontEvent: { id, name, description, date, endDate, location, imageUrl, ticketTypes: StorefrontTicketType[] }\n StorefrontTicketType: { id, name, priceKobo, quantity, sold, available }\n\nuseEvent(eventId: string)\n Returns: FetchState<StorefrontEvent | null>\n\nusePurchaseTickets()\n Returns: { initiate: (input: { eventId, tickets: [{ ticketTypeId, quantity }], customer: { name, email, phone } }) => Promise<CommerceActionResult>; loading; error }\n\nuseCreateRSVP()\n Returns: { submit: (input: { eventId, name, email, phone? }) => Promise<void>; loading; success; error }\n\n---\n\n## DONATIONS\n\nuseDonationCampaigns(options?)\n Options: { limit?: number; ids?: string[] }\n Returns: FetchState<StorefrontDonationCampaign[]>\n StorefrontDonationCampaign: { id, name, description, imageUrl, goalAmountKobo: number|null, raisedAmountKobo: number }\n WARNING: .raised, .raisedAmount, .goalAmount DO NOT EXIST. Use .raisedAmountKobo and .goalAmountKobo only.\n\nuseDonationCampaign(campaignId: string)\n Returns: FetchState<StorefrontDonationCampaign | null>\n\nuseDonate()\n Returns: { initiate: (input: { campaignId, amountKobo, customer: { name, email, phone } }) => Promise<CommerceActionResult>; loading; error }\n\n---\n\n## MEMBERSHIPS\n\nuseMemberships()\n Returns: FetchState<StorefrontMembershipTier[]>\n StorefrontMembershipTier: { id, name, description, priceKobo, billingPeriod: \"monthly\"|\"yearly\"|\"one_time\", benefits: string[]|null, imageUrl: string|null, isFeatured }\n WARNING: .price, .amount, .features, .perks DO NOT EXIST. Use .priceKobo and .benefits only.\n WARNING: billingPeriod is \"yearly\" not \"annual\".\n\nuseSubscribeMembership()\n Returns: { initiate: (input: { tierId, customer: { name, email, phone } }) => Promise<CommerceActionResult>; loading; error }\n\n---\n\n## CONTENT & CMS\n\nuseContentItems(options: { type: string })\n Type values: \"image\", \"video\", \"testimonial\", \"faq\", \"stat\", \"feature\", \"team_member\", \"logo_cloud\", \"pricing\"\n Returns: FetchState<ContentItem[]>\n\nConvenience aliases (all return FetchState<ContentItem[]>):\n useContentImages(), useContentVideos(), useContentTestimonials(), useContentFaqs(),\n useContentStats(), useContentFeatures(), useContentTeam(), useContentLogoCloud(), useContentPricing()\n\nuseArticles(options?)\n Options: { limit?: number; category?: string; tag?: string }\n Returns: FetchState<StorefrontArticle[]>\n\nuseArticle(slug: string)\n Returns: FetchState<StorefrontArticle | null>\n\nuseContentForm(formId: string)\n Returns: { form: ContentFormSchema | null; loading; error }\n ContentFormSchema: { id, name, fields: ContentFormField[] }\n ContentFormField: { id, label, type, required, options?, placeholder? }\n\nuseSubmitForm(formId: string)\n Returns: { submit: (data: Record<string, unknown>) => Promise<void>; submitting; submitted; error }\n\nuseMailingListSubscribe()\n Returns: { subscribe: (email: string) => Promise<void>; subscribing; subscribed; error }\n\n---\n\n## LISTINGS\n\nuseListings(options?)\n Options: { listingTypeId?: string; page?: number; limit?: number; filters?: Record<string, string> }\n Returns: { listings: StorefrontListing[]; total; page; totalPages; loading; error }\n\nuseListingDetail(slugOrId: string)\n Returns: { listing: StorefrontListingDetail | null; related: StorefrontListing[]; loading; error }\n\nuseListingFilters(listingTypeId?: string)\n Returns: { fieldDefinitions; params; setParam; reset; loading; error }\n\nAction hooks (each takes listingId):\n useListingEnquiry(id) \u2014 { submit: ({ name, email?, phone?, message? }) => Promise<void>; loading; success; error }\n useListingApplication(id) \u2014 { submit: ({ name, email, phone?, coverNote?, resumeUrl? }) => Promise<void>; loading; success; error }\n useListingPayment(id) \u2014 { checkout: (customer, fieldData?) => Promise<void>; loading; error }\n useListingReserve(id) \u2014 { reserve: (data?) => Promise<void>; loading; success; alreadyReserved; error }\n useListingDownload(id) \u2014 { download: () => Promise<void>; downloadUrl; loading; error }\n\n---\n\n## CUSTOMER ACCOUNTS\n\nuseCustomerAuth()\n Returns: { customer: { id, name, email, phone, address } | null; isLoggedIn: boolean; isLoading: boolean;\n signUp: (name, email, password) => Promise<void>; signIn: (email, password) => Promise<void>; signOut: () => void; error }\n WARNING: field is isLoggedIn, NOT isAuthenticated.\n\nuseMyOrders() \u2014 { orders: MyOrder[]; loading; error }\nuseMyAppointments() \u2014 { appointments: MyAppointment[]; loading; error }\nuseMyPurchases() \u2014 { purchases: MyPurchase[]; loading; error }\nuseMyMembership() \u2014 { membership: MyMembership | null; loading; error }\nuseMyProfile() \u2014 { profile: { name, email, phone, address } | null; update: ({ name, phone, address }) => Promise<void>; updating; updateError }\n\n---\n\n## ORDER TRACKING\n\nuseOrderStatus() \u2014 { status: OrderStatusData | null; loading; error }\nuseOrderLookup() \u2014 { lookup: (orderRef, email) => Promise<OrderLookupItem[]>; items; loading; error }\n\n---\n\n## COMPONENTS (import from \"@sellhapi/studio-runtime\")\n\nCartDrawer \u2014 renders the slide-out cart drawer. REQUIRED if using useCart().\n";
|
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Runtime hooks reference — extracted from @sellhapi/studio-runtime.
|
|
4
|
+
* This resource teaches AI clients the exact hook signatures and return shapes.
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.RUNTIME_HOOKS = void 0;
|
|
8
|
+
exports.RUNTIME_HOOKS = `
|
|
9
|
+
# @sellhapi/studio-runtime — Hook Reference
|
|
10
|
+
|
|
11
|
+
All imports come from "@sellhapi/studio-runtime". No other network/data access is allowed.
|
|
12
|
+
|
|
13
|
+
## IMPORTANT RULES
|
|
14
|
+
- All prices are in kobo (divide by 100 for Naira display): product.priceKobo / 100
|
|
15
|
+
- product.images is string[] — each element IS the URL. NEVER access .url on it.
|
|
16
|
+
- Always include type: 'physical' | 'digital' when calling addItem()
|
|
17
|
+
- If you use useCart/addItem, you MUST render <CartDrawer /> in App.tsx
|
|
18
|
+
- FetchState<T> = { data: T; loading: boolean; error: string | null }
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## ROUTING (required for multi-page storefronts)
|
|
23
|
+
|
|
24
|
+
useHashRouter()
|
|
25
|
+
Returns: { route: string; params: Record<string, string>; navigate: (route: string) => void }
|
|
26
|
+
Usage: const { route, navigate } = useHashRouter();
|
|
27
|
+
Routes: "#/" (home), "#/product/:id", "#/digital/:id", "#/services/:id", etc.
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## PHYSICAL PRODUCTS
|
|
32
|
+
|
|
33
|
+
useProducts(options?)
|
|
34
|
+
Options: { limit?: number; categoryId?: string; collectionId?: string; sectionId?: string; sort?: "newest"|"price_asc"|"price_desc"; search?: string; ids?: string[] }
|
|
35
|
+
Returns: FetchState<StoreProduct[]>
|
|
36
|
+
StoreProduct: { id, name, priceKobo, compareAtPriceKobo, imageUrl, images: string[], category, type: "physical"|"digital", variants: StoreProductVariant[], inStock }
|
|
37
|
+
|
|
38
|
+
useProduct(productId: string)
|
|
39
|
+
Returns: FetchState<StoreProductDetail | null>
|
|
40
|
+
StoreProductDetail: extends StoreProduct + { description, variants: StoreProductVariant[] }
|
|
41
|
+
StoreProductVariant: { id, size, color, priceKobo, compareAtPriceKobo, stock, imageUrl }
|
|
42
|
+
|
|
43
|
+
useCategories()
|
|
44
|
+
Returns: FetchState<StoreCategory[]>
|
|
45
|
+
StoreCategory: { name: string; count: number }
|
|
46
|
+
|
|
47
|
+
useProductCollections()
|
|
48
|
+
Returns: FetchState<ProductCollection[]>
|
|
49
|
+
ProductCollection: { id, name, coverImage: string | null }
|
|
50
|
+
|
|
51
|
+
useSections()
|
|
52
|
+
Returns: FetchState<StoreSection[]>
|
|
53
|
+
useSection(sectionId: string)
|
|
54
|
+
Returns: FetchState<StoreProduct[]>
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
## CART & CHECKOUT
|
|
59
|
+
|
|
60
|
+
useCart()
|
|
61
|
+
Returns: {
|
|
62
|
+
items: CartItem[]; count: number; totalKobo: number; savingsKobo: number; isEmpty: boolean;
|
|
63
|
+
addItem: (item: { productId, name, priceKobo, imageUrl?, variantId?, variantLabel?, quantity?, type: "physical"|"digital" }) => void;
|
|
64
|
+
removeItem: (productId, variantId?) => void;
|
|
65
|
+
updateQty: (productId, quantity, variantId?) => void;
|
|
66
|
+
clear: () => void;
|
|
67
|
+
openCart: () => void; // MUST call after addItem — opens the cart drawer
|
|
68
|
+
openCheckout: () => void;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
CartDrawer — REQUIRED component if using cart. Import from "@sellhapi/studio-runtime".
|
|
72
|
+
Usage: <CartDrawer /> in App.tsx root. Without it, openCart() does nothing.
|
|
73
|
+
|
|
74
|
+
useCartCheckout()
|
|
75
|
+
Returns: { initiate: (customer: { name, email, phone, address? }, options?: { deliveryMethod?, shippingRateId?, discountCode?, note? }) => Promise<CommerceActionResult>; loading; error }
|
|
76
|
+
CommerceActionResult: { paymentLink: string } — redirect to paymentLink
|
|
77
|
+
|
|
78
|
+
useProductCheckout()
|
|
79
|
+
Returns: { initiate: (input: { productId, quantity, variantId?, customer: { name, email, phone, address? }, deliveryMethod? }) => Promise<CommerceActionResult>; loading; error }
|
|
80
|
+
Use for single-product "Buy Now" flows only. For multi-product stores, use cart.
|
|
81
|
+
|
|
82
|
+
useShippingRates()
|
|
83
|
+
Returns: FetchState<ShippingRate[]>
|
|
84
|
+
ShippingRate: { id, name, amountKobo, estimatedDays }
|
|
85
|
+
|
|
86
|
+
useValidateDiscount()
|
|
87
|
+
Returns: { validate: (code: string) => Promise<DiscountValidationResult>; validating; error }
|
|
88
|
+
|
|
89
|
+
useCheckoutConfig()
|
|
90
|
+
Returns: FetchState<CheckoutConfig>
|
|
91
|
+
CheckoutConfig: { deliveryEnabled, pickupEnabled, deliveryOnly, pickupOnly }
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
## DIGITAL PRODUCTS
|
|
96
|
+
|
|
97
|
+
useDigitalProducts(options?)
|
|
98
|
+
Options: { limit?: number }
|
|
99
|
+
Returns: FetchState<StoreProduct[]> (filtered to type="digital")
|
|
100
|
+
|
|
101
|
+
useDigitalProduct(productId: string)
|
|
102
|
+
Returns: FetchState<StoreProductDetail | null>
|
|
103
|
+
|
|
104
|
+
---
|
|
105
|
+
|
|
106
|
+
## SERVICES
|
|
107
|
+
|
|
108
|
+
useServiceListings(options?)
|
|
109
|
+
Options: { limit?: number; category?: string }
|
|
110
|
+
Returns: FetchState<StorefrontServiceListing[]>
|
|
111
|
+
StorefrontServiceListing: { id, title, description, coverImage, category, priceKobo, availability, addOns: ServiceAddOn[], subServices: SubService[] }
|
|
112
|
+
|
|
113
|
+
useServiceListing(listingId: string)
|
|
114
|
+
Returns: FetchState<StorefrontServiceListing | null>
|
|
115
|
+
|
|
116
|
+
useCheckoutService()
|
|
117
|
+
Returns: { checkout: (input: { listingId, customer: { name, email, phone }, addOnIds?, subServiceIds? }) => Promise<{ paymentLink }>; loading; error }
|
|
118
|
+
|
|
119
|
+
useInitiateEngagement()
|
|
120
|
+
Returns: { submit: (input: { listingId, customer: { name, email, phone }, message? }) => Promise<void>; submitting; submitted; error }
|
|
121
|
+
|
|
122
|
+
---
|
|
123
|
+
|
|
124
|
+
## APPOINTMENTS
|
|
125
|
+
|
|
126
|
+
useServices(options?)
|
|
127
|
+
Options: { limit?: number }
|
|
128
|
+
Returns: FetchState<StorefrontService[]>
|
|
129
|
+
StorefrontService: { id, name, description, durationMinutes, priceKobo, imageUrl, category }
|
|
130
|
+
|
|
131
|
+
useAvailableDates(options)
|
|
132
|
+
Options: { serviceId: string; month: string } (month format: "YYYY-MM")
|
|
133
|
+
Returns: FetchState<string[]> — array of date strings "YYYY-MM-DD"
|
|
134
|
+
|
|
135
|
+
useAvailability(options)
|
|
136
|
+
Options: { serviceId: string; date: string } (date format: "YYYY-MM-DD")
|
|
137
|
+
Returns: FetchState<AvailabilitySlot[]>
|
|
138
|
+
AvailabilitySlot: { id, startTime, endTime, status }
|
|
139
|
+
|
|
140
|
+
useBookAppointment()
|
|
141
|
+
Returns: { initiate: (input: { serviceId, slotId, customer: { name, email, phone } }) => Promise<CommerceActionResult>; loading; error }
|
|
142
|
+
|
|
143
|
+
---
|
|
144
|
+
|
|
145
|
+
## EVENTS
|
|
146
|
+
|
|
147
|
+
useEvents(options?)
|
|
148
|
+
Options: { limit?: number }
|
|
149
|
+
Returns: FetchState<StorefrontEvent[]>
|
|
150
|
+
StorefrontEvent: { id, name, description, date, endDate, location, imageUrl, ticketTypes: StorefrontTicketType[] }
|
|
151
|
+
StorefrontTicketType: { id, name, priceKobo, quantity, sold, available }
|
|
152
|
+
|
|
153
|
+
useEvent(eventId: string)
|
|
154
|
+
Returns: FetchState<StorefrontEvent | null>
|
|
155
|
+
|
|
156
|
+
usePurchaseTickets()
|
|
157
|
+
Returns: { initiate: (input: { eventId, tickets: [{ ticketTypeId, quantity }], customer: { name, email, phone } }) => Promise<CommerceActionResult>; loading; error }
|
|
158
|
+
|
|
159
|
+
useCreateRSVP()
|
|
160
|
+
Returns: { submit: (input: { eventId, name, email, phone? }) => Promise<void>; loading; success; error }
|
|
161
|
+
|
|
162
|
+
---
|
|
163
|
+
|
|
164
|
+
## DONATIONS
|
|
165
|
+
|
|
166
|
+
useDonationCampaigns(options?)
|
|
167
|
+
Options: { limit?: number; ids?: string[] }
|
|
168
|
+
Returns: FetchState<StorefrontDonationCampaign[]>
|
|
169
|
+
StorefrontDonationCampaign: { id, name, description, imageUrl, goalAmountKobo: number|null, raisedAmountKobo: number }
|
|
170
|
+
WARNING: .raised, .raisedAmount, .goalAmount DO NOT EXIST. Use .raisedAmountKobo and .goalAmountKobo only.
|
|
171
|
+
|
|
172
|
+
useDonationCampaign(campaignId: string)
|
|
173
|
+
Returns: FetchState<StorefrontDonationCampaign | null>
|
|
174
|
+
|
|
175
|
+
useDonate()
|
|
176
|
+
Returns: { initiate: (input: { campaignId, amountKobo, customer: { name, email, phone } }) => Promise<CommerceActionResult>; loading; error }
|
|
177
|
+
|
|
178
|
+
---
|
|
179
|
+
|
|
180
|
+
## MEMBERSHIPS
|
|
181
|
+
|
|
182
|
+
useMemberships()
|
|
183
|
+
Returns: FetchState<StorefrontMembershipTier[]>
|
|
184
|
+
StorefrontMembershipTier: { id, name, description, priceKobo, billingPeriod: "monthly"|"yearly"|"one_time", benefits: string[]|null, imageUrl: string|null, isFeatured }
|
|
185
|
+
WARNING: .price, .amount, .features, .perks DO NOT EXIST. Use .priceKobo and .benefits only.
|
|
186
|
+
WARNING: billingPeriod is "yearly" not "annual".
|
|
187
|
+
|
|
188
|
+
useSubscribeMembership()
|
|
189
|
+
Returns: { initiate: (input: { tierId, customer: { name, email, phone } }) => Promise<CommerceActionResult>; loading; error }
|
|
190
|
+
|
|
191
|
+
---
|
|
192
|
+
|
|
193
|
+
## CONTENT & CMS
|
|
194
|
+
|
|
195
|
+
useContentItems(options: { type: string })
|
|
196
|
+
Type values: "image", "video", "testimonial", "faq", "stat", "feature", "team_member", "logo_cloud", "pricing"
|
|
197
|
+
Returns: FetchState<ContentItem[]>
|
|
198
|
+
|
|
199
|
+
Convenience aliases (all return FetchState<ContentItem[]>):
|
|
200
|
+
useContentImages(), useContentVideos(), useContentTestimonials(), useContentFaqs(),
|
|
201
|
+
useContentStats(), useContentFeatures(), useContentTeam(), useContentLogoCloud(), useContentPricing()
|
|
202
|
+
|
|
203
|
+
useArticles(options?)
|
|
204
|
+
Options: { limit?: number; category?: string; tag?: string }
|
|
205
|
+
Returns: FetchState<StorefrontArticle[]>
|
|
206
|
+
|
|
207
|
+
useArticle(slug: string)
|
|
208
|
+
Returns: FetchState<StorefrontArticle | null>
|
|
209
|
+
|
|
210
|
+
useContentForm(formId: string)
|
|
211
|
+
Returns: { form: ContentFormSchema | null; loading; error }
|
|
212
|
+
ContentFormSchema: { id, name, fields: ContentFormField[] }
|
|
213
|
+
ContentFormField: { id, label, type, required, options?, placeholder? }
|
|
214
|
+
|
|
215
|
+
useSubmitForm(formId: string)
|
|
216
|
+
Returns: { submit: (data: Record<string, unknown>) => Promise<void>; submitting; submitted; error }
|
|
217
|
+
|
|
218
|
+
useMailingListSubscribe()
|
|
219
|
+
Returns: { subscribe: (email: string) => Promise<void>; subscribing; subscribed; error }
|
|
220
|
+
|
|
221
|
+
---
|
|
222
|
+
|
|
223
|
+
## LISTINGS
|
|
224
|
+
|
|
225
|
+
useListings(options?)
|
|
226
|
+
Options: { listingTypeId?: string; page?: number; limit?: number; filters?: Record<string, string> }
|
|
227
|
+
Returns: { listings: StorefrontListing[]; total; page; totalPages; loading; error }
|
|
228
|
+
|
|
229
|
+
useListingDetail(slugOrId: string)
|
|
230
|
+
Returns: { listing: StorefrontListingDetail | null; related: StorefrontListing[]; loading; error }
|
|
231
|
+
|
|
232
|
+
useListingFilters(listingTypeId?: string)
|
|
233
|
+
Returns: { fieldDefinitions; params; setParam; reset; loading; error }
|
|
234
|
+
|
|
235
|
+
Action hooks (each takes listingId):
|
|
236
|
+
useListingEnquiry(id) — { submit: ({ name, email?, phone?, message? }) => Promise<void>; loading; success; error }
|
|
237
|
+
useListingApplication(id) — { submit: ({ name, email, phone?, coverNote?, resumeUrl? }) => Promise<void>; loading; success; error }
|
|
238
|
+
useListingPayment(id) — { checkout: (customer, fieldData?) => Promise<void>; loading; error }
|
|
239
|
+
useListingReserve(id) — { reserve: (data?) => Promise<void>; loading; success; alreadyReserved; error }
|
|
240
|
+
useListingDownload(id) — { download: () => Promise<void>; downloadUrl; loading; error }
|
|
241
|
+
|
|
242
|
+
---
|
|
243
|
+
|
|
244
|
+
## CUSTOMER ACCOUNTS
|
|
245
|
+
|
|
246
|
+
useCustomerAuth()
|
|
247
|
+
Returns: { customer: { id, name, email, phone, address } | null; isLoggedIn: boolean; isLoading: boolean;
|
|
248
|
+
signUp: (name, email, password) => Promise<void>; signIn: (email, password) => Promise<void>; signOut: () => void; error }
|
|
249
|
+
WARNING: field is isLoggedIn, NOT isAuthenticated.
|
|
250
|
+
|
|
251
|
+
useMyOrders() — { orders: MyOrder[]; loading; error }
|
|
252
|
+
useMyAppointments() — { appointments: MyAppointment[]; loading; error }
|
|
253
|
+
useMyPurchases() — { purchases: MyPurchase[]; loading; error }
|
|
254
|
+
useMyMembership() — { membership: MyMembership | null; loading; error }
|
|
255
|
+
useMyProfile() — { profile: { name, email, phone, address } | null; update: ({ name, phone, address }) => Promise<void>; updating; updateError }
|
|
256
|
+
|
|
257
|
+
---
|
|
258
|
+
|
|
259
|
+
## ORDER TRACKING
|
|
260
|
+
|
|
261
|
+
useOrderStatus() — { status: OrderStatusData | null; loading; error }
|
|
262
|
+
useOrderLookup() — { lookup: (orderRef, email) => Promise<OrderLookupItem[]>; items; loading; error }
|
|
263
|
+
|
|
264
|
+
---
|
|
265
|
+
|
|
266
|
+
## COMPONENTS (import from "@sellhapi/studio-runtime")
|
|
267
|
+
|
|
268
|
+
CartDrawer — renders the slide-out cart drawer. REQUIRED if using useCart().
|
|
269
|
+
`;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wireframe schema resource.
|
|
3
|
+
*
|
|
4
|
+
* Documents the exact shape and conventions for wireframe HTML submitted via
|
|
5
|
+
* apply_wireframe. The AI client generates mid-fidelity wireframes per page,
|
|
6
|
+
* presents each one to the user for review, then submits the approved set.
|
|
7
|
+
*/
|
|
8
|
+
export declare const WIREFRAME_SCHEMA = "\n# SellHapi Wireframe Schema\n\nWireframes are mid-fidelity HTML page layouts \u2014 one per storefront page.\nThey are the USER'S checkpoint before code is written: present each page\nwireframe in the conversation, collect feedback, revise if needed, then\ncall apply_wireframe only after the user approves the full set.\n\n---\n\n## Data shape\n\n```\n{ [pageSlug: string]: htmlString }\n```\n\n- Each key is a page slug that MUST match one of the slugs in designDirection.pages\n- Each value is a valid HTML string for that page's mid-fidelity wireframe\n- All pages from designDirection.pages should be included (missing pages means no wireframe guidance for code generation on that page)\n\n---\n\n## What a wireframe IS\n\nMid-fidelity structural HTML. It communicates:\n- Which sections appear on the page and in what order\n- What content each section contains (headings, CTAs, placeholder data)\n- Navigation structure (links, cart icon, CTA button)\n- Footer structure (columns, links)\n- Which runtime hook each section will use\n\nIt does NOT include:\n- Real CSS (use only inline style for basic layout structure if needed)\n- JavaScript or React\n- Actual product/service data (use descriptive placeholders)\n- Final colour values or fonts (those come from designDirection.visualDirection)\n\n---\n\n## Structure rules\n\n### Navigation \u2014 MANDATORY on every page\n```html\n<nav>\n <div class=\"nav-logo\">[Business Name]</div>\n <ul class=\"nav-links\">\n <li><a href=\"#/home\">Home</a></li>\n <li><a href=\"#/shop\">Shop</a></li>\n <!-- one <li> per page in designDirection.pages -->\n </ul>\n <!-- If physical_products or digital_products in capabilityIds: -->\n <button class=\"nav-cart\">Cart (0)</button>\n <button class=\"nav-cta\">[Primary CTA]</button>\n</nav>\n```\n\n- Navigation HTML must be **identical across all pages** \u2014 copy verbatim after the first page\n- Highlight the current page's link as active (add class=\"active\" to that <li>)\n- Cart icon only when capability includes physical_products or digital_products\n\n### Footer \u2014 MANDATORY on every page\n```html\n<footer>\n <div class=\"footer-col\">\n <div class=\"footer-logo\">[Business Name]</div>\n <p>[tagline]</p>\n </div>\n <div class=\"footer-col\">\n <h4>NAVIGATE</h4>\n <ul><!-- one <li> per page --></ul>\n </div>\n <div class=\"footer-col\">\n <h4>RESOURCES</h4>\n <ul><!-- relevant links for this business type --></ul>\n </div>\n <div class=\"footer-col\">\n <h4>CONNECT</h4>\n <!-- social platform links -->\n </div>\n <div class=\"footer-bottom\">\u00A9 [year] [Business Name]. All rights reserved.</div>\n</footer>\n```\n\n- Footer HTML must be **identical across all pages** \u2014 copy verbatim after the first page\n\n### Sections\nEach section gets a wrapper with a comment and data-hook attribute:\n\n```html\n<!-- Hero Section -->\n<section data-section=\"hero\" data-hook=\"none\">\n <h1>[Headline \u2014 use the business tagline]</h1>\n <p>[Supporting copy]</p>\n <button>[Primary CTA]</button>\n <div class=\"hero-image\">[Hero image placeholder]</div>\n</section>\n\n<!-- Product Grid Section -->\n<section data-section=\"product-grid\" data-hook=\"useProducts\">\n <h2>Our Products</h2>\n <div class=\"product-grid\">\n <div class=\"product-card\">\n <div class=\"product-image\">[Product image]</div>\n <h3>[Product name]</h3>\n <p>[Price in \u20A6]</p>\n <button>Add to Cart</button>\n </div>\n <!-- repeat 2 more placeholder cards -->\n </div>\n</section>\n\n<!-- Testimonials Section -->\n<section data-section=\"testimonials\" data-hook=\"useContentTestimonials\">\n <h2>What Customers Say</h2>\n <div class=\"testimonials-grid\">\n <blockquote>[Testimonial 1]</blockquote>\n <blockquote>[Testimonial 2]</blockquote>\n <blockquote>[Testimonial 3]</blockquote>\n </div>\n</section>\n```\n\n---\n\n## data-hook values by capability\n\nUse these exact hook names in data-hook attributes so code generation\nmatches the correct runtime hook:\n\n| Section type | data-hook |\n|-------------------------------|---------------------------------|\n| Product listing / grid | useProducts |\n| Product collections | useProductCollections |\n| Single collection | useCollection |\n| Category sections | useSections |\n| Service listings | useServiceListings |\n| Appointment booking | useBookAppointment |\n| Available dates | useAvailableDates |\n| Events listing | useEvents |\n| Donation campaigns | useDonationCampaigns |\n| Membership tiers | useMemberships |\n| Content images | useContentImages |\n| Testimonials | useContentTestimonials |\n| FAQs | useContentFaqs |\n| Team members | useContentTeam |\n| Stats / social proof | useContentStats |\n| Feature highlights | useContentFeatures |\n| Contact / enquiry form | useSubmitForm |\n| Listings (classifieds/jobs) | useListings |\n| Non-data sections (hero, about, footer, nav) | none |\n\n---\n\n## Presenting wireframes to the user\n\nBefore calling apply_wireframe, show each page to the user like this:\n\n```\n\uD83D\uDCC4 HOME PAGE\n\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\n[NAV] Logo | Home \u00B7 Shop \u00B7 About \u00B7 Contact [Cart] [Shop Now]\n\n[HERO]\n \"Authentic Nigerian fashion for the bold\"\n Subheading + CTA button\n Full-width hero image\n\n[FEATURED PRODUCTS] (useProducts)\n 3-column product grid \u2014 6 products\n Each card: image, name, price, Add to Cart\n\n[COLLECTIONS] (useProductCollections)\n 4 collection tiles with cover images\n\n[TESTIMONIALS] (useContentTestimonials)\n 3 customer quotes in a row\n\n[NEWSLETTER]\n Email capture form\n\n[FOOTER] 4-column footer\n\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\n\nDoes this structure look right? Any sections to add, remove, or reorder?\n```\n\nWait for explicit user approval before proceeding to apply_wireframe.\nIf the user requests changes, update the affected page(s) and show again.\n\n---\n\n## Submission\n\nOnce ALL pages are approved by the user:\n\n```\napply_wireframe(projectId, {\n \"home\": \"<nav>...</nav><main>...</main><footer>...</footer>\",\n \"shop\": \"<nav>...</nav><main>...</main><footer>...</footer>\",\n \"about\": \"<nav>...</nav><main>...</main><footer>...</footer>\",\n \"contact\": \"<nav>...</nav><main>...</main><footer>...</footer>\"\n})\n```\n\nReturns: `{ pageCount: 4, slugs: [\"home\", \"shop\", \"about\", \"contact\"] }`\n\nAfter this, proceed to generate React code using the wireframe as your\nstructural reference. The wireframe defines WHAT to build \u2014 runtime-hooks\nand build-rules define HOW to build it in React.\n";
|