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

[TOB-24] feat: Add functionality to edit raw text in statement editor



 * Add directive for automatically resizing text areas
 * Display replacement texts in preview
 * Use currently set values when converting text blocks to free text
 * Prevent shrink of multiple whitespaces in preview

[TOB-58] feat: Add functionality to edit text palceholders in statement editor

 * Disable statement editor when loading back end data
 * Improve styling and UX of input elements for statement editor
 * Add input elements for text placeholders in statement editor

[TOB-189] feat: Add tagging functionality to attachment form

 * Add abstract component for form arrays
 * Add global styles for checkbox and chips
 * Add rxjs operator to end with another observable
 * Add download service
 * Use abstract form array class in arrangement form
 * Add back end calls for editing attachment tags
 * Use form arrays in existing attachment forms
 * Add user control component for attachments
 * Integrate attachment form in statement editor form
 * Integrate attachment form in statement info form
 * Extend attachment store for tagging
 * Download attachment instead of opening it
 * Add translations

[TOB-63] feat: Select optional departments in workflow data form

 * Add indeterminate functionality to multi select component
 * Adjust back end calls for selecting optional departments

[TOB-282] feat: Add side menu

 * Add navigation links to header bar
 * Add module for side menu
 * Integrate side menu to main navigation page
 * Add empty components for future routes
 * Add side menu to statement details page
 * Add side menu to statement info form
 * Add side menu to workflow data form
 * Add side menu to statement editor form
 * Add translations

[TOB-69] feat: Add functionality for contributions/finalization of statements

 * Add back end calls for contributions/finalization
 * Add component for viewing pdfs
 * Integrate pdf preview to statement editor
 * Add contribution select to statement editor
 * Reorganize statement editor form
 * Prevent parallel get requests in forms

[TOB-181] feat: Add functionality to approve statements

 * Add rxjs operator to emit on completion
 * Reorganize effects for process store
 * Integrate process task effect in statement store
 * Unclaim all tasks when leaving edit page
 * Disable side menus on claim/unclaim
 * Reorganize user roles in core module

[TOB-317] fix: Fix minor bugs

 * Fix positioninig of dropw down template in header bar
 * Properly resize input in comment component
 * Create only one license file for all chunks in webpack plugin
