Commit a48ee2af authored by Christopher Keim's avatar Christopher Keim
Browse files

[TOB-127,21,406,74,315,52,92,354,391] feat: v0.9.0



[TOB-127] feat: Add components to display statement details

* Add back end calls for statement details
* Add component to display general statement information
* Add component to display geographic position
* Add component to display contribution status
* Add component to display statements inbox attachments
* Add component to display statements outbox attachments
* Add component to display linked statements
* Integrate details components in details page
* Integrate details components in form components
* Refresh process history on task change

[TOB-21] feat: Add functionality to manually redispatch statement email

* Add back end calls to redispatch statement email
* Add store actions and effect for redispatching statement email
* Integrate store into statement details page

[TOB-406] feat: Add statement search

* Extend back end API interface for search parameters
* Add component for search filters
* Add sorting buttons to statement table component
* Integrate statement search into search page

[TOB-74] feat: Add map to position search page

* Reorganize routing of search subpages
* Add directive to display leaflet popups
* Integrate leaflet map, markers and popups into position search page

[TOB-315] feat: Add search functionality to position search page

* Add back end calls for position search
* Add store effect for position search
* Integrate search component and store into position search page

[TOB-52] feat: Add GIS call to leaflet map

* Reorganize website configuration to separate config file
* Add back end calls to transform geographic positions
* Add store module for leaflet map
* Add store effecto to open GIS
* Integrate store module into map components
* Match leaflet styling to openk theme

[TOB-92] feat: Add upload functionality for consideration attachments

* Add component to display and upload consideration attachments
* Integrate component into details page

[TOB-354] feat: Add dockerfiles

[TOB-391] fix: Fix minor bugs

