Skip to content

Commit 85b0f5d

Browse files
committed
app: fix site dont using cookies until reload
1 parent 8944b0e commit 85b0f5d

9 files changed

Lines changed: 173 additions & 46 deletions

File tree

app/src/app/(account)/page.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,12 @@
33
import { useRouter } from 'next/navigation'
44
import { useState } from 'react'
55
import { LoginForm } from '@/components/LoginForm'
6+
import { useUser } from '@/contexts/UserContext'
67
import { login } from '@/services/auth'
78

89
export default function LoginPage() {
910
const router = useRouter()
11+
const { refetch } = useUser() // Get the refetch function to update user data after login
1012
const [error, setError] = useState<string>('')
1113

1214
const handleSubmit = async (event: React.FormEvent<HTMLFormElement>) => {
@@ -20,6 +22,7 @@ export default function LoginPage() {
2022

2123
try {
2224
await login(email, password)
25+
await refetch()
2326
router.push('/dashboard')
2427
} catch (err) {
2528
setError((err as Error).message || 'Erro ao realizar login')

app/src/app/(marketing)/about/page.tsx

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,22 +31,40 @@ export default function About() {
3131
</section>
3232

3333
<section className="mb-12 grid grid-cols-1 md:grid-cols-3 gap-8">
34-
<Link href={NAVIGATION.STUDENTS} className="btn text-2xl text-center">
34+
<Link
35+
href={NAVIGATION.STUDENTS}
36+
className="btn text-2xl text-center"
37+
>
3538
Consultar Alunos
3639
</Link>
37-
<Link href={NAVIGATION.PROFESSORS} className="btn text-2xl text-center">
40+
<Link
41+
href={NAVIGATION.PROFESSORS}
42+
className="btn text-2xl text-center"
43+
>
3844
Consultar Professores
3945
</Link>
40-
<Link href={NAVIGATION.GROUPS} className="btn text-2xl text-center">
46+
<Link
47+
href={NAVIGATION.GROUPS}
48+
className="btn text-2xl text-center"
49+
>
4150
Ver Turmas
4251
</Link>
43-
<Link href={NAVIGATION.SUBJECTS} className="btn text-2xl text-center">
52+
<Link
53+
href={NAVIGATION.SUBJECTS}
54+
className="btn text-2xl text-center"
55+
>
4456
Disciplinas Oferecidas
4557
</Link>
46-
<Link href={NAVIGATION.ITINERARIES} className="btn text-2xl text-center">
58+
<Link
59+
href={NAVIGATION.ITINERARIES}
60+
className="btn text-2xl text-center"
61+
>
4762
Itinerários Formativos
4863
</Link>
49-
<Link href={NAVIGATION.LESSONS} className="btn text-2xl text-center">
64+
<Link
65+
href={NAVIGATION.LESSONS}
66+
className="btn text-2xl text-center"
67+
>
5068
Horários de Aula
5169
</Link>
5270
</section>

app/src/app/AuthLayout.tsx

Lines changed: 28 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,40 @@
1-
"use client";
1+
'use client'
22

3-
import { getCookie } from "cookies-next";
4-
import { useEffect, useState } from "react";
5-
import { AppSidebar } from "@/components/AppSidebar";
6-
import SiteHeader from "@/components/SiteHeader";
7-
import { SidebarInset } from "@/components/ui/sidebar";
8-
import { Toaster } from "@/components/ui/sonner";
3+
import { getCookie } from 'cookies-next'
4+
import { useEffect, useState } from 'react'
5+
import { AppSidebar } from '@/components/AppSidebar'
6+
import SiteHeader from '@/components/SiteHeader'
7+
import { SidebarInset } from '@/components/ui/sidebar'
8+
import { Toaster } from '@/components/ui/sonner'
99

1010
export default function AuthLayout({
1111
children,
1212
}: {
13-
children: React.ReactNode;
13+
children: React.ReactNode
1414
}) {
15-
const [isClient, setIsClient] = useState(false);
16-
const [hasAccessToken, setHasAccessToken] = useState(false);
15+
const [isClient, setIsClient] = useState(false)
16+
const [hasAccessToken, setHasAccessToken] = useState(false)
1717

1818
useEffect(() => {
19-
setIsClient(true);
20-
const accessToken = getCookie("access");
21-
setHasAccessToken(!!accessToken);
22-
}, []);
19+
setIsClient(true)
20+
const accessToken = getCookie('access')
21+
setHasAccessToken(!!accessToken)
22+
23+
// Listen for storage events to detect when cookies are set from other tabs/windows
24+
const handleStorageChange = () => {
25+
const newToken = getCookie('access')
26+
setHasAccessToken(!!newToken)
27+
}
28+
29+
window.addEventListener('storage', handleStorageChange)
30+
31+
return () => {
32+
window.removeEventListener('storage', handleStorageChange)
33+
}
34+
}, [])
2335

2436
if (isClient && !hasAccessToken) {
25-
return <main className="flex flex-1 flex-col">{children}</main>;
37+
return <main className="flex flex-1 flex-col">{children}</main>
2638
}
2739

2840
return (
@@ -36,5 +48,5 @@ export default function AuthLayout({
3648
<Toaster />
3749
</SidebarInset>
3850
</>
39-
);
51+
)
4052
}

app/src/components/RoleDashboardCards/components/StaffDashboardCard.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { toast } from 'sonner'
2-
import { NAVIGATION } from '@/config'
32
import { ButtonGridCard } from '@/components/ButtonGridCard'
3+
import { NAVIGATION } from '@/config'
44
import { useUser } from '@/hooks/useUser'
55
import type { DocumentRequest } from '@/types/documentRequest'
66
import { RoleDashboardCardLayout } from './RoleDashboardCardLayout'

app/src/components/RoleDashboardCards/components/StudentDashboardCard.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { toast } from 'sonner'
2-
import { NAVIGATION, ROUTES } from '@/config'
32
import { ButtonGridCard } from '@/components/ButtonGridCard'
43
import { GradesTableCard } from '@/components/GradesTableCard'
4+
import { NAVIGATION, ROUTES } from '@/config'
55
import { useUser } from '@/hooks/useUser'
66
import type { DocumentRequest } from '@/types/documentRequest'
77
import type { StudentProps } from '@/types/student'

app/src/config.ts

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
// API URL configuration
2-
const API_BASE = process.env.NEXT_PUBLIC_PUBLIC_API_HOST || `/api/`;
2+
const API_BASE = process.env.NEXT_PUBLIC_PUBLIC_API_HOST || `/api/`
33
const API_INTERNAL_BASE =
4-
process.env.NEXT_PUBLIC_PRIVATE_API_HOST || `http://api:8000/api/`;
4+
process.env.NEXT_PUBLIC_PRIVATE_API_HOST || `http://api:8000/api/`
55

66
// Factory function to create routes based on a base URL
77
function createRoutes(base: string) {
88
// Django apps base URLs as variables
9-
const usersUrl = `${base}users/`;
10-
const studentsUrl = `${base}students/`;
11-
const schoolUrl = `${base}school/`;
12-
const resourcesUrl = `${base}resources/`;
9+
const usersUrl = `${base}users/`
10+
const studentsUrl = `${base}students/`
11+
const schoolUrl = `${base}school/`
12+
const resourcesUrl = `${base}resources/`
1313

1414
return {
1515
// Main paths
@@ -41,13 +41,13 @@ function createRoutes(base: string) {
4141

4242
// RESOURCES app subpaths
4343
RESOURCE_LOANS: `${resourcesUrl}loans/`,
44-
};
44+
}
4545
}
4646

4747
// Frontend navigation routes
4848
const createNavigation = () => {
49-
const apiPrefix = `/api/`;
50-
const adminPrefix = `${apiPrefix}admin/`;
49+
const apiPrefix = `/api/`
50+
const adminPrefix = `${apiPrefix}admin/`
5151

5252
return {
5353
// Main pages
@@ -78,12 +78,12 @@ const createNavigation = () => {
7878
ADMIN_SCHOOL_LESSON_PLAN: `${adminPrefix}school/lessonplan/`,
7979
ADMIN_REPORTS: `${adminPrefix}reports/`,
8080
ADMIN_AUTH_GROUP: `${adminPrefix}auth/group/`,
81-
};
82-
};
81+
}
82+
}
8383

8484
// Export routes for application use
85-
const ROUTES = createRoutes(API_BASE);
86-
const ROUTES_INTERNAL = createRoutes(API_INTERNAL_BASE);
87-
const NAVIGATION = createNavigation();
85+
const ROUTES = createRoutes(API_BASE)
86+
const ROUTES_INTERNAL = createRoutes(API_INTERNAL_BASE)
87+
const NAVIGATION = createNavigation()
8888

89-
export { ROUTES, ROUTES_INTERNAL, NAVIGATION };
89+
export { ROUTES, ROUTES_INTERNAL, NAVIGATION }

app/src/contexts/UserContext.tsx

Lines changed: 86 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ export const UserProvider: React.FC<UserProviderProps> = ({ children }) => {
3636

3737
const getUser = useCallback(
3838
async (forceRefetch: boolean = false): Promise<UserProps | null> => {
39-
if (user && !forceRefetch) {
39+
if (user && !forceRefetch && loading === false) {
4040
// If user data already exists and we're not forcing a refetch, return it without making a new request
4141
return user
4242
}
@@ -60,7 +60,14 @@ export const UserProvider: React.FC<UserProviderProps> = ({ children }) => {
6060
}
6161
}
6262

63-
setLoading(true)
63+
// Only set loading to true if this is not a force refetch
64+
// This prevents flickering when user data is being revalidated
65+
if (forceRefetch) {
66+
// Don't change the loading state when forcing refetch for smoother UX
67+
} else {
68+
setLoading(true)
69+
}
70+
6471
setError(null)
6572

6673
const response = await api.get<UserProps>(`${ROUTES.USER_INFO}`)
@@ -92,15 +99,26 @@ export const UserProvider: React.FC<UserProviderProps> = ({ children }) => {
9299
return null
93100
}
94101
} finally {
95-
setLoading(false)
102+
// Only set loading to false if not forcing a refetch (to maintain current state during refresh)
103+
if (!forceRefetch) {
104+
setLoading(false)
105+
} else {
106+
// For forced refetch, ensure loading is eventually set to false
107+
setTimeout(() => {
108+
if (typeof window !== 'undefined') {
109+
setLoading(false)
110+
}
111+
}, 0)
112+
}
96113
}
97114
},
98-
[user],
115+
[user, loading],
99116
)
100117

101-
const refetch = useCallback(() => {
102-
setUser(null)
103-
void getUser(true) // force refetch
118+
const refetch = useCallback(async () => {
119+
setLoading(true) // Set loading state during refetch
120+
// Don't reset user to null during refetch to avoid UI flickering
121+
return getUser(true) // force refetch and return the result
104122
}, [getUser])
105123

106124
// Initialize user data on mount
@@ -132,6 +150,67 @@ export const UserProvider: React.FC<UserProviderProps> = ({ children }) => {
132150
}
133151
}, [getUser])
134152

153+
// Listen for token refresh events to update user info
154+
useEffect(() => {
155+
const handleTokenRefresh = () => {
156+
if (typeof window !== 'undefined') {
157+
const accessToken = getCookie('access')
158+
if (accessToken) {
159+
// Token was refreshed, refetch user data
160+
void refetch()
161+
}
162+
}
163+
}
164+
165+
if (typeof window !== 'undefined') {
166+
window.addEventListener('tokenRefreshed', handleTokenRefresh)
167+
}
168+
169+
return () => {
170+
if (typeof window !== 'undefined') {
171+
window.removeEventListener('tokenRefreshed', handleTokenRefresh)
172+
}
173+
}
174+
}, [refetch])
175+
176+
// Listen for login and logout events to update user state accordingly
177+
useEffect(() => {
178+
const handleLoginCompleted = () => {
179+
if (typeof window !== 'undefined') {
180+
const accessToken = getCookie('access')
181+
if (accessToken) {
182+
// New login, fetch user data
183+
void getUser(true) // force refetch
184+
}
185+
}
186+
}
187+
188+
const handleLogoutCompleted = () => {
189+
// Clear user data on logout
190+
setUser(null)
191+
setLoading(false)
192+
setError(null)
193+
}
194+
195+
if (typeof window !== 'undefined') {
196+
window.addEventListener('loginCompleted', handleLoginCompleted)
197+
window.addEventListener('logoutCompleted', handleLogoutCompleted)
198+
}
199+
200+
return () => {
201+
if (typeof window !== 'undefined') {
202+
window.removeEventListener(
203+
'loginCompleted',
204+
handleLoginCompleted,
205+
)
206+
window.removeEventListener(
207+
'logoutCompleted',
208+
handleLogoutCompleted,
209+
)
210+
}
211+
}
212+
}, [getUser])
213+
135214
const value = {
136215
user,
137216
loading,

app/src/services/api.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,11 @@ api.interceptors.response.use(
3131

3232
setCookie('access', access, { path: '/', sameSite: 'lax' })
3333

34+
// Dispatch a custom event to notify the app about token refresh
35+
if (typeof window !== 'undefined') {
36+
window.dispatchEvent(new CustomEvent('tokenRefreshed'))
37+
}
38+
3439
if (!originalRequest.headers) {
3540
originalRequest.headers = axios.AxiosHeaders.from({})
3641
}

app/src/services/auth.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,11 @@ import axios from 'axios'
33
export async function login(email: string, password: string): Promise<void> {
44
try {
55
await axios.post('/auth/login', { email, password })
6+
7+
// Dispatch a custom event to notify the app about login
8+
if (typeof window !== 'undefined') {
9+
window.dispatchEvent(new CustomEvent('loginCompleted'))
10+
}
611
} catch (_error) {
712
throw new Error('Login falhou. Verifique email e senha.')
813
}
@@ -11,6 +16,11 @@ export async function login(email: string, password: string): Promise<void> {
1116
export async function logout(): Promise<void> {
1217
try {
1318
await axios.post('/auth/logout')
19+
20+
// Dispatch a custom event to notify the app about logout
21+
if (typeof window !== 'undefined') {
22+
window.dispatchEvent(new CustomEvent('logoutCompleted'))
23+
}
1424
} catch (error) {
1525
console.error('Falha no logout:', error)
1626
}

0 commit comments

Comments
 (0)