feat: polish release v2 experience (#6800)

This commit is contained in:
Thomas authored and GitHub committed 2026-09-07 19:17:37 +02:00
1 parent 925b0fb801
commit a4a903d7c3
295 files changed
+10627 -7115

No files matched your search

+19 -1
View File
@@ -3,10 +3,12 @@
import type { ChangeEvent } from "react";
import { useCallback, useEffect, useState } from "react";
import { usePathname, useRouter, useSearchParams } from "next/navigation";
import { Loader, TextInput } from "@mantine/core";
import { Input, Loader, TextInput } from "@mantine/core";
import { useDebouncedCallback } from "@mantine/hooks";
import { IconSearch } from "@tabler/icons-react";
import { useI18n } from "@homarr/translation/client";
interface SearchInputProps {
defaultValue?: string;
placeholder: string;
@@ -18,6 +20,7 @@ export const SearchInput = ({ placeholder, ariaLabel, defaultValue, flexExpand =
const router = useRouter();
const pathName = usePathname();
const searchParams = useSearchParams();
const t = useI18n("common.action");
const [loading, setLoading] = useState(false);
const [value, setValue] = useState(defaultValue ?? "");
useEffect(() => {
@@ -47,11 +50,26 @@ export const SearchInput = ({ placeholder, ariaLabel, defaultValue, flexExpand =
[setLoading, handleSearchDebounced],
);
const clearSearch = useCallback(() => {
setValue("");
setLoading(true);
handleSearchDebounced("");
handleSearchDebounced.flush();
}, [handleSearchDebounced]);
return (
<TextInput
leftSection={<LeftSection loading={loading} />}
rightSection={value ? <Input.ClearButton aria-label={t("clearSearch")} onClick={clearSearch} /> : undefined}
rightSectionPointerEvents="all"
value={value}
onChange={handleSearch}
onKeyDown={(event) => {
if (event.key !== "Escape" || !value) return;
event.preventDefault();
clearSearch();
}}
placeholder={placeholder}
aria-label={ariaLabel}
style={{ flex: flexExpand ? "1" : undefined }}
@@ -18,7 +18,7 @@ export const TablePagination = ({ total }: TablePaginationProps) => {
const pathName = usePathname();
const searchParams = useSearchParams();
const t = useI18n("common.pagination");
const current = Number(searchParams.get("page")) || 1;
const current = parsePage(searchParams.getAll("page"));
const getItemProps = useCallback(
(page: number) => {
@@ -62,6 +62,18 @@ export const TablePagination = ({ total }: TablePaginationProps) => {
);
};
const parsePage = (pageValues: string[]) => {
if (pageValues.length !== 1) return 1;
const [pageValue] = pageValues;
if (!pageValue || !/^[1-9]\d*$/u.test(pageValue)) return 1;
const page = Number(pageValue);
if (!Number.isSafeInteger(page)) return 1;
return page;
};
type ControlType = Parameters<Exclude<PaginationProps["getControlProps"], undefined>>[0];
const calculatePageFor = (type: ControlType, current: number, total: number) => {
switch (type) {