@opensite/ui 1.7.5 → 1.7.6

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.
@@ -1,12 +1,12 @@
1
1
  "use client";
2
2
  import * as React from 'react';
3
- import React__default, { useMemo } from 'react';
4
- import { useForm, Form, Field } from '@page-speed/forms';
5
- import { TextInput, TextArea } from '@page-speed/forms/inputs';
3
+ import React__default, { useMemo, useState, useCallback } from 'react';
4
+ import { Form, useForm, Field } from '@page-speed/forms';
6
5
  import { clsx } from 'clsx';
7
6
  import { twMerge } from 'tailwind-merge';
8
7
  import { cva } from 'class-variance-authority';
9
8
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
9
+ import { TextInput, TextArea, Select, Radio, Checkbox, CheckboxGroup, DatePicker, DateRangePicker, TimePicker, FileInput, RichTextEditor } from '@page-speed/forms/inputs';
10
10
  import * as LabelPrimitive from '@radix-ui/react-label';
11
11
  import * as AccordionPrimitive from '@radix-ui/react-accordion';
12
12
  import { serializeForRails, deserializeErrors } from '@page-speed/forms/integration';
@@ -472,6 +472,200 @@ function Label({
472
472
  }
473
473
  );
474
474
  }
475
+ function DynamicFormField({
476
+ field,
477
+ className,
478
+ uploadProgress = {},
479
+ onFileUpload,
480
+ onFileRemove,
481
+ isUploading = false
482
+ }) {
483
+ const fieldId = `field-${field.name}`;
484
+ return /* @__PURE__ */ jsx(Field, { name: field.name, children: ({ field: formField, meta }) => /* @__PURE__ */ jsxs("div", { className: cn("space-y-2", className), children: [
485
+ field.type !== "checkbox" && /* @__PURE__ */ jsxs(Label, { htmlFor: fieldId, children: [
486
+ field.label,
487
+ field.required && /* @__PURE__ */ jsx("span", { className: "text-destructive ml-1", children: "*" })
488
+ ] }),
489
+ (field.type === "text" || field.type === "email" || field.type === "tel" || field.type === "search" || field.type === "password" || field.type === "url") && /* @__PURE__ */ jsx(
490
+ TextInput,
491
+ {
492
+ ...formField,
493
+ id: fieldId,
494
+ type: field.type,
495
+ placeholder: field.placeholder,
496
+ error: meta.touched && !!meta.error,
497
+ disabled: field.disabled,
498
+ "aria-label": field.label
499
+ }
500
+ ),
501
+ field.type === "number" && /* @__PURE__ */ jsx(
502
+ TextInput,
503
+ {
504
+ ...formField,
505
+ id: fieldId,
506
+ type: "text",
507
+ placeholder: field.placeholder,
508
+ error: meta.touched && !!meta.error,
509
+ disabled: field.disabled,
510
+ "aria-label": field.label
511
+ }
512
+ ),
513
+ field.type === "textarea" && /* @__PURE__ */ jsx(
514
+ TextArea,
515
+ {
516
+ ...formField,
517
+ id: fieldId,
518
+ placeholder: field.placeholder,
519
+ rows: field.rows || 4,
520
+ error: meta.touched && !!meta.error,
521
+ disabled: field.disabled,
522
+ "aria-label": field.label
523
+ }
524
+ ),
525
+ field.type === "select" && field.options && /* @__PURE__ */ jsx(
526
+ Select,
527
+ {
528
+ ...formField,
529
+ id: fieldId,
530
+ options: field.options,
531
+ placeholder: field.placeholder || `Select ${field.label.toLowerCase()}`,
532
+ error: meta.touched && !!meta.error,
533
+ disabled: field.disabled,
534
+ "aria-label": field.label
535
+ }
536
+ ),
537
+ field.type === "multi-select" && field.options && /* @__PURE__ */ jsx(
538
+ Select,
539
+ {
540
+ ...formField,
541
+ id: fieldId,
542
+ options: field.options,
543
+ placeholder: field.placeholder || `Select ${field.label.toLowerCase()}`,
544
+ error: meta.touched && !!meta.error,
545
+ disabled: field.disabled,
546
+ "aria-label": field.label,
547
+ multiple: true
548
+ }
549
+ ),
550
+ field.type === "radio" && field.options && /* @__PURE__ */ jsx(
551
+ Radio,
552
+ {
553
+ ...formField,
554
+ id: fieldId,
555
+ options: field.options,
556
+ disabled: field.disabled,
557
+ layout: field.layout || "stacked",
558
+ error: meta.touched && !!meta.error,
559
+ "aria-label": field.label
560
+ }
561
+ ),
562
+ field.type === "checkbox" && /* @__PURE__ */ jsxs("div", { className: "flex items-start space-x-2", children: [
563
+ /* @__PURE__ */ jsx(
564
+ Checkbox,
565
+ {
566
+ ...formField,
567
+ id: fieldId,
568
+ value: formField.value === true || formField.value === "true",
569
+ onChange: (checked) => formField.onChange(checked),
570
+ disabled: field.disabled,
571
+ error: meta.touched && !!meta.error,
572
+ "aria-label": field.label
573
+ }
574
+ ),
575
+ /* @__PURE__ */ jsxs(
576
+ Label,
577
+ {
578
+ htmlFor: fieldId,
579
+ className: "font-normal cursor-pointer leading-relaxed",
580
+ children: [
581
+ field.label,
582
+ field.required && /* @__PURE__ */ jsx("span", { className: "text-destructive ml-1", children: "*" })
583
+ ]
584
+ }
585
+ )
586
+ ] }),
587
+ field.type === "checkbox-group" && field.options && /* @__PURE__ */ jsx(
588
+ CheckboxGroup,
589
+ {
590
+ ...formField,
591
+ id: fieldId,
592
+ options: field.options,
593
+ disabled: field.disabled,
594
+ layout: field.layout || "stacked",
595
+ error: meta.touched && !!meta.error,
596
+ "aria-label": field.label
597
+ }
598
+ ),
599
+ (field.type === "date-picker" || field.type === "date") && /* @__PURE__ */ jsx(
600
+ DatePicker,
601
+ {
602
+ ...formField,
603
+ id: fieldId,
604
+ placeholder: field.placeholder,
605
+ error: meta.touched && !!meta.error,
606
+ disabled: field.disabled,
607
+ "aria-label": field.label
608
+ }
609
+ ),
610
+ field.type === "date-range" && /* @__PURE__ */ jsx(
611
+ DateRangePicker,
612
+ {
613
+ ...formField,
614
+ id: fieldId,
615
+ error: meta.touched && !!meta.error,
616
+ disabled: field.disabled,
617
+ "aria-label": field.label
618
+ }
619
+ ),
620
+ field.type === "time" && /* @__PURE__ */ jsx(
621
+ TimePicker,
622
+ {
623
+ ...formField,
624
+ id: fieldId,
625
+ placeholder: field.placeholder,
626
+ error: meta.touched && !!meta.error,
627
+ disabled: field.disabled,
628
+ "aria-label": field.label
629
+ }
630
+ ),
631
+ field.type === "file" && /* @__PURE__ */ jsx(
632
+ FileInput,
633
+ {
634
+ ...formField,
635
+ id: fieldId,
636
+ accept: field.accept,
637
+ maxSize: field.maxSize || 5 * 1024 * 1024,
638
+ maxFiles: field.maxFiles || 1,
639
+ multiple: field.multiple || false,
640
+ placeholder: field.placeholder || "Choose file(s)...",
641
+ error: meta.touched && !!meta.error,
642
+ disabled: field.disabled || isUploading,
643
+ showProgress: true,
644
+ uploadProgress,
645
+ onChange: (files) => {
646
+ formField.onChange(files);
647
+ if (files.length > 0 && onFileUpload) {
648
+ onFileUpload(files);
649
+ }
650
+ },
651
+ onFileRemove,
652
+ "aria-label": field.label
653
+ }
654
+ ),
655
+ field.type === "rich-text" && /* @__PURE__ */ jsx(
656
+ RichTextEditor,
657
+ {
658
+ ...formField,
659
+ id: fieldId,
660
+ placeholder: field.placeholder,
661
+ error: meta.touched && !!meta.error,
662
+ disabled: field.disabled,
663
+ "aria-label": field.label
664
+ }
665
+ ),
666
+ meta.touched && meta.error && /* @__PURE__ */ jsx("p", { className: "text-sm text-destructive", children: meta.error })
667
+ ] }) });
668
+ }
475
669
  var svgCache = /* @__PURE__ */ new Map();
