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 && (
-
@@ -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() {