account Komponente

This commit is contained in:
Andreas Knuth 2024-07-09 21:19:51 +02:00
parent 7f67b81242
commit 08c179fa09
9 changed files with 306 additions and 322 deletions

View File

@ -1,14 +0,0 @@
<angular-cropper #cropper [imageUrl]="imageUrl" [cropperOptions]="cropperConfig"></angular-cropper>
<div class="flex justify-content-between mt-3">
@if(ratioVariable){
<div>
<!-- <p-selectButton [options]="stateOptions" [ngModel]="value" (ngModelChange)="changeAspectRation($event)" optionLabel="label" optionValue="value" class="small"></p-selectButton> -->
</div>
} @else {
<div></div>
}
<div class="flex justify-content-between">
<!-- <p-button icon="pi" (click)="cancelUpload()" label="Cancel" [outlined]="true" size="small" class="mr-2"></p-button>
<p-button icon="pi pi-check" (click)="sendImage()" label="Finish" pAutoFocus [autofocus]="true" size="small"></p-button> -->
</div>
</div>

View File

@ -1,4 +0,0 @@
::ng-deep p-selectbutton.small .p-button {
font-size: 0.875rem;
padding: 0.65625rem 1.09375rem;
}

View File

@ -1,48 +0,0 @@
import { Component, ViewChild } from '@angular/core';
import { AngularCropperjsModule, CropperComponent } from 'angular-cropperjs';
import { KeyValueRatio } from '../../../../../bizmatch-server/src/models/main.model';
import { ImageService } from '../../services/image.service';
import { LoadingService } from '../../services/loading.service';
import { SharedModule } from '../../shared/shared/shared.module';
export const stateOptions: KeyValueRatio[] = [
{ label: '16/9', value: 16 / 9 },
{ label: '1/1', value: 1 },
{ label: 'Free', value: NaN },
];
@Component({
selector: 'app-image-cropper',
standalone: true,
imports: [SharedModule, AngularCropperjsModule],
templateUrl: './image-cropper.component.html',
styleUrl: './image-cropper.component.scss',
})
export class ImageCropperComponent {
@ViewChild(CropperComponent) public angularCropper: CropperComponent;
imageUrl: string; //wird im Template verwendet
value: number = stateOptions[0].value;
cropperConfig = { aspectRatio: this.value };
ratioVariable: boolean;
stateOptions = stateOptions;
constructor(private loadingService: LoadingService, private imageUploadService: ImageService) {}
// ngOnInit(): void {
// if (this.config.data) {
// this.imageUrl = this.config.data.imageUrl;
// this.fileUpload = this.config.data.fileUpload;
// this.cropperConfig = this.config.data.config ? this.config.data.config : this.cropperConfig;
// this.ratioVariable = this.config.data.ratioVariable;
// }
// }
// sendImage() {
// this.fileUpload.clear();
// this.ref.close(this.angularCropper.cropper);
// }
// cancelUpload() {
// this.fileUpload.clear();
// this.ref.close();
// }
changeAspectRation(ratio: number) {
this.cropperConfig = { aspectRatio: ratio };
this.angularCropper.cropper.setAspectRatio(ratio);
}
}

View File

