Skip to content

Commit

Permalink
add file searching
Browse files Browse the repository at this point in the history
  • Loading branch information
RealFascinated committed Mar 8, 2025
1 parent b014080 commit 0f3f7d7
Show file tree
Hide file tree
Showing 5 changed files with 68 additions and 12 deletions.
3 changes: 3 additions & 0 deletions bun.lock
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
"@radix-ui/react-tooltip": "^1.1.8",
"@t3-oss/env-core": "^0.12.0",
"@tanstack/react-query": "^5.67.1",
"@uidotdev/usehooks": "^2.4.1",
"axios": "^1.8.1",
"better-auth": "^1.2.3",
"bluebun": "^0.0.34",
Expand Down Expand Up @@ -428,6 +429,8 @@

"@typescript-eslint/visitor-keys": ["@typescript-eslint/[email protected]", "", { "dependencies": { "@typescript-eslint/types": "8.26.0", "eslint-visitor-keys": "^4.2.0" } }, "sha512-2z8JQJWAzPdDd51dRQ/oqIJxe99/hoLIqmf8RMCAJQtYDc535W/Jt2+RTP4bP0aKeBG1F65yjIZuczOXCmbWwg=="],

"@uidotdev/usehooks": ["@uidotdev/[email protected]", "", { "peerDependencies": { "react": ">=18.0.0", "react-dom": ">=18.0.0" } }, "sha512-1I+RwWyS+kdv3Mv0Vmc+p0dPYH0DTRAo04HLyXReYBL9AeseDWUJyi4THuksBJcu9F0Pih69Ak150VDnqbVnXg=="],

"@zxing/text-encoding": ["@zxing/[email protected]", "", {}, "sha512-U/4aVJ2mxI0aDNI8Uq0wEhMgY+u4CNtEb0om3+y3+niDAsoTCOB33UF0sxpzqzdqXLqmvc+vZyAt4O8pPdfkwA=="],

