Lädt…
: null} + {isError ? ( ++ Bienenstände konnten nicht geladen werden. +
+ ) : null} + + {apiaries && located.length === 0 ? ( +diff --git a/ApiCore-Frontend/package-lock.json b/ApiCore-Frontend/package-lock.json index 306995c..43f032d 100644 --- a/ApiCore-Frontend/package-lock.json +++ b/ApiCore-Frontend/package-lock.json @@ -1,22 +1,24 @@ { - "name": "apicore-frontend-tmp", + "name": "apicore-frontend", "version": "0.1.0", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "apicore-frontend-tmp", + "name": "apicore-frontend", "version": "0.1.0", "dependencies": { "@base-ui/react": "^1.7.0", "@tanstack/react-query": "^5.101.4", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", + "leaflet": "^1.9.4", "lucide-react": "^1.33.0", "next": "16.3.1", "next-themes": "^0.4.6", "react": "19.2.8", "react-dom": "19.2.8", + "react-leaflet": "^5.0.0", "shadcn": "^4.18.0", "sonner": "^2.0.8", "tailwind-merge": "^3.6.0", @@ -27,6 +29,7 @@ "@testing-library/jest-dom": "^7.0.1", "@testing-library/react": "^16.3.2", "@testing-library/user-event": "^14.6.5", + "@types/leaflet": "^1.9.22", "@types/node": "^20", "@types/react": "^19", "@types/react-dom": "^19", @@ -2690,6 +2693,17 @@ "node": ">=12.4.0" } }, + "node_modules/@react-leaflet/core": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@react-leaflet/core/-/core-3.0.0.tgz", + "integrity": "sha512-3EWmekh4Nz+pGcr+xjf0KNyYfC3U2JjnkWsh0zcqaexYqmmB5ZhH37kz41JXGmKzpaMZCnPofBBm64i+YrEvGQ==", + "license": "Hippocratic-2.1", + "peerDependencies": { + "leaflet": "^1.9.0", + "react": "^19.0.0", + "react-dom": "^19.0.0" + } + }, "node_modules/@rolldown/pluginutils": { "version": "1.0.0-beta.27", "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.27.tgz", @@ -3710,6 +3724,13 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/geojson": { + "version": "7946.0.16", + "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.16.tgz", + "integrity": "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/json-schema": { "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", @@ -3724,6 +3745,16 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/leaflet": { + "version": "1.9.22", + "resolved": "https://registry.npmjs.org/@types/leaflet/-/leaflet-1.9.22.tgz", + "integrity": "sha512-h3lhECYEKDasG7LFHu+GiHqAvsgLuQvlJvVZzJDGONo3sEL+wUOqSFLnwkZlK0qVxnxbuGFW8iBlJNYs5wgndA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/geojson": "*" + } + }, "node_modules/@types/node": { "version": "20.19.43", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.43.tgz", @@ -8325,6 +8356,12 @@ "node": ">=0.10" } }, + "node_modules/leaflet": { + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/leaflet/-/leaflet-1.9.4.tgz", + "integrity": "sha512-nxS1ynzJOmOlHp+iL3FyWqK89GtNL8U8rvlMOsQdTTssxZwCXh8N2NB3GDQOL+YR3XnWyZAxwQixURb+FA74PA==", + "license": "BSD-2-Clause" + }, "node_modules/levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -9940,6 +9977,20 @@ "dev": true, "license": "MIT" }, + "node_modules/react-leaflet": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/react-leaflet/-/react-leaflet-5.0.0.tgz", + "integrity": "sha512-CWbTpr5vcHw5bt9i4zSlPEVQdTVcML390TjeDG0cK59z1ylexpqC6M1PJFjV8jD7CF+ACBFsLIDs6DRMoLEofw==", + "license": "Hippocratic-2.1", + "dependencies": { + "@react-leaflet/core": "^3.0.0" + }, + "peerDependencies": { + "leaflet": "^1.9.0", + "react": "^19.0.0", + "react-dom": "^19.0.0" + } + }, "node_modules/react-refresh": { "version": "0.17.0", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz", diff --git a/ApiCore-Frontend/package.json b/ApiCore-Frontend/package.json index 3bb6051..27ecfb0 100644 --- a/ApiCore-Frontend/package.json +++ b/ApiCore-Frontend/package.json @@ -16,11 +16,13 @@ "@tanstack/react-query": "^5.101.4", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", + "leaflet": "^1.9.4", "lucide-react": "^1.33.0", "next": "16.3.1", "next-themes": "^0.4.6", "react": "19.2.8", "react-dom": "19.2.8", + "react-leaflet": "^5.0.0", "shadcn": "^4.18.0", "sonner": "^2.0.8", "tailwind-merge": "^3.6.0", @@ -31,6 +33,7 @@ "@testing-library/jest-dom": "^7.0.1", "@testing-library/react": "^16.3.2", "@testing-library/user-event": "^14.6.5", + "@types/leaflet": "^1.9.22", "@types/node": "^20", "@types/react": "^19", "@types/react-dom": "^19", diff --git a/ApiCore-Frontend/public/leaflet/marker-icon-2x.png b/ApiCore-Frontend/public/leaflet/marker-icon-2x.png new file mode 100644 index 0000000..88f9e50 Binary files /dev/null and b/ApiCore-Frontend/public/leaflet/marker-icon-2x.png differ diff --git a/ApiCore-Frontend/public/leaflet/marker-icon.png b/ApiCore-Frontend/public/leaflet/marker-icon.png new file mode 100644 index 0000000..950edf2 Binary files /dev/null and b/ApiCore-Frontend/public/leaflet/marker-icon.png differ diff --git a/ApiCore-Frontend/public/leaflet/marker-shadow.png b/ApiCore-Frontend/public/leaflet/marker-shadow.png new file mode 100644 index 0000000..9fd2979 Binary files /dev/null and b/ApiCore-Frontend/public/leaflet/marker-shadow.png differ diff --git a/ApiCore-Frontend/src/app/(app)/apiaries/[id]/apiary-edit-card.tsx b/ApiCore-Frontend/src/app/(app)/apiaries/[id]/apiary-edit-card.tsx index f27489e..3f6fe63 100644 --- a/ApiCore-Frontend/src/app/(app)/apiaries/[id]/apiary-edit-card.tsx +++ b/ApiCore-Frontend/src/app/(app)/apiaries/[id]/apiary-edit-card.tsx @@ -23,6 +23,8 @@ export function ApiaryEditCard({ apiary }: { apiary: Apiary }) { const queryClient = useQueryClient(); const [name, setName] = useState(apiary.name); const [location, setLocation] = useState(apiary.location ?? ""); + const [latitude, setLatitude] = useState(apiary.latitude?.toString() ?? ""); + const [longitude, setLongitude] = useState(apiary.longitude?.toString() ?? ""); const [address, setAddress] = useState(apiary.address ?? ""); const [notes, setNotes] = useState(apiary.notes ?? ""); @@ -31,6 +33,8 @@ export function ApiaryEditCard({ apiary }: { apiary: Apiary }) { updateApiary(apiary.id, { name, location: location || undefined, + latitude: latitude ? Number(latitude) : undefined, + longitude: longitude ? Number(longitude) : undefined, address: address || undefined, notes: notes || undefined, }), @@ -99,6 +103,32 @@ export function ApiaryEditCard({ apiary }: { apiary: Apiary }) { onChange={(event) => setLocation(event.target.value)} /> +
Karte lädt…
}, +); + +function hasCoordinates( + apiary: Apiary, +): apiary is Apiary & { latitude: number; longitude: number } { + return apiary.latitude !== null && apiary.longitude !== null; +} + +export default function ApiaryMapPage() { + const { data: apiaries, isLoading, isError } = useQuery({ + queryKey: ["apiaries"], + queryFn: () => fetchApiaries(), + }); + + const located = apiaries?.filter(hasCoordinates) ?? []; + + return ( +Lädt…
: null} + {isError ? ( ++ Bienenstände konnten nicht geladen werden. +
+ ) : null} + + {apiaries && located.length === 0 ? ( +{apiary.location}
: null} +