diff --git a/.codex/config.toml b/.codex/config.toml new file mode 100644 index 0000000..c46619e --- /dev/null +++ b/.codex/config.toml @@ -0,0 +1,13 @@ +[mcp_servers.homaroma_mcp] +command = "/home/ti/.bun/bin/bun" +args = ["index.ts"] +cwd = "/home/ti/git/homaroma/apps/mcp-server" +startup_timeout_sec = 20 +tool_timeout_sec = 60 +enabled = true +required = false +default_tools_approval_mode = "prompt" + +[mcp_servers.homaroma_mcp.env] +MCP_BACKEND_URL_LOCAL = "http://localhost:3051/api" +MCP_BACKEND_URL_PRODUCTION = "https://api.homaroma.shop/api" diff --git a/apps/admin/src/components/page-builder/HeroArticleForm.tsx b/apps/admin/src/components/page-builder/HeroArticleForm.tsx new file mode 100644 index 0000000..ecd1647 --- /dev/null +++ b/apps/admin/src/components/page-builder/HeroArticleForm.tsx @@ -0,0 +1,145 @@ +import React from 'react'; +import { UploadCloud, Image as ImageIcon } from 'lucide-react'; +import { type PageSection } from '@homaroma/types'; + +interface HeroArticleFormProps { + editingSection: Partial; + setEditingSection: React.Dispatch | null>>; + editingLang: string; + getLocalizedValue: (payload: any, field: string) => string; + updateLocalizedValue: (field: string, value: string) => void; + setUploadTarget: (target: { type: 'main' | 'slide'; index?: number } | null) => void; + setIsMediaModalOpen: (open: boolean) => void; +} + +export default function HeroArticleForm({ + editingSection, + setEditingSection, + getLocalizedValue, + updateLocalizedValue, + setUploadTarget, + setIsMediaModalOpen, +}: HeroArticleFormProps) { + return ( +
+
+ +
{ + setUploadTarget({ type: 'main' }); + setIsMediaModalOpen(true); + }} + > + {editingSection.payload?.imageUrl ? ( + <> + Hero Article +
+ +
+ + ) : ( +
+ +

Drop in an image URL first

+
+ )} +
+
+ +
+ + updateLocalizedValue('title', e.target.value)} + className="admin-input w-full" + placeholder="Editorial title" + /> +
+ +
+ + updateLocalizedValue('subtitle', e.target.value)} + className="admin-input w-full" + placeholder="Short supporting line" + /> +
+ +
+ + updateLocalizedValue('author', e.target.value)} + className="admin-input w-full" + placeholder="Author name" + /> +
+ +
+ + updateLocalizedValue('category', e.target.value)} + className="admin-input w-full" + placeholder="Story" + /> +
+ +
+ + + setEditingSection({ + ...editingSection, + payload: { ...(editingSection.payload || {}), publishDate: e.target.value } + }) + } + className="admin-input w-full" + placeholder="2026-01-01" + /> +
+ +
+ + + setEditingSection({ + ...editingSection, + payload: { ...(editingSection.payload || {}), overlayColor: e.target.value } + }) + } + className="admin-input w-full" + placeholder="#00000080" + /> +
+ +
+ + + setEditingSection({ + ...editingSection, + payload: { ...(editingSection.payload || {}), imageUrl: e.target.value } + }) + } + className="admin-input w-full" + placeholder="/images/hero.png or select from media library" + /> +
+
+ ); +} diff --git a/apps/admin/src/components/page-builder/ProductCarouselForm.tsx b/apps/admin/src/components/page-builder/ProductCarouselForm.tsx new file mode 100644 index 0000000..5cfe9f3 --- /dev/null +++ b/apps/admin/src/components/page-builder/ProductCarouselForm.tsx @@ -0,0 +1,110 @@ +import React from 'react'; +import { type PageSection } from '@homaroma/types'; + +interface ProductCarouselFormProps { + editingSection: Partial; + editingLang: string; + setEditingSection: React.Dispatch | null>>; + getLocalizedValue: (payload: any, field: string) => string; + updateLocalizedValue: (field: string, value: string) => void; +} + +export default function ProductCarouselForm({ + editingSection, + setEditingSection, + getLocalizedValue, + updateLocalizedValue, +}: ProductCarouselFormProps) { + const updateFlag = (field: 'showPrice' | 'showAddToCart', value: boolean) => { + setEditingSection({ + ...editingSection, + payload: { ...(editingSection.payload || {}), [field]: value } + }); + }; + + return ( +
+
+
+ + updateLocalizedValue('title', e.target.value)} + className="admin-input w-full" + placeholder="Carousel heading" + /> +
+ +
+ + updateLocalizedValue('description', e.target.value)} + className="admin-input w-full" + placeholder="Optional supporting line" + /> +
+ +
+ + + setEditingSection({ + ...editingSection, + payload: { ...(editingSection.payload || {}), collectionId: e.target.value } + }) + } + className="admin-input w-full" + placeholder="Optional collection identifier" + /> +
+ +
+ + + setEditingSection({ + ...editingSection, + payload: { ...(editingSection.payload || {}), limit: parseInt(e.target.value, 10) } + }) + } + className="admin-input w-full" + /> +
+
+ +
+ + + +
+ +

+ If your backend supports collection lookup by ID, place that value here. Otherwise leave it blank and use the default product list behavior. +

+
+ ); +} diff --git a/apps/admin/src/components/page-builder/QuoteCalloutForm.tsx b/apps/admin/src/components/page-builder/QuoteCalloutForm.tsx new file mode 100644 index 0000000..eb7f4c5 --- /dev/null +++ b/apps/admin/src/components/page-builder/QuoteCalloutForm.tsx @@ -0,0 +1,52 @@ +import { type PageSection } from '@homaroma/types'; + +interface QuoteCalloutFormProps { + editingSection: Partial; + editingLang: string; + getLocalizedValue: (payload: any, field: string) => string; + updateLocalizedValue: (field: string, value: string) => void; +} + +export default function QuoteCalloutForm({ + editingSection, + getLocalizedValue, + updateLocalizedValue, +}: QuoteCalloutFormProps) { + return ( +
+
+ +