mirror of
https://framagit.org/framasoft/framadate/funky-framadate-front.git
synced 2023-08-25 13:53:14 +02:00
add user settings button & modal
This commit is contained in:
parent
4f0a29e806
commit
355fed53f3
@ -28,6 +28,7 @@
|
||||
"node_modules/primeicons/primeicons.css",
|
||||
"node_modules/primeng/resources/themes/nova-light/theme.css",
|
||||
"node_modules/primeng/resources/primeng.min.css",
|
||||
"node_modules/primeflex/primeflex.css",
|
||||
"src/styles.scss"
|
||||
],
|
||||
"scripts": [
|
||||
|
@ -43,6 +43,7 @@
|
||||
"ngx-clipboard": "^13.0.0",
|
||||
"ngx-markdown": "^9.0.0",
|
||||
"ngx-webstorage": "^5.0.0",
|
||||
"primeflex": "^1.0.0",
|
||||
"primeicons": "^2.0.0",
|
||||
"primeng": "^9.0.6",
|
||||
"quill": "^1.3.7",
|
||||
@ -67,12 +68,12 @@
|
||||
"@types/uuid": "^7.0.2",
|
||||
"@typescript-eslint/eslint-plugin": "^2.27.0",
|
||||
"@typescript-eslint/parser": "^2.27.0",
|
||||
"babel-jest": "^25.4.0",
|
||||
"babel-jest": "^26.0.0",
|
||||
"eslint": "^6.8.0",
|
||||
"eslint-config-prettier": "^6.11.0",
|
||||
"eslint-plugin-prettier": "^3.1.3",
|
||||
"husky": "^4.2.5",
|
||||
"jest": "^25.5.1",
|
||||
"jest": "^26.0.0",
|
||||
"jest-environment-jsdom-sixteen": "^1.0.3",
|
||||
"jest-preset-angular": "^8.1.3",
|
||||
"lint-staged": "^10.1.7",
|
||||
|
@ -2,12 +2,10 @@ import { NgModule } from '@angular/core';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
|
||||
import { HomeComponent } from './core/components/home/home.component';
|
||||
import { LoginComponent } from './core/components/login/login.component';
|
||||
import { PageNotFoundComponent } from './shared/components/page-not-found/page-not-found.component';
|
||||
|
||||
const routes: Routes = [
|
||||
{ path: '', component: HomeComponent },
|
||||
{ path: 'login', component: LoginComponent },
|
||||
{
|
||||
path: 'administration',
|
||||
loadChildren: () =>
|
||||
|
@ -5,6 +5,8 @@ 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';
|
||||
|
||||
@ -22,15 +24,18 @@ export class AppComponent implements OnInit, OnDestroy {
|
||||
constructor(
|
||||
private titleService: Title,
|
||||
private themeService: ThemeService,
|
||||
private languageService: LanguageService,
|
||||
private mockingService: MockingService
|
||||
) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
if (!environment.production) {
|
||||
this.appTitle += ' [DEV]';
|
||||
this.mockingService.loadUser(UserRole.REGISTERED);
|
||||
// TODO: to be removed
|
||||
this.mockingService.loadUser(new User('TOTO', 'toto@gafam.com', UserRole.REGISTERED, false));
|
||||
}
|
||||
this.titleService.setTitle(this.appTitle);
|
||||
this.languageService.configureAndInitTranslations();
|
||||
this.themeSubscription = this.themeService.theme.subscribe((theme: Theme) => {
|
||||
switch (theme) {
|
||||
case Theme.DARK:
|
||||
|
@ -40,8 +40,12 @@
|
||||
<a class="navbar-item" routerLink="administration" routerLinkActive="is-active">
|
||||
Administration
|
||||
</a>
|
||||
<a class="navbar-item" routerLink="participation" routerLinkActive="is-active">
|
||||
Participation
|
||||
<a
|
||||
class="navbar-item"
|
||||
routerLink="participation/poll/citron_ou_orange"
|
||||
routerLinkActive="is-active"
|
||||
>
|
||||
Participation à citron_ou_orange
|
||||
</a>
|
||||
<a class="navbar-item" routerLink="oldstuff" routerLinkActive="is-active">
|
||||
Old stuff
|
||||
@ -50,22 +54,14 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="navbar-item">
|
||||
<app-theme-selector></app-theme-selector>
|
||||
</div>
|
||||
<div class="navbar-item">
|
||||
<app-language-selector></app-language-selector>
|
||||
</div>
|
||||
|
||||
<div class="navbar-end">
|
||||
<div class="navbar-item">
|
||||
<div class="buttons">
|
||||
<a class="button is-primary">
|
||||
<strong>Sign up</strong>
|
||||
</a>
|
||||
<a class="button is-light">
|
||||
Log in
|
||||
</a>
|
||||
<div class="navbar-item" #container>
|
||||
<div class="buttons has-addons is-centered clickable" (click)="openDialog()">
|
||||
<button class="button is-static"><i class="fa fa-user-circle"></i></button>
|
||||
<button class="button is-static" *ngIf="_user | async as user">
|
||||
{{ user.pseudo || 'anonyme' }}
|
||||
</button>
|
||||
<button class="button is-static"><i class="fa fa-cogs"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,4 +1,9 @@
|
||||
import { Component, EventEmitter, Output, Input } from '@angular/core';
|
||||
import { Component, EventEmitter, Input, Output } from '@angular/core';
|
||||
import { Observable } from 'rxjs';
|
||||
|
||||
import { User } from '../../models/user.model';
|
||||
import { ModalService } from '../../services/modal.service';
|
||||
import { UserService } from '../../services/user.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-header',
|
||||
@ -9,6 +14,14 @@ export class HeaderComponent {
|
||||
@Input() isSidebarOpened: boolean;
|
||||
@Output() toggleSidebarEE = new EventEmitter<boolean>();
|
||||
|
||||
public _user: Observable<User> = this.userService.user;
|
||||
|
||||
constructor(private userService: UserService, private modalService: ModalService) {}
|
||||
|
||||
public openDialog(): void {
|
||||
this.modalService.openSettingsComponent();
|
||||
}
|
||||
|
||||
public toggleSidebarOpening(): void {
|
||||
this.isSidebarOpened = !this.isSidebarOpened;
|
||||
this.toggleSidebarEE.emit(this.isSidebarOpened);
|
||||
|
@ -1 +0,0 @@
|
||||
<p>login works!</p>
|
@ -1,12 +0,0 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-login',
|
||||
templateUrl: './login.component.html',
|
||||
styleUrls: ['./login.component.scss'],
|
||||
})
|
||||
export class LoginComponent implements OnInit {
|
||||
constructor() {}
|
||||
|
||||
ngOnInit(): void {}
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
<div class="control has-icons-left">
|
||||
<select class="select is-small" [(ngModel)]="currentLang">
|
||||
<ng-container *ngFor="let language of languagesAvailable">
|
||||
<option value="{{ language }}">{{ 'LANGUAGES.' + language | translate }}</option>
|
||||
</ng-container>
|
||||
</select>
|
||||
<div class="icon is-left">
|
||||
<i class="fa fa-globe"></i>
|
||||
</div>
|
||||
</div>
|
@ -1,38 +0,0 @@
|
||||
import { Component, DoCheck, OnInit } from '@angular/core';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
|
||||
import { Language } from '../../../enums/language.enum';
|
||||
import { StorageService } from '../../../services/storage.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-language-selector',
|
||||
templateUrl: './language-selector.component.html',
|
||||
styleUrls: ['./language-selector.component.scss'],
|
||||
})
|
||||
export class LanguageSelectorComponent implements OnInit, DoCheck {
|
||||
public currentLang: Language;
|
||||
public languagesAvailable: string[] = Object.values(Language);
|
||||
|
||||
constructor(private translate: TranslateService, private storageService: StorageService) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
const currentBrowserLanguage: Language = this.translate.getBrowserLang().toUpperCase() as Language;
|
||||
if (this.storageService.language && Object.keys(Language).includes(this.storageService.language)) {
|
||||
this.currentLang = this.storageService.language;
|
||||
} else if (Object.keys(Language).includes(currentBrowserLanguage)) {
|
||||
this.currentLang = currentBrowserLanguage;
|
||||
} else {
|
||||
this.currentLang = Language.EN;
|
||||
}
|
||||
this.updateLanguage();
|
||||
}
|
||||
|
||||
ngDoCheck(): void {
|
||||
this.updateLanguage();
|
||||
}
|
||||
|
||||
public updateLanguage(): void {
|
||||
this.translate.use(this.currentLang.toString().toUpperCase());
|
||||
this.storageService.language = this.currentLang;
|
||||
}
|
||||
}
|
@ -1,24 +0,0 @@
|
||||
<div class="buttons has-addons is-centered">
|
||||
<button class="button is-small is-static">Theme</button>
|
||||
<button
|
||||
class="button is-small"
|
||||
[ngClass]="{ 'is-active': (currentTheme | async) === themeEnum.LIGHT }"
|
||||
(click)="selectTheme('LIGHT')"
|
||||
>
|
||||
<i class="fa fa-sun-o"></i>
|
||||
</button>
|
||||
<button
|
||||
class="button is-small"
|
||||
[ngClass]="{ 'is-active': (currentTheme | async) === themeEnum.DARK }"
|
||||
(click)="selectTheme('DARK')"
|
||||
>
|
||||
<i class="fa fa-moon"></i>
|
||||
</button>
|
||||
<button
|
||||
class="button is-small"
|
||||
[ngClass]="{ 'is-active': (currentTheme | async) === themeEnum.RED }"
|
||||
(click)="selectTheme('RED')"
|
||||
>
|
||||
<i class="fa fa-adjust"></i>
|
||||
</button>
|
||||
</div>
|
@ -7,26 +7,14 @@ import { TranslateModule } from '@ngx-translate/core';
|
||||
import { FooterComponent } from './components/footer/footer.component';
|
||||
import { HeaderComponent } from './components/header/header.component';
|
||||
import { HomeComponent } from './components/home/home.component';
|
||||
import { LoginComponent } from './components/login/login.component';
|
||||
import { LogoComponent } from './components/logo/logo.component';
|
||||
import { LanguageSelectorComponent } from './components/selectors/language-selector/language-selector.component';
|
||||
import { ThemeSelectorComponent } from './components/selectors/theme-selector/theme-selector.component';
|
||||
import { NavigationComponent } from './components/sibebar/navigation/navigation.component';
|
||||
import { throwIfAlreadyLoaded } from './guards/module-import.guard';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
FooterComponent,
|
||||
HeaderComponent,
|
||||
HomeComponent,
|
||||
LanguageSelectorComponent,
|
||||
LoginComponent,
|
||||
LogoComponent,
|
||||
NavigationComponent,
|
||||
ThemeSelectorComponent,
|
||||
],
|
||||
declarations: [FooterComponent, HeaderComponent, HomeComponent, LogoComponent, NavigationComponent],
|
||||
imports: [CommonModule, FormsModule, RouterModule, TranslateModule],
|
||||
exports: [HeaderComponent, FooterComponent, NavigationComponent, LoginComponent, LogoComponent],
|
||||
exports: [HeaderComponent, FooterComponent, NavigationComponent, LogoComponent],
|
||||
})
|
||||
export class CoreModule {
|
||||
constructor(@Optional() @SkipSelf() parentModule: CoreModule) {
|
||||
|
@ -1,4 +0,0 @@
|
||||
export enum AnswerGranularity {
|
||||
BASIC = 'BASIC',
|
||||
COMPLEX = 'COMPLEX',
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
export enum AnswerType {
|
||||
export enum Response {
|
||||
YES = 'YES',
|
||||
NO = 'NO',
|
||||
MAYBE = 'MAYBE',
|
@ -1,5 +1,5 @@
|
||||
export enum UserRole {
|
||||
ANONYMOUS = 'ANONYMOUS',
|
||||
REGISTERED = 'ADMIN',
|
||||
REGISTERED = 'REGISTERED',
|
||||
ADMIN = 'ADMIN',
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
export enum WorkflowStep {
|
||||
DESCRIPTION = 'DESCRIPTION',
|
||||
OPTIONS = 'OPTIONS',
|
||||
CHOICES = 'CHOICES',
|
||||
CONFIGURATION = 'CONFIGURATION',
|
||||
}
|
||||
|
@ -1,5 +0,0 @@
|
||||
export interface DateOption {
|
||||
timeList: any;
|
||||
literal: string;
|
||||
date_object?: object;
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
import { AnswerType } from '../enums/answer-type.enum';
|
||||
import { PollOption } from './poll-options.model';
|
||||
import { Response } from '../enums/response.enum';
|
||||
import { Choice } from './choice.model';
|
||||
|
||||
export class Answer {
|
||||
constructor(public pollOption: PollOption, public type: AnswerType, public userPseudo: string) {}
|
||||
constructor(public author: string, public choice: Choice, public response: Response) {}
|
||||
}
|
||||
|
5
src/app/core/models/choice.model.ts
Normal file
5
src/app/core/models/choice.model.ts
Normal file
@ -0,0 +1,5 @@
|
||||
import { Response } from '../enums/response.enum';
|
||||
|
||||
export class Choice {
|
||||
constructor(public label: string, public responses: Response[] = [Response.YES, Response.NO]) {}
|
||||
}
|
3
src/app/core/models/comment.model.ts
Normal file
3
src/app/core/models/comment.model.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export class Comment {
|
||||
constructor(public author: string, public content: string) {}
|
||||
}
|
26
src/app/core/models/configuration.model.ts
Normal file
26
src/app/core/models/configuration.model.ts
Normal file
@ -0,0 +1,26 @@
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
import { environment } from '../../../environments/environment';
|
||||
import { DateUtilsService } from '../utils/date-utils.service';
|
||||
|
||||
export class Configuration {
|
||||
constructor(
|
||||
public isAboutDate: boolean = false,
|
||||
public slug: string = uuidv4(),
|
||||
public isProtectedByPassword: boolean = false,
|
||||
public isMaybeAnswerAvailable: boolean = false,
|
||||
public creationDate: Date = new Date(Date.now()),
|
||||
public expirationDate: Date = DateUtilsService.addDaysToDate(
|
||||
environment.poll.defaultConfig.expiracyInDays,
|
||||
new Date(Date.now())
|
||||
)
|
||||
) {}
|
||||
|
||||
public getAdministrationUrl(): string {
|
||||
return `${environment.api.baseHref}/administration/${environment.api.endpoints.polls}/${this.slug}`;
|
||||
}
|
||||
|
||||
public getParticipationUrl(): string {
|
||||
return `${environment.api.baseHref}/participation/${environment.api.endpoints.polls}/${this.slug}`;
|
||||
}
|
||||
}
|
@ -1,13 +0,0 @@
|
||||
import { AnswerGranularity } from '../enums/answer-granularity.enum';
|
||||
|
||||
export class PollConfig {
|
||||
constructor(
|
||||
public allowSeveralHours = true,
|
||||
public isVisibleToAnyoneWithTheLink: boolean = true,
|
||||
public answerType: AnswerGranularity = AnswerGranularity.BASIC,
|
||||
public creationDate: Date = new Date(),
|
||||
public expirationDate?: Date,
|
||||
public canVotersModifyTheirAnswers = true,
|
||||
public isProtectedByPassword: boolean = false
|
||||
) {}
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
import * as moment from 'moment';
|
||||
|
||||
export class PollOption {
|
||||
constructor(public label: string, public url?: string, public subOptions?: PollOption[]) {}
|
||||
|
||||
public isDatePoll(): boolean {
|
||||
return moment(this.label).isValid();
|
||||
}
|
||||
}
|
@ -1,23 +1,17 @@
|
||||
import { Answer } from './answer.model';
|
||||
import { PollConfig } from './poll-config.model';
|
||||
import { PollOption } from './poll-options.model';
|
||||
import { Choice } from './choice.model';
|
||||
import { Comment } from './comment.model';
|
||||
import { Configuration } from './configuration.model';
|
||||
import { Question } from './question.model';
|
||||
import { User } from './user.model';
|
||||
import { environment } from 'src/environments/environment';
|
||||
|
||||
export class Poll {
|
||||
constructor(
|
||||
public isDateType: boolean,
|
||||
public title: string,
|
||||
public description: string,
|
||||
public slug: string,
|
||||
public id: string,
|
||||
public owner?: User,
|
||||
public config?: PollConfig,
|
||||
public options: PollOption[] = [],
|
||||
public answers: Answer[] = []
|
||||
public owner: User,
|
||||
public question: Question,
|
||||
public choices: Choice[],
|
||||
public configuration: Configuration = new Configuration(),
|
||||
public answers: Answer[] = [],
|
||||
public comments: Comment[] = []
|
||||
) {}
|
||||
|
||||
public getUrl(): string {
|
||||
return `${environment.api.baseHref}/${this.slug}`;
|
||||
}
|
||||
}
|
||||
|
3
src/app/core/models/question.model.ts
Normal file
3
src/app/core/models/question.model.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export class Question {
|
||||
constructor(public label: string, public description: string) {}
|
||||
}
|
@ -1,12 +1,12 @@
|
||||
import { Poll } from './poll.model';
|
||||
import { UserRole } from '../enums/user-role.enum';
|
||||
import { Poll } from './poll.model';
|
||||
|
||||
export class User {
|
||||
constructor(
|
||||
public pseudo: string,
|
||||
public email: string,
|
||||
public role: UserRole = UserRole.ANONYMOUS,
|
||||
public isOwner: boolean = false,
|
||||
public pseudo?: string,
|
||||
public email?: string,
|
||||
public polls?: Poll[]
|
||||
) {}
|
||||
}
|
||||
|
@ -14,7 +14,7 @@ export class ApiService {
|
||||
private readonly commentsEndpoint = environment.api.endpoints.polls.comments.name;
|
||||
private readonly votesEndpoint = environment.api.endpoints.polls.votes.name;
|
||||
private readonly slugsEndpoint = environment.api.endpoints.polls.slugs.name;
|
||||
private readonly votesStacksEndpoint = environment.api.endpoints.voteStack.name;
|
||||
private readonly votesStacksEndpoint = environment.api.endpoints.polls.votesStacks.name;
|
||||
private readonly usersEndpoint = environment.api.endpoints.users.name;
|
||||
private readonly usersPollsEndpoint = environment.api.endpoints.users.polls.name;
|
||||
private readonly usersPollsSendEmailEndpoint = environment.api.endpoints.users.polls.sendEmail.name;
|
||||
@ -28,7 +28,7 @@ export class ApiService {
|
||||
////////////
|
||||
public async savePoll(poll: Poll): Promise<void> {
|
||||
try {
|
||||
await this.axiosInstance.post(`${this.pollsEndpoint}`, { params: { config: poll.config } });
|
||||
await this.axiosInstance.post(`${this.pollsEndpoint}`, poll);
|
||||
} catch (error) {
|
||||
this.handleError(error);
|
||||
}
|
||||
@ -37,7 +37,7 @@ export class ApiService {
|
||||
public async saveVote(poll: Poll): Promise<void> {
|
||||
try {
|
||||
// TODO: add the votestack in the params
|
||||
await this.axiosInstance.post(`${this.pollsEndpoint}/${poll.id}${this.votesEndpoint}`, {
|
||||
await this.axiosInstance.post(`${this.pollsEndpoint}/${poll.configuration.slug}${this.votesEndpoint}`, {
|
||||
params: { voteStack: {} },
|
||||
});
|
||||
} catch (error) {
|
||||
@ -48,9 +48,10 @@ export class ApiService {
|
||||
public async saveComment(poll: Poll, comment: string): Promise<void> {
|
||||
try {
|
||||
// TODO: add the comment in the params
|
||||
await this.axiosInstance.post(`${this.pollsEndpoint}/${poll.id}${this.commentsEndpoint}`, {
|
||||
params: { comment },
|
||||
});
|
||||
await this.axiosInstance.post(
|
||||
`${this.pollsEndpoint}/${poll.configuration.slug}${this.commentsEndpoint}`,
|
||||
comment
|
||||
);
|
||||
} catch (error) {
|
||||
this.handleError(error);
|
||||
}
|
||||
@ -104,9 +105,17 @@ export class ApiService {
|
||||
public async getPollByIdentifier(identifier: string): Promise<Poll | undefined> {
|
||||
// TODO: identifier should be decided according to backend : Id || Slug ?
|
||||
try {
|
||||
// TODO: this interceptor should not be existing, backend should return the good object poll
|
||||
const adapterInterceptor: number = this.axiosInstance.interceptors.response.use(
|
||||
(response): AxiosResponse => {
|
||||
response.data = response.data['poll'];
|
||||
return response;
|
||||
}
|
||||
);
|
||||
const response: AxiosResponse<Poll> = await this.axiosInstance.get<Poll>(
|
||||
`${this.pollsEndpoint}/${identifier}`
|
||||
);
|
||||
axios.interceptors.request.eject(adapterInterceptor);
|
||||
return response?.data;
|
||||
} catch (error) {
|
||||
if (error.response?.status === 404) {
|
||||
@ -123,7 +132,7 @@ export class ApiService {
|
||||
public async updatePoll(poll: Poll): Promise<void> {
|
||||
try {
|
||||
// TODO: implement the params when entities are finalized.
|
||||
await this.axiosInstance.put(`${this.pollsEndpoint}/${poll.id}`, {
|
||||
await this.axiosInstance.put(`${this.pollsEndpoint}/${poll.configuration.slug}`, {
|
||||
params: { voteStack: {}, token: '' },
|
||||
});
|
||||
} catch (error) {
|
||||
@ -145,10 +154,9 @@ export class ApiService {
|
||||
////////////
|
||||
// DELETE //
|
||||
////////////
|
||||
|
||||
public async deletePoll(poll: Poll): Promise<void> {
|
||||
try {
|
||||
await this.axiosInstance.delete(`${this.pollsEndpoint}${poll.id}`, {});
|
||||
await this.axiosInstance.delete(`${this.pollsEndpoint}${poll.configuration.slug}`, {});
|
||||
} catch (error) {
|
||||
this.handleError(error);
|
||||
}
|
||||
@ -157,7 +165,7 @@ export class ApiService {
|
||||
public async deletePollVotes(poll: Poll): Promise<void> {
|
||||
try {
|
||||
// TODO: update endpoint in Backend
|
||||
await this.axiosInstance.delete(`${this.pollsEndpoint}${poll.id}${this.votesEndpoint}`);
|
||||
await this.axiosInstance.delete(`${this.pollsEndpoint}${poll.configuration.slug}${this.votesEndpoint}`);
|
||||
} catch (error) {
|
||||
this.handleError(error);
|
||||
}
|
||||
@ -166,7 +174,7 @@ export class ApiService {
|
||||
public async deletePollComments(poll: Poll): Promise<void> {
|
||||
try {
|
||||
// TODO: modify endpoint in Backend
|
||||
await this.axiosInstance.delete(`${this.pollsEndpoint}${poll.id}${this.commentsEndpoint}`);
|
||||
await this.axiosInstance.delete(`${this.pollsEndpoint}${poll.configuration.slug}${this.commentsEndpoint}`);
|
||||
} catch (error) {
|
||||
this.handleError(error);
|
||||
}
|
||||
@ -192,5 +200,6 @@ export class ApiService {
|
||||
console.log('Error', error.message);
|
||||
}
|
||||
console.log(error.config);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
16
src/app/core/services/language.service.spec.ts
Normal file
16
src/app/core/services/language.service.spec.ts
Normal file
@ -0,0 +1,16 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
|
||||
import { LanguageService } from './language.service';
|
||||
|
||||
describe('LanguageService', () => {
|
||||
let service: LanguageService;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({});
|
||||
service = TestBed.inject(LanguageService);
|
||||
});
|
||||
|
||||
it('should be created', () => {
|
||||
expect(service).toBeTruthy();
|
||||
});
|
||||
});
|
66
src/app/core/services/language.service.ts
Normal file
66
src/app/core/services/language.service.ts
Normal file
@ -0,0 +1,66 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { TranslateService, LangChangeEvent } from '@ngx-translate/core';
|
||||
|
||||
import { Language } from '../enums/language.enum';
|
||||
import { StorageService } from './storage.service';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class LanguageService {
|
||||
constructor(private translate: TranslateService, private storageService: StorageService) {}
|
||||
|
||||
public getLangage(): Language {
|
||||
return this.translate.currentLang as Language;
|
||||
}
|
||||
|
||||
public setLanguage(language: Language): void {
|
||||
this.translate.use(language.toString().toUpperCase());
|
||||
}
|
||||
|
||||
public getAvailableLanguages(): string[] {
|
||||
return this.translate.getLangs();
|
||||
}
|
||||
|
||||
public configureAndInitTranslations(): void {
|
||||
// always save in storage the currentLang used
|
||||
this.translate.onLangChange.subscribe((event: LangChangeEvent) => {
|
||||
this.storageService.language = event.lang as Language;
|
||||
});
|
||||
|
||||
// set all languages available
|
||||
this.translate.addLangs(Object.keys(Language));
|
||||
|
||||
// set language
|
||||
this.setLanguageOnInit();
|
||||
}
|
||||
|
||||
private setLanguageOnInit(): void {
|
||||
// set language from storage
|
||||
if (!this.translate.currentLang) {
|
||||
this.setLanguageFromStorage();
|
||||
}
|
||||
|
||||
// or set language from browser
|
||||
if (!this.translate.currentLang) {
|
||||
this.setLanguageFromBrowser();
|
||||
}
|
||||
|
||||
// set default language
|
||||
if (!this.translate.currentLang) {
|
||||
this.setLanguage(Language.EN);
|
||||
}
|
||||
}
|
||||
|
||||
private setLanguageFromStorage(): void {
|
||||
if (this.storageService.language && this.translate.getLangs().includes(this.storageService.language)) {
|
||||
this.setLanguage(this.storageService.language);
|
||||
}
|
||||
}
|
||||
private setLanguageFromBrowser(): void {
|
||||
const currentBrowserLanguage: Language = this.translate.getBrowserLang().toUpperCase() as Language;
|
||||
if (this.translate.getLangs().includes(currentBrowserLanguage)) {
|
||||
this.setLanguage(currentBrowserLanguage);
|
||||
}
|
||||
}
|
||||
}
|
16
src/app/core/services/loader.service.spec.ts
Normal file
16
src/app/core/services/loader.service.spec.ts
Normal file
@ -0,0 +1,16 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
|
||||
import { LoaderService } from './loader.service';
|
||||
|
||||
describe('LoaderService', () => {
|
||||
let service: LoaderService;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({});
|
||||
service = TestBed.inject(LoaderService);
|
||||
});
|
||||
|
||||
it('should be created', () => {
|
||||
expect(service).toBeTruthy();
|
||||
});
|
||||
});
|
14
src/app/core/services/loader.service.ts
Normal file
14
src/app/core/services/loader.service.ts
Normal file
@ -0,0 +1,14 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { BehaviorSubject, Observable } from 'rxjs';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class LoaderService {
|
||||
private _loadingStatus: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(true);
|
||||
public readonly isLoading: Observable<boolean> = this._loadingStatus.asObservable();
|
||||
|
||||
public setStatus(status: boolean): void {
|
||||
this._loadingStatus.next(status);
|
||||
}
|
||||
}
|
@ -1,7 +1,9 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
|
||||
import { UserRole } from '../enums/user-role.enum';
|
||||
import { Choice } from '../models/choice.model';
|
||||
import { Poll } from '../models/poll.model';
|
||||
import { Question } from '../models/question.model';
|
||||
import { User } from '../models/user.model';
|
||||
import { PollService } from './poll.service';
|
||||
import { UserService } from './user.service';
|
||||
@ -10,28 +12,33 @@ import { UserService } from './user.service';
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class MockingService {
|
||||
public pollsDatabase: Poll[];
|
||||
private user: User;
|
||||
public pollsDatabase: Poll[] = [];
|
||||
|
||||
private user: User = new User(UserRole.ANONYMOUS, false, 'toto', 'toto@gafam.com', []);
|
||||
private poll1: Poll = new Poll(false, 'mon super sondage', 'super description 1', 'super_slug_1', 'id1');
|
||||
private poll2: Poll = new Poll(false, 'mon autre sondage', 'super description 2', 'super_slug_2', 'id2');
|
||||
private poll1: Poll = new Poll(this.user, new Question('Quand le picnic ?', 'Pour faire la teuf'), [
|
||||
new Choice('mardi prochain'),
|
||||
new Choice('mercredi'),
|
||||
]);
|
||||
private poll2: Poll = new Poll(this.user, new Question('On fait quoi à la soirée ?', 'Balancez vos idées'), [
|
||||
new Choice('jeux'),
|
||||
new Choice('danser'),
|
||||
new Choice('discuter en picolant'),
|
||||
]);
|
||||
|
||||
constructor(private userService: UserService, private pollService: PollService) {
|
||||
this.pollsDatabase = [this.poll1, this.poll2];
|
||||
this.user.polls = this.pollsDatabase;
|
||||
}
|
||||
|
||||
public loadUser(role: UserRole): void {
|
||||
this.user.role = role;
|
||||
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 loadPoll(slug: string): void {
|
||||
const poll: Poll | undefined = this.pollsDatabase.find((poll: Poll) => poll.slug === slug);
|
||||
if (poll) {
|
||||
console.info('MOCKING poll', { poll });
|
||||
this.pollService.updateCurrentPoll(poll);
|
||||
}
|
||||
this.poll1.configuration.slug = slug;
|
||||
console.info('MOCKING poll', { poll: this.poll1 });
|
||||
this.pollService.updateCurrentPoll(this.poll1);
|
||||
}
|
||||
}
|
||||
|
16
src/app/core/services/modal.service.spec.ts
Normal file
16
src/app/core/services/modal.service.spec.ts
Normal file
@ -0,0 +1,16 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
|
||||
import { ModalService } from './modal.service';
|
||||
|
||||
describe('ModalService', () => {
|
||||
let service: ModalService;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({});
|
||||
service = TestBed.inject(ModalService);
|
||||
});
|
||||
|
||||
it('should be created', () => {
|
||||
expect(service).toBeTruthy();
|
||||
});
|
||||
});
|
15
src/app/core/services/modal.service.ts
Normal file
15
src/app/core/services/modal.service.ts
Normal file
@ -0,0 +1,15 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { DialogService } from 'primeng';
|
||||
|
||||
import { SettingsComponent } from '../../shared/components/settings/settings.component';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class ModalService {
|
||||
constructor(public dialogService: DialogService) {}
|
||||
|
||||
public openSettingsComponent(): void {
|
||||
this.dialogService.open(SettingsComponent, { header: 'Paramètres', dismissableMask: true });
|
||||
}
|
||||
}
|
@ -37,7 +37,10 @@ export class PollService {
|
||||
|
||||
// GET
|
||||
public async getPollByIdentifier(slug: string): Promise<void> {
|
||||
this.updateCurrentPoll(await this.apiService.getPollByIdentifier(slug));
|
||||
const poll: Poll | undefined = await this.apiService.getPollByIdentifier(slug);
|
||||
if (poll) {
|
||||
this.updateCurrentPoll(poll);
|
||||
}
|
||||
}
|
||||
|
||||
// DELETE
|
||||
|
@ -16,4 +16,7 @@ export class StorageService {
|
||||
|
||||
@LocalStorage()
|
||||
public userPollsIds: string[];
|
||||
|
||||
@LocalStorage()
|
||||
public pseudo: string;
|
||||
}
|
||||
|
16
src/app/core/services/url.service.spec.ts
Normal file
16
src/app/core/services/url.service.spec.ts
Normal file
@ -0,0 +1,16 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
|
||||
import { UrlService } from './url.service';
|
||||
|
||||
describe('UrlService', () => {
|
||||
let service: UrlService;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({});
|
||||
service = TestBed.inject(UrlService);
|
||||
});
|
||||
|
||||
it('should be created', () => {
|
||||
expect(service).toBeTruthy();
|
||||
});
|
||||
});
|
23
src/app/core/services/url.service.ts
Normal file
23
src/app/core/services/url.service.ts
Normal file
@ -0,0 +1,23 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { ActivatedRoute, UrlSegment } from '@angular/router';
|
||||
|
||||
import { LoaderService } from './loader.service';
|
||||
import { PollService } from './poll.service';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class UrlService {
|
||||
constructor(
|
||||
private route: ActivatedRoute,
|
||||
private pollService: PollService,
|
||||
private loaderService: LoaderService
|
||||
) {}
|
||||
|
||||
public async loadPollFromUrl(): Promise<void> {
|
||||
this.loaderService.setStatus(true);
|
||||
const wantedSlug: string = this.route.snapshot.firstChild.firstChild.url[1].path;
|
||||
await this.pollService.getPollByIdentifier(wantedSlug);
|
||||
this.loaderService.setStatus(false);
|
||||
}
|
||||
}
|
@ -1,6 +1,8 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { DialogService } from 'primeng';
|
||||
import { BehaviorSubject, Observable } from 'rxjs';
|
||||
|
||||
import { UserRole } from '../enums/user-role.enum';
|
||||
import { User } from '../models/user.model';
|
||||
import { ApiService } from './api.service';
|
||||
|
||||
@ -8,16 +10,21 @@ import { ApiService } from './api.service';
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class UserService {
|
||||
public anonymous: User = new User();
|
||||
public anonymous: User = new User('', '', UserRole.ANONYMOUS, false);
|
||||
|
||||
private _user: BehaviorSubject<User> = new BehaviorSubject<User>(this.anonymous);
|
||||
public readonly user: Observable<User> = this._user.asObservable();
|
||||
|
||||
constructor(private apiService: ApiService) {}
|
||||
constructor(private apiService: ApiService, public dialogService: DialogService) {}
|
||||
|
||||
public updateUser(user: User): void {
|
||||
this._user.next(user);
|
||||
}
|
||||
|
||||
public isCurrentUserIdentifiable(): boolean {
|
||||
return this._user.getValue().pseudo ? true : false;
|
||||
}
|
||||
|
||||
// GET
|
||||
public async getUserPolls(): Promise<void> {
|
||||
const currentUser: User = this._user.getValue();
|
||||
|
@ -7,7 +7,7 @@ import { WorkflowStep } from '../enums/workflow-step.enum';
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class WorkflowService {
|
||||
private steps = [WorkflowStep.DESCRIPTION, WorkflowStep.OPTIONS, WorkflowStep.CONFIGURATION];
|
||||
private steps = [WorkflowStep.DESCRIPTION, WorkflowStep.CHOICES, WorkflowStep.CONFIGURATION];
|
||||
|
||||
private _currentStep: BehaviorSubject<WorkflowStep> = new BehaviorSubject<WorkflowStep>(WorkflowStep[0]);
|
||||
public readonly currentStep: Observable<WorkflowStep> = this._currentStep.asObservable();
|
||||
|
@ -16,35 +16,4 @@ export class DateUtilsService {
|
||||
public static formatDate(date): string {
|
||||
return moment(date).format('yyyy-MM-dd');
|
||||
}
|
||||
|
||||
public static orderDates(): Date[] {
|
||||
// TODO: to implement
|
||||
const datesOrdered: Date[] = [];
|
||||
return datesOrdered;
|
||||
}
|
||||
|
||||
public static getDatesInRange(d1: Date, d2: Date, interval: number): Date[] {
|
||||
// TODO: refacto this
|
||||
d1 = new Date(d1);
|
||||
d2 = new Date(d2);
|
||||
const dates = [];
|
||||
while (+d1 < +d2) {
|
||||
dates.push({
|
||||
literal: this.formateDate(d1),
|
||||
date_object: d1,
|
||||
});
|
||||
d1.setDate(d1.getDate() + interval);
|
||||
}
|
||||
return [...dates];
|
||||
}
|
||||
|
||||
public static getDoubleDigits(str: string): string {
|
||||
// TODO: ça sert à quoi ça ?
|
||||
// Parce que ajouter 2 caractère à une string et ensuite slicer à partir du 2ème caractère, euh…
|
||||
return ('00' + str).slice(-2);
|
||||
}
|
||||
|
||||
private isInChronologicalOrder(date1: Date, date2: Date): boolean {
|
||||
return date1 < date2;
|
||||
}
|
||||
}
|
||||
|
@ -18,10 +18,10 @@ export class EditDescriptionComponent implements OnInit {
|
||||
|
||||
ngOnInit(): void {
|
||||
this.pollForm = this.fb.group({
|
||||
type: [this.poll ? this.poll.isDateType : false, [Validators.required]],
|
||||
title: [this.poll ? this.poll.title : '', [Validators.required]],
|
||||
description: [this.poll ? this.poll.description : ''],
|
||||
slug: [this.poll ? this.poll.slug : this.generateRandomSlug(), [Validators.required]],
|
||||
type: [this.poll ? this.poll.configuration.isAboutDate : false, [Validators.required]],
|
||||
title: [this.poll ? this.poll.question.label : '', [Validators.required]],
|
||||
description: [this.poll ? this.poll.question.description : ''],
|
||||
slug: [this.poll ? this.poll.configuration.slug : this.generateRandomSlug(), [Validators.required]],
|
||||
address: this.fb.group({
|
||||
street: [''],
|
||||
city: [''],
|
||||
|
@ -18,10 +18,10 @@ export class PollEditComponent implements OnInit {
|
||||
|
||||
ngOnInit(): void {
|
||||
this.pollForm = this.fb.group({
|
||||
type: [this.poll ? this.poll.isDateType : false, [Validators.required]],
|
||||
title: [this.poll ? this.poll.title : '', [Validators.required]],
|
||||
description: [this.poll ? this.poll.description : ''],
|
||||
slug: [this.poll ? this.poll.slug : this.generateRandomSlug(), [Validators.required]],
|
||||
type: [this.poll ? this.poll.configuration.isAboutDate : false, [Validators.required]],
|
||||
title: [this.poll ? this.poll.question.label : '', [Validators.required]],
|
||||
description: [this.poll ? this.poll.question.description : ''],
|
||||
slug: [this.poll ? this.poll.configuration.slug : this.generateRandomSlug(), [Validators.required]],
|
||||
address: this.fb.group({
|
||||
street: [''],
|
||||
city: [''],
|
||||
|
@ -11,10 +11,10 @@
|
||||
<thead></thead>
|
||||
<tbody>
|
||||
<tr *ngFor="let poll of (_user | async)?.polls">
|
||||
<th>{{ poll.title }}</th>
|
||||
<th>{{ poll.question.label }}</th>
|
||||
<td>
|
||||
<a routerLink="{{ '/administration/edit/description/' + poll.slug }}">
|
||||
{{ poll.getUrl() }}
|
||||
<a routerLink="{{ '/administration/edit/description/' + poll.configuration.slug }}">
|
||||
{{ poll.configuration.getAdministrationUrl() }}
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
@ -26,7 +26,7 @@
|
||||
<ng-container *ngIf="['ANONYMOUS'].includes((_user | async)?.role)">
|
||||
<div class="columns">
|
||||
<div class="column">
|
||||
<a class="button is-primary" role="button" routerLink="/login">
|
||||
<a class="button is-primary" role="button" routerLink="/">
|
||||
J’ai un compte, je me connecte
|
||||
</a>
|
||||
</div>
|
||||
|
@ -10,14 +10,13 @@ import { UserService } from '../../../core/services/user.service';
|
||||
styleUrls: ['./profile.component.scss'],
|
||||
})
|
||||
export class ProfileComponent implements OnInit {
|
||||
public _user: Observable<User>;
|
||||
public _user: Observable<User> = this.userService.user;
|
||||
public isModalOpened = false;
|
||||
|
||||
constructor(private userService: UserService) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
this._user = this.userService.user;
|
||||
}
|
||||
ngOnInit(): void {}
|
||||
|
||||
public toggleModal(): void {
|
||||
this.isModalOpened = !this.isModalOpened;
|
||||
}
|
||||
|
@ -22,7 +22,7 @@ export class StepperComponent implements OnInit {
|
||||
};
|
||||
public itemOptions: MenuItem = {
|
||||
id: '2',
|
||||
label: WorkflowStep.OPTIONS,
|
||||
label: WorkflowStep.CHOICES,
|
||||
title: 'Je renseigne les différentes options sur lesquelles les gens vont donner leur avis',
|
||||
routerLink: './options',
|
||||
command: () => {},
|
||||
|
@ -32,8 +32,8 @@ const routes: Routes = [
|
||||
{ path: 'step/resume', component: ResumeComponent },
|
||||
{ path: 'step/end', component: EndConfirmationComponent },
|
||||
{ path: 'graphic/:poll', component: PollGraphicComponent },
|
||||
{ path: 'vote/poll/id/:poll', component: PollDisplayComponent },
|
||||
{ path: 'vote/poll/slug/:pollSlug', component: PollDisplayComponent },
|
||||
{ path: 'vote/poll/id/:id', component: PollDisplayComponent },
|
||||
{ path: 'vote/poll/slug/:slug', component: PollDisplayComponent },
|
||||
{ path: 'votingchoice', component: VotingChoiceComponent },
|
||||
{ path: 'voting', component: VotingComponent },
|
||||
];
|
||||
|
@ -51,9 +51,9 @@
|
||||
<ul class="poll-list" *ngFor="let poll of config.myPolls; index as i; trackBy: trackFunction">
|
||||
<li>
|
||||
<a href="{{ poll.url }}">
|
||||
{{ poll.title }}
|
||||
{{ poll.question.label }}
|
||||
<sub>
|
||||
{{ poll.description }}
|
||||
{{ poll.question.description }}
|
||||
</sub>
|
||||
</a>
|
||||
</li>
|
||||
|
@ -3,7 +3,7 @@
|
||||
<ul>
|
||||
<li>
|
||||
<a href="title">
|
||||
{{ config.currentPoll.poll.title }}
|
||||
{{ config.currentPoll.poll.question.label }}
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
|
@ -1,8 +1,8 @@
|
||||
<h2>Résumé</h2>
|
||||
<div class="heading">
|
||||
<div class="col-xs-6">
|
||||
<h1 id="title">{{ config.currentPoll.poll.title }}</h1>
|
||||
<p>{{ config.currentPoll.poll.description }}</p>
|
||||
<h1 id="title">{{ config.currentPoll.poll.question.label }}</h1>
|
||||
<p>{{ config.currentPoll.poll.question.description }}</p>
|
||||
<span class="creationDate"> Créé le {{ config.currentPoll.poll.creationDate.date }} </span>
|
||||
<span class="expiracyDate"> Expire le {{ config.currentPoll.poll.expiracyDate.date }} </span>
|
||||
<div class="votants">
|
||||
|
@ -2,14 +2,12 @@ import { NgModule } from '@angular/core';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
|
||||
import { ParticipationComponent } from './participation.component';
|
||||
import { PollComponent } from './poll/poll.component';
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: '',
|
||||
component: ParticipationComponent,
|
||||
children: [{ path: 'poll/:slug', component: PollComponent }],
|
||||
},
|
||||
{ path: '', redirectTo: 'poll', pathMatch: 'full' },
|
||||
{ path: 'poll', component: ParticipationComponent },
|
||||
{ path: ':slug', redirectTo: 'poll/:slug', pathMatch: 'full' },
|
||||
{ path: 'poll/:slug', component: ParticipationComponent },
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
|
@ -1,7 +1,30 @@
|
||||
<h1>Participation</h1>
|
||||
<div class="p-grid">
|
||||
<div class="p-col has-text-centered">
|
||||
<h1>Participation</h1>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<a role="button" class="button" routerLink="../participation/poll/groSlug">
|
||||
Participer au sondage « Gros slug »
|
||||
</a>
|
||||
<div *ngIf="_isLoading | async" class="p-grid p-justify-center">
|
||||
<div class="p-col has-text-centered">
|
||||
<p-progressSpinner></p-progressSpinner>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<router-outlet></router-outlet>
|
||||
<ng-container *ngIf="!(_isLoading | async)">
|
||||
<ng-container *ngIf="!(_poll | async)">
|
||||
<app-page-not-found [message]="'PAGE_NOT_FOUND.POLL'"></app-page-not-found>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngIf="_poll | async">
|
||||
<div class="p-grid">
|
||||
<div class="p-col">
|
||||
<app-poll></app-poll>
|
||||
</div>
|
||||
</div>
|
||||
<div class="p-grid">
|
||||
<div class="p-col">
|
||||
<app-poll-comment></app-poll-comment>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
|
@ -1,4 +1,12 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { Observable } from 'rxjs';
|
||||
|
||||
import { Poll } from '../../core/models/poll.model';
|
||||
import { LoaderService } from '../../core/services/loader.service';
|
||||
import { ModalService } from '../../core/services/modal.service';
|
||||
import { PollService } from '../../core/services/poll.service';
|
||||
import { UrlService } from '../../core/services/url.service';
|
||||
import { UserService } from '../../core/services/user.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-participation',
|
||||
@ -6,7 +14,21 @@ import { Component, OnInit } from '@angular/core';
|
||||
styleUrls: ['./participation.component.scss'],
|
||||
})
|
||||
export class ParticipationComponent implements OnInit {
|
||||
constructor() {}
|
||||
public _isLoading: Observable<boolean> = this.loaderService.isLoading;
|
||||
public _poll: Observable<Poll> = this.pollService.poll;
|
||||
|
||||
ngOnInit(): void {}
|
||||
constructor(
|
||||
private urlService: UrlService,
|
||||
private loaderService: LoaderService,
|
||||
private pollService: PollService,
|
||||
private userService: UserService,
|
||||
private modalService: ModalService
|
||||
) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
if (!this.userService.isCurrentUserIdentifiable()) {
|
||||
this.modalService.openSettingsComponent();
|
||||
}
|
||||
this.urlService.loadPollFromUrl();
|
||||
}
|
||||
}
|
||||
|
@ -6,9 +6,10 @@ import { SharedModule } from '../../shared/shared.module';
|
||||
import { ParticipationRoutingModule } from './participation-routing.module';
|
||||
import { ParticipationComponent } from './participation.component';
|
||||
import { PollComponent } from './poll/poll.component';
|
||||
import { PollCommentComponent } from './poll-comment/poll-comment.component';
|
||||
|
||||
@NgModule({
|
||||
declarations: [ParticipationComponent, PollComponent],
|
||||
declarations: [ParticipationComponent, PollComponent, PollCommentComponent],
|
||||
imports: [CommonModule, ParticipationRoutingModule, SharedModule, TranslateModule.forChild({ extend: true })],
|
||||
exports: [],
|
||||
})
|
||||
|
@ -0,0 +1 @@
|
||||
<p>poll-comment works!</p>
|
@ -0,0 +1,24 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { PollCommentComponent } from './poll-comment.component';
|
||||
|
||||
describe('PollCommentComponent', () => {
|
||||
let component: PollCommentComponent;
|
||||
let fixture: ComponentFixture<PollCommentComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [PollCommentComponent],
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(PollCommentComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
@ -0,0 +1,12 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-poll-comment',
|
||||
templateUrl: './poll-comment.component.html',
|
||||
styleUrls: ['./poll-comment.component.scss'],
|
||||
})
|
||||
export class PollCommentComponent implements OnInit {
|
||||
constructor() {}
|
||||
|
||||
ngOnInit(): void {}
|
||||
}
|
@ -1,12 +1,6 @@
|
||||
<p-progressSpinner *ngIf="isLoading" class="has-text-centered"></p-progressSpinner>
|
||||
|
||||
<ng-container *ngIf="!isLoading">
|
||||
<ng-container *ngIf="poll | async">
|
||||
<!-- <app-poll-presentation></app-poll-presentation> -->
|
||||
<h1>Sondage : {{ poll.title | async }}</h1>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngIf="!(poll | async)">
|
||||
<app-page-not-found [message]="'PAGE_NOT_FOUND.POLL'"></app-page-not-found>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="_poll | async as poll">
|
||||
<h1>Titre:{{ poll.question.label }}</h1>
|
||||
<p>description: {{ poll.question.description }}</p>
|
||||
<p>isDateType:{{ poll.configuration.isAboutDate }}</p>
|
||||
<p>slug:{{ poll.configuration.slug }}</p>
|
||||
</ng-container>
|
||||
|
@ -1,5 +1,4 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
import { Observable } from 'rxjs';
|
||||
|
||||
import { Poll } from '../../../core/models/poll.model';
|
||||
@ -11,24 +10,9 @@ import { PollService } from '../../../core/services/poll.service';
|
||||
styleUrls: ['./poll.component.scss'],
|
||||
})
|
||||
export class PollComponent implements OnInit {
|
||||
public isLoading = false;
|
||||
public wantedSlug: string;
|
||||
public poll: Observable<Poll>;
|
||||
public slugFromUrl: string;
|
||||
private readonly urlMatcher: RegExp = new RegExp(/participation\/poll\/(\w+)/);
|
||||
public _poll: Observable<Poll> = this.pollService.poll;
|
||||
|
||||
constructor(private router: Router, private pollService: PollService) {}
|
||||
constructor(private pollService: PollService) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.wantedSlug = this.urlMatcher.exec(this.router.url)[1];
|
||||
this.poll = this.pollService.poll;
|
||||
|
||||
this.loadPollFromUrl();
|
||||
}
|
||||
|
||||
private async loadPollFromUrl(): Promise<void> {
|
||||
this.isLoading = true;
|
||||
await this.pollService.getPollByIdentifier(this.wantedSlug);
|
||||
this.isLoading = false;
|
||||
}
|
||||
ngOnInit(): void {}
|
||||
}
|
||||
|
@ -0,0 +1,14 @@
|
||||
<div class="p-grid p-align-center p-justify-between">
|
||||
<div class="p-col">
|
||||
<p>Langue</p>
|
||||
</div>
|
||||
<div class="p-col">
|
||||
<div class="buttons has-addons">
|
||||
<select class="select" [(ngModel)]="currentLang">
|
||||
<option *ngFor="let language of availableLanguages" value="{{ language }}">
|
||||
{{ 'LANGUAGES.' + language | translate }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -0,0 +1,27 @@
|
||||
import { Component, DoCheck, OnInit } from '@angular/core';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
|
||||
import { Language } from '../../../../core/enums/language.enum';
|
||||
import { StorageService } from '../../../../core/services/storage.service';
|
||||
import { LanguageService } from '../../../../core/services/language.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-language-selector',
|
||||
templateUrl: './language-selector.component.html',
|
||||
styleUrls: ['./language-selector.component.scss'],
|
||||
})
|
||||
export class LanguageSelectorComponent implements OnInit, DoCheck {
|
||||
public currentLang: Language;
|
||||
public availableLanguages: string[] = [];
|
||||
|
||||
constructor(private languageService: LanguageService) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.availableLanguages = this.languageService.getAvailableLanguages();
|
||||
this.currentLang = this.languageService.getLangage();
|
||||
}
|
||||
|
||||
ngDoCheck(): void {
|
||||
this.languageService.setLanguage(this.currentLang);
|
||||
}
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
<div class="p-grid p-align-center p-justify-between">
|
||||
<div class="p-col">
|
||||
<p>Theme</p>
|
||||
</div>
|
||||
<div class="p-col">
|
||||
<div class="buttons has-addons">
|
||||
<button
|
||||
class="button"
|
||||
[ngClass]="{ 'is-active': (currentTheme | async) === themeEnum.LIGHT }"
|
||||
(click)="selectTheme('LIGHT')"
|
||||
>
|
||||
<i class="fa fa-sun-o"></i>
|
||||
</button>
|
||||
<button
|
||||
class="button"
|
||||
[ngClass]="{ 'is-active': (currentTheme | async) === themeEnum.DARK }"
|
||||
(click)="selectTheme('DARK')"
|
||||
>
|
||||
<i class="fa fa-moon"></i>
|
||||
</button>
|
||||
<button
|
||||
class="button"
|
||||
[ngClass]="{ 'is-active': (currentTheme | async) === themeEnum.RED }"
|
||||
(click)="selectTheme('RED')"
|
||||
>
|
||||
<i class="fa fa-adjust"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -1,8 +1,8 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { Observable } from 'rxjs';
|
||||
|
||||
import { Theme } from '../../../enums/theme.enum';
|
||||
import { ThemeService } from '../../../services/theme.service';
|
||||
import { Theme } from '../../../../core/enums/theme.enum';
|
||||
import { ThemeService } from '../../../../core/services/theme.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-theme-selector',
|
||||
@ -11,13 +11,11 @@ import { ThemeService } from '../../../services/theme.service';
|
||||
})
|
||||
export class ThemeSelectorComponent implements OnInit {
|
||||
public themeEnum = Theme;
|
||||
public currentTheme: Observable<Theme>;
|
||||
public currentTheme: Observable<Theme> = this.themeService.theme;
|
||||
|
||||
constructor(private themeService: ThemeService) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.currentTheme = this.themeService.theme;
|
||||
}
|
||||
ngOnInit(): void {}
|
||||
|
||||
public selectTheme(theme: string): void {
|
||||
this.themeService.selectTheme(theme as Theme);
|
33
src/app/shared/components/settings/settings.component.html
Normal file
33
src/app/shared/components/settings/settings.component.html
Normal file
@ -0,0 +1,33 @@
|
||||
<p-fieldset legend="Mes données personnelles">
|
||||
<app-language-selector></app-language-selector>
|
||||
<app-theme-selector></app-theme-selector>
|
||||
<div class="p-grid p-align-center p-justify-between">
|
||||
<div class="p-col">
|
||||
<p>Pseudo, nom ou prénom :</p>
|
||||
</div>
|
||||
<div class="p-col" pFocusTrap>
|
||||
<input
|
||||
pInputText
|
||||
type="text"
|
||||
[(ngModel)]="user.pseudo"
|
||||
(keydown.enter)="saveChanges()"
|
||||
autofocus
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="p-grid p-align-center">
|
||||
<div class="p-col">
|
||||
<p>Email :</p>
|
||||
</div>
|
||||
<div class="p-col">
|
||||
<input pInputText type="text" [(ngModel)]="user.email" (keydown.enter)="saveChanges()" required />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<br />
|
||||
|
||||
<div class="p-grid">
|
||||
<button class="p-col" pButton (click)="saveChanges()">Enregistrer</button>
|
||||
</div>
|
||||
</p-fieldset>
|
@ -1,19 +1,19 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { LoginComponent } from './login.component';
|
||||
import { SettingsComponent } from './settings.component';
|
||||
|
||||
describe('LoginComponent', () => {
|
||||
let component: LoginComponent;
|
||||
let fixture: ComponentFixture<LoginComponent>;
|
||||
describe('SettingsComponent', () => {
|
||||
let component: SettingsComponent;
|
||||
let fixture: ComponentFixture<SettingsComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [LoginComponent],
|
||||
declarations: [SettingsComponent],
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(LoginComponent);
|
||||
fixture = TestBed.createComponent(SettingsComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
41
src/app/shared/components/settings/settings.component.ts
Normal file
41
src/app/shared/components/settings/settings.component.ts
Normal file
@ -0,0 +1,41 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { DynamicDialogConfig, DynamicDialogRef } from 'primeng';
|
||||
import { Subscription } from 'rxjs';
|
||||
|
||||
import { User } from '../../../core/models/user.model';
|
||||
import { UserService } from '../../../core/services/user.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-settings',
|
||||
templateUrl: './settings.component.html',
|
||||
styleUrls: ['./settings.component.scss'],
|
||||
})
|
||||
export class SettingsComponent implements OnInit {
|
||||
public user: User;
|
||||
private userSubscription: Subscription;
|
||||
|
||||
constructor(public ref: DynamicDialogRef, public config: DynamicDialogConfig, private userService: UserService) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.userSubscription = this.userService.user.subscribe((user: User) => {
|
||||
this.user = user;
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
if (this.userSubscription) {
|
||||
this.userSubscription.unsubscribe();
|
||||
}
|
||||
}
|
||||
|
||||
public saveChanges(): void {
|
||||
if (this.user?.pseudo?.length > 0) {
|
||||
this.userService.updateUser(this.user);
|
||||
}
|
||||
this.closeDialog();
|
||||
}
|
||||
|
||||
public closeDialog(): void {
|
||||
this.ref.close();
|
||||
}
|
||||
}
|
@ -1,55 +1,67 @@
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { ChartsModule } from 'ng2-charts';
|
||||
import {
|
||||
ButtonModule,
|
||||
ConfirmDialogModule,
|
||||
DialogModule,
|
||||
DialogService,
|
||||
DynamicDialogModule,
|
||||
FieldsetModule,
|
||||
FocusTrapModule,
|
||||
InputSwitchModule,
|
||||
InputTextareaModule,
|
||||
MessageModule,
|
||||
PanelModule,
|
||||
ProgressSpinnerModule,
|
||||
SidebarModule,
|
||||
StepsModule,
|
||||
ToastModule,
|
||||
TooltipModule,
|
||||
} from 'primeng';
|
||||
import { ConfirmationService, MessageService } from 'primeng/api';
|
||||
|
||||
import { FeedbackComponent } from './components/feedback/feedback.component';
|
||||
import { PageNotFoundComponent } from './components/page-not-found/page-not-found.component';
|
||||
import { PollPageComponent } from './components/poll-page/poll-page.component';
|
||||
import { FeedbackComponent } from './components/feedback/feedback.component';
|
||||
import { LanguageSelectorComponent } from './components/selectors/language-selector/language-selector.component';
|
||||
import { ThemeSelectorComponent } from './components/selectors/theme-selector/theme-selector.component';
|
||||
import { SettingsComponent } from './components/settings/settings.component';
|
||||
|
||||
const MODULE_LIST = [CommonModule, ChartsModule, FormsModule, TranslateModule];
|
||||
const PRIMENG_MODULE_LIST = [
|
||||
ButtonModule,
|
||||
ConfirmDialogModule,
|
||||
DialogModule,
|
||||
DynamicDialogModule,
|
||||
FieldsetModule,
|
||||
FocusTrapModule,
|
||||
InputSwitchModule,
|
||||
InputTextareaModule,
|
||||
MessageModule,
|
||||
PanelModule,
|
||||
ProgressSpinnerModule,
|
||||
SidebarModule,
|
||||
StepsModule,
|
||||
ToastModule,
|
||||
TooltipModule,
|
||||
];
|
||||
const COMPONENT_LIST = [
|
||||
ThemeSelectorComponent,
|
||||
LanguageSelectorComponent,
|
||||
PageNotFoundComponent,
|
||||
PollPageComponent,
|
||||
SettingsComponent,
|
||||
FeedbackComponent,
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
declarations: [PageNotFoundComponent, PollPageComponent, FeedbackComponent],
|
||||
imports: [
|
||||
CommonModule,
|
||||
ChartsModule,
|
||||
ConfirmDialogModule,
|
||||
DialogModule,
|
||||
InputSwitchModule,
|
||||
InputTextareaModule,
|
||||
MessageModule,
|
||||
ProgressSpinnerModule,
|
||||
SidebarModule,
|
||||
StepsModule,
|
||||
ToastModule,
|
||||
],
|
||||
exports: [
|
||||
TranslateModule,
|
||||
ChartsModule,
|
||||
ConfirmDialogModule,
|
||||
DialogModule,
|
||||
FeedbackComponent,
|
||||
InputSwitchModule,
|
||||
InputTextareaModule,
|
||||
MessageModule,
|
||||
ProgressSpinnerModule,
|
||||
SidebarModule,
|
||||
StepsModule,
|
||||
ToastModule,
|
||||
PageNotFoundComponent,
|
||||
PollPageComponent,
|
||||
],
|
||||
providers: [ConfirmationService, MessageService],
|
||||
declarations: COMPONENT_LIST,
|
||||
imports: [...MODULE_LIST, ...PRIMENG_MODULE_LIST],
|
||||
exports: [...MODULE_LIST, ...PRIMENG_MODULE_LIST, ...COMPONENT_LIST],
|
||||
providers: [ConfirmationService, MessageService, DialogService],
|
||||
entryComponents: [SettingsComponent],
|
||||
})
|
||||
export class SharedModule {}
|
||||
|
@ -1,5 +1,5 @@
|
||||
const backendApiUrlsInDev = {
|
||||
local: 'http://localhost:8000/api',
|
||||
local: 'http://localhost:8000',
|
||||
remote: 'https://framadate-api.cipherbliss.com/api/v1',
|
||||
};
|
||||
|
||||
@ -9,31 +9,37 @@ export const environment = {
|
||||
api: {
|
||||
baseHref: backendApiUrlsInDev.remote,
|
||||
endpoints: {
|
||||
poll: {
|
||||
name: '/poll',
|
||||
vote: {
|
||||
name: '/vote',
|
||||
polls: {
|
||||
name: '/polls',
|
||||
votes: {
|
||||
name: '/votes',
|
||||
},
|
||||
comment: {
|
||||
name: '/comment',
|
||||
comments: {
|
||||
name: '/comments',
|
||||
},
|
||||
slug: {
|
||||
name: '/slug',
|
||||
slugs: {
|
||||
name: '/slugs',
|
||||
},
|
||||
},
|
||||
user: {
|
||||
name: '/user',
|
||||
voteStack: {
|
||||
name: '/votes-stacks',
|
||||
},
|
||||
users: {
|
||||
name: '/users',
|
||||
polls: {
|
||||
name: '/polls',
|
||||
sendEmail: {
|
||||
name: '/polls/send-email',
|
||||
name: '/send-email',
|
||||
},
|
||||
},
|
||||
},
|
||||
admin: {
|
||||
name: '/admin',
|
||||
cleanPolls: {
|
||||
name: '/admin/clean-polls',
|
||||
polls: {
|
||||
name: '/polls',
|
||||
clean: {
|
||||
name: '/clean',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -3,7 +3,7 @@
|
||||
// The list of file replacements can be found in `angular.json`.
|
||||
|
||||
const backendApiUrlsInDev = {
|
||||
local: 'http://localhost:8000',
|
||||
local: 'http://localhost:8000/api/v1',
|
||||
remote: 'https://framadate-api.cipherbliss.com/api/v1',
|
||||
};
|
||||
|
||||
@ -11,13 +11,16 @@ export const environment = {
|
||||
production: false,
|
||||
appTitle: 'FramaSondage',
|
||||
api: {
|
||||
baseHref: backendApiUrlsInDev.remote,
|
||||
baseHref: backendApiUrlsInDev.local,
|
||||
endpoints: {
|
||||
polls: {
|
||||
name: '/polls',
|
||||
votes: {
|
||||
name: '/votes',
|
||||
},
|
||||
votesStacks: {
|
||||
name: '/votes-stacks',
|
||||
},
|
||||
comments: {
|
||||
name: '/comments',
|
||||
},
|
||||
@ -25,9 +28,6 @@ export const environment = {
|
||||
name: '/slugs',
|
||||
},
|
||||
},
|
||||
voteStack: {
|
||||
name: '/votes-stacks',
|
||||
},
|
||||
users: {
|
||||
name: '/users',
|
||||
polls: {
|
||||
@ -37,15 +37,6 @@ export const environment = {
|
||||
},
|
||||
},
|
||||
},
|
||||
admin: {
|
||||
name: '/admin',
|
||||
polls: {
|
||||
name: '/polls',
|
||||
clean: {
|
||||
name: '/clean',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
poll: {
|
||||
|
Loading…
Reference in New Issue
Block a user