@djangocfg/layouts 2.1.10 → 2.1.14

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 (105) hide show
  1. package/README.md +53 -161
  2. package/package.json +6 -6
  3. package/src/components/RedirectPage/RedirectPage.tsx +1 -1
  4. package/src/index.ts +0 -6
  5. package/src/layouts/AppLayout/AppLayout.tsx +1 -1
  6. package/src/layouts/AppLayout/BaseApp.tsx +1 -1
  7. package/src/layouts/AuthLayout/AuthContext.tsx +1 -1
  8. package/src/layouts/AuthLayout/OAuthCallback.tsx +1 -1
  9. package/src/layouts/AuthLayout/OAuthProviders.tsx +1 -1
  10. package/src/layouts/PrivateLayout/PrivateLayout.tsx +1 -1
  11. package/src/layouts/PrivateLayout/components/PrivateHeader.tsx +1 -1
  12. package/src/layouts/ProfileLayout/components/AvatarSection.tsx +2 -2
  13. package/src/layouts/PublicLayout/components/PublicMobileDrawer.tsx +1 -1
  14. package/src/layouts/PublicLayout/components/PublicNavigation.tsx +1 -1
  15. package/src/layouts/_components/UserMenu.tsx +1 -1
  16. package/src/layouts/index.ts +0 -2
  17. package/src/snippets/Analytics/useAnalytics.ts +1 -1
  18. package/src/snippets/index.ts +0 -3
  19. package/src/auth/README.md +0 -962
  20. package/src/auth/context/AccountsContext.tsx +0 -240
  21. package/src/auth/context/AuthContext.tsx +0 -604
  22. package/src/auth/context/index.ts +0 -4
  23. package/src/auth/context/types.ts +0 -68
  24. package/src/auth/hooks/index.ts +0 -17
  25. package/src/auth/hooks/useAuthForm.ts +0 -332
  26. package/src/auth/hooks/useAuthGuard.ts +0 -25
  27. package/src/auth/hooks/useAuthRedirect.ts +0 -51
  28. package/src/auth/hooks/useAutoAuth.ts +0 -49
  29. package/src/auth/hooks/useGithubAuth.ts +0 -184
  30. package/src/auth/hooks/useLocalStorage.ts +0 -214
  31. package/src/auth/hooks/useProfileCache.ts +0 -146
  32. package/src/auth/hooks/useSessionStorage.ts +0 -189
  33. package/src/auth/index.ts +0 -10
  34. package/src/auth/middlewares/index.ts +0 -1
  35. package/src/auth/middlewares/proxy.ts +0 -32
  36. package/src/auth/server.ts +0 -6
  37. package/src/auth/utils/errors.ts +0 -34
  38. package/src/auth/utils/index.ts +0 -2
  39. package/src/auth/utils/validation.ts +0 -14
  40. package/src/contexts/LeadsContext.tsx +0 -156
  41. package/src/contexts/NewsletterContext.tsx +0 -263
  42. package/src/contexts/SupportContext.tsx +0 -256
  43. package/src/contexts/index.ts +0 -59
  44. package/src/contexts/knowbase/ChatContext.tsx +0 -174
  45. package/src/contexts/knowbase/DocumentsContext.tsx +0 -304
  46. package/src/contexts/knowbase/SessionsContext.tsx +0 -174
  47. package/src/contexts/knowbase/index.ts +0 -61
  48. package/src/contexts/payments/BalancesContext.tsx +0 -65
  49. package/src/contexts/payments/CurrenciesContext.tsx +0 -66
  50. package/src/contexts/payments/OverviewContext.tsx +0 -174
  51. package/src/contexts/payments/PaymentsContext.tsx +0 -132
  52. package/src/contexts/payments/README.md +0 -201
  53. package/src/contexts/payments/RootPaymentsContext.tsx +0 -68
  54. package/src/contexts/payments/index.ts +0 -50
  55. package/src/layouts/PaymentsLayout/PaymentsLayout.tsx +0 -92
  56. package/src/layouts/PaymentsLayout/components/CreatePaymentDialog.tsx +0 -291
  57. package/src/layouts/PaymentsLayout/components/PaymentDetailsDialog.tsx +0 -290
  58. package/src/layouts/PaymentsLayout/components/index.ts +0 -2
  59. package/src/layouts/PaymentsLayout/events.ts +0 -47
  60. package/src/layouts/PaymentsLayout/index.ts +0 -16
  61. package/src/layouts/PaymentsLayout/types.ts +0 -6
  62. package/src/layouts/PaymentsLayout/views/overview/components/BalanceCard.tsx +0 -128
  63. package/src/layouts/PaymentsLayout/views/overview/components/RecentPayments.tsx +0 -142
  64. package/src/layouts/PaymentsLayout/views/overview/components/index.ts +0 -2
  65. package/src/layouts/PaymentsLayout/views/overview/index.tsx +0 -20
  66. package/src/layouts/PaymentsLayout/views/payments/components/PaymentsList.tsx +0 -276
  67. package/src/layouts/PaymentsLayout/views/payments/components/index.ts +0 -1
  68. package/src/layouts/PaymentsLayout/views/payments/index.tsx +0 -17
  69. package/src/layouts/PaymentsLayout/views/transactions/components/TransactionsList.tsx +0 -273
  70. package/src/layouts/PaymentsLayout/views/transactions/components/index.ts +0 -1
  71. package/src/layouts/PaymentsLayout/views/transactions/index.tsx +0 -17
  72. package/src/layouts/SupportLayout/README.md +0 -91
  73. package/src/layouts/SupportLayout/SupportLayout.tsx +0 -179
  74. package/src/layouts/SupportLayout/components/CreateTicketDialog.tsx +0 -155
  75. package/src/layouts/SupportLayout/components/MessageInput.tsx +0 -92
  76. package/src/layouts/SupportLayout/components/MessageList.tsx +0 -314
  77. package/src/layouts/SupportLayout/components/TicketCard.tsx +0 -96
  78. package/src/layouts/SupportLayout/components/TicketList.tsx +0 -153
  79. package/src/layouts/SupportLayout/components/index.ts +0 -6
  80. package/src/layouts/SupportLayout/context/SupportLayoutContext.tsx +0 -263
  81. package/src/layouts/SupportLayout/context/index.ts +0 -2
  82. package/src/layouts/SupportLayout/events.ts +0 -33
  83. package/src/layouts/SupportLayout/hooks/index.ts +0 -2
  84. package/src/layouts/SupportLayout/hooks/useInfiniteMessages.ts +0 -119
  85. package/src/layouts/SupportLayout/hooks/useInfiniteTickets.ts +0 -92
  86. package/src/layouts/SupportLayout/index.ts +0 -8
  87. package/src/layouts/SupportLayout/types.ts +0 -21
  88. package/src/snippets/Chat/ChatUIContext.tsx +0 -110
  89. package/src/snippets/Chat/ChatWidget.tsx +0 -476
  90. package/src/snippets/Chat/README.md +0 -122
  91. package/src/snippets/Chat/components/MessageInput.tsx +0 -124
  92. package/src/snippets/Chat/components/MessageList.tsx +0 -169
  93. package/src/snippets/Chat/components/SessionList.tsx +0 -192
  94. package/src/snippets/Chat/components/index.ts +0 -9
  95. package/src/snippets/Chat/hooks/index.ts +0 -6
  96. package/src/snippets/Chat/hooks/useInfiniteSessions.ts +0 -82
  97. package/src/snippets/Chat/index.tsx +0 -45
  98. package/src/snippets/Chat/types.ts +0 -80
  99. package/src/snippets/ContactForm/ContactForm.tsx +0 -346
  100. package/src/snippets/ContactForm/ContactFormProvider.tsx +0 -153
  101. package/src/snippets/ContactForm/ContactInfo.tsx +0 -114
  102. package/src/snippets/ContactForm/ContactPage.tsx +0 -131
  103. package/src/snippets/ContactForm/dynamic.tsx +0 -55
  104. package/src/snippets/ContactForm/index.ts +0 -34
  105. package/src/snippets/ContactForm/types.ts +0 -110
