mirror of
https://github.com/pretix/pretix.git
synced 2026-08-13 11:17:01 +00:00
WIP
prettify & cleanup preview support multiline fields in preview add venue store active state for predefined fieldgroups
This commit is contained in:
@@ -28,7 +28,7 @@ function openForm(url: string, data: Record<string, string>) {
|
||||
document.body.removeChild(form);
|
||||
}
|
||||
|
||||
function openPreview(e: SubmitEvent) {
|
||||
function openPreview(e: Event) {
|
||||
e.preventDefault();
|
||||
openForm("../../preview/", {
|
||||
csrfmiddlewaretoken: store.csrfToken,
|
||||
@@ -78,7 +78,7 @@ const preview_layout = computed(() => {
|
||||
.panel-body
|
||||
div(v-if="preview_layout")
|
||||
span.text-muted The preview below is only a rough representation of what the pass might look like. Please check the generated pass.
|
||||
div
|
||||
div(style="display: grid; gap: 1em; grid-template-columns: repeat(auto-fit, minmax(auto, 360px));")
|
||||
PassPreview(v-for="layout in preview_layout" :layout="layout")
|
||||
div(v-else) Preview not supported
|
||||
//- pre
|
||||
@@ -98,6 +98,7 @@ const preview_layout = computed(() => {
|
||||
.walletsettings-panel .panel-heading {
|
||||
.checkbox {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
display: inline-block;
|
||||
input[type=checkbox] {
|
||||
margin-top: 0;
|
||||
@@ -106,6 +107,9 @@ const preview_layout = computed(() => {
|
||||
position: relative;
|
||||
top: 1px;
|
||||
}
|
||||
label {
|
||||
padding-left: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
+1
-1
@@ -22,7 +22,7 @@ watchEffect(() => {
|
||||
.panel-heading
|
||||
h3.panel-title.form-inline
|
||||
.form-group
|
||||
Checkbox(:label="fieldgroup.name" v-model="fieldConfig.active")
|
||||
Checkbox(v-if="!!fieldConfig" :label="fieldgroup.name" v-model="fieldConfig.active")
|
||||
.panel-body(v-if="!!fieldgroup.description")
|
||||
.form-group
|
||||
span.text-muted {{ fieldgroup.description }}
|
||||
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, inject, reactive, watchEffect } from "vue";
|
||||
import { StoreKey } from "../../walletStore";
|
||||
import { i18nstringLocalize } from "../../helpers";
|
||||
|
||||
const store = inject(StoreKey)!;
|
||||
|
||||
const props = defineProps<{
|
||||
label: I18nString;
|
||||
content: I18nString;
|
||||
content_type: FieldContentType;
|
||||
display: FieldGroupDisplay;
|
||||
display_class?: string | string[];
|
||||
}>();
|
||||
</script>
|
||||
|
||||
<template lang="pug">
|
||||
div.fieldgroup-item
|
||||
div.fieldgroup-item-qrcode(v-if="display == 'code'") {{ label }}
|
||||
|
||||
template(v-else)
|
||||
div.fieldgroup-label.nowrap(v-if="display == 'with_label'") {{ i18nstringLocalize(label) }}
|
||||
div.nowrap.content(v-if="content_type == 'text'" :class="display_class") {{ i18nstringLocalize(content) }}
|
||||
img.fieldgroup-item-image(v-else-if="content_type == 'image' && content" :src="i18nstringLocalize(content)")
|
||||
</template>
|
||||
-18
@@ -22,22 +22,4 @@ const style_def = computed(() => {
|
||||
<template lang="pug">
|
||||
PlaceholderFieldgroupPreview(v-if="style_def && style_def.type == 'placeholder'" :config="config" :style_def="style_def")
|
||||
PredefinedFieldgroupPreview(v-else-if="style_def && style_def.type == 'predefined'" :config="config" :style_def="style_def")
|
||||
//- // TODO: support predefined group
|
||||
//- div(:style="{'flex-grow': config.relSize}")
|
||||
//- div(style="background-color: gray; display: flex; flex-direction: row;")
|
||||
//- div(v-for="entry in layout.entries.slice(0,style_def.max_entries)")
|
||||
//- div(v-if="style_def.labels") {{ i18nstringLocalize(entry.label) }}
|
||||
//- div(v-if="entry.type == 'custom'") {{ i18nstringLocalize(entry.content) }}
|
||||
//- div(v-if="entry.type == 'placeholder'") {{ store.variables[style_def.content_type][entry.content] }}
|
||||
//- pre
|
||||
//- //- code {{ config }}
|
||||
//- code {{ style_def }}
|
||||
//- code {{ layout }}
|
||||
//- div(style="position: absolute; background-color: red; top: 0.5cm; left: 0cm; height: 1cm; width: 2cm;") Logo
|
||||
//- div(style="position: absolute; background-color: red; top: 0.5cm; left: 2.25cm; height: 1cm; width: 5.5cm;") Logo Text
|
||||
//- div(style="position: absolute; background-color: red; top: 0.5cm; left: 8cm; height: 1cm; width: 2cm;") Header
|
||||
//- div(style="position: absolute; background-color: red; top: 2.5cm; left: 0cm; height: 2cm; width: 10cm;") Primary
|
||||
//- div(style="position: absolute; background-color: red; top: 4.75cm; left: 0cm; height: 1cm; width: 10cm;") Secondary
|
||||
//- div(style="position: absolute; background-color: red; top: 7cm; left: 0cm; height: 1cm; width: 10cm;") Auxiliary
|
||||
//- div(style="position: absolute; background-color: red; top: 10cm; left: 3cm; height: 4cm; width: 4cm;")
|
||||
</template>
|
||||
|
||||
+59
-5
@@ -16,19 +16,18 @@ const props = defineProps<{ layout: Array<PreviewLayout> }>();
|
||||
|
||||
<style lang="css" scoped>
|
||||
.pass-container {
|
||||
margin: 1em;
|
||||
|
||||
width: 100%;
|
||||
aspect-ratio: 9 / 11;
|
||||
max-width: 360px;
|
||||
vertical-align: top;
|
||||
display: inline-block;
|
||||
|
||||
outline: solid 1px #949494;
|
||||
border: solid 1px #949494;
|
||||
border-radius: 1em;
|
||||
|
||||
padding: 1em;
|
||||
|
||||
overflow: hidden
|
||||
}
|
||||
|
||||
.pass-content {
|
||||
overflow: scroll;
|
||||
display: flex;
|
||||
@@ -36,3 +35,58 @@ const props = defineProps<{ layout: Array<PreviewLayout> }>();
|
||||
gap: 1em;
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="css">
|
||||
.pass-container {
|
||||
.fieldgroup-container {
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
gap: 0.5em;
|
||||
min-height: 2lh;
|
||||
}
|
||||
.fieldgroup-label {
|
||||
font-weight: bold;
|
||||
font-size: 0.8em;
|
||||
}
|
||||
.fieldgroup-item {
|
||||
flex: 0 1 100%;
|
||||
min-width: 0;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
.fieldgroup-item-image {
|
||||
max-height: 3lh;
|
||||
}
|
||||
.fieldgroup-item-qrcode {
|
||||
font-weight: bold;
|
||||
background-color: lightgray;
|
||||
width: 50%;
|
||||
aspect-ratio: 1;
|
||||
margin: auto;
|
||||
/* center content */
|
||||
display: flex;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
justify-content: center;
|
||||
padding: 1em;
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
.nowrap {
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
text-wrap: nowrap;
|
||||
}
|
||||
.bold {
|
||||
font-weight: bold;
|
||||
}
|
||||
.large {
|
||||
font-size: 1.5em;
|
||||
}
|
||||
.tight {
|
||||
line-height: 1;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
+2
-62
@@ -1,7 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, inject, reactive, watchEffect } from "vue";
|
||||
import { StoreKey } from "../../walletStore";
|
||||
import { i18nstringLocalize } from "../../helpers";
|
||||
import FieldgroupItemPreview from "./fieldgroup-item-preview.vue";
|
||||
|
||||
const store = inject(StoreKey)!;
|
||||
|
||||
@@ -13,65 +13,5 @@ const props = defineProps<{
|
||||
|
||||
<template lang="pug">
|
||||
div.fieldgroup-container(:style="{'flex-grow': config.relSize, 'flex-direction': config.direction || 'row'}")
|
||||
div.fieldgroup-item(v-for="{ entry, label, content } of store.currentLayoutFieldContent[config.fieldgroup]")
|
||||
div.fieldgroup-item-qrcode(v-if="style_def.display == 'code'") {{ label }}
|
||||
|
||||
template(v-else)
|
||||
div.fieldgroup-label.nowrap(v-if="style_def.display == 'with_label'") {{ label }}
|
||||
div.nowrap.content(v-if="style_def.content_type == 'text'" :class="config.display") {{ content }}
|
||||
img.fieldgroup-item-image(v-else-if="style_def.content_type == 'image' && content" :src="i18nstringLocalize(content)")
|
||||
|
||||
|
||||
FieldgroupItemPreview(v-for="{ label, content } of store.currentLayoutFieldContent[config.fieldgroup]" :label="label" :content="content" :content_type="style_def.content_type" :display_class="config.display" :display="style_def.display")
|
||||
</template>
|
||||
|
||||
<style lang="css" scoped>
|
||||
.fieldgroup-container {
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
gap: 0.5em;
|
||||
min-height: 2lh;
|
||||
}
|
||||
.fieldgroup-label {
|
||||
font-weight: bold;
|
||||
font-size: 0.8em;
|
||||
}
|
||||
.fieldgroup-item {
|
||||
flex: 0 1 100%;
|
||||
min-width: 0;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
}
|
||||
.fieldgroup-item-image {
|
||||
max-height: 3lh;
|
||||
}
|
||||
.fieldgroup-item-qrcode {
|
||||
font-weight: bold;
|
||||
background-color: lightgray;
|
||||
width: 50%;
|
||||
aspect-ratio: 1;
|
||||
margin: auto;
|
||||
/* center content */
|
||||
display: flex;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
justify-content: center;
|
||||
padding: 1em;
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
.nowrap {
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
text-wrap: nowrap;
|
||||
}
|
||||
.bold {
|
||||
font-weight: bold;
|
||||
}
|
||||
.large {
|
||||
font-size: 1.5em;
|
||||
}
|
||||
.tight {
|
||||
line-height: 1;
|
||||
}
|
||||
</style>
|
||||
+4
-60
@@ -1,13 +1,13 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, inject, reactive, watchEffect } from "vue";
|
||||
import { StoreKey } from "../../walletStore";
|
||||
import { i18nstringLocalize } from "../../helpers";
|
||||
import FieldgroupItemPreview from "./fieldgroup-item-preview.vue";
|
||||
|
||||
const store = inject(StoreKey)!;
|
||||
|
||||
const props = defineProps<{
|
||||
config: PredefinedFieldGroupConfig;
|
||||
style_def: PredefinedFieldGroupDefinition;
|
||||
config: PredefinedFieldgroupPreview;
|
||||
}>();
|
||||
|
||||
const isActive = computed(
|
||||
@@ -18,61 +18,5 @@ const isActive = computed(
|
||||
|
||||
<template lang="pug">
|
||||
div.fieldgroup-container(v-if="isActive" :style="{'flex-grow': config.relSize, 'flex-direction': config.direction || 'row'}")
|
||||
div.fieldgroup-item(v-for="{ label, content } of config.sample")
|
||||
div.fieldgroup-label.nowrap(v-if="!!label") {{ i18nstringLocalize(label) }}
|
||||
div.nowrap.content(v-if="!!content" :class="config.display") {{ i18nstringLocalize(content) }}
|
||||
|
||||
|
||||
</template>
|
||||
|
||||
<style lang="css" scoped>
|
||||
.fieldgroup-container {
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
gap: 0.5em;
|
||||
min-height: 2lh;
|
||||
}
|
||||
.fieldgroup-label {
|
||||
font-weight: bold;
|
||||
font-size: 0.8em;
|
||||
}
|
||||
.fieldgroup-item {
|
||||
flex: 0 1 100%;
|
||||
min-width: 0;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
}
|
||||
.fieldgroup-item-image {
|
||||
max-height: 3lh;
|
||||
}
|
||||
.fieldgroup-item-qrcode {
|
||||
font-weight: bold;
|
||||
background-color: lightgray;
|
||||
width: 50%;
|
||||
aspect-ratio: 1;
|
||||
margin: auto;
|
||||
/* center content */
|
||||
display: flex;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
padding: 1em;
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
|
||||
.nowrap {
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
text-wrap: nowrap;
|
||||
}
|
||||
.bold {
|
||||
font-weight: bold;
|
||||
}
|
||||
.large {
|
||||
font-size: 1.5em;
|
||||
}
|
||||
.tight {
|
||||
line-height: 1;
|
||||
}
|
||||
</style>
|
||||
FieldgroupItemPreview(v-for="{ label, content } of config.sample" :label="label" :content="content" content_type="text" display="with_label")
|
||||
</template>
|
||||
+1
-1
@@ -3,7 +3,7 @@ import FieldgroupPreview from './fieldgroup-preview.vue';
|
||||
import FixedPreview from './fixed-preview.vue';
|
||||
|
||||
const props = defineProps<{
|
||||
config: Record<string, string>;
|
||||
config: PreviewRow;
|
||||
}>();
|
||||
|
||||
</script>
|
||||
|
||||
@@ -19,7 +19,7 @@ type PlaceholderFieldGroupDefinition = BaseFieldGroupDefinition & {
|
||||
display: FieldGroupDisplay;
|
||||
min_entries: number | null;
|
||||
max_entries: number | null;
|
||||
context_args: string[]
|
||||
context_args: string[];
|
||||
};
|
||||
|
||||
type PredefinedFieldGroupDefinition = BaseFieldGroupDefinition & {
|
||||
@@ -104,10 +104,32 @@ type WalletStore = {
|
||||
};
|
||||
|
||||
type PreviewLayout = Array<PreviewRow>;
|
||||
type PreviewRow = { children: Array<PreviewRow> } | PreviewFieldgroup;
|
||||
type PreviewFieldgroup = {
|
||||
fieldgroup: string;
|
||||
type PreviewRow =
|
||||
| { children: Array<PreviewRow>; direction?: "row" | "column"; display?: Array<string>;}
|
||||
| PreviewFieldgroup
|
||||
| FixedPreview;
|
||||
|
||||
type PreviewProps = {
|
||||
relSize?: number;
|
||||
direction?: 'row' | 'column',
|
||||
display?: Array<string>
|
||||
};
|
||||
direction?: "row" | "column";
|
||||
display?: Array<string>;
|
||||
}
|
||||
|
||||
type PreviewFieldgroup = PredefinedFieldgroupPreview | PlaceholderFieldGroupPreview;
|
||||
type FixedPreview = {
|
||||
value: I18nString;
|
||||
} & PreviewProps
|
||||
|
||||
type PlaceholderFieldGroupPreview = {
|
||||
fieldgroup: string;
|
||||
} & PreviewProps;
|
||||
|
||||
type PredefinedFieldgroupPreview = {
|
||||
fieldgroup: string;
|
||||
sample: PreviewSample[];
|
||||
} & PreviewProps;
|
||||
|
||||
type PreviewSample = {
|
||||
content: I18nString;
|
||||
label: I18nString;
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
import enum
|
||||
from typing import Any, TypedDict
|
||||
from black.nodes import is_vararg
|
||||
from typing import TypedDict
|
||||
from i18nfield.strings import LazyI18nString
|
||||
import jsonschema
|
||||
from django.core.exceptions import ValidationError
|
||||
@@ -46,7 +45,7 @@ class FieldGroup:
|
||||
remaining_fields: list["FieldGroup"],
|
||||
context: LayoutContext,
|
||||
) -> dict:
|
||||
raise NotImplemented()
|
||||
raise NotImplementedError()
|
||||
|
||||
def asdict(self):
|
||||
return {
|
||||
@@ -119,7 +118,7 @@ class PredefinedFieldGroup(FieldGroup):
|
||||
remaining_fields: list["FieldGroup"],
|
||||
context: LayoutContext,
|
||||
):
|
||||
return {"type": "object"}
|
||||
return {"type": "object", "properties": {"active": {"type": "boolean"}}}
|
||||
|
||||
|
||||
class PlaceholderFieldGroup(FieldGroup):
|
||||
@@ -165,7 +164,7 @@ class PlaceholderFieldGroup(FieldGroup):
|
||||
"display": self.display.value,
|
||||
"min_entries": self.min_entries,
|
||||
"max_entries": self.max_entries,
|
||||
"context_args": list(sorted(self.context_args))
|
||||
"context_args": list(sorted(self.context_args)),
|
||||
}
|
||||
|
||||
def layout_schema(
|
||||
@@ -257,8 +256,9 @@ class ImageFieldGroup(PlaceholderFieldGroup):
|
||||
class PassStyle:
|
||||
identifier: str # unique within platform
|
||||
name: str
|
||||
# order here limits in what order users can configure field "overspilling" (if too many fields are defined, where should the rest go) -> can only go down in the list
|
||||
# we evaluate the fields in this order, so they overspill in this order as well (fields from primary are appended to the overspilling field before fields from secondary are etc)
|
||||
# order here limits in what order users can configure field "overspilling" (if too many fields are defined, where should the rest go)
|
||||
# -> can only go down in the list
|
||||
# we evaluate the fields in this order, so they overspill in this order as well
|
||||
fieldgroups: list[FieldGroup]
|
||||
|
||||
@property
|
||||
@@ -379,5 +379,8 @@ class PassStyle:
|
||||
raise ValueError("Unknown field group")
|
||||
return fields
|
||||
|
||||
def group_is_active(self, identifier: str):
|
||||
return self.layout['fieldgroups'].get(identifier, {}).get("active", False)
|
||||
|
||||
def generate(self, op: OrderPosition):
|
||||
raise NotImplementedError()
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
from i18nfield.fields import LazyI18nString
|
||||
|
||||
from pretix.base.models import Event, OrderPosition
|
||||
|
||||
from .base import (
|
||||
@@ -12,7 +10,6 @@ from .base import (
|
||||
WalletPlatform,
|
||||
)
|
||||
from django.utils.translation import gettext as _
|
||||
import json
|
||||
|
||||
from walletobjects import ButtonJWT, EventTicketClass, EventTicketObject
|
||||
from walletobjects.comms import Comms
|
||||
@@ -207,7 +204,12 @@ class GoogleWalletStyle(PassStyle):
|
||||
)
|
||||
)
|
||||
|
||||
return 'googlepaypass', 'text/uri-list', 'https://pay.google.com/gp/v/save/%s' % generated_jwt
|
||||
return (
|
||||
"googlepaypass",
|
||||
"text/uri-list",
|
||||
"https://pay.google.com/gp/v/save/%s" % generated_jwt,
|
||||
)
|
||||
|
||||
|
||||
class GoogleWalletEventTicket(GoogleWalletStyle):
|
||||
identifier = "event"
|
||||
@@ -266,7 +268,7 @@ class GoogleWalletEventTicket(GoogleWalletStyle):
|
||||
{"value": str(self.event.name), "display": "large"},
|
||||
],
|
||||
"direction": "column",
|
||||
"display": ["tight"]
|
||||
"display": ["tight"],
|
||||
},
|
||||
{
|
||||
"fieldgroup": "date",
|
||||
@@ -283,7 +285,15 @@ class GoogleWalletEventTicket(GoogleWalletStyle):
|
||||
],
|
||||
},
|
||||
{"fieldgroup": "code"},
|
||||
]
|
||||
],
|
||||
[
|
||||
{
|
||||
"fieldgroup": "venue",
|
||||
"sample": [
|
||||
{"content": self.venue()[1], "label": self.venue()[0]},
|
||||
],
|
||||
},
|
||||
],
|
||||
]
|
||||
|
||||
def venue(self):
|
||||
@@ -291,17 +301,23 @@ class GoogleWalletEventTicket(GoogleWalletStyle):
|
||||
name = {}
|
||||
address = {}
|
||||
|
||||
for key, value in self.event.location.data.items():
|
||||
for key, value in self.event.location.data.items():
|
||||
lines = value.splitlines()
|
||||
name[key] = lines[0]
|
||||
# We must provide at least one address line each for the name and address - no way around it.
|
||||
if len(lines) > 1:
|
||||
address[key] = '\n'.join(value.splitlines()[1:])
|
||||
address[key] = "\n".join(value.splitlines()[1:])
|
||||
else:
|
||||
address[key] = lines[0]
|
||||
|
||||
return name, address
|
||||
return None, None
|
||||
return "", ""
|
||||
|
||||
def _generate_class(self):
|
||||
output_class = super()._generate_class()
|
||||
if self.group_is_active("venue") and all(self.venue()):
|
||||
output_class.venue(*self.venue())
|
||||
return output_class
|
||||
|
||||
def _generate_object(self, op: OrderPosition, class_id: str):
|
||||
output_object = super()._generate_object(op, class_id)
|
||||
|
||||
Reference in New Issue
Block a user