Heerrrs commited on
Commit
4ef0610
·
1 Parent(s): ea93dc4

Finalized project structure

Browse files
Files changed (1) hide show
  1. App.jsx +265 -0
App.jsx ADDED
@@ -0,0 +1,265 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import React, { useState, useEffect } from "react";
2
+ import mockProfiles from "./mockData";
3
+ import {
4
+ Home,
5
+ Search,
6
+ MessageCircle,
7
+ UserCircle,
8
+ Heart,
9
+ X,
10
+ } from "lucide-react";
11
+ import Login from "./components/Login";
12
+
13
+ function App() {
14
+ const [currentIndex, setCurrentIndex] = useState(0);
15
+ const [matches, setMatches] = useState([]);
16
+ const [showContactDialog, setShowContactDialog] = useState(false);
17
+ const [userPreferences, setUserPreferences] = useState(() => {
18
+ const saved = localStorage.getItem("userPreferences");
19
+ return saved ? JSON.parse(saved) : null;
20
+ });
21
+
22
+ const [sortedProfiles, setSortedProfiles] = useState([]);
23
+
24
+ useEffect(() => {
25
+ if (userPreferences) {
26
+ localStorage.setItem("userPreferences", JSON.stringify(userPreferences));
27
+ fetchCompatibility(userPreferences, mockProfiles);
28
+ }
29
+ }, [userPreferences]);
30
+
31
+ const fetchCompatibility = async (userProfile, candidates) => {
32
+ try {
33
+ const response = await fetch("http://127.0.0.1:8000/compute_compatibility", {
34
+ method: "POST",
35
+ headers: { "Content-Type": "application/json" },
36
+ body: JSON.stringify({
37
+ user_profile: userProfile,
38
+ candidate_profiles: candidates.map((p) => p.profile),
39
+ }),
40
+ });
41
+
42
+ const data = await response.json();
43
+ console.log("API Response:", data);
44
+
45
+ if (data.all_matches) {
46
+ const mergedProfiles = candidates.map((profile) => {
47
+ const match = data.all_matches.find((m) => m.profile === profile.profile.name);
48
+
49
+ return {
50
+ profile: {
51
+ name: profile.profile.name,
52
+ age: profile.profile.age || "N/A",
53
+ occupation: profile.profile.occupation || "Unknown",
54
+ imageUrl: profile.profile.imageUrl || "/default.jpg",
55
+ bio: profile.profile.bio || "No bio available.",
56
+ budget: profile.profile.budget || "N/A",
57
+ },
58
+ compatibility: match ? match.compatibility : 0,
59
+ matchReasons: match ? match.matchReasons : [],
60
+ };
61
+ });
62
+
63
+ console.log("Merged Profiles:", mergedProfiles);
64
+
65
+ setSortedProfiles(mergedProfiles.sort((a, b) => b.compatibility - a.compatibility));
66
+ }
67
+ } catch (error) {
68
+ console.error("Error fetching compatibility:", error);
69
+ setSortedProfiles(mockProfiles);
70
+ }
71
+ };
72
+
73
+ const handleLogout = () => {
74
+ setUserPreferences(null);
75
+ localStorage.removeItem("userPreferences");
76
+ setSortedProfiles([]);
77
+ setMatches([]);
78
+ setCurrentIndex(0);
79
+ };
80
+
81
+
82
+
83
+ const currentMatch = sortedProfiles[currentIndex];
84
+
85
+ const handleLike = () => {
86
+ setShowContactDialog(true);
87
+ };
88
+
89
+ const handleConfirmContact = () => {
90
+ setMatches([...matches, currentMatch.profile.id]);
91
+ setShowContactDialog(false);
92
+ handleNext();
93
+ };
94
+
95
+ const handleNext = () => {
96
+ setCurrentIndex((prevIndex) => prevIndex + 1);
97
+ };
98
+
99
+ if (!userPreferences?.isLoggedIn) {
100
+ return <Login setUserPreferences={setUserPreferences} />;
101
+ }
102
+
103
+ if (!currentMatch || currentIndex >= sortedProfiles.length) {
104
+ return (
105
+ <div className="min-h-screen bg-gray-100 flex items-center justify-center">
106
+ <div className="bg-white p-8 rounded-xl shadow-lg text-center">
107
+ <h2 className="text-2xl font-bold text-gray-800 mb-4">
108
+ No More Profiles
109
+ </h2>
110
+ <p className="text-gray-600">You've viewed all available matches!</p>
111
+ {matches.length > 0 && (
112
+ <div className="mt-4">
113
+ <p className="text-green-600 font-semibold">
114
+ You matched with {matches.length} potential roommates!
115
+ </p>
116
+ </div>
117
+ )}
118
+ <button
119
+ onClick={handleLogout}
120
+ className="mt-6 px-4 py-2 bg-red-500 text-white rounded-lg hover:bg-red-600 transition-colors"
121
+ >
122
+ Logout
123
+ </button>
124
+ </div>
125
+ </div>
126
+ );
127
+ }
128
+
129
+ return (
130
+ <div className="min-h-screen bg-gray-100">
131
+ <nav className="bg-white shadow-sm">
132
+ <div className="max-w-5xl mx-auto px-4">
133
+ <div className="flex justify-between items-center h-16">
134
+ <div className="flex space-x-8">
135
+ <Home className="w-6 h-6 text-green-600" />
136
+ <Search className="w-6 h-6 text-gray-400" />
137
+ <MessageCircle className="w-6 h-6 text-gray-400" />
138
+ <UserCircle className="w-6 h-6 text-gray-400" />
139
+ </div>
140
+ <div className="text-green-600 font-semibold">RoomieMatch AI</div>
141
+ <button
142
+ onClick={handleLogout}
143
+ className="px-4 py-2 text-sm text-red-600 hover:text-red-700"
144
+ >
145
+ Logout
146
+ </button>
147
+ </div>
148
+ </div>
149
+ </nav>
150
+
151
+ <div className="max-w-5xl mx-auto px-4 py-8">
152
+ <div className="flex flex-col items-center">
153
+ <div className="bg-white rounded-xl shadow-lg overflow-hidden max-w-md w-full">
154
+ {currentMatch?.profile ? (
155
+ <>
156
+ <div className="relative h-96">
157
+ <img
158
+ src={currentMatch.profile.imageUrl || "/default.jpg"} // Fallback Image
159
+ alt={currentMatch.profile.name || "Profile"}
160
+ className="w-full h-full object-cover"
161
+ />
162
+ <div className="absolute bottom-0 left-0 right-0 bg-gradient-to-t from-black/70 to-transparent p-6">
163
+ <div className="flex justify-between items-center">
164
+ <h2 className="text-2xl font-bold text-white">
165
+ {currentMatch.profile.name || "Unknown"}, {currentMatch.profile.age || "N/A"}
166
+ </h2>
167
+ <div className="bg-green-500 rounded-full px-3 py-1">
168
+ <span className="text-white font-semibold">
169
+ {currentMatch.compatibility || 0}% Match
170
+ </span>
171
+ </div>
172
+ </div>
173
+ <p className="text-white/90">{currentMatch.profile.occupation || "No Occupation Info"}</p>
174
+ </div>
175
+ </div>
176
+
177
+ <div className="p-6">
178
+ <div className="mb-4">
179
+ <h3 className="font-semibold text-gray-700 mb-2">About</h3>
180
+ <p className="text-gray-600">
181
+ {currentMatch.profile.bio || "No bio available."}
182
+ </p>
183
+ </div>
184
+
185
+ <div className="mb-4">
186
+ <h3 className="font-semibold text-gray-700 mb-2">
187
+ Match Reasons
188
+ </h3>
189
+ <ul className="list-disc list-inside text-sm text-gray-600">
190
+ {currentMatch.matchReasons?.length > 0 ? (
191
+ currentMatch.matchReasons.map((reason, index) => (
192
+ <li key={index}>{reason}</li>
193
+ ))
194
+ ) : (
195
+ <li>No specific reasons provided.</li>
196
+ )}
197
+ </ul>
198
+ </div>
199
+
200
+ <div className="mb-6">
201
+ <h3 className="text-lg font-semibold text-gray-800 mb-3">
202
+ Budget
203
+ </h3>
204
+ <p className="text-sm text-gray-600">
205
+ {currentMatch?.profile?.budget ? `$${currentMatch.profile.budget} per month` : "N/A"}
206
+ </p>
207
+ </div>
208
+
209
+ <div className="flex justify-center gap-4 mt-6">
210
+ <button
211
+ onClick={handleNext}
212
+ className="p-4 rounded-full bg-gray-100 hover:bg-gray-200 transition-colors"
213
+ >
214
+ <X className="w-6 h-6 text-gray-600" />
215
+ </button>
216
+ <button
217
+ onClick={handleLike}
218
+ className="p-4 rounded-full bg-green-100 hover:bg-green-200 transition-colors"
219
+ >
220
+ <Heart className="w-6 h-6 text-green-600" />
221
+ </button>
222
+ </div>
223
+ </div>
224
+ </>
225
+ ) : (
226
+ <div className="w-full h-96 flex items-center justify-center bg-gray-200">
227
+ <span className="text-gray-500">No Profile Found</span>
228
+ </div>
229
+ )}
230
+ </div>
231
+ </div>
232
+ </div>
233
+
234
+ {showContactDialog && (
235
+ <div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center p-4">
236
+ <div className="bg-white rounded-lg p-6 max-w-sm w-full">
237
+ <h3 className="text-xl font-bold text-gray-900 mb-4">
238
+ Contact {currentMatch?.profile?.name}?
239
+ </h3>
240
+ <p className="text-gray-600 mb-6">
241
+ Would you like to initiate contact with{" "}
242
+ {currentMatch?.profile?.name}?
243
+ </p>
244
+ <div className="flex justify-end gap-4">
245
+ <button
246
+ onClick={() => setShowContactDialog(false)}
247
+ className="px-4 py-2 text-gray-600 hover:text-gray-700"
248
+ >
249
+ Cancel
250
+ </button>
251
+ <button
252
+ onClick={handleConfirmContact}
253
+ className="px-4 py-2 bg-green-600 text-white rounded-lg hover:bg-green-700"
254
+ >
255
+ Confirm Contact
256
+ </button>
257
+ </div>
258
+ </div>
259
+ </div>
260
+ )}
261
+ </div>
262
+ );
263
+ }
264
+
265
+ export default App;