@ -1,197 +0,0 @@
import { CdkDrag, CdkDragEnter, CdkDragSortEvent, CdkDropList, CdkDropListGroup, DropListOrientation, moveItemInArray } from '@angular/cdk/drag-drop';
import { AfterViewInit, Directive, ElementRef, EventEmitter, Input, OnChanges, OnDestroy, OnInit, Output, Self, SimpleChanges, SkipSelf } from '@angular/core';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
export const autoScrollStep = 6;
@Directive({
selector: '[cdkDropListGroup][mixedCdkDragDrop]', // eslint-disable-line
})
export class MixedCdkDragDropDirective<T = any> implements OnChanges, AfterViewInit, OnDestroy {
/** @param {EventEmitter} dropped: emit previousIndex and currentIndex when dropList dropped. Valid when itemList is not being provided. **/
@Output() readonly dropped = new EventEmitter<{ previousIndex: number; currentIndex: number }>();
@Input() itemList: T[] | undefined;
@Input() orientation: DropListOrientation = 'horizontal';
@Input() containerSelector = '';
private readonly _resizeDragItem = new Set<MixedCdkDragSizeHelperDirective>();
private targetIndex = -1;
private sourceIndex = -1;
private source: CdkDropList | undefined;
private observer: ResizeObserver | undefined;
private currentContentRect: DOMRectReadOnly | undefined;
private animationFrame: number | undefined;
constructor(public element: ElementRef<HTMLElement>, @Self() private cdkDropListGroup: CdkDropListGroup<any>) {
this.observer = new ResizeObserver((entries: Array<ResizeObserverEntry>) => {
this.animationFrame = window.requestAnimationFrame(() => {
if (entries.length) {
const element = this.containerSelector ? entries[0] : entries.find((e: ResizeObserverEntry) => e.target === this.element.nativeElement);
if (element) {
this.currentContentRect = element.contentRect;
for (let item of this._resizeDragItem) {
item.onSizeChangeEmit(element.contentRect);
}
}
}
});
});
}
ngAfterViewInit() {
this.observeAll();
}
ngOnChanges(changes: SimpleChanges) {
if (changes['orientation']) {
this.cdkDropListGroup._items.forEach((i: CdkDropList) => {
i.orientation = this.orientation;
i.element.nativeElement.style.flexDirection = this.orientation === 'horizontal' ? 'row' : 'column';
});
}
if (changes['containerSelector']) {
this.observer?.disconnect();
this.observeAll();
}
}
addResizeDragItem(item: MixedCdkDragSizeHelperDirective) {
this._resizeDragItem.add(item);
if (this.currentContentRect) {
item.onSizeChangeEmit(this.currentContentRect);
}
}
deleteResizeDragItem(item: MixedCdkDragSizeHelperDirective) {
this._resizeDragItem.delete(item);
}
onDropListDropped() {
if (this.sourceIndex < 0 || this.targetIndex < 0) {
return;
}
// if sourceIndex is before targetIndex then the real target should minus one, to remove the source placeholder which being counted.
const target = this.targetIndex + (this.sourceIndex < this.targetIndex ? -1 : 0);
if (this.sourceIndex !== this.targetIndex && target >= 0) {
if (this.itemList) {
moveItemInArray(this.itemList, this.sourceIndex, target);
} else {
this.dropped.emit({
previousIndex: this.sourceIndex,
currentIndex: target,
});
}
this.sourceIndex = -1;
this.targetIndex = -1;
}
// reset
this.source = undefined;
}
onDropListEntered({ item, container, currentIndex }: CdkDragEnter | CdkDragSortEvent) {
// dropList which the cdkDrag currently entered.
const dropElement = container.element.nativeElement;
// get all the dropList nodes in dropListGroup
const dropListNodes = Array.from(dropElement.parentElement?.children ?? []);
// dropList which the cdkDrag originally belonged.
const sourceElement = item.dropContainer.element.nativeElement;
// might enter multiple dropList after drag start, should only keep the index from the first time
if (!this.source || this.sourceIndex === -1) {
this.sourceIndex = dropListNodes.indexOf(sourceElement);
this.source = item.dropContainer;
}
// target index should consider the currentIndex, which indicate drop before/after dropIndex (index of dropList which currently entered).
this.targetIndex = dropListNodes.indexOf(dropElement) + currentIndex;
}
private observeAll() {
if (this.containerSelector) {
const el = document.querySelector(this.containerSelector);
if (el) {
this.observer?.observe(el);
}
} else {
this.observer?.observe(this.element.nativeElement);
}
}
ngOnDestroy() {
this.observer?.disconnect();
this.observer = undefined;
this.currentContentRect = undefined;
this._resizeDragItem.clear();
if (this.animationFrame) {
window.cancelAnimationFrame(this.animationFrame);
}
}
}
@Directive({
selector: '[cdkDropList][mixedCdkDropList]', // eslint-disable-line
})
export class MixedCdkDropListDirective implements OnInit, OnDestroy {
private lifecycleEmitter = new Subject<void>();
constructor(@Self() private cdkDropList: CdkDropList, @SkipSelf() private mixedDragDrop: MixedCdkDragDropDirective) {}
ngOnInit() {
this.cdkDropList.autoScrollStep = autoScrollStep;
this.cdkDropList.orientation = this.mixedDragDrop.orientation;
this.cdkDropList.element.nativeElement.style.flexDirection = this.mixedDragDrop.orientation === 'horizontal' ? 'row' : 'column';
this.cdkDropList.element.nativeElement.style.display = 'flex';
this.cdkDropList.element.nativeElement.style.flexWrap = 'nowrap';
this.cdkDropList.element.nativeElement.style.width = 'fit-content';
this.cdkDropList.element.nativeElement.style.height = 'fit-content';
this.cdkDropList.sorted.pipe(takeUntil(this.lifecycleEmitter)).subscribe(event => this.mixedDragDrop.onDropListEntered(event));
this.cdkDropList.entered.pipe(takeUntil(this.lifecycleEmitter)).subscribe(event => this.mixedDragDrop.onDropListEntered(event));
this.cdkDropList.dropped.pipe(takeUntil(this.lifecycleEmitter)).subscribe(() => this.mixedDragDrop.onDropListDropped());
}
ngOnDestroy() {
this.lifecycleEmitter.next();
this.lifecycleEmitter.unsubscribe();
}
}
@Directive({
selector: '[cdkDrag][mixedCdkDragSizeHelper]', // eslint-disable-line
})
export class MixedCdkDragSizeHelperDirective implements AfterViewInit, OnDestroy {
@Output() contentBoxSize = new EventEmitter<{
drag: CdkDrag;
containerSize: DOMRectReadOnly;
}>();
constructor(@Self() private cdkDrag: CdkDrag, @SkipSelf() private mixedContainer: MixedCdkDragDropDirective) {}
ngAfterViewInit() {
this.mixedContainer.addResizeDragItem(this);
}
ngOnDestroy() {
this.mixedContainer.deleteResizeDragItem(this);
}
onSizeChangeEmit(rect: DOMRectReadOnly) {
this.contentBoxSize?.emit({ drag: this.cdkDrag, containerSize: rect });
}
/** @param {drag: CdkDrag, containerSize: DOMRectReadOnly} event: contentSize observer event.
* @param {number} percentWidth: set width to the percentage based on the dropListGroup Container width, valid from 0 to 100.
* @param {number} percentHeight: set width to the percentage based on the dropListGroup Container width, valid from 0 to 100. **/
static defaultEmitter(event: { drag: CdkDrag; containerSize: DOMRectReadOnly }, percentWidth: number, percentHeight: number) {
if (percentWidth) {
event.drag.element.nativeElement.style.width = `${(percentWidth * event.containerSize.width) / 100}px`;
} else {
event.drag.element.nativeElement.style.width = '';
}
if (percentHeight) {
event.drag.element.nativeElement.style.height = `${(percentHeight * event.containerSize.height) / 100}px`;
} else {
event.drag.element.nativeElement.style.height = '';
}
}
}

