Skip to content

Commit

Permalink
Eingabemaske standort (#59)
Browse files Browse the repository at this point in the history
Close #19
  • Loading branch information
MiraGeowerkstatt authored Jul 6, 2022
1 parent 4e77c95 commit 9d226e8
Show file tree
Hide file tree
Showing 23 changed files with 988 additions and 83 deletions.
41 changes: 41 additions & 0 deletions docs/articles/einstiegsseite.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,44 @@ Die gefundenen Standorte werden in einer Tabelle unterhalb der Suchmaske angezei
## Daten exportieren

Unter dem Menüpunkt _Daten exportieren_ ![Daten exportieren](../images/file-download-icon.png) können die Daten in eine Textdatei mit kommagetrennten Werten (CSV) exportiert und heruntergeladen werden. Die exportierten Felder entsprechen denen der Datenbank View _bohrung.data_export_. Die in UTF-8 codierten Daten können anschliessend, bspw. in Excel unter _Daten_ -> _Aus Text/CSV_ geladen werden.


## Standorte hinzufügen

Durch Klicken auf den Button _Neuen Standort hinzufügen_ wird ein Dialog geöffnet, in dem ein neuer Standort angelegt werden kann. Hier können die folgenden Felder befüllt werden.

- Bezeichnung (obligatorisch)
- Bemerkung
- Grundbuchnummer

Die folgenden Attribute werden automatisch gesetzt und sind nicht im Dialog sichtbar.

- Gemeinde (sobald eine Bohrung mit Geometrie vorhanden ist)
- Erstellungsdatum
- Benutzername bei Erstellung

## Standorte editieren

In der Tabelle mit gefundenen Standorten unterhalb der Suchmaske können einzelne Standorte editiert werden. Durch Klicken auf das Editieren-Icon ![Editieren-Icon](../images/edit-icon.png) wird ein Dialog geöffnet, in dem der Standort editiert werden kann.

Die folgenden Felder können editiert werden.

- Bezeichnung
- Bemerkung
- Grundbuchnummer

Die folgenden Attribute werden zusätzlich angezeigt, können aber nicht editiert werden.

- Gemeinde
- Erstellungs- und Mutationsdatum
- Benutzername bei Erstellung und Mutation
- Datum der Freigabe durch das AfU
- Benutzername bei Freigabe durch das AfU

Ausserdem werden die dem Standort zugeordneten Bohrungen auf einer Karte dargestellt und in einer Tabelle angezeigt.

## Standorte löschen

In der Tabelle mit gefundenen Standorten unterhalb der Suchmaske können einzelne Standorte durch Klicken auf das Löschen-Icon ![Löschen-Icon](../images/delete-icon.png) gelöscht werden.


Binary file added docs/images/delete-icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/edit-icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
12 changes: 12 additions & 0 deletions launchSettings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"profiles": {
"Docker Compose": {
"commandName": "DockerCompose",
"commandVersion": "1.0",
"serviceActions": {
"db": "StartWithoutDebugging",
"pgadmin": "StartWithoutDebugging"
}
}
}
}
30 changes: 30 additions & 0 deletions src/ChangeTrackerExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using EWS.Models;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.ChangeTracking;

namespace EWS
{
public static class ChangeTrackerExtensions
{
internal static void UpdateChangeInformation(this ChangeTracker changeTracker)
{
var entities = changeTracker.Entries<IDateUserSettable>();

// Replace as soon as authentication is available.
string currentUser = "Temporary Test User";
foreach (var entity in entities)
{
if (entity.State == EntityState.Added)
{
entity.Entity.Erstellungsdatum = DateTime.Now.ToUniversalTime();
entity.Entity.UserErstellung = currentUser;
}
else
{
entity.Entity.Mutationsdatum = DateTime.Now.ToUniversalTime();
entity.Entity.UserMutation = currentUser;
}
}
}
}
}
12 changes: 6 additions & 6 deletions src/ClientApp/cypress/integration/home.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@ describe("Home page tests", () => {
);

cy.visit("/");
cy.get("div[name=home-container]").should("not.contain", "Suchresultate");
cy.get("div[name=home-container]").should("not.contain", "Standorte");

// use click 'force: true' because element is covered by another element.
cy.get("div[name=gemeinde] input").should("be.visible").click({ force: true }).type("Hein{downarrow}{enter}");
cy.get("button[name=submit-button]").should("be.visible").click();
cy.get("div[name=home-container]").should("contain", "Suchresultate");
cy.get("div[name=home-container]").should("contain", "Standorte");
cy.get("tbody").children().should("have.length", 4);
});

Expand All @@ -26,10 +26,10 @@ describe("Home page tests", () => {
standorteGbnummer
);
cy.visit("/");
cy.get("div[name=home-container]").should("not.contain", "Suchresultate");
cy.get("div[name=home-container]").should("not.contain", "Standorte");
cy.get("input[name=gbnummer]").should("be.visible").click({ force: true }).type(gbnummer);
cy.get("button[name=submit-button]").should("be.visible").click();
cy.get("div[name=home-container]").should("contain", "Suchresultate");
cy.get("div[name=home-container]").should("contain", "Standorte");
cy.get("tbody").children().should("have.length", 3);
});

