Compare commits
No commits in common. "44ea68cce7fda202d810d7d3e5b15a3713ac9db5" and "d692cf3bdf6c85c99f55a79e9a85208d0a366ca1" have entirely different histories.
44ea68cce7
...
d692cf3bdf
|
@ -5,7 +5,7 @@ RUN composer install --ignore-platform-reqs --no-dev
|
||||||
RUN php artisan telescope:publish
|
RUN php artisan telescope:publish
|
||||||
RUN php artisan horizon:publish
|
RUN php artisan horizon:publish
|
||||||
|
|
||||||
FROM node:18.13.0-slim as node
|
FROM node:17.9.0-slim as node
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
COPY . /app
|
COPY . /app
|
||||||
RUN npm install && npm run prod && npm run img && rm -R node_modules
|
RUN npm install && npm run prod && npm run img && rm -R node_modules
|
||||||
|
|
|
@ -22,9 +22,9 @@ steps:
|
||||||
- while ! mysqladmin ping -h db -u db -pdb --silent; do sleep 1; done
|
- while ! mysqladmin ping -h db -u db -pdb --silent; do sleep 1; done
|
||||||
|
|
||||||
- name: node
|
- name: node
|
||||||
image: node:18.13.0-slim
|
image: node:17.9.0-slim
|
||||||
commands:
|
commands:
|
||||||
- npm ci && cd packages/adrema-plugin && npm ci && npm run build && cd ../../ && npm run img && npm run prod && rm -R node_modules
|
- npm ci && npm run img && npm run prod && rm -R node_modules
|
||||||
|
|
||||||
- name: tests
|
- name: tests
|
||||||
image: zoomyboy/adrema-base:latest
|
image: zoomyboy/adrema-base:latest
|
||||||
|
|
|
@ -7,10 +7,3 @@
|
||||||
[submodule "packages/tex"]
|
[submodule "packages/tex"]
|
||||||
path = packages/tex
|
path = packages/tex
|
||||||
url = https://git.zoomyboy.de/pille/tex
|
url = https://git.zoomyboy.de/pille/tex
|
||||||
[submodule "packages/adrema-form"]
|
|
||||||
path = packages/adrema-form
|
|
||||||
url = https://git.zoomyboy.de/silva/adrema-form.git
|
|
||||||
[submodule "packages/medialibrary-helper"]
|
|
||||||
path = packages/medialibrary-helper
|
|
||||||
url = https://git.zoomyboy.de/zoomyboy/medialibrary-helper.git
|
|
||||||
branch = version2
|
|
||||||
|
|
|
@ -1,33 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Form\Actions;
|
|
||||||
|
|
||||||
use App\Form\Scopes\FormFilterScope;
|
|
||||||
use App\Form\Models\Form;
|
|
||||||
use App\Form\Resources\FormApiResource;
|
|
||||||
use Illuminate\Http\Resources\Json\AnonymousResourceCollection;
|
|
||||||
use Illuminate\Pagination\LengthAwarePaginator;
|
|
||||||
use Lorisleiva\Actions\ActionRequest;
|
|
||||||
use Lorisleiva\Actions\Concerns\AsAction;
|
|
||||||
|
|
||||||
class FormApiListAction
|
|
||||||
{
|
|
||||||
use AsAction;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string $filter
|
|
||||||
* @return LengthAwarePaginator<Form>
|
|
||||||
*/
|
|
||||||
public function handle(string $filter, int $perPage): LengthAwarePaginator
|
|
||||||
{
|
|
||||||
return FormFilterScope::fromRequest($filter)->getQuery()->paginate($perPage);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function asController(ActionRequest $request): AnonymousResourceCollection
|
|
||||||
{
|
|
||||||
return FormApiResource::collection($this->handle(
|
|
||||||
$request->input('filter', ''),
|
|
||||||
$request->input('perPage', 10)
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,19 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Form\Actions;
|
|
||||||
|
|
||||||
use App\Form\Models\Form;
|
|
||||||
use App\Lib\Events\Succeeded;
|
|
||||||
use Lorisleiva\Actions\Concerns\AsAction;
|
|
||||||
|
|
||||||
class FormDestroyAction
|
|
||||||
{
|
|
||||||
use AsAction;
|
|
||||||
|
|
||||||
public function asController(Form $form): void
|
|
||||||
{
|
|
||||||
$form->delete();
|
|
||||||
|
|
||||||
Succeeded::message('Veranstaltung gelöscht.')->dispatch();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,35 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Form\Actions;
|
|
||||||
|
|
||||||
use App\Form\Scopes\FormFilterScope;
|
|
||||||
use App\Form\Models\Form;
|
|
||||||
use App\Form\Resources\FormResource;
|
|
||||||
use Illuminate\Pagination\LengthAwarePaginator;
|
|
||||||
use Inertia\Inertia;
|
|
||||||
use Inertia\Response;
|
|
||||||
use Lorisleiva\Actions\ActionRequest;
|
|
||||||
use Lorisleiva\Actions\Concerns\AsAction;
|
|
||||||
|
|
||||||
class FormIndexAction
|
|
||||||
{
|
|
||||||
use AsAction;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return LengthAwarePaginator<Form>
|
|
||||||
*/
|
|
||||||
public function handle(string $filter): LengthAwarePaginator
|
|
||||||
{
|
|
||||||
return FormFilterScope::fromRequest($filter)->getQuery()->query(fn ($query) => $query->withCount('participants'))->paginate(15);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function asController(ActionRequest $request): Response
|
|
||||||
{
|
|
||||||
session()->put('menu', 'form');
|
|
||||||
session()->put('title', 'Veranstaltungen');
|
|
||||||
|
|
||||||
return Inertia::render('form/Index', [
|
|
||||||
'data' => FormResource::collection($this->handle($request->input('filter', ''))),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,69 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Form\Actions;
|
|
||||||
|
|
||||||
use App\Form\Models\Form;
|
|
||||||
use App\Lib\Events\Succeeded;
|
|
||||||
use Illuminate\Http\JsonResponse;
|
|
||||||
use Lorisleiva\Actions\Concerns\AsAction;
|
|
||||||
use Lorisleiva\Actions\ActionRequest;
|
|
||||||
|
|
||||||
class FormStoreAction
|
|
||||||
{
|
|
||||||
use AsAction;
|
|
||||||
use HasValidation;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return array<string, mixed>
|
|
||||||
*/
|
|
||||||
public function rules(): array
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
...$this->globalRules(),
|
|
||||||
'description.time' => 'required|integer',
|
|
||||||
'description.blocks' => 'required|array',
|
|
||||||
'description.version' => 'required|string',
|
|
||||||
'excerpt' => 'required|string|max:130',
|
|
||||||
'from' => 'required|date',
|
|
||||||
'to' => 'required|date',
|
|
||||||
'registration_from' => 'present|nullable|date',
|
|
||||||
'registration_until' => 'present|nullable|date',
|
|
||||||
'mail_top' => 'nullable|string',
|
|
||||||
'mail_bottom' => 'nullable|string',
|
|
||||||
'header_image' => 'required|exclude',
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param array<string, mixed> $attributes
|
|
||||||
*/
|
|
||||||
public function handle(array $attributes): Form
|
|
||||||
{
|
|
||||||
return tap(
|
|
||||||
Form::create($attributes),
|
|
||||||
fn ($form) => $form->setDeferredUploads(request()->input('header_image'))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return array<string, mixed>
|
|
||||||
*/
|
|
||||||
public function getValidationAttributes(): array
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
...$this->globalValidationAttributes(),
|
|
||||||
'from' => 'Start',
|
|
||||||
'to' => 'Ende',
|
|
||||||
'header_image' => 'Bild',
|
|
||||||
'description.blocks' => 'Beschreibung',
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
public function asController(ActionRequest $request): JsonResponse
|
|
||||||
{
|
|
||||||
$this->handle($request->validated());
|
|
||||||
|
|
||||||
Succeeded::message('Veranstaltung gespeichert.')->dispatch();
|
|
||||||
return response()->json([]);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,66 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Form\Actions;
|
|
||||||
|
|
||||||
use App\Form\Models\Form;
|
|
||||||
use App\Lib\Events\Succeeded;
|
|
||||||
use Illuminate\Http\JsonResponse;
|
|
||||||
use Lorisleiva\Actions\Concerns\AsAction;
|
|
||||||
use Lorisleiva\Actions\ActionRequest;
|
|
||||||
|
|
||||||
class FormUpdateAction
|
|
||||||
{
|
|
||||||
use AsAction;
|
|
||||||
use HasValidation;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return array<string, mixed>
|
|
||||||
*/
|
|
||||||
public function rules(): array
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
...$this->globalRules(),
|
|
||||||
'description' => 'required|array',
|
|
||||||
'description.time' => 'required|integer',
|
|
||||||
'description.blocks' => 'required|array',
|
|
||||||
'description.version' => 'required|string',
|
|
||||||
'excerpt' => 'required|string|max:130',
|
|
||||||
'from' => 'required|date',
|
|
||||||
'to' => 'required|date',
|
|
||||||
'registration_from' => 'present|nullable|date',
|
|
||||||
'registration_until' => 'present|nullable|date',
|
|
||||||
'mail_top' => 'nullable|string',
|
|
||||||
'mail_bottom' => 'nullable|string',
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param array<string, mixed> $attributes
|
|
||||||
*/
|
|
||||||
public function handle(Form $form, array $attributes): Form
|
|
||||||
{
|
|
||||||
$form->update($attributes);
|
|
||||||
return $form;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return array<string, mixed>
|
|
||||||
*/
|
|
||||||
public function getValidationAttributes(): array
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
...$this->globalValidationAttributes(),
|
|
||||||
'from' => 'Start',
|
|
||||||
'to' => 'Ende',
|
|
||||||
'description.blocks' => 'Beschreibung',
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
public function asController(Form $form, ActionRequest $request): JsonResponse
|
|
||||||
{
|
|
||||||
$this->handle($form, $request->validated());
|
|
||||||
|
|
||||||
Succeeded::message('Veranstaltung aktualisiert.')->dispatch();
|
|
||||||
return response()->json([]);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,46 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Form\Actions;
|
|
||||||
|
|
||||||
use App\Form\Models\Form;
|
|
||||||
use Illuminate\Validation\Rule;
|
|
||||||
use Lorisleiva\Actions\ActionRequest;
|
|
||||||
use Lorisleiva\Actions\Concerns\AsAction;
|
|
||||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
|
||||||
|
|
||||||
class FormUpdateMetaAction
|
|
||||||
{
|
|
||||||
use AsAction;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return array<string, mixed>
|
|
||||||
*/
|
|
||||||
public function rules(): array
|
|
||||||
{
|
|
||||||
/** @var Form */
|
|
||||||
$form = request()->route('form');
|
|
||||||
|
|
||||||
return [
|
|
||||||
'sorting' => 'array',
|
|
||||||
'sorting.0' => 'required|string',
|
|
||||||
'sorting.1' => 'required|string|in:asc,desc',
|
|
||||||
'active_columns' => 'array',
|
|
||||||
'active_columns.*' => ['string', Rule::in($form->getFields()->pluck('key')->toArray())]
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param array<string, mixed> $input
|
|
||||||
*/
|
|
||||||
public function handle(Form $form, array $input): void
|
|
||||||
{
|
|
||||||
$form->update(['meta' => $input]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function asController(Form $form, ActionRequest $request): JsonResponse
|
|
||||||
{
|
|
||||||
$this->handle($form, $request->validated());
|
|
||||||
|
|
||||||
return response()->json($form->fresh()->meta);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,27 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Form\Actions;
|
|
||||||
|
|
||||||
use App\Form\Models\Formtemplate;
|
|
||||||
use App\Lib\Events\Succeeded;
|
|
||||||
use Illuminate\Http\JsonResponse;
|
|
||||||
use Lorisleiva\Actions\Concerns\AsAction;
|
|
||||||
|
|
||||||
class FormtemplateDestroyAction
|
|
||||||
{
|
|
||||||
use AsAction;
|
|
||||||
|
|
||||||
public function handle(Formtemplate $formtemplate): void
|
|
||||||
{
|
|
||||||
$formtemplate->delete();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function asController(Formtemplate $formtemplate): JsonResponse
|
|
||||||
{
|
|
||||||
$this->handle($formtemplate);
|
|
||||||
|
|
||||||
Succeeded::message('Vorlage gelöscht.')->dispatch();
|
|
||||||
|
|
||||||
return response()->json([]);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,33 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Form\Actions;
|
|
||||||
|
|
||||||
use App\Form\Models\Formtemplate;
|
|
||||||
use App\Form\Resources\FormtemplateResource;
|
|
||||||
use Illuminate\Pagination\LengthAwarePaginator;
|
|
||||||
use Inertia\Inertia;
|
|
||||||
use Inertia\Response;
|
|
||||||
use Lorisleiva\Actions\Concerns\AsAction;
|
|
||||||
|
|
||||||
class FormtemplateIndexAction
|
|
||||||
{
|
|
||||||
use AsAction;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return LengthAwarePaginator<Formtemplate>
|
|
||||||
*/
|
|
||||||
public function handle(): LengthAwarePaginator
|
|
||||||
{
|
|
||||||
return Formtemplate::paginate(15);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function asController(): Response
|
|
||||||
{
|
|
||||||
session()->put('menu', 'form');
|
|
||||||
session()->put('title', 'Formular-Vorlagen');
|
|
||||||
|
|
||||||
return Inertia::render('formtemplate/Index', [
|
|
||||||
'data' => FormtemplateResource::collection($this->handle()),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,51 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Form\Actions;
|
|
||||||
|
|
||||||
use App\Form\Models\Formtemplate;
|
|
||||||
use App\Lib\Events\Succeeded;
|
|
||||||
use Illuminate\Http\JsonResponse;
|
|
||||||
use Lorisleiva\Actions\ActionRequest;
|
|
||||||
use Lorisleiva\Actions\Concerns\AsAction;
|
|
||||||
|
|
||||||
class FormtemplateStoreAction
|
|
||||||
{
|
|
||||||
use AsAction;
|
|
||||||
use HasValidation;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return array<string, mixed>
|
|
||||||
*/
|
|
||||||
public function rules(): array
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
...$this->globalRules(),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return array<string, mixed>
|
|
||||||
*/
|
|
||||||
public function getValidationAttributes(): array
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
...$this->globalValidationAttributes(),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param array<string, mixed> $attributes
|
|
||||||
*/
|
|
||||||
public function handle(array $attributes): Formtemplate
|
|
||||||
{
|
|
||||||
return Formtemplate::create($attributes);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function asController(ActionRequest $request): JsonResponse
|
|
||||||
{
|
|
||||||
$this->handle($request->validated());
|
|
||||||
|
|
||||||
Succeeded::message('Vorlage gespeichert.')->dispatch();
|
|
||||||
return response()->json([]);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,52 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Form\Actions;
|
|
||||||
|
|
||||||
use App\Form\Models\Formtemplate;
|
|
||||||
use App\Lib\Events\Succeeded;
|
|
||||||
use Illuminate\Http\JsonResponse;
|
|
||||||
use Lorisleiva\Actions\ActionRequest;
|
|
||||||
use Lorisleiva\Actions\Concerns\AsAction;
|
|
||||||
|
|
||||||
class FormtemplateUpdateAction
|
|
||||||
{
|
|
||||||
use AsAction;
|
|
||||||
use HasValidation;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return array<string, mixed>
|
|
||||||
*/
|
|
||||||
public function rules(): array
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
...$this->globalRules(),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return array<string, mixed>
|
|
||||||
*/
|
|
||||||
public function getValidationAttributes(): array
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
...$this->globalValidationAttributes(),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param array<string, mixed> $attributes
|
|
||||||
*/
|
|
||||||
public function handle(Formtemplate $formtemplate, array $attributes): void
|
|
||||||
{
|
|
||||||
$formtemplate->update($attributes);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function asController(Formtemplate $formtemplate, ActionRequest $request): JsonResponse
|
|
||||||
{
|
|
||||||
$this->handle($formtemplate, $request->validated());
|
|
||||||
|
|
||||||
Succeeded::message('Vorlage aktualisiert.')->dispatch();
|
|
||||||
|
|
||||||
return response()->json([]);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,67 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Form\Actions;
|
|
||||||
|
|
||||||
use Illuminate\Validation\Rule;
|
|
||||||
use App\Form\Fields\Field;
|
|
||||||
use Illuminate\Validation\Validator;
|
|
||||||
use Lorisleiva\Actions\ActionRequest;
|
|
||||||
|
|
||||||
trait HasValidation
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @return array<string, mixed>
|
|
||||||
*/
|
|
||||||
public function globalRules(): array
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
'name' => 'required|string|max:255',
|
|
||||||
'config' => 'array',
|
|
||||||
'config.sections.*.name' => 'required',
|
|
||||||
'config.sections.*.fields' => 'array',
|
|
||||||
'config.sections.*.fields.*.name' => 'required|string',
|
|
||||||
'config.sections.*.fields.*.type' => ['required', 'string', Rule::in(array_column(Field::asMeta(), 'id'))],
|
|
||||||
'config.sections.*.fields.*.key' => ['required', 'string', 'regex:/^[a-zA-Z_]*$/'],
|
|
||||||
'config.sections.*.fields.*.columns' => 'required|array',
|
|
||||||
'config.sections.*.fields.*.*' => '',
|
|
||||||
'config.sections.*.fields.*.columns.mobile' => 'required|numeric|gt:0|lte:2',
|
|
||||||
'config.sections.*.fields.*.columns.tablet' => 'required|numeric|gt:0|lte:4',
|
|
||||||
'config.sections.*.fields.*.columns.desktop' => 'required|numeric|gt:0|lte:6',
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return array<string, mixed>
|
|
||||||
*/
|
|
||||||
public function globalValidationAttributes(): array
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
'config.sections.*.name' => 'Sektionsname',
|
|
||||||
'config.sections.*.fields.*.name' => 'Feldname',
|
|
||||||
'config.sections.*.fields.*.type' => 'Feldtyp',
|
|
||||||
'config.sections.*.fields.*.key' => 'Feldkey',
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
public function withValidator(Validator $validator, ActionRequest $request): void
|
|
||||||
{
|
|
||||||
if (!$validator->passes()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($request->input('config.sections') as $sindex => $section) {
|
|
||||||
foreach (data_get($section, 'fields') as $findex => $field) {
|
|
||||||
$fieldClass = Field::classFromType($field['type']);
|
|
||||||
if (!$fieldClass) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
foreach ($fieldClass::metaRules() as $fieldName => $rules) {
|
|
||||||
$validator->addRules(["config.sections.{$sindex}.fields.{$findex}.{$fieldName}" => $rules]);
|
|
||||||
}
|
|
||||||
foreach ($fieldClass::metaAttributes() as $fieldName => $attribute) {
|
|
||||||
$validator->addCustomAttributes(["config.sections.{$sindex}.fields.{$findex}.{$fieldName}" => $attribute]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,31 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Form\Actions;
|
|
||||||
|
|
||||||
use App\Form\Models\Form;
|
|
||||||
use App\Form\Models\Participant;
|
|
||||||
use App\Form\Resources\ParticipantResource;
|
|
||||||
use App\Form\Scopes\ParticipantFilterScope;
|
|
||||||
use Illuminate\Http\Resources\Json\AnonymousResourceCollection;
|
|
||||||
use Illuminate\Pagination\LengthAwarePaginator;
|
|
||||||
use Lorisleiva\Actions\Concerns\AsAction;
|
|
||||||
|
|
||||||
class ParticipantIndexAction
|
|
||||||
{
|
|
||||||
use AsAction;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return LengthAwarePaginator<Participant>
|
|
||||||
*/
|
|
||||||
public function handle(Form $form, ParticipantFilterScope $filter): LengthAwarePaginator
|
|
||||||
{
|
|
||||||
return $form->participants()->withFilter($filter)->with('form')->paginate(15);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function asController(Form $form): AnonymousResourceCollection
|
|
||||||
{
|
|
||||||
$filter = ParticipantFilterScope::fromRequest(request()->input('filter'));
|
|
||||||
return ParticipantResource::collection($this->handle($form, $filter))
|
|
||||||
->additional(['meta' => ParticipantResource::meta($form)]);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,78 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Form\Actions;
|
|
||||||
|
|
||||||
use App\Form\Data\FieldCollection;
|
|
||||||
use App\Form\Models\Form;
|
|
||||||
use App\Form\Models\Participant;
|
|
||||||
use App\Member\Member;
|
|
||||||
use Illuminate\Http\JsonResponse;
|
|
||||||
use Lorisleiva\Actions\ActionRequest;
|
|
||||||
use Lorisleiva\Actions\Concerns\AsAction;
|
|
||||||
|
|
||||||
class RegisterAction
|
|
||||||
{
|
|
||||||
use AsAction;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param array<string, mixed> $input
|
|
||||||
*/
|
|
||||||
public function handle(Form $form, array $input): Participant
|
|
||||||
{
|
|
||||||
$memberQuery = FieldCollection::fromRequest($form, $input)
|
|
||||||
->withNamiType()
|
|
||||||
->reduce(fn ($query, $field) => $field->namiType->performQuery($query, $field->value), (new Member())->newQuery());
|
|
||||||
$mglnr = $form->getFields()->withNamiType()->count() && $memberQuery->count() === 1 ? $memberQuery->first()->mitgliedsnr : null;
|
|
||||||
|
|
||||||
$participant = $form->participants()->create([
|
|
||||||
'data' => $input,
|
|
||||||
'mitgliedsnr' => $mglnr,
|
|
||||||
]);
|
|
||||||
|
|
||||||
$form->getFields()->each(fn ($field) => $field->afterRegistration($form, $participant, $input));
|
|
||||||
|
|
||||||
$participant->sendConfirmationMail();
|
|
||||||
|
|
||||||
return $participant;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return array<string, mixed>
|
|
||||||
*/
|
|
||||||
public function rules(): array
|
|
||||||
{
|
|
||||||
/** @var Form */
|
|
||||||
$form = request()->route('form');
|
|
||||||
|
|
||||||
return $form->getRegistrationRules();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return array<string, mixed>
|
|
||||||
*/
|
|
||||||
public function getValidationAttributes(): array
|
|
||||||
{
|
|
||||||
/** @var Form */
|
|
||||||
$form = request()->route('form');
|
|
||||||
|
|
||||||
return $form->getRegistrationAttributes();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return array<string, mixed>
|
|
||||||
*/
|
|
||||||
public function getValidationMessages(): array
|
|
||||||
{
|
|
||||||
/** @var Form */
|
|
||||||
$form = request()->route('form');
|
|
||||||
|
|
||||||
return $form->getRegistrationMessages();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function asController(ActionRequest $request, Form $form): JsonResponse
|
|
||||||
{
|
|
||||||
$participant = $this->handle($form, $request->validated());
|
|
||||||
|
|
||||||
return response()->json($participant);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,30 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Form\Casts;
|
|
||||||
|
|
||||||
use App\Form\Fields\Field;
|
|
||||||
use Illuminate\Support\Collection;
|
|
||||||
use Spatie\LaravelData\Casts\Cast;
|
|
||||||
use Spatie\LaravelData\Data;
|
|
||||||
use Spatie\LaravelData\Support\DataProperty;
|
|
||||||
|
|
||||||
class CollectionCast implements Cast
|
|
||||||
{
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param class-string<Data> $target
|
|
||||||
*/
|
|
||||||
public function __construct(public string $target)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param array<int, array<string, mixed>> $value
|
|
||||||
* @param array<string, mixed> $context
|
|
||||||
* @return Collection<int, Data>
|
|
||||||
*/
|
|
||||||
public function cast(DataProperty $property, mixed $value, array $context): mixed
|
|
||||||
{
|
|
||||||
return collect($value)->map(fn ($item) => $this->target::from($item));
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,21 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Form\Casts;
|
|
||||||
|
|
||||||
use App\Form\Data\FieldCollection;
|
|
||||||
use App\Form\Fields\Field;
|
|
||||||
use Spatie\LaravelData\Casts\Cast;
|
|
||||||
use Spatie\LaravelData\Support\DataProperty;
|
|
||||||
|
|
||||||
class FieldCollectionCast implements Cast
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @param array<int, array<string, mixed>> $value
|
|
||||||
* @param array<string, mixed> $context
|
|
||||||
* @return FieldCollection
|
|
||||||
*/
|
|
||||||
public function cast(DataProperty $property, mixed $value, array $context): mixed
|
|
||||||
{
|
|
||||||
return new FieldCollection(collect($value)->map(fn ($value) => Field::classFromType($value['type'])::from($value))->all());
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,16 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Form\Data;
|
|
||||||
|
|
||||||
use Spatie\LaravelData\Data;
|
|
||||||
|
|
||||||
class ColumnData extends Data
|
|
||||||
{
|
|
||||||
|
|
||||||
public function __construct(
|
|
||||||
public int $mobile,
|
|
||||||
public int $tablet,
|
|
||||||
public int $desktop,
|
|
||||||
) {
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,91 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Form\Data;
|
|
||||||
|
|
||||||
use App\Form\Enums\SpecialType;
|
|
||||||
use App\Form\Fields\Field;
|
|
||||||
use App\Form\Fields\NamiField;
|
|
||||||
use App\Form\Models\Form;
|
|
||||||
use Illuminate\Support\Collection;
|
|
||||||
use stdClass;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @extends Collection<int, Field>
|
|
||||||
*/
|
|
||||||
class FieldCollection extends Collection
|
|
||||||
{
|
|
||||||
|
|
||||||
public function forMembers(): self
|
|
||||||
{
|
|
||||||
return $this->filter(fn ($field) => $field->forMembers === true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function withNamiType(): self
|
|
||||||
{
|
|
||||||
return $this->filter(fn ($field) => $field->namiType !== null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function noNamiType(): self
|
|
||||||
{
|
|
||||||
return $this->filter(fn ($field) => $field->namiType === null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function noNamiField(): self
|
|
||||||
{
|
|
||||||
return $this->filter(fn ($field) => !is_a($field, NamiField::class));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return stdClass
|
|
||||||
*/
|
|
||||||
public function getMailRecipient(): ?stdClass
|
|
||||||
{
|
|
||||||
$email = $this->findBySpecialType(SpecialType::EMAIL)?->value;
|
|
||||||
|
|
||||||
return $this->getFullname() && $email
|
|
||||||
? (object) [
|
|
||||||
'name' => $this->getFullname(),
|
|
||||||
"email" => $email,
|
|
||||||
] : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getFullname(): ?string
|
|
||||||
{
|
|
||||||
$firstname = $this->findBySpecialType(SpecialType::FIRSTNAME)?->value;
|
|
||||||
$lastname = $this->findBySpecialType(SpecialType::LASTNAME)?->value;
|
|
||||||
|
|
||||||
return $firstname && $lastname ? "$firstname $lastname" : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param array<string, mixed> $input
|
|
||||||
*/
|
|
||||||
public static function fromRequest(Form $form, array $input): self
|
|
||||||
{
|
|
||||||
return $form->getFields()->each(fn ($field) => $field->value = array_key_exists($field->key, $input) ? $input[$field->key] : $field->default());
|
|
||||||
}
|
|
||||||
|
|
||||||
public function find(Field $givenField): ?Field
|
|
||||||
{
|
|
||||||
return $this->first(fn ($field) => $field->key === $givenField->key);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return array<string, mixed>
|
|
||||||
*/
|
|
||||||
public function present(): array
|
|
||||||
{
|
|
||||||
$attributes = collect([]);
|
|
||||||
|
|
||||||
foreach ($this as $field) {
|
|
||||||
$attributes = $attributes->merge($field->present());
|
|
||||||
}
|
|
||||||
|
|
||||||
return $attributes->toArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
private function findBySpecialType(SpecialType $specialType): ?Field
|
|
||||||
{
|
|
||||||
return $this->first(fn ($field) => $field->specialType === $specialType);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,32 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Form\Data;
|
|
||||||
|
|
||||||
use App\Form\Casts\CollectionCast;
|
|
||||||
use App\Form\Transformers\CollectionTransformer;
|
|
||||||
use Illuminate\Support\Collection;
|
|
||||||
use Spatie\LaravelData\Data;
|
|
||||||
use Spatie\LaravelData\Attributes\WithCast;
|
|
||||||
use Spatie\LaravelData\Attributes\WithTransformer;
|
|
||||||
|
|
||||||
class FormConfigData extends Data
|
|
||||||
{
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param Collection<int, SectionData> $sections
|
|
||||||
*/
|
|
||||||
public function __construct(
|
|
||||||
#[WithCast(CollectionCast::class, target: SectionData::class)]
|
|
||||||
#[WithTransformer(CollectionTransformer::class, target: SectionData::class)]
|
|
||||||
public Collection $sections
|
|
||||||
) {
|
|
||||||
}
|
|
||||||
|
|
||||||
public function fields(): FieldCollection
|
|
||||||
{
|
|
||||||
return $this->sections->reduce(
|
|
||||||
fn ($carry, $current) => $carry->merge($current->fields->all()),
|
|
||||||
new FieldCollection([])
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,21 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Form\Data;
|
|
||||||
|
|
||||||
use App\Form\Casts\FieldCollectionCast;
|
|
||||||
use Spatie\LaravelData\Data;
|
|
||||||
use App\Form\Transformers\FieldCollectionTransformer;
|
|
||||||
use Spatie\LaravelData\Attributes\WithCast;
|
|
||||||
use Spatie\LaravelData\Attributes\WithTransformer;
|
|
||||||
|
|
||||||
class SectionData extends Data
|
|
||||||
{
|
|
||||||
|
|
||||||
public function __construct(
|
|
||||||
public string $name,
|
|
||||||
#[WithCast(FieldCollectionCast::class)]
|
|
||||||
#[WithTransformer(FieldCollectionTransformer::class)]
|
|
||||||
public FieldCollection $fields
|
|
||||||
) {
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,77 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Form\Enums;
|
|
||||||
|
|
||||||
use App\Group\Enums\Level;
|
|
||||||
use App\Member\Member;
|
|
||||||
use Illuminate\Database\Eloquent\Builder;
|
|
||||||
|
|
||||||
enum NamiType: string
|
|
||||||
{
|
|
||||||
case FIRSTNAME = 'Vorname';
|
|
||||||
case LASTNAME = 'Nachname';
|
|
||||||
case BIRTHDAY = 'Geburtstag';
|
|
||||||
case REGION = 'Bezirk';
|
|
||||||
case STAMM = 'Stamm';
|
|
||||||
case EMAIL = 'E-Mail-Adresse';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return array<int, array{name: string, id: string}>
|
|
||||||
*/
|
|
||||||
public static function forSelect(): array
|
|
||||||
{
|
|
||||||
return collect(static::cases())
|
|
||||||
->map(fn ($case) => ['id' => $case->value, 'name' => $case->value])
|
|
||||||
->toArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getMemberAttribute(Member $member): ?string
|
|
||||||
{
|
|
||||||
return match ($this) {
|
|
||||||
static::FIRSTNAME => $member->firstname,
|
|
||||||
static::LASTNAME => $member->lastname,
|
|
||||||
static::BIRTHDAY => $member->birthday?->format('Y-m-d'),
|
|
||||||
static::REGION => $this->matchRegion($member),
|
|
||||||
static::STAMM => $this->matchGroup($member),
|
|
||||||
static::EMAIL => $member->email,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param Builder<Member> $query
|
|
||||||
* @return Builder<Member>
|
|
||||||
*/
|
|
||||||
public function performQuery(Builder $query, mixed $value): Builder
|
|
||||||
{
|
|
||||||
return match ($this) {
|
|
||||||
static::FIRSTNAME => $query->where('firstname', $value),
|
|
||||||
static::LASTNAME => $query->where('lastname', $value),
|
|
||||||
static::BIRTHDAY => $query->where('birthday', $value),
|
|
||||||
static::REGION => $query,
|
|
||||||
static::STAMM => $query,
|
|
||||||
static::EMAIL => $query->where('email', $value),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function matchGroup(Member $member): ?int
|
|
||||||
{
|
|
||||||
if ($member->group->level === Level::GROUP) {
|
|
||||||
return $member->group_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function matchRegion(Member $member): ?int
|
|
||||||
{
|
|
||||||
if ($member->group->parent?->level === Level::REGION) {
|
|
||||||
return $member->group->parent->id;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($member->group->level === Level::REGION) {
|
|
||||||
return $member->group_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,20 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Form\Enums;
|
|
||||||
|
|
||||||
enum SpecialType: string
|
|
||||||
{
|
|
||||||
case FIRSTNAME = 'Vorname';
|
|
||||||
case LASTNAME = 'Nachname';
|
|
||||||
case EMAIL = 'E-Mail-Adresse';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return array<int, array{name: string, id: string}>
|
|
||||||
*/
|
|
||||||
public static function forSelect(): array
|
|
||||||
{
|
|
||||||
return collect(static::cases())
|
|
||||||
->map(fn ($case) => ['id' => $case->value, 'name' => $case->value])
|
|
||||||
->toArray();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,75 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Form\Fields;
|
|
||||||
|
|
||||||
use App\Form\Models\Form;
|
|
||||||
use App\Form\Models\Participant;
|
|
||||||
use Faker\Generator;
|
|
||||||
use Illuminate\Validation\Rule;
|
|
||||||
|
|
||||||
class CheckboxField extends Field
|
|
||||||
{
|
|
||||||
public bool $required;
|
|
||||||
public string $description;
|
|
||||||
|
|
||||||
public static function name(): string
|
|
||||||
{
|
|
||||||
return 'Checkbox';
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function meta(): array
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
['key' => 'description', 'default' => '', 'rules' => ['description' => 'required|string'], 'label' => 'Beschreibung'],
|
|
||||||
['key' => 'required', 'default' => false, 'rules' => ['required' => 'present|boolean'], 'label' => 'Erforderlich'],
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function default()
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function fake(Generator $faker): array
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
'description' => $faker->text(),
|
|
||||||
'required' => $faker->boolean(),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @inheritdoc
|
|
||||||
*/
|
|
||||||
public function getRegistrationRules(Form $form): array
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
$this->key => $this->required ? ['boolean', 'accepted'] : ['present', 'boolean'],
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @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,82 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Form\Fields;
|
|
||||||
|
|
||||||
use App\Form\Models\Form;
|
|
||||||
use App\Form\Models\Participant;
|
|
||||||
use App\Form\Presenters\EnumPresenter;
|
|
||||||
use App\Form\Presenters\Presenter;
|
|
||||||
use Faker\Generator;
|
|
||||||
use Illuminate\Validation\Rule;
|
|
||||||
|
|
||||||
class CheckboxesField extends Field
|
|
||||||
{
|
|
||||||
/** @var array<int, string> */
|
|
||||||
public array $options;
|
|
||||||
|
|
||||||
public static function name(): string
|
|
||||||
{
|
|
||||||
return 'Checkboxes';
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function meta(): array
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
['key' => 'options', 'default' => [], 'rules' => ['options' => 'array', 'options.*' => 'required|string'], 'label' => 'Optionen'],
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function default()
|
|
||||||
{
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function fake(Generator $faker): array
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
'options' => $faker->words(4),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @inheritdoc
|
|
||||||
*/
|
|
||||||
public function getRegistrationRules(Form $form): array
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
$this->key => 'array',
|
|
||||||
$this->key . '.*' => ['string', Rule::in($this->options)],
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @inheritdoc
|
|
||||||
*/
|
|
||||||
public function getRegistrationAttributes(Form $form): array
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
...collect($this->options)->mapWithKeys(fn ($option, $key) => [$this->key . '.' . $key => $this->name])->toArray(),
|
|
||||||
$this->key => $this->name,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @inheritdoc
|
|
||||||
*/
|
|
||||||
public function getRegistrationMessages(Form $form): array
|
|
||||||
{
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getPresenter(): Presenter
|
|
||||||
{
|
|
||||||
return app(EnumPresenter::class);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @inheritdoc
|
|
||||||
*/
|
|
||||||
public function afterRegistration(Form $form, Participant $participant, array $input): void
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,92 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Form\Fields;
|
|
||||||
|
|
||||||
use App\Form\Contracts\Displayable;
|
|
||||||
use App\Form\Models\Form;
|
|
||||||
use App\Form\Models\Participant;
|
|
||||||
use App\Form\Presenters\DatePresenter;
|
|
||||||
use App\Form\Presenters\Presenter;
|
|
||||||
use Carbon\Carbon;
|
|
||||||
use Faker\Generator;
|
|
||||||
|
|
||||||
class DateField extends Field
|
|
||||||
{
|
|
||||||
|
|
||||||
public bool $required;
|
|
||||||
public bool $maxToday;
|
|
||||||
|
|
||||||
public static function name(): string
|
|
||||||
{
|
|
||||||
return 'Datum';
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function meta(): array
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
['key' => 'required', 'default' => false, 'rules' => ['required' => 'present|boolean'], 'label' => 'Erforderlich'],
|
|
||||||
['key' => 'max_today', 'default' => false, 'rules' => ['required' => 'present|boolean'], 'label' => 'Nur daten bis heute erlauben'],
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function default(): ?string
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function fake(Generator $faker): array
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
'required' => $faker->boolean(),
|
|
||||||
'max_today' => $faker->boolean(),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @inheritdoc
|
|
||||||
*/
|
|
||||||
public function getRegistrationRules(Form $form): array
|
|
||||||
{
|
|
||||||
$rules = [$this->required ? 'required' : 'nullable'];
|
|
||||||
|
|
||||||
$rules[] = 'date';
|
|
||||||
|
|
||||||
if ($this->maxToday) {
|
|
||||||
$rules[] = 'before_or_equal:' . now()->format('Y-m-d');
|
|
||||||
}
|
|
||||||
|
|
||||||
return [$this->key => $rules];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @inheritdoc
|
|
||||||
*/
|
|
||||||
public function getRegistrationAttributes(Form $form): array
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
$this->key => $this->name,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @inheritdoc
|
|
||||||
*/
|
|
||||||
public function getRegistrationMessages(Form $form): array
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
$this->key . '.before_or_equal' => $this->name . ' muss ein Datum vor oder gleich dem ' . now()->format('d.m.Y') . ' sein.',
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getPresenter(): Presenter
|
|
||||||
{
|
|
||||||
return app(DatePresenter::class);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @inheritdoc
|
|
||||||
*/
|
|
||||||
public function afterRegistration(Form $form, Participant $participant, array $input): void
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,74 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Form\Fields;
|
|
||||||
|
|
||||||
use App\Form\Models\Form;
|
|
||||||
use App\Form\Models\Participant;
|
|
||||||
use Faker\Generator;
|
|
||||||
use Illuminate\Validation\Rule;
|
|
||||||
|
|
||||||
class DropdownField extends Field
|
|
||||||
{
|
|
||||||
public bool $required;
|
|
||||||
/** @var array<int, string> */
|
|
||||||
public array $options;
|
|
||||||
|
|
||||||
public static function name(): string
|
|
||||||
{
|
|
||||||
return 'Dropdown';
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function meta(): array
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
['key' => 'options', 'default' => [], 'rules' => ['options' => 'present|array', 'options.*' => 'string'], 'label' => 'Optionen'],
|
|
||||||
['key' => 'required', 'default' => false, 'rules' => ['required' => 'present|boolean'], 'label' => 'Erforderlich'],
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function default()
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function fake(Generator $faker): array
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
'options' => $faker->words(4),
|
|
||||||
'required' => $faker->boolean(),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @inheritdoc
|
|
||||||
*/
|
|
||||||
public function getRegistrationRules(Form $form): array
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
$this->key => $this->required ? ['required', 'string', Rule::in($this->options)] : ['nullable', 'string', Rule::in($this->options)],
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @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,167 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Form\Fields;
|
|
||||||
|
|
||||||
use App\Form\Data\ColumnData;
|
|
||||||
use App\Form\Enums\NamiType;
|
|
||||||
use App\Form\Enums\SpecialType;
|
|
||||||
use App\Form\Models\Form;
|
|
||||||
use App\Form\Models\Participant;
|
|
||||||
use App\Form\Presenters\DefaultPresenter;
|
|
||||||
use App\Form\Presenters\Presenter;
|
|
||||||
use Faker\Generator;
|
|
||||||
use Spatie\LaravelData\Data;
|
|
||||||
use Spatie\LaravelData\Attributes\MapInputName;
|
|
||||||
use Spatie\LaravelData\Attributes\MapOutputName;
|
|
||||||
use Spatie\LaravelData\Mappers\SnakeCaseMapper;
|
|
||||||
|
|
||||||
#[MapInputName(SnakeCaseMapper::class)]
|
|
||||||
#[MapOutputName(SnakeCaseMapper::class)]
|
|
||||||
abstract class Field extends Data
|
|
||||||
{
|
|
||||||
|
|
||||||
public string $key;
|
|
||||||
public string $name;
|
|
||||||
public ?NamiType $namiType = null;
|
|
||||||
public ColumnData $columns;
|
|
||||||
public bool $forMembers;
|
|
||||||
public ?SpecialType $specialType = null;
|
|
||||||
|
|
||||||
/** @var mixed */
|
|
||||||
public $value;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param array<array-key, mixed> $input
|
|
||||||
*/
|
|
||||||
abstract public function afterRegistration(Form $form, Participant $participant, array $input): void;
|
|
||||||
|
|
||||||
abstract public static function name(): string;
|
|
||||||
|
|
||||||
/** @return array<int, array{key: string, default: mixed, label: string, rules: array<string, mixed>}> */
|
|
||||||
abstract public static function meta(): array;
|
|
||||||
|
|
||||||
/** @return mixed */
|
|
||||||
abstract public static function default();
|
|
||||||
|
|
||||||
/** @return array<string, mixed> */
|
|
||||||
abstract public function getRegistrationRules(Form $form): array;
|
|
||||||
|
|
||||||
/** @return array<string, mixed> */
|
|
||||||
abstract public function getRegistrationAttributes(Form $form): array;
|
|
||||||
|
|
||||||
/** @return array<string, mixed> */
|
|
||||||
abstract public function getRegistrationMessages(Form $form): array;
|
|
||||||
|
|
||||||
/** @return array<string, mixed> */
|
|
||||||
abstract public static function fake(Generator $faker): array;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return array<int, array<string, mixed>>
|
|
||||||
*/
|
|
||||||
public static function asMeta(): array
|
|
||||||
{
|
|
||||||
return array_map(fn ($class) => $class::allMeta(), self::classNames());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return array<int, class-string<self>>
|
|
||||||
*/
|
|
||||||
private static function classNames(): array
|
|
||||||
{
|
|
||||||
return collect(glob(base_path('app/Form/Fields/*.php')))
|
|
||||||
->filter(fn ($fieldClass) => preg_match('/[A-Za-z]Field\.php$/', $fieldClass) === 1)
|
|
||||||
->map(fn ($fieldClass) => str($fieldClass)->replace(base_path(''), '')->replace('/app', '/App')->replace('.php', '')->replace('/', '\\')->toString())
|
|
||||||
->values()
|
|
||||||
->toArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function classFromType(string $type): ?string
|
|
||||||
{
|
|
||||||
/** @var class-string<Field> */
|
|
||||||
$fieldClass = '\\App\\Form\\Fields\\' . $type;
|
|
||||||
if (!class_exists($fieldClass)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $fieldClass;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return mixed
|
|
||||||
*/
|
|
||||||
public function present()
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
$this->key => $this->value,
|
|
||||||
$this->getDisplayAttribute() => $this->presentRaw(),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return mixed
|
|
||||||
*/
|
|
||||||
public function presentRaw()
|
|
||||||
{
|
|
||||||
return $this->getPresenter()->present($this->value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return array<string, string>
|
|
||||||
*/
|
|
||||||
public static function metaAttributes(): array
|
|
||||||
{
|
|
||||||
return collect(static::meta())->mapWithKeys(fn ($meta) => [$meta['key'] => $meta['label']])->toArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return array<string, mixed>
|
|
||||||
**/
|
|
||||||
public static function metaRules(): array
|
|
||||||
{
|
|
||||||
$result = [];
|
|
||||||
foreach (static::meta() as $meta) {
|
|
||||||
foreach ($meta['rules'] as $fieldName => $rules) {
|
|
||||||
$result[$fieldName] = $rules;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function type(): string
|
|
||||||
{
|
|
||||||
return class_basename(static::class);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return array<string, mixed>
|
|
||||||
*/
|
|
||||||
public static function allMeta(): array
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
'id' => static::type(),
|
|
||||||
'name' => static::name(),
|
|
||||||
'default' => [
|
|
||||||
'name' => '',
|
|
||||||
'type' => static::type(),
|
|
||||||
'columns' => ['mobile' => 2, 'tablet' => 4, 'desktop' => 6],
|
|
||||||
'value' => static::default(),
|
|
||||||
'required' => false,
|
|
||||||
'nami_type' => null,
|
|
||||||
'for_members' => true,
|
|
||||||
'special_type' => null,
|
|
||||||
...collect(static::meta())->mapWithKeys(fn ($meta) => [$meta['key'] => $meta['default']])->toArray(),
|
|
||||||
],
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getPresenter(): Presenter
|
|
||||||
{
|
|
||||||
return app(DefaultPresenter::class);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getDisplayAttribute(): string
|
|
||||||
{
|
|
||||||
return $this->key . '_display';
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,96 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Form\Fields;
|
|
||||||
|
|
||||||
use App\Form\Models\Form;
|
|
||||||
use App\Form\Models\Participant;
|
|
||||||
use App\Form\Presenters\GroupPresenter;
|
|
||||||
use App\Form\Presenters\Presenter;
|
|
||||||
use App\Group;
|
|
||||||
use App\Group\Enums\Level;
|
|
||||||
use Faker\Generator;
|
|
||||||
use Illuminate\Validation\Rule;
|
|
||||||
|
|
||||||
class GroupField extends Field
|
|
||||||
{
|
|
||||||
public bool $required;
|
|
||||||
public ?string $parentField = null;
|
|
||||||
public ?int $parentGroup = null;
|
|
||||||
|
|
||||||
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,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @inheritdoc
|
|
||||||
*/
|
|
||||||
public function getRegistrationRules(Form $form): array
|
|
||||||
{
|
|
||||||
|
|
||||||
$rules = [$this->required ? 'required' : 'nullable'];
|
|
||||||
|
|
||||||
$rules[] = 'integer';
|
|
||||||
|
|
||||||
if ($this->parentGroup) {
|
|
||||||
$rules[] = Rule::in(Group::find($this->parentGroup)->children()->pluck('id'));
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($this->parentField && request()->input($this->parentField)) {
|
|
||||||
$rules[] = Rule::in(Group::find(request()->input($this->parentField))->children()->pluck('id'));
|
|
||||||
}
|
|
||||||
|
|
||||||
return [$this->key => $rules];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @inheritdoc
|
|
||||||
*/
|
|
||||||
public function getRegistrationAttributes(Form $form): array
|
|
||||||
{
|
|
||||||
return [$this->key => $this->name];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @inheritdoc
|
|
||||||
*/
|
|
||||||
public function getRegistrationMessages(Form $form): array
|
|
||||||
{
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getPresenter(): Presenter
|
|
||||||
{
|
|
||||||
return app(GroupPresenter::class);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @inheritdoc
|
|
||||||
*/
|
|
||||||
public function afterRegistration(Form $form, Participant $participant, array $input): void
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,131 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Form\Fields;
|
|
||||||
|
|
||||||
use App\Form\Data\FieldCollection;
|
|
||||||
use App\Form\Models\Form;
|
|
||||||
use App\Form\Models\Participant;
|
|
||||||
use App\Form\Presenters\NamiPresenter;
|
|
||||||
use App\Form\Presenters\Presenter;
|
|
||||||
use App\Member\Member;
|
|
||||||
use Faker\Generator;
|
|
||||||
|
|
||||||
class NamiField extends Field
|
|
||||||
{
|
|
||||||
|
|
||||||
public static function name(): string
|
|
||||||
{
|
|
||||||
return 'NaMi-Mitglieder';
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function meta(): array
|
|
||||||
{
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return array<string, mixed>
|
|
||||||
*/
|
|
||||||
public static function default(): array
|
|
||||||
{
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function fake(Generator $faker): array
|
|
||||||
{
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @inheritdoc
|
|
||||||
*/
|
|
||||||
public function getRegistrationRules(Form $form): array
|
|
||||||
{
|
|
||||||
$rules = [$this->key => 'present|array'];
|
|
||||||
|
|
||||||
$c = $form->getFields()->forMembers()->noNamiType()->noNamiField()
|
|
||||||
->map(fn ($field) => $field->getRegistrationRules($form))
|
|
||||||
->toArray();
|
|
||||||
|
|
||||||
foreach ($c as $field) {
|
|
||||||
foreach ($field as $ruleKey => $rule) {
|
|
||||||
$rules[$this->key . '.*.' . $ruleKey] = $rule;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return [
|
|
||||||
$this->key . '.*.id' => ['required', 'numeric', 'exists:members,mitgliedsnr'],
|
|
||||||
...$rules,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @inheritdoc
|
|
||||||
*/
|
|
||||||
public function getRegistrationAttributes(Form $form): array
|
|
||||||
{
|
|
||||||
$rules = [];
|
|
||||||
$inputMembers = request($this->key);
|
|
||||||
|
|
||||||
if (!is_array($inputMembers)) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
$c = $form->getFields()->noNamiField()->forMembers();
|
|
||||||
|
|
||||||
foreach ($c as $field) {
|
|
||||||
foreach ($field->getRegistrationRules($form) as $ruleKey => $rule) {
|
|
||||||
foreach ($inputMembers as $memberIndex => $inputMember) {
|
|
||||||
|
|
||||||
$message = $field->name . ' für ein Mitglied';
|
|
||||||
$rules = array_merge(
|
|
||||||
$rules,
|
|
||||||
str($ruleKey)->contains('*')
|
|
||||||
? collect(request($this->key . '.' . $memberIndex . '.' . $field->key))
|
|
||||||
->mapWithKeys(fn ($value, $key) => [$this->key . '.' . $memberIndex . '.' . str($ruleKey)->replace('*', $key) => $message])
|
|
||||||
->toArray()
|
|
||||||
: [$this->key . '.' . $memberIndex . '.' . $ruleKey => $message]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($inputMembers as $memberIndex => $inputMember) {
|
|
||||||
$rules[$this->key . '.' . $memberIndex . '.id'] = 'Mitglied Nr ' . $inputMember['id'];
|
|
||||||
}
|
|
||||||
|
|
||||||
return [
|
|
||||||
$this->key => $this->name,
|
|
||||||
...$rules,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @inheritdoc
|
|
||||||
*/
|
|
||||||
public function getRegistrationMessages(Form $form): array
|
|
||||||
{
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
public function afterRegistration(Form $form, Participant $participant, array $input): void
|
|
||||||
{
|
|
||||||
foreach ($input[$this->key] as $memberData) {
|
|
||||||
$member = Member::firstWhere(['mitgliedsnr' => $memberData['id']]);
|
|
||||||
$data = [];
|
|
||||||
foreach (FieldCollection::fromRequest($form, $memberData) as $field) {
|
|
||||||
$data[$field->key] = $field->namiType === null
|
|
||||||
? $field->value
|
|
||||||
: $field->namiType->getMemberAttribute($member);
|
|
||||||
}
|
|
||||||
|
|
||||||
$data[$this->key] = [];
|
|
||||||
$form->participants()->create(['data' => $data, 'mitgliedsnr' => $memberData['id'], 'parent_id' => $participant->id]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getPresenter(): Presenter
|
|
||||||
{
|
|
||||||
return app(NamiPresenter::class);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,75 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Form\Fields;
|
|
||||||
|
|
||||||
use App\Form\Models\Form;
|
|
||||||
use App\Form\Models\Participant;
|
|
||||||
use App\Form\Presenters\Presenter;
|
|
||||||
use Faker\Generator;
|
|
||||||
use Illuminate\Validation\Rule;
|
|
||||||
|
|
||||||
class RadioField extends Field
|
|
||||||
{
|
|
||||||
public bool $required;
|
|
||||||
/** @var array<int, string> */
|
|
||||||
public array $options;
|
|
||||||
|
|
||||||
public static function name(): string
|
|
||||||
{
|
|
||||||
return 'Radio';
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function meta(): array
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
['key' => 'options', 'default' => [], 'rules' => ['options' => 'present|array', 'options.*' => 'required|string'], 'label' => 'Optionen'],
|
|
||||||
['key' => 'required', 'default' => false, 'rules' => ['required' => 'present|boolean'], 'label' => 'Erforderlich'],
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function default()
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function fake(Generator $faker): array
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
'options' => $faker->words(4),
|
|
||||||
'required' => $faker->boolean(),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @inheritdoc
|
|
||||||
*/
|
|
||||||
public function getRegistrationRules(Form $form): array
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
$this->key => $this->required ? ['required', 'string', Rule::in($this->options)] : ['nullable', 'string', Rule::in($this->options)],
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @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,68 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Form\Fields;
|
|
||||||
|
|
||||||
use App\Form\Models\Form;
|
|
||||||
use App\Form\Models\Participant;
|
|
||||||
use Faker\Generator;
|
|
||||||
|
|
||||||
class TextField extends Field
|
|
||||||
{
|
|
||||||
|
|
||||||
public bool $required;
|
|
||||||
|
|
||||||
public static function name(): string
|
|
||||||
{
|
|
||||||
return 'Text';
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function meta(): array
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
['key' => 'required', 'default' => false, 'rules' => ['required' => 'present|boolean'], 'label' => 'Erforderlich'],
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function default(): string
|
|
||||||
{
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function fake(Generator $faker): array
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
'required' => $faker->boolean(),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @inheritdoc
|
|
||||||
*/
|
|
||||||
public function getRegistrationRules(Form $form): array
|
|
||||||
{
|
|
||||||
return [$this->key => $this->required ? ['required', 'string'] : ['nullable', 'string']];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @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,70 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Form\Fields;
|
|
||||||
|
|
||||||
use App\Form\Models\Form;
|
|
||||||
use App\Form\Models\Participant;
|
|
||||||
use Faker\Generator;
|
|
||||||
|
|
||||||
class TextareaField extends Field
|
|
||||||
{
|
|
||||||
public bool $required;
|
|
||||||
public int $rows;
|
|
||||||
|
|
||||||
public static function name(): string
|
|
||||||
{
|
|
||||||
return 'Textarea';
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function meta(): array
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
['key' => 'rows', 'default' => 5, 'rules' => ['rows' => 'present|integer|gt:0'], 'label' => 'Zeilen'],
|
|
||||||
['key' => 'required', 'default' => false, 'rules' => ['required' => 'present|boolean'], 'label' => 'Erforderlich'],
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function default(): string
|
|
||||||
{
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function fake(Generator $faker): array
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
'rows' => $faker->numberBetween(5, 10),
|
|
||||||
'required' => $faker->boolean(),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @inheritdoc
|
|
||||||
*/
|
|
||||||
public function getRegistrationRules(Form $form): array
|
|
||||||
{
|
|
||||||
return [$this->key => $this->required ? ['required', 'string'] : ['nullable', 'string']];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @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,65 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Form\Mails;
|
|
||||||
|
|
||||||
use App\Form\Data\FormConfigData;
|
|
||||||
use App\Form\Models\Participant;
|
|
||||||
use Illuminate\Bus\Queueable;
|
|
||||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
|
||||||
use Illuminate\Mail\Mailable;
|
|
||||||
use Illuminate\Mail\Mailables\Content;
|
|
||||||
use Illuminate\Mail\Mailables\Envelope;
|
|
||||||
use Illuminate\Queue\SerializesModels;
|
|
||||||
|
|
||||||
class ConfirmRegistrationMail extends Mailable
|
|
||||||
{
|
|
||||||
use Queueable, SerializesModels;
|
|
||||||
|
|
||||||
public string $fullname;
|
|
||||||
public FormConfigData $config;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new message instance.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function __construct(public Participant $participant)
|
|
||||||
{
|
|
||||||
$this->fullname = $participant->getFields()->getFullname();
|
|
||||||
$this->config = $participant->getConfig();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the message envelope.
|
|
||||||
*
|
|
||||||
* @return \Illuminate\Mail\Mailables\Envelope
|
|
||||||
*/
|
|
||||||
public function envelope()
|
|
||||||
{
|
|
||||||
return new Envelope(
|
|
||||||
subject: 'Deine Anmeldung zu ' . $this->participant->form->name,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the message content definition.
|
|
||||||
*
|
|
||||||
* @return \Illuminate\Mail\Mailables\Content
|
|
||||||
*/
|
|
||||||
public function content()
|
|
||||||
{
|
|
||||||
return new Content(
|
|
||||||
markdown: 'mail.form.confirm-registration',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the attachments for the message.
|
|
||||||
*
|
|
||||||
* @return array<int, mixed>
|
|
||||||
*/
|
|
||||||
public function attachments()
|
|
||||||
{
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,144 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Form\Models;
|
|
||||||
|
|
||||||
use App\Form\Data\FieldCollection;
|
|
||||||
use App\Form\Data\FormConfigData;
|
|
||||||
use App\Form\Fields\Field;
|
|
||||||
use Cviebrock\EloquentSluggable\Sluggable;
|
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
|
||||||
use Illuminate\Database\Eloquent\Model;
|
|
||||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
|
||||||
use Illuminate\Support\Arr;
|
|
||||||
use Illuminate\Support\Collection;
|
|
||||||
use Laravel\Scout\Searchable;
|
|
||||||
use Spatie\Image\Manipulations;
|
|
||||||
use Spatie\MediaLibrary\HasMedia;
|
|
||||||
use Spatie\MediaLibrary\InteractsWithMedia;
|
|
||||||
use Spatie\MediaLibrary\MediaCollections\Models\Media;
|
|
||||||
use Zoomyboy\MedialibraryHelper\DefersUploads;
|
|
||||||
|
|
||||||
class Form extends Model implements HasMedia
|
|
||||||
{
|
|
||||||
use HasFactory;
|
|
||||||
use Sluggable;
|
|
||||||
use InteractsWithMedia;
|
|
||||||
use DefersUploads;
|
|
||||||
use Searchable;
|
|
||||||
|
|
||||||
public $guarded = [];
|
|
||||||
|
|
||||||
public $casts = [
|
|
||||||
'config' => FormConfigData::class,
|
|
||||||
'meta' => 'json',
|
|
||||||
'description' => 'json',
|
|
||||||
];
|
|
||||||
|
|
||||||
/** @var array<int, string> */
|
|
||||||
public $dates = ['from', 'to', 'registration_from', 'registration_until'];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return SluggableConfig
|
|
||||||
*/
|
|
||||||
public function sluggable(): array
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
'slug' => ['source' => ['name']],
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return HasMany<Participant>
|
|
||||||
*/
|
|
||||||
public function participants(): HasMany
|
|
||||||
{
|
|
||||||
return $this->hasMany(Participant::class);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public function registerMediaCollections(): void
|
|
||||||
{
|
|
||||||
$this->addMediaCollection('headerImage')
|
|
||||||
->singleFile()
|
|
||||||
->maxWidth(fn () => 500)
|
|
||||||
->forceFileName(fn (Form $model, string $name) => $model->slug)
|
|
||||||
->registerMediaConversions(function (Media $media) {
|
|
||||||
$this->addMediaConversion('square')->fit(Manipulations::FIT_CROP, 400, 400);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return array<string, mixed>
|
|
||||||
*/
|
|
||||||
public function getRegistrationRules(): array
|
|
||||||
{
|
|
||||||
return $this->getFields()->reduce(fn ($carry, $field) => [
|
|
||||||
...$carry,
|
|
||||||
...$field->getRegistrationRules($this),
|
|
||||||
], []);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return array<string, mixed>
|
|
||||||
*/
|
|
||||||
public function getRegistrationMessages(): array
|
|
||||||
{
|
|
||||||
return $this->getFields()->reduce(fn ($carry, $field) => [
|
|
||||||
...$carry,
|
|
||||||
...$field->getRegistrationMessages($this),
|
|
||||||
], []);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return array<string, mixed>
|
|
||||||
*/
|
|
||||||
public function getRegistrationAttributes(): array
|
|
||||||
{
|
|
||||||
return $this->getFields()->reduce(fn ($carry, $field) => [
|
|
||||||
...$carry,
|
|
||||||
...$field->getRegistrationAttributes($this),
|
|
||||||
], []);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getFields(): FieldCollection
|
|
||||||
{
|
|
||||||
return $this->config->fields();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// --------------------------------- Searching ---------------------------------
|
|
||||||
// *****************************************************************************
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the indexable data array for the model.
|
|
||||||
*
|
|
||||||
* @return array<string, mixed>
|
|
||||||
*/
|
|
||||||
public function toSearchableArray()
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
'from' => $this->from->timestamp,
|
|
||||||
'to' => $this->to->timestamp,
|
|
||||||
'name' => $this->name,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function booted(): void
|
|
||||||
{
|
|
||||||
static::saving(function (self $model) {
|
|
||||||
if (is_null($model->meta)) {
|
|
||||||
$model->setAttribute('meta', [
|
|
||||||
'active_columns' => $model->getFields()->count() ? $model->getFields()->take(4)->pluck('key')->toArray() : null,
|
|
||||||
'sorting' => $model->getFields()->count() ? [$model->getFields()->first()->key, 'asc'] : null,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (is_array(data_get($model->meta, 'active_columns'))) {
|
|
||||||
$model->setAttribute('meta', [
|
|
||||||
...$model->meta,
|
|
||||||
'active_columns' => array_values(array_intersect($model->getFields()->pluck('key')->toArray(), $model->meta['active_columns'])),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,21 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Form\Models;
|
|
||||||
|
|
||||||
use App\Form\Data\FormConfigData;
|
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
|
||||||
use Illuminate\Database\Eloquent\Model;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @property FormConfigData $config
|
|
||||||
*/
|
|
||||||
class Formtemplate extends Model
|
|
||||||
{
|
|
||||||
use HasFactory;
|
|
||||||
|
|
||||||
public $guarded = [];
|
|
||||||
|
|
||||||
public $casts = [
|
|
||||||
'config' => FormConfigData::class,
|
|
||||||
];
|
|
||||||
}
|
|
|
@ -1,75 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Form\Models;
|
|
||||||
|
|
||||||
use App\Form\Data\FieldCollection;
|
|
||||||
use App\Form\Data\FormConfigData;
|
|
||||||
use App\Form\Mails\ConfirmRegistrationMail;
|
|
||||||
use App\Form\Scopes\ParticipantFilterScope;
|
|
||||||
use Illuminate\Database\Eloquent\Builder;
|
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
|
||||||
use Illuminate\Database\Eloquent\Model;
|
|
||||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
|
||||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
|
||||||
use Illuminate\Support\Facades\Mail;
|
|
||||||
|
|
||||||
class Participant extends Model
|
|
||||||
{
|
|
||||||
use HasFactory;
|
|
||||||
|
|
||||||
public $guarded = [];
|
|
||||||
|
|
||||||
public $casts = [
|
|
||||||
'data' => 'json',
|
|
||||||
];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return BelongsTo<Form, self>
|
|
||||||
*/
|
|
||||||
public function form(): BelongsTo
|
|
||||||
{
|
|
||||||
return $this->belongsTo(Form::class);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return HasMany<self>
|
|
||||||
*/
|
|
||||||
public function children(): HasMany
|
|
||||||
{
|
|
||||||
return $this->hasMany(self::class, 'parent_id');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param Builder<self> $query
|
|
||||||
* @return Builder<self>
|
|
||||||
*/
|
|
||||||
public function scopeWithFilter(Builder $query, ParticipantFilterScope $filter): Builder
|
|
||||||
{
|
|
||||||
return $filter->apply($query);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getFields(): FieldCollection
|
|
||||||
{
|
|
||||||
return FieldCollection::fromRequest($this->form, $this->data);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getConfig(): FormConfigData
|
|
||||||
{
|
|
||||||
return tap($this->form->config, function ($config) {
|
|
||||||
$config->sections->each(function ($section) {
|
|
||||||
$section->fields->each(function ($field) {
|
|
||||||
$field->value = $this->getFields()->find($field)->value;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public function sendConfirmationMail(): void
|
|
||||||
{
|
|
||||||
if (!$this->getFields()->getMailRecipient()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Mail::to($this->getFields()->getMailRecipient())->queue(new ConfirmRegistrationMail($this));
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,32 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Form\Policies;
|
|
||||||
|
|
||||||
use App\User;
|
|
||||||
use Illuminate\Auth\Access\HandlesAuthorization;
|
|
||||||
use Spatie\MediaLibrary\HasMedia;
|
|
||||||
|
|
||||||
class FormPolicy
|
|
||||||
{
|
|
||||||
use HandlesAuthorization;
|
|
||||||
|
|
||||||
public function listMedia(User $user, HasMedia $model): bool
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function storeMedia(User $user, ?HasMedia $model, ?string $collection = null): bool
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function updateMedia(User $user, HasMedia $model, string $collection): bool
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function destroyMedia(User $user, HasMedia $model, string $collection): bool
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,16 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Form\Presenters;
|
|
||||||
|
|
||||||
use Carbon\Carbon;
|
|
||||||
|
|
||||||
class DatePresenter extends Presenter
|
|
||||||
{
|
|
||||||
|
|
||||||
public function present($value): string
|
|
||||||
{
|
|
||||||
return $value ? Carbon::parse($value)->format('d.m.Y') : '';
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,15 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Form\Presenters;
|
|
||||||
|
|
||||||
class DefaultPresenter extends Presenter
|
|
||||||
{
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param mixed $value
|
|
||||||
*/
|
|
||||||
public function present($value): string
|
|
||||||
{
|
|
||||||
return ((string) $value) ?: '';
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,17 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Form\Presenters;
|
|
||||||
|
|
||||||
class EnumPresenter extends Presenter
|
|
||||||
{
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param ?array<int, string> $value
|
|
||||||
*/
|
|
||||||
public function present($value): string
|
|
||||||
{
|
|
||||||
return is_array($value)
|
|
||||||
? implode(', ', $value)
|
|
||||||
: '';
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,22 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Form\Presenters;
|
|
||||||
|
|
||||||
use App\Group;
|
|
||||||
use Carbon\Carbon;
|
|
||||||
|
|
||||||
class GroupPresenter extends Presenter
|
|
||||||
{
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param ?int $value
|
|
||||||
*/
|
|
||||||
public function present($value): string
|
|
||||||
{
|
|
||||||
if (!$value) {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
return Group::find($value)?->display() ?: '';
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,15 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Form\Presenters;
|
|
||||||
|
|
||||||
class NamiPresenter extends Presenter
|
|
||||||
{
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param ?array<int, array{id: int}> $value
|
|
||||||
*/
|
|
||||||
public function present($value): string
|
|
||||||
{
|
|
||||||
return collect(array_column($value, 'id'))->implode(', ');
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,14 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Form\Presenters;
|
|
||||||
|
|
||||||
abstract class Presenter
|
|
||||||
{
|
|
||||||
/** @var mixed */
|
|
||||||
public $value;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param mixed $value
|
|
||||||
*/
|
|
||||||
abstract public function present($value): string;
|
|
||||||
}
|
|
|
@ -1,50 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Form\Resources;
|
|
||||||
|
|
||||||
use App\Form\Models\Form;
|
|
||||||
use App\Lib\HasMeta;
|
|
||||||
use App\Subactivity;
|
|
||||||
use Illuminate\Http\Resources\Json\JsonResource;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @mixin Form
|
|
||||||
*/
|
|
||||||
class FormApiResource extends JsonResource
|
|
||||||
{
|
|
||||||
use HasMeta;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Transform the resource into an array.
|
|
||||||
*
|
|
||||||
* @param \Illuminate\Http\Request $request
|
|
||||||
* @return array<string, mixed>
|
|
||||||
*/
|
|
||||||
public function toArray($request)
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
'id' => $this->id,
|
|
||||||
'name' => $this->name,
|
|
||||||
'from_human' => $this->from->format('d.m.Y'),
|
|
||||||
'to_human' => $this->to->format('d.m.Y'),
|
|
||||||
'excerpt' => $this->excerpt,
|
|
||||||
'description' => $this->description,
|
|
||||||
'config' => $this->config,
|
|
||||||
'slug' => $this->slug,
|
|
||||||
'dates' => $this->from->equalTo($this->to) ? $this->from->format('d.m.Y') : $this->from->format('d.m.Y') . ' - ' . $this->to->format('d.m.Y'),
|
|
||||||
'image' => $this->getMedia('headerImage')->first()->getFullUrl('square'),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return array<string, mixed>
|
|
||||||
*/
|
|
||||||
public static function meta(): array
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
'base_url' => url(''),
|
|
||||||
'agegroups' => Subactivity::remote()->where('is_age_group', true)->get()
|
|
||||||
->map(fn ($subactivity) => ['id' => $subactivity->nami_id, 'name' => $subactivity->name]),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,92 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Form\Resources;
|
|
||||||
|
|
||||||
use App\Form\Enums\NamiType;
|
|
||||||
use App\Form\Enums\SpecialType;
|
|
||||||
use App\Form\Fields\Field;
|
|
||||||
use App\Form\Scopes\FormFilterScope;
|
|
||||||
use App\Form\Models\Form;
|
|
||||||
use App\Form\Models\Formtemplate;
|
|
||||||
use App\Group;
|
|
||||||
use App\Lib\HasMeta;
|
|
||||||
use Illuminate\Http\Resources\Json\JsonResource;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @mixin Form
|
|
||||||
*/
|
|
||||||
class FormResource extends JsonResource
|
|
||||||
{
|
|
||||||
|
|
||||||
use HasMeta;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Transform the resource into an array.
|
|
||||||
*
|
|
||||||
* @param \Illuminate\Http\Request $request
|
|
||||||
* @return array<string, mixed>
|
|
||||||
*/
|
|
||||||
public function toArray($request)
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
'id' => $this->id,
|
|
||||||
'name' => $this->name,
|
|
||||||
'from_human' => $this->from->format('d.m.Y'),
|
|
||||||
'to_human' => $this->to->format('d.m.Y'),
|
|
||||||
'from' => $this->from->format('Y-m-d'),
|
|
||||||
'to' => $this->to->format('Y-m-d'),
|
|
||||||
'excerpt' => $this->excerpt,
|
|
||||||
'description' => $this->description,
|
|
||||||
'mail_top' => $this->mail_top,
|
|
||||||
'mail_bottom' => $this->mail_bottom,
|
|
||||||
'registration_from' => $this->registration_from?->format('Y-m-d H:i:s'),
|
|
||||||
'registration_until' => $this->registration_until?->format('Y-m-d H:i:s'),
|
|
||||||
'config' => $this->config,
|
|
||||||
'participants_count' => $this->participants_count,
|
|
||||||
'links' => [
|
|
||||||
'participant_index' => route('form.participant.index', ['form' => $this->getModel()]),
|
|
||||||
'update' => route('form.update', ['form' => $this->getModel()]),
|
|
||||||
'destroy' => route('form.destroy', ['form' => $this->getModel()]),
|
|
||||||
]
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return array<string, mixed>
|
|
||||||
*/
|
|
||||||
public static function meta(): array
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
'base_url' => url(''),
|
|
||||||
'groups' => Group::forSelect(),
|
|
||||||
'fields' => Field::asMeta(),
|
|
||||||
'filter' => FormFilterScope::fromRequest(request()->input('filter', '')),
|
|
||||||
'links' => [
|
|
||||||
'store' => route('form.store'),
|
|
||||||
'formtemplate_index' => route('formtemplate.index'),
|
|
||||||
],
|
|
||||||
'templates' => FormtemplateResource::collection(Formtemplate::get()),
|
|
||||||
'namiTypes' => NamiType::forSelect(),
|
|
||||||
'specialTypes' => SpecialType::forSelect(),
|
|
||||||
'default' => [
|
|
||||||
'description' => [],
|
|
||||||
'name' => '',
|
|
||||||
'excerpt' => '',
|
|
||||||
'from' => null,
|
|
||||||
'to' => null,
|
|
||||||
'registration_from' => null,
|
|
||||||
'registration_until' => null,
|
|
||||||
'mail_top' => null,
|
|
||||||
'mail_bottom' => null,
|
|
||||||
'config' => null,
|
|
||||||
'header_image' => null,
|
|
||||||
'id' => null,
|
|
||||||
],
|
|
||||||
'section_default' => [
|
|
||||||
'name' => '',
|
|
||||||
'intro' => '',
|
|
||||||
'fields' => [],
|
|
||||||
]
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,66 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Form\Resources;
|
|
||||||
|
|
||||||
use App\Form\Enums\NamiType;
|
|
||||||
use App\Form\Enums\SpecialType;
|
|
||||||
use App\Form\Fields\Field;
|
|
||||||
use App\Form\Models\Formtemplate;
|
|
||||||
use App\Group;
|
|
||||||
use App\Lib\HasMeta;
|
|
||||||
use Illuminate\Http\Resources\Json\JsonResource;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @mixin Formtemplate
|
|
||||||
*/
|
|
||||||
class FormtemplateResource extends JsonResource
|
|
||||||
{
|
|
||||||
|
|
||||||
use HasMeta;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Transform the resource into an array.
|
|
||||||
*
|
|
||||||
* @param \Illuminate\Http\Request $request
|
|
||||||
* @return array<string, mixed>
|
|
||||||
*/
|
|
||||||
public function toArray($request)
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
...parent::toArray($request),
|
|
||||||
'links' => [
|
|
||||||
'update' => route('formtemplate.update', ['formtemplate' => $this->getModel()]),
|
|
||||||
'destroy' => route('formtemplate.destroy', ['formtemplate' => $this->getModel()]),
|
|
||||||
]
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return array<string, mixed>
|
|
||||||
*/
|
|
||||||
public static function meta(): array
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
'base_url' => url(''),
|
|
||||||
'groups' => Group::forSelect(),
|
|
||||||
'fields' => Field::asMeta(),
|
|
||||||
'namiTypes' => NamiType::forSelect(),
|
|
||||||
'specialTypes' => SpecialType::forSelect(),
|
|
||||||
'links' => [
|
|
||||||
'store' => route('formtemplate.store'),
|
|
||||||
'form_index' => route('form.index'),
|
|
||||||
],
|
|
||||||
'default' => [
|
|
||||||
'name' => '',
|
|
||||||
'config' => [
|
|
||||||
'sections' => [],
|
|
||||||
]
|
|
||||||
],
|
|
||||||
'section_default' => [
|
|
||||||
'name' => '',
|
|
||||||
'intro' => '',
|
|
||||||
'fields' => [],
|
|
||||||
]
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,44 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Form\Resources;
|
|
||||||
|
|
||||||
use App\Form\Models\Form;
|
|
||||||
use App\Form\Models\Participant;
|
|
||||||
use Illuminate\Http\Resources\Json\JsonResource;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @mixin Participant
|
|
||||||
*/
|
|
||||||
class ParticipantResource extends JsonResource
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Transform the resource into an array.
|
|
||||||
*
|
|
||||||
* @param \Illuminate\Http\Request $request
|
|
||||||
* @return array<string, mixed>
|
|
||||||
*/
|
|
||||||
public function toArray($request)
|
|
||||||
{
|
|
||||||
return $this->getModel()->getFields()->present();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return array<string, mixed>
|
|
||||||
*/
|
|
||||||
public static function meta(Form $form): array
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
'form_meta' => $form->meta,
|
|
||||||
'links' => [
|
|
||||||
'update_form_meta' => route('form.update-meta', ['form' => $form]),
|
|
||||||
],
|
|
||||||
'columns' => $form->getFields()
|
|
||||||
->map(fn ($field) => [
|
|
||||||
'name' => $field->name,
|
|
||||||
'base_type' => class_basename($field),
|
|
||||||
'id' => $field->key,
|
|
||||||
'display_attribute' => $field->getDisplayAttribute(),
|
|
||||||
])
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,43 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Form\Scopes;
|
|
||||||
|
|
||||||
use Laravel\Scout\Builder;
|
|
||||||
use App\Form\Models\Form;
|
|
||||||
use App\Lib\ScoutFilter;
|
|
||||||
use Spatie\LaravelData\Attributes\MapInputName;
|
|
||||||
use Spatie\LaravelData\Attributes\MapOutputName;
|
|
||||||
use Spatie\LaravelData\Mappers\SnakeCaseMapper;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @extends ScoutFilter<Form>
|
|
||||||
*/
|
|
||||||
#[MapInputName(SnakeCaseMapper::class)]
|
|
||||||
#[MapOutputName(SnakeCaseMapper::class)]
|
|
||||||
class FormFilterScope extends ScoutFilter
|
|
||||||
{
|
|
||||||
public function __construct(
|
|
||||||
public ?string $search = '',
|
|
||||||
public bool $past = false,
|
|
||||||
) {
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getQuery(): Builder
|
|
||||||
{
|
|
||||||
$this->search = $this->search ?: '';
|
|
||||||
|
|
||||||
return Form::search($this->search, function ($engine, string $query, array $options) {
|
|
||||||
$options['sort'] = ['from:asc'];
|
|
||||||
|
|
||||||
$filters = collect([]);
|
|
||||||
|
|
||||||
if ($this->past === false) {
|
|
||||||
$filters->push('to > ' . now()->timestamp);
|
|
||||||
}
|
|
||||||
|
|
||||||
$options['filter'] = $filters->implode(' AND ');
|
|
||||||
|
|
||||||
return $engine->search($query, $options);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,39 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Form\Scopes;
|
|
||||||
|
|
||||||
use App\Form\Models\Participant;
|
|
||||||
use App\Lib\Filter;
|
|
||||||
use Illuminate\Database\Eloquent\Builder;
|
|
||||||
use Spatie\LaravelData\Attributes\MapInputName;
|
|
||||||
use Spatie\LaravelData\Attributes\MapOutputName;
|
|
||||||
use Spatie\LaravelData\Mappers\SnakeCaseMapper;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @extends Filter<Participant>
|
|
||||||
*/
|
|
||||||
#[MapInputName(SnakeCaseMapper::class)]
|
|
||||||
#[MapOutputName(SnakeCaseMapper::class)]
|
|
||||||
class ParticipantFilterScope extends Filter
|
|
||||||
{
|
|
||||||
public function __construct(
|
|
||||||
public ?int $parent = null,
|
|
||||||
) {
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @inheritdoc
|
|
||||||
*/
|
|
||||||
public function apply(Builder $query): Builder
|
|
||||||
{
|
|
||||||
if ($this->parent === -1) {
|
|
||||||
$query = $query->whereNull('parent_id');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!is_null($this->parent) && $this->parent > 0) {
|
|
||||||
$query = $query->where('parent_id', $this->parent);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $query;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,25 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Form\Transformers;
|
|
||||||
|
|
||||||
use App\Form\Fields\Field;
|
|
||||||
use Illuminate\Support\Collection;
|
|
||||||
use Spatie\LaravelData\Support\DataProperty;
|
|
||||||
use Spatie\LaravelData\Transformers\Transformer;
|
|
||||||
|
|
||||||
class CollectionTransformer implements Transformer
|
|
||||||
{
|
|
||||||
|
|
||||||
public function __construct(public string $target)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param Collection<int, Field> $value
|
|
||||||
* @return array<string, mixed>
|
|
||||||
*/
|
|
||||||
public function transform(DataProperty $property, mixed $value): mixed
|
|
||||||
{
|
|
||||||
return $value->toArray();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,24 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Form\Transformers;
|
|
||||||
|
|
||||||
use App\Form\Fields\Field;
|
|
||||||
use Illuminate\Support\Collection;
|
|
||||||
use Spatie\LaravelData\Support\DataProperty;
|
|
||||||
use Spatie\LaravelData\Transformers\Transformer;
|
|
||||||
|
|
||||||
class FieldCollectionTransformer implements Transformer
|
|
||||||
{
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param Collection<int, Field> $value
|
|
||||||
* @return array<string, mixed>
|
|
||||||
*/
|
|
||||||
public function transform(DataProperty $property, mixed $value): mixed
|
|
||||||
{
|
|
||||||
return $value->map(fn ($field) => [
|
|
||||||
...$field->toArray(),
|
|
||||||
'type' => class_basename($field),
|
|
||||||
])->toArray();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -45,25 +45,4 @@ class Group extends Model
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @return array<int, array{id: int, name: string}>
|
|
||||||
*/
|
|
||||||
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->display())]])
|
|
||||||
->concat($group->children_count > 0 ? self::forSelect($group, $prefix . '-- ') : []),
|
|
||||||
collect([])
|
|
||||||
)
|
|
||||||
->toArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function display(): string
|
|
||||||
{
|
|
||||||
return $this->inner_name ?: $this->name;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,9 +18,9 @@ class NamiSearchAction
|
||||||
*
|
*
|
||||||
* @return LengthAwarePaginator<MemberEntry>
|
* @return LengthAwarePaginator<MemberEntry>
|
||||||
*/
|
*/
|
||||||
public function handle(Api $api, int $page, array $params, int $perPage = 10): LengthAwarePaginator
|
public function handle(Api $api, int $page, array $params): LengthAwarePaginator
|
||||||
{
|
{
|
||||||
return $api->pageSearch($params, $page, $perPage);
|
return $api->pageSearch($params, $page, 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -2,8 +2,8 @@
|
||||||
|
|
||||||
namespace App\Lib;
|
namespace App\Lib;
|
||||||
|
|
||||||
use Illuminate\Database\Eloquent\Builder;
|
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
use Laravel\Scout\Builder;
|
||||||
use Spatie\LaravelData\Data;
|
use Spatie\LaravelData\Data;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -14,10 +14,10 @@ abstract class Filter extends Data
|
||||||
{
|
{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param Builder<T> $query
|
* @return Builder
|
||||||
* @return Builder<T>
|
|
||||||
*/
|
*/
|
||||||
abstract public function apply(Builder $query): Builder;
|
abstract public function getQuery(): Builder;
|
||||||
|
protected Builder $query;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param array<string, mixed>|string|null $request
|
* @param array<string, mixed>|string|null $request
|
||||||
|
|
|
@ -1,41 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Lib;
|
|
||||||
|
|
||||||
use Illuminate\Database\Eloquent\Model;
|
|
||||||
use Laravel\Scout\Builder;
|
|
||||||
use Spatie\LaravelData\Data;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @template T of Model
|
|
||||||
* @property Builder $query
|
|
||||||
*/
|
|
||||||
abstract class ScoutFilter extends Data
|
|
||||||
{
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return Builder
|
|
||||||
*/
|
|
||||||
abstract public function getQuery(): Builder;
|
|
||||||
protected Builder $query;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param array<string, mixed>|string|null $request
|
|
||||||
*/
|
|
||||||
public static function fromRequest(array|string|null $request = null): static
|
|
||||||
{
|
|
||||||
$payload = is_string($request)
|
|
||||||
? json_decode(rawurldecode(base64_decode($request)), true)
|
|
||||||
: $request;
|
|
||||||
|
|
||||||
return static::fromPost($payload);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param array<string, mixed> $post
|
|
||||||
*/
|
|
||||||
public static function fromPost(?array $post = null): static
|
|
||||||
{
|
|
||||||
return static::withoutMagicalCreationFrom($post ?: []);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -5,8 +5,8 @@ namespace App\Member;
|
||||||
use App\Activity;
|
use App\Activity;
|
||||||
use App\Group;
|
use App\Group;
|
||||||
use App\Invoice\BillKind;
|
use App\Invoice\BillKind;
|
||||||
|
use App\Lib\Filter;
|
||||||
use App\Subactivity;
|
use App\Subactivity;
|
||||||
use App\Lib\ScoutFilter;
|
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
use Laravel\Scout\Builder;
|
use Laravel\Scout\Builder;
|
||||||
use Spatie\LaravelData\Attributes\MapInputName;
|
use Spatie\LaravelData\Attributes\MapInputName;
|
||||||
|
@ -14,11 +14,11 @@ use Spatie\LaravelData\Attributes\MapOutputName;
|
||||||
use Spatie\LaravelData\Mappers\SnakeCaseMapper;
|
use Spatie\LaravelData\Mappers\SnakeCaseMapper;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @extends ScoutFilter<Member>
|
* @extends Filter<Member>
|
||||||
*/
|
*/
|
||||||
#[MapInputName(SnakeCaseMapper::class)]
|
#[MapInputName(SnakeCaseMapper::class)]
|
||||||
#[MapOutputName(SnakeCaseMapper::class)]
|
#[MapOutputName(SnakeCaseMapper::class)]
|
||||||
class FilterScope extends ScoutFilter
|
class FilterScope extends Filter
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @param array<int, int> $activityIds
|
* @param array<int, int> $activityIds
|
||||||
|
|
|
@ -93,7 +93,7 @@ class Member extends Model implements Geolocatable
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return SluggableConfig
|
* @return array<string, array{source: array<int, string>}>
|
||||||
*/
|
*/
|
||||||
public function sluggable(): array
|
public function sluggable(): array
|
||||||
{
|
{
|
||||||
|
|
|
@ -7,14 +7,12 @@ enum Module: string
|
||||||
|
|
||||||
case BILL = 'bill';
|
case BILL = 'bill';
|
||||||
case COURSE = 'course';
|
case COURSE = 'course';
|
||||||
case EVENT = 'event';
|
|
||||||
|
|
||||||
public function title(): string
|
public function title(): string
|
||||||
{
|
{
|
||||||
return match ($this) {
|
return match ($this) {
|
||||||
static::BILL => 'Zahlungs-Management',
|
static::BILL => 'Zahlungs-Management',
|
||||||
static::COURSE => 'Ausbildung',
|
static::COURSE => 'Ausbildung',
|
||||||
static::EVENT => 'Veranstaltungen',
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
|
|
||||||
namespace App\Providers;
|
namespace App\Providers;
|
||||||
|
|
||||||
use App\Form\Models\Form;
|
|
||||||
use App\Mailgateway\Types\LocalType;
|
use App\Mailgateway\Types\LocalType;
|
||||||
use App\Mailgateway\Types\MailmanType;
|
use App\Mailgateway\Types\MailmanType;
|
||||||
use Illuminate\Http\RedirectResponse;
|
use Illuminate\Http\RedirectResponse;
|
||||||
|
@ -34,8 +33,6 @@ class AppServiceProvider extends ServiceProvider
|
||||||
LocalType::class,
|
LocalType::class,
|
||||||
MailmanType::class,
|
MailmanType::class,
|
||||||
]));
|
]));
|
||||||
|
|
||||||
app()->extend('media-library-helpers', fn ($p) => $p->put('form', Form::class));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -2,8 +2,6 @@
|
||||||
|
|
||||||
namespace App\Providers;
|
namespace App\Providers;
|
||||||
|
|
||||||
use App\Form\Models\Form;
|
|
||||||
use App\Form\Policies\FormPolicy;
|
|
||||||
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
|
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
|
||||||
use Laravel\Passport\Passport;
|
use Laravel\Passport\Passport;
|
||||||
|
|
||||||
|
@ -12,10 +10,10 @@ class AuthServiceProvider extends ServiceProvider
|
||||||
/**
|
/**
|
||||||
* The policy mappings for the application.
|
* The policy mappings for the application.
|
||||||
*
|
*
|
||||||
* @var array<class-string, class-string>
|
* @var array
|
||||||
*/
|
*/
|
||||||
protected $policies = [
|
protected $policies = [
|
||||||
Form::class => FormPolicy::class,
|
// 'App\Model' => 'App\Policies\ModelPolicy',
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -57,8 +57,5 @@ class RouteServiceProvider extends ServiceProvider
|
||||||
Route::middleware('api')
|
Route::middleware('api')
|
||||||
->prefix('api')
|
->prefix('api')
|
||||||
->group(base_path('routes/api.php'));
|
->group(base_path('routes/api.php'));
|
||||||
Route::middleware('api')
|
|
||||||
->prefix('remote')
|
|
||||||
->group(base_path('routes/remote.php'));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,35 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Remote\Actions;
|
|
||||||
|
|
||||||
use Illuminate\Http\JsonResponse;
|
|
||||||
use Illuminate\Support\Facades\Crypt;
|
|
||||||
use Lorisleiva\Actions\ActionRequest;
|
|
||||||
use Lorisleiva\Actions\Concerns\AsAction;
|
|
||||||
use Zoomyboy\LaravelNami\Nami;
|
|
||||||
|
|
||||||
class LoginAction
|
|
||||||
{
|
|
||||||
use AsAction;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return array<string, mixed>
|
|
||||||
*/
|
|
||||||
public function rules(): array
|
|
||||||
{
|
|
||||||
|
|
||||||
return [
|
|
||||||
'mglnr' => 'required|numeric',
|
|
||||||
'password' => 'required|string',
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
public function handle(ActionRequest $request): JsonResponse
|
|
||||||
{
|
|
||||||
Nami::freshLogin($request->mglnr, $request->password);
|
|
||||||
|
|
||||||
return response()->json([
|
|
||||||
'access_token' => Crypt::encryptString(json_encode(['mglnr' => $request->mglnr, 'password' => $request->password])),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,30 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Remote\Actions;
|
|
||||||
|
|
||||||
use App\Initialize\Actions\NamiSearchAction;
|
|
||||||
use Illuminate\Pagination\LengthAwarePaginator;
|
|
||||||
use Illuminate\Support\Facades\Crypt;
|
|
||||||
use Lorisleiva\Actions\ActionRequest;
|
|
||||||
use Lorisleiva\Actions\Concerns\AsAction;
|
|
||||||
use Zoomyboy\LaravelNami\Nami;
|
|
||||||
|
|
||||||
class SearchAction
|
|
||||||
{
|
|
||||||
use AsAction;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return LengthAwarePaginator<array<string, mixed>>
|
|
||||||
*/
|
|
||||||
public function handle(ActionRequest $request): LengthAwarePaginator
|
|
||||||
{
|
|
||||||
$token = str($request->header('Authorization'))->replace('Bearer ', '')->toString();
|
|
||||||
$credentials = json_decode(Crypt::decryptString($token));
|
|
||||||
|
|
||||||
$api = Nami::login($credentials->mglnr, $credentials->password);
|
|
||||||
|
|
||||||
$results = NamiSearchAction::run($api, $request->input('page', 1), $request->except('page'), 50);
|
|
||||||
$results->transform(fn ($member) => ['id' => $member->memberId, 'name' => $member->firstname . ' ' . $member->lastname]);
|
|
||||||
return $results;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -20,13 +20,6 @@
|
||||||
"options": {
|
"options": {
|
||||||
"symlink": true
|
"symlink": true
|
||||||
}
|
}
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "path",
|
|
||||||
"url": "./packages/medialibrary-helper",
|
|
||||||
"options": {
|
|
||||||
"symlink": true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
@ -60,12 +53,12 @@
|
||||||
"zoomyboy/laravel-nami": "dev-master",
|
"zoomyboy/laravel-nami": "dev-master",
|
||||||
"zoomyboy/osm": "1.0.3",
|
"zoomyboy/osm": "1.0.3",
|
||||||
"zoomyboy/phone": "^1.0",
|
"zoomyboy/phone": "^1.0",
|
||||||
"zoomyboy/tex": "dev-main as 1.0",
|
"zoomyboy/tex": "dev-main as 1.0"
|
||||||
"zoomyboy/medialibrary-helper": "dev-master as 1.0"
|
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"fakerphp/faker": "^1.9.1",
|
"fakerphp/faker": "^1.9.1",
|
||||||
"laravel/envoy": "^2.8",
|
"laravel/envoy": "^2.8",
|
||||||
|
"laravel/sail": "^1.0.1",
|
||||||
"mockery/mockery": "^1.4.4",
|
"mockery/mockery": "^1.4.4",
|
||||||
"larastan/larastan": "^2.0",
|
"larastan/larastan": "^2.0",
|
||||||
"orchestra/testbench": "^7.0",
|
"orchestra/testbench": "^7.0",
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,267 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
return [
|
|
||||||
|
|
||||||
/*
|
|
||||||
* The disk on which to store added files and derived images by default. Choose
|
|
||||||
* one or more of the disks you've configured in config/filesystems.php.
|
|
||||||
*/
|
|
||||||
'disk_name' => env('MEDIA_DISK', 'public'),
|
|
||||||
|
|
||||||
/*
|
|
||||||
* The maximum file size of an item in bytes.
|
|
||||||
* Adding a larger file will result in an exception.
|
|
||||||
*/
|
|
||||||
'max_file_size' => 1024 * 1024 * 10, // 10MB
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This queue connection will be used to generate derived and responsive images.
|
|
||||||
* Leave empty to use the default queue connection.
|
|
||||||
*/
|
|
||||||
'queue_connection_name' => env('QUEUE_CONNECTION', 'sync'),
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This queue will be used to generate derived and responsive images.
|
|
||||||
* Leave empty to use the default queue.
|
|
||||||
*/
|
|
||||||
'queue_name' => '',
|
|
||||||
|
|
||||||
/*
|
|
||||||
* By default all conversions will be performed on a queue.
|
|
||||||
*/
|
|
||||||
'queue_conversions_by_default' => env('QUEUE_CONVERSIONS_BY_DEFAULT', true),
|
|
||||||
|
|
||||||
/*
|
|
||||||
* The fully qualified class name of the media model.
|
|
||||||
*/
|
|
||||||
'media_model' => Spatie\MediaLibrary\MediaCollections\Models\Media::class,
|
|
||||||
|
|
||||||
/*
|
|
||||||
* When enabled, media collections will be serialised using the default
|
|
||||||
* laravel model serialization behaviour.
|
|
||||||
*
|
|
||||||
* Keep this option disabled if using Media Library Pro components (https://medialibrary.pro)
|
|
||||||
*/
|
|
||||||
'use_default_collection_serialization' => false,
|
|
||||||
|
|
||||||
/*
|
|
||||||
* The fully qualified class name of the model used for temporary uploads.
|
|
||||||
*
|
|
||||||
* This model is only used in Media Library Pro (https://medialibrary.pro)
|
|
||||||
*/
|
|
||||||
'temporary_upload_model' => Spatie\MediaLibraryPro\Models\TemporaryUpload::class,
|
|
||||||
|
|
||||||
/*
|
|
||||||
* When enabled, Media Library Pro will only process temporary uploads that were uploaded
|
|
||||||
* in the same session. You can opt to disable this for stateless usage of
|
|
||||||
* the pro components.
|
|
||||||
*/
|
|
||||||
'enable_temporary_uploads_session_affinity' => true,
|
|
||||||
|
|
||||||
/*
|
|
||||||
* When enabled, Media Library pro will generate thumbnails for uploaded file.
|
|
||||||
*/
|
|
||||||
'generate_thumbnails_for_temporary_uploads' => true,
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This is the class that is responsible for naming generated files.
|
|
||||||
*/
|
|
||||||
'file_namer' => Spatie\MediaLibrary\Support\FileNamer\DefaultFileNamer::class,
|
|
||||||
|
|
||||||
/*
|
|
||||||
* The class that contains the strategy for determining a media file's path.
|
|
||||||
*/
|
|
||||||
'path_generator' => Spatie\MediaLibrary\Support\PathGenerator\DefaultPathGenerator::class,
|
|
||||||
|
|
||||||
/*
|
|
||||||
* The class that contains the strategy for determining how to remove files.
|
|
||||||
*/
|
|
||||||
'file_remover_class' => Spatie\MediaLibrary\Support\FileRemover\DefaultFileRemover::class,
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Here you can specify which path generator should be used for the given class.
|
|
||||||
*/
|
|
||||||
'custom_path_generators' => [
|
|
||||||
// Model::class => PathGenerator::class
|
|
||||||
// or
|
|
||||||
// 'model_morph_alias' => PathGenerator::class
|
|
||||||
],
|
|
||||||
|
|
||||||
/*
|
|
||||||
* When urls to files get generated, this class will be called. Use the default
|
|
||||||
* if your files are stored locally above the site root or on s3.
|
|
||||||
*/
|
|
||||||
'url_generator' => Spatie\MediaLibrary\Support\UrlGenerator\DefaultUrlGenerator::class,
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Moves media on updating to keep path consistent. Enable it only with a custom
|
|
||||||
* PathGenerator that uses, for example, the media UUID.
|
|
||||||
*/
|
|
||||||
'moves_media_on_update' => false,
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Whether to activate versioning when urls to files get generated.
|
|
||||||
* When activated, this attaches a ?v=xx query string to the URL.
|
|
||||||
*/
|
|
||||||
'version_urls' => false,
|
|
||||||
|
|
||||||
/*
|
|
||||||
* The media library will try to optimize all converted images by removing
|
|
||||||
* metadata and applying a little bit of compression. These are
|
|
||||||
* the optimizers that will be used by default.
|
|
||||||
*/
|
|
||||||
'image_optimizers' => [
|
|
||||||
Spatie\ImageOptimizer\Optimizers\Jpegoptim::class => [
|
|
||||||
'-m85', // set maximum quality to 85%
|
|
||||||
'--force', // ensure that progressive generation is always done also if a little bigger
|
|
||||||
'--strip-all', // this strips out all text information such as comments and EXIF data
|
|
||||||
'--all-progressive', // this will make sure the resulting image is a progressive one
|
|
||||||
],
|
|
||||||
Spatie\ImageOptimizer\Optimizers\Pngquant::class => [
|
|
||||||
'--force', // required parameter for this package
|
|
||||||
],
|
|
||||||
Spatie\ImageOptimizer\Optimizers\Optipng::class => [
|
|
||||||
'-i0', // this will result in a non-interlaced, progressive scanned image
|
|
||||||
'-o2', // this set the optimization level to two (multiple IDAT compression trials)
|
|
||||||
'-quiet', // required parameter for this package
|
|
||||||
],
|
|
||||||
Spatie\ImageOptimizer\Optimizers\Svgo::class => [
|
|
||||||
'--disable=cleanupIDs', // disabling because it is known to cause troubles
|
|
||||||
],
|
|
||||||
Spatie\ImageOptimizer\Optimizers\Gifsicle::class => [
|
|
||||||
'-b', // required parameter for this package
|
|
||||||
'-O3', // this produces the slowest but best results
|
|
||||||
],
|
|
||||||
Spatie\ImageOptimizer\Optimizers\Cwebp::class => [
|
|
||||||
'-m 6', // for the slowest compression method in order to get the best compression.
|
|
||||||
'-pass 10', // for maximizing the amount of analysis pass.
|
|
||||||
'-mt', // multithreading for some speed improvements.
|
|
||||||
'-q 90', //quality factor that brings the least noticeable changes.
|
|
||||||
],
|
|
||||||
Spatie\ImageOptimizer\Optimizers\Avifenc::class => [
|
|
||||||
'-a cq-level=23', // constant quality level, lower values mean better quality and greater file size (0-63).
|
|
||||||
'-j all', // number of jobs (worker threads, "all" uses all available cores).
|
|
||||||
'--min 0', // min quantizer for color (0-63).
|
|
||||||
'--max 63', // max quantizer for color (0-63).
|
|
||||||
'--minalpha 0', // min quantizer for alpha (0-63).
|
|
||||||
'--maxalpha 63', // max quantizer for alpha (0-63).
|
|
||||||
'-a end-usage=q', // rate control mode set to Constant Quality mode.
|
|
||||||
'-a tune=ssim', // SSIM as tune the encoder for distortion metric.
|
|
||||||
],
|
|
||||||
],
|
|
||||||
|
|
||||||
/*
|
|
||||||
* These generators will be used to create an image of media files.
|
|
||||||
*/
|
|
||||||
'image_generators' => [
|
|
||||||
Spatie\MediaLibrary\Conversions\ImageGenerators\Image::class,
|
|
||||||
Spatie\MediaLibrary\Conversions\ImageGenerators\Webp::class,
|
|
||||||
Spatie\MediaLibrary\Conversions\ImageGenerators\Avif::class,
|
|
||||||
Spatie\MediaLibrary\Conversions\ImageGenerators\Pdf::class,
|
|
||||||
Spatie\MediaLibrary\Conversions\ImageGenerators\Svg::class,
|
|
||||||
Spatie\MediaLibrary\Conversions\ImageGenerators\Video::class,
|
|
||||||
],
|
|
||||||
|
|
||||||
/*
|
|
||||||
* The path where to store temporary files while performing image conversions.
|
|
||||||
* If set to null, storage_path('media-library/temp') will be used.
|
|
||||||
*/
|
|
||||||
'temporary_directory_path' => null,
|
|
||||||
|
|
||||||
/*
|
|
||||||
* The engine that should perform the image conversions.
|
|
||||||
* Should be either `gd` or `imagick`.
|
|
||||||
*/
|
|
||||||
'image_driver' => env('IMAGE_DRIVER', 'gd'),
|
|
||||||
|
|
||||||
/*
|
|
||||||
* FFMPEG & FFProbe binaries paths, only used if you try to generate video
|
|
||||||
* thumbnails and have installed the php-ffmpeg/php-ffmpeg composer
|
|
||||||
* dependency.
|
|
||||||
*/
|
|
||||||
'ffmpeg_path' => env('FFMPEG_PATH', '/usr/bin/ffmpeg'),
|
|
||||||
'ffprobe_path' => env('FFPROBE_PATH', '/usr/bin/ffprobe'),
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Here you can override the class names of the jobs used by this package. Make sure
|
|
||||||
* your custom jobs extend the ones provided by the package.
|
|
||||||
*/
|
|
||||||
'jobs' => [
|
|
||||||
'perform_conversions' => Spatie\MediaLibrary\Conversions\Jobs\PerformConversionsJob::class,
|
|
||||||
'generate_responsive_images' => Spatie\MediaLibrary\ResponsiveImages\Jobs\GenerateResponsiveImagesJob::class,
|
|
||||||
],
|
|
||||||
|
|
||||||
/*
|
|
||||||
* When using the addMediaFromUrl method you may want to replace the default downloader.
|
|
||||||
* This is particularly useful when the url of the image is behind a firewall and
|
|
||||||
* need to add additional flags, possibly using curl.
|
|
||||||
*/
|
|
||||||
'media_downloader' => Spatie\MediaLibrary\Downloaders\DefaultDownloader::class,
|
|
||||||
|
|
||||||
'remote' => [
|
|
||||||
/*
|
|
||||||
* Any extra headers that should be included when uploading media to
|
|
||||||
* a remote disk. Even though supported headers may vary between
|
|
||||||
* different drivers, a sensible default has been provided.
|
|
||||||
*
|
|
||||||
* Supported by S3: CacheControl, Expires, StorageClass,
|
|
||||||
* ServerSideEncryption, Metadata, ACL, ContentEncoding
|
|
||||||
*/
|
|
||||||
'extra_headers' => [
|
|
||||||
'CacheControl' => 'max-age=604800',
|
|
||||||
],
|
|
||||||
],
|
|
||||||
|
|
||||||
'responsive_images' => [
|
|
||||||
/*
|
|
||||||
* This class is responsible for calculating the target widths of the responsive
|
|
||||||
* images. By default we optimize for filesize and create variations that each are 30%
|
|
||||||
* smaller than the previous one. More info in the documentation.
|
|
||||||
*
|
|
||||||
* https://docs.spatie.be/laravel-medialibrary/v9/advanced-usage/generating-responsive-images
|
|
||||||
*/
|
|
||||||
'width_calculator' => Spatie\MediaLibrary\ResponsiveImages\WidthCalculator\FileSizeOptimizedWidthCalculator::class,
|
|
||||||
|
|
||||||
/*
|
|
||||||
* By default rendering media to a responsive image will add some javascript and a tiny placeholder.
|
|
||||||
* This ensures that the browser can already determine the correct layout.
|
|
||||||
*/
|
|
||||||
'use_tiny_placeholders' => true,
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This class will generate the tiny placeholder used for progressive image loading. By default
|
|
||||||
* the media library will use a tiny blurred jpg image.
|
|
||||||
*/
|
|
||||||
'tiny_placeholder_generator' => Spatie\MediaLibrary\ResponsiveImages\TinyPlaceholderGenerator\Blurred::class,
|
|
||||||
],
|
|
||||||
|
|
||||||
/*
|
|
||||||
* When enabling this option, a route will be registered that will enable
|
|
||||||
* the Media Library Pro Vue and React components to move uploaded files
|
|
||||||
* in a S3 bucket to their right place.
|
|
||||||
*/
|
|
||||||
'enable_vapor_uploads' => env('ENABLE_MEDIA_LIBRARY_VAPOR_UPLOADS', false),
|
|
||||||
|
|
||||||
/*
|
|
||||||
* When converting Media instances to response the media library will add
|
|
||||||
* a `loading` attribute to the `img` tag. Here you can set the default
|
|
||||||
* value of that attribute.
|
|
||||||
*
|
|
||||||
* Possible values: 'lazy', 'eager', 'auto' or null if you don't want to set any loading instruction.
|
|
||||||
*
|
|
||||||
* More info: https://css-tricks.com/native-lazy-loading/
|
|
||||||
*/
|
|
||||||
'default_loading_attribute_value' => null,
|
|
||||||
|
|
||||||
/*
|
|
||||||
* You can specify a prefix for that is used for storing all media.
|
|
||||||
* If you set this to `/my-subdir`, all your media will be stored in a `/my-subdir` directory.
|
|
||||||
*/
|
|
||||||
'prefix' => env('MEDIA_PREFIX', ''),
|
|
||||||
|
|
||||||
/*
|
|
||||||
* The temp disk for deferred media data.
|
|
||||||
* Only used by the medialibrary-helper package.
|
|
||||||
*/
|
|
||||||
'temp_disk' => 'temp',
|
|
||||||
];
|
|
|
@ -1,6 +1,5 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
use App\Form\Models\Form;
|
|
||||||
use App\Member\Member;
|
use App\Member\Member;
|
||||||
|
|
||||||
return [
|
return [
|
||||||
|
@ -141,12 +140,6 @@ return [
|
||||||
'searchableAttributes' => ['fullname', 'address'],
|
'searchableAttributes' => ['fullname', 'address'],
|
||||||
'sortableAttributes' => ['lastname', 'firstname'],
|
'sortableAttributes' => ['lastname', 'firstname'],
|
||||||
'displayedAttributes' => ['age_group_icon', 'group_name', 'links', 'is_leader', 'lastname', 'firstname', 'fullname', 'address', 'ausstand', 'birthday', 'id', 'memberships', 'bill_kind', 'group_id'],
|
'displayedAttributes' => ['age_group_icon', 'group_name', 'links', 'is_leader', 'lastname', 'firstname', 'fullname', 'address', 'ausstand', 'birthday', 'id', 'memberships', 'bill_kind', 'group_id'],
|
||||||
],
|
|
||||||
Form::class => [
|
|
||||||
'filterableAttributes' => ['to'],
|
|
||||||
'searchableAttributes' => ['name'],
|
|
||||||
'sortableAttributes' => ['from',],
|
|
||||||
'displayedAttributes' => ['from', 'name', 'id', 'to'],
|
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
|
|
|
@ -1,78 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace Database\Factories\Form\Models;
|
|
||||||
|
|
||||||
use App\Form\Models\Form;
|
|
||||||
use Database\Factories\Traits\FakesMedia;
|
|
||||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
|
||||||
use Tests\Feature\Form\FormtemplateFieldRequest;
|
|
||||||
use Tests\Feature\Form\FormtemplateSectionRequest;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @extends Factory<Form>
|
|
||||||
* @method self name(string $name)
|
|
||||||
* @method self from(string $from)
|
|
||||||
* @method self to(string $to)
|
|
||||||
* @method self mailTop(string $content)
|
|
||||||
* @method self mailBottom(string $content)
|
|
||||||
* @method self excerpt(string $excerpt)
|
|
||||||
* @method self description(string $description)
|
|
||||||
* @method self registrationFrom(string|null $date)
|
|
||||||
* @method self registrationUntil(string|null $date)
|
|
||||||
*/
|
|
||||||
class FormFactory extends Factory
|
|
||||||
{
|
|
||||||
use FakesMedia;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The name of the factory's corresponding model.
|
|
||||||
*
|
|
||||||
* @var class-string<Form>
|
|
||||||
*/
|
|
||||||
protected $model = Form::class;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Define the model's default state.
|
|
||||||
*
|
|
||||||
* @return array<string, mixed>
|
|
||||||
*/
|
|
||||||
public function definition()
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
'name' => $this->faker->words(4, true),
|
|
||||||
'description' => $this->faker->text(),
|
|
||||||
'excerpt' => $this->faker->words(10, true),
|
|
||||||
'config' => ['sections' => []],
|
|
||||||
'from' => $this->faker->dateTimeBetween('+1 week', '+4 weeks')->format('Y-m-d H:i:s'),
|
|
||||||
'to' => $this->faker->dateTimeBetween('+1 week', '+4 weeks')->format('Y-m-d H:i:s'),
|
|
||||||
'registration_from' => $this->faker->dateTimeBetween('-2 weeks', 'now')->format('Y-m-d H:i:s'),
|
|
||||||
'registration_until' => $this->faker->dateTimeBetween('now', '+2 weeks')->format('Y-m-d H:i:s'),
|
|
||||||
'mail_top' => $this->faker->text(),
|
|
||||||
'mail_bottom' => $this->faker->text(),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param array<int, FormtemplateSectionRequest> $sections
|
|
||||||
*/
|
|
||||||
public function sections(array $sections): self
|
|
||||||
{
|
|
||||||
return $this->state(['config' => ['sections' => array_map(fn ($section) => $section->create(), $sections)]]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param array<int, FormtemplateFieldRequest> $fields
|
|
||||||
*/
|
|
||||||
public function fields(array $fields): self
|
|
||||||
{
|
|
||||||
return $this->sections([FormtemplateSectionRequest::new()->fields($fields)]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param mixed $parameters
|
|
||||||
*/
|
|
||||||
public function __call($method, $parameters): self
|
|
||||||
{
|
|
||||||
return $this->state([str($method)->snake()->toString() => $parameters[0]]);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,48 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace Database\Factories\Form\Models;
|
|
||||||
|
|
||||||
use App\Form\Models\Formtemplate;
|
|
||||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
|
||||||
use Tests\Feature\Form\FormtemplateSectionRequest;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @extends Factory<Formtemplate>
|
|
||||||
* @method self name(string $name)
|
|
||||||
*/
|
|
||||||
class FormtemplateFactory extends Factory
|
|
||||||
{
|
|
||||||
|
|
||||||
public $model = Formtemplate::class;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Define the model's default state.
|
|
||||||
*
|
|
||||||
* @return array<string, mixed>
|
|
||||||
*/
|
|
||||||
public function definition()
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
'name' => $this->faker->words(4, true),
|
|
||||||
'config' => [
|
|
||||||
'sections' => [],
|
|
||||||
],
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param array<int, FormtemplateSectionRequest> $sections
|
|
||||||
*/
|
|
||||||
public function sections(array $sections): self
|
|
||||||
{
|
|
||||||
return $this->state(['config' => ['sections' => array_map(fn ($section) => $section->create(), $sections)]]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param mixed $parameters
|
|
||||||
*/
|
|
||||||
public function __call($method, $parameters): self
|
|
||||||
{
|
|
||||||
return $this->state([str($method)->snake()->toString() => $parameters[0]]);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,45 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace Database\Factories\Form\Models;
|
|
||||||
|
|
||||||
use App\Form\Models\Participant;
|
|
||||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
|
||||||
use Tests\Feature\Form\FormtemplateSectionRequest;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @extends Factory<Participant>
|
|
||||||
*/
|
|
||||||
class ParticipantFactory extends Factory
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* The name of the factory's corresponding model.
|
|
||||||
*
|
|
||||||
* @var class-string<Participant>
|
|
||||||
*/
|
|
||||||
protected $model = Participant::class;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Define the model's default state.
|
|
||||||
*
|
|
||||||
* @return array<string, mixed>
|
|
||||||
*/
|
|
||||||
public function definition()
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
'data' => [],
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param array<string, mixed> $data
|
|
||||||
*/
|
|
||||||
public function data(array $data): self
|
|
||||||
{
|
|
||||||
return $this->state(['data' => $data]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function nr(int $number): self
|
|
||||||
{
|
|
||||||
return $this->state(['mitgliedsnr' => $number]);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -37,14 +37,4 @@ class GroupFactory extends Factory
|
||||||
{
|
{
|
||||||
return $this->state(['name' => $name]);
|
return $this->state(['name' => $name]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function level(Level $level): self
|
|
||||||
{
|
|
||||||
return $this->state(['level' => $level]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function innerName(string $name): self
|
|
||||||
{
|
|
||||||
return $this->state(['inner_name' => $name]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,7 +52,7 @@ class MemberFactory extends Factory
|
||||||
: Nationality::factory()->create();
|
: Nationality::factory()->create();
|
||||||
$subscription = Subscription::count()
|
$subscription = Subscription::count()
|
||||||
? Subscription::get()->random()
|
? Subscription::get()->random()
|
||||||
: Subscription::factory()->forFee()->create();
|
: Subscription::factory()->for(Fee::factory())->create();
|
||||||
|
|
||||||
return $this
|
return $this
|
||||||
->for($country)
|
->for($country)
|
||||||
|
|
|
@ -19,16 +19,10 @@ class SubscriptionFactory extends Factory
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'name' => $this->faker->word,
|
'name' => $this->faker->word,
|
||||||
|
'fee_id' => Fee::factory()->createOne()->id,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function forFee(?int $namiId = null): self
|
|
||||||
{
|
|
||||||
$namiId = $namiId ?: $this->faker->numberBetween(600, 800);
|
|
||||||
|
|
||||||
return $this->for(Fee::factory()->inNami($namiId));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function name(string $name): self
|
public function name(string $name): self
|
||||||
{
|
{
|
||||||
return $this->state(['name' => $name]);
|
return $this->state(['name' => $name]);
|
||||||
|
|
|
@ -48,9 +48,9 @@ class SubactivityFactory extends Factory
|
||||||
return $this->state(['nami_id' => $namiId]);
|
return $this->state(['nami_id' => $namiId]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function ageGroup(bool $ageGroup = true): self
|
public function ageGroup(): self
|
||||||
{
|
{
|
||||||
return $this->state(['is_age_group' => $ageGroup]);
|
return $this->state(['is_age_group' => true]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function filterable(): self
|
public function filterable(): self
|
||||||
|
|
|
@ -1,24 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace Database\Factories\Traits;
|
|
||||||
|
|
||||||
use Illuminate\Http\UploadedFile;
|
|
||||||
use Spatie\MediaLibrary\HasMedia;
|
|
||||||
|
|
||||||
trait FakesMedia
|
|
||||||
{
|
|
||||||
|
|
||||||
public function withImage(string $collection, string $filename): self
|
|
||||||
{
|
|
||||||
return $this->afterCreating(function (HasMedia $model) use ($filename, $collection) {
|
|
||||||
$pathinfo = pathinfo($filename);
|
|
||||||
|
|
||||||
UploadedFile::fake()->image($filename, 1000, 1000)->storeAs('media-library', $filename, 'temp');
|
|
||||||
|
|
||||||
$model->addMediaFromDisk('media-library/' . $filename, 'temp')
|
|
||||||
->usingName($pathinfo['filename'])
|
|
||||||
->usingFileName($pathinfo['basename'])
|
|
||||||
->toMediaCollection($collection);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,51 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
use Illuminate\Database\Migrations\Migration;
|
|
||||||
use Illuminate\Database\Schema\Blueprint;
|
|
||||||
use Illuminate\Support\Facades\Schema;
|
|
||||||
|
|
||||||
return new class extends Migration
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Run the migrations.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function up()
|
|
||||||
{
|
|
||||||
Schema::create('formtemplates', function (Blueprint $table) {
|
|
||||||
$table->id();
|
|
||||||
$table->string('name');
|
|
||||||
$table->json('config');
|
|
||||||
$table->timestamps();
|
|
||||||
});
|
|
||||||
|
|
||||||
Schema::create('forms', function (Blueprint $table) {
|
|
||||||
$table->id();
|
|
||||||
$table->string('name');
|
|
||||||
$table->string('slug');
|
|
||||||
$table->json('description');
|
|
||||||
$table->text('excerpt');
|
|
||||||
$table->json('config');
|
|
||||||
$table->date('from');
|
|
||||||
$table->date('to');
|
|
||||||
$table->dateTime('registration_from')->nullable();
|
|
||||||
$table->dateTime('registration_until')->nullable();
|
|
||||||
$table->text('mail_top')->nullable();
|
|
||||||
$table->text('mail_bottom')->nullable();
|
|
||||||
$table->json('meta');
|
|
||||||
$table->timestamps();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reverse the migrations.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function down()
|
|
||||||
{
|
|
||||||
Schema::dropIfExists('formtemplates');
|
|
||||||
Schema::dropIfExists('forms');
|
|
||||||
}
|
|
||||||
};
|
|
|
@ -1,42 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
use Illuminate\Database\Migrations\Migration;
|
|
||||||
use Illuminate\Database\Schema\Blueprint;
|
|
||||||
use Illuminate\Support\Facades\Schema;
|
|
||||||
|
|
||||||
return new class extends Migration
|
|
||||||
{
|
|
||||||
public function up(): void
|
|
||||||
{
|
|
||||||
Schema::create('media', function (Blueprint $table) {
|
|
||||||
$table->id();
|
|
||||||
|
|
||||||
$table->morphs('model');
|
|
||||||
$table->uuid('uuid')->nullable()->unique();
|
|
||||||
$table->string('collection_name');
|
|
||||||
$table->string('name');
|
|
||||||
$table->string('file_name');
|
|
||||||
$table->string('mime_type')->nullable();
|
|
||||||
$table->string('disk');
|
|
||||||
$table->string('conversions_disk')->nullable();
|
|
||||||
$table->unsignedBigInteger('size');
|
|
||||||
$table->json('manipulations');
|
|
||||||
$table->json('custom_properties');
|
|
||||||
$table->json('generated_conversions');
|
|
||||||
$table->json('responsive_images');
|
|
||||||
$table->unsignedInteger('order_column')->nullable()->index();
|
|
||||||
|
|
||||||
$table->nullableTimestamps();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reverse the migrations.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function down()
|
|
||||||
{
|
|
||||||
Schema::dropIfExists('media');
|
|
||||||
}
|
|
||||||
};
|
|
|
@ -1,35 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
use Illuminate\Database\Migrations\Migration;
|
|
||||||
use Illuminate\Database\Schema\Blueprint;
|
|
||||||
use Illuminate\Support\Facades\Schema;
|
|
||||||
|
|
||||||
return new class extends Migration
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Run the migrations.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function up()
|
|
||||||
{
|
|
||||||
Schema::create('participants', function (Blueprint $table) {
|
|
||||||
$table->id();
|
|
||||||
$table->json('data');
|
|
||||||
$table->foreignId('form_id');
|
|
||||||
$table->foreignId('parent_id')->nullable();
|
|
||||||
$table->string('mitgliedsnr')->nullable();
|
|
||||||
$table->timestamps();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reverse the migrations.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function down()
|
|
||||||
{
|
|
||||||
Schema::dropIfExists('participants');
|
|
||||||
}
|
|
||||||
};
|
|
|
@ -5,10 +5,6 @@
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@editorjs/editorjs": "^2.29.0",
|
|
||||||
"@editorjs/header": "^2.8.1",
|
|
||||||
"@editorjs/nested-list": "^1.4.2",
|
|
||||||
"@editorjs/paragraph": "^2.11.3",
|
|
||||||
"@inertiajs/vue3": "^1.0.14",
|
"@inertiajs/vue3": "^1.0.14",
|
||||||
"@tailwindcss/typography": "^0.5.10",
|
"@tailwindcss/typography": "^0.5.10",
|
||||||
"@vitejs/plugin-vue": "^4.6.2",
|
"@vitejs/plugin-vue": "^4.6.2",
|
||||||
|
@ -29,7 +25,6 @@
|
||||||
"wnumb": "^1.2.0"
|
"wnumb": "^1.2.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"accounting": "^0.4.1",
|
|
||||||
"autoprefixer": "^10.4.17",
|
"autoprefixer": "^10.4.17",
|
||||||
"axios": "^1.6.6",
|
"axios": "^1.6.6",
|
||||||
"dayjs": "^1.11.10",
|
"dayjs": "^1.11.10",
|
||||||
|
@ -72,11 +67,6 @@
|
||||||
"node": ">=6.0.0"
|
"node": ">=6.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@codexteam/icons": {
|
|
||||||
"version": "0.0.5",
|
|
||||||
"resolved": "https://registry.npmjs.org/@codexteam/icons/-/icons-0.0.5.tgz",
|
|
||||||
"integrity": "sha512-s6H2KXhLz2rgbMZSkRm8dsMJvyUNZsEjxobBEg9ztdrb1B2H3pEzY6iTwI4XUPJWJ3c3qRKwV4TrO3J5jUdoQA=="
|
|
||||||
},
|
|
||||||
"node_modules/@colors/colors": {
|
"node_modules/@colors/colors": {
|
||||||
"version": "1.6.0",
|
"version": "1.6.0",
|
||||||
"resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz",
|
||||||
|
@ -95,45 +85,6 @@
|
||||||
"kuler": "^2.0.0"
|
"kuler": "^2.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@editorjs/editorjs": {
|
|
||||||
"version": "2.29.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@editorjs/editorjs/-/editorjs-2.29.0.tgz",
|
|
||||||
"integrity": "sha512-w2BVboSHokMBd/cAOZn0UU328o3gSZ8XUvFPA2e9+ciIGKILiRSPB70kkNdmhHkuNS3q2px+vdaIFaywBl7wGA=="
|
|
||||||
},
|
|
||||||
"node_modules/@editorjs/header": {
|
|
||||||
"version": "2.8.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/@editorjs/header/-/header-2.8.1.tgz",
|
|
||||||
"integrity": "sha512-y0HVXRP7m2W617CWo3fsb5HhXmSLaRpb9GzFx0Vkp/HEm9Dz5YO1s8tC7R8JD3MskwoYh7V0hRFQt39io/r6hA==",
|
|
||||||
"dependencies": {
|
|
||||||
"@codexteam/icons": "^0.0.5"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@editorjs/nested-list": {
|
|
||||||
"version": "1.4.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/@editorjs/nested-list/-/nested-list-1.4.2.tgz",
|
|
||||||
"integrity": "sha512-qb1dAoJ+bihqmlR3822TC2GuIxEjTCLTZsZVWNces3uJIZ+W4019G3IJKBt/MOOgz4Evzad/RvUEKwPCPe6YOQ==",
|
|
||||||
"dependencies": {
|
|
||||||
"@codexteam/icons": "^0.0.2"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@editorjs/nested-list/node_modules/@codexteam/icons": {
|
|
||||||
"version": "0.0.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/@codexteam/icons/-/icons-0.0.2.tgz",
|
|
||||||
"integrity": "sha512-KdeKj3TwaTHqM3IXd5YjeJP39PBUZTb+dtHjGlf5+b0VgsxYD4qzsZkb11lzopZbAuDsHaZJmAYQ8LFligIT6Q=="
|
|
||||||
},
|
|
||||||
"node_modules/@editorjs/paragraph": {
|
|
||||||
"version": "2.11.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/@editorjs/paragraph/-/paragraph-2.11.3.tgz",
|
|
||||||
"integrity": "sha512-ON72lhvhgWzPrq4VXpHUeut9bsFeJgVATDeL850FVToOwYHKvdsNpfu0VgxEodhkXgzU/IGl4FzdqC2wd3AJUQ==",
|
|
||||||
"dependencies": {
|
|
||||||
"@codexteam/icons": "^0.0.4"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@editorjs/paragraph/node_modules/@codexteam/icons": {
|
|
||||||
"version": "0.0.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/@codexteam/icons/-/icons-0.0.4.tgz",
|
|
||||||
"integrity": "sha512-V8N/TY2TGyas4wLrPIFq7bcow68b3gu8DfDt1+rrHPtXxcexadKauRJL6eQgfG7Z0LCrN4boLRawR4S9gjIh/Q=="
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/android-arm": {
|
"node_modules/@esbuild/android-arm": {
|
||||||
"version": "0.18.20",
|
"version": "0.18.20",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.20.tgz",
|
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.20.tgz",
|
||||||
|
@ -1132,12 +1083,6 @@
|
||||||
"node": ">=10.0.0"
|
"node": ">=10.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/accounting": {
|
|
||||||
"version": "0.4.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/accounting/-/accounting-0.4.1.tgz",
|
|
||||||
"integrity": "sha512-RU6KY9Y5wllyaCNBo1W11ZOTnTHMMgOZkIwdOOs6W5ibMTp72i4xIbEA48djxVGqMNTUNbvrP/1nWg5Af5m2gQ==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"node_modules/acorn": {
|
"node_modules/acorn": {
|
||||||
"version": "8.11.3",
|
"version": "8.11.3",
|
||||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz",
|
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz",
|
||||||
|
|
|
@ -12,7 +12,6 @@
|
||||||
"fix": "eslint \"resources/js/**/*.{js,vue}\" --fix"
|
"fix": "eslint \"resources/js/**/*.{js,vue}\" --fix"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"accounting": "^0.4.1",
|
|
||||||
"autoprefixer": "^10.4.17",
|
"autoprefixer": "^10.4.17",
|
||||||
"axios": "^1.6.6",
|
"axios": "^1.6.6",
|
||||||
"dayjs": "^1.11.10",
|
"dayjs": "^1.11.10",
|
||||||
|
@ -24,10 +23,6 @@
|
||||||
"vue-axios": "^3.5.2"
|
"vue-axios": "^3.5.2"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@editorjs/editorjs": "^2.29.0",
|
|
||||||
"@editorjs/header": "^2.8.1",
|
|
||||||
"@editorjs/nested-list": "^1.4.2",
|
|
||||||
"@editorjs/paragraph": "^2.11.3",
|
|
||||||
"@inertiajs/vue3": "^1.0.14",
|
"@inertiajs/vue3": "^1.0.14",
|
||||||
"@tailwindcss/typography": "^0.5.10",
|
"@tailwindcss/typography": "^0.5.10",
|
||||||
"@vitejs/plugin-vue": "^4.6.2",
|
"@vitejs/plugin-vue": "^4.6.2",
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
Subproject commit 2e232a95fd84f6dcca80abe4d2ed37e1fac7e352
|
|
|
@ -1 +0,0 @@
|
||||||
Subproject commit 91e5cc3e3b6d7d0a8a3e2361514ec8c1ce9cb655
|
|
26
phpstan.neon
26
phpstan.neon
|
@ -24,7 +24,6 @@ parameters:
|
||||||
ContributionApiRequestArray: 'array{dateFrom: string, dateUntil: string, zipLocation: string, country: int, eventName: string, member_data: array<int, ContributionMemberData>}'
|
ContributionApiRequestArray: 'array{dateFrom: string, dateUntil: string, zipLocation: string, country: int, eventName: string, member_data: array<int, ContributionMemberData>}'
|
||||||
MailgatewayCustomField: 'array{name: string, label: string, type: string, storeValidator: string, updateValidator: string, default: string}'
|
MailgatewayCustomField: 'array{name: string, label: string, type: string, storeValidator: string, updateValidator: string, default: string}'
|
||||||
MailgatewayParsedCustomField: 'array{name: string, label: string, type: string, storeValidator: string, updateValidator: string, default: string, is_required: bool}'
|
MailgatewayParsedCustomField: 'array{name: string, label: string, type: string, storeValidator: string, updateValidator: string, default: string, is_required: bool}'
|
||||||
SluggableConfig: 'array<string, array{source: array<int, string>}>'
|
|
||||||
|
|
||||||
ignoreErrors:
|
ignoreErrors:
|
||||||
-
|
-
|
||||||
|
@ -137,6 +136,16 @@ parameters:
|
||||||
count: 1
|
count: 1
|
||||||
path: app/Payment/SubscriptionResource.php
|
path: app/Payment/SubscriptionResource.php
|
||||||
|
|
||||||
|
-
|
||||||
|
message: "#^PHPDoc type array of property App\\\\Providers\\\\AuthServiceProvider\\:\\:\\$policies is not covariant with PHPDoc type array\\<class\\-string, class\\-string\\> of overridden property Illuminate\\\\Foundation\\\\Support\\\\Providers\\\\AuthServiceProvider\\:\\:\\$policies\\.$#"
|
||||||
|
count: 1
|
||||||
|
path: app/Providers/AuthServiceProvider.php
|
||||||
|
|
||||||
|
-
|
||||||
|
message: "#^Property App\\\\Providers\\\\AuthServiceProvider\\:\\:\\$policies type has no value type specified in iterable type array\\.$#"
|
||||||
|
count: 1
|
||||||
|
path: app/Providers/AuthServiceProvider.php
|
||||||
|
|
||||||
-
|
-
|
||||||
message: "#^Method Database\\\\Factories\\\\ActivityFactory\\:\\:definition\\(\\) return type has no value type specified in iterable type array\\.$#"
|
message: "#^Method Database\\\\Factories\\\\ActivityFactory\\:\\:definition\\(\\) return type has no value type specified in iterable type array\\.$#"
|
||||||
count: 1
|
count: 1
|
||||||
|
@ -532,18 +541,3 @@ parameters:
|
||||||
message: "#^Parameter \\#1 \\.\\.\\.\\$parts of method App\\\\Member\\\\FilterScope\\:\\:combinations\\(\\) expects array\\<int, int\\>, array\\<int, array\\<int, int\\>\\> given\\.$#"
|
message: "#^Parameter \\#1 \\.\\.\\.\\$parts of method App\\\\Member\\\\FilterScope\\:\\:combinations\\(\\) expects array\\<int, int\\>, array\\<int, array\\<int, int\\>\\> given\\.$#"
|
||||||
count: 1
|
count: 1
|
||||||
path: app/Member/FilterScope.php
|
path: app/Member/FilterScope.php
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Call to function is_null\\(\\) with array will always evaluate to false\\.$#"
|
|
||||||
count: 1
|
|
||||||
path: app/Form/Models/Form.php
|
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Unable to resolve the template type TKey in call to function collect$#"
|
|
||||||
count: 1
|
|
||||||
path: app/Form/Fields/NamiField.php
|
|
||||||
|
|
||||||
-
|
|
||||||
message: "#^Unable to resolve the template type TValue in call to function collect$#"
|
|
||||||
count: 1
|
|
||||||
path: app/Form/Fields/NamiField.php
|
|
||||||
|
|
|
@ -9,4 +9,3 @@
|
||||||
@import 'table';
|
@import 'table';
|
||||||
@import 'bool';
|
@import 'bool';
|
||||||
@import 'form';
|
@import 'form';
|
||||||
@import 'editor';
|
|
||||||
|
|
|
@ -1,7 +1,3 @@
|
||||||
::selection {
|
|
||||||
@apply bg-blue-800;
|
|
||||||
}
|
|
||||||
|
|
||||||
.button-group > div {
|
.button-group > div {
|
||||||
padding-left: 0 !important;
|
padding-left: 0 !important;
|
||||||
padding-right: 0 !important;
|
padding-right: 0 !important;
|
||||||
|
|
|
@ -1,83 +0,0 @@
|
||||||
.ce-inline-toolbar,
|
|
||||||
.codex-editor--narrow .ce-toolbox,
|
|
||||||
.ce-conversion-toolbar,
|
|
||||||
.ce-settings,
|
|
||||||
.ce-settings__button,
|
|
||||||
.ce-toolbar__settings-btn,
|
|
||||||
.cdx-button,
|
|
||||||
.ce-popover,
|
|
||||||
.ce-toolbar__plus:hover {
|
|
||||||
@apply bg-primary-700;
|
|
||||||
color: inherit;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ce-inline-tool-input {
|
|
||||||
@apply bg-primary-700 text-primary-200 placeholder-primary-500;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ce-block--selected {
|
|
||||||
@apply bg-gray-800;
|
|
||||||
}
|
|
||||||
|
|
||||||
h2.ce-header {
|
|
||||||
@apply font-semibold text-white text-xl mt-2;
|
|
||||||
}
|
|
||||||
|
|
||||||
h3.ce-header {
|
|
||||||
@apply font-semibold text-white text-lg mt-2;
|
|
||||||
}
|
|
||||||
|
|
||||||
h4.ce-header {
|
|
||||||
@apply font-semibold text-white mt-2;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ce-block--selected .ce-block__content {
|
|
||||||
@apply bg-gray-800;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ce-conversion-tool__icon,
|
|
||||||
.ce-popover-item__icon {
|
|
||||||
@apply bg-primary-800;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ce-popover-item__title {
|
|
||||||
@apply text-primary-200;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ce-popover-item:hover:not(.ce-popover-item--no-hover) {
|
|
||||||
@apply bg-primary-800;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ce-inline-tool,
|
|
||||||
.ce-conversion-toolbar__label,
|
|
||||||
.ce-toolbox__button,
|
|
||||||
.cdx-settings-button,
|
|
||||||
.ce-toolbar__plus {
|
|
||||||
color: inherit;
|
|
||||||
}
|
|
||||||
|
|
||||||
.codex-editor ::selection {
|
|
||||||
@apply bg-blue-800;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cdx-settings-button:hover,
|
|
||||||
.ce-settings__button:hover,
|
|
||||||
.ce-toolbox__button--active,
|
|
||||||
.ce-toolbox__button:hover,
|
|
||||||
.cdx-button:hover,
|
|
||||||
.ce-inline-toolbar__dropdown:hover,
|
|
||||||
.ce-inline-tool:hover,
|
|
||||||
.ce-popover__item:hover,
|
|
||||||
.ce-toolbar__settings-btn:hover {
|
|
||||||
background-color: #439a86;
|
|
||||||
color: inherit;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cdx-notify--error {
|
|
||||||
background: #fb5d5d !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cdx-notify__cross::after,
|
|
||||||
.cdx-notify__cross::before {
|
|
||||||
background: white;
|
|
||||||
}
|
|
|
@ -1 +0,0 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 503.664 503.664" style="enable-background:new 0 0 503.664 503.664" xml:space="preserve"><path d="M467.98 39.376H35.792C16.112 39.376.1 55.384.1 75.064L0 369.52c0 19.676 16.004 35.748 35.684 35.748h141.4v35.408h-27.612c-2.172 0-3.864 1.848-3.864 4.02v15.532c0 2.172 1.688 4.06 3.864 4.06h204c2.164 0 4.584-1.888 4.584-4.06v-15.532c0-2.172-2.42-4.02-4.584-4.02H326.58v-35.408h141.304c19.664 0 35.688-16.072 35.688-35.748l.092-294.424c.004-19.684-16.004-35.72-35.684-35.72zM251.34 377.524c-6.524 0-11.804-5.284-11.804-11.804 0-6.516 5.28-11.8 11.804-11.8 6.52 0 11.804 5.284 11.804 11.8 0 6.52-5.28 11.804-11.804 11.804zm220.812-50.944H31.512V70.848h440.64V326.58z"/></svg>
|
|
Before Width: | Height: | Size: 723 B |
|
@ -1 +0,0 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="124.813" height="124.813" style="enable-background:new 0 0 124.813 124.813" xml:space="preserve"><path d="m48.083 80.355-1.915 11.374a4.158 4.158 0 0 0 1.65 4.05 4.15 4.15 0 0 0 4.361.32l10.226-5.338L72.631 96.1a4.184 4.184 0 0 0 1.924.472c.859 0 1.716-.269 2.439-.792a4.152 4.152 0 0 0 1.651-4.05l-1.913-11.374 8.234-8.077a4.159 4.159 0 0 0 1.044-4.247 4.152 4.152 0 0 0-3.341-2.823l-11.41-1.692-5.139-10.329a4.147 4.147 0 0 0-3.716-2.303 4.16 4.16 0 0 0-3.718 2.303l-5.134 10.329-11.41 1.691a4.147 4.147 0 0 0-3.339 2.823 4.147 4.147 0 0 0 1.042 4.247l8.238 8.077z"/><path d="M111.443 13.269H98.378V6.022A6.022 6.022 0 0 0 92.355 0H91.4a6.021 6.021 0 0 0-6.021 6.022v7.247H39.282V6.022A6.022 6.022 0 0 0 33.261 0h-.956a6.021 6.021 0 0 0-6.021 6.022v7.247H13.371C6.538 13.269.977 18.828.977 25.663v86.757c0 6.831 5.561 12.394 12.394 12.394h98.073c6.832 0 12.394-5.562 12.394-12.394V25.663c-.001-6.835-5.563-12.394-12.395-12.394zm-1.617 97.534H14.988V43.268h94.838v67.535z"/></svg>
|
|
Before Width: | Height: | Size: 1.0 KiB |
|
@ -1 +0,0 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 503.604 503.604" style="enable-background:new 0 0 503.604 503.604" xml:space="preserve"><path d="M337.324 0H167.192c-28.924 0-53.5 23.584-53.5 52.5v398.664c0 28.916 24.056 52.44 52.98 52.44l170.412-.184c28.92 0 52.58-23.528 52.58-52.448l.248-398.5C389.908 23.452 366.364 0 337.324 0zM227.68 31.476h49.36c4.336 0 7.868 3.52 7.868 7.868 0 4.348-3.532 7.868-7.868 7.868h-49.36a7.865 7.865 0 0 1-7.868-7.868 7.865 7.865 0 0 1 7.868-7.868zm-29.66 2.504c2.916-2.912 8.224-2.952 11.136 0a7.973 7.973 0 0 1 2.324 5.588c0 2.048-.864 4.088-2.324 5.548-1.452 1.46-3.504 2.32-5.548 2.32-2.084 0-4.088-.86-5.588-2.32-1.452-1.456-2.28-3.5-2.28-5.548-.004-2.088.828-4.132 2.28-5.588zm52.752 454.028c-12.984 0-23.544-10.568-23.544-23.548 0-12.984 10.56-23.548 23.544-23.548s23.544 10.564 23.544 23.548c0 12.98-10.564 23.548-23.544 23.548zm114.716-63.1H141.232V74.756h224.256v350.152z"/></svg>
|
|
Before Width: | Height: | Size: 929 B |
|
@ -1 +1,18 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" style="enable-background:new 0 0 512 512" xml:space="preserve"><path d="M51.2 353.28 0 512l158.72-51.2zm35.96-36.788L336.96 66.69 445.57 175.3l-249.8 249.802zM504.32 79.36 432.64 7.68c-10.24-10.24-25.6-10.24-35.84 0l-23.04 23.04 107.52 107.52 23.04-23.04c10.24-10.24 10.24-25.6 0-35.84z"/></svg>
|
<?xml version="1.0" ?>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" id="Capa_1" x="0px" y="0px" viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<polygon points="51.2,353.28 0,512 158.72,460.8 "/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<rect x="89.73" y="169.097" transform="matrix(0.7071 -0.7071 0.7071 0.7071 -95.8575 260.3719)" width="353.277" height="153.599"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<path d="M504.32,79.36L432.64,7.68c-10.24-10.24-25.6-10.24-35.84,0l-23.04,23.04l107.52,107.52l23.04-23.04 C514.56,104.96,514.56,89.6,504.32,79.36z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
|
Before Width: | Height: | Size: 357 B After Width: | Height: | Size: 660 B |
|
@ -1 +0,0 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 503.592 503.592" style="enable-background:new 0 0 503.592 503.592" xml:space="preserve"><path d="M428.636 27.852C428.636 12.508 416.144 0 400.792 0H102.66C87.312 0 74.828 12.504 74.828 27.852l.132 447.892c0 15.352 12.488 27.848 27.832 27.848h298.136c15.352 0 27.836-12.496 27.836-27.848l-.128-447.892zm-196.276 3.62h38.884c4.36 0 7.868 3.52 7.868 7.868a7.863 7.863 0 0 1-7.868 7.872H232.36c-4.348 0-7.868-3.524-7.868-7.872s3.52-7.868 7.868-7.868zm-29.396 2.44c2.916-2.948 8.184-2.948 11.136 0 1.452 1.46 2.324 3.504 2.324 5.552 0 2.08-.872 4.084-2.324 5.544-1.46 1.496-3.504 2.324-5.584 2.324a7.747 7.747 0 0 1-5.552-2.324c-1.452-1.456-2.316-3.464-2.316-5.544 0-2.048.868-4.092 2.316-5.552zm47.252 454.216c-12.98 0-23.544-10.564-23.544-23.544 0-12.984 10.568-23.548 23.544-23.548 12.988 0 23.544 10.564 23.544 23.548s-10.552 23.544-23.544 23.544zM94.424 424.904V74.752h314.744v350.152H94.424z"/></svg>
|
|
Before Width: | Height: | Size: 954 B |
|
@ -50,10 +50,6 @@ createInertiaApp({
|
||||||
requireModules(import.meta.glob('./components/form/*.vue'), app, 'f');
|
requireModules(import.meta.glob('./components/form/*.vue'), app, 'f');
|
||||||
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(
|
|
||||||
'f-singlefile',
|
|
||||||
defineAsyncComponent(() => import('!/medialibrary-helper/assets/components/SingleFile.vue'))
|
|
||||||
);
|
|
||||||
|
|
||||||
app.provide('axios', app.config.globalProperties.axios);
|
app.provide('axios', app.config.globalProperties.axios);
|
||||||
app.mount(el);
|
app.mount(el);
|
||||||
|
|
|
@ -1,98 +0,0 @@
|
||||||
<template>
|
|
||||||
<div>
|
|
||||||
<span v-if="label" class="font-semibold text-gray-400" :class="labelClass(size)">{{ label }}<span v-show="required" class="text-red-800"> *</span></span>
|
|
||||||
<div class="relative w-full h-full">
|
|
||||||
<div :id="id" :class="[defaultFieldClass, fieldClass(size)]"></div>
|
|
||||||
<div v-if="hint" v-tooltip="hint" class="absolute right-0 top-0 mr-2 mt-2">
|
|
||||||
<ui-sprite src="info-button" class="w-5 h-5 text-indigo-200"></ui-sprite>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
import {debounce} from 'lodash';
|
|
||||||
import {onMounted, ref} from 'vue';
|
|
||||||
import EditorJS from '@editorjs/editorjs';
|
|
||||||
import Header from '@editorjs/header';
|
|
||||||
import Paragraph from '@editorjs/paragraph';
|
|
||||||
import NestedList from '@editorjs/nested-list';
|
|
||||||
import useFieldSize from '../../composables/useFieldSize.js';
|
|
||||||
const emit = defineEmits(['update:modelValue']);
|
|
||||||
|
|
||||||
const {labelClass, fieldClass, defaultFieldClass} = useFieldSize();
|
|
||||||
|
|
||||||
const props = defineProps({
|
|
||||||
required: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
size: {
|
|
||||||
default: null,
|
|
||||||
},
|
|
||||||
rows: {
|
|
||||||
default: function () {
|
|
||||||
return 4;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
id: {
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
hint: {
|
|
||||||
default: null,
|
|
||||||
},
|
|
||||||
modelValue: {
|
|
||||||
default: undefined,
|
|
||||||
},
|
|
||||||
label: {
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
placeholder: {
|
|
||||||
default: '',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const editor = ref(null);
|
|
||||||
|
|
||||||
onMounted(async () => {
|
|
||||||
editor.value = new EditorJS({
|
|
||||||
placeholder: props.placeholder,
|
|
||||||
holder: props.id,
|
|
||||||
minHeight: 0,
|
|
||||||
defaultBlock: 'paragraph',
|
|
||||||
data: JSON.parse(JSON.stringify(props.modelValue)),
|
|
||||||
tools: {
|
|
||||||
paragraph: {
|
|
||||||
class: Paragraph,
|
|
||||||
shortcut: 'CTRL+P',
|
|
||||||
inlineToolbar: true,
|
|
||||||
config: {
|
|
||||||
preserveBlank: true,
|
|
||||||
placeholder: 'Absatz',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
heading: {
|
|
||||||
class: Header,
|
|
||||||
shortcut: 'CTRL+H',
|
|
||||||
inlineToolbar: true,
|
|
||||||
config: {
|
|
||||||
placeholder: 'Überschrift',
|
|
||||||
levels: [2, 3, 4],
|
|
||||||
defaultLevel: 2,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
list: {
|
|
||||||
class: NestedList,
|
|
||||||
shortcut: 'CTRL+L',
|
|
||||||
inlineToolbar: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
onChange: debounce(async (api, event) => {
|
|
||||||
const data = await editor.value.save();
|
|
||||||
emit('update:modelValue', data);
|
|
||||||
}, 500),
|
|
||||||
});
|
|
||||||
await editor.value.isReady;
|
|
||||||
console.log('Editor is ready');
|
|
||||||
});
|
|
||||||
</script>
|
|
|
@ -85,8 +85,8 @@ export default {
|
||||||
return Array.isArray(this.options)
|
return Array.isArray(this.options)
|
||||||
? this.options
|
? this.options
|
||||||
: map(this.options, (value, key) => {
|
: map(this.options, (value, key) => {
|
||||||
return {name: value, id: key};
|
return { name: value, id: key };
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
|
|
|
@ -5,20 +5,8 @@
|
||||||
<span v-show="required" class="text-red-800"> *</span>
|
<span v-show="required" class="text-red-800"> *</span>
|
||||||
</span>
|
</span>
|
||||||
<div class="real-field-wrap size-sm" :class="sizes[size].field">
|
<div class="real-field-wrap size-sm" :class="sizes[size].field">
|
||||||
<input
|
<input :name="name" :type="type" :value="transformedValue" :disabled="disabled" :placeholder="placeholder"
|
||||||
:name="name"
|
@keypress="$emit('keypress', $event)" @input="onInput" @change="onChange" @focus="onFocus" @blur="onBlur" />
|
||||||
:type="type"
|
|
||||||
:value="transformedValue"
|
|
||||||
:disabled="disabled"
|
|
||||||
:placeholder="placeholder"
|
|
||||||
:min="min"
|
|
||||||
:max="max"
|
|
||||||
@keypress="$emit('keypress', $event)"
|
|
||||||
@input="onInput"
|
|
||||||
@change="onChange"
|
|
||||||
@focus="onFocus"
|
|
||||||
@blur="onBlur"
|
|
||||||
/>
|
|
||||||
<div v-if="hint" class="info-wrap">
|
<div v-if="hint" class="info-wrap">
|
||||||
<div v-tooltip="hint">
|
<div v-tooltip="hint">
|
||||||
<ui-sprite src="info-button" class="info-button"></ui-sprite>
|
<ui-sprite src="info-button" class="info-button"></ui-sprite>
|
||||||
|
@ -293,12 +281,6 @@ export default {
|
||||||
default: false,
|
default: false,
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
},
|
},
|
||||||
min: {
|
|
||||||
default: () => '',
|
|
||||||
},
|
|
||||||
max: {
|
|
||||||
default: () => '',
|
|
||||||
},
|
|
||||||
name: {},
|
name: {},
|
||||||
},
|
},
|
||||||
data: function () {
|
data: function () {
|
||||||
|
@ -342,7 +324,7 @@ export default {
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
if (typeof this.modelValue === 'undefined') {
|
if (typeof this.modelValue === 'undefined') {
|
||||||
this.$emit('update:modelValue', this.default === undefined ? '' : this.default);
|
this.$emit('input', this.default === undefined ? '' : this.default);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
|
|
@ -1,54 +1,104 @@
|
||||||
<template>
|
<template>
|
||||||
<label class="flex flex-col">
|
<label class="flex flex-col relative">
|
||||||
<span v-if="label" class="font-semibold text-gray-400" :class="labelClass(size)">{{ label }}<span v-show="required" class="text-red-800"> *</span></span>
|
<span
|
||||||
<div class="relative w-full h-full">
|
v-if="label && !inset"
|
||||||
<textarea :placeholder="placeholder" class="h-full w-full outline-none" :class="[defaultFieldClass, fieldClass(size)]" :rows="rows" @input="trigger" v-text="modelValue"></textarea>
|
class="font-semibold text-gray-400"
|
||||||
<div v-if="hint" v-tooltip="hint" class="absolute right-0 top-0 mr-2 mt-2">
|
:class="{
|
||||||
<ui-sprite src="info-button" class="w-5 h-5 text-indigo-200"></ui-sprite>
|
'text-xs': size == 'sm',
|
||||||
</div>
|
'text-sm': size === null,
|
||||||
|
}"
|
||||||
|
>{{ label }}<span v-show="required" class="text-red-800"> *</span></span
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
v-if="label && inset"
|
||||||
|
class="absolute top-0 left-0 -mt-2 px-1 ml-3 inset-bg font-semibold text-gray-700"
|
||||||
|
:class="{
|
||||||
|
'text-xs': size == 'sm',
|
||||||
|
'text-sm': size === null,
|
||||||
|
}"
|
||||||
|
>{{ label }}<span v-show="required" class="text-red-800"> *</span></span
|
||||||
|
>
|
||||||
|
<textarea
|
||||||
|
v-text="value"
|
||||||
|
@input="trigger"
|
||||||
|
:placeholder="placeholder"
|
||||||
|
class="h-full outline-none bg-gray-700 border-gray-600 border-solid"
|
||||||
|
:rows="rows"
|
||||||
|
:class="{
|
||||||
|
'rounded-lg text-sm border-2 p-2 text-gray-300': size === null,
|
||||||
|
'rounded-lg py-2 px-2 text-xs border-2 text-gray-300': size == 'sm',
|
||||||
|
}"
|
||||||
|
></textarea>
|
||||||
|
<div v-if="hint" v-tooltip="hint" class="absolute right-0 top-0 mr-2 mt-2">
|
||||||
|
<ui-sprite src="info-button" class="w-5 h-5 text-indigo-200"></ui-sprite>
|
||||||
</div>
|
</div>
|
||||||
</label>
|
</label>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script>
|
||||||
import useFieldSize from '../../composables/useFieldSize.js';
|
export default {
|
||||||
const emit = defineEmits(['update:modelValue']);
|
data: function () {
|
||||||
|
return {
|
||||||
const {labelClass, fieldClass, defaultFieldClass} = useFieldSize();
|
focus: false,
|
||||||
|
};
|
||||||
const props = defineProps({
|
|
||||||
required: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
},
|
},
|
||||||
size: {
|
props: {
|
||||||
default: null,
|
required: {
|
||||||
},
|
type: Boolean,
|
||||||
rows: {
|
default: false,
|
||||||
default: function () {
|
},
|
||||||
return 4;
|
inset: {
|
||||||
|
default: false,
|
||||||
|
type: Boolean,
|
||||||
|
},
|
||||||
|
size: {
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
|
rows: {
|
||||||
|
default: function () {
|
||||||
|
return 4;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
id: {
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
hint: {
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
|
value: {
|
||||||
|
default: undefined,
|
||||||
|
},
|
||||||
|
mask: {
|
||||||
|
default: undefined,
|
||||||
|
},
|
||||||
|
label: {
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
type: {
|
||||||
|
required: false,
|
||||||
|
default: function () {
|
||||||
|
return 'text';
|
||||||
|
},
|
||||||
|
},
|
||||||
|
placeholder: {
|
||||||
|
default: '',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
id: {
|
methods: {
|
||||||
required: true,
|
trigger(v) {
|
||||||
|
this.$emit('input', v.target.value);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
hint: {
|
created() {
|
||||||
default: null,
|
if (typeof this.value === 'undefined') {
|
||||||
|
this.$emit('input', '');
|
||||||
|
}
|
||||||
},
|
},
|
||||||
modelValue: {
|
};
|
||||||
default: undefined,
|
|
||||||
},
|
|
||||||
label: {
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
placeholder: {
|
|
||||||
default: '',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
function trigger(v) {
|
|
||||||
emit('update:modelValue', v.target.value);
|
|
||||||
}
|
|
||||||
if (typeof props.modelValue === 'undefined') {
|
|
||||||
emit('update:modelValue', '');
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style scope>
|
||||||
|
.inset-bg {
|
||||||
|
background: linear-gradient(to bottom, hsl(247.5, 66.7%, 97.6%) 0%, hsl(247.5, 66.7%, 97.6%) 41%, hsl(0deg 0% 100%) 41%, hsl(180deg 0% 100%) 100%);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
|
@ -1,23 +0,0 @@
|
||||||
<template>
|
|
||||||
<div class="p-6 bg-gray-700 border-r border-gray-600 flex-none w-maxc flex flex-col justify-between">
|
|
||||||
<div class="grid gap-1">
|
|
||||||
<a v-for="(item, index) in entries" :key="index" href="#" class="rounded py-1 px-3 text-gray-400"
|
|
||||||
:class="index === modelValue ? `bg-gray-600` : ''" @click.prevent="openMenu(index)" v-text="item.title"></a>
|
|
||||||
</div>
|
|
||||||
<slot name="bottom"></slot>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
props: {
|
|
||||||
modelValue: {},
|
|
||||||
entries: {},
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
openMenu(index) {
|
|
||||||
this.$emit('update:modelValue', index);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
</script>
|
|
|
@ -1,9 +1,6 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="fixed z-40 top-0 left-0 w-full h-full flex items-center justify-center p-6">
|
<div class="fixed z-40 top-0 left-0 w-full h-full flex items-center justify-center p-6">
|
||||||
<div
|
<div class="relative rounded-lg p-8 bg-zinc-800 shadow-2xl shadow-black border border-zinc-700 border-solid w-full max-h-full overflow-auto" :class="full ? 'h-full' : innerWidth">
|
||||||
class="relative rounded-lg p-8 bg-zinc-800 shadow-2xl shadow-black border border-zinc-700 border-solid w-full max-h-full flex flex-col overflow-auto"
|
|
||||||
:class="full ? 'h-full' : innerWidth"
|
|
||||||
>
|
|
||||||
<div class="absolute top-0 right-0 mt-6 mr-6 flex space-x-6">
|
<div class="absolute top-0 right-0 mt-6 mr-6 flex space-x-6">
|
||||||
<slot name="actions"></slot>
|
<slot name="actions"></slot>
|
||||||
<a href="#" @click.prevent="$emit('close')">
|
<a href="#" @click.prevent="$emit('close')">
|
||||||
|
@ -11,18 +8,8 @@
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<h3 v-if="heading" class="font-semibold text-primary-200 text-xl" v-html="heading"></h3>
|
<h3 v-if="heading" class="font-semibold text-primary-200 text-xl" v-html="heading"></h3>
|
||||||
<div class="text-primary-100 group is-popup grow flex flex-col">
|
<div class="text-primary-100 group is-popup">
|
||||||
<suspense>
|
<slot></slot>
|
||||||
<div>
|
|
||||||
<slot></slot>
|
|
||||||
</div>
|
|
||||||
<template #fallback>
|
|
||||||
<div class="flex flex-col items-center justify-center h-full">
|
|
||||||
<ui-spinner class="border-primary-400 w-32 h-32"></ui-spinner>
|
|
||||||
<div class="text-3xl mt-10">Lade …</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</suspense>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue