Skip to content

Commit

Permalink
ui: refactor search page and actions
Browse files Browse the repository at this point in the history
Signed-off-by: pamfilos <[email protected]>
  • Loading branch information
pamfilos committed Mar 22, 2024
1 parent 936bcd6 commit 7ab0eb6
Show file tree
Hide file tree
Showing 4 changed files with 90 additions and 103 deletions.
40 changes: 28 additions & 12 deletions ui/src/components/search/CheckboxFacet.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* eslint-disable react-hooks/exhaustive-deps */
import React, { useState, useEffect } from "react";
import { useRouter } from "next/navigation";
import React, { useState, useEffect, useCallback } from "react";
import { useRouter, useSearchParams, usePathname } from "next/navigation";
import { Card } from "antd";

import { getSearchUrl } from "@/utils/utils";
Expand All @@ -9,28 +9,40 @@ import { Country, Journal, Params } from "@/types";
interface CheckboxFacetProps {
type: "country" | "journal";
title: string;
params: Params;
data: Country[] | Journal[];
}

const CheckboxFacet: React.FC<CheckboxFacetProps> = ({
type,
title,
params,
data,
}) => {
const [filters, setFilters] = useState<any[]>([]);
const [showMore, setShowMore] = useState(false);
const displayedData = showMore ? data : data?.slice(0, 13);
const router = useRouter();
const searchParams = useSearchParams();
const pathname = usePathname()

useEffect(() => {
setFilters([params[type]].flat());
}, []);
const createQueryString = useCallback(
(name: string, value: any) => {
const params = new URLSearchParams(searchParams)

params.delete(name);

if (!Array.isArray(value)) value = [value];
value.forEach((val: string) => {
params.append(name, val);
});

return params.toString()
},
[searchParams]
)

useEffect(() => {
router.push(getSearchUrl({ ...params, page: 1, [type]: filters }));
}, [filters]);
setFilters(searchParams.getAll(type));
}, []);

