mirror of
https://github.com/pretix/pretix.git
synced 2026-10-01 19:14:43 +00:00
add DjangoDialog / notify_parent - allow to load existing views in iframe
...and some more changes
This commit is contained in:
@@ -878,7 +878,7 @@ class BaseQuestionsForm(forms.Form):
|
||||
field.answer = answers[0]
|
||||
|
||||
if qc.dependency_question_id:
|
||||
field.widget.attrs['data-question-dependency'] = qc.dependency_question_id
|
||||
field.widget.attrs['data-question-dependency'] = f"question_{qc.dependency_question.user_datafield_id}" if qc.dependency_question.user_datafield_id else qc.dependency_question.system_datafield
|
||||
field.widget.attrs['data-question-dependency-values'] = escapejson_attr(json.dumps(qc.dependency_values))
|
||||
if datafield.type != 'M':
|
||||
field.widget.attrs['required'] = qc.required and not self.all_optional
|
||||
|
||||
@@ -153,54 +153,12 @@ class QuestionForm(I18nModelForm):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
if self.instance.container_type == Question.ContainerType.ORDERPOSITION:
|
||||
self.fields['items'].queryset = self.instance.event.items.all()
|
||||
self.fields['items'].required = True
|
||||
else:
|
||||
del self.fields['items']
|
||||
del self.fields['ask_during_checkin']
|
||||
if self.instance.container_type != Question.ContainerType.ORDERPOSITION:
|
||||
del self.fields['show_during_checkin']
|
||||
del self.fields['print_on_invoice']
|
||||
self.fields['dependency_question'].widget.attrs['data-container-type'] = self.instance.container_type
|
||||
self.fields['dependency_question'].queryset = self.instance.event.questions.filter(
|
||||
type__in=(Question.TYPE_BOOLEAN, Question.TYPE_CHOICE, Question.TYPE_CHOICE_MULTIPLE),
|
||||
ask_during_checkin=False,
|
||||
container_type=self.instance.container_type,
|
||||
)
|
||||
if self.instance.pk:
|
||||
self.fields['dependency_question'].queryset = self.fields['dependency_question'].queryset.exclude(
|
||||
pk=self.instance.pk
|
||||
)
|
||||
self.fields['identifier'].required = False
|
||||
self.fields['dependency_values'].required = False
|
||||
self.fields['help_text'].widget.attrs['rows'] = 3
|
||||
|
||||
def clean_dependency_values(self):
|
||||
val = self.data.getlist('dependency_values')
|
||||
return val
|
||||
|
||||
def clean_dependency_question(self):
|
||||
dep = val = self.cleaned_data.get('dependency_question')
|
||||
if dep:
|
||||
if dep.ask_during_checkin:
|
||||
raise ValidationError(_('Question cannot depend on a question asked during check-in.'))
|
||||
|
||||
seen_ids = {self.instance.pk} if self.instance else set()
|
||||
while dep:
|
||||
if dep.pk in seen_ids:
|
||||
raise ValidationError(_('Circular dependency between questions detected.'))
|
||||
seen_ids.add(dep.pk)
|
||||
dep = dep.dependency_question
|
||||
return val
|
||||
|
||||
def clean_ask_during_checkin(self):
|
||||
val = self.cleaned_data.get('ask_during_checkin')
|
||||
|
||||
if val and self.cleaned_data.get('type') in Question.ASK_DURING_CHECKIN_UNSUPPORTED:
|
||||
raise ValidationError(_('This type of question cannot be asked during check-in.'))
|
||||
|
||||
return val
|
||||
|
||||
def clean_show_during_checkin(self):
|
||||
val = self.cleaned_data.get('show_during_checkin')
|
||||
|
||||
@@ -236,13 +194,9 @@ class QuestionForm(I18nModelForm):
|
||||
'help_text',
|
||||
'type',
|
||||
'required',
|
||||
'ask_during_checkin',
|
||||
'show_during_checkin',
|
||||
'hidden',
|
||||
'identifier',
|
||||
'items',
|
||||
'dependency_question',
|
||||
'dependency_values',
|
||||
'print_on_invoice',
|
||||
'valid_number_min',
|
||||
'valid_number_max',
|
||||
@@ -259,17 +213,11 @@ class QuestionForm(I18nModelForm):
|
||||
'valid_datetime_max': SplitDateTimePickerWidget(without_seconds=True),
|
||||
'valid_date_min': DatePickerWidget(),
|
||||
'valid_date_max': DatePickerWidget(),
|
||||
'items': forms.CheckboxSelectMultiple(
|
||||
attrs={'class': 'scrolling-multiple-choice'}
|
||||
),
|
||||
'dependency_values': forms.SelectMultiple,
|
||||
'help_text': I18nMarkdownTextarea,
|
||||
}
|
||||
field_classes = {
|
||||
'valid_datetime_min': SplitDateTimeField,
|
||||
'valid_datetime_max': SplitDateTimeField,
|
||||
'items': ItemMultipleChoiceField,
|
||||
'dependency_question': SafeModelChoiceField,
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -7,14 +7,14 @@
|
||||
{% if question %}
|
||||
{% blocktrans with name=question.question %}Data field: {{ name }}{% endblocktrans %}
|
||||
{% else %}
|
||||
{% trans "Question" %}
|
||||
{% trans "Create new data field" %}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
{% block inside %}
|
||||
{% if question %}
|
||||
<h1>{% blocktrans with name=question.question %}Data field: {{ name }}{% endblocktrans %}</h1>
|
||||
{% else %}
|
||||
<h1>{% trans "Data field" %}</h1>
|
||||
<h1>{% trans "Create new data field" %}</h1>
|
||||
{% endif %}
|
||||
<form action="" method="post" class="form-horizontal">
|
||||
{% csrf_token %}
|
||||
@@ -114,9 +114,6 @@
|
||||
</div>
|
||||
</div>
|
||||
{% bootstrap_field form.identifier layout="control" %}
|
||||
{% if form.ask_during_checkin %}
|
||||
{% bootstrap_field form.ask_during_checkin layout="control" %}
|
||||
{% endif %}
|
||||
{% if form.show_during_checkin %}
|
||||
{% bootstrap_field form.show_during_checkin layout="control" %}
|
||||
{% endif %}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
{% extends "error.html" %}
|
||||
{% load i18n %}
|
||||
{% load eventurl %}
|
||||
{% load urlreplace %}
|
||||
{% load static %}
|
||||
|
||||
{% block content %}
|
||||
<h1>{% trans "Please wait..." %}</h1>
|
||||
{{ notify_info|json_script:"notify_info" }}
|
||||
<script type="text/javascript" src="{% static "pretixcontrol/js/notify_parent.js" %}"></script>
|
||||
{% endblock %}
|
||||
@@ -21,10 +21,15 @@
|
||||
#
|
||||
import collections.abc
|
||||
import warnings
|
||||
from errno import EMSGSIZE
|
||||
|
||||
from django.contrib import messages
|
||||
from django.core.paginator import (
|
||||
EmptyPage, PageNotAnInteger, UnorderedObjectListWarning,
|
||||
)
|
||||
from django.http.response import HttpResponseRedirect
|
||||
from django.shortcuts import render
|
||||
from django.urls import reverse
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django.views.generic import edit
|
||||
|
||||
@@ -38,7 +43,22 @@ class EventBasedFormMixin:
|
||||
return kwargs
|
||||
|
||||
|
||||
class CreateView(EventBasedFormMixin, edit.CreateView):
|
||||
class TellParentFormMixin:
|
||||
|
||||
def form_valid(self, form):
|
||||
result = super().form_valid(form)
|
||||
if self.request.GET.get('notify_parent') and isinstance(result, HttpResponseRedirect):
|
||||
message_store = messages.get_messages(self.request)
|
||||
msgs = [{'level': msg.level_tag, 'message': msg.message} for msg in message_store]
|
||||
#message_store._queued_messages = []
|
||||
return render(self.request, 'pretixcontrol/notify_parent.html', {
|
||||
'notify_info': {'object': self.object.pk, 'redirect_url': result.url, 'messages': msgs},
|
||||
})
|
||||
else:
|
||||
return result
|
||||
|
||||
|
||||
class CreateView(EventBasedFormMixin, TellParentFormMixin, edit.CreateView):
|
||||
"""
|
||||
Like Django's default CreateView, but passes the optional event
|
||||
argument to the form. This is necessary for I18nModelForms to work
|
||||
@@ -47,7 +67,7 @@ class CreateView(EventBasedFormMixin, edit.CreateView):
|
||||
pass
|
||||
|
||||
|
||||
class UpdateView(EventBasedFormMixin, edit.UpdateView):
|
||||
class UpdateView(EventBasedFormMixin, TellParentFormMixin, edit.UpdateView):
|
||||
"""
|
||||
Like Django's default UpdateView, but passes the optional event
|
||||
argument to the form. This is necessary for I18nModelForms to work
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
try {
|
||||
window.parent.postMessage({
|
||||
type: 'pretix:notify-parent',
|
||||
data: JSON.parse(document.getElementById('notify_info').textContent),
|
||||
}, location.origin)
|
||||
} catch (e) {
|
||||
console.error('Could not post message to parent.', e)
|
||||
}
|
||||
@@ -1121,3 +1121,31 @@ $(function () {
|
||||
return $(this).find('button:not([type=button]), input[type=submit]').length > 0
|
||||
}).areYouSure({ message: gettext('You have unsaved changes!') })
|
||||
})
|
||||
|
||||
function notify_parent_frame() {
|
||||
window.addEventListener('message', function(e) {
|
||||
if (e.source === window) return
|
||||
if (e.origin === location.origin && e.data.type === 'pretix:dialog-handshake') {
|
||||
if (!window.isInDialog) {
|
||||
window.isInDialog = true
|
||||
window.document.documentElement.classList.add('in-iframe')
|
||||
}
|
||||
}
|
||||
if (e.origin === location.origin && e.data.type === 'pretix:dialog-loading') {
|
||||
e.source.postMessage({ type: 'pretix:dialog-handshake' })
|
||||
}
|
||||
})
|
||||
try {
|
||||
window.parent.postMessage({
|
||||
type: 'pretix:dialog-loading',
|
||||
title: document.title,
|
||||
}, location.origin)
|
||||
} catch {}
|
||||
$(function () {
|
||||
window.parent.postMessage({
|
||||
type: 'pretix:dialog-loaded',
|
||||
contentHeight: $('body').height(),
|
||||
}, location.origin)
|
||||
})
|
||||
}
|
||||
notify_parent_frame()
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
<script setup lang="ts">
|
||||
|
||||
import {gettext} from "./gettextstub";
|
||||
import NativeDialog from "./NativeDialog.vue";
|
||||
import {computed, onMounted, onUnmounted, ref, watch} from "vue";
|
||||
|
||||
const props = defineProps(['defaultUrl'])
|
||||
const emit = defineEmits(['confirm'])
|
||||
const dlgDjangoDialog = ref()
|
||||
const url = ref(props.defaultUrl)
|
||||
const frameHeight = ref(400)
|
||||
const frameLoading = ref(true)
|
||||
|
||||
function messageEvent(e: MessageEvent) {
|
||||
console.log('messageEvent', e.origin, e.source, e.data)
|
||||
if (!dlgDjangoDialog.value.visible) return
|
||||
if (e.origin === location.origin && e.data.type === 'pretix:dialog-loaded') {
|
||||
frameHeight.value = Math.min(window.innerHeight - 120, e.data.contentHeight|0)
|
||||
frameLoading.value = false
|
||||
}
|
||||
if (e.origin === location.origin && e.data.type === 'pretix:notify-parent') {
|
||||
dlgDjangoDialog.value.close()
|
||||
emit('confirm', e.data.data)
|
||||
if (e.data.data.messages) {
|
||||
alert(e.data.data.messages.map(m => m.message).join('\n\n'))
|
||||
}
|
||||
}
|
||||
}
|
||||
onMounted(() => {
|
||||
window.addEventListener('message', messageEvent)
|
||||
})
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener('message', messageEvent)
|
||||
})
|
||||
|
||||
defineExpose({
|
||||
dialog: dlgDjangoDialog,
|
||||
open: (newUrl) => {
|
||||
frameLoading.value = true
|
||||
frameHeight.value = 400
|
||||
url.value = newUrl
|
||||
dlgDjangoDialog.value.show()
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NativeDialog ref="dlgDjangoDialog" class="modal-card" no-padding="true" no-scroll="true">
|
||||
<div :style="{'height': frameHeight + 'px'}">
|
||||
<i class="fa fa-cog big-rotating-icon" v-if="frameLoading"></i>
|
||||
<iframe :src="url" v-if="dlgDjangoDialog.visible" :height="frameHeight" :style="{'visibility': frameLoading ? 'hidden' : 'visible'}"></iframe>
|
||||
</div>
|
||||
</NativeDialog>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
div { text-align: center; }
|
||||
iframe { width: 100%; border: 0; }
|
||||
</style>
|
||||
@@ -1,10 +1,10 @@
|
||||
<script setup lang="ts">
|
||||
import QuestionnaireElement from './QuestionnaireElement.vue';
|
||||
import * as api from './api';
|
||||
import { Questionnaire } from './model';
|
||||
import {Datafield, Questionnaire} from './model';
|
||||
import {i18n_any, sort, numericComp, groupBy, SYSTEM_DATAFIELDS} from './helper';
|
||||
import { gettext } from './gettextstub';
|
||||
import { ref } from 'vue';
|
||||
import {onMounted, onUnmounted, ref} from 'vue';
|
||||
import { SlickList, SlickItem } from 'vue-slicksort';
|
||||
import { ProgressBar } from "./ProgressBar";
|
||||
|
||||
@@ -17,25 +17,38 @@ console.log('items_list', items_list)
|
||||
const grouped_items = [...groupBy(items_list, item => categories[item.category])]
|
||||
console.log('grouped_items', grouped_items)
|
||||
|
||||
const all_questionnaires: (Omit<Questionnaire, 'id'> & { _new_id?: number, id?: number })[] = await api.getQuestionnaires();
|
||||
const order_questionnaires = ref(all_questionnaires.filter(q => q.type.startsWith('O')));
|
||||
const position_questionnaires = ref(all_questionnaires.filter(q => q.type.startsWith('P')));
|
||||
const order_datafields = ref(await api.getDatafields('O'));
|
||||
const position_datafields = ref((await api.getDatafields('P')).concat(Object.values(SYSTEM_DATAFIELDS)));
|
||||
type QuestionnaireMaybeUnsaved = Omit<Questionnaire, 'id'> & { _new_id?: number, id?: number };
|
||||
const order_questionnaires = ref<QuestionnaireMaybeUnsaved[]>();
|
||||
const position_questionnaires = ref<QuestionnaireMaybeUnsaved[]>();
|
||||
const order_datafields = ref<Datafield[]>();
|
||||
const position_datafields = ref<Datafield[]>();
|
||||
|
||||
let lastDataRefresh = 0
|
||||
async function refreshQuestionnaireList () {
|
||||
const all_questionnaires: QuestionnaireMaybeUnsaved[] = await api.getQuestionnaires();
|
||||
|
||||
order_questionnaires.value = all_questionnaires.filter(q => q.type.startsWith('O'))
|
||||
position_questionnaires.value = all_questionnaires.filter(q => q.type.startsWith('P'))
|
||||
lastDataRefresh = Date.now()
|
||||
}
|
||||
async function refreshDatafieldList () {
|
||||
order_datafields.value = await api.getDatafields('O')
|
||||
position_datafields.value = (await api.getDatafields('P')).concat(Object.values(SYSTEM_DATAFIELDS))
|
||||
}
|
||||
await Promise.all([refreshQuestionnaireList(), refreshDatafieldList()])
|
||||
|
||||
function saveQuestionnaire(questionnaire) {
|
||||
let result;
|
||||
questionnaire._loading = true
|
||||
if (questionnaire.id) {
|
||||
result = api.updateQuestionnaire(questionnaire.id, questionnaire);
|
||||
result = api.updateQuestionnaire(questionnaire.id, questionnaire)
|
||||
} else {
|
||||
result = api.createQuestionnaire(questionnaire).then(d => {
|
||||
questionnaire.id = d.id;
|
||||
return d;
|
||||
});
|
||||
result = api.createQuestionnaire(questionnaire)
|
||||
}
|
||||
result = result.then(d => {
|
||||
console.log(questionnaire, 'ok')
|
||||
questionnaire.id = d.id
|
||||
questionnaire.children = d.children
|
||||
questionnaire._err_mes = null
|
||||
questionnaire._loading = false
|
||||
return d;
|
||||
@@ -97,6 +110,16 @@ export default {
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
function windowFocused() {
|
||||
if (Date.now() - lastDataRefresh > 30000) {
|
||||
console.log('refreshing to avoid overwriting with older data on edit')
|
||||
refreshQuestionnaireList()
|
||||
}
|
||||
}
|
||||
onMounted(() => window.addEventListener('focus', windowFocused))
|
||||
onUnmounted(() => window.removeEventListener('focus', windowFocused))
|
||||
|
||||
const selected_product = ref("")
|
||||
const preview_mode = ref(false)
|
||||
</script>
|
||||
@@ -174,7 +197,8 @@ const preview_mode = ref(false)
|
||||
:grouped_items="null"
|
||||
:selected_product="null"
|
||||
:preview_mode="false"
|
||||
@update="saveQuestionnaire(questionnaire)" />
|
||||
@update="saveQuestionnaire(questionnaire)"
|
||||
@invalidate:datafields="refreshDatafieldList()" />
|
||||
</SlickItem>
|
||||
</SlickList>
|
||||
<div class="editor-action-row form-horizontal">
|
||||
@@ -220,7 +244,8 @@ const preview_mode = ref(false)
|
||||
:grouped_items="grouped_items"
|
||||
:selected_product="selected_product"
|
||||
:preview_mode="true"
|
||||
@update="saveQuestionnaire(questionnaire)"/>
|
||||
@update="saveQuestionnaire(questionnaire)"
|
||||
@invalidate:datafields="refreshDatafieldList()"/>
|
||||
</details>
|
||||
</div>
|
||||
|
||||
@@ -233,7 +258,8 @@ const preview_mode = ref(false)
|
||||
:grouped_items="grouped_items"
|
||||
:selected_product="selected_product"
|
||||
:preview_mode="false"
|
||||
@update="saveQuestionnaire(questionnaire)"/>
|
||||
@update="saveQuestionnaire(questionnaire)"
|
||||
@invalidate:datafields="refreshDatafieldList()"/>
|
||||
</SlickItem>
|
||||
</SlickList>
|
||||
<div v-if="!preview_mode" class="editor-action-row form-horizontal">
|
||||
|
||||
@@ -9,6 +9,8 @@ const props = defineProps({
|
||||
default: "",
|
||||
},
|
||||
title: '',
|
||||
noPadding: false,
|
||||
noScroll: true,
|
||||
});
|
||||
|
||||
const visible = ref(false);
|
||||
@@ -28,7 +30,7 @@ const id = useId();
|
||||
|
||||
<template>
|
||||
<dialog
|
||||
ref="dialog" class="modal-card"
|
||||
ref="dialog" :class="`modal-card ${props.noPadding ? 'no-padding' : ''} ${props.noPadding ? 'no-scroll' : ''}`"
|
||||
@close="visible = false"
|
||||
closedby="any"
|
||||
:aria-labelledby="`${id}-title`"
|
||||
@@ -41,9 +43,14 @@ const id = useId();
|
||||
}"
|
||||
>
|
||||
<div class="modal-card-content">
|
||||
<h2 :id="`${id}-title`" class="modal-card-title h3">{{ title }}</h2>
|
||||
<h2 :id="`${id}-title`" class="modal-card-title h3" v-if="title">{{ title }}</h2>
|
||||
<slot />
|
||||
</div>
|
||||
</form>
|
||||
</dialog>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
.modal-card.no-padding, .modal-card.no-padding .modal-card-content { padding: 0; }
|
||||
.modal-card.no-scroll { overflow: hidden; }
|
||||
</style>
|
||||
|
||||
@@ -4,15 +4,18 @@ import NativeDialog from './NativeDialog.vue';
|
||||
import I18nTextField from './I18nTextField.vue';
|
||||
import {useId, ref, computed} from 'vue'
|
||||
import { DragHandle } from 'vue-slicksort';
|
||||
import {getDatafieldEditUrl} from "./api";
|
||||
import {getDatafieldCreateUrl, getDatafieldEditUrl} from "./api";
|
||||
import I18nTextArea from "./I18nTextArea.vue";
|
||||
import DjangoDialog from "./DjangoDialog.vue";
|
||||
|
||||
const id = useId();
|
||||
const props = defineProps(['question', 'datafields', 'editable', 'possible_dependencies'])
|
||||
const emit = defineEmits(['removeSelf', 'update']);
|
||||
const emit = defineEmits(['removeSelf', 'update', 'invalidate:datafields']);
|
||||
const gettext = (window as any).gettext;
|
||||
const question = ref(props.question);
|
||||
|
||||
const dlgEditDatafield = ref()
|
||||
|
||||
const df = typeof question.value.question === 'number' ?
|
||||
props.datafields.find(el => el.id === question.value.question) :
|
||||
typeof question.value.question === 'string' ?
|
||||
@@ -106,7 +109,7 @@ const editor = ref();
|
||||
<p class="form-control-static">
|
||||
<template v-if="typeof question.question === 'number'">
|
||||
{{ df.internal_name }}
|
||||
<a :href="getDatafieldEditUrl(df.id)" target="_blank">Manage data field details</a>
|
||||
<a href="javascript:" @click="dlgEditDatafield.open(getDatafieldEditUrl(df.id))">Manage data field details</a>
|
||||
</template>
|
||||
<template v-else>
|
||||
{{ question.question }}
|
||||
@@ -157,5 +160,7 @@ const editor = ref();
|
||||
<button @click="editor.close(); emit('update')" 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>
|
||||
|
||||
<DjangoDialog ref="dlgEditDatafield" @confirm="emit('invalidate:datafields')"></DjangoDialog>
|
||||
</Teleport>
|
||||
</template>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import {useId, ref, computed} from 'vue'
|
||||
import {useId, ref, computed, onMounted} from 'vue'
|
||||
import QuestionElement from "./QuestionElement.vue";
|
||||
import {
|
||||
i18n_any,
|
||||
@@ -14,17 +14,19 @@ import I18nTextField from "./I18nTextField.vue";
|
||||
import NativeDialog from "./NativeDialog.vue";
|
||||
import { SlickList, SlickItem, DragHandle } from 'vue-slicksort';
|
||||
import {getDatafieldCreateUrl} from "./api";
|
||||
import DjangoDialog from "./DjangoDialog.vue";
|
||||
|
||||
const dlgEditor = ref()
|
||||
const dlgAddExisting = ref()
|
||||
const dlgAddTextblock = ref()
|
||||
const dlgNewDatafield = ref()
|
||||
|
||||
const newTextblockTitle = ref()
|
||||
const newTextblockText = ref()
|
||||
|
||||
const id = useId();
|
||||
const props = defineProps(['questionnaire', 'datafields', 'selected_product', 'grouped_items', 'preview_mode', 'err_mes'])
|
||||
const emit = defineEmits(['update'])
|
||||
const emit = defineEmits(['update', 'invalidate:datafields'])
|
||||
|
||||
for (let qc of props.questionnaire.children) {
|
||||
if (!qc._cid) qc._cid = useId();
|
||||
@@ -74,7 +76,12 @@ function addTextblock () {
|
||||
}
|
||||
|
||||
function newDatafield (container_type) {
|
||||
location.href = getDatafieldCreateUrl(container_type)
|
||||
dlgNewDatafield.value.dialog.show()
|
||||
}
|
||||
|
||||
async function onNewDatafieldCreated (data) {
|
||||
await emit('invalidate:datafields')
|
||||
dlgAddExisting.value.show()
|
||||
}
|
||||
|
||||
const isHidden = computed(() => props.selected_product && props.questionnaire.items.indexOf(props.selected_product) === -1)
|
||||
@@ -98,7 +105,7 @@ const isEditable = computed(() => props.selected_product && props.questionnaire.
|
||||
|
||||
<aside class="editor-action-area"><div class="btn-group">
|
||||
<DragHandle tag="button" class="btn btn-default" v-if="!preview_mode"><i class="fa fa-arrows"></i></DragHandle>
|
||||
<button class="btn btn-default" @click="dlgEditor.show()"><i class="fa fa-edit"></i></button>
|
||||
<button class="btn btn-default" @click="dlgEditor.show()"><i class="fa fa-wrench"></i></button>
|
||||
</div></aside>
|
||||
</div>
|
||||
</summary>
|
||||
@@ -113,7 +120,8 @@ const isEditable = computed(() => props.selected_product && props.questionnaire.
|
||||
:editable="true"
|
||||
:possible_dependencies="props.questionnaire.children.slice(0, index)"
|
||||
@remove-self="questionnaire.children.splice(index, 1); emit('update')"
|
||||
@update="emit('update')"/>
|
||||
@update="emit('update')"
|
||||
@invalidate:datafields="emit('invalidate:datafields')"/>
|
||||
</SlickItem>
|
||||
</SlickList>
|
||||
</div>
|
||||
@@ -217,5 +225,7 @@ const isEditable = computed(() => props.selected_product && props.questionnaire.
|
||||
<button @click="dlgAddTextblock.close()" class="btn btn-default pull-right">{{ gettext('Cancel') }}</button>
|
||||
|
||||
</NativeDialog>
|
||||
|
||||
<DjangoDialog ref="dlgNewDatafield" :default-url="getDatafieldCreateUrl(questionnaire.type[0])" @confirm="onNewDatafieldCreated"></DjangoDialog>
|
||||
</Teleport>
|
||||
</template>
|
||||
|
||||
@@ -76,9 +76,9 @@ export function getEventLocales() {
|
||||
}
|
||||
|
||||
export function getDatafieldEditUrl(datafield_id) {
|
||||
return fromJsonScript('datafield_edit_url').replace('/0/', `/${datafield_id}/`);
|
||||
return fromJsonScript('datafield_edit_url').replace('/0/', `/${datafield_id}/`) + '?notify_parent=true&';
|
||||
}
|
||||
|
||||
export function getDatafieldCreateUrl(container_type) {
|
||||
return fromJsonScript('datafield_create_url') + '?container_type=' + container_type;
|
||||
return fromJsonScript('datafield_create_url') + '?notify_parent=true&container_type=' + container_type;
|
||||
}
|
||||
|
||||
@@ -898,6 +898,9 @@ tbody th {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
html.in-iframe body { background: white }
|
||||
.in-iframe #wrapper > nav.navbar, .in-iframe #page-wrapper > div > footer { display: none; }
|
||||
|
||||
@import "../../pretixbase/scss/_rtl.scss";
|
||||
@import "../../bootstrap/scss/_rtl.scss";
|
||||
@import "_rtl.scss";
|
||||
|
||||
@@ -4,7 +4,7 @@ function questions_toggle_dependent (ev) {
|
||||
return true
|
||||
}
|
||||
|
||||
let dependency_name = $el.attr('name').split('_')[0] + '_' + $el.attr('data-question-dependency')
|
||||
let dependency_name = $el.attr('name').split('-')[0] + '-' + $el.attr('data-question-dependency')
|
||||
let dependency_values = JSON.parse($el.attr('data-question-dependency-values'))
|
||||
let $dependency_el
|
||||
|
||||
|
||||
@@ -134,14 +134,13 @@ class CategoriesTest(ItemFormTest):
|
||||
assert not ItemCategory.objects.filter(id=c.id).exists()
|
||||
|
||||
|
||||
class QuestionsTest(ItemFormTest):
|
||||
class DatafieldsTest(ItemFormTest):
|
||||
|
||||
def test_create(self):
|
||||
doc = self.get_doc('/control/event/%s/%s/questions/add' % (self.orga1.slug, self.event1.slug))
|
||||
form_data = extract_form_fields(doc.select('.container-fluid form')[0])
|
||||
form_data['question_0'] = 'What is your shoe size?'
|
||||
form_data['type'] = 'N'
|
||||
form_data['items'] = self.item1.id
|
||||
doc = self.post_doc('/control/event/%s/%s/questions/add' % (self.orga1.slug, self.event1.slug), form_data)
|
||||
assert doc.select(".alert-success")
|
||||
self.assertIn("shoe size", doc.select("#page-wrapper table")[0].text)
|
||||
@@ -157,7 +156,6 @@ class QuestionsTest(ItemFormTest):
|
||||
form_data['form-MIN_NUM_FORMS'] = '0'
|
||||
form_data['form-MAX_NUM_FORMS'] = '1'
|
||||
form_data['form-0-id'] = o1.pk
|
||||
form_data['items'] = self.item1.id
|
||||
form_data['form-0-answer_0'] = 'England'
|
||||
self.post_doc('/control/event/%s/%s/questions/%s/change' % (self.orga1.slug, self.event1.slug, c.id),
|
||||
form_data)
|
||||
@@ -176,7 +174,6 @@ class QuestionsTest(ItemFormTest):
|
||||
form_data['form-INITIAL_FORMS'] = '1'
|
||||
form_data['form-MIN_NUM_FORMS'] = '0'
|
||||
form_data['form-MAX_NUM_FORMS'] = '1'
|
||||
form_data['items'] = self.item1.id
|
||||
form_data['form-0-id'] = o1.pk
|
||||
form_data['form-0-answer_0'] = 'England'
|
||||
form_data['form-0-DELETE'] = 'yes'
|
||||
@@ -196,7 +193,6 @@ class QuestionsTest(ItemFormTest):
|
||||
form_data['form-INITIAL_FORMS'] = '0'
|
||||
form_data['form-MIN_NUM_FORMS'] = '0'
|
||||
form_data['form-MAX_NUM_FORMS'] = '1'
|
||||
form_data['items'] = self.item1.id
|
||||
form_data['form-0-id'] = ''
|
||||
form_data['form-0-answer_0'] = 'Germany'
|
||||
self.post_doc('/control/event/%s/%s/questions/%s/change' % (self.orga1.slug, self.event1.slug, c.id),
|
||||
@@ -211,7 +207,6 @@ class QuestionsTest(ItemFormTest):
|
||||
c = Question.objects.create(event=self.event1, question="What is your shoe size?", type="N", required=True)
|
||||
doc = self.get_doc('/control/event/%s/%s/questions/%s/change' % (self.orga1.slug, self.event1.slug, c.id))
|
||||
form_data = extract_form_fields(doc.select('.container-fluid form')[0])
|
||||
form_data['items'] = self.item1.id
|
||||
form_data['question_0'] = 'How old are you?'
|
||||
doc = self.post_doc('/control/event/%s/%s/questions/%s/change' % (self.orga1.slug, self.event1.slug, c.id),
|
||||
form_data)
|
||||
|
||||
Reference in New Issue
Block a user