@businessflow/reviews 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 +97 -0
- package/dist/client/index.d.mts +245 -0
- package/dist/client/index.d.ts +245 -0
- package/dist/client/index.js +400 -0
- package/dist/client/index.js.map +1 -0
- package/dist/client/index.mjs +368 -0
- package/dist/client/index.mjs.map +1 -0
- package/dist/index.d.mts +239 -0
- package/dist/index.d.ts +239 -0
- package/dist/index.js +1381 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +1349 -0
- package/dist/index.mjs.map +1 -0
- package/dist/react/index.d.mts +311 -0
- package/dist/react/index.d.ts +311 -0
- package/dist/react/index.js +1390 -0
- package/dist/react/index.js.map +1 -0
- package/dist/react/index.mjs +1355 -0
- package/dist/react/index.mjs.map +1 -0
- package/dist/server/index.d.mts +189 -0
- package/dist/server/index.d.ts +189 -0
- package/dist/server/index.js +402 -0
- package/dist/server/index.js.map +1 -0
- package/dist/server/index.mjs +369 -0
- package/dist/server/index.mjs.map +1 -0
- package/dist/types/index.d.mts +141 -0
- package/dist/types/index.d.ts +141 -0
- package/dist/types/index.js +19 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/index.mjs +1 -0
- package/dist/types/index.mjs.map +1 -0
- package/package.json +104 -0
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
interface Review {
|
|
4
|
+
id: string | number;
|
|
5
|
+
reviewerName: string;
|
|
6
|
+
reviewerEmail?: string;
|
|
7
|
+
reviewerTitle?: string;
|
|
8
|
+
reviewerCompany?: string;
|
|
9
|
+
reviewerAvatar?: string;
|
|
10
|
+
rating: number;
|
|
11
|
+
content?: string;
|
|
12
|
+
createdAt: string | Date;
|
|
13
|
+
updatedAt?: string | Date;
|
|
14
|
+
featured?: boolean;
|
|
15
|
+
approved?: boolean;
|
|
16
|
+
metadata?: Record<string, any>;
|
|
17
|
+
}
|
|
18
|
+
interface ReviewFormData {
|
|
19
|
+
reviewerName: string;
|
|
20
|
+
reviewerEmail: string;
|
|
21
|
+
reviewerTitle?: string;
|
|
22
|
+
reviewerCompany?: string;
|
|
23
|
+
rating: number;
|
|
24
|
+
content?: string;
|
|
25
|
+
[key: string]: any;
|
|
26
|
+
}
|
|
27
|
+
interface ReviewApiResponse {
|
|
28
|
+
success: boolean;
|
|
29
|
+
message?: string;
|
|
30
|
+
reviewId?: string | number;
|
|
31
|
+
status?: 'pending' | 'approved' | 'rejected';
|
|
32
|
+
data?: any;
|
|
33
|
+
}
|
|
34
|
+
interface ReviewSubmissionConfig {
|
|
35
|
+
endpoint: string;
|
|
36
|
+
method?: 'POST' | 'PUT';
|
|
37
|
+
headers?: Record<string, string>;
|
|
38
|
+
maxRetries?: number;
|
|
39
|
+
timeout?: number;
|
|
40
|
+
transformRequest?: (data: ReviewFormData) => any;
|
|
41
|
+
transformResponse?: (response: any) => ReviewApiResponse;
|
|
42
|
+
onSuccess?: (response: ReviewApiResponse) => void;
|
|
43
|
+
onError?: (error: Error) => void;
|
|
44
|
+
}
|
|
45
|
+
interface ReviewFetchConfig {
|
|
46
|
+
endpoint: string;
|
|
47
|
+
method?: 'GET';
|
|
48
|
+
headers?: Record<string, string>;
|
|
49
|
+
maxRetries?: number;
|
|
50
|
+
timeout?: number;
|
|
51
|
+
params?: {
|
|
52
|
+
limit?: number;
|
|
53
|
+
offset?: number;
|
|
54
|
+
featured?: boolean;
|
|
55
|
+
minRating?: number;
|
|
56
|
+
sortBy?: 'date' | 'rating' | 'name';
|
|
57
|
+
sortOrder?: 'asc' | 'desc';
|
|
58
|
+
};
|
|
59
|
+
transformResponse?: (response: any) => Review[];
|
|
60
|
+
onError?: (error: Error) => void;
|
|
61
|
+
}
|
|
62
|
+
interface ReviewFieldConfig {
|
|
63
|
+
show?: boolean;
|
|
64
|
+
required?: boolean;
|
|
65
|
+
label?: string;
|
|
66
|
+
type?: string;
|
|
67
|
+
placeholder?: string;
|
|
68
|
+
validation?: ValidationRule[];
|
|
69
|
+
}
|
|
70
|
+
interface ReviewFormConfig {
|
|
71
|
+
fields?: {
|
|
72
|
+
reviewerName?: ReviewFieldConfig;
|
|
73
|
+
reviewerEmail?: ReviewFieldConfig;
|
|
74
|
+
reviewerTitle?: ReviewFieldConfig;
|
|
75
|
+
reviewerCompany?: ReviewFieldConfig;
|
|
76
|
+
rating?: ReviewFieldConfig;
|
|
77
|
+
content?: ReviewFieldConfig;
|
|
78
|
+
[key: string]: ReviewFieldConfig | undefined;
|
|
79
|
+
};
|
|
80
|
+
recaptcha?: {
|
|
81
|
+
siteKey: string;
|
|
82
|
+
action?: string;
|
|
83
|
+
};
|
|
84
|
+
styling?: {
|
|
85
|
+
className?: string;
|
|
86
|
+
fieldClassName?: string;
|
|
87
|
+
buttonClassName?: string;
|
|
88
|
+
errorClassName?: string;
|
|
89
|
+
starClassName?: string;
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
interface TestimonialDisplayConfig {
|
|
93
|
+
limit?: number;
|
|
94
|
+
showRating?: boolean;
|
|
95
|
+
showDate?: boolean;
|
|
96
|
+
showAvatar?: boolean;
|
|
97
|
+
showCompany?: boolean;
|
|
98
|
+
showTitle?: boolean;
|
|
99
|
+
layout?: 'grid' | 'list' | 'slider';
|
|
100
|
+
columns?: number;
|
|
101
|
+
autoRefresh?: boolean;
|
|
102
|
+
refreshInterval?: number;
|
|
103
|
+
styling?: {
|
|
104
|
+
containerClassName?: string;
|
|
105
|
+
cardClassName?: string;
|
|
106
|
+
headerClassName?: string;
|
|
107
|
+
contentClassName?: string;
|
|
108
|
+
ratingClassName?: string;
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
interface ValidationRule {
|
|
112
|
+
type: 'required' | 'email' | 'minLength' | 'maxLength' | 'pattern' | 'custom' | 'rating';
|
|
113
|
+
value?: string | number;
|
|
114
|
+
message?: string;
|
|
115
|
+
validator?: (value: any) => boolean | string;
|
|
116
|
+
}
|
|
117
|
+
interface ValidationErrors {
|
|
118
|
+
[field: string]: string;
|
|
119
|
+
}
|
|
120
|
+
interface ReviewHandlerConfig {
|
|
121
|
+
onFetch?: (params?: any) => Promise<Review[]>;
|
|
122
|
+
onSubmit?: (data: ReviewFormData) => Promise<ReviewApiResponse>;
|
|
123
|
+
recaptcha?: {
|
|
124
|
+
secretKey: string;
|
|
125
|
+
minimumScore?: number;
|
|
126
|
+
};
|
|
127
|
+
validation?: {
|
|
128
|
+
[field: string]: ValidationRule[];
|
|
129
|
+
};
|
|
130
|
+
onSuccess?: (data: ReviewFormData, response: ReviewApiResponse) => Promise<void>;
|
|
131
|
+
onError?: (data: ReviewFormData, error: Error) => Promise<void>;
|
|
132
|
+
rateLimiter?: (request: Request) => Promise<boolean>;
|
|
133
|
+
}
|
|
134
|
+
interface StarRatingProps {
|
|
135
|
+
rating: number;
|
|
136
|
+
maxRating?: number;
|
|
137
|
+
editable?: boolean;
|
|
138
|
+
onChange?: (rating: number) => void;
|
|
139
|
+
size?: 'small' | 'medium' | 'large';
|
|
140
|
+
className?: string;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Generic review client for fetching and submitting reviews
|
|
145
|
+
*/
|
|
146
|
+
declare class ReviewClient {
|
|
147
|
+
private fetchConfig?;
|
|
148
|
+
private submitConfig?;
|
|
149
|
+
private maxRetries;
|
|
150
|
+
private timeout;
|
|
151
|
+
constructor(config: {
|
|
152
|
+
fetchConfig?: ReviewFetchConfig;
|
|
153
|
+
submitConfig?: ReviewSubmissionConfig;
|
|
154
|
+
maxRetries?: number;
|
|
155
|
+
timeout?: number;
|
|
156
|
+
});
|
|
157
|
+
/**
|
|
158
|
+
* Fetch reviews with retry logic
|
|
159
|
+
*/
|
|
160
|
+
fetchReviews(params?: {
|
|
161
|
+
limit?: number;
|
|
162
|
+
offset?: number;
|
|
163
|
+
featured?: boolean;
|
|
164
|
+
minRating?: number;
|
|
165
|
+
}, retryCount?: number): Promise<Review[]>;
|
|
166
|
+
/**
|
|
167
|
+
* Submit a review with retry logic
|
|
168
|
+
*/
|
|
169
|
+
submitReview(reviewData: ReviewFormData, retryCount?: number): Promise<ReviewApiResponse>;
|
|
170
|
+
/**
|
|
171
|
+
* Update configuration
|
|
172
|
+
*/
|
|
173
|
+
updateConfig(config: {
|
|
174
|
+
fetchConfig?: Partial<ReviewFetchConfig>;
|
|
175
|
+
submitConfig?: Partial<ReviewSubmissionConfig>;
|
|
176
|
+
}): void;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
interface ReviewSubmissionState {
|
|
180
|
+
isSubmitting: boolean;
|
|
181
|
+
isSuccess: boolean;
|
|
182
|
+
error: string | null;
|
|
183
|
+
retryCount: number;
|
|
184
|
+
canRetry: boolean;
|
|
185
|
+
lastSubmissionTime: number | null;
|
|
186
|
+
}
|
|
187
|
+
interface UseReviewSubmissionConfig extends ReviewFormConfig {
|
|
188
|
+
endpoint?: string;
|
|
189
|
+
onSubmit?: (data: ReviewFormData) => Promise<ReviewApiResponse>;
|
|
190
|
+
onSuccess?: (response: ReviewApiResponse) => void;
|
|
191
|
+
onError?: (error: Error) => void;
|
|
192
|
+
maxRetries?: number;
|
|
193
|
+
}
|
|
194
|
+
interface UseReviewSubmissionReturn {
|
|
195
|
+
formData: ReviewFormData;
|
|
196
|
+
errors: ValidationErrors;
|
|
197
|
+
state: ReviewSubmissionState;
|
|
198
|
+
handleChange: (field: keyof ReviewFormData, value: any) => void;
|
|
199
|
+
handleSubmit: () => Promise<void>;
|
|
200
|
+
handleRetry: () => Promise<void>;
|
|
201
|
+
resetForm: () => void;
|
|
202
|
+
client: ReviewClient | null;
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Generic review submission hook with validation, submission, and retry logic
|
|
206
|
+
*/
|
|
207
|
+
declare const useReviewSubmission: (config: UseReviewSubmissionConfig) => UseReviewSubmissionReturn;
|
|
208
|
+
|
|
209
|
+
interface ReviewFormProps extends UseReviewSubmissionConfig {
|
|
210
|
+
className?: string;
|
|
211
|
+
children?: React.ReactNode;
|
|
212
|
+
renderField?: (field: {
|
|
213
|
+
name: keyof ReviewFormData;
|
|
214
|
+
label: string;
|
|
215
|
+
value: any;
|
|
216
|
+
type: string;
|
|
217
|
+
required: boolean;
|
|
218
|
+
placeholder?: string;
|
|
219
|
+
error?: string;
|
|
220
|
+
onChange: (value: any) => void;
|
|
221
|
+
}) => React.ReactNode;
|
|
222
|
+
renderSubmitButton?: (props: {
|
|
223
|
+
isSubmitting: boolean;
|
|
224
|
+
isDisabled: boolean;
|
|
225
|
+
onClick: () => void;
|
|
226
|
+
}) => React.ReactNode;
|
|
227
|
+
renderSuccess?: (message: string) => React.ReactNode;
|
|
228
|
+
renderError?: (error: string, canRetry: boolean, onRetry: () => void) => React.ReactNode;
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Generic review form component with customizable rendering
|
|
232
|
+
*/
|
|
233
|
+
declare const ReviewForm: React.FC<ReviewFormProps>;
|
|
234
|
+
declare const defaultReviewFormStyles = "\n .review-form .form-field {\n margin-bottom: 1rem;\n }\n\n .review-form .form-label {\n display: block;\n margin-bottom: 0.25rem;\n font-weight: 500;\n }\n\n .review-form .required {\n color: #e53e3e;\n margin-left: 0.25rem;\n }\n\n .review-form .form-input {\n width: 100%;\n padding: 0.75rem;\n border: 1px solid #d1d5db;\n border-radius: 4px;\n font-size: 1rem;\n transition: border-color 0.2s;\n }\n\n .review-form .form-input:focus {\n outline: none;\n border-color: #3b82f6;\n box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);\n }\n\n .review-form .form-input.error {\n border-color: #e53e3e;\n }\n\n .review-form .rating-field {\n display: flex;\n align-items: center;\n gap: 1rem;\n }\n\n .review-form .rating-label {\n font-size: 0.875rem;\n color: #6b7280;\n }\n\n .review-form .form-error {\n color: #e53e3e;\n font-size: 0.875rem;\n margin-top: 0.25rem;\n display: block;\n }\n\n .review-form .general-error {\n background-color: #fee2e2;\n border: 1px solid #fecaca;\n border-radius: 4px;\n padding: 1rem;\n margin-bottom: 1rem;\n }\n\n .review-form .general-error p {\n margin: 0 0 0.5rem 0;\n }\n\n .review-form .general-error button {\n background-color: #ef4444;\n color: white;\n padding: 0.5rem 1rem;\n border: none;\n border-radius: 4px;\n cursor: pointer;\n }\n\n .review-form .submit-button {\n background-color: #3b82f6;\n color: white;\n padding: 0.75rem 1.5rem;\n border: none;\n border-radius: 4px;\n font-size: 1rem;\n cursor: pointer;\n transition: background-color 0.2s;\n width: 100%;\n }\n\n .review-form .submit-button:hover:not(:disabled) {\n background-color: #2563eb;\n }\n\n .review-form .submit-button:disabled {\n opacity: 0.6;\n cursor: not-allowed;\n }\n\n .success-message {\n text-align: center;\n padding: 2rem 1rem;\n background-color: #f0fdf4;\n border: 1px solid #bbf7d0;\n border-radius: 8px;\n }\n\n .success-message h3 {\n color: #059669;\n margin-bottom: 1rem;\n }\n\n .success-message button {\n background-color: #6b7280;\n color: white;\n padding: 0.5rem 1rem;\n border: none;\n border-radius: 4px;\n cursor: pointer;\n margin-top: 1rem;\n }\n";
|
|
235
|
+
|
|
236
|
+
interface TestimonialsSectionProps extends TestimonialDisplayConfig {
|
|
237
|
+
fetchConfig: ReviewFetchConfig;
|
|
238
|
+
className?: string;
|
|
239
|
+
renderTestimonial?: (testimonial: Review) => React.ReactNode;
|
|
240
|
+
renderLoading?: () => React.ReactNode;
|
|
241
|
+
renderError?: (error: string, canRetry: boolean, onRetry: () => void) => React.ReactNode;
|
|
242
|
+
renderEmpty?: () => React.ReactNode;
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* Generic testimonials display component
|
|
246
|
+
*/
|
|
247
|
+
declare const TestimonialsSection: React.FC<TestimonialsSectionProps>;
|
|
248
|
+
declare const defaultTestimonialsStyles = "\n .testimonials-section {\n width: 100%;\n }\n\n .testimonials-grid {\n display: grid;\n gap: 1.5rem;\n }\n\n .testimonials-grid.columns-1 {\n grid-template-columns: 1fr;\n }\n\n .testimonials-grid.columns-2 {\n grid-template-columns: repeat(2, 1fr);\n }\n\n .testimonials-grid.columns-3 {\n grid-template-columns: repeat(3, 1fr);\n }\n\n .testimonials-grid.columns-4 {\n grid-template-columns: repeat(4, 1fr);\n }\n\n @media (max-width: 768px) {\n .testimonials-grid.columns-3,\n .testimonials-grid.columns-4 {\n grid-template-columns: 1fr;\n }\n \n .testimonials-grid.columns-2 {\n grid-template-columns: 1fr;\n }\n }\n\n @media (max-width: 1024px) and (min-width: 769px) {\n .testimonials-grid.columns-3,\n .testimonials-grid.columns-4 {\n grid-template-columns: repeat(2, 1fr);\n }\n }\n\n .testimonials-list {\n display: flex;\n flex-direction: column;\n gap: 1.5rem;\n }\n\n .testimonials-slider {\n display: flex;\n overflow-x: auto;\n gap: 1.5rem;\n padding-bottom: 1rem;\n scrollbar-width: thin;\n }\n\n .testimonials-slider::-webkit-scrollbar {\n height: 8px;\n }\n\n .testimonials-slider::-webkit-scrollbar-track {\n background: #f1f5f9;\n border-radius: 4px;\n }\n\n .testimonials-slider::-webkit-scrollbar-thumb {\n background: #cbd5e1;\n border-radius: 4px;\n }\n\n .testimonials-slider .testimonial-card {\n flex: 0 0 300px;\n }\n\n .testimonial-card {\n background: white;\n border: 1px solid #e2e8f0;\n border-radius: 8px;\n padding: 1.5rem;\n box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);\n transition: box-shadow 0.2s ease;\n }\n\n .testimonial-card:hover {\n box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);\n }\n\n .testimonial-header {\n display: flex;\n justify-content: space-between;\n align-items: flex-start;\n margin-bottom: 1rem;\n }\n\n .reviewer-info {\n display: flex;\n gap: 0.75rem;\n align-items: center;\n }\n\n .reviewer-avatar {\n width: 48px;\n height: 48px;\n border-radius: 50%;\n object-fit: cover;\n }\n\n .reviewer-name {\n margin: 0 0 0.25rem 0;\n font-size: 1rem;\n font-weight: 600;\n color: #1f2937;\n }\n\n .reviewer-title,\n .reviewer-company {\n margin: 0;\n font-size: 0.875rem;\n color: #6b7280;\n }\n\n .testimonial-content {\n margin-bottom: 1rem;\n }\n\n .testimonial-content blockquote {\n margin: 0;\n font-style: italic;\n color: #374151;\n line-height: 1.6;\n }\n\n .testimonial-date {\n font-size: 0.75rem;\n color: #9ca3af;\n text-align: right;\n }\n\n .loading-spinner {\n text-align: center;\n padding: 3rem;\n color: #6b7280;\n }\n\n .error-message {\n text-align: center;\n padding: 3rem;\n color: #ef4444;\n }\n\n .retry-button {\n background-color: #3b82f6;\n color: white;\n border: none;\n padding: 0.5rem 1rem;\n border-radius: 4px;\n cursor: pointer;\n margin-top: 1rem;\n }\n\n .retry-button:hover {\n background-color: #2563eb;\n }\n\n .refresh-indicator {\n text-align: center;\n padding: 1rem;\n font-size: 0.875rem;\n color: #6b7280;\n }\n\n .empty {\n text-align: center;\n padding: 3rem;\n color: #6b7280;\n }\n";
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Star rating component for displaying and selecting ratings
|
|
252
|
+
*/
|
|
253
|
+
declare const StarRating: React.FC<StarRatingProps>;
|
|
254
|
+
declare const defaultStarRatingStyles = "\n .star-rating {\n display: inline-flex;\n gap: 2px;\n }\n\n .star {\n display: inline-block;\n cursor: default;\n user-select: none;\n transition: color 0.2s ease;\n }\n\n .star.editable {\n cursor: pointer;\n }\n\n .star.editable:hover {\n transform: scale(1.1);\n }\n\n .star.editable:focus {\n outline: 2px solid #3b82f6;\n outline-offset: 2px;\n border-radius: 2px;\n }\n\n .star.small {\n font-size: 1rem;\n }\n\n .star.medium {\n font-size: 1.5rem;\n }\n\n .star.large {\n font-size: 2rem;\n }\n\n .star.filled {\n color: #fbbf24;\n }\n\n .star.empty {\n color: #d1d5db;\n }\n\n .star.editable.empty:hover {\n color: #fbbf24;\n }\n";
|
|
255
|
+
|
|
256
|
+
interface TestimonialsState {
|
|
257
|
+
testimonials: Review[];
|
|
258
|
+
isLoading: boolean;
|
|
259
|
+
error: string | null;
|
|
260
|
+
retryCount: number;
|
|
261
|
+
canRetry: boolean;
|
|
262
|
+
lastFetchTime: number | null;
|
|
263
|
+
}
|
|
264
|
+
interface UseTestimonialsConfig {
|
|
265
|
+
fetchConfig: ReviewFetchConfig;
|
|
266
|
+
limit?: number;
|
|
267
|
+
featured?: boolean;
|
|
268
|
+
minRating?: number;
|
|
269
|
+
autoRefresh?: boolean;
|
|
270
|
+
refreshInterval?: number;
|
|
271
|
+
maxRetries?: number;
|
|
272
|
+
}
|
|
273
|
+
interface UseTestimonialsReturn {
|
|
274
|
+
testimonials: Review[];
|
|
275
|
+
isLoading: boolean;
|
|
276
|
+
error: string | null;
|
|
277
|
+
retryCount: number;
|
|
278
|
+
canRetry: boolean;
|
|
279
|
+
refetch: () => Promise<void>;
|
|
280
|
+
handleRetry: () => Promise<void>;
|
|
281
|
+
}
|
|
282
|
+
/**
|
|
283
|
+
* Generic hook for fetching and managing testimonials/reviews
|
|
284
|
+
*/
|
|
285
|
+
declare const useTestimonials: (config: UseTestimonialsConfig) => UseTestimonialsReturn;
|
|
286
|
+
|
|
287
|
+
interface UseRecaptchaConfig {
|
|
288
|
+
siteKey: string;
|
|
289
|
+
action?: string;
|
|
290
|
+
}
|
|
291
|
+
interface UseRecaptchaReturn {
|
|
292
|
+
executeRecaptcha: () => Promise<string>;
|
|
293
|
+
isLoaded: boolean;
|
|
294
|
+
error: string | null;
|
|
295
|
+
}
|
|
296
|
+
declare global {
|
|
297
|
+
interface Window {
|
|
298
|
+
grecaptcha: {
|
|
299
|
+
ready: (callback: () => void) => void;
|
|
300
|
+
execute: (siteKey: string, options: {
|
|
301
|
+
action: string;
|
|
302
|
+
}) => Promise<string>;
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
/**
|
|
307
|
+
* Generic reCAPTCHA hook for form submissions
|
|
308
|
+
*/
|
|
309
|
+
declare const useRecaptcha: (config: UseRecaptchaConfig) => UseRecaptchaReturn;
|
|
310
|
+
|
|
311
|
+
export { type Review, type ReviewApiResponse, type ReviewFetchConfig, type ReviewFieldConfig, ReviewForm, type ReviewFormConfig, type ReviewFormData, type ReviewFormProps, type ReviewHandlerConfig, type ReviewSubmissionConfig, type ReviewSubmissionState, StarRating, type StarRatingProps, type TestimonialDisplayConfig, TestimonialsSection, type TestimonialsSectionProps, type TestimonialsState, type UseRecaptchaConfig, type UseRecaptchaReturn, type UseReviewSubmissionConfig, type UseReviewSubmissionReturn, type UseTestimonialsConfig, type UseTestimonialsReturn, type ValidationErrors, type ValidationRule, defaultReviewFormStyles, defaultStarRatingStyles, defaultTestimonialsStyles, useRecaptcha, useReviewSubmission, useTestimonials };
|