diff --git a/app/api/auth/[...nextauth]/route.ts b/app/api/auth/[...nextauth]/route.ts index 7b25923a..5cc81efb 100644 --- a/app/api/auth/[...nextauth]/route.ts +++ b/app/api/auth/[...nextauth]/route.ts @@ -42,7 +42,7 @@ export const authOptions: NextAuthOptions = { clientSecret: getRequiredEnvVar("KEYCLOAK_CLIENT_SECRET"), issuer: getRequiredEnvVar("KEYCLOAK_ISSUER"), profile(profile) { - console.log("Keycloak profile:", profile); + console.log("Keycloak profile received for user:", profile.preferred_username); return { id: profile.sub, name: profile.name ?? profile.preferred_username, @@ -57,9 +57,8 @@ export const authOptions: NextAuthOptions = { ], callbacks: { async jwt({ token, account, profile }) { - console.log("JWT callback - token:", token); - console.log("JWT callback - account:", account); - console.log("JWT callback - profile:", profile); + // Log only non-sensitive information + console.log("JWT callback - processing token for user:", token.sub); if (account && profile) { token.accessToken = account.access_token; @@ -82,7 +81,7 @@ export const authOptions: NextAuthOptions = { const clientId = getRequiredEnvVar("KEYCLOAK_CLIENT_ID"); const clientSecret = getRequiredEnvVar("KEYCLOAK_CLIENT_SECRET"); - console.log("Attempting to refresh token..."); + console.log("Attempting to refresh token for user:", token.sub); const response = await fetch( `${process.env.KEYCLOAK_BASE_URL}/realms/${process.env.KEYCLOAK_REALM}/protocol/openid-connect/token`, { @@ -100,10 +99,9 @@ export const authOptions: NextAuthOptions = { ); const tokens = await response.json(); - console.log("Token refresh response:", tokens); if (!response.ok) { - console.error("Token refresh failed:", tokens); + console.error("Token refresh failed for user:", token.sub); throw new Error("RefreshAccessTokenError"); } @@ -114,7 +112,7 @@ export const authOptions: NextAuthOptions = { accessTokenExpires: Date.now() + tokens.expires_in * 1000, }; } catch (error) { - console.error("Error refreshing token:", error); + console.error("Error refreshing token for user:", token.sub); // Return token with error flag - this will trigger a redirect to sign-in return { @@ -125,11 +123,10 @@ export const authOptions: NextAuthOptions = { }, async session({ session, token }) { - console.log("Session callback - session:", session); - console.log("Session callback - token:", token); + console.log("Session callback - processing session for user:", token.sub); if (token.error) { - console.error("Token error detected:", token.error); + console.error("Token error detected for user:", token.sub); // Force sign out if there was a refresh error throw new Error("RefreshAccessTokenError"); } @@ -148,7 +145,7 @@ export const authOptions: NextAuthOptions = { }, events: { async signOut({ token }) { - console.log("Sign out event - token:", token); + console.log("Sign out event - processing logout for user:", token.sub); if (token.refreshToken) { try { await fetch( @@ -166,12 +163,12 @@ export const authOptions: NextAuthOptions = { } ); } catch (error) { - console.error("Error during logout:", error); + console.error("Error during logout for user:", token.sub); } } }, }, - debug: true, // Enable debug logging + debug: process.env.NODE_ENV === 'development', // Only enable debug logging in development }; const handler = NextAuth(authOptions); diff --git a/components/flow.tsx b/components/flow.tsx index e7b18f96..29acf531 100644 --- a/components/flow.tsx +++ b/components/flow.tsx @@ -16,8 +16,13 @@ interface Task { details: string | null; } +interface ProjectTasks { + projectName: string; + tasks: Task[]; +} + export function Flow() { - const [tasks, setTasks] = useState([]); + const [projectTasks, setProjectTasks] = useState([]); const [error, setError] = useState(null); const [loading, setLoading] = useState(true); const [refreshing, setRefreshing] = useState(false); @@ -44,9 +49,35 @@ export function Flow() { const data = await response.json(); if (data.tasks && Array.isArray(data.tasks)) { - setTasks(data.tasks); + // Group tasks by project + const groupedTasks = data.tasks.reduce((acc: { [key: string]: Task[] }, task: Task) => { + if (!acc[task.projectName]) { + acc[task.projectName] = []; + } + acc[task.projectName].push(task); + return acc; + }, {}); + + // Convert to array and sort tasks by due date within each project + const sortedProjectTasks = Object.entries(groupedTasks).map(([projectName, tasks]) => ({ + projectName, + tasks: tasks.sort((a, b) => { + if (!a.dueDate) return 1; + if (!b.dueDate) return -1; + return new Date(a.dueDate).getTime() - new Date(b.dueDate).getTime(); + }).slice(0, 6) // Limit to 6 tasks per project + })); + + // Sort projects by earliest due date + sortedProjectTasks.sort((a, b) => { + const aEarliest = a.tasks[0]?.dueDate ? new Date(a.tasks[0].dueDate).getTime() : Infinity; + const bEarliest = b.tasks[0]?.dueDate ? new Date(b.tasks[0].dueDate).getTime() : Infinity; + return aEarliest - bEarliest; + }); + + setProjectTasks(sortedProjectTasks); } else { - setTasks([]); + setProjectTasks([]); } setError(null); } catch (err) { @@ -67,6 +98,16 @@ export function Flow() { }; }, []); + const formatDate = (dateString: string | null) => { + if (!dateString) return 'No due date'; + const date = new Date(dateString); + return date.toLocaleDateString('fr-FR', { + day: 'numeric', + month: 'short', + year: 'numeric' + }); + }; + return ( @@ -88,26 +129,33 @@ export function Flow() { ) : error ? (
{error}
- ) : tasks.length === 0 ? ( + ) : projectTasks.length === 0 ? (
No tasks found
) : (
- {tasks.map((task) => ( -
-

{task.projectName}

+ {projectTasks.map((project) => ( +
+

{project.projectName}

-
- - {task.headline} - - - {task.status} - -
+ {project.tasks.map((task) => ( +
+ + {task.headline} + +
+ + {task.status} + + + {formatDate(task.dueDate)} + +
+
+ ))}
))}