mirror of
https://github.com/pretix/pretix.git
synced 2026-08-28 13:34:40 +00:00
preview from server, qrcode in apple event pass
This commit is contained in:
@@ -6,65 +6,53 @@ import Input from "./input/input.vue";
|
|||||||
import PassPreview from "./preview/pass-preview.vue";
|
import PassPreview from "./preview/pass-preview.vue";
|
||||||
import { StoreKey } from "../walletStore";
|
import { StoreKey } from "../walletStore";
|
||||||
|
|
||||||
|
const store = inject(StoreKey)!;
|
||||||
const gettext = (window as any).gettext;
|
const gettext = (window as any).gettext;
|
||||||
|
|
||||||
const store = inject(StoreKey)!;
|
|
||||||
|
|
||||||
const platformChoices = computed(() => {
|
|
||||||
return [
|
|
||||||
[null, "Do not generate pass"],
|
|
||||||
...Object.values(store.currentPlatformStyles).map((x) => [
|
|
||||||
x.identifier,
|
|
||||||
x.name,
|
|
||||||
]),
|
|
||||||
];
|
|
||||||
});
|
|
||||||
|
|
||||||
function openForm(url: string, data: Record<string, string>) {
|
function openForm(url: string, data: Record<string, string>) {
|
||||||
let form = document.createElement("form");
|
let form = document.createElement("form");
|
||||||
form.target = "_blank";
|
form.target = "_blank";
|
||||||
form.method = "POST";
|
form.method = "POST";
|
||||||
form.action = url;
|
form.action = url;
|
||||||
form.style.display = "none";
|
form.style.display = "none";
|
||||||
|
|
||||||
for (var key in data) {
|
for (var key in data) {
|
||||||
var input = document.createElement("input");
|
var input = document.createElement("input");
|
||||||
input.type = "hidden";
|
input.type = "hidden";
|
||||||
input.name = key;
|
input.name = key;
|
||||||
input.value = data[key];
|
input.value = data[key];
|
||||||
form.appendChild(input);
|
form.appendChild(input);
|
||||||
}
|
}
|
||||||
document.body.appendChild(form);
|
document.body.appendChild(form);
|
||||||
form.submit();
|
form.submit();
|
||||||
document.body.removeChild(form);
|
document.body.removeChild(form);
|
||||||
}
|
}
|
||||||
|
|
||||||
function openPreview(e: SubmitEvent) {
|
function openPreview(e: SubmitEvent) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
openForm("../../preview/", {
|
openForm("../../preview/", {
|
||||||
csrfmiddlewaretoken: store.csrfToken,
|
csrfmiddlewaretoken: store.csrfToken,
|
||||||
platform: store.currentPlatform,
|
platform: store.currentPlatform,
|
||||||
style: store.currentPlatformLayout.style,
|
style: store.currentPlatformLayout.style,
|
||||||
layout: JSON.stringify(store.currentPlatformLayout.layout),
|
layout: JSON.stringify(store.currentPlatformLayout.layout),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const preview_layouts = [
|
|
||||||
[
|
|
||||||
{
|
const platformChoices = computed(() => {
|
||||||
children: [
|
return [
|
||||||
{ fieldgroup: "logo", relSize: 1 },
|
[null, "Do not generate pass"],
|
||||||
{ fieldgroup: "logo_text", relSize: 3 },
|
...Object.values(store.currentPlatformStyles).map((x) => [
|
||||||
{ fieldgroup: "header", relSize: 2 },
|
x.identifier,
|
||||||
],
|
x.name,
|
||||||
},
|
]),
|
||||||
{ fieldgroup: "primary" },
|
];
|
||||||
{ fieldgroup: "secondary" },
|
});
|
||||||
{ fieldgroup: "auxiliary" },
|
|
||||||
{ fieldgroup: "code" },
|
const preview_layout = computed(() => {
|
||||||
],
|
return JSON.parse(store.currentPlatformStyles[store.currentPlatformLayout.style].preview_layout)
|
||||||
[{ fieldgroup: "back", mode: "column" }],
|
})
|
||||||
];
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template lang="pug">
|
<template lang="pug">
|
||||||
@@ -82,23 +70,25 @@ const preview_layouts = [
|
|||||||
a(role="tab" @click="store.currentPlatform = platform.identifier") {{ platform.name }}
|
a(role="tab" @click="store.currentPlatform = platform.identifier") {{ platform.name }}
|
||||||
.tabbed-form.tab-content
|
.tabbed-form.tab-content
|
||||||
.tab-pane.active.row
|
.tab-pane.active.row
|
||||||
.col-md-8
|
.col-md-6.col-lg-8
|
||||||
Select.form-group(label="Style" :modelValue="store.currentPlatformLayout.style" @update:modelValue="store.setCurrentPlatformStyle" :choices="platformChoices")
|
Select.form-group(label="Style" :modelValue="store.currentPlatformLayout.style" @update:modelValue="store.setCurrentPlatformStyle" :choices="platformChoices")
|
||||||
|
|
||||||
StyleSettings(v-if="store.currentPlatformLayout.style" v-model="store.currentPlatformLayout.layout" :style="store.currentPlatformStyles[store.currentPlatformLayout.style]")
|
StyleSettings(v-if="store.currentPlatformLayout.style" v-model="store.currentPlatformLayout.layout" :style="store.currentPlatformStyles[store.currentPlatformLayout.style]")
|
||||||
.col-md-4
|
.col-md-6.col-lg-4
|
||||||
.panel.panel-default
|
.panel.panel-default
|
||||||
.panel-heading Preview
|
.panel-heading Preview
|
||||||
.panel-body
|
.panel-body
|
||||||
span.text-muted The preview below is only a rough representation of what the pass might look like. Please check the generated pass.
|
div(v-if="preview_layout")
|
||||||
PassPreview(v-for="layout in preview_layouts" :layout="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
|
||||||
pre
|
PassPreview(v-for="layout in preview_layout" :layout="layout")
|
||||||
code {{ store.currentPlatformLayout }}
|
div(v-else) Preview not supported
|
||||||
pre(v-if="store.currentPlatformLayout.style")
|
//- pre
|
||||||
code {{ store.currentPlatformStyles[store.currentPlatformLayout.style] }}
|
//- code {{ store.currentPlatformLayout }}
|
||||||
pre
|
//- pre(v-if="store.currentPlatformLayout.style")
|
||||||
code {{ store.walletLayout }}
|
//- code {{ store.currentPlatformStyles[store.currentPlatformLayout.style] }}
|
||||||
|
//- pre
|
||||||
|
//- code {{ store.walletLayout }}
|
||||||
.form-group.submit-group
|
.form-group.submit-group
|
||||||
button.btn.btn-lg.btn-default(type="button" @click="openPreview") Preview
|
button.btn.btn-lg.btn-default(type="button" @click="openPreview") Preview
|
||||||
button.btn.btn-primary.btn-save(type="submit") Submit
|
button.btn.btn-primary.btn-save(type="submit") Submit
|
||||||
|
|||||||
@@ -3,40 +3,42 @@ type BaseFieldGroupDefinition = {
|
|||||||
identifier: string;
|
identifier: string;
|
||||||
name: string;
|
name: string;
|
||||||
required: boolean;
|
required: boolean;
|
||||||
}
|
};
|
||||||
|
|
||||||
type FieldGroupDefinition = PlaceholderFieldGroupDefinition | PredefinedFieldGroupDefinition;
|
type FieldGroupDefinition =
|
||||||
|
| PlaceholderFieldGroupDefinition
|
||||||
|
| PredefinedFieldGroupDefinition;
|
||||||
|
|
||||||
type FieldGroupDisplay = 'plain' | 'with_label' | 'code';
|
type FieldGroupDisplay = "plain" | "with_label" | "code";
|
||||||
|
|
||||||
type PlaceholderFieldGroupDefinition = BaseFieldGroupDefinition & {
|
type PlaceholderFieldGroupDefinition = BaseFieldGroupDefinition & {
|
||||||
type: 'placeholder';
|
type: "placeholder";
|
||||||
content_type: FieldContentType;
|
content_type: FieldContentType;
|
||||||
default_entries: FieldEntry[];
|
default_entries: FieldEntry[];
|
||||||
display: FieldGroupDisplay;
|
display: FieldGroupDisplay;
|
||||||
min_entries: number|null;
|
min_entries: number | null;
|
||||||
max_entries: number|null;
|
max_entries: number | null;
|
||||||
}
|
};
|
||||||
|
|
||||||
type PredefinedFieldGroupDefinition = BaseFieldGroupDefinition & {
|
type PredefinedFieldGroupDefinition = BaseFieldGroupDefinition & {
|
||||||
type: 'predefined';
|
type: "predefined";
|
||||||
}
|
};
|
||||||
|
|
||||||
type I18nString = string | Record<string, string>
|
type I18nString = null | string | Record<string, string>;
|
||||||
|
|
||||||
type FieldContentType = 'text' | 'image';
|
type FieldContentType = "text" | "image";
|
||||||
|
|
||||||
type PlaceholderFieldEntry = {
|
type PlaceholderFieldEntry = {
|
||||||
type: 'placeholder';
|
type: "placeholder";
|
||||||
label?: I18nString;
|
label?: I18nString;
|
||||||
content?: string;
|
content?: string;
|
||||||
}
|
};
|
||||||
|
|
||||||
type CustomFieldEntry = {
|
type CustomFieldEntry = {
|
||||||
type: 'custom';
|
type: "custom";
|
||||||
label?: I18nString;
|
label?: I18nString;
|
||||||
content?: I18nString;
|
content?: I18nString;
|
||||||
}
|
};
|
||||||
|
|
||||||
type FieldEntry = PlaceholderFieldEntry | CustomFieldEntry;
|
type FieldEntry = PlaceholderFieldEntry | CustomFieldEntry;
|
||||||
|
|
||||||
@@ -47,7 +49,7 @@ type Style = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
type Variable = {
|
type Variable = {
|
||||||
label: string
|
label: string;
|
||||||
editor_sample: I18nString;
|
editor_sample: I18nString;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -62,7 +64,6 @@ type Variables = Record<string, Variable>;
|
|||||||
type VariableConfig = Record<string, Variables>;
|
type VariableConfig = Record<string, Variables>;
|
||||||
type Platforms = Platform[];
|
type Platforms = Platform[];
|
||||||
|
|
||||||
|
|
||||||
type PlaceholderFieldGroupConfig = {
|
type PlaceholderFieldGroupConfig = {
|
||||||
entries: Array<FieldEntry>;
|
entries: Array<FieldEntry>;
|
||||||
overflow: string | null;
|
overflow: string | null;
|
||||||
@@ -70,7 +71,9 @@ type PlaceholderFieldGroupConfig = {
|
|||||||
|
|
||||||
type PredefinedFieldGroupConfig = {};
|
type PredefinedFieldGroupConfig = {};
|
||||||
|
|
||||||
type FieldGroupConfig = PlaceholderFieldGroupConfig | PredefinedFieldGroupConfig;
|
type FieldGroupConfig =
|
||||||
|
| PlaceholderFieldGroupConfig
|
||||||
|
| PredefinedFieldGroupConfig;
|
||||||
|
|
||||||
type LayoutData = {
|
type LayoutData = {
|
||||||
fieldgroups: Record<string, FieldGroupConfig>;
|
fieldgroups: Record<string, FieldGroupConfig>;
|
||||||
@@ -85,11 +88,20 @@ type PlatformLayout = {
|
|||||||
type WalletLayout = {
|
type WalletLayout = {
|
||||||
name?: string;
|
name?: string;
|
||||||
platform_layouts: PlatformLayout[];
|
platform_layouts: PlatformLayout[];
|
||||||
}
|
};
|
||||||
type WalletStore = {
|
type WalletStore = {
|
||||||
platforms: Platforms,
|
platforms: Platforms;
|
||||||
variables: VariableConfig,
|
variables: VariableConfig;
|
||||||
locales: Record<string, string>,
|
locales: Record<string, string>;
|
||||||
csrfToken: String,
|
csrfToken: String;
|
||||||
walletLayout: WalletLayout | null
|
walletLayout: WalletLayout | null;
|
||||||
}
|
};
|
||||||
|
|
||||||
|
type PreviewLayout = Array<PreviewRow>;
|
||||||
|
type PreviewRow = { children: Array<PreviewRow> } | PreviewFieldgroup;
|
||||||
|
type PreviewFieldgroup = {
|
||||||
|
fieldgroup: string;
|
||||||
|
relSize?: number;
|
||||||
|
direction?: 'row' | 'column',
|
||||||
|
display?: Array<string>
|
||||||
|
};
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
from typing import Any
|
||||||
|
|
||||||
from .base import (
|
from .base import (
|
||||||
FieldEntryType,
|
FieldEntryType,
|
||||||
FieldGroupDisplay,
|
FieldGroupDisplay,
|
||||||
@@ -268,7 +270,6 @@ class AppleWalletEventTicket(AppleWalletStyle):
|
|||||||
), # TODO: validation of max field count if combined "Coupons, store cards, and generic passes with a square barcode can have a total of up to four secondary and auxiliary fields, combined."
|
), # TODO: validation of max field count if combined "Coupons, store cards, and generic passes with a square barcode can have a total of up to four secondary and auxiliary fields, combined."
|
||||||
TextFieldGroup(identifier="header", name=_("Header"), max_entries=3),
|
TextFieldGroup(identifier="header", name=_("Header"), max_entries=3),
|
||||||
TextFieldGroup(identifier="auxiliary", name=_("Auxiliary"), max_entries=4),
|
TextFieldGroup(identifier="auxiliary", name=_("Auxiliary"), max_entries=4),
|
||||||
TextFieldGroup(identifier="back", name=_("Back")),
|
|
||||||
TextFieldGroup(
|
TextFieldGroup(
|
||||||
identifier="code",
|
identifier="code",
|
||||||
name=_("QR-Code"),
|
name=_("QR-Code"),
|
||||||
@@ -280,8 +281,9 @@ class AppleWalletEventTicket(AppleWalletStyle):
|
|||||||
)
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
TextFieldGroup(identifier="back", name=_("Back")),
|
||||||
]
|
]
|
||||||
# preview_image = "apple/event_ticket.svg"
|
preview_layout = '[[{"children":[{"fieldgroup":"logo","relSize":1},{"fieldgroup":"logo_text","relSize":3,"display":["bold","large","centered"]},{"fieldgroup":"header","relSize":2,"display":["large", "tight"]}]},{"fieldgroup":"primary","display":"large"},{"fieldgroup":"secondary"},{"fieldgroup":"auxiliary"},{"fieldgroup":"code"}],[{"fieldgroup":"back","direction":"column"}]]'
|
||||||
|
|
||||||
def convert_fields(self, strings, fields, prefix):
|
def convert_fields(self, strings, fields, prefix):
|
||||||
converted = []
|
converted = []
|
||||||
@@ -300,7 +302,7 @@ class AppleWalletEventTicket(AppleWalletStyle):
|
|||||||
return converted
|
return converted
|
||||||
|
|
||||||
def pass_content(self, fields, strings):
|
def pass_content(self, fields, strings):
|
||||||
content = {
|
content: dict[str, Any] = {
|
||||||
"eventTicket": {
|
"eventTicket": {
|
||||||
"primaryFields": self.convert_fields(
|
"primaryFields": self.convert_fields(
|
||||||
strings, fields["primary"], "primary"
|
strings, fields["primary"], "primary"
|
||||||
@@ -319,4 +321,12 @@ class AppleWalletEventTicket(AppleWalletStyle):
|
|||||||
content["logoText"] = self.convert_fields(
|
content["logoText"] = self.convert_fields(
|
||||||
strings, fields["logo_text"], "logo_text"
|
strings, fields["logo_text"], "logo_text"
|
||||||
)[0]["value"]
|
)[0]["value"]
|
||||||
|
|
||||||
|
if fields['code']:
|
||||||
|
content["barcodes"] = [{
|
||||||
|
"format": "PKBarcodeFormatQR",
|
||||||
|
"message": str(fields["code"][0]["value"]),
|
||||||
|
"messageEncoding": "utf-8",
|
||||||
|
"altText": str(fields["code"][0]["value"])
|
||||||
|
}]
|
||||||
return content
|
return content
|
||||||
|
|||||||
@@ -231,14 +231,15 @@ class PassStyle:
|
|||||||
name: str
|
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
|
# 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)
|
# 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)
|
||||||
|
|
||||||
fieldgroups: list[FieldGroup]
|
fieldgroups: list[FieldGroup]
|
||||||
|
preview_layout: str | None
|
||||||
|
|
||||||
def asdict(self):
|
def asdict(self):
|
||||||
return {
|
return {
|
||||||
"identifier": self.identifier,
|
"identifier": self.identifier,
|
||||||
"name": self.name,
|
"name": self.name,
|
||||||
"fieldgroups": [x.asdict() for x in self.fieldgroups],
|
"fieldgroups": [x.asdict() for x in self.fieldgroups],
|
||||||
|
"preview_layout": self.preview_layout
|
||||||
}
|
}
|
||||||
|
|
||||||
def layout_schema(self, context):
|
def layout_schema(self, context):
|
||||||
|
|||||||
@@ -18,3 +18,4 @@ class GoogleWalletEventTicket(PassStyle):
|
|||||||
PredefinedFieldGroup(identifier="seating", name=_("Seating")),
|
PredefinedFieldGroup(identifier="seating", name=_("Seating")),
|
||||||
TextFieldGroup(identifier="qrcode", name=_("QR-Code"), display=FieldGroupDisplay.PLAIN),
|
TextFieldGroup(identifier="qrcode", name=_("QR-Code"), display=FieldGroupDisplay.PLAIN),
|
||||||
]
|
]
|
||||||
|
preview_layout = None
|
||||||
Reference in New Issue
Block a user