Add group field
This commit is contained in:
parent
c8bc69ae30
commit
89b5cbd4b5
|
@ -0,0 +1,39 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Form\Fields;
|
||||||
|
|
||||||
|
use App\Group;
|
||||||
|
use App\Group\Enums\Level;
|
||||||
|
use Faker\Generator;
|
||||||
|
use Illuminate\Validation\Rule;
|
||||||
|
|
||||||
|
class GroupField extends Field
|
||||||
|
{
|
||||||
|
public static function name(): string
|
||||||
|
{
|
||||||
|
return 'Gruppierungs-Auswahl';
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function meta(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
['key' => 'required', 'default' => false, 'rules' => ['required' => 'present|boolean'], 'label' => 'Erforderlich'],
|
||||||
|
['key' => 'parent_field', 'default' => null, 'rules' => ['parent_field' => 'present|nullable|string'], 'label' => 'Übergeordnetes Feld'],
|
||||||
|
['key' => 'parent_group', 'default' => null, 'rules' => ['parent_group' => ['present', 'nullable', Rule::in(Group::pluck('id')->toArray())]], 'label' => 'Übergeordnete Gruppierung'],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function default(): string
|
||||||
|
{
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function fake(Generator $faker): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'required' => $faker->boolean(),
|
||||||
|
'parent_field' => null,
|
||||||
|
'parent_group' => null,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,6 +4,8 @@ namespace App\Form\Resources;
|
||||||
|
|
||||||
use App\Form\Fields\Field;
|
use App\Form\Fields\Field;
|
||||||
use App\Form\Models\Formtemplate;
|
use App\Form\Models\Formtemplate;
|
||||||
|
use App\Group;
|
||||||
|
use App\Group\Enums\Level;
|
||||||
use App\Lib\HasMeta;
|
use App\Lib\HasMeta;
|
||||||
use Illuminate\Http\Resources\Json\JsonResource;
|
use Illuminate\Http\Resources\Json\JsonResource;
|
||||||
|
|
||||||
|
@ -37,6 +39,8 @@ class FormtemplateResource extends JsonResource
|
||||||
public static function meta(): array
|
public static function meta(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
|
'base_url' => url(''),
|
||||||
|
'groups' => Group::forSelect(),
|
||||||
'fields' => Field::asMeta(),
|
'fields' => Field::asMeta(),
|
||||||
'links' => [
|
'links' => [
|
||||||
'store' => route('formtemplate.store'),
|
'store' => route('formtemplate.store'),
|
||||||
|
|
|
@ -45,4 +45,17 @@ class Group extends Model
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function forSelect(?self $parent = null, string $prefix = ''): array
|
||||||
|
{
|
||||||
|
$result = self::where('parent_id', $parent ? $parent->id : null)->withCount('children')->get();
|
||||||
|
|
||||||
|
return $result
|
||||||
|
->reduce(
|
||||||
|
fn ($before, $group) => $before->concat([['id' => $group->id, 'name' => $prefix . ($group->inner_name ?: $group->name)]])
|
||||||
|
->concat($group->children_count > 0 ? self::forSelect($group, $prefix . '-- ') : []),
|
||||||
|
collect([])
|
||||||
|
)
|
||||||
|
->toArray();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,8 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
modelValue: {},
|
modelValue: {},
|
||||||
|
meta: {},
|
||||||
|
payload: {},
|
||||||
});
|
});
|
||||||
|
|
||||||
const emit = defineEmits(['update:modelValue']);
|
const emit = defineEmits(['update:modelValue']);
|
||||||
|
|
|
@ -17,6 +17,8 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
modelValue: {},
|
modelValue: {},
|
||||||
|
meta: {},
|
||||||
|
payload: {},
|
||||||
});
|
});
|
||||||
|
|
||||||
const emit = defineEmits(['update:modelValue']);
|
const emit = defineEmits(['update:modelValue']);
|
||||||
|
|
|
@ -22,6 +22,8 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
modelValue: {},
|
modelValue: {},
|
||||||
|
meta: {},
|
||||||
|
payload: {},
|
||||||
});
|
});
|
||||||
|
|
||||||
const emit = defineEmits(['update:modelValue']);
|
const emit = defineEmits(['update:modelValue']);
|
||||||
|
|
|
@ -21,13 +21,14 @@
|
||||||
>
|
>
|
||||||
<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"></component>
|
<component :is="fields[singleField.model.type]" v-model="singleField.model" :payload="inner.sections" :meta="props.meta"></component>
|
||||||
</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"
|
||||||
:value="previewString"
|
:value="previewString"
|
||||||
@editSection="editSection($event.detail[0])"
|
@editSection="editSection($event.detail[0])"
|
||||||
@addSection="addSection"
|
@addSection="addSection"
|
||||||
|
@ -50,6 +51,7 @@ import TextField from './TextField.vue';
|
||||||
import DateField from './DateField.vue';
|
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 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';
|
||||||
|
@ -79,6 +81,7 @@ const fields = {
|
||||||
RadioField: RadioField,
|
RadioField: RadioField,
|
||||||
CheckboxField: CheckboxField,
|
CheckboxField: CheckboxField,
|
||||||
CheckboxesField: CheckboxesField,
|
CheckboxesField: CheckboxesField,
|
||||||
|
GroupField: GroupField,
|
||||||
};
|
};
|
||||||
|
|
||||||
function editSection(sectionIndex) {
|
function editSection(sectionIndex) {
|
||||||
|
|
|
@ -0,0 +1,49 @@
|
||||||
|
<template>
|
||||||
|
<f-switch
|
||||||
|
id="fieldrequired"
|
||||||
|
v-model="modelValue.required"
|
||||||
|
label="Erforderlich"
|
||||||
|
size="sm"
|
||||||
|
name="fieldrequired"
|
||||||
|
inline
|
||||||
|
@update:modelValue="$emit('update:modelValue', {...modelValue, required: $event})"
|
||||||
|
></f-switch>
|
||||||
|
<f-select
|
||||||
|
id="parent_field"
|
||||||
|
:options="fieldOptions"
|
||||||
|
size="sm"
|
||||||
|
name="parent_field"
|
||||||
|
label="Übergeordnetes Feld"
|
||||||
|
:model-value="modelValue.parent_field"
|
||||||
|
@update:modelValue="$emit('update:modelValue', {...props.modelValue, parent_field: $event})"
|
||||||
|
></f-select>
|
||||||
|
<f-select
|
||||||
|
id="parent_group"
|
||||||
|
:options="meta.groups"
|
||||||
|
size="sm"
|
||||||
|
name="parent_group"
|
||||||
|
label="Übergeordnete Gruppe"
|
||||||
|
:model-value="modelValue.parent_group"
|
||||||
|
@update:modelValue="$emit('update:modelValue', {...props.modelValue, parent_group: $event})"
|
||||||
|
></f-select>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import {computed} from 'vue';
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
modelValue: {},
|
||||||
|
meta: {},
|
||||||
|
payload: {},
|
||||||
|
});
|
||||||
|
|
||||||
|
const fieldOptions = computed(() => {
|
||||||
|
return props.payload.reduce((carry, section) => {
|
||||||
|
return section.fields.reduce((fcarry, field) => {
|
||||||
|
return field.type === 'GroupField' ? fcarry.concat({id: field.key, name: field.name}) : fcarry;
|
||||||
|
}, carry);
|
||||||
|
}, []);
|
||||||
|
});
|
||||||
|
|
||||||
|
const emit = defineEmits(['update:modelValue']);
|
||||||
|
</script>
|
|
@ -26,6 +26,8 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
modelValue: {},
|
modelValue: {},
|
||||||
|
meta: {},
|
||||||
|
payload: {},
|
||||||
});
|
});
|
||||||
|
|
||||||
const emit = defineEmits(['update:modelValue']);
|
const emit = defineEmits(['update:modelValue']);
|
||||||
|
|
|
@ -1,11 +1,20 @@
|
||||||
<template>
|
<template>
|
||||||
<f-switch id="fieldrequired" :model-value="modelValue.required" label="Erforderlich" size="sm" name="fieldrequired"
|
<f-switch
|
||||||
inline @update:modelValue="$emit('update:modelValue', { ...modelValue, required: $event })"></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>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
modelValue: {},
|
modelValue: {},
|
||||||
|
meta: {},
|
||||||
|
payload: {},
|
||||||
});
|
});
|
||||||
|
|
||||||
const emit = defineEmits(['update:modelValue']);
|
const emit = defineEmits(['update:modelValue']);
|
||||||
|
|
|
@ -14,6 +14,8 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
modelValue: {},
|
modelValue: {},
|
||||||
|
meta: {},
|
||||||
|
payload: {},
|
||||||
});
|
});
|
||||||
|
|
||||||
const emit = defineEmits(['update:modelValue']);
|
const emit = defineEmits(['update:modelValue']);
|
||||||
|
|
|
@ -136,7 +136,6 @@ Route::group(['middleware' => 'auth:web'], function (): void {
|
||||||
|
|
||||||
// ----------------------------------- group ----------------------------------
|
// ----------------------------------- group ----------------------------------
|
||||||
Route::get('/group', GroupIndexAction::class)->name('group.index');
|
Route::get('/group', GroupIndexAction::class)->name('group.index');
|
||||||
Route::get('/api/group/{group?}', GroupApiIndexAction::class)->name('api.group');
|
|
||||||
Route::post('/group/bulkstore', GroupBulkstoreAction::class)->name('group.bulkstore');
|
Route::post('/group/bulkstore', GroupBulkstoreAction::class)->name('group.bulkstore');
|
||||||
|
|
||||||
// ----------------------------------- course ----------------------------------
|
// ----------------------------------- course ----------------------------------
|
||||||
|
@ -151,3 +150,5 @@ Route::group(['middleware' => 'auth:web'], function (): void {
|
||||||
Route::patch('/formtemplate/{formtemplate}', FormtemplateUpdateAction::class)->name('formtemplate.update');
|
Route::patch('/formtemplate/{formtemplate}', FormtemplateUpdateAction::class)->name('formtemplate.update');
|
||||||
Route::post('/form', FormStoreAction::class)->name('form.store');
|
Route::post('/form', FormStoreAction::class)->name('form.store');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Route::get('/api/group/{group?}', GroupApiIndexAction::class)->name('api.group');
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
namespace Tests\Feature\Form;
|
namespace Tests\Feature\Form;
|
||||||
|
|
||||||
use App\Form\Models\Formtemplate;
|
use App\Form\Models\Formtemplate;
|
||||||
|
use App\Group;
|
||||||
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
||||||
use Tests\TestCase;
|
use Tests\TestCase;
|
||||||
|
|
||||||
|
@ -15,13 +16,19 @@ class FormtemplateIndexActionTest extends TestCase
|
||||||
{
|
{
|
||||||
$formtemplate = Formtemplate::factory()->create();
|
$formtemplate = Formtemplate::factory()->create();
|
||||||
|
|
||||||
$this->login()->loginNami()->withoutExceptionHandling();
|
$group = Group::factory()->has(Group::factory()->state(['inner_name' => 'child']), 'children')->create(['inner_name' => 'root']);
|
||||||
|
$this->login()->loginNami(12345, 'pasword', $group)->withoutExceptionHandling();
|
||||||
|
|
||||||
$this->get(route('formtemplate.index'))
|
$this->get(route('formtemplate.index'))
|
||||||
->assertInertiaPath('data.data.0.links', [
|
->assertInertiaPath('data.data.0.links', [
|
||||||
'update' => route('formtemplate.update', ['formtemplate' => $formtemplate]),
|
'update' => route('formtemplate.update', ['formtemplate' => $formtemplate]),
|
||||||
])
|
])
|
||||||
->assertInertiaPath('data.meta.fields.2', [
|
->assertInertiaPath('data.meta.groups', [
|
||||||
|
['id' => $group->id, 'name' => 'root'],
|
||||||
|
['id' => $group->children->first()->id, 'name' => '-- child'],
|
||||||
|
])
|
||||||
|
->assertInertiaPath('data.meta.base_url', url(''))
|
||||||
|
->assertInertiaPath('data.meta.fields.3', [
|
||||||
'id' => 'DropdownField',
|
'id' => 'DropdownField',
|
||||||
'name' => 'Dropdown',
|
'name' => 'Dropdown',
|
||||||
'default' => [
|
'default' => [
|
||||||
|
@ -33,7 +40,7 @@ class FormtemplateIndexActionTest extends TestCase
|
||||||
'options' => [],
|
'options' => [],
|
||||||
]
|
]
|
||||||
])
|
])
|
||||||
->assertInertiaPath('data.meta.fields.4', [
|
->assertInertiaPath('data.meta.fields.6', [
|
||||||
'id' => 'TextField',
|
'id' => 'TextField',
|
||||||
'name' => 'Text',
|
'name' => 'Text',
|
||||||
'default' => [
|
'default' => [
|
||||||
|
@ -44,7 +51,7 @@ class FormtemplateIndexActionTest extends TestCase
|
||||||
'required' => false,
|
'required' => false,
|
||||||
]
|
]
|
||||||
])
|
])
|
||||||
->assertInertiaPath('data.meta.fields.5', [
|
->assertInertiaPath('data.meta.fields.7', [
|
||||||
'id' => 'TextareaField',
|
'id' => 'TextareaField',
|
||||||
'name' => 'Textarea',
|
'name' => 'Textarea',
|
||||||
'default' => [
|
'default' => [
|
||||||
|
|
Loading…
Reference in New Issue