@thorprovider/create-storefront 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 +119 -0
- package/bin/install.js +116 -0
- package/commands/sf-add-view.md +21 -0
- package/commands/sf-init.md +16 -0
- package/commands/sf-theme.md +15 -0
- package/commands/sf-view.md +20 -0
- package/package.json +40 -0
- package/recipes/archetype.schema.json +39 -0
- package/recipes/archetypes.json +148 -0
- package/recipes/recipe.schema.json +59 -0
- package/recipes/recipes.json +90 -0
- package/recipes/sections.json +46 -0
- package/recipes/validate.mjs +190 -0
- package/skills/building-storefronts/SKILL.md +178 -0
- package/skills/building-storefronts/references/frontend-integration.md +229 -0
- package/skills/json-render-core/SKILL.md +291 -0
- package/skills/json-render-next/SKILL.md +194 -0
- package/skills/json-render-react/SKILL.md +298 -0
- package/skills/json-render-remotion/SKILL.md +111 -0
- package/skills/json-render-shadcn/SKILL.md +159 -0
- package/skills/json-render-solid/SKILL.md +204 -0
- package/skills/nextjs-shadcn/SKILL.md +303 -0
- package/skills/nextjs-shadcn/references/architecture.md +499 -0
- package/skills/nextjs-shadcn/references/project-setup.md +127 -0
- package/skills/nextjs-shadcn/references/shadcn-platform.md +258 -0
- package/skills/nextjs-shadcn/references/sidebar.md +274 -0
- package/skills/nextjs-shadcn/references/styling.md +555 -0
- package/skills/sf-scaffold/SKILL.md +118 -0
- package/skills/sf-theme-gen/SKILL.md +44 -0
- package/skills/sf-view-gen/SKILL.md +94 -0
- package/skills/shadcn-component-discovery/SKILL.md +273 -0
- package/skills/shadcn-component-discovery/references/registries.md +226 -0
- package/skills/shadcn-theming/SKILL.md +104 -0
- package/skills/shadcn-theming/references/templates/theme-setup.md +109 -0
- package/skills/shadcn-theming/references/theming-guide.md +90 -0
- package/skills/storefront-best-practices/SKILL.md +421 -0
- package/skills/storefront-best-practices/reference/components/breadcrumbs.md +123 -0
- package/skills/storefront-best-practices/reference/components/cart-popup.md +189 -0
- package/skills/storefront-best-practices/reference/components/country-selector.md +298 -0
- package/skills/storefront-best-practices/reference/components/footer.md +112 -0
- package/skills/storefront-best-practices/reference/components/hero.md +241 -0
- package/skills/storefront-best-practices/reference/components/megamenu.md +239 -0
- package/skills/storefront-best-practices/reference/components/navbar.md +397 -0
- package/skills/storefront-best-practices/reference/components/popups.md +221 -0
- package/skills/storefront-best-practices/reference/components/product-card.md +125 -0
- package/skills/storefront-best-practices/reference/components/product-reviews.md +217 -0
- package/skills/storefront-best-practices/reference/components/product-slider.md +174 -0
- package/skills/storefront-best-practices/reference/components/search.md +101 -0
- package/skills/storefront-best-practices/reference/connecting-to-backend.md +391 -0
- package/skills/storefront-best-practices/reference/design.md +388 -0
- package/skills/storefront-best-practices/reference/features/promotions.md +307 -0
- package/skills/storefront-best-practices/reference/features/wishlist.md +230 -0
- package/skills/storefront-best-practices/reference/layouts/account.md +380 -0
- package/skills/storefront-best-practices/reference/layouts/cart.md +316 -0
- package/skills/storefront-best-practices/reference/layouts/checkout.md +486 -0
- package/skills/storefront-best-practices/reference/layouts/home-page.md +264 -0
- package/skills/storefront-best-practices/reference/layouts/order-confirmation.md +231 -0
- package/skills/storefront-best-practices/reference/layouts/product-details.md +527 -0
- package/skills/storefront-best-practices/reference/layouts/product-listing.md +520 -0
- package/skills/storefront-best-practices/reference/layouts/static-pages.md +356 -0
- package/skills/storefront-best-practices/reference/medusa.md +307 -0
- package/skills/storefront-best-practices/reference/mobile-responsiveness.md +183 -0
- package/skills/storefront-best-practices/reference/seo.md +195 -0
- package/templates/app/app/[[...slug]]/page.tsx +17 -0
- package/templates/app/app/[[...slug]]/renderer.tsx +10 -0
- package/templates/app/app/globals.css +101 -0
- package/templates/app/app/layout.tsx +35 -0
- package/templates/app/lib/__STOREFRONT__/catalog.ts +132 -0
- package/templates/app/lib/__STOREFRONT__/handlers.ts +33 -0
- package/templates/app/lib/__STOREFRONT__/registry.tsx +134 -0
- package/templates/app/lib/__STOREFRONT__/runtime.ts +25 -0
- package/templates/app/lib/__STOREFRONT__/spec/home.ts +62 -0
- package/templates/app/lib/__STOREFRONT__/spec/index.ts +59 -0
- package/templates/app/lib/__STOREFRONT__/spec/types.ts +14 -0
- package/templates/app/lib/__STOREFRONT__/state.ts +35 -0
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
// Generated by Thor Commerce sf-scaffold (template: templates/app/lib/__STOREFRONT__/handlers.ts)
|
|
4
|
+
// Registers json-render action handlers. Recipe-added views extend this file.
|
|
5
|
+
|
|
6
|
+
import { useEffect } from 'react'
|
|
7
|
+
import { useActions, useStateStore } from '@json-render/react'
|
|
8
|
+
import { useNextApp } from '@json-render/next'
|
|
9
|
+
|
|
10
|
+
export function useStorefrontHandlers() {
|
|
11
|
+
const { set } = useStateStore()
|
|
12
|
+
const { navigate } = useNextApp()
|
|
13
|
+
const { registerHandler } = useActions()
|
|
14
|
+
|
|
15
|
+
useEffect(() => {
|
|
16
|
+
const handlers: Record<string, () => void> = {
|
|
17
|
+
addToCart: () => {},
|
|
18
|
+
openProduct: () => navigate('/productos'),
|
|
19
|
+
openCategory: () => {
|
|
20
|
+
const handle = ''
|
|
21
|
+
navigate(handle ? `/categorias/${handle}` : '/categorias')
|
|
22
|
+
},
|
|
23
|
+
goHome: () => navigate('/'),
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
for (const [name, handler] of Object.entries(handlers)) {
|
|
27
|
+
registerHandler(name, handler)
|
|
28
|
+
}
|
|
29
|
+
void set
|
|
30
|
+
// registerHandler uses functional state updates; store/navigate captured once.
|
|
31
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
32
|
+
}, [])
|
|
33
|
+
}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
// Generated by Thor Commerce sf-scaffold (template: templates/app/lib/__STOREFRONT__/registry.tsx)
|
|
4
|
+
// Maps json-render element types to @thorprovider/blocks components and wires actions.
|
|
5
|
+
// Recipe-added views extend this file.
|
|
6
|
+
|
|
7
|
+
import { defineRegistry, useStateStore } from '@json-render/react'
|
|
8
|
+
import {
|
|
9
|
+
Feature1,
|
|
10
|
+
Navbar1,
|
|
11
|
+
ProductCategory2,
|
|
12
|
+
ProductCard1,
|
|
13
|
+
StorefrontHero1,
|
|
14
|
+
} from '@thorprovider/blocks'
|
|
15
|
+
import { catalog } from './catalog'
|
|
16
|
+
import { useStorefrontHandlers } from './handlers'
|
|
17
|
+
|
|
18
|
+
export const { registry } = defineRegistry(catalog, {
|
|
19
|
+
components: {
|
|
20
|
+
Layout: ({ children }) => {
|
|
21
|
+
useStorefrontHandlers()
|
|
22
|
+
return <div className="flex min-h-screen flex-col">{children}</div>
|
|
23
|
+
},
|
|
24
|
+
Navbar: ({ props, emit }) => (
|
|
25
|
+
<Navbar1
|
|
26
|
+
navLinks={props.navLinks}
|
|
27
|
+
logoText={props.logoText}
|
|
28
|
+
logoHref={props.logoHref}
|
|
29
|
+
currentPath={props.currentPath}
|
|
30
|
+
signIn={props.signIn}
|
|
31
|
+
cta={props.cta}
|
|
32
|
+
cartCount={props.cartCount}
|
|
33
|
+
onOpenCart={() => emit('openCart')}
|
|
34
|
+
/>
|
|
35
|
+
),
|
|
36
|
+
StorefrontHero: ({ props, emit }) => {
|
|
37
|
+
const { set } = useStateStore()
|
|
38
|
+
return (
|
|
39
|
+
<StorefrontHero1
|
|
40
|
+
title={props.title}
|
|
41
|
+
description={props.description}
|
|
42
|
+
products={props.products}
|
|
43
|
+
titleAccent={props.titleAccent}
|
|
44
|
+
badge={props.badge}
|
|
45
|
+
primaryCta={props.primaryCta}
|
|
46
|
+
secondaryCta={props.secondaryCta}
|
|
47
|
+
onAddToCart={(product) => {
|
|
48
|
+
set('/lastClicked', product)
|
|
49
|
+
emit('addToCart')
|
|
50
|
+
}}
|
|
51
|
+
onBuyNow={(product) => {
|
|
52
|
+
set('/lastClicked', product)
|
|
53
|
+
emit('openProduct')
|
|
54
|
+
}}
|
|
55
|
+
onSelectColor={() => {}}
|
|
56
|
+
/>
|
|
57
|
+
)
|
|
58
|
+
},
|
|
59
|
+
Feature: ({ props }) => (
|
|
60
|
+
<Feature1
|
|
61
|
+
items={props.items}
|
|
62
|
+
badge={props.badge}
|
|
63
|
+
title={props.title}
|
|
64
|
+
description={props.description}
|
|
65
|
+
learnMoreLabel={props.learnMoreLabel}
|
|
66
|
+
ctaLabel={props.ctaLabel}
|
|
67
|
+
/>
|
|
68
|
+
),
|
|
69
|
+
CategoryCards: ({ props, emit }) => {
|
|
70
|
+
const { set } = useStateStore()
|
|
71
|
+
return (
|
|
72
|
+
<ProductCategory2
|
|
73
|
+
title={props.title}
|
|
74
|
+
description={props.description}
|
|
75
|
+
categories={props.categories}
|
|
76
|
+
cta={props.cta}
|
|
77
|
+
onBrowse={(category) => {
|
|
78
|
+
set('/lastCategory', category.handle ?? String(category.id))
|
|
79
|
+
emit('openCategory')
|
|
80
|
+
}}
|
|
81
|
+
/>
|
|
82
|
+
)
|
|
83
|
+
},
|
|
84
|
+
ProductCard: ({ props, emit }) => {
|
|
85
|
+
const { set } = useStateStore()
|
|
86
|
+
return (
|
|
87
|
+
<ProductCard1
|
|
88
|
+
products={props.products}
|
|
89
|
+
heading={props.heading}
|
|
90
|
+
maxItems={props.maxItems}
|
|
91
|
+
onAddToCart={(product) => {
|
|
92
|
+
set('/lastClicked', product)
|
|
93
|
+
emit('addToCart')
|
|
94
|
+
}}
|
|
95
|
+
onProductClick={(product) => {
|
|
96
|
+
set('/lastClicked', product)
|
|
97
|
+
emit('openProduct')
|
|
98
|
+
}}
|
|
99
|
+
/>
|
|
100
|
+
)
|
|
101
|
+
},
|
|
102
|
+
Footer: ({ props }) => (
|
|
103
|
+
<footer className="mt-auto border-t bg-background">
|
|
104
|
+
<div className="mx-auto w-full max-w-7xl px-4 py-12">
|
|
105
|
+
<p className="text-lg font-semibold">{props.brandName}</p>
|
|
106
|
+
{props.brandDescription ? (
|
|
107
|
+
<p className="mt-2 max-w-prose text-sm text-muted-foreground">
|
|
108
|
+
{props.brandDescription}
|
|
109
|
+
</p>
|
|
110
|
+
) : null}
|
|
111
|
+
<div className="mt-8 grid grid-cols-2 gap-6 sm:grid-cols-4">
|
|
112
|
+
{(props.linkColumns ?? []).map((column) => (
|
|
113
|
+
<div key={column.title}>
|
|
114
|
+
<p className="text-sm font-semibold">{column.title}</p>
|
|
115
|
+
<ul className="mt-3 space-y-2">
|
|
116
|
+
{(column.links ?? []).map((link) => (
|
|
117
|
+
<li key={link.label}>
|
|
118
|
+
<a
|
|
119
|
+
href={link.href ?? '#'}
|
|
120
|
+
className="text-sm text-muted-foreground hover:text-foreground"
|
|
121
|
+
>
|
|
122
|
+
{link.label}
|
|
123
|
+
</a>
|
|
124
|
+
</li>
|
|
125
|
+
))}
|
|
126
|
+
</ul>
|
|
127
|
+
</div>
|
|
128
|
+
))}
|
|
129
|
+
</div>
|
|
130
|
+
</div>
|
|
131
|
+
</footer>
|
|
132
|
+
),
|
|
133
|
+
},
|
|
134
|
+
})
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
// Generated by Thor Commerce sf-scaffold (template: templates/app/lib/__STOREFRONT__/runtime.ts)
|
|
2
|
+
// Wires the json-render app to the Medusa backend via @thorprovider/adapters
|
|
3
|
+
// (through @thorprovider/storefronts/commerce). Recipe-added views extend the loaders map.
|
|
4
|
+
|
|
5
|
+
import type { LoaderFn } from '@json-render/next'
|
|
6
|
+
import { createStorefrontApp } from '@thorprovider/storefronts/render-core/app'
|
|
7
|
+
import { getCatalog } from '@thorprovider/storefronts/commerce'
|
|
8
|
+
import { __STOREFRONT_CAMEL__Spec } from './spec'
|
|
9
|
+
|
|
10
|
+
const loaders: Record<string, LoaderFn> = {
|
|
11
|
+
home: async () => {
|
|
12
|
+
const { products, categories } = await getCatalog()
|
|
13
|
+
return {
|
|
14
|
+
heroProducts: products.slice(0, 4),
|
|
15
|
+
categories,
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export const __STOREFRONT_CAMEL__App = createStorefrontApp({
|
|
21
|
+
spec: __STOREFRONT_CAMEL__Spec,
|
|
22
|
+
loaders,
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
export type { LoaderFn }
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
// Generated by Thor Commerce sf-scaffold (template: templates/app/lib/__STOREFRONT__/spec/home.ts)
|
|
2
|
+
|
|
3
|
+
import type { StorefrontElement } from './types'
|
|
4
|
+
|
|
5
|
+
export const homeElements: Record<string, StorefrontElement> = {
|
|
6
|
+
'home.hero': {
|
|
7
|
+
type: 'StorefrontHero',
|
|
8
|
+
props: {
|
|
9
|
+
badge: 'Bienvenido',
|
|
10
|
+
title: '__SITE_NAME__',
|
|
11
|
+
titleAccent: 'para cada proyecto',
|
|
12
|
+
description:
|
|
13
|
+
'Productos seleccionados con precios justos y entrega a tu puerta.',
|
|
14
|
+
products: { $state: '/heroProducts' },
|
|
15
|
+
primaryCta: { label: 'Comprar ahora' },
|
|
16
|
+
secondaryCta: { label: 'Ver categorías' },
|
|
17
|
+
},
|
|
18
|
+
on: {
|
|
19
|
+
addToCart: { action: 'addToCart' },
|
|
20
|
+
openProduct: { action: 'openProduct' },
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
'home.benefits': {
|
|
24
|
+
type: 'Feature',
|
|
25
|
+
props: {
|
|
26
|
+
badge: '__SITE_NAME__',
|
|
27
|
+
title: 'Compra con confianza',
|
|
28
|
+
description: 'Beneficios pensados para que tu compra llegue segura y a tiempo.',
|
|
29
|
+
items: [
|
|
30
|
+
{
|
|
31
|
+
id: 'entrega',
|
|
32
|
+
title: 'Entrega segura',
|
|
33
|
+
description: 'Seguimiento de tu pedido hasta la puerta de tu casa.',
|
|
34
|
+
icon: 'truck',
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
id: 'pago',
|
|
38
|
+
title: 'Pago protegido',
|
|
39
|
+
description: 'Compra con confianza, múltiples opciones de pago.',
|
|
40
|
+
icon: 'shield-check',
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
id: 'soporte',
|
|
44
|
+
title: 'Soporte local',
|
|
45
|
+
description: 'Atención personalizada en todo el país.',
|
|
46
|
+
icon: 'headphones',
|
|
47
|
+
},
|
|
48
|
+
],
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
'home.categories': {
|
|
52
|
+
type: 'CategoryCards',
|
|
53
|
+
props: {
|
|
54
|
+
title: 'Compra por categoría',
|
|
55
|
+
description: 'Explora nuestro catálogo completo.',
|
|
56
|
+
categories: { $state: '/categories' },
|
|
57
|
+
},
|
|
58
|
+
on: {
|
|
59
|
+
openCategory: { action: 'openCategory' },
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
// Generated by Thor Commerce sf-scaffold (template: templates/app/lib/__STOREFRONT__/spec/index.ts)
|
|
2
|
+
// Assembles the json-render app spec. Recipe-added views extend `routes`.
|
|
3
|
+
|
|
4
|
+
import type { NextAppSpec } from '@json-render/next'
|
|
5
|
+
import { homeElements } from './home'
|
|
6
|
+
import type { StorefrontElement } from './types'
|
|
7
|
+
|
|
8
|
+
export type { StorefrontElement }
|
|
9
|
+
|
|
10
|
+
const pageRoot = (
|
|
11
|
+
id: string,
|
|
12
|
+
children: string[],
|
|
13
|
+
elements: Record<string, StorefrontElement>,
|
|
14
|
+
) => ({
|
|
15
|
+
page: {
|
|
16
|
+
root: id,
|
|
17
|
+
elements: {
|
|
18
|
+
[id]: { type: 'Layout', props: {}, children },
|
|
19
|
+
...elements,
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
export const __STOREFRONT_CAMEL__Spec: NextAppSpec = {
|
|
25
|
+
metadata: {
|
|
26
|
+
title: {
|
|
27
|
+
default: '__SITE_NAME__',
|
|
28
|
+
template: '%s · __SITE_NAME__',
|
|
29
|
+
},
|
|
30
|
+
description: 'Tienda online de __SITE_NAME__.',
|
|
31
|
+
},
|
|
32
|
+
state: {},
|
|
33
|
+
layouts: {
|
|
34
|
+
default: {
|
|
35
|
+
layout: {
|
|
36
|
+
root: 'layout',
|
|
37
|
+
elements: {
|
|
38
|
+
layout: {
|
|
39
|
+
type: 'Layout',
|
|
40
|
+
props: {},
|
|
41
|
+
slots: { header: [], footer: [] },
|
|
42
|
+
children: [],
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
routes: {
|
|
49
|
+
'/': {
|
|
50
|
+
layout: 'default',
|
|
51
|
+
loader: 'home',
|
|
52
|
+
metadata: {
|
|
53
|
+
title: 'Inicio',
|
|
54
|
+
description: 'Descubre los productos de __SITE_NAME__.',
|
|
55
|
+
},
|
|
56
|
+
...pageRoot('home', ['home.hero', 'home.benefits', 'home.categories'], homeElements),
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// Generated by Thor Commerce sf-scaffold (template: templates/app/lib/__STOREFRONT__/spec/types.ts)
|
|
2
|
+
// Adapted from the Thor Commerce storefront reference (read-only).
|
|
3
|
+
|
|
4
|
+
import type { VisibilityCondition } from '@json-render/core'
|
|
5
|
+
|
|
6
|
+
export interface StorefrontElement {
|
|
7
|
+
type: string
|
|
8
|
+
props: Record<string, unknown>
|
|
9
|
+
children?: string[]
|
|
10
|
+
slots?: Record<string, string[]>
|
|
11
|
+
visible?: VisibilityCondition
|
|
12
|
+
repeat?: { statePath: string; key?: string }
|
|
13
|
+
on?: Record<string, { action: string }>
|
|
14
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// Generated by Thor Commerce sf-scaffold (template: templates/app/lib/__STOREFRONT__/state.ts)
|
|
2
|
+
// Initial storefront state. Extended by recipes as views are added.
|
|
3
|
+
|
|
4
|
+
import type { CardProduct, ProductCategory2Category } from '@thorprovider/blocks'
|
|
5
|
+
|
|
6
|
+
export interface StoreOverviewProduct {
|
|
7
|
+
id: string
|
|
8
|
+
brand: string
|
|
9
|
+
name: string
|
|
10
|
+
description: string
|
|
11
|
+
price: number
|
|
12
|
+
currency: string
|
|
13
|
+
rating: number
|
|
14
|
+
images: { id: number; src: string; alt: string }[]
|
|
15
|
+
sizes: number[]
|
|
16
|
+
colors: { name: string; value: string }[]
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface StorefrontState {
|
|
20
|
+
heroProducts: CardProduct[]
|
|
21
|
+
categories: ProductCategory2Category[]
|
|
22
|
+
selectedProduct: StoreOverviewProduct | null
|
|
23
|
+
lastClicked: CardProduct | null
|
|
24
|
+
lastCategory: string | null
|
|
25
|
+
bannerDismissed: boolean
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export const storefrontInitialState: StorefrontState = {
|
|
29
|
+
heroProducts: [],
|
|
30
|
+
categories: [],
|
|
31
|
+
selectedProduct: null,
|
|
32
|
+
lastClicked: null,
|
|
33
|
+
lastCategory: null,
|
|
34
|
+
bannerDismissed: false,
|
|
35
|
+
}
|