Skip to content

Commit

Permalink
Search Function overwritten for Territories ... proof of concept, it …
Browse files Browse the repository at this point in the history
…works, but now we need also more types of searchable elements
  • Loading branch information
hydrogen2oxygen committed Oct 28, 2023
1 parent bab4dbf commit d21a0ad
Show file tree
Hide file tree
Showing 11 changed files with 844 additions and 348 deletions.
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,8 @@ The application is intended to be run *"offline"*, not as a server, because it l
- Send a Whatsapp message with a list of assigned territories to a preacher
- Download maps as KML file
- Print statistics and tables (PDF)

## Development Environment
1) Download XAMPP (or set up your own Apache/PHP/FakeSftp)
2) Set the port in Apache to 90, at least not 80 as FM (Listen 90)
3) Change Setting in FinalApproach accordingly (FTP, User, PW, etc)
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package de.pietro.lusso.territory.services;

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import de.pietro.lusso.territory.domain.*;
Expand Down Expand Up @@ -71,6 +72,7 @@ public void initService() throws Exception {

objectMapper = new ObjectMapper();
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
objectMapper.configure(JsonParser.Feature.AUTO_CLOSE_SOURCE, true);

congregationFolder.mkdirs();
new File("data/congregation/").mkdirs();
Expand Down
977 changes: 634 additions & 343 deletions server/src/main/ui2/package-lock.json

Large diffs are not rendered by default.

7 changes: 7 additions & 0 deletions server/src/main/ui2/src/app/components/app/app.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,10 @@ <h1 class="badge bg-danger">Server is offline!</h1>
<p>You need to restart your server application. Click on start.bat</p>
<img src="assets/startServer.png">
</div>

<div *ngIf="showSearch" class="content centeredSearch">
<div>
<input #centeredSearch [formControl]="searchField" class="form-control" (keyup)="search()" />
</div>
<div style="margin-top: 0.2rem" *ngFor="let s of searchResult?.searchResults"><button class="btn btn-sm btn-success" (click)="selectSearchResult(s)">{{s.searchType}} | {{s.readableText}}</button></div>
</div>
8 changes: 8 additions & 0 deletions server/src/main/ui2/src/app/components/app/app.component.scss
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,11 @@
cursor: pointer;
}

.centeredSearch {
position: fixed;
top: 50%!important;
left: 50%!important;
width: 25%!important;
margin: auto;
transform: translate(-50%, -50%);
}
55 changes: 53 additions & 2 deletions server/src/main/ui2/src/app/components/app/app.component.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
import {Component} from '@angular/core';
import {Component, ElementRef, HostListener, ViewChild} from '@angular/core';
import {Link} from "../../domains/Link";
import {ActivatedRoute, Router} from "@angular/router";
import {CongregationService} from "../../services/congregation.service";
import {Version} from "../../domains/Congregation";
import {NavigationService} from "../../services/navigation.service";
import {SettingsService} from "../../services/settings.service";
import {FormControl} from "@angular/forms";
import {Subject} from "rxjs";
import {SharedService} from "../../services/shared.service";
import {Search, SearchResult} from "../../domains/Search";

@Component({
selector: 'app-root',
Expand All @@ -16,14 +20,20 @@ export class AppComponent {
links: Link[] = this.getLinks();
version: Version|null = null;
internetOffline:boolean = false;
showSearch:boolean = false;
searchField = new FormControl('');
searchResult:Search|undefined;

@ViewChild('centeredSearch') centeredSearchElement: ElementRef | undefined;

constructor(
private router: Router,

private route: ActivatedRoute,
private congregationService:CongregationService,
private navigationService:NavigationService,
private settingsService:SettingsService
private settingsService:SettingsService,
private sharedService:SharedService
) {
let currentUrl = window.location.href;
currentUrl = currentUrl.substring(currentUrl.lastIndexOf("/") + 1);
Expand All @@ -43,6 +53,41 @@ export class AppComponent {
}

})

this.sharedService.getSearchPerformed().subscribe(value => this.searchResult = value);
}

@HostListener('window:keydown', ['$event'])
onKeyDown(event: KeyboardEvent) {
if ((event.ctrlKey || event.metaKey) && event.key === 'f') {
// Prevent the default behavior (search) of Ctrl+F
event.preventDefault();
this.showSearch = true;
this.searchResult = undefined;

setTimeout(() => {
if (this.centeredSearchElement) {
this.centeredSearchElement.nativeElement.focus();
}
}, 200);

// Do something else or run your custom search logic here
} else if (this.showSearch && event.key === 'Enter') {
// TODO select the first occurrence
//this.searchResult
// Do something else or run your custom search logic here
} else if (this.showSearch && event.key === 'Escape') {
this.showSearch = false;
// Do something else or run your custom search logic here
}
}

search() {
if (this.searchField.value == null || this.searchField.value == "") {
this.showSearch = false;
return
}
this.sharedService.search.next(this.searchField.value)
}

checkVersion() {
Expand Down Expand Up @@ -85,4 +130,10 @@ export class AppComponent {
}
});
}

