account, myListings and emailUs pages

This commit is contained in:
2024-07-10 18:40:46 +02:00
parent 08c179fa09
commit 7bd5e1aaf8
19 changed files with 622 additions and 226 deletions

View File

@@ -19,7 +19,7 @@
<div class="flex items-center md:order-2 space-x-3 md:space-x-0 rtl:space-x-reverse">
<button
type="button"
class="flex text-sm bg-gray-800 rounded-full md:me-0 focus:ring-4 focus:ring-gray-300 dark:focus:ring-gray-600"
class="flex text-sm bg-gray-200 rounded-full md:me-0 focus:ring-4 focus:ring-gray-300 dark:focus:ring-gray-600"
id="user-menu-button"
aria-expanded="false"
[attr.data-dropdown-toggle]="user ? 'user-login' : 'user-unknown'"
@@ -27,7 +27,7 @@
>
<span class="sr-only">Open user menu</span>
@if(user){
<img class="w-8 h-8 rounded-full" src="/docs/images/people/profile-picture-3.jpg" alt="user photo" />
<img class="w-8 h-8 rounded-full object-cover" src="{{ profileUrl }}" alt="user photo" />
} @else {
<i class="flex justify-center items-center text-stone-50 w-8 h-8 rounded-full fa-solid fa-bars"></i>
}
@@ -36,7 +36,7 @@
@if(user){
<div class="z-50 hidden my-4 text-base list-none bg-white divide-y divide-gray-100 rounded-lg shadow dark:bg-gray-700 dark:divide-gray-600" id="user-login">
<div class="px-4 py-3">
<span class="block text-sm text-gray-900 dark:text-white">Welcome, {{ user.firstName }} </span>
<span class="block text-sm text-gray-900 dark:text-white">Welcome, {{ user.firstname }} </span>
<span class="block text-sm text-gray-500 truncate dark:text-gray-400">{{ user.email }}</span>
</div>
<ul class="py-2" aria-labelledby="user-menu-button">

View File

@@ -5,8 +5,11 @@ import { faUserGear } from '@fortawesome/free-solid-svg-icons';
import { initFlowbite } from 'flowbite';
import { KeycloakService } from 'keycloak-angular';
import { Observable } from 'rxjs';
import { KeycloakUser } from '../../../../../bizmatch-server/src/models/main.model';
import { User } from '../../../../../bizmatch-server/src/models/db.model';
import { emailToDirName, KeycloakUser } from '../../../../../bizmatch-server/src/models/main.model';
import { environment } from '../../../environments/environment';
import { SharedService } from '../../services/shared.service';
import { UserService } from '../../services/user.service';
import { map2User } from '../../utils/utils';
@Component({
selector: 'header',
@@ -18,17 +21,27 @@ import { map2User } from '../../utils/utils';
export class HeaderComponent {
public buildVersion = environment.buildVersion;
user$: Observable<KeycloakUser>;
user: KeycloakUser;
keycloakUser: KeycloakUser;
user: User;
activeItem;
faUserGear = faUserGear;
constructor(public keycloakService: KeycloakService, private router: Router) {}
profileUrl: string;
env = environment;
constructor(public keycloakService: KeycloakService, private router: Router, private userService: UserService, private sharedService: SharedService) {}
async ngOnInit() {
const token = await this.keycloakService.getToken();
this.user = map2User(token);
this.keycloakUser = map2User(token);
this.user = await this.userService.getByMail(this.keycloakUser.email);
this.profileUrl = this.user.hasProfile ? `${this.env.imageBaseUrl}/pictures/profile/${emailToDirName(this.user.email)}.avif?_ts=${new Date().getTime()}` : `/assets/images/placeholder.png`;
setTimeout(() => {
initFlowbite();
});
this.sharedService.currentProfilePhoto.subscribe(photoUrl => {
if (photoUrl) {
this.profileUrl = photoUrl;
}
});
}
ngAfterViewInit() {}

View File

@@ -0,0 +1,12 @@
<!-- Modal -->
<div *ngIf="showModal" class="fixed inset-0 bg-gray-600 bg-opacity-50 overflow-y-auto h-full w-full flex items-center justify-center">
<div class="bg-white p-5 rounded-lg shadow-xl" style="width: 90%; max-width: 600px">
<h3 class="text-lg font-semibold mb-4">Crop Image</h3>
<image-cropper (loadImageFailed)="loadImageFailed()" [imageChangedEvent]="imageChangedEvent" [maintainAspectRatio]="false" format="png" (imageCropped)="imageCropped($event)"></image-cropper>
<div class="mt-4 flex justify-end">
<button (click)="closeModal()" class="mr-2 px-4 py-2 bg-gray-200 text-gray-800 rounded hover:bg-gray-300">Cancel</button>
<button (click)="uploadImage()" class="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600">Upload</button>
</div>
</div>
</div>
<input type="file" #fileInput style="display: none" (change)="fileChangeEvent($event)" accept="image/*" />

View File

@@ -0,0 +1,6 @@
::ng-deep image-cropper {
justify-content: center;
& > div {
width: unset !important;
}
}

View File

@@ -0,0 +1,74 @@
import { Component, ElementRef, Input, output, ViewChild } from '@angular/core';
import { ImageCroppedEvent, ImageCropperComponent } from 'ngx-image-cropper';
import { UploadParams } from '../../../../../bizmatch-server/src/models/main.model';
import { ImageService } from '../../services/image.service';
import { ListingsService } from '../../services/listings.service';
import { SharedModule } from '../../shared/shared/shared.module';
export interface UploadReponse {
success: boolean;
type: 'uploadPropertyPicture' | 'uploadCompanyLogo' | 'uploadProfile';
}
@Component({
selector: 'app-image-crop-and-upload',
standalone: true,
imports: [SharedModule, ImageCropperComponent],
templateUrl: './image-crop-and-upload.component.html',
styleUrl: './image-crop-and-upload.component.scss',
})
export class ImageCropAndUploadComponent {
showModal = false;
imageChangedEvent: any = '';
croppedImage: Blob | null = null;
@Input() uploadParams: UploadParams;
uploadFinished = output<UploadReponse>();
@ViewChild('fileInput', { static: true }) fileInput!: ElementRef<HTMLInputElement>;
constructor(private imageService: ImageService, private listingsService: ListingsService) {}
ngOnInit() {}
ngOnChanges() {
this.openFileDialog();
}
openFileDialog() {
if (this.uploadParams) {
this.fileInput.nativeElement.click();
}
}
fileChangeEvent(event: any): void {
this.imageChangedEvent = event;
this.showModal = true;
}
imageCropped(event: ImageCroppedEvent) {
this.croppedImage = event.blob;
}
closeModal() {
this.imageChangedEvent = null;
this.croppedImage = null;
this.showModal = false;
this.uploadFinished.emit({ success: false, type: this.uploadParams.type });
}
uploadImage() {
if (this.croppedImage) {
this.imageService.uploadImage(this.croppedImage, this.uploadParams.type, this.uploadParams.imagePath, this.uploadParams.serialId).subscribe(
response => {
console.log('Upload successful', response);
this.closeModal();
this.uploadFinished.emit({ success: true, type: this.uploadParams.type });
},
error => {
console.error('Upload failed', error);
this.closeModal();
this.uploadFinished.emit({ success: false, type: this.uploadParams.type });
},
);
}
}
loadImageFailed() {
console.error('Load image failed');
}
}

View File

@@ -8,19 +8,25 @@ import { MessageService } from './message.service';
standalone: true,
imports: [AsyncPipe, NgIf],
template: `
<div id="toast-success" class="flex items-center w-full max-w-xs p-4 mb-4 text-gray-500 bg-white rounded-lg shadow dark:text-gray-400 dark:bg-gray-800" role="alert">
<div
*ngIf="messageService.modalVisible$ | async"
id="toast-success"
class="fixed top-[0.5rem] right-[1rem] flex items-center w-full max-w-xs p-4 mb-4 text-gray-500 bg-slate-200 rounded-lg shadow dark:text-gray-400 dark:bg-gray-800"
role="alert"
>
<div class="inline-flex items-center justify-center flex-shrink-0 w-8 h-8 text-green-500 bg-green-100 rounded-lg dark:bg-green-800 dark:text-green-200">
<svg class="w-5 h-5" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 20 20">
<path d="M10 .5a9.5 9.5 0 1 0 9.5 9.5A9.51 9.51 0 0 0 10 .5Zm3.707 8.207-4 4a1 1 0 0 1-1.414 0l-2-2a1 1 0 0 1 1.414-1.414L9 10.586l3.293-3.293a1 1 0 0 1 1.414 1.414Z" />
</svg>
<span class="sr-only">Check icon</span>
</div>
<div class="ms-3 text-sm font-normal">Item moved successfully.</div>
<div class="ms-3 text-sm font-normal">{{ messageService.message$ | async }}</div>
<button
type="button"
class="ms-auto -mx-1.5 -my-1.5 bg-white text-gray-400 hover:text-gray-900 rounded-lg focus:ring-2 focus:ring-gray-300 p-1.5 hover:bg-gray-100 inline-flex items-center justify-center h-8 w-8 dark:text-gray-500 dark:hover:text-white dark:bg-gray-800 dark:hover:bg-gray-700"
data-dismiss-target="#toast-success"
aria-label="Close"
(click)="messageService.reject()"
>
<span class="sr-only">Close</span>
<svg class="w-3 h-3" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 14 14">