new Landing page, stripped app
This commit is contained in:
318
bizmatch-client/src/app/pages/account/account.component.html
Normal file
318
bizmatch-client/src/app/pages/account/account.component.html
Normal file
@@ -0,0 +1,318 @@
|
||||
<div class="container mx-auto p-4">
|
||||
@if (user){
|
||||
<div class="bg-white rounded-lg drop-shadow-custom-bg-mobile md:drop-shadow-custom-bg 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" disabled 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@bizmatch.net</p>
|
||||
</div>
|
||||
@if (isProfessional || (authService.isAdmin() | async)){
|
||||
<div class="flex flex-row items-center justify-around md:space-x-4">
|
||||
<div class="flex h-full justify-between flex-col">
|
||||
<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 relative">
|
||||
@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 drop-shadow-custom-bg 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>
|
||||
<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"
|
||||
(click)="uploadCompanyLogo()"
|
||||
>
|
||||
Upload
|
||||
</button>
|
||||
</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>
|
||||
<div class="w-20 h-20 w-full rounded-md flex items-center justify-center relative">
|
||||
@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 drop-shadow-custom-bg 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>
|
||||
<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"
|
||||
(click)="uploadProfile()"
|
||||
>
|
||||
Upload
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<app-validated-input label="First Name" name="firstname" [(ngModel)]="user.firstname"></app-validated-input>
|
||||
<app-validated-input label="Last Name" name="lastname" [(ngModel)]="user.lastname"></app-validated-input>
|
||||
</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> -->
|
||||
@if ((authService.isAdmin() | async) && !id){
|
||||
<div>
|
||||
<label for="customerType" class="block text-sm font-medium text-gray-700">User Type</label>
|
||||
<span class="bg-blue-100 text-blue-800 text-sm font-medium me-2 px-2.5 py-0.5 rounded dark:bg-blue-900 dark:text-blue-300">ADMIN</span>
|
||||
</div>
|
||||
|
||||
}
|
||||
<app-validated-select [disabled]="true" label="Customer Type" name="customerType" [(ngModel)]="user.customerType" [options]="customerTypeOptions"></app-validated-select>
|
||||
@if (isProfessional){
|
||||
<!-- <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> -->
|
||||
<app-validated-select label="Professional Type" name="customerSubType" [(ngModel)]="user.customerSubType" [options]="customerSubTypeOptions"></app-validated-select>
|
||||
}
|
||||
</div>
|
||||
@if (isProfessional){
|
||||
<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> -->
|
||||
<app-validated-input label="Company Name" name="companyName" [(ngModel)]="user.companyName"></app-validated-input>
|
||||
<app-validated-input label="Describe Yourself" name="description" [(ngModel)]="user.description"></app-validated-input>
|
||||
</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> -->
|
||||
<app-validated-input label="Your Phone Number" name="phoneNumber" [(ngModel)]="user.phoneNumber" mask="(000) 000-0000"></app-validated-input>
|
||||
<app-validated-input label="Company Website" name="companyWebsite" [(ngModel)]="user.companyWebsite"></app-validated-input>
|
||||
<!-- <app-validated-input label="Company Location" name="companyLocation" [(ngModel)]="user.companyLocation"></app-validated-input> -->
|
||||
<!-- <app-validated-city label="Company Location" name="location" [(ngModel)]="user.location"></app-validated-city> -->
|
||||
<app-validated-location label="Company Location" name="location" [(ngModel)]="user.location"></app-validated-location>
|
||||
</div>
|
||||
|
||||
<!-- <div>
|
||||
<label for="companyOverview" class="block text-sm font-medium text-gray-700">Company Overview</label>
|
||||
<quill-editor [(ngModel)]="user.companyOverview" name="companyOverview" [modules]="quillModules"></quill-editor>
|
||||
</div> -->
|
||||
<div>
|
||||
<app-validated-quill label="Company Overview" name="companyOverview" [(ngModel)]="user.companyOverview"></app-validated-quill>
|
||||
</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" [modules]="quillModules"></quill-editor> -->
|
||||
<app-validated-quill label="Services We Offer" name="offeredServices" [(ngModel)]="user.offeredServices"></app-validated-quill>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3 class="text-lg font-medium text-gray-700 mb-2 relative w-fit">
|
||||
Areas We Serve @if(getValidationMessage('areasServed')){
|
||||
<div
|
||||
[attr.data-tooltip-target]="tooltipTargetAreasServed"
|
||||
class="absolute inline-flex items-center justify-center w-6 h-6 text-xs font-bold text-white bg-red-500 border-2 border-white rounded-full -top-2 dark:border-gray-900 hover:cursor-pointer"
|
||||
>
|
||||
!
|
||||
</div>
|
||||
<app-tooltip [id]="tooltipTargetAreasServed" [text]="getValidationMessage('areasServed')"></app-tooltip>
|
||||
}
|
||||
</h3>
|
||||
<div class="grid grid-cols-12 gap-4">
|
||||
<div class="col-span-6">
|
||||
<label for="state" class="block text-sm font-medium text-gray-700">State</label>
|
||||
</div>
|
||||
<div class="col-span-5">
|
||||
<label for="county" class="block text-sm font-medium text-gray-700">County</label>
|
||||
</div>
|
||||
</div>
|
||||
@for (areasServed of user.areasServed; track areasServed; let i=$index){
|
||||
<div class="grid grid-cols-12 md:gap-4 gap-1 mb-3 md:mb-1">
|
||||
<div class="col-span-6">
|
||||
<ng-select [items]="selectOptions?.states" bindLabel="name" bindValue="value" [(ngModel)]="areasServed.state" (ngModelChange)="setState(i, $event)" name="areasServed_state{{ i }}"> </ng-select>
|
||||
</div>
|
||||
<div class="col-span-5">
|
||||
<!-- <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" /> -->
|
||||
<app-validated-county name="county{{ i }}" [(ngModel)]="areasServed.county" labelClasses="text-gray-900 font-medium" [state]="areasServed.state" [readonly]="!areasServed.state"></app-validated-county>
|
||||
</div>
|
||||
<div class="col-span-1">
|
||||
<button type="button" class="px-2 py-1 bg-red-500 text-white rounded-md h-[42px] w-8" (click)="removeArea(i)">-</button>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
<div class="mt-2">
|
||||
<button type="button" class="px-2 py-1 bg-green-500 text-white rounded-md mr-2 h-[42px] w-8" (click)="addArea()">+</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 relative">
|
||||
Licensed In@if(getValidationMessage('licensedIn')){
|
||||
<div
|
||||
[attr.data-tooltip-target]="tooltipTargetLicensed"
|
||||
class="absolute inline-flex items-center justify-center w-6 h-6 text-xs font-bold text-white bg-red-500 border-2 border-white rounded-full -top-2 dark:border-gray-900 hover:cursor-pointer"
|
||||
>
|
||||
!
|
||||
</div>
|
||||
<app-tooltip [id]="tooltipTargetLicensed" [text]="getValidationMessage('licensedIn')"></app-tooltip>
|
||||
}
|
||||
</h3>
|
||||
<div class="grid grid-cols-12 gap-4">
|
||||
<div class="col-span-6">
|
||||
<label for="state" class="block text-sm font-medium text-gray-700">State</label>
|
||||
</div>
|
||||
<div class="col-span-5">
|
||||
<label for="county" class="block text-sm font-medium text-gray-700">License Number</label>
|
||||
</div>
|
||||
</div>
|
||||
@for (licensedIn of user.licensedIn; track licensedIn; let i=$index){
|
||||
<div class="grid grid-cols-12 md:gap-4 gap-1 mb-3 md:mb-1">
|
||||
<div class="col-span-6">
|
||||
<ng-select [items]="selectOptions?.states" bindLabel="name" bindValue="value" [(ngModel)]="licensedIn.state" name="licensedIn_state{{ i }}"> </ng-select>
|
||||
</div>
|
||||
<div class="col-span-5">
|
||||
<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>
|
||||
<button type="button" class="px-2 py-1 bg-red-500 text-white rounded-md h-[42px] w-8" (click)="removeLicence(i)">-</button>
|
||||
</div>
|
||||
}
|
||||
<div class="mt-2">
|
||||
<button type="button" class="px-2 py-1 bg-green-500 text-white rounded-md mr-2 h-[42px] w-8" (click)="addLicence()">+</button>
|
||||
<span class="text-sm text-gray-500 ml-2">[Add more licenses or remove existing ones.]</span>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
<!-- <div class="flex items-center !my-8">
|
||||
<label class="flex items-center cursor-pointer">
|
||||
<div class="relative">
|
||||
<input type="checkbox" [(ngModel)]="user.showInDirectory" name="showInDirectory" class="hidden" />
|
||||
<div class="toggle-bg block w-12 h-6 rounded-full bg-gray-600 transition"></div>
|
||||
</div>
|
||||
<div class="ml-3 text-gray-700 font-medium">Show your profile in Professional Directory</div>
|
||||
</label>
|
||||
</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" (click)="updateProfile(user)">
|
||||
Update Profile
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
<!-- <div class="mt-8 max-lg:hidden">
|
||||
<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">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">End Date</th>
|
||||
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Next Settlement</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">
|
||||
@for (subscription of subscriptions; track subscriptions; let i=$index){
|
||||
<tr>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">{{ getLevel(i) }}</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">{{ getStartDate(i) }}</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">{{ getEndDate(i) }}</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">{{ getNextSettlement(i) }}</td>
|
||||
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">{{ getStatus(i) }}</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-8 sm:hidden">
|
||||
<h3 class="text-lg font-medium text-gray-700 mb-1">Membership Level</h3>
|
||||
<div class="space-y-2">
|
||||
@for (subscription of subscriptions; track subscriptions; let i=$index){
|
||||
<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-2 sm:grid-cols-2">
|
||||
<div class="sm:col-span-1 flex">
|
||||
<dt class="text-sm font-bold text-gray-500 mr-2">Level</dt>
|
||||
<dd class="text-sm text-gray-900">{{ getLevel(i) }}</dd>
|
||||
</div>
|
||||
<div class="sm:col-span-1 flex">
|
||||
<dt class="text-sm font-bold text-gray-500 mr-2">Start Date</dt>
|
||||
<dd class="text-sm text-gray-900">{{ getStartDate(i) }}</dd>
|
||||
</div>
|
||||
<div class="sm:col-span-1 flex">
|
||||
<dt class="text-sm font-bold text-gray-500 mr-2">End Date</dt>
|
||||
<dd class="text-sm text-gray-900">{{ getEndDate(i) }}</dd>
|
||||
</div>
|
||||
<div class="sm:col-span-1 flex">
|
||||
<dt class="text-sm font-bold text-gray-500 mr-2">Next Settlement</dt>
|
||||
<dd class="text-sm text-gray-900">{{ getNextSettlement(i) }}</dd>
|
||||
</div>
|
||||
<div class="sm:col-span-1 flex">
|
||||
<dt class="text-sm font-bold text-gray-500 mr-2">Status</dt>
|
||||
<dd class="text-sm text-gray-900">{{ getStatus(i) }}</dd>
|
||||
</div>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div> -->
|
||||
<!-- @if(user.subscriptionPlan==='free'){
|
||||
<div class="flex justify-start">
|
||||
<button
|
||||
routerLink="/pricing"
|
||||
class="py-2.5 px-5 me-2 mb-2 text-sm font-medium text-white focus:outline-none bg-green-500 rounded-lg border border-gray-400 hover:bg-green-600 focus:z-10 focus:ring-4 focus:ring-gray-100 dark:focus:ring-gray-700 dark:bg-gray-800 dark:text-gray-400 dark:border-gray-600 dark:hover:text-white dark:hover:bg-gray-700"
|
||||
>
|
||||
Upgrade Subscription Plan
|
||||
</button>
|
||||
</div>
|
||||
} -->
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
<app-image-crop-and-upload [uploadParams]="uploadParams" (uploadFinished)="uploadFinished($event)"></app-image-crop-and-upload>
|
||||
<app-confirmation></app-confirmation>
|
||||
42
bizmatch-client/src/app/pages/account/account.component.scss
Normal file
42
bizmatch-client/src/app/pages/account/account.component.scss
Normal file
@@ -0,0 +1,42 @@
|
||||
.rounded-logo {
|
||||
border-radius: 6px;
|
||||
width: 120px;
|
||||
height: 30px;
|
||||
border: 1px solid #6b7280;
|
||||
padding: 1px 1px;
|
||||
object-fit: contain;
|
||||
}
|
||||
.rounded-profile {
|
||||
// @extend .rounded-logo;
|
||||
max-width: 100px;
|
||||
max-height: 120px;
|
||||
border-radius: 6px;
|
||||
border: 1px solid #6b7280;
|
||||
padding: 1px 1px;
|
||||
object-fit: contain;
|
||||
}
|
||||
.wfull {
|
||||
width: 100%;
|
||||
}
|
||||
.image-wrap {
|
||||
position: relative; /* Ermöglicht die absolute Positionierung des Icons bezogen auf diesen Container */
|
||||
display: inline-block; /* Erlaubt die Inline-Anordnung, falls mehrere Bilder vorhanden sind */
|
||||
}
|
||||
/* Stil für das FontAwesome Icon */
|
||||
.image-wrap fa-icon {
|
||||
position: absolute;
|
||||
top: -5px; /* Positioniert das Icon am oberen Rand des Bildes */
|
||||
right: -18px; /* Positioniert das Icon am rechten Rand des Bildes */
|
||||
color: #fff; /* Weiße Farbe für das Icon */
|
||||
background-color: rgba(0, 0, 0, 0.5); /* Halbtransparenter Hintergrund für bessere Sichtbarkeit */
|
||||
padding: 5px; /* Ein wenig Platz um das Icon */
|
||||
cursor: pointer; /* Verwandelt den Cursor in eine Hand, um Interaktivität anzudeuten */
|
||||
border-radius: 8px; /* Optional: Abrunden der linken unteren Ecke für ästhetische Zwecke */
|
||||
font-size: 0.7rem;
|
||||
}
|
||||
quill-editor {
|
||||
width: 100%;
|
||||
}
|
||||
:host ::ng-deep .ng-select.ng-select-single .ng-select-container {
|
||||
height: 42px;
|
||||
}
|
||||
257
bizmatch-client/src/app/pages/account/account.component.ts
Normal file
257
bizmatch-client/src/app/pages/account/account.component.ts
Normal file
@@ -0,0 +1,257 @@
|
||||
import { CommonModule, DatePipe, TitleCasePipe } from '@angular/common';
|
||||
import { ChangeDetectorRef, Component } from '@angular/core';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { ActivatedRoute, Router, RouterModule } from '@angular/router';
|
||||
import { FontAwesomeModule } from '@fortawesome/angular-fontawesome';
|
||||
import { faTrash } from '@fortawesome/free-solid-svg-icons';
|
||||
import { NgSelectModule } from '@ng-select/ng-select';
|
||||
|
||||
import { NgxCurrencyDirective } from 'ngx-currency';
|
||||
import { ImageCropperComponent } from 'ngx-image-cropper';
|
||||
import { QuillModule } from 'ngx-quill';
|
||||
import { lastValueFrom } from 'rxjs';
|
||||
import { User } from '../../../../../bizmatch-server/src/models/db.model';
|
||||
import { AutoCompleteCompleteEvent, Invoice, UploadParams, emailToDirName } from '../../../../../bizmatch-server/src/models/main.model';
|
||||
import { environment } from '../../../environments/environment';
|
||||
import { ConfirmationComponent } from '../../components/confirmation.component';
|
||||
import { ImageCropAndUploadComponent, UploadReponse } from '../../components/image-crop-and-upload/image-crop-and-upload.component';
|
||||
import { MessageComponent } from '../../components/message/message.component';
|
||||
import { TooltipComponent } from '../../components/tooltip/tooltip.component';
|
||||
import { ValidatedCityComponent } from '../../components/validated-city/validated-city.component';
|
||||
import { ValidatedCountyComponent } from '../../components/validated-county/validated-county.component';
|
||||
import { ValidatedInputComponent } from '../../components/validated-input/validated-input.component';
|
||||
import { ValidatedLocationComponent } from '../../components/validated-location/validated-location.component';
|
||||
import { ValidatedQuillComponent } from '../../components/validated-quill/validated-quill.component';
|
||||
import { ValidatedSelectComponent } from '../../components/validated-select/validated-select.component';
|
||||
import { ValidationMessage, ValidationMessagesService } from '../../components/validation-messages.service';
|
||||
import { AuthService } from '../../services/auth.service';
|
||||
import { ConfirmationService } from '../../services/confirmation.service';
|
||||
import { GeoService } from '../../services/geo.service';
|
||||
import { ImageService } from '../../services/image.service';
|
||||
import { LoadingService } from '../../services/loading.service';
|
||||
import { MessageService } from '../../services/message.service';
|
||||
import { SelectOptionsService } from '../../services/select-options.service';
|
||||
import { SharedService } from '../../services/shared.service';
|
||||
import { UserService } from '../../services/user.service';
|
||||
import { TOOLBAR_OPTIONS, map2User } from '../../utils/utils';
|
||||
|
||||
@Component({
|
||||
selector: 'app-account',
|
||||
standalone: true,
|
||||
imports: [
|
||||
CommonModule,
|
||||
FormsModule,
|
||||
RouterModule,
|
||||
FontAwesomeModule,
|
||||
QuillModule,
|
||||
NgxCurrencyDirective,
|
||||
NgSelectModule,
|
||||
ImageCropperComponent,
|
||||
ConfirmationComponent,
|
||||
ImageCropAndUploadComponent,
|
||||
MessageComponent,
|
||||
ValidatedInputComponent,
|
||||
ValidatedSelectComponent,
|
||||
ValidatedQuillComponent,
|
||||
ValidatedCityComponent,
|
||||
TooltipComponent,
|
||||
ValidatedCountyComponent,
|
||||
ValidatedLocationComponent,
|
||||
],
|
||||
providers: [TitleCasePipe, DatePipe],
|
||||
templateUrl: './account.component.html',
|
||||
styleUrl: './account.component.scss',
|
||||
})
|
||||
export class AccountComponent {
|
||||
id: string | undefined;
|
||||
user: User;
|
||||
companyLogoUrl: string;
|
||||
profileUrl: string;
|
||||
type: 'company' | 'profile';
|
||||
environment = environment;
|
||||
editorModules = TOOLBAR_OPTIONS;
|
||||
env = environment;
|
||||
faTrash = faTrash;
|
||||
quillModules = {
|
||||
toolbar: [['bold', 'italic', 'underline', 'strike'], [{ list: 'ordered' }, { list: 'bullet' }], [{ header: [1, 2, 3, 4, 5, 6, false] }], [{ color: [] }, { background: [] }], ['clean']],
|
||||
};
|
||||
uploadParams: UploadParams;
|
||||
validationMessages: ValidationMessage[] = [];
|
||||
customerTypeOptions: Array<{ value: string; label: string }> = [];
|
||||
customerSubTypeOptions: Array<{ value: string; label: string }> = [];
|
||||
tooltipTargetAreasServed = 'tooltip-areasServed';
|
||||
tooltipTargetLicensed = 'tooltip-licensedIn';
|
||||
// subscriptions: StripeSubscription[] | any[];
|
||||
constructor(
|
||||
public userService: UserService,
|
||||
private geoService: GeoService,
|
||||
public selectOptions: SelectOptionsService,
|
||||
private cdref: ChangeDetectorRef,
|
||||
private activatedRoute: ActivatedRoute,
|
||||
private loadingService: LoadingService,
|
||||
private imageUploadService: ImageService,
|
||||
private imageService: ImageService,
|
||||
private confirmationService: ConfirmationService,
|
||||
private messageService: MessageService,
|
||||
private sharedService: SharedService,
|
||||
private titleCasePipe: TitleCasePipe,
|
||||
private validationMessagesService: ValidationMessagesService,
|
||||
// private subscriptionService: SubscriptionsService,
|
||||
private datePipe: DatePipe,
|
||||
private router: Router,
|
||||
public authService: AuthService,
|
||||
) {}
|
||||
async ngOnInit() {
|
||||
this.id = this.activatedRoute.snapshot.params['id'] as string | undefined;
|
||||
if (this.id) {
|
||||
this.user = await this.userService.getById(this.id);
|
||||
} else {
|
||||
const token = await this.authService.getToken();
|
||||
const keycloakUser = map2User(token);
|
||||
const email = keycloakUser.email;
|
||||
this.user = await this.userService.getByMail(email);
|
||||
}
|
||||
|
||||
// this.subscriptions = await lastValueFrom(this.subscriptionService.getAllSubscriptions(this.user.email));
|
||||
// await this.synchronizeSubscriptions(this.subscriptions);
|
||||
this.profileUrl = this.user.hasProfile ? `${this.env.imageBaseUrl}/pictures/profile/${emailToDirName(this.user.email)}.avif?_ts=${new Date().getTime()}` : `/assets/images/placeholder.png`;
|
||||
this.companyLogoUrl = this.user.hasCompanyLogo ? `${this.env.imageBaseUrl}/pictures/logo/${emailToDirName(this.user.email)}.avif?_ts=${new Date().getTime()}` : `/assets/images/placeholder.png`;
|
||||
|
||||
this.customerTypeOptions = this.selectOptions.customerTypes
|
||||
// .filter(ct => ct.value === 'buyer' || ct.value === 'seller' || this.user.customerType === 'professional')
|
||||
.map(type => ({
|
||||
value: type.value,
|
||||
label: this.titleCasePipe.transform(type.name),
|
||||
}));
|
||||
|
||||
this.customerSubTypeOptions = this.selectOptions.customerSubTypes
|
||||
// .filter(ct => ct.value !== 'broker' || this.user.customerSubType === 'broker')
|
||||
.map(type => ({
|
||||
value: type.value,
|
||||
label: this.titleCasePipe.transform(type.name),
|
||||
}));
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.validationMessagesService.clearMessages(); // Löschen Sie alle bestehenden Validierungsnachrichten
|
||||
}
|
||||
printInvoice(invoice: Invoice) {}
|
||||
|
||||
async updateProfile(user: User) {
|
||||
try {
|
||||
await this.userService.save(this.user);
|
||||
this.userService.changeUser(this.user);
|
||||
this.messageService.addMessage({ severity: 'success', text: 'Account changes have been persisted', duration: 3000 });
|
||||
this.validationMessagesService.clearMessages(); // Löschen Sie alle bestehenden Validierungsnachrichten
|
||||
this.validationMessages = [];
|
||||
} catch (error) {
|
||||
this.messageService.addMessage({
|
||||
severity: 'danger',
|
||||
text: 'An error occurred while saving the profile - Please check your inputs',
|
||||
duration: 5000,
|
||||
});
|
||||
if (error.error && Array.isArray(error.error?.message)) {
|
||||
this.validationMessagesService.updateMessages(error.error.message);
|
||||
this.validationMessages = error.error.message;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onUploadCompanyLogo(event: any) {
|
||||
const uniqueSuffix = '?_ts=' + new Date().getTime();
|
||||
this.companyLogoUrl = `${this.env.imageBaseUrl}/pictures/logo/${emailToDirName(this.user.email)}${uniqueSuffix}`;
|
||||
}
|
||||
onUploadProfilePicture(event: any) {
|
||||
const uniqueSuffix = '?_ts=' + new Date().getTime();
|
||||
this.profileUrl = `${this.env.imageBaseUrl}/pictures/profile/${emailToDirName(this.user.email)}${uniqueSuffix}`;
|
||||
}
|
||||
setImageToFallback(event: Event) {
|
||||
(event.target as HTMLImageElement).src = `/assets/images/placeholder.png`; // Pfad zum Platzhalterbild
|
||||
}
|
||||
|
||||
suggestions: string[] | undefined;
|
||||
|
||||
async search(event: AutoCompleteCompleteEvent) {
|
||||
const result = await lastValueFrom(this.geoService.findCitiesStartingWith(event.query));
|
||||
this.suggestions = result.map(r => `${r.name} - ${r.state}`).slice(0, 5);
|
||||
}
|
||||
addLicence() {
|
||||
this.user.licensedIn.push({ registerNo: '', state: '' });
|
||||
}
|
||||
removeLicence(index: number) {
|
||||
this.user.licensedIn.splice(index, 1);
|
||||
}
|
||||
addArea() {
|
||||
this.user.areasServed.push({ county: '', state: '' });
|
||||
}
|
||||
removeArea(index: number) {
|
||||
this.user.areasServed.splice(index, 1);
|
||||
}
|
||||
get isProfessional() {
|
||||
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);
|
||||
}
|
||||
this.userService.changeUser(this.user);
|
||||
await this.userService.saveGuaranteed(this.user);
|
||||
}
|
||||
}
|
||||
async deleteConfirm(type: 'profile' | 'logo') {
|
||||
const confirmed = await this.confirmationService.showConfirmation({ message: `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.deleteProfileImagesByMail(this.user.email), this.userService.saveGuaranteed(this.user)]);
|
||||
} else {
|
||||
this.user.hasCompanyLogo = false;
|
||||
await Promise.all([this.imageService.deleteLogoImagesByMail(this.user.email), this.userService.saveGuaranteed(this.user)]);
|
||||
}
|
||||
this.user = await this.userService.getById(this.user.id);
|
||||
// this.messageService.showMessage('Image deleted');
|
||||
this.messageService.addMessage({
|
||||
severity: 'success',
|
||||
text: 'Image deleted.',
|
||||
duration: 3000, // 3 seconds
|
||||
});
|
||||
}
|
||||
}
|
||||
getValidationMessage(fieldName: string): string {
|
||||
const message = this.validationMessages.find(msg => msg.field === fieldName);
|
||||
return message ? message.message : '';
|
||||
}
|
||||
|
||||
setState(index: number, state: string) {
|
||||
if (state === null) {
|
||||
this.user.areasServed[index].county = null;
|
||||
}
|
||||
}
|
||||
// getLevel(i: number) {
|
||||
// return this.subscriptions[i].metadata.plan;
|
||||
// }
|
||||
// getStartDate(i: number) {
|
||||
// return this.datePipe.transform(new Date(this.subscriptions[i].start_date * 1000));
|
||||
// }
|
||||
// getEndDate(i: number) {
|
||||
// return this.subscriptions[i].status === 'trialing' ? this.datePipe.transform(new Date(this.subscriptions[i].current_period_end * 1000)) : '---';
|
||||
// }
|
||||
// getNextSettlement(i: number) {
|
||||
// return this.subscriptions[i].status === 'active' ? this.datePipe.transform(new Date(this.subscriptions[i].current_period_end * 1000)) : '---';
|
||||
// }
|
||||
// getStatus(i: number) {
|
||||
// return this.subscriptions[i].status ? this.subscriptions[i].status : '';
|
||||
// }
|
||||
}
|
||||
Reference in New Issue
Block a user