--wip-- [skip ci]

This commit is contained in:
philipp lang 2025-01-04 23:07:35 +01:00
parent 67f45886ea
commit 2a1107f59b
12 changed files with 168 additions and 81 deletions

View File

@ -1,36 +0,0 @@
<?php
namespace App\Contribution\Actions;
use App\Contribution\ContributionFactory;
use App\Country;
use Inertia\Inertia;
use Inertia\Response;
use Lorisleiva\Actions\Concerns\AsAction;
class FormAction
{
use AsAction;
/**
* @return array<string, mixed>
*/
public function handle(): array
{
return [
'countries' => Country::select('name', 'id')->get(),
'data' => [
'country' => Country::firstWhere('name', 'Deutschland')->id,
],
'compilers' => app(ContributionFactory::class)->compilerSelect(),
];
}
public function asController(): Response
{
session()->put('menu', 'contribution');
session()->put('title', 'Zuschüsse');
return Inertia::render('contribution/VIndex', $this->handle());
}
}

View File

@ -32,6 +32,10 @@ class AppServiceProvider extends ServiceProvider
Blade::componentNamespace('App\\View\\Mail', 'mail-view');
Model::preventLazyLoading(!app()->isProduction());
Blade::directive('rawAttribute', function ($expression) {
return '<?php if ($attributes->has("' . $expression . '.raw")): ?> <?php echo $attributes->get("' . $expression . '.raw"); ?> <?php else: ?> <?php echo "`".e($attributes->get("' . $expression . '"))."`"; ?> <?php endif ?>';
});
}
/**

View File

@ -17,7 +17,6 @@ class Label extends Component
) {
}
public function render()
{
return <<<'HTML'
@ -26,6 +25,7 @@ class Label extends Component
<template x-if="{!! $asAlpineBool($attributes, 'required') !!}">
<span class="text-red-800">&nbsp;*</span>
</template>
{{$attributes}}
</span>
HTML;
}

View File

@ -3,14 +3,14 @@
namespace App\View\Form;
use App\View\Traits\HasFormDimensions;
use App\View\Traits\RendersAlpine;
use Illuminate\View\Component;
class Text extends Component
{
use HasFormDimensions;
public string $id;
use RendersAlpine;
public function __construct(
public string $name,
@ -18,17 +18,23 @@ class Text extends Component
public ?string $hint = null,
public bool $required = false,
public string $label = '',
public string $type = 'text'
public string $type = 'text',
public string $id = '',
) {
$this->id = str()->uuid()->toString();
if (!$id) {
$this->id = str()->uuid()->toString();
}
}
public function render()
{
return <<<'HTML'
<label {{ $attributes->merge(['class' => 'flex flex-col group '.$heightClass])->only('class') }} for="{{$id}}" style="{{$heightVars}}">
@if ($label)
<x-form::label :required="$required" :value="$label" />
<label {{ $attributes->merge(['class' => 'flex flex-col group '.$heightClass])->only('class') }}
@if ($attributes->has(':id')) :for="{{$attributes->get(':id')}}" @else for="{{$id}}" @endif
style="{{$heightVars}}"
>
@if ($hasAlpineAttribute($attributes, 'label'))
<x-form::label {{$attributes->merge(['value' => 'lalala', 'required' => false]) }} ></x-form::label>
@endif
<div class="relative flex-none flex">
<input

View File

@ -6,11 +6,17 @@ use Illuminate\View\ComponentAttributeBag;
trait RendersAlpine
{
public function hasAlpineAttribute(ComponentAttributeBag $attributes, string $attribute): bool
{
return $this->{$attribute} || ($attributes->has(':' . $attribute) && $attributes->get(':' . $attribute));
}
public function asAlpineString(ComponentAttributeBag $attributes, string $tagName): string
{
$rawTag = ':' . $tagName;
if ($attributes->has($rawTag)) {
if ($attributes->has($rawTag) && $attributes->get($rawTag)) {
return $attributes->get($rawTag);
}
@ -33,4 +39,21 @@ trait RendersAlpine
return $this->{$tagName} ? 'true' : 'false';
}
public function renderAlpineFallbackAttribute(ComponentAttributeBag $attributes, string $tagName, string $prop): string
{
return ':label="aaa"';
return new ComponentAttributeBag([]);
return $attributes;
}
public function ca(ComponentAttributeBag $attributes): ComponentAttributeBag
{
return new ComponentAttributeBag([
'::value' => $attributes->get(':label'),
]);
return ':value="$label" ::value="{!!$attributes->get(":label")!!}"';
}
}

View File

@ -104,6 +104,8 @@ services:
meilisearch:
image: getmeili/meilisearch:v1.6
ports:
- '7700:7700'
volumes:
- ./data/meilisearch:/meili_data
env_file:

View File

@ -2,14 +2,63 @@
namespace Modules\Contribution\Components;
use App\Contribution\ContributionFactory;
use App\Country;
use Illuminate\Support\Collection;
use Livewire\Component;
class FillList extends Component
{
public Collection $countries;
public ?int $country = null;
public Collection $compilers;
public string $eventName = '';
public string $dateFrom = '';
public string $dateUntil = '';
public string $zipLocation = '';
public function mount(): void
{
$this->countries = Country::select('name', 'id')->get();
$this->country = Country::firstWhere('name', 'Deutschland')->id;
$this->compilers = app(ContributionFactory::class)->compilerSelect();
}
public function render()
{
return <<<'HTML'
<x-page::layout title="Zuschüsse" menu="contribution">
<form target="_BLANK" class="max-w-4xl w-full mx-auto gap-6 grid-cols-2 grid p-6">
<x-form::text name="event_name" wire:model="eventName" class="col-span-full" label="Veranstaltungs-Name" required></x-form::text>
<x-form::text name="date_until" wire:model="dateUntil" type="date" label="Datum bis" required></x-form::text>
<x-form::text name="date_until" wire:model="dateUntil" type="date" label="Datum bis"></x-form::text>
<x-form::text name="date_from" wire:model="dateFrom" type="date" label="Datum von" required></x-form::text>
<x-form::text name="zip_location" wire:model="zipLocation" label="PLZ / Ort" required></x-form::text>
<x-form::select id="country" wire:model="country" :options="$countries" name="country" label="Land" required></x-form::select>
<x-ui::box class="col-span-2" title="Mitglieder finden" x-data="memberSearch()">
<span x-text="JSON.stringify(members)"></span>
<div class="mt-2 grid grid-cols-[repeat(auto-fill,minmax(180px,1fr))] gap-2 col-span-2">
<x-form::text name="search_text" ref="searchInput" x-model="searchString" class="col-span-2" label="Suchen …" size="sm" @keydown.enter.prevent="onSubmitFirstMemberResult"></x-form::text>
<template x-for="member in searchHelper.getHits()" :key="member.id">
<x-form::lever
name="members"
::label="member.fullname"
::id="member.id"
::value="member.id"
size="sm"
ref="input"
x-model="members"
@keydown.enter.prevent="onSubmitMemberResult($event.target.value)"
></x-form::lever>
</template>
</div>
</x-ui::box>
<button v-for="(compiler, index) in compilers" :key="index" class="btn btn-primary mt-3 inline-block" @click.prevent="submit(compiler.class)" v-text="compiler.title"></button>
</form>
</x-page::layout>
HTML;
}

View File

@ -2,8 +2,11 @@
namespace Modules\Contribution\Components;
use App\Contribution\Documents\RdpNrwDocument;
use App\Country;
use Tests\TestCase;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Livewire\Livewire;
uses(TestCase::class);
uses(DatabaseTransactions::class);
@ -16,3 +19,17 @@ it('displays page', function () {
test()->get(route('contribution.index'))
->assertSeeLivewire(FillList::class);
});
it('displays fields', function () {
$country = Country::factory()->create(['name' => 'Deutschland']);
Livewire::test(FillList::class)
->assertSet('compilers.0.class', RdpNrwDocument::class)
->assertSet('compilers.0.title', 'Für RdP NRW erstellen')
->assertSet('countries.0.name', 'Deutschland')
->assertSet('countries.0.id', $country->id)
->assertSet('country', $country->id)
->assertSee('Veranstaltungs-Name')
->assertSee('Datum von')
->assertSee('Deutschland');
});

View File

@ -1,36 +0,0 @@
<?php
namespace Tests\Feature\Contribution;
use App\Contribution\Documents\RdpNrwDocument;
use App\Country;
use App\Member\Member;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Tests\TestCase;
class IndexTest extends TestCase
{
use DatabaseTransactions;
public function testItHasContributionIndex(): void
{
$this->withoutExceptionHandling()->login()->loginNami();
$country = Country::factory()->create(['name' => 'Deutschland']);
Member::factory()->defaults()->create(['firstname' => 'Max', 'lastname' => 'Muster']);
Member::factory()->defaults()->create(['firstname' => 'Jane', 'lastname' => 'Muster']);
$response = $this->get('/contribution');
$this->assertInertiaHas([
'class' => RdpNrwDocument::class,
'title' => 'Für RdP NRW erstellen',
], $response, 'compilers.0');
$this->assertInertiaHas([
'id' => $country->id,
'name' => $country->name,
], $response, 'countries.0');
$this->assertInertiaHas([
'country' => $country->id,
], $response, 'data');
}
}

View File

@ -26,6 +26,7 @@ it('renders component', function ($component, $params, $expected = '', $notExpec
'raw php string' => ['<x-form::label value="the label" />', [], 'x-text="`the label`"'],
'raw js string' => ['<x-form::label ::value="theLabel" />', [], 'x-text="theLabel"'],
'raw js string with tag' => ['<x-form::label ::value="the<Label" />', [], 'x-text="the<Label"'],
'raw js string empty' => ['<x-form::label value="lala" ::value="" />', [], 'x-text="`lala`"'],
'renders required' => ['<x-form::label value="" required/>', [], 'x-if="true"'],
'renders required' => ['<x-form::label value="" />', [], 'x-if="false"'],
]);

View File

@ -0,0 +1,31 @@
<?php
namespace Tests\Unit\View\Form;
use Illuminate\Support\Facades\Blade;
use Illuminate\Support\Facades\View;
use Illuminate\Support\ViewErrorBag;
use Tests\TestCase;
uses(TestCase::class);
it('renders component', function ($component, $params, $expected = '', $notExpected = '') {
View::share(['errors' => new ViewErrorBag()]);
$rendered = Blade::render($component, $params);
if ($expected) {
expect($rendered)->toContain($expected);
}
if ($notExpected) {
expect($rendered)->not()->toContain($notExpected);
}
})->with([
'php string' => ['<x-form::lever name="name" label="the label" />', [], 'x-text="`the label`"'],
'php string with escaping' => ['<x-form::lever name="name" label="the <label>" />', [], 'x-text="`the &lt;label&gt;`"'],
'php string with var' => ['<x-form::lever name="name" :label="$label" />', ['label' => 'the <label>'], 'x-text="`the &lt;label&gt;`"'],
'js plain string' => ['<x-form::lever name="name" ::label="post.name" />', [], 'x-text="post.name"'],
'js plain string with <' => ['<x-form::lever name="name" ::label="post.<name" />', [], 'x-text="post.<name"'],
'raw id' => ['<x-form::lever name="name" ::id="post.id" />', [], ':id="post.id"'],
'raw id with for' => ['<x-form::lever name="name" ::id="post.id" />', [], ':for="post.id"'],
]);

View File

@ -0,0 +1,26 @@
<?php
namespace Tests\Unit\View\Form;
use Illuminate\Support\Facades\Blade;
use Illuminate\Support\Facades\View;
use Illuminate\Support\ViewErrorBag;
use Tests\TestCase;
uses(TestCase::class);
it('renders component', function ($component, $params, $expected) {
View::share(['errors' => new ViewErrorBag()]);
$rendered = Blade::render($component, $params);
dd($rendered);
expect($rendered)->toContain($expected);
})->with([
'php string' => ['<x-form::text name="name" label="the label" />', [], 'x-text="`the label`"'],
// 'php string with escaping' => ['<x-form::text name="name" label="the <label>" />', [], 'x-text="`the &lt;label&gt;`"'],
// 'php string with var' => ['<x-form::text name="name" :label="$label" />', ['label' => 'the <label>'], 'x-text="`the &lt;label&gt;`"'],
// 'js plain string' => ['<x-form::text name="name" ::label="post.name" />', [], 'x-text="post.name"'],
// 'js plain string with <' => ['<x-form::text name="name" ::label="post.<name" />', [], 'x-text="post.<name"'],
// 'raw id' => ['<x-form::text name="name" ::id="post.id" />', [], ':id="post.id"'],
// 'raw id with for' => ['<x-form::text name="name" ::id="post.id" />', [], ' :for="post.id"'],
]);