Add NoteController and associated templates, entities, repository.

This commit is contained in:
Ryan Prather 2024-12-17 11:56:14 -05:00
parent 803ce84996
commit db756d83e4
17 changed files with 1408 additions and 0 deletions

View File

@ -0,0 +1,168 @@
<?php
namespace App\Controller;
use App\Entity\Member;
use App\Entity\Referral;
use App\Entity\StandardNote;
use App\Entity\User;
use App\Entity\UserCase;
use App\Entity\VisitNote;
use App\Enums\NoteLocation;
use App\Enums\NoteMethod;
use App\Enums\ReferralServiceType;
use App\Form\StandardNoteFormType;
use App\Form\VisitNoteFormType;
use App\Libs\Breadcrumb;
use App\Libs\NavList;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Security\Http\Attribute\CurrentUser;
class NoteController extends AbstractController
{
public function __construct(
private EntityManagerInterface $entityManager,
private array $navLinks = [],
) {
$this->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),
));
}
}

199
src/Entity/Note.php Normal file
View File

@ -0,0 +1,199 @@
<?php
namespace App\Entity;
use App\Enums\NoteLocation;
use App\Enums\NoteMethod;
use App\Enums\NoteStatus;
use App\Repository\NoteRepository;
use DateTime;
use Doctrine\Common\Collections\Collection;
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\ORM\Mapping\MappedSuperclass;
use Symfony\Bridge\Doctrine\Types\UuidType;
use Symfony\Component\Uid\Uuid;
#[MappedSuperclass(NoteRepository::class)]
class Note
{
#[ORM\Id]
#[ORM\Column(type: UuidType::NAME, unique: true)]
#[ORM\GeneratedValue(strategy: 'CUSTOM')]
#[ORM\CustomIdGenerator(class: 'doctrine.uuid_generator')]
private ?Uuid $id = null;
#[ORM\Column(type: Types::DATE_MUTABLE)]
private ?\DateTimeInterface $date = null;
#[ORM\ManyToOne(inversedBy: 'notes')]
#[ORM\JoinColumn(nullable: false)]
private ?Referral $referral = null;
#[ORM\Column(type: Types::TIME_MUTABLE)]
private ?\DateTimeInterface $startTime = null;
#[ORM\Column(type: Types::TIME_MUTABLE)]
private ?\DateTimeInterface $endTime = null;
#[ORM\Column(enumType: NoteStatus::class)]
private ?NoteStatus $status = null;
/**
* @var Collection<int, Member>
*/
#[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<int, Member>
*/
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;
}
}

View File

@ -0,0 +1,26 @@
<?php
namespace App\Entity;
use App\Repository\StandardNoteRepository;
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity(repositoryClass: StandardNoteRepository::class)]
class StandardNote extends Note
{
#[ORM\Column(type: Types::TEXT, nullable: true)]
private ?string $note = null;
public function getNote(): ?string
{
return $this->note;
}
public function setNote(?string $note): static
{
$this->note = $note;
return $this;
}
}

162
src/Entity/VisitNote.php Normal file
View File

@ -0,0 +1,162 @@
<?php
namespace App\Entity;
use App\Enums\VisitQualityLevel;
use App\Repository\VisitNoteRepository;
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity(repositoryClass: VisitNoteRepository::class)]
class VisitNote extends Note
{
#[ORM\Column(type: Types::TEXT)]
private ?string $narrative = null;
#[ORM\Column(type: Types::TEXT)]
private ?string $strengths = null;
#[ORM\Column(type: Types::TEXT)]
private ?string $issues = null;
#[ORM\Column(type: Types::TEXT)]
private ?string $recommendation = null;
#[ORM\Column(enumType: VisitQualityLevel::class)]
private ?VisitQualityLevel $parentalRole = null;
#[ORM\Column(enumType: VisitQualityLevel::class)]
private ?VisitQualityLevel $childDevelopment = null;
#[ORM\Column(enumType: VisitQualityLevel::class)]
private ?VisitQualityLevel $appropriateResponse = null;
#[ORM\Column(enumType: VisitQualityLevel::class)]
private ?VisitQualityLevel $childAheadOfSelf = null;
#[ORM\Column(enumType: VisitQualityLevel::class)]
private ?VisitQualityLevel $showsEmpathy = null;
#[ORM\Column(enumType: VisitQualityLevel::class)]
private ?VisitQualityLevel $childFocused = null;
public function getNarrative(): ?string
{
return $this->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;
}
}

