diff --git a/src/Controller/NoteController.php b/src/Controller/NoteController.php new file mode 100644 index 0000000..b4db534 --- /dev/null +++ b/src/Controller/NoteController.php @@ -0,0 +1,168 @@ +navLinks = NavList::LIST; + $this->navLinks['case_notes'] = NavList::PRESENT_LINK; + } + + #[Route('/list-notes/{id?null}', name: 'app_list_notes')] + public function listNotes(#[CurrentUser()] User $user, ?string $id = null): Response + { + /** @var UserCase[] $cases */ + $cases = $this->entityManager->getRepository(UserCase::class)->findBy(['user' => $user]); + $referrals = []; + $notes = []; + + if ($id == 'null') { + $id = null; + } + + if ($id) { + $referrals[] = $this->entityManager->getRepository(Referral::class)->find($id); + } else { + foreach ($cases as $case) { + $referrals = array_merge( + $referrals, + $this->entityManager->getRepository(Referral::class)->findBy(['memberCase' => $case->getMemberCase()]) + ); + } + } + + foreach ($referrals as $referral) { + $notes = array_merge( + $notes, + $this->entityManager->getRepository(VisitNote::class)->getOrderedNotes($referral), + $this->entityManager->getRepository(StandardNote::class)->getOrderedNotes($referral), + ); + } + + return $this->render( + 'internal/cases/notes/list-notes.html.twig', + array_merge( + $this->navLinks, + [ + 'breadcrumbs' => [ + new Breadcrumb($this->generateUrl('app_list_notes'), 'List Notes') + ], + 'notifications' => $user->retrieveUnreadNotifications(), + 'cases' => $cases, + 'notes' => $notes, + ] + ) + ); + } + + #[Route('/add-note/{id?null}', name: 'app_add_note')] + public function addNote(#[CurrentUser()] User $user, Request $request, ?string $id = null): Response + { + /** @var Referral $referral */ + $referral = $this->entityManager->getRepository(Referral::class)->find($id); + $this->entityManager->getRepository(Referral::class)->populateNotes($referral); + + //dd($referral); + + $members = $this->entityManager->getRepository(Member::class)->findBy(['caseId' => $referral->getMemberCase()]); + $defaultMethod = NoteMethod::BILLABLE; + $defaultLocation = NoteLocation::COMMUNITY_OUTING; + $form = $this->createForm(StandardNoteFormType::class, null, ['members' => $members]); + $template = 'internal/cases/notes/add-standard-note.html.twig'; + + if ($id == 'null') { + $id = null; + } + + if ($referral->getServiceCode() == ReferralServiceType::VS_THBB) { + $form = $this->createForm(VisitNoteFormType::class, null, ['members' => $members]); + $template = 'internal/cases/notes/add-visit-note.html.twig'; + $defaultMethod = NoteMethod::BILLABLE_SUPERVISED_VISIT; + } elseif ($referral->getServiceCode() == ReferralServiceType::VS_THBBT) { + $defaultMethod = NoteMethod::BILLABLE; + $defaultLocation = NoteLocation::VEHICLE_TRANSPORTATION; + } + + $form = $form->handleRequest($request); + + if ($form->isSubmitted() && $form->isValid()) { + /** @var VisitNote|StandardNote $note */ + $note = $form->getData(); + $note->setReferral($referral); + + $this->entityManager->persist($note); + $this->entityManager->flush(); + + return $this->redirectToRoute('app_list_notes'); + } + + return $this->render( + $template, + array_merge( + $this->navLinks, + [ + 'breadcrumbs' => [ + new Breadcrumb($this->generateUrl('app_list_notes'), 'List Notes') + ], + 'notifications' => $user->retrieveUnreadNotifications(), + 'referral' => $referral, + 'form' => $form, + 'default_method' => $defaultMethod, + 'default_location' => $defaultLocation, + ] + ) + ); + } + + #[Route('/edit-note', name: 'app_edit_note')] + public function editNote(): Response + { + return $this->render( + 'internal/cases/notes/edit-note.html.twig', + array_merge( + $this->navLinks, + ) + ); + } + + #[Route('/api/filter-notes', name: 'api_filter_notes')] + public function filterNotes(#[CurrentUser()] User $user, Request $request): Response + { + $startDate = $request->get('startDate'); + $endDate = $request->get('endDate'); + $referralId = $request->get('referralId'); + $referral = null; + if ($referralId) { + $referral = $this->entityManager->getRepository(Referral::class)->find($referralId); + } + + return $this->json(array_merge( + $this->entityManager->getRepository(VisitNote::class)->filterNotes($user, $referral, $startDate, $endDate), + $this->entityManager->getRepository(StandardNote::class)->filterNotes($user, $referral, $startDate, $endDate), + )); + } +} diff --git a/src/Entity/Note.php b/src/Entity/Note.php new file mode 100644 index 0000000..48b9a1b --- /dev/null +++ b/src/Entity/Note.php @@ -0,0 +1,199 @@ + + */ + #[ORM\OneToMany(targetEntity: Member::class, mappedBy: 'note')] + public Collection $members; + + #[ORM\Column(enumType: NoteLocation::class)] + private ?NoteLocation $location = null; + + #[ORM\Column(enumType: NoteMethod::class)] + private ?NoteMethod $method = null; + + public function getId(): ?UUid + { + return $this->id; + } + + public function getDate(): ?\DateTimeInterface + { + return $this->date; + } + + public function setDate(\DateTimeInterface $date): static + { + $this->date = $date; + + return $this; + } + + public function getReferral(): ?Referral + { + return $this->referral; + } + + public function setReferral(?Referral $referral): static + { + $this->referral = $referral; + + return $this; + } + + public function getStartTime(): ?\DateTimeInterface + { + return $this->startTime; + } + + public function setStartTime(\DateTimeInterface $startTime): static + { + $this->startTime = $startTime; + + return $this; + } + + public function getEndTime(): ?\DateTimeInterface + { + return $this->endTime; + } + + public function setEndTime(\DateTimeInterface $endTime): static + { + $this->endTime = $endTime; + + return $this; + } + + public function getStatus(): ?NoteStatus + { + return $this->status; + } + + public function setStatus(NoteStatus $status): static + { + $this->status = $status; + + return $this; + } + + /** + * @return Collection + */ + public function getMembers(): Collection + { + return $this->members; + } + + public function addUser(Member $member): static + { + if (!$this->members->contains($member)) { + $this->members->add($member); + } + + return $this; + } + + public function removeUser(Member $member): static + { + $this->members->removeElement($member); + + return $this; + } + + public function getLocation(): ?NoteLocation + { + return $this->location; + } + + public function setLocation(NoteLocation $location): static + { + $this->location = $location; + + return $this; + } + + public function getMethod(): ?NoteMethod + { + return $this->method; + } + + public function setMethod(NoteMethod $method): static + { + $this->method = $method; + + return $this; + } + + /** + * Method to calculate the number of minutes used for a visit rounded to the nearest 15 min increment + * + * @param int $precision + * The number of minutes to round the time to defaulted to 15 + * + * @return int + * The number of minutes calculated + */ + public function calcTimeUsed(int $precision = 15): int + { + // get the number of seconds between the two times + $timestamp = $this->endTime->getTimestamp() - $this->startTime->getTimestamp(); + + // find out how many increments of precision there are between these two increments + $increments = ($timestamp / 60) / $precision; + + // if the number of increments is a float that means there is a difference + if (is_float($increments)) { + // calculate the modulo + $mod = ($timestamp / 60) % $precision; + // if the modulo is higher than half the precision increment increase the increments by 1 + if ($mod >= ($precision / 2)) { + $increments++; + } + // convert the increments to an integer + $increments = (int) $increments; + } + + // return the number of increments times the precision to the partials + return $increments * $precision; + } +} diff --git a/src/Entity/StandardNote.php b/src/Entity/StandardNote.php new file mode 100644 index 0000000..b15420a --- /dev/null +++ b/src/Entity/StandardNote.php @@ -0,0 +1,26 @@ +note; + } + + public function setNote(?string $note): static + { + $this->note = $note; + + return $this; + } +} diff --git a/src/Entity/VisitNote.php b/src/Entity/VisitNote.php new file mode 100644 index 0000000..6e07b8c --- /dev/null +++ b/src/Entity/VisitNote.php @@ -0,0 +1,162 @@ +narrative; + } + + public function setNarrative(string $narrative): static + { + $this->narrative = $narrative; + + return $this; + } + + public function getStrengths(): ?string + { + return $this->strengths; + } + + public function setStrengths(string $strengths): static + { + $this->strengths = $strengths; + + return $this; + } + + public function getIssues(): ?string + { + return $this->issues; + } + + public function setIssues(string $issues): static + { + $this->issues = $issues; + + return $this; + } + + public function getRecommendation(): ?string + { + return $this->recommendation; + } + + public function setRecommendation(string $recommendation): static + { + $this->recommendation = $recommendation; + + return $this; + } + + public function getParentalRole(): ?VisitQualityLevel + { + return $this->parentalRole; + } + + public function setParentalRole(VisitQualityLevel $parentalRole): static + { + $this->parentalRole = $parentalRole; + + return $this; + } + + public function getChildDevelopment(): ?VisitQualityLevel + { + return $this->childDevelopment; + } + + public function setChildDevelopment(VisitQualityLevel $childDevelopment): static + { + $this->childDevelopment = $childDevelopment; + + return $this; + } + + public function getAppropriateResponse(): ?VisitQualityLevel + { + return $this->appropriateResponse; + } + + public function setAppropriateResponse(VisitQualityLevel $appropriateResponse): static + { + $this->appropriateResponse = $appropriateResponse; + + return $this; + } + + public function getChildAheadOfSelf(): ?VisitQualityLevel + { + return $this->childAheadOfSelf; + } + + public function setChildAheadOfSelf(VisitQualityLevel $childAheadOfSelf): static + { + $this->childAheadOfSelf = $childAheadOfSelf; + + return $this; + } + + public function getShowsEmpathy(): ?VisitQualityLevel + { + return $this->showsEmpathy; + } + + public function setShowsEmpathy(VisitQualityLevel $showsEmpathy): static + { + $this->showsEmpathy = $showsEmpathy; + + return $this; + } + + public function getChildFocused(): ?VisitQualityLevel + { + return $this->childFocused; + } + + public function setChildFocused(VisitQualityLevel $childFocused): static + { + $this->childFocused = $childFocused; + + return $this; + } +} diff --git a/src/Enums/NoteLocation.php b/src/Enums/NoteLocation.php new file mode 100644 index 0000000..ffad071 --- /dev/null +++ b/src/Enums/NoteLocation.php @@ -0,0 +1,36 @@ +add('date', null, [ + 'widget' => 'single_text', + ]) + ->add('startTime', null, [ + 'widget' => 'single_text', + ]) + ->add('endTime', null, [ + 'widget' => 'single_text', + ]) + ->add('location', EnumType::class, [ + 'class' => NoteLocation::class + ]) + ->add('status', EnumType::class, [ + 'class' => NoteStatus::class, + ]) + ->add('note', null, [ + 'required' => true, + 'attr' => [ + 'class' => 'form-control', + 'placeholder' => 'Note', + ], + ]) + ->add('members', EntityType::class, [ + 'class' => Member::class, + 'choices' => $members, + 'multiple' => true, + 'expanded' => true, + 'choice_label' => 'name', + ]) + ->add('method', EnumType::class, [ + 'class' => NoteMethod::class, + ]) + ; + } + + public function configureOptions(OptionsResolver $resolver): void + { + $resolver->setDefaults([ + 'data_class' => StandardNote::class, + 'members' => [], + 'csrf_protection' => true, + 'csrf_field_name' => '_token', + 'csrf_token_id' => 'standard_note', + ]); + } +} diff --git a/src/Form/VisitNoteFormType.php b/src/Form/VisitNoteFormType.php new file mode 100644 index 0000000..4b088cf --- /dev/null +++ b/src/Form/VisitNoteFormType.php @@ -0,0 +1,84 @@ +add('date', null, [ + 'widget' => 'single_text', + ]) + ->add('startTime', null, [ + 'widget' => 'single_text', + ]) + ->add('endTime', null, [ + 'widget' => 'single_text', + ]) + ->add('status', EnumType::class, [ + 'class' => NoteStatus::class + ]) + ->add('location', EnumType::class, [ + 'class' => NoteLocation::class + ]) + ->add('narrative') + ->add('strengths') + ->add('issues') + ->add('recommendation') + ->add('parentalRole', EnumType::class, [ + 'class' => VisitQualityLevel::class + ]) + ->add('childDevelopment', EnumType::class, [ + 'class' => VisitQualityLevel::class + ]) + ->add('appropriateResponse', EnumType::class, [ + 'class' => VisitQualityLevel::class + ]) + ->add('childAheadOfSelf', EnumType::class, [ + 'class' => VisitQualityLevel::class + ]) + ->add('showsEmpathy', EnumType::class, [ + 'class' => VisitQualityLevel::class + ]) + ->add('childFocused', EnumType::class, [ + 'class' => VisitQualityLevel::class + ]) + ->add('members', EntityType::class, [ + 'class' => Member::class, + 'choices' => $members, + 'multiple' => true, + 'expanded' => true, + 'choice_label' => 'name', + ]) + ->add('method', EnumType::class, [ + 'class' => NoteMethod::class + ]) + ; + } + + public function configureOptions(OptionsResolver $resolver): void + { + $resolver->setDefaults([ + 'data_class' => VisitNote::class, + 'members' => [], + 'csrf_protection' => true, + 'csrf_field_name' => '_token', + 'csrf_token_id' => 'visit_note', + ]); + } +} diff --git a/src/Repository/NoteRepository.php b/src/Repository/NoteRepository.php new file mode 100644 index 0000000..4dbced3 --- /dev/null +++ b/src/Repository/NoteRepository.php @@ -0,0 +1,61 @@ + + */ +class NoteRepository extends ServiceEntityRepository +{ + public function __construct(ManagerRegistry $registry) + { + parent::__construct($registry, Note::class); + } + + public function getHoursUsed(Referral $referral): int + { + $ret = 0; + + $query = $this->createQueryBuilder('n') + ->select('SUM(n.hours) AS hours') + ->andWhere('n.referral = :referral') + ->setParameter('referral', $referral->getId()->toBinary()) + ->getQuery() + //->getResult() + ; + + dd($query->getSQL()); + + return $ret; + } + + // /** + // * @return Notes[] Returns an array of Notes objects + // */ + // public function findByExampleField($value): array + // { + // return $this->createQueryBuilder('n') + // ->andWhere('n.exampleField = :val') + // ->setParameter('val', $value) + // ->orderBy('n.id', 'ASC') + // ->setMaxResults(10) + // ->getQuery() + // ->getResult() + // ; + // } + + // public function findOneBySomeField($value): ?Notes + // { + // return $this->createQueryBuilder('n') + // ->andWhere('n.exampleField = :val') + // ->setParameter('val', $value) + // ->getQuery() + // ->getOneOrNullResult() + // ; + // } +} diff --git a/src/Repository/StandardNoteRepository.php b/src/Repository/StandardNoteRepository.php new file mode 100644 index 0000000..eedf561 --- /dev/null +++ b/src/Repository/StandardNoteRepository.php @@ -0,0 +1,86 @@ + + */ +class StandardNoteRepository extends ServiceEntityRepository +{ + public function __construct(ManagerRegistry $registry) + { + parent::__construct($registry, StandardNote::class); + } + + public function getOrderedNotes(Referral $referral): array + { + return $this->createQueryBuilder('s') + ->andWhere('s.referral = :referral') + ->setParameter('referral', $referral->getId()->toBinary()) + ->orderBy('s.date', 'DESC') + ->addOrderBy('s.startTime', 'DESC') + ->getQuery() + ->getResult() + ; + } + + public function filterNotes(User $user, ?Referral $referral = null, ?DateTimeImmutable $startDate = null, ?DateTimeImmutable $endDate = null): array + { + $query = $this->createQueryBuilder('v') + ->join('v.referral', 'r') + ; + + if ($referral) { + $query->andWhere('v.referral = :referral') + ->setParameter('referral', $referral->getId()->toBinary()); + } + + if ($startDate) { + $query->andWhere('v.date >= :startDate') + ->setParameter('startDate', $startDate); + } + + if ($endDate) { + $query->andWhere('v.date <= :endDate') + ->setParameter('endDate', $endDate); + } + + return $query->orderBy('v.date', 'DESC') + ->addOrderBy('v.startTime', 'DESC') + ->getQuery() + ->getResult() + ; + } + + // /** + // * @return StandardNote[] Returns an array of StandardNote objects + // */ + // public function findByExampleField($value): array + // { + // return $this->createQueryBuilder('s') + // ->andWhere('s.exampleField = :val') + // ->setParameter('val', $value) + // ->orderBy('s.id', 'ASC') + // ->setMaxResults(10) + // ->getQuery() + // ->getResult() + // ; + // } + + // public function findOneBySomeField($value): ?StandardNote + // { + // return $this->createQueryBuilder('s') + // ->andWhere('s.exampleField = :val') + // ->setParameter('val', $value) + // ->getQuery() + // ->getOneOrNullResult() + // ; + // } +} diff --git a/src/Repository/VisitNoteRepository.php b/src/Repository/VisitNoteRepository.php new file mode 100644 index 0000000..be8137e --- /dev/null +++ b/src/Repository/VisitNoteRepository.php @@ -0,0 +1,86 @@ + + */ +class VisitNoteRepository extends ServiceEntityRepository +{ + public function __construct(ManagerRegistry $registry) + { + parent::__construct($registry, VisitNote::class); + } + + public function getOrderedNotes(Referral $referral): array + { + return $this->createQueryBuilder('v') + ->andWhere('v.referral = :referral') + ->setParameter('referral', $referral->getId()->toBinary()) + ->orderBy('v.date', 'DESC') + ->addOrderBy('v.startTime', 'DESC') + ->getQuery() + ->getResult() + ; + } + + public function filterNotes(User $user, ?Referral $referral = null, ?DateTimeImmutable $startDate = null, ?DateTimeImmutable $endDate = null): array + { + $query = $this->createQueryBuilder('v') + ->join('v.referral', 'r') + ; + + if ($referral) { + $query->andWhere('v.referral = :referral') + ->setParameter('referral', $referral->getId()->toBinary()); + } + + if ($startDate) { + $query->andWhere('v.date >= :startDate') + ->setParameter('startDate', $startDate); + } + + if ($endDate) { + $query->andWhere('v.date <= :endDate') + ->setParameter('endDate', $endDate); + } + + return $query->orderBy('v.date', 'DESC') + ->addOrderBy('v.startTime', 'DESC') + ->getQuery() + ->getResult() + ; + } + + // /** + // * @return VisitNote[] Returns an array of VisitNote objects + // */ + // public function findByExampleField($value): array + // { + // return $this->createQueryBuilder('v') + // ->andWhere('v.exampleField = :val') + // ->setParameter('val', $value) + // ->orderBy('v.id', 'ASC') + // ->setMaxResults(10) + // ->getQuery() + // ->getResult() + // ; + // } + + // public function findOneBySomeField($value): ?VisitNote + // { + // return $this->createQueryBuilder('v') + // ->andWhere('v.exampleField = :val') + // ->setParameter('val', $value) + // ->getQuery() + // ->getOneOrNullResult() + // ; + // } +} diff --git a/templates/internal/cases/notes/add-standard-note.html.twig b/templates/internal/cases/notes/add-standard-note.html.twig new file mode 100644 index 0000000..a9269ac --- /dev/null +++ b/templates/internal/cases/notes/add-standard-note.html.twig @@ -0,0 +1,107 @@ +{% extends 'base.html.twig' %} + +{% block body %} + {{ block('nav', 'internal/libs/nav.html.twig') }} + +
+ {{ block('topnav', 'internal/libs/top-nav.html.twig') }} + +
+
+
+

