Move filter to the left

This commit is contained in:
MartinBraquet
2025-07-31 01:52:48 +02:00
parent cacab9a32d
commit b7ed9da37d
3 changed files with 148 additions and 120 deletions

View File

@@ -43,7 +43,7 @@ export default function HomePage() {
<div className="mt-8 flex space-x-4">
<Link
href="/learn-more"
className="px-6 py-3 bg-gray-200 text-gray-800 text-lg rounded-lg hover:bg-gray-300 transition"
className="px-6 py-3 bg-gray-200 dark:bg-gray-600 text-gray-800 dark:text-white text-lg rounded-lg hover:bg-gray-300 dark:hover:bg-gray-500 transition"
>
Learn More
</Link>

View File

@@ -229,24 +229,25 @@ export function ProfileFilters({filters, onFilterChange, onToggleFilter, onReset
</div>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 dark:text-white mb-1">Cause Areas</label>
<div className="flex flex-wrap gap-2">
{allCauseAreas.map((cause) => (
<button
key={cause.name}
onClick={() => onToggleFilter('causeAreas', cause.name)}
className={`px-3 py-1 text-sm rounded-full ${
filters.causeAreas.includes(cause.name)
? 'bg-green-100 dark:text-white dark:bg-green-900 text-green-800 border border-green-200'
: 'bg-gray-100 dark:text-white dark:bg-gray-700 text-gray-800 border border-gray-200 hover:bg-gray-200'
}`}
>
{cause.name}
</button>
))}
</div>
</div>
{/*<div>*/}
{/* <label className="block text-sm font-medium text-gray-700 dark:text-white mb-1">Cause Areas</label>*/}
{/* <div className="flex flex-wrap gap-2">*/}
{/* {allCauseAreas.map((cause) => (*/}
{/* <button*/}
{/* key={cause.name}*/}
{/* onClick={() => onToggleFilter('causeAreas', cause.name)}*/}
{/* className={`px-3 py-1 text-sm rounded-full ${*/}
{/* filters.causeAreas.includes(cause.name)*/}
{/* ? 'bg-green-100 dark:text-white dark:bg-green-900 text-green-800 border border-green-200'*/}
{/* : 'bg-gray-100 dark:text-white dark:bg-gray-700 text-gray-800 border border-gray-200 hover:bg-gray-200'*/}
{/* }`}*/}
{/* >*/}
{/* {cause.name}*/}
{/* </button>*/}
{/* ))}*/}
{/* </div>*/}
{/*</div>*/}
</div>
<div className="flex justify-end">

View File

@@ -13,6 +13,7 @@ export default function ProfilePage() {
const [profiles, setProfiles] = useState<ProfileData[]>([]);
const [loading, setLoading] = useState(true);
const [totalUsers, setTotalUsers] = useState<number>(0);
const [images, setImages] = useState<string[] | null>([])
const [filters, setFilters] = useState({
gender: '',
interests: [] as string[],
@@ -21,37 +22,59 @@ export default function ProfilePage() {
});
const fetchProfiles = useCallback(async () => {
try {
setLoading(true);
const params = new URLSearchParams();
try {
setLoading(true);
const params = new URLSearchParams();
if (filters.gender) params.append('gender', filters.gender);
if (filters.interests.length > 0) params.append('interests', filters.interests.join(','));
if (filters.causeAreas.length > 0) params.append('causeAreas', filters.causeAreas.join(','));
if (filters.searchQuery) params.append('search', filters.searchQuery);
// Fetch total users count (unfiltered)
const countResponse = await fetch('/api/profiles/count');
if (countResponse.ok) {
const { count } = await countResponse.json();
setTotalUsers(count);
if (filters.gender) params.append('gender', filters.gender);
if (filters.interests.length > 0) params.append('interests', filters.interests.join(','));
if (filters.causeAreas.length > 0) params.append('causeAreas', filters.causeAreas.join(','));
if (filters.searchQuery) params.append('search', filters.searchQuery);
// Fetch total users count (unfiltered)
const countResponse = await fetch('/api/profiles/count');
if (countResponse.ok) {
const {count} = await countResponse.json();
setTotalUsers(count);
}
const response = await fetch(`/api/profiles?${params.toString()}`);
const data = await response.json();
if (!response.ok) {
console.error(data.error || 'Failed to fetch profiles');
return;
}
setProfiles(data.profiles || []);
console.log(data.profiles);
for (const u of data.profiles) {
console.log(u);
const img = u.image;
let url = img;
if (img && !img.startsWith('http')) {
const imageResponse = await fetch(`/api/download?key=${img}`);
console.log(`imageResponse: ${imageResponse}`)
if (imageResponse.ok) {
const imageBlob = await imageResponse.json();
url = imageBlob['url'];
}
}
setImages(prev => [...prev, url]);
}
console.log(images);
} catch
(error) {
console.error('Error fetching profiles:', error);
} finally {
setLoading(false);
}
}
const response = await fetch(`/api/profiles?${params.toString()}`);
const data = await response.json();
if (!response.ok) {
console.error(data.error || 'Failed to fetch profiles');
return;
}
setProfiles(data.profiles || []);
} catch (error) {
console.error('Error fetching profiles:', error);
} finally {
setLoading(false);
}
}, [filters]);
,
[filters]
)
;
useEffect(() => {
fetchProfiles();
@@ -83,8 +106,8 @@ export default function ProfilePage() {
};
return (
<div className="min-h-screen flex flex-col items-center py-12 px-4 sm:px-6 lg:px-8">
<div className="w-full max-w-6xl">
<div className="min-h-screen py-12 px-4 sm:px-6 lg:px-8">
<div className="w-full max-w-7xl mx-auto">
<div className="flex justify-between items-end mb-8">
<h1 className="text-4xl sm:text-5xl font-extrabold">People</h1>
<div className="text-lg pb-1">
@@ -92,89 +115,93 @@ export default function ProfilePage() {
</div>
</div>
<ProfileFilters
filters={filters}
onFilterChange={handleFilterChange}
onToggleFilter={toggleFilter}
onReset={resetFilters}
/>
{loading ? (
<div className="flex justify-center my-12">
<LoadingSpinner/>
<div className="flex flex-col md:flex-row gap-8">
{/* Filters Sidebar */}
<div className="w-full md:w-80 flex-shrink-0">
<div className="sticky top-24">
<ProfileFilters
filters={filters}
onFilterChange={handleFilterChange}
onToggleFilter={toggleFilter}
onReset={resetFilters}
/>
</div>
</div>
) : (
<div className="grid gap-6 md:grid-cols-2 lg:grid-cols-1 w-full">
{profiles.length > 0 ? (
profiles.map((user) => (
<Link key={user.id} href={`/profiles/${user.id}`} className="group">
<div
className="border rounded-lg shadow-sm p-6 hover:shadow-md transition-shadow duration-300 h-full">
<div className="flex items-start space-x-4">
<div className="flex-1">
<h2 className="text-xl font-semibold group-hover:underline mb-1">
{user.name}
</h2>
{user?.profile?.description && (
<p className="text-sm line-clamp-2">
{user.profile.description}
</p>
)}
<div className="mt-3 flex flex-wrap gap-2">
{user.profile?.intellectualInterests && user.profile.intellectualInterests.length > 0 && (
<div>
{user.profile.intellectualInterests.slice(0, 3).map(({interest}) => (
<span key={interest?.id}
className="text-xs px-2 py-1 bg-blue-50 text-blue-700 dark:text-white dark:bg-gray-700 rounded-full">
{/* Profiles Grid */}
<div className="flex-1">
{loading ? (
<div className="flex justify-center py-12">
<LoadingSpinner/>
</div>
) : profiles.length > 0 ? (
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-1 xl:grid-cols-3 gap-6">
{profiles.map((user, idx) => (
<Link
key={user.id}
href={`/profiles/${user.id}`}
className="group block bg-white dark:bg-gray-800 rounded-lg overflow-hidden shadow hover:shadow-md transition-shadow duration-200 h-full"
>
<div className="p-4 h-full flex flex-col">
<div className="flex items-center space-x-4">
{/*<div className="flex-shrink-0">*/}
{/* <img*/}
{/* className="h-16 w-16 rounded-full object-cover"*/}
{/* src={images[idx]}*/}
{/* alt={``}*/}
{/* />*/}
{/*</div>*/}
<div className="flex-1 min-w-0">
<h3 className="text-lg font-medium text-gray-900 dark:text-white truncate">
{user.name}
</h3>
<p className="text-sm text-gray-500 dark:text-gray-400 line-clamp-2">
{user.profile?.description || ''}
</p>
</div>
</div>
<div className="mt-4 space-y-2 flex-grow">
{user.profile?.intellectualInterests && user.profile.intellectualInterests.length > 0 && (
<div className="flex flex-wrap gap-1">
{user.profile.intellectualInterests.slice(0, 3).map(({interest}) => (
<span key={interest?.id}
className="inline-block text-xs px-2 py-1 bg-blue-50 text-blue-700 dark:text-white dark:bg-gray-700 rounded-full">
{interest?.name}
</span>
))}
</div>
)}
{user.profile?.causeAreas && user.profile.causeAreas.length > 0 && (
<div>
{user.profile.causeAreas.slice(0, 3).map(({causeArea}) => (
<span key={causeArea?.id}
className="text-xs px-2 py-1 bg-blue-50 text-blue-700 dark:text-white dark:bg-gray-700 rounded-full">
{causeArea?.name}
</span>
))}
</div>
)}
</div>
))}
</div>
)}
{/*{user.profile?.causeAreas && user.profile.causeAreas.length > 0 && (*/}
{/* <div className="flex flex-wrap gap-1">*/}
{/* {user.profile.causeAreas.slice(0, 3).map(({causeArea}) => (*/}
{/* <span key={causeArea?.id}*/}
{/* className="inline-block text-xs px-2 py-1 bg-blue-50 text-blue-700 dark:text-white dark:bg-gray-700 rounded-full">*/}
{/* {causeArea?.name}*/}
{/* </span>*/}
{/* ))}*/}
{/* </div>*/}
{/*)}*/}
</div>
</div>
</div>
</Link>
))
</Link>
))}
</div>
) : (
<div className="col-span-full text-center py-12">
<svg
className="mx-auto h-12 w-12"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
aria-hidden="true"
>
<path
vectorEffect="non-scaling-stroke"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M9.172 16.172a4 4 0 015.656 0M9 10h.01M15 10h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
/>
<div className="text-center py-12">
<p className="text-gray-500 dark:text-gray-400">No profiles found matching your criteria.</p>
<svg className="mx-auto h-12 w-12 mt-4 text-gray-400" fill="none" viewBox="0 0 24 24"
stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5}
d="M9.172 16.172a4 4 0 015.656 0M9 10h.01M15 10h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/>
</svg>
<h3 className="mt-2 text-sm font-medium">No profiles found</h3>
<p className="mt-1 text-sm ">
<p className="mt-1 text-sm">
Try adjusting your search or filter to find what you're looking for.
</p>
</div>
)}
</div>
)}
</div>
</div>
</div>
);
}
)