diff --git a/client/.env.example b/client/.env.example index 63e7f21..16dbde7 100644 --- a/client/.env.example +++ b/client/.env.example @@ -1,4 +1,5 @@ REACT_EDITOR=code APP_ENV=DEVELOPMENT -NEXT_PUBLIC_BACKEND_URL="http://localhost:8000/api" \ No newline at end of file +# NEXT_PUBLIC_BACKEND_URL="http://localhost:8000/api" +NEXT_PUBLIC_BACKEND_URL="http://127.0.0.1:8000" \ No newline at end of file diff --git a/client/package-lock.json b/client/package-lock.json index 4df6af7..8cc1337 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -146,7 +146,6 @@ } ], "license": "MIT", - "peer": true, "engines": { "node": ">=18" }, @@ -170,7 +169,6 @@ } ], "license": "MIT", - "peer": true, "engines": { "node": ">=18" } @@ -1383,7 +1381,6 @@ "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.80.7.tgz", "integrity": "sha512-u2F0VK6+anItoEvB3+rfvTO9GEh2vb00Je05OwlUe/A0lkJBgW1HckiY3f9YZa+jx6IOe4dHPh10dyp9aY3iRQ==", "license": "MIT", - "peer": true, "dependencies": { "@tanstack/query-core": "5.80.7" }, @@ -1449,7 +1446,6 @@ "integrity": "sha512-AwAfQ2Wa5bCx9WP8nZL2uMZWod7J7/JSplxbTmBQ5ms6QpqNYm672H0Vu9ZVKVngQ+ii4R/byguVEUZQyeg44g==", "devOptional": true, "license": "MIT", - "peer": true, "dependencies": { "csstype": "^3.0.2" } @@ -1470,7 +1466,6 @@ "integrity": "sha512-4O3idHxhyzjClSMJ0a29AcoK0+YwnEqzI6oz3vlRf3xw0zbzt15MzXwItOlnr5nIth6zlY2RENLsOPvhyrKAQA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.34.1", "@typescript-eslint/types": "8.34.1", @@ -1675,7 +1670,6 @@ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -2117,7 +2111,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "caniuse-lite": "^1.0.30001718", "electron-to-chromium": "^1.5.160", @@ -2863,7 +2856,6 @@ "integrity": "sha512-GsGizj2Y1rCWDu6XoEekL3RLilp0voSePurjZIkxL3wlm5o5EC9VpgaP7lrCvjnkuLvzFBQWB3vWB3K5KQTveQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", @@ -3069,7 +3061,6 @@ "integrity": "sha512-ixmkI62Rbc2/w8Vfxyh1jQRTdRTF52VxwRVHl/ykPAmqG+Nb7/kNn+byLP0LxPgI7zWA16Jt82SybJInmMia3A==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@rtsao/scc": "^1.1.0", "array-includes": "^3.1.8", @@ -4568,7 +4559,6 @@ "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.6.tgz", "integrity": "sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w==", "license": "MIT", - "peer": true, "bin": { "jiti": "bin/jiti.js" } @@ -5493,7 +5483,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", @@ -5599,7 +5588,6 @@ "integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==", "dev": true, "license": "MIT", - "peer": true, "bin": { "prettier": "bin/prettier.cjs" }, @@ -5742,7 +5730,6 @@ "resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz", "integrity": "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==", "license": "MIT", - "peer": true, "engines": { "node": ">=0.10.0" } @@ -5752,7 +5739,6 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.0.tgz", "integrity": "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==", "license": "MIT", - "peer": true, "dependencies": { "scheduler": "^0.26.0" }, @@ -6630,7 +6616,6 @@ "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.17.tgz", "integrity": "sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og==", "license": "MIT", - "peer": true, "dependencies": { "@alloc/quick-lru": "^5.2.0", "arg": "^5.0.2", @@ -6885,7 +6870,6 @@ "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", "dev": true, "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" diff --git a/client/src/components/ui/navbar_organization.tsx b/client/src/components/ui/navbar_organization.tsx index 6268f89..41c7adf 100644 --- a/client/src/components/ui/navbar_organization.tsx +++ b/client/src/components/ui/navbar_organization.tsx @@ -87,7 +87,10 @@ const Header = () => { // to prevent the blur event from firing when they are clicked. onMouseDown={(e) => e.preventDefault()} > - + + {/* */} + + )} diff --git a/client/src/components/ui/popups/user_friends_add_popup.tsx b/client/src/components/ui/popups/user_friends_add_popup.tsx new file mode 100644 index 0000000..3681420 --- /dev/null +++ b/client/src/components/ui/popups/user_friends_add_popup.tsx @@ -0,0 +1,54 @@ +import { removeFriend, sendFriendRequest } from "../../../lib/api/friends"; +import { OtherUsers } from "../user_tables/user_friends_add_table"; + +const FriendAddItemModal = ({ + user, + onClose, +}: { + user: OtherUsers; + onClose: () => void; +}) => { + const handleSend = async () => { + await sendFriendRequest(user.id); + onClose(); + }; + + const handleRemove = async () => { + await removeFriend(user.id); + onClose(); + }; + + return ( +
+
+

