mirror of
https://github.com/pretix/pretix.git
synced 2026-10-01 19:14:43 +00:00
Improve UI for changing Questionnaire sales_channels
This commit is contained in:
@@ -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[]][];
|
||||
|
||||
Reference in New Issue
Block a user