account Komponente
This commit is contained in:
parent
7f67b81242
commit
08c179fa09
|
|
@ -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>
|
|
||||||
|
|
@ -1,4 +0,0 @@
|
||||||
::ng-deep p-selectbutton.small .p-button {
|
|
||||||
font-size: 0.875rem;
|
|
||||||
padding: 0.65625rem 1.09375rem;
|
|
||||||
}
|
|
||||||
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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 = '';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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@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>
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue