Repository: ever-co/ever-teams
Pull request #3694: [Feat]: Improve Meet Component
Author: login: Innocent-Akim
Labels: name: feature
name: WEB
description: Web app
name: Improvement
description: Improvement
name: Ever Teams
name: Bug fix
Created: 2025-03-26T17:19:37Z | Merged: 2025-03-26T20:36:03Z

## Description

Please include a summary of the changes and the related issues.

## Type of Change

-   [ ] Bug fix
-   [x] New feature
-   [ ] Breaking change
-   [ ] Documentation update

## Checklist

-   [x] My code follows the style guidelines of this project
-   [x] I have performed a self-review of my code
-   [ ] I have commented on my code, particularly in hard-to-understand areas
-   [ ] I have made corresponding changes to the documentation
-   [x] My changes generate no new warnings

## Previous screenshots

Please add here videos or images of the previous status

## Current screenshots

Please add here videos or images of the current (new) status


<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit

- **New Features**
  - Enhanced the meeting experience with improved user feedback, including clear error messages, a retry option, and loading indicators during security token retrieval.
  - Updated the meeting interface to streamline transitions and validate meeting details, while increasing authentication flexibility.
  - Introduced local storage persistence for the activity filter in the Profile component.

- **Bug Fixes**
  - Strengthened input validation and error handling to prevent issues with meeting information.
  - Improved the retrieval of daily plans to gracefully manage missing or unexpected data.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->


Diff:
diff --git a/.cspell.json b/.cspell.json
index f3c3af37fe..5e1b4fb1a9 100644
--- a/.cspell.json
+++ b/.cspell.json
@@ -118,6 +118,7 @@
 		"dolore",
 		"domhandler",
 		"domutils",
+		"prejoin",
 		"DONT",
 		"dotenv",
 		"dpkg",
diff --git a/apps/web/app/[locale]/meet/jitsi/component.tsx b/apps/web/app/[locale]/meet/jitsi/component.tsx
index cff21b92ff..861cda882a 100644
--- a/apps/web/app/[locale]/meet/jitsi/component.tsx
+++ b/apps/web/app/[locale]/meet/jitsi/component.tsx
@@ -1,62 +1,135 @@
 'use client';
 
 import { useCollaborative, useQuery } from '@app/hooks';
-import { getMeetJwtAuthTokenAPI } from '@app/services/client/api';
+import { getMeetJwtAuthTokenAPI } from '@app/services/client/api/meet-auth';
 import { withAuthentication } from 'lib/app/authenticator';
 import { BackdropLoader, Meta } from 'lib/components';
 import dynamic from 'next/dynamic';
 import { useRouter, usePathname } from 'next/navigation';
 import { useEffect, useMemo, useRef, useState } from 'react';
 
+// Maximum number of retry attempts
+const MAX_RETRIES = 3;
+const RETRY_DELAY = 1000; // 1 second
+
+// Lazy load Meet component
 const Meet = dynamic(() => import('lib/features/integrations/meet'), {
 	ssr: false,
 	loading: () => <BackdropLoader show />
 });
 
-function useMeetJwtToken() {
+function useMeetJwtToken(room?: string) {
 	const [token, setToken] = useState<string>();
+	const [error, setError] = useState<Error>();
+	const [retryCount, setRetryCount] = useState(0);
 	const { queryCall, loading } = useQuery(getMeetJwtAuthTokenAPI);
 
 	useEffect(() => {
-		queryCall().then((res) => setToken(res.data.token));
-		// eslint-disable-next-line react-hooks/exhaustive-deps
-	}, []);
+		const getToken = async () => {
+			try {
+				const res = await queryCall({ room });
+				setToken(res.data.token);
+				setError(undefined);
+			} catch (err) {
+				console.error('Failed to fetch JWT token:', err);
+
+				// If we haven't exceeded max retries, try again after delay
+				if (retryCount < MAX_RETRIES) {
+					setTimeout(() => {
+						setRetryCount((prev) => prev + 1);
+					}, RETRY_DELAY);
+					return;
+				}
+
+				setError(err as Error);
+			}
+		};
+
+		if (room) {
+			getToken();
+		}
+	}, [queryCall, retryCount, room]); // Added room to dependencies
+
+	// Reset retry count when query or room changes
+	useEffect(() => {
+		setRetryCount(0);
+	}, [queryCall, room]);
 
