some custom stuff
This commit is contained in:
34
frontend/package-lock.json
generated
34
frontend/package-lock.json
generated
@@ -9,7 +9,8 @@
|
||||
"version": "0.0.0",
|
||||
"dependencies": {
|
||||
"react": "^19.2.0",
|
||||
"react-dom": "^19.2.0"
|
||||
"react-dom": "^19.2.0",
|
||||
"swr": "^2.3.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.39.1",
|
||||
@@ -1675,6 +1676,15 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/dequal": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz",
|
||||
"integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/electron-to-chromium": {
|
||||
"version": "1.5.252",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.252.tgz",
|
||||
@@ -2653,6 +2663,19 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/swr": {
|
||||
"version": "2.3.6",
|
||||
"resolved": "https://registry.npmjs.org/swr/-/swr-2.3.6.tgz",
|
||||
"integrity": "sha512-wfHRmHWk/isGNMwlLGlZX5Gzz/uTgo0o2IRuTMcf4CPuPFJZlq0rDaKUx+ozB5nBOReNV1kiOyzMfj+MBMikLw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"dequal": "^2.0.3",
|
||||
"use-sync-external-store": "^1.4.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.11.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/tinyglobby": {
|
||||
"version": "0.2.15",
|
||||
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz",
|
||||
@@ -2724,6 +2747,15 @@
|
||||
"punycode": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/use-sync-external-store": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz",
|
||||
"integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/vite": {
|
||||
"version": "7.2.2",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-7.2.2.tgz",
|
||||
|
||||
@@ -11,7 +11,8 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"react": "^19.2.0",
|
||||
"react-dom": "^19.2.0"
|
||||
"react-dom": "^19.2.0",
|
||||
"swr": "^2.3.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.39.1",
|
||||
|
||||
@@ -1,30 +1,27 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import { useState, useMemo } from "react";
|
||||
import useSWR from "swr";
|
||||
import "./App.css";
|
||||
|
||||
function StopSelector({ stops, onChange, placeholder, inputId }) {
|
||||
const [inputValue, setInputValue] = useState("");
|
||||
const [filteredStops, setFilteredStops] = useState([]);
|
||||
const [showList, setShowList] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
const filteredStops = useMemo(() => {
|
||||
if (inputValue.trim()) {
|
||||
const filtered = stops
|
||||
return stops
|
||||
.filter((stop) =>
|
||||
stop.stop_name.toLowerCase().includes(inputValue.toLowerCase()),
|
||||
)
|
||||
.slice(0, 10);
|
||||
setFilteredStops(filtered);
|
||||
setShowList(true);
|
||||
} else {
|
||||
setFilteredStops([]);
|
||||
setShowList(false);
|
||||
return [];
|
||||
}
|
||||
}, [inputValue, stops]);
|
||||
|
||||
const showList = useMemo(() => filteredStops.length > 0, [filteredStops]);
|
||||
|
||||
const handleSelect = (stop) => {
|
||||
setInputValue(stop.stop_name);
|
||||
onChange({ id: stop.stop_id, name: stop.stop_name });
|
||||
setShowList(false);
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -50,8 +47,10 @@ function StopSelector({ stops, onChange, placeholder, inputId }) {
|
||||
);
|
||||
}
|
||||
|
||||
const fetcher = (url) => fetch(url).then(res => res.ok ? res.json() : Promise.reject(res.statusText));
|
||||
|
||||
function App() {
|
||||
const [stops, setStops] = useState([]);
|
||||
const { data: stops = [], error: stopsError } = useSWR('/api/stops', fetcher);
|
||||
const [from, setFrom] = useState({
|
||||
id: "740000030",
|
||||
name: "Falun Centralstation",
|
||||
@@ -60,40 +59,21 @@ function App() {
|
||||
id: "740000001",
|
||||
name: "Stockholm Centralstation",
|
||||
});
|
||||
const [time, setTime] = useState(new Date().toISOString().slice(0, 16));
|
||||
const [time, setTime] = useState(new Date().toISOString());
|
||||
const [num, setNum] = useState(3);
|
||||
const [routes, setRoutes] = useState([]);
|
||||
const [routeKey, setRouteKey] = useState(null);
|
||||
const { data: routes = [], error: routesError, isLoading } = useSWR(routeKey, fetcher);
|
||||
const [selectedRouteIndex, setSelectedRouteIndex] = useState(0);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState("");
|
||||
|
||||
useEffect(() => {
|
||||
fetch("/api/stops")
|
||||
.then((res) => res.json())
|
||||
.then(setStops)
|
||||
.catch((err) => setError("Failed to load stops: " + err.message));
|
||||
}, []);
|
||||
|
||||
const findRoute = () => {
|
||||
if (!from.id || !to.id) {
|
||||
setError("Please select both from and to stops");
|
||||
return;
|
||||
}
|
||||
setLoading(true);
|
||||
setError("");
|
||||
fetch(
|
||||
`/api/route?from=${from.id}&to=${to.id}&when=${encodeURIComponent(time)}&num=${num}`,
|
||||
)
|
||||
.then((res) => (res.ok ? res.json() : Promise.reject(res.statusText)))
|
||||
.then((data) => {
|
||||
setRoutes(data);
|
||||
setSelectedRouteIndex(0);
|
||||
setLoading(false);
|
||||
})
|
||||
.catch((err) => {
|
||||
setError("Failed to find route: " + err);
|
||||
setLoading(false);
|
||||
});
|
||||
setRouteKey(`/api/route?from=${from.id}&to=${to.id}&when=${encodeURIComponent(time)}&num=${num}`);
|
||||
setSelectedRouteIndex(0);
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -142,12 +122,12 @@ function App() {
|
||||
<option value={10}>10</option>
|
||||
</select>
|
||||
</label>
|
||||
<button onClick={findRoute} disabled={loading} type="button">
|
||||
{loading ? "Finding route..." : "Find Route"}
|
||||
<button onClick={findRoute} disabled={isLoading} type="button">
|
||||
{isLoading ? "Finding route..." : "Find Route"}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{error && <p className="error">{error}</p>}
|
||||
{(error || stopsError || routesError) && <p className="error">{error || (stopsError ? "Failed to load stops: " + stopsError.message : "Failed to find route: " + routesError)}</p>}
|
||||
{routes.length > 0 && (
|
||||
<div className="routes">
|
||||
<h2 className="route-title">
|
||||
|
||||
@@ -8,8 +8,6 @@ export default defineConfig({
|
||||
proxy: {
|
||||
"/api": {
|
||||
target: "http://localhost:8080",
|
||||
changeOrigin: true,
|
||||
secure: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user