Note: Access to the dashboard or seller app requires credentials. Screenshots are provided upon request.
import React, { useState } from 'react';
import { useRouter } from 'next/router';
import type { NextPage } from 'next';
import { Grid, SimpleGrid, Card } from '@mantine/core';
import { EmptyComponent } from '@/components/commons/empty-component';
import BillingPaymentCTA, {
checkOverdue,
} from '@/components/features/billing/billing-payment-cta';
import BillingTable from '@/components/features/billing/billing-table';
import AppLayout from '@/components/layout/app-layout';
import Pagination from '@/components/pagination';
import useGetBillingOverdue from '@/hooks/api/billing-seller/use-get-billing-overdue';
import useGetBillings from '@/hooks/api/billing-seller/use-get-billings';
import { arrayIsEmpty } from '@/utils';
const caption = {
pageTitle: 'Detail Tagihan',
noBillingData: 'Anda belum memiliki riwayat pembayaran',
};
const BillingPage: NextPage = () => {
const [params, setParams] = useState({ page: 1, pageSize: 25 });
const router = useRouter();
const { data: billings, isLoading: isLoadingBillings } = useGetBillings({
params,
});
const isReloadOverdue = !!(isLoadingBillings && params.page === 1);
const { data: billingOverdue } = useGetBillingOverdue({
page: params.page,
isLoading: isReloadOverdue,
});
const setPage = (newPage: number) => setParams({ ...params, page: newPage });
const hasOverdue = checkOverdue(
billingOverdue.accumulatedFee,
isReloadOverdue,
billings?.items,
);
return (
<AppLayout title={caption.pageTitle} pageTitle={caption.pageTitle}>
<SimpleGrid cols={1} breakpoints={[{ maxWidth: 'xl', cols: 1 }]}>
<Grid gutter={20}>
<Grid.Col span={12}>
<BillingPaymentCTA
isLoading={isReloadOverdue}
amount={billingOverdue?.accumulatedFee}
amountOverdueFormatCurrency={
billingOverdue?.accumulatedFeeFormatCurrency
}
dateTime={billingOverdue?.dueDate}
onClick={() =>
router.push(`/billing/payment/${billingOverdue?.id}`)
}
hasOverdue={hasOverdue}
/>
</Grid.Col>
<Grid.Col span={12}>
{!isLoadingBillings && arrayIsEmpty(billings?.items) ? (
<EmptyComponent label={caption.noBillingData} mt="lg" pt="lg" />
) : (
<Card withBorder radius="md" p="xl">
<BillingTable
data={billings?.items || []}
isLoading={isLoadingBillings}
pagination={
<Pagination
page={params.page}
total={billings?.totalPage ?? 0}
onChange={setPage}
/>
}
/>
</Card>
)}
</Grid.Col>
</Grid>
</SimpleGrid>
</AppLayout>
);
};
export default BillingPage;
import React from 'react';
import { ScrollView, StyleSheet, ImageBackground } from 'react-native';
import { useNavigation, useRoute } from '@react-navigation/native';
import { useAuthenticatedNavigation } from '@root/src/navigation/AuthenticatedNavigator';
import { colors } from '@root/src/themes/colors';
import images from '@root/src/themes/images';
import { StackRouteParams, maskPhoneNumber } from '@root/src/utils';
import { AuthenticatedStackParams } from '@root/src/navigation/AuthenticatedNavigator';
import { amp, eventName } from '@root/src/libraries/monitoring/amplitude';
import { queryClient } from '@root/App';
import Header from './elements/Headers';
import Footer from './elements/Footers';
import Summary from './elements/Summary';
import { View } from '@root/src/components';
import ItemProducts from './elements/ItemProducts';
import PaymentMethod from './elements/PaymentMethod';
import DestinationDetailModal from './elements/DestinationDetailModal';
import DeliveryInformation from './elements/DeliveryInformation';
import AcceptOrderModal from './elements/AcceptOrderModal';
import { DeliveryMethodName, OrderStatus } from './detailOrder.interface';
import ExpiredOrderModal from './elements/ExpiredOrderModal';
import OutdatedResponseModal from './elements/OutdatedResponseModal';
import {
OrderDetailModal,
useAcceptOrder,
useDetailOrderModal,
useOrderDetail,
} from './detailOrder.hook';
type RouteDetailProductType = StackRouteParams<AuthenticatedStackParams, 'DetailOrder'>;
const DetailOrders = () => {
const navigate = useNavigation();
const { params } = useRoute<RouteDetailProductType>();
const { data } = useOrderDetail(params?.referenceId);
const orderDetailModal = useDetailOrderModal();
const { navigate: navigateAuth } = useAuthenticatedNavigation();
const {
mutate: acceptOrderMutate,
isLoading: isLoadingAcceptOrder,
error: errorAcceptOrder,
reset: resetAcceptOrder,
} = useAcceptOrder(params?.referenceId);
React.useEffect(() => {
amp.track(eventName.order.viewDetail, {data});
}, [data]);
const onExpiredModalClose = () => {
orderDetailModal.close();
queryClient.invalidateQueries(['getOrderDetail', params.referenceId]);
};
const onOutdatedResponseModalClose = () => {
orderDetailModal.close();
resetAcceptOrder();
queryClient.invalidateQueries(['getOrderDetail', params.referenceId]);
};
const checkExpired = (expiredDate: string) => {
const expired = new Date(expiredDate).getTime();
const now = new Date().getTime();
const isExpired = now > expired;
if (isExpired) {
orderDetailModal.set(OrderDetailModal.ExpiredOrderModal);
}
return isExpired;
};
const onAccept = () => {
const isExpired = checkExpired(data?.expiredAt ?? '');
if (isExpired) {
return;
}
orderDetailModal.set(OrderDetailModal.AcceptOrderModal);
};
const onReject = () => {
const isExpired = checkExpired(data?.expiredAt ?? '');
if (isExpired) {
return;
}
navigateAuth('RejectOrder', {
referenceId: data?.referenceId as string,
});
};
return (
<>
<Header goBack={navigate.goBack} />
<ScrollView style={styles.container}>
<View size="plain" style={styles.bgHeader}>
<ImageBackground
source={images.illustrationOrderDetailBackground}
style={styles.imageCover}
/>
<Summary
date={data?.createdAt}
expireDate={data?.expiredAt}
status={data?.orderStatus ?? OrderStatus.FAILED}
id={data?.referenceId ?? ''}
name={data?.username || '-'}
phone={maskPhoneNumber(data?.phoneNumber ?? '-')}
isPickup={data?.delivery?.name === DeliveryMethodName.PICKUP}
/>
</View>
<ItemProducts orderItems={data?.orderItems ?? []} />
<PaymentMethod type={data?.paymentMethod ?? ''} />
<DeliveryInformation
destinationAddress={data?.delivery.destinationAddress ?? '-'}
destinationName={data?.delivery.destinationName ?? ''}
destinationNote={data?.delivery.destinationNote ?? ''}
resi={data?.delivery.resi ?? ''}
deliveryName={data?.delivery.name ?? ''}
deliveryService={data?.delivery.service ?? ''}
onOpenDetail={() =>
orderDetailModal.set(OrderDetailModal.DetailAddressModal)
}
originName={data?.delivery.originName ?? ''}
courierName={data?.delivery.courierName ?? ''}
courierPhoneNumber={data?.delivery.courierPhoneNumber ?? ''}
/>
{/* Modal */}
<DestinationDetailModal
destinationAddress={data?.delivery.destinationAddress ?? '-'}
destinationName={data?.delivery.destinationName ?? ''}
destinationNote={data?.delivery.destinationNote ?? ''}
destinationLatLong={data?.delivery.destinationLatLong}
open={orderDetailModal.state === OrderDetailModal.DetailAddressModal}
onClose={orderDetailModal.close}
/>
<AcceptOrderModal
onAccepted={acceptOrderMutate}
open={
orderDetailModal.state === OrderDetailModal.AcceptOrderModal &&
data?.orderStatus === 'PAYMENT_CONFIRMED'
}
isLoading={isLoadingAcceptOrder}
onClose={orderDetailModal.close}
/>
<ExpiredOrderModal
open={orderDetailModal.state === OrderDetailModal.ExpiredOrderModal}
onClose={onExpiredModalClose}
/>
<OutdatedResponseModal
onClose={onOutdatedResponseModalClose}
open={
orderDetailModal.state === OrderDetailModal.OutdatedResponseModal
}
requestOpen={() =>
orderDetailModal.set(OrderDetailModal.OutdatedResponseModal)
}
message={errorAcceptOrder?.message}
/>
</ScrollView>
<Footer
id={data?.referenceId}
status={data?.orderStatus}
deliveryName={data?.delivery.name}
onAccept={onAccept}
onReject={onReject}
/>
</>
);
};
const styles = StyleSheet.create({
container: {
backgroundColor: colors.bgBleachedSilk,
},
bgHeader: {
backgroundColor: colors.white,
maxHeight: 215,
},
imageCover: {
resizeMode: 'cover',
justifyContent: 'flex-start',
height: 90,
paddingLeft: 10,
borderBottomRightRadius: 20,
borderBottomLeftRadius: 20,
overflow: 'hidden',
},
});
export default DetailOrders;
Note: Access to the Skillacademy learning dashboard requires credentials. Screenshots are provided upon request.