Skip to content

Commit 7d99de7

Browse files
committed
feat(event): fetch events from backend
1 parent 78b3dc5 commit 7d99de7

11 files changed

Lines changed: 52 additions & 35 deletions

File tree

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

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,26 @@
1+
"use client";
2+
13
import { Avatar, AvatarImage, AvatarFallback } from "@/components/ui/Avatar";
24

35
import { Calendar1, User } from "lucide-react";
46
import { Separator } from "@/components/ui/Separator";
57
import { Link } from "@/lib/navigation";
8+
import { useAuthUser } from "@/components/hooks/UseAuthUser";
69

7-
const user = {
8-
name: "Putra Satria",
9-
image: "",
10-
fallback: "PS",
11-
};
12-
13-
export default async function UserLayout({ children }: { children: React.ReactNode }) {
10+
export default function UserLayout({ children }: { children: React.ReactNode }) {
11+
const { user } = useAuthUser();
1412
return (
1513
<section className="container mx-auto px-5 pt-24 pb-28">
1614
<div className="grid grid-cols-5 gap-8">
1715
<aside className="fixed right-0 bottom-0 left-0 col-span-1 mt-8 flex w-full flex-col justify-between gap-4 self-start rounded-lg bg-white lg:sticky lg:top-24 lg:flex-col lg:justify-start lg:bg-transparent dark:bg-slate-950">
1816
<div className="flex items-center gap-4">
1917
<Avatar className="h-12 w-12">
20-
<AvatarImage src={user.image} />
21-
<AvatarFallback>{user.fallback}</AvatarFallback>
18+
<AvatarImage src="" />
19+
<AvatarFallback>US</AvatarFallback>
2220
</Avatar>
2321
<div>
24-
<p className="text-muted-foreground text-sm">Hi</p>
25-
<p className="font-semibold">{user.name}</p>
22+
<p className="text-muted-foreground text-sm">Hello</p>
23+
<p className="font-semibold">{user?.username}</p>
2624
</div>
2725
</div>
2826

src/app/[locale]/admin/events/page.tsx

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,20 @@
1+
"use client";
2+
3+
import { useEvents } from "@/features/events/hooks/useEvent";
14
import Link from "next/link";
25

36
const EventListPage = () => {
7+
const { events, isLoading } = useEvents();
48
return (
59
<div>
610
<h1>Event List</h1>
7-
11+
{isLoading && <p>Fetching events...</p>}
812
<ul>
9-
<li>
10-
Event 123 <Link href="/admin/events/123/edit">Edit</Link>
11-
</li>
12-
<li>
13-
Event 456 <Link href="/admin/events/456/edit">Edit</Link>
14-
</li>
13+
{events.map((ev) => (
14+
<li key={ev.id}>
15+
{ev.title} <Link href={`/admin/events/${ev.id}/edit`}>Edit</Link>
16+
</li>
17+
))}
1518
</ul>
1619
</div>
1720
);

src/app/[locale]/admin/layout.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import { cookies } from "next/headers";
22
import RouteBreadcrumb from "@/components/common/RouteBreadcrumb";
33
import AdminSidebar from "@/components/layout/AdminSidebar";
44

5-
65
import { jwtDecode } from "jwt-decode";
76
import { redirect } from "next/navigation";
87
import { AuthJwtPayload } from "@/types";
@@ -16,7 +15,7 @@ export default async function AdminLayout({ children }: { children: React.ReactN
1615
if (!token) return redirect("/");
1716

1817
const payload = jwtDecode<AuthJwtPayload>(token.value);
19-
if (payload.role !== "user") return redirect("/");
18+
if (payload.role !== "admin") return redirect("/");
2019

2120
return (
2221
<SidebarProvider>

src/app/api/login/route.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,15 @@ export async function POST(request: Request) {
77
try {
88
const body = await request.json();
99
const { email, password } = body;
10-
const result = await authService.getToken({ email, password });
11-
const token = result.data;
10+
const token = await authService.getToken({ email, password });
1211
const decoded = jwtDecode<AuthJwtPayload>(token);
1312

14-
const res = NextResponse.json(token);
15-
// TODO: fix(security) set cookie with these attributes
16-
// httpOnly=true, secure=true if prod, same-site
17-
// max-age to follow the token. extract from token
18-
// TODO: create logout endpoint and remove cookie there
13+
const res = NextResponse.json({
14+
token,
15+
payload: decoded,
16+
});
17+
// TODO: fix(security)
18+
// set cookie with these attributes: same-site
1919

2020
// Now server components will have access to token
2121
// Question: should we encrypt token?

src/components/layout/Navbar/UserMenu.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,11 @@ const DesktopUserMenu = () => {
4141
</div>
4242
</Link>
4343
</DropdownMenuItem>
44+
{user?.role === "admin" && (
45+
<DropdownMenuItem asChild>
46+
<Link href="/admin/events">Dashboard</Link>
47+
</DropdownMenuItem>
48+
)}
4449
{USER_LINKS.map(({ id, href }) => (
4550
<DropdownMenuItem key={id} asChild>
4651
<Link href={href}>{t(`navbar.user.${id}`)}</Link>

src/components/provider/AuthProvider/index.tsx

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
"use client";
22

3-
import { createContext, ReactNode } from "react";
3+
import { createContext, ReactNode, useState } from "react";
44
import { AuthJwtPayload, UserContextType } from "@/types";
55

66
export const UserContext = createContext<UserContextType | undefined>(undefined);
@@ -15,8 +15,10 @@ interface AuthProviderProps {
1515
}
1616

1717
export const AuthProvider = ({ payload, children }: AuthProviderProps) => {
18-
const user = payload || null;
18+
const [user, setUser] = useState(payload || null);
1919
const isAuthenticated = !!user;
2020

21-
return <UserContext.Provider value={{ user, isAuthenticated, isLoading: false }}>{children}</UserContext.Provider>;
21+
return (
22+
<UserContext.Provider value={{ user, setUser, isAuthenticated, isLoading: false }}>{children}</UserContext.Provider>
23+
);
2224
};

src/features/auth/hooks/useAuth.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ export const useAuthService = () => {
2121
router.push("/");
2222
toast.success(t("sign-in-success"));
2323

24-
return res.data;
24+
return res;
2525
} catch (err) {
2626
toast((err as Error)?.message || t("sign-in-failed"));
2727
} finally {

src/features/auth/sign-in/SignInPage.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,12 @@ import { LoginForm, loginSchema } from "@/domains/Auth";
1010
import { zodResolver } from "@hookform/resolvers/zod";
1111
import { Link } from "@/lib/navigation";
1212
import { useAuthService } from "../hooks/useAuth";
13+
import { useAuthUser } from "@/components/hooks/UseAuthUser";
1314

1415
const SignInPage = () => {
1516
const t = useTranslations("Auth.SignInPage");
1617
const { login, isLoading } = useAuthService();
18+
const { setUser } = useAuthUser();
1719

1820
const form = useForm<LoginForm>({
1921
resolver: zodResolver(loginSchema),
@@ -24,7 +26,9 @@ const SignInPage = () => {
2426
});
2527

2628
const onSubmit: SubmitHandler<LoginForm> = (formData) => {
27-
login(formData);
29+
login(formData).then((data) => {
30+
setUser(data!.data.payload);
31+
});
2832
};
2933

3034
return (

src/services/auth/index.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,24 @@
11
import { fetcher } from "../instance";
22
import { LoginForm, RegisterForm, ForgotPasswordForm, ResetPasswordForm } from "@/domains/Auth";
33
import { User } from "@/features/auth/types";
4+
import { AuthJwtPayload } from "@/types";
45
import { HttpResponse } from "@/types/http";
56
import axios from "axios";
67

78
export const authService = {
8-
login(payload: LoginForm): Promise<HttpResponse<string>> {
9+
login(payload: LoginForm): Promise<HttpResponse<{ token: string; payload: AuthJwtPayload }>> {
910
return axios.post("/api/login", payload);
1011
},
1112

1213
logout(): Promise<HttpResponse<unknown>> {
1314
return axios.post("/api/logout");
1415
},
1516

16-
getToken(payload: LoginForm): Promise<HttpResponse<string>> {
17-
return fetcher.post("auth/login", payload);
17+
getToken(payload: LoginForm): Promise<string> {
18+
// FIXME
19+
return axios
20+
.post("https://lms-be-development.hammercode.org/api/v1/auth/login", payload)
21+
.then((res) => res.data.data);
1822
},
1923

2024
register(payload: RegisterForm): Promise<HttpResponse<User>> {

src/services/instance.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ const injectInterceptors = (instance: AxiosInstance): AxiosInstance => {
2626
return response.data;
2727
},
2828
(error: AxiosError) => {
29+
console.log({ error });
2930
return Promise.reject(error?.response?.data);
3031
}
3132
);

0 commit comments

Comments
 (0)