fix(ui): unify list filters across all pages (bare controls, standard sizes)

Every list-page FilterBar now follows one spec: bare controls (no Field label rows — which broke flex-end alignment), standardized widths (search 1 1 320px grows; small selects 0 0 160px; entity selects 0 0 220px; MonthField 0 0 180px; dates 0 0 150px), consistent 'Všechny/Všichni/Všechna <entity>' defaults, and a placeholder on every search. TripsAdmin's separate month-Select + year-Select collapse into one MonthField (yyyy-MM, split at the query boundary) — matching Attendance/TripsHistory. The 4 labeled attendance/trips pages move off the Field wrapper; Projects & ReceivedInvoices move their inline search into a FilterBar. Chrome-verified all ~18 filter pages in light + the existing theme.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
BOHA
2026-06-07 11:49:28 +02:00
parent f1c533488a
commit f017867d5f
13 changed files with 104 additions and 139 deletions

View File

@@ -13,7 +13,6 @@ import {
Button,
Card,
ConfirmDialog,
Field,
FilterBar,
LoadingState,
MonthField,
@@ -200,25 +199,21 @@ export default function AttendanceAdmin() {
{/* Filters */}
<FilterBar>
<Box sx={{ flex: "0 0 200px" }}>
<Field label="Měsíc">
<MonthField value={month} onChange={(val) => setMonth(val)} />
</Field>
<Box sx={{ flex: "0 0 180px" }}>
<MonthField value={month} onChange={(val) => setMonth(val)} />
</Box>
<Box sx={{ flex: "1 1 240px" }}>
<Field label="Zaměstnanec">
<Select
value={filterUserId}
onChange={setFilterUserId}
options={[
{ value: "", label: "Všichni" },
...data.users.map((user) => ({
value: String(user.id),
label: user.name,
})),
]}
/>
</Field>
<Box sx={{ flex: "0 0 220px" }}>
<Select
value={filterUserId}
onChange={setFilterUserId}
options={[
{ value: "", label: "Všichni zaměstnanci" },
...data.users.map((user) => ({
value: String(user.id),
label: user.name,
})),
]}
/>
</Box>
</FilterBar>

View File

@@ -29,7 +29,6 @@ import {
Button,
Card,
DataTable,
Field,
FilterBar,
MonthField,
ProgressBar,
@@ -515,10 +514,8 @@ export default function AttendanceHistory() {
{/* Filters */}
<FilterBar>
<Box sx={{ flex: "1 1 200px" }}>
<Field label="Měsíc">
<MonthField value={month} onChange={(val) => setMonth(val)} />
</Field>
<Box sx={{ flex: "0 0 180px" }}>
<MonthField value={month} onChange={(val) => setMonth(val)} />
</Box>
</FilterBar>

View File

@@ -311,7 +311,7 @@ export default function AuditLog() {
</Modal>
<FilterBar>
<Box sx={{ flex: "1 1 220px" }}>
<Box sx={{ flex: "1 1 320px" }}>
<TextField
value={filters.search}
onChange={(e) => handleFilterChange("search", e.target.value)}
@@ -319,14 +319,14 @@ export default function AuditLog() {
fullWidth
/>
</Box>
<Box sx={{ flex: "0 0 170px" }}>
<Box sx={{ flex: "0 0 160px" }}>
<Select
value={filters.action}
onChange={(val) => handleFilterChange("action", val)}
options={[{ value: "", label: "Všechny akce" }, ...ACTION_OPTIONS]}
/>
</Box>
<Box sx={{ flex: "0 0 200px" }}>
<Box sx={{ flex: "0 0 220px" }}>
<Select
value={filters.entity_type}
onChange={(val) => handleFilterChange("entity_type", val)}

View File

@@ -739,7 +739,7 @@ export default function Offers() {
fullWidth
/>
</Box>
<Box sx={{ flex: "0 1 240px" }}>
<Box sx={{ flex: "0 0 220px" }}>
<Select
value={customerFilter === "" ? "" : String(customerFilter)}
onChange={(value) => {

View File

@@ -25,6 +25,7 @@ import {
Modal,
ConfirmDialog,
Field,
FilterBar,
TextField,
Select,
DateField,
@@ -365,17 +366,19 @@ export default function Projects() {
)}
</Box>
<Box sx={{ mb: 3 }}>
<TextField
value={search}
onChange={(e) => {
setSearch(e.target.value);
setPage(1);
}}
placeholder="Hledat podle čísla, názvu nebo zákazníka..."
fullWidth
/>
</Box>
<FilterBar>
<Box sx={{ flex: "1 1 320px" }}>
<TextField
value={search}
onChange={(e) => {
setSearch(e.target.value);
setPage(1);
}}
placeholder="Hledat podle čísla, názvu nebo zákazníka..."
fullWidth
/>
</Box>
</FilterBar>
<Card sx={{ opacity: isFetching ? 0.6 : 1, transition: "opacity .2s" }}>
<DataTable<Project>

View File

@@ -30,6 +30,7 @@ import {
Modal,
ConfirmDialog,
Field,
FilterBar,
TextField,
Select,
DateField,
@@ -762,14 +763,16 @@ export default function ReceivedInvoices({
transition={{ duration: 0.25, delay: 0.08 }}
>
<Card>
<Box sx={{ mb: 2 }}>
<TextField
value={search}
onChange={(e) => setSearch(e.target.value)}
placeholder="Hledat podle dodavatele nebo čísla faktury..."
fullWidth
/>
</Box>
<FilterBar>
<Box sx={{ flex: "1 1 320px" }}>
<TextField
value={search}
onChange={(e) => setSearch(e.target.value)}
placeholder="Hledat podle dodavatele nebo čísla faktury..."
fullWidth
/>
</Box>
</FilterBar>
{showListSkeleton ? (
<LoadingState />

View File

@@ -27,6 +27,7 @@ import {
TextField,
Select,
DateField,
MonthField,
StatusChip,
FilterBar,
LoadingState,
@@ -181,12 +182,10 @@ const BusinessIcon = (
export default function TripsAdmin() {
const alert = useAlert();
const { hasPermission } = useAuth();
const [filterMonth, setFilterMonth] = useState(() =>
String(new Date().getMonth() + 1),
);
const [filterYear, setFilterYear] = useState(() =>
String(new Date().getFullYear()),
);
const [filterPeriod, setFilterPeriod] = useState(() => {
const now = new Date();
return `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, "0")}`;
});
const [filterVehicleId, setFilterVehicleId] = useState("");
const [filterUserId, setFilterUserId] = useState("");
const printRef = useRef<HTMLDivElement>(null);
@@ -220,8 +219,8 @@ export default function TripsAdmin() {
const { data: tripsData, isPending } = useQuery(
tripListOptions({
month: Number(filterMonth) || undefined,
year: Number(filterYear) || undefined,
month: Number(filterPeriod.slice(5, 7)) || undefined,
year: Number(filterPeriod.slice(0, 4)) || undefined,
vehicleId: filterVehicleId ? Number(filterVehicleId) : undefined,
userId: filterUserId ? Number(filterUserId) : undefined,
perPage: 100,
@@ -319,10 +318,10 @@ export default function TripsAdmin() {
};
const getPeriodName = () =>
new Date(Number(filterYear), Number(filterMonth) - 1).toLocaleString(
"cs-CZ",
{ month: "long", year: "numeric" },
);
new Date(
Number(filterPeriod.slice(0, 4)),
Number(filterPeriod.slice(5, 7)) - 1,
).toLocaleString("cs-CZ", { month: "long", year: "numeric" });
const getSelectedVehicleName = () => {
if (!filterVehicleId) return null;
const v = vehicles.find((v) => String(v.id) === filterVehicleId);
@@ -566,61 +565,34 @@ export default function TripsAdmin() {
{/* Filters */}
<FilterBar>
<Box sx={{ flex: "0 0 160px" }}>
<Field label="Měsíc">
<Select
value={filterMonth}
onChange={setFilterMonth}
options={Array.from({ length: 12 }, (_, i) => ({
value: String(i + 1),
label: new Date(2000, i).toLocaleString("cs-CZ", {
month: "long",
}),
}))}
/>
</Field>
<Box sx={{ flex: "0 0 180px" }}>
<MonthField value={filterPeriod} onChange={setFilterPeriod} />
</Box>
<Box sx={{ flex: "0 0 120px" }}>
<Field label="Rok">
<Select
value={filterYear}
onChange={setFilterYear}
options={Array.from({ length: 5 }, (_, i) => {
const y = new Date().getFullYear() - 2 + i;
return { value: String(y), label: String(y) };
})}
/>
</Field>
<Box sx={{ flex: "0 0 220px" }}>
<Select
value={filterVehicleId}
onChange={setFilterVehicleId}
options={[
{ value: "", label: "Všechna vozidla" },
...vehicles.map((v) => ({
value: String(v.id),
label: `${v.spz} - ${v.name}`,
})),
]}
/>
</Box>
<Box sx={{ flex: "1 1 200px" }}>
<Field label="Vozidlo">
<Select
value={filterVehicleId}
onChange={setFilterVehicleId}
options={[
{ value: "", label: "Všechna vozidla" },
...vehicles.map((v) => ({
value: String(v.id),
label: `${v.spz} - ${v.name}`,
})),
]}
/>
</Field>
</Box>
<Box sx={{ flex: "1 1 200px" }}>
<Field label="Řidič">
<Select
value={filterUserId}
onChange={setFilterUserId}
options={[
{ value: "", label: "Všichni řidiči" },
...tripUsers.map((u) => ({
value: String(u.id),
label: u.name,
})),
]}
/>
</Field>
<Box sx={{ flex: "0 0 220px" }}>
<Select
value={filterUserId}
onChange={setFilterUserId}
options={[
{ value: "", label: "Všichni řidiči" },
...tripUsers.map((u) => ({
value: String(u.id),
label: u.name,
})),
]}
/>
</Box>
</FilterBar>

View File

@@ -9,7 +9,6 @@ import { tripHistoryOptions, tripVehiclesOptions } from "../lib/queries/trips";
import {
Card,
DataTable,
Field,
Select,
MonthField,
StatusChip,
@@ -221,25 +220,21 @@ export default function TripsHistory() {
{/* Filters */}
<FilterBar>
<Box sx={{ flex: "1 1 200px" }}>
<Field label="Měsíc">
<MonthField value={month} onChange={(val) => setMonth(val)} />
</Field>
<Box sx={{ flex: "0 0 180px" }}>
<MonthField value={month} onChange={(val) => setMonth(val)} />
</Box>
<Box sx={{ flex: "1 1 200px" }}>
<Field label="Vozidlo">
<Select
value={vehicleId}
onChange={(value) => setVehicleId(value)}
options={[
{ value: "", label: "Všechna vozidla" },
...vehicles.map((v) => ({
value: String(v.id),
label: `${v.spz} - ${v.name}`,
})),
]}
/>
</Field>
<Box sx={{ flex: "0 0 220px" }}>
<Select
value={vehicleId}
onChange={(value) => setVehicleId(value)}
options={[
{ value: "", label: "Všechna vozidla" },
...vehicles.map((v) => ({
value: String(v.id),
label: `${v.spz} - ${v.name}`,
})),
]}
/>
</Box>
</FilterBar>

View File

@@ -142,7 +142,7 @@ export default function WarehouseInventory() {
/>
<FilterBar>
<Box sx={{ flex: "0 0 200px" }}>
<Box sx={{ flex: "0 0 160px" }}>
<Select
value={statusFilter}
onChange={(val) => {

View File

@@ -183,7 +183,7 @@ export default function WarehouseIssues() {
/>
<FilterBar>
<Box sx={{ flex: "1 1 220px" }}>
<Box sx={{ flex: "1 1 320px" }}>
<TextField
value={search}
onChange={(e) => {
@@ -210,7 +210,7 @@ export default function WarehouseIssues() {
/>
</Box>
{projects.length > 0 && (
<Box sx={{ flex: "0 0 200px" }}>
<Box sx={{ flex: "0 0 220px" }}>
<Select
value={projectId === "" ? "" : String(projectId)}
onChange={(val) => {

View File

@@ -170,7 +170,7 @@ export default function WarehouseItems() {
/>
<FilterBar>
<Box sx={{ flex: "1 1 280px" }}>
<Box sx={{ flex: "1 1 320px" }}>
<TextField
value={search}
onChange={(e) => {
@@ -182,7 +182,7 @@ export default function WarehouseItems() {
/>
</Box>
{categories.length > 0 && (
<Box sx={{ flex: "0 0 200px" }}>
<Box sx={{ flex: "0 0 220px" }}>
<Select
value={categoryId === "" ? "" : String(categoryId)}
onChange={(val) => {

View File

@@ -182,7 +182,7 @@ export default function WarehouseReceipts() {
/>
<FilterBar>
<Box sx={{ flex: "1 1 220px" }}>
<Box sx={{ flex: "1 1 320px" }}>
<TextField
value={search}
onChange={(e) => {
@@ -209,7 +209,7 @@ export default function WarehouseReceipts() {
/>
</Box>
{suppliers.length > 0 && (
<Box sx={{ flex: "0 0 200px" }}>
<Box sx={{ flex: "0 0 220px" }}>
<Select
value={supplierId === "" ? "" : String(supplierId)}
onChange={(val) => {

View File

@@ -323,7 +323,7 @@ function ProjectConsumptionTab({
<Box>
<FilterBar>
{projects.length > 0 && (
<Box sx={{ flex: "0 0 280px" }}>
<Box sx={{ flex: "0 0 220px" }}>
<Select
value={projectId === "" ? "" : String(projectId)}
onChange={(val) => setProjectId(val === "" ? "" : Number(val))}