feat: voucher purchase flow

This commit is contained in:
fromme committed 2026-10-01 15:03:27 +02:00
1 parent a628e96d06
commit f3dd122678
70 files changed
+3672 -122

No files matched your search

@@ -0,0 +1,22 @@
import {Controller} from '@hotwired/stimulus'
/*
* Keeps buttons disabled until the given fields pass the browser's constraint validation
* (required, min, max, step, …), which the form types render from the same rules the server
* validates. A text field holding only whitespace counts as empty, as NotBlank does.
*
* Works on forms with novalidate too: that only stops the browser from blocking the submit.
*/
export default class extends Controller {
static targets = ['field', 'button']
connect() {
this.update()
}
update() {
const invalid = this.fieldTargets.some(field => !field.checkValidity()
|| ('radio' !== field.type && 'checkbox' !== field.type && '' === field.value.trim()))
this.buttonTargets.forEach(button => button.disabled = invalid)
}
}
@@ -0,0 +1,26 @@
import {Controller} from '@hotwired/stimulus'
/*
* Shows an indicator over an iframe until its content has loaded.
*
* The controller sets the frame's src itself, after the load listener is attached, so a fast
* response cannot finish before anybody listens.
*/
export default class extends Controller {
static targets = ['frame', 'indicator']
static values = {src: String}
connect() {
this.loaded = this.loaded.bind(this)
this.frameTarget.addEventListener('load', this.loaded)
this.frameTarget.src = this.srcValue
}
disconnect() {
this.frameTarget.removeEventListener('load', this.loaded)
}
loaded() {
this.indicatorTarget.classList.add('hidden')
}
}