Introduces Unicode-safe base64 encoding/decoding for call details between incidents and call screens. Refactors PageHeader to accept left, center, and right header props for more flexible layouts. Updates call.jsx and register.jsx to use the new header structure, and improves department/unit rendering and modal dropdowns for department selection. Generalizes and modernizes UI code for better maintainability and cross-platform compatibility.
379 lines
No EOL
9.5 KiB
JavaScript
379 lines
No EOL
9.5 KiB
JavaScript
import React, { useState } from 'react';
|
|
import styled from 'styled-components';
|
|
import { View, Text, LayoutAnimation, Image, TextInput, TouchableOpacity, TouchableNativeFeedback, ScrollView, Platform, Modal, Pressable } from 'react-native';
|
|
import { Picker } from '@react-native-picker/picker';
|
|
import { Ionicons } from '@expo/vector-icons';
|
|
import { Container } from './Container';
|
|
|
|
export const StyledContainer = styled.View`
|
|
flex: 1;
|
|
padding: 25px;
|
|
`;
|
|
|
|
export const InnerContainer = styled.View`
|
|
flex: 1;
|
|
width: 100%;
|
|
align-items: center;
|
|
`;
|
|
|
|
export const StyledFormArea = styled.View`
|
|
padding-top: 10px;
|
|
width: 90%;
|
|
`;
|
|
|
|
export const Title = styled.Text`
|
|
text-align: center;
|
|
padding: 10px 10px 0px 10px;
|
|
font-size: 30px;
|
|
font-wieght: bold;
|
|
`;
|
|
|
|
export const SubTitle = styled.Text`
|
|
text-align: center;
|
|
font-size: 15px;
|
|
`;
|
|
|
|
export const PageImage = styled.Image``;
|
|
|
|
export const StyledTextInput = styled.TextInput`
|
|
background-color: #E5E7EB;
|
|
padding: 15px 55px 15px 55px;
|
|
border-radius: 5px;
|
|
font-size: 16px;
|
|
height: 60px;
|
|
margin-vertical: 3px;
|
|
margin-bottom: 10px;
|
|
`;
|
|
|
|
export const StyledInputLabel = styled.Text`
|
|
font-size: 13px;
|
|
text-align: left;
|
|
`;
|
|
|
|
export const LeftIcon = styled.View`
|
|
left: 15px;
|
|
top: 32px;
|
|
position: absolute;
|
|
z-index: 1;
|
|
`;
|
|
|
|
export const RightIcon = styled.TouchableOpacity`
|
|
right: 15px;
|
|
top: 32px;
|
|
position: absolute;
|
|
z-index: 1;
|
|
`;
|
|
|
|
export const DropdownArrow = styled.TouchableOpacity`
|
|
right: 15px;
|
|
position: absolute;
|
|
z-index: 1;
|
|
`;
|
|
|
|
export const SelectedCheckmark = styled.View`
|
|
right: 4px;
|
|
top: 4px;
|
|
position: absolute;
|
|
z-index: 1;
|
|
`;
|
|
|
|
export const StyledButton = styled.TouchableOpacity`
|
|
padding: 15px;
|
|
background-color: red;
|
|
justify-content: center;
|
|
align-items: center;
|
|
border-radius: 5px;
|
|
margin-vertical: 5px;
|
|
height: 60px;
|
|
`;
|
|
|
|
export const ButtonText = styled.Text`
|
|
font-size: 16px;
|
|
font-weight: 450;
|
|
color: white;
|
|
`;
|
|
|
|
export const MessageBox = styled.Text`
|
|
text-align: center;
|
|
font-size: 13px;
|
|
`
|
|
|
|
export const Line = styled.View`
|
|
height: 1px;
|
|
width: 100%;
|
|
background-color: 'gray';
|
|
margin-vertical: 10px;
|
|
`
|
|
|
|
export const ExtraView = styled.View`
|
|
justify-content: center;
|
|
flex-direction: row;
|
|
align-items: center;
|
|
padding: 10px;
|
|
`
|
|
|
|
export const ExtraText = styled.Text`
|
|
justify0content: center;
|
|
align-content: center;
|
|
font-size: 15px;
|
|
`
|
|
|
|
export const TextLink = styled.TouchableOpacity`
|
|
justify-content: center;
|
|
align-content: center;
|
|
`
|
|
|
|
export const TextLinkContent = styled.Text`
|
|
color: red;
|
|
font-size: 15px;
|
|
`
|
|
|
|
const ProviderDropdown = styled.TouchableOpacity`
|
|
background-color: #E5E7EB;
|
|
padding: 15px 55px 15px 55px;
|
|
border-radius: 5px;
|
|
font-size: 16px;
|
|
height: 60px;
|
|
margin-vertical: 3px;
|
|
margin-bottom: 10px;
|
|
`
|
|
|
|
export const providerMenu = {
|
|
menuName: 'providerList',
|
|
placeholder: 'Select Provider',
|
|
iconColor: 'red',
|
|
iconName: 'globe-outline',
|
|
dropdownList: [
|
|
{ label: 'AllTel', value: 'alltel' },
|
|
{ label: 'AT&T', value: 'att' },
|
|
{ label: 'AT&T MMS', value: 'attmms' },
|
|
{ label: 'Boost', value: 'boost' },
|
|
{ label: 'Boost MMS', value: 'boostmms' },
|
|
{ label: 'C Spire', value: 'c' },
|
|
{ label: 'Consumer Cellular', value: 'consumer' },
|
|
{ label: 'Cricket', value: 'cricket' },
|
|
{ label: 'Cricket MMS', value: 'cricketmms' },
|
|
{ label: 'Google Fi', value: 'googlefi' },
|
|
{ label: 'Mint Mobile', value: 'mint' },
|
|
{ label: 'MetroPCS', value: 'metro' },
|
|
{ label: 'Optimum', value: 'optimum' },
|
|
{ label: 'Republic Wireless', value: 'republic' },
|
|
{ label: 'Spectrum', value: 'spectrum' },
|
|
{ label: 'Sprint', value: 'sprint' },
|
|
{ label: 'Sprint MMS', value: 'sprintmms' },
|
|
{ label: 'Ting', value: 'ting' },
|
|
{ label: 'T-Mobile', value: 'tmobile' },
|
|
{ label: 'TracFone', value: 'tracfone' },
|
|
{ label: 'US Cellular', value: 'us' },
|
|
{ label: 'US Cellular MMS', value: 'usmms' },
|
|
{ label: 'Verizon', value: 'verizon' },
|
|
{ label: 'Verizon MMS', value: 'verizonmms' },
|
|
{ label: 'VerizonBiz', value: 'verizonbiz' },
|
|
{ label: 'Virgin', value: 'virgin' },
|
|
{ label: 'Virgin MMS', value: 'virginmms' },
|
|
]
|
|
}
|
|
|
|
const providerConversion = {
|
|
alltel: 'AllTel',
|
|
att: 'AT&T',
|
|
attmms: 'AT&T MMS',
|
|
boost: 'Boost',
|
|
boostmms: 'Boost MMS',
|
|
c: 'C Spire',
|
|
consumer: 'Consumer Cellular',
|
|
cricket: 'Cricket',
|
|
cricketmms: 'Cricket MMS',
|
|
googlefi: 'Google Fi',
|
|
mint: 'Mint Mobile',
|
|
metro: 'MetroPCS',
|
|
optimum: 'Optimum',
|
|
republic: 'Republic Wireless',
|
|
spectrum: 'Spectrum',
|
|
sprint: 'Sprint',
|
|
sprintmms: 'Sprint MMS',
|
|
ting: 'Ting',
|
|
tmobile: 'T-Mobile',
|
|
tracfone: 'TracFone',
|
|
us: 'US Cellular',
|
|
usmms: 'US Cellular MMS',
|
|
verizon: 'Verizon',
|
|
verizonmms: 'Verizon MMS',
|
|
verizonbiz: 'VerizonBiz',
|
|
virgin: 'Virgin',
|
|
virginmms: 'Virgin MMS',
|
|
}
|
|
|
|
export const PageHeader = ({
|
|
leftHeader = <View style={{ flex: 1 }} />,
|
|
centerHeader = <View style={{ flex: 1 }} />,
|
|
rightHeader = <View style={{ flex: 1 }} />
|
|
}) => {
|
|
return (
|
|
<View style={{ position: 'sticky', top: 0, backgroundColor: '#ECEDEE', zIndex: 1, marginBottom: -100 }}>
|
|
<View
|
|
style={{
|
|
flexDirection: 'column',
|
|
height: 80,
|
|
alignItems: 'center',
|
|
justifyContent: 'flex-end',
|
|
paddingBottom: 7
|
|
}}
|
|
>
|
|
<View style={{
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
justifyContent: 'space-between',
|
|
width: '100%',
|
|
paddingHorizontal: 0
|
|
}}>
|
|
{leftHeader}
|
|
{centerHeader}
|
|
{rightHeader}
|
|
</View>
|
|
</View>
|
|
</View>
|
|
)
|
|
}
|
|
|
|
export const PageFooter = ({
|
|
children
|
|
}) => {
|
|
return (
|
|
<View style={{ position: 'fixed', top: 0, backgroundColor: '#ECEDEE', zIndex: 1 }}>
|
|
{children}
|
|
</View>
|
|
)
|
|
}
|
|
|
|
export const LoginTextInput = ({
|
|
label,
|
|
icon,
|
|
isPassword = false,
|
|
hidePassword = true,
|
|
setHidePassword = (boolean) => { },
|
|
...props
|
|
}) => {
|
|
return (
|
|
<View>
|
|
<LeftIcon>
|
|
<Ionicons name={icon} size={30} color='red' />
|
|
</LeftIcon>
|
|
<StyledInputLabel>{label}</StyledInputLabel>
|
|
<StyledTextInput {...props} />
|
|
{isPassword ? (
|
|
<RightIcon onPress={() => { setHidePassword(!hidePassword) }}>
|
|
<Ionicons name={hidePassword ? 'eye-off-outline' : 'eye-outline'} size={30} color="gray" />
|
|
</RightIcon>
|
|
) : null}
|
|
</View>
|
|
)
|
|
}
|
|
|
|
export const RegisterDropdownInput = ({
|
|
label,
|
|
isOpen: _isOpen,
|
|
setOpen: _setOpen,
|
|
selectedValue,
|
|
onValueChange,
|
|
menu
|
|
}) => {
|
|
const [modalVisible, setModalVisible] = useState(false);
|
|
|
|
return (
|
|
<Container>
|
|
<StyledInputLabel>{label}</StyledInputLabel>
|
|
<TouchableOpacity
|
|
activeOpacity={0.8}
|
|
style={{
|
|
backgroundColor: '#E5E7EB',
|
|
marginTop: 3,
|
|
marginBottom: 10,
|
|
borderRadius: 5,
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
minHeight: 60,
|
|
paddingHorizontal: 15,
|
|
}}
|
|
onPress={() => setModalVisible(true)}
|
|
>
|
|
<Ionicons
|
|
name={menu.iconName}
|
|
size={30}
|
|
color={menu.iconColor}
|
|
/>
|
|
<Text style={{ color: selectedValue ? 'black' : 'grey', fontSize: 16, paddingHorizontal: 16, flex: 1 }}>
|
|
{selectedValue ? providerConversion[selectedValue] : menu.placeholder}
|
|
</Text>
|
|
<DropdownArrow onPress={() => setModalVisible(!modalVisible)}>
|
|
<Ionicons name={modalVisible ? "chevron-up-outline" : "chevron-down-outline"} size={30} color="gray" />
|
|
</DropdownArrow>
|
|
</TouchableOpacity>
|
|
<Modal
|
|
visible={modalVisible}
|
|
animationType="slide"
|
|
transparent={true}
|
|
onRequestClose={() => setModalVisible(false)}
|
|
>
|
|
<Pressable
|
|
style={{ flex: 1 }}
|
|
onPress={() => setModalVisible(false)}
|
|
/>
|
|
<View style={{
|
|
position: 'absolute',
|
|
left: 0,
|
|
right: 0,
|
|
bottom: 0,
|
|
backgroundColor: '#fff',
|
|
borderTopLeftRadius: 16,
|
|
borderTopRightRadius: 16,
|
|
paddingBottom: 32,
|
|
paddingTop: 16,
|
|
}}>
|
|
<Pressable
|
|
style={{ flex: 1, paddingHorizontal: 15, alignItems: 'flex-end' }}
|
|
onPress={() => setModalVisible(false)}
|
|
>
|
|
<Text style={{ color: 'red', fontWeight: 600 }}>
|
|
Close
|
|
</Text>
|
|
</Pressable>
|
|
<Picker
|
|
selectedValue={selectedValue}
|
|
onValueChange={(value) => {
|
|
onValueChange(value);
|
|
}}
|
|
>
|
|
{menu.dropdownList.map((item) => (
|
|
<Picker.Item color="black" label={item.label} value={item.value} key={item.value} />
|
|
))}
|
|
</Picker>
|
|
</View>
|
|
</Modal>
|
|
</Container>
|
|
);
|
|
}
|
|
|
|
export const formatPhoneNumber = (e) => {
|
|
let formattedNumber;
|
|
const length = e?.length;
|
|
|
|
const regex = () => e.replace(/[^0-9\.]+/g, "");
|
|
const areaCode = () => `(${regex().slice(0, 3)})`;
|
|
const firstSix = () => `${areaCode()} ${regex().slice(3, 6)}`;
|
|
const trailer = (start) => `${regex().slice(start, regex().length)}`;
|
|
|
|
if (length <= 3) {
|
|
formattedNumber = regex();
|
|
} else if (length === 4) {
|
|
formattedNumber = `${areaCode()} ${trailer(3)}`;
|
|
} else if (length === 5) {
|
|
formattedNumber = `${areaCode().replace(")", "")}`;
|
|
} else if (length >= 5 && length <= 9) {
|
|
formattedNumber = `${areaCode()} ${trailer(3)}`;
|
|
} else if (length >= 10) {
|
|
formattedNumber = `${firstSix()}-${trailer(6)}`;
|
|
}
|
|
|
|
return formattedNumber;
|
|
}; |