mirror of
https://github.com/pretix/pretix.git
synced 2026-04-25 23:42:32 +00:00
adapt vue app to new data model (wip)
This commit is contained in:
@@ -1,30 +1,32 @@
|
||||
<script>
|
||||
import Question from './Question.vue';
|
||||
import {get_questions, get_items} from './api';
|
||||
import Questionnaire from './Questionnaire.vue';
|
||||
import {get_datafields, get_items, get_questionnaires} from './api';
|
||||
import { i18n_any, QUESTION_TYPE } from './helper';
|
||||
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 questions = ref(questions_response.results);
|
||||
const questionnaires = ref(questionnaires_response.results);
|
||||
const datafields = ref(datafields_response.results);
|
||||
export default {
|
||||
components: {
|
||||
Question
|
||||
Questionnaire
|
||||
},
|
||||
methods: {
|
||||
i18n_any,
|
||||
addQuestion: function() {
|
||||
questions.value.push({
|
||||
items: [], question: {en:"Untitled question"},
|
||||
type: QUESTION_TYPE.TEXT, help_text: {en:"Help text"},
|
||||
})
|
||||
console.log(questions.value)
|
||||
addQuestionnaire: function() {
|
||||
questionnaires.value.push({
|
||||
items: [], internal_name: "Unnamed questionnaire",
|
||||
type: 'PS',
|
||||
});
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
questions,
|
||||
questionnaires,
|
||||
datafields,
|
||||
items: items_response.results,
|
||||
selected_product: ref(""),
|
||||
}
|
||||
@@ -44,31 +46,25 @@ export default {
|
||||
</style>
|
||||
<template>
|
||||
|
||||
<p>
|
||||
<button class="btn btn-default" @click="addQuestion()"><i class="fa fa-plus"></i> Neue Frage erstellen</button>
|
||||
</p>
|
||||
|
||||
<div class="panel panel-default question-editor">
|
||||
<div class="panel-heading">
|
||||
Edit questions for product:
|
||||
<select v-model="selected_product">
|
||||
<option value="">(all)</option>
|
||||
<option v-for="item in items" :value="item.id">
|
||||
{{ i18n_any(item.name) }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<div class="form-horizontal">
|
||||
|
||||
<Question
|
||||
v-for="question in questions"
|
||||
:question="question"
|
||||
<p>
|
||||
Questionnaires for product:
|
||||
<select v-model="selected_product">
|
||||
<option value="">(all)</option>
|
||||
<option v-for="item in items" :value="item.id">
|
||||
{{ i18n_any(item.name) }}
|
||||
</option>
|
||||
</select>
|
||||
</p>
|
||||
<div class="question-editor">
|
||||
<Questionnaire
|
||||
v-for="questionnaire in questionnaires"
|
||||
:questionnaire="questionnaire"
|
||||
:datafields="datafields"
|
||||
:items="items"
|
||||
:selected_product="selected_product" />
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<p>
|
||||
<button class="btn btn-default" @click="addQuestionnaire()"><i class="fa fa-plus"></i> Neuen Fragebogen erstellen</button>
|
||||
</p>
|
||||
</template>
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, useId, defineProps } from 'vue';
|
||||
|
||||
defineProps(['value', 'id'])
|
||||
const props = defineProps(['value', 'id']);
|
||||
|
||||
if (!props.value) props.value = {};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
@@ -1,47 +1,47 @@
|
||||
<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 I18nTextField from './I18nTextField.vue';
|
||||
import { useId, ref } from 'vue'
|
||||
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();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="form-group"
|
||||
:class="{ 'hidden-question': selected_product && question.items.indexOf(selected_product) === -1 }">
|
||||
<div class="question-edit-buttons"><div>
|
||||
<div class="form-group">
|
||||
<div class="question-edit-buttons" v-if="editable"><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${(question.items.indexOf(selected_product) === -1) ? '-slash':''}`"></i></button>
|
||||
</div></div>
|
||||
<label class="col-md-3 control-label" :for="id" v-if="question.type != QUESTION_TYPE.BOOLEAN">
|
||||
{{ i18n_any(question.question) }}
|
||||
<label class="col-md-3 control-label" :for="id" v-if="df.type !== QUESTION_TYPE.BOOLEAN">
|
||||
{{ i18n_any(question.label) }}
|
||||
</label>
|
||||
<div v-else class="col-md-3 control-label label-empty"></div>
|
||||
<div class="col-md-9">
|
||||
|
||||
<input :id="id" type="text" v-if="question.type == QUESTION_TYPE.TEXT" class="form-control">
|
||||
<div class="checkbox" v-if="question.type == QUESTION_TYPE.BOOLEAN">
|
||||
<input :id="id" type="text" v-if="df.type === QUESTION_TYPE.STRING" class="form-control">
|
||||
<textarea :id="id" v-if="df.type === QUESTION_TYPE.TEXT" class="form-control"></textarea>
|
||||
<div class="checkbox" v-if="df.type === QUESTION_TYPE.BOOLEAN">
|
||||
<label :for="id">
|
||||
<input :id="id" type="checkbox"> {{ i18n_any(question.question) }}
|
||||
<input :id="id" type="checkbox"> {{ i18n_any(question.label) }}
|
||||
</label>
|
||||
</div>
|
||||
<input :id="id" type="number" v-if="question.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="number" v-if="df.type === QUESTION_TYPE.NUMBER" class="form-control">
|
||||
<input :id="id" type="file" v-if="df.type === QUESTION_TYPE.FILE" class="form-control">
|
||||
<select :id="id"
|
||||
v-if="question.type == QUESTION_TYPE.CHOICE || question.type == QUESTION_TYPE.CHOICE_MULTIPLE"
|
||||
:multiple="question.type == QUESTION_TYPE.CHOICE_MULTIPLE" class="form-control">
|
||||
v-if="df.type === QUESTION_TYPE.CHOICE || df.type === QUESTION_TYPE.CHOICE_MULTIPLE"
|
||||
:multiple="df.type === QUESTION_TYPE.CHOICE_MULTIPLE" class="form-control">
|
||||
<option></option>
|
||||
<option v-for="opt in question.options">{{ i18n_any(opt.answer) }}</option>
|
||||
</select>
|
||||
@@ -58,15 +58,15 @@ const editor = ref();
|
||||
Question
|
||||
</label>
|
||||
<div class="col-md-9">
|
||||
<I18nTextField :value="question.question"/>
|
||||
<I18nTextField :value="question.label"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-md-3 control-label">
|
||||
Question type
|
||||
Data field type
|
||||
</label>
|
||||
<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>
|
||||
</select>
|
||||
</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>
|
||||
</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>
|
||||
</Teleport>
|
||||
</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'),
|
||||
event_slug = document.body.getAttribute('data-event');
|
||||
|
||||
export async function get_questions() {
|
||||
return await (await fetch(`/api/v1/organizers/${organizer_slug}/events/${event_slug}/questions`)).json();
|
||||
export async function get_datafields() {
|
||||
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() {
|
||||
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) {
|
||||
if (!data) return null;
|
||||
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 = {
|
||||
NUMBER: "N",
|
||||
@@ -35,3 +39,13 @@ export const QUESTION_TYPE_LABEL = {
|
||||
COUNTRYCODE: _("Country code (ISO 3166-1 alpha-2)"),
|
||||
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