Add Referral Note

+

{{ referral.memberCase.caseName }}

+
+
+ {{ form_start(form) }} + {{ form_errors(form) }} +
+
+ {% set endDateWarning = '' %} + {% if date("+28 days") >= referral.endDate %} + {% set endDateWarning = 'bg-gradient-warning' %} + {% elseif date("+14 days") >= referral.endDate %} + {% set endDateWarning = 'bg-gradient-danger text-white' %} + {% endif %} + + Hours: + {{ referral.hours }} + / + Remaining: + {{ referral.getHoursRemaining() }} + + + Expiration Date: + {{ referral.endDate|date('M j, Y') }} + +
+
+
+
+ + +
+
+ +    + + +
+
+ + +
+
+ +
+
+ +
+
+ +
+
+
+
+ {{ form_row(form.members, { + 'label': 'Members Present', + 'label_attr': {'class': ''}, + 'attr': {'class': 'form-control'} + }) }} +
+
+ + +
+
+
+
+
+ +
+
+
+ {{ form_end(form) }} +
+
+
+
+{% endblock %} diff --git a/templates/internal/cases/notes/add-visit-note.html.twig b/templates/internal/cases/notes/add-visit-note.html.twig new file mode 100644 index 0000000..0af0753 --- /dev/null +++ b/templates/internal/cases/notes/add-visit-note.html.twig @@ -0,0 +1,191 @@ +{% extends 'base.html.twig' %} + +{% block body %} + {{ block('nav', 'internal/libs/nav.html.twig') }} + +
+ {{ block('topnav', 'internal/libs/top-nav.html.twig') }} + +
+
+
+

