feat:search + tags

This commit is contained in:
Damien Ostler 2024-05-26 19:49:20 -04:00
parent 812c01e518
commit b12d3e1752
6 changed files with 186 additions and 55 deletions

View File

@ -2,11 +2,39 @@ import { NextResponse } from "next/server";
import { createClient } from "@/utils/supabase/server";
export async function GET(request: Request) {
export async function POST(request: Request) {
const supabase = createClient();
const url = new URL(request.url);
const search = url.searchParams.get("search");
const data = await request.json();
const tags = data.tags;
if(tags.length === 0){
let { data: galleries, error } = await supabase
.from('galleries')
.select('*')
return NextResponse.json(galleries)
.ilike('name', `%${search}%`)
.ilike('description', `%${search}%`);
return NextResponse.json(galleries);
}
else{
// Rest of the code...
let { data: galleries, error } = await supabase
.from('galleries')
.select('*')
.contains('tags', tags)
.ilike('name', `%${search}%`)
.ilike('description', `%${search}%`)
.order('created_at', { ascending: false });
return NextResponse.json(galleries);
return NextResponse.json(galleries);
}
}
// const tagsResponse = await fetch(`/api/galleries/tags?search=${search}`);
// const tagsData = await tagsResponse.json();
// const galleriesWithTagData = galleriesData.map((gallery: any) => {
// const tags = tagsData.filter((tag: any) => gallery.tags.includes(tag.id));
// return { ...gallery, tags };
// });

View File

@ -0,0 +1,12 @@
import { NextResponse } from "next/server";
import { createClient } from "@/utils/supabase/server";
export async function GET(request: Request) {
const supabase = createClient();
let { data: tags, error } = await supabase
.from('tags')
.select('*')
return NextResponse.json(tags)
}

View File

@ -18,7 +18,10 @@ function PageComponent() {
const [user, setUser] = useState<User | null>(null);
const [loading, setLoading] = useState<boolean>(true);
const [selectedGallery, setSelectedGallery] = useState<string | null>(null);
const [tags, setTags] = useState<any[]>([]);
const [search, setSearch] = useState<string>('');
const [galleryColumns, setColumns] = useState<number>(0);
const [selectedTags, setSelectedTags] = useState<number[]>([]);
const generateRandomString = function (length:number) {
let result = '';
let characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
@ -43,58 +46,122 @@ function PageComponent() {
}
const getData = async () => {
const galleriesResponse = await fetch('/api/galleries');
const galleriesData = await galleriesResponse.json();
let { data: { user } } = await supabase.auth.getUser();
const galleriesResponse = await fetch(`/api/galleries?search=`+search, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ tags:selectedTags })
});
console.log(galleriesResponse)
const galleriesData = await galleriesResponse.json();
const tagsResponse = await fetch(`/api/galleries/tags`);
const tagsData = await tagsResponse.json();
setGalleries(galleriesData);
setTags(tagsData);
setUser(user);
setLoading(false);
}
useEffect(() => {
getData();
}, []);
return ( ( user ? (
}, [selectedTags,search]);
const handleTagClick = (tag: number) => {
if (selectedTags.includes(tag)) {
setSelectedTags(selectedTags.filter((selectedTag) => selectedTag !== tag));
} else {
setSelectedTags([...selectedTags, tag]);
}
console.log(selectedTags)
};
return (
user ? (
<div className="w-full h-full flex justify-center ">
<div className="flex-1 w-full h-full flex flex-col gap-20">
<>
<div className="absolute pl-8 w-2/4 left-1/2 h-full overflow-hidden z-20 animate-flip-up animate-ease-out">
<section className="neroshi-blue-900 h-50 p-8 pt-20 opacity-30 hover:opacity-100">
<div className="container mx-auto py-8">
<input
className="w-full text-neroshi-blue-950 h-16 px-3 rounded mb-8 focus:outline-none focus:shadow-outline text-xl px-8 shadow-lg"
type="search"
placeholder="Search..."
onChange={(e) => setSearch(e.target.value)}
/>
<nav className="grid grid-cols-4 gap-4">
{tags.map((tag, index) => (
<a
key={index}
className={`rounded-lg no-underline text-white py-3 px-4 font-medium text-center ${
selectedTags.includes(tag.id) ? 'bg-neroshi-blue-950 hover:bg-neroshi-blue-900' : 'bg-neroshi-blue-800 hover:bg-neroshi-blue-700'
}`}
href="#"
onClick={() => handleTagClick(tag.id)}
>
{tag.name}
</a>
))}
</nav>
</div>
</section>
</div>
<div className="absolute w-full h-full overflow-hidden z-0 animate-flip-up animate-ease-out">
<img src="gallery_girl.png" className="float-right object-cover h-screen w-3/6" alt="Background" />
<img
src="gallery_girl.png"
className="float-right object-cover h-screen w-3/6"
alt="Background"
/>
</div>
<div className="absolute items-center w-3/5 h-full ml-10 z-0 overflow-hidden animate-in animate-ease-out">
<div className="absolute items-center w-2/4 h-full ml-10 z-0 overflow-hidden animate-in animate-ease-out">
<div className="grid grid-cols-3 gap-y-36 gap-x-10 h-full overflow-y-auto no-scrollbar pt-20">
{galleries.map((gallery, index) => (
<GalleryThumbnail key={index} id={gallery.id} title={gallery.name} columns={gallery.columns} subscription={gallery.tier as string} onSelect={selectGallery}></GalleryThumbnail>
{galleries && galleries.map((gallery, index) => (
<GalleryThumbnail
key={index}
id={gallery.id}
title={gallery.name}
columns={gallery.columns}
subscription={gallery.tier as string}
onSelect={selectGallery}
></GalleryThumbnail>
))}
<div className="pt-10">
</div>
<div className="pt-10">
</div>
<div className="pt-10"></div>
<div className="pt-10"></div>
</div>
</div>
</>
</div>
{(isOpen ? (
{isOpen ? (
<>
<div className={`fixed inset-0 transition-opacity z-30 ${isOpen ? 'animate-in' : 'fade-out'}`} aria-hidden="true">
<div className="absolute inset-0 bg-neroshi-blue-900 opacity-70 z-30" onClick={()=>setIsOpen(false)} >
</div>
<div
className={`fixed inset-0 transition-opacity z-30 ${
isOpen ? "animate-in" : "fade-out"
}`}
aria-hidden="true"
>
<div
className="absolute inset-0 bg-neroshi-blue-900 opacity-70 z-30"
onClick={() => setIsOpen(false)}
></div>
<div className="absolute inset-0 overflow-y-auto overflow-x-hidden no-scrollbar pt-2 w-full p-20 z-30">
<Gallery id={selectedGallery as string} columns={galleryColumns} closeMenu={() => closeGallery()}></Gallery>
<Gallery
id={selectedGallery as string}
columns={galleryColumns}
closeMenu={() => closeGallery()}
></Gallery>
</div>
</div>
</>
): null)}
) : null}
</div>
) : (
<h1>loading</h1>
)));
)
);
}
export default PageComponent;

