feat: hide search input when list is empty and add animated city selector highlight

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-19 14:14:04 +03:00
parent fbf6b0dc9d
commit 7e539f550b
12 changed files with 111 additions and 50 deletions

View File

@@ -108,7 +108,9 @@ export const ArticleListPage = observer(() => {
</div>
)}
{rows.length > 0 && (
<SearchInput value={searchQuery} onChange={setSearchQuery} />
)}
<div className="w-full">
<DataGrid

View File

@@ -162,7 +162,9 @@ export const CarrierListPage = observer(() => {
</div>
)}
{rows.length > 0 && (
<SearchInput value={searchQuery} onChange={setSearchQuery} />
)}
<DataGrid
rows={rows}

View File

@@ -165,7 +165,9 @@ export const CityListPage = observer(() => {
</div>
)}
{rows.length > 0 && (
<SearchInput value={searchQuery} onChange={setSearchQuery} />
)}
<DataGrid
rows={filteredRows}

View File

@@ -117,7 +117,9 @@ export const CountryListPage = observer(() => {
</div>
)}
{rows.length > 0 && (
<SearchInput value={searchQuery} onChange={setSearchQuery} />
)}
<DataGrid
rows={rows}

View File

@@ -113,7 +113,9 @@ export const MediaListPage = observer(() => {
return (
<>
<div className="w-full">
{rows.length > 0 && (
<SearchInput value={searchQuery} onChange={setSearchQuery} />
)}
{canWriteMedia && ids.length > 0 && (
<div className="flex justify-end mb-5 duration-300">

View File

@@ -271,7 +271,9 @@ export const RouteListPage = observer(() => {
</div>
)}
{rows.length > 0 && (
<SearchInput value={searchQuery} onChange={setSearchQuery} />
)}
<DataGrid
rows={rows}

View File

@@ -182,7 +182,9 @@ export const SightListPage = observer(() => {
</div>
)}
{rows.length > 0 && (
<SearchInput value={searchQuery} onChange={setSearchQuery} />
)}
<DataGrid
rows={rows}

View File

@@ -299,7 +299,9 @@ export const SnapshotListPage = observer(() => {
</Alert>
)}
{rows.length > 0 && (
<SearchInput value={searchQuery} onChange={setSearchQuery} />
)}
<DataGrid
rows={rows}

View File

@@ -226,7 +226,9 @@ export const StationListPage = observer(() => {
</div>
)}
{rows.length > 0 && (
<SearchInput value={searchQuery} onChange={setSearchQuery} />
)}
<DataGrid
rows={rows}

View File

@@ -147,7 +147,9 @@ export const UserListPage = observer(() => {
</div>
)}
{rows.length > 0 && (
<SearchInput value={searchQuery} onChange={setSearchQuery} />
)}
<DataGrid
rows={rows}

View File

@@ -174,7 +174,9 @@ export const VehicleListPage = observer(() => {
/>
</div>
{rows.length > 0 && (
<SearchInput value={searchQuery} onChange={setSearchQuery} />
)}
{canWriteVehicles && ids.length > 0 && (
<div className="flex justify-end mb-5 duration-300">

View File

@@ -6,11 +6,21 @@ import {
SelectChangeEvent,
Typography,
Box,
keyframes,
} from "@mui/material";
import { observer } from "mobx-react-lite";
import { authStore, cityStore, selectedCityStore, snapshotStore, type City } from "@shared";
import { MapPin } from "lucide-react";
const borderSpin = keyframes`
0% {
background-position: 0% 50%;
}
100% {
background-position: 200% 50%;
}
`;
export const CitySelector: React.FC = observer(() => {
const { selectedCity, setSelectedCity, isLocked } = selectedCityStore;
const canLoadAllCities = authStore.isAdmin && authStore.canRead("cities");
@@ -43,6 +53,8 @@ export const CitySelector: React.FC = observer(() => {
})()
: baseCities;
const noCitySelected = !selectedCity?.id;
const handleCityChange = (event: SelectChangeEvent<string>) => {
const cityId = event.target.value;
if (cityId === "") {
@@ -58,10 +70,7 @@ export const CitySelector: React.FC = observer(() => {
}
};
return (
<Box className="flex items-center gap-2">
<MapPin size={16} className={isLocked ? "text-gray-400" : "text-white"} />
<FormControl size="medium" sx={{ minWidth: 120 }}>
const selectElement = (
<Select
value={selectedCity?.id?.toString() || ""}
onChange={handleCityChange}
@@ -70,15 +79,23 @@ export const CitySelector: React.FC = observer(() => {
sx={{
height: "40px",
color: "white",
"&.Mui-disabled": {
color: "rgba(255, 255, 255, 0.5)",
WebkitTextFillColor: "rgba(255, 255, 255, 0.5)",
},
borderRadius: "4px",
...(noCitySelected && !isLocked
? {
backgroundColor: "#48989f",
"& .MuiOutlinedInput-notchedOutline": { border: "none" },
}
: {
"& .MuiOutlinedInput-notchedOutline": {
borderColor: isLocked
? "rgba(255, 255, 255, 0.1)"
: "rgba(255, 255, 255, 0.3)",
},
}),
"&.Mui-disabled": {
color: "rgba(255, 255, 255, 0.5)",
WebkitTextFillColor: "rgba(255, 255, 255, 0.5)",
},
"&:hover .MuiOutlinedInput-notchedOutline": {
borderColor: isLocked
? "rgba(255, 255, 255, 0.1)"
@@ -101,6 +118,28 @@ export const CitySelector: React.FC = observer(() => {
</MenuItem>
))}
</Select>
);
return (
<Box className="flex items-center gap-2">
<MapPin size={16} className={isLocked ? "text-gray-400" : "text-white"} />
<FormControl size="medium" sx={{ minWidth: 120 }}>
{noCitySelected && !isLocked ? (
<Box
sx={{
position: "relative",
borderRadius: "4px",
padding: "2px",
background: "linear-gradient(90deg, rgba(255,255,255,0.1), rgba(255,255,255,0.7), rgba(255,255,255,0.1), rgba(255,255,255,0.7), rgba(255,255,255,0.1))",
backgroundSize: "200% 100%",
animation: `${borderSpin} 2.5s linear infinite`,
}}
>
{selectElement}
</Box>
) : (
selectElement
)}
</FormControl>
</Box>
);