View File

@ -261,6 +261,8 @@
} }
</div> </div>
</div> </div>
} @if( user?.email===keycloakUser?.email || isAdmin()){
<button class="mt-4 bg-blue-500 text-white px-4 py-2 rounded hover:bg-blue-600" [routerLink]="['/account', user.id]">Edit</button>
} }
</div> </div>
</div> </div>

View File

@ -1,3 +1,248 @@
<div class="container mx-auto p-4">
<div class="bg-white rounded-lg shadow-md p-6">
<form #accountForm="ngForm" class="space-y-4">
<h2 class="text-2xl font-bold mb-4">Account Details</h2>
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
<div class="md:col-span-2">
<label for="email" class="block text-sm font-medium text-gray-700">E-mail (required)</label>
<input type="email" id="email" name="email" [(ngModel)]="user.email" required class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500" />
<p class="text-xs text-gray-500 mt-1">You can only modify your email by contacting us at support&#64;bizwatch.net</p>
</div>
<div class="flex flex-row items-center justify-around md:space-x-4">
<div>
<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">
<img src="https://placehold.co/80x80" alt="Company logo" class="max-w-full max-h-full" />
</div>
<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"
>
Upload
</button>
</div>
<div>
<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">
<img src="https://placehold.co/80x80" alt="Profile picture" class="max-w-full max-h-full" />
</div>
<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"
>
Upload
</button>
</div>
</div>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<label for="firstname" class="block text-sm font-medium text-gray-700">First Name</label>
<input type="text" id="firstname" name="firstname" [(ngModel)]="user.firstname" required class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500" />
</div>
<div>
<label for="lastname" class="block text-sm font-medium text-gray-700">Last Name</label>
<input type="text" id="lastname" name="lastname" [(ngModel)]="user.lastname" required class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500" />
</div>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<label for="customerType" class="block text-sm font-medium text-gray-700">Customer Type</label>
<select id="customerType" name="customerType" [(ngModel)]="user.customerType" required class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500">
<option *ngFor="let type of customerTypes" [value]="type">{{ type | titlecase }}</option>
</select>
</div>
<div>
<label for="customerSubType" class="block text-sm font-medium text-gray-700">Professional Type</label>
<select id="customerSubType" name="customerSubType" [(ngModel)]="user.customerSubType" required class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500">
<option *ngFor="let subType of customerSubTypes" [value]="subType">{{ subType | titlecase }}</option>
</select>
</div>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<label for="companyName" class="block text-sm font-medium text-gray-700">Company Name</label>
<input type="text" id="companyName" name="companyName" [(ngModel)]="user.companyName" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500" />
</div>
<div>
<label for="description" class="block text-sm font-medium text-gray-700">Describe yourself</label>
<input type="text" id="description" name="description" [(ngModel)]="user.description" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500" />
</div>
</div>
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
<div>
<label for="phoneNumber" class="block text-sm font-medium text-gray-700">Your Phone Number</label>
<input type="tel" id="phoneNumber" name="phoneNumber" [(ngModel)]="user.phoneNumber" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500" />
</div>
<div>
<label for="companyWebsite" class="block text-sm font-medium text-gray-700">Company Website</label>
<input type="url" id="companyWebsite" name="companyWebsite" [(ngModel)]="user.companyWebsite" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500" />
</div>
<div>
<label for="companyLocation" class="block text-sm font-medium text-gray-700">Company Location</label>
<input type="text" id="companyLocation" name="companyLocation" [(ngModel)]="user.companyLocation" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500" />
</div>
</div>
<div>
<label for="companyOverview" class="block text-sm font-medium text-gray-700">Company Overview</label>
<quill-editor [(ngModel)]="user.companyOverview" name="companyOverview"></quill-editor>
</div>
<div>
<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>
</div>
<div>
<h3 class="text-lg font-medium text-gray-700 mb-2">Areas We Serve</h3>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<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>
<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 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-red-500 text-white rounded-md">-</button>
<span class="text-sm text-gray-500 ml-2">[Add more Areas or remove existing ones.]</span>
</div>
</div>
<div>
<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>
<label for="licenseState" 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>
<label for="licenseNumber" 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>
}
<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-red-500 text-white rounded-md">-</button>
<span class="text-sm text-gray-500 ml-2">[Add more licenses or remove existing ones.]</span>
</div>
</div>
<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>
</div>
</form>
<!-- <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>
<div class="overflow-x-auto">
<div class="inline-block min-w-full">
<div class="shadow overflow-hidden border-b border-gray-200 sm:rounded-lg">
<table class="min-w-full divide-y divide-gray-200">
<thead class="bg-gray-50">
<tr>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">ID</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Level</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Start Date</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Date Modified</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">End Date</th>
<th scope="col" 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 font-medium text-gray-900">1</td>
<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 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>
</div>
</div>
<!-- Responsive version for small screens -->
<div class="mt-8 sm:hidden">
<h3 class="text-lg font-medium text-gray-700 mb-2">Membership Level</h3>
<div class="space-y-6">
<div class="bg-white shadow overflow-hidden sm:rounded-lg">
<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">
<div class="sm:col-span-1">
<dt class="text-sm font-medium text-gray-500">ID</dt>
<dd class="mt-1 text-sm text-gray-900">1</dd>
</div>
<div class="sm:col-span-1">
<dt class="text-sm font-medium text-gray-500">Level</dt>
<dd class="mt-1 text-sm text-gray-900">Business Broker</dd>
</div>
<div class="sm:col-span-1">
<dt class="text-sm font-medium text-gray-500">Start Date</dt>
<dd class="mt-1 text-sm text-gray-900">May 24, 2024</dd>
</div>
<div class="sm:col-span-1">
<dt class="text-sm font-medium text-gray-500">Date Modified</dt>
<dd class="mt-1 text-sm text-gray-900">May 24, 2024</dd>
</div>
<div class="sm:col-span-1">
<dt class="text-sm font-medium text-gray-500">End Date</dt>
<dd class="mt-1 text-sm text-gray-900">Feb 12, 9999</dd>
</div>
<div class="sm:col-span-1">
<dt class="text-sm font-medium text-gray-500">Status</dt>
<dd class="mt-1 text-sm text-gray-900">active</dd>
</div>
</dl>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- <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>

