diff --git a/frontends/nextjs/src/app/_components/auth-provider/use-auth.ts b/frontends/nextjs/src/app/_components/auth-provider/use-auth.ts index 03377f642..d0141b3bf 100644 --- a/frontends/nextjs/src/app/_components/auth-provider/use-auth.ts +++ b/frontends/nextjs/src/app/_components/auth-provider/use-auth.ts @@ -1,7 +1,10 @@ /** - * useAuth hook (stub) + * useAuth hook */ +import { useEffect, useState } from 'react' +import { getSessionUser } from '@/lib/routing' + export interface AuthUser { id: string username: string @@ -17,10 +20,48 @@ export interface AuthState { } export function useAuth(): AuthState { - // TODO: Implement useAuth hook - return { + const [state, setState] = useState({ user: null, - isLoading: false, + isLoading: true, isAuthenticated: false, - } + }) + + useEffect(() => { + const loadUser = async () => { + try { + const sessionUser = await getSessionUser() + if (sessionUser.user) { + const user = sessionUser.user as any // Type assertion for now + setState({ + user: { + id: user.id, + username: user.username, + email: user.email, + role: user.role, + level: user.level || 0, + }, + isLoading: false, + isAuthenticated: true, + }) + } else { + setState({ + user: null, + isLoading: false, + isAuthenticated: false, + }) + } + } catch (error) { + console.error('Error loading user:', error) + setState({ + user: null, + isLoading: false, + isAuthenticated: false, + }) + } + } + + loadUser() + }, []) + + return state } diff --git a/frontends/nextjs/src/hooks/useAutoRefresh.ts b/frontends/nextjs/src/hooks/useAutoRefresh.ts index 097774e10..fa0f8e3b9 100644 --- a/frontends/nextjs/src/hooks/useAutoRefresh.ts +++ b/frontends/nextjs/src/hooks/useAutoRefresh.ts @@ -1,12 +1,32 @@ /** - * useAutoRefresh hook (stub) + * useAutoRefresh hook */ +import { useEffect, useRef } from 'react' + export interface AutoRefreshOptions { interval?: number enabled?: boolean } -export function useAutoRefresh(_callback: () => void, _options?: AutoRefreshOptions): void { - // TODO: Implement auto refresh +export function useAutoRefresh(callback: () => void, options?: AutoRefreshOptions): void { + const { interval = 30000, enabled = true } = options || {} + const intervalRef = useRef(null) + + useEffect(() => { + if (enabled) { + intervalRef.current = setInterval(callback, interval) + } else { + if (intervalRef.current) { + clearInterval(intervalRef.current) + intervalRef.current = null + } + } + + return () => { + if (intervalRef.current) { + clearInterval(intervalRef.current) + } + } + }, [callback, interval, enabled]) } diff --git a/frontends/nextjs/src/hooks/useFileTree.ts b/frontends/nextjs/src/hooks/useFileTree.ts index e01531c73..bf46d292a 100644 --- a/frontends/nextjs/src/hooks/useFileTree.ts +++ b/frontends/nextjs/src/hooks/useFileTree.ts @@ -1,7 +1,11 @@ /** - * useFileTree hook (stub) + * useFileTree hook */ +import { useState, useEffect, useCallback } from 'react' +import { promises as fs } from 'fs' +import path from 'path' + export interface FileNode { name: string path: string @@ -9,12 +13,56 @@ export interface FileNode { children?: FileNode[] } -export function useFileTree(_rootPath?: string) { - // TODO: Implement useFileTree +export function useFileTree(rootPath = '.') { + const [tree, setTree] = useState(null) + const [loading, setLoading] = useState(false) + const [error, setError] = useState(null) + + const buildTree = useCallback(async (dirPath: string, name: string): Promise => { + const fullPath = path.resolve(rootPath, dirPath) + const stats = await fs.stat(fullPath) + + const node: FileNode = { + name, + path: dirPath, + type: stats.isDirectory() ? 'directory' : 'file', + } + + if (stats.isDirectory()) { + try { + const entries = await fs.readdir(fullPath) + node.children = await Promise.all( + entries.map(entry => buildTree(path.join(dirPath, entry), entry)) + ) + } catch { + // Ignore errors reading subdirectories + } + } + + return node + }, [rootPath]) + + const refresh = useCallback(async () => { + setLoading(true) + setError(null) + try { + const rootTree = await buildTree('.', path.basename(rootPath)) + setTree(rootTree) + } catch (err) { + setError(err as Error) + } finally { + setLoading(false) + } + }, [buildTree, rootPath]) + + useEffect(() => { + refresh() + }, [refresh]) + return { - tree: null as FileNode | null, - loading: false, - error: null as Error | null, - refresh: () => {}, + tree, + loading, + error, + refresh, } } diff --git a/frontends/nextjs/src/hooks/useGitHubFetcher.ts b/frontends/nextjs/src/hooks/useGitHubFetcher.ts index 013c417ba..6530caab4 100644 --- a/frontends/nextjs/src/hooks/useGitHubFetcher.ts +++ b/frontends/nextjs/src/hooks/useGitHubFetcher.ts @@ -1,7 +1,9 @@ /** - * useGitHubFetcher hook (stub) + * useGitHubFetcher hook */ +import { useState, useEffect, useCallback } from 'react' + export interface WorkflowRun { id: number name: string @@ -11,11 +13,33 @@ export interface WorkflowRun { } export function useGitHubFetcher() { - // TODO: Implement useGitHubFetcher + const [runs, setRuns] = useState([]) + const [loading, setLoading] = useState(false) + const [error, setError] = useState(null) + + const refetch = useCallback(async () => { + setLoading(true) + setError(null) + try { + const { listWorkflowRuns } = await import('@/lib/github/workflows/listing/list-workflow-runs') + const workflowRuns = await listWorkflowRuns() + setRuns(workflowRuns) + } catch (err) { + setError(err as Error) + } finally { + setLoading(false) + } + }, []) + + useEffect(() => { + refetch() + }, [refetch]) + return { - runs: [] as WorkflowRun[], - loading: false, - error: null as Error | null, - refetch: () => {}, + runs, + loading, + error, + refetch, } } +}