View File

@ -0,0 +1,36 @@
<?php
namespace App\Enums;
enum NoteLocation: string
{
case AGENCY_OTHER_PROFESSIONAL = 'Agency/Other Professional';
case AT_HOME = 'At Home';
case CHILDPLACE = 'Childplace';
case CHURCH = 'Church';
case CLIENT_HOME = 'Client Home';
case CLIENT_SEARCH = 'Client Search';
case COMMUNITY_OUTING = 'Community Outing';
case COURT_HOUSE = 'Court House';
case DCS_OFFICE = 'DCS Office';
case DENTIST_EYE_APPOINTMENT = 'Dentist/Eye appointment';
case DOCTOR_DENTIST_APPOINTMENT = 'Doctor/Dentist Appointment';
case EMPLOYMENT_SEARCH = 'Employment Search';
case FOOD_PANTRY = 'Food Pantry';
case FOSTER_HOME = 'Foster home';
case HOUSING_SEARCH = 'Housing Search';
case JAIL_CORRECTIONAL_FACILITY = 'Jail/Correctional Facility';
case LIBRARY = 'Library';
case OTHER = 'Other';
case PARK = 'Park';
case PROBATION_OFFICE = 'Probation Office';
case RELATIVE_HOME = 'Relative home';
case RESTAURANT = 'Restaurant';
case SCHOOL = 'School';
case SHOPPING_CENTER = 'Shopping Center';
case TELEPHONE = 'Telephone';
case TEXT_MESSAGE = 'Text Message';
case THERAPY_APPOINTMENT = 'Therapy Appointment';
case VEHICLE_TRANSPORTATION = 'Vehicle/Transportation';
case WELLSTONE = 'Wellstone';
}

25
src/Enums/NoteMethod.php Normal file
View File

@ -0,0 +1,25 @@
<?php
namespace App\Enums;
enum NoteMethod: int
{
case DCS_PROBATION_CONTACT = 0;
case BILLABLE = 1;
case BILLABLE_CASE_CONFERENCE = 2;
case BILLABLE_CFTM = 3;
case BILLABLE_COURT = 4;
case BILLABLE_CRISIS_PHONE_CALL = 6;
case BILLABLE_FACE_TO_FACE = 7;
case BILLABLE_PA_DENIED_BY_MEDIACID_SERVICES_TO_BE_PAID_BY_DCS = 8;
case BILLABLE_REPORT_WRITING = 9;
case BILLABLE_SUPERVISED_VISIT = 10;
case BILLABLE_TESTING_INTERPRETATION = 11;
case BILLED_TO_MEDIACID_INSURANCE = 12;
case CANCEL_BY_CLIENT = 13;
case CANCEL_BY_PROVIDER = 14;
case COLLATERAL = 15;
case NO_SHOW = 16;
case NON_BILLABLE = 17;
case SUPERVISED_VISIT_PRIVATE = 18;
}

15
src/Enums/NoteStatus.php Normal file
View File

@ -0,0 +1,15 @@
<?php
namespace App\Enums;
enum NoteStatus: string
{
case DRAFT = 'draft';
case IN_PROGRESS = 'in progress';
case IN_REVIEW = 'in review';
case PUBLISHED = 'published';
case SUBMITTED = 'submitted';
case APPROVED = 'approved';
case REJECTED = 'rejected';
case PAID = 'paid';
}

View File

@ -0,0 +1,11 @@
<?php
namespace App\Enums;
enum VisitQualityLevel: int
{
case RARELY = 0;
case OFTEN = 1;
case OCCASIONALLY = 2;
case ALWAYS = 3;
}

View File

@ -0,0 +1,69 @@
<?php
namespace App\Form;
use App\Entity\Member;
use App\Entity\Referral;
use App\Entity\StandardNote;
use App\Enums\NoteLocation;
use App\Enums\NoteMethod;
use App\Enums\NoteStatus;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\EnumType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class StandardNoteFormType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$members = $options['members'];
$builder
->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',
]);
}
}

