diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 6cdeb920..f0c8b6e7 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -47,7 +47,7 @@ import { HeaderComponent } from 'app/shared/header/header.component'; import { NavigationComponent } from 'app/shared/navigation/navigation.component'; import { UsersService } from 'app/services/users.service'; import { FmusService } from 'app/services/fmus.service'; -// import { TabsComponent } from 'app/shared/tabs/tabs.component'; +import { TabsComponent } from 'app/shared/tabs/tabs.component'; import { ObservationDetailComponent } from 'app/pages/observations/observation-detail.component'; import { FieldListComponent } from 'app/pages/fields/field-list.component'; import { FieldDetailComponent } from 'app/pages/fields/field-detail.component'; @@ -121,7 +121,7 @@ export function createTranslateLoader(http: HttpClient) { ProfileComponent, FieldListComponent, FieldDetailComponent, - // TabsComponent, + TabsComponent, NavigationComponent, NavigationItemDirective, HeaderComponent, diff --git a/src/app/models/observation_report.ts b/src/app/models/observation_report.ts index c8d80887..f1f0e966 100644 --- a/src/app/models/observation_report.ts +++ b/src/app/models/observation_report.ts @@ -14,5 +14,6 @@ export class ObservationReport extends JsonApiModel { @Attribute({ converter: new DateConverter() }) 'publication-date': Date; @HasMany() observers: Observer[]; + @HasMany() observations: Observation[]; } diff --git a/src/app/pages/observations/observation-detail.component.html b/src/app/pages/observations/observation-detail.component.html index 3f5146e0..9fdcf597 100644 --- a/src/app/pages/observations/observation-detail.component.html +++ b/src/app/pages/observations/observation-detail.component.html @@ -85,6 +85,125 @@

