feat: add time range filter for Mii discovery

This commit is contained in:
AlexHelo 2026-04-16 17:58:45 -06:00
parent c72dab1962
commit 8df69bcd79
3 changed files with 84 additions and 3 deletions

View file

@ -1,12 +1,11 @@
import { Prisma } from "@prisma/client";
import crypto from "crypto";
import { searchSchema } from "@/lib/schemas";
import { auth } from "@/lib/auth";
import { prisma } from "@/lib/prisma";
import SortSelect from "./sort-select";
import TimeRangeSelect from "./time-range-select";
import Pagination from "./pagination";
import FilterMenu from "./filter-menu";
import MiiGrid from "./mii-grid";
@ -22,7 +21,7 @@ export default async function MiiList({ searchParams, userId, parentPage }: Prop
const parsed = searchSchema.safeParse(searchParams);
if (!parsed.success) return <h1>{parsed.error.issues[0].message}</h1>;
const { q: query, sort, tags, exclude, platform, gender, makeup, allowCopying, quarantined, page = 1, limit = 24 } = parsed.data;
const { q: query, sort, tags, exclude, platform, gender, makeup, allowCopying, quarantined, page = 1, limit = 24, timeRange } = parsed.data;
// My Likes page
let miiIdsLiked: number[] | undefined = undefined;
@ -68,6 +67,14 @@ export default async function MiiList({ searchParams, userId, parentPage }: Prop
...(makeup && { makeup: { equals: makeup } }),
// Quarantined
...(!quarantined && !userId && { quarantined: false }),
// Time range
...(timeRange && {
createdAt: {
gte: new Date(
Date.now() - { day: 86400000, week: 604800000, month: 2592000000, year: 31536000000 }[timeRange],
),
},
}),
};
const select: Prisma.MiiSelect = {
@ -145,6 +152,7 @@ export default async function MiiList({ searchParams, userId, parentPage }: Prop
<div className="relative flex items-center justify-end gap-2 w-full md:max-w-2/3 max-md:justify-center">
<FilterMenu />
<SortSelect />
<TimeRangeSelect />
</div>
</div>

View file

@ -0,0 +1,71 @@
"use client";
import { useRouter, useSearchParams } from "next/navigation";
import { useTransition } from "react";
import { useSelect } from "downshift";
import { Icon } from "@iconify/react";
type TimeRange = "day" | "week" | "month" | "year";
const items: { value: TimeRange | "all"; label: string }[] = [
{ value: "all", label: "all time" },
{ value: "day", label: "today" },
{ value: "week", label: "this week" },
{ value: "month", label: "this month" },
{ value: "year", label: "this year" },
];
export default function TimeRangeSelect() {
const router = useRouter();
const searchParams = useSearchParams();
const [, startTransition] = useTransition();
const currentRange = (searchParams.get("timeRange") as TimeRange) || "all";
const currentItem = items.find((i) => i.value === currentRange) || items[0];
const { isOpen, getToggleButtonProps, getMenuProps, getItemProps, highlightedIndex, selectedItem } = useSelect({
items,
selectedItem: currentItem,
itemToString: (item) => item?.label || "",
onSelectedItemChange: ({ selectedItem }) => {
if (!selectedItem) return;
const params = new URLSearchParams(searchParams);
params.set("page", "1");
if (selectedItem.value === "all") {
params.delete("timeRange");
} else {
params.set("timeRange", selectedItem.value);
}
startTransition(() => {
router.push(`?${params.toString()}`, { scroll: false });
});
},
});
return (
<div className="relative w-fit">
<button type="button" {...getToggleButtonProps()} aria-label="Time range dropdown" className="pill input w-full gap-1 justify-between! text-nowrap">
<Icon icon="mdi:clock-outline" className="size-5" />
{selectedItem?.label || "all time"}
<Icon icon="tabler:chevron-down" className="ml-1 size-5" />
</button>
<ul
{...getMenuProps()}
className={`absolute z-50 w-full bg-orange-200 border-2 border-orange-400 rounded-lg mt-1 shadow-lg max-h-60 overflow-y-auto ${
isOpen ? "block" : "hidden"
}`}
>
{isOpen &&
items.map((item, index) => (
<li key={item.value} {...getItemProps({ item, index })} className={`px-4 py-1 cursor-pointer text-sm ${highlightedIndex === index ? "bg-black/15" : ""}`}>
{item.label}
</li>
))}
</ul>
</div>
);
}

View file

@ -72,6 +72,8 @@ export const searchSchema = z.object({
.max(100, { error: "Limit cannot be more than 100" })
.optional(),
page: z.coerce.number({ error: "Page must be a number" }).int({ error: "Page must be an integer" }).min(1, { error: "Page must be at least 1" }).optional(),
// Time range filter
timeRange: z.enum(["day", "week", "month", "year"], { error: "Time range must be either 'day', 'week', 'month', or 'year'" }).optional(),
});
export const userNameSchema = z