Skip to content

Commit

Permalink
feat: add quick search in /all (#1)
Browse files Browse the repository at this point in the history
* feat: add quick search in /all

* refactor: fix code smells
  • Loading branch information
agustinusnathaniel authored Dec 26, 2020
1 parent 550cc12 commit 58b1f36
Show file tree
Hide file tree
Showing 4 changed files with 164 additions and 23 deletions.
15 changes: 8 additions & 7 deletions src/components/form/SearchContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ const INITIAL_VALUES: SearchFormValueType = {
const SearchContainer = () => {
const {
values: {
queryParams: { title, description, https, cors, category },
queryParams: { title, description, https, category },
searchDescription,
selectCategory,
isRandom,
Expand Down Expand Up @@ -101,6 +101,7 @@ const SearchContainer = () => {
<Stack>
<FormControl>
<Input
type="text"
borderRadius={12}
textAlign="center"
name="queryParams.title"
Expand Down Expand Up @@ -141,6 +142,7 @@ const SearchContainer = () => {
{searchDescription && (
<FormControl>
<Input
type="text"
borderRadius={12}
textAlign="center"
name="queryParams.description"
Expand Down Expand Up @@ -175,12 +177,11 @@ const SearchContainer = () => {
value={category}
onChange={handleChange}
>
{categories &&
categories.map((category: string, index) => (
<Text as="option" key={index}>
{category}
</Text>
))}
{categories?.map((categoryItem: string, index) => (
<Text as="option" key={index}>
{categoryItem}
</Text>
))}
</Select>
</FormControl>
)}
Expand Down
7 changes: 7 additions & 0 deletions src/components/models/list.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
export type ListItem = {
/** API name */
API: string;
/** API Authentication type */
Auth: string;
/** the category of the API */
Category: string;
/** does the API support CORS */
Cors: string;
/** API description */
Description: string;
/** does the API support HTTPS */
HTTPS: boolean;
/** the API url / link */
Link: string;
};
3 changes: 1 addition & 2 deletions src/pages/_document.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import Document, { Html, Head, Main, NextScript } from "next/document";

const APP_NAME = "Public APIs";
const APP_DESCRIPTION =
"Find public APIs for your next projects.";
const APP_DESCRIPTION = "Find public APIs for your next projects.";

class MyDocument extends Document {
static async getInitialProps(ctx) {
Expand Down
162 changes: 148 additions & 14 deletions src/pages/all.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,119 @@
import { Box, Button, Skeleton } from "@chakra-ui/react";
import {
Box,
Button,
FormControl,
Grid,
Input,
Skeleton,
} from "@chakra-ui/react";
import Link from "next/link";
import { ChangeEvent, useEffect, useState } from "react";

import ItemContainer from "../components/item/ItemContainer";
import { ListItem } from "../components/models/list";

import { useAPIList } from "../helpers/fetchHooks";

const ITEM_PER_PAGE = 20;

const All = () => {
const { data, isLoading } = useAPIList(true);

const sortedData =
data &&
data.entries.sort((a, b) => {
if (a.API < b.API) {
return -1;
}
if (a.API > b.API) {
return 1;
const [sortedData, setSortedData] = useState<Array<ListItem>>([]);
const [pagedData, setPagedData] = useState<Array<Array<ListItem>>>([]);
const [currentPage, setCurrentPage] = useState<number>(0);

const [keyword, setKeyword] = useState<string>("");
const [filteredData, setFiltedData] = useState<Array<ListItem>>([]);

const paginateData = () => {
const totalPage = Math.ceil(
(keyword.length ? filteredData.length : sortedData.length) / ITEM_PER_PAGE
);
const updatePagedData: Array<Array<ListItem>> = [];

for (let i = 0; i < totalPage; i++) {
const start = i * ITEM_PER_PAGE;
const end =
i === totalPage - 1
? i * ITEM_PER_PAGE +
((keyword.length ? filteredData.length : sortedData.length) -
i * ITEM_PER_PAGE)
: (i + 1) * ITEM_PER_PAGE;

const currentPageData: Array<ListItem> = [];

for (let j = start; j < end; j++) {
currentPageData.push(
keyword.length && filteredData.length
? filteredData[j]
: sortedData[j]
);
}
return 0;
});

updatePagedData.push(currentPageData);
}

setPagedData(updatePagedData);
};

const filterData = () => {
const kword = keyword.toLowerCase();
const updateFilteredData = sortedData.filter(
(entry) =>
entry.API.toLowerCase().indexOf(kword) > -1 ||
entry.Description.toLowerCase().indexOf(kword) > -1
);

setFiltedData(updateFilteredData);
};

useEffect(() => {
if (data?.entries) {
const updateSortedData = data && data.entries;

updateSortedData.sort((a, b) => {
if (a.API < b.API) {
return -1;
}
if (a.API > b.API) {
return 1;
}
return 0;
});
setSortedData(updateSortedData);
}
}, [data]);

useEffect(() => {
paginateData();
}, [sortedData, filteredData]);

useEffect(() => {
if (keyword.length) {
filterData();
setCurrentPage(0);
} else {
paginateData();
}
}, [keyword]);

const handleChangePage = (type: "next" | "prev") => () => {
const updatePageNumber =
type === "next" ? currentPage + 1 : currentPage - 1;
setCurrentPage(updatePageNumber);
window.scrollTo(0, 0);
};

const handleChangeKeyword = (event: ChangeEvent<HTMLInputElement>) => {
setKeyword(event.target.value);
};

const pageNavigationButtonsProps: PageNavigationButtonsProps = {
currentPage,
handleChangePage,
totalPage: pagedData.length - 1,
};

return (
<Box>
Expand All @@ -28,12 +123,51 @@ const All = () => {
</Button>
</Link>
<Skeleton isLoaded={!isLoading} minHeight="80vh">
{data && data.entries && (
<ItemContainer entries={sortedData} useAccordion={false} />
)}
<FormControl>
<Input
type="text"
placeholder="quick search"
size="lg"
onChange={handleChangeKeyword}
/>
</FormControl>
<PageNavigationButtons {...pageNavigationButtonsProps} />
{pagedData[currentPage]?.length ? (
<ItemContainer
entries={pagedData[currentPage]}
useAccordion={false}
/>
) : null}
<PageNavigationButtons {...pageNavigationButtonsProps} />
</Skeleton>
</Box>
);
};

type PageNavigationButtonsProps = {
currentPage: number;
handleChangePage: (type: "next" | "prev") => () => void;
totalPage: number;
};

const PageNavigationButtons = ({
currentPage,
handleChangePage,
totalPage,
}: PageNavigationButtonsProps) => {
return (
<Grid templateColumns="repeat(2, 1fr)" marginY={4} gap={2}>
<Button disabled={currentPage === 0} onClick={handleChangePage("prev")}>
Prev
</Button>
<Button
disabled={currentPage === totalPage}
onClick={handleChangePage("next")}
>
Next
</Button>
</Grid>
);
};

export default All;

1 comment on commit 58b1f36

@vercel
Copy link

@vercel vercel bot commented on 58b1f36 Dec 26, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.