{user.username}

+ +
+ {!user.is_friend && !user.request_sent && ( + + )} + + {user.is_friend && ( + + )} + + +
+
+
+ ); +}; + +export default FriendAddItemModal; diff --git a/client/src/components/ui/popups/user_friends_recieved_popup.tsx b/client/src/components/ui/popups/user_friends_recieved_popup.tsx new file mode 100644 index 0000000..2907b94 --- /dev/null +++ b/client/src/components/ui/popups/user_friends_recieved_popup.tsx @@ -0,0 +1,101 @@ +// src/components/popups/user_groups_select_popup.tsx + +// import Link from "next/link"; +// import React from "react"; +import React, { useEffect, useState } from "react"; + +import { + acceptFriendRequest, + declineFriendRequest, + getIncomingRequests, +} from "../../../lib/api/friends"; + +export type FriendRequest = { + id: number; + sender: { + id: number; + username: string; + }; +}; + +type Props = { + isOpen: boolean; + onClose: () => void; +}; + +function UserFriendsRecievedPopup({ isOpen, onClose }: Props) { + const [requests, setRequests] = useState([]); + const [loading, setLoading] = useState(false); + useEffect(() => { + if (!isOpen) return; + + setLoading(true); + getIncomingRequests() + .then((res) => { + console.log("Incoming requests:", res.data); + setRequests(res.data); + }) + + .finally(() => setLoading(false)); + }, [isOpen]); + + if (!isOpen) return null; + return ( +
+
+
+

Friend Requests

+ +
+ + {loading &&

Loading…

} + + {!loading && requests.length === 0 && ( +

No friend requests.

+ )} + +
+ {requests.map((r) => ( +
+ {r.sender.username} + +
+ + + +
+
+ ))} +
+
+
+ ); +} +export default UserFriendsRecievedPopup; diff --git a/client/src/components/ui/popups/user_friends_search_popup.tsx b/client/src/components/ui/popups/user_friends_search_popup.tsx new file mode 100644 index 0000000..2b53c7a --- /dev/null +++ b/client/src/components/ui/popups/user_friends_search_popup.tsx @@ -0,0 +1,83 @@ +// src/components/popups/user_groups_add_popup.tsx + +import Link from "next/link"; +import React from "react"; + +type ItemModalProps = { + // user={selectedItem; + isOpen: boolean; + onClose: () => void; +}; + +function UserFriendsSearchPopup({ isOpen, onClose }: ItemModalProps) { + if (!isOpen) return null; + return ( +
+
+
+ {/* Header */} +
+

Add Friends

+ +
+ + {/* Body */} +
+ {/* Start */} +
+
+ + +
+
+ {/* End */} + {/*

ID: Ummm ~~

*/} +
+ + {/* Footer */} +
+ + + + + +
+
+
+ ); +} +export default UserFriendsSearchPopup; diff --git a/client/src/components/ui/popups/user_friends_select_popup.tsx b/client/src/components/ui/popups/user_friends_select_popup.tsx new file mode 100644 index 0000000..219c015 --- /dev/null +++ b/client/src/components/ui/popups/user_friends_select_popup.tsx @@ -0,0 +1,136 @@ +// src/components/popups/user_groups_select_popup.tsx + +import Link from "next/link"; +import React from "react"; + +import { removeFriend } from "../../../lib/api/friends"; +import ProfilePicture from "../profile_picture"; + +type MyFriends = { + id: number; + // name: string; + username: string; +}; + +type ItemModalProps = { + isOpen: boolean; + item: MyFriends | null; + onClose: () => void; + onFriendRemoved: (id: number) => void; +}; + +function UserFriendsSelectPopup({ + isOpen, + item, + onClose, + onFriendRemoved, +}: ItemModalProps) { + if (!item) return; + const handleRemoveFriend = async () => { + try { + await removeFriend(item.id); + onFriendRemoved(item.id); + onClose(); + } catch (err) { + console.error("Failed to remove friend", err); + } + }; + if (!isOpen || !item) return null; + return ( +
+
+
+
+ {/* Header */} + {/* Avatar */} + + {/* Implement below when we implement a profile picture field */} + + {/* {profile_picture === Null ? ( + + ) : ( + item.profile_picture + )} + } */} + + {/* Text column */} +
+ {/* Name */} + {/* Replace below with logic to forward to unique profile of other user */} + +

