Files
twenty/packages/twenty-website/src/sections/PartnerApplication/wizard/partner-fields.data.ts
T
Rashad Karanouh 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>
2026-06-02 10:23:31 +00:00

546 lines
18 KiB
TypeScript

import type { MessageDescriptor } from '@lingui/core';
import { msg } from '@lingui/core/macro';
import type { PartnerApplicationState } from './use-partner-application-state';
export const PARTNER_APPLICATION_STEP_IDS = [
'identity',
'profile',
'expertise',
'commercials',
] as const;
export type PartnerApplicationStepId =
(typeof PARTNER_APPLICATION_STEP_IDS)[number];
export const PARTNER_TYPE_OF_TEAM_VALUES = ['SOLO', 'AGENCY'] as const;
export type PartnerTypeOfTeam = (typeof PARTNER_TYPE_OF_TEAM_VALUES)[number];
export const PARTNER_TYPE_OF_TEAM_OPTIONS: ReadonlyArray<{
value: PartnerTypeOfTeam;
label: MessageDescriptor;
}> = [
{ value: 'SOLO', label: msg`Solo` },
{ value: 'AGENCY', label: msg`Agency` },
];
export const PARTNER_SCOPE_VALUES = [
'ADVISORY',
'SOLUTIONING',
'DEVELOPMENT',
'HOSTING',
'SUPPORT',
] as const;
export type PartnerScopeValue = (typeof PARTNER_SCOPE_VALUES)[number];
export const PARTNER_SCOPE_OPTIONS: ReadonlyArray<{
value: PartnerScopeValue;
label: MessageDescriptor;
description: MessageDescriptor;
examples: MessageDescriptor;
}> = [
{
value: 'ADVISORY',
label: msg`Advisory & Discovery`,
description: msg`Upfront consulting, scoping, strategy.`,
examples: msg`CRM audit · Requirements · Process mapping · ROI · RevOps · Vendor selection`,
},
{
value: 'SOLUTIONING',
label: msg`Solutioning`,
description: msg`What an admin can do without writing code.`,
examples: msg`Data modeling · Migrations · No-code workflows · Dashboards · SSO/SCIM · Integrations`,
},
{
value: 'DEVELOPMENT',
label: msg`Custom Development`,
description: msg`Anything that needs a developer.`,
examples: msg`Custom Apps · Scripts · AI/agent integrations`,
},
{
value: 'HOSTING',
label: msg`Hosting & Infrastructure`,
description: msg`Anything that needs devops skills.`,
examples: msg`Self-hosted (Docker/K8s) · Cloud architecture · Scaling · Security · Monitoring`,
},
{
value: 'SUPPORT',
label: msg`Training, Adoption & Support`,
description: msg`User-side rollout & ongoing support.`,
examples: msg`Onboarding · Documentation · Change management · L1/L2 support · Managed services`,
},
];
export const PARTNER_SKILL_SUGGESTIONS: ReadonlyArray<string> = [
'React',
'TypeScript',
'Node.js',
'Python',
'PostgreSQL',
'GraphQL',
'n8n',
'Zapier',
'Make',
'Salesforce',
'HubSpot',
'SAP',
'Shopify',
'Stripe',
'Docker',
'Kubernetes',
'AWS',
'GCP',
];
export const PARTNER_COUNTRY_VALUES = [
'AFGHANISTAN',
'ALBANIA',
'ALGERIA',
'ANDORRA',
'ANGOLA',
'ANTIGUA_AND_BARBUDA',
'ARGENTINA',
'ARMENIA',
'AUSTRALIA',
'AUSTRIA',
'AZERBAIJAN',
'BAHAMAS',
'BAHRAIN',
'BANGLADESH',
'BARBADOS',
'BELARUS',
'BELGIUM',
'BELIZE',
'BENIN',
'BHUTAN',
'BOLIVIA',
'BOSNIA_AND_HERZEGOVINA',
'BOTSWANA',
'BRAZIL',
'BRUNEI',
'BULGARIA',
'BURKINA_FASO',
'BURUNDI',
'CAMBODIA',
'CAMEROON',
'CANADA',
'CAPE_VERDE',
'CENTRAL_AFRICAN_REPUBLIC',
'CHAD',
'CHILE',
'CHINA',
'COLOMBIA',
'COMOROS',
'CONGO',
'DR_CONGO',
'COSTA_RICA',
'CROATIA',
'CUBA',
'CYPRUS',
'CZECH_REPUBLIC',
'DENMARK',
'DJIBOUTI',
'DOMINICA',
'DOMINICAN_REPUBLIC',
'ECUADOR',
'EGYPT',
'EL_SALVADOR',
'EQUATORIAL_GUINEA',
'ERITREA',
'ESTONIA',
'ESWATINI',
'ETHIOPIA',
'FIJI',
'FINLAND',
'FRANCE',
'GABON',
'GAMBIA',
'GEORGIA',
'GERMANY',
'GHANA',
'GREECE',
'GRENADA',
'GUATEMALA',
'GUINEA',
'GUINEA_BISSAU',
'GUYANA',
'HAITI',
'HONDURAS',
'HUNGARY',
'ICELAND',
'INDIA',
'INDONESIA',
'IRAN',
'IRAQ',
'IRELAND',
'ISRAEL',
'ITALY',
'IVORY_COAST',
'JAMAICA',
'JAPAN',
'JORDAN',
'KAZAKHSTAN',
'KENYA',
'KIRIBATI',
'KOSOVO',
'KUWAIT',
'KYRGYZSTAN',
'LAOS',
'LATVIA',
'LEBANON',
'LESOTHO',
'LIBERIA',
'LIBYA',
'LIECHTENSTEIN',
'LITHUANIA',
'LUXEMBOURG',
'MADAGASCAR',
'MALAWI',
'MALAYSIA',
'MALDIVES',
'MALI',
'MALTA',
'MARSHALL_ISLANDS',
'MAURITANIA',
'MAURITIUS',
'MEXICO',
'MICRONESIA',
'MOLDOVA',
'MONACO',
'MONGOLIA',
'MONTENEGRO',
'MOROCCO',
'MOZAMBIQUE',
'MYANMAR',
'NAMIBIA',
'NAURU',
'NEPAL',
'NETHERLANDS',
'NEW_ZEALAND',
'NICARAGUA',
'NIGER',
'NIGERIA',
'NORTH_KOREA',
'NORTH_MACEDONIA',
'NORWAY',
'OMAN',
'PAKISTAN',
'PALAU',
'PALESTINE',
'PANAMA',
'PAPUA_NEW_GUINEA',
'PARAGUAY',
'PERU',
'PHILIPPINES',
'POLAND',
'PORTUGAL',
'QATAR',
'ROMANIA',
'RUSSIA',
'RWANDA',
'SAINT_KITTS_AND_NEVIS',
'SAINT_LUCIA',
'SAINT_VINCENT',
'SAMOA',
'SAN_MARINO',
'SAO_TOME_AND_PRINCIPE',
'SAUDI_ARABIA',
'SENEGAL',
'SERBIA',
'SEYCHELLES',
'SIERRA_LEONE',
'SINGAPORE',
'SLOVAKIA',
'SLOVENIA',
'SOLOMON_ISLANDS',
'SOMALIA',
'SOUTH_AFRICA',
'SOUTH_KOREA',
'SOUTH_SUDAN',
'SPAIN',
'SRI_LANKA',
'SUDAN',
'SURINAME',
'SWEDEN',
'SWITZERLAND',
'SYRIA',
'TAIWAN',
'TAJIKISTAN',
'TANZANIA',
'THAILAND',
'TIMOR_LESTE',
'TOGO',
'TONGA',
'TRINIDAD_AND_TOBAGO',
'TUNISIA',
'TURKEY',
'TURKMENISTAN',
'TUVALU',
'UGANDA',
'UKRAINE',
'UNITED_ARAB_EMIRATES',
'UNITED_KINGDOM',
'UNITED_STATES',
'URUGUAY',
'UZBEKISTAN',
'VANUATU',
'VATICAN',
'VENEZUELA',
'VIETNAM',
'YEMEN',
'ZAMBIA',
'ZIMBABWE',
] as const;
export type PartnerCountryValue = (typeof PARTNER_COUNTRY_VALUES)[number];
export const PARTNER_COUNTRY_OPTIONS: ReadonlyArray<{
value: PartnerCountryValue;
label: MessageDescriptor;
}> = [
{ value: 'AFGHANISTAN', label: msg`Afghanistan 🇦🇫` },
{ value: 'ALBANIA', label: msg`Albania 🇦🇱` },
{ value: 'ALGERIA', label: msg`Algeria 🇩🇿` },
{ value: 'ANDORRA', label: msg`Andorra 🇦🇩` },
{ value: 'ANGOLA', label: msg`Angola 🇦🇴` },
{ value: 'ANTIGUA_AND_BARBUDA', label: msg`Antigua & Barbuda 🇦🇬` },
{ value: 'ARGENTINA', label: msg`Argentina 🇦🇷` },
{ value: 'ARMENIA', label: msg`Armenia 🇦🇲` },
{ value: 'AUSTRALIA', label: msg`Australia 🇦🇺` },
{ value: 'AUSTRIA', label: msg`Austria 🇦🇹` },
{ value: 'AZERBAIJAN', label: msg`Azerbaijan 🇦🇿` },
{ value: 'BAHAMAS', label: msg`Bahamas 🇧🇸` },
{ value: 'BAHRAIN', label: msg`Bahrain 🇧🇭` },
{ value: 'BANGLADESH', label: msg`Bangladesh 🇧🇩` },
{ value: 'BARBADOS', label: msg`Barbados 🇧🇧` },
{ value: 'BELARUS', label: msg`Belarus 🇧🇾` },
{ value: 'BELGIUM', label: msg`Belgium 🇧🇪` },
{ value: 'BELIZE', label: msg`Belize 🇧🇿` },
{ value: 'BENIN', label: msg`Benin 🇧🇯` },
{ value: 'BHUTAN', label: msg`Bhutan 🇧🇹` },
{ value: 'BOLIVIA', label: msg`Bolivia 🇧🇴` },
{ value: 'BOSNIA_AND_HERZEGOVINA', label: msg`Bosnia & Herzegovina 🇧🇦` },
{ value: 'BOTSWANA', label: msg`Botswana 🇧🇼` },
{ value: 'BRAZIL', label: msg`Brazil 🇧🇷` },
{ value: 'BRUNEI', label: msg`Brunei 🇧🇳` },
{ value: 'BULGARIA', label: msg`Bulgaria 🇧🇬` },
{ value: 'BURKINA_FASO', label: msg`Burkina Faso 🇧🇫` },
{ value: 'BURUNDI', label: msg`Burundi 🇧🇮` },
{ value: 'CAMBODIA', label: msg`Cambodia 🇰🇭` },
{ value: 'CAMEROON', label: msg`Cameroon 🇨🇲` },
{ value: 'CANADA', label: msg`Canada 🇨🇦` },
{ value: 'CAPE_VERDE', label: msg`Cape Verde 🇨🇻` },
{
value: 'CENTRAL_AFRICAN_REPUBLIC',
label: msg`Central African Republic 🇨🇫`,
},
{ value: 'CHAD', label: msg`Chad 🇹🇩` },
{ value: 'CHILE', label: msg`Chile 🇨🇱` },
{ value: 'CHINA', label: msg`China 🇨🇳` },
{ value: 'COLOMBIA', label: msg`Colombia 🇨🇴` },
{ value: 'COMOROS', label: msg`Comoros 🇰🇲` },
{ value: 'CONGO', label: msg`Congo 🇨🇬` },
{ value: 'DR_CONGO', label: msg`DR Congo 🇨🇩` },
{ value: 'COSTA_RICA', label: msg`Costa Rica 🇨🇷` },
{ value: 'CROATIA', label: msg`Croatia 🇭🇷` },
{ value: 'CUBA', label: msg`Cuba 🇨🇺` },
{ value: 'CYPRUS', label: msg`Cyprus 🇨🇾` },
{ value: 'CZECH_REPUBLIC', label: msg`Czech Republic 🇨🇿` },
{ value: 'DENMARK', label: msg`Denmark 🇩🇰` },
{ value: 'DJIBOUTI', label: msg`Djibouti 🇩🇯` },
{ value: 'DOMINICA', label: msg`Dominica 🇩🇲` },
{ value: 'DOMINICAN_REPUBLIC', label: msg`Dominican Republic 🇩🇴` },
{ value: 'ECUADOR', label: msg`Ecuador 🇪🇨` },
{ value: 'EGYPT', label: msg`Egypt 🇪🇬` },
{ value: 'EL_SALVADOR', label: msg`El Salvador 🇸🇻` },
{ value: 'EQUATORIAL_GUINEA', label: msg`Equatorial Guinea 🇬🇶` },
{ value: 'ERITREA', label: msg`Eritrea 🇪🇷` },
{ value: 'ESTONIA', label: msg`Estonia 🇪🇪` },
{ value: 'ESWATINI', label: msg`Eswatini 🇸🇿` },
{ value: 'ETHIOPIA', label: msg`Ethiopia 🇪🇹` },
{ value: 'FIJI', label: msg`Fiji 🇫🇯` },
{ value: 'FINLAND', label: msg`Finland 🇫🇮` },
{ value: 'FRANCE', label: msg`France 🇫🇷` },
{ value: 'GABON', label: msg`Gabon 🇬🇦` },
{ value: 'GAMBIA', label: msg`Gambia 🇬🇲` },
{ value: 'GEORGIA', label: msg`Georgia 🇬🇪` },
{ value: 'GERMANY', label: msg`Germany 🇩🇪` },
{ value: 'GHANA', label: msg`Ghana 🇬🇭` },
{ value: 'GREECE', label: msg`Greece 🇬🇷` },
{ value: 'GRENADA', label: msg`Grenada 🇬🇩` },
{ value: 'GUATEMALA', label: msg`Guatemala 🇬🇹` },
{ value: 'GUINEA', label: msg`Guinea 🇬🇳` },
{ value: 'GUINEA_BISSAU', label: msg`Guinea-Bissau 🇬🇼` },
{ value: 'GUYANA', label: msg`Guyana 🇬🇾` },
{ value: 'HAITI', label: msg`Haiti 🇭🇹` },
{ value: 'HONDURAS', label: msg`Honduras 🇭🇳` },
{ value: 'HUNGARY', label: msg`Hungary 🇭🇺` },
{ value: 'ICELAND', label: msg`Iceland 🇮🇸` },
{ value: 'INDIA', label: msg`India 🇮🇳` },
{ value: 'INDONESIA', label: msg`Indonesia 🇮🇩` },
{ value: 'IRAN', label: msg`Iran 🇮🇷` },
{ value: 'IRAQ', label: msg`Iraq 🇮🇶` },
{ value: 'IRELAND', label: msg`Ireland 🇮🇪` },
{ value: 'ISRAEL', label: msg`Israel 🇮🇱` },
{ value: 'ITALY', label: msg`Italy 🇮🇹` },
{ value: 'IVORY_COAST', label: msg`Ivory Coast 🇨🇮` },
{ value: 'JAMAICA', label: msg`Jamaica 🇯🇲` },
{ value: 'JAPAN', label: msg`Japan 🇯🇵` },
{ value: 'JORDAN', label: msg`Jordan 🇯🇴` },
{ value: 'KAZAKHSTAN', label: msg`Kazakhstan 🇰🇿` },
{ value: 'KENYA', label: msg`Kenya 🇰🇪` },
{ value: 'KIRIBATI', label: msg`Kiribati 🇰🇮` },
{ value: 'KOSOVO', label: msg`Kosovo 🇽🇰` },
{ value: 'KUWAIT', label: msg`Kuwait 🇰🇼` },
{ value: 'KYRGYZSTAN', label: msg`Kyrgyzstan 🇰🇬` },
{ value: 'LAOS', label: msg`Laos 🇱🇦` },
{ value: 'LATVIA', label: msg`Latvia 🇱🇻` },
{ value: 'LEBANON', label: msg`Lebanon 🇱🇧` },
{ value: 'LESOTHO', label: msg`Lesotho 🇱🇸` },
{ value: 'LIBERIA', label: msg`Liberia 🇱🇷` },
{ value: 'LIBYA', label: msg`Libya 🇱🇾` },
{ value: 'LIECHTENSTEIN', label: msg`Liechtenstein 🇱🇮` },
{ value: 'LITHUANIA', label: msg`Lithuania 🇱🇹` },
{ value: 'LUXEMBOURG', label: msg`Luxembourg 🇱🇺` },
{ value: 'MADAGASCAR', label: msg`Madagascar 🇲🇬` },
{ value: 'MALAWI', label: msg`Malawi 🇲🇼` },
{ value: 'MALAYSIA', label: msg`Malaysia 🇲🇾` },
{ value: 'MALDIVES', label: msg`Maldives 🇲🇻` },
{ value: 'MALI', label: msg`Mali 🇲🇱` },
{ value: 'MALTA', label: msg`Malta 🇲🇹` },
{ value: 'MARSHALL_ISLANDS', label: msg`Marshall Islands 🇲🇭` },
{ value: 'MAURITANIA', label: msg`Mauritania 🇲🇷` },
{ value: 'MAURITIUS', label: msg`Mauritius 🇲🇺` },
{ value: 'MEXICO', label: msg`Mexico 🇲🇽` },
{ value: 'MICRONESIA', label: msg`Micronesia 🇫🇲` },
{ value: 'MOLDOVA', label: msg`Moldova 🇲🇩` },
{ value: 'MONACO', label: msg`Monaco 🇲🇨` },
{ value: 'MONGOLIA', label: msg`Mongolia 🇲🇳` },
{ value: 'MONTENEGRO', label: msg`Montenegro 🇲🇪` },
{ value: 'MOROCCO', label: msg`Morocco 🇲🇦` },
{ value: 'MOZAMBIQUE', label: msg`Mozambique 🇲🇿` },
{ value: 'MYANMAR', label: msg`Myanmar 🇲🇲` },
{ value: 'NAMIBIA', label: msg`Namibia 🇳🇦` },
{ value: 'NAURU', label: msg`Nauru 🇳🇷` },
{ value: 'NEPAL', label: msg`Nepal 🇳🇵` },
{ value: 'NETHERLANDS', label: msg`Netherlands 🇳🇱` },
{ value: 'NEW_ZEALAND', label: msg`New Zealand 🇳🇿` },
{ value: 'NICARAGUA', label: msg`Nicaragua 🇳🇮` },
{ value: 'NIGER', label: msg`Niger 🇳🇪` },
{ value: 'NIGERIA', label: msg`Nigeria 🇳🇬` },
{ value: 'NORTH_KOREA', label: msg`North Korea 🇰🇵` },
{ value: 'NORTH_MACEDONIA', label: msg`North Macedonia 🇲🇰` },
{ value: 'NORWAY', label: msg`Norway 🇳🇴` },
{ value: 'OMAN', label: msg`Oman 🇴🇲` },
{ value: 'PAKISTAN', label: msg`Pakistan 🇵🇰` },
{ value: 'PALAU', label: msg`Palau 🇵🇼` },
{ value: 'PALESTINE', label: msg`Palestine 🇵🇸` },
{ value: 'PANAMA', label: msg`Panama 🇵🇦` },
{ value: 'PAPUA_NEW_GUINEA', label: msg`Papua New Guinea 🇵🇬` },
{ value: 'PARAGUAY', label: msg`Paraguay 🇵🇾` },
{ value: 'PERU', label: msg`Peru 🇵🇪` },
{ value: 'PHILIPPINES', label: msg`Philippines 🇵🇭` },
{ value: 'POLAND', label: msg`Poland 🇵🇱` },
{ value: 'PORTUGAL', label: msg`Portugal 🇵🇹` },
{ value: 'QATAR', label: msg`Qatar 🇶🇦` },
{ value: 'ROMANIA', label: msg`Romania 🇷🇴` },
{ value: 'RUSSIA', label: msg`Russia 🇷🇺` },
{ value: 'RWANDA', label: msg`Rwanda 🇷🇼` },
{ value: 'SAINT_KITTS_AND_NEVIS', label: msg`Saint Kitts & Nevis 🇰🇳` },
{ value: 'SAINT_LUCIA', label: msg`Saint Lucia 🇱🇨` },
{ value: 'SAINT_VINCENT', label: msg`Saint Vincent 🇻🇨` },
{ value: 'SAMOA', label: msg`Samoa 🇼🇸` },
{ value: 'SAN_MARINO', label: msg`San Marino 🇸🇲` },
{ value: 'SAO_TOME_AND_PRINCIPE', label: msg`São Tomé & Príncipe 🇸🇹` },
{ value: 'SAUDI_ARABIA', label: msg`Saudi Arabia 🇸🇦` },
{ value: 'SENEGAL', label: msg`Senegal 🇸🇳` },
{ value: 'SERBIA', label: msg`Serbia 🇷🇸` },
{ value: 'SEYCHELLES', label: msg`Seychelles 🇸🇨` },
{ value: 'SIERRA_LEONE', label: msg`Sierra Leone 🇸🇱` },
{ value: 'SINGAPORE', label: msg`Singapore 🇸🇬` },
{ value: 'SLOVAKIA', label: msg`Slovakia 🇸🇰` },
{ value: 'SLOVENIA', label: msg`Slovenia 🇸🇮` },
{ value: 'SOLOMON_ISLANDS', label: msg`Solomon Islands 🇸🇧` },
{ value: 'SOMALIA', label: msg`Somalia 🇸🇴` },
{ value: 'SOUTH_AFRICA', label: msg`South Africa 🇿🇦` },
{ value: 'SOUTH_KOREA', label: msg`South Korea 🇰🇷` },
{ value: 'SOUTH_SUDAN', label: msg`South Sudan 🇸🇸` },
{ value: 'SPAIN', label: msg`Spain 🇪🇸` },
{ value: 'SRI_LANKA', label: msg`Sri Lanka 🇱🇰` },
{ value: 'SUDAN', label: msg`Sudan 🇸🇩` },
{ value: 'SURINAME', label: msg`Suriname 🇸🇷` },
{ value: 'SWEDEN', label: msg`Sweden 🇸🇪` },
{ value: 'SWITZERLAND', label: msg`Switzerland 🇨🇭` },
{ value: 'SYRIA', label: msg`Syria 🇸🇾` },
{ value: 'TAIWAN', label: msg`Taiwan 🇹🇼` },
{ value: 'TAJIKISTAN', label: msg`Tajikistan 🇹🇯` },
{ value: 'TANZANIA', label: msg`Tanzania 🇹🇿` },
{ value: 'THAILAND', label: msg`Thailand 🇹🇭` },
{ value: 'TIMOR_LESTE', label: msg`Timor-Leste 🇹🇱` },
{ value: 'TOGO', label: msg`Togo 🇹🇬` },
{ value: 'TONGA', label: msg`Tonga 🇹🇴` },
{ value: 'TRINIDAD_AND_TOBAGO', label: msg`Trinidad & Tobago 🇹🇹` },
{ value: 'TUNISIA', label: msg`Tunisia 🇹🇳` },
{ value: 'TURKEY', label: msg`Turkey 🇹🇷` },
{ value: 'TURKMENISTAN', label: msg`Turkmenistan 🇹🇲` },
{ value: 'TUVALU', label: msg`Tuvalu 🇹🇻` },
{ value: 'UGANDA', label: msg`Uganda 🇺🇬` },
{ value: 'UKRAINE', label: msg`Ukraine 🇺🇦` },
{ value: 'UNITED_ARAB_EMIRATES', label: msg`UAE 🇦🇪` },
{ value: 'UNITED_KINGDOM', label: msg`UK 🇬🇧` },
{ value: 'UNITED_STATES', label: msg`USA 🇺🇸` },
{ value: 'URUGUAY', label: msg`Uruguay 🇺🇾` },
{ value: 'UZBEKISTAN', label: msg`Uzbekistan 🇺🇿` },
{ value: 'VANUATU', label: msg`Vanuatu 🇻🇺` },
{ value: 'VATICAN', label: msg`Vatican 🇻🇦` },
{ value: 'VENEZUELA', label: msg`Venezuela 🇻🇪` },
{ value: 'VIETNAM', label: msg`Vietnam 🇻🇳` },
{ value: 'YEMEN', label: msg`Yemen 🇾🇪` },
{ value: 'ZAMBIA', label: msg`Zambia 🇿🇲` },
{ value: 'ZIMBABWE', label: msg`Zimbabwe 🇿🇼` },
];
export const PARTNER_LANGUAGE_VALUES = [
'ENGLISH',
'FRENCH',
'GERMAN',
'SPANISH',
'PORTUGUESE',
'ITALIAN',
'DUTCH',
'ARABIC',
'CHINESE',
'JAPANESE',
'RUSSIAN',
'HINDI',
] as const;
export type PartnerLanguageValue = (typeof PARTNER_LANGUAGE_VALUES)[number];
export const PARTNER_LANGUAGE_OPTIONS: ReadonlyArray<{
value: PartnerLanguageValue;
label: MessageDescriptor;
}> = [
{ value: 'ENGLISH', label: msg`English` },
{ value: 'FRENCH', label: msg`French` },
{ value: 'GERMAN', label: msg`German` },
{ value: 'SPANISH', label: msg`Spanish` },
{ value: 'PORTUGUESE', label: msg`Portuguese` },
{ value: 'ITALIAN', label: msg`Italian` },
{ value: 'DUTCH', label: msg`Dutch` },
{ value: 'ARABIC', label: msg`Arabic` },
{ value: 'CHINESE', label: msg`Chinese` },
{ value: 'JAPANESE', label: msg`Japanese` },
{ value: 'RUSSIAN', label: msg`Russian` },
{ value: 'HINDI', label: msg`Hindi` },
];
// Per-step required field names. The wizard reducer reads this to gate `goNext`.
export const PARTNER_APPLICATION_STEP_REQUIRED_FIELDS: Record<
PartnerApplicationStepId,
ReadonlyArray<keyof PartnerApplicationState>
> = {
identity: ['name', 'email', 'company'],
profile: ['country', 'typeOfTeam'],
expertise: ['partnerScope'],
commercials: [],
};