Compare commits

...

20 Commits

Author SHA1 Message Date
78d149c348 Make case list available to users
Add case notes link
Move user list into admin section
2024-12-17 12:11:32 -05:00
de81c2ffb1 Hide Add Case button if not admin or case manager
Hide case filter if user not admin or case manager
Hide case edit button and case assign button if not admin or case manager
2024-12-17 12:09:00 -05:00
c76783b79f Don't display add button if not a case manager or admin
Display highlighting for referrals close to expiration
Highlight hours if < 40
2024-12-17 12:07:13 -05:00
6287ec6e2f Formatting 2024-12-17 12:05:36 -05:00
b2e90908cd Formatting 2024-12-17 12:05:16 -05:00
c7c14b5726 Formatting 2024-12-17 12:04:48 -05:00
f6addcc199 Add populateNotes method to populate the appropriate notes within a referral 2024-12-17 12:03:25 -05:00
de08885e0b Add notes and present members collections
Add time calculation and getHoursRemaining helper variable and method
2024-12-17 12:02:01 -05:00
365a486c84 Add getName helper method 2024-12-17 11:57:10 -05:00
c2710b7142 Add PRESENT_LINK constant 2024-12-17 11:56:38 -05:00
db756d83e4 Add NoteController and associated templates, entities, repository. 2024-12-17 11:56:14 -05:00
803ce84996 Convert to use NavList::PRESENT_LINK constant 2024-12-17 11:54:14 -05:00
ef053bae29 Convert to use NavList::PRESENT_LINK constant 2024-12-17 11:53:58 -05:00
a42ce28f14 Use Breadcrumb class
Convert to use NavList::PRESENT_LINK constant
2024-12-17 11:53:45 -05:00
f17be14174 Convert to use NavList::PRESENT_LINK constant 2024-12-17 11:53:12 -05:00
4405cb19ff Convert to use NavList::PRESENT_LINK constant 2024-12-17 11:52:48 -05:00
5975785580 Filter case list if not admin
Convert to use NavList::PRESENT_LINK constant
2024-12-17 11:52:10 -05:00
9fc84a85e9 Convert to use NavList::PRESENT_LINK constant 2024-12-17 11:50:32 -05:00
0ca406f635 Fix CSRF not showing the token 2024-12-17 11:49:22 -05:00
804652ac67 Fix asset links 2024-12-13 09:00:01 -05:00
39 changed files with 1671 additions and 71 deletions

11
config/packages/csrf.yaml Normal file
View File

@ -0,0 +1,11 @@
# Enable stateless CSRF protection for forms and logins/logouts
framework:
form:
csrf_protection:
token_id: submit
csrf_protection:
stateless_token_ids:
- submit
- authenticate
- logout

View File

@ -1,16 +1,16 @@
# see https://symfony.com/doc/current/reference/configuration/framework.html
framework:
secret: '%env(APP_SECRET)%'
#csrf_protection: true
secret: "%env(APP_SECRET)%"
csrf_protection: true
# Note that the session will be started ONLY if you read or write from it.
session: true
# Note that the session will be started ONLY if you read or write from it.
session: true
#esi: true
#fragments: true
#esi: true
#fragments: true
when@test:
framework:
test: true
session:
storage_factory_id: session.storage.factory.mock_file
framework:
test: true
session:
storage_factory_id: session.storage.factory.mock_file

View File

