import React, { useState, useEffect, useRef } from 'react' import { useTheme } from 'styled-components/native' import { Platform, View, StyleSheet, Dimensions, ScrollView, TouchableOpacity, Text } from 'react-native' import { OText, OButton, OModal, OIcon } from '../shared' import FastImage from 'react-native-fast-image' import IconAntDesign from 'react-native-vector-icons/AntDesign' import SelectDropdown from 'react-native-select-dropdown' import moment from 'moment' import CalendarPicker from 'react-native-calendar-picker' import FeatherIcon from 'react-native-vector-icons/Feather'; import { useSafeAreaInsets } from 'react-native-safe-area-context' import { ServiceFormParams } from '../../types' import { Placeholder, PlaceholderLine, Fade } from 'rn-placeholder'; import uuid from 'react-native-uuid'; import { orderTypeList } from '../../utils' import { ProductForm as ProductFormController, useUtils, useLanguage, useConfig, useOrder, useSession } from 'ordering-components/native' import { Container, ProfessionalPhoto, InfoWrapper, Divider, ProfessionalWrapper, ScheduleWrapper, CalendarWrapper, ButtonWrapper } from './styles' const screenWidth = Dimensions.get('window').width const ServiceFormUI = (props: ServiceFormParams) => { const { professionalSelected, productObject, handleSave, productCart, navigation, isSoldOut, maxProductQuantity, onClose, professionalListState, isCartProduct, actionStatus, handleCreateGuestUser } = props const theme = useTheme() const [, t] = useLanguage() const [{ optimizeImage, parsePrice, parseDate }] = useUtils() const { top } = useSafeAreaInsets() const [{ configs }] = useConfig() const [orderState] = useOrder() const [{ auth }] = useSession() const { product, loading, error } = productObject; const [selectDate, setSelectedDate] = useState(new Date()) const [timeList, setTimeList] = useState([]) const [isEnabled, setIsEnabled] = useState(false) const [timeSelected, setTimeSelected] = useState(null) const [dateSelected, setDateSelected] = useState(null) const [isOpen, setIsOpen] = useState(false) const [currentProfessional, setCurrentProfessional] = useState(null) const guestCheckoutEnabled = configs?.guest_checkout_enabled?.value === '1' const orderTypeEnabled = !orderTypeList[orderState?.options?.type - 1] || configs?.allowed_order_types_guest_checkout?.value?.includes(orderTypeList[orderState?.options?.type - 1]) const dropdownRef = useRef(null) const styles = StyleSheet.create({ photoStyle: { width: 45, height: 45, borderRadius: 7.6 }, buttonStyle: { borderRadius: 7.6, height: 44, borderWidth: 0 }, professionalSelect: { borderRadius: 7.6, padding: 11, borderWidth: 1, borderColor: theme.colors.backgroundGray200, flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between' }, professionalItem: { paddingVertical: 11, borderColor: theme.colors.backgroundGray200, borderTopWidth: 1 }, selectOption: { width: '100%', backgroundColor: theme.colors.backgroundGray100, paddingVertical: 5, paddingHorizontal: 14, flexDirection: 'row-reverse', alignItems: 'center', justifyContent: 'space-between', height: 40, marginBottom: 30 }, dropDownRow: { color: theme.colors.primary, fontSize: 14, marginHorizontal: 0 }, professionalList: { paddingHorizontal: 20, paddingVertical: 30, } }) const getMomentTime = (time) => { const _moment = moment(`${moment(selectDate).format('YYYY-MM-DD')} ${time}`, 'YYYY-MM-DD HH:mm').toDate() return _moment } const isBusyTime = (professional, selectedMoment) => { if (!selectedMoment) return false const startDay = moment(selectedMoment).utc().format('d') const isStartScheduleEnabled = professional?.schedule?.[startDay]?.enabled const duration = product?.duration ?? 0 const endDay = moment(selectedMoment).add(duration - 1, 'minutes').utc().format('d') const isEndScheduleEnabled = professional?.schedule?.[endDay]?.enabled if (!isStartScheduleEnabled || !isEndScheduleEnabled) return true if (professional?.busy_times?.length === 0) return false const busyTimes = isCartProduct ? professional?.busy_times.filter(item => !(item.start === productCart?.calendar_event?.start && item.end === productCart?.calendar_event?.end)) : [...professional?.busy_times] const valid = busyTimes.some(item => { return (moment.utc(item?.start).local().valueOf() <= moment(selectedMoment).valueOf() && moment(selectedMoment).valueOf() < moment.utc(item?.end).local().valueOf()) || (moment.utc(item?.start).local().valueOf() < moment(selectedMoment).valueOf() && moment(selectedMoment).add(duration, 'minutes').valueOf() < moment.utc(item?.end).local().valueOf()) }) return valid } const onDateChange = (date: any) => { setSelectedDate(date) setTimeSelected(null) dropdownRef?.current && dropdownRef.current.reset() } const dropDownIcon = () => { return ( ) } const handleUpdateGuest = () => { const guestToken = uuid.v4() if (guestToken) handleCreateGuestUser({ guest_token: guestToken }) } const customDayHeaderStylesCallback = () => { return { textStyle: { color: theme.colors.disabled, fontSize: 12, }, }; }; const handleSaveService = () => { const updated = { serviceTime: moment(dateSelected).utc().format('YYYY-MM-DD HH:mm:00'), professional: currentProfessional } handleSave && handleSave(updated) } const validateSelectedDate = (curdate: any, menu: any) => { const day = moment(curdate).format('d') setIsEnabled(menu?.schedule?.[day]?.enabled || false) } const handleRedirectLogin = () => { navigation && navigation.navigate('Login', { store_slug: props.businessSlug }); onClose && onClose() }; const getTimes = (curdate: any, menu: any) => { validateSelectedDate(curdate, menu) const date = new Date() var dateSeleted = new Date(curdate) var times = [] for (var k = 0; k < menu.schedule[dateSeleted.getDay()].lapses.length; k++) { var open = { hour: menu.schedule[dateSeleted.getDay()].lapses[k].open.hour, minute: menu.schedule[dateSeleted.getDay()].lapses[k].open.minute } var close = { hour: menu.schedule[dateSeleted.getDay()].lapses[k].close.hour, minute: menu.schedule[dateSeleted.getDay()].lapses[k].close.minute } for (var i = open.hour; i <= close.hour; i++) { if (date.getDate() !== dateSeleted.getDate() || i >= date.getHours()) { let hour = '' let meridian = '' if (configs?.format_time?.value === '12') { if (i === 0) { hour = '12' meridian = ' ' + t('AM', 'AM') } else if (i > 0 && i < 12) { hour = (i < 10 ? '0' + i : i) meridian = ' ' + t('AM', 'AM') } else if (i === 12) { hour = '12' meridian = ' ' + t('PM', 'PM') } else { hour = ((i - 12 < 10) ? '0' + (i - 12) : `${(i - 12)}`) meridian = ' ' + t('PM', 'PM') } } else { hour = i < 10 ? '0' + i : i } for (let j = (i === open.hour ? open.minute : 0); j <= (i === close.hour ? close.minute : 59); j += 15) { if (i !== date.getHours() || j >= date.getMinutes() || date.getDate() !== dateSeleted.getDate()) { times.push({ text: hour + ':' + (j < 10 ? '0' + j : j) + meridian, value: (i < 10 ? '0' + i : i) + ':' + (j < 10 ? '0' + j : j) }) } } } } } return times } const addressRedirect = () => { navigation && navigation.navigate('AddressList') onClose && onClose() } const handleChangeProfessional = (professional: any) => { setCurrentProfessional(professional) setIsOpen(false) } useEffect(() => { if (selectDate === null || !currentProfessional?.schedule) return const _times = getTimes(selectDate, currentProfessional) setTimeList(_times) }, [selectDate, currentProfessional]) useEffect(() => { if (!selectDate || !timeSelected) { setDateSelected(null) return } const date = `${moment(selectDate).format('YYYY-MM-DD')} ${timeSelected}:00` setDateSelected(date) }, [selectDate, timeSelected]) useEffect(() => { if (!professionalSelected?.schedule) return setCurrentProfessional(professionalSelected) }, [professionalSelected]) useEffect(() => { if (isCartProduct && professionalListState?.professionals?.length > 0) { const professional = professionalListState?.professionals?.find((item: any) => item.id === professionalSelected?.id) setCurrentProfessional(professional) } }, [isCartProduct, professionalListState?.professionals]) useEffect(() => { if (!productCart?.calendar_event?.start) return setSelectedDate(moment.utc(productCart?.calendar_event?.start).local()) setTimeSelected(moment.utc(productCart?.calendar_event?.start).local().format('HH:mm')) }, [productCart]) return ( <> {loading && !error && ( )} {!loading && !error && ( {!!product?.images ? ( ) : ( )} {product?.name} {parsePrice(product?.price)} • {product?.duration}min {product?.description} {t('PROFESSIONAL', 'Professional')} {t('REQUIRED', 'Required')} setIsOpen(true)} > {!!currentProfessional ? ( <> {!!currentProfessional?.photo ? ( ) : ( )} {currentProfessional?.name} {currentProfessional?.lastname} {isBusyTime(currentProfessional, dateSelected) ? t('BUSY_ON_SELECTED_TIME', 'Busy on selected time') : t('AVAILABLE', 'Available') } ) : ( {t('SELECT_PROFESSIONAL', 'Select professional')} )} {t('SCHEDULE', 'Schedule')} {t('REQUIRED', 'Required')} {!!currentProfessional?.schedule ? ( {(timeList?.length > 0 && isEnabled) ? ( item.value === timeSelected)} data={timeList} onSelect={(selectedItem, index) => { setTimeSelected(selectedItem?.value) }} buttonTextAfterSelection={(selectedItem, index) => { return selectedItem?.text }} rowTextForSelection={(item, index) => { return item.text }} buttonStyle={{ borderRadius: 7.6, ...styles.selectOption }} buttonTextStyle={{ color: theme.colors.disabled, fontSize: 14, textAlign: 'left', marginHorizontal: 0 }} dropdownStyle={{ borderRadius: 8, borderColor: theme.colors.lightGray, marginTop: Platform.OS === 'ios' ? 12 : -top }} rowStyle={{ borderBottomColor: theme.colors.backgroundGray100, backgroundColor: theme.colors.backgroundGray100, height: 30, flexDirection: 'column', alignItems: 'flex-start', paddingTop: 8, paddingHorizontal: 12 }} renderCustomizedRowChild={(item, index) => { return ( {item.text} ) }} renderDropdownIcon={() => dropDownIcon()} dropdownOverlayColor='transparent' /> ) : ( {t('PROFESSIONAL_NOT_AVAILABLE', 'Professional is not available at the moment')} )} } nextComponent={ } width={screenWidth - 110} selectedDayTextColor={theme.colors.white} selectedDayColor={theme.colors.primary} todayBackgroundColor={theme.colors.border} dayLabelsWrapper={{ borderColor: theme.colors.clear }} onDateChange={onDateChange} minDate={new Date()} customDayHeaderStyles={customDayHeaderStylesCallback} selectedStartDate={selectDate} /> ) : ( {t('NO_SCHEDULE', 'No schedule')} )} {dateSelected && moment(dateSelected).format('hh:mm A')} {((productCart && auth && orderState.options?.address_id)) && ( handleSaveService()} text={orderState.loading ? t('LOADING', 'Loading') : ((isSoldOut || maxProductQuantity <= 0) ? t('SOLD_OUT', 'Sold out') : t('BOOK', 'Book'))} style={styles.buttonStyle} isDisabled={isSoldOut || maxProductQuantity <= 0 || !currentProfessional?.id || !dateSelected || isBusyTime(currentProfessional, dateSelected)} textStyle={{ fontSize: 14 }} /> )} {auth && !orderState.options?.address_id && (orderState.loading ? ( ) : ( addressRedirect()} /> ))} {!auth && ( handleRedirectLogin()} text={ isSoldOut || maxProductQuantity <= 0 ? t('SOLD_OUT', 'Sold out') : t('LOGIN_SIGNUP', 'Login / Sign Up') } imgRightSrc="" textStyle={{ color: theme.colors.primary, fontSize: 14 }} style={{ height: 44, backgroundColor: theme.colors.white, }} /> )} {!auth && guestCheckoutEnabled && orderTypeEnabled && ( {actionStatus?.loading ? ( ) : ( {t('AS_GUEST_USER', 'As guest user')} )} )} )} setIsOpen(false)} entireModal > {t('ANY_OROFESSIONAL_MEMBER', 'Any professional member')} {professionalListState?.professionals?.map((professional: any) => ( handleChangeProfessional(professional)} > {!!professional?.photo ? ( ) : ( )} {professional?.name} {professional?.lastname} {isBusyTime(professional, dateSelected) ? t('BUSY_ON_SELECTED_TIME', 'Busy on selected time') : t('AVAILABLE', 'Available') } ))} ) } export const ServiceForm = (props: any) => { const serviceFormProps = { ...props, UIComponent: ServiceFormUI, isService: true } return }