@uptrademedia/site-kit 1.0.0
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 +305 -0
- package/dist/analytics/index.js +88 -0
- package/dist/analytics/index.js.map +1 -0
- package/dist/analytics/index.mjs +70 -0
- package/dist/analytics/index.mjs.map +1 -0
- package/dist/api-N35S3EES.js +57 -0
- package/dist/api-N35S3EES.js.map +1 -0
- package/dist/api-SYBTK7Z7.mjs +4 -0
- package/dist/api-SYBTK7Z7.mjs.map +1 -0
- package/dist/blog/index.js +200 -0
- package/dist/blog/index.js.map +1 -0
- package/dist/blog/index.mjs +194 -0
- package/dist/blog/index.mjs.map +1 -0
- package/dist/chunk-3MUOUXHV.js +3721 -0
- package/dist/chunk-3MUOUXHV.js.map +1 -0
- package/dist/chunk-4HVYXYQL 2.mjs +255 -0
- package/dist/chunk-4HVYXYQL.mjs +255 -0
- package/dist/chunk-4HVYXYQL.mjs.map +1 -0
- package/dist/chunk-7H6I3ECV.mjs +120 -0
- package/dist/chunk-7H6I3ECV.mjs.map +1 -0
- package/dist/chunk-COI6GOX2.mjs +3679 -0
- package/dist/chunk-COI6GOX2.mjs.map +1 -0
- package/dist/chunk-EQCVQC35.js +35 -0
- package/dist/chunk-EQCVQC35.js 2.map +1 -0
- package/dist/chunk-EQCVQC35.js.map +1 -0
- package/dist/chunk-FEBYQGY4 2.mjs +251 -0
- package/dist/chunk-FEBYQGY4.mjs +251 -0
- package/dist/chunk-FEBYQGY4.mjs.map +1 -0
- package/dist/chunk-FKVJOT2F.mjs +796 -0
- package/dist/chunk-FKVJOT2F.mjs.map +1 -0
- package/dist/chunk-GQ6ZOU2N.mjs +134 -0
- package/dist/chunk-GQ6ZOU2N.mjs.map +1 -0
- package/dist/chunk-HCFPU7TU.js +137 -0
- package/dist/chunk-HCFPU7TU.js.map +1 -0
- package/dist/chunk-NYKRE2FL 2.mjs +31 -0
- package/dist/chunk-NYKRE2FL.mjs +31 -0
- package/dist/chunk-NYKRE2FL.mjs 2.map +1 -0
- package/dist/chunk-NYKRE2FL.mjs.map +1 -0
- package/dist/chunk-QP5NCO2E.js +133 -0
- package/dist/chunk-QP5NCO2E.js.map +1 -0
- package/dist/chunk-RV7H3I6J.js +255 -0
- package/dist/chunk-RV7H3I6J.js 2.map +1 -0
- package/dist/chunk-RV7H3I6J.js.map +1 -0
- package/dist/chunk-SBVEYCSV.js +140 -0
- package/dist/chunk-SBVEYCSV.js.map +1 -0
- package/dist/chunk-TUKGA3UK.js +257 -0
- package/dist/chunk-TUKGA3UK.js 2.map +1 -0
- package/dist/chunk-TUKGA3UK.js.map +1 -0
- package/dist/chunk-V3F5J6CV.js +801 -0
- package/dist/chunk-V3F5J6CV.js.map +1 -0
- package/dist/chunk-WPSRS352.mjs +135 -0
- package/dist/chunk-WPSRS352.mjs.map +1 -0
- package/dist/commerce/index.js +157 -0
- package/dist/commerce/index.js.map +1 -0
- package/dist/commerce/index.mjs +4 -0
- package/dist/commerce/index.mjs.map +1 -0
- package/dist/commerce/server.js +186 -0
- package/dist/commerce/server.js.map +1 -0
- package/dist/commerce/server.mjs +176 -0
- package/dist/commerce/server.mjs.map +1 -0
- package/dist/engage/index.js +50 -0
- package/dist/engage/index.js.map +1 -0
- package/dist/engage/index.mjs +44 -0
- package/dist/engage/index.mjs.map +1 -0
- package/dist/forms/index.js +1053 -0
- package/dist/forms/index.js.map +1 -0
- package/dist/forms/index.mjs +1035 -0
- package/dist/forms/index.mjs.map +1 -0
- package/dist/generators-7Y5ABRYV 2.mjs +161 -0
- package/dist/generators-7Y5ABRYV.mjs +161 -0
- package/dist/generators-7Y5ABRYV.mjs 2.map +1 -0
- package/dist/generators-7Y5ABRYV.mjs.map +1 -0
- package/dist/generators-GWIYCA5M.js +171 -0
- package/dist/generators-GWIYCA5M.js 2.map +1 -0
- package/dist/generators-GWIYCA5M.js.map +1 -0
- package/dist/index 2.mjs +74 -0
- package/dist/index.js +326 -0
- package/dist/index.js 2.map +1 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +222 -0
- package/dist/index.mjs.map +1 -0
- package/dist/migrator-V6KS75EA 2.mjs +265 -0
- package/dist/migrator-V6KS75EA.mjs +265 -0
- package/dist/migrator-V6KS75EA.mjs 2.map +1 -0
- package/dist/migrator-V6KS75EA.mjs.map +1 -0
- package/dist/migrator-XKM7YQCY.js +272 -0
- package/dist/migrator-XKM7YQCY.js 2.map +1 -0
- package/dist/migrator-XKM7YQCY.js.map +1 -0
- package/dist/scanner-MF7P3CDE 2.mjs +14386 -0
- package/dist/scanner-MF7P3CDE.mjs +14386 -0
- package/dist/scanner-MF7P3CDE.mjs 2.map +1 -0
- package/dist/scanner-MF7P3CDE.mjs.map +1 -0
- package/dist/scanner-NT6YG4TD 2.js +14397 -0
- package/dist/scanner-NT6YG4TD.js +14397 -0
- package/dist/scanner-NT6YG4TD.js 2.map +1 -0
- package/dist/scanner-NT6YG4TD.js.map +1 -0
- package/dist/seo/index.js +447 -0
- package/dist/seo/index.js.map +1 -0
- package/dist/seo/index.mjs +411 -0
- package/dist/seo/index.mjs.map +1 -0
- package/dist/seo/server.js +66 -0
- package/dist/seo/server.js.map +1 -0
- package/dist/seo/server.mjs +5 -0
- package/dist/seo/server.mjs.map +1 -0
- package/dist/setup/index.js +1050 -0
- package/dist/setup/index.js.map +1 -0
- package/dist/setup/index.mjs +1046 -0
- package/dist/setup/index.mjs.map +1 -0
- package/dist/sitemap/index.js +212 -0
- package/dist/sitemap/index.js.map +1 -0
- package/dist/sitemap/index.mjs +206 -0
- package/dist/sitemap/index.mjs.map +1 -0
- package/dist/web-vitals-BH55V7EJ.js +252 -0
- package/dist/web-vitals-BH55V7EJ.js 2.map +1 -0
- package/dist/web-vitals-BH55V7EJ.js.map +1 -0
- package/dist/web-vitals-RJYPWAR3 2.mjs +241 -0
- package/dist/web-vitals-RJYPWAR3.mjs +241 -0
- package/dist/web-vitals-RJYPWAR3.mjs 2.map +1 -0
- package/dist/web-vitals-RJYPWAR3.mjs.map +1 -0
- package/package.json +118 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/SiteKitProvider.tsx","../src/affiliates/api.ts","../src/affiliates/useAffiliates.ts","../src/affiliates/AffiliatesWidget.tsx"],"names":["createContext","useContext","useEffect","configureFormsApi","useMemo","jsx","Suspense","AnalyticsProvider","jsxs","Fragment","EngageWidget","SitemapSync","useState","getTrackingUrl"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * @uptrade/site-kit - SiteKitProvider\n * \n * Unified provider component that initializes all enabled modules.\n * All API calls go through Portal API with API key auth - never Supabase directly.\n */\n\n'use client'\n\nimport React, { createContext, useContext, useMemo, useEffect, ReactNode, Suspense } from 'react'\nimport type { SiteKitConfig } from './types'\n\n// Module providers\nimport { AnalyticsProvider } from './analytics/AnalyticsProvider'\nimport { EngageWidget } from './engage/EngageWidget'\nimport { configureFormsApi } from './forms/formsApi'\nimport { SitemapSync } from './seo/SitemapSync'\n\ninterface SiteKitContextValue extends SiteKitConfig {\n isReady: boolean\n}\n\nconst SiteKitContext = createContext<SiteKitContextValue | null>(null)\n\nexport function useSiteKit(): SiteKitContextValue {\n const context = useContext(SiteKitContext)\n if (!context) {\n throw new Error('useSiteKit must be used within a SiteKitProvider')\n }\n return context\n}\n\ninterface SiteKitProviderProps extends SiteKitConfig {\n children: ReactNode\n}\n\nexport function SiteKitProvider({\n children,\n apiUrl = 'https://api.uptrademedia.com',\n apiKey,\n analytics,\n engage,\n forms,\n debug = false,\n}: SiteKitProviderProps) {\n // Set window globals for Portal API access\n useEffect(() => {\n if (typeof window !== 'undefined') {\n ;(window as any).__SITE_KIT_API_URL__ = apiUrl\n ;(window as any).__SITE_KIT_API_KEY__ = apiKey\n ;(window as any).__SITE_KIT_DEBUG__ = debug\n }\n \n // Configure forms API\n configureFormsApi({\n baseUrl: apiUrl,\n apiKey,\n })\n }, [apiUrl, apiKey, debug])\n\n const contextValue = useMemo<SiteKitContextValue>(\n () => ({\n apiUrl,\n apiKey,\n analytics,\n engage,\n forms,\n debug,\n isReady: true,\n }),\n [apiUrl, apiKey, analytics, engage, forms, debug]\n )\n\n // Build the provider tree based on enabled modules\n let content = <>{children}</>\n\n // Wrap with Analytics if enabled\n if (analytics?.enabled) {\n content = (\n <Suspense fallback={null}>\n <AnalyticsProvider\n apiUrl={apiUrl}\n apiKey={apiKey}\n trackPageViews={analytics.trackPageViews !== false}\n trackWebVitals={analytics.trackWebVitals !== false}\n trackScrollDepth={analytics.trackScrollDepth !== false}\n trackClicks={analytics.trackClicks !== false}\n debug={debug}\n >\n {content}\n </AnalyticsProvider>\n </Suspense>\n )\n }\n\n // Add Engage widget if enabled (doesn't wrap, just renders alongside)\n if (engage?.enabled) {\n content = (\n <>\n {content}\n <EngageWidget apiUrl={apiUrl} apiKey={apiKey} />\n </>\n )\n }\n\n // Always include SitemapSync to keep seo_pages in sync with sitemap.xml\n // This is the canonical source of truth for what pages exist\n content = (\n <>\n {content}\n <SitemapSync debug={debug} />\n </>\n )\n\n return (\n <SiteKitContext.Provider value={contextValue}>\n {content}\n </SiteKitContext.Provider>\n )\n}\n\n/**\n * Hook to check if a specific module is enabled\n */\nexport function useModuleEnabled(module: 'analytics' | 'engage' | 'forms' | 'seo'): boolean {\n const context = useSiteKit()\n \n switch (module) {\n case 'analytics':\n return context.analytics?.enabled ?? false\n case 'engage':\n return context.engage?.enabled ?? false\n case 'forms':\n return context.forms?.enabled ?? false\n case 'seo':\n return true // SEO is always enabled via RSC components\n default:\n return false\n }\n}\n","/**\n * @uptrade/site-kit/affiliates - Affiliates API\n * \n * API functions for fetching affiliate data for client sites.\n * All data goes through Portal API with API key auth - never Supabase directly.\n */\n\nimport type { Affiliate, AffiliateWithOffers, FetchAffiliatesOptions } from './types'\n\n// ============================================\n// API Config Helpers\n// ============================================\n\nfunction getApiConfig() {\n const apiUrl = typeof window !== 'undefined' \n ? (window as any).__SITE_KIT_API_URL__ || 'https://api.uptrademedia.com'\n : 'https://api.uptrademedia.com'\n const apiKey = typeof window !== 'undefined' \n ? (window as any).__SITE_KIT_API_KEY__ || ''\n : ''\n return { apiUrl, apiKey }\n}\n\nasync function apiGet<T>(endpoint: string): Promise<T | null> {\n const { apiUrl, apiKey } = getApiConfig()\n \n if (!apiKey) {\n console.error('[Affiliates] No API key configured')\n return null\n }\n \n try {\n const response = await fetch(`${apiUrl}${endpoint}`, {\n method: 'GET',\n headers: {\n 'x-api-key': apiKey,\n },\n })\n \n if (!response.ok) {\n console.error(`[Affiliates] API error: ${response.statusText}`)\n return null\n }\n \n return await response.json()\n } catch (error) {\n console.error('[Affiliates] Network error:', error)\n return null\n }\n}\n\n// ============================================\n// Fetch Affiliates\n// ============================================\n\n/**\n * Fetch all active affiliates for the project\n * \n * @example\n * ```ts\n * const affiliates = await fetchAffiliates()\n * ```\n */\nexport async function fetchAffiliates(\n options: FetchAffiliatesOptions = {}\n): Promise<AffiliateWithOffers[]> {\n const result = await apiGet<{ affiliates: AffiliateWithOffers[] }>(\n '/api/public/affiliates'\n )\n \n return result?.affiliates || []\n}\n\n/**\n * Build a tracking URL for an affiliate offer\n * When clicked, this redirects through our tracking endpoint and then to the destination\n * \n * @example\n * ```ts\n * const trackingUrl = getTrackingUrl(affiliate.id, offer.id)\n * // Returns: https://api.uptrademedia.com/a/{affiliateId}/{offerId}\n * ```\n */\nexport function getTrackingUrl(affiliateId: string, offerId: string): string {\n const { apiUrl } = getApiConfig()\n return `${apiUrl}/a/${affiliateId}/${offerId}`\n}\n","/**\n * @uptrade/site-kit/affiliates - useAffiliates Hook\n * \n * React hook for fetching and displaying affiliates on client sites.\n */\n\n'use client'\n\nimport { useState, useEffect } from 'react'\nimport type { AffiliateWithOffers } from './types'\nimport { fetchAffiliates, getTrackingUrl } from './api'\n\nexport interface UseAffiliatesResult {\n affiliates: AffiliateWithOffers[]\n isLoading: boolean\n error: string | null\n getTrackingUrl: (affiliateId: string, offerId: string) => string\n}\n\n/**\n * Hook to fetch affiliates for display on client sites\n * \n * @example\n * ```tsx\n * function AffiliatesSection() {\n * const { affiliates, isLoading } = useAffiliates()\n * \n * if (isLoading) return <div>Loading...</div>\n * \n * return (\n * <div className=\"grid grid-cols-3 gap-4\">\n * {affiliates.map(affiliate => (\n * <AffiliateCard key={affiliate.id} affiliate={affiliate} />\n * ))}\n * </div>\n * )\n * }\n * ```\n */\nexport function useAffiliates(): UseAffiliatesResult {\n const [affiliates, setAffiliates] = useState<AffiliateWithOffers[]>([])\n const [isLoading, setIsLoading] = useState(true)\n const [error, setError] = useState<string | null>(null)\n\n useEffect(() => {\n async function load() {\n try {\n setIsLoading(true)\n setError(null)\n const data = await fetchAffiliates()\n setAffiliates(data)\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to load affiliates')\n } finally {\n setIsLoading(false)\n }\n }\n \n load()\n }, [])\n\n return {\n affiliates,\n isLoading,\n error,\n getTrackingUrl,\n }\n}\n","/**\n * @uptrade/site-kit/affiliates - AffiliatesWidget Component\n * \n * Unstyled, headless component for displaying affiliates.\n * Client sites can wrap this or use it directly with their own styling.\n */\n\n'use client'\n\nimport React from 'react'\nimport { useAffiliates } from './useAffiliates'\nimport type { AffiliateWithOffers, AffiliateOffer } from './types'\n\n// ============================================\n// Affiliate Card (Unstyled)\n// ============================================\n\nexport interface AffiliateCardProps {\n affiliate: AffiliateWithOffers\n className?: string\n /** Optional custom render for the logo */\n renderLogo?: (affiliate: AffiliateWithOffers) => React.ReactNode\n /** Show offers as links below the card */\n showOffers?: boolean\n}\n\n/**\n * Single affiliate card - unstyled, bring your own CSS\n * \n * @example\n * ```tsx\n * <AffiliateCard \n * affiliate={affiliate}\n * className=\"p-4 bg-white rounded-lg shadow\"\n * showOffers\n * />\n * ```\n */\nexport function AffiliateCard({\n affiliate,\n className = '',\n renderLogo,\n showOffers = false,\n}: AffiliateCardProps) {\n const { getTrackingUrl } = useAffiliates()\n \n const defaultLogo = (\n <div data-affiliate-logo className=\"affiliate-logo\">\n {affiliate.logo_url ? (\n <img \n src={affiliate.logo_url} \n alt={affiliate.name}\n loading=\"lazy\"\n />\n ) : (\n <span>{affiliate.name.charAt(0)}</span>\n )}\n </div>\n )\n\n return (\n <div className={className} data-affiliate-card data-affiliate-id={affiliate.id}>\n {/* Logo */}\n {renderLogo ? renderLogo(affiliate) : defaultLogo}\n \n {/* Name */}\n <div data-affiliate-name className=\"affiliate-name\">\n {affiliate.name}\n </div>\n \n {/* Website link */}\n {affiliate.website_url && (\n <a \n href={affiliate.website_url}\n target=\"_blank\"\n rel=\"noopener noreferrer sponsored\"\n data-affiliate-website\n className=\"affiliate-website\"\n >\n Visit Website\n </a>\n )}\n \n {/* Offers */}\n {showOffers && affiliate.offers && affiliate.offers.length > 0 && (\n <div data-affiliate-offers className=\"affiliate-offers\">\n {affiliate.offers.map(offer => (\n <a\n key={offer.id}\n href={getTrackingUrl(affiliate.id, offer.id)}\n target=\"_blank\"\n rel=\"noopener noreferrer sponsored\"\n data-affiliate-offer\n data-offer-id={offer.id}\n className=\"affiliate-offer\"\n >\n {offer.name}\n </a>\n ))}\n </div>\n )}\n </div>\n )\n}\n\n// ============================================\n// Affiliates Grid (Unstyled)\n// ============================================\n\nexport interface AffiliatesWidgetProps {\n className?: string\n /** Custom loading component */\n loadingComponent?: React.ReactNode\n /** Custom empty state component */\n emptyComponent?: React.ReactNode\n /** Custom error component */\n errorComponent?: React.ReactNode\n /** Show offers on each card */\n showOffers?: boolean\n /** Custom render function for each affiliate */\n renderAffiliate?: (affiliate: AffiliateWithOffers) => React.ReactNode\n /** Maximum number to display */\n limit?: number\n}\n\n/**\n * Display a grid of affiliates - unstyled, bring your own CSS\n * \n * @example\n * ```tsx\n * // Basic usage\n * <AffiliatesWidget className=\"grid grid-cols-3 gap-4\" />\n * \n * // With custom rendering\n * <AffiliatesWidget\n * renderAffiliate={(affiliate) => (\n * <MyCustomCard key={affiliate.id} data={affiliate} />\n * )}\n * />\n * ```\n */\nexport function AffiliatesWidget({\n className = '',\n loadingComponent,\n emptyComponent,\n errorComponent,\n showOffers = false,\n renderAffiliate,\n limit,\n}: AffiliatesWidgetProps) {\n const { affiliates, isLoading, error, getTrackingUrl } = useAffiliates()\n \n if (isLoading) {\n return loadingComponent || <div data-affiliates-loading>Loading affiliates...</div>\n }\n \n if (error) {\n return errorComponent || <div data-affiliates-error>{error}</div>\n }\n \n const displayAffiliates = limit ? affiliates.slice(0, limit) : affiliates\n \n if (displayAffiliates.length === 0) {\n return emptyComponent || null\n }\n \n return (\n <div className={className} data-affiliates-widget>\n {displayAffiliates.map(affiliate => \n renderAffiliate \n ? renderAffiliate(affiliate)\n : <AffiliateCard \n key={affiliate.id} \n affiliate={affiliate} \n showOffers={showOffers}\n />\n )}\n </div>\n )\n}\n"]}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
import { SitemapSync } from './chunk-WPSRS352.mjs';
|
|
2
|
+
import { AnalyticsProvider } from './chunk-FKVJOT2F.mjs';
|
|
3
|
+
import { EngageWidget } from './chunk-4HVYXYQL.mjs';
|
|
4
|
+
import { configureFormsApi } from './chunk-FEBYQGY4.mjs';
|
|
5
|
+
export { CalendarView, CheckoutForm, EventCalendar, EventEmbed, EventModal, EventTile, OfferingCard, OfferingList, ProductEmbed, RegistrationForm, UpcomingEvents, createCheckoutSession, fetchNextEvent, fetchOffering, fetchOfferings, fetchProducts, fetchServices, fetchUpcomingEvents, formatDate, formatDateTime, formatPrice, getOfferingUrl, registerForEvent, useEventModal } from './chunk-COI6GOX2.mjs';
|
|
6
|
+
import './chunk-NYKRE2FL.mjs';
|
|
7
|
+
import { createContext, useContext, useEffect, useMemo, Suspense, useState } from 'react';
|
|
8
|
+
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
9
|
+
|
|
10
|
+
var SiteKitContext = createContext(null);
|
|
11
|
+
function useSiteKit() {
|
|
12
|
+
const context = useContext(SiteKitContext);
|
|
13
|
+
if (!context) {
|
|
14
|
+
throw new Error("useSiteKit must be used within a SiteKitProvider");
|
|
15
|
+
}
|
|
16
|
+
return context;
|
|
17
|
+
}
|
|
18
|
+
function SiteKitProvider({
|
|
19
|
+
children,
|
|
20
|
+
apiUrl = "https://api.uptrademedia.com",
|
|
21
|
+
apiKey,
|
|
22
|
+
analytics,
|
|
23
|
+
engage,
|
|
24
|
+
forms,
|
|
25
|
+
debug = false
|
|
26
|
+
}) {
|
|
27
|
+
useEffect(() => {
|
|
28
|
+
if (typeof window !== "undefined") {
|
|
29
|
+
window.__SITE_KIT_API_URL__ = apiUrl;
|
|
30
|
+
window.__SITE_KIT_API_KEY__ = apiKey;
|
|
31
|
+
window.__SITE_KIT_DEBUG__ = debug;
|
|
32
|
+
}
|
|
33
|
+
configureFormsApi({
|
|
34
|
+
baseUrl: apiUrl,
|
|
35
|
+
apiKey
|
|
36
|
+
});
|
|
37
|
+
}, [apiUrl, apiKey, debug]);
|
|
38
|
+
const contextValue = useMemo(
|
|
39
|
+
() => ({
|
|
40
|
+
apiUrl,
|
|
41
|
+
apiKey,
|
|
42
|
+
analytics,
|
|
43
|
+
engage,
|
|
44
|
+
forms,
|
|
45
|
+
debug,
|
|
46
|
+
isReady: true
|
|
47
|
+
}),
|
|
48
|
+
[apiUrl, apiKey, analytics, engage, forms, debug]
|
|
49
|
+
);
|
|
50
|
+
let content = /* @__PURE__ */ jsx(Fragment, { children });
|
|
51
|
+
if (analytics?.enabled) {
|
|
52
|
+
content = /* @__PURE__ */ jsx(Suspense, { fallback: null, children: /* @__PURE__ */ jsx(
|
|
53
|
+
AnalyticsProvider,
|
|
54
|
+
{
|
|
55
|
+
apiUrl,
|
|
56
|
+
apiKey,
|
|
57
|
+
trackPageViews: analytics.trackPageViews !== false,
|
|
58
|
+
trackWebVitals: analytics.trackWebVitals !== false,
|
|
59
|
+
trackScrollDepth: analytics.trackScrollDepth !== false,
|
|
60
|
+
trackClicks: analytics.trackClicks !== false,
|
|
61
|
+
debug,
|
|
62
|
+
children: content
|
|
63
|
+
}
|
|
64
|
+
) });
|
|
65
|
+
}
|
|
66
|
+
if (engage?.enabled) {
|
|
67
|
+
content = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
68
|
+
content,
|
|
69
|
+
/* @__PURE__ */ jsx(EngageWidget, { apiUrl, apiKey })
|
|
70
|
+
] });
|
|
71
|
+
}
|
|
72
|
+
content = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
73
|
+
content,
|
|
74
|
+
/* @__PURE__ */ jsx(SitemapSync, { debug })
|
|
75
|
+
] });
|
|
76
|
+
return /* @__PURE__ */ jsx(SiteKitContext.Provider, { value: contextValue, children: content });
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// src/affiliates/api.ts
|
|
80
|
+
function getApiConfig() {
|
|
81
|
+
const apiUrl = typeof window !== "undefined" ? window.__SITE_KIT_API_URL__ || "https://api.uptrademedia.com" : "https://api.uptrademedia.com";
|
|
82
|
+
const apiKey = typeof window !== "undefined" ? window.__SITE_KIT_API_KEY__ || "" : "";
|
|
83
|
+
return { apiUrl, apiKey };
|
|
84
|
+
}
|
|
85
|
+
async function apiGet(endpoint) {
|
|
86
|
+
const { apiUrl, apiKey } = getApiConfig();
|
|
87
|
+
if (!apiKey) {
|
|
88
|
+
console.error("[Affiliates] No API key configured");
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
try {
|
|
92
|
+
const response = await fetch(`${apiUrl}${endpoint}`, {
|
|
93
|
+
method: "GET",
|
|
94
|
+
headers: {
|
|
95
|
+
"x-api-key": apiKey
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
if (!response.ok) {
|
|
99
|
+
console.error(`[Affiliates] API error: ${response.statusText}`);
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
return await response.json();
|
|
103
|
+
} catch (error) {
|
|
104
|
+
console.error("[Affiliates] Network error:", error);
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
async function fetchAffiliates(options = {}) {
|
|
109
|
+
const result = await apiGet(
|
|
110
|
+
"/api/public/affiliates"
|
|
111
|
+
);
|
|
112
|
+
return result?.affiliates || [];
|
|
113
|
+
}
|
|
114
|
+
function getTrackingUrl(affiliateId, offerId) {
|
|
115
|
+
const { apiUrl } = getApiConfig();
|
|
116
|
+
return `${apiUrl}/a/${affiliateId}/${offerId}`;
|
|
117
|
+
}
|
|
118
|
+
function useAffiliates() {
|
|
119
|
+
const [affiliates, setAffiliates] = useState([]);
|
|
120
|
+
const [isLoading, setIsLoading] = useState(true);
|
|
121
|
+
const [error, setError] = useState(null);
|
|
122
|
+
useEffect(() => {
|
|
123
|
+
async function load() {
|
|
124
|
+
try {
|
|
125
|
+
setIsLoading(true);
|
|
126
|
+
setError(null);
|
|
127
|
+
const data = await fetchAffiliates();
|
|
128
|
+
setAffiliates(data);
|
|
129
|
+
} catch (err) {
|
|
130
|
+
setError(err instanceof Error ? err.message : "Failed to load affiliates");
|
|
131
|
+
} finally {
|
|
132
|
+
setIsLoading(false);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
load();
|
|
136
|
+
}, []);
|
|
137
|
+
return {
|
|
138
|
+
affiliates,
|
|
139
|
+
isLoading,
|
|
140
|
+
error,
|
|
141
|
+
getTrackingUrl
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
function AffiliateCard({
|
|
145
|
+
affiliate,
|
|
146
|
+
className = "",
|
|
147
|
+
renderLogo,
|
|
148
|
+
showOffers = false
|
|
149
|
+
}) {
|
|
150
|
+
const { getTrackingUrl: getTrackingUrl2 } = useAffiliates();
|
|
151
|
+
const defaultLogo = /* @__PURE__ */ jsx("div", { "data-affiliate-logo": true, className: "affiliate-logo", children: affiliate.logo_url ? /* @__PURE__ */ jsx(
|
|
152
|
+
"img",
|
|
153
|
+
{
|
|
154
|
+
src: affiliate.logo_url,
|
|
155
|
+
alt: affiliate.name,
|
|
156
|
+
loading: "lazy"
|
|
157
|
+
}
|
|
158
|
+
) : /* @__PURE__ */ jsx("span", { children: affiliate.name.charAt(0) }) });
|
|
159
|
+
return /* @__PURE__ */ jsxs("div", { className, "data-affiliate-card": true, "data-affiliate-id": affiliate.id, children: [
|
|
160
|
+
renderLogo ? renderLogo(affiliate) : defaultLogo,
|
|
161
|
+
/* @__PURE__ */ jsx("div", { "data-affiliate-name": true, className: "affiliate-name", children: affiliate.name }),
|
|
162
|
+
affiliate.website_url && /* @__PURE__ */ jsx(
|
|
163
|
+
"a",
|
|
164
|
+
{
|
|
165
|
+
href: affiliate.website_url,
|
|
166
|
+
target: "_blank",
|
|
167
|
+
rel: "noopener noreferrer sponsored",
|
|
168
|
+
"data-affiliate-website": true,
|
|
169
|
+
className: "affiliate-website",
|
|
170
|
+
children: "Visit Website"
|
|
171
|
+
}
|
|
172
|
+
),
|
|
173
|
+
showOffers && affiliate.offers && affiliate.offers.length > 0 && /* @__PURE__ */ jsx("div", { "data-affiliate-offers": true, className: "affiliate-offers", children: affiliate.offers.map((offer) => /* @__PURE__ */ jsx(
|
|
174
|
+
"a",
|
|
175
|
+
{
|
|
176
|
+
href: getTrackingUrl2(affiliate.id, offer.id),
|
|
177
|
+
target: "_blank",
|
|
178
|
+
rel: "noopener noreferrer sponsored",
|
|
179
|
+
"data-affiliate-offer": true,
|
|
180
|
+
"data-offer-id": offer.id,
|
|
181
|
+
className: "affiliate-offer",
|
|
182
|
+
children: offer.name
|
|
183
|
+
},
|
|
184
|
+
offer.id
|
|
185
|
+
)) })
|
|
186
|
+
] });
|
|
187
|
+
}
|
|
188
|
+
function AffiliatesWidget({
|
|
189
|
+
className = "",
|
|
190
|
+
loadingComponent,
|
|
191
|
+
emptyComponent,
|
|
192
|
+
errorComponent,
|
|
193
|
+
showOffers = false,
|
|
194
|
+
renderAffiliate,
|
|
195
|
+
limit
|
|
196
|
+
}) {
|
|
197
|
+
const { affiliates, isLoading, error, getTrackingUrl: getTrackingUrl2 } = useAffiliates();
|
|
198
|
+
if (isLoading) {
|
|
199
|
+
return loadingComponent || /* @__PURE__ */ jsx("div", { "data-affiliates-loading": true, children: "Loading affiliates..." });
|
|
200
|
+
}
|
|
201
|
+
if (error) {
|
|
202
|
+
return errorComponent || /* @__PURE__ */ jsx("div", { "data-affiliates-error": true, children: error });
|
|
203
|
+
}
|
|
204
|
+
const displayAffiliates = limit ? affiliates.slice(0, limit) : affiliates;
|
|
205
|
+
if (displayAffiliates.length === 0) {
|
|
206
|
+
return emptyComponent || null;
|
|
207
|
+
}
|
|
208
|
+
return /* @__PURE__ */ jsx("div", { className, "data-affiliates-widget": true, children: displayAffiliates.map(
|
|
209
|
+
(affiliate) => renderAffiliate ? renderAffiliate(affiliate) : /* @__PURE__ */ jsx(
|
|
210
|
+
AffiliateCard,
|
|
211
|
+
{
|
|
212
|
+
affiliate,
|
|
213
|
+
showOffers
|
|
214
|
+
},
|
|
215
|
+
affiliate.id
|
|
216
|
+
)
|
|
217
|
+
) });
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
export { AffiliateCard, AffiliatesWidget, SiteKitProvider, fetchAffiliates, getTrackingUrl, useAffiliates, useSiteKit };
|
|
221
|
+
//# sourceMappingURL=index.mjs.map
|
|
222
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/SiteKitProvider.tsx","../src/affiliates/api.ts","../src/affiliates/useAffiliates.ts","../src/affiliates/AffiliatesWidget.tsx"],"names":["useEffect","getTrackingUrl","jsx","jsxs"],"mappings":"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","file":"index.mjs","sourcesContent":["/**\n * @uptrade/site-kit - SiteKitProvider\n * \n * Unified provider component that initializes all enabled modules.\n * All API calls go through Portal API with API key auth - never Supabase directly.\n */\n\n'use client'\n\nimport React, { createContext, useContext, useMemo, useEffect, ReactNode, Suspense } from 'react'\nimport type { SiteKitConfig } from './types'\n\n// Module providers\nimport { AnalyticsProvider } from './analytics/AnalyticsProvider'\nimport { EngageWidget } from './engage/EngageWidget'\nimport { configureFormsApi } from './forms/formsApi'\nimport { SitemapSync } from './seo/SitemapSync'\n\ninterface SiteKitContextValue extends SiteKitConfig {\n isReady: boolean\n}\n\nconst SiteKitContext = createContext<SiteKitContextValue | null>(null)\n\nexport function useSiteKit(): SiteKitContextValue {\n const context = useContext(SiteKitContext)\n if (!context) {\n throw new Error('useSiteKit must be used within a SiteKitProvider')\n }\n return context\n}\n\ninterface SiteKitProviderProps extends SiteKitConfig {\n children: ReactNode\n}\n\nexport function SiteKitProvider({\n children,\n apiUrl = 'https://api.uptrademedia.com',\n apiKey,\n analytics,\n engage,\n forms,\n debug = false,\n}: SiteKitProviderProps) {\n // Set window globals for Portal API access\n useEffect(() => {\n if (typeof window !== 'undefined') {\n ;(window as any).__SITE_KIT_API_URL__ = apiUrl\n ;(window as any).__SITE_KIT_API_KEY__ = apiKey\n ;(window as any).__SITE_KIT_DEBUG__ = debug\n }\n \n // Configure forms API\n configureFormsApi({\n baseUrl: apiUrl,\n apiKey,\n })\n }, [apiUrl, apiKey, debug])\n\n const contextValue = useMemo<SiteKitContextValue>(\n () => ({\n apiUrl,\n apiKey,\n analytics,\n engage,\n forms,\n debug,\n isReady: true,\n }),\n [apiUrl, apiKey, analytics, engage, forms, debug]\n )\n\n // Build the provider tree based on enabled modules\n let content = <>{children}</>\n\n // Wrap with Analytics if enabled\n if (analytics?.enabled) {\n content = (\n <Suspense fallback={null}>\n <AnalyticsProvider\n apiUrl={apiUrl}\n apiKey={apiKey}\n trackPageViews={analytics.trackPageViews !== false}\n trackWebVitals={analytics.trackWebVitals !== false}\n trackScrollDepth={analytics.trackScrollDepth !== false}\n trackClicks={analytics.trackClicks !== false}\n debug={debug}\n >\n {content}\n </AnalyticsProvider>\n </Suspense>\n )\n }\n\n // Add Engage widget if enabled (doesn't wrap, just renders alongside)\n if (engage?.enabled) {\n content = (\n <>\n {content}\n <EngageWidget apiUrl={apiUrl} apiKey={apiKey} />\n </>\n )\n }\n\n // Always include SitemapSync to keep seo_pages in sync with sitemap.xml\n // This is the canonical source of truth for what pages exist\n content = (\n <>\n {content}\n <SitemapSync debug={debug} />\n </>\n )\n\n return (\n <SiteKitContext.Provider value={contextValue}>\n {content}\n </SiteKitContext.Provider>\n )\n}\n\n/**\n * Hook to check if a specific module is enabled\n */\nexport function useModuleEnabled(module: 'analytics' | 'engage' | 'forms' | 'seo'): boolean {\n const context = useSiteKit()\n \n switch (module) {\n case 'analytics':\n return context.analytics?.enabled ?? false\n case 'engage':\n return context.engage?.enabled ?? false\n case 'forms':\n return context.forms?.enabled ?? false\n case 'seo':\n return true // SEO is always enabled via RSC components\n default:\n return false\n }\n}\n","/**\n * @uptrade/site-kit/affiliates - Affiliates API\n * \n * API functions for fetching affiliate data for client sites.\n * All data goes through Portal API with API key auth - never Supabase directly.\n */\n\nimport type { Affiliate, AffiliateWithOffers, FetchAffiliatesOptions } from './types'\n\n// ============================================\n// API Config Helpers\n// ============================================\n\nfunction getApiConfig() {\n const apiUrl = typeof window !== 'undefined' \n ? (window as any).__SITE_KIT_API_URL__ || 'https://api.uptrademedia.com'\n : 'https://api.uptrademedia.com'\n const apiKey = typeof window !== 'undefined' \n ? (window as any).__SITE_KIT_API_KEY__ || ''\n : ''\n return { apiUrl, apiKey }\n}\n\nasync function apiGet<T>(endpoint: string): Promise<T | null> {\n const { apiUrl, apiKey } = getApiConfig()\n \n if (!apiKey) {\n console.error('[Affiliates] No API key configured')\n return null\n }\n \n try {\n const response = await fetch(`${apiUrl}${endpoint}`, {\n method: 'GET',\n headers: {\n 'x-api-key': apiKey,\n },\n })\n \n if (!response.ok) {\n console.error(`[Affiliates] API error: ${response.statusText}`)\n return null\n }\n \n return await response.json()\n } catch (error) {\n console.error('[Affiliates] Network error:', error)\n return null\n }\n}\n\n// ============================================\n// Fetch Affiliates\n// ============================================\n\n/**\n * Fetch all active affiliates for the project\n * \n * @example\n * ```ts\n * const affiliates = await fetchAffiliates()\n * ```\n */\nexport async function fetchAffiliates(\n options: FetchAffiliatesOptions = {}\n): Promise<AffiliateWithOffers[]> {\n const result = await apiGet<{ affiliates: AffiliateWithOffers[] }>(\n '/api/public/affiliates'\n )\n \n return result?.affiliates || []\n}\n\n/**\n * Build a tracking URL for an affiliate offer\n * When clicked, this redirects through our tracking endpoint and then to the destination\n * \n * @example\n * ```ts\n * const trackingUrl = getTrackingUrl(affiliate.id, offer.id)\n * // Returns: https://api.uptrademedia.com/a/{affiliateId}/{offerId}\n * ```\n */\nexport function getTrackingUrl(affiliateId: string, offerId: string): string {\n const { apiUrl } = getApiConfig()\n return `${apiUrl}/a/${affiliateId}/${offerId}`\n}\n","/**\n * @uptrade/site-kit/affiliates - useAffiliates Hook\n * \n * React hook for fetching and displaying affiliates on client sites.\n */\n\n'use client'\n\nimport { useState, useEffect } from 'react'\nimport type { AffiliateWithOffers } from './types'\nimport { fetchAffiliates, getTrackingUrl } from './api'\n\nexport interface UseAffiliatesResult {\n affiliates: AffiliateWithOffers[]\n isLoading: boolean\n error: string | null\n getTrackingUrl: (affiliateId: string, offerId: string) => string\n}\n\n/**\n * Hook to fetch affiliates for display on client sites\n * \n * @example\n * ```tsx\n * function AffiliatesSection() {\n * const { affiliates, isLoading } = useAffiliates()\n * \n * if (isLoading) return <div>Loading...</div>\n * \n * return (\n * <div className=\"grid grid-cols-3 gap-4\">\n * {affiliates.map(affiliate => (\n * <AffiliateCard key={affiliate.id} affiliate={affiliate} />\n * ))}\n * </div>\n * )\n * }\n * ```\n */\nexport function useAffiliates(): UseAffiliatesResult {\n const [affiliates, setAffiliates] = useState<AffiliateWithOffers[]>([])\n const [isLoading, setIsLoading] = useState(true)\n const [error, setError] = useState<string | null>(null)\n\n useEffect(() => {\n async function load() {\n try {\n setIsLoading(true)\n setError(null)\n const data = await fetchAffiliates()\n setAffiliates(data)\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to load affiliates')\n } finally {\n setIsLoading(false)\n }\n }\n \n load()\n }, [])\n\n return {\n affiliates,\n isLoading,\n error,\n getTrackingUrl,\n }\n}\n","/**\n * @uptrade/site-kit/affiliates - AffiliatesWidget Component\n * \n * Unstyled, headless component for displaying affiliates.\n * Client sites can wrap this or use it directly with their own styling.\n */\n\n'use client'\n\nimport React from 'react'\nimport { useAffiliates } from './useAffiliates'\nimport type { AffiliateWithOffers, AffiliateOffer } from './types'\n\n// ============================================\n// Affiliate Card (Unstyled)\n// ============================================\n\nexport interface AffiliateCardProps {\n affiliate: AffiliateWithOffers\n className?: string\n /** Optional custom render for the logo */\n renderLogo?: (affiliate: AffiliateWithOffers) => React.ReactNode\n /** Show offers as links below the card */\n showOffers?: boolean\n}\n\n/**\n * Single affiliate card - unstyled, bring your own CSS\n * \n * @example\n * ```tsx\n * <AffiliateCard \n * affiliate={affiliate}\n * className=\"p-4 bg-white rounded-lg shadow\"\n * showOffers\n * />\n * ```\n */\nexport function AffiliateCard({\n affiliate,\n className = '',\n renderLogo,\n showOffers = false,\n}: AffiliateCardProps) {\n const { getTrackingUrl } = useAffiliates()\n \n const defaultLogo = (\n <div data-affiliate-logo className=\"affiliate-logo\">\n {affiliate.logo_url ? (\n <img \n src={affiliate.logo_url} \n alt={affiliate.name}\n loading=\"lazy\"\n />\n ) : (\n <span>{affiliate.name.charAt(0)}</span>\n )}\n </div>\n )\n\n return (\n <div className={className} data-affiliate-card data-affiliate-id={affiliate.id}>\n {/* Logo */}\n {renderLogo ? renderLogo(affiliate) : defaultLogo}\n \n {/* Name */}\n <div data-affiliate-name className=\"affiliate-name\">\n {affiliate.name}\n </div>\n \n {/* Website link */}\n {affiliate.website_url && (\n <a \n href={affiliate.website_url}\n target=\"_blank\"\n rel=\"noopener noreferrer sponsored\"\n data-affiliate-website\n className=\"affiliate-website\"\n >\n Visit Website\n </a>\n )}\n \n {/* Offers */}\n {showOffers && affiliate.offers && affiliate.offers.length > 0 && (\n <div data-affiliate-offers className=\"affiliate-offers\">\n {affiliate.offers.map(offer => (\n <a\n key={offer.id}\n href={getTrackingUrl(affiliate.id, offer.id)}\n target=\"_blank\"\n rel=\"noopener noreferrer sponsored\"\n data-affiliate-offer\n data-offer-id={offer.id}\n className=\"affiliate-offer\"\n >\n {offer.name}\n </a>\n ))}\n </div>\n )}\n </div>\n )\n}\n\n// ============================================\n// Affiliates Grid (Unstyled)\n// ============================================\n\nexport interface AffiliatesWidgetProps {\n className?: string\n /** Custom loading component */\n loadingComponent?: React.ReactNode\n /** Custom empty state component */\n emptyComponent?: React.ReactNode\n /** Custom error component */\n errorComponent?: React.ReactNode\n /** Show offers on each card */\n showOffers?: boolean\n /** Custom render function for each affiliate */\n renderAffiliate?: (affiliate: AffiliateWithOffers) => React.ReactNode\n /** Maximum number to display */\n limit?: number\n}\n\n/**\n * Display a grid of affiliates - unstyled, bring your own CSS\n * \n * @example\n * ```tsx\n * // Basic usage\n * <AffiliatesWidget className=\"grid grid-cols-3 gap-4\" />\n * \n * // With custom rendering\n * <AffiliatesWidget\n * renderAffiliate={(affiliate) => (\n * <MyCustomCard key={affiliate.id} data={affiliate} />\n * )}\n * />\n * ```\n */\nexport function AffiliatesWidget({\n className = '',\n loadingComponent,\n emptyComponent,\n errorComponent,\n showOffers = false,\n renderAffiliate,\n limit,\n}: AffiliatesWidgetProps) {\n const { affiliates, isLoading, error, getTrackingUrl } = useAffiliates()\n \n if (isLoading) {\n return loadingComponent || <div data-affiliates-loading>Loading affiliates...</div>\n }\n \n if (error) {\n return errorComponent || <div data-affiliates-error>{error}</div>\n }\n \n const displayAffiliates = limit ? affiliates.slice(0, limit) : affiliates\n \n if (displayAffiliates.length === 0) {\n return emptyComponent || null\n }\n \n return (\n <div className={className} data-affiliates-widget>\n {displayAffiliates.map(affiliate => \n renderAffiliate \n ? renderAffiliate(affiliate)\n : <AffiliateCard \n key={affiliate.id} \n affiliate={affiliate} \n showOffers={showOffers}\n />\n )}\n </div>\n )\n}\n"]}
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
import './chunk-NYKRE2FL.mjs';
|
|
2
|
+
import fs from 'fs/promises';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
|
|
5
|
+
async function migrateFiles(scanResults, options) {
|
|
6
|
+
const results = [];
|
|
7
|
+
for (const form of scanResults.forms) {
|
|
8
|
+
if (form.suggestedAction === "manual") {
|
|
9
|
+
results.push({
|
|
10
|
+
filePath: form.filePath,
|
|
11
|
+
success: false,
|
|
12
|
+
changes: [],
|
|
13
|
+
error: "Form too complex for auto-migration"
|
|
14
|
+
});
|
|
15
|
+
continue;
|
|
16
|
+
}
|
|
17
|
+
try {
|
|
18
|
+
const result = await migrateForm(form, options);
|
|
19
|
+
results.push(result);
|
|
20
|
+
} catch (error) {
|
|
21
|
+
results.push({
|
|
22
|
+
filePath: form.filePath,
|
|
23
|
+
success: false,
|
|
24
|
+
changes: [],
|
|
25
|
+
error: error.message
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
for (const widget of scanResults.widgets) {
|
|
30
|
+
try {
|
|
31
|
+
const result = await migrateWidget(widget.filePath, widget, options);
|
|
32
|
+
results.push(result);
|
|
33
|
+
} catch (error) {
|
|
34
|
+
results.push({
|
|
35
|
+
filePath: widget.filePath,
|
|
36
|
+
success: false,
|
|
37
|
+
changes: [],
|
|
38
|
+
error: error.message
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return results;
|
|
43
|
+
}
|
|
44
|
+
async function migrateForm(form, options) {
|
|
45
|
+
const changes = [];
|
|
46
|
+
const fullPath = path.resolve(process.cwd(), form.filePath);
|
|
47
|
+
const formSlug = generateSlug(form.componentName);
|
|
48
|
+
const formId = await createFormInUptrade(form, formSlug, options);
|
|
49
|
+
changes.push(`Created managed form: ${formSlug}`);
|
|
50
|
+
if (options.dryRun) {
|
|
51
|
+
changes.push("[DRY RUN] Would update file");
|
|
52
|
+
return { filePath: form.filePath, success: true, changes, formId };
|
|
53
|
+
}
|
|
54
|
+
await fs.readFile(fullPath, "utf-8");
|
|
55
|
+
const newCode = generateMigratedFormCode(form, formSlug);
|
|
56
|
+
await fs.writeFile(fullPath, newCode, "utf-8");
|
|
57
|
+
changes.push("Updated component to use useForm hook");
|
|
58
|
+
return {
|
|
59
|
+
filePath: form.filePath,
|
|
60
|
+
success: true,
|
|
61
|
+
changes,
|
|
62
|
+
formId
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
function generateMigratedFormCode(form, formSlug) {
|
|
66
|
+
const componentName = form.componentName || "MigratedForm";
|
|
67
|
+
return `/**
|
|
68
|
+
* ${componentName}
|
|
69
|
+
*
|
|
70
|
+
* Migrated to @uptrade/site-kit
|
|
71
|
+
* Managed form: ${formSlug}
|
|
72
|
+
*/
|
|
73
|
+
|
|
74
|
+
'use client'
|
|
75
|
+
|
|
76
|
+
import { useForm } from '@uptrade/site-kit/forms'
|
|
77
|
+
|
|
78
|
+
export function ${componentName}({ className }: { className?: string }) {
|
|
79
|
+
const {
|
|
80
|
+
form,
|
|
81
|
+
fields,
|
|
82
|
+
values,
|
|
83
|
+
errors,
|
|
84
|
+
setFieldValue,
|
|
85
|
+
submit,
|
|
86
|
+
isSubmitting,
|
|
87
|
+
isComplete
|
|
88
|
+
} = useForm('${formSlug}')
|
|
89
|
+
|
|
90
|
+
if (isComplete) {
|
|
91
|
+
return (
|
|
92
|
+
<div className={className}>
|
|
93
|
+
<p className="text-green-600">{form?.successMessage || 'Thanks for your submission!'}</p>
|
|
94
|
+
</div>
|
|
95
|
+
)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return (
|
|
99
|
+
<form
|
|
100
|
+
onSubmit={(e) => { e.preventDefault(); submit() }}
|
|
101
|
+
className={className}
|
|
102
|
+
>
|
|
103
|
+
{fields.map(field => (
|
|
104
|
+
<div key={field.slug} className="mb-4">
|
|
105
|
+
<label className="block text-sm font-medium mb-1">
|
|
106
|
+
{field.label}
|
|
107
|
+
{field.isRequired && <span className="text-red-500 ml-1">*</span>}
|
|
108
|
+
</label>
|
|
109
|
+
|
|
110
|
+
{field.fieldType === 'textarea' ? (
|
|
111
|
+
<textarea
|
|
112
|
+
name={field.slug}
|
|
113
|
+
placeholder={field.placeholder}
|
|
114
|
+
value={String(values[field.slug] || '')}
|
|
115
|
+
onChange={(e) => setFieldValue(field.slug, e.target.value)}
|
|
116
|
+
className="w-full p-2 border rounded focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
|
117
|
+
rows={4}
|
|
118
|
+
/>
|
|
119
|
+
) : field.fieldType === 'select' && field.options ? (
|
|
120
|
+
<select
|
|
121
|
+
name={field.slug}
|
|
122
|
+
value={String(values[field.slug] || '')}
|
|
123
|
+
onChange={(e) => setFieldValue(field.slug, e.target.value)}
|
|
124
|
+
className="w-full p-2 border rounded focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
|
125
|
+
>
|
|
126
|
+
<option value="">Select...</option>
|
|
127
|
+
{field.options.map(opt => (
|
|
128
|
+
<option key={opt.value} value={opt.value}>{opt.label}</option>
|
|
129
|
+
))}
|
|
130
|
+
</select>
|
|
131
|
+
) : (
|
|
132
|
+
<input
|
|
133
|
+
type={field.fieldType}
|
|
134
|
+
name={field.slug}
|
|
135
|
+
placeholder={field.placeholder}
|
|
136
|
+
value={String(values[field.slug] || '')}
|
|
137
|
+
onChange={(e) => setFieldValue(field.slug, e.target.value)}
|
|
138
|
+
className="w-full p-2 border rounded focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
|
139
|
+
/>
|
|
140
|
+
)}
|
|
141
|
+
|
|
142
|
+
{errors[field.slug] && (
|
|
143
|
+
<p className="mt-1 text-sm text-red-500">{errors[field.slug]}</p>
|
|
144
|
+
)}
|
|
145
|
+
|
|
146
|
+
{field.helpText && (
|
|
147
|
+
<p className="mt-1 text-xs text-gray-500">{field.helpText}</p>
|
|
148
|
+
)}
|
|
149
|
+
</div>
|
|
150
|
+
))}
|
|
151
|
+
|
|
152
|
+
<button
|
|
153
|
+
type="submit"
|
|
154
|
+
disabled={isSubmitting}
|
|
155
|
+
className="w-full py-2 px-4 bg-blue-600 text-white rounded hover:bg-blue-700 disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
|
|
156
|
+
>
|
|
157
|
+
{isSubmitting ? 'Submitting...' : (form?.submitButtonText || 'Submit')}
|
|
158
|
+
</button>
|
|
159
|
+
</form>
|
|
160
|
+
)
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
export default ${componentName}
|
|
164
|
+
`;
|
|
165
|
+
}
|
|
166
|
+
async function createFormInUptrade(form, slug, options) {
|
|
167
|
+
const response = await fetch("https://api.uptrademedia.com/forms", {
|
|
168
|
+
method: "POST",
|
|
169
|
+
headers: {
|
|
170
|
+
"Content-Type": "application/json",
|
|
171
|
+
"Authorization": `Bearer ${options.apiKey}`
|
|
172
|
+
},
|
|
173
|
+
body: JSON.stringify({
|
|
174
|
+
projectId: options.projectId,
|
|
175
|
+
slug,
|
|
176
|
+
name: formatName(form.componentName),
|
|
177
|
+
formType: detectFormType(form),
|
|
178
|
+
successMessage: "Thanks for your submission!",
|
|
179
|
+
submitButtonText: "Submit",
|
|
180
|
+
fields: form.fields.map((f, i) => ({
|
|
181
|
+
slug: f.name,
|
|
182
|
+
label: formatLabel(f.name),
|
|
183
|
+
fieldType: mapFieldType(f.type),
|
|
184
|
+
placeholder: f.placeholder,
|
|
185
|
+
isRequired: f.required,
|
|
186
|
+
sortOrder: i,
|
|
187
|
+
width: "full"
|
|
188
|
+
}))
|
|
189
|
+
})
|
|
190
|
+
});
|
|
191
|
+
if (!response.ok) {
|
|
192
|
+
const error = await response.json();
|
|
193
|
+
throw new Error(error.message || "Failed to create form");
|
|
194
|
+
}
|
|
195
|
+
const data = await response.json();
|
|
196
|
+
return data.id;
|
|
197
|
+
}
|
|
198
|
+
async function migrateWidget(filePath, widget, options) {
|
|
199
|
+
const changes = [];
|
|
200
|
+
if (options.dryRun) {
|
|
201
|
+
changes.push(`[DRY RUN] Would remove ${widget.widgetType} script`);
|
|
202
|
+
changes.push("[DRY RUN] Would enable Engage in SiteKitProvider");
|
|
203
|
+
return { filePath, success: true, changes };
|
|
204
|
+
}
|
|
205
|
+
const fullPath = path.resolve(process.cwd(), filePath);
|
|
206
|
+
let content = await fs.readFile(fullPath, "utf-8");
|
|
207
|
+
switch (widget.widgetType) {
|
|
208
|
+
case "intercom":
|
|
209
|
+
content = content.replace(/<Script[^>]*intercom[^>]*\/?>(?:<\/Script>)?/gi, "{/* Intercom replaced with Uptrade Engage */}");
|
|
210
|
+
content = content.replace(/window\.Intercom\s*=\s*[^;]+;/g, "");
|
|
211
|
+
break;
|
|
212
|
+
case "crisp":
|
|
213
|
+
content = content.replace(/<Script[^>]*crisp[^>]*\/?>(?:<\/Script>)?/gi, "{/* Crisp replaced with Uptrade Engage */}");
|
|
214
|
+
break;
|
|
215
|
+
case "drift":
|
|
216
|
+
content = content.replace(/<Script[^>]*drift[^>]*\/?>(?:<\/Script>)?/gi, "{/* Drift replaced with Uptrade Engage */}");
|
|
217
|
+
break;
|
|
218
|
+
}
|
|
219
|
+
await fs.writeFile(fullPath, content, "utf-8");
|
|
220
|
+
changes.push(`Removed ${widget.widgetType} script`);
|
|
221
|
+
changes.push("Enable Engage in SiteKitProvider to add chat widget");
|
|
222
|
+
return { filePath, success: true, changes };
|
|
223
|
+
}
|
|
224
|
+
function generateSlug(name) {
|
|
225
|
+
return name.replace(/([A-Z])/g, "-$1").toLowerCase().replace(/^-/, "").replace(/form$/i, "").replace(/-+/g, "-").replace(/-$/, "") || "form";
|
|
226
|
+
}
|
|
227
|
+
function formatName(name) {
|
|
228
|
+
return name.replace(/([A-Z])/g, " $1").replace(/^./, (str) => str.toUpperCase()).trim();
|
|
229
|
+
}
|
|
230
|
+
function formatLabel(name) {
|
|
231
|
+
return name.replace(/([A-Z])/g, " $1").replace(/_/g, " ").replace(/^./, (str) => str.toUpperCase()).trim();
|
|
232
|
+
}
|
|
233
|
+
function mapFieldType(type) {
|
|
234
|
+
const mapping = {
|
|
235
|
+
"text": "text",
|
|
236
|
+
"email": "email",
|
|
237
|
+
"tel": "phone",
|
|
238
|
+
"phone": "phone",
|
|
239
|
+
"number": "number",
|
|
240
|
+
"textarea": "textarea",
|
|
241
|
+
"select": "select",
|
|
242
|
+
"checkbox": "checkbox",
|
|
243
|
+
"radio": "radio",
|
|
244
|
+
"date": "date",
|
|
245
|
+
"file": "file",
|
|
246
|
+
"url": "url",
|
|
247
|
+
"password": "text"
|
|
248
|
+
// Don't use password for managed forms
|
|
249
|
+
};
|
|
250
|
+
return mapping[type] || "text";
|
|
251
|
+
}
|
|
252
|
+
function detectFormType(form) {
|
|
253
|
+
const name = form.componentName.toLowerCase();
|
|
254
|
+
const fields = form.fields.map((f) => f.name.toLowerCase()).join(" ");
|
|
255
|
+
if (name.includes("contact") || fields.includes("message")) return "contact";
|
|
256
|
+
if (name.includes("newsletter") || name.includes("subscribe")) return "newsletter";
|
|
257
|
+
if (name.includes("quote") || name.includes("estimate")) return "prospect";
|
|
258
|
+
if (name.includes("support") || name.includes("help")) return "support";
|
|
259
|
+
if (name.includes("feedback")) return "feedback";
|
|
260
|
+
return "contact";
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
export { migrateFiles };
|
|
264
|
+
//# sourceMappingURL=migrator-V6KS75EA.mjs.map
|
|
265
|
+
//# sourceMappingURL=migrator-V6KS75EA.mjs.map
|