Browse Source

add app/pages/sign/RegisterV2.js

wudebin 5 months ago
parent
commit
1142f9be66

+ 747 - 0
Strides-SPAPP/app/pages/sign/RegisterV2.js

@@ -0,0 +1,747 @@
+/**
+ * 带Public和PHV切换的注册页面V2
+ * @邠心vbe on 2023/02/01
+ */
+import React from 'react';
+import { View, ScrollView, StyleSheet, TextInput, Pressable, Image } from 'react-native';
+import { BackButton, Styles } from '../../components/Toolbar';
+import apiUser from '../../api/apiUser';
+import Button from '../../components/Button';
+import { PageList } from '../Router';
+import Dialog from '../../components/Dialog';
+import Modal from 'react-native-modal';
+import { RegisterDialog } from '../charge/InfoDialog';
+import Dropdown from '../../components/Dropdown';
+import ImagePicker from 'react-native-image-crop-picker';
+import apiUpload from '../../api/apiUpload';
+import { ModalProps } from '../../components/BottomModal';
+import { CountryDropNum, GetCountryList } from '../../components/CountryIcon';
+import StrengthView from './StrengthView';
+import CheckBox from '../../components/CheckBox';
+import { UploadThemes } from '../../components/ThemesConfig';
+import TextView from '../../components/TextView';
+import utils from '../../utils/utils';
+
+const options = {
+  width: 300,
+  height: 200,
+  cropping: true,
+  multiple: false,
+  mediaType: 'photo',
+  writeTempFile: false,
+  compressImageQuality: 0.8,
+  compressImageMaxWidth: 720,
+  compressImageMaxHeight: 1280,
+  ...UploadThemes
+}
+
+export default class RegisterV2 extends React.Component {
+  constructor(props) {
+    super(props);
+    this.state = {
+      agree: true,
+      strength: 0,
+      countryNum: '65',
+      countryShow: false,
+      userInfo: {
+        email: ""
+      },
+      countryNums: [],
+      wrongCount: 0,
+      params: {...this.props.route.params},
+      email: '',
+      password: '',
+      fleetCompanyId: '',
+      visible: false,
+      isFleetDriver: false,
+      pdvImages: ['', ''],
+      companyList: []
+    };
+  }
+
+  componentDidMount() {
+    //console.log(this.state.params);
+    this.getCountryList();
+    this.getCompanyList();
+  }
+
+  applyStrength(text) {
+    StrengthView.V4.apply(text, strength => {
+      if (this.state.strength != strength) {
+        this.setState({
+          password: text,
+          strength: strength
+        });
+      } else {
+        this.setState({
+          password: text
+        });
+      }
+    });
+  }
+
+  changeInfo(key, value) {
+    var info = this.state.userInfo;
+    info[key] = value;
+    this.setState({
+      'userInfo': info
+    });
+  }
+
+  changeTab(type) {
+    this.setState({
+      isFleetDriver: type
+    })
+  }
+
+  getCountryList() {
+    GetCountryList(list => {
+      this.setState({
+        countryNums: list
+      })
+    })
+  }
+
+  getCompanyList() {
+    apiUser.getConmpany().then(res => {
+      if (res.data) {
+        this.setState({
+          companyList: res.data
+        })
+      }
+    }).catch(err => [
+      toastShort(err)
+    ])
+  }
+
+  onRegister() {
+    //console.log('sign up', this.state);
+    var info = this.state.userInfo;
+    if (!info.nickName) {
+      toastShort($t('sign.plsInputDiaplayName'));
+      return;
+    }
+    if (!info.email) {
+      toastShort($t('sign.plsInputEmail'));
+      return;
+    }
+    if (!utils.isValidEmail(info.email)) {
+      toastShort($t('sign.errEmailFormat'));
+      return;
+    }
+    if (!info.phone) {
+      toastShort($t('sign.plsInputContactNo'));
+      return;
+    }
+    if (!/^\d{6,15}$/.test(info.phone)) {
+      toastShort($t('sign.errContactNoFormat'));
+      return;
+    }
+    if (!this.state.password) {
+      toastShort($t('sign.plsInputPassword'));
+      return;
+    }
+    if (this.state.strength < StrengthView.V4.maxStrength) {
+      toastShort($t('sign.errPasswordStrong'));
+      return;
+    }
+    if (!info.password) {
+      toastShort($t('sign.plsInputPassword2'));
+      return;
+    }
+    if (info.password != this.state.password) {
+      toastShort($t('sign.errPasswordConfirm'));
+      if (this.state.wrongCount < 3) {
+        this.setState({
+          wrongCount: this.state.wrongCount + 1
+        })
+      }
+      return;
+    }
+    if (this.state.isFleetDriver) {
+      if (!info.pdvLicence) {
+        toastShort($t('sign.plsInputPDVLicence'));
+        return;
+      }
+      if (this.state.pdvImages[0] == '' || this.state.pdvImages[1] == '') {
+        toastShort($t('sign.plsUploadLicencePhotos'));
+        return;
+      }
+    }
+    let param = Object.assign({}, info);
+    //param.phone = this.state.countryNum + info.phone
+    param.callingCode = this.state.countryNum;
+    if (this.state.isFleetDriver) {
+      param.userType = 'Driver';
+      param.pdvLicencePictures = this.state.pdvImages;
+      param.fleetCompanyId = this.state.fleetCompanyId;
+    } else {
+      param.userType = 'Public';
+    }
+    console.log('params', param);
+    Dialog.showProgressDialog();
+    apiUser.register(param).then(res => {
+      Dialog.dismissLoading();
+      //toastShort('Sign up successfully!');
+      if (isIOS) {
+        setTimeout(() => {
+          this.setState({
+            email: param.email,
+            visible: true
+          });
+        }, 500);
+      } else {
+        this.setState({
+          email: param.email,
+          visible: true
+        });
+      }
+      //this.backToLogin();
+    }).catch(err => {
+      toastShort(err);
+      Dialog.dismissLoading();
+    });
+  }
+  
+  getBackTopPosition() {
+    return isIOS ? statusHeight - 16 : 8;
+  }
+  
+  backToLogin() {
+    if (this.state.params.actionLogin) {
+      goBack()
+      startPage(PageList.login);
+    } else {
+      goBack();
+    }
+  }
+
+  uploadImage(index) {
+    ImagePicker.openPicker({
+      ...options,
+      cropperToolbarTitle: $t('common.cropperTitle')
+    }).then(image => {
+      if (image.path) {
+        apiUpload.uploadImage(image.path, image.mime, 'PDVL').then(res => {
+          if (res.success && res.data.picturePath) {
+            let imageUrl = this.state.pdvImages;
+            imageUrl[index] = res.data.picturePath;
+            this.setState({
+              pdvImages: imageUrl
+            });
+            toastShort($t('common.uploadSuccess'));
+          } else {
+            toastShort($t('common.updateFailed'));
+          }
+        }).catch(err => {
+          toastShort(err);
+        });
+      }
+    }).catch(err1 => {
+      //console.log(err1);
+    });
+  }
+
+  changeAgree(ag) {
+    this.setState({
+      agree: ag
+    })
+  }
+
+  hideDialog() {
+    this.setState({
+      visible: false
+    });
+    this.backToLogin();
+  }
+
+  render() {
+    return (
+      <View style={ui.container}>
+        <View style={Styles.toolbar}>
+          <BackButton/>
+          <View style={styles.logoView}>
+            <Image
+              source={require('../../images/app-logo.png')}
+              style={Styles.logo}
+              resizeMode='contain'
+            />
+          </View>
+        </View>
+        <ScrollView
+          style={styles.scollView}
+          keyboardShouldPersistTaps={isIOS ? 'never' : 'handled'}>
+          <View style={styles.signView}>
+            <View style={styles.tabView}>
+              <TextView 
+                style={[
+                  styles.tabText, 
+                  this.state.isFleetDriver ? {} : styles.tabActive
+                ]}
+                onPress={() => this.changeTab(false)}
+              >Public Users</TextView>
+              <TextView
+                style={[
+                  styles.tabText,
+                  this.state.isFleetDriver ? styles.tabActive: {}
+                ]}
+                onPress={() => this.changeTab(true)}
+              >Fleet/PH Drivers</TextView>
+            </View>
+            {/* <Text style={styles.title}>Registration</Text> */}
+            <View style={styles.signInput}>
+              <TextView style={styles.inputLabel}>{$t('sign.labelDisplayName')}</TextView>
+              <TextInput
+                style={styles.inputView} 
+                placeholder={$t('sign.labelDisplayName')}
+                placeholderTextColor={textPlacehoder}
+                maxLength={50}
+                onChangeText={v => this.changeInfo('nickName', v)}
+              />
+            </View>
+            <View style={styles.signInput}>
+              <TextView style={styles.inputLabel}>{$t('sign.labelEmail')}</TextView>
+              <TextInput
+                style={styles.inputView}
+                placeholder={$t('sign.labelEmail')}
+                placeholderTextColor={textPlacehoder}
+                maxLength={50}
+                onChangeText={v => this.changeInfo('email', v)}
+              />
+            </View>
+            <View style={styles.signInput}>
+              <TextView style={styles.inputLabel}>{$t('sign.labelPhoneNumber')}</TextView>
+              <View style={styles.mobileView}>
+                <View style={styles.dropView}>
+                  <TextInput style={styles.dropInput} editable={false}/>
+                  <TextView style={styles.countryText}>{"+" + this.state.countryNum}</TextView>
+                  <MaterialIcons name={'arrow-drop-down'} size={24} color={colorDark}/>
+                  <Dropdown
+                    style={styles.dropLayer}
+                    title={$t('sign.labelCountry')}
+                    prefixText="+"
+                    list={this.state.countryNums}
+                    value={this.state.countryNum}
+                    nameKey='countryNum'
+                    valueKey='countryNum'
+                    onChange={(value, index)=> {
+                      this.setState({
+                        countryNum: value
+                      })
+                    }}
+                    customerItemView={
+                      (item, index, onClick) => 
+                      <CountryDropNum
+                        key={index} 
+                        country={item}
+                        value={this.state.countryNum}
+                        onClick={onClick}/>
+                    }/>
+                </View>
+                <TextInput
+                  style={styles.contactView}
+                  placeholder={$t('sign.labelMobileNumber')}
+                  placeholderTextColor={textPlacehoder}
+                  keyboardType='phone-pad'
+                  maxLength={15}
+                  onChangeText={v => this.changeInfo('phone', v)}
+                />
+              </View>
+            </View>
+            <View style={styles.signInput}>
+              <TextView style={styles.inputLabel}>{$t('sign.labelCreatePassword')}</TextView>
+              <TextInput
+                secureTextEntry={this.state.wrongCount < 3}
+                style={styles.inputView}
+                placeholder={$t('sign.labelPassword')}
+                placeholderTextColor={textPlacehoder}
+                maxLength={20}
+                onChangeText={(value) => {
+                  this.applyStrength(value);
+                }}
+              />
+            </View>
+            <View style={styles.signInput}>
+              <TextView style={styles.inputLabel}></TextView>
+              <View style={styles.passwordView}>
+                <StrengthView.V4.VIEW {...this.state}/>
+              </View>
+            </View>
+            <View style={styles.signInput}>
+              <TextView style={styles.inputLabel}>{$t('sign.labelConfirmPassword')}</TextView>
+              <TextInput
+                secureTextEntry={this.state.wrongCount < 3}
+                style={styles.inputView}
+                placeholder={$t('sign.labelPassword')}
+                placeholderTextColor={textPlacehoder}
+                maxLength={20}
+                onChangeText={v => this.changeInfo('password', v)}
+              />
+            </View>
+            { this.state.isFleetDriver &&
+              <>
+              <View style={styles.signInput}>
+                <TextView style={styles.inputLabel}>{$t('sign.labelYourCompany')}</TextView>
+                <Dropdown
+                  style={[styles.inputView, ui.flexc]}
+                  title={$t('sign.labelCompany')}
+                  list={this.state.companyList}
+                  value={this.state.fleetCompanyId}
+                  valueKey='fleetCompanyId'
+                  nameKey='fleetCompanyName'
+                  onChange={(value, index)=> {
+                    this.setState({
+                      fleetCompanyId: value
+                    })
+                  }}/>
+              </View>
+              <View style={styles.signInput}>
+                <TextView style={styles.inputLabel}>{$t('sign.labelPDVLicence')}</TextView>
+                <TextInput
+                  style={styles.inputView}
+                  placeholder={$t('sign.hintPDVLicence')}
+                  placeholderTextColor={textPlacehoder}
+                  maxLength={20}
+                  onChangeText={v => this.changeInfo('pdvLicence', v)}
+                />
+              </View>
+              <View style={styles.signInput}>
+                <TextView style={styles.inputLabel}>{$t('sign.labelPDVPhotos')}</TextView>
+                <View style={styles.uploadGroup}>
+                  { this.state.pdvImages.map((item, index) => (
+                    <UploadView
+                      key={index}
+                      onPress={() => this.uploadImage(index)}
+                      url={item}/>
+                    )) 
+                  }
+                </View>
+              </View>
+              </>
+            }
+            <View style={styles.referView}>
+              <TextView style={styles.referTitle}>Have a referral code?</TextView>
+              <View style={styles.referText}>
+                <TextView>You'll get</TextView>
+                <TeTextViewxt style={styles.weight}>$5</TeTextViewxt>
+                <TextView>Credit as registration bonus!</TextView>
+              </View>
+              <View style={styles.codeView}>
+                <TextView style={{ color: textPrimary, fontSize: 16 }}>Referral Code</TextView>
+                <TextInput
+                  style={styles.codeText}
+                  maxLength={6}
+                  placeholder='Referral Code'
+                  placeholderTextColor={textPlacehoder}
+                  onChangeText={v => this.changeInfo('referralCode', v)}
+                />
+              </View>
+            </View>
+            <View style={styles.agreeView}>
+              <CheckBox
+                value={this.state.agree}
+                onValueChange={v => this.changeAgree(v)}
+              />
+              <View style={styles.agreeTextRow}>
+                <TextView style={styles.agreeText} onPress={() => this.changeAgree(!this.state.agree)}>
+                  {$t('sign.iHaveReadAndAgree')}
+                </TextView>
+                <TextView style={styles.agreeLink} onPress={() => startPage(PageList.condition)}>{$t('drawer.termsOfUse')}</TextView>
+                <TextView style={styles.agreeText}>{' '}</TextView>
+                <TextView style={styles.agreeText}>{$t('sign.linkAndLink')}</TextView>
+                <TextView style={styles.agreeLink} onPress={() => startPage(PageList.privacy)}>{$t('drawer.privacyPolicy')}</TextView>
+                <TextView style={styles.agreeText}>{$t('sign.linkAndLinkEnd')}</TextView>
+              </View>
+            </View>
+            <Button
+              style={styles.signButton}
+              elevation={1.5}
+              disabled={!this.state.agree}
+              text={$t('sign.btnSignUp')}
+              fontSize={14}
+              onClick={() => {
+                this.onRegister();
+              }}
+            />
+          </View>
+        </ScrollView>
+        <Modal
+          isVisible={this.state.visible}
+          onBackButtonPress={() => this.hideDialog()}
+          {...ModalProps}>
+          <RegisterDialog
+            address={this.state.email}
+            onClose={() => this.hideDialog()}
+          />
+        </Modal>
+      </View>
+    );
+  }
+}
+
+const UploadView = ({url, onPress}) => (
+  <Pressable
+    style={styles.uploadView}
+    onPress={onPress}>
+    { url == ''
+    ? <Image
+        style={styles.uploadIcon}
+        source={require('../../images/icon/ic-add-photo.png')}/>
+    : <Image
+        style={styles.uploadIcon}
+        defaultSource={require('../../images/icon/icon-upload-default.png')}
+        source={{uri: utils.getImageUrl(url)}}/>
+    }
+  </Pressable>
+)
+
+const styles = StyleSheet.create({
+  header: {
+    paddingTop: 56,
+    backgroundColor: colorThemes
+  },
+  backView: {
+    top: 12,
+    zIndex: 5,
+    padding: 16,
+    position: 'absolute',
+    flexDirection: 'row'
+  },
+  backButton: {
+    borderRadius: 60,
+    backgroundColor: colorLight
+  },
+  backButtonView: {
+    height: 43,
+    paddingLeft: 16,
+    paddingRight: 16,
+    alignItems: 'center',
+    flexDirection: 'row'
+  },
+  scollView: {
+    flex: 1
+  },
+  logoView: {
+    flex: 1,
+    paddingRight: 48,
+    alignItems: 'center',
+    justifyContent: 'center'
+  },
+  logoImg: {
+    width:165.09,
+    height: 51.94,
+  },
+  signView: {
+    flex: 1,
+    padding: 16,
+    marginTop: 0,
+    borderTopLeftRadius: 20,
+    borderTopRightRadius: 20,
+    backgroundColor: colorLight
+  },
+  tabView: {
+    marginBottom: 4,
+    borderWidth: 1,
+    borderRadius: 60,
+    overflow: 'hidden',
+    alignItems: 'center',
+    flexDirection: 'row',
+    borderColor: colorPrimary,
+  },
+  tabText: {
+    flex: 1,
+    color: textPrimary,
+    fontSize: 15,
+    textAlign: 'center',
+    ...$padding(10, 0),
+  },
+  tabActive: {
+    color: textLight,
+    fontWeight: 'bold',
+    backgroundColor: colorPrimary
+  },
+  title: {
+    color: textPrimary,
+    fontSize: 18,
+    fontWeight: '700',
+    paddingTop: 4,
+    paddingBottom: 6,
+  },
+  signInput: {
+    marginTop: 16,
+    alignItems: 'center',
+    flexDirection: 'row'
+  },
+  inputLabel: {
+    flex: 1,
+    color: textPrimary,
+    fontSize: 14,
+    marginRight: 4
+  },
+  inputView: {
+    flex: 2,
+    color: textPrimary,
+    fontSize: 14,
+    borderRadius: 5,
+    minHeight: 40,
+    paddingTop: 6,
+    paddingLeft: 12,
+    paddingRight: 12,
+    paddingBottom: 6,
+    backgroundColor: '#F5F5F5'
+  },
+  mobileView: {
+    flex: 2,
+    marginLeft: -12,
+    alignItems: 'center',
+    flexDirection: 'row'
+  },
+  dropView: {
+    fontSize: 16,
+    borderRadius: 4,
+    paddingRight: 4,
+    flexDirection: 'row',
+    alignItems: 'center',
+    backgroundColor: '#F5F5F5'
+  },
+  dropInput: {
+    width: 12,
+    padding: 6,
+    color: textPrimary,
+    minHeight: 40,
+  },
+  countryText: {
+    color: textPrimary,
+    fontSize: 14,
+    paddingRight: 4
+  },
+  dropLayer: {
+    left: 0,
+    right: 0,
+    opacity: 0,
+    position: 'absolute'
+  },
+  contactView: {
+    flex: 1,
+    color: textPrimary,
+    fontSize: 15,
+    borderRadius: 4,
+    minHeight: 40,
+    paddingTop: 6,
+    paddingLeft: 12,
+    paddingRight: 12,
+    paddingBottom: 6,
+    marginLeft: 10,
+    backgroundColor: '#F5F5F5'
+  },
+  passwordView: {
+    flex: 2,
+    marginTop: -8,
+  },
+  strengthView: {
+    marginTop: -4,
+    alignItems: 'center',
+    paddingBottom: 4,
+    flexDirection: 'row'
+  },
+  passwordRole: {
+    color: '#999',
+    fontSize: 10,
+    lineHeight: 13
+  },
+  strengthItem: {
+    width: 14,
+    height: 2.5,
+    marginLeft: 8,
+    borderRadius: 3,
+    backgroundColor: '#CCC'
+  },
+  referView: {
+    padding: 16,
+    marginTop: 24,
+    borderRadius: 6,
+    alignItems: 'center',
+    backgroundColor: '#F5F5F5'
+  },
+  referTitle: {
+    color: textPrimary,
+    fontSize: 18,
+    fontWeight: 'bold'
+  },
+  referText: {
+    color: textPrimary,
+    paddingTop: 4,
+    alignItems: 'flex-end',
+    flexDirection: 'row'
+  },
+  weight: {
+    fontSize: 18,
+    paddingLeft: 4,
+    paddingRight: 4,
+    color: colorAccent,
+    fontWeight: 'bold'
+  },
+  codeView: {
+    paddingTop: 16,
+    alignItems: 'center',
+    flexDirection: 'row'
+  },
+  codeText: {
+    color: textPrimary,
+    fontSize: 16,
+    paddingTop: 6,
+    paddingLeft: 12,
+    paddingRight: 12,
+    paddingBottom: 6,
+    minHeight: 40,
+    marginLeft: 16,
+    borderRadius: 6,
+    textAlign: 'center',
+    backgroundColor: colorLight
+  },
+  signButton: {
+    marginTop: 24,
+    marginBottom: 8,
+  },
+  uploadGroup: {
+    flex: 2,
+    alignItems: 'center',
+    flexDirection: 'row',
+    justifyContent: 'center'
+  },
+  uploadView: {
+    flex: 1,
+    alignItems: 'center'
+  },
+  uploadIcon: {
+    width: $vw(28),
+    height: $vw(28),
+    borderRadius: 6
+  },
+  agreeView: {
+    marginTop: 24,
+    marginBottom: 16,
+    flexDirection: 'row',
+    alignItems: 'flex-start',
+  },
+  agreeTextRow: {
+    flex: 1,
+    paddingTop: 4,
+    paddingLeft: 8,
+    flexWrap: 'wrap',
+    flexDirection: 'row'
+  },
+  agreeText: {
+    color: textPrimary,
+    fontSize: 14,
+    paddingTop: 2,
+    paddingBottom: 2
+  },
+  agreeLink: {
+    ...ui.link,
+    fontSize: 14,
+    paddingTop: 2,
+    paddingBottom: 2,
+    textDecorationLine: 'underline'
+  }
+});

