Skip to content

Commit 4ba2f88

Browse files
authored
Merge pull request #1592 from canyongbs/ADVAPP-1545
[ADVAPP-1545]: Update the experience for prospects and students when managing the list view and separate out what is currently listed as engage into Send Email and Send Text
2 parents 38a07ee + f5d231a commit 4ba2f88

6 files changed

Lines changed: 245 additions & 46 deletions

File tree

app-modules/engagement/src/Filament/Actions/BulkEngagementAction.php renamed to app-modules/engagement/src/Filament/Actions/BulkEmailAction.php

Lines changed: 10 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,9 @@
3838

3939
use AdvisingApp\Engagement\Actions\CreateEngagementBatch;
4040
use AdvisingApp\Engagement\DataTransferObjects\EngagementCreationData;
41-
use AdvisingApp\Engagement\Filament\Forms\Components\EngagementSmsBodyInput;
4241
use AdvisingApp\Engagement\Models\EmailTemplate;
4342
use AdvisingApp\Notification\Enums\NotificationChannel;
4443
use AdvisingApp\Notification\Models\Contracts\CanBeNotified;
45-
use Exception;
4644
use Filament\Forms\Components\Actions;
4745
use Filament\Forms\Components\Actions\Action;
4846
use Filament\Forms\Components\Checkbox;
@@ -62,26 +60,16 @@
6260
use Illuminate\Support\Facades\Auth;
6361
use Livewire\Features\SupportFileUploads\TemporaryUploadedFile;
6462

