Skip to content

Commit cee995d

Browse files
committed
feat: finishing flow payment events
1 parent 2f05f7b commit cee995d

File tree

7 files changed

+242
-11
lines changed

7 files changed

+242
-11
lines changed

src/app/[locale]/(public)/layout.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import Navbar from "@/components/layout/Navbar";
44
import Footer from "@/components/layout/Footer";
55

66
import { useParams, usePathname } from "next/navigation";
7-
const authPaths = ["sign-in", "sign-up", "forgot-password", "reset-password"];
7+
const authPaths = ["sign-in", "sign-up", "forgot-password", "reset-password", "payment/success"];
88

99
const WrapperLayout = ({ children }: { children: React.ReactNode }) => {
1010
const params = useParams();
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import UserPaymentEventSuccess from "@/features/events/pages/UserPaymentEventSuccess";
2+
import ProtectedRoute from "@/components/layout/ProtectedRoute";
3+
4+
const UserPaymentEventSuccessPage = () => {
5+
return (
6+
<ProtectedRoute requiredRole="user">
7+
<UserPaymentEventSuccess />
8+
</ProtectedRoute>
9+
);
10+
};
11+
12+
export default UserPaymentEventSuccessPage;

src/components/common/TableData/TableData.tsx

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ interface TableDataProps<T> {
1818
rightAction?: React.ReactNode;
1919
currentPage?: number;
2020
totalPages?: number;
21-
onSearchChange?: (search: string) => void;
2221
}
2322

2423
function TableData<T>({
@@ -31,23 +30,28 @@ function TableData<T>({
3130
rightAction,
3231
currentPage = 1,
3332
totalPages = 1,
34-
onSearchChange,
3533
}: TableDataProps<T>) {
36-
const { getParam, setParams } = useQueryParams();
34+
const { getParam, getNumberParam, setParams } = useQueryParams();
3735
const [searchValue, setSearchValue] = useState(getParam("search", ""));
3836
const debouncedSearchValue = useDebounced(searchValue, 500);
3937
const { handleItemsPerPageChange } = usePagination({ currentPage, totalPages, itemsPerPage });
4038

39+
const currentSearch = getParam("search", "");
40+
const currentPageParam = getNumberParam("page", 1);
41+
4142
useEffect(() => {
42-
setParams({
43-
search: debouncedSearchValue || null,
44-
page: debouncedSearchValue ? 1 : currentPage,
45-
});
43+
const nextSearch = debouncedSearchValue || null;
44+
const nextPage = currentPageParam || 1;
4645

47-
if (onSearchChange) {
48-
onSearchChange(debouncedSearchValue);
46+
if (currentSearch === (nextSearch ?? "") && currentPageParam === nextPage) {
47+
return;
4948
}
50-
}, [debouncedSearchValue, onSearchChange, currentPage]);
49+
50+
setParams({
51+
search: nextSearch,
52+
page: nextPage === currentPageParam ? null : nextPage,
53+
});
54+
}, [debouncedSearchValue, currentPageParam, currentSearch]);
5155

5256
const table = useReactTable({
5357
data,

src/features/events/hooks/useEvent.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,3 +214,11 @@ export const useUpdateEvent = (t: (key: string) => string, id: string) => {
214214
isLoading: loadingCreateEvent || loadingCreateImage,
215215
};
216216
};
217+
218+
export const useGetPaymentDetail = (order_no: string) => {
219+
return useQuery({
220+
queryKey: ["getPaymentDetail", order_no],
221+
queryFn: async () => eventsService.getPaymentDetail(order_no),
222+
enabled: !!order_no,
223+
});
224+
};
Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
"use client";
2+
3+
import { CircleCheckBig, ArrowLeft, MessageSquare } from "lucide-react";
4+
import { useParams, useRouter } from "next/navigation";
5+
// import { useTranslations } from "next-intl";
6+
import { Card, CardContent, CardFooter, CardHeader } from "@/components/ui/Card";
7+
import Badge from "@/components/ui/Badge";
8+
import { Button } from "@/components/ui/Button";
9+
import { useGetPaymentDetail } from "../hooks/useEvent";
10+
11+
const UserPaymentEventSuccess = () => {
12+
const router = useRouter();
13+
const params = useParams();
14+
15+
const { data: paymentData, isLoading: isLoadingPaymentDetail } = useGetPaymentDetail(params?.orderNo as string);
16+
17+
const handleBackToEvents = () => {
18+
router.push("/my-events");
19+
};
20+
21+
if (isLoadingPaymentDetail) {
22+
return <div>Loading...</div>;
23+
}
24+
25+
return (
26+
<div className="container mx-auto max-w-3xl px-4 py-8">
27+
<div className="flex flex-col items-center">
28+
<div className="mb-6">
29+
<div className="flex h-24 w-24 items-center justify-center rounded-full bg-green-50 dark:bg-green-950">
30+
<CircleCheckBig className="h-16 w-16 text-green-600 dark:text-green-400" />
31+
</div>
32+
</div>
33+
34+
<h1 className="mb-3 text-center text-3xl font-bold text-slate-900 dark:text-slate-50">Payment Successful!</h1>
35+
36+
<p className="mb-8 max-w-md text-center text-slate-600 dark:text-slate-400">
37+
Your payment has been processed successfully. We've sent a confirmation email to your registered email
38+
address.
39+
</p>
40+
41+
<div className="w-full">
42+
<Card className="shadow-lg">
43+
<CardHeader className="border-b border-slate-200 dark:border-slate-800">
44+
<div className="flex items-center justify-between">
45+
<h2 className="text-xl font-semibold text-slate-900 dark:text-slate-50">Payment Details</h2>
46+
<Badge variant="open">PAID</Badge>
47+
</div>
48+
</CardHeader>
49+
50+
<CardContent className="space-y-4 pt-6">
51+
<div className="flex items-start justify-between">
52+
<span className="text-sm text-slate-600 dark:text-slate-400">Order Number</span>
53+
<span className="text-right text-sm font-medium text-slate-900 dark:text-slate-50">
54+
{paymentData?.data?.order_no}
55+
</span>
56+
</div>
57+
58+
<div className="flex items-start justify-between">
59+
<span className="text-sm text-slate-600 dark:text-slate-400">Transaction Number</span>
60+
<span className="text-right text-sm font-medium text-slate-900 dark:text-slate-50">
61+
{paymentData?.data?.transaction_no}
62+
</span>
63+
</div>
64+
65+
<div className="flex items-start justify-between">
66+
<span className="text-sm text-slate-600 dark:text-slate-400">Event</span>
67+
<span className="max-w-[60%] text-right text-sm font-medium text-slate-900 dark:text-slate-50">
68+
{paymentData?.data?.event_detail.title}
69+
</span>
70+
</div>
71+
72+
<div className="flex items-start justify-between">
73+
<span className="text-sm text-slate-600 dark:text-slate-400">Payment Date</span>
74+
<span className="text-right text-sm font-medium text-slate-900 dark:text-slate-50">
75+
{paymentData?.data?.payment_date
76+
? new Date(paymentData?.data?.payment_date).toLocaleString("id-ID", {
77+
day: "2-digit",
78+
month: "long",
79+
year: "numeric",
80+
hour: "2-digit",
81+
minute: "2-digit",
82+
})
83+
: "-"}
84+
</span>
85+
</div>
86+
87+
<div className="flex items-start justify-between border-t border-slate-200 pt-4 dark:border-slate-800">
88+
<span className="text-base font-semibold text-slate-900 dark:text-slate-50">Total Amount</span>
89+
<span className="text-base font-bold text-green-600 dark:text-green-400">
90+
Rp {paymentData?.data?.event_detail.price.toLocaleString("id-ID")}
91+
</span>
92+
</div>
93+
</CardContent>
94+
95+
<CardContent className="pt-0 pb-6">
96+
<div className="space-y-3 rounded-lg bg-slate-50 p-4 dark:bg-slate-900">
97+
<h3 className="text-sm font-semibold text-slate-900 dark:text-slate-50">Participant Information</h3>
98+
<div className="space-y-2">
99+
<div className="flex justify-between">
100+
<span className="text-sm text-slate-600 dark:text-slate-400">Name</span>
101+
<span className="text-sm font-medium text-slate-900 dark:text-slate-50">
102+
{paymentData?.data?.user_detail.fullname}
103+
</span>
104+
</div>
105+
<div className="flex justify-between">
106+
<span className="text-sm text-slate-600 dark:text-slate-400">Email</span>
107+
<span className="text-sm font-medium text-slate-900 dark:text-slate-50">
108+
{paymentData?.data?.user_detail.email}
109+
</span>
110+
</div>
111+
<div className="flex justify-between">
112+
<span className="text-sm text-slate-600 dark:text-slate-400">Phone</span>
113+
<span className="text-sm font-medium text-slate-900 dark:text-slate-50">
114+
{paymentData?.data?.user_detail.phone_number}
115+
</span>
116+
</div>
117+
</div>
118+
</div>
119+
</CardContent>
120+
121+
<CardContent className="pt-0 pb-6">
122+
<div className="space-y-3 rounded-lg bg-blue-50 p-4 dark:bg-blue-950">
123+
<h3 className="text-sm font-semibold text-blue-900 dark:text-blue-50">Event Schedule</h3>
124+
<div className="space-y-2">
125+
<div className="flex justify-between">
126+
<span className="text-sm text-blue-700 dark:text-blue-300">Date</span>
127+
<span className="text-sm font-medium text-blue-900 dark:text-blue-50">
128+
{paymentData?.data?.event_detail?.date
129+
? new Date(paymentData?.data?.event_detail?.date).toLocaleDateString("id-ID", {
130+
day: "2-digit",
131+
month: "long",
132+
year: "numeric",
133+
})
134+
: "-"}
135+
</span>
136+
</div>
137+
<div className="flex justify-between">
138+
<span className="text-sm text-blue-700 dark:text-blue-300">Duration</span>
139+
<span className="text-sm font-medium text-blue-900 dark:text-blue-50">
140+
{paymentData?.data?.event_detail.duration}
141+
</span>
142+
</div>
143+
<div className="flex justify-between">
144+
<span className="text-sm text-blue-700 dark:text-blue-300">Type</span>
145+
<span className="text-sm font-medium text-blue-900 dark:text-blue-50">
146+
{paymentData?.data?.event_detail.type} ({paymentData?.data?.event_detail.session_type})
147+
</span>
148+
</div>
149+
<div className="flex justify-between">
150+
<span className="text-sm text-blue-700 dark:text-blue-300">Location</span>
151+
<span className="text-sm font-medium text-blue-900 dark:text-blue-50">
152+
{paymentData?.data?.event_detail.location}
153+
</span>
154+
</div>
155+
</div>
156+
</div>
157+
</CardContent>
158+
159+
<CardFooter className="flex flex-col items-center justify-center gap-3 border-t border-slate-200 pt-6 dark:border-slate-800">
160+
<Button variant="outline" className="w-full sm:w-auto" onClick={handleBackToEvents}>
161+
<ArrowLeft className="mr-2 h-4 w-4" />
162+
Back to My Events
163+
</Button>
164+
</CardFooter>
165+
</Card>
166+
</div>
167+
168+
<div className="mt-8 text-center">
169+
<p className="text-sm text-slate-600 dark:text-slate-400">
170+
Need help? Contact us at{" "}
171+
<a href="https://wa.me/6281243530207" className="text-hmc-primary flex gap-2 hover:underline">
172+
+62-812-4353-0207 <MessageSquare className="h-4 w-4" />
173+
</a>
174+
</p>
175+
</div>
176+
</div>
177+
</div>
178+
);
179+
};
180+
181+
export default UserPaymentEventSuccess;

src/features/events/types.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,24 @@ export type EventInfoType = {
2525
id: number;
2626
icon: React.ReactNode;
2727
};
28+
29+
export type PaymentDetailResponse = {
30+
order_no: string;
31+
transaction_no: string;
32+
payment_date: string;
33+
status: string;
34+
event_detail: {
35+
title: string;
36+
date: Date;
37+
type: string;
38+
location: string;
39+
duration: string;
40+
price: number;
41+
session_type: string;
42+
};
43+
user_detail: {
44+
fullname: string;
45+
email: string;
46+
phone_number: string;
47+
};
48+
};

src/services/events/index.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
// RegistrationForm,
99
} from "@/domains/Events";
1010
import { UserEventResponse } from "@/features/events/types/userEvent";
11+
import { PaymentDetailResponse } from "@/features/events/types";
1112

1213
export const eventsService = {
1314
/**
@@ -92,4 +93,8 @@ export const eventsService = {
9293
async registerEvent(event_id: number): Promise<HttpResponse<any>> {
9394
return fetcher.post(`/transactions`, { event_id });
9495
},
96+
97+
async getPaymentDetail(order_no: string): Promise<HttpResponse<PaymentDetailResponse>> {
98+
return fetcher.get(`/orders/${order_no}`);
99+
},
95100
};

0 commit comments

Comments
 (0)