Răsfoiți Sursa

#14062 improve add vehicle

vbea 2 ani în urmă
părinte
comite
196419ea20

+ 6 - 0
Strides-APP/app/api/apiUser.js

@@ -81,5 +81,11 @@ export default user = {
   },
   deleteAccount: () => {
     return get(prefix + 'delUser')
+  },
+  getVehicleBrandList: () => {
+    return get(prefix + "brands")
+  },
+  getVehicleModelByBrand: (data) => {
+    return get(prefix + "models", data)
   }
 }

+ 17 - 1
Strides-APP/app/i18n/locales/en.js

@@ -199,6 +199,11 @@ export default {
     msgInputBrand: "Please type brand",
     msgInputModel: "Please type model",
     msgInputPlate: "Please type license plate",
+    msgInputBatCap: "Please type battery capacity",
+    msgSelectBrand: "Please select brand",
+    msgSelectModel: "Please select model",
+    msgSelectType: "Please select connector type",
+    msgVehiclePhoto: "Please upload car image",
     myVehicles: "My Vehicles: ",
     nickname: "Nickname",
     notificationSettings: "Notification Settings",
@@ -223,7 +228,18 @@ export default {
     updateSuccess: "Update profile successfully",
     userType: "User Type",
     vehicleBrand: "Brand",
-    vehicleModel: "Model"
+    vehicleModel: "Model",
+    selectBrand: "Select Brand",
+    selectModel: "Select Model",
+    selectUpload: "Select To Upload",
+    max4mbPhoto: "(Max 4 mb Per Photo)",
+    connecterType: "Connecter Type",
+    selectConnecterType: "Select Connecter Type",
+    enterLicensePlate: "Enter License Plate",
+    enterBatteryCapacity: "Enter Battery Capacity",
+    plsUploadImageCar: "Please Upload An Image Of Your Car",
+    btnAdd: "Add",
+    titleVehicles: "Vehicle(s)"
   },
   charging: {
     AC: "AC",

+ 17 - 1
Strides-APP/app/i18n/locales/zh-TW.js

@@ -199,6 +199,11 @@ export default {
     msgInputBrand: "請輸入車輛品牌",
     msgInputModel: "請輸入車輛型號",
     msgInputPlate: "請輸入車牌號",
+    msgInputBatCap: "請輸入電池容量",
+    msgSelectBrand: "請選擇車輛品牌",
+    msgSelectModel: "請選擇車輛型號",
+    msgSelectType: "請選擇充電器類型",
+    msgVehiclePhoto: "請上傳車輛照片",
     myVehicles: "我的車輛:",
     nickname: "名字",
     notificationSettings: "通知設定",
@@ -223,7 +228,18 @@ export default {
     updateSuccess: "更新個人訊息成功",
     userType: "賬號類別",
     vehicleBrand: "車輛品牌",
-    vehicleModel: "車輛型號"
+    vehicleModel: "車輛型號",
+    selectBrand: "請選擇車輛品牌",
+    selectModel: "請選擇車輛型號",
+    selectUpload: "點擊上傳圖片",
+    max4mbPhoto: "(圖片最大為4MB)",
+    connecterType: "充電器類型",
+    selectConnecterType: "請選擇充電器類型",
+    enterLicensePlate: "請輸入車牌號",
+    enterBatteryCapacity: "請輸入電池容量",
+    plsUploadImageCar: "請上傳您的車輛照片",
+    btnAdd: "添加車輛",
+    titleVehicles: "我的車輛"
   },
   charging: {
     AC: "AC",

+ 17 - 1
Strides-APP/app/i18n/locales/zh.js

@@ -199,6 +199,11 @@ export default {
     msgInputBrand: "请输入车辆品牌",
     msgInputModel: "请输入车辆型号",
     msgInputPlate: "请输入车牌号",
+    msgInputBatCap: "请输入电池容量",
+    msgSelectBrand: "请选择车辆品牌",
+    msgSelectModel: "请选择车辆型号",
+    msgSelectType: "请选择充电器类型",
+    msgVehiclePhoto: "请上传车辆照片",
     myVehicles: "我的车辆:",
     nickname: "昵称",
     notificationSettings: "通知设置",
@@ -223,7 +228,18 @@ export default {
     updateSuccess: "更新个人信息成功",
     userType: "账户类型",
     vehicleBrand: "车辆品牌",
-    vehicleModel: "车辆型号"
+    vehicleModel: "车辆型号",
+    selectBrand: "请选择车辆品牌",
+    selectModel: "请选择车辆型号",
+    selectUpload: "点击上传图片",
+    max4mbPhoto: "(图片限制最大4MB)",
+    connecterType: "充电器类型",
+    selectConnecterType: "请选择充电器类型",
+    enterLicensePlate: "请输入车牌号",
+    enterBatteryCapacity: "请输入电池容量",
+    plsUploadImageCar: "请上传您的车辆照片",
+    btnAdd: "添加车辆",
+    titleVehicles: "我的车辆"
   },
   charging: {
     AC: "AC交流",

+ 20 - 3
Strides-APP/app/pages/Router.js

@@ -36,9 +36,10 @@ import TopupV2 from './wallet/TopupV2'; //2C2P payment
 import TopupNew from './wallet/TopupNew';
 import AddCard from './wallet/AddCard';
 import FormCard from './payment/FormCard';
-import VehicleList from './my/VehicleList';
-import AddVehicle from './my/AddVehicle';
-import EditVehicle from './my/EditVehicle';
+import VehicleList from './vehicles/VehicleList';
+import AddVehicle from './vehicles/AddVehicle';
+import EditVehicle from './vehicles/EditVehicle';
+import VehicleDetail from './vehicles/VehicleDetail';
 import PayNow from './payment/PayNow';
 import CreditCard from './payment/CreditCard';
 import EditAddress from './my/EditAddress';
@@ -62,6 +63,7 @@ import ViewAlerts from './alert/ViewAlerts';
 import ViewCampaign from './alert/ViewCampaign';
 import RefundPolicy from './payment/RefundPolicy';
 import ViewArticle from './alert/ViewArticle';
+import VehicleListV2 from './vehicles/VehicleListV2';
 
 export var PageList = {
   'splash': {
@@ -217,6 +219,11 @@ export var PageList = {
       )
     }
   },
+  'vehiclesListV2': {
+    title: 'My Vehicles',
+    titleScope: 'route.myVehicles',
+    component: VehicleListV2
+  },
   'addVehicle': {
     title: 'Add Vehicle',
     titleScope: 'route.addVehicle',
@@ -227,6 +234,16 @@ export var PageList = {
     titleScope: 'route.editVehicle',
     component: EditVehicle
   },
+  'addVehicleV2': {
+    title: 'Add Vehicle',
+    titleScope: 'route.addVehicle',
+    component: VehicleDetail
+  },
+  'editVehicleV2': {
+    title: 'Update Detail',
+    titleScope: 'route.editVehicle',
+    component: VehicleDetail
+  },
   'paynow': {
     title: 'PAYNOW',
     titleScope: 'route.paynow',

+ 1 - 1
Strides-APP/app/pages/my/ProfileV2.js

@@ -157,7 +157,7 @@ export default class ProfileV2 extends Component {
         <Button
           style={styles.cardView}
           viewStyle={styles.profileItem}
-          onClick={() => startPage(PageList.myVehicles)}>
+          onClick={() => startPage(app.v3.vehicleModel ? PageList.vehiclesListV2 : PageList.myVehicles)}>
           <Image
             style={styles.cardIcon}
             source={require('../../images/user/card-vehicle.png')}/>

+ 0 - 0
Strides-APP/app/pages/my/AddVehicle.js → Strides-APP/app/pages/vehicles/AddVehicle.js


+ 0 - 0
Strides-APP/app/pages/my/EditVehicle.js → Strides-APP/app/pages/vehicles/EditVehicle.js


+ 401 - 0
Strides-APP/app/pages/vehicles/VehicleDetail.js

@@ -0,0 +1,401 @@
+/**
+ * 添加/编辑 Vehicle页面
+ * @邠心vbe on 2024/01/30
+ */
+import React, { Component } from 'react';
+import { View, Text, StyleSheet, TextInput, Image, Pressable, ScrollView } from 'react-native';
+import Dialog from '../../components/Dialog';
+import apiUser from '../../api/apiUser';
+import TextView from '../../components/TextView';
+import Dropdown from '../../components/Dropdown';
+import { TypeImageList } from '../chargeV2/Charging';
+import ImageCropPicker from 'react-native-image-crop-picker';
+import { UploadThemes } from '../../components/ThemesConfig';
+import apiUpload from '../../api/apiUpload';
+
+const options = {
+  width: 300,
+  height: 300,
+  cropping: true,
+  multiple: false,
+  mediaType: 'photo',
+  writeTempFile: false,
+  compressImageQuality: 0.8,
+  compressImageMaxWidth: 720,
+  compressImageMaxHeight: 1280,
+  ...UploadThemes
+}
+
+export default class VehicleDetail extends Component {
+  constructor(props) {
+    super(props);
+    this.state = {
+      isEdit: false,
+      vehicleInfo: {
+        brand: "",
+        model: "",
+        connectorType: ""
+      },
+      brandOptions: [],
+      modelOptions: [],
+      typeOptions: []
+    };
+  }
+
+  componentDidMount() {
+    this.getBrandOptions();
+    const params = this.props.route.params;
+    if (params.id) {
+      this.setState({
+        isEdit: true
+      })
+      Dialog.showProgressDialog();
+      this.getVehicleData(params.id);
+    }
+  }
+
+  getBrandOptions() {
+    apiUser.getVehicleBrandList().then(res => {
+      if (res.data) {
+        this.setState({
+          brandOptions: res.data
+        })
+      }
+    }).catch((err) => {
+      Dialog.dismissLoading();
+      toastShort(err)
+    });
+    const types = []
+    TypeImageList.forEach((item, index) => {
+      types.push(item.name);
+    })
+    this.setState({
+      typeOptions: types
+    })
+  }
+
+  getModelOptions() {
+    console.log("getModelOptions", this.state.vehicleInfo);
+    if (this.state.vehicleInfo.brand) {
+      apiUser.getVehicleModelByBrand({
+        brand: this.state.vehicleInfo.brand
+      }).then(res => {
+        if (res.data) {
+          this.setState({
+            modelOptions: res.data
+          })
+        }
+      }).catch((err) => {
+        toastShort(err)
+        this.setState({
+          modelOptions: []
+        })
+      });
+    } else {
+      this.setState({
+        modelOptions: []
+      })
+    }
+  }
+
+  getVehicleData(id) {
+    apiUser.getVehicleById({
+      vehiclePk: id
+    }).then(res => {
+      Dialog.dismissLoading();
+      if (res.data) {
+        const info = res.data;
+        this.setState({
+          vehicleInfo: info
+        }, () => {
+          this.getModelOptions();
+        });
+      }
+    }).catch((err) => {
+      Dialog.dismissLoading();
+      toastShort(err)
+    });
+  }
+
+  changeForm(key, value) {
+    const form = {
+      ...this.state.vehicleInfo
+    }
+    form[key] = value;
+    this.setState({
+      vehicleInfo: form
+    })
+  }
+
+  uploadImage() {
+    ImageCropPicker.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) {
+            this.changeForm("vehiclePhoto", res.data.picturePath)
+            toastShort($t('common.uploadSuccess'));
+          } else {
+            toastShort($t('common.uploadFailed'));
+          }
+        }).catch(err => {
+          toastShort(err);
+        });
+      }
+    }).catch(err1 => {
+      //console.log(err1);
+    });
+  }
+
+  validate() {
+    if (!this.state.vehicleInfo.brand) {
+      toastShort($t('profile.msgSelectBrand'));
+      return;
+    }
+    if (!this.state.vehicleInfo.vehicleModelId) {
+      toastShort($t('profile.msgSelectModel'));
+      return;
+    }
+    if (!this.state.vehicleInfo.licensePlate) {
+      toastShort($t('profile.msgInputPlate'));
+      return;
+    }
+    if (!this.state.vehicleInfo.batteryCapacity) {
+      toastShort($t('profile.msgInputBatCap'));
+      return;
+    }
+    if (!this.state.vehicleInfo.connectorType) {
+      toastShort($t('profile.msgSelectType'));
+      return;
+    }
+    if (!this.state.vehicleInfo.vehiclePhoto) {
+      toastShort($t('profile.msgVehiclePhoto'));
+      return;
+    }
+    if (this.state.isEdit) {
+      this.updateVehicle(this.state.vehicleInfo)
+    } else {
+      this.addVehicle(this.state.vehicleInfo)
+    }
+  }
+
+  addVehicle(params) {
+    Dialog.showProgressDialog();
+    apiUser.addVehicle(params).then(res => {
+      Dialog.dismissLoading();
+      toastShort($t('common.addSuccess'));
+      goBack();
+    }).catch((err) => {
+      Dialog.dismissLoading();
+      toastShort(err);
+    });
+  }
+
+  updateVehicle(params) {
+    Dialog.showProgressDialog();
+    apiUser.updateVehicle(params).then(res => {
+      Dialog.dismissLoading();
+      toastShort($t('common.updateSuccess'));
+      goBack();
+    }).catch((err) => {
+      Dialog.dismissLoading();
+      toastShort(err);
+    });
+  }
+
+  render() {
+    return (
+      <ScrollView
+        style={styles.container}
+        contentContainerStyle={$padding(16)}>
+        <View style={ui.flexc}>
+          <MaterialCommunityIcons
+            name="form-dropdown"
+            size={20}
+            color={textPrimary}
+            style={ui.bold}
+          />
+          <TextView style={styles.titleText}>{$t('profile.vehicleBrand')}</TextView>
+        </View>
+        <Dropdown
+          style={styles.formDropdown}
+          list={this.state.brandOptions}
+          placeholder={$t('profile.selectBrand')}
+          title={$t('profile.vehicleBrand')}
+          value={this.state.vehicleInfo.brand}
+          autoSelect={false}
+          onChange={value => {
+            this.changeForm("brand", value);
+            this.getModelOptions();
+          }}
+        />
+        <Dropdown
+          style={styles.formDropdown}
+          list={this.state.modelOptions}
+          placeholder={$t('profile.selectModel')}
+          title={$t('profile.vehicleModel')}
+          value={this.state.vehicleInfo.vehicleModelId}
+          valueKey={"vehicleModelId"}
+          nameKey={"model"}
+          autoSelect={true}
+          onChange={value => this.changeForm("vehicleModelId", value)}
+        />
+        <TextInput
+          style={styles.formInput}
+          defaultValue={this.state.vehicleInfo.licensePlate}
+          placeholder={$t('profile.enterLicensePlate')}
+          placeholderTextColor={textPlacehoder}
+          maxLength={20}
+          onChangeText={text => this.changeForm("licensePlate", text)}
+        />
+        <View style={styles.formInputRow}>
+          <TextInput
+            style={styles.itemInput}
+            defaultValue={this.state.vehicleInfo.batteryCapacity}
+            placeholder={$t('profile.enterBatteryCapacity')}
+            placeholderTextColor={textPlacehoder}
+            maxLength={20}
+            keyboardType='decimal-pad'
+            onChangeText={text => this.changeForm("batteryCapacity", text)}
+          />
+          <TextView style={styles.titleText}>kWh</TextView>
+        </View>
+        <View style={ui.flexc}>
+          <MaterialCommunityIcons
+            name="form-dropdown"
+            size={20}
+            color={textPrimary}
+            style={ui.bold}
+          />
+          <TextView style={styles.titleText}>{$t('profile.connecterType')}</TextView>
+        </View>
+        <Dropdown
+          style={styles.formDropdown}
+          list={this.state.typeOptions}
+          placeholder={$t('profile.selectConnecterType')}
+          title={$t('profile.connecterType')}
+          value={this.state.vehicleInfo.connectorType}
+          onChange={value => this.changeForm("connectorType", value)}
+          autoSelect={false}
+        />
+        <View style={ui.flexc}>
+          <MaterialCommunityIcons
+            name="tray-arrow-up"
+            size={20}
+            color={textPrimary}
+            style={ui.bold}
+          />
+          <TextView style={styles.titleText}>{$t('profile.plsUploadImageCar')}</TextView>
+        </View>
+        <View style={styles.uploadGroup}>
+          { this.state.vehicleInfo.vehiclePhoto
+          ? <Pressable
+              onPress={() => this.uploadImage()}>
+              <Image
+                style={styles.uploadImage}
+                source={{uri: this.state.vehicleInfo.vehiclePhoto}}
+              />
+            </Pressable>
+          : <Pressable
+              style={styles.uploadView}
+              onPress={() => this.uploadImage()}>
+              <MaterialCommunityIcons
+                name="tray-arrow-up"
+                size={32}
+                color={textCancel}
+                style={ui.bold}
+              />
+              <TextView style={styles.uploadTitle}>{$t('profile.selectUpload')}</TextView>
+              <TextView style={styles.uploadDesc}>{$t('profile.max4mbPhoto')}</TextView>
+            </Pressable>
+          }
+        </View>
+        <View style={styles.buttonView}>
+        <Button
+          text={$t('common.save')}
+          elevation={1.5}
+          onClick={() => {
+            this.validate();
+          }}/>
+        </View>
+      </ScrollView>
+    );
+  }
+}
+
+const styles = StyleSheet.create({
+  container: {
+    flex: 1
+  },
+  titleText: {
+    padding: 8,
+    fontSize: 14,
+    color: textPrimary,
+    fontWeight: 'bold'
+  },
+  formInput: {
+    ...$margin(8, 0),
+    padding: 12,
+    borderWidth: 1,
+    borderRadius: 4,
+    borderStyle: 'solid',
+    borderColor: "#EAEAEA",
+  },
+  formDropdown: {
+    ...$margin(8, 0),
+    padding: 12,
+    borderWidth: 1,
+    borderRadius: 4,
+    borderStyle: 'solid',
+    borderColor: "#EAEAEA",
+    alignItems: 'center',
+    flexDirection: 'row'
+  },
+  formInputRow: {
+    ...$margin(8, 0),
+    padding: 8,
+    borderWidth: 1,
+    borderRadius: 4,
+    borderStyle: 'solid',
+    borderColor: "#EAEAEA",
+    alignItems: 'center',
+    flexDirection: 'row'
+  },
+  itemInput: {
+    flex: 1,
+    padding: 4
+  },
+  uploadView: {
+    width: 160,
+    height: 160,
+    ...$margin(8, 0),
+    padding: 8,
+    borderWidth: 1,
+    borderRadius: 4,
+    borderStyle: 'solid',
+    borderColor: "#EAEAEA",
+    alignItems: 'center',
+    justifyContent: 'center'
+  },
+  uploadImage: {
+    width: 160,
+    height: 160,
+    ...$margin(8, 0)
+  },
+  uploadTitle: {
+    fontSize: 14,
+    color: textPrimary,
+    paddingTop: 8
+  },
+  uploadDesc: {
+    fontSize: 12,
+    color: textCancel,
+    paddingTop: 2
+  },
+  buttonView: {
+    marginTop: 32,
+    marginBottom: 8
+  }
+})

+ 0 - 0
Strides-APP/app/pages/my/VehicleList.js → Strides-APP/app/pages/vehicles/VehicleList.js


+ 255 - 0
Strides-APP/app/pages/vehicles/VehicleListV2.js

@@ -0,0 +1,255 @@
+/**
+ * 车辆列表V2
+ * @邠心vbe on 2024/01/30
+ */
+import React, { Component } from 'react';
+import { View, Text, Image, StyleSheet, Pressable, FlatList, RefreshControl } from 'react-native';
+import apiUser from '../../api/apiUser';
+import Button, { ElevationObject } from '../../components/Button';
+import Dialog from '../../components/Dialog';
+import TextView from '../../components/TextView';
+import { PageList } from '../Router';
+import { MyRefreshProps } from '../../components/ThemesConfig';
+import utils from '../../utils/utils';
+
+export default class VehicleListV2 extends Component {
+  constructor(props) {
+    super(props);
+    this.state = {
+      vehicleList: [],
+      refreshing: false
+    };
+  }
+
+  componentDidMount() {
+    //this.getVehicleList();
+    this.props.navigation.addListener('focus', () => {
+      this.getVehicleList();
+    })
+  }
+
+  onRefresh() {
+    this.setState({
+      refreshing: true
+    })
+    this.getVehicleList();
+  }
+
+  getVehicleList() {
+    apiUser.getVehicles().then(res => {
+      if (res.data) {
+        this.setState({
+          vehicleList: res.data,
+          refreshing: false
+        });
+        if (this.props.onResult) {
+          this.props.onResult(res.data.length)
+        }
+      }
+    }).catch(err => {
+      this.setState({
+        vehicleList: [],
+        refreshing: false
+      });
+      if (this.props.onResult) {
+        this.props.onResult(0)
+      }
+    });
+  }
+
+  removeVehicle(id) {
+    Dialog.showDialog({
+      title: $t('profile.removeVehicle'),
+      message: $t('profile.tipRemoveVehicle'),
+      callback: btn => {
+        if (btn == 'ok') {
+          Dialog.dismissLoading();
+          this.deleteVehicle(id);
+        }
+      }
+    })
+  }
+
+  deleteVehicle(id) {
+    Dialog.showProgressDialog();
+    apiUser.deleteVehicle({
+      vehiclePk: id
+    }).then(res => {
+      Dialog.dismissLoading();
+      toastShort($t('common.deleteSuccess'));
+      this.getVehicleList();
+    }).catch(err => {
+      Dialog.dismissLoading();
+      toastShort(err);
+    });
+  }
+
+  listItem = ({item, index, separators}) => {
+    return (
+      <Pressable
+        style={styles.vehicleView}
+        android_ripple={ripple}
+        onPress={() => {
+          startPage(PageList.editVehicleV2, {id: item.vehiclePk});
+        }}
+        onLongPress={() => {
+          this.removeVehicle(item.vehiclePk)
+        }}>
+        <View style={styles.vehicleRow}>
+          <TextView style={styles.vehicleName}>{item.brand} {item.model}</TextView>
+        </View>
+        <TextView style={styles.vehicleModle}>{item.licensePlate}</TextView>
+        <View style={styles.vehicleRow}>
+          <TextView style={styles.vehicleType}>{item.connectorType + ", " + item.batteryCapacity + "KWH"}</TextView>
+          {/* <View style={styles.vehicleTypeRow}>
+            <View style={styles.vehicleTypeIcon}>
+              <VehicleType size={10}/>
+            </View>
+            
+          </View> */}
+        </View>
+        {/* <Pressable
+          style={styles.closeIcon}
+          android_ripple={rippleLess}
+          onPress={() => this.removeVehicle(item.vehiclePk)}>
+          <MaterialCommunityIcons
+            name="close"
+            size={20}
+            color={textCancel}
+          />
+        </Pressable> */}
+        { utils.isNotEmpty(item.vehiclePhoto) && 
+          <Image
+            resizeMode="contain"
+            style={styles.vehicleViewBg}
+            source={{uri: item.vehiclePhoto}}/>
+        }
+      </Pressable>
+    )
+  }
+
+  listHeader = () => {
+    return (
+      <View style={$padding(8, 16)}>
+        <TextView style={styles.titleText}>{$t('profile.titleVehicles')}</TextView>
+        <Button
+          style={styles.addButton}
+          onClick={() => startPage(PageList.addVehicleV2)}>
+          <MaterialIcons
+            name="add-circle"
+            size={18}
+            color={colorPrimary}
+          />
+          <TextView style={styles.addBtnText}>{$t('profile.btnAdd')}</TextView>
+        </Button>
+      </View>
+    )
+  }
+
+  render() {
+    return (
+      <FlatList
+        style={ui.flex1}
+        data={this.state.vehicleList}
+        contentContainerStyle={$padding(8, 0)}
+        renderItem={this.listItem}
+        keyExtractor={item => item?.vehiclePk}
+        //onEndReached={() => this.getVehicleListPage()}
+        refreshControl={
+          <RefreshControl
+            {...MyRefreshProps()}
+            refreshing={this.state.refreshing}
+            onRefresh={() => this.onRefresh()}
+          />
+        }
+        ListHeaderComponent={this.listHeader}
+        ListEmptyComponent={<Text style={ui.noData}>{$t('profile.noVehicles')}</Text>}
+      />
+    );
+  }
+}
+
+const styles = StyleSheet.create({
+  titleText: {
+    fontSize: 14,
+    color: textPrimary,
+    fontWeight: 'bold'
+  },
+  vehicleView: {
+    borderWidth: 1,
+    borderRadius: 4,
+    borderStyle: 'solid',
+    borderColor: '#EAEAEA',
+    overflow: 'hidden',
+    //...ElevationObject(5),
+    ...$margin(8, 16),
+    ...$padding(12, 16, 12),
+    backgroundColor: colorLight
+  },
+  vehicleViewBg: {
+    top: 16,
+    right: 16,
+    width: 40,
+    height: 40,
+    position: 'absolute'
+  },
+  vehicleRow: {
+    alignItems: 'center',
+    flexDirection: 'row',
+  },
+  vehicleIcon: {
+    width: 32,
+    height: 32,
+  },
+  vehicleName: {
+    color: textDark,
+    fontSize: 14,
+    fontWeight: 'bold'
+  },
+  vehicleModle: {
+    color: textPrimary,
+    fontSize: 14,
+    paddingTop: 4,
+    paddingBottom: 4
+  },
+  vehicleTypeRow: {
+    height: 20,
+    marginLeft: 44,
+    borderRadius: 4,
+    overflow: 'hidden',
+    alignItems: 'center',
+    flexDirection: 'row',
+    backgroundColor: '#E5EFDE'
+  },
+  vehicleTypeIcon: {
+    width: 20,
+    height: 20,
+    alignItems: 'center',
+    justifyContent: 'center',
+    backgroundColor: colorAccent
+  },
+  vehicleType: {
+    color: colorAccent,
+    fontSize: 14
+  },
+  closeIcon: {
+    top: 0,
+    right: 0,
+    padding: 12,
+    position: 'absolute'
+  },
+  addButton: {
+    marginTop: 16,
+    borderWidth: 1,
+    borderRadius: 4,
+    borderStyle: 'solid',
+    borderColor: colorPrimary,
+    backgroundColor: 'transparent'
+  },
+  addBtnText: {
+    fontSize: 15,
+    color: colorPrimary,
+    fontWeight: 'bold',
+    paddingLeft: 8
+  }
+});