From fe44642fee6fc1efc2b86c39574fe52b4c91c193 Mon Sep 17 00:00:00 2001 From: Ryan Prather Date: Wed, 18 Dec 2024 05:24:20 +0000 Subject: [PATCH] Add community resource content and associated pages, links, forms, etc --- .../CommunityResourceController.php | 138 ++++ src/Entity/CommunityResource.php | 626 ++++++++++++++++++ src/Enums/State.php | 58 ++ src/Form/ResourceFormType.php | 96 +++ src/Libs/NavList.php | 1 + .../CommunityResourceRepository.php | 43 ++ .../internal/community_resource/add.html.twig | 165 +++++ .../community_resource/edit.html.twig | 164 +++++ .../community_resource/list.html.twig | 79 +++ templates/internal/libs/nav.html.twig | 6 + 10 files changed, 1376 insertions(+) create mode 100644 src/Controller/CommunityResourceController.php create mode 100644 src/Entity/CommunityResource.php create mode 100644 src/Enums/State.php create mode 100644 src/Form/ResourceFormType.php create mode 100644 src/Repository/CommunityResourceRepository.php create mode 100644 templates/internal/community_resource/add.html.twig create mode 100644 templates/internal/community_resource/edit.html.twig create mode 100644 templates/internal/community_resource/list.html.twig diff --git a/src/Controller/CommunityResourceController.php b/src/Controller/CommunityResourceController.php new file mode 100644 index 0000000..2b5d3a9 --- /dev/null +++ b/src/Controller/CommunityResourceController.php @@ -0,0 +1,138 @@ +navLinks = NavList::LIST; + $this->navLinks['community_resources'] = NavList::PRESENT_LINK; + } + + #[Route('/resource/list', name: 'app_community_resource')] + public function list(#[CurrentUser()] User $user): Response + { + $rsc = $this->entityManager->getRepository(CommunityResource::class)->findAll(); + + return $this->render( + 'internal/community_resource/list.html.twig', + array_merge( + $this->navLinks, + [ + 'breadcrumbs' => [ + new Breadcrumb('#', 'Community Resources') + ], + 'resources' => $rsc, + 'notifications' => $user->retrieveUnreadNotifications(), + ] + ) + ); + } + + #[Route('/resource/map', name: 'app_community_resource_map')] + public function map(): Response + { + return $this->render('internal/community_resource/map.html.twig', [ + ]); + } + + #[Route('/resource/add', name: 'app_community_resource_add')] + public function add(#[CurrentUser()] User $user, Request $request): Response + { + $form = $this->createForm(ResourceFormType::class); + $form->handleRequest($request); + + if ($form->isSubmitted() && $form->isValid()) { + $rsc = $form->getData(); + $this->entityManager->persist($rsc); + $this->entityManager->flush(); + + return $this->redirectToRoute('app_community_resource'); + } + + return $this->render( + 'internal/community_resource/add.html.twig', + array_merge( + $this->navLinks, + [ + 'form' => $form, + 'breadcrumbs' => [ + new Breadcrumb($this->generateUrl('app_community_resource'), 'List Resources'), + new Breadcrumb('#', 'Add Resource') + ], + 'notifications' => $user->retrieveUnreadNotifications(), + ] + ) + ); + + } + + #[Route('/resource/edit/{id}', name: 'app_community_resource_edit')] + public function edit(string $id, #[CurrentUser()] User $user, Request $request): Response + { + $rsc = $this->entityManager->getRepository(CommunityResource::class)->find($id); + $form = $this->createForm(ResourceFormType::class, $rsc); + + $form->handleRequest($request); + + if ($form->isSubmitted() && $form->isValid()) { + $this->entityManager->flush(); + + return $this->redirectToRoute('app_community_resource'); + } + + return $this->render( + 'internal/community_resource/edit.html.twig', + array_merge( + $this->navLinks, + [ + 'form' => $form, + 'breadcrumbs' => [ + new Breadcrumb($this->generateUrl('app_community_resource'), 'List Resources'), + new Breadcrumb('#', 'Edit Resource') + ], + 'notifications' => $user->retrieveUnreadNotifications(), + ] + ) + ); + + } + + #[Route('/resource/download/{id}', name: 'app_community_resource_download')] + public function download(string $id): Response + { + /** @var CommunityResource $rsc */ + $rsc = $this->entityManager->getRepository(CommunityResource::class)->find($id); + + if (!$rsc) { + $this->addFlash('error', 'Resource not found.'); + return $this->redirectToRoute('app_community_resource'); + } + + return new Response($rsc->generateVCard(), 200, [ + 'Content-Type' => 'text/vcf', + 'Content-Disposition' => 'attachment; filename="' . str_replace(' ', '', $rsc->getName()) . '.vcf"', + 'Content-Length' => strlen($rsc->generateVCard()), + 'Cache-Control' => 'must-revalidate, post-check=0, pre-check=0', + 'Expires' => '0', + 'Pragma' => 'public', + 'Content-Transfer-Encoding' => 'binary' + ]); + } +} diff --git a/src/Entity/CommunityResource.php b/src/Entity/CommunityResource.php new file mode 100644 index 0000000..8533d65 --- /dev/null +++ b/src/Entity/CommunityResource.php @@ -0,0 +1,626 @@ +today = new DateTime('now', new DateTimeZone('America/New_York')); + } + + public function getId(): ?Uuid + { + return $this->id; + } + + public function getName(): ?string + { + return $this->name; + } + + public function setName(string $name): static + { + $this->name = $name; + + return $this; + } + + public function getAddress(): ?string + { + return $this->address; + } + + public function setAddress(string $address): static + { + $this->address = $address; + + return $this; + } + + public function getAddress2(): ?string + { + return $this->address2; + } + + public function setAddress2(?string $address2): static + { + $this->address2 = $address2; + + return $this; + } + + public function getCity(): ?string + { + return $this->city; + } + + public function setCity(string $city): static + { + $this->city = $city; + + return $this; + } + + public function getState(): ?State + { + return $this->state; + } + + public function setState(State $state): static + { + $this->state = $state; + + return $this; + } + + public function getZip(): ?int + { + return $this->zip; + } + + public function setZip(int $zip): static + { + $this->zip = $zip; + + return $this; + } + + public function getCounty(): ?County + { + return $this->county; + } + + public function setCounty(County $county): static + { + $this->county = $county; + + return $this; + } + + public function getFormattedAddress(): ?string + { + return $this->address . + ($this->address2 ? ' ' . $this->address2 : '') . '
' . + $this->city . ', ' . $this->state->value . ' ' . $this->zip; + } + + public function getPhone(): ?string + { + return $this->phone; + } + + public function setPhone(?string $phone): static + { + $this->phone = $phone; + + return $this; + } + + public function getEmail(): ?string + { + return $this->email; + } + + public function setEmail(?string $email): static + { + $this->email = $email; + + return $this; + } + + public function getUrl(): ?string + { + return $this->url; + } + + public function setUrl(?string $url): static + { + $this->url = $url; + + return $this; + } + + public function urlString(): ?string + { + if (preg_match("/facebook/i", $this->url)) { + return ""; + } else { + return ""; + } + return null; + } + + public function getContactCard(): ?string + { + $formattedPhone = ($this->phone ? '(' . substr($this->phone, 0, 3) . ') ' . substr($this->phone, 3, 3) . '-' . substr($this->phone, 6) : ''); + return ($this->email ? "$this->email
" : '') . + ($this->phone ? "$formattedPhone" : ''); + } + + public function generateVCard(): string + { + return 'BEGIN:VCARD' . + "\nVERSION:3.0" . + "\nN:$this->name" . + "\nFN:$this->name" . + "\nORG:$this->name" . + "\nADR;TYPE=WORK:;;$this->address;$this->city;{$this->state->value};$this->zip" . + ($this->phone ? "\nTEL;TYPE=WORK,VOICE:$this->phone" : null) . + ($this->email ? "\nEMAIL;TYPE=WORK,INTERNET:$this->email" : null) . + ($this->url ? "\nURL:$this->url" : null) . + "\nNOTE:$this->notes" . + "\nREV:" . date('c') . + "\nEND:VCARD"; + } + + public function getMonOpen(): ?\DateTimeInterface + { + return $this->monOpen; + } + + public function setMonOpen(?\DateTimeInterface $monOpen): static + { + $this->monOpen = $monOpen; + + return $this; + } + + public function getMonClose(): ?\DateTimeInterface + { + return $this->monClose; + } + + public function setMonClose(?\DateTimeInterface $monClose): static + { + $this->monClose = $monClose; + + return $this; + } + + public function mon(): ?string + { + if (!$this->monOpen || !$this->monClose) { + return 'C'; + } + + $closeAt = new DateTime($this->today->format('Y-m-d') . ' ' . $this->monClose->format('H:i:s')); + if ($closeAt <= new DateTime()) { + return 'C'; + } + + return $this->monOpen->format('g:i A') . '-' . $this->monClose->format('g:i A'); + } + + public function getTueOpen(): ?\DateTimeInterface + { + return $this->tueOpen; + } + + public function setTueOpen(?\DateTimeInterface $tueOpen): static + { + $this->tueOpen = $tueOpen; + + return $this; + } + + public function getTueClose(): ?\DateTimeInterface + { + return $this->tueClose; + } + + public function setTueClose(?\DateTimeInterface $tueClose): static + { + $this->tueClose = $tueClose; + + return $this; + } + + public function tue(): ?string + { + if (!$this->tueOpen || !$this->tueClose) { + return 'C'; + } + + $closeAt = new DateTime($this->today->format('Y-m-d') . ' ' . $this->tueClose->format('H:i:s')); + if ($closeAt <= new DateTime()) { + return 'C'; + } + + return $this->tueOpen->format('g:i A') . '-' . $this->tueClose->format('g:i A'); + } + + public function getWedOpen(): ?\DateTimeInterface + { + return $this->wedOpen; + } + + public function setWedOpen(?\DateTimeInterface $wedOpen): static + { + $this->wedOpen = $wedOpen; + + return $this; + } + + public function getWedClose(): ?\DateTimeInterface + { + return $this->wedClose; + } + + public function setWedClose(?\DateTimeInterface $wedClose): static + { + $this->wedClose = $wedClose; + + return $this; + } + + public function wed(): ?string + { + if (!$this->wedOpen || !$this->wedClose) { + return 'C'; + } + + $closeAt = new DateTime($this->today->format('Y-m-d') . ' ' . $this->wedClose->format('H:i:s')); + if ($closeAt <= new DateTime()) { + return 'C'; + } + + return $this->wedOpen->format('g:i A') . '-' . $this->wedClose->format('g:i A'); + } + + public function getThuOpen(): ?\DateTimeInterface + { + return $this->thuOpen; + } + + public function setThuOpen(?\DateTimeInterface $thuOpen): static + { + $this->thuOpen = $thuOpen; + + return $this; + } + + public function getThuClose(): ?\DateTimeInterface + { + return $this->thuClose; + } + + public function setThuClose(?\DateTimeInterface $thuClose): static + { + $this->thuClose = $thuClose; + + return $this; + } + + public function thu(): ?string + { + if (!$this->thuOpen || !$this->thuClose) { + return 'C'; + } + + $closeAt = new DateTime($this->today->format('Y-m-d') . ' ' . $this->thuClose->format('H:i:s')); + if ($closeAt <= new DateTime()) { + return 'C'; + } + + return $this->thuOpen->format('g:i A') . '-' . $this->thuClose->format('g:i A'); + } + + public function getFriOpen(): ?\DateTimeInterface + { + return $this->friOpen; + } + + public function setFriOpen(?\DateTimeInterface $friOpen): static + { + $this->friOpen = $friOpen; + + return $this; + } + + public function getFriClose(): ?\DateTimeInterface + { + return $this->friClose; + } + + public function setFriClose(?\DateTimeInterface $friClose): static + { + $this->friClose = $friClose; + + return $this; + } + + public function fri(): ?string + { + if (!$this->friOpen || !$this->friClose) { + return 'C'; + } + + $closeAt = new DateTime($this->today->format('Y-m-d') . ' ' . $this->friClose->format('H:i:s')); + if ($closeAt <= new DateTime()) { + return 'C'; + } + + return $this->friOpen->format('g:i A') . '-' . $this->friClose->format('g:i A'); + } + + public function getSatOpen(): ?\DateTimeInterface + { + return $this->satOpen; + } + + public function setSatOpen(?\DateTimeInterface $satOpen): static + { + $this->satOpen = $satOpen; + + return $this; + } + + public function getSatClose(): ?\DateTimeInterface + { + return $this->satClose; + } + + public function setSatClose(?\DateTimeInterface $satClose): static + { + $this->satClose = $satClose; + + return $this; + } + + public function sat(): ?string + { + if (!$this->satOpen || !$this->satClose) { + return 'C'; + } + + $closeAt = new DateTime($this->today->format('Y-m-d') . ' ' . $this->satClose->format('H:i:s')); + if ($closeAt <= new DateTime()) { + return 'C'; + } + + return $this->satOpen->format('g:i A') . '-' . $this->satClose->format('g:i A'); + } + + public function getSunOpen(): ?\DateTimeInterface + { + return $this->sunOpen; + } + + public function setSunOpen(?\DateTimeInterface $sunOpen): static + { + $this->sunOpen = $sunOpen; + + return $this; + } + + public function getSunClose(): ?\DateTimeInterface + { + return $this->sunClose; + } + + public function setSunClose(?\DateTimeInterface $sunClose): static + { + $this->sunClose = $sunClose; + + return $this; + } + + public function sun(): ?string + { + if (!$this->sunOpen || !$this->sunClose) { + return 'C'; + } + + $closeAt = new DateTime($this->today->format('Y-m-d') . ' ' . $this->sunClose->format('H:i:s')); + if ($closeAt <= new DateTime()) { + return 'C'; + } + + return $this->sunOpen->format('g:i A') . '-' . $this->sunClose->format('g:i A'); + } + + public function getHours(): ?string + { + $this->today = new DateTime('now', new DateTimeZone('America/New_York')); + switch ($this->today->format('w')) { + case 0: + return $this->sun(); + case 1: + return $this->mon(); + case 2: + return $this->tue(); + case 3: + return $this->wed(); + case 4: + return $this->thu(); + case 5: + return $this->fri(); + case 6: + return $this->sat(); + } + } + + public function getFormattedHours(): ?string + { + $mon = 'CLOSED'; + $tue = 'CLOSED'; + $wed = 'CLOSED'; + $thu = 'CLOSED'; + $fri = 'CLOSED'; + $sat = 'CLOSED'; + $sun = 'CLOSED'; + + if ($this->monOpen && $this->monClose) { + $mon = $this->monOpen->format('g:i A') . '-' . $this->monClose->format('g:i A'); + } + if ($this->tueOpen && $this->tueClose) { + $tue = $this->tueOpen->format('g:i A') . '-' . $this->tueClose->format('g:i A'); + } + if ($this->wedOpen && $this->wedClose) { + $wed = $this->wedOpen->format('g:i A') . '-' . $this->wedClose->format('g:i A'); + } + if ($this->thuOpen && $this->thuClose) { + $thu = $this->thuOpen->format('g:i A') . '-' . $this->thuClose->format('g:i A'); + } + if ($this->friOpen && $this->friClose) { + $fri = $this->friOpen->format('g:i A') . '-' . $this->friClose->format('g:i A'); + } + if ($this->satOpen && $this->satClose) { + $sat = $this->satOpen->format('g:i A') . '-' . $this->satClose->format('g:i A'); + } + if ($this->sunOpen && $this->sunClose) { + $sun = $this->sunOpen->format('g:i A') . '-' . $this->sunClose->format('g:i A'); + } + + return <<Sun: {$sun}

