rebase branch 'feature/refacto_1' of https://framagit.org/framasoft/framadate/funky-framadate-front into feature/refacto_1

develop
Baptiste Lemoine 2 years ago
commit 7a4f16e484
  1. 3
      .gitignore
  2. 28
      README.md
  3. 42
      backend-api-endpoints-doc.md
  4. 12
      package.json
  5. 10
      src/app/app-routing.module.ts
  6. 21
      src/app/app.module.ts
  7. 2
      src/app/config/DateUtilities.ts
  8. 4
      src/app/config/PollConfig.ts
  9. 4
      src/app/config/Routes.ts
  10. 24
      src/app/config/graph-canevas-options.ts
  11. 0
      src/app/mocks/choice.ts
  12. 0
      src/app/mocks/comment-same-text-error.json
  13. 0
      src/app/mocks/comment-too-fast-error.json
  14. 0
      src/app/mocks/config-poll-dessins-animes.json
  15. 0
      src/app/mocks/created-comment.json
  16. 0
      src/app/mocks/mock-comments.ts
  17. 0
      src/app/mocks/mock-graph.ts
  18. 0
      src/app/mocks/mock-poll3.ts
  19. 0
      src/app/mocks/mock-success-vote.ts
  20. 0
      src/app/mocks/mockmypolls.ts
  21. 0
      src/app/mocks/votestack-success.json
  22. 2
      src/app/pages/answers/answers.component.ts
  23. 2
      src/app/pages/create-or-retrieve/create-or-retrieve.component.ts
  24. 2
      src/app/pages/dates/dates.component.ts
  25. 2
      src/app/pages/end-confirmation/end-confirmation.component.ts
  26. 0
      src/app/pages/example/base-page/base.component.html
  27. 0
      src/app/pages/example/base-page/base.component.scss
  28. 0
      src/app/pages/example/base-page/base.component.spec.ts
  29. 2
      src/app/pages/example/base-page/base.component.ts
  30. 0
      src/app/pages/example/kind/kind.component.html
  31. 0
      src/app/pages/example/kind/kind.component.scss
  32. 0
      src/app/pages/example/kind/kind.component.spec.ts
  33. 2
      src/app/pages/example/kind/kind.component.ts
  34. 0
      src/app/pages/example/pictures/pictures.component.html
  35. 0
      src/app/pages/example/pictures/pictures.component.scss
  36. 0
      src/app/pages/example/pictures/pictures.component.spec.ts
  37. 2
      src/app/pages/example/pictures/pictures.component.ts
  38. 2
      src/app/pages/home/home.component.ts
  39. 2
      src/app/pages/password/password.component.ts
  40. 1
      src/app/pages/poll-display/poll-display.component.scss
  41. 8
      src/app/pages/poll/poll-administration/poll-administration.module.ts
  42. 0
      src/app/pages/poll/poll-display/poll-display.component.html
  43. 1
      src/app/pages/poll/poll-display/poll-display.component.scss
  44. 0
      src/app/pages/poll/poll-display/poll-display.component.spec.ts
  45. 10
      src/app/pages/poll/poll-display/poll-display.component.ts
  46. 0
      src/app/pages/poll/poll-graphic/poll-graphic.component.html
  47. 0
      src/app/pages/poll/poll-graphic/poll-graphic.component.scss
  48. 0
      src/app/pages/poll/poll-graphic/poll-graphic.component.spec.ts
  49. 40
      src/app/pages/poll/poll-graphic/poll-graphic.component.ts
  50. 8
      src/app/pages/poll/poll-participation/poll-participation.module.ts
  51. 2
      src/app/pages/resume/resume.component.ts
  52. 2
      src/app/pages/visibility/visibility.component.html
  53. 4
      src/app/pages/visibility/visibility.component.ts
  54. 4
      src/app/pages/voting/voting-choice/voting-choice.component.spec.ts
  55. 2
      src/app/pages/voting/voting-comment/voting-comment.component.ts
  56. 2
      src/app/pages/voting/voting-graph/voting-graph.component.ts
  57. 6
      src/app/pages/voting/voting-summary/voting-summary.component.html
  58. 13
      src/app/pages/voting/voting-summary/voting-summary.component.ts
  59. 69
      src/app/services/config.service.ts
  60. 11
      src/app/services/poll.service.ts
  61. 12
      src/app/services/progression.service.spec.ts
  62. 4
      src/app/shared/enums/answer-granularity.enum.ts
  63. 5
      src/app/shared/enums/answer-type.enum.ts
  64. 4
      src/app/shared/enums/poll-type.enum.ts
  65. 5
      src/app/shared/enums/theme.enum.ts
  66. 5
      src/app/shared/interfaces/date-options.interface.ts
  67. 6
      src/app/shared/models/answer.model.ts
  68. 13
      src/app/shared/models/poll-config.model.ts
  69. 9
      src/app/shared/models/poll-options.model.ts
  70. 19
      src/app/shared/models/poll.model.ts
  71. 5
      src/app/shared/models/ui-config.model.ts
  72. 10
      src/app/shared/models/user.model.ts
  73. 16
      src/app/shared/services/api.service.spec.ts
  74. 195
      src/app/shared/services/api.service.ts
  75. 16
      src/app/shared/services/comment.service.spec.ts
  76. 15
      src/app/shared/services/comment.service.ts
  77. 16
      src/app/shared/services/date-utils.service.spec.ts
  78. 50
      src/app/shared/services/date-utils.service.ts
  79. 16
      src/app/shared/services/poll-utils.service.spec.ts
  80. 47
      src/app/shared/services/poll-utils.service.ts
  81. 10
      src/app/shared/services/poll.service.spec.ts
  82. 42
      src/app/shared/services/poll.service.ts
  83. 16
      src/app/shared/services/theme.service.spec.ts
  84. 8
      src/app/shared/services/theme.service.ts
  85. 16
      src/app/shared/services/user.service.spec.ts
  86. 19
      src/app/shared/services/user.service.ts
  87. 16
      src/app/shared/services/vote.service.spec.ts
  88. 17
      src/app/shared/services/vote.service.ts
  89. 8
      src/app/shared/shared.module.ts
  90. 0
      src/app/ui/debugger/debugger.component.html
  91. 0
      src/app/ui/debugger/debugger.component.scss
  92. 0
      src/app/ui/debugger/debugger.component.spec.ts
  93. 2
      src/app/ui/debugger/debugger.component.ts
  94. 0
      src/app/ui/navigation/header/header.component.html
  95. 0
      src/app/ui/navigation/header/header.component.scss
  96. 0
      src/app/ui/navigation/header/header.component.spec.ts
  97. 0
      src/app/ui/navigation/header/header.component.ts
  98. 2
      src/app/ui/navigation/navigation.component.ts
  99. 8
      src/app/ui/navigation/two-links/two-links.component.html
  100. 0
      src/app/ui/navigation/two-links/two-links.component.scss
  101. Some files were not shown because too many files have changed in this diff Show More

