mirror of
https://github.com/D4M13N-D3V/neroshitron.git
synced 2025-06-16 13:19:17 +00:00
feat:search + tags
This commit is contained in:
parent
812c01e518
commit
b12d3e1752
@ -2,11 +2,39 @@ import { NextResponse } from "next/server";
|
|||||||
import { createClient } from "@/utils/supabase/server";
|
import { createClient } from "@/utils/supabase/server";
|
||||||
|
|
||||||
|
|
||||||
export async function GET(request: Request) {
|
export async function POST(request: Request) {
|
||||||
const supabase = createClient();
|
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('*')
|
||||||
|
.ilike('name', `%${search}%`)
|
||||||
|
.ilike('description', `%${search}%`);
|
||||||
|
|
||||||
|
return NextResponse.json(galleries);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
// Rest of the code...
|
||||||
let { data: galleries, error } = await supabase
|
let { data: galleries, error } = await supabase
|
||||||
.from('galleries')
|
.from('galleries')
|
||||||
.select('*')
|
.select('*')
|
||||||
return NextResponse.json(galleries)
|
.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 };
|
||||||
|
// });
|
12
app/api/galleries/tags/route.ts
Normal file
12
app/api/galleries/tags/route.ts
Normal 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)
|
||||||
|
}
|
||||||
|
|
@ -18,7 +18,10 @@ function PageComponent() {
|
|||||||
const [user, setUser] = useState<User | null>(null);
|
const [user, setUser] = useState<User | null>(null);
|
||||||
const [loading, setLoading] = useState<boolean>(true);
|
const [loading, setLoading] = useState<boolean>(true);
|
||||||
const [selectedGallery, setSelectedGallery] = useState<string | null>(null);
|
const [selectedGallery, setSelectedGallery] = useState<string | null>(null);
|
||||||
|
const [tags, setTags] = useState<any[]>([]);
|
||||||
|
const [search, setSearch] = useState<string>('');
|
||||||
const [galleryColumns, setColumns] = useState<number>(0);
|
const [galleryColumns, setColumns] = useState<number>(0);
|
||||||
|
const [selectedTags, setSelectedTags] = useState<number[]>([]);
|
||||||
const generateRandomString = function (length:number) {
|
const generateRandomString = function (length:number) {
|
||||||
let result = '';
|
let result = '';
|
||||||
let characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
let characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
||||||
@ -43,58 +46,122 @@ function PageComponent() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const getData = async () => {
|
const getData = async () => {
|
||||||
const galleriesResponse = await fetch('/api/galleries');
|
|
||||||
const galleriesData = await galleriesResponse.json();
|
|
||||||
let { data: { user } } = await supabase.auth.getUser();
|
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);
|
setGalleries(galleriesData);
|
||||||
|
setTags(tagsData);
|
||||||
setUser(user);
|
setUser(user);
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
getData();
|
getData();
|
||||||
|
|
||||||
}, []);
|
}, [selectedTags,search]);
|
||||||
return ( ( user ? (
|
|
||||||
<div className="w-full h-full flex justify-center">
|
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="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">
|
<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
|
||||||
</div>
|
src="gallery_girl.png"
|
||||||
<div className="absolute items-center w-3/5 h-full ml-10 z-0 overflow-hidden animate-in animate-ease-out">
|
className="float-right object-cover h-screen w-3/6"
|
||||||
<div className="grid grid-cols-3 gap-y-36 gap-x-10 h-full overflow-y-auto no-scrollbar pt-20">
|
alt="Background"
|
||||||
{galleries.map((gallery, index) => (
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
<GalleryThumbnail key={index} id={gallery.id} title={gallery.name} columns={gallery.columns} subscription={gallery.tier as string} onSelect={selectGallery}></GalleryThumbnail>
|
<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">
|
||||||
<div className="pt-10">
|
{galleries && galleries.map((gallery, index) => (
|
||||||
</div>
|
<GalleryThumbnail
|
||||||
<div className="pt-10">
|
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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</>
|
</>
|
||||||
</div>
|
</div>
|
||||||
{(isOpen ? (
|
{isOpen ? (
|
||||||
<>
|
<>
|
||||||
|
<div
|
||||||
|
className={`fixed inset-0 transition-opacity z-30 ${
|
||||||
<div className={`fixed inset-0 transition-opacity z-30 ${isOpen ? 'animate-in' : 'fade-out'}`} aria-hidden="true">
|
isOpen ? "animate-in" : "fade-out"
|
||||||
<div className="absolute inset-0 bg-neroshi-blue-900 opacity-70 z-30" onClick={()=>setIsOpen(false)} >
|
}`}
|
||||||
</div>
|
aria-hidden="true"
|
||||||
<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>
|
<div
|
||||||
</div>
|
className="absolute inset-0 bg-neroshi-blue-900 opacity-70 z-30"
|
||||||
|
onClick={() => setIsOpen(false)}
|
||||||
</div>
|
></div>
|
||||||
</>
|
<div className="absolute inset-0 overflow-y-auto overflow-x-hidden no-scrollbar pt-2 w-full p-20 z-30">
|
||||||
): null)}
|
<Gallery
|
||||||
</div>
|
id={selectedGallery as string}
|
||||||
|
columns={galleryColumns}
|
||||||
) : (
|
closeMenu={() => closeGallery()}
|
||||||
<h1>loading</h1>
|
></Gallery>
|
||||||
)));
|
</div>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<h1>loading</h1>
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default PageComponent;
|
export default PageComponent;
|
@ -2,6 +2,7 @@ import { use, useState } from 'react';
|
|||||||
import { useEffect } from 'react';
|
import { useEffect } from 'react';
|
||||||
import { render } from 'react-dom';
|
import { render } from 'react-dom';
|
||||||
import Masonry from 'react-masonry-css';
|
import Masonry from 'react-masonry-css';
|
||||||
|
import PanZoom from 'react-easy-panzoom';
|
||||||
|
|
||||||
interface GalleryProps {
|
interface GalleryProps {
|
||||||
id: string;
|
id: string;
|
||||||
@ -173,23 +174,24 @@ const Gallery = ({ id, columns, closeMenu }: GalleryProps) => {
|
|||||||
Back
|
Back
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<div
|
|
||||||
className="z-30 pb-10"
|
|
||||||
style={{
|
|
||||||
display: selectedImage ? "flex" : "block",
|
|
||||||
alignItems: "flex-start",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<>
|
|
||||||
{renderButtons()}
|
{renderButtons()}
|
||||||
{selectedImage ? (
|
{selectedImage ? (<PanZoom>
|
||||||
|
|
||||||
<img
|
<img
|
||||||
src={images[currentIndex]}
|
src={images[currentIndex]}
|
||||||
style={{ objectFit: "contain", maxWidth: "100%", maxHeight: "calc(100vh - 20px)" }}
|
style={{ objectFit: "contain", maxWidth: "100%", maxHeight: "calc(100vh - 20px)", pointerEvents:"none" }}
|
||||||
className="cursor-pointer animate-in w-full h-auto"
|
className="cursor-pointer animate-in w-full h-auto"
|
||||||
onClick={() => close()}
|
onClick={() => close()}
|
||||||
/>
|
/>
|
||||||
|
</PanZoom>
|
||||||
) : (
|
) : (
|
||||||
|
<div
|
||||||
|
className="z-30 pb-10"
|
||||||
|
style={{
|
||||||
|
display: selectedImage ? "flex" : "block",
|
||||||
|
alignItems: "flex-start",
|
||||||
|
}}
|
||||||
|
>
|
||||||
<Masonry
|
<Masonry
|
||||||
breakpointCols={columns}
|
breakpointCols={columns}
|
||||||
className="my-masonry-grid"
|
className="my-masonry-grid"
|
||||||
@ -205,9 +207,10 @@ const Gallery = ({ id, columns, closeMenu }: GalleryProps) => {
|
|||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</Masonry>
|
</Masonry>
|
||||||
|
<>
|
||||||
|
</>
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
</>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
20
package-lock.json
generated
20
package-lock.json
generated
@ -18,6 +18,7 @@
|
|||||||
"prop-types": "^15.8.1",
|
"prop-types": "^15.8.1",
|
||||||
"react": "18.2.0",
|
"react": "18.2.0",
|
||||||
"react-dom": "18.2.0",
|
"react-dom": "18.2.0",
|
||||||
|
"react-easy-panzoom": "^0.4.4",
|
||||||
"react-masonry-css": "^1.0.16",
|
"react-masonry-css": "^1.0.16",
|
||||||
"react-responsive-masonry": "^2.2.0",
|
"react-responsive-masonry": "^2.2.0",
|
||||||
"simplex-noise": "^4.0.1",
|
"simplex-noise": "^4.0.1",
|
||||||
@ -2154,6 +2155,17 @@
|
|||||||
"react": "^18.2.0"
|
"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": {
|
"node_modules/react-is": {
|
||||||
"version": "16.13.1",
|
"version": "16.13.1",
|
||||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
||||||
@ -2775,6 +2787,14 @@
|
|||||||
"uuid": "dist/bin/uuid"
|
"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": {
|
"node_modules/webidl-conversions": {
|
||||||
"version": "3.0.1",
|
"version": "3.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
"prop-types": "^15.8.1",
|
"prop-types": "^15.8.1",
|
||||||
"react": "18.2.0",
|
"react": "18.2.0",
|
||||||
"react-dom": "18.2.0",
|
"react-dom": "18.2.0",
|
||||||
|
"react-easy-panzoom": "^0.4.4",
|
||||||
"react-masonry-css": "^1.0.16",
|
"react-masonry-css": "^1.0.16",
|
||||||
"react-responsive-masonry": "^2.2.0",
|
"react-responsive-masonry": "^2.2.0",
|
||||||
"simplex-noise": "^4.0.1",
|
"simplex-noise": "^4.0.1",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user