This commit is contained in:
@@ -153,38 +153,43 @@ type PropsType<T extends FieldValues> = {
|
||||
) &
|
||||
Partial<UseFormRegisterReturn>;
|
||||
|
||||
export function Select<T extends FieldValues>({
|
||||
items,
|
||||
label,
|
||||
defaultValue,
|
||||
placeholder,
|
||||
prefixIcon,
|
||||
className,
|
||||
name,
|
||||
errors,
|
||||
required,
|
||||
onChange,
|
||||
onBlur,
|
||||
ref,
|
||||
clearable = false,
|
||||
onClear,
|
||||
value: controlledValue,
|
||||
dataCy,
|
||||
disabled = false,
|
||||
searchable = false,
|
||||
searchPlaceholder = "Search by label or value…",
|
||||
searchMode = "client",
|
||||
searchDebounceMs = 300,
|
||||
searchQueryKey,
|
||||
searchFn,
|
||||
onSearch,
|
||||
onLoadMore,
|
||||
hasMore = false,
|
||||
isLoading = false,
|
||||
isLoadingMore = false,
|
||||
register: _register,
|
||||
...props
|
||||
}: PropsType<T>) {
|
||||
export function Select<T extends FieldValues>(selectProps: PropsType<T>) {
|
||||
const hasControlledValue = Object.prototype.hasOwnProperty.call(
|
||||
selectProps,
|
||||
"value",
|
||||
);
|
||||
const {
|
||||
items,
|
||||
label,
|
||||
defaultValue,
|
||||
placeholder,
|
||||
prefixIcon,
|
||||
className,
|
||||
name,
|
||||
errors,
|
||||
required,
|
||||
onChange,
|
||||
onBlur,
|
||||
ref,
|
||||
clearable = false,
|
||||
onClear,
|
||||
value: controlledValue,
|
||||
dataCy,
|
||||
disabled = false,
|
||||
searchable = false,
|
||||
searchPlaceholder = "Search by label or value…",
|
||||
searchMode = "client",
|
||||
searchDebounceMs = 300,
|
||||
searchQueryKey,
|
||||
searchFn,
|
||||
onSearch,
|
||||
onLoadMore,
|
||||
hasMore = false,
|
||||
isLoading = false,
|
||||
isLoadingMore = false,
|
||||
register: _register,
|
||||
...props
|
||||
} = selectProps;
|
||||
void _register;
|
||||
|
||||
/** Parent-controlled list: search + pagination handled outside, `items` consumed as-is. */
|
||||
@@ -223,11 +228,12 @@ export function Select<T extends FieldValues>({
|
||||
const error = errors && errors[name];
|
||||
|
||||
useEffect(() => {
|
||||
if (controlledValue !== undefined) {
|
||||
setValue(controlledValue);
|
||||
setIsOptionSelected(!!controlledValue);
|
||||
if (hasControlledValue) {
|
||||
const nextValue = controlledValue ?? "";
|
||||
setValue(nextValue);
|
||||
setIsOptionSelected(!!nextValue);
|
||||
}
|
||||
}, [controlledValue]);
|
||||
}, [controlledValue, hasControlledValue]);
|
||||
|
||||
const {
|
||||
isServerMode,
|
||||
@@ -333,7 +339,7 @@ export function Select<T extends FieldValues>({
|
||||
}, [open, showSearch]);
|
||||
|
||||
const emitChange = (next: string) => {
|
||||
if (controlledValue === undefined) {
|
||||
if (!hasControlledValue) {
|
||||
setValue(next);
|
||||
}
|
||||
setIsOptionSelected(!!next);
|
||||
@@ -345,7 +351,7 @@ export function Select<T extends FieldValues>({
|
||||
};
|
||||
|
||||
const handleClear = () => {
|
||||
if (controlledValue === undefined) {
|
||||
if (!hasControlledValue) {
|
||||
setValue("");
|
||||
}
|
||||
setIsOptionSelected(false);
|
||||
@@ -498,7 +504,7 @@ export function Select<T extends FieldValues>({
|
||||
data-cy={dataCy}
|
||||
className="sr-only"
|
||||
onChange={(e) => {
|
||||
if (controlledValue === undefined) {
|
||||
if (!hasControlledValue) {
|
||||
setValue(e.target.value);
|
||||
}
|
||||
setIsOptionSelected(!!e.target.value);
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
DropdownContent,
|
||||
DropdownTrigger,
|
||||
} from "@/components/ui/dropdown";
|
||||
import { useUser } from "@/contexts";
|
||||
import Modal from "@/components/ui/modal";
|
||||
import Status from "@/components/ui/Status";
|
||||
import { useMarkNotificationAsSeen } from "@/hooks/queries/useNotificationQueries";
|
||||
@@ -13,7 +14,7 @@ import { TNotificationDetailsResponse } from "@/lib/api/types/NotificationHistor
|
||||
import { cn, sanitizeHtmlWithStyles } from "@/lib/utils";
|
||||
import { capitalize, unixToDate } from "@/utils/shared";
|
||||
import Link from "next/link";
|
||||
import { useState } from "react";
|
||||
import { useMemo, useState } from "react";
|
||||
import { BellIcon } from "./icons";
|
||||
import NotificationList from "./notification-list";
|
||||
|
||||
@@ -28,6 +29,12 @@ export function Notification() {
|
||||
|
||||
const { mutate } = useMarkNotificationAsSeen();
|
||||
const isMobile = useIsMobile();
|
||||
const { user } = useUser();
|
||||
const notificationHistoryHref = useMemo(() => {
|
||||
if (!user?.id) return "/notification-history";
|
||||
|
||||
return `/notification-history?user_id=${encodeURIComponent(user.id)}`;
|
||||
}, [user?.id]);
|
||||
|
||||
const handleNotificationClick = (
|
||||
notification: TNotificationDetailsResponse,
|
||||
@@ -86,7 +93,7 @@ export function Notification() {
|
||||
/>
|
||||
|
||||
<Link
|
||||
href="/notification-history"
|
||||
href={notificationHistoryHref}
|
||||
onClick={() => setIsOpen(false)}
|
||||
className="block rounded-xl border border-primary/40 bg-white/70 p-2 text-center text-sm font-semibold tracking-wide text-primary outline-none transition-colors hover:bg-primary/10 focus:bg-primary/10 focus:text-primary focus-visible:border-primary dark:border-dark-3 dark:bg-dark-2/70 dark:text-dark-6 dark:hover:border-dark-5 dark:hover:bg-dark-3 dark:hover:text-dark-7 dark:focus-visible:border-dark-5 dark:focus-visible:bg-dark-3 dark:focus-visible:text-dark-7"
|
||||
>
|
||||
|
||||
@@ -4,6 +4,10 @@ import InputGroup from "@/components/FormElements/InputGroup";
|
||||
import { Select } from "@/components/FormElements/select";
|
||||
import { Button } from "@/components/ui-elements/button";
|
||||
import InlineListFiltersPanel from "@/components/ui/InlineListFiltersPanel";
|
||||
import { useUser } from "@/contexts";
|
||||
import { apiDefaultParams } from "@/constants/Api_Params";
|
||||
import { userService } from "@/lib/api/services/user-services";
|
||||
import { ILabelValue } from "@/types/shared";
|
||||
import {
|
||||
FieldValues,
|
||||
UseFormHandleSubmit,
|
||||
@@ -34,11 +38,38 @@ function countActive(values: Record<string, unknown>): number {
|
||||
) {
|
||||
n++;
|
||||
}
|
||||
const userId = values.user_id;
|
||||
if (
|
||||
userId !== undefined &&
|
||||
userId !== null &&
|
||||
userId !== "" &&
|
||||
String(userId).trim()
|
||||
) {
|
||||
n++;
|
||||
}
|
||||
const searchVal = values.search;
|
||||
if (typeof searchVal === "string" && searchVal.trim()) n++;
|
||||
return n;
|
||||
}
|
||||
|
||||
const searchUsers = async (query: string): Promise<ILabelValue[]> => {
|
||||
const response = await userService.adminsList({
|
||||
...apiDefaultParams,
|
||||
search: query,
|
||||
offset: 0,
|
||||
limit: 20,
|
||||
});
|
||||
|
||||
return (response.data?.users ?? []).map((user) => ({
|
||||
value: user.id,
|
||||
label:
|
||||
user.full_name ||
|
||||
user.username ||
|
||||
user.email ||
|
||||
user.id,
|
||||
}));
|
||||
};
|
||||
|
||||
const NotificationHistoryListFilters = ({
|
||||
filterRegister,
|
||||
isMutating,
|
||||
@@ -48,7 +79,22 @@ const NotificationHistoryListFilters = ({
|
||||
setValue,
|
||||
onClearAll,
|
||||
}: NotificationHistoryListFiltersProps) => {
|
||||
const { user } = useUser();
|
||||
const watched = watch();
|
||||
const currentUserOption = useMemo<ILabelValue[]>(() => {
|
||||
if (!user?.id) return [];
|
||||
|
||||
return [
|
||||
{
|
||||
value: user.id,
|
||||
label:
|
||||
user.full_name ||
|
||||
user.username ||
|
||||
user.email ||
|
||||
user.id,
|
||||
},
|
||||
];
|
||||
}, [user?.email, user?.full_name, user?.id, user?.username]);
|
||||
const activeCount = useMemo(
|
||||
() => countActive(watched as Record<string, unknown>),
|
||||
[watched],
|
||||
@@ -68,7 +114,7 @@ const NotificationHistoryListFilters = ({
|
||||
onSubmit={handleFilterSubmit(search)}
|
||||
data-cy="notification-history-filter-form"
|
||||
>
|
||||
<div className="grid gap-4 sm:grid-cols-2">
|
||||
<div className="grid gap-4 lg:grid-cols-3">
|
||||
<Select
|
||||
{...filterRegister("seen")}
|
||||
items={[
|
||||
@@ -82,6 +128,22 @@ const NotificationHistoryListFilters = ({
|
||||
value={watch("seen")}
|
||||
onClear={() => setValue("seen", undefined)}
|
||||
/>
|
||||
<Select
|
||||
{...filterRegister("user_id")}
|
||||
items={currentUserOption}
|
||||
label="User"
|
||||
name="user_id"
|
||||
placeholder="Current user"
|
||||
clearable
|
||||
searchable
|
||||
searchMode="server"
|
||||
searchPlaceholder="Search users"
|
||||
searchQueryKey={["notification-history-user-filter"]}
|
||||
searchFn={searchUsers}
|
||||
value={watch("user_id")}
|
||||
onClear={() => setValue("user_id", undefined)}
|
||||
dataCy="notification-history-filter-user-select"
|
||||
/>
|
||||
<InputGroup
|
||||
{...filterRegister("search")}
|
||||
name="search"
|
||||
|
||||
@@ -83,7 +83,7 @@ const useNotificationHistoryTablePresenter = () => {
|
||||
};
|
||||
|
||||
const clearAllFilters = useCallback(() => {
|
||||
filterFormReset({ seen: undefined, search: "" });
|
||||
filterFormReset({ seen: undefined, search: "", user_id: undefined });
|
||||
router.push(pathName);
|
||||
}, [pathName, router, filterFormReset]);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user