Commit 1fb8fa2b authored by seraf's avatar seraf
Browse files

Merge branch 'features/routing_improvments' into 'develop'

refacto routing

See merge request !43
parents 2e913e26 a62e00c6
Pipeline #414994 failed with stage
in 2 minutes and 13 seconds
{
"owners": [
{ "id": 1, "email": "toto@gafam.com", "pseudo": "TOTO" },
{ "id": 2, "email": "titi@gafam.com", "pseudo": "TITI" }
{ "id": 1, "email": "titi@gafam.com", "pseudo": "TITI", "role": "REGISTERED" },
{ "id": 2, "email": "toto@gafam.com", "pseudo": "TOTO", "role": "REGISTERED" }
],
"voters": [{ "pseudo": "TOTO", "polls": [] }],
"polls": [
{
"id": 1,
......
{
"/api/v1/*": "/$1",
"/owners/:email/": "/owners?email=:email",
"/polls": "/polls?_expand=owner&_embed=choices&_embed=comments",
"/polls/:slug": "/polls?slug=:slug&_expand=owner&_embed=choices&_embed=comments",
"/polls/:slug/choices": "/choices?pollSlug=:slug",
"/polls/:slug/comments": "/comments?pollSlug=:slug"
......
......@@ -7,6 +7,10 @@ import { PageNotFoundComponent } from './shared/components/page-not-found/page-n
const routes: Routes = [
{ path: '', component: HomeComponent },
{
path: 'user',
loadChildren: () => import('./features/user-profile/user-profile.module').then((m) => m.UserProfileModule),
},
{
path: 'administration',
loadChildren: () =>
......@@ -14,12 +18,18 @@ const routes: Routes = [
resolve: { poll: PollService },
},
{
path: 'consultation',
path: 'poll/:slug/administration',
loadChildren: () =>
import('./features/administration/administration.module').then((m) => m.AdministrationModule),
resolve: { poll: PollService },
},
{
path: 'poll/:slug/consultation',
loadChildren: () => import('./features/consultation/consultation.module').then((m) => m.ConsultationModule),
resolve: { poll: PollService },
},
{
path: 'participation',
path: 'poll/:slug/participation',
loadChildren: () => import('./features/participation/participation.module').then((m) => m.ParticipationModule),
resolve: { poll: PollService },
},
......@@ -27,8 +37,8 @@ const routes: Routes = [
path: 'oldstuff',
loadChildren: () => import('./features/old-stuff/old-stuff.module').then((m) => m.OldStuffModule),
},
{ path: '**', component: PageNotFoundComponent },
{ path: 'page-not-found', component: PageNotFoundComponent },
{ path: '**', redirectTo: 'page-not-found', pathMatch: 'full' },
];
@NgModule({
......
......@@ -4,11 +4,9 @@ import { Subscription } from 'rxjs';
import { environment } from '../environments/environment';
import { Theme } from './core/enums/theme.enum';
import { UserRole } from './core/enums/user-role.enum';
import { User } from './core/models/user.model';
import { LanguageService } from './core/services/language.service';
import { MockingService } from './core/services/mocking.service';
import { ThemeService } from './core/services/theme.service';
import { MockingService } from './core/services/mocking.service';
@Component({
selector: 'app-root',
......@@ -32,7 +30,7 @@ export class AppComponent implements OnInit, OnDestroy {
if (!environment.production) {
this.appTitle += ' [DEV]';
// TODO: to be removed
this.mockingService.loadUser(new User('TOTO', 'toto@gafam.com', [], UserRole.REGISTERED));
this.mockingService.init();
}
this.titleService.setTitle(this.appTitle);
this.languageService.configureAndInitTranslations();
......@@ -41,7 +39,7 @@ export class AppComponent implements OnInit, OnDestroy {
case Theme.DARK:
this.themeClass = 'theme-dark-crystal';
break;
case Theme.RED:
case Theme.CONTRAST:
this.themeClass = 'theme-hot-covid';
break;
default:
......
<nav class="navbar" role="navigation" aria-label="main navigation">
<div class="navbar-brand">
<a class="navbar-item" role="button" (click)="toggleSidebarOpening()"> Dev menu </a>
<a
role="button"
class="navbar-burger burger"
aria-label="menu"
aria-expanded="false"
data-target="navbarBasicExample"
>
<span aria-hidden="true"></span>
<span aria-hidden="true"></span>
<span aria-hidden="true"></span>
</a>
</div>
<div id="navbarBasicExample" class="navbar-menu">
<div class="navbar-start">
<div class="navbar-item has-dropdown is-hoverable">
<a class="navbar-link"> Modules </a>
<div class="navbar-dropdown">
<a class="navbar-item" routerLink="oldstuff" routerLinkActive="is-active">
Old stuff
</a>
<a class="navbar-item" routerLink="administration" routerLinkActive="is-active">
Administration
</a>
<a class="navbar-item" routerLink="consultation" routerLinkActive="is-active">
Consultation
</a>
<a class="navbar-item" routerLink="participation" routerLinkActive="is-active">
Participation
</a>
</div>
</div>
<div class="navbar-item has-dropdown is-hoverable">
<a class="navbar-link"> Tous les sondages </a>
<div class="navbar-dropdown">
<a
class="navbar-item"
*ngFor="let slug of slugsAvailables"
routerLink="{{ '/consultation/poll/' + slug }}"
routerLinkActive="is-active"
>
« {{ slug }} »
</a>
</div>
</div>
</div>
</div>
</nav>
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { DevNavbarComponent } from './dev-navbar.component';
describe('DevNavbarComponent', () => {
let component: DevNavbarComponent;
let fixture: ComponentFixture<DevNavbarComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [DevNavbarComponent],
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(DevNavbarComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
import { Observable } from 'rxjs';
import { User } from '../../models/user.model';
import { ApiService } from '../../services/api.service';
import { UserService } from '../../services/user.service';
@Component({
selector: 'app-dev-navbar',
templateUrl: './dev-navbar.component.html',
styleUrls: ['./dev-navbar.component.scss'],
})
export class DevNavbarComponent implements OnInit {
@Input() isSidebarOpened: boolean;
@Output() toggleSidebarEE = new EventEmitter<boolean>();
public _user: Observable<User> = this.userService.user;
public slugsAvailables: string[] = [];
constructor(private apiService: ApiService, private userService: UserService) {}
public ngOnInit(): void {
this.getSlugs();
}
public async getSlugs(): Promise<void> {
this.slugsAvailables = await this.apiService.getAllPollsSlugs();
}
public toggleSidebarOpening(): void {
this.isSidebarOpened = !this.isSidebarOpened;
this.toggleSidebarEE.emit(this.isSidebarOpened);
}
}
......@@ -21,7 +21,7 @@
<a class="navbar-item" routerLink="administration" routerLinkActive="is-active">
Créer un sondage
</a>
<a class="navbar-item" routerLink="administration/user-polls" routerLinkActive="is-active">
<a class="navbar-item" routerLink="user/polls" routerLinkActive="is-active">
Mes sondages
</a>
</div>
......
......@@ -17,7 +17,7 @@
<h2 class="subtitle">
Où sont mes sondages?
</h2>
<a role="button" class="button is-fullwidth is-primary" routerLink="administration/user-polls">
<a role="button" class="button is-fullwidth is-primary" routerLink="user/polls">
Mes sondages
</a>
</div>
......
......@@ -2,16 +2,16 @@
<div class="navbar-item has-dropdown is-hoverable">
<a class="navbar-link"> Tous les sondages </a>
<div class="navbar-dropdown">
<a class="navbar-item" routerLink="/consultation/poll/inexistentPoll" routerLinkActive="is-active">
<a class="navbar-item" routerLink="/poll/inexistentPoll/consultation" routerLinkActive="is-active">
« inexistentPoll »
</a>
<a
class="navbar-item"
*ngFor="let slug of slugsAvailables"
routerLink="{{ '/consultation/poll/' + slug }}"
*ngFor="let poll of _pollsAvailables | async"
routerLink="{{ '/poll/' + poll.slug + '/consultation' }}"
routerLinkActive="is-active"
>
« {{ slug }} »
« {{ poll.slug }} »
</a>
</div>
</div>
......
import { Component, OnInit } from '@angular/core';
import { Observable } from 'rxjs';
import { ApiService } from '../../../services/api.service';
import { UserService } from '../../../services/user.service';
import { Poll } from '../../../models/poll.model';
import { MockingService } from '../../../services/mocking.service';
@Component({
selector: 'app-navigation',
......@@ -9,15 +10,9 @@ import { UserService } from '../../../services/user.service';
styleUrls: ['./navigation.component.scss'],
})
export class NavigationComponent implements OnInit {
public slugsAvailables: string[] = [];
public _pollsAvailables: Observable<Poll[]> = this.mockingService.pollsAvailables;
constructor(private apiService: ApiService, private userService: UserService) {}
constructor(private mockingService: MockingService) {}
public ngOnInit(): void {
this.getSlugs();
}
public async getSlugs(): Promise<void> {
this.slugsAvailables = await this.apiService.getAllPollsSlugs();
}
public ngOnInit(): void {}
}
......@@ -10,17 +10,9 @@ import { HomeComponent } from './components/home/home.component';
import { LogoComponent } from './components/logo/logo.component';
import { NavigationComponent } from './components/sibebar/navigation/navigation.component';
import { throwIfAlreadyLoaded } from './guards/module-import.guard';
import { DevNavbarComponent } from './components/dev-navbar/dev-navbar.component';
@NgModule({
declarations: [
FooterComponent,
HeaderComponent,
HomeComponent,
LogoComponent,
NavigationComponent,
DevNavbarComponent,
],
declarations: [FooterComponent, HeaderComponent, HomeComponent, LogoComponent, NavigationComponent],
imports: [CommonModule, FormsModule, RouterModule, TranslateModule],
exports: [HeaderComponent, FooterComponent, NavigationComponent, LogoComponent],
})
......
export enum MessageSeverity {
SUCCESS = 'success',
INFO = 'info',
WARN = 'warn',
ERROR = 'error',
}
export enum Theme {
LIGHT = 'LIGHT',
DARK = 'DARK',
RED = 'RED',
CONTRAST = 'CONTRAST',
}
export enum WorkflowStep {
DESCRIPTION = 'DESCRIPTION',
CHOICES = 'CHOICES',
CONFIGURATION = 'CONFIGURATION',
}
......@@ -64,11 +64,11 @@ export class ApiService {
//////////
// READ //
//////////
public async getAllPollsSlugs(): Promise<string[]> {
public async getAllAvailablePolls(): Promise<Poll[]> {
// TODO: used for facilities in DEV, should be removed in production
try {
const response: AxiosResponse<Poll[]> = await this.axiosInstance.get<Poll[]>(`${this.pollsEndpoint}`);
return response?.data.map((poll: Poll) => poll.slug);
return response?.data;
} catch (error) {
this.handleError(error);
}
......@@ -79,7 +79,7 @@ export class ApiService {
try {
// TODO: this interceptor should be avoided if backends returns the good object
const adapterInterceptor: number = this.axiosInstance.interceptors.response.use(
(response): AxiosResponse => {
(response: AxiosResponse): AxiosResponse => {
if (response.data['poll']) {
// response from cipherbliss backend, actually used by oldstuffModule
response.data = response.data['poll'];
......
import { Injectable } from '@angular/core';
import { BehaviorSubject, Observable } from 'rxjs';
import { UserRole } from '../enums/user-role.enum';
import { Choice } from '../models/choice.model';
import { Poll } from '../models/poll.model';
import { User } from '../models/user.model';
import { PollService } from './poll.service';
import { ApiService } from './api.service';
import { UserService } from './user.service';
import { UuidService } from './uuid.service';
......@@ -11,31 +13,43 @@ import { UuidService } from './uuid.service';
providedIn: 'root',
})
export class MockingService {
private user: User;
public pollsDatabase: Poll[] = [];
private _pollsAvailables: BehaviorSubject<Poll[]> = new BehaviorSubject<Poll[]>([]);
public readonly pollsAvailables: Observable<Poll[]> = this._pollsAvailables.asObservable();
private poll1: Poll = new Poll(this.user, undefined, 'Quand le picnic ?', 'Pour faire la teuf');
private poll2: Poll = new Poll(this.user, undefined, 'On fait quoi à la soirée ?', 'Balancez vos idées');
constructor(private apiService: ApiService, private userService: UserService, private uuidService: UuidService) {}
constructor(private userService: UserService, private pollService: PollService, private uuidService: UuidService) {
this.poll1.slug = this.uuidService.getUUID();
this.poll2.slug = this.uuidService.getUUID();
this.poll1.choices = [new Choice('mardi prochain'), new Choice('mercredi')];
this.poll2.choices = [new Choice('jeux'), new Choice('danser'), new Choice('discuter en picolant')];
public async init(): Promise<void> {
const pollsAvailable = await this.apiService.getAllAvailablePolls();
this._pollsAvailables.next(pollsAvailable);
this.pollsDatabase = [this.poll1, this.poll2];
if (this._pollsAvailables.getValue() && this._pollsAvailables.getValue().length > 0) {
// arbitrary choose first owner available
const currentUser = this._pollsAvailables.getValue()[0].owner;
currentUser.polls = [this._pollsAvailables.getValue()[0]];
this.userService.updateUser(currentUser);
} else {
this.loadMock();
}
}
public loadUser(user: User): void {
this.user = user;
this.user.polls = this.pollsDatabase;
console.info('MOCKING user', { user: this.user });
this.userService.updateUser(this.user);
}
public loadMock(): void {
const owner = new User('TOTO', 'toto@gafam.com', [], UserRole.REGISTERED);
const poll1: Poll = new Poll(owner, this.uuidService.getUUID(), 'Quand le picnic ?', 'Pour faire la teuf');
const poll2: Poll = new Poll(
owner,
this.uuidService.getUUID(),
'On fait quoi à la soirée ?',
'Balancez vos idées'
);
poll1.choices = [new Choice('mardi prochain'), new Choice('mercredi')];
poll2.choices = [new Choice('jeux'), new Choice('danser'), new Choice('discuter en picolant')];
this._pollsAvailables.next([poll1, poll2]);
owner.polls = [poll1, poll2];
this.userService.updateUser(owner);
public loadPoll(slug: string): void {
this.poll1.slug = slug;
console.info('MOCKING poll', { poll: this.poll1 });
this.pollService.updateCurrentPoll(this.poll1);
console.info('MOCKING user', { user: owner });
}
}
......@@ -3,12 +3,13 @@ import { ActivatedRouteSnapshot, Resolve, Router, RouterStateSnapshot } from '@a
import { BehaviorSubject, Observable } from 'rxjs';
import { Answer } from '../enums/answer.enum';
import { MessageSeverity } from '../enums/message-severity.enum';
import { Choice } from '../models/choice.model';
import { Poll } from '../models/poll.model';
import { User } from '../models/user.model';
import { ApiService } from './api.service';
import { ToastService } from './toast.service';
import { UserService } from './user.service';
import { UuidService } from './uuid.service';
@Injectable({
providedIn: 'root',
......@@ -17,13 +18,22 @@ export class PollService implements Resolve<Poll> {
private _poll: BehaviorSubject<Poll | undefined> = new BehaviorSubject<Poll | undefined>(undefined);
public readonly poll: Observable<Poll | undefined> = this._poll.asObservable();
constructor(private router: Router, private apiService: ApiService, private toastService: ToastService) {}
constructor(
private router: Router,
private apiService: ApiService,
private userService: UserService,
private uuidService: UuidService,
private toastService: ToastService
) {}
public async resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise<Poll> {
const wantedSlug: string = state.url.split('/poll/')[1];
const segments: string[] = state.url.split('/');
const wantedSlug: string = segments.includes('poll') ? segments[segments.indexOf('poll') + 1] : '';
if (!wantedSlug && state.url.includes('administration')) {
// creation of new poll
return;
const poll = new Poll(this.userService.getCurrentUser(), this.uuidService.getUUID(), '');
this._poll.next(poll);
this.router.navigate(['poll/' + poll.slug + '/administration']);
}
if (!this._poll.getValue() || !this._poll.getValue().slug || this._poll.getValue().slug !== wantedSlug) {
await this.loadPollBySlug(wantedSlug);
......@@ -38,7 +48,7 @@ export class PollService implements Resolve<Poll> {
public async loadPollBySlug(slug: string): Promise<void> {
const poll: Poll | undefined = await this.apiService.getPollBySlug(slug);
console.log({ fetchedResponse: poll });
console.log({ loadPollBySlugResponse: poll });
this.updateCurrentPoll(poll);
}
......@@ -61,15 +71,12 @@ export class PollService implements Resolve<Poll> {
currentPoll.choices.find((c) => c.label === choice.label)?.updateParticipation(user, response);
this.updateCurrentPoll(currentPoll);
this.apiService.createParticipation(currentPoll.slug, choice.label, user.pseudo, response);
this.toastService.display(MessageSeverity.SUCCESS, 'Votre participation au sondage a été enregistrée.');
this.toastService.display('Votre participation au sondage a été enregistrée.');
}
public async deleteAllAnswers(): Promise<void> {
await this.apiService.deletePollAnswers(this._poll.getValue().slug);
this.toastService.display(
MessageSeverity.SUCCESS,
'Les participations des votants à ce sondage ont été supprimées.'
);
this.toastService.display('Les participations des votants à ce sondage ont été supprimées.');
}
public async addComment(comment: string): Promise<void> {
......
......@@ -20,8 +20,12 @@ export class UserService {
this._user.next(user);
}
public getCurrentUser(): User {
return this._user.getValue();
}
public isCurrentUserIdentifiable(): boolean {
return this._user.getValue().pseudo ? true : false;
return this._user.getValue()?.pseudo ? true : false;
}
public async getUserPolls(): Promise<void> {
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment