@withpotter/components 0.13.0-next.84 → 0.13.0-next.86

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.
Files changed (44) hide show
  1. package/dist/billing/overview-tab.js +4 -3
  2. package/dist/billing/overview-tab.js.map +1 -1
  3. package/dist/billing/plans-tab.js +6 -12
  4. package/dist/billing/plans-tab.js.map +1 -1
  5. package/dist/dashboard/activity-feed.d.ts +2 -0
  6. package/dist/dashboard/activity-feed.js +3 -1
  7. package/dist/dashboard/activity-feed.js.map +1 -1
  8. package/dist/dashboard/activity-paid.d.ts +10 -0
  9. package/dist/dashboard/activity-paid.js +7 -0
  10. package/dist/dashboard/activity-paid.js.map +1 -0
  11. package/dist/dashboard/home/ask-controls.js +1 -0
  12. package/dist/dashboard/home/ask-controls.js.map +1 -1
  13. package/dist/dashboard/home/ask-mode-chip.js +4 -4
  14. package/dist/dashboard/home/ask-mode-chip.js.map +1 -1
  15. package/dist/dashboard/home/home-ask.js +3 -3
  16. package/dist/dashboard/home/home-ask.js.map +1 -1
  17. package/dist/dashboard/home/home-quick-actions.js +9 -1
  18. package/dist/dashboard/home/home-quick-actions.js.map +1 -1
  19. package/dist/dashboard/home/host-quick-actions.d.ts +19 -0
  20. package/dist/dashboard/home/host-quick-actions.js +12 -0
  21. package/dist/dashboard/home/host-quick-actions.js.map +1 -0
  22. package/dist/dashboard/home/widgets/activity-widget.js +7 -1
  23. package/dist/dashboard/home/widgets/activity-widget.js.map +1 -1
  24. package/dist/features/gift-card-artwork.js +1 -1
  25. package/dist/features/gift-card-artwork.js.map +1 -1
  26. package/dist/features/gift-cards/gift-card-logic.d.ts +41 -0
  27. package/dist/features/gift-cards/gift-card-logic.js +65 -0
  28. package/dist/features/gift-cards/gift-card-logic.js.map +1 -0
  29. package/dist/features/gift-cards/gift-cards-feature.js +656 -518
  30. package/dist/features/gift-cards/gift-cards-feature.js.map +1 -1
  31. package/dist/features/gift-cards/hint.d.ts +9 -0
  32. package/dist/features/gift-cards/hint.js +25 -0
  33. package/dist/features/gift-cards/hint.js.map +1 -0
  34. package/dist/features/product-form.js +1 -1
  35. package/dist/features/product-form.js.map +1 -1
  36. package/dist/mobile/screens/billing-flows.js +7 -6
  37. package/dist/mobile/screens/billing-flows.js.map +1 -1
  38. package/dist/mobile/screens/billing.js +2 -2
  39. package/dist/mobile/screens/billing.js.map +1 -1
  40. package/dist/sidebar/nav-renderers.js +2 -0
  41. package/dist/sidebar/nav-renderers.js.map +1 -1
  42. package/dist/sidebar/nav-section.js +5 -3
  43. package/dist/sidebar/nav-section.js.map +1 -1
  44. package/package.json +5 -5
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/features/product-form.tsx"],"sourcesContent":["// @ts-nocheck — Legacy product form, no longer imported. Kept for reference only.\n'use client';\n\n/**\n * @deprecated Use ContentTypeStepperPanel + useProductFormState + useApiDispatch instead.\n *\n * This component is retained only for the product detail edit pages\n * (inventory/[productId]/page.tsx). New creation flows should use\n * ContentTypeStepperPanel with productMode props.\n */\n\nimport { useState, useEffect, useMemo, useCallback } from 'react';\nimport {\n PointerSensor,\n KeyboardSensor,\n useSensor,\n useSensors,\n type DragEndEvent,\n} from '@dnd-kit/core';\nimport { arrayMove, sortableKeyboardCoordinates } from '@dnd-kit/sortable';\nimport { useAuth } from '@withpotter/lib/auth-context';\nimport { useCurrentTemplate } from '@withpotter/lib/template-context';\nimport { getCurrencySymbol } from '@withpotter/components/dynamic-form';\nimport { contentApi } from '@withpotter/lib/api/content';\nimport { inventoryApi } from '@withpotter/lib/api/inventory';\nimport { merchantSubscriptionsApi } from '@withpotter/lib/api/merchant-subscriptions';\nimport { resourceCategoriesApi } from '@withpotter/lib/api/resource-categories';\nimport type { MerchantPlan } from '@withpotter/lib/api/types';\nimport { ContentItem } from '@withpotter/lib/api/types';\nimport {\n ProductType,\n generateSlug,\n type CreateProductPayload,\n type Product,\n type ProductMedia,\n type ProductBehaviors,\n type BookableConfig,\n type ShippableConfig,\n type SubscribableConfig,\n type DownloadableConfig,\n type RentableConfig,\n type TicketedConfig,\n isBehaviorEnabled,\n} from '@withpotter/lib/product-types';\nimport type { ContentResourceRequirementItem } from '@withpotter/lib/resource-types';\nimport {\n FormStepperPanel,\n type FormStep,\n} from '@withpotter/ui-kit/form-stepper-panel';\nimport {\n type BehaviorKey,\n type BehaviorLabelOverrides,\n} from '@withpotter/components/behaviors/behavior-config-section';\nimport { createBasicsStep } from './product-form/basics-step';\nimport {\n createBehaviorConfigPanels,\n createBehaviorsStep,\n} from './product-form/behaviors-step';\nimport { createDetailsStep } from './product-form/details-step';\nimport { createOrganizeStep } from './product-form/organize-step';\n\n// ============================================================================\n// TYPES\n// ============================================================================\n\ninterface ProductFormProps {\n /** Existing product to edit, or null for create mode */\n product?: Product | null;\n /** Called after successful save with the saved product */\n onSaved: () => void;\n /** Called when the form is closed/cancelled */\n onClose: () => void;\n}\n\n// Platform field names that are extracted from formValues and sent as top-level payload fields\n// Fields that are rendered manually in the platform section below.\n// All other template fields pass through to DynamicForm (e.g. category,\n// tags, gallery, shortDescription, allergens, dietary, etc.).\nconst PLATFORM_FIELDS = new Set([\n 'title',\n 'slug',\n 'description',\n 'featured',\n 'isActive',\n 'image',\n 'gallery',\n 'price',\n 'compareAtPrice',\n 'currency',\n 'trackInventory',\n 'quantity',\n 'lowStockThreshold',\n 'type',\n]);\n\nfunction normalizeMediaItem(input: unknown): ProductMedia | null {\n if (!input) return null;\n if (typeof input === 'string') {\n return { url: input, type: 'image' };\n }\n\n if (typeof input !== 'object') return null;\n const item = input as Record<string, unknown>;\n if (typeof item.url !== 'string' || item.url.trim().length === 0) return null;\n\n return {\n url: item.url,\n type: item.type === 'video' ? 'video' : 'image',\n alt: typeof item.alt === 'string' ? item.alt : undefined,\n width: typeof item.width === 'number' ? item.width : undefined,\n height: typeof item.height === 'number' ? item.height : undefined,\n thumbnail: typeof item.thumbnail === 'string' ? item.thumbnail : undefined,\n duration: typeof item.duration === 'number' ? item.duration : undefined,\n };\n}\n\nfunction normalizeMediaList(input: unknown): ProductMedia[] {\n if (!Array.isArray(input)) return [];\n return input\n .map((item) => normalizeMediaItem(item))\n .filter((item): item is ProductMedia => item !== null);\n}\n\ninterface ProductGalleryItem extends ProductMedia {\n id: string;\n}\n\nfunction createGalleryItemId(url: string): string {\n return `${url}-${Math.random().toString(36).slice(2, 10)}`;\n}\n\nfunction toGalleryItems(media: ProductMedia[]): ProductGalleryItem[] {\n return media.map((item) => ({\n ...item,\n id: createGalleryItemId(item.url),\n }));\n}\n\nfunction stripGalleryItemIds(items: ProductGalleryItem[]): ProductMedia[] {\n return items.map(({ id, ...rest }) => rest);\n}\n\n// ============================================================================\n// COMPONENT\n// ============================================================================\n\nexport function ProductForm({ product, onSaved, onClose }: ProductFormProps) {\n const { tenant } = useAuth();\n const { template } = useCurrentTemplate(tenant?.templateId);\n const sellableContentType = null as any;\n\n // Form state\n const [saving, setSaving] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const [slugManuallyEdited, setSlugManuallyEdited] = useState(false);\n\n // Platform field values\n const [productType, setProductType] = useState<ProductType>(\n (product?.type as ProductType) || ProductType.PHYSICAL,\n );\n const [title, setTitle] = useState(product?.title || '');\n const [slug, setSlug] = useState(product?.slug || '');\n const [description, setDescription] = useState(product?.description || '');\n const [price, setPrice] = useState<number | undefined>(\n product?.price != null ? Number(product.price) : undefined,\n );\n const [compareAtPrice, setCompareAtPrice] = useState<number | undefined>(\n product?.compareAtPrice != null\n ? Number(product.compareAtPrice)\n : undefined,\n );\n const [trackInventory, setTrackInventory] = useState(\n product?.trackInventory ?? true,\n );\n const [quantity, setQuantity] = useState<number | ''>(product?.quantity ?? 0);\n const [lowStockThreshold, setLowStockThreshold] = useState<number | ''>(\n product?.lowStockThreshold ?? 5,\n );\n const [featured, setFeatured] = useState(product?.featured ?? false);\n const [isActive, setIsActive] = useState(product?.isActive ?? true);\n const [heroImage, setHeroImage] = useState<ProductMedia | null>(\n normalizeMediaItem(product?.image) || null,\n );\n const [galleryItems, setGalleryItems] = useState<ProductGalleryItem[]>(() =>\n toGalleryItems(normalizeMediaList(product?.gallery)),\n );\n const [showHeroImageModal, setShowHeroImageModal] = useState(false);\n const [showGalleryMediaModal, setShowGalleryMediaModal] = useState(false);\n const [editingGalleryIndex, setEditingGalleryIndex] = useState<number | null>(\n null,\n );\n\n const gallerySensors = useSensors(\n useSensor(PointerSensor, {\n activationConstraint: { distance: 4 },\n }),\n useSensor(KeyboardSensor, {\n coordinateGetter: sortableKeyboardCoordinates,\n }),\n );\n\n const handleGalleryDragEnd = useCallback(\n (event: DragEndEvent) => {\n const { active, over } = event;\n if (!over || active.id === over.id) return;\n const oldIndex = galleryItems.findIndex(\n (item) => item.id === String(active.id),\n );\n const newIndex = galleryItems.findIndex(\n (item) => item.id === String(over.id),\n );\n if (oldIndex < 0 || newIndex < 0) return;\n setGalleryItems((prev) => arrayMove(prev, oldIndex, newIndex));\n },\n [galleryItems],\n );\n\n // Collection assignment state\n const [allCollections, setAllCollections] = useState<ContentItem[]>([]);\n const [selectedCollectionIds, setSelectedCollectionIds] = useState<\n Set<string>\n >(new Set());\n const [collectionsLoading, setCollectionsLoading] = useState(false);\n\n // Resource categories & requirements state (for bookable behavior)\n const [resourceCategories, setResourceCategories] = useState<\n Array<{ id: string; label: string; labelPlural: string; kind: string }>\n >([]);\n const [resourceRequirements, setResourceRequirements] = useState<\n ContentResourceRequirementItem[]\n >([]);\n const [activePlans, setActivePlans] = useState<MerchantPlan[]>([]);\n\n // Template field values (stored in `data`)\n const [templateValues, setTemplateValues] = useState<Record<string, unknown>>(\n product?.data || {},\n );\n\n const isEditing = !!product;\n\n // Get tenant currency\n const tenantCurrency = useMemo(() => {\n const settings = tenant?.settings as Record<string, unknown> | undefined;\n return (settings?.currency as string) || 'USD';\n }, [tenant?.settings]);\n\n // Get tenant categories from settings, falling back to template defaults\n const tenantCategories = useMemo(() => {\n const saved =\n ((tenant?.settings as Record<string, unknown>)?.categories as Record<\n string,\n { value: string; label: string; sortOrder?: number }[]\n >) || {};\n\n // If tenant already has saved categories, use them\n if (Object.keys(saved).length > 0) return saved;\n\n // Fall back to defaults from template's categoryManager siteSettings\n if (!template?.siteSettings) return saved;\n const defaults: Record<\n string,\n { value: string; label: string; sortOrder?: number }[]\n > = {};\n for (const [, section] of Object.entries(template.siteSettings)) {\n const s = section as unknown as {\n type?: string;\n contentTypeKey?: string;\n defaults?: { value: string; label: string; sortOrder?: number }[];\n };\n if (s?.type === 'categoryManager' && s.contentTypeKey && s.defaults) {\n defaults[s.contentTypeKey] = [...s.defaults];\n }\n }\n return Object.keys(defaults).length > 0 ? defaults : saved;\n }, [tenant?.settings, template?.siteSettings]);\n\n // Currency symbol helper (moved up — needed by behavior config panels)\n const currencySymbol = useMemo(\n () => getCurrencySymbol(tenantCurrency),\n [tenantCurrency],\n );\n\n // Industry-defined fields from the content type\n const mergedFields = useMemo(() => {\n if (!sellableContentType) return [];\n return sellableContentType.fields;\n }, [sellableContentType]);\n\n // Filter out platform fields that we render manually — only show template-specific fields\n const templateOnlyFields = useMemo(() => {\n return mergedFields.filter((f) => !PLATFORM_FIELDS.has(f.name));\n }, [mergedFields]);\n\n // ── Suggested detail sections (opt-in template fields) ──\n // Build a list of toggleable sections from template field groups + ungrouped fields\n const detailSections = useMemo(() => {\n const groups = sellableContentType?.fieldGroups || [];\n const groupedFieldNames = new Set(groups.flatMap((g) => g.fields));\n const ungrouped = templateOnlyFields.filter(\n (f) => !groupedFieldNames.has(f.name),\n );\n\n const sections: Array<{\n key: string;\n label: string;\n description?: string;\n fieldNames: string[];\n }> = [];\n\n // Each field group becomes a section\n for (const g of groups) {\n const hasFields = g.fields.some((fn) =>\n templateOnlyFields.some((f) => f.name === fn),\n );\n if (hasFields) {\n sections.push({\n key: `group:${g.label}`,\n label: g.label,\n description: g.description,\n fieldNames: g.fields,\n });\n }\n }\n\n // Each ungrouped field becomes its own section\n for (const f of ungrouped) {\n sections.push({\n key: `field:${f.name}`,\n label: f.label || f.name,\n fieldNames: [f.name],\n });\n }\n\n return sections;\n }, [templateOnlyFields, sellableContentType?.fieldGroups]);\n\n // Track which sections the user has opted into\n const [enabledDetailSections, setEnabledDetailSections] = useState<\n Set<string>\n >(new Set());\n\n // On edit, auto-enable sections that already have data\n useEffect(() => {\n if (!isEditing || !product?.data || detailSections.length === 0) return;\n const data = product.data as Record<string, unknown>;\n const enabled = new Set<string>();\n for (const section of detailSections) {\n const hasData = section.fieldNames.some((fn) => {\n const val = data[fn];\n if (val === undefined || val === null || val === '') return false;\n if (Array.isArray(val) && val.length === 0) return false;\n return true;\n });\n if (hasData) enabled.add(section.key);\n }\n if (enabled.size > 0) setEnabledDetailSections(enabled);\n }, [isEditing, product?.data, detailSections]);\n\n const toggleDetailSection = useCallback((key: string) => {\n setEnabledDetailSections((prev) => {\n const next = new Set(prev);\n if (next.has(key)) {\n next.delete(key);\n } else {\n next.add(key);\n }\n return next;\n });\n }, []);\n\n // Filter template fields + groups to only enabled ones for DynamicForm\n const activeTemplateFields = useMemo(() => {\n const enabledFieldNames = new Set<string>();\n for (const section of detailSections) {\n if (enabledDetailSections.has(section.key)) {\n for (const fn of section.fieldNames) enabledFieldNames.add(fn);\n }\n }\n return templateOnlyFields.filter((f) => enabledFieldNames.has(f.name));\n }, [templateOnlyFields, detailSections, enabledDetailSections]);\n\n const activeFieldGroups = useMemo(() => {\n if (!sellableContentType?.fieldGroups) return undefined;\n return sellableContentType.fieldGroups.filter((g) =>\n enabledDetailSections.has(`group:${g.label}`),\n );\n }, [sellableContentType?.fieldGroups, enabledDetailSections]);\n\n // Auto-generate slug from title (on create only)\n useEffect(() => {\n if (!isEditing && !slugManuallyEdited && title) {\n setSlug(generateSlug(title));\n }\n }, [title, isEditing, slugManuallyEdited]);\n\n // Pre-populate template values from product.data on edit\n useEffect(() => {\n setTemplateValues(product?.data || {});\n setHeroImage(normalizeMediaItem(product?.image) || null);\n setGalleryItems(toGalleryItems(normalizeMediaList(product?.gallery)));\n }, [product?.data, product?.image, product?.gallery]);\n\n // Fetch collections and pre-select those containing this product\n useEffect(() => {\n if (!tenant?.slug) return;\n let cancelled = false;\n const fetchCollections = async () => {\n setCollectionsLoading(true);\n try {\n const result = await contentApi.list(tenant.slug, 'collections', {\n limit: 200,\n });\n if (cancelled) return;\n const items = result.data || [];\n setAllCollections(items);\n\n // On edit, find which collections already contain this product\n if (product?.id) {\n const matched = new Set<string>();\n for (const c of items) {\n const ids = c.data?.productIds;\n if (Array.isArray(ids) && ids.includes(product.id)) {\n matched.add(c.id);\n }\n }\n setSelectedCollectionIds(matched);\n }\n } catch {\n // silently handle — collections picker just won't appear\n } finally {\n if (!cancelled) setCollectionsLoading(false);\n }\n };\n fetchCollections();\n return () => {\n cancelled = true;\n };\n }, [tenant?.slug, product?.id]);\n\n // Fetch resource categories (for bookable resource requirements)\n useEffect(() => {\n if (!tenant?.slug) return;\n let cancelled = false;\n const fetchCategories = async () => {\n try {\n const cats = await resourceCategoriesApi.list(tenant.slug);\n if (cancelled) return;\n setResourceCategories(\n (cats || []).map((c) => ({\n id: c.id,\n label: c.label || (c as any).name || '',\n labelPlural: c.labelPlural || '',\n kind: c.kind || 'person',\n })),\n );\n } catch {\n // silently handle — resource requirements section won't appear\n }\n };\n fetchCategories();\n return () => {\n cancelled = true;\n };\n }, [tenant?.slug]);\n\n // Fetch active MerchantPlans (for subscribable linked plan selector)\n useEffect(() => {\n if (!tenant?.slug) return;\n merchantSubscriptionsApi\n .getActivePlans(tenant.slug)\n .then((plans) => setActivePlans(plans || []))\n .catch(() => {\n // silently handle — plan selector will be hidden when plans is empty\n });\n }, [tenant?.slug]);\n\n // Fetch existing resource requirements when editing a product\n useEffect(() => {\n if (!tenant?.slug || !product?.id) return;\n let cancelled = false;\n const fetchRequirements = async () => {\n try {\n const reqs = await resourceCategoriesApi.getContentRequirements(\n tenant.slug,\n product.id,\n );\n if (cancelled) return;\n setResourceRequirements(\n (reqs || []).map((r) => ({\n categoryId: r.categoryId,\n isRequired: r.isRequired ?? true,\n assignmentStrategy: r.assignmentStrategy as\n ContentResourceRequirementItem['assignmentStrategy'] | undefined,\n requiredSkills: r.requiredSkills || [],\n eligibleResourceIds: r.eligibleResourceIds || null,\n })),\n );\n } catch {\n // silently handle — no existing requirements\n }\n };\n fetchRequirements();\n return () => {\n cancelled = true;\n };\n }, [tenant?.slug, product?.id]);\n\n // ── Behavior config state (Phase 4: config objects instead of booleans) ──\n const [behaviorConfigs, setBehaviorConfigs] = useState<ProductBehaviors>(\n () => {\n // On edit, load existing behaviors from the product\n if (product?.behaviors) {\n return product.behaviors as ProductBehaviors;\n }\n // On create, initialize from the template's content type behaviors\n if (sellableContentType?.behaviors) {\n const b: ProductBehaviors = {};\n for (const behavior of sellableContentType.behaviors) {\n (b as any)[behavior] = { enabled: true };\n }\n return b;\n }\n return { sellable: { enabled: true } };\n },\n );\n\n // Re-initialize behavior configs when the template changes (create mode)\n useEffect(() => {\n if (!isEditing && sellableContentType?.behaviors) {\n setBehaviorConfigs((prev) => {\n // Only set defaults for behaviors that aren't already configured\n const updated = { ...prev };\n for (const behavior of sellableContentType.behaviors) {\n if ((updated as any)[behavior] === undefined) {\n (updated as any)[behavior] = { enabled: true };\n }\n }\n return updated;\n });\n }\n }, [sellableContentType, isEditing]);\n\n // ── Content gating state ──\n const [gatedBy, setGatedBy] = useState<string[]>(() => {\n if (\n product?.behaviors?.gatedBy &&\n Array.isArray(product.behaviors.gatedBy)\n ) {\n return product.behaviors.gatedBy;\n }\n return [];\n });\n const [gatedByPlanIds, setGatedByPlanIds] = useState<string[]>(() => {\n if (\n product?.behaviors?.gatedByPlanIds &&\n Array.isArray(product.behaviors.gatedByPlanIds)\n ) {\n return product.behaviors.gatedByPlanIds;\n }\n return [];\n });\n\n // Derive which MerchantPlan is linked to the subscribable behavior\n const linkedPlanId = useMemo(() => {\n const config = getBehaviorConfig<SubscribableConfig>(\n behaviorConfigs.subscribable,\n );\n return config?.linkedPlanId;\n }, [behaviorConfigs.subscribable]);\n\n const linkedPlanName = useMemo(() => {\n if (!linkedPlanId || !activePlans) return undefined;\n return activePlans.find((p) => p.id === linkedPlanId)?.name;\n }, [linkedPlanId, activePlans]);\n\n // Derive which behaviors are available for the product form toggle list.\n // Priority: tenant's enabledBehaviors > template content type behaviors > fallback to all behaviors\n const availableBehaviors = useMemo<BehaviorKey[]>(() => {\n const tenantSettings = tenant?.settings as\n Record<string, unknown> | undefined;\n const tenantBehaviors = tenantSettings?.enabledBehaviors;\n\n // 1. Tenant has explicit preferences — use them\n if (Array.isArray(tenantBehaviors) && tenantBehaviors.length > 0) {\n return tenantBehaviors.filter(\n (b): b is BehaviorKey => b !== 'giftable' && b !== 'leadCapture',\n );\n }\n\n // 2. Template content type defines behaviors\n if (\n sellableContentType?.behaviors &&\n sellableContentType.behaviors.length > 0\n ) {\n const filtered = sellableContentType.behaviors.filter(\n (b): b is BehaviorKey => b !== 'giftable' && b !== 'leadCapture',\n );\n if (filtered.length > 0) return filtered;\n }\n\n // 3. Fallback — offer all behaviors by platform default\n return [\n 'sellable',\n 'bookable',\n 'shippable',\n 'subscribable',\n 'downloadable',\n 'rentable',\n 'ticketed',\n ] as BehaviorKey[];\n }, [tenant?.settings, sellableContentType]);\n\n // Template-provided behavior label overrides\n const behaviorLabelOverrides = useMemo<\n BehaviorLabelOverrides | undefined\n >(() => {\n // Templates can provide behaviorLabels in their schema\n const ct = sellableContentType as unknown as {\n behaviorLabels?: BehaviorLabelOverrides;\n };\n return ct?.behaviorLabels;\n }, [sellableContentType]);\n\n // Build flat behaviors for backward-compatible save (also used by behavior field merging)\n const behaviors = useMemo(() => {\n const b: Record<string, boolean> = {};\n for (const [key, value] of Object.entries(behaviorConfigs)) {\n if (key === 'gatedBy' || key === 'gatedByPlanIds' || key === 'reservable')\n continue;\n b[key] = isBehaviorEnabled(value);\n }\n return b;\n }, [behaviorConfigs]);\n\n // ── Behavior config update handlers ──\n const updateBookableConfig = useCallback((config: BookableConfig) => {\n setBehaviorConfigs((prev) => ({ ...prev, bookable: config }));\n }, []);\n\n const updateShippableConfig = useCallback((config: ShippableConfig) => {\n setBehaviorConfigs((prev) => ({ ...prev, shippable: config }));\n }, []);\n\n const updateSubscribableConfig = useCallback((config: SubscribableConfig) => {\n setBehaviorConfigs((prev) => ({ ...prev, subscribable: config }));\n }, []);\n\n const updateDownloadableConfig = useCallback((config: DownloadableConfig) => {\n setBehaviorConfigs((prev) => ({ ...prev, downloadable: config }));\n }, []);\n\n const updateRentableConfig = useCallback((config: RentableConfig) => {\n setBehaviorConfigs((prev) => ({ ...prev, rentable: config }));\n }, []);\n\n const updateTicketedConfig = useCallback((config: TicketedConfig) => {\n setBehaviorConfigs((prev) => ({ ...prev, ticketed: config }));\n }, []);\n\n // Build config panel map for the BehaviorConfigSection\n const behaviorConfigPanels = useMemo(\n () =>\n createBehaviorConfigPanels({\n behaviorConfigs,\n updateBookableConfig,\n updateShippableConfig,\n updateSubscribableConfig,\n updateDownloadableConfig,\n updateRentableConfig,\n updateTicketedConfig,\n behaviorLabelOverrides,\n currencySymbol,\n resourceCategories,\n resourceRequirements,\n setResourceRequirements,\n activePlans,\n }),\n [\n behaviorConfigs,\n updateBookableConfig,\n updateShippableConfig,\n updateSubscribableConfig,\n updateDownloadableConfig,\n updateRentableConfig,\n updateTicketedConfig,\n behaviorLabelOverrides,\n currencySymbol,\n resourceCategories,\n resourceRequirements,\n ],\n );\n\n // Handle save\n const handleSave = useCallback(async () => {\n if (!tenant?.slug) return;\n\n // Basic validation\n if (!title.trim()) {\n setError('Title is required');\n return;\n }\n if (!slug.trim()) {\n setError('Slug is required');\n return;\n }\n\n setSaving(true);\n setError(null);\n\n try {\n const payload: CreateProductPayload = {\n type: productType,\n title: title.trim(),\n slug: slug.trim(),\n description: description || undefined,\n price: price !== undefined ? Number(price) || 0 : undefined,\n compareAtPrice:\n compareAtPrice !== undefined\n ? Number(compareAtPrice) || 0\n : undefined,\n currency: tenantCurrency,\n trackInventory,\n quantity: typeof quantity === 'number' ? quantity : 0,\n lowStockThreshold:\n typeof lowStockThreshold === 'number' ? lowStockThreshold : 5,\n featured,\n isActive,\n image: heroImage\n ? ({\n ...heroImage,\n type: 'image',\n } as ProductMedia)\n : undefined,\n // An empty list on an edit is sent as [], not left out: the API reads\n // an omitted gallery as \"unchanged\", so removing the last item would\n // save successfully and bring it straight back.\n gallery:\n galleryItems.length > 0\n ? stripGalleryItemIds(galleryItems)\n : isEditing\n ? []\n : undefined,\n behaviors: {\n ...behaviorConfigs,\n ...(gatedBy.length > 0 && { gatedBy }),\n ...(gatedByPlanIds.length > 0 && { gatedByPlanIds }),\n },\n data: templateValues,\n };\n\n let productId: string | undefined;\n\n if (isEditing && product) {\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n const { type, ...updatePayload } = payload;\n await inventoryApi.update(\n tenant.slug,\n product.id,\n updatePayload as Record<string, unknown>,\n );\n productId = product.id;\n } else {\n const created = await inventoryApi.create(\n tenant.slug,\n payload as unknown as Record<string, unknown>,\n );\n productId = created.id;\n }\n\n // Sync collection assignments — add/remove this product from collections\n if (productId && allCollections.length > 0) {\n const updates: Promise<unknown>[] = [];\n for (const collection of allCollections) {\n const currentIds: string[] = Array.isArray(\n collection.data?.productIds,\n )\n ? (collection.data.productIds as string[])\n : [];\n const hasProduct = currentIds.includes(productId);\n const shouldHave = selectedCollectionIds.has(collection.id);\n\n if (shouldHave && !hasProduct) {\n // Add product to this collection\n updates.push(\n contentApi.update(tenant.slug, 'collections', collection.id, {\n data: {\n ...collection.data,\n productIds: [...currentIds, productId],\n },\n }),\n );\n } else if (!shouldHave && hasProduct) {\n // Remove product from this collection\n updates.push(\n contentApi.update(tenant.slug, 'collections', collection.id, {\n data: {\n ...collection.data,\n productIds: currentIds.filter((id) => id !== productId),\n },\n }),\n );\n }\n }\n if (updates.length > 0) {\n await Promise.all(updates);\n }\n }\n\n // Save resource requirements if bookable behavior is active\n if (\n productId &&\n isBehaviorEnabled(behaviorConfigs.bookable) &&\n resourceRequirements.length > 0\n ) {\n try {\n await resourceCategoriesApi.setContentRequirements(\n tenant.slug,\n productId,\n { requirements: resourceRequirements },\n );\n } catch {\n // Non-blocking — product was saved, requirements just didn't persist\n console.warn('Failed to save resource requirements');\n }\n }\n\n onSaved();\n } catch (e: unknown) {\n const msg = e instanceof Error ? e.message : 'Failed to save product';\n setError(msg);\n } finally {\n setSaving(false);\n }\n }, [\n tenant?.slug,\n productType,\n title,\n slug,\n description,\n price,\n compareAtPrice,\n tenantCurrency,\n trackInventory,\n quantity,\n lowStockThreshold,\n featured,\n isActive,\n heroImage,\n galleryItems,\n behaviorConfigs,\n gatedBy,\n gatedByPlanIds,\n templateValues,\n isEditing,\n product,\n onSaved,\n allCollections,\n selectedCollectionIds,\n resourceRequirements,\n ]);\n\n // Currency formatting helpers\n const formatCurrency = (num: number | undefined): string => {\n if (num === undefined || num === null || isNaN(num)) return '';\n return new Intl.NumberFormat('en-US', {\n minimumFractionDigits: 2,\n maximumFractionDigits: 2,\n }).format(num);\n };\n\n const parseCurrency = (str: string): number | undefined => {\n const cleaned = str.replace(/[^0-9.]/g, '');\n if (!cleaned) return undefined;\n const num = parseFloat(cleaned);\n return isNaN(num) ? undefined : num;\n };\n\n // Display state for currency fields\n const [priceDisplay, setPriceDisplay] = useState(() =>\n formatCurrency(product?.price != null ? Number(product.price) : undefined),\n );\n const [priceFocused, setPriceFocused] = useState(false);\n const [compareDisplay, setCompareDisplay] = useState(() =>\n formatCurrency(\n product?.compareAtPrice != null\n ? Number(product.compareAtPrice)\n : undefined,\n ),\n );\n const [compareFocused, setCompareFocused] = useState(false);\n\n // Build steps for the stepper\n const steps = useMemo<FormStep[]>(() => {\n const result: FormStep[] = [];\n\n // Step 1: Title, Image & Pricing\n result.push(\n createBasicsStep({\n title,\n setTitle,\n heroImage,\n setHeroImage,\n showHeroImageModal,\n setShowHeroImageModal,\n galleryItems,\n setGalleryItems,\n gallerySensors,\n handleGalleryDragEnd,\n showGalleryMediaModal,\n setShowGalleryMediaModal,\n editingGalleryIndex,\n setEditingGalleryIndex,\n description,\n setDescription,\n currencySymbol,\n priceFocused,\n setPriceFocused,\n priceDisplay,\n setPriceDisplay,\n price,\n setPrice,\n compareFocused,\n setCompareFocused,\n compareDisplay,\n setCompareDisplay,\n compareAtPrice,\n setCompareAtPrice,\n formatCurrency,\n parseCurrency,\n }),\n );\n\n // Step 2: Behaviors — What can customers do with this product?\n result.push(\n createBehaviorsStep({\n behaviorConfigs,\n setBehaviorConfigs,\n behaviorLabelOverrides,\n availableBehaviors,\n behaviorConfigPanels,\n gatedBy,\n setGatedBy,\n gatedByPlanIds,\n setGatedByPlanIds,\n activePlans,\n linkedPlanName,\n }),\n );\n\n // Step 3: Product Details & Inventory\n result.push(\n createDetailsStep({\n behaviorConfigs,\n detailSections,\n enabledDetailSections,\n toggleDetailSection,\n activeTemplateFields,\n activeFieldGroups,\n templateValues,\n setTemplateValues,\n tenantCurrency,\n tenantCategories,\n tenantSlug: tenant?.slug,\n trackInventory,\n setTrackInventory,\n quantity,\n setQuantity,\n lowStockThreshold,\n setLowStockThreshold,\n }),\n );\n\n // Step 4: Collections & Organization\n result.push(\n createOrganizeStep({\n collectionsLoading,\n allCollections,\n selectedCollectionIds,\n setSelectedCollectionIds,\n isActive,\n setIsActive,\n isEditing,\n productType,\n setProductType,\n slug,\n setSlug,\n setSlugManuallyEdited,\n product,\n featured,\n setFeatured,\n }),\n );\n\n return result;\n }, [\n title,\n heroImage,\n galleryItems,\n showHeroImageModal,\n showGalleryMediaModal,\n editingGalleryIndex,\n gallerySensors,\n handleGalleryDragEnd,\n description,\n currencySymbol,\n priceFocused,\n priceDisplay,\n price,\n compareFocused,\n compareDisplay,\n compareAtPrice,\n templateOnlyFields,\n sellableContentType,\n templateValues,\n tenantCurrency,\n tenantCategories,\n tenant?.slug,\n trackInventory,\n quantity,\n lowStockThreshold,\n collectionsLoading,\n allCollections,\n selectedCollectionIds,\n isEditing,\n productType,\n slug,\n product?.slug,\n isActive,\n featured,\n behaviorConfigs,\n behaviorLabelOverrides,\n availableBehaviors,\n behaviorConfigPanels,\n detailSections,\n enabledDetailSections,\n toggleDetailSection,\n activeTemplateFields,\n activeFieldGroups,\n gatedBy,\n gatedByPlanIds,\n linkedPlanId,\n linkedPlanName,\n ]);\n\n return (\n <FormStepperPanel\n title={isEditing ? 'Edit Product' : 'Add Product'}\n steps={steps}\n onSave={handleSave}\n onClose={onClose}\n saving={saving}\n saveLabel={isEditing ? 'Update Product' : 'Create Product'}\n error={error}\n onClearError={() => setError(null)}\n />\n );\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/features/product-form.tsx"],"sourcesContent":["// @ts-nocheck — Legacy product form, no longer imported. Kept for reference only.\n'use client';\n\n/**\n * @deprecated Use ContentTypeStepperPanel + useProductFormState + useApiDispatch instead.\n *\n * This component is retained only for the product detail edit pages\n * (inventory/[productId]/page.tsx). New creation flows should use\n * ContentTypeStepperPanel with productMode props.\n */\n\nimport { useState, useEffect, useMemo, useCallback } from 'react';\nimport {\n PointerSensor,\n KeyboardSensor,\n useSensor,\n useSensors,\n type DragEndEvent,\n} from '@dnd-kit/core';\nimport { arrayMove, sortableKeyboardCoordinates } from '@dnd-kit/sortable';\nimport { useAuth } from '@withpotter/lib/auth-context';\nimport { useCurrentTemplate } from '@withpotter/lib/template-context';\nimport { getCurrencySymbol } from '@withpotter/components/dynamic-form';\nimport { contentApi } from '@withpotter/lib/api/content';\nimport { inventoryApi } from '@withpotter/lib/api/inventory';\nimport { merchantSubscriptionsApi } from '@withpotter/lib/api/merchant-subscriptions';\nimport { resourceCategoriesApi } from '@withpotter/lib/api/resource-categories';\nimport type { MerchantPlan } from '@withpotter/lib/api/types';\nimport { ContentItem } from '@withpotter/lib/api/types';\nimport {\n ProductType,\n generateSlug,\n type CreateProductPayload,\n type Product,\n type ProductMedia,\n type ProductBehaviors,\n type BookableConfig,\n type ShippableConfig,\n type SubscribableConfig,\n type DownloadableConfig,\n type RentableConfig,\n type TicketedConfig,\n isBehaviorEnabled,\n} from '@withpotter/lib/product-types';\nimport type { ContentResourceRequirementItem } from '@withpotter/lib/resource-types';\nimport {\n FormStepperPanel,\n type FormStep,\n} from '@withpotter/ui-kit/form-stepper-panel';\nimport {\n type BehaviorKey,\n type BehaviorLabelOverrides,\n} from '@withpotter/components/behaviors/behavior-config-section';\nimport { createBasicsStep } from './product-form/basics-step';\nimport {\n createBehaviorConfigPanels,\n createBehaviorsStep,\n} from './product-form/behaviors-step';\nimport { createDetailsStep } from './product-form/details-step';\nimport { createOrganizeStep } from './product-form/organize-step';\n\n// ============================================================================\n// TYPES\n// ============================================================================\n\ninterface ProductFormProps {\n /** Existing product to edit, or null for create mode */\n product?: Product | null;\n /** Called after successful save with the saved product */\n onSaved: () => void;\n /** Called when the form is closed/cancelled */\n onClose: () => void;\n}\n\n// Platform field names that are extracted from formValues and sent as top-level payload fields\n// Fields that are rendered manually in the platform section below.\n// All other template fields pass through to DynamicForm (e.g. category,\n// tags, gallery, shortDescription, allergens, dietary, etc.).\nconst PLATFORM_FIELDS = new Set([\n 'title',\n 'slug',\n 'description',\n 'featured',\n 'isActive',\n 'image',\n 'gallery',\n 'price',\n 'compareAtPrice',\n 'currency',\n 'trackInventory',\n 'quantity',\n 'lowStockThreshold',\n 'type',\n]);\n\nfunction normalizeMediaItem(input: unknown): ProductMedia | null {\n if (!input) return null;\n if (typeof input === 'string') {\n return { url: input, type: 'image' };\n }\n\n if (typeof input !== 'object') return null;\n const item = input as Record<string, unknown>;\n if (typeof item.url !== 'string' || item.url.trim().length === 0) return null;\n\n return {\n url: item.url,\n type: item.type === 'video' ? 'video' : 'image',\n alt: typeof item.alt === 'string' ? item.alt : undefined,\n width: typeof item.width === 'number' ? item.width : undefined,\n height: typeof item.height === 'number' ? item.height : undefined,\n thumbnail: typeof item.thumbnail === 'string' ? item.thumbnail : undefined,\n duration: typeof item.duration === 'number' ? item.duration : undefined,\n };\n}\n\nfunction normalizeMediaList(input: unknown): ProductMedia[] {\n if (!Array.isArray(input)) return [];\n return input\n .map((item) => normalizeMediaItem(item))\n .filter((item): item is ProductMedia => item !== null);\n}\n\ninterface ProductGalleryItem extends ProductMedia {\n id: string;\n}\n\nfunction createGalleryItemId(url: string): string {\n return `${url}-${Math.random().toString(36).slice(2, 10)}`;\n}\n\nfunction toGalleryItems(media: ProductMedia[]): ProductGalleryItem[] {\n return media.map((item) => ({\n ...item,\n id: createGalleryItemId(item.url),\n }));\n}\n\nfunction stripGalleryItemIds(items: ProductGalleryItem[]): ProductMedia[] {\n return items.map(({ id, ...rest }) => rest);\n}\n\n// ============================================================================\n// COMPONENT\n// ============================================================================\n\nexport function ProductForm({ product, onSaved, onClose }: ProductFormProps) {\n const { tenant } = useAuth();\n const { template } = useCurrentTemplate(tenant?.templateId);\n const sellableContentType = null as any;\n\n // Form state\n const [saving, setSaving] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const [slugManuallyEdited, setSlugManuallyEdited] = useState(false);\n\n // Platform field values\n const [productType, setProductType] = useState<ProductType>(\n (product?.type as ProductType) || ProductType.PHYSICAL,\n );\n const [title, setTitle] = useState(product?.title || '');\n const [slug, setSlug] = useState(product?.slug || '');\n const [description, setDescription] = useState(product?.description || '');\n const [price, setPrice] = useState<number | undefined>(\n product?.price != null ? Number(product.price) : undefined,\n );\n const [compareAtPrice, setCompareAtPrice] = useState<number | undefined>(\n product?.compareAtPrice != null\n ? Number(product.compareAtPrice)\n : undefined,\n );\n const [trackInventory, setTrackInventory] = useState(\n product?.trackInventory ?? true,\n );\n const [quantity, setQuantity] = useState<number | ''>(product?.quantity ?? 0);\n const [lowStockThreshold, setLowStockThreshold] = useState<number | ''>(\n product?.lowStockThreshold ?? 5,\n );\n const [featured, setFeatured] = useState(product?.featured ?? false);\n const [isActive, setIsActive] = useState(product?.isActive ?? true);\n const [heroImage, setHeroImage] = useState<ProductMedia | null>(\n normalizeMediaItem(product?.image) || null,\n );\n const [galleryItems, setGalleryItems] = useState<ProductGalleryItem[]>(() =>\n toGalleryItems(normalizeMediaList(product?.gallery)),\n );\n const [showHeroImageModal, setShowHeroImageModal] = useState(false);\n const [showGalleryMediaModal, setShowGalleryMediaModal] = useState(false);\n const [editingGalleryIndex, setEditingGalleryIndex] = useState<number | null>(\n null,\n );\n\n const gallerySensors = useSensors(\n useSensor(PointerSensor, {\n activationConstraint: { distance: 4 },\n }),\n useSensor(KeyboardSensor, {\n coordinateGetter: sortableKeyboardCoordinates,\n }),\n );\n\n const handleGalleryDragEnd = useCallback(\n (event: DragEndEvent) => {\n const { active, over } = event;\n if (!over || active.id === over.id) return;\n const oldIndex = galleryItems.findIndex(\n (item) => item.id === String(active.id),\n );\n const newIndex = galleryItems.findIndex(\n (item) => item.id === String(over.id),\n );\n if (oldIndex < 0 || newIndex < 0) return;\n setGalleryItems((prev) => arrayMove(prev, oldIndex, newIndex));\n },\n [galleryItems],\n );\n\n // Collection assignment state\n const [allCollections, setAllCollections] = useState<ContentItem[]>([]);\n const [selectedCollectionIds, setSelectedCollectionIds] = useState<\n Set<string>\n >(new Set());\n const [collectionsLoading, setCollectionsLoading] = useState(false);\n\n // Resource categories & requirements state (for bookable behavior)\n const [resourceCategories, setResourceCategories] = useState<\n Array<{ id: string; label: string; labelPlural: string; kind: string }>\n >([]);\n const [resourceRequirements, setResourceRequirements] = useState<\n ContentResourceRequirementItem[]\n >([]);\n const [activePlans, setActivePlans] = useState<MerchantPlan[]>([]);\n\n // Template field values (stored in `data`)\n const [templateValues, setTemplateValues] = useState<Record<string, unknown>>(\n product?.data || {},\n );\n\n const isEditing = !!product;\n\n // Get tenant currency\n const tenantCurrency = useMemo(() => {\n const settings = tenant?.settings as Record<string, unknown> | undefined;\n return (settings?.currency as string) || 'NGN';\n }, [tenant?.settings]);\n\n // Get tenant categories from settings, falling back to template defaults\n const tenantCategories = useMemo(() => {\n const saved =\n ((tenant?.settings as Record<string, unknown>)?.categories as Record<\n string,\n { value: string; label: string; sortOrder?: number }[]\n >) || {};\n\n // If tenant already has saved categories, use them\n if (Object.keys(saved).length > 0) return saved;\n\n // Fall back to defaults from template's categoryManager siteSettings\n if (!template?.siteSettings) return saved;\n const defaults: Record<\n string,\n { value: string; label: string; sortOrder?: number }[]\n > = {};\n for (const [, section] of Object.entries(template.siteSettings)) {\n const s = section as unknown as {\n type?: string;\n contentTypeKey?: string;\n defaults?: { value: string; label: string; sortOrder?: number }[];\n };\n if (s?.type === 'categoryManager' && s.contentTypeKey && s.defaults) {\n defaults[s.contentTypeKey] = [...s.defaults];\n }\n }\n return Object.keys(defaults).length > 0 ? defaults : saved;\n }, [tenant?.settings, template?.siteSettings]);\n\n // Currency symbol helper (moved up — needed by behavior config panels)\n const currencySymbol = useMemo(\n () => getCurrencySymbol(tenantCurrency),\n [tenantCurrency],\n );\n\n // Industry-defined fields from the content type\n const mergedFields = useMemo(() => {\n if (!sellableContentType) return [];\n return sellableContentType.fields;\n }, [sellableContentType]);\n\n // Filter out platform fields that we render manually — only show template-specific fields\n const templateOnlyFields = useMemo(() => {\n return mergedFields.filter((f) => !PLATFORM_FIELDS.has(f.name));\n }, [mergedFields]);\n\n // ── Suggested detail sections (opt-in template fields) ──\n // Build a list of toggleable sections from template field groups + ungrouped fields\n const detailSections = useMemo(() => {\n const groups = sellableContentType?.fieldGroups || [];\n const groupedFieldNames = new Set(groups.flatMap((g) => g.fields));\n const ungrouped = templateOnlyFields.filter(\n (f) => !groupedFieldNames.has(f.name),\n );\n\n const sections: Array<{\n key: string;\n label: string;\n description?: string;\n fieldNames: string[];\n }> = [];\n\n // Each field group becomes a section\n for (const g of groups) {\n const hasFields = g.fields.some((fn) =>\n templateOnlyFields.some((f) => f.name === fn),\n );\n if (hasFields) {\n sections.push({\n key: `group:${g.label}`,\n label: g.label,\n description: g.description,\n fieldNames: g.fields,\n });\n }\n }\n\n // Each ungrouped field becomes its own section\n for (const f of ungrouped) {\n sections.push({\n key: `field:${f.name}`,\n label: f.label || f.name,\n fieldNames: [f.name],\n });\n }\n\n return sections;\n }, [templateOnlyFields, sellableContentType?.fieldGroups]);\n\n // Track which sections the user has opted into\n const [enabledDetailSections, setEnabledDetailSections] = useState<\n Set<string>\n >(new Set());\n\n // On edit, auto-enable sections that already have data\n useEffect(() => {\n if (!isEditing || !product?.data || detailSections.length === 0) return;\n const data = product.data as Record<string, unknown>;\n const enabled = new Set<string>();\n for (const section of detailSections) {\n const hasData = section.fieldNames.some((fn) => {\n const val = data[fn];\n if (val === undefined || val === null || val === '') return false;\n if (Array.isArray(val) && val.length === 0) return false;\n return true;\n });\n if (hasData) enabled.add(section.key);\n }\n if (enabled.size > 0) setEnabledDetailSections(enabled);\n }, [isEditing, product?.data, detailSections]);\n\n const toggleDetailSection = useCallback((key: string) => {\n setEnabledDetailSections((prev) => {\n const next = new Set(prev);\n if (next.has(key)) {\n next.delete(key);\n } else {\n next.add(key);\n }\n return next;\n });\n }, []);\n\n // Filter template fields + groups to only enabled ones for DynamicForm\n const activeTemplateFields = useMemo(() => {\n const enabledFieldNames = new Set<string>();\n for (const section of detailSections) {\n if (enabledDetailSections.has(section.key)) {\n for (const fn of section.fieldNames) enabledFieldNames.add(fn);\n }\n }\n return templateOnlyFields.filter((f) => enabledFieldNames.has(f.name));\n }, [templateOnlyFields, detailSections, enabledDetailSections]);\n\n const activeFieldGroups = useMemo(() => {\n if (!sellableContentType?.fieldGroups) return undefined;\n return sellableContentType.fieldGroups.filter((g) =>\n enabledDetailSections.has(`group:${g.label}`),\n );\n }, [sellableContentType?.fieldGroups, enabledDetailSections]);\n\n // Auto-generate slug from title (on create only)\n useEffect(() => {\n if (!isEditing && !slugManuallyEdited && title) {\n setSlug(generateSlug(title));\n }\n }, [title, isEditing, slugManuallyEdited]);\n\n // Pre-populate template values from product.data on edit\n useEffect(() => {\n setTemplateValues(product?.data || {});\n setHeroImage(normalizeMediaItem(product?.image) || null);\n setGalleryItems(toGalleryItems(normalizeMediaList(product?.gallery)));\n }, [product?.data, product?.image, product?.gallery]);\n\n // Fetch collections and pre-select those containing this product\n useEffect(() => {\n if (!tenant?.slug) return;\n let cancelled = false;\n const fetchCollections = async () => {\n setCollectionsLoading(true);\n try {\n const result = await contentApi.list(tenant.slug, 'collections', {\n limit: 200,\n });\n if (cancelled) return;\n const items = result.data || [];\n setAllCollections(items);\n\n // On edit, find which collections already contain this product\n if (product?.id) {\n const matched = new Set<string>();\n for (const c of items) {\n const ids = c.data?.productIds;\n if (Array.isArray(ids) && ids.includes(product.id)) {\n matched.add(c.id);\n }\n }\n setSelectedCollectionIds(matched);\n }\n } catch {\n // silently handle — collections picker just won't appear\n } finally {\n if (!cancelled) setCollectionsLoading(false);\n }\n };\n fetchCollections();\n return () => {\n cancelled = true;\n };\n }, [tenant?.slug, product?.id]);\n\n // Fetch resource categories (for bookable resource requirements)\n useEffect(() => {\n if (!tenant?.slug) return;\n let cancelled = false;\n const fetchCategories = async () => {\n try {\n const cats = await resourceCategoriesApi.list(tenant.slug);\n if (cancelled) return;\n setResourceCategories(\n (cats || []).map((c) => ({\n id: c.id,\n label: c.label || (c as any).name || '',\n labelPlural: c.labelPlural || '',\n kind: c.kind || 'person',\n })),\n );\n } catch {\n // silently handle — resource requirements section won't appear\n }\n };\n fetchCategories();\n return () => {\n cancelled = true;\n };\n }, [tenant?.slug]);\n\n // Fetch active MerchantPlans (for subscribable linked plan selector)\n useEffect(() => {\n if (!tenant?.slug) return;\n merchantSubscriptionsApi\n .getActivePlans(tenant.slug)\n .then((plans) => setActivePlans(plans || []))\n .catch(() => {\n // silently handle — plan selector will be hidden when plans is empty\n });\n }, [tenant?.slug]);\n\n // Fetch existing resource requirements when editing a product\n useEffect(() => {\n if (!tenant?.slug || !product?.id) return;\n let cancelled = false;\n const fetchRequirements = async () => {\n try {\n const reqs = await resourceCategoriesApi.getContentRequirements(\n tenant.slug,\n product.id,\n );\n if (cancelled) return;\n setResourceRequirements(\n (reqs || []).map((r) => ({\n categoryId: r.categoryId,\n isRequired: r.isRequired ?? true,\n assignmentStrategy: r.assignmentStrategy as\n ContentResourceRequirementItem['assignmentStrategy'] | undefined,\n requiredSkills: r.requiredSkills || [],\n eligibleResourceIds: r.eligibleResourceIds || null,\n })),\n );\n } catch {\n // silently handle — no existing requirements\n }\n };\n fetchRequirements();\n return () => {\n cancelled = true;\n };\n }, [tenant?.slug, product?.id]);\n\n // ── Behavior config state (Phase 4: config objects instead of booleans) ──\n const [behaviorConfigs, setBehaviorConfigs] = useState<ProductBehaviors>(\n () => {\n // On edit, load existing behaviors from the product\n if (product?.behaviors) {\n return product.behaviors as ProductBehaviors;\n }\n // On create, initialize from the template's content type behaviors\n if (sellableContentType?.behaviors) {\n const b: ProductBehaviors = {};\n for (const behavior of sellableContentType.behaviors) {\n (b as any)[behavior] = { enabled: true };\n }\n return b;\n }\n return { sellable: { enabled: true } };\n },\n );\n\n // Re-initialize behavior configs when the template changes (create mode)\n useEffect(() => {\n if (!isEditing && sellableContentType?.behaviors) {\n setBehaviorConfigs((prev) => {\n // Only set defaults for behaviors that aren't already configured\n const updated = { ...prev };\n for (const behavior of sellableContentType.behaviors) {\n if ((updated as any)[behavior] === undefined) {\n (updated as any)[behavior] = { enabled: true };\n }\n }\n return updated;\n });\n }\n }, [sellableContentType, isEditing]);\n\n // ── Content gating state ──\n const [gatedBy, setGatedBy] = useState<string[]>(() => {\n if (\n product?.behaviors?.gatedBy &&\n Array.isArray(product.behaviors.gatedBy)\n ) {\n return product.behaviors.gatedBy;\n }\n return [];\n });\n const [gatedByPlanIds, setGatedByPlanIds] = useState<string[]>(() => {\n if (\n product?.behaviors?.gatedByPlanIds &&\n Array.isArray(product.behaviors.gatedByPlanIds)\n ) {\n return product.behaviors.gatedByPlanIds;\n }\n return [];\n });\n\n // Derive which MerchantPlan is linked to the subscribable behavior\n const linkedPlanId = useMemo(() => {\n const config = getBehaviorConfig<SubscribableConfig>(\n behaviorConfigs.subscribable,\n );\n return config?.linkedPlanId;\n }, [behaviorConfigs.subscribable]);\n\n const linkedPlanName = useMemo(() => {\n if (!linkedPlanId || !activePlans) return undefined;\n return activePlans.find((p) => p.id === linkedPlanId)?.name;\n }, [linkedPlanId, activePlans]);\n\n // Derive which behaviors are available for the product form toggle list.\n // Priority: tenant's enabledBehaviors > template content type behaviors > fallback to all behaviors\n const availableBehaviors = useMemo<BehaviorKey[]>(() => {\n const tenantSettings = tenant?.settings as\n Record<string, unknown> | undefined;\n const tenantBehaviors = tenantSettings?.enabledBehaviors;\n\n // 1. Tenant has explicit preferences — use them\n if (Array.isArray(tenantBehaviors) && tenantBehaviors.length > 0) {\n return tenantBehaviors.filter(\n (b): b is BehaviorKey => b !== 'giftable' && b !== 'leadCapture',\n );\n }\n\n // 2. Template content type defines behaviors\n if (\n sellableContentType?.behaviors &&\n sellableContentType.behaviors.length > 0\n ) {\n const filtered = sellableContentType.behaviors.filter(\n (b): b is BehaviorKey => b !== 'giftable' && b !== 'leadCapture',\n );\n if (filtered.length > 0) return filtered;\n }\n\n // 3. Fallback — offer all behaviors by platform default\n return [\n 'sellable',\n 'bookable',\n 'shippable',\n 'subscribable',\n 'downloadable',\n 'rentable',\n 'ticketed',\n ] as BehaviorKey[];\n }, [tenant?.settings, sellableContentType]);\n\n // Template-provided behavior label overrides\n const behaviorLabelOverrides = useMemo<\n BehaviorLabelOverrides | undefined\n >(() => {\n // Templates can provide behaviorLabels in their schema\n const ct = sellableContentType as unknown as {\n behaviorLabels?: BehaviorLabelOverrides;\n };\n return ct?.behaviorLabels;\n }, [sellableContentType]);\n\n // Build flat behaviors for backward-compatible save (also used by behavior field merging)\n const behaviors = useMemo(() => {\n const b: Record<string, boolean> = {};\n for (const [key, value] of Object.entries(behaviorConfigs)) {\n if (key === 'gatedBy' || key === 'gatedByPlanIds' || key === 'reservable')\n continue;\n b[key] = isBehaviorEnabled(value);\n }\n return b;\n }, [behaviorConfigs]);\n\n // ── Behavior config update handlers ──\n const updateBookableConfig = useCallback((config: BookableConfig) => {\n setBehaviorConfigs((prev) => ({ ...prev, bookable: config }));\n }, []);\n\n const updateShippableConfig = useCallback((config: ShippableConfig) => {\n setBehaviorConfigs((prev) => ({ ...prev, shippable: config }));\n }, []);\n\n const updateSubscribableConfig = useCallback((config: SubscribableConfig) => {\n setBehaviorConfigs((prev) => ({ ...prev, subscribable: config }));\n }, []);\n\n const updateDownloadableConfig = useCallback((config: DownloadableConfig) => {\n setBehaviorConfigs((prev) => ({ ...prev, downloadable: config }));\n }, []);\n\n const updateRentableConfig = useCallback((config: RentableConfig) => {\n setBehaviorConfigs((prev) => ({ ...prev, rentable: config }));\n }, []);\n\n const updateTicketedConfig = useCallback((config: TicketedConfig) => {\n setBehaviorConfigs((prev) => ({ ...prev, ticketed: config }));\n }, []);\n\n // Build config panel map for the BehaviorConfigSection\n const behaviorConfigPanels = useMemo(\n () =>\n createBehaviorConfigPanels({\n behaviorConfigs,\n updateBookableConfig,\n updateShippableConfig,\n updateSubscribableConfig,\n updateDownloadableConfig,\n updateRentableConfig,\n updateTicketedConfig,\n behaviorLabelOverrides,\n currencySymbol,\n resourceCategories,\n resourceRequirements,\n setResourceRequirements,\n activePlans,\n }),\n [\n behaviorConfigs,\n updateBookableConfig,\n updateShippableConfig,\n updateSubscribableConfig,\n updateDownloadableConfig,\n updateRentableConfig,\n updateTicketedConfig,\n behaviorLabelOverrides,\n currencySymbol,\n resourceCategories,\n resourceRequirements,\n ],\n );\n\n // Handle save\n const handleSave = useCallback(async () => {\n if (!tenant?.slug) return;\n\n // Basic validation\n if (!title.trim()) {\n setError('Title is required');\n return;\n }\n if (!slug.trim()) {\n setError('Slug is required');\n return;\n }\n\n setSaving(true);\n setError(null);\n\n try {\n const payload: CreateProductPayload = {\n type: productType,\n title: title.trim(),\n slug: slug.trim(),\n description: description || undefined,\n price: price !== undefined ? Number(price) || 0 : undefined,\n compareAtPrice:\n compareAtPrice !== undefined\n ? Number(compareAtPrice) || 0\n : undefined,\n currency: tenantCurrency,\n trackInventory,\n quantity: typeof quantity === 'number' ? quantity : 0,\n lowStockThreshold:\n typeof lowStockThreshold === 'number' ? lowStockThreshold : 5,\n featured,\n isActive,\n image: heroImage\n ? ({\n ...heroImage,\n type: 'image',\n } as ProductMedia)\n : undefined,\n // An empty list on an edit is sent as [], not left out: the API reads\n // an omitted gallery as \"unchanged\", so removing the last item would\n // save successfully and bring it straight back.\n gallery:\n galleryItems.length > 0\n ? stripGalleryItemIds(galleryItems)\n : isEditing\n ? []\n : undefined,\n behaviors: {\n ...behaviorConfigs,\n ...(gatedBy.length > 0 && { gatedBy }),\n ...(gatedByPlanIds.length > 0 && { gatedByPlanIds }),\n },\n data: templateValues,\n };\n\n let productId: string | undefined;\n\n if (isEditing && product) {\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n const { type, ...updatePayload } = payload;\n await inventoryApi.update(\n tenant.slug,\n product.id,\n updatePayload as Record<string, unknown>,\n );\n productId = product.id;\n } else {\n const created = await inventoryApi.create(\n tenant.slug,\n payload as unknown as Record<string, unknown>,\n );\n productId = created.id;\n }\n\n // Sync collection assignments — add/remove this product from collections\n if (productId && allCollections.length > 0) {\n const updates: Promise<unknown>[] = [];\n for (const collection of allCollections) {\n const currentIds: string[] = Array.isArray(\n collection.data?.productIds,\n )\n ? (collection.data.productIds as string[])\n : [];\n const hasProduct = currentIds.includes(productId);\n const shouldHave = selectedCollectionIds.has(collection.id);\n\n if (shouldHave && !hasProduct) {\n // Add product to this collection\n updates.push(\n contentApi.update(tenant.slug, 'collections', collection.id, {\n data: {\n ...collection.data,\n productIds: [...currentIds, productId],\n },\n }),\n );\n } else if (!shouldHave && hasProduct) {\n // Remove product from this collection\n updates.push(\n contentApi.update(tenant.slug, 'collections', collection.id, {\n data: {\n ...collection.data,\n productIds: currentIds.filter((id) => id !== productId),\n },\n }),\n );\n }\n }\n if (updates.length > 0) {\n await Promise.all(updates);\n }\n }\n\n // Save resource requirements if bookable behavior is active\n if (\n productId &&\n isBehaviorEnabled(behaviorConfigs.bookable) &&\n resourceRequirements.length > 0\n ) {\n try {\n await resourceCategoriesApi.setContentRequirements(\n tenant.slug,\n productId,\n { requirements: resourceRequirements },\n );\n } catch {\n // Non-blocking — product was saved, requirements just didn't persist\n console.warn('Failed to save resource requirements');\n }\n }\n\n onSaved();\n } catch (e: unknown) {\n const msg = e instanceof Error ? e.message : 'Failed to save product';\n setError(msg);\n } finally {\n setSaving(false);\n }\n }, [\n tenant?.slug,\n productType,\n title,\n slug,\n description,\n price,\n compareAtPrice,\n tenantCurrency,\n trackInventory,\n quantity,\n lowStockThreshold,\n featured,\n isActive,\n heroImage,\n galleryItems,\n behaviorConfigs,\n gatedBy,\n gatedByPlanIds,\n templateValues,\n isEditing,\n product,\n onSaved,\n allCollections,\n selectedCollectionIds,\n resourceRequirements,\n ]);\n\n // Currency formatting helpers\n const formatCurrency = (num: number | undefined): string => {\n if (num === undefined || num === null || isNaN(num)) return '';\n return new Intl.NumberFormat('en-US', {\n minimumFractionDigits: 2,\n maximumFractionDigits: 2,\n }).format(num);\n };\n\n const parseCurrency = (str: string): number | undefined => {\n const cleaned = str.replace(/[^0-9.]/g, '');\n if (!cleaned) return undefined;\n const num = parseFloat(cleaned);\n return isNaN(num) ? undefined : num;\n };\n\n // Display state for currency fields\n const [priceDisplay, setPriceDisplay] = useState(() =>\n formatCurrency(product?.price != null ? Number(product.price) : undefined),\n );\n const [priceFocused, setPriceFocused] = useState(false);\n const [compareDisplay, setCompareDisplay] = useState(() =>\n formatCurrency(\n product?.compareAtPrice != null\n ? Number(product.compareAtPrice)\n : undefined,\n ),\n );\n const [compareFocused, setCompareFocused] = useState(false);\n\n // Build steps for the stepper\n const steps = useMemo<FormStep[]>(() => {\n const result: FormStep[] = [];\n\n // Step 1: Title, Image & Pricing\n result.push(\n createBasicsStep({\n title,\n setTitle,\n heroImage,\n setHeroImage,\n showHeroImageModal,\n setShowHeroImageModal,\n galleryItems,\n setGalleryItems,\n gallerySensors,\n handleGalleryDragEnd,\n showGalleryMediaModal,\n setShowGalleryMediaModal,\n editingGalleryIndex,\n setEditingGalleryIndex,\n description,\n setDescription,\n currencySymbol,\n priceFocused,\n setPriceFocused,\n priceDisplay,\n setPriceDisplay,\n price,\n setPrice,\n compareFocused,\n setCompareFocused,\n compareDisplay,\n setCompareDisplay,\n compareAtPrice,\n setCompareAtPrice,\n formatCurrency,\n parseCurrency,\n }),\n );\n\n // Step 2: Behaviors — What can customers do with this product?\n result.push(\n createBehaviorsStep({\n behaviorConfigs,\n setBehaviorConfigs,\n behaviorLabelOverrides,\n availableBehaviors,\n behaviorConfigPanels,\n gatedBy,\n setGatedBy,\n gatedByPlanIds,\n setGatedByPlanIds,\n activePlans,\n linkedPlanName,\n }),\n );\n\n // Step 3: Product Details & Inventory\n result.push(\n createDetailsStep({\n behaviorConfigs,\n detailSections,\n enabledDetailSections,\n toggleDetailSection,\n activeTemplateFields,\n activeFieldGroups,\n templateValues,\n setTemplateValues,\n tenantCurrency,\n tenantCategories,\n tenantSlug: tenant?.slug,\n trackInventory,\n setTrackInventory,\n quantity,\n setQuantity,\n lowStockThreshold,\n setLowStockThreshold,\n }),\n );\n\n // Step 4: Collections & Organization\n result.push(\n createOrganizeStep({\n collectionsLoading,\n allCollections,\n selectedCollectionIds,\n setSelectedCollectionIds,\n isActive,\n setIsActive,\n isEditing,\n productType,\n setProductType,\n slug,\n setSlug,\n setSlugManuallyEdited,\n product,\n featured,\n setFeatured,\n }),\n );\n\n return result;\n }, [\n title,\n heroImage,\n galleryItems,\n showHeroImageModal,\n showGalleryMediaModal,\n editingGalleryIndex,\n gallerySensors,\n handleGalleryDragEnd,\n description,\n currencySymbol,\n priceFocused,\n priceDisplay,\n price,\n compareFocused,\n compareDisplay,\n compareAtPrice,\n templateOnlyFields,\n sellableContentType,\n templateValues,\n tenantCurrency,\n tenantCategories,\n tenant?.slug,\n trackInventory,\n quantity,\n lowStockThreshold,\n collectionsLoading,\n allCollections,\n selectedCollectionIds,\n isEditing,\n productType,\n slug,\n product?.slug,\n isActive,\n featured,\n behaviorConfigs,\n behaviorLabelOverrides,\n availableBehaviors,\n behaviorConfigPanels,\n detailSections,\n enabledDetailSections,\n toggleDetailSection,\n activeTemplateFields,\n activeFieldGroups,\n gatedBy,\n gatedByPlanIds,\n linkedPlanId,\n linkedPlanName,\n ]);\n\n return (\n <FormStepperPanel\n title={isEditing ? 'Edit Product' : 'Add Product'}\n steps={steps}\n onSave={handleSave}\n onClose={onClose}\n saving={saving}\n saveLabel={isEditing ? 'Update Product' : 'Create Product'}\n error={error}\n onClearError={() => setError(null)}\n />\n );\n}\n"],"mappings":"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","names":[]}
@@ -1,6 +1,7 @@
1
1
  "use client";