* Cancel rerouting of email when leaving mail page
* Prevent refetching of deleted emails
* Properly reset error messages in side menu
* Also perform contact search in new statement page when no mailid is provided
* Close drop downs on scroll
* Fix minor styling issues
Signed-off-by: Christopher Keim's avatarChristopher Keim <keim@develop-group.de>
parent c14146b0
# ******************************************************************************
# Copyright (c) 2020 Contributors to the Eclipse Foundation
#
# See the NOTICE file(s) distributed with this work for additional
# information regarding copyright ownership.
#
# This program and the accompanying materials are made available under the
# terms of the Eclipse Public License v. 2.0 which is available at
# http://www.eclipse.org/legal/epl-2.0.
#
# SPDX-License-Identifier: EPL-2.0
# ******************************************************************************
FROM nginx:1.18-alpine
COPY dist/statement-public-affairs /html-root
COPY docker/buildDocker/default.conf /etc/nginx/conf.d/default.conf
...@@ -17,25 +17,42 @@ the Java Archiver command line tool in the path variable. ...@@ -17,25 +17,42 @@ the Java Archiver command line tool in the path variable.
## Configuration ## Configuration
The whole application can be configured via certain properties in The whole application can be configured via certain properties in
the `./package.json`. The following options are available: file `./app.config.json`. Changes to these properties take only
effect after (re-)building the application.
* `routes.spaFrontend`: Route on which the website is served
* `routes.spaBackend`: Route on which the website's backend is served * `routes.spaBackend`: Route on which the website's backend is
* `routes.portal`: Route on which the main portal is served served
* `routes.contactDataBase`: Route on which the contact data base module is served * `routes.portal`: Route on which the main portal is served
* `routes.contactDataBase`: Route on which the contact data base
Changes to these properties take only effect after rebuilding the module is served
application. * `leaflet.urlTemplate`: URL template to the map tile server
required by [Leaflet](https://leafletjs.com)
Additionally, the following options can be used to configure all map views based * `leaflet.attribution`: Attribution which is added to all
on [Leaflet](https://leafletjs.com): Leaflet maps
* `leaflet.lat`/`leaflet.lng`/`leaflet.zoom`: Default coordinates
* `leaflet.templateUrl`: Route to the map tile server required by leaflet and zoom level to which all leaflet maps are initially configured
* `leaflet.attribution`: Attribution which is added to the leaflet map, e.g. * `gis.urlTemplate`: URL template to the geographic information
`&copy; <a>TileServer</a> contributors` system (GIS)
* `leaflet.gis`: Route to a GIS system * `gis.projectionFrom`: Coordinate projection used by the
* `leaflet.lat`/`leaflet.lng`/`leaflet.zoom`: Default coordinates and zoom configured Leaflet tile server
level to which all leaflet maps are initially configured * `gis.projectionTo`: Coordinate projection used in the
configured GIS
* `nominatim.url`: URL to a [Nominatim](https://nominatim.org)
geocoding service
* `nominatim.searchQueryPrefix`: Prefix which is added automatically
to every [Nominatim](https://nominatim.org) search query
The following key words are replaced in the given GIS URL template
for each map view:
* `{centerX}`, `{centerY}`: Center coordinates
* `{northEastX}`, `{northEastY}`: North east boundary coordinates
* `{northWestX}`, `{northWestY}`: North west boundary coordinates
* `{southEastX}`, `{southEastY}`: South east boundary coordinates
* `{southWestX}`, `{southWestY}`: South west boundary coordinates
* `{user}`: User name
All coordinates are transformed via a HTTP call to the back end.
For all available projections, please visit [Proj4j](https://trac.osgeo.org/proj4j/).
## Build ## Build
......
{
"gis": {
"urlTemplate": "http://localhost/Ext2GWS/GinPrjExt2GService.asmx/Coordinates?pLLX={southWestX}&pLLY={southWestY}&pURX={northEastX}&pURY={northEastY}&pReportName=TestName&pOSUser={user}&pLSNO=900",
"projectionFrom": "EPSG:4326",
"projectionTo": "EPSG:25832"
},
"leaflet": {
"urlTemplate": "http://localhost:4201/{s}/{z}/{x}/{y}.png",
"attribution": "&copy; <a href=\"http://localhost:4201/copyright\">Tile Server</a> contributors",
"lat": 49.87282103349044,
"lng": 8.651196956634523,
"zoom": 12
},
"nominatim": {
"url": "http://localhost:4202",
"searchQueryPrefix": "Germany"
},
"routes": {
"spaBackend": "/statementpaBE",
"portal": "/portalFE",
"contactDataBase": "/contactdatabase"
}
}
*******************************************************************************
Copyright (c) 2019 Contributors to the Eclipse Foundation
See the NOTICE file(s) distributed with this work for additional
information regarding copyright ownership.
This program and the accompanying materials are made available under the
terms of the Eclipse Public License v. 2.0 which is available at
http://www.eclipse.org/legal/epl-2.0.
SPDX-License-Identifier: EPL-2.0
*******************************************************************************
# Nginx based docker image that contains the statement module frontend
This docker configuration creates a nginx based reverse-proxy.
It provides the statement module frontend at port 80.
Please go to the root folder to build this docker image.
## Configuration
The nginx configuration can be found in the default.conf file.
server {
listen 80 default_server;
listen [::]:80 default_server;
root /html-root;
index index.html;
server_name you.server.com;
location / {
try_files $uri $uri/ @rewrites;
}
location @rewrites {
rewrite ^(.+)$ /index.html last;
}
}
# ******************************************************************************
# Copyright (c) 2020 Contributors to the Eclipse Foundation
#
# See the NOTICE file(s) distributed with this work for additional
# information regarding copyright ownership.
#
# This program and the accompanying materials are made available under the
# terms of the Eclipse Public License v. 2.0 which is available at
# http://www.eclipse.org/legal/epl-2.0.
#
# SPDX-License-Identifier: EPL-2.0
# ******************************************************************************
FROM node:12-alpine
RUN mkdir -p /buildenv/node_modules
RUN mkdir -p /buildenv/root
RUN npm install -g --silent @angular/cli
RUN apk add openjdk8
RUN ln -s /usr/lib/jvm/java-1.8-openjdk/bin/jar /usr/local/bin
*******************************************************************************
Copyright (c) 2019 Contributors to the Eclipse Foundation
See the NOTICE file(s) distributed with this work for additional
information regarding copyright ownership.
This program and the accompanying materials are made available under the
terms of the Eclipse Public License v. 2.0 which is available at
http://www.eclipse.org/legal/epl-2.0.
SPDX-License-Identifier: EPL-2.0
*******************************************************************************
# Build environment
The 'build' script creates reproducable artifact builds.
It uses an node based docker image.
## Usage
To build the artifact, run the compile script from the project base folder (where the package.json is located).
The script deletes the target folder and then runs node build commands.
After a successful build, the artifact is located in the 'dist' folder.
docker volume create --name node-modules
docker run -it -v "$PWD":/buildenv/root -v node-modules:/buildenv/root/node_modules -w /buildenv/root openk-node:12-alpine sh -c "npm install --silent && npm run build && npm run build:archive"
{ {
"name": "openkonsequenz-statement-public-affairs", "name": "openkonsequenz-statement-public-affairs",
"version": "0.8.0", "version": "0.9.0",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {
......
{ {
"name": "openkonsequenz-statement-public-affairs", "name": "openkonsequenz-statement-public-affairs",
"version": "0.8.0", "version": "0.9.0",
"description": "Statement Public Affairs", "description": "Statement Public Affairs",
"license": "Eclipse Public License - v 2.0", "license": "Eclipse Public License - v 2.0",
"repository": { "repository": {
"type": "git", "type": "git",
"url": "https://git.eclipse.org/r/plugins/gitiles/openk-usermodules/org.eclipse.openk-usermodules.statementPublicAffairs.frontend" "url": "https://git.eclipse.org/r/plugins/gitiles/openk-usermodules/org.eclipse.openk-usermodules.statementPublicAffairs.frontend"
}, },
"routes": {
"spaFrontend": "/statementpaFE",
"spaBackend": "/statementpaBE",
"portal": "/portalFE",
"contactDataBase": "/contactdatabase"
},
"leaflet": {
"urlTemplate": "https://localhost:4200/{s}/{z}/{x}/{y}.png",
"attribution": "&copy;",
"gis": "http://localhost:4200?X=##C_X##&Y=##C_Y##pLLX=##LL_X##&pLLY=##LL_Y##&pURX=##UR_X##&pURY=##UR_Y##&user=##OS_USER##",
"lat": 49.87282103349044,
"lng": 8.651196956634523,
"zoom": 12
},
"scripts": { "scripts": {
"-- Build ----------------": "", "-- Build ----------------": "",
"build": "ng build --prod --base-href /statementpaFE/", "build": "ng build --prod --base-href /statementpaFE/",
......
...@@ -85,7 +85,20 @@ describe("AppRoutingModule", () => { ...@@ -85,7 +85,20 @@ describe("AppRoutingModule", () => {
it("should navigate to /search", async () => { it("should navigate to /search", async () => {
const isRoutingSuccessful = await callInZone(() => router.navigate(["search"])); const isRoutingSuccessful = await callInZone(() => router.navigate(["search"]));
expect(isRoutingSuccessful).toBeTruthy(); expect(isRoutingSuccessful).toBeTruthy();
expect(location.path()).toBe("/search"); await Promise.resolve();
expect(location.path()).toBe("/search/list");
});
it("should navigate to /search/list", async () => {
const isRoutingSuccessful = await callInZone(() => router.navigate(["search", "list"]));
expect(isRoutingSuccessful).toBeTruthy();
expect(location.path()).toBe("/search/list");
});
it("should navigate to /search/map", async () => {
const isRoutingSuccessful = await callInZone(() => router.navigate(["search", "map"]));
expect(isRoutingSuccessful).toBeTruthy();
expect(location.path()).toBe("/search/map");
}); });
it("should navigate to /settings", async () => { it("should navigate to /settings", async () => {
......
...@@ -20,8 +20,6 @@ import {AppRoutingModule} from "./app-routing.module"; ...@@ -20,8 +20,6 @@ import {AppRoutingModule} from "./app-routing.module";
import {AppComponent} from "./app.component"; import {AppComponent} from "./app.component";
import {CoreModule} from "./core"; import {CoreModule} from "./core";
import {AppNavigationFrameModule} from "./features/navigation"; import {AppNavigationFrameModule} from "./features/navigation";
import {LeafletModule} from "./shared/layout/leaflet";
import {SideMenuRegistrationService} from "./shared/layout/side-menu/services";
import {AppStoreModule} from "./store"; import {AppStoreModule} from "./store";
@NgModule({ @NgModule({
...@@ -38,8 +36,6 @@ import {AppStoreModule} from "./store"; ...@@ -38,8 +36,6 @@ import {AppStoreModule} from "./store";
AppStoreModule, AppStoreModule,
AppNavigationFrameModule, AppNavigationFrameModule,
LeafletModule.for(SideMenuRegistrationService),
// This import is only important for development; in production, nothing is imported. // This import is only important for development; in production, nothing is imported.
// ! This import must come after AppStoreModule in order make the NGRX Store Devtools available. ! // ! This import must come after AppStoreModule in order make the NGRX Store Devtools available. !
...environment.imports ...environment.imports
......
...@@ -47,6 +47,16 @@ export class AttachmentsApiService { ...@@ -47,6 +47,16 @@ export class AttachmentsApiService {
return this.httpClient.post<IAPIAttachmentModel>(urlJoin(this.baseUrl, endPoint), formData, {params}); return this.httpClient.post<IAPIAttachmentModel>(urlJoin(this.baseUrl, endPoint), formData, {params});
} }
/**
* Uploads a new file for the statement with the given id and automatically sets the consideration tag on the attachment.
*/
public postConsideration(statementId: number, file: File) {
const endPoint = `/statements/${statementId}/consideration`;
const formData = new FormData();
formData.append("attachment", file, file.name);
return this.httpClient.post<IAPIAttachmentModel>(urlJoin(this.baseUrl, endPoint), formData);
}
/** /**
* Uploads a new file to the back end linked to a specific statement. * Uploads a new file to the back end linked to a specific statement.
*/ */
......
/********************************************************************************
* Copyright (c) 2020 Contributors to the Eclipse Foundation
*
* See the NOTICE file(s) distributed with this work for additional
* information regarding copyright ownership.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
********************************************************************************/
export interface IAPIGeographicPositions {
[key: string]: {
x: number;
y: number;
};
}
/********************************************************************************
* Copyright (c) 2020 Contributors to the Eclipse Foundation
*
* See the NOTICE file(s) distributed with this work for additional
* information regarding copyright ownership.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
********************************************************************************/
import {HttpClient} from "@angular/common/http";
import {Inject, Injectable} from "@angular/core";
import {Observable} from "rxjs";
import {objectToHttpParams, urlJoin} from "../../../util/http";
import {SPA_BACKEND_ROUTE} from "../../external-routes";
import {IAPIGeographicPositions} from "./IAPIGeographicPositions";
@Injectable({providedIn: "root"})
export class GeoApiService {
public constructor(
protected readonly httpClient: HttpClient,
@Inject(SPA_BACKEND_ROUTE) protected readonly baseUrl: string
) {
}
public transform(geographicPositions: IAPIGeographicPositions, from: string, to: string): Observable<IAPIGeographicPositions> {
const params = objectToHttpParams({from, to});
const endPoint = `/geo-coordinate-transform`;
return this.httpClient.post<IAPIGeographicPositions>(urlJoin(this.baseUrl, endPoint), geographicPositions, {params});
}
}
/********************************************************************************
* Copyright (c) 2020 Contributors to the Eclipse Foundation
*
* See the NOTICE file(s) distributed with this work for additional
* information regarding copyright ownership.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
********************************************************************************/
export * from "./IAPIGeographicPositions";
export * from "./geo-api.service";
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
export * from "./attachments"; export * from "./attachments";
export * from "./contacts"; export * from "./contacts";
export * from "./core"; export * from "./core";
export * from "./geo";
export * from "./mail"; export * from "./mail";
export * from "./process"; export * from "./process";
export * from "./settings"; export * from "./settings";
......
...@@ -70,12 +70,4 @@ export class MailApiService { ...@@ -70,12 +70,4 @@ export class MailApiService {
return this.httpClient.post<IAPIAttachmentModel[]>(urlJoin(this.baseUrl, endPoint), body); return this.httpClient.post<IAPIAttachmentModel[]>(urlJoin(this.baseUrl, endPoint), body);
} }
/**
* Re-sends the outgoing email for a statement.
*/
public dispatchStatement(statementId: number, taskId: string) {
const endPoint = `/process/statements/${statementId}/task/${taskId}/maildispatch`;
return this.httpClient.post(urlJoin(this.baseUrl, endPoint), null);
}
} }
...@@ -80,4 +80,12 @@ export class ProcessApiService { ...@@ -80,4 +80,12 @@ export class ProcessApiService {
return this.httpClient.get(urlJoin(this.baseUrl, endPoint), {responseType: "text"}); return this.httpClient.get(urlJoin(this.baseUrl, endPoint), {responseType: "text"});
} }
/**
* Re-sends the outgoing email for a statement.
*/
public dispatchStatement(statementId: number, taskId: string) {
const endPoint = `/process/statements/${statementId}/task/${taskId}/mailandcomplete`;
return this.httpClient.post(urlJoin(this.baseUrl, endPoint), null);
}
} }
/********************************************************************************
* Copyright (c) 2020 Contributors to the Eclipse Foundation
*
* See the NOTICE file(s) distributed with this work for additional
* information regarding copyright ownership.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
********************************************************************************/
/**
* Interface which represents the options for a paginated search in the back end data base.
*/
export interface IAPIPositionSearchOptions {
/**
* Key to filter the search by type. Only show results of chosen type.
*/
typeId?: number;
/**
* Key for filtering by due date.
*/
dueDateFrom?: string;
/**
* Key for filtering by due date.
*/
dueDateTo?: string;
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment