Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PRC-748: return PRC results even if no BCGW results #289

Merged
merged 1 commit into from
Nov 30, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<div class="top-container">
<div class="top-container" *ngIf="application">
<div class="container">
<div class="title-container">
<div class="title-container__title">
Expand Down Expand Up @@ -31,7 +31,7 @@ <h1><span class="text-muted">Crown land File: {{application['clFile']}} &nbsp;&r
</div>
</div>

<div class="bottom-container">
<div class="bottom-container" *ngIf="application">
<div class="container">
<form class="mb-3" #applicationForm="ngForm">
<fieldset>
Expand Down
48 changes: 26 additions & 22 deletions src/app/search/search.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,10 @@ <h2 *ngIf="ranSearch && !searching && count == 0">
<h2 *ngIf="ranSearch && !searching && count > 0">
<strong>{{count}} result<span *ngIf="count != 1">s</span> found for "{{keywords.length > 0 ? keywords.join(', ') : 'unknown'}}"</strong>
</h2>
<table class="table" *ngIf="!searching && count > 0">
<!-- <ngb-alert type="danger" [dismissible]="false" *ngIf="ranSearch && !searching && searchService.isBcgwError">
Cannot connect to BCGW at the moment... Try searching for new applications later.
</ngb-alert> -->
<table class="table" *ngIf="count > 0">
<thead>
<tr>
<th class="cl-file" name="Crowns land File Number">CL File</th>
Expand All @@ -37,63 +40,64 @@ <h2 *ngIf="ranSearch && !searching && count > 0">
<th class="actions"></th>
</tr>
</thead>
<ng-template ngFor let-item [ngForOf]="groupByResults">
<tr class="app-details" *ngIf="item.loaded">
<ng-template ngFor let-application [ngForOf]="applications">
<tr class="app-details">
<td class="cl-file">
<span class="cl-arrow">{{item.properties.CROWN_LANDS_FILE}}</span>
<span class="cl-arrow">{{application['clFile']}}</span>
</td>
<td class="disp">
<strong>{{item.properties.DISPOSITION_TRANSACTION_SID}}</strong>
<strong>{{application.tantalisID}}</strong>
</td>
<td [innerHTML]="item.app?.client ? (item.app.client | titlecase) : '<em>Applicant Not Selected</em>'"></td>
<td [innerHTML]="application.client ? (application.client | titlecase) : '<em>Applicant Not Selected</em>'"></td>
<td>
{{item.properties.TENURE_PURPOSE | titlecase}} / {{item.properties.TENURE_SUBPURPOSE | titlecase}}
{{application.purpose | titlecase}} / {{application.subpurpose | titlecase}}
</td>
<td>
{{item.prcStatus || 'Unknown'}}
{{application.appStatus || 'Unknown'}}
</td>
<td class="actions">
<button class="btn btn-sm btn-primary" type="button" *ngIf="!item.app" (click)="importProject(item)">
<button class="btn btn-sm btn-primary" type="button" *ngIf="!application['isCreated']" (click)="onImport(application)">
<i class="material-icons">add</i>
Create
</button>
<div ngbDropdown placement="bottom-right" *ngIf="item.app">
<div ngbDropdown placement="bottom-right" *ngIf="application['isCreated']">
<button ngbDropdownToggle class="btn btn-sm btn-outline-primary" type="button">
Actions
</button>
<div ngbDropdownMenu>
<button class="dropdown-item" type="button" [routerLink]="['/a', item.app._id]">
<button class="dropdown-item" type="button" [routerLink]="['/a', application._id]">
<i class="material-icons">insert_drive_file</i>
View Application
</button>
<button class="dropdown-item" type="button" [routerLink]="['/a', item.app._id, 'edit']">
<button class="dropdown-item" type="button" [routerLink]="['/a', application._id, 'edit']">
<i class="material-icons">edit</i>
Edit Details
</button>
<button class="dropdown-item" type="button" [routerLink]="['/comments', item.app._id]">
<button class="dropdown-item" type="button" [routerLink]="['/comments', application._id]">
<i class="material-icons">mode_comment</i>
Review Comments
</button>
</div>
</div>
</td>
</tr>
<tr class="app-comment-details" *ngIf="item.loaded">
<tr class="app-comment-details" *ngIf="application['isCreated']">
<td colspan="6">
<span class="comment-info">
<span *ngIf="item.app && item.app['numComments'] > 0">
<span *ngIf="application['numComments'] > 0">
<strong>
<a [routerLink]="['/comments', item.app._id]">{{item.app['numComments']}} {{item.app['numComments'] === 1 ? 'comment' : 'comments'}}</a>
<a [routerLink]="['/comments', application._id]">{{application['numComments']}} {{application['numComments'] === 1 ? 'comment' : 'comments'}}</a>
</strong>
&nbsp;-&nbsp;
</span>
<span *ngIf="item.app?.cpStatus">
{{item.app.cpStatus}}
<span>
{{application.cpStatus || 'Unknown'}}
</span>
<span *ngIf="item.app?.currentPeriod">
&nbsp;-&nbsp; {{item.app.currentPeriod.startDate | date:'longDate'}} to {{item.app.currentPeriod.endDate | date:'longDate'}}
<span *ngIf="item.app.currentPeriod['daysRemaining']">
&nbsp;({{item.app.currentPeriod['daysRemaining'] + (item.app.currentPeriod['daysRemaining'] === 1 ? ' day ' : ' days ') + 'remaining'}})
<span *ngIf="application.currentPeriod">
&nbsp;-&nbsp;
{{application.currentPeriod.startDate | date:'longDate'}} to {{application.currentPeriod.endDate | date:'longDate'}}
<span *ngIf="application.currentPeriod['daysRemaining']">
&nbsp;({{application.currentPeriod['daysRemaining'] + (application.currentPeriod['daysRemaining'] === 1 ? ' day ' : ' days ') + 'remaining'}})
</span>
</span>
</span>
Expand Down
4 changes: 2 additions & 2 deletions src/app/search/search.component.scss
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ $sr-table-row-bg: #f7f8fa;
td {
padding-top: 1.25rem;
padding-right: 1rem;
padding-bottom: 0;
padding-bottom: 1.25rem;
padding-left: 1rem;
border: none;
background: $sr-table-row-bg;
Expand Down Expand Up @@ -128,7 +128,7 @@ $sr-table-row-bg: #f7f8fa;

.app-comment-details {
td {
padding-top: 0.75rem;
padding-top: 0;
padding-bottom: 1.25rem;

a {
Expand Down
96 changes: 33 additions & 63 deletions src/app/search/search.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ import { Subject } from 'rxjs/Subject';
import 'rxjs/add/operator/takeUntil';
import * as _ from 'lodash';

import { SearchTerms } from 'app/models/search';
import { ApplicationService } from 'app/services/application.service';
import { SearchService } from 'app/services/search.service';
import { SearchTerms } from 'app/models/search';
import { Application } from 'app/models/application';

@Component({
selector: 'app-search',
Expand All @@ -20,15 +20,14 @@ export class SearchComponent implements OnInit, OnDestroy {
public searching = false;
public ranSearch = false;
public keywords: Array<string> = [];
public groupByResults: Array<any> = [];
public applications: Array<Application> = [];
public count = 0; // for template
private snackBarRef: MatSnackBarRef<SimpleSnackBar> = null;
private ngUnsubscribe = new Subject<boolean>();

constructor(
public snackBar: MatSnackBar,
private applicationService: ApplicationService,
private searchService: SearchService,
public searchService: SearchService, // also used in template
private router: Router,
private route: ActivatedRoute
) { }
Expand Down Expand Up @@ -59,56 +58,28 @@ export class SearchComponent implements OnInit, OnDestroy {

private doSearch() {
this.searching = true;
this.count = 0;
this.keywords = this.terms.keywords && _.uniq(_.compact(this.terms.keywords.split(' '))) || []; // safety checks
this.groupByResults.length = 0; // empty the list
this.applications.length = 0; // empty the list

this.searchService.getByClidDtid(this.keywords)
this.searchService.getAppsByClidDtid(this.keywords)
.takeUntil(this.ngUnsubscribe)
.subscribe(
search => {
// console.log('search =', search);
if (search && search.totalFeatures > 0 && search.features && search.features.length > 0) {
const groupedFeatures = _.groupBy(search.features, 'properties.DISPOSITION_TRANSACTION_SID');
const self = this; // for closure below
_.each(groupedFeatures, function (value: any, key: string) {
// display PRC status from properties
value[0].prcStatus = self.applicationService.getStatusString(self.applicationService.getStatusCode(value[0].properties.TENURE_STATUS));
// ensure result is not already in list
if (!_.find(self.groupByResults, result => { return result.properties.DISPOSITION_TRANSACTION_SID === +key; })) {
// if app is in PRC, query application data to update UI
if (_.includes(search.sidsFound, key)) {
value[0].loaded = false;
self.applicationService.getByTantalisID(+key, { getCurrentPeriod: true })
.takeUntil(self.ngUnsubscribe)
.subscribe(
application => {
value[0].loaded = true;
if (application) {
// display PRC status from application
value[0].prcStatus = application.appStatus;
value[0].app = application;
}
},
error => {
console.log('error =', error);
self.snackBarRef = self.snackBar.open('Error retrieving application ...', null, { duration: 3000 });
}
);
} else {
value[0].loaded = true;
}
self.groupByResults.push(value[0]);
}
});
}
applications => {
applications.forEach(application => {
// add if not already in list
if (!_.find(this.applications, app => { return app.tantalisID === application.tantalisID; })) {
this.applications.push(application);
}
});
this.count = this.applications.length;
},
error => {
console.log('error =', error);

// update variables on error
this.searching = false;
this.ranSearch = true;
this.count = 0;

this.snackBarRef = this.snackBar.open('Error searching applications ...', 'RETRY');
this.snackBarRef.onAction().subscribe(() => this.onSubmit());
Expand All @@ -117,7 +88,6 @@ export class SearchComponent implements OnInit, OnDestroy {
// update variables on completion
this.searching = false;
this.ranSearch = true;
this.count = this.groupByResults.length;
});
}

Expand All @@ -127,36 +97,36 @@ export class SearchComponent implements OnInit, OnDestroy {
if (this.snackBarRef) { this.snackBarRef.dismiss(); }

// NOTE: Angular Router doesn't reload page on same URL
// ref: https://stackoverflow.com/questions/40983055/how-to-reload-the-current-route-with-the-angular-2-router
// REF: https://stackoverflow.com/questions/40983055/how-to-reload-the-current-route-with-the-angular-2-router
// WORKAROUND: add timestamp to force URL to be different than last time
const params = this.terms.getParams();
params['now'] = Date.now();
params['ms'] = new Date().getMilliseconds();

// console.log('params =', params);
this.router.navigate(['search', params]);
}

public importProject(item: any) {
if (item.properties) {
// save application properties from search results
public onImport(application: Application) {
if (application) {
// save application data from search results
const params = {
// initial cached data
purpose: item.properties.TENURE_PURPOSE,
subpurpose: item.properties.TENURE_SUBPURPOSE,
type: item.properties.TENURE_TYPE,
subtype: item.properties.TENURE_SUBTYPE,
status: item.properties.TENURE_STATUS,
tenureStage: item.properties.TENURE_STAGE,
location: item.properties.TENURE_LOCATION,
businessUnit: item.properties.RESPONSIBLE_BUSINESS_UNIT,
cl_file: item.properties.CROWN_LANDS_FILE,
tantalisID: item.properties.DISPOSITION_TRANSACTION_SID,
legalDescription: item.properties.TENURE_LEGAL_DESCRIPTION
// initial data
purpose: application.purpose,
subpurpose: application.subpurpose,
type: application.type,
subtype: application.subtype,
status: application.status,
tenureStage: application.tenureStage,
location: application.location,
businessUnit: application.businessUnit,
cl_file: application.cl_file,
tantalisID: application.tantalisID,
legalDescription: application.legalDescription
};
// go to add-edit page
this.router.navigate(['/a', 0, 'edit'], { queryParams: params });
} else {
console.log('error, invalid item =', item);
console.log('error, invalid application =', application);
this.snackBarRef = this.snackBar.open('Error creating application ...', null, { duration: 3000 });
}
}
Expand Down
35 changes: 31 additions & 4 deletions src/app/services/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ export class ApiService {
return this.http.post<LocalLoginResponse>(`${this.pathAPI}/login/token`, { username: username, password: password })
.map(res => {
// login successful if there's a jwt token in the response
if (res.accessToken) {
if (res && res.accessToken) {
this.token = res.accessToken;

// store username and jwt token in local storage to keep user logged in between page refreshes
Expand Down Expand Up @@ -199,6 +199,33 @@ export class ApiService {
);
}

// NB: returns array
getApplicationsByCrownLandID(clid: string): Observable<Application[]> {
const fields = [
'agency',
'areaHectares',
'businessUnit',
'centroid',
'cl_file',
'client',
'description',
'internal',
'legalDescription',
'location',
'name',
'publishDate',
'purpose',
'status',
'subpurpose',
'subtype',
'tantalisID',
'tenureStage',
'type'
];
const queryString = `application?isDeleted=false&cl_file=${clid}&fields=${this.buildValues(fields)}`;
return this.http.get<Application[]>(`${this.pathAPI}/${queryString}`, {});
}

// NB: returns array with 1 element
getApplicationByTantalisId(tantalisId: number): Observable<Application[]> {
const fields = [
Expand Down Expand Up @@ -598,17 +625,17 @@ export class ApiService {
//
// Searching
//
getAppsByCLID(clid: string): Observable<SearchResults> {
searchAppsByCLID(clid: string): Observable<SearchResults> {
const queryString = `public/search/bcgw/crownLandsId/${clid}`;
return this.http.get<SearchResults>(`${this.pathAPI}/${queryString}`, {});
}

getAppsByDTID(dtid: number): Observable<SearchResults> {
searchAppsByDTID(dtid: number): Observable<SearchResults> {
const queryString = `public/search/bcgw/dispositionTransactionId/${dtid}`;
return this.http.get<SearchResults>(`${this.pathAPI}/${queryString}`, {});
}

getClientsByDTID(dtid: number): Observable<Client[]> {
searchClientsByDTID(dtid: number): Observable<Client[]> {
const queryString = `public/search/bcgw/getClientsInfoByDispositionId/${dtid}`;
return this.http.get<Client[]>(`${this.pathAPI}/${queryString}`, {});
}
Expand Down
Loading