Skip to content

Commit

Permalink
Anlegen einer Subsite mit dem Titel des entsprechenden Gruppennamens (#…
Browse files Browse the repository at this point in the history
…17)

Co-authored-by: Thomas Schley <thomas.schley@lmis.de>
  • Loading branch information
THS-LMIS and Thomas Schley authored Dec 20, 2024
1 parent 06004ad commit 9e40701
Show file tree
Hide file tree
Showing 14 changed files with 357 additions and 20 deletions.
10 changes: 10 additions & 0 deletions app/components/Breadcrumbs.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,16 @@
background-color: #41485A;
display: flex;
padding: 10px min(4vh, 2vw);
@media only screen and (max-height: 767px) {
padding-bottom: 5px;
padding-top: 5px;
}
a {
color: white;
font-size: large;
@media only screen and (max-height: 767px) {
font-size: medium;
}
text-decoration: none;
}
a + a:before {
Expand All @@ -15,4 +22,7 @@
a.active {
font-weight: bold;
}
a.disabled {
pointer-events: none;
}
}
21 changes: 19 additions & 2 deletions app/components/Breadcrumbs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,25 @@ import {NavLink, useLocation} from 'react-router';
import styles from './Breadcrumbs.module.css';

const routeLabels: { [key: string]: string } = {
group: 'Gruppe',
impressum: 'Impressum'
};

const routeWithoutLink: string[] = [
'group'
];

function calculateNavLinkClassNames(isActive: boolean, disabled: boolean) {
const classNames: string[] = [];
if (isActive) {
classNames.push(styles.active);
}
if (disabled) {
classNames.push(styles.disabled);
}
return classNames.join(' ');
}