3
.gitignore vendored

@ -48,3 +48,6 @@ Thumbs.db
# Editor-specific configuration
.vscode/
# linter
.eslintcache

@ -1,3 +1,30 @@
## 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 |
---
# Framadate
This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 8.2.1.
@ -27,4 +54,3 @@ Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protrac
Before using ng for the first time in this project, use `npm i` to install needed npm modules.
To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md).

@ -0,0 +1,42 @@
/**
*
* -------------------------- -------- -------- ------ ------------------------------------------------
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}
-------------------------- -------- -------- ------ ------------------------------------------------
*/

@ -10,7 +10,6 @@
"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:demobliss": "ng build --crossOrigin=anonymous --extractCss=true --baseHref=https://framadate-api.cipherbliss.com --progress=true --prod && npm run package",
"test": "jest",
"test:watch": "jest --watch",
"test:ci": "jest --runInBand",
@ -20,8 +19,7 @@
"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",
"postinstall": "ngcc"
"build-storybook": "build-storybook"
},
"private": false,
"dependencies": {
@ -39,13 +37,16 @@
"@ngx-translate/core": "^12.1.2",
"@ngx-translate/http-loader": "^4.0.0",
"angular-date-value-accessor": "^1.0.2",
"axios": "^0.19.2",
"bulma": "^0.8.2",
"chart.js": "^2.8.0",
"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",
"ngx-clipboard": "^13.0.0",
"ngx-markdown": "^9.0.0",
"ngx-toaster": "^1.0.1",
@ -55,6 +56,7 @@
"rxjs": "^6.5.5",
"rxjs-compat": "^6.5.5",
"tslib": "^1.11.1",
"uuid": "^7.0.3",
"zone.js": "^0.10.3"
},
"devDependencies": {
@ -72,6 +74,7 @@
"@types/jasminewd2": "~2.0.8",
"@types/jest": "^25.2.1",
"@types/node": "^13.11.1",
"@types/uuid": "^7.0.2",
"@typescript-eslint/eslint-plugin": "^2.27.0",
"@typescript-eslint/parser": "^2.27.0",
"babel-loader": "^8.1.0",
@ -98,7 +101,8 @@
"src/{app,environments,assets}/**/*.{js,jsx,ts,tsx,md,html,css,scss}": [
"prettier --write",
"git add"
]
],
"*.js": "eslint --cache --fix"
},
"jest": {
"preset": "jest-preset-angular",

@ -3,20 +3,20 @@ import { RouterModule, Routes } from '@angular/router';
import { AdminComponent } from './pages/admin/admin.component';
import { AnswersComponent } from './pages/answers/answers.component';
import { BaseComponent } from './pages/base-page/base.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/kind/kind.component';
import { KindComponent } from './pages/example/kind/kind.component';
import { PasswordComponent } from './pages/password/password.component';
import { PicturesComponent } from './pages/pictures/pictures.component';
import { PollDisplayComponent } from './pages/poll-display/poll-display.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 './poll-graphic/poll-graphic.component';
import { PollGraphicComponent } from './pages/poll/poll-graphic/poll-graphic.component';
const routes: Routes = [
{ path: '', redirectTo: 'step/creation', pathMatch: 'full' },

@ -24,19 +24,19 @@ import { ToastModule } from 'primeng/toast';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { DateValueAccessorModule } from './custom-lib/date-value-accessor';
import { DebuggerComponent } from './debugger/debugger.component';
import { HeaderComponent } from './header/header.component';
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/base-page/base.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/kind/kind.component';
import { KindComponent } from './pages/example/kind/kind.component';
import { PasswordComponent } from './pages/password/password.component';
import { PicturesComponent } from './pages/pictures/pictures.component';
import { PollDisplayComponent } from './pages/poll-display/poll-display.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';
@ -47,9 +47,8 @@ import { VotingGraphComponent } from './pages/voting/voting-graph/voting-graph.c
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 './poll-graphic/poll-graphic.component';
import { PollGraphicComponent } from './pages/poll/poll-graphic/poll-graphic.component';
import { ConfigService } from './services/config.service';
import { PollService } from './services/poll.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';
@ -57,7 +56,8 @@ import { MasterHeadComponent } from './ui/navigation/master-head/master-head.com
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/theme-selector/theme-selector.component';
import { ThemeSelectorComponent } from './ui/selector/theme-selector/theme-selector.component';
import { TwoLinksComponent } from './ui/navigation/two-links/two-links.component';
export class MyMissingTranslationHandler implements MissingTranslationHandler {
handle(params: MissingTranslationHandlerParams) {
@ -107,6 +107,7 @@ export function HttpLoaderFactory(http: HttpClient): TranslateHttpLoader {
ThemeSelectorComponent,
MasterHeadComponent,
LanguageComponent,
TwoLinksComponent,
],
imports: [
ConfirmDialogModule,
@ -135,7 +136,7 @@ export function HttpLoaderFactory(http: HttpClient): TranslateHttpLoader {
HttpClientModule,
FormsModule,
],
providers: [TranslateService, ConfigService, PollService, MessageService, ConfirmationService],
providers: [TranslateService, ConfigService, MessageService, ConfirmationService],
bootstrap: [AppComponent],
})
export class AppModule {}

@ -32,7 +32,7 @@ export class DateUtilities {
});
d1.setDate(d1.getDate() + interval);
}
return dates.slice(0);
return [...dates];
}
/**

@ -57,8 +57,8 @@ export class PollConfig {
customUrl = ''; // custom slug in the url, must be unique
customUrlIsUnique = null; // given by the backend
urlSlugPublic = null;
urlPublic = environment.production ? '' : environment.baseHref + '/#/poll/id/4';
urlAdmin = environment.baseHref + '/#/admin/d65es45fd45sdf45sd345f312sdf31sgfd345';
urlPublic = environment.production ? '' : window.location.origin + '/#/poll/id/4';
urlAdmin = window.location.origin + '/#/admin/d65es45fd45sdf45sd345f312sdf31sgfd345';
adminKey = ''; // key to change config of the poll
owner_modifier_token = ''; // key to change a vote stack
canModifyAnswers = true; // bool for the frontend selector

@ -1,4 +0,0 @@
/**
* each step in the form is a component
*/
export const Routes = [];

