feat: added option to filter no name people
Signed-off-by: Varun Raj <varun@skcript.com>
This commit is contained in:
1 parent
cbfee2ee40
commit
0597f34662
8 files changed
+45
-26
No files matched your search
@@ -15,10 +15,11 @@ import {
|
||||
import { Input } from "../ui/input";
|
||||
import { IPersonListFilters } from "@/handlers/api/people.handler";
|
||||
import AssetFilter from "../shared/common/AssetFilter";
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "../ui/select";
|
||||
|
||||
export function PeopleFilters() {
|
||||
const router = useRouter();
|
||||
const { updateContext, page, maximumAssetCount } = usePeopleFilterContext();
|
||||
const { updateContext, page, maximumAssetCount, type = "all" } = usePeopleFilterContext();
|
||||
|
||||
const handleChange = (data: Partial<IPersonListFilters>) => {
|
||||
updateContext(data);
|
||||
@@ -28,6 +29,7 @@ export function PeopleFilters() {
|
||||
...router.query,
|
||||
...data,
|
||||
page: data.page || undefined,
|
||||
type: data.type || undefined,
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -51,17 +53,17 @@ export function PeopleFilters() {
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<div className="flex items-center gap-1">
|
||||
<Switch
|
||||
id="nameLessOnly"
|
||||
onCheckedChange={(checked) => {
|
||||
handleChange({ nameLessOnly: checked, page: 1 });
|
||||
}}
|
||||
/>
|
||||
<Label className="text-nowrap" htmlFor="nameLessOnly">
|
||||
Show only Nameless
|
||||
</Label>
|
||||
</div>
|
||||
|
||||
<Select value={type} onValueChange={(value) => handleChange({ type: value })}>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="Person Type" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="all">All</SelectItem>
|
||||
<SelectItem value="nameless">Nameless</SelectItem>
|
||||
<SelectItem value="named">Named</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
|
||||
<Button
|
||||
disabled={prevPage < 1}
|
||||
|
||||
@@ -8,12 +8,11 @@ type ISortField = "assetCount" | "updatedAt" | "createdAt";
|
||||
export interface IPersonListFilters {
|
||||
page: number | string;
|
||||
perPage?: number;
|
||||
nameLessOnly?: boolean | string;
|
||||
minimumAssetCount?: number;
|
||||
maximumAssetCount?: number;
|
||||
sort?: ISortField;
|
||||
sortOrder?: "asc" | "desc";
|
||||
|
||||
type?: string;
|
||||
}
|
||||
export const listPeople = (filters: IPersonListFilters): Promise<IPeopleListResponse> => {
|
||||
return API.get(LIST_PEOPLE_PATH, filters).then((response) => {
|
||||
|
||||
@@ -14,6 +14,7 @@ import { Select, SelectItem, SelectContent, SelectTrigger, SelectValue } from '@
|
||||
import { useRouter } from 'next/router'
|
||||
import { ChevronDownIcon, ChevronUpIcon } from '@radix-ui/react-icons'
|
||||
import { SortAsc, SortDesc } from 'lucide-react'
|
||||
import { Input } from '@/components/ui/input'
|
||||
|
||||
const SORT_BY_OPTIONS = [
|
||||
{ value: 'lastPhotoDate', label: 'Last Photo Date' },
|
||||
@@ -28,6 +29,7 @@ const SORT_BY_OPTIONS = [
|
||||
export default function AlbumListPage() {
|
||||
const { exImmichUrl } = useConfig()
|
||||
const router = useRouter()
|
||||
const [search, setSearch] = useState('')
|
||||
const { query, pathname } = router
|
||||
const { sortBy, sortOrder } = query as { sortBy: string, sortOrder: string }
|
||||
const [albums, setAlbums] = useState<IAlbum[]>([])
|
||||
@@ -37,6 +39,7 @@ export default function AlbumListPage() {
|
||||
|
||||
const selectedSortBy = useMemo(() => SORT_BY_OPTIONS.find((option) => option.value === sortBy), [sortBy])
|
||||
|
||||
const searchedAlbums = useMemo(() => albums.filter((album) => album.albumName.toLowerCase().includes(search.toLowerCase())), [albums, search])
|
||||
const fetchAlbums = async () => {
|
||||
setLoading(true)
|
||||
listAlbums({
|
||||
@@ -74,7 +77,7 @@ export default function AlbumListPage() {
|
||||
}
|
||||
return (
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-5 gap-4 p-4">
|
||||
{albums.map((album) => (
|
||||
{searchedAlbums.map((album) => (
|
||||
<AlbumThumbnail album={album} key={album.id} onSelect={(checked) => handleSelect(checked, album.id)} />
|
||||
))}
|
||||
</div>
|
||||
@@ -86,6 +89,7 @@ export default function AlbumListPage() {
|
||||
leftComponent="Manage Albums"
|
||||
rightComponent={
|
||||
<div className="flex items-center gap-2">
|
||||
<Input type="text" placeholder="Search" className="w-48" onChange={(e) => setSearch(e.target.value)} />
|
||||
<Select
|
||||
defaultValue={selectedSortBy?.value}
|
||||
onValueChange={(value) => router.push({
|
||||
|
||||
@@ -28,7 +28,7 @@ export default async function handler(
|
||||
if (!id) {
|
||||
return res.status(400).json({ error: "Album id is required" });
|
||||
}
|
||||
const dbAssets = await db.select({
|
||||
const dbAssets = await db.selectDistinctOn([assets.id], {
|
||||
id: assets.id,
|
||||
deviceId: assets.deviceId,
|
||||
type: assets.type,
|
||||
@@ -53,6 +53,7 @@ export default async function handler(
|
||||
.where(and(
|
||||
eq(albumsAssetsAssets.albumsId, id),
|
||||
eq(assets.isVisible, true),
|
||||
isNotNull(assets.isArchived),
|
||||
faceId ? eq(assetFaces.personId, faceId) : undefined,
|
||||
))
|
||||
.limit(100)
|
||||
|
||||
@@ -21,7 +21,7 @@ export default async function handler(
|
||||
}
|
||||
const { id } = req.query as { id: string };
|
||||
|
||||
const dbAlbums = await db.select({
|
||||
const dbAlbums = await db.selectDistinctOn([assets.id], {
|
||||
id: albums.id,
|
||||
albumName: albums.albumName,
|
||||
createdAt: albums.createdAt,
|
||||
|
||||
@@ -4,7 +4,7 @@ import { db } from "@/config/db";
|
||||
import { getCurrentUser } from "@/handlers/serverUtils/user.utils";
|
||||
import { NextApiResponse } from "next";
|
||||
import { albums } from "@/schema/albums.schema";
|
||||
import { count, desc, eq, min, max, sql, and, sum } from "drizzle-orm";
|
||||
import { count, desc, eq, min, max, sql, and, sum, isNotNull } from "drizzle-orm";
|
||||
import { assets } from "@/schema/assets.schema";
|
||||
import { albumsAssetsAssets } from "@/schema/albumAssetsAssets.schema";
|
||||
import { users } from "@/schema/users.schema";
|
||||
@@ -55,7 +55,7 @@ export default async function handler(
|
||||
createdAt: albums.createdAt,
|
||||
updatedAt: albums.updatedAt,
|
||||
albumThumbnailAssetId: albums.albumThumbnailAssetId,
|
||||
assetCount: count(assets.id),
|
||||
assetCount: count(sql<string>`DISTINCT ${assets.id}`),
|
||||
firstPhotoDate: min(exif.dateTimeOriginal),
|
||||
lastPhotoDate: max(exif.dateTimeOriginal),
|
||||
size: sum(exif.fileSizeInByte),
|
||||
@@ -68,7 +68,11 @@ export default async function handler(
|
||||
})
|
||||
.from(albums)
|
||||
.leftJoin(albumsAssetsAssets, eq(albums.id, albumsAssetsAssets.albumsId))
|
||||
.leftJoin(assets, eq(albumsAssetsAssets.assetsId, assets.id))
|
||||
.leftJoin(assets, and(
|
||||
eq(albumsAssetsAssets.assetsId, assets.id),
|
||||
eq(assets.isVisible, true),
|
||||
isNotNull(assets.isArchived),
|
||||
))
|
||||
.leftJoin(exif, eq(assets.id, exif.assetId))
|
||||
.leftJoin(assetFaces, eq(assets.id, assetFaces.assetId))
|
||||
.leftJoin(person, and(eq(assetFaces.personId, person.id), eq(person.isHidden, false)))
|
||||
|
||||
@@ -25,7 +25,7 @@ type ISortField = "assetCount" | "updatedAt" | "createdAt";
|
||||
interface IQuery {
|
||||
page: number;
|
||||
perPage: number;
|
||||
nameLessOnly: boolean | string;
|
||||
type: string;
|
||||
maximumAssetCount: number;
|
||||
sort: ISortField;
|
||||
sortOrder: "asc" | "desc";
|
||||
@@ -38,10 +38,10 @@ export default async function handler(
|
||||
const {
|
||||
page = 1,
|
||||
perPage = 60,
|
||||
nameLessOnly = false,
|
||||
maximumAssetCount: maxValue = 1000000,
|
||||
sort = "assetCount",
|
||||
sortOrder = "desc",
|
||||
type = "all",
|
||||
} = req.query as any as IQuery;
|
||||
|
||||
const currentUser = await getCurrentUser(req);
|
||||
@@ -53,7 +53,7 @@ export default async function handler(
|
||||
eq(assets.isVisible, true),
|
||||
eq(assets.isArchived, false),
|
||||
eq(assets.ownerId, currentUser.id),
|
||||
stringToBoolean(nameLessOnly) ? eq(person.name, "") : undefined
|
||||
type === "all" ? undefined : (type === "nameless" ? eq(person.name, "") : ne(person.name, ""))
|
||||
);
|
||||
|
||||
let query = db
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
// pages/api/proxy.js
|
||||
|
||||
import { db } from "@/config/db";
|
||||
import { ENV } from "@/config/environment";
|
||||
import { getCurrentUser } from "@/handlers/serverUtils/user.utils";
|
||||
import { NextApiRequest, NextApiResponse } from "next";
|
||||
@@ -19,13 +20,21 @@ export default async function handler(
|
||||
}
|
||||
|
||||
try {
|
||||
const user = await getCurrentUser(req);
|
||||
|
||||
if (!db) {
|
||||
return res.status(500).json({ message: "Database connection failed" });
|
||||
}
|
||||
|
||||
const user = await getCurrentUser(req);
|
||||
|
||||
if (user) return res.status(200).json(user);
|
||||
|
||||
return res.status(403).json({ message: "Forbidden" });
|
||||
|
||||
} catch (error) {
|
||||
return res.status(500).json(error);
|
||||
} catch (error: any) {
|
||||
return res.status(500).json({
|
||||
message: error.message ||"Internal Server Error",
|
||||
error: error,
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user