keystone-design-bootstrap 1.0.41 → 1.0.43

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.
@@ -0,0 +1,88 @@
1
+ interface Service {
2
+ id: number;
3
+ name: string;
4
+ slug: string;
5
+ description_markdown: string;
6
+ summary?: string;
7
+ pricing_info?: string;
8
+ features_markdown?: string;
9
+ featured: boolean;
10
+ sort_order: number;
11
+ photo_attachments?: Array<{
12
+ id: number;
13
+ featured: boolean;
14
+ attachable_id?: number;
15
+ attachable_type?: string;
16
+ photo?: {
17
+ id: number;
18
+ title: string;
19
+ thumbnail_url?: string;
20
+ medium_url?: string;
21
+ large_url?: string;
22
+ original_url?: string;
23
+ };
24
+ }>;
25
+ created_at: string;
26
+ updated_at: string;
27
+ }
28
+ interface ServiceParams {
29
+ featured?: boolean;
30
+ q?: string;
31
+ page?: number;
32
+ per_page?: number;
33
+ }
34
+ type ServiceResponse = Service[];
35
+
36
+ /**
37
+ * Photo and PhotoAttachment type definitions
38
+ */
39
+ interface Photo {
40
+ id: number;
41
+ title: string;
42
+ description?: string;
43
+ alt_text?: string;
44
+ thumbnail_url?: string;
45
+ medium_url?: string;
46
+ large_url?: string;
47
+ original_url?: string;
48
+ generated: boolean;
49
+ created_at: string;
50
+ updated_at: string;
51
+ }
52
+ interface PhotoAttachment {
53
+ id: number;
54
+ photo: Photo;
55
+ featured: boolean;
56
+ sort_order: number;
57
+ created_at: string;
58
+ updated_at: string;
59
+ }
60
+
61
+ /**
62
+ * Form definition from API (GET /public/forms/:form_type).
63
+ * fields is an ordered array; each item is a single field or an array of fields (inline row). Max depth 2.
64
+ */
65
+ interface FormFieldDefinition {
66
+ name: string;
67
+ type: string;
68
+ label: string;
69
+ required?: boolean;
70
+ placeholder?: string;
71
+ /** For hidden fields, optional value to submit. */
72
+ value?: string;
73
+ }
74
+ /** One item in the fields array: either a single field or a row of fields (inline). */
75
+ type FormFieldItem = FormFieldDefinition | FormFieldDefinition[];
76
+ interface FormDefinition {
77
+ id: number;
78
+ name: string;
79
+ form_type: string;
80
+ /** Ordered array; each element is a field or an array of fields (inline row). */
81
+ fields: FormFieldItem[];
82
+ settings?: Record<string, unknown>;
83
+ created_at?: string;
84
+ updated_at?: string;
85
+ }
86
+ type FormType = 'lead' | 'job_application';
87
+
88
+ export type { FormDefinition as F, Photo as P, Service as S, PhotoAttachment as a, FormFieldDefinition as b, FormFieldItem as c, FormType as d, ServiceParams as e, ServiceResponse as f };
@@ -0,0 +1,76 @@
1
+ export { AboutHome, BlogCardFullWidthHorizontal, BlogCardFullWidthHorizontalAlt, BlogCardFullWidthHorizontalCompact, BlogCardFullWidthHorizontalMinimal, BlogCardFullWidthVertical, BlogCardFullWidthVerticalAlt, BlogCardFullWidthVerticalCompact, BlogCardFullWidthVerticalMinimal, BlogCardHorizontal, BlogCardHorizontalBadge, BlogCardHorizontalCompact, BlogCardHorizontalMinimal, BlogCardVertical, BlogCardVerticalBadge, BlogCardVerticalCompact, BlogCardVerticalMinimal, BlogGallery, BlogHome, BlogPostSection, BlogSection, ContactHome, ContactSection, FAQGrid, FAQHero, FAQHome, FeatureTabHorizontal, FeatureTabVertical, FeatureTextCentered, FeatureTextFeaturedIconBox, FeatureTextFeaturedIconCard, FeatureTextFeaturedIconLeft, FeatureTextFeaturedIconTopCentered, FeatureTextFeaturedIconTopCenteredBrand, FeatureTextFeaturedIconTopLeft, FeatureTextFeaturedIconTopLeftBrand, FeatureTextIconCard, FeatureTextIconLeft, FeatureTextIconTopCentered, FeatureTextIconTopLeft, FeatureTextIntegrationIconBox, FeatureTextIntegrationIconLeft, FeatureTextIntegrationIconTopCentered, FeatureTextIntegrationIconTopLeft, FeatureTextLeft, FeatureTextLeftBrand, FooterHome, GenericHeaderComponent, GenericTextHero, HeaderNavigation, HeroHome, HomeHeroComponent, JobApplicationForm, JobDetailHero, JobDetailSection, JobGallery, LocationDetailHero, LocationDetailsSection, LocationGrid, PolicyDocumentSection, ServiceDetailContent, ServiceDetailHero, ServicesGrid, ServicesHome, SocialMediaGrid, SocialMediaHero, StatisticsSection, TeamGrid, TestimonialsGrid, TestimonialsHero, TestimonialsHome, ValuesSection, getBlogPostData } from './design_system/sections/index.js';
2
+ export { Avatar, AvatarLabelGroup, Badge, BadgeGroup, BadgeWithDot, Breadcrumb, Button, ButtonGroup, Carousel, CarouselContext, CarouselSectionWrapper, ComboBox, FeaturedIcon, Form, FormContainer, GoogleMap, HintText, Input, InputBase, InputGroup, Label, MarkdownRenderer, NativeSelect, Pagination, PaginationPageDefault, PaginationPageMinimalCenter, PhotoWithFallback, PrivacyCheckbox, RatingBadge, RatingStars, RoundButton, Select, SelectItem, SocialIcon, StarIcon, Textarea, Tooltip, VerifiedTick, VideoModal, VideoPlayButton, Wreath, getSocialIcon, getStarProgress, useCarousel } from './design_system/elements/index.js';
3
+ import { C as CompanyInformation } from './company-information-C_k_sLSB.js';
4
+ export { ThemeProvider, useTheme } from './contexts/index.js';
5
+ import React__default from 'react';
6
+ export { Theme } from './themes/index.js';
7
+ import './form-CWXC-IHT.js';
8
+ import './blog-post-CvRhU9ss.js';
9
+ import 'react-aria-components';
10
+ import 'embla-carousel-react';
11
+
12
+ /**
13
+ * Server Actions for form submissions
14
+ * These run on the server and can safely use API_KEY without exposing it to the browser
15
+ */
16
+ interface ContactFormResult {
17
+ success: boolean;
18
+ message?: string;
19
+ error?: string;
20
+ /** Present on lead form success; use for Meta Pixel dedup: fbq('track', 'Lead', {}, { eventID: eventId }) */
21
+ eventId?: string;
22
+ }
23
+ /**
24
+ * Submit contact form via Server Action
25
+ */
26
+ declare function submitContactFormAction(formData: FormData): Promise<ContactFormResult>;
27
+ /**
28
+ * Submit lead form via Server Action
29
+ */
30
+ declare function submitLeadFormAction(formData: FormData): Promise<ContactFormResult>;
31
+
32
+ /**
33
+ * Single place that defines default primary and secondary CTA URLs.
34
+ * Primary = external management (booking) URL when present, else /contact.
35
+ * Secondary = /contact.
36
+ */
37
+
38
+ /** True when the href is absolute (external); use for target="_blank" and rel="noopener noreferrer". */
39
+ declare function isExternalCtaUrl(href: string): boolean;
40
+ interface CtaUrlOverrides {
41
+ primaryHrefOverride?: string | null;
42
+ secondaryHrefOverride?: string | null;
43
+ }
44
+ interface CtaButtonLike {
45
+ href?: string | null;
46
+ secondary_href?: string | null;
47
+ }
48
+ interface ResolvedCtaUrls {
49
+ primaryHref: string;
50
+ secondaryHref: string;
51
+ hasSecondary: boolean;
52
+ }
53
+ /**
54
+ * Resolve primary and secondary CTA hrefs from company info and optional overrides.
55
+ * - primaryHref = external_management_url (if set) else /contact
56
+ * - secondaryHref = /contact
57
+ * Overrides win when provided.
58
+ */
59
+ declare function resolveCtaUrls(companyInformation?: CompanyInformation | null, _ctaButton?: CtaButtonLike | null, overrides?: CtaUrlOverrides | null): ResolvedCtaUrls;
60
+
61
+ interface TeamMember {
62
+ id: number;
63
+ name: string;
64
+ position?: string;
65
+ photo_url?: string;
66
+ }
67
+ interface ChatWidgetProps {
68
+ position?: 'bottom-right' | 'bottom-left';
69
+ primaryColor?: string;
70
+ sessionId?: string;
71
+ displayName?: string;
72
+ teamMembers?: TeamMember[];
73
+ }
74
+ declare function ChatWidget({ position, primaryColor, sessionId: providedSessionId, displayName: providedDisplayName, teamMembers }: ChatWidgetProps): React__default.JSX.Element;
75
+
76
+ export { ChatWidget, type CtaUrlOverrides, type ResolvedCtaUrls, isExternalCtaUrl, resolveCtaUrls, submitContactFormAction, submitLeadFormAction };
@@ -0,0 +1,13 @@
1
+ import React__default from 'react';
2
+ import { Theme } from '../themes/index.js';
3
+
4
+ /**
5
+ * Component Registry
6
+ * Runtime registry for component theme variants
7
+ */
8
+
9
+ declare function registerThemeVariant<P = unknown>(componentName: string, theme: string, component: React__default.ComponentType<P>): void;
10
+ declare function getThemedComponent(componentName: string, theme?: Theme): React__default.ComponentType<unknown>;
11
+ declare function getRegistry(): Record<string, string[]>;
12
+
13
+ export { getRegistry, getThemedComponent, registerThemeVariant };
@@ -0,0 +1,64 @@
1
+ import { RefObject } from '@react-types/shared';
2
+
3
+ /**
4
+ * Checks whether a particular Tailwind CSS viewport size applies.
5
+ *
6
+ * @param size The size to check, which must either be included in Tailwind CSS's
7
+ * list of default screen sizes, or added to the Tailwind CSS config file.
8
+ *
9
+ * @returns A boolean indicating whether the viewport size applies.
10
+ */
11
+ declare const useBreakpoint: (size: "sm" | "md" | "lg" | "xl" | "2xl") => boolean;
12
+
13
+ type UseClipboardReturnType = {
14
+ /**
15
+ * The state indicating whether the text has been copied.
16
+ * If a string is provided, it will be used as the identifier for the copied state.
17
+ */
18
+ copied: string | boolean;
19
+ /**
20
+ * Function to copy text to the clipboard using the modern clipboard API.
21
+ * Falls back to the fallback function if the modern API fails.
22
+ *
23
+ * @param {string} text - The text to be copied.
24
+ * @param {string} [id] - Optional identifier to set the copied state.
25
+ * @returns {Promise<Object>} - A promise that resolves to an object containing:
26
+ * - `success` (boolean): Whether the copy operation was successful.
27
+ * - `error` (Error | undefined): The error object if the copy operation failed.
28
+ */
29
+ copy: (text: string, id?: string) => Promise<{
30
+ success: boolean;
31
+ error?: Error;
32
+ }>;
33
+ };
34
+ /**
35
+ * Custom hook to copy text to the clipboard.
36
+ *
37
+ * @returns {UseClipboardReturnType} - An object containing the copied state and the copy function.
38
+ */
39
+ declare const useClipboard: () => UseClipboardReturnType;
40
+
41
+ /**
42
+ * The options for the useResizeObserver hook.
43
+ */
44
+ type useResizeObserverOptionsType<T> = {
45
+ /**
46
+ * The ref to the element to observe.
47
+ */
48
+ ref: RefObject<T | undefined | null> | undefined;
49
+ /**
50
+ * The box to observe.
51
+ */
52
+ box?: ResizeObserverBoxOptions;
53
+ /**
54
+ * The callback function to call when the size changes.
55
+ */
56
+ onResize: () => void;
57
+ };
58
+ /**
59
+ * A hook that observes the size of an element and calls a callback function when the size changes.
60
+ * @param options - The options for the hook.
61
+ */
62
+ declare function useResizeObserver<T extends Element>(options: useResizeObserverOptionsType<T>): void;
63
+
64
+ export { useBreakpoint, useClipboard, useResizeObserver };
@@ -0,0 +1,41 @@
1
+ import { S as Service, F as FormDefinition } from '../form-CWXC-IHT.js';
2
+ import { C as CompanyInformation } from '../company-information-C_k_sLSB.js';
3
+ import { W as WebsitePhotos } from '../website-photos-_n2g24IM.js';
4
+
5
+ interface FetchOptions {
6
+ cache?: RequestCache;
7
+ revalidate?: number;
8
+ }
9
+ /**
10
+ * Generic serverApi object for flexible endpoint access
11
+ */
12
+ declare const serverApi: {
13
+ get: <T = unknown>(endpoint: string, options?: FetchOptions) => Promise<T | null>;
14
+ };
15
+ declare function getCompanyInformation(): Promise<CompanyInformation | null>;
16
+ /** Ads config (e.g. Meta Pixel). Returns { meta_pixel_id?: string } or {}. Only present when account has connected Meta Ads. */
17
+ declare function getAdsConfig(): Promise<{
18
+ meta_pixel_id?: string;
19
+ } | null>;
20
+ /** Extract Meta Pixel ID from ads config for use with <MetaPixel pixelId={...} />. Only returns a value when present and valid (numeric). */
21
+ declare function getMetaPixelId(adsConfig: {
22
+ meta_pixel_id?: string;
23
+ } | null | undefined): string | null;
24
+ declare function getServices(): Promise<unknown>;
25
+ declare function getService(slug: string): Promise<Service | null>;
26
+ declare function getLocations(): Promise<unknown>;
27
+ declare function getLocation(slug: string): Promise<unknown>;
28
+ declare function getReviews(): Promise<unknown>;
29
+ declare function getFAQs(): Promise<unknown>;
30
+ declare function getBlogPosts(): Promise<unknown>;
31
+ declare function getBlogPost(slug: string): Promise<unknown>;
32
+ declare function getTeamMembers(): Promise<unknown>;
33
+ declare function getWebsitePhotos(): Promise<WebsitePhotos | null>;
34
+ declare function getJobPostings(): Promise<unknown>;
35
+ declare function getJobPosting(slug: string): Promise<unknown>;
36
+ declare function getSocialPosts(): Promise<unknown>;
37
+ declare function getTestimonials(): Promise<unknown>;
38
+ /** Form definition for dynamic form rendering (fields may include optional placeholder). */
39
+ declare function getForm(formType: string): Promise<FormDefinition | null>;
40
+
41
+ export { getAdsConfig, getBlogPost, getBlogPosts, getCompanyInformation, getFAQs, getForm, getJobPosting, getJobPostings, getLocation, getLocations, getMetaPixelId, getReviews, getService, getServices, getSocialPosts, getTeamMembers, getTestimonials, getWebsitePhotos, serverApi };
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/lib/server-api.ts"],"sourcesContent":["/**\n * Server-side API client for SSR\n * API key is stored securely on the server - never exposed to browser\n */\n\nimport type { CompanyInformation } from '../types/api/company-information';\nimport type { Service } from '../types/api/service';\n\nconst API_URL = process.env.API_URL || 'http://localhost:3000/api/v1';\nconst API_KEY = process.env.API_KEY || '';\n\ninterface FetchOptions {\n cache?: RequestCache;\n revalidate?: number;\n}\n\nasync function serverFetch<T>(endpoint: string, options: FetchOptions = {}): Promise<T | null> {\n const url = `${API_URL}${endpoint}`;\n \n try {\n const fetchOptions: RequestInit & { next?: { revalidate?: number } } = {\n headers: {\n 'X-API-Key': API_KEY,\n 'Content-Type': 'application/json',\n },\n cache: options.cache,\n };\n \n if (options.revalidate) {\n fetchOptions.next = { revalidate: options.revalidate };\n }\n \n const response = await fetch(url, fetchOptions);\n\n if (!response.ok) {\n console.error(`[Server API] Error ${response.status} for ${endpoint}`);\n return null;\n }\n\n const json = await response.json();\n \n // Rails API returns { data: [...], meta: {...} }\n return json.data ?? json;\n } catch (error) {\n console.error(`[Server API] Failed to fetch ${endpoint}:`, error);\n return null;\n }\n}\n\n/**\n * Generic serverApi object for flexible endpoint access\n */\nexport const serverApi = {\n get: <T = unknown>(endpoint: string, options?: FetchOptions): Promise<T | null> => {\n return serverFetch<T>(endpoint, options || { revalidate: 60 });\n }\n};\n\n// Revalidate data every 60 seconds (ISR)\nconst defaultOptions: FetchOptions = { revalidate: 60 };\n\nexport async function getCompanyInformation(): Promise<CompanyInformation | null> {\n return serverFetch<CompanyInformation>('/public/company_information', defaultOptions);\n}\n\n/** Ads config (e.g. Meta Pixel). Returns { meta_pixel_id?: string } or {}. Only present when account has connected Meta Ads. */\nexport async function getAdsConfig(): Promise<{ meta_pixel_id?: string } | null> {\n const data = await serverFetch<{ meta_pixel_id?: string }>('/public/ads_config', defaultOptions);\n return data ?? null;\n}\n\n/** Extract Meta Pixel ID from ads config for use with <MetaPixel pixelId={...} />. Only returns a value when present and valid (numeric). */\nexport function getMetaPixelId(adsConfig: { meta_pixel_id?: string } | null | undefined): string | null {\n const id = adsConfig && typeof adsConfig === 'object' && 'meta_pixel_id' in adsConfig && adsConfig.meta_pixel_id;\n const str = id != null && id !== '' ? String(id).trim() : '';\n return str !== '' && str !== 'null' && /^\\d+$/.test(str) ? str : null;\n}\n\nexport async function getServices() {\n return serverFetch('/public/services', defaultOptions);\n}\n\nexport async function getService(slug: string): Promise<Service | null> {\n return serverFetch<Service>(`/public/services/by_slug/${slug}`, defaultOptions);\n}\n\nexport async function getLocations() {\n return serverFetch('/public/locations', defaultOptions);\n}\n\nexport async function getLocation(slug: string) {\n return serverFetch(`/public/locations/by_slug/${slug}`, defaultOptions);\n}\n\nexport async function getReviews() {\n return serverFetch('/public/reviews', defaultOptions);\n}\n\nexport async function getFAQs() {\n return serverFetch('/public/faq_questions', defaultOptions);\n}\n\nexport async function getBlogPosts() {\n return serverFetch('/public/blog_posts', defaultOptions);\n}\n\nexport async function getBlogPost(slug: string) {\n return serverFetch(`/public/blog_posts/by_slug/${slug}`, defaultOptions);\n}\n\nexport async function getTeamMembers() {\n return serverFetch('/public/team_members', defaultOptions);\n}\n\nexport async function getWebsitePhotos() {\n return serverFetch('/public/website_photos', defaultOptions);\n}\n\nexport async function getJobPostings() {\n return serverFetch('/public/job_postings', defaultOptions);\n}\n\nexport async function getJobPosting(slug: string) {\n return serverFetch(`/public/job_postings/by_slug/${slug}`, defaultOptions);\n}\n\nexport async function getSocialPosts() {\n return serverFetch('/public/social_posts', defaultOptions);\n}\n\n// Alias for testimonials (API uses \"reviews\")\nexport async function getTestimonials() {\n return getReviews();\n}\n\n/** Form definition for dynamic form rendering (fields may include optional placeholder). */\nexport async function getForm(formType: string) {\n type FormDefinition = import('../types/api/form').FormDefinition;\n return serverFetch<FormDefinition>(`/public/forms/${encodeURIComponent(formType)}`, defaultOptions);\n}\n\n"],"mappings":";AAQA,IAAM,UAAU,QAAQ,IAAI,WAAW;AACvC,IAAM,UAAU,QAAQ,IAAI,WAAW;AAOvC,eAAe,YAAe,UAAkB,UAAwB,CAAC,GAAsB;AAhB/F;AAiBE,QAAM,MAAM,GAAG,OAAO,GAAG,QAAQ;AAEjC,MAAI;AACF,UAAM,eAAiE;AAAA,MACrE,SAAS;AAAA,QACP,aAAa;AAAA,QACb,gBAAgB;AAAA,MAClB;AAAA,MACA,OAAO,QAAQ;AAAA,IACjB;AAEA,QAAI,QAAQ,YAAY;AACtB,mBAAa,OAAO,EAAE,YAAY,QAAQ,WAAW;AAAA,IACvD;AAEA,UAAM,WAAW,MAAM,MAAM,KAAK,YAAY;AAE9C,QAAI,CAAC,SAAS,IAAI;AAChB,cAAQ,MAAM,sBAAsB,SAAS,MAAM,QAAQ,QAAQ,EAAE;AACrE,aAAO;AAAA,IACT;AAEA,UAAM,OAAO,MAAM,SAAS,KAAK;AAGjC,YAAO,UAAK,SAAL,YAAa;AAAA,EACtB,SAAS,OAAO;AACd,YAAQ,MAAM,gCAAgC,QAAQ,KAAK,KAAK;AAChE,WAAO;AAAA,EACT;AACF;AAKO,IAAM,YAAY;AAAA,EACvB,KAAK,CAAc,UAAkB,YAA8C;AACjF,WAAO,YAAe,UAAU,WAAW,EAAE,YAAY,GAAG,CAAC;AAAA,EAC/D;AACF;AAGA,IAAM,iBAA+B,EAAE,YAAY,GAAG;AAEtD,eAAsB,wBAA4D;AAChF,SAAO,YAAgC,+BAA+B,cAAc;AACtF;AAGA,eAAsB,eAA2D;AAC/E,QAAM,OAAO,MAAM,YAAwC,sBAAsB,cAAc;AAC/F,SAAO,sBAAQ;AACjB;AAGO,SAAS,eAAe,WAAyE;AACtG,QAAM,KAAK,aAAa,OAAO,cAAc,YAAY,mBAAmB,aAAa,UAAU;AACnG,QAAM,MAAM,MAAM,QAAQ,OAAO,KAAK,OAAO,EAAE,EAAE,KAAK,IAAI;AAC1D,SAAO,QAAQ,MAAM,QAAQ,UAAU,QAAQ,KAAK,GAAG,IAAI,MAAM;AACnE;AAEA,eAAsB,cAAc;AAClC,SAAO,YAAY,oBAAoB,cAAc;AACvD;AAEA,eAAsB,WAAW,MAAuC;AACtE,SAAO,YAAqB,4BAA4B,IAAI,IAAI,cAAc;AAChF;AAEA,eAAsB,eAAe;AACnC,SAAO,YAAY,qBAAqB,cAAc;AACxD;AAEA,eAAsB,YAAY,MAAc;AAC9C,SAAO,YAAY,6BAA6B,IAAI,IAAI,cAAc;AACxE;AAEA,eAAsB,aAAa;AACjC,SAAO,YAAY,mBAAmB,cAAc;AACtD;AAEA,eAAsB,UAAU;AAC9B,SAAO,YAAY,yBAAyB,cAAc;AAC5D;AAEA,eAAsB,eAAe;AACnC,SAAO,YAAY,sBAAsB,cAAc;AACzD;AAEA,eAAsB,YAAY,MAAc;AAC9C,SAAO,YAAY,8BAA8B,IAAI,IAAI,cAAc;AACzE;AAEA,eAAsB,iBAAiB;AACrC,SAAO,YAAY,wBAAwB,cAAc;AAC3D;AAEA,eAAsB,mBAAmB;AACvC,SAAO,YAAY,0BAA0B,cAAc;AAC7D;AAEA,eAAsB,iBAAiB;AACrC,SAAO,YAAY,wBAAwB,cAAc;AAC3D;AAEA,eAAsB,cAAc,MAAc;AAChD,SAAO,YAAY,gCAAgC,IAAI,IAAI,cAAc;AAC3E;AAEA,eAAsB,iBAAiB;AACrC,SAAO,YAAY,wBAAwB,cAAc;AAC3D;AAGA,eAAsB,kBAAkB;AACtC,SAAO,WAAW;AACpB;AAGA,eAAsB,QAAQ,UAAkB;AAE9C,SAAO,YAA4B,iBAAiB,mBAAmB,QAAQ,CAAC,IAAI,cAAc;AACpG;","names":[]}
1
+ {"version":3,"sources":["../../src/lib/server-api.ts"],"sourcesContent":["/**\n * Server-side API client for SSR\n * API key is stored securely on the server - never exposed to browser\n */\n\nimport type { CompanyInformation } from '../types/api/company-information';\nimport type { Service } from '../types/api/service';\nimport type { WebsitePhotos } from '../types/api/website-photos';\n\nconst API_URL = process.env.API_URL || 'http://localhost:3000/api/v1';\nconst API_KEY = process.env.API_KEY || '';\n\ninterface FetchOptions {\n cache?: RequestCache;\n revalidate?: number;\n}\n\nasync function serverFetch<T>(endpoint: string, options: FetchOptions = {}): Promise<T | null> {\n const url = `${API_URL}${endpoint}`;\n \n try {\n const fetchOptions: RequestInit & { next?: { revalidate?: number } } = {\n headers: {\n 'X-API-Key': API_KEY,\n 'Content-Type': 'application/json',\n },\n cache: options.cache,\n };\n \n if (options.revalidate) {\n fetchOptions.next = { revalidate: options.revalidate };\n }\n \n const response = await fetch(url, fetchOptions);\n\n if (!response.ok) {\n console.error(`[Server API] Error ${response.status} for ${endpoint}`);\n return null;\n }\n\n const json = await response.json();\n \n // Rails API returns { data: [...], meta: {...} }\n return json.data ?? json;\n } catch (error) {\n console.error(`[Server API] Failed to fetch ${endpoint}:`, error);\n return null;\n }\n}\n\n/**\n * Generic serverApi object for flexible endpoint access\n */\nexport const serverApi = {\n get: <T = unknown>(endpoint: string, options?: FetchOptions): Promise<T | null> => {\n return serverFetch<T>(endpoint, options || { revalidate: 60 });\n }\n};\n\n// Revalidate data every 60 seconds (ISR)\nconst defaultOptions: FetchOptions = { revalidate: 60 };\n\nexport async function getCompanyInformation(): Promise<CompanyInformation | null> {\n return serverFetch<CompanyInformation>('/public/company_information', defaultOptions);\n}\n\n/** Ads config (e.g. Meta Pixel). Returns { meta_pixel_id?: string } or {}. Only present when account has connected Meta Ads. */\nexport async function getAdsConfig(): Promise<{ meta_pixel_id?: string } | null> {\n const data = await serverFetch<{ meta_pixel_id?: string }>('/public/ads_config', defaultOptions);\n return data ?? null;\n}\n\n/** Extract Meta Pixel ID from ads config for use with <MetaPixel pixelId={...} />. Only returns a value when present and valid (numeric). */\nexport function getMetaPixelId(adsConfig: { meta_pixel_id?: string } | null | undefined): string | null {\n const id = adsConfig && typeof adsConfig === 'object' && 'meta_pixel_id' in adsConfig && adsConfig.meta_pixel_id;\n const str = id != null && id !== '' ? String(id).trim() : '';\n return str !== '' && str !== 'null' && /^\\d+$/.test(str) ? str : null;\n}\n\nexport async function getServices() {\n return serverFetch('/public/services', defaultOptions);\n}\n\nexport async function getService(slug: string): Promise<Service | null> {\n return serverFetch<Service>(`/public/services/by_slug/${slug}`, defaultOptions);\n}\n\nexport async function getLocations() {\n return serverFetch('/public/locations', defaultOptions);\n}\n\nexport async function getLocation(slug: string) {\n return serverFetch(`/public/locations/by_slug/${slug}`, defaultOptions);\n}\n\nexport async function getReviews() {\n return serverFetch('/public/reviews', defaultOptions);\n}\n\nexport async function getFAQs() {\n return serverFetch('/public/faq_questions', defaultOptions);\n}\n\nexport async function getBlogPosts() {\n return serverFetch('/public/blog_posts', defaultOptions);\n}\n\nexport async function getBlogPost(slug: string) {\n return serverFetch(`/public/blog_posts/by_slug/${slug}`, defaultOptions);\n}\n\nexport async function getTeamMembers() {\n return serverFetch('/public/team_members', defaultOptions);\n}\n\nexport async function getWebsitePhotos(): Promise<WebsitePhotos | null> {\n return serverFetch<WebsitePhotos>('/public/website_photos', defaultOptions);\n}\n\nexport async function getJobPostings() {\n return serverFetch('/public/job_postings', defaultOptions);\n}\n\nexport async function getJobPosting(slug: string) {\n return serverFetch(`/public/job_postings/by_slug/${slug}`, defaultOptions);\n}\n\nexport async function getSocialPosts() {\n return serverFetch('/public/social_posts', defaultOptions);\n}\n\n// Alias for testimonials (API uses \"reviews\")\nexport async function getTestimonials() {\n return getReviews();\n}\n\n/** Form definition for dynamic form rendering (fields may include optional placeholder). */\nexport async function getForm(formType: string) {\n type FormDefinition = import('../types/api/form').FormDefinition;\n return serverFetch<FormDefinition>(`/public/forms/${encodeURIComponent(formType)}`, defaultOptions);\n}\n\n"],"mappings":";AASA,IAAM,UAAU,QAAQ,IAAI,WAAW;AACvC,IAAM,UAAU,QAAQ,IAAI,WAAW;AAOvC,eAAe,YAAe,UAAkB,UAAwB,CAAC,GAAsB;AAjB/F;AAkBE,QAAM,MAAM,GAAG,OAAO,GAAG,QAAQ;AAEjC,MAAI;AACF,UAAM,eAAiE;AAAA,MACrE,SAAS;AAAA,QACP,aAAa;AAAA,QACb,gBAAgB;AAAA,MAClB;AAAA,MACA,OAAO,QAAQ;AAAA,IACjB;AAEA,QAAI,QAAQ,YAAY;AACtB,mBAAa,OAAO,EAAE,YAAY,QAAQ,WAAW;AAAA,IACvD;AAEA,UAAM,WAAW,MAAM,MAAM,KAAK,YAAY;AAE9C,QAAI,CAAC,SAAS,IAAI;AAChB,cAAQ,MAAM,sBAAsB,SAAS,MAAM,QAAQ,QAAQ,EAAE;AACrE,aAAO;AAAA,IACT;AAEA,UAAM,OAAO,MAAM,SAAS,KAAK;AAGjC,YAAO,UAAK,SAAL,YAAa;AAAA,EACtB,SAAS,OAAO;AACd,YAAQ,MAAM,gCAAgC,QAAQ,KAAK,KAAK;AAChE,WAAO;AAAA,EACT;AACF;AAKO,IAAM,YAAY;AAAA,EACvB,KAAK,CAAc,UAAkB,YAA8C;AACjF,WAAO,YAAe,UAAU,WAAW,EAAE,YAAY,GAAG,CAAC;AAAA,EAC/D;AACF;AAGA,IAAM,iBAA+B,EAAE,YAAY,GAAG;AAEtD,eAAsB,wBAA4D;AAChF,SAAO,YAAgC,+BAA+B,cAAc;AACtF;AAGA,eAAsB,eAA2D;AAC/E,QAAM,OAAO,MAAM,YAAwC,sBAAsB,cAAc;AAC/F,SAAO,sBAAQ;AACjB;AAGO,SAAS,eAAe,WAAyE;AACtG,QAAM,KAAK,aAAa,OAAO,cAAc,YAAY,mBAAmB,aAAa,UAAU;AACnG,QAAM,MAAM,MAAM,QAAQ,OAAO,KAAK,OAAO,EAAE,EAAE,KAAK,IAAI;AAC1D,SAAO,QAAQ,MAAM,QAAQ,UAAU,QAAQ,KAAK,GAAG,IAAI,MAAM;AACnE;AAEA,eAAsB,cAAc;AAClC,SAAO,YAAY,oBAAoB,cAAc;AACvD;AAEA,eAAsB,WAAW,MAAuC;AACtE,SAAO,YAAqB,4BAA4B,IAAI,IAAI,cAAc;AAChF;AAEA,eAAsB,eAAe;AACnC,SAAO,YAAY,qBAAqB,cAAc;AACxD;AAEA,eAAsB,YAAY,MAAc;AAC9C,SAAO,YAAY,6BAA6B,IAAI,IAAI,cAAc;AACxE;AAEA,eAAsB,aAAa;AACjC,SAAO,YAAY,mBAAmB,cAAc;AACtD;AAEA,eAAsB,UAAU;AAC9B,SAAO,YAAY,yBAAyB,cAAc;AAC5D;AAEA,eAAsB,eAAe;AACnC,SAAO,YAAY,sBAAsB,cAAc;AACzD;AAEA,eAAsB,YAAY,MAAc;AAC9C,SAAO,YAAY,8BAA8B,IAAI,IAAI,cAAc;AACzE;AAEA,eAAsB,iBAAiB;AACrC,SAAO,YAAY,wBAAwB,cAAc;AAC3D;AAEA,eAAsB,mBAAkD;AACtE,SAAO,YAA2B,0BAA0B,cAAc;AAC5E;AAEA,eAAsB,iBAAiB;AACrC,SAAO,YAAY,wBAAwB,cAAc;AAC3D;AAEA,eAAsB,cAAc,MAAc;AAChD,SAAO,YAAY,gCAAgC,IAAI,IAAI,cAAc;AAC3E;AAEA,eAAsB,iBAAiB;AACrC,SAAO,YAAY,wBAAwB,cAAc;AAC3D;AAGA,eAAsB,kBAAkB;AACtC,SAAO,WAAW;AACpB;AAGA,eAAsB,QAAQ,UAAkB;AAE9C,SAAO,YAA4B,iBAAiB,mBAAmB,QAAQ,CAAC,IAAI,cAAc;AACpG;","names":[]}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Theme Configuration
3
+ * Single source of truth for all themes
4
+ */
5
+ declare const THEME_CONFIG: {
6
+ readonly classic: "";
7
+ readonly aman: ".aman";
8
+ readonly barelux: ".barelux";
9
+ readonly balance: ".balance";
10
+ };
11
+ type Theme = keyof typeof THEME_CONFIG;
12
+ declare function getAvailableThemes(): Theme[];
13
+ declare function getThemeSuffix(theme: Theme): string;
14
+ declare function isValidTheme(theme: string): theme is Theme;
15
+
16
+ export { THEME_CONFIG, type Theme, getAvailableThemes, getThemeSuffix, isValidTheme };
@@ -0,0 +1,263 @@
1
+ import { Theme } from '../themes/index.js';
2
+ export { B as BlogPost, a as BlogPostAuthor, c as BlogPostParams, d as BlogPostResponse, b as BlogPostTag } from '../blog-post-CvRhU9ss.js';
3
+ export { C as CompanyInformation, a as CompanyInformationResponse } from '../company-information-C_k_sLSB.js';
4
+ import { a as PhotoAttachment } from '../form-CWXC-IHT.js';
5
+ export { F as FormDefinition, b as FormFieldDefinition, c as FormFieldItem, d as FormType, P as Photo, S as Service, e as ServiceParams, f as ServiceResponse } from '../form-CWXC-IHT.js';
6
+ export { a as WebsitePhoto, W as WebsitePhotos, b as WebsitePhotosResponse } from '../website-photos-_n2g24IM.js';
7
+
8
+ interface NavItem {
9
+ label: string;
10
+ href: string;
11
+ subtitle?: string;
12
+ children?: NavItem[];
13
+ }
14
+ interface SiteConfig {
15
+ site: {
16
+ title: string;
17
+ description: string;
18
+ theme: Theme;
19
+ };
20
+ navigation: {
21
+ header: NavItem[];
22
+ footer: NavItem[][];
23
+ };
24
+ }
25
+
26
+ interface Testimonial {
27
+ id: number;
28
+ reviewer_name: string;
29
+ reviewer_title?: string;
30
+ company?: string;
31
+ rating?: number;
32
+ content_markdown: string;
33
+ source?: string;
34
+ source_url?: string;
35
+ featured: boolean;
36
+ reviewed_at: string;
37
+ created_at: string;
38
+ updated_at: string;
39
+ photo_attachments?: PhotoAttachment[];
40
+ }
41
+ interface TestimonialParams {
42
+ source?: string;
43
+ rating?: number;
44
+ verified?: boolean;
45
+ featured?: boolean;
46
+ page?: number;
47
+ per_page?: number;
48
+ }
49
+ type TestimonialResponse = Testimonial[];
50
+
51
+ interface TeamMember {
52
+ id: number;
53
+ name: string;
54
+ position: string;
55
+ bio_markdown: string;
56
+ experience_markdown: string;
57
+ email: string;
58
+ phone: string;
59
+ sort_order: number;
60
+ featured: boolean;
61
+ specialties_markdown: string;
62
+ education_markdown: string;
63
+ certifications_markdown: string;
64
+ linkedin_url: string;
65
+ twitter_url: string;
66
+ photo_attachments?: PhotoAttachment[];
67
+ }
68
+ interface TeamMemberParams {
69
+ featured?: boolean;
70
+ sort_order?: number;
71
+ page?: number;
72
+ per_page?: number;
73
+ }
74
+ type TeamMemberResponse = TeamMember[];
75
+
76
+ interface Location {
77
+ id: number;
78
+ name: string;
79
+ slug: string;
80
+ address_line_1: string;
81
+ address_line_2?: string;
82
+ city: string;
83
+ state: string;
84
+ zip_code: string;
85
+ country?: string;
86
+ latitude?: number;
87
+ longitude?: number;
88
+ phone?: string;
89
+ email?: string;
90
+ description_markdown?: string;
91
+ business_hours?: string;
92
+ is_primary: boolean;
93
+ active: boolean;
94
+ sort_order: number;
95
+ created_at: string;
96
+ updated_at: string;
97
+ photo_attachments?: PhotoAttachment[];
98
+ }
99
+ interface LocationParams {
100
+ active?: boolean;
101
+ is_primary?: boolean;
102
+ sort_order?: number;
103
+ page?: number;
104
+ per_page?: number;
105
+ }
106
+ type LocationResponse = Location[];
107
+
108
+ interface SocialPost {
109
+ id: number;
110
+ platform: string;
111
+ content_markdown: string;
112
+ posted_at: string;
113
+ status?: string;
114
+ created_at: string;
115
+ updated_at: string;
116
+ /** Image URLs from photo_attachments (preferred for display) */
117
+ image_urls?: string[];
118
+ /** Photo attachments (same pattern as blog post, team member, etc.) */
119
+ photo_attachments?: PhotoAttachment[];
120
+ /** Legacy; prefer image_urls / photo_attachments */
121
+ media_urls?: {
122
+ images?: string[];
123
+ videos?: string[];
124
+ };
125
+ engagement_metrics?: {
126
+ likes?: number;
127
+ comments?: number;
128
+ shares?: number;
129
+ views?: number;
130
+ };
131
+ /** Permalink to the post on the platform when available; only link when present to avoid broken links */
132
+ external_post_url?: string | null;
133
+ }
134
+ interface SocialPostParams {
135
+ platform?: string;
136
+ featured?: boolean;
137
+ page?: number;
138
+ per_page?: number;
139
+ }
140
+ type SocialPostResponse = SocialPost[];
141
+
142
+ interface FaqQuestion {
143
+ id: number;
144
+ question: string;
145
+ answer: string;
146
+ answer_markdown?: string;
147
+ category_slug?: string;
148
+ featured: boolean;
149
+ sort_order: number;
150
+ created_at: string;
151
+ updated_at: string;
152
+ photo_attachments?: PhotoAttachment[];
153
+ }
154
+ interface FaqCategory {
155
+ id: number;
156
+ name: string;
157
+ slug: string;
158
+ description?: string;
159
+ active: boolean;
160
+ sort_order: number;
161
+ questions_count: number;
162
+ }
163
+ interface FaqParams {
164
+ category_slug?: string;
165
+ featured?: boolean;
166
+ q?: string;
167
+ page?: number;
168
+ per_page?: number;
169
+ }
170
+ type FaqResponse = FaqQuestion[];
171
+ type FaqCategoryResponse = FaqCategory[];
172
+
173
+ interface JobPosting {
174
+ id: number;
175
+ title: string;
176
+ slug: string;
177
+ description_markdown: string;
178
+ requirements_markdown?: string;
179
+ benefits_markdown?: string;
180
+ employment_type: string;
181
+ location: string;
182
+ experience_level?: string;
183
+ remote_ok?: boolean;
184
+ salary_range?: string;
185
+ posted_at: string;
186
+ expires_at?: string;
187
+ status: 'active' | 'inactive' | 'expired';
188
+ featured: boolean;
189
+ created_at: string;
190
+ updated_at: string;
191
+ photo_attachments?: PhotoAttachment[];
192
+ }
193
+ interface JobPostingParams {
194
+ featured?: boolean;
195
+ employment_type?: string;
196
+ location?: string;
197
+ q?: string;
198
+ page?: number;
199
+ per_page?: number;
200
+ }
201
+ type JobPostingResponse = JobPosting[];
202
+
203
+ interface ContactInfo {
204
+ id?: number;
205
+ about_text_markdown?: string;
206
+ business_hours?: Record<string, {
207
+ open: string;
208
+ close: string;
209
+ }>;
210
+ company_name?: string;
211
+ description_markdown?: string;
212
+ facebook_url?: string;
213
+ favicon_url?: string;
214
+ founded_year?: string;
215
+ google_my_business_url?: string;
216
+ google_reviews_url?: string;
217
+ instagram_url?: string;
218
+ linkedin_url?: string;
219
+ mission_statement_markdown?: string;
220
+ pinterest_url?: string;
221
+ primary_address_line_1?: string;
222
+ primary_address_line_2?: string;
223
+ primary_city?: string;
224
+ primary_country?: string;
225
+ primary_email?: string;
226
+ primary_phone?: string;
227
+ primary_state?: string;
228
+ primary_zip_code?: string;
229
+ sales_email?: string;
230
+ stats_markdown?: string;
231
+ support_email?: string;
232
+ tagline?: string;
233
+ tiktok_url?: string;
234
+ tripadvisor_url?: string;
235
+ twitter_url?: string;
236
+ values_markdown?: string;
237
+ yelp_url?: string;
238
+ youtube_url?: string;
239
+ photo_attachments?: PhotoAttachment[];
240
+ }
241
+ interface ContactFormSubmission {
242
+ id: number;
243
+ name: string;
244
+ email: string;
245
+ phone?: string;
246
+ message: string;
247
+ source: string;
248
+ created_at: string;
249
+ }
250
+ interface ContactFormData {
251
+ name: string;
252
+ email: string;
253
+ phone?: string;
254
+ message: string;
255
+ source?: string;
256
+ }
257
+ type ContactResponse = ContactInfo | null;
258
+ interface ContactFormResponse {
259
+ message: string;
260
+ data: ContactFormSubmission;
261
+ }
262
+
263
+ export { type ContactFormData, type ContactFormResponse, type ContactFormSubmission, type ContactInfo, type ContactResponse, type FaqCategory, type FaqCategoryResponse, type FaqParams, type FaqQuestion, type FaqResponse, type JobPosting, type JobPostingParams, type JobPostingResponse, type Location, type LocationParams, type LocationResponse, type NavItem, PhotoAttachment, type SiteConfig, type SocialPost, type SocialPostParams, type SocialPostResponse, type TeamMember, type TeamMemberParams, type TeamMemberResponse, type Testimonial, type TestimonialParams, type TestimonialResponse, Theme };
@@ -0,0 +1,15 @@
1
+ import * as tailwind_merge from 'tailwind-merge';
2
+
3
+ /**
4
+ * This function is a wrapper around the twMerge function.
5
+ * It is used to merge the classes inside style objects.
6
+ */
7
+ declare const cx: (...classLists: tailwind_merge.ClassNameValue[]) => string;
8
+ /**
9
+ * This function does nothing besides helping us to be able to
10
+ * sort the classes inside style objects which is not supported
11
+ * by the Tailwind IntelliSense by default.
12
+ */
13
+ declare function sortCx<T extends Record<string, string | number | Record<string, string | number | Record<string, string | number>>>>(classes: T): T;
14
+
15
+ export { cx, sortCx };
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Generates a beautiful gradient SVG data URL using the full color palette
3
+ * Based on gradients used in corporate marketing site animations
4
+ * Each seed (post ID or index) will consistently return the same gradient
5
+ */
6
+ declare const getGradientUrl: (seed: string | number) => string;
7
+
8
+ export { getGradientUrl };
@@ -0,0 +1,21 @@
1
+ import React__default from 'react';
2
+
3
+ type ReactComponent = React__default.FC<unknown> | React__default.ComponentClass<unknown, unknown>;
4
+ /**
5
+ * Checks if a given value is a function component.
6
+ */
7
+ declare const isFunctionComponent: (component: unknown) => component is React__default.FC<unknown>;
8
+ /**
9
+ * Checks if a given value is a class component.
10
+ */
11
+ declare const isClassComponent: (component: unknown) => component is React__default.ComponentClass<unknown, unknown>;
12
+ /**
13
+ * Checks if a given value is a forward ref component.
14
+ */
15
+ declare const isForwardRefComponent: (component: unknown) => component is React__default.ForwardRefExoticComponent<unknown>;
16
+ /**
17
+ * Checks if a given value is a valid React component.
18
+ */
19
+ declare const isReactComponent: (component: unknown) => component is ReactComponent;
20
+
21
+ export { isClassComponent, isForwardRefComponent, isFunctionComponent, isReactComponent };
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Utility functions for extracting table of contents from markdown
3
+ */
4
+ interface TableOfContentsItem {
5
+ id: string;
6
+ title: string;
7
+ level: number;
8
+ }
9
+ /**
10
+ * Extract headings from markdown content and generate table of contents
11
+ */
12
+ declare function extractTableOfContents(markdown: string): TableOfContentsItem[];
13
+
14
+ export { type TableOfContentsItem, extractTableOfContents };