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:
@@ -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>
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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)}
|
||||
|
||||
@@ -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) => {
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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 />
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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) => {
|
||||
|
||||
@@ -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) => {
|
||||
|
||||
@@ -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) => {
|
||||
|
||||
@@ -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) => {
|
||||
|
||||
@@ -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))}
|
||||
|
||||
Reference in New Issue
Block a user