-	return { loading, token };
+	return { loading, token, error, retrying: retryCount > 0 };
 }
 
 function MeetPage() {
 	const router = useRouter();
 	const pathname = usePathname();
-	const { token } = useMeetJwtToken();
-	const { randomMeetName } = useCollaborative();
 	const replaced = useRef(false);
 
+	// Extract room name from URL with proper validation
 	const room = useMemo(() => {
-		if (!pathname) {
-			return false;
-		}
+		if (!pathname) return null;
+
+		const queryIndex = pathname.indexOf('?');
+		if (queryIndex === -1) return null;
 
-		const urlParams = pathname.substring(pathname.indexOf('?'));
+		const urlParams = pathname.substring(queryIndex);
 		const searchParams = new URLSearchParams(urlParams);
+		const roomParam = searchParams.get('room');
 
-		return searchParams.get('room');
+		return roomParam && /^[A-Za-z0-9+/=]+$/.test(roomParam) ? roomParam : null;
 	}, [pathname]);
 
+	const { token, error, retrying } = useMeetJwtToken(room || undefined);
+	const { randomMeetName } = useCollaborative();
+
+	// Handle room creation and navigation
 	useEffect(() => {
 		if (!room && pathname?.startsWith('/meet/jitsi') && !replaced.current) {
-			const url = new URL(window.location.href);
-			url.searchParams.set('room', btoa(randomMeetName()));
-
-			router.replace(url.pathname + url.search);
-			replaced.current = true;
+			try {
+				const url = new URL(window.location.href);
+				const newRoom = btoa(randomMeetName());
+				url.searchParams.set('room', newRoom);
+				router.replace(url.pathname + url.search);
+				replaced.current = true;
+			} catch (err) {
+				console.error('Failed to create room:', err);
+			}
 		}
 	}, [room, router, randomMeetName, pathname]);
 
+	// Decode room name with validation
 	const roomName = useMemo(() => {
-		return room ? atob(room) : undefined;
+		if (!room) return null;
+		try {
+			return atob(room);
+		} catch {
+			return null;
+		}
 	}, [room]);
 
+	// Show error or retrying state
+	if (error) {
+		return (
+			<div className="flex flex-col items-center justify-center p-4">
+				<h2 className="text-xl font-semibold text-red-600 mb-2">Failed to initialize meeting</h2>
+				<p className="text-gray-600 mb-4">{error.message}</p>
+				<button
+					onClick={() => window.location.reload()}
+					className="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600"
+				>
+					Try Again
+				</button>
+			</div>
+		);
+	}
+
+	if (retrying) {
+		return (
+			<div className="flex items-center justify-center p-4">
+				<BackdropLoader show />
+				<p className="ml-2">Reconnecting...</p>
+			</div>
+		);
+	}
+
 	return (
 		<>
 			<Meta title="Meet" />
diff --git a/apps/web/app/[locale]/profile/[memberId]/page.tsx b/apps/web/app/[locale]/profile/[memberId]/page.tsx
index 462ef5b7b7..a286a70a98 100644
--- a/apps/web/app/[locale]/profile/[memberId]/page.tsx
+++ b/apps/web/app/[locale]/profile/[memberId]/page.tsx
@@ -1,7 +1,13 @@
 'use client';
 
 /* eslint-disable no-mixed-spaces-and-tabs */
-import { useAuthenticateUser, useDailyPlan, useOrganizationTeams, useUserProfilePage } from '@app/hooks';
+import {
+	useAuthenticateUser,
+	useDailyPlan,
+	useLocalStorageState,
+	useOrganizationTeams,
+	useUserProfilePage
+} from '@app/hooks';
 import { withAuthentication } from 'lib/app/authenticator';
 import { Breadcrumb, Button, Container, Text, VerticalSeparator } from 'lib/components';
 import { ArrowLeftIcon } from 'assets/svg';
@@ -31,7 +37,7 @@ const Profile = React.memo(function ProfilePage({ params }: { params: { memberId
 	const members = activeTeam?.members;
 	const { getEmployeeDayPlans } = useDailyPlan();
 	const fullWidth = useAtomValue(fullWidthState);
-	const [activityFilter, setActivityFilter] = useState<FilterTab>('Tasks');
+	const [activityFilter, setActivityFilter] = useLocalStorageState<FilterTab>('activity-filter', 'Tasks');
 	const setActivityTypeFilter = useSetAtom(activityTypeState);
 	const hook = useTaskFilter(profile);
 
diff --git a/apps/web/app/hooks/features/useDailyPlan.ts b/apps/web/app/hooks/features/useDailyPlan.ts
index c93b8fc188..831e5ce210 100644
--- a/apps/web/app/hooks/features/useDailyPlan.ts
+++ b/apps/web/app/hooks/features/useDailyPlan.ts
@@ -71,6 +71,7 @@ export function useDailyPlan() {
 			if (res) {
 				return res.data;
 			} else {
+<<<<<<< HEAD
 				console.error('Error fetching all day plans');
 			}
 		} catch (error) {
@@ -111,6 +112,17 @@ export function useDailyPlan() {
 	}, [getMyDailyPlans, setMyDailyPlans]);
 
 	// Employee day plans
+=======
+				// If no plans found, initialize with empty array
+				setMyDailyPlans({ items: [], total: 0 });
+			}
+		}).catch((error) => {
+			console.error('Failed to fetch daily plans:', error);
+			// In case of error, initialize with empty array
+			setMyDailyPlans({ items: [], total: 0 });
+		});
+	}, [getMyDailyPlansQueryCall, setMyDailyPlans]);
+>>>>>>> e5db32f0 (docs: translate comments to English in useDailyPlan)
 
 	const getEmployeeDayPlans = useCallback(
 		async (employeeId: string) => {
diff --git a/apps/web/app/services/client/api/meet-auth.ts b/apps/web/app/services/client/api/meet-auth.ts
index f3347f917e..b26c12b67e 100644
--- a/apps/web/app/services/client/api/meet-auth.ts
+++ b/apps/web/app/services/client/api/meet-auth.ts
@@ -1,5 +1,12 @@
 import api from '../axios';
 
-export const getMeetJwtAuthTokenAPI = () => {
-	return api.get<{ token: string }>('/auth/meet/jwt');
+export interface IMeetAuthRequest {
+	room?: string;
+	tenant?: string;
+}
+
+export const getMeetJwtAuthTokenAPI = (params?: IMeetAuthRequest) => {
+	return api.get<{ token: string }>('/auth/meet/jwt', { 
+		params
+	});
 };
diff --git a/apps/web/lib/features/integrations/meet/index.tsx b/apps/web/lib/features/integrations/meet/index.tsx
index ad95faf2b2..26e8462cf0 100644
--- a/apps/web/lib/features/integrations/meet/index.tsx
+++ b/apps/web/lib/features/integrations/meet/index.tsx
@@ -1,9 +1,80 @@
+'use client';
+
 import { MEET_DOMAIN } from '@app/constants';
 import { JitsiMeeting } from '@jitsi/react-sdk';
 import { useRouter } from 'next/navigation';
+import { useCallback, useEffect, useState } from 'react';
+
+interface MeetPageProps {
+	jwt: string;
+	roomName: string;
+}
 
-export default function MeetPage({ jwt, roomName }: { jwt: string; roomName: string }) {
+export default function MeetPage({ jwt, roomName }: MeetPageProps) {
 	const router = useRouter();
+	const [error, setError] = useState<Error>();
+
+	// Validate props
+	useEffect(() => {
+		if (!jwt) {
+			setError(new Error('JWT token is required'));
+			return;
+		}
+		if (!roomName) {
+			setError(new Error('Room name is required'));
+			return;
+		}
+	}, [jwt, roomName]);
+
+	// Handle meeting close
+	const handleClose = useCallback(() => {
+		try {
+			router.push('/');
+		} catch (err) {
+			console.error('Failed to redirect after meeting close:', err);
+		}
+	}, [router]);
+
+	// Handle API ready
+	const handleApiReady = useCallback(
+		(externalApi: any) => {
+			try {
+				externalApi.addListener('readyToClose', handleClose);
+
+				// Add error handling
+				externalApi.addListener('videoConferenceLeft', handleClose);
+				externalApi.addListener('connectionFailed', () => {
+					setError(new Error('Failed to connect to meeting'));
+				});
+			} catch (err) {
+				console.error('Failed to setup meeting API:', err);
+				setError(err as Error);
+			}
+		},
+		[handleClose]
+	);
+
+	// Handle iframe setup
+	const handleIFrameRef = useCallback((parentNode: HTMLDivElement) => {
+		if (parentNode) {
+			parentNode.style.height = '100vh';
+		}
+	}, []);
+
+	if (error) {
+		return (
+			<div className="flex flex-col items-center justify-center p-4">
+				<h2 className="text-xl font-semibold text-red-600 mb-2">Failed to initialize meeting</h2>
+				<p className="text-gray-600 mb-4">{error.message}</p>
+				<button
+					onClick={() => router.push('/')}
+					className="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600"
+				>
+					Return Home
+				</button>
+			</div>
+		);
+	}
 
 	return (
 		<JitsiMeeting
@@ -14,22 +85,18 @@ export default function MeetPage({ jwt, roomName }: { jwt: string; roomName: str
 				startWithVideoMuted: true,
 				disableModeratorIndicator: false,
 				startScreenSharing: false,
-				enableEmailInStats: false
+				enableEmailInStats: false,
+				preJoinPageEnabled: true,
+				enableClosePage: true
 			}}
 			interfaceConfigOverwrite={{
-				DISABLE_JOIN_LEAVE_NOTIFICATIONS: false
+				DISABLE_JOIN_LEAVE_NOTIFICATIONS: false,
+				MOBILE_APP_PROMO: false,
+				SHOW_CHROME_EXTENSION_BANNER: false
 			}}
 			jwt={jwt}
-			onApiReady={(externalApi) => {
-				externalApi.addListener('readyToClose', () => {
-					router.push('/');
-				});
-				// here you can attach custom event listeners to the Jitsi Meet External API
-				// you can also store it locally to execute commands
-			}}
-			getIFrameRef={(iframeRef) => {
-				iframeRef.style.height = '100vh';
-			}}
+			onApiReady={handleApiReady}
+			getIFrameRef={handleIFrameRef}
 		/>
 	);
 }


Reviews:
user: login: greptile-apps[bot]
body: 
## PR Summary

Implemented comprehensive improvements to the Meet component with enhanced error handling and JWT token management.

- Added retry mechanism with configurable attempts (MAX_RETRIES=3) for JWT token fetching in `/apps/web/app/[locale]/meet/jitsi/component.tsx`
- Added strict room name validation using regex `/^[A-Za-z0-9+/=]+$/` for security in `/apps/web/app/[locale]/meet/jitsi/component.tsx`
- Implemented proper error boundaries and user feedback states in `/apps/web/lib/features/integrations/meet/index.tsx`
- Added type-safe `IMeetAuthRequest` interface for JWT authentication parameters in `/apps/web/app/services/client/api/meet-auth.ts`
- Enhanced error handling in `getMyDailyPlans` to properly handle empty states in `/apps/web/app/hooks/features/useDailyPlan.ts`

<sub>4 file(s) reviewed, 4 comment(s)</sub>
<sub>[Edit PR Review Bot Settings](https://app.greptile.com/review/github) | [Greptile](https://greptile.com?utm_source=greptile_expert&utm_medium=github&utm_campaign=code_reviews&utm_content=ever-teams_3694)</sub>
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-26T17:20:26Z
user: login: coderabbitai[bot]
body: **Actionable comments posted: 0**

<details>
<summary>🔭 Outside diff range comments (1)</summary><blockquote>

<details>
<summary>apps/web/app/[locale]/profile/[memberId]/page.tsx (1)</summary><blockquote>

`1-191`: _💡 Verification agent_

<details>
<summary>🧩 Analysis chain</summary>

**PR description mismatch with actual changes.**

The PR is titled "Improve Meet Component" but the changes in this file are related to profile page activity filtering, not meeting functionality. Ensure the PR description accurately reflects all the changes being made across the codebase.

---

🏁 Script executed:

```shell
#!/bin/bash
# Search for any Meet component related changes that might justify the PR title
echo "Searching for Meet component related files..."
fd "Meet" --type f --exec grep -l "Meet" {} \;

echo "Searching for JWT token hook changes mentioned in the summary..."
rg -l "useJwt|JWT" 
```

Length of output: 598

---



</details>

**PR Title/Description Update Needed**

- The changes in this file focus on profile page activity filtering rather than any Meet component functionality.
- Although there are Meet-related files elsewhere (as confirmed by our repository search), this file does not include such changes.
- Please update the PR title and description to accurately reflect all the changes across the codebase.

</blockquote></details>

</blockquote></details>

<details>
<summary>🧹 Nitpick comments (1)</summary><blockquote>

<details>
<summary>apps/web/app/[locale]/profile/[memberId]/page.tsx (1)</summary><blockquote>

`40-40`: **Good enhancement to user experience through persistent state.**

Using `useLocalStorageState` for the `activityFilter` state is a great improvement. This ensures that user preferences for activity filtering persist across sessions and page refreshes, providing a more consistent user experience.


However, there's a potential edge case to consider: if multiple users share the same browser, they would share the same activity filter preference. If this is a concern, consider scoping the storage key to include the user ID:

```diff
- const [activityFilter, setActivityFilter] = useLocalStorageState<FilterTab>('activity-filter', 'Tasks');
+ const [activityFilter, setActivityFilter] = useLocalStorageState<FilterTab>(`user-${user?.id}-activity-filter`, 'Tasks');
```

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 9a0bfc39f4c0a45333e82131f8809ca012819589 and 45bc304e3a8320bd526ce9a57741b9fcb20e5ed6.

</details>

<details>
<summary>📒 Files selected for processing (2)</summary>

* `.cspell.json` (1 hunks)
* `apps/web/app/[locale]/profile/[memberId]/page.tsx` (2 hunks)

</details>

<details>
<summary>⏰ Context from checks skipped due to timeout of 90000ms (2)</summary>

* GitHub Check: Codacy Static Code Analysis
* GitHub Check: deploy

</details>

<details>
<summary>🔇 Additional comments (3)</summary><blockquote>

<details>
<summary>.cspell.json (1)</summary>

`121-121`: **New Recognized Term "prejoin" Added**

The addition of the word "prejoin" is correctly placed within the `words` array. This change supports the Meet component's prejoin functionality as outlined in the PR objectives. Please ensure that its usage in the Meet configuration remains consistent with this accepted spelling.

</details>
<details>
<summary>apps/web/app/[locale]/profile/[memberId]/page.tsx (2)</summary>

`4-10`: **Improved import structure with useLocalStorageState.**

The addition of the `useLocalStorageState` hook to your imports enhances the component's state management capabilities.

---

`78-83`: **Properly updated dependency array.**

The dependency array correctly includes `setActivityFilter`, ensuring the callback is recreated when the setter function changes.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-26T17:54:38Z

Review comments:
diff_hunk: @@ -1,9 +1,80 @@
+'use client';
+
 import { MEET_DOMAIN } from '@app/constants';
 import { JitsiMeeting } from '@jitsi/react-sdk';
 import { useRouter } from 'next/navigation';
+import { useCallback, useEffect, useState } from 'react';
+
+interface MeetPageProps {
+	jwt: string;
+	roomName: string;
+}
 
-export default function MeetPage({ jwt, roomName }: { jwt: string; roomName: string }) {
+export default function MeetPage({ jwt, roomName }: MeetPageProps) {
 	const router = useRouter();
+	const [error, setError] = useState<Error>();
+
+	// Validate props
+	useEffect(() => {
+		if (!jwt) {
+			setError(new Error('JWT token is required'));
+			return;
+		}
+		if (!roomName) {
+			setError(new Error('Room name is required'));
+			return;
+		}
+	}, [jwt, roomName]);
+
+	// Handle meeting close
+	const handleClose = useCallback(() => {
+		try {
+			router.push('/');
+		} catch (err) {
+			console.error('Failed to redirect after meeting close:', err);
+		}
+	}, [router]);
+
+	// Handle API ready
+	const handleApiReady = useCallback(
+		(externalApi: any) => {
path: apps/web/lib/features/integrations/meet/index.tsx
user: login: greptile-apps[bot]
body: style: externalApi should be properly typed instead of 'any' to ensure type safety
created_at: 2025-03-26T17:20:06Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1,62 +1,135 @@
 'use client';
 
 import { useCollaborative, useQuery } from '@app/hooks';
-import { getMeetJwtAuthTokenAPI } from '@app/services/client/api';
+import { getMeetJwtAuthTokenAPI } from '@app/services/client/api/meet-auth';
 import { withAuthentication } from 'lib/app/authenticator';
 import { BackdropLoader, Meta } from 'lib/components';
 import dynamic from 'next/dynamic';
 import { useRouter, usePathname } from 'next/navigation';
 import { useEffect, useMemo, useRef, useState } from 'react';
 
+// Maximum number of retry attempts
+const MAX_RETRIES = 3;
+const RETRY_DELAY = 1000; // 1 second
+
+// Lazy load Meet component
 const Meet = dynamic(() => import('lib/features/integrations/meet'), {
 	ssr: false,
 	loading: () => <BackdropLoader show />
 });
 
-function useMeetJwtToken() {
+function useMeetJwtToken(room?: string) {
 	const [token, setToken] = useState<string>();
+	const [error, setError] = useState<Error>();
+	const [retryCount, setRetryCount] = useState(0);
 	const { queryCall, loading } = useQuery(getMeetJwtAuthTokenAPI);
 
 	useEffect(() => {
-		queryCall().then((res) => setToken(res.data.token));
-		// eslint-disable-next-line react-hooks/exhaustive-deps
-	}, []);
+		const getToken = async () => {
+			try {
+				const res = await queryCall({ room });
+				setToken(res.data.token);
+				setError(undefined);
+			} catch (err) {
+				console.error('Failed to fetch JWT token:', err);
+
+				// If we haven't exceeded max retries, try again after delay
+				if (retryCount < MAX_RETRIES) {
+					setTimeout(() => {
+						setRetryCount((prev) => prev + 1);
+					}, RETRY_DELAY);
+					return;
path: apps/web/app/[locale]/meet/jitsi/component.tsx
user: login: greptile-apps[bot]
body: logic: Potential memory leak - cleanup function needed to clear timeout on unmount

```suggestion
					const timeoutId = setTimeout(() => {
						setRetryCount((prev) => prev + 1);
					}, RETRY_DELAY);
					return () => clearTimeout(timeoutId);
```
created_at: 2025-03-26T17:20:09Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1,62 +1,135 @@
 'use client';
 
 import { useCollaborative, useQuery } from '@app/hooks';
-import { getMeetJwtAuthTokenAPI } from '@app/services/client/api';
+import { getMeetJwtAuthTokenAPI } from '@app/services/client/api/meet-auth';
 import { withAuthentication } from 'lib/app/authenticator';
 import { BackdropLoader, Meta } from 'lib/components';
 import dynamic from 'next/dynamic';
 import { useRouter, usePathname } from 'next/navigation';
 import { useEffect, useMemo, useRef, useState } from 'react';
 
+// Maximum number of retry attempts
+const MAX_RETRIES = 3;
+const RETRY_DELAY = 1000; // 1 second
+
+// Lazy load Meet component
 const Meet = dynamic(() => import('lib/features/integrations/meet'), {
 	ssr: false,
 	loading: () => <BackdropLoader show />
 });
 
-function useMeetJwtToken() {
+function useMeetJwtToken(room?: string) {
 	const [token, setToken] = useState<string>();
+	const [error, setError] = useState<Error>();
+	const [retryCount, setRetryCount] = useState(0);
 	const { queryCall, loading } = useQuery(getMeetJwtAuthTokenAPI);
 
 	useEffect(() => {
-		queryCall().then((res) => setToken(res.data.token));
-		// eslint-disable-next-line react-hooks/exhaustive-deps
-	}, []);
+		const getToken = async () => {
+			try {
+				const res = await queryCall({ room });
+				setToken(res.data.token);
+				setError(undefined);
+			} catch (err) {
+				console.error('Failed to fetch JWT token:', err);
+
+				// If we haven't exceeded max retries, try again after delay
+				if (retryCount < MAX_RETRIES) {
+					setTimeout(() => {
+						setRetryCount((prev) => prev + 1);
+					}, RETRY_DELAY);
+					return;
+				}
+
+				setError(err as Error);
+			}
+		};
+
+		if (room) {
+			getToken();
+		}
+	}, [queryCall, retryCount, room]); // Added room to dependencies
+
+	// Reset retry count when query or room changes
+	useEffect(() => {
+		setRetryCount(0);
+	}, [queryCall, room]);
 
-	return { loading, token };
+	return { loading, token, error, retrying: retryCount > 0 };
 }
 
 function MeetPage() {
 	const router = useRouter();
 	const pathname = usePathname();
-	const { token } = useMeetJwtToken();
-	const { randomMeetName } = useCollaborative();
 	const replaced = useRef(false);
 
+	// Extract room name from URL with proper validation
 	const room = useMemo(() => {
-		if (!pathname) {
-			return false;
-		}
+		if (!pathname) return null;
+
+		const queryIndex = pathname.indexOf('?');
+		if (queryIndex === -1) return null;
 
-		const urlParams = pathname.substring(pathname.indexOf('?'));
+		const urlParams = pathname.substring(queryIndex);
 		const searchParams = new URLSearchParams(urlParams);
+		const roomParam = searchParams.get('room');
 
-		return searchParams.get('room');
+		return roomParam && /^[A-Za-z0-9+/=]+$/.test(roomParam) ? roomParam : null;
path: apps/web/app/[locale]/meet/jitsi/component.tsx
user: login: greptile-apps[bot]
body: style: Base64 validation regex may be too restrictive - consider allowing URL-safe base64 characters
created_at: 2025-03-26T17:20:10Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1,62 +1,135 @@
 'use client';
 
 import { useCollaborative, useQuery } from '@app/hooks';
-import { getMeetJwtAuthTokenAPI } from '@app/services/client/api';
+import { getMeetJwtAuthTokenAPI } from '@app/services/client/api/meet-auth';
 import { withAuthentication } from 'lib/app/authenticator';
 import { BackdropLoader, Meta } from 'lib/components';
 import dynamic from 'next/dynamic';
 import { useRouter, usePathname } from 'next/navigation';
 import { useEffect, useMemo, useRef, useState } from 'react';
 
+// Maximum number of retry attempts
+const MAX_RETRIES = 3;
+const RETRY_DELAY = 1000; // 1 second
+
+// Lazy load Meet component
 const Meet = dynamic(() => import('lib/features/integrations/meet'), {
 	ssr: false,
 	loading: () => <BackdropLoader show />
 });
 
-function useMeetJwtToken() {
+function useMeetJwtToken(room?: string) {
 	const [token, setToken] = useState<string>();
+	const [error, setError] = useState<Error>();
+	const [retryCount, setRetryCount] = useState(0);
 	const { queryCall, loading } = useQuery(getMeetJwtAuthTokenAPI);
 
 	useEffect(() => {
-		queryCall().then((res) => setToken(res.data.token));
-		// eslint-disable-next-line react-hooks/exhaustive-deps
-	}, []);
+		const getToken = async () => {
+			try {
+				const res = await queryCall({ room });
+				setToken(res.data.token);
+				setError(undefined);
+			} catch (err) {
+				console.error('Failed to fetch JWT token:', err);
+
+				// If we haven't exceeded max retries, try again after delay
+				if (retryCount < MAX_RETRIES) {
+					setTimeout(() => {
+						setRetryCount((prev) => prev + 1);
+					}, RETRY_DELAY);
+					return;
+				}
+
+				setError(err as Error);
+			}
+		};
+
+		if (room) {
+			getToken();
+		}
+	}, [queryCall, retryCount, room]); // Added room to dependencies
+
+	// Reset retry count when query or room changes
+	useEffect(() => {
+		setRetryCount(0);
+	}, [queryCall, room]);
 
-	return { loading, token };
+	return { loading, token, error, retrying: retryCount > 0 };
 }
 
 function MeetPage() {
 	const router = useRouter();
 	const pathname = usePathname();
-	const { token } = useMeetJwtToken();
-	const { randomMeetName } = useCollaborative();
 	const replaced = useRef(false);
 
+	// Extract room name from URL with proper validation
 	const room = useMemo(() => {
-		if (!pathname) {
-			return false;
-		}
+		if (!pathname) return null;
+
+		const queryIndex = pathname.indexOf('?');
+		if (queryIndex === -1) return null;
 
-		const urlParams = pathname.substring(pathname.indexOf('?'));
+		const urlParams = pathname.substring(queryIndex);
 		const searchParams = new URLSearchParams(urlParams);
+		const roomParam = searchParams.get('room');
 
-		return searchParams.get('room');
+		return roomParam && /^[A-Za-z0-9+/=]+$/.test(roomParam) ? roomParam : null;
 	}, [pathname]);
 
+	const { token, error, retrying } = useMeetJwtToken(room || undefined);
+	const { randomMeetName } = useCollaborative();
+
+	// Handle room creation and navigation
 	useEffect(() => {
 		if (!room && pathname?.startsWith('/meet/jitsi') && !replaced.current) {
-			const url = new URL(window.location.href);
-			url.searchParams.set('room', btoa(randomMeetName()));
-
-			router.replace(url.pathname + url.search);
-			replaced.current = true;
+			try {
+				const url = new URL(window.location.href);
+				const newRoom = btoa(randomMeetName());
+				url.searchParams.set('room', newRoom);
+				router.replace(url.pathname + url.search);
+				replaced.current = true;
+			} catch (err) {
+				console.error('Failed to create room:', err);
+			}
 		}
 	}, [room, router, randomMeetName, pathname]);
 
+	// Decode room name with validation
 	const roomName = useMemo(() => {
-		return room ? atob(room) : undefined;
+		if (!room) return null;
+		try {
+			return atob(room);
+		} catch {
+			return null;
+		}
 	}, [room]);
 
+	// Show error or retrying state
+	if (error) {
+		return (
+			<div className="flex flex-col items-center justify-center p-4">
+				<h2 className="text-xl font-semibold text-red-600 mb-2">Failed to initialize meeting</h2>
+				<p className="text-gray-600 mb-4">{error.message}</p>
+				<button
+					onClick={() => window.location.reload()}
path: apps/web/app/[locale]/meet/jitsi/component.tsx
user: login: greptile-apps[bot]
body: style: window.location.reload() loses state - consider using router.refresh() instead
created_at: 2025-03-26T17:20:10Z
author_association: CONTRIBUTOR

PR comments:
user: login: coderabbitai[bot]
created_at: 2025-03-26T17:19:48Z
body: <!-- This is an auto-generated comment: summarize by coderabbit.ai -->
<!-- This is an auto-generated comment: failure by coderabbit.ai -->

> [!CAUTION]
> ## Review failed
> 
> The pull request is closed.

<!-- end of auto-generated comment: failure by coderabbit.ai -->
<!-- walkthrough_start -->

## Walkthrough
The changes enhance error handling and introduce a retry mechanism for fetching the JWT token in the `useMeetJwtToken` hook, which now accepts an optional `room` parameter. The `MeetPage` component has been updated for better prop validation and error management. The `useDailyPlan` function has improved robustness against undefined states, while the API method for meet authentication now supports additional parameters through a new interface. State management for the `activityFilter` in the `Profile` component has been updated to persist in local storage.

## Changes

| File(s) | Change Summary |
|---------|----------------|
| `apps/web/app/[locale]/meet/…/component.tsx`<br>`apps/web/app/services/client/api/meet-auth.ts` | Enhanced JWT token fetching with optional `room` parameter and added error handling via a new `IMeetAuthRequest` interface. |
| `apps/web/app/hooks/features/useDailyPlan.ts` | Improved error handling and initialization for daily plans to avoid undefined states. |
| `apps/web/lib/features/integrations/meet/index.tsx` | Refactored MeetPage component with a defined props interface and enhanced error handling for Jitsi meeting integration. |
| `.cspell.json` | Added the word "prejoin" to the recognized words list. |
| `apps/web/app/[locale]/profile/[memberId]/page.tsx` | Updated `activityFilter` state management to use `useLocalStorageState` for persistence. |

## Sequence Diagram(s)

```mermaid
sequenceDiagram
    participant UI as MeetPage/UI
    participant Hook as useMeetJwtToken
    participant API as queryCall
    UI->>Hook: Call useMeetJwtToken(room)
    Hook->>API: Execute queryCall(room)
    alt Successful Token Fetch
        API-->>Hook: Return JWT token
        Hook-->>UI: Render meeting session
    else Token Fetch Error
        API-->>Hook: Error response
        Hook->>Hook: Check retry count
        alt Retry Allowed
            Hook->>Hook: Set retry timeout & retry queryCall(room)
        else Max Retries Exceeded
            Hook-->>UI: Update UI with error state and show retry option
        end
    end
```

```mermaid
sequenceDiagram
    participant MP as MeetPage
    participant Jitsi as JitsiMeeting API
    MP->>Jitsi: Initialize meeting (with updated configuration)
    Jitsi-->>MP: onApiReady -> Trigger handleApiReady callback
    Jitsi-->>MP: onMeetingLeave/onConnectionFailed -> Trigger error handling via callbacks
    MP->>MP: Update error state and perform redirection (handleClose)
```

## Possibly related PRs
- ever-co/ever-teams#3694: This PR modifies the `useMeetJwtToken` hook to enhance error handling and introduce a retry mechanism for fetching the JWT token with an optional `room` parameter.

## Suggested reviewers
- evereq

## Poem
> I’m a rabbit in a land of code,  
> Hopping over bugs on a winding road.  
> With retry hops and interfaces so neat,  
> The changes make our app a treat.  
> Code carrots shine in each debug light,  
> Leaping with joy deep into the night!  
> 🐇✨

<!-- walkthrough_end -->
<!-- This is an auto-generated comment: all tool run failures by coderabbit.ai -->

> [!WARNING]
> There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it’s a critical failure.
> 
> <details>
> <summary>🔧 ESLint</summary>
> 
> > If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.
> 
> <details>
> <summary>apps/web/app/hooks/features/useDailyPlan.ts</summary>
> 
> 
> Oops! Something went wrong! :(
> 
> ESLint: 8.46.0
> 
> ESLint couldn't find the config "next/core-web-vitals" to extend from. Please check that the name of the config is correct.
> 
> The config "next/core-web-vitals" was referenced from the config file in "/apps/web/.eslintrc.json".
> 
> If you still have problems, please stop by https://eslint.org/chat/help to chat with the team.
> 
> 
> </details>
> 
> <details>
> <summary>apps/web/app/[locale]/meet/jitsi/component.tsx</summary>
> 
> 
> Oops! Something went wrong! :(
> 
> ESLint: 8.46.0
> 
> ESLint couldn't find the config "next/core-web-vitals" to extend from. Please check that the name of the config is correct.
> 
> The config "next/core-web-vitals" was referenced from the config file in "/apps/web/.eslintrc.json".
> 
> If you still have problems, please stop by https://eslint.org/chat/help to chat with the team.
> 
> 
> </details>
> 
> <details>
> <summary>apps/web/lib/features/integrations/meet/index.tsx</summary>
> 
> 
> Oops! Something went wrong! :(
> 
> ESLint: 8.46.0
> 
> ESLint couldn't find the config "next/core-web-vitals" to extend from. Please check that the name of the config is correct.
> 
> The config "next/core-web-vitals" was referenced from the config file in "/apps/web/.eslintrc.json".
> 
> If you still have problems, please stop by https://eslint.org/chat/help to chat with the team.
> 
> 
> </details>
> 
> + 2 others
> 
> </details>

<!-- end of auto-generated comment: all tool run failures by coderabbit.ai -->
<!-- internal state start -->


<!-- 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 -->

<!-- internal state end -->

---

<details>
<summary>📜 Recent review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 45bc304e3a8320bd526ce9a57741b9fcb20e5ed6 and 9f783ba52d4b6ba87b721d34554c01b3fe612d1c.

</details>

<details>
<summary>📒 Files selected for processing (6)</summary>

* `.cspell.json` (1 hunks)
* `apps/web/app/[locale]/meet/jitsi/component.tsx` (1 hunks)
* `apps/web/app/[locale]/profile/[memberId]/page.tsx` (2 hunks)
* `apps/web/app/hooks/features/useDailyPlan.ts` (2 hunks)
* `apps/web/app/services/client/api/meet-auth.ts` (1 hunks)
* `apps/web/lib/features/integrations/meet/index.tsx` (2 hunks)

</details>

</details>
<!-- finishing_touch_checkbox_start -->

<details>
<summary>✨ Finishing Touches</summary>

- [ ] <!-- {"checkboxId": "7962f53c-55bc-4827-bfbf-6a18da830691"} --> 📝 Generate Docstrings

</details>

<!-- finishing_touch_checkbox_end -->
<!-- tips_start -->

---



<details>
<summary>🪧 Tips</summary>

### Chat

There are 3 ways to chat with [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=ever-co/ever-teams&utm_content=3694):

- Review comments: Directly reply to a review comment made by CodeRabbit. Example:
  - `I pushed a fix in commit <commit_id>, please review it.`
  - `Generate unit testing code for this file.`
  - `Open a follow-up GitHub issue for this discussion.`
- Files and specific lines of code (under the "Files changed" tab): Tag `@coderabbitai` in a new review comment at the desired location with your query. Examples:
  - `@coderabbitai generate unit testing code for this file.`
  -	`@coderabbitai modularize this function.`
- PR comments: Tag `@coderabbitai` in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
  - `@coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.`
  - `@coderabbitai read src/utils.ts and generate unit testing code.`
  - `@coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.`
  - `@coderabbitai help me debug CodeRabbit configuration file.`

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

### CodeRabbit Commands (Invoked using PR comments)

- `@coderabbitai pause` to pause the reviews on a PR.
- `@coderabbitai resume` to resume the paused reviews.
- `@coderabbitai review` to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
- `@coderabbitai full review` to do a full review from scratch and review all the files again.
- `@coderabbitai summary` to regenerate the summary of the PR.
- `@coderabbitai generate docstrings` to [generate docstrings](https://docs.coderabbit.ai/finishing-touches/docstrings) for this PR.
- `@coderabbitai resolve` resolve all the CodeRabbit review comments.
- `@coderabbitai plan` to trigger planning for file edits and PR creation.
- `@coderabbitai configuration` to show the current CodeRabbit configuration for the repository.
- `@coderabbitai help` to get help.

### Other keywords and placeholders

- Add `@coderabbitai ignore` anywhere in the PR description to prevent this PR from being reviewed.
- Add `@coderabbitai summary` to generate the high-level summary at a specific location in the PR description.
- Add `@coderabbitai` anywhere in the PR title to generate the title automatically.

### CodeRabbit Configuration File (`.coderabbit.yaml`)

- You can programmatically configure CodeRabbit by adding a `.coderabbit.yaml` file to the root of your repository.
- Please see the [configuration documentation](https://docs.coderabbit.ai/guides/configure-coderabbit) for more information.
- If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: `# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json`

### Documentation and Community

- Visit our [Documentation](https://docs.coderabbit.ai) for detailed information on how to use CodeRabbit.
- Join our [Discord Community](http://discord.gg/coderabbit) to get help, request features, and share feedback.
- Follow us on [X/Twitter](https://twitter.com/coderabbitai) for updates and announcements.

</details>

<!-- tips_end -->
author_association: CONTRIBUTOR
user: login: evereq
created_at: 2025-03-26T20:10:32Z
body: @Innocent-Akim can you carefully fix merge conflict please?
author_association: MEMBER