SignIn
This commit is contained in:
parent
534c613e71
commit
6414e85a78
@ -1,184 +0,0 @@
|
|||||||
# Problème TypeScript : Block-scoped variable 'status' used before its declaration
|
|
||||||
|
|
||||||
## Contexte du projet
|
|
||||||
|
|
||||||
- **Framework** : Next.js 16.1.1 (App Router)
|
|
||||||
- **TypeScript** : Mode strict activé (`"strict": true` dans `tsconfig.json`)
|
|
||||||
- **Fichier concerné** : `components/calendar/calendar-widget.tsx`
|
|
||||||
- **Erreur** : `Type error: Block-scoped variable 'status' used before its declaration.`
|
|
||||||
|
|
||||||
## Description du problème
|
|
||||||
|
|
||||||
L'erreur TypeScript se produit à la ligne 33 du fichier `components/calendar/calendar-widget.tsx` :
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
export function CalendarWidget() {
|
|
||||||
const { data: session, status } = useSession(); // Ligne 25 - Déclaration
|
|
||||||
const [events, setEvents] = useState<Event[]>([]);
|
|
||||||
const [loading, setLoading] = useState(true);
|
|
||||||
const [error, setError] = useState<string | null>(null);
|
|
||||||
const { triggerNotification } = useWidgetNotification();
|
|
||||||
const lastEventCountRef = useRef<number>(-1);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
console.log("Calendar Widget - Session Status:", status); // Ligne 33 - ERREUR ICI
|
|
||||||
// ...
|
|
||||||
}, [session, status, triggerNotification]);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**Erreur exacte :**
|
|
||||||
```
|
|
||||||
./components/calendar/calendar-widget.tsx:33:54
|
|
||||||
Type error: Block-scoped variable 'status' used before its declaration.
|
|
||||||
```
|
|
||||||
|
|
||||||
## Constat important
|
|
||||||
|
|
||||||
Le code est **identique en structure** à `components/email.tsx` qui **compile sans erreur** :
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
// components/email.tsx - FONCTIONNE
|
|
||||||
export function Email() {
|
|
||||||
const { data: session, status } = useSession(); // Même déclaration
|
|
||||||
// ... autres hooks ...
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (status === 'authenticated') { // Même utilisation
|
|
||||||
loadAccounts();
|
|
||||||
}
|
|
||||||
}, [status]);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Ce qui a été testé
|
|
||||||
|
|
||||||
### 1. Tentatives de solutions (toutes échouées)
|
|
||||||
|
|
||||||
#### Tentative 1 : Déclaration dans le useEffect
|
|
||||||
```typescript
|
|
||||||
useEffect(() => {
|
|
||||||
const session = sessionHook.data;
|
|
||||||
const status = sessionHook.status;
|
|
||||||
// ...
|
|
||||||
}, [sessionHook.data, sessionHook.status, triggerNotification]);
|
|
||||||
```
|
|
||||||
**Résultat** : Erreur `Block-scoped variable 'sessionHook' used before its declaration`
|
|
||||||
|
|
||||||
#### Tentative 2 : Séparation de la destructuration
|
|
||||||
```typescript
|
|
||||||
const sessionResult = useSession();
|
|
||||||
const session = sessionResult.data;
|
|
||||||
const status = sessionResult.status;
|
|
||||||
```
|
|
||||||
**Résultat** : Même erreur avec `sessionResult`
|
|
||||||
|
|
||||||
#### Tentative 3 : Ordre des hooks modifié
|
|
||||||
- Déplacer `useSession()` avant les autres hooks
|
|
||||||
- Déplacer `useSession()` après les autres hooks
|
|
||||||
**Résultat** : Même erreur
|
|
||||||
|
|
||||||
#### Tentative 4 : Alignement avec email.tsx
|
|
||||||
Copie exacte de la structure de `email.tsx` qui fonctionne
|
|
||||||
**Résultat** : Même erreur
|
|
||||||
|
|
||||||
### 2. Vérifications effectuées
|
|
||||||
|
|
||||||
✅ **Pas de conflit de noms** : Aucune autre déclaration de `status` dans le fichier
|
|
||||||
✅ **Pas de cache** :
|
|
||||||
- `rm -rf .next` exécuté
|
|
||||||
- `rm -rf node_modules/.cache` exécuté
|
|
||||||
- Erreur persiste après nettoyage
|
|
||||||
|
|
||||||
✅ **Fichier sur disque vérifié** : Le code est correct (pas de `sessionResult` ou autre)
|
|
||||||
```bash
|
|
||||||
sed -n '32,45p' components/calendar/calendar-widget.tsx
|
|
||||||
# Confirme que le code est correct
|
|
||||||
```
|
|
||||||
|
|
||||||
✅ **Comparaison avec email.tsx** : Structure identique, même ordre de déclaration
|
|
||||||
|
|
||||||
✅ **Recherche dans le projet** : Aucune autre référence à `sessionResult` trouvée
|
|
||||||
|
|
||||||
### 3. État actuel du code
|
|
||||||
|
|
||||||
Le fichier `components/calendar/calendar-widget.tsx` contient :
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
"use client";
|
|
||||||
|
|
||||||
import { useState, useEffect, useRef } from "react";
|
|
||||||
import { format, isToday, isTomorrow, addDays } from "date-fns";
|
|
||||||
import { fr } from "date-fns/locale";
|
|
||||||
import { CalendarIcon, ChevronRight } from "lucide-react";
|
|
||||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
|
||||||
import { Button } from "@/components/ui/button";
|
|
||||||
import Link from "next/link";
|
|
||||||
import { useSession } from "next-auth/react";
|
|
||||||
import { useWidgetNotification } from "@/hooks/use-widget-notification";
|
|
||||||
|
|
||||||
type Event = {
|
|
||||||
id: string;
|
|
||||||
title: string;
|
|
||||||
start: Date;
|
|
||||||
end: Date;
|
|
||||||
isAllDay: boolean;
|
|
||||||
calendarId: string;
|
|
||||||
calendarName?: string;
|
|
||||||
calendarColor?: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export function CalendarWidget() {
|
|
||||||
const { data: session, status } = useSession(); // Ligne 25
|
|
||||||
const [events, setEvents] = useState<Event[]>([]);
|
|
||||||
const [loading, setLoading] = useState(true);
|
|
||||||
const [error, setError] = useState<string | null>(null);
|
|
||||||
const { triggerNotification } = useWidgetNotification();
|
|
||||||
const lastEventCountRef = useRef<number>(-1);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
console.log("Calendar Widget - Session Status:", status); // Ligne 33 - ERREUR
|
|
||||||
console.log("Calendar Widget - Session Data:", session);
|
|
||||||
|
|
||||||
if (status === "loading") {
|
|
||||||
console.log("Calendar Widget - Session is loading");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (status !== "authenticated" || !session) {
|
|
||||||
console.log("Calendar Widget - Not authenticated, skipping fetch");
|
|
||||||
setLoading(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ... reste du code ...
|
|
||||||
|
|
||||||
}, [session, status, triggerNotification]);
|
|
||||||
|
|
||||||
// ... reste du composant ...
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Hypothèses
|
|
||||||
|
|
||||||
1. **Bug TypeScript** : Problème d'analyse statique avec les hooks React dans ce contexte spécifique
|
|
||||||
2. **Incompatibilité de versions** : Possible problème entre Next.js 16.1.1 et TypeScript
|
|
||||||
3. **Analyse séquentielle** : TypeScript pourrait analyser le `useEffect` avant que la destructuration de `useSession()` soit complètement résolue
|
|
||||||
|
|
||||||
## Informations techniques
|
|
||||||
|
|
||||||
- **Next.js** : 16.1.1
|
|
||||||
- **TypeScript** : Version dans `tsconfig.json` avec `"strict": true`
|
|
||||||
- **React** : Version via Next.js 16.1.1
|
|
||||||
- **next-auth** : Utilisé pour `useSession()`
|
|
||||||
|
|
||||||
## Question
|
|
||||||
|
|
||||||
Pourquoi TypeScript signale-t-il que `status` est utilisé avant sa déclaration alors que :
|
|
||||||
1. `status` est déclaré ligne 25
|
|
||||||
2. `status` est utilisé ligne 33 (dans un `useEffect` qui est déclaré après)
|
|
||||||
3. La même structure fonctionne dans `components/email.tsx`
|
|
||||||
4. Aucun conflit de noms n'existe
|
|
||||||
5. Les caches ont été nettoyés
|
|
||||||
|
|
||||||
Quelle est la solution pour résoudre cette erreur TypeScript sans compromettre la structure du code qui fonctionne ailleurs dans le projet ?
|
|
||||||
@ -1,7 +1,7 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { signIn, useSession } from "next-auth/react";
|
import { signIn, useSession } from "next-auth/react";
|
||||||
import { useEffect, useState, useRef } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { useRouter, useSearchParams } from "next/navigation";
|
import { useRouter, useSearchParams } from "next/navigation";
|
||||||
|
|
||||||
export default function SignIn() {
|
export default function SignIn() {
|
||||||
@ -10,7 +10,6 @@ export default function SignIn() {
|
|||||||
const searchParams = useSearchParams();
|
const searchParams = useSearchParams();
|
||||||
const [initializationStatus, setInitializationStatus] = useState<string | null>(null);
|
const [initializationStatus, setInitializationStatus] = useState<string | null>(null);
|
||||||
const [isLoggingIn, setIsLoggingIn] = useState(false);
|
const [isLoggingIn, setIsLoggingIn] = useState(false);
|
||||||
const hasAttemptedLogin = useRef(false);
|
|
||||||
|
|
||||||
// Check URL parameters for logout flag
|
// Check URL parameters for logout flag
|
||||||
const logoutParam = searchParams.get('logout');
|
const logoutParam = searchParams.get('logout');
|
||||||
@ -18,7 +17,7 @@ export default function SignIn() {
|
|||||||
|
|
||||||
// Debug logging
|
// Debug logging
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
console.log('[SignIn] Status:', status, 'Session:', !!session, 'Logout redirect:', isLogoutRedirect, 'Has attempted:', hasAttemptedLogin.current);
|
console.log('[SignIn] Status:', status, 'Session:', !!session, 'Logout redirect:', isLogoutRedirect);
|
||||||
}, [status, session, isLogoutRedirect]);
|
}, [status, session, isLogoutRedirect]);
|
||||||
|
|
||||||
// Clear stale force_login_prompt cookie on mount (it should only last 5 minutes)
|
// Clear stale force_login_prompt cookie on mount (it should only last 5 minutes)
|
||||||
@ -45,32 +44,9 @@ export default function SignIn() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't auto-login if this is a logout redirect
|
// Auto-login disabled - user must click the button to connect
|
||||||
if (isLogoutRedirect) {
|
// This allows users to see and appreciate the sign-in page
|
||||||
console.log('[SignIn] Logout redirect detected, showing login button');
|
}, [status, session, router]);
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Don't auto-login if we've already attempted or are currently logging in
|
|
||||||
if (hasAttemptedLogin.current || isLoggingIn) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Don't auto-login if status is still loading
|
|
||||||
if (status === "loading") {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Auto-login for new users (SSO natural flow)
|
|
||||||
if (status === "unauthenticated") {
|
|
||||||
console.log('[SignIn] Status is unauthenticated, triggering Keycloak login');
|
|
||||||
hasAttemptedLogin.current = true;
|
|
||||||
setIsLoggingIn(true);
|
|
||||||
|
|
||||||
// Trigger Keycloak sign-in immediately
|
|
||||||
signIn("keycloak", { callbackUrl: "/" });
|
|
||||||
}
|
|
||||||
}, [status, session, router, isLogoutRedirect, isLoggingIn]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (session?.user) {
|
if (session?.user) {
|
||||||
@ -128,23 +104,22 @@ export default function SignIn() {
|
|||||||
: initializationStatus === "failed"
|
: initializationStatus === "failed"
|
||||||
? "Échec de l'initialisation. Veuillez réessayer."
|
? "Échec de l'initialisation. Veuillez réessayer."
|
||||||
: isLoggingIn
|
: isLoggingIn
|
||||||
? "Connexion à Keycloak en cours..."
|
? "Connexion en cours..."
|
||||||
: status === "loading"
|
: status === "loading"
|
||||||
? "Chargement..."
|
? "Chargement..."
|
||||||
: "Redirection vers la page de connexion..."}
|
: "Bienvenue"}
|
||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
{/* Show login button after logout OR if auto-login failed */}
|
{/* Show login button when user is not authenticated and not currently logging in */}
|
||||||
{(isLogoutRedirect || (status === "unauthenticated" && hasAttemptedLogin.current && !isLoggingIn)) && (
|
{status === "unauthenticated" && !isLoggingIn && (
|
||||||
<div className="mt-4 text-center">
|
<div className="mt-8 text-center">
|
||||||
<button
|
<button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
console.log('[SignIn] Manual login button clicked');
|
console.log('[SignIn] Login button clicked');
|
||||||
hasAttemptedLogin.current = false;
|
|
||||||
setIsLoggingIn(true);
|
setIsLoggingIn(true);
|
||||||
signIn("keycloak", { callbackUrl: "/" });
|
signIn("keycloak", { callbackUrl: "/" });
|
||||||
}}
|
}}
|
||||||
className="mt-4 px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors"
|
className="px-6 py-3 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors font-semibold text-lg shadow-lg hover:shadow-xl"
|
||||||
>
|
>
|
||||||
Se connecter
|
Se connecter
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user