make the search work

This commit is contained in:
Matthias Kretschmann 2020-06-30 15:24:30 +02:00
parent 829ec502a2
commit 4a35fd73d2
Signed by: m
GPG Key ID: 606EEEF3C479A91F
10 changed files with 183 additions and 157 deletions

View File

@ -65,6 +65,11 @@ module.exports = ({ config }) => {
config.resolve.extensions.push('.ts', '.tsx')
// 'fs' fix for squid.js
config.node = {
fs: 'empty'
}
// Handle SVGs
// Don't use Storybook's default SVG Configuration
config.module.rules = config.module.rules.map((rule) => {

53
package-lock.json generated
View File

@ -14100,6 +14100,18 @@
"prepend-http": "^2.0.0",
"query-string": "^5.0.1",
"sort-keys": "^2.0.0"
},
"dependencies": {
"query-string": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/query-string/-/query-string-5.1.1.tgz",
"integrity": "sha512-gjWOsm2SoGlgLEdAGt7a6slVOk9mGiXmPFMqrEhLQ68rhQuBnpfs3+EmlvqKyxnCo9/PPlF+9MtY02S1aFg+Jw==",
"requires": {
"decode-uri-component": "^0.2.0",
"object-assign": "^4.1.0",
"strict-uri-encode": "^1.0.0"
}
}
}
},
"p-cancelable": {
@ -23978,6 +23990,18 @@
"prepend-http": "^2.0.0",
"query-string": "^5.0.1",
"sort-keys": "^2.0.0"
},
"dependencies": {
"query-string": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/query-string/-/query-string-5.1.1.tgz",
"integrity": "sha512-gjWOsm2SoGlgLEdAGt7a6slVOk9mGiXmPFMqrEhLQ68rhQuBnpfs3+EmlvqKyxnCo9/PPlF+9MtY02S1aFg+Jw==",
"requires": {
"decode-uri-component": "^0.2.0",
"object-assign": "^4.1.0",
"strict-uri-encode": "^1.0.0"
}
}
}
},
"p-cancelable": {
@ -36070,13 +36094,20 @@
"integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ=="
},
"query-string": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/query-string/-/query-string-5.1.1.tgz",
"integrity": "sha512-gjWOsm2SoGlgLEdAGt7a6slVOk9mGiXmPFMqrEhLQ68rhQuBnpfs3+EmlvqKyxnCo9/PPlF+9MtY02S1aFg+Jw==",
"version": "6.13.1",
"resolved": "https://registry.npmjs.org/query-string/-/query-string-6.13.1.tgz",
"integrity": "sha512-RfoButmcK+yCta1+FuU8REvisx1oEzhMKwhLUNcepQTPGcNMp1sIqjnfCtfnvGSQZQEhaBHvccujtWoUV3TTbA==",
"requires": {
"decode-uri-component": "^0.2.0",
"object-assign": "^4.1.0",
"strict-uri-encode": "^1.0.0"
"split-on-first": "^1.0.0",
"strict-uri-encode": "^2.0.0"
},
"dependencies": {
"strict-uri-encode": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz",
"integrity": "sha1-ucczDHBChi9rFC3CdLvMWGbONUY="
}
}
},
"querystring": {
@ -44017,6 +44048,18 @@
"timed-out": "^4.0.1",
"url-set-query": "^1.0.0",
"xhr": "^2.0.4"
},
"dependencies": {
"query-string": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/query-string/-/query-string-5.1.1.tgz",
"integrity": "sha512-gjWOsm2SoGlgLEdAGt7a6slVOk9mGiXmPFMqrEhLQ68rhQuBnpfs3+EmlvqKyxnCo9/PPlF+9MtY02S1aFg+Jw==",
"requires": {
"decode-uri-component": "^0.2.0",
"object-assign": "^4.1.0",
"strict-uri-encode": "^1.0.0"
}
}
}
},
"xhr-request-promise": {

View File

@ -49,6 +49,7 @@
"intersection-observer": "^0.11.0",
"is-url-superb": "^4.0.0",
"numeral": "^2.0.6",
"query-string": "^6.13.1",
"react": "^16.13.1",
"react-data-table-component": "^6.9.3",
"react-datepicker": "^3.0.0",

View File

@ -2,7 +2,15 @@
margin-bottom: var(--spacer);
}
.inputGroup .input {
.inputGroup > div {
margin: 0;
}
.inputGroup label {
display: none;
}
.inputGroup input {
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
}
@ -12,44 +20,23 @@
border-top-right-radius: 0;
margin-top: -1px;
width: 100%;
border-width: 1px;
text-transform: uppercase;
color: var(--brand-grey-lighter);
background: var(--brand-gradient);
box-shadow: none;
}
.inputGroup button:hover,
.inputGroup button:focus,
.inputGroup .input:focus + button:hover,
.inputGroup .input:focus + button:focus {
.inputGroup input:focus + button:hover,
.inputGroup input:focus + button:focus {
background: var(--brand-gradient);
transform: none;
box-shadow: none;
}
.inputGroup .input:focus + button {
color: var(--brand-white);
}
/* .inputGroup button:hover,
.inputGroup button:focus,
.inputGroup .input:focus + button {
color: var(--brand-white);
background-color: var(--brand-pink);
}
.inputGroup .input:focus + button {
border-color: var(--brand-pink);
background-color: var(--brand-pink);
} */
@media screen and (min-width: 30rem) {
.inputGroup {
display: flex;
}
.inputGroup .input {
.inputGroup input {
border-bottom-left-radius: var(--border-radius);
border-top-right-radius: 0;
border-bottom-right-radius: 0;
@ -64,14 +51,3 @@
width: auto;
}
}
.input {
composes: input from './Form/FieldTemplate.module.css';
}
.large {
composes: large from './Form/FieldTemplate.module.css';
}
.filters {
}

View File

@ -3,6 +3,7 @@ import { useNavigate } from '@reach/router'
import styles from './SearchBar.module.css'
import Loader from '../atoms/Loader'
import Button from '../atoms/Button'
import Input from '../atoms/Input'
export default function SearchBar({
placeholder,
@ -35,12 +36,12 @@ export default function SearchBar({
return (
<form className={styles.form}>
<div className={styles.inputGroup}>
<input
<Input
type="search"
className={large ? `${styles.input} ${styles.large}` : styles.input}
name="search"
placeholder={placeholder || 'What are you looking for?'}
value={value}
onChange={(e) => handleChange(e)}
onChange={handleChange}
required
/>
<Button onClick={(e: FormEvent<HTMLButtonElement>) => startSearch(e)}>

View File

@ -1,7 +1,3 @@
.input {
composes: input from './Form/FieldTemplate.module.css';
}
.layout {
display: flex;
flex-direction: column;

View File

@ -3,6 +3,7 @@ import SearchFilterSection from '../atoms/SearchFilterSection'
import usePriceQueryParams from '../../hooks/usePriceQueryParams'
import styles from './SearchPriceFilter.module.css'
import Input from '../atoms/Input'
export declare type PriceInputProps = {
label: string
@ -18,18 +19,14 @@ export const PriceInput = ({
text
}: PriceInputProps) => {
return (
<label htmlFor={label} className={styles.label}>
<input
id={label}
name={label}
type="number"
min="0"
value={value}
onChange={onChange}
className={styles.input}
/>
<span>{text}</span>
</label>
<Input
name={label}
label={text}
type="number"
min="0"
value={value}
onChange={onChange}
/>
)
}

View File

@ -1,43 +1,110 @@
import React from 'react'
import { QueryResult } from '@oceanprotocol/squid/dist/node/aquarius/Aquarius'
import Layout from '../../components/Layout'
import PageHeader from '../molecules/PageHeader'
import React, { ReactElement, useState, useEffect } from 'react'
import {
QueryResult,
SearchQuery
} from '@oceanprotocol/squid/dist/node/aquarius/Aquarius'
import SearchBar from '../molecules/SearchBar'
import AssetList from '../organisms/AssetList'
import { SearchPriceFilter } from '../molecules/SearchPriceFilter'
import styles from './Search.module.css'
import { priceQueryParamToWei } from '../../utils'
import { Aquarius, Logger } from '@oceanprotocol/squid'
import { config } from '../../config/ocean'
import { useLocation } from '@reach/router'
import queryString from 'query-string'
export declare type SearchPageProps = {
text: string | string[]
tag: string | string[]
queryResult: QueryResult
}
const SearchPage = ({ text, tag, queryResult }: SearchPageProps) => {
return (
<Layout noPageHeader>
<section className={styles.grid}>
<div className={styles.search}>
<PageHeader title={`Search for ${text || tag}`} />
{text && <SearchBar initialValue={text as string} />}
</div>
export function getSearchQuery(
page?: string | string[],
offset?: string | string[],
text?: string | string[],
tag?: string | string[],
priceQuery?: [string | undefined, string | undefined]
): SearchQuery {
return {
page: Number(page) || 1,
offset: Number(offset) || 20,
query: {
text,
tags: tag ? [tag] : undefined,
price: priceQuery
},
sort: {
created: -1
}
<aside className={styles.side}>
<SearchPriceFilter />
</aside>
<div className={styles.results}>
{queryResult.results.length > 0 ? (
<AssetList queryResult={queryResult} />
) : (
<div className={styles.empty}>No results found.</div>
)}
</div>
</section>
</Layout>
)
// Something in squid-js is weird when using 'categories: [type]'
// which is the only way the query actually returns desired results.
// But it doesn't follow 'SearchQuery' interface so we have to assign
// it here.
} as SearchQuery
}
export default SearchPage
export async function getResults(params: any): Promise<QueryResult> {
const { text, tag, page, offset, minPrice, maxPrice } = params
const minPriceParsed = priceQueryParamToWei(
minPrice as string,
'Error parsing context.query.minPrice'
)
const maxPriceParsed = priceQueryParamToWei(
maxPrice as string,
'Error parsing context.query.maxPrice'
)
const priceQuery =
minPriceParsed || maxPriceParsed
? // sometimes TS gets a bit silly
([minPriceParsed, maxPriceParsed] as [
string | undefined,
string | undefined
])
: undefined
const aquarius = new Aquarius(config.aquariusUri as string, Logger)
const queryResult = await aquarius.queryMetadata(
getSearchQuery(page, offset, text, tag, priceQuery)
)
return queryResult
}
export default function SearchPage(): ReactElement {
const location = useLocation()
const parsed = queryString.parse(location.search)
const { text, tag } = parsed
const [queryResult, setQueryResult] = useState<QueryResult>()
useEffect(() => {
async function initSearch() {
const results = await getResults(parsed)
setQueryResult(results)
}
initSearch()
}, [])
return (
<section className={styles.grid}>
<div className={styles.search}>
{text && <SearchBar initialValue={text as string} />}
</div>
<aside className={styles.side}>
<SearchPriceFilter />
</aside>
<div className={styles.results}>
{queryResult && queryResult.results.length > 0 ? (
<AssetList queryResult={queryResult} />
) : (
<div className={styles.empty}>No results found.</div>
)}
</div>
</section>
)
}

View File

@ -1,14 +1,15 @@
import { useEffect, useState } from 'react'
import { useLocation } from '@reach/router'
import queryString from 'query-string'
export default function usePriceQueryParams() {
const location = useLocation()
const [min, setMin] = useState(
(JSON.parse(location.search).minPrice as string) || '0'
(queryString.parse(location.search).minPrice as string) || '0'
)
const [max, setMax] = useState(
(JSON.parse(location.search).maxPrice as string) || '0'
(queryString.parse(location.search).maxPrice as string) || '0'
)
useEffect(() => {

View File

@ -1,77 +1,16 @@
import React, { ReactElement } from 'react'
import { Aquarius, Logger } from '@oceanprotocol/squid'
import { SearchQuery } from '@oceanprotocol/squid/dist/node/aquarius/Aquarius'
import PageSearch, { SearchPageProps } from '../components/templates/Search'
import { config } from '../config/ocean'
import { JSONparse, priceQueryParamToWei } from '../utils'
import PageSearch from '../components/templates/Search'
import { PageProps } from 'gatsby'
import Layout from '../components/Layout'
import queryString from 'query-string'
export default function PageGatsbySearch(props: PageProps): ReactElement {
const content = (props.data as any).content.edges[0].node.childPagesJson
const { title, description } = content
const parsed = queryString.parse(props.location.search)
const { text, tag } = parsed
return (
<Layout title={title} description={description} location={props.location}>
<PageSearch text={text} tag={tag} queryResult={queryResult} />
<Layout title={`Search for ${text || tag}`} location={props.location}>
<PageSearch />
</Layout>
)
}
// export function getSearchQuery(
// page?: string | string[],
// offset?: string | string[],
// text?: string | string[],
// tag?: string | string[],
// priceQuery?: [string | undefined, string | undefined]
// ) {
// return {
// page: Number(page) || 1,
// offset: Number(offset) || 20,
// query: {
// text,
// tags: tag ? [tag] : undefined,
// price: priceQuery
// },
// sort: {
// created: -1
// }
// // Something in squid-js is weird when using 'categories: [type]'
// // which is the only way the query actually returns desired results.
// // But it doesn't follow 'SearchQuery' interface so we have to assign
// // it here.
// } as SearchQuery
// }
// Search.getInitialProps = async (context) => {
// const { text, tag, page, offset, minPrice, maxPrice } = context.query
// const minPriceParsed = priceQueryParamToWei(
// minPrice as string,
// 'Error parsing context.query.minPrice'
// )
// const maxPriceParsed = priceQueryParamToWei(
// maxPrice as string,
// 'Error parsing context.query.maxPrice'
// )
// const priceQuery =
// minPriceParsed || maxPriceParsed
// ? // sometimes TS gets a bit silly
// ([minPriceParsed, maxPriceParsed] as [
// string | undefined,
// string | undefined
// ])
// : undefined
// const aquarius = new Aquarius(config.aquariusUri as string, Logger)
// const queryResult = await aquarius.queryMetadata(
// getSearchQuery(page, offset, text, tag, priceQuery)
// )
// return {
// text: text,
// tag: tag,
// queryResult
// }
// }