Browse Source

Approval flow support for in-site mail & in-site mail content refinement

moell 8 months ago
parent
commit
31048caa47

+ 9 - 19
app/Listeners/SendActionBrowserNotification.php

@@ -9,6 +9,9 @@ use App\Models\Enums\ObjectAction;
 use App\Models\Notification;
 use App\Models\NotificationRecord;
 use App\Repositories\ConfigRepository;
+use App\Services\Notification\ActionBrowser\NormalNotification;
+use App\Services\Notification\ActionBrowser\RequirementNotification;
+use App\Services\Notification\ActionBrowser\TaskNotification;
 use Illuminate\Contracts\Queue\ShouldQueue;
 use Illuminate\Queue\InteractsWithQueue;
 
@@ -37,27 +40,14 @@ class SendActionBrowserNotification implements ShouldQueue
             return;
         }
 
-        $userIds = match ($actionObjectType) {
-            ActionObjectType::TASK => $object->assign > 0 ? [$object->assign] : [],
-            ActionObjectType::REQUIREMENT=>$object->reviewed_by > 0 ? [$object->reviewed_by ] :[],
-            default => [],
+        $notification = match ($actionObjectType) {
+            ActionObjectType::PROJECT, ActionObjectType::CONTAINER  => new NormalNotification($event->action, $object),
+            ActionObjectType::REQUIREMENT => new RequirementNotification($event->action, $object),
+            ActionObjectType::TASK => new TaskNotification($event->action, $object),
+            default => null,
         };
 
-        if (! $userIds) {
-            return;
-        }
-
-        $notification = Notification::query()->create([
-            'object_type' => NotificationObjectType::ACTION->value,
-            'object_id' => $event->action->id,
-        ]);
-
-        foreach ($userIds as $userId) {
-            NotificationRecord::query()->create([
-                'notification_id' => $notification->id,
-                'user_id' => $userId,
-            ]);
-        }
+        $notification?->handle();
     }
 
     public function shouldQueue(ObjectActionCreate $event): bool

+ 23 - 0
app/Models/Enums/ObjectAction.php

@@ -88,8 +88,25 @@ enum ObjectAction: string
 
     case APPROVED_CANCELED ='approvedCanceled';
 
+    public function isApproval()
+    {
+        return in_array($this->value, [
+            self::APPROVAL_REQUEST->value,
+            self::APPROVAL_APPROVED->value,
+            self::APPROVAL_REJECTED->value,
+            self::APPROVED_TO_NEXT_NODE->value,
+        ]);
+    }
+
     public static function messageNotificationItems()
     {
+        $approval = [
+            self::APPROVAL_REQUEST,
+            self::APPROVAL_APPROVED,
+            self::APPROVAL_REJECTED,
+            self::APPROVED_TO_NEXT_NODE,
+        ];
+
         return [
             ActionObjectType::REQUIREMENT->value => [
                 self::CREATED,
@@ -97,6 +114,7 @@ enum ObjectAction: string
                 self::STARTED,
                 self::CHANGED,
                 self::CLOSED,
+                ...$approval,
             ],
             ActionObjectType::TASK->value => [
                 ObjectAction::STARTED,
@@ -105,10 +123,15 @@ enum ObjectAction: string
                 ObjectAction::DONE,
                 ObjectAction::CANCELED,
                 ObjectAction::EDITED,
+                ...$approval,
             ],
             ActionObjectType::CONTAINER->value => [
                 ObjectAction::CREATED,
                 ObjectAction::EDITED,
+                ...$approval,
+            ],
+            ActionObjectType::PROJECT->value => [
+                ...$approval,
             ]
         ];
     }

+ 4 - 0
app/Models/Notification.php

@@ -11,4 +11,8 @@ class Notification extends Model
     use HasFactory, Filterable;
 
     protected $guarded = ['id'];
+
+    protected $casts = [
+        'content' => 'array'
+    ];
 }

+ 9 - 1
app/Services/Approval/ActionService.php

@@ -12,6 +12,8 @@ class ActionService
 {
     protected ?Approval $approval = null;
 
+    protected array $actionExtraFields = [];
+
     public function action(Approval $approval, int $status, string $comment = null): void
     {
         $this->approval = $approval;
@@ -54,7 +56,8 @@ class ActionService
         ActionRepository::createByApproval(
             $this->approval,
             $objectAction,
-            $comment
+            $comment,
+            extraFields: $this->actionExtraFields
         );
     }
 
@@ -69,6 +72,11 @@ class ActionService
             return;
         }
 
+        $this->actionExtraFields = [
+            'now' => $this->approval->node_level,
+            'next' =>  $nextNodeIndex,
+        ];
+
         $this->approval->node_level = $nextNodeIndex;
         $this->approval->users = sprintf(",%s,", implode(',', $nextNodes['approval_users']));
     }

+ 91 - 0
app/Services/Notification/Abstracts/ActionBrowserNotificationAbstract.php

