|
@@ -0,0 +1,339 @@
|
|
|
+import React, { useEffect, useState } from 'react';
|
|
|
+import {
|
|
|
+ View,
|
|
|
+ Text,
|
|
|
+ TouchableOpacity,
|
|
|
+ ScrollView,
|
|
|
+ Image,
|
|
|
+ Platform,
|
|
|
+ KeyboardAvoidingView
|
|
|
+} from 'react-native';
|
|
|
+import { useNavigation } from '@react-navigation/native';
|
|
|
+import { Dropdown, MultiSelect } from 'react-native-searchable-dropdown-kj';
|
|
|
+import { Formik } from 'formik';
|
|
|
+import * as yup from 'yup';
|
|
|
+
|
|
|
+import { PageWrapper, Header, Input, CheckBox } from 'src/components';
|
|
|
+import { StoreType, storage } from 'src/storage';
|
|
|
+import { Colors } from 'src/theme';
|
|
|
+import { NAVIGATION_PAGES } from 'src/types';
|
|
|
+import { styles } from './styles';
|
|
|
+import {
|
|
|
+ useGetAllCountriesQuery,
|
|
|
+ usePostAddFixerMutation,
|
|
|
+ usePostEditFixerMutation
|
|
|
+} from '@api/fixers';
|
|
|
+import { API_HOST } from 'src/constants';
|
|
|
+import { months } from '../utils/constants';
|
|
|
+import { FixerType } from '../utils/types';
|
|
|
+
|
|
|
+import CheckSvg from 'assets/icons/mark.svg';
|
|
|
+import CrossSvg from 'assets/icons/close.svg';
|
|
|
+
|
|
|
+const NewFixerSchema = yup.object().shape({
|
|
|
+ selectedCountries: yup
|
|
|
+ .array()
|
|
|
+ .min(1, 'select at least one country')
|
|
|
+ .required('country is required'),
|
|
|
+ name: yup.string().required('name is required'),
|
|
|
+ email: yup.string().email('invalid email format'),
|
|
|
+ website: yup.string().url('invalid URL format').required('website is required'),
|
|
|
+ comment: yup
|
|
|
+ .string()
|
|
|
+ .required('comment is required')
|
|
|
+ .max(8000, 'comment should not exceed 8000 characters')
|
|
|
+});
|
|
|
+
|
|
|
+const AddNewFixerScreen = ({ route }: { route: any }) => {
|
|
|
+ const existingFixer = route.params?.fixer ?? null;
|
|
|
+ const token = storage.get('token', StoreType.STRING) as string;
|
|
|
+ const navigation = useNavigation();
|
|
|
+ const { data: countries } = useGetAllCountriesQuery(token, true);
|
|
|
+ const { mutateAsync: addFixer } = usePostAddFixerMutation();
|
|
|
+ const { mutateAsync: editFixer } = usePostEditFixerMutation();
|
|
|
+ const [allCountries, setAllCountries] = useState<
|
|
|
+ { label: string; value: number; flag: string }[]
|
|
|
+ >([]);
|
|
|
+
|
|
|
+ useEffect(() => {
|
|
|
+ if (countries?.data) {
|
|
|
+ setAllCountries([
|
|
|
+ ...countries.data.map((item) => ({
|
|
|
+ label: item.country,
|
|
|
+ value: item.id,
|
|
|
+ flag: item.flag
|
|
|
+ }))
|
|
|
+ ]);
|
|
|
+ }
|
|
|
+ }, [countries]);
|
|
|
+
|
|
|
+ const years = Array.from({ length: 75 }, (_, i) => {
|
|
|
+ const year = new Date().getFullYear() - i;
|
|
|
+
|
|
|
+ return { label: year.toString(), value: year };
|
|
|
+ });
|
|
|
+
|
|
|
+ const month = new Date().getMonth() + 1;
|
|
|
+ const year = new Date().getFullYear();
|
|
|
+
|
|
|
+ const initialData: FixerType = existingFixer
|
|
|
+ ? {
|
|
|
+ month: existingFixer.month,
|
|
|
+ year: existingFixer.year,
|
|
|
+ selectedCountries: [route.params?.un_id],
|
|
|
+ name: existingFixer.name,
|
|
|
+ email: existingFixer.email,
|
|
|
+ phone: existingFixer.phone,
|
|
|
+ website: existingFixer.web,
|
|
|
+ comment: existingFixer.comment,
|
|
|
+ anonymous: existingFixer.anonymous === 1
|
|
|
+ }
|
|
|
+ : {
|
|
|
+ month: month,
|
|
|
+ year: year,
|
|
|
+ selectedCountries: [],
|
|
|
+ name: '',
|
|
|
+ email: '',
|
|
|
+ phone: '',
|
|
|
+ website: '',
|
|
|
+ comment: '',
|
|
|
+ anonymous: false
|
|
|
+ };
|
|
|
+
|
|
|
+ return (
|
|
|
+ <Formik
|
|
|
+ initialValues={initialData}
|
|
|
+ validationSchema={NewFixerSchema}
|
|
|
+ onSubmit={async (values) => {
|
|
|
+ if (existingFixer) {
|
|
|
+ await editFixer(
|
|
|
+ {
|
|
|
+ token,
|
|
|
+ fixer_id: existingFixer.id,
|
|
|
+ month: values.month,
|
|
|
+ year: values.year,
|
|
|
+ un_ids: values.selectedCountries,
|
|
|
+ name: values.name,
|
|
|
+ anonymous: values.anonymous ? 1 : 0,
|
|
|
+ email: values.email,
|
|
|
+ phone: values.phone,
|
|
|
+ website: values.website,
|
|
|
+ comment: values.comment
|
|
|
+ },
|
|
|
+ {
|
|
|
+ onSuccess: () => {
|
|
|
+ navigation.navigate(...([NAVIGATION_PAGES.FIXERS, { saved: true }] as never));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ );
|
|
|
+ } else {
|
|
|
+ await addFixer(
|
|
|
+ {
|
|
|
+ token,
|
|
|
+ month: values.month,
|
|
|
+ year: values.year,
|
|
|
+ un_ids: values.selectedCountries,
|
|
|
+ name: values.name,
|
|
|
+ anonymous: values.anonymous ? 1 : 0,
|
|
|
+ email: values.email,
|
|
|
+ phone: values.phone,
|
|
|
+ website: values.website,
|
|
|
+ comment: values.comment
|
|
|
+ },
|
|
|
+ {
|
|
|
+ onSuccess: () => {
|
|
|
+ navigation.navigate(...([NAVIGATION_PAGES.FIXERS, { saved: true }] as never));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ );
|
|
|
+ }
|
|
|
+ }}
|
|
|
+ >
|
|
|
+ {({ values, errors, touched, handleChange, handleBlur, handleSubmit, setFieldValue }) => (
|
|
|
+ <PageWrapper>
|
|
|
+ <Header label={existingFixer ? 'Edit Fixer' : 'Add New Fixer'} />
|
|
|
+ <KeyboardAvoidingView
|
|
|
+ behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
|
|
|
+ style={{ flex: 1 }}
|
|
|
+ >
|
|
|
+ <ScrollView
|
|
|
+ contentContainerStyle={styles.scrollContainer}
|
|
|
+ showsVerticalScrollIndicator={false}
|
|
|
+ >
|
|
|
+ <View>
|
|
|
+ <Text style={styles.title}>Date</Text>
|
|
|
+ <View style={[styles.row, { justifyContent: 'space-between' }]}>
|
|
|
+ <Dropdown
|
|
|
+ style={styles.dateSelector}
|
|
|
+ placeholderStyle={styles.placeholderStyle}
|
|
|
+ selectedTextStyle={styles.placeholderStyle}
|
|
|
+ containerStyle={styles.dropdownContent}
|
|
|
+ data={months}
|
|
|
+ labelField="label"
|
|
|
+ valueField="value"
|
|
|
+ value={values.month}
|
|
|
+ placeholder="Month"
|
|
|
+ onChange={(item) => setFieldValue('month', item.value)}
|
|
|
+ autoScroll={false}
|
|
|
+ flatListProps={{ initialNumToRender: 50, maxToRenderPerBatch: 10 }}
|
|
|
+ />
|
|
|
+ <Dropdown
|
|
|
+ style={styles.dateSelector}
|
|
|
+ placeholderStyle={styles.placeholderStyle}
|
|
|
+ selectedTextStyle={styles.placeholderStyle}
|
|
|
+ containerStyle={styles.dropdownContent}
|
|
|
+ data={years}
|
|
|
+ labelField="label"
|
|
|
+ valueField="value"
|
|
|
+ value={values.year}
|
|
|
+ placeholder="Year"
|
|
|
+ onChange={(item) => setFieldValue('year', item.value)}
|
|
|
+ search={true}
|
|
|
+ searchPlaceholder="Search"
|
|
|
+ autoScroll={false}
|
|
|
+ inputSearchStyle={styles.search}
|
|
|
+ flatListProps={{ initialNumToRender: 50, maxToRenderPerBatch: 10 }}
|
|
|
+ searchQuery={(keyword, item) => item.includes(keyword)}
|
|
|
+ />
|
|
|
+ </View>
|
|
|
+ </View>
|
|
|
+
|
|
|
+ <View style={{ flex: 1 }}>
|
|
|
+ <Text style={styles.title}>Countries</Text>
|
|
|
+ <MultiSelect
|
|
|
+ style={[
|
|
|
+ styles.dateSelector,
|
|
|
+ {
|
|
|
+ width: '100%',
|
|
|
+ marginBottom: values.selectedCountries.length ? 4 : 0
|
|
|
+ },
|
|
|
+ touched.selectedCountries && errors.selectedCountries
|
|
|
+ ? { borderColor: Colors.RED, borderWidth: 1 }
|
|
|
+ : {}
|
|
|
+ ]}
|
|
|
+ placeholderStyle={styles.placeholderStyle}
|
|
|
+ selectedTextStyle={styles.placeholderStyle}
|
|
|
+ containerStyle={styles.dropdownContent}
|
|
|
+ data={allCountries}
|
|
|
+ labelField="label"
|
|
|
+ valueField="value"
|
|
|
+ value={values.selectedCountries}
|
|
|
+ placeholder="Select countries"
|
|
|
+ activeColor="#E7E7E7"
|
|
|
+ search={true}
|
|
|
+ searchPlaceholder="Search"
|
|
|
+ inputSearchStyle={styles.search}
|
|
|
+ searchQuery={(keyword, item) =>
|
|
|
+ item.toLowerCase().includes(keyword.toLowerCase())
|
|
|
+ }
|
|
|
+ flatListProps={{ initialNumToRender: 30, maxToRenderPerBatch: 10 }}
|
|
|
+ onChange={(item) => setFieldValue('selectedCountries', item)}
|
|
|
+ renderItem={(item) => (
|
|
|
+ <View style={[styles.row, styles.multiOption]}>
|
|
|
+ <View style={[styles.row, { gap: 8, flex: 1 }]}>
|
|
|
+ <Image
|
|
|
+ source={{ uri: API_HOST + item.flag }}
|
|
|
+ style={[styles.flag, styles.borderSolid]}
|
|
|
+ />
|
|
|
+ <Text style={styles.optionText}>{item.label}</Text>
|
|
|
+ </View>
|
|
|
+
|
|
|
+ {values.selectedCountries.includes(item.value) && (
|
|
|
+ <CheckSvg fill={Colors.DARK_BLUE} height={8} />
|
|
|
+ )}
|
|
|
+ </View>
|
|
|
+ )}
|
|
|
+ renderSelectedItem={(item, unSelect) => (
|
|
|
+ <TouchableOpacity style={[styles.row, styles.countryItem]} onPress={unSelect}>
|
|
|
+ <Image
|
|
|
+ source={{ uri: API_HOST + item.flag }}
|
|
|
+ style={[styles.flagSmall, styles.borderSolid]}
|
|
|
+ />
|
|
|
+ <Text style={styles.label}>{item.label}</Text>
|
|
|
+ <CrossSvg fill={Colors.DARK_BLUE} height={10} />
|
|
|
+ </TouchableOpacity>
|
|
|
+ )}
|
|
|
+ />
|
|
|
+ {touched.selectedCountries && errors.selectedCountries && (
|
|
|
+ <Text style={styles.textError}>{errors.selectedCountries}</Text>
|
|
|
+ )}
|
|
|
+ </View>
|
|
|
+
|
|
|
+ <Input
|
|
|
+ placeholder="Name"
|
|
|
+ inputMode={'text'}
|
|
|
+ onChange={handleChange('name')}
|
|
|
+ onBlur={handleBlur('name')}
|
|
|
+ value={values.name}
|
|
|
+ header="Name"
|
|
|
+ formikError={touched.name && errors.name}
|
|
|
+ />
|
|
|
+
|
|
|
+ <Input
|
|
|
+ placeholder="E-mail"
|
|
|
+ inputMode={'email'}
|
|
|
+ onChange={handleChange('email')}
|
|
|
+ onBlur={handleBlur('email')}
|
|
|
+ value={values.email}
|
|
|
+ header="E-mail"
|
|
|
+ formikError={touched.email && errors.email}
|
|
|
+ />
|
|
|
+
|
|
|
+ <Input
|
|
|
+ placeholder="Phone"
|
|
|
+ inputMode={'tel'}
|
|
|
+ onChange={handleChange('phone')}
|
|
|
+ onBlur={handleBlur('phone')}
|
|
|
+ value={values.phone}
|
|
|
+ header="Phone"
|
|
|
+ />
|
|
|
+
|
|
|
+ <Input
|
|
|
+ placeholder="Website"
|
|
|
+ inputMode={'url'}
|
|
|
+ onChange={handleChange('website')}
|
|
|
+ onBlur={handleBlur('website')}
|
|
|
+ value={values.website}
|
|
|
+ header="Website"
|
|
|
+ formikError={touched.website && errors.website}
|
|
|
+ />
|
|
|
+
|
|
|
+ <Input
|
|
|
+ placeholder="Comment"
|
|
|
+ inputMode={'text'}
|
|
|
+ onChange={handleChange('comment')}
|
|
|
+ onBlur={handleBlur('comment')}
|
|
|
+ value={values.comment}
|
|
|
+ header="Comment"
|
|
|
+ height={64}
|
|
|
+ multiline={true}
|
|
|
+ formikError={touched.comment && errors.comment}
|
|
|
+ />
|
|
|
+
|
|
|
+ <TouchableOpacity
|
|
|
+ onPress={() => setFieldValue('anonymous', !values.anonymous)}
|
|
|
+ style={[styles.row, { gap: 8 }]}
|
|
|
+ >
|
|
|
+ <CheckBox
|
|
|
+ onChange={(value) => setFieldValue('anonymous', value)}
|
|
|
+ value={values.anonymous}
|
|
|
+ />
|
|
|
+ <Text style={[styles.title, { marginBottom: 0 }]}>
|
|
|
+ share this information anonymously
|
|
|
+ </Text>
|
|
|
+ </TouchableOpacity>
|
|
|
+ </ScrollView>
|
|
|
+ </KeyboardAvoidingView>
|
|
|
+
|
|
|
+ <View style={[styles.tabContainer, styles.row]}>
|
|
|
+ <TouchableOpacity style={styles.tabStyle} onPress={() => handleSubmit()}>
|
|
|
+ <Text style={styles.tabText}>{existingFixer ? 'Save Fixer' : 'Add New Fixer'}</Text>
|
|
|
+ </TouchableOpacity>
|
|
|
+ </View>
|
|
|
+ </PageWrapper>
|
|
|
+ )}
|
|
|
+ </Formik>
|
|
|
+ );
|
|
|
+};
|
|
|
+
|
|
|
+export default AddNewFixerScreen;
|