Merge branch 'api' into 'dev'

Api calls and merge with dev

See merge request framasoft/framadate/funky-framadate-front!24
This commit is contained in:
ty kayn 2020-01-16 12:06:50 +01:00
commit d63f46483f
78 changed files with 1933 additions and 1314 deletions

View File

@ -1,133 +1,138 @@
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1,
"newProjectRoot": "projects",
"projects": {
"framadate": {
"projectType": "application",
"schematics": {
"@schematics/angular:component": {
"style": "scss"
}
},
"root": "",
"sourceRoot": "src",
"prefix": "framadate",
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:browser",
"options": {
"outputPath": "dist/framadate",
"index": "src/index.html",
"main": "src/main.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "tsconfig.app.json",
"aot": false,
"assets": [
"src/favicon.ico",
"src/assets"
],
"styles": [
"src/styles.scss"
],
"scripts": [
"node_modules/marked/lib/marked.js",
"node_modules/prismjs/prism.js",
"node_modules/prismjs/components/prism-css.min.js"
]
},
"configurations": {
"production": {
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.prod.ts"
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1,
"newProjectRoot": "projects",
"projects": {
"framadate": {
"projectType": "application",
"schematics": {
"@schematics/angular:component": {
"style": "scss"
}
],
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
"extractCss": true,
"namedChunks": false,
"aot": true,
"extractLicenses": true,
"vendorChunk": false,
"buildOptimizer": true,
"budgets": [
{
"type": "initial",
"maximumWarning": "2mb",
"maximumError": "5mb"
},
"root": "",
"sourceRoot": "src",
"prefix": "framadate",
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:browser",
"options": {
"outputPath": "dist/framadate",
"index": "src/index.html",
"main": "src/main.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "tsconfig.app.json",
"aot": false,
"assets": [
"src/favicon.ico",
"src/assets"
],
"styles": [
"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/styles.scss"
],
"scripts": [
"node_modules/marked/lib/marked.js",
"node_modules/prismjs/prism.js",
"node_modules/prismjs/components/prism-css.min.js"
]
},
"configurations": {
"production": {
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.prod.ts"
}
],
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
"extractCss": true,
"namedChunks": false,
"aot": true,
"extractLicenses": true,
"vendorChunk": false,
"buildOptimizer": true,
"budgets": [
{
"type": "initial",
"maximumWarning": "2mb",
"maximumError": "5mb"
},
{
"type": "anyComponentStyle",
"maximumWarning": "6kb",
"maximumError": "10kb"
}
]
}
}
},
{
"type": "anyComponentStyle",
"maximumWarning": "6kb",
"maximumError": "10kb"
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"options": {
"browserTarget": "framadate:build"
},
"configurations": {
"production": {
"browserTarget": "framadate:build:production"
}
}
},
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n",
"options": {
"browserTarget": "framadate:build"
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "src/test.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "tsconfig.spec.json",
"karmaConfig": "karma.conf.js",
"assets": [
"src/favicon.ico",
"src/assets"
],
"styles": [
"src/styles.scss"
],
"scripts": []
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": [
"tsconfig.app.json",
"tsconfig.spec.json",
"e2e/tsconfig.json"
],
"exclude": [
"**/node_modules/**"
]
}
},
"e2e": {
"builder": "@angular-devkit/build-angular:protractor",
"options": {
"protractorConfig": "e2e/protractor.conf.js",
"devServerTarget": "framadate:serve"
},
"configurations": {
"production": {
"devServerTarget": "framadate:serve:production"
}
}
}
]
}
}
},
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"options": {
"browserTarget": "framadate:build"
},
"configurations": {
"production": {
"browserTarget": "framadate:build:production"
}
}
},
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n",
"options": {
"browserTarget": "framadate:build"
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "src/test.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "tsconfig.spec.json",
"karmaConfig": "karma.conf.js",
"assets": [
"src/favicon.ico",
"src/assets"
],
"styles": [
"src/styles.scss"
],
"scripts": []
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": [
"tsconfig.app.json",
"tsconfig.spec.json",
"e2e/tsconfig.json"
],
"exclude": [
"**/node_modules/**"
]
}
},
"e2e": {
"builder": "@angular-devkit/build-angular:protractor",
"options": {
"protractorConfig": "e2e/protractor.conf.js",
"devServerTarget": "framadate:serve"
},
"configurations": {
"production": {
"devServerTarget": "framadate:serve:production"
}
}
}
}
}},
"defaultProject": "framadate"
},
"defaultProject": "framadate"
}

View File

@ -17,7 +17,7 @@ module.exports = function (config) {
},
coverageIstanbulReporter: {
dir: require('path').join(__dirname, './coverage/framadate'),
reports: ['html', 'lcovonly', 'text-summary'],
reports: ['html', 'lcovonly', 'name-summary'],
fixWebpackSourcePaths: true
},
reporters: ['progress', 'kjhtml'],

View File

@ -1,52 +1,62 @@
{
"name": "framadate-funky-frontend",
"version": "0.1.0",
"scripts": {
"ng": "ng",
"start": "ng serve",
"build": "ng build",
"test": "ng test",
"lint": "ng lint",
"e2e": "ng e2e",
"trans": "ng xi18n --output-path=src/locale --i18n-locale=fr"
},
"private": true,
"dependencies": {
"@angular/animations": "~8.2.0",
"@angular/common": "~8.2.0",
"@angular/compiler": "~8.2.0",
"@angular/core": "~8.2.0",
"@angular/forms": "~8.2.0",
"@angular/platform-browser": "~8.2.0",
"@angular/platform-browser-dynamic": "~8.2.0",
"@angular/router": "~8.2.0",
"@ngx-translate/core": "^11.0.1",
"@ngx-translate/http-loader": "^4.0.0",
"chart.js": "^2.8.0",
"ngx-markdown": "^8.2.1",
"rxjs": "~6.4.0",
"tslib": "^1.10.0",
"zone.js": "~0.9.1"
},
"devDependencies": {
"@angular-devkit/build-angular": "^0.803.21",
"@angular/cli": "~8.3.21",
"@angular/compiler-cli": "^8.2.14",
"@angular/language-service": "^8.2.14",
"@types/jasmine": "~3.3.8",
"@types/jasminewd2": "^2.0.8",
"@types/node": "~8.9.4",
"codelyzer": "^5.2.1",
"jasmine-core": "~3.4.0",
"jasmine-spec-reporter": "~4.2.1",
"karma": "~4.1.0",
"karma-chrome-launcher": "~2.2.0",
"karma-coverage-istanbul-reporter": "~2.0.1",
"karma-jasmine": "~2.0.1",
"karma-jasmine-html-reporter": "^1.5.1",
"protractor": "~5.4.0",
"ts-node": "~7.0.0",
"tslint": "~5.15.0",
"typescript": "~3.5.3"
}
"name": "framadate-funky-frontend",
"version": "0.1.0",
"scripts": {
"ng": "ng",
"start": "ng serve",
"serve": "ng serve",
"build": "ng build",
"test": "ng test",
"lint": "ng lint",
"e2e": "ng e2e",
"trans": "ng xi18n --output-path=src/locale --i18n-locale=fr"
},
"private": true,
"dependencies": {
"@angular/animations": "^8.2.14",
"@angular/cdk": "^8.2.3",
"@angular/common": "~8.2.0",
"@angular/compiler": "~8.2.0",
"@angular/core": "~8.2.0",
"@angular/forms": "~8.2.0",
"@angular/platform-browser": "~8.2.0",
"@angular/platform-browser-dynamic": "~8.2.0",
"@angular/router": "~8.2.0",
"@fullcalendar/core": "^4.3.1",
"@ngx-translate/core": "^11.0.1",
"@ngx-translate/http-loader": "^4.0.0",
"chart.js": "^2.8.0",
"font-awesome": "^4.7.0",
"ngx-clipboard": "^12.3.0",
"ngx-markdown": "^8.2.1",
"ngx-toaster": "^1.0.1",
"primeicons": "^2.0.0",
"primeng": "^9.0.0-rc.2",
"quill": "^1.3.7",
"rxjs": "~6.4.0",
"rxjs-compat": "^6.5.3",
"tslib": "^1.10.0",
"zone.js": "~0.9.1"
},
"devDependencies": {
"@angular-devkit/build-angular": "~0.803.21",
"@angular/cli": "~8.3.21",
"@angular/compiler-cli": "~8.2.14",
"@angular/language-service": "~8.2.14",
"@types/jasmine": "~3.3.8",
"@types/jasminewd2": "~2.0.8",
"@types/node": "~8.9.4",
"codelyzer": "^5.2.1",
"jasmine-core": "~3.4.0",
"jasmine-spec-reporter": "~4.2.1",
"karma": "~4.1.0",
"karma-chrome-launcher": "~2.2.0",
"karma-coverage-istanbul-reporter": "~2.0.1",
"karma-jasmine": "~2.0.1",
"karma-jasmine-html-reporter": "^1.5.1",
"protractor": "~5.4.0",
"ts-node": "~7.0.0",
"tslint": "~5.15.0",
"typescript": "~3.5.3"
}
}

View File

@ -18,16 +18,20 @@
<img
src="assets/img/icone-langue.svg"
alt="location icon"
class="lang_icon"
> <img
src="assets/img/icone-menu.svg"
alt="menu icon"
class="menu_icon"
>
(click)="changeLanguage()"
class="lang_icon clickable"
>
<img
(click)="toggleMenu()"
alt="menu icon"
class="menu_icon clickable"
src="assets/img/icone-menu.svg"
>
<select
name="language"
class="Language-"
(change)="switchLanguage($event.target.value)"
(change)="switchLanguage(currentLang)"
[(ngModel)]="currentLang"
>
<option
value="d"
@ -37,17 +41,16 @@
<option value="en">English</option>
<option value="fr">Français</option>
</select>
<span class="menu_label">Menu</span>
</div>
<h1 i18n>{{"Title"|translate}}</h1>
<div>
{{"Intro"|translate:user}}
<span (click)="toggleMenu()" class="menu_label">Menu</span>
</div>
</div>
</header>
<framadate-navigation [step]="step"></framadate-navigation>
<framadate-navigation *ngIf="menuVisible" [step]="step"></framadate-navigation>
<main>
<router-outlet></router-outlet>
</main>
<framadate-debugger></framadate-debugger>
<p-toast position="top-right"></p-toast>

View File

