qalainau/filament-team-chat
Slack-like team chat inside Filament v5: channels, DMs, threads, reactions, @mentions, file sharing, search, and unread tracking. Self-hosted with no external services—works via Livewire polling. Multi-tenant ready with optional team scoping.
Install and Publish:
composer require qalainau/filament-team-chat
php artisan vendor:publish --tag=team-chat-migrations
php artisan migrate
Register Plugin:
Add to your panel() method in AppServiceProvider or panel config:
->plugin(FilamentTeamChatPlugin::make())
Enable User Support:
Add the trait to your User model:
use Filament\TeamChat\Concerns\HasTeamChat;
Tailwind Setup (if not already configured):
php artisan filament:theme
Add to resources/css/filament/admin/theme.css:
@source '../../../../vendor/qalainau/filament-team-chat/resources/views/**/*';
Run npm run build.
First Use Case:
Visit /admin/team-chat and start a DM with @user or create a public channel (e.g., #general). Test sending a message with Markdown (e.g., **bold** text) and an attachment.
// Create a public channel
$channel = Channel::create([
'name' => 'dev-discussion',
'slug' => 'dev-discussion',
'type' => 'public',
'created_by' => auth()->id(),
]);
$channel->members()->attach(auth()->id(), ['role' => 'owner']);
// Create a DM
$dm = auth()->user()->findOrCreateDirectMessage($otherUser);
Channel::query()->where(...) or Conversation::query()->where(...) with Laravel’s query builder for filtering (e.g., archived channels, active DMs).$message = app(SendMessage::class)->execute(
messageable: $channel, // Channel or Conversation
userId: auth()->id(),
body: 'Hello @Jordan! Check this `code`.',
files: [new UploadedFile(...)] // Optional
);
parentId to nest replies:
app(SendMessage::class)->execute(
messageable: $channel,
userId: auth()->id(),
body: 'Reply to parent',
parentId: $parentMessage->id
);
app(ToggleReaction::class)->execute($message->id, auth()->id(), '👍');
@user, @channel, or @here in messages. Parse mentions programmatically:
$mentions = $message->mentions()->where('type', 'user')->get();
$results = app(SearchMessages::class)->execute(
userId: auth()->id(),
query: 'deploy',
limit: 20
);
app(MarkAsRead::class)->execute($channel, auth()->id());
config/team-chat.php:
'tenancy' => [
'enabled' => true,
'model' => \App\Models\Team::class,
'resolver' => fn() => Filament::getTenant()->id,
],
$channel = Channel::where('team_id', $teamId)->first();
vendor/qalainau/filament-team-chat/resources/views to resources/views/vendor/filament/team-chat.resources/css/filament/admin/theme.css).// Example: Test DM creation
public function test_dm_creation()
{
$user1 = User::factory()->create();
$user2 = User::factory()->create();
$dm = $user1->findOrCreateDirectMessage($user2);
$this->assertInstanceOf(Conversation::class, $dm);
}
team-chat.message.sent events (dispatches in SendMessage action).Filament\TeamChat\Notifications\MessageNotification for custom alerts.Polling Delays:
config/team-chat.php).'messages' => 2) for critical paths, but monitor server load. For high-traffic apps, consider adding WebSocket support via laravel-websockets.Tailwind Conflicts:
!important in your theme or use arbitrary variants:
.team-chat-custom { @apply !important; }
File Uploads:
uploads.max_size (default: 10MB) and set up disk cleanup (e.g., tc_attachments table pruning).Multi-Tenancy Quirks:
Filament::getTenant() returns null.'resolver' => fn() => auth()->user()->team_id ?? null,
Mentions Parsing:
@user mentions don’t resolve if the user’s name or email changes.user()->name consistently and update mentions via a queue job after user updates.Livewire State Persistence:
wire:key is unique in Livewire components (e.g., wire:key="thread-{{ $message->id }}").Database Locking:
tc_messages or tc_read_receipts cause deadlocks.DB::transaction(function () use ($message) {
$message->reactions()->create([...]);
});
Table Prefix:
tc_ to a custom prefix (e.g., app_tc_) if your database already uses tc_ for other tables.User Model:
Filament\TeamChat\Contracts\User:
use Filament\TeamChat\Contracts\User as TeamChatUser;
class User implements TeamChatUser { ... }
Dark Mode:
.dark .team-chat-dark { @apply bg-gray-800; }
Notifications Table:
php artisan make:notifications-table, @mentions and DM alerts will be silently ignored. Add it later with:
php artisan make:notifications-table --force
Custom Reactions:
reactions table and override the ToggleReaction action to support custom emojis (e.g., :rocket:).Message Validation:
Filament\TeamChat\Actions\SendMessage to add custom rules (e.g., block profanity):
public function rules(): array
{
return array_merge(parent::rules(), [
'body' => ['required', 'max:2000', 'profanity'], // Custom rule
]);
}
Channel Types:
type column to tc_channels (e.g., announcement, private) and filter in the sidebar:
Channel::query()->where('type', 'announcement')->get();
Message Events:
SendMessage:
event(new MessageSent($message));
How can I help you explore Laravel packages today?