Add customer contact inquiry system, automated receipt emails, admin communication inbox, and customer profile management

This commit is contained in:
2026-11-02 09:56:24 +01:00
parent 387241f971
commit 9fd39bd7d6
28 changed files with 2426 additions and 8 deletions
+234
View File
@@ -0,0 +1,234 @@
<?php
namespace Tests\Feature;
use App\Mail\ContactInquiryConfirmationMail;
use App\Mail\ContactInquiryReceivedMail;
use App\Mail\ContactInquiryReplyMail;
use App\Models\Communication;
use App\Models\Order;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Mail;
use Tests\TestCase;
class CustomerCommunicationTest extends TestCase
{
use RefreshDatabase;
private User $admin;
private User $customer;
protected function setUp(): void
{
parent::setUp();
$this->admin = User::factory()->create([
'name' => 'Admin Beheerder',
'email' => 'admin@jouwdriedee.nl',
'is_admin' => true,
]);
$this->customer = User::factory()->create([
'name' => 'Kees de Vries',
'email' => 'kees@example.com',
'is_admin' => false,
]);
}
public function test_contact_page_loads_successfully(): void
{
$response = $this->get(route('contact.index'));
$response->assertStatus(200);
$response->assertSee('Stel een vraag');
$response->assertSee('Directe Contactgegevens');
}
public function test_customer_can_submit_question_and_both_emails_are_dispatched(): void
{
Mail::fake();
$response = $this->post(route('contact.store'), [
'name' => 'Lisa Jansen',
'email' => 'lisa@example.com',
'phone' => '06-87654321',
'subject' => 'Vraag over levertijd van Pokémon model',
'message' => 'Goedemiddag, hoe lang duurt het printen van het Charizard model in PETG?',
]);
$response->assertRedirect(route('contact.index'));
$response->assertSessionHas('success');
// Check database
$this->assertDatabaseHas('communications', [
'name' => 'Lisa Jansen',
'email' => 'lisa@example.com',
'subject' => 'Vraag over levertijd van Pokémon model',
'status' => 'new',
]);
$communication = Communication::where('email', 'lisa@example.com')->first();
$this->assertNotNull($communication);
// 1. Mail to webshop management (info@jouwdrieedee.nl / info@jouwdriedee.nl)
Mail::assertSent(ContactInquiryReceivedMail::class, function ($mail) use ($communication) {
return $mail->communication->id === $communication->id;
});
// 2. Afschrift to customer from (noreply@jouwdriedee.nl)
Mail::assertSent(ContactInquiryConfirmationMail::class, function ($mail) use ($communication) {
$hasCorrectRecipient = $mail->hasTo('lisa@example.com');
$hasCorrectFrom = $mail->hasFrom('noreply@jouwdriedee.nl');
return $hasCorrectRecipient && $hasCorrectFrom && $mail->communication->id === $communication->id;
});
}
public function test_logged_in_customer_inquiry_is_linked_and_visible_in_account(): void
{
Mail::fake();
$response = $this->actingAs($this->customer)->post(route('contact.store'), [
'name' => $this->customer->name,
'email' => $this->customer->email,
'subject' => 'Speciale kleur vraag',
'message' => 'Kan het model ook in glow-in-the-dark filament worden geprint?',
]);
$response->assertRedirect(route('contact.index'));
$this->assertDatabaseHas('communications', [
'user_id' => $this->customer->id,
'email' => 'kees@example.com',
'subject' => 'Speciale kleur vraag',
]);
// Customer dashboard in /mijn-account shows their question
$accountResponse = $this->actingAs($this->customer)->get(route('account.index'));
$accountResponse->assertStatus(200);
$accountResponse->assertSee('Speciale kleur vraag');
$accountResponse->assertSee('Mijn Vragen');
}
public function test_admin_can_view_communications_in_dedicated_admin_section(): void
{
$comm = Communication::create([
'name' => 'Peter Bakker',
'email' => 'peter@example.com',
'subject' => 'Garantie op 3D prints',
'message' => 'Wat voor garantie zit er op technische 3D prints?',
'status' => 'new',
]);
// Admin list
$indexResponse = $this->actingAs($this->admin)->get(route('admin.communications.index'));
$indexResponse->assertStatus(200);
$indexResponse->assertSee('Communicatie');
$indexResponse->assertSee('Garantie op 3D prints');
$indexResponse->assertSee('Peter Bakker');
// Admin detail
$showResponse = $this->actingAs($this->admin)->get(route('admin.communications.show', $comm));
$showResponse->assertStatus(200);
$showResponse->assertSee('Garantie op 3D prints');
$showResponse->assertSee('Wat voor garantie zit er op technische 3D prints?');
// Status is automatically transitioned from new to in_progress upon opening
$this->assertEquals('in_progress', $comm->fresh()->status);
}
public function test_admin_can_reply_to_customer_from_admin_section(): void
{
Mail::fake();
$comm = Communication::create([
'name' => 'Sophie Visser',
'email' => 'sophie@example.com',
'subject' => 'Custom formaat mogelijk?',
'message' => 'Kan de plantenbak ook 30cm hoog geprint worden?',
'status' => 'in_progress',
]);
$replyResponse = $this->actingAs($this->admin)->post(route('admin.communications.reply', $comm), [
'message' => 'Beste Sophie, ja zeker! We kunnen dit model opschalen naar 30cm. De levertijd is 3 dagen.',
]);
$replyResponse->assertRedirect();
$replyResponse->assertSessionHas('success');
// Verify status and reply in database
$this->assertEquals('resolved', $comm->fresh()->status);
$this->assertNotNull($comm->fresh()->last_replied_at);
$this->assertDatabaseHas('communication_replies', [
'communication_id' => $comm->id,
'is_admin_reply' => true,
]);
// Verify email was sent to customer from info@jouwdriedee.nl
Mail::assertSent(ContactInquiryReplyMail::class, function ($mail) {
return $mail->hasTo('sophie@example.com') && $mail->hasFrom('info@jouwdriedee.nl');
});
}
public function test_admin_can_view_communication_inside_customer_profile(): void
{
// Link inquiry to customer
$comm = Communication::create([
'user_id' => $this->customer->id,
'name' => $this->customer->name,
'email' => $this->customer->email,
'subject' => 'Vraag binnen profiel',
'message' => 'Dit is een vraag die in het klantprofiel zichtbaar moet zijn.',
'status' => 'new',
]);
// Also create an order for this customer
Order::create([
'user_id' => $this->customer->id,
'order_number' => 'ORD-TEST-999',
'customer_name' => $this->customer->name,
'customer_email' => $this->customer->email,
'shipping_street' => 'Dorpsstraat',
'shipping_number' => '1',
'shipping_postal_code' => '1234 AB',
'shipping_city' => 'Utrecht',
'subtotal' => 45.00,
'shipping_cost' => 0.00,
'total' => 45.00,
'status' => 'pending',
'payment_status' => 'paid',
]);
// 1. Customers overview in admin
$indexResponse = $this->actingAs($this->admin)->get(route('admin.customers.index'));
$indexResponse->assertStatus(200);
$indexResponse->assertSee($this->customer->name);
$indexResponse->assertSee($this->customer->email);
// 2. Customer profile in admin (by user ID)
$profileResponse = $this->actingAs($this->admin)->get(route('admin.customers.show', $this->customer->id));
$profileResponse->assertStatus(200);
$profileResponse->assertSee('Klantprofiel: '.$this->customer->name);
$profileResponse->assertSee('Communicatie &amp; Vragen', false);
$profileResponse->assertSee('Vraag binnen profiel');
$profileResponse->assertSee('ORD-TEST-999');
// 3. Customer profile in admin by email
$profileByEmail = $this->actingAs($this->admin)->get(route('admin.customers.show', $this->customer->email));
$profileByEmail->assertStatus(200);
$profileByEmail->assertSee('Vraag binnen profiel');
}
public function test_guest_and_non_admin_cannot_access_admin_communications_or_customers(): void
{
// Guests redirected to login
$this->get(route('admin.communications.index'))->assertRedirect(route('login'));
$this->get(route('admin.customers.index'))->assertRedirect(route('login'));
// Normal customer is redirected to home with error flash
$this->actingAs($this->customer)->get(route('admin.communications.index'))->assertRedirect(route('home'));
$this->actingAs($this->customer)->get(route('admin.customers.index'))->assertRedirect(route('home'));
}
}