Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(client): save data in cookie after login #175

Merged
merged 6 commits into from
Jun 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions apps/admin/app/(dashboard)/dashboard/documents/page.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
'use client';
import { returnUserId } from '@/app/ui/utils';
import { returnUser } from '@/app/lib/utils';
import { Button } from '@repo/ui/components/ui/button';
import { Checkbox } from '@repo/ui/components/ui/checkbox';
import {
Expand Down Expand Up @@ -28,7 +28,8 @@ export default function Documents() {

useEffect(() => {
const getFIles = async () => {
setUserId(await returnUserId());
const user = await returnUser();
setUserId(user?.id || null);
const files = await getAllFiles();
setFiles(files);
};
Expand Down
15 changes: 15 additions & 0 deletions apps/admin/app/lib/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
'use server';
import { cookies } from 'next/headers';
import type { User } from '../ui/LoginForm';

export async function saveCookie(user: User, token?: string): Promise<void> {
cookies().set('user', JSON.stringify(user), { path: '/', secure: true, sameSite: 'strict' });
if (token) {
cookies().set('access_token', token, { path: '/', secure: true, sameSite: 'strict' });
}
}

export async function returnUser(): Promise<User | null> {
const user = cookies().get('user')?.value;
return user ? JSON.parse(user) : null;
}
2 changes: 1 addition & 1 deletion apps/admin/app/ui/LoginForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { Label } from '@repo/ui/components/ui/label';
import { useRouter } from 'next/navigation';
import { useForm } from 'react-hook-form';
import { z } from 'zod';
import { saveCookie } from './utils';
import { saveCookie } from '../lib/utils';

export type User = {
id: number;
Expand Down
15 changes: 0 additions & 15 deletions apps/admin/app/ui/utils.ts

This file was deleted.

2 changes: 1 addition & 1 deletion apps/admin/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
"@hookform/resolvers": "^3.6.0",
"@repo/ui": "workspace:*",
"date-fns": "^3.6.0",
"lucide-react": "^0.387.0",
"lucide-react": "^0.390.0",
"next": "^14.2.3",
"react": "^18.3.1",
"react-dom": "^18.3.1",
Expand Down
2 changes: 1 addition & 1 deletion apps/api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
"@hono/zod-validator": "^0.2.2",
"@repo/types": "workspace:*",
"@supabase/supabase-js": "^2.43.4",
"hono": "^4.4.3",
"hono": "^4.4.4",
"stripe": "^15.9.0",
"zod": "^3.23.8",
"zod-validation-error": "^3.3.0"
Expand Down
13 changes: 1 addition & 12 deletions apps/client/app/(auth)/(members)/members/votes/page.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
'use client';
import type { Vote } from '@/app/lib/type/Votes';
import { type User, checkSubscription } from '@/app/lib/user/utils';
import { getAllVotes } from '@/app/lib/votes/utils';
import { Badge } from '@repo/ui/components/ui/badge';
import { Button } from '@repo/ui/components/ui/button';
import { Card } from '@repo/ui/components/ui/card';
import { Loader2 } from 'lucide-react';
import { useRouter } from 'next/navigation';
import { useEffect, useState } from 'react';

const Icons = {
Expand All @@ -17,18 +15,9 @@ export default function ListVotes() {
const [votes, setVotes] = useState<Vote[]>([]);
const [loading, setLoading] = useState(true);
const [filter, setFilter] = useState<'all' | 'ongoing' | 'finished' | 'not_started'>('all');
const [subscriptionChecked, setSubscriptionChecked] = useState(false);
const router = useRouter();

useEffect(() => {
async function fetchData() {
if (!subscriptionChecked) {
const user = JSON.parse(localStorage.getItem('user') as string) as User;
if (!checkSubscription(user)) {
return router.push('/');
}
setSubscriptionChecked(true);
}
try {
const result = await getAllVotes();
setVotes(result.data);
Expand All @@ -39,7 +28,7 @@ export default function ListVotes() {
}

fetchData();
}, [subscriptionChecked, router]);
}, []);

const filteredVotes = () => {
switch (filter) {
Expand Down
60 changes: 26 additions & 34 deletions apps/client/app/(auth)/account/page.tsx
Original file line number Diff line number Diff line change
@@ -1,62 +1,54 @@
'use client';
import { type User, checkSubscription, getUserAvatar, getUserInfo } from '@/app/lib/user/utils';
import {
type User,
checkSubscriptionStatus,
getUserFromDB,
saveUserCookie,
updateUserInformation,
} from '@/app/lib/utils';
import { Avatar, AvatarFallback } from '@repo/ui/components/ui/avatar';
import { Badge } from '@repo/ui/components/ui/badge';
import { Button } from '@repo/ui/components/ui/button';
import { Card, CardContent, CardFooter, CardHeader, CardTitle } from '@repo/ui/components/ui/card';
import { Input } from '@repo/ui/components/ui/input';
import { Label } from '@repo/ui/components/ui/label';
import { toast } from '@repo/ui/components/ui/sonner';
import { Loader2 } from 'lucide-react';
import { CircleArrowLeft, Loader2 } from 'lucide-react';
import Link from 'next/link';
import { useEffect, useState } from 'react';

const Icons = {
spinner: Loader2,
};

async function updateUserInformation(id: number, username: string, first_name: string, last_name: string) {
fetch(`${process.env.NEXT_PUBLIC_API_URL}/users/${id}`, {
method: 'PATCH',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${localStorage.getItem('access_token')}`,
},
body: JSON.stringify({
username,
first_name,
last_name,
}),
})
.then(async (response) => await response.json())
.then((data: { user: User }) => {
if ('error' in data) {
return;
}
localStorage.setItem('user', JSON.stringify(data));
})
.catch((error: Error) => console.error(error));
}

export default function UserAccount() {
const [user, setUser] = useState<User | null>(null);
const [status, setStatus] = useState<null | 'applied' | 'approved' | 'rejected'>(null);
const [loading, setLoading] = useState(true);

useEffect(() => {
const fetchData = async () => {
const user = await getUserInfo();
if (!user) {
return;
}
setUser(user);
setStatus(checkSubscription(user));
const user = await getUserFromDB();
setUser(user || null);
setStatus(await checkSubscriptionStatus(user));
setLoading(false);
};

fetchData();
}, []);

async function handleUpateUserInformation(user: User) {
try {
await updateUserInformation(user.id, user.username, user.first_name, user.last_name);
} catch (error) {
throw new Error('Failed to update file');
} finally {
toast.success('Informations mises à jour');
saveUserCookie(user);
setUser(user);
}
}

if (loading) {
return (
<div className="flex items-center justify-center h-96">
Expand All @@ -82,8 +74,9 @@ export default function UserAccount() {
</Link>
</div>
<header className="flex items-center gap-4 mb-8">
<CircleArrowLeft className="w-8 h-8" onClick={() => window.history.back()} cursor={'pointer'} />
<Avatar className="h-12 w-12">
<AvatarFallback>{getUserAvatar()}</AvatarFallback>
<AvatarFallback>{user?.username.charAt(0).toUpperCase()}</AvatarFallback>
</Avatar>
<div>
<h1 className="text-2xl font-bold">
Expand Down Expand Up @@ -143,8 +136,7 @@ export default function UserAccount() {
<Button
className="w-[120px] text-center mt-4 rounded-full align-middle"
onClick={() => {
updateUserInformation(user.id, user.username, user.first_name, user.last_name);
toast.success('Informations mises à jour');
handleUpateUserInformation(user);
}}
>
Editer
Expand Down
2 changes: 1 addition & 1 deletion apps/client/app/(withNavbar)/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ export default function Page(): JSX.Element {
className="object-cover h-full rounded-[96px]"
/>
</div>
<h1 className="font-normal text-[144px] ">Association</h1>
<h1 className="font-normal text-[144px]">Association</h1>
</div>

<div className="flex gap-8 h-44 items-center justify-center">
Expand Down
51 changes: 0 additions & 51 deletions apps/client/app/lib/user/utils.ts

This file was deleted.

95 changes: 95 additions & 0 deletions apps/client/app/lib/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
'use server';
import { cookies } from 'next/headers';
export interface User {
id: number;
username: string;
email: string;
first_name: string;
last_name: string;
subscription: string | null;
status: 'applied' | 'approved' | 'rejected' | null;
date_validity: string | null;
roles: { id: number; name: string }[];
}

// Always up to date but less performant
export async function getUserFromDB(): Promise<User> {
const API_URL = process.env.ATHLONIX_API_URL;
const token = cookies().get('access_token')?.value;
const response = await fetch(`${API_URL}/users/me`, {
headers: { Authorization: `Bearer ${token}` },
});
if (!response.ok) {
throw new Error('Failed to fetch user info');
}
const user = (await response.json()) as User;
// Save user in cookie for faster access
cookies().set('user', JSON.stringify(user), { path: '/', secure: true, sameSite: 'strict' });
return user;
}

// Faster but might be outdated
export async function getUserFromCookie(): Promise<User | null> {
const user = cookies().get('user')?.value;
return user ? JSON.parse(user) : null;
}

export async function checkSubscriptionStatus(user: User): Promise<'applied' | 'approved' | 'rejected' | null> {
if (user.status === null) {
return null;
}

if (user.status === 'applied') {
return 'applied';
}

if (user.status === 'rejected') {
return 'rejected';
}

if (user.date_validity === null || new Date(user.date_validity) < new Date()) {
return null;
}

return 'approved';
}

export async function saveUserCookie(user: User, token?: string): Promise<void> {
cookies().set('user', JSON.stringify(user), { path: '/', secure: true, sameSite: 'strict' });
if (token) {
cookies().set('access_token', token, { path: '/', secure: true, sameSite: 'strict' });
}
}

export async function updateUserInformation(
id: number,
username: string,
first_name: string,
last_name: string,
): Promise<User | null> {
const token = cookies().get('access_token')?.value;
const url = process.env.ATHLONIX_API_URL;
fetch(`${url}/users/${id}`, {
method: 'PATCH',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${token}`,
},
body: JSON.stringify({
username,
first_name,
last_name,
}),
})
.then(async (response) => await response.json())
.then((data: { user: User }) => {
if ('error' in data) {
return;
}
cookies().set('user', JSON.stringify(data.user), { path: '/', secure: true, sameSite: 'strict' });
return data.user;
})
.catch((error: Error) => console.error(error));

return null;
}
7 changes: 5 additions & 2 deletions apps/client/app/lib/votes/utils.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
'use server';
import { cookies } from 'next/headers';
import type { Vote } from '../type/Votes';

export async function getAllVotes(): Promise<{ data: Vote[]; count: number }> {
const API_URL = process.env.NEXT_PUBLIC_API_URL;
const API_URL = process.env.ATHLONIX_API_URL;
const token = cookies().get('access_token')?.value;
const response = await fetch(`${API_URL}/polls?all=true`, {
headers: { Authorization: `Bearer ${localStorage.getItem('access_token')}` },
headers: { Authorization: `Bearer ${token}` },
cache: 'no-cache',
});
if (!response.ok) {
Expand Down
Loading
Loading