@burdenoff/website-sdk 2026.817.1 → 2026.824.1

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/index.d.mts CHANGED
@@ -69,6 +69,36 @@ interface CareersApplyFormProps {
69
69
  }
70
70
  declare function CareersApplyForm({ productName, recaptchaSiteKey, positions, postings, heroTitle, heroDescription, headingLevel, maxResumeBytes, className, seo, }: CareersApplyFormProps): react_jsx_runtime.JSX.Element;
71
71
 
72
+ interface CareersSubmissionFormProps {
73
+ /** Product name (falls back to product context / SDK config). */
74
+ productName?: string;
75
+ /** reCAPTCHA site key (falls back to SDK config). */
76
+ recaptchaSiteKey?: string;
77
+ /**
78
+ * Job postings for the dropdown (e.g. from `usePublicJobPostings`). When
79
+ * non-empty the selected posting's ID is sent as `jobPostingId` and its
80
+ * title as `position`. When empty, a free-text "position" input is shown
81
+ * instead so the form works before a postings list exists.
82
+ */
83
+ postings?: readonly CareersPostingOption[];
84
+ heroTitle?: string;
85
+ heroDescription?: string;
86
+ /**
87
+ * `1` (default) renders a full page hero `<h1>`; `2` renders a compact
88
+ * `<h2>` section header for embedding under a page that owns the `<h1>`.
89
+ */
90
+ headingLevel?: 1 | 2;
91
+ className?: string;
92
+ seo?: {
93
+ title?: string;
94
+ description?: string;
95
+ keywords?: string;
96
+ };
97
+ }
98
+ /** True when `value` parses as an absolute http(s) URL. */
99
+ declare function isValidUrl(value: string): boolean;
100
+ declare function CareersSubmissionForm({ productName, recaptchaSiteKey, postings, heroTitle, heroDescription, headingLevel, className, seo, }: CareersSubmissionFormProps): react_jsx_runtime.JSX.Element;
101
+
72
102
  declare const buttonVariants: (props?: ({
73
103
  variant?: "link" | "default" | "destructive" | "outline" | "secondary" | "ghost" | null | undefined;
74
104
  size?: "icon" | "default" | "sm" | "lg" | null | undefined;
@@ -95,4 +125,4 @@ declare const Textarea: React.ForwardRefExoticComponent<Omit<React.DetailedHTMLP
95
125
  */
96
126
  declare const ECOSYSTEM_PRODUCTS: WebsiteSwitcherProduct[];
97
127
 
98
- export { Button, type ButtonProps, CareersApplyForm, type CareersApplyFormProps, type CareersPostingOption, ECOSYSTEM_PRODUCTS, Input, Label, Select, Textarea, WebsiteSwitcherProduct, buttonVariants };
128
+ export { Button, type ButtonProps, CareersApplyForm, type CareersApplyFormProps, type CareersPostingOption, CareersSubmissionForm, type CareersSubmissionFormProps, ECOSYSTEM_PRODUCTS, Input, Label, Select, Textarea, WebsiteSwitcherProduct, buttonVariants, isValidUrl };
package/dist/index.d.ts CHANGED
@@ -69,6 +69,36 @@ interface CareersApplyFormProps {
69
69
  }
70
70
  declare function CareersApplyForm({ productName, recaptchaSiteKey, positions, postings, heroTitle, heroDescription, headingLevel, maxResumeBytes, className, seo, }: CareersApplyFormProps): react_jsx_runtime.JSX.Element;
71
71
 
72
+ interface CareersSubmissionFormProps {
73
+ /** Product name (falls back to product context / SDK config). */
74
+ productName?: string;
75
+ /** reCAPTCHA site key (falls back to SDK config). */
76
+ recaptchaSiteKey?: string;
77
+ /**
78
+ * Job postings for the dropdown (e.g. from `usePublicJobPostings`). When
79
+ * non-empty the selected posting's ID is sent as `jobPostingId` and its
80
+ * title as `position`. When empty, a free-text "position" input is shown
81
+ * instead so the form works before a postings list exists.
82
+ */
83
+ postings?: readonly CareersPostingOption[];
84
+ heroTitle?: string;
85
+ heroDescription?: string;
86
+ /**
87
+ * `1` (default) renders a full page hero `<h1>`; `2` renders a compact
88
+ * `<h2>` section header for embedding under a page that owns the `<h1>`.
89
+ */
90
+ headingLevel?: 1 | 2;
91
+ className?: string;
92
+ seo?: {
93
+ title?: string;
94
+ description?: string;
95
+ keywords?: string;
96
+ };
97
+ }
98
+ /** True when `value` parses as an absolute http(s) URL. */
99
+ declare function isValidUrl(value: string): boolean;
100
+ declare function CareersSubmissionForm({ productName, recaptchaSiteKey, postings, heroTitle, heroDescription, headingLevel, className, seo, }: CareersSubmissionFormProps): react_jsx_runtime.JSX.Element;
101
+
72
102
  declare const buttonVariants: (props?: ({
73
103
  variant?: "link" | "default" | "destructive" | "outline" | "secondary" | "ghost" | null | undefined;
74
104
  size?: "icon" | "default" | "sm" | "lg" | null | undefined;
@@ -95,4 +125,4 @@ declare const Textarea: React.ForwardRefExoticComponent<Omit<React.DetailedHTMLP
95
125
  */
96
126
  declare const ECOSYSTEM_PRODUCTS: WebsiteSwitcherProduct[];
97
127
 
98
- export { Button, type ButtonProps, CareersApplyForm, type CareersApplyFormProps, type CareersPostingOption, ECOSYSTEM_PRODUCTS, Input, Label, Select, Textarea, WebsiteSwitcherProduct, buttonVariants };
128
+ export { Button, type ButtonProps, CareersApplyForm, type CareersApplyFormProps, type CareersPostingOption, CareersSubmissionForm, type CareersSubmissionFormProps, ECOSYSTEM_PRODUCTS, Input, Label, Select, Textarea, WebsiteSwitcherProduct, buttonVariants, isValidUrl };
package/dist/index.js CHANGED
@@ -3,7 +3,7 @@
3
3
  var React = require('react');
4
4
  var jsxRuntime = require('react/jsx-runtime');
5
5
  var lucideReact = require('lucide-react');
6
- var ReCAPTCHA3 = require('react-google-recaptcha');
6
+ var ReCAPTCHA4 = require('react-google-recaptcha');
7
7
  var sonner = require('sonner');
8
8
  var reactHelmetAsync = require('react-helmet-async');
9
9
  var reactSlot = require('@radix-ui/react-slot');
@@ -41,7 +41,7 @@ function _interopNamespace(e) {
41
41
  }
42
42
 
43
43
  var React__namespace = /*#__PURE__*/_interopNamespace(React);
44
- var ReCAPTCHA3__default = /*#__PURE__*/_interopDefault(ReCAPTCHA3);
44
+ var ReCAPTCHA4__default = /*#__PURE__*/_interopDefault(ReCAPTCHA4);
45
45
  var LabelPrimitive__namespace = /*#__PURE__*/_interopNamespace(LabelPrimitive);
46
46
  var DOMPurify__default = /*#__PURE__*/_interopDefault(DOMPurify);
47
47
  var ReactMarkdown__default = /*#__PURE__*/_interopDefault(ReactMarkdown);
@@ -1011,7 +1011,7 @@ function ContactPage({
1011
1011
  ] }),
1012
1012
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-4", children: [
1013
1013
  effectiveRecaptchaSiteKey && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-center sm:justify-start", children: /* @__PURE__ */ jsxRuntime.jsx(
1014
- ReCAPTCHA3__default.default,
1014
+ ReCAPTCHA4__default.default,
1015
1015
  {
1016
1016
  ref: recaptchaRef,
1017
1017
  sitekey: effectiveRecaptchaSiteKey
@@ -1185,16 +1185,38 @@ var PUBLIC_CONTENT_PAGES_QUERY = `
1185
1185
  }
1186
1186
  }
1187
1187
  `;
1188
+ function resolveContentPageGate(resolvedProductId, hasProvider, productLoading) {
1189
+ if (resolvedProductId) return "fetch";
1190
+ return hasProvider && productLoading ? "wait" : "unavailable";
1191
+ }
1188
1192
  function useContentPage(slug, options) {
1189
1193
  const client = useWebSDK();
1190
1194
  const config = useWebSDKConfig();
1191
- const { product } = useProduct();
1195
+ const {
1196
+ product,
1197
+ loading: productLoading,
1198
+ error: productError,
1199
+ hasProvider
1200
+ } = useProduct();
1192
1201
  const resolvedProductId = product?.id || config.productId;
1202
+ const gate = resolveContentPageGate(
1203
+ resolvedProductId,
1204
+ hasProvider,
1205
+ productLoading
1206
+ );
1193
1207
  const [data, setData] = React.useState(null);
1194
1208
  const [loading, setLoading] = React.useState(true);
1195
1209
  const [error, setError] = React.useState(null);
1196
1210
  const fetchPage = React.useCallback(async () => {
1197
- if (!resolvedProductId) return;
1211
+ if (gate === "wait") return;
1212
+ if (gate === "unavailable") {
1213
+ setData(null);
1214
+ setError(
1215
+ productError ?? "Product unavailable \u2014 cannot load content page"
1216
+ );
1217
+ setLoading(false);
1218
+ return;
1219
+ }
1198
1220
  setLoading(true);
1199
1221
  setError(null);
1200
1222
  try {
@@ -1219,7 +1241,7 @@ function useContentPage(slug, options) {
1219
1241
  } finally {
1220
1242
  setLoading(false);
1221
1243
  }
1222
- }, [client, resolvedProductId, slug, options?.resolve]);
1244
+ }, [client, gate, productError, resolvedProductId, slug, options?.resolve]);
1223
1245
  React.useEffect(() => {
1224
1246
  fetchPage();
1225
1247
  }, [fetchPage]);
@@ -1977,7 +1999,7 @@ function CareersApplyForm({
1977
1999
  )
1978
2000
  ] }),
1979
2001
  effectiveRecaptchaSiteKey && /* @__PURE__ */ jsxRuntime.jsx(
1980
- ReCAPTCHA3__default.default,
2002
+ ReCAPTCHA4__default.default,
1981
2003
  {
1982
2004
  ref: recaptchaRef,
1983
2005
  sitekey: effectiveRecaptchaSiteKey
@@ -2002,6 +2024,313 @@ function CareersApplyForm({
2002
2024
  )
2003
2025
  ] });
2004
2026
  }
2027
+ var SUBMIT_ASSIGNMENT_RESPONSE_MUTATION = (
2028
+ /* GraphQL */
2029
+ `
2030
+ mutation SubmitAssignmentResponse($input: SubmitAssignmentResponseInput!) {
2031
+ submitAssignmentResponse(input: $input) {
2032
+ success
2033
+ message
2034
+ applicationId
2035
+ roundId
2036
+ }
2037
+ }
2038
+ `
2039
+ );
2040
+ function isValidUrl(value) {
2041
+ try {
2042
+ const url = new URL(value);
2043
+ return url.protocol === "http:" || url.protocol === "https:";
2044
+ } catch {
2045
+ return false;
2046
+ }
2047
+ }
2048
+ async function submitAssignmentResponse(client, data) {
2049
+ try {
2050
+ const result = await client.mutate(SUBMIT_ASSIGNMENT_RESPONSE_MUTATION, {
2051
+ input: {
2052
+ email: data.email.trim().toLowerCase(),
2053
+ repoUrl: data.repoUrl.trim(),
2054
+ videoUrl: data.videoUrl.trim(),
2055
+ recaptchaToken: data.recaptchaToken ?? "",
2056
+ ...data.name ? { name: data.name } : {},
2057
+ ...data.position ? { position: data.position } : {},
2058
+ ...data.jobPostingId ? { jobPostingId: data.jobPostingId } : {},
2059
+ ...data.notes ? { notes: data.notes } : {}
2060
+ }
2061
+ });
2062
+ if (result.errors?.length) {
2063
+ return {
2064
+ success: false,
2065
+ message: result.errors[0]?.message ?? "Failed to submit assignment"
2066
+ };
2067
+ }
2068
+ const response = result.data?.submitAssignmentResponse;
2069
+ return response?.success ? { success: true, message: response.message || "Assignment submitted!" } : {
2070
+ success: false,
2071
+ message: response?.message || "Failed to submit assignment"
2072
+ };
2073
+ } catch (error) {
2074
+ console.error("Assignment submission error:", error);
2075
+ return { success: false, message: "Network error. Please try again." };
2076
+ }
2077
+ }
2078
+ var NO_APPLICATION_HINT = "Please use the same email address you applied with \u2014 or submit an application first.";
2079
+ function isNoApplicationError(message) {
2080
+ return /no application|not found/i.test(message);
2081
+ }
2082
+ var OTHER_POSITION = "__other__";
2083
+ function CareersSubmissionForm({
2084
+ productName,
2085
+ recaptchaSiteKey,
2086
+ postings = [],
2087
+ heroTitle = "Submit your assignment",
2088
+ heroDescription = "Share your GitHub repo and a short video walkthrough \u2014 make sure to use the same email you applied with.",
2089
+ headingLevel = 1,
2090
+ className,
2091
+ seo
2092
+ }) {
2093
+ const client = useWebSDK();
2094
+ const config = useWebSDKConfig();
2095
+ const { product } = useProduct();
2096
+ const displayName = productName ?? product?.name ?? config.productId;
2097
+ const effectiveRecaptchaSiteKey = recaptchaSiteKey ?? product?.recaptchaSiteKey ?? config.recaptchaSiteKey ?? "";
2098
+ const recaptchaRef = React.useRef(null);
2099
+ const [isSubmitting, setIsSubmitting] = React.useState(false);
2100
+ const [submittedMessage, setSubmittedMessage] = React.useState(null);
2101
+ const [formError, setFormError] = React.useState(null);
2102
+ const [useFreeTextPosition, setUseFreeTextPosition] = React.useState(
2103
+ postings.length === 0
2104
+ );
2105
+ const handleSubmit = async (e) => {
2106
+ e.preventDefault();
2107
+ setFormError(null);
2108
+ const recaptchaToken = recaptchaRef.current?.getValue() ?? "";
2109
+ if (effectiveRecaptchaSiteKey && !recaptchaToken) {
2110
+ sonner.toast.error("Please complete the reCAPTCHA verification");
2111
+ return;
2112
+ }
2113
+ const form = e.currentTarget;
2114
+ const formData = new FormData(form);
2115
+ const name = (formData.get("name") ?? "").trim();
2116
+ const email = (formData.get("email") ?? "").trim();
2117
+ const positionValue = (formData.get("position") ?? "").trim();
2118
+ const repoUrl = (formData.get("repoUrl") ?? "").trim();
2119
+ const videoUrl = (formData.get("videoUrl") ?? "").trim();
2120
+ const notes = (formData.get("notes") ?? "").trim();
2121
+ const selectedPosting = postings.length > 0 && !useFreeTextPosition ? postings.find((p) => p.id === positionValue) : void 0;
2122
+ const position = selectedPosting ? selectedPosting.title : positionValue;
2123
+ if (!email) {
2124
+ setFormError("Please enter the email address you applied with.");
2125
+ return;
2126
+ }
2127
+ if (!position) {
2128
+ setFormError("Please select or enter the position you applied for.");
2129
+ return;
2130
+ }
2131
+ if (!repoUrl || !isValidUrl(repoUrl)) {
2132
+ setFormError(
2133
+ "Please enter a valid GitHub repository URL (e.g. https://github.com/you/project)."
2134
+ );
2135
+ return;
2136
+ }
2137
+ if (!videoUrl || !isValidUrl(videoUrl)) {
2138
+ setFormError(
2139
+ "Please enter a valid video URL (e.g. a Jam or Loom link starting with https://)."
2140
+ );
2141
+ return;
2142
+ }
2143
+ setIsSubmitting(true);
2144
+ try {
2145
+ const result = await submitAssignmentResponse(client, {
2146
+ name: name || void 0,
2147
+ email,
2148
+ position,
2149
+ jobPostingId: selectedPosting?.id,
2150
+ repoUrl,
2151
+ videoUrl,
2152
+ notes: notes || void 0,
2153
+ recaptchaToken
2154
+ });
2155
+ if (result.success) {
2156
+ setSubmittedMessage(result.message ?? "Assignment submitted!");
2157
+ sonner.toast.success(result.message ?? "Assignment submitted!");
2158
+ } else {
2159
+ const message = result.message ?? "Failed to submit assignment";
2160
+ setFormError(
2161
+ isNoApplicationError(message) ? `${message} ${NO_APPLICATION_HINT}` : message
2162
+ );
2163
+ sonner.toast.error(message);
2164
+ recaptchaRef.current?.reset();
2165
+ }
2166
+ } catch (err) {
2167
+ console.error("Careers submission form error:", err);
2168
+ setFormError("Something went wrong. Please try again.");
2169
+ sonner.toast.error("Something went wrong. Please try again.");
2170
+ recaptchaRef.current?.reset();
2171
+ } finally {
2172
+ setIsSubmitting(false);
2173
+ }
2174
+ };
2175
+ const Heading = headingLevel === 2 ? "h2" : "h1";
2176
+ const headingClass = headingLevel === 2 ? "text-2xl md:text-3xl font-bold mb-3" : "text-3xl sm:text-4xl md:text-5xl font-bold mb-4 sm:mb-6";
2177
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className, children: [
2178
+ seo && /* @__PURE__ */ jsxRuntime.jsx(
2179
+ PageHead,
2180
+ {
2181
+ title: seo.title ?? `Submit your assignment \u2014 ${displayName}`,
2182
+ description: seo.description ?? heroDescription,
2183
+ keywords: seo.keywords
2184
+ }
2185
+ ),
2186
+ /* @__PURE__ */ jsxRuntime.jsx(
2187
+ "section",
2188
+ {
2189
+ className: headingLevel === 2 ? "py-12 md:py-16" : "bg-gradient-to-b from-background to-muted/20 py-12 sm:py-16 md:py-20 lg:py-24",
2190
+ children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "max-w-3xl mx-auto px-4 sm:px-6 lg:px-8", children: [
2191
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center mb-8 sm:mb-10", children: [
2192
+ /* @__PURE__ */ jsxRuntime.jsx(Heading, { className: headingClass, children: heroTitle }),
2193
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-lg text-muted-foreground leading-relaxed", children: heroDescription })
2194
+ ] }),
2195
+ submittedMessage ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center rounded-xl border border-border bg-card p-10", children: [
2196
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.CheckCircle, { className: "mx-auto mb-4 h-12 w-12 text-accent" }),
2197
+ /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-xl font-semibold text-foreground mb-2", children: "Assignment received" }),
2198
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-muted-foreground", children: submittedMessage })
2199
+ ] }) : /* @__PURE__ */ jsxRuntime.jsxs("form", { onSubmit: handleSubmit, className: "space-y-5", children: [
2200
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-1 sm:grid-cols-2 gap-5", children: [
2201
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
2202
+ /* @__PURE__ */ jsxRuntime.jsx(Label, { htmlFor: "name", children: "Name" }),
2203
+ /* @__PURE__ */ jsxRuntime.jsx(Input, { id: "name", name: "name", placeholder: "Ada Lovelace" })
2204
+ ] }),
2205
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
2206
+ /* @__PURE__ */ jsxRuntime.jsx(Label, { htmlFor: "email", children: "Email *" }),
2207
+ /* @__PURE__ */ jsxRuntime.jsx(
2208
+ Input,
2209
+ {
2210
+ id: "email",
2211
+ name: "email",
2212
+ type: "email",
2213
+ required: true,
2214
+ placeholder: "ada@example.com"
2215
+ }
2216
+ )
2217
+ ] })
2218
+ ] }),
2219
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
2220
+ /* @__PURE__ */ jsxRuntime.jsx(Label, { htmlFor: "position", children: "Position *" }),
2221
+ postings.length > 0 && !useFreeTextPosition ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2222
+ /* @__PURE__ */ jsxRuntime.jsxs(
2223
+ "select",
2224
+ {
2225
+ id: "position",
2226
+ name: "position",
2227
+ required: true,
2228
+ defaultValue: "",
2229
+ onChange: (e) => {
2230
+ if (e.target.value === OTHER_POSITION) {
2231
+ setUseFreeTextPosition(true);
2232
+ }
2233
+ },
2234
+ className: "flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-ring",
2235
+ children: [
2236
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "", disabled: true, children: "Select a role" }),
2237
+ postings.map((p) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: p.id, children: p.title }, p.id)),
2238
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: OTHER_POSITION, children: "My role isn't listed" })
2239
+ ]
2240
+ }
2241
+ ),
2242
+ /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "mt-1 text-xs text-muted-foreground", children: [
2243
+ "Applied to a role that's since closed? Choose",
2244
+ " ",
2245
+ /* @__PURE__ */ jsxRuntime.jsx("em", { children: "My role isn't listed" }),
2246
+ " and type it in \u2014 you can still submit."
2247
+ ] })
2248
+ ] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2249
+ /* @__PURE__ */ jsxRuntime.jsx(
2250
+ Input,
2251
+ {
2252
+ id: "position",
2253
+ name: "position",
2254
+ required: true,
2255
+ placeholder: "Role you applied for"
2256
+ }
2257
+ ),
2258
+ postings.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(
2259
+ "button",
2260
+ {
2261
+ type: "button",
2262
+ onClick: () => setUseFreeTextPosition(false),
2263
+ className: "mt-1 text-xs text-accent hover:underline",
2264
+ children: "Choose from open roles instead"
2265
+ }
2266
+ )
2267
+ ] })
2268
+ ] }),
2269
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
2270
+ /* @__PURE__ */ jsxRuntime.jsx(Label, { htmlFor: "repoUrl", children: "GitHub repository URL *" }),
2271
+ /* @__PURE__ */ jsxRuntime.jsx(
2272
+ Input,
2273
+ {
2274
+ id: "repoUrl",
2275
+ name: "repoUrl",
2276
+ type: "url",
2277
+ required: true,
2278
+ placeholder: "https://github.com/you/assignment"
2279
+ }
2280
+ )
2281
+ ] }),
2282
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
2283
+ /* @__PURE__ */ jsxRuntime.jsx(Label, { htmlFor: "videoUrl", children: "Video walkthrough URL (Jam, Loom, \u2026) *" }),
2284
+ /* @__PURE__ */ jsxRuntime.jsx(
2285
+ Input,
2286
+ {
2287
+ id: "videoUrl",
2288
+ name: "videoUrl",
2289
+ type: "url",
2290
+ required: true,
2291
+ placeholder: "https://jam.dev/c/\u2026"
2292
+ }
2293
+ )
2294
+ ] }),
2295
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
2296
+ /* @__PURE__ */ jsxRuntime.jsx(Label, { htmlFor: "notes", children: "Notes" }),
2297
+ /* @__PURE__ */ jsxRuntime.jsx(
2298
+ Textarea,
2299
+ {
2300
+ id: "notes",
2301
+ name: "notes",
2302
+ rows: 4,
2303
+ placeholder: "Anything we should know before reviewing\u2026"
2304
+ }
2305
+ )
2306
+ ] }),
2307
+ formError && /* @__PURE__ */ jsxRuntime.jsx("p", { role: "alert", className: "text-sm text-destructive", children: formError }),
2308
+ effectiveRecaptchaSiteKey && /* @__PURE__ */ jsxRuntime.jsx(
2309
+ ReCAPTCHA4__default.default,
2310
+ {
2311
+ ref: recaptchaRef,
2312
+ sitekey: effectiveRecaptchaSiteKey
2313
+ }
2314
+ ),
2315
+ /* @__PURE__ */ jsxRuntime.jsx(
2316
+ Button,
2317
+ {
2318
+ type: "submit",
2319
+ size: "lg",
2320
+ disabled: isSubmitting,
2321
+ className: "w-full sm:w-auto",
2322
+ children: isSubmitting ? "Submitting\u2026" : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2323
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Send, { className: "mr-2 h-4 w-4" }),
2324
+ " Submit assignment"
2325
+ ] })
2326
+ }
2327
+ )
2328
+ ] })
2329
+ ] })
2330
+ }
2331
+ )
2332
+ ] });
2333
+ }
2005
2334
  var newsletterSchema = zod.z.object({
2006
2335
  email: zod.z.string().email("Please enter a valid email address")
2007
2336
  });
