diff --git a/config/acl.yaml b/config/acl.yaml index abd73118..e4fcc744 100644 --- a/config/acl.yaml +++ b/config/acl.yaml @@ -429,3 +429,11 @@ access_keys: - id: analytics.forecast label: Forecasting + - id: notifications + label: Notifications + acls: + - id: notification.menu + label: Menu + - id: notification.list + label: List + diff --git a/config/menu.yaml b/config/menu.yaml index 9f28b315..0310fc36 100644 --- a/config/menu.yaml +++ b/config/menu.yaml @@ -177,3 +177,12 @@ main_menu: acl: review.list label: Reviews parent: partner + + - id: notification + acl: notification.menu + label: Notifications + icon: flaticon-bell + - id: notification_list + acl: notification.list + label: Notifications + parent: notification diff --git a/config/routes/notification.yaml b/config/routes/notification.yaml new file mode 100644 index 00000000..86023d64 --- /dev/null +++ b/config/routes/notification.yaml @@ -0,0 +1,14 @@ +notification_list: + path: /notifications + controller: App\Controller\NotificationController::index + +notification_rows: + path: /notifications/rows + controller: App\Controller\NotificationController::rows + methods: [POST] + +# ajax call +notification_ajax_read: + path: /notifications/read + controller: App\Controller\NotificationController::markNotificationRead + methods: [POST] diff --git a/src/Controller/NotificationController.php b/src/Controller/NotificationController.php new file mode 100644 index 00000000..75ea2812 --- /dev/null +++ b/src/Controller/NotificationController.php @@ -0,0 +1,129 @@ +denyAccessUnlessGranted('notification.list', null, 'No access.'); + + return $this->render('notification/list.html.twig'); + } + + public function rows(Request $req) + { + $this->denyAccessUnlessGranted('notification.list', null, 'No access.'); + + // get current user + $user = $this->getUser(); + + // build query + $qb = $this->getDoctrine() + ->getRepository(Notification::class) + ->createQueryBuilder('q'); + + // get datatable params + $datatable = $req->request->get('datatable'); + + // count total records + $tquery = $qb->select('COUNT(q)'); + + // find by user + $tquery->andWhere('q.user = :user') + ->setParameter('user', $user); + + $total = $tquery->getQuery() + ->getSingleScalarResult(); + + // get current page number + $page = $datatable['pagination']['page'] ?? 1; + + $perpage = $datatable['pagination']['perpage']; + $offset = ($page - 1) * $perpage; + + // add metadata + $meta = [ + 'page' => $page, + 'perpage' => $perpage, + 'pages' => ceil($total / $perpage), + 'total' => $total, + 'sort' => 'asc', + 'field' => 'id' + ]; + + // build query + $query = $qb->select('q'); + + // find by user + $query->andWhere('q.user = :user') + ->setParameter('user', $user); + + // order by date_create desc + $query->orderBy('q.date_create', 'desc'); + + // get rows for this page + $obj_rows = $query->setFirstResult($offset) + ->setMaxResults($perpage) + ->getQuery() + ->getResult(); + + // process rows + $rows = []; + foreach ($obj_rows as $orow) { + // add row data + $row['id'] = $orow->getID(); + $row['message'] = $orow->getMessage(); + $row['notif_type'] = $orow->getNotificationTypeName(); + $row['jo_id'] = $orow->getJobOrder()->getID(); + $row['flag_read'] = $orow->isNotificationRead(); + + // add row metadata + $row['meta']['view_jo_url'] = $this->generateUrl('jo_walkin_edit_form', ['id' => $row['jo_id']]); + + $rows[] = $row; + } + + // response + return $this->json([ + 'meta' => $meta, + 'data' => $rows + ]); + } + + public function markNotificationRead(EntityManagerInterface $em, Request $req) + { + $id = $req->request->get('id'); + + $notif = $em->getRepository(Notification::class)->find($id); + // make sure this notification exists + if (empty($notif)) { + return $this->json([ + 'success' => false, + 'errors' => 'No notification found.', + ], 422); + } + + $notif->setReadNotification(); + + $em->flush(); + + return $this->json([ + 'success' => 'Changes have been saved!' + ]); + } +} diff --git a/src/Entity/JobOrder.php b/src/Entity/JobOrder.php index 718c70ee..4bfcbcf5 100644 --- a/src/Entity/JobOrder.php +++ b/src/Entity/JobOrder.php @@ -346,6 +346,12 @@ class JobOrder */ protected $date_status_change; + // notifications for this job order + /** + * @ORM\OneToMany(targetEntity="Notification", mappedBy="job_order") + */ + protected $jo_notifications; + public function __construct() { $this->date_create = new DateTime(); @@ -368,6 +374,8 @@ class JobOrder $this->meta = []; $this->phone_mobile = ''; + + $this->jo_notifications = new ArrayCollection(); } public function getID() @@ -996,4 +1004,9 @@ class JobOrder return $this->jo_extra; } + public function getNotifications() + { + return $this->jo_notifications; + } + } diff --git a/src/Entity/Notification.php b/src/Entity/Notification.php new file mode 100644 index 00000000..82a5d3a6 --- /dev/null +++ b/src/Entity/Notification.php @@ -0,0 +1,145 @@ +flag_read = false; + $this->date_create = new DateTime(); + } + + public function getID() + { + return $this->id; + } + + public function setDateCreate(DateTime $date_create) + { + $this->date_create = $date_create; + return $this; + } + + public function getDateCreate() + { + return $this->date_create; + } + + public function setMessage($message) + { + $this->message = $message; + return $this; + } + + public function getMessage() + { + return $this->message; + } + + public function isNotificationRead() + { + return $this->flag_read; + } + + public function setReadNotification($bool = true) + { + $this->flag_read = $bool; + return $this; + } + + public function setUser(User $user) + { + $this->user = $user; + return $this; + } + + public function getUser() + { + return $this->user; + } + + public function setJobOrder(JobOrder $jo) + { + $this->job_order = $jo; + return $this; + } + + public function getJobOrder() + { + return $this->job_order; + } + + public function setNotificationType($notif_type) + { + $this->notif_type = $notif_type; + return $this; + } + + public function getNotificationType() + { + return $this->notif_type; + } + + public function getNotificationTypeName() + { + return NotificationType::getName($this->notif_type); + } + +} diff --git a/src/Entity/User.php b/src/Entity/User.php index a57afb97..be0d59f2 100644 --- a/src/Entity/User.php +++ b/src/Entity/User.php @@ -110,6 +110,18 @@ class User extends BaseUser implements Serializable */ protected $partners_created; + // notifications for this user + /** + * @ORM\OneToMany(targetEntity="Notification", mappedBy="user", indexBy="id") + */ + protected $user_notifications; + + // flag to check if user has read notifications + /** + * @ORM\Column(type="boolean") + */ + protected $flag_read_notifications; + public function __construct() { parent::__construct(); @@ -119,6 +131,9 @@ class User extends BaseUser implements Serializable $this->job_orders_assigned = new ArrayCollection(); $this->tickets = new ArrayCollection(); $this->partners_created = new ArrayCollection(); + $this->user_notifications = new ArrayCollection(); + + $this->flag_read_notifications = false; } public function getID() @@ -309,4 +324,21 @@ class User extends BaseUser implements Serializable { return $this->partners_created; } + + public function getNotifications() + { + return $this->user_notifications; + } + + public function isNotificationsRead() + { + return $this->flag_read_notifications; + } + + public function setReadNotifications($bool = true) + { + $this->flag_read_notifications = $bool; + return $this; + } + } diff --git a/src/Ramcar/NotificationType.php b/src/Ramcar/NotificationType.php new file mode 100644 index 00000000..630cda7b --- /dev/null +++ b/src/Ramcar/NotificationType.php @@ -0,0 +1,14 @@ + 'Cancellation', + 'rejection' => 'Rejection', + ]; +} diff --git a/src/Service/RiderAPIHandler/CMBRiderAPIHandler.php b/src/Service/RiderAPIHandler/CMBRiderAPIHandler.php index bd723ab4..84656d98 100644 --- a/src/Service/RiderAPIHandler/CMBRiderAPIHandler.php +++ b/src/Service/RiderAPIHandler/CMBRiderAPIHandler.php @@ -3,6 +3,7 @@ namespace App\Service\RiderAPIHandler; use Doctrine\ORM\EntityManagerInterface; + use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Security\Core\Encoder\EncoderFactoryInterface; @@ -14,6 +15,7 @@ use App\Ramcar\InvoiceStatus; use App\Ramcar\CMBModeOfPayment; use App\Ramcar\InvoiceCriteria; use App\Ramcar\CMBCancelReason; +use App\Ramcar\NotificationType; use App\Service\RiderAPIHandlerInterface; use App\Service\RedisClientProvider; @@ -33,6 +35,7 @@ use App\Entity\BatteryModel; use App\Entity\BatterySize; use App\Entity\JobOrder; use App\Entity\JOExtra; +use App\Entity\Notification; use DateTime; use DateInterval; @@ -865,6 +868,14 @@ class CMBRiderAPIHandler implements RiderAPIHandlerInterface ->setRider($rider); $this->em->persist($event); + // add to notifications + $notif = new Notification(); + $notif->setJobOrder($jo) + ->setMessage($rider->getFullName() . ' cancelled Job Order# ' . $jo->getID()) + ->setNotificationType(NotificationType::CANCELLATION) + ->setUser($jo->getCreatedBy()); + $this->em->persist($notif); + $this->em->flush(); return $data; @@ -924,6 +935,14 @@ class CMBRiderAPIHandler implements RiderAPIHandlerInterface ->setRider($rider); $this->em->persist($event); + // add to notifications + $notif = new Notification(); + $notif->setJobOrder($jo) + ->setMessage($rider->getFullName() . ' rejected Job Order# ' . $jo->getID()) + ->setNotificationType(NotificationType::REJECTION) + ->setUser($jo->getCreatedBy()); + $this->em->persist($notif); + $this->em->flush(); // send mqtt event diff --git a/templates/notification/list.html.twig b/templates/notification/list.html.twig new file mode 100644 index 00000000..4840ec43 --- /dev/null +++ b/templates/notification/list.html.twig @@ -0,0 +1,118 @@ +{% extends 'base.html.twig' %} + +{% block body %} + +