476
670
  function DynamicIcon({
477
671
  name,
@@ -637,6 +831,197 @@ function AccordionContent({
637
831
  }
638
832
  );
639
833
  }
834
+ function useFileUpload(options) {
835
+ const [uploadTokens, setUploadTokens] = useState([]);
836
+ const [uploadProgress, setUploadProgress] = useState({});
837
+ const [isUploading, setIsUploading] = useState(false);
838
+ const endpoint = options?.endpoint || "https://api.dashtrack.com/contacts/_/contact_form_uploads";
839
+ const uploadFiles = useCallback(
840
+ async (files) => {
841
+ if (files.length === 0) return;
842
+ setIsUploading(true);
843
+ try {
844
+ const tokens = [];
845
+ for (const file of files) {
846
+ const formData = new FormData();
847
+ formData.append("contact_form_upload[file_upload]", file);
848
+ formData.append("contact_form_upload[title]", file.name);
849
+ formData.append("contact_form_upload[file_name]", file.name);
850
+ formData.append("contact_form_upload[file_size]", String(file.size));
851
+ const response = await fetch(endpoint, {
852
+ method: "POST",
853
+ body: formData
854
+ });
855
+ if (!response.ok) {
856
+ throw new Error(`Upload failed: ${response.statusText}`);
857
+ }
858
+ const data = await response.json();
859
+ if (data.contact_form_upload?.token) {
860
+ tokens.push(`upload_${data.contact_form_upload.token}`);
861
+ }
862
+ setUploadProgress((prev) => ({
863
+ ...prev,
864
+ [file.name]: 100
865
+ }));
866
+ }
867
+ setUploadTokens(tokens);
868
+ } catch (error) {
869
+ console.error("File upload error:", error);
870
+ options?.onError?.(error);
871
+ } finally {
872
+ setIsUploading(false);
873
+ }
874
+ },
875
+ [endpoint, options]
876
+ );
877
+ const removeFile = useCallback((file, index) => {
878
+ setUploadTokens((prev) => prev.filter((_, i) => i !== index));
879
+ setUploadProgress((prev) => {
880
+ const newProgress = { ...prev };
881
+ delete newProgress[file.name];
882
+ return newProgress;
883
+ });
884
+ }, []);
885
+ const resetUpload = useCallback(() => {
886
+ setUploadTokens([]);
887
+ setUploadProgress({});
888
+ }, []);
889
+ return {
890
+ uploadTokens,
891
+ uploadProgress,
892
+ isUploading,
893
+ uploadFiles,
894
+ removeFile,
895
+ resetUpload
896
+ };
897
+ }
898
+
899
+ // lib/form-field-types.ts
900
+ function generateInitialValues(fields) {
901
+ return fields.reduce(
902
+ (acc, field) => {
903
+ if (field.type === "checkbox") {
904
+ acc[field.name] = false;
905
+ } else if (field.type === "checkbox-group" || field.type === "multi-select") {
906
+ acc[field.name] = [];
907
+ } else if (field.type === "file") {
908
+ acc[field.name] = [];
909
+ } else if (field.type === "date-range") {
910
+ acc[field.name] = { start: null, end: null };
911
+ } else {
912
+ acc[field.name] = "";
913
+ }
914
+ return acc;
915
+ },
916
+ {}
917
+ );
918
+ }
919
+ function generateValidationSchema(fields) {
920
+ return fields.reduce(
921
+ (acc, field) => {
922
+ acc[field.name] = (value, allValues) => {
923
+ if (field.required) {
924
+ if (!value || typeof value === "string" && !value.trim()) {
925
+ return `${field.label} is required`;
926
+ }
927
+ }
928
+ if (field.type === "email" && value) {
929
+ if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value)) {
930
+ return "Please enter a valid email address";
931
+ }
932
+ }
933
+ if (field.type === "url" && value) {
934
+ try {
935
+ new URL(value);
936
+ } catch {
937
+ return "Please enter a valid URL";
938
+ }
939
+ }
940
+ if (field.validator) {
941
+ return field.validator(value, allValues);
942
+ }
943
+ return void 0;
944
+ };
945
+ return acc;
946
+ },
947
+ {}
948
+ );
949
+ }
950
+ function getColumnSpanClass(span) {
951
+ if (!span || span === 12) return "col-span-12";
952
+ return `col-span-12 sm:col-span-${Math.min(span, 12)}`;
953
+ }
954
+
955
+ // lib/forms/use-contact-form.ts
956
+ function useContactForm(options) {
957
+ const {
958
+ formFields,
959
+ formConfig,
960
+ onSubmit,
961
+ onSuccess,
962
+ onError,
963
+ resetOnSuccess = true,
964
+ uploadTokens = []
965
+ } = options;
966
+ const [isSubmitted, setIsSubmitted] = useState(false);
967
+ const [submissionError, setSubmissionError] = useState(null);
968
+ const form = useForm({
969
+ initialValues: useMemo(
970
+ () => generateInitialValues(formFields),
971
+ [formFields]
972
+ ),
973
+ validationSchema: useMemo(
974
+ () => generateValidationSchema(formFields),
975
+ [formFields]
976
+ ),
977
+ onSubmit: async (values, helpers) => {
978
+ setSubmissionError(null);
979
+ const shouldAutoSubmit = Boolean(formConfig?.endpoint);
980
+ if (!shouldAutoSubmit && !onSubmit) {
981
+ return;
982
+ }
983
+ try {
984
+ let result;
985
+ const submissionValues = {
986
+ ...values,
987
+ ...uploadTokens.length > 0 && {
988
+ contact_form_upload_tokens: uploadTokens
989
+ }
990
+ };
991
+ if (shouldAutoSubmit) {
992
+ result = await submitPageSpeedForm(submissionValues, formConfig);
993
+ }
994
+ if (onSubmit) {
995
+ await onSubmit(submissionValues);
996
+ }
997
+ if (shouldAutoSubmit || onSubmit) {
998
+ setIsSubmitted(true);
999
+ if (resetOnSuccess) {
1000
+ helpers.resetForm();
1001
+ }
1002
+ onSuccess?.(result);
1003
+ setTimeout(() => setIsSubmitted(false), 5e3);
1004
+ }
1005
+ } catch (error) {
1006
+ if (error instanceof PageSpeedFormSubmissionError && error.formErrors) {
1007
+ helpers.setErrors(error.formErrors);
1008
+ }
1009
+ const errorMessage = error instanceof Error ? error.message : "Form submission failed";
1010
+ setSubmissionError(errorMessage);
1011
+ onError?.(error);
1012
+ }
1013
+ }
1014
+ });
1015
+ const formMethod = formConfig?.method?.toLowerCase() === "get" ? "get" : "post";
1016
+ return {
1017
+ form,
1018
+ isSubmitted,
1019
+ submissionError,
1020
+ formMethod
1021
+ };
1022
+ }
1023
+
1024
+ // lib/forms.ts
640
1025
  var PageSpeedFormSubmissionError = class extends Error {
641
1026
  constructor(message, options = {}) {
642
1027
  super(message);
@@ -1127,18 +1512,57 @@ var Section = React__default.forwardRef(
1127
1512
  }
1128
1513
  );
1129
1514
  Section.displayName = "Section";
1515
+ var DEFAULT_FORM_FIELDS = [
1516
+ {
1517
+ name: "name",
1518
+ type: "text",
1519
+ label: "Name",
1520
+ placeholder: "Full Name",
1521
+ required: true,
1522
+ columnSpan: 6
1523
+ },
1524
+ {
1525
+ name: "email",
1526
+ type: "email",
1527
+ label: "Email",
1528
+ placeholder: "your@email.com",
1529
+ required: true,
1530
+ columnSpan: 6
1531
+ },
1532
+ {
1533
+ name: "subject",
1534
+ type: "text",
1535
+ label: "Subject",
1536
+ placeholder: "What is this regarding?",
1537
+ required: true,
1538
+ columnSpan: 12
1539
+ },
1540
+ {
1541
+ name: "message",
1542
+ type: "textarea",
1543
+ label: "Message",
1544
+ placeholder: "Your message...",
1545
+ required: true,
1546
+ rows: 4,
1547
+ columnSpan: 12
1548
+ }
1549
+ ];
1130
1550
  function ContactFaq({
1131
1551
  heading,
1132
1552
  description,
1133
1553
  formHeading,
1134
- buttonText,
1554
+ buttonText = "Submit",
1135
1555
  buttonIcon,
1136
1556
  actions,
1137
1557
  actionsSlot,
1138
1558
  items,
1139
1559
  itemsSlot,
1140
1560
  faqHeading,
1561
+ formFields = DEFAULT_FORM_FIELDS,
1562
+ successMessage = "Thank you! Your message has been sent successfully.",
1563
+ errorMessage = "There was an error sending your message. Please try again.",
1141
1564
  className,
1565
+ containerClassName = "px-6 sm:px-6 md:px-8 lg:px-8",
1142
1566
  headerClassName,
1143
1567
  headingClassName,
1144
1568
  descriptionClassName,
@@ -1154,9 +1578,10 @@ function ContactFaq({
1154
1578
  accordionTriggerClassName,
1155
1579
  accordionContentClassName,
1156
1580
  gridClassName,
1581
+ successMessageClassName,
1582
+ errorMessageClassName,
1157
1583
  background,
1158
1584
  spacing = "py-8 md:py-32",
1159
- containerClassName = "px-6 sm:px-6 md:px-8 lg:px-8",
1160
1585
  pattern,
1161
1586
  patternOpacity,
1162
1587
  formConfig,
@@ -1164,54 +1589,27 @@ function ContactFaq({
1164
1589
  onSuccess,
1165
1590
  onError
1166
1591
  }) {
1167
- const form = useForm({
1168
- initialValues: {
1169
- name: "",
1170
- email: "",
1171
- subject: "",
1172
- message: ""
1592
+ const {
1593
+ uploadTokens,
1594
+ uploadProgress,
1595
+ isUploading,
1596
+ uploadFiles,
1597
+ removeFile,
1598
+ resetUpload
1599
+ } = useFileUpload({ onError });
1600
+ const { form, isSubmitted, submissionError, formMethod } = useContactForm({
1601
+ formFields,
1602
+ formConfig,
1603
+ onSubmit,
1604
+ onSuccess: (data) => {
1605
+ resetUpload();
1606
+ onSuccess?.(data);
1173
1607
  },
1174
- validationSchema: {
1175
- name: (value) => !value ? "Name is required" : void 0,
1176
- email: (value) => {
1177
- if (!value) return "Email is required";
1178
- if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value))
1179
- return "Please enter a valid email address";
1180
- return void 0;
1181
- },
1182
- subject: (value) => !value ? "Subject is required" : void 0,
1183
- message: (value) => !value ? "Message is required" : void 0
1184
- },
1185
- onSubmit: async (values, helpers) => {
1186
- const shouldAutoSubmit = Boolean(formConfig?.endpoint);
1187
- if (!shouldAutoSubmit && !onSubmit) {
1188
- return;
1189
- }
1190
- try {
1191
- let result;
1192
- if (shouldAutoSubmit) {
1193
- result = await submitPageSpeedForm(values, formConfig);
1194
- }
1195
- if (onSubmit) {
1196
- await onSubmit(values);
1197
- }
1198
- if (shouldAutoSubmit || onSubmit) {
1199
- if (formConfig?.resetOnSuccess !== false) {
1200
- helpers.resetForm();
1201
- }
1202
- onSuccess?.(result);
1203
- }
1204
- } catch (error) {
1205
- if (error instanceof PageSpeedFormSubmissionError && error.formErrors) {
1206
- helpers.setErrors(error.formErrors);
1207
- }
1208
- onError?.(error);
1209
- throw error;
1210
- }
1211
- }
1608
+ onError,
1609
+ resetOnSuccess: formConfig?.resetOnSuccess !== false,
1610
+ uploadTokens
1212
1611
  });
1213
- const formMethod = formConfig?.method?.toLowerCase() === "get" ? "get" : "post";
1214
- const actionsContent = React.useMemo(() => {
1612
+ const actionsContent = useMemo(() => {
1215
1613
  if (actionsSlot) return actionsSlot;
1216
1614
  if (actions && actions.length > 0) {
1217
1615
  return actions.map((action, index) => {
@@ -1272,8 +1670,7 @@ function ContactFaq({
1272
1670
  accordionClassName,
1273
1671
  accordionItemClassName,
1274
1672
  accordionTriggerClassName,
1275
- accordionContentClassName,
1276
- background
1673
+ accordionContentClassName
1277
1674
  ]);
1278
1675
  return /* @__PURE__ */ jsx(
1279
1676
  Section,
@@ -1336,6 +1733,26 @@ function ContactFaq({
1336
1733
  children: formHeading
1337
1734
  }
1338
1735
  ) : /* @__PURE__ */ jsx("div", { className: formHeadingClassName, children: formHeading })),
1736
+ isSubmitted && /* @__PURE__ */ jsx(
1737
+ "div",
1738
+ {
1739
+ className: cn(
1740
+ "mb-6 p-4 bg-primary/10 border border-primary rounded-md",
1741
+ successMessageClassName
1742
+ ),
1743
+ children: typeof successMessage === "string" ? /* @__PURE__ */ jsx("p", { className: "text-sm text-primary-foreground/90 text-center", children: successMessage }) : successMessage
1744
+ }
1745
+ ),
1746
+ submissionError && /* @__PURE__ */ jsx(
1747
+ "div",
1748
+ {
1749
+ className: cn(
1750
+ "mb-6 p-4 bg-destructive/10 border border-destructive rounded-md",
1751
+ errorMessageClassName
1752
+ ),
1753
+ children: typeof errorMessage === "string" ? /* @__PURE__ */ jsx("p", { className: "text-sm text-destructive text-center", children: submissionError }) : errorMessage
1754
+ }
1755
+ ),
1339
1756
  /* @__PURE__ */ jsxs(
1340
1757
  Form,
1341
1758
  {
@@ -1344,62 +1761,23 @@ function ContactFaq({
1344
1761
  method: formMethod,
1345
1762
  className: cn("space-y-4", formClassName),
1346
1763
  children: [
1347
- /* @__PURE__ */ jsxs("div", { className: "grid gap-4 sm:grid-cols-2", children: [
1348
- /* @__PURE__ */ jsx(Field, { name: "name", children: ({ field, meta }) => /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
1349
- /* @__PURE__ */ jsx(Label, { htmlFor: "name", children: "Name" }),
1350
- /* @__PURE__ */ jsx(
1351
- TextInput,
1352
- {
1353
- ...field,
1354
- id: "name",
1355
- placeholder: "John Doe",
1356
- error: meta.touched && !!meta.error,
1357
- "aria-label": "Name"
1358
- }
1359
- )
1360
- ] }) }),
1361
- /* @__PURE__ */ jsx(Field, { name: "email", children: ({ field, meta }) => /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
1362
- /* @__PURE__ */ jsx(Label, { htmlFor: "email", children: "Email" }),
1363
- /* @__PURE__ */ jsx(
1364
- TextInput,
1764
+ /* @__PURE__ */ jsx("div", { className: "grid grid-cols-12 gap-4", children: formFields.map((field) => /* @__PURE__ */ jsx(
1765
+ "div",
1766
+ {
1767
+ className: getColumnSpanClass(field.columnSpan),
1768
+ children: /* @__PURE__ */ jsx(
1769
+ DynamicFormField,
1365
1770
  {
1366
- ...field,
1367
- id: "email",
1368
- type: "email",
1369
- placeholder: "john@example.com",
1370
- error: meta.touched && !!meta.error,
1371
- "aria-label": "Email"
1771
+ field,
1772
+ uploadProgress,
1773
+ onFileUpload: uploadFiles,
1774
+ onFileRemove: removeFile,
1775
+ isUploading
1372
1776
  }
1373
1777
  )
1374
- ] }) })
1375
- ] }),
1376
- /* @__PURE__ */ jsx(Field, { name: "subject", children: ({ field, meta }) => /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
1377
- /* @__PURE__ */ jsx(Label, { htmlFor: "subject", children: "Subject" }),
1378
- /* @__PURE__ */ jsx(
1379
- TextInput,
1380
- {
1381
- ...field,
1382
- id: "subject",
1383
- placeholder: "What is this regarding?",
1384
- error: meta.touched && !!meta.error,
1385
- "aria-label": "Subject"
1386
- }
1387
- )
1388
- ] }) }),
1389
- /* @__PURE__ */ jsx(Field, { name: "message", children: ({ field, meta }) => /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
1390
- /* @__PURE__ */ jsx(Label, { htmlFor: "message", children: "Message" }),
1391
- /* @__PURE__ */ jsx(
1392
- TextArea,
1393
- {
1394
- ...field,
1395
- id: "message",
1396
- placeholder: "Your question...",
1397
- rows: 4,
1398
- error: meta.touched && !!meta.error,
1399
- "aria-label": "Message"
1400
- }
1401
- )
1402
- ] }) }),
1778
+ },
1779
+ field.name
1780
+ )) }),
1403
1781
  actionsSlot || actions && actions.length > 0 ? actionsContent : /* @__PURE__ */ jsxs(
1404
1782
  Pressable,
1405
1783
  {