diff --git a/app/Http/Controllers/DynamicController.php b/app/Http/Controllers/DynamicController.php index d6ac56d..ef12316 100644 --- a/app/Http/Controllers/DynamicController.php +++ b/app/Http/Controllers/DynamicController.php @@ -46,7 +46,7 @@ class DynamicController extends Controller { $this->authorize('view', $dynamic); - $dynamic->load('ledgers', 'participants'); + $dynamic->load('ledgers', 'participants', 'chat.messages.user'); return Inertia::render('Dynamics/Show', [ 'dynamic' => $dynamic, diff --git a/app/Http/Controllers/LedgerController.php b/app/Http/Controllers/LedgerController.php index b1bd75c..b20414a 100644 --- a/app/Http/Controllers/LedgerController.php +++ b/app/Http/Controllers/LedgerController.php @@ -43,7 +43,7 @@ class LedgerController extends Controller { $this->authorize('view', $ledger); - $ledger->load('mutations.user'); + $ledger->load('mutations.user', 'mutations.chat.messages.user'); return Inertia::render('Ledgers/Show', [ 'dynamic' => $dynamic, diff --git a/app/Http/Controllers/MessageController.php b/app/Http/Controllers/MessageController.php new file mode 100644 index 0000000..3d5ce97 --- /dev/null +++ b/app/Http/Controllers/MessageController.php @@ -0,0 +1,72 @@ +messages()->create([ + ...$request->validated(), + 'user_id' => $request->user()->id, + ]); + + return redirect()->back(); + } + + /** + * Display the specified resource. + */ + public function show(Message $message) + { + // + } + + /** + * Show the form for editing the specified resource. + */ + public function edit(Message $message) + { + // + } + + /** + * Update the specified resource in storage. + */ + public function update(Request $request, Message $message) + { + // + } + + /** + * Remove the specified resource from storage. + */ + public function destroy(Message $message) + { + // + } +} diff --git a/app/Http/Requests/StoreMessageRequest.php b/app/Http/Requests/StoreMessageRequest.php new file mode 100644 index 0000000..1f2767f --- /dev/null +++ b/app/Http/Requests/StoreMessageRequest.php @@ -0,0 +1,34 @@ +route('chat'); + + return $chat && $this->user()->can('view', $chat->chatable); + } + + /** + * Get the validation rules that apply to the request. + * + * @return array + */ + public function rules(): array + { + return [ + 'content' => ['required', 'string'], + ]; + } +} diff --git a/app/Models/Chat.php b/app/Models/Chat.php new file mode 100644 index 0000000..22584bd --- /dev/null +++ b/app/Models/Chat.php @@ -0,0 +1,24 @@ + */ + use HasFactory; + + public function chatable(): MorphTo + { + return $this->morphTo(); + } + + public function messages(): HasMany + { + return $this->hasMany(Message::class); + } +} diff --git a/app/Models/Dynamic.php b/app/Models/Dynamic.php index b7e8dde..9531506 100644 --- a/app/Models/Dynamic.php +++ b/app/Models/Dynamic.php @@ -7,6 +7,7 @@ use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsToMany; use Illuminate\Database\Eloquent\Relations\HasMany; +use Illuminate\Database\Eloquent\Relations\MorphOne; class Dynamic extends Model { @@ -27,4 +28,16 @@ class Dynamic extends Model { return $this->hasMany(Ledger::class); } + + public function chat(): MorphOne + { + return $this->morphOne(Chat::class, 'chatable'); + } + + protected static function booted(): void + { + static::created(function (Dynamic $dynamic) { + $dynamic->chat()->create([]); + }); + } } diff --git a/app/Models/Message.php b/app/Models/Message.php new file mode 100644 index 0000000..ee804ac --- /dev/null +++ b/app/Models/Message.php @@ -0,0 +1,29 @@ + */ + use HasFactory; + + protected $fillable = [ + 'chat_id', + 'user_id', + 'content', + ]; + + public function chat(): BelongsTo + { + return $this->belongsTo(Chat::class); + } + + public function user(): BelongsTo + { + return $this->belongsTo(User::class); + } +} diff --git a/app/Models/Mutation.php b/app/Models/Mutation.php index 46fda0b..0ffe06a 100644 --- a/app/Models/Mutation.php +++ b/app/Models/Mutation.php @@ -6,6 +6,7 @@ use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; +use Illuminate\Database\Eloquent\Relations\MorphOne; class Mutation extends Model { @@ -30,4 +31,16 @@ class Mutation extends Model { return $this->belongsTo(User::class); } + + public function chat(): MorphOne + { + return $this->morphOne(Chat::class, 'chatable'); + } + + protected static function booted(): void + { + static::created(function (Mutation $mutation) { + $mutation->chat()->create([]); + }); + } } diff --git a/database/factories/ChatFactory.php b/database/factories/ChatFactory.php new file mode 100644 index 0000000..d6f408d --- /dev/null +++ b/database/factories/ChatFactory.php @@ -0,0 +1,24 @@ + + */ +class ChatFactory extends Factory +{ + /** + * Define the model's default state. + * + * @return array + */ + public function definition(): array + { + return [ + // + ]; + } +} diff --git a/database/factories/MessageFactory.php b/database/factories/MessageFactory.php new file mode 100644 index 0000000..8a56204 --- /dev/null +++ b/database/factories/MessageFactory.php @@ -0,0 +1,24 @@ + + */ +class MessageFactory extends Factory +{ + /** + * Define the model's default state. + * + * @return array + */ + public function definition(): array + { + return [ + // + ]; + } +} diff --git a/database/migrations/2026_06_14_223609_create_messages_table.php b/database/migrations/2026_06_14_223609_create_messages_table.php new file mode 100644 index 0000000..dae1866 --- /dev/null +++ b/database/migrations/2026_06_14_223609_create_messages_table.php @@ -0,0 +1,30 @@ +id(); + $table->foreignId('chat_id')->constrained()->cascadeOnDelete(); + $table->foreignId('user_id')->constrained()->cascadeOnDelete(); + $table->text('content'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('messages'); + } +}; diff --git a/database/migrations/2026_06_14_223624_create_chats_table.php b/database/migrations/2026_06_14_223624_create_chats_table.php new file mode 100644 index 0000000..56d8ace --- /dev/null +++ b/database/migrations/2026_06_14_223624_create_chats_table.php @@ -0,0 +1,28 @@ +id(); + $table->morphs('chatable'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('chats'); + } +}; diff --git a/resources/js/components/Chat.vue b/resources/js/components/Chat.vue new file mode 100644 index 0000000..fdfb196 --- /dev/null +++ b/resources/js/components/Chat.vue @@ -0,0 +1,48 @@ + + + diff --git a/resources/js/pages/Dynamics/Show.vue b/resources/js/pages/Dynamics/Show.vue index f84464c..12fab48 100644 --- a/resources/js/pages/Dynamics/Show.vue +++ b/resources/js/pages/Dynamics/Show.vue @@ -1,5 +1,6 @@