View File

@ -0,0 +1,84 @@
<?php
namespace App\Form;
use App\Entity\Member;
use App\Entity\VisitNote;
use App\Enums\NoteLocation;
use App\Enums\NoteMethod;
use App\Enums\NoteStatus;
use App\Enums\VisitQualityLevel;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\EnumType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class VisitNoteFormType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$members = $options['members'];
$builder
->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',
]);
}
}

View File

@ -0,0 +1,61 @@
<?php
namespace App\Repository;
use App\Entity\Note;
use App\Entity\Referral;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
/**
* @extends ServiceEntityRepository<Notes>
*/
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()
// ;
// }
}

View File

@ -0,0 +1,86 @@
<?php
namespace App\Repository;
use App\Entity\Referral;
use App\Entity\StandardNote;
use App\Entity\User;
use DateTimeImmutable;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
/**
* @extends ServiceEntityRepository<StandardNote>
*/
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()
// ;
// }
}

View File

@ -0,0 +1,86 @@
<?php
namespace App\Repository;
use App\Entity\Referral;
use App\Entity\User;
use App\Entity\VisitNote;
use DateTimeImmutable;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
/**
* @extends ServiceEntityRepository<VisitNote>
*/
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()
// ;
// }
}

View File

@ -0,0 +1,107 @@
{% extends 'base.html.twig' %}
{% block body %}
{{ block('nav', 'internal/libs/nav.html.twig') }}
<main class="main-content position-relative max-height-vh-100 h-100 border-radius-lg ">
{{ block('topnav', 'internal/libs/top-nav.html.twig') }}
<section>
<div class="card card-plain">
<div class="card-header">
<h4 class="font-weight-bolder">Add Referral Note</h4>
<p class="mb-0">{{ referral.memberCase.caseName }}</p>
</div>
<div class="card-body">
{{ form_start(form) }}
{{ form_errors(form) }}
<div class='container'>
<div class='row'>
{% 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 %}
<span class='col{% if referral.hours < 40 %} bg-gradient-danger text-white{% endif %}'>
Hours:
{{ referral.hours }}
/
Remaining:
{{ referral.getHoursRemaining() }}
</span>
<span class='col {{ endDateWarning }}'>
Expiration Date:
{{ referral.endDate|date('M j, Y') }}
</span>
</div>
<div class='row' style='margin-top:10px;'>
<div class='col'>
<div class='input-group input-group-outline mb-3'>
<label for='note_form_date'></label>
<input type='date' name='{{ field_name(form.date) }}' id='note_form_date' class='form-control' title='Visit Date'/>
</div>
<div class='input-group input-group-outline mb-3'>
<label for='note_form_startTime'></label>
<input type='time' name='{{ field_name(form.startTime) }}' id='note_form_startTime' onchange='calcTime()' class='form-control' title='Start Time'/>&nbsp;&nbsp;
<label for='note_form_endTime'></label>
<input type='time' name='{{ field_name(form.endTime) }}' id='note_form_endTime' onchange='calcTime()' class='form-control' title='End Time'/>
</div>
<div class='input-group input-group-outline mb-3'>
<input type='text' id='case-mins' style='width:49%;margin-right:5px;' disabled='disabled' title='Case Minutes'/>
<input type='text' id='case-hours' style='width:49%;margin-left:5px;' disabled='disabled' title='Case Hours'/>
</div>
<div class='input-group input-group-outline mb-3'>
<select name='{{ field_name(form.status) }}' id='note_form_status' class='form-control'>
<option value=''>-- Status --</option>
{% for s in enum('App\\Enums\\NoteStatus').cases() %}
<option value='{{ s.value }}'>{{ s.value|capitalize }}</option>
{% endfor %}
</select>
</div>
<div class='input-group input-group-outline mb-3'>
<select name='{{ field_name(form.location) }}' id='note_form_location' class='form-control'>
<option value=''>-- Location --</option>
{% for l in enum('App\\Enums\\NoteLocation').cases() %}
<option value='{{ l.value }}' {% if l == default_location %} selected='selected' {% endif %}>{{ l.value }}</option>
{% endfor %}
</select>
</div>
<div class='input-group input-group-outline mb-3'>
<select name='{{ field_name(form.method) }}' id='note_form_method' class='form-control'>
<option value=''>-- Method --</option>
{% for m in enum('App\\Enums\\NoteMethod').cases() %}
<option value='{{ m.value }}' {% if m == default_method %} selected='selected' {% endif %}>{{ m.name|replace({'_': ' '})|lower|capitalize }}</option>
{% endfor %}
</select>
</div>
</div>
<div class='col'>
<div class='input-group input-group-outline mb-3'>
{{ form_row(form.members, {
'label': 'Members Present',
'label_attr': {'class': ''},
'attr': {'class': 'form-control'}
}) }}
</div>
<div class='input-group input-group-outline mb-3'>
<label for='case_note_note' class='form-label'>Notes</label>
<textarea name='{{ field_name(form.note) }}' id='case_note_note' class='form-control' style='width:100%;height:300px;'></textarea>
</div>
</div>
</div>
<div class='row'>
<div class="text-center">
<button type="submit" class="btn btn-lg bg-gradient-dark btn-lg w-100 mt-4 mb-0">Save Note</button>
</div>
</div>
</div>
{{ form_end(form) }}
</div>
</div>
</section>
</main>
{% endblock %}

