Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • web-et-numerique/factory/pamn_plateforme-des-acteurs-de-la-mediation-numerique/pamn_client
1 result
Show changes
Commits on Source (4)
Showing
with 197 additions and 137 deletions
...@@ -3,12 +3,14 @@ ...@@ -3,12 +3,14 @@
<thead> <thead>
<th scope="col">Utilisateur</th> <th scope="col">Utilisateur</th>
<th scope="col">Structure</th> <th scope="col">Structure</th>
<th scope="col">Date de demande</th>
<th scope="col">Options</th> <th scope="col">Options</th>
</thead> </thead>
<tbody> <tbody>
<tr *ngFor="let structure of demandsAttachment"> <tr *ngFor="let structure of demandsAttachment">
<td>{{ structure.userEmail }}</td> <td>{{ structure.userEmail }}</td>
<td>{{ structure.structureName }}</td> <td>{{ structure.structureName }}</td>
<td>{{ structure.createdAt | date: 'mediumDate' }}</td>
<td> <td>
<button (click)="acceptDemand(structure)">Valider</button <button (click)="acceptDemand(structure)">Valider</button
><button (click)="refuseDemand(structure)">Refuser</button> ><button (click)="refuseDemand(structure)">Refuser</button>
......
import { Component, OnInit } from '@angular/core'; import { Component, OnInit } from '@angular/core';
import { NotificationService } from '../../../services/notification.service';
import { StructureAdminInfo } from '../../models/demandAttachment.model'; import { StructureAdminInfo } from '../../models/demandAttachment.model';
import { AdminService } from '../../services/admin.service'; import { AdminService } from '../../services/admin.service';
...@@ -12,7 +13,7 @@ export class ClaimStructureComponent implements OnInit { ...@@ -12,7 +13,7 @@ export class ClaimStructureComponent implements OnInit {
public structuresUnclaimed: StructureAdminInfo[]; public structuresUnclaimed: StructureAdminInfo[];
public isClaimedStructure: boolean = true; public isClaimedStructure: boolean = true;
public isUnclaimedStructure: boolean = false; public isUnclaimedStructure: boolean = false;
constructor(private adminService: AdminService) {} constructor(private adminService: AdminService, private notificationService: NotificationService) {}
ngOnInit(): void { ngOnInit(): void {
this.adminService.getPendingStructure().subscribe((demands) => { this.adminService.getPendingStructure().subscribe((demands) => {
...@@ -21,19 +22,27 @@ export class ClaimStructureComponent implements OnInit { ...@@ -21,19 +22,27 @@ export class ClaimStructureComponent implements OnInit {
} }
public acceptDemand(demand: StructureAdminInfo): void { public acceptDemand(demand: StructureAdminInfo): void {
this.adminService this.adminService.acceptStructureClaim(demand.userEmail, demand.structureId, demand.structureName).subscribe({
.acceptStructureClaim(demand.userEmail, demand.structureId, demand.structureName) next: (data) => {
.subscribe((data) => {
this.demandsAttachment = data; this.demandsAttachment = data;
}); this.notificationService.showSuccess('Demande acceptée avec succès');
},
error: (e) => {
this.notificationService.showError('Une erreur est survenue');
},
});
} }
public refuseDemand(demand: StructureAdminInfo): void { public refuseDemand(demand: StructureAdminInfo): void {
this.adminService this.adminService.refuseStructureClaim(demand.userEmail, demand.structureId, demand.structureName).subscribe({
.refuseStructureClaim(demand.userEmail, demand.structureId, demand.structureName) next: (data) => {
.subscribe((data) => {
this.demandsAttachment = data; this.demandsAttachment = data;
}); this.notificationService.showSuccess('Demande refusée avec succès');
},
error: (e) => {
this.notificationService.showError('Une erreur est survenue');
},
});
} }
public claimedStructure(_event: boolean): void { public claimedStructure(_event: boolean): void {
......
...@@ -2,4 +2,5 @@ export class StructureAdminInfo { ...@@ -2,4 +2,5 @@ export class StructureAdminInfo {
userEmail: string; userEmail: string;
structureId: number; structureId: number;
structureName: string; structureName: string;
createdAt: string;
} }
...@@ -150,7 +150,21 @@ const routes: Routes = [ ...@@ -150,7 +150,21 @@ const routes: Routes = [
], ],
}, },
{ {
path: 'join/:id', path: 'join-request/:id',
children: [
{
path: '',
canActivate: [AuthGuard],
component: StructureJoinComponent,
resolve: {
structure: StructureResolver,
},
},
footerOutletRoute,
],
},
{
path: 'join-validation',
children: [ children: [
{ {
path: '', path: '',
......
...@@ -11,6 +11,7 @@ import { ProfileService } from '../../profile/services/profile.service'; ...@@ -11,6 +11,7 @@ import { ProfileService } from '../../profile/services/profile.service';
import { NotificationService } from '../../services/notification.service'; import { NotificationService } from '../../services/notification.service';
import { PersonalOfferService } from '../../services/personal-offer.service'; import { PersonalOfferService } from '../../services/personal-offer.service';
import { StructureService } from '../../services/structure.service'; import { StructureService } from '../../services/structure.service';
import { UserService } from '../../services/user.service';
import { MustMatch } from '../../shared/validator/form'; import { MustMatch } from '../../shared/validator/form';
import { CustomRegExp } from '../../utils/CustomRegExp'; import { CustomRegExp } from '../../utils/CustomRegExp';
import { formUtils } from '../../utils/formUtils'; import { formUtils } from '../../utils/formUtils';
...@@ -86,7 +87,8 @@ export class FormViewComponent implements OnInit, AfterViewInit { ...@@ -86,7 +87,8 @@ export class FormViewComponent implements OnInit, AfterViewInit {
private route: ActivatedRoute, private route: ActivatedRoute,
private router: Router, private router: Router,
private structureService: StructureService, private structureService: StructureService,
private utils: Utils private utils: Utils,
private usersService: UserService
) {} ) {}
ngAfterViewInit(): void { ngAfterViewInit(): void {
...@@ -151,7 +153,7 @@ export class FormViewComponent implements OnInit, AfterViewInit { ...@@ -151,7 +153,7 @@ export class FormViewComponent implements OnInit, AfterViewInit {
this.route.data.subscribe((data) => { this.route.data.subscribe((data) => {
if (data.user) { if (data.user) {
this.createAccountForm(data.user.email); this.createAccountForm(data.user.email);
this.linkedStructureId = data.user.pendingStructuresLink; this.linkedStructureId = data.user.structuresLink;
this.currentForm = this.accountForm; this.currentForm = this.accountForm;
this.isAccountMode = true; this.isAccountMode = true;
} }
...@@ -275,7 +277,7 @@ export class FormViewComponent implements OnInit, AfterViewInit { ...@@ -275,7 +277,7 @@ export class FormViewComponent implements OnInit, AfterViewInit {
this.structureService.isClaimed(this.structure._id, this.profile).subscribe((isClaimed) => { this.structureService.isClaimed(this.structure._id, this.profile).subscribe((isClaimed) => {
this.structure.isClaimed = isClaimed; this.structure.isClaimed = isClaimed;
if (isClaimed) { if (isClaimed) {
this.structureService.joinStructure(this.structureForm.value._id, this.profile.email).subscribe(() => { this.usersService.joinStructure(this.structureForm.value._id, this.profile.email).subscribe(() => {
this.currentPage = structureFormStep.mailSentInfo; this.currentPage = structureFormStep.mailSentInfo;
}); });
} else { } else {
......
...@@ -13,7 +13,7 @@ export class PersonalOfferGuard implements CanActivate { ...@@ -13,7 +13,7 @@ export class PersonalOfferGuard implements CanActivate {
(this.router.routerState.snapshot.url === '/profile' || (this.router.routerState.snapshot.url === '/profile' ||
this.router.routerState.snapshot.url === '/form/profile' || this.router.routerState.snapshot.url === '/form/profile' ||
this.router.routerState.snapshot.url === '/form/structure' || this.router.routerState.snapshot.url === '/form/structure' ||
this.router.routerState.snapshot.url.includes('/join/')) this.router.routerState.snapshot.url.includes('/join-request/'))
) { ) {
return true; return true;
} }
......
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
import { UntypedFormGroup } from '@angular/forms'; import { UntypedFormGroup } from '@angular/forms';
import { pendingStructureLink } from '../../../../models/pendingStructure.model';
import { Structure } from '../../../../models/structure.model'; import { Structure } from '../../../../models/structure.model';
import { ProfileService } from '../../../../profile/services/profile.service'; import { ProfileService } from '../../../../profile/services/profile.service';
import { StructureService } from '../../../../services/structure.service'; import { StructureService } from '../../../../services/structure.service';
...@@ -22,6 +23,7 @@ export class ProfileStructureChoiceComponent implements OnInit { ...@@ -22,6 +23,7 @@ export class ProfileStructureChoiceComponent implements OnInit {
public isAlreadySearching = false; public isAlreadySearching = false;
public buttonTypeEnum = ButtonType; public buttonTypeEnum = ButtonType;
public profileStructuresLink: string[] = []; public profileStructuresLink: string[] = [];
public profilePendingStructureLink: pendingStructureLink[] = [];
constructor(private structureService: StructureService, private profileService: ProfileService) {} constructor(private structureService: StructureService, private profileService: ProfileService) {}
...@@ -29,7 +31,8 @@ export class ProfileStructureChoiceComponent implements OnInit { ...@@ -29,7 +31,8 @@ export class ProfileStructureChoiceComponent implements OnInit {
this.isAlreadySearching = true; this.isAlreadySearching = true;
this.profileService.getProfile().then((profile) => { this.profileService.getProfile().then((profile) => {
this.isAlreadySearching = false; this.isAlreadySearching = false;
this.profileStructuresLink = [...profile.structuresLink, ...profile.pendingStructuresLink]; this.profileStructuresLink = [...profile.structuresLink];
this.profilePendingStructureLink = [...profile.pendingStructuresLink];
this.getStructures(null); this.getStructures(null);
}); });
} }
...@@ -68,7 +71,10 @@ export class ProfileStructureChoiceComponent implements OnInit { ...@@ -68,7 +71,10 @@ export class ProfileStructureChoiceComponent implements OnInit {
this.isAlreadySearching = true; this.isAlreadySearching = true;
this.structureService.getStructuresByName(filters).subscribe((structures) => { this.structureService.getStructuresByName(filters).subscribe((structures) => {
structures.forEach((structure) => { structures.forEach((structure) => {
if (this.profileStructuresLink.includes(structure._id)) { if (
this.profileStructuresLink.includes(structure._id) ||
this.profilePendingStructureLink.map((pending) => pending.id).includes(structure._id)
) {
structure.alreadySelected = true; structure.alreadySelected = true;
} }
}); });
......
export class pendingStructureLink {
id: string;
token: string;
createdAt: string;
}
import { Employer } from './employer.model'; import { Employer } from './employer.model';
import { Job } from './job.model'; import { Job } from './job.model';
import { pendingStructureLink } from './pendingStructure.model';
export class User { export class User {
_id: string; _id: string;
...@@ -13,7 +14,7 @@ export class User { ...@@ -13,7 +14,7 @@ export class User {
role: number; role: number;
validationToken: string; validationToken: string;
structuresLink: string[]; structuresLink: string[];
pendingStructuresLink: string[] = []; pendingStructuresLink: pendingStructureLink[] = [];
profileImage: string; profileImage: string;
personalOffers: string[] = []; personalOffers: string[] = [];
job: Job; job: Job;
......
...@@ -56,6 +56,7 @@ export class EditComponent implements OnInit { ...@@ -56,6 +56,7 @@ export class EditComponent implements OnInit {
public employers: Employer[]; public employers: Employer[];
private selectedEmployer: Employer; private selectedEmployer: Employer;
private isAlreadySearching = false; private isAlreadySearching = false;
public isNewUser = false;
@ViewChild('searchEmployer') searchEmployer: ElementRef; @ViewChild('searchEmployer') searchEmployer: ElementRef;
@ViewChild('newJobInput') newJobInput: ElementRef; @ViewChild('newJobInput') newJobInput: ElementRef;
...@@ -76,7 +77,9 @@ export class EditComponent implements OnInit { ...@@ -76,7 +77,9 @@ export class EditComponent implements OnInit {
this.userProfile = new User(profile); this.userProfile = new User(profile);
this.initialUserProfile = { ...profile }; this.initialUserProfile = { ...profile };
this.selectedEmployer = { ...profile.employer }; this.selectedEmployer = { ...profile.employer };
if (!profile.employer || !profile.job) {
this.isNewUser = true;
}
const otherJob = new Job({ name: 'Autre' }); const otherJob = new Job({ name: 'Autre' });
this.profileService.getJobs().subscribe((jobs) => { this.profileService.getJobs().subscribe((jobs) => {
this.jobs = [...jobs, otherJob]; this.jobs = [...jobs, otherJob];
...@@ -165,10 +168,18 @@ export class EditComponent implements OnInit { ...@@ -165,10 +168,18 @@ export class EditComponent implements OnInit {
return this.passwordValid(this.newPassword) && this.newPassword == this.newPasswordConfirm; return this.passwordValid(this.newPassword) && this.newPassword == this.newPasswordConfirm;
} }
} else if (this.currentTab === tabsEnum.employer) { } else if (this.currentTab === tabsEnum.employer) {
return !!( if (this.isNewUser) {
if (!this.selectedEmployer || !this.selectedJob) {
return false;
}
return true;
} else if (
this.selectedEmployer?.name !== this.userProfile.employer?.name || this.selectedEmployer?.name !== this.userProfile.employer?.name ||
this.selectedJob?.name !== this.userProfile.job?.name this.selectedJob?.name !== this.userProfile.job?.name
); ) {
return true;
}
return false;
} else if (this.currentTab === tabsEnum.description) { } else if (this.currentTab === tabsEnum.description) {
return this.descriptionValid() && this.initialUserProfile.description !== this.userProfile.description; return this.descriptionValid() && this.initialUserProfile.description !== this.userProfile.description;
} }
......
<div class="structureCard" [ngClass]="{ fold: !showDetails }"> <div class="structureCard" [ngClass]="{ fold: !showDetails, pending: isPending }">
<div class="collapseHeader" (click)="toggleDetails()"> <div class="collapseHeader" (click)="toggleDetails(); $event.stopPropagation()">
<div class="left"> <div class="left">
<app-svg-icon [type]="'ico'" [icon]="getStructureTypeIcon()" [iconClass]="'icon-52'"></app-svg-icon> <app-svg-icon [type]="'ico'" [icon]="getStructureTypeIcon()" [iconClass]="'icon-52'"></app-svg-icon>
<div class="structureInfos"> <div class="structureInfos">
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
</div> </div>
</div> </div>
<div class="right"> <div class="right">
<div class="missingData" *ngIf="!isPublic && !isValid()"> <div class="missingData" *ngIf="!isPublic && !isValid() && !isPending">
<app-svg-icon [iconClass]="'icon-26'" [type]="'form'" [icon]="'notValidate'"></app-svg-icon> <app-svg-icon [iconClass]="'icon-26'" [type]="'form'" [icon]="'notValidate'"></app-svg-icon>
<p class="invalidText hide-on-mobile"> <p class="invalidText hide-on-mobile">
Informations Informations
...@@ -16,6 +16,21 @@ ...@@ -16,6 +16,21 @@
manquantes manquantes
</p> </p>
</div> </div>
<div class="missingData" *ngIf="isPending">
<p class="invalidText hide-on-mobile">
En attente d'acceptation <br />
Demande faite le {{ getFormattedDate() }}
</p>
</div>
<app-button
*ngIf="isPending"
class="hide-on-mobile"
[type]="'button'"
[iconType]="'form'"
[text]="'Annuler la demande'"
[style]="buttonTypeEnum.Secondary"
(click)="handleCancelJoin(structure._id); $event.stopPropagation()"
></app-button>
<app-svg-icon <app-svg-icon
class="showDetails" class="showDetails"
[ngClass]="!showDetails ? 'visible' : 'hidden'" [ngClass]="!showDetails ? 'visible' : 'hidden'"
...@@ -52,7 +67,7 @@ ...@@ -52,7 +67,7 @@
[routerLinkActive]="'active'" [routerLinkActive]="'active'"
></app-button> ></app-button>
<app-button <app-button
*ngIf="!isPublic" *ngIf="!isPublic && !isPending"
class="hide-on-mobile" class="hide-on-mobile"
[type]="'button'" [type]="'button'"
[iconBtn]="'edit'" [iconBtn]="'edit'"
...@@ -63,7 +78,7 @@ ...@@ -63,7 +78,7 @@
[ngClass]="{ warning: !isValid() }" [ngClass]="{ warning: !isValid() }"
></app-button> ></app-button>
<app-button <app-button
*ngIf="!isPublic" *ngIf="!isPublic && !isPending"
class="hide-on-desktop" class="hide-on-desktop"
[type]="'button'" [type]="'button'"
[iconBtn]="'edit'" [iconBtn]="'edit'"
...@@ -84,7 +99,7 @@ ...@@ -84,7 +99,7 @@
</div> </div>
<app-personal-offer <app-personal-offer
class="section" class="section"
*ngIf="this.personalOffer" *ngIf="this.personalOffer && !isPending"
[personalOffer]="personalOffer" [personalOffer]="personalOffer"
[isPublic]="isPublic" [isPublic]="isPublic"
> >
...@@ -93,7 +108,7 @@ ...@@ -93,7 +108,7 @@
<div class="sectionHeader"> <div class="sectionHeader">
<p class="sectionTitle">membres</p> <p class="sectionTitle">membres</p>
<app-button <app-button
*ngIf="!isPublic" *ngIf="!isPublic && !isPending"
class="hide-on-mobile" class="hide-on-mobile"
[type]="'button'" [type]="'button'"
[iconBtn]="'edit'" [iconBtn]="'edit'"
...@@ -103,7 +118,7 @@ ...@@ -103,7 +118,7 @@
[routerLinkActive]="'active'" [routerLinkActive]="'active'"
></app-button> ></app-button>
<app-button <app-button
*ngIf="!isPublic" *ngIf="!isPublic && !isPending"
class="hide-on-desktop" class="hide-on-desktop"
[type]="'button'" [type]="'button'"
[iconBtn]="'edit'" [iconBtn]="'edit'"
...@@ -120,7 +135,7 @@ ...@@ -120,7 +135,7 @@
></app-profile-structure-member> ></app-profile-structure-member>
</div> </div>
</div> </div>
<div class="call-to-action" *ngIf="!isPublic && members.length === 0"> <div class="call-to-action" *ngIf="!isPublic && members.length === 0 && !isPending">
<app-button <app-button
[type]="'button'" [type]="'button'"
[iconBtn]="'add'" [iconBtn]="'add'"
...@@ -130,7 +145,10 @@ ...@@ -130,7 +145,10 @@
(click)="addMemberModalOpenned = true" (click)="addMemberModalOpenned = true"
></app-button> ></app-button>
</div> </div>
<div class="call-to-action" *ngIf="!isPublic && !this.personalOffer && userProfile.job.hasPersonalOffer"> <div
class="call-to-action"
*ngIf="!isPublic && !this.personalOffer && userProfile.job?.hasPersonalOffer && !isPending"
>
<app-button <app-button
[type]="'button'" [type]="'button'"
[iconBtn]="'add'" [iconBtn]="'add'"
...@@ -143,7 +161,7 @@ ...@@ -143,7 +161,7 @@
</div> </div>
</div> </div>
<app-structure-add-member-modal <app-structure-add-member-modal
*ngIf="addMemberModalOpenned" *ngIf="addMemberModalOpenned && !isPending"
[openned]="addMemberModalOpenned" [openned]="addMemberModalOpenned"
[structure]="structureWithOwners" [structure]="structureWithOwners"
(closed)="closeAddMemberModal($event)" (closed)="closeAddMemberModal($event)"
......
...@@ -8,11 +8,13 @@ ...@@ -8,11 +8,13 @@
border: 1px solid $grey-5; border: 1px solid $grey-5;
border-radius: 4px; border-radius: 4px;
overflow: hidden; overflow: hidden;
&.fold { &.fold {
background-color: $grey-8; background-color: $grey-8;
border: 1px solid $grey-8; border: 1px solid $grey-8;
} }
&.pending {
border: 1px solid $orange-warning;
}
.collapseHeader { .collapseHeader {
padding: 0 0.5rem; padding: 0 0.5rem;
...@@ -31,6 +33,11 @@ ...@@ -31,6 +33,11 @@
.right { .right {
display: flex; display: flex;
align-items: center;
::ng-deep button {
margin-right: 1rem;
margin-top: 0.25rem;
}
.showDetails { .showDetails {
transition: all 0.3s; transition: all 0.3s;
&.visible { &.visible {
......
import { animate, AUTO_STYLE, state, style, transition, trigger } from '@angular/animations'; import { animate, AUTO_STYLE, state, style, transition, trigger } from '@angular/animations';
import { Component, Input, OnInit } from '@angular/core'; import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
import { FormGroup } from '@angular/forms'; import { FormGroup } from '@angular/forms';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { DateTime } from 'luxon';
import { structureFormStep } from '../../form/form-view/structure-form/structureFormStep.enum'; import { structureFormStep } from '../../form/form-view/structure-form/structureFormStep.enum';
import { Structure } from '../../models/structure.model'; import { Structure } from '../../models/structure.model';
import { StructureWithOwners } from '../../models/structureWithOwners.model'; import { StructureWithOwners } from '../../models/structureWithOwners.model';
import { NotificationService } from '../../services/notification.service';
import { StructureService } from '../../services/structure.service'; import { StructureService } from '../../services/structure.service';
import { ButtonType } from '../../shared/components/button/buttonType.enum'; import { ButtonType } from '../../shared/components/button/buttonType.enum';
import { SearchService } from '../../structure-list/services/search.service'; import { SearchService } from '../../structure-list/services/search.service';
...@@ -32,6 +32,10 @@ export class ProfileStructureComponent implements OnInit { ...@@ -32,6 +32,10 @@ export class ProfileStructureComponent implements OnInit {
@Input() public structureWithOwners: StructureWithOwners; @Input() public structureWithOwners: StructureWithOwners;
@Input() public userProfile: User; @Input() public userProfile: User;
@Input() public isPublic: boolean; @Input() public isPublic: boolean;
@Input() public isPending = false;
@Input() public joinRequestDate: string | null;
@Output() cancelJoin = new EventEmitter<any>();
public members: User[] = []; public members: User[] = [];
public structureForm: FormGroup; public structureForm: FormGroup;
public buttonTypeEnum = ButtonType; public buttonTypeEnum = ButtonType;
...@@ -43,7 +47,6 @@ export class ProfileStructureComponent implements OnInit { ...@@ -43,7 +47,6 @@ export class ProfileStructureComponent implements OnInit {
constructor( constructor(
private router: Router, private router: Router,
private userService: UserService, private userService: UserService,
private notificationService: NotificationService,
private searchService: SearchService, private searchService: SearchService,
private structureService: StructureService, private structureService: StructureService,
public utils: Utils public utils: Utils
...@@ -68,11 +71,9 @@ export class ProfileStructureComponent implements OnInit { ...@@ -68,11 +71,9 @@ export class ProfileStructureComponent implements OnInit {
// Check if user has personal offers // Check if user has personal offers
if ( if (
!this.userProfile || !this.userProfile?.personalOffers ||
!this.userProfile.job || !this.userProfile?.job?.hasPersonalOffer ||
!this.userProfile.personalOffers || this.userProfile?.personalOffers?.length === 0
!this.userProfile.job.hasPersonalOffer ||
this.userProfile.personalOffers.length === 0
) )
return null; return null;
...@@ -134,4 +135,11 @@ export class ProfileStructureComponent implements OnInit { ...@@ -134,4 +135,11 @@ export class ProfileStructureComponent implements OnInit {
}); });
} }
} }
public getFormattedDate(): string {
if (this.joinRequestDate) return DateTime.fromISO(this.joinRequestDate, { zone: 'Europe/Paris' }).toISODate();
}
public handleCancelJoin(idStructure: string): void {
this.cancelJoin.emit(idStructure);
}
} }
...@@ -83,7 +83,20 @@ ...@@ -83,7 +83,20 @@
[routerLinkActive]="'active'" [routerLinkActive]="'active'"
></app-button> ></app-button>
</div> </div>
<div class="structuresContainer" *ngIf="userProfile.structuresLink.length > 0 && structures"> <div
class="structuresContainer"
*ngIf="userProfile.structuresLink.length > 0 || userProfile.pendingStructuresLink.length > 0"
>
<div *ngFor="let structure of pendingStructures; let i = index">
<app-profile-structure
[structureWithOwners]="structure"
[userProfile]="this.userProfile"
[isPublic]="this.isPublic"
[isPending]="true"
[joinRequestDate]="userProfile.pendingStructuresLink[i].createdAt"
(cancelJoin)="cancelJoin($event)"
></app-profile-structure>
</div>
<div *ngFor="let structure of structures; let i = index"> <div *ngFor="let structure of structures; let i = index">
<app-profile-structure <app-profile-structure
[structureWithOwners]="structure" [structureWithOwners]="structure"
......
...@@ -10,6 +10,8 @@ import { Utils } from '../utils/utils'; ...@@ -10,6 +10,8 @@ import { Utils } from '../utils/utils';
import { catchError, map, tap } from 'rxjs/operators'; import { catchError, map, tap } from 'rxjs/operators';
import { forkJoin, Observable } from 'rxjs'; import { forkJoin, Observable } from 'rxjs';
import { Location } from '@angular/common'; import { Location } from '@angular/common';
import { NotificationService } from '../services/notification.service';
import { pendingStructureLink } from '../models/pendingStructure.model';
@Component({ @Component({
selector: 'app-profile', selector: 'app-profile',
...@@ -19,6 +21,7 @@ import { Location } from '@angular/common'; ...@@ -19,6 +21,7 @@ import { Location } from '@angular/common';
export class ProfileComponent implements OnInit { export class ProfileComponent implements OnInit {
public userProfile: User; public userProfile: User;
public structures: StructureWithOwners[] = []; public structures: StructureWithOwners[] = [];
public pendingStructures: StructureWithOwners[] = [];
public buttonTypeEnum = ButtonType; public buttonTypeEnum = ButtonType;
public isPublic: boolean; public isPublic: boolean;
...@@ -29,7 +32,8 @@ export class ProfileComponent implements OnInit { ...@@ -29,7 +32,8 @@ export class ProfileComponent implements OnInit {
private route: ActivatedRoute, private route: ActivatedRoute,
private router: Router, private router: Router,
private location: Location, private location: Location,
public utils: Utils public utils: Utils,
private notificationService: NotificationService
) {} ) {}
ngOnInit(): void { ngOnInit(): void {
...@@ -49,12 +53,14 @@ export class ProfileComponent implements OnInit { ...@@ -49,12 +53,14 @@ export class ProfileComponent implements OnInit {
.subscribe((user) => { .subscribe((user) => {
this.userProfile = new User(user); this.userProfile = new User(user);
this.getStructuresFromProfile(); this.getStructuresFromProfile();
this.getPendingStructuresFromProfile();
}); });
} else { } else {
this.isPublic = false; this.isPublic = false;
this.profileService.getProfile().then((profile: User) => { this.profileService.getProfile().then((profile: User) => {
this.userProfile = new User(profile); this.userProfile = new User(profile);
this.getStructuresFromProfile(); this.getStructuresFromProfile();
this.getPendingStructuresFromProfile();
}); });
} }
}); });
...@@ -77,6 +83,22 @@ export class ProfileComponent implements OnInit { ...@@ -77,6 +83,22 @@ export class ProfileComponent implements OnInit {
this.structures.sort((a, b) => a.structure.structureName.localeCompare(b.structure.structureName)); this.structures.sort((a, b) => a.structure.structureName.localeCompare(b.structure.structureName));
}); });
} }
private getPendingStructuresFromProfile() {
const structures$: Observable<any>[] = [];
this.structures = [];
this.userProfile.pendingStructuresLink.forEach((pending: pendingStructureLink) => {
structures$.push(
this.structureService.getStructureWithOwners(pending.id, null).pipe(
tap((structure) => {
this.pendingStructures.push(structure);
})
)
);
});
forkJoin(structures$).subscribe(() => {
this.pendingStructures.sort((a, b) => a.structure.structureName.localeCompare(b.structure.structureName));
});
}
public goBack(): void { public goBack(): void {
this.location.back(); this.location.back();
...@@ -85,4 +107,17 @@ export class ProfileComponent implements OnInit { ...@@ -85,4 +107,17 @@ export class ProfileComponent implements OnInit {
public addStructure(): void { public addStructure(): void {
this.router.navigateByUrl('/form/structure'); this.router.navigateByUrl('/form/structure');
} }
public cancelJoin(idStructure: string): void {
this.userService.cancelJoin(idStructure, this.userProfile._id).subscribe({
next: () => {
const index = this.pendingStructures.map((s) => s.structure._id).indexOf(idStructure);
this.pendingStructures.splice(index, 1);
this.notificationService.showSuccess('La demande a été annulée avec succès', '');
},
error: (err) => {
this.notificationService.showError(`${err.error.message}`, 'Une erreur est survenue');
console.error(err);
},
});
}
} }
...@@ -49,16 +49,7 @@ export class ProfileService { ...@@ -49,16 +49,7 @@ export class ProfileService {
if (!this.currentProfile) { if (!this.currentProfile) {
return false; return false;
} }
return this.currentProfile.pendingStructuresLink.includes(idStructure); return this.currentProfile.pendingStructuresLink.map((pending) => pending.id).includes(idStructure);
}
public removeProfile(): void {
this.currentProfile = null;
}
public createUserandLinkStructure(id: string, body: User): Observable<User> {
body.pendingStructuresLink = [id];
return this.http.post<any>(`${this.baseUrl}`, body);
} }
public isAdmin(): boolean { public isAdmin(): boolean {
......
...@@ -3,15 +3,11 @@ import { Injectable } from '@angular/core'; ...@@ -3,15 +3,11 @@ import { Injectable } from '@angular/core';
import * as _ from 'lodash'; import * as _ from 'lodash';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { map } from 'rxjs/operators'; import { map } from 'rxjs/operators';
import { OpeningDay } from '../models/openingDay.model';
import { Structure } from '../models/structure.model'; import { Structure } from '../models/structure.model';
import { StructureWithOwners } from '../models/structureWithOwners.model'; import { StructureWithOwners } from '../models/structureWithOwners.model';
import { TempUser } from '../models/temp-user.model'; import { TempUser } from '../models/temp-user.model';
import { User } from '../models/user.model'; import { User } from '../models/user.model';
import { WeekDayEnum } from '../shared/enum/weekDay.enum';
import { Weekday } from '../structure-list/enum/weekday.enum';
import { Filter } from '../structure-list/models/filter.model'; import { Filter } from '../structure-list/models/filter.model';
const { DateTime } = require('luxon');
@Injectable({ @Injectable({
providedIn: 'root', providedIn: 'root',
...@@ -54,10 +50,6 @@ export class StructureService { ...@@ -54,10 +50,6 @@ export class StructureService {
return this.http.get<Structure>(`${this.baseUrl}/${id}`); return this.http.get<Structure>(`${this.baseUrl}/${id}`);
} }
public joinStructure(id: string, email: string): Observable<Structure> {
return this.http.post<any>(`${this.baseUrl}/${id}/join`, { email });
}
public delete(id: string): Observable<Structure> { public delete(id: string): Observable<Structure> {
return this.http.delete<Structure>(`${this.baseUrl}/${id}`); return this.http.delete<Structure>(`${this.baseUrl}/${id}`);
} }
...@@ -103,78 +95,6 @@ export class StructureService { ...@@ -103,78 +95,6 @@ export class StructureService {
}); });
} }
/**
* Checks if the current time is in the time interval of the structure
* @param startTime start of period
* @param endTime end of period
* @param currentTime actual time
*/
private compareSchedules(startTime: string, endTime: string, currentTime: typeof DateTime): boolean {
const day = currentTime.toISO().split('T')[0];
let start = DateTime.fromISO(`${day}T${startTime}`);
if (startTime.length === 4) {
start = DateTime.fromISO(`${day}T0${startTime}`);
}
const end = DateTime.fromISO(`${day}T${endTime}`);
return currentTime > start && currentTime < end;
}
// Get enum key
private getEnumKeyByEnumValue(enumBase, enumValue): number {
let keys = Object.keys(enumBase).filter((x) => {
if (enumBase[x].toString().toLowerCase() == enumValue) {
return x;
}
});
return keys.length > 0 ? parseInt(keys[0]) : null;
}
private getNextOpening(s: Structure, dayOfWeek: number, currentTime: typeof DateTime): OpeningDay {
let periodBeforeCurrentDay = null;
const time = currentTime.toISO().split('T')[1];
const currentHour = new Date('1/1/1999 ' + time.split('+')[0]);
// Browse day of week
for (const [i, period] of Object.entries(s.hours)) {
if (period.open) {
// Check if it's current day
if (i === this.numberToDay(dayOfWeek)) {
if (
(new Date('1/1/1999 ' + period.time[0].opening) <= currentHour &&
new Date('1/1/1999 ' + period.time[0].closing) >= currentHour) ||
(period.time[1] &&
new Date('1/1/1999 ' + period.time[1].opening) <= currentHour &&
new Date('1/1/1999 ' + period.time[1].closing) >= currentHour)
) {
return new OpeningDay(i, null);
} else if (new Date('1/1/1999 ' + period.time[0].opening) > currentHour) {
return new OpeningDay(i, this.numberToHour(period.time[0].opening));
} else if (period.time[1] && new Date('1/1/1999 ' + period.time[1].opening) > currentHour) {
return new OpeningDay(i, this.numberToHour(period.time[1].opening));
}
// Return the next day > current day.
} else if (
this.getEnumKeyByEnumValue(WeekDayEnum, i) >
this.getEnumKeyByEnumValue(WeekDayEnum, this.numberToDay(dayOfWeek))
) {
return new OpeningDay(i, this.numberToHour(period.time[0].opening));
// Return the next day < current day.
} else if (!periodBeforeCurrentDay) {
periodBeforeCurrentDay = new OpeningDay(i, this.numberToHour(period.time[0].opening));
}
}
}
return periodBeforeCurrentDay ? periodBeforeCurrentDay : '';
}
private numberToDay(n: number): string {
return Weekday[n];
}
private numberToHour(n: string): string {
return n.replace(':', 'h');
}
public getStructureWithOwners(structureId: string, profile: User): Observable<StructureWithOwners> { public getStructureWithOwners(structureId: string, profile: User): Observable<StructureWithOwners> {
return this.http.post<any>(`${this.baseUrl}/${structureId}/withOwners`, { emailUser: profile?.email }); return this.http.post<any>(`${this.baseUrl}/${structureId}/withOwners`, { emailUser: profile?.email });
} }
......
import { HttpClient } from '@angular/common/http'; import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { Structure } from '../models/structure.model';
import { User } from '../models/user.model'; import { User } from '../models/user.model';
@Injectable({ @Injectable({
...@@ -13,4 +14,15 @@ export class UserService { ...@@ -13,4 +14,15 @@ export class UserService {
public getUser(userId: string): Observable<User> { public getUser(userId: string): Observable<User> {
return this.http.get<User>(`${this.baseUrl}/${userId}`); return this.http.get<User>(`${this.baseUrl}/${userId}`);
} }
public joinStructure(id: string, email: string): Observable<Structure> {
return this.http.post<any>(`${this.baseUrl}/join-request/${id}`, { email });
}
public validateJoinStructure(token: string, status: string): Observable<any> {
return this.http.get<any>(`${this.baseUrl}/join-validate/${token}/${status}`);
}
public cancelJoin(idStructure: string, idUser: string): Observable<any> {
return this.http.get<any>(`${this.baseUrl}/join-cancel/${idStructure}/${idUser}
`);
}
} }
...@@ -53,7 +53,10 @@ ...@@ -53,7 +53,10 @@
</div> </div>
<!-- Je travaille ici --> <!-- Je travaille ici -->
<div <div
*ngIf="!profileService.isLinkedToStructure(structure._id)" *ngIf="
!profileService.isLinkedToStructure(structure._id) &&
!profileService.isPendingLinkedToStructure(structure._id)
"
class="clickableDiv" class="clickableDiv"
role="button" role="button"
(click)="handleJoin()" (click)="handleJoin()"
......
...@@ -11,6 +11,7 @@ import { ProfileService } from '../../../profile/services/profile.service'; ...@@ -11,6 +11,7 @@ import { ProfileService } from '../../../profile/services/profile.service';
import { AuthService } from '../../../services/auth.service'; import { AuthService } from '../../../services/auth.service';
import { StructureService } from '../../../services/structure.service'; import { StructureService } from '../../../services/structure.service';
import { TclService } from '../../../services/tcl.service'; import { TclService } from '../../../services/tcl.service';
import { UserService } from '../../../services/user.service';
import { PrintService } from '../../../shared/service/print.service'; import { PrintService } from '../../../shared/service/print.service';
import { Utils } from '../../../utils/utils'; import { Utils } from '../../../utils/utils';
import { AccessModality } from '../../enum/access-modality.enum'; import { AccessModality } from '../../enum/access-modality.enum';
...@@ -71,7 +72,8 @@ export class StructureDetailsComponent implements OnInit { ...@@ -71,7 +72,8 @@ export class StructureDetailsComponent implements OnInit {
private parametersService: ParametersService, private parametersService: ParametersService,
private route: ActivatedRoute, private route: ActivatedRoute,
private location: Location, private location: Location,
private router: Router private router: Router,
private usersService: UserService
) { ) {
this.route.url.subscribe((url) => { this.route.url.subscribe((url) => {
if (url.length > 0 && url[0].path === 'structure') { if (url.length > 0 && url[0].path === 'structure') {
...@@ -229,15 +231,15 @@ export class StructureDetailsComponent implements OnInit { ...@@ -229,15 +231,15 @@ export class StructureDetailsComponent implements OnInit {
this.structureService this.structureService
.claimStructureWithAccount(this.structure._id, this.authService.userValue.username) .claimStructureWithAccount(this.structure._id, this.authService.userValue.username)
.subscribe(); .subscribe();
this.router.navigate(['join', this.structure._id], { state: { isClaimed: this.structure.isClaimed } }); this.router.navigate(['join-request', this.structure._id], { state: { isClaimed: this.structure.isClaimed } });
} }
} }
public joinStructure(shouldJoin: boolean): void { public joinStructure(shouldJoin: boolean): void {
this.toggleJoinModal(); this.toggleJoinModal();
if (shouldJoin) { if (shouldJoin) {
this.structureService.joinStructure(this.structure._id, this.authService.userValue.username).subscribe(); this.usersService.joinStructure(this.structure._id, this.authService.userValue.username).subscribe();
this.router.navigate(['join', this.structure._id], { state: { isClaimed: this.structure.isClaimed } }); this.router.navigate(['join-request', this.structure._id], { state: { isClaimed: this.structure.isClaimed } });
} }
} }
......