From 6914645c19e5d56bcb71fa6ce20ee944246f735b Mon Sep 17 00:00:00 2001 From: Cooldude2606 <25043174+Cooldude2606@users.noreply.github.com> Date: Mon, 1 Jun 2026 21:58:27 +0100 Subject: [PATCH] Make Username reusable and fix active icon issue --- .../web_ui/src/components/UsersFilters.tsx | 117 +++++++++++------- packages/web_ui/src/components/UsersTable.tsx | 15 +-- 2 files changed, 76 insertions(+), 56 deletions(-) diff --git a/packages/web_ui/src/components/UsersFilters.tsx b/packages/web_ui/src/components/UsersFilters.tsx index 34295d18..264fa60d 100644 --- a/packages/web_ui/src/components/UsersFilters.tsx +++ b/packages/web_ui/src/components/UsersFilters.tsx @@ -1,5 +1,5 @@ import React, { useRef } from "react"; -import { InputRef, Input, Space, Segmented, Button, Typography } from "antd"; +import { InputRef, Input, Space, Segmented, Button, Typography, Tag } from "antd"; import type { FilterDropdownProps } from "antd/es/table/interface"; import { CheckOutlined, CloseOutlined, MinusOutlined } from "@ant-design/icons"; import { UserDetails } from "@clusterio/lib"; @@ -59,7 +59,28 @@ export function onFilterUser( && matchesTriState(record.isBanned, filter.banned); } -export function useUserFilter() { +export function Username({ + user, + withStatus = false, +} : { + user: UserDetails, + withStatus: boolean, +}) { + return ( + + {user.name} + {withStatus && ( + + {user.isAdmin && Admin} + {user.isWhitelisted && Whitelisted} + {user.isBanned && Banned} + + )} + + ); +} + +export function useUserFilter(withStatus?: boolean) { const searchInput = useRef(null); function filterDropdown({ @@ -77,9 +98,13 @@ export function useUserFilter() { ? decodeFilter(selectedKeys[0]) : {}; - function update(next: Partial, search?: boolean) { - const updated = { ...filter, ...next }; - setSelectedKeys([encodeFilter(updated)]); + function update(next: Partial) { + const encoded = encodeFilter({ ...filter, ...next }); + if (encoded === "{}") { + clearFilters?.({ closeDropdown: false }); + } else { + setSelectedKeys([encoded]); + } confirm({ closeDropdown: false }); } @@ -91,55 +116,57 @@ export function useUserFilter() { placeholder="Search username" value={filter.search} allowClear - onChange={(e) => update({ search: e.target.value })} onSearch={() => confirm({ closeDropdown: true })} + onChange={(e) => update({ search: e.target.value !== "" ? e.target.value : undefined })} onClear={() => { clearFilters?.({ closeDropdown: false }); confirm({ closeDropdown: true }); }} /> - - update({ admin: segmentedToTriState(value) })} - options={[ - { icon: , value: "no", className: "seg-no" }, - { icon: , value: "any" }, - { icon: , value: "yes", className: "seg-yes" }, - ]} - /> - Admin - + {withStatus &&<> + + update({ admin: segmentedToTriState(value) })} + options={[ + { icon: , value: "no", className: "seg-no" }, + { icon: , value: "any" }, + { icon: , value: "yes", className: "seg-yes" }, + ]} + /> + Admin + - - update({ whitelisted: segmentedToTriState(value) })} - options={[ - { icon: , value: "no", className: "seg-no" }, - { icon: , value: "any" }, - { icon: , value: "yes", className: "seg-yes" }, - ]} - /> - Whitelist - + + update({ whitelisted: segmentedToTriState(value) })} + options={[ + { icon: , value: "no", className: "seg-no" }, + { icon: , value: "any" }, + { icon: , value: "yes", className: "seg-yes" }, + ]} + /> + Whitelist + - - update({ banned: segmentedToTriState(value) })} - options={[ - { icon: , value: "no", className: "seg-no" }, - { icon: , value: "any" }, - { icon: , value: "yes", className: "seg-yes" }, - ]} - /> - Banned - + + update({ banned: segmentedToTriState(value) })} + options={[ + { icon: , value: "no", className: "seg-no" }, + { icon: , value: "any" }, + { icon: , value: "yes", className: "seg-yes" }, + ]} + /> + Banned + + } ); diff --git a/packages/web_ui/src/components/UsersTable.tsx b/packages/web_ui/src/components/UsersTable.tsx index 15b31f97..a553bc6f 100644 --- a/packages/web_ui/src/components/UsersTable.tsx +++ b/packages/web_ui/src/components/UsersTable.tsx @@ -1,5 +1,5 @@ import React from "react"; -import { Table, Tag, Space } from "antd"; +import { Table, Tag } from "antd"; import { SearchOutlined } from "@ant-design/icons"; import { useNavigate } from "react-router-dom"; @@ -15,7 +15,7 @@ import { useUsers, } from "../model/user"; -import { onFilterUser, useUserFilter } from "./UsersFilters"; +import { onFilterUser, Username, useUserFilter } from "./UsersFilters"; import Link from "./Link"; export interface UsersTableProps { @@ -39,7 +39,7 @@ export default function UsersTable({ instanceId, onlyOnline = false, pagination, const [roles] = useRoles(); const navigate = useNavigate(); - const {filterDropdown, filterDropdownProps} = useUserFilter(); + const {filterDropdown, filterDropdownProps} = useUserFilter(true); const [users] = useUsers(); const data = [...users.values()]; @@ -60,14 +60,7 @@ export default function UsersTable({ instanceId, onlyOnline = false, pagination, title: "Name", key: "name", render: (_: any, user: lib.UserDetails) => ( - - {user.name} - - {user.isAdmin && Admin} - {user.isWhitelisted && Whitelisted} - {user.isBanned && Banned} - - + ), defaultSortOrder: "ascend", sorter: (a: lib.UserDetails, b: lib.UserDetails) => strcmp(a.name, b.name),