View File

@ -2,6 +2,7 @@ import { use, useState } from 'react';
import { useEffect } from 'react';
import { render } from 'react-dom';
import Masonry from 'react-masonry-css';
import PanZoom from 'react-easy-panzoom';
interface GalleryProps {
id: string;
@ -173,6 +174,17 @@ const Gallery = ({ id, columns, closeMenu }: GalleryProps) => {
Back
</button>
{renderButtons()}
{selectedImage ? (<PanZoom>
<img
src={images[currentIndex]}
style={{ objectFit: "contain", maxWidth: "100%", maxHeight: "calc(100vh - 20px)", pointerEvents:"none" }}
className="cursor-pointer animate-in w-full h-auto"
onClick={() => close()}
/>
</PanZoom>
) : (
<div
className="z-30 pb-10"
style={{
@ -180,16 +192,6 @@ const Gallery = ({ id, columns, closeMenu }: GalleryProps) => {
alignItems: "flex-start",
}}
>
<>
{renderButtons()}
{selectedImage ? (
<img
src={images[currentIndex]}
style={{ objectFit: "contain", maxWidth: "100%", maxHeight: "calc(100vh - 20px)" }}
className="cursor-pointer animate-in w-full h-auto"
onClick={() => close()}
/>
) : (
<Masonry
breakpointCols={columns}
className="my-masonry-grid"
@ -205,9 +207,10 @@ const Gallery = ({ id, columns, closeMenu }: GalleryProps) => {
/>
))}
</Masonry>
)}
<>
</>
</div>
)}
</div>
);
}

20
package-lock.json generated
View File

@ -18,6 +18,7 @@
"prop-types": "^15.8.1",
"react": "18.2.0",
"react-dom": "18.2.0",
"react-easy-panzoom": "^0.4.4",
"react-masonry-css": "^1.0.16",
"react-responsive-masonry": "^2.2.0",
"simplex-noise": "^4.0.1",
@ -2154,6 +2155,17 @@
"react": "^18.2.0"
}
},
"node_modules/react-easy-panzoom": {
"version": "0.4.4",
"resolved": "https://registry.npmjs.org/react-easy-panzoom/-/react-easy-panzoom-0.4.4.tgz",
"integrity": "sha512-1zgT6boDVPcrR3Egcz8KEVpM3fs50o22iIWPRlAqvev0/4nw5RnUNFsvmOJ/b5M2nd8MDGknLmyfBdhjoLB6+g==",
"dependencies": {
"warning": "4.0.3"
},
"peerDependencies": {
"react": ">=16.0.0"
}
},
"node_modules/react-is": {
"version": "16.13.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
@ -2775,6 +2787,14 @@
"uuid": "dist/bin/uuid"
}
},
"node_modules/warning": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz",
"integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==",
"dependencies": {
"loose-envify": "^1.0.0"
}
},
"node_modules/webidl-conversions": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",

View File

@ -19,6 +19,7 @@
"prop-types": "^15.8.1",
"react": "18.2.0",
"react-dom": "18.2.0",
"react-easy-panzoom": "^0.4.4",
"react-masonry-css": "^1.0.16",
"react-responsive-masonry": "^2.2.0",
"simplex-noise": "^4.0.1",