forked from CGM_Public/pretix_original
adapt vue app to new data model (wip)
This commit is contained in:
@@ -1,30 +1,32 @@
|
|||||||
<script>
|
<script>
|
||||||
import Question from './Question.vue';
|
import Questionnaire from './Questionnaire.vue';
|
||||||
import {get_questions, get_items} from './api';
|
import {get_datafields, get_items, get_questionnaires} from './api';
|
||||||
import { i18n_any, QUESTION_TYPE } from './helper';
|
import { i18n_any, QUESTION_TYPE } from './helper';
|
||||||
import { ref } from 'vue';
|
import { ref } from 'vue';
|
||||||
|
|
||||||
const questions_response = await get_questions();
|
const datafields_response = await get_datafields();
|
||||||
|
const questionnaires_response = await get_questionnaires();
|
||||||
const items_response = await get_items();
|
const items_response = await get_items();
|
||||||
|
|
||||||
const questions = ref(questions_response.results);
|
const questionnaires = ref(questionnaires_response.results);
|
||||||
|
const datafields = ref(datafields_response.results);
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
Question
|
Questionnaire
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
i18n_any,
|
i18n_any,
|
||||||
addQuestion: function() {
|
addQuestionnaire: function() {
|
||||||
questions.value.push({
|
questionnaires.value.push({
|
||||||
items: [], question: {en:"Untitled question"},
|
items: [], internal_name: "Unnamed questionnaire",
|
||||||
type: QUESTION_TYPE.TEXT, help_text: {en:"Help text"},
|
type: 'PS',
|
||||||
})
|
});
|
||||||
console.log(questions.value)
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
questions,
|
questionnaires,
|
||||||
|
datafields,
|
||||||
items: items_response.results,
|
items: items_response.results,
|
||||||
selected_product: ref(""),
|
selected_product: ref(""),
|
||||||
}
|
}
|
||||||
@@ -44,31 +46,25 @@ export default {
|
|||||||
</style>
|
</style>
|
||||||
<template>
|
<template>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
<button class="btn btn-default" @click="addQuestion()"><i class="fa fa-plus"></i> Neue Frage erstellen</button>
|
Questionnaires for product:
|
||||||
</p>
|
<select v-model="selected_product">
|
||||||
|
<option value="">(all)</option>
|
||||||
<div class="panel panel-default question-editor">
|
<option v-for="item in items" :value="item.id">
|
||||||
<div class="panel-heading">
|
{{ i18n_any(item.name) }}
|
||||||
Edit questions for product:
|
</option>
|
||||||
<select v-model="selected_product">
|
</select>
|
||||||
<option value="">(all)</option>
|
</p>
|
||||||
<option v-for="item in items" :value="item.id">
|
<div class="question-editor">
|
||||||
{{ i18n_any(item.name) }}
|
<Questionnaire
|
||||||
</option>
|
v-for="questionnaire in questionnaires"
|
||||||
</select>
|
:questionnaire="questionnaire"
|
||||||
</div>
|
:datafields="datafields"
|
||||||
<div class="panel-body">
|
:items="items"
|
||||||
<div class="form-horizontal">
|
|
||||||
|
|
||||||
<Question
|
|
||||||
v-for="question in questions"
|
|
||||||
:question="question"
|
|
||||||
:selected_product="selected_product" />
|
:selected_product="selected_product" />
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
<p>
|
||||||
</div>
|
<button class="btn btn-default" @click="addQuestionnaire()"><i class="fa fa-plus"></i> Neuen Fragebogen erstellen</button>
|
||||||
</div>
|
</p>
|
||||||
|
|
||||||
|
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, useId, defineProps } from 'vue';
|
import { ref, useId, defineProps } from 'vue';
|
||||||
|
|
||||||
defineProps(['value', 'id'])
|
const props = defineProps(['value', 'id']);
|
||||||
|
|
||||||
|
if (!props.value) props.value = {};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|||||||
@@ -1,47 +1,47 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { i18n_any, QUESTION_TYPE, QUESTION_TYPE_LABEL } from './helper';
|
import { i18n_any, QUESTION_TYPE, QUESTION_TYPE_LABEL, SYSTEM_DATAFIELDS } from './helper';
|
||||||
import NativeDialog from './NativeDialog.vue';
|
import NativeDialog from './NativeDialog.vue';
|
||||||
import I18nTextField from './I18nTextField.vue';
|
import I18nTextField from './I18nTextField.vue';
|
||||||
import { useId, ref } from 'vue'
|
import { useId, ref } from 'vue'
|
||||||
const id = useId();
|
const id = useId();
|
||||||
const props = defineProps(['question', 'selected_product'])
|
const props = defineProps(['question', 'datafields', 'editable'])
|
||||||
|
const emit = defineEmits(['removeSelf']);
|
||||||
|
|
||||||
|
const df = typeof props.question.question === 'number' ?
|
||||||
|
props.datafields.find(el => el.id === props.question.question) :
|
||||||
|
typeof props.question.question === 'string' ?
|
||||||
|
SYSTEM_DATAFIELDS[props.question.question] :
|
||||||
|
{};
|
||||||
|
|
||||||
|
if (!props.question.label) props.question.label = {};
|
||||||
|
if (!props.question.help_text) props.question.help_text = {};
|
||||||
|
|
||||||
function toggleItem() {
|
|
||||||
const i = props.question.items.indexOf(props.selected_product);
|
|
||||||
if (i === -1) {
|
|
||||||
props.question.items.push(props.selected_product);
|
|
||||||
} else {
|
|
||||||
props.question.items.splice(i, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const editor = ref();
|
const editor = ref();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="form-group"
|
<div class="form-group">
|
||||||
:class="{ 'hidden-question': selected_product && question.items.indexOf(selected_product) === -1 }">
|
<div class="question-edit-buttons" v-if="editable"><div>
|
||||||
<div class="question-edit-buttons"><div>
|
|
||||||
<button class="btn btn-default"><i class="fa fa-arrows"></i></button>
|
<button class="btn btn-default"><i class="fa fa-arrows"></i></button>
|
||||||
<button class="btn btn-default" @click="editor.show()"><i class="fa fa-edit"></i></button>
|
<button class="btn btn-default" @click="editor.show()"><i class="fa fa-edit"></i></button>
|
||||||
<button class="btn btn-default" @click="toggleItem()" v-if="selected_product"><i :class="`fa fa-eye${(question.items.indexOf(selected_product) === -1) ? '-slash':''}`"></i></button>
|
|
||||||
</div></div>
|
</div></div>
|
||||||
<label class="col-md-3 control-label" :for="id" v-if="question.type != QUESTION_TYPE.BOOLEAN">
|
<label class="col-md-3 control-label" :for="id" v-if="df.type !== QUESTION_TYPE.BOOLEAN">
|
||||||
{{ i18n_any(question.question) }}
|
{{ i18n_any(question.label) }}
|
||||||
</label>
|
</label>
|
||||||
<div v-else class="col-md-3 control-label label-empty"></div>
|
<div v-else class="col-md-3 control-label label-empty"></div>
|
||||||
<div class="col-md-9">
|
<div class="col-md-9">
|
||||||
|
<input :id="id" type="text" v-if="df.type === QUESTION_TYPE.STRING" class="form-control">
|
||||||
<input :id="id" type="text" v-if="question.type == QUESTION_TYPE.TEXT" class="form-control">
|
<textarea :id="id" v-if="df.type === QUESTION_TYPE.TEXT" class="form-control"></textarea>
|
||||||
<div class="checkbox" v-if="question.type == QUESTION_TYPE.BOOLEAN">
|
<div class="checkbox" v-if="df.type === QUESTION_TYPE.BOOLEAN">
|
||||||
<label :for="id">
|
<label :for="id">
|
||||||
<input :id="id" type="checkbox"> {{ i18n_any(question.question) }}
|
<input :id="id" type="checkbox"> {{ i18n_any(question.label) }}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<input :id="id" type="number" v-if="question.type == QUESTION_TYPE.NUMBER" class="form-control">
|
<input :id="id" type="number" v-if="df.type === QUESTION_TYPE.NUMBER" class="form-control">
|
||||||
<input :id="id" type="file" v-if="question.type == QUESTION_TYPE.FILE" class="form-control">
|
<input :id="id" type="file" v-if="df.type === QUESTION_TYPE.FILE" class="form-control">
|
||||||
<select :id="id"
|
<select :id="id"
|
||||||
v-if="question.type == QUESTION_TYPE.CHOICE || question.type == QUESTION_TYPE.CHOICE_MULTIPLE"
|
v-if="df.type === QUESTION_TYPE.CHOICE || df.type === QUESTION_TYPE.CHOICE_MULTIPLE"
|
||||||
:multiple="question.type == QUESTION_TYPE.CHOICE_MULTIPLE" class="form-control">
|
:multiple="df.type === QUESTION_TYPE.CHOICE_MULTIPLE" class="form-control">
|
||||||
<option></option>
|
<option></option>
|
||||||
<option v-for="opt in question.options">{{ i18n_any(opt.answer) }}</option>
|
<option v-for="opt in question.options">{{ i18n_any(opt.answer) }}</option>
|
||||||
</select>
|
</select>
|
||||||
@@ -58,15 +58,15 @@ const editor = ref();
|
|||||||
Question
|
Question
|
||||||
</label>
|
</label>
|
||||||
<div class="col-md-9">
|
<div class="col-md-9">
|
||||||
<I18nTextField :value="question.question"/>
|
<I18nTextField :value="question.label"/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="col-md-3 control-label">
|
<label class="col-md-3 control-label">
|
||||||
Question type
|
Data field type
|
||||||
</label>
|
</label>
|
||||||
<div class="col-md-9">
|
<div class="col-md-9">
|
||||||
<select v-model="question.type" class="form-control">
|
<select v-model="df.type" class="form-control">
|
||||||
<option v-for="(label, type) in QUESTION_TYPE_LABEL" :value="QUESTION_TYPE[type]">{{ label }}</option>
|
<option v-for="(label, type) in QUESTION_TYPE_LABEL" :value="QUESTION_TYPE[type]">{{ label }}</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
@@ -80,7 +80,8 @@ const editor = ref();
|
|||||||
<div class="help-block">Wenn diese Frage noch weitere Erklärung braucht, können Sie sie hier eintragen.</div>
|
<div class="help-block">Wenn diese Frage noch weitere Erklärung braucht, können Sie sie hier eintragen.</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button @click="editor.close()">Close</button>
|
<button @click="editor.close()" class="btn btn-primary pull-right"><span class="fa fa-check"></span> Save and close</button>
|
||||||
|
<button @click="emit('removeSelf')" class="btn btn-default">Remove from questionnaire</button>
|
||||||
</NativeDialog>
|
</NativeDialog>
|
||||||
</Teleport>
|
</Teleport>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -0,0 +1,86 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import {useId, ref, computed} from 'vue'
|
||||||
|
import Question from "./Question.vue";
|
||||||
|
import {i18n_any, QUESTION_TYPE, QUESTION_TYPE_LABEL} from "./helper";
|
||||||
|
import I18nTextField from "./I18nTextField.vue";
|
||||||
|
import NativeDialog from "./NativeDialog.vue";
|
||||||
|
const id = useId();
|
||||||
|
const props = defineProps(['questionnaire', 'datafields', 'selected_product', 'items'])
|
||||||
|
|
||||||
|
function toggleItem() {
|
||||||
|
const i = props.questionnaire.items.indexOf(props.selected_product);
|
||||||
|
if (i === -1) {
|
||||||
|
props.questionnaire.items.push(props.selected_product);
|
||||||
|
} else {
|
||||||
|
props.questionnaire.items.splice(i, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const isHidden = computed(() => props.selected_product && props.questionnaire.items.indexOf(props.selected_product) === -1);
|
||||||
|
const isEditable = computed(() => props.selected_product && props.questionnaire.items.indexOf(props.selected_product) !== -1);
|
||||||
|
|
||||||
|
const editor = ref();
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="question-edit-buttons"><div>
|
||||||
|
<button class="btn btn-default"><i class="fa fa-arrows"></i></button>
|
||||||
|
<button class="btn btn-default" @click="editor.show()"><i class="fa fa-edit"></i></button>
|
||||||
|
<button class="btn btn-default" @click="toggleItem()" v-if="selected_product"><i :class="`fa fa-eye${isHidden ? '-slash':''}`"></i></button>
|
||||||
|
</div></div>
|
||||||
|
|
||||||
|
<details class="panel panel-default " :open="!!isEditable"
|
||||||
|
:class="{ 'hidden-question': isHidden }">
|
||||||
|
<summary class="panel-heading">
|
||||||
|
{{ props.questionnaire.internal_name }}
|
||||||
|
</summary>
|
||||||
|
<div class="panel-body" v-if="!isHidden">
|
||||||
|
<div class="form-horizontal">
|
||||||
|
|
||||||
|
<Question
|
||||||
|
v-for="(child, index) in props.questionnaire.children" :key="index"
|
||||||
|
:datafields="props.datafields"
|
||||||
|
:question="child"
|
||||||
|
:editable="true"
|
||||||
|
@remove-self="questionnaire.children.splice(index, 1)" />
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<p v-if="true">
|
||||||
|
<button class="btn btn-default" @click="addExistingDatafield()"><i class="fa fa-plus"></i> Bestehendes Datenfeld hinzufügen</button>
|
||||||
|
<button class="btn btn-default" @click="newDatafield()"><i class="fa fa-plus"></i> Neues Datenfeld</button>
|
||||||
|
<button class="btn btn-default" @click="addSubtitle()"><i class="fa fa-plus"></i> Zwischenüberschrift</button>
|
||||||
|
<button class="btn btn-default" @click="addTextBlock()"><i class="fa fa-plus"></i> Text</button>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<Teleport to="body">
|
||||||
|
<NativeDialog ref="editor" class="modal-card"
|
||||||
|
title="Edit questionnaire">
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="col-md-3 control-label">
|
||||||
|
Internal name
|
||||||
|
</label>
|
||||||
|
<div class="col-md-9">
|
||||||
|
<input type="text" class="form-control" v-model="questionnaire.internal_name"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="col-md-3 control-label">
|
||||||
|
Visible on products
|
||||||
|
</label>
|
||||||
|
<div class="col-md-9">
|
||||||
|
<div class="checkbox" v-for="item in items">
|
||||||
|
<label :for="id + '_' + item.id">
|
||||||
|
<input :id="id + '_' + item.id" type="checkbox" :checked="questionnaire.items.indexOf(item.id) !== -1"> {{ item.internal_name || i18n_any(item.name) }}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button @click="editor.close()" class="btn btn-primary pull-right"><span class="fa fa-check"></span> Save and close</button>
|
||||||
|
<button class="btn btn-default">Delete</button>
|
||||||
|
</NativeDialog>
|
||||||
|
</Teleport>
|
||||||
|
</template>
|
||||||
@@ -1,10 +1,14 @@
|
|||||||
const organizer_slug = document.body.getAttribute('data-organizer'),
|
const organizer_slug = document.body.getAttribute('data-organizer'),
|
||||||
event_slug = document.body.getAttribute('data-event');
|
event_slug = document.body.getAttribute('data-event');
|
||||||
|
|
||||||
export async function get_questions() {
|
export async function get_datafields() {
|
||||||
return await (await fetch(`/api/v1/organizers/${organizer_slug}/events/${event_slug}/questions`)).json();
|
return await (await fetch(`/api/v1/organizers/${organizer_slug}/events/${event_slug}/datafields/`)).json();
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function get_questionnaires() {
|
||||||
|
return await (await fetch(`/api/v1/organizers/${organizer_slug}/events/${event_slug}/questionnaires/`)).json();
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function get_items() {
|
export async function get_items() {
|
||||||
return await (await fetch(`/api/v1/organizers/${organizer_slug}/events/${event_slug}/items`)).json();
|
return await (await fetch(`/api/v1/organizers/${organizer_slug}/events/${event_slug}/items/`)).json();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,12 @@
|
|||||||
|
|
||||||
export function i18n_any(data) {
|
export function i18n_any(data) {
|
||||||
|
if (!data) return null;
|
||||||
return Object.values(data)[0];
|
return Object.values(data)[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function freezeRec(o) {
|
||||||
|
return Object.freeze(Object.fromEntries(Object.entries(o).map(([k, v]) => [k, v && Object.getPrototypeOf(v) === Object.prototype ? freezeRec(v) : v])))
|
||||||
|
}
|
||||||
|
|
||||||
export const QUESTION_TYPE = {
|
export const QUESTION_TYPE = {
|
||||||
NUMBER: "N",
|
NUMBER: "N",
|
||||||
@@ -35,3 +39,13 @@ export const QUESTION_TYPE_LABEL = {
|
|||||||
COUNTRYCODE: _("Country code (ISO 3166-1 alpha-2)"),
|
COUNTRYCODE: _("Country code (ISO 3166-1 alpha-2)"),
|
||||||
PHONENUMBER: _("Phone number"),
|
PHONENUMBER: _("Phone number"),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const SYSTEM_DATAFIELDS = freezeRec({
|
||||||
|
'attendee_name_parts': { label: _('Attendee name'), type: QUESTION_TYPE.STRING },
|
||||||
|
'attendee_email': { label: _('Attendee email'), type: QUESTION_TYPE.STRING },
|
||||||
|
'company': { label: _('Company'), type: QUESTION_TYPE.STRING },
|
||||||
|
'street': { label: _('Street'), type: QUESTION_TYPE.STRING },
|
||||||
|
'zipcode': { label: _('ZIP code'), type: QUESTION_TYPE.STRING },
|
||||||
|
'city': { label: _('City'), type: QUESTION_TYPE.STRING },
|
||||||
|
'country': { label: _('Country'), type: QUESTION_TYPE.COUNTRYCODE },
|
||||||
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user