-
+
diff --git a/src/components/frame/v5/pages/UserCryptoToFiatPage/partials/PersonalDetailsForm/constants.ts b/src/components/frame/v5/pages/UserCryptoToFiatPage/partials/PersonalDetailsForm/constants.ts
new file mode 100644
index 00000000000..a6f766481a0
--- /dev/null
+++ b/src/components/frame/v5/pages/UserCryptoToFiatPage/partials/PersonalDetailsForm/constants.ts
@@ -0,0 +1,56 @@
+import { defineMessages } from 'react-intl';
+
+const displayname =
+ 'v5.pages.UserCryptoToFiatPage.partials.PersonalDetailsForm';
+
+export const PERSONAL_DETAILS_FORM_MSGS = defineMessages({
+ title: {
+ id: `${displayname}.title`,
+ defaultMessage: 'Personal details',
+ },
+ subtitle: {
+ id: `${displayname}.subtitle`,
+ defaultMessage:
+ 'The information is only provided to Bridge and not stored by Colony.',
+ },
+ cancelButtonTitle: {
+ id: `${displayname}.cancelButtonTitle`,
+ defaultMessage: 'Cancel',
+ },
+ proceedButtonTitle: {
+ id: `${displayname}.proceedButtonTitle`,
+ defaultMessage: `Next`,
+ },
+ firstNameLabel: {
+ id: `${displayname}.firstNameLabel`,
+ defaultMessage: 'First name',
+ },
+ firstNamePlaceholder: {
+ id: `${displayname}.firstNamePlaceholder`,
+ defaultMessage: 'First name',
+ },
+ lastNameLabel: {
+ id: `${displayname}.lastNameLabel`,
+ defaultMessage: 'Last name',
+ },
+ lastNamePlaceholder: {
+ id: `${displayname}.lastNamePlaceholder`,
+ defaultMessage: 'Last name',
+ },
+ emailLabel: {
+ id: `${displayname}.emailLabel`,
+ defaultMessage: 'Email address',
+ },
+ emailPlaceholder: {
+ id: `${displayname}.emailPlaceholder`,
+ defaultMessage: 'Email address',
+ },
+ countryLabel: {
+ id: `${displayname}.countryLabel`,
+ defaultMessage: 'Country',
+ },
+ postcodePlaceholder: {
+ id: `${displayname}.postcodePlaceholder`,
+ defaultMessage: 'Postcode',
+ },
+});
diff --git a/src/components/frame/v5/pages/UserCryptoToFiatPage/partials/PersonalDetailsForm/validation.ts b/src/components/frame/v5/pages/UserCryptoToFiatPage/partials/PersonalDetailsForm/validation.ts
index 78593eb6eeb..9b05dbbce5f 100644
--- a/src/components/frame/v5/pages/UserCryptoToFiatPage/partials/PersonalDetailsForm/validation.ts
+++ b/src/components/frame/v5/pages/UserCryptoToFiatPage/partials/PersonalDetailsForm/validation.ts
@@ -1,9 +1,27 @@
import { type InferType, object, string } from 'yup';
+import { formErrorMessage, formatText } from '~utils/intl.ts';
+import { capitalizeFirstLetter } from '~utils/strings.ts';
+
+import { PERSONAL_DETAILS_FORM_MSGS } from './constants.ts';
+
export const validationSchema = object({
- firstName: string().required(),
- lastName: string().required(),
- email: string().email().required(),
+ firstName: string().required(
+ formatText({ id: 'cryptoToFiat.forms.error.personalDetails.firstName' }),
+ ),
+ lastName: string().required(
+ formatText({ id: 'cryptoToFiat.forms.error.personalDetails.lastName' }),
+ ),
+ email: string()
+ .email(
+ capitalizeFirstLetter(
+ formErrorMessage(PERSONAL_DETAILS_FORM_MSGS.emailLabel, 'invalid'),
+ { lowerCaseRemainingLetters: true },
+ ),
+ )
+ .required(
+ formatText({ id: 'cryptoToFiat.forms.error.personalDetails.email' }),
+ ),
}).defined();
export type FormValues = InferType
;
diff --git a/src/components/frame/v5/pages/UserCryptoToFiatPage/partials/RowItem/RowItem.tsx b/src/components/frame/v5/pages/UserCryptoToFiatPage/partials/RowItem/RowItem.tsx
index 92009a7eb9e..1a3777d3d07 100644
--- a/src/components/frame/v5/pages/UserCryptoToFiatPage/partials/RowItem/RowItem.tsx
+++ b/src/components/frame/v5/pages/UserCryptoToFiatPage/partials/RowItem/RowItem.tsx
@@ -1,6 +1,8 @@
+import clsx from 'clsx';
import React from 'react';
import LoadingSkeleton from '~common/LoadingSkeleton/LoadingSkeleton.tsx';
+import { useMobile } from '~hooks';
import CryptoToFiatBadge from '~v5/common/Pills/CryptoToFiatBadge.tsx/CryptoToFiatBadge.tsx';
import Button from '~v5/shared/Button/Button.tsx';
@@ -21,13 +23,13 @@ const Heading: React.FC = ({
}) => {
return (
-
+
- {title}
+ {title}
({accessory})
@@ -56,9 +58,15 @@ const Body: React.FC = ({
ctaComponent,
isDataLoading,
}) => {
+ const isMobile = useMobile();
return (
-
-
+
+
{descriptionComponent ?? (
{title &&
{title}
}
@@ -68,7 +76,11 @@ const Body: React.FC = ({
)}
-
+
{ctaComponent ?? (
= ({
text={ctaTitle}
onClick={ctaOnClick}
disabled={ctaDisabled}
+ isFullSize={isMobile}
/>
)}
@@ -92,7 +105,7 @@ const Container: React.FC = ({ children }) => {
};
Body.displayName = `${displayName}.Body`;
-Heading.displayName = `${displayName}.heading`;
+Heading.displayName = `${displayName}.Heading`;
Container.displayName = `${displayName}.Container`;
const RowItem = { Heading, Container, Body };
diff --git a/src/components/frame/v5/pages/UserCryptoToFiatPage/partials/Verification/VerificationModal.tsx b/src/components/frame/v5/pages/UserCryptoToFiatPage/partials/Verification/VerificationModal.tsx
index 7d64bd6d8aa..d640eeadbb6 100644
--- a/src/components/frame/v5/pages/UserCryptoToFiatPage/partials/Verification/VerificationModal.tsx
+++ b/src/components/frame/v5/pages/UserCryptoToFiatPage/partials/Verification/VerificationModal.tsx
@@ -45,7 +45,7 @@ const MSG = defineMessages({
},
kycTabHeading: {
id: `${displayName}.kycTabHeading`,
- defaultMessage: 'KYC form',
+ defaultMessage: 'KYC',
},
});
@@ -81,7 +81,6 @@ const VerificationModal: FC = ({
isFullOnMobile={false}
isOpen={isOpened}
onRequestClose={onClose}
- isTopSectionWithBackground
>
= ({
onClick={onClose}
className="absolute right-4 top-4 text-gray-400 hover:text-gray-600"
/>
-
+
= ({
)}
diff --git a/src/components/v5/common/Fields/Input/Input.tsx b/src/components/v5/common/Fields/Input/Input.tsx
index 6a407a4eb4a..ac6a65d2809 100644
--- a/src/components/v5/common/Fields/Input/Input.tsx
+++ b/src/components/v5/common/Fields/Input/Input.tsx
@@ -20,6 +20,7 @@ const Input: FC = ({
errorMaxChar = false,
placeholder,
shouldNumberOfCharsBeVisible = false,
+ shouldErrorMessageBeVisible = true,
isError,
customErrorMessage,
defaultValue,
@@ -141,7 +142,7 @@ const Input: FC = ({
)}
- {isErrorStatus && !isTyping && (
+ {shouldErrorMessageBeVisible && isErrorStatus && !isTyping && (
<>
{isDecoratedError ? (
diff --git a/src/components/v5/common/Fields/Input/types.ts b/src/components/v5/common/Fields/Input/types.ts
index 3e3f48dc8cb..25728eed88d 100644
--- a/src/components/v5/common/Fields/Input/types.ts
+++ b/src/components/v5/common/Fields/Input/types.ts
@@ -9,6 +9,7 @@ export type InputProps = {
errorMaxChar?: boolean;
placeholder?: string;
shouldNumberOfCharsBeVisible?: boolean;
+ shouldErrorMessageBeVisible?: boolean;
isError?: boolean;
isErrorStatus?: boolean;
customErrorMessage?: string;
diff --git a/src/components/v5/common/Fields/Select/Select.module.css b/src/components/v5/common/Fields/Select/Select.module.css
index bdd6d3735e3..12f3c50b12f 100644
--- a/src/components/v5/common/Fields/Select/Select.module.css
+++ b/src/components/v5/common/Fields/Select/Select.module.css
@@ -1,5 +1,5 @@
.wrapper :global(.select__control) {
- @apply rounded-e rounded-s border-gray-300 text-md font-medium text-gray-900;
+ @apply rounded-e rounded-s border-gray-300 text-md font-normal text-gray-900;
}
.wrapper--error :global(.select__control) {
@@ -35,7 +35,7 @@
}
.menu {
- @apply my-1 rounded-e rounded-s border border-gray-300 shadow-[0px_1px_2px_0px_rgba(16,24,40,0.05)];
+ @apply !my-1 rounded-e rounded-s border border-gray-300 !shadow-[0px_0px_2px_0px_rgba(16,24,40,0.05)];
}
.menu :global(.select__menu-list) {
@@ -43,7 +43,11 @@
}
.menu :global(.select__option) {
- @apply cursor-pointer rounded-e rounded-s text-gray-900 transition-all;
+ @apply cursor-pointer rounded-e rounded-s text-md font-normal text-gray-900 transition-all;
+}
+
+.menu :global(.select__option):hover {
+ @apply bg-gray-50;
}
.menu :global(.select__option:not(.with-link)) {
@@ -59,9 +63,9 @@
}
.menu :global(.select__option--is-selected) {
- @apply bg-transparent font-medium text-blue-400;
+ @apply bg-transparent font-medium;
}
.menu :global(.select__option--is-focused) {
- @apply bg-transparent sm:bg-gray-200;
+ @apply bg-transparent sm:bg-gray-50;
}
diff --git a/src/components/v5/common/Pills/CryptoToFiatBadge.tsx/consts.ts b/src/components/v5/common/Pills/CryptoToFiatBadge.tsx/consts.ts
index 8d1e8434d71..e3859c32c59 100644
--- a/src/components/v5/common/Pills/CryptoToFiatBadge.tsx/consts.ts
+++ b/src/components/v5/common/Pills/CryptoToFiatBadge.tsx/consts.ts
@@ -6,7 +6,7 @@ export const badgeThemes: CryptoToFiatBadgeTheme = {
iconClassName: 'text-negative-400 h-3',
},
green: {
- className: 'bg-teams-green-100 text-teams-green-400',
+ className: 'bg-success-100 text-success-400',
},
orange: {
className: 'bg-orange-100 text-orange-400',
diff --git a/src/components/v5/frame/PageLayout/PageLayout.tsx b/src/components/v5/frame/PageLayout/PageLayout.tsx
index 61a8ab598f7..1e2a01cd9bb 100644
--- a/src/components/v5/frame/PageLayout/PageLayout.tsx
+++ b/src/components/v5/frame/PageLayout/PageLayout.tsx
@@ -67,7 +67,6 @@ const PageLayout: FC> = ({
pauseOnFocusLoss
pauseOnHover
closeButton={CloseButton}
- className="modal-blur"
/>
{/* This div has to always be rendered, otherwise the height of the top content wrapper won't be calculated correctly */}
diff --git a/src/components/v5/shared/Modal/Modal.tsx b/src/components/v5/shared/Modal/Modal.tsx
index bd9179ac61a..874c516d4de 100644
--- a/src/components/v5/shared/Modal/Modal.tsx
+++ b/src/components/v5/shared/Modal/Modal.tsx
@@ -41,7 +41,8 @@ const Modal: FC
> = ({
onRequestClose={onClose}
isOpen={isOpen}
{...{ isFullOnMobile, ...props }}
- isTopSectionWithBackground={isTopSectionWithBackground}
+ isHighlighted={isTopSectionWithBackground}
+ hasPadding={!isTopSectionWithBackground}
>
{Icon && (
= ({
role = 'dialog',
isFullOnMobile,
- isTopSectionWithBackground,
+ hasPadding,
+ isHighlighted,
isOpen,
...props
}) => {
@@ -39,8 +40,9 @@ const ModalBase: FC = ({
'max-h-[calc(100%-4rem)] w-[calc(100vw-3rem)] rounded-xl border border-gray-200 shadow-default':
!isFullOnMobile,
base: 'z-base outline-0',
- 'pl-6 pt-6': !isTopSectionWithBackground,
- 'border-2 border-purple-200 pl-0 pt-0': isTopSectionWithBackground,
+ 'pl-6 pt-6': hasPadding,
+ 'pl-0 pt-0': !hasPadding,
+ 'border-2 border-purple-200': isHighlighted,
},
)}
shouldFocusAfterRender
diff --git a/src/components/v5/shared/Modal/types.ts b/src/components/v5/shared/Modal/types.ts
index 7621f60bcf8..512c0274923 100644
--- a/src/components/v5/shared/Modal/types.ts
+++ b/src/components/v5/shared/Modal/types.ts
@@ -6,7 +6,8 @@ import { type ButtonMode } from '../Button/types.ts';
export interface ModalBaseProps extends ReactModalProps {
isFullOnMobile?: boolean;
- isTopSectionWithBackground?: boolean;
+ hasPadding?: boolean;
+ isHighlighted?: boolean;
}
export interface ModalProps extends ModalBaseProps {
diff --git a/src/constants/postalCodesRegex.ts b/src/constants/postalCodesRegex.ts
new file mode 100644
index 00000000000..b9427e657e9
--- /dev/null
+++ b/src/constants/postalCodesRegex.ts
@@ -0,0 +1,166 @@
+export const postalCodesRegex = {
+ AF: '[0-9]{4}',
+ AL: '(120|122)[0-9]{2}',
+ DZ: '[0-9]{5}',
+ AS: '[0-9]{5}',
+ AD: '[0-9]{5}',
+ AI: 'AI-2640',
+ AR: '[A-Z]{1}[0-9]{4}[A-Z]{3}',
+ AM: '[0-9]{4}',
+ AU: '[0-9]{4}',
+ AT: '[0-9]{4}',
+ AZ: '[0-9]{4}',
+ BD: '[0-9]{4}',
+ BB: 'BB[0-9]{5}',
+ BY: '[0-9]{6}',
+ BE: '[0-9]{4}',
+ BM: '[A-Z]{2}[0-9]{2}',
+ BT: '[0-9]{5}',
+ BA: '[0-9]{5}',
+ BR: '[0-9]{5}-[0-9]{3}',
+ BN: '[A-Z]{2}[0-9]{4}',
+ BG: '[0-9]{4}',
+ KH: '[0-9]{5}',
+ CA: '[A-Z][0-9][A-Z] ?[0-9][A-Z][0-9]',
+ CI: '[0-9]{5}',
+ CV: '[0-9]{4}',
+ KY: '[A-Z]{2}[0-9]-[0-9]{4}',
+ CL: '[0-9]{7}',
+ CN: '[0-9]{6}',
+ CO: '[0-9]{6}',
+ CR: '[0-9]{5}',
+ HR: '[0-9]{5}',
+ CU: '[0-9]{5}',
+ CY: '[0-9]{4}',
+ CZ: '[0-9]{3} [0-9]{2}',
+ DK: '[0-9]{4}',
+ DO: '[0-9]{5}',
+ EC: '[0-9]{6}',
+ EG: '[0-9]{5}',
+ SV: '[0-9]{4}',
+ EE: '[0-9]{5}',
+ ET: '[0-9]{4}',
+ FK: 'FIQQ 1ZZ',
+ FO: '[0-9]{3}',
+ FI: '[0-9]{5}',
+ FR: '[0-9]{5}',
+ PF: '987[0-9]{2}',
+ DE: '[0-9]{5}',
+ GI: 'GX11 1AA',
+ GR: '[0-9]{3} [0-9]{2}',
+ GL: '[0-9]{4}',
+ GP: '971[0-9]{2}',
+ GU: '\\d{5}(?:[-\\s]\\d{4})?',
+ GT: '[0-9]{5}',
+ GG: '([Gg][Ii][Rr] 0[Aa]{2})|((([A-Za-z][0-9]{1,2})|(([A-Za-z][A-Ha-hJ-Yj-y][0-9]{1,2})|(([A-Za-z][0-9][A-Za-z])|([A-Za-z][A-Ha-hJ-Yj-y][0-9][A-Za-z]?))))\\s?[0-9][A-Za-z]{2})',
+ GW: '[0-9]{4}',
+ GN: '[0-9]{3}',
+ GF: '973[0-9]{2}',
+ HT: '[0-9]{4}',
+ HN: '[0-9]{5}',
+ HU: '[0-9]{4}',
+ IS: '[0-9]{3}',
+ IN: '[1-9][0-9]{5}',
+ ID: '[0-9]{5}',
+ IR: '[0-9]{5}',
+ IQ: '[0-9]{5}',
+ IE: '(?:^[AC-FHKNPRTV-Y][0-9]{2}|D6W)[ -]?[0-9AC-FHKNPRTV-Y]{4}$',
+ IL: '[0-9]{5}|[0-9]{7}',
+ IT: '[0-9]{5}',
+ JP: '[0-9]{3}-[0-9]{4}',
+ JE: '([Gg][Ii][Rr] 0[Aa]{2})|((([A-Za-z][0-9]{1,2})|(([A-Za-z][A-Ha-hJ-Yj-y][0-9]{1,2})|(([A-Za-z][0-9][A-Za-z])|([A-Za-z][A-Ha-hJ-Yj-y][0-9][A-Za-z]?))))\\s?[0-9][A-Za-z]{2})',
+ JO: '[0-9]{5}',
+ KZ: '[0-9]{6}',
+ KE: '[0-9]{5}',
+ KR: '[0-9]{5}',
+ XK: '[0-9]{5}',
+ KW: '[0-9]{5}',
+ KG: '[0-9]{6}',
+ LA: '[0-9]{5}',
+ LV: 'LV-[0-9]{4}',
+ LB: '[0-9]{4} [0-9]{4}',
+ LS: '[0-9]{3}',
+ LR: '[0-9]{4}',
+ LI: '[0-9]{4}',
+ LT: 'LT-[0-9]{5}',
+ LU: '[0-9]{4}',
+ MK: '[0-9]{4}',
+ MG: '[0-9]{3}',
+ MY: '[0-9]{5}',
+ MV: '[0-9]{5}',
+ MT: '[A-Z]{3} [0-9]{4}',
+ MH: '\\d{5}(?:[-\\s]\\d{4})?',
+ MQ: '972[0-9]{2}',
+ MU: '[0-9]{5}',
+ YT: '976[0-9]{2}',
+ MX: '[0-9]{5}',
+ MD: 'MD-?[0-9]{4}',
+ MC: '980[0-9]{2}',
+ MN: '[0-9]{5}',
+ ME: '[0-9]{5}',
+ MS: 'MSR [0-9]{4}',
+ MA: '[0-9]{5}',
+ MZ: '[0-9]{4}',
+ MM: '[0-9]{5}',
+ NP: '[0-9]{5}',
+ NL: '(?:NL-)?(\\d{4})\\s*([A-Z]{2})',
+ NC: '988[0-9]{2}',
+ NZ: '[0-9]{4}',
+ NE: '[0-9]{4}',
+ NG: '[0-9]{6}',
+ MP: '^\\d{5}(?:[-\\s]\\d{4})?$',
+ NO: '[0-9]{4}',
+ OM: '[0-9]{3}',
+ PK: '[0-9]{5}',
+ PA: '[0-9]{4}',
+ PG: '[0-9]{3}',
+ PY: '[0-9]{4}',
+ PE: '[0-9]{5}',
+ PH: '[0-9]{4}',
+ PL: '[0-9]{2}-[0-9]{3}',
+ PT: '[0-9]{4}-[0-9]{3}',
+ QA: '[0-9]{5}',
+ RO: '[0-9]{6}',
+ RU: '[0-9]{6}',
+ WS: 'WS[0-9]{4}',
+ SA: '[0-9]{5}(-[0-9]{4})?',
+ SN: '[0-9]{5}',
+ RS: '[0-9]{5}',
+ SG: '[0-9]{6}',
+ SK: '[0-9]{3} [0-9]{2}',
+ SI: '[0-9]{4}',
+ ZA: '[0-9]{4}',
+ ES: '[0-9]{5}',
+ LK: '[0-9]{5}',
+ SD: '[0-9]{5}',
+ SZ: '[0-9]{3}',
+ SE: '[0-9]{3} [0-9]{2}',
+ CH: '[0-9]{4}',
+ SY: '[0-9]{4}',
+ TW: '[0-9]{3}(\\d{2})?',
+ TJ: '[0-9]{6}',
+ TZ: '[0-9]{5}',
+ TH: '[0-9]{5}',
+ TG: '[0-9]{5}',
+ TO: '[0-9]{4}',
+ TT: '[0-9]{6}',
+ TN: '[0-9]{4}',
+ TR: '[0-9]{5}',
+ TM: '[0-9]{6}',
+ TC: 'TKCA 1ZZ',
+ UG: '[0-9]{5}',
+ UA: '[0-9]{5}',
+ AE: '[0-9]{5}',
+ GB: '([Gg][Ii][Rr] 0[Aa]{2})|((([A-Za-z][0-9]{1,2})|(([A-Za-z][A-Ha-hJ-Yj-y][0-9]{1,2})|(([A-Za-z][0-9][A-Za-z])|([A-Za-z][A-Ha-hJ-Yj-y][0-9][A-Za-z]?))))\\s?[0-9][A-Za-z]{2})',
+ US: '\\d{5}(-\\d{4})?',
+ UY: '[0-9]{5}',
+ UZ: '[0-9]{6}',
+ VU: '[0-9]{4}',
+ VE: '[0-9]{4}',
+ VN: '[0-9]{6}',
+ VG: 'VG[0-9]{4}',
+ VI: '[0-9]{5}',
+ YE: '[0-9]{5}',
+ ZM: '[0-9]{5}',
+ ZW: '[0-9]{5}',
+};
diff --git a/src/i18n/en.json b/src/i18n/en.json
index 61c41c9e61b..f0a6290eb61 100644
--- a/src/i18n/en.json
+++ b/src/i18n/en.json
@@ -1623,5 +1623,27 @@
"expenditure.paymentOverview": "Payment overview",
"expenditure.paymentOverview.totalPayments": "Total payments",
"expenditure.paymentOverview.paid": "Paid",
- "expenditure.paymentOverview.payableNow": "Payable now"
+ "expenditure.paymentOverview.payableNow": "Payable now",
+ "cryptoToFiat.forms.error.personalDetails.firstName": "Please enter your first name",
+ "cryptoToFiat.forms.error.personalDetails.lastName": "Please enter your last name",
+ "cryptoToFiat.forms.error.personalDetails.email": "Please enter your email in the format: name@example.com",
+ "cryptoToFiat.forms.error.bankAccount.accountOwner": "Please enter your full name",
+ "cryptoToFiat.forms.error.bankAccount.bankName": "Please enter a bank name",
+ "cryptoToFiat.forms.error.bankAccount.currency": "A preferred currency is required",
+ "cryptoToFiat.forms.error.bankAccount.accountNumber": "Please enter an account number",
+ "cryptoToFiat.forms.error.bankAccount.routingNumber": "Please enter a routing number",
+ "cryptoToFiat.forms.error.bankAccount.iban": "Please enter an IBAN",
+ "cryptoToFiat.forms.error.bankAccount.swift": "Please enter a SWIFT/BIC",
+ "cryptoToFiat.forms.error.bankAccount.country": "Please select a Country",
+ "cryptoToFiat.forms.error.address": "Please provide your {fields} information",
+ "cryptoToFiat.forms.error.address.address1": "Please enter your address",
+ "cryptoToFiat.forms.error.address.country": "Please select your country",
+ "cryptoToFiat.forms.error.address.city": "Please enter your city",
+ "cryptoToFiat.forms.error.address.state": "Please select your state",
+ "cryptoToFiat.forms.error.address.postcode": "Please enter your postcode",
+ "form.required": "{fieldName} is a required field",
+ "form.invalid": "Invalid {fieldName} format",
+ "form.length": "{fieldName} must be exactly {length} characters",
+ "form.min": "{fieldName} must be at least {length} characters",
+ "form.max": "{fieldName} must be a maximum of {length} characters"
}
diff --git a/src/styles/main.css b/src/styles/main.css
index d65758833a2..995b124fdfc 100644
--- a/src/styles/main.css
+++ b/src/styles/main.css
@@ -15,6 +15,10 @@
}
}
+iframe {
+ @apply overflow-hidden;
+}
+
.rts___tabs {
@apply py-0;
}
diff --git a/src/utils/countries.ts b/src/utils/countries.ts
index b0ed830fd7b..d0600fb318d 100644
--- a/src/utils/countries.ts
+++ b/src/utils/countries.ts
@@ -2,9 +2,10 @@ import countries from 'i18n-iso-countries';
import enLocale from 'i18n-iso-countries/langs/en.json';
countries.registerLocale(enLocale);
+const { locale } = enLocale;
const alpha2ToAlpha3 = countries.getAlpha2Codes();
-const countryNames = countries.getNames('en');
+const countryNames = countries.getNames(locale);
export const COUNTRIES_WITHOUT_STATES = [
'ASM',
@@ -104,14 +105,14 @@ export const getCountryByCode = (code: string): CountryData | undefined => {
if (code.length === 2) {
alpha2Code = code;
} else if (code.length === 3) {
- alpha2Code = countries.getAlpha2Code(code, 'en');
+ alpha2Code = countries.alpha3ToAlpha2(code);
}
if (!alpha2Code || FILTERED_COUNTRIES.includes(alpha2Code)) {
return undefined;
}
- const name = countries.getName(alpha2Code, 'en');
+ const name = countries.getName(alpha2Code, locale);
if (!name) {
return undefined;
diff --git a/src/utils/intl.ts b/src/utils/intl.ts
index 4c80f6db386..abc9984a248 100644
--- a/src/utils/intl.ts
+++ b/src/utils/intl.ts
@@ -114,3 +114,53 @@ export function formatText(
return message;
}
+
+/**
+ * @param fieldName { id: 'name', defaultMessage: 'Name' }
+ * @param validationMessage 'required'
+ * @returns `{fieldName} is a required field`
+ * ---
+ * @param fieldName { id: 'name', defaultMessage: 'Name' }
+ * @param validationMessage 'length'
+ * @param length null | undefined
+ * @throws `length is required when validationMessage is min, max or length`
+ * ---
+ * @param fieldName { id: 'name', defaultMessage: 'Name' }
+ * @param validationMessage 'min'
+ * @param length 3
+ * @returns `{fieldName} must be at least {length} characters`
+ * ---
+ */
+export function formErrorMessage(
+ fieldName: MessageDescriptor,
+ validationMessage: 'required' | 'invalid',
+): string;
+export function formErrorMessage(
+ fieldName: MessageDescriptor,
+ validationMessage: 'min' | 'max' | 'length',
+ length: number,
+): string;
+export function formErrorMessage(
+ fieldName: MessageDescriptor,
+ validationMessage: 'required' | 'min' | 'max' | 'invalid' | 'length',
+ length?: number,
+) {
+ if (
+ (validationMessage === 'min' ||
+ validationMessage === 'max' ||
+ validationMessage === 'length') &&
+ length === undefined
+ ) {
+ throw new Error(
+ 'length is required when validationMessage is min, max or length',
+ );
+ }
+
+ return formatText(
+ { id: `form.${validationMessage}` },
+ {
+ fieldName: formatText(fieldName),
+ length,
+ },
+ );
+}
diff --git a/src/utils/strings.ts b/src/utils/strings.ts
index b86a3fb2b35..089c81d5bc5 100644
--- a/src/utils/strings.ts
+++ b/src/utils/strings.ts
@@ -57,8 +57,15 @@ export const humanReadableFileSize = (size: number) => {
* @param {string} word The word / string to capitalize
* @return {string} The capitalized string
*/
-export const capitalizeFirstLetter = (word: string): string =>
- word && word.charAt(0).toUpperCase() + word.slice(1);
+export const capitalizeFirstLetter = (
+ word: string,
+ options?: { lowerCaseRemainingLetters?: boolean },
+): string =>
+ word &&
+ word.charAt(0).toUpperCase() +
+ (options?.lowerCaseRemainingLetters
+ ? word.slice(1).toLowerCase()
+ : word.slice(1));
/**
* Capitalize a word (converts the word to lower case, except for the first letter)