diff --git a/package-lock.json b/package-lock.json index cd019a5..090ffc3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1865,25 +1865,25 @@ } }, "@farmmaps/common": { - "version": "0.0.1-prerelease.530", - "resolved": "https://repository.akkerweb.nl/repository/npm-group/@farmmaps/common/-/common-0.0.1-prerelease.530.tgz", - "integrity": "sha512-cvUKFctQvl91gE5zCJvzxhcnZ35XpletqFmxOFwj6qxLr975qs/Ia9VrzwjkqoIoXlsO9xlM/0Cn1PnPsF7nIg==", + "version": "0.0.1-prerelease.548", + "resolved": "https://repository.akkerweb.nl/repository/npm-group/@farmmaps/common/-/common-0.0.1-prerelease.548.tgz", + "integrity": "sha512-gI+5kwpdimDkJUqU51PsyfZ0My5QRrEtwHGpCivd4kHxBZJ053cz6vHFnH1HquxesIXBVmDxjyeYGCrxhXVIOA==", "requires": { "tslib": "^2.0.0" } }, "@farmmaps/common-map": { - "version": "0.0.1-prerelease.530", - "resolved": "https://repository.akkerweb.nl/repository/npm-group/@farmmaps/common-map/-/common-map-0.0.1-prerelease.530.tgz", - "integrity": "sha512-LU4yzsTvja85GciySpJB5LjUD7fndEsnd2KKj0q9+IFZl9jOFfVcP+/3nImsYYmV9CK6sczNdevaSxQW+lpseQ==", + "version": "0.0.1-prerelease.548", + "resolved": "https://repository.akkerweb.nl/repository/npm-group/@farmmaps/common-map/-/common-map-0.0.1-prerelease.548.tgz", + "integrity": "sha512-rrlYUZvq4r1ANlbinucdasy5iRidOt8DI9V7ra4pKcgZqlsD5hteeosX45DnDlbSvcNzTtY3A0E28e4WXd25jA==", "requires": { "tslib": "^2.0.0" } }, "@farmmaps/common-map3d": { - "version": "0.0.1-prerelease.530", - "resolved": "https://repository.akkerweb.nl/repository/npm-group/@farmmaps/common-map3d/-/common-map3d-0.0.1-prerelease.530.tgz", - "integrity": "sha512-4OG3yv6vOMJZ5dLZ2e8W9P1F1qvzcYBA/zCIA+kxfS0iiOfrOIiJgDvXPhMot+swATSsWeY4Txh6ydh6KmTzlg==", + "version": "0.0.1-prerelease.548", + "resolved": "https://repository.akkerweb.nl/repository/npm-group/@farmmaps/common-map3d/-/common-map3d-0.0.1-prerelease.548.tgz", + "integrity": "sha512-l3XQMSclt6x0fIjKw4K9j1IAr+L+0alHLd/9p80jfCkhB0qo1jZf/TorA636PSYB9NIBioiXOKMDVqTuPi+s/w==", "requires": { "tslib": "^2.0.0" } @@ -8604,10 +8604,20 @@ "ts-md5": "^1.2.4" } }, - "ngx-bootstrap": { - "version": "5.6.2", - "resolved": "https://registry.npmjs.org/ngx-bootstrap/-/ngx-bootstrap-5.6.2.tgz", - "integrity": "sha512-6YHXtdXkGH3w0NQoaUgNYAcrj064Lv5RTO284ha/hvpNTrh55yQz2cVh0VvwBk3MjyY2tdmLH4SuCJDszYdYiw==" + "ngx-image-cropper": { + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/ngx-image-cropper/-/ngx-image-cropper-3.3.5.tgz", + "integrity": "sha512-0yRVKG5XAbVo3rOaj/iFDlekGsxEqXKU9iXFbjyvHvRT2DFs+AjwtyvINsHCWw+4ed9yA4Y+wLIUNqzA0bfxLw==", + "requires": { + "tslib": "^1.9.0" + }, + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + } + } }, "ngx-openlayers": { "version": "1.0.0-next.17", diff --git a/package.json b/package.json index eb5cfb9..1ad2d5b 100644 --- a/package.json +++ b/package.json @@ -19,9 +19,9 @@ "@angular/platform-browser": "~10.2.4", "@angular/platform-browser-dynamic": "~10.2.4", "@angular/router": "~10.2.4", - "@farmmaps/common": ">=0.0.1-prerelease.530 <0.0.1", - "@farmmaps/common-map": ">=0.0.1-prerelease.530 <0.0.1", - "@farmmaps/common-map3d": ">=0.0.1-prerelease.530 <0.0.1", + "@farmmaps/common": ">=0.0.1-prerelease.548 <0.0.1", + "@farmmaps/common-map": ">=0.0.1-prerelease.548 <0.0.1", + "@farmmaps/common-map3d": ">=0.0.1-prerelease.548 <0.0.1", "@microsoft/signalr": "^3.1.3", "@ng-bootstrap/ng-bootstrap": "^7.0", "@ngrx/effects": "^10.0", @@ -31,8 +31,10 @@ "bootstrap": "^4.4.1", "cesium": "^1.77.0", "core-js": "^2.6.11", + "moment": "^2.27.0", "ngrx-store-localstorage": "^10.0", - "ngx-bootstrap": "^5.6.1", + "ngx-avatar": "^4.0.0", + "ngx-image-cropper": "^3.3.5", "ngx-openlayers": "1.0.0-next.17", "ngx-uploadx": "^3.5.1", "ol": "6.5.0", @@ -41,9 +43,7 @@ "rxjs": "^6.5.4", "tassign": "^1.0.0", "tslib": "^2.0.0", - "zone.js": "~0.10.2", - "moment": "^2.27.0", - "ngx-avatar": "^4.0.0" + "zone.js": "~0.10.2" }, "devDependencies": { "@angular-builders/custom-webpack": "~10.0.1", diff --git a/projects/common-map/src/fm-map/components/selected-item/selected-item.component.html b/projects/common-map/src/fm-map/components/selected-item/selected-item.component.html index e52559e..025f82f 100644 --- a/projects/common-map/src/fm-map/components/selected-item/selected-item.component.html +++ b/projects/common-map/src/fm-map/components/selected-item/selected-item.component.html @@ -1,9 +1,6 @@
- -
- -
+
diff --git a/projects/common/package.json b/projects/common/package.json index a0e9f57..10f7777 100644 --- a/projects/common/package.json +++ b/projects/common/package.json @@ -18,6 +18,7 @@ "ngx-uploadx": "^3.3.4", "angular-oauth2-oidc": "^10.0.3", "moment": "^2.27.0", - "ngx-avatar": "^4.0.0" + "ngx-avatar": "^4.0.0", + "ngx-image-cropper": "^3.3.5" } } diff --git a/projects/common/src/fm/common-service.module.ts b/projects/common/src/fm/common-service.module.ts index a5e6ebd..8e8bddf 100644 --- a/projects/common/src/fm/common-service.module.ts +++ b/projects/common/src/fm/common-service.module.ts @@ -16,6 +16,7 @@ import { ItemService } from './services/item.service'; import { EventService } from './services/event.service'; import { TypeaheadService } from './services/typeahead.service'; import { UserService } from './services/user.service'; +import { ImageService } from './services/image.service'; import { WeatherService} from './services/weather.service'; import { AppConfig } from './shared/app.config'; import { AccessTokenInterceptor } from "./shared/accesstoken.interceptor"; @@ -42,6 +43,7 @@ export { EventService, TypeaheadService, UserService, + ImageService, WeatherService, AppConfig, AccessTokenInterceptor, diff --git a/projects/common/src/fm/common.module.ts b/projects/common/src/fm/common.module.ts index 23baa24..3097b6d 100644 --- a/projects/common/src/fm/common.module.ts +++ b/projects/common/src/fm/common.module.ts @@ -60,8 +60,11 @@ import { AppMenuComponent } from './components/app-menu/app-menu.component'; import { NotificationMenuComponent} from './components/notification-menu/notification-menu.component'; import { HelpMenuComponent} from './components/help-menu/help-menu.component'; import { BackButtonComponent } from './components/back-button/back-button.component'; +import { EditImageModalComponent } from './components/edit-image-modal/edit-image-modal.component'; import { AvatarComponent } from './components/avatar/avatar.component'; import { AvatarModule } from 'ngx-avatar'; +import { ImageCropperModule } from 'ngx-image-cropper'; + export { SafePipe, @@ -119,7 +122,8 @@ export { NgbModule, FormsModule, UploadxModule, - AvatarModule + AvatarModule, + ImageCropperModule ], declarations: [ AppComponent, @@ -143,6 +147,7 @@ export { HelpMenuComponent, BackButtonComponent, ThumbnailComponent, + EditImageModalComponent, AvatarComponent ], exports: [ diff --git a/projects/common/src/fm/components/edit-image-modal/edit-image-modal.component.html b/projects/common/src/fm/components/edit-image-modal/edit-image-modal.component.html new file mode 100644 index 0000000..413e5d7 --- /dev/null +++ b/projects/common/src/fm/components/edit-image-modal/edit-image-modal.component.html @@ -0,0 +1,34 @@ + + + + + diff --git a/projects/common/src/fm/components/edit-image-modal/edit-image-modal.component.scss b/projects/common/src/fm/components/edit-image-modal/edit-image-modal.component.scss new file mode 100644 index 0000000..be7e1fd --- /dev/null +++ b/projects/common/src/fm/components/edit-image-modal/edit-image-modal.component.scss @@ -0,0 +1,13 @@ +.cropper { + position: relative; + height: calc(60vh); +} + +.no-image { + text-align: center; +} + +.no-image i { + font-size: calc(50vh); + color: gray; +} \ No newline at end of file diff --git a/projects/common/src/fm/components/edit-image-modal/edit-image-modal.component.spec.ts b/projects/common/src/fm/components/edit-image-modal/edit-image-modal.component.spec.ts new file mode 100644 index 0000000..7395ec0 --- /dev/null +++ b/projects/common/src/fm/components/edit-image-modal/edit-image-modal.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { EditImageModalComponent } from './edit-image-modal.component'; + +describe('EditImageModalComponent', () => { + let component: EditImageModalComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ EditImageModalComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(EditImageModalComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/projects/common/src/fm/components/edit-image-modal/edit-image-modal.component.ts b/projects/common/src/fm/components/edit-image-modal/edit-image-modal.component.ts new file mode 100644 index 0000000..3b8e7fb --- /dev/null +++ b/projects/common/src/fm/components/edit-image-modal/edit-image-modal.component.ts @@ -0,0 +1,62 @@ +import { Component, OnInit,ViewChild,ElementRef,EventEmitter, Output } from '@angular/core'; +import { HttpClient, HttpParams,HttpHeaders } from "@angular/common/http"; +import {NgbModal} from "@ng-bootstrap/ng-bootstrap" +import { ImageCroppedEvent,LoadedImage } from 'ngx-image-cropper'; +import {ImageService } from '../../services/image.service'; + +@Component({ + selector: 'fm-edit-image-modal', + templateUrl: './edit-image-modal.component.html', + styleUrls: ['./edit-image-modal.component.scss'] +}) +export class EditImageModalComponent implements OnInit { + + @Output() changed = new EventEmitter(); + @ViewChild('upload_modal') modal:ElementRef; + + constructor(private modalService: NgbModal,public imageService:ImageService) { } + + isImageLoaded:boolean = false; + aspectRatio:number = 4/3; + imageChangedEvent: any = ''; + croppedImage: string = ''; + endpointUrl:string = null; + imageUrl:string = null; + + ngOnInit(): void { + } + + open(endpoint:string,aspectRatio:number) { + this.endpointUrl = endpoint; + this.imageUrl = endpoint; + this.aspectRatio= aspectRatio; + this.modalService.open(this.modal,{ size: 'lg' }); + } + + fileChangeEvent(event: any): void { + this.imageChangedEvent = event; + } + imageCropped(event: ImageCroppedEvent) { + this.croppedImage = event.base64; + } + imageLoaded(image: LoadedImage) { + this.isImageLoaded=true; + } + cropperReady() { + // cropper ready + } + loadImageFailed() { + // show message + } + + save() { + if(this.croppedImage) { + + var body = this.croppedImage.substr(23); + this.imageService.putImage(this.endpointUrl,this.imageService.b64toBlob(body,"image/jpeg")).subscribe(() => { + this.changed.emit({}); + }); + this,this.modalService.dismissAll("Save"); + } + } +} diff --git a/projects/common/src/fm/components/thumbnail/thumbnail.component.html b/projects/common/src/fm/components/thumbnail/thumbnail.component.html index 16a806e..a5a560a 100644 --- a/projects/common/src/fm/components/thumbnail/thumbnail.component.html +++ b/projects/common/src/fm/components/thumbnail/thumbnail.component.html @@ -1,6 +1,9 @@ -
- -
- +
+
+ +
+
-
\ No newline at end of file +
+ + diff --git a/projects/common/src/fm/components/thumbnail/thumbnail.component.scss b/projects/common/src/fm/components/thumbnail/thumbnail.component.scss index e69de29..6ce8ec0 100644 --- a/projects/common/src/fm/components/thumbnail/thumbnail.component.scss +++ b/projects/common/src/fm/components/thumbnail/thumbnail.component.scss @@ -0,0 +1,28 @@ +.thumbnail { + width: 100%; + position: relative; + padding-top: 75%; +} + +.content { + position: absolute; + top: 0; + left: 0; + bottom: 0; + right: 0; + text-align: center; +} + +.large-icon i { + color: white; +} + +.edit { + position: absolute; + bottom: 1rem; + right: 1rem; + width: 2.5rem; + height: 2.5rem; + padding: 0; + line-height: 2.5rem; +} \ No newline at end of file diff --git a/projects/common/src/fm/components/thumbnail/thumbnail.component.ts b/projects/common/src/fm/components/thumbnail/thumbnail.component.ts index 9163846..075be8e 100644 --- a/projects/common/src/fm/components/thumbnail/thumbnail.component.ts +++ b/projects/common/src/fm/components/thumbnail/thumbnail.component.ts @@ -1,7 +1,10 @@ -import { Component,Input } from '@angular/core'; +import { Component,Input ,ViewChild,ElementRef,ChangeDetectorRef} from '@angular/core'; import { Store } from '@ngrx/store'; -import { IItem } from '../../models/item'; + +import { IListItem } from '../../models/list.item'; import { commonReducers,ItemTypeService } from '../../../public-api' +import { EditImageModalComponent} from '../edit-image-modal/edit-image-modal.component'; +import { AppConfig } from "../../shared/app.config"; @Component({ selector: 'fm-thumbnail', @@ -11,12 +14,48 @@ import { commonReducers,ItemTypeService } from '../../../public-api' export class ThumbnailComponent { - @Input() public item: IItem; + @Input() public item: IListItem; + @Input() public edit: boolean = false; + @ViewChild('thumbnail') el:ElementRef; + @ViewChild('modal') modal:EditImageModalComponent; - constructor(public store: Store, public itemTypeService: ItemTypeService) { + constructor(public store: Store, public itemTypeService: ItemTypeService,public appConfig: AppConfig,private changeDetector:ChangeDetectorRef) { } - getThumbnailUrl(item:IItem):string { - return item.url+'/thumbnail?v=' + Date.parse(item.updated); - } + getThumbnailUrl(item:IListItem):string { + return this.appConfig.getConfig("apiEndPoint")+item.url+'/thumbnail?v=' + Date.parse(item.updated); + } + + getFontSize():string { + if(this.el) { + var h = this.el.nativeElement.offsetHeight - (this.el.nativeElement.offsetHeight / 5 ) + return h + "px"; + } else { + return "1em"; + } + } + + getLineHeight():string { + if(this.el) { + return this.el.nativeElement.offsetHeight + "px"; + } else { + return "1em"; + } + } + + canEdit():boolean { + return this.edit && this.item != null; + } + + onEditClick() { + var endpoint = `${this.appConfig.getConfig("apiEndPoint")}/api/v1/items/${this.item.code}/thumbnail`; + this.modal.open(endpoint,4/3); + } + + onChanged() { + if(this.item) { + this.item.updated = new Date(new Date().getTime()).toISOString(); + this.changeDetector.detectChanges(); + } + } } \ No newline at end of file diff --git a/projects/common/src/fm/components/user-menu/user-menu.component.scss b/projects/common/src/fm/components/user-menu/user-menu.component.scss index 481addf..0b0f03c 100644 --- a/projects/common/src/fm/components/user-menu/user-menu.component.scss +++ b/projects/common/src/fm/components/user-menu/user-menu.component.scss @@ -1,5 +1,4 @@ .menu-button { - background-color: purple; display: inline-block; width: 2.5em; height: 2.5em; diff --git a/projects/common/src/fm/services/image.service.ts b/projects/common/src/fm/services/image.service.ts new file mode 100644 index 0000000..24b7a96 --- /dev/null +++ b/projects/common/src/fm/services/image.service.ts @@ -0,0 +1,44 @@ +import { Injectable } from '@angular/core'; +import { Observable } from 'rxjs'; +import { IUser } from '../models/user'; +import { HttpClient,HttpHeaders } from "@angular/common/http"; +import { AppConfig } from "../shared/app.config"; + +@Injectable({ + providedIn: 'root', +}) +export class ImageService { + constructor(public httpClient: HttpClient, public appConfig: AppConfig) { + } + + ApiEndpoint() { + return this.appConfig.getConfig("apiEndPoint"); + } + + putImage(endpoint:string,blob:Blob) { + const formData = new FormData(); + formData.append('file', blob,blob.type); + return this.httpClient.put(endpoint,formData); + } + + b64toBlob(b64Data:string, contentType?:string):Blob { + const sliceSize = 512; + const byteCharacters = atob(b64Data); + const byteArrays = []; + + for (let offset = 0; offset < byteCharacters.length; offset += sliceSize) { + const slice = byteCharacters.slice(offset, offset + sliceSize); + + const byteNumbers = new Array(slice.length); + for (let i = 0; i < slice.length; i++) { + byteNumbers[i] = slice.charCodeAt(i); + } + + const byteArray = new Uint8Array(byteNumbers); + byteArrays.push(byteArray); + } + + const blob = new Blob(byteArrays, {type: contentType}); + return blob; + } +} diff --git a/src/configuration.json b/src/configuration.json index 61c0dff..c38ec06 100644 --- a/src/configuration.json +++ b/src/configuration.json @@ -1,9 +1,9 @@ { "issuer": "https://accounts.test.farmmaps.eu", "clientId": "farmmapsdev", - "audience": "https://test.farmmaps.eu", + "audience": "http://localhost:8082", "requireHttps": true, - "apiEndPoint": "https://test.farmmaps.eu", + "apiEndPoint": "http://localhost:8082", "grantType":"code" } \ No newline at end of file