diff --git a/packages/backend/package.json b/packages/backend/package.json index 22a492e6..7012a7d7 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -60,6 +60,7 @@ "https-proxy-agent": "^7.0.1", "jsonwebtoken": "^9.0.0", "knex": "^2.4.0", + "libphonenumber-js": "^1.10.48", "lodash.get": "^4.4.2", "luxon": "2.5.2", "memory-cache": "^0.2.0", diff --git a/packages/backend/src/apps/formatter/actions/numbers/index.ts b/packages/backend/src/apps/formatter/actions/numbers/index.ts index 80531947..c4518b44 100644 --- a/packages/backend/src/apps/formatter/actions/numbers/index.ts +++ b/packages/backend/src/apps/formatter/actions/numbers/index.ts @@ -3,11 +3,13 @@ import defineAction from '../../../../helpers/define-action'; import performMathOperation from './transformers/perform-math-operation'; import randomNumber from './transformers/random-number'; import formatNumber from './transformers/format-number'; +import formatPhoneNumber from './transformers/format-phone-number'; const transformers = { performMathOperation, randomNumber, formatNumber, + formatPhoneNumber, }; export default defineAction({ @@ -26,6 +28,7 @@ export default defineAction({ { label: 'Perform Math Operation', value: 'performMathOperation' }, { label: 'Random Number', value: 'randomNumber' }, { label: 'Format Number', value: 'formatNumber' }, + { label: 'Format Phone Number', value: 'formatPhoneNumber' }, ], additionalFields: { type: 'query', diff --git a/packages/backend/src/apps/formatter/actions/numbers/transformers/format-phone-number.ts b/packages/backend/src/apps/formatter/actions/numbers/transformers/format-phone-number.ts new file mode 100644 index 00000000..a0a986b7 --- /dev/null +++ b/packages/backend/src/apps/formatter/actions/numbers/transformers/format-phone-number.ts @@ -0,0 +1,24 @@ +import { IGlobalVariable } from '@automatisch/types'; +import parsePhoneNumber, { CountryCode } from 'libphonenumber-js'; + +const formatPhoneNumber = ($: IGlobalVariable) => { + const phoneNumber = $.step.parameters.phoneNumber as string; + const toFormat = $.step.parameters.toFormat as string; + const phoneNumberCountryCode = ($.step.parameters.phoneNumberCountryCode || + 'US') as CountryCode; + + const parsedPhoneNumber = parsePhoneNumber( + phoneNumber, + phoneNumberCountryCode + ); + + if (toFormat === 'e164') { + return parsedPhoneNumber.format('E.164'); + } else if (toFormat === 'international') { + return parsedPhoneNumber.formatInternational(); + } else if (toFormat === 'national') { + return parsedPhoneNumber.formatNational(); + } +}; + +export default formatPhoneNumber; diff --git a/packages/backend/src/apps/formatter/common/phone-number-country-codes.ts b/packages/backend/src/apps/formatter/common/phone-number-country-codes.ts new file mode 100644 index 00000000..f4512f8f --- /dev/null +++ b/packages/backend/src/apps/formatter/common/phone-number-country-codes.ts @@ -0,0 +1,249 @@ +const phoneNumberCountryCodes = [ + { label: 'Ascension Island', value: 'AC' }, + { label: 'Andorra', value: 'AD' }, + { label: 'United Arab Emirates', value: 'AE' }, + { label: 'Afghanistan', value: 'AF' }, + { label: 'Antigua & Barbuda', value: 'AG' }, + { label: 'Anguilla', value: 'AI' }, + { label: 'Albania', value: 'AL' }, + { label: 'Armenia', value: 'AM' }, + { label: 'Angola', value: 'AO' }, + { label: 'Argentina', value: 'AR' }, + { label: 'American Samoa', value: 'AS' }, + { label: 'Austria', value: 'AT' }, + { label: 'Australia', value: 'AU' }, + { label: 'Aruba', value: 'AW' }, + { label: 'Åland Islands', value: 'AX' }, + { label: 'Azerbaijan', value: 'AZ' }, + { label: 'Bosnia & Herzegovina', value: 'BA' }, + { label: 'Barbados', value: 'BB' }, + { label: 'Bangladesh', value: 'BD' }, + { label: 'Belgium', value: 'BE' }, + { label: 'Burkina Faso', value: 'BF' }, + { label: 'Bulgaria', value: 'BG' }, + { label: 'Bahrain', value: 'BH' }, + { label: 'Burundi', value: 'BI' }, + { label: 'Benin', value: 'BJ' }, + { label: 'St. Barthélemy', value: 'BL' }, + { label: 'Bermuda', value: 'BM' }, + { label: 'Brunei', value: 'BN' }, + { label: 'Bolivia', value: 'BO' }, + { label: 'Caribbean Netherlands', value: 'BQ' }, + { label: 'Brazil', value: 'BR' }, + { label: 'Bahamas', value: 'BS' }, + { label: 'Bhutan', value: 'BT' }, + { label: 'Botswana', value: 'BW' }, + { label: 'Belarus', value: 'BY' }, + { label: 'Belize', value: 'BZ' }, + { label: 'Canada', value: 'CA' }, + { label: 'Cocos (Keeling) Islands', value: 'CC' }, + { label: 'Congo - Kinshasa', value: 'CD' }, + { label: 'Central African Republic', value: 'CF' }, + { label: 'Congo - Brazzaville', value: 'CG' }, + { label: 'Switzerland', value: 'CH' }, + { label: 'Côte d’Ivoire', value: 'CI' }, + { label: 'Cook Islands', value: 'CK' }, + { label: 'Chile', value: 'CL' }, + { label: 'Cameroon', value: 'CM' }, + { label: 'China', value: 'CN' }, + { label: 'Colombia', value: 'CO' }, + { label: 'Costa Rica', value: 'CR' }, + { label: 'Cuba', value: 'CU' }, + { label: 'Cape Verde', value: 'CV' }, + { label: 'Curaçao', value: 'CW' }, + { label: 'Christmas Island', value: 'CX' }, + { label: 'Cyprus', value: 'CY' }, + { label: 'Czechia', value: 'CZ' }, + { label: 'Germany', value: 'DE' }, + { label: 'Djibouti', value: 'DJ' }, + { label: 'Denmark', value: 'DK' }, + { label: 'Dominica', value: 'DM' }, + { label: 'Dominican Republic', value: 'DO' }, + { label: 'Algeria', value: 'DZ' }, + { label: 'Ecuador', value: 'EC' }, + { label: 'Estonia', value: 'EE' }, + { label: 'Egypt', value: 'EG' }, + { label: 'Western Sahara', value: 'EH' }, + { label: 'Eritrea', value: 'ER' }, + { label: 'Spain', value: 'ES' }, + { label: 'Ethiopia', value: 'ET' }, + { label: 'Finland', value: 'FI' }, + { label: 'Fiji', value: 'FJ' }, + { label: 'Falkland Islands (Islas Malvinas)', value: 'FK' }, + { label: 'Micronesia', value: 'FM' }, + { label: 'Faroe Islands', value: 'FO' }, + { label: 'France', value: 'FR' }, + { label: 'Gabon', value: 'GA' }, + { label: 'United Kingdom', value: 'GB' }, + { label: 'Grenada', value: 'GD' }, + { label: 'Georgia', value: 'GE' }, + { label: 'French Guiana', value: 'GF' }, + { label: 'Guernsey', value: 'GG' }, + { label: 'Ghana', value: 'GH' }, + { label: 'Gibraltar', value: 'GI' }, + { label: 'Greenland', value: 'GL' }, + { label: 'Gambia', value: 'GM' }, + { label: 'Guinea', value: 'GN' }, + { label: 'Guadeloupe', value: 'GP' }, + { label: 'Equatorial Guinea', value: 'GQ' }, + { label: 'Greece', value: 'GR' }, + { label: 'Guatemala', value: 'GT' }, + { label: 'Guam', value: 'GU' }, + { label: 'Guinea-Bissau', value: 'GW' }, + { label: 'Guyana', value: 'GY' }, + { label: 'Hong Kong', value: 'HK' }, + { label: 'Honduras', value: 'HN' }, + { label: 'Croatia', value: 'HR' }, + { label: 'Haiti', value: 'HT' }, + { label: 'Hungary', value: 'HU' }, + { label: 'Indonesia', value: 'ID' }, + { label: 'Ireland', value: 'IE' }, + { label: 'Israel', value: 'IL' }, + { label: 'Isle of Man', value: 'IM' }, + { label: 'India', value: 'IN' }, + { label: 'British Indian Ocean Territory', value: 'IO' }, + { label: 'Iraq', value: 'IQ' }, + { label: 'Iran', value: 'IR' }, + { label: 'Iceland', value: 'IS' }, + { label: 'Italy', value: 'IT' }, + { label: 'Jersey', value: 'JE' }, + { label: 'Jamaica', value: 'JM' }, + { label: 'Jordan', value: 'JO' }, + { label: 'Japan', value: 'JP' }, + { label: 'Kenya', value: 'KE' }, + { label: 'Kyrgyzstan', value: 'KG' }, + { label: 'Cambodia', value: 'KH' }, + { label: 'Kiribati', value: 'KI' }, + { label: 'Comoros', value: 'KM' }, + { label: 'St. Kitts & Nevis', value: 'KN' }, + { label: 'North Korea', value: 'KP' }, + { label: 'South Korea', value: 'KR' }, + { label: 'Kuwait', value: 'KW' }, + { label: 'Cayman Islands', value: 'KY' }, + { label: 'Kazakhstan', value: 'KZ' }, + { label: 'Laos', value: 'LA' }, + { label: 'Lebanon', value: 'LB' }, + { label: 'St. Lucia', value: 'LC' }, + { label: 'Liechtenstein', value: 'LI' }, + { label: 'Sri Lanka', value: 'LK' }, + { label: 'Liberia', value: 'LR' }, + { label: 'Lesotho', value: 'LS' }, + { label: 'Lithuania', value: 'LT' }, + { label: 'Luxembourg', value: 'LU' }, + { label: 'Latvia', value: 'LV' }, + { label: 'Libya', value: 'LY' }, + { label: 'Morocco', value: 'MA' }, + { label: 'Monaco', value: 'MC' }, + { label: 'Moldova', value: 'MD' }, + { label: 'Montenegro', value: 'ME' }, + { label: 'St. Martin', value: 'MF' }, + { label: 'Madagascar', value: 'MG' }, + { label: 'Marshall Islands', value: 'MH' }, + { label: 'North Macedonia', value: 'MK' }, + { label: 'Mali', value: 'ML' }, + { label: 'Myanmar (Burma)', value: 'MM' }, + { label: 'Mongolia', value: 'MN' }, + { label: 'Macao', value: 'MO' }, + { label: 'Northern Mariana Islands', value: 'MP' }, + { label: 'Martinique', value: 'MQ' }, + { label: 'Mauritania', value: 'MR' }, + { label: 'Montserrat', value: 'MS' }, + { label: 'Malta', value: 'MT' }, + { label: 'Mauritius', value: 'MU' }, + { label: 'Maldives', value: 'MV' }, + { label: 'Malawi', value: 'MW' }, + { label: 'Mexico', value: 'MX' }, + { label: 'Malaysia', value: 'MY' }, + { label: 'Mozambique', value: 'MZ' }, + { label: 'Namibia', value: 'NA' }, + { label: 'New Caledonia', value: 'NC' }, + { label: 'Niger', value: 'NE' }, + { label: 'Norfolk Island', value: 'NF' }, + { label: 'Nigeria', value: 'NG' }, + { label: 'Nicaragua', value: 'NI' }, + { label: 'Netherlands', value: 'NL' }, + { label: 'Norway', value: 'NO' }, + { label: 'Nepal', value: 'NP' }, + { label: 'Nauru', value: 'NR' }, + { label: 'Niue', value: 'NU' }, + { label: 'New Zealand', value: 'NZ' }, + { label: 'Oman', value: 'OM' }, + { label: 'Panama', value: 'PA' }, + { label: 'Peru', value: 'PE' }, + { label: 'French Polynesia', value: 'PF' }, + { label: 'Papua New Guinea', value: 'PG' }, + { label: 'Philippines', value: 'PH' }, + { label: 'Pakistan', value: 'PK' }, + { label: 'Poland', value: 'PL' }, + { label: 'St. Pierre & Miquelon', value: 'PM' }, + { label: 'Puerto Rico', value: 'PR' }, + { label: 'Palestine', value: 'PS' }, + { label: 'Portugal', value: 'PT' }, + { label: 'Palau', value: 'PW' }, + { label: 'Paraguay', value: 'PY' }, + { label: 'Qatar', value: 'QA' }, + { label: 'Réunion', value: 'RE' }, + { label: 'Romania', value: 'RO' }, + { label: 'Serbia', value: 'RS' }, + { label: 'Russia', value: 'RU' }, + { label: 'Rwanda', value: 'RW' }, + { label: 'Saudi Arabia', value: 'SA' }, + { label: 'Solomon Islands', value: 'SB' }, + { label: 'Seychelles', value: 'SC' }, + { label: 'Sudan', value: 'SD' }, + { label: 'Sweden', value: 'SE' }, + { label: 'Singapore', value: 'SG' }, + { label: 'St. Helena', value: 'SH' }, + { label: 'Slovenia', value: 'SI' }, + { label: 'Svalbard & Jan Mayen', value: 'SJ' }, + { label: 'Slovakia', value: 'SK' }, + { label: 'Sierra Leone', value: 'SL' }, + { label: 'San Marino', value: 'SM' }, + { label: 'Senegal', value: 'SN' }, + { label: 'Somalia', value: 'SO' }, + { label: 'Suriname', value: 'SR' }, + { label: 'South Sudan', value: 'SS' }, + { label: 'São Tomé & Príncipe', value: 'ST' }, + { label: 'El Salvador', value: 'SV' }, + { label: 'Sint Maarten', value: 'SX' }, + { label: 'Syria', value: 'SY' }, + { label: 'Eswatini', value: 'SZ' }, + { label: 'Tristan da Cunha', value: 'TA' }, + { label: 'Turks & Caicos Islands', value: 'TC' }, + { label: 'Chad', value: 'TD' }, + { label: 'Togo', value: 'TG' }, + { label: 'Thailand', value: 'TH' }, + { label: 'Tajikistan', value: 'TJ' }, + { label: 'Tokelau', value: 'TK' }, + { label: 'Timor-Leste', value: 'TL' }, + { label: 'Turkmenistan', value: 'TM' }, + { label: 'Tunisia', value: 'TN' }, + { label: 'Tonga', value: 'TO' }, + { label: 'Türkiye', value: 'TR' }, + { label: 'Trinidad & Tobago', value: 'TT' }, + { label: 'Tuvalu', value: 'TV' }, + { label: 'Taiwan', value: 'TW' }, + { label: 'Tanzania', value: 'TZ' }, + { label: 'Ukraine', value: 'UA' }, + { label: 'Uganda', value: 'UG' }, + { label: 'United States', value: 'US' }, + { label: 'Uruguay', value: 'UY' }, + { label: 'Uzbekistan', value: 'UZ' }, + { label: 'Vatican City', value: 'VA' }, + { label: 'St. Vincent & Grenadines', value: 'VC' }, + { label: 'Venezuela', value: 'VE' }, + { label: 'British Virgin Islands', value: 'VG' }, + { label: 'U.S. Virgin Islands', value: 'VI' }, + { label: 'Vietnam', value: 'VN' }, + { label: 'Vanuatu', value: 'VU' }, + { label: 'Wallis & Futuna', value: 'WF' }, + { label: 'Samoa', value: 'WS' }, + { label: 'Kosovo', value: 'XK' }, + { label: 'Yemen', value: 'YE' }, + { label: 'Mayotte', value: 'YT' }, + { label: 'South Africa', value: 'ZA' }, + { label: 'Zambia', value: 'ZM' }, + { label: 'Zimbabwe', value: 'ZW' }, +]; + +export default phoneNumberCountryCodes; diff --git a/packages/backend/src/apps/formatter/dynamic-fields/list-transform-options/index.ts b/packages/backend/src/apps/formatter/dynamic-fields/list-transform-options/index.ts index 60c3f50c..3d143f33 100644 --- a/packages/backend/src/apps/formatter/dynamic-fields/list-transform-options/index.ts +++ b/packages/backend/src/apps/formatter/dynamic-fields/list-transform-options/index.ts @@ -12,6 +12,7 @@ import useDefaultValue from './text/use-default-value'; import performMathOperation from './numbers/perform-math-operation'; import randomNumber from './numbers/random-number'; import formatNumber from './numbers/format-number'; +import formatPhoneNumber from './numbers/format-phone-number'; import formatDateTime from './date-time/format-date-time'; const options: IJSONObject = { @@ -28,6 +29,7 @@ const options: IJSONObject = { performMathOperation, randomNumber, formatNumber, + formatPhoneNumber, formatDateTime, }; diff --git a/packages/backend/src/apps/formatter/dynamic-fields/list-transform-options/numbers/format-phone-number.ts b/packages/backend/src/apps/formatter/dynamic-fields/list-transform-options/numbers/format-phone-number.ts new file mode 100644 index 00000000..fa48d1b1 --- /dev/null +++ b/packages/backend/src/apps/formatter/dynamic-fields/list-transform-options/numbers/format-phone-number.ts @@ -0,0 +1,36 @@ +import phoneNumberCountryCodes from '../../../common/phone-number-country-codes'; + +const formatPhoneNumber = [ + { + label: 'Phone Number', + key: 'phoneNumber', + type: 'string' as const, + required: true, + description: 'The phone number you want to format.', + variables: true, + }, + { + label: 'To Format', + key: 'toFormat', + type: 'dropdown' as const, + required: true, + description: 'The format you want to convert the number to.', + variables: true, + options: [ + { label: '+491632223344 (E164)', value: 'e164' }, + { label: '+49 163 2223344 (International)', value: 'international' }, + { label: '0163 2223344 (National)', value: 'national' }, + ], + }, + { + label: 'Phone Number Country Code', + key: 'phoneNumberCountryCode', + type: 'dropdown' as const, + required: true, + description: 'The country code of the phone number. The default is US.', + variables: true, + options: phoneNumberCountryCodes, + }, +]; + +export default formatPhoneNumber; diff --git a/yarn.lock b/yarn.lock index 677a497f..78e4f476 100644 --- a/yarn.lock +++ b/yarn.lock @@ -12206,6 +12206,11 @@ libnpmpublish@^4.0.0: semver "^7.1.3" ssri "^8.0.1" +libphonenumber-js@^1.10.48: + version "1.10.48" + resolved "https://registry.yarnpkg.com/libphonenumber-js/-/libphonenumber-js-1.10.48.tgz#3c426b4aa21dfe3210bfbda47d208acffa3631bf" + integrity sha512-Vvcgt4+o8+puIBJZLdMshPYx9nRN3/kTT7HPtOyfYrSQuN9PGBF1KUv0g07fjNzt4E4GuA7FnsLb+WeAMzyRQg== + lilconfig@^2.0.3, lilconfig@^2.0.4: version "2.0.4" resolved "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.4.tgz"