From 9afa2dfc6cca8975c7e0cc1e01da88e242759c8e Mon Sep 17 00:00:00 2001 From: Ramon Gutierrez Date: Wed, 17 May 2023 00:30:13 +0800 Subject: [PATCH] Add CRM support for upcoming motolite events #730 --- config/packages/catalyst_auth.yaml | 14 + config/packages/catalyst_menu.yaml | 14 +- config/routes/motolite_event.yaml | 38 +++ public/assets/images/image.gif | Bin 0 -> 2908 bytes .../MotoliteEventController.php | 1 + src/Controller/MotoliteEventController.php | 304 ++++++++++++++++++ src/Entity/MotoliteEvent.php | 6 +- templates/motolite-event/form.html.twig | 223 +++++++++++++ templates/motolite-event/list.html.twig | 155 +++++++++ translations/messages.en.yaml | 3 + 10 files changed, 752 insertions(+), 6 deletions(-) create mode 100644 config/routes/motolite_event.yaml create mode 100755 public/assets/images/image.gif create mode 100644 src/Controller/MotoliteEventController.php create mode 100644 templates/motolite-event/form.html.twig create mode 100644 templates/motolite-event/list.html.twig diff --git a/config/packages/catalyst_auth.yaml b/config/packages/catalyst_auth.yaml index 324d0812..596ce6de 100644 --- a/config/packages/catalyst_auth.yaml +++ b/config/packages/catalyst_auth.yaml @@ -384,6 +384,20 @@ catalyst_auth: - id: partner.delete label: Delete + - id: motolite_event + label: Motolite Events + acls: + - id: motolite_event.menu + label: Menu + - id: motolite_event.list + label: List + - id: motolite_event.add + label: Add + - id: motolite_event.update + label: Update + - id: motolite_event.delete + label: Delete + - id: review label: Reviews acls: diff --git a/config/packages/catalyst_menu.yaml b/config/packages/catalyst_menu.yaml index 328321b8..5d0cb29f 100644 --- a/config/packages/catalyst_menu.yaml +++ b/config/packages/catalyst_menu.yaml @@ -235,11 +235,21 @@ catalyst_menu: label: '[menu.partner.reviews]' parent: partner + - id: motolite_event + acl: motolite_event.menu + label: '[menu.motolite_event]' + icon: flaticon-event-calendar-symbol + order: 13 + - id: motolite_event_list + acl: motolite_event.list + label: '[menu.motolite_event.events]' + parent: motolite_event + - id: analytics acl: analytics.menu label: '[menu.analytics]' icon: flaticon-graphic - order: 13 + order: 14 - id: analytics_forecast_form acl: analytics.forecast label: '[menu.analytics.forecasting]' @@ -249,7 +259,7 @@ catalyst_menu: acl: database.menu label: '[menu.database]' icon: fa fa-database - order: 14 + order: 15 - id: ticket_type_list acl: ticket_type.menu label: '[menu.database.tickettypes]' diff --git a/config/routes/motolite_event.yaml b/config/routes/motolite_event.yaml new file mode 100644 index 00000000..865a6162 --- /dev/null +++ b/config/routes/motolite_event.yaml @@ -0,0 +1,38 @@ +motolite_event_list: + path: /motolite_events + controller: App\Controller\MotoliteEventController::index + +motolite_event_rows: + path: /motolite_events/rows + controller: App\Controller\MotoliteEventController::rows + methods: [POST] + +motolite_event_create: + path: /motolite_events/create + controller: App\Controller\MotoliteEventController::addForm + methods: [GET] + +motolite_event_create_submit: + path: /motolite_events/create + controller: App\Controller\MotoliteEventController::addSubmit + methods: [POST] + +motolite_event_upload_image: + path: /motolite_events/upload + controller: App\Controller\MotoliteEventController::uploadImage + methods: [POST] + +motolite_event_update: + path: /motolite_events/{id} + controller: App\Controller\MotoliteEventController::updateForm + methods: [GET] + +motolite_event_update_submit: + path: /motolite_events/{id} + controller: App\Controller\MotoliteEventController::updateSubmit + methods: [POST] + +motolite_event_delete: + path: /motolite_events/{id} + controller: App\Controller\MotoliteEventController::destroy + methods: [DELETE] diff --git a/public/assets/images/image.gif b/public/assets/images/image.gif new file mode 100755 index 0000000000000000000000000000000000000000..c32e7886b9b4958a494af0bf009edde9c2da1f9e GIT binary patch literal 2908 zcmeH`=RX^Y0)`W!A@m?-i%N}F%h6Eh9=&y1RU=}LT0x~|teUM=1R*4(2{lTM*u-8j zisF(8yoBE>ns*)ZEbCJb#-NBWqEmdX=!P3adBZ`VSavoZf~2EGQ_TQmMJQxj8vGZ{EDg z%F4>j%zXX&bw)-;dU|?lYHCVK3WY);lgT6!DJdx_F)@)yBoYXOgoK3n_;@@X9~T!F z8ykzm;bLN9qNAguqN1=^Y-D6)L_|b*c=(GKFT%pYLPJACLPCOrgM)&Co|pQlfsdV70&d3kwydU|+xxVyV!Fc>#CH&<6z7Z(?2XJ;oT zCr3xeCr_R@I5<3h{Mg>!-p*47q`PH{wa?z?DCft$8{)p^}Asr$UJ~qKg1ByN0V7$?tjv~CTR4ygckIv7tt%SJRQ2DOX4DlA=cq?R zB#`wbqxp#Bn|A&6r5}oo^O0|m4P_t8t?Jxo`Wwn8s~ox$B=j0-pX=N{l-Uh5R!lYd zEcL(9YpR@S4cgzB8EC4S>xg9MzM@aBUg*XP$lDLnYnFOnO6z6oH`lHVq~G?K9c->! z8_s=5xMI*!zy6`btlWO6rD1cj+HoM;ptbSq6y1MwcBr*!d#*E@Thg$NzPr?yEdQ9< z*1S(59?{VhM=;N&d(V8V^YNqpMWiFX^JW+z@_3)co5I>#J2627YUbt`&4mFa0;YKr z^Yhxzf8Glax}tfWI2F zc2%Du3SUeBbj~y46>(4?K|vYKMU=;Mu_vlPqVkd09fUOdtwkXQIs*ZdNi>>&#DKmO zwRlfa`E3MYN0w1`yb#Sd!2p?$x#462*0FiP{I<%UB++9m_3M7t&?Hbo8}iP(#otI<=YMOINi*|OWes-ZG-$yO5als81wve9YT?zo_6}!dH*B(V z`CLGu^IF*2f!8ZKMP9$bI5Psh)V*R*>9Y8QSmE3_yWhhI^Ab)NX${J8&^cZ z)<(vgxNH7clkEu0*h0p|4aEh#t>>BE;|tT_9Pv$u?UA~+FBNik!gTuvUcvIuPS`r0 znm~L0W_{ijlJ$A%c?BHSu7&Rhs5xLPQ;mUj!Q94ssnLDH`hmWGv#s3KLVGRnyAP;kkEXXj8&dEdcyKz~hIgKVL;3}&t6eP)t&UJreDp#QzNfeR;iGcj*+r)mICc5#Ez!Q1eR1&+0@2Onz%j@nvZQ>f|Rwu9O8Wd9%~r1 z&>}v_?kvHGFCtayMbo$Dn{?m~hgv`PP(Jr!$5a5*qjGu!`cER&{sWiuV z35|Q&=N8Ea8*2i#M3mkSwOl5&0AB*>WZ#7Hd^1FhtGPDAk#N;Pgux-$MgU% zjc`Cx3bFCeGkOHl1aaoxGG1IIh5hM~eJyZ}7<2+q@efL@1jNkQY6z!>;Y$$?*t z+#`~$TYqIl7V4S%NG$9Qzl3kH8zh%oFa;N*Z{@^)TlM;=3@i;1M6&sPBwqstJO>6d zZ!1Qzidz%`cpWKCsP9q1eYCbw6GDTBNlOEat21MI(0L{vawr)xDBUEF!r9z20{>~$ zC>M1>lkh<4B3t}T%7M#e18x{as~#-A186N5!T&k}B$4nVo?n+t@B^kFSJgg;cPsZbpaJU07H5e+SY6;G@+ zFU}$@L9ZI*ad3CLA2vCyRIiJK<#Gq$yd}-}NkO!M0{ZP9-ui|`4Xy$r3E%nDtKAsn zVuf!Vq1oheESq`)RL3&%dg~u8A_hD$ZVb! ze&iil^GZLCDk4R}<2c;*UVK^y@hv>lY0!!JCPOc3RVBjvd`JdUm&pxYBliU}J$)Seo*lqmr2V{7nuF2@x_jR~De7n22%4yq3=T zrc!b5E!mjx#jIcgUuxu4V(#knk>;ITuEe%!Wrmp$DQ&30#dr`{A(JW_fm-1gw}iMd zrFs+_W0U?OJIRYxMua~*PXC?XRqsT7JPF@W^)<)yVL`z3*)&^jW-Nz}^1<_iH4D literal 0 HcmV?d00001 diff --git a/src/Controller/CustomerAppAPI/MotoliteEventController.php b/src/Controller/CustomerAppAPI/MotoliteEventController.php index 93f924f2..9565f656 100644 --- a/src/Controller/CustomerAppAPI/MotoliteEventController.php +++ b/src/Controller/CustomerAppAPI/MotoliteEventController.php @@ -37,6 +37,7 @@ class MotoliteEventController extends ApiController 'id' => $result->getID(), 'name' => $result->getName(), 'event_type' => MotoliteEventType::getName($result->getEventType()), + 'event_time' => $result->getEventTime()->format("d M Y g:i A"), 'url' => $result->getUrl(), 'image_file' => $result->getImageFile(), ]; diff --git a/src/Controller/MotoliteEventController.php b/src/Controller/MotoliteEventController.php new file mode 100644 index 00000000..3807f49e --- /dev/null +++ b/src/Controller/MotoliteEventController.php @@ -0,0 +1,304 @@ +denyAccessUnlessGranted('motolite_event.list', null, 'No access.'); + + return $this->render('motolite-event/list.html.twig'); + } + + public function rows(Request $req) + { + $this->denyAccessUnlessGranted('motolite_event.list', null, 'No access.'); + + // get query builder + $qb = $this->getDoctrine() + ->getRepository(MotoliteEvent::class) + ->createQueryBuilder('q'); + + // get datatable params + $datatable = $req->request->get('datatable'); + + // count total records + $tquery = $qb->select('COUNT(q)'); + $this->setQueryFilters($datatable, $tquery); + $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'); + $this->setQueryFilters($datatable, $query); + + // check if sorting is present, otherwise use default + if (isset($datatable['sort']['field']) && !empty($datatable['sort']['field'])) { + $order = $datatable['sort']['sort'] ?? 'asc'; + $query->orderBy('q.' . $datatable['sort']['field'], $order); + } else { + $query->orderBy('q.event_time', 'asc'); + } + + // 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['name'] = $orow->getName(); + $row['event_type'] = MotoliteEventType::getName($orow->getEventType()); + $row['event_time'] = $orow->getEventTime()->format("d M Y g:i A"); + $row['url'] = 'Link'; + $row['image_file'] = $orow->getImageFile(); + + // add row metadata + $row['meta'] = [ + 'update_url' => '', + 'delete_url' => '' + ]; + + // add crud urls + if ($this->isGranted('motolite_event.update')) + $row['meta']['update_url'] = $this->generateUrl('motolite_event_update', ['id' => $row['id']]); + if ($this->isGranted('motolite_event.delete')) + $row['meta']['delete_url'] = $this->generateUrl('motolite_event_delete', ['id' => $row['id']]); + + $rows[] = $row; + } + + // response + return $this->json([ + 'meta' => $meta, + 'data' => $rows + ]); + } + + /** + * @Menu(selected="motolite_event_list") + */ + public function addForm() + { + $this->denyAccessUnlessGranted('motolite_event.add', null, 'No access.'); + + $params = []; + $params['obj'] = new MotoliteEvent(); + $params['mode'] = 'create'; + + $params['event_types'] = MotoliteEventType::getCollection(); + + $em = $this->getDoctrine()->getManager(); + + // response + return $this->render('motolite-event/form.html.twig', $params); + } + + public function addSubmit(Request $req, EncoderFactoryInterface $ef, ValidatorInterface $validator) + { + $this->denyAccessUnlessGranted('motolite_event.add', null, 'No access.'); + + // create new object + $em = $this->getDoctrine()->getManager(); + $obj = new MotoliteEvent(); + + $this->setObject($obj, $em, $req); + + // validate + $errors = $validator->validate($obj); + + // initialize error list + $error_array = []; + + // add errors to list + foreach ($errors as $error) { + $error_array[$error->getPropertyPath()] = $error->getMessage(); + } + + // check if any errors were found + if (!empty($error_array)) { + // return validation failure response + return $this->json([ + 'success' => false, + 'errors' => $error_array + ], 422); + } + + // validated! save the entity + $em->persist($obj); + $em->flush(); + + // return successful response + return $this->json([ + 'success' => 'Changes have been saved!' + ]); + } + + /** + * @Menu(selected="motolite_event_list") + */ + public function updateForm($id) + { + $this->denyAccessUnlessGranted('motolite_event.update', null, 'No access.'); + + // get row data + $em = $this->getDoctrine()->getManager(); + $obj = $em->getRepository(MotoliteEvent::class)->find($id); + + // make sure this row exists + if (empty($obj)) + throw $this->createNotFoundException('The item does not exist'); + + $params = []; + $params['obj'] = $obj; + $params['mode'] = 'update'; + + $params['event_types'] = MotoliteEventType::getCollection(); + + // response + return $this->render('motolite-event/form.html.twig', $params); + } + + public function updateSubmit(Request $req, EncoderFactoryInterface $ef, ValidatorInterface $validator, $id) + { + $this->denyAccessUnlessGranted('motolite_event.update', null, 'No access.'); + + // get object data + $em = $this->getDoctrine()->getManager(); + $obj = $em->getRepository(MotoliteEvent::class)->find($id); + + // make sure this object exists + if (empty($obj)) + throw $this->createNotFoundException('The item does not exist'); + + $this->setObject($obj, $em, $req); + + // validate + $errors = $validator->validate($obj); + + // initialize error list + $error_array = []; + + // add errors to list + foreach ($errors as $error) { + $error_array[$error->getPropertyPath()] = $error->getMessage(); + } + + // check if any errors were found + if (!empty($error_array)) { + // return validation failure response + return $this->json([ + 'success' => false, + 'errors' => $error_array + ], 422); + } + + // validated! save the entity + $em->flush(); + + // return successful response + return $this->json([ + 'success' => 'Changes have been saved!' + ]); + } + + public function destroy($id) + { + $this->denyAccessUnlessGranted('motolite_event.delete', null, 'No access.'); + + // get object data + $em = $this->getDoctrine()->getManager(); + $obj = $em->getRepository(MotoliteEvent::class)->find($id); + + if (empty($obj)) + throw $this->createNotFoundException('The item does not exist'); + + // delete this object + $em->remove($obj); + $em->flush(); + + // response + $response = new Response(); + $response->setStatusCode(Response::HTTP_OK); + $response->send(); + } + + protected function setQueryFilters($datatable, QueryBuilder $query) + { + if (isset($datatable['query']['data-rows-search']) && !empty($datatable['query']['data-rows-search'])) { + $query->where('q.name LIKE :filter') + ->orWhere('q.url LIKE :filter') + ->orWhere('q.event_type LIKE :filter') + ->setParameter('filter', '%' . $datatable['query']['data-rows-search'] . '%'); + } + } + + protected function setObject(MotoliteEvent $obj, EntityManager $em, Request $req) + { + // times + $format = "Y-m-d\TH:i"; + $event_time = DateTime::createFromFormat($format, $req->request->get('event_time')); + + $event_type = $req->request->get('event_type'); + + // set and save values + $obj->setName($req->request->get('name')) + ->setEventType($event_type) + ->setEventTime($event_time) + ->setUrl($req->request->get('url')) + ->setImageFile($req->request->get('image_file')); + } + + public function uploadImage(Request $req, FileUploader $uploader) + { + // retrieve temporary info for file + $file = $req->files->get('image_file'); + + // upload the file + $filename = $uploader->upload($file); + + // return response + return $this->json([ + 'success' => true, + 'filename' => $filename + ]); + } +} diff --git a/src/Entity/MotoliteEvent.php b/src/Entity/MotoliteEvent.php index 4349b44b..ccdb367b 100644 --- a/src/Entity/MotoliteEvent.php +++ b/src/Entity/MotoliteEvent.php @@ -4,7 +4,6 @@ namespace App\Entity; use Doctrine\ORM\Mapping as ORM; use Symfony\Component\Validator\Constraints as Assert; -use Symfony\Component\HttpFoundation\File\File; use DateTime; @@ -48,13 +47,12 @@ class MotoliteEvent /** * @ORM\Column(type="string") * @Assert\NotBlank() - * @Assert\File(mimeTypes={"image/png", "image/jpeg", "image/gif", "image/webp"}) */ protected $image_file; public function __construct() { - $this->event_time = new DateTime(); + $this->event_time = new DateTime('today noon'); } public function setName($name) @@ -112,7 +110,7 @@ class MotoliteEvent return $this->event_time; } - public function setImageFile(File $image_file = null) + public function setImageFile($image_file = null) { $this->image_file = $image_file; return $this; diff --git a/templates/motolite-event/form.html.twig b/templates/motolite-event/form.html.twig new file mode 100644 index 00000000..8abdb362 --- /dev/null +++ b/templates/motolite-event/form.html.twig @@ -0,0 +1,223 @@ +{% extends 'base.html.twig' %} + +{% block body %} + +
+
+
+