View File

@ -38,6 +38,8 @@ export class AccountComponent {
editorModules = TOOLBAR_OPTIONS; editorModules = TOOLBAR_OPTIONS;
env = environment; env = environment;
faTrash = faTrash; faTrash = faTrash;
customerTypes = ['buyer', 'professional'];
customerSubTypes = ['broker', 'cpa', 'attorney', 'titleCompany', 'surveyor', 'appraiser'];
constructor( constructor(
public userService: UserService, public userService: UserService,
private subscriptionService: SubscriptionsService, private subscriptionService: SubscriptionsService,

View File

@ -158,21 +158,19 @@ export class EditCommercialPropertyListingComponent {
} }
closeModal() { closeModal() {
this.showModal = false; this.imageChangedEvent = null;
this.imageChangedEvent = '';
this.croppedImage = null; this.croppedImage = null;
this.showModal = false;
} }
uploadImage() { uploadImage() {
if (this.croppedImage) { if (this.croppedImage) {
// Convert base64 to blob
// Replace with your actual API endpoint
this.imageService.uploadImage(this.croppedImage, 'uploadPropertyPicture', this.listing.imagePath, this.listing.serialId).subscribe( this.imageService.uploadImage(this.croppedImage, 'uploadPropertyPicture', this.listing.imagePath, this.listing.serialId).subscribe(
async response => { async () => {
console.log('Upload successful', response); //console.log('Upload successful', response);
setTimeout(async () => { //setTimeout(async () => {
this.listing = await lastValueFrom(this.listingsService.getListingById(this.id, 'commercialProperty')); this.listing = await lastValueFrom(this.listingsService.getListingById(this.id, 'commercialProperty'));
}); //}, 10);
this.closeModal(); this.closeModal();
}, },
error => { error => {
@ -181,6 +179,28 @@ export class EditCommercialPropertyListingComponent {
); );
} }
} }
async deleteConfirm(imageName: string) {
const confirmed = await this.confirmationService.showConfirmation('Are you sure you want to delete this image?');
if (confirmed) {
this.listing.imageOrder = this.listing.imageOrder.filter(item => item !== imageName);
// await Promise.all([, ]);
await this.imageService.deleteListingImage(this.listing.imagePath, this.listing.serialId, imageName);
await this.listingsService.save(this.listing, 'commercialProperty');
this.listing = await lastValueFrom(this.listingsService.getListingById(this.id, 'commercialProperty'));
this.messageService.showMessage('Image deleted');
} else {
console.log('deny');
}
}
changeListingCategory(value: 'business' | 'commercialProperty') {
routeListingWithState(this.router, value, this.listing);
}
imageOrderChanged(imageOrder: string[]) {
this.listing.imageOrder = imageOrder;
}
// select(event: any) { // select(event: any) {
// const imageUrl = URL.createObjectURL(event.files[0]); // const imageUrl = URL.createObjectURL(event.files[0]);
// getImageDimensions(imageUrl).then(dimensions => { // getImageDimensions(imageUrl).then(dimensions => {
@ -223,23 +243,4 @@ export class EditCommercialPropertyListingComponent {
// }); // });
// }); // });
// } // }
async deleteConfirm(imageName: string) {
const confirmed = await this.confirmationService.showConfirmation('Are you sure you want to delete this image?');
if (confirmed) {
this.listing.imageOrder = this.listing.imageOrder.filter(item => item !== imageName);
await Promise.all([this.imageService.deleteListingImage(this.listing.imagePath, this.listing.serialId, imageName), this.listingsService.save(this.listing, 'commercialProperty')]);
this.listing = await lastValueFrom(this.listingsService.getListingById(this.id, 'commercialProperty'));
this.messageService.showMessage('Image deleted');
} else {
console.log('deny');
}
}
changeListingCategory(value: 'business' | 'commercialProperty') {
routeListingWithState(this.router, value, this.listing);
}
imageOrderChanged(imageOrder: string[]) {
this.listing.imageOrder = imageOrder;
}
} }

View File

@ -2,39 +2,36 @@ import { Injectable } from '@angular/core';
import { BehaviorSubject, Observable, debounceTime, distinctUntilChanged, map, shareReplay } from 'rxjs'; import { BehaviorSubject, Observable, debounceTime, distinctUntilChanged, map, shareReplay } from 'rxjs';
@Injectable({ @Injectable({
providedIn: 'root', providedIn: 'root',
}) })
export class LoadingService { export class LoadingService {
public loading$ = new BehaviorSubject<string[]>([]); public loading$ = new BehaviorSubject<string[]>([]);
private loadingTextSubject = new BehaviorSubject<string | null>(null);
loadingText$: Observable<string | null> = this.loadingTextSubject.asObservable();
public isLoading$ = this.loading$.asObservable().pipe( private loadingTextSubject = new BehaviorSubject<string | null>(null);
map((loading) => loading.length > 0), loadingText$: Observable<string | null> = this.loadingTextSubject.asObservable();
debounceTime(200),
distinctUntilChanged(),
shareReplay(1)
);
public startLoading(type: string,request?:string): void { public isLoading$ = this.loading$.asObservable().pipe(
if (!this.loading$.value.includes(type)) { map(loading => loading.length > 0),
this.loading$.next(this.loading$.value.concat(type)); debounceTime(200),
if (type==='uploadImage' distinctUntilChanged(),
|| request?.includes('uploadPropertyPicture') shareReplay(1),
|| request?.includes('uploadProfile') );
|| request?.includes('uploadCompanyLogo')) {
this.loadingTextSubject.next("Please wait - we're processing your image...");
} else {
this.loadingTextSubject.next(null);
}
}
}
public stopLoading(type: string): void { public startLoading(type: string, request?: string): void {
if (this.loading$.value.includes(type)) { if (!this.loading$.value.includes(type)) {
this.loading$.next(this.loading$.value.filter((t) => t !== type)); this.loading$.next(this.loading$.value.concat(type));
this.loadingTextSubject.next(null); if (type === 'uploadImage' || request?.includes('uploadImage') || request?.includes('uploadPropertyPicture') || request?.includes('uploadProfile') || request?.includes('uploadCompanyLogo')) {
} this.loadingTextSubject.next("Please wait - we're processing your image...");
} } else {
} this.loadingTextSubject.next(null);
}
}
}
public stopLoading(type: string): void {
if (this.loading$.value.includes(type)) {
this.loading$.next(this.loading$.value.filter(t => t !== type));
this.loadingTextSubject.next(null);
}
}
}