diff --git a/README.md b/README.md index 6a51f0ab7441304cdba061551134dfb57269d228..09658de792e5c0680275e277c3a6503bc9011586 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,9 @@ npm install # run in dev mode +npm run electron:dev + +#electron build npm run app:build # vite dev mode diff --git a/src/components/ContentButton.vue b/src/components/ContentButton.vue index 7c05be9d8100c078476bd9ea3be8a8d02e5cf050..7618d84608792a83f4db62383b7fe567f3d21e10 100644 --- a/src/components/ContentButton.vue +++ b/src/components/ContentButton.vue @@ -5,6 +5,7 @@ defineProps<{ classList: object; isActive: boolean; isDraggable: boolean; + subtitle?: string; }>(); const emit = defineEmits<{ @@ -17,14 +18,23 @@ function click(event) { </script> <template> - <button + <div + class="btn btn-content" + :class="[classList, { active: isActive }, { 'draggable': isDraggable }]" + :draggable="isDraggable" + @click="click($event)" + > + <i :class="icon" /> + <span v-if="subtitle" class="subtitle">{{ subtitle }}</span> + </div> + <!-- <button class="btn btn-content" :draggable="isDraggable" :class="[classList, { active: isActive }, { 'draggable': isDraggable }]" @click="click($event)" > <i :class="icon" /> - </button> + </button> --> </template> <style scoped lang="scss"> diff --git a/src/features/ePocFlow/ePocFlow.vue b/src/features/ePocFlow/ePocFlow.vue index 9bcd6f81c9fcc6da1124ba3be076364435f4fed4..75a3af02c732522857bb1eeebab72c8eed672359 100644 --- a/src/features/ePocFlow/ePocFlow.vue +++ b/src/features/ePocFlow/ePocFlow.vue @@ -1,22 +1,57 @@ <script setup lang="ts"> -import { VueFlow, useVueFlow } from '@vue-flow/core'; +import { VueFlow, useVueFlow, Panel, PanelPosition } from '@vue-flow/core'; import { markRaw, nextTick, watch } from 'vue'; import ContentNode from './nodes/ContentNode.vue'; import CustomConnectContent from './edges/CustomConnectContent.vue'; import { SideAction, NodeElement } from '../../shared/interfaces'; import { useEditorStore } from '../../shared/stores'; +import ChapterNode from './nodes/ChapterNode.vue'; +import ePocNode from './nodes/ePocNode.vue'; +import AddChapterNode from './nodes/AddChapterNode.vue'; -const { nodes, addNodes, addEdges, onConnect, vueFlowRef, project, findNode } = useVueFlow(); +const { nodes, addNodes, addEdges, onConnect, vueFlowRef, project, findNode, setNodes, setEdges } = useVueFlow(); onConnect((params) => addEdges([{...params, updatable: true, style: { stroke: '#384257', strokeWidth: 2.5 }}])); const editorStore = useEditorStore(); const nodeTypes = { - content: markRaw(ContentNode) + content: markRaw(ContentNode), + chapter: markRaw(ChapterNode), + epoc: markRaw(ePocNode), + add: markRaw(AddChapterNode), +}; + +const epoc = { + id: 1, + type: 'epoc', + position: { x: 0, y: 0 }, + draggable: false, +}; + +const chapter = { + id: 3, + type: 'chapter', + position: { x: 0, y: 200 }, + draggable: false, +}; + +const add = { + id: 2, + type: 'add', + position: { x: 33, y: 325 }, + draggable: false +}; + +const mainEdge = { + id: 'mainEdge', + source: '1', + target: '2', + style: { stroke: '#CDD3E0', strokeWidth: 2.5 } }; -const elements = []; + +const elements = [epoc, chapter, add, mainEdge]; //? Use this to detect interesctions(for creating screen); // onNodeDrag(({ intersections }) => { @@ -36,12 +71,12 @@ const onDrop = (event) => { }); const data = event.dataTransfer.getData('sideAction'); - - console.log(data); + const isScreen = event.dataTransfer.getData('isScreen'); const actions = JSON.parse(data); - if(actions.length > 1) { + // not sure if this is better than transfer the isScreen in all the case and use it as a boolean here + if(isScreen === 'true') { addNode(position, actions); } else { if(!addToExistingScreen(actions)) { @@ -56,7 +91,7 @@ function addNode(position, actions: SideAction[]) { let elements: NodeElement[] = []; - const id= (nodes.value.length + 1).toString(); + const id = (nodes.value.length + 1).toString(); actions.forEach((action) => { elements.push({ @@ -114,6 +149,13 @@ function addToExistingScreen(action : SideAction):boolean { } return false; } + +//Temporary function +function onDelete() { + setNodes([epoc, chapter, add]); + setEdges([mainEdge]); +} + </script> <template> @@ -129,10 +171,21 @@ function addToExistingScreen(action : SideAction):boolean { @dragover.prevent @dragenter.prevent > - <MiniMap /> + <Panel :position="PanelPosition.TopRight" class="save-restore-controls"> + <button style="background-color: #ff0000; padding: 1rem; border-radius: 8px; border: none; cursor: pointer; font-size: 1.2rem;" @click="onDelete">Delete all</button> + </Panel> <template #node-custom="{ id, data }"> <ContentNode :id="id" :data="data" /> </template> + <template #node-chapter="{ id, data }"> + <ChapterNode :id="id" :data="data" /> + </template> + <template #node-epoc="{ id, data }"> + <ePocNode :id="id" :data="data" /> + </template> + <template #node-add="{ id, data }"> + <ePocNode :id="id" :data="data" /> + </template> <template #connection-line="{ sourceX, sourceY, targetX, targetY, sourcePosition, targetPosition }"> <CustomConnectContent :source-x="sourceX" diff --git a/src/features/ePocFlow/nodes/AddChapterNode.vue b/src/features/ePocFlow/nodes/AddChapterNode.vue new file mode 100644 index 0000000000000000000000000000000000000000..8e29ece96d08a84297892c881f79d0e6266bc824 --- /dev/null +++ b/src/features/ePocFlow/nodes/AddChapterNode.vue @@ -0,0 +1,24 @@ +<template> + <div class="add-chapter"> + <button class="add-btn"><i class="icon-plus"></i></button> + </div> +</template> + +<style scoped lang="scss"> +.add-btn { + border-radius: 50%; + height: 30px; + width: 30px; + background-color: var(--content); + border: 1px solid var(--border); + font-size: .7rem; + cursor: pointer; + transition: box-shadow .1s ease-in-out; + &:hover { + box-shadow: 0 1px 5px var(--shadow-outer); + } + &:active { + opacity: .7; + } +} +</style> \ No newline at end of file diff --git a/src/features/ePocFlow/nodes/ChapterNode.vue b/src/features/ePocFlow/nodes/ChapterNode.vue new file mode 100644 index 0000000000000000000000000000000000000000..099b17e647b915ebbdaf4f053ce72268267f6338 --- /dev/null +++ b/src/features/ePocFlow/nodes/ChapterNode.vue @@ -0,0 +1,47 @@ +<script setup lang="ts"> +import ContentButton from '../../../components/ContentButton.vue'; +import { NodeElement } from '../../../shared/interfaces'; +import { useEditorStore } from '../../../shared/stores'; +import { Handle } from '@vue-flow/core'; + +const editorStore = useEditorStore(); + +const props = defineProps<{ + id: string; + data: { + type: object; + required: true; + } +}>(); + +const element: NodeElement = { id: props.id, action: { icon: 'icon-chapitre', type: 'chapter'}, form: editorStore.getForm('chapter') }; + +function openForm(element: NodeElement) { + editorStore.openFormPanel(element); +} + +</script> + +<template> + <div> + <ContentButton + :icon="element.action.icon" + :is-active="editorStore.formPanel.openedElement ? editorStore.formPanel.openedElement.id === element.id : false" + :is-draggable="false" + :class-list="{ 'btn-content-blue' : false, 'clickable': true, 'btn-content-node': true, 'btn-content-large': true }" + subtitle="Chapitre 1" + @click="openForm(element)" + /> + </div> + <Handle type="source" position="right" /> +</template> + +<style scoped lang="scss"> +.vue-flow__handle { + width: 12px; + height: 12px; + &-right { + right: -6px; + } +} +</style> \ No newline at end of file diff --git a/src/features/ePocFlow/nodes/ePocNode.vue b/src/features/ePocFlow/nodes/ePocNode.vue new file mode 100644 index 0000000000000000000000000000000000000000..9cc5897630a871181bf8e0d851f3bc36d6d418b1 --- /dev/null +++ b/src/features/ePocFlow/nodes/ePocNode.vue @@ -0,0 +1,34 @@ +<script setup lang="ts"> +import ContentButton from '../../../components/ContentButton.vue'; +import { NodeElement } from '../../../shared/interfaces'; +import { useEditorStore } from '../../../shared/stores'; + +const editorStore = useEditorStore(); +const props = defineProps<{ + id: string; + data: { + type: object; + required: true; + } +}>(); + +const element: NodeElement = { id: props.id, action: { icon: 'icon-epoc', type: 'epoc'}, form: editorStore.getForm('epoc') }; + +function openForm(element: NodeElement) { + editorStore.openFormPanel(element); +} + +</script> + +<template> + <div> + <ContentButton + :icon="element.action.icon" + :is-active="editorStore.formPanel.openedElement ? editorStore.formPanel.openedElement.id === element.id : false" + :is-draggable="false" + :class-list="{ 'btn-content-blue' : false, 'clickable': true, 'btn-content-node': true, 'btn-content-large': true }" + subtitle="ePoc" + @click="openForm(element)" + /> + </div> +</template> \ No newline at end of file diff --git a/src/features/forms/FormPanel.vue b/src/features/forms/FormPanel.vue index e3f1b1b749485dc7ce758e138d9be49530bbfd77..5323e2df3f2f951c0305c9a8658975e7bf829adf 100644 --- a/src/features/forms/FormPanel.vue +++ b/src/features/forms/FormPanel.vue @@ -38,6 +38,7 @@ function actionOnForm(action: string) { :label="input.label" :placeholder="input.placeholder" :accept="input.accept" + :question="input.question" :input-value="input.value" @input="input.value = $event" /> diff --git a/src/features/forms/components/inputs/CheckBoxInput.vue b/src/features/forms/components/inputs/CheckBoxInput.vue new file mode 100644 index 0000000000000000000000000000000000000000..27626b32c37e2fadabf1f7795335c4ea8afc2e29 --- /dev/null +++ b/src/features/forms/components/inputs/CheckBoxInput.vue @@ -0,0 +1,62 @@ +<script setup lang="ts"> +import { ref } from 'vue'; + +const inputValue = ref(false); +const label = ref('C\'est une bonne réponse'); + +const emit = defineEmits<{ + (e: 'input', value: string): void; +}>(); + +</script> + +<template> + <div class="checkbox"> + <input + :id="label" + class="checkbox-input" + type="checkbox" + :value="inputValue" + @input="emit('input', ($event.target as HTMLInputElement).value)" + > + <label :for="label">{{ label }}</label> + </div> +</template> + +<style scoped lang="scss"> +.checkbox { + display: flex; + margin: 0 1rem 1rem 1rem; + input[type="checkbox"] { + appearance: none; + margin: 0; + margin-right: .5rem; + font: inherit; + width: 20px; + height: 20px; + border: 1px solid var(--border); + border-radius: 4px; + transform: translateY(-0.075em); + background-color: var(--item-background); + + cursor: pointer; + + display: grid; + place-content: center; + + &:checked::before { + transform: scale(1); + } + + &::before { + content: ''; + width: 14px; + height: 14px; + transform: scale(0); + transition: .1s transform ease-in-out; + border-radius: 4px; + background-color: var(--editor-blue); + } + } +} +</style> \ No newline at end of file diff --git a/src/features/forms/components/inputs/GenericInput.vue b/src/features/forms/components/inputs/GenericInput.vue index 574a5db6984f3b1cb6a2f3ccaf06b7c1e9f63b6b..2c5f3afb3cab614e3ca0b6540d2e7eba26da398b 100644 --- a/src/features/forms/components/inputs/GenericInput.vue +++ b/src/features/forms/components/inputs/GenericInput.vue @@ -3,6 +3,7 @@ import TextInput from './TextInput.vue'; import TextAreaInput from './TextAreaInput.vue'; import FileInput from './FileInput.vue'; import AddInput from './AddInput.vue'; +import ResponseCard from './question/ReponseCard.vue'; defineProps<{ type: string; @@ -11,9 +12,14 @@ defineProps<{ placeholder?: string; accept?: string; icon?: string; + question?: { + isLast?: boolean; + pos: number; + } }>(); const emit = defineEmits<{ + //! This will surely be replaced by input event (e: 'update:modelValue', value: string): void; (e: 'input', value: string): void; }>(); @@ -46,4 +52,11 @@ const emit = defineEmits<{ :placeholder="placeholder" :label="label" /> + <ResponseCard + v-if="type === 'response'" + :pos="question.pos" + :is-last="question.isLast" + :input-value="inputValue" + @input="emit('input', $event)" + /> </template> \ No newline at end of file diff --git a/src/features/forms/components/inputs/TextAreaInput.vue b/src/features/forms/components/inputs/TextAreaInput.vue index d632d85a3f81753f502e3ba6e3dae1503608c221..e23676dccb430009fc67867abf129e2ab2e09d8d 100644 --- a/src/features/forms/components/inputs/TextAreaInput.vue +++ b/src/features/forms/components/inputs/TextAreaInput.vue @@ -4,6 +4,7 @@ defineProps<{ label: string; placeholder: string; inputValue: string; + insideCard?: boolean; }>(); const emit = defineEmits<{ @@ -17,6 +18,7 @@ const emit = defineEmits<{ <textarea :id="label" class="input input-textarea" + :class="{ 'input-card' : insideCard }" :placeholder="placeholder" :value="inputValue" @input="emit('input', ($event.target as HTMLInputElement).value)" diff --git a/src/features/forms/components/inputs/TextInput.vue b/src/features/forms/components/inputs/TextInput.vue index d9359cc1e40954882a3b7cb08d04cffef522d4a6..893666929c6d6b65f437cc2f3b5dc094e9337841 100644 --- a/src/features/forms/components/inputs/TextInput.vue +++ b/src/features/forms/components/inputs/TextInput.vue @@ -13,7 +13,7 @@ const emit = defineEmits<{ </script> <template> - <label class="input-label" :for="label">{{ label }}</label> + <label v-if="label !== ''" class="input-label" :for="label">{{ label }}</label> <input :id="label" class="input" diff --git a/src/features/forms/components/inputs/question/ReponseCard.vue b/src/features/forms/components/inputs/question/ReponseCard.vue new file mode 100644 index 0000000000000000000000000000000000000000..24d09ac57794794d0a541ede3e74727bc4cbf279 --- /dev/null +++ b/src/features/forms/components/inputs/question/ReponseCard.vue @@ -0,0 +1,83 @@ + +<script setup lang="ts"> +import TextAreaInput from '../TextAreaInput.vue'; +import CheckBoxInput from '../CheckBoxInput.vue'; + +defineProps<{ + inputValue: string; + pos: number; + isLast: boolean; +}>(); + +const emit = defineEmits<{ + (e: 'input', value: string): void; +}>(); + +</script> +<template> + <div class="card"> + <div class="card-header"> + <h3>Réponse {{ pos }}</h3> + <div class="card-header-icon"> + <i class="icon-supprimer"></i> + <hr class="vertical-separator"> + <i v-if="!isLast" class="icon-bas"></i> + <i v-if="pos !== 1" class="icon-haut"></i> + <hr class="vertical-separator"> + <i class="icon-glisser"></i> + </div> + </div> + <div class="card-content"> + <TextAreaInput + label="" + :inside-card="true" + placeholder="Saisissez une réponse..." + :input-value="inputValue" + @input="emit('input', $event)" + /> + </div> + <CheckBoxInput /> + </div> +</template> + +<style scoped lang="scss"> +.card { + border: 1px solid var(--border); + width: 25rem; + border-radius: 4px; + margin-bottom: 1rem; + &-header { + padding: 0 .7rem; + border-bottom: 1px solid var(--border); + display: flex; + flex-direction: row; + + h3 { + font-weight: bold; + font-size: 1rem; + margin: .7rem 0; + flex-grow: 1; + } + &-icon { + display: flex; + flex-direction: row; + width: fit-content; + text-align: end; + margin: auto; + hr { + margin-right: .5rem; + } + i { + color: var(--editor-grayblue); + margin: auto; + &:not(:last-child) { + margin-right: .5rem; + } + } + } + } + &-content { + padding: .7rem; + } +} +</style> \ No newline at end of file diff --git a/src/features/sideBar/SideBarV0.vue b/src/features/sideBar/SideBarV0.vue index 1a0219a8b89eb1da54524479469829134aa08f11..704f83827fbc53b07f2a5d9b827e459454f37003 100644 --- a/src/features/sideBar/SideBarV0.vue +++ b/src/features/sideBar/SideBarV0.vue @@ -1,10 +1,11 @@ <script setup lang="ts"> import ModelMenuV0 from './components/ModelMenuV0.vue'; -function startDragModel({event, sideAction}) { +function startDragModel({event, sideAction, isScreen }) { event.dataTransfer.dropEffect = 'move'; event.dataTransfer.effectAllowed = 'move'; event.dataTransfer.setData('sideAction', JSON.stringify(sideAction)); + event.dataTransfer.setData('isScreen', isScreen); } </script> diff --git a/src/features/sideBar/components/ModelMenuV0.vue b/src/features/sideBar/components/ModelMenuV0.vue index 51d6e2e3c39b94815adf479e13ac16fd8939285b..834f73cb87e78a9bca064d4492d0cbd5511411ec 100644 --- a/src/features/sideBar/components/ModelMenuV0.vue +++ b/src/features/sideBar/components/ModelMenuV0.vue @@ -8,8 +8,7 @@ import SideActionButtonV0 from './SideActionButtonV0.vue'; const editorStore = useEditorStore(); const emit = defineEmits<{ - (e: 'dragStart', { event , sideAction }): void; - (e: 'dragStartQuestion', { event, sideAction }): void; + (e: 'dragStart', { event , sideAction, isScreen }): void; }>(); const rightCol: Ref<Screen[]> = ref([]); @@ -27,7 +26,7 @@ editorStore.questions.forEach((sideAction: SideAction, index) => { }); function dragStart(event, screen) { - emit('dragStart',{ event: event, sideAction: screen.actions}); + emit('dragStart',{ event: event, sideAction: screen.actions, isScreen: true }); } const displayScreen = ref(false); diff --git a/src/global.scss b/src/global.scss index 462d285e3cbed4366357725f3597c3afc90cdac8..77070ad2db1e0bef7d30fb204e58d5ce77a8d762 100644 --- a/src/global.scss +++ b/src/global.scss @@ -68,10 +68,24 @@ body, box-shadow: 0 1px 8px 0 var(--shadow); transition: all 0.2s ease-in-out; cursor: inherit; + display: flex; + flex-direction: column; i { margin: auto; font-size: 1.5rem; } + &-large { + width: calc(60px + 2rem); + height: calc(60px + 2rem); + margin: 0; + span { + margin-top: 0; + margin: .5rem auto auto auto; + } + i { + margin-bottom: 0; + } + } &-node { &:hover { box-shadow: 0 2px 5px 0 var(--shadow-outer); @@ -223,8 +237,19 @@ hr { font-size: 1.1rem; color: var(--text); outline: none; + + &::placeholder { + color: var(--editor-grayblue); + } - width: 24rem; + &:not(.input-card) { + width: 24rem; + } + + &-card { + width: calc(100% - 1.4rem); + margin: 0; + } &-file { font-size: 1.1rem; @@ -238,6 +263,9 @@ hr { &-textarea { resize: none; min-height: 10rem; + &.input-card { + min-height: 5rem; + } } &:focus { outline: 1px solid var(--editor-blue); diff --git a/src/shared/interfaces/input.interface.ts b/src/shared/interfaces/input.interface.ts index cff9f399999f80ce52f7b9b947a634528c5892d4..a5cd64fddb83744360883170d0421a5c8ffc1f62 100644 --- a/src/shared/interfaces/input.interface.ts +++ b/src/shared/interfaces/input.interface.ts @@ -6,4 +6,8 @@ export interface Input { placeholder?: string; accept?: string; icon?: string; + question?: { + pos: number; + isLast?: boolean; + } } \ No newline at end of file diff --git a/src/shared/interfaces/nodeElement.interface.ts b/src/shared/interfaces/nodeElement.interface.ts index 6e4e4092862e78adde20bb39f6d154a1e15e5792..4b9a931646731f6e09745d47e49f942fb2a98083 100644 --- a/src/shared/interfaces/nodeElement.interface.ts +++ b/src/shared/interfaces/nodeElement.interface.ts @@ -4,5 +4,5 @@ export interface NodeElement { id: string; action: SideAction form: Form; - parentId: string; + parentId?: string; } \ No newline at end of file diff --git a/src/shared/stores/editorStore.ts b/src/shared/stores/editorStore.ts index d2bdda93c6684e12a4e054048444b98e93c63d10..7ffcb87e18f85784488d687b95b9dd27ed10afa8 100644 --- a/src/shared/stores/editorStore.ts +++ b/src/shared/stores/editorStore.ts @@ -2,7 +2,6 @@ import { defineStore } from 'pinia'; import { fetchRecentProjects } from '../services'; import { SideAction, Screen, ePocProject, NodeElement, Form } from '../interfaces'; import { toRaw } from 'vue'; -import { NodeChange, applyNodeChanges, useVueFlow } from '@vue-flow/core'; interface EditorState { recentProjects: ePocProject[]; @@ -86,10 +85,14 @@ export const useEditorStore = defineStore('editor', { return structuredClone(toRaw(this.forms.find(form => form.type === type))); }, deleteCurrentElement() { - const { nodes } = useVueFlow(); - const node = nodes[this.formPanel.openedElement.id - 1]; - const changes: NodeChange[] = [{ id: node.id, type: 'remove' }]; - applyNodeChanges(changes, nodes.value); + // const { applyNodeChanges } = useVueFlow(); + // applyNodeChanges([ + // { + // id: this.formPanel.openedElement.parentId, + // type: 'remove', + // } + // ]); + console.log(this.formPanel.openedElement.parentId); } } }); @@ -267,5 +270,172 @@ const forms: Form[] = [ action: 'delete' } ] + }, + { + type: 'qcm', + name: 'QCM', + icon: 'icon-qcm', + inputs: [ + { + type: 'text', + label: 'Titre', + value: '', + placeholder: 'Saisissez...' + }, + { + type: 'textarea', + label: 'Enoncé', + value: '', + placeholder: 'Saisissez...' + }, + { + type: 'textarea', + label: '', + value: '', + placeholder: 'Saisissez l\'intitulé de la question...' + }, + { + type: 'response', + label: '', + value: '', + placeholder: 'Saisissez une réponse...', + question: { + pos: 1, + } + }, + { + type: 'response', + label: '', + value: '', + placeholder: 'Saisissez une réponse...', + question: { + pos: 2, + isLast: true, + } + }, + { + type: 'add', + label: '', + value: '', + placeholder: 'Ajouter une autre réponse' + }, + { + type: 'textarea', + label: '', + value: '', + placeholder: 'Saisissez une explication...' + }, + ], + buttons: [ + { + label: 'Supprimer', + icon: 'icon-supprimer', + action: 'delete' + }, + { + label: 'Copier le lien', + icon: 'icon-copie', + action: 'copy' + }, + ] + }, + { + type: 'chapter', + name: 'Chapitre', + icon: 'icon-chapitre', + inputs: [ + { + type: 'text', + label: 'Titre', + value: '', + placeholder: 'Saisissez...' + }, + { + type: 'response', + label: '', + value: '', + placeholder: 'Saisissez un objectif...', + question: { + pos: 1, + isLast: false + } + }, + { + type: 'response', + label: '', + value: '', + placeholder: 'Saisissez un objectif...', + question: { + pos: 2, + isLast: true + } + }, + { + type: 'add', + label: '', + value: '', + placeholder: 'Ajouter un autre objectif' + } + ], + buttons: [ + { + label: 'Supprimer', + icon: 'icon-supprimer', + action: 'delete' + }, + { + label: 'Copier le lien', + icon: 'icon-copie', + action: 'copy' + }, + ] + }, + { + type: 'epoc', + name: 'Paramètre de l\'ePoc', + icon: 'icon-epoc', + inputs :[ + { + type: 'text', + label: 'Titre', + value: '', + placeholder: 'Saisissez...' + }, + { + type: 'file', + label: 'Image de couverture', + value: '', + accept: 'image/*' + }, + { + type: 'add', + label: 'Vignette', + value: '', + placeholder: 'Ajouter une vignette' + }, + { + type: 'add', + label: 'Teaser', + value: '', + placeholder: 'Ajouter un teaser' + }, + { + type: 'textarea', + label: 'Présentation', + value: '', + placeholder: 'Saisissez une présentation de l\'ePoc...' + }, + { + type: 'text', + label: 'ID de l\'ePoc', + value: 'id234567890', + + }, + { + type: 'text', + label: 'Version', + value: '1.0', + } + ] } ]; \ No newline at end of file