Compare commits
No commits in common. "b2a7e2557ec8e05cca50effc2ef1c0df25c9b9c1" and "371b75193ae164cdac63db3d699cf26f3dff3534" have entirely different histories.
b2a7e2557e
...
371b75193a
@ -11,10 +11,10 @@ class DashboardController extends Controller
|
|||||||
public function index(Request $request, ActivityService $activityService)
|
public function index(Request $request, ActivityService $activityService)
|
||||||
{
|
{
|
||||||
$user = $request->user();
|
$user = $request->user();
|
||||||
$unreadDynamics = $activityService->getUnreadEntitiesGrouped($user);
|
$unreadEntities = $activityService->getUnreadEntitiesGrouped($user);
|
||||||
|
|
||||||
return Inertia::render('Dashboard', [
|
return Inertia::render('Dashboard', [
|
||||||
'unreadDynamics' => $unreadDynamics,
|
'unreadEntities' => $unreadEntities,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -30,7 +30,7 @@ class NewActivityNotification extends Notification
|
|||||||
*/
|
*/
|
||||||
public function via(object $notifiable): array
|
public function via(object $notifiable): array
|
||||||
{
|
{
|
||||||
return ['database', WebPushChannel::class];
|
return [WebPushChannel::class];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -46,6 +46,15 @@ class NewActivityNotification extends Notification
|
|||||||
->action('View', 'view')
|
->action('View', 'view')
|
||||||
->data(['url' => $this->activity['url']]);
|
->data(['url' => $this->activity['url']]);
|
||||||
|
|
||||||
|
switch (get_class($this->activity)) {
|
||||||
|
case Message::class:
|
||||||
|
/** @var Chat $chat */
|
||||||
|
$chat = $this->activity->chat;
|
||||||
|
|
||||||
|
$result->data(['url' => $chat->subjectUrl]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -57,8 +66,7 @@ class NewActivityNotification extends Notification
|
|||||||
public function toArray(object $notifiable): array
|
public function toArray(object $notifiable): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'content' => $this->activity['content'],
|
//
|
||||||
'url' => $this->activity['url'],
|
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,67 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Policies;
|
|
||||||
|
|
||||||
use App\Models\Chat;
|
|
||||||
use App\Models\User;
|
|
||||||
use Illuminate\Auth\Access\Response;
|
|
||||||
|
|
||||||
class ChatPolicy
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Determine whether the user can view any models.
|
|
||||||
*/
|
|
||||||
public function viewAny(User $user): bool
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determine whether the user can view the model.
|
|
||||||
*/
|
|
||||||
public function view(User $user, Chat $chat): bool
|
|
||||||
{
|
|
||||||
$chatable = $chat->chatable;
|
|
||||||
return $user->can('view', $chatable);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determine whether the user can create models.
|
|
||||||
*/
|
|
||||||
public function create(User $user): bool
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determine whether the user can update the model.
|
|
||||||
*/
|
|
||||||
public function update(User $user, Chat $chat): bool
|
|
||||||
{
|
|
||||||
return $this->view($user, $chat);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determine whether the user can delete the model.
|
|
||||||
*/
|
|
||||||
public function delete(User $user, Chat $chat): bool
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determine whether the user can restore the model.
|
|
||||||
*/
|
|
||||||
public function restore(User $user, Chat $chat): bool
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determine whether the user can permanently delete the model.
|
|
||||||
*/
|
|
||||||
public function forceDelete(User $user, Chat $chat): bool
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,31 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
use Illuminate\Database\Migrations\Migration;
|
|
||||||
use Illuminate\Database\Schema\Blueprint;
|
|
||||||
use Illuminate\Support\Facades\Schema;
|
|
||||||
|
|
||||||
return new class extends Migration
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Run the migrations.
|
|
||||||
*/
|
|
||||||
public function up(): void
|
|
||||||
{
|
|
||||||
Schema::create('notifications', function (Blueprint $table) {
|
|
||||||
$table->uuid('id')->primary();
|
|
||||||
$table->string('type');
|
|
||||||
$table->morphs('notifiable');
|
|
||||||
$table->text('data');
|
|
||||||
$table->timestamp('read_at')->nullable();
|
|
||||||
$table->timestamps();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reverse the migrations.
|
|
||||||
*/
|
|
||||||
public function down(): void
|
|
||||||
{
|
|
||||||
Schema::dropIfExists('notifications');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
@ -1,15 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<phpunit backupGlobals="false"
|
|
||||||
beStrictAboutTestsThatDoNotTestAnything="false"
|
|
||||||
colors="true"
|
|
||||||
processIsolation="false"
|
|
||||||
stopOnError="false"
|
|
||||||
stopOnFailure="false"
|
|
||||||
cacheDirectory=".phpunit.cache"
|
|
||||||
backupStaticProperties="false">
|
|
||||||
<testsuites>
|
|
||||||
<testsuite name="Browser Test Suite">
|
|
||||||
<directory suffix="Test.php">./tests/Browser</directory>
|
|
||||||
</testsuite>
|
|
||||||
</testsuites>
|
|
||||||
</phpunit>
|
|
||||||
@ -104,7 +104,6 @@ function submit() {
|
|||||||
id="amount"
|
id="amount"
|
||||||
type="number"
|
type="number"
|
||||||
class="c-add-mutation-form__input"
|
class="c-add-mutation-form__input"
|
||||||
data-test="amount-input"
|
|
||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
v-if="form.errors.amount"
|
v-if="form.errors.amount"
|
||||||
@ -123,7 +122,6 @@ function submit() {
|
|||||||
id="description"
|
id="description"
|
||||||
rows="4"
|
rows="4"
|
||||||
class="c-add-mutation-form__textarea"
|
class="c-add-mutation-form__textarea"
|
||||||
data-test="description-input"
|
|
||||||
></textarea>
|
></textarea>
|
||||||
<div
|
<div
|
||||||
v-if="form.errors.description"
|
v-if="form.errors.description"
|
||||||
@ -173,7 +171,6 @@ function submit() {
|
|||||||
type="submit"
|
type="submit"
|
||||||
:disabled="form.processing"
|
:disabled="form.processing"
|
||||||
class="c-add-mutation-form__submit-btn"
|
class="c-add-mutation-form__submit-btn"
|
||||||
data-test="add-mutation-button"
|
|
||||||
>
|
>
|
||||||
Add Mutation
|
Add Mutation
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@ -14,7 +14,7 @@ defineOptions({
|
|||||||
});
|
});
|
||||||
|
|
||||||
defineProps<{
|
defineProps<{
|
||||||
unreadDynamics: Array<{
|
unreadEntities: Array<{
|
||||||
id: number;
|
id: number;
|
||||||
name: string;
|
name: string;
|
||||||
url: string;
|
url: string;
|
||||||
@ -50,17 +50,15 @@ function formatTime(isoString: string): string {
|
|||||||
<div class="c-dashboard__container">
|
<div class="c-dashboard__container">
|
||||||
<h2 class="c-dashboard__title">Recent Activity</h2>
|
<h2 class="c-dashboard__title">Recent Activity</h2>
|
||||||
|
|
||||||
<div v-if="unreadDynamics.length > 0" class="c-dashboard__grid">
|
<div v-if="unreadEntities.length > 0" class="c-dashboard__grid">
|
||||||
<div
|
<div
|
||||||
v-for="dynamic in unreadDynamics"
|
v-for="entity in unreadEntities"
|
||||||
:key="dynamic.id"
|
:key="entity.id"
|
||||||
class="c-dashboard__card"
|
class="c-dashboard__card"
|
||||||
>
|
>
|
||||||
<div class="c-dashboard__card-header">
|
<div class="c-dashboard__card-header">
|
||||||
<div class="c-dashboard__entity-meta">
|
<div class="c-dashboard__entity-meta">
|
||||||
<span
|
<span class="c-dashboard__badge-type c-dashboard__badge-type--dynamic">
|
||||||
class="c-dashboard__badge-type c-dashboard__badge-type--dynamic"
|
|
||||||
>
|
|
||||||
Dynamic
|
Dynamic
|
||||||
</span>
|
</span>
|
||||||
<span class="c-dashboard__unread-count">
|
<span class="c-dashboard__unread-count">
|
||||||
@ -104,7 +102,9 @@ function formatTime(isoString: string): string {
|
|||||||
v-if="dynamic.new_activities.length > 0"
|
v-if="dynamic.new_activities.length > 0"
|
||||||
class="c-dashboard__divider"
|
class="c-dashboard__divider"
|
||||||
>
|
>
|
||||||
<span class="c-dashboard__divider-text">NEW</span>
|
<span class="c-dashboard__divider-text"
|
||||||
|
>NEW</span
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- New / Unread Activities -->
|
<!-- New / Unread Activities -->
|
||||||
|
|||||||
@ -8,5 +8,5 @@ Broadcast::channel('App.Models.User.{id}', function ($user, $id) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
Broadcast::channel('chats.{chat}', function ($user, Chat $chat) {
|
Broadcast::channel('chats.{chat}', function ($user, Chat $chat) {
|
||||||
return $user?->can('view', $chat);
|
return $user->can('view', $chat->chatable);
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,78 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Tests\Browser;
|
|
||||||
|
|
||||||
use App\Models\Dynamic;
|
|
||||||
use App\Models\Ledger;
|
|
||||||
use App\Models\Mutation;
|
|
||||||
use App\Models\User;
|
|
||||||
use Illuminate\Foundation\Testing\DatabaseMigrations;
|
|
||||||
use Laravel\Dusk\Browser;
|
|
||||||
use Tests\DuskTestCase;
|
|
||||||
|
|
||||||
class NotificationTest extends DuskTestCase
|
|
||||||
{
|
|
||||||
use DatabaseMigrations;
|
|
||||||
|
|
||||||
protected function setUp(): void
|
|
||||||
{
|
|
||||||
parent::setUp();
|
|
||||||
$this->artisan('migrate:fresh', ['--seed' => true]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A basic browser test example.
|
|
||||||
*/
|
|
||||||
public function test_it_shows_notifications_on_mutation_activity(): void
|
|
||||||
{
|
|
||||||
$owner = User::factory()->create();
|
|
||||||
$participant = User::factory()->create();
|
|
||||||
$dynamic = Dynamic::factory()->create();
|
|
||||||
$dynamic->chat()->create();
|
|
||||||
$dynamic->participants()->attach($owner->id, ['role' => 'owner']);
|
|
||||||
$dynamic->participants()->attach($participant->id, ['role' => 'participant']);
|
|
||||||
$ledger = Ledger::factory()->create(['dynamic_id' => $dynamic->id]);
|
|
||||||
|
|
||||||
$this->browse(function (Browser $ownerBrowser, Browser $participantBrowser) use ($owner, $participant, $dynamic, $ledger) {
|
|
||||||
$ownerBrowser->loginAs($owner)
|
|
||||||
->visit(route('dynamics.ledgers.show', [$dynamic, $ledger]))
|
|
||||||
->waitForText($ledger->name)
|
|
||||||
->assertSee($ledger->name)
|
|
||||||
->script([
|
|
||||||
"window.notifications = [];",
|
|
||||||
"window.Notification = function(title, options) { window.notifications.push({title, options}); };",
|
|
||||||
]);
|
|
||||||
|
|
||||||
$participantBrowser->loginAs($participant)
|
|
||||||
->visit(route('dynamics.ledgers.show', [$dynamic, $ledger]))
|
|
||||||
->waitForText('Add Mutation')
|
|
||||||
->type('[data-test="description-input"]', 'A new task suggestion')
|
|
||||||
->type('[data-test="amount-input"]', 10)
|
|
||||||
->press('[data-test="add-mutation-button"]')
|
|
||||||
->waitForText('A new task suggestion');
|
|
||||||
|
|
||||||
$ownerBrowser->pause(1000);
|
|
||||||
$lastNotification = $ownerBrowser->script("return window.notifications.pop();")[0] ?? null;
|
|
||||||
$this->assertNotNull($lastNotification, "Owner did not receive the 'new suggestion' notification.");
|
|
||||||
$this->assertEquals('New Activity', $lastNotification['title']);
|
|
||||||
$this->assertStringContainsString('suggested a new entry', $lastNotification['options']['body']);
|
|
||||||
|
|
||||||
$mutation = Mutation::where('description', 'A new task suggestion')->first();
|
|
||||||
$ownerBrowser->press("#mutation-{$mutation->id}-approve")
|
|
||||||
->waitForText('APPROVED');
|
|
||||||
|
|
||||||
$participantBrowser->script([
|
|
||||||
"window.notifications = [];",
|
|
||||||
"window.Notification = function(title, options) { window.notifications.push({title, options}); };",
|
|
||||||
]);
|
|
||||||
|
|
||||||
$ownerBrowser->pause(1000);
|
|
||||||
|
|
||||||
$participantBrowser->pause(1000);
|
|
||||||
$lastNotification = $participantBrowser->script("return window.notifications.pop();")[0] ?? null;
|
|
||||||
$this->assertNotNull($lastNotification, "Participant did not receive the 'approved' notification.");
|
|
||||||
$this->assertEquals('New Activity', $lastNotification['title']);
|
|
||||||
$this->assertStringContainsString('APPROVED the suggestion', $lastNotification['options']['body']);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -31,7 +31,7 @@ abstract class DuskTestCase extends BaseTestCase
|
|||||||
$options = (new ChromeOptions)->addArguments((new Collection([
|
$options = (new ChromeOptions)->addArguments((new Collection([
|
||||||
$this->shouldStartMaximized() ? '--start-maximized' : '--window-size=1920,1080',
|
$this->shouldStartMaximized() ? '--start-maximized' : '--window-size=1920,1080',
|
||||||
'--disable-gpu',
|
'--disable-gpu',
|
||||||
// '--headless=new',
|
'--headless=new',
|
||||||
'--no-sandbox',
|
'--no-sandbox',
|
||||||
'--disable-dev-shm-usage',
|
'--disable-dev-shm-usage',
|
||||||
]))->unless(static::runningInSail(), function (Collection $arguments) {
|
]))->unless(static::runningInSail(), function (Collection $arguments) {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user