diff --git a/.babelrc b/.babelrc new file mode 100644 index 0000000..854cb73 --- /dev/null +++ b/.babelrc @@ -0,0 +1,4 @@ +{ + "presets": ["next/babel"], + "plugins": [["styled-components", { "ssr": true }]] +} diff --git a/.husky/.gitignore b/.husky/.gitignore new file mode 100644 index 0000000..31354ec --- /dev/null +++ b/.husky/.gitignore @@ -0,0 +1 @@ +_ diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100755 index 0000000..522cb52 --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1,4 @@ +#!/bin/sh +. "$(dirname "$0")/_/husky.sh" + +yarn cache:update diff --git a/articles/en/getting-started/welcome-aboard.md b/articles/en/getting-started/welcome-aboard.md index ee7b29b..b9bffb1 100644 --- a/articles/en/getting-started/welcome-aboard.md +++ b/articles/en/getting-started/welcome-aboard.md @@ -1,6 +1,7 @@ --- title: 'Welcome aboard 👋' description: 'We want to help you provide great customer support to all of your customers in a sane and sustainable way.' +tags: 'getting started, intro, presentation' order: 1 updatedAt: '2021-04-29' --- diff --git a/articles/pt-BR/primeiros-passos/seja-bem-vindo.md b/articles/pt-BR/primeiros-passos/seja-bem-vindo.md index 322499b..1082fdd 100644 --- a/articles/pt-BR/primeiros-passos/seja-bem-vindo.md +++ b/articles/pt-BR/primeiros-passos/seja-bem-vindo.md @@ -1,6 +1,7 @@ --- title: 'Seja bem-vindo 👋' description: 'Queremos ajudá-lo a fornecer um ótimo suporte para todos os seus clientes de uma forma simples e sustentável.' +tags: 'primeiros passos, introdução, apresentação' order: 1 updatedAt: '2021-04-29' --- diff --git a/cache/.gitkeep b/cache/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/cache/data.json b/cache/data.json new file mode 100644 index 0000000..68f7edd --- /dev/null +++ b/cache/data.json @@ -0,0 +1 @@ +[{"locale":"en","category":"getting-started","slug":"welcome-aboard","tags":"getting started, intro, presentation","title":"Welcome aboard 👋","description":"We want to help you provide great customer support to all of your customers in a sane and sustainable way."},{"locale":"pt-BR","category":"primeiros-passos","slug":"seja-bem-vindo","tags":"primeiros passos, introdução, apresentação","title":"Seja bem-vindo 👋","description":"Queremos ajudá-lo a fornecer um ótimo suporte para todos os seus clientes de uma forma simples e sustentável."}] \ No newline at end of file diff --git a/components/lists/CategoriesList.tsx b/components/lists/CategoriesList.tsx index 1cfcffa..3c3be4c 100644 --- a/components/lists/CategoriesList.tsx +++ b/components/lists/CategoriesList.tsx @@ -14,10 +14,6 @@ export default function CategoriesList({ categories, locale }: Props) { const { t } = useTranslation('categories'); const sortedCategories = categories.sort((a, b) => { - console.log(t(`${a.category}.title`)); - console.log(t(`${a.category}.order`)); - console.log(t(`${b.category}.order`)); - return Number(t(`${a.category}.order`)) - Number(t(`${b.category}.order`)); }); @@ -28,7 +24,7 @@ export default function CategoriesList({ categories, locale }: Props) { key={category} title={t(`${category}.title`)} description={t(`${category}.description`)} - path={category} + path={`/${category}`} locale={locale} /> ))} diff --git a/components/lists/ListItem.tsx b/components/lists/ListItem.tsx index fe75191..669ca5c 100644 --- a/components/lists/ListItem.tsx +++ b/components/lists/ListItem.tsx @@ -12,7 +12,7 @@ interface Props { export default function ListItem({ title, description, path, locale }: Props) { return ( - + {title} {description} @@ -23,11 +23,6 @@ export default function ListItem({ title, description, path, locale }: Props) { } const Item = styled.li``; - -const ItemLink = styled.a.attrs({ - className: - 'flex flex-col cursor-pointer border border-gray-50 bg-gray-50 rounded-xl p-6 space-y-1 hover:bg-white hover:border-gray-200', -})``; - +const ItemLink = styled.a.attrs({ className: 'list-item focusable' })``; const ItemLabel = styled.p.attrs({ className: 'font-medium text-lg text-primary-500' })``; const ItemDescription = styled.p.attrs({ className: 'text-gray-500' })``; diff --git a/components/navigation/AppHeader/SearchInput.tsx b/components/navigation/AppHeader/SearchInput.tsx new file mode 100644 index 0000000..7c44d09 --- /dev/null +++ b/components/navigation/AppHeader/SearchInput.tsx @@ -0,0 +1,103 @@ +import { useRouter } from 'next/router'; +import React, { useRef, useState } from 'react'; +import { useEffect } from 'react'; +import { RiLoader5Line, RiSearchLine } from 'react-icons/ri'; +import { useQuery } from 'react-query'; +import styled from 'styled-components'; + +import { SearchResultType } from '../../../utils/datasource'; +import { searchForArticles } from '../../../utils/libs/api'; +import SearchResults from './SearchResults'; + +export default function SearchInput() { + const [isSearching, setIsSearching] = useState(false); + const [cursor, setCursor] = useState(0); + const [term, setTerm] = useState(''); + + const inputRef = useRef(); + const router = useRouter(); + + const { isLoading, data } = useQuery(['searchResults', term], () => + searchForArticles(term, router.locale) + ); + + const results = data || []; + + useEffect(() => { + if (!isSearching) { + inputRef.current.blur(); + } + }, [isSearching, inputRef]); + + useEffect(() => { + if (cursor > results.length) { + setCursor(0); + } + }, [results]); + + function handleKeyUp({ key }: React.KeyboardEvent) { + if (key === 'Escape') { + setIsSearching(false); + return; + } + + if (key === 'ArrowDown' && cursor < results.length) { + setCursor(cursor + 1); + return; + } + + if (cursor > 0) { + switch (key) { + case 'ArrowUp': + setCursor(cursor - 1); + break; + case 'Enter': + handleSelect(results[cursor - 1]); + } + } + } + + function handleSelect({ item }: SearchResultType) { + const path = `/${item.category}/${item.slug}`; + const locale = item.locale; + + setIsSearching(false); + + router.push(path, path, { locale }); + } + + return ( + + + setTerm(target.value)} + onFocus={() => setIsSearching(true)} + onKeyUp={handleKeyUp} + /> + + {isLoading ? : } + + + {isSearching && } + + ); +} + +const Container = styled.div.attrs({ className: 'relative' })``; + +const InputContainer = styled.div.attrs({ + className: 'transition relative text-gray-400 focus-within:text-primary-500', +})``; + +const InputIconContainer = styled.div.attrs({ + className: 'flex absolute items-center inset-y-0 right-4', +})``; + +const Input = styled.input.attrs({ + placeholder: 'Search', + className: 'form-input focusable', +})``; + +const LoaderIcon = styled(RiLoader5Line).attrs({ size: 24, className: 'animate-spin' })``; +const SearchIcon = styled(RiSearchLine).attrs({ size: 24, className: '' })``; diff --git a/components/navigation/AppHeader/SearchResults.tsx b/components/navigation/AppHeader/SearchResults.tsx new file mode 100644 index 0000000..a37b02d --- /dev/null +++ b/components/navigation/AppHeader/SearchResults.tsx @@ -0,0 +1,58 @@ +import useTranslation from 'next-translate/useTranslation'; +import React from 'react'; +import styled from 'styled-components'; + +import { SearchResultType } from '../../../utils/datasource'; + +export interface SelectableItem { + onSelect?: (param: SearchResultType) => void; +} + +interface Props extends SelectableItem { + cursor: number; + results: SearchResultType[]; +} + +interface ItemProps extends SelectableItem { + item: SearchResultType['item']; + isActive?: boolean; +} + +export default function SearchResults({ cursor, results, onSelect }: Props) { + return ( + <> + {results?.length > 0 && ( + + {results.map(({ item }, index) => ( + + ))} + + )} + + ); +} + +function SearchResultsItem({ item, isActive, onSelect }: ItemProps) { + const { t } = useTranslation('categories'); + + function handleSelect() { + onSelect?.({ item }); + } + + return ( + + + {t(`${item.category}.title`)} + {item.title} + + + ); +} + +const ItemsList = styled.ul.attrs({ className: 'search-result-list' })``; + +const Item = styled.li``; +const ItemButton = styled.button``; + +const ItemCategory = styled.span.attrs({ className: 'search-result-description' })``; +const ItemLabel = styled.span.attrs({ className: 'search-result-title' })``; diff --git a/components/navigation/AppHeader.tsx b/components/navigation/AppHeader/index.tsx similarity index 88% rename from components/navigation/AppHeader.tsx rename to components/navigation/AppHeader/index.tsx index 4676f4a..2d30536 100644 --- a/components/navigation/AppHeader.tsx +++ b/components/navigation/AppHeader/index.tsx @@ -3,7 +3,8 @@ import Link from 'next/link'; import React from 'react'; import styled from 'styled-components'; -import { Brand } from '../../utils/constants/app'; +import { Brand } from '../../../utils/constants/app'; +import SearchInput from './SearchInput'; interface Props { isCompact?: boolean; @@ -28,11 +29,10 @@ export default function AppHeader({ isCompact }: Props) { - {!isCompact && ( - - {t('header.title')} - - )} + + {!isCompact && {t('header.title')}} + + ); diff --git a/package.json b/package.json index 8b7ea04..0796b9f 100644 --- a/package.json +++ b/package.json @@ -5,16 +5,21 @@ "scripts": { "dev": "next dev", "build": "next build", - "start": "next start" + "start": "next start", + "cache:update": "ts-node utils/scripts/updateCache", + "prepare": "husky install" }, "dependencies": { "autoprefixer": "^10.2.5", + "fuse.js": "^6.4.6", "gray-matter": "^4.0.2", "next": "^10.2.0", "next-translate": "^1.0.6", "postcss": "^8.2.14", "react": "17.0.2", "react-dom": "17.0.2", + "react-icons": "^4.2.0", + "react-query": "^3.17.0", "remark": "^12.0.1", "remark-html": "^13.0.1", "styled-components": "^5.3.0", @@ -31,7 +36,9 @@ "eslint-plugin-prettier": "^3.1.4", "eslint-plugin-react": "^7.21.3", "eslint-plugin-simple-import-sort": "^5.0.3", + "husky": "^6.0.0", "prettier": "^2.1.2", + "ts-node": "^10.0.0", "typescript": "^4.2.4" } } diff --git a/pages/_app.tsx b/pages/_app.tsx index 2d8eafe..7d1670f 100644 --- a/pages/_app.tsx +++ b/pages/_app.tsx @@ -2,9 +2,16 @@ import '../styles/globals.css'; import { AppProps } from 'next/dist/next-server/lib/router/router'; import React from 'react'; +import { QueryClient, QueryClientProvider } from 'react-query'; + +const queryClient = new QueryClient(); function App({ Component, pageProps }: AppProps) { - return ; + return ( + + + + ); } export default App; diff --git a/pages/api/hello.ts b/pages/api/hello.ts deleted file mode 100644 index 059f2e9..0000000 --- a/pages/api/hello.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { NextApiRequest, NextApiResponse } from 'next'; - -export default (req: NextApiRequest, res: NextApiResponse) => { - res.statusCode = 200; - res.json({ name: 'Hey!' }); -}; diff --git a/pages/api/search.ts b/pages/api/search.ts new file mode 100644 index 0000000..960b14c --- /dev/null +++ b/pages/api/search.ts @@ -0,0 +1,14 @@ +import { NextApiRequest, NextApiResponse } from 'next'; + +import Search from '../../utils/libs/search'; + +export default (req: NextApiRequest, res: NextApiResponse) => { + const term = req.query.term as string; + const locale = req.query.locale as string; + + try { + res.json(Search.find(term, locale)); + } catch { + res.json([]); + } +}; diff --git a/styles/custom/buttons.css b/styles/custom/buttons.css new file mode 100644 index 0000000..ac4128a --- /dev/null +++ b/styles/custom/buttons.css @@ -0,0 +1,7 @@ +.btn { + @apply font-medium rounded-xl transition ring-inset px-4 py-2 focus:outline-none focus:ring-2; +} + +.btn-default { + @apply bg-gray-100 ring-gray-300 hover:bg-gray-200; +} diff --git a/styles/custom/components.css b/styles/custom/components.css index fb48605..dc2387a 100644 --- a/styles/custom/components.css +++ b/styles/custom/components.css @@ -1,11 +1,43 @@ -.btn { - @apply font-medium rounded-xl transition ring-inset px-4 py-2 focus:outline-none focus:ring-2; +.list-item { + @apply transition flex flex-col cursor-pointer border border-gray-50 bg-gray-50 rounded-xl p-6 space-y-1 hover:bg-white hover:border-gray-200 focus:border-primary-500; } -.btn-default { - @apply bg-gray-100 ring-gray-300 hover:bg-gray-200; +/** + * ------------------------------ + * Search results + * ------------------------------ + */ + +.search-result-list { + @apply absolute rounded-lg shadow-lg overflow-hidden w-full mt-1; +} + +.search-result-item button { + @apply group flex flex-col text-left bg-white cursor-pointer p-4 w-full hover:bg-primary-500 hover:text-white; +} + +.search-result-item .search-result-description { + @apply text-xs text-gray-500 uppercase group-hover:text-primary-200; +} + +.search-result-item .search-result-title { + @apply text-lg font-medium; +} + +.search-result-item.active button { + @apply bg-primary-500 text-white; } +.search-result-item.active .search-result-description { + @apply text-primary-200; +} + +/** + * ------------------------------ + * Article content + * ------------------------------ + */ + .article-content { @apply leading-relaxed; } diff --git a/styles/custom/inputs.css b/styles/custom/inputs.css new file mode 100644 index 0000000..7b6e4b5 --- /dev/null +++ b/styles/custom/inputs.css @@ -0,0 +1,7 @@ +.focusable { + @apply ring-primary-500 ring-inset focus:bg-white focus:ring-1 focus:outline-none; +} + +.form-input { + @apply transition text-gray-800 border border-gray-200 rounded-lg py-3 pl-4 pr-10 w-full focus:border-primary-500; +} diff --git a/styles/globals.css b/styles/globals.css index 95ce5d1..cf0191b 100644 --- a/styles/globals.css +++ b/styles/globals.css @@ -2,7 +2,9 @@ @tailwind components; @tailwind utilities; +@import 'custom/buttons.css'; @import 'custom/components.css'; +@import 'custom/inputs.css'; @layer base { html, diff --git a/tsconfig.json b/tsconfig.json index 35d51ea..384593c 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,6 +1,6 @@ { "compilerOptions": { - "target": "es5", + "target": "es5", "lib": [ "dom", "dom.iterable", @@ -12,7 +12,7 @@ "forceConsistentCasingInFileNames": true, "noEmit": true, "esModuleInterop": true, - "module": "esnext", + "module": "commonjs", "moduleResolution": "node", "resolveJsonModule": true, "isolatedModules": true, diff --git a/utils/datasource.ts b/utils/datasource.ts index 1f4b85a..b06a210 100644 --- a/utils/datasource.ts +++ b/utils/datasource.ts @@ -29,6 +29,17 @@ export type CategoryListItemType = { category: string; }; +export type SearchResultType = { + item: { + category: string; + description?: string; + locale: string; + slug: string; + tags: string; + title: string; + }; +}; + const articlesDirectory = path.join(process.cwd(), 'articles'); export async function getArticle(locale: string, category: string, slug: string, parseContent = true) { diff --git a/utils/libs/api.ts b/utils/libs/api.ts new file mode 100644 index 0000000..a68a205 --- /dev/null +++ b/utils/libs/api.ts @@ -0,0 +1,5 @@ +const SEARCH_PATH = '/api/search'; + +export function searchForArticles(term: string, locale: string) { + return term.length >= 3 ? fetch(`${SEARCH_PATH}?term=${term}&locale=${locale}`).then((res) => res.json()) : []; +} diff --git a/utils/libs/cache.ts b/utils/libs/cache.ts new file mode 100644 index 0000000..fbdbce9 --- /dev/null +++ b/utils/libs/cache.ts @@ -0,0 +1,45 @@ +import fs from 'fs'; +import path from 'path'; + +import { getArticle, getArticlesList } from '../datasource'; + +const CACHE_PATH = 'cache'; +const CACHE_FILE = 'data.json'; + +export interface CacheEntry { + locale: string; + category: string; + slug: string; + tags: string; + title: string; + description: string; +} + +export function updateArticlesCache() { + const articles = getArticlesList(); + + Promise.all( + articles.map(async (article) => { + const { meta } = await getArticle(article.locale, article.category, article.slug); + + return { + ...article, + tags: meta.tags, + title: meta.title, + description: meta.description, + }; + }) + ).then((data) => { + fs.writeFileSync(path.join(CACHE_PATH, CACHE_FILE), JSON.stringify(data)); + }); +} + +export function getArticlesCache(): CacheEntry[] { + const rawData = fs.readFileSync(path.join(CACHE_PATH, CACHE_FILE)); + + if (!rawData) { + return []; + } + + return JSON.parse(String(rawData)); +} diff --git a/utils/libs/search.ts b/utils/libs/search.ts new file mode 100644 index 0000000..1646d01 --- /dev/null +++ b/utils/libs/search.ts @@ -0,0 +1,39 @@ +import Fuse from 'fuse.js'; + +import { CacheEntry, getArticlesCache } from './cache'; + +const Search = (function () { + let finder: Fuse; + + function hydrateFinder() { + const documents = loadDocuments(); + + finder = new Fuse(documents, { + useExtendedSearch: true, + keys: ['locale', 'tags', 'title', 'description'], + }); + } + + function loadDocuments() { + return getArticlesCache(); + } + + return { + find: (term: string, locale: string) => { + if (!finder) { + hydrateFinder(); + } + + return finder.search({ + $and: [ + { locale: `=${locale}` }, + { + $or: [{ tags: term }, { title: term }, { description: term }], + }, + ], + }); + }, + }; +})(); + +export default Search; diff --git a/utils/scripts/updateCache.ts b/utils/scripts/updateCache.ts new file mode 100644 index 0000000..73d0b36 --- /dev/null +++ b/utils/scripts/updateCache.ts @@ -0,0 +1,3 @@ +import { updateArticlesCache } from '../libs/cache'; + +updateArticlesCache(); diff --git a/yarn.lock b/yarn.lock index 7ffc492..cd1a6e9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -109,6 +109,13 @@ dependencies: regenerator-runtime "^0.13.4" +"@babel/runtime@^7.12.5", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.2", "@babel/runtime@^7.7.2": + version "7.14.5" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.14.5.tgz#665450911c6031af38f81db530f387ec04cd9a98" + integrity sha512-121rumjddw9c3NCQ55KGkyE1h/nzWhU/owjhw0l4mQrkzz4x9SGS1X8gFLraHwX7td3Yo4QTL+qj0NcIzN87BA== + dependencies: + regenerator-runtime "^0.13.4" + "@babel/template@^7.12.13": version "7.12.13" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.12.13.tgz#530265be8a2589dbb37523844c5bcb55947fb327" @@ -279,6 +286,26 @@ resolved "https://registry.yarnpkg.com/@opentelemetry/context-base/-/context-base-0.14.0.tgz#c67fc20a4d891447ca1a855d7d70fa79a3533001" integrity sha512-sDOAZcYwynHFTbLo6n8kIbLiVF3a3BLkrmehJUyEbT9F+Smbi47kLGS2gG2g0fjBLR/Lr1InPD7kXL7FaTqEkw== +"@tsconfig/node10@^1.0.7": + version "1.0.7" + resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.7.tgz#1eb1de36c73478a2479cc661ef5af1c16d86d606" + integrity sha512-aBvUmXLQbayM4w3A8TrjwrXs4DZ8iduJnuJLLRGdkWlyakCf1q6uHZJBzXoRA/huAEknG5tcUyQxN3A+In5euQ== + +"@tsconfig/node12@^1.0.7": + version "1.0.7" + resolved "https://registry.yarnpkg.com/@tsconfig/node12/-/node12-1.0.7.tgz#677bd9117e8164dc319987dd6ff5fc1ba6fbf18b" + integrity sha512-dgasobK/Y0wVMswcipr3k0HpevxFJLijN03A8mYfEPvWvOs14v0ZlYTR4kIgMx8g4+fTyTFv8/jLCIfRqLDJ4A== + +"@tsconfig/node14@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@tsconfig/node14/-/node14-1.0.0.tgz#5bd046e508b1ee90bc091766758838741fdefd6e" + integrity sha512-RKkL8eTdPv6t5EHgFKIVQgsDapugbuOptNd9OOunN/HAkzmmTnZELx1kNCK0rSdUYGmiFMM3rRQMAWiyp023LQ== + +"@tsconfig/node16@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.1.tgz#a6ca6a9a0ff366af433f42f5f0e124794ff6b8f1" + integrity sha512-FTgBI767POY/lKNDNbIzgAX6miIDBs6NTCbdlDb8TrWovHsSvaVIZDlTqym29C6UqhzwcJx4CYr+AlrMywA0cA== + "@types/color-name@^1.1.1": version "1.1.1" resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0" @@ -494,6 +521,11 @@ anymatch@~3.1.1: normalize-path "^3.0.0" picomatch "^2.0.4" +arg@^4.1.0: + version "4.1.3" + resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" + integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== + argparse@^1.0.7: version "1.0.10" resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" @@ -621,6 +653,11 @@ base64-js@^1.0.2: resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.1.tgz#58ece8cb75dd07e71ed08c736abc5fac4dbf8df1" integrity sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g== +big-integer@^1.6.16: + version "1.6.48" + resolved "https://registry.yarnpkg.com/big-integer/-/big-integer-1.6.48.tgz#8fd88bd1632cba4a1c8c3e3d7159f08bb95b4b9e" + integrity sha512-j51egjPa7/i+RdiRuJbPdJ2FIUYYPhvYLjzoYbcMMm62ooO6F94fETG4MTs46zPAF9Brs04OajboA/qTGuz78w== + big.js@^5.2.2: version "5.2.2" resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" @@ -656,6 +693,19 @@ braces@^3.0.1, braces@~3.0.2: dependencies: fill-range "^7.0.1" +broadcast-channel@^3.4.1: + version "3.6.0" + resolved "https://registry.yarnpkg.com/broadcast-channel/-/broadcast-channel-3.6.0.tgz#7843ffd86529be300dd1d03668a35301626ed03f" + integrity sha512-0x87tKJULniTOfECZP/LCsqWyMEbz0Oa+4yJ4i5dosOMxWUjx6mZ6nt9QmD2ox0r3MaCPojHrTQ2dj4ASZupeA== + dependencies: + "@babel/runtime" "^7.7.2" + detect-node "^2.1.0" + js-sha3 "0.8.0" + microseconds "0.2.0" + nano-time "1.0.0" + rimraf "3.0.2" + unload "2.2.0" + brorand@^1.0.1: version "1.1.0" resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" @@ -744,6 +794,11 @@ browserslist@^4.16.3: escalade "^3.1.1" node-releases "^1.1.71" +buffer-from@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" + integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A== + buffer-xor@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9" @@ -1018,6 +1073,11 @@ create-hmac@^1.1.0, create-hmac@^1.1.4, create-hmac@^1.1.7: safe-buffer "^5.0.1" sha.js "^2.4.8" +create-require@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" + integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== + cross-spawn@^7.0.2: version "7.0.3" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" @@ -1148,6 +1208,11 @@ des.js@^1.0.0: inherits "^2.0.1" minimalistic-assert "^1.0.0" +detect-node@^2.0.4, detect-node@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.1.0.tgz#c9c70775a49c3d03bc2c06d9a73be550f978f8b1" + integrity sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g== + detective@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/detective/-/detective-5.2.0.tgz#feb2a77e85b904ecdea459ad897cc90a99bd2a7b" @@ -1162,6 +1227,11 @@ didyoumean@^1.2.1: resolved "https://registry.yarnpkg.com/didyoumean/-/didyoumean-1.2.1.tgz#e92edfdada6537d484d73c0172fd1eba0c4976ff" integrity sha1-6S7f2tplN9SE1zwBcv0eugxJdv8= +diff@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" + integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== + diffie-hellman@^5.0.0: version "5.0.3" resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.3.tgz#40e8ee98f55a2149607146921c63e1ae5f3d2875" @@ -1643,6 +1713,11 @@ functional-red-black-tree@^1.0.1: resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= +fuse.js@^6.4.6: + version "6.4.6" + resolved "https://registry.yarnpkg.com/fuse.js/-/fuse.js-6.4.6.tgz#62f216c110e5aa22486aff20be7896d19a059b79" + integrity sha512-/gYxR/0VpXmWSfZOIPS3rWwU8SHgsRTwWuXhyb2O6s7aRuVtHtxCkR33bNYu3wyLyNx/Wpv0vU7FZy8Vj53VNw== + get-intrinsic@^1.0.2, get-intrinsic@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.1.tgz#15f59f376f855c446963948f0d24cd3637b4abc6" @@ -1871,6 +1946,11 @@ https-browserify@1.0.0, https-browserify@^1.0.0: resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73" integrity sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM= +husky@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/husky/-/husky-6.0.0.tgz#810f11869adf51604c32ea577edbc377d7f9319e" + integrity sha512-SQS2gDTB7tBN486QSoKPKQItZw97BMOd+Kdb6ghfpBc0yXyzrddI0oDV5MkDAbuB4X2mO3/nj60TRMcYxwzZeQ== + iconv-lite@0.4.24: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" @@ -2180,6 +2260,11 @@ jest-worker@27.0.0-next.5: merge-stream "^2.0.0" supports-color "^8.0.0" +js-sha3@0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.8.0.tgz#b9b7a5da73afad7dedd0f8c463954cbde6818840" + integrity sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q== + "js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" @@ -2305,6 +2390,11 @@ make-dir@^3.0.2: dependencies: semver "^6.0.0" +make-error@^1.1.1: + version "1.3.6" + resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" + integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== + markdown-escapes@^1.0.0: version "1.0.4" resolved "https://registry.yarnpkg.com/markdown-escapes/-/markdown-escapes-1.0.4.tgz#c95415ef451499d7602b91095f3c8e8975f78535" @@ -2317,6 +2407,14 @@ markdown-table@^2.0.0: dependencies: repeat-string "^1.0.0" +match-sorter@^6.0.2: + version "6.3.0" + resolved "https://registry.yarnpkg.com/match-sorter/-/match-sorter-6.3.0.tgz#454a1b31ed218cddbce6231a0ecb5fdc549fed01" + integrity sha512-efYOf/wUpNb8FgNY+cOD2EIJI1S5I7YPKsw0LBp7wqPh5pmMS6i/wr3ZWwfwrAw1NvqTA2KUReVRWDX84lUcOQ== + dependencies: + "@babel/runtime" "^7.12.5" + remove-accents "0.4.2" + md5.js@^1.3.4: version "1.3.5" resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.5.tgz#b5d07b8e3216e3e27cd728d72f70d1e6a342005f" @@ -2377,6 +2475,11 @@ micromatch@^4.0.2: braces "^3.0.1" picomatch "^2.0.5" +microseconds@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/microseconds/-/microseconds-0.2.0.tgz#233b25f50c62a65d861f978a4a4f8ec18797dc39" + integrity sha512-n7DHHMjR1avBbSpsTBj6fmMGh2AGrifVV4e+WYc3Q9lO+xnSZ3NyhcBND3vzzatt05LFhoKFRxrIyklmLlUtyA== + miller-rabin@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.1.tgz#f080351c865b0dc562a8462966daa53543c78a4d" @@ -2429,6 +2532,13 @@ ms@2.1.2: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== +nano-time@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/nano-time/-/nano-time-1.0.0.tgz#b0554f69ad89e22d0907f7a12b0993a5d96137ef" + integrity sha1-sFVPaa2J4i0JB/ehKwmTpdlhN+8= + dependencies: + big-integer "^1.6.16" + nanoid@^3.1.22: version "3.1.22" resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.22.tgz#b35f8fb7d151990a8aebd5aa5015c03cf726f844" @@ -3036,11 +3146,25 @@ react-dom@17.0.2: object-assign "^4.1.1" scheduler "^0.20.2" +react-icons@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/react-icons/-/react-icons-4.2.0.tgz#6dda80c8a8f338ff96a1851424d63083282630d0" + integrity sha512-rmzEDFt+AVXRzD7zDE21gcxyBizD/3NqjbX6cmViAgdqfJ2UiLer8927/QhhrXQV7dEj/1EGuOTPp7JnLYVJKQ== + react-is@16.13.1, react-is@^16.7.0, react-is@^16.8.1: version "16.13.1" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== +react-query@^3.17.0: + version "3.17.0" + resolved "https://registry.yarnpkg.com/react-query/-/react-query-3.17.0.tgz#461c0a030044760cd874c7ea8aa9d55c2dceb15d" + integrity sha512-/qUNb6ESCz75Z/bR5p/ztp5ipRj8IQSiIpHK3AkCLTT4IqZsceAoD+9B+wbitA0LkxsR3snGrpgKUc9MMYQ/Ow== + dependencies: + "@babel/runtime" "^7.5.5" + broadcast-channel "^3.4.1" + match-sorter "^6.0.2" + react-refresh@0.8.3: version "0.8.3" resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.8.3.tgz#721d4657672d400c5e3c75d063c4a85fb2d5d68f" @@ -3169,6 +3293,11 @@ remark@^12.0.1: remark-stringify "^8.0.0" unified "^9.0.0" +remove-accents@0.4.2: + version "0.4.2" + resolved "https://registry.yarnpkg.com/remove-accents/-/remove-accents-0.4.2.tgz#0a43d3aaae1e80db919e07ae254b285d9e1c7bb5" + integrity sha1-CkPTqq4egNuRngeuJUsoXZ4ce7U= + repeat-string@^1.0.0, repeat-string@^1.5.4: version "1.6.1" resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" @@ -3211,6 +3340,13 @@ rimraf@2.6.3: dependencies: glob "^7.1.3" +rimraf@3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" + integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== + dependencies: + glob "^7.1.3" + ripemd160@^2.0.0, ripemd160@^2.0.1: version "2.0.2" resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.2.tgz#a1c1a6f624751577ba5d07914cbc92850585890c" @@ -3334,6 +3470,14 @@ slice-ansi@^2.1.0: astral-regex "^1.0.0" is-fullwidth-code-point "^2.0.0" +source-map-support@^0.5.17: + version "0.5.19" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61" + integrity sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + source-map@0.7.3: version "0.7.3" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383" @@ -3351,7 +3495,7 @@ source-map@^0.5.0: resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w= -source-map@^0.6.1: +source-map@^0.6.0, source-map@^0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== @@ -3701,6 +3845,22 @@ trough@^1.0.0: resolved "https://registry.yarnpkg.com/trough/-/trough-1.0.5.tgz#b8b639cefad7d0bb2abd37d433ff8293efa5f406" integrity sha512-rvuRbTarPXmMb79SmzEp8aqXNKcK+y0XaB298IXueQ8I2PsrATcPBCSPyK/dDNa2iWOhKlfNnOjdAOTBU/nkFA== +ts-node@^10.0.0: + version "10.0.0" + resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.0.0.tgz#05f10b9a716b0b624129ad44f0ea05dac84ba3be" + integrity sha512-ROWeOIUvfFbPZkoDis0L/55Fk+6gFQNZwwKPLinacRl6tsxstTF1DbAcLKkovwnpKMVvOMHP1TIbnwXwtLg1gg== + dependencies: + "@tsconfig/node10" "^1.0.7" + "@tsconfig/node12" "^1.0.7" + "@tsconfig/node14" "^1.0.0" + "@tsconfig/node16" "^1.0.1" + arg "^4.1.0" + create-require "^1.1.0" + diff "^4.0.1" + make-error "^1.1.1" + source-map-support "^0.5.17" + yn "3.1.1" + ts-pnp@^1.1.6: version "1.2.0" resolved "https://registry.yarnpkg.com/ts-pnp/-/ts-pnp-1.2.0.tgz#a500ad084b0798f1c3071af391e65912c86bca92" @@ -3841,6 +4001,14 @@ universalify@^2.0.0: resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717" integrity sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ== +unload@2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/unload/-/unload-2.2.0.tgz#ccc88fdcad345faa06a92039ec0f80b488880ef7" + integrity sha512-B60uB5TNBLtN6/LsgAf3udH9saB5p7gqJwcFfbOEZ8BcBHnGwCf6G/TGiEqkRAxX7zAFIUtzdrXQSdL3Q/wqNA== + dependencies: + "@babel/runtime" "^7.6.2" + detect-node "^2.0.4" + unpipe@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" @@ -4008,6 +4176,11 @@ xtend@^4.0.0, xtend@^4.0.1, xtend@^4.0.2: resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== +yn@3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" + integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== + yocto-queue@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b"