aboutsummaryrefslogtreecommitdiffstats
path: root/apps/web/components
diff options
context:
space:
mode:
authorDaksh Pareek <dakshpareek7@gmail.com>2025-01-12 21:53:34 +0530
committerGitHub <noreply@github.com>2025-01-12 16:23:34 +0000
commitb6293d118e7545b81e216073e66cd54c5b1a0b00 (patch)
tree19c2308ea7ea26593d890770945a2c5c71440048 /apps/web/components
parentb8bd7d7eb27aaaadae728599f64a0874f66196ea (diff)
downloadkarakeep-b6293d118e7545b81e216073e66cd54c5b1a0b00.tar.zst
feat: Add Bookmark Sorting Feature (#812)
* feat: add bookmark sorting by creation date - Add sort order toggle in GlobalActions component - Implement ascending/descending sort functionality - Update translations for sorting feature in all languages - Add sort order icons and dropdown menu - Maintain sort preference in URL params * feat: add bookmark sorting by creation date - Add sort order toggle in GlobalActions component - Implement ascending/descending sort functionality - Update translations for sorting feature in all languages - Add sort order icons and dropdown menu - Maintain sort preference in URL params during session Note: Sort order resets to default on page refresh, server-side persistence can be implemented in future enhancement * feat: Add global sort by date feature with shared sort order state - Implement global sort order functionality using a shared Zustand store (`useSortOrder` hook). - Update `getBookmarks` and `searchBookmarks` endpoints to accept a `sortOrder` parameter. - Refactor code to import `ZSortOrder` from shared types (`bookmarks.ts`), ensuring consistency across the codebase. - Update components (`UpdatableBookmarksGrid`, `bookmark-search`) to use the shared `useSortOrder` hook. - Remove unused `zSortBy` definition from `packages/shared/types/bookmarks.ts` to avoid confusion. - Ensure consistent naming conventions by prefixing Zod inferred types with `Z`. - Clean up code and address previous PR feedback comments. * tiny fixes and fixing TS errors --------- Co-authored-by: Mohamed Bassem <me@mbassem.com>
Diffstat (limited to 'apps/web/components')
-rw-r--r--apps/web/components/dashboard/GlobalActions.tsx2
-rw-r--r--apps/web/components/dashboard/SortOrderToggle.tsx56
-rw-r--r--apps/web/components/dashboard/bookmarks/Bookmarks.tsx2
-rw-r--r--apps/web/components/dashboard/bookmarks/UpdatableBookmarksGrid.tsx19
4 files changed, 74 insertions, 5 deletions
diff --git a/apps/web/components/dashboard/GlobalActions.tsx b/apps/web/components/dashboard/GlobalActions.tsx
index 9c05dddf..ecbb70bf 100644
--- a/apps/web/components/dashboard/GlobalActions.tsx
+++ b/apps/web/components/dashboard/GlobalActions.tsx
@@ -2,11 +2,13 @@
import BulkBookmarksAction from "@/components/dashboard/BulkBookmarksAction";
import ChangeLayout from "@/components/dashboard/ChangeLayout";
+import SortOrderToggle from "@/components/dashboard/SortOrderToggle";
export default function GlobalActions() {
return (
<div className="flex min-w-max flex-wrap overflow-hidden">
<ChangeLayout />
+ <SortOrderToggle />
<BulkBookmarksAction />
</div>
);
diff --git a/apps/web/components/dashboard/SortOrderToggle.tsx b/apps/web/components/dashboard/SortOrderToggle.tsx
new file mode 100644
index 00000000..8c0f617d
--- /dev/null
+++ b/apps/web/components/dashboard/SortOrderToggle.tsx
@@ -0,0 +1,56 @@
+import { ButtonWithTooltip } from "@/components/ui/button";
+import {
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuItem,
+ DropdownMenuTrigger,
+} from "@/components/ui/dropdown-menu";
+import { useTranslation } from "@/lib/i18n/client";
+import { useSortOrderStore } from "@/lib/store/useSortOrderStore";
+import { Check, SortAsc, SortDesc } from "lucide-react";
+
+export default function SortOrderToggle() {
+ const { t } = useTranslation();
+
+ const { sortOrder: currentSort, setSortOrder } = useSortOrderStore();
+
+ return (
+ <DropdownMenu>
+ <DropdownMenuTrigger asChild>
+ <ButtonWithTooltip
+ tooltip={t("actions.sort.title")}
+ delayDuration={100}
+ variant="ghost"
+ >
+ {currentSort === "asc" ? (
+ <SortAsc size={18} />
+ ) : (
+ <SortDesc size={18} />
+ )}
+ </ButtonWithTooltip>
+ </DropdownMenuTrigger>
+ <DropdownMenuContent className="w-fit">
+ <DropdownMenuItem
+ className="cursor-pointer justify-between"
+ onClick={() => setSortOrder("desc")}
+ >
+ <div className="flex items-center">
+ <SortDesc size={16} className="mr-2" />
+ <span>{t("actions.sort.newest_first")}</span>
+ </div>
+ {currentSort === "desc" && <Check className="ml-2 h-4 w-4" />}
+ </DropdownMenuItem>
+ <DropdownMenuItem
+ className="cursor-pointer justify-between"
+ onClick={() => setSortOrder("asc")}
+ >
+ <div className="flex items-center">
+ <SortAsc size={16} className="mr-2" />
+ <span>{t("actions.sort.oldest_first")}</span>
+ </div>
+ {currentSort === "asc" && <Check className="ml-2 h-4 w-4" />}
+ </DropdownMenuItem>
+ </DropdownMenuContent>
+ </DropdownMenu>
+ );
+}
diff --git a/apps/web/components/dashboard/bookmarks/Bookmarks.tsx b/apps/web/components/dashboard/bookmarks/Bookmarks.tsx
index 5729e846..3f606346 100644
--- a/apps/web/components/dashboard/bookmarks/Bookmarks.tsx
+++ b/apps/web/components/dashboard/bookmarks/Bookmarks.tsx
@@ -13,7 +13,7 @@ export default async function Bookmarks({
showDivider,
showEditorCard = false,
}: {
- query: ZGetBookmarksRequest;
+ query: Omit<ZGetBookmarksRequest, "sortOrder">; // Sort order is handled by the store
header?: React.ReactNode;
showDivider?: boolean;
showEditorCard?: boolean;
diff --git a/apps/web/components/dashboard/bookmarks/UpdatableBookmarksGrid.tsx b/apps/web/components/dashboard/bookmarks/UpdatableBookmarksGrid.tsx
index d18eeb1b..e43d061b 100644
--- a/apps/web/components/dashboard/bookmarks/UpdatableBookmarksGrid.tsx
+++ b/apps/web/components/dashboard/bookmarks/UpdatableBookmarksGrid.tsx
@@ -1,6 +1,8 @@
"use client";
+import { useEffect } from "react";
import UploadDropzone from "@/components/dashboard/UploadDropzone";
+import { useSortOrderStore } from "@/lib/store/useSortOrderStore";
import { api } from "@/lib/trpc";
import type {
@@ -16,14 +18,18 @@ export default function UpdatableBookmarksGrid({
bookmarks: initialBookmarks,
showEditorCard = false,
}: {
- query: ZGetBookmarksRequest;
+ query: Omit<ZGetBookmarksRequest, "sortOrder">; // Sort order is handled by the store
bookmarks: ZGetBookmarksResponse;
showEditorCard?: boolean;
itemsPerPage?: number;
}) {
- const { data, fetchNextPage, hasNextPage, isFetchingNextPage } =
+ const sortOrder = useSortOrderStore((state) => state.sortOrder);
+
+ const finalQuery = { ...query, sortOrder };
+
+ const { data, fetchNextPage, hasNextPage, isFetchingNextPage, refetch } =
api.bookmarks.getBookmarks.useInfiniteQuery(
- { ...query, useCursorV2: true },
+ { ...finalQuery, useCursorV2: true },
{
initialData: () => ({
pages: [initialBookmarks],
@@ -31,9 +37,14 @@ export default function UpdatableBookmarksGrid({
}),
initialCursor: null,
getNextPageParam: (lastPage) => lastPage.nextCursor,
+ refetchOnMount: true,
},
);
+ useEffect(() => {
+ refetch();
+ }, [sortOrder, refetch]);
+
const grid = (
<BookmarksGrid
bookmarks={data!.pages.flatMap((b) => b.bookmarks)}
@@ -45,7 +56,7 @@ export default function UpdatableBookmarksGrid({
);
return (
- <BookmarkGridContextProvider query={query}>
+ <BookmarkGridContextProvider query={finalQuery}>
{showEditorCard ? <UploadDropzone>{grid}</UploadDropzone> : grid}
</BookmarkGridContextProvider>
);