65-
class BulkEngagementAction
63+
class BulkEmailAction
6664
{
67-
public static function make(string $context)
65+
public static function make(string $context): BulkAction
6866
{
69-
return BulkAction::make('engage')
70-
->icon('heroicon-o-chat-bubble-bottom-center-text')
71-
->modalHeading('Send Bulk Engagement')
72-
->modalDescription(fn (Collection $records) => "You have selected {$records->count()} {$context} to engage.")
67+
return BulkAction::make('send_email')
68+
->label('Send Email')
69+
->icon('heroicon-o-envelope')
70+
->modalHeading('Send Bulk Email')
71+
->modalDescription(fn (Collection $records) => "You have selected {$records->count()} {$context} to send email.")
7372
->steps([
74-
Step::make('Choose your delivery method')
75-
->description('Select email or sms.')
76-
->schema([
77-
Select::make('channel')
78-
->label('How would you like to send this engagement?')
79-
->options(NotificationChannel::getEngagementOptions())
80-
->default(NotificationChannel::Email->value)
81-
->disableOptionWhen(fn (string $value): bool => NotificationChannel::tryFrom($value)?->getCaseDisabled())
82-
->selectablePlaceholder(false)
83-
->live(),
84-
]),
8573
Step::make('Engagement Details')
8674
->description("Add the details that will be sent to the selected {$context}")
8775
->schema([
@@ -96,7 +84,6 @@ public static function make(string $context)
9684
])
9785
->showMergeTagsInBlocksPanel(false)
9886
->helperText('You may use “merge tags” to substitute information about a recipient into your subject line. Insert a “{{“ in the subject line field to see a list of available merge tags')
99-
->hidden(fn (Get $get): bool => $get('channel') === NotificationChannel::Sms->value)
10087
->profile('sms')
10188
->required()
10289
->placeholder('Enter the email subject here...')
@@ -165,10 +152,8 @@ public static function make(string $context)
165152
$component->generateImageUrls($template->content),
166153
);
167154
}))
168-
->hidden(fn (Get $get): bool => $get('channel') === NotificationChannel::Sms->value)
169155
->helperText('You can insert recipient information by typing {{ and choosing a merge value to insert.')
170156
->columnSpanFull(),
171-
EngagementSmsBodyInput::make(context: 'create'),
172157
Actions::make([
173158
BulkDraftWithAiAction::make()
174159
->mergeTags($mergeTags),
@@ -187,23 +172,18 @@ public static function make(string $context)
187172
])
188173
->action(function (Collection $records, array $data, Form $form) {
189174
/** @var Collection<int, CanBeNotified> $records */
190-
$channel = NotificationChannel::parse($data['channel']);
191-
192175
app(CreateEngagementBatch::class)->execute(new EngagementCreationData(
193176
user: Auth::user(),
194-
recipient: match ($channel) {
195-
NotificationChannel::Email => $records->filter(fn (CanBeNotified $record) => $record->canReceiveEmail()),
196-
NotificationChannel::Sms => $records->filter(fn (CanBeNotified $record) => $record->canReceiveSms()),
197-
default => throw new Exception('Invalid engagement channel'),
198-
},
199-
channel: $channel,
177+
recipient: $records->filter(fn (CanBeNotified $record) => $record->canReceiveEmail()),
178+
channel: NotificationChannel::Email,
200179
subject: $data['subject'] ?? null,
201180
body: $data['body'] ?? null,
202181
temporaryBodyImages: array_map(
203182
fn (TemporaryUploadedFile $file): array => [
204183
'extension' => $file->getClientOriginalExtension(),
205184
'path' => (fn () => $this->path)->call($file),
206185
],
186+
/**@phpstan-ignore-next-line */
207187
$form->getFlatFields()['body']->getTemporaryImages(),
208188
),
209189
scheduledAt: ($data['send_later'] ?? false) ? Carbon::parse($data['scheduled_at'] ?? null) : null,
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
<?php
2+
3+
/*
4+
<COPYRIGHT>
5+
6+
Copyright © 2016-2025, Canyon GBS LLC. All rights reserved.
7+
8+
Advising App™ is licensed under the Elastic License 2.0. For more details,
9+
see https://github.com/canyongbs/advisingapp/blob/main/LICENSE.
10+
11+
Notice:
12+
13+
- You may not provide the software to third parties as a hosted or managed
14+
service, where the service provides users with access to any substantial set of
15+
the features or functionality of the software.
16+
- You may not move, change, disable, or circumvent the license key functionality
17+
in the software, and you may not remove or obscure any functionality in the
18+
software that is protected by the license key.
19+
- You may not alter, remove, or obscure any licensing, copyright, or other notices
20+
of the licensor in the software. Any use of the licensor’s trademarks is subject
21+
to applicable law.
22+
- Canyon GBS LLC respects the intellectual property rights of others and expects the
23+
same in return. Canyon GBS™ and Advising App™ are registered trademarks of
24+
Canyon GBS LLC, and we are committed to enforcing and protecting our trademarks
25+
vigorously.
26+
- The software solution, including services, infrastructure, and code, is offered as a
27+
Software as a Service (SaaS) by Canyon GBS LLC.
28+
- Use of this software implies agreement to the license terms and conditions as stated
29+
in the Elastic License 2.0.
30+
31+
For more information or inquiries please visit our website at
32+
https://www.canyongbs.com or contact us via email at legal@canyongbs.com.
33+
34+
</COPYRIGHT>
35+
*/
36+
37+
namespace AdvisingApp\Engagement\Filament\Actions;
38+
39+
use AdvisingApp\Engagement\Actions\CreateEngagementBatch;
40+
use AdvisingApp\Engagement\DataTransferObjects\EngagementCreationData;
41+
use AdvisingApp\Engagement\Filament\Forms\Components\EngagementSmsBodyInput;
42+
use AdvisingApp\Notification\Enums\NotificationChannel;
43+
use AdvisingApp\Notification\Models\Contracts\CanBeNotified;
44+
use Filament\Forms\Components\DateTimePicker;
45+
use Filament\Forms\Components\Toggle;
46+
use Filament\Forms\Components\Wizard\Step;
47+
use Filament\Forms\Form;
48+
use Filament\Forms\Get;
49+
use Filament\Tables\Actions\BulkAction;
50+
use Illuminate\Support\Carbon;
51+
use Illuminate\Support\Collection;
52+
use Illuminate\Support\Facades\Auth;
53+
use Livewire\Features\SupportFileUploads\TemporaryUploadedFile;
54+
55+
class BulkTextAction
56+
{
57+
public static function make(string $context): BulkAction
58+
{
59+
return BulkAction::make('send_text')
60+
->label('Send Text')
61+
->icon('heroicon-o-chat-bubble-bottom-center-text')
62+
->modalHeading('Send Bulk Text')
63+
->modalDescription(fn (Collection $records) => "You have selected {$records->count()} {$context} to text.")
64+
->steps([
65+
Step::make('Engagement Details')
66+
->description("Add the details that will be sent to the selected {$context}")
67+
->schema([
68+
EngagementSmsBodyInput::make(context: 'create'),
69+
]),
70+
Step::make('Schedule')
71+
->description('Choose when you would like to send this engagement.')
72+
->schema([
73+
Toggle::make('send_later')
74+
->reactive()
75+
->helperText('By default, this email or text will send as soon as it is created unless you schedule it to send later.'),
76+
DateTimePicker::make('scheduled_at')
77+
->required()
78+
->visible(fn (Get $get) => $get('send_later')),
79+
]),
80+
])
81+
->action(function (Collection $records, array $data, Form $form) {
82+
/** @var Collection<int, CanBeNotified> $records */
83+
app(CreateEngagementBatch::class)->execute(new EngagementCreationData(
84+
user: Auth::user(),
85+
recipient: $records->filter(fn (CanBeNotified $record) => $record->canReceiveSms()),
86+
channel: NotificationChannel::Sms,
87+
subject: $data['subject'] ?? null,
88+
body: $data['body'] ?? null,
89+
temporaryBodyImages: array_map(
90+
fn (TemporaryUploadedFile $file): array => [
91+
'extension' => $file->getClientOriginalExtension(),
92+
'path' => (fn () => $this->path)->call($file),
93+
],
94+
/**@phpstan-ignore-next-line */
95+
$form->getFlatFields()['body']->getTemporaryImages(),
96+
),
97+
scheduledAt: ($data['send_later'] ?? false) ? Carbon::parse($data['scheduled_at'] ?? null) : null,
98+
));
99+
})
100+
->modalSubmitActionLabel('Send')
101+
->deselectRecordsAfterCompletion()
102+
->modalCloseButton(false)
103+
->closeModalByClickingAway(false)
104+
->closeModalByEscaping(false);
105+
}
106+
}
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
<?php
2+
3+
/*
4+
<COPYRIGHT>
5+
6+
Copyright © 2016-2025, Canyon GBS LLC. All rights reserved.
7+
8+
Advising App™ is licensed under the Elastic License 2.0. For more details,
9+
see https://github.com/canyongbs/advisingapp/blob/main/LICENSE.
10+
11+
Notice:
12+
13+
- You may not provide the software to third parties as a hosted or managed
14+
service, where the service provides users with access to any substantial set of
15+
the features or functionality of the software.
16+
- You may not move, change, disable, or circumvent the license key functionality
17+
in the software, and you may not remove or obscure any functionality in the
18+
software that is protected by the license key.
19+
- You may not alter, remove, or obscure any licensing, copyright, or other notices
20+
of the licensor in the software. Any use of the licensor’s trademarks is subject
21+
to applicable law.
22+
- Canyon GBS LLC respects the intellectual property rights of others and expects the
23+
same in return. Canyon GBS™ and Advising App™ are registered trademarks of
24+
Canyon GBS LLC, and we are committed to enforcing and protecting our trademarks
25+
vigorously.
26+
- The software solution, including services, infrastructure, and code, is offered as a
27+
Software as a Service (SaaS) by Canyon GBS LLC.
28+
- Use of this software implies agreement to the license terms and conditions as stated
29+
in the Elastic License 2.0.
30+
31+
For more information or inquiries please visit our website at
32+
https://www.canyongbs.com or contact us via email at legal@canyongbs.com.
33+
34+
</COPYRIGHT>
35+
*/
36+
37+
use AdvisingApp\Engagement\Jobs\CreateBatchedEngagement;
38+
use AdvisingApp\Engagement\Models\EngagementBatch;
39+
use AdvisingApp\Notification\Enums\NotificationChannel;
40+
use AdvisingApp\Prospect\Filament\Resources\ProspectResource\Pages\ListProspects;
41+
use AdvisingApp\Prospect\Models\Prospect;
42+
use AdvisingApp\StudentDataModel\Filament\Resources\StudentResource\Pages\ListStudents;
43+
use AdvisingApp\StudentDataModel\Models\Student;
44+
use Illuminate\Support\Facades\Queue;
45+
46+
use function Pest\Faker\fake;
47+
use function Pest\Laravel\assertDatabaseCount;
48+
use function Pest\Livewire\livewire;
49+
use function Tests\asSuperAdmin;
50+
51+
it('can create a bulk Email Engagement properly for students', function () {
52+
Queue::fake();
53+
54+
asSuperAdmin();
55+
56+
$students = Student::factory()->count(3)->create();
57+
58+
$faker = fake();
59+
60+
$subject = ['type' => 'doc', 'content' => [['type' => 'paragraph', 'content' => [['type' => 'text', 'text' => $faker->sentence()]]]]];
61+
$body = ['type' => 'doc', 'content' => [['type' => 'paragraph', 'content' => [['type' => 'text', 'text' => $faker->paragraph()]]]]];
62+
63+
livewire(ListStudents::class)
64+
->mountTableBulkAction('send_email', $students->pluck('sisid')->toArray())
65+
->setTableBulkActionData([
66+
'subject' => $subject,
67+
'body' => $body,
68+
])
69+
->callMountedTableBulkAction()
70+
->assertHasNoTableBulkActionErrors();
71+
72+
assertDatabaseCount(EngagementBatch::class, 1);
73+
74+
$engagementBatch = EngagementBatch::first();
75+
76+
expect($engagementBatch->channel)->toEqual(NotificationChannel::Email);
77+
expect($engagementBatch->subject)->toEqual($subject);
78+
expect($engagementBatch->body)->toEqual($body);
79+
Queue::assertPushed(CreateBatchedEngagement::class, 3);
80+
});
81+
82+
it('can create a bulk Email Engagement properly for prospects', function () {
83+
Queue::fake();
84+
85+
asSuperAdmin();
86+
87+
$prospects = Prospect::factory()->count(3)->create();
88+
89+
$faker = fake();
90+
91+
$subject = ['type' => 'doc', 'content' => [['type' => 'paragraph', 'content' => [['type' => 'text', 'text' => $faker->sentence()]]]]];
92+
$body = ['type' => 'doc', 'content' => [['type' => 'paragraph', 'content' => [['type' => 'text', 'text' => $faker->paragraph()]]]]];
93+
94+
livewire(ListProspects::class)
95+
->mountTableBulkAction('send_email', $prospects->pluck('id')->toArray())
96+
->setTableBulkActionData([
97+
'subject' => $subject,
98+
'body' => $body,
99+
])
100+
->callMountedTableBulkAction()
101+
->assertHasNoTableBulkActionErrors();
102+
103+
assertDatabaseCount(EngagementBatch::class, 1);
104+
105+
$engagementBatch = EngagementBatch::first();
106+
107+
expect($engagementBatch->channel)->toEqual(NotificationChannel::Email);
108+
expect($engagementBatch->subject)->toEqual($subject);
109+
expect($engagementBatch->body)->toEqual($body);
110+
Queue::assertPushed(CreateBatchedEngagement::class, 3);
111+
});

app-modules/engagement/tests/Tenant/Filament/Actions/BulkEngagementActionTest.php renamed to app-modules/engagement/tests/Tenant/Filament/Actions/BulkTextActionTest.php

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@
3737
use AdvisingApp\Engagement\Jobs\CreateBatchedEngagement;
3838
use AdvisingApp\Engagement\Models\EngagementBatch;
3939
use AdvisingApp\Notification\Enums\NotificationChannel;
40+
use AdvisingApp\Prospect\Filament\Resources\ProspectResource\Pages\ListProspects;
41+
use AdvisingApp\Prospect\Models\Prospect;
4042
use AdvisingApp\StudentDataModel\Filament\Resources\StudentResource\Pages\ListStudents;
4143
use AdvisingApp\StudentDataModel\Models\Student;
4244
use Illuminate\Support\Facades\Queue;
@@ -46,7 +48,7 @@
4648
use function Pest\Livewire\livewire;
4749
use function Tests\asSuperAdmin;
4850

49-
it('can create an Email Engagement properly', function () {
51+
it('can create a bulk SMS Engagement properly for students', function () {
5052
Queue::fake();
5153

5254
asSuperAdmin();
@@ -55,14 +57,11 @@
5557

5658
$faker = fake();
5759

58-
$subject = ['type' => 'doc', 'content' => [['type' => 'paragraph', 'content' => [['type' => 'text', 'text' => $faker->sentence()]]]]];
5960
$body = ['type' => 'doc', 'content' => [['type' => 'paragraph', 'content' => [['type' => 'text', 'text' => $faker->paragraph()]]]]];
6061

6162
livewire(ListStudents::class)
62-
->mountTableBulkAction('engage', $students->pluck('sisid')->toArray())
63+
->mountTableBulkAction('send_text', $students->pluck('sisid')->toArray())
6364
->setTableBulkActionData([
64-
'channel' => NotificationChannel::Email->value,
65-
'subject' => $subject,
6665
'body' => $body,
6766
])
6867
->callMountedTableBulkAction()
@@ -72,27 +71,25 @@
7271

7372
$engagementBatch = EngagementBatch::first();
7473

75-
expect($engagementBatch->channel)->toEqual(NotificationChannel::Email);
76-
expect($engagementBatch->subject)->toEqual($subject);
74+
expect($engagementBatch->channel)->toEqual(NotificationChannel::Sms);
7775
expect($engagementBatch->body)->toEqual($body);
7876
Queue::assertPushed(CreateBatchedEngagement::class, 3);
7977
});
8078

81-
it('can create an SMS Engagement properly', function () {
79+
it('can create a bulk SMS Engagement properly for prospects', function () {
8280
Queue::fake();
8381

8482
asSuperAdmin();
8583

86-
$students = Student::factory()->count(3)->create();
84+
$prospects = Prospect::factory()->count(3)->create();
8785

8886
$faker = fake();
8987

9088
$body = ['type' => 'doc', 'content' => [['type' => 'paragraph', 'content' => [['type' => 'text', 'text' => $faker->paragraph()]]]]];
9189

92-
livewire(ListStudents::class)
93-
->mountTableBulkAction('engage', $students->pluck('sisid')->toArray())
90+
livewire(ListProspects::class)
91+
->mountTableBulkAction('send_text', $prospects->pluck('id')->toArray())
9492
->setTableBulkActionData([
95-
'channel' => NotificationChannel::Sms->value,
9693
'body' => $body,
9794
])
9895
->callMountedTableBulkAction()

0 commit comments

Comments
 (0)