@@ -0,0 +1,91 @@
+<?php
+
+namespace App\Services\Notification\Abstracts;
+
+use App\Models\Action;
+use App\Models\Approval;
+use App\Models\Enums\ActionObjectType;
+use App\Models\Enums\NotificationObjectType;
+use App\Models\Enums\ObjectAction;
+use App\Models\Notification;
+use App\Models\NotificationRecord;
+use App\Services\Notification\Contacts\ActionBrowserNotificationContacts;
+use App\Services\Notification\NotificationContent;
+use Illuminate\Database\Eloquent\Model;
+
+abstract class ActionBrowserNotificationAbstract implements ActionBrowserNotificationContacts
+{
+    protected ObjectAction $objectAction;
+
+    public function __construct(protected Action $action,  protected Model $object)
+    {
+        $this->objectAction = ObjectAction::tryFrom($this->action->action);
+    }
+
+    public function handle()
+    {
+        if ($this->objectAction->isApproval()) {
+            $this->handleByApprovalAction();
+        } else {
+            $this->handleByObjectAction($this->userIDs());
+        }
+    }
+
+    abstract protected function userIDs(): array;
+
+    protected function handleByApprovalAction()
+    {
+        $approval = Approval::query()->find($this->action->additional_id);
+        if (! $approval) {
+            return;
+        }
+
+        match ($this->objectAction) {
+            ObjectAction::APPROVAL_REQUEST, ObjectAction::APPROVED_TO_NEXT_NODE => $this->notificationNextApprovalUsers($approval),
+            ObjectAction::APPROVAL_APPROVED, ObjectAction::APPROVAL_REJECTED => $this->handleByObjectAction([$approval->create_by]),
+            default => '',
+        };
+    }
+
+    protected function handleByObjectAction(array $userIDs)
+    {
+        $this->storeNotification(
+            new NotificationContent($this->objectAction->value),
+            $userIDs,
+        );
+    }
+
+    protected function storeNotification(NotificationContent $content, array $userIds)
+    {
+        if (! $userIds) {
+            return;
+        }
+
+        $notification =  Notification::query()->create([
+            'object_type' => NotificationObjectType::ACTION->value,
+            'object_id' => $this->action->id,
+            'content' => $content->toArray()
+        ]);
+
+        foreach ($userIds as $userId) {
+            NotificationRecord::query()->create([
+                'notification_id' => $notification->id,
+                'user_id' => $userId,
+            ]);
+        }
+    }
+
+    protected function notificationNextApprovalUsers(Approval $approval)
+    {
+        $approvalFlow = $approval->approvalFlow()->first();
+        if (! $approvalFlow) {
+            return;
+        }
+
+        $userIds = $approvalFlow->nodes[$this->action->extra_fields['next']]['approval_users'] ?? [];
+
+        $content = new NotificationContent("waitingForMyApproval");
+
+        $this->storeNotification($content, $userIds);
+    }
+}

+ 13 - 0
app/Services/Notification/ActionBrowser/NormalNotification.php

@@ -0,0 +1,13 @@
+<?php
+
+namespace App\Services\Notification\ActionBrowser;
+
+use App\Services\Notification\Abstracts\ActionBrowserNotificationAbstract;
+
+class NormalNotification extends ActionBrowserNotificationAbstract
+{
+    protected function userIDs(): array
+    {
+        return [];
+    }
+}

+ 13 - 0
app/Services/Notification/ActionBrowser/RequirementNotification.php

@@ -0,0 +1,13 @@
+<?php
+
+namespace App\Services\Notification\ActionBrowser;
+
+use App\Services\Notification\Abstracts\ActionBrowserNotificationAbstract;
+
+class RequirementNotification extends ActionBrowserNotificationAbstract
+{
+    protected function userIDs(): array
+    {
+        return $this->object->reviewed_by > 0 ? [$this->object->reviewed_by ] :[];
+    }
+}

+ 13 - 0
app/Services/Notification/ActionBrowser/TaskNotification.php

@@ -0,0 +1,13 @@
+<?php
+
+namespace App\Services\Notification\ActionBrowser;
+
+use App\Services\Notification\Abstracts\ActionBrowserNotificationAbstract;
+
+class TaskNotification extends ActionBrowserNotificationAbstract
+{
+    protected function userIDs(): array
+    {
+        return $this->object->assign > 0 ? [$this->object->assign] : [];
+    }
+}

+ 1 - 0
app/Services/Notification/ActionEmail/ActionEmailService.php

@@ -48,6 +48,7 @@ class ActionEmailService
             ActionObjectType::REQUIREMENT => $this->requirement($actionObjectModel),
             ActionObjectType::TASK => $this->task($actionObjectModel),
             ActionObjectType::CONTAINER => $this->container($actionObjectModel),
+            default => null,
         };
     }
 

+ 13 - 0
app/Services/Notification/Contacts/ActionBrowserNotificationContacts.php

@@ -0,0 +1,13 @@
+<?php
+
+namespace App\Services\Notification\Contacts;
+
+use App\Models\Action;
+use Illuminate\Database\Eloquent\Model;
+
+interface ActionBrowserNotificationContacts
+{
+    public function __construct(Action $action,  Model $object);
+
+    public function handle();
+}

+ 17 - 0
app/Services/Notification/NotificationContent.php

@@ -0,0 +1,17 @@
+<?php
+
+namespace App\Services\Notification;
+
+class NotificationContent
+{
+    public function __construct(protected string $action)
+    {
+    }
+
+    public function toArray()
+    {
+        return [
+            'action' => $this->action,
+        ];
+    }
+}

+ 28 - 0
database/migrations/2024_06_28_233556_change_content_to_notifications_tables.php

@@ -0,0 +1,28 @@
+<?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::table('notifications', function (Blueprint $table) {
+            $table->json("content")->nullable()->change();
+        });
+    }
+
+    /**
+     * Reverse the migrations.
+     */
+    public function down(): void
+    {
+        Schema::table('notifications', function (Blueprint $table) {
+            $table->text('content')->nullable()->change();
+        });
+    }
+};

+ 2 - 0
lang/en/action-labels.php

@@ -48,5 +48,7 @@ return[
         ObjectAction::APPROVAL_REJECTED->value => "approval rejected",
         ObjectAction::APPROVED_CANCELED->value => "approval canceled",
         ObjectAction::APPROVED_TO_NEXT_NODE->value => "approved to next step",
+
+        "waitingForMyApproval" => "Waiting for my approval",
     ]
 ];