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
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Project Roadmap Redesign
-
-
- Experience a comprehensive visual representation of task progression within your projects.
- The sequential arrangement unfolds seamlessly in a user-friendly timeline format, allowing
- for effortless understanding and efficient project management.
-
-
-
-
-
-
-
-
-
-
-
-
Project Workload Redesign
-
- Gain insights into the optimized allocation and utilization of resources within your project.
-
-
-
-
-
-
-
-
-
-
-
-
Create new tasks from the roadmap itself
-
- Effortlessly generate and modify tasks directly from the roadmap interface with a simple
- click-and-drag functionality.
- Seamlessly adjust the task's date range according to your
- preferences, providing a user-friendly and intuitive experience for efficient task management.
-
-
-
-
-
-
-
-
-
-
-
-
Deactivate Team Members
-
- Effortlessly manage your team by deactivating members without losing their valuable work.
-
-
- Navigate to the "Settings" section and access "Team Members" to conveniently deactivate
- team members while preserving the work they have contributed.
-
-
-
-
-
-
-
-
-
-
-
-
Reporting Enhancements
-
- This release also includes several other miscellaneous bug fixes and performance
- enhancements to further improve your experience.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Click here to unsubscribe and manage your email preferences.
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
🚀 New Tasks List & Kanban Board
+
+ Performance optimized for faster loading
+ Redesigned UI for clarity and speed
+ Advanced filters for easier task management
+
+
+
+
+
+
📁 Group View in Projects List
+
+ Toggle between list and group view
+ Group projects by client or category
+ Improved navigation and organization
+
+
+
+
+
🌐 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}
+
+ Retry
+
+
+ }
+ />
+ );
+ }
+ 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,