Skip to content

Commit b7ac775

Browse files
committed
feat(payments): add check payment service for update payment status
1 parent 8082304 commit b7ac775

File tree

7 files changed

+96
-10
lines changed

7 files changed

+96
-10
lines changed

src/app/[locale]/layout.tsx

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -60,17 +60,17 @@ export default async function LocaleRootLayout(props: Readonly<Props>) {
6060
<html lang={locale} suppressHydrationWarning>
6161
<body className={`${sora.className}`}>
6262
<NextIntlClientProvider messages={messages}>
63-
<DialogProvider>
64-
<TanstackProvider>
63+
<TanstackProvider>
64+
<DialogProvider>
6565
<AuthProvider payload={payload}>
6666
<ThemeProvider attribute="class" defaultTheme="system" enableSystem disableTransitionOnChange>
6767
{children}
6868
</ThemeProvider>
6969
</AuthProvider>
70-
</TanstackProvider>
71-
<Toaster />
72-
<DialogGlobal />
73-
</DialogProvider>
70+
<Toaster />
71+
<DialogGlobal />
72+
</DialogProvider>
73+
</TanstackProvider>
7474
</NextIntlClientProvider>
7575
</body>
7676
</html>

src/domains/Events.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,3 +132,10 @@ export type AdminEventResponseType = Omit<EventFormType, "image"> & {
132132
author: string;
133133
file_name: string;
134134
};
135+
136+
export type CheckPaymentResponse = {
137+
transaction_no: string;
138+
status: string;
139+
paid_at: Date;
140+
payment_method: string;
141+
};
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import { Button } from "@/components/ui/Button";
2+
import { useDialog } from "@/contexts";
3+
import { Check, CreditCard, HelpCircle } from "lucide-react";
4+
import { ReactNode } from "react";
5+
6+
type Props = {
7+
status?: string;
8+
transaction_no?: string;
9+
};
10+
11+
const statusIconMap: Record<string, ReactNode> = {
12+
paid: <Check className="h-20 w-20 text-green-500" />,
13+
pending: <CreditCard className="h-20 w-20 text-yellow-500" />,
14+
};
15+
16+
const EventCheckStatusModal = ({ status = "", transaction_no = "" }: Props) => {
17+
const { closeDialog } = useDialog();
18+
19+
const icon = statusIconMap[status] ?? <HelpCircle className="h-20 w-20 text-gray-400" />;
20+
21+
return (
22+
<div className="flex flex-col items-center justify-center gap-6 py-8 text-center">
23+
{icon}
24+
25+
<div className="space-y-1">
26+
<p className="text-xl font-semibold capitalize">{status || "Unknown"}</p>
27+
{transaction_no && (
28+
<p className="text-muted-foreground text-sm">
29+
Transaction No: <span className="font-medium">{transaction_no}</span>
30+
</p>
31+
)}
32+
</div>
33+
34+
<Button variant="outline" onClick={closeDialog}>
35+
Kembali
36+
</Button>
37+
</div>
38+
);
39+
};
40+
41+
export default EventCheckStatusModal;

src/features/events/components/EventDetailModal.tsx

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,16 @@ import {
1313
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/Tabs";
1414
import { Check, X, UserCheck, CreditCard, CheckCircle2, Calendar, User } from "lucide-react";
1515
import { cn } from "@/lib/utils";
16+
import { Button } from "@/components/ui/Button";
17+
import { useRegistEvent } from "../hooks/useRegistEvent";
1618

1719
interface EventDetailModalProps {
1820
event: UserEventResponse;
1921
}
2022

2123
export const EventDetailModal = ({ event }: EventDetailModalProps) => {
2224
const { event_detail, user_detail } = event;
25+
const { checkPaymentStatus } = useRegistEvent();
2326

2427
const getActiveStep = () => {
2528
if (event.status === "SUCCESS") return 3;
@@ -155,7 +158,7 @@ export const EventDetailModal = ({ event }: EventDetailModalProps) => {
155158
</Stepper>
156159

157160
{event.status === "PENDING" && event.payment_url && (
158-
<div className="mt-4 flex items-center justify-center rounded-md border border-blue-200 bg-blue-50 p-3 dark:border-blue-800 dark:bg-blue-900/20">
161+
<div className="mt-4 flex flex-col items-center justify-center gap-4 rounded-md border border-blue-200 bg-blue-50 p-3 dark:border-blue-800 dark:bg-blue-900/20">
159162
<a
160163
href={event.payment_url}
161164
target="_blank"
@@ -164,6 +167,14 @@ export const EventDetailModal = ({ event }: EventDetailModalProps) => {
164167
>
165168
Click here to complete your payment →
166169
</a>
170+
<Button
171+
className="cursor-pointer"
172+
onClick={() => {
173+
checkPaymentStatus({ transaction_no: event.transaction_no });
174+
}}
175+
>
176+
Check Payment
177+
</Button>
167178
</div>
168179
)}
169180
</div>

src/features/events/hooks/useRegistEvent.ts renamed to src/features/events/hooks/useRegistEvent.tsx

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,13 @@ import { toast } from "sonner";
44
import { uploadsService } from "@/services/uploads";
55
import { eventsService } from "@/services/events";
66
import { EventType, RegistrationForm } from "@/domains/Events";
7+
import { useMutation } from "@tanstack/react-query";
8+
import { useDialog } from "@/contexts";
9+
import EventCheckStatusModal from "../components/EventCheckStatusModal";
710

8-
export const useRegistEvent = (data: EventType) => {
11+
export const useRegistEvent = (data?: EventType) => {
912
const t = useTranslations("EventsPage");
13+
const { openDialog } = useDialog();
1014
const [isLoading, setIsLoading] = useState<boolean>(false);
1115
const [nameImage, setNameImage] = useState<string | null>("");
1216
// const [isDialogOpen, setIsDialogOpen] = useState<boolean>(false);
@@ -63,5 +67,17 @@ export const useRegistEvent = (data: EventType) => {
6367
}
6468
};
6569

66-
return { registEvent, isLoading };
70+
const { mutate: checkPaymentStatus } = useMutation({
71+
mutationKey: ["checkPaymentStatus"],
72+
mutationFn: async ({ transaction_no }: { transaction_no: string }) =>
73+
eventsService.checkPaymentStatus(transaction_no),
74+
onSuccess: (data) => {
75+
openDialog({
76+
content: <EventCheckStatusModal status={data?.data?.status} transaction_no={data?.data?.transaction_no} />,
77+
size: "sm",
78+
});
79+
},
80+
});
81+
82+
return { registEvent, isLoading, checkPaymentStatus };
6783
};

src/features/events/types/userEvent.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ export interface UserEventResponse {
6666
user_id: string;
6767
image_proof_payment: string;
6868
payment_url: string;
69+
transaction_no: string;
6970
payment_date: string | null;
7071
status: "PENDING" | "SUCCESS" | "FAILED";
7172
created_at: string;

src/services/events/index.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
import { HttpResponse } from "@/types/http";
22
import { fetcher } from "../instance";
3-
import { AdminEventResponseType, CreateEventPayload, EventType, RegistrationForm } from "@/domains/Events";
3+
import {
4+
AdminEventResponseType,
5+
CheckPaymentResponse,
6+
CreateEventPayload,
7+
EventType,
8+
RegistrationForm,
9+
} from "@/domains/Events";
410
import { UserEventResponse } from "@/features/events/types/userEvent";
511

612
export const eventsService = {
@@ -77,4 +83,8 @@ export const eventsService = {
7783
async updateEventAdmin(id: string, payload: CreateEventPayload): Promise<HttpResponse<null>> {
7884
return fetcher.put(`/admin/events/${id}`, payload);
7985
},
86+
87+
async checkPaymentStatus(transaction_no: string): Promise<HttpResponse<CheckPaymentResponse>> {
88+
return fetcher.get(`/transactions/${transaction_no}/status`);
89+
},
8090
};

0 commit comments

Comments
 (0)