broker direcrtory renewed, imageservice updated, demo data
This commit is contained in:
@@ -42,7 +42,7 @@ export const routes: Routes = [
|
||||
component: DetailsUserComponent,
|
||||
},
|
||||
{
|
||||
path: 'account',
|
||||
path: 'account/:id',
|
||||
component: AccountComponent,
|
||||
canActivate: [authGuard],
|
||||
},
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
<div class="col-12 md:col-3 md:mb-0 mb-3">
|
||||
<img src="assets/images/header-logo.png" alt="footer sections" height="30" class="mr-3">
|
||||
<div class="text-500">© 2024 Bizmatch All rights reserved.</div>
|
||||
<!-- <div class="text-gray-300 font-bold text-5xl">Bastion</div> -->
|
||||
</div>
|
||||
<div class="col-12 md:col-3">
|
||||
<div class="text-black mb-4 flex flex-wrap" style="max-width: 290px">BizMatch, Inc., 1001 Blucher Street, Corpus Christi, Texas 78401</div>
|
||||
@@ -19,7 +18,7 @@
|
||||
<div class="col-12 md:col-3 text-500">
|
||||
<div class="text-black font-bold line-height-3 mb-3">Actions</div>
|
||||
<a *ngIf="!userService.isLoggedIn()" (click)="login()" class="text-500 line-height-3 block cursor-pointer mb-2 no-underline">Login</a>
|
||||
<a *ngIf="userService.isLoggedIn()" [routerLink]="['/account']" class="text-500 line-height-3 block cursor-pointer mb-2 no-underline">Account</a>
|
||||
<a *ngIf="userService.isLoggedIn()" [routerLink]="['/account',userService.getUser()?.id]" class="text-500 line-height-3 block cursor-pointer mb-2 no-underline">Account</a>
|
||||
<a *ngIf="userService.isLoggedIn()" class="text-500 line-height-3 block cursor-pointer mb-2 no-underline" (click)="userService.logout()">Log Out</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -21,8 +21,8 @@ import { User } from '../../../../../common-models/src/main.model';
|
||||
})
|
||||
export class HeaderComponent {
|
||||
public buildVersion = environment.buildVersion;
|
||||
user:User;
|
||||
user$:Observable<User>
|
||||
user:User;
|
||||
public tabItems: MenuItem[];
|
||||
public menuItems: MenuItem[];
|
||||
activeItem
|
||||
@@ -33,6 +33,60 @@ export class HeaderComponent {
|
||||
|
||||
ngOnInit(){
|
||||
this.user$=this.userService.getUserObservable();
|
||||
this.user$.subscribe(u=>{
|
||||
this.user=u;
|
||||
this.menuItems = [
|
||||
{
|
||||
label: 'User Actions',
|
||||
icon: 'fas fa-cog',
|
||||
items: [
|
||||
{
|
||||
label: 'Account',
|
||||
icon: 'pi pi-user',
|
||||
routerLink: `/account/${this.user.id}`,
|
||||
visible: this.isUserLogedIn()
|
||||
},
|
||||
{
|
||||
label: 'Create Listing',
|
||||
icon: 'pi pi-plus-circle',
|
||||
routerLink: "/createListing",
|
||||
visible: this.isUserLogedIn()
|
||||
},
|
||||
{
|
||||
label: 'My Listings',
|
||||
icon: 'pi pi-list',
|
||||
routerLink:"/myListings",
|
||||
visible: this.isUserLogedIn()
|
||||
},
|
||||
{
|
||||
label: 'My Favorites',
|
||||
icon: 'pi pi-star',
|
||||
routerLink:"/myFavorites",
|
||||
visible: this.isUserLogedIn()
|
||||
},
|
||||
{
|
||||
label: 'EMail Us',
|
||||
icon: 'fa-regular fa-envelope',
|
||||
routerLink:"/emailUs",
|
||||
visible: this.isUserLogedIn()
|
||||
},
|
||||
{
|
||||
label: 'Logout',
|
||||
icon: 'fa-solid fa-right-from-bracket',
|
||||
routerLink:"/logout",
|
||||
visible: this.isUserLogedIn()
|
||||
},
|
||||
{
|
||||
label: 'Login',
|
||||
icon: 'fa-solid fa-right-from-bracket',
|
||||
//routerLink:"/account",
|
||||
command: () => this.login(),
|
||||
visible: !this.isUserLogedIn()
|
||||
},
|
||||
]
|
||||
}
|
||||
]
|
||||
});
|
||||
this.tabItems = [
|
||||
{
|
||||
label: 'Businesses for Sale',
|
||||
@@ -50,63 +104,13 @@ export class HeaderComponent {
|
||||
fragment:''
|
||||
}
|
||||
];
|
||||
this.menuItems = [
|
||||
{
|
||||
label: 'User Actions',
|
||||
icon: 'fas fa-cog',
|
||||
items: [
|
||||
{
|
||||
label: 'Account',
|
||||
icon: 'pi pi-user',
|
||||
routerLink: '/account',
|
||||
visible: this.isUserLoogedIn()
|
||||
},
|
||||
{
|
||||
label: 'Create Listing',
|
||||
icon: 'pi pi-plus-circle',
|
||||
routerLink: "/createListing",
|
||||
visible: this.isUserLoogedIn()
|
||||
},
|
||||
{
|
||||
label: 'My Listings',
|
||||
icon: 'pi pi-list',
|
||||
routerLink:"/myListings",
|
||||
visible: this.isUserLoogedIn()
|
||||
},
|
||||
{
|
||||
label: 'My Favorites',
|
||||
icon: 'pi pi-star',
|
||||
routerLink:"/myFavorites",
|
||||
visible: this.isUserLoogedIn()
|
||||
},
|
||||
{
|
||||
label: 'EMail Us',
|
||||
icon: 'fa-regular fa-envelope',
|
||||
routerLink:"/emailUs",
|
||||
visible: this.isUserLoogedIn()
|
||||
},
|
||||
{
|
||||
label: 'Logout',
|
||||
icon: 'fa-solid fa-right-from-bracket',
|
||||
routerLink:"/logout",
|
||||
visible: this.isUserLoogedIn()
|
||||
},
|
||||
{
|
||||
label: 'Login',
|
||||
icon: 'fa-solid fa-right-from-bracket',
|
||||
//routerLink:"/account",
|
||||
command: () => this.login(),
|
||||
visible: !this.isUserLoogedIn()
|
||||
},
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
this.activeItem=this.tabItems[0];
|
||||
}
|
||||
navigateWithState(dest: string, state: any) {
|
||||
this.router.navigate([dest], { state: state });
|
||||
}
|
||||
isUserLoogedIn(){
|
||||
isUserLogedIn(){
|
||||
return this.userService?.isLoggedIn();
|
||||
}
|
||||
login(){
|
||||
|
||||
@@ -8,8 +8,12 @@
|
||||
<div class="surface-section px-6 pt-5">
|
||||
<div class="flex align-items-start flex-column lg:flex-row lg:justify-content-between">
|
||||
<div class="flex align-items-start flex-column md:flex-row">
|
||||
<img src="{{environment.apiBaseUrl}}/profile/{{user.id}}" class="mr-5 mb-3 lg:mb-0"
|
||||
style="width:90px;height:90px" />
|
||||
@if(user.hasProfile){
|
||||
<img src="{{environment.apiBaseUrl}}/profile/{{user.id}}.avif" class="mr-5 mb-3 lg:mb-0"
|
||||
style="width:90px" />
|
||||
} @else {
|
||||
<img src="assets/images/person_placeholder.jpg" class="mr-5 mb-3 lg:mb-0" style="width:90px" />
|
||||
}
|
||||
<div>
|
||||
<span class="text-900 font-medium text-3xl">{{user.firstname}} {{user.lastname}}</span>
|
||||
<i class="pi pi-star text-2xl ml-4 text-yellow-500"></i>
|
||||
@@ -28,8 +32,14 @@
|
||||
</div>
|
||||
<div class="flex align-items-center mt-3">
|
||||
<!-- <span class="font-medium text-500">Logo</span> -->
|
||||
<div ><img src="{{environment.apiBaseUrl}}/logo/{{user.id}}"
|
||||
class="mr-5 lg:mb-0" style="width:100px;height:30px" /></div>
|
||||
<div >
|
||||
@if(user.hasCompanyLogo){
|
||||
<img src="{{environment.apiBaseUrl}}/logo/{{user.id}}.avif"
|
||||
class="mr-5 lg:mb-0" style="height:60px;max-width:100px" />
|
||||
}
|
||||
<!-- <img *ngIf="!user.hasCompanyLogo" src="assets/images/placeholder.png"
|
||||
class="mr-5 lg:mb-0" style="height:60px;max-width:100px" /> -->
|
||||
</div>
|
||||
<!-- <div class="text-700 mt-2">130</div> -->
|
||||
</div>
|
||||
</div>
|
||||
@@ -37,18 +47,13 @@
|
||||
|
||||
</div>
|
||||
|
||||
<!-- <div class="mt-3 lg:mt-0">
|
||||
<button pButton pRipple icon="pi pi-bookmark" class="p-button-rounded mr-2"></button>
|
||||
<button pButton pRipple icon="pi pi-heart" class="p-button-rounded p-button-success mr-2"></button>
|
||||
<button pButton pRipple icon="pi pi-list" class="p-button-rounded p-button-help"></button>
|
||||
</div> -->
|
||||
</div>
|
||||
<p class="mt-2 text-700 line-height-3 text-l font-semibold">{{user.description}}</p>
|
||||
</div>
|
||||
<div class="px-6 py-5">
|
||||
<div class="surface-card p-4 shadow-2 border-round">
|
||||
<div class="font-medium text-3xl text-900 mb-3">Company Profile</div>
|
||||
<div class="text-500 mb-5">{{user.companyOverview}}</div>
|
||||
<div class="text-500 mb-5" [innerHTML]="companyOverview"></div>
|
||||
<ul class="list-none p-0 m-0 border-top-1 border-300">
|
||||
<li class="flex align-items-center py-3 px-2 flex-wrap surface-ground">
|
||||
<div class="text-500 w-full md:w-2 font-medium">Name</div>
|
||||
@@ -67,14 +72,10 @@
|
||||
<div class="text-900 w-full md:w-10 line-height-3">{{user.companyLocation}}</div>
|
||||
</li>
|
||||
<li class="flex align-items-center py-3 px-2 flex-wrap surface-ground">
|
||||
<div class="text-500 w-full md:w-2 font-medium">Company Overview</div>
|
||||
<div class="text-900 w-full md:w-10 line-height-3">{{user.companyOverview}}</div>
|
||||
<div class="text-500 w-full md:w-2 font-medium">Services we offer</div>
|
||||
<div class="text-900 w-full md:w-10" [innerHTML]="offeredServices"></div>
|
||||
</li>
|
||||
<li class="flex align-items-center py-3 px-2 flex-wrap ">
|
||||
<div class="text-500 w-full md:w-2 font-medium">Services we offer</div>
|
||||
<div class="text-900 w-full md:w-10">{{user.offeredServices}}</div>
|
||||
</li>
|
||||
<li class="flex align-items-center py-3 px-2 flex-wrap surface-ground">
|
||||
<div class="text-500 w-full md:w-2 font-medium">Areas we serve</div>
|
||||
<div class="text-900 w-full md:w-10">
|
||||
@for (area of user.areasServed; track area) {
|
||||
@@ -123,7 +124,7 @@
|
||||
</div>
|
||||
@if( user?.id===(user$| async)?.id || isAdmin()){
|
||||
<button pButton pRipple label="Edit" icon="pi pi-file-edit" class="w-auto"
|
||||
[routerLink]="['/account']"></button>
|
||||
[routerLink]="['/account',user.id]"></button>
|
||||
}
|
||||
</div>
|
||||
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
:host ::ng-deep p {
|
||||
margin:3px 0;
|
||||
}
|
||||
@@ -9,6 +9,8 @@ import { UserService } from '../../../services/user.service';
|
||||
import { Observable } from 'rxjs';
|
||||
import { ListingsService } from '../../../services/listings.service';
|
||||
import { SelectOptionsService } from '../../../services/select-options.service';
|
||||
import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
|
||||
import { ImageService } from '../../../services/image.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-details-user',
|
||||
@@ -25,18 +27,25 @@ export class DetailsUserComponent {
|
||||
environment = environment;
|
||||
criteria:ListingCriteria;
|
||||
userListings:BusinessListing[]
|
||||
companyOverview:SafeHtml;
|
||||
offeredServices:SafeHtml;
|
||||
constructor(private activatedRoute: ActivatedRoute,
|
||||
private router: Router,
|
||||
private userService: UserService,
|
||||
private listingsService:ListingsService,
|
||||
private messageService: MessageService,
|
||||
public selectOptions: SelectOptionsService) {
|
||||
public selectOptions: SelectOptionsService,
|
||||
private sanitizer: DomSanitizer,
|
||||
private imageService:ImageService) {
|
||||
}
|
||||
|
||||
async ngOnInit() {
|
||||
this.user = await this.userService.getById(this.id);
|
||||
|
||||
this.userListings = await this.listingsService.getListingByUserId(this.id);
|
||||
this.user$ = this.userService.getUserObservable();
|
||||
this.companyOverview=this.sanitizer.bypassSecurityTrustHtml(this.user.companyOverview);
|
||||
this.offeredServices=this.sanitizer.bypassSecurityTrustHtml(this.user.offeredServices);
|
||||
}
|
||||
back() {
|
||||
this.router.navigate(['listings', this.criteria.listingsCategory])
|
||||
|
||||
@@ -5,31 +5,13 @@
|
||||
<div
|
||||
class="align-items-center flex-grow-1 justify-content-between hidden lg:flex absolute lg:static w-full left-0 top-100 px-6 lg:px-0 shadow-2 lg:shadow-none z-2">
|
||||
<section></section>
|
||||
<!-- <ul
|
||||
class="list-none p-0 m-0 flex lg:align-items-center text-blue-900 select-none flex-column lg:flex-row cursor-pointer">
|
||||
<li>
|
||||
<a pRipple
|
||||
class="flex px-0 lg:px-5 py-3 hover:text-blue-600 font-medium transition-colors transition-duration-150">
|
||||
<span>Corporate</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a pRipple
|
||||
class="flex px-0 lg:px-5 py-3 hover:text-blue-600 font-medium transition-colors transition-duration-150">
|
||||
<span>Resources</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a pRipple
|
||||
class="flex px-0 lg:px-5 py-3 hover:text-blue-600 font-medium transition-colors transition-duration-150">
|
||||
<span>Pricing</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul> -->
|
||||
<div
|
||||
class="flex justify-content-between lg:block border-top-1 lg:border-top-none border-gray-800 py-3 lg:py-0 mt-3 lg:mt-0">
|
||||
<!-- <p-button label="Account" class="ml-3 font-bold" [outlined]="true" severity="secondary" [routerLink]="['/account']"></p-button> -->
|
||||
<p-button label="Account" class="ml-3 font-bold" [outlined]="true" severity="secondary" (click)="account()"></p-button>
|
||||
@if(userService.isLoggedIn()){
|
||||
<p-button label="Account" class="ml-3 font-bold" [outlined]="true" severity="secondary" [routerLink]="['/account',(user$|async).id]"></p-button>
|
||||
} @else {
|
||||
<p-button label="Log In" class="ml-3 font-bold" [outlined]="true" severity="secondary" (click)="login()"></p-button>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -12,7 +12,8 @@ import { SelectOptionsService } from '../../services/select-options.service';
|
||||
import { UserService } from '../../services/user.service';
|
||||
import onChange from 'on-change';
|
||||
import { getCriteriaStateObject, getSessionStorageHandler } from '../../utils/utils';
|
||||
import { ListingCriteria } from '../../../../../common-models/src/main.model';
|
||||
import { ListingCriteria, User } from '../../../../../common-models/src/main.model';
|
||||
import { Observable } from 'rxjs';
|
||||
@Component({
|
||||
selector: 'app-home',
|
||||
standalone: true,
|
||||
@@ -26,20 +27,20 @@ export class HomeComponent {
|
||||
maxPrice:string;
|
||||
minPrice:string;
|
||||
criteria:ListingCriteria
|
||||
user$:Observable<User>
|
||||
public constructor(private router: Router,private activatedRoute: ActivatedRoute, public selectOptions:SelectOptionsService, public userService:UserService) {
|
||||
this.criteria = onChange(getCriteriaStateObject(),getSessionStorageHandler);
|
||||
}
|
||||
ngOnInit(){
|
||||
|
||||
this.user$=this.userService.getUserObservable();
|
||||
}
|
||||
|
||||
search(){
|
||||
this.router.navigate([`listings/${this.activeTabAction}`])
|
||||
}
|
||||
|
||||
account(){
|
||||
setTimeout(()=>{
|
||||
this.router.navigate([`account`])
|
||||
},10);
|
||||
}
|
||||
|
||||
login(){
|
||||
this.userService.login(window.location.href);
|
||||
}
|
||||
}
|
||||
@@ -121,11 +121,15 @@
|
||||
|
||||
}
|
||||
@for (user of users; track user.id) {
|
||||
<div class="col-12 lg:col-6 xl:col-4 p-4">
|
||||
<div class="surface-card shadow-2 p-2" style="border-radius: 10px">
|
||||
<div class="col-12 lg:col-6 xl:col-4 p-4 flex flex-column flex-grow-1">
|
||||
<div class="surface-card shadow-2 p-2 flex flex-column flex-grow-1 justify-content-between" style="border-radius: 10px">
|
||||
<div class="surface-card p-4 flex flex-column align-items-center md:flex-row md:align-items-stretch h-full" >
|
||||
<span>
|
||||
<img src="{{environment.apiBaseUrl}}/profile/{{user.id}}" class="w-5rem" />
|
||||
@if(user.hasProfile){
|
||||
<img src="{{environment.apiBaseUrl}}/profile/{{user.id}}.avif" class="w-5rem" />
|
||||
} @else {
|
||||
<img src="assets/images/person_placeholder.jpg" class="w-5rem" />
|
||||
}
|
||||
</span>
|
||||
<div class="flex flex-column align-items-center md:align-items-stretch ml-4 mt-4 md:mt-0">
|
||||
<p class="mt-0 mb-3 line-height-3 text-center md:text-left">{{user.description}}</p>
|
||||
@@ -134,7 +138,11 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="px-4 py-3 text-right flex justify-content-between align-items-center">
|
||||
<img *ngIf="user.hasCompanyLogo" src="{{environment.apiBaseUrl}}/logo/{{user.id}}" class="rounded-image"/>
|
||||
@if(user.hasCompanyLogo){
|
||||
<img src="{{environment.apiBaseUrl}}/logo/{{user.id}}.avif" class="rounded-image"/>
|
||||
} @else {
|
||||
<img src="assets/images/placeholder.png" class="rounded-image"/>
|
||||
}
|
||||
<button pButton pRipple icon="pi pi-arrow-right" iconPos="right" label="View Full profile"
|
||||
class="p-button-rounded p-button-success" [routerLink]="['/details-user',user.id]"></button>
|
||||
|
||||
|
||||
@@ -14,8 +14,9 @@
|
||||
}
|
||||
.rounded-image {
|
||||
border-radius: 6px;
|
||||
width: 100px;
|
||||
height: 25px;
|
||||
// width: 100px;
|
||||
max-width: 100px;
|
||||
height: 45px;
|
||||
border: 1px solid rgba(0,0,0,0.2);
|
||||
padding: 1px 1px;
|
||||
object-fit: contain;
|
||||
|
||||
@@ -18,6 +18,7 @@ import { InitEditableRow } from 'primeng/table';
|
||||
import { environment } from '../../../environments/environment';
|
||||
import { ListingCriteria, ListingType, User } from '../../../../../common-models/src/main.model';
|
||||
import { UserService } from '../../services/user.service';
|
||||
import { ImageService } from '../../services/image.service';
|
||||
@Component({
|
||||
selector: 'app-listings',
|
||||
standalone: true,
|
||||
@@ -49,7 +50,8 @@ export class ListingsComponent {
|
||||
private userService:UserService,
|
||||
private activatedRoute: ActivatedRoute,
|
||||
private router:Router,
|
||||
private cdRef:ChangeDetectorRef) {
|
||||
private cdRef:ChangeDetectorRef,
|
||||
private imageService:ImageService) {
|
||||
this.criteria = onChange(getCriteriaStateObject(),getSessionStorageHandler);
|
||||
this.router.getCurrentNavigation()
|
||||
this.activatedRoute.snapshot
|
||||
@@ -80,6 +82,12 @@ export class ListingsComponent {
|
||||
this.listings=[]
|
||||
this.filteredListings=[];
|
||||
this.users=await this.userService.search(this.criteria);
|
||||
const profiles = await this.imageService.getProfileImagesForUsers(this.users.map(u=>u.id));
|
||||
const logos = await this.imageService.getCompanyLogosForUsers(this.users.map(u=>u.id));
|
||||
this.users.forEach(u=>{
|
||||
u.hasProfile=profiles[u.id]
|
||||
u.hasCompanyLogo=logos[u.id]
|
||||
})
|
||||
this.cdRef.markForCheck();
|
||||
this.cdRef.detectChanges();
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
<div class="surface-ground px-4 py-8 md:px-6 lg:px-8">
|
||||
<div class="p-fluid flex flex-column lg:flex-row">
|
||||
<menu-account></menu-account>
|
||||
@@ -16,7 +15,8 @@
|
||||
<div class="mb-4">
|
||||
<label for="state" class="block font-medium text-900 mb-2">E-mail (required)</label>
|
||||
<input id="state" type="text" [disabled]="true" pInputText [(ngModel)]="user.email">
|
||||
<p class="font-italic text-sm line-height-1">You can only modify your email by contacting us at emailchange@bizmatch.net</p>
|
||||
<p class="font-italic text-sm line-height-1">You can only modify your email by contacting us at
|
||||
emailchange@bizmatch.net</p>
|
||||
</div>
|
||||
<div class="grid">
|
||||
<div class="mb-4 col-12 md:col-6">
|
||||
@@ -48,60 +48,109 @@
|
||||
<input id="companyWebsite" type="text" pInputText [(ngModel)]="user.companyWebsite">
|
||||
</div>
|
||||
<div class="mb-4 col-12 md:col-4">
|
||||
<label for="companyLocation" class="block font-medium text-900 mb-2">Company Location</label>
|
||||
<p-autoComplete [(ngModel)]="user.companyLocation" [suggestions]="suggestions" (completeMethod)="search($event)"></p-autoComplete>
|
||||
<label for="companyLocation" class="block font-medium text-900 mb-2">Company
|
||||
Location</label>
|
||||
<p-autoComplete [(ngModel)]="user.companyLocation" [suggestions]="suggestions"
|
||||
(completeMethod)="search($event)"></p-autoComplete>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-4">
|
||||
<label for="companyOverview" class="block font-medium text-900 mb-2">Company Overview</label>
|
||||
<textarea id="companyOverview" type="text" pInputTextarea rows="5" [autoResize]="true" [(ngModel)]="user.companyOverview"></textarea>
|
||||
</div>
|
||||
<p-editor [(ngModel)]="user.companyOverview" [style]="{ height: '320px' }">
|
||||
<ng-template pTemplate="header">
|
||||
<span class="ql-formats">
|
||||
<button type="button" class="ql-bold" aria-label="Bold"></button>
|
||||
<button type="button" class="ql-italic" aria-label="Italic"></button>
|
||||
<button type="button" class="ql-underline" aria-label="Underline"></button>
|
||||
<button value="ordered" aria-label="Ordered List" type="button"
|
||||
class="ql-list"></button>
|
||||
<button value="bullet" aria-label="Unordered List" type="button"
|
||||
class="ql-list"></button>
|
||||
</span>
|
||||
</ng-template>
|
||||
</p-editor>
|
||||
</div>
|
||||
<div class="mb-4">
|
||||
<label for="companyOverview" class="block font-medium text-900 mb-2">Services We offer</label>
|
||||
<p-editor [(ngModel)]="user.offeredServices" [style]="{ height: '320px' }">
|
||||
<ng-template pTemplate="header">
|
||||
<span class="ql-formats">
|
||||
<button type="button" class="ql-bold" aria-label="Bold"></button>
|
||||
<button type="button" class="ql-italic" aria-label="Italic"></button>
|
||||
<button type="button" class="ql-underline" aria-label="Underline"></button>
|
||||
<button value="ordered" aria-label="Ordered List" type="button"
|
||||
class="ql-list"></button>
|
||||
<button value="bullet" aria-label="Unordered List" type="button"
|
||||
class="ql-list"></button>
|
||||
</span>
|
||||
</ng-template>
|
||||
</p-editor>
|
||||
</div>
|
||||
|
||||
<div class="mb-4">
|
||||
<label for="areasServed" class="block font-medium text-900 mb-2">Areas We Serve</label>
|
||||
<textarea id="areasServed" type="text" pInputTextarea rows="5" [autoResize]="true" [(ngModel)]="user.areasServed"></textarea>
|
||||
</div>
|
||||
<div >
|
||||
<textarea id="areasServed" type="text" pInputTextarea rows="5" [autoResize]="true"
|
||||
[(ngModel)]="user.areasServed"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label for="companyOverview" class="block font-medium text-900 mb-2">Licensed In</label>
|
||||
@for (licensedIn of user.licensedIn; track licensedIn.value){
|
||||
<div class="grid">
|
||||
<div class="flex col-12 md:col-6">
|
||||
<p-dropdown id="states" [options]="selectOptions?.states" [(ngModel)]="licensedIn.name" optionLabel="name" optionValue="value" [showClear]="true" placeholder="State" [ngStyle]="{ width: '100%'}"></p-dropdown>
|
||||
<p-dropdown id="states" [options]="selectOptions?.states" [(ngModel)]="licensedIn.name"
|
||||
optionLabel="name" optionValue="value" [showClear]="true" placeholder="State"
|
||||
[ngStyle]="{ width: '100%'}"></p-dropdown>
|
||||
</div>
|
||||
<div class="flex col-12 md:col-6">
|
||||
<input id="companyWebsite" type="text" pInputText [(ngModel)]="licensedIn.value" placeholder="Licence Number">
|
||||
<input id="companyWebsite" type="text" pInputText [(ngModel)]="licensedIn.value"
|
||||
placeholder="Licence Number">
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<div class="field mb-5 col-12 md:col-6 flex align-items-center">
|
||||
<p-button class="mr-1" icon="pi pi-plus" severity="success" (click)="addLicence()"></p-button>
|
||||
<p-button icon="pi pi-minus" severity="danger" (click)="removeLicence()" [disabled]="user.licensedIn?.length<2"></p-button>
|
||||
<p-button icon="pi pi-minus" severity="danger" (click)="removeLicence()"
|
||||
[disabled]="user.licensedIn?.length<2"></p-button>
|
||||
<span class="text-xs"> (Add more licenses or remove existing ones.)</span>
|
||||
<!-- <button pButton pRipple label="Add Licence" class="w-auto" (click)="addLicence()"></button> -->
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<button pButton pRipple label="Update Profile" class="w-auto" (click)="updateProfile(user)"></button>
|
||||
<button pButton pRipple label="Update Profile" class="w-auto"
|
||||
(click)="updateProfile(user)"></button>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="flex flex-column align-items-center flex-or mb-8">
|
||||
<span class="font-medium text-900 mb-2">Company Logo</span>
|
||||
<span class="font-medium text-xs mb-2">(is shown in every offer)</span>
|
||||
<img [src]="companyLogoUrl" class="rounded-logo"/>
|
||||
<p-fileUpload mode="basic" chooseLabel="Upload" name="file" [url]="uploadCompanyUrl" accept="image/*" [maxFileSize]="maxFileSize" (onUpload)="onUploadCompanyLogo($event)" [auto]="true" styleClass="p-button-outlined p-button-plain p-button-rounded mt-4"></p-fileUpload>
|
||||
@if(user.hasCompanyLogo){
|
||||
<img src="{{environment.apiBaseUrl}}/logo/{{user.id}}.avif" class="rounded-profile" />
|
||||
} @else {
|
||||
<img src="assets/images/placeholder.png" class="rounded-profile" />
|
||||
}
|
||||
<p-fileUpload #companyUpload mode="basic" chooseLabel="Upload" name="file" [customUpload]="true"
|
||||
accept="image/*" [maxFileSize]="maxFileSize" (onSelect)="select($event,'company')"
|
||||
styleClass="p-button-outlined p-button-plain p-button-rounded mt-4"></p-fileUpload>
|
||||
</div>
|
||||
<p-divider></p-divider>
|
||||
<div class="flex flex-column align-items-center flex-or">
|
||||
<span class="font-medium text-900 mb-2">Your Profile Picture</span>
|
||||
<img [src]="profileUrl" class="rounded-profile"/>
|
||||
<p-fileUpload mode="basic" chooseLabel="Upload" name="file" [url]="uploadProfileUrl" accept="image/*" [maxFileSize]="maxFileSize" (onUpload)="onUploadProfilePicture($event)" [auto]="true" styleClass="p-button-outlined p-button-plain p-button-rounded mt-4"></p-fileUpload>
|
||||
@if(user.hasProfile){
|
||||
<img src="{{environment.apiBaseUrl}}/profile/{{user.id}}.avif" class="rounded-profile" />
|
||||
} @else {
|
||||
<img src="assets/images/person_placeholder.jpg" class="rounded-profile" />
|
||||
}
|
||||
<p-fileUpload #profileUpload mode="basic" chooseLabel="Upload" name="file" [customUpload]="true"
|
||||
accept="image/*" [maxFileSize]="maxFileSize" (onSelect)="select($event,'profile')"
|
||||
styleClass="p-button-outlined p-button-plain p-button-rounded mt-4"></p-fileUpload>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-900 font-semibold text-lg mt-3">Membership Level</div>
|
||||
<p-divider></p-divider>
|
||||
<p-table [value]="userSubscriptions" [tableStyle]="{ 'min-width': '50rem' }" dataKey="id">
|
||||
<p-table [value]="userSubscriptions" [tableStyle]="{ 'min-width': '50rem' }" dataKey="id">
|
||||
<ng-template pTemplate="header">
|
||||
<tr>
|
||||
<th style="width: 5rem"></th>
|
||||
@@ -116,7 +165,9 @@
|
||||
<ng-template pTemplate="body" let-subscription let-expanded="expanded">
|
||||
<tr>
|
||||
<td>
|
||||
<button type="button" pButton pRipple [pRowToggler]="subscription" class="p-button-text p-button-rounded p-button-plain" [icon]="expanded ? 'pi pi-chevron-down' : 'pi pi-chevron-right'"></button>
|
||||
<button type="button" pButton pRipple [pRowToggler]="subscription"
|
||||
class="p-button-text p-button-rounded p-button-plain"
|
||||
[icon]="expanded ? 'pi pi-chevron-down' : 'pi pi-chevron-right'"></button>
|
||||
</td>
|
||||
<td>{{ subscription.id }}</td>
|
||||
<td>{{ subscription.level }}</td>
|
||||
@@ -132,31 +183,51 @@
|
||||
<div class="p-3">
|
||||
<p-table [value]="subscription.invoices" dataKey="id">
|
||||
<ng-template pTemplate="header">
|
||||
<tr>
|
||||
<th style="width: 5rem"></th>
|
||||
<th>ID</th>
|
||||
<th>Date</th>
|
||||
<th>Price</th>
|
||||
</tr>
|
||||
</ng-template>
|
||||
<ng-template pTemplate="body" let-invoice>
|
||||
<tr>
|
||||
<td>
|
||||
<button pButton pRipple icon="pi pi-print" class="p-button-rounded p-button-success mr-2" (click)="printInvoice(invoice)"></button>
|
||||
</td>
|
||||
<td>{{ invoice.id }}</td>
|
||||
<td>{{ invoice.date | date}}</td>
|
||||
<td>{{ invoice.price | currency}}</td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
</ng-template>
|
||||
</p-table>
|
||||
</div>
|
||||
<tr>
|
||||
<th style="width: 5rem"></th>
|
||||
<th>ID</th>
|
||||
<th>Date</th>
|
||||
<th>Price</th>
|
||||
</tr>
|
||||
</ng-template>
|
||||
<ng-template pTemplate="body" let-invoice>
|
||||
<tr>
|
||||
<td>
|
||||
<button pButton pRipple icon="pi pi-print" class="p-button-rounded p-button-success mr-2"
|
||||
(click)="printInvoice(invoice)"></button>
|
||||
</td>
|
||||
<td>{{ invoice.id }}</td>
|
||||
<td>{{ invoice.date | date}}</td>
|
||||
<td>{{ invoice.price | currency}}</td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
</ng-template>
|
||||
</p-table>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</ng-template>
|
||||
</p-table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<p-dialog header="Edit Image" [visible]="imageUrl" [modal]="true" [style]="{ width: '50vw' }" [draggable]="false" [resizable]="false">
|
||||
<!-- <app-cropper #cropper [imageUrl]="imageUrl"></app-cropper> -->
|
||||
<angular-cropper #cropper [imageUrl]="imageUrl" [cropperOptions]="config"></angular-cropper>
|
||||
<ng-template pTemplate="footer" let-config="config">
|
||||
<div class="flex justify-content-between">
|
||||
@if(type==='company'){
|
||||
<div>
|
||||
<p-selectButton [options]="stateOptions" [ngModel]="value" (ngModelChange)="changeAspectRation($event)" optionLabel="label" optionValue="value"></p-selectButton>
|
||||
</div>
|
||||
} @else {
|
||||
<div></div>
|
||||
}
|
||||
<div>
|
||||
<p-button icon="pi" (click)="cancelUpload()" label="Cancel" [outlined]="true"></p-button>
|
||||
<p-button icon="pi pi-check" (click)="sendImage()" label="Finish" pAutoFocus [autofocus]="true"></p-button>
|
||||
</div>
|
||||
</div>
|
||||
</ng-template>
|
||||
</p-dialog>
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ChangeDetectorRef, Component } from '@angular/core';
|
||||
import { ChangeDetectorRef, Component, ViewChild } from '@angular/core';
|
||||
import { ButtonModule } from 'primeng/button';
|
||||
import { CheckboxModule } from 'primeng/checkbox';
|
||||
import { InputTextModule } from 'primeng/inputtext';
|
||||
@@ -15,56 +15,72 @@ import { ChipModule } from 'primeng/chip';
|
||||
import { MenuAccountComponent } from '../../menu-account/menu-account.component';
|
||||
import { DividerModule } from 'primeng/divider';
|
||||
import { TableModule } from 'primeng/table';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { HttpClient, HttpEventType } from '@angular/common/http';
|
||||
import { UserService } from '../../../services/user.service';
|
||||
import { SharedModule } from '../../../shared/shared/shared.module';
|
||||
import { SubscriptionsService } from '../../../services/subscriptions.service';
|
||||
import { lastValueFrom } from 'rxjs';
|
||||
import { MessageService } from 'primeng/api';
|
||||
import { environment } from '../../../../environments/environment';
|
||||
import { FileUploadModule } from 'primeng/fileupload';
|
||||
import { AutoCompleteCompleteEvent, Invoice, Subscription, User } from '../../../../../../common-models/src/main.model';
|
||||
import { FileUpload, FileUploadModule } from 'primeng/fileupload';
|
||||
import { AutoCompleteCompleteEvent, Invoice, KeyValue, KeyValueRatio, Subscription, User } from '../../../../../../common-models/src/main.model';
|
||||
import { GeoService } from '../../../services/geo.service';
|
||||
import { ChangeDetectionStrategy } from '@angular/compiler';
|
||||
|
||||
|
||||
import { EditorModule } from 'primeng/editor';
|
||||
import { LoadingService } from '../../../services/loading.service';
|
||||
import { AngularCropperjsModule, CropperComponent } from 'angular-cropperjs';
|
||||
import { ImageService } from '../../../services/image.service';
|
||||
import { DialogModule } from 'primeng/dialog';
|
||||
import { SelectButtonModule } from 'primeng/selectbutton';
|
||||
@Component({
|
||||
selector: 'app-account',
|
||||
standalone: true,
|
||||
// imports: [CommonModule, StyleClassModule, MenuAccountComponent, DividerModule,ButtonModule, TableModule, InputTextModule, DropdownModule, FormsModule, ChipModule,InputTextareaModule ],
|
||||
imports: [SharedModule,FileUploadModule],
|
||||
imports: [SharedModule,FileUploadModule,EditorModule,AngularCropperjsModule,DialogModule,SelectButtonModule],
|
||||
providers:[MessageService],
|
||||
templateUrl: './account.component.html',
|
||||
styleUrl: './account.component.scss'
|
||||
})
|
||||
export class AccountComponent {
|
||||
@ViewChild(CropperComponent) public angularCropper: CropperComponent;
|
||||
@ViewChild('companyUpload') public companyUpload: FileUpload;
|
||||
@ViewChild('profileUpload') public profileUpload: FileUpload;
|
||||
private id: string | undefined = this.activatedRoute.snapshot.params['id'] as string | undefined;
|
||||
user:User;
|
||||
subscriptions:Array<Subscription>;
|
||||
userSubscriptions:Array<Subscription>=[];
|
||||
uploadProfileUrl:string;
|
||||
uploadCompanyUrl:string;
|
||||
maxFileSize=1000000;
|
||||
companyLogoUrl:string;
|
||||
profileUrl:string;
|
||||
imageUrl;
|
||||
type:'company'|'profile'
|
||||
stateOptions:KeyValueRatio[]=[
|
||||
{label:'16/9',value:16/9},
|
||||
{label:'1/1',value:1},
|
||||
{label:'2/3',value:2/3},
|
||||
]
|
||||
value:number = this.stateOptions[0].value;
|
||||
config={aspectRatio: this.value}
|
||||
environment=environment
|
||||
constructor(public userService: UserService,
|
||||
private subscriptionService: SubscriptionsService,
|
||||
private messageService: MessageService,
|
||||
private geoService:GeoService,
|
||||
public selectOptions:SelectOptionsService,
|
||||
private cdref:ChangeDetectorRef) {
|
||||
this.user=this.userService.getUser()
|
||||
private cdref:ChangeDetectorRef,
|
||||
private activatedRoute: ActivatedRoute,
|
||||
private loadingService:LoadingService,
|
||||
private imageUploadService: ImageService) {
|
||||
|
||||
}
|
||||
async ngOnInit(){
|
||||
|
||||
this.user=await this.userService.getById(this.id);
|
||||
this.userSubscriptions=await lastValueFrom(this.subscriptionService.getAllSubscriptions());
|
||||
this.uploadProfileUrl = `${environment.apiBaseUrl}/bizmatch/image/uploadProfile/${this.user.id}`;
|
||||
this.uploadCompanyUrl = `${environment.apiBaseUrl}/bizmatch/image/uploadCompanyLogo/${this.user.id}`;
|
||||
if (!this.user.licensedIn || this.user.licensedIn?.length===0){
|
||||
this.user.licensedIn = [{name:'',value:''}]
|
||||
}
|
||||
this.user=await this.userService.getById(this.user.id);
|
||||
this.profileUrl = this.user.hasProfile?`${environment.apiBaseUrl}/profile/${this.user.id}`:`/assets/images/placeholder.png`
|
||||
this.companyLogoUrl = this.user.hasCompanyLogo?`${environment.apiBaseUrl}/logo/${this.user.id}`:`/assets/images/placeholder.png`
|
||||
this.profileUrl = this.user.hasProfile?`${environment.apiBaseUrl}/profile/${this.user.id}.avif`:`/assets/images/placeholder.png`
|
||||
this.companyLogoUrl = this.user.hasCompanyLogo?`${environment.apiBaseUrl}/logo/${this.user.id}.avif`:`/assets/images/placeholder.png`
|
||||
}
|
||||
printInvoice(invoice:Invoice){}
|
||||
|
||||
@@ -99,4 +115,60 @@ export class AccountComponent {
|
||||
removeLicence(){
|
||||
this.user.licensedIn.splice(this.user.licensedIn.length-2,1);
|
||||
}
|
||||
|
||||
select(event:any,type:'company'|'profile'){
|
||||
this.imageUrl = URL.createObjectURL(event.files[0]);
|
||||
this.type=type
|
||||
this.config={aspectRatio: type==='company'?this.stateOptions[0].value:this.stateOptions[2].value}
|
||||
}
|
||||
sendImage(){
|
||||
this.imageUrl=null
|
||||
this.loadingService.startLoading('uploadImage');
|
||||
this.angularCropper.cropper.getCroppedCanvas().toBlob(async(blob) => {
|
||||
if (this.type==='company'){
|
||||
this.imageUploadService.uploadCompanyLogo(blob,this.user.id).subscribe(async(event) => {
|
||||
if (event.type === HttpEventType.UploadProgress) {
|
||||
// Berechne und zeige den Fortschritt basierend auf event.loaded und event.total
|
||||
const progress = event.total ? event.loaded / event.total : 0;
|
||||
console.log(`Upload-Fortschritt: ${progress * 100}%`);
|
||||
// Hier könntest du beispielsweise eine Fortschrittsanzeige aktualisieren
|
||||
} else if (event.type === HttpEventType.Response) {
|
||||
console.log('Upload abgeschlossen', event.body);
|
||||
this.companyUpload.clear();
|
||||
this.loadingService.stopLoading('uploadImage');
|
||||
this.companyLogoUrl=`${environment.apiBaseUrl}/logo/${this.user.id}.avif?_ts=${new Date().getTime()}`
|
||||
}
|
||||
}, error => console.error('Fehler beim Upload:', error));
|
||||
} else {
|
||||
this.imageUploadService.uploadProfileImage(blob,this.user.id).subscribe(async(event) => {
|
||||
if (event.type === HttpEventType.UploadProgress) {
|
||||
// Berechne und zeige den Fortschritt basierend auf event.loaded und event.total
|
||||
const progress = event.total ? event.loaded / event.total : 0;
|
||||
console.log(`Upload-Fortschritt: ${progress * 100}%`);
|
||||
// Hier könntest du beispielsweise eine Fortschrittsanzeige aktualisieren
|
||||
} else if (event.type === HttpEventType.Response) {
|
||||
console.log('Upload abgeschlossen', event.body);
|
||||
this.profileUpload.clear();
|
||||
this.loadingService.stopLoading('uploadImage');
|
||||
this.profileUrl=`${environment.apiBaseUrl}/profile/${this.user.id}.avif?_ts=${new Date().getTime()}`
|
||||
}
|
||||
}, error => console.error('Fehler beim Upload:', error));
|
||||
}
|
||||
|
||||
|
||||
// this.fileUpload.upload();
|
||||
}, 'image/png');
|
||||
}
|
||||
cancelUpload(){
|
||||
this.imageUrl=null
|
||||
if (this.type==='company'){
|
||||
this.companyUpload.clear();
|
||||
} else {
|
||||
this.profileUpload.clear();
|
||||
}
|
||||
}
|
||||
changeAspectRation(ratio:number){
|
||||
this.config={aspectRatio: ratio}
|
||||
this.angularCropper.cropper.setAspectRatio(ratio);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -134,7 +134,7 @@ export class EditListingComponent {
|
||||
this.loadingService.startLoading('uploadImage');
|
||||
this.angularCropper.cropper.getCroppedCanvas().toBlob(async(blob) => {
|
||||
|
||||
this.imageUploadService.uploadImage(blob).subscribe(async(event) => {
|
||||
this.imageUploadService.uploadPropertyImage(blob,this.listing.id).subscribe(async(event) => {
|
||||
if (event.type === HttpEventType.UploadProgress) {
|
||||
// Berechne und zeige den Fortschritt basierend auf event.loaded und event.total
|
||||
const progress = event.total ? event.loaded / event.total : 0;
|
||||
|
||||
@@ -1,23 +1,44 @@
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { environment } from '../../environments/environment';
|
||||
import { lastValueFrom } from 'rxjs';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class ImageService {
|
||||
|
||||
private uploadUrl = 'http://localhost:3000/bizmatch/image/uploadPropertyPicture/1a4b800e-793c-4c47-b987-7bf634060a4e';
|
||||
private apiBaseUrl = environment.apiBaseUrl;
|
||||
|
||||
constructor(private http: HttpClient) { }
|
||||
|
||||
uploadImage(imageBlob: Blob) {
|
||||
uploadPropertyImage(imageBlob: Blob,listingId:string) {
|
||||
const uploadUrl = `${this.apiBaseUrl}/bizmatch/image/uploadPropertyPicture/${listingId}`;
|
||||
return this.uploadImage(imageBlob,uploadUrl);
|
||||
}
|
||||
uploadCompanyLogo(imageBlob: Blob,userId:string) {
|
||||
const uploadUrl = `${this.apiBaseUrl}/bizmatch/image/uploadCompanyLogo/${userId}`;
|
||||
return this.uploadImage(imageBlob,uploadUrl);
|
||||
}
|
||||
uploadProfileImage(imageBlob: Blob,userId:string) {
|
||||
const uploadUrl = `${this.apiBaseUrl}/bizmatch/image/uploadProfile/${userId}`;
|
||||
return this.uploadImage(imageBlob,uploadUrl);
|
||||
}
|
||||
uploadImage(imageBlob: Blob,uploadUrl:string) {
|
||||
const formData = new FormData();
|
||||
formData.append('file', imageBlob, 'image.png');
|
||||
|
||||
return this.http.post(this.uploadUrl, formData,{
|
||||
return this.http.post(uploadUrl, formData,{
|
||||
// headers: this.headers,
|
||||
reportProgress: true,
|
||||
observe: 'events',
|
||||
});
|
||||
}
|
||||
|
||||
async getProfileImagesForUsers(userids:string[]){
|
||||
return await lastValueFrom(this.http.get<[]>(`${this.apiBaseUrl}/bizmatch/image/profileImages/${userids.join(',')}`));
|
||||
}
|
||||
async getCompanyLogosForUsers(userids:string[]){
|
||||
return await lastValueFrom(this.http.get<[]>(`${this.apiBaseUrl}/bizmatch/image/companyLogos/${userids.join(',')}`));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ export class LoadingService {
|
||||
public startLoading(type: string,request?:string): void {
|
||||
if (!this.loading$.value.includes(type)) {
|
||||
this.loading$.next(this.loading$.value.concat(type));
|
||||
if (type==='uploadImage' || request?.includes('uploadPropertyPicture')) {
|
||||
if (type==='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);
|
||||
|
||||
BIN
bizmatch/src/assets/images/person_placeholder.jpg
Normal file
BIN
bizmatch/src/assets/images/person_placeholder.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 70 KiB |
@@ -0,0 +1,3 @@
|
||||
[ZoneTransfer]
|
||||
LastWriterPackageFamilyName=Microsoft.Windows.Photos_8wekyb3d8bbwe
|
||||
ZoneId=3
|
||||
Reference in New Issue
Block a user