diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts index 732708a7012b..1bc9c827e2e9 100755 --- a/src/ONYXKEYS.ts +++ b/src/ONYXKEYS.ts @@ -501,6 +501,9 @@ const ONYXKEYS = { /** Information about loading states while talking with AI sales */ TALK_TO_AI_SALES: 'talkToAISales', + /** Stores draft information while user is scheduling the call. */ + SCHEDULE_CALL_DRAFT: 'scheduleCallDraft', + /** Onyx updates that should be stored after sequential queue is flushed */ QUEUE_FLUSHED_DATA: 'queueFlushedData', @@ -1147,6 +1150,7 @@ type OnyxValuesMapping = { [ONYXKEYS.SHOULD_BILL_WHEN_DOWNGRADING]: boolean | undefined; [ONYXKEYS.BILLING_RECEIPT_DETAILS]: OnyxTypes.BillingReceiptDetails; [ONYXKEYS.NVP_SIDE_PANEL]: OnyxTypes.SidePanel; + [ONYXKEYS.SCHEDULE_CALL_DRAFT]: OnyxTypes.ScheduleCallDraft; }; type OnyxDerivedValuesMapping = { diff --git a/src/ROUTES.ts b/src/ROUTES.ts index b2af2bb7f778..24a7bb40ec64 100644 --- a/src/ROUTES.ts +++ b/src/ROUTES.ts @@ -2483,6 +2483,14 @@ const ROUTES = { route: 'debug/transaction/:transactionID/violations/:index/json', getRoute: (transactionID: string, index: string) => `debug/transaction/${transactionID}/violations/${index}/json` as const, }, + SCHEDULE_CALL_BOOK: { + route: 'r/:reportID/schedule-call/book', + getRoute: (reportID: string) => `r/${reportID}/schedule-call/book` as const, + }, + SCHEDULE_CALL_CONFIRMATON: { + route: 'r/:reportID/schedule-call/confimation', + getRoute: (reportID: string) => `r/${reportID}/schedule-call/confimation` as const, + }, } as const; /** diff --git a/src/SCREENS.ts b/src/SCREENS.ts index 102650427eb0..59e3996cd55c 100644 --- a/src/SCREENS.ts +++ b/src/SCREENS.ts @@ -215,6 +215,7 @@ const SCREENS = { REPORT_EXPORT: 'Report_Export', MISSING_PERSONAL_DETAILS: 'MissingPersonalDetails', DEBUG: 'Debug', + SCHEDULE_CALL: 'ScheduleCall', }, PUBLIC_CONSOLE_DEBUG: 'Console_Debug', ONBOARDING_MODAL: { @@ -717,6 +718,10 @@ const SCREENS = { TRANSACTION_VIOLATION_CREATE: 'Debug_Transaction_Violation_Create', TRANSACTION_VIOLATION: 'Debug_Transaction_Violation', }, + SCHEDULE_CALL: { + BOOK: 'ScheduleCall_Book', + CONFIRMATION: 'ScheduleCall_Confirmation', + }, } as const; type Screen = DeepValueOf; diff --git a/src/components/DatePicker/CalendarPicker/Day.tsx b/src/components/DatePicker/CalendarPicker/Day.tsx new file mode 100644 index 000000000000..1c3a3b0dd6df --- /dev/null +++ b/src/components/DatePicker/CalendarPicker/Day.tsx @@ -0,0 +1,44 @@ +import React from 'react'; +import {View} from 'react-native'; +import Text from '@components/Text'; +import useStyleUtils from '@hooks/useStyleUtils'; +import useThemeStyles from '@hooks/useThemeStyles'; +import getButtonState from '@libs/getButtonState'; + +type DayProps = { + /** Whether day is disabled */ + disabled?: boolean; + + /** Whether day is selected */ + selected?: boolean; + + /** Whether day is pressed */ + pressed?: boolean; + + /** Whether day is hovered */ + hovered?: boolean; + + /** date to show */ + children?: number; +}; + +function Day({disabled, selected, pressed, hovered, children}: DayProps) { + const themeStyles = useThemeStyles(); + const StyleUtils = useStyleUtils(); + return ( + + {children} + + ); +} + +Day.displayName = 'Day'; + +export default Day; +export type {DayProps}; diff --git a/src/components/DatePicker/CalendarPicker/index.tsx b/src/components/DatePicker/CalendarPicker/index.tsx index 46f30192c317..0e1ae279bcbf 100644 --- a/src/components/DatePicker/CalendarPicker/index.tsx +++ b/src/components/DatePicker/CalendarPicker/index.tsx @@ -8,12 +8,11 @@ import PressableWithoutFeedback from '@components/Pressable/PressableWithoutFeed import Text from '@components/Text'; import useLocalize from '@hooks/useLocalize'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; -import useStyleUtils from '@hooks/useStyleUtils'; import useThemeStyles from '@hooks/useThemeStyles'; import DateUtils from '@libs/DateUtils'; -import getButtonState from '@libs/getButtonState'; import CONST from '@src/CONST'; import ArrowIcon from './ArrowIcon'; +import Day from './Day'; import generateMonthMatrix from './generateMonthMatrix'; import type CalendarPickerListItem from './types'; import YearPickerModal from './YearPickerModal'; @@ -28,6 +27,12 @@ type CalendarPickerProps = { /** A maximum date (earliest) allowed to select */ maxDate?: Date; + /** Restrict selection to only specific dates */ + selectedableDates?: string[]; + + /** Day component to render for dates */ + DayComponent?: typeof Day; + /** A function called when the date is selected */ onSelected?: (selectedDate: string) => void; }; @@ -49,12 +54,13 @@ function CalendarPicker({ minDate = setYear(new Date(), CONST.CALENDAR_PICKER.MIN_YEAR), maxDate = setYear(new Date(), CONST.CALENDAR_PICKER.MAX_YEAR), onSelected, + DayComponent = Day, + selectedableDates, }: CalendarPickerProps) { // eslint-disable-next-line rulesdir/prefer-shouldUseNarrowLayout-instead-of-isSmallScreenWidth const {isSmallScreenWidth} = useResponsiveLayout(); const styles = useThemeStyles(); const themeStyles = useThemeStyles(); - const StyleUtils = useStyleUtils(); const {preferredLocale, translate} = useLocalize(); const pressableRef = useRef(null); const [currentDateView, setCurrentDateView] = useState(() => getInitialCurrentDateView(value, minDate, maxDate)); @@ -251,7 +257,8 @@ function CalendarPicker({ const currentDate = new Date(currentYearView, currentMonthView, day); const isBeforeMinDate = currentDate < startOfDay(new Date(minDate)); const isAfterMaxDate = currentDate > startOfDay(new Date(maxDate)); - const isDisabled = !day || isBeforeMinDate || isAfterMaxDate; + const isSelectable = selectedableDates ? selectedableDates?.some((date) => isSameDay(parseISO(date), currentDate)) : true; + const isDisabled = !day || isBeforeMinDate || isAfterMaxDate || !isSelectable; const isSelected = !!day && isSameDay(parseISO(value.toString()), new Date(currentYearView, currentMonthView, day)); const handleOnPress = () => { if (!day || isDisabled) { @@ -273,15 +280,14 @@ function CalendarPicker({ dataSet={{[CONST.SELECTION_SCRAPER_HIDDEN_ELEMENT]: true}} > {({hovered, pressed}) => ( - - {day} - + {day} + )} ); diff --git a/src/languages/en.ts b/src/languages/en.ts index 716fbaef9e2b..54b26cb300fe 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -5003,7 +5003,7 @@ const translations = { description: 'Choose from the support options below:', chatWithConcierge: 'Chat with Concierge', scheduleSetupCall: 'Schedule a setup call', - scheduleADemo: 'Schedule demo', + scheduleACall: 'Schedule call', questionMarkButtonTooltip: 'Get assistance from our team', exploreHelpDocs: 'Explore help docs', }, @@ -6278,6 +6278,21 @@ const translations = { talkToConcierge: 'Talk to Concierge', hangUp: 'Hang up', }, + scheduledCall: { + book: { + title: 'Schedule call', + description: 'Find a time that works for you.', + slots: 'Available times for ', + }, + confirmation: { + title: 'Confirm call', + description: "Make sure the details below look good to you. Once you confirm the call, we'll send an invite with more info.", + setupSpecialist: 'Your setup specialist', + meetingLength: 'Meeting length', + dateTime: 'Date & time', + minutes: '30 minutes', + }, + }, testDrive: { quickAction: { takeATwoMinuteTestDrive: 'Take a 2-minute test drive', diff --git a/src/languages/es.ts b/src/languages/es.ts index 690c078d368d..87b0f5a0d0bf 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -5056,7 +5056,7 @@ const translations = { description: 'Elige una de las siguientes opciones:', chatWithConcierge: 'Chatear con Concierge', scheduleSetupCall: 'Concertar una llamada', - scheduleADemo: 'Programa una demostración', + scheduleACall: 'Programar llamada', questionMarkButtonTooltip: 'Obtén ayuda de nuestro equipo', exploreHelpDocs: 'Explorar la documentación de ayuda', }, @@ -6802,6 +6802,21 @@ const translations = { talkToConcierge: 'Habla con Concierge', hangUp: 'Colgar', }, + scheduledCall: { + book: { + title: 'Programar llamada', + description: 'Encuentra un horario que funcione para ti.', + slots: 'Horarios disponibles para el ', + }, + confirmation: { + title: 'Confirmar llamada', + description: 'Asegúrate de que los detalles a continuación sean correctos. Una vez que confirmes la llamada, enviaremos una invitación con más información.', + setupSpecialist: 'Tu especialista asignado', + meetingLength: 'Duración de la reunión', + dateTime: 'Fecha y hora', + minutes: '30 minutos', + }, + }, testDrive: { quickAction: { takeATwoMinuteTestDrive: 'Haz una proba de 2 minutos', diff --git a/src/libs/API/parameters/GetGuideCallAvailabilitySchedule.ts b/src/libs/API/parameters/GetGuideCallAvailabilitySchedule.ts new file mode 100644 index 000000000000..109b96debfd6 --- /dev/null +++ b/src/libs/API/parameters/GetGuideCallAvailabilitySchedule.ts @@ -0,0 +1,6 @@ +type GetGuideCallAvailabilityScheduleParams = { + /** Admins room reportID */ + reportID: string; +}; + +export default GetGuideCallAvailabilityScheduleParams; diff --git a/src/libs/API/parameters/index.ts b/src/libs/API/parameters/index.ts index 1004e373202c..0c0e57557350 100644 --- a/src/libs/API/parameters/index.ts +++ b/src/libs/API/parameters/index.ts @@ -389,6 +389,7 @@ export type {ChangeTransactionsReportParams, TransactionThreadInfo} from './Chan export type {default as ResetBankAccountSetupParams} from './ResetBankAccountSetupParams'; export type {default as SendRecapInAdminsRoomParams} from './SendRecapInAdminsRoomParams'; export type {default as SetPolicyProhibitedExpensesParams} from './SetPolicyProhibitedExpensesParams'; +export type {default as GetGuideCallAvailabilityScheduleParams} from './GetGuideCallAvailabilitySchedule'; export type {default as GetEmphemeralTokenParams} from './GetEmphemeralTokenParams'; export type {default as CreateAppleDigitalWalletParams} from './CreateAppleDigitalWalletParams'; export type {default as CompleteConciergeCallParams} from './CompleteConciergeCallParams'; diff --git a/src/libs/API/types.ts b/src/libs/API/types.ts index 1d2cae7e961a..4616e906bfe0 100644 --- a/src/libs/API/types.ts +++ b/src/libs/API/types.ts @@ -475,6 +475,7 @@ const WRITE_COMMANDS = { PAY_AND_DOWNGRADE: 'PayAndDowngrade', COMPLETE_CONCIERGE_CALL: 'CompleteConciergeCall', FINISH_CORPAY_BANK_ACCOUNT_ONBOARDING: 'FinishCorpayBankAccountOnboarding', + GET_GUIDE_CALL_AVAILABILITY_SCHEDULE: 'GetGuideCallAvailabilitySchedule', } as const; type WriteCommand = ValueOf; @@ -970,6 +971,8 @@ type WriteCommandParameters = { // Change transaction report [WRITE_COMMANDS.CHANGE_TRANSACTIONS_REPORT]: Parameters.ChangeTransactionsReportParams; + + [WRITE_COMMANDS.GET_GUIDE_CALL_AVAILABILITY_SCHEDULE]: Parameters.GetGuideCallAvailabilityScheduleParams; }; const READ_COMMANDS = { diff --git a/src/libs/DebugUtils.ts b/src/libs/DebugUtils.ts index cf5b061864f9..ab725d193787 100644 --- a/src/libs/DebugUtils.ts +++ b/src/libs/DebugUtils.ts @@ -566,6 +566,12 @@ function validateReportDraftProperty(key: keyof Report | keyof ReportNameValuePa endDate: 'string', tripID: 'string', }); + case 'calendlySchedule': + return validateObject>(value, { + isLoading: 'boolean', + data: 'object', + errors: 'object', + }); case 'pendingAction': return validateConstantEnum(value, CONST.RED_BRICK_ROAD_PENDING_ACTION); case 'pendingFields': @@ -630,6 +636,7 @@ function validateReportDraftProperty(key: keyof Report | keyof ReportNameValuePa errors: CONST.RED_BRICK_ROAD_PENDING_ACTION, createReport: CONST.RED_BRICK_ROAD_PENDING_ACTION, exportFailedTime: CONST.RED_BRICK_ROAD_PENDING_ACTION, + calendlySchedule: CONST.RED_BRICK_ROAD_PENDING_ACTION, }); } } diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx index c69e0f05c1e2..1dbed4da5e13 100644 --- a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx +++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx @@ -23,6 +23,7 @@ import type { ReportDetailsNavigatorParamList, ReportSettingsNavigatorParamList, RoomMembersNavigatorParamList, + ScheduleCallParamList, SearchAdvancedFiltersParamList, SearchReportParamList, SearchSavedSearchParamList, @@ -753,6 +754,11 @@ const DebugModalStackNavigator = createModalStackNavigator({ [SCREENS.DEBUG.TRANSACTION_VIOLATION]: () => require('../../../../pages/Debug/TransactionViolation/DebugTransactionViolationPage').default, }); +const ScheduleCallModalStackNavigator = createModalStackNavigator({ + [SCREENS.SCHEDULE_CALL.BOOK]: () => require('../../../../pages/ScheduleCall/ScheduleCallPage').default, + [SCREENS.SCHEDULE_CALL.CONFIRMATION]: () => require('../../../../pages/ScheduleCall/ScheduleCallConfirmationPage').default, +}); + export { AddPersonalBankAccountModalStackNavigator, EditRequestStackNavigator, @@ -793,4 +799,5 @@ export { DebugModalStackNavigator, WorkspaceConfirmationModalStackNavigator, ConsoleModalStackNavigator, + ScheduleCallModalStackNavigator, }; diff --git a/src/libs/Navigation/AppNavigator/Navigators/RightModalNavigator.tsx b/src/libs/Navigation/AppNavigator/Navigators/RightModalNavigator.tsx index d41864904711..dcc95d0b534b 100644 --- a/src/libs/Navigation/AppNavigator/Navigators/RightModalNavigator.tsx +++ b/src/libs/Navigation/AppNavigator/Navigators/RightModalNavigator.tsx @@ -219,6 +219,10 @@ function RightModalNavigator({navigation, route}: RightModalNavigatorProps) { name={SCREENS.RIGHT_MODAL.MISSING_PERSONAL_DETAILS} component={ModalStackNavigators.MissingPersonalDetailsModalStackNavigator} /> + diff --git a/src/libs/Navigation/linkingConfig/config.ts b/src/libs/Navigation/linkingConfig/config.ts index 34c219f283a8..9f24045bb6f4 100644 --- a/src/libs/Navigation/linkingConfig/config.ts +++ b/src/libs/Navigation/linkingConfig/config.ts @@ -1590,6 +1590,16 @@ const config: LinkingOptions['config'] = { }, }, }, + [SCREENS.RIGHT_MODAL.SCHEDULE_CALL]: { + screens: { + [SCREENS.SCHEDULE_CALL.BOOK]: { + path: ROUTES.SCHEDULE_CALL_BOOK.route, + }, + [SCREENS.SCHEDULE_CALL.CONFIRMATION]: { + path: ROUTES.SCHEDULE_CALL_CONFIRMATON.route, + }, + }, + }, }, }, diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts index 2b7787e93105..8e0415879be3 100644 --- a/src/libs/Navigation/types.ts +++ b/src/libs/Navigation/types.ts @@ -1594,6 +1594,7 @@ type RightModalNavigatorParamList = { [SCREENS.RIGHT_MODAL.SEARCH_SAVED_SEARCH]: NavigatorScreenParams; [SCREENS.RIGHT_MODAL.MISSING_PERSONAL_DETAILS]: NavigatorScreenParams; [SCREENS.RIGHT_MODAL.DEBUG]: NavigatorScreenParams; + [SCREENS.RIGHT_MODAL.SCHEDULE_CALL]: NavigatorScreenParams; }; type TravelNavigatorParamList = { @@ -1995,6 +1996,15 @@ type DebugParamList = { }; }; +type ScheduleCallParamList = { + [SCREENS.SCHEDULE_CALL.BOOK]: { + reportID: string; + }; + [SCREENS.SCHEDULE_CALL.CONFIRMATION]: { + reportID: string; + }; +}; + type RootNavigatorParamList = PublicScreensParamList & AuthScreensParamList & LeftModalNavigatorParamList & SearchFullscreenNavigatorParamList; type OnboardingFlowName = keyof OnboardingModalNavigatorParamList; @@ -2084,6 +2094,7 @@ export type { WorkspaceConfirmationNavigatorParamList, TwoFactorAuthNavigatorParamList, ConsoleNavigatorParamList, + ScheduleCallParamList, TestDriveModalNavigatorParamList, WorkspaceScreenName, SettingsTabScreenName, diff --git a/src/libs/Permissions.ts b/src/libs/Permissions.ts index 547ea172a007..d71a92b58868 100644 --- a/src/libs/Permissions.ts +++ b/src/libs/Permissions.ts @@ -70,6 +70,10 @@ function canUsePrivateDomainOnboarding(betas: OnyxEntry): boolean { return !!betas?.includes(CONST.BETAS.PRIVATE_DOMAIN_ONBOARDING) || canUseAllBetas(betas); } +function canUseCallScheduling() { + return false; +} + export default { canUseDefaultRooms, canUseLinkPreviews, @@ -86,4 +90,5 @@ export default { canUseInAppProvisioning, canUseGlobalReimbursementsOnND, canUsePrivateDomainOnboarding, + canUseCallScheduling, }; diff --git a/src/libs/actions/ScheduleCall.ts b/src/libs/actions/ScheduleCall.ts new file mode 100644 index 000000000000..b68ecffdfb33 --- /dev/null +++ b/src/libs/actions/ScheduleCall.ts @@ -0,0 +1,78 @@ +import type {OnyxUpdate} from 'react-native-onyx'; +import Onyx from 'react-native-onyx'; +import * as API from '@libs/API'; +import type {GetGuideCallAvailabilityScheduleParams} from '@libs/API/parameters'; +import {WRITE_COMMANDS} from '@libs/API/types'; +import Navigation from '@libs/Navigation/Navigation'; +import ONYXKEYS from '@src/ONYXKEYS'; +import type {PersonalDetails, ScheduleCallDraft} from '@src/types/onyx'; +import {openExternalLink} from './Link'; + +function getGuideCallAvailabilitySchedule(reportID: string) { + if (!reportID) { + return; + } + + const optimisticData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${reportID}`, + value: { + calendlySchedule: { + isLoading: true, + errors: null, + }, + }, + }, + ]; + + const successData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${reportID}`, + value: { + calendlySchedule: { + isLoading: false, + errors: null, + }, + }, + }, + ]; + + const failureData: OnyxUpdate[] = [ + { + onyxMethod: Onyx.METHOD.MERGE, + key: `${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${reportID}`, + value: { + calendlySchedule: { + isLoading: false, + }, + }, + }, + ]; + + const params: GetGuideCallAvailabilityScheduleParams = { + reportID, + }; + + API.write(WRITE_COMMANDS.GET_GUIDE_CALL_AVAILABILITY_SCHEDULE, params, {optimisticData, successData, failureData}); +} + +function saveBookingDraft(data: ScheduleCallDraft) { + Onyx.merge(`${ONYXKEYS.SCHEDULE_CALL_DRAFT}`, data); +} + +function clearBookingDraft() { + Onyx.set(`${ONYXKEYS.SCHEDULE_CALL_DRAFT}`, null); +} + +function confirmBooking(data: Required, currentUser: PersonalDetails) { + const scheduleURL = `${data.guide.scheduleURL}?name=${encodeURIComponent(currentUser.displayName ?? '')}&email=${encodeURIComponent( + currentUser?.login ?? '', + )}&utm_source=newDot&utm_medium=report&utm_content=${data.reportID}`; + + openExternalLink(scheduleURL); + clearBookingDraft(); + Navigation.dismissModal(); +} +export {getGuideCallAvailabilitySchedule, saveBookingDraft, clearBookingDraft, confirmBooking}; diff --git a/src/pages/ScheduleCall/AvailableBookingDay.tsx b/src/pages/ScheduleCall/AvailableBookingDay.tsx new file mode 100644 index 000000000000..5c8a22b3c383 --- /dev/null +++ b/src/pages/ScheduleCall/AvailableBookingDay.tsx @@ -0,0 +1,27 @@ +import React from 'react'; +import {View} from 'react-native'; +import type {DayProps} from '@components/DatePicker/CalendarPicker/Day'; +import Text from '@components/Text'; +import useStyleUtils from '@hooks/useStyleUtils'; +import useThemeStyles from '@hooks/useThemeStyles'; +import getButtonState from '@libs/getButtonState'; + +function AvailableBookingDay({disabled, selected, pressed, hovered, children}: DayProps) { + const themeStyles = useThemeStyles(); + const StyleUtils = useStyleUtils(); + return ( + + {children} + + ); +} + +AvailableBookingDay.displayName = 'AvailableBookingDay'; + +export default AvailableBookingDay; diff --git a/src/pages/ScheduleCall/ScheduleCallConfirmationPage.tsx b/src/pages/ScheduleCall/ScheduleCallConfirmationPage.tsx new file mode 100644 index 000000000000..f71d6a719b4c --- /dev/null +++ b/src/pages/ScheduleCall/ScheduleCallConfirmationPage.tsx @@ -0,0 +1,139 @@ +import {useRoute} from '@react-navigation/native'; +import {addMinutes, format} from 'date-fns'; +import React, {useCallback, useMemo} from 'react'; +import {useOnyx} from 'react-native-onyx'; +import FullPageOfflineBlockingView from '@components/BlockingViews/FullPageOfflineBlockingView'; +import Button from '@components/Button'; +import FixedFooter from '@components/FixedFooter'; +import HeaderWithBackButton from '@components/HeaderWithBackButton'; +import {FallbackAvatar} from '@components/Icon/Expensicons'; +import MenuItem from '@components/MenuItem'; +import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription'; +import {usePersonalDetails} from '@components/OnyxProvider'; +import ScreenWrapper from '@components/ScreenWrapper'; +import ScrollView from '@components/ScrollView'; +import Text from '@components/Text'; +import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; +import useLocalize from '@hooks/useLocalize'; +import useThemeStyles from '@hooks/useThemeStyles'; +import {confirmBooking} from '@libs/actions/ScheduleCall'; +import DateUtils from '@libs/DateUtils'; +import Navigation from '@libs/Navigation/Navigation'; +import type {PlatformStackRouteProp} from '@libs/Navigation/PlatformStackNavigation/types'; +import type {ScheduleCallParamList} from '@libs/Navigation/types'; +import CONST from '@src/CONST'; +import ONYXKEYS from '@src/ONYXKEYS'; +import ROUTES from '@src/ROUTES'; +import type SCREENS from '@src/SCREENS'; +import type {Timezone} from '@src/types/onyx/PersonalDetails'; + +function ScheduleCallConfirmationPage() { + const styles = useThemeStyles(); + const {translate} = useLocalize(); + const [scheduleCallDraft] = useOnyx(`${ONYXKEYS.SCHEDULE_CALL_DRAFT}`, {canBeMissing: false}); + const currentUserPersonalDetails = useCurrentUserPersonalDetails(); + const timezone: Timezone = currentUserPersonalDetails?.timezone ?? CONST.DEFAULT_TIME_ZONE; + const personalDetails = usePersonalDetails(); + const route = useRoute>(); + + const confirm = useCallback(() => { + if (!scheduleCallDraft?.timeSlot || !scheduleCallDraft?.date || !scheduleCallDraft.guide || !scheduleCallDraft.reportID) { + return; + } + confirmBooking( + { + date: scheduleCallDraft.date, + timeSlot: scheduleCallDraft.timeSlot, + guide: scheduleCallDraft.guide, + reportID: scheduleCallDraft.reportID, + }, + currentUserPersonalDetails, + ); + }, [currentUserPersonalDetails, scheduleCallDraft]); + + const guideDetails = useMemo( + () => (scheduleCallDraft?.guide?.accountID ? personalDetails?.[scheduleCallDraft?.guide?.accountID] : null), + [personalDetails, scheduleCallDraft?.guide?.accountID], + ); + + const dateTimeString = useMemo(() => { + if (!scheduleCallDraft?.timeSlot || !scheduleCallDraft.date) { + return ''; + } + const dateString = format(scheduleCallDraft.date, CONST.DATE.MONTH_DAY_YEAR_FORMAT); + const timeString = `${DateUtils.formatToLocalTime(scheduleCallDraft?.timeSlot)} - ${DateUtils.formatToLocalTime(addMinutes(scheduleCallDraft?.timeSlot, 30))}`; + + const timeZoneStirng = timezone.selected ? DateUtils.getZoneAbbreviation(new Date(scheduleCallDraft?.timeSlot), timezone.selected) : ''; + + return `${dateString} from ${timeString} ${timeZoneStirng}`; + }, [scheduleCallDraft?.date, scheduleCallDraft?.timeSlot, timezone.selected]); + + return ( + + { + if (!route?.params?.reportID) { + return; + } + Navigation.goBack(ROUTES.SCHEDULE_CALL_BOOK.getRoute(route?.params?.reportID)); + }} + /> + + + {translate('scheduledCall.confirmation.description')} + + { + if (!route?.params?.reportID) { + return; + } + Navigation.goBack(ROUTES.SCHEDULE_CALL_BOOK.getRoute(route?.params?.reportID)); + }} + /> + + + +