Compare commits
	
		
			7 Commits
		
	
	
		
			94f91cbda7
			...
			4d46de2763
		
	
	| Author | SHA1 | Date | 
|---|---|---|
|  | 4d46de2763 | |
|  | edf836235b | |
|  | 67e50709c0 | |
|  | 4ba13a6c42 | |
|  | a8bdc2c4b2 | |
|  | aa7cdb3fa3 | |
|  | c6c01a3e24 | 
|  | @ -13,7 +13,7 @@ class PreventionRememberAction | ||||||
| { | { | ||||||
|     use AsAction; |     use AsAction; | ||||||
| 
 | 
 | ||||||
|     public string $commandSignature = 'prevention:remember'; |     public string $commandSignature = 'prevention:remember-forms'; | ||||||
| 
 | 
 | ||||||
|     public function handle(): void |     public function handle(): void | ||||||
|     { |     { | ||||||
|  | @ -33,7 +33,7 @@ class PreventionRememberAction | ||||||
|                 continue; |                 continue; | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             if ($participant->getFields()->getMailRecipient() === null || count($participant->preventions()) === 0) { |             if ($participant->getFields()->getMailRecipient() === null || $participant->preventions()->count() === 0) { | ||||||
|                 continue; |                 continue; | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -14,6 +14,7 @@ use Illuminate\Database\Eloquent\Factories\HasFactory; | ||||||
| use Illuminate\Database\Eloquent\Model; | use Illuminate\Database\Eloquent\Model; | ||||||
| use Illuminate\Database\Eloquent\Relations\BelongsTo; | use Illuminate\Database\Eloquent\Relations\BelongsTo; | ||||||
| use Illuminate\Database\Eloquent\Relations\HasMany; | use Illuminate\Database\Eloquent\Relations\HasMany; | ||||||
|  | use Illuminate\Support\Collection; | ||||||
| use Illuminate\Support\Facades\Mail; | use Illuminate\Support\Facades\Mail; | ||||||
| use Laravel\Scout\Searchable; | use Laravel\Scout\Searchable; | ||||||
| use stdClass; | use stdClass; | ||||||
|  | @ -81,20 +82,15 @@ class Participant extends Model implements Preventable | ||||||
|         Mail::to($this->getMailRecipient())->queue(new ConfirmRegistrationMail($this)); |         Mail::to($this->getMailRecipient())->queue(new ConfirmRegistrationMail($this)); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     public function preventableLayout(): string |  | ||||||
|     { |  | ||||||
|         return 'mail.prevention.prevention-remember-participant'; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /** |     /** | ||||||
|      * @inheritdoc |      * @inheritdoc | ||||||
|      */ |      */ | ||||||
|     public function preventions(): array |     public function preventions(): Collection | ||||||
|     { |     { | ||||||
|         return $this->member?->preventions($this->form->from) ?: []; |         return $this->member?->preventions($this->form->from) ?: collect([]); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     public function getMailRecipient(): stdClass |     public function getMailRecipient(): ?stdClass | ||||||
|     { |     { | ||||||
|         return $this->getFields()->getMailRecipient(); |         return $this->getFields()->getMailRecipient(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  | @ -2,7 +2,6 @@ | ||||||
| 
 | 
 | ||||||
| namespace App\Member; | namespace App\Member; | ||||||
| 
 | 
 | ||||||
| use stdClass; |  | ||||||
| use App\Confession; | use App\Confession; | ||||||
| use App\Country; | use App\Country; | ||||||
| use App\Course\Models\CourseMember; | use App\Course\Models\CourseMember; | ||||||
|  | @ -14,7 +13,8 @@ use App\Nami\HasNamiField; | ||||||
| use App\Nationality; | use App\Nationality; | ||||||
| use App\Payment\Subscription; | use App\Payment\Subscription; | ||||||
| use App\Pdf\Sender; | use App\Pdf\Sender; | ||||||
| use App\Prevention\Contracts\YearlyPreventable; | use App\Prevention\Contracts\Preventable; | ||||||
|  | use App\Prevention\Data\PreventionData; | ||||||
| use App\Region; | use App\Region; | ||||||
| use App\Setting\NamiSettings; | use App\Setting\NamiSettings; | ||||||
| use Carbon\Carbon; | use Carbon\Carbon; | ||||||
|  | @ -37,12 +37,14 @@ use Zoomyboy\Phone\HasPhoneNumbers; | ||||||
| use App\Prevention\Enums\Prevention; | use App\Prevention\Enums\Prevention; | ||||||
| use Database\Factories\Member\MemberFactory; | use Database\Factories\Member\MemberFactory; | ||||||
| use Illuminate\Database\Eloquent\Relations\HasOne; | use Illuminate\Database\Eloquent\Relations\HasOne; | ||||||
|  | use Illuminate\Support\Collection; | ||||||
|  | use stdClass; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * @property string $subscription_name |  * @property string $subscription_name | ||||||
|  * @property int    $pending_payment |  * @property int    $pending_payment | ||||||
|  */ |  */ | ||||||
| class Member extends Model implements Geolocatable, YearlyPreventable | class Member extends Model implements Geolocatable, Preventable | ||||||
| { | { | ||||||
|     use Notifiable; |     use Notifiable; | ||||||
|     use HasNamiField; |     use HasNamiField; | ||||||
|  | @ -196,6 +198,20 @@ class Member extends Model implements Geolocatable, YearlyPreventable | ||||||
|         return (int) $this->invoicePositions()->whereHas('invoice', fn($query) => $query->whereNeedsPayment())->sum('price'); |         return (int) $this->invoicePositions()->whereHas('invoice', fn($query) => $query->whereNeedsPayment())->sum('price'); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     public function getMailRecipient(): ?stdClass | ||||||
|  |     { | ||||||
|  |         if (!$this->fullname) { | ||||||
|  |             return null; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         return (object) ['name' => $this->fullname, 'email' => $this->email]; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public function preventableSubject(): string | ||||||
|  |     { | ||||||
|  |         return 'Nachweise erforderlich'; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     // ---------------------------------- Relations ----------------------------------
 |     // ---------------------------------- Relations ----------------------------------
 | ||||||
|     /** |     /** | ||||||
|      * @return BelongsTo<Country, $this> |      * @return BelongsTo<Country, $this> | ||||||
|  | @ -366,32 +382,47 @@ class Member extends Model implements Geolocatable, YearlyPreventable | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * @return array<int, Prevention> |      * @inheritdoc | ||||||
|      */ |      */ | ||||||
|     public function preventions(?Carbon $date = null): array |     public function preventions(?Carbon $date = null): Collection | ||||||
|     { |     { | ||||||
|         $date = $date ?: now(); |         $date = $date ?: now(); | ||||||
| 
 | 
 | ||||||
|         /** @var array<int, Prevention> */ |         /** @var Collection<int, PreventionData> */ | ||||||
|         $preventions = []; |         $preventions = collect([]); | ||||||
| 
 | 
 | ||||||
|         if ($this->efz === null || $this->efz->diffInYears($date) >= 5) { |         if ($this->efz === null || $this->efz->diffInYears($date) >= 5) { | ||||||
|             $preventions[] = Prevention::EFZ; |             $preventions->push(PreventionData::from([ | ||||||
|  |                 'type' => Prevention::EFZ, | ||||||
|  |                 'expires' => $this->efz === null ? now() : $this->efz->addYears(5) | ||||||
|  |             ])); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         if (!$this->has_vk) { |         if (!$this->has_vk) { | ||||||
|             $preventions[] = Prevention::VK; |             $preventions->push(PreventionData::from([ | ||||||
|  |                 'type' => Prevention::VK, | ||||||
|  |                 'expires' => now(), | ||||||
|  |             ])); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         if ($this->more_ps_at === null) { |         if ($this->more_ps_at === null) { | ||||||
|             if ($this->ps_at === null) { |             if ($this->ps_at === null) { | ||||||
|                 $preventions[] = Prevention::PS; |                 $preventions->push(PreventionData::from([ | ||||||
|  |                     'type' => Prevention::PS, | ||||||
|  |                     'expires' => now(), | ||||||
|  |                 ])); | ||||||
|             } else if ($this->ps_at->diffInYears($date) >= 5) { |             } else if ($this->ps_at->diffInYears($date) >= 5) { | ||||||
|                 $preventions[] = Prevention::MOREPS; |                 $preventions->push(PreventionData::from([ | ||||||
|  |                     'type' => Prevention::MOREPS, | ||||||
|  |                     'expires' => $this->ps_at->addYears(5), | ||||||
|  |                 ])); | ||||||
|             } |             } | ||||||
|         } else { |         } else { | ||||||
|             if ($this->more_ps_at === null || $this->more_ps_at->diffInYears($date) >= 5) { |             if ($this->more_ps_at === null || $this->more_ps_at->diffInYears($date) >= 5) { | ||||||
|                 $preventions[] = Prevention::MOREPS; |                 $preventions->push(PreventionData::from([ | ||||||
|  |                     'type' => Prevention::MOREPS, | ||||||
|  |                     'expires' => $this->more_ps_at->addYears(5), | ||||||
|  |                 ])); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  | @ -572,21 +603,4 @@ class Member extends Model implements Geolocatable, YearlyPreventable | ||||||
|                 ->map(fn($membership) => [...$membership->only('activity_id', 'subactivity_id'), 'both' => $membership->activity_id . '|' . $membership->subactivity_id, 'with_group' => $membership->group_id . '|' . $membership->activity_id . '|' . $membership->subactivity_id]), |                 ->map(fn($membership) => [...$membership->only('activity_id', 'subactivity_id'), 'both' => $membership->activity_id . '|' . $membership->subactivity_id, 'with_group' => $membership->group_id . '|' . $membership->activity_id . '|' . $membership->subactivity_id]), | ||||||
|         ]; |         ]; | ||||||
|     } |     } | ||||||
| 
 |  | ||||||
|     // -------------------------------- Prevention ---------------------------------
 |  | ||||||
|     // *****************************************************************************
 |  | ||||||
|     public function preventableSubject(): string |  | ||||||
|     { |  | ||||||
|         return 'Nachweise erforderlich'; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public function preventableLayout(): string |  | ||||||
|     { |  | ||||||
|         return 'mail.prevention.prevention-remember-participant'; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     public function getMailRecipient(): stdClass |  | ||||||
|     { |  | ||||||
|         return (object) ['name' => $this->fullname, 'email' => $this->email]; |  | ||||||
|     } |  | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -0,0 +1,55 @@ | ||||||
|  | <?php | ||||||
|  | 
 | ||||||
|  | namespace App\Prevention\Actions; | ||||||
|  | 
 | ||||||
|  | use App\Member\Member; | ||||||
|  | use App\Prevention\Data\PreventionData; | ||||||
|  | use App\Prevention\Mails\YearlyMail; | ||||||
|  | use App\Prevention\PreventionSettings; | ||||||
|  | use Illuminate\Support\Collection; | ||||||
|  | use Illuminate\Support\Facades\Mail; | ||||||
|  | use Lorisleiva\Actions\Concerns\AsAction; | ||||||
|  | 
 | ||||||
|  | class YearlyRememberAction | ||||||
|  | { | ||||||
|  |     use AsAction; | ||||||
|  | 
 | ||||||
|  |     public string $commandSignature = 'prevention:remember-yearly'; | ||||||
|  | 
 | ||||||
|  |     public function handle(): void | ||||||
|  |     { | ||||||
|  |         $settings = app(PreventionSettings::class); | ||||||
|  |         $expireDate = now()->addWeeks($settings->weeks); | ||||||
|  | 
 | ||||||
|  |         foreach (Member::get() as $member) { | ||||||
|  |             $noticePreventions = $member->preventions($expireDate) | ||||||
|  |                 ->filter(fn($prevention) => $prevention->expiresAt($expireDate)); | ||||||
|  | 
 | ||||||
|  |             if ($noticePreventions->count() === 0) { | ||||||
|  |                 continue; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             Mail::send($this->createMail($member, $noticePreventions)); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         foreach (Member::get() as $member) { | ||||||
|  |             $preventions = $member->preventions() | ||||||
|  |                 ->filter(fn($prevention) => $prevention->expiresAt(now())); | ||||||
|  | 
 | ||||||
|  |             if ($preventions->count() === 0) { | ||||||
|  |                 continue; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             Mail::send($this->createMail($member, $preventions)); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * @param Collection<int, PreventionData> $preventions | ||||||
|  |      */ | ||||||
|  |     protected function createMail(Member $member, Collection $preventions): YearlyMail | ||||||
|  |     { | ||||||
|  |         $body = app(PreventionSettings::class)->refresh()->formmail; | ||||||
|  |         return new YearlyMail($member, $body, $preventions); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | @ -2,19 +2,19 @@ | ||||||
| 
 | 
 | ||||||
| namespace App\Prevention\Contracts; | namespace App\Prevention\Contracts; | ||||||
| 
 | 
 | ||||||
| use App\Prevention\Enums\Prevention; | use App\Prevention\Data\PreventionData; | ||||||
|  | use Illuminate\Support\Collection; | ||||||
| use stdClass; | use stdClass; | ||||||
| 
 | 
 | ||||||
| interface Preventable | interface Preventable | ||||||
| { | { | ||||||
| 
 | 
 | ||||||
|     public function preventableLayout(): string; |  | ||||||
|     public function preventableSubject(): string; |     public function preventableSubject(): string; | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * @return array<int, Prevention> |      * @return Collection<int, PreventionData> | ||||||
|      */ |      */ | ||||||
|     public function preventions(): array; |     public function preventions(): Collection; | ||||||
| 
 | 
 | ||||||
|     public function getMailRecipient(): stdClass; |     public function getMailRecipient(): ?stdClass; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -1,20 +0,0 @@ | ||||||
| <?php |  | ||||||
| 
 |  | ||||||
| namespace App\Prevention\Contracts; |  | ||||||
| 
 |  | ||||||
| use App\Prevention\Enums\Prevention; |  | ||||||
| use stdClass; |  | ||||||
| 
 |  | ||||||
| interface YearlyPreventable |  | ||||||
| { |  | ||||||
| 
 |  | ||||||
|     public function preventableLayout(): string; |  | ||||||
|     public function preventableSubject(): string; |  | ||||||
| 
 |  | ||||||
|     /** |  | ||||||
|      * @return array<int, Prevention> |  | ||||||
|      */ |  | ||||||
|     public function preventions(): array; |  | ||||||
| 
 |  | ||||||
|     public function getMailRecipient(): stdClass; |  | ||||||
| } |  | ||||||
|  | @ -0,0 +1,17 @@ | ||||||
|  | <?php | ||||||
|  | 
 | ||||||
|  | namespace App\Prevention\Data; | ||||||
|  | 
 | ||||||
|  | use App\Prevention\Enums\Prevention; | ||||||
|  | use Carbon\Carbon; | ||||||
|  | use Spatie\LaravelData\Data; | ||||||
|  | 
 | ||||||
|  | class PreventionData extends Data | ||||||
|  | { | ||||||
|  |     public function __construct(public Prevention $type, public Carbon $expires) {} | ||||||
|  | 
 | ||||||
|  |     public function expiresAt(Carbon $date): bool | ||||||
|  |     { | ||||||
|  |         return $this->expires->isSameDay($date); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | @ -2,8 +2,6 @@ | ||||||
| 
 | 
 | ||||||
| namespace App\Prevention\Enums; | namespace App\Prevention\Enums; | ||||||
| 
 | 
 | ||||||
| use App\Member\Member; |  | ||||||
| use Carbon\Carbon; |  | ||||||
| use Illuminate\Support\Collection; | use Illuminate\Support\Collection; | ||||||
| 
 | 
 | ||||||
| enum Prevention | enum Prevention | ||||||
|  |  | ||||||
|  | @ -25,7 +25,7 @@ class PreventionRememberMail extends Mailable | ||||||
|     { |     { | ||||||
|         $this->settings = app(InvoiceSettings::class); |         $this->settings = app(InvoiceSettings::class); | ||||||
|         $this->bodyText = $this->bodyText |         $this->bodyText = $this->bodyText | ||||||
|             ->replaceWithList('wanted', collect($preventable->preventions())->map(fn ($prevention) => $prevention->text())->toArray()); |             ->replaceWithList('wanted', collect($preventable->preventions())->map(fn($prevention) => $prevention->type->text())->toArray()); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|  | @ -48,7 +48,7 @@ class PreventionRememberMail extends Mailable | ||||||
|     public function content() |     public function content() | ||||||
|     { |     { | ||||||
|         return new Content( |         return new Content( | ||||||
|             markdown: $this->preventable->preventableLayout(), |             markdown: 'mail.prevention.prevention-remember-participant', | ||||||
|         ); |         ); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -5,13 +5,14 @@ namespace App\Prevention\Mails; | ||||||
| use App\Invoice\InvoiceSettings; | use App\Invoice\InvoiceSettings; | ||||||
| use App\Lib\Editor\EditorData; | use App\Lib\Editor\EditorData; | ||||||
| use App\Prevention\Contracts\Preventable; | use App\Prevention\Contracts\Preventable; | ||||||
| use App\Prevention\Contracts\YearlyPreventable; | use App\Prevention\Data\PreventionData; | ||||||
| use Illuminate\Bus\Queueable; | use Illuminate\Bus\Queueable; | ||||||
| use Illuminate\Mail\Attachment; | use Illuminate\Mail\Attachment; | ||||||
| use Illuminate\Mail\Mailable; | use Illuminate\Mail\Mailable; | ||||||
| use Illuminate\Mail\Mailables\Content; | use Illuminate\Mail\Mailables\Content; | ||||||
| use Illuminate\Mail\Mailables\Envelope; | use Illuminate\Mail\Mailables\Envelope; | ||||||
| use Illuminate\Queue\SerializesModels; | use Illuminate\Queue\SerializesModels; | ||||||
|  | use Illuminate\Support\Collection; | ||||||
| 
 | 
 | ||||||
| class YearlyMail extends Mailable | class YearlyMail extends Mailable | ||||||
| { | { | ||||||
|  | @ -21,12 +22,14 @@ class YearlyMail extends Mailable | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|      * Create a new message instance. |      * Create a new message instance. | ||||||
|  |      * | ||||||
|  |      * @param Collection<int, PreventionData> $preventions | ||||||
|      */ |      */ | ||||||
|     public function __construct(public YearlyPreventable $preventable, public EditorData $bodyText) |     public function __construct(public Preventable $preventable, public EditorData $bodyText, public Collection $preventions) | ||||||
|     { |     { | ||||||
|         $this->settings = app(InvoiceSettings::class); |         $this->settings = app(InvoiceSettings::class); | ||||||
|         $this->bodyText = $this->bodyText |         $this->bodyText = $this->bodyText | ||||||
|             ->replaceWithList('wanted', collect($preventable->preventions())->map(fn($prevention) => $prevention->text())->toArray()); |             ->replaceWithList('wanted', collect($preventions)->pluck('type')->map(fn($prevention) => $prevention->text())->toArray()); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|  | @ -49,7 +52,7 @@ class YearlyMail extends Mailable | ||||||
|     public function content() |     public function content() | ||||||
|     { |     { | ||||||
|         return new Content( |         return new Content( | ||||||
|             markdown: $this->preventable->preventableLayout(), |             markdown: 'mail.prevention.prevention-remember-participant', | ||||||
|         ); |         ); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -10,6 +10,7 @@ class PreventionSettings extends LocalSettings | ||||||
| 
 | 
 | ||||||
|     public EditorData $formmail; |     public EditorData $formmail; | ||||||
|     public EditorData $yearlymail; |     public EditorData $yearlymail; | ||||||
|  |     public int $weeks; | ||||||
| 
 | 
 | ||||||
|     public static function group(): string |     public static function group(): string | ||||||
|     { |     { | ||||||
|  |  | ||||||
|  | @ -7,5 +7,6 @@ return new class extends SettingsMigration | ||||||
|     public function up(): void |     public function up(): void | ||||||
|     { |     { | ||||||
|         $this->migrator->add('prevention.yearlymail', ['time' => 1, 'blocks' => [], 'version' => '1.0']); |         $this->migrator->add('prevention.yearlymail', ['time' => 1, 'blocks' => [], 'version' => '1.0']); | ||||||
|  |         $this->migrator->add('prevention.weeks', 8); | ||||||
|     } |     } | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | @ -91,8 +91,6 @@ services: | ||||||
|             - ./data/db:/var/lib/mysql |             - ./data/db:/var/lib/mysql | ||||||
| 
 | 
 | ||||||
|     socketi: |     socketi: | ||||||
|         ports: |  | ||||||
|             - '6001:6001' |  | ||||||
|         image: quay.io/soketi/soketi:89604f268623cf799573178a7ba56b7491416bde-16-debian |         image: quay.io/soketi/soketi:89604f268623cf799573178a7ba56b7491416bde-16-debian | ||||||
|         environment: |         environment: | ||||||
|             SOKETI_DEFAULT_APP_ID: adremaid |             SOKETI_DEFAULT_APP_ID: adremaid | ||||||
|  | @ -105,8 +103,6 @@ services: | ||||||
|             - ./data/redis:/data |             - ./data/redis:/data | ||||||
| 
 | 
 | ||||||
|     meilisearch: |     meilisearch: | ||||||
|         ports: |  | ||||||
|             - '7700:7700' |  | ||||||
|         image: getmeili/meilisearch:v1.6 |         image: getmeili/meilisearch:v1.6 | ||||||
|         volumes: |         volumes: | ||||||
|             - ./data/meilisearch:/meili_data |             - ./data/meilisearch:/meili_data | ||||||
|  |  | ||||||
|  | @ -1,19 +1,47 @@ | ||||||
| <template> | <template> | ||||||
|     <page-filter-sidebar v-if="visible === true" @close="visible = false"> |     <ui-popup v-if="visible === true" heading="Filtern" @close="visible = false"> | ||||||
|  |         <div class="grid gap-3 md:grid-cols-2"> | ||||||
|             <slot name="fields"></slot> |             <slot name="fields"></slot> | ||||||
|     </page-filter-sidebar> |         </div> | ||||||
|     <div class="px-6 py-2 border-b border-gray-600 items-center space-x-3"> |     </ui-popup> | ||||||
|  |     <div class="px-6 py-2 border-b border-gray-600" :class="visibleDesktopBlock"> | ||||||
|  |         <div class="flex items-end space-x-3"> | ||||||
|  |             <slot name="buttons"></slot> | ||||||
|  |             <ui-icon-button v-if="filterable" icon="filter" @click="filterVisible = !filterVisible">Filtern</ui-icon-button> | ||||||
|  |         </div> | ||||||
|  |         <ui-box v-if="filterVisible" class="mt-3"> | ||||||
|  |             <div class="grid grid-cols-4 gap-3 items-end"> | ||||||
|  |                 <slot name="fields"></slot> | ||||||
|  |                 <ui-icon-button class="col-start-1" icon="close" @click="filterVisible = false">Schließen</ui-icon-button> | ||||||
|  |             </div> | ||||||
|  |         </ui-box> | ||||||
|  |     </div> | ||||||
|  |     <div class="px-6 py-2 border-b border-gray-600 items-center space-x-3" :class="visibleMobile"> | ||||||
|         <div class="flex flex-col sm:flex-row items-stretch sm:items-end space-y-1 sm:space-y-0 sm:space-x-3"> |         <div class="flex flex-col sm:flex-row items-stretch sm:items-end space-y-1 sm:space-y-0 sm:space-x-3"> | ||||||
|             <slot name="buttons"></slot> |             <slot name="buttons"></slot> | ||||||
|             <ui-icon-button v-if="!!$slots.fields" icon="filter" @click="visible = true">Filtern</ui-icon-button> |             <ui-icon-button v-if="filterable" icon="filter" @click="visible = true">Filtern</ui-icon-button> | ||||||
|         </div> |         </div> | ||||||
|     </div> |     </div> | ||||||
| </template> | </template> | ||||||
| 
 | 
 | ||||||
| <script setup> | <script setup> | ||||||
| import {ref} from 'vue'; | import {defineProps, ref} from 'vue'; | ||||||
| 
 | import useBreakpoints from '../../composables/useBreakpoints.js'; | ||||||
| defineEmits(['close']); |  | ||||||
| 
 | 
 | ||||||
| const visible = ref(false); | const visible = ref(false); | ||||||
|  | 
 | ||||||
|  | const filterVisible = ref(false); | ||||||
|  | 
 | ||||||
|  | const props = defineProps({ | ||||||
|  |     breakpoint: { | ||||||
|  |         type: String, | ||||||
|  |         required: true, | ||||||
|  |     }, | ||||||
|  |     filterable: { | ||||||
|  |         type: Boolean, | ||||||
|  |         default: () => true, | ||||||
|  |     }, | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | const {visibleDesktopBlock, visibleMobile} = useBreakpoints(props); | ||||||
| </script> | </script> | ||||||
|  |  | ||||||
|  | @ -1,16 +0,0 @@ | ||||||
| <template> |  | ||||||
|     <ui-sidebar :max="0" @close="$emit('close')"> |  | ||||||
|         <page-header title="Filtern" @close="$emit('close')"></page-header> |  | ||||||
|         <div class="grid gap-6 p-6"> |  | ||||||
|             <slot></slot> |  | ||||||
|         </div> |  | ||||||
|     </ui-sidebar> |  | ||||||
| </template> |  | ||||||
| 
 |  | ||||||
| <script setup> |  | ||||||
| import {ref} from 'vue'; |  | ||||||
| 
 |  | ||||||
| const visible = ref(false); |  | ||||||
| 
 |  | ||||||
| defineEmits(['close']); |  | ||||||
| </script> |  | ||||||
|  | @ -1,5 +1,6 @@ | ||||||
| <template> | <template> | ||||||
|     <div class="fixed shadow-2xl bg-gray-600 right-0 top-0 h-full flex flex-col group is-bright" :class="widths[max]"> |     <div | ||||||
|  |         class="fixed w-full w-[80vw] max-w-[40rem] shadow-2xl bg-gray-600 right-0 top-0 h-full flex flex-col group is-bright"> | ||||||
|         <suspense> |         <suspense> | ||||||
|             <slot></slot> |             <slot></slot> | ||||||
| 
 | 
 | ||||||
|  | @ -17,20 +18,4 @@ | ||||||
| 
 | 
 | ||||||
| <script setup> | <script setup> | ||||||
| defineEmits(['close']); | defineEmits(['close']); | ||||||
| 
 |  | ||||||
| const widths = { |  | ||||||
|     40: 'w-full w-[80vw] max-w-[40rem]', |  | ||||||
|     30: 'w-full w-[80vw] max-w-[30rem]', |  | ||||||
|     20: 'w-full w-[80vw] max-w-[20rem]', |  | ||||||
|     10: 'w-full w-[80vw] max-w-[10rem]', |  | ||||||
|     0: '', |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| defineProps({ |  | ||||||
|     max: { |  | ||||||
|         default: () => 40, |  | ||||||
|         type: Number, |  | ||||||
|         required: false, |  | ||||||
|     }, |  | ||||||
| }); |  | ||||||
| </script> | </script> | ||||||
|  |  | ||||||
|  | @ -1,15 +1,9 @@ | ||||||
| <template> | <template> | ||||||
|     <div class="flex-none w-maxc flex flex-col justify-between border-b-2 border-gray-500 group-[.is-popup]:border-zinc-500 mb-3"> |     <div class="flex-none w-maxc flex flex-col justify-between border-b-2 group-[.is-popup]:border-zinc-500 mb-3"> | ||||||
|         <div class="flex space-x-1 px-2"> |         <div class="flex space-x-1 px-2"> | ||||||
|             <a |             <a v-for="(item, index) in entries" :key="index" href="#" class="rounded-t-lg py-1 px-3 text-zinc-300" | ||||||
|                 v-for="(item, index) in entries" |                 :class="index === modelValue ? `group-[.is-popup]:bg-zinc-600` : ''" @click.prevent="openMenu(index)" | ||||||
|                 :key="index" |                 v-text="item.title"></a> | ||||||
|                 href="#" |  | ||||||
|                 class="rounded-t-lg py-1 px-3 text-zinc-300" |  | ||||||
|                 :class="index === modelValue ? `bg-gray-700 group-[.is-popup]:bg-zinc-600` : ''" |  | ||||||
|                 @click.prevent="openMenu(index)" |  | ||||||
|                 v-text="item.title" |  | ||||||
|             ></a> |  | ||||||
|         </div> |         </div> | ||||||
|     </div> |     </div> | ||||||
| </template> | </template> | ||||||
|  |  | ||||||
|  | @ -0,0 +1,46 @@ | ||||||
|  | import {computed} from 'vue'; | ||||||
|  | 
 | ||||||
|  | export default function (props) { | ||||||
|  |     const visibleMobile = computed(() => { | ||||||
|  |         return { | ||||||
|  |             sm: 'flex sm:hidden', | ||||||
|  |             md: 'flex md:hidden', | ||||||
|  |             lg: 'flex lg:hidden', | ||||||
|  |             xl: 'flex xl:hidden', | ||||||
|  |         }[props.breakpoint]; | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     const visibleDesktop = computed(() => { | ||||||
|  |         return { | ||||||
|  |             sm: 'hidden sm:flex', | ||||||
|  |             md: 'hidden md:flex', | ||||||
|  |             lg: 'hidden lg:flex', | ||||||
|  |             xl: 'hidden xl:flex', | ||||||
|  |         }[props.breakpoint]; | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     const visibleMobileBlock = computed(() => { | ||||||
|  |         return { | ||||||
|  |             sm: 'block sm:hidden', | ||||||
|  |             md: 'block md:hidden', | ||||||
|  |             lg: 'block lg:hidden', | ||||||
|  |             xl: 'block xl:hidden', | ||||||
|  |         }[props.breakpoint]; | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     const visibleDesktopBlock = computed(() => { | ||||||
|  |         return { | ||||||
|  |             sm: 'hidden sm:block', | ||||||
|  |             md: 'hidden md:block', | ||||||
|  |             lg: 'hidden lg:block', | ||||||
|  |             xl: 'hidden xl:block', | ||||||
|  |         }[props.breakpoint]; | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     return { | ||||||
|  |         visibleMobile, | ||||||
|  |         visibleDesktop, | ||||||
|  |         visibleDesktopBlock, | ||||||
|  |         visibleMobileBlock, | ||||||
|  |     }; | ||||||
|  | } | ||||||
|  | @ -144,7 +144,7 @@ | ||||||
|             <conditions-form id="filesettings" :single="single" :value="fileSettingPopup.properties.conditions" @save="saveFileConditions"> </conditions-form> |             <conditions-form id="filesettings" :single="single" :value="fileSettingPopup.properties.conditions" @save="saveFileConditions"> </conditions-form> | ||||||
|         </ui-popup> |         </ui-popup> | ||||||
| 
 | 
 | ||||||
|         <page-filter> |         <page-filter breakpoint="xl" :filterable="false"> | ||||||
|             <template #buttons> |             <template #buttons> | ||||||
|                 <f-text id="search" :model-value="getFilter('search')" label="Suchen …" size="sm" @update:model-value="setFilter('search', $event)"></f-text> |                 <f-text id="search" :model-value="getFilter('search')" label="Suchen …" size="sm" @update:model-value="setFilter('search', $event)"></f-text> | ||||||
|                 <f-switch id="past" :model-value="getFilter('past')" label="vergangene zeigen" name="past" size="sm" @update:model-value="setFilter('past', $event)"></f-switch> |                 <f-switch id="past" :model-value="getFilter('past')" label="vergangene zeigen" name="past" size="sm" @update:model-value="setFilter('past', $event)"></f-switch> | ||||||
|  |  | ||||||
|  | @ -21,7 +21,7 @@ | ||||||
|                 </div> |                 </div> | ||||||
|             </div> |             </div> | ||||||
|         </ui-popup> |         </ui-popup> | ||||||
|         <page-filter> |         <page-filter breakpoint="lg"> | ||||||
|             <template #buttons> |             <template #buttons> | ||||||
|                 <f-text id="search" v-model="innerFilter.search" name="search" label="Suchen" size="sm"></f-text> |                 <f-text id="search" v-model="innerFilter.search" name="search" label="Suchen" size="sm"></f-text> | ||||||
|                 <ui-icon-button icon="plus" @click="editing = {participant: null, preview: JSON.stringify(meta.form_config)}">Hinzufügen</ui-icon-button> |                 <ui-icon-button icon="plus" @click="editing = {participant: null, preview: JSON.stringify(meta.form_config)}">Hinzufügen</ui-icon-button> | ||||||
|  |  | ||||||
|  | @ -72,7 +72,7 @@ | ||||||
|                 </section> |                 </section> | ||||||
|             </form> |             </form> | ||||||
|         </ui-popup> |         </ui-popup> | ||||||
|         <page-filter> |         <page-filter breakpoint="xl" :filterable="false"> | ||||||
|             <template #buttons> |             <template #buttons> | ||||||
|                 <f-text id="search" :model-value="getFilter('search')" label="Suchen …" size="sm" @update:model-value="setFilter('search', $event)"></f-text> |                 <f-text id="search" :model-value="getFilter('search')" label="Suchen …" size="sm" @update:model-value="setFilter('search', $event)"></f-text> | ||||||
|                 <f-multipleselect |                 <f-multipleselect | ||||||
|  |  | ||||||
|  | @ -1,3 +0,0 @@ | ||||||
| <template></template> |  | ||||||
| 
 |  | ||||||
| <script setup></script> |  | ||||||
|  | @ -37,14 +37,53 @@ | ||||||
|                 <span class="hidden xl:inline">Anwenden</span> |                 <span class="hidden xl:inline">Anwenden</span> | ||||||
|             </button> |             </button> | ||||||
|         </ui-popup> |         </ui-popup> | ||||||
| 
 |         <page-filter breakpoint="xl"> | ||||||
|         <page-filter> |  | ||||||
|             <template #fields> |             <template #fields> | ||||||
|                 <f-switch v-show="hasModule('bill')" id="ausstand" name="ausstand" v-model="filter.ausstand" label="Nur Ausstände" size="sm"></f-switch> |                 <f-switch | ||||||
|                 <f-select id="has_vk" name="has_vk" v-model="filter.has_vk" label="Verhaltenskodex unterschrieben" size="sm" :options="meta.boolean_filter"></f-select> |                     v-show="hasModule('bill')" | ||||||
|                 <f-select id="has_svk" name="has_svk" v-model="filter.has_svk" label="SVK unterschrieben" size="sm" :options="meta.boolean_filter"></f-select> |                     id="ausstand" | ||||||
|                 <f-multipleselect id="group_ids" :options="meta.groups" v-model="filter.group_ids" label="Gruppierungen" size="sm"></f-multipleselect> |                     name="ausstand" | ||||||
|                 <f-select v-show="hasModule('bill')" id="billKinds" name="billKinds" :options="meta.billKinds" v-model="filter.bill_kind" label="Rechnung" size="sm"></f-select> |                     :model-value="getFilter('ausstand')" | ||||||
|  |                     label="Nur Ausstände" | ||||||
|  |                     size="sm" | ||||||
|  |                     @update:model-value="setFilter('ausstand', $event)" | ||||||
|  |                 ></f-switch> | ||||||
|  |                 <f-select | ||||||
|  |                     id="has_vk" | ||||||
|  |                     name="has_vk" | ||||||
|  |                     :model-value="getFilter('has_vk')" | ||||||
|  |                     label="Verhaltenskodex unterschrieben" | ||||||
|  |                     size="sm" | ||||||
|  |                     :options="meta.boolean_filter" | ||||||
|  |                     @update:model-value="setFilter('has_vk', $event)" | ||||||
|  |                 ></f-select> | ||||||
|  |                 <f-select | ||||||
|  |                     id="has_svk" | ||||||
|  |                     name="has_svk" | ||||||
|  |                     :model-value="getFilter('has_svk')" | ||||||
|  |                     label="SVK unterschrieben" | ||||||
|  |                     size="sm" | ||||||
|  |                     :options="meta.boolean_filter" | ||||||
|  |                     @update:model-value="setFilter('has_svk', $event)" | ||||||
|  |                 ></f-select> | ||||||
|  |                 <f-multipleselect | ||||||
|  |                     id="group_ids" | ||||||
|  |                     :options="meta.groups" | ||||||
|  |                     :model-value="getFilter('group_ids')" | ||||||
|  |                     label="Gruppierungen" | ||||||
|  |                     size="sm" | ||||||
|  |                     @update:model-value="setFilter('group_ids', $event)" | ||||||
|  |                 ></f-multipleselect> | ||||||
|  |                 <f-select | ||||||
|  |                     v-show="hasModule('bill')" | ||||||
|  |                     id="billKinds" | ||||||
|  |                     name="billKinds" | ||||||
|  |                     :options="meta.billKinds" | ||||||
|  |                     :model-value="getFilter('bill_kind')" | ||||||
|  |                     label="Rechnung" | ||||||
|  |                     size="sm" | ||||||
|  |                     @update:model-value="setFilter('bill_kind', $event)" | ||||||
|  |                 ></f-select> | ||||||
|                 <button class="btn btn-primary label mr-2" @click.prevent="membershipFilters = getFilter('memberships')"> |                 <button class="btn btn-primary label mr-2" @click.prevent="membershipFilters = getFilter('memberships')"> | ||||||
|                     <ui-sprite class="w-3 h-3 xl:mr-2" src="filter"></ui-sprite> |                     <ui-sprite class="w-3 h-3 xl:mr-2" src="filter"></ui-sprite> | ||||||
|                     <span class="hidden xl:inline">Mitgliedschaften</span> |                     <span class="hidden xl:inline">Mitgliedschaften</span> | ||||||
|  | @ -135,11 +174,6 @@ const single = ref(null); | ||||||
| const deleting = ref(null); | const deleting = ref(null); | ||||||
| const membershipFilters = ref(null); | const membershipFilters = ref(null); | ||||||
| 
 | 
 | ||||||
| var filter = ref({ |  | ||||||
|     ausstand: false, |  | ||||||
| }); |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| const props = defineProps(indexProps); | const props = defineProps(indexProps); | ||||||
| var { router, data, meta, getFilter, setFilter, filterString, reloadPage } = useIndex(props.data, 'member'); | var { router, data, meta, getFilter, setFilter, filterString, reloadPage } = useIndex(props.data, 'member'); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -3,31 +3,24 @@ | ||||||
|         <template #right> |         <template #right> | ||||||
|             <f-save-button form="preventionform"></f-save-button> |             <f-save-button form="preventionform"></f-save-button> | ||||||
|         </template> |         </template> | ||||||
| 
 |  | ||||||
|         <setting-layout v-if="loaded"> |         <setting-layout v-if="loaded"> | ||||||
|             <form id="preventionform" class="grow p-6" @submit.prevent="submit"> |             <form id="preventionform" class="grow p-6" @submit.prevent="submit"> | ||||||
|                 <div class="col-span-full text-gray-100 mb-3"> |                 <div class="col-span-full text-gray-100 mb-3"> | ||||||
|                     <p class="text-sm">Hier kannst du Einstellungen zu Prävention setzen.</p> |                     <p class="text-sm">Hier kannst du Einstellungen zu Prävention setzen.</p> | ||||||
|                 </div> |                 </div> | ||||||
|                 <ui-tabs v-model="active" class="mt-2" :entries="tabs"></ui-tabs> |                 <div class="grid gap-4 mt-2"> | ||||||
|                 <f-editor v-if="active === 0" id="formmail" v-model="data.formmail" label="E-Mail für Veranstaltungs-TN"></f-editor> |                     <f-editor id="frommail" v-model="data.formmail" label="E-Mail für Veranstaltungs-TN"></f-editor> | ||||||
|                 <f-editor v-if="active === 1" id="yearlymail" v-model="data.yearlymail" label="Jährliche Präventions-Erinnerung"></f-editor> |                 </div> | ||||||
|             </form> |             </form> | ||||||
|         </setting-layout> |         </setting-layout> | ||||||
|     </page-layout> |     </page-layout> | ||||||
| </template> | </template> | ||||||
| 
 | 
 | ||||||
| <script lang="js" setup> | <script lang="js" setup> | ||||||
| import { reactive, ref } from 'vue'; | import { ref } from 'vue'; | ||||||
| import { useApiIndex } from '../../composables/useApiIndex.js'; | import { useApiIndex } from '../../composables/useApiIndex.js'; | ||||||
| import SettingLayout from '../setting/Layout.vue'; | import SettingLayout from '../setting/Layout.vue'; | ||||||
| 
 | 
 | ||||||
| const tabs = [ |  | ||||||
|     { title: 'für Veranstaltungen' }, |  | ||||||
|     { title: 'Jährlich' }, |  | ||||||
| ]; |  | ||||||
| const active = ref(0); |  | ||||||
| 
 |  | ||||||
| const { axios, data, reload } = useApiIndex('/api/prevention', 'prevention'); | const { axios, data, reload } = useApiIndex('/api/prevention', 'prevention'); | ||||||
| const loaded = ref(false); | const loaded = ref(false); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1,5 +1,5 @@ | ||||||
| @component('mail::message') | @component('mail::message') | ||||||
| # Hallo {{ $preventable->member->fullname }},
 | # Hallo {{ $preventable->getMailRecipient()->name }},
 | ||||||
| 
 | 
 | ||||||
| <x-mail-view::editor :content="$bodyText->toArray()['blocks']"></x-mail-view::editor> | <x-mail-view::editor :content="$bodyText->toArray()['blocks']"></x-mail-view::editor> | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -13,15 +13,13 @@ use App\Lib\Editor\Condition; | ||||||
| use App\Prevention\Mails\PreventionRememberMail; | use App\Prevention\Mails\PreventionRememberMail; | ||||||
| use App\Member\Member; | use App\Member\Member; | ||||||
| use App\Member\Membership; | use App\Member\Membership; | ||||||
|  | use App\Prevention\Actions\YearlyRememberAction; | ||||||
| use App\Prevention\Mails\YearlyMail; | use App\Prevention\Mails\YearlyMail; | ||||||
| use App\Prevention\PreventionSettings; | use App\Prevention\PreventionSettings; | ||||||
| use Generator; |  | ||||||
| use Illuminate\Foundation\Testing\DatabaseTransactions; | use Illuminate\Foundation\Testing\DatabaseTransactions; | ||||||
| use Illuminate\Support\Facades\Mail; | use Illuminate\Support\Facades\Mail; | ||||||
| use PHPUnit\Framework\Attributes\DataProvider; |  | ||||||
| use Tests\Lib\CreatesFormFields; | use Tests\Lib\CreatesFormFields; | ||||||
| use Tests\RequestFactories\EditorRequestFactory; | use Tests\RequestFactories\EditorRequestFactory; | ||||||
| use Tests\TestCase; |  | ||||||
| 
 | 
 | ||||||
| uses(DatabaseTransactions::class); | uses(DatabaseTransactions::class); | ||||||
| uses(CreatesFormFields::class); | uses(CreatesFormFields::class); | ||||||
|  | @ -50,6 +48,11 @@ function createMember(array $attributes): Member | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| dataset('attributes', fn() => [ | dataset('attributes', fn() => [ | ||||||
|  |     [ | ||||||
|  |         ['has_vk' => false, 'efz' => null, 'ps_at' => null], | ||||||
|  |         [Prevention::EFZ, Prevention::VK, Prevention::PS] | ||||||
|  |     ], | ||||||
|  | 
 | ||||||
|     [ |     [ | ||||||
|         ['has_vk' => true, 'efz' => null, 'ps_at' => now()], |         ['has_vk' => true, 'efz' => null, 'ps_at' => now()], | ||||||
|         [Prevention::EFZ] |         [Prevention::EFZ] | ||||||
|  | @ -180,7 +183,7 @@ it('testItDoesntRememberWhenParticipantDoesntHaveMember', function () { | ||||||
|     $this->assertNull($participant->fresh()->last_remembered_at); |     $this->assertNull($participant->fresh()->last_remembered_at); | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| it('testItRemembersNonLeaders', function () { | it('doesnt remember non leaders', function () { | ||||||
|     Mail::fake(); |     Mail::fake(); | ||||||
|     $form = createForm(); |     $form = createForm(); | ||||||
|     $participant = createParticipant($form); |     $participant = createParticipant($form); | ||||||
|  | @ -192,7 +195,7 @@ it('testItRemembersNonLeaders', function () { | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| it('testItRemembersMember', function ($attrs, $preventions) { | it('remembers event participant', function ($attrs, $preventions) { | ||||||
|     Mail::fake(); |     Mail::fake(); | ||||||
|     $form = createForm(); |     $form = createForm(); | ||||||
|     $participant = createParticipant($form); |     $participant = createParticipant($form); | ||||||
|  | @ -201,7 +204,7 @@ it('testItRemembersMember', function ($attrs, $preventions) { | ||||||
|     PreventionRememberAction::run(); |     PreventionRememberAction::run(); | ||||||
| 
 | 
 | ||||||
|     if (count($preventions)) { |     if (count($preventions)) { | ||||||
|         Mail::assertSent(PreventionRememberMail::class, fn($mail) => $mail->preventable->preventions() === $preventions); |         Mail::assertSent(PreventionRememberMail::class, fn($mail) => $mail->preventable->preventions()->pluck('type')->toArray() === $preventions); | ||||||
|         $this->assertNotNull($participant->fresh()->last_remembered_at); |         $this->assertNotNull($participant->fresh()->last_remembered_at); | ||||||
|     } else { |     } else { | ||||||
|         Mail::assertNotSent(PreventionRememberMail::class); |         Mail::assertNotSent(PreventionRememberMail::class); | ||||||
|  | @ -209,6 +212,49 @@ it('testItRemembersMember', function ($attrs, $preventions) { | ||||||
|     } |     } | ||||||
| })->with('attributes'); | })->with('attributes'); | ||||||
| 
 | 
 | ||||||
|  | it('sets due date in mail when not now', function () { | ||||||
|  |     Mail::fake(); | ||||||
|  |     $form = createForm(); | ||||||
|  |     $form->update(['from' => now()->addMonths(8)]); | ||||||
|  |     $participant = createParticipant($form); | ||||||
|  |     $participant->member->update(['efz' =>  now()->subYears(5)->addMonth(), 'ps_at' => now(), 'has_vk' => true]); | ||||||
|  | 
 | ||||||
|  |     PreventionRememberAction::run(); | ||||||
|  | 
 | ||||||
|  |     Mail::assertSent(PreventionRememberMail::class, fn($mail) => $mail->preventable->preventions()->first()->expires->isSameDay(now()->addMonth())); | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | it('notices a few weeks before', function ($date, bool $shouldSend) { | ||||||
|  |     Mail::fake(); | ||||||
|  |     app(PreventionSettings::class)->fill(['weeks' => 2])->save(); | ||||||
|  |     createMember(['efz' => $date, 'ps_at' => now(), 'has_vk' => true]); | ||||||
|  | 
 | ||||||
|  |     YearlyRememberAction::run(); | ||||||
|  | 
 | ||||||
|  |     $shouldSend | ||||||
|  |         ? Mail::assertSent(YearlyMail::class, fn($mail) => $mail->preventions->first()->expires->isSameDay(now()->addWeeks(2))) | ||||||
|  |         : Mail::assertNotSent(YearlyMail::class); | ||||||
|  | })->with([ | ||||||
|  |     [fn() => now()->subYears(5)->addWeeks(2), true], | ||||||
|  |     [fn() => now()->subYears(5)->addWeeks(2)->addDay(), false], | ||||||
|  |     [fn() => now()->subYears(5)->addWeeks(2)->subDay(), false], | ||||||
|  | ]); | ||||||
|  | 
 | ||||||
|  | it('remembers members yearly', function ($date, $shouldSend) { | ||||||
|  |     Mail::fake(); | ||||||
|  |     createMember(['efz' => $date, 'ps_at' => now(), 'has_vk' => true]); | ||||||
|  | 
 | ||||||
|  |     YearlyRememberAction::run(); | ||||||
|  | 
 | ||||||
|  |     $shouldSend | ||||||
|  |         ? Mail::assertSent(YearlyMail::class, fn($mail) => $mail->preventions->first()->expires->isSameDay(now())) | ||||||
|  |         : Mail::assertNotSent(YearlyMail::class); | ||||||
|  | })->with([ | ||||||
|  |     [fn() => now()->subYears(5), true], | ||||||
|  |     [fn() => now()->subYears(5)->addDay(), false], | ||||||
|  |     [fn() => now()->subYears(5)->subDay(), false], | ||||||
|  | ]); | ||||||
|  | 
 | ||||||
| it('testItDoesntRememberParticipantThatHasNoMail', function () { | it('testItDoesntRememberParticipantThatHasNoMail', function () { | ||||||
|     Mail::fake(); |     Mail::fake(); | ||||||
|     $form = createForm(); |     $form = createForm(); | ||||||
|  | @ -220,16 +266,6 @@ it('testItDoesntRememberParticipantThatHasNoMail', function () { | ||||||
|     Mail::assertNotSent(PreventionRememberMail::class); |     Mail::assertNotSent(PreventionRememberMail::class); | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| it('testItRendersMail', function () { |  | ||||||
|     InvoiceSettings::fake(['from_long' => 'Stamm Beispiel']); |  | ||||||
|     $form = createForm(); |  | ||||||
|     $participant = createParticipant($form); |  | ||||||
|     (new PreventionRememberMail($participant, app(PreventionSettings::class)->formmail)) |  | ||||||
|         ->assertSeeInText($participant->member->firstname) |  | ||||||
|         ->assertSeeInText($participant->member->lastname) |  | ||||||
|         ->assertSeeInText('Stamm Beispiel'); |  | ||||||
| }); |  | ||||||
| 
 |  | ||||||
| it('testItRendersSetttingMail', function () { | it('testItRendersSetttingMail', function () { | ||||||
|     Mail::fake(); |     Mail::fake(); | ||||||
|     app(PreventionSettings::class)->fill([ |     app(PreventionSettings::class)->fill([ | ||||||
|  | @ -279,7 +315,8 @@ it('testItDoesntAppendTextTwice', function () { | ||||||
|     Mail::assertSent(PreventionRememberMail::class, fn($mail) => $mail->bodyText->hasAll(['oberhausen']) && !$mail->bodyText->hasAll(['siegburg'])); |     Mail::assertSent(PreventionRememberMail::class, fn($mail) => $mail->bodyText->hasAll(['oberhausen']) && !$mail->bodyText->hasAll(['siegburg'])); | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| it('testItDisplaysBodyTextInMail', function () { | /* ----------------------------------------- Mail contents ----------------------------------------- */ | ||||||
|  | it('displays body text in prevention remember mail', function () { | ||||||
|     $form = createForm(); |     $form = createForm(); | ||||||
|     $participant = createParticipant($form); |     $participant = createParticipant($form); | ||||||
| 
 | 
 | ||||||
|  | @ -287,8 +324,21 @@ it('testItDisplaysBodyTextInMail', function () { | ||||||
|     $mail->assertSeeInText('ggtt'); |     $mail->assertSeeInText('ggtt'); | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| it('displays text in yearly mail', function () { | it('renders prevention mail for events with group name', function () { | ||||||
|     $member = createMember([]); |     InvoiceSettings::fake(['from_long' => 'Stamm Beispiel']); | ||||||
|     $mail = new YearlyMail($member, EditorRequestFactory::new()->paragraphs(['ggtt'])->toData()); |     $form = createForm(); | ||||||
|     $mail->assertSeeInText('ggtt'); |     $participant = createParticipant($form); | ||||||
|  |     (new PreventionRememberMail($participant, app(PreventionSettings::class)->formmail, collect([]))) | ||||||
|  |         ->assertSeeInText('Max') | ||||||
|  |         ->assertSeeInText('Muster') | ||||||
|  |         ->assertSeeInText('Stamm Beispiel'); | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | it('renders yearly mail', function () { | ||||||
|  |     InvoiceSettings::fake(['from_long' => 'Stamm Beispiel']); | ||||||
|  |     $member = createMember([]); | ||||||
|  |     $mail = new YearlyMail($member, EditorRequestFactory::new()->paragraphs(['ggtt'])->toData(), collect([])); | ||||||
|  |     $mail | ||||||
|  |         ->assertSeeInText('ggtt') | ||||||
|  |         ->assertSeeInText('Stamm Beispiel'); | ||||||
| }); | }); | ||||||
|  |  | ||||||
|  | @ -9,6 +9,7 @@ use Tests\RequestFactories\EditorRequestFactory; | ||||||
| uses(DatabaseTransactions::class); | uses(DatabaseTransactions::class); | ||||||
| 
 | 
 | ||||||
| it('testItOpensSettingsPage', function () { | it('testItOpensSettingsPage', function () { | ||||||
|  |     test()->withoutExceptionHandling(); | ||||||
|     test()->login()->loginNami(); |     test()->login()->loginNami(); | ||||||
| 
 | 
 | ||||||
|     test()->get('/setting/prevention')->assertComponent('setting/Prevention')->assertOk(); |     test()->get('/setting/prevention')->assertComponent('setting/Prevention')->assertOk(); | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue