@notionhive/contacts 0.1.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.
Files changed (68) hide show
  1. package/bin/contacts.js +16 -0
  2. package/category.config.json +7 -0
  3. package/package.json +24 -0
  4. package/registry/contact-01.json +9 -0
  5. package/registry/contact-02.json +9 -0
  6. package/registry/contact-03.json +9 -0
  7. package/registry/contact-04.json +6 -0
  8. package/registry/contact-05.json +9 -0
  9. package/registry/contact-06.json +9 -0
  10. package/registry/contact-07.json +9 -0
  11. package/registry/contact-08.json +6 -0
  12. package/registry/contact-09.json +10 -0
  13. package/registry/contact-10.json +6 -0
  14. package/registry/index.json +88 -0
  15. package/templates/components/atoms/SafeImage/SafeImage.jsx +101 -0
  16. package/templates/components/atoms/SafeImage/index.js +1 -0
  17. package/templates/components/hooks/useCarousel.js +73 -0
  18. package/templates/components/organisms/Contact01/Contact01.jsx +363 -0
  19. package/templates/components/organisms/Contact01/Contact01.propTypes.js +105 -0
  20. package/templates/components/organisms/Contact01/index.js +1 -0
  21. package/templates/components/organisms/Contact02/Contact02.jsx +288 -0
  22. package/templates/components/organisms/Contact02/Contact02.propTypes.js +78 -0
  23. package/templates/components/organisms/Contact02/index.js +1 -0
  24. package/templates/components/organisms/Contact03/Contact03.jsx +94 -0
  25. package/templates/components/organisms/Contact03/Contact03.propTypes.js +25 -0
  26. package/templates/components/organisms/Contact03/index.js +1 -0
  27. package/templates/components/organisms/Contact04/Contact04.jsx +239 -0
  28. package/templates/components/organisms/Contact04/Contact04.propTypes.js +50 -0
  29. package/templates/components/organisms/Contact04/index.js +1 -0
  30. package/templates/components/organisms/Contact05/Contact05.jsx +272 -0
  31. package/templates/components/organisms/Contact05/Contact05.propTypes.js +49 -0
  32. package/templates/components/organisms/Contact05/index.js +1 -0
  33. package/templates/components/organisms/Contact06/Contact06.jsx +223 -0
  34. package/templates/components/organisms/Contact06/Contact06.propTypes.js +40 -0
  35. package/templates/components/organisms/Contact06/index.js +1 -0
  36. package/templates/components/organisms/Contact07/Contact07.jsx +348 -0
  37. package/templates/components/organisms/Contact07/Contact07.propTypes.js +92 -0
  38. package/templates/components/organisms/Contact07/index.js +1 -0
  39. package/templates/components/organisms/Contact08/Contact08.jsx +178 -0
  40. package/templates/components/organisms/Contact08/Contact08.propTypes.js +36 -0
  41. package/templates/components/organisms/Contact08/index.js +1 -0
  42. package/templates/components/organisms/Contact09/Contact09.jsx +345 -0
  43. package/templates/components/organisms/Contact09/Contact09.propTypes.js +96 -0
  44. package/templates/components/organisms/Contact09/index.js +1 -0
  45. package/templates/components/organisms/Contact10/Contact10.jsx +175 -0
  46. package/templates/components/organisms/Contact10/Contact10.propTypes.js +55 -0
  47. package/templates/components/organisms/Contact10/index.js +1 -0
  48. package/templates/public/contact/contact01/ellipse.svg +12 -0
  49. package/templates/public/contact/contact01/logo-accenture.svg +6 -0
  50. package/templates/public/contact/contact01/logo-amart.svg +12 -0
  51. package/templates/public/contact/contact01/logo-brand12.png +0 -0
  52. package/templates/public/contact/contact01/logo-brand5.png +0 -0
  53. package/templates/public/contact/contact01/logo-brand7.png +0 -0
  54. package/templates/public/contact/contact01/logo-brand9.svg +12 -0
  55. package/templates/public/contact/contact01/logo-great-eastern.png +0 -0
  56. package/templates/public/contact/contact01/logo-kenwood.png +0 -0
  57. package/templates/public/contact/contact01/logo-nissan.svg +12 -0
  58. package/templates/public/contact/contact01/logo-suncorp.png +0 -0
  59. package/templates/public/contact/contact01/logo-ticketmaster.png +0 -0
  60. package/templates/public/contact/contact01/logo-toyota.svg +5 -0
  61. package/templates/public/contact/contact02/map.jpg +0 -0
  62. package/templates/public/contact/contact03/bg.jpg +0 -0
  63. package/templates/public/contact/contact03/card-photo.jpg +0 -0
  64. package/templates/public/contact/contact05/hero.jpg +0 -0
  65. package/templates/public/contact/contact05/pattern.png +0 -0
  66. package/templates/public/contact/contact06/hero.jpg +0 -0
  67. package/templates/public/contact/contact07/hero.jpg +0 -0
  68. package/templates/public/contact/contact09/avatar.jpg +0 -0
