@burdenoff/website-sdk 2026.522.3 → 2026.522.5

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.mjs CHANGED
@@ -1,8 +1,8 @@
1
1
  import * as React from 'react';
2
2
  import { createContext, useMemo, useContext, useState, useCallback, useLayoutEffect, useRef, useEffect } from 'react';
3
3
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
4
- import { Instagram, Youtube, Facebook, Github, Linkedin, Twitter, Mail, Phone, MapPin, Clock, Send, FileText, Download } from 'lucide-react';
5
- import ReCAPTCHA2 from 'react-google-recaptcha';
4
+ import { Instagram, Youtube, Facebook, Github, Linkedin, Twitter, Mail, Phone, MapPin, Clock, Send, CheckCircle, Upload, FileText, Download } from 'lucide-react';
5
+ import ReCAPTCHA3 from 'react-google-recaptcha';
6
6
  import { toast } from 'sonner';
7
7
  import { Helmet } from 'react-helmet-async';
8
8
  import { Slot } from '@radix-ui/react-slot';
@@ -910,7 +910,7 @@ function ContactPage({
910
910
  ] }),
911
911
  /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
912
912
  effectiveRecaptchaSiteKey && /* @__PURE__ */ jsx("div", { className: "flex justify-center sm:justify-start", children: /* @__PURE__ */ jsx(
913
- ReCAPTCHA2,
913
+ ReCAPTCHA3,
914
914
  {
915
915
  ref: recaptchaRef,
916
916
  sitekey: effectiveRecaptchaSiteKey
@@ -1489,6 +1489,401 @@ function PartnersPage(props) {
1489
1489
  )
1490
1490
  ] });
1491
1491
  }
