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/draft_observation.interface.ts b/src/app/models/draft_observation.interface.ts index bb641a46..1c9707a0 100644 --- a/src/app/models/draft_observation.interface.ts +++ b/src/app/models/draft_observation.interface.ts @@ -23,6 +23,7 @@ export interface DraftObservation { // New evidence evidenceTitle?: string; evidenceAttachment?: string; // Base64 + evidenceDocumentType?: string; isPhysicalPlace?: boolean; litigationStatus?: string; diff --git a/src/app/models/observation_document.ts b/src/app/models/observation_document.ts index bc61e779..4b99654d 100644 --- a/src/app/models/observation_document.ts +++ b/src/app/models/observation_document.ts @@ -1,5 +1,5 @@ import { Observation } from 'app/models/observation.model'; -import { User } from 'app/models/user.model'; +import { ObservationReport } from 'app/models/observation_report'; import { JsonApiModel, JsonApiModelConfig, Attribute, BelongsTo } from 'angular2-jsonapi'; @JsonApiModelConfig({ @@ -8,7 +8,9 @@ import { JsonApiModel, JsonApiModelConfig, Attribute, BelongsTo } from 'angular2 export class ObservationDocument extends JsonApiModel { @Attribute() name: string; + @Attribute() 'document-type': string; @Attribute() attachment: string|{ url: string }; @BelongsTo() observation: Observation; + @BelongsTo() 'observation-report'?: ObservationReport; } 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..596aa189 100644 --- a/src/app/pages/observations/observation-detail.component.html +++ b/src/app/pages/observations/observation-detail.component.html @@ -85,6 +85,124 @@

{{'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}}
    +
    {{document['document-type']}} | {{ (document.id ? 'Already uploaded' : 'To upload') | translate}}
    +
    +
    + +
    +
  • +
+

{{'No evidence' | translate}}

+
+
+ + + + +
    +
  • +
    +
    {{document.name}}
    +
    {{document['document-type']}}
    +
    +
    + +
    +
  • +
+

{{'No evidence' | translate}}

+
+ + +
+ +
+ + +
{{'Please select a document type' | 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 +511,7 @@

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

- +
{{'The file should weight less than 20MB' | translate}}
@@ -420,92 +538,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 +865,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..8e701c03 100644 --- a/src/app/pages/observations/observation-detail.component.ts +++ b/src/app/pages/observations/observation-detail.component.ts @@ -1,6 +1,8 @@ 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 flatten from 'lodash/flatten'; import * as EXIF from 'exif-js'; import proj4 from 'proj4'; import { Law } from 'app/models/law.model'; @@ -75,12 +77,26 @@ 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, {}); - evidenceTypes = [ // Possible types of an evidence - 'Government Documents', 'Company Documents', 'Photos', - 'Testimony from local communities', 'Other', 'Evidence presented in the report' + evidenceTypes = [ + 'No evidence', 'Uploaded documents', 'Evidence presented in the report' ]; evidenceTypeOptions: any = {}; // Object of options for evidence type selection + documentTypes = [ // Possible types of an observation document/evidence + 'Government Documents', 'Company Documents', 'Photos', + 'Testimony from local communities', 'Other' + ]; + documentTypeOptions: any = {}; // Object of options for document type selection + + evidenceTabs = [{ + id: 'existing', + name: 'Select from report evidences' + }, { + id: 'new', + name: 'Upload a new evidence' + }] + currentEvidenceTab = this.evidenceTabs[0]; coordinatesFormats = [ 'Decimal', @@ -648,6 +664,10 @@ export class ObservationDetailComponent implements OnDestroy { ); } + get isEvidenceOnReport() { + return this._evidenceType === 'Evidence presented in the report'; + } + get mapLayers(): any[] { const layers = []; @@ -675,6 +695,24 @@ 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.observationDocumentsService.getAll({ filter: { 'observation-report-id': reportChoice.id } }).then((documents) => { + this.reportDocuments = orderBy( + uniqBy(documents, 'id'), + [(d) => d.name.toLowerCase()] + ); + }); + + // this.observationReportsService.getById(reportChoice.id, { include: 'observations,observations.observation-documents' }).then((report) => { + // if (report.observations && report.observations.length > 0) { + // this.reportDocuments = orderBy( + // uniqBy(flatten(report.observations.map(o => o['observation-documents'])), 'id'), + // [(d) => d.name.toLowerCase()] + // ); + // } + // }); + } + if (this.observation) { this.observation['observation-report'] = reportChoice; } else { @@ -759,12 +797,14 @@ export class ObservationDetailComponent implements OnDestroy { private translateService: TranslateService ) { this.updateTranslatedOptions(this.evidenceTypes, 'evidenceType'); + this.updateTranslatedOptions(this.documentTypes, 'documentType'); this.updateTranslatedOptions(this.coordinatesFormats, 'coordinatesFormat'); this.updateMultiSelectTexts(); this.translateService.onLangChange.subscribe(() => { this.updateTranslatedOptions(this.evidenceTypes, 'evidenceType'); + this.updateTranslatedOptions(this.documentTypes, 'documentType'); this.updateTranslatedOptions(this.coordinatesFormats, 'coordinatesFormat'); }); @@ -873,6 +913,7 @@ export class ObservationDetailComponent implements OnDestroy { // If we were going to add an evidence this.evidence.name = this.draft.evidenceTitle; this.evidence.attachment = this.draft.evidenceAttachment; + this.evidence['document-type'] = this.draft.evidenceDocumentType; // if we were going to upload a new report this.report.title = this.draft.reportTitle; this.reportAttachment = this.draft.reportAttachment; @@ -929,6 +970,7 @@ export class ObservationDetailComponent implements OnDestroy { } public onChangeEvidenceType(previousType: string, type: string, typeElement: HTMLSelectElement): void { + console.log('on change evidence type', previousType, type, typeElement); if (!this.isEvidenceTypeOnReport(type) || !this.documents.length) { this.evidenceType = type; } @@ -939,6 +981,7 @@ export class ObservationDetailComponent implements OnDestroy { if (confirm(phrase)) { this.evidenceType = type; this.evidence.name = null; + this.evidence['document-type'] = null; this.evidence.attachment = null; this.georeferencedPhoto.isUsed = false; this.evidenceInput.nativeElement.value = ''; @@ -946,11 +989,14 @@ export class ObservationDetailComponent implements OnDestroy { typeElement.selectedIndex = this.evidenceTypes.indexOf(previousType) + 1; } }); - } else { - this.evidenceOnReport = null; } } + // public onChangeDocumentType(type: string): void { + // console.log('onChangeEvidenceCategory', previousType, type, typeElement); + // this.evidenceCategory = type; + // } + private saveAsDraftObservation(): void { const draftModel: DraftObservation = { observationType: this.type, @@ -966,6 +1012,7 @@ export class ObservationDetailComponent implements OnDestroy { evidenceOnReport: this.evidenceOnReport, evidenceTitle: this.evidence.name, evidenceAttachment: this.evidence.attachment && String(this.evidence.attachment), + evidenceDocumentType: this.evidence['document-type'], documents: this.documents.map(document => ({ name: document.name, attachement: document.attachment @@ -1277,9 +1324,12 @@ export class ObservationDetailComponent implements OnDestroy { * Event handler executed when the user clicks the "Add evidence" button */ onClickAddEvidence() { + console.log('evidence name', this.evidence.name); + console.log('evidence type', this.evidence['document-type']); + const evidence = this.datastoreService.createRecord(ObservationDocument, { name: this.evidence.name, - type: this.evidence.type, + ['document-type']: this.evidence['document-type'], attachment: this.evidence.attachment }); @@ -1295,6 +1345,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 +1364,13 @@ 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)); + 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 +1394,10 @@ export class ObservationDetailComponent implements OnDestroy { this.needsRevisionState = 'explain'; } + onChangeEvidenceTab(tab) { + this.currentEvidenceTab = tab; + } + /** * Return whether the form is disabled * @returns {boolean} @@ -1548,18 +1612,20 @@ export class ObservationDetailComponent implements OnDestroy { */ updateDocuments(observation: Observation): Promise<{}> { // We create an array of the documents to delete - const deletePromises = this.documentsToDelete.map((d) => { - return this.datastoreService.deleteRecord(ObservationDocument, d.id) - .toPromise(); - }); + // const deletePromises = this.documentsToDelete.map((d) => { + // return this.datastoreService.deleteRecord(ObservationDocument, d.id) + // .toPromise(); + // }); // We create an array of the documents to upload const uploadPromises = !this.isEvidenceTypeOnReport(this.evidenceType) ? this.documentsToUpload.map((d) => { d.observation = observation; // We link the document to the observation + d['observation-report'] = observation['observation-report']; // We link the document to the report return d.save().toPromise(); }) : []; - return Promise.all(deletePromises.concat(uploadPromises)); + // return Promise.all(deletePromises.concat(uploadPromises)); + return Promise.all(uploadPromises); } async onSubmitForReview() { 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/assets/locale/zu.json b/src/assets/locale/zu.json index 2cbf6509..7e9efe1c 100644 --- a/src/assets/locale/zu.json +++ b/src/assets/locale/zu.json @@ -398,5 +398,8 @@ "Add a new producer to the list": "Add a new producer to the list", "Please select an existing report or upload a new one": "Please select an existing report or upload a new one", "You will not be able to submit this observation to review with unknown operator selected": "You will not be able to submit this observation to review with unknown operator selected", - "fmus": "FMUs" + "fmus": "FMUs", + "Document Type": "Document Type", + "Select a document type": "Select a document type", + "Please select a document type": "Please select a document type" } 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;