diff --git a/app/Contribution/ContributionFactory.php b/app/Contribution/ContributionFactory.php index 8e510389..e9dd6dfd 100644 --- a/app/Contribution/ContributionFactory.php +++ b/app/Contribution/ContributionFactory.php @@ -34,8 +34,8 @@ class ContributionFactory public function compilerSelect(): Collection { return collect($this->documents)->map(fn ($document) => [ - 'title' => $document::buttonName(), - 'class' => $document, + 'name' => $document::getName(), + 'id' => $document, ]); } diff --git a/app/Contribution/Documents/ContributionDocument.php b/app/Contribution/Documents/ContributionDocument.php index 310ea5c5..b2ce95a3 100644 --- a/app/Contribution/Documents/ContributionDocument.php +++ b/app/Contribution/Documents/ContributionDocument.php @@ -37,11 +37,6 @@ abstract class ContributionDocument extends Document ]; } - public static function buttonName(): string - { - return 'Für ' . static::getName() . ' erstellen';; - } - public function setEventName(string $eventName): void { $this->eventName = $eventName; diff --git a/app/Contribution/Requests/GenerateRequest.php b/app/Contribution/Requests/GenerateRequest.php index 54a72b73..dccce166 100644 --- a/app/Contribution/Requests/GenerateRequest.php +++ b/app/Contribution/Requests/GenerateRequest.php @@ -16,7 +16,7 @@ class GenerateRequest extends ActionRequest implements HasContributionData { /** * @return array */ - protected function payloada(): array + protected function payload(): array { return json_decode(rawurldecode(base64_decode($this->input('payload', ''))), true); } diff --git a/app/Form/Actions/GenerateContributionAction.php b/app/Form/Actions/GenerateContributionAction.php new file mode 100644 index 00000000..2f23c7d6 --- /dev/null +++ b/app/Form/Actions/GenerateContributionAction.php @@ -0,0 +1,45 @@ +type()::fromPayload($request)); + } + + public function asController(ActionRequest $request, Form $form): BaseCompiler|JsonResponse + { + $r = FormCompileRequest::from(['form' => $form]); + app(ContributionFactory::class)->validateType($r); + $r->validateContribution(); + + return $request->input('validate') + ? response()->json([]) + : $this->handle($r); + } + + /** + * @return array + */ + public function rules(): array + { + return [ + 'payload' => [new JsonBase64Rule()], + ]; + } +} diff --git a/app/Form/Data/FieldCollection.php b/app/Form/Data/FieldCollection.php index f1d0348b..0f989074 100644 --- a/app/Form/Data/FieldCollection.php +++ b/app/Form/Data/FieldCollection.php @@ -114,7 +114,12 @@ class FieldCollection extends Collection return $this->map(fn ($field) => $field->presentRaw())->toArray(); } - private function findBySpecialType(SpecialType $specialType): ?Field + public function hasSpecialType(SpecialType $specialType): bool + { + return $this->findBySpecialType($specialType) !== null; + } + + public function findBySpecialType(SpecialType $specialType): ?Field { return $this->first(fn ($field) => $field->specialType === $specialType); } diff --git a/app/Form/Requests/FormCompileRequest.php b/app/Form/Requests/FormCompileRequest.php new file mode 100644 index 00000000..e5b20d7b --- /dev/null +++ b/app/Form/Requests/FormCompileRequest.php @@ -0,0 +1,103 @@ + + */ + public function type(): string + { + $payload = json_decode(rawurldecode(base64_decode(request()->input('payload', ''))), true); + + return $payload['type']; + } + + public function dateFrom(): Carbon + { + return $this->form->from; + } + + public function dateUntil(): Carbon + { + return $this->form->to; + } + + public function zipLocation(): string + { + return $this->form->zip.' '.$this->form->location; + } + + public function eventName(): string + { + return $this->form->name; + } + + public function members(): Collection + { + $members = []; + $fields = [ + [SpecialType::FIRSTNAME, 'firstname'], + [SpecialType::LASTNAME, 'lastname'], + [SpecialType::BIRTHDAY, 'birthday'], + [SpecialType::ADDRESS, 'address'], + [SpecialType::ZIP, 'zip'], + [SpecialType::LOCATION, 'location'], + [SpecialType::GENDER, 'gender'] + ]; + + foreach ($this->form->participants as $participant) { + $member = []; + foreach ($fields as [$type, $name]) { + $f = $this->form->getFields()->findBySpecialType($type); + $member[$name] = $participant->getFields()->find($f)->value; + } + + $members[] = [ + 'is_leader' => false, + 'gender' => 'weiblich', + ...$member, + ]; + } + + return MemberData::fromApi($members); + } + + public function country(): ?Country + { + return Country::first(); + } + + public function validateContribution(): void + { + Validator::make($this->form->toArray(), [ + 'zip' => 'required', + 'location' => 'required' + ]) + ->after(function($validator) { + foreach ($this->type()::requiredFormSpecialTypes() as $type) { + if (!$this->form->getFields()->hasSpecialType($type)) { + $validator->errors()->add($type->name, 'Kein Feld für ' . $type->value . ' vorhanden.'); + } + } + if ($this->form->participants->count() === 0) { + $validator->errors()->add('participants', 'Veranstaltung besitzt noch keine Teilnehmer*innen.'); + } + }) + ->validate(); + } +} diff --git a/app/Form/Resources/FormResource.php b/app/Form/Resources/FormResource.php index fcafe9d5..282a10f0 100644 --- a/app/Form/Resources/FormResource.php +++ b/app/Form/Resources/FormResource.php @@ -15,6 +15,7 @@ use App\Group; use App\Lib\Editor\EditorData; use App\Lib\HasMeta; use Illuminate\Http\Resources\Json\JsonResource; +use App\Contribution\ContributionFactory; /** * @mixin Form @@ -66,6 +67,7 @@ class FormResource extends JsonResource 'frontend' => str(app(FormSettings::class)->registerUrl)->replace('{slug}', $this->slug), 'export' => route('form.export', $this->getModel()), 'copy' => route('form.copy', $this->getModel()), + 'contribution' => route('form.contribution', $this->getModel()), ] ]; } @@ -88,6 +90,7 @@ class FormResource extends JsonResource 'namiTypes' => NamiType::forSelect(), 'specialTypes' => SpecialType::forSelect(), 'countries' => Country::forSelect(), + 'contribution_types' => app(ContributionFactory::class)->compilerSelect(), 'default' => [ 'description' => [], 'is_active' => true, diff --git a/resources/js/components/page/Popups.vue b/resources/js/components/page/Popups.vue index b8d014ae..c771b3bf 100644 --- a/resources/js/components/page/Popups.vue +++ b/resources/js/components/page/Popups.vue @@ -1,11 +1,15 @@ diff --git a/resources/js/stores/swalStore.ts b/resources/js/stores/swalStore.ts index 397a3b6a..09cf9aec 100644 --- a/resources/js/stores/swalStore.ts +++ b/resources/js/stores/swalStore.ts @@ -1,6 +1,7 @@ import { defineStore } from 'pinia'; import { v4 as uuidv4 } from 'uuid'; +type Payload = Record; interface Popup { id: string; @@ -11,6 +12,16 @@ interface Popup { cancelButton: string; resolve: (id: string) => void; reject: (id: string) => void; + fields: SwalField[]; + payload: Payload; +} + +interface SwalField { + name: string; + label: string; + required: boolean; + type: 'select' | 'text'; + options: [], } export default defineStore('swal', { @@ -30,6 +41,8 @@ export default defineStore('swal', { reject, id: uuidv4(), icon: 'warning-triangle-light', + fields: [], + payload: {}, }); }).then((id) => { this.remove(id); @@ -41,8 +54,40 @@ export default defineStore('swal', { }); }, + ask(title: string, body: string, fields: SwalField[] = []): Promise { + return new Promise((resolve, reject) => { + new Promise((resolve, reject) => { + const payload: Payload = {}; + fields.forEach(f => payload[f.name] = null); + this.popups.push({ + title, + body, + confirmButton: 'Okay', + cancelButton: 'Abbrechen', + resolve, + reject, + id: uuidv4(), + icon: 'warning-triangle-light', + fields: fields, + payload: payload, + }); + }).then((id) => { + const p = this.find(id)?.payload; + this.remove(id); + resolve(p || {}); + }).catch((id) => { + this.remove(id); + reject(); + }); + }); + }, + remove(id: string) { this.popups = this.popups.filter(p => p.id !== id); + }, + + find(id: string): Popup|undefined { + return this.popups.find(p => p.id === id); } }, }); diff --git a/resources/js/views/contribution/VIndex.vue b/resources/js/views/contribution/VIndex.vue index 65ad713f..adfba2b1 100644 --- a/resources/js/views/contribution/VIndex.vue +++ b/resources/js/views/contribution/VIndex.vue @@ -25,7 +25,7 @@ -