diff --git a/panel/src/components/dropDownSelect.tsx b/panel/src/components/dropDownSelect.tsx new file mode 100644 index 000000000..d9938b747 --- /dev/null +++ b/panel/src/components/dropDownSelect.tsx @@ -0,0 +1,116 @@ +import * as React from "react" +import * as SelectPrimitive from "@radix-ui/react-select" +import { ChevronDown, ChevronUp } from "lucide-react" + +import { cn } from "@/lib/utils" + +const DropDownSelect = SelectPrimitive.Root + +const DropDownSelectValue = SelectPrimitive.Value + +const DropDownSelectTrigger = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ children }, ref) => ( + + {children} + +)) +DropDownSelectTrigger.displayName = SelectPrimitive.Trigger.displayName + +const DropDownSelectScrollUpButton = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + + + +)) +DropDownSelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName + +const DropDownSelectScrollDownButton = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + + + +)) +DropDownSelectScrollDownButton.displayName = SelectPrimitive.ScrollDownButton.displayName + +const DropDownSelectContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, position = "popper", ...props }, ref) => ( + + + + + {children} + + + + +)) +DropDownSelectContent.displayName = SelectPrimitive.Content.displayName + + +const DropDownSelectItem = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + + {children} + +)) +DropDownSelectItem.displayName = SelectPrimitive.Item.displayName + +export { + DropDownSelect, + DropDownSelectValue, + DropDownSelectTrigger, + DropDownSelectItem, + DropDownSelectContent, + DropDownSelectScrollUpButton, + DropDownSelectScrollDownButton, +} diff --git a/panel/src/layout/PlayerModal/PlayerBanTab.tsx b/panel/src/layout/PlayerModal/PlayerBanTab.tsx index 4521c76d3..dbf5f066c 100644 --- a/panel/src/layout/PlayerModal/PlayerBanTab.tsx +++ b/panel/src/layout/PlayerModal/PlayerBanTab.tsx @@ -4,16 +4,31 @@ import { Label } from "@/components/ui/label"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { useAdminPerms } from "@/hooks/auth"; import { PlayerModalRefType, useClosePlayerModal } from "@/hooks/playerModal"; -import { Loader2Icon } from "lucide-react"; -import { useRef, useState } from "react"; +import { ClipboardPasteIcon, ExternalLinkIcon, Loader2Icon } from "lucide-react"; +import { useMemo, useRef, useState } from "react"; import { useBackendApi } from "@/hooks/fetch"; import { GenericApiOkResp } from "@shared/genericApiTypes"; import ModalCentralMessage from "@/components/ModalCentralMessage"; +import { DropDownSelect, DropDownSelectContent, DropDownSelectItem, DropDownSelectTrigger } from "@/components/dropDownSelect"; +import { banDurationToShortString, banDurationToString, cn } from "@/lib/utils"; +import { Link, useLocation } from "wouter"; +import { DynamicNewItem } from "@/components/DynamicNewBadge"; +import type { BanTemplatesDataType } from "@shared/otherTypes"; -export default function PlayerBanTab({ playerRef }: { playerRef: PlayerModalRefType }) { +const ADD_NEW_SELECT_OPTION = '!add-new'; +const maxReasonSize = 150; +const defaultDurations = ['permanent', '2 hours', '8 hours', '1 day', '2 days', '1 week', '2 weeks']; + +type PlayerBanTabProps = { + banTemplates: BanTemplatesDataType[]; + playerRef: PlayerModalRefType; +}; + +export default function PlayerBanTab({ playerRef, banTemplates }: PlayerBanTabProps) { const reasonRef = useRef(null); const customMultiplierRef = useRef(null); + const setLocation = useLocation()[1]; const [currentDuration, setCurrentDuration] = useState('2 days'); const [customUnits, setCustomUnits] = useState('days'); const [isSaving, setIsSaving] = useState(false); @@ -54,20 +69,106 @@ export default function PlayerBanTab({ playerRef }: { playerRef: PlayerModalRefT }); }; + const handleTemplateSelectChange = (value: string) => { + if (value === ADD_NEW_SELECT_OPTION) { + setLocation('/settings/ban-templates'); + closeModal(); + } else { + const template = banTemplates.find(template => template.id === value); + if (!template) return; + + const processedDuration = banDurationToString(template.duration); + if (defaultDurations.includes(processedDuration)) { + setCurrentDuration(processedDuration); + } else if (typeof template.duration === 'object') { + setCurrentDuration('custom'); + customMultiplierRef.current!.value = template.duration.value.toString(); + setCustomUnits(template.duration.unit); + } + + reasonRef.current!.value = template.reason; + setTimeout(() => { + reasonRef.current!.focus(); + }, 50); + } + } + + //Optimization + const processedTemplates = useMemo(() => { + return banTemplates.map((template, index) => { + const duration = banDurationToShortString(template.duration); + const reason = template.reason.length > maxReasonSize + ? template.reason.slice(0, maxReasonSize - 3) + '...' + : template.reason; + return ( + + {duration} {reason} + + ); + }); + }, [banTemplates]); + return (
- +
+ + + + + + + {!banTemplates.length ? ( +
+ You do not have any template configured.
+ { closeModal(); }} + > + Add Ban Template + + +
+ ) : null} + {processedTemplates} + {banTemplates.length ? ( + + Add Ban Template + + + ) : null} +
+
+