@ -1,24 +0,0 @@
export var graphOptions = {
legend: { display: false },
scales: {
xAxes: [
{
gridLines: { drawBorder: false, display: false },
display: false,
stacked: true,
ticks: {
beginAtZero: true,
maxRotation: 0,
minRotation: 0,
},
},
],
yAxes: [
{
gridLines: { drawBorder: true, display: false },
display: true,
stacked: true,
},
],
},
};

@ -1,5 +1,5 @@
import { AfterViewInit, ChangeDetectorRef, Component, Inject, OnChanges, OnInit } from '@angular/core';
import { BaseComponent } from '../base-page/base.component';
import { BaseComponent } from '../example/base-page/base.component';
import { ConfigService } from '../../services/config.service';
import { DOCUMENT } from '@angular/common';

@ -1,5 +1,5 @@
import { Component, OnInit } from '@angular/core';
import { BaseComponent } from '../base-page/base.component';
import { BaseComponent } from '../example/base-page/base.component';
import { ConfigService } from '../../services/config.service';
@Component({

@ -1,6 +1,6 @@
import { ChangeDetectorRef, Component, Inject, OnInit } from '@angular/core';
import { ConfigService } from '../../services/config.service';
import { BaseComponent } from '../base-page/base.component';
import { BaseComponent } from '../example/base-page/base.component';
import { DOCUMENT } from '@angular/common';
import { MessageService } from 'primeng/api';
import { otherDefaultDates } from '../../config/defaultConfigs';

@ -1,5 +1,5 @@
import { Component, OnInit } from '@angular/core';
import { BaseComponent } from '../base-page/base.component';
import { BaseComponent } from '../example/base-page/base.component';
import { HttpClient } from '@angular/common/http';
import { ConfigService } from '../../services/config.service';
import { MessageService } from 'primeng/api';

@ -1,5 +1,5 @@
import { Component, OnInit } from '@angular/core';
import { ConfigService } from '../../services/config.service';
import { ConfigService } from '../../../services/config.service';
@Component({
selector: 'framadate-base-page',

@ -1,6 +1,6 @@
import { Component, OnInit } from '@angular/core';
import { BaseComponent } from '../base-page/base.component';
import { ConfigService } from '../../services/config.service';
import { ConfigService } from '../../../services/config.service';
@Component({
selector: 'framadate-page-kind',

@ -1,5 +1,5 @@
import { Component, OnInit } from '@angular/core';
import { ConfigService } from '../../services/config.service';
import { ConfigService } from '../../../services/config.service';
import { BaseComponent } from '../base-page/base.component';
@Component({

@ -1,6 +1,6 @@
import { Component, OnInit } from '@angular/core';
import { ConfigService } from '../../services/config.service';
import { BaseComponent } from '../base-page/base.component';
import { BaseComponent } from '../example/base-page/base.component';
@Component({
selector: 'framadate-home',

@ -1,5 +1,5 @@
import { Component, OnInit } from '@angular/core';
import { BaseComponent } from '../base-page/base.component';
import { BaseComponent } from '../example/base-page/base.component';
import { ConfigService } from '../../services/config.service';
@Component({

@ -1 +0,0 @@
@import '../../../assets/scss/variables';

@ -0,0 +1,8 @@
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
@NgModule({
declarations: [],
imports: [CommonModule],
})
export class PollAdministrationModule {}

@ -0,0 +1 @@
@import '../../../../assets/scss/variables';

@ -1,10 +1,10 @@
import { Component, OnInit } from '@angular/core';
import { BaseComponent } from '../base-page/base.component';
import { ConfigService } from '../../services/config.service';
import { mockComments } from '../../config/mocks/mock-comments';
import { BaseComponent } from '../../example/base-page/base.component';
import { ConfigService } from '../../../services/config.service';
import { mockComments } from '../../../mocks/mock-comments';
import { ActivatedRoute, Router } from '@angular/router';
import { environment } from '../../../environments/environment';
import { mockPoll3 } from '../../config/mocks/mock-poll3';
import { environment } from '../../../../environments/environment';
import { mockPoll3 } from '../../../mocks/mock-poll3';
@Component({
selector: 'framadate-poll-display',

@ -1,10 +1,9 @@
import { Component, Inject, OnInit } from '@angular/core';
import { Chart } from 'chart.js';
import { DOCUMENT } from '@angular/common';
import { mockGraphConfig } from '../config/mocks/mock-graph';
import { graphOptions } from '../config/graph-canevas-options';
import { ConfigService } from '../services/config.service';
import { mockPoll3 } from '../config/mocks/mock-poll3';
import { mockGraphConfig } from '../../../mocks/mock-graph';
import { ConfigService } from '../../../services/config.service';
import { mockPoll3 } from '../../../mocks/mock-poll3';
@Component({
selector: 'framadate-poll-graphic',
@ -24,7 +23,7 @@ export class PollGraphicComponent implements OnInit {
constructor(@Inject(DOCUMENT) private document: any, private config: ConfigService) {}
ngOnInit() {
ngOnInit(): void {
this.formatDataAnswers(this.graphicConfig);
this.isColorblind = false;
this.pollConfigRetrieved = new Chart(this.document.getElementById('graph'), {
@ -52,15 +51,15 @@ export class PollGraphicComponent implements OnInit {
},
],
},
options: graphOptions,
options: this.getSettedGraphOptions(),
});
}
toggleColorblind() {
public toggleColorblind(): void {
this.isColorblind = !this.isColorblind;
}
formatDataAnswers(poll) {
public formatDataAnswers(poll): void {
// if (poll && poll.pollType === "date") {
this.initPollCounter();
poll.answers.forEach((response) => {
@ -79,11 +78,34 @@ export class PollGraphicComponent implements OnInit {
// }
}
initPollCounter() {
public initPollCounter(): void {
this.nbPoll++;
this.dateList[this.nbPoll - 1] = 'jeudi';
this.maybeList[this.nbPoll - 1] = 0;
this.yesList[this.nbPoll - 1] = 0;
this.noList[this.nbPoll - 1] = 0;
}
private getSettedGraphOptions(): any {
// TODO: create interfaces, or find another way to work. "any" return type should be removed.
return {
legend: { display: false },
scales: {
xAxes: [
{
gridLines: { drawBorder: false, display: false },
display: false,
stacked: true,
ticks: { beginAtZero: true, maxRotation: 0, minRotation: 0 },
},
],
yAxes: [
{
gridLines: { drawBorder: true, display: false },
display: true,
stacked: true,
},
],
},
};
}
}

@ -0,0 +1,8 @@
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
@NgModule({
declarations: [],
imports: [CommonModule],
})
export class PollParticipationModule {}

@ -1,5 +1,5 @@
import { Component } from '@angular/core';
import { BaseComponent } from '../base-page/base.component';
import { BaseComponent } from '../example/base-page/base.component';
import { ConfigService } from '../../services/config.service';
import { Router } from '@angular/router';

@ -103,7 +103,7 @@
</sub>
<div class="preview-url">
<a [href]="'/#/vote/poll/slug/' + config.customUrl">
{{ environment.baseHref + '#/vote/poll/slug/' + config.customUrl }}
{{ window.location.origin + '#/vote/poll/slug/' + config.customUrl }}
</a>
<framadate-copy-text [textToCopy]="config.urlPublic"></framadate-copy-text>
</div>

@ -1,5 +1,5 @@
import { Component, OnInit } from '@angular/core';
import { BaseComponent } from '../base-page/base.component';
import { BaseComponent } from '../example/base-page/base.component';
import { ConfigService } from '../../services/config.service';
import { environment } from '../../../environments/environment';
import { PollUtilities } from '../../config/PollUtilities';
@ -11,7 +11,7 @@ import { PollUtilities } from '../../config/PollUtilities';
})
export class VisibilityComponent extends BaseComponent implements OnInit {
showCustomPassword = false;
baseUrl = environment.baseApiHref;
baseUrl = environment.api.baseHref;
environment = environment;
constructor(public config: ConfigService, public utils: PollUtilities) {

@ -7,8 +7,8 @@ import { ConfirmationService, MessageService } from 'primeng';
import { Router } from '@angular/router';
import { ConfigService } from '../../../services/config.service';
import { VotingChoiceComponent } from './voting-choice.component';
import { mockChoice } from '../../../config/mocks/choice';
import { mockPoll3 } from '../../../config/mocks/mock-poll3';
import { mockChoice } from '../../../mocks/choice';
import { mockPoll3 } from '../../../mocks/mock-poll3';
const routerSpy = jest.fn({ navigateByUrl: jest.fn() });

@ -1,5 +1,5 @@
import { Component, Input, OnInit } from '@angular/core';
import { mockComments } from '../../../config/mocks/mock-comments';
import { mockComments } from '../../../mocks/mock-comments';
@Component({
selector: 'framadate-voting-comment',

@ -1,5 +1,5 @@
import { Component, OnInit } from '@angular/core';
import { BaseComponent } from '../../base-page/base.component';
import { BaseComponent } from '../../example/base-page/base.component';
import { ConfigService } from '../../../services/config.service';
@Component({

@ -113,7 +113,7 @@
<button
*ngIf="config.isAdmin"
type="button"
(click)="showModalDialog()"
(click)="toggleModalDialogVisibility()"
pButton
icon="pi pi-external-link"
label="Show"
@ -121,7 +121,7 @@
show admin confirmation modal
</button>
<p-dialog
[(visible)]="config.displayConfirmVoteModalAdmin"
[visible]="displayConfirmVoteModalAdmin"
[modal]="true"
[baseZIndex]="10000"
[draggable]="false"
@ -147,7 +147,7 @@
type="button btn--large btn btn--block"
pButton
icon="fa fa-check"
(click)="config.displayConfirmVoteModalAdmin = false"
(click)="toggleModalDialogVisibility()"
label="Revenir au sondage"
class="btn btn--primary btn--default btn--purple btn--black-text"
></button>

@ -1,6 +1,6 @@
import { Component, Input, OnInit } from '@angular/core';
import { ConfigService } from '../../../services/config.service';
import { mockPoll3 } from '../../../config/mocks/mock-poll3';
import { mockPoll3 } from '../../../mocks/mock-poll3';
@Component({
selector: 'framadate-voting-summary',
@ -8,14 +8,15 @@ import { mockPoll3 } from '../../../config/mocks/mock-poll3';
styleUrls: ['./voting-summary.component.scss'],
})
export class VotingSummaryComponent implements OnInit {
preferred = 'rien';
severalPreferred = false;
public displayConfirmVoteModalAdmin = false;
public preferred = 'rien';
public severalPreferred = false;
@Input() pollconfig = mockPoll3;
constructor(public config: ConfigService) {}
ngOnInit() {
ngOnInit(): void {
this.computePreferred();
}
@ -44,7 +45,7 @@ export class VotingSummaryComponent implements OnInit {
});
}
showModalDialog() {
this.config.displayConfirmVoteModalAdmin = true;
toggleModalDialogVisibility(): void {
this.displayConfirmVoteModalAdmin = !this.displayConfirmVoteModalAdmin;
}
}

@ -4,9 +4,9 @@ import { HttpClient } from '@angular/common/http';
import { environment } from '../../environments/environment';
import { ConfirmationService, MessageService } from 'primeng/api';
import { Router } from '@angular/router';
import { mockMyPolls } from '../config/mocks/mockmypolls';
import { mockPoll3 } from '../config/mocks/mock-poll3';
import { mockSuccessVote } from '../config/mocks/mock-success-vote';
import { mockMyPolls } from '../mocks/mockmypolls';
import { mockPoll3 } from '../mocks/mock-poll3';
import { mockSuccessVote } from '../mocks/mock-success-vote';
import { PollUtilities } from '../config/PollUtilities';
const LocalstoragePreferences = {
@ -25,7 +25,7 @@ const LocalstoragePreferences = {
export class ConfigService extends PollConfig {
preferences: any = LocalstoragePreferences; // user specific preferences, stored in localstorage, used for theme.
loading = false;
baseHref: any = environment.baseApiHref;
apiBaseHref: any = environment.api.baseHref;
constructor(
private http: HttpClient,
@ -148,7 +148,7 @@ export class ConfigService extends PollConfig {
// TODO
this.todo('check slug is unique');
this.http
.get(`${this.baseHref}/check-slug-is-uniq/${slug}`, this.utils.makeHeaders({ slug: this.customUrl }))
.get(`${this.apiBaseHref}/check-slug-is-uniq/${slug}`, this.utils.makeHeaders({ slug: this.customUrl }))
.subscribe(
(res: any) => {
this.customUrlIsUnique = res.poll.isUnique;
@ -170,7 +170,7 @@ export class ConfigService extends PollConfig {
this.todo('send email for real : TODO');
this.loading = true;
this.http.get(`${this.baseHref}/send-polls-to-user/${this.myEmail}`, this.utils.makeHeaders()).subscribe(
this.http.get(`${this.apiBaseHref}/send-polls-to-user/${this.myEmail}`, this.utils.makeHeaders()).subscribe(
(res) => {
// message: 'Trouvé! Allez voir votre boite email',
this.myPolls = res;
@ -211,7 +211,7 @@ export class ConfigService extends PollConfig {
*/
getPollByURL(url: string) {
this.todo();
return this.http.get(`${this.baseHref}/poll/slug/${url}`, this.utils.makeHeaders());
return this.http.get(`${this.apiBaseHref}/poll/slug/${url}`, this.utils.makeHeaders());
}
/**
@ -220,7 +220,7 @@ export class ConfigService extends PollConfig {
* @param id
*/
getPollById(id: string, password?: string) {
return this.http.get(`${this.baseHref}/poll/${id}`, this.utils.makeHeaders({ body: password }));
return this.http.get(`${this.apiBaseHref}/poll/${id}`, this.utils.makeHeaders({ body: password }));
}
fetchPollFromRoute(event) {
@ -233,7 +233,7 @@ export class ConfigService extends PollConfig {
* @param ownerEmail
*/
getMyPolls(ownerEmail: string) {
this.http.get(`${this.baseHref}/my-polls`, this.utils.makeHeaders({ ownerEmail })).subscribe(
this.http.get(`${this.apiBaseHref}/my-polls`, this.utils.makeHeaders({ ownerEmail })).subscribe(
(res: any) => {
// this.myPolls = res.poll;
},
@ -256,16 +256,16 @@ export class ConfigService extends PollConfig {
this.currentPoll = res;
this.pollId = res.poll.id;
this.owner_modifier_token = res.owner_modifier_token;
this.urlPublic = this.baseHref + '#/vote/poll/id/' + res.poll.id;
this.urlSlugPublic = this.baseHref + '#/vote/poll/slug/' + res.poll.id;
this.urlPublic = this.apiBaseHref + '#/vote/poll/id/' + res.poll.id;
this.urlSlugPublic = this.apiBaseHref + '#/vote/poll/slug/' + res.poll.id;
if (res.poll.customUrl) {
this.urlSlugPublic = this.baseHref + '#/vote/poll/id/' + res.poll.customUrl;
this.urlSlugPublic = this.apiBaseHref + '#/vote/poll/id/' + res.poll.customUrl;
}
if (res.vote_stack) {
this.loadVoteStack(res.vote_stack);
}
this.adminKey = res.admin_key;
this.urlAdmin = this.baseHref + '#/admin/' + res.admin_key;
this.urlAdmin = this.apiBaseHref + '#/admin/' + res.admin_key;
}
resetCurrentChoicesAnswers() {
@ -312,7 +312,7 @@ export class ConfigService extends PollConfig {
createPollFromConfig(config: any) {
this.loading = true;
console.log('config', config);
return this.http.post(`${this.baseHref}/poll`, config, this.utils.makeHeaders()).subscribe(
return this.http.post(`${this.apiBaseHref}/poll`, config, this.utils.makeHeaders()).subscribe(
(res: any) => {
// redirect to the page to administrate the new poll
this.messageService.add({ severity: 'success', summary: 'Sondage Créé' });
@ -372,7 +372,7 @@ export class ConfigService extends PollConfig {
this.handleVoteAdded(mockSuccessVote);
return;
}
this.http.post(`${this.baseHref}/poll/${this.pollId}/vote`, voteStack, this.utils.makeHeaders()).subscribe(
this.http.post(`${this.apiBaseHref}/poll/${this.pollId}/vote`, voteStack, this.utils.makeHeaders()).subscribe(
(res: any) => {
this.handleVoteAdded(res);
},
@ -384,7 +384,8 @@ export class ConfigService extends PollConfig {
handleVoteAdded(res) {
if (this.isAdmin) {
this.displayConfirmVoteModalAdmin = true;
// TODO : REFACTO, displayConfirmVoteModalAdmin exists only in voting-summary.component
//this.displayConfirmVoteModalAdmin = true;
}
// save modifier token
this.myVoteStack.modifier_token = res.modifier_token;
@ -405,7 +406,7 @@ export class ConfigService extends PollConfig {
}
this.http
.patch(
`${this.baseHref}/vote-stack/${voteStack.id}/token/${this.owner_modifier_token}`,
`${this.apiBaseHref}/vote-stack/${voteStack.id}/token/${this.owner_modifier_token}`,
voteStack,
this.utils.makeHeaders()
)
@ -436,7 +437,7 @@ export class ConfigService extends PollConfig {
};
}
console.log('comment', comment);
this.http.post(`${this.baseHref}/poll/${this.pollId}/comment`, comment, this.utils.makeHeaders()).subscribe(
this.http.post(`${this.apiBaseHref}/poll/${this.pollId}/comment`, comment, this.utils.makeHeaders()).subscribe(
(res: any) => {
this.messageService.add({
severity: 'success',
@ -468,18 +469,20 @@ export class ConfigService extends PollConfig {
this.title +
') permanentely?',
accept: () => {
this.http.delete(`${this.baseHref}/poll/${this.pollId}/comments`, this.utils.makeHeaders()).subscribe(
(res: any) => {
this.messageService.add({
severity: 'success',
summary: 'Commentaires bien supprimés',
detail: 'Commentaires du sondage "' + this.title + '" supprimé',
});
},
(e) => {
this.handleError(e);
}
);
this.http
.delete(`${this.apiBaseHref}/poll/${this.pollId}/comments`, this.utils.makeHeaders())
.subscribe(
(res: any) => {
this.messageService.add({
severity: 'success',
summary: 'Commentaires bien supprimés',
detail: 'Commentaires du sondage "' + this.title + '" supprimé',
});
},
(e) => {
this.handleError(e);
}
);
},
});
}
@ -492,7 +495,7 @@ export class ConfigService extends PollConfig {
this.title +
') permanentely?',
accept: () => {
this.http.delete(`${this.baseHref}/poll/${this.pollId}/votes`, this.utils.makeHeaders()).subscribe(
this.http.delete(`${this.apiBaseHref}/poll/${this.pollId}/votes`, this.utils.makeHeaders()).subscribe(
(res: any) => {
this.messageService.add({
severity: 'success',
@ -524,7 +527,7 @@ export class ConfigService extends PollConfig {
self.title +
') and all is data permanentely?',
accept: () => {
this.http.delete(`${this.baseHref}/poll/${this.pollId}`, this.utils.makeHeaders()).subscribe(
this.http.delete(`${this.apiBaseHref}/poll/${this.pollId}`, this.utils.makeHeaders()).subscribe(
(res: any) => {
this.messageService.add({
severity: 'success',
@ -549,7 +552,7 @@ export class ConfigService extends PollConfig {
* TODO
*/
updatePoll(voteStack: any) {
this.http.put(`${this.baseHref}/poll/${this.pollId}`, voteStack, this.utils.makeHeaders()).subscribe(
this.http.put(`${this.apiBaseHref}/poll/${this.pollId}`, voteStack, this.utils.makeHeaders()).subscribe(
(res: any) => {
this.messageService.add({
severity: 'success',

@ -1,11 +0,0 @@
import { Injectable } from '@angular/core';
import { environment } from '../../environments/environment';
@Injectable({
providedIn: 'root',
})
export class PollService {
private baseHref: string = environment.baseApiHref;
constructor() {}
}

@ -1,12 +0,0 @@
import { TestBed } from '@angular/core/testing';
import { ConfigService } from './config.service';
describe('ConfigService', () => {
beforeEach(() => TestBed.configureTestingModule({}));
it('should be created', () => {
const service: ConfigService = TestBed.get(ConfigService);
expect(service).toBeTruthy();
});
});

@ -0,0 +1,4 @@
export enum AnswerGranularity {
BASIC = 'BASIC',
COMPLEX = 'COMPLEX',
}

@ -0,0 +1,5 @@
export enum AnswerType {
YES = 'YES',
NO = 'NO',
MAYBE = 'MAYBE',
}

@ -0,0 +1,4 @@
export enum PollType {
CLASSIC = 'CLASSIC',
DATES = 'DATES',
}

@ -0,0 +1,5 @@
export enum Theme {
LIGHT = 'light-watermelon',
DARK = 'dark-crystal',
RED = 'hot-covid',
}

@ -0,0 +1,5 @@
export interface DateOption {
timeList: any;
literal: string;
date_object?: object;
}

@ -0,0 +1,6 @@
import { AnswerType } from '../enums/answer-type.enum';
import { PollOption } from './poll-options.model';
export class Answer {
constructor(public pollOption: PollOption, public type: AnswerType, public userPseudo: string) {}
}

@ -0,0 +1,13 @@
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
) {}
}

@ -0,0 +1,9 @@
import { isValid } from 'date-fns';
export class PollOption {
constructor(public label: string, public url?: string, public subOptions?: PollOption[]) {}
public isDatePoll(): boolean {
return isValid(this.label);
}
}

@ -0,0 +1,19 @@
import { PollType } from '../enums/poll-type.enum';
import { Answer } from './answer.model';
import { PollConfig } from './poll-config.model';
import { PollOption } from './poll-options.model';
import { User } from './user.model';
export class Poll {
constructor(
public id: string,
public slug: string,
public type: PollType,
public title: string,
public description: string,
public owner: User,
public config: PollConfig,
public options: PollOption[] = [],
public answers: Answer[] = []
) {}
}

@ -0,0 +1,5 @@
import { Theme } from '../enums/theme.enum';
export class UIConfig {
constructor(public isMenuVisible = true, public theme: Theme.LIGHT) {}
}

@ -0,0 +1,10 @@
import { Poll } from './poll.model';
export class User {
constructor(
public isOwner: boolean = false,
public pseudo?: string,
public email?: string,
public polls?: Poll[]
) {}
}

@ -0,0 +1,16 @@
import { TestBed } from '@angular/core/testing';
import { ApiService } from './api.service';
describe('ApiService', () => {
let service: ApiService;
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(ApiService);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
});

@ -0,0 +1,195 @@
import { Injectable } from '@angular/core';
import axios, { AxiosResponse } from 'axios';
import { environment } from 'src/environments/environment';
import { Poll } from '../models/poll.model';
import { User } from '../models/user.model';
@Injectable({
providedIn: 'root',
})
export class ApiService {
////////////
// CREATE //
////////////
public async savePoll(poll: Poll): Promise<void> {
try {
await axios.post(`${environment.api.baseHref}${environment.api.endpoints.poll.name}`, {
params: { config: poll.config },
});
} catch (error) {
this.handleError(error);
}
}
public async saveVote(poll: Poll): Promise<void> {
try {
// TODO: add the votestack in the params
await axios.post(
`${environment.api.baseHref}${environment.api.endpoints.poll.name}/${poll.id}${environment.api.endpoints.poll.vote.name}`,
{ params: { voteStack: {} } }
);
} catch (error) {
this.handleError(error);
}
}
public async saveComment(poll: Poll, comment: string): Promise<void> {
try {
// TODO: add the comment in the params
await axios.post(
`${environment.api.baseHref}${environment.api.endpoints.poll.name}/${poll.id}${environment.api.endpoints.poll.comment.name}`,
{ params: { comment } }
);
} catch (error) {
this.handleError(error);
}
}
//////////
// READ //
//////////
public async isSlugAvailable(slug: string): Promise<boolean> {
try {
// TODO: scenario should be : if we can get this slug, it exists. if not, it doesn't. It's just a GET.
const response: AxiosResponse = await axios.get(
`${environment.api.baseHref}${environment.api.endpoints.poll.slug.name}/${slug}`
);
return response && response.status === 404 ? true : false;
} catch (error) {
this.handleError(error);
}
}
public async sendEmailToUserOfItsPollsList(email: string): Promise<Poll[]> {
// If user is not authenticated: the list of polls is send to user's email by the backend.
try {
const response: AxiosResponse<Poll[]> = await axios.get<Poll[]>(
`${environment.api.baseHref}${environment.api.endpoints.user.polls.sendEmail.name}/${email}`
);
return response ? response.data : [];
} catch (error) {
this.handleError(error);
}
}
public async getPollsByUserEmail(user: User): Promise<Poll[]> {