+ 725 - 0
Strides-SPAPP/app/pages/sign/RegisterV3.js

@@ -0,0 +1,725 @@
+/**
+ * 公共用户和司机用户注册页面
+ * @邠心vbe on 2023/02/01
+ */
+import React from 'react';
+import { View, ScrollView, StyleSheet, TextInput, Pressable, Image } from 'react-native';
+import apiUser from '../../api/apiUser';
+import Button from '../../components/Button';
+import { PageList } from '../Router';
+import Dialog from '../../components/Dialog';
+import Modal from 'react-native-modal';
+import { RegisterDialog } from '../charge/InfoDialog';
+import Dropdown from '../../components/Dropdown';
+import ImagePicker from 'react-native-image-crop-picker';
+import apiUpload from '../../api/apiUpload';
+import { ModalProps } from '../../components/BottomModal';
+import { CountryDropNum, GetCountryList } from '../../components/CountryIcon';
+import StrengthView from './StrengthView';
+import CheckBox from '../../components/CheckBox';
+import { UploadThemes } from '../../components/ThemesConfig';
+import TextView from '../../components/TextView';
+import utils from '../../utils/utils';
+
+const options = {
+  width: 300,
+  height: 200,
+  cropping: true,
+  multiple: false,
+  mediaType: 'photo',
+  writeTempFile: false,
+  compressImageQuality: 0.8,
+  compressImageMaxWidth: 720,
+  compressImageMaxHeight: 1280,
+  ...UploadThemes
+}
+
+export default class RegisterV3 extends React.Component {
+  constructor(props) {
+    super(props);
+    this.StrengthView = StrengthView.V1
+    this.state = {
+      agree: true,
+      strength: 0,
+      countryNum: '65',
+      countryShow: false,
+      userInfo: {
+        email: ""
+      },
+      countryNums: [],
+      wrongCount: 0,
+      params: {...this.props.route.params},
+      email: '',
+      password: '',
+      fleetCompanyId: '',
+      visible: false,
+      isFleetDriver: false,
+      pdvImages: ['', ''],
+      companyList: []
+    };
+  }
+
+  componentDidMount() {
+    //console.log(this.state.params);
+    if (this.state.params.isFleetUser) {
+      this.setState({
+        isFleetDriver: true
+      })
+      this.props.navigation.setOptions({
+        headerTitle: $t('route.driverRegister')
+      })
+    }
+    this.getCountryList();
+    this.getCompanyList();
+  }
+
+  applyStrength(text) {
+    this.StrengthView.apply(text, strength => {
+      if (this.state.strength != strength) {
+        this.setState({
+          password: text,
+          strength: strength
+        });
+      } else {
+        this.setState({
+          password: text
+        });
+      }
+    });
+  }
+
+  changeInfo(key, value) {
+    var info = this.state.userInfo;
+    info[key] = value;
+    this.setState({
+      'userInfo': info
+    });
+  }
+
+  getCountryList() {
+    GetCountryList(list => {
+      this.setState({
+        countryNums: list
+      })
+    })
+  }
+
+  getCompanyList() {
+    apiUser.getConmpany().then(res => {
+      if (res.data) {
+        this.setState({
+          companyList: res.data
+        })
+      }
+    }).catch(err => [
+      toastShort(err)
+    ])
+  }
+
+  onRegister() {
+    //console.log('sign up', this.state);
+    var info = this.state.userInfo;
+    if (!info.nickName) {
+      toastShort($t('sign.plsInputDiaplayName'));
+      return;
+    }
+    if (!info.email) {
+      toastShort($t('sign.plsInputEmail'));
+      return;
+    }
+    if (!utils.isValidEmail(info.email)) {
+      toastShort($t('sign.errEmailFormat'));
+      return;
+    }
+    if (!info.phone) {
+      toastShort($t('sign.plsInputContactNo'));
+      return;
+    }
+    if (!/^\d{6,15}$/.test(info.phone)) {
+      toastShort($t('sign.errContactNoFormat'));
+      return;
+    }
+    if (!this.state.password) {
+      toastShort($t('sign.plsInputPassword'));
+      return;
+    }
+    if (this.state.strength < this.StrengthView.maxStrength) {
+      toastShort($t('sign.errPasswordStrong'));
+      return;
+    }
+    if (!info.password) {
+      toastShort($t('sign.plsInputPassword2'));
+      return;
+    }
+    if (info.password != this.state.password) {
+      toastShort($t('sign.errPasswordConfirm'));
+      if (this.state.wrongCount < 3) {
+        this.setState({
+          wrongCount: this.state.wrongCount + 1
+        })
+      }
+      return;
+    }
+    if (this.state.isFleetDriver) {
+      if (!info.pdvLicence) {
+        toastShort($t('sign.plsInputPDVLicence'));
+        return;
+      }
+      if (this.state.pdvImages[0] == '' || this.state.pdvImages[1] == '') {
+        toastShort($t('sign.plsUploadLicencePhotos'));
+        return;
+      }
+    }
+    let param = Object.assign({}, info);
+    //param.phone = this.state.countryNum + info.phone
+    param.callingCode = this.state.countryNum;
+    if (this.state.isFleetDriver) {
+      param.userType = 'Driver';
+      param.pdvLicencePictures = this.state.pdvImages;
+      param.fleetCompanyId = this.state.fleetCompanyId;
+    } else {
+      param.userType = 'Public';
+    }
+    console.log('params', param);
+    Dialog.showProgressDialog();
+    apiUser.register(param).then(res => {
+      Dialog.dismissLoading();
+      //toastShort('Sign up successfully!');
+      if (isIOS) {
+        setTimeout(() => {
+          this.setState({
+            email: param.email,
+            visible: true
+          });
+        }, 500);
+      } else {
+        this.setState({
+          email: param.email,
+          visible: true
+        });
+      }
+      //this.backToLogin();
+    }).catch(err => {
+      toastShort(err);
+      Dialog.dismissLoading();
+    });
+  }
+  
+  getBackTopPosition() {
+    return isIOS ? statusHeight - 16 : 8;
+  }
+  
+  backToLogin() {
+    if (this.state.params.actionLogin) {
+      goBack()
+      startPage(PageList.login);
+    } else {
+      goBack();
+    }
+  }
+
+  uploadImage(index) {
+    ImagePicker.openPicker({
+      ...options,
+      cropperToolbarTitle: $t('common.cropperTitle')
+    }).then(image => {
+      if (image.path) {
+        apiUpload.uploadImage(image.path, image.mime, 'PDVL').then(res => {
+          if (res.success && res.data.picturePath) {
+            let imageUrl = this.state.pdvImages;
+            imageUrl[index] = res.data.picturePath;
+            this.setState({
+              pdvImages: imageUrl
+            });
+            toastShort($t('common.uploadSuccess'));
+          } else {
+            toastShort($t('common.updateFailed'));
+          }
+        }).catch(err => {
+          toastShort(err);
+        });
+      }
+    }).catch(err1 => {
+      //console.log(err1);
+    });
+  }
+
+  changeAgree(ag) {
+    this.setState({
+      agree: ag
+    })
+  }
+
+  hideDialog() {
+    this.setState({
+      visible: false
+    });
+    this.backToLogin();
+  }
+
+  render() {
+    return (
+      <View style={styles.container}>
+        <ScrollView
+          style={styles.scollView}
+          keyboardShouldPersistTaps={isIOS ? 'never' : 'handled'}>
+          <View style={styles.signView}>
+            {/* <View style={styles.tabView}>
+              <Text 
+                style={[
+                  styles.tabText, 
+                  this.state.isFleetDriver ? {} : styles.tabActive
+                ]}
+                onPress={() => this.changeTab(false)}
+              >Public Users</Text>
+              <Text
+                style={[
+                  styles.tabText,
+                  this.state.isFleetDriver ? styles.tabActive: {}
+                ]}
+                onPress={() => this.changeTab(true)}
+              >Fleet/PH Drivers</Text>
+            </View> */}
+            <View style={styles.signInput}>
+              <TextView style={styles.inputLabel}>{$t('sign.labelDisplayName')}</TextView>
+              <TextInput
+                style={styles.inputView} 
+                placeholder={$t('sign.labelDisplayName')}
+                placeholderTextColor={textPlacehoder}
+                maxLength={50}
+                onChangeText={v => this.changeInfo('nickName', v)}
+              />
+            </View>
+            <View style={styles.signInput}>
+              <TextView style={styles.inputLabel}>{$t('sign.labelEmail')}</TextView>
+              <TextInput
+                style={styles.inputView}
+                placeholder={$t('sign.labelEmail')}
+                placeholderTextColor={textPlacehoder}
+                maxLength={50}
+                keyboardType="email-address"
+                textContentType='emailAddress'
+                onChangeText={v => this.changeInfo('email', v)}
+              />
+            </View>
+            <View style={styles.signInput}>
+              <TextView style={styles.inputLabel}>{$t('sign.labelPhoneNumber')}</TextView>
+              <View style={styles.mobileView}>
+                <View style={styles.dropView}>
+                  <TextInput style={styles.dropInput} editable={false}/>
+                  <TextView style={styles.countryText}>{"+" + this.state.countryNum}</TextView>
+                  <MaterialIcons name={'arrow-drop-down'} size={24} color={colorDark}/>
+                  <Dropdown
+                    style={styles.dropLayer}
+                    title={$t('sign.labelCountry')}
+                    prefixText="+"
+                    list={this.state.countryNums}
+                    value={this.state.countryNum}
+                    nameKey='countryNum'
+                    valueKey='countryNum'
+                    onChange={(value, index)=> {
+                      this.setState({
+                        countryNum: value
+                      })
+                    }}
+                    customerItemView={
+                      (item, index, onClick) => 
+                      <CountryDropNum
+                        key={index} 
+                        country={item}
+                        value={this.state.countryNum}
+                        onClick={onClick}/>
+                    }/>
+                </View>
+                <TextInput
+                  style={styles.contactView}
+                  placeholder={$t('sign.labelMobileNumber')}
+                  placeholderTextColor={textPlacehoder}
+                  keyboardType='phone-pad'
+                  maxLength={15}
+                  onChangeText={v => this.changeInfo('phone', v)}
+                />
+              </View>
+            </View>
+            <View style={styles.signInput}>
+              <TextView style={styles.inputLabel}>{$t('sign.labelCreatePassword')}</TextView>
+              <TextInput
+                secureTextEntry={this.state.wrongCount < 3}
+                style={styles.inputView}
+                placeholder={$t('sign.labelPassword')}
+                placeholderTextColor={textPlacehoder}
+                maxLength={20}
+                onChangeText={(value) => {
+                  this.applyStrength(value);
+                }}
+              />
+            </View>
+            <View style={styles.signInput}>
+              <TextView style={styles.inputLabel}></TextView>
+              <View style={styles.passwordView}>
+                <this.StrengthView.VIEW strength={this.state.strength}/>
+              </View>
+            </View>
+            <View style={styles.signInput}>
+              <TextView style={styles.inputLabel}>{$t('sign.labelConfirmPassword')}</TextView>
+              <TextInput
+                secureTextEntry={this.state.wrongCount < 3}
+                style={styles.inputView}
+                placeholder={$t('sign.labelPassword')}
+                placeholderTextColor={textPlacehoder}
+                maxLength={20}
+                onChangeText={v => this.changeInfo('password', v)}
+              />
+            </View>
+            { this.state.isFleetDriver &&
+              <>
+              <View style={styles.signInput}>
+                <TextView style={styles.inputLabel}>{$t('sign.labelYourCompany')}</TextView>
+                <Dropdown
+                  style={[styles.inputView, ui.flexc]}
+                  title={$t('sign.labelCompany')}
+                  list={this.state.companyList}
+                  value={this.state.fleetCompanyId}
+                  valueKey='fleetCompanyId'
+                  nameKey='fleetCompanyName'
+                  onChange={(value, index)=> {
+                    this.setState({
+                      fleetCompanyId: value
+                    })
+                  }}/>
+              </View>
+              <View style={styles.signInput}>
+                <TextView style={styles.inputLabel}>{$t('sign.labelPDVLicence')}</TextView>
+                <TextInput
+                  style={styles.inputView}
+                  placeholder={$t('sign.hintPDVLicence')}
+                  placeholderTextColor={textPlacehoder}
+                  maxLength={20}
+                  onChangeText={v => this.changeInfo('pdvLicence', v)}
+                />
+              </View>
+              <View style={styles.signInput}>
+                <TextView style={styles.inputLabel}>{$t('sign.labelPDVPhotos')}</TextView>
+                <View style={styles.uploadGroup}>
+                  { this.state.pdvImages.map((item, index) => (
+                    <UploadView
+                      key={index}
+                      onPress={() => this.uploadImage(index)}
+                      url={item}/>
+                    )) 
+                  }
+                </View>
+              </View>
+              </>
+            }
+            {/* <View style={styles.referView}>
+              <Text style={styles.referTitle}>Have a referral code?</Text>
+              <View style={styles.referText}>
+                <Text>You'll get</Text>
+                <Text style={styles.weight}>$5</Text>
+                <Text>Credit as registration bonus!</Text>
+              </View>
+              <View style={styles.codeView}>
+                <Text style={{ color: textPrimary, fontSize: 16 }}>Referral Code</Text>
+                <TextInput
+                  style={styles.codeText}
+                  maxLength={6}
+                  placeholder='Referral Code'
+                  placeholderTextColor={textPlacehoder}
+                  onChangeText={v => this.changeInfo('referralCode', v)}
+                />
+              </View>
+            </View> */}
+            <View style={styles.agreeView}>
+              <CheckBox
+                value={this.state.agree}
+                onValueChange={v => this.changeAgree(v)}
+              />
+              <View style={styles.agreeTextRow}>
+                <TextView style={styles.agreeText} onPress={() => this.changeAgree(!this.state.agree)}>
+                  {$t('sign.iHaveReadAndAgree')}
+                </TextView>
+                <TextView style={styles.agreeLink} onPress={() => startPage(PageList.condition)}>{$t('drawer.termsOfUse')}</TextView>
+                <TextView style={styles.agreeText}>{' '}</TextView>
+                <TextView style={styles.agreeText}>{$t('sign.linkAndLink')}</TextView>
+                <TextView style={styles.agreeLink} onPress={() => startPage(PageList.privacy)}>{$t('drawer.privacyPolicy')}</TextView>
+                <TextView style={styles.agreeText}>{$t('sign.linkAndLinkEnd')}</TextView>
+              </View>
+            </View>
+            <Button
+              style={styles.signButton}
+              elevation={1.5}
+              disabled={!this.state.agree}
+              text={$t('sign.btnSignUp')}
+              fontSize={14}
+              onClick={() => {
+                this.onRegister();
+              }}
+            />
+          </View>
+        </ScrollView>
+        <Modal
+          isVisible={this.state.visible}
+          onBackButtonPress={() => this.hideDialog()}
+          {...ModalProps}>
+          <RegisterDialog
+            address={this.state.email}
+            onClose={() => this.hideDialog()}
+          />
+        </Modal>
+      </View>
+    );
+  }
+}
+
+const UploadView = ({url, onPress}) => (
+  <Pressable
+    style={styles.uploadView}
+    onPress={onPress}>
+    { url == ''
+    ? <Image
+        style={styles.uploadIcon}
+        source={require('../../images/icon/ic-add-photo.png')}/>
+    : <Image
+        style={styles.uploadIcon}
+        defaultSource={require('../../images/icon/icon-upload-default.png')}
+        source={{uri: utils.getImageUrl(url)}}/>
+    }
+  </Pressable>
+)
+
+const styles = StyleSheet.create({
+  container: {
+    flex: 1,
+    backgroundColor: colorLight
+  },
+  header: {
+    paddingTop: 56,
+    backgroundColor: colorAccent
+  },
+  backView: {
+    top: 12,
+    zIndex: 5,
+    padding: 16,
+    position: 'absolute',
+    flexDirection: 'row'
+  },
+  backButton: {
+    borderRadius: 60,
+    backgroundColor: colorLight
+  },
+  backButtonView: {
+    height: 43,
+    paddingLeft: 16,
+    paddingRight: 16,
+    alignItems: 'center',
+    flexDirection: 'row'
+  },
+  scollView: {
+    flex: 1
+  },
+  logoView: {
+    paddingTop: 32,
+    paddingBottom: 56,
+    alignItems: 'center'
+  },
+  logoImg: {
+    width:165.09,
+    height: 51.94,
+  },
+  signView: {
+    flex: 1,
+    padding: 16,
+    //marginTop: -30,
+    borderTopLeftRadius: 20,
+    borderTopRightRadius: 20,
+    backgroundColor: colorLight
+  },
+  tabView: {
+    marginBottom: 4,
+    borderWidth: 1,
+    borderRadius: 6,
+    overflow: 'hidden',
+    alignItems: 'center',
+    flexDirection: 'row',
+    borderColor: colorAccent,
+  },
+  tabText: {
+    flex: 1,
+    color: textPrimary,
+    fontSize: 15,
+    textAlign: 'center',
+    ...$padding(10, 0),
+  },
+  tabActive: {
+    fontWeight: 'bold',
+    backgroundColor: colorAccent
+  },
+  title: {
+    color: textPrimary,
+    fontSize: 18,
+    fontWeight: '700',
+    paddingTop: 4,
+    paddingBottom: 6,
+  },
+  signInput: {
+    marginTop: 16,
+    alignItems: 'center',
+    flexDirection: 'row'
+  },
+  inputLabel: {
+    flex: 1,
+    color: textPrimary,
+    fontSize: 14,
+    marginRight: 4
+  },
+  inputView: {
+    flex: 2,
+    color: textPrimary,
+    fontSize: 14,
+    borderRadius: 5,
+    minHeight: 40,
+    paddingTop: 6,
+    paddingLeft: 12,
+    paddingRight: 12,
+    paddingBottom: 6,
+    backgroundColor: '#F5F5F5'
+  },
+  mobileView: {
+    flex: 2,
+    marginLeft: -12,
+    alignItems: 'center',
+    flexDirection: 'row'
+  },
+  dropView: {
+    fontSize: 16,
+    borderRadius: 4,
+    paddingRight: 4,
+    flexDirection: 'row',
+    alignItems: 'center',
+    backgroundColor: '#F5F5F5'
+  },
+  dropInput: {
+    width: 12,
+    padding: 6,
+    color: textPrimary,
+    minHeight: 40,
+  },
+  countryText: {
+    color: textPrimary,
+    fontSize: 14,
+    paddingRight: 4
+  },
+  dropLayer: {
+    left: 0,
+    right: 0,
+    opacity: 0,
+    position: 'absolute'
+  },
+  contactView: {
+    flex: 1,
+    color: textPrimary,
+    fontSize: 15,
+    borderRadius: 4,
+    minHeight: 40,
+    paddingTop: 6,
+    paddingLeft: 12,
+    paddingRight: 12,
+    paddingBottom: 6,
+    marginLeft: 10,
+    backgroundColor: '#F5F5F5'
+  },
+  passwordView: {
+    flex: 2,
+    marginTop: -8,
+  },
+  referView: {
+    padding: 16,
+    marginTop: 24,
+    borderRadius: 6,
+    alignItems: 'center',
+    backgroundColor: '#F5F5F5'
+  },
+  referTitle: {
+    color: textPrimary,
+    fontSize: 18,
+    fontWeight: 'bold'
+  },
+  referText: {
+    color: textPrimary,
+    paddingTop: 4,
+    alignItems: 'flex-end',
+    flexDirection: 'row'
+  },
+  weight: {
+    fontSize: 18,
+    paddingLeft: 4,
+    paddingRight: 4,
+    color: colorAccent,
+    fontWeight: 'bold'
+  },
+  codeView: {
+    paddingTop: 16,
+    alignItems: 'center',
+    flexDirection: 'row'
+  },
+  codeText: {
+    color: textPrimary,
+    fontSize: 16,
+    paddingTop: 6,
+    paddingLeft: 12,
+    paddingRight: 12,
+    paddingBottom: 6,
+    minHeight: 40,
+    marginLeft: 16,
+    borderRadius: 6,
+    textAlign: 'center',
+    backgroundColor: colorLight
+  },
+  signButton: {
+    marginTop: 24,
+    marginBottom: 8,
+  },
+  uploadGroup: {
+    flex: 2,
+    alignItems: 'center',
+    flexDirection: 'row',
+    justifyContent: 'center'
+  },
+  uploadView: {
+    flex: 1,
+    alignItems: 'center'
+  },
+  uploadIcon: {
+    width: $vw(28),
+    height: $vw(28),
+    borderRadius: 6
+  },
+  agreeView: {
+    marginTop: 24,
+    marginBottom: 16,
+    flexDirection: 'row',
+    alignItems: 'flex-start',
+  },
+  agreeTextRow: {
+    flex: 1,
+    paddingTop: 4,
+    paddingLeft: 8,
+    flexWrap: 'wrap',
+    flexDirection: 'row'
+  },
+  agreeText: {
+    color: textPrimary,
+    fontSize: 14,
+    paddingTop: 2,
+    paddingBottom: 2
+  },
+  agreeLink: {
+    ...ui.link,
+    fontSize: 14,
+    paddingTop: 2,
+    paddingBottom: 2,
+    textDecorationLine: 'underline'
+  }
+});
+ 