{{'Edit observation' | tra

{{'observation.statusNote.Published' | translate}}

+ +
+ {{'Evidence' | translate}} +
+
+ + +

{{'observation.evidenceType.note' | translate}}

+
+ + +
+ + +
{{'observation.evidenceDetails.invalid' | translate}}
+

{{ 'observation.evidenceDetails.note' | translate }}

+
+
+ + +
+
+
+

{{'List of evidence' | translate}}

+
    +
  • +
    +
    {{document.name}}
    +
    {{'Already uploaded' | translate}}
    +
    {{'To upload' | translate}}
    +
    +
  • +
+

{{'No evidence' | translate}}

+
+
+ +

{{'List of evidence' | translate}}

+
    +
  • +
    +
    {{document.name}}
    +
    {{'Already uploaded' | translate}}
    +
    {{'To upload' | translate}}
    +
    +
    + +
    +
  • +
+

{{'No evidence' | translate}}

+
+
+ + + + +
    +
  • +
    +
    {{document.name}}
    +
    Preview
    +
    +
    + +
    +
  • +
+

{{'No evidence' | translate}}

+
+ + +
+ +
+ + +
{{'observation.evidenceType.invalid' | translate}}
+
+ + +
+ + +
{{'Please enter the title' | translate}}
+
+ + +

{{'When you upload a new file, it will appear in the list of evidence.' | translate}}

+ + +
+
{{'You need to upload evidence' | translate}}
+
+
+ + +
{{'observation.geoPhotoUploaded.note' | translate}}
+
{{'The file should weight less than 20MB' | translate}}
+
{{'The file is not valid' | translate}}
+
+ + +
+ +
+
+
+
+
+
+
+
+
@@ -393,7 +512,7 @@

{{'Get coordinates from a photo' | translate}}

- +
{{'The file should weight less than 20MB' | translate}}
@@ -420,92 +539,7 @@

{{'Get coordinates from a photo' | translate}}

-
- {{'Evidence' | translate}} -
-
- -
- - -
{{'observation.evidenceType.invalid' | translate}}
-

{{'observation.evidenceType.note' | translate}}

-
- - -
- - -
{{'observation.evidenceDetails.invalid' | translate}}
-

{{ 'observation.evidenceDetails.note' | translate }}

-
-
- -
-
-

{{'List of evidence' | translate}}

-
    -
  • -
    -
    {{document.name}}
    -
    {{'Already uploaded' | translate}}
    -
    {{'To upload' | translate}}
    -
    -
  • -
-

{{'No evidence' | translate}}

-
-
- -

{{'List of evidence' | translate}}

-
    -
  • -
    -
    {{document.name}}
    -
    {{'Already uploaded' | translate}}
    -
    {{'To upload' | translate}}
    -
    -
    - -
    -
  • -
-

{{'No evidence' | translate}}

-
-
- -
- - -
{{'Please enter the title' | translate}}
-
- - -

{{'When you upload a new file, it will appear in the list of evidence.' | translate}}

- - -
-
{{'You need to upload evidence' | translate}}
-
-
- - -
{{'observation.geoPhotoUploaded.note' | translate}}
-
{{'The file should weight less than 20MB' | translate}}
-
{{'The file is not valid' | translate}}
-
- - -
- -
-
-
-
-
+
@@ -832,92 +866,7 @@

{{'Upload report' | translate}}

-
- {{'Evidence' | translate}} -
-
- -
- - -
{{'observation.evidenceType.invalid' | translate}}
-

{{'observation.evidenceType.note' | translate}}

-
- - -
- - -
{{'observation.evidenceDetails.invalid' | translate}}
-

{{ 'observation.evidenceDetails.note' | translate }}

-
-
- -
-
-

{{'List of evidence' | translate}}

-
    -
  • -
    -
    {{document.name}}
    -
    {{'Already uploaded' | translate}}
    -
    {{'To upload' | translate}}
    -
    -
  • -
-

{{'No evidence' | translate}}

-
-
- -

{{'List of evidence' | translate}}

-
    -
  • -
    -
    {{document.name}}
    -
    {{'Already uploaded' | translate}}
    -
    {{'To upload' | translate}}
    -
    -
    - -
    -
  • -
-

{{'No evidence' | translate}}

-
-
- -
- - -
{{'Please enter the title' | translate}}
-
- - -

{{'When you upload a new file, it will appear in the list of evidence.' | translate}}

- - -
-
{{'You need to upload evidence' | translate}}
-
-
- - -
{{'observation.geoPhotoUploaded.note' | translate}}
-
{{'The file should weight less than 20MB' | translate}}
-
{{'The file is not valid' | translate}}
-
- - -
- -
-
-
-
-
+
diff --git a/src/app/pages/observations/observation-detail.component.scss b/src/app/pages/observations/observation-detail.component.scss index fce2c80a..90c0a73b 100644 --- a/src/app/pages/observations/observation-detail.component.scss +++ b/src/app/pages/observations/observation-detail.component.scss @@ -54,6 +54,8 @@ padding: 0; border-top: 1px solid $lighter-grey; border-bottom: 1px solid $lighter-grey; + max-height: 400px; + overflow: auto; > li { display: flex; diff --git a/src/app/pages/observations/observation-detail.component.ts b/src/app/pages/observations/observation-detail.component.ts index 9b7d4ce4..5b936744 100644 --- a/src/app/pages/observations/observation-detail.component.ts +++ b/src/app/pages/observations/observation-detail.component.ts @@ -1,6 +1,7 @@ import { TranslateService } from '@ngx-translate/core'; -import * as cloneDeep from 'lodash/cloneDeep'; -import * as orderBy from 'lodash/orderBy'; +import cloneDeep from 'lodash/cloneDeep'; +import orderBy from 'lodash/orderBy'; +import uniqBy from 'lodash/uniqBy'; import * as EXIF from 'exif-js'; import proj4 from 'proj4'; import { Law } from 'app/models/law.model'; @@ -75,13 +76,27 @@ export class ObservationDetailComponent implements OnDestroy { documents: ObservationDocument[] = []; // Sorted by name initially documentsToDelete: ObservationDocument[] = []; // Existing document to delete documentsToUpload: ObservationDocument[] = []; // New document to upload + reportDocuments: ObservationDocument[] = []; // Documents of the selected report evidence: ObservationDocument = this.datastoreService.createRecord(ObservationDocument, {}); + evidenceCategories = [ + 'No evidence', 'Upload new or select existing', 'Evidence presented in the report' + ]; + evidenceCategoryOptions: any = {}; // Object of options for evidence type selection evidenceTypes = [ // Possible types of an evidence 'Government Documents', 'Company Documents', 'Photos', - 'Testimony from local communities', 'Other', 'Evidence presented in the report' + 'Testimony from local communities', 'Other' ]; evidenceTypeOptions: any = {}; // Object of options for evidence type selection + evidenceTabs = [{ + id: 'existing', + name: 'Select from report evidences' + }, { + id: 'new', + name: 'Add a new evidence' + }] + currentEvidenceTab = this.evidenceTabs[0]; + coordinatesFormats = [ 'Decimal', 'Degrees and decimal minutes', @@ -192,6 +207,7 @@ export class ObservationDetailComponent implements OnDestroy { _country: Country = null; _details: string; _evidenceType: string = null; + _evidenceCategory: string = null; _evidenceOnReport: string; _severity: Severity = null; _subcategory: Subcategory = null; @@ -563,6 +579,17 @@ export class ObservationDetailComponent implements OnDestroy { } } + get evidenceCategory() { + return this.observation ? this.observation['evidence-category'] : this._evidenceCategory + } + set evidenceCategory(evidenceCategory) { + if (this.observation) { + this.observation['evidence-category'] = evidenceCategory; + } else { + this._evidenceCategory = evidenceCategory; + } + } + get evidenceOnReport() { return this.observation ? this.observation['evidence-on-report'] : this._evidenceOnReport } set evidenceOnReport(evidenceOnReport) { if (this.observation) { @@ -648,6 +675,10 @@ export class ObservationDetailComponent implements OnDestroy { ); } + get isEvidenceOnReport() { + return this._evidenceCategory === 'Evidence presented in the report'; + } + get mapLayers(): any[] { const layers = []; @@ -675,6 +706,17 @@ export class ObservationDetailComponent implements OnDestroy { get reportChoice() { return this.observation ? (this.observation['observation-report'] || null) : this._reportChoice; } set reportChoice(reportChoice) { + if (reportChoice && reportChoice.id) { + this.observationReportsService.getById(reportChoice.id, { include: 'observations,observations.observation-documents' }).then((report) => { + if (report.observations && report.observations.length > 0) { + this.reportDocuments = orderBy( + uniqBy(report.observations.flatMap(o => o['observation-documents']), 'id'), + [(d) => d.name.toLowerCase()] + ); + } + }); + } + if (this.observation) { this.observation['observation-report'] = reportChoice; } else { @@ -759,12 +801,14 @@ export class ObservationDetailComponent implements OnDestroy { private translateService: TranslateService ) { this.updateTranslatedOptions(this.evidenceTypes, 'evidenceType'); + this.updateTranslatedOptions(this.evidenceCategories, 'evidenceCategory'); this.updateTranslatedOptions(this.coordinatesFormats, 'coordinatesFormat'); this.updateMultiSelectTexts(); this.translateService.onLangChange.subscribe(() => { this.updateTranslatedOptions(this.evidenceTypes, 'evidenceType'); + this.updateTranslatedOptions(this.evidenceCategories, 'evidenceCategory'); this.updateTranslatedOptions(this.coordinatesFormats, 'coordinatesFormat'); }); @@ -928,6 +972,11 @@ export class ObservationDetailComponent implements OnDestroy { return type === 'Evidence presented in the report'; } + public onChangeEvidenceCategory(previousType: string, type: string, typeElement: HTMLSelectElement): void { + console.log('onChangeEvidenceCategory', previousType, type, typeElement); + this.evidenceCategory = type; + } + public onChangeEvidenceType(previousType: string, type: string, typeElement: HTMLSelectElement): void { if (!this.isEvidenceTypeOnReport(type) || !this.documents.length) { this.evidenceType = type; @@ -1295,6 +1344,12 @@ export class ObservationDetailComponent implements OnDestroy { this.georeferencedPhoto.isUsed = false; } + onClickAddReportDocument(document: ObservationDocument) { + let documentIndex = this.reportDocuments.findIndex(d => d === document); + this.reportDocuments.splice(documentIndex, 1); + document.fromReportLibrary = true; + this.documents.push(document); + } /** * Event handler executed when the user clicks the delete button * of an evidence @@ -1308,9 +1363,15 @@ export class ObservationDetailComponent implements OnDestroy { this.documents.splice(documentIndex, 1); if (document.id) { - // If the document is an existing one, we add it - // to the list of documents to delete - this.documentsToDelete.push(cloneDeep(document)); + + if (document.fromReportLibrary) { + this.reportDocuments.push(document); + this.reportDocuments = orderBy(this.reportDocuments, [(d) => d.name.toLowerCase()]); + } else { + // If the document is an existing one, we add it + // to the list of documents to delete + this.documentsToDelete.push(cloneDeep(document)); + } } else { // We get the index of the document within this.documentsToUpload documentIndex = this.documentsToUpload.findIndex((d) => { @@ -1334,6 +1395,10 @@ export class ObservationDetailComponent implements OnDestroy { this.needsRevisionState = 'explain'; } + onChangeEvidenceTab(tab) { + this.currentEvidenceTab = tab; + } + /** * Return whether the form is disabled * @returns {boolean} diff --git a/src/app/shared/tabs/tabs.component.ts b/src/app/shared/tabs/tabs.component.ts index 4ac93a65..3cfa7449 100644 --- a/src/app/shared/tabs/tabs.component.ts +++ b/src/app/shared/tabs/tabs.component.ts @@ -1,76 +1,76 @@ -// import { Component, Input, HostListener, Output, EventEmitter } from '@angular/core'; +import { Component, Input, HostListener, Output, EventEmitter } from '@angular/core'; -// export interface Tab { -// id: string; -// name: string; -// } +export interface Tab { + id: string; + name: string; +} -// @Component({ -// selector: 'otp-tabs', -// templateUrl: './tabs.component.html', -// styleUrls: ['./tabs.component.scss'] -// }) -// export class TabsComponent { +@Component({ + selector: 'otp-tabs', + templateUrl: './tabs.component.html', + styleUrls: ['./tabs.component.scss'] +}) +export class TabsComponent { -// @Input() tabs: Tab[] = []; -// @Input() currentTab = 0; // Index of the current Tab -// @Output() change: EventEmitter = new EventEmitter(); + @Input() tabs: Tab[] = []; + @Input() currentTab = 0; // Index of the current Tab + @Output() change: EventEmitter = new EventEmitter(); -// get tab () { -// return this.tabs[this.currentTab]; -// } + get tab () { + return this.tabs[this.currentTab]; + } -// set tab (tab: Tab) { -// this.currentTab = this.tabs.findIndex(function (t) { -// return t === tab; -// }); + set tab (tab: Tab) { + this.currentTab = this.tabs.findIndex(function (t) { + return t === tab; + }); -// this.change.emit(this.tab); -// } + this.change.emit(this.tab); + } -// /** -// * Event handler executed when the user clicks a tab -// * @param {TabItem} tab clicked tab -// */ -// onClickTab (tab: Tab): void { -// this.tab = tab; -// } + /** + * Event handler executed when the user clicks a tab + * @param {TabItem} tab clicked tab + */ + onClickTab (tab: Tab): void { + this.tab = tab; + } -// /** -// * Event handler executed when the user presses a key while the focus -// * is on the component -// * @param {KeyboardEvent} e event -// */ -// @HostListener('keydown',['$event']) -// onKeydown (e: KeyboardEvent) { -// switch (e.keyCode) { -// case 37: // left arrow -// case 38: // top arrow -// let previousTabIndex = (this.currentTab - 1) % this.tabs.length; -// if (previousTabIndex < 0) { -// previousTabIndex = this.tabs.length - 1; -// } -// this.tab = this.getTab(previousTabIndex); -// break; + /** + * Event handler executed when the user presses a key while the focus + * is on the component + * @param {KeyboardEvent} e event + */ + @HostListener('keydown',['$event']) + onKeydown (e: KeyboardEvent) { + switch (e.keyCode) { + case 37: // left arrow + case 38: // top arrow + let previousTabIndex = (this.currentTab - 1) % this.tabs.length; + if (previousTabIndex < 0) { + previousTabIndex = this.tabs.length - 1; + } + this.tab = this.getTab(previousTabIndex); + break; -// case 39: // right arrow -// case 40: // down arrow -// const nextTabIndex = (this.currentTab + 1) % this.tabs.length; -// this.tab = this.getTab(nextTabIndex); -// break; + case 39: // right arrow + case 40: // down arrow + const nextTabIndex = (this.currentTab + 1) % this.tabs.length; + this.tab = this.getTab(nextTabIndex); + break; -// default: -// } -// } + default: + } + } -// /** -// * Return the tab associated to the index -// * @private -// * @param {number} tabIndex index of the tab -// * @returns {Tab} tab -// */ -// private getTab (tabIndex: number): Tab { -// return this.tabs[tabIndex]; -// } + /** + * Return the tab associated to the index + * @private + * @param {number} tabIndex index of the tab + * @returns {Tab} tab + */ + private getTab (tabIndex: number): Tab { + return this.tabs[tabIndex]; + } -// } +} diff --git a/src/styles.scss b/src/styles.scss index f06f4dbd..f396c2db 100644 --- a/src/styles.scss +++ b/src/styles.scss @@ -36,7 +36,7 @@ // @include foundation-reveal; // @include foundation-switch; @include foundation-table; -// @include foundation-tabs; +@include foundation-tabs; // @include foundation-thumbnail; // @include foundation-title-bar; // @include foundation-tooltip;