Phone input prefill country issue fixed (#213)

* fix prefill phone issue

* js phone parse fixes

* revert last change

* fix phone UI

* Code optimize

---------

Co-authored-by: Julien Nahum <julien@nahum.net>
This commit is contained in:
formsdev 2023-10-03 21:20:46 +05:30 committed by GitHub
parent 54f92f844f
commit f7ecd6f233
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 310 additions and 38 deletions

View File

@ -220,14 +220,14 @@ class AnswerFormRequest extends FormRequest
protected function prepareForValidation() protected function prepareForValidation()
{ {
// Escape all '\' in select options
$receivedData = $this->toArray(); $receivedData = $this->toArray();
$mergeData = []; $mergeData = [];
collect($this->form->properties)->filter(function ($property) { $countryCodeMapper = json_decode(file_get_contents(resource_path('data/country_code_mapper.json')), true);
return in_array($property['type'], ['select', 'multi_select']); collect($this->form->properties)->each(function ($property) use ($countryCodeMapper, $receivedData, &$mergeData) {
})->each(function ($property) use ($receivedData, &$mergeData) {
$receivedValue = $receivedData[$property['id']] ?? null; $receivedValue = $receivedData[$property['id']] ?? null;
if (!is_null($receivedValue)) {
// Escape all '\' in select options
if(in_array($property['type'], ['select', 'multi_select']) && !is_null($receivedValue)){
if (is_array($receivedValue)) { if (is_array($receivedValue)) {
$mergeData[$property['id']] = collect($receivedValue)->map(function ($value) { $mergeData[$property['id']] = collect($receivedValue)->map(function ($value) {
$value = Str::of($value); $value = Str::of($value);
@ -244,6 +244,10 @@ class AnswerFormRequest extends FormRequest
)->toString(); )->toString();
} }
} }
if($property['type'] === 'phone_number' && (!isset($property['use_simple_text_input']) || !$property['use_simple_text_input']) && $receivedValue && in_array($receivedValue, $countryCodeMapper)){
$mergeData[$property['id']] = null;
}
}); });
$this->merge($mergeData); $this->merge($mergeData);

View File

@ -131,6 +131,11 @@ class StoreFormSubmissionJob implements ShouldQueue
if($this->form->is_pro && $field['type'] == 'signature') { if($this->form->is_pro && $field['type'] == 'signature') {
$finalData[$field['id']] = $this->storeSignature($answerValue); $finalData[$field['id']] = $this->storeSignature($answerValue);
} }
// For Phone
if($field['type'] == 'phone_number' && $answerValue && ctype_alpha(substr($answerValue, 0, 2)) && (!isset($field['use_simple_text_input']) || !$field['use_simple_text_input'])) {
$finalData[$field['id']] = substr($answerValue, 2);
}
} }
return $finalData; return $finalData;

View File

@ -12,10 +12,13 @@ class ValidPhoneInputRule implements Rule
public function passes($attribute, $value) public function passes($attribute, $value)
{ {
if (!is_string($value) || !Str::startsWith($value, '+')) { if (!is_string($value) || !$value) {
return false; return false;
} }
try { try {
if(ctype_alpha(substr($value, 0, 2))){ // First 2 will be country code
$value = substr($value, 2);
}
$phoneUtil = \libphonenumber\PhoneNumberUtil::getInstance(); $phoneUtil = \libphonenumber\PhoneNumberUtil::getInstance();
$phone = $phoneUtil->parse($value); $phone = $phoneUtil->parse($value);
$this->reason = $phoneUtil->isPossibleNumberWithReason($phone); $this->reason = $phoneUtil->isPossibleNumberWithReason($phone);

View File

@ -0,0 +1,248 @@
[
"AF+93",
"AX+358",
"AL+355",
"DZ+213",
"AS+1684",
"AD+376",
"AO+244",
"AI+1264",
"AQ+672",
"AG+1268",
"AR+54",
"AM+374",
"AW+297",
"AU+61",
"AT+43",
"AZ+994",
"BS+1242",
"BH+973",
"BD+880",
"BB+1246",
"BY+375",
"BE+32",
"BZ+501",
"BJ+229",
"BM+1441",
"BT+975",
"BO+591",
"BA+387",
"BW+267",
"BV+47",
"BR+55",
"IO+246",
"BN+673",
"BG+359",
"BF+226",
"BI+257",
"KH+855",
"CM+237",
"CA+1",
"CV+238",
"KY+345",
"CF+236",
"TD+235",
"CL+56",
"CN+86",
"CX+61",
"CC+61",
"CO+57",
"KM+269",
"CG+242",
"CD+243",
"CK+682",
"CR+506",
"CI+225",
"HR+385",
"CU+53",
"CY+357",
"CZ+420",
"DK+45",
"DJ+253",
"DM+1767",
"DO+1849",
"EC+593",
"EG+20",
"SV+503",
"GQ+240",
"ER+291",
"EE+372",
"ET+251",
"FK+500",
"FO+298",
"FJ+679",
"FI+358",
"FR+33",
"GF+594",
"PF+689",
"TF+262",
"GA+241",
"GM+220",
"GE+995",
"DE+49",
"GH+233",
"GI+350",
"GR+30",
"GL+299",
"GD+1473",
"GP+590",
"GU+1671",
"GT+502",
"GG+44",
"GN+224",
"GW+245",
"GY+592",
"HT+509",
"HM+672",
"VA+379",
"HN+504",
"HK+852",
"HU+36",
"IS+354",
"IN+91",
"ID+62",
"IR+98",
"IQ+964",
"IE+353",
"IM+44",
"IL+972",
"IT+39",
"JM+1876",
"JP+81",
"JE+44",
"JO+962",
"KZ+7",
"KE+254",
"KI+686",
"KP+850",
"KR+82",
"XK+383",
"KW+965",
"KG+996",
"LA+856",
"LV+371",
"LB+961",
"LS+266",
"LR+231",
"LY+218",
"LI+423",
"LT+370",
"LU+352",
"MO+853",
"MK+389",
"MG+261",
"MW+265",
"MY+60",
"MV+960",
"ML+223",
"MT+356",
"MH+692",
"MQ+596",
"MR+222",
"MU+230",
"YT+262",
"MX+52",
"FM+691",
"MD+373",
"MC+377",
"MN+976",
"ME+382",
"MS+1664",
"MA+212",
"MZ+258",
"MM+95",
"NA+264",
"NR+674",
"NP+977",
"NL+31",
"AN+599",
"NC+687",
"NZ+64",
"NI+505",
"NE+227",
"NG+234",
"NU+683",
"NF+672",
"MP+1670",
"NO+47",
"OM+968",
"PK+92",
"PW+680",
"PS+970",
"PA+507",
"PG+675",
"PY+595",
"PE+51",
"PH+63",
"PN+64",
"PL+48",
"PT+351",
"PR+1939",
"QA+974",
"RO+40",
"RU+7",
"RW+250",
"RE+262",
"BL+590",
"SH+290",
"KN+1869",
"LC+1758",
"MF+590",
"PM+508",
"VC+1784",
"WS+685",
"SM+378",
"ST+239",
"SA+966",
"SN+221",
"RS+381",
"SC+248",
"SL+232",
"SG+65",
"SK+421",
"SI+386",
"SB+677",
"SO+252",
"ZA+27",
"SS+211",
"GS+500",
"ES+34",
"LK+94",
"SD+249",
"SR+597",
"SJ+47",
"SZ+268",
"SE+46",
"CH+41",
"SY+963",
"TW+886",
"TJ+992",
"TZ+255",
"TH+66",
"TL+670",
"TG+228",
"TK+690",
"TO+676",
"TT+1868",
"TN+216",
"TR+90",
"TM+993",
"TC+1649",
"TV+688",
"UG+256",
"UA+380",
"AE+971",
"GB+44",
"US+1",
"UY+598",
"UZ+998",
"VU+678",
"VE+58",
"VN+84",
"VG+1284",
"VI+1340",
"WF+681",
"YE+967",
"ZM+260",
"ZW+263"
]

View File

@ -13,7 +13,7 @@
</small> </small>
</div> </div>
<div :id="id ? id : name" :name="name" :style="inputStyle" class="flex items-center"> <div :id="id ? id : name" :name="name" :style="inputStyle" class="flex items-center">
<v-select class="w-[110px]" dropdown-class="w-[350px]" input-class="rounded-r-none" :data="countries" <v-select class="w-[130px]" dropdown-class="w-[300px]" input-class="rounded-r-none" :data="countries"
v-model="selectedCountryCode" v-model="selectedCountryCode"
:has-error="hasValidation && form.errors.has(name)" :has-error="hasValidation && form.errors.has(name)"
:disabled="disabled" :searchable="true" :search-keys="['name']" :option-key="'code'" :color="color" :disabled="disabled" :searchable="true" :search-keys="['name']" :option-key="'code'" :color="color"
@ -54,64 +54,74 @@ import parsePhoneNumber from 'libphonenumber-js'
export default { export default {
phone: 'PhoneInput', phone: 'PhoneInput',
components: {CountryFlag}, components: { CountryFlag },
directives: { directives: {
onClickaway: onClickaway onClickaway: onClickaway
}, },
mixins: [inputMixin], mixins: [inputMixin],
props: { props: {
canOnlyCountry: {type: Boolean, default: false} canOnlyCountry: { type: Boolean, default: false }
}, },
data() { data () {
return { return {
selectedCountryCode: this.getCountryBy('US'), // Default US selectedCountryCode: null,
countries: countryCodes, countries: countryCodes,
inputVal: null inputVal: null
} }
}, },
mounted() { watch: {
if (this.compVal) { inputVal: {
const phoneObj = parsePhoneNumber(this.compVal) handler (val) {
if (phoneObj !== undefined && phoneObj) { if (val && val.startsWith('0')) {
if (phoneObj.country !== undefined && phoneObj.country) { val = val.substring(1)
this.selectedCountryCode = this.getCountryBy(phoneObj.country)
} }
this.inputVal = phoneObj.nationalNumber if (this.canOnlyCountry) {
} else if (this.compVal) { this.compVal = (val) ? this.selectedCountryCode.code + this.selectedCountryCode.dial_code + val : this.selectedCountryCode.code + this.selectedCountryCode.dial_code
this.selectedCountryCode = this.getCountryBy(this.compVal, 'dial_code') } else {
this.compVal = (val) ? this.selectedCountryCode.code + this.selectedCountryCode.dial_code + val : null
}
}
},
selectedCountryCode (newVal, oldVal) {
if (this.compVal && newVal && oldVal) {
this.compVal = this.compVal.replace(oldVal.code + oldVal.dial_code, newVal.code + newVal.dial_code)
} }
} }
}, },
watch: { mounted () {
inputVal: { if (this.compVal) {
handler(val) { if(!this.compVal.startsWith('+')){
if (val && val.startsWith('0')) { this.selectedCountryCode = this.getCountryBy(this.compVal.substring(2, 0))
val = val.substring(1) }
const phoneObj = parsePhoneNumber(this.compVal)
if (phoneObj !== undefined && phoneObj) {
if (!this.selectedCountryCode && phoneObj.country !== undefined && phoneObj.country) {
this.selectedCountryCode = this.getCountryBy(phoneObj.country)
} }
this.compVal = (val) ? this.selectedCountryCode.dial_code + val : null this.inputVal = phoneObj.nationalNumber
}
},
selectedCountryCode(newVal, oldVal) {
if (this.compVal) {
this.compVal = this.compVal.replace(oldVal.dial_code, newVal.dial_code)
} }
} }
if(!this.selectedCountryCode){
this.selectedCountryCode = this.getCountryBy()
}
}, },
methods: { methods: {
getCountryBy(code, type = 'code') { getCountryBy (code = 'US', type = 'code') {
if(!code) code = 'US' // Default US
return countryCodes.find((item) => { return countryCodes.find((item) => {
return item[type] === code return item[type] === code
}) })
}, },
onInput(event) { onInput (event) {
this.inputVal = event.target.value.replace(/[^0-9]/g, '') this.inputVal = event.target.value.replace(/[^0-9]/g, '')
}, },
onChangeCountryCode() { onChangeCountryCode () {
if (this.canOnlyCountry && (this.inputVal === null || this.inputVal === '' || !this.inputVal)) { if (this.canOnlyCountry && (this.inputVal === null || this.inputVal === '' || !this.inputVal)) {
this.compVal = this.selectedCountryCode.dial_code this.compVal = this.selectedCountryCode.code + this.selectedCountryCode.dial_code
} }
} }
} }

View File

@ -59,7 +59,8 @@
</div> </div>
<template v-if="filteredOptions.length>0"> <template v-if="filteredOptions.length>0">
<li v-for="item in filteredOptions" :key="item[optionKey]" role="option" :style="optionStyle" <li v-for="item in filteredOptions" :key="item[optionKey]" role="option" :style="optionStyle"
class="text-gray-900 cursor-default select-none relative py-2 pl-3 pr-9 cursor-pointer group hover:text-white hover:bg-form-color focus:outline-none focus:text-white focus:bg-nt-blue" :class="{'px-3 pr-9':multiple, 'px-3':!multiple}"
class="text-gray-900 cursor-default select-none relative py-2 cursor-pointer group hover:text-white hover:bg-form-color focus:outline-none focus:text-white focus:bg-nt-blue"
:dusk="dusk+'_option'" @click="select(item)" :dusk="dusk+'_option'" @click="select(item)"
> >
<slot name="option" :option="item" :selected="isSelected(item)" /> <slot name="option" :option="item" :selected="isSelected(item)" />
@ -69,7 +70,8 @@
{{ (allowCreation ? 'Type something to add an option': 'No option available') }}. {{ (allowCreation ? 'Type something to add an option': 'No option available') }}.
</p> </p>
<li v-if="allowCreation && searchTerm" role="option" :style="optionStyle" <li v-if="allowCreation && searchTerm" role="option" :style="optionStyle"
class="text-gray-900 cursor-default select-none relative py-2 pl-3 pr-9 cursor-pointer group hover:text-white hover:bg-form-color focus:outline-none focus:text-white focus:bg-nt-blue" :class="{'px-3 pr-9':multiple, 'px-3':!multiple}"
class="text-gray-900 cursor-default select-none relative py-2 cursor-pointer group hover:text-white hover:bg-form-color focus:outline-none focus:text-white focus:bg-nt-blue"
@click="createOption(searchTerm)" @click="createOption(searchTerm)"
> >
Create <b class="px-1 bg-gray-300 rounded group-hover:text-black">{{ searchTerm }}</b> Create <b class="px-1 bg-gray-300 rounded group-hover:text-black">{{ searchTerm }}</b>

View File

@ -44,7 +44,7 @@ class FormSubmissionDataFactory
$value = $this->faker->url(); $value = $this->faker->url();
break; break;
case 'phone_number': case 'phone_number':
$value = '+33749119783'; $value = 'FR+33749119783';
break; break;
case 'date': case 'date':
$value = $this->faker->date(); $value = $this->faker->date();