@@ -1,153 +0,0 @@
1
- 'use client';
2
-
3
- import React, { createContext, useContext, useState, useCallback, useEffect } from 'react';
4
- import { configureAPI, createLeadsSubmitCreate, Enums } from '@djangocfg/api';
5
- import type {
6
- ContactFormContextValue,
7
- ContactFormProviderProps,
8
- LeadSubmissionData,
9
- LeadSubmissionResult,
10
- } from './types';
11
-
12
- // ============================================================================
13
- // Context
14
- // ============================================================================
15
-
16
- const ContactFormContext = createContext<ContactFormContextValue | undefined>(undefined);
17
-
18
- // ============================================================================
19
- // Helpers
20
- // ============================================================================
21
-
22
- const CONTACT_TYPE_MAP: Record<string, Enums.LeadSubmissionRequestContactType> = {
23
- email: Enums.LeadSubmissionRequestContactType.EMAIL,
24
- telegram: Enums.LeadSubmissionRequestContactType.TELEGRAM,
25
- whatsapp: Enums.LeadSubmissionRequestContactType.WHATSAPP,
26
- phone: Enums.LeadSubmissionRequestContactType.PHONE,
27
- other: Enums.LeadSubmissionRequestContactType.OTHER,
28
- };
29
-
30
- // ============================================================================
31
- // Provider
32
- // ============================================================================
33
-
34
- /**
35
- * ContactFormProvider - Configures API and provides submit functionality
36
- *
37
- * Uses the generated Django-CFG API client for lead submission.
38
- *
39
- * @example
40
- * ```tsx
41
- * import { ContactFormProvider, ContactForm } from '@djangocfg/layouts';
42
- *
43
- * <ContactFormProvider apiUrl="https://api.example.com">
44
- * <ContactForm fields={fields} />
45
- * </ContactFormProvider>
46
- * ```
47
- */
48
- export function ContactFormProvider({ children, apiUrl }: ContactFormProviderProps) {
49
- const [isSubmitting, setIsSubmitting] = useState(false);
50
- const [error, setError] = useState<Error | null>(null);
51
- const [lastResponse, setLastResponse] = useState<LeadSubmissionResult | null>(null);
52
-
53
- // Always use local API route to avoid CORS issues
54
- // Pass apiUrl in request body so route.ts knows where to proxy
55
-
56
- // Submit function - always uses local API route
57
- const submit = useCallback(async (data: LeadSubmissionData): Promise<LeadSubmissionResult> => {
58
- setIsSubmitting(true);
59
- setError(null);
60
-
61
- try {
62
- // Always use local Next.js API route to avoid CORS issues
63
- // Pass apiUrl from ContactPage in the request body so route.ts knows where to proxy
64
- const res = await fetch('/api/contact', {
65
- method: 'POST',
66
- headers: {
67
- 'Content-Type': 'application/json',
68
- },
69
- body: JSON.stringify({
70
- _apiUrl: apiUrl || '', // Pass apiUrl to route.ts for proxying (can be empty for fallback)
71
- name: data.name,
72
- email: data.email,
73
- message: data.message,
74
- company: data.company,
75
- company_site: data.company_site,
76
- contact_type: data.contact_type,
77
- contact_value: data.contact_value,
78
- subject: data.subject,
79
- extra: data.extra,
80
- site_url: data.site_url || (typeof window !== 'undefined' ? window.location.origin : undefined),
81
- }),
82
- });
83
-
84
- if (!res.ok) {
85
- const errorData = await res.json().catch(() => ({ message: 'Failed to submit contact form' }));
86
- throw new Error(errorData.message || `HTTP ${res.status}`);
87
- }
88
-
89
- const response: LeadSubmissionResult = await res.json();
90
-
91
- const result: LeadSubmissionResult = {
92
- success: response.success,
93
- message: response.message,
94
- lead_id: response.lead_id,
95
- };
96
-
97
- setLastResponse(result);
98
- return result;
99
- } catch (err) {
100
- const error = err instanceof Error ? err : new Error(String(err));
101
- setError(error);
102
- throw error;
103
- } finally {
104
- setIsSubmitting(false);
105
- }
106
- }, [apiUrl]);
107
-
108
- // Reset error state
109
- const resetError = useCallback(() => {
110
- setError(null);
111
- }, []);
112
-
113
- const value: ContactFormContextValue = {
114
- apiUrl: '/api/contact', // Always use local endpoint
115
- submit,
116
- isSubmitting,
117
- error,
118
- lastResponse,
119
- resetError,
120
- };
121
-
122
- return (
123
- <ContactFormContext.Provider value={value}>
124
- {children}
125
- </ContactFormContext.Provider>
126
- );
127
- }
128
-
129
- // ============================================================================
130
- // Hooks
131
- // ============================================================================
132
-
133
- /**
134
- * useContactForm - Access contact form context
135
- *
136
- * Must be used within ContactFormProvider
137
- */
138
- export function useContactForm(): ContactFormContextValue {
139
- const context = useContext(ContactFormContext);
140
- if (!context) {
141
- throw new Error('useContactForm must be used within ContactFormProvider');
142
- }
143
- return context;
144
- }
145
-
146
- /**
147
- * useContactFormOptional - Access contact form context (optional)
148
- *
149
- * Returns null if not within ContactFormProvider
150
- */
151
- export function useContactFormOptional(): ContactFormContextValue | null {
152
- return useContext(ContactFormContext) ?? null;
153
- }
@@ -1,114 +0,0 @@
1
- 'use client';
2
-
3
- import React from 'react';
4
- import {
5
- Card,
6
- CardHeader,
7
- CardTitle,
8
- CardContent,
9
- Button,
10
- cn,
11
- } from '@djangocfg/ui-nextjs';
12
- import type { ContactInfoProps, ContactDetail } from './types';
13
- import { DEFAULT_INFO_TITLE } from './types';
14
-
15
- /**
16
- * ContactInfo - Display contact information with optional action card
17
- *
18
- * @example
19
- * ```tsx
20
- * import { ContactInfo } from '@djangocfg/layouts';
21
- * import { Mail, Phone, MapPin, Calendar } from 'lucide-react';
22
- *
23
- * const details = [
24
- * { icon: <Mail />, label: 'Email', value: 'hello@example.com', href: 'mailto:hello@example.com' },
25
- * { icon: <Phone />, label: 'Phone', value: '+1 234 567 890', href: 'tel:+1234567890' },
26
- * { icon: <MapPin />, label: 'Address', value: '123 Main St, City' },
27
- * ];
28
- *
29
- * <ContactInfo
30
- * details={details}
31
- * action={{
32
- * title: 'Schedule a Meeting',
33
- * description: 'Book a 30-minute call',
34
- * button: {
35
- * icon: <Calendar />,
36
- * label: 'Book Now',
37
- * href: 'https://calendly.com/...',
38
- * },
39
- * }}
40
- * />
41
- * ```
42
- */
43
- export function ContactInfo({
44
- details,
45
- title = DEFAULT_INFO_TITLE,
46
- action,
47
- className,
48
- }: ContactInfoProps) {
49
- const renderDetail = (detail: ContactDetail, index: number) => {
50
- const content = (
51
- <div className="flex items-start gap-3">
52
- <div className="text-muted-foreground mt-0.5">{detail.icon}</div>
53
- <div>
54
- <p className="text-sm text-muted-foreground">{detail.label}</p>
55
- <p className="font-medium">{detail.value}</p>
56
- </div>
57
- </div>
58
- );
59
-
60
- if (detail.href) {
61
- return (
62
- <a
63
- key={index}
64
- href={detail.href}
65
- target={detail.external !== false ? '_blank' : undefined}
66
- rel={detail.external !== false ? 'noopener noreferrer' : undefined}
67
- className="block hover:bg-muted/50 rounded-lg p-2 -m-2 transition-colors"
68
- >
69
- {content}
70
- </a>
71
- );
72
- }
73
-
74
- return <div key={index}>{content}</div>;
75
- };
76
-
77
- return (
78
- <div className={cn('space-y-6', className)}>
79
- {/* Contact Details Card */}
80
- <Card>
81
- <CardHeader>
82
- <CardTitle>{title}</CardTitle>
83
- </CardHeader>
84
- <CardContent className="space-y-4">
85
- {details.map(renderDetail)}
86
- </CardContent>
87
- </Card>
88
-
89
- {/* Action Card */}
90
- {action && (
91
- <Card className="bg-primary/5 border-primary/20">
92
- <CardContent className="pt-6">
93
- <h3 className="font-semibold mb-2">{action.title}</h3>
94
- {action.description && (
95
- <p className="text-sm text-muted-foreground mb-4">
96
- {action.description}
97
- </p>
98
- )}
99
- <Button asChild className="w-full">
100
- <a
101
- href={action.button.href}
102
- target={action.button.external !== false ? '_blank' : undefined}
103
- rel={action.button.external !== false ? 'noopener noreferrer' : undefined}
104
- >
105
- {action.button.icon}
106
- <span className="ml-2">{action.button.label}</span>
107
- </a>
108
- </Button>
109
- </CardContent>
110
- </Card>
111
- )}
112
- </div>
113
- );
114
- }
@@ -1,131 +0,0 @@
1
- 'use client';
2
-
3
- import React from 'react';
4
- import { Mail, MapPin, Calendar, MessageCircle } from 'lucide-react';
5
- import { ContactFormBase as ContactForm } from './ContactForm';
6
- import { ContactInfo } from './ContactInfo';
7
- import type { ContactDetail, LeadSubmissionResult } from './types';
8
-
9
- // ============================================================================
10
- // Config
11
- // ============================================================================
12
-
13
- const DEFAULT_CONFIG = {
14
- apiUrl: 'https://api.reforms.ai',
15
- email: 'markolofsen@gmail.com',
16
- whatsapp: '+62 813 39646301',
17
- calendly: 'https://calendly.com/markolofsen/meeting',
18
- };
19
-
20
- // ============================================================================
21
- // Props
22
- // ============================================================================
23
-
24
- export interface ContactPageProps {
25
- /** Override API URL (defaults to DEFAULT_CONFIG.apiUrl) */
26
- apiUrl?: string;
27
- /** Override email */
28
- email?: string;
29
- /** Override whatsapp */
30
- whatsapp?: string;
31
- /** Override calendly link */
32
- calendlyUrl?: string;
33
- /** Page title */
34
- title?: React.ReactNode;
35
- /** Page subtitle */
36
- subtitle?: string;
37
- /** Location text */
38
- location?: string;
39
- /** Hide calendly action card */
40
- hideCalendly?: boolean;
41
- /** Additional className */
42
- className?: string;
43
- /** Callback after successful submit */
44
- onSuccess?: (result: LeadSubmissionResult) => void;
45
- }
46
-
47
- // ============================================================================
48
- // Component
49
- // ============================================================================
50
-
51
- /**
52
- * ContactPageBase - Pre-configured contact page component
53
- *
54
- * NOTE: Use ContactPage from index.ts which is dynamically imported (ssr: false)
55
- *
56
- * @example
57
- * ```tsx
58
- * import { ContactPage } from '@djangocfg/layouts';
59
- * <ContactPage />
60
- * ```
61
- */
62
- export function ContactPageBase({
63
- apiUrl = DEFAULT_CONFIG.apiUrl,
64
- email = DEFAULT_CONFIG.email,
65
- whatsapp = DEFAULT_CONFIG.whatsapp,
66
- calendlyUrl = DEFAULT_CONFIG.calendly,
67
- title = 'Get in Touch',
68
- subtitle = "Have a question or want to work together? We'd love to hear from you.",
69
- location = 'Remote-first team',
70
- hideCalendly = false,
71
- className,
72
- onSuccess,
73
- }: ContactPageProps) {
74
- // Format phone for WhatsApp link (remove spaces and special chars)
75
- const whatsappPhone = whatsapp.replace(/[\s\-\(\)]/g, '');
76
-
77
- const contactDetails: ContactDetail[] = [
78
- {
79
- icon: <Mail className="h-5 w-5" />,
80
- label: 'Email',
81
- value: email,
82
- href: `mailto:${email}`,
83
- },
84
- {
85
- icon: <MessageCircle className="h-5 w-5" />,
86
- label: 'WhatsApp',
87
- value: whatsapp,
88
- href: `https://wa.me/${whatsappPhone}`,
89
- },
90
- {
91
- icon: <MapPin className="h-5 w-5" />,
92
- label: 'Location',
93
- value: location,
94
- },
95
- ];
96
-
97
- return (
98
- <div className={className}>
99
- {/* Header */}
100
- <div className="text-center mb-8 md:mb-12">
101
- <h1 className="text-3xl sm:text-4xl md:text-5xl font-bold mb-4">
102
- {title}
103
- </h1>
104
- <p className="text-base sm:text-lg text-muted-foreground max-w-2xl mx-auto px-4">
105
- {subtitle}
106
- </p>
107
- </div>
108
-
109
- {/* Content Grid */}
110
- <div className="grid grid-cols-1 lg:grid-cols-2 gap-8 w-full">
111
- <div>
112
- <ContactForm apiUrl={apiUrl} onSuccess={onSuccess} />
113
- </div>
114
- <div>
115
- <ContactInfo
116
- details={contactDetails}
117
- action={hideCalendly ? undefined : {
118
- title: 'Schedule a Meeting',
119
- description: 'Book a time that works for you',
120
- button: {
121
- icon: <Calendar className="h-4 w-4" />,
122
- label: 'Book a Call',
123
- href: calendlyUrl,
124
- },
125
- }}
126
- />
127
- </div>
128
- </div>
129
- </div>
130
- );
131
- }
@@ -1,55 +0,0 @@
1
- /**
2
- * Dynamic ContactForm components (ssr: false)
3
- *
4
- * Avoids hydration mismatch when using localStorage for form drafts.
5
- * Components are loaded client-side only.
6
- */
7
-
8
- 'use client';
9
-
10
- import dynamic from 'next/dynamic';
11
- import { Skeleton } from '@djangocfg/ui-nextjs';
12
- import type { ContactFormProps } from './ContactForm';
13
- import type { ContactPageProps } from './ContactPage';
14
-
15
- function ContactFormSkeleton() {
16
- return (
17
- <div className="space-y-4">
18
- <Skeleton className="w-full h-10" />
19
- <Skeleton className="w-full h-10" />
20
- <Skeleton className="w-full h-10" />
21
- <Skeleton className="w-full h-24" />
22
- <Skeleton className="w-full h-10" />
23
- </div>
24
- );
25
- }
26
-
27
- function ContactPageSkeleton() {
28
- return (
29
- <div>
30
- <div className="text-center mb-8 md:mb-12">
31
- <Skeleton className="w-64 h-12 mx-auto mb-4" />
32
- <Skeleton className="w-96 h-6 mx-auto" />
33
- </div>
34
- <div className="grid grid-cols-1 lg:grid-cols-3 gap-8">
35
- <div className="lg:col-span-2">
36
- <ContactFormSkeleton />
37
- </div>
38
- <div className="space-y-4">
39
- <Skeleton className="w-full h-32" />
40
- <Skeleton className="w-full h-24" />
41
- </div>
42
- </div>
43
- </div>
44
- );
45
- }
46
-
47
- export const ContactForm = dynamic<ContactFormProps>(
48
- () => import('./ContactForm').then((mod) => mod.ContactFormBase),
49
- { ssr: false, loading: () => <ContactFormSkeleton /> }
50
- );
51
-
52
- export const ContactPage = dynamic<ContactPageProps>(
53
- () => import('./ContactPage').then((mod) => mod.ContactPageBase),
54
- { ssr: false, loading: () => <ContactPageSkeleton /> }
55
- );
@@ -1,34 +0,0 @@
1
- // ============================================================================
2
- // ContactForm - Contact form using Django-CFG Lead API
3
- // ============================================================================
4
-
5
- // Components (dynamic import, ssr: false - no hydration issues)
6
- export { ContactForm, ContactPage } from './dynamic';
7
- export { ContactInfo } from './ContactInfo';
8
-
9
- // Types
10
- export type { ContactFormProps } from './ContactForm';
11
- export type { ContactPageProps } from './ContactPage';
12
-
13
- // Provider & Hooks
14
- export {
15
- ContactFormProvider,
16
- useContactForm,
17
- useContactFormOptional,
18
- } from './ContactFormProvider';
19
-
20
- // Types
21
- export type {
22
- ContactFormProviderProps,
23
- ContactFormContextValue,
24
- LeadSubmissionData,
25
- LeadSubmissionResult,
26
- UseContactFormReturn,
27
- ContactFormTexts,
28
- ContactDetail,
29
- ContactAction,
30
- ContactInfoProps,
31
- } from './types';
32
-
33
- // Constants
34
- export { DEFAULT_FORM_TEXTS, DEFAULT_INFO_TITLE } from './types';
@@ -1,110 +0,0 @@
1
- import type { ReactNode } from 'react';
2
- import type { Schemas } from '@djangocfg/api';
3
-
4
- // ============================================================================
5
- // Re-export API Types
6
- // ============================================================================
7
-
8
- /** Lead submission request data - uses generated API type */
9
- export type LeadSubmissionData = Schemas.LeadSubmissionRequest;
10
-
11
- /** Lead submission response - uses generated API type */
12
- export type LeadSubmissionResult = Schemas.LeadSubmissionResponse;
13
-
14
- // ============================================================================
15
- // Provider Types
16
- // ============================================================================
17
-
18
- export interface ContactFormProviderProps {
19
- children: ReactNode;
20
- /**
21
- * API base URL for lead submission
22
- * If not provided or empty, will use local Next.js API route (/api/contact)
23
- * @example "https://api.example.com" or undefined to use /api/contact
24
- */
25
- apiUrl?: string;
26
- }
27
-
28
- // ============================================================================
29
- // Hook Types
30
- // ============================================================================
31
-
32
- export interface UseContactFormReturn {
33
- /** Submit lead data */
34
- submit: (data: LeadSubmissionData) => Promise<LeadSubmissionResult>;
35
- /** Is currently submitting */
36
- isSubmitting: boolean;
37
- /** Last error */
38
- error: Error | null;
39
- /** Last successful response */
40
- lastResponse: LeadSubmissionResult | null;
41
- /** Reset error state */
42
- resetError: () => void;
43
- }
44
-
45
- // ============================================================================
46
- // Context Types
47
- // ============================================================================
48
-
49
- export interface ContactFormContextValue extends UseContactFormReturn {
50
- /** API base URL */
51
- apiUrl: string;
52
- }
53
-
54
- // ============================================================================
55
- // Form Texts
56
- // ============================================================================
57
-
58
- export interface ContactFormTexts {
59
- title?: string;
60
- description?: string;
61
- submitText?: string;
62
- loadingText?: string;
63
- successTitle?: string;
64
- successMessage?: string;
65
- errorTitle?: string;
66
- errorMessage?: string;
67
- }
68
-
69
- export const DEFAULT_FORM_TEXTS: Required<ContactFormTexts> = {
70
- title: 'Send us a message',
71
- description: "Fill out the form below and we'll get back to you as soon as possible",
72
- submitText: 'Send Message',
73
- loadingText: 'Sending...',
74
- successTitle: 'Message Sent!',
75
- successMessage: "We'll get back to you within 24 hours.",
76
- errorTitle: 'Error',
77
- errorMessage: 'Failed to send message. Please try again.',
78
- };
79
-
80
- // ============================================================================
81
- // Contact Info Types
82
- // ============================================================================
83
-
84
- export interface ContactDetail {
85
- icon: ReactNode;
86
- label: string;
87
- value: string;
88
- href?: string;
89
- external?: boolean;
90
- }
91
-
92
- export interface ContactAction {
93
- icon: ReactNode;
94
- label: string;
95
- href: string;
96
- external?: boolean;
97
- }
98
-
99
- export interface ContactInfoProps {
100
- details: ContactDetail[];
101
- title?: string;
102
- action?: {
103
- title: string;
104
- description?: string;
105
- button: ContactAction;
106
- };
107
- className?: string;
108
- }
109
-
110
- export const DEFAULT_INFO_TITLE = 'Contact Information';