+ {/*
{JSON.stringify(searchBoxReturn)}
*/}
+
+
,
+ },
+ {
+ label: 'New Warns Last 7d',
+ value: hasCalloutData ? calloutData.warnsLast7d : false,
+ icon:
,
+ prefix: '+'
+ },
+ {
+ label: 'Total Bans',
+ value: hasCalloutData ? calloutData.totalBans : false,
+ icon:
,
+ },
+ {
+ label: 'New Bans Last 7d',
+ value: hasCalloutData ? calloutData.bansLast7d : false,
+ icon:
,
+ prefix: '+'
+ }
+ ]}
+ />
+
+ {hasCalloutData ? (
+
+ ) : null}
+
+ {searchBoxReturn ? (
+
+ ) : null}
+
);
+}
diff --git a/panel/src/pages/History/HistorySearchBox.tsx b/panel/src/pages/History/HistorySearchBox.tsx
new file mode 100644
index 000000000..fbfbdf010
--- /dev/null
+++ b/panel/src/pages/History/HistorySearchBox.tsx
@@ -0,0 +1,289 @@
+import { throttle } from "throttle-debounce";
+import { useEffect, useMemo, useRef, useState } from 'react';
+import { Button } from '@/components/ui/button';
+import { ChevronsUpDownIcon, XIcon, ChevronDownIcon, ExternalLinkIcon } from 'lucide-react';
+import { Input } from '@/components/ui/input';
+import {
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuItem,
+ DropdownMenuLabel,
+ DropdownMenuRadioGroup,
+ DropdownMenuRadioItem,
+ DropdownMenuSeparator, DropdownMenuTrigger
+} from "@/components/ui/dropdown-menu";
+import InlineCode from '@/components/InlineCode';
+import { useEventListener } from "usehooks-ts";
+import { Link } from "wouter";
+import { useAuth } from "@/hooks/auth"
+import {
+ Select,
+ SelectContent,
+ SelectItem,
+ SelectSeparator,
+ SelectTrigger,
+ SelectValue
+} from "@/components/ui/select"
+import { HistoryTableSearchType } from "@shared/historyApiTypes";
+
+
+/**
+ * Helpers
+ */
+const availableSearchTypes = [
+ {
+ value: 'actionId',
+ label: 'Action ID',
+ placeholder: 'XXXX-XXXX',
+ description: 'Search actions by their ID.'
+ },
+ {
+ value: 'reason',
+ label: 'Reason',
+ placeholder: 'Enter part of the reason to search for',
+ description: 'Search actions by their reason contents.'
+ },
+ {
+ value: 'identifiers',
+ label: 'Player IDs',
+ placeholder: 'License, Discord, Steam, etc.',
+ description: 'Search actions by their player IDs separated by a comma.'
+ },
+] as const;
+
+const SEARCH_ANY_STRING = '!any';
+
+//FIXME: this doesn't require exporting, but HMR doesn't work without it
+// eslint-disable-next-line @typescript-eslint/no-explicit-any, react-refresh/only-export-components
+export const throttleFunc = throttle(1250, (func: any) => {
+ func();
+}, { noLeading: true });
+
+
+
+/**
+ * Component
+ */
+export type HistorySearchBoxReturnStateType = {
+ search: HistoryTableSearchType;
+ filterbyType?: string;
+ filterbyAdmin?: string;
+}
+
+type HistorySearchBoxProps = {
+ doSearch: (search: HistoryTableSearchType, filterbyType: string | undefined, filterbyAdmin: string | undefined) => void;
+ initialState: HistorySearchBoxReturnStateType;
+ adminStats: {
+ name: string;
+ actions: number;
+ }[];
+};
+
+export function HistorySearchBox({ doSearch, initialState, adminStats }: HistorySearchBoxProps) {
+ const { authData } = useAuth();
+ const inputRef = useRef