Compare commits

...

9 Commits

Author SHA1 Message Date
fc9ebbd327 upd: importmap
* add moment library
2025-01-24 11:20:13 -05:00
4cddccdf6e upd: twig nav
* remove case notes nav in favor of navigating through case data
* rename Case Addresses to Addresses
2025-01-24 11:19:06 -05:00
3d786f1f16 upd: twig list-notes
* correct link for adding a new note
* remove display of present members
2025-01-24 11:16:25 -05:00
e5f09bd8cc upd: Libs
Permissions checking
* Add checkPermissions method to check the permissions of the user to the case to make sure they have permissions to add or edit.
2025-01-24 10:35:46 -05:00
77d90ed691 fix: NoteForms
* change members field to unmapped
2025-01-24 10:34:12 -05:00
0d69b51ff9 upd: CommunityResource
* Add method to convert class to Location object
2025-01-24 10:19:43 -05:00
df29fd0d99 fix: NoteController
Save members
* get note members working, still need to setup editing
2025-01-24 10:18:56 -05:00
09ae2756c2 upd: ItineraryController
* remove unnecessary use statements
2025-01-24 10:17:43 -05:00
520409b0b4 fix: CaseController
* add permission validation
2025-01-24 10:15:49 -05:00
10 changed files with 99 additions and 17 deletions

View File

@ -88,4 +88,7 @@ return [
'tinymce' => [ 'tinymce' => [
'version' => '7.6.0', 'version' => '7.6.0',
], ],
'moment' => [
'version' => '2.30.1',
],
]; ];

View File

@ -8,6 +8,7 @@ use App\Entity\CompanyDocument;
use App\Entity\Location; use App\Entity\Location;
use App\Entity\Member; use App\Entity\Member;
use App\Entity\MemberCase; use App\Entity\MemberCase;
use App\Entity\MemberDocument;
use App\Entity\ReferralSource; use App\Entity\ReferralSource;
use App\Entity\User; use App\Entity\User;
use App\Entity\UserCase; use App\Entity\UserCase;
@ -23,7 +24,9 @@ use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\Routing\Attribute\Route; use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
use Symfony\Component\Security\Http\Attribute\CurrentUser; use Symfony\Component\Security\Http\Attribute\CurrentUser;
class CaseController extends AbstractController class CaseController extends AbstractController
@ -39,6 +42,10 @@ class CaseController extends AbstractController
#[Route('/my-cases', name: 'app_my_cases')] #[Route('/my-cases', name: 'app_my_cases')]
public function myCases(#[CurrentUser()] User $user, Request $request): Response public function myCases(#[CurrentUser()] User $user, Request $request): Response
{ {
if (!$this->isGranted('IS_AUTHENTICATED_FULLY')) {
return $this->redirectToRoute('app_login');
}
$this->navLinks['my_cases'] = NavList::PRESENT_LINK; $this->navLinks['my_cases'] = NavList::PRESENT_LINK;
$this->navLinks['case_list'] = NavList::DEFAULT; $this->navLinks['case_list'] = NavList::DEFAULT;
@ -58,7 +65,7 @@ class CaseController extends AbstractController
'breadcrumbs' => [ 'breadcrumbs' => [
( (
strpos($request->server->get('HTTP_REFERER'), 'list-cases') !== false strpos($request->server->get('HTTP_REFERER'), 'list-cases') !== false
? new Breadcrumb($this->generateUrl('app_list_cases'), 'List Cases') ? new Breadcrumb($this->generateUrl('app_list_cases'), 'Cases')
: new Breadcrumb($this->generateUrl('app_my_cases'), 'My Cases') : new Breadcrumb($this->generateUrl('app_my_cases'), 'My Cases')
), ),
], ],
@ -103,7 +110,9 @@ class CaseController extends AbstractController
$case = $this->entityManager->getRepository(MemberCase::class)->find($caseId); $case = $this->entityManager->getRepository(MemberCase::class)->find($caseId);
/** @todo validate user has access to case, check for admin, case manager of case worker */ /** @todo validate user has access to case, check for admin, case manager of case worker */
//$uc = $this->entityManager->getRepository(UserCase::class) if (!Libs::checkPermissions($user, $case, $this->entityManager)) {
throw new AccessDeniedException();
}
$sources = $this->entityManager->getRepository(ReferralSource::class)->findAll(); $sources = $this->entityManager->getRepository(ReferralSource::class)->findAll();
@ -115,7 +124,7 @@ class CaseController extends AbstractController
'case' => $case, 'case' => $case,
'sources' => $sources, 'sources' => $sources,
'breadcrumbs' => [ 'breadcrumbs' => [
new Breadcrumb($this->generateUrl('app_list_cases'), 'List Cases'), new Breadcrumb($this->generateUrl('app_list_cases'), 'Cases'),
new Breadcrumb($this->generateUrl('app_view_case', ['caseId' => $case->getId()]), 'View Case') new Breadcrumb($this->generateUrl('app_view_case', ['caseId' => $case->getId()]), 'View Case')
], ],
'notifications' => Libs::getMessages($user, $this->entityManager), 'notifications' => Libs::getMessages($user, $this->entityManager),
@ -452,10 +461,4 @@ class CaseController extends AbstractController
) )
); );
} }
#[Route('/sign-case-doc/{caseId}/{docId}/{memberId}', name: 'app_display_case_document')]
public function displayCaseDocument(string $caseId, string $docId, Request $request, #[CurrentUser()] User $user): Response
{
return new Response();
}
} }