Motolite Events

+
+
+
+ +
+ +
+
+
+
+
+
+ + + +

+ {% if mode == 'update' %} + Edit Event + {{ obj.getName() }} + {% else %} + New Event + {% endif %} +

+
+
+
+
+
+
+
+
+
+ + + +
+
+ + + +
+
+
+
+ + + +
+
+ + + +
+
+
+
+ +
+
+

+ Drop files here or click to upload. +

+ + Upload only valid PNG, GIF, or JPEG images + +
+
+ {% if mode == 'update' and obj.getImageFile %} + Leave blank for unchanged + {% endif %} + +
+ {% if mode == 'update' %} +
+ +
+
+ {% endif %} +
+
+
+
+
+
+
+
+ + + Back +
+
+
+
+
+
+
+
+
+{% endblock %} + + +{% block scripts %} + +{% endblock %} diff --git a/templates/motolite-event/list.html.twig b/templates/motolite-event/list.html.twig new file mode 100644 index 00000000..550c4221 --- /dev/null +++ b/templates/motolite-event/list.html.twig @@ -0,0 +1,155 @@ +{% extends 'base.html.twig' %} + +{% block body %} + +
+
+
+

+ Motolite Events +

+
+
+
+ +
+ +
+
+
+
+
+
+
+
+
+
+ + + + +
+
+
+
+ +
+
+ +
+ +
+
+
+
+
+{% endblock %} + +{% block scripts %} + +{% endblock %} diff --git a/translations/messages.en.yaml b/translations/messages.en.yaml index 42a5aa54..01df542d 100644 --- a/translations/messages.en.yaml +++ b/translations/messages.en.yaml @@ -145,6 +145,9 @@ menu.partner: 'Partners' menu.partner.partners: 'Partners' menu.partner.reviews: 'Reviews' +menu.motolite_event: 'Motolite Events' +menu.motolite_event.events: 'Events' + menu.analytics: 'Analytics' menu.analytics.forecasting: 'Forecasting'