π Parent Appointments
This document covers the comprehensive appointment management system for parents in the Com DEALL mobile application, including appointment booking, payment processing, status tracking, and appointment management.
π― Overviewβ
The parent appointment system enables parents to book therapy sessions for their children, manage payments, track appointment status, and handle rescheduling. The system supports multiple appointment types and integrates with payment processing.
ποΈ Architectureβ
Appointment Management Componentsβ
Parent Appointment System
βββ Appointment Booking
βββ Payment Processing
βββ Status Management
βββ Rescheduling
βββ Appointment History
βββ Transaction Management
Data Flowβ
Appointment Request β Payment Processing β Confirmation β Status Update β Notification
β β β β β
Parent Booking β Razorpay/iOS/Android β Database β Real-time Sync β Parent Notification
π± Mobile Implementationβ
Appointment Home Screenβ
Tab Navigation System:
- System implements three main appointment tabs: Requested, Upcoming, and Past
- Each tab displays appointments filtered by specific status criteria
- Tab navigation uses Material Top Tab Navigator for smooth transitions
- Each tab maintains its own state and filtering logic independently
Tab Status Logic:
- Requested Tab: Shows appointments with
Appointment_Status_Enum.Scheduledstatus - Upcoming Tab: Shows appointments with
Appointment_Status_Enum.ConfirmedandAppointment_Status_Enum.Startedstatus - Past Tab: Shows appointments with
Appointment_Status_Enum.Rejected,Appointment_Status_Enum.Cancelled,Appointment_Status_Enum.Completed, andAppointment_Status_Enum.Expiredstatus
Filtering System:
- Each tab supports date range filtering with start and end time selection
- Time filter component provides quick day picker for future/past appointments
- Filter state management includes date range, time range, and filter options
- Filter application updates query variables for real-time data filtering
// AppointmentsHome.tsx - Main appointment management screen with tab navigation
const AppointmentHome = ({ navigation }: ScreenProps) => {
const [index, setIndex] = React.useState(0);
// Tab navigation configuration with status-based filtering
const TopTab = createMaterialTopTabNavigator();
// Tab configuration with status-specific appointment display
const tabConfig = [
{
name: 'RequestedAppointment',
label: 'Pending',
status: [Appointment_Status_Enum.Scheduled],
quickPickerType: 'FUTURE'
},
{
name: 'UpcomingAppointment',
label: 'Upcoming',
status: [Appointment_Status_Enum.Confirmed, Appointment_Status_Enum.Started],
quickPickerType: 'FUTURE'
},
{
name: 'PastAppointment',
label: 'Past',
status: [Appointment_Status_Enum.Rejected, Appointment_Status_Enum.Cancelled,
Appointment_Status_Enum.Completed, Appointment_Status_Enum.Expired],
quickPickerType: 'PAST'
}
];
// Navigation header configuration with back button and title
useLayoutEffect(() => {
navigation.setOptions({
headerLeft: () => (
<Box alignItems="center" flexDirection="row">
<BackButton navigation={navigation} />
<Text color="secondary20" variant="heading4Light">
{t('appointment')}
</Text>
</Box>
),
});
}, [navigation, colors.secondary63, t]);
};
Upcoming Appointments Tabβ
Status-Based Filtering Logic:
- Displays appointments with
Appointment_Status_Enum.ConfirmedandAppointment_Status_Enum.Startedstatus - Filters appointments to show only future sessions that are confirmed or currently in progress
- Excludes past appointments and scheduled (pending) appointments from this tab
Data Processing Logic:
- Processes appointment data to extract therapist information, child details, and time slots
- Maps appointment slots to find the active time slot using
in_useflag - Calculates time differences to determine if appointment is still upcoming
- Formats appointment data for display with proper date/time formatting
Filter Management:
- Implements date range filtering with start and end time selection
- Supports quick day picker for future date selection
- Maintains filter state with timestamp conversion for query optimization
- Provides filter reset functionality with clear filter option
// Upcoming.tsx - Upcoming appointments with comprehensive filtering
const Upcoming = () => {
const [dateRangeData, setDateRangeData] = useState<TimeFilterRange>({
startTime: '',
endTime: '',
});
const [filterOption, setFilterOption] = useState<string>('');
const [timeRangeData, setTimeRangeData] = useState<TimeFilterRange>({
startTime: undefined,
endTime: undefined,
});
const [isFilterApplied, setIsFilterApplied] = useState(false);
const [filterTimeStamp, setFilterTimeStamp] = useState<TimeFilterRange>({
startTime: '',
endTime: '',
});
// Query configuration for upcoming appointments with status filtering
const { data, refetch, loading, error } = useGetAppointmentListQuery({
variables: {
status: [
Appointment_Status_Enum.Confirmed,
Appointment_Status_Enum.Started,
],
StartTime: {
_gte: filterTimeStamp?.startTime || dayjs().startOf('day').toISOString(),
},
EndTime: filterTimeStamp?.endTime
? { _lte: filterTimeStamp?.endTime }
: {},
},
});
// Appointment data processing with comprehensive filtering and time validation
const appointments = useMemo(() => {
return (
data?.appointment
?.filter(f => {
const { endTimeSlot } = getAppointmentSlot(f.appointment_slots);
return dayjs(new Date()).isBefore(endTimeSlot?.EndTime);
})
?.map(entry => {
const { startTimeSlot, endTimeSlot } = getAppointmentSlot(
entry.appointment_slots,
);
const slot = entry?.appointment_slots?.find(s => s.in_use);
const startTime = slot?.timeSlot?.StartTime;
const endTime = slot?.timeSlot?.EndTime;
return {
name: entry?.appointment_therapists[0]?.therapist?.user?.name,
child: entry?.child?.name,
degree: entry.appointment_therapists[0]?.therapist?.degree_name,
specialty: entry?.appointment_therapists[0]?.therapist
?.therapist_specialities[0]?.speciality?.title,
date: startTime,
timeSlot: {
startTime: startTime,
endTime: endTime,
},
status: entry.status!,
img: entry.appointment_therapists[0]?.therapist?.user
?.profile_picture?.path,
id: entry.id,
paymentExpiry: entry?.payment_expiry,
rescheduleCount: entry?.reschedule_count,
rescheduleRequested: entry?.reschedule_requested,
appointmentType: entry?.appointment_type,
};
}) || []
);
}, [data?.appointment]);
// Filter application logic with comprehensive state management
const handleApplyFilter = (
dateRange: TimeFilterRange,
timeRange: TimeFilterRange,
selectedOption: string,
isApply: boolean,
) => {
setDateRangeData(dateRange);
setTimeRangeData(timeRange);
setFilterOption(selectedOption);
setIsFilterApplied(isApply);
setFilterTimeStamp({ ...timeRange });
};
};
Requested Appointments Tabβ
Status-Based Filtering Logic:
- Displays appointments with
Appointment_Status_Enum.Scheduledstatus - Shows appointments that are pending confirmation or payment
- Includes appointments that are waiting for therapist confirmation or payment processing
Data Processing Logic:
- Processes scheduled appointments with payment expiry tracking
- Handles payment link expiration logic with time-based calculations
- Manages reschedule request status and count tracking
- Formats appointment data for pending status display
// Requested.tsx - Requested appointments with payment tracking
const Requested = () => {
const [dateRangeData, setDateRangeData] = useState<TimeFilterRange>({
startTime: '',
endTime: '',
});
const [filterOption, setFilterOption] = useState<string>('');
const [timeRangeData, setTimeRangeData] = useState<TimeFilterRange>({
startTime: undefined,
endTime: undefined,
});
const [isFilterApplied, setIsFilterApplied] = useState(false);
const [filterTimeStamp, setFilterTimeStamp] = useState<TimeFilterRange>({
startTime: '',
endTime: '',
});
// Query configuration for requested appointments with scheduled status
const { data, refetch, loading, error } = useGetAppointmentListQuery({
variables: {
status: [Appointment_Status_Enum.Scheduled],
StartTime: {
_gte: filterTimeStamp?.startTime || dayjs().startOf('day').toISOString(),
},
EndTime: filterTimeStamp?.endTime
? { _lte: filterTimeStamp?.endTime }
: {},
},
});
// Appointment data processing with payment expiry and reschedule tracking
const appointments = useMemo(() => {
return (
data?.appointment
?.filter(f => {
const { endTimeSlot } = getAppointmentSlot(f.appointment_slots);
return dayjs(new Date()).isBefore(endTimeSlot?.EndTime);
})
?.map(entry => {
const { startTimeSlot, endTimeSlot } = getAppointmentSlot(
entry.appointment_slots,
);
const slot = entry?.appointment_slots?.find(s => s.in_use);
const startTime = slot?.timeSlot?.StartTime;
const endTime = slot?.timeSlot?.EndTime;
return {
name: entry?.appointment_therapists[0]?.therapist?.user?.name,
child: entry?.child?.name,
degree: entry.appointment_therapists[0]?.therapist?.degree_name,
specialty: entry?.appointment_therapists[0]?.therapist
?.therapist_specialities[0]?.speciality?.title,
date: startTime,
timeSlot: {
startTime: startTime,
endTime: endTime,
},
status: entry.status!,
img: entry.appointment_therapists[0]?.therapist?.user
?.profile_picture?.path,
id: entry.id,
paymentExpiry: entry?.payment_expiry,
rescheduleCount: entry?.reschedule_count,
appointmentType: entry?.appointment_type,
};
}) || []
);
}, [data?.appointment]);
};
Past Appointments Tabβ
Status-Based Filtering Logic:
- Displays appointments with
Appointment_Status_Enum.Rejected,Appointment_Status_Enum.Cancelled,Appointment_Status_Enum.Completed, andAppointment_Status_Enum.Expiredstatus - Shows historical appointments that have been concluded or cancelled
- Includes completed sessions, cancelled appointments, and expired payment links
Data Processing Logic:
- Processes past appointments with completion status tracking
- Handles appointment status transformation (Scheduled β Completed for past appointments)
- Manages appointment history with proper chronological ordering
- Formats appointment data for historical display
Selection Filtering:
- Provides radio selection between "All" and "Completed" appointments
- Filters past appointments based on completion status
- Maintains selection state for user preference persistence
// Past.tsx - Past appointments with comprehensive filtering
const Past = () => {
const [selected, setSelected] = useState('all');
const [dateRangeData, setDateRangeData] = useState<TimeFilterRange>({
startTime: '',
endTime: '',
});
const [filterOption, setFilterOption] = useState<string>('');
const [timeRangeData, setTimeRangeData] = useState<TimeFilterRange>({
startTime: undefined,
endTime: undefined,
});
const [isFilterApplied, setIsFilterApplied] = useState(false);
const [filterTimeStamp, setFilterTimeStamp] = useState<TimeFilterRange>({
startTime: '',
endTime: '',
});
// Query configuration for past appointments with multiple status filtering
const { data, refetch, loading, error } = useGetAppointmentListQuery({
variables: {
status: [
Appointment_Status_Enum.Rejected,
Appointment_Status_Enum.Cancelled,
Appointment_Status_Enum.Completed,
Appointment_Status_Enum.Expired,
],
StartTime: filterTimeStamp?.startTime
? {
_gte: filterTimeStamp?.startTime,
}
: {},
EndTime: filterTimeStamp?.endTime
? { _lte: filterTimeStamp?.endTime }
: {},
order_by: Order_By.DescNullsLast,
},
});
// Appointment data processing with status transformation and chronological ordering
const appointments = useMemo(() => {
return (
data?.appointment?.map(entry => {
const { startTimeSlot, endTimeSlot } = getAppointmentSlot(
entry.appointment_slots,
);
const slot = entry?.appointment_slots?.find(s => s.in_use);
const startTime = slot?.timeSlot?.StartTime;
const endTime = slot?.timeSlot?.EndTime;
return {
name: entry?.appointment_therapists[0]?.therapist?.user?.name,
child: entry?.child?.name,
degree: entry.appointment_therapists[0]?.therapist?.degree_name,
specialty: entry?.appointment_therapists[0]?.therapist
?.therapist_specialities[0]?.speciality?.title,
date: startTime,
timeSlot: {
startTime: startTime,
endTime: endTime,
},
status: entry.status === Appointment_Status_Enum.Scheduled
? Appointment_Status_Enum.Completed
: entry.status,
img: entry.appointment_therapists[0]?.therapist?.user?.profile_picture
?.path,
id: entry.id,
paymentExpiry: entry?.payment_expiry,
rescheduleCount: entry?.reschedule_count,
rescheduleRequested: entry?.reschedule_requested,
appointmentType: entry?.appointment_type,
};
}) || []
);
}, [data?.appointment]);
// Selection data configuration for appointment filtering
const selectionData = [
{ name: t('all'), key: 'all' },
{ name: t('completed'), key: 'completed' },
];
// Appointment data filtering based on selection
const appointmentData = useMemo(() => {
return selected === 'completed'
? appointments.filter(x => x.status === Appointment_Status_Enum.Completed)
: appointments;
}, [appointments, selected]);
};
π± Appointment Details Screenβ
Appointment Details Overviewβ
Navigation Logic:
- Navigates to appointment details when appointment card is pressed
- Passes appointment ID as route parameter for data fetching
- Maintains navigation state for back button functionality
Data Fetching Logic:
- Fetches comprehensive appointment details using appointment ID
- Retrieves therapist information, child details, and appointment metadata
- Handles loading states and error scenarios with retry functionality
- Implements cache-and-network fetch policy for optimal performance
Status-Based Display Logic:
- Displays different UI elements based on appointment status
- Shows payment tags for scheduled appointments with expiry information
- Handles team meeting display with multiple therapist information
- Manages appointment action buttons based on current status
// AppointmentDetails.tsx - Comprehensive appointment details with status-based logic
const AppointmentDetails = ({ navigation, route }: ScreenProps) => {
const [isSessionTriggered, setSessionTriggered] = useState(false);
const [showMore, setShowMore] = useState(false);
const [view, setView] = useState<'CANCEL' | 'RESCHEDULE' | 'REBOOK'>();
const [successModalVisible, setSuccessModalVisible] = useState(false);
const [rescheduleModalVisible, setRescheduleModalVisible] = useState(false);
// Appointment details query with comprehensive data fetching
const {
data,
refetch: refetchAppointmentDetails,
loading: appointmentDetailsLoading,
error: appointmentDetailsError,
} = useGetAppointmentDetailsQuery({
variables: {
id: route.params?.appointmentId,
},
fetchPolicy: 'cache-and-network',
});
const appointment = data?.appointment?.[0];
// Therapist and child data processing with comprehensive information extraction
const { therapist, child } = useMemo(() => {
const therapistData = {
id: appointment?.appointment_therapists[0]?.therapist_id,
name: appointment?.appointment_therapists[0]?.therapist?.user?.name,
qualification: appointment?.appointment_therapists[0]?.therapist?.degree_name,
designation: 'Physician',
experience: appointment?.appointment_therapists[0]?.therapist?.experience,
consultationFees: appointment?.appointment_therapists[0]?.therapist?.consultation_fees,
image: appointment?.appointment_therapists[0]?.therapist?.user?.profile_img,
};
const childData = {
childName: appointment?.child?.name,
age: appointment?.child?.age,
lastTherapy: appointment?.child?.appointments?.[0]?.date,
image: appointment?.child?.profile_image?.path,
id: appointment?.child?.id,
assessmentTaken: appointment?.child?.assessment_responses_aggregate?.aggregate?.count,
};
return {
therapist: therapistData,
child: childData,
};
}, [appointment]);
// Appointment data processing with comprehensive status and payment logic
const appointmentData = useMemo(() => {
const isAfter = dayjs(new Date()).isAfter(endTimeSlot?.EndTime);
const activeSessionJustEnded = isSessionTriggered && !showJoin;
const isAfterSession = appointment?.status === Appointment_Status_Enum.Scheduled && isAfter;
const appointmentStatus = activeSessionJustEnded || isAfterSession
? Appointment_Status_Enum.Completed
: appointment?.status;
return {
date: startTime,
startTime: startTime,
endTime: endTime,
status: appointmentStatus,
payment: {
tax: appointment?.taxes,
serviceFee: appointment?.service_fee,
consultationFee: appointment?.consultation_fee,
otherCharges: appointment?.other_charges,
paymentExpiry: appointment?.payment_expiry,
},
meetLink: appointment?.meet_link,
};
}, [
endTimeSlot?.EndTime,
isSessionTriggered,
showJoin,
appointment?.status,
appointment?.date,
appointment?.taxes,
appointment?.service_fee,
appointment?.meet_link,
startTimeSlot?.StartTime,
appointment?.other_charges,
appointment?.consultation_fee,
]);
};
Payment Processing Logicβ
Payment Status Management:
- Handles payment expiry calculations with hour and minute precision
- Displays payment link expiry information for scheduled appointments
- Manages payment confirmation flow with Razorpay integration
- Tracks payment status and provides appropriate user feedback
Payment Flow Logic:
- Initiates payment process for scheduled appointments
- Handles Razorpay payment gateway integration
- Processes payment confirmation with signature validation
- Manages payment success and failure scenarios
// Make Payment functionality for scheduled appointments
const isScheduled = appointmentData?.status === Appointment_Status_Enum.Scheduled;
// Payment button display logic for scheduled appointments
{isScheduled && (
<Box gap="l" flexDirection="row" flex={1}>
<Box>
<Text variant="title2" color="secondary20">
{renderFee(paymentDetails?.payment?.amount)}
</Text>
<Text mt="xxxxs" variant="paragraph2" color="primary36">
{t('totalAmount')}
</Text>
</Box>
<CsmButton
handleClick={() => handlePayment()}
style={commonFlexStyles.FLEX2}
label={'Make Payment'}
isLoading={startPaymentLoading}
disabled={startPaymentLoading}
/>
</Box>
)}
// Payment processing logic with comprehensive status management
const handlePayment = async () => {
const response = await startPayment();
const orderId = response?.data?.startAppointmentPayment?.data?.order_id;
const parentInfo = parentDetails?.parent[0];
var options = {
description: 'Credits towards Com DEALL',
image: 'https://comdeall-prod-sb.blr1.cdn.digitaloceanspaces.com/COVER_PAGE_BG_TOP_RIGHT.svg',
currency: 'INR',
key: process.env.EXPO_PUBLIC_RAZOR_PAY_KEY!,
name: 'ComDEALL',
order_id: orderId,
prefill: {
email: parentInfo?.user?.email,
contact: parentInfo?.user?.phone,
name: parentInfo?.user?.name,
},
theme: { color: '#EE7B36' },
retry: { enabled: false },
timeout: 600,
modal: {
handleback: false,
escape: false,
confirm_close: true,
},
};
RazorpayCheckout.open(options)
.then((res: any) => {
const paymentConfirmationData = {
payment_signature: res.razorpay_signature,
provider_id: res.razorpay_payment_id,
razorpay_order_id: res.razorpay_order_id,
};
handleConfirmPayment(paymentConfirmationData, true);
})
.catch((err: any) => {
if (err?.details?.error?.metadata?.payment_id) {
const paymentConfirmationData = {
provider_id: err?.details?.error?.metadata?.payment_id,
razorpay_order_id: err?.details?.error?.metadata?.order_id,
};
handleConfirmPayment(paymentConfirmationData, false);
} else {
const paymentConfirmationData = {
provider_id: null,
razorpay_order_id: orderId,
};
handleConfirmPayment(paymentConfirmationData, false);
}
});
};
Make Payment for Requested Appointmentsβ
Payment Button Display Logic:
- Shows "Make Payment" button only for scheduled appointments (
Appointment_Status_Enum.Scheduled) - Displays total amount and payment details for pending appointments
- Handles payment button loading state during payment processing
- Provides payment button disable state during payment initiation
Payment Processing Flow:
- Initiates payment process by calling
startPaymentmutation - Retrieves Razorpay order ID from payment response
- Opens Razorpay checkout with pre-filled parent information
- Handles payment success and failure scenarios with appropriate feedback
Payment Confirmation Logic:
- Processes payment confirmation with signature validation
- Updates appointment status after successful payment
- Shows success modal with appointment confirmation details
- Handles payment failure scenarios with error feedback
// Make Payment button display for scheduled appointments
const isScheduled = appointmentData?.status === Appointment_Status_Enum.Scheduled;
// Payment button with total amount display
{isScheduled && (
<Box gap="l" flexDirection="row" flex={1}>
<Box>
<Text variant="title2" color="secondary20">
{renderFee(paymentDetails?.payment?.amount)}
</Text>
<Text mt="xxxxs" variant="paragraph2" color="primary36">
{t('totalAmount')}
</Text>
</Box>
<CsmButton
handleClick={() => handlePayment()}
style={commonFlexStyles.FLEX2}
label={'Make Payment'}
isLoading={startPaymentLoading}
disabled={startPaymentLoading}
/>
</Box>
)}
// Payment initiation logic with comprehensive error handling
const [startPayment, { loading: startPaymentLoading }] = useStartAppointmentPaymentMutation({
variables: {
id: route.params?.appointmentId,
},
fetchPolicy: 'no-cache',
});
const handlePayment = async () => {
const response = await startPayment();
const orderId = response?.data?.startAppointmentPayment?.data?.order_id;
const parentInfo = parentDetails?.parent[0];
// Razorpay payment options configuration
var options = {
description: 'Credits towards Com DEALL',
image: 'https://comdeall-prod-sb.blr1.cdn.digitaloceanspaces.com/COVER_PAGE_BG_TOP_RIGHT.svg',
currency: 'INR',
key: process.env.EXPO_PUBLIC_RAZOR_PAY_KEY!,
name: 'ComDEALL',
order_id: orderId,
prefill: {
email: parentInfo?.user?.email,
contact: parentInfo?.user?.phone,
name: parentInfo?.user?.name,
},
theme: { color: '#EE7B36' },
retry: { enabled: false },
timeout: 600,
modal: {
handleback: false,
escape: false,
confirm_close: true,
},
};
// Razorpay payment processing with success/failure handling
RazorpayCheckout.open(options)
.then((res: any) => {
const paymentConfirmationData = {
payment_signature: res.razorpay_signature,
provider_id: res.razorpay_payment_id,
razorpay_order_id: res.razorpay_order_id,
};
handleConfirmPayment(paymentConfirmationData, true);
})
.catch((err: any) => {
if (err?.details?.error?.metadata?.payment_id) {
const paymentConfirmationData = {
provider_id: err?.details?.error?.metadata?.payment_id,
razorpay_order_id: err?.details?.error?.metadata?.order_id,
};
handleConfirmPayment(paymentConfirmationData, false);
} else {
const paymentConfirmationData = {
provider_id: null,
razorpay_order_id: orderId,
};
handleConfirmPayment(paymentConfirmationData, false);
}
});
};
Appointment Actions Logicβ
Reschedule Request Logic:
- Handles reschedule request submission with appointment ID
- Manages reschedule count tracking and limit enforcement
- Provides user feedback for successful reschedule requests
- Tracks reschedule request status and display appropriate UI
Cancellation Logic:
- Manages appointment cancellation with reason tracking
- Handles cancellation charges calculation and display
- Processes refund information for cancelled appointments
- Provides cancellation confirmation and success feedback
// Appointment actions logic with comprehensive request management
const [requestReschedule, { loading: reScheduleLoading }] = useRescheduleRequestMutation({
onCompleted(data) {
if (data?.appointment_reschedule_request[0]?.reschedule_requested) {
toast.show({ text: t('rescheduleSuccess') });
refetchAppointmentDetails();
}
},
onError(error) {
toast.show({ text: error.message || tError('error') });
},
});
const handleReschedule = () => {
requestReschedule({
variables: { appointmentId: route.params?.appointmentId },
});
setRescheduleModalVisible(false);
};
const handleCancelButton = () => {
setView('CANCEL');
sheetRef.current?.present();
};
Billing Details Displayβ
Billing Information Processing:
- Displays comprehensive billing details including consultation fees, taxes, and service fees
- Handles other charges processing with JSON parsing and display formatting
- Shows total amount calculation with proper currency formatting
- Manages cancellation charges and refund amount display for cancelled appointments
Payment Status Display:
- Shows payment expiry information for scheduled appointments with time calculations
- Displays payment link expiry warnings with hour and minute precision
- Handles different payment statuses (Pending, Successful, Failed) with appropriate UI
- Manages payment confirmation flow with success/failure feedback
Make Payment Option for Requested Appointments:
- Displays "Make Payment" button for scheduled appointments that require payment
- Shows total amount and payment details for pending appointments
- Handles payment processing with Razorpay integration for scheduled appointments
- Provides payment confirmation and success feedback after successful payment
// Billing details processing with comprehensive charge calculation
const getOtherCharges = (charges: string | Array<other_charge>) => {
if (typeof charges === 'string') {
try {
const parseCharges = JSON.parse(charges);
return parseCharges;
} catch (error) {
toast.show({ text: t('error.error') });
return [];
}
}
return charges || [];
};
// Payment expiry calculation with comprehensive time formatting
let paymentTagText = '';
if (appointmentData?.status === Appointment_Status_Enum.Scheduled) {
let hourDiff = dayjs(appointmentData?.payment?.paymentExpiry).diff(
dayjs(),
'hour',
);
if (hourDiff < 1) {
hourDiff = dayjs(appointmentData?.payment?.paymentExpiry).diff(
dayjs(),
'minutes',
);
paymentTagText =
t('paymentLinkExpiresIn') +
` ${hourDiff} ` +
(hourDiff > 1 ? 'min' : 'mins');
} else {
paymentTagText =
t('paymentLinkExpiresIn') +
` ${hourDiff} ` +
(hourDiff > 1 ? 'hrs' : 'hr');
}
}
Team Meeting Display Logicβ
Team Meeting Information:
- Displays multiple therapist information for team meeting appointments
- Shows therapist details including name, degree, and profile picture
- Handles team meeting specific UI elements and information display
- Manages team meeting appointment type identification and display
Appointment Type Handling:
- Identifies team meeting appointments using
Appointment_Type_Enum.TeamMeeting - Displays team meeting specific information and therapist list
- Handles different appointment types with appropriate UI rendering
- Manages appointment type-based action button display
// Team meeting display logic with comprehensive therapist information
const isTeamMeeting = appointment?.appointment_type === Appointment_Type_Enum.TeamMeeting;
// Team meeting therapist information processing
{isTeamMeeting && (
<Box p="m" bg="white" mb="l" borderRadius="s">
<Text variant="title2" mb="m">
{t('teamMeeting')}
</Text>
<Box gap="l">
{appointment?.appointment_therapists?.map(
({ therapist, therapist_id }) => (
<TherapistInfoCard
imageUrl={therapist?.user?.profile_picture?.path}
name={therapist?.user?.name}
degree={therapist?.degree_name}
key={therapist_id}
/>
),
)}
</Box>
</Box>
)}
Reschedule History Displayβ
Reschedule Tracking Logic:
- Displays reschedule history with chronological order
- Shows reschedule count and request status
- Handles reschedule limit enforcement (maximum 3 reschedules)
- Manages reschedule request display and user feedback
Reschedule Status Management:
- Tracks reschedule count and request status
- Displays reschedule history with proper date formatting
- Handles reschedule limit warnings and restrictions
- Manages reschedule request confirmation and success feedback
// Reschedule history processing with comprehensive tracking
const getReschedules = () => {
const reschedules: JSX.Element[] = [];
appointment?.appointment_slots?.forEach((slot, index) => {
if (slot?.schedule_number > 0) {
reschedules.push(
<Box flexDirection="row" alignItems="center" gap="xxs" key={index}>
<CalenderIcon width={20} />
<Text
variant="body3"
color="secondary20"
opacity={slot?.in_use ? 1 : 0.3}>
{t('rescheduledTo')}
{dayjs(slot?.timeSlot?.StartTime).format('MMM DD, YYYY')}
</Text>
</Box>,
);
}
});
return reschedules;
};
π³ Payment Processingβ
Payment Integrationβ
Payment Processing Architecture: The appointment payment system supports multiple payment gateways to accommodate different platforms and regions. The system integrates Razorpay for web-based payments, iOS In-App Purchases for Apple devices, and Google Play Billing for Android devices. This multi-platform approach ensures seamless payment experiences across all supported devices.
Payment Gateway Selection Logic:
- Razorpay: Primary payment gateway for web and cross-platform payments, supporting UPI, cards, net banking, and wallets
- iOS In-App Purchase: Used for subscription and appointment payments on iOS devices to comply with Apple's guidelines
- Android In-App Billing: Handles payments on Android devices through Google Play Store integration
Payment State Management: The payment processing hook maintains loading states throughout the payment flow to provide visual feedback to users. Loading states are activated when payment is initiated and deactivated upon completion or failure, preventing duplicate payment attempts and improving user experience.
// Payment processing for appointments
const usePaymentProcessing = () => {
const [paymentLoading, setPaymentLoading] = useState(false);
const processRazorpayPayment = async (paymentData: PaymentData) => {
setPaymentLoading(true);
try {
const result = await RazorpayCheckout.open({
description: paymentData.description,
image: 'https://your-logo-url.com/logo.png',
currency: 'INR',
key: process.env.EXPO_PUBLIC_RAZORPAY_KEY,
amount: paymentData.amount * 100,
name: 'Com DEALL',
order_id: paymentData.razorpay_order_id,
prefill: {
email: paymentData.email,
contact: paymentData.phone,
name: paymentData.name,
},
theme: { color: '#F37254' }
});
return result;
} catch (error) {
console.error('Payment error:', error);
throw error;
} finally {
setPaymentLoading(false);
}
};
const processIOSPayment = async (productId: string) => {
setPaymentLoading(true);
try {
const result = await purchaseProduct(productId);
return result;
} catch (error) {
console.error('iOS payment error:', error);
throw error;
} finally {
setPaymentLoading(false);
}
};
const processAndroidPayment = async (productId: string) => {
setPaymentLoading(true);
try {
const result = await purchaseProduct(productId);
return result;
} catch (error) {
console.error('Android payment error:', error);
throw error;
} finally {
setPaymentLoading(false);
}
};
return {
processRazorpayPayment,
processIOSPayment,
processAndroidPayment,
paymentLoading
};
};
Payment Confirmationβ
// Payment confirmation handling
const usePaymentConfirmation = () => {
const [confirmPayment] = useConfirmAppointmentPaymentMutation({
onCompleted: (data) => {
if (data.confirmAppointmentPayment.success) {
toast.show({ text: 'Payment confirmed successfully' });
navigation.navigate('parent/appointments');
} else {
toast.show({ text: data.confirmAppointmentPayment.message || 'Payment confirmation failed' });
}
},
onError: (error) => {
toast.show({ text: error.message || 'Payment confirmation failed' });
}
});
const confirmRazorpayPayment = async (orderId: string, paymentId: string, signature: string) => {
await confirmPayment({
variables: {
order_id: orderId,
payment_id: paymentId,
payment_signature: signature
}
});
};
const confirmIOSPayment = async (transactionId: string, productId: string) => {
await confirmPayment({
variables: {
ios_transaction_id: transactionId,
ios_product_id: productId
}
});
};
const confirmAndroidPayment = async (purchaseToken: string, productId: string) => {
await confirmPayment({
variables: {
android_purchase_token: purchaseToken,
android_product_id: productId
}
});
};
// Payment confirmation logic with comprehensive error handling
const confirmRazorpayPayment = async (orderId: string, paymentId: string, signature: string) => {
await confirmPayment({
variables: {
order_id: orderId,
payment_id: paymentId,
payment_signature: signature
}
});
};
const confirmIOSPayment = async (transactionId: string, productId: string) => {
await confirmPayment({
variables: {
ios_transaction_id: transactionId,
ios_product_id: productId
}
});
};
const confirmAndroidPayment = async (purchaseToken: string, productId: string) => {
await confirmPayment({
variables: {
android_purchase_token: purchaseToken,
android_product_id: productId
}
});
};
};
π Appointment Analytics - Actual Implementation
Based on the provided code files, here's the real implementation of appointment statistics logic with explanations:
1. Pending Appointments with Filteringβ
From:PendingAppointment.tsx
const PendingAppointment = () => {
// State management for date/time filtering
const [filterTimeStamp, setFilterTimeStamp] = useState<TimeFilterRange>({
startTime: '',
endTime: '',
});
const [isFilterApplied, setIsFilterApplied] = useState(false);
// Lazy query for fetching appointment data
const [loadData, { data, refetch, loading, error }] =
useGetAppointmentListLazyQuery({
fetchPolicy: 'cache-and-network',
variables: {
status: [Appointment_Status_Enum.Scheduled], // Filter by scheduled status
StartTime: {
_gte: filterTimeStamp?.startTime || dayjs().startOf('day').toISOString(),
},
EndTime: filterTimeStamp?.endTime
? { _lte: filterTimeStamp?.endTime }
: {},
},
});
// Process and transform appointment data
const newAppointments: TAppointmentCardItem[] = useMemo(() => {
return (
data?.appointment?.map(appointment => {
const { startTimeSlot, endTimeSlot } = getAppointmentSlot(
appointment?.appointment_slots,
);
const slot = appointment.appointment_slots?.find(s => s.in_use);
const startTime = slot?.timeSlot?.StartTime;
const endTime = slot?.timeSlot?.EndTime;
return {
id: appointment.id,
profile_image_url: appointment.child?.profile_image?.path || '',
child_name: appointment.child?.name || '',
age: appointment.child?.age,
parent1: appointment.child?.parent.parent1_name || '',
appointment_date: startTime,
appointment_start_time: startTime,
appointment_end_time: endTime,
status: appointment.status || '',
appointmentType: appointment?.appointment_type || '',
paymentExpiry: appointment?.payment_expiry,
};
}) || []
);
}, [data]);
// Calculate if appointments exist
const hasAppointmentData = useMemo(() => {
return (newAppointments?.length ?? 0) > 0;
}, [newAppointments]);
// Determine if filter UI should be shown
const shouldShowFilter = useMemo(() => {
if (isFilterApplied) {
return true; // Always show if filter is applied
}
return hasAppointmentData; // Show if there's data to filter
}, [isFilterApplied, hasAppointmentData]);
};
π Explanationβ
1. Filter State Managementβ
filterTimeStamp: Stores timestamp values used in the query.isFilterApplied: Tracks if filters are applied.- Controls both query variables and UI visibility.
2. Lazy Query Patternβ
fetchPolicy: 'cache-and-network'
- First returns cached data (fast initial render)
- Then fetches from network (gets fresh data)
- Best of both worlds: speed + freshness
3. Dynamic Query Variablesβ
StartTime: {
_gte: filterTimeStamp?.startTime || dayjs().startOf('day').toISOString(),
}
-
Uses the filter timestamp if available.
-
Falls back to start of the current day otherwise.
-
_gte means "greater than or equal to" (GraphQL comparison operator)
4. Slot Processingβ
const slot = appointment.appointment_slots?.find(s => s.in_use);
- Finds the active slot marked with in_use: true.
- in_use: true marks the currently active slot
- Ensures the correct appointment time is displayed.
5. Data Transformation**:β
-
Converts raw backend data β UI-friendly format.
-
Uses useMemo for performance optimization (avoids re-computation).
6. Conditional UI Logic**:β
const shouldShowFilter = useMemo(() => {
if (isFilterApplied) return true;
return hasAppointmentData;
}, [isFilterApplied, hasAppointmentData]);
Shows the filter only when relevant or applied.
2 π New Appointments with Status Filteringβ
File: NewAppointment.tsx
const NewAppointment = () => {
const [dateRange, setDateRange] = useState<TimeFilterRange>({
startTime: '',
endTime: '',
});
// Query with multiple status filtering
const [loadData, { data, refetch, loading, error }] =
useGetAppointmentListLazyQuery({
fetchPolicy: 'cache-and-network',
variables: {
status: [
Appointment_Status_Enum.Pending,
Appointment_Status_Enum.Rejected,
Appointment_Status_Enum.Cancelled,
],
date: dateRange?.startTime
? {
_gte: format(new Date(dateRange.startTime), 'YYYY-MM-DD'),
_lte: format(new Date(dateRange.endTime!), 'YYYY-MM-DD'),
}
: {
_gte: format(new Date(), 'YYYY-MM-DD'),
},
},
});
// Process appointments with cancellation filtering
const newAppointments = useMemo(() => {
return data?.appointment
?.filter(tempAppointment => tempAppointment?.is_cancelled === false)
?.map(appointment => {
const { startTimeSlot, endTimeSlot } = getAppointmentSlot(
appointment?.appointment_slots,
);
return {
id: appointment.id,
profile_image_url: appointment.child?.profile_image?.path,
child_name: appointment.child?.name,
age: appointment.child?.age,
parent1: appointment.child?.parent.parent1_name,
appointment_date: appointment.date,
appointment_start_time: startTimeSlot?.StartTime,
appointment_end_time: endTimeSlot?.EndTime,
status: appointment.approval_status,
};
});
}, [data]);
};
π§ Explanation
1. Multiple Status Filteringβ
status: [
Appointment_Status_Enum.Pending,
Appointment_Status_Enum.Rejected,
Appointment_Status_Enum.Cancelled,
]
- Shows appointments in multiple states
- Therapists can see all "new" appointments requiring attention
- GraphQL _in operator checks if status matches any in the array
2. Conditional Date Filtering:β
3.Additional Client-Side Filtering:β
?.filter(tempAppointment => tempAppointment?.is_cancelled === false)
- Server returns appointments with various statuses
- Client removes those with is_cancelled = true
- Why? Status might be "Pending" but is_cancelled flag provides additional context
4. Helper Function Usage:β
const { startTimeSlot, endTimeSlot } = getAppointmentSlot(
appointment?.appointment_slots,
);
- Abstracts complex slot extraction logic
- Returns the active time slot from multiple slots
- Reusable across different components
Upcoming Appointments with Real-time Sortingβ
From: UpcomingAppointments.tsx
const UpcomingAppointments = ({ appointments }) => {
const [upcommingAppointments, setUpcommingAppointments] = useState([]);
const [isSessionTriggered, setSessionTriggered] = useState(false);
const appointmentData = upcommingAppointments?.[0]; // First = next appointment
const { startTimeSlot, endTimeSlot } = getAppointmentSlot(
appointmentData?.appointment_slots,
);
// Check if session can be joined (within time window)
const { showJoin } = useGetActiveSessionTimer({
timeSlot: {
startTime: startTimeSlot?.StartTime,
endTime: endTimeSlot?.EndTime,
},
showTimer: false,
execute: hasAppointments,
});
// Sort and filter appointments when screen focused
useFocusEffect(
useCallback(() => {
const filteredData = sortBy(
appointments?.appointment,
d => d.appointment_slots?.[0]?.timeSlot?.StartTime, // Sort by start time
)?.filter(f => {
const { endTimeSlot: curEndTimeSlot } = getAppointmentSlot(
f.appointment_slots,
);
return dayjs(new Date()).isBefore(curEndTimeSlot?.EndTime); // Only future appointments
});
setUpcommingAppointments(filteredData);
}, [appointments]),
);
// Track when session becomes active
useFocusEffect(
useCallback(() => {
if (showJoin) {
setSessionTriggered(true);
}
}, [showJoin]),
);
};
Explanation:β
1. Chronological Sorting:β
sortBy(appointments, d => d.appointment_slots?.[0]?.timeSlot?.StartTime)
- lodash sortBy orders appointments by start time
- Earliest appointment appears first
- Ensures "next appointment" is always shown at top
2. Future-Only Filtering:β
return dayjs(new Date()).isBefore(curEndTimeSlot?.EndTime);
- dayjs().isBefore(endTime) checks if appointment hasn't ended yet
- Removes past appointments from the list
- Shows only relevant upcoming sessions
3. useFocusEffect Hook:β
useFocusEffect(
useCallback(() => {
// Logic runs when screen gains focus
}, [appointments])
);
- Runs when user navigates to this screen
- Re-sorts/filters data with latest appointments
- Updates when appointments prop changes
4. Active Session Detection:β
const { showJoin } = useGetActiveSessionTimer({
timeSlot: { startTime, endTime },
execute: hasAppointments,
});
- Custom hook checks if current time is within session window
- Returns showJoin: true when session can be joined
- Typically allows joining 10-15 mins before start time
5. Session State Persistence:β
if (showJoin) {
setSessionTriggered(true); // Once triggered, stays true
}
- Prevents "Join" button from disappearing if user navigates away
- Marks session as "active" even if timer expires
- Helps show "Completed" badge after session ends
π Real-time Updatesβ
Appointment Notificationsβ
// Real-time appointment updates
const useAppointmentSubscription = (parentId: string) => {
const { data: subscriptionData } = useAppointmentUpdatesSubscription({
variables: { parent_id: parentId }
});
useEffect(() => {
if (subscriptionData?.appointment_updated) {
const update = subscriptionData.appointment_updated;
// Update local cache
updateAppointmentCache(update);
// Show notification
toast.show({
text: `Appointment ${update.status.toLowerCase()}`,
type: 'info'
});
}
}, [subscriptionData]);
};
πΉ Explanation: Real-time Appointment Updatesβ
-
Subscription Hook
useAppointmentUpdatesSubscriptionlistens for any appointment changes for a specificparentId. -
Effect on Data Change
useEffecttriggers wheneversubscriptionDataupdates. -
Cache Update
updateAppointmentCache(update)ensures the local state reflects the latest appointment status. -
User Notification
toast.showdisplays a real-time notification to inform the user of the updated appointment status.
π± Mobile-Specific Featuresβ
Touch Interactionsβ
// Appointment card touch interactions
const useAppointmentGestures = () => {
const handleSwipeLeft = (appointment: Appointment) => {
// Swipe left to cancel
showCancelConfirmation(appointment);
};
const handleSwipeRight = (appointment: Appointment) => {
// Swipe right to reschedule
showRescheduleOptions(appointment);
};
const handleLongPress = (appointment: Appointment) => {
// Long press to show options
showAppointmentOptions(appointment);
};
return {
handleSwipeLeft,
handleSwipeRight,
handleLongPress
};
};
π± Explanation: Mobile-Specific Touch Interactionsβ
-
Gesture Hook
useAppointmentGesturesmanages touch-based interactions for appointment cards. -
Swipe Left β Cancel
TriggersshowCancelConfirmation(appointment)to confirm appointment cancellation. -
Swipe Right β Reschedule
OpensshowRescheduleOptions(appointment)for choosing a new time slot. -
Long Press β Options Menu
InvokesshowAppointmentOptions(appointment)to display additional actions.
Offline Appointment Managementβ
// Offline appointment capabilities
const useOfflineAppointments = () => {
const [offlineAppointments, setOfflineAppointments] = useState([]);
const saveOfflineAppointment = (appointment: Appointment) => {
const offlineAppointment = {
...appointment,
id: `offline_${Date.now()}`,
isOffline: true,
createdAt: new Date().toISOString()
};
setOfflineAppointments(prev => [...prev, offlineAppointment]);
mmkvStorage.set('offline_appointments', JSON.stringify([...offlineAppointments, offlineAppointment]));
};
const syncOfflineAppointments = async () => {
if (offlineAppointments.length === 0) return;
try {
for (const appointment of offlineAppointments) {
await createAppointment(appointment);
}
setOfflineAppointments([]);
mmkvStorage.delete('offline_appointments');
toast.show({ text: 'Offline appointments synced successfully' });
} catch (error) {
toast.show({ text: 'Failed to sync offline appointments' });
}
};
// Offline appointment management with comprehensive sync logic
const saveOfflineAppointment = (appointment: Appointment) => {
const offlineAppointment = {
...appointment,
id: `offline_${Date.now()}`,
isOffline: true,
createdAt: new Date().toISOString()
};
setOfflineAppointments(prev => [...prev, offlineAppointment]);
mmkvStorage.set('offline_appointments', JSON.stringify([...offlineAppointments, offlineAppointment]));
};
const syncOfflineAppointments = async () => {
if (offlineAppointments.length === 0) return;
try {
for (const appointment of offlineAppointments) {
await createAppointment(appointment);
}
setOfflineAppointments([]);
mmkvStorage.delete('offline_appointments');
toast.show({ text: 'Offline appointments synced successfully' });
} catch (error) {
toast.show({ text: 'Failed to sync offline appointments' });
}
};
};
βοΈ Explanation: Offline Appointment Managementβ
-
Offline State Handling
offlineAppointmentsstores appointments created while offline using React state. -
Save Offline Appointment
saveOfflineAppointment()- Generates a temporary ID (
offline_<timestamp>) - Marks the appointment as offline (
isOffline: true) - Persists data locally using
mmkvStoragefor reliability.
- Generates a temporary ID (
-
Sync Logic
syncOfflineAppointments()- Checks if any offline appointments exist.
- Attempts to sync each appointment to the server using
createAppointment(). - On success, clears both local state and stored data.
-
User Feedback
- Shows a success toast on successful sync.
- Displays an error toast if syncing fails.
-
Purpose
Enables seamless appointment creation and recovery even without an internet connection.
π¨ UI Componentsβ
Appointment Cardβ
// AppointmentCard.tsx
const AppointmentCard = ({
appointment,
onReschedule,
onCancel,
onJoin
}) => {
const { colors } = useTheme<Theme>();
return (
<TouchableOpacity
style={[
styles.appointmentCard,
{ borderLeftColor: getStatusColor(appointment.status) }
]}
onPress={() => onJoin(appointment)}
>
<Box flexDirection="row" alignItems="center">
<FastImage
source={{ uri: appointment.img }}
style={styles.therapistImage}
/>
<Box flex={1} marginLeft="m">
<Text variant="heading5">{appointment.name}</Text>
<Text variant="body2" color="secondary63">
Child: {appointment.child}
</Text>
<Text variant="body2" color="secondary63">
{appointment.degree} β’ {appointment.specialty}
</Text>
</Box>
<Box alignItems="flex-end">
<Text variant="body2" color="primary36">
{format(appointment.date, 'MMM dd')}
</Text>
<Text variant="caption">
{appointment.timeSlot}
</Text>
</Box>
</Box>
<Box flexDirection="row" justifyContent="space-between" marginTop="m">
<StatusBadge status={appointment.status} />
<Box flexDirection="row">
<TouchableOpacity
onPress={() => onReschedule(appointment.id)}
style={[styles.actionButton, styles.rescheduleButton]}
>
<Text color="primary36">Reschedule</Text>
</TouchableOpacity>
<TouchableOpacity
onPress={() => onCancel(appointment.id)}
style={[styles.actionButton, styles.cancelButton]}
>
<Text color="error">Cancel</Text>
</TouchableOpacity>
</Box>
</Box>
</TouchableOpacity>
);
};
π¨ Explanation: Appointment Card UI Componentβ
-
Purpose
Displays a single appointment with therapist info, status, and quick actions (join, reschedule, cancel). -
Main Layout
Wrapped in aTouchableOpacityto make the entire card tappable β triggersonJoin(appointment)when pressed. -
Therapist Info Section
FastImageshows the therapistβs profile picture.- Displays therapist name, childβs name, degree, and specialty using styled
Textcomponents.
-
Date & Time Display
- Shows appointment date (
MMM dd) and time slot aligned to the right.
- Shows appointment date (
-
Status and Actions
StatusBadgeindicates current appointment status (e.g., Pending, Confirmed, Cancelled).- Includes Reschedule and Cancel buttons with respective handlers:
onReschedule(appointment.id)onCancel(appointment.id)
-
Styling
- Dynamic left border color based on appointment status (
getStatusColor). - Responsive layout using
Boxfor flexible alignment and spacing.
- Dynamic left border color based on appointment status (
β Provides an interactive, visually clear appointment summary optimized for mobile use.
π§ GraphQL Integrationβ
Appointment Queriesβ
# Get upcoming appointments
query GetUpcomingAppointments($parent_id: String!, $date: date_comparison_exp) {
appointment(
where: {
child: { parent_id: { _eq: $parent_id } }
date: $date
status: { _in: ["SCHEDULED", "CONFIRMED"] }
}
order_by: { date: asc }
) {
id
date
status
appointment_type
child { name }
therapist {
user { name profile_picture { path } }
degree_name
therapist_specialities {
speciality { title }
}
}
appointment_slots {
timeSlot {
StartTime
EndTime
}
}
}
}
# Create appointment
mutation CreateAppointment($child_id: uuid!, $therapist_id: uuid!, $date: date!, $time_slot_id: uuid!, $appointment_type: String!) {
createAppointment(
child_id: $child_id
therapist_id: $therapist_id
date: $date
time_slot_id: $time_slot_id
appointment_type: $appointment_type
) {
success
message
data {
order_id
appointment_id
}
}
}
π§ Explanation: GraphQL Appointment Integrationβ
-
π Get Upcoming Appointments (
GetUpcomingAppointments)- Purpose: Fetches all upcoming appointments for a specific parent.
- Filters:
parent_id: Matches appointments linked to the parentβs children.date: Can be dynamically filtered usingdate_comparison_exp(e.g., future dates).status: Includes only"SCHEDULED"and"CONFIRMED"appointments.
- Sorting: Orders results by
date(ascending). - Fetched Fields:
- Appointment Info:
id,date,status,appointment_type. - Child Info: Childβs name.
- Therapist Info: Name, profile picture, degree, and specialties.
- Time Slot Info: Start and end times from
appointment_slots.
- Appointment Info:
-
β Create Appointment (
CreateAppointment)- Purpose: Creates a new appointment entry.
- Parameters:
child_id: ID of the child for whom the appointment is booked.therapist_id: ID of the assigned therapist.date: Appointment date.time_slot_id: Chosen time slot.appointment_type: Type of session (e.g., "Online" or "In-Person").
- Response:
success: Boolean indicating success status.message: Operation result message.data: Containsorder_idandappointment_idfor reference.
β Together, these queries and mutations form the backbone of the appβs appointment scheduling and management workflow.
Payment Mutationsβ
# Confirm appointment payment
mutation ConfirmAppointmentPayment($order_id: String!, $payment_id: String!, $payment_signature: String!) {
confirmAppointmentPayment(
order_id: $order_id
payment_id: $payment_id
payment_signature: $payment_signature
) {
success
message
}
}
# Cancel appointment
mutation CancelAppointment($appointment_id: uuid!, $reason: String!) {
cancelAppointment(
appointment_id: $appointment_id
reason: $reason
) {
success
message
}
}
π³ Explanation: Payment & Cancellation Mutationsβ
-
β Confirm Appointment Payment (
ConfirmAppointmentPayment)- Purpose: Confirms that a payment for an appointment has been successfully processed.
- Parameters:
order_id: Unique ID for the payment order.payment_id: Payment provider transaction ID.payment_signature: Signature for verifying payment authenticity.
- Response:
success: Boolean indicating whether payment confirmation succeeded.message: Provides additional status or error information.
-
β Cancel Appointment (
CancelAppointment)- Purpose: Cancels an existing appointment.
- Parameters:
appointment_id: ID of the appointment to cancel.reason: Reason for cancellation.
- Response:
success: Boolean indicating if cancellation was successful.message: Provides confirmation or error details.
β Use Case: These mutations handle both payment verification and appointment lifecycle management, ensuring data consistency and user transparency.
π― Best Practicesβ
Appointment Managementβ
- Payment Security: Secure payment processing with proper validation
- Status Tracking: Clear appointment status management
- Real-time Updates: Live appointment status synchronization
- Offline Support: Allow offline appointment management
User Experienceβ
- Visual Feedback: Clear status indicators and payment confirmations
- Touch Interactions: Intuitive swipe and tap gestures
- Payment Flow: Smooth payment processing experience
- Notifications: Timely appointment notifications
Performanceβ
- Payment Optimization: Fast payment processing
- Data Caching: Cache appointment data locally
- Lazy Loading: Load appointments on demand
- Background Sync: Sync data in background