Refactor: apply PhoneInputCustom to PortofolioEdit
Changes: - Replace react-native-international-phone-number with PhoneInputCustom - Remove ICountry dependency, use CountryData from constants - Add phone number state management - Implement country detection from existing phone number - Auto-detect country based on calling code on load - Improve phone number formatting logic Features Applied: ✅ NO emoji flags - only calling codes (+62, +65, etc) ✅ Clean, professional UI ✅ Modal country picker with search ✅ Auto-detect country from saved phone number ✅ Real-time phone number formatting ✅ Auto-update country code on change ✅ Consistent with LoginView & ScreenPortofolioCreate Phone Detection Logic: - Load existing phone number from API - Detect country by matching calling code - Extract phone number without country code for display - Set detected country for country picker - Re-format on country change UI: - Phone Input: [+62 ⌄ | xxx-xxx-xxx] - Country Picker: Modal with search - Display: Country name + calling code only Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
This commit is contained in:
@@ -5,6 +5,7 @@ import {
|
|||||||
ButtonCustom,
|
ButtonCustom,
|
||||||
CenterCustom,
|
CenterCustom,
|
||||||
NewWrapper,
|
NewWrapper,
|
||||||
|
PhoneInputCustom,
|
||||||
SelectCustom,
|
SelectCustom,
|
||||||
Spacing,
|
Spacing,
|
||||||
StackCustom,
|
StackCustom,
|
||||||
@@ -15,6 +16,7 @@ import {
|
|||||||
import ListSkeletonComponent from "@/components/_ShareComponent/ListSkeletonComponent";
|
import ListSkeletonComponent from "@/components/_ShareComponent/ListSkeletonComponent";
|
||||||
import { MainColor } from "@/constants/color-palet";
|
import { MainColor } from "@/constants/color-palet";
|
||||||
import { ICON_SIZE_XLARGE } from "@/constants/constans-value";
|
import { ICON_SIZE_XLARGE } from "@/constants/constans-value";
|
||||||
|
import { DEFAULT_COUNTRY, type CountryData, COUNTRIES } from "@/constants/countries";
|
||||||
import {
|
import {
|
||||||
apiMasterBidangBisnis,
|
apiMasterBidangBisnis,
|
||||||
apiMasterSubBidangBisnis,
|
apiMasterSubBidangBisnis,
|
||||||
@@ -32,7 +34,6 @@ import { router, useLocalSearchParams } from "expo-router";
|
|||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { Text, View } from "react-native";
|
import { Text, View } from "react-native";
|
||||||
import PhoneInput, { ICountry } from "react-native-international-phone-number";
|
|
||||||
import { ActivityIndicator } from "react-native-paper";
|
import { ActivityIndicator } from "react-native-paper";
|
||||||
import Toast from "react-native-toast-message";
|
import Toast from "react-native-toast-message";
|
||||||
|
|
||||||
@@ -59,8 +60,8 @@ export default function PortofolioEdit() {
|
|||||||
const { id } = useLocalSearchParams();
|
const { id } = useLocalSearchParams();
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
const [data, setData] = useState<any>({});
|
const [data, setData] = useState<any>({});
|
||||||
|
const [phoneNumber, setPhoneNumber] = useState<string>("");
|
||||||
const [selectedCountry, setSelectedCountry] = useState<null | ICountry>(null);
|
const [selectedCountry, setSelectedCountry] = useState<CountryData>(DEFAULT_COUNTRY);
|
||||||
const [bidangBisnis, setBidangBisnis] = useState<
|
const [bidangBisnis, setBidangBisnis] = useState<
|
||||||
IMasterBidangBisnis[] | null
|
IMasterBidangBisnis[] | null
|
||||||
>(null);
|
>(null);
|
||||||
@@ -72,12 +73,42 @@ export default function PortofolioEdit() {
|
|||||||
IListSubBidangSelected[]
|
IListSubBidangSelected[]
|
||||||
>([]);
|
>([]);
|
||||||
|
|
||||||
function handleInputValue(phoneNumber: string) {
|
function handlePhoneChange(phone: string) {
|
||||||
setData({ ...data, tlpn: phoneNumber });
|
setPhoneNumber(phone);
|
||||||
|
|
||||||
|
// Format phone number for API
|
||||||
|
const callingCode = selectedCountry.callingCode;
|
||||||
|
let fixNumber = phone.replace(/\s+/g, "").replace(/^0+/, "");
|
||||||
|
|
||||||
|
// Remove country code if already present
|
||||||
|
if (fixNumber.startsWith(callingCode)) {
|
||||||
|
fixNumber = fixNumber.substring(callingCode.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove leading zero
|
||||||
|
fixNumber = fixNumber.replace(/^0+/, "");
|
||||||
|
|
||||||
|
const realNumber = callingCode + fixNumber;
|
||||||
|
setData({ ...data, tlpn: realNumber });
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleSelectedCountry(country: ICountry) {
|
function handleCountryChange(country: CountryData) {
|
||||||
setSelectedCountry(country);
|
setSelectedCountry(country);
|
||||||
|
|
||||||
|
// Re-format with new country code
|
||||||
|
const callingCode = country.callingCode;
|
||||||
|
let fixNumber = phoneNumber.replace(/\s+/g, "").replace(/^0+/, "");
|
||||||
|
|
||||||
|
// Remove country code if already present
|
||||||
|
if (fixNumber.startsWith(callingCode)) {
|
||||||
|
fixNumber = fixNumber.substring(callingCode.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove leading zero
|
||||||
|
fixNumber = fixNumber.replace(/^0+/, "");
|
||||||
|
|
||||||
|
const realNumber = callingCode + fixNumber;
|
||||||
|
setData({ ...data, tlpn: realNumber });
|
||||||
}
|
}
|
||||||
|
|
||||||
const onLoadMasterBidang = async () => {
|
const onLoadMasterBidang = async () => {
|
||||||
@@ -122,8 +153,27 @@ export default function PortofolioEdit() {
|
|||||||
const response = await apiGetOnePortofolio({ id: id });
|
const response = await apiGetOnePortofolio({ id: id });
|
||||||
|
|
||||||
if (response.success) {
|
if (response.success) {
|
||||||
const fixNumber = response.data.tlpn.replace("62", "");
|
// Extract phone number without country code for display
|
||||||
setData({ ...response.data, tlpn: fixNumber });
|
const fullNumber = response.data.tlpn;
|
||||||
|
let displayNumber = fullNumber;
|
||||||
|
let detectedCountry = DEFAULT_COUNTRY;
|
||||||
|
|
||||||
|
// Try to detect country from calling code
|
||||||
|
for (const country of COUNTRIES) {
|
||||||
|
if (fullNumber.startsWith(country.callingCode)) {
|
||||||
|
detectedCountry = country;
|
||||||
|
displayNumber = fullNumber.substring(country.callingCode.length);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setSelectedCountry(detectedCountry);
|
||||||
|
|
||||||
|
// Remove leading zero if present
|
||||||
|
displayNumber = displayNumber.replace(/^0+/, "");
|
||||||
|
|
||||||
|
setPhoneNumber(displayNumber);
|
||||||
|
setData({ ...response.data, tlpn: displayNumber });
|
||||||
|
|
||||||
// Cek apakah ada sub bidang bisnis yang terpilih
|
// Cek apakah ada sub bidang bisnis yang terpilih
|
||||||
const prevSubBidang = response.data.Portofolio_BidangDanSubBidangBisnis;
|
const prevSubBidang = response.data.Portofolio_BidangDanSubBidangBisnis;
|
||||||
@@ -244,15 +294,11 @@ export default function PortofolioEdit() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const handleSubmitUpdate = async () => {
|
const handleSubmitUpdate = async () => {
|
||||||
const callingCode = selectedCountry?.callingCode.replace(/^\+/, "") || "";
|
|
||||||
let fixNumber = data.tlpn.replace(/\s+/g, "").replace(/^0+/, "");
|
|
||||||
const realNumber = callingCode + fixNumber;
|
|
||||||
|
|
||||||
const newData: IFormData = {
|
const newData: IFormData = {
|
||||||
id_Portofolio: data.id_Portofolio,
|
id_Portofolio: data.id_Portofolio,
|
||||||
namaBisnis: data.namaBisnis,
|
namaBisnis: data.namaBisnis,
|
||||||
alamatKantor: data.alamatKantor,
|
alamatKantor: data.alamatKantor,
|
||||||
tlpn: realNumber,
|
tlpn: data.tlpn, // Already formatted by PhoneInputCustom
|
||||||
deskripsi: data.deskripsi,
|
deskripsi: data.deskripsi,
|
||||||
masterBidangBisnisId: data.masterBidangBisnisId,
|
masterBidangBisnisId: data.masterBidangBisnisId,
|
||||||
subBidang: listSubBidangSelected,
|
subBidang: listSubBidangSelected,
|
||||||
@@ -435,12 +481,11 @@ export default function PortofolioEdit() {
|
|||||||
<Text style={{ color: "red" }}> *</Text>
|
<Text style={{ color: "red" }}> *</Text>
|
||||||
</View>
|
</View>
|
||||||
<Spacing height={5} />
|
<Spacing height={5} />
|
||||||
<PhoneInput
|
<PhoneInputCustom
|
||||||
value={data.tlpn}
|
value={phoneNumber}
|
||||||
onChangePhoneNumber={handleInputValue}
|
onChangePhoneNumber={handlePhoneChange}
|
||||||
selectedCountry={selectedCountry}
|
selectedCountry={selectedCountry}
|
||||||
onChangeSelectedCountry={handleSelectedCountry}
|
onChangeCountry={handleCountryChange}
|
||||||
defaultCountry="ID"
|
|
||||||
placeholder="xxx-xxx-xxx"
|
placeholder="xxx-xxx-xxx"
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
|
|||||||
@@ -160,7 +160,7 @@ const NewWrapper = (props: NewWrapperProps) => {
|
|||||||
<ScrollView
|
<ScrollView
|
||||||
contentContainerStyle={{ flexGrow: 1 }}
|
contentContainerStyle={{ flexGrow: 1 }}
|
||||||
keyboardShouldPersistTaps="handled"
|
keyboardShouldPersistTaps="handled"
|
||||||
refreshControl={refreshControl} // ✅ sekarang valid
|
refreshControl={refreshControl}
|
||||||
>
|
>
|
||||||
<TouchableWithoutFeedback onPress={Keyboard.dismiss}>
|
<TouchableWithoutFeedback onPress={Keyboard.dismiss}>
|
||||||
{renderContainer(staticProps.children)}
|
{renderContainer(staticProps.children)}
|
||||||
|
|||||||
Reference in New Issue
Block a user