Improve UI for changing Questionnaire sales_channels

This commit is contained in:
Mira Weller
2026-09-30 21:03:28 +02:00
parent 449fd0ac35
commit 3e087a4c9c
6 changed files with 54 additions and 5 deletions
+8 -1
View File
@@ -26,6 +26,7 @@ from django.conf import settings
from django.core.exceptions import ObjectDoesNotExist
from django.db import transaction
from django.db.models import Q
from django.templatetags.static import static
from django.utils.crypto import get_random_string
from django.utils.translation import gettext, gettext_lazy as _
from i18nfield.rest_framework import I18nField
@@ -219,10 +220,12 @@ class FlexibleTicketRelatedField(serializers.PrimaryKeyRelatedField):
class SalesChannelSerializer(I18nAwareModelSerializer):
type = serializers.CharField(default="api")
icon_url = serializers.SerializerMethodField()
class Meta:
model = SalesChannel
fields = ('identifier', 'type', 'label', 'position')
fields = ('identifier', 'type', 'label', 'position', 'icon', 'icon_url')
read_only_fields = ('icon',)
def validate_type(self, value):
if (not self.instance or not self.instance.pk) and value != "api":
@@ -246,6 +249,10 @@ class SalesChannelSerializer(I18nAwareModelSerializer):
)
return value
def get_icon_url(self, instance):
if '.' in instance.icon:
return static(instance.icon)
class GiftCardSerializer(I18nAwareModelSerializer):
value = serializers.DecimalField(max_digits=13, decimal_places=2, min_value=Decimal('0.00'))
@@ -3,6 +3,7 @@ import {gettext} from "./gettextstub";
import {GroupedItems, Questionnaire, SalesChannel} from "./model";
import {inject, Ref, useId} from "vue";
import {i18n_any, QUESTIONNAIRE_TYPE, QUESTIONNAIRE_TYPE_LABEL, setListState} from "./helper";
import SalesChannelIcon from "./SalesChannelIcon.vue";
const sales_channels: Ref<SalesChannel[]> = inject('pretix:env:organizer:sales_channels')
@@ -26,15 +27,17 @@ const props = defineProps<{
</div>
<div class="form-group">
<label class="col-md-3 control-label">
{{ gettext('Where to ask') }}
{{ gettext('When to ask') }}
</label>
<div class="col-md-9">
<select v-model="questionnaire.type" class="form-control">
<option v-for="(label, type) in QUESTIONNAIRE_TYPE_LABEL" :value="QUESTIONNAIRE_TYPE[type]">{{ label }}</option>
<template v-for="(label, type) in QUESTIONNAIRE_TYPE_LABEL">
<option v-if="QUESTIONNAIRE_TYPE[type][0] == questionnaire.type[0]" :value="QUESTIONNAIRE_TYPE[type]">{{ label }}</option>
</template>
</select>
</div>
</div>
<div class="form-group">
<div class="form-group" v-if="questionnaire.type[1] == 'S'">
<label class="col-md-3 control-label">
{{ gettext('Sales channels') }}
</label>
@@ -49,6 +52,7 @@ const props = defineProps<{
<input type="checkbox" :checked="questionnaire.all_sales_channels || questionnaire.limit_sales_channels.indexOf(channel.identifier) !== -1"
@change="e => setListState(questionnaire.limit_sales_channels, e.target.checked, channel.identifier)"
:disabled="questionnaire.all_sales_channels">
<SalesChannelIcon :sales-channel="channel"/>
{{ i18n_any(channel.label) }}
</label>
</div>
@@ -1,5 +1,5 @@
<script setup lang="ts">
import {useId, ref, computed, onMounted, watch} from 'vue'
import {useId, ref, computed, onMounted, watch, Ref, inject} from 'vue'
import QuestionElement from "./QuestionElement.vue";
import {
i18n_any,
@@ -17,6 +17,8 @@ import {getDatafieldCreateUrl} from "./api";
import DjangoDialog from "./DjangoDialog.vue";
import * as api from './api';
import QuestionnaireDetailForm from "./QuestionnaireDetailForm.vue";
import SalesChannelIcon from "./SalesChannelIcon.vue";
import {SalesChannel} from "./model";
const dlgEditor = ref()
const dlgAddExisting = ref()
@@ -29,6 +31,7 @@ const newTextblockText = ref()
const id = useId();
const props = defineProps(['questionnaire', 'datafields', 'selected_product', 'grouped_items', 'preview_mode', 'err_mes'])
const emit = defineEmits(['update', 'invalidate:datafields', 'invalidate:questionnaires'])
const sales_channels: Ref<SalesChannel[]> = inject('pretix:env:organizer:sales_channels')
let nextId = 1;
watch(() => props.questionnaire.children, () => {
@@ -104,6 +107,9 @@ const isEditable = computed(() => props.selected_product && props.questionnaire.
</script>
<style>
.sales-channel-icons > * { margin-left: 4px; opacity: 0.8; }
</style>
<template>
@@ -116,6 +122,19 @@ const isEditable = computed(() => props.selected_product && props.questionnaire.
{{ props.questionnaire.internal_name }}
<span class="fa fa-warning" v-if="questionnaire._err_mes"></span>
<span class="fa fa-cog fa-spin" v-if="questionnaire._loading"></span>
<span class="pull-right" v-if="questionnaire.type === 'PC'">
<span class="label label-default">{{ gettext('Check-in') }}</span>
</span>
<span class="pull-right" v-else-if="questionnaire.type === 'PA'">
<span class="label label-default">{{ gettext('Attendee link') }}</span>
</span>
<span class="pull-right sales-channel-icons" v-else>
<template v-for="sc in sales_channels">
<SalesChannelIcon :sales-channel="sc" :tooltip="true"
v-if="props.questionnaire.all_sales_channels || props.questionnaire.limit_sales_channels.indexOf(sc.identifier) !== -1" />
</template>
</span>
</div>
<aside class="editor-action-area"><div class="btn-group">
@@ -0,0 +1,16 @@
<script setup lang="ts">
import {SalesChannel} from "./model";
import {i18n_any} from "./helper";
const props = defineProps<{ salesChannel: SalesChannel, tooltip?: boolean }>();
</script>
<template>
<template v-if="salesChannel.icon_url">
<img :src="salesChannel.icon_url" class="fa-like-image" :alt="i18n_any(salesChannel.label)" :data-toggle="tooltip ? 'tooltip' : ''" :title="tooltip ? i18n_any(salesChannel.label) : ''">
</template>
<template v-else>
<span :class="`fa fa-${salesChannel.icon} fa-fw text-muted`" :data-toggle="tooltip ? 'tooltip' : ''" :title="tooltip ? i18n_any(salesChannel.label) : ''"></span>
</template>
</template>
@@ -88,6 +88,7 @@ export function getEventLocales() {
export function getDatafieldViewUrl(datafield_id) {
return fromJsonScript('datafield_view_url').replace('/0/', `/${datafield_id}/`);
}
export function getDatafieldEditUrl(datafield_id) {
return fromJsonScript('datafield_edit_url').replace('/0/', `/${datafield_id}/`) + '?notify_parent=true&';
}
@@ -76,6 +76,8 @@ export type SalesChannel = {
type: string,
label: I18nString,
position: number,
icon: string,
icon_url: string | null,
}
export type GroupedItems = [Category, Item[]][];