From bb3a93bfdc04a6dcc461a9c41b61a002fdf8ce39 Mon Sep 17 00:00:00 2001 From: Yaojia Wang Date: Wed, 5 Nov 2025 19:47:33 +0100 Subject: [PATCH] refactor(frontend): Replace console.log with logger utility - Sprint 3 Story 1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace all console.log/warn/error statements with unified logger utility. Changes: - Replaced console in lib/hooks/use-stories.ts - Replaced console in lib/signalr/SignalRContext.tsx - Replaced console in lib/hooks/useProjectHub.ts - Replaced console in lib/hooks/use-tasks.ts - Replaced console in lib/hooks/useNotificationHub.ts - Replaced console in lib/hooks/use-projects.ts - Replaced console in app/(dashboard)/projects/[id]/kanban/page.tsx Logger respects NODE_ENV (debug disabled in production). ๐Ÿค– Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- app/(dashboard)/projects/[id]/kanban/page.tsx | 9 ++-- lib/hooks/use-projects.ts | 7 +-- lib/hooks/use-stories.ts | 25 ++++----- lib/hooks/use-tasks.ts | 17 ++++--- lib/hooks/useNotificationHub.ts | 7 +-- lib/hooks/useProjectHub.ts | 51 ++++++++++--------- lib/signalr/SignalRContext.tsx | 7 +-- 7 files changed, 65 insertions(+), 58 deletions(-) diff --git a/app/(dashboard)/projects/[id]/kanban/page.tsx b/app/(dashboard)/projects/[id]/kanban/page.tsx index 9f26cdb..dee930e 100644 --- a/app/(dashboard)/projects/[id]/kanban/page.tsx +++ b/app/(dashboard)/projects/[id]/kanban/page.tsx @@ -20,6 +20,7 @@ import { KanbanColumn } from '@/components/features/kanban/KanbanColumn'; import { StoryCard } from '@/components/features/kanban/StoryCard'; import { CreateStoryDialog } from '@/components/features/stories/CreateStoryDialog'; import type { Story, WorkItemStatus } from '@/types/project'; +import { logger } from '@/lib/utils/logger'; const COLUMNS = [ { id: 'Backlog', title: 'Backlog', color: 'bg-gray-100' }, @@ -50,15 +51,15 @@ export default function KanbanPage() { { // Story events (3 events) 'StoryCreated': (event: any) => { - console.log('[Kanban] Story created:', event); + logger.debug('[Kanban] Story created:', event); queryClient.invalidateQueries({ queryKey: ['project-stories', projectId] }); }, 'StoryUpdated': (event: any) => { - console.log('[Kanban] Story updated:', event); + logger.debug('[Kanban] Story updated:', event); queryClient.invalidateQueries({ queryKey: ['project-stories', projectId] }); }, 'StoryDeleted': (event: any) => { - console.log('[Kanban] Story deleted:', event); + logger.debug('[Kanban] Story deleted:', event); queryClient.invalidateQueries({ queryKey: ['project-stories', projectId] }); }, }, @@ -97,7 +98,7 @@ export default function KanbanPage() { const story = stories.find((s) => s.id === active.id); if (story && story.status !== newStatus) { - console.log(`[Kanban] Changing story ${story.id} status to ${newStatus}`); + logger.debug(`[Kanban] Changing story ${story.id} status to ${newStatus}`); changeStatusMutation.mutate({ id: story.id, status: newStatus }); } }; diff --git a/lib/hooks/use-projects.ts b/lib/hooks/use-projects.ts index 64f348e..906f90c 100644 --- a/lib/hooks/use-projects.ts +++ b/lib/hooks/use-projects.ts @@ -1,18 +1,19 @@ import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import { projectsApi } from '@/lib/api/projects'; import type { Project, CreateProjectDto, UpdateProjectDto } from '@/types/project'; +import { logger } from '@/lib/utils/logger'; export function useProjects(page = 1, pageSize = 20) { return useQuery({ queryKey: ['projects', page, pageSize], queryFn: async () => { - console.log('[useProjects] Fetching projects...', { page, pageSize }); + logger.debug('[useProjects] Fetching projects...', { page, pageSize }); try { const result = await projectsApi.getAll(page, pageSize); - console.log('[useProjects] Fetch successful:', result); + logger.debug('[useProjects] Fetch successful:', result); return result; } catch (error) { - console.error('[useProjects] Fetch failed:', error); + logger.error('[useProjects] Fetch failed:', error); throw error; } }, diff --git a/lib/hooks/use-stories.ts b/lib/hooks/use-stories.ts index 2a6446d..96e06a4 100644 --- a/lib/hooks/use-stories.ts +++ b/lib/hooks/use-stories.ts @@ -3,19 +3,20 @@ import { storiesApi } from '@/lib/api/pm'; import { epicsApi } from '@/lib/api/pm'; import type { Story, CreateStoryDto, UpdateStoryDto, WorkItemStatus } from '@/types/project'; import { toast } from 'sonner'; +import { logger } from '@/lib/utils/logger'; // ==================== Query Hooks ==================== export function useStories(epicId?: string) { return useQuery({ queryKey: ['stories', epicId], queryFn: async () => { - console.log('[useStories] Fetching stories...', { epicId }); + logger.debug('[useStories] Fetching stories...', { epicId }); try { const result = await storiesApi.list(epicId); - console.log('[useStories] Fetch successful:', result); + logger.debug('[useStories] Fetch successful:', result); return result; } catch (error) { - console.error('[useStories] Fetch failed:', error); + logger.error('[useStories] Fetch failed:', error); throw error; } }, @@ -33,12 +34,12 @@ export function useProjectStories(projectId?: string) { throw new Error('projectId is required'); } - console.log('[useProjectStories] Fetching all stories for project...', { projectId }); + logger.debug('[useProjectStories] Fetching all stories for project...', { projectId }); try { // First fetch all epics for the project const epics = await epicsApi.list(projectId); - console.log('[useProjectStories] Epics fetched:', epics.length); + logger.debug('[useProjectStories] Epics fetched:', epics.length); // Then fetch stories for each epic const storiesPromises = epics.map((epic) => storiesApi.list(epic.id)); @@ -46,11 +47,11 @@ export function useProjectStories(projectId?: string) { // Flatten the array of arrays into a single array const allStories = storiesArrays.flat(); - console.log('[useProjectStories] Total stories fetched:', allStories.length); + logger.debug('[useProjectStories] Total stories fetched:', allStories.length); return allStories; } catch (error) { - console.error('[useProjectStories] Fetch failed:', error); + logger.error('[useProjectStories] Fetch failed:', error); throw error; } }, @@ -84,7 +85,7 @@ export function useCreateStory() { toast.success('Story created successfully!'); }, onError: (error: any) => { - console.error('[useCreateStory] Error:', error); + logger.error('[useCreateStory] Error:', error); toast.error(error.response?.data?.detail || 'Failed to create story'); }, }); @@ -109,7 +110,7 @@ export function useUpdateStory() { return { previousStory }; }, onError: (error: any, variables, context) => { - console.error('[useUpdateStory] Error:', error); + logger.error('[useUpdateStory] Error:', error); if (context?.previousStory) { queryClient.setQueryData(['stories', variables.id], context.previousStory); @@ -138,7 +139,7 @@ export function useDeleteStory() { toast.success('Story deleted successfully!'); }, onError: (error: any) => { - console.error('[useDeleteStory] Error:', error); + logger.error('[useDeleteStory] Error:', error); toast.error(error.response?.data?.detail || 'Failed to delete story'); }, }); @@ -163,7 +164,7 @@ export function useChangeStoryStatus() { return { previousStory }; }, onError: (error: any, variables, context) => { - console.error('[useChangeStoryStatus] Error:', error); + logger.error('[useChangeStoryStatus] Error:', error); if (context?.previousStory) { queryClient.setQueryData(['stories', variables.id], context.previousStory); @@ -193,7 +194,7 @@ export function useAssignStory() { toast.success('Story assigned successfully!'); }, onError: (error: any) => { - console.error('[useAssignStory] Error:', error); + logger.error('[useAssignStory] Error:', error); toast.error(error.response?.data?.detail || 'Failed to assign story'); }, }); diff --git a/lib/hooks/use-tasks.ts b/lib/hooks/use-tasks.ts index 4c075b0..979cc20 100644 --- a/lib/hooks/use-tasks.ts +++ b/lib/hooks/use-tasks.ts @@ -2,19 +2,20 @@ import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import { tasksApi } from '@/lib/api/pm'; import type { Task, CreateTaskDto, UpdateTaskDto, WorkItemStatus } from '@/types/project'; import { toast } from 'sonner'; +import { logger } from '@/lib/utils/logger'; // ==================== Query Hooks ==================== export function useTasks(storyId?: string) { return useQuery({ queryKey: ['tasks', storyId], queryFn: async () => { - console.log('[useTasks] Fetching tasks...', { storyId }); + logger.debug('[useTasks] Fetching tasks...', { storyId }); try { const result = await tasksApi.list(storyId); - console.log('[useTasks] Fetch successful:', result); + logger.debug('[useTasks] Fetch successful:', result); return result; } catch (error) { - console.error('[useTasks] Fetch failed:', error); + logger.error('[useTasks] Fetch failed:', error); throw error; } }, @@ -47,7 +48,7 @@ export function useCreateTask() { toast.success('Task created successfully!'); }, onError: (error: any) => { - console.error('[useCreateTask] Error:', error); + logger.error('[useCreateTask] Error:', error); toast.error(error.response?.data?.detail || 'Failed to create task'); }, }); @@ -72,7 +73,7 @@ export function useUpdateTask() { return { previousTask }; }, onError: (error: any, variables, context) => { - console.error('[useUpdateTask] Error:', error); + logger.error('[useUpdateTask] Error:', error); if (context?.previousTask) { queryClient.setQueryData(['tasks', variables.id], context.previousTask); @@ -101,7 +102,7 @@ export function useDeleteTask() { toast.success('Task deleted successfully!'); }, onError: (error: any) => { - console.error('[useDeleteTask] Error:', error); + logger.error('[useDeleteTask] Error:', error); toast.error(error.response?.data?.detail || 'Failed to delete task'); }, }); @@ -126,7 +127,7 @@ export function useChangeTaskStatus() { return { previousTask }; }, onError: (error: any, variables, context) => { - console.error('[useChangeTaskStatus] Error:', error); + logger.error('[useChangeTaskStatus] Error:', error); if (context?.previousTask) { queryClient.setQueryData(['tasks', variables.id], context.previousTask); @@ -156,7 +157,7 @@ export function useAssignTask() { toast.success('Task assigned successfully!'); }, onError: (error: any) => { - console.error('[useAssignTask] Error:', error); + logger.error('[useAssignTask] Error:', error); toast.error(error.response?.data?.detail || 'Failed to assign task'); }, }); diff --git a/lib/hooks/useNotificationHub.ts b/lib/hooks/useNotificationHub.ts index 639c9ad..c8e5b58 100644 --- a/lib/hooks/useNotificationHub.ts +++ b/lib/hooks/useNotificationHub.ts @@ -4,6 +4,7 @@ import { useEffect, useState, useCallback, useRef } from 'react'; import { SignalRConnectionManager } from '@/lib/signalr/ConnectionManager'; import { SIGNALR_CONFIG } from '@/lib/signalr/config'; import { useAuthStore } from '@/stores/authStore'; +import { logger } from '@/lib/utils/logger'; export interface Notification { message: string; @@ -32,14 +33,14 @@ export function useNotificationHub() { // ็›‘ๅฌ้€š็Ÿฅไบ‹ไปถ manager.on('Notification', (notification: Notification) => { - console.log('[NotificationHub] Received notification:', notification); + logger.debug('[NotificationHub] Received notification:', notification); setNotifications((prev) => [notification, ...prev].slice(0, 50)); // ไฟ็•™ๆœ€่ฟ‘ 50 ๆก }); manager.on( 'NotificationRead', (data: { NotificationId: string; ReadAt: string }) => { - console.log('[NotificationHub] Notification read:', data); + logger.debug('[NotificationHub] Notification read:', data); } ); @@ -58,7 +59,7 @@ export function useNotificationHub() { try { await managerRef.current.invoke('MarkAsRead', notificationId); } catch (error) { - console.error( + logger.error( '[NotificationHub] Error marking notification as read:', error ); diff --git a/lib/hooks/useProjectHub.ts b/lib/hooks/useProjectHub.ts index 35e6531..a82512a 100644 --- a/lib/hooks/useProjectHub.ts +++ b/lib/hooks/useProjectHub.ts @@ -5,6 +5,7 @@ import { SignalRConnectionManager } from '@/lib/signalr/ConnectionManager'; import { SIGNALR_CONFIG } from '@/lib/signalr/config'; import { useAuthStore } from '@/stores/authStore'; import type { ProjectHubEventCallbacks } from '@/lib/signalr/types'; +import { logger } from '@/lib/utils/logger'; // Re-export for backward compatibility interface UseProjectHubOptions extends ProjectHubEventCallbacks {} @@ -30,17 +31,17 @@ export function useProjectHub(projectId?: string, options?: UseProjectHubOptions // PROJECT EVENTS (3) // ============================================ manager.on('ProjectCreated', (data: any) => { - console.log('[ProjectHub] Project created:', data); + logger.debug('[ProjectHub] Project created:', data); options?.onProjectCreated?.(data); }); manager.on('ProjectUpdated', (data: any) => { - console.log('[ProjectHub] Project updated:', data); + logger.debug('[ProjectHub] Project updated:', data); options?.onProjectUpdated?.(data); }); manager.on('ProjectArchived', (data: any) => { - console.log('[ProjectHub] Project archived:', data); + logger.debug('[ProjectHub] Project archived:', data); options?.onProjectArchived?.(data); }); @@ -48,17 +49,17 @@ export function useProjectHub(projectId?: string, options?: UseProjectHubOptions // EPIC EVENTS (3) // ============================================ manager.on('EpicCreated', (data: any) => { - console.log('[ProjectHub] Epic created:', data); + logger.debug('[ProjectHub] Epic created:', data); options?.onEpicCreated?.(data); }); manager.on('EpicUpdated', (data: any) => { - console.log('[ProjectHub] Epic updated:', data); + logger.debug('[ProjectHub] Epic updated:', data); options?.onEpicUpdated?.(data); }); manager.on('EpicDeleted', (data: any) => { - console.log('[ProjectHub] Epic deleted:', data); + logger.debug('[ProjectHub] Epic deleted:', data); options?.onEpicDeleted?.(data); }); @@ -66,17 +67,17 @@ export function useProjectHub(projectId?: string, options?: UseProjectHubOptions // STORY EVENTS (3) // ============================================ manager.on('StoryCreated', (data: any) => { - console.log('[ProjectHub] Story created:', data); + logger.debug('[ProjectHub] Story created:', data); options?.onStoryCreated?.(data); }); manager.on('StoryUpdated', (data: any) => { - console.log('[ProjectHub] Story updated:', data); + logger.debug('[ProjectHub] Story updated:', data); options?.onStoryUpdated?.(data); }); manager.on('StoryDeleted', (data: any) => { - console.log('[ProjectHub] Story deleted:', data); + logger.debug('[ProjectHub] Story deleted:', data); options?.onStoryDeleted?.(data); }); @@ -84,22 +85,22 @@ export function useProjectHub(projectId?: string, options?: UseProjectHubOptions // TASK EVENTS (4) // ============================================ manager.on('TaskCreated', (data: any) => { - console.log('[ProjectHub] Task created:', data); + logger.debug('[ProjectHub] Task created:', data); options?.onTaskCreated?.(data); }); manager.on('TaskUpdated', (data: any) => { - console.log('[ProjectHub] Task updated:', data); + logger.debug('[ProjectHub] Task updated:', data); options?.onTaskUpdated?.(data); }); manager.on('TaskDeleted', (data: any) => { - console.log('[ProjectHub] Task deleted:', data); + logger.debug('[ProjectHub] Task deleted:', data); options?.onTaskDeleted?.(data); }); manager.on('TaskAssigned', (data: any) => { - console.log('[ProjectHub] Task assigned:', data); + logger.debug('[ProjectHub] Task assigned:', data); options?.onTaskAssigned?.(data); }); @@ -107,22 +108,22 @@ export function useProjectHub(projectId?: string, options?: UseProjectHubOptions // LEGACY ISSUE EVENTS (Backward Compatibility) // ============================================ manager.on('IssueCreated', (data: any) => { - console.log('[ProjectHub] Issue created:', data); + logger.debug('[ProjectHub] Issue created:', data); options?.onIssueCreated?.(data); }); manager.on('IssueUpdated', (data: any) => { - console.log('[ProjectHub] Issue updated:', data); + logger.debug('[ProjectHub] Issue updated:', data); options?.onIssueUpdated?.(data); }); manager.on('IssueDeleted', (data: any) => { - console.log('[ProjectHub] Issue deleted:', data); + logger.debug('[ProjectHub] Issue deleted:', data); options?.onIssueDeleted?.(data); }); manager.on('IssueStatusChanged', (data: any) => { - console.log('[ProjectHub] Issue status changed:', data); + logger.debug('[ProjectHub] Issue status changed:', data); options?.onIssueStatusChanged?.(data); }); @@ -130,17 +131,17 @@ export function useProjectHub(projectId?: string, options?: UseProjectHubOptions // USER COLLABORATION EVENTS // ============================================ manager.on('UserJoinedProject', (data: any) => { - console.log('[ProjectHub] User joined:', data); + logger.debug('[ProjectHub] User joined:', data); options?.onUserJoinedProject?.(data); }); manager.on('UserLeftProject', (data: any) => { - console.log('[ProjectHub] User left:', data); + logger.debug('[ProjectHub] User left:', data); options?.onUserLeftProject?.(data); }); manager.on('TypingIndicator', (data: any) => { - console.log('[ProjectHub] Typing indicator:', data); + logger.debug('[ProjectHub] Typing indicator:', data); options?.onTypingIndicator?.(data); }); @@ -158,9 +159,9 @@ export function useProjectHub(projectId?: string, options?: UseProjectHubOptions try { await managerRef.current.invoke('JoinProject', projectId); - console.log(`[ProjectHub] Joined project ${projectId}`); + logger.debug(`[ProjectHub] Joined project ${projectId}`); } catch (error) { - console.error('[ProjectHub] Error joining project:', error); + logger.error('[ProjectHub] Error joining project:', error); } }, []); @@ -170,9 +171,9 @@ export function useProjectHub(projectId?: string, options?: UseProjectHubOptions try { await managerRef.current.invoke('LeaveProject', projectId); - console.log(`[ProjectHub] Left project ${projectId}`); + logger.debug(`[ProjectHub] Left project ${projectId}`); } catch (error) { - console.error('[ProjectHub] Error leaving project:', error); + logger.error('[ProjectHub] Error leaving project:', error); } }, []); @@ -189,7 +190,7 @@ export function useProjectHub(projectId?: string, options?: UseProjectHubOptions isTyping ); } catch (error) { - console.error('[ProjectHub] Error sending typing indicator:', error); + logger.error('[ProjectHub] Error sending typing indicator:', error); } }, [] diff --git a/lib/signalr/SignalRContext.tsx b/lib/signalr/SignalRContext.tsx index c5d8704..fa10bc3 100644 --- a/lib/signalr/SignalRContext.tsx +++ b/lib/signalr/SignalRContext.tsx @@ -6,6 +6,7 @@ import { SignalRConnectionManager, ConnectionState } from './ConnectionManager'; import { SIGNALR_CONFIG } from './config'; import { useAuthStore } from '@/stores/authStore'; import { toast } from 'sonner'; +import { logger } from '@/lib/utils/logger'; // ============================================ // TYPE DEFINITIONS @@ -110,12 +111,12 @@ export function SignalRProvider({ const connect = useCallback(async () => { if (!isAuthenticated) { - console.warn('[SignalRContext] Cannot connect: user not authenticated'); + logger.warn('[SignalRContext] Cannot connect: user not authenticated'); return; } if (managerRef.current?.state === 'connected') { - console.log('[SignalRContext] Already connected'); + logger.debug('[SignalRContext] Already connected'); return; } @@ -148,7 +149,7 @@ export function SignalRProvider({ try { await manager.start(); } catch (error) { - console.error('[SignalRContext] Connection error:', error); + logger.error('[SignalRContext] Connection error:', error); if (showToasts) { toast.error('Failed to connect to real-time updates'); }