[frontend]: Usability tips

This commit is contained in:
2025-04-06 04:16:17 +01:00
parent 56a4bfe27e
commit fc3371dd09
2 changed files with 86 additions and 20 deletions

View File

@ -41,6 +41,7 @@ const defaultFavourites = {
function App() { function App() {
const [favourites, setFavourites] = useState(defaultFavourites); const [favourites, setFavourites] = useState(defaultFavourites);
const [showFaovouritesOnly, setShowFavouritesOnly] = useState(false); const [showFaovouritesOnly, setShowFavouritesOnly] = useState(false);
const searchInputRef = useRef(null);
useEffect(() => { useEffect(() => {
try { try {
@ -108,6 +109,22 @@ function App() {
} }
}, []); }, []);
useEffect(() => {
const handleKeyDown = (e) => {
if ((e.ctrlKey || e.metaKey) && e.key === "k") {
e.preventDefault(); // prevent browser search
if (searchInputRef.current) {
searchInputRef.current.focus();
}
}
};
window.addEventListener("keydown", handleKeyDown);
return () => {
window.removeEventListener("keydown", handleKeyDown);
};
}, []);
const handleSearchChange = (e) => { const handleSearchChange = (e) => {
const value = e.target.value; const value = e.target.value;
let timeout = 300; let timeout = 300;
@ -451,31 +468,59 @@ function App() {
<Route <Route
path="/" path="/"
element={ element={
<div style={{ height: "100vh", width: "100vw", display: "flex", position: "relative", paddingTop: "5vh" }}> <div style={{
{loading && <LoadingOverlay message={"Loading data..."} />} height: "100vh",
width: "100vw",
display: "flex",
position: "relative",
paddingTop: "5vh"
}}>
{loading && <LoadingOverlay message={"Loading data..."}/>}
<div <div
style={{ style={{
position: "absolute", position: "absolute",
top: "1vh", top: "6vh",
height: "5vh", height: "5vh",
width: "250px", minWidth: "50px", width: "250px", minWidth: "50px",
left: "50%", left: "50%",
transform: "translateX(-50%)", transform: "translateX(-50%)",
zIndex: 1000, zIndex: 1000,
...(window.innerWidth < 800 ? { top: "auto", bottom: "10vh" } : {}) ...(window.innerWidth < 800 ? {top: "auto", bottom: "4vh"} : {})
}} }}
> >
<div style={{position: "relative"}}>
<input <input
ref={searchInputRef}
type="text" type="text"
onChange={(e) => handleSearchChange(e)} onChange={(e) => handleSearchChange(e)}
placeholder="Search..." placeholder="Search..."
style={{ style={{
width: "250px", fontSize: "16px", width: "100%",
top: "6vh", marginTop: "5vh", fontSize: "16px",
padding: "10px", background: "rgba(255, 255, 255, 0.9)", color: "black", padding: "10px 40px 10px 10px", // space for badge
borderRadius: "10px", overflow: "hidden" background: "rgba(255, 255, 255, 0.9)",
color: "black",
borderRadius: "10px",
border: "1px solid #ccc",
overflow: "hidden",
}} }}
/> />
<span style={{
position: "absolute",
right: "10px",
top: "50%",
transform: "translateY(-50%)",
background: "#f0f0f0",
border: "1px solid #ccc",
borderRadius: "6px",
fontSize: "12px",
padding: "2px 6px",
color: "#333",
fontFamily: "monospace"
}}>
Ctrl+K
</span>
</div>
</div> </div>
<Sidebar <Sidebar
selectedSources={selectedSources} selectedSources={selectedSources}
@ -486,8 +531,9 @@ function App() {
userLocationAvailable={userLocationAvailable} userLocationAvailable={userLocationAvailable}
showFavouritesOnly={showFaovouritesOnly} showFavouritesOnly={showFaovouritesOnly}
setShowFavouritesOnly={setShowFavouritesOnly} setShowFavouritesOnly={setShowFavouritesOnly}
favourites={favourites}
/> />
<div style={{ flex: 1 }}> <div style={{flex: 1}}>
<MapComponent <MapComponent
markers={filteredMarkers} markers={filteredMarkers}
clusteringEnabled={clusteringEnabled} clusteringEnabled={clusteringEnabled}
@ -497,12 +543,13 @@ function App() {
</div> </div>
} }
/> />
<Route path="/statistics" element={<Statistics />} /> <Route path="/statistics" element={<Statistics/>}/>
<Route path="/help" element={<Help />} /> <Route path="/help" element={<Help/>}/>
</Routes> </Routes>
</Router> </Router>
<ToastContainer position="bottom-right"/> <ToastContainer position="bottom-right"/>
</> </>
); );
} }
export default App; export default App;

View File

@ -138,6 +138,19 @@ const CheckboxItem = ({ item, selectedSources, setSelectedSources, enabledSource
/> />
<label htmlFor={item.id} style={{ cursor: isDisabled ? "not-allowed" : "pointer" }}> <label htmlFor={item.id} style={{ cursor: isDisabled ? "not-allowed" : "pointer" }}>
{item.name} {item.name}
{item.id === "luas-stops" && (
<span
title="To view live Luas data, click on a stop and click 'Load Inbound/Outbound Trams'"
style={{
display: "inline-block",
color: "#666",
fontSize: "14px",
cursor: "help",
marginLeft: "4px"
}}
> 🛈
</span>
)}
</label> </label>
</div> </div>
{hasChildren && ( {hasChildren && (
@ -164,7 +177,7 @@ const CheckboxItem = ({ item, selectedSources, setSelectedSources, enabledSource
); );
}; };
const Sidebar = ({ selectedSources, setSelectedSources, clusteringEnabled, setClusteringEnabled, fetchData, userLocationAvailable, showFavouritesOnly, setShowFavouritesOnly }) => { const Sidebar = ({ selectedSources, setSelectedSources, clusteringEnabled, setClusteringEnabled, fetchData, userLocationAvailable, showFavouritesOnly, setShowFavouritesOnly, favourites }) => {
const [isOpen, setIsOpen] = useState(true); const [isOpen, setIsOpen] = useState(true);
const [enabledSources, setEnabledSources] = useState([]); // New state to track enabled sources const [enabledSources, setEnabledSources] = useState([]); // New state to track enabled sources
const [numberInputValue, setNumberInputValue] = useState(""); // State to manage number input value const [numberInputValue, setNumberInputValue] = useState(""); // State to manage number input value
@ -196,6 +209,12 @@ const Sidebar = ({ selectedSources, setSelectedSources, clusteringEnabled, setCl
const handleSubmit = () => { const handleSubmit = () => {
Cookies.set("selectedSources", JSON.stringify(selectedSources), { expires: 365 }); Cookies.set("selectedSources", JSON.stringify(selectedSources), { expires: 365 });
Cookies.set("numberInputValue", numberInputValue, { expires: 365 }); // Save numberInputValue to cookie Cookies.set("numberInputValue", numberInputValue, { expires: 365 }); // Save numberInputValue to cookie
if (showFavouritesOnly && (!favourites.length || favourites.length < 1)) {
toast.warn("You haven't added any favourites yet!");
return;
}
fetchData(enabledSources, numberInputValue); // Use enabledSources for data fetching fetchData(enabledSources, numberInputValue); // Use enabledSources for data fetching
}; };