- Add marketing_channel_id to broadcasts table - Create Campaign views (index, create, edit, show) with modern UX - Implement test send functionality with provider abstraction - Create Conversations data model (conversations, participants, messages) - Create Messaging inbox UI with conversation threads - Link broadcast sends to conversation system - Add Marketing nav section (premium feature, gated by has_marketing flag) - Update BroadcastController with sendTest method - Create MessagingController with conversation management - Add SMS provider support (Twilio, Telnyx, Cannabrands) - Create comprehensive platform naming and style guide Phase 9 Complete: Full campaign management UX Phase 10 Complete: Messaging foundation ready for two-way communication
121 lines
3.4 KiB
PHP
121 lines
3.4 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers\Seller;
|
|
|
|
use App\Http\Controllers\Controller;
|
|
use App\Models\Conversation;
|
|
use App\Models\Message;
|
|
use Illuminate\Http\Request;
|
|
|
|
class MessagingController extends Controller
|
|
{
|
|
/**
|
|
* Display messaging inbox with conversation list
|
|
*/
|
|
public function index(Request $request)
|
|
{
|
|
$business = $request->user()->currentBusiness;
|
|
|
|
$query = Conversation::where('business_id', $business->id)
|
|
->with(['primaryContact', 'latestMessage']);
|
|
|
|
// Filter by status
|
|
if ($request->has('status') && $request->status) {
|
|
$query->where('status', $request->status);
|
|
}
|
|
|
|
// Filter by channel
|
|
if ($request->has('channel') && $request->channel) {
|
|
$query->where('channel_type', $request->channel);
|
|
}
|
|
|
|
// Search
|
|
if ($request->has('search') && $request->search) {
|
|
$query->where(function ($q) use ($request) {
|
|
$q->where('subject', 'LIKE', "%{$request->search}%")
|
|
->orWhereHas('primaryContact', function ($contactQuery) use ($request) {
|
|
$contactQuery->where('name', 'LIKE', "%{$request->search}%")
|
|
->orWhere('email', 'LIKE', "%{$request->search}%")
|
|
->orWhere('phone', 'LIKE', "%{$request->search}%");
|
|
});
|
|
});
|
|
}
|
|
|
|
$conversations = $query
|
|
->orderByDesc('last_message_at')
|
|
->paginate(20);
|
|
|
|
return view('seller.messaging.index', compact('business', 'conversations'));
|
|
}
|
|
|
|
/**
|
|
* Display a specific conversation thread
|
|
*/
|
|
public function show(Request $request, Conversation $conversation)
|
|
{
|
|
$business = $request->user()->currentBusiness;
|
|
|
|
// Ensure business owns this conversation
|
|
if ($conversation->business_id !== $business->id) {
|
|
abort(403);
|
|
}
|
|
|
|
$conversation->load(['primaryContact', 'participants']);
|
|
|
|
$messages = $conversation->messages()
|
|
->with('contact')
|
|
->orderBy('created_at', 'asc')
|
|
->get();
|
|
|
|
return view('seller.messaging.show', compact('business', 'conversation', 'messages'));
|
|
}
|
|
|
|
/**
|
|
* Close a conversation
|
|
*/
|
|
public function close(Request $request, Conversation $conversation)
|
|
{
|
|
$business = $request->user()->currentBusiness;
|
|
|
|
if ($conversation->business_id !== $business->id) {
|
|
abort(403);
|
|
}
|
|
|
|
$conversation->close();
|
|
|
|
return back()->with('success', 'Conversation closed');
|
|
}
|
|
|
|
/**
|
|
* Reopen a conversation
|
|
*/
|
|
public function reopen(Request $request, Conversation $conversation)
|
|
{
|
|
$business = $request->user()->currentBusiness;
|
|
|
|
if ($conversation->business_id !== $business->id) {
|
|
abort(403);
|
|
}
|
|
|
|
$conversation->reopen();
|
|
|
|
return back()->with('success', 'Conversation reopened');
|
|
}
|
|
|
|
/**
|
|
* Archive a conversation
|
|
*/
|
|
public function archive(Request $request, Conversation $conversation)
|
|
{
|
|
$business = $request->user()->currentBusiness;
|
|
|
|
if ($conversation->business_id !== $business->id) {
|
|
abort(403);
|
|
}
|
|
|
|
$conversation->update(['status' => 'archived']);
|
|
|
|
return back()->with('success', 'Conversation archived');
|
|
}
|
|
}
|