changeCarPageComponent.tsx 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. import { View, Text, ScrollView, Pressable, Alert } from 'react-native';
  2. import React, { useContext, useEffect, useState } from 'react';
  3. import { SafeAreaView } from 'react-native-safe-area-context';
  4. import { router } from 'expo-router';
  5. import { CrossLogoSvg } from '../global/SVG';
  6. import { AuthContext } from '../../context/AuthProvider';
  7. import NormalInput from '../global/normal_input';
  8. import NormalButton from '../global/normal_button';
  9. import { authenticationService } from '../../service/authService';
  10. import * as SecureStore from 'expo-secure-store';
  11. import { chargeStationService } from '../../service/chargeStationService';
  12. import { User } from '../../types/user';
  13. import { useTranslation } from '../../util/hooks/useTranslation';
  14. const ChangeCarPageComponent = () => {
  15. const { user, setUser } = useContext(AuthContext);
  16. const [token, setToken] = useState<string | null>(null);
  17. const [car, setCar] = useState<string | null>(null);
  18. const [isLoading, setIsLoading] = useState(false);
  19. const [error, setError] = useState<string | null>(null);
  20. const { t } = useTranslation();
  21. useEffect(() => {
  22. const getToken = async () => {
  23. const storedToken = await SecureStore.getItemAsync('accessToken');
  24. setToken(storedToken);
  25. };
  26. getToken();
  27. }, []);
  28. const validateLicensePlate = (licensePlate: string | null): string | null => {
  29. if (!licensePlate || licensePlate.trim() === '') {
  30. return t('accountSettings.change_car.enter_license_plate_error');
  31. }
  32. const trimmedPlate = licensePlate.trim();
  33. if (trimmedPlate.length < 2) {
  34. return t('accountSettings.change_car.license_plate_min_length');
  35. }
  36. if (trimmedPlate.length > 10) {
  37. return t('accountSettings.change_car.license_plate_max_length');
  38. }
  39. // Check for special characters (allow letters, numbers, hyphen, and spaces)
  40. const validFormat = /^[A-Za-z0-9-\s]+$/;
  41. if (!validFormat.test(trimmedPlate)) {
  42. return t('accountSettings.change_car.license_plate_invalid_format');
  43. }
  44. return null;
  45. };
  46. const saveLicensePlate = async (licensePlate: string | null) => {
  47. const validationError = validateLicensePlate(licensePlate);
  48. if (validationError) {
  49. setError(validationError);
  50. Alert.alert(t('common.error'), validationError);
  51. return;
  52. }
  53. try {
  54. const response = await chargeStationService.addCar(
  55. licensePlate!,
  56. '1834d087-bfc1-4f90-8f09-805e3d9422b5',
  57. 'f599470d-53a5-4026-99c0-2dab34c77f39',
  58. true
  59. );
  60. if (response === true) {
  61. setError(null);
  62. Alert.alert(t('common.success'), t('accountSettings.change_car.save_success'), [
  63. {
  64. text: t('common.ok'),
  65. onPress: () => router.replace('accountMainPage')
  66. }
  67. ]);
  68. const newUser = {...user}
  69. newUser.car = car
  70. setUser({
  71. ...newUser,
  72. } as User);
  73. } else {
  74. setError(t('accountSettings.change_car.save_failed'));
  75. Alert.alert(t('common.error'), t('accountSettings.change_car.save_failed'));
  76. }
  77. } catch (error) {
  78. setError(t('accountSettings.change_car.temporary_save_failed'));
  79. Alert.alert(t('common.error'), t('accountSettings.change_car.temporary_save_failed'));
  80. }
  81. };
  82. return (
  83. <SafeAreaView className="flex-1 bg-white" edges={['top', 'right', 'left']}>
  84. <ScrollView className="flex-1 mx-[5%]">
  85. <View style={{ marginTop: 25 }}>
  86. <Pressable
  87. onPress={() => {
  88. if (router.canGoBack()) {
  89. router.back();
  90. } else {
  91. router.replace('/accountMainPage');
  92. }
  93. }}
  94. >
  95. <CrossLogoSvg />
  96. </Pressable>
  97. <Text style={{ fontSize: 45, marginVertical: 25 }}>{t('accountSettings.change_car.change_license_plate_title')}</Text>
  98. <Text className="text-xl ">{t('accountSettings.change_car.enter_new_license_plate')}</Text>
  99. <View className="py-2">
  100. <NormalInput
  101. placeholder={t('accountSettings.change_car.enter_new_license_plate_placeholder')}
  102. onChangeText={(t) => {
  103. setCar(t);
  104. setError(null); // Clear error when user types
  105. }}
  106. editable={!isLoading}
  107. />
  108. {error && <Text className="text-red-500 mt-1">{error}</Text>}
  109. </View>
  110. <NormalButton
  111. title={<Text className="text-white">{isLoading ? t('accountSettings.change_car.changing') : t('common.confirm')}</Text>}
  112. disabled={isLoading}
  113. onPress={() => saveLicensePlate(car)}
  114. />
  115. </View>
  116. </ScrollView>
  117. </SafeAreaView>
  118. );
  119. };
  120. export default ChangeCarPageComponent;