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