+

Mon: {$mon}

+

Tue: {$tue}

+

Wed: {$wed}

+

Thu: {$thu}

+

Fri: {$fri}

+

Sat: {$sat}

+ HTML; + } + + public function getNotes(): ?string + { + return $this->notes; + } + + public function setNotes(?string $notes): static + { + $this->notes = $notes; + + return $this; + } + + public function getServicesAvailable(): ?string + { + return $this->servicesAvailable; + } + + public function setServicesAvailable(?string $servicesAvailable): static + { + $this->servicesAvailable = $servicesAvailable; + + return $this; + } +} diff --git a/src/Enums/State.php b/src/Enums/State.php new file mode 100644 index 0000000..a1e923d --- /dev/null +++ b/src/Enums/State.php @@ -0,0 +1,58 @@ +add('name', TextType::class, [ + 'required' => true, + ]) + ->add('address', TextType::class, [ + 'required' => true, + ]) + ->add('address2') + ->add('city', TextType::class, [ + 'required' => true, + ]) + ->add('state', EnumType::class, [ + 'class' => State::class, + ]) + ->add('zip', NumberType::class) + ->add('county', EnumType::class, [ + 'class' => County::class, + ]) + ->add('phone') + ->add('email', EmailType::class) + ->add('url', UrlType::class) + ->add('monOpen', TimeType::class, [ + 'widget' => 'single_text', + ]) + ->add('monClose', TimeType::class, [ + 'widget' => 'single_text', + ]) + ->add('tueOpen', TimeType::class, [ + 'widget' => 'single_text', + ]) + ->add('tueClose', TimeType::class, [ + 'widget' => 'single_text', + ]) + ->add('wedOpen', TimeType::class, [ + 'widget' => 'single_text', + ]) + ->add('wedClose', TimeType::class, [ + 'widget' => 'single_text', + ]) + ->add('thuOpen', TimeType::class, [ + 'widget' => 'single_text', + ]) + ->add('thuClose', TimeType::class, [ + 'widget' => 'single_text', + ]) + ->add('friOpen', TimeType::class, [ + 'widget' => 'single_text', + ]) + ->add('friClose', TimeType::class, [ + 'widget' => 'single_text', + ]) + ->add('satOpen', TimeType::class, [ + 'widget' => 'single_text', + ]) + ->add('satClose', TimeType::class, [ + 'widget' => 'single_text', + ]) + ->add('sunOpen', TimeType::class, [ + 'widget' => 'single_text', + ]) + ->add('sunClose', TimeType::class, [ + 'widget' => 'single_text', + ]) + ->add('notes') + ->add('servicesAvailable') + ; + } + + public function configureOptions(OptionsResolver $resolver): void + { + $resolver->setDefaults([ + 'data_class' => CommunityResource::class, + ]); + } +} diff --git a/src/Libs/NavList.php b/src/Libs/NavList.php index 0a39eba..8a9f5aa 100644 --- a/src/Libs/NavList.php +++ b/src/Libs/NavList.php @@ -14,6 +14,7 @@ class NavList 'add_user' => 'nav-link text-dark', 'referral_sources' => 'nav-link text-dark', 'case_notes' => 'nav-link text-dark', + 'community_resource' => 'nav-link text-dark', ]; public const PRESENT_LINK = 'nav-link text-white active bg-gradient-dark'; diff --git a/src/Repository/CommunityResourceRepository.php b/src/Repository/CommunityResourceRepository.php new file mode 100644 index 0000000..9b083a2 --- /dev/null +++ b/src/Repository/CommunityResourceRepository.php @@ -0,0 +1,43 @@ + + */ +class CommunityResourceRepository extends ServiceEntityRepository +{ + public function __construct(ManagerRegistry $registry) + { + parent::__construct($registry, CommunityResource::class); + } + + // /** + // * @return CommunityResource[] Returns an array of CommunityResource objects + // */ + // public function findByExampleField($value): array + // { + // return $this->createQueryBuilder('c') + // ->andWhere('c.exampleField = :val') + // ->setParameter('val', $value) + // ->orderBy('c.id', 'ASC') + // ->setMaxResults(10) + // ->getQuery() + // ->getResult() + // ; + // } + + // public function findOneBySomeField($value): ?CommunityResource + // { + // return $this->createQueryBuilder('c') + // ->andWhere('c.exampleField = :val') + // ->setParameter('val', $value) + // ->getQuery() + // ->getOneOrNullResult() + // ; + // } +} diff --git a/templates/internal/community_resource/add.html.twig b/templates/internal/community_resource/add.html.twig new file mode 100644 index 0000000..51bf6f0 --- /dev/null +++ b/templates/internal/community_resource/add.html.twig @@ -0,0 +1,165 @@ +{% extends 'base.html.twig' %} + +{% block title %}Community Resources +{% endblock %} + +{% block body %} + {{ block('nav', 'internal/libs/nav.html.twig') }} + +
+ {{ block('topnav', 'internal/libs/top-nav.html.twig') }} + +
+ +
+
+

Case Info

+

+
+
+
+ {{ form_start(form) }} + + {{ form_errors(form) }} +
+
+
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+
+
+
+ Sunday + + + + +
+ +
+ Monday + + + + +
+ +
+ Tuesday + + + + +
+ +
+ Wednesday + + + + +
+ +
+ Thursday + + + + +
+ +
+ Friday + + + + +
+ +
+ Saturday + + + + +
+ +
+ +
+
+
+
+
+ +
+
+ {{ form_end(form) }} +
+
+
+
+
+{% endblock %} diff --git a/templates/internal/community_resource/edit.html.twig b/templates/internal/community_resource/edit.html.twig new file mode 100644 index 0000000..898468d --- /dev/null +++ b/templates/internal/community_resource/edit.html.twig @@ -0,0 +1,164 @@ +{% extends 'base.html.twig' %} + +{% block title %}Community Resources +{% endblock %} + +{% block body %} + {{ block('nav', 'internal/libs/nav.html.twig') }} + +
+ {{ block('topnav', 'internal/libs/top-nav.html.twig') }} + +
+ +
+
+

{{ field_value(form.name) }}

+
+
+
+ {{ form_start(form) }} + + {{ form_errors(form) }} +
+
+
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+
+
+
+ Sunday + + + + +
+ +
+ Monday + + + + +
+ +
+ Tuesday + + + + +
+ +
+ Wednesday + + + + +
+ +
+ Thursday + + + + +
+ +
+ Friday + + + + +
+ +
+ Saturday + + + + +
+ +
+ +
+
+
+
+
+ +
+
+ {{ form_end(form) }} +
+
+
+
+
+{% endblock %} diff --git a/templates/internal/community_resource/list.html.twig b/templates/internal/community_resource/list.html.twig new file mode 100644 index 0000000..e1a6592 --- /dev/null +++ b/templates/internal/community_resource/list.html.twig @@ -0,0 +1,79 @@ +{% extends 'base.html.twig' %} + +{% block title %}Community Resources +{% endblock %} + +{% block body %} + {% set today = date("now", "America/Indiana/Indianapolis") %} + {{ block('nav', 'internal/libs/nav.html.twig') }} + +
+ {{ block('topnav', 'internal/libs/top-nav.html.twig') }} + +
+
+
+
+
+
+
+
Free/Low-cost Community Resources
+
+
+ +
+
+
+
+
+ Filters: + +
+
+ + + + + + + + + + + + {% for r in resources %} + + + + + + + + {% endfor %} + +
NameAddressContact InfoHours
{{ r.name }} + {% if r.url %} +
{{ r.urlString|raw }} + {% endif %} +
{{ r.getFormattedAddress()|raw }}{{ r.getContactCard()|raw }}{{ r.getHours() }} + + edit + + + import_contacts + +
+
+
+
+
+
+
+
+{% endblock %} diff --git a/templates/internal/libs/nav.html.twig b/templates/internal/libs/nav.html.twig index c48e6fb..7feadad 100644 --- a/templates/internal/libs/nav.html.twig +++ b/templates/internal/libs/nav.html.twig @@ -70,6 +70,12 @@ Case Notes +