+ 885 - 0
Strides-SPAPP/app/pages/sign/RegisterV4.js

@@ -0,0 +1,885 @@
+/**
+ * 带OTP的注册页面
+ * @邠心vbe on 2023/02/06
+ */
+import React from 'react';
+import { View, ScrollView, StyleSheet, TextInput, Pressable, Image, Linking } from 'react-native';
+import apiUser from '../../api/apiUser';
+import Button from '../../components/Button';
+import { PageList } from '../Router';
+import Dialog from '../../components/Dialog';
+import app from '../../../app.json';
+import Dropdown from '../../components/Dropdown';
+import ImagePicker from 'react-native-image-crop-picker';
+import apiUpload from '../../api/apiUpload';
+import { CountryDropCode, CountryDropNum, GetCountryList } from '../../components/CountryIcon';
+import StrengthView from './StrengthView';
+import CheckBox from '../../components/CheckBox';
+import { UploadThemes } from '../../components/ThemesConfig';
+import TextView from '../../components/TextView';
+import utils from '../../utils/utils';
+
+const options = {
+  width: 300,
+  height: 200,
+  cropping: true,
+  multiple: false,
+  mediaType: 'photo',
+  writeTempFile: false,
+  compressImageQuality: 0.8,
+  compressImageMaxWidth: 720,
+  compressImageMaxHeight: 1280,
+  ...UploadThemes
+}
+
+export default class RegisterV4 extends React.Component {
+  constructor(props) {
+    super(props);
+    this.StrengthView = StrengthView.V4
+    this.state = {
+      agree: false,
+      agree2: false,
+      strength: 0,
+      countryNum: '65',
+      countryCode: "SG",
+      userInfo: {
+        email: "",
+        verificationCode: ""
+      },
+      countryList: [],
+      wrongCount: 0,
+      params: {...this.props.route.params},
+      email: '',
+      password: '',
+      fleetCompanyId: '',
+      sendMinutes: 0,
+      isFleetDriver: false,
+      pdvImages: ['', ''],
+      companyList: []
+    };
+    this.canChangeCalling = true;
+  }
+
+  componentDidMount() {
+    //console.log(this.state.params);
+    if (this.state.params.isFleetUser) {
+      this.setState({
+        isFleetDriver: true
+      })
+      this.props.navigation.setOptions({
+        headerTitle: $t('route.driverRegister')
+      })
+    }
+    this.getCountryList();
+    this.getCompanyList();
+  }
+
+  applyStrength(text) {
+    this.StrengthView.apply(text, strength => {
+      if (this.state.strength != strength) {
+        this.setState({
+          password: text,
+          strength: strength
+        });
+      } else {
+        this.setState({
+          password: text
+        });
+      }
+    });
+  }
+
+  changeInfo(key, value) {
+    var info = this.state.userInfo;
+    info[key] = value;
+    this.setState({
+      'userInfo': info
+    });
+  }
+
+  getCountryList() {
+    GetCountryList(list => {
+      this.setState({
+        countryList: list
+      })
+    })
+  }
+
+  getCompanyList() {
+    apiUser.getConmpany().then(res => {
+      if (res.data) {
+        this.setState({
+          companyList: res.data
+        })
+      }
+    }).catch(err => [
+      toastShort(err)
+    ])
+  }
+
+  sendVerification() {
+    var info = this.state.userInfo;
+    if (!info.email) {
+      toastShort($t('sign.plsInputEmail'));
+      return;
+    }
+    if (!utils.isValidEmail(info.email)) {
+      toastShort($t('sign.errEmailFormat'));
+      return;
+    }
+    Dialog.showProgressDialog()
+    apiUser.sendVerificationCodeV2({email: info.email, type: "register"}).then(res => {
+      Dialog.dismissLoading()
+      //this.state.sendMinutes = 60;
+      this.state.sendMinutes = res.data?.resendTime ?? 60;
+      toastShort($t('sign.sendOTPSuccess'));
+      this.contdownTime();
+    }).catch(err => {
+      Dialog.dismissLoading()
+      toastShort(err);
+    });
+  }
+
+  contdownTime() {
+    if (this.state.sendMinutes > 0) {
+      this.setState({
+        sendMinutes: this.state.sendMinutes - 1
+      })
+      setTimeout(() => {
+        this.contdownTime();
+      }, 1000);
+    }
+  }
+
+  onRegister() {
+    //console.log('sign up', this.state);
+    var info = this.state.userInfo;
+    if (!info.nickName) {
+      toastShort($t('sign.plsInputDiaplayName'));
+      return;
+    }
+    if (!info.email) {
+      toastShort($t('sign.plsInputEmail'));
+      return;
+    }
+    if (!utils.isValidEmail(info.email)) {
+      toastShort($t('sign.errEmailFormat'));
+      return;
+    }
+    if (!info.verificationCode) {
+      toastShort($t('sign.plsInputOTP'));
+      return;
+    }
+    if (!info.phone) {
+      toastShort($t('sign.plsInputContactNo'));
+      return;
+    }
+    if (!/^\d{6,15}$/.test(info.phone)) {
+      toastShort($t('sign.errContactNoFormat'));
+      return;
+    }
+    if (!this.state.password) {
+      toastShort($t('sign.plsInputPassword'));
+      return;
+    }
+    if (this.state.strength < this.StrengthView.maxStrength) {
+      toastShort($t('sign.errPasswordStrong'));
+      return;
+    }
+    if (!info.password) {
+      toastShort($t('sign.plsInputPassword2'));
+      return;
+    }
+    if (info.password != this.state.password) {
+      toastShort($t('sign.errPasswordConfirm'));
+      if (this.state.wrongCount < 3) {
+        this.setState({
+          wrongCount: this.state.wrongCount + 1
+        })
+      }
+      return;
+    }
+    if (this.state.isFleetDriver) {
+      if (!info.pdvLicence) {
+        toastShort($t('sign.plsInputPDVLicence'));
+        return;
+      }
+      if (this.state.pdvImages[0] == '' || this.state.pdvImages[1] == '') {
+        toastShort($t('sign.plsUploadLicencePhotos'));
+        return;
+      }
+    }
+    let param = Object.assign({}, info);
+    //param.phone = this.state.countryNum + info.phone
+    param.callingCode = this.state.countryNum;
+    param.countryCode = this.state.countryCode;
+    if (this.state.isFleetDriver) {
+      param.userType = 'Driver';
+      param.pdvLicencePictures = this.state.pdvImages;
+      param.fleetCompanyId = this.state.fleetCompanyId;
+    } else {
+      param.userType = 'Public';
+    }
+    param.otp = param.verificationCode;
+    console.log('params', param);
+    Dialog.showProgressDialog();
+    apiUser.register(param).then(res => {
+      Dialog.dismissLoading();
+      //toastShort('Sign up successfully!');
+      if (isIOS) {
+        setTimeout(() => {
+          this.showSuccessDialog();
+        }, 600);
+      } else {
+        this.showSuccessDialog();
+      }
+    }).catch(err => {
+      toastShort(err);
+      Dialog.dismissLoading();
+    });
+  }
+
+  showSuccessDialog() {
+    Dialog.showResultDialog($t('sign.signUpSuccess'), $t('nav.ok'),
+      () => {
+        this.backToLogin();
+      }
+    )
+  }
+  
+  getBackTopPosition() {
+    return isIOS ? statusHeight - 16 : 8;
+  }
+  
+  backToLogin() {
+    if (this.state.params.actionLogin) {
+      goBack()
+      startPage(PageList.login);
+    } else {
+      goBack();
+    }
+  }
+
+  uploadImage(index) {
+    ImagePicker.openPicker({
+      ...options,
+      cropperToolbarTitle: $t('common.cropperTitle')
+    }).then(image => {
+      if (image.path) {
+        apiUpload.uploadImage(image.path, image.mime, 'PDVL').then(res => {
+          if (res.success && res.data.picturePath) {
+            let imageUrl = this.state.pdvImages;
+            imageUrl[index] = res.data.picturePath;
+            this.setState({
+              pdvImages: imageUrl
+            });
+            toastShort($t('common.uploadSuccess'));
+          } else {
+            toastShort($t('common.updateFailed'));
+          }
+        }).catch(err => {
+          toastShort(err);
+        });
+      }
+    }).catch(err1 => {
+      //console.log(err1);
+    });
+  }
+
+  changeAgree(ag) {
+    this.setState({
+      agree: ag
+    })
+  }
+
+  changeAgree2(ag) {
+    this.setState({
+      agree2: ag
+    })
+  }
+
+  changeCountry(value, index) {
+    this.setState({
+      countryCode: value
+    })
+    if (this.canChangeCalling) {
+      const country = this.state.countryList[index]
+      this.changeCalling(country.countryNum, -1);
+    }
+  }
+
+  changeCalling(value, index) {
+    this.setState({
+      countryNum: value
+    })
+    if (index >= 0) {
+      this.canChangeCalling = false;
+    }
+  }
+
+  toPrivayPolicy() {
+    Linking.openURL(app.storeUrl.privacyPolicyUrl)
+  }
+
+  toUserTerms() {
+    Linking.openURL(app.storeUrl.termsUseUrl)
+  }
+
+  render() {
+    return (
+      <View style={styles.container}>
+        <ScrollView
+          style={styles.scollView}
+          keyboardShouldPersistTaps={isIOS ? 'never' : 'handled'}>
+          <View style={styles.signView}>
+            {/* <View style={styles.tabView}>
+              <Text 
+                style={[
+                  styles.tabText, 
+                  this.state.isFleetDriver ? {} : styles.tabActive
+                ]}
+                onPress={() => this.changeTab(false)}
+              >Public Users</Text>
+              <Text
+                style={[
+                  styles.tabText,
+                  this.state.isFleetDriver ? styles.tabActive: {}
+                ]}
+                onPress={() => this.changeTab(true)}
+              >Fleet/PH Drivers</Text>
+            </View> */}
+            <View style={styles.signInput}>
+              <TextView style={styles.inputLabel}>{$t('sign.labelDisplayName')}</TextView>
+              <TextInput
+                style={styles.inputView} 
+                placeholder={$t('sign.labelDisplayName')}
+                placeholderTextColor={textPlacehoder}
+                maxLength={50}
+                onChangeText={v => this.changeInfo('nickName', v)}
+              />
+            </View>
+            <View style={styles.signInput}>
+              <TextView style={styles.inputLabel}>{$t('sign.labelEmail')}</TextView>
+              <TextInput
+                style={styles.inputView}
+                placeholder={$t('sign.labelEmail')}
+                placeholderTextColor={textPlacehoder}
+                maxLength={50}
+                keyboardType="email-address"
+                textContentType='emailAddress'
+                onChangeText={v => this.changeInfo('email', v)}
+              />
+            </View>
+            <View style={styles.signInput}>
+              <TextView style={[styles.inputLabel, {flex: 2}]}>{$t('sign.labelValidateEmail')}</TextView>
+              <TextInput
+                style={[styles.inputView, {flex: 2.2, marginLeft: 2}]}
+                placeholder={$t('sign.labelOtp')}
+                placeholderTextColor={textPlacehoder}
+                maxLength={6}
+                keyboardType="number-pad"
+                textContentType="telephoneNumber"
+                onChangeText={v => this.changeInfo('verificationCode', v)}
+              />
+              <Button
+                text={this.state.sendMinutes > 0 ? (this.state.sendMinutes + " s") : $t('sign.btnSendOTP')}
+                style={styles.sendBtn}
+                disabled={this.state.sendMinutes > 0}
+                viewStyle={styles.sendBtnView}
+                textStyle={styles.sendBtnText}
+                onClick={() => this.sendVerification()}
+              />
+            </View>
+            <View style={styles.signInput}>
+              <TextView style={styles.inputLabel}>{$t('sign.labelCountry')}</TextView>
+              <Dropdown
+                style={styles.selectView}
+                title={$t('sign.labelCountry')}
+                list={this.state.countryList}
+                value={this.state.countryCode}
+                nameKey='countryName'
+                valueKey='countryCode'
+                iconColor={colorDark}
+                onChange={(value, index)=> this.changeCountry(value, index)}
+                customerItemView={
+                  (item, index, onClick) => 
+                  <CountryDropCode
+                    key={index} 
+                    country={item}
+                    value={this.state.countryCode}
+                    onClick={onClick}/>
+                }/>
+            </View>
+            <View style={styles.signInput}>
+              <TextView style={styles.inputLabel}>{$t('sign.labelPhoneNumber')}</TextView>
+              <View style={styles.mobileView}>
+                <View style={styles.dropView}>
+                  <TextInput style={styles.dropInput} editable={false}/>
+                  <TextView style={styles.countryText}>{"+" + this.state.countryNum}</TextView>
+                  <MaterialIcons name={'arrow-drop-down'} size={24} color={colorDark}/>
+                  <Dropdown
+                    style={styles.dropLayer}
+                    prefixText="+"
+                    list={this.state.countryList}
+                    value={this.state.countryNum}
+                    nameKey='countryNum'
+                    valueKey='countryNum'
+                    onChange={(value, index)=> this.changeCalling(value, index)}
+                    customerItemView={
+                      (item, index, onClick) => 
+                      <CountryDropNum
+                        key={index} 
+                        country={item}
+                        value={this.state.countryNum}
+                        onClick={onClick}/>
+                    }/>
+                </View>
+                <TextInput
+                  style={styles.contactView}
+                  placeholder={$t('sign.labelMobileNumber')}
+                  placeholderTextColor={textPlacehoder}
+                  keyboardType='phone-pad'
+                  maxLength={15}
+                  onChangeText={v => this.changeInfo('phone', v)}
+                />
+              </View>
+            </View>
+            <View style={styles.signInput}>
+              <TextView style={styles.inputLabel}>{$t('sign.labelCreatePassword')}</TextView>
+              <TextInput
+                secureTextEntry={this.state.wrongCount < 3}
+                style={styles.inputView}
+                placeholder={$t('sign.labelPassword')}
+                placeholderTextColor={textPlacehoder}
+                maxLength={20}
+                onChangeText={(value) => {
+                  this.applyStrength(value);
+                }}
+              />
+            </View>
+            <View style={styles.signInput}>
+              <TextView style={styles.inputLabel}></TextView>
+              <View style={styles.passwordView}>
+                <this.StrengthView.VIEW strength={this.state.strength}/>
+              </View>
+            </View>
+            <View style={styles.signInput}>
+              <TextView style={styles.inputLabel}>{$t('sign.labelConfirmPassword')}</TextView>
+              <TextInput
+                secureTextEntry={this.state.wrongCount < 3}
+                style={styles.inputView}
+                placeholder={$t('sign.labelPassword')}
+                placeholderTextColor={textPlacehoder}
+                maxLength={20}
+                onChangeText={v => this.changeInfo('password', v)}
+              />
+            </View>
+            { this.state.isFleetDriver &&
+              <>
+              <View style={styles.signInput}>
+                <TextView style={styles.inputLabel}>{$t('sign.labelYourCompany')}</TextView>
+                <Dropdown
+                  style={[styles.inputView, ui.flexc]}
+                  title={$t('sign.labelCompany')}
+                  list={this.state.companyList}
+                  value={this.state.fleetCompanyId}
+                  valueKey='fleetCompanyId'
+                  nameKey='fleetCompanyName'
+                  onChange={(value, index)=> {
+                    this.setState({
+                      fleetCompanyId: value
+                    })
+                  }}/>
+              </View>
+              <View style={styles.signInput}>
+                <TextView style={styles.inputLabel}>{$t('sign.labelPDVLicence')}</TextView>
+                <TextInput
+                  style={styles.inputView}
+                  placeholder={$t('sign.hintPDVLicence')}
+                  placeholderTextColor={textPlacehoder}
+                  maxLength={20}
+                  onChangeText={v => this.changeInfo('pdvLicence', v)}
+                />
+              </View>
+              <View style={styles.signInput}>
+                <TextView style={styles.inputLabel}>{$t('sign.labelPDVPhotos')}</TextView>
+                <View style={styles.uploadGroup}>
+                  { this.state.pdvImages.map((item, index) => (
+                    <UploadView
+                      key={index}
+                      onPress={() => this.uploadImage(index)}
+                      url={item}/>
+                    )) 
+                  }
+                </View>
+              </View>
+              </>
+            }
+            {/* <View style={styles.referView}>
+              <Text style={styles.referTitle}>Have a referral code?</Text>
+              <View style={styles.referText}>
+                <Text>You'll get</Text>
+                <Text style={styles.weight}>$5</Text>
+                <Text>Credit as registration bonus!</Text>
+              </View>
+              <View style={styles.codeView}>
+                <Text style={{ color: textPrimary, fontSize: 16 }}>Referral Code</Text>
+                <TextInput
+                  style={styles.codeText}
+                  maxLength={6}
+                  placeholder='Referral Code'
+                  placeholderTextColor={textPlacehoder}
+                  onChangeText={v => this.changeInfo('referralCode', v)}
+                />
+              </View>
+            </View> */}
+            <View style={styles.consentView}>
+              <TextView style={styles.consentTitle}>Consent and purpose notification</TextView>
+              <TextView style={styles.agreeText}>
+              We collect, use and disclose your personal data for the purpose of providing Electric Vehicle (EV) charging services to you. 
+              </TextView>
+            </View>
+            <View style={styles.agreeView}>
+              <CheckBox
+                value={this.state.agree2}
+                onValueChange={v => this.changeAgree2(v)}
+              />
+              <View style={styles.agreeTextRow}>
+                <TextView style={styles.agreeText} onPress={() => this.changeAgree2(!this.state.agree2)}>
+                  I consent to Strides YTL Pte. Ltd. to collect, use and disclose my personal data for the above purposes and in accordance with the SMRT Corporation’s Privacy Statement and Personal Data Protection Act 2012.
+                </TextView>
+              </View>
+            </View>
+            <View style={styles.agreeView2}>
+              <CheckBox
+                value={this.state.agree}
+                onValueChange={v => this.changeAgree(v)}
+              />
+              <View style={styles.agreeTextRow}>
+                <TextView style={styles.agreeText} onPress={() => this.changeAgree(!this.state.agree)}>
+                  {$t('sign.iHaveReadAndAgree')}
+                </TextView>
+                <TextView style={styles.agreeLink} onPress={() => this.toUserTerms()}>{$t('drawer.termsOfUse')}</TextView>
+                <TextView style={styles.agreeText}>{' '}</TextView>
+                <TextView style={styles.agreeText}>{$t('sign.linkAndLink')}</TextView>
+                <TextView style={styles.agreeLink} onPress={() => this.toPrivayPolicy()}>{$t('drawer.privacyPolicy')}</TextView>
+                <TextView style={styles.agreeText}>{$t('sign.linkAndLinkEnd')}</TextView>
+              </View>
+            </View>
+            <Button
+              style={styles.signButton}
+              elevation={1.5}
+              disabled={!this.state.agree || !this.state.agree2}
+              text={$t('sign.btnSignUp')}
+              fontSize={14}
+              onClick={() => {
+                this.onRegister();
+              }}
+            />
+          </View>
+        </ScrollView>
+      </View>
+    );
+  }
+}
+
+const UploadView = ({url, onPress}) => (
+  <Pressable
+    style={styles.uploadView}
+    onPress={onPress}>
+    { url == ''
+    ? <Image
+        style={styles.uploadIcon}
+        source={require('../../images/icon/ic-add-photo.png')}/>
+    : <Image
+        style={styles.uploadIcon}
+        defaultSource={require('../../images/icon/icon-upload-default.png')}
+        source={{uri: utils.getImageUrl(url)}}/>
+    }
+  </Pressable>
+)
+
+const styles = StyleSheet.create({
+  container: {
+    flex: 1,
+    backgroundColor: colorLight
+  },
+  header: {
+    paddingTop: 56,
+    backgroundColor: colorAccent
+  },
+  backView: {
+    top: 12,
+    zIndex: 5,
+    padding: 16,
+    position: 'absolute',
+    flexDirection: 'row'
+  },
+  backButton: {
+    borderRadius: 60,
+    backgroundColor: colorLight
+  },
+  backButtonView: {
+    height: 43,
+    paddingLeft: 16,
+    paddingRight: 16,
+    alignItems: 'center',
+    flexDirection: 'row'
+  },
+  scollView: {
+    flex: 1
+  },
+  logoView: {
+    paddingTop: 32,
+    paddingBottom: 56,
+    alignItems: 'center'
+  },
+  logoImg: {
+    width:165.09,
+    height: 51.94,
+  },
+  signView: {
+    flex: 1,
+    padding: 16,
+    //marginTop: -30,
+    borderTopLeftRadius: 20,
+    borderTopRightRadius: 20,
+    backgroundColor: colorLight
+  },
+  tabView: {
+    marginBottom: 4,
+    borderWidth: 1,
+    borderRadius: 6,
+    overflow: 'hidden',
+    alignItems: 'center',
+    flexDirection: 'row',
+    borderColor: colorAccent,
+  },
+  tabText: {
+    flex: 1,
+    color: textPrimary,
+    fontSize: 15,
+    textAlign: 'center',
+    ...$padding(10, 0),
+  },
+  tabActive: {
+    fontWeight: 'bold',
+    backgroundColor: colorAccent
+  },
+  title: {
+    color: textPrimary,
+    fontSize: 18,
+    fontWeight: '700',
+    paddingTop: 4,
+    paddingBottom: 6,
+  },
+  signInput: {
+    marginTop: 16,
+    alignItems: 'center',
+    flexDirection: 'row'
+  },
+  inputLabel: {
+    flex: 1,
+    color: textPrimary,
+    fontSize: 14,
+    marginRight: 4
+  },
+  inputView: {
+    flex: 2,
+    color: textPrimary,
+    fontSize: 14,
+    borderRadius: 5,
+    minHeight: 40,
+    paddingTop: 6,
+    paddingLeft: 12,
+    paddingRight: 12,
+    paddingBottom: 6,
+    backgroundColor: '#F5F5F5'
+  },
+  selectView: {
+    flex: 2,
+    fontSize: 14,
+    borderRadius: 5,
+    minHeight: 40,
+    paddingTop: 6,
+    paddingLeft: 12,
+    paddingRight: 6,
+    paddingBottom: 6,
+    alignItems: 'center',
+    flexDirection: 'row',
+    backgroundColor: '#F5F5F5'
+  },
+  mobileView: {
+    flex: 2,
+    marginLeft: -12,
+    alignItems: 'center',
+    flexDirection: 'row'
+  },
+  dropView: {
+    fontSize: 16,
+    borderRadius: 4,
+    paddingRight: 4,
+    flexDirection: 'row',
+    alignItems: 'center',
+    backgroundColor: '#F5F5F5'
+  },
+  dropInput: {
+    width: 12,
+    padding: 6,
+    color: textPrimary,
+    minHeight: 40,
+  },
+  countryText: {
+    color: textPrimary,
+    fontSize: 14,
+    paddingRight: 4
+  },
+  dropLayer: {
+    left: 0,
+    right: 0,
+    opacity: 0,
+    position: 'absolute'
+  },
+  contactView: {
+    flex: 1,
+    color: textPrimary,
+    fontSize: 15,
+    borderRadius: 4,
+    minHeight: 40,
+    paddingTop: 6,
+    paddingLeft: 12,
+    paddingRight: 12,
+    paddingBottom: 6,
+    marginLeft: 10,
+    backgroundColor: '#F5F5F5'
+  },
+  passwordView: {
+    flex: 2,
+    marginTop: -8,
+  },
+  referView: {
+    padding: 16,
+    marginTop: 24,
+    borderRadius: 6,
+    alignItems: 'center',
+    backgroundColor: '#F5F5F5'
+  },
+  referTitle: {
+    color: textPrimary,
+    fontSize: 18,
+    fontWeight: 'bold'
+  },
+  referText: {
+    color: textPrimary,
+    paddingTop: 4,
+    alignItems: 'flex-end',
+    flexDirection: 'row'
+  },
+  weight: {
+    fontSize: 18,
+    paddingLeft: 4,
+    paddingRight: 4,
+    color: colorAccent,
+    fontWeight: 'bold'
+  },
+  codeView: {
+    paddingTop: 16,
+    alignItems: 'center',
+    flexDirection: 'row'
+  },
+  codeText: {
+    color: textPrimary,
+    fontSize: 16,
+    paddingTop: 6,
+    paddingLeft: 12,
+    paddingRight: 12,
+    paddingBottom: 6,
+    minHeight: 40,
+    marginLeft: 16,
+    borderRadius: 6,
+    textAlign: 'center',
+    backgroundColor: colorLight
+  },
+  signButton: {
+    marginTop: 24,
+    marginBottom: 8,
+  },
+  uploadGroup: {
+    flex: 2,
+    alignItems: 'center',
+    flexDirection: 'row',
+    justifyContent: 'center'
+  },
+  uploadView: {
+    flex: 1,
+    alignItems: 'center'
+  },
+  uploadIcon: {
+    width: $vw(28),
+    height: $vw(28),
+    borderRadius: 6
+  },
+  consentView: {
+    paddingTop: 32
+  },
+  consentTitle: {
+    color: textPrimary,
+    fontSize: 15,
+    paddingTop: 2,
+    paddingBottom: 8,
+    fontWeight: 'bold',
+    textDecorationLine: 'underline'
+  },
+  agreeView: {
+    paddingTop: 16,
+    marginBottom: 8,
+    flexDirection: 'row',
+    alignItems: 'flex-start',
+  },
+  agreeView2: {
+    marginTop: 8,
+    marginBottom: 16,
+    flexDirection: 'row',
+    alignItems: 'flex-start',
+  },
+  agreeTextRow: {
+    flex: 1,
+    paddingTop: 4,
+    paddingLeft: 8,
+    flexWrap: 'wrap',
+    flexDirection: 'row'
+  },
+  agreeText: {
+    color: textPrimary,
+    fontSize: 14,
+    paddingTop: 2,
+    paddingBottom: 2
+  },
+  agreeLink: {
+    ...ui.link,
+    fontSize: 14,
+    paddingTop: 2,
+    paddingBottom: 2,
+    textDecorationLine: 'underline'
+  },
+  sendBtn: {
+    flex: 1.8,
+    marginLeft: 6,
+    marginRight: 0,
+    borderRadius: 6
+  },
+  sendBtnView: {
+    flex: 1,
+    height: 40,
+    paddingLeft: 4,
+    paddingRight: 4,
+    alignItems: 'center',
+    justifyContent: 'center'
+  },
+  sendBtnText: {
+    color: colorLight,
+    fontSize: 13,
+    fontWeight: 'bold'
+  },
+});
+