function Breadcrumbs() {
const location = useLocation();
const pathnames = location.pathname.split('/').filter((x) => x);
Expand All @@ -20,9 +36,10 @@ function Breadcrumbs() {
{pathnames.map((pathname, index) => {
const to = `/${pathnames.slice(0, index + 1).join('/')}`;
const label: string = routeLabels[pathname] || pathname;
const disabled = routeWithoutLink.includes(pathname);
return <NavLink
to={to}
className={({ isActive }) => (isActive ? styles.active : undefined)}
to={disabled ? '#' : to}
className={({ isActive }) => calculateNavLinkClassNames(isActive, disabled)}
key={pathname}
>
{decodeURIComponent(label)}
Expand Down
4 changes: 4 additions & 0 deletions app/components/Footer.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@
gap: 40px;
list-style: none;
padding: 20px min(4vh, 2vw);
@media only screen and (max-height: 767px) {
padding-bottom: 10px;
padding-top: 10px;
}
a {
color: white;
font-size: x-large;
Expand Down
5 changes: 5 additions & 0 deletions app/components/Header.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,9 @@
padding: 20px min(4vh, 2vw);
text-align: center;
text-decoration: none;
@media only screen and (max-height: 767px) {
font-size: xx-large;
padding-bottom: 10px;
padding-top: 10px;
}
}
48 changes: 40 additions & 8 deletions app/components/OpenLayers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@ import React, {useEffect, useRef} from 'react';

import {Feature, Map, View} from 'ol';
import {Coordinate} from 'ol/coordinate';
import {Extent} from "ol/extent";
import {Extent} from 'ol/extent';
import {Point, Polygon} from 'ol/geom';
import TileLayer from 'ol/layer/Tile';
import VectorLayer from "ol/layer/Vector";
import {fromLonLat} from 'ol/proj';
import {OSM} from 'ol/source';
import VectorSource from 'ol/source/Vector';
import {Fill, Stroke, Style} from 'ol/style';
import CircleStyle from 'ol/style/Circle';

import 'ol/ol.css';
import styles from './OpenLayers.module.css';
Expand All @@ -36,7 +37,9 @@ function createPointVectorLayer(name: string, source: VectorSource<Feature<Point
interface Props {
id: string,
agriCrops: AgriCrop[],
selectedAgriCrop?: AgriCrop|undefined,
selectedGroup: TenantGroup|undefined,
selectedSensor?: Sensor|undefined,
sensors: Sensor[]
}

Expand All @@ -52,7 +55,7 @@ function fitMap(mapView: View | undefined, extent: Extent | undefined) {
}
}

function OpenLayers({ agriCrops, id, selectedGroup, sensors }: Props) {
function OpenLayers({ agriCrops, id, selectedAgriCrop, selectedGroup, selectedSensor, sensors }: Props) {

const mapRef = useRef<Map | undefined>(undefined);

Expand All @@ -70,7 +73,7 @@ function OpenLayers({ agriCrops, id, selectedGroup, sensors }: Props) {
let pointVectorLayer = createPointVectorLayer(pointVectorLayerName, pointVectorSource);
let polygonVectorLayer = createPolygonVectorLayer(polygonVectorLayerName, polygonVectorSource);

const style = new Style({
const normalPolygonStyle = new Style({
fill: new Fill({
color: 'rgba(0, 128, 255, 0.4)',
}),
Expand All @@ -80,6 +83,29 @@ function OpenLayers({ agriCrops, id, selectedGroup, sensors }: Props) {
}),
});

const highlightPolygonStyle = new Style({
fill: new Fill({
color: 'rgba(255, 0, 0, 0.4)',
}),
stroke: new Stroke({
color: 'red',
width: 2,
}),
});

const highlightPointStyle = new Style({
image: new CircleStyle({
fill: new Fill({
color: 'rgba(255, 0, 0, 0.4)'
}),
stroke: new Stroke({
color: 'red',
width: 1
}),
radius: 5
})
});

function updateAgriCrops () {
const map = mapRef.current;
if (!map || agriCrops.length === 0) return;
Expand All @@ -93,7 +119,7 @@ function OpenLayers({ agriCrops, id, selectedGroup, sensors }: Props) {
lonLatCoordinates.push(fromLonLat(coordinate));
});
const polygonFeature = new Feature({ geometry: new Polygon([lonLatCoordinates]) });
polygonFeature.setStyle(style);
polygonFeature.setStyle(agriCrop.id === selectedAgriCrop?.id ? highlightPolygonStyle : normalPolygonStyle);
features.push(polygonFeature);
});
polygonVectorSource.clear();
Expand All @@ -105,7 +131,9 @@ function OpenLayers({ agriCrops, id, selectedGroup, sensors }: Props) {
}
polygonVectorLayer = createPolygonVectorLayer(polygonVectorLayerName, polygonVectorSource);
map.addLayer(polygonVectorLayer);
fitMap(map.getView(), polygonVectorSource.getExtent());
if (!selectedAgriCrop && !selectedSensor) {
fitMap(map.getView(), polygonVectorSource.getExtent());
}
}

function updateSensors() {
Expand All @@ -116,7 +144,11 @@ function OpenLayers({ agriCrops, id, selectedGroup, sensors }: Props) {
return !selectedGroup || sensor.customGroup === selectedGroup.groupId;
});
_sensors.forEach((sensor: Sensor) => {
features.push(new Feature({ geometry: new Point(fromLonLat(sensor.coordinates)) }));
const pointFeature = new Feature({ geometry: new Point(fromLonLat(sensor.coordinates)) });
if (sensor.id === selectedSensor?.id) {
pointFeature.setStyle(highlightPointStyle);
}
features.push(pointFeature);
});
pointVectorSource.clear();
pointVectorSource.addFeatures(features);
Expand Down Expand Up @@ -148,13 +180,13 @@ function OpenLayers({ agriCrops, id, selectedGroup, sensors }: Props) {
if (mapRef.current && agriCrops.length > 0) {
updateAgriCrops();
}
}, [agriCrops, selectedGroup]);
}, [agriCrops, selectedGroup, selectedAgriCrop]);

useEffect(() => {
if (mapRef.current && sensors.length > 0) {
updateSensors();
}
}, [sensors, selectedGroup]);
}, [sensors, selectedGroup, selectedSensor]);

return <div id={id} className={styles.map}></div>;
}
Expand Down
57 changes: 57 additions & 0 deletions app/components/SingleSelectionGroupContents.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import React, {ChangeEventHandler, MouseEventHandler} from 'react';
import {Button, Form} from 'react-bootstrap';
import {NavLink} from 'react-router';

