broker direcrtory renewed, imageservice updated, demo data
This commit is contained in:
parent
73ab12a694
commit
840d7a63b1
|
|
@ -30,19 +30,33 @@ export class FileService {
|
|||
return this.subscriptions
|
||||
}
|
||||
async storeProfilePicture(file: Express.Multer.File, userId: string) {
|
||||
const suffix = file.mimetype.includes('png') ? 'png' : 'jpg'
|
||||
await fs.outputFile(`./pictures/profile/${userId}`, file.buffer);
|
||||
// const suffix = file.mimetype.includes('png') ? 'png' : 'jpg'
|
||||
// await fs.outputFile(`./pictures/profile/${userId}`, file.buffer);
|
||||
let quality = 50;
|
||||
const output = await sharp(file.buffer)
|
||||
.resize({ width: 300 })
|
||||
.avif({ quality }) // Verwende AVIF
|
||||
//.webp({ quality }) // Verwende Webp
|
||||
.toBuffer();
|
||||
await sharp(output).toFile(`./pictures/profile/${userId}.avif`);
|
||||
}
|
||||
hasProfile(userId: string){
|
||||
return fs.existsSync(`./pictures/profile/${userId}`)
|
||||
return fs.existsSync(`./pictures/profile/${userId}.avif`)
|
||||
}
|
||||
|
||||
async storeCompanyLogo(file: Express.Multer.File, userId: string) {
|
||||
const suffix = file.mimetype.includes('png') ? 'png' : 'jpg'
|
||||
await fs.outputFile(`./pictures/logo/${userId}`, file.buffer);
|
||||
// const suffix = file.mimetype.includes('png') ? 'png' : 'jpg'
|
||||
let quality = 50;
|
||||
const output = await sharp(file.buffer)
|
||||
.resize({ width: 300 })
|
||||
.avif({ quality }) // Verwende AVIF
|
||||
//.webp({ quality }) // Verwende Webp
|
||||
.toBuffer();
|
||||
await sharp(output).toFile(`./pictures/logo/${userId}.avif`); // Ersetze Dateierweiterung
|
||||
// await fs.outputFile(`./pictures/logo/${userId}`, file.buffer);
|
||||
}
|
||||
hasCompanyLogo(userId: string){
|
||||
return fs.existsSync(`./pictures/logo/${userId}`)
|
||||
return fs.existsSync(`./pictures/logo/${userId}.avif`)
|
||||
}
|
||||
|
||||
async getPropertyImages(listingId: string): Promise<ImageProperty[]> {
|
||||
|
|
@ -103,13 +117,28 @@ export class FileService {
|
|||
//.webp({ quality }) // Verwende Webp
|
||||
.toBuffer();
|
||||
|
||||
if (output.byteLength > maxSize) {
|
||||
quality -= 5; // Justiere Qualität in feineren Schritten
|
||||
}
|
||||
// if (output.byteLength > maxSize) {
|
||||
// quality -= 5; // Justiere Qualität in feineren Schritten
|
||||
// }
|
||||
// } while (output.byteLength > maxSize && quality > 0);
|
||||
await sharp(output).toFile(`${directory}/${imageName}.avif`); // Ersetze Dateierweiterung
|
||||
let timeTaken = Date.now() - start;
|
||||
this.logger.info(`Quality: ${quality} - Time: ${timeTaken} milliseconds`)
|
||||
}
|
||||
|
||||
getProfileImagesForUsers(userids:string){
|
||||
const ids = userids.split(',');
|
||||
let result = {};
|
||||
for (const id of ids){
|
||||
result = {...result,[id]:fs.existsSync(`./pictures/profile/${id}.avif`)}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
getCompanyLogosForUsers(userids:string){
|
||||
const ids = userids.split(',');
|
||||
let result = {};
|
||||
for (const id of ids){
|
||||
result = {...result,[id]:fs.existsSync(`./pictures/logo/${id}.avif`)}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,13 +23,22 @@ export class ImageController {
|
|||
|
||||
@Post('uploadProfile/:id')
|
||||
@UseInterceptors(FileInterceptor('file'),)
|
||||
uploadProfile(@UploadedFile() file: Express.Multer.File,@Param('id') id:string) {
|
||||
this.fileService.storeProfilePicture(file,id);
|
||||
async uploadProfile(@UploadedFile() file: Express.Multer.File,@Param('id') id:string) {
|
||||
await this.fileService.storeProfilePicture(file,id);
|
||||
}
|
||||
|
||||
@Post('uploadCompanyLogo/:id')
|
||||
@UseInterceptors(FileInterceptor('file'),)
|
||||
uploadCompanyLogo(@UploadedFile() file: Express.Multer.File,@Param('id') id:string) {
|
||||
this.fileService.storeCompanyLogo(file,id);
|
||||
async uploadCompanyLogo(@UploadedFile() file: Express.Multer.File,@Param('id') id:string) {
|
||||
await this.fileService.storeCompanyLogo(file,id);
|
||||
}
|
||||
|
||||
@Get('profileImages/:userids')
|
||||
async getProfileImagesForUsers(@Param('userids') userids:string): Promise<any> {
|
||||
return await this.fileService.getProfileImagesForUsers(userids);
|
||||
}
|
||||
@Get('companyLogos/:userids')
|
||||
async getCompanyLogosForUsers(@Param('userids') userids:string): Promise<any> {
|
||||
return await this.fileService.getCompanyLogosForUsers(userids);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,22 +7,30 @@ import { UserEntity } from 'src/models/server.model.js';
|
|||
@Controller('user')
|
||||
export class UserController {
|
||||
|
||||
constructor(private userService:UserService,@Inject(WINSTON_MODULE_PROVIDER) private readonly logger: Logger){}
|
||||
constructor(private userService: UserService, @Inject(WINSTON_MODULE_PROVIDER) private readonly logger: Logger) {}
|
||||
|
||||
@Get(':id')
|
||||
findById(@Param('id') id:string): any {
|
||||
return this.userService.getUserById(id);
|
||||
findById(@Param('id') id: string): any {
|
||||
this.logger.info(`Searching for user with ID: ${id}`);
|
||||
const user = this.userService.getUserById(id);
|
||||
this.logger.info(`Found user: ${JSON.stringify(user)}`);
|
||||
return user;
|
||||
}
|
||||
|
||||
@Post()
|
||||
save(@Body() user: any):Promise<UserEntity>{
|
||||
this.logger.info(`User persisted: `);
|
||||
return this.userService.saveUser(user);
|
||||
save(@Body() user: any): Promise<UserEntity> {
|
||||
this.logger.info(`Saving user: ${JSON.stringify(user)}`);
|
||||
const savedUser = this.userService.saveUser(user);
|
||||
this.logger.info(`User persisted: ${JSON.stringify(savedUser)}`);
|
||||
return savedUser;
|
||||
}
|
||||
|
||||
@Post('search')
|
||||
find(@Body() criteria: any): any {
|
||||
return this.userService.findUser(criteria);
|
||||
this.logger.info(`Searching for users with criteria: ${JSON.stringify(criteria)}`);
|
||||
const foundUsers = this.userService.findUser(criteria);
|
||||
this.logger.info(`Found users: ${JSON.stringify(foundUsers)}`);
|
||||
return foundUsers;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ export class UserService {
|
|||
companyWebsite:{ type: 'string' },
|
||||
companyLocation:{ type: 'string' },
|
||||
offeredServices:{ type: 'string' },
|
||||
areasServed:{ type: 'string' },
|
||||
areasServed:{ type: 'string[]' },
|
||||
names:{ type: 'string[]', path:'$.licensedIn.name' },
|
||||
values:{ type: 'string[]', path:'$.licensedIn.value' }
|
||||
}, {
|
||||
|
|
|
|||
|
|
@ -31,7 +31,8 @@
|
|||
"src/assets"
|
||||
],
|
||||
"styles": [
|
||||
"src/styles.scss"
|
||||
"src/styles.scss",
|
||||
"node_modules/quill/dist/quill.snow.css"
|
||||
]
|
||||
},
|
||||
"configurations": {
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@
|
|||
"primeflex": "^3.3.1",
|
||||
"primeicons": "^6.0.1",
|
||||
"primeng": "^17.10.0",
|
||||
"quill": "^1.3.7",
|
||||
"rxjs": "~7.8.1",
|
||||
"tslib": "^2.3.0",
|
||||
"urlcat": "^3.1.0",
|
||||
|
|
|
|||
|
|
@ -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,54 +48,103 @@
|
|||
<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>
|
||||
<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="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>
|
||||
<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 >
|
||||
|
||||
<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>
|
||||
<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 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>
|
||||
<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>
|
||||
|
|
@ -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>
|
||||
|
|
@ -142,7 +193,8 @@
|
|||
<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>
|
||||
<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>
|
||||
|
|
@ -158,5 +210,24 @@
|
|||
</ng-template>
|
||||
</p-table>
|
||||
</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);
|
||||
|
|
|
|||
Binary file not shown.
|
After Width: | Height: | Size: 70 KiB |
|
|
@ -0,0 +1,3 @@
|
|||
[ZoneTransfer]
|
||||
LastWriterPackageFamilyName=Microsoft.Windows.Photos_8wekyb3d8bbwe
|
||||
ZoneId=3
|
||||
|
|
@ -2,6 +2,10 @@ export interface KeyValue {
|
|||
name: string;
|
||||
value: string;
|
||||
}
|
||||
export interface KeyValueRatio {
|
||||
label: string;
|
||||
value: number;
|
||||
}
|
||||
export interface KeyValueStyle {
|
||||
name: string;
|
||||
value: string;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,13 @@
|
|||
import fs from 'fs-extra';
|
||||
|
||||
(async () => {
|
||||
const listings = await fs.readJson('./users.json');
|
||||
//listings.forEach(element => {
|
||||
for (const listing of listings) {
|
||||
const response = await fetch('http://localhost:3000/bizmatch/user', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(listing),
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
});
|
||||
}
|
||||
})();
|
||||
|
|
@ -106,7 +106,7 @@ async function extractListingData(page: Page): Promise<BusinessListing | null> {
|
|||
employees: content[labels.employeesLabel],
|
||||
reasonForSale: content[labels.reasonLabel],
|
||||
internals: '',
|
||||
} as BusinessListing;
|
||||
} as any;
|
||||
return listing;
|
||||
} catch (error) {
|
||||
console.log(`Fehler bei ${title}`);
|
||||
|
|
|
|||
|
|
@ -21,9 +21,9 @@ import { BusinessListing } from "../common-models/src/main.model"
|
|||
})
|
||||
const listings:Array<BusinessListing> = await response.json();
|
||||
for (const listing of listings) {
|
||||
const option = selectOptions.locations.find(l=>l.name.toLowerCase()===listing.location.toLowerCase());
|
||||
const option = selectOptions.locations.find(l=>l.name.toLowerCase()===listing.state.toLowerCase());
|
||||
if (option){
|
||||
listing.location=option.value
|
||||
listing.state=option.value
|
||||
}
|
||||
const response = await fetch(`http://localhost:3000/bizmatch/listings/${listing.id}`, {
|
||||
method: 'PUT',
|
||||
|
|
|
|||
|
|
@ -0,0 +1,156 @@
|
|||
[
|
||||
{
|
||||
"id": "8a2b1c5d-7e6f-4g3h-9i1j-2k3l4m5n6o7p",
|
||||
"firstname": "Sarah",
|
||||
"lastname": "Thompson",
|
||||
"email": "sarah.thompson@businessbrokers.com",
|
||||
"licensedIn": [
|
||||
{
|
||||
"name": "California",
|
||||
"value": "123456"
|
||||
},
|
||||
{
|
||||
"name": "Nevada",
|
||||
"value": "789012"
|
||||
}
|
||||
],
|
||||
"phoneNumber": "(310) 555-1234",
|
||||
"companyLocation": "Los Angeles - CA",
|
||||
"hasCompanyLogo": true,
|
||||
"hasProfile": true,
|
||||
"companyName": "Business Brokers Inc.",
|
||||
"companyWebsite": "https://www.businessbrokers.com",
|
||||
"description": "Experienced business brokers helping clients buy and sell businesses in California and Nevada.",
|
||||
"companyOverview": "<p>Business Brokers Inc. is a team of experienced professionals dedicated to helping clients navigate the complex process of buying and selling businesses. With a strong focus on client satisfaction and a deep understanding of the local markets in California and Nevada, we strive to deliver exceptional results for our clients.</p>",
|
||||
"areasServed": [
|
||||
"Los Angeles County, CA",
|
||||
"Orange County, CA",
|
||||
"San Diego County, CA",
|
||||
"Clark County, NV",
|
||||
"Washoe County, NV"
|
||||
],
|
||||
"offeredServices": "<h2>Services Offered</h2><ul><li>Business valuation</li><li>Market analysis</li><li>Buyer and seller representation</li><li>Due diligence assistance</li><li>Negotiations and closing support</li></ul>"
|
||||
},
|
||||
{
|
||||
"id": "4q5r6s7t-8u9v-0w1x-2y3z-4a5b6c7d8e9f",
|
||||
"firstname": "Michael",
|
||||
"lastname": "Johnson",
|
||||
"email": "michael@bizbrokers.net",
|
||||
"licensedIn": [
|
||||
{
|
||||
"name": "Florida",
|
||||
"value": "654321"
|
||||
}
|
||||
],
|
||||
"phoneNumber": "(407) 555-9876",
|
||||
"companyLocation": "Orlando - FL",
|
||||
"hasCompanyLogo": false,
|
||||
"hasProfile": true,
|
||||
"companyName": "BizBrokers",
|
||||
"companyWebsite": "https://www.bizbrokers.net",
|
||||
"description": "Helping business owners in Florida sell their businesses and assisting buyers in finding the perfect opportunity.",
|
||||
"companyOverview": "<p>At BizBrokers, we understand the unique challenges of buying and selling businesses in the Florida market. Our team of experienced brokers is committed to providing personalized service and expert guidance to ensure a smooth and successful transaction for our clients.</p>",
|
||||
"areasServed": [
|
||||
"Orange County, FL",
|
||||
"Seminole County, FL",
|
||||
"Osceola County, FL",
|
||||
"Brevard County, FL",
|
||||
"Volusia County, FL"
|
||||
],
|
||||
"offeredServices": "<h2>Services Offered</h2><ul><li>Business listings</li><li>Buyer search and qualification</li><li>Due diligence support</li><li>Financing assistance</li><li>Closing coordination</li></ul>"
|
||||
},
|
||||
{
|
||||
"id": "1g2h3i4j-5k6l-7m8n-9o0p-1q2r3s4t5u6v",
|
||||
"firstname": "Emily",
|
||||
"lastname": "Davis",
|
||||
"email": "emily.davis@bizsaleexperts.com",
|
||||
"licensedIn": [
|
||||
{
|
||||
"name": "New York",
|
||||
"value": "987654"
|
||||
},
|
||||
{
|
||||
"name": "New Jersey",
|
||||
"value": "210987"
|
||||
}
|
||||
],
|
||||
"phoneNumber": "(212) 555-4321",
|
||||
"companyLocation": "New York City - NY",
|
||||
"hasCompanyLogo": true,
|
||||
"hasProfile": true,
|
||||
"companyName": "Business Sale Experts",
|
||||
"companyWebsite": "https://www.bizsaleexperts.com",
|
||||
"description": "Experienced business brokers specializing in the sale of businesses in the New York and New Jersey area.",
|
||||
"companyOverview": "<p>Business Sale Experts is a leading business brokerage firm serving the New York and New Jersey markets. With a team of seasoned professionals and a proven track record of success, we are dedicated to helping our clients achieve their goals in the sale or acquisition of businesses.</p>",
|
||||
"areasServed": [
|
||||
"New York County, NY",
|
||||
"Kings County, NY",
|
||||
"Queens County, NY",
|
||||
"Bronx County, NY",
|
||||
"Hudson County, NJ",
|
||||
"Bergen County, NJ",
|
||||
"Essex County, NJ"
|
||||
],
|
||||
"offeredServices": "<h2>Services Offered</h2><ul><li>Business valuation</li><li>Marketing and advertising</li><li>Buyer screening and qualification</li><li>Negotiations and deal structuring</li><li>Due diligence coordination</li><li>Closing support</li></ul>"
|
||||
},
|
||||
{
|
||||
"id": "7w8x9y0z-1a2b-3c4d-5e6f-7g8h9i0j1k2l",
|
||||
"firstname": "David",
|
||||
"lastname": "Wilson",
|
||||
"email": "david.wilson@bizacquisitions.com",
|
||||
"licensedIn": [
|
||||
{
|
||||
"name": "Texas",
|
||||
"value": "543210"
|
||||
}
|
||||
],
|
||||
"phoneNumber": "(713) 555-8765",
|
||||
"companyLocation": "Houston - TX",
|
||||
"hasCompanyLogo": true,
|
||||
"hasProfile": true,
|
||||
"companyName": "Business Acquisitions Ltd.",
|
||||
"companyWebsite": "https://www.bizacquisitions.com",
|
||||
"description": "Helping business owners in Texas sell their businesses and assisting buyers in finding the perfect acquisition opportunity.",
|
||||
"companyOverview": "<p>Business Acquisitions Ltd. is a leading business brokerage firm serving the Texas market. Our team of experienced brokers is dedicated to providing expert guidance and personalized service to help our clients achieve their goals in the sale or acquisition of businesses.</p>",
|
||||
"areasServed": [
|
||||
"Harris County, TX",
|
||||
"Dallas County, TX",
|
||||
"Tarrant County, TX",
|
||||
"Bexar County, TX",
|
||||
"Travis County, TX"
|
||||
],
|
||||
"offeredServices": "<h2>Services Offered</h2><ul><li>Business valuation</li><li>Market analysis</li><li>Buyer search and qualification</li><li>Due diligence coordination</li><li>Financing assistance</li><li>Closing support</li></ul>"
|
||||
},
|
||||
{
|
||||
"id": "3m4n5o6p-7q8r-9s0t-1u2v-3w4x5y6z7a8b",
|
||||
"firstname": "Jessica",
|
||||
"lastname": "Brown",
|
||||
"email": "jessica@bizbrokerexperts.com",
|
||||
"licensedIn": [
|
||||
{
|
||||
"name": "Illinois",
|
||||
"value": "876543"
|
||||
},
|
||||
{
|
||||
"name": "Wisconsin",
|
||||
"value": "109876"
|
||||
}
|
||||
],
|
||||
"phoneNumber": "(312) 555-2109",
|
||||
"companyLocation": "Chicago - IL",
|
||||
"hasCompanyLogo": true,
|
||||
"hasProfile": true,
|
||||
"companyName": "Business Broker Experts",
|
||||
"companyWebsite": "https://www.bizbrokerexperts.com",
|
||||
"description": "Experienced business brokers helping clients buy and sell businesses in Illinois and Wisconsin.",
|
||||
"companyOverview": "<p>Business Broker Experts is a leading business brokerage firm serving the Illinois and Wisconsin markets. With a team of seasoned professionals and a commitment to client satisfaction, we strive to deliver exceptional results for our clients in the sale or acquisition of businesses.</p>",
|
||||
"areasServed": [
|
||||
"Cook County, IL",
|
||||
"DuPage County, IL",
|
||||
"Lake County, IL",
|
||||
"Milwaukee County, WI",
|
||||
"Dane County, WI"
|
||||
],
|
||||
"offeredServices": "<h2>Services Offered</h2><ul><li>Business valuation</li><li>Market analysis</li><li>Buyer and seller representation</li><li>Due diligence assistance</li><li>Negotiations and closing support</li></ul>"
|
||||
}
|
||||
]
|
||||
Loading…
Reference in New Issue