9de1b6330c476e09da2593f6a51170d7d93e6e91
5 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
7afc991bd6 |
Partner application form: required fields, skills rework, fail-fast validation (#21710)
## Summary (twenty-website — partner application form) - **Required fields**: website URL, city, hourly rate, minimum project are now required (client step-gate + server zod) with `*` markers. The final step validates before POSTing, so empty required fields fail fast client-side instead of round-tripping. - **Technical skills reworked to *complement* "What you cover"** (the service categories) rather than duplicate them — now a small shown set + a larger searchable-only pool of tools / technologies / industries. Field hint clarifies the intent. - **No competitor CRMs** in suggestions (Salesforce/HubSpot/Attio removed); a guard test fails if one ever reappears. Migrations surface as a generic "CRM migration". - `Form.TagInput` gains an optional `searchPool` prop (autocomplete-only entries, not rendered as chips). Companion to the app-side PR #21709. <!-- This is an auto-generated description by cubic. --> <a href="https://cubic.dev/pr/twentyhq/twenty/pull/21710?utm_source=github" target="_blank" rel="noopener noreferrer" data-no-image-dialog="true"><picture><source media="(prefers-color-scheme: dark)" srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source media="(prefers-color-scheme: light)" srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img alt="Review in cubic" src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a> <!-- End of auto-generated description by cubic. --> |
||
|
|
bda6fcfec9 |
feat(website): book an intro call after partner application (#21343)
## What
After a partner submits the application wizard, the success screen now
offers an inline Cal.com booking widget so they can book an intro call
on the spot — keeping the partner process high-touch.
- Inline Cal.com embed on the application success step, prefilled with
the applicant's name/email (company in the notes)
- Wide `month_view` layout; the success modal widens to ~960px (the
4-step form and mobile are unchanged)
- Event-type-details panel hidden via `cal('ui', { hideEventTypeDetails:
true })` so it's just calendar + times, on its own `partner-intro` Cal
namespace (isolated from the ContactCal embed)
- "I'll book later" escape hatch; backend / submission path untouched
## Why
The warmest moment is right after someone opts in. Today the success
screen only shows a Close button — this turns that moment into a
scheduled conversation.
## How
- `PartnerIntroCalEmbed` — thin wrapper over `@calcom/embed-react`
(already a dependency), reusing the existing `ContactCal` embed pattern
- `buildPartnerIntroPrefill` — pure mapping of applicant fields → Cal
prefill
- `PartnerApplicationSuccess` — presentational success view (heading +
subtitle + embed + dismiss)
- The wizard reports submitted-state up (`onSubmittedChange`) so the
modal widens only on the booking step
- New Lingui copy + regenerated catalogs (en/es/fr)
## Test plan
- `npx jest PartnerApplication` — green (prefill mapping, embed
link/layout/prefill, success view)
- `npx nx typecheck twenty-website` — clean
- `npx oxlint -c .oxlintrc.json` / `npx oxfmt --check` — clean
- Manual: submit the wizard → success step shows the wide booking
calendar, prefilled, dark theme, no event-details panel; "I'll book
later" closes the modal
## Notes
- Frontend only — no backend, schema, or submission-path change
- Cal link `rashad-twenty/partner-intro` lives as a constant in
`config.ts`
- Branch is currently behind `main`; happy to rebase before review
|
||
|
|
5a55021e26 |
feat(website): add /partners/apply standalone page (#21219)
## Summary - Adds a standalone `/partners/apply` page — a shareable URL that opens the partner application wizard full-page (no modal, no nav, no footer), on a plain black background - Adds a `slots` prop to `PartnerApplicationWizard` so it can render outside a `Dialog.Root` context (Base UI), keeping the existing modal on `/partners` completely untouched - Surfaces logic function errors to the user: the API route now checks the webhook response body for `ok: true`, so a silent backend failure no longer shows a false success state ## Test plan - [ ] `yarn jest --no-coverage` — 365/365 passing - [ ] `npx oxlint -c .oxlintrc.json .` — 0 errors (1 pre-existing warning unrelated to this PR) - [ ] `npx oxfmt --check .` — clean - [ ] `npx tsc --noEmit` — clean - [ ] Visit `/partners/apply` — wizard loads full-page on black background, no menu, no footer - [ ] Complete the wizard and submit — redirects to `/partners/list` - [ ] Visit `/partners` — "Become a partner" modal still opens normally |
||
|
|
d0e0e27035 |
[Website] Partner application wizard + logic-function handover (#21039)
## Summary Replaces the single-screen partner-application modal with a **4-step wizard** on the public form, and points the route's upstream at the new `submit-partner-application` HTTP logic function in the twenty-partners SDK app. After design review, the Expertise step landed on the validated **Category + Skills** model: a small set of *stable* macro categories the partner operates in, plus a *free, semi-structured* Skills field for the concrete things that differentiate them (React, SAP, Shopify, …). Companion PR (partners-app side): #21040 ## ⚠️ Deployment notes Before this can ship to prod, the website worker needs a new env var: - **Add `PARTNER_APPLICATION_SECRET`** to the deploy config at https://github.com/twentyhq/twenty-infra/tree/main/cloudflare/website. Without it the route returns `503` ("Partner application endpoint is not configured."). - The value must **match** the `PARTNER_APPLICATION_SECRET` workspace variable set in the partners workspace UI (Settings → Apps → Twenty Partners → Variables) — that's how the handler authenticates the incoming `X-Application-Secret` header. - `PARTNER_APPLICATION_WEBHOOK_URL` also needs repointing from the TFT webhook to the logic-function URL (`https://partner.twenty.com/s/partner-applications` or equivalent) at the same time. ## Wizard - 4 steps inside `Modal.Root`: **Identity → Profile → Expertise → Commercials**. Step-dot indicator, per-step required-field gating, reset on close. The big serif hero shows **only on step 1**; later steps use the compact `STEP n OF 4 · NAME` strip to reclaim vertical space. - **Profile** captures Type of team (Solo/Agency), LinkedIn, City, Country, Languages. Country uses the searchable Select (placeholder-only label). - **Expertise = Category + Skills + Notes:** - **Category** — multi-select cards over 5 macro categories (`ADVISORY`, `SOLUTIONING`, `DEVELOPMENT`, `HOSTING`, `SUPPORT`), each with a one-line description + examples. (Replaces the old draft `partnerScope` enum; the backend keeps the field name — see #21040.) - **Skills** — free tag input with a clickable suggestion row + keyboard autocomplete (↑/↓/Enter/Esc) and "add your own". Empty by default. - **Notes** — one free textarea (merges the former `workspaceUrl` + `customerReferences`), reviewed manually. - `deploymentExpertise` removed from the form (covered by the Hosting category). - **In-modal success view** on submit ("Thanks, / we'll be in touch!") with a Close button — replaces the old silent close. - Removes the partners-page "Which partner program is right for you?" three-cards section. ## Design-system primitives - **`Form.Select`** — searchable popup whose dropdown is **portaled to `<body>`** (fixed, anchored to the trigger, flips up, height-capped) so the modal's `overflow`/`transform` can't clip it; pointer events are stopped so clicking inside it doesn't dismiss the dialog. - **`Form.TagInput`** — optional `suggestions` prop adds the suggestion row + autocomplete menu (used by Skills); behaviour unchanged when no suggestions are passed. - **`CategoryCardSelect`** — compact multi-select cards. - `Form.MultiSelect`, `Form.Currency`. ## Validation & payload - **Single validation source:** client and server share Zod field schemas (`partner-application-field-schemas.ts`). The reducer validates via those instead of hand-rolled regexes, so client and server agree by construction (e.g. both reject non-TLD URLs). - **Typed request body:** `buildPartnerApplicationRequestBody(state)` returns a typed `PartnerApplicationRequest` (unit-tested); `handleSubmit` just serializes it. - Payload is camelCase matching the logic-function input; `applicationNotes` replaces `workspaceUrl`/`customerReferences`. - Auth: the upstream call carries an `X-Application-Secret` header backed by `PARTNER_APPLICATION_SECRET` (handler-enforced — the SDK's `isAuthRequired` only accepts user-session JWTs, not workspace API keys). The webhook-URL env uses `z.url()` (not `z.httpUrl()`) so `http://localhost:2020/...` dev destinations parse. ## Demo 📹 _Screen recording of the wizard end-to-end (open → walk steps → submit → Partner record lands):_ https://github.com/user-attachments/assets/7458dd86-e3ff-47b5-9878-0eb134ff38e3 ## Tests - **62 passing** across reducer, Zod schema, route, the new payload-builder suite, and Form helper suites. `npx tsc` clean, `nx lint:diff-with-main` clean, Lingui catalogs regenerated (French slots are a follow-up). ## Test plan - [ ] `/partners` → "Become a partner" → wizard opens on Step 1 (full hero) - [ ] Identity: name / work email / company → Next - [ ] Profile: pick **Type of team**; search country ("fra" → France); pick languages → Next (compact header from here on) - [ ] Expertise: select 1+ **Category** cards; add **Skills** (click a suggestion, type one + Enter, drive the ↑/↓ autocomplete); optionally fill **Notes** - [ ] Country dropdown opens without being clipped by the modal, and clicking inside it does **not** close the wizard - [ ] Commercials → Submit → **in-modal "Thanks, we'll be in touch!"**; Network shows POST `/api/partner-application` `200` - [ ] Partner record lands with the chosen categories in `partnerScope`, plus `skills`, `applicationNotes`, `slug` from company, `reviewed: false`, `partnerTier: 'NEW'` - [ ] Re-submit same email + different city → Partner updates; `validationStage`/`reviewed`/`partnerTier` preserved - [ ] Back/Next preserves entered values; Reset on close; mobile single-column / chips wrap --------- Co-authored-by: Etienne <45695613+etiennejouan@users.noreply.github.com> |
||
|
|
658bdf3e57 |
chore(website): rename twenty-website-new → twenty-website (#20745)
## Summary Follow-up to the Cloudflare/OpenNext migration (#20741). Now that the legacy `twenty-website` package was already removed in #20270, the `-new` suffix on the marketing site package is no longer meaningful. ## What changes - **Directory rename**: `git mv packages/twenty-website-new packages/twenty-website` (1213 files moved, no content change) - **Package + nx config**: `package.json` and `project.json` name fields updated, `sourceRoot` repointed - **Source refs**: `load-local-articles.ts` and `load-local-release-notes.ts` had a hardcoded `'twenty-website-new'` segment in their monorepo-root fallback path; `app/[locale]/releases/page.tsx` had display strings showing where to add content - **External refs**: root `package.json` workspaces, root `CLAUDE.md` / `README.md`, `twenty-sdk` + `create-twenty-app` READMEs, `.vscode/twenty.code-workspace`, `.cursor/rules/changelog-process.mdc`, Crowdin config + the three `website-i18n-*` CI workflows + `ci-website.yaml` - **Docker cleanup**: `packages/twenty-docker/twenty-website-new/Dockerfile` deleted; the two Makefile targets (`prod-website-new-build` / `prod-website-new-run`) that referenced it removed — EKS deploy was retired in the Cloudflare migration - **`yarn.lock`** regenerated against the new workspace path ## What's deliberately not in this PR The dev hostname `website-new.twenty-main.com` in `wrangler.jsonc` stays for now. Migrating it to `website.twenty-main.com` needs coordinated DNS deletion (current CNAME points at the legacy Docusaurus NLB and serves 503s) and removal of the matching legacy `website` Helm chart in `twenty-infra`. Flagged as a separate cleanup. Companion infra PR: https://github.com/twentyhq/twenty-infra/pull/682 (workflow paths + Terraform ECR + docs) ## Test plan - [x] `yarn install --immutable` resolves clean against the new path - [x] `npx nx typecheck twenty-website` passes - [x] `npx nx lint twenty-website` passes - [ ] CI on this PR confirms the same on a fresh checkout - [ ] After merge: trigger `Deploy Website` workflow against `environment=dev` to confirm the renamed working-directory deploys correctly |