import AgriCrop from '../models/AgriCrop';
import Sensor from '../models/Sensor';

import styles from './SingleSelectionGroups.module.css';

interface Props {
agriCrops: AgriCrop[],
handleChange: ChangeEventHandler,
handleReset: MouseEventHandler,
selectedAgriCrop: AgriCrop | undefined,
selectedSensor: Sensor | undefined,
sensors: Sensor[]
}

function SingleSelectionGroupContents({ agriCrops, handleChange, handleReset, selectedAgriCrop, selectedSensor, sensors }: Props) {
return <>
<h4>Übersicht der Gruppeninhalte:</h4>
<Form id="group_contents">
{sensors.map((sensor) => (
<Form.Check
className={styles.groups}
type="radio"
id={sensor.id}
key={sensor.id}
label={sensor.id}
value={sensor.id}
name="group_contents"
checked={selectedSensor?.id === sensor.id}
onChange={handleChange}
/>
))}
{agriCrops.map((agriCrops) => (
<Form.Check
className={styles.groups}
type="radio"
id={agriCrops.id}
key={agriCrops.id}
label={agriCrops.id}
value={agriCrops.id}
name="group_contents"
checked={selectedAgriCrop?.id === agriCrops.id}
onChange={handleChange}
/>
))}
<NavLink className="btn btn-secondary mt-5" to="/">Zurück</NavLink>
<Button className="ms-5 mt-5" variant="secondary" onClick={handleReset} disabled={!selectedSensor && !selectedAgriCrop}>
Auswahl aufheben
</Button>
</Form>
</>;
}

export default SingleSelectionGroupContents;
13 changes: 9 additions & 4 deletions app/components/SingleSelectionGroups.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import styles from './SingleSelectionGroups.module.css';

import React from 'react';
import React, {ChangeEventHandler, MouseEventHandler} from 'react';
import {Button, Form} from 'react-bootstrap';
import {NavLink} from 'react-router';

import TenantGroup from '../models/TenantGroup';

interface Props {
groups: TenantGroup[],
handleChange: any,
handleReset: any,
handleChange: ChangeEventHandler,
handleReset: MouseEventHandler,
selectedGroup: TenantGroup|undefined
}

Expand All @@ -31,9 +32,13 @@ function SingleSelectionGroups({ handleChange, handleReset, groups, selectedGrou
onChange={handleChange}
/>
))}
<Button className="mt-5" variant="secondary" onClick={handleReset}>
<Button className="mt-5" variant="secondary" onClick={handleReset} disabled={!selectedGroup}>
Auswahl aufheben
</Button>
<NavLink className={`btn btn-secondary ms-5 mt-5 ${selectedGroup ? '' : 'disabled'}`}
to={selectedGroup ? '/group/' + selectedGroup.name : '#'}>
Weiter
</NavLink>
</Form>
</>
);
Expand Down
1 change: 1 addition & 0 deletions app/models/Sensor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {Coordinate} from 'ol/coordinate';
export default interface Sensor {
id: string,
type: string,
name: string,
customGroup: string,
coordinates: Coordinate
}
3 changes: 2 additions & 1 deletion app/models/SensorResponse.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@ export default interface SensorResponse {
id: string,
type: string,
customGroup: string,
location: SingleLocationResponse | null
location: SingleLocationResponse | null,
name: string
}
3 changes: 2 additions & 1 deletion app/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@ import {index, route, RouteConfig} from '@react-router/dev/routes';

export default [
index('./routes/Home.tsx'),
route('impressum', './routes/Impressum.tsx')
route('impressum', './routes/Impressum.tsx'),
route('group/:groupName', './routes/Group.tsx')
] satisfies RouteConfig;
Loading

0 comments on commit 9e40701

Please sign in to comment.