selectSearchResult(searchResult: SearchResult) {
this.sharedService.searchResultIdentified.next(searchResult);
this.searchField.setValue(null)
this.showSearch = false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,27 @@ <h4 class="collapseHeader" data-bs-toggle="collapse" href="#archived" role="butt
</div>
</div>


<div class="collapseContainer collapseArchived">
<h4 class="collapseHeader" data-bs-toggle="collapse" href="#sorted" role="button" aria-expanded="false" aria-controls="sorted">
ALL TERRITORIES SORTED
</h4>
<div class="collapse" id="sorted">
<button class="btn btn-sm btn-territory"
*ngFor="let t of territoriesSorted"
(click)="showTerritoryDetails(t)"
style="{{getTerritoryButtonColor(t.number)}}"
data-bs-toggle="modal" data-bs-target="#territoryDetailsModal">
{{t.number}} {{t.name}} {{t.date | date:'dd-MM-yy'}} <i-bs *ngIf="t.notes.length > 0" name="info-circle" style="color: #380000"></i-bs>
<i *ngIf="t.mapExist" class="bi bi-map-fill"></i>
<i *ngIf="t.uuid && !t.ftpExported" class="bi bi-cloud-slash" style="margin-left: 0.25rem"></i>
</button>
</div>
</div>




Territories {{
congregation.territoriesAssigned.length
+ congregation.territoriesNoContacts.length
Expand All @@ -112,7 +133,7 @@ <h3>Protocol:</h3>


<!-- Modal -->
<div class="modal fade" id="territoryDetailsModal" tabindex="-1" aria-labelledby="territoryDetailsModal" aria-hidden="true">
<div #territoryDetailsModal class="modal fade" id="territoryDetailsModal" tabindex="-1" aria-labelledby="territoryDetailsModal" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-body" *ngIf="territory">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
import {Component, OnInit} from '@angular/core';
import {Component, ElementRef, OnInit, ViewChild} from '@angular/core';
import {CongregationService} from "../../services/congregation.service";
import {Congregation, Preacher, RegistryEntry, Territory} from "../../domains/Congregation";
import {FormControl} from "@angular/forms";
import {ToastrService} from "ngx-toastr";
import {SharedService} from "../../services/shared.service";
import {Search, SearchResult} from "../../domains/Search";

declare var $:any;

@Component({
selector: 'app-territories',
Expand All @@ -11,9 +15,18 @@ import {ToastrService} from "ngx-toastr";
})
export class TerritoriesComponent implements OnInit {

private territoryDetailsModal: ElementRef | undefined;

@ViewChild('territoryDetailsModal') set content(content: ElementRef) {
if(content) { // initially setter gets called with undefined
this.territoryDetailsModal = content;
}
}

loading:boolean=false;
congregation: Congregation = new Congregation();
territory: Territory | null = null;
territoriesSorted: Territory[] = [];
preacherList: Preacher[] = [];
selectedPreacher: any = null;
keyword: string = "name";
Expand All @@ -24,17 +37,50 @@ export class TerritoriesComponent implements OnInit {

constructor(
private congregationService: CongregationService,
private toastr: ToastrService) {
private toastr: ToastrService,
private sharedService:SharedService
) {
}

ngOnInit(): void {
this.reloadCongregation();
this.sharedService.getSearchSubject().subscribe(value => this.search(value))
this.sharedService.getSearchResultIdentifiedSubject().subscribe(value => this.searchResultIdentified(value))
}

searchResultIdentified(searchResult:SearchResult) {
this.showTerritoryDetails(searchResult.data);
$('#territoryDetailsModal').modal('show');
}

search(text:string) {
console.log(`SEARCH FUNCTION IN TERRITORIES ... ${text}`)
if (text.length < 4) return
let territory = this.territoriesSorted.find(territory => territory.number == text)
if (territory) {
let search = new Search();
let searchResult = new SearchResult();
searchResult.searchType = "TERRITORY"
searchResult.readableText = `${territory.number} ${territory.name}`
searchResult.data = territory
search.searchResults.push(searchResult)
this.sharedService.searchPerformed.next(search)
}
}

private reloadCongregation() {
this.congregationService.getCongregation().subscribe((c: Congregation) => {
this.congregation = c;
this.preacherList = c.preacherList;

this.territoriesSorted = [];
this.territoriesSorted = this.territoriesSorted.concat(c.territoriesAssigned);
this.territoriesSorted = this.territoriesSorted.concat(c.territoriesNoContacts);
this.territoriesSorted = this.territoriesSorted.concat(c.territoriesToBeAssigned);
this.territoriesSorted = this.territoriesSorted.concat(c.territoriesOlder8Months);
this.territoriesSorted = this.territoriesSorted.concat(c.territoriesOlder4Months);

this.territoriesSorted = this.territoriesSorted.sort((a, b) => (a.number > b.number ? 1 : -1));
});
}

Expand Down Expand Up @@ -152,4 +198,18 @@ export class TerritoriesComponent implements OnInit {
this.toastr.info('Territory registered!','Territory Service')
});
}

getTerritoryButtonColor(number: string) {
// #002347, #003366, #003f7d, #ff8e00, #fd7702 and #ff5003.
if (number.startsWith("1")) return "background-color: #002347; color: white;";
if (number.startsWith("2")) return "background-color: #003366; color: white;";
if (number.startsWith("3")) return "background-color: #003f7d; color: white;";
if (number.startsWith("4")) return "background-color: #ff8e00; color: white;";
if (number.startsWith("5")) return "background-color: #fd7702; color: white;";
if (number.startsWith("6")) return "background-color: #ff5003; color: white;";
if (number.startsWith("7")) return "background-color: #ff0000; color: white;";
if (number.startsWith("8")) return "background-color: #ff4800; color: white;";
if (number.startsWith("9")) return "background-color: #ffae00; color: white;";
return "";
}
}
11 changes: 11 additions & 0 deletions server/src/main/ui2/src/app/domains/Search.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export class Search {

searchText:string = "";
searchResults:SearchResult[] = [];
}

export class SearchResult {
searchType:string = "";
readableText:string = "";
data:any;
}
39 changes: 39 additions & 0 deletions server/src/main/ui2/src/app/services/shared.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { Injectable } from '@angular/core';
import {Subject} from "rxjs";
import {Search, SearchResult} from "../domains/Search";

/**
* <h3>Search functionality</h3>
* <ul>
* <li>app provides the search textfield and also the search result display</li>
* <li>if user hits CTRL+F the input field is displayed</li>
* <li>every key hit (except ENTER and ESC) is forwarded to the sub components by the search Subject (they subscribe to the search)</li>
* <li>the component performs the search according to the context and returns the search result to this service as a closeSearch</li>
* <li>app component displays the search result</li>
* <li>if user selects a result, app triggers the corresponding event inside the sub component</li>
* </ul>
* <p>All in all, the service provides the 3 subjects for the interaction</p>
*/
@Injectable({
providedIn: 'root'
})
export class SharedService {

search = new Subject<string>();
searchPerformed = new Subject<Search|undefined>();
searchResultIdentified = new Subject<SearchResult>();

constructor() { }

getSearchSubject() {
return this.search.asObservable();
}

getSearchResultIdentifiedSubject() {
return this.searchResultIdentified.asObservable();
}

getSearchPerformed() {
return this.searchPerformed.asObservable();
}
}
1 change: 1 addition & 0 deletions server/src/main/ui2/src/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,6 @@
<body>
<app-root></app-root>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0-beta1/dist/js/bootstrap.bundle.min.js" integrity="sha384-pprn3073KE6tl6bjs2QrFaJGz5/SUsLqktiwsUTF55Jfv3qYSDhgCecCxMW52nD2" crossorigin="anonymous"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
</body>
</html>

0 comments on commit d21a0ad

Please sign in to comment.