ayezee-astro-cms 1.1.0 → 1.2.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/dist/components/AyezeeForm.astro +175 -0
- package/dist/components/index.d.ts +7 -0
- package/dist/components/index.js +8 -0
- package/dist/form-handler.d.ts +80 -0
- package/dist/form-handler.js +289 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +2 -0
- package/package.json +8 -2
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
---
|
|
2
|
+
/**
|
|
3
|
+
* AyezeeForm Component
|
|
4
|
+
*
|
|
5
|
+
* A flexible form component for AyeZee CMS that handles form submission,
|
|
6
|
+
* Turnstile integration, and validation. Fully customizable via slots and props.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```astro
|
|
10
|
+
* <AyezeeForm label="Contact Form" formClass="my-form" submitButtonClass="btn-primary">
|
|
11
|
+
* <input type="text" name="name" required />
|
|
12
|
+
* <input type="email" name="email" required />
|
|
13
|
+
* <textarea name="message" required></textarea>
|
|
14
|
+
* </AyezeeForm>
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import { getFormConfig } from '../cms-helper.js';
|
|
19
|
+
|
|
20
|
+
export interface Props {
|
|
21
|
+
/** Module label, slug, or instance key to identify the form */
|
|
22
|
+
label?: string;
|
|
23
|
+
/** Instance key (alternative to label) */
|
|
24
|
+
instanceKey?: string;
|
|
25
|
+
/** Text for the submit button */
|
|
26
|
+
submitButtonText?: string;
|
|
27
|
+
/** CSS classes for the submit button */
|
|
28
|
+
submitButtonClass?: string;
|
|
29
|
+
/** CSS classes for the form element */
|
|
30
|
+
formClass?: string;
|
|
31
|
+
/** Success message to display */
|
|
32
|
+
successMessage?: string;
|
|
33
|
+
/** Error message to display */
|
|
34
|
+
errorMessage?: string;
|
|
35
|
+
/** CSS classes for the message container */
|
|
36
|
+
messageClass?: string;
|
|
37
|
+
/** CSS classes for the Turnstile container */
|
|
38
|
+
turnstileClass?: string;
|
|
39
|
+
/** Whether to show the default submit button (set to false if using custom button in slot) */
|
|
40
|
+
showSubmitButton?: boolean;
|
|
41
|
+
/** Whether to show the default message div (set to false if handling messages externally) */
|
|
42
|
+
showMessageDiv?: boolean;
|
|
43
|
+
/** Additional data attributes to add to the form */
|
|
44
|
+
dataAttributes?: Record<string, string>;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const {
|
|
48
|
+
label,
|
|
49
|
+
instanceKey,
|
|
50
|
+
submitButtonText = 'Submit',
|
|
51
|
+
submitButtonClass = '',
|
|
52
|
+
formClass = '',
|
|
53
|
+
successMessage = 'Form submitted successfully!',
|
|
54
|
+
errorMessage = 'Failed to submit form. Please try again.',
|
|
55
|
+
messageClass = '',
|
|
56
|
+
turnstileClass = '',
|
|
57
|
+
showSubmitButton = true,
|
|
58
|
+
showMessageDiv = true,
|
|
59
|
+
dataAttributes = {},
|
|
60
|
+
} = Astro.props;
|
|
61
|
+
|
|
62
|
+
// Get form configuration (includes Turnstile settings)
|
|
63
|
+
const moduleIdentifier = instanceKey || label;
|
|
64
|
+
if (!moduleIdentifier) {
|
|
65
|
+
throw new Error('AyezeeForm requires either "label" or "instanceKey" prop');
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const formConfig = getFormConfig(moduleIdentifier);
|
|
69
|
+
|
|
70
|
+
if (!formConfig.module) {
|
|
71
|
+
throw new Error(`Form module "${moduleIdentifier}" not found`);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Generate unique form ID
|
|
75
|
+
const formId = `ayezee-form-${formConfig.module.instanceKey}`;
|
|
76
|
+
|
|
77
|
+
// Pass config to client
|
|
78
|
+
const clientConfig = {
|
|
79
|
+
formId,
|
|
80
|
+
apiUrl: formConfig.apiUrl,
|
|
81
|
+
apiKey: formConfig.apiKey,
|
|
82
|
+
turnstile: formConfig.turnstile,
|
|
83
|
+
successMessage,
|
|
84
|
+
errorMessage,
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
// Build data attributes
|
|
88
|
+
const dataAttrs = {
|
|
89
|
+
'data-ayezee-form': '',
|
|
90
|
+
...dataAttributes,
|
|
91
|
+
};
|
|
92
|
+
---
|
|
93
|
+
|
|
94
|
+
<form id={formId} class={formClass} {...dataAttrs}>
|
|
95
|
+
<!-- Honeypot field (hidden from users, catches bots) -->
|
|
96
|
+
<input
|
|
97
|
+
type="text"
|
|
98
|
+
name="website"
|
|
99
|
+
class="absolute -left-[9999px] opacity-0 pointer-events-none"
|
|
100
|
+
tabindex="-1"
|
|
101
|
+
autocomplete="off"
|
|
102
|
+
aria-hidden="true"
|
|
103
|
+
/>
|
|
104
|
+
|
|
105
|
+
<!-- Form fields from slot -->
|
|
106
|
+
<slot />
|
|
107
|
+
|
|
108
|
+
<!-- Turnstile container (only rendered if enabled) -->
|
|
109
|
+
{
|
|
110
|
+
formConfig.turnstile.enabled && (
|
|
111
|
+
<div id={`${formId}-turnstile`} class={turnstileClass || 'turnstile-container my-4'} />
|
|
112
|
+
)
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
<!-- Message container -->
|
|
116
|
+
{
|
|
117
|
+
showMessageDiv && (
|
|
118
|
+
<div
|
|
119
|
+
id={`${formId}-message`}
|
|
120
|
+
class={messageClass || 'hidden mt-2 p-3 border rounded text-sm'}
|
|
121
|
+
role="alert"
|
|
122
|
+
aria-live="polite"
|
|
123
|
+
/>
|
|
124
|
+
)
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
<!-- Submit button -->
|
|
128
|
+
{
|
|
129
|
+
showSubmitButton && (
|
|
130
|
+
<button
|
|
131
|
+
type="submit"
|
|
132
|
+
id={`${formId}-submit`}
|
|
133
|
+
class={
|
|
134
|
+
submitButtonClass ||
|
|
135
|
+
'bg-blue-600 text-white px-6 py-3 rounded hover:bg-blue-700 disabled:opacity-50 disabled:cursor-not-allowed'
|
|
136
|
+
}
|
|
137
|
+
>
|
|
138
|
+
<span class="button-text">{submitButtonText}</span>
|
|
139
|
+
<span class="button-loading hidden">Submitting...</span>
|
|
140
|
+
</button>
|
|
141
|
+
)
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
<!-- Slot for custom submit button or additional form elements -->
|
|
145
|
+
<slot name="after-form" />
|
|
146
|
+
</form>
|
|
147
|
+
|
|
148
|
+
<!-- Load Turnstile script if enabled -->
|
|
149
|
+
{
|
|
150
|
+
formConfig.turnstile.enabled && (
|
|
151
|
+
<script is:inline src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer />
|
|
152
|
+
)
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
<!-- Pass config to client-side script -->
|
|
156
|
+
<script is:inline define:vars={{ clientConfig }}>
|
|
157
|
+
window[`FORM_CONFIG_${clientConfig.formId}`] = clientConfig;
|
|
158
|
+
</script>
|
|
159
|
+
|
|
160
|
+
<!-- Form handler script -->
|
|
161
|
+
<script>
|
|
162
|
+
import { AyezeeFormHandler, initAyezeeForms } from '../form-handler.js';
|
|
163
|
+
|
|
164
|
+
// Initialize all forms on the page
|
|
165
|
+
function initForms() {
|
|
166
|
+
initAyezeeForms();
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Init when DOM is ready
|
|
170
|
+
if (document.readyState === 'loading') {
|
|
171
|
+
document.addEventListener('DOMContentLoaded', initForms);
|
|
172
|
+
} else {
|
|
173
|
+
initForms();
|
|
174
|
+
}
|
|
175
|
+
</script>
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AyeZee CMS Components
|
|
3
|
+
*
|
|
4
|
+
* Export Astro components for use in projects
|
|
5
|
+
*/
|
|
6
|
+
// Note: Astro components are exported individually, not through this index
|
|
7
|
+
// Import them directly like: import AyezeeForm from 'ayezee-astro-cms/components/AyezeeForm.astro'
|
|
8
|
+
export { AyezeeFormHandler, initAyezeeForms, submitToAyezeeCms } from '../form-handler.js';
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Form Handler Utilities for AyeZee CMS Forms
|
|
3
|
+
*
|
|
4
|
+
* Provides core form submission logic, Turnstile integration, and error handling
|
|
5
|
+
* that can be used in custom form implementations or with the default AyezeeForm component
|
|
6
|
+
*/
|
|
7
|
+
export interface FormHandlerConfig {
|
|
8
|
+
formId: string;
|
|
9
|
+
apiUrl: string;
|
|
10
|
+
apiKey: string;
|
|
11
|
+
turnstile: {
|
|
12
|
+
enabled: boolean;
|
|
13
|
+
siteKey: string | null;
|
|
14
|
+
};
|
|
15
|
+
successMessage?: string;
|
|
16
|
+
errorMessage?: string;
|
|
17
|
+
onSuccess?: (data: any) => void;
|
|
18
|
+
onError?: (error: string) => void;
|
|
19
|
+
onSubmitStart?: () => void;
|
|
20
|
+
onSubmitEnd?: () => void;
|
|
21
|
+
}
|
|
22
|
+
export interface FormSubmissionResult {
|
|
23
|
+
success: boolean;
|
|
24
|
+
message: string;
|
|
25
|
+
data?: any;
|
|
26
|
+
errors?: string[];
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Core form handler class that manages form submission, validation, and Turnstile integration
|
|
30
|
+
*/
|
|
31
|
+
export declare class AyezeeFormHandler {
|
|
32
|
+
private form;
|
|
33
|
+
private submitButton;
|
|
34
|
+
private messageDiv;
|
|
35
|
+
private turnstileContainer;
|
|
36
|
+
private turnstileWidgetId;
|
|
37
|
+
private config;
|
|
38
|
+
constructor(config: FormHandlerConfig);
|
|
39
|
+
private init;
|
|
40
|
+
private initTurnstile;
|
|
41
|
+
private handleSubmit;
|
|
42
|
+
/**
|
|
43
|
+
* Show a message in the message div
|
|
44
|
+
*/
|
|
45
|
+
showMessage(message: string, isSuccess: boolean): void;
|
|
46
|
+
/**
|
|
47
|
+
* Hide the message div
|
|
48
|
+
*/
|
|
49
|
+
hideMessage(): void;
|
|
50
|
+
/**
|
|
51
|
+
* Set the loading state of the form
|
|
52
|
+
*/
|
|
53
|
+
setLoading(isLoading: boolean): void;
|
|
54
|
+
/**
|
|
55
|
+
* Manually submit the form (useful for custom implementations)
|
|
56
|
+
*/
|
|
57
|
+
submit(customData?: Record<string, any>): Promise<FormSubmissionResult>;
|
|
58
|
+
/**
|
|
59
|
+
* Reset the form
|
|
60
|
+
*/
|
|
61
|
+
reset(): void;
|
|
62
|
+
/**
|
|
63
|
+
* Get form data as an object
|
|
64
|
+
*/
|
|
65
|
+
getFormData(): Record<string, any>;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Initialize all AyeZee forms on the page
|
|
69
|
+
* This automatically finds and initializes all forms with [data-ayezee-form] attribute
|
|
70
|
+
*/
|
|
71
|
+
export declare function initAyezeeForms(): void;
|
|
72
|
+
/**
|
|
73
|
+
* Standalone function to submit form data to AyeZee CMS API
|
|
74
|
+
* Useful for custom form implementations that don't use the default form handler
|
|
75
|
+
*/
|
|
76
|
+
export declare function submitToAyezeeCms(data: Record<string, any>, config: {
|
|
77
|
+
apiUrl: string;
|
|
78
|
+
apiKey: string;
|
|
79
|
+
turnstileToken?: string;
|
|
80
|
+
}): Promise<FormSubmissionResult>;
|
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Form Handler Utilities for AyeZee CMS Forms
|
|
3
|
+
*
|
|
4
|
+
* Provides core form submission logic, Turnstile integration, and error handling
|
|
5
|
+
* that can be used in custom form implementations or with the default AyezeeForm component
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Core form handler class that manages form submission, validation, and Turnstile integration
|
|
9
|
+
*/
|
|
10
|
+
export class AyezeeFormHandler {
|
|
11
|
+
form;
|
|
12
|
+
submitButton;
|
|
13
|
+
messageDiv;
|
|
14
|
+
turnstileContainer;
|
|
15
|
+
turnstileWidgetId = null;
|
|
16
|
+
config;
|
|
17
|
+
constructor(config) {
|
|
18
|
+
this.config = config;
|
|
19
|
+
this.form = document.getElementById(config.formId);
|
|
20
|
+
this.submitButton = document.getElementById(`${config.formId}-submit`);
|
|
21
|
+
this.messageDiv = document.getElementById(`${config.formId}-message`);
|
|
22
|
+
this.turnstileContainer = document.getElementById(`${config.formId}-turnstile`);
|
|
23
|
+
if (!this.form) {
|
|
24
|
+
console.error('Form not found:', config.formId);
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
this.init();
|
|
28
|
+
}
|
|
29
|
+
init() {
|
|
30
|
+
// Initialize Turnstile if enabled
|
|
31
|
+
if (this.config.turnstile.enabled && this.turnstileContainer) {
|
|
32
|
+
this.initTurnstile();
|
|
33
|
+
}
|
|
34
|
+
// Attach submit handler
|
|
35
|
+
this.form.addEventListener('submit', (e) => this.handleSubmit(e));
|
|
36
|
+
}
|
|
37
|
+
initTurnstile() {
|
|
38
|
+
if (!window.turnstile) {
|
|
39
|
+
console.warn('Turnstile script not loaded yet, waiting...');
|
|
40
|
+
setTimeout(() => this.initTurnstile(), 100);
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
this.turnstileWidgetId = window.turnstile.render(this.turnstileContainer, {
|
|
44
|
+
sitekey: this.config.turnstile.siteKey,
|
|
45
|
+
theme: 'light',
|
|
46
|
+
size: 'normal',
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
async handleSubmit(e) {
|
|
50
|
+
e.preventDefault();
|
|
51
|
+
this.hideMessage();
|
|
52
|
+
this.setLoading(true);
|
|
53
|
+
this.config.onSubmitStart?.();
|
|
54
|
+
try {
|
|
55
|
+
const formData = new FormData(this.form);
|
|
56
|
+
const data = {};
|
|
57
|
+
formData.forEach((value, key) => {
|
|
58
|
+
data[key] = value;
|
|
59
|
+
});
|
|
60
|
+
// Honeypot check
|
|
61
|
+
if (data.website) {
|
|
62
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
63
|
+
this.showMessage(this.config.successMessage || 'Form submitted successfully!', true);
|
|
64
|
+
this.form.reset();
|
|
65
|
+
this.config.onSuccess?.(data);
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
// Remove honeypot field before sending to API
|
|
69
|
+
delete data.website;
|
|
70
|
+
// Get Turnstile token if enabled
|
|
71
|
+
if (this.config.turnstile.enabled) {
|
|
72
|
+
const token = window.turnstile?.getResponse(this.turnstileWidgetId);
|
|
73
|
+
if (!token) {
|
|
74
|
+
this.showMessage('Please complete the security challenge.', false);
|
|
75
|
+
this.config.onError?.('Turnstile validation failed');
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
data['cf-turnstile-response'] = token;
|
|
79
|
+
}
|
|
80
|
+
// Submit to API
|
|
81
|
+
console.log('Submitting to:', this.config.apiUrl);
|
|
82
|
+
console.log('Data being sent:', data);
|
|
83
|
+
const response = await fetch(this.config.apiUrl, {
|
|
84
|
+
method: 'POST',
|
|
85
|
+
headers: {
|
|
86
|
+
'Content-Type': 'application/json',
|
|
87
|
+
Authorization: `Bearer ${this.config.apiKey}`,
|
|
88
|
+
},
|
|
89
|
+
body: JSON.stringify(data),
|
|
90
|
+
});
|
|
91
|
+
const result = await response.json();
|
|
92
|
+
console.log('API Response:', result);
|
|
93
|
+
if (response.ok && result.success) {
|
|
94
|
+
this.showMessage(this.config.successMessage || 'Form submitted successfully!', true);
|
|
95
|
+
this.form.reset();
|
|
96
|
+
// Reset Turnstile
|
|
97
|
+
if (this.turnstileWidgetId !== null) {
|
|
98
|
+
window.turnstile?.reset(this.turnstileWidgetId);
|
|
99
|
+
}
|
|
100
|
+
this.config.onSuccess?.(result);
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
console.error('Submission failed:', result);
|
|
104
|
+
// Parse validation errors
|
|
105
|
+
let errorMessage = this.config.errorMessage || 'Failed to submit form. Please try again.';
|
|
106
|
+
if (result.details) {
|
|
107
|
+
try {
|
|
108
|
+
const details = typeof result.details === 'string' ? JSON.parse(result.details) : result.details;
|
|
109
|
+
if (details.validationErrors && Array.isArray(details.validationErrors)) {
|
|
110
|
+
errorMessage = details.validationErrors.join('. ');
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
catch (e) {
|
|
114
|
+
console.error('Error parsing validation details:', e);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
if (!errorMessage || errorMessage === this.config.errorMessage) {
|
|
118
|
+
errorMessage = result.error || result.message || this.config.errorMessage || 'An error occurred';
|
|
119
|
+
}
|
|
120
|
+
this.showMessage(errorMessage, false);
|
|
121
|
+
this.config.onError?.(errorMessage);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
catch (error) {
|
|
125
|
+
console.error('Form submission error:', error);
|
|
126
|
+
const errorMsg = 'Network error. Please try again.';
|
|
127
|
+
this.showMessage(errorMsg, false);
|
|
128
|
+
this.config.onError?.(errorMsg);
|
|
129
|
+
}
|
|
130
|
+
finally {
|
|
131
|
+
this.setLoading(false);
|
|
132
|
+
this.config.onSubmitEnd?.();
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Show a message in the message div
|
|
137
|
+
*/
|
|
138
|
+
showMessage(message, isSuccess) {
|
|
139
|
+
if (!this.messageDiv)
|
|
140
|
+
return;
|
|
141
|
+
this.messageDiv.textContent = message;
|
|
142
|
+
this.messageDiv.classList.remove('hidden');
|
|
143
|
+
if (isSuccess) {
|
|
144
|
+
this.messageDiv.classList.remove('border-red-300', 'bg-red-50', 'text-red-700');
|
|
145
|
+
this.messageDiv.classList.add('border-green-300', 'bg-green-50', 'text-green-700');
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
this.messageDiv.classList.remove('border-green-300', 'bg-green-50', 'text-green-700');
|
|
149
|
+
this.messageDiv.classList.add('border-red-300', 'bg-red-50', 'text-red-700');
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Hide the message div
|
|
154
|
+
*/
|
|
155
|
+
hideMessage() {
|
|
156
|
+
if (!this.messageDiv)
|
|
157
|
+
return;
|
|
158
|
+
this.messageDiv.classList.add('hidden');
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Set the loading state of the form
|
|
162
|
+
*/
|
|
163
|
+
setLoading(isLoading) {
|
|
164
|
+
if (!this.submitButton)
|
|
165
|
+
return;
|
|
166
|
+
this.submitButton.disabled = isLoading;
|
|
167
|
+
const textSpan = this.submitButton.querySelector('.button-text');
|
|
168
|
+
const loadingSpan = this.submitButton.querySelector('.button-loading');
|
|
169
|
+
if (isLoading) {
|
|
170
|
+
textSpan?.classList.add('hidden');
|
|
171
|
+
loadingSpan?.classList.remove('hidden');
|
|
172
|
+
}
|
|
173
|
+
else {
|
|
174
|
+
textSpan?.classList.remove('hidden');
|
|
175
|
+
loadingSpan?.classList.add('hidden');
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Manually submit the form (useful for custom implementations)
|
|
180
|
+
*/
|
|
181
|
+
async submit(customData) {
|
|
182
|
+
// If custom data provided, temporarily populate form
|
|
183
|
+
if (customData) {
|
|
184
|
+
Object.entries(customData).forEach(([key, value]) => {
|
|
185
|
+
const input = this.form.querySelector(`[name="${key}"]`);
|
|
186
|
+
if (input) {
|
|
187
|
+
input.value = String(value);
|
|
188
|
+
}
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
// Trigger form submission
|
|
192
|
+
const event = new Event('submit', { cancelable: true });
|
|
193
|
+
this.form.dispatchEvent(event);
|
|
194
|
+
return {
|
|
195
|
+
success: true,
|
|
196
|
+
message: 'Form submitted',
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Reset the form
|
|
201
|
+
*/
|
|
202
|
+
reset() {
|
|
203
|
+
this.form.reset();
|
|
204
|
+
this.hideMessage();
|
|
205
|
+
if (this.turnstileWidgetId !== null) {
|
|
206
|
+
window.turnstile?.reset(this.turnstileWidgetId);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Get form data as an object
|
|
211
|
+
*/
|
|
212
|
+
getFormData() {
|
|
213
|
+
const formData = new FormData(this.form);
|
|
214
|
+
const data = {};
|
|
215
|
+
formData.forEach((value, key) => {
|
|
216
|
+
data[key] = value;
|
|
217
|
+
});
|
|
218
|
+
return data;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Initialize all AyeZee forms on the page
|
|
223
|
+
* This automatically finds and initializes all forms with [data-ayezee-form] attribute
|
|
224
|
+
*/
|
|
225
|
+
export function initAyezeeForms() {
|
|
226
|
+
const forms = document.querySelectorAll('[data-ayezee-form]');
|
|
227
|
+
forms.forEach((form) => {
|
|
228
|
+
const formId = form.id;
|
|
229
|
+
const config = window[`FORM_CONFIG_${formId}`];
|
|
230
|
+
if (config) {
|
|
231
|
+
new AyezeeFormHandler(config);
|
|
232
|
+
}
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Standalone function to submit form data to AyeZee CMS API
|
|
237
|
+
* Useful for custom form implementations that don't use the default form handler
|
|
238
|
+
*/
|
|
239
|
+
export async function submitToAyezeeCms(data, config) {
|
|
240
|
+
try {
|
|
241
|
+
const payload = { ...data };
|
|
242
|
+
if (config.turnstileToken) {
|
|
243
|
+
payload['cf-turnstile-response'] = config.turnstileToken;
|
|
244
|
+
}
|
|
245
|
+
const response = await fetch(config.apiUrl, {
|
|
246
|
+
method: 'POST',
|
|
247
|
+
headers: {
|
|
248
|
+
'Content-Type': 'application/json',
|
|
249
|
+
Authorization: `Bearer ${config.apiKey}`,
|
|
250
|
+
},
|
|
251
|
+
body: JSON.stringify(payload),
|
|
252
|
+
});
|
|
253
|
+
const result = await response.json();
|
|
254
|
+
if (response.ok && result.success) {
|
|
255
|
+
return {
|
|
256
|
+
success: true,
|
|
257
|
+
message: result.message || 'Form submitted successfully!',
|
|
258
|
+
data: result.data,
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
else {
|
|
262
|
+
// Parse validation errors
|
|
263
|
+
let errors = [];
|
|
264
|
+
if (result.details) {
|
|
265
|
+
try {
|
|
266
|
+
const details = typeof result.details === 'string' ? JSON.parse(result.details) : result.details;
|
|
267
|
+
if (details.validationErrors && Array.isArray(details.validationErrors)) {
|
|
268
|
+
errors = details.validationErrors;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
catch (e) {
|
|
272
|
+
console.error('Error parsing validation details:', e);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
return {
|
|
276
|
+
success: false,
|
|
277
|
+
message: result.error || result.message || 'Failed to submit form',
|
|
278
|
+
errors: errors.length > 0 ? errors : undefined,
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
catch (error) {
|
|
283
|
+
console.error('Form submission error:', error);
|
|
284
|
+
return {
|
|
285
|
+
success: false,
|
|
286
|
+
message: 'Network error. Please try again.',
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
}
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -9,6 +9,8 @@
|
|
|
9
9
|
export * from './cms-helper.js';
|
|
10
10
|
// Export Cloudinary utilities
|
|
11
11
|
export * from './cloudinary-utils.js';
|
|
12
|
+
// Export form handler utilities
|
|
13
|
+
export * from './form-handler.js';
|
|
12
14
|
// Export integration
|
|
13
15
|
export { ayezeeCms } from './integration.js';
|
|
14
16
|
// Default export
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ayezee-astro-cms",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "AyeZee CMS integration for Astro with automatic data fetching, form handling, and validation",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -17,6 +17,11 @@
|
|
|
17
17
|
"./components": {
|
|
18
18
|
"types": "./dist/components/index.d.ts",
|
|
19
19
|
"import": "./dist/components/index.js"
|
|
20
|
+
},
|
|
21
|
+
"./components/AyezeeForm.astro": "./dist/components/AyezeeForm.astro",
|
|
22
|
+
"./form-handler": {
|
|
23
|
+
"types": "./dist/form-handler.d.ts",
|
|
24
|
+
"import": "./dist/form-handler.js"
|
|
20
25
|
}
|
|
21
26
|
},
|
|
22
27
|
"files": [
|
|
@@ -24,7 +29,8 @@
|
|
|
24
29
|
"README.md"
|
|
25
30
|
],
|
|
26
31
|
"scripts": {
|
|
27
|
-
"build": "tsc",
|
|
32
|
+
"build": "tsc && npm run copy-astro",
|
|
33
|
+
"copy-astro": "cp src/components/*.astro dist/components/ 2>/dev/null || true",
|
|
28
34
|
"dev": "tsc --watch",
|
|
29
35
|
"prepublishOnly": "npm run build"
|
|
30
36
|
},
|