Add number field
continuous-integration/drone/push Build is failing
Details
continuous-integration/drone/push Build is failing
Details
This commit is contained in:
parent
a6a8983ae9
commit
8117cfb4f4
|
@ -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
|
||||
{
|
||||
}
|
||||
}
|
|
@ -1 +1 @@
|
|||
Subproject commit 57d6236a930039b56b64da84b2ecca00601485bb
|
||||
Subproject commit ecf008f12130c8ba18fced50f0a6fe1378d88755
|
|
@ -2,59 +2,55 @@
|
|||
<form class="grid gap-3 mt-4 grid-cols-[1fr_max-content] items-start" @submit.prevent="submit">
|
||||
<div class="grid gap-3">
|
||||
<slot name="meta"></slot>
|
||||
<asideform v-if="singleSection !== null" :heading="`Sektion ${singleSection.index !== null ? 'bearbeiten' : 'erstellen'}`" @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 v-if="singleSection !== null"
|
||||
:heading="`Sektion ${singleSection.index !== null ? 'bearbeiten' : 'erstellen'}`"
|
||||
@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 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">
|
||||
<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>
|
||||
</a>
|
||||
</div>
|
||||
</asideform>
|
||||
<asideform
|
||||
v-if="singleSection === null && singleField !== null"
|
||||
:heading="`Feld ${singleField.index !== null ? 'bearbeiten' : 'erstellen'}`"
|
||||
@close="singleField = null"
|
||||
@submit="storeField"
|
||||
>
|
||||
<asideform v-if="singleSection === null && singleField !== null"
|
||||
:heading="`Feld ${singleField.index !== null ? 'bearbeiten' : 'erstellen'}`" @close="singleField = null"
|
||||
@submit="storeField">
|
||||
<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>
|
||||
<component :is="fields[singleField.model.type]" v-model="singleField.model" :payload="inner.sections" :meta="props.meta"></component>
|
||||
<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>
|
||||
<component :is="fields[singleField.model.type]" v-model="singleField.model" :payload="inner.sections"
|
||||
:meta="props.meta"></component>
|
||||
<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-switch
|
||||
v-show="singleField.model.nami_type === null"
|
||||
id="for_members"
|
||||
v-model="singleField.model.for_members"
|
||||
label="Für Unter-Mitglieder zusätzlich abfragen"
|
||||
size="sm"
|
||||
name="for_members"
|
||||
></f-switch>
|
||||
<f-switch v-show="singleField.model.nami_type === null" id="for_members"
|
||||
v-model="singleField.model.for_members" label="Für Unter-Mitglieder zusätzlich abfragen" size="sm"
|
||||
name="for_members"></f-switch>
|
||||
</asideform>
|
||||
</div>
|
||||
<ui-box heading="Vorschau" container-class="grid gap-3" class="w-[800px]">
|
||||
<event-form
|
||||
editable
|
||||
<event-form editable
|
||||
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"
|
||||
:value="previewString"
|
||||
@editSection="editSection($event.detail[0])"
|
||||
@addSection="addSection"
|
||||
@editField="editField($event.detail[0], $event.detail[1])"
|
||||
:base-url="meta.base_url" :value="previewString" @editSection="editSection($event.detail[0])"
|
||||
@addSection="addSection" @editField="editField($event.detail[0], $event.detail[1])"
|
||||
@deleteField="deleteField($event.detail[0], $event.detail[1])"
|
||||
@deleteSection="deleteSection($event.detail[0])"
|
||||
@active="updateActive($event.detail[0])"
|
||||
></event-form>
|
||||
@deleteSection="deleteSection($event.detail[0])" @active="updateActive($event.detail[0])"></event-form>
|
||||
</ui-box>
|
||||
</form>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {watch, computed, ref} from 'vue';
|
||||
import {snakeCase} from 'change-case';
|
||||
import { watch, computed, ref } from 'vue';
|
||||
import { snakeCase } from 'change-case';
|
||||
import '!/adrema-form/dist/main.js';
|
||||
import Asideform from './Asideform.vue';
|
||||
import TextareaField from './TextareaField.vue';
|
||||
|
@ -63,6 +59,7 @@ import DateField from './DateField.vue';
|
|||
import DropdownField from './RadioField.vue';
|
||||
import RadioField from './RadioField.vue';
|
||||
import GroupField from './GroupField.vue';
|
||||
import NumberField from './NumberField.vue';
|
||||
import CheckboxField from './CheckboxField.vue';
|
||||
import CheckboxesField from './CheckboxesField.vue';
|
||||
import ColumnSelector from './ColumnSelector.vue';
|
||||
|
@ -93,11 +90,12 @@ const fields = {
|
|||
CheckboxField: CheckboxField,
|
||||
CheckboxesField: CheckboxesField,
|
||||
GroupField: GroupField,
|
||||
NumberField: NumberField,
|
||||
};
|
||||
|
||||
function editSection(sectionIndex) {
|
||||
singleSection.value = {
|
||||
model: {...inner.value.sections[sectionIndex]},
|
||||
model: { ...inner.value.sections[sectionIndex] },
|
||||
index: 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>
|
|
@ -266,6 +266,30 @@ class FormRegisterActionTest extends FormTestCase
|
|||
['email' => 'alaaa'],
|
||||
['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
|
||||
|
|
|
@ -9,6 +9,7 @@ use App\Form\Fields\DropdownField;
|
|||
use App\Form\Fields\EmailField;
|
||||
use App\Form\Fields\GroupField;
|
||||
use App\Form\Fields\NamiField;
|
||||
use App\Form\Fields\NumberField;
|
||||
use App\Form\Fields\RadioField;
|
||||
use App\Form\Fields\TextareaField;
|
||||
use App\Form\Fields\TextField;
|
||||
|
@ -27,6 +28,11 @@ trait CreatesFormFields
|
|||
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
|
||||
{
|
||||
return FormtemplateFieldRequest::type(EmailField::class)->key($key ?? $this->randomKey());
|
||||
|
|
Loading…
Reference in New Issue