Skip to content

Commit 1bda829

Browse files
authored
layout reviews (#114)
1 parent 85e14c0 commit 1bda829

File tree

1 file changed

+125
-94
lines changed

1 file changed

+125
-94
lines changed

my-app/src/views/ReviewView.jsx

Lines changed: 125 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,18 @@ export function ReviewView(props) {
66
const { formData, setFormData } = props;
77
const [showGradeOptions, setShowGradeOptions] = useState(false);
88
const [showRecommendOptions, setShowRecommendOptions] = useState(false);
9+
const [expandedReviews, setExpandedReviews] = useState({});
910
const gradeRef = useRef(null);
1011
const recommendRef = useRef(null);
1112

13+
// Function to get user initials from their name
14+
const getInitials = (name) => {
15+
if (!name) return "N/A";
16+
const words = name.trim().split(" ");
17+
if (words.length === 1) return words[0][0]?.toUpperCase() || "N/A";
18+
return `${words[0][0]?.toUpperCase() || ""}${words[words.length - 1][0]?.toUpperCase() || ""}`;
19+
};
20+
1221
useEffect(() => {
1322
function handleClickOutside(event) {
1423
if (gradeRef.current && !gradeRef.current.contains(event.target)) {
@@ -25,6 +34,14 @@ export function ReviewView(props) {
2534
};
2635
}, []);
2736

37+
// Function to toggle expanded state for a review
38+
const toggleExpanded = (index) => {
39+
setExpandedReviews((prev) => ({
40+
...prev,
41+
[index]: !prev[index],
42+
}));
43+
};
44+
2845
return (
2946
<div className="max-w-3xl mx-auto">
3047
<div className="mt-6">
@@ -50,7 +67,6 @@ export function ReviewView(props) {
5067
/>
5168
</div>
5269

53-
5470
{/* Professor Rating */}
5571
<div className="text-center">
5672
<p className="font-semibold text-gray-700 text-sm mb-1">Professor Rating</p>
@@ -73,7 +89,7 @@ export function ReviewView(props) {
7389
{formData.grade || "Select"}
7490
</div>
7591
{showGradeOptions && (
76-
<div className="absolute left-1/2 -translate-x-1/2 mt-2 bg-white p-2 rounded-md shadow-lg Disturbance shadow-lg z-10 flex space-x-2 animate-fadeIn">
92+
<div className="absolute left-1/2 -translate-x-1/2 mt-2 bg-white p-2 rounded-md shadow-lg z-10 flex space-x-2 animate-fadeIn">
7793
{grades.map((grade) => (
7894
<button
7995
key={grade}
@@ -139,14 +155,14 @@ export function ReviewView(props) {
139155
</div>
140156

141157
<div className="mt-4">
142-
<input
143-
type="text"
144-
placeholder="Enter professor name"
145-
maxLength={100}
146-
className="w-full border border-gray-300 rounded-md p-2 text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 transition-colors duration-200"
147-
value={formData.professorName}
148-
onChange={(e) => setFormData({ ...formData, professorName: e.target.value })}
149-
/>
158+
<input
159+
type="text"
160+
placeholder="Enter professor name"
161+
maxLength={100}
162+
className="w-full border border-gray-300 rounded-md p-2 text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 transition-colors duration-200"
163+
value={formData.professorName}
164+
onChange={(e) => setFormData({ ...formData, professorName: e.target.value })}
165+
/>
150166
</div>
151167

152168
<div className="relative mt-4">
@@ -173,92 +189,107 @@ export function ReviewView(props) {
173189
<div className="mt-6">
174190
<h3 className="text-lg font-semibold text-gray-800 mb-4">Previous Reviews</h3>
175191
<div className="space-y-6">
176-
{/* {props.reviews.map((rev, i) => ( */}
177-
{[...props.reviews].sort((a, b) => new Date(b.timestamp) - new Date(a.timestamp)).map((rev, i) => (
178-
<div key={i} className="bg-white shadow-md rounded-lg p-4">
179-
<div className="flex justify-between items-center mb-2">
180-
<p className="font-semibold text-gray-800">{rev.userName}</p>
181-
<p className="text-sm text-gray-500">
182-
Posted on {new Date(rev.timestamp).toLocaleDateString("en-US", {
183-
month: "long",
184-
day: "numeric",
185-
year: "numeric",
186-
})}
187-
</p>
188-
</div>
189-
<div className="grid grid-cols-2 md:grid-cols-3 gap-4 mb-2">
190-
<div>
191-
<p className="text-sm font-semibold text-gray-700">Overall Rating</p>
192-
{/* <RatingComponent
193-
className="flex space-x-1 text-sm"
194-
value={rev.overallRating}
195-
readOnly={true}
196-
/> */}
197-
{rev.overallRating > 0 ? (
198-
<RatingComponent
199-
className="flex space-x-1 text-sm"
200-
value={rev.overallRating}
201-
readOnly={true}
202-
/>
203-
) : (
204-
<p className="text-sm text-gray-600">N/A</p>
205-
)}
206-
207-
</div>
208-
<div>
209-
<p className="text-sm font-semibold text-gray-700">Difficulty Rating</p>
210-
{rev.difficultyRating > 0 ? (
211-
<RatingComponent
212-
className="flex space-x-1 text-sm"
213-
value={rev.difficultyRating}
214-
readOnly={true}
215-
/>
216-
) : (
217-
<p className="text-sm text-gray-600">N/A</p>
218-
)}
219-
220-
</div>
221-
<div>
222-
<p className="text-sm font-semibold text-gray-700">Professor Rating</p>
223-
{rev.professorRating > 0 ? (
224-
<RatingComponent
225-
className="flex space-x-1 text-sm"
226-
value={rev.professorRating}
227-
readOnly={true}
228-
/>
229-
) : (
230-
<p className="text-sm text-gray-600">N/A</p>
231-
)}
232-
233-
</div>
234-
<div>
235-
<p className="text-sm font-semibold text-gray-700">Professor</p>
236-
{/* <p className="text-sm text-gray-600">{rev.professorName}</p> */}
237-
<p className="text-sm text-gray-600">{rev.professorName || "N/A"}</p>
238-
239-
</div>
240-
<div>
241-
<p className="text-sm font-semibold text-gray-700">Grade</p>
242-
{/* <p className="text-sm text-gray-600">{rev.grade}</p> */}
243-
<p className="text-sm text-gray-600">{rev.grade || "N/A"}</p>
244-
245-
</div>
246-
<div>
247-
<p className="text-sm font-semibold text-gray-700">Recommended</p>
248-
{/* <p className="text-sm text-gray-600">{rev.recommend ? "Yes" : "No"}</p> */}
249-
<p className="text-sm text-gray-600">
250-
{rev.recommend === true ? "Yes" : rev.recommend === false ? "No" : "N/A"}
251-
</p>
192+
{[...props.reviews]
193+
.sort((a, b) => new Date(b.timestamp) - new Date(a.timestamp))
194+
.map((rev, i) => {
195+
const maxLength = 200;
196+
const isLongReview = rev.text.length > maxLength;
197+
const isExpanded = expandedReviews[i] || false;
252198

199+
return (
200+
<div key={i} className="bg-white shadow-md rounded-lg p-4">
201+
<div className="flex justify-between items-center mb-2">
202+
<div className="flex items-center gap-2">
203+
<div className="w-6 h-6 rounded-full bg-blue-600 text-white flex items-center justify-center text-sm font-semibold">
204+
{getInitials(rev.userName)}
205+
</div>
206+
<p className="font-semibold text-gray-800">{rev.userName}</p>
207+
</div>
208+
<p className="text-sm text-gray-500">
209+
Posted on{" "}
210+
{new Date(rev.timestamp).toLocaleDateString("en-US", {
211+
month: "long",
212+
day: "numeric",
213+
year: "numeric",
214+
})}
215+
</p>
216+
</div>
217+
<div className="grid grid-cols-2 md:grid-cols-3 gap-4 mb-2">
218+
<div>
219+
<p className="text-sm font-semibold text-gray-700">Overall Rating</p>
220+
{rev.overallRating > 0 ? (
221+
<RatingComponent
222+
className="flex space-x-1 text-sm"
223+
value={rev.overallRating}
224+
readOnly={true}
225+
/>
226+
) : (
227+
<p className="text-sm text-gray-600">N/A</p>
228+
)}
229+
</div>
230+
<div>
231+
<p className="text-sm font-semibold text-gray-700">Difficulty Rating</p>
232+
{rev.difficultyRating > 0 ? (
233+
<RatingComponent
234+
className="flex space-x-1 text-sm"
235+
value={rev.difficultyRating}
236+
readOnly={true}
237+
/>
238+
) : (
239+
<p className="text-sm text-gray-600">N/A</p>
240+
)}
241+
</div>
242+
<div>
243+
<p className="text-sm font-semibold text-gray-700">Professor Rating</p>
244+
{rev.professorRating > 0 ? (
245+
<RatingComponent
246+
className="flex space-x-1 text-sm"
247+
value={rev.professorRating}
248+
readOnly={true}
249+
/>
250+
) : (
251+
<p className="text-sm text-gray-600">N/A</p>
252+
)}
253+
</div>
254+
<div>
255+
<p className="text-sm font-semibold text-gray-700">Professor</p>
256+
<p className="text-sm text-gray-600">{rev.professorName || "N/A"}</p>
257+
</div>
258+
<div>
259+
<p className="text-sm font-semibold text-gray-700">Grade</p>
260+
<p className="text-sm text-gray-600">{rev.grade || "N/A"}</p>
261+
</div>
262+
<div>
263+
<p className="text-sm font-semibold text-gray-700">Recommended</p>
264+
<p className="text-sm text-gray-600">
265+
{rev.recommend === true ? "Yes" : rev.recommend === false ? "No" : "N/A"}
266+
</p>
267+
</div>
268+
</div>
269+
<div>
270+
<p className="text-sm font-semibold text-gray-700">Review</p>
271+
<div
272+
className={`text-sm text-gray-600 transition-all duration-300 ${
273+
isExpanded || !isLongReview ? "" : "max-h-10 overflow-hidden"
274+
}`}
275+
>
276+
{isExpanded || !isLongReview
277+
? rev.text
278+
: `${rev.text.slice(0, maxLength)}...`}
279+
</div>
280+
{isLongReview && (
281+
<button
282+
onClick={() => toggleExpanded(i)}
283+
className="mt-1 text-blue-600 hover:text-blue-800 text-sm font-semibold focus:outline-none"
284+
>
285+
{isExpanded ? "Read Less" : "Read More"}
286+
</button>
287+
)}
288+
</div>
253289
</div>
254-
</div>
255-
<div>
256-
<p className="text-sm font-semibold text-gray-700">Review</p>
257-
<p className="text-sm text-gray-600">{rev.text}</p>
258-
</div>
259-
</div>
260-
))}
261-
{props.reviews.length === 0 && <p className="text-gray-600">No reviews yet.</p>}
290+
);
291+
})}
292+
{props.reviews.length === 0 && <p className="text-sm text-gray-600">No reviews yet.</p>}
262293
</div>
263294
</div>
264295
</div>

0 commit comments

Comments
 (0)