Skip to content

Commit 406eb74

Browse files
authored
Merge pull request #59951 from parasharrajat/parasharrajat/scheduling-call
Add Call Scheduling page for users
2 parents 67db54b + 6b2d7f1 commit 406eb74

27 files changed

+735
-17
lines changed

src/ONYXKEYS.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -501,6 +501,9 @@ const ONYXKEYS = {
501501
/** Information about loading states while talking with AI sales */
502502
TALK_TO_AI_SALES: 'talkToAISales',
503503

504+
/** Stores draft information while user is scheduling the call. */
505+
SCHEDULE_CALL_DRAFT: 'scheduleCallDraft',
506+
504507
/** Onyx updates that should be stored after sequential queue is flushed */
505508
QUEUE_FLUSHED_DATA: 'queueFlushedData',
506509

@@ -1147,6 +1150,7 @@ type OnyxValuesMapping = {
11471150
[ONYXKEYS.SHOULD_BILL_WHEN_DOWNGRADING]: boolean | undefined;
11481151
[ONYXKEYS.BILLING_RECEIPT_DETAILS]: OnyxTypes.BillingReceiptDetails;
11491152
[ONYXKEYS.NVP_SIDE_PANEL]: OnyxTypes.SidePanel;
1153+
[ONYXKEYS.SCHEDULE_CALL_DRAFT]: OnyxTypes.ScheduleCallDraft;
11501154
};
11511155

11521156
type OnyxDerivedValuesMapping = {

src/ROUTES.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2483,6 +2483,14 @@ const ROUTES = {
24832483
route: 'debug/transaction/:transactionID/violations/:index/json',
24842484
getRoute: (transactionID: string, index: string) => `debug/transaction/${transactionID}/violations/${index}/json` as const,
24852485
},
2486+
SCHEDULE_CALL_BOOK: {
2487+
route: 'r/:reportID/schedule-call/book',
2488+
getRoute: (reportID: string) => `r/${reportID}/schedule-call/book` as const,
2489+
},
2490+
SCHEDULE_CALL_CONFIRMATON: {
2491+
route: 'r/:reportID/schedule-call/confimation',
2492+
getRoute: (reportID: string) => `r/${reportID}/schedule-call/confimation` as const,
2493+
},
24862494
} as const;
24872495

24882496
/**

src/SCREENS.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,7 @@ const SCREENS = {
215215
REPORT_EXPORT: 'Report_Export',
216216
MISSING_PERSONAL_DETAILS: 'MissingPersonalDetails',
217217
DEBUG: 'Debug',
218+
SCHEDULE_CALL: 'ScheduleCall',
218219
},
219220
PUBLIC_CONSOLE_DEBUG: 'Console_Debug',
220221
ONBOARDING_MODAL: {
@@ -717,6 +718,10 @@ const SCREENS = {
717718
TRANSACTION_VIOLATION_CREATE: 'Debug_Transaction_Violation_Create',
718719
TRANSACTION_VIOLATION: 'Debug_Transaction_Violation',
719720
},
721+
SCHEDULE_CALL: {
722+
BOOK: 'ScheduleCall_Book',
723+
CONFIRMATION: 'ScheduleCall_Confirmation',
724+
},
720725
} as const;
721726

722727
type Screen = DeepValueOf<typeof SCREENS>;
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import React from 'react';
2+
import {View} from 'react-native';
3+
import Text from '@components/Text';
4+
import useStyleUtils from '@hooks/useStyleUtils';
5+
import useThemeStyles from '@hooks/useThemeStyles';
6+
import getButtonState from '@libs/getButtonState';
7+
8+
type DayProps = {
9+
/** Whether day is disabled */
10+
disabled?: boolean;
11+
12+
/** Whether day is selected */
13+
selected?: boolean;
14+
15+
/** Whether day is pressed */
16+
pressed?: boolean;
17+
18+
/** Whether day is hovered */
19+
hovered?: boolean;
20+
21+
/** date to show */
22+
children?: number;
23+
};
24+
25+
function Day({disabled, selected, pressed, hovered, children}: DayProps) {
26+
const themeStyles = useThemeStyles();
27+
const StyleUtils = useStyleUtils();
28+
return (
29+
<View
30+
style={[
31+
themeStyles.calendarDayContainer,
32+
selected ? themeStyles.buttonDefaultBG : {},
33+
!disabled ? StyleUtils.getButtonBackgroundColorStyle(getButtonState(hovered, pressed)) : {},
34+
]}
35+
>
36+
<Text style={disabled ? themeStyles.buttonOpacityDisabled : {}}>{children}</Text>
37+
</View>
38+
);
39+
}
40+
41+
Day.displayName = 'Day';
42+
43+
export default Day;
44+
export type {DayProps};