const shortJournalName = (value: string) => {
const journalMapping: Record<string, string> = {
Expand All @@ -46,12 +58,16 @@ const CheckboxFacet: React.FC<CheckboxFacetProps> = ({
};

const onCheckboxChange = (value: string) => {
let updated_filters = [];

if (filters.includes(value)) {
const newFilters = filters.filter((item: string) => item !== value);
setFilters(newFilters);
updated_filters = filters.filter((item: string) => item !== value);
} else {
setFilters((oldFilters) => [...oldFilters, value]);
updated_filters = [...filters, value]
}

setFilters(updated_filters);
router.replace(pathname + '?' + createQueryString(type, updated_filters))
};

return (
Expand Down
127 changes: 51 additions & 76 deletions ui/src/components/search/YearFacet.tsx
Original file line number Diff line number Diff line change
@@ -1,113 +1,77 @@
import React, { useEffect, useState } from "react";
import React, { useEffect, useState, useCallback } from "react";
import { XYPlot, VerticalBarSeries, Hint } from "react-vis";
import { Button, Card, Slider } from "antd";
import { SliderMarks } from "antd/es/slider";
import { useRouter } from "next/navigation";
import isEqual from "lodash.isequal";
import { useRouter, usePathname, useSearchParams } from "next/navigation";
import "react-vis/dist/style.css";

import { getSearchUrl } from "@/utils/utils";
import { PublicationYear, YearFacetData, Params } from "@/types";

interface YearFacetProps {
data: PublicationYear[];
params: Params;
}

const mapInitialDataToYears = (
initial: PublicationYear[]
): YearFacetData[] => {
return initial?.map((item) => ({
x: new Date(item?.key)?.getFullYear(),
y: item?.doc_count,
}));
};

const YearFacet: React.FC<YearFacetProps> = ({ data, params }) => {
const [hoveredBar, setHoveredBar] = useState<any>(null);
const [filters, setFilters] = useState<YearFacetData[]>([]);
const [initialData, setInitialData] = useState<YearFacetData[]>([]);
const [initialEndpoints, setInitialEndpoints] = useState<number[]>([]);
const [sliderEndpoints, setSliderEndpoints] = useState<number[]>([]);
const [marks, setMarks] = useState<SliderMarks>(undefined);
const [reset, setReset] = useState<boolean>(false);

const router = useRouter();
const pathname = usePathname()
const searchParams = useSearchParams();

useEffect(() => {
const initialData = mapInitialDataToYears(data);
setFilters(initialData);
setInitialData(initialData);
setSliderEndpoints(getSliderEndpoints(initialData));
setInitialEndpoints(getSliderEndpoints(initialData));
setMarks(getMarks(initialData));
}, []);
}, [data]);

useEffect(() => {
router.push(
getSearchUrl({
...params,
page: 1,
publication_year__range: resolveYearQuery(),
})
);
}, [filters]);

const resolveYearQuery = () => {
if (sliderEndpoints[0] === sliderEndpoints[1]) {
return sliderEndpoints[0]?.toString();
const resolveYearQuery = (range) => {
if (range[0] === range[1]) {
return range[0]?.toString();
}
return sliderEndpoints.join("__");
return range.join("__");
};

const mapInitialDataToYears = (
initial: PublicationYear[]
): YearFacetData[] => {
return initial?.map((item) => ({
x: new Date(item?.key)?.getFullYear(),
y: item?.doc_count,
}));
};
const createQueryString = useCallback(
(name: string, value: any) => {
const params = new URLSearchParams(searchParams)

params.delete(name);
params.set(name, resolveYearQuery([value[0].x, value[1].x]));

return params.toString()
},
[searchParams]
)

const getSliderEndpoints = (initial: YearFacetData[]): number[] => {
if (initial.length === 1) return [initial[0]?.x];
return [initial[0]?.x, initial[initial.length - 1]?.x];
};

const getMarks = (initial: YearFacetData[]): SliderMarks => {
if (initial.length === 1) {
return {
[initial[0]?.x]: [initial[0]?.x],
};
}
return {
[initial[0]?.x]: [initial[0]?.x],
[initial[initial.length - 1]?.x]: [initial[initial.length - 1]?.x],
};
};

const updateStateAndMarks = (newFilters: YearFacetData[]) => {
setSliderEndpoints(getSliderEndpoints(newFilters));
setMarks(getMarks(newFilters));
};

const sliderDataToGraphData = (data: number[]) => {
const firstIndex = initialData.findIndex(
(item: YearFacetData) => item.x === data[0]
);
const lastIndex = initialData.findIndex(
(item: YearFacetData) => item.x === data[data.length - 1]
);
const range = initialData.slice(firstIndex, lastIndex + 1);

if (range.length === 1) {
return [range, range].flat();
}

return range;
};

const onSliderChange = (data: number[]) => {
updateStateAndMarks(sliderDataToGraphData(data));
setSliderEndpoints(data)
};

const onSliderAfterChange = (data: number[]) => {
setFilters(sliderDataToGraphData(data));
const onSliderChange2 = (data: number[]) => {
setSliderEndpoints(data)
router.replace(pathname + '?' + createQueryString('publication_year__range', [{x:data[0]}, {x:data[1]}]))
};

const onBarClick = (value: YearFacetData) => {
updateStateAndMarks([value, value]);
setFilters([value, value]);
router.replace(pathname + '?' + createQueryString('publication_year__range', [value, value]))
};

const onBarMouseHover = (bar: YearFacetData) => {
Expand All @@ -117,15 +81,24 @@ const YearFacet: React.FC<YearFacetProps> = ({ data, params }) => {
const onBarMouseOut = () => setHoveredBar(null);

const resetFilters = () => {
setReset(!reset);
updateStateAndMarks(initialData);
setFilters(initialData);
const params = new URLSearchParams(searchParams);
params.delete('publication_year__range');
router.replace(pathname + params.toString())
};

let marks = {}
filters.map(
(i, idx) => {
marks[`${i.x}`] = {
label: idx == 0 || idx == filters.length-1 ? `${i.x}` : ` `
};
}
);

return (
<Card title="Year" className="search-facets-facet mb-5">
<div>
{!isEqual(initialData, filters) && (
{searchParams.get('publication_year__range') && (
<div className="text-right mb-3">
<Button
onClick={resetFilters}
Expand Down Expand Up @@ -157,10 +130,12 @@ const YearFacet: React.FC<YearFacetProps> = ({ data, params }) => {
</div>
<Slider
range
step={null}
className="year-facet-slider"
onChange={onSliderChange}
onAfterChange={onSliderAfterChange}
onAfterChange={onSliderChange2}
value={sliderEndpoints}
defaultValue={initialEndpoints}
min={initialEndpoints[0]}
max={initialEndpoints[1]}
marks={marks}
Expand Down
5 changes: 2 additions & 3 deletions ui/src/pages/search.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ const SearchPage: React.FC<SearchPageProps> = ({
data={countries}
title="Country / Region / Territory"
type={"country"}
params={query}
/>
)}

Expand All @@ -49,7 +48,6 @@ const SearchPage: React.FC<SearchPageProps> = ({
data={journals}
title="Journal"
type={"journal"}
params={query}
/>
)}
</>
Expand All @@ -68,7 +66,8 @@ const SearchPage: React.FC<SearchPageProps> = ({
export const getServerSideProps: GetServerSideProps = async (context) => {
const query = context?.query as unknown as Params;

const res = await fetch(getApiUrl() + getSearchUrl({ ...query }), authToken);
const url = getApiUrl() + getSearchUrl(query);
const res = await fetch(url, authToken);
const { results, count, facets } = (await res.json()) as Response;

return { props: { results, count, query, facets } };
Expand Down
21 changes: 9 additions & 12 deletions ui/src/utils/utils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,21 +25,18 @@ const isValue = (value: any): boolean =>
value !== undefined && value !== null && value !== "";

const buildSearchParams = (q: Params): string => {
const query = { ...defaultQueryValues, ...q };

const values = Object.entries(query).flatMap(([key, value]) => {
if (queryTypes.includes(key as QueryType)) {
if (Array.isArray(value)) {
return value.filter(isValue).map((v) => `${key}=${v}`);
} else if (isValue(value)) {
return [`${key}=${value}`];
}
const searchParams = new URLSearchParams();

Object.entries(q).forEach(([key, value]) => {
if (Array.isArray(value)) {
value.forEach(item => searchParams.append(key, item));
} else {
searchParams.set(key, value);
}
return [];
});

return values.join("&");
};
return searchParams.toString();
}

const getSearchUrl = (query: Params, local?: boolean) => {
const searchParams = buildSearchParams(query);
Expand Down

0 comments on commit 7ab0eb6

Please sign in to comment.