View File

@ -0,0 +1,191 @@
{% extends 'base.html.twig' %}
{% block body %}
{{ block('nav', 'internal/libs/nav.html.twig') }}
<main class="main-content position-relative max-height-vh-100 h-100 border-radius-lg ">
{{ block('topnav', 'internal/libs/top-nav.html.twig') }}
<section>
<div class="card card-plain">
<div class="card-header">
<h4 class="font-weight-bolder">Add Referral Note</h4>
<p class="mb-0">{{ referral.memberCase.caseName }}</p>
</div>
<div class="card-body">
{{ form_start(form) }}
{{ form_errors(form) }}
<div class='container'>
<div class='row'>
{% 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 %}
<span class='col{% if referral.hours < 40 %} bg-gradient-danger text-white{% endif %}'>
Hours:
{{ referral.hours }}
/
Remaining:
{{ referral.getHoursRemaining() }}
</span>
<span class='col {{ endDateWarning }}'>
Expiration Date:
{{ referral.endDate|date('M j, Y') }}
</span>
</div>
<div class='row' style='margin-top:10px;'>
<div class='col'>
<div class='input-group input-group-outline mb-3'>
<label for='note_form_date'></label>
<input type='date' name='{{ field_name(form.date) }}' id='note_form_date' class='form-control' title='Visit Date'/>
</div>
<div class='input-group input-group-outline mb-3'>
<label for='note_form_startTime'></label>
<input type='time' name='{{ field_name(form.startTime) }}' id='note_form_startTime' onchange='calcTime()' class='form-control' title='Start Time'/>&nbsp;&nbsp;
<label for='note_form_endTime'></label>
<input type='time' name='{{ field_name(form.endTime) }}' id='note_form_endTime' onchange='calcTime()' class='form-control' title='End Time'/>
</div>
<div class='input-group input-group-outline mb-3'>
<input type='text' id='case-mins' style='width:49%;margin-right:5px;' disabled='disabled' title='Case Minutes'/>
<input type='text' id='case-hours' style='width:49%;margin-left:5px;' disabled='disabled' title='Case Hours'/>
</div>
<div class='input-group input-group-outline mb-3'>
<select name='{{ field_name(form.status) }}' id='note_form_status' class='form-control'>
<option value=''>-- Status --</option>
{% for s in enum('App\\Enums\\NoteStatus').cases() %}
<option value='{{ s.value }}'>{{ s.value|capitalize }}</option>
{% endfor %}
</select>
</div>
<div class='input-group input-group-outline mb-3'>
<select name='{{ field_name(form.location) }}' id='note_form_location' class='form-control'>
<option value=''>-- Location --</option>
{% for l in enum('App\\Enums\\NoteLocation').cases() %}
<option value='{{ l.value }}' {% if l == default_location %} selected='selected' {% endif %}>{{ l.value }}</option>
{% endfor %}
</select>
</div>
<div class='input-group input-group-outline mb-3'>
<select name='{{ field_name(form.method) }}' id='note_form_method' class='form-control'>
<option value=''>-- Method --</option>
{% for m in enum('App\\Enums\\NoteMethod').cases() %}
<option value='{{ m.value }}' {% if m == default_method %} selected='selected' {% endif %}>{{ m.name|replace({'_': ' '})|lower|capitalize }}</option>
{% endfor %}
</select>
</div>
<div class='input-group input-group-outline mb-3'>
<label for='note_form_parentalRole'>Parental Role</label>
</div>
<div class='input-group input-group-outline mb-3'>
<select name='{{ field_name(form.parentalRole) }}' id='note_form_parentalRole' class='form-control'>
<option value=''>-- Select --</option>
{% for q in enum('App\\Enums\\VisitQualityLevel').cases() %}
<option value='{{ q.value }}'>{{ q.name|lower|capitalize }}</option>
{% endfor %}
</select>
</div>
<div class='input-group input-group-outline mb-3'>
<label for='note_form_childDevelopment'>Child Development</label>
</div>
<div class='input-group input-group-outline mb-3'>
<select name='{{ field_name(form.childDevelopment) }}' id='note_form_childDevelopment' class='form-control'>
<option value=''>-- Select --</option>
{% for q in enum('App\\Enums\\VisitQualityLevel').cases() %}
<option value='{{ q.value }}'>{{ q.name|lower|capitalize }}</option>
{% endfor %}
</select>
</div>
<div class='input-group input-group-outline mb-3'>
<label for='note_form_appropriateResponse'>Appropriate Response</label>
</div>
<div class='input-group input-group-outline mb-3'>
<select name='{{ field_name(form.appropriateResponse) }}' id='note_form_appropriateResponse' class='form-control'>
<option value=''>-- Select --</option>
{% for q in enum('App\\Enums\\VisitQualityLevel').cases() %}
<option value='{{ q.value }}'>{{ q.name|lower|capitalize }}</option>
{% endfor %}
</select>
</div>
<div class='input-group input-group-outline mb-3'>
<label for='note_form_childAheadOfSelf'>Child Ahead Of Self</label>
</div>
<div class='input-group input-group-outline mb-3'>
<select name='{{ field_name(form.childAheadOfSelf) }}' id='note_form_childAheadOfSelf' class='form-control'>
<option value=''>-- Select --</option>
{% for q in enum('App\\Enums\\VisitQualityLevel').cases() %}
<option value='{{ q.value }}'>{{ q.name|lower|capitalize }}</option>
{% endfor %}
</select>
</div>
<div class='input-group input-group-outline mb-3'>
<label for='note_form_showsEmpathy'>Shows Empathy</label>
</div>
<div class='input-group input-group-outline mb-3'>
<select name='{{ field_name(form.showsEmpathy) }}' id='note_form_showsEmpathy' class='form-control'>
<option value=''>-- Select --</option>
{% for q in enum('App\\Enums\\VisitQualityLevel').cases() %}
<option value='{{ q.value }}'>{{ q.name|lower|capitalize }}</option>
{% endfor %}
</select>
</div>
<div class='input-group input-group-outline mb-3'>
<label for='note_form_childFocused'>Child Focused</label>
</div>
<div class='input-group input-group-outline mb-3'>
<select name='{{ field_name(form.childFocused) }}' id='note_form_childFocused' class='form-control'>
<option value=''>-- Select --</option>
{% for q in enum('App\\Enums\\VisitQualityLevel').cases() %}
<option value='{{ q.value }}'>{{ q.name|lower|capitalize }}</option>
{% endfor %}
</select>
</div>
</div>
<div class='col'>
<div class='input-group input-group-outline mb-3'>
{{ form_row(form.members, {
'label': 'Members Present',
'label_attr': {'class': ''},
'attr': {'class': 'form-control'}
}) }}
</div>
<div class='input-group input-group-outline mb-3'>
<label for='note_form_narrative'>Observed Narrative</label>
<textarea name='{{ field_name(form.narrative) }}' id='note_form_narrative' class='form-control' style='width:100%;height:200px;'></textarea>
</div>
<div class='input-group input-group-outline mb-3'>
<label for='note_form_strengths'>Observed Strengths</label>
<textarea name='{{ field_name(form.strengths) }}' id='note_form_strengths' class='form-control' style='width:100%;height:200px;'></textarea>
</div>
<div class='input-group input-group-outline mb-3'>
<label for='note_form_issues'>Observed Issues</label>
<textarea name='{{ field_name(form.issues) }}' id='note_form_issues' class='form-control' style='width:100%;height:100px;'></textarea>
</div>
<div class='input-group input-group-outline mb-3'>
<label for='note_form_recommendation'>Recommendation</label>
<textarea name='{{ field_name(form.recommendation) }}' id='note_form_recommendation' class='form-control' style='width:100%;height:100px;'></textarea>
</div>
</div>
</div>
<div class='row'>
<div class="text-center">
<button type="submit" class="btn btn-lg bg-gradient-dark btn-lg w-100 mt-4 mb-0">Save Note</button>
</div>
</div>
</div>
{{ form_end(form) }}
</div>
</div>
</section>
</main>
{% endblock %}

