Merge branch 'feature/refacto_config_service' into 'develop'

split config service & dispatch

See merge request framasoft/framadate/funky-framadate-front!35
archived-develop
ty kayn 3 years ago
commit dedc70a0a6

@ -1,4 +0,0 @@
module.exports = {
stories: ['../src/**/*.stories.ts'],
addons: ['@storybook/addon-actions', '@storybook/addon-links', '@storybook/addon-notes'],
};

@ -1,20 +0,0 @@
{
"extends": "../tsconfig.app.json",
"compilerOptions": {
"types": [
"node"
]
},
"exclude": [
"../src/test.ts",
"../src/**/*.spec.ts",
"../projects/**/*.spec.ts"
],
"include": [
"../src/**/*",
"../projects/**/*"
],
"files": [
"./typings.d.ts"
]
}

@ -1,4 +0,0 @@
declare module '*.md' {
const content: string;
export default content;
}

@ -1,27 +1,26 @@
## LIBRARIES USED
| lib name | usage |
| ------------------------------------------------------------------ | -------------------------------------- |
| [axios](https://github.com/axios/axios) | http client |
| [bulma](https://bulma.io/) | CSS framework |
| [chart.js](https://www.chartjs.org/) | Generate beautiful graphs |
| [compodoc](https://compodoc.app/) | Generate technic documentation |
| [date-fns](https://date-fns.org) | manipulate dates |
| ESlint, Prettier, Lint-staged | Format & lint code |
| [font-awesome](https://github.com/FortAwesome/angular-fontawesome) | Icons collection |
| [fullcalendar](https://fullcalendar.io/docs/initialize-es6) | Manage & display calendars |
| [husky](https://www.npmjs.com/package/husky) | Hook actions on commit |
| [jest](https://jestjs.io/) | test engine |
| [locale-enum](https://www.npmjs.com/package/locale-enum) | enum of all locales |
| [ngx-clipboard](https://www.npmjs.com/package/ngx-clipboard) | Handle clipboard |
| [ngx-markdown](https://www.npmjs.com/package/ngx-markdown) | markdown parser |
| [ngx-toaster](https://www.npmjs.com/package/ngx-toaster) | toast notifications |
| [ngx-webstorage](https://www.npmjs.com/package/ngx-webstorage) | handle localStorage & webStorage |
| [primeNG](https://www.primefaces.org/primeng/) | UI components collection |
| [quill](https://www.npmjs.com/package/quill) | powerful rich text editor. WYSIWYG. |
| [storybook](https://storybook.js.org/) | StyleGuide UI |
| [ts-mockito](https://www.npmjs.com/package/ts-mockito) | Mocks for testing. |
| [uuid](https://www.npmjs.com/package/uuid) | handle client-side generation of uuids |
| status | lib name | usage |
| ------- | -------------------------------------------------------------- | -------------------------------------- |
| | [axios](https://github.com/axios/axios) | http client |
| | [bulma](https://bulma.io/) | CSS framework |
| | [chart.js](https://www.chartjs.org/) | Display graphs. (Commes with MomentJS) |
| | [compodoc](https://compodoc.app/) | Generate technic documentation |
| | [date-fns](https://date-fns.org) | manipulate dates |
| | ESlint, Prettier, Lint-staged | Format & lint code |
| | [fork-awesome](https://forkaweso.me) | Icons collection |
| | [fullcalendar](https://fullcalendar.io/docs/initialize-es6) | Manage & display calendars |
| | [husky](https://www.npmjs.com/package/husky) | Hook actions on commit |
| | [jest](https://jestjs.io/) | test engine |
| removed | [locale-enum](https://www.npmjs.com/package/locale-enum) | enum of all locales |
| | [ngx-clipboard](https://www.npmjs.com/package/ngx-clipboard) | Handle clipboard |
| | [ngx-markdown](https://www.npmjs.com/package/ngx-markdown) | markdown parser |
| | [ngx-webstorage](https://www.npmjs.com/package/ngx-webstorage) | handle localStorage & webStorage |
| | [primeNG](https://www.primefaces.org/primeng/) | UI components collection |
| | [quill](https://www.npmjs.com/package/quill) | powerful rich text editor. WYSIWYG. |
| removed | [storybook](https://storybook.js.org/) | StyleGuide UI |
| | [ts-mockito](https://www.npmjs.com/package/ts-mockito) | Mocks for testing. |
| | [uuid](https://www.npmjs.com/package/uuid) | handle client-side generation of uuids |
---

@ -12,7 +12,7 @@
},
"root": "",
"sourceRoot": "src",
"prefix": "framadate",
"prefix": "app",
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:browser",
@ -22,17 +22,13 @@
"main": "src/main.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "tsconfig.app.json",
"aot": true,
"assets": [
"src/favicon.ico",
"src/assets"
],
"assets": ["src/favicon.ico", "src/assets"],
"styles": [
"node_modules/fork-awesome/css/fork-awesome.min.css",
"node_modules/primeicons/primeicons.css",
"node_modules/font-awesome/css/font-awesome.css",
"node_modules/primeng/resources/themes/nova-light/theme.css",
"node_modules/primeng/resources/primeng.min.css",
"src/assets/scss/styles.scss"
"src/styles.scss"
],
"scripts": [
"node_modules/marked/lib/marked.js",
@ -53,9 +49,11 @@
"sourceMap": false,
"extractCss": true,
"namedChunks": false,
"aot": true,
"extractLicenses": true,
"vendorChunk": false,
"buildOptimizer": true,
"crossOrigin": "anonymous",
"budgets": [
{
"type": "initial",
@ -90,20 +88,13 @@
},
"test": {
"builder": "@angular-builders/jest:run",
"options": {
}
"options": {}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": [
"tsconfig.app.json",
"tsconfig.spec.json",
"e2e/tsconfig.json"
],
"exclude": [
"**/node_modules/**"
]
"tsConfig": ["tsconfig.app.json", "tsconfig.spec.json", "e2e/tsconfig.json"],
"exclude": ["**/node_modules/**"]
}
},
"e2e": {
@ -121,5 +112,8 @@
}
}
},
"defaultProject": "framadate"
"defaultProject": "framadate",
"cli": {
"analytics": "0ba9c0a9-850f-4c5f-8124-cbe6f4c79ef1"
}
}

@ -0,0 +1,3 @@
{
"presets": ["@babel/preset-env"]
}

@ -1,42 +0,0 @@
/**
*
* -------------------------- -------- -------- ------ ------------------------------------------------
Name Method Scheme Host Path
-------------------------- -------- -------- ------ ------------------------------------------------
_twig_error_test ANY ANY ANY /_error/{code}.{_format}
api_get_poll_comment GET ANY ANY /poll/{id}/comments
api_new_comment POST ANY ANY /poll/{id}/comment
api_poll_comments_delete DELETE ANY ANY /poll/{id}/comments
api_send_user_polls GET ANY ANY /send-polls-to-user/{email}
homepageget_default GET ANY ANY /
api_get_all_polls GET ANY ANY /poll/
api_get_poll GET ANY ANY /poll/{id}
api_update_poll PUT ANY ANY /poll/{id}/{token}
api_new_poll POST ANY ANY /poll/
api_test-mail-poll GET ANY ANY /poll/mail/test-mail-poll/{emailChoice}
api_poll_delete DELETE ANY ANY /poll/{id}
api_clean_expired_polls GET ANY ANY /poll/clean-polls
api_check_slug_is_unique GET ANY ANY /poll/admin/{token}
api_new_vote_stack POST ANY ANY /poll/{id}/vote
api_update_vote_stack PATCH ANY ANY /vote-stack/{id}/token/{modifierToken}
api_poll_votes_delete DELETE ANY ANY /poll/{id}/votes/{accessToken}
app.swagger GET ANY ANY /doc.json
-------------------------- -------- -------- ------ ------------------------------------------------
*/
/**
* WANTED CHANGES (seraf)
* -------------------------- -------- -------- ------ ------------------------------------------------
Name Method Scheme Host Path
-------------------------- -------- -------- ------ ------------------------------------------------
api_get_poll_comment GET ANY ANY /poll/{id}/comment
api_delete_poll_comments DELETE ANY ANY /poll/{id}/comment
api_user_polls_send_by_email GET ANY ANY /user/{email}/polls/send-by-email
api_get_user_polls GET ANY ANY /user/{email}/polls
api_get_poll_slug GET ANY ANY /poll/slug/{id}/{token}
api_clean_expired_polls GET ANY ANY /admin/clean-polls/{token}
api_test-mail-poll GET ANY ANY /poll/mail/test-mail-poll/{emailChoice}
api_update_vote_stack PATCH ANY ANY /vote-stack/{id}/token/{modifierToken}
-------------------------- -------- -------- ------ ------------------------------------------------
*/

@ -0,0 +1,46 @@
// TODO: File to be deleted : just temporary documentation of backend API endpoints
/**
*
* -------------------------- -------- -------- ------ ------------------------------------------------
Name Method Scheme Host Path
-------------------------- -------- -------- ------ ------------------------------------------------
_twig_error_test ANY ANY ANY /_error/{code}.{_format}
admin_homepage_get_default GET ANY ANY /admin/
admin_homepage_clean_expired_polls GET ANY ANY /admin/polls/clean/{token}
api_get_poll_comment GET ANY ANY /polls/{id}/comments
api_new_comment POST ANY ANY /polls/{id}/comments
api_poll_comments_delete DELETE ANY ANY /polls/{id}/comments
user_homepageget_default GET ANY ANY /users/
user_homepage_polls_send_by_email GET ANY ANY /users/{email}/polls/send-by-email
api_get_all_polls GET ANY ANY /polls/
api_get_poll GET ANY ANY /polls/{id}
api_update_poll PUT ANY ANY /polls/{id}/{token}
api_new_poll POST ANY ANY /polls/
api_test-mail-polls GET ANY ANY /polls/mail/test-mail-polls/{emailChoice}
api_poll_delete DELETE ANY ANY /polls/{id}
api_check_slug_is_unique GET ANY ANY /polls/slugs/{slug}
api_get_admin_config GET ANY ANY /polls/admin/{token}
api_new_vote_stack POST ANY ANY /polls/{id}/votes
api_update_vote_stack PATCH ANY ANY /votes-stacks/{id}/token/{modifierToken}
api_poll_votes_delete DELETE ANY ANY /polls/{id}/votes/{accessToken}
app.swagger GET ANY ANY /api/doc.json
-------------------------- -------- -------- ------ ------------------------------------------------
*/
/**
* WANTED CHANGES (seraf)
* -------------------------- -------- -------- ------ ------------------------------------------------
Name Method Scheme Host Path
-------------------------- -------- -------- ------ ------------------------------------------------
api_get_poll_comment GET ANY ANY /polls/{id}/comment
api_delete_poll_comments DELETE ANY ANY /polls/{id}/comment
api_user_polls_send_by_email GET ANY ANY /users/{email}/polls/send-by-email
api_get_user_polls GET ANY ANY /users/{email}/polls
api_get_poll_slug GET ANY ANY /polls/slug/{id}/{token}
api_clean_expired_polls GET ANY ANY /admin/clean-polls/{token}
api_test-mail-polls GET ANY ANY /polls/mail/test-mail-polls/{emailChoice}
api_update_vote_stack PATCH ANY ANY /votes-stack/{id}/token/{modifierToken}
-------------------------- -------- -------- ------ ------------------------------------------------
*/

@ -1,13 +1,9 @@
{
"extends": "../tsconfig.json",
"compilerOptions": {
"outDir": "../out-tsc/e2e",
"module": "commonjs",
"target": "es5",
"types": [
"jasmine",
"jasminewd2",
"node"
]
}
"extends": "../tsconfig.json",
"compilerOptions": {
"outDir": "../out-tsc/e2e",
"module": "commonjs",
"target": "es5",
"types": ["jasmine", "jasminewd2", "node"]
}
}

@ -1,11 +0,0 @@
import 'jest-preset-angular';
// const { defaults } = require('jest-config');
//
// module.exports = {
// verbose: true,
// collectCoverage: true,
// // collectCoverageFrom: ['src/**/*.ts'],
// collectCoverageFrom: ['src/app/pages/admin/*.ts'],
// };
Error.stackTraceLimit = 2;

@ -1,15 +1,12 @@
{
"name": "framadate-funky-frontend",
"version": "1.0.0",
"licence": "AGPL-3.0-or-later",
"license": "AGPL-3.0-or-later",
"scripts": {
"ng": "ng",
"start": "ng serve",
"compodoc": "compodoc -p tsconfig.json",
"build": "ng build --crossOrigin=anonymous --prod",
"package": "cat dist/framadate/*.js > dist/framadate/framadate-scripts-bundled.js && ls -l dist/framadate",
"bld:pkg": "npm run build && npm run package",
"build:demo": "ng build --crossOrigin=anonymous --extractCss=true --progress=true --prod && npm run package",
"build": "ng build --prod --progress=true",
"build-prod-stats": "ng build --prod --stats-json",
"test": "jest",
"test:watch": "jest --watch",
"test:ci": "jest --runInBand",
@ -18,13 +15,12 @@
"format:check": "prettier --list-different \"src/{app,environments,assets}/**/*{.ts,.js,.json,.css,.scss}\"",
"format:all": "prettier --write \"src/**/*.{js,jsx,ts,tsx,md,html,css,scss}\"",
"trans": "ng xi18n --output-path=src/locale --i18n-locale=fr",
"storybook": "start-storybook -p 6006",
"build-storybook": "build-storybook"
"compodoc": "compodoc -p tsconfig.app.json"
},
"private": false,
"dependencies": {
"@angular/animations": "^9.1.1",
"@angular/cdk": "^9.2.0",
"@angular/cdk": "^9.2.2",
"@angular/common": "^9.0.7",
"@angular/compiler": "^9.0.7",
"@angular/core": "^9.0.7",
@ -39,57 +35,50 @@
"angular-date-value-accessor": "^1.0.2",
"axios": "^0.19.2",
"bulma": "^0.8.2",
"chart.js": "^2.8.0",
"chart.js": "^2.9.3",
"date-fns": "^2.12.0",
"font-awesome": "^4.7.0",
"jest-preset-angular": "^8.1.3",
"karma-coverage": "^2.0.1",
"karma-firefox-launcher": "^1.3.0",
"karma-phantomjs-launcher": "^1.0.4",
"locale-enum": "^1.1.0",
"fork-awesome": "^1.1.7",
"ngx-clipboard": "^13.0.0",
"ngx-markdown": "^9.0.0",
"ngx-toaster": "^1.0.1",
"ngx-webstorage": "^5.0.0",
"primeicons": "^2.0.0",
"primeng": "^9.0.5",
"primeng": "^9.0.6",
"quill": "^1.3.7",
"rxjs": "^6.5.5",
"rxjs-compat": "^6.5.5",
"tslib": "^1.11.1",
"uuid": "^7.0.3",
"uuid": "^8.0.0",
"zone.js": "^0.10.3"
},
"devDependencies": {
"@angular-builders/jest": "^9.0.1",
"@angular-devkit/build-angular": "^0.901.1",
"@angular/cli": "^9.0.7",
"@angular-devkit/build-angular": "^0.901.2",
"@angular/cli": "^9.1.2",
"@angular/compiler-cli": "^9.1.1",
"@angular/language-service": "^9.0.7",
"@babel/core": "^7.9.0",
"@storybook/addon-actions": "^5.3.18",
"@storybook/addon-links": "^5.3.18",
"@storybook/addon-notes": "^5.3.18",
"@storybook/addons": "^5.3.18",
"@storybook/angular": "^5.3.18",
"@types/jasminewd2": "~2.0.8",
"@babel/preset-env": "^7.9.5",
"@babel/preset-typescript": "^7.9.0",
"@compodoc/compodoc": "^1.1.11",
"@types/jest": "^25.2.1",
"@types/node": "^13.11.1",
"@types/node": "^13.13.2",
"@types/uuid": "^7.0.2",
"@typescript-eslint/eslint-plugin": "^2.27.0",
"@typescript-eslint/parser": "^2.27.0",
"babel-loader": "^8.1.0",
"compodoc": "^0.0.41",
"babel-jest": "^25.4.0",
"eslint": "^6.8.0",
"eslint-config-prettier": "^6.10.1",
"eslint-config-prettier": "^6.11.0",
"eslint-plugin-prettier": "^3.1.3",
"husky": "^4.2.5",
"jasmine-core": "~3.5.0",
"jasmine-spec-reporter": "~5.0.1",
"jest": "^25.3.0",
"lint-staged": "^10.1.3",
"prettier": "^2.0.4",
"jest": "^25.5.1",
"jest-environment-jsdom-sixteen": "^1.0.3",
"jest-preset-angular": "^8.1.3",
"lint-staged": "^10.1.7",
"prettier": "^2.0.5",
"protractor": "~5.4.3",
"ts-node": "~8.8.2",
"ts-jest": "^25.4.0",
"ts-mockito": "^2.5.0",
"ts-node": "~8.9.0",
"typescript": "~3.8.3"
},
"husky": {
@ -106,6 +95,13 @@
},
"jest": {
"preset": "jest-preset-angular",
"setupFilesAfterEnv": "./jest.config.js"
"setupFilesAfterEnv": [
"<rootDir>/src/jest.setup.ts"
],
"testEnvironment": "jest-environment-jsdom-sixteen",
"transform": {
"^.+\\.(ts|html)$": "ts-jest",
"^.+\\.jsx?$": "babel-jest"
}
}
}

@ -1,49 +1,35 @@
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { AdminComponent } from './pages/admin/admin.component';
import { AnswersComponent } from './pages/answers/answers.component';
import { BaseComponent } from './pages/example/base-page/base.component';
import { CreateOrRetrieveComponent } from './pages/create-or-retrieve/create-or-retrieve.component';
import { DatesComponent } from './pages/dates/dates.component';
import { EndConfirmationComponent } from './pages/end-confirmation/end-confirmation.component';
import { HomeComponent } from './pages/home/home.component';
import { KindComponent } from './pages/example/kind/kind.component';
import { PasswordComponent } from './pages/password/password.component';
import { PicturesComponent } from './pages/example/pictures/pictures.component';
import { PollDisplayComponent } from './pages/poll/poll-display/poll-display.component';
import { ResumeComponent } from './pages/resume/resume.component';
import { VisibilityComponent } from './pages/visibility/visibility.component';
import { VotingChoiceComponent } from './pages/voting/voting-choice/voting-choice.component';
import { VotingComponent } from './pages/voting/voting.component';
import { PollGraphicComponent } from './pages/poll/poll-graphic/poll-graphic.component';
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: '', redirectTo: 'step/creation', pathMatch: 'full' },
{ path: 'admin/:token', component: AdminComponent }, // http://localhost:4200/#/admin/srfdgedsTGETHRYJtujTUjTUkTIUKTK
{ path: 'home', component: HomeComponent },
{ path: 'base', component: BaseComponent },
{ path: 'step/base', component: BaseComponent },
{ path: 'step/creation', component: CreateOrRetrieveComponent },
{ path: 'step/date', component: DatesComponent },
{ path: 'step/kind', component: KindComponent },
{ path: 'step/answers', component: AnswersComponent },
{ path: 'step/admin', component: AdminComponent },
{ path: 'step/pictures', component: PicturesComponent },
{ path: 'step/visibility', component: VisibilityComponent },
{ 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: 'votingchoice', component: VotingChoiceComponent },
{ path: 'voting', component: VotingComponent },
{ path: 'step/password', component: PasswordComponent },
{ path: '**', redirectTo: '/home', pathMatch: 'full' },
{ path: '', component: HomeComponent },
{ path: 'login', component: LoginComponent },
{
path: 'administration',
loadChildren: () =>
import('./features/administration/administration.module').then((m) => m.AdministrationModule),
},
{
path: 'participation',
loadChildren: () => import('./features/participation/participation.module').then((m) => m.ParticipationModule),
},
{
path: 'oldstuff',
loadChildren: () => import('./features/old-stuff/old-stuff.module').then((m) => m.OldStuffModule),
},
{ path: '**', component: PageNotFoundComponent },
];
@NgModule({
imports: [RouterModule.forRoot(routes, { useHash: true, anchorScrolling: 'enabled' })],
imports: [
RouterModule.forRoot(routes, {
// enableTracing: true, // <-- debugging purposes only
}),
],
exports: [RouterModule],
})
export class AppRoutingModule {}

@ -1,31 +1,15 @@
<div id="big_container" class="{{ this.config.preferences.themeClass }}">
<header class="big-header">
<div class="container">
<div class="columns">
<div class="column">
<framadate-master-head></framadate-master-head>
</div>
<div class="column">
<framadate-language></framadate-language>
</div>
</div>
</div>
</header>
<div id="big_container" [class]="themeClass">
<app-header [isSidebarOpened]="isSidebarOpened" (toggleSidebarEE)="toggleSidebar($event)"></app-header>
<main>
<div class="container">
<div class="columns">
<div class="column is-one-quarter togglable-menu" *ngIf="config.menuVisible">
<framadate-theme-selector></framadate-theme-selector>
<framadate-navigation [step]="step"></framadate-navigation>
<framadate-debugger *ngIf="isDevelopmentEnv"></framadate-debugger>
</div>
<div class="column">
<router-outlet></router-outlet>
</div>
</div>
</div>
<router-outlet></router-outlet>
</main>
<p-toast position="top-right"></p-toast>
<app-footer></app-footer>
</div>
<p-toast position="bottom-center"></p-toast>
<p-sidebar [(visible)]="isSidebarOpened">
<app-navigation></app-navigation>
</p-sidebar>

@ -1,13 +0,0 @@
@charset "UTF-8";
.big-header {
padding: 0.5rem;
}
i {
display: block;
}
.language-selector {
width: auto;
}

@ -1,55 +1,57 @@
import { Component, Inject } from '@angular/core';
import { TranslateService } from '@ngx-translate/core';
import { NavigationStart, Router } from '@angular/router';
import { DOCUMENT } from '@angular/common';
import { filter } from 'rxjs/operators';
import { ConfigService } from './services/config.service';
import { Component, OnDestroy, OnInit } from '@angular/core';
import { Title } from '@angular/platform-browser';
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 { MockingService } from './core/services/mocking.service';
import { ThemeService } from './core/services/theme.service';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss'],
})
export class AppComponent {
step: string;
isDevelopmentEnv = false;
export class AppComponent implements OnInit, OnDestroy {
public appTitle: string = environment.appTitle;
public themeClass: string;
public isSidebarOpened = false;
private themeSubscription: Subscription;
constructor(
private translate: TranslateService,
public config: ConfigService,
@Inject(DOCUMENT) private document,
private route: Router
) {
this.detectCurrentTabOnRouteChange();
this.isDevelopmentEnv = !environment.production;
}
private titleService: Title,
private themeService: ThemeService,
private mockingService: MockingService
) {}
detectCurrentTabOnRouteChange() {
this.route.events.subscribe((event: any) => {});
this.route.events
.pipe(filter((event) => event instanceof NavigationStart))
.subscribe((event: NavigationStart) => {
this.scrollGoToTop();
this.updateCurrentTab(event);
// only if there is a poll ID
this.config.fetchPollFromRoute(event);
});
ngOnInit(): void {
if (!environment.production) {
this.appTitle += ' [DEV]';
this.mockingService.loadUser(UserRole.REGISTERED);
}
this.titleService.setTitle(this.appTitle);
this.themeSubscription = this.themeService.theme.subscribe((theme: Theme) => {
switch (theme) {
case Theme.DARK:
this.themeClass = 'theme-dark-crystal';
break;
case Theme.RED:
this.themeClass = 'theme-hot-covid';
break;
default:
this.themeClass = 'theme-light-watermelon';
}
});
}
scrollGoToTop() {
this.document.documentElement.scrollTop = 0;
ngOnDestroy(): void {
if (this.themeSubscription) {
this.themeSubscription.unsubscribe();
}
}
updateCurrentTab(event) {
if (event.url) {
const tab = event.url.split('/');
if (tab && tab[2]) {
this.step = tab[2];
} else {
this.step = 'home';
}
}
public toggleSidebar(status: boolean): void {
this.isSidebarOpened = status === true;
}
}

@ -1,10 +1,8 @@
import { CommonModule, registerLocaleData } from '@angular/common';
import { CommonModule } from '@angular/common';
import { HttpClient, HttpClientModule } from '@angular/common/http';
import localeEn from '@angular/common/locales/en';
import localeFr from '@angular/common/locales/fr';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { BrowserModule } from '@angular/platform-browser';
import { BrowserModule, Title } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import {
MissingTranslationHandler,
@ -16,127 +14,54 @@ import {
import { TranslateHttpLoader } from '@ngx-translate/http-loader';
import { ClipboardModule } from 'ngx-clipboard';
import { MarkdownModule } from 'ngx-markdown';
import { ConfirmationService, MessageModule, MessageService } from 'primeng';
import { ConfirmDialogModule } from 'primeng/confirmdialog';
import { DialogModule } from 'primeng/dialog';
import { ToastModule } from 'primeng/toast';
import { NgxWebstorageModule } from 'ngx-webstorage';
import { environment } from '../environments/environment';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { DateValueAccessorModule } from './custom-lib/date-value-accessor';
import { DebuggerComponent } from './ui/debugger/debugger.component';
import { HeaderComponent } from './ui/navigation/header/header.component';
import { AdminComponent } from './pages/admin/admin.component';
import { AnswersComponent } from './pages/answers/answers.component';
import { BaseComponent } from './pages/example/base-page/base.component';
import { CreateOrRetrieveComponent } from './pages/create-or-retrieve/create-or-retrieve.component';
import { DatesComponent } from './pages/dates/dates.component';
import { EndConfirmationComponent } from './pages/end-confirmation/end-confirmation.component';
import { HomeComponent } from './pages/home/home.component';
import { KindComponent } from './pages/example/kind/kind.component';
import { PasswordComponent } from './pages/password/password.component';
import { PicturesComponent } from './pages/example/pictures/pictures.component';
import { PollDisplayComponent } from './pages/poll/poll-display/poll-display.component';
import { ResumeComponent } from './pages/resume/resume.component';
import { VisibilityComponent } from './pages/visibility/visibility.component';
import { ChoicesListComponent } from './pages/voting/choices-list/choices-list.component';
import { CommentsListComponent } from './pages/voting/comments-list/comments-list.component';
import { VotingChoiceComponent } from './pages/voting/voting-choice/voting-choice.component';
import { VotingCommentComponent } from './pages/voting/voting-comment/voting-comment.component';
import { VotingGraphComponent } from './pages/voting/voting-graph/voting-graph.component';
import { VotingNavigationComponent } from './pages/voting/voting-navigation/voting-navigation.component';
import { VotingSummaryComponent } from './pages/voting/voting-summary/voting-summary.component';
import { VotingComponent } from './pages/voting/voting.component';
import { PollGraphicComponent } from './pages/poll/poll-graphic/poll-graphic.component';
import { ConfigService } from './services/config.service';
import { CopyTextComponent } from './ui/copy-text/copy-text.component';
import { ResettableInputDirective } from './ui/directives/resettable-input.directive';
import { ErasableInputComponent } from './ui/erasable-input/erasable-input.component';
import { MasterHeadComponent } from './ui/navigation/master-head/master-head.component';
import { NavigationComponent } from './ui/navigation/navigation.component';
import { LanguageComponent } from './ui/selector/language/language.component';
import { SelectorComponent } from './ui/selector/selector.component';
import { ThemeSelectorComponent } from './ui/selector/theme-selector/theme-selector.component';
import { TwoLinksComponent } from './ui/navigation/two-links/two-links.component';
import { CoreModule } from './core/core.module';
import { OldStuffModule } from './features/old-stuff/old-stuff.module';
import { SharedModule } from './shared/shared.module';
export class MyMissingTranslationHandler implements MissingTranslationHandler {
handle(params: MissingTranslationHandlerParams) {
return 'some value';
public handle(params: MissingTranslationHandlerParams): string {
return `MISSING TRANSLATION FOR [${params.key}]`;
}
}
registerLocaleData(localeFr, 'fr');
registerLocaleData(localeEn, 'en');
export function HttpLoaderFactory(http: HttpClient): TranslateHttpLoader {
return new TranslateHttpLoader(http);
}
@NgModule({
declarations: [
AppComponent,
BaseComponent,
KindComponent,
HeaderComponent,
NavigationComponent,
DatesComponent,
DebuggerComponent,
VisibilityComponent,
ResumeComponent,
PicturesComponent,
AnswersComponent,
EndConfirmationComponent,
CreateOrRetrieveComponent,
VotingSummaryComponent,
VotingGraphComponent,
VotingChoiceComponent,
PasswordComponent,
HomeComponent,
PollGraphicComponent,
AdminComponent,
SelectorComponent,
PollDisplayComponent,
VotingComponent,
VotingCommentComponent,
ResettableInputDirective,
ErasableInputComponent,
CopyTextComponent,
CommentsListComponent,
ChoicesListComponent,
VotingNavigationComponent,
ThemeSelectorComponent,
MasterHeadComponent,
LanguageComponent,
TwoLinksComponent,
],
declarations: [AppComponent],
imports: [
ConfirmDialogModule,
AppRoutingModule,
BrowserAnimationsModule,
BrowserModule,
ClipboardModule,
CommonModule,
BrowserModule,
DialogModule,
DateValueAccessorModule,
BrowserAnimationsModule,
AppRoutingModule,
ToastModule,
MessageModule,
CoreModule,
FormsModule,
HttpClientModule,
MarkdownModule.forRoot(),
NgxWebstorageModule.forRoot({ prefix: environment.localStorage.key }),
SharedModule,
TranslateModule.forRoot({
missingTranslationHandler: {
provide: MissingTranslationHandler,
useClass: MyMissingTranslationHandler,
},
// useDefaultLang: false,
loader: {
provide: TranslateLoader,
useFactory: HttpLoaderFactory,
deps: [HttpClient],
},
missingTranslationHandler: {
provide: MissingTranslationHandler,
useClass: MyMissingTranslationHandler,
},
useDefaultLang: false,
}),
HttpClientModule,
FormsModule,
OldStuffModule,
],
providers: [TranslateService, ConfigService, MessageService, ConfirmationService],
providers: [Title, TranslateService],
bootstrap: [AppComponent],
})
export class AppModule {}

@ -0,0 +1,9 @@
<footer class="footer">
<div class="content has-text-centered">
<p>
<strong>Bulma</strong> by <a href="https://jgthms.com">Jeremy Thomas</a>. The source code is licensed
<a href="http://opensource.org/licenses/mit-license.php">MIT</a>. The website content is licensed
<a href="http://creativecommons.org/licenses/by-nc-sa/4.0/">CC BY NC SA 4.0</a>.
</p>
</div>
</footer>

@ -0,0 +1,24 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { FooterComponent } from './footer.component';
describe('FooterComponent', () => {
let component: FooterComponent;
let fixture: ComponentFixture<FooterComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [FooterComponent],
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(FooterComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

@ -0,0 +1,12 @@
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-footer',
templateUrl: './footer.component.html',
styleUrls: ['./footer.component.scss'],
})
export class FooterComponent implements OnInit {
constructor() {}
ngOnInit(): void {}
}

@ -0,0 +1,74 @@
<header>
<nav class="navbar" role="navigation" aria-label="main navigation">
<div class="navbar-brand">
<a class="navbar-item">
<a class="navbar-item" role="button" (click)="toggleSidebarOpening()"> Dev menu </a>
</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">
<a class="navbar-item" routerLink="/" routerLinkActive="is-active">
Home
</a>
<div class="navbar-item has-dropdown is-hoverable">
<a class="navbar-link"> Menu </a>
<div class="navbar-dropdown">
<a class="navbar-item" routerLink="administration" routerLinkActive="is-active">
Crรฉer un sondage
</a>
<a class="navbar-item" routerLink="administration/profile" routerLinkActive="is-active">
Mes sondages
</a>
</div>
</div>
<div class="navbar-item has-dropdown is-hoverable">
<a class="navbar-link"> Modules </a>
<div class="navbar-dropdown">
<a class="navbar-item" routerLink="administration" routerLinkActive="is-active">
Administration
</a>
<a class="navbar-item" routerLink="participation" routerLinkActive="is-active">
Participation
</a>
<a class="navbar-item" routerLink="oldstuff" routerLinkActive="is-active">
Old stuff
</a>
</div>
</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>
</div>
</div>
</div>
</nav>
</header>

@ -0,0 +1,16 @@
import { Component, EventEmitter, Output, Input } from '@angular/core';
@Component({
selector: 'app-header',
templateUrl: './header.component.html',
styleUrls: ['./header.component.scss'],
})
export class HeaderComponent {
@Input() isSidebarOpened: boolean;
@Output() toggleSidebarEE = new EventEmitter<boolean>();
public toggleSidebarOpening(): void {
this.isSidebarOpened = !this.isSidebarOpened;
this.toggleSidebarEE.emit(this.isSidebarOpened);
}
}

@ -0,0 +1,19 @@
<section class="hero">
<div class="hero-body">
<div class="container">
<h1 class="title">
Bienvenue sur Framasondage
</h1>
<h2 class="subtitle">
Se consulter simplement pour sโ€™organiser collectivement.
</h2>
<div class="columns">
<div class="column">
<a role="button" class="button is-fullwidth" routerLink="administration">
Crรฉer un nouveau sondage
</a>
</div>
</div>
</div>
</div>
</section>

@ -0,0 +1,12 @@
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-home',
templateUrl: './home.component.html',
styleUrls: ['./home.component.scss'],
})
export class HomeComponent implements OnInit {
constructor() {}
ngOnInit(): void {}
}

@ -0,0 +1,24 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { LoginComponent } from './login.component';
describe('LoginComponent', () => {
let component: LoginComponent;
let fixture: ComponentFixture<LoginComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [LoginComponent],
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(LoginComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

@ -0,0 +1,12 @@
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,14 +1,13 @@
<div class="home_link">
<a [routerLink]="'home'" aria-roledescription="home">
<a class="button" routerLink="/" aria-roledescription="home">
<h1>
<span class="logo_first">Frama</span>
<span class="logo_second">date</span>
(dรฉmo)
<span class="logo_second">Sondage</span>
</h1>
</a>
<div class="legend">
<a class="button legend" href="https://framasoft.org/" target="_blank">
proposรฉ par
<span class="legend_first">Frama</span>
<span class="legend_second">soft</span>
</div>
</a>
</div>

@ -0,0 +1,24 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { LogoComponent } from './logo.component';
describe('LogoComponent', () => {
let component: LogoComponent;
let fixture: ComponentFixture<LogoComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [LogoComponent],
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(LogoComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

@ -0,0 +1,8 @@
import { Component } from '@angular/core';
@Component({
selector: 'app-logo',
templateUrl: './logo.component.html',
styleUrls: ['./logo.component.scss'],
})
export class LogoComponent {}

@ -0,0 +1,10 @@
<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>

@ -0,0 +1,24 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { LanguageSelectorComponent } from './language-selector.component';
describe('LanguageSelectorComponent', () => {
let component: LanguageSelectorComponent;
let fixture: ComponentFixture<LanguageSelectorComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [LanguageSelectorComponent],
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(LanguageSelectorComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

@ -0,0 +1,38 @@
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;
}
}

@ -0,0 +1,24 @@
<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>

@ -0,0 +1,25 @@
import { Component, OnInit } from '@angular/core';
import { Observable } from 'rxjs';
import { Theme } from '../../../enums/theme.enum';
import { ThemeService } from '../../../services/theme.service';
@Component({
selector: 'app-theme-selector',
templateUrl: './theme-selector.component.html',
styleUrls: ['./theme-selector.component.scss'],
})
export class ThemeSelectorComponent implements OnInit {
public themeEnum = Theme;
public currentTheme: Observable<Theme>;
constructor(private themeService: ThemeService) {}
ngOnInit(): void {
this.currentTheme = this.themeService.theme;
}
public selectTheme(theme: string): void {
this.themeService.selectTheme(theme as Theme);
}
}

@ -0,0 +1,23 @@
<nav>
<a class="button" routerLink="administration" routerLinkActive="active"> AdministrationModule</a>
<a class="button" routerLink="participation/poll/SuperCustomSlug" routerLinkActive="active">
Participate to poll/SuperCustomSlug
</a>
<hr />
<a class="button" routerLink="oldstuff/home" routerLinkActive="active"> <i class="fa fa-home"></i> Accueil </a>
<a class="button" routerLink="oldstuff/step/creation" routerLinkActive="active"> Crรฉation </a>
<a class="button" routerLink="oldstuff/step/date" routerLinkActive="active"> Les Dates </a>
<a class="button" routerLink="oldstuff/step/answers" routerLinkActive="active"> Rรฉponses </a>
<a class="button" routerLink="oldstuff/step/visibility" routerLinkActive="active"> Visibilitรฉ </a>
<a class="button" routerLink="oldstuff/step/resume" routerLinkActive="active"> Rรฉsumรฉ </a>
<a class="button" routerLink="oldstuff/step/end" routerLinkActive="active"> Confirmation </a>
<a class="button" routerLink="oldstuff/step/admin"> Administration </a>
<hr />
<a class="button" routerLink="oldstuff/step/kind" routerLinkActive="active"> Page dรฉmo </a>
<a class="button" routerLink="oldstuff/vote/poll/id/1" routerLinkActive="active"> Sondage 1 </a>
<a class="button" routerLink="oldstuff/vote/poll/id/2" routerLinkActive="active"> Sondage 2 </a>
<a class="button" routerLink="oldstuff/vote/poll/id/3" routerLinkActive="active"> Sondage 3 (dessins animรฉs) </a>
<a class="button" routerLink="oldstuff/graphic/toto" routerLinkActive="active"> Graphique </a>
</nav>

@ -0,0 +1,18 @@
import { Component, OnInit } from '@angular/core';
import { MockingService } from '../../../services/mocking.service';
import { Poll } from '../../../models/poll.model';
@Component({
selector: 'app-navigation',
templateUrl: './navigation.component.html',
styleUrls: ['./navigation.component.scss'],
})
export class NavigationComponent implements OnInit {
public pollsDatabase: Poll[] = [];
constructor(private mockingService: MockingService) {}
ngOnInit(): void {
this.pollsDatabase = this.mockingService.pollsDatabase;
}
}

@ -0,0 +1,35 @@
import { CommonModule } from '@angular/common';
import { NgModule, Optional, SkipSelf } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { RouterModule } from '@angular/router';
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,
],
imports: [CommonModule, FormsModule, RouterModule, TranslateModule],
exports: [HeaderComponent, FooterComponent, NavigationComponent, LoginComponent, LogoComponent],
})
export class CoreModule {
constructor(@Optional() @SkipSelf() parentModule: CoreModule) {
throwIfAlreadyLoaded(parentModule, 'CoreModule');
}
}

@ -0,0 +1,4 @@
export enum Language {
FR = 'FR',
EN = 'EN',
}

@ -0,0 +1,6 @@
export enum MessageSeverity {
SUCCESS = 'success',
INFO = 'info',
WARN = 'warn',
ERROR = 'error',
}

@ -0,0 +1,5 @@
export enum Theme {
LIGHT = 'LIGHT',
DARK = 'DARK',
RED = 'RED',
}

@ -0,0 +1,5 @@
export enum UserRole {
ANONYMOUS = 'ANONYMOUS',
REGISTERED = 'ADMIN',
ADMIN = 'ADMIN',
}

@ -0,0 +1,5 @@
export enum WorkflowStep {
DESCRIPTION = 'DESCRIPTION',
OPTIONS = 'OPTIONS',