@@ -3,39 +3,108 @@ import { useParams, useNavigate } from "react-router-dom";
33import { supabase } from "@/integrations/supabase/client" ;
44import Navbar from "@/components/Navbar" ;
55import Footer from "@/components/Footer" ;
6- import { Loader2 } from "lucide-react" ;
6+ import { Loader2 , Upload } from "lucide-react" ;
7+ import { motion } from "framer-motion" ;
78
89export 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