Commit 9954d9d7 authored by Julien BOUYER's avatar Julien BOUYER
Browse files

Merge branch 'feature/UNOTOPLYS-166_modif_ecran' into 'develop'

UNOTOPLYS-166 feat(screen) : modifier les infos d'un écran

See merge request !66
parents 91260dea 28a966b2
......@@ -2,8 +2,8 @@ import Vue from 'vue';
import Component from 'vue-class-component';
import Login from '@/components/header/login/login.vue';
import NavItem from '@/components/header/nav/nav-item/nav-item.vue';
import TabItem from '@/components/header/nav/tab-item/tab-item.vue';
import NavItem from '@/components/nav/nav-item/nav-item.vue';
import TabItem from '@/components/nav/tab-item/tab-item.vue';
@Component({
components: {
......
import Vue from 'vue';
import { Component, Prop } from 'vue-property-decorator';
import TabItem from '@/components/nav/tab-item/tab-item.vue';
@Component({
components: {
TabItem,
},
})
export default class ScreenHeader extends Vue {
@Prop()
public screenName: string;
@Prop()
public tabs: any;
}
<template>
<div>
<h1 class="mb-6 text-3xl">{{ screenName }}</h1>
<ul class="flex justify-center border-b-2 mb-8">
<tab-item v-for="(tab, index) in tabs" :key="index" :title="tab.title" :link="tab.link" :state="tab.isCurrent ? 'current' : ''" />
</ul>
</div>
</template>
<script lang="ts" src="./screen-header.component.ts" />
......@@ -2,6 +2,7 @@ import { Authority } from '@/shared/security/authority';
const ScreenCreateComponent = () => import('@/views/screen/screen-create.vue');
const ScreenEditComponent = () => import('@/views/screen/screen-edit.vue');
const ScreenSettingsComponent = () => import('@/views/screen/screen-settings.vue');
export default [
{
......@@ -22,6 +23,15 @@ export default [
panel: 'panel/panel-screen/panel-screen',
},
},
{
path: '/admin/workspace/:idWorkspace/form/:idForm/screen/:idScreen/settings',
name: 'ScreenSettingsComponent',
component: ScreenSettingsComponent,
meta: {
authorities: [Authority.ADMIN],
panel: 'panel/panel-screen/panel-screen',
},
},
{
path: '/admin/workspace/:idWorkspace/form/:idForm/view',
name: 'FirstScreenEditComponent',
......
export const TABS = {
KEY_EDIT: 'edit',
KEY_SETTINGS: 'settings',
KEY_VIEW: 'view',
KEY_VARIABLE: 'variable',
......
import Vue from 'vue';
import { Component, Inject } from 'vue-property-decorator';
import { Component } from 'vue-property-decorator';
import { mixins } from 'vue-class-component';
import { TABS } from '@/shared/service/tabs-constants';
import ScreenMixin from '@/views/screen/screen.mixin';
import Icon from '@/components/icon/icon.vue';
import OaButton from '@/components/button/oa-button.vue';
import ScreenItemEdit from '@/components/screen-item/screen-item-edit.vue';
import StepToolbar from '@/components/step-toolbar/step-toolbar.vue';
import OaButton from '@/components/button/oa-button.vue';
import OaInput from '@/components/forms/input/oa-input.vue';
import OaTextarea from '@/components/forms/textarea/oa-textarea.vue';
import { IScreen, Screen, screenValidate } from '@/shared/model/screen.model';
import ScreenService from '@/services/screen.service';
import Icon from '@/components/icon/icon.vue';
import { TABS } from '@/shared/service/tabs-constants';
import HeaderService from '@/shared/service/header-service';
@Component({
components: {
ScreenItemEdit,
Icon,
OaButton,
OaInput,
OaTextarea,
ScreenItemEdit,
StepToolbar,
Icon,
},
})
export default class ScreenEdit extends Vue {
@Inject('screenService') private screenService: () => ScreenService;
@Inject('headerService')
private headerService: () => HeaderService;
export default class ScreenEdit extends mixins(ScreenMixin) {
public items: any = [{}]; // TODO récupérer les items dans le store
public screen: IScreen = new Screen();
public newItemType = '';
beforeRouteEnter(to, from, next) {
next(vm => {
vm.handleNavigationChange(to.params);
vm.headerService().filArianeForForm(to.params.idForm);
if (to.params.idForm) {
vm.headerService().updateTabsForFormId(to.params.idWorkspace, to.params.idForm, TABS.KEY_VIEW);
}
});
}
beforeRouteUpdate(to, from, next) {
this.handleNavigationChange(to.params);
next();
}
public handleNavigationChange({ idWorkspace, idForm, idScreen }) {
this.$store.commit('setIdWorkspace', idWorkspace);
this.$store.commit('setIdForm', idForm);
this.$store.commit('setIdScreen', idScreen);
this.$store.dispatch('loadScreens', idForm);
if (idScreen) {
this.retrieveScreen(idScreen);
} else {
this.retrieveFirstScreenOfForm(idForm);
}
}
public get stepNum(): number {
const idScreen = this.$store.getters.idScreen;
const index = this.$store.getters.screenList.findIndex((s: IScreen) => s.id === idScreen);
return index + 1;
}
public retrieveScreen(idScreen: number) {
this.screenService()
.find(idScreen)
.then(res => {
this.screen = res;
})
.catch(console.error);
}
public retrieveFirstScreenOfForm(idForm) {
this.screenService()
.retrieveFirstScreenOfForm(idForm)
.then(res => {
this.screen = res.data;
this.$store.commit('setIdScreen', res.data.id);
})
.catch(console.error);
}
public save() {
if (screenValidate.validate(this.screen)) {
this.screenService()
.update(this.screen)
.then(res => {
this.screen = res;
})
.catch(console.error);
} else {
// TODO message d'erreur de validation
}
constructor() {
super();
this.currScreenTab = TABS.KEY_EDIT;
}
public saveItemChange() {
......@@ -104,27 +32,7 @@ export default class ScreenEdit extends Vue {
this.save();
}
public setName(name) {
this.screen.name = name;
this.save();
}
public setNameBo(nameBo) {
this.screen.nameBo = nameBo;
this.save();
}
public setReference(reference) {
this.screen.reference = reference;
this.save();
}
public setDescription(description) {
this.screen.description = description;
this.save();
}
public setNewItemType(type) {
public setNewItemType(type: string) {
this.newItemType = type;
}
......
<template>
<main>
<section class="max-w-4xl mx-auto p-9">
<h1 class="mb-6 text-3xl">{{ $t('screen.label.step', { num: stepNum }) }}</h1>
<oa-input id="title" :label="$t('screen.label.title')" :value="screen.name" required="true" @update="setName" />
<oa-input id="titleBo" :label="$t('screen.label.titleBo')" :value="screen.nameBo" required="true" @update="setNameBo" />
<oa-input
id="reference"
:label="$t('screen.label.reference.label')"
:value="screen.reference"
required="true"
@update="setReference"
:helpText="$t('screen.label.reference.helpText')"
/>
<oa-textarea id="description" :label="$t('screen.label.description')" :value="screen.description" @update="setDescription" />
<ul class="flex justify-center border-b-2 mb-8">
<li>
<a class="block p-2 text-blue-500 border-b border-blue-600 py-2" href="#">Contenus</a>
</li>
</ul>
<screen-header :screenName="screen.name" :tabs="screenTabs" />
<div class="mb-8 border-b-2">
<div v-for="(item, index) in screen.itemsList" :key="index">
<div class="bg-blue-100 mb-8 flex w-auto items-center">
......
import { Component } from 'vue-property-decorator';
import { mixins } from 'vue-class-component';
import { TABS } from '@/shared/service/tabs-constants';
import ScreenMixin from '@/views/screen/screen.mixin';
@Component
export default class ScreenSettings extends mixins(ScreenMixin) {
constructor() {
super();
this.currScreenTab = TABS.KEY_SETTINGS;
}
public setName(name: string) {
this.screen.name = name;
this.save();
}
public setNameBo(nameBo: string) {
this.screen.nameBo = nameBo;
this.save();
}
public setReference(reference: string) {
this.screen.reference = reference;
this.save();
}
public setDescription(description: string) {
this.screen.description = description;
this.save();
}
}
<template>
<section class="max-w-4xl mx-auto p-9">
<screen-header :screenName="screen.name" :tabs="screenTabs" />
<section class="max-w-4xl mx-auto pb-6">
<h2 class="mb-4 text-2xl">{{ $t('screen.titles.informations') }}</h2>
<oa-input id="title" :label="$t('screen.label.title')" :value="screen.name" required="true" @update="setName" />
<oa-input id="titleBo" :label="$t('screen.label.titleBo')" :value="screen.nameBo" required="true" @update="setNameBo" />
<oa-input
id="reference"
:label="$t('screen.label.reference.label')"
:value="screen.reference"
required="true"
@update="setReference"
:helpText="$t('screen.label.reference.helpText')"
/>
<oa-textarea id="description" :label="$t('screen.label.description')" :value="screen.description" @update="setDescription" />
</section>
<section class="max-w-4xl mx-auto pb-6">
<h2 class="mb-4 text-2xl">{{ $t('screen.titles.determination') }}</h2>
<div class="mb-8">
<p class="mb-4">Vous pouvez personnaliser les règles permettant de déterminer le prochain écran.</p>
<p class="mb-2">Quand l'utilisateur a validé l'écran en cliquant sur "Suivant" :</p>
<ul class="flex flex-col space-y-2 mb-2 pl-4">
<li class="flex space-x-2 items-center">
<input
class="checked:border-blue-700 checked:bg-blue-500 appearance-none border border-gray-400 w-5 h-5 rounded-full ring-inset ring-4 ring-gray-100"
type="radio"
name="nextScreen"
id="default"
checked
/>
<label for="default">Afficher le prochain écran (cf. liste à gauche)</label>
</li>
<li class="flex space-x-2 items-center">
<input
class="checked:border-blue-700 checked:bg-blue-500 appearance-none border border-gray-400 w-5 h-5 rounded-full ring-inset ring-4 ring-gray-100"
type="radio"
name="nextScreen"
id="screen"
/>
<label for="screen">Toujours afficher l'écran ...</label>
</li>
<div class="pl-6">
<input
class="w-full px-6 py-4 text-lg transition bg-white border rounded-md outline-none ring-inset focus:ring"
type="text"
list="screen"
name="screen"
placeholder="Nom de l'écran"
/>
<datalist class="bg-white" id="screen">
<option>Hello</option>
<option>World lorem</option>
<option>Nada Banana</option>
</datalist>
</div>
<li class="flex space-x-2 items-center">
<input
class="checked:border-blue-700 checked:bg-blue-500 appearance-none border border-gray-400 w-5 h-5 rounded-full ring-inset ring-4 ring-gray-100"
type="radio"
name="nextScreen"
id="condition"
/>
<label for="condition">Choisir l'écran en fonction de la réponse à une question</label>
</li>
<div class="pl-6">
<div class="mb-8">
<label class="block mb-2 font-bold" for="f1">Intitulé de la question</label>
<div class="relative">
<select
class="focus:ring w-full px-6 py-4 text-lg transition bg-white border rounded-md outline-none appearance-none ring-inset"
id="i1"
>
<option value="q1" selected>
À combien estimez-vous votre moyenne en Enseignement scientifique (hors spécialités) ?
</option>
<option value="q2">Une autre question ?</option></select
><span class="absolute top-0 bottom-0 flex items-center pl-4 text-gray-400 border-l right-5 pointer-events-none">
<svg class="icon" viewbox="0 0 24 24">
<path
fill="currentColor"
d="M12,18.17L8.83,15L7.42,16.41L12,21L16.59,16.41L15.17,15M12,5.83L15.17,9L16.58,7.59L12,3L7.41,7.59L8.83,9L12,5.83Z"
></path></svg
></span>
</div>
<p class="mt-1 text-sm italic text-gray-500" id="h1"></p>
</div>
<!--TODO DEV : utiliser un composant datalist / autocomplete de type https://material-ui.com/components/autocomplete/-->
<table class="w-full bg-white border-t mt-2">
<thead class="font-bold">
<tr class="border-b-2">
<th class="p-2 text-left w-1/2">Si la réponse est</th>
<th class="p-2 text-left">Alors redirection vers l'écran</th>
<th class="p-2 text-right w-6"></th>
</tr>
</thead>
<tbody>
<tr class="text-left align-top border-b">
<td class="p-2 focus:ring" contenteditable="true">Bac général</td>
<td class="p-2 focus:ring" contenteditable="true">6 - Vos résultats Bac G</td>
<td class="align-middle text-center">
<button class="flex items-center p-2 text-red-600 transition hover:bg-red-600 focus:ring hover:text-red-100">
<svg class="icon" viewbox="0 0 24 24">
<path
fill="currentColor"
d="M9,3V4H4V6H5V19A2,2 0 0,0 7,21H17A2,2 0 0,0 19,19V6H20V4H15V3H9M9,8H11V17H9V8M13,8H15V17H13V8Z"
></path>
</svg>
</button>
</td>
</tr>
<tr class="text-left align-top border-b">
<td class="p-2 focus:ring" contenteditable="true">Bac professionnel</td>
<td class="p-2 focus:ring" contenteditable="true">7 - Vos résultats Bac P/T</td>
<td class="align-middle text-right">
<button class="flex items-center p-2 text-red-600 transition hover:bg-red-600 focus:ring hover:text-red-100">
<svg class="icon" viewbox="0 0 24 24">
<path
fill="currentColor"
d="M9,3V4H4V6H5V19A2,2 0 0,0 7,21H17A2,2 0 0,0 19,19V6H20V4H15V3H9M9,8H11V17H9V8M13,8H15V17H13V8Z"
></path>
</svg>
</button>
</td>
</tr>
<tr class="text-left align-top border-b">
<td class="p-2 focus:ring" contenteditable="true">Bac technologique</td>
<td class="p-2 focus:ring" contenteditable="true">7 - Vos résultats Bac P/T</td>
<td class="align-middle text-right">
<button class="flex items-center p-2 text-red-600 transition hover:bg-red-600 focus:ring hover:text-red-100">
<svg class="icon" viewbox="0 0 24 24">
<path
fill="currentColor"
d="M9,3V4H4V6H5V19A2,2 0 0,0 7,21H17A2,2 0 0,0 19,19V6H20V4H15V3H9M9,8H11V17H9V8M13,8H15V17H13V8Z"
></path>
</svg>
</button>
</td>
</tr>
<tr class="text-left align-top border-b">
<td class="p-2 focus:ring" contenteditable="true">Par défaut</td>
<td class="p-2 focus:ring" contenteditable="true">Écran suivant</td>
<td class="align-middle text-right">
<button class="flex items-center p-2 text-red-600 transition hover:bg-red-600 focus:ring hover:text-red-100">
<svg class="icon" viewbox="0 0 24 24">
<path
fill="currentColor"
d="M9,3V4H4V6H5V19A2,2 0 0,0 7,21H17A2,2 0 0,0 19,19V6H20V4H15V3H9M9,8H11V17H9V8M13,8H15V17H13V8Z"
></path>
</svg>
</button>
</td>
</tr>
<tr class="text-left align-top border-b text-gray-500">
<td class="p-2 focus:ring focus:text-gray-900" contenteditable="true">Ajouter une réponse…</td>
<td class="p-2 focus:ring" contenteditable="true">Écran suivant</td>
<td class="align-middle text-right"></td>
</tr>
</tbody>
</table>
</div>
</ul>
</div>
</section>
<!-- Une barre d’action-->
<div class="flex items-center">
<div class="text-right flex-grow">
<button
class="px-6 py-4 mx-1 my-1 text-base font-bold text-white transition bg-blue-600 rounded-full hover:bg-blue-800 focus:ring active:bg-blue-900"
type="submit"
>
Enregistrer
</button>
</div>
</div>
</section>
</template>
<script lang="ts" src="./screen-settings.component.ts" />
import { Component, Inject, Vue } from 'vue-property-decorator';
import HeaderService from '@/shared/service/header-service';
import ScreenService from '@/entities/screen/screen.service';
import { IScreen, Screen, screenValidate } from '@/shared/model/screen.model';
import { TABS } from '@/shared/service/tabs-constants';
import OaInput from '@/components/forms/input/oa-input.vue';
import OaTextarea from '@/components/forms/textarea/oa-textarea.vue';
import ScreenHeader from '@/components/screen-header/screen-header.vue';
@Component({
components: {
OaInput,
OaTextarea,
ScreenHeader,
},
})
export default class ScreenMixin extends Vue {
@Inject('screenService') public screenService: () => ScreenService;
@Inject('headerService') public headerService: () => HeaderService;
public screen: IScreen = new Screen();
public currScreenTab: string;
beforeRouteEnter(to, from, next) {
next(vm => {
vm.handleNavigationChange(to.params);
vm.headerService().filArianeForForm(to.params.idForm);
if (to.params.idForm) {
vm.headerService().updateTabsForFormId(to.params.idWorkspace, to.params.idForm, TABS.KEY_VIEW);
}
});
}
beforeRouteUpdate(to, from, next) {
this.handleNavigationChange(to.params);
next();
}
public handleNavigationChange({ idWorkspace, idForm, idScreen }) {
this.$store.commit('setIdWorkspace', idWorkspace);
this.$store.commit('setIdForm', idForm);
this.$store.commit('setIdScreen', idScreen);
this.$store.dispatch('loadScreens', idForm);
if (idScreen) {
this.retrieveScreen(idScreen);
} else {
this.retrieveFirstScreenOfForm(idForm);
}
}
public retrieveScreen(idScreen: number) {
this.screenService()
.find(idScreen)
.then(res => {
this.screen = res;
})
.catch(console.error);
}
public retrieveFirstScreenOfForm(idForm: number) {
this.screenService()
.retrieveFirstScreenOfForm(idForm)
.then(res => {
this.screen = res.data;
this.$store.commit('setIdScreen', res.data.id);
})
.catch(console.error);
}
public save() {
if (screenValidate.validate(this.screen)) {
this.screenService()
.update(this.screen)
.then(res => {
this.screen = res;
})
.catch(console.error);
} else {
// TODO message d'erreur de validation
}
}
public get screenTabs(): any {
const path = `/admin/workspace/${this.$store.getters.idWorkspace}/form/${this.$store.getters.idForm}/screen/${this.$store.getters.idScreen}`;
return [
{
title: this.$t('screen.tabs.content'),
link: `${path}/edit`,
isCurrent: !this.currScreenTab || this.currScreenTab === TABS.KEY_EDIT,
},
{
title: this.$t('screen.tabs.settings'),
link: `${path}/settings`,
isCurrent: this.currScreenTab === TABS.KEY_SETTINGS,
},
];
}
}
......@@ -47,6 +47,14 @@
"newItemType": "Type de l'item à ajouter",
"delete": "Voulez-vous vraiment supprimer cet écran ?"
},
"titles": {
"informations": "Informations de l'écran",
"determination": "Détermination de l'écran suivant"
},
"tabs": {
"content": "Contenu",
"settings": "Paramétrage"
},
"items": {