account, myListings and emailUs pages
This commit is contained in:
parent
08c179fa09
commit
7bd5e1aaf8
|
|
@ -185,6 +185,11 @@ export interface FieldError {
|
||||||
fieldname: string;
|
fieldname: string;
|
||||||
message: string;
|
message: string;
|
||||||
}
|
}
|
||||||
|
export interface UploadParams {
|
||||||
|
type: 'uploadPropertyPicture' | 'uploadCompanyLogo' | 'uploadProfile';
|
||||||
|
imagePath: string;
|
||||||
|
serialId?: number;
|
||||||
|
}
|
||||||
export function isEmpty(value: any): boolean {
|
export function isEmpty(value: any): boolean {
|
||||||
// Check for undefined or null
|
// Check for undefined or null
|
||||||
if (value === undefined || value === null) {
|
if (value === undefined || value === null) {
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@
|
||||||
<div class="flex items-center md:order-2 space-x-3 md:space-x-0 rtl:space-x-reverse">
|
<div class="flex items-center md:order-2 space-x-3 md:space-x-0 rtl:space-x-reverse">
|
||||||
<button
|
<button
|
||||||
type="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"
|
id="user-menu-button"
|
||||||
aria-expanded="false"
|
aria-expanded="false"
|
||||||
[attr.data-dropdown-toggle]="user ? 'user-login' : 'user-unknown'"
|
[attr.data-dropdown-toggle]="user ? 'user-login' : 'user-unknown'"
|
||||||
|
|
@ -27,7 +27,7 @@
|
||||||
>
|
>
|
||||||
<span class="sr-only">Open user menu</span>
|
<span class="sr-only">Open user menu</span>
|
||||||
@if(user){
|
@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 {
|
} @else {
|
||||||
<i class="flex justify-center items-center text-stone-50 w-8 h-8 rounded-full fa-solid fa-bars"></i>
|
<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){
|
@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="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">
|
<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>
|
<span class="block text-sm text-gray-500 truncate dark:text-gray-400">{{ user.email }}</span>
|
||||||
</div>
|
</div>
|
||||||
<ul class="py-2" aria-labelledby="user-menu-button">
|
<ul class="py-2" aria-labelledby="user-menu-button">
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,11 @@ import { faUserGear } from '@fortawesome/free-solid-svg-icons';
|
||||||
import { initFlowbite } from 'flowbite';
|
import { initFlowbite } from 'flowbite';
|
||||||
import { KeycloakService } from 'keycloak-angular';
|
import { KeycloakService } from 'keycloak-angular';
|
||||||
import { Observable } from 'rxjs';
|
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 { environment } from '../../../environments/environment';
|
||||||
|
import { SharedService } from '../../services/shared.service';
|
||||||
|
import { UserService } from '../../services/user.service';
|
||||||
import { map2User } from '../../utils/utils';
|
import { map2User } from '../../utils/utils';
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'header',
|
selector: 'header',
|
||||||
|
|
@ -18,17 +21,27 @@ import { map2User } from '../../utils/utils';
|
||||||
export class HeaderComponent {
|
export class HeaderComponent {
|
||||||
public buildVersion = environment.buildVersion;
|
public buildVersion = environment.buildVersion;
|
||||||
user$: Observable<KeycloakUser>;
|
user$: Observable<KeycloakUser>;
|
||||||
user: KeycloakUser;
|
keycloakUser: KeycloakUser;
|
||||||
|
user: User;
|
||||||
activeItem;
|
activeItem;
|
||||||
faUserGear = faUserGear;
|
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() {
|
async ngOnInit() {
|
||||||
const token = await this.keycloakService.getToken();
|
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(() => {
|
setTimeout(() => {
|
||||||
initFlowbite();
|
initFlowbite();
|
||||||
});
|
});
|
||||||
|
this.sharedService.currentProfilePhoto.subscribe(photoUrl => {
|
||||||
|
if (photoUrl) {
|
||||||
|
this.profileUrl = photoUrl;
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
ngAfterViewInit() {}
|
ngAfterViewInit() {}
|
||||||
|
|
|
||||||
|
|
@ -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/*" />
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
::ng-deep image-cropper {
|
||||||
|
justify-content: center;
|
||||||
|
& > div {
|
||||||
|
width: unset !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -8,19 +8,25 @@ import { MessageService } from './message.service';
|
||||||
standalone: true,
|
standalone: true,
|
||||||
imports: [AsyncPipe, NgIf],
|
imports: [AsyncPipe, NgIf],
|
||||||
template: `
|
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">
|
<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">
|
<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" />
|
<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>
|
</svg>
|
||||||
<span class="sr-only">Check icon</span>
|
<span class="sr-only">Check icon</span>
|
||||||
</div>
|
</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
|
<button
|
||||||
type="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"
|
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"
|
data-dismiss-target="#toast-success"
|
||||||
aria-label="Close"
|
aria-label="Close"
|
||||||
|
(click)="messageService.reject()"
|
||||||
>
|
>
|
||||||
<span class="sr-only">Close</span>
|
<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">
|
<svg class="w-3 h-3" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 14 14">
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,111 @@
|
||||||
|
<div class="container mx-auto p-4">
|
||||||
|
<div class="bg-white rounded-lg shadow-lg overflow-hidden relative">
|
||||||
|
<button
|
||||||
|
(click)="historyService.goBack()"
|
||||||
|
class="absolute top-4 right-4 bg-red-500 text-white rounded-full w-8 h-8 flex items-center justify-center hover:bg-red-600 focus:outline-none focus:ring-2 focus:ring-red-500 focus:ring-opacity-50"
|
||||||
|
>
|
||||||
|
<i class="fas fa-times"></i>
|
||||||
|
</button>
|
||||||
|
@if(listing){
|
||||||
|
<div class="p-6 flex flex-col lg:flex-row">
|
||||||
|
<!-- Left column -->
|
||||||
|
<div class="w-full lg:w-1/2 pr-0 lg:pr-6">
|
||||||
|
<h1 class="text-2xl font-bold mb-4">{{ listing.title }}</h1>
|
||||||
|
<p class="mb-4" [innerHTML]="description"></p>
|
||||||
|
|
||||||
|
<div class="space-y-2">
|
||||||
|
<div class="flex flex-col sm:flex-row" [ngClass]="{ 'bg-gray-100': i % 2 === 0 }" *ngFor="let item of listingDetails; let i = index">
|
||||||
|
<div class="w-full sm:w-1/3 font-semibold p-2">{{ item.label }}</div>
|
||||||
|
@if(item.label==='Category'){
|
||||||
|
<span class="bg-blue-100 text-blue-800 font-medium me-2 px-2.5 py-0.5 rounded-full dark:bg-blue-900 dark:text-blue-300 my-1">{{ item.value }}</span>
|
||||||
|
} @else {
|
||||||
|
<div class="w-full sm:w-2/3 p-2">{{ item.value }}</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@if(listing && listingUser && (listingUser?.email===user?.email || isAdmin())){
|
||||||
|
<button
|
||||||
|
[routerLink]="['/editBusinessListing', listing.id]"
|
||||||
|
class="w-full sm:w-auto px-4 py-2 mt-2 bg-blue-600 text-white rounded-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-opacity-50"
|
||||||
|
>
|
||||||
|
Edit
|
||||||
|
</button>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Right column -->
|
||||||
|
<div class="w-full lg:w-1/2 mt-6 lg:mt-0">
|
||||||
|
<h3 class="text-lg font-semibold mb-4">Contact the Author of this Listing</h3>
|
||||||
|
<p class="text-sm mb-4">Please include your contact info below</p>
|
||||||
|
<form class="space-y-4">
|
||||||
|
<div class="flex flex-col sm:flex-row space-y-4 sm:space-y-0 sm:space-x-4">
|
||||||
|
<div class="w-full sm:w-1/2">
|
||||||
|
<label for="name" class="block text-sm font-medium text-gray-700">Your Name</label>
|
||||||
|
<input type="text" id="name" name="name" [(ngModel)]="mailinfo.sender.name" class="mt-1 block w-full p-2 border border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500 sm:text-sm" />
|
||||||
|
</div>
|
||||||
|
<div class="w-full sm:w-1/2">
|
||||||
|
<label for="email" class="block text-sm font-medium text-gray-700">Your Email</label>
|
||||||
|
<input
|
||||||
|
type="email"
|
||||||
|
id="email"
|
||||||
|
name="email"
|
||||||
|
[(ngModel)]="mailinfo.sender.email"
|
||||||
|
class="mt-1 block w-full p-2 border border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500 sm:text-sm"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-col sm:flex-row space-y-4 sm:space-y-0 sm:space-x-4">
|
||||||
|
<div class="w-full sm:w-1/2">
|
||||||
|
<label for="phone" class="block text-sm font-medium text-gray-700">Phone Number</label>
|
||||||
|
<input
|
||||||
|
type="tel"
|
||||||
|
id="phone"
|
||||||
|
name="phone"
|
||||||
|
[(ngModel)]="mailinfo.sender.phoneNumber"
|
||||||
|
placeholder="(123) 456-7890"
|
||||||
|
class="mt-1 block w-full p-2 border border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500 sm:text-sm"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="w-full sm:w-1/2">
|
||||||
|
<label for="country" class="block text-sm font-medium text-gray-700">Country/State</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
id="country"
|
||||||
|
name="country"
|
||||||
|
[(ngModel)]="mailinfo.sender.state"
|
||||||
|
class="mt-1 block w-full p-2 border border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500 sm:text-sm"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="comments" class="block text-sm font-medium text-gray-700">Questions/Comments</label>
|
||||||
|
<textarea
|
||||||
|
id="comments"
|
||||||
|
name="comments"
|
||||||
|
rows="4"
|
||||||
|
[(ngModel)]="mailinfo.sender.comments"
|
||||||
|
class="mt-1 block w-full p-2 border border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500 sm:text-sm"
|
||||||
|
></textarea>
|
||||||
|
</div>
|
||||||
|
@if(listingUser){
|
||||||
|
<div class="flex items-center space-x-2">
|
||||||
|
<p>Listing by</p>
|
||||||
|
<!-- <p class="text-sm font-semibold">Noah Nguyen</p> -->
|
||||||
|
<a routerLink="/details-user/{{ listingUser.id }}" class="text-blue-600 dark:text-blue-500 hover:underline">{{ listingUser.firstname }} {{ listingUser.lastname }}</a>
|
||||||
|
<!-- <img src="https://placehold.co/20x20" alt="Broker logo" class="w-5 h-5" /> -->
|
||||||
|
@if(listingUser.hasCompanyLogo){
|
||||||
|
<img src="{{ env.imageBaseUrl }}/pictures/logo/{{ listing.imageName }}.avif?_ts={{ ts }}" class="mr-5 lg:mb-0" style="max-height: 30px; max-width: 100px" />
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
<button (click)="mail()" class="w-full sm:w-auto px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-opacity-50">Submit</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- <div class="surface-ground h-full">
|
<!-- <div class="surface-ground h-full">
|
||||||
<div class="px-6 py-5">
|
<div class="px-6 py-5">
|
||||||
<div class="surface-card p-4 shadow-2 border-round">
|
<div class="surface-card p-4 shadow-2 border-round">
|
||||||
|
|
@ -117,109 +225,3 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div> -->
|
</div> -->
|
||||||
|
|
||||||
<div class="container mx-auto p-4">
|
|
||||||
<div class="bg-white rounded-lg shadow-lg overflow-hidden relative">
|
|
||||||
<button
|
|
||||||
(click)="historyService.goBack()"
|
|
||||||
class="absolute top-4 right-4 bg-red-500 text-white rounded-full w-8 h-8 flex items-center justify-center hover:bg-red-600 focus:outline-none focus:ring-2 focus:ring-red-500 focus:ring-opacity-50"
|
|
||||||
>
|
|
||||||
<i class="fas fa-times"></i>
|
|
||||||
</button>
|
|
||||||
<div class="p-6 flex flex-col lg:flex-row">
|
|
||||||
<!-- Left column -->
|
|
||||||
<div class="w-full lg:w-1/2 pr-0 lg:pr-6">
|
|
||||||
<h1 class="text-2xl font-bold mb-4">{{ listing.title }}</h1>
|
|
||||||
<p class="mb-4" [innerHTML]="description"></p>
|
|
||||||
|
|
||||||
<div class="space-y-2">
|
|
||||||
<div class="flex flex-col sm:flex-row" [ngClass]="{ 'bg-gray-100': i % 2 === 0 }" *ngFor="let item of listingDetails; let i = index">
|
|
||||||
<div class="w-full sm:w-1/3 font-semibold p-2">{{ item.label }}</div>
|
|
||||||
@if(item.label==='Category'){
|
|
||||||
<span class="bg-blue-100 text-blue-800 font-medium me-2 px-2.5 py-0.5 rounded-full dark:bg-blue-900 dark:text-blue-300 my-1">{{ item.value }}</span>
|
|
||||||
} @else {
|
|
||||||
<div class="w-full sm:w-2/3 p-2">{{ item.value }}</div>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
@if(listing && listingUser && (listingUser?.email===user?.email || isAdmin())){
|
|
||||||
<button
|
|
||||||
[routerLink]="['/editBusinessListing', listing.id]"
|
|
||||||
class="w-full sm:w-auto px-4 py-2 mt-2 bg-blue-600 text-white rounded-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-opacity-50"
|
|
||||||
>
|
|
||||||
Edit
|
|
||||||
</button>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Right column -->
|
|
||||||
<div class="w-full lg:w-1/2 mt-6 lg:mt-0">
|
|
||||||
<h3 class="text-lg font-semibold mb-4">Contact the Author of this Listing</h3>
|
|
||||||
<p class="text-sm mb-4">Please include your contact info below</p>
|
|
||||||
<form class="space-y-4">
|
|
||||||
<div class="flex flex-col sm:flex-row space-y-4 sm:space-y-0 sm:space-x-4">
|
|
||||||
<div class="w-full sm:w-1/2">
|
|
||||||
<label for="name" class="block text-sm font-medium text-gray-700">Your Name</label>
|
|
||||||
<input type="text" id="name" name="name" [(ngModel)]="mailinfo.sender.name" class="mt-1 block w-full p-2 border border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500 sm:text-sm" />
|
|
||||||
</div>
|
|
||||||
<div class="w-full sm:w-1/2">
|
|
||||||
<label for="email" class="block text-sm font-medium text-gray-700">Your Email</label>
|
|
||||||
<input
|
|
||||||
type="email"
|
|
||||||
id="email"
|
|
||||||
name="email"
|
|
||||||
[(ngModel)]="mailinfo.sender.email"
|
|
||||||
class="mt-1 block w-full p-2 border border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500 sm:text-sm"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="flex flex-col sm:flex-row space-y-4 sm:space-y-0 sm:space-x-4">
|
|
||||||
<div class="w-full sm:w-1/2">
|
|
||||||
<label for="phone" class="block text-sm font-medium text-gray-700">Phone Number</label>
|
|
||||||
<input
|
|
||||||
type="tel"
|
|
||||||
id="phone"
|
|
||||||
name="phone"
|
|
||||||
[(ngModel)]="mailinfo.sender.phoneNumber"
|
|
||||||
placeholder="(123) 456-7890"
|
|
||||||
class="mt-1 block w-full p-2 border border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500 sm:text-sm"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div class="w-full sm:w-1/2">
|
|
||||||
<label for="country" class="block text-sm font-medium text-gray-700">Country/State</label>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
id="country"
|
|
||||||
name="country"
|
|
||||||
[(ngModel)]="mailinfo.sender.state"
|
|
||||||
class="mt-1 block w-full p-2 border border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500 sm:text-sm"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<label for="comments" class="block text-sm font-medium text-gray-700">Questions/Comments</label>
|
|
||||||
<textarea
|
|
||||||
id="comments"
|
|
||||||
name="comments"
|
|
||||||
rows="4"
|
|
||||||
[(ngModel)]="mailinfo.sender.comments"
|
|
||||||
class="mt-1 block w-full p-2 border border-gray-300 rounded-md shadow-sm focus:ring-blue-500 focus:border-blue-500 sm:text-sm"
|
|
||||||
></textarea>
|
|
||||||
</div>
|
|
||||||
@if(listingUser){
|
|
||||||
<div class="flex items-center space-x-2">
|
|
||||||
<p>Listing by</p>
|
|
||||||
<!-- <p class="text-sm font-semibold">Noah Nguyen</p> -->
|
|
||||||
<a routerLink="/details-user/{{ listingUser.id }}" class="text-blue-600 dark:text-blue-500 hover:underline">{{ listingUser.firstname }} {{ listingUser.lastname }}</a>
|
|
||||||
<!-- <img src="https://placehold.co/20x20" alt="Broker logo" class="w-5 h-5" /> -->
|
|
||||||
@if(listingUser.hasCompanyLogo){
|
|
||||||
<img src="{{ env.imageBaseUrl }}/pictures/logo/{{ listing.imageName }}.avif?_ts={{ ts }}" class="mr-5 lg:mb-0" style="max-height: 30px; max-width: 100px" />
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
<button (click)="mail()" class="w-full sm:w-auto px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-opacity-50">Submit</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
|
||||||
|
|
@ -135,6 +135,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div> -->
|
</div> -->
|
||||||
<div class="container mx-auto p-4">
|
<div class="container mx-auto p-4">
|
||||||
|
@if(user){
|
||||||
<div class="bg-white shadow-md rounded-lg overflow-hidden">
|
<div class="bg-white shadow-md rounded-lg overflow-hidden">
|
||||||
<!-- Header -->
|
<!-- Header -->
|
||||||
<div class="flex items-center justify-between p-4 border-b">
|
<div class="flex items-center justify-between p-4 border-b">
|
||||||
|
|
@ -266,4 +267,5 @@
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
<div class="container mx-auto p-4">
|
<div class="container mx-auto p-4">
|
||||||
|
@if (user){
|
||||||
<div class="bg-white rounded-lg shadow-md p-6">
|
<div class="bg-white rounded-lg shadow-md p-6">
|
||||||
<form #accountForm="ngForm" class="space-y-4">
|
<form #accountForm="ngForm" class="space-y-4">
|
||||||
<h2 class="text-2xl font-bold mb-4">Account Details</h2>
|
<h2 class="text-2xl font-bold mb-4">Account Details</h2>
|
||||||
|
|
@ -9,26 +10,46 @@
|
||||||
<p class="text-xs text-gray-500 mt-1">You can only modify your email by contacting us at support@bizwatch.net</p>
|
<p class="text-xs text-gray-500 mt-1">You can only modify your email by contacting us at support@bizwatch.net</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-row items-center justify-around md:space-x-4">
|
<div class="flex flex-row items-center justify-around md:space-x-4">
|
||||||
<div>
|
<div class="flex h-full justify-between flex-col">
|
||||||
<p class="text-sm font-medium text-gray-700 mb-1">Company Logo</p>
|
<p class="text-sm font-medium text-gray-700 mb-1">Company Logo</p>
|
||||||
<div class="w-20 h-20 w-full rounded-md flex items-center justify-center">
|
<div class="w-20 h-20 w-full rounded-md flex items-center justify-center relative">
|
||||||
<img src="https://placehold.co/80x80" alt="Company logo" class="max-w-full max-h-full" />
|
@if(user?.hasCompanyLogo){
|
||||||
|
<img src="{{ companyLogoUrl }}" alt="Company logo" class="max-w-full max-h-full" />
|
||||||
|
<div class="absolute top-[-0.5rem] right-[0rem] bg-white rounded-full p-1 shadow-md hover:cursor-pointer" (click)="deleteConfirm('logo')">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" class="w-4 h-4 text-gray-600">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
} @else {
|
||||||
|
<img src="assets/images/placeholder.png" class="max-w-full max-h-full" />
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="mt-2 w-full px-4 py-2 border border-gray-300 rounded-md shadow-sm text-sm font-medium text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"
|
class="mt-2 w-full px-4 py-2 border border-gray-300 rounded-md shadow-sm text-sm font-medium text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"
|
||||||
|
(click)="uploadCompanyLogo()"
|
||||||
>
|
>
|
||||||
Upload
|
Upload
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div class="flex h-full justify-between flex-col">
|
||||||
<p class="text-sm font-medium text-gray-700 mb-1">Your Profile Picture</p>
|
<p class="text-sm font-medium text-gray-700 mb-1">Your Profile Picture</p>
|
||||||
<div class="w-20 h-20 w-full rounded-md flex items-center justify-center">
|
<div class="w-20 h-20 w-full rounded-md flex items-center justify-center relative">
|
||||||
<img src="https://placehold.co/80x80" alt="Profile picture" class="max-w-full max-h-full" />
|
@if(user?.hasProfile){
|
||||||
|
<img src="{{ profileUrl }}" alt="Profile picture" class="max-w-full max-h-full" />
|
||||||
|
<div class="absolute top-[-0.5rem] right-[0rem] bg-white rounded-full p-1 shadow-md hover:cursor-pointer" (click)="deleteConfirm('profile')">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" class="w-4 h-4 text-gray-600">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
} @else {
|
||||||
|
<img src="assets/images/placeholder.png" class="max-w-full max-h-full" />
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="mt-2 w-full px-4 py-2 border border-gray-300 rounded-md shadow-sm text-sm font-medium text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"
|
class="mt-2 w-full px-4 py-2 border border-gray-300 rounded-md shadow-sm text-sm font-medium text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"
|
||||||
|
(click)="uploadProfile()"
|
||||||
>
|
>
|
||||||
Upload
|
Upload
|
||||||
</button>
|
</button>
|
||||||
|
|
@ -90,12 +111,12 @@
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label for="companyOverview" class="block text-sm font-medium text-gray-700">Company Overview</label>
|
<label for="companyOverview" class="block text-sm font-medium text-gray-700">Company Overview</label>
|
||||||
<quill-editor [(ngModel)]="user.companyOverview" name="companyOverview"></quill-editor>
|
<quill-editor [(ngModel)]="user.companyOverview" name="companyOverview" [modules]="quillModules"></quill-editor>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label for="offeredServices" class="block text-sm font-medium text-gray-700">Services We Offer</label>
|
<label for="offeredServices" class="block text-sm font-medium text-gray-700">Services We Offer</label>
|
||||||
<quill-editor [(ngModel)]="user.offeredServices" name="offeredServices"></quill-editor>
|
<quill-editor [(ngModel)]="user.offeredServices" name="offeredServices" [modules]="quillModules"></quill-editor>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
|
|
@ -103,76 +124,68 @@
|
||||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||||
<div>
|
<div>
|
||||||
<label for="state" class="block text-sm font-medium text-gray-700">State</label>
|
<label for="state" class="block text-sm font-medium text-gray-700">State</label>
|
||||||
<select id="state" name="state" [(ngModel)]="user.areasServed[0].state" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500">
|
|
||||||
<option value="TEXAS">TEXAS</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label for="county" class="block text-sm font-medium text-gray-700">County</label>
|
<label for="county" class="block text-sm font-medium text-gray-700">County</label>
|
||||||
<input type="text" id="county" name="county" [(ngModel)]="user.areasServed[0].county" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500" />
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@for (areasServed of user.areasServed; track areasServed; let i=$index){
|
||||||
|
<div class="grid grid-cols-1 md:grid-cols-2 md:gap-4 gap-1 mb-3 md:mb-1">
|
||||||
|
<div>
|
||||||
|
<ng-select [items]="selectOptions?.states" bindLabel="name" bindValue="value" [(ngModel)]="areasServed.state" name="state{{ i }}"> </ng-select>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<input type="text" id="county{{ i }}" name="county{{ i }}" [(ngModel)]="areasServed.county" class="block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
<div class="mt-2">
|
<div class="mt-2">
|
||||||
<button type="button" class="px-2 py-1 bg-green-500 text-white rounded-md mr-2">+</button>
|
<button type="button" class="px-2 py-1 bg-green-500 text-white rounded-md mr-2" (click)="addArea()">+</button>
|
||||||
<button type="button" class="px-2 py-1 bg-red-500 text-white rounded-md">-</button>
|
<button type="button" class="px-2 py-1 bg-red-500 text-white rounded-md" (click)="removeArea()">-</button>
|
||||||
<span class="text-sm text-gray-500 ml-2">[Add more Areas or remove existing ones.]</span>
|
<span class="text-sm text-gray-500 ml-2">[Add more Areas or remove existing ones.]</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<h3 class="text-lg font-medium text-gray-700 mb-2">Licensed In</h3>
|
<h3 class="text-lg font-medium text-gray-700 mb-2">Licensed In</h3>
|
||||||
@for (licensedIn of user.licensedIn; track licensedIn){
|
|
||||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||||
<div>
|
<div>
|
||||||
<label for="licenseState" class="block text-sm font-medium text-gray-700">State</label>
|
<label for="state" class="block text-sm font-medium text-gray-700">State</label>
|
||||||
<select id="licenseState" name="licenseState" [(ngModel)]="licensedIn.state" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500">
|
|
||||||
<option value="TEXAS">TEXAS</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label for="licenseNumber" class="block text-sm font-medium text-gray-700">License Number</label>
|
<label for="county" class="block text-sm font-medium text-gray-700">License Number</label>
|
||||||
<input type="text" id="licenseNumber" name="licenseNumber" [(ngModel)]="licensedIn.registerNo" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500" />
|
</div>
|
||||||
|
</div>
|
||||||
|
@for (licensedIn of user.licensedIn; track licensedIn; let i=$index){
|
||||||
|
<div class="grid grid-cols-1 md:grid-cols-2 md:gap-4 gap-1 mb-3 md:mb-1">
|
||||||
|
<div>
|
||||||
|
<ng-select [items]="selectOptions?.states" bindLabel="name" bindValue="value" [(ngModel)]="licensedIn.state" name="state{{ i }}"> </ng-select>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
id="licenseNumber{{ i }}"
|
||||||
|
name="licenseNumber{{ i }}"
|
||||||
|
[(ngModel)]="licensedIn.registerNo"
|
||||||
|
class="block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
<div class="mt-2">
|
<div class="mt-2">
|
||||||
<button type="button" class="px-2 py-1 bg-green-500 text-white rounded-md mr-2">+</button>
|
<button type="button" class="px-2 py-1 bg-green-500 text-white rounded-md mr-2" (click)="addLicence()">+</button>
|
||||||
<button type="button" class="px-2 py-1 bg-red-500 text-white rounded-md">-</button>
|
<button type="button" class="px-2 py-1 bg-red-500 text-white rounded-md" (click)="removeLicence()">-</button>
|
||||||
<span class="text-sm text-gray-500 ml-2">[Add more licenses or remove existing ones.]</span>
|
<span class="text-sm text-gray-500 ml-2">[Add more licenses or remove existing ones.]</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex justify-start">
|
<div class="flex justify-start">
|
||||||
<button type="submit" class="px-4 py-2 bg-blue-500 text-white rounded-md hover:bg-blue-600 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500">Update Profile</button>
|
<button type="submit" class="px-4 py-2 bg-blue-500 text-white rounded-md hover:bg-blue-600 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500" (click)="updateProfile(user)">
|
||||||
|
Update Profile
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
<div class="mt-8 max-lg:hidden">
|
||||||
<!-- <div class="mt-8">
|
|
||||||
<h3 class="text-lg font-medium text-gray-700 mb-2">Membership Level</h3>
|
|
||||||
<table class="min-w-full divide-y divide-gray-200">
|
|
||||||
<thead class="bg-gray-50">
|
|
||||||
<tr>
|
|
||||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">ID</th>
|
|
||||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Level</th>
|
|
||||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Start Date</th>
|
|
||||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Date Modified</th>
|
|
||||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">End Date</th>
|
|
||||||
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Status</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody class="bg-white divide-y divide-gray-200">
|
|
||||||
<tr>
|
|
||||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">1</td>
|
|
||||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">Business Broker</td>
|
|
||||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">May 24, 2024</td>
|
|
||||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">May 24, 2024</td>
|
|
||||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">Feb 12, 9999</td>
|
|
||||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">active</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div> -->
|
|
||||||
<div class="mt-8">
|
|
||||||
<h3 class="text-lg font-medium text-gray-700 mb-2">Membership Level</h3>
|
<h3 class="text-lg font-medium text-gray-700 mb-2">Membership Level</h3>
|
||||||
<div class="overflow-x-auto">
|
<div class="overflow-x-auto">
|
||||||
<div class="inline-block min-w-full">
|
<div class="inline-block min-w-full">
|
||||||
|
|
@ -190,12 +203,14 @@
|
||||||
</thead>
|
</thead>
|
||||||
<tbody class="bg-white divide-y divide-gray-200">
|
<tbody class="bg-white divide-y divide-gray-200">
|
||||||
<tr>
|
<tr>
|
||||||
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">1</td>
|
@for (subscription of userSubscriptions; track userSubscriptions){
|
||||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">Business Broker</td>
|
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">{{ subscription.id }}</td>
|
||||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">May 24, 2024</td>
|
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">{{ subscription.level }}</td>
|
||||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">May 24, 2024</td>
|
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">{{ subscription.start | date }}</td>
|
||||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">Feb 12, 9999</td>
|
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">{{ subscription.modified | date }}</td>
|
||||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">active</td>
|
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">{{ subscription.end | date }}</td>
|
||||||
|
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">{{ subscription.status }}</td>
|
||||||
|
}
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
@ -206,43 +221,50 @@
|
||||||
|
|
||||||
<!-- Responsive version for small screens -->
|
<!-- Responsive version for small screens -->
|
||||||
<div class="mt-8 sm:hidden">
|
<div class="mt-8 sm:hidden">
|
||||||
<h3 class="text-lg font-medium text-gray-700 mb-2">Membership Level</h3>
|
<h3 class="text-lg font-medium text-gray-700 mb-1">Membership Level</h3>
|
||||||
<div class="space-y-6">
|
<div class="space-y-2">
|
||||||
|
@for (subscription of userSubscriptions; track userSubscriptions){
|
||||||
<div class="bg-white shadow overflow-hidden sm:rounded-lg">
|
<div class="bg-white shadow overflow-hidden sm:rounded-lg">
|
||||||
<div class="px-4 py-5 sm:px-6">
|
<div class="px-4 py-5 sm:px-6">
|
||||||
<dl class="grid grid-cols-1 gap-x-4 gap-y-8 sm:grid-cols-2">
|
<dl class="grid grid-cols-1 gap-x-4 gap-y-2 sm:grid-cols-2">
|
||||||
<div class="sm:col-span-1">
|
<div class="sm:col-span-1 flex">
|
||||||
<dt class="text-sm font-medium text-gray-500">ID</dt>
|
<dt class="text-sm font-bold text-gray-500 mr-2">ID</dt>
|
||||||
<dd class="mt-1 text-sm text-gray-900">1</dd>
|
<dd class="text-sm text-gray-900">{{ subscription.id }}</dd>
|
||||||
</div>
|
</div>
|
||||||
<div class="sm:col-span-1">
|
<div class="sm:col-span-1 flex">
|
||||||
<dt class="text-sm font-medium text-gray-500">Level</dt>
|
<dt class="text-sm font-bold text-gray-500 mr-2">Level</dt>
|
||||||
<dd class="mt-1 text-sm text-gray-900">Business Broker</dd>
|
<dd class="text-sm text-gray-900">{{ subscription.level }}</dd>
|
||||||
</div>
|
</div>
|
||||||
<div class="sm:col-span-1">
|
<div class="sm:col-span-1 flex">
|
||||||
<dt class="text-sm font-medium text-gray-500">Start Date</dt>
|
<dt class="text-sm font-bold text-gray-500 mr-2">Start Date</dt>
|
||||||
<dd class="mt-1 text-sm text-gray-900">May 24, 2024</dd>
|
<dd class="text-sm text-gray-900">{{ subscription.start | date }}</dd>
|
||||||
</div>
|
</div>
|
||||||
<div class="sm:col-span-1">
|
<div class="sm:col-span-1 flex">
|
||||||
<dt class="text-sm font-medium text-gray-500">Date Modified</dt>
|
<dt class="text-sm font-bold text-gray-500 mr-2">Date Modified</dt>
|
||||||
<dd class="mt-1 text-sm text-gray-900">May 24, 2024</dd>
|
<dd class="text-sm text-gray-900">{{ subscription.modified | date }}</dd>
|
||||||
</div>
|
</div>
|
||||||
<div class="sm:col-span-1">
|
<div class="sm:col-span-1 flex">
|
||||||
<dt class="text-sm font-medium text-gray-500">End Date</dt>
|
<dt class="text-sm font-bold text-gray-500 mr-2">End Date</dt>
|
||||||
<dd class="mt-1 text-sm text-gray-900">Feb 12, 9999</dd>
|
<dd class="text-sm text-gray-900">{{ subscription.end | date }}</dd>
|
||||||
</div>
|
</div>
|
||||||
<div class="sm:col-span-1">
|
<div class="sm:col-span-1 flex">
|
||||||
<dt class="text-sm font-medium text-gray-500">Status</dt>
|
<dt class="text-sm font-bold text-gray-500 mr-2">Status</dt>
|
||||||
<dd class="mt-1 text-sm text-gray-900">active</dd>
|
<dd class="text-sm text-gray-900">{{ subscription.status }}</dd>
|
||||||
</div>
|
</div>
|
||||||
</dl>
|
</dl>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
|
<!-- @if(showModal){ -->
|
||||||
|
<app-image-crop-and-upload [uploadParams]="uploadParams" (uploadFinished)="uploadFinished($event)"></app-image-crop-and-upload>
|
||||||
|
<app-confirmation></app-confirmation>
|
||||||
|
<app-message></app-message>
|
||||||
|
<!-- } -->
|
||||||
<!-- <div class="surface-ground px-4 py-8 md:px-6 lg:px-8">
|
<!-- <div class="surface-ground px-4 py-8 md:px-6 lg:px-8">
|
||||||
<div class="p-fluid flex flex-column lg:flex-row">
|
<div class="p-fluid flex flex-column lg:flex-row">
|
||||||
<menu-account></menu-account>
|
<menu-account></menu-account>
|
||||||
|
|
|
||||||
|
|
@ -34,3 +34,9 @@
|
||||||
border-radius: 8px; /* Optional: Abrunden der linken unteren Ecke für ästhetische Zwecke */
|
border-radius: 8px; /* Optional: Abrunden der linken unteren Ecke für ästhetische Zwecke */
|
||||||
font-size: 0.7rem;
|
font-size: 0.7rem;
|
||||||
}
|
}
|
||||||
|
quill-editor {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
::ng-deep .ng-select.ng-select-single .ng-select-container {
|
||||||
|
height: 42px;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,25 @@
|
||||||
import { ChangeDetectorRef, Component } from '@angular/core';
|
import { ChangeDetectorRef, Component } from '@angular/core';
|
||||||
import { ActivatedRoute } from '@angular/router';
|
import { ActivatedRoute } from '@angular/router';
|
||||||
import { faTrash } from '@fortawesome/free-solid-svg-icons';
|
import { faTrash } from '@fortawesome/free-solid-svg-icons';
|
||||||
|
import { NgSelectModule } from '@ng-select/ng-select';
|
||||||
import { KeycloakService } from 'keycloak-angular';
|
import { KeycloakService } from 'keycloak-angular';
|
||||||
|
import { NgxCurrencyDirective } from 'ngx-currency';
|
||||||
|
import { ImageCropperComponent } from 'ngx-image-cropper';
|
||||||
|
import { QuillModule } from 'ngx-quill';
|
||||||
import { lastValueFrom } from 'rxjs';
|
import { lastValueFrom } from 'rxjs';
|
||||||
import { User } from '../../../../../../bizmatch-server/src/models/db.model';
|
import { User } from '../../../../../../bizmatch-server/src/models/db.model';
|
||||||
import { AutoCompleteCompleteEvent, Invoice, Subscription, emailToDirName } from '../../../../../../bizmatch-server/src/models/main.model';
|
import { AutoCompleteCompleteEvent, Invoice, Subscription, UploadParams, emailToDirName } from '../../../../../../bizmatch-server/src/models/main.model';
|
||||||
import { environment } from '../../../../environments/environment';
|
import { environment } from '../../../../environments/environment';
|
||||||
|
import { ConfirmationComponent } from '../../../components/confirmation/confirmation.component';
|
||||||
|
import { ConfirmationService } from '../../../components/confirmation/confirmation.service';
|
||||||
|
import { ImageCropAndUploadComponent, UploadReponse } from '../../../components/image-crop-and-upload/image-crop-and-upload.component';
|
||||||
|
import { MessageComponent } from '../../../components/message/message.component';
|
||||||
|
import { MessageService } from '../../../components/message/message.service';
|
||||||
import { GeoService } from '../../../services/geo.service';
|
import { GeoService } from '../../../services/geo.service';
|
||||||
import { ImageService } from '../../../services/image.service';
|
import { ImageService } from '../../../services/image.service';
|
||||||
import { LoadingService } from '../../../services/loading.service';
|
import { LoadingService } from '../../../services/loading.service';
|
||||||
import { SelectOptionsService } from '../../../services/select-options.service';
|
import { SelectOptionsService } from '../../../services/select-options.service';
|
||||||
|
import { SharedService } from '../../../services/shared.service';
|
||||||
import { SubscriptionsService } from '../../../services/subscriptions.service';
|
import { SubscriptionsService } from '../../../services/subscriptions.service';
|
||||||
import { UserService } from '../../../services/user.service';
|
import { UserService } from '../../../services/user.service';
|
||||||
import { SharedModule } from '../../../shared/shared/shared.module';
|
import { SharedModule } from '../../../shared/shared/shared.module';
|
||||||
|
|
@ -18,7 +28,7 @@ import { TOOLBAR_OPTIONS } from '../../utils/defaults';
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-account',
|
selector: 'app-account',
|
||||||
standalone: true,
|
standalone: true,
|
||||||
imports: [SharedModule],
|
imports: [SharedModule, QuillModule, NgxCurrencyDirective, NgSelectModule, ImageCropperComponent, ConfirmationComponent, ImageCropAndUploadComponent, MessageComponent],
|
||||||
providers: [],
|
providers: [],
|
||||||
templateUrl: './account.component.html',
|
templateUrl: './account.component.html',
|
||||||
styleUrl: './account.component.scss',
|
styleUrl: './account.component.scss',
|
||||||
|
|
@ -40,6 +50,10 @@ export class AccountComponent {
|
||||||
faTrash = faTrash;
|
faTrash = faTrash;
|
||||||
customerTypes = ['buyer', 'professional'];
|
customerTypes = ['buyer', 'professional'];
|
||||||
customerSubTypes = ['broker', 'cpa', 'attorney', 'titleCompany', 'surveyor', 'appraiser'];
|
customerSubTypes = ['broker', 'cpa', 'attorney', 'titleCompany', 'surveyor', 'appraiser'];
|
||||||
|
quillModules = {
|
||||||
|
toolbar: [['bold', 'italic', 'underline', 'strike'], [{ list: 'ordered' }, { list: 'bullet' }], [{ header: [1, 2, 3, 4, 5, 6, false] }], [{ color: [] }, { background: [] }], ['clean']],
|
||||||
|
};
|
||||||
|
uploadParams: UploadParams;
|
||||||
constructor(
|
constructor(
|
||||||
public userService: UserService,
|
public userService: UserService,
|
||||||
private subscriptionService: SubscriptionsService,
|
private subscriptionService: SubscriptionsService,
|
||||||
|
|
@ -51,6 +65,9 @@ export class AccountComponent {
|
||||||
private imageUploadService: ImageService,
|
private imageUploadService: ImageService,
|
||||||
private imageService: ImageService,
|
private imageService: ImageService,
|
||||||
private keycloakService: KeycloakService,
|
private keycloakService: KeycloakService,
|
||||||
|
private confirmationService: ConfirmationService,
|
||||||
|
private messageService: MessageService,
|
||||||
|
private sharedService: SharedService,
|
||||||
) {}
|
) {}
|
||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
if (this.id) {
|
if (this.id) {
|
||||||
|
|
@ -121,6 +138,39 @@ export class AccountComponent {
|
||||||
get isProfessional() {
|
get isProfessional() {
|
||||||
return this.user.customerType === 'professional';
|
return this.user.customerType === 'professional';
|
||||||
}
|
}
|
||||||
|
uploadCompanyLogo() {
|
||||||
|
this.uploadParams = { type: 'uploadCompanyLogo', imagePath: emailToDirName(this.user.email) };
|
||||||
|
}
|
||||||
|
uploadProfile() {
|
||||||
|
this.uploadParams = { type: 'uploadProfile', imagePath: emailToDirName(this.user.email) };
|
||||||
|
}
|
||||||
|
async uploadFinished(response: UploadReponse) {
|
||||||
|
if (response.success) {
|
||||||
|
if (response.type === 'uploadCompanyLogo') {
|
||||||
|
this.user.hasCompanyLogo = true; //
|
||||||
|
this.companyLogoUrl = `${this.env.imageBaseUrl}/pictures/logo/${emailToDirName(this.user.email)}.avif?_ts=${new Date().getTime()}`;
|
||||||
|
} else {
|
||||||
|
this.user.hasProfile = true;
|
||||||
|
this.profileUrl = `${this.env.imageBaseUrl}/pictures/profile/${emailToDirName(this.user.email)}.avif?_ts=${new Date().getTime()}`;
|
||||||
|
this.sharedService.changeProfilePhoto(this.profileUrl);
|
||||||
|
}
|
||||||
|
await this.userService.save(this.user);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async deleteConfirm(type: 'profile' | 'logo') {
|
||||||
|
const confirmed = await this.confirmationService.showConfirmation(`Do you want to delete your ${type === 'logo' ? 'Logo' : 'Profile'} image`);
|
||||||
|
if (confirmed) {
|
||||||
|
if (type === 'profile') {
|
||||||
|
this.user.hasProfile = false;
|
||||||
|
await Promise.all([this.imageService.deleteProfileImagesById(this.user.email), this.userService.save(this.user)]);
|
||||||
|
} else {
|
||||||
|
this.user.hasCompanyLogo = false;
|
||||||
|
await Promise.all([this.imageService.deleteLogoImagesById(this.user.email), this.userService.save(this.user)]);
|
||||||
|
}
|
||||||
|
this.user = await this.userService.getById(this.user.id);
|
||||||
|
this.messageService.showMessage('Image deleted');
|
||||||
|
}
|
||||||
|
}
|
||||||
// select(event: any, type: 'company' | 'profile') {
|
// select(event: any, type: 'company' | 'profile') {
|
||||||
// const imageUrl = URL.createObjectURL(event.files[0]);
|
// const imageUrl = URL.createObjectURL(event.files[0]);
|
||||||
// this.type = type;
|
// this.type = type;
|
||||||
|
|
@ -167,31 +217,30 @@ export class AccountComponent {
|
||||||
// });
|
// });
|
||||||
// });
|
// });
|
||||||
// }
|
// }
|
||||||
// deleteConfirm(type: 'profile' | 'logo') {
|
|
||||||
// this.confirmationService.confirm({
|
|
||||||
// target: event.target as EventTarget,
|
|
||||||
// message: `Do you want to delete your ${type === 'logo' ? 'Logo' : 'Profile'} image`,
|
|
||||||
// header: 'Delete Confirmation',
|
|
||||||
// icon: 'pi pi-info-circle',
|
|
||||||
// acceptButtonStyleClass: 'p-button-danger p-button-text',
|
|
||||||
// rejectButtonStyleClass: 'p-button-text p-button-text',
|
|
||||||
// acceptIcon: 'none',
|
|
||||||
// rejectIcon: 'none',
|
|
||||||
|
|
||||||
// accept: async () => {
|
// this.confirmationService.showConfirmation({
|
||||||
// if (type === 'profile') {
|
// target: event.target as EventTarget,
|
||||||
// this.user.hasProfile = false;
|
// message: `Do you want to delete your ${type === 'logo' ? 'Logo' : 'Profile'} image`,
|
||||||
// await Promise.all([this.imageService.deleteProfileImagesById(this.user.email), this.userService.save(this.user)]);
|
// header: 'Delete Confirmation',
|
||||||
// } else {
|
// icon: 'pi pi-info-circle',
|
||||||
// this.user.hasCompanyLogo = false;
|
// acceptButtonStyleClass: 'p-button-danger p-button-text',
|
||||||
// await Promise.all([this.imageService.deleteLogoImagesById(this.user.email), this.userService.save(this.user)]);
|
// rejectButtonStyleClass: 'p-button-text p-button-text',
|
||||||
// }
|
// acceptIcon: 'none',
|
||||||
// this.messageService.add({ severity: 'info', summary: 'Confirmed', detail: 'Image deleted' });
|
// rejectIcon: 'none',
|
||||||
// this.user = await this.userService.getById(this.user.id);
|
|
||||||
// },
|
// accept: async () => {
|
||||||
// reject: () => {
|
// if (type === 'profile') {
|
||||||
// console.log('deny');
|
// this.user.hasProfile = false;
|
||||||
// },
|
// await Promise.all([this.imageService.deleteProfileImagesById(this.user.email), this.userService.save(this.user)]);
|
||||||
// });
|
// } else {
|
||||||
// }
|
// this.user.hasCompanyLogo = false;
|
||||||
|
// await Promise.all([this.imageService.deleteLogoImagesById(this.user.email), this.userService.save(this.user)]);
|
||||||
|
// }
|
||||||
|
// this.messageService.add({ severity: 'info', summary: 'Confirmed', detail: 'Image deleted' });
|
||||||
|
// this.user = await this.userService.getById(this.user.id);
|
||||||
|
// },
|
||||||
|
// reject: () => {
|
||||||
|
// console.log('deny');
|
||||||
|
// },
|
||||||
|
// });
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -160,6 +160,7 @@
|
||||||
<div class="container mx-auto p-4">
|
<div class="container mx-auto p-4">
|
||||||
<div class="bg-white rounded-lg shadow-md p-6">
|
<div class="bg-white rounded-lg shadow-md p-6">
|
||||||
<h1 class="text-2xl font-semibold mb-6">Edit Listing</h1>
|
<h1 class="text-2xl font-semibold mb-6">Edit Listing</h1>
|
||||||
|
@if(listing){
|
||||||
<form #listingForm="ngForm">
|
<form #listingForm="ngForm">
|
||||||
<div class="mb-4">
|
<div class="mb-4">
|
||||||
<label for="listingsCategory" class="block text-sm font-bold text-gray-700 mb-1">Listing category</label>
|
<label for="listingsCategory" class="block text-sm font-bold text-gray-700 mb-1">Listing category</label>
|
||||||
|
|
@ -302,5 +303,6 @@
|
||||||
<button (click)="save()" class="bg-blue-500 text-white px-4 py-2 rounded-md hover:bg-blue-600">Update Listing</button>
|
<button (click)="save()" class="bg-blue-500 text-white px-4 py-2 rounded-md hover:bg-blue-600">Update Listing</button>
|
||||||
}
|
}
|
||||||
</form>
|
</form>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -26,3 +26,9 @@
|
||||||
.dot {
|
.dot {
|
||||||
transition: all 0.3s ease-in-out;
|
transition: all 0.3s ease-in-out;
|
||||||
}
|
}
|
||||||
|
::ng-deep .ng-select.ng-select-single .ng-select-container {
|
||||||
|
height: 42px;
|
||||||
|
}
|
||||||
|
quill-editor {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -57,7 +57,9 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
// -------------------------------
|
// -------------------------------
|
||||||
|
quill-editor {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
.grid-container {
|
.grid-container {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
|
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
|
||||||
|
|
@ -104,3 +106,6 @@
|
||||||
.grid-container.cdk-drop-list-dragging .grid-item:not(.cdk-drag-placeholder) {
|
.grid-container.cdk-drop-list-dragging .grid-item:not(.cdk-drag-placeholder) {
|
||||||
transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);
|
transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);
|
||||||
}
|
}
|
||||||
|
::ng-deep .ng-select.ng-select-single .ng-select-container {
|
||||||
|
height: 42px;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,65 @@
|
||||||
|
<div class="container mx-auto p-4">
|
||||||
|
<div class="bg-white rounded-lg shadow-md p-6">
|
||||||
|
<h2 class="text-2xl font-semibold mb-6">Contact Us</h2>
|
||||||
|
<form #contactForm="ngForm" class="space-y-4">
|
||||||
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||||
|
<div>
|
||||||
|
<label for="name" class="block text-sm font-medium text-gray-700">Your name</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
id="name"
|
||||||
|
name="name"
|
||||||
|
[(ngModel)]="mailinfo.sender.name"
|
||||||
|
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-300 focus:ring focus:ring-blue-200 focus:ring-opacity-50"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="email" class="block text-sm font-medium text-gray-700">Your Email</label>
|
||||||
|
<input
|
||||||
|
type="email"
|
||||||
|
id="email"
|
||||||
|
name="email"
|
||||||
|
[(ngModel)]="mailinfo.sender.email"
|
||||||
|
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-300 focus:ring focus:ring-blue-200 focus:ring-opacity-50"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="phone" class="block text-sm font-medium text-gray-700">Your Phone</label>
|
||||||
|
<input
|
||||||
|
type="tel"
|
||||||
|
id="phone"
|
||||||
|
name="phone"
|
||||||
|
[(ngModel)]="mailinfo.sender.phoneNumber"
|
||||||
|
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-300 focus:ring focus:ring-blue-200 focus:ring-opacity-50"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="message" class="block text-sm font-medium text-gray-700">How can we help you ?</label>
|
||||||
|
<textarea
|
||||||
|
id="message"
|
||||||
|
name="message"
|
||||||
|
rows="4"
|
||||||
|
[(ngModel)]="mailinfo.sender.comments"
|
||||||
|
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-300 focus:ring focus:ring-blue-200 focus:ring-opacity-50"
|
||||||
|
required
|
||||||
|
></textarea>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
class="w-full md:w-auto px-6 py-2 border border-transparent text-base font-medium rounded-md text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
|
||||||
|
(click)="mail()"
|
||||||
|
>
|
||||||
|
Submit
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<!-- <div class="surface-ground px-4 py-8 md:px-6 lg:px-8 h-full">
|
<!-- <div class="surface-ground px-4 py-8 md:px-6 lg:px-8 h-full">
|
||||||
<div class="p-fluid flex flex-column lg:flex-row">
|
<div class="p-fluid flex flex-column lg:flex-row">
|
||||||
<menu-account></menu-account>
|
<menu-account></menu-account>
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,94 @@
|
||||||
|
<div class="container mx-auto p-4">
|
||||||
|
<div class="bg-white rounded-lg shadow-md p-6">
|
||||||
|
<h1 class="text-2xl font-bold mb-4">My Listings</h1>
|
||||||
|
|
||||||
|
<!-- Desktop view -->
|
||||||
|
<div class="hidden md:block">
|
||||||
|
<table class="w-full bg-white shadow-md rounded-lg overflow-hidden">
|
||||||
|
<thead class="bg-gray-100">
|
||||||
|
<tr>
|
||||||
|
<th class="py-2 px-4 text-left">Title</th>
|
||||||
|
<th class="py-2 px-4 text-left">Category</th>
|
||||||
|
<th class="py-2 px-4 text-left">Located in</th>
|
||||||
|
<th class="py-2 px-4 text-left">Actions</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr *ngFor="let listing of myListings" class="border-b">
|
||||||
|
<td class="py-2 px-4">{{ listing.title }}</td>
|
||||||
|
<td class="py-2 px-4">{{ listing.listingsCategory === 'commercialProperty' ? 'Commercial Property' : 'Business' }}</td>
|
||||||
|
<td class="py-2 px-4">{{ listing.state }}</td>
|
||||||
|
<td class="py-2 px-4">
|
||||||
|
@if(listing.listingsCategory==='business'){
|
||||||
|
<button class="bg-green-500 text-white p-2 rounded-full mr-2" [routerLink]="['/editBusinessListing', listing.id]">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
|
||||||
|
<path d="M13.586 3.586a2 2 0 112.828 2.828l-.793.793-2.828-2.828.793-.793zM11.379 5.793L3 14.172V17h2.828l8.38-8.379-2.83-2.828z" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
} @if(listing.listingsCategory==='commercialProperty'){
|
||||||
|
<button class="bg-green-500 text-white p-2 rounded-full mr-2" [routerLink]="['/editCommercialPropertyListing', listing.id]">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
|
||||||
|
<path d="M13.586 3.586a2 2 0 112.828 2.828l-.793.793-2.828-2.828.793-.793zM11.379 5.793L3 14.172V17h2.828l8.38-8.379-2.83-2.828z" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
}
|
||||||
|
<button class="bg-orange-500 text-white p-2 rounded-full" (click)="confirm(listing)">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
|
||||||
|
<path
|
||||||
|
fill-rule="evenodd"
|
||||||
|
d="M9 2a1 1 0 00-.894.553L7.382 4H4a1 1 0 000 2v10a2 2 0 002 2h8a2 2 0 002-2V6a1 1 0 100-2h-3.382l-.724-1.447A1 1 0 0011 2H9zM7 8a1 1 0 012 0v6a1 1 0 11-2 0V8zm5-1a1 1 0 00-1 1v6a1 1 0 102 0V8a1 1 0 00-1-1z"
|
||||||
|
clip-rule="evenodd"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Mobile view -->
|
||||||
|
<div class="md:hidden">
|
||||||
|
<div *ngFor="let listing of myListings" class="bg-white shadow-md rounded-lg p-4 mb-4">
|
||||||
|
<h2 class="text-xl font-semibold mb-2">{{ listing.title }}</h2>
|
||||||
|
<p class="text-gray-600 mb-2">Category: {{ listing.listingsCategory === 'commercialProperty' ? 'Commercial Property' : 'Business' }}</p>
|
||||||
|
<p class="text-gray-600 mb-4">Located in: {{ listing.state }}</p>
|
||||||
|
<div class="flex justify-end">
|
||||||
|
<button class="bg-green-500 text-white p-2 rounded-full mr-2">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
|
||||||
|
<path d="M13.586 3.586a2 2 0 112.828 2.828l-.793.793-2.828-2.828.793-.793zM11.379 5.793L3 14.172V17h2.828l8.38-8.379-2.83-2.828z" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
<button class="bg-orange-500 text-white p-2 rounded-full">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
|
||||||
|
<path
|
||||||
|
fill-rule="evenodd"
|
||||||
|
d="M9 2a1 1 0 00-.894.553L7.382 4H4a1 1 0 000 2v10a2 2 0 002 2h8a2 2 0 002-2V6a1 1 0 100-2h-3.382l-.724-1.447A1 1 0 0011 2H9zM7 8a1 1 0 012 0v6a1 1 0 11-2 0V8zm5-1a1 1 0 00-1 1v6a1 1 0 102 0V8a1 1 0 00-1-1z"
|
||||||
|
clip-rule="evenodd"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex items-center justify-between mt-4">
|
||||||
|
<p class="text-sm text-gray-600">Showing 1 to 2 of 2 entries</p>
|
||||||
|
<div class="flex items-center">
|
||||||
|
<button class="px-2 py-1 border rounded-l-md bg-gray-100"><<</button>
|
||||||
|
<button class="px-2 py-1 border-t border-b bg-gray-100"><</button>
|
||||||
|
<button class="px-2 py-1 border bg-blue-500 text-white">1</button>
|
||||||
|
<button class="px-2 py-1 border-t border-b bg-gray-100">></button>
|
||||||
|
<button class="px-2 py-1 border rounded-r-md bg-gray-100">>></button>
|
||||||
|
<select class="ml-2 border rounded-md px-2 py-1">
|
||||||
|
<option>10</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<app-confirmation></app-confirmation>
|
||||||
|
<app-message></app-message>
|
||||||
<!-- <div class="surface-ground px-4 py-8 md:px-6 lg:px-8 h-full">
|
<!-- <div class="surface-ground px-4 py-8 md:px-6 lg:px-8 h-full">
|
||||||
<div class="p-fluid flex flex-column lg:flex-row">
|
<div class="p-fluid flex flex-column lg:flex-row">
|
||||||
<menu-account></menu-account>
|
<menu-account></menu-account>
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,10 @@ import { ChangeDetectorRef, Component } from '@angular/core';
|
||||||
import { KeycloakService } from 'keycloak-angular';
|
import { KeycloakService } from 'keycloak-angular';
|
||||||
import { CommercialPropertyListing, User } from '../../../../../../bizmatch-server/src/models/db.model';
|
import { CommercialPropertyListing, User } from '../../../../../../bizmatch-server/src/models/db.model';
|
||||||
import { ListingType } from '../../../../../../bizmatch-server/src/models/main.model';
|
import { ListingType } from '../../../../../../bizmatch-server/src/models/main.model';
|
||||||
|
import { ConfirmationComponent } from '../../../components/confirmation/confirmation.component';
|
||||||
|
import { ConfirmationService } from '../../../components/confirmation/confirmation.service';
|
||||||
|
import { MessageComponent } from '../../../components/message/message.component';
|
||||||
|
import { MessageService } from '../../../components/message/message.service';
|
||||||
import { ListingsService } from '../../../services/listings.service';
|
import { ListingsService } from '../../../services/listings.service';
|
||||||
import { SelectOptionsService } from '../../../services/select-options.service';
|
import { SelectOptionsService } from '../../../services/select-options.service';
|
||||||
import { UserService } from '../../../services/user.service';
|
import { UserService } from '../../../services/user.service';
|
||||||
|
|
@ -11,7 +15,7 @@ import { map2User } from '../../../utils/utils';
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-my-listing',
|
selector: 'app-my-listing',
|
||||||
standalone: true,
|
standalone: true,
|
||||||
imports: [SharedModule],
|
imports: [SharedModule, ConfirmationComponent, MessageComponent],
|
||||||
providers: [],
|
providers: [],
|
||||||
templateUrl: './my-listing.component.html',
|
templateUrl: './my-listing.component.html',
|
||||||
styleUrl: './my-listing.component.scss',
|
styleUrl: './my-listing.component.scss',
|
||||||
|
|
@ -20,7 +24,15 @@ export class MyListingComponent {
|
||||||
listings: Array<ListingType> = []; //dataListings as unknown as Array<BusinessListing>;
|
listings: Array<ListingType> = []; //dataListings as unknown as Array<BusinessListing>;
|
||||||
myListings: Array<ListingType>;
|
myListings: Array<ListingType>;
|
||||||
user: User;
|
user: User;
|
||||||
constructor(public userService: UserService, public keycloakService: KeycloakService, private listingsService: ListingsService, private cdRef: ChangeDetectorRef, public selectOptions: SelectOptionsService) {}
|
constructor(
|
||||||
|
public userService: UserService,
|
||||||
|
public keycloakService: KeycloakService,
|
||||||
|
private listingsService: ListingsService,
|
||||||
|
private cdRef: ChangeDetectorRef,
|
||||||
|
public selectOptions: SelectOptionsService,
|
||||||
|
private messageService: MessageService,
|
||||||
|
private confirmationService: ConfirmationService,
|
||||||
|
) {}
|
||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
// const keycloakUser = this.userService.getKeycloakUser();
|
// const keycloakUser = this.userService.getKeycloakUser();
|
||||||
const token = await this.keycloakService.getToken();
|
const token = await this.keycloakService.getToken();
|
||||||
|
|
@ -41,7 +53,12 @@ export class MyListingComponent {
|
||||||
this.myListings = [...result[0], ...result[1]];
|
this.myListings = [...result[0], ...result[1]];
|
||||||
}
|
}
|
||||||
|
|
||||||
confirm(event: Event, listing: ListingType) {
|
async confirm(listing: ListingType) {
|
||||||
|
const confirmed = await this.confirmationService.showConfirmation(`Are you sure you want to delete this listing?`);
|
||||||
|
if (confirmed) {
|
||||||
|
this.messageService.showMessage('Listing has been deleted');
|
||||||
|
this.deleteListing(listing);
|
||||||
|
}
|
||||||
// this.confirmationService.confirm({
|
// this.confirmationService.confirm({
|
||||||
// target: event.target as EventTarget,
|
// target: event.target as EventTarget,
|
||||||
// message: 'Are you sure you want to delet this listing?',
|
// message: 'Are you sure you want to delet this listing?',
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { BehaviorSubject } from 'rxjs';
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root',
|
||||||
|
})
|
||||||
|
export class SharedService {
|
||||||
|
private profilePhotoSource = new BehaviorSubject<string>(null);
|
||||||
|
currentProfilePhoto = this.profilePhotoSource.asObservable();
|
||||||
|
|
||||||
|
constructor() {}
|
||||||
|
|
||||||
|
changeProfilePhoto(photoUrl: string) {
|
||||||
|
this.profilePhotoSource.next(photoUrl);
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue