Skip to content

Commit 01b2fe1

Browse files
committed
Changed UI in ArchitectDetail page with some dynamic features
1 parent e37494d commit 01b2fe1

1 file changed

Lines changed: 145 additions & 25 deletions

File tree

src/pages/categories/ArchitectDetail.tsx

Lines changed: 145 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -3,39 +3,108 @@ import { useParams, useNavigate } from "react-router-dom";
33
import { supabase } from "@/integrations/supabase/client";
44
import Navbar from "@/components/Navbar";
55
import Footer from "@/components/Footer";
6-
import { Loader2 } from "lucide-react";
6+
import { Loader2, Upload } from "lucide-react";
7+
import { motion } from "framer-motion";
78

89
export default function ArchitectDetail() {
910
const { id } = useParams<{ id: string }>();
1011
const navigate = useNavigate();
1112
const [architect, setArchitect] = useState<any>(null);
13+
const [photos, setPhotos] = useState<string[]>([]);
1214
const [loading, setLoading] = useState(true);
15+
const [uploading, setUploading] = useState(false);
16+
const [photoError, setPhotoError] = useState<string | null>(null);
17+
const [currentUser, setCurrentUser] = useState<any>(null);
18+
const [canUpload, setCanUpload] = useState(false);
1319

20+
// ✅ Get current logged-in user
21+
useEffect(() => {
22+
const getUser = async () => {
23+
const {
24+
data: { user },
25+
} = await supabase.auth.getUser();
26+
setCurrentUser(user);
27+
};
28+
getUser();
29+
}, []);
30+
31+
// ✅ Fetch architect data
1432
useEffect(() => {
1533
const fetchArchitect = async () => {
1634
if (!id) return;
17-
1835
setLoading(true);
1936
const { data, error } = await supabase
2037
.from("architects")
2138
.select("*")
2239
.eq("id", Number(id))
2340
.single();
24-
25-
console.log("Supabase data:", data);
26-
console.log("Supabase error:", error);
27-
28-
if (error) {
29-
console.error("Error fetching architect:", error);
30-
} else {
31-
setArchitect(data);
32-
}
41+
if (!error && data) setArchitect(data);
3342
setLoading(false);
3443
};
35-
3644
fetchArchitect();
3745
}, [id]);
3846

47+
// ✅ Determine if user can upload
48+
useEffect(() => {
49+
if (currentUser && architect) {
50+
setCanUpload(currentUser.id === architect.user_id);
51+
}
52+
}, [currentUser, architect]);
53+
54+
// ✅ Fetch photos
55+
const fetchPhotos = async () => {
56+
if (!id) return;
57+
const folderName = `architect_${id}`;
58+
const { data, error } = await supabase.storage
59+
.from("Architects")
60+
.list(folderName, { limit: 100 });
61+
if (error) {
62+
setPhotoError("Failed to load photos. Please refresh the page.");
63+
return;
64+
}
65+
const urls =
66+
data?.map((file) => {
67+
const { data: publicData } = supabase.storage
68+
.from("Architects")
69+
.getPublicUrl(`${folderName}/${file.name}`);
70+
return publicData?.publicUrl || "";
71+
}) || [];
72+
setPhotos(urls.filter(Boolean));
73+
};
74+
75+
useEffect(() => {
76+
fetchPhotos();
77+
}, [id]);
78+
79+
// ✅ Handle photo upload
80+
const handleUpload = async (e: React.ChangeEvent<HTMLInputElement>) => {
81+
if (!canUpload) {
82+
alert("You can only upload photos to your own profile.");
83+
return;
84+
}
85+
try {
86+
setPhotoError(null);
87+
const file = e.target.files?.[0];
88+
if (!file || !id) return;
89+
setUploading(true);
90+
const folderName = `architect_${id}`;
91+
const fileName = `${Date.now()}_${file.name}`;
92+
const filePath = `${folderName}/${fileName}`;
93+
const { error: uploadError } = await supabase.storage
94+
.from("Architects")
95+
.upload(filePath, file);
96+
if (uploadError) {
97+
setPhotoError("Failed to upload photo. Please try again.");
98+
return;
99+
}
100+
await fetchPhotos();
101+
} catch {
102+
setPhotoError("Failed to upload photo. Please try again.");
103+
} finally {
104+
setUploading(false);
105+
}
106+
};
107+
39108
if (loading) {
40109
return (
41110
<div className="flex items-center justify-center h-screen">
@@ -62,28 +131,79 @@ export default function ArchitectDetail() {
62131
<div className="bg-white min-h-screen font-sans">
63132
<Navbar />
64133

65-
<div className="max-w-5xl mx-auto p-6 mt-10">
66-
<h1 className="text-4xl font-bold mb-4 text-center text-purple-700">
67-
{architect.name}
68-
</h1>
69-
<p className="text-center text-gray-600 mb-8 text-lg">
70-
{architect.specialization || "Architecture Expert"}
71-
</p>
134+
{/* ======= Banner Section ======= */}
135+
<div className="relative w-full h-64 md:h-80 bg-gray-100">
136+
<img
137+
src={architect.cover_url || photos[0] || "/placeholder-banner.jpg"}
138+
alt="Banner"
139+
className="w-full h-full object-cover"
140+
/>
141+
<div className="absolute inset-0 bg-black/25"></div>
142+
</div>
72143

73-
<div className="flex justify-center mb-8">
144+
{/* ======= Profile Section ======= */}
145+
<div className="max-w-6xl mx-auto p-6 -mt-20 relative z-10 bg-white rounded-2xl shadow-md">
146+
<div className="flex flex-col md:flex-row items-center md:items-start gap-6">
74147
<img
75148
src={architect.image_url || "/placeholder-architect.jpg"}
76149
alt={architect.name}
77-
className="w-full sm:w-2/3 rounded-2xl shadow-lg"
150+
className="w-40 h-40 object-cover rounded-2xl shadow-md border-4 border-white"
78151
/>
152+
<div className="flex flex-col flex-1">
153+
<h1 className="text-3xl font-bold text-purple-700">
154+
{architect.name}
155+
</h1>
156+
{/* Removed Rating Stars */}
157+
<p className="text-gray-700 mt-1">{architect.specialization}</p>
158+
<p className="text-gray-600 mt-3 leading-relaxed">
159+
{architect.description}
160+
</p>
161+
162+
{canUpload && (
163+
<label className="mt-5 inline-flex items-center gap-2 bg-purple-600 hover:bg-purple-700 text-white px-4 py-2 rounded-xl shadow cursor-pointer transition w-fit">
164+
<Upload size={18} />
165+
{uploading ? "Uploading..." : "Upload New Photo"}
166+
<input
167+
type="file"
168+
accept="image/*"
169+
className="hidden"
170+
onChange={handleUpload}
171+
disabled={uploading}
172+
/>
173+
</label>
174+
)}
175+
{photoError && (
176+
<p className="text-red-500 text-sm mt-2">{photoError}</p>
177+
)}
178+
</div>
79179
</div>
180+
</div>
80181

81-
<p className="text-gray-700 leading-relaxed text-center text-lg">
82-
{architect.description || "No additional details available."}
83-
</p>
182+
{/* ======= Gallery Section ======= */}
183+
<div className="max-w-6xl mx-auto p-6">
184+
<h2 className="text-2xl font-semibold mb-6 text-gray-800 border-b pb-2">
185+
Design Gallery
186+
</h2>
187+
{photos.length === 0 ? (
188+
<p className="text-center text-gray-500">
189+
No photos uploaded yet. Be the first to upload!
190+
</p>
191+
) : (
192+
<div className="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 gap-4">
193+
{photos.map((url, i) => (
194+
<motion.img
195+
key={i}
196+
src={url}
197+
alt={`Work ${i + 1}`}
198+
className="rounded-xl shadow-md object-cover w-full h-48"
199+
whileHover={{ scale: 1.05 }}
200+
/>
201+
))}
202+
</div>
203+
)}
84204
</div>
85205

86206
<Footer />
87207
</div>
88208
);
89-
}
209+
}

0 commit comments

Comments
 (0)