Add Referral Note

+

{{ referral.memberCase.caseName }}

+
+
+ {{ form_start(form) }} + {{ form_errors(form) }} +
+
+ {% set endDateWarning = '' %} + {% if date("+28 days") >= referral.endDate %} + {% set endDateWarning = 'bg-gradient-warning' %} + {% elseif date("+14 days") >= referral.endDate %} + {% set endDateWarning = 'bg-gradient-danger text-white' %} + {% endif %} + + Hours: + {{ referral.hours }} + / + Remaining: + {{ referral.getHoursRemaining() }} + + + Expiration Date: + {{ referral.endDate|date('M j, Y') }} + +
+
+
+
+ + +
+
+ +    + + +
+
+ + +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+
+
+ {{ form_row(form.members, { + 'label': 'Members Present', + 'label_attr': {'class': ''}, + 'attr': {'class': 'form-control'} + }) }} +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+
+
+ +
+
+
+ {{ form_end(form) }} +
+
+
+
+{% endblock %} diff --git a/templates/internal/cases/notes/edit-note.html.twig b/templates/internal/cases/notes/edit-note.html.twig new file mode 100644 index 0000000..e69de29 diff --git a/templates/internal/cases/notes/list-notes.html.twig b/templates/internal/cases/notes/list-notes.html.twig new file mode 100644 index 0000000..d2dfd6e --- /dev/null +++ b/templates/internal/cases/notes/list-notes.html.twig @@ -0,0 +1,82 @@ +{% extends 'base.html.twig' %} + +{% block body %} + {{ block('nav', 'internal/libs/nav.html.twig') }} + +
+ {{ block('topnav', 'internal/libs/top-nav.html.twig') }} + +
+
+
+
+
+
+
+
Referral Note List
+
+
+    + +
+
+
+
+
+ Filter:   +    + +
+
+ + + + + + + + + + + + + {% for note in notes %} + {% set members = note.getMembers() %} + + + + + + + + + {% endfor %} + +
DOSServiceLocationMethodMembers Present
+ {{ note.date|date('M j, Y') }}
+ {{ note.startTime|date('g:i a') }}-{{ note.endTime|date('g:i a') }} +
{{ note.referral.serviceCode.value }}{{ note.location.value }}{{ note.method.name|replace({'_': ' '})|lower|capitalize }} + {{ dump(members) }} +
+
+
+
+
+
+
+
+{% endblock %}