@ -8,6 +8,8 @@ import {Router} from '@angular/router';
styleUrls: ['./app.component.scss']
})
export class AppComponent {
currentLang = 'fr';
langsAvailable = ['fr', 'en'];
title = 'framadate';
//translation demo:
minutes = 12;
@ -15,11 +17,17 @@ export class AppComponent {
name: 'Arthur',
age: 42
};
menuVisible: boolean = true;
private step: string;
constructor(private translate: TranslateService,
private route: Router) {
this.translate.setDefaultLang('fr');
this.translate.setDefaultLang(this.currentLang);
this.detectCurrentTabOnRouteChange();
}
detectCurrentTabOnRouteChange() {
this.route.events.subscribe((event: any) => {
// console.log('event', event);
@ -39,6 +47,27 @@ export class AppComponent {
switchLanguage(language: string) {
this.translate.use(language);
this.currentLang = language;
}
/**
* set the next lang or loop to the first
* this is to manage future languages available
*/
changeLanguage() {
let langs = this.langsAvailable;
let indexofCurrent = langs.indexOf(this.currentLang);
if (indexofCurrent > -1) {
let newIndex = indexofCurrent + 1;
if (newIndex > (langs.length - 1)) {
newIndex = 0;
}
this.currentLang = this.langsAvailable[newIndex];
}
this.translate.use(this.currentLang);
}
toggleMenu() {
this.menuVisible = !this.menuVisible;
}
}

View File

@ -1,6 +1,5 @@
import {BrowserModule} from '@angular/platform-browser';
import {NgModule} from '@angular/core';
import {AppRoutingModule} from './app-routing.module';
import {AppComponent} from './app.component';
import {BaseComponent} from './pages/base-page/base.component';
@ -21,12 +20,12 @@ import {EndConfirmationComponent} from './pages/end-confirmation/end-confirmatio
import {CreateOrRetrieveComponent} from './pages/create-or-retrieve/create-or-retrieve.component';
import localeFr from '@angular/common/locales/fr';
import localeEn from '@angular/common/locales/en';
import {VotingSummaryComponent} from './pages/voting-summary/voting-summary.component';
import {VotingGraphComponent} from './pages/voting-graph/voting-graph.component';
import {VotingChoiceComponent} from './pages/voting-choice/voting-choice.component';
import {VotingSummaryComponent} from './pages/voting/voting-summary/voting-summary.component';
import {VotingGraphComponent} from './pages/voting/voting-graph/voting-graph.component';
import {VotingChoiceComponent} from './pages/voting/voting-choice/voting-choice.component';
import {PasswordComponent} from './pages/password/password.component';
import {HomeComponent} from './pages/home/home.component';
import {VoteChoiceComponent} from './vote-choice/vote-choice.component';
import {VoteChoiceComponent} from './ui/vote-choice/vote-choice.component';
import {HttpClient, HttpClientModule} from '@angular/common/http';
import {MarkdownModule} from 'ngx-markdown';
import {
@ -41,7 +40,16 @@ import {PollGraphicComponent} from './poll-graphic/poll-graphic.component';
import {AdminComponent} from './pages/admin/admin.component';
import {SelectorComponent} from './ui/selector/selector.component';
import { VotingComponent } from './pages/voting/voting.component';
import {BrowserAnimationsModule} from "@angular/platform-browser/animations";
import {ConfigService} from "./services/config.service";
import {PollService} from "./services/poll.service";
import {ToastModule} from 'primeng/toast';
import {MessageModule, MessageService} from "primeng";
import {PollDisplayComponent} from './pages/poll-display/poll-display.component';
import {VotingComponent} from './pages/voting/voting.component';
import {VotingCommentComponent} from './pages/voting/voting-comment/voting-comment.component';
import {ResettableInputDirective} from './ui/directives/resettable-input.directive';
import {ClipboardModule} from "ngx-clipboard";
export class MyMissingTranslationHandler implements MissingTranslationHandler {
handle(params: MissingTranslationHandlerParams) {
@ -80,13 +88,20 @@ export function HttpLoaderFactory(http: HttpClient) {
VoteChoiceComponent,
AdminComponent,
SelectorComponent,
PollDisplayComponent,
VotingComponent,
VotingCommentComponent,
ResettableInputDirective,
],
imports: [
ClipboardModule,
CommonModule,
BrowserModule,
BrowserAnimationsModule,
AppRoutingModule,
ToastModule,
MessageModule,
MarkdownModule.forRoot(),
TranslateModule.forRoot({
missingTranslationHandler: {
@ -104,7 +119,7 @@ export function HttpLoaderFactory(http: HttpClient) {
FormsModule,
RouterModule.forRoot(Routes)
],
providers: [TranslateService],
providers: [TranslateService, ConfigService, PollService, MessageService],
bootstrap: [AppComponent]
})
export class AppModule {

View File

@ -1,37 +0,0 @@
import {Injectable} from '@angular/core';
import {PollConfig} from './config/PollConfig';
import {HttpClient} from "@angular/common/http";
import {environment} from "../environments/environment";
/**
* le service transverse à chaque page qui permet de syncroniser la configuration de sondage souhaitée
*/
@Injectable({
providedIn: 'root'
})
export class ConfigService extends PollConfig {
myEmail: string;
myPolls: any;// list of retrieved polls from the backend api
constructor(public http: HttpClient) {
super();
}
set(key, val) {
this[key] = val;
}
createPoll() {
// todo
console.log('sends the form');
alert('envoi de formulaire pour création de sondage en XHR à faire');
const payload = this;
this.http.post(`${environment.baseApiHref}/poll`, payload)
.subscribe(res => {
console.log('res', res)
},
err => console.error('err', err))
}
}

View File

@ -1,6 +1,8 @@
/**
* une option de date dans les sondages spéciaux
*/
import {environment} from "../../environments/environment";
export interface DateOption {
timeList: any;
literal: string;
@ -45,22 +47,29 @@ export const defaultAnswers = [{
*/
export class PollConfig {
expiracyDateDefaultInDays = 60;
deletionDateAfterLastModification = 180;
step = 0;
stepMax = 3;
pollType = 'classic';// classic or date
title = 'titre';
description = 'ma description';
myName = 'mon pseudo';
myEmail: string = "";
// date specific poll, we have the choice to setup different hours (timeList) for all possible dates (dateList), or use the same hours for all dates
allowSeveralHours = 'false';
// access
visibility = 'link_only'; // visible to anyone with the link:
voteChoices = 'only_yes'; // possible answers to a vote choice: only "yes", "yes, maybe, no"
creationDate = new Date();
expirationDate = ''; // expiracy date
pollId = null; // id of the current poll when created. data given by the backend api
selectedPoll = null; // current poll selected with createPoll or getPoll of ConfigService
passwordAccess = 0;
password = '';
customUrl = '';
customUrl = ''; // custom slug in the url, must be unique
customUrlIsUnique = null; // given by the backend
urlPublic = environment.baseApiHref + '/default-url';
urlAdmin = environment.baseApiHref + '/default-url/admin/d65es45fd45sdf45sd345f312sdf31sgfd345';
canModifyAnswers = 1;// everybody, self, nobody (= just admin)
whoModifiesAnswers = "self";// everybody, self, nobody (= just admin)
whoCanChangeAnswers = 'everybody';// everybody, self, nobody (= just admin)
@ -69,5 +78,6 @@ export class PollConfig {
answers: any = defaultAnswers;
constructor() {
}
}

View File

@ -10,15 +10,18 @@ import {CreateOrRetrieveComponent} from '../pages/create-or-retrieve/create-or-r
import {BaseComponent} from '../pages/base-page/base.component';
import {HomeComponent} from "../pages/home/home.component";
import {PollGraphicComponent} from '../poll-graphic/poll-graphic.component';
import {VoteChoiceComponent} from "../vote-choice/vote-choice.component";
import {VoteChoiceComponent} from "../ui/vote-choice/vote-choice.component";
import {PollDisplayComponent} from "../pages/poll-display/poll-display.component";
import {VotingComponent} from "../pages/voting/voting.component";
import {PasswordComponent} from "../pages/password/password.component";
/**
* each step in the form is a component
*/
export const Routes =
[
{path: '', component: CreateOrRetrieveComponent},
{path: '', redirectTo: 'step/creation', pathMatch: 'full'},
{path: 'home', component: HomeComponent},
{path: 'base', component: BaseComponent},
{path: 'step/base', component: BaseComponent},
@ -32,8 +35,10 @@ export const Routes =
{path: 'step/resume', component: ResumeComponent},
{path: 'step/end', component: EndConfirmationComponent},
{path: 'graphic/:poll', component: PollGraphicComponent},
{path: 'vote/poll/id/:poll', component: PollDisplayComponent},
{path: 'votechoice', component: VoteChoiceComponent},
{path: 'voting', component: VotingComponent},
{path: 'step/password', component: PasswordComponent},
{path: '**', redirectTo: '/home', pathMatch: 'full'},
]
;

View File

@ -0,0 +1,8 @@
export const mockComments = [
{
name: "Bulbizarre",
date: "23 décembre 2019",
text: "Pokem ipsum dolor sit amet Electric Cottonee Scratch Leech Life Ice Berry Ducklett. Leaf Green Durant Zoroark\n" +
" Skitty Rock Luxio Surskit. Glacier Badge",
}
];

View File

@ -0,0 +1,52 @@
export const mockGraphConfig = {
step: 0,
stepMax: 3,
pollType: "special dates",
title: "",
description: "",
myName: "",
visibility: "link_only",
// date specific poll
allowSeveralHours: "true",
dateLgfgfgfgist: ["jeudi", "vendredi", "samedi"], // sets of days as strings
timeList: ["08:00", "08:30", "09:00"], // ranges of time expressed as strings
answers: [
{
id: 0,
text: "no"
},
{
id: 1,
text: "yes"
},
{
id: 2,
text: "maybe"
},
{
id: 3,
text: "maybe"
},
{
id: 4,
text: "maybe"
},
{
id: 5,
text: "maybe"
},
{
id: 6,
text: "maybe"
},
{
id: 7,
text: "maybe"
},
{
id: 8,
text: "maybe"
}
]
};

View File

@ -0,0 +1,186 @@
export const mockPoll3 = {
"message": "your poll config",
"data": {
"id": 3,
"title": "dessin animé préféré",
"customUrl": null,
"description": "choisissez votre animé préféré",
"creationDate": {
"date": "2020-01-15 15:07:53.000000",
"timezone_type": 3,
"timezone": "Europe/Paris"
},
"expiracyDate": {
"date": "2020-01-15 15:07:53.000000",
"timezone_type": 3,
"timezone": "Europe/Paris"
},
"owner": {
"__initializer__": {},
"__cloner__": {},
"__isInitialized__": false
},
"kind": "text",
"allowedAnswers": [
"yes"
],
"modificationPolicy": "self",
"mailOnComment": null,
"mailOnVote": null,
"hideResults": null,
"showResultEvenIfPasswords": null,
"votes": {},
"stacksOfVotes": {},
"choices": {},
"comments": {},
"defaultExpiracyDaysFromNow": 60
},
"stacks_count": 2,
"stacks": [
{
"pseudo": "Wulfila",
"votes": {}
},
{
"pseudo": "Tykayn",
"votes": {}
}
],
"choices_count": 7,
"choices": [
{
"id": 5,
"name": "Vic le viking",
"dateTime": {
"date": "2020-01-15 15:07:53.000000",
"timezone_type": 3,
"timezone": "Europe/Paris"
},
"votes": {
"count": {
"yes": 0,
"no": 0,
"maybe": 0,
"null": 0,
}
},
simpleAnswer: true,
"answer": null
},
{
"id": 6,
"name": "Boumbo petite automobile",
"dateTime": {
"date": "2020-01-15 15:07:53.000000",
"timezone_type": 3,
"timezone": "Europe/Paris"
},
"votes": {
"count": {
"yes": 0,
"no": 0,
"maybe": 0,
"null": 0,
}
},
simpleAnswer: true,
"answer": null
},
{
"id": 7,
"name": "Les mystérieuses cités d'or",
"dateTime": {
"date": "2020-01-15 15:07:53.000000",
"timezone_type": 3,
"timezone": "Europe/Paris"
},
"votes": {
"count": {
"yes": 0,
"no": 0,
"maybe": 0,
"null": 0,
}
},
simpleAnswer: true,
"answer": null
},
{
"id": 8,
"name": "Les mondes engloutis",
"dateTime": {
"date": "2020-01-15 15:07:53.000000",
"timezone_type": 3,
"timezone": "Europe/Paris"
},
"votes": {
"count": {
"yes": 0,
"no": 0,
"maybe": 0,
"null": 0,
}
},
simpleAnswer: true,
"answer": null
},
{
"id": 9,
"name": "Foot 2 rue",
"dateTime": {
"date": "2020-01-15 15:07:53.000000",
"timezone_type": 3,
"timezone": "Europe/Paris"
},
"votes": {
"count": {
"yes": 0,
"no": 0,
"maybe": 0,
"null": 0,
}
},
simpleAnswer: true,
"answer": null
},
{
"id": 10,
"name": "Le chat, la vache, et l'océan",
"dateTime": {
"date": "2020-01-15 15:07:53.000000",
"timezone_type": 3,
"timezone": "Europe/Paris"
},
"votes": {
"count": {
"yes": 0,
"no": 0,
"maybe": 0,
"null": 0,
}
},
simpleAnswer: true,
"answer": null
},
{
"id": 11,
"name": "Digimon",
"dateTime": {
"date": "2020-01-15 15:07:53.000000",
"timezone_type": 3,
"timezone": "Europe/Paris"
},
"votes": {
"count": {
"yes": 0,
"no": 0,
"maybe": 0,
"null": 0,
}
},
simpleAnswer: true,
"answer": null
}
],
"comments": []
}

View File

@ -17,52 +17,32 @@
<li>
type de formulaire: {{config.pollType}}
</li>
<li>
config:
<pre>
{{config.answers|json}}
</pre>
</li>
</ul>
<button
class="btn btn--primary"
i18n
(click)="config.createPoll()"
>
Envoyer le formulaire
</button>
<button
class="btn btn--primary"
i18n
(click)="config.getPollById(1, 'example password')"
>
get poll 1
</button>
<button
class="btn btn--primary"
i18n
(click)="config.getMyPolls( 'tktest@tktest.com')"
>
get my polls
</button>
<button class="btn btn--success" (click)="launchToast()">
launch success toast
</button>
<a [routerLink]="'/vote/poll/id/3'" class="btn btn--success">
See example of vote page
</a>
</div>
<span i18n>
Choix cornélien syncronisé:
</span>
<!-- todo: factoriser les boutons-->
<button
(click)="config.set('pollType' , 'classic')"
[class.active]="config.pollType == 'classic'"
[disabled]="!formIsValid"
class="btn btn--primary next"
>
<span i18n>
sondage classique
</span>
<span *ngIf="config.pollType == 'classic'">
[x]
</span>
</button>
<button
(click)="selectOption('pollType' ,'dates')"
[class.active]="config.pollType == 'dates'"
[disabled]="!formIsValid"
class="btn btn--primary next"
>
<span i18n>
sondage spécial date
</span>
<span *ngIf="config.pollType == 'dates'">
[x]
</span>
</button>
<button
class="btn btn--primary"
i18n
(click)="config.createPoll()"
>
Envoyer le formulaire
</button>

View File

@ -1,5 +1,5 @@
import {Component, OnInit} from '@angular/core';
import {ConfigService} from '../config.service';
import {ConfigService} from '../services/config.service';
@Component({
selector: 'framadate-debugger',
@ -23,4 +23,8 @@ export class DebuggerComponent implements OnInit {
return true;
}
launchToast() {
this.config.handleError({message: "hop"})
}
}

View File

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

View File

@ -11,7 +11,7 @@
<li #answers *ngFor="let answer of config.answers; index as i;trackBy trackFunction"
class="answer-item">
<input
type="text"
type="name"
class="answer"
[(ngModel)]="answer.text"
(keyup.enter)="addAnswer()"

View File

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

View File

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

View File

@ -1,39 +1,63 @@
<div class="container">
<section class="creation">
<h1>
{{"creation.title"|translate}}
</h1>
<p class="description margin-btm-x5" i18n>
{{"config.description"|translate}}
</p>
<div class="btn-next">
<section class="creation">
<h1>
{{"creation.title"|translate}}
</h1>
<p class="description margin-btm-x5" i18n>
{{"config.description"|translate}}
</p>
<div class="btn-next">
<button class="btn btn--full btn--primary">{{"config.letsgo"|translate}}</button>
<button [routerLink]="'step/date'" class="btn btn--full btn--primary">
{{"config.letsgo"|translate}}
</button>
</div>
</section>
<section class="recuperation">
<h1 class="margin-top-x8">
{{"config.find_my_polls"|translate}}
</h1>
<section class="list-my-polls">
<!--<ul class="poll-list" *ngFor="let poll of config.myPolls">-->
<a href="#" class="next margin-btm-x3">Anniversaire de tonton Patrick</a>
<a href="#" class="next margin-btm-x5">Anniversaire de Bernard </a>
<!--</ul>-->
</div>
</section>
<form (ngSubmit)="findMyPollsByEmail(emailToFind.value)">
<section class="row">
<label class="description" for="email" i18n>
{{"config.find_helper"|translate}} :
</label>
<input class="margin-btm-x5" #emailToFind type="email" name="mail" id="email" autofocus="autofocus">
</section>
<section class="recuperation">
<h1 class="margin-top-x8">
{{"config.find_my_polls"|translate}}
</h1>
<form
(ngSubmit)="findMyPollsByEmail(config.myEmail)"
>
<label
class="description"
for="email"
i18n
>
{{"config.find_helper"|translate}} :
</label>
<input
[(ngModel)]="config.myEmail"
autofocus="autofocus"
id="email"
name="mail"
required="required"
type="email"
/>
<input
[disabled]="!config.myEmail"
[ngClass]="{'btn-primary': config.myEmail}"
class="btn btn-block"
i18n-value="'config.find_button'|translate"
type="submit"
/>
</form>
</section>
<section class="list-my-polls" *ngIf="!config.loading">
<ul class="poll-list" *ngFor="let poll of config.myPolls">
<li> poll</li>
</ul>
<div class="no-polls" *ngIf="! config.myPolls || !config.myPolls.length">
Aucun sondage.
</div>
</section>
<div class="loading" *ngIf="config.loading">
<i class="fa fa-refresh fa-spin fa-3x fa-fw"></i>
</div>
<input type="submit" class="btn btn--primary btn--default btn--purple btn--black-text" value='{{"config.find_button"|translate}}'>
</form>
</section>
</div>

View File

@ -1,7 +1,6 @@
import {Component, OnInit} from '@angular/core';
import {BaseComponent} from "../base-page/base.component";
import {ConfigService} from "../../config.service";
import {PollServiceService} from "../../services/poll-service.service";
import {ConfigService} from "../../services/config.service";
@Component({
selector: 'framadate-create-or-retrieve',
@ -10,15 +9,25 @@ import {PollServiceService} from "../../services/poll-service.service";
})
export class CreateOrRetrieveComponent extends BaseComponent implements OnInit {
constructor(public config: ConfigService, public pollService: PollServiceService) {
loadedMyPolls: boolean = false;
constructor(public config: ConfigService) {
super(config);
}
ngOnInit() {
// if (!environment.production) {
// this.findMyPollsByEmail('tktest@tktest.com')
// }
}
findMyPollsByEmail(email: string) {
this.pollService.findPollsByEmail(email);
if (!email) {
return
}
this.config.findPollsByEmail(email);
this.loadedMyPolls = true;
}
}

View File

@ -89,7 +89,7 @@
<input
[(ngModel)]="choice.literal"
name="timeChoices_{{id}}"
type="text"
type="name"
>
<button (click)="config.timeList.splice(id, 1)" class="btn btn-warning">X</button>
</div>
@ -133,7 +133,7 @@
<input
[(ngModel)]="time.literal"
name="dateTime_{{id}}_Choices_{{idTime}}"
type="text"
type="name"
>
<button
(click)="choice.timeList.splice(idTime, 1)"

View File

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

View File

@ -7,27 +7,35 @@
Votre sondage « 
<span class="poll-title">
{{config.title}} </span>
{{config.title}}
</span>
 » a bien été créé !
</p>
<p>
Voici les liens daccès au sondage, conservez-les soigneusement !
(Si vous les perdez vous pourrez toujours les recevoir par email)
</p>
<p>
Pour accéder au sondage et à tous ses paramètres : https://framadate.org/urladmindusondage
Pour accéder au sondage et à tous ses paramètres :
<a href="{{config.urlAdmin}}">{{config.urlAdmin}}
</a>
</p>
<button
class="btn"
(click)="copyLink(adminLink)"
(click)="copyLink(config.urlAdmin)"
[ngxClipboard]
cbContent="{{config.urlAdmin}}"
>
<i class="fa fa-copy"></i>
Copier le lien
</button>
<a href="{{adminLink}}">
<a href="{{config.urlAdmin}}">
Voir le sondage coté administrateur·ice
</a>
<p class="note">
Note : Le sondage sera supprimé 180 jours après la date de sa dernière modification.
Note : Le sondage sera supprimé {{config.deletionDateAfterLastModification}} jours après la date de sa dernière
modification.
</p>
@ -36,15 +44,21 @@
<h2 i18n>{{"resume.users"|translate}}</h2>
<p>
Pour accéder au sondage : https://framadate.org/urladmindusondage
Pour accéder au sondage :
<a href="{{config.urlPublic}}">{{config.urlPublic}}
</a>
</p>
<button
class="btn"
(click)="copyLink(publicLink)"
(click)="copyLink(config.urlPublic)"
[ngxClipboard]
cbContent="{{config.urlPublic}}"
>
<i class="fa fa-copy"></i>
Copier le lien
</button>
<a href="{{publicLink}}">
<a href="{{config.urlPublic}}">
Voir le sondage
</a>
</section>
@ -60,16 +74,19 @@
[(ngModel)]="mailToRecieve"
paceholder="email"
>
</p>
<button
class="btn"
(click)="sendToEmail()"
>
><i class="fa fa-paper-plane"></i>
Envoyer les liens du sondage
</button>
<a href="{{publicLink}}">
<a href="{{config.urlPublic}}">
Voir le sondage
</a>
</section>
<p-toast position="bottom-right"></p-toast>

View File

@ -0,0 +1,5 @@
:host {
button {
margin-right: 1em;
}
}

View File

@ -1,7 +1,8 @@
import {Component, OnInit} from '@angular/core';
import {BaseComponent} from '../base-page/base.component';
import {ConfigService} from '../../config.service';
import {HttpClient} from '@angular/common/http';
import {ConfigService} from "../../services/config.service";
import {MessageService} from 'primeng/api';
@Component({
selector: 'framadate-end-confirmation',
@ -9,19 +10,25 @@ import {HttpClient} from '@angular/common/http';
styleUrls: ['./end-confirmation.component.scss']
})
export class EndConfirmationComponent extends BaseComponent implements OnInit {
public adminLink = '';
public publicLink = '';
mailToRecieve = '';
constructor(public config: ConfigService, public http: HttpClient) {
constructor(public config: ConfigService,
public http: HttpClient,
private messageService: MessageService
) {
super(config);
this.mailToRecieve = this.config.myEmail;
}
ngOnInit() {
}
copyLink(adminLink: any) {
copyLink(str: any) {
this.messageService.add({
severity: 'success',
summary: 'Lien copié',
detail: str
});
}
sendToEmail() {

View File

@ -1,79 +1,92 @@
<div class="description">
<router-outlet></router-outlet>
<h1 i18n>
{{"creation.title"|translate}}
</h1>
<span
class="pre-selector"
i18n
>
<router-outlet></router-outlet>
<h1 i18n>
{{"creation.title"|translate}}
</h1>
<span
class="pre-selector"
i18n
>
{{"creation.want"|translate}}
</span>
<select
id="selector"
name="selector"
autofocus="autofocus"
[(ngModel)]="config.pollType"
>
<option value="dates">
{{"creation.kind.date"|translate}}
</option>
<option value="classic">
{{"creation.kind.classic"|translate}}
</option>
</select>
<select
[(ngModel)]="config.pollType"
autofocus="autofocus"
id="selector"
name="selector"
>
<option value="dates">
{{"creation.kind.date"|translate}}
</option>
<option value="classic">
{{"creation.kind.classic"|translate}}
</option>
</select>
<span class="post-selector">
<span class="post-selector">
</span>
<div>
<div>
<label
for="poll_title"
class="title-label"
i18n
>
{{"creation.choose_title"|translate}}
</label>
<input
type="text"
id="poll_title"
name="poll_title"
placeholder="{{'creation.choose_title_placeholder'|translate}}"
[(ngModel)]="config.title"
>
</div>
<label
class="title-label"
for="poll_title"
i18n
>
{{"creation.choose_title"|translate}}
</label>
<input
[(ngModel)]="config.title"
id="poll_title"
name="poll_title"
placeholder="{{'creation.choose_title_placeholder'|translate}}"
type="name"
>
</div>
<div>
<label for="my_name">
{{"creation.name"|translate}} :
</label>
<input
type="text"
name="my_name"
id="my_name"
placeholder="{{'creation.name_placeholder'|translate}}"
[(ngModel)]="config.myName"
>
</div>
<div>
<label for="my_name">
{{"creation.name"|translate}} :
</label>
<input
[(ngModel)]="config.myName"
id="my_name"
name="my_name"
placeholder="{{'creation.name_placeholder'|translate}}"
type="name"
>
</div>
<div>
<label
for="poll_description"
class="title-label"
i18n
>
{{"creation.description"|translate}}
</label>
<textarea
id="poll_description"
name="poll_description"
[(ngModel)]="config.description"
placeholder="description"
cols="50"
lines="5"
></textarea>
</div>
<div>
<label
class="title-label"
for="poll_description"
i18n
>
{{"creation.description"|translate}}
</label>
<textarea
[(ngModel)]="config.description"
cols="50"
id="poll_description"
lines="5"
name="poll_description"
placeholder="description"
></textarea>
</div>
<div>
<label for="email">
Mon email pour administrer le sondage est
</label>
<input
[(ngModel)]="config.myEmail"
autofocus="autofocus"
id="email"
name="mail"
required="required"
type="email"
/>
</div>
<button
[routerLink]="'/step/answers'"
@ -88,6 +101,6 @@
i18n
>
Retour
</a>
<hr>
</a>
<hr>
</div>

View File

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

View File

@ -104,9 +104,9 @@
<label for="">Un label pour les labelliser tous</label>
<h3>Input text</h3>
<input type="text" name="" id=""><br>
<input type="text" name="" id="" value="texte">
<h3>Input name</h3>
<input type="name" name="" id=""><br>
<input type="name" name="" id="" value="texte">
<h3>Input email</h3>
<input type="email" name="" id=""><br>
@ -198,9 +198,9 @@
</article>
<article>
<h2>Label + input text</h2>
<h2>Label + input name</h2>
<label for="test-text">Ceci est un label un peu long mais pas trop</label>
<input type="text" name="test-text" id="test-text">
<input type="name" name="test-text" id="test-text">
</article>
<article>
@ -220,7 +220,7 @@
</article>
<article>
<h2>Input text with info</h2>
<h2>Input name with info</h2>
<a href="https://sketch.cloud/s/00A80/a/MAl5q7">like here</a>
</article>

View File

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

View File

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

View File

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

View File

@ -0,0 +1,47 @@
<div class="poll">
<h1>{{pollConfigFetched.data.title}}</h1>
<p>{{pollConfigFetched.data.description}}</p>
<p class="creationDate">{{pollConfigFetched.data.creationDate.date}}</p>
<p class="expiracyDate">{{pollConfigFetched.data.creationDate.date}}</p>
<p class="votants">
{{pollConfigFetched.stacks_count}} votants,
{{pollConfigFetched.choices_count}} choix,
</p>
</div>
<section class="row name">
<label for="name">Votre nom :</label>
<input type="text" name="name" id="name" [(ngModel)]="config.myName">
</section>
<div class="list-of-choices">
<div *ngFor="let choice of pollConfigFetched.choices">
<framadate-vote-choice [choice]="choice"></framadate-vote-choice>
</div>
</div>
<framadate-voting-graph></framadate-voting-graph>
<framadate-voting-summary></framadate-voting-summary>
<div class="comments">
<h2 class="margin-top-x7">Laisser un commentaire</h2>
<label for="crname">Votre nom :</label>
<input type="text" class="margin-btm-x3" name="crname" id="crname">
<div>
<label for="comment">Votre commentaire :</label>
<textarea name="comment" id="comment">
</textarea>
</div>
<input type="submit" name="add-comment" class="btn btn--primary btn--outline" value="Ajouter mon commentaire">
<framadate-voting-comment [comment]="c" *ngFor="let c of comments"></framadate-voting-comment>
</div>
<div class="sharing">
<h3 class="margin-top-x8">Partager le sondage</h3>
<label class="nobold text-14" for="copyLink">Pour partager le sondage, vous pouvez diffuser ce lien :
<code>
{{config.urlAdmin}}
</code>
</label>
<input type="submit" name="copy-link" class=" btn btn--primary btn--outline" value="Copier le lien" id="copyLink">
<h3 class="margin-top-x6 margin-btm-x3">Exporter/Imprimer</h3>
<input type="submit" name="export" class="margin-btm-x3 btn btn--primary btn--outline" value="Exporter en .csv" (click)="exportCSV()">
<input type="submit" name="copy-link" class="btn btn--primary btn--outline" value="Imprimer le sondage">
</div>

View File

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

View File

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

View File

@ -0,0 +1,29 @@
import {Component, OnInit} from '@angular/core';
import {BaseComponent} from "../base-page/base.component";
import {ConfigService} from "../../services/config.service";
import {mockPoll3} from "../../config/mock-poll3";
import {mockComments} from "../../config/mock-comments";
@Component({
selector: 'framadate-poll-display',
templateUrl: './poll-display.component.html',
styleUrls: ['./poll-display.component.scss']
})
export class PollDisplayComponent extends BaseComponent implements OnInit {
private pollConfigFetched = mockPoll3;
private comments = mockComments;
constructor(public config: ConfigService) {
super(config);
}
ngOnInit() {
// fetch poll with its ID or slug.
// store it in the poll property here
}
exportCSV() {
// TODO
}
}

View File

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

View File

@ -103,7 +103,7 @@
{{"visibility.access_url_key"|translate}}
</label>
<br>
<input type="text"
<input type="name"
class="input-lg"
name="url"
id="url"
@ -112,6 +112,9 @@
<sub class="instructions">
{{"visibility.access_instructions"|translate}}
</sub>
<div class="preview-url">
{{baseUrl + '/' + config.customUrl}}
</div>
<br>
<label for="passwordAccess">
{{"visibility.access_want"|translate}}
@ -133,13 +136,15 @@
min="8"
*ngIf="!showCustomPassword"
[(ngModel)]="config.password">
<input type="text"
<input type="name"
name="password_visible"
id="password_visible"
min="8"
*ngIf="showCustomPassword"
[(ngModel)]="config.password">
<button class="btn btn--default" (click)="showCustomPassword = !showCustomPassword">
<button (click)="showCustomPassword = !showCustomPassword" [disabled]="!config.password"
class="btn btn--default">
<i class="fa fa-eye"></i>
{{"visibility.see_pass"|translate}}
</button>
</div>

View File

@ -1,6 +1,7 @@
import {Component, OnInit} from '@angular/core';
import {BaseComponent} from '../base-page/base.component';
import {ConfigService} from '../../config.service';
import {ConfigService} from '../../services/config.service';
import {environment} from "../../../environments/environment";
@Component({
selector: 'framadate-visibility',
@ -9,35 +10,16 @@ import {ConfigService} from '../../config.service';
})
export class VisibilityComponent extends BaseComponent implements OnInit {
showCustomPassword = false;
baseUrl = environment.baseApiHref;
constructor(public config: ConfigService) {
super(config);
}
ngOnInit() {
this.config.customUrl = this.makeUuid();
this.config.expirationDate = (this.addDaysToDate(this.config.expiracyDateDefaultInDays, new Date())).toISOString().substring(0, 10);
this.config.customUrl = this.config.makeSlug();
this.config.expirationDate = (this.config.addDaysToDate(this.config.expiracyDateDefaultInDays, new Date())).toISOString().substring(0, 10);
}
/**
* generate unique id to have a default url for future poll
*/
makeUuid() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
}
/**
* add some days to a date, to compute intervals
* @param days
* @param date
*/
addDaysToDate(days: number, date: Date) {
date = new Date(date.valueOf());
date.setDate(date.getDate() + days);
return date;
};
}

View File

@ -1 +0,0 @@
<p>voting-graph works!</p>

View File

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

View File

@ -0,0 +1,7 @@
<div class="comment">
<span class="cname">{{comment.name}} </span>, le
<span class="date padding-btm-x1">{{comment.date}}</span>
<p class="text">
{{comment.text}}
</p>
</div>

View File

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

View File

@ -0,0 +1,19 @@
import {Component, Input, OnInit} from '@angular/core';
import {mockComments} from "../../../config/mock-comments";
@Component({
selector: 'framadate-voting-comment',
templateUrl: './voting-comment.component.html',
styleUrls: ['./voting-comment.component.scss']
})
export class VotingCommentComponent implements OnInit {
@Input() private comment = mockComments[0];
constructor() {
}
ngOnInit() {
}
}

View File

@ -0,0 +1,2 @@
<h1>Graphique</h1>
<framadate-poll-graphic></framadate-poll-graphic>

View File

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

View File

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

View File

@ -1,62 +1,28 @@
<form>
<form>
<h1 >Nom du sondage</h1>
<p>crée par piou</p>
<p>Pokem ipsum dolor sit amet Electric Cottonee Scratch Leech Life Ice Berry Ducklett. Leaf Green Durant Zoroark Skitty Rock Luxio Surskit. Glacier Badge Fuchsia City Team Rocket Ferroseed Cranidos Exeggcute Entei. Dig Thunder Badge Exeggcute Mightyena Milotic Mantyke Drapion. Hydro Pump Silver Treecko Missingno Growlithe Wingull Registeel. </p>
<section class="row name">
<label for="name">Votre nom :</label>
<input type="text" name="name">
</section>
<framadate-vote-choice></framadate-vote-choice>
<framadate-vote-choice></framadate-vote-choice>
<framadate-vote-choice></framadate-vote-choice>
<framadate-vote-choice></framadate-vote-choice>
<framadate-vote-choice class=" margin-btm-x2"></framadate-vote-choice>
<button class="btn btn--primary btn--full" ng-model="show_popup">Valider mes réponses</button>
<p class="prefered">Pour l'instant, le choix ayant reçu le plus grand nombre de votes est :
<li>- Vendredi 6 juillet
</li><li>
- Samedi 7 août
</li>
</p>
<a class="next">Voir le graphique</a>
<input type="submit" name="modify" class="btn btn--primary btn--outline btn--full"value="Je veux modifier le vote de quelqu'un">
<h2 class="margin-top-x4">Les commentaires</h2>
<p class="comment">
<span class="cname">Pikachu </span>
<span class="date date padding-btm-x1"> le 26 novembre 2019</span>
Rock Luxio Surskit. Glacier Badge
</p>
<p class="comment">
<span class="cname">Bulbizarre </span>
<span class="date padding-btm-x1">le 23 décembre 2019</span>
Pokem ipsum dolor sit amet Electric Cottonee Scratch Leech Life Ice Berry Ducklett. Leaf Green Durant Zoroark Skitty Rock Luxio Surskit. Glacier Badge
<a class="next">Voir le graphique</a>
<input type="submit" name="modify" class="btn btn--primary btn--outline btn--full"
value="Je veux modifier le vote de quelqu'un">
<h2 class="margin-top-x4">Les commentaires</h2>
<p class="comment">
<span class="cname">Pikachu </span>
<span class="date date padding-btm-x1"> le 26 novembre 2019</span>
Rock Luxio Surskit. Glacier Badge
</p>
<h2 class="margin-top-x7">Laisser un commentaire</h2>
<label for="crname">Votre nom :</label>
<input type="text" class="margin-btm-x3" name="crname">
<label for="comment">Votre commentaire :</label>
<textarea name="comment">
</textarea>
<input type="submit" name="add-comment" class="btn btn--primary btn--outline"value="Ajouter mon commentaire">
<h3 class="margin-top-x8">Partager le sondage</h3>
<label class="nobold text-14" for="copy-link">Pour partager le sondage, vous pouvez diffuser ce lien : https://framadate.org/urladmindusondage</label>
<input type="submit" name="copy-link" class=" btn btn--primary btn--outline"value="Copier le lien">
<h3 class="margin-top-x6 margin-btm-x3">Exporter/Imprimer</h3>
<input type="submit" name="export" class="margin-btm-x3 btn btn--primary btn--outline"value="Exporter en .csv">
<input type="submit" name="copy-link" class="btn btn--primary btn--outline"value="Imprimer le sondage">
</form>
<div ng-show="show_popup" class="popup">
<h4 class="margin-btm-x2">Participation validée !</h4>
<p class="margin-btm-x1">Votre vote a bien été pris en compte, mais faites attention : ce sondage n'autorise l'édition de votre vote qu'avec le lien personnalisé suivant :</p>
<p>https://framadate.org/urladmindusondage</p>
<button class="btn btn--primary btn--small btn--purple btn--black-text">Copier le lien</button>
<p class="margin-btm-x6 margin-top-x2">Conservez-le précieusement !</p>
<button class="btn btn--primary btn--default btn--purple btn--black-text">Revenir au sondage</button>
<p class="margin-btm-x1">Votre vote a bien été pris en compte, mais faites attention : ce sondage n'autorise
l'édition de votre vote qu'avec le lien personnalisé suivant :</p>
<p>https://framadate.org/urladmindusondage</p>
<button class="btn btn--primary btn--small btn--purple btn--black-text">Copier le lien</button>
<p class="margin-btm-x6 margin-top-x2">Conservez-le précieusement !</p>
<button class="btn btn--primary btn--default btn--purple btn--black-text">Revenir au sondage</button>
</div>

View File

@ -1,71 +0,0 @@
@import "../../../assets/scss/variables";
form{
display:flex;
margin:auto;
flex-direction: column;
align-items: center;
justify-content: flex-start;
}
.popup{
display:flex;
flex-direction: column;
z-index:2222222222;
}
.row{
flex-direction: row;
}
label{
font-weight: 600;
font-size: 18px;
}
.comment,.prefered{
border-left:6px solid $ugly-purple ;
padding-left: 5px;
margin-top:25px;
margin-bottom: 25px;
flex-wrap: wrap;
}
.comment{
display:flex;
}
.commeny span{
flex-direction: row;
}
.cname{
padding-left:17px;
font-weight: bold;
}
.btn{
max-width: 300px;
}
.next{
max-width:200px;
}
input[type=text],textarea{
max-width:350px;
}
li{
list-style-type: none;
}
h2{
}
.next{
align-self: flex-end;
margin-bottom:50px;
}
textarea{
height:213px;
margin-bottom:20px;
}
.name{
margin-bottom:50px;
margin-top:50px;
}
label{
align-items: flex-start;
}
.nobold{
font-weight: normal;
}

View File

@ -4,6 +4,7 @@
autofocus="autofocus"
[(ngModel)]="isColorblind"
(change)="toggleColorblind()"
class="input"
>
<option value="true">
{{ "pollGraphic.choiceColorblind" | translate }}
@ -12,8 +13,14 @@
{{ "pollGraphic.choiceNotColorblind" | translate }}
</option>
</select>
<span class="colorblind">
{{ "pollGraphic.colorblindText" | translate }}
</span>
<div>
<canvas id="graph" width="100" height="50"></canvas>
</div>
<div class="preferred">
Pour l'instant, le choix ayant reçu le plus grand nombre de votes est :
{{preferred}}
</div>

View File

@ -1,5 +1,7 @@
import {Component, OnInit} from "@angular/core";
import {Component, Inject, OnInit} from "@angular/core";
import {Chart} from "chart.js";
import {DOCUMENT} from '@angular/common';
import {mockGraphConfig} from "../config/mock-graph";
@Component({
selector: "framadate-poll-graphic",
@ -8,75 +10,24 @@ import {Chart} from "chart.js";
})
export class PollGraphicComponent implements OnInit {
isColorblind: boolean = false;
lineChart: Chart;
pollData: any;
preferred: any = "rien";
yesList: number[] = [];
maybeList: number[] = [];
noList: number[] = [];
nbPoll: number = 0;
dateList: string[] = [];
constructor() {
constructor(@Inject(DOCUMENT) private document: any,) {
}
ngOnInit() {
var toto = {
step: 0,
stepMax: 3,
pollType: "special dates",
title: "",
description: "",
myName: "",
visibility: "link_only",
// date specific poll
allowSeveralHours: "true",
dateLgfgfgfgist: ["jeudi", "vendredi", "samedi"], // sets of days as strings
timeList: ["08:00", "08:30", "09:00"], // ranges of time expressed as strings
answers: [
{
id: 0,
text: "no"
},
{
id: 1,
text: "yes"
},
{
id: 2,
text: "maybe"
},
{
id: 3,
text: "maybe"
},
{
id: 4,
text: "maybe"
},
{
id: 5,
text: "maybe"
},
{
id: 6,
text: "maybe"
},
{
id: 7,
text: "maybe"
},
{
id: 8,
text: "maybe"
}
]
};
var toto = mockGraphConfig;
this.formatDataAnswers(toto);
this.isColorblind = false;
this.pollData = new Chart(document.getElementById("graph"), {
this.pollData = new Chart(this.document.getElementById("graph"), {
type: "horizontalBar",
data: {
labels: ["jeudi"],

View File

@ -0,0 +1,377 @@
import {Injectable} from '@angular/core';
import {PollConfig} from '../config/PollConfig';
import {HttpClient, HttpHeaders} from "@angular/common/http";
import {environment} from "../../environments/environment";
import {MessageService} from 'primeng/api';
/**
* le service transverse à chaque page qui permet de syncroniser la configuration de sondage souhaitée
*/
@Injectable({
providedIn: 'root'
})
export class ConfigService extends PollConfig {
loading: boolean = false;
baseHref: any = environment.baseApiHref;
myPolls: any;// list of retrieved polls from the backend api
constructor(private http: HttpClient,
private messageService: MessageService) {
super();
}
set(key, val) {
this[key] = val;
}
clear() {
this.messageService.clear();
}
/**
* generate unique id to have a default url for future poll
*/
makeUuid() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
}
makeSlug(str?: string) {
if (!str) {
str = this.creationDate.getFullYear() + '_' + (this.creationDate.getMonth() + 1) + '_' + this.creationDate.getDate() + '_' + this.myName + '_' + this.title;
}
str = str.replace(/^\s+|\s+$/g, ''); // trim
str = str.toLowerCase();
// remove accents, swap ñ for n, etc
var from = "àáäâèéëêìíïîòóöôùúüûñç·/_,:;";
var to = "aaaaeeeeiiiioooouuuunc------";
for (var i = 0, l = from.length; i < l; i++) {
str = str.replace(new RegExp(from.charAt(i), 'g'), to.charAt(i));
}
str = str.replace(/[^a-z0-9 -]/g, '') // remove invalid chars
.replace(/\s+/g, '-') // collapse whitespace and replace by -
.replace(/-+/g, '-'); // collapse dashes
return str;
}
/**
* add some days to a date, to compute intervals
* @param days
* @param date
*/
addDaysToDate(days: number, date: Date) {
date = new Date(date.valueOf());
date.setDate(date.getDate() + days);
return date;
};
/** ==================================
*
* poll public calls to get non authenticated info
*
* ==================================/
/**
* convert current poll config to a payload to send to the backend API
*/
getPollConfig() {
const jsonConfig = {
method: 'POST',
data: {
owner: {
email: this.myEmail,
pseudo: this.myName,
},
title: this.title,
description: this.description,
type: this.pollType,
visibility: this.visibility,
voteChoices: this.voteChoices,
allowSeveralHours: this.allowSeveralHours,
expirationDate: this.expirationDate,
passwordAccess: this.passwordAccess,
password: this.password,
customUrl: this.customUrl,
canModifyAnswers: this.canModifyAnswers,
whoModifiesAnswers: this.whoModifiesAnswers,
dateList: this.dateList,
timeList: this.timeList,
answers: this.answers,
}
};
console.log('jsonConfig', jsonConfig);
return jsonConfig
}
makeHeaders(bodyContent?: any) {
const headerDict = {
'Charset': 'UTF-8',
'Content-Type': 'application/json',
'Accept': 'application/json',
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE',
'Access-Control-Allow-Origin': '*'
};
const requestOptions = {
headers: new HttpHeaders(headerDict),
body: bodyContent
};
return requestOptions;
}
checkIfSlugIsUniqueInDatabase(slug: string) {
this.customUrlIsUnique = null;
this.loading = true;
this.http.get(`${this.baseHref}/check-slug-is-uniq`,
this.makeHeaders({slug: this.customUrl}),
)
.subscribe((res: any) => {
this.customUrlIsUnique = res.data.isUnique;
this.loading = false;
},
(e) => this.handleError(e))
;
}
/**
* search in localstorage, fallback asking the backend to send an email to the owner if it exists
* @param email
*/
findPollsByEmail(email: string) {
this.findLocalStorageData();
// If no key is found in the localstorage, ask the backend to send an email to the user
this.myEmail = email;
this.loading = true;
this.http.get(`${this.baseHref}/my-polls`,
this.makeHeaders({email: this.myEmail}),
)
.subscribe(res => {
// message: 'Trouvé! Allez voir votre boite email',
this.myPolls = res;
console.log('res', res);
this.loading = false;
this.messageService.add({
severity: 'success',
summary: 'Service Message',
detail: 'Via MessageService'
});
}, (e) => {
this.handleError(e)
}
)
}
/**
* display error message depending on the response of the backend
* @param err
*/
handleError(err: any) {
// TODO handle a toast message
console.error('err', err);
this.loading = false;
this.messageService.add({severity: 'warning', summary: "Erreur lors de l'appel ", detail: err.message});
}
findLocalStorageData() {
// TODO check if the person has a key to retrieve her polls
}
/**
* get one poll by its slug name
* @param url
*/
getPollByURL(url: string) {
this.http.get(`${this.baseHref}/poll/${url}`, this.makeHeaders()).subscribe(
(res: any) => {
this.myPolls = res.data;
}, (e) => {
this.handleError(e)
}
);
}
/**
* GET
* api/v1/poll/{id}
* @param id
*/
getPollById(id: string, password: string) {
this.http
.get(`${this.baseHref}/poll/${id}`,
this.makeHeaders({body: password}))
.subscribe(
(res: any) => {
this.myPolls = res.data;
}, (e) => {
this.handleError(e)
}
);
}
/**
* GET
* api/v1/my-polls
* @param ownerEmail
*/
getMyPolls(ownerEmail: string) {
this.http
.get(`${this.baseHref}/my-polls`,
this.makeHeaders({ownerEmail: ownerEmail})
)
.subscribe(
(res: any) => {
this.myPolls = res.data;
}, (e) => {
this.handleError(e)
}
);
}
/**
* action of the form
*/
createPoll() {
console.log('sends the form');
// alert('envoi de formulaire pour création de sondage en XHR à faire');
this.http.get(`${this.baseHref}/`, this.makeHeaders())
.subscribe((res) => {
console.log('res', res);
this.createPollFromConfig(this.getPollConfig())
},
this.handleError
)
;
}
/**
* POST
* /api/v1/poll/{id}/poll
* @param config
*/
createPollFromConfig(config: any) {
this.http.post(`${this.baseHref}/poll`, this.makeHeaders({config: config}))
.subscribe((res: any) => {
// redirect to the page to administrate the new poll
this.messageService.add({severity: 'success', summary: 'Sondage Créé',});
this.selectedPoll = res;
this.pollId = res.pollId;
}, (e) => {
this.handleError(e)
}
);
}
/**
* UPDATE
* /api/v1/poll/{id}/vote
* @param voteStack
*/
updatePoll(voteStack: any) {
this.http.put(
`${this.baseHref}/poll/${this.pollId}`,
voteStack,
this.makeHeaders()
)
.subscribe((res: any) => {
this.messageService.add({
severity: 'success',
summary: 'Sondage mis à jour',
});
this.myPolls = res;
}, (e) => {
this.handleError(e)
}
);
}
/**
* POST
* /api/v1/poll/{id}/vote
* @param voteStack
*/
addVote(voteStack: any) {
this.http.post(
`${this.baseHref}/poll/${this.pollId}/vote`,
voteStack,
this.makeHeaders())
.subscribe((res: any) => {
this.messageService.add({severity: 'success', summary: 'Vote ajouté'});
alert("succès!");
this.myPolls = res;
}, (e) => {
this.handleError(e)
}
);
}
/**
* UPDATE
* /api/v1/poll/{id}/vote
* @param voteStack
*/
updateVote(voteStack: any) {
this.http.put(
`${this.baseHref}/poll/${this.pollId}/vote`,
voteStack,
this.makeHeaders())
.subscribe((res: any) => {
this.messageService.add({severity: 'success', summary: 'Vote mis à jour'});
this.myPolls = res;
}, (e) => {
this.handleError(e)
}
);
}
/**
* POST
* /api/v1/poll/{id}/comment
* @param comment
*/
addComment(comment: any) {
this.http.post(
`${this.baseHref}/poll/${this.pollId}/comment`,
comment,
this.makeHeaders())
.subscribe((res: any) => {
this.messageService.add({
severity: 'success',
summary: 'Commentaire Créé',
detail: 'Via MessageService'
});
}, (e) => {
this.handleError(e)
}
);
}
/**
* administrator calls
*/
}

View File

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

View File

@ -1,32 +0,0 @@
import {Injectable} from '@angular/core';
import {ConfigService} from "../config.service";
import {HttpClient} from "@angular/common/http";
import {environment} from "../../environments/environment";
class JsonResponse {
message: string;
data: string;
}
@Injectable({
providedIn: 'root'
})
export class PollServiceService {
private baseHref: string = environment.baseApiHref;
constructor(private configService: ConfigService,
private http: HttpClient) {
}
findPollsByEmail(email: string) {
// TODO check if the person has a key to retrieve her polls
// If no key is found in the localstorage, ask the backend to send an email to the user
this.configService.myEmail = email;
this.http.get(this.baseHref + '/').subscribe(res => {
this.configService.myPolls = res;
}, err => console.error('err', err)
)
}
}

View File

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

View File

@ -0,0 +1,8 @@
import { ResettableInputDirective } from './resettable-input.directive';
describe('ResettableInputDirective', () => {
it('should create an instance', () => {
const directive = new ResettableInputDirective();
expect(directive).toBeTruthy();
});
});

View File

@ -0,0 +1,11 @@
import {Directive} from '@angular/core';
@Directive({
selector: '[framadateResettableInput]'
})
export class ResettableInputDirective {
constructor() {
}
}

View File

@ -69,6 +69,9 @@
>
Page démo
</a>
<a [routerLink]="'/vote/poll/id/3'" i18n>
Sondage dessins animés
</a>
<a
[routerLink]="'/graphic/toto'"
[ngClass]="{'active': step == 'graphic'}"

View File

@ -1,7 +1,8 @@
import {Component, Input, OnInit} from '@angular/core';
import {BaseComponent} from '../../pages/base-page/base.component';
import {ConfigService} from '../../config.service';
import {Router} from '@angular/router';
import {ConfigService} from '../../services/config.service';
@Component({
selector: 'framadate-navigation',

View File

@ -1,7 +1,10 @@
<div class="choicebox"><!-- add .choicebox--active to most voted -->
<div class="choicebox selection-{{choice.answer}}"><!-- add .choicebox--active to most voted -->
<div class="text title ">
{{choice.name}}
</div>
<div class="choicebox__subject">
<!-- TEXT CASE --><!--
<p class="choicebox__txt">
Lorem ipsum dolor sit amet consectetur, adipisicing elit. Nulla nobis nam culpa !
@ -13,11 +16,14 @@
--><!-- IMG CASE -->
<!-- DATE CASE -->
<div class="choicebox__date" *ngIf="choice.date">
{{choice.date | date:'EEE'}} <span class="choicebox__day">{{choice.date | date:'dd'}}</span> {{choice.date | date:'LLL'}}
</div>
<div class="choicebox__hour">
08:00
<div class="dates" *ngIf="choice.date">
<div class="choicebox__date">
{{choice.date | date:'EEE'}} <span
class="choicebox__day">{{choice.date | date:'dd'}}</span> {{choice.date | date:'LLL'}}
</div>
<div class="choicebox__hour">
08:00
</div>
</div>
<!-- DATE CASE -->
@ -26,32 +32,46 @@
<div class="choicebox__actions">
<!-- show only the yes check if the config is set to simpleAnswer -->
<!-- add .choicebox__btn--active to selected <button> -->
<button class="choicebox__btn choicebox__btn--yes" type="button">
<img src="../../assets/img/check.svg" (click)="setAnswserTo('yes')" alt="">
<span class="simple-answer">
<button class="choicebox__btn choicebox__btn--yes" type="button"
[ngClass]="{'choicebox__btn--active': choice.answer === 'yes'}"
(click)="setAnswserTo('yes')">
<img src="../../../assets/img/check.svg" alt="">
</button>
<button class="choicebox__btn choicebox__btn--maybe" type="button">
<img src="../../assets/img/check-2.svg" (click)="setAnswserTo('maybe')" alt="" *ngIf="!choice.simpleAnswer">
</span>
<span class="complex-answers" *ngIf="!choice.simpleAnswer">
<button class="choicebox__btn choicebox__btn--maybe" type="button"
[ngClass]="{'choicebox__btn--active': choice.answer === 'maybe'}"
(click)="setAnswserTo('maybe')">
<img src="../../../assets/img/check-2.svg" alt="">
</button>
<button class="choicebox__btn choicebox__btn--no" type="button">
<img src="../../assets/img/croix.svg" (click)="setAnswserTo('no')" alt="" *ngIf="!choice.simpleAnswer">
<button class="choicebox__btn choicebox__btn--no" type="button"
[ngClass]="{'choicebox__btn--active': choice.answer === 'no'}"
(click)="setAnswserTo('no')"
>
<img src="../../../assets/img/croix.svg" alt="">
</button>
</span>
</div>
<div class="choicebox__count">
<button type="button" aria-describedby="choicebox-tooltip" class="choicebox__votes">
<button type="button" aria-describedby="choicebox-tooltip" class="choicebox__votes"
*ngIf="choice.votes.count">
<div class="choicebox__vote">
{{choice.votesCount.yes}}
<img width="20px" height="21px" src="../../assets/img/votant-sur.svg" alt="">
{{choice.votes.count.yes}}
<img width="20px" height="21px" src="../../../assets/img/votant-sur.svg" alt="">
</div>
<div class="choicebox__vote">
{{choice.votesCount.maybe}}
<img width="22px" height="24px" src="../../assets/img/votant-pas-sur.svg" alt="">
{{choice.votes.count.maybe}}
<img width="22px" height="24px" src="../../../assets/img/votant-pas-sur.svg" alt="">
</div>
<div class="choicebox__tooltip" id="choicebox-tooltip">
<div class="choicebox__tooltiplist">
<div class="choicebox__tooltipttl">
<img width="20px" height="21px" src="../../assets/img/votant-sur.svg" alt="">
{{choice.votesCount.yes}} "Oui"
<img width="20px" height="21px" src="../../../assets/img/votant-sur.svg" alt="">
{{choice.votes.count.yes}} "Oui"
</div>
<ul>
<li>Lorem</li>
@ -62,8 +82,8 @@
</div>
<div class="choicebox__tooltiplist">
<div class="choicebox__tooltipttl">
<img width="22px" height="24px" src="../../assets/img/votant-pas-sur.svg" alt="">
{{choice.votesCount.maybe}} "Peut-être"
<img width="22px" height="24px" src="../../../assets/img/votant-pas-sur.svg" alt="">
{{choice.votes.count.maybe}} "Peut-être"
</div>
<ul>
<li>Lorem</li>
@ -82,4 +102,4 @@
Choix ayant reçu le plus de votes
</div>
</div>
</div>
</div>

View File

@ -5,22 +5,20 @@
// -- IMPORTS
// ----------------------------
@import "../../assets/scss/variables";
@import "../../../assets/scss/variables";
// -- VARIABLES
// ----------------------------
$box-padding : 2rem;
$box-border-width : .6rem;
$btn-size : 5rem;
$btn-margin-x : 1rem;
$btn-margin-y : 1.5rem;
$btn-wrap-size : calc(2 * #{$btn-size} + 4 * #{$btn-margin-x});
$img-maxheight : 12rem;
$breakpoint-responsive : 640px; // à définir
$box-padding: 2rem;
$box-border-width: .6rem;
$btn-size: 5rem;
$btn-margin-x: 1rem;
$btn-margin-y: 1.5rem;
$btn-wrap-size: calc(2 * #{$btn-size} + 4 * #{$btn-margin-x});
$img-maxheight: 12rem;
$breakpoint-responsive: 640px; // à définir
// -- GLOBAL
@ -34,11 +32,17 @@ $breakpoint-responsive : 640px; // à définir
border-left: $box-border-width solid transparent;
background-color: $white;
box-shadow: 0 0 .6rem 0 rgba($black, .2);
&--active {
padding-left: $box-padding;
border-left-color: $primary_color;
}
&.selection-yes {
font-weight: 700;
background: #e9bdeb;
}
@media (min-width: $breakpoint-responsive) {
display: flex;
align-items: center;
@ -58,7 +62,6 @@ $breakpoint-responsive : 640px; // à définir
}
// -- DATE
// ----------------------------
@ -79,7 +82,6 @@ $breakpoint-responsive : 640px; // à définir
}
// -- IMG
// ----------------------------
@ -89,7 +91,6 @@ $breakpoint-responsive : 640px; // à définir
}
// -- TXT
// ----------------------------
@ -99,7 +100,6 @@ $breakpoint-responsive : 640px; // à définir
}
// -- VOTE BTNS
// ----------------------------
@ -129,25 +129,29 @@ $breakpoint-responsive : 640px; // à définir
align-items: center;
justify-content: center;
margin: $btn-margin-y $btn-margin-x;
border: .1rem solid $primary_color;
border: .3rem solid #ccc9c9;
background-color: transparent;
border-radius: 50%;
@media (min-width: $breakpoint-responsive) {
margin-top: 0;
margin-bottom: 0;
}
&:focus,
&:hover,
&:active {
border-color: #ccc9c9;
background-color: #f7f7f7;
}
//&:focus,
//&:hover,
//&:active {
//
// border-color: #ccc9c9;
// background-color: #f7f7f7;
//
// &--active {
// border-color: #bf83c2 !important;
// }
//}
&--maybe {
position: relative;
top: calc( (#{$btn-size} + 2 * #{$btn-margin-y}) / 2 );
top: calc((#{$btn-size} + 2 * #{$btn-margin-y}) / 2);
@media (min-width: $breakpoint-responsive) {
top: auto;
left: auto;
@ -155,13 +159,11 @@ $breakpoint-responsive : 640px; // à définir
}
&--active {
border-width: .3rem;
border-color: #bf83c2;
}
}
// -- VOTE COUNT
// ----------------------------
@ -195,6 +197,7 @@ $breakpoint-responsive : 640px; // à définir
.choicebox__vote {
display: inline-block;
vertical-align: middle;
& + .choicebox__vote {
margin-left: 1.5rem;
}
@ -203,6 +206,7 @@ $breakpoint-responsive : 640px; // à définir
.choicebox__countxt {
display: none;
margin-top: .5rem;
.choicebox--active & {
display: block;
@media (min-width: $breakpoint-responsive) {
@ -212,7 +216,6 @@ $breakpoint-responsive : 640px; // à définir
}
// -- TOOLTIP
// ----------------------------
@ -265,6 +268,7 @@ $breakpoint-responsive : 640px; // à définir
& + .choicebox__tooltiplist {
padding-left: 3rem;
}
ul {
max-height: 11rem;
overflow: auto;

View File

@ -1,21 +1,22 @@
import {Component, Input} from '@angular/core';
import {Component, ElementRef, Input} from '@angular/core';
interface VoteChoice {
votesCount: {
votes?: {
yes: number
no: number
maybe: number
notAnswered: number
};
text?: string;
name?: string;
date?: Date;
answer: 'yes' | 'no' | 'maybe' | null;
simpleAnswer: boolean; // enable if we display only a togglable "yes"
simpleAnswer?: boolean
false; // enable if we display only a togglable "yes"
}
/**
* each vote choice takes a configuration from the container of all choices.
* this component is used to select a date choice, or a text answer
* this component is used to select a date choice, or a name answer
*/
@Component({
selector: 'framadate-vote-choice',
@ -24,20 +25,9 @@ interface VoteChoice {
})
export class VoteChoiceComponent {
@Input() choice: VoteChoice = {
date: new Date(),
text: 'description ',
votesCount: {
yes: 0,
no: 0,
maybe: 0,
notAnswered: 0
},
simpleAnswer: false,
answer: null
};
@Input() private choice: any;
constructor() {
constructor(private el: ElementRef) {
}
@ -53,6 +43,7 @@ export class VoteChoiceComponent {
} else {
this.choice.answer = newAnswer;
}
this.el.nativeElement.blur();
}
}

View File

@ -18,6 +18,9 @@
}
}
}
.clickable {
cursor: pointer;
}
.btn {
margin: .5rem 0;

View File

@ -51,7 +51,7 @@ option {
background-color: transparent;
background-image: url("./assets/img/fleche_bas.svg");
padding-right: 1.5rem;
padding-right: 2.5rem;
background-repeat: no-repeat;
background-size: 9px 8px;
@ -76,3 +76,91 @@ label {
}
}
}
input {
&:not([id]) {
color: $dusty-orange;
&:before {
content: "cet input n'a pas d'attribut id, c'est mal.";
color: $violet;
display: block;
padding: 1em;
background: yellow;
position: relative;
top: -1em;
z-index: 2;
margin-right: 0.5em;
right: 0;
border: solid red 2px;
box-shadow: 0 0 10px orange;
}
}
}
label {
font-weight: 600;
font-size: 18px;
}
.comment, .prefered {
border-left: 6px solid $ugly-purple;
padding-left: 5px;
margin-top: 25px;
margin-bottom: 25px;
flex-wrap: wrap;
}
.cname {
padding-left: 17px;
font-weight: bold;
}
.btn {
max-width: 300px;
}
.next {
max-width: 200px;
}
input[type=text], textarea {
max-width: 350px;
}
li {
list-style-type: none;
}
h2 {
}
.next {
align-self: flex-end;
margin-bottom: 50px;
}
textarea {
height: 213px;
margin-bottom: 20px;
}
.name {
margin-bottom: 50px;
margin-top: 50px;
}
.nobold {
font-weight: normal;
}
select, input, textarea {
@extend .clickable;
&:active,
&:focus,
&:hover {
color: $primary_color;
}
}

View File

@ -1,4 +1,5 @@
export const environment = {
production: true,
baseApiHref : 'http://127.0.0.1:8000/api/v1/'
baseHref: "https://framadate-api.cipherbliss.com/",
baseApiHref: 'https://framadate-api.cipherbliss.com/api/v1'
};

View File

@ -4,7 +4,9 @@
export const environment = {
production: false,
baseApiHref: "http://127.0.0.1:8000/api/v1/"
baseHref: "https://framadate-api.cipherbliss.com/",
baseApiHref: "https://framadate-api.cipherbliss.com/api/v1"
// baseApiHref: "http://localhost:8000/api/v1"
};
/*

View File

@ -1,343 +0,0 @@
<?xml version="1.0" encoding="UTF-8" ?>
<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
<file source-language="fr" datatype="plaintext" original="ng2.template">
<body>
<trans-unit id="demo" datatype="html">
<source>
Ceci est une démo
</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/app.component.html</context>
<context context-type="linenumber">7</context>
</context-group>
<note priority="1" from="description">introduction header saying just demo </note>
<note priority="1" from="meaning">demo title</note>
</trans-unit>
<trans-unit id="plural_example" datatype="html">
<source>
Updated <x id="ICU" equiv-text="{minutes, plural, =0 {...} =1 {...} other {...}}"/>
</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/app.component.html</context>
<context context-type="linenumber">10</context>
</context-group>
</trans-unit>
<trans-unit id="5a134dee893586d02bffc9611056b9cadf9abfad" datatype="html">
<source>{VAR_PLURAL, plural, =0 {just now} =1 {one minute ago} other {<x id="INTERPOLATION" equiv-text="{{minutes}}"/> minutes ago} }</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/app.component.html</context>
<context context-type="linenumber">11</context>
</context-group>
</trans-unit>
<trans-unit id="c279d3eb8220c1a0b77adb0a520b3e81f086d046" datatype="html">
<source>placeholder à traduire</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/app.component.html</context>
<context context-type="linenumber">13</context>
</context-group>
</trans-unit>
<trans-unit id="confirm" datatype="html">
<source>C&apos;est parfait!</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/pages/base-page/base.component.html</context>
<context context-type="linenumber">2</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/pages/kind/kind.component.html</context>
<context context-type="linenumber">4</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/pages/visibility/visibility.component.html</context>
<context context-type="linenumber">14</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/app/pages/resume/resume.component.html</context>
<context context-type="linenumber">10</context>
</context-group>
</trans-unit>
<trans-unit id="8dfa2af2946c94200a9c49226ac8221ddec0834d" datatype="html">
<source>
Config spécialement pour les dates
</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/pages/dates/dates.component.html</context>
<context context-type="linenumber">1</context>
</context-group>
</trans-unit>
<trans-unit id="05c94bdaeec38467e5d8ec67ef06ae02bc2721fd" datatype="html">
<source>
Je souhaite mettre des créneaux horaires
</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/pages/dates/dates.component.html</context>
<context context-type="linenumber">16</context>
</context-group>
</trans-unit>
<trans-unit id="3da20d71c6eacf0bf933e2ebefcc8581b77fcd07" datatype="html">
<source>
pour chaque journée
</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/pages/dates/dates.component.html</context>
<context context-type="linenumber">20</context>
</context-group>
</trans-unit>
<trans-unit id="0bd3bae4b21407cbd1ab7f12f2516aba84ee8cd5" datatype="html">
<source>
Ajouter une plage de dates
</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/pages/dates/dates.component.html</context>
<context context-type="linenumber">30</context>
</context-group>
</trans-unit>
<trans-unit id="d5ac5c6775ece9a7d33e5dc3519806c6edfb87bc" datatype="html">
<source>
choix de Dates
</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/pages/dates/dates.component.html</context>
<context context-type="linenumber">35</context>
</context-group>
</trans-unit>
<trans-unit id="6387b579b9b1bdb74c3dfd9d63387adb40076376" datatype="html">
<source>
infos de debug
</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/debugger/debugger.component.html</context>
<context context-type="linenumber">4</context>
</context-group>
</trans-unit>
<trans-unit id="ddacf8feec398fe7fcda34b82a7aa4d71e10e755" datatype="html">
<source>
Choix cornélien syncronisé:
</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/debugger/debugger.component.html</context>
<context context-type="linenumber">27</context>
</context-group>
</trans-unit>
<trans-unit id="d146b7d3598f0f99412cb115c4c9b9dc6724a3db" datatype="html">
<source>
sondage classique
</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/debugger/debugger.component.html</context>
<context context-type="linenumber">37</context>
</context-group>
</trans-unit>
<trans-unit id="9e1841dca6f43cec422f009746b78fa72021aed3" datatype="html">
<source>
sondage spécial date
</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/debugger/debugger.component.html</context>
<context context-type="linenumber">51</context>
</context-group>
</trans-unit>
<trans-unit id="9dffbece2b62a762cc013cd9a092a45386644395" datatype="html">
<source>
Envoyer le formulaire
</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/debugger/debugger.component.html</context>
<context context-type="linenumber">64</context>
</context-group>
</trans-unit>
<trans-unit id="a2e46fcfdbcd6457dd6dda15896ff772a4cccb59" datatype="html">
<source>
Visibilité des réponses
</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/pages/visibility/visibility.component.html</context>
<context context-type="linenumber">1</context>
</context-group>
</trans-unit>
<trans-unit id="f3fa123ec08a0f55d20203474e459cb166171530" datatype="html">
<source>
Votes
</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/pages/visibility/visibility.component.html</context>
<context context-type="linenumber">4</context>
</context-group>
</trans-unit>
<trans-unit id="07056464cc8e77270490824db1c119774a9605d8" datatype="html">
<source>
Archivage
</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/pages/visibility/visibility.component.html</context>
<context context-type="linenumber">7</context>
</context-group>
</trans-unit>
<trans-unit id="e89a47f338845c2bc853956b810455622ea70d18" datatype="html">
<source>
Accès au sondage
</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/pages/visibility/visibility.component.html</context>
<context context-type="linenumber">11</context>
</context-group>
</trans-unit>
<trans-unit id="575c777901e66582920b91544ddfc3f1cff6dfdd" datatype="html">
<source>
Résumé avant validation
</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/pages/resume/resume.component.html</context>
<context context-type="linenumber">1</context>
</context-group>
</trans-unit>
<trans-unit id="e73989397fa8b6b2a516ec090b7653d7ca8a9f2d" datatype="html">
<source>
Images
</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/pages/pictures/pictures.component.html</context>
<context context-type="linenumber">2</context>
</context-group>
</trans-unit>
<trans-unit id="f6d82bb9d11a1a8fbfbc8dd54388b068db42fd4a" datatype="html">
<source>
Choisir les propositions
</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/pages/answers/answers.component.html</context>
<context context-type="linenumber">2</context>
</context-group>
</trans-unit>
<trans-unit id="aa15ed6f3664a52d0532b4caccc5e3862eceaff6" datatype="html">
<source>
vous pouvez utiliser la syntaxe markdown
</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/pages/answers/answers.component.html</context>
<context context-type="linenumber">6</context>
</context-group>
</trans-unit>
<trans-unit id="8c8496fbdf12a4a3ef88119cd37deca10bf6c661" datatype="html">
<source>
Et c&apos;est tout pour nous!
</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/pages/end-confirmation/end-confirmation.component.html</context>
<context context-type="linenumber">1</context>
</context-group>
</trans-unit>
<trans-unit id="c434b3773c35a5d55a1f5648433e7521e4424aea" datatype="html">
<source>Coté administrateur-ice-eux</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/pages/end-confirmation/end-confirmation.component.html</context>
<context context-type="linenumber">4</context>
</context-group>
</trans-unit>
<trans-unit id="d0225bc631b962e0c6696ebc3e26cc90ecf15387" datatype="html">
<source>Coté sondés</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/pages/end-confirmation/end-confirmation.component.html</context>
<context context-type="linenumber">5</context>
</context-group>
</trans-unit>
<trans-unit id="585c8fc362784c1de91b02f0e69e08c6993f9dd2" datatype="html">
<source>recevoir les liens par e-mail</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/pages/end-confirmation/end-confirmation.component.html</context>
<context context-type="linenumber">6</context>
</context-group>
</trans-unit>
<trans-unit id="f74e29e3e6b015dc4163c3d43002088d0e1da5dc" datatype="html">
<source>
Créer un sondage
</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/pages/create-or-retrieve/create-or-retrieve.component.html</context>
<context context-type="linenumber">2</context>
</context-group>
</trans-unit>
<trans-unit id="36ab2505621b4451317833191d6a4c2d7c214b3c" datatype="html">
<source>
Planifiez des rendez-vous avec vos amis ou votre famille ou créez un sondage avec du texte, des images ou des
liens… un sondage quoi !
</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/pages/create-or-retrieve/create-or-retrieve.component.html</context>
<context context-type="linenumber">8</context>
</context-group>
</trans-unit>
<trans-unit id="c3781d8c21f3eba04fe803a3fe0a4560401491d5" datatype="html">
<source>
C&apos;est parti
</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/pages/create-or-retrieve/create-or-retrieve.component.html</context>
<context context-type="linenumber">19</context>
</context-group>
</trans-unit>
<trans-unit id="9e1fa5a7ae3c13a9fa4a657d94085e239066377a" datatype="html">
<source>
Où sont mes sondages ?
</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/pages/create-or-retrieve/create-or-retrieve.component.html</context>
<context context-type="linenumber">29</context>
</context-group>
</trans-unit>
<trans-unit id="b291d9e84fc9afe337b3450266847c430523bcc5" datatype="html">
<source>
Je cherche les sondages qui correspondent à mon mail :
</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/pages/create-or-retrieve/create-or-retrieve.component.html</context>
<context context-type="linenumber">40</context>
</context-group>
</trans-unit>
<trans-unit id="9606f1c734851ad834d5f9eca4fbc79fad2cf406" datatype="html">
<source>
Pour commencer
</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/pages/home/home.component.html</context>
<context context-type="linenumber">4</context>
</context-group>
</trans-unit>
<trans-unit id="e40f615c5bada8d5458ede5b03812bdb8074d7d7" datatype="html">
<source>
Je veux créer un sondage
</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/pages/home/home.component.html</context>
<context context-type="linenumber">10</context>
</context-group>
</trans-unit>
<trans-unit id="99eef0ca99f5a0e0aee806ffdefb5ca1ca318b07" datatype="html">
<source>
Dont le titre sera
</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/pages/home/home.component.html</context>
<context context-type="linenumber">34</context>
</context-group>
</trans-unit>
<trans-unit id="77100ae0f206a821d33d9582dd2962f379eaeac7" datatype="html">
<source>
et la description serait
</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/pages/home/home.component.html</context>
<context context-type="linenumber">64</context>
</context-group>
</trans-unit>
<trans-unit id="114007b6744088dc57d792702b19f91f68f7926e" datatype="html">
<source>
Continuer
</source>
<context-group purpose="location">
<context context-type="sourcefile">src/app/pages/home/home.component.html</context>
<context context-type="linenumber">82</context>
</context-group>
</trans-unit>
</body>
</file>
</xliff>

587
yarn.lock

File diff suppressed because it is too large Load Diff