View File

@ -0,0 +1,82 @@
{% extends 'base.html.twig' %}
{% block body %}
{{ block('nav', 'internal/libs/nav.html.twig') }}
<main class="main-content position-relative max-height-vh-100 h-100 border-radius-lg ">
{{ block('topnav', 'internal/libs/top-nav.html.twig') }}
<div class="container-fluid py-2">
<div class="row">
<div class="col-12">
<div class="card my-4">
<div class="card-header p-0 position-relative mt-n4 mx-3 z-index-2">
<div class="d-flex justify-content-between bg-gradient-dark shadow-dark border-radius-lg pt-4 pb-3 ps-3 p-2">
<div>
<h6 class="text-white text-capitalize ps-3">Referral Note List</h6>
</div>
<div>
<select id='referralList' onchange='filterNotes()'>
<option value=''>-- Select Referral --</option>
{% for c in cases %}
<optgroup label='{{ c.memberCase.caseName }}'>
{% for r in c.memberCase.referrals %}
<option value='{{ r.id }}'>
{{ r.referralId }}
/
{{ r.serviceCode.value }}
</option>
{% endfor %}
</optgroup>
{% endfor %}
</select>&nbsp;&nbsp;
<button type="button" class="btn btn-block btn-light mb-3" onclick="window.open('/index.php/add-note/'+document.getElementById('referralList').value, '_self')">Add Note</button>
</div>
</div>
</div>
<div class="card-body px-0 pb-2">
<div>
Filter:&nbsp;&nbsp;
<input type='date' id='startDate' onchange='filterNotes()' title='Start Date'/>&nbsp;&nbsp;
<input type='date' id='endDate' onchange='filterNotes()' title='End Date'/>
</div>
<div class="table-responsive p-0">
<table class="table align-items-center mb-0">
<thead>
<tr>
<th class="text-uppercase text-secondary text-xxs font-weight-bolder opacity-7">DOS</th>
<th class="text-uppercase text-secondary text-xxs font-weight-bolder opacity-7 ps-2">Service</th>
<th class="text-center text-uppercase text-secondary text-xxs font-weight-bolder opacity-7">Location</th>
<th class="text-center text-uppercase text-secondary text-xxs font-weight-bolder opacity-7">Method</th>
<th class='text-right text-uppercase text-secondary text-xxs font-weight-bolder opacity-7'>Members Present</th>
<th class="text-secondary opacity-7"></th>
</tr>
</thead>
<tbody id='note-list'>
{% for note in notes %}
{% set members = note.getMembers() %}
<tr>
<td>
{{ note.date|date('M j, Y') }}<br/>
{{ note.startTime|date('g:i a') }}-{{ note.endTime|date('g:i a') }}
</td>
<td>{{ note.referral.serviceCode.value }}</td>
<td class='text-center'>{{ note.location.value }}</td>
<td>{{ note.method.name|replace({'_': ' '})|lower|capitalize }}</td>
<td>
{{ dump(members) }}
</td>
<td></td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
</main>
{% endblock %}