2
2
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
3
  import React, { useMemo, useState } from "react";
4
+ import { pickPlanPrice } from "@withpotter/lib/plan-price";
4
5
  import {
5
6
  AlertTriangle,
6
7
  ArrowRight,
@@ -52,17 +53,17 @@ function MobileBillingPlanScreen({
52
53
  }) {
53
54
  const sortedPlans = useMemo(() => {
54
55
  return [...plans ?? []].sort((a, b) => {
55
- const ap = Object.values(a.prices)[0] ?? 0;
56
- const bp = Object.values(b.prices)[0] ?? 0;
56
+ const ap = pickPlanPrice(a.prices)?.amount ?? 0;
57
+ const bp = pickPlanPrice(b.prices)?.amount ?? 0;
57
58
  return ap - bp;
58
59
  });
59
60
  }, [plans]);
60
61
  const currentPlanId = subscription?.plan?.id;
61
62
  const [selectedId, setSelectedId] = useState(
62
- currentPlanId ?? sortedPlans.find((p) => Object.values(p.prices)[0] > 0)?.id ?? null
63
+ currentPlanId ?? sortedPlans.find((p) => (pickPlanPrice(p.prices)?.amount ?? 0) > 0)?.id ?? null
63
64
  );
64
65
  const selected = sortedPlans.find((p) => p.id === selectedId);
65
- const selectedPrice = selected ? Object.values(selected.prices)[0] ?? 0 : 0;
66
+ const selectedPrice = selected ? pickPlanPrice(selected.prices)?.amount ?? 0 : 0;
66
67
  const isCurrent = selectedId === currentPlanId;
67
68
  const cancelling = subscription?.cancellation?.cancelAtPeriodEnd;
68
69
  return /* @__PURE__ */ jsxs(
@@ -131,7 +132,7 @@ function MobileBillingPlanScreen({
131
132
  }
132
133
  ),
133
134
  /* @__PURE__ */ jsx("section", { className: "mt-6 grid grid-cols-2 gap-2", children: sortedPlans.map((p) => {
134
- const price = Object.values(p.prices)[0] ?? 0;
135
+ const price = pickPlanPrice(p.prices)?.amount ?? 0;
135
136
  const isActive = p.id === selectedId;
136
137
  return /* @__PURE__ */ jsxs(
137
138
  Button,
@@ -830,7 +831,7 @@ function MobileBillingInvoicesScreen({
830
831
  formatCurrency
831
832
  }) {
832
833
  const plan = subscription?.plan;
833
- const planPrice = plan ? Object.values(plan.prices)[0] ?? 0 : 0;
834
+ const planPrice = plan ? pickPlanPrice(plan.prices)?.amount ?? 0 : 0;
834
835
  const renewsAt = subscription?.currentPeriod?.end;
835
836
  return /* @__PURE__ */ jsxs(
836
837
  "div",