@ -34,7 +34,7 @@ class AdminController extends AbstractController
public function adminDashboard(#[CurrentUser()] User $user): Response
{
$this->denyAccessUnlessGranted('ROLE_ADMIN');
$this->navLinks['admin_dashboard'] = 'nav-link text-white active bg-gradient-dark';
$this->navLinks['admin_dashboard'] = NavList::PRESENT_LINK;
return $this->render(
'internal/admin/admin-dashboard.html.twig',
@ -66,7 +66,7 @@ class AdminController extends AbstractController
$users[$idx]->setSupervisor($supervisor);
}
$this->navLinks['user_list'] = 'nav-link text-white active bg-gradient-dark';
$this->navLinks['user_list'] = NavList::PRESENT_LINK;
return $this->render(
'internal/admin/users/list-users.html.twig',
@ -136,7 +136,7 @@ class AdminController extends AbstractController
return $this->redirectToRoute('app_list_users');
}
$this->navLinks['user_list'] = 'nav-link text-white active bg-gradient-dark';
$this->navLinks['user_list'] = NavList::PRESENT_LINK;
return $this->render(
'internal/admin/users/add-user.html.twig',
@ -166,7 +166,7 @@ class AdminController extends AbstractController
$form = $this->createForm(EditUserFormType::class, $user);
$form->handleRequest($request);
$this->navLinks['user_list'] = 'nav-link text-white active bg-gradient-dark';
$this->navLinks['user_list'] = NavList::PRESENT_LINK;
if ($form->isSubmitted() && $form->isValid()) {
$user->setName($form->get('name')->getData())

View File

@ -2,7 +2,6 @@
namespace App\Controller;
use Exception;
use App\Entity\MemberCase;
use App\Entity\ReferralSource;
use App\Entity\User;
@ -25,16 +24,22 @@ class CaseController extends AbstractController
private array $navLinks = []
) {
$this->navLinks = NavList::LIST;
$this->navLinks['case_list'] = NavList::PRESENT_LINK;
}
#[Route('/list-cases', name: 'app_list_cases')]
public function listCases(#[CurrentUser()] User $user): Response
{
$this->denyAccessUnlessGranted('ROLE_ADMIN');
$this->navLinks['case_list'] = 'nav-link text-white active bg-gradient-dark';
$cases = $this->entityManager->getRepository(MemberCase::class)->findAll();
if($this->isGranted('ROLE_ADMIN')) {
$cases = $this->entityManager->getRepository(MemberCase::class)->findAll();
} else {
$ucs = $this->entityManager->getRepository(UserCase::class)->findBy(['user' => $user]);
$cases = [];
foreach ($ucs as $uc) {
/** @var UserCase $uc */
$cases[] = $uc->getMemberCase();
}
}
$workers = $this->entityManager->getRepository(User::class)->getCaseWorkers();
return $this->render(
@ -58,8 +63,6 @@ class CaseController extends AbstractController
{
$this->denyAccessUnlessGranted('ROLE_ADMIN');
$this->navLinks['case_list'] = 'nav-link text-white active bg-gradient-dark';
$case = new MemberCase();
$form = $this->createForm(MemberCaseFormType::class, $case);
@ -98,7 +101,6 @@ class CaseController extends AbstractController
public function editCase(Request $request, #[CurrentUser()] User $admin, string $id): Response
{
$this->denyAccessUnlessGranted('ROLE_ADMIN');
$this->navLinks['case_list'] = 'nav-link text-white active bg-gradient-dark';
$case = $this->entityManager->getRepository(MemberCase::class)->find($id);
$form = $this->createForm(MemberCaseFormType::class, $case);
@ -137,7 +139,6 @@ class CaseController extends AbstractController
public function assignCase(string $id, Request $request, #[CurrentUser()] User $admin): Response
{
$this->denyAccessUnlessGranted('ROLE_ADMIN');
$this->navLinks['case_list'] = 'nav-link text-white active bg-gradient-dark';
$caseWorkers = $this->entityManager->getRepository(User::class)->getCaseWorkers();
$case = $this->entityManager->getRepository(MemberCase::class)->find($id);

View File

@ -32,7 +32,7 @@ class DefaultController extends AbstractController
return $this->redirectToRoute('app_register_step', ['step' => RegistrationController::REGISTER_STEP_TWO]);
}
$this->navLinks['user_dashboard'] = 'nav-link text-white active bg-gradient-dark';
$this->navLinks['user_dashboard'] = NavList::PRESENT_LINK;
return $this->render(
'internal/dashboard.html.twig',
@ -53,7 +53,7 @@ class DefaultController extends AbstractController
{
$this->denyAccessUnlessGranted('IS_AUTHENTICATED_FULLY');
$this->navLinks['profile'] = 'nav-link text-white active bg-gradient-dark';
$this->navLinks['profile'] = NavList::PRESENT_LINK;
return $this->render(
'internal/profile.html.twig',
@ -68,5 +68,4 @@ class DefaultController extends AbstractController
)
);
}
}

View File

@ -22,6 +22,7 @@ class MemberController extends AbstractController
private array $navLinks = [],
) {
$this->navLinks = NavList::LIST;
$this->navLinks['case_list'] = NavList::PRESENT_LINK;
}
#[Route('/list-members/{id}', name: 'app_case_members')]
@ -36,7 +37,7 @@ class MemberController extends AbstractController
$this->navLinks,
[
'breadcrumbs' => [
new Breadcrumb($this->generateUrl('app_case_list'), 'List Cases'),
new Breadcrumb($this->generateUrl('app_list_cases'), 'List Cases'),
new Breadcrumb($this->generateUrl('app_case_members', ['id' => $id]), 'List Members'),
],
'notifications' => $user->retrieveUnreadNotifications(),
@ -105,7 +106,7 @@ class MemberController extends AbstractController
$this->navLinks,
[
'breadcrumbs' => [
new Breadcrumb($this->generateUrl('app_case_list'), 'List Cases'),
new Breadcrumb($this->generateUrl('app_list_cases'), 'List Cases'),
new Breadcrumb($this->generateUrl('app_case_members', ['id' => $id]), 'List Members'),
new Breadcrumb($this->generateUrl('app_case_add_member', ['id' => $id]), 'Add Member'),
],
@ -170,7 +171,7 @@ class MemberController extends AbstractController
$this->navLinks,
[
'breadcrumbs' => [
new Breadcrumb($this->generateUrl('app_case_list'), 'List Cases'),
new Breadcrumb($this->generateUrl('app_list_cases'), 'List Cases'),
new Breadcrumb($this->generateUrl('app_case_members', ['id' => $caseId]), 'List Members'),
new Breadcrumb($this->generateUrl('app_case_edit_member', ['caseId' => $caseId, 'memberId' => $memberId]), 'Edit Member'),
],

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),
));
}
}

View File

@ -6,6 +6,7 @@ use App\Entity\MemberCase;
use App\Entity\Referral;
use App\Entity\User;
use App\Form\ReferralFormType;
use App\Libs\Breadcrumb;
use App\Libs\NavList;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
@ -21,6 +22,7 @@ class ReferralController extends AbstractController
private array $navLinks = []
) {
$this->navLinks = NavList::LIST;
$this->navLinks['case_list'] = NavList::PRESENT_LINK;
}
#[Route('/list-referrals/{id}', name: 'app_list_referrals')]
@ -36,8 +38,8 @@ class ReferralController extends AbstractController
$this->navLinks,
[
'breadcrumbs' => [
'Case',
'Referrals'
new Breadcrumb($this->generateUrl('app_list_cases'), 'List Cases'),
new Breadcrumb($this->generateUrl('app_list_referrals', ['id' => $case->getId()]), 'Referrals'),
],
'notifications' => $user->retrieveUnreadNotifications(),
'case' => $case,
@ -74,8 +76,9 @@ class ReferralController extends AbstractController
$this->navLinks,
[
'breadcrumbs' => [
'Case',
'Add Referral'
new Breadcrumb($this->generateUrl('app_list_cases'), 'List Cases'),
new Breadcrumb($this->generateUrl('app_list_referrals', ['id' => $case->getId()]), 'Referrals'),
new Breadcrumb($this->generateUrl('app_case_add_referral', ['id' => $case->getId()]), 'Add Referral')
],
'notifications' => $user->retrieveUnreadNotifications(),
'case' => $case,
@ -108,8 +111,9 @@ class ReferralController extends AbstractController
$this->navLinks,
[
'breadcrumbs' => [
'Case',
'Edit Referral'
new Breadcrumb($this->generateUrl('app_list_cases'), 'List Cases'),
new Breadcrumb($this->generateUrl('app_list_referrals', ['id' => $case->getId()]), 'Referrals'),
new Breadcrumb($this->generateUrl('app_case_edit_referral', ['caseId' => $case->getId(), 'referralId' => $referral->getId()]), 'Edit Referral'),
],
'notifications' => $user->retrieveUnreadNotifications(),
'case' => $case,

View File

@ -21,13 +21,13 @@ class ReferralSourceController extends AbstractController
private array $navList = []
) {
$this->navList = NavList::LIST;
$this->navList['referral_sources'] = NavList::PRESENT_LINK;
}
#[Route('/list-referral-sources', name: 'app_referral_source')]
public function listReferralSources(#[CurrentUser()] User $user): Response
{
$this->denyAccessUnlessGranted('ROLE_ADMIN');
$this->navList['referral_sources'] = 'nav-link text-white active bg-gradient-dark';
$sources = $this->entityManager->getRepository(ReferralSource::class)->retrieveOrderedList();
@ -50,7 +50,6 @@ class ReferralSourceController extends AbstractController
public function addSource(Request $request, #[CurrentUser()] User $user): Response
{
$this->denyAccessUnlessGranted('ROLE_ADMIN');
$this->navList['referral_sources'] = 'nav-link text-white active bg-gradient-dark';
$rs = new ReferralSource();
$form = $this->createForm(ReferralSourceFormType::class, $rs);
@ -88,7 +87,6 @@ class ReferralSourceController extends AbstractController
public function editSource(Request $request, #[CurrentUser()] User $user, string $id): Response
{
$this->denyAccessUnlessGranted('ROLE_ADMIN');
$this->navList['referral_sources'] = 'nav-link text-white active bg-gradient-dark';
$rs = $this->entityManager->getRepository(ReferralSource::class)->find($id);

View File

@ -20,13 +20,12 @@ class StaffController extends AbstractController
private array $navLinks = []
) {
$this->navLinks = NavList::LIST;
$this->navLinks['staff_dashboard'] = NavList::PRESENT_LINK;
}
#[Route('/staff-dashboard', name: 'app_staff_dashboard')]
public function staffDashboard(#[CurrentUser()] User $user): Response
{
$this->navLinks['staff_dashboard'] = 'nav-link text-white active bg-gradient-dark';
return $this->render(
'internal/staff/staff-dashboard.html.twig',
array_merge(

View File

@ -144,6 +144,11 @@ class Member
return $this;
}
public function getName(): string
{
return "{$this->firstName} {$this->lastName}";
}
public function getRelationship(): ?RelationshipType
{
return $this->relationship;

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

@ -5,6 +5,8 @@ namespace App\Entity;
use App\Enums\DischargeReason;
use App\Enums\ReferralServiceType;
use App\Repository\ReferralRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Types\UuidType;
@ -41,6 +43,32 @@ class Referral
#[ORM\Column(type: Types::DATE_MUTABLE, nullable: true)]
private ?\DateTimeInterface $dischargeDate = null;
/**
* @var Collection<int, Note>
*/
#[ORM\OneToMany(targetEntity: Note::class, mappedBy: 'referral', orphanRemoval: true)]
private Collection $notes;
/**
* @var Collection<int, Member>
*/
#[ORM\ManyToMany(targetEntity: Member::class)]
private Collection $present;
/**
* @var float $hoursUsed
*/
private float $hoursUsed = 0.0;
/**
* Constructor
*/
public function __construct()
{
$this->notes = new ArrayCollection();
$this->present = new ArrayCollection();
}
public function getId(): ?Uuid
{
return $this->id;
@ -129,4 +157,75 @@ class Referral
return $this;
}
/**
* @return Collection<int, Note>
*/
public function getNotes(): Collection
{
return $this->notes;
}
public function addNote(Note $note): static
{
if (!$this->notes->contains($note)) {
$this->notes->add($note);
$note->setReferral($this);
$this->hoursUsed += ($note->calcTimeUsed() / 60);
}
return $this;
}
public function removeNote(Note $note): static
{
if ($this->notes->removeElement($note)) {
$this->hoursUsed -= $note->calcTimeUsed();
// set the owning side to null (unless already changed)
if ($note->getReferral() === $this) {
$note->setReferral(null);
}
}
return $this;
}
public function setNotes(ArrayCollection $notes): void
{
$this->notes = $notes;
$this->hoursUsed = 0.0;
foreach($this->notes as $note) {
$this->hoursUsed += ($note->calcTimeUsed() / 60);
}
}
/**
* @return Collection<int, Member>
*/
public function getPresent(): Collection
{
return $this->present;
}
public function addPresent(Member $present): static
{
if (!$this->present->contains($present)) {
$this->present->add($present);
}
return $this;
}
public function removePresent(Member $present): static
{
$this->present->removeElement($present);
return $this;
}
public function getHoursRemaining(): float
{
return $this->hours - $this->hoursUsed;
}
}

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

@ -13,5 +13,8 @@ class NavList
'case_list' => 'nav-link text-dark',
'add_user' => 'nav-link text-dark',
'referral_sources' => 'nav-link text-dark',
'case_notes' => 'nav-link text-dark',
];
public const PRESENT_LINK = 'nav-link text-white active bg-gradient-dark';
}

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

@ -4,7 +4,9 @@ namespace App\Repository;
use App\Entity\MemberCase;
use App\Entity\Referral;
use App\Enums\ReferralServiceType;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Persistence\ManagerRegistry;
/**
@ -19,7 +21,7 @@ class ReferralRepository extends ServiceEntityRepository
public function getActiveReferrals(MemberCase $case): array
{
return $this->createQueryBuilder('r')
$referrals = $this->createQueryBuilder('r')
->andWhere('r.dischargeDate IS NULL')
->andWhere('r.memberCase = :case')
->setParameter('case', $case->getId()->toBinary())
@ -27,11 +29,17 @@ class ReferralRepository extends ServiceEntityRepository
->getQuery()
->getResult()
;
foreach ($referrals as $idx => $ref) {
$this->populateNotes($referrals[$idx]);
}
return $referrals;
}
public function getClosedReferrals(MemberCase $case): array
{
return $this->createQueryBuilder('r')
$referrals = $this->createQueryBuilder('r')
->andWhere('r.dischargeDate IS NOT NULL')
->andWhere('r.memberCase = :case')
->setParameter('case', $case->getId()->toBinary())
@ -39,6 +47,35 @@ class ReferralRepository extends ServiceEntityRepository
->getQuery()
->getResult()
;
foreach ($referrals as $idx => $ref) {
$this->populateNotes($referrals[$idx]);
}
return $referrals;
}
public function populateNotes(Referral &$referral): void
{
$noteType = 'App\Entity\StandardNote';
if ($referral->getServiceCode() == ReferralServiceType::VS_THBB) {
$noteType = 'App\Entity\VisitNote';
}
$query = $this->getEntityManager()->createQuery("
SELECT n
FROM $noteType n
WHERE n.referral = :referral
");
$query->setParameter('referral', $referral->getId()->toBinary());
$res = $query->getResult();
if (!count($res)){
return;
}
$referral->setNotes(new ArrayCollection($res));
}
// /**

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

@ -60,9 +60,13 @@
{% endif %}
</td>
<td class='align-middle'>
<a href='{{ path('app_edit_user', {id: user.id}) }}' class='text-secondary font-weight-bold text-xs' data-toggle='tooltip' data-original-title='Edit user'>Edit</a>
<a href='{{ path('app_edit_user', {id: user.id}) }}' class='text-secondary font-weight-bold text-xs' tooltip='Edit User'>
<i class="material-symbols-rounded opacity-5">edit</i>
</a>
&nbsp;&nbsp;
<a href='{{ path('app_assign_supervisor', {id: user.id}) }}' class='text-secondary text-xs' data-toggle='tooltip' data-original-title='Assign supervisor'>Assign</a>
<a href='{{ path('app_assign_supervisor', {id: user.id}) }}' class='text-secondary text-xs' tooltip="Assign Supervisor">
<i class='material-symbols-rounded opacity-5'>badge</i>
</a>
</td>
</tr>
{% endfor %}

View File

@ -15,12 +15,15 @@
<div>
<h6 class="text-white text-capitalize ps-3">Case List</h6>
</div>
{% if is_granted('ROLE_CASE_MANAGER') or is_granted('ROLE_ADMIN') %}
<div>
<button type="button" class="btn btn-block btn-light mb-3" onclick="window.open('{{ path('app_add_case') }}', '_self')">Add Case</button>
</div>
{% endif %}
</div>
</div>
<div class="card-body px-0 pb-2">
{% if is_granted('ROLE_ADMIN') %}
<div>
Filters:
<select onchange='filterCasesByUser(this.value)'>
@ -31,6 +34,7 @@
{% endfor %}
</select>
</div>
{% endif %}
<div class="table-responsive p-0">
<table class="table align-items-center mb-0">
<thead>
@ -81,12 +85,14 @@
</p>
</td>
<td class='align-right'>
{% if is_granted('ROLE_CASE_MANAGER') or is_granted('ROLE_ADMIN') %}
<a href='{{ path('app_edit_case', {id: c.id}) }}' class='' title='Edit Case' data-toggle='tooltip'>
<i class="material-symbols-rounded opacity-5">edit</i>
</a>
<a href='{{ path('app_assign_case', {id: c.id}) }}' class='' title='Assign Case Worker' data-toggle='tooltip'>
<i class='material-symbols-rounded opacity-5'>badge</i>
</a>
{% endif %}
<a href='{{ path('app_list_referrals', {id: c.id}) }}' class='' title='List Referrals' data-toggle='tooltip'>
<i class='material-symbols-rounded opacity-5'>create_new_folder</i>
</a>

View File

@ -165,7 +165,7 @@
</div>
<div class='input-group input-group-outline mb-3'>
<input type='checkbox' name='{{ field_name(form.dcsApproved) }}' id='member_form_dcsApproved'/>
<input type='checkbox' name='{{ field_name(form.dcsApproved) }}' id='member_form_dcsApproved'/>&nbsp;&nbsp;
<label for='member_form_dcsApproved'>DCS Approved?</label>
</div>
</div>

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 %}

View File

@ -7,7 +7,6 @@
{{ 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</h4>
@ -16,7 +15,6 @@
<div class="card-body">
<div class="container">
{{ form_errors(form) }}
{{ form_start(form) }}
<div class='input-group input-group-outline mb-3'>

View File

@ -15,9 +15,11 @@
<div>
<h6 class="text-white text-capitalize ps-3">Referral List</h6>
</div>
{% if is_granted('ROLE_ADMIN') or is_granted('ROLE_CASE_MANAGER') %}
<div>
<button type="button" class="btn btn-block btn-light mb-3" onclick="window.open('{{ path('app_case_add_referral', {id: case.id}) }}', '_self')">Add Referral</button>
</div>
{% endif %}
</div>
</div>
<div class="card-body px-0 pb-2">
@ -37,6 +39,12 @@
</thead>
<tbody>
{% for r in openReferrals %}
{% set dateEndWarning = '' %}
{% if date('+14 days') >= r.endDate %}
{% set dateEndWarning = 'bg-gradient-danger text-white' %}
{% elseif date('+28 days') >= r.endDate %}
{% set dateEndWarning = 'bg-gradient-faded-warning' %}
{% endif %}
<tr>
<td>
<h6 class='mb-0 text-small'>{{ r.referralId }}</h6>
@ -44,13 +52,13 @@
<td>
<p class='text-xs font-weight-bold mb-0'>{{ r.serviceCode.value }}</p>
</td>
<td>
<p class='text-xs font-weight-bold mb-0'>{{ r.hours }}</p>
<td class='text-center {% if r.getHoursRemaining() < 40 %}bg-gradient-danger text-white{% endif %}'>
<p class='text-xs font-weight-bold mb-0'>{{ r.hours }} / {{ r.getHoursRemaining() }}</p>
</td>
<td>
<p class='text-xs font-weight-bold mb-0'>{{ r.endDate|date("F j, Y") }}</p>
<td class='text-center {{ dateEndWarning }}'>
<p class='text-xs font-weight-bold mb-0'>{{ r.endDate|date("M j, Y") }}</p>
</td>
<td class='align-right'>
<td class='text-center'>
<a href='{{ path('app_case_edit_referral', {caseId: case.id, referralId: r.id}) }}' class='' title='Edit Referral' data-toggle='tooltip'>
<i class="material-symbols-rounded opacity-5">edit</i>
</a>

View File

@ -1,5 +1,5 @@
{% block nav %}
<aside class="sidenav navbar navbar-vertical navbar-expand-xs border-radius-lg fixed-start ms-2 bg-white my-2" id="sidenav-main">
<aside class="sidenav navbar navbar-vertical navbar-expand-xs border-radius-lg fixed-start ms-2 my-2 bg-white" id="sidenav-main">
<div class="sidenav-header">
<i class="fas fa-times p-3 cursor-pointer text-dark opacity-5 position-absolute end-0 top-0 d-none d-xl-none" aria-hidden="true" id="iconSidenav"></i>
<a class="navbar-brand px-4 py-3 m-0" href="{{ path('app_dashboard') }}">
@ -17,16 +17,19 @@
<div class="collapse navbar-collapse w-auto " id="sidenav-collapse-main">
<ul class="navbar-nav">
{% if is_granted('ROLE_ADMIN') %}
<li class="nav-item mt-3">
<h6 class="ps-4 ms-2 text-uppercase text-xs text-dark font-weight-bolder opacity-5">Admin pages</h6>
</li>
<li class='nav-item'>
<a class="{{ admin_dashboard }}" href="{{ path('app_admin_dashboard') }}">
<i class="material-symbols-rounded opacity-5">dashboard</i>
<span class="nav-link-text ms-1">Admin Dashboard</span>
</a>
</li>
<li class='nav-item'>
<a class='{{ case_list }}' href='{{ path('app_list_cases') }}'>
<i class='material-symbols-rounded opacity-5'>cases</i>
<span class='nav-link-text ms-1'>Case List</span>
<li class="nav-item">
<a class="{{ user_list }}" href="{{ path('app_list_users') }}">
<i class="material-symbols-rounded opacity-5">assignment</i>
<span class="nav-link-text ms-1">User List</span>
</a>
</li>
<li clas='nav-item'>
@ -37,7 +40,7 @@
</li>
{% endif %}
{% if is_granted('ROLE_CASE_MANAGER') %}
{% if is_granted('ROLE_CASE_MANAGER') or is_granted('ROLE_ADMIN') %}
<li class='nav-item'>
<a class='{{ staff_dashboard }}' href='{{ path('app_staff_dashboard') }}'>
<i class='material-symbols-rounded opacity-5'>dashboard</i>
@ -46,12 +49,27 @@
</li>
{% endif %}
<li class="nav-item mt-3">
<h6 class="ps-4 ms-2 text-uppercase text-xs text-dark font-weight-bolder opacity-5">User pages</h6>
</li>
<li class="nav-item">
<a class="{{ user_dashboard }}" href="{{ path('app_dashboard') }}">
<i class="material-symbols-rounded opacity-5">dashboard</i>
<span class="nav-link-text ms-1">Dashboard</span>
</a>
</li>
<li class='nav-item'>
<a class='{{ case_list }}' href='{{ path('app_list_cases') }}'>
<i class='material-symbols-rounded opacity-5'>cases</i>
<span class='nav-link-text ms-1'>Case List</span>
</a>
</li>
<li class='nav-item'>
<a class='{{ case_notes }}' href='{{ path('app_list_notes') }}'>
<i class='material-symbols-rounded opacity-5'></i>
<span class='nav-link-text ms-1'>Case Notes</span>
</a>
</li>
<li class="nav-item mt-3">
<h6 class="ps-4 ms-2 text-uppercase text-xs text-dark font-weight-bolder opacity-5">Account pages</h6>
</li>
@ -61,14 +79,6 @@
<span class="nav-link-text ms-1">Profile</span>
</a>
</li>
{% if is_granted('ROLE_ADMIN') %}
<li class="nav-item">
<a class="{{ user_list }}" href="{{ path('app_list_users') }}">
<i class="material-symbols-rounded opacity-5">assignment</i>
<span class="nav-link-text ms-1">User List</span>
</a>
</li>
{% endif %}
<li class='nav-item'>
<a class='text-dark nav-link' href='{{ logout_path() }}'>
<i class='material-symbols-rounded opacity-5'>logout</i>

View File

@ -20,6 +20,15 @@
</div>
</div>
<ul class="navbar-nav d-flex align-items-center justify-content-end">
<li class="nav-item d-xl-none ps-3 d-flex align-items-center">
<a href="javascript:;" class="nav-link p-0 text-body" id="iconNavbarSidenav">
<div class="sidenav-toggler-inner">
<i class="sidenav-toggler-line"></i>
<i class="sidenav-toggler-line"></i>
<i class="sidenav-toggler-line"></i>
</div>
</a>
</li>
{% if is_granted('ROLE_ADMIN') %}
<li class="nav-item px-3 d-flex align-items-center" title="Site Settings">
<a href="javascript:;" class="nav-link text-body p-0">

View File

@ -17,7 +17,7 @@
<div class="container">
<div class="row">
<div class="col-6 d-lg-flex d-none h-100 my-auto pe-0 position-absolute top-0 start-0 text-center justify-content-center flex-column">
<div class="position-relative bg-gradient-primary h-100 m-3 px-7 border-radius-lg d-flex flex-column justify-content-center" style="background-image: url('/assets/img/illustrations/illustration-signup.jpg'); background-size: cover;"></div>
<div class="position-relative bg-gradient-primary h-100 m-3 px-7 border-radius-lg d-flex flex-column justify-content-center" style="background-image: url('{{ asset('img/illustrations/illustration-signup.jpg') }}'); background-size: cover;"></div>
</div>
<div class="col-xl-4 col-lg-5 col-md-7 d-flex flex-column ms-auto me-auto ms-lg-auto me-lg-5">
<div class="card card-plain">

View File

@ -17,13 +17,13 @@
<div class="container">
<div class="row">
<div class="col-6 d-lg-flex d-none h-100 my-auto pe-0 position-absolute top-0 start-0 text-center justify-content-center flex-column">
<div class="position-relative bg-gradient-primary h-100 m-3 px-7 border-radius-lg d-flex flex-column justify-content-center" style="background-image: url('/assets/img/illustrations/illustration-signup.jpg'); background-size: cover;"></div>
<div class="position-relative bg-gradient-primary h-100 m-3 px-7 border-radius-lg d-flex flex-column justify-content-center" style="background-image: url('{{ asset('img/illustrations/illustration-signup.jpg') }}'); background-size: cover;"></div>
</div>
<div class="col-xl-4 col-lg-5 col-md-7 d-flex flex-column ms-auto me-auto ms-lg-auto me-lg-5">
<div class="card card-plain">
<div class="card-header">
<h4 class="font-weight-bolder">Sign Up</h4>
<p class="mb-0">Enter your email and password to register</p>
<h4 class="font-weight-bolder">Add Company</h4>
<p class="mb-0">Add company data</p>
</div>
<div class="card-body">
{{ form_errors(form) }}