Skip to content

Commit

Permalink
Added filtering to assets page
Browse files Browse the repository at this point in the history
  • Loading branch information
matvp91 committed Oct 31, 2024
1 parent 11f5023 commit d99ad7f
Show file tree
Hide file tree
Showing 11 changed files with 253 additions and 172 deletions.
68 changes: 52 additions & 16 deletions packages/api/src/repositories/assets.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,60 @@
import { db } from "../db";
import { executeAsTable } from "../utils/query-table";
import type { AssetInsert } from "../db/types";
import type { TableQuery } from "../utils/query-table";

export async function createAsset(fields: AssetInsert) {
return await db.insertInto("assets").values(fields).executeTakeFirstOrThrow();
}

export async function getAssetsTable(query: TableQuery) {
return await executeAsTable(
query,
db
.selectFrom("assets")
.leftJoin("playables", "playables.assetId", "assets.id")
.select(({ fn }) => [
"assets.id",
"assets.groupId",
"assets.createdAt",
fn.count<number>("playables.assetId").as("playablesCount"),
])
.groupBy("assets.id"),
);
export async function getAssetsCount() {
const { count } = await db
.selectFrom("assets")
.select((eb) => eb.fn.count<number>("id").as("count"))
.executeTakeFirstOrThrow();
return count;
}

export async function getAssets(filter: {
page: number;
perPage: number;
orderBy: string;
direction: string;
}) {
const orderBy = mapOrderBy(filter.orderBy);
const direction = mapDirection(filter.direction);

const assets = await db
.selectFrom("assets")
.leftJoin("playables", "playables.assetId", "assets.id")
.select(({ fn }) => [
"assets.id",
"assets.groupId",
"assets.createdAt",
fn.count<number>("playables.assetId").as("playablesCount"),
])
.groupBy("assets.id")
.limit(filter.perPage)
.offset((filter.page - 1) * filter.perPage)
.orderBy(orderBy, direction)
.execute();

return assets.map((asset) => {
return {
...asset,
name: asset.id,
};
});
}

function mapOrderBy(orderBy: string) {
if (orderBy === "name") {
return "id";
}
if (orderBy === "createdAt") {
return "createdAt";
}
return "createdAt";
}

function mapDirection(direction: string) {
return direction === "asc" || direction === "desc" ? direction : "desc";
}
30 changes: 24 additions & 6 deletions packages/api/src/routes/assets.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,43 @@
import { Elysia } from "elysia";
import { Elysia, t } from "elysia";
import { authUser } from "./token";
import { getAssetsTable } from "../repositories/assets";
import { getAssets, getAssetsCount } from "../repositories/assets";
import { getGroups } from "../repositories/groups";
import { AssetSchema } from "../types";
import { tableQuery, getTableObject } from "../utils/query-table";

export const assets = new Elysia()
.use(authUser)
.get(
"/assets",
async ({ query }) => {
return await getAssetsTable(query);
const assets = await getAssets(query);

const count = await getAssetsCount();
const totalPages = Math.ceil(count / query.perPage);

return {
page: query.page,
totalPages,
assets,
};
},
{
detail: {
summary: "Get all assets",
tags: ["Assets"],
},
query: tableQuery,

query: t.Object({
page: t.Number(),
perPage: t.Number(),
orderBy: t.String(),
direction: t.String(),
}),
response: {
200: getTableObject(AssetSchema),
200: t.Object({
page: t.Number(),
totalPages: t.Number(),
assets: t.Array(AssetSchema),
}),
},
},
)
Expand Down
1 change: 1 addition & 0 deletions packages/api/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ export const AssetSchema = t.Object(
{
id: t.String({ format: "uuid" }),
groupId: t.Nullable(t.Number()),
name: t.String(),
createdAt: t.Date(),
playablesCount: t.Number(),
},
Expand Down
55 changes: 0 additions & 55 deletions packages/api/src/utils/query-table.ts

This file was deleted.

23 changes: 17 additions & 6 deletions packages/app/src/components/AssetsTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,25 +15,31 @@ import FileVideo from "lucide-react/icons/file-video";
import GroupIcon from "lucide-react/icons/group";
import { getTimeAgo } from "@/lib/helpers";
import { TableHeadSorter } from "./TableHeadSorter";
import { TableFilterValue } from "@/hooks/useTableFilter";

type AssetsTableProps = {
assets: Asset[];
groups: Group[];
sort: string;
onSort(sort: string): void;
filter: TableFilterValue;
onSort(orderBy: string, direction: string): void;
};

export function AssetsTable({
assets,
groups,
sort,
filter,
onSort,
}: AssetsTableProps) {
return (
<Table>
<TableHeader>
<TableRow>
<TableHeadSorter field="id" sort={sort} onChange={onSort}>
<TableHeadSorter
name="name"
orderBy={filter.orderBy}
direction={filter.direction}
onChange={onSort}
>
Name
</TableHeadSorter>
<TableHead>
Expand All @@ -46,7 +52,12 @@ export function AssetsTable({
<GroupIcon className="w-4 h-4" />
</div>
</TableHead>
<TableHeadSorter field="createdAt" sort={sort} onChange={onSort}>
<TableHeadSorter
name="createdAt"
orderBy={filter.orderBy}
direction={filter.direction}
onChange={onSort}
>
Created
</TableHeadSorter>
<TableHead />
Expand All @@ -57,7 +68,7 @@ export function AssetsTable({
const group = groups.find((group) => group.id === asset.groupId);
return (
<TableRow key={asset.id}>
<TableCell className="w-full">{asset.id}</TableCell>
<TableCell className="w-full">{asset.name}</TableCell>
<TableCell>
<div className="flex justify-center">
{asset.playablesCount > 0 ? (
Expand Down
35 changes: 20 additions & 15 deletions packages/app/src/components/TableHeadSorter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,41 +6,46 @@ import ChevronsUpDown from "lucide-react/icons/chevrons-up-down";
import type { ReactNode } from "react";

type TableHeadSorterProps = {
field: string;
sort: string;
name: string;
children: ReactNode;
onChange(sort: string): void;
orderBy: string;
direction: string;
onChange(orderBy: string, duration: string): void;
};

export function TableHeadSorter({
field,
sort,
name,
children,
orderBy,
direction,
onChange,
}: TableHeadSorterProps) {
const [key, mode] = sort.split(":");

return (
<TableHead>
<Button
variant="ghost"
className="-ml-2 px-2 flex gap-2 text-xs h-6"
onClick={() => {
const sort = `${field}:${mode === "asc" ? "desc" : "asc"}`;
onChange(sort);
onChange(name, direction === "asc" ? "desc" : "asc");
}}
>
{children}
{key === field ? (
mode === "desc" ? (
<ArrowUp className="w-3 h-3" />
) : (
<ArrowDown className="w-3 h-3" />
)
{orderBy === name ? (
getArrow(direction)
) : (
<ChevronsUpDown className="w-3 h-3" />
)}
</Button>
</TableHead>
);
}

function getArrow(direction: string) {
if (direction === "asc") {
return <ArrowUp className="w-3 h-3" />;
}
if (direction === "desc") {
return <ArrowDown className="w-3 h-3" />;
}
return null;
}
68 changes: 36 additions & 32 deletions packages/app/src/components/TablePagination.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,44 +15,48 @@ export function TablePagination({
totalPages,
onSelect,
}: TablePaginationProps) {
const buttons = [
{
disabled: page === 1,
icon: <ChevronsLeft className="w-4 h-4" />,
page: 1,
},
{
disabled: page === 1,
icon: <ChevronLeft className="w-4 h-4" />,
page: page - 1,
},
{
disabled: page === totalPages,
icon: <ChevronRight className="w-4 h-4" />,
page: page + 1,
},
{
disabled: page === totalPages,
icon: <ChevronsRight className="w-4 h-4" />,
page: totalPages,
},
];

return (
<div className="flex items-center">
<div className="mr-6 text-medium text-sm">
Page {page} of {totalPages}
</div>
<div className="flex gap-1">
<Button
className="w-8 h-8 p-0"
variant="outline"
disabled={page === 1}
onClick={() => onSelect(1)}
>
<ChevronsLeft className="w-4 h-4" />
</Button>
<Button
className="w-8 h-8 p-0"
variant="outline"
disabled={page === 1}
onClick={() => onSelect(page - 1)}
>
<ChevronLeft className="w-4 h-4" />
</Button>
<Button
className="w-8 h-8 p-0"
variant="outline"
disabled={page === totalPages}
onClick={() => onSelect(page + 1)}
>
<ChevronRight className="w-4 h-4" />
</Button>
<Button
className="w-8 h-8 p-0"
variant="outline"
disabled={page === totalPages}
onClick={() => onSelect(totalPages)}
>
<ChevronsRight className="w-4 h-4" />
</Button>
{buttons.map((button, index) => {
return (
<Button
key={index}
className="w-8 h-8 p-0"
variant="outline"
disabled={button.disabled}
onClick={() => onSelect(button.page)}
>
{button.icon}
</Button>
);
})}
</div>
</div>
);
Expand Down
Loading

0 comments on commit d99ad7f

Please sign in to comment.