1492
+ var ALLOWED_RESUME_TYPES = {
1493
+ "application/pdf": "PDF",
1494
+ "application/msword": "DOC",
1495
+ "application/vnd.openxmlformats-officedocument.wordprocessingml.document": "DOCX"
1496
+ };
1497
+ var EXT_TO_MIME = {
1498
+ pdf: "application/pdf",
1499
+ doc: "application/msword",
1500
+ docx: "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
1501
+ };
1502
+ var DEFAULT_MAX_RESUME_BYTES = 10 * 1024 * 1024;
1503
+ function resolveResumeType(file) {
1504
+ if (ALLOWED_RESUME_TYPES[file.type]) return file.type;
1505
+ const ext = file.name.split(".").pop()?.toLowerCase();
1506
+ return ext && EXT_TO_MIME[ext] ? EXT_TO_MIME[ext] : null;
1507
+ }
1508
+ var SUBMIT_JOB_APPLICATION_MUTATION = (
1509
+ /* GraphQL */
1510
+ `
1511
+ mutation SubmitJobApplication($input: SubmitJobApplicationInput!) {
1512
+ submitJobApplication(input: $input) {
1513
+ success
1514
+ message
1515
+ }
1516
+ }
1517
+ `
1518
+ );
1519
+ function fileToBase64(file) {
1520
+ return new Promise((resolve, reject) => {
1521
+ const reader = new FileReader();
1522
+ reader.onload = () => {
1523
+ const result = typeof reader.result === "string" ? reader.result : "";
1524
+ const comma = result.indexOf(",");
1525
+ resolve(comma >= 0 ? result.slice(comma + 1) : result);
1526
+ };
1527
+ reader.onerror = () => reject(new Error("Failed to read the resume file"));
1528
+ reader.readAsDataURL(file);
1529
+ });
1530
+ }
1531
+ async function submitApplication(client, data, productId) {
1532
+ try {
1533
+ const result = await client.mutate(SUBMIT_JOB_APPLICATION_MUTATION, {
1534
+ input: {
1535
+ firstName: data.firstName,
1536
+ lastName: data.lastName,
1537
+ email: data.email.trim().toLowerCase(),
1538
+ position: data.position,
1539
+ resumeBase64: data.resumeBase64,
1540
+ resumeFileName: data.resumeFileName,
1541
+ resumeContentType: data.resumeContentType,
1542
+ productId,
1543
+ ...data.phone ? { phone: data.phone } : {},
1544
+ ...data.linkedinUrl ? { linkedinUrl: data.linkedinUrl } : {},
1545
+ ...data.portfolioUrl ? { portfolioUrl: data.portfolioUrl } : {},
1546
+ ...data.coverLetter ? { coverLetter: data.coverLetter } : {},
1547
+ ...data.recaptchaToken ? { recaptchaToken: data.recaptchaToken } : {}
1548
+ }
1549
+ });
1550
+ if (result.errors?.length) {
1551
+ return {
1552
+ success: false,
1553
+ message: result.errors[0]?.message ?? "Failed to submit application"
1554
+ };
1555
+ }
1556
+ const response = result.data?.submitJobApplication;
1557
+ return response?.success ? { success: true, message: response.message || "Application submitted!" } : {
1558
+ success: false,
1559
+ message: response?.message || "Failed to submit application"
1560
+ };
1561
+ } catch (error) {
1562
+ console.error("Job application submission error:", error);
1563
+ return { success: false, message: "Network error. Please try again." };
1564
+ }
1565
+ }
1566
+ function CareersApplyForm({
1567
+ productName,
1568
+ recaptchaSiteKey,
1569
+ positions = [],
1570
+ heroTitle = "Become a part of the team",
1571
+ heroDescription = "Tell us about yourself and upload your resume \u2014 we'll be in touch.",
1572
+ headingLevel = 1,
1573
+ maxResumeBytes = DEFAULT_MAX_RESUME_BYTES,
1574
+ className,
1575
+ seo
1576
+ }) {
1577
+ const client = useWebSDK();
1578
+ const config = useWebSDKConfig();
1579
+ const { product } = useProduct();
1580
+ const resolvedProductId = product?.id || config.productId;
1581
+ const displayName = productName ?? product?.name ?? config.productId;
1582
+ const effectiveRecaptchaSiteKey = recaptchaSiteKey ?? config.recaptchaSiteKey ?? "";
1583
+ const recaptchaRef = useRef(null);
1584
+ const [isSubmitting, setIsSubmitting] = useState(false);
1585
+ const [submitted, setSubmitted] = useState(false);
1586
+ const [resumeFile, setResumeFile] = useState(null);
1587
+ const [resumeType, setResumeType] = useState(null);
1588
+ const [resumeError, setResumeError] = useState(null);
1589
+ const maxMb = Math.floor(maxResumeBytes / (1024 * 1024));
1590
+ const clearResume = () => {
1591
+ setResumeFile(null);
1592
+ setResumeType(null);
1593
+ };
1594
+ const handleFileChange = (e) => {
1595
+ setResumeError(null);
1596
+ const file = e.target.files?.[0] ?? null;
1597
+ if (!file) {
1598
+ clearResume();
1599
+ return;
1600
+ }
1601
+ const type = resolveResumeType(file);
1602
+ if (!type) {
1603
+ clearResume();
1604
+ setResumeError("Resume must be a PDF, DOC, or DOCX file.");
1605
+ return;
1606
+ }
1607
+ if (file.size > maxResumeBytes) {
1608
+ clearResume();
1609
+ setResumeError(`Resume must be ${maxMb}MB or smaller.`);
1610
+ return;
1611
+ }
1612
+ setResumeFile(file);
1613
+ setResumeType(type);
1614
+ };
1615
+ const handleSubmit = async (e) => {
1616
+ e.preventDefault();
1617
+ const recaptchaToken = recaptchaRef.current?.getValue() ?? "";
1618
+ if (effectiveRecaptchaSiteKey && !recaptchaToken) {
1619
+ toast.error("Please complete the reCAPTCHA verification");
1620
+ return;
1621
+ }
1622
+ if (!resumeFile) {
1623
+ setResumeError("Please attach your resume (PDF, DOC, or DOCX).");
1624
+ return;
1625
+ }
1626
+ if (!resolvedProductId) {
1627
+ console.warn(
1628
+ "[CareersApplyForm] No productId resolved \u2014 submission will not be attributed to a product."
1629
+ );
1630
+ }
1631
+ const form = e.currentTarget;
1632
+ setIsSubmitting(true);
1633
+ try {
1634
+ const formData = new FormData(form);
1635
+ const resumeBase64 = await fileToBase64(resumeFile);
1636
+ const result = await submitApplication(
1637
+ client,
1638
+ {
1639
+ firstName: formData.get("firstName") ?? "",
1640
+ lastName: formData.get("lastName") ?? "",
1641
+ email: formData.get("email") ?? "",
1642
+ phone: formData.get("phone") || void 0,
1643
+ position: formData.get("position") ?? "",
1644
+ linkedinUrl: formData.get("linkedinUrl") || void 0,
1645
+ portfolioUrl: formData.get("portfolioUrl") || void 0,
1646
+ coverLetter: formData.get("coverLetter") || void 0,
1647
+ resumeBase64,
1648
+ resumeFileName: resumeFile.name,
1649
+ // Use the resolved type (handles browsers that report an empty
1650
+ // file.type for .doc/.docx).
1651
+ resumeContentType: resumeType ?? resumeFile.type,
1652
+ recaptchaToken
1653
+ },
1654
+ resolvedProductId
1655
+ );
1656
+ if (result.success) {
1657
+ setSubmitted(true);
1658
+ toast.success(result.message ?? "Application submitted!");
1659
+ } else {
1660
+ toast.error(result.message ?? "Failed to submit application");
1661
+ recaptchaRef.current?.reset();
1662
+ }
1663
+ } catch (err) {
1664
+ console.error("Careers form error:", err);
1665
+ toast.error("Something went wrong. Please try again.");
1666
+ recaptchaRef.current?.reset();
1667
+ } finally {
1668
+ setIsSubmitting(false);
1669
+ }
1670
+ };
1671
+ const Heading = headingLevel === 2 ? "h2" : "h1";
1672
+ 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";
1673
+ return /* @__PURE__ */ jsxs("div", { className, children: [
1674
+ seo && /* @__PURE__ */ jsx(
1675
+ PageHead,
1676
+ {
1677
+ title: seo.title ?? `Careers \u2014 ${displayName}`,
1678
+ description: seo.description ?? heroDescription,
1679
+ keywords: seo.keywords
1680
+ }
1681
+ ),
1682
+ /* @__PURE__ */ jsx(
1683
+ "section",
1684
+ {
1685
+ 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",
1686
+ children: /* @__PURE__ */ jsxs("div", { className: "max-w-3xl mx-auto px-4 sm:px-6 lg:px-8", children: [
1687
+ /* @__PURE__ */ jsxs("div", { className: "text-center mb-8 sm:mb-10", children: [
1688
+ /* @__PURE__ */ jsx(Heading, { className: headingClass, children: heroTitle }),
1689
+ /* @__PURE__ */ jsx("p", { className: "text-lg text-muted-foreground leading-relaxed", children: heroDescription })
1690
+ ] }),
1691
+ submitted ? /* @__PURE__ */ jsxs("div", { className: "text-center rounded-xl border border-border bg-card p-10", children: [
1692
+ /* @__PURE__ */ jsx(CheckCircle, { className: "mx-auto mb-4 h-12 w-12 text-accent" }),
1693
+ /* @__PURE__ */ jsx("h3", { className: "text-xl font-semibold text-foreground mb-2", children: "Application received" }),
1694
+ /* @__PURE__ */ jsxs("p", { className: "text-muted-foreground", children: [
1695
+ "Thanks for applying to ",
1696
+ displayName,
1697
+ ". We've emailed you a confirmation and our team will be in touch."
1698
+ ] })
1699
+ ] }) : /* @__PURE__ */ jsxs("form", { onSubmit: handleSubmit, className: "space-y-5", children: [
1700
+ /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 sm:grid-cols-2 gap-5", children: [
1701
+ /* @__PURE__ */ jsxs("div", { children: [
1702
+ /* @__PURE__ */ jsx(Label, { htmlFor: "firstName", children: "First name *" }),
1703
+ /* @__PURE__ */ jsx(
1704
+ Input,
1705
+ {
1706
+ id: "firstName",
1707
+ name: "firstName",
1708
+ required: true,
1709
+ placeholder: "Ada"
1710
+ }
1711
+ )
1712
+ ] }),
1713
+ /* @__PURE__ */ jsxs("div", { children: [
1714
+ /* @__PURE__ */ jsx(Label, { htmlFor: "lastName", children: "Last name *" }),
1715
+ /* @__PURE__ */ jsx(
1716
+ Input,
1717
+ {
1718
+ id: "lastName",
1719
+ name: "lastName",
1720
+ required: true,
1721
+ placeholder: "Lovelace"
1722
+ }
1723
+ )
1724
+ ] })
1725
+ ] }),
1726
+ /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 sm:grid-cols-2 gap-5", children: [
1727
+ /* @__PURE__ */ jsxs("div", { children: [
1728
+ /* @__PURE__ */ jsx(Label, { htmlFor: "email", children: "Email *" }),
1729
+ /* @__PURE__ */ jsx(
1730
+ Input,
1731
+ {
1732
+ id: "email",
1733
+ name: "email",
1734
+ type: "email",
1735
+ required: true,
1736
+ placeholder: "ada@example.com"
1737
+ }
1738
+ )
1739
+ ] }),
1740
+ /* @__PURE__ */ jsxs("div", { children: [
1741
+ /* @__PURE__ */ jsx(Label, { htmlFor: "phone", children: "Phone" }),
1742
+ /* @__PURE__ */ jsx(
1743
+ Input,
1744
+ {
1745
+ id: "phone",
1746
+ name: "phone",
1747
+ placeholder: "+1 555 010 0100"
1748
+ }
1749
+ )
1750
+ ] })
1751
+ ] }),
1752
+ /* @__PURE__ */ jsxs("div", { children: [
1753
+ /* @__PURE__ */ jsx(Label, { htmlFor: "position", children: "Position *" }),
1754
+ positions.length > 0 ? /* @__PURE__ */ jsxs(
1755
+ "select",
1756
+ {
1757
+ id: "position",
1758
+ name: "position",
1759
+ required: true,
1760
+ defaultValue: "",
1761
+ 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",
1762
+ children: [
1763
+ /* @__PURE__ */ jsx("option", { value: "", disabled: true, children: "Select a role" }),
1764
+ positions.map((p) => /* @__PURE__ */ jsx("option", { value: p, children: p }, p))
1765
+ ]
1766
+ }
1767
+ ) : /* @__PURE__ */ jsx(
1768
+ Input,
1769
+ {
1770
+ id: "position",
1771
+ name: "position",
1772
+ required: true,
1773
+ placeholder: "Role you're applying for"
1774
+ }
1775
+ )
1776
+ ] }),
1777
+ /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 sm:grid-cols-2 gap-5", children: [
1778
+ /* @__PURE__ */ jsxs("div", { children: [
1779
+ /* @__PURE__ */ jsx(Label, { htmlFor: "linkedinUrl", children: "LinkedIn" }),
1780
+ /* @__PURE__ */ jsx(
1781
+ Input,
1782
+ {
1783
+ id: "linkedinUrl",
1784
+ name: "linkedinUrl",
1785
+ type: "url",
1786
+ placeholder: "https://linkedin.com/in/\u2026"
1787
+ }
1788
+ )
1789
+ ] }),
1790
+ /* @__PURE__ */ jsxs("div", { children: [
1791
+ /* @__PURE__ */ jsx(Label, { htmlFor: "portfolioUrl", children: "Portfolio / GitHub" }),
1792
+ /* @__PURE__ */ jsx(
1793
+ Input,
1794
+ {
1795
+ id: "portfolioUrl",
1796
+ name: "portfolioUrl",
1797
+ type: "url",
1798
+ placeholder: "https://\u2026"
1799
+ }
1800
+ )
1801
+ ] })
1802
+ ] }),
1803
+ /* @__PURE__ */ jsxs("div", { children: [
1804
+ /* @__PURE__ */ jsx(Label, { htmlFor: "coverLetter", children: "Cover letter" }),
1805
+ /* @__PURE__ */ jsx(
1806
+ Textarea,
1807
+ {
1808
+ id: "coverLetter",
1809
+ name: "coverLetter",
1810
+ rows: 5,
1811
+ placeholder: "Tell us why you'd be a great fit\u2026"
1812
+ }
1813
+ )
1814
+ ] }),
1815
+ /* @__PURE__ */ jsxs("div", { children: [
1816
+ /* @__PURE__ */ jsxs(Label, { htmlFor: "resume", children: [
1817
+ "Resume * (PDF, DOC, or DOCX \u2014 max ",
1818
+ maxMb,
1819
+ "MB)"
1820
+ ] }),
1821
+ /* @__PURE__ */ jsxs(
1822
+ "label",
1823
+ {
1824
+ htmlFor: "resume",
1825
+ className: "mt-1 flex cursor-pointer items-center gap-3 rounded-md border border-dashed border-input bg-background px-4 py-3 text-sm hover:bg-muted/40 transition-colors",
1826
+ children: [
1827
+ /* @__PURE__ */ jsx(
1828
+ Upload,
1829
+ {
1830
+ className: "h-5 w-5 text-muted-foreground",
1831
+ "aria-hidden": "true"
1832
+ }
1833
+ ),
1834
+ /* @__PURE__ */ jsx("span", { className: "text-foreground", children: resumeFile ? resumeFile.name : "Choose a file\u2026" }),
1835
+ /* @__PURE__ */ jsx(
1836
+ "input",
1837
+ {
1838
+ id: "resume",
1839
+ name: "resume",
1840
+ type: "file",
1841
+ accept: ".pdf,.doc,.docx,application/pdf,application/msword,application/vnd.openxmlformats-officedocument.wordprocessingml.document",
1842
+ className: "sr-only",
1843
+ "aria-describedby": resumeError ? "resume-error" : void 0,
1844
+ "aria-invalid": resumeError ? true : void 0,
1845
+ onChange: handleFileChange
1846
+ }
1847
+ )
1848
+ ]
1849
+ }
1850
+ ),
1851
+ resumeError && /* @__PURE__ */ jsx(
1852
+ "p",
1853
+ {
1854
+ id: "resume-error",
1855
+ role: "alert",
1856
+ className: "mt-1 text-sm text-destructive",
1857
+ children: resumeError
1858
+ }
1859
+ )
1860
+ ] }),
1861
+ effectiveRecaptchaSiteKey && /* @__PURE__ */ jsx(
1862
+ ReCAPTCHA3,
1863
+ {
1864
+ ref: recaptchaRef,
1865
+ sitekey: effectiveRecaptchaSiteKey
1866
+ }
1867
+ ),
1868
+ /* @__PURE__ */ jsx(
1869
+ Button,
1870
+ {
1871
+ type: "submit",
1872
+ size: "lg",
1873
+ disabled: isSubmitting,
1874
+ className: "w-full sm:w-auto",
1875
+ children: isSubmitting ? "Submitting\u2026" : /* @__PURE__ */ jsxs(Fragment, { children: [
1876
+ /* @__PURE__ */ jsx(Send, { className: "mr-2 h-4 w-4" }),
1877
+ " Submit application"
1878
+ ] })
1879
+ }
1880
+ )
1881
+ ] })
1882
+ ] })
1883
+ }
1884
+ )
1885
+ ] });
1886
+ }
1492
1887
  var newsletterSchema = z.object({
1493
1888
  email: z.string().email("Please enter a valid email address")
1494
1889
  });
