diff --git a/application/src/lib/transform.ts b/application/src/lib/transform.ts index 8590128..fbdd2f0 100644 --- a/application/src/lib/transform.ts +++ b/application/src/lib/transform.ts @@ -18,6 +18,14 @@ export function preserveNull (cast: (value:Source)=>Target) { } } +export function undefinedToDefault (defaultValue:Type): (value:Type|undefined)=>Type { + return (value) => typeof value === 'undefined' ? defaultValue : value +} + +export function stringTrimmed (value: string|undefined): string { + return (value ?? '').trim().replace(/^\++|\++$/g, '') +} + export function stringToInt (value: string): number { return parseInt(value) } diff --git a/application/src/pages/api/feed.ts b/application/src/pages/api/feed.ts index b3cc599..1462f04 100644 --- a/application/src/pages/api/feed.ts +++ b/application/src/pages/api/feed.ts @@ -8,7 +8,7 @@ const handleFeedSearch = async (req: NextApiRequest, res: NextApiResponse { diff --git a/application/src/pages/api/node.ts b/application/src/pages/api/node.ts index 02e04f4..753e30c 100644 --- a/application/src/pages/api/node.ts +++ b/application/src/pages/api/node.ts @@ -8,9 +8,7 @@ const handleFeedSearch = async (req: NextApiRequest, res: NextApiResponse> = ({ matomoConfig }) => { - const [query, setQuery] = useState('') - const [submitted, setSubmitted] = useState(null) - const [loading, setLoading] = useState(false) - const [results, setResults] = useState([]) - const [page, setPage] = useState(0) - const [hasMore, setHasMore] = useState(false) - const [loaded, setLoaded] = useState(false) - const [sort, setSort] = useState({ - sortBy: 'refreshedAt', sortWay: 'desc' - }) + const router = useRouter() + const routerQuery = nodeRequestQuerySchema.parse(router.query) + console.log('Router query', routerQuery) + const [query, setQuery] = useState(routerQuery) + const [submitted, setSubmitted] = useState(null) + const [loading, setLoading] = useState(false) + const [results, setResults] = useState([]) + const [page, setPage] = useState(0) + const [hasMore, setHasMore] = useState(false) + const [loaded, setLoaded] = useState(false) const search = async () => { setLoading(true) @@ -34,7 +34,7 @@ const Nodes: React.FC> = console.info('Retrieving results', { query, page }) source = axios.CancelToken.source() const response = await axios.get('/api/node', { - params: { search: query, page, sortBy: sort.sortBy, sortWay: sort.sortWay }, + params: { ...query, page }, cancelToken: source.token }) const responseData = await nodeResponseSchema.parseAsync(response.data) @@ -54,6 +54,8 @@ const Nodes: React.FC> = const loadNewQueryResults = () => { console.info('Cancelling searches') source.cancel('New query on the way') + router.query = query + router.push(router) setResults([]) setHasMore(false) setLoaded(false) @@ -86,9 +88,13 @@ const Nodes: React.FC> = } const handleQueryChange = (event) => { - const value = event.target.value - console.info('Query changed', { query: value }) - setQuery(value) + const targetInput = event.target + const value = targetInput.value + const name = targetInput.name + const newQuery:NodeRequestQuery = { ...query } + newQuery[name] = value + console.info('Query changed', { name, value }) + setQuery(newQuery) setPage(0) } @@ -104,8 +110,8 @@ const Nodes: React.FC> = setPage(page + 1) } - const toggleSort = (sortBy: StatsRequestSortBy) => { - const sortWay = sort.sortBy === sortBy && sort.sortWay === 'asc' ? 'desc' : 'asc' + const toggleSort = (sortBy: NodeRequestSortBy) => { + const sortWay = query.sortBy === sortBy && query.sortWay === 'asc' ? 'desc' : 'asc' getMatomo(matomoConfig).trackEvent({ category: 'nodes', action: 'sort', @@ -116,13 +122,13 @@ const Nodes: React.FC> = } ] }) - setSort({ - sortBy: sortBy, - sortWay: sortWay - }) + const newQuery:NodeRequestQuery = { ...query } + newQuery.sortBy = sortBy + newQuery.sortWay = sortWay + setQuery(newQuery) } - useEffect(loadNewQueryResults, [query, submitted, sort]) + useEffect(loadNewQueryResults, [query, submitted]) useEffect(loadNextPageResults, [page]) return ( @@ -134,13 +140,13 @@ const Nodes: React.FC> =
> = - + Domain - + Software User count - + Statuses - + Registrations - + Last refreshed - + Total - + Month active - + Half year active diff --git a/application/src/types/FeedRequest.ts b/application/src/types/FeedRequest.ts index 118581a..ae2d6bd 100644 --- a/application/src/types/FeedRequest.ts +++ b/application/src/types/FeedRequest.ts @@ -1,8 +1,12 @@ import { z } from 'zod' -import { preserveUndefined, stringToInt, transform } from '../lib/transform' +import { preserveUndefined, stringToInt, stringTrimmed, transform } from '../lib/transform' export const feedRequestQuerySchema = z.object({ - search: z.string().optional() + search: transform( + z.string().optional(), + stringTrimmed, + z.string() + ) /* softwareName: z.string().optional(), domain: z.string().optional(), diff --git a/application/src/types/NodeRequest.ts b/application/src/types/NodeRequest.ts index 1c05e73..203fc40 100644 --- a/application/src/types/NodeRequest.ts +++ b/application/src/types/NodeRequest.ts @@ -1,7 +1,7 @@ import { z } from 'zod' -import { preserveUndefined, stringToInt, transform } from '../lib/transform' +import { preserveUndefined, stringToInt, stringTrimmed, transform, undefinedToDefault } from '../lib/transform' -export const statsRequestSortBySchema = z.enum([ +export const nodeRequestSortBySchema = z.enum([ 'softwareName', 'softwareVersion', 'totalUserCount', @@ -13,15 +13,30 @@ export const statsRequestSortBySchema = z.enum([ 'domain' ]) -export const statsRequestSortWaySchema = z.enum([ +export const nodeRequestSortWaySchema = z.enum([ 'asc', 'desc' ]) -export const nodeRequestSchema = z.object({ - sortBy: z.optional(statsRequestSortBySchema), - sortWay: z.optional(statsRequestSortWaySchema), - search: z.string().optional(), +export const nodeRequestQuerySchema = z.object({ + sortBy: transform( + z.optional(nodeRequestSortBySchema), + undefinedToDefault('refreshedAt'), + nodeRequestSortBySchema + ), + sortWay: transform( + z.optional(nodeRequestSortWaySchema), + undefinedToDefault('desc'), + nodeRequestSortWaySchema + ), + search: transform( + z.string().optional(), + stringTrimmed, + z.string() + ) +}) + +export const nodeRequestSchema = nodeRequestQuerySchema.extend({ page: transform( z.string().optional(), preserveUndefined(stringToInt), @@ -29,4 +44,7 @@ export const nodeRequestSchema = z.object({ ) }) +export type NodeRequestQuery = z.infer export type NodeRequest = z.infer +export type NodeRequestSortWay = z.infer +export type NodeRequestSortBy = z.infer diff --git a/application/src/types/Sort.ts b/application/src/types/Sort.ts index 6a0a72f..ef14b20 100644 --- a/application/src/types/Sort.ts +++ b/application/src/types/Sort.ts @@ -1,4 +1,4 @@ export type Sort = { - sortBy: string, - sortWay: 'asc' | 'desc' + sortBy?: string, + sortWay?: 'asc' | 'desc' }