View File

@ -3,8 +3,6 @@
namespace App\Controller; namespace App\Controller;
use App\Entity\CaseItinerary; use App\Entity\CaseItinerary;
use App\Entity\CaseLocation;
use App\Entity\Location;
use App\Entity\MemberCase; use App\Entity\MemberCase;
use App\Entity\User; use App\Entity\User;
use App\Entity\UserCase; use App\Entity\UserCase;
@ -17,7 +15,6 @@ use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Session\Session;
use Symfony\Component\Routing\Attribute\Route; use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Security\Http\Attribute\CurrentUser; use Symfony\Component\Security\Http\Attribute\CurrentUser;
use Symfony\UX\Map\InfoWindow; use Symfony\UX\Map\InfoWindow;

View File

@ -84,7 +84,7 @@ class NoteController extends AbstractController
$referral = $this->entityManager->getRepository(Referral::class)->find($referralId); $referral = $this->entityManager->getRepository(Referral::class)->find($referralId);
$this->entityManager->getRepository(Referral::class)->populateNotes($referral); $this->entityManager->getRepository(Referral::class)->populateNotes($referral);
$members = $this->entityManager->getRepository(Member::class)->findBy(['caseId' => $referral->getMemberCase()]); $members = $this->entityManager->getRepository(Member::class)->findBy(['memberCase' => $referral->getMemberCase()]);
$defaultMethod = NoteMethod::BILLABLE; $defaultMethod = NoteMethod::BILLABLE;
$defaultLocation = NoteLocation::COMMUNITY_OUTING; $defaultLocation = NoteLocation::COMMUNITY_OUTING;
$form = $this->createForm(StandardNoteFormType::class, null, ['members' => $members]); $form = $this->createForm(StandardNoteFormType::class, null, ['members' => $members]);
@ -113,6 +113,27 @@ class NoteController extends AbstractController
$this->entityManager->persist($note); $this->entityManager->persist($note);
$this->entityManager->flush(); $this->entityManager->flush();
if ($form->get('members')->getData()) {
foreach ($form->get('members')->getData() as $mem) {
if ($referral->getServiceCode() == ReferralServiceType::VS_THBB) {
$nm = new VisitNoteMembers();
$nm->setVisitNote($note)
->setPerson($mem)
;
} else {
$nm = new StandardNoteMember();
$nm->setStandardNote($note)
->setPerson($mem)
;
}
$this->entityManager->persist($nm);
$this->entityManager->flush();
}
}
$this->addFlash('success', 'Note added successfully');
return $this->redirectToRoute('app_list_notes'); return $this->redirectToRoute('app_list_notes');
} }
@ -127,6 +148,7 @@ class NoteController extends AbstractController
'notifications' => Libs::getMessages($user, $this->entityManager), 'notifications' => Libs::getMessages($user, $this->entityManager),
'referral' => $referral, 'referral' => $referral,
'form' => $form, 'form' => $form,
'members' => $members,
'default_method' => $defaultMethod, 'default_method' => $defaultMethod,
'default_location' => $defaultLocation, 'default_location' => $defaultLocation,
] ]

View File

@ -683,4 +683,19 @@ class CommunityResource
{$this->getContactCard()} {$this->getContactCard()}
EOL; EOL;
} }
public function toLocation(): Location
{
$loc = new Location();
$loc->setName($this->name)
->setAddress($this->address)
->setCity($this->city)
->setState($this->state)
->setZip($this->zip)
->setLat($this->lat)
->setLon($this->lon)
;
return $loc;
}
} }

View File

