diff --git a/src/components/CreateConversationModal.tsx b/src/components/CreateConversationModal.tsx index 4d91a607..b5ccce31 100644 --- a/src/components/CreateConversationModal.tsx +++ b/src/components/CreateConversationModal.tsx @@ -1,10 +1,10 @@ import { useState, useEffect } from 'react'; import { useCreateConversation } from '../hooks/useMessaging'; -import { supabase } from '../supabase-client'; +import { supabase, isBackendAvailable } from '../supabase-client'; import { useAuth } from '../hooks/useAuth'; import { X, Search, Users, MessageCircle, Check } from 'lucide-react'; import type { Conversation } from '../types/messaging'; -import { showSuccess,showError } from '../utils/toast'; +import { showSuccess, showError } from '../utils/toast'; interface CreateConversationModalProps { @@ -39,15 +39,39 @@ const CreateConversationModal = ({ onClose, onConversationCreated }: CreateConve // Fetch users for selection useEffect(() => { const fetchUsers = async () => { + if (!isBackendAvailable || !supabase) { + console.warn('Backend unavailable - cannot fetch users'); + setUsers([]); + return; + } + try { - const { data, error } = await supabase.auth.admin.listUsers(); + // ✅ SECURE: Query profiles table with RLS instead of admin API + let query = supabase + .from('Profiles') + .select('id, full_name, avatar_url'); + + if (currentUser?.id) { + query = query.neq('id', currentUser.id); + } + + const { data, error } = await query; if (error) throw error; - - // Filter out current user - const filteredUsers = data.users.filter(u => u.id !== currentUser?.id); - setUsers(filteredUsers as User[]); + + // Map profiles to User format + const mappedUsers = (data || []).map(profile => ({ + id: profile.id, + email: '', // Email not exposed for privacy + user_metadata: { + full_name: profile.full_name, + avatar_url: profile.avatar_url, + }, + })); + + setUsers(mappedUsers as User[]); } catch (error) { console.error('Failed to fetch users:', error); + showError('Failed to load users'); } }; @@ -60,10 +84,10 @@ const CreateConversationModal = ({ onClose, onConversationCreated }: CreateConve const fullName = user.user_metadata?.full_name?.toLowerCase() || ''; const userName = user.user_metadata?.user_name?.toLowerCase() || ''; const email = user.email.toLowerCase(); - - return fullName.includes(searchLower) || - userName.includes(searchLower) || - email.includes(searchLower); + + return fullName.includes(searchLower) || + userName.includes(searchLower) || + email.includes(searchLower); }); const handleUserSelect = (user: User) => { @@ -83,14 +107,14 @@ const CreateConversationModal = ({ onClose, onConversationCreated }: CreateConve const handleCreate = async () => { if (selectedUsers.length === 0) return; - + if (conversationType === 'group' && !groupName.trim()) { showError('Please enter a group name'); return; } setIsLoading(true); - + try { const conversationData = { type: conversationType, @@ -101,12 +125,12 @@ const CreateConversationModal = ({ onClose, onConversationCreated }: CreateConve }; const conversation = await createConversation.mutateAsync(conversationData); - + showSuccess( - conversationType === 'group' + conversationType === 'group' ? "Group conversation created" - : "Conversation started" - ); + : "Conversation started" + ); onConversationCreated(conversation); @@ -119,9 +143,9 @@ const CreateConversationModal = ({ onClose, onConversationCreated }: CreateConve }; const getUserDisplayName = (user: User) => { - return user.user_metadata?.full_name || - user.user_metadata?.user_name || - user.email; + return user.user_metadata?.full_name || + user.user_metadata?.user_name || + user.email; }; return ( @@ -196,7 +220,7 @@ const CreateConversationModal = ({ onClose, onConversationCreated }: CreateConve className="w-full px-3 py-2 bg-slate-800/50 border border-slate-700 rounded-lg text-white placeholder-gray-400 focus:outline-none focus:border-cyan-400/50 focus:ring-1 focus:ring-cyan-400/50" /> - +
- +
{filteredUsers.map(user => { const isSelected = selectedUsers.some(u => u.id === user.id); - + return (
)} - +

{getUserDisplayName(user)}

{user.email}

- + {isSelected && ( )}
); })} - + {filteredUsers.length === 0 && (
No users found diff --git a/src/pages/LoginPage.tsx b/src/pages/LoginPage.tsx index 5381b914..d14192a0 100644 --- a/src/pages/LoginPage.tsx +++ b/src/pages/LoginPage.tsx @@ -11,7 +11,7 @@ export default function LoginPage() { const [showPassword, setShowPassword] = useState(false); const [isSuccess, setIsSuccess] = useState(false); const [theme, setTheme] = useState<'light' | 'dark'>('dark'); - const [particles, setParticles] = useState>([]); + const [particles, setParticles] = useState>([]); const { signInWithEmail, signInWithGithub } = useAuth(); const navigate = useNavigate(); @@ -20,14 +20,14 @@ export default function LoginPage() { // Check for saved theme preference or system preference const savedTheme = localStorage.getItem('theme') as 'light' | 'dark' | null; const systemPrefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches; - + let initialTheme = 'dark'; if (savedTheme) { initialTheme = savedTheme; } else if (!systemPrefersDark) { initialTheme = 'light'; } - + setTheme(initialTheme as 'light' | 'dark'); document.documentElement.classList.remove('light', 'dark'); document.documentElement.classList.add(initialTheme); @@ -48,7 +48,7 @@ export default function LoginPage() { }; window.addEventListener('themeChanged', handleThemeChange as EventListener); - + return () => { window.removeEventListener('themeChanged', handleThemeChange as EventListener); }; @@ -100,7 +100,7 @@ export default function LoginPage() { // Theme-based classes (aligned with footer) const isDark = theme === 'dark'; - + const themeClasses = { light: { background: 'from-blue-50 via-gray-50 to-purple-50', @@ -154,7 +154,7 @@ export default function LoginPage() { return (
- + {/* Animated Background */}
{/* Animated Particles */} @@ -172,14 +172,12 @@ export default function LoginPage() { }} /> ))} - + {/* Floating Orbs */} -
-
+
+
{/* Animated Grid */}
- + {/* Main Content */}
{/* Glass Container */}
{/* Outer Glow */}
- + {/* Glass Card */} -
setHoveredCard('main')} onMouseLeave={() => setHoveredCard(null)} > {/* Animated Top Border */} -
- +
+ {/* Cross Button - Positioned inside container at top-right */}
- +
{/* Header */}
@@ -241,17 +237,14 @@ export default function LoginPage() {
-
- +
+
-

Success! Redirecting...

+

Success! Redirecting...

@@ -265,19 +258,16 @@ export default function LoginPage() { onClick={handleGithubLogin} onMouseEnter={() => setHoveredCard('github')} onMouseLeave={() => setHoveredCard(null)} - className={`w-full group relative ${t.socialButton} backdrop-blur-sm border rounded-xl p-3 transition-all duration-300 hover:scale-[1.02] ${ - isDark ? 'hover:border-cyan-500/30' : 'hover:border-blue-500/30' - }`} + className={`w-full group relative ${t.socialButton} backdrop-blur-sm border rounded-xl p-3 transition-all duration-300 hover:scale-[1.02] ${isDark ? 'hover:border-cyan-500/30' : 'hover:border-blue-500/30' + }`} >
-
+
- Continue with GitHub + Continue with GitHub
@@ -286,15 +276,13 @@ export default function LoginPage() { className={`w-full group relative ${t.socialButton} backdrop-blur-sm border rounded-xl p-3 opacity-60 cursor-not-allowed`} >
-
+
Continue with Google - Soon + Soon
@@ -305,9 +293,8 @@ export default function LoginPage() {
- + Or with email
@@ -317,12 +304,10 @@ export default function LoginPage() { {error && (
-
-
+
+

{error}

@@ -344,9 +329,8 @@ export default function LoginPage() { required value={email} onChange={(e) => setEmail(e.target.value)} - className={`w-full pl-10 pr-4 py-2.5 ${t.input} rounded-xl ${t.inputText} placeholder-gray-500 focus:outline-none focus:border-blue-500/50 focus:ring-1 ${ - isDark ? 'focus:ring-blue-500/30' : 'focus:ring-blue-400/30' - } transition-all duration-300`} + className={`w-full pl-10 pr-4 py-2.5 ${t.input} rounded-xl ${t.inputText} placeholder-gray-500 focus:outline-none focus:border-blue-500/50 focus:ring-1 ${isDark ? 'focus:ring-blue-500/30' : 'focus:ring-blue-400/30' + } transition-all duration-300`} placeholder="you@example.com" />
@@ -360,9 +344,8 @@ export default function LoginPage() { Forgot? @@ -374,9 +357,8 @@ export default function LoginPage() { required value={password} onChange={(e) => setPassword(e.target.value)} - className={`w-full pl-10 pr-10 py-2.5 ${t.input} rounded-xl ${t.inputText} placeholder-gray-500 focus:outline-none focus:border-blue-500/50 focus:ring-1 ${ - isDark ? 'focus:ring-blue-500/30' : 'focus:ring-blue-400/30' - } transition-all duration-300`} + className={`w-full pl-10 pr-10 py-2.5 ${t.input} rounded-xl ${t.inputText} placeholder-gray-500 focus:outline-none focus:border-blue-500/50 focus:ring-1 ${isDark ? 'focus:ring-blue-500/30' : 'focus:ring-blue-400/30' + } transition-all duration-300`} placeholder="••••••••" />
@@ -402,11 +382,9 @@ export default function LoginPage() {