From 66882eb115f62fc11d541940b5f56094c700b19d Mon Sep 17 00:00:00 2001 From: Kara Engelhardt Date: Tue, 7 Apr 2026 19:26:14 +0200 Subject: [PATCH] WIP --- package.json | 2 +- .../pretixplugins/wallet/components/app.vue | 42 +++++++++++ .../wallet/components/field-settings.vue | 55 ++++++++++++++ .../wallet/components/optional-select.vue | 18 +++++ .../wallet/components/style-settings.vue | 45 ++++++++++++ .../static/pretixplugins/wallet/main.ts | 12 +++ src/pretix/plugins/wallet/styles.py | 65 ++++++++++++----- .../templates/pretixplugins/wallet/edit.html | 23 +++--- .../wallet/placeholder_formset.html | 73 +++++++++++++++++++ .../plugins/wallet/templatetags/wallet.py | 10 +++ 10 files changed, 318 insertions(+), 27 deletions(-) create mode 100644 src/pretix/plugins/wallet/static/pretixplugins/wallet/components/app.vue create mode 100644 src/pretix/plugins/wallet/static/pretixplugins/wallet/components/field-settings.vue create mode 100644 src/pretix/plugins/wallet/static/pretixplugins/wallet/components/optional-select.vue create mode 100644 src/pretix/plugins/wallet/static/pretixplugins/wallet/components/style-settings.vue create mode 100644 src/pretix/plugins/wallet/static/pretixplugins/wallet/main.ts create mode 100644 src/pretix/plugins/wallet/templates/pretixplugins/wallet/placeholder_formset.html create mode 100644 src/pretix/plugins/wallet/templatetags/wallet.py diff --git a/package.json b/package.json index c20749c4eb..e58a65afae 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,7 @@ "build": "npm run build:control -s && npm run build:widget -s", "build:control": "vite build", "build:widget": "vite build src/pretix/static/pretixpresale/widget", - "lint:eslint": "eslint src/pretix/static/pretixpresale/widget src/pretix/static/pretixcontrol/js/ui/checkinrules src/pretix/plugins/webcheckin", + "lint:eslint": "eslint src/pretix/static/pretixpresale/widget src/pretix/static/pretixcontrol/js/ui/checkinrules src/pretix/plugins/webcheckin src/pretix/plugins/wallet", "test": "echo \"Error: no test specified\" && exit 1" }, "dependencies": { diff --git a/src/pretix/plugins/wallet/static/pretixplugins/wallet/components/app.vue b/src/pretix/plugins/wallet/static/pretixplugins/wallet/components/app.vue new file mode 100644 index 0000000000..cb59ad70d3 --- /dev/null +++ b/src/pretix/plugins/wallet/static/pretixplugins/wallet/components/app.vue @@ -0,0 +1,42 @@ + + + diff --git a/src/pretix/plugins/wallet/static/pretixplugins/wallet/components/field-settings.vue b/src/pretix/plugins/wallet/static/pretixplugins/wallet/components/field-settings.vue new file mode 100644 index 0000000000..0ba28ad736 --- /dev/null +++ b/src/pretix/plugins/wallet/static/pretixplugins/wallet/components/field-settings.vue @@ -0,0 +1,55 @@ + + + diff --git a/src/pretix/plugins/wallet/static/pretixplugins/wallet/components/optional-select.vue b/src/pretix/plugins/wallet/static/pretixplugins/wallet/components/optional-select.vue new file mode 100644 index 0000000000..ac738b0564 --- /dev/null +++ b/src/pretix/plugins/wallet/static/pretixplugins/wallet/components/optional-select.vue @@ -0,0 +1,18 @@ + + + diff --git a/src/pretix/plugins/wallet/static/pretixplugins/wallet/components/style-settings.vue b/src/pretix/plugins/wallet/static/pretixplugins/wallet/components/style-settings.vue new file mode 100644 index 0000000000..b8910f8f04 --- /dev/null +++ b/src/pretix/plugins/wallet/static/pretixplugins/wallet/components/style-settings.vue @@ -0,0 +1,45 @@ + + + diff --git a/src/pretix/plugins/wallet/static/pretixplugins/wallet/main.ts b/src/pretix/plugins/wallet/static/pretixplugins/wallet/main.ts new file mode 100644 index 0000000000..ec20a3924f --- /dev/null +++ b/src/pretix/plugins/wallet/static/pretixplugins/wallet/main.ts @@ -0,0 +1,12 @@ +import { createApp } from 'vue' +import App from './components/app.vue' + +const app = createApp(App) +app.mount('#editor') + +app.config.errorHandler = (error, _vm, info) => { + // vue fatals on errors by default, which is a weird choice + // https://github.com/vuejs/core/issues/3525 + // https://github.com/vuejs/router/discussions/2435 + console.error('[VUE]', info, error) +} diff --git a/src/pretix/plugins/wallet/styles.py b/src/pretix/plugins/wallet/styles.py index 6813264662..c851281cba 100644 --- a/src/pretix/plugins/wallet/styles.py +++ b/src/pretix/plugins/wallet/styles.py @@ -6,18 +6,22 @@ from django.core.exceptions import ValidationError from i18nfield.strings import LazyI18nString from .models import WalletLayout + class WalletPlatform: identifier: str name: str + class ApplePlatform(WalletPlatform): identifier = "apple" name = _("Apple") + class GooglePlatform(WalletPlatform): identifier = "google" name = _("Google") - + + class PlaceholderFieldType(enum.Enum): TEXT = "text" CODE = "qr" @@ -30,10 +34,11 @@ class PlaceholderFieldType(enum.Enum): class PlaceholderField: type: PlaceholderFieldType label: LazyI18nString - value: LazyI18nString + value: str def asdict(self): - return {'type': self.type.value, 'label': self.label, 'value': self.value} + return {"type": self.type.value, "label": self.label.data, "value": self.value} + @dataclass class FieldGroupDefinition: @@ -44,7 +49,13 @@ class FieldGroupDefinition: max_entries: int | None = None def asdict(self): - return {"identifier": self.identifier, "name": self.name, "min_entries": self.min_entries, "max_entries": self.max_entries} + return { + "identifier": self.identifier, + "name": self.name, + "entry_type": self.entry_type.value, + "min_entries": self.min_entries, + "max_entries": self.max_entries, + } @dataclass @@ -54,7 +65,7 @@ class PlaceholderFieldGroup(FieldGroupDefinition): def asdict(self): asdict = super().asdict() - asdict['default_entries'] = [x.asdict() for x in self.default_entries] + asdict["default_entries"] = [x.asdict() for x in self.default_entries] return asdict @@ -67,7 +78,7 @@ class PredefinedFieldGroup(FieldGroupDefinition): class PassStyle: identifier: str # unique within platform - name: str + name: str platform: Literal["apple"] | Literal["google"] fields: list[FieldGroupDefinition] # preview_image: str # TODO: preview @@ -94,18 +105,28 @@ class AppleWalletEventTicket(PassStyle): min_entries=1, max_entries=1, default_entries=[ - PlaceholderField(PlaceholderFieldType.IMAGE, "logo", "event:image") + PlaceholderField(PlaceholderFieldType.IMAGE, LazyI18nString("logo"), "event:image") ], entry_type=PlaceholderFieldType.IMAGE, ), - PlaceholderFieldGroup(identifier="primary", name=_("Primary"), min_entries=1, max_entries=1), + PlaceholderFieldGroup( + identifier="primary", + name=_("Primary"), + min_entries=1, + max_entries=1, + default_entries=[ + PlaceholderField(PlaceholderFieldType.TEXT, LazyI18nString("Ticket type"), "item") + ], + ), PlaceholderFieldGroup( identifier="secondary", name=_("Secondary"), max_entries=4 ), # 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." PlaceholderFieldGroup( identifier="headers", name=_("Header"), max_entries=3 ), # TODO: header image - PlaceholderFieldGroup(identifier="auxillary", name=_("Auxillary"), max_entries=4), + PlaceholderFieldGroup( + identifier="auxillary", name=_("Auxillary"), max_entries=4 + ), PlaceholderFieldGroup(identifier="back", name=_("Back")), ] # preview_image = "apple/event_ticket.svg" @@ -117,13 +138,16 @@ class GoogleWalletEventTicket(PassStyle): platform = "google" fields = [ PredefinedFieldGroup(identifier="seating", name=_("Seating")), - PlaceholderFieldGroup(identifier="qrcode", name=_("QR-Code"), entry_type=PlaceholderFieldType.CODE), + PlaceholderFieldGroup( + identifier="qrcode", name=_("QR-Code"), entry_type=PlaceholderFieldType.CODE + ), ] AVAILABLE_PLATFORMS = {"apple": ApplePlatform, "google": GooglePlatform} AVAILABLE_STYLES = [AppleWalletEventTicket(), GoogleWalletEventTicket()] + def get_platforms_with_styles(): platforms_with_styles = {} for style in AVAILABLE_STYLES: @@ -133,6 +157,7 @@ def get_platforms_with_styles(): platforms_with_styles[platform][style.identifier] = style return platforms_with_styles + def get_platform_styles(platform): platform_styles = {} for style in AVAILABLE_STYLES: @@ -140,9 +165,11 @@ def get_platform_styles(platform): platform_styles[style.identifier] = style return platform_styles + def get_platforms(): return AVAILABLE_PLATFORMS + class PassLayout: style: PassStyle layout: dict @@ -156,15 +183,19 @@ class PassLayout: def validate_fields(self): style_fields = self.style.fields - if 'fields' not in self.layout: + if "fields" not in self.layout: raise ValidationError(_("Layout did not contain any fields")) - layout_fields = self.layout['fields'] + layout_fields = self.layout["fields"] if not isinstance(layout_fields, dict): raise ValidationError(_("'fields' must be dict")) for fieldgroup in style_fields: - layout_field_data = layout_fields.get(fieldgroup.identifier, []) - if fieldgroup.min_entries and fieldgroup.min_entries < len(layout_field_data): - raise ValidationError(_("At least {min_entries} must be specified for {name}").format(min_entries=fieldgroup.min_entries, name=fieldgroup.name)) - - \ No newline at end of file + layout_field_data = layout_fields.get(fieldgroup.identifier, {}) + if fieldgroup.min_entries and fieldgroup.min_entries < len( + layout_field_data.get('entries') + ): + raise ValidationError( + _("At least {min_entries} must be specified for {name}").format( + min_entries=fieldgroup.min_entries, name=fieldgroup.name + ) + ) diff --git a/src/pretix/plugins/wallet/templates/pretixplugins/wallet/edit.html b/src/pretix/plugins/wallet/templates/pretixplugins/wallet/edit.html index 4bd6c08e46..e0d869cbb0 100644 --- a/src/pretix/plugins/wallet/templates/pretixplugins/wallet/edit.html +++ b/src/pretix/plugins/wallet/templates/pretixplugins/wallet/edit.html @@ -2,18 +2,23 @@ {% load i18n %} {% load money %} {% load bootstrap3 %} +{% load vite %} +{% load static %} +{% load compress %} + {% block title %}{% trans "Wallet layouts" %}{% endblock %} + {% block content %} -
{{ styles }}
-
{{ variables }}
+

{% trans "Edit layout" %}

{{ styles|json_script:"styles" }} {{ variables|json_script:"variables" }} -
- {% csrf_token %} - {% bootstrap_form form %} - -
+ {{ form.errors|json_script:"form_errors" }} +
+ {% csrf_token %} +
+
+ {% vite_hmr %} + {% vite_asset "src/pretix/plugins/wallet/static/pretixplugins/wallet/main.ts" %} + {% csrf_token %} {% endblock %} diff --git a/src/pretix/plugins/wallet/templates/pretixplugins/wallet/placeholder_formset.html b/src/pretix/plugins/wallet/templates/pretixplugins/wallet/placeholder_formset.html new file mode 100644 index 0000000000..b3b20d5ebb --- /dev/null +++ b/src/pretix/plugins/wallet/templates/pretixplugins/wallet/placeholder_formset.html @@ -0,0 +1,73 @@ +{% load i18n %} +{% load bootstrap3 %} +{% load escapejson %} +{% load formset_tags %} +
+ {{ formset.management_form }} + {% bootstrap_formset_errors formset %} +
+ {% for form in formset %} +
+ {% bootstrap_form_errors form %} +
+ {{ form.id }} + {% bootstrap_field form.DELETE form_group_class="" layout="inline" %} + {% bootstrap_field form.ORDER form_group_class="" layout="inline" %} +
+
+ {% bootstrap_field form.entry layout="inline" %} +
+
+ + + + + + +
+
+ {% endfor %} +
+ +

+ +

+
+{% if external_fields %} + {{ external_fields|json_script:external_fields_id }} +{% endif %} diff --git a/src/pretix/plugins/wallet/templatetags/wallet.py b/src/pretix/plugins/wallet/templatetags/wallet.py new file mode 100644 index 0000000000..af5d3dc62c --- /dev/null +++ b/src/pretix/plugins/wallet/templatetags/wallet.py @@ -0,0 +1,10 @@ +from django import template + +from ..models import WalletLayout + +register = template.Library() + + +@register.filter +def platform_layouts(platform, event): + return WalletLayout.objects.filter(event=event, platform=platform.identifier)