@nuasite/components 0.15.1 → 0.16.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/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@nuasite/components",
3
3
  "description": "Nua Site astro components.",
4
4
  "license": "Apache-2.0",
5
- "version": "0.15.1",
5
+ "version": "0.16.0",
6
6
  "files": [
7
7
  "dist/**",
8
8
  "src/**",
@@ -70,12 +70,15 @@ const tokenId = `${tokenFieldName}_${formId}`
70
70
  ))}
71
71
  </div>
72
72
 
73
+ <label for={cssHoneypotField} class="sr-only">Leave this field empty</label>
73
74
  <input
74
75
  type="text"
76
+ id={cssHoneypotField}
75
77
  name={cssHoneypotField}
76
78
  style="position: absolute; left: -9999px; opacity: 0;"
77
79
  tabindex="-1"
78
80
  autocomplete="nope"
81
+ aria-hidden="true"
79
82
  />
80
83
 
81
84
  <slot />
@@ -216,16 +219,32 @@ const tokenId = `${tokenFieldName}_${formId}`
216
219
 
217
220
  refreshSubmissionGuards()
218
221
 
222
+ const trackForm = (action: string, detail?: Record<string, unknown>) => {
223
+ if (typeof window !== 'undefined' && (window as any).analytics?.form) {
224
+ (window as any).analytics.form(formId, action, detail)
225
+ }
226
+ }
227
+
219
228
  form.addEventListener('submit', async (e) => {
220
229
  e.preventDefault()
221
230
 
231
+ const formDataForTracking = Object.fromEntries(
232
+ Array.from(new FormData(form).entries())
233
+ .filter(([k]) => !this.honeypotFieldNames.includes(k) && !/^token_/.test(k) && !form.querySelector(`[name="${k}"][aria-hidden="true"]`))
234
+ .map(([k, v]) => [k, typeof v === 'string' ? v.slice(0, 100) : '[File]']),
235
+ )
236
+
237
+ trackForm('attempt', { formData: formDataForTracking })
238
+
222
239
  if (!this.passesHumanityChecks(form)) {
240
+ trackForm('reject', { reason: 'humanity_check_failed', formData: formDataForTracking })
223
241
  showError(tryAgainMessage)
224
242
  return
225
243
  }
226
244
 
227
245
  const elapsed = performance.now() - activationTime
228
246
  if (minSubmitDelay > 0 && elapsed < minSubmitDelay) {
247
+ trackForm('reject', { reason: 'submitted_too_fast', formData: formDataForTracking })
229
248
  showError(fastSubmitMessage || tryAgainMessage)
230
249
  return
231
250
  }
@@ -233,6 +252,7 @@ const tokenId = `${tokenFieldName}_${formId}`
233
252
  activationTime = performance.now()
234
253
 
235
254
  if (tokenInput && !tokenInput.value) {
255
+ trackForm('reject', { reason: 'missing_token', formData: formDataForTracking })
236
256
  showError(tryAgainMessage)
237
257
  return
238
258
  }
@@ -251,15 +271,18 @@ const tokenId = `${tokenFieldName}_${formId}`
251
271
 
252
272
  if (response.ok && 'success' in result && result.success) {
253
273
  const message = hasCustomSuccess ? successMessage : (result.message || successMessage)
274
+ trackForm('success', { formData: formDataForTracking })
254
275
  showSuccess(message)
255
276
  form.reset()
256
277
  refreshSubmissionGuards()
257
278
  } else {
258
279
  const message = hasCustomError ? errorMessage : (result.error || result.message || errorMessage)
280
+ trackForm('error', { reason: result.error || `http_${response.status}`, formData: formDataForTracking })
259
281
  showError(message)
260
282
  }
261
283
  } catch (err) {
262
284
  console.error('Form submission error:', err);
285
+ trackForm('error', { reason: err instanceof Error ? err.message : 'network_error', formData: formDataForTracking })
263
286
  showError(networkErrorMessage)
264
287
  }
265
288
  })