+ {item.username} +

+ + + {/* other */} +
+

User: {item.id}

+ +

+ {" "} + Mutual Groups: {item.id} +

+ +
+
+ +
+ + {/* Footer */} +
+ + + + + +
+
+
+ ); +} +export default UserFriendsSelectPopup; diff --git a/client/src/components/ui/popups/user_friends_sent_popup.tsx b/client/src/components/ui/popups/user_friends_sent_popup.tsx new file mode 100644 index 0000000..aebb030 --- /dev/null +++ b/client/src/components/ui/popups/user_friends_sent_popup.tsx @@ -0,0 +1,80 @@ +// src/components/popups/user_groups_select_popup.tsx + +// import Link from "next/link"; +// import React from "react"; +import React, { useEffect, useState } from "react"; + +import { cancelFriendRequest,getSentRequests } from "../../../lib/api/friends"; + +export type FriendRequest = { + id: number; + receiver: { + id: number; + username: string; + }; +}; + +type Props = { + isOpen: boolean; + onClose: () => void; +}; + +function UserFriendsSentPopup({ isOpen, onClose }: Props) { + const [requests, setRequests] = useState([]); + const [loading, setLoading] = useState(false); + useEffect(() => { + if (!isOpen) return; + + setLoading(true); + getSentRequests() + .then((res) => setRequests(res.data)) + .finally(() => setLoading(false)); + }, [isOpen]); + + if (!isOpen) return null; + return ( +
+
+
+

Friend Requests

+ +
+ + {loading &&

Loading…

} + + {!loading && requests.length === 0 && ( +

No friend requests.

+ )} + +
+ {requests.map((r) => ( +
+ {r.receiver.username} + +
+ +
+
+ ))} +
+
+
+ ); +} +export default UserFriendsSentPopup; diff --git a/client/src/components/ui/popups/user_groups_select_popup.tsx b/client/src/components/ui/popups/user_groups_select_popup.tsx new file mode 100644 index 0000000..4886c63 --- /dev/null +++ b/client/src/components/ui/popups/user_groups_select_popup.tsx @@ -0,0 +1,115 @@ +// src/components/popups/user_groups_select_popup.tsx + +import Link from "next/link"; +import React from "react"; + +type MyGroups = { + id: number; + name: string; +}; + +type ItemModalProps = { + isOpen: boolean; + item: MyGroups | null; + onClose: () => void; +}; + +function UserGroupsSelectPopup({ isOpen, item, onClose }: ItemModalProps) { + if (!isOpen || !item) return null; + return ( +
+
+
+
+ {/* Header */} + {/* Avatar */} +
+
+
+
+
+
+ {/* Text column */} +
+ {/* Name */} + +

+ {item.name} +

+ + {/* other */} +
+

Group: {item.id}

+ +

+ {" "} + Mutual Friends: {item.id} +

+ +
+ +

+ View Inventory +

+ +
+ +
+ + {/* Footer */} +
+ + + + + +
+
+
+ ); +} +export default UserGroupsSelectPopup; diff --git a/client/src/components/ui/popups/user_inventory_edit_popup.tsx b/client/src/components/ui/popups/user_inventory_edit_popup.tsx new file mode 100644 index 0000000..79a6123 --- /dev/null +++ b/client/src/components/ui/popups/user_inventory_edit_popup.tsx @@ -0,0 +1,112 @@ +// src/components/popups/user_groups_select_popup.tsx + +import Link from "next/link"; +import React from "react"; + +type InventoryItem = { + id: number; + name: string; + date: string; + dateNew: Date; +}; + +type ItemModalProps = { + isOpen: boolean; + item: InventoryItem | null; + onClose: () => void; +}; + +function UserInventoryEditPopup({ isOpen, item, onClose }: ItemModalProps) { + if (!isOpen || !item) return null; + return ( +
+
+
+
+ {/* Avatar */} +
+
+
+
+
+
+ {/* Text column */} +
+ {/* Name */} +

+ {item.name} +

+ {/* other */} +
+

Category: {item.id}

+

+ {" "} + Owned by: {item.id} +

+
+
+ +
+ {/* Body */} + {/* Date Change */} +
+

+ Extend/Reduce Return Date: +

{" "} + + {/* Description */} +
+ +