@ -49,6 +49,7 @@ class StandardNoteFormType extends AbstractType
'multiple' => true, 'multiple' => true,
'expanded' => true, 'expanded' => true,
'choice_label' => 'name', 'choice_label' => 'name',
'mapped' => false,
]) ])
->add('method', EnumType::class, [ ->add('method', EnumType::class, [
'class' => NoteMethod::class, 'class' => NoteMethod::class,

View File

@ -64,6 +64,7 @@ class VisitNoteFormType extends AbstractType
'multiple' => true, 'multiple' => true,
'expanded' => true, 'expanded' => true,
'choice_label' => 'name', 'choice_label' => 'name',
'mapped' => false,
]) ])
->add('method', EnumType::class, [ ->add('method', EnumType::class, [
'class' => NoteMethod::class 'class' => NoteMethod::class

View File

@ -3,8 +3,11 @@
namespace App\Libs; namespace App\Libs;
use App\Entity\Location; use App\Entity\Location;
use App\Entity\MemberCase;
use App\Entity\Messages; use App\Entity\Messages;
use App\Entity\Supervision;
use App\Entity\User; use App\Entity\User;
use App\Entity\UserCase;
use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Response;
@ -108,4 +111,41 @@ class Libs extends AbstractController
return $msgs; return $msgs;
} }
/**
* Checks if the user has permission to access a case.
*
* @param User $user
* @param MemberCase $case
* @param EntityManagerInterface $em
*
* @return bool
*/
public static function checkPermissions(User $user, MemberCase $case, EntityManagerInterface $em): bool
{
// if user is an admin, allow the action
if (in_array('ROLE_ADMIN', $user->getRoles())) {
return true;
}
// if user is assigned to this case, allow the action
$uc = $em->getRepository(UserCase::class)->findOneBy(['user' => $user, 'memberCase' => $case]);
if ($uc) {
return true;
}
// get user of the case and check if user is a supervisor of the worker
/** @var ?UserCase $uc */
$uc = $em->getRepository(UserCase::class)->findOneBy(['memberCase' => $case]);
if ($uc) {
$sup = $em->getRepository(Supervision::class)->findOneBy(['supervisor' => $user, 'worker' => $uc->getUser()]);
if ($sup) {
return true;
}
}
// user does not have permissions to the case
return false;
}
} }

View File

@ -16,7 +16,7 @@
<h6 class="text-white text-capitalize ps-3">Referral Note List</h6> <h6 class="text-white text-capitalize ps-3">Referral Note List</h6>
</div> </div>
<div> <div>
<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> <button type="button" class="btn btn-block btn-light mb-3" onclick="window.open('/add-note/'+document.getElementById('referralList').value, '_self')">Add Note</button>
</div> </div>
</div> </div>
</div> </div>
@ -52,7 +52,6 @@
<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">DOS</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">Location</th>
<th class="text-center text-uppercase text-secondary text-xxs font-weight-bolder opacity-7">Method</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> <th class="text-secondary opacity-7"></th>
</tr> </tr>
</thead> </thead>
@ -64,7 +63,6 @@
{{ n.startTime|date("g:i a", company_timezone) }}-{{ n.endTime|date("g:i a", company_timezone) }} ({{ n.calcTimeUsed() }})</td> {{ n.startTime|date("g:i a", company_timezone) }}-{{ n.endTime|date("g:i a", company_timezone) }} ({{ n.calcTimeUsed() }})</td>
<td class='text-center'>{{ n.location.value }}</td> <td class='text-center'>{{ n.location.value }}</td>
<td class='text-center'>{{ n.method.name|lower|replace({"_": " "})|capitalize }}</td> <td class='text-center'>{{ n.method.name|lower|replace({"_": " "})|capitalize }}</td>
<td></td>
<td style='text-align: right;'> <td style='text-align: right;'>
<a href='/edit-note/{{ n.id }}' class='text-secondary' title='Edit Note'> <a href='/edit-note/{{ n.id }}' class='text-secondary' title='Edit Note'>
<i class="material-symbols-rounded opacity-5">edit</i> <i class="material-symbols-rounded opacity-5">edit</i>

View File

@ -89,16 +89,18 @@
<span class='nav-link-text ms-1'>My Cases</span> <span class='nav-link-text ms-1'>My Cases</span>
</a> </a>
</li> </li>
{#
<li class='nav-item'> <li class='nav-item'>
<a class='{{ case_notes }}' href='{{ path('app_list_notes') }}'> <a class='{{ case_notes }}' href='{{ path('app_list_notes') }}'>
<i class='material-symbols-rounded opacity-5'>note_add</i> <i class='material-symbols-rounded opacity-5'>note_add</i>
<span class='nav-link-text ms-1'>Case Notes</span> <span class='nav-link-text ms-1'>Case Notes</span>
</a> </a>
</li> </li>
#}
<li class='nav-item'> <li class='nav-item'>
<a class='{{ case_itinerary }}' href='{{ path('app_list_case_addresses') }}'> <a class='{{ case_itinerary }}' href='{{ path('app_list_case_addresses') }}'>
<i class='material-symbols-rounded opacity-5'>map</i> <i class='material-symbols-rounded opacity-5'>map</i>
<span class='nav-link-text ms-1'>Case Addresses</span> <span class='nav-link-text ms-1'>Addresses</span>
</a> </a>
</li> </li>
<li class='nav-item'> <li class='nav-item'>