Skip to content

Commit

Permalink
feat: add search func (#23)
Browse files Browse the repository at this point in the history
  • Loading branch information
HarshPatel5940 authored May 5, 2024
1 parent 90fd9c3 commit 9e05943
Show file tree
Hide file tree
Showing 3 changed files with 94 additions and 40 deletions.
23 changes: 18 additions & 5 deletions src/components/projectDataCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import { Button } from './ui/button';
import { toast } from 'sonner';
import DeleteImageDialog from '@/components/projectData/DeleteImageDialog';
import UpdateProjectDialog from './projectData/UpdateProjectDataDialog';
import { Link } from 'react-router-dom';
import { API_BASE_URL } from '@/lib/utils';

export interface ProjectData {
id: string;
Expand Down Expand Up @@ -41,15 +43,21 @@ export default function MyProjectDataCard(props: ProjectData) {
</CardHeader>
<CardContent className="space-y-1">
<CardTitle>{props.title}</CardTitle>
<CardDescription>
<CardDescription className="flex flex-col space-y-3">
{props.description ? props.description : 'No Descrpition Provided'}
<div>
Image Source URL:{' '}
<Link className="text-blue-500" to={props.imageUrl}>
Source 🔗
</Link>
</div>
</CardDescription>
<div className="flex align-middle">
<LinkIcon size={21} className="pt-1" />
{props.url ? (
<a className="text-blue-500" href={props.url}>
<Link className="text-blue-500" to={props.url}>
{props.url}
</a>
</Link>
) : (
<CardDescription>No Link Provided</CardDescription>
)}
Expand All @@ -65,8 +73,13 @@ export default function MyProjectDataCard(props: ProjectData) {
<Button
className="w-full"
onClick={() => {
navigator.clipboard.writeText(props.imageUrl);
toast.info('Copied to clipboard');
navigator.clipboard.writeText(
`${API_BASE_URL}/api/public/project/${props.projectId}/data/${props.id}`
);
toast('Copied to clipboard', {
description:
'Make sure to use the Project Token to access this data!\nOrelse, Feel Free to use Source URL',
});
}}
>
Copy 🔗
Expand Down
2 changes: 1 addition & 1 deletion src/lib/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}

const API_BASE_URL = 'http://localhost:5050';
export const API_BASE_URL = 'http://localhost:5050';

export default axios.create({
baseURL: API_BASE_URL,
Expand Down
109 changes: 75 additions & 34 deletions src/pages/project/manageProjectData.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import server from '@/lib/utils';
import { AxiosError } from 'axios';
import { destroyCookie, parseCookies } from 'nookies';
import type React from 'react';
import { useEffect, useState } from 'react';
import { useEffect, useRef, useState } from 'react';
import { useNavigate, useParams } from 'react-router-dom';
import { toast } from 'sonner';

Expand All @@ -27,10 +27,15 @@ export interface Project {

export default function ManageProjectDataPage() {
const navigate = useNavigate();
const [projectCards, setProjectCards] = useState<React.ReactNode[]>([]);
const [token] = useState<string | null>(parseCookies().userToken || null);
const inputRef = useRef<HTMLInputElement>(null);
const [search, setSearch] = useState('');
const [debouncedSearch, setDebouncedSearch] = useState('');
const [project, setProject] = useState<Project | null>(null);
const [projectData, setProjectData] = useState<ProjectData[]>([]);
const [token] = useState<string | null>(parseCookies().userToken || null);
const [debouncedProjectData, setDebouncedProjectData] = useState<
ProjectData[]
>([]);
const [isVerified, setIsVerified] = useState<boolean>(false);
const projectId = useParams().projectId;

Expand All @@ -50,8 +55,49 @@ export default function ManageProjectDataPage() {
}, [project]);

useEffect(() => {
handleProjectData(projectData);
}, [projectData]);
if (!debouncedSearch) {
setDebouncedProjectData(projectData);
}

const filterdProjectData = projectData.filter(
projectData =>
projectData.title
.toLowerCase()
.includes(debouncedSearch.toLowerCase()) ||
projectData.description
?.toLowerCase()
.includes(debouncedSearch.toLowerCase()) ||
projectData.url
?.toLowerCase()
.includes(debouncedSearch.toLowerCase()) ||
projectData.imageUrl
.toLowerCase()
.includes(debouncedSearch.toLowerCase()) ||
projectData.id.toLowerCase().includes(debouncedSearch.toLowerCase())
);

setDebouncedProjectData(filterdProjectData);
}, [projectData, debouncedSearch]);

useEffect(() => {
if (inputRef.current) {
inputRef.current.value = search;
}
}, [search]);

useEffect(() => {
const timeoutId = setTimeout(() => {
setDebouncedSearch(search);
}, 350);

return () => {
clearTimeout(timeoutId);
};
}, [search]);

function handleSearch() {
setSearch(inputRef.current?.value || '');
}

const fetchProjectData = async () => {
try {
Expand Down Expand Up @@ -100,41 +146,35 @@ export default function ManageProjectDataPage() {
}
};

function handleProjectData(projectData: ProjectData[]) {
let projectCards: React.ReactNode[] = [];
if (!project) {
return;
}

projectCards = projectData.map((project: ProjectData) => {
return (
<ProjectDataCard
id={project.id}
key={project.id}
title={project.title}
description={project.description}
imageUrl={project.imageUrl}
url={project.url}
createdAt={project.createdAt}
updatedAt={project.updatedAt}
projectId={project.projectId}
setProjectData={setProjectData}
/>
);
});
setProjectCards(projectCards);
}

function displayDataTable(): React.ReactNode {
return (
<div className="flex flex-col items-center w-full">
{projectCards.length > 0 ? (
{debouncedProjectData.length > 0 ? (
<div className="grid gap-6 grid-cols-1 lg:grid-cols-2 xl:grid-cols-3 max-w-6xl w-full mx-auto">
{projectCards}
{debouncedProjectData.map((project: ProjectData) => {
return (
<ProjectDataCard
id={project.id}
key={project.id}
title={project.title}
description={project.description}
imageUrl={project.imageUrl}
url={project.url}
createdAt={project.createdAt}
updatedAt={project.updatedAt}
projectId={project.projectId}
setProjectData={setProjectData}
/>
);
})}
</div>
) : (
<div className="text-center space-y-2">
<div className="text-4xl opacity-50">No Data Added Yet!</div>
<div className="text-4xl opacity-50">
{debouncedSearch
? `No Projects with ${debouncedSearch} Found`
: 'No Projects Created Yet!'}
</div>
</div>
)}
</div>
Expand All @@ -149,9 +189,10 @@ export default function ManageProjectDataPage() {
{isVerified && (
<main className="flex min-h-screen bg-gray-200/40 flex-1 flex-col gap-4 p-4 md:gap-8 md:p-10 dark:bg-gray-800/40">
<div className="max-w-6xl w-full mx-auto flex items-center gap-4">
{/* // TODO: implement search functionality */}
<Input
className="shadow-md bg-white dark:bg-gray-950"
onChange={handleSearch}
ref={inputRef}
placeholder="Search Inside Your Project..."
/>
<Button className="sr-only" type="submit">
Expand Down

0 comments on commit 9e05943

Please sign in to comment.