Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions app/Http/Controllers/Admin/UserController.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@
use App\Http\Controllers\Controller;
use App\Http\Requests\User\StoreUserRequest;
use App\Http\Requests\User\UpdateUserRequest;
use App\Mail\AccountDeletedMail;
use App\Models\User;
use App\Notifications\WelcomeNotification;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Facades\Storage;
use Inertia\Inertia;
use Spatie\Permission\Models\Permission;
Expand Down Expand Up @@ -157,6 +159,10 @@ public function update(UpdateUserRequest $request, User $user)

public function destroy(User $user)
{
if (! empty($user->email)) {
Mail::to($user->email)->queue(new AccountDeletedMail($user->name));
}

$user->delete();

return redirect()
Expand Down
59 changes: 59 additions & 0 deletions app/Mail/AccountDeletedMail.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<?php

namespace App\Mail;

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 AccountDeletedMail extends Mailable implements ShouldQueue
{
use Queueable, SerializesModels;

public function __construct(
public ?string $recipientName = null,
) {}

public function envelope(): Envelope
{
return new Envelope(
subject: 'আপনার অ্যাকাউন্ট ডিলিট করা হয়েছে',
);
}

public function content(): Content
{
$name = $this->recipientName ? "Hello {$this->recipientName}," : 'প্রিয় শিক্ষার্থী,';
$privacyUrl = url('/privacy-policy');
$termsUrl = url('/terms-service');
$archiveUrl = url('/');
$forumUrl = url('/forum');
$chatUrl = url('/chat');
$trackerUrl = url('/tracker');

return new Content(
view: 'emails.default',
with: [
'subject' => 'আপনার অ্যাকাউন্ট ডিলিট করা হয়েছে',
'greeting' => $name,
'lines' => [
"আমরা আপনার অ্যাকাউন্ট ডিলিট করার অনুরোধটি পেয়েছি। আপনার অ্যাকাউন্ট এবং এর সাথে সম্পর্কিত যাবতীয় তথ্য আমাদের <a href=\"{$privacyUrl}\" target=\"_blank\">Privacy Policy</a> ও <a href=\"{$termsUrl}\" target=\"_blank\">Terms & Conditions</a> অনুযায়ী স্থায়ীভাবে মুছে ফেলা হয়েছে।",
'ভবিষ্যতে যদি আবার HSCStack ব্যবহার করতে চান, তাহলে যেকোনো সময় নতুন করে একটি অ্যাকাউন্ট তৈরি করে আমাদের প্ল্যাটফর্মে ফিরে আসতে পারেন।',
'<strong>HSCStack-এর উল্লেখযোগ্য ফিচারসমূহ:</strong>',
"• <a href=\"{$archiveUrl}\" target=\"_blank\">Resource Archive</a> — HSC ও SSC-এর Science, Arts এবং Commerce বিভাগের ক্লাস, নোট ও বিভিন্ন শিক্ষামূলক রিসোর্স।",
"• <a href=\"{$forumUrl}\" target=\"_blank\">Forum</a> — যেকোনো প্রশ্ন করতে পারবেন এবং সহপাঠীদের কাছ থেকে উত্তর ও সহযোগিতা পেতে পারবেন।",
"• <a href=\"{$chatUrl}\" target=\"_blank\">Global Chat</a> — সারাদেশের শিক্ষার্থীদের সাথে যোগাযোগ ও মতবিনিময়ের সুযোগ।",
"• <a href=\"{$trackerUrl}\" target=\"_blank\">Study Tracker</a> — আপনার পড়াশোনা ও সিলেবাসের অগ্রগতি সহজেই ট্র্যাক করুন।",
'• Community Interaction — অন্যান্য শিক্ষার্থীদের সাথে যুক্ত হয়ে আলোচনা, সহযোগিতা ও জ্ঞান বিনিময় করুন।',
'আবারও ধন্যবাদ HSCStack-এর সাথে থাকার জন্য।',
'ভবিষ্যতে আবার দেখা হবে! 💙',
],
'actionText' => 'Visit HSCStack',
'actionUrl' => config('app.url', url('/')),
],
);
}
}
7 changes: 6 additions & 1 deletion resources/views/emails/default.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,11 @@
.content p {
margin: 0 0 16px;
}
.content a {
color: #4f46e5;
text-decoration: underline;
font-weight: 600;
}
.action-button-container {
margin: 28px 0 16px;
text-align: left;
Expand Down Expand Up @@ -153,7 +158,7 @@

@if(!empty($lines))
@foreach($lines as $line)
<p>{{ $line }}</p>
<p>{!! $line !!}</p>
@endforeach
@endif

Expand Down
56 changes: 56 additions & 0 deletions tests/Feature/AdminUserDeletionMailTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<?php

use App\Mail\AccountDeletedMail;
use App\Models\User;
use Database\Seeders\RolePermissionSeeder;
use Illuminate\Support\Facades\Mail;

beforeEach(function () {
$this->seed(RolePermissionSeeder::class);
});

test('account deleted mailable renders expected subject, content and anchored links via emails.default', function () {
$mailable = new AccountDeletedMail('Rahim Ahmed');

$mailable->assertHasSubject('আপনার অ্যাকাউন্ট ডিলিট করা হয়েছে');

$html = $mailable->render();

expect($html)
->toContain('আপনার অ্যাকাউন্ট ডিলিট করা হয়েছে')
->toContain('আমরা আপনার অ্যাকাউন্ট ডিলিট করার অনুরোধটি পেয়েছি')
->toContain('Privacy Policy')
->toContain('Terms & Conditions')
->toContain('/privacy-policy')
->toContain('/terms-service')
->toContain('Resource Archive')
->toContain('Forum')
->toContain('Global Chat')
->toContain('Study Tracker')
->toContain('Community Interaction')
->toContain('ভবিষ্যতে আবার দেখা হবে! 💙');
});

test('admin deleting a user queues account deleted mail and deletes user', function () {
Mail::fake();

$admin = User::factory()->create();
$admin->assignRole('admin');

$userToDelete = User::factory()->create([
'name' => 'Karim Hasan',
'email' => 'karim@example.com',
]);

$this->actingAs($admin)
->delete("/admin/users/{$userToDelete->id}")
->assertRedirect(route('admin.users.index'))
->assertSessionHas('success');

expect(User::find($userToDelete->id))->toBeNull();

Mail::assertQueued(AccountDeletedMail::class, function (AccountDeletedMail $mail) use ($userToDelete) {
return $mail->hasTo($userToDelete->email)
&& $mail->recipientName === 'Karim Hasan';
});
});
Loading