Signed-off-by: Christopher Keim's avatarChristopher Keim <keim@develop-group.de>
parent 3272c2d7
{
"name": "openkonsequenz-statement-public-affairs",
"version": "0.6.0",
"version": "0.7.0",
"description": "Statement Public Affairs",
"license": "Eclipse Public License - v 2.0",
"repository": {
......
......@@ -79,4 +79,23 @@ describe("AppRoutingModule", () => {
expect(isRoutingSuccessful).toBeTruthy();
expect(location.path()).toBe("/new");
});
it("should navigate to /mail", async () => {
const isRoutingSuccessful = await callInZone(() => router.navigate(["mail"]));
expect(isRoutingSuccessful).toBeTruthy();
expect(location.path()).toBe("/mail");
});
it("should navigate to /search", async () => {
const isRoutingSuccessful = await callInZone(() => router.navigate(["search"]));
expect(isRoutingSuccessful).toBeTruthy();
expect(location.path()).toBe("/search");
});
it("should navigate to /settings", async () => {
const isRoutingSuccessful = await callInZone(() => router.navigate(["settings"]));
expect(isRoutingSuccessful).toBeTruthy();
expect(location.path()).toBe("/settings");
});
});
......@@ -17,23 +17,38 @@ import {PreloadAllModules, RouterModule, Routes} from "@angular/router";
export const appRoutes: Routes = [
{
path: "",
loadChildren: () => import("./features/dashboard")
.then((m) => m.DashboardModule)
loadChildren: () => import("./features/dashboard/dashboard-routing.module")
.then((m) => m.DashboardRoutingModule)
},
{
path: "details",
loadChildren: () => import("./features/details")
.then((m) => m.StatementDetailsModule)
loadChildren: () => import("./features/details/statement-details-routing.module")
.then((m) => m.StatementDetailsRoutingModule)
},
{
path: "new",
loadChildren: () => import("./features/new")
.then((m) => m.NewStatementModule)
loadChildren: () => import("./features/new/new-statement-routing.module")
.then((m) => m.NewStatementRoutingModule)
},
{
path: "mail",
loadChildren: () => import("./features/mail/mail-routing.module")
.then((m) => m.MailRoutingModule)
},
{
path: "search",
loadChildren: () => import("./features/search/search-routing.module")
.then((m) => m.SearchRoutingModule)
},
{
path: "edit",
loadChildren: () => import("./features/edit")
.then((m) => m.StatementEditModule)
loadChildren: () => import("./features/edit/statement-edit-routing.module")
.then((m) => m.StatementEditRoutingModule)
},
{
path: "settings",
loadChildren: () => import("./features/settings/settings-routing.module")
.then((m) => m.SettingsRoutingModule)
},
// The wildcard has to be placed as the last item (otherwise, its overriding routes)
{
......
......@@ -26,3 +26,11 @@ export enum EAPIStaticAttachmentTagIds {
COVER_LETTER = "cover-letter"
}
export const AUTO_SELECTED_TAGS = [
EAPIStaticAttachmentTagIds.EMAIL_TEXT,
EAPIStaticAttachmentTagIds.EMAIL,
EAPIStaticAttachmentTagIds.OUTBOX,
EAPIStaticAttachmentTagIds.CONSIDERATION,
EAPIStaticAttachmentTagIds.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 IAPIAttachmentTag {
label: string;
id: string;
}
......@@ -16,6 +16,7 @@ import {Inject, Injectable} from "@angular/core";
import {objectToHttpParams, urlJoin} from "../../../util/http";
import {SPA_BACKEND_ROUTE} from "../../external-routes";
import {IAPIAttachmentModel} from "./IAPIAttachmentModel";
import {IAPIAttachmentTag} from "./IAPIAttachmentTag";
@Injectable({providedIn: "root"})
export class AttachmentsApiService {
......@@ -54,4 +55,28 @@ export class AttachmentsApiService {
return this.httpClient.delete(urlJoin(this.baseUrl, endPoint));
}
/**
* Changes the tags of a given attachment in the back end data base.
*/
public postAttachmentTags(statementId: number, taskId: string, attachmentId: number, ...tagId: string[]) {
const endPoint = `/process/statements/${statementId}/task/${taskId}/attachments/${attachmentId}/tags`;
return this.httpClient.post(urlJoin(this.baseUrl, endPoint), tagId);
}
/**
* Fetches the list of all available tags in the back end data base.
*/
public getTagList() {
const endPoint = `/tags`;
return this.httpClient.get<IAPIAttachmentTag[]>(urlJoin(this.baseUrl, endPoint));
}
/**
* Creates a new tag in the back end data base.
*/
public addNewTag(label: string) {
const endPoint = `/tags`;
return this.httpClient.put(urlJoin(this.baseUrl, endPoint), {label});
}
}
......@@ -14,3 +14,4 @@
export * from "./attachments-api.service";
export * from "./EAPIStaticAttachmentTagIds";
export * from "./IAPIAttachmentModel";
export * from "./IAPIAttachmentTag";
......@@ -11,9 +11,15 @@
* SPDX-License-Identifier: EPL-2.0
********************************************************************************/
export enum ESPAUserRoles {
export enum EAPIUserRoles {
DIVISION_MEMBER = "ROLE_SPA_DIVISION_MEMBER",
ROLE_SPA_ACCESS = "ROLE_SPA_ACCESS",
SPA_APPROVER = "ROLE_SPA_APPROVER",
SPA_OFFICIAL_IN_CHARGE = "ROLE_SPA_OFFICIAL_IN_CHARGE"
}
export const ALL_NON_TRIVIAL_USER_ROLES = [
EAPIUserRoles.DIVISION_MEMBER,
EAPIUserRoles.SPA_OFFICIAL_IN_CHARGE,
EAPIUserRoles.SPA_APPROVER
];
......@@ -11,8 +11,11 @@
* SPDX-License-Identifier: EPL-2.0
********************************************************************************/
import {EAPIUserRoles} from "./EAPIUserRoles";
export interface IAPIUserInfo {
firstName: string;
lastName: string;
roles: string[];
roles: EAPIUserRoles[];
userName: string;
}
......@@ -12,5 +12,6 @@
********************************************************************************/
export * from "./core-api.service";
export * from "./EAPIUserRoles";
export * from "./IAPIUserInfo";
export * from "./IAPIVersion";
......@@ -19,11 +19,11 @@ export enum EAPIProcessTaskDefinitionKey {
CREATE_DRAFT = "createDraft",
ENRICH_DRAFT = "enrichDraft",
CREATE_NEGATIVE_RESPONSE = "createNegativeResponse",
CHECK_FOR_COMPLETENESS = "checkForCompleteness",
ENRICH_DRAFT = "enrichDraft",
FINALIZE_STATEMENT = "finalizeStatement",
CHECK_AND_FORMULATE_RESPONSE = "checkAndFormulateResponse",
APPROVE_STATEMENT = "approveStatement",
......
......@@ -26,3 +26,26 @@ export type _IAPIProcessObject =
| { [key: string]: { type: "String", value: string } }
| { [key: string]: { type: "Number", value: number } };
export type TAddBasicInfoDataCompleteVariable = {
responsible: { type: "Boolean", value: boolean }
};
export type TCreateNegativeResponseCompleteVariable = {
response_created: { type: "Boolean", value: boolean }
};
export type TCheckAndFormulateResponseCompleteVariable = {
response_created: { type: "Boolean", value: boolean },
data_complete: { type: "Boolean", value: boolean }
};
export type TApproveStatementCompleteVariable = {
approved_statement: { type: "Boolean", value: boolean }
};
export type TCompleteTaskVariable = {}
| TAddBasicInfoDataCompleteVariable
| TCreateNegativeResponseCompleteVariable
| TCheckAndFormulateResponseCompleteVariable
| TApproveStatementCompleteVariable;
......@@ -19,9 +19,14 @@ import {IAPIDepartmentGroups} from "../settings";
export interface IAPIWorkflowData {
/**
* Object which contains all departments assigned to a statement.
* Object which contains all mandatory departments assigned to a statement.
*/
selectedDepartments: IAPIDepartmentGroups;
mandatoryDepartments: IAPIDepartmentGroups;
/**
* Object which contains all optional departments assigned to a statement.
*/
optionalDepartments: IAPIDepartmentGroups;
/**
* String which represents the geographic position assigned to a statement.
......
......@@ -15,6 +15,7 @@ import {HttpClient} from "@angular/common/http";
import {Inject, Injectable} from "@angular/core";
import {objectToHttpParams, urlJoin} from "../../../util";
import {SPA_BACKEND_ROUTE} from "../../external-routes";
import {IAPIDepartmentGroups} from "../settings";
import {IAPIPaginationResponse, IAPISearchOptions} from "../shared";
import {IAPICommentModel} from "./IAPICommentModel";
import {IAPISectorsModel} from "./IAPISectorsModel";
......@@ -93,6 +94,22 @@ export class StatementsApiService {
return this.httpClient.post(urlJoin(this.baseUrl, endPoint), body);
}
/**
* Fetches the current status of the departments that have finished their contribution to the statement..
*/
public getContributions(statementId: number) {
const endPoint = `/process/statements/${statementId}/workflow/contributions`;
return this.httpClient.get<IAPIDepartmentGroups>(urlJoin(this.baseUrl, endPoint));
}
/**
* Creates or updates the contributions in the back end data base.
*/
public postContributions(statementId: number, taskId: string, body: IAPIDepartmentGroups) {
const endPoint = `/process/statements/${statementId}/task/${taskId}/workflow/contributions`;
return this.httpClient.post(urlJoin(this.baseUrl, endPoint), body);
}
/**
* Fetches the IDs of all parents to a specific statement.
*/
......@@ -141,4 +158,13 @@ export class StatementsApiService {
return this.httpClient.get<IAPISectorsModel>(urlJoin(this.baseUrl, endPoint));
}
/**
* Updates the contribution status of the user's department to true for the given statement.
*/
public contribute(statementId: number, taskId: string) {
const endPoint = `/process/statements/${statementId}/task/${taskId}/workflow/contribute`;
return this.httpClient.patch(urlJoin(this.baseUrl, endPoint), null);
}
}
......@@ -10,22 +10,26 @@
*
* SPDX-License-Identifier: EPL-2.0
********************************************************************************/
import {createSelector} from "@ngrx/store";
import {IPageHeaderAction} from "../../../shared/layout/page-header";
import {taskSelector} from "../../../store";
import {DOCUMENT} from "@angular/common";
import {Inject, Injectable} from "@angular/core";
@Injectable({providedIn: "root"})
export class DownloadService {
public constructor(@Inject(DOCUMENT) public readonly document: Document) {
export const statementEditHeaderButtonSelector = createSelector(
taskSelector,
(task): IPageHeaderAction[] => {
return [task?.statementId != null ? {
name: "core.actions.backToDetails",
icon: "arrow_back",
routerLink: "/details",
queryParams: {id: task?.statementId}
} : {
name: "core.actions.backToDashboard",
icon: "arrow_back",
routerLink: "/",
}];
}
);
public startDownload(url: string, token?: string) {
url += token == null ? "" : `?accessToken=${token}`;
const anchor = this.document.createElement("a");
anchor.style.display = "none";
anchor.target = "_blank";
anchor.rel = "noreferrer";
anchor.href = url;
anchor.download = "";
anchor.click();
anchor.href = null;
}
}
......@@ -11,4 +11,4 @@
* SPDX-License-Identifier: EPL-2.0
********************************************************************************/
export * from "./text-field.module";
export * from "./download.service";
......@@ -14,6 +14,7 @@
export * from "./api";
export * from "./auth";
export * from "./dom";
export * from "./download";
export * from "./external-routes";
export * from "./i18n";
......
......@@ -15,17 +15,6 @@
<span class="dashboard-header-title">Stellungnahmen öffentlicher Belange</span>
<div class="dashboard-header-actions">
<button class="openk-button openk-info" disabled>
<mat-icon>view_list</mat-icon>
Alle Stellungnahmen anzeigen
</button>
<a *ngIf="isOfficialInCharge$ | async" class="openk-button openk-info" routerLink="/new">
<mat-icon>note_add</mat-icon>
Neue Stellungnahme hinzufügen
</a>
</div>
</div>
<div class="dashboard">
......
......@@ -14,6 +14,7 @@
import {NgModule} from "@angular/core";
import {RouterModule, Routes} from "@angular/router";
import {DashboardComponent} from "./components";
import {DashboardModule} from "./dashboard.module";
const routes: Routes = [
{
......@@ -25,6 +26,7 @@ const routes: Routes = [
@NgModule({
imports: [
DashboardModule,
RouterModule.forChild(routes)
],
exports: [
......
......@@ -14,15 +14,15 @@
import {CommonModule} from "@angular/common";
import {NgModule} from "@angular/core";
import {MatIconModule} from "@angular/material/icon";
import {RouterModule} from "@angular/router";
import {CardModule} from "../../shared/layout/card";
import {SharedPipesModule} from "../../shared/pipes";
import {DashboardComponent, DashboardItemComponent, DashboardListComponent} from "./components";
import {DashboardRoutingModule} from "./dashboard-routing.module";
@NgModule({
imports: [
CommonModule,
DashboardRoutingModule,
RouterModule,
MatIconModule,
CardModule,
SharedPipesModule
......
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