Expand All @@ -39,10 +39,10 @@ describe("Home page tests", () => {
standorteBezeichnung
);
cy.visit("/");
cy.get("div[name=home-container]").should("not.contain", "Suchresultate");
cy.get("div[name=home-container]").should("not.contain", "Standorte");
cy.get("input[name=bezeichnung]").should("be.visible").click({ force: true }).type("Rustic Wooden Keyboard");
cy.get("button[name=submit-button]").should("be.visible").click();
cy.get("div[name=home-container]").should("contain", "Suchresultate");
cy.get("div[name=home-container]").should("contain", "Standorte");
cy.get("tbody").children().should("have.length", 1);
});

Expand Down
48 changes: 38 additions & 10 deletions src/ClientApp/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions src/ClientApp/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
"ol": "^6.14.1",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-hook-form": "^7.33.0",
"react-router-dom": "^5.2.0",
"react-scripts": "^5.0.0",
"rimraf": "^2.6.2"
Expand Down
28 changes: 28 additions & 0 deletions src/ClientApp/src/components/ConfirmationDialog.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import * as React from "react";
import Button from "@mui/material/Button";
import Dialog from "@mui/material/Dialog";
import DialogActions from "@mui/material/DialogActions";
import DialogContent from "@mui/material/DialogContent";
import DialogContentText from "@mui/material/DialogContentText";
import DialogTitle from "@mui/material/DialogTitle";

export default function ConfirmationDialog(props) {
const { entityName, open, confirm } = props;

return (
<div>
<Dialog open={open} onClose={() => confirm(false)} aria-labelledby="Bestätigungsdialog">
<DialogTitle>{entityName + " löschen?"}</DialogTitle>
<DialogContent>
<DialogContentText>Bestätigen Sie den Löschvorgang.</DialogContentText>
</DialogContent>
<DialogActions>
<Button onClick={() => confirm(false)}>Abbrechen</Button>
<Button onClick={() => confirm(true)} autoFocus>
OK
</Button>
</DialogActions>
</Dialog>
</div>
);
}
128 changes: 128 additions & 0 deletions src/ClientApp/src/components/DetailMap.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
import React, { useState, useEffect, useRef } from "react";
import Map from "ol/Map";
import View from "ol/View";
import TileLayer from "ol/layer/Tile";
import WMTS from "ol/source/WMTS";
import VectorSource from "ol/source/Vector";
import Feature from "ol/Feature";
import { optionsFromCapabilities } from "ol/source/WMTS";
import { WMTSCapabilities } from "ol/format";
import { Projection, addProjection } from "ol/proj";
import { Vector } from "ol/layer";
import { Point } from "ol/geom";
import { Style, Circle, Fill, Stroke } from "ol/style";
import "ol/ol.css";

export default function DetailMap(props) {
const { standorte } = props;
const [map, setMap] = useState();
const [bohrungenLayer, setBohrungenLayer] = useState();

const mapElement = useRef();

const defaultStyle = new Style({
image: new Circle({
radius: 4,
stroke: new Stroke({
color: [25, 118, 210, 1],
width: 2,
}),
fill: new Fill({
color: [25, 118, 210, 0.3],
}),
}),
});

// Initialize map on first render
useEffect(() => {
// Add custom projection for LV95
const projection = new Projection({
code: "EPSG:2056",
extent: [2572670, 1211017, 2664529, 1263980],
units: "m",
});
addProjection(projection);

// Create map and feature layer
const bohrungenLayer = new Vector({
zIndex: 1,
source: new VectorSource(),
style: defaultStyle,
});

const initialMap = new Map({
target: mapElement.current,
layers: [bohrungenLayer],
view: new View({
projection: projection,
maxZoom: 8,
zoom: 2,
}),
});

// Save map and vector layer references to state
setMap(initialMap);
setBohrungenLayer(bohrungenLayer);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);

// Handle asynchronous calls to layer sources
// Baselayer
useEffect(() => {
const parser = new WMTSCapabilities();
const url = "https://geo.so.ch/api/wmts/1.0.0/WMTSCapabilities.xml";
fetch(url)
.then(function (response) {
return response.text();
})
.then(function (text) {
const result = parser.read(text);
const options = optionsFromCapabilities(result, {
layer: "ch.so.agi.hintergrundkarte_sw",
});
const landeskarte = new TileLayer({
source: new WMTS(options),
zIndex: 0,
});
map && map.addLayer(landeskarte);
});
}, [map]);

// BohrungenLayer
useEffect(() => {
if (standorte && bohrungenLayer) {
let parsedFeatures;
let bohrungen = standorte?.flatMap((s) => s.bohrungen);
if (bohrungen.length) {
parsedFeatures = bohrungen.map(
(f) =>
new Feature({
geometry: new Point([f.geometrie.coordinates[0], f.geometrie.coordinates[1]]),
Id: f.id,
Bezeichnung: f.bezeichnung,
"Standort Id": f.standortId,
})
);
} else {
parsedFeatures = [];
}
bohrungenLayer.setSource(
new VectorSource({
features: parsedFeatures,
})
);
if (bohrungen.length) {
const currentExtent = bohrungenLayer.getSource().getExtent();
map.getView().fit(currentExtent, {
padding: [30, 30, 30, 30],
});
}
}
}, [standorte, bohrungenLayer, map]);

return (
<div>
<div ref={mapElement} className="detail-map"></div>
</div>
);
}
Loading

0 comments on commit 9d226e8

Please sign in to comment.