Compare commits
4 Commits
56e0223492
...
8117cfb4f4
Author | SHA1 | Date |
---|---|---|
|
8117cfb4f4 | |
|
a6a8983ae9 | |
|
8b42a62b2d | |
|
e6261d42ff |
|
@ -39,10 +39,10 @@ class FormStoreAction
|
||||||
*/
|
*/
|
||||||
public function handle(array $attributes): Form
|
public function handle(array $attributes): Form
|
||||||
{
|
{
|
||||||
return tap(
|
return tap(Form::create($attributes), function ($form) {
|
||||||
Form::create($attributes),
|
$form->setDeferredUploads(request()->input('header_image'));
|
||||||
fn ($form) => $form->setDeferredUploads(request()->input('header_image'))
|
$form->setDeferredUploads(request()->input('mailattachments'));
|
||||||
);
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -0,0 +1,84 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Form\Fields;
|
||||||
|
|
||||||
|
use App\Form\Models\Form;
|
||||||
|
use App\Form\Models\Participant;
|
||||||
|
use Faker\Generator;
|
||||||
|
|
||||||
|
class NumberField extends Field
|
||||||
|
{
|
||||||
|
|
||||||
|
public bool $required;
|
||||||
|
public ?int $min;
|
||||||
|
public ?int $max;
|
||||||
|
|
||||||
|
public static function name(): string
|
||||||
|
{
|
||||||
|
return 'Numerisch';
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function meta(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
['key' => 'required', 'default' => true, 'rules' => ['required' => 'present|boolean'], 'label' => 'Erforderlich'],
|
||||||
|
['key' => 'min', 'default' => null, 'rules' => ['min' => 'present|nullable|numeric'], 'label' => 'minimaler Wert'],
|
||||||
|
['key' => 'max', 'default' => null, 'rules' => ['min' => 'present|nullable|numeric'], 'label' => 'maximaler Wert'],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function default(): ?int
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function fake(Generator $faker): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'required' => $faker->boolean(),
|
||||||
|
'min' => $faker->numberBetween(0, 100),
|
||||||
|
'max' => $faker->numberBetween(0, 100),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
public function getRegistrationRules(Form $form): array
|
||||||
|
{
|
||||||
|
$minmax = [];
|
||||||
|
|
||||||
|
if ($this->min !== null) {
|
||||||
|
$minmax[] = 'gte:' . $this->min;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->max !== null) {
|
||||||
|
$minmax[] = 'lte:' . $this->max;
|
||||||
|
}
|
||||||
|
|
||||||
|
return [$this->key => $this->required ? ['required', 'integer', ...$minmax] : ['nullable', 'integer', ...$minmax]];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
public function getRegistrationAttributes(Form $form): array
|
||||||
|
{
|
||||||
|
return [$this->key => $this->name];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
public function getRegistrationMessages(Form $form): array
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
public function afterRegistration(Form $form, Participant $participant, array $input): void
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,11 +5,12 @@ namespace App\Form\Mails;
|
||||||
use App\Form\Data\FormConfigData;
|
use App\Form\Data\FormConfigData;
|
||||||
use App\Form\Models\Participant;
|
use App\Form\Models\Participant;
|
||||||
use Illuminate\Bus\Queueable;
|
use Illuminate\Bus\Queueable;
|
||||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
use Illuminate\Mail\Attachment;
|
||||||
use Illuminate\Mail\Mailable;
|
use Illuminate\Mail\Mailable;
|
||||||
use Illuminate\Mail\Mailables\Content;
|
use Illuminate\Mail\Mailables\Content;
|
||||||
use Illuminate\Mail\Mailables\Envelope;
|
use Illuminate\Mail\Mailables\Envelope;
|
||||||
use Illuminate\Queue\SerializesModels;
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
use Illuminate\Support\Facades\Storage;
|
||||||
|
|
||||||
class ConfirmRegistrationMail extends Mailable
|
class ConfirmRegistrationMail extends Mailable
|
||||||
{
|
{
|
||||||
|
@ -56,10 +57,12 @@ class ConfirmRegistrationMail extends Mailable
|
||||||
/**
|
/**
|
||||||
* Get the attachments for the message.
|
* Get the attachments for the message.
|
||||||
*
|
*
|
||||||
* @return array<int, mixed>
|
* @return array<int, Attachment>
|
||||||
*/
|
*/
|
||||||
public function attachments()
|
public function attachments()
|
||||||
{
|
{
|
||||||
return [];
|
return $this->participant->form->getMedia('mailattachments')
|
||||||
|
->map(fn ($media) => Attachment::fromStorageDisk($media->disk, $media->getPathRelativeToRoot()))
|
||||||
|
->all();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,13 +4,10 @@ namespace App\Form\Models;
|
||||||
|
|
||||||
use App\Form\Data\FieldCollection;
|
use App\Form\Data\FieldCollection;
|
||||||
use App\Form\Data\FormConfigData;
|
use App\Form\Data\FormConfigData;
|
||||||
use App\Form\Fields\Field;
|
|
||||||
use Cviebrock\EloquentSluggable\Sluggable;
|
use Cviebrock\EloquentSluggable\Sluggable;
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||||
use Illuminate\Support\Arr;
|
|
||||||
use Illuminate\Support\Collection;
|
|
||||||
use Laravel\Scout\Searchable;
|
use Laravel\Scout\Searchable;
|
||||||
use Spatie\Image\Manipulations;
|
use Spatie\Image\Manipulations;
|
||||||
use Spatie\MediaLibrary\HasMedia;
|
use Spatie\MediaLibrary\HasMedia;
|
||||||
|
@ -65,6 +62,7 @@ class Form extends Model implements HasMedia
|
||||||
->registerMediaConversions(function (Media $media) {
|
->registerMediaConversions(function (Media $media) {
|
||||||
$this->addMediaConversion('square')->fit(Manipulations::FIT_CROP, 400, 400);
|
$this->addMediaConversion('square')->fit(Manipulations::FIT_CROP, 400, 400);
|
||||||
});
|
});
|
||||||
|
$this->addMediaCollection('mailattachments');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -80,6 +80,7 @@ class FormResource extends JsonResource
|
||||||
'mail_bottom' => null,
|
'mail_bottom' => null,
|
||||||
'config' => null,
|
'config' => null,
|
||||||
'header_image' => null,
|
'header_image' => null,
|
||||||
|
'mailattachments' => [],
|
||||||
'id' => null,
|
'id' => null,
|
||||||
],
|
],
|
||||||
'section_default' => [
|
'section_default' => [
|
||||||
|
|
|
@ -21,4 +21,18 @@ trait FakesMedia
|
||||||
->toMediaCollection($collection);
|
->toMediaCollection($collection);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function withDocument(string $collection, string $filename, string $content = ''): self
|
||||||
|
{
|
||||||
|
return $this->afterCreating(function (HasMedia $model) use ($filename, $collection, $content) {
|
||||||
|
$pathinfo = pathinfo($filename);
|
||||||
|
|
||||||
|
UploadedFile::fake()->create($filename, $content, 'application/pdf')->storeAs('media-library', $filename, 'temp');
|
||||||
|
|
||||||
|
$model->addMediaFromDisk('media-library/' . $filename, 'temp')
|
||||||
|
->usingName($pathinfo['filename'])
|
||||||
|
->usingFileName($pathinfo['basename'])
|
||||||
|
->toMediaCollection($collection);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit bc45e0cdf792158372b0f12db8739c19beda1a4f
|
Subproject commit ecf008f12130c8ba18fced50f0a6fe1378d88755
|
|
@ -1 +1 @@
|
||||||
Subproject commit 91e5cc3e3b6d7d0a8a3e2361514ec8c1ce9cb655
|
Subproject commit 3a7f5587550f27864a14236f2ef9af77e947966c
|
|
@ -51,9 +51,13 @@ createInertiaApp({
|
||||||
requireModules(import.meta.glob('./components/ui/*.vue'), app, 'ui');
|
requireModules(import.meta.glob('./components/ui/*.vue'), app, 'ui');
|
||||||
requireModules(import.meta.glob('./components/page/*.vue', {eager: true}), app, 'page');
|
requireModules(import.meta.glob('./components/page/*.vue', {eager: true}), app, 'page');
|
||||||
app.component(
|
app.component(
|
||||||
'f-singlefile',
|
'FSinglefile',
|
||||||
defineAsyncComponent(() => import('!/medialibrary-helper/assets/components/SingleFile.vue'))
|
defineAsyncComponent(() => import('!/medialibrary-helper/assets/components/SingleFile.vue'))
|
||||||
);
|
);
|
||||||
|
app.component(
|
||||||
|
'FMultiplefiles',
|
||||||
|
defineAsyncComponent(() => import('!/medialibrary-helper/assets/components/MultipleFiles.vue'))
|
||||||
|
);
|
||||||
|
|
||||||
app.provide('axios', app.config.globalProperties.axios);
|
app.provide('axios', app.config.globalProperties.axios);
|
||||||
app.mount(el);
|
app.mount(el);
|
||||||
|
|
|
@ -38,7 +38,7 @@
|
||||||
<ui-popup v-if="single !== null && single.config !== null" :heading="`Veranstaltung ${single.id ? 'bearbeiten' : 'erstellen'}`" full @close="cancel">
|
<ui-popup v-if="single !== null && single.config !== null" :heading="`Veranstaltung ${single.id ? 'bearbeiten' : 'erstellen'}`" full @close="cancel">
|
||||||
<div class="flex flex-col mt-3">
|
<div class="flex flex-col mt-3">
|
||||||
<ui-tabs v-model="active" :entries="tabs"></ui-tabs>
|
<ui-tabs v-model="active" :entries="tabs"></ui-tabs>
|
||||||
<div v-if="active === 0" class="grid grid-cols-2 gap-3">
|
<div v-show="active === 0" class="grid grid-cols-2 gap-3">
|
||||||
<f-text id="name" v-model="single.name" name="name" label="Name" required></f-text>
|
<f-text id="name" v-model="single.name" name="name" label="Name" required></f-text>
|
||||||
<f-singlefile
|
<f-singlefile
|
||||||
id="header_image"
|
id="header_image"
|
||||||
|
@ -63,17 +63,28 @@
|
||||||
></f-textarea>
|
></f-textarea>
|
||||||
<f-editor id="description" v-model="single.description" name="description" label="Beschreibung" rows="10" required></f-editor>
|
<f-editor id="description" v-model="single.description" name="description" label="Beschreibung" rows="10" required></f-editor>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="active === 1">
|
<div v-show="active === 1">
|
||||||
<ui-note class="mt-2"> Sobald sich der erste Teilnehmer für die Veranstaltung angemeldet hat, kann dieses Formular nicht mehr geändert werden. </ui-note>
|
<ui-note class="mt-2"> Sobald sich der erste Teilnehmer für die Veranstaltung angemeldet hat, kann dieses Formular nicht mehr geändert werden. </ui-note>
|
||||||
<form-builder v-model="single.config" :meta="meta"></form-builder>
|
<form-builder v-model="single.config" :meta="meta"></form-builder>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="active === 2" class="grid gap-3">
|
<div v-show="active === 2" class="grid grid-cols-[1fr_300px] gap-3">
|
||||||
<ui-note class="mt-2">
|
<ui-note class="mt-2 col-span-full">
|
||||||
Hier kannst du die E-Mail anpassen, die nach der Anmeldung an den Teilnehmer verschickt wird.<br />
|
Hier kannst du die E-Mail anpassen, die nach der Anmeldung an den Teilnehmer verschickt wird.<br />
|
||||||
Es gibt dafür einen ersten E-Mail-Teil und einen zweiten E-Mail-Teil. Dazwischen werden die Daten des Teilnehmers aufgelistet.<br />
|
Es gibt dafür einen ersten E-Mail-Teil und einen zweiten E-Mail-Teil. Dazwischen werden die Daten des Teilnehmers aufgelistet.<br />
|
||||||
Die Anrede ("Hallo Max Mustermann") wird automatisch an den Anfang gesetzt.</ui-note
|
Die Anrede ("Hallo Max Mustermann") wird automatisch an den Anfang gesetzt.<br />
|
||||||
>
|
Außerdem kannst du Dateien hochladen, die automatisch mit angehangen werden.
|
||||||
|
</ui-note>
|
||||||
<f-textarea id="mail_top" v-model="single.mail_top" name="mail_top" label="E-Mail-Teil 1" rows="8" required></f-textarea>
|
<f-textarea id="mail_top" v-model="single.mail_top" name="mail_top" label="E-Mail-Teil 1" rows="8" required></f-textarea>
|
||||||
|
<f-multiplefiles
|
||||||
|
id="mailattachments"
|
||||||
|
v-model="single.mailattachments"
|
||||||
|
label="Anhänge"
|
||||||
|
name="mailattachments"
|
||||||
|
parent-name="form"
|
||||||
|
:parent-id="single.id"
|
||||||
|
collection="mailattachments"
|
||||||
|
class="row-span-2"
|
||||||
|
></f-multiplefiles>
|
||||||
<f-textarea id="mail_bottom" v-model="single.mail_bottom" name="mail_bottom" label="E-Mail-Teil 2" rows="8" required></f-textarea>
|
<f-textarea id="mail_bottom" v-model="single.mail_bottom" name="mail_bottom" label="E-Mail-Teil 2" rows="8" required></f-textarea>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -2,52 +2,48 @@
|
||||||
<form class="grid gap-3 mt-4 grid-cols-[1fr_max-content] items-start" @submit.prevent="submit">
|
<form class="grid gap-3 mt-4 grid-cols-[1fr_max-content] items-start" @submit.prevent="submit">
|
||||||
<div class="grid gap-3">
|
<div class="grid gap-3">
|
||||||
<slot name="meta"></slot>
|
<slot name="meta"></slot>
|
||||||
<asideform v-if="singleSection !== null" :heading="`Sektion ${singleSection.index !== null ? 'bearbeiten' : 'erstellen'}`" @close="singleSection = null" @submit="storeSection">
|
<asideform v-if="singleSection !== null"
|
||||||
<f-text :id="`sectionform-name`" v-model="singleSection.model.name" label="Name" :name="`sectionform-name`"></f-text>
|
:heading="`Sektion ${singleSection.index !== null ? 'bearbeiten' : 'erstellen'}`"
|
||||||
<f-textarea :id="`sectionform-intro`" v-model="singleSection.model.intro" label="Einleitung" :name="`sectionform-intro`"></f-textarea>
|
@close="singleSection = null" @submit="storeSection">
|
||||||
|
<f-text :id="`sectionform-name`" v-model="singleSection.model.name" label="Name"
|
||||||
|
:name="`sectionform-name`"></f-text>
|
||||||
|
<f-textarea :id="`sectionform-intro`" v-model="singleSection.model.intro" label="Einleitung"
|
||||||
|
:name="`sectionform-intro`"></f-textarea>
|
||||||
</asideform>
|
</asideform>
|
||||||
<asideform v-if="singleSection === null && singleField === null" heading="Feld erstellen" :closeable="false" :storeable="false" @submit="storeField">
|
<asideform v-if="singleSection === null && singleField === null" heading="Feld erstellen" :closeable="false"
|
||||||
|
:storeable="false" @submit="storeField">
|
||||||
<div class="mt-3 grid gap-3">
|
<div class="mt-3 grid gap-3">
|
||||||
<a v-for="(field, index) in props.meta.fields" :key="index" class="py-2 px-3 border rounded bg-zinc-800 hover:bg-zinc-700 transition" href="#" @click.prevent="addField(field)">
|
<a v-for="(field, index) in props.meta.fields" :key="index"
|
||||||
|
class="py-2 px-3 border rounded bg-zinc-800 hover:bg-zinc-700 transition" href="#"
|
||||||
|
@click.prevent="addField(field)">
|
||||||
<span v-text="field.name"></span>
|
<span v-text="field.name"></span>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</asideform>
|
</asideform>
|
||||||
<asideform
|
<asideform v-if="singleSection === null && singleField !== null"
|
||||||
v-if="singleSection === null && singleField !== null"
|
:heading="`Feld ${singleField.index !== null ? 'bearbeiten' : 'erstellen'}`" @close="singleField = null"
|
||||||
:heading="`Feld ${singleField.index !== null ? 'bearbeiten' : 'erstellen'}`"
|
@submit="storeField">
|
||||||
@close="singleField = null"
|
|
||||||
@submit="storeField"
|
|
||||||
>
|
|
||||||
<f-text id="fieldname" v-model="singleField.model.name" label="Name" size="sm" name="fieldname"></f-text>
|
<f-text id="fieldname" v-model="singleField.model.name" label="Name" size="sm" name="fieldname"></f-text>
|
||||||
<column-selector v-model="singleField.model.columns"></column-selector>
|
<column-selector v-model="singleField.model.columns"></column-selector>
|
||||||
<component :is="fields[singleField.model.type]" v-model="singleField.model" :payload="inner.sections" :meta="props.meta"></component>
|
<component :is="fields[singleField.model.type]" v-model="singleField.model" :payload="inner.sections"
|
||||||
<f-select id="nami_type" v-model="singleField.model.nami_type" :options="meta.namiTypes" label="NaMi-Feld" size="sm" name="nami_type"></f-select>
|
:meta="props.meta"></component>
|
||||||
<f-select id="special_type" v-model="singleField.model.special_type" :options="meta.specialTypes" label="Bedeutung" size="sm" name="special_type"></f-select>
|
<f-select id="nami_type" v-model="singleField.model.nami_type" :options="meta.namiTypes" label="NaMi-Feld"
|
||||||
|
size="sm" name="nami_type"></f-select>
|
||||||
|
<f-select id="special_type" v-model="singleField.model.special_type" :options="meta.specialTypes"
|
||||||
|
label="Bedeutung" size="sm" name="special_type"></f-select>
|
||||||
<f-textarea id="hint" v-model="singleField.model.hint" label="Hinweis" size="sm" name="hint"></f-textarea>
|
<f-textarea id="hint" v-model="singleField.model.hint" label="Hinweis" size="sm" name="hint"></f-textarea>
|
||||||
<f-switch
|
<f-switch v-show="singleField.model.nami_type === null" id="for_members"
|
||||||
v-show="singleField.model.nami_type === null"
|
v-model="singleField.model.for_members" label="Für Unter-Mitglieder zusätzlich abfragen" size="sm"
|
||||||
id="for_members"
|
name="for_members"></f-switch>
|
||||||
v-model="singleField.model.for_members"
|
|
||||||
label="Für Unter-Mitglieder zusätzlich abfragen"
|
|
||||||
size="sm"
|
|
||||||
name="for_members"
|
|
||||||
></f-switch>
|
|
||||||
</asideform>
|
</asideform>
|
||||||
</div>
|
</div>
|
||||||
<ui-box heading="Vorschau" container-class="grid gap-3" class="w-[800px]">
|
<ui-box heading="Vorschau" container-class="grid gap-3" class="w-[800px]">
|
||||||
<event-form
|
<event-form editable
|
||||||
editable
|
|
||||||
style="--primary: hsl(181, 75%, 26%); --secondary: hsl(181, 75%, 35%); --font: hsl(181, 84%, 78%); --circle: hsl(181, 86%, 16%)"
|
style="--primary: hsl(181, 75%, 26%); --secondary: hsl(181, 75%, 35%); --font: hsl(181, 84%, 78%); --circle: hsl(181, 86%, 16%)"
|
||||||
:base-url="meta.base_url"
|
:base-url="meta.base_url" :value="previewString" @editSection="editSection($event.detail[0])"
|
||||||
:value="previewString"
|
@addSection="addSection" @editField="editField($event.detail[0], $event.detail[1])"
|
||||||
@editSection="editSection($event.detail[0])"
|
|
||||||
@addSection="addSection"
|
|
||||||
@editField="editField($event.detail[0], $event.detail[1])"
|
|
||||||
@deleteField="deleteField($event.detail[0], $event.detail[1])"
|
@deleteField="deleteField($event.detail[0], $event.detail[1])"
|
||||||
@deleteSection="deleteSection($event.detail[0])"
|
@deleteSection="deleteSection($event.detail[0])" @active="updateActive($event.detail[0])"></event-form>
|
||||||
@active="updateActive($event.detail[0])"
|
|
||||||
></event-form>
|
|
||||||
</ui-box>
|
</ui-box>
|
||||||
</form>
|
</form>
|
||||||
</template>
|
</template>
|
||||||
|
@ -63,6 +59,7 @@ import DateField from './DateField.vue';
|
||||||
import DropdownField from './RadioField.vue';
|
import DropdownField from './RadioField.vue';
|
||||||
import RadioField from './RadioField.vue';
|
import RadioField from './RadioField.vue';
|
||||||
import GroupField from './GroupField.vue';
|
import GroupField from './GroupField.vue';
|
||||||
|
import NumberField from './NumberField.vue';
|
||||||
import CheckboxField from './CheckboxField.vue';
|
import CheckboxField from './CheckboxField.vue';
|
||||||
import CheckboxesField from './CheckboxesField.vue';
|
import CheckboxesField from './CheckboxesField.vue';
|
||||||
import ColumnSelector from './ColumnSelector.vue';
|
import ColumnSelector from './ColumnSelector.vue';
|
||||||
|
@ -93,6 +90,7 @@ const fields = {
|
||||||
CheckboxField: CheckboxField,
|
CheckboxField: CheckboxField,
|
||||||
CheckboxesField: CheckboxesField,
|
CheckboxesField: CheckboxesField,
|
||||||
GroupField: GroupField,
|
GroupField: GroupField,
|
||||||
|
NumberField: NumberField,
|
||||||
};
|
};
|
||||||
|
|
||||||
function editSection(sectionIndex) {
|
function editSection(sectionIndex) {
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
<template>
|
||||||
|
<f-switch id="fieldrequired" :model-value="modelValue.required" label="Erforderlich" size="sm" name="fieldrequired"
|
||||||
|
inline @update:modelValue="$emit('update:modelValue', { ...modelValue, required: $event })"></f-switch>
|
||||||
|
<f-text id="min" :model-value="modelValue.min" label="minimaler Wert" size="sm" name="min" type="number"
|
||||||
|
@update:modelValue="$emit('update:modelValue', { ...modelValue, min: parse($event) })"></f-text>
|
||||||
|
<f-text id="max" :model-value="modelValue.max" label="maximaler Wert" size="sm" name="max" type="number"
|
||||||
|
@update:modelValue="$emit('update:modelValue', { ...modelValue, max: parse($event) })"></f-text>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
const props = defineProps({
|
||||||
|
modelValue: {},
|
||||||
|
meta: {},
|
||||||
|
payload: {},
|
||||||
|
});
|
||||||
|
|
||||||
|
function parse(input) {
|
||||||
|
return parseInt(input) === NaN ? null : parseInt(input);
|
||||||
|
}
|
||||||
|
|
||||||
|
const emit = defineEmits(['update:modelValue']);
|
||||||
|
</script>
|
|
@ -55,6 +55,7 @@ class FormIndexActionTest extends FormTestCase
|
||||||
->assertInertiaPath('data.meta.default.name', '')
|
->assertInertiaPath('data.meta.default.name', '')
|
||||||
->assertInertiaPath('data.meta.default.description', [])
|
->assertInertiaPath('data.meta.default.description', [])
|
||||||
->assertInertiaPath('data.meta.default.excerpt', '')
|
->assertInertiaPath('data.meta.default.excerpt', '')
|
||||||
|
->assertInertiaPath('data.meta.default.mailattachments', [])
|
||||||
->assertInertiaPath('data.meta.default.config', null)
|
->assertInertiaPath('data.meta.default.config', null)
|
||||||
->assertInertiaPath('data.meta.base_url', url(''))
|
->assertInertiaPath('data.meta.base_url', url(''))
|
||||||
->assertInertiaPath('data.meta.namiTypes.0', ['id' => 'Vorname', 'name' => 'Vorname'])
|
->assertInertiaPath('data.meta.namiTypes.0', ['id' => 'Vorname', 'name' => 'Vorname'])
|
||||||
|
|
|
@ -266,6 +266,30 @@ class FormRegisterActionTest extends FormTestCase
|
||||||
['email' => 'alaaa'],
|
['email' => 'alaaa'],
|
||||||
['email' => 'Mail muss eine gültige E-Mail-Adresse sein.']
|
['email' => 'Mail muss eine gültige E-Mail-Adresse sein.']
|
||||||
];
|
];
|
||||||
|
|
||||||
|
yield [
|
||||||
|
$this->numberField('numb')->name('Nummer')->required(false)->min(10)->max(20),
|
||||||
|
['numb' => 21],
|
||||||
|
['numb' => 'Nummer muss kleiner oder gleich 20 sein.']
|
||||||
|
];
|
||||||
|
|
||||||
|
yield [
|
||||||
|
$this->numberField('numb')->name('Nummer')->required(false)->min(10)->max(20),
|
||||||
|
['numb' => 9],
|
||||||
|
['numb' => 'Nummer muss größer oder gleich 10 sein.']
|
||||||
|
];
|
||||||
|
|
||||||
|
yield [
|
||||||
|
$this->numberField('numb')->name('Nummer')->required(false)->min(10)->max(20),
|
||||||
|
['numb' => 'asss'],
|
||||||
|
['numb' => 'Nummer muss eine ganze Zahl sein.']
|
||||||
|
];
|
||||||
|
|
||||||
|
yield [
|
||||||
|
$this->numberField('numb')->name('Nummer')->required(true),
|
||||||
|
['numb' => ''],
|
||||||
|
['numb' => 'Nummer ist erforderlich.']
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testItValidatesGroupFieldWithParentGroupField(): void
|
public function testItValidatesGroupFieldWithParentGroupField(): void
|
||||||
|
|
|
@ -54,4 +54,24 @@ class FormRegisterMailTest extends FormTestCase
|
||||||
$mail->assertSeeInText('* Vorname: Max');
|
$mail->assertSeeInText('* Vorname: Max');
|
||||||
$mail->assertSeeInText('* Volljährig: Ja');
|
$mail->assertSeeInText('* Volljährig: Ja');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testItAttachesMailAttachments(): void
|
||||||
|
{
|
||||||
|
$this->login()->loginNami()->withoutExceptionHandling();
|
||||||
|
$participant = Participant::factory()->for(
|
||||||
|
Form::factory()
|
||||||
|
->fields([
|
||||||
|
$this->textField('vorname')->name('Vorname')->specialType(SpecialType::FIRSTNAME),
|
||||||
|
$this->textField('nachname')->specialType(SpecialType::LASTNAME),
|
||||||
|
])
|
||||||
|
->withDocument('mailattachments', 'beispiel.pdf', 'content1')
|
||||||
|
->withDocument('mailattachments', 'beispiel2.pdf', 'content2')
|
||||||
|
)
|
||||||
|
->data(['vorname' => 'Max', 'nachname' => 'Muster'])
|
||||||
|
->create();
|
||||||
|
|
||||||
|
$mail = new ConfirmRegistrationMail($participant);
|
||||||
|
$mail->assertHasAttachedData('content1', 'beispiel.pdf', ['mime' => 'application/pdf']);
|
||||||
|
$mail->assertHasAttachedData('content2', 'beispiel2.pdf', ['mime' => 'application/pdf']);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ use App\Form\Fields\DropdownField;
|
||||||
use App\Form\Fields\EmailField;
|
use App\Form\Fields\EmailField;
|
||||||
use App\Form\Fields\GroupField;
|
use App\Form\Fields\GroupField;
|
||||||
use App\Form\Fields\NamiField;
|
use App\Form\Fields\NamiField;
|
||||||
|
use App\Form\Fields\NumberField;
|
||||||
use App\Form\Fields\RadioField;
|
use App\Form\Fields\RadioField;
|
||||||
use App\Form\Fields\TextareaField;
|
use App\Form\Fields\TextareaField;
|
||||||
use App\Form\Fields\TextField;
|
use App\Form\Fields\TextField;
|
||||||
|
@ -27,6 +28,11 @@ trait CreatesFormFields
|
||||||
return FormtemplateFieldRequest::type(TextField::class)->key($key ?? $this->randomKey());
|
return FormtemplateFieldRequest::type(TextField::class)->key($key ?? $this->randomKey());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function numberField(?string $key = null): FormtemplateFieldRequest
|
||||||
|
{
|
||||||
|
return FormtemplateFieldRequest::type(NumberField::class)->key($key ?? $this->randomKey());
|
||||||
|
}
|
||||||
|
|
||||||
protected function emailField(?string $key = null): FormtemplateFieldRequest
|
protected function emailField(?string $key = null): FormtemplateFieldRequest
|
||||||
{
|
{
|
||||||
return FormtemplateFieldRequest::type(EmailField::class)->key($key ?? $this->randomKey());
|
return FormtemplateFieldRequest::type(EmailField::class)->key($key ?? $this->randomKey());
|
||||||
|
|
Loading…
Reference in New Issue