@@ -2291,7 +2620,7 @@ function NewsletterPage({
2291
2620
  errors.email && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-destructive", children: errors.email.message })
2292
2621
  ] }),
2293
2622
  effectiveRecaptchaSiteKey && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(
2294
- ReCAPTCHA3__default.default,
2623
+ ReCAPTCHA4__default.default,
2295
2624
  {
2296
2625
  ref: recaptchaRef,
2297
2626
  sitekey: effectiveRecaptchaSiteKey
@@ -2343,7 +2672,7 @@ function NewsletterPage({
2343
2672
  errors.email && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-destructive", children: errors.email.message })
2344
2673
  ] }),
2345
2674
  effectiveRecaptchaSiteKey && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-center sm:justify-start", children: /* @__PURE__ */ jsxRuntime.jsx(
2346
- ReCAPTCHA3__default.default,
2675
+ ReCAPTCHA4__default.default,
2347
2676
  {
2348
2677
  ref: recaptchaRef,
2349
2678
  sitekey: effectiveRecaptchaSiteKey
@@ -2521,7 +2850,7 @@ function NewsletterForm({
2521
2850
  justifyContent: "flex-start"
2522
2851
  },
2523
2852
  children: /* @__PURE__ */ jsxRuntime.jsx(
2524
- ReCAPTCHA3__default.default,
2853
+ ReCAPTCHA4__default.default,
2525
2854
  {
2526
2855
  ref: recaptchaRef,
2527
2856
  sitekey: effectiveRecaptchaSiteKey,
@@ -9267,6 +9596,7 @@ exports.BlogListPage = BlogListPage;
9267
9596
  exports.BlogPostPage = BlogPostPage;
9268
9597
  exports.Button = Button;
9269
9598
  exports.CareersApplyForm = CareersApplyForm;
9599
+ exports.CareersSubmissionForm = CareersSubmissionForm;
9270
9600
  exports.CaseStudiesListPage = CaseStudiesListPage;
9271
9601
  exports.CaseStudyPage = CaseStudyPage;
9272
9602
  exports.ChangelogPage = ChangelogPage;
@@ -9313,6 +9643,7 @@ exports.fetchPublicJobPosting = fetchPublicJobPosting;
9313
9643
  exports.fetchPublicJobPostings = fetchPublicJobPostings;
9314
9644
  exports.formatDownloads = formatDownloads;
9315
9645
  exports.formatPrice = formatPrice2;
9646
+ exports.isValidUrl = isValidUrl;
9316
9647
  exports.useContentPage = useContentPage;
9317
9648
  exports.useContentPages = useContentPages;
9318
9649
  exports.useProduct = useProduct;