diff --git a/worklenz-backend/worklenz-email-templates/release-note-template.html b/worklenz-backend/worklenz-email-templates/release-note-template.html index 592917bf..4f0e2a45 100644 --- a/worklenz-backend/worklenz-email-templates/release-note-template.html +++ b/worklenz-backend/worklenz-email-templates/release-note-template.html @@ -2,31 +2,35 @@ - + Worklenz 2.1.0 Release + - - - - - - - - -
-

- Click here to unsubscribe and manage your email preferences. -

+ + + + + +
+ + + + + + + + + + +
+ + Worklenz Light Logo + + +
+
+

🚀 New Tasks List & Kanban Board

+
    +
  • Performance optimized for faster loading
  • +
  • Redesigned UI for clarity and speed
  • +
  • Advanced filters for easier task management
  • +
+ New Task List + New Kanban Board +
+
+

📁 Group View in Projects List

+
    +
  • Toggle between list and group view
  • +
  • Group projects by client or category
  • +
  • Improved navigation and organization
  • +
+ Project List Group View +
+
+

🌐 New Language Support

+ Deutsch (DE) + Shqip (ALB) +

Worklenz is now available in German and Albanian!

+
+
+

🛠️ Bug Fixes & UI Improvements

+
    +
  • General bug fixes
  • +
  • UI/UX enhancements for a smoother experience
  • +
  • Performance improvements across the platform
  • +
+
+ +
+
+

+ Click here to unsubscribe and + manage your email preferences. +

+
+
- + \ No newline at end of file diff --git a/worklenz-frontend/index.html b/worklenz-frontend/index.html index 1a945264..faeccff7 100644 --- a/worklenz-frontend/index.html +++ b/worklenz-frontend/index.html @@ -45,7 +45,7 @@ // Determine which tracking ID to use based on the environment const isProduction = window.location.hostname === 'app.worklenz.com'; - const trackingId = isProduction ? 'G-XXXXXXXXXX' : 'G-3LM2HGWEXG'; // Open source tracking ID + const trackingId = isProduction ? 'G-7KSRKQ1397' : 'G-3LM2HGWEXG'; // Open source tracking ID // Load the Google Analytics script const script = document.createElement('script'); diff --git a/worklenz-frontend/src/pages/home/home-page.tsx b/worklenz-frontend/src/pages/home/home-page.tsx index 6186fbcb..72d06bf6 100644 --- a/worklenz-frontend/src/pages/home/home-page.tsx +++ b/worklenz-frontend/src/pages/home/home-page.tsx @@ -26,7 +26,7 @@ const TASK_LIST_MIN_WIDTH = 500; const SIDEBAR_MAX_WIDTH = 400; // Lazy load heavy components -const TaskDrawer = React.lazy(() => import('@components/task-drawer/task-drawer')); +const TaskDrawer = React.lazy(() => import('@/components/task-drawer/task-drawer')); const HomePage = memo(() => { const dispatch = useAppDispatch(); @@ -35,6 +35,19 @@ const HomePage = memo(() => { useDocumentTitle('Home'); + // Preload TaskDrawer component to prevent dynamic import failures + useEffect(() => { + const preloadTaskDrawer = async () => { + try { + await import('@/components/task-drawer/task-drawer'); + } catch (error) { + console.warn('Failed to preload TaskDrawer:', error); + } + }; + + preloadTaskDrawer(); + }, []); + // Memoize fetch function to prevent recreation on every render const fetchLookups = useCallback(async () => { const fetchPromises = [ @@ -113,9 +126,15 @@ const HomePage = memo(() => { {MainContent} - {/* Use Suspense for lazy-loaded components */} - - {createPortal(, document.body, 'home-task-drawer')} + {/* Use Suspense for lazy-loaded components with error boundary */} + Loading...}> + {createPortal( + + + , + document.body, + 'home-task-drawer' + )} {createPortal( diff --git a/worklenz-frontend/src/pages/projects/project-list.tsx b/worklenz-frontend/src/pages/projects/project-list.tsx index a4477493..aa2a4011 100644 --- a/worklenz-frontend/src/pages/projects/project-list.tsx +++ b/worklenz-frontend/src/pages/projects/project-list.tsx @@ -379,6 +379,51 @@ const ProjectList: React.FC = () => { } }, [projectsError]); + // Optimized refresh handler with better error handling + const handleRefresh = useCallback(async () => { + try { + trackMixpanelEvent(evt_projects_refresh_click); + setIsLoading(true); + setErrorMessage(null); + + if (viewMode === ProjectViewType.LIST) { + await refetchProjects(); + } else if (viewMode === ProjectViewType.GROUP && groupBy) { + await dispatch(fetchGroupedProjects(groupedRequestParams)).unwrap(); + } + } catch (error) { + console.error('Error refreshing projects:', error); + setErrorMessage('Failed to refresh projects. Please try again.'); + } finally { + setIsLoading(false); + } + }, [trackMixpanelEvent, refetchProjects, viewMode, groupBy, dispatch, groupedRequestParams]); + + // Enhanced empty text with error handling + const emptyContent = useMemo(() => { + if (errorMessage) { + return ( + +

{errorMessage}

+ + + } + /> + ); + } + return ; + }, [errorMessage, handleRefresh, isLoading, t]); + + // Memoize the pagination show total function + const paginationShowTotal = useMemo( + () => (total: number, range: [number, number]) => `${range[0]}-${range[1]} of ${total} groups`, + [] + ); + const handleTableChange = useCallback( ( newPagination: TablePaginationConfig,