Skip to content

v0.7.19: newsletter, contact, careers#5318

Merged
waleedlatif1 merged 3 commits into
mainfrom
staging
Jul 1, 2026
Merged

v0.7.19: newsletter, contact, careers#5318
waleedlatif1 merged 3 commits into
mainfrom
staging

Conversation

@waleedlatif1

Copy link
Copy Markdown
Collaborator
…5317)

- Drop the #F8F8F8 canvas to a clean white background (body + outer wrapper).
- Replace Discord with LinkedIn in the footer social row.
- Swap the low-res header logo for the full-res sim logotype.
* feat(landing): reintroduce /contact page styled like /demo

- Restore the /contact page (removed in #5181) with a two-column layout
  mirroring /demo: value prop + trusted-by logos on the left, a message
  form card on the right, on the platform light tokens and chip components
- Restore the contact contract, /api/contact route (rate-limit, honeypot,
  Turnstile, help-inbox notification + visitor confirmation), now fully
  contract-bound via parseRequest
- Add a useSubmitContact React Query mutation hook
- Link Contact from the footer Resources column and add it to the sitemap

* fix(contact): server-authoritative captcha + review fixes

- Make captcha server-authoritative: drop the client-trusted captchaUnavailable
  flag; a valid Turnstile token is the only way past the stricter fallback
  bucket, so callers can't opt out of the challenge
- Re-execute the Turnstile widget on every submit (incl. after expiry) instead
  of falling into the no-captcha path once the token expires
- Reset the pre-submit gate on mutation settle so rapid double-clicks can't fire
  a duplicate /api/contact request
- Map only feature_request to its email type; every other topic resolves to a
  General Inquiry confirmation so support requests aren't labeled bug reports
- Drop the confirmation-email promise from the success copy (it's best-effort)
- Collapse the duplicated no-captcha rate-limit branch; hoist shared response
  constants; read the Turnstile site key as a module constant

* fix(contact): drop redundant Turnstile hostname pin

The Turnstile site key is already domain-bound in Cloudflare, so pinning
expectedHostname to the marketing SITE_URL (www.sim.ai) only rejected valid
tokens issued on self-hosted, preview, and apex-vs-www hosts. Remove the pin
and rely on Cloudflare's own domain binding.

* fix(contact): fail closed on the no-captcha rate-limit backstop

checkRateLimitDirect fails open on limiter-storage errors so a limiter outage
never takes down normal traffic. But the contact route's no-captcha bucket is
the only throttle on token-less submits, so a fail-open there let uncaptcha'd
requests reach the email path unthrottled during an outage.

- Add an opt-in { failClosed } option to checkRateLimitDirect; default behavior
  (fail open) is unchanged
- Use failClosed on the contact no-captcha backstop so an unenforceable limit
  rejects instead of admitting
- Cover both fail-open and fail-closed paths with tests

* refactor(contact): TSDoc over inline comments

Move the captcha-design rationale into the route handler's TSDoc and drop the
inline body/JSX comments, per the project's TSDoc-only comment convention.
* feat(careers): careers page backed by the Ashby job board

* fix(careers): harden Ashby parsing and filter edge cases from review

- validate jobUrl as http(s) only; drop postings with unsafe URLs
- validate postings individually so one bad row can't empty the board
- namespace the all-filter sentinel to avoid colliding with real values
- dedupe the job metadata line (fixes duplicate React keys / Remote·Remote)
- parse filters server-side so deep-linked views don't flash unfiltered

* fix(careers): filter-aware empty state; drop inline comments

- JobGroups owns its empty copy via a filtersActive flag, so the server
  fallback and client board render identical, correct empty messaging
  (no-open-roles vs no-matching-filters)
- convert remaining inline comments to TSDoc
@vercel

vercel Bot commented Jul 1, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
docs Skipped Skipped Jul 1, 2026 8:26am

Request Review

@cursor

cursor Bot commented Jul 1, 2026

Copy link
Copy Markdown

PR Summary

Medium Risk
New public /api/contact sends email and handles captcha/rate limits; mistakes could affect spam handling or deliverability, though protections are layered.

Overview
Adds on-site /careers and /contact landing pages and wires them through nav, footer, and the sitemap (Careers moves from external Ashby to /careers; Contact is new).

Careers loads open roles from Ashby’s public API (getAshbyJobs), server-renders the full list for SEO, and hydrates a Team/Location board with shareable ?team= / ?location= filters (nuqs). The Suspense fallback applies the same server-side filters so deep links don’t flash unfiltered listings.

Contact mirrors the demo layout: chip form, client validation via shared Zod contract, invisible Turnstile, honeypot, and POST /api/contact that rate-limits per IP, verifies captcha (stricter bucket when captcha can’t be verified, with failClosed on the limiter backstop), emails help@, and sends a visitor confirmation.

The July broadcast email template switches to a white canvas and replaces the footer Discord link with LinkedIn.

Reviewed by Cursor Bugbot for commit 2393b72. Configure here.

@waleedlatif1 waleedlatif1 merged commit 6e426f8 into main Jul 1, 2026
24 checks passed
@greptile-apps

greptile-apps Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds new public landing surfaces and updates the newsletter. The main changes are:

  • New /careers page backed by Ashby job postings.
  • New /contact page with a public contact API route.
  • Footer and sitemap links for careers and contact.
  • Newsletter background, logo, and social-link updates.
  • Rate-limiter support for fail-closed direct checks.

Confidence Score: 5/5

This looks safe to merge after checking the optional Ashby row-handling cleanup.

  • No blocking issues found in the changed code.
  • The contact API uses the existing client error path for non-success responses.
  • The careers page can skip Ashby rows with missing hosted URLs, but the page still renders and logs skipped rows.

apps/sim/lib/ashby/jobs.ts

Important Files Changed

Filename Overview
apps/sim/app/api/contact/route.ts Adds the public contact submission endpoint with validation, Turnstile fallback handling, rate limiting, and email dispatch.
apps/sim/lib/api/contracts/contact.ts Defines the shared contact request and response contract with field bounds and email-header character checks.
apps/sim/app/(landing)/contact/components/contact-form/contact-form.tsx Adds the client contact form with shared validation, Turnstile execution, honeypot payload, and mutation-driven success/error UI.
apps/sim/lib/ashby/jobs.ts Adds the Ashby job-board fetcher, validation, normalization, URL scheme restriction, and posting sort order.
apps/sim/app/(landing)/careers/components/job-board/job-board.tsx Adds the URL-backed careers filter UI and renders grouped job postings.
apps/sim/app/(landing)/careers/search-params.ts Adds shared nuqs parsers and server cache for careers team and location filters.
apps/sim/lib/core/rate-limiter/rate-limiter.ts Adds an optional fail-closed mode for direct token-bucket checks while keeping the default fail-open behavior.

Comments Outside Diff (1)

  1. apps/sim/lib/ashby/jobs.ts, line 1326 (link)

    P2 Missing Job URLs Drop Roles

    When Ashby returns a listed posting without jobUrl, this required schema field makes that row fail validation and the board skips it. Ashby omits fields that are missing, so a valid listed role can disappear from /careers, and if every listed role is missing this field the page shows “No open roles right now.”

Reviews (1): Last reviewed commit: "feat(careers): careers page backed by th..." | Re-trigger Greptile

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

1 participant