|
@@ -0,0 +1,391 @@
|
|
|
|
+import React, { useCallback, useRef, useState } from 'react';
|
|
|
|
+import {
|
|
|
|
+ View,
|
|
|
|
+ Text,
|
|
|
|
+ Image,
|
|
|
|
+ TouchableOpacity,
|
|
|
|
+ ScrollView,
|
|
|
|
+ KeyboardAvoidingView,
|
|
|
|
+ Platform,
|
|
|
|
+ TouchableWithoutFeedback,
|
|
|
|
+ Keyboard
|
|
|
|
+} from 'react-native';
|
|
|
|
+import { Formik } from 'formik';
|
|
|
|
+import * as yup from 'yup';
|
|
|
|
+import { useNavigation } from '@react-navigation/native';
|
|
|
|
+import { styles } from './styles';
|
|
|
|
+import { ButtonVariants } from 'src/types/components';
|
|
|
|
+import ImageView from 'better-react-native-image-viewing';
|
|
|
|
+
|
|
|
|
+import { StoreType, storage } from 'src/storage';
|
|
|
|
+
|
|
|
|
+import AddImgSvg from 'assets/icons/travels-screens/add-img.svg';
|
|
|
|
+import { Button, Header, Input, PageWrapper } from 'src/components';
|
|
|
|
+import { Colors } from 'src/theme';
|
|
|
|
+
|
|
|
|
+import EarthIcon from 'assets/icons/travels-section/earth.svg';
|
|
|
|
+import NomadsIcon from 'assets/icons/bottom-navigation/travellers.svg';
|
|
|
|
+import { getFontSize } from 'src/utils';
|
|
|
|
+import { RichEditor, RichToolbar, actions } from 'react-native-pell-rich-editor';
|
|
|
|
+import CalendarSvg from '../../../../../assets/icons/calendar.svg';
|
|
|
|
+import RangeCalendar from 'src/components/Calendars/RangeCalendar';
|
|
|
|
+import LocationIcon from 'assets/icons/bottom-navigation/map.svg';
|
|
|
|
+import { ModalFlatList } from 'src/components/FlatList/modal-flatlist';
|
|
|
|
+import { usePostAddEventMutation, usePostGetPhotosForRegionMutation } from '@api/events';
|
|
|
|
+import { SheetManager } from 'react-native-actions-sheet';
|
|
|
|
+import PhotosForRegionModal from '../Components/PhotosForRegionModal/PhotosForRegionModal';
|
|
|
|
+import { API_HOST } from 'src/constants';
|
|
|
|
+import AddMapPinModal from '../Components/AddMapPinModal';
|
|
|
|
+
|
|
|
|
+const EventSchema = yup.object({
|
|
|
|
+ event_name: yup.string().required().min(3),
|
|
|
|
+ date: yup.date().nullable().required(),
|
|
|
|
+ capacity: yup.number().optional(),
|
|
|
|
+ region: yup.number().required(),
|
|
|
|
+ photo: yup.number().nullable().optional(),
|
|
|
|
+ city: yup.string().required(),
|
|
|
|
+ address: yup.string().optional(),
|
|
|
|
+ pin: yup.object().nullable().required(),
|
|
|
|
+ details: yup.string().optional()
|
|
|
|
+});
|
|
|
|
+
|
|
|
|
+const CreateEventScreen = () => {
|
|
|
|
+ const token = (storage.get('token', StoreType.STRING) as string) ?? null;
|
|
|
|
+ const navigation = useNavigation();
|
|
|
|
+ const scrollRef = useRef<ScrollView>(null);
|
|
|
|
+ const richText = useRef<RichEditor | null>(null);
|
|
|
|
+ const { mutateAsync: getPhotosForRegion } = usePostGetPhotosForRegionMutation();
|
|
|
|
+ const { mutateAsync: addEvent } = usePostAddEventMutation();
|
|
|
|
+
|
|
|
|
+ const [isSubmitting, setIsSubmitting] = useState(false);
|
|
|
|
+ const [calendarVisible, setCalendarVisible] = useState(false);
|
|
|
|
+ const [isViewerVisible, setIsViewerVisible] = useState(false);
|
|
|
|
+ const [photos, setPhotos] = useState<any[]>([]);
|
|
|
|
+
|
|
|
|
+ const handleGetPhotosForRegion = useCallback(
|
|
|
|
+ async (regionId: number) => {
|
|
|
|
+ await getPhotosForRegion(
|
|
|
|
+ { region_id: regionId },
|
|
|
|
+ {
|
|
|
|
+ onSuccess: (res) => {
|
|
|
|
+ if (res.photos) {
|
|
|
|
+ setPhotos(res.photos);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ );
|
|
|
|
+ },
|
|
|
|
+ [token]
|
|
|
|
+ );
|
|
|
|
+
|
|
|
|
+ return (
|
|
|
|
+ <PageWrapper>
|
|
|
|
+ <Header label="Add event" />
|
|
|
|
+
|
|
|
|
+ <KeyboardAvoidingView
|
|
|
|
+ behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
|
|
|
|
+ style={{ flex: 1 }}
|
|
|
|
+ >
|
|
|
|
+ <TouchableWithoutFeedback onPress={Keyboard.dismiss} accessible={false}>
|
|
|
|
+ <ScrollView ref={scrollRef} showsVerticalScrollIndicator={false}>
|
|
|
|
+ <Formik
|
|
|
|
+ validationSchema={EventSchema}
|
|
|
|
+ initialValues={{
|
|
|
|
+ event_name: '',
|
|
|
|
+ date: '',
|
|
|
|
+ capacity: '',
|
|
|
|
+ region: null,
|
|
|
|
+ photo: null,
|
|
|
|
+ city: '',
|
|
|
|
+ address: '',
|
|
|
|
+ pin: null,
|
|
|
|
+ details: ''
|
|
|
|
+ }}
|
|
|
|
+ onSubmit={async (values) => {
|
|
|
|
+ setIsSubmitting(true);
|
|
|
|
+
|
|
|
|
+ const newEvent: any = {
|
|
|
|
+ title: values.event_name,
|
|
|
|
+ region: values.region,
|
|
|
|
+ address1: values.city,
|
|
|
|
+ address2: values.address,
|
|
|
|
+ date: values.date,
|
|
|
|
+ lon: (values.pin as any)?.coordinate?.longitude,
|
|
|
|
+ lat: (values.pin as any)?.coordinate?.latitude,
|
|
|
|
+ details: values.details
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ if (values.photo) {
|
|
|
|
+ newEvent.photo = values.photo;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (values.capacity) {
|
|
|
|
+ newEvent.capacity = Number(values.capacity);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ await addEvent(
|
|
|
|
+ { token, event: JSON.stringify(newEvent) },
|
|
|
|
+ {
|
|
|
|
+ onSuccess: (res) => {
|
|
|
|
+ setIsSubmitting(false);
|
|
|
|
+ navigation.goBack();
|
|
|
|
+ },
|
|
|
|
+ onError: (err) => {
|
|
|
|
+ setIsSubmitting(false);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ );
|
|
|
|
+ }}
|
|
|
|
+ >
|
|
|
|
+ {(props) => (
|
|
|
|
+ <View style={{ gap: 12 }}>
|
|
|
|
+ <Input
|
|
|
|
+ header={'Event name'}
|
|
|
|
+ placeholder={'Add event name'}
|
|
|
|
+ inputMode={'text'}
|
|
|
|
+ onChange={props.handleChange('event_name')}
|
|
|
|
+ value={props.values.event_name}
|
|
|
|
+ onBlur={props.handleBlur('event_name')}
|
|
|
|
+ formikError={props.touched.event_name && props.errors.event_name}
|
|
|
|
+ />
|
|
|
|
+
|
|
|
|
+ <Input
|
|
|
|
+ header={'Date'}
|
|
|
|
+ placeholder={'Add date'}
|
|
|
|
+ inputMode={'none'}
|
|
|
|
+ onChange={props.handleChange('date')}
|
|
|
|
+ value={props.values.date}
|
|
|
|
+ onBlur={props.handleBlur('date')}
|
|
|
|
+ isFocused={() => setCalendarVisible(true)}
|
|
|
|
+ formikError={props.touched.date && props.errors.date}
|
|
|
|
+ icon={<CalendarSvg fill={Colors.LIGHT_GRAY} width={20} height={20} />}
|
|
|
|
+ />
|
|
|
|
+
|
|
|
|
+ <Input
|
|
|
|
+ header={'Maximum capacity'}
|
|
|
|
+ placeholder={'Set the maximum of people'}
|
|
|
|
+ inputMode={'numeric'}
|
|
|
|
+ onChange={props.handleChange('capacity')}
|
|
|
|
+ value={props.values.capacity}
|
|
|
|
+ onBlur={props.handleBlur('capacity')}
|
|
|
|
+ formikError={props.touched.capacity && props.errors.capacity}
|
|
|
|
+ icon={<NomadsIcon fill={Colors.LIGHT_GRAY} width={20} height={20} />}
|
|
|
|
+ />
|
|
|
|
+
|
|
|
|
+ <ModalFlatList
|
|
|
|
+ headerTitle={'NomadMania region'}
|
|
|
|
+ selectedObject={(data) => {
|
|
|
|
+ props.setFieldValue('region', data.id);
|
|
|
|
+ props.setFieldValue('photo', null);
|
|
|
|
+ handleGetPhotosForRegion(data.id);
|
|
|
|
+ }}
|
|
|
|
+ placeholder="Please choose a region"
|
|
|
|
+ formikError={
|
|
|
|
+ props.touched.region && !props.values.region ? props.errors.region : null
|
|
|
|
+ }
|
|
|
|
+ />
|
|
|
|
+
|
|
|
|
+ {props.values.region && photos.length > 0 ? (
|
|
|
|
+ <Input
|
|
|
|
+ header={'Photo'}
|
|
|
|
+ placeholder={props.values.photo ? 'Photo selected' : 'Choose a photo'}
|
|
|
|
+ inputMode={'none'}
|
|
|
|
+ onBlur={props.handleBlur('photo')}
|
|
|
|
+ isFocused={() =>
|
|
|
|
+ SheetManager.show('photos-for-region-modal', {
|
|
|
|
+ payload: {
|
|
|
|
+ photos,
|
|
|
|
+ selectPhoto: (photo: any) => {
|
|
|
|
+ props.setFieldValue('photo', photo);
|
|
|
|
+ }
|
|
|
|
+ } as any
|
|
|
|
+ })
|
|
|
|
+ }
|
|
|
|
+ formikError={props.touched.photo && props.errors.photo}
|
|
|
|
+ icon={<AddImgSvg fill={Colors.LIGHT_GRAY} width={20} height={20} />}
|
|
|
|
+ />
|
|
|
|
+ ) : null}
|
|
|
|
+
|
|
|
|
+ {props.values.photo ? (
|
|
|
|
+ <View
|
|
|
|
+ style={{
|
|
|
|
+ display: 'flex',
|
|
|
|
+ justifyContent: 'center',
|
|
|
|
+ alignItems: 'center',
|
|
|
|
+ gap: 8
|
|
|
|
+ }}
|
|
|
|
+ >
|
|
|
|
+ <TouchableOpacity
|
|
|
|
+ style={{ width: '100%' }}
|
|
|
|
+ onPress={async () => setIsViewerVisible(true)}
|
|
|
|
+ >
|
|
|
|
+ <Image
|
|
|
|
+ source={{ uri: `${API_HOST}/webapi/photos/${props.values.photo}/small` }}
|
|
|
|
+ style={{ width: '100%', height: 200, borderRadius: 4 }}
|
|
|
|
+ />
|
|
|
|
+ </TouchableOpacity>
|
|
|
|
+ </View>
|
|
|
|
+ ) : null}
|
|
|
|
+
|
|
|
|
+ <Input
|
|
|
|
+ header={'City'}
|
|
|
|
+ placeholder={'Add event city'}
|
|
|
|
+ inputMode={'text'}
|
|
|
|
+ onChange={props.handleChange('city')}
|
|
|
|
+ value={props.values.city}
|
|
|
|
+ onBlur={props.handleBlur('city')}
|
|
|
|
+ formikError={props.touched.city && props.errors.city}
|
|
|
|
+ icon={<EarthIcon fill={Colors.LIGHT_GRAY} width={20} height={20} />}
|
|
|
|
+ />
|
|
|
|
+
|
|
|
|
+ <Input
|
|
|
|
+ header={'Address'}
|
|
|
|
+ placeholder={'Add event address'}
|
|
|
|
+ inputMode={'text'}
|
|
|
|
+ onChange={props.handleChange('address')}
|
|
|
|
+ value={props.values.address}
|
|
|
|
+ onBlur={props.handleBlur('address')}
|
|
|
|
+ formikError={props.touched.address && props.errors.address}
|
|
|
|
+ icon={<LocationIcon fill={Colors.LIGHT_GRAY} width={20} height={20} />}
|
|
|
|
+ />
|
|
|
|
+
|
|
|
|
+ <Input
|
|
|
|
+ header={'Map pin'}
|
|
|
|
+ placeholder={'Choose from map'}
|
|
|
|
+ inputMode={'none'}
|
|
|
|
+ onChange={props.handleChange('pin')}
|
|
|
|
+ value={(props.values.pin as any)?.name}
|
|
|
|
+ onBlur={props.handleBlur('pin')}
|
|
|
|
+ isFocused={() =>
|
|
|
|
+ SheetManager.show('add-map-pin-modal', {
|
|
|
|
+ payload: {
|
|
|
|
+ setPin: (pin: any) => {
|
|
|
|
+ props.setFieldValue('pin', pin);
|
|
|
|
+ props.setFieldValue('city', pin?.city);
|
|
|
|
+ }
|
|
|
|
+ } as any
|
|
|
|
+ })
|
|
|
|
+ }
|
|
|
|
+ formikError={props.touched.pin && !props.values.pin ? props.errors.pin : false}
|
|
|
|
+ icon={<LocationIcon fill={Colors.LIGHT_GRAY} width={20} height={20} />}
|
|
|
|
+ />
|
|
|
|
+
|
|
|
|
+ <View>
|
|
|
|
+ <Text
|
|
|
|
+ style={{
|
|
|
|
+ color: Colors.DARK_BLUE,
|
|
|
|
+ fontSize: getFontSize(14),
|
|
|
|
+ fontFamily: 'redhat-700',
|
|
|
|
+ marginBottom: 8
|
|
|
|
+ }}
|
|
|
|
+ >
|
|
|
|
+ Details
|
|
|
|
+ </Text>
|
|
|
|
+ <RichToolbar
|
|
|
|
+ editor={richText}
|
|
|
|
+ selectedButtonStyle={{ backgroundColor: Colors.LIGHT_GRAY }}
|
|
|
|
+ style={{
|
|
|
|
+ borderTopLeftRadius: 4,
|
|
|
|
+ borderTopRightRadius: 4,
|
|
|
|
+ backgroundColor: Colors.FILL_LIGHT
|
|
|
|
+ }}
|
|
|
|
+ actions={[
|
|
|
|
+ actions.keyboard,
|
|
|
|
+ actions.setBold,
|
|
|
|
+ actions.setItalic,
|
|
|
|
+ actions.setUnderline,
|
|
|
|
+ actions.alignLeft,
|
|
|
|
+ actions.alignCenter,
|
|
|
|
+ actions.alignRight,
|
|
|
|
+ actions.alignFull,
|
|
|
|
+ actions.insertLink,
|
|
|
|
+ actions.insertBulletsList,
|
|
|
|
+ actions.insertOrderedList,
|
|
|
|
+ actions.setStrikethrough,
|
|
|
|
+ actions.indent,
|
|
|
|
+ actions.outdent,
|
|
|
|
+ actions.undo,
|
|
|
|
+ actions.redo,
|
|
|
|
+ actions.blockquote,
|
|
|
|
+ actions.insertLine
|
|
|
|
+ ]}
|
|
|
|
+ />
|
|
|
|
+
|
|
|
|
+ <RichEditor
|
|
|
|
+ ref={richText}
|
|
|
|
+ initialHeight={100}
|
|
|
|
+ onFocus={() => {
|
|
|
|
+ scrollRef.current?.scrollTo({
|
|
|
|
+ y: 500,
|
|
|
|
+ animated: true
|
|
|
|
+ });
|
|
|
|
+ }}
|
|
|
|
+ placeholder="Add event details"
|
|
|
|
+ initialContentHTML={props.values.details}
|
|
|
|
+ onChange={props.handleChange('details')}
|
|
|
|
+ editorStyle={{
|
|
|
|
+ contentCSSText: 'font-size: 14px; padding: 12px; color: #C8C8C8;',
|
|
|
|
+ backgroundColor: Colors.FILL_LIGHT,
|
|
|
|
+ color: Colors.DARK_BLUE
|
|
|
|
+ }}
|
|
|
|
+ style={{
|
|
|
|
+ minHeight: 100,
|
|
|
|
+ borderBottomLeftRadius: 4,
|
|
|
|
+ borderBottomRightRadius: 4
|
|
|
|
+ }}
|
|
|
|
+ onBlur={() => props.handleBlur('details')}
|
|
|
|
+ />
|
|
|
|
+ </View>
|
|
|
|
+
|
|
|
|
+ <View style={{ marginTop: 15, marginBottom: 15, gap: 8 }}>
|
|
|
|
+ <Button onPress={() => props.handleSubmit()} disabled={isSubmitting}>
|
|
|
|
+ Save
|
|
|
|
+ </Button>
|
|
|
|
+
|
|
|
|
+ <Button
|
|
|
|
+ variant={ButtonVariants.OPACITY}
|
|
|
|
+ containerStyles={{
|
|
|
|
+ backgroundColor: Colors.WHITE,
|
|
|
|
+ borderColor: Colors.BORDER_LIGHT
|
|
|
|
+ }}
|
|
|
|
+ textStyles={{ color: Colors.DARK_BLUE }}
|
|
|
|
+ onPress={() => navigation.goBack()}
|
|
|
|
+ >
|
|
|
|
+ Cancel
|
|
|
|
+ </Button>
|
|
|
|
+ </View>
|
|
|
|
+ <RangeCalendar
|
|
|
|
+ isModalVisible={calendarVisible}
|
|
|
|
+ closeModal={(startDate?: string | null, endDate?: string | null) => {
|
|
|
|
+ startDate && props.handleChange('date')(startDate.toString());
|
|
|
|
+ setCalendarVisible(false);
|
|
|
|
+ }}
|
|
|
|
+ allowRangeSelection={false}
|
|
|
|
+ selectedDate={props.values.date}
|
|
|
|
+ />
|
|
|
|
+ <ImageView
|
|
|
|
+ images={[
|
|
|
|
+ {
|
|
|
|
+ uri: `${API_HOST}/webapi/photos/${props.values.photo}/full`
|
|
|
|
+ }
|
|
|
|
+ ]}
|
|
|
|
+ keyExtractor={(imageSrc, index) => index.toString()}
|
|
|
|
+ imageIndex={0}
|
|
|
|
+ visible={isViewerVisible}
|
|
|
|
+ onRequestClose={() => setIsViewerVisible(false)}
|
|
|
|
+ swipeToCloseEnabled={false}
|
|
|
|
+ backgroundColor={Colors.DARK_BLUE}
|
|
|
|
+ />
|
|
|
|
+ </View>
|
|
|
|
+ )}
|
|
|
|
+ </Formik>
|
|
|
|
+ </ScrollView>
|
|
|
|
+ </TouchableWithoutFeedback>
|
|
|
|
+ </KeyboardAvoidingView>
|
|
|
|
+ <PhotosForRegionModal />
|
|
|
|
+ <AddMapPinModal />
|
|
|
|
+ </PageWrapper>
|
|
|
|
+ );
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+export default CreateEventScreen;
|