Added Commitment Tracker (#652)

This commit is contained in:
Ashish Deo
2024-01-08 19:47:49 +05:30
committed by GitHub
parent 676be8060f
commit 9f68b361ec
15 changed files with 529 additions and 14 deletions

View File

@@ -672,6 +672,17 @@ export const CLICKSTREAM_EVENT_NAMES = {
description: 'FA_NEARBY_CASE_CLICKED',
},
FA_DAILY_COMMITMENT_CLICKED: {
name: 'FA_DAILY_COMMITMENT_CLICKED',
description: 'FA_DAILY_COMMITMENT_CLICKED',
},
FA_DAILY_COMMITMENT_SUBMITTED: {
name: 'FA_DAILY_COMMITMENT_SUBMITTED',
description: 'FA_DAILY_COMMITMENT_SUBMITTED',
},
FA_SNAPSHOT_LISTENER: {
name: 'FA_SNAPSHOT_LISTENER',
description: 'FA_SNAPSHOT_LISTENER',

View File

@@ -64,6 +64,9 @@ export enum ApiKeys {
GET_TELEPHONE_NUMBERS = 'GET_TELEPHONE_NUMBERS',
FIRESTORE_INCONSISTENCY_INFO = 'FIRESTORE_INCONSISTENCY_INFO',
GET_CASE_DETAILS_FROM_API = 'GET_CASE_DETAILS_FROM_API',
DAILY_COMMITMENT = 'DAILY_COMMITMENT',
GET_PTP_AMOUNT = 'GET_PTP_AMOUNT',
GET_VISIBILITY_STATUS = 'GET_VISIBILITY_STATUS',
}
export const API_URLS: Record<ApiKeys, string> = {} as Record<ApiKeys, string>;
@@ -112,8 +115,9 @@ API_URLS[ApiKeys.GET_CASH_COLLECTED] = '/allocation-cycle/cash-collected-split';
API_URLS[ApiKeys.GET_TELEPHONE_NUMBERS] = '/v2/collection-cases/telephones-view/{loanAccountNumber}';
API_URLS[ApiKeys.FIRESTORE_INCONSISTENCY_INFO] = '/cases/sync-status';
API_URLS[ApiKeys.GET_CASE_DETAILS_FROM_API] = '/collection-cases/minimal-collection-case-view/{caseId}';
API_URLS[ApiKeys.DAILY_COMMITMENT] = '/daily-commitment';
API_URLS[ApiKeys.GET_PTP_AMOUNT] = '/ptps-due-view/agent-detail';
API_URLS[ApiKeys.GET_VISIBILITY_STATUS] = '/daily-commitment/visibility';
export const API_STATUS_CODE = {
OK: 200,

View File

@@ -68,7 +68,7 @@ export interface IUserSlice extends IUser {
agentAttendance: {
showAttendanceBanner: boolean;
attendanceDate: string;
}
};
}
const initialState: IUserSlice = {

View File

@@ -87,6 +87,7 @@ export const ToastMessages = {
WHATSAPP_NOT_INSTALLED: 'WhatsApp is not installed on your device',
FILE_DOWNLOAD_SUCCESS: 'File downloaded successfully',
FILE_DOWNLOAD_FAILURE: 'File download failed',
COMMITMENT_SUBMITTED_SUCCESSFULLY: 'Commitment submitted successfully',
};
export enum BOTTOM_TAB_ROUTES {

View File

@@ -1,6 +1,7 @@
import React, { useEffect, useMemo } from 'react';
import React, { useEffect, useMemo, useState } from 'react';
import { useAppDispatch, useAppSelector } from '@hooks';
import CasesList from './CasesList';
import Text from '../../../RN-UI-LIB/src/components/Text';
import { RootState } from '@store';
import { initCrashlytics } from '@utils/firebaseUtils';
import Layout from '../layout/Layout';
@@ -10,7 +11,6 @@ import Profile from '../Profile';
import ProfileIcon from '@rn-ui-lib/icons/ProfileIcon';
import VisitPlanIcon from '@rn-ui-lib/icons/VisitPlanIcon';
import CasesActionButtons from './CasesActionButtons';
import FullScreenLoader from '@rn-ui-lib/components/FullScreenLoader';
import { getCurrentScreen } from '@utils/navigationUtlis';
import {
resetSelectedTodoList,
@@ -21,11 +21,17 @@ import {
import { addClickstreamEvent } from '@services/clickstreamEventService';
import { CLICKSTREAM_EVENT_NAMES } from '@common/Constants';
import { BOTTOM_TAB_ROUTES } from './constants';
import { getSelfieDocument } from '@actions/profileActions';
import FullScreenLoaderWrapper from '@common/FullScreenLoaderWrapper';
import DashboardIcon from '../../assets/icons/DashboardIcon';
import DashBoardScreens from '../Dashboard/DashBoardScreens';
import { isAgentDashboardVisible } from '@screens/Dashboard/utils';
import { View, StyleSheet, TouchableOpacity } from 'react-native';
import { COLORS } from '@rn-ui-lib/colors';
import { GenericStyles } from '@rn-ui-lib/styles';
import DailyCommitmentIcon from '@rn-ui-lib/icons/DailyCommitmentIcon';
import AddCommitment from '../dailyCommitment/DailyCommitmentBottomSheet';
import { getVisibilityStatus } from '@screens/dailyCommitment/actions';
import { logError } from '@components/utlis/errorUtils';
const AllCasesMain = () => {
const { pendingList, pinnedList, completedList, loading } = useAppSelector(
@@ -35,15 +41,57 @@ const AllCasesMain = () => {
const dispatch = useAppDispatch();
const isTeamLead = useAppSelector((state) => state.user.isTeamLead);
const showAgentDashboard = isAgentDashboardVisible();
const [openBottomSheet, setOpenBottomSheet] = useState(false);
const [isCommitmentFormVisible, setIsCommitmentFormVisible] = useState(false);
const [isCommitmentSubmitted, setIsCommitmentSubmitted] = useState(true);
useEffect(() => {
getVisibilityStatus()
.then((response) => {
setIsCommitmentSubmitted(response?.isCommitmentFilled);
setIsCommitmentFormVisible(response?.isCommitmentBannerVisible);
})
.catch((err) => {
logError(err);
});
}, [isCommitmentSubmitted,isCommitmentFormVisible]);
const shouldShowBanner = useMemo(() => {
return !isTeamLead && !isCommitmentSubmitted && isCommitmentFormVisible;
}, [isCommitmentSubmitted, isCommitmentFormVisible, isTeamLead]);
const openCommitmentScreen = () => {
setOpenBottomSheet(true);
};
const CommitmentComponent = () => {
return (
<>
<TouchableOpacity
activeOpacity={0.8}
style={[GenericStyles.centerAlignedRow, styles.container]}
onPress={openCommitmentScreen}
>
<DailyCommitmentIcon />
<Text bold dark style={[GenericStyles.ml8, GenericStyles.mr8, styles.updateText]}>
Update your daily commitment
</Text>
</TouchableOpacity>
</>
);
};
const HOME_SCREENS: ITabScreen[] = useMemo(() => {
const bottomSheetScreens = [
{
name: BOTTOM_TAB_ROUTES.Cases,
component: () => (
<CasesList casesList={[...pendingList, ...pinnedList, ...completedList]} allCasesView />
<>
<CasesList casesList={[...pendingList, ...pinnedList, ...completedList]} allCasesView />
{shouldShowBanner ? <CommitmentComponent /> : null}
</>
),
icon: CasesIcon,
},
@@ -51,7 +99,12 @@ const AllCasesMain = () => {
if (!isTeamLead) {
bottomSheetScreens.push({
name: BOTTOM_TAB_ROUTES.VisitPlan,
component: () => <CasesList casesList={pinnedList} isVisitPlan />,
component: () => (
<>
<CasesList casesList={pinnedList} isVisitPlan />
{shouldShowBanner ? <CommitmentComponent /> : null}
</>
),
icon: VisitPlanIcon,
});
}
@@ -59,18 +112,34 @@ const AllCasesMain = () => {
if (showAgentDashboard) {
bottomSheetScreens.push({
name: BOTTOM_TAB_ROUTES.Dashboard,
component: () => <DashBoardScreens />,
component: () => (
<>
<DashBoardScreens />
{shouldShowBanner ? <CommitmentComponent /> : null}
</>
),
icon: DashboardIcon,
});
}
bottomSheetScreens.push({
name: BOTTOM_TAB_ROUTES.Profile,
component: () => <Profile />,
component: () => (
<>
<Profile />
{shouldShowBanner ? <CommitmentComponent /> : null}
</>
),
icon: ProfileIcon,
});
return bottomSheetScreens;
}, [pendingList, pinnedList, isTeamLead, showAgentDashboard]);
}, [
pendingList,
pinnedList,
isTeamLead,
showAgentDashboard,
shouldShowBanner,
]);
const onTabPressHandler = (e: any) => {
const nextTab = e?.data?.routeName;
@@ -99,8 +168,18 @@ const AllCasesMain = () => {
onTabPress={(e) => onTabPressHandler(e)}
/>
<CasesActionButtons />
<AddCommitment openBottomSheet={openBottomSheet} setOpenBottomSheet={setOpenBottomSheet} />
</Layout>
);
};
const styles = StyleSheet.create({
container: {
backgroundColor: COLORS.BACKGROUND.TEAL,
height: 40,
},
updateText: {
color: COLORS.TEXT.TEAL,
},
});
export default AllCasesMain;
export default AllCasesMain;

View File

@@ -67,6 +67,7 @@ function AuthRouter() {
};
const CHECK_ATTENDANCE_TIME = 10000;
useEffect(() => {
const appStateChange = AppState.addEventListener('change', async (change) => {
if (change !== 'active') return;

View File

@@ -0,0 +1,221 @@
import { Keyboard, SafeAreaView, View, StyleSheet, TouchableOpacity } from 'react-native';
import React, { useEffect, useState } from 'react';
import Text from '../../../RN-UI-LIB/src/components/Text';
import TextInput, { TextInputMaskType } from '../../../RN-UI-LIB/src/components/TextInput';
import Button from '../../../RN-UI-LIB/src/components/Button';
import { GenericStyles } from '../../../RN-UI-LIB/src/styles';
import { Controller, FieldErrors, useForm } from 'react-hook-form';
import { isValidAmountEntered } from '../../components/utlis/commonFunctions';
import { addCommitmentApi, getTodaysPtpAmount} from './actions';
import Heading from '@rn-ui-lib/components/Heading';
import DailyCommitmentIcon from '@rn-ui-lib/icons/DailyCommitmentIcon';
import BottomSheetWrapper from '@common/BottomSheetWrapper';
import CloseIcon from '@rn-ui-lib/icons/CloseIcon';
import { COLORS } from '@rn-ui-lib/colors';
import { EXAMPLE_VALUES } from './constants';
import { useAppDispatch} from '@hooks';
import { formatAmount } from '@rn-ui-lib/utils/amount';
import { getErrorCashMessage, getErrorVisitMessage } from './ErrorMessages';
import { IAddCommitment, IAddCommitmentProps } from '@interfaces/commitmentTracker.types';
import HeaderComponent from './headerComponent';
import { logError } from '@components/utlis/errorUtils';
const AddCommitment: React.FC<IAddCommitmentProps> = ({ openBottomSheet, setOpenBottomSheet }) => {
const [loading, setLoading] = useState(false);
const {
control,
formState: { errors, dirtyFields },
trigger,
reset,
handleSubmit,
} = useForm<IAddCommitment>({
mode: 'onChange',
});
const [submitErrors, setSubmitErrors] = useState<FieldErrors<IAddCommitment>>({});
const [todaysPtpAmount, setTodaysPtpAmount] = useState(1000);
useEffect(() => {
getTodaysPtpAmount()
.then((response) => {
setTodaysPtpAmount(response?.todaysPtpAmount);
})
.catch((err) => {logError(err);});
}, []);
const onBack = () => {
setOpenBottomSheet((prev) => !prev);
setSubmitErrors({});
reset();
};
const visitError = Boolean(
dirtyFields?.visitsCommitted &&
(errors?.visitsCommitted || submitErrors?.visitsCommitted)
);
const cashError = Boolean(
dirtyFields?.cashCommitted &&
(errors?.cashCommitted || submitErrors?.cashCommitted)
);
const errorCashMessage = getErrorCashMessage(errors);
const errorVisitMessage = getErrorVisitMessage(errors);
const [isKeyboardVisible, setKeyboardVisible] = useState(false);
useEffect(() => {
const keyboardDidShowListener = Keyboard.addListener('keyboardDidShow', () => {
setKeyboardVisible(true);
});
const keyboardDidHideListener = Keyboard.addListener('keyboardDidHide', () => {
setKeyboardVisible(false);
});
return () => {
keyboardDidShowListener.remove();
keyboardDidHideListener.remove();
};
}, []);
const dispatch = useAppDispatch();
const addCtaHandler = (data: IAddCommitment) => {
setLoading(true);
dispatch(
addCommitmentApi(data, () => {
setLoading(false);
})
);
onBack();
};
const handleError = (data: FieldErrors<IAddCommitment>) => {
setSubmitErrors(data);
};
return (
<BottomSheetWrapper
heightPercentage={60}
visible={openBottomSheet}
setVisible={() => setOpenBottomSheet((prev) => !prev)}
moveForKeyboard={0.2}
HeaderNode={() => <HeaderComponent onBack={onBack} />}
>
<SafeAreaView style={[GenericStyles.whiteBackground, { height: '100%' }]}>
<View style={[GenericStyles.ph16, GenericStyles.pv24]}>
<View>
<Text style={[GenericStyles.mb8]}>
Cash collection commitment
<Text style={styles.mandatoryStar}> *</Text>
</Text>
<Controller
control={control}
render={({ field: { onChange, onBlur, value } }) => (
<TextInput
value={value}
onChangeText={(number) => {
onChange(number);
trigger();
}}
keyboardType="numeric"
placeholder={EXAMPLE_VALUES.DUMMY_PTP_AMOUNT}
placeholderTextColor='COLORS.TEXT.LIGHT'
required={true}
maskType={TextInputMaskType.CURRENCY}
error={cashError}
errorMessage={errorCashMessage}
/>
)}
name="cashCommitted"
rules={{
required: true,
validate: (value) => isValidAmountEntered(Number(value)),
min: 0,
max: 1e6,
}}
/>
</View>
<View style={GenericStyles.mt4}>
<Text light>PTP amount for today: {formatAmount(todaysPtpAmount)}</Text>
</View>
<View style={[GenericStyles.mt12]}>
<Text style={[GenericStyles.mb8]}>
Number of visits<Text style={styles.mandatoryStar}> *</Text>
</Text>
<Controller
control={control}
render={({ field: { onChange, onBlur, value } }) => (
<TextInput
value={value}
onChangeText={(number) => {
onChange(number);
trigger();
}}
keyboardType="numeric"
placeholder={EXAMPLE_VALUES.DUMMY_VISIT_COUNT}
placeholderTextColor='COLORS.TEXT.LIGHT'
maskType={TextInputMaskType.NUMBER}
error={visitError}
errorMessage={errorVisitMessage}
required={true}
/>
)}
name="visitsCommitted"
rules={{
required: true,
validate: (value) => isValidAmountEntered(Number(value)),
pattern: /^[0-9]*$/,
min: 0,
max: 99,
}}
/>
</View>
</View>
<View style={[styles.buttonContainer, isKeyboardVisible && { display: 'none' }]}>
<Button
variant="primary"
title="Commit"
style={[GenericStyles.w100]}
onPress={handleSubmit(
(data) => addCtaHandler(data),
(data) => handleError(data)
)}
showLoader={loading}
/>
</View>
</SafeAreaView>
</BottomSheetWrapper>
);
};
const styles = StyleSheet.create({
heading: {
...GenericStyles.relative,
},
bottomSheet: {
marginTop: 10,
},
headingCrossIcon: {
...GenericStyles.absolute,
right: 10,
},
mandatoryStar: {
color: COLORS.TEXT.RED,
},
shadowContainer: {
shadowColor: COLORS.BACKGROUND.BLACK,
shadowOffset: {
width: 0,
height: 4,
},
shadowOpacity: 0.08,
shadowRadius: 20,
borderRadius: 8,
},
buttonContainer: {
bottom: 60,
position: 'absolute',
marginTop: 24,
width: '100%',
paddingHorizontal: 24,
},
});
export default AddCommitment;

View File

@@ -0,0 +1,33 @@
import { FieldErrorsImpl } from "react-hook-form";
import { ERROR_MESSAGES } from "./constants";
export const getErrorCashMessage = (errors: Partial<FieldErrorsImpl<{ cashCommitted: string; visitsCommitted: string; }>>) => {
let errorCashMessage = '';
switch (errors?.cashCommitted?.type) {
case 'required':
errorCashMessage = ERROR_MESSAGES.PTP_REQUIRED_ERROR;
break;
case 'min':
case 'max':
errorCashMessage = ERROR_MESSAGES.PTP_AMOUNT_ERROR;
break;
}
return errorCashMessage;
};
export const getErrorVisitMessage = (errors: Partial<FieldErrorsImpl<{ cashCommitted: string; visitsCommitted: string; }>>) => {
let errorVisitMessage = '';
switch (errors?.visitsCommitted?.type) {
case 'required':
errorVisitMessage = ERROR_MESSAGES.VISIT_REQUIRED_ERROR;
break;
case 'min':
case 'max':
errorVisitMessage = ERROR_MESSAGES.VISIT_COUNT_ERROR;
break;
case 'pattern':
errorVisitMessage = ERROR_MESSAGES.VISIT_COUNT_ERROR_2;
break;
}
return errorVisitMessage;
};

View File

@@ -0,0 +1,67 @@
import { AxiosResponse } from 'axios';
import { toast } from '../../../RN-UI-LIB/src/components/toast';
import { GenericFunctionArgs } from '../../common/GenericTypes';
import axiosInstance, {
API_STATUS_CODE,
ApiKeys,
getApiUrl,
} from '../../components/utlis/apiHelper';
import { COMMITMENT_TYPE } from './constants';
import { addClickstreamEvent } from '@services/clickstreamEventService';
import { CLICKSTREAM_EVENT_NAMES } from '@common/Constants';
import { AppDispatch } from '@store';
import { IAddCommitment } from '@interfaces/commitmentTracker.types';
import { getSyncTime } from '@hooks/capturingApi';
import { logError } from '@components/utlis/errorUtils';
export const getTodaysPtpAmount = async () => {
try {
const url = getApiUrl(ApiKeys.GET_PTP_AMOUNT);
const response = await axiosInstance.get(url);
return response?.data;
} catch (err) {
logError(err as Error, 'Error fetching today\'s PTP amount:');
return COMMITMENT_TYPE.EMPTY_PTP_AMOUNT;
}
};
export const getVisibilityStatus = async () => {
try {
const url = getApiUrl(ApiKeys.GET_VISIBILITY_STATUS);
const response = await axiosInstance.get(url);
return response?.data;
} catch (err) {
logError(err as Error, 'Error fetching visibility status:');
}
};
export const addCommitmentApi =
(data: IAddCommitment, afterApiCallback?: GenericFunctionArgs) => async (dispatch: AppDispatch) => {
const { cashCommitted, visitsCommitted } = data;
const url = getApiUrl(ApiKeys.DAILY_COMMITMENT);
const timestamp = await getSyncTime();
const payload = data;
axiosInstance
.post(url, payload)
.then((response: AxiosResponse) => {
if (response.status === API_STATUS_CODE.OK) {
toast({
type: 'info',
text1: COMMITMENT_TYPE.COMMITMENT_SUBMITTED_SUCCESSFULLY,
});
addClickstreamEvent(CLICKSTREAM_EVENT_NAMES.FA_DAILY_COMMITMENT_SUBMITTED);
}
})
.catch((error) => {
toast({
type: 'error',
text1: COMMITMENT_TYPE.COMMITMENT_SUBMIT_ERROR,
});
return error?.response?.data || error?.message || error;
})
.finally(() => {
if (typeof afterApiCallback === 'function') {
afterApiCallback();
}
});
};

View File

@@ -0,0 +1,16 @@
export enum EXAMPLE_VALUES {
DUMMY_PTP_AMOUNT = 'Eg: ₹10,000',
DUMMY_VISIT_COUNT = 'Eg: 12',
}
export enum ERROR_MESSAGES {
VISIT_REQUIRED_ERROR = 'Please enter a value to commit',
PTP_REQUIRED_ERROR = 'Please enter an amount to commit',
VISIT_COUNT_ERROR = 'Enter a value within 0-99',
PTP_AMOUNT_ERROR = 'Enter an amount within ₹10,00,000',
VISIT_COUNT_ERROR_2 = 'Please enter a valid number',
}
export enum COMMITMENT_TYPE {
COMMITMENT_SUBMITTED_SUCCESSFULLY = 'Commitment submitted successfully',
COMMITMENT_SUBMIT_ERROR = 'Error submitting commitment',
EMPTY_PTP_AMOUNT = '---',
}

View File

@@ -0,0 +1,42 @@
import { COLORS } from '@rn-ui-lib/colors';
import Heading from '@rn-ui-lib/components/Heading';
import CloseIcon from '@rn-ui-lib/icons/CloseIcon';
import DailyCommitmentIcon from '@rn-ui-lib/icons/DailyCommitmentIcon';
import { GenericStyles } from '@rn-ui-lib/styles';
import { StyleSheet } from 'react-native';
import React from 'react';
import { View, TouchableOpacity } from 'react-native';
const HeaderComponent = ({ onBack }) => {
return (
<View style={[GenericStyles.row, GenericStyles.alignCenter, styles.heading]}>
<View style={[GenericStyles.mh12]}>
<DailyCommitmentIcon />
</View>
<View style={[GenericStyles.mr20]}>
<Heading dark type="h3">
Update your daily commitment
</Heading>
</View>
<View style={styles.headingCrossIcon}>
<TouchableOpacity activeOpacity={0.7} onPress={onBack}>
<CloseIcon size={20} color={COLORS.TEXT.LIGHT} />
</TouchableOpacity>
</View>
</View>
);
};
const styles = StyleSheet.create({
heading: {
...GenericStyles.relative,
},
headingCrossIcon: {
...GenericStyles.absolute,
right: 10,
},
});
export default HeaderComponent;

View File

@@ -38,6 +38,8 @@ export interface INotification {
date: string;
time: string;
caseCount: number;
cashCommitted: number;
visitsCommitted: number;
};
template: {
id: number;

View File

@@ -27,6 +27,8 @@ const NotificationTemplate: React.FC<INotificationTemplateProps> = ({ data }) =>
paymentTimestamp,
date,
caseCount,
cashCommitted,
visitsCommitted,
} = params || {};
switch (templateName) {
@@ -199,8 +201,23 @@ const NotificationTemplate: React.FC<INotificationTemplateProps> = ({ data }) =>
</Text>
</View>
);
case NotificationTypes.AGENT_COMMITMENTS_VISIT_CASH:
return (
<Text>
<Text light>Your commitment for today has been updated. Cash commitment - </Text>
{formatAmount(cashCommitted)}. <Text light> Visit Commitment - </Text> {visitsCommitted}{' '}
</Text>
);
case NotificationTypes.AGENT_DAILY_COMMITMENT:
return (
<Text>
<Text light>Your commitment for today has been submitted. Cash commitment - </Text>
{formatAmount(cashCommitted)}. <Text light>Visit Commitment - </Text> {visitsCommitted}{' '}
</Text>
);
default:
return <Text>New notification</Text>;
return <Text>New notification </Text>;
}
};

View File

@@ -30,6 +30,8 @@ export enum NotificationTypes {
VISIT_PLAN_UPDATE_NOTIFICATION_TEMPLATE = 'VISIT_PLAN_UPDATE_NOTIFICATION_TEMPLATE',
AGENT_ID_APPROVED_TEMPLATE = 'AGENT_ID_APPROVED_TEMPLATE',
AGENT_ID_REJECTED_TEMPLATE = 'AGENT_ID_REJECTED_TEMPLATE',
AGENT_COMMITMENTS_VISIT_CASH = 'AGENT_COMMITMENTS_VISIT_CASH',
AGENT_DAILY_COMMITMENT = 'AGENT_DAILY_COMMITMENT',
}
export const NotificationIconsMap = {
@@ -54,6 +56,9 @@ export const NotificationIconsMap = {
[NotificationTypes.VISIT_PLAN_UPDATE_NOTIFICATION_TEMPLATE]: <NotificationVisitPlanIcon />,
[NotificationTypes.AGENT_ID_APPROVED_TEMPLATE]: <IDCardApproveIcon />,
[NotificationTypes.AGENT_ID_REJECTED_TEMPLATE]: <IDCardRejectIcon />,
[NotificationTypes.AGENT_COMMITMENTS_VISIT_CASH]: <NotificationIcon />,
[NotificationTypes.AGENT_DAILY_COMMITMENT]: <NotificationIcon />,
};
export enum WidgetStatus {

View File

@@ -0,0 +1,16 @@
export interface IAddCommitmentProps {
openBottomSheet: boolean;
setOpenBottomSheet: React.Dispatch<React.SetStateAction<boolean>>;
}
export interface IAddCommitment {
cashCommitted: string;
visitsCommitted: string;
}
export interface IAlterVIsibility {
isCommitmentFilled : boolean;
isCommitmentBannerVisible : boolean;
}