@@ -1778,7 +2173,7 @@ function NewsletterPage({
1778
2173
  errors.email && /* @__PURE__ */ jsx("p", { className: "text-sm text-destructive", children: errors.email.message })
1779
2174
  ] }),
1780
2175
  effectiveRecaptchaSiteKey && /* @__PURE__ */ jsx("div", { className: "flex justify-center", children: /* @__PURE__ */ jsx(
1781
- ReCAPTCHA2,
2176
+ ReCAPTCHA3,
1782
2177
  {
1783
2178
  ref: recaptchaRef,
1784
2179
  sitekey: effectiveRecaptchaSiteKey
@@ -1830,7 +2225,7 @@ function NewsletterPage({
1830
2225
  errors.email && /* @__PURE__ */ jsx("p", { className: "text-sm text-destructive", children: errors.email.message })
1831
2226
  ] }),
1832
2227
  effectiveRecaptchaSiteKey && /* @__PURE__ */ jsx("div", { className: "flex justify-center sm:justify-start", children: /* @__PURE__ */ jsx(
1833
- ReCAPTCHA2,
2228
+ ReCAPTCHA3,
1834
2229
  {
1835
2230
  ref: recaptchaRef,
1836
2231
  sitekey: effectiveRecaptchaSiteKey
@@ -1999,7 +2394,7 @@ function NewsletterForm({
1999
2394
  }
2000
2395
  ` }),
2001
2396
  /* @__PURE__ */ jsx("div", { style: { position: "relative", zIndex: 50 }, children: /* @__PURE__ */ jsx(
2002
- ReCAPTCHA2,
2397
+ ReCAPTCHA3,
2003
2398
  {
2004
2399
  ref: recaptchaRef,
2005
2400
  sitekey: effectiveRecaptchaSiteKey,
@@ -4496,6 +4891,6 @@ function PressKitPage(props) {
4496
4891
  ] });
4497
4892
  }
4498
4893
 
4499
- export { BlogListPage, BlogPostPage, Button, ContactPage, ContentRenderer, DEFAULT_CONTACT_CATEGORIES, Input, Label, NewsletterForm, NewsletterPage, PageHead, PartnersPage, PressKitPage, PricingPage, PricingSection, ProductProvider, ResourceLinks, RybbitAnalytics, Select, SocialLinks, Textarea, UnsubscribePage, WaitlistForm, WaitlistPage, WebSDKClient, WebSDKProvider, buttonVariants, cn, useContentPage, useContentPages, useProduct, useProductConfig, useWebSDK, useWebSDKConfig };
4894
+ export { BlogListPage, BlogPostPage, Button, CareersApplyForm, ContactPage, ContentRenderer, DEFAULT_CONTACT_CATEGORIES, Input, Label, NewsletterForm, NewsletterPage, PageHead, PartnersPage, PressKitPage, PricingPage, PricingSection, ProductProvider, ResourceLinks, RybbitAnalytics, Select, SocialLinks, Textarea, UnsubscribePage, WaitlistForm, WaitlistPage, WebSDKClient, WebSDKProvider, buttonVariants, cn, useContentPage, useContentPages, useProduct, useProductConfig, useWebSDK, useWebSDKConfig };
4500
4895
  //# sourceMappingURL=index.mjs.map
4501
4896
  //# sourceMappingURL=index.mjs.map