feat: added date and asset count sorting for missing location
Signed-off-by: Varun Raj <varun@skcript.com>
This commit is contained in:
@@ -1,7 +1,9 @@
|
||||
import { usePotentialAlbumContext } from "@/contexts/PotentialAlbumContext";
|
||||
import { IPotentialAlbumsDatesResponse } from "@/handlers/api/album.handler";
|
||||
import { formatDate } from "@/helpers/date.helper";
|
||||
import { parseDate } from "@/helpers/date.helper";
|
||||
import { cn } from "@/lib/utils";
|
||||
import React from "react";
|
||||
import React, { useMemo } from "react";
|
||||
|
||||
interface IProps {
|
||||
record: IPotentialAlbumsDatesResponse;
|
||||
@@ -9,6 +11,9 @@ interface IProps {
|
||||
}
|
||||
export default function PotentialDateItem({ record, onSelect }: IProps) {
|
||||
const { startDate } = usePotentialAlbumContext()
|
||||
const dateLabel = useMemo(() => {
|
||||
return formatDate(parseDate(record.date, "yyyy-MM-dd").toISOString(), "do MMM yyyy")
|
||||
}, [record.date])
|
||||
return (
|
||||
<div
|
||||
role="button"
|
||||
@@ -19,8 +24,8 @@ export default function PotentialDateItem({ record, onSelect }: IProps) {
|
||||
startDate === record.date ? "bg-zinc-100 dark:bg-zinc-800 border-gray-300 dark:border-zinc-700" : "")
|
||||
}
|
||||
>
|
||||
<p className="font-mono text-sm">{record.date}</p>
|
||||
<p className="text-xs">{record.asset_count} Orphan Assets</p>
|
||||
<p className="font-mono text-sm">{dateLabel}</p>
|
||||
<p className="text-xs text-foreground/50">{record.asset_count} Orphan Assets</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { useMissingLocationContext } from "@/contexts/MissingLocationContext";
|
||||
|
||||
import { IMissingLocationDatesResponse } from "@/handlers/api/asset.handler";
|
||||
import { parseDate, formatDate } from "@/helpers/date.helper";
|
||||
import { cn } from "@/lib/utils";
|
||||
import React from "react";
|
||||
import React, { useMemo } from "react";
|
||||
|
||||
interface IProps {
|
||||
record: IMissingLocationDatesResponse;
|
||||
@@ -10,6 +11,10 @@ interface IProps {
|
||||
}
|
||||
export default function MissingLocationDateItem({ record, onSelect }: IProps) {
|
||||
const { startDate } = useMissingLocationContext()
|
||||
const dateLabel = useMemo(() => {
|
||||
if (!record.date) return "Unknown"
|
||||
return formatDate(parseDate(record.date, "yyyy-MM-dd").toISOString(), "do MMM yyyy")
|
||||
}, [record.date])
|
||||
return (
|
||||
<div
|
||||
role="button"
|
||||
@@ -20,8 +25,8 @@ export default function MissingLocationDateItem({ record, onSelect }: IProps) {
|
||||
startDate === record.date ? "bg-zinc-100 dark:bg-zinc-800 border-gray-300 dark:border-zinc-700" : "")
|
||||
}
|
||||
>
|
||||
<p className="font-mono text-sm">{record.date}</p>
|
||||
<p className="text-xs">{record.asset_count} W/O Location</p>
|
||||
<p className="font-mono text-sm">{dateLabel}</p>
|
||||
<p className="text-xs text-foreground/50">{record.asset_count} Orphan Assets</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -4,6 +4,9 @@ import { IMissingLocationDatesResponse, listMissingLocationDates } from "@/handl
|
||||
import MissingLocationDateItem from "./MissingLocationDateItem";
|
||||
import { useMissingLocationContext } from "@/contexts/MissingLocationContext";
|
||||
import { useRouter } from "next/router";
|
||||
import { Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectTrigger, SelectValue } from "@/components/ui/select";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { SortAsc, SortDesc } from "lucide-react";
|
||||
|
||||
export default function MissingLocationDates() {
|
||||
const { updateContext } = useMissingLocationContext();
|
||||
@@ -11,12 +14,16 @@ export default function MissingLocationDates() {
|
||||
const [dateRecords, setDateRecords] = React.useState<
|
||||
IMissingLocationDatesResponse[]
|
||||
>([]);
|
||||
const [filters, setFilters] = useState<{ sortBy: string, sortOrder: string }>({ sortBy: "date", sortOrder: "desc" });
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
const [errorMessage, setErrorMessage] = useState<string | null>(null);
|
||||
|
||||
const fetchData = async () => {
|
||||
return listMissingLocationDates({})
|
||||
return listMissingLocationDates({
|
||||
sortBy: filters.sortBy,
|
||||
sortOrder: filters.sortOrder,
|
||||
})
|
||||
.then(setDateRecords)
|
||||
.catch(setErrorMessage)
|
||||
.finally(() => setLoading(false));
|
||||
@@ -30,12 +37,37 @@ export default function MissingLocationDates() {
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
fetchData();
|
||||
}, []);
|
||||
}, [filters]);
|
||||
|
||||
return (
|
||||
<div className="overflow-y-auto min-w-[170px] py-4 max-h-[calc(100vh-60px)] min-h-[calc(100vh-60px)] dark:bg-zinc-900 bg-gray-200 flex flex-col gap-2 px-2">
|
||||
<div className="overflow-y-auto min-w-[200px] py-4 max-h-[calc(100vh-60px)] min-h-[calc(100vh-60px)] dark:bg-zinc-900 bg-gray-200 flex flex-col gap-2 px-2">
|
||||
<div className="flex justify-between items-center gap-2">
|
||||
<Select
|
||||
defaultValue={filters.sortBy}
|
||||
onValueChange={(value) => setFilters({ ...filters, sortBy: value })}
|
||||
>
|
||||
<SelectTrigger className="!p-2">
|
||||
<SelectValue placeholder="Sort by" />
|
||||
</SelectTrigger>
|
||||
<SelectContent defaultValue={filters.sortBy}>
|
||||
<SelectGroup title="Sort by">
|
||||
<SelectLabel>Sort by</SelectLabel>
|
||||
<SelectItem value="date">Date</SelectItem>
|
||||
<SelectItem value="asset_count">Asset Count</SelectItem>
|
||||
</SelectGroup>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<div>
|
||||
<Button variant="default" size="sm" onClick={() => setFilters({ ...filters, sortOrder: filters.sortOrder === "asc" ? "desc" : "asc" })}>
|
||||
{filters.sortOrder === "asc" ? <SortAsc size={16} /> : <SortDesc size={16} />}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{dateRecords.map((record) => (
|
||||
<MissingLocationDateItem
|
||||
key={record.date}
|
||||
|
||||
@@ -12,6 +12,8 @@ import { IAsset } from "@/types/asset";
|
||||
interface IMissingAssetAlbumsFilters {
|
||||
startDate?: string;
|
||||
endDate?: string;
|
||||
sortBy?: string;
|
||||
sortOrder?: string;
|
||||
}
|
||||
export interface IMissingLocationDatesResponse {
|
||||
date: string;
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
|
||||
import { CHART_COLORS } from "@/config/constants/chart.constant";
|
||||
import { db } from "@/config/db";
|
||||
import { IMissingLocationDatesResponse } from "@/handlers/api/asset.handler";
|
||||
import { getCurrentUser } from "@/handlers/serverUtils/user.utils";
|
||||
import { parseDate } from "@/helpers/date.helper";
|
||||
import { assets, exif } from "@/schema";
|
||||
import { and, count, desc, eq, isNotNull, isNull, ne, sql } from "drizzle-orm";
|
||||
import type { NextApiRequest, NextApiResponse } from "next";
|
||||
@@ -11,7 +13,13 @@ export default async function handler(
|
||||
res: NextApiResponse
|
||||
) {
|
||||
try {
|
||||
const { sortBy = "date", sortOrder = "desc" } = req.query;
|
||||
const currentUser = await getCurrentUser(req);
|
||||
if (!currentUser) {
|
||||
return res.status(401).json({
|
||||
error: "Unauthorized",
|
||||
});
|
||||
}
|
||||
const rows = await db
|
||||
.select({
|
||||
asset_count: desc(count(assets.id)),
|
||||
@@ -22,12 +30,25 @@ export default async function handler(
|
||||
.where(and(
|
||||
isNull(exif.latitude),
|
||||
isNotNull(assets.createdAt),
|
||||
isNotNull(exif.dateTimeOriginal),
|
||||
eq(assets.type, "VIDEO"),
|
||||
eq(assets.ownerId, currentUser.id),
|
||||
eq(assets.ownerId, currentUser.id),
|
||||
eq(assets.isVisible, true),
|
||||
))
|
||||
.groupBy(sql`DATE(${exif.dateTimeOriginal})`)
|
||||
.orderBy(desc(count(assets.id)));
|
||||
.orderBy(desc(count(assets.id))) as IMissingLocationDatesResponse[];
|
||||
|
||||
if (sortBy === "date") {
|
||||
rows.sort((a, b) => {
|
||||
const aDate = parseDate(a.date, "yyyy-MM-dd");
|
||||
const bDate = parseDate(b.date, "yyyy-MM-dd");
|
||||
return sortOrder === "asc" ? aDate.getTime() - bDate.getTime() : bDate.getTime() - aDate.getTime();
|
||||
});
|
||||
} else if (sortBy === "asset_count") {
|
||||
rows.sort((a, b) => {
|
||||
return sortOrder === "asc" ? a.asset_count - b.asset_count : b.asset_count - a.asset_count;
|
||||
});
|
||||
}
|
||||
return res.status(200).json(rows);
|
||||
} catch (error: any) {
|
||||
res.status(500).json({
|
||||
|
||||
Reference in New Issue
Block a user