diff --git a/backend/main.py b/backend/main.py index 84d0c3a..4349aa0 100644 --- a/backend/main.py +++ b/backend/main.py @@ -238,8 +238,9 @@ app.add_middleware( CORSMiddleware, allow_origins=allowed_origins, allow_credentials=True, - allow_methods=["*"], + allow_methods=["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"], allow_headers=["*"], + expose_headers=["*"], ) # Include social network routers diff --git a/backend/social_db_utils.py b/backend/social_db_utils.py index f29de4e..a8d0f1e 100644 --- a/backend/social_db_utils.py +++ b/backend/social_db_utils.py @@ -1,6 +1,7 @@ import os import psycopg2 from psycopg2.extras import RealDictCursor +from psycopg2 import errors def get_db_connection(): @@ -38,17 +39,29 @@ def send_friend_request(sender_id: int, receiver_id: int): if existing: return dict(existing) - cur.execute( - """ - INSERT INTO friend_requests (sender_id, receiver_id) - VALUES (%s, %s) - RETURNING id, sender_id, receiver_id, status, created_at - """, - (sender_id, receiver_id) - ) - request = cur.fetchone() - conn.commit() - return dict(request) + try: + cur.execute( + """ + INSERT INTO friend_requests (sender_id, receiver_id) + VALUES (%s, %s) + RETURNING id, sender_id, receiver_id, status, created_at + """, + (sender_id, receiver_id) + ) + request = cur.fetchone() + conn.commit() + return dict(request) + except errors.UniqueViolation: + # Request already exists, fetch and return it + conn.rollback() + cur.execute( + "SELECT id, sender_id, receiver_id, status, created_at FROM friend_requests WHERE sender_id = %s AND receiver_id = %s", + (sender_id, receiver_id) + ) + existing_request = cur.fetchone() + if existing_request: + return dict(existing_request) + return {"error": "Friend request already exists"} finally: cur.close() conn.close() diff --git a/frontend/src/components/Chat.css b/frontend/src/components/Chat.css index 3d681b2..27761a7 100644 --- a/frontend/src/components/Chat.css +++ b/frontend/src/components/Chat.css @@ -151,6 +151,14 @@ color: var(--text-muted); font-size: 1.1rem; background: var(--bg); + padding: 2rem; + text-align: center; +} + +.no-selection p { + white-space: normal; + word-wrap: break-word; + max-width: 300px; } /* Messages */ diff --git a/frontend/src/components/Groups.css b/frontend/src/components/Groups.css index f6ef3f6..86eadaf 100644 --- a/frontend/src/components/Groups.css +++ b/frontend/src/components/Groups.css @@ -105,6 +105,14 @@ justify-content: center; color: #999; font-size: 1.1rem; + padding: 2rem; + text-align: center; +} + +.no-selection p { + white-space: normal; + word-wrap: break-word; + max-width: 300px; } /* Group Header */ diff --git a/frontend/src/components/Groups.jsx b/frontend/src/components/Groups.jsx index 02aeb3f..50f858e 100644 --- a/frontend/src/components/Groups.jsx +++ b/frontend/src/components/Groups.jsx @@ -80,7 +80,7 @@ export default function Groups({ showToast, onRecipeSelect }) { try { await createGroup(newGroupName, newGroupDescription, isPrivate); - showToast("Group created!", "success"); + showToast("הקבוצה נוצרה!", "success"); setNewGroupName(""); setNewGroupDescription(""); setIsPrivate(true); @@ -104,7 +104,7 @@ export default function Groups({ showToast, onRecipeSelect }) { async function handleAddMember(friendId) { try { await addGroupMember(selectedGroup.group_id, friendId); - showToast("Member added!", "success"); + showToast("החבר נוסף!", "success"); setShowAddMember(false); await loadGroupDetails(); } catch (error) { @@ -113,11 +113,11 @@ export default function Groups({ showToast, onRecipeSelect }) { } async function handleRemoveMember(userId) { - if (!confirm("Remove this member?")) return; + if (!confirm("להסיר את החבר הזה?")) return; try { await removeGroupMember(selectedGroup.group_id, userId); - showToast("Member removed", "info"); + showToast("החבר הוסר", "info"); await loadGroupDetails(); } catch (error) { showToast(error.message, "error"); @@ -137,7 +137,7 @@ export default function Groups({ showToast, onRecipeSelect }) { async function handleShareRecipe(recipeId) { try { await shareRecipeToGroup(selectedGroup.group_id, recipeId); - showToast("Recipe shared!", "success"); + showToast("המתכון שותף!", "success"); setShowShareRecipe(false); await loadGroupRecipes(); } catch (error) { @@ -146,22 +146,22 @@ export default function Groups({ showToast, onRecipeSelect }) { } if (loading) { - return
Loading groups...
; + return
טוען קבוצות...
; } return (
-

Recipe Groups

+

קבוצות מתכונים

{groups.length === 0 ? ( -

No groups yet. Create one!

+

אין קבוצות עדיין. צור אחת!

) : ( groups.map((group) => (
- {group.member_count} members · {group.recipe_count || 0} recipes + {group.member_count} חברים · {group.recipe_count || 0} מתכונים
)) @@ -188,25 +188,25 @@ export default function Groups({ showToast, onRecipeSelect }) {
{activeTab === "create" ? (
-

Create New Group

+

צור קבוצה חדשה

- + setNewGroupName(e.target.value)} - placeholder="Family Recipes, Vegan Friends, etc." + placeholder="מתכוני משפחה, חברים טבעונים וכו'" required />
- +