From 53de4389d3a567d9738852c4d55ce7784265e476 Mon Sep 17 00:00:00 2001 From: Christophe <christophe.misc+git@protonmail.ch> Date: Tue, 17 Oct 2023 16:03:58 +0200 Subject: [PATCH] feat(index): sorting --- frontend/components/OrderingSelector.tsx | 36 +++++++++++ frontend/lib/Ordering.ts | 11 ++++ frontend/pages/index.tsx | 79 +++++++++++++----------- 3 files changed, 90 insertions(+), 36 deletions(-) create mode 100644 frontend/components/OrderingSelector.tsx create mode 100644 frontend/lib/Ordering.ts diff --git a/frontend/components/OrderingSelector.tsx b/frontend/components/OrderingSelector.tsx new file mode 100644 index 0000000..0787798 --- /dev/null +++ b/frontend/components/OrderingSelector.tsx @@ -0,0 +1,36 @@ +import { ChangeEventHandler, FC } from 'react'; +import Ordering from 'lib/Ordering'; + +type OrderingSelectorProps = { + onChange: (ordering: Ordering | undefined) => void; +}; +const OrderingSelector: FC<OrderingSelectorProps> = ({ onChange }) => { + const _onChange: ChangeEventHandler<HTMLSelectElement> = (e) => { + if (e.target.value.trim().length === 0) { + onChange(undefined); + return; + } + onChange(e.target.value as Ordering); + }; + + return ( + <div> + <label htmlFor="ordering" className="mr-2 text-lg"> + Ordering: + </label> + <select + className="rounded-md w-60 disabled:opacity-50" + id="ordering" + onChange={_onChange} + > + <option></option> + <option value={Ordering.TITLE_ASC}>Title Ascending</option> + <option value={Ordering.TITLE_DES}>Title Descending</option> + <option value={Ordering.SCORE_ASC}>Score Ascending</option> + <option value={Ordering.SCORE_DES}>Score Descending</option> + </select> + </div> + ); +}; + +export default OrderingSelector; diff --git a/frontend/lib/Ordering.ts b/frontend/lib/Ordering.ts new file mode 100644 index 0000000..c418d4e --- /dev/null +++ b/frontend/lib/Ordering.ts @@ -0,0 +1,11 @@ +const Ordering = { + ID_ASC: '+id', + ID_DES: '-id', + SCORE_ASC: '+score', + SCORE_DES: '-score', + TITLE_ASC: '+title', + TITLE_DES: '-title', +} as const; +type Ordering = typeof Ordering[keyof typeof Ordering]; + +export default Ordering; diff --git a/frontend/pages/index.tsx b/frontend/pages/index.tsx index d4d9ecb..18636bc 100644 --- a/frontend/pages/index.tsx +++ b/frontend/pages/index.tsx @@ -8,15 +8,18 @@ import ErrorBox from 'components/ErrorBox'; import { useMemo, useState } from 'react'; import { difference } from 'lodash'; import SelectedTag from 'components/SelectedTag'; +import Ordering from 'lib/Ordering'; +import OrderingSelector from 'components/OrderingSelector'; // TODO: SSR? const Templates: NextPage = () => { const api = useTemplateApi(); const [selectedTags, setSelectedTags] = useState<string[]>([]); + const [ordering, setOrdering] = useState<Ordering | undefined>(undefined); const templates = useQuery( - ['templates', selectedTags], - () => api.listTemplates(undefined, selectedTags), + ['templates', selectedTags, ordering], + () => api.listTemplates(undefined, selectedTags, undefined, ordering), { keepPreviousData: true, } @@ -58,41 +61,45 @@ const Templates: NextPage = () => { {templates.data && ( <> - <div className="flex flex-row items-center mb-2"> - <label htmlFor="tag-select" className="mr-2 text-lg"> - Filter by tags: - </label> - <select - onChange={(e) => select(e.currentTarget.value)} - className="rounded-md w-60 disabled:opacity-50" - id="tag-select" - disabled={availableTags.length === selectedTags.length} - value={''} - > - {availableTags.length === selectedTags.length ? ( - <option>No more tags available.</option> - ) : ( - <> - <option selected></option> - {difference(availableTags, selectedTags) - .sort() - .map((tag) => ( - <option key={tag} value={tag}> - {tag} - </option> - ))} - </> - )} - </select> - <div className="ml-2 flex flex-row flex-wrap gap-1"> - {selectedTags.map((tag) => ( - <SelectedTag - key={tag} - tag={tag} - onDelete={(tag) => unselect(tag)} - /> - ))} + <div className="flex flex-row"> + <div className="flex flex-row items-center mb-2"> + <label htmlFor="tag-select" className="mr-2 text-lg"> + Filter by tags: + </label> + <select + onChange={(e) => select(e.currentTarget.value)} + className="rounded-md w-60 disabled:opacity-50" + id="tag-select" + disabled={availableTags.length === selectedTags.length} + value={''} + > + {availableTags.length === selectedTags.length ? ( + <option>No more tags available.</option> + ) : ( + <> + <option selected></option> + {difference(availableTags, selectedTags) + .sort() + .map((tag) => ( + <option key={tag} value={tag}> + {tag} + </option> + ))} + </> + )} + </select> + <div className="ml-2 flex flex-row flex-wrap gap-1"> + {selectedTags.map((tag) => ( + <SelectedTag + key={tag} + tag={tag} + onDelete={(tag) => unselect(tag)} + /> + ))} + </div> </div> + <div className="flex-grow" /> + <OrderingSelector onChange={setOrdering} /> </div> <div className="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-4 auto-rows-fr"> {templates.data.data.map((template) => ( -- GitLab