From d75975bdeca38a92dd08b2db35ae40869ff30e8b Mon Sep 17 00:00:00 2001 From: formsdev <136701234+formsdev@users.noreply.github.com> Date: Mon, 18 Sep 2023 18:42:05 +0530 Subject: [PATCH] Fix phone input (#201) * Fix phone input * remove extra * fix factory * Fix phone input * Validate phone number rule * Prefill support for country only * Fix phone input error * fix tests --------- Co-authored-by: Julien Nahum --- app/Http/Requests/AnswerFormRequest.php | 2 +- app/Rules/ValidPhoneInputRule.php | 15 +- composer.json | 3 +- composer.lock | 224 ++++++++++++++---- package-lock.json | 11 + package.json | 1 + resources/js/components/forms/PhoneInput.vue | 78 ++++-- .../components/open/forms/OpenFormField.vue | 5 +- .../forms/fields/components/FieldOptions.vue | 9 +- tests/Helpers/FormSubmissionDataFactory.php | 2 +- 10 files changed, 256 insertions(+), 94 deletions(-) diff --git a/app/Http/Requests/AnswerFormRequest.php b/app/Http/Requests/AnswerFormRequest.php index 59617da96..d7c02441c 100644 --- a/app/Http/Requests/AnswerFormRequest.php +++ b/app/Http/Requests/AnswerFormRequest.php @@ -190,7 +190,7 @@ private function getPropertyRules($property): array } return $this->getRulesForDate($property); case 'phone_number': - return [new ValidPhoneInputRule]; + return ['string', 'min:6', new ValidPhoneInputRule]; default: return []; } diff --git a/app/Rules/ValidPhoneInputRule.php b/app/Rules/ValidPhoneInputRule.php index 612bd9cf0..9e6c728d4 100644 --- a/app/Rules/ValidPhoneInputRule.php +++ b/app/Rules/ValidPhoneInputRule.php @@ -9,21 +9,16 @@ class ValidPhoneInputRule implements Rule { public function passes($attribute, $value) { - if (!is_string($value)) { + if (!is_string($value) || !Str::startsWith($value, '+')) { return false; } - if (!Str::startsWith($value, '+')) { - return false; - } - $parts = explode(' ', $value); - if (count($parts) < 2) { - return false; - } - return strlen($parts[1]) >= 5; + + $phoneUtil = \libphonenumber\PhoneNumberUtil::getInstance(); + return $phoneUtil->isValidNumber($phoneUtil->parse($value)); } public function message() { - return 'The :attribute must be a string that starts with a "+" character and must be at least 5 digits long.'; + return 'The :attribute is invalid.'; } } \ No newline at end of file diff --git a/composer.json b/composer.json index a9f539118..84ed6be16 100644 --- a/composer.json +++ b/composer.json @@ -18,11 +18,12 @@ } ], "require": { - "php": "^8.0", + "php": "^8.2", "ext-json": "*", "aws/aws-sdk-php": "^3.183", "doctrine/dbal": "^3.4", "fruitcake/laravel-cors": "^2.0", + "giggsey/libphonenumber-for-php": "^8.13", "guzzlehttp/guzzle": "^7.0.1", "jhumanj/laravel-model-stats": "^0.4.0", "laravel/cashier": "^13.4", diff --git a/composer.lock b/composer.lock index fdda0045f..9a5177fc2 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "9c7ea2e339a9d3c8ecae0a3ae7cc2883", + "content-hash": "563d3a4c1489aa79b9fd5b87309eb28b", "packages": [ { "name": "asm89/stack-cors", @@ -118,16 +118,16 @@ }, { "name": "aws/aws-sdk-php", - "version": "3.281.2", + "version": "3.281.8", "source": { "type": "git", "url": "https://github.com/aws/aws-sdk-php.git", - "reference": "5b33690b4ebc32a75164be0d6805d393a3ca9df9" + "reference": "eb349b9f31502a05c70362f57913b9fed6b65b1f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/5b33690b4ebc32a75164be0d6805d393a3ca9df9", - "reference": "5b33690b4ebc32a75164be0d6805d393a3ca9df9", + "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/eb349b9f31502a05c70362f57913b9fed6b65b1f", + "reference": "eb349b9f31502a05c70362f57913b9fed6b65b1f", "shasum": "" }, "require": { @@ -207,9 +207,9 @@ "support": { "forum": "https://forums.aws.amazon.com/forum.jspa?forumID=80", "issues": "https://github.com/aws/aws-sdk-php/issues", - "source": "https://github.com/aws/aws-sdk-php/tree/3.281.2" + "source": "https://github.com/aws/aws-sdk-php/tree/3.281.8" }, - "time": "2023-09-07T18:06:59+00:00" + "time": "2023-09-15T18:34:59+00:00" }, { "name": "brick/math", @@ -1402,6 +1402,132 @@ ], "time": "2022-02-20T15:07:15+00:00" }, + { + "name": "giggsey/libphonenumber-for-php", + "version": "8.13.20", + "source": { + "type": "git", + "url": "https://github.com/giggsey/libphonenumber-for-php.git", + "reference": "c8da9366ab46cbc83f9fd0e7b0ac12f8ddbb721a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/giggsey/libphonenumber-for-php/zipball/c8da9366ab46cbc83f9fd0e7b0ac12f8ddbb721a", + "reference": "c8da9366ab46cbc83f9fd0e7b0ac12f8ddbb721a", + "shasum": "" + }, + "require": { + "giggsey/locale": "^1.7|^2.0", + "php": ">=5.3.2", + "symfony/polyfill-mbstring": "^1.17" + }, + "require-dev": { + "pear/pear-core-minimal": "^1.9", + "pear/pear_exception": "^1.0", + "pear/versioncontrol_git": "^0.5", + "phing/phing": "^2.7", + "php-coveralls/php-coveralls": "^1.0|^2.0", + "symfony/console": "^2.8|^3.0|^v4.4|^v5.2", + "symfony/phpunit-bridge": "^4.2 || ^5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "8.x-dev" + } + }, + "autoload": { + "psr-4": { + "libphonenumber\\": "src/" + }, + "exclude-from-classmap": [ + "/src/data/", + "/src/carrier/data/", + "/src/geocoding/data/", + "/src/timezone/data/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Joshua Gigg", + "email": "giggsey@gmail.com", + "homepage": "https://giggsey.com/" + } + ], + "description": "PHP Port of Google's libphonenumber", + "homepage": "https://github.com/giggsey/libphonenumber-for-php", + "keywords": [ + "geocoding", + "geolocation", + "libphonenumber", + "mobile", + "phonenumber", + "validation" + ], + "support": { + "issues": "https://github.com/giggsey/libphonenumber-for-php/issues", + "source": "https://github.com/giggsey/libphonenumber-for-php" + }, + "time": "2023-09-07T06:33:03+00:00" + }, + { + "name": "giggsey/locale", + "version": "2.4", + "source": { + "type": "git", + "url": "https://github.com/giggsey/Locale.git", + "reference": "a6b33dfc9e8949b7e28133c4628b29cd9f1850bb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/giggsey/Locale/zipball/a6b33dfc9e8949b7e28133c4628b29cd9f1850bb", + "reference": "a6b33dfc9e8949b7e28133c4628b29cd9f1850bb", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "require-dev": { + "ext-json": "*", + "pear/pear-core-minimal": "^1.9", + "pear/pear_exception": "^1.0", + "pear/versioncontrol_git": "^0.5", + "phing/phing": "^2.7", + "php-coveralls/php-coveralls": "^2.0", + "phpunit/phpunit": "^8.5|^9.5", + "symfony/console": "^5.0|^6.0", + "symfony/filesystem": "^5.0|^6.0", + "symfony/finder": "^5.0|^6.0", + "symfony/process": "^5.0|^6.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Giggsey\\Locale\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Joshua Gigg", + "email": "giggsey@gmail.com", + "homepage": "https://giggsey.com/" + } + ], + "description": "Locale functions required by libphonenumber-for-php", + "support": { + "issues": "https://github.com/giggsey/Locale/issues", + "source": "https://github.com/giggsey/Locale/tree/2.4" + }, + "time": "2023-04-13T07:40:58+00:00" + }, { "name": "graham-campbell/manager", "version": "v4.7.0", @@ -2613,16 +2739,16 @@ }, { "name": "laravel/horizon", - "version": "v5.20.0", + "version": "v5.20.1", "source": { "type": "git", "url": "https://github.com/laravel/horizon.git", - "reference": "2fc2ba769a592e92d170d85e346a5631b3a90ed5" + "reference": "6eaf4efebbfc9955be7504b5b6f045d9d10e86af" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/horizon/zipball/2fc2ba769a592e92d170d85e346a5631b3a90ed5", - "reference": "2fc2ba769a592e92d170d85e346a5631b3a90ed5", + "url": "https://api.github.com/repos/laravel/horizon/zipball/6eaf4efebbfc9955be7504b5b6f045d9d10e86af", + "reference": "6eaf4efebbfc9955be7504b5b6f045d9d10e86af", "shasum": "" }, "require": { @@ -2685,9 +2811,9 @@ ], "support": { "issues": "https://github.com/laravel/horizon/issues", - "source": "https://github.com/laravel/horizon/tree/v5.20.0" + "source": "https://github.com/laravel/horizon/tree/v5.20.1" }, - "time": "2023-08-30T14:04:11+00:00" + "time": "2023-09-12T11:21:57+00:00" }, { "name": "laravel/serializable-closure", @@ -2751,16 +2877,16 @@ }, { "name": "laravel/socialite", - "version": "v5.9.0", + "version": "v5.9.1", "source": { "type": "git", "url": "https://github.com/laravel/socialite.git", - "reference": "14acfa3262875f180fba51efe3c7aaa089a9ef24" + "reference": "49ecc4c907ed88c1254bae991c6b2948945645c2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/socialite/zipball/14acfa3262875f180fba51efe3c7aaa089a9ef24", - "reference": "14acfa3262875f180fba51efe3c7aaa089a9ef24", + "url": "https://api.github.com/repos/laravel/socialite/zipball/49ecc4c907ed88c1254bae991c6b2948945645c2", + "reference": "49ecc4c907ed88c1254bae991c6b2948945645c2", "shasum": "" }, "require": { @@ -2817,7 +2943,7 @@ "issues": "https://github.com/laravel/socialite/issues", "source": "https://github.com/laravel/socialite" }, - "time": "2023-09-05T15:20:21+00:00" + "time": "2023-09-07T16:13:53+00:00" }, { "name": "laravel/tinker", @@ -7526,16 +7652,16 @@ }, { "name": "spatie/laravel-webhook-server", - "version": "3.4.3", + "version": "3.5.0", "source": { "type": "git", "url": "https://github.com/spatie/laravel-webhook-server.git", - "reference": "9d4506fb340db7750bcb088fe0253bdd30beaa32" + "reference": "7aec82720bf06c9c9a9221a1f2d5175492a4c3fe" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/laravel-webhook-server/zipball/9d4506fb340db7750bcb088fe0253bdd30beaa32", - "reference": "9d4506fb340db7750bcb088fe0253bdd30beaa32", + "url": "https://api.github.com/repos/spatie/laravel-webhook-server/zipball/7aec82720bf06c9c9a9221a1f2d5175492a4c3fe", + "reference": "7aec82720bf06c9c9a9221a1f2d5175492a4c3fe", "shasum": "" }, "require": { @@ -7588,7 +7714,7 @@ "webhook" ], "support": { - "source": "https://github.com/spatie/laravel-webhook-server/tree/3.4.3" + "source": "https://github.com/spatie/laravel-webhook-server/tree/3.5.0" }, "funding": [ { @@ -7596,7 +7722,7 @@ "type": "custom" } ], - "time": "2023-03-17T09:01:16+00:00" + "time": "2023-09-12T09:01:43+00:00" }, { "name": "spatie/robots-txt", @@ -11920,16 +12046,16 @@ }, { "name": "laravel/sail", - "version": "v1.24.1", + "version": "v1.25.0", "source": { "type": "git", "url": "https://github.com/laravel/sail.git", - "reference": "3a373bb2845623aed2017c672dc61c84ae974890" + "reference": "e81a7bd7ac1a745ccb25572830fecf74a89bb48a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/sail/zipball/3a373bb2845623aed2017c672dc61c84ae974890", - "reference": "3a373bb2845623aed2017c672dc61c84ae974890", + "url": "https://api.github.com/repos/laravel/sail/zipball/e81a7bd7ac1a745ccb25572830fecf74a89bb48a", + "reference": "e81a7bd7ac1a745ccb25572830fecf74a89bb48a", "shasum": "" }, "require": { @@ -11981,7 +12107,7 @@ "issues": "https://github.com/laravel/sail/issues", "source": "https://github.com/laravel/sail" }, - "time": "2023-09-01T14:05:17+00:00" + "time": "2023-09-11T17:37:09+00:00" }, { "name": "mockery/mockery", @@ -12952,16 +13078,16 @@ }, { "name": "phpunit/php-code-coverage", - "version": "9.2.27", + "version": "9.2.28", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "b0a88255cb70d52653d80c890bd7f38740ea50d1" + "reference": "7134a5ccaaf0f1c92a4f5501a6c9f98ac4dcc0ef" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/b0a88255cb70d52653d80c890bd7f38740ea50d1", - "reference": "b0a88255cb70d52653d80c890bd7f38740ea50d1", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/7134a5ccaaf0f1c92a4f5501a6c9f98ac4dcc0ef", + "reference": "7134a5ccaaf0f1c92a4f5501a6c9f98ac4dcc0ef", "shasum": "" }, "require": { @@ -13018,7 +13144,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.27" + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.28" }, "funding": [ { @@ -13026,7 +13152,7 @@ "type": "github" } ], - "time": "2023-07-26T13:44:30+00:00" + "time": "2023-09-12T14:36:20+00:00" }, { "name": "phpunit/php-file-iterator", @@ -13271,16 +13397,16 @@ }, { "name": "phpunit/phpunit", - "version": "9.6.11", + "version": "9.6.12", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "810500e92855eba8a7a5319ae913be2da6f957b0" + "reference": "a122c2ebd469b751d774aa0f613dc0d67697653f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/810500e92855eba8a7a5319ae913be2da6f957b0", - "reference": "810500e92855eba8a7a5319ae913be2da6f957b0", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/a122c2ebd469b751d774aa0f613dc0d67697653f", + "reference": "a122c2ebd469b751d774aa0f613dc0d67697653f", "shasum": "" }, "require": { @@ -13295,7 +13421,7 @@ "phar-io/manifest": "^2.0.3", "phar-io/version": "^3.0.2", "php": ">=7.3", - "phpunit/php-code-coverage": "^9.2.13", + "phpunit/php-code-coverage": "^9.2.28", "phpunit/php-file-iterator": "^3.0.5", "phpunit/php-invoker": "^3.1.1", "phpunit/php-text-template": "^2.0.3", @@ -13354,7 +13480,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.11" + "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.12" }, "funding": [ { @@ -13370,7 +13496,7 @@ "type": "tidelift" } ], - "time": "2023-08-19T07:10:56+00:00" + "time": "2023-09-12T14:39:31+00:00" }, { "name": "pimple/pimple", @@ -14831,16 +14957,16 @@ }, { "name": "spatie/ray", - "version": "1.37.7", + "version": "1.38.0", "source": { "type": "git", "url": "https://github.com/spatie/ray.git", - "reference": "31e07c3eadc736e180810efff72a0ff882dcddb6" + "reference": "b77656d7475875375fcbccf28441eedf1596acd5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/ray/zipball/31e07c3eadc736e180810efff72a0ff882dcddb6", - "reference": "31e07c3eadc736e180810efff72a0ff882dcddb6", + "url": "https://api.github.com/repos/spatie/ray/zipball/b77656d7475875375fcbccf28441eedf1596acd5", + "reference": "b77656d7475875375fcbccf28441eedf1596acd5", "shasum": "" }, "require": { @@ -14891,7 +15017,7 @@ ], "support": { "issues": "https://github.com/spatie/ray/issues", - "source": "https://github.com/spatie/ray/tree/1.37.7" + "source": "https://github.com/spatie/ray/tree/1.38.0" }, "funding": [ { @@ -14903,7 +15029,7 @@ "type": "other" } ], - "time": "2023-09-08T12:08:25+00:00" + "time": "2023-09-12T10:41:10+00:00" }, { "name": "symfony/polyfill-iconv", @@ -15314,7 +15440,7 @@ "prefer-stable": true, "prefer-lowest": false, "platform": { - "php": "^8.0", + "php": "^8.2", "ext-json": "*" }, "platform-dev": [], diff --git a/package-lock.json b/package-lock.json index 4e959487c..aa32126d4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,6 +16,7 @@ "fuse.js": "^6.4.6", "js-cookie": "^2.2.1", "js-sha256": "^0.9.0", + "libphonenumber-js": "^1.10.44", "portal-vue": "^2.1.7", "prismjs": "^1.24.1", "qrcode": "^1.5.1", @@ -8142,6 +8143,11 @@ "node": ">= 0.8.0" } }, + "node_modules/libphonenumber-js": { + "version": "1.10.44", + "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.10.44.tgz", + "integrity": "sha512-svlRdNBI5WgBjRC20GrCfbFiclbF0Cx+sCcQob/C1r57nsoq0xg8r65QbTyVyweQIlB33P+Uahyho6EMYgcOyQ==" + }, "node_modules/lilconfig": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.6.tgz", @@ -19111,6 +19117,11 @@ "type-check": "~0.4.0" } }, + "libphonenumber-js": { + "version": "1.10.44", + "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.10.44.tgz", + "integrity": "sha512-svlRdNBI5WgBjRC20GrCfbFiclbF0Cx+sCcQob/C1r57nsoq0xg8r65QbTyVyweQIlB33P+Uahyho6EMYgcOyQ==" + }, "lilconfig": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.6.tgz", diff --git a/package.json b/package.json index 861bd87fd..07da23d2b 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "fuse.js": "^6.4.6", "js-cookie": "^2.2.1", "js-sha256": "^0.9.0", + "libphonenumber-js": "^1.10.44", "portal-vue": "^2.1.7", "prismjs": "^1.24.1", "qrcode": "^1.5.1", diff --git a/resources/js/components/forms/PhoneInput.vue b/resources/js/components/forms/PhoneInput.vue index c189032b1..9809af07f 100644 --- a/resources/js/components/forms/PhoneInput.vue +++ b/resources/js/components/forms/PhoneInput.vue @@ -12,10 +12,12 @@ -
- +
+ -
+
+ + + +
+
@@ -42,32 +50,49 @@ import {directive as onClickaway} from 'vue-clickaway' import inputMixin from '~/mixins/forms/input.js' import countryCodes from '../../../data/country_codes.json' import CountryFlag from 'vue-country-flag' -import VSelect from './components/VSelect.vue' +import parsePhoneNumber from 'libphonenumber-js' export default { phone: 'PhoneInput', - components: { - CountryFlag, VSelect - }, + components: {CountryFlag}, directives: { onClickaway: onClickaway }, mixins: [inputMixin], + props: { + canOnlyCountry: {type: Boolean, default: false} + }, data() { return { - selectedCountryCode: countryCodes[234], + selectedCountryCode: this.getCountryBy('US'), // Default US countries: countryCodes, - isOpen: false, - inputVal: '' + inputVal: null } }, + + mounted() { + if (this.compVal) { + const phoneObj = parsePhoneNumber(this.compVal) + if (phoneObj !== undefined && phoneObj) { + if (phoneObj.country !== undefined && phoneObj.country) { + this.selectedCountryCode = this.getCountryBy(phoneObj.country) + } + this.inputVal = phoneObj.nationalNumber + } else if (this.compVal) { + this.selectedCountryCode = this.getCountryBy(this.compVal, 'dial_code') + } + } + }, + watch: { - inputVal(newVal, oldVal) { - if (newVal.startsWith('0')) { - newVal = newVal.replace(/^0+/, '') + inputVal: { + handler(val) { + if (val && val.startsWith('0')) { + val = val.substring(1) + } + this.compVal = (val) ? this.selectedCountryCode.dial_code + val : null } - this.compVal = this.selectedCountryCode.dial_code + ' ' + newVal }, selectedCountryCode(newVal, oldVal) { if (this.compVal) { @@ -76,18 +101,19 @@ export default { } }, methods: { - onCountryChange(country) { - this.selectedCountryCode = country - this.closeDropdown() - }, - closeDropdown() { - this.isOpen = false + getCountryBy(code, type = 'code') { + return countryCodes.find((item) => { + return item[type] === code + }) }, onInput(event) { - const input = event.target.value - const digitsOnly = input.replace(/[^0-9]/g, '') - this.inputVal = digitsOnly + this.inputVal = event.target.value.replace(/[^0-9]/g, '') }, + onChangeCountryCode() { + if (this.canOnlyCountry && (this.inputVal === null || this.inputVal === '' || !this.inputVal)) { + this.compVal = this.selectedCountryCode.dial_code + } + } } } diff --git a/resources/js/components/open/forms/OpenFormField.vue b/resources/js/components/open/forms/OpenFormField.vue index ad8200604..156e911b0 100644 --- a/resources/js/components/open/forms/OpenFormField.vue +++ b/resources/js/components/open/forms/OpenFormField.vue @@ -69,14 +69,11 @@