@@ -0,0 +1,239 @@
1
+ "use client";
2
+
3
+ import { useState } from "react";
4
+ import {
5
+ contact04DefaultProps,
6
+ contact04PropTypes,
7
+ } from "./Contact04.propTypes";
8
+
9
+ const inputClassName =
10
+ "w-full bg-transparent py-2 text-lg text-[rgba(0,0,0,0.9)] outline-none transition-colors duration-200 ease-out placeholder:text-[rgba(0,0,0,0.4)] focus:border-[#007cff]";
11
+
12
+ function EastArrowIcon({ className = "" }) {
13
+ return (
14
+ <svg viewBox="0 0 24 24" fill="none" aria-hidden="true" className={className}>
15
+ <path
16
+ d="M5 12h14M13 6l6 6-6 6"
17
+ stroke="currentColor"
18
+ strokeWidth="1.5"
19
+ strokeLinecap="round"
20
+ strokeLinejoin="round"
21
+ />
22
+ </svg>
23
+ );
24
+ }
25
+
26
+ function ChevronDownIcon({ className = "" }) {
27
+ return (
28
+ <svg viewBox="0 0 20 20" fill="none" aria-hidden="true" className={className}>
29
+ <path
30
+ d="M5 8l5 5 5-5"
31
+ stroke="currentColor"
32
+ strokeWidth="1.5"
33
+ strokeLinecap="round"
34
+ strokeLinejoin="round"
35
+ />
36
+ </svg>
37
+ );
38
+ }
39
+
40
+ function UnderlineField({ label, required = false, children }) {
41
+ return (
42
+ <div className="flex w-full flex-col">
43
+ <label className="text-base leading-6 text-[#010623]">
44
+ {label}
45
+ {required ? "*" : ""}
46
+ </label>
47
+ <div className="border-b border-[rgba(0,0,0,0.15)]">{children}</div>
48
+ </div>
49
+ );
50
+ }
51
+
52
+ /**
53
+ * Contact04 — Brace PL split contact info + form.
54
+ * Figma node 26:15849 at 1920px.
55
+ *
56
+ * @param {object} props - See Contact04.propTypes.js.
57
+ */
58
+ export function Contact04({
59
+ label = contact04DefaultProps.label,
60
+ headline = contact04DefaultProps.headline,
61
+ phoneLabel = contact04DefaultProps.phoneLabel,
62
+ phone = contact04DefaultProps.phone,
63
+ emailLabel = contact04DefaultProps.emailLabel,
64
+ email = contact04DefaultProps.email,
65
+ socialLinks = contact04DefaultProps.socialLinks,
66
+ formTitle = contact04DefaultProps.formTitle,
67
+ accountTypeOptions = contact04DefaultProps.accountTypeOptions,
68
+ submitText = contact04DefaultProps.submitText,
69
+ formData: controlledForm,
70
+ onFieldChange,
71
+ onSubmit,
72
+ className = "",
73
+ }) {
74
+ const [internalForm, setInternalForm] = useState({
75
+ fullName: "",
76
+ email: "",
77
+ phone: "",
78
+ accountType: "",
79
+ message: "",
80
+ });
81
+
82
+ const form = controlledForm ?? internalForm;
83
+
84
+ const setField = (field, value) => {
85
+ onFieldChange?.(field, value);
86
+ if (!controlledForm) {
87
+ setInternalForm((prev) => ({ ...prev, [field]: value }));
88
+ }
89
+ };
90
+
91
+ const handleSubmit = (event) => {
92
+ event.preventDefault();
93
+ onSubmit?.(form);
94
+ };
95
+
96
+ return (
97
+ <section
98
+ className={["relative w-full overflow-hidden bg-[#f7f8fa]", className]
99
+ .filter(Boolean)
100
+ .join(" ")}
101
+ data-contact="contact04"
102
+ >
103
+ <div className="relative mx-auto w-full max-w-[1920px] px-4 py-16 sm:px-6 md:px-10 lg:px-20 lg:py-24 xl:px-[160px] xl:py-28">
104
+ <div className="mx-auto flex w-full max-w-[1260px] flex-col gap-12 lg:flex-row lg:items-center lg:justify-between lg:gap-16 xl:gap-20">
105
+ {/* Left — contact info */}
106
+ <div className="flex w-full max-w-[490px] flex-col gap-12 lg:gap-[86px]">
107
+ <div className="flex flex-col gap-4">
108
+ <p className="text-lg font-bold leading-[22px] text-[#007cff]">{label}</p>
109
+ <h2 className="whitespace-pre-wrap font-serif text-4xl font-extralight leading-tight tracking-[-0.02em] text-[#010623] sm:text-5xl md:text-6xl lg:text-7xl xl:text-[104px] xl:leading-[108px] xl:tracking-[-2.08px]">
110
+ {headline}
111
+ </h2>
112
+ </div>
113
+
114
+ <div className="flex flex-col gap-12 lg:gap-[88px]">
115
+ <div className="flex flex-col gap-8 lg:gap-10">
116
+ <div className="flex flex-col gap-2">
117
+ <p className="text-sm font-bold leading-[18px] text-[#414c5e]">{phoneLabel}</p>
118
+ <a
119
+ href={`tel:${phone.replace(/\s/g, "")}`}
120
+ className="font-serif text-2xl leading-10 text-[#010623] transition-opacity duration-200 hover:opacity-80 sm:text-3xl lg:text-[32px]"
121
+ >
122
+ {phone}
123
+ </a>
124
+ </div>
125
+ <div className="flex flex-col gap-2">
126
+ <p className="text-sm font-bold leading-[18px] text-[#414c5e]">{emailLabel}</p>
127
+ <a
128
+ href={`mailto:${email}`}
129
+ className="font-serif text-2xl leading-10 text-[#010623] transition-opacity duration-200 hover:opacity-80 sm:text-3xl lg:text-[32px]"
130
+ >
131
+ {email}
132
+ </a>
133
+ </div>
134
+ </div>
135
+
136
+ {socialLinks?.length ? (
137
+ <div className="flex max-w-[220px] flex-col gap-6 lg:gap-8">
138
+ {socialLinks.map((link) => (
139
+ <a
140
+ key={link.label}
141
+ href={link.href ?? "#"}
142
+ className="flex items-center justify-between gap-4 font-serif text-2xl leading-10 text-[#010623] transition-opacity duration-200 hover:opacity-80 sm:text-3xl lg:text-[32px]"
143
+ >
144
+ <span>{link.label}</span>
145
+ <EastArrowIcon className="size-6 shrink-0" />
146
+ </a>
147
+ ))}
148
+ </div>
149
+ ) : null}
150
+ </div>
151
+ </div>
152
+
153
+ {/* Right — form card */}
154
+ <form
155
+ onSubmit={handleSubmit}
156
+ className="flex w-full max-w-[666px] flex-col gap-8 bg-white/90 p-6 backdrop-blur-[22px] sm:p-8 lg:gap-10 lg:p-10"
157
+ >
158
+ <h3 className="font-serif text-2xl font-light leading-tight tracking-[-0.01em] text-[#007cff] sm:text-3xl lg:text-[48px] lg:leading-[54px] lg:tracking-[-0.48px]">
159
+ {formTitle}
160
+ </h3>
161
+
162
+ <div className="flex flex-col gap-6 lg:gap-8">
163
+ <UnderlineField label="Full Name " required>
164
+ <input
165
+ type="text"
166
+ value={form.fullName}
167
+ onChange={(e) => setField("fullName", e.target.value)}
168
+ className={inputClassName}
169
+ required
170
+ />
171
+ </UnderlineField>
172
+
173
+ <UnderlineField label="Email " required>
174
+ <input
175
+ type="email"
176
+ value={form.email}
177
+ onChange={(e) => setField("email", e.target.value)}
178
+ className={inputClassName}
179
+ required
180
+ />
181
+ </UnderlineField>
182
+
183
+ <UnderlineField label="Phone" required>
184
+ <input
185
+ type="tel"
186
+ value={form.phone}
187
+ onChange={(e) => setField("phone", e.target.value)}
188
+ className={inputClassName}
189
+ required
190
+ />
191
+ </UnderlineField>
192
+
193
+ <UnderlineField label="Account type ">
194
+ <div className="relative">
195
+ <select
196
+ value={form.accountType}
197
+ onChange={(e) => setField("accountType", e.target.value)}
198
+ className={`${inputClassName} appearance-none pr-8`}
199
+ >
200
+ <option value="" disabled>
201
+ Select account type
202
+ </option>
203
+ {accountTypeOptions.map((option) => (
204
+ <option key={option} value={option}>
205
+ {option}
206
+ </option>
207
+ ))}
208
+ </select>
209
+ <ChevronDownIcon className="pointer-events-none absolute right-0 top-1/2 size-5 -translate-y-1/2 text-[#010623]" />
210
+ </div>
211
+ </UnderlineField>
212
+
213
+ <UnderlineField label="Message " required>
214
+ <textarea
215
+ value={form.message}
216
+ onChange={(e) => setField("message", e.target.value)}
217
+ rows={4}
218
+ className={`${inputClassName} min-h-[80px] resize-none pt-2 lg:min-h-[100px]`}
219
+ required
220
+ />
221
+ </UnderlineField>
222
+
223
+ <button
224
+ type="submit"
225
+ className="w-max max-w-full rounded-sm bg-[#0058b5] px-8 py-6 text-lg text-white transition-colors duration-200 ease-out hover:opacity-90 focus-visible:outline-2 focus-visible:outline-offset-2"
226
+ >
227
+ {submitText}
228
+ </button>
229
+ </div>
230
+ </form>
231
+ </div>
232
+ </div>
233
+ </section>
234
+ );
235
+ }
236
+
237
+ Contact04.propTypes = contact04PropTypes;
238
+
239
+ export default Contact04;
@@ -0,0 +1,50 @@
1
+ import PropTypes from "prop-types";
2
+
3
+ const socialLinkShape = PropTypes.shape({
4
+ label: PropTypes.string.isRequired,
5
+ href: PropTypes.string,
6
+ });
7
+
8
+ export const contact04PropTypes = {
9
+ label: PropTypes.string,
10
+ headline: PropTypes.string,
11
+ phoneLabel: PropTypes.string,
12
+ phone: PropTypes.string,
13
+ emailLabel: PropTypes.string,
14
+ email: PropTypes.string,
15
+ socialLinks: PropTypes.arrayOf(socialLinkShape),
16
+ formTitle: PropTypes.string,
17
+ accountTypeOptions: PropTypes.arrayOf(PropTypes.string),
18
+ submitText: PropTypes.string,
19
+ formData: PropTypes.shape({
20
+ fullName: PropTypes.string,
21
+ email: PropTypes.string,
22
+ phone: PropTypes.string,
23
+ accountType: PropTypes.string,
24
+ message: PropTypes.string,
25
+ }),
26
+ onFieldChange: PropTypes.func,
27
+ onSubmit: PropTypes.func,
28
+ className: PropTypes.string,
29
+ };
30
+
31
+ export const contact04DefaultProps = {
32
+ label: "Contact Us",
33
+ headline: "Get in touch with us",
34
+ phoneLabel: "Phone Number",
35
+ phone: "+88 02-9514-729-30",
36
+ emailLabel: "Email Address",
37
+ email: "info@bracepl.com",
38
+ socialLinks: [
39
+ { label: "Facebook", href: "#" },
40
+ { label: "Youtube", href: "#" },
41
+ ],
42
+ formTitle: "Fill out this brief from, and we'll get in touch with you shortly.",
43
+ accountTypeOptions: [
44
+ "Individual",
45
+ "Business",
46
+ "Enterprise",
47
+ "Partner",
48
+ ],
49
+ submitText: "Submit",
50
+ };
@@ -0,0 +1 @@
1
+ export { Contact04, default } from "./Contact04";
@@ -0,0 +1,272 @@
1
+ "use client";
2
+
3
+ import { useState } from "react";
4
+ import SafeImage from "../../atoms/SafeImage";
5
+ import {
6
+ contact05DefaultProps,
7
+ contact05PropTypes,
8
+ } from "./Contact05.propTypes";
9
+
10
+ const fieldClassName =
11
+ "h-[52px] w-full rounded-lg border border-white/30 bg-white/10 px-4 text-base text-white outline-none transition-colors duration-200 ease-out placeholder:text-white/60 focus:border-white focus:bg-white/15";
12
+
13
+ function ChevronDownIcon({ className = "" }) {
14
+ return (
15
+ <svg viewBox="0 0 20 20" fill="none" aria-hidden="true" className={className}>
16
+ <path
17
+ d="M5 8l5 5 5-5"
18
+ stroke="currentColor"
19
+ strokeWidth="1.5"
20
+ strokeLinecap="round"
21
+ strokeLinejoin="round"
22
+ />
23
+ </svg>
24
+ );
25
+ }
26
+
27
+ function ArrowRightIcon({ className = "" }) {
28
+ return (
29
+ <svg viewBox="0 0 24 24" fill="none" aria-hidden="true" className={className}>
30
+ <path
31
+ d="M5 12h14M13 6l6 6-6 6"
32
+ stroke="currentColor"
33
+ strokeWidth="1.5"
34
+ strokeLinecap="round"
35
+ strokeLinejoin="round"
36
+ />
37
+ </svg>
38
+ );
39
+ }
40
+
41
+ function PhoneIcon({ className = "" }) {
42
+ return (
43
+ <svg viewBox="0 0 24 24" fill="none" aria-hidden="true" className={className}>
44
+ <path
45
+ d="M6.5 3.5h3l1.5 4-2 1.5a11 11 0 005 5l1.5-2 4 1.5v3a1.5 1.5 0 01-1.5 1.5A14 14 0 013.5 5a1.5 1.5 0 011-1.5z"
46
+ stroke="currentColor"
47
+ strokeWidth="1.5"
48
+ strokeLinecap="round"
49
+ strokeLinejoin="round"
50
+ />
51
+ </svg>
52
+ );
53
+ }
54
+
55
+ function FieldLabel({ children, required = false }) {
56
+ return (
57
+ <label className="text-sm font-medium tracking-[0.28px] text-white/90">
58
+ {children}
59
+ {required ? <span className="text-white"> *</span> : null}
60
+ </label>
61
+ );
62
+ }
63
+
64
+ /**
65
+ * Contact05 — Red freight quote split with hero image and patterned form panel.
66
+ * Figma node 26:17380 at 1920px.
67
+ *
68
+ * @param {object} props - See Contact05.propTypes.js.
69
+ */
70
+ export function Contact05({
71
+ headline = contact05DefaultProps.headline,
72
+ description = contact05DefaultProps.description,
73
+ heroImage = contact05DefaultProps.heroImage,
74
+ heroAlt = contact05DefaultProps.heroAlt,
75
+ patternImage = contact05DefaultProps.patternImage,
76
+ serviceOptions = contact05DefaultProps.serviceOptions,
77
+ submitText = contact05DefaultProps.submitText,
78
+ callLabel = contact05DefaultProps.callLabel,
79
+ phoneNumber = contact05DefaultProps.phoneNumber,
80
+ phoneHref = contact05DefaultProps.phoneHref,
81
+ formData: controlledForm,
82
+ onFieldChange,
83
+ onSubmit,
84
+ className = "",
85
+ }) {
86
+ const [internalForm, setInternalForm] = useState({
87
+ serviceType: "",
88
+ organizationName: "",
89
+ fullName: "",
90
+ phone: "",
91
+ email: "",
92
+ message: "",
93
+ });
94
+
95
+ const form = controlledForm ?? internalForm;
96
+
97
+ const setField = (field, value) => {
98
+ onFieldChange?.(field, value);
99
+ if (!controlledForm) {
100
+ setInternalForm((prev) => ({ ...prev, [field]: value }));
101
+ }
102
+ };
103
+
104
+ const handleSubmit = (event) => {
105
+ event.preventDefault();
106
+ onSubmit?.(form);
107
+ };
108
+
109
+ return (
110
+ <section
111
+ className={["relative w-full overflow-hidden bg-white", className]
112
+ .filter(Boolean)
113
+ .join(" ")}
114
+ data-contact="contact05"
115
+ >
116
+ <div className="relative mx-auto flex w-full max-w-[1920px] flex-col lg:min-h-[700px] lg:flex-row xl:min-h-[900px]">
117
+ {/* Left — hero image */}
118
+ <div className="relative h-[50vh] w-full shrink-0 lg:h-auto lg:min-h-[700px] lg:w-1/2 xl:min-h-[900px]">
119
+ <SafeImage
120
+ src={heroImage}
121
+ alt={heroAlt}
122
+ fill
123
+ className="object-cover object-center"
124
+ sizes="(max-width: 1024px) 100vw, 50vw"
125
+ priority
126
+ />
127
+ </div>
128
+
129
+ {/* Right — red form panel */}
130
+ <div className="relative flex w-full items-center bg-[#e6232d] lg:w-1/2 lg:min-h-[700px] xl:min-h-[900px]">
131
+ {patternImage ? (
132
+ <div className="pointer-events-none absolute inset-0 opacity-20" aria-hidden="true">
133
+ <SafeImage
134
+ src={patternImage}
135
+ alt=""
136
+ fill
137
+ className="object-cover object-center"
138
+ sizes="50vw"
139
+ />
140
+ </div>
141
+ ) : null}
142
+
143
+ <form
144
+ onSubmit={handleSubmit}
145
+ className="relative z-10 flex w-full flex-col gap-8 px-4 py-12 sm:px-6 md:px-10 lg:px-16 lg:py-16 xl:px-[80px] xl:py-20"
146
+ >
147
+ <div className="flex flex-col gap-4 text-white">
148
+ <h2 className="text-3xl font-bold leading-[1.1] tracking-[-0.02em] sm:text-4xl xl:text-[48px]">
149
+ {headline}
150
+ </h2>
151
+ <p className="text-base leading-[1.4] text-white/85 sm:text-lg">
152
+ {description}
153
+ </p>
154
+ </div>
155
+
156
+ <div className="flex flex-col gap-5">
157
+ <div className="flex flex-col gap-2">
158
+ <FieldLabel required>Service Type</FieldLabel>
159
+ <div className="relative">
160
+ <select
161
+ value={form.serviceType}
162
+ onChange={(e) => setField("serviceType", e.target.value)}
163
+ className={`${fieldClassName} appearance-none pr-12`}
164
+ required
165
+ >
166
+ <option value="" disabled className="text-[#1b1e2e]">
167
+ Select service
168
+ </option>
169
+ {serviceOptions.map((option) => (
170
+ <option key={option} value={option} className="text-[#1b1e2e]">
171
+ {option}
172
+ </option>
173
+ ))}
174
+ </select>
175
+ <ChevronDownIcon className="pointer-events-none absolute right-4 top-1/2 size-5 -translate-y-1/2 text-white" />
176
+ </div>
177
+ </div>
178
+
179
+ <div className="flex flex-col gap-2">
180
+ <FieldLabel required>Organization Name</FieldLabel>
181
+ <input
182
+ type="text"
183
+ value={form.organizationName}
184
+ onChange={(e) => setField("organizationName", e.target.value)}
185
+ className={fieldClassName}
186
+ required
187
+ />
188
+ </div>
189
+
190
+ <div className="grid grid-cols-1 gap-5 sm:grid-cols-2">
191
+ <div className="flex flex-col gap-2">
192
+ <FieldLabel required>Full Name</FieldLabel>
193
+ <input
194
+ type="text"
195
+ value={form.fullName}
196
+ onChange={(e) => setField("fullName", e.target.value)}
197
+ className={fieldClassName}
198
+ required
199
+ />
200
+ </div>
201
+ <div className="flex flex-col gap-2">
202
+ <FieldLabel required>Phone</FieldLabel>
203
+ <input
204
+ type="tel"
205
+ value={form.phone}
206
+ onChange={(e) => setField("phone", e.target.value)}
207
+ className={fieldClassName}
208
+ required
209
+ />
210
+ </div>
211
+ </div>
212
+
213
+ <div className="flex flex-col gap-2">
214
+ <FieldLabel required>Email</FieldLabel>
215
+ <input
216
+ type="email"
217
+ value={form.email}
218
+ onChange={(e) => setField("email", e.target.value)}
219
+ className={fieldClassName}
220
+ required
221
+ />
222
+ </div>
223
+
224
+ <div className="flex flex-col gap-2">
225
+ <FieldLabel>Message</FieldLabel>
226
+ <textarea
227
+ value={form.message}
228
+ onChange={(e) => setField("message", e.target.value)}
229
+ rows={4}
230
+ className="min-h-[120px] w-full resize-none rounded-lg border border-white/30 bg-white/10 px-4 py-3 text-base text-white outline-none transition-colors duration-200 ease-out placeholder:text-white/60 focus:border-white focus:bg-white/15"
231
+ />
232
+ </div>
233
+ </div>
234
+
235
+ <button
236
+ type="submit"
237
+ className="flex h-14 w-full items-center justify-center gap-2 rounded-lg bg-white text-base font-semibold tracking-[0.32px] text-[#e6232d] transition-colors duration-200 ease-out hover:bg-white/90 focus-visible:outline-2 focus-visible:outline-offset-2"
238
+ >
239
+ {submitText}
240
+ <ArrowRightIcon className="size-5" />
241
+ </button>
242
+
243
+ <div className="flex items-center gap-4">
244
+ <div className="h-px flex-1 bg-white/40" />
245
+ <span className="text-sm font-medium uppercase tracking-[0.96px] text-white/80">
246
+ OR
247
+ </span>
248
+ <div className="h-px flex-1 bg-white/40" />
249
+ </div>
250
+
251
+ <a
252
+ href={phoneHref}
253
+ className="flex items-center justify-center gap-3 text-white transition-opacity duration-200 ease-out hover:opacity-90 focus-visible:outline-2 focus-visible:outline-offset-2"
254
+ >
255
+ <PhoneIcon className="size-6 shrink-0" />
256
+ <span className="flex flex-col items-start sm:flex-row sm:items-center sm:gap-2">
257
+ <span className="text-sm font-semibold uppercase tracking-[0.96px]">
258
+ {callLabel}
259
+ </span>
260
+ <span className="text-lg font-bold sm:text-xl">{phoneNumber}</span>
261
+ </span>
262
+ </a>
263
+ </form>
264
+ </div>
265
+ </div>
266
+ </section>
267
+ );
268
+ }
269
+
270
+ Contact05.propTypes = contact05PropTypes;
271
+
272
+ export default Contact05;
@@ -0,0 +1,49 @@
1
+ import PropTypes from "prop-types";
2
+
3
+ export const contact05PropTypes = {
4
+ headline: PropTypes.string,
5
+ description: PropTypes.string,
6
+ heroImage: PropTypes.string,
7
+ heroAlt: PropTypes.string,
8
+ patternImage: PropTypes.string,
9
+ serviceOptions: PropTypes.arrayOf(PropTypes.string),
10
+ submitText: PropTypes.string,
11
+ callLabel: PropTypes.string,
12
+ phoneNumber: PropTypes.string,
13
+ phoneHref: PropTypes.string,
14
+ formData: PropTypes.shape({
15
+ serviceType: PropTypes.string,
16
+ organizationName: PropTypes.string,
17
+ fullName: PropTypes.string,
18
+ phone: PropTypes.string,
19
+ email: PropTypes.string,
20
+ message: PropTypes.string,
21
+ }),
22
+ onFieldChange: PropTypes.func,
23
+ onSubmit: PropTypes.func,
24
+ className: PropTypes.string,
25
+ };
26
+
27
+ export const contact05DefaultProps = {
28
+ headline: "Request an Expedited Freight Quote Now",
29
+ description:
30
+ "Tell us about your shipment and our logistics team will respond with a tailored quote within one business day.",
31
+ heroImage: "/contact/contact05/hero.jpg",
32
+ heroAlt: "Freight truck on highway at sunset",
33
+ patternImage: "/contact/contact05/pattern.png",
34
+ serviceOptions: [
35
+ "Full truckload (FTL)",
36
+ "Less than truckload (LTL)",
37
+ "Intermodal",
38
+ "Expedited freight",
39
+ "International shipping",
40
+ ],
41
+ submitText: "Send Message",
42
+ callLabel: "CALL US",
43
+ phoneNumber: "+ (880) 2-2222-94745",
44
+ phoneHref: "tel:+8802222294745",
45
+ formData: undefined,
46
+ onFieldChange: undefined,
47
+ onSubmit: undefined,
48
+ className: "",
49
+ };
@@ -0,0 +1 @@
1
+ export { Contact05, default } from "./Contact05";