src/components/DatePicker/CalendarPicker/index.tsx

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,11 @@ import PressableWithoutFeedback from '@components/Pressable/PressableWithoutFeed
88
import Text from '@components/Text';
99
import useLocalize from '@hooks/useLocalize';
1010
import useResponsiveLayout from '@hooks/useResponsiveLayout';
11-
import useStyleUtils from '@hooks/useStyleUtils';
1211
import useThemeStyles from '@hooks/useThemeStyles';
1312
import DateUtils from '@libs/DateUtils';
14-
import getButtonState from '@libs/getButtonState';
1513
import CONST from '@src/CONST';
1614
import ArrowIcon from './ArrowIcon';
15+
import Day from './Day';
1716
import generateMonthMatrix from './generateMonthMatrix';
1817
import type CalendarPickerListItem from './types';
1918
import YearPickerModal from './YearPickerModal';
@@ -28,6 +27,12 @@ type CalendarPickerProps = {
2827
/** A maximum date (earliest) allowed to select */
2928
maxDate?: Date;
3029

30+
/** Restrict selection to only specific dates */
31+
selectedableDates?: string[];
32+
33+
/** Day component to render for dates */
34+
DayComponent?: typeof Day;
35+
3136
/** A function called when the date is selected */
3237
onSelected?: (selectedDate: string) => void;
3338
};
@@ -49,12 +54,13 @@ function CalendarPicker({
4954
minDate = setYear(new Date(), CONST.CALENDAR_PICKER.MIN_YEAR),
5055
maxDate = setYear(new Date(), CONST.CALENDAR_PICKER.MAX_YEAR),
5156
onSelected,
57+
DayComponent = Day,
58+
selectedableDates,
5259
}: CalendarPickerProps) {
5360
// eslint-disable-next-line rulesdir/prefer-shouldUseNarrowLayout-instead-of-isSmallScreenWidth
5461
const {isSmallScreenWidth} = useResponsiveLayout();
5562
const styles = useThemeStyles();
5663
const themeStyles = useThemeStyles();
57-
const StyleUtils = useStyleUtils();
5864
const {preferredLocale, translate} = useLocalize();
5965
const pressableRef = useRef<View>(null);
6066
const [currentDateView, setCurrentDateView] = useState(() => getInitialCurrentDateView(value, minDate, maxDate));
@@ -251,7 +257,8 @@ function CalendarPicker({
251257
const currentDate = new Date(currentYearView, currentMonthView, day);
252258
const isBeforeMinDate = currentDate < startOfDay(new Date(minDate));
253259
const isAfterMaxDate = currentDate > startOfDay(new Date(maxDate));
254-
const isDisabled = !day || isBeforeMinDate || isAfterMaxDate;
260+
const isSelectable = selectedableDates ? selectedableDates?.some((date) => isSameDay(parseISO(date), currentDate)) : true;
261+
const isDisabled = !day || isBeforeMinDate || isAfterMaxDate || !isSelectable;
255262
const isSelected = !!day && isSameDay(parseISO(value.toString()), new Date(currentYearView, currentMonthView, day));
256263
const handleOnPress = () => {
257264
if (!day || isDisabled) {
@@ -273,15 +280,14 @@ function CalendarPicker({
273280
dataSet={{[CONST.SELECTION_SCRAPER_HIDDEN_ELEMENT]: true}}
274281
>
275282
{({hovered, pressed}) => (
276-
<View
277-
style={[
278-
themeStyles.calendarDayContainer,
279-
isSelected ? themeStyles.buttonDefaultBG : {},
280-
!isDisabled ? StyleUtils.getButtonBackgroundColorStyle(getButtonState(hovered, pressed)) : {},
281-
]}
283+
<DayComponent
284+
selected={isSelected}
285+
disabled={isDisabled}
286+
hovered={hovered}
287+
pressed={pressed}
282288
>
283-
<Text style={isDisabled ? themeStyles.buttonOpacityDisabled : {}}>{day}</Text>
284-
</View>
289+
{day}
290+
</DayComponent>
285291
)}
286292
</PressableWithoutFeedback>
287293
);

src/languages/en.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5003,7 +5003,7 @@ const translations = {
50035003
description: 'Choose from the support options below:',
50045004
chatWithConcierge: 'Chat with Concierge',
50055005
scheduleSetupCall: 'Schedule a setup call',
5006-
scheduleADemo: 'Schedule demo',
5006+
scheduleACall: 'Schedule call',
50075007
questionMarkButtonTooltip: 'Get assistance from our team',
50085008
exploreHelpDocs: 'Explore help docs',
50095009
},
@@ -6278,6 +6278,21 @@ const translations = {
62786278
talkToConcierge: 'Talk to Concierge',
62796279
hangUp: 'Hang up',
62806280
},
6281+
scheduledCall: {
6282+
book: {
6283+
title: 'Schedule call',
6284+
description: 'Find a time that works for you.',
6285+
slots: 'Available times for ',
6286+
},
6287+
confirmation: {
6288+
title: 'Confirm call',
6289+
description: "Make sure the details below look good to you. Once you confirm the call, we'll send an invite with more info.",
6290+
setupSpecialist: 'Your setup specialist',
6291+
meetingLength: 'Meeting length',
6292+
dateTime: 'Date & time',
6293+
minutes: '30 minutes',
6294+
},
6295+
},
62816296
testDrive: {
62826297
quickAction: {
62836298
takeATwoMinuteTestDrive: 'Take a 2-minute test drive',

src/languages/es.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5056,7 +5056,7 @@ const translations = {
50565056
description: 'Elige una de las siguientes opciones:',
50575057
chatWithConcierge: 'Chatear con Concierge',
50585058
scheduleSetupCall: 'Concertar una llamada',
5059-
scheduleADemo: 'Programa una demostración',
5059+
scheduleACall: 'Programar llamada',
50605060
questionMarkButtonTooltip: 'Obtén ayuda de nuestro equipo',
50615061
exploreHelpDocs: 'Explorar la documentación de ayuda',
50625062
},
@@ -6802,6 +6802,21 @@ const translations = {
68026802
talkToConcierge: 'Habla con Concierge',
68036803
hangUp: 'Colgar',
68046804
},
6805+
scheduledCall: {
6806+
book: {
6807+
title: 'Programar llamada',
6808+
description: 'Encuentra un horario que funcione para ti.',
6809+
slots: 'Horarios disponibles para el ',
6810+
},
6811+
confirmation: {
6812+
title: 'Confirmar llamada',
6813+
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.',
6814+
setupSpecialist: 'Tu especialista asignado',
6815+
meetingLength: 'Duración de la reunión',
6816+
dateTime: 'Fecha y hora',
6817+
minutes: '30 minutos',
6818+
},
6819+
},
68056820
testDrive: {
68066821
quickAction: {
68076822
takeATwoMinuteTestDrive: 'Haz una proba de 2 minutos',
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
type GetGuideCallAvailabilityScheduleParams = {
2+
/** Admins room reportID */
3+
reportID: string;
4+
};
5+
6+
export default GetGuideCallAvailabilityScheduleParams;

src/libs/API/parameters/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -389,6 +389,7 @@ export type {ChangeTransactionsReportParams, TransactionThreadInfo} from './Chan
389389
export type {default as ResetBankAccountSetupParams} from './ResetBankAccountSetupParams';
390390
export type {default as SendRecapInAdminsRoomParams} from './SendRecapInAdminsRoomParams';
391391
export type {default as SetPolicyProhibitedExpensesParams} from './SetPolicyProhibitedExpensesParams';
392+
export type {default as GetGuideCallAvailabilityScheduleParams} from './GetGuideCallAvailabilitySchedule';
392393
export type {default as GetEmphemeralTokenParams} from './GetEmphemeralTokenParams';
393394
export type {default as CreateAppleDigitalWalletParams} from './CreateAppleDigitalWalletParams';
394395
export type {default as CompleteConciergeCallParams} from './CompleteConciergeCallParams';

src/libs/API/types.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -475,6 +475,7 @@ const WRITE_COMMANDS = {
475475
PAY_AND_DOWNGRADE: 'PayAndDowngrade',
476476
COMPLETE_CONCIERGE_CALL: 'CompleteConciergeCall',
477477
FINISH_CORPAY_BANK_ACCOUNT_ONBOARDING: 'FinishCorpayBankAccountOnboarding',
478+
GET_GUIDE_CALL_AVAILABILITY_SCHEDULE: 'GetGuideCallAvailabilitySchedule',
478479
} as const;
479480

480481
type WriteCommand = ValueOf<typeof WRITE_COMMANDS>;
@@ -970,6 +971,8 @@ type WriteCommandParameters = {
970971

971972
// Change transaction report
972973
[WRITE_COMMANDS.CHANGE_TRANSACTIONS_REPORT]: Parameters.ChangeTransactionsReportParams;
974+
975+
[WRITE_COMMANDS.GET_GUIDE_CALL_AVAILABILITY_SCHEDULE]: Parameters.GetGuideCallAvailabilityScheduleParams;
973976
};
974977

975978
const READ_COMMANDS = {

src/libs/DebugUtils.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -566,6 +566,12 @@ function validateReportDraftProperty(key: keyof Report | keyof ReportNameValuePa
566566
endDate: 'string',
567567
tripID: 'string',
568568
});
569+
case 'calendlySchedule':
570+
return validateObject<ObjectElement<ReportNameValuePairs, 'calendlySchedule'>>(value, {
571+
isLoading: 'boolean',
572+
data: 'object',
573+
errors: 'object',
574+
});
569575
case 'pendingAction':
570576
return validateConstantEnum(value, CONST.RED_BRICK_ROAD_PENDING_ACTION);
571577
case 'pendingFields':
@@ -630,6 +636,7 @@ function validateReportDraftProperty(key: keyof Report | keyof ReportNameValuePa
630636
errors: CONST.RED_BRICK_ROAD_PENDING_ACTION,
631637
createReport: CONST.RED_BRICK_ROAD_PENDING_ACTION,
632638
exportFailedTime: CONST.RED_BRICK_ROAD_PENDING_ACTION,
639+
calendlySchedule: CONST.RED_BRICK_ROAD_PENDING_ACTION,
633640
});
634641
}
635642
}

src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import type {
2323
ReportDetailsNavigatorParamList,
2424
ReportSettingsNavigatorParamList,
2525
RoomMembersNavigatorParamList,
26+
ScheduleCallParamList,
2627
SearchAdvancedFiltersParamList,
2728
SearchReportParamList,
2829
SearchSavedSearchParamList,
@@ -753,6 +754,11 @@ const DebugModalStackNavigator = createModalStackNavigator<DebugParamList>({
753754
[SCREENS.DEBUG.TRANSACTION_VIOLATION]: () => require<ReactComponentModule>('../../../../pages/Debug/TransactionViolation/DebugTransactionViolationPage').default,
754755
});
755756

757+
const ScheduleCallModalStackNavigator = createModalStackNavigator<ScheduleCallParamList>({
758+
[SCREENS.SCHEDULE_CALL.BOOK]: () => require<ReactComponentModule>('../../../../pages/ScheduleCall/ScheduleCallPage').default,
759+
[SCREENS.SCHEDULE_CALL.CONFIRMATION]: () => require<ReactComponentModule>('../../../../pages/ScheduleCall/ScheduleCallConfirmationPage').default,
760+
});
761+
756762
export {
757763
AddPersonalBankAccountModalStackNavigator,
758764
EditRequestStackNavigator,
@@ -793,4 +799,5 @@ export {
793799
DebugModalStackNavigator,
794800
WorkspaceConfirmationModalStackNavigator,
795801
ConsoleModalStackNavigator,
802+
ScheduleCallModalStackNavigator,
796803
};

src/libs/Navigation/AppNavigator/Navigators/RightModalNavigator.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,10 @@ function RightModalNavigator({navigation, route}: RightModalNavigatorProps) {
219219
name={SCREENS.RIGHT_MODAL.MISSING_PERSONAL_DETAILS}
220220
component={ModalStackNavigators.MissingPersonalDetailsModalStackNavigator}
221221
/>
222+
<Stack.Screen
223+
name={SCREENS.RIGHT_MODAL.SCHEDULE_CALL}
224+
component={ModalStackNavigators.ScheduleCallModalStackNavigator}
225+
/>
222226
</Stack.Navigator>
223227
</View>
224228
</NoDropZone>

src/libs/Navigation/linkingConfig/config.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1590,6 +1590,16 @@ const config: LinkingOptions<RootNavigatorParamList>['config'] = {
15901590
},
15911591
},
15921592
},
1593+
[SCREENS.RIGHT_MODAL.SCHEDULE_CALL]: {
1594+
screens: {
1595+
[SCREENS.SCHEDULE_CALL.BOOK]: {
1596+
path: ROUTES.SCHEDULE_CALL_BOOK.route,
1597+
},
1598+
[SCREENS.SCHEDULE_CALL.CONFIRMATION]: {
1599+
path: ROUTES.SCHEDULE_CALL_CONFIRMATON.route,
1600+
},
1601+
},
1602+
},
15931603
},
15941604
},
15951605

src/libs/Navigation/types.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1594,6 +1594,7 @@ type RightModalNavigatorParamList = {
15941594
[SCREENS.RIGHT_MODAL.SEARCH_SAVED_SEARCH]: NavigatorScreenParams<SearchSavedSearchParamList>;
15951595
[SCREENS.RIGHT_MODAL.MISSING_PERSONAL_DETAILS]: NavigatorScreenParams<MissingPersonalDetailsParamList>;
15961596
[SCREENS.RIGHT_MODAL.DEBUG]: NavigatorScreenParams<DebugParamList>;
1597+
[SCREENS.RIGHT_MODAL.SCHEDULE_CALL]: NavigatorScreenParams<ScheduleCallParamList>;
15971598
};
15981599

15991600
type TravelNavigatorParamList = {
@@ -1995,6 +1996,15 @@ type DebugParamList = {
19951996
};
19961997
};
19971998

1999+
type ScheduleCallParamList = {
2000+
[SCREENS.SCHEDULE_CALL.BOOK]: {
2001+
reportID: string;
2002+
};
2003+
[SCREENS.SCHEDULE_CALL.CONFIRMATION]: {
2004+
reportID: string;
2005+
};
2006+
};
2007+
19982008
type RootNavigatorParamList = PublicScreensParamList & AuthScreensParamList & LeftModalNavigatorParamList & SearchFullscreenNavigatorParamList;
19992009

20002010
type OnboardingFlowName = keyof OnboardingModalNavigatorParamList;
@@ -2084,6 +2094,7 @@ export type {
20842094
WorkspaceConfirmationNavigatorParamList,
20852095
TwoFactorAuthNavigatorParamList,
20862096
ConsoleNavigatorParamList,
2097+
ScheduleCallParamList,
20872098
TestDriveModalNavigatorParamList,
20882099
WorkspaceScreenName,
20892100
SettingsTabScreenName,

src/libs/Permissions.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,10 @@ function canUsePrivateDomainOnboarding(betas: OnyxEntry<Beta[]>): boolean {
7070
return !!betas?.includes(CONST.BETAS.PRIVATE_DOMAIN_ONBOARDING) || canUseAllBetas(betas);
7171
}
7272

73+
function canUseCallScheduling() {
74+
return false;
75+
}
76+
7377
export default {
7478
canUseDefaultRooms,
7579
canUseLinkPreviews,
@@ -86,4 +90,5 @@ export default {
8690
canUseInAppProvisioning,
8791
canUseGlobalReimbursementsOnND,
8892
canUsePrivateDomainOnboarding,
93+
canUseCallScheduling,
8994
};

0 commit comments

Comments
 (0)