"acorn": ["[email protected]", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg=="],
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
"@radix-ui/react-tooltip": "^1.1.8",
"@t3-oss/env-core": "^0.12.0",
"@tanstack/react-query": "^5.67.1",
"@uidotdev/usehooks": "^2.4.1",
"axios": "^1.8.1",
"better-auth": "^1.2.3",
"bluebun": "^0.0.34",
Expand Down
4 changes: 3 additions & 1 deletion src/app/api/user/files/[page]/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export async function GET(
key: searchParams.get("sortKey") ?? "createdAt",
direction: searchParams.get("sortDirection") ?? "desc",
} as UserFilesSort;
const search = searchParams.get("search") ?? "";

// todo: validate the sort query

Expand All @@ -32,11 +33,12 @@ export async function GET(

const paginatedPage = await pagination.getPage(
Number(page),
async fetchItems => {
async (fetchItems) => {
const files = await getUserFiles(user.id, {
limit: ITEMS_PER_PAGE,
offset: fetchItems.start,
sort: sort,
search: search,
});

return files;
Expand Down
53 changes: 44 additions & 9 deletions src/components/dashboard/user/files/files.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,17 @@ import { FileKeys } from "@/type/file/file-keys";
import { SortDirection } from "@/type/sort-direction";
import { UserFilesSort } from "@/type/user/user-file-sort";
import { useQuery } from "@tanstack/react-query";
import { ArrowDown01, ArrowDown10 } from "lucide-react";
import {
ArrowDown01,
ArrowDown10,
ArrowDownWideNarrow,
ArrowUpNarrowWide,
X,
} from "lucide-react";
import { useEffect, useState } from "react";
import UserFile from "./file";
import { Input } from "@/components/ui/input";
import { useDebounce } from "@uidotdev/usehooks";

const sortNames: {
name: string;
Expand All @@ -46,11 +54,14 @@ const sortNames: {
export default function UserFiles({ user }: { user: UserType }) {
const isMobile = useIsScreenSize(ScreenSize.Small);
const [page, setPage] = useState<number>(1);
const [search, setSearch] = useState<string>("");
const [sort, setSort] = useState<UserFilesSort>({
key: "createdAt",
direction: "desc",
});

const debouncedSearch = useDebounce(search, 500);

useEffect(() => {
setSort({
key: (localStorage.getItem("sortKey") as FileKeys) ?? sort.key,
Expand All @@ -60,21 +71,26 @@ export default function UserFiles({ user }: { user: UserType }) {
});
}, []);

useEffect(() => {
setPage(1);
}, [debouncedSearch]);

const {
data: files,
isLoading,
isRefetching,
refetch,
} = useQuery<Page<FileType>>({
queryKey: ["userFiles", page, sort],
queryKey: ["userFiles", page, sort, debouncedSearch],
queryFn: async () =>
(await request.get<Page<FileType>>(`/api/user/files/${page}`, {
searchParams: {
sortKey: sort.key,
sortDirection: sort.direction,
...(debouncedSearch && { search: debouncedSearch }),
},
}))!,
placeholderData: data => data,
placeholderData: (data) => data,
});

return (
Expand All @@ -88,19 +104,37 @@ export default function UserFiles({ user }: { user: UserType }) {
</div>

{/* File Sorting */}
<div className="flex gap-2 mr-2 select-none">
<div className="flex gap-2 sm:mr-2 select-none">
{/* Search */}
<div className="relative flex-1 w-full">
<Input
placeholder="Query..."
value={search}
onChange={(e) => setSearch(e.target.value)}
className="pr-8"
/>
{search && (
<button
className="absolute right-0 top-0 h-full w-6.5 p-0"
onClick={() => setSearch("")}
>
<X className="size-4" />
</button>
)}
</div>

{/* Sort By */}
<Select
value={sort.key}
onValueChange={value => {
onValueChange={(value) => {
setSort({
...sort,
key: value as FileKeys,
});
localStorage.setItem("sortKey", value);
}}
>
<SelectTrigger className="w-[180px]">
<SelectTrigger className="w-[140px]">
<SelectValue />
</SelectTrigger>
<SelectContent>
Expand All @@ -114,6 +148,7 @@ export default function UserFiles({ user }: { user: UserType }) {
</SelectGroup>
</SelectContent>
</Select>

{/* Sort Direction */}
<Button
variant="outline"
Expand All @@ -128,9 +163,9 @@ export default function UserFiles({ user }: { user: UserType }) {
}}
>
{sort.direction == "asc" ? (
<ArrowDown01 className="size-5" />
<ArrowUpNarrowWide className="size-5" />
) : (
<ArrowDown10 className="size-5" />
<ArrowDownWideNarrow className="size-5" />
)}
</Button>
</div>
Expand Down Expand Up @@ -172,7 +207,7 @@ export default function UserFiles({ user }: { user: UserType }) {
totalItems={files.metadata.totalItems}
itemsPerPage={files.metadata.itemsPerPage}
loadingPage={isLoading || isRefetching ? page : undefined}
onPageChange={newPage => setPage(newPage)}
onPageChange={(newPage) => setPage(newPage)}
/>
</>
)}
Expand Down
19 changes: 17 additions & 2 deletions src/lib/helpers/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import request from "@/lib/request";
import { DiscordEmbed } from "@/type/discord";
import { UserFilesSort } from "@/type/user/user-file-sort";
import { format } from "date-fns";
import { AnyColumn, asc, count, desc, eq } from "drizzle-orm";
import { AnyColumn, and, asc, count, desc, eq, like, or } from "drizzle-orm";
import { headers } from "next/headers";
import { redirect } from "next/navigation";
import { authError } from "../api-commons";
Expand Down Expand Up @@ -84,9 +84,24 @@ export async function getUserFiles(
sort?: UserFilesSort;
limit?: number;
offset?: number;
search?: string;
}
) {
const query = db.select().from(fileTable).where(eq(fileTable.userId, id));
const query = db
.select()
.from(fileTable)
.where(
and(
eq(fileTable.userId, id),
options?.search
? or(
like(fileTable.id, `%${options.search}%`),
like(fileTable.extension, `%${options.search}%`),
like(fileTable.mimeType, `%${options.search}%`)
)
: undefined
)
);

if (options?.limit) {
query.limit(options.limit);
Expand Down

0 comments on commit 0f3f7d7

Please sign in to comment.