fix: DocumentController

move case documents to member documents
first draft of signing
This commit is contained in:
Ryan Prather 2025-01-21 14:13:19 -05:00
parent caeb15f05b
commit 001a674f6f
4 changed files with 220 additions and 11 deletions

View File

@ -2,15 +2,17 @@
namespace App\Controller;
use App\Entity\CaseDocument;
use App\Entity\CompanyDocument;
use App\Entity\MemberCase;
use App\Entity\Member;
use App\Entity\MemberDocument;
use App\Entity\User;
use App\Libs\Breadcrumb;
use App\Libs\NavList;
use App\Libs\Libs;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Security\Http\Attribute\CurrentUser;
@ -23,22 +25,51 @@ class DocumentController extends AbstractController
$this->navLinks = NavList::LIST;
}
#[Route('/case/{caseId}/doc/{docId}', name: 'app_display_case_doc')]
public function displayCaseDocument(string $caseId, string $docId, #[CurrentUser()] User $user): Response
#[Route('/member/{memberId}', name: 'app_display_case_doc')]
public function displayCaseDocument(string $memberId, #[CurrentUser()] User $user): Response
{
$case = $this->entityManager->getRepository(MemberCase::class)->find($caseId);
$doc = $this->entityManager->getRepository(CompanyDocument::class)->find($docId);
$caseDoc = $this->entityManager->getRepository(CaseDocument::class)->getCaseDocument($case, $doc);
/** @var Member $member */
$member = $this->entityManager->getRepository(Member::class)->find($memberId);
$memberDocs = $this->entityManager->getRepository(MemberDocument::class)->findBy(['client' => $member]);
return $this->render(
'internal/cases/documents/display-case-doc.html.twig',
'internal/cases/members/documents/list-member-docs.html.twig',
array_merge(
$this->navLinks,
[
'msgs' => Libs::getMessages($user, $this->entityManager),
'breadcrumbs' => []
'notifications' => Libs::getMessages($user, $this->entityManager),
'breadcrumbs' => [
new Breadcrumb($this->generateUrl('app_my_cases'), 'Cases'),
new Breadcrumb($this->generateUrl('app_case_members', ['id' => $member->getCaseId()->getId()->toString()]), 'Members'),
],
'docs' => $memberDocs,
]
)
);
}
#[Route('/member/{memberId}/document/{docId}/sign', name: 'app_member_sign_case_doc')]
public function signCaseDocument(string $memberId, string $docId, #[CurrentUser()] User $user): Response
{
/** @var Member $member */
$member = $this->entityManager->getRepository(Member::class)->find($memberId);
$doc = $this->entityManager->getRepository(CompanyDocument::class)->find($docId);
$memDoc = $this->entityManager->getRepository(MemberDocument::class)->findOneBy([
'document' => $doc,
'client' => $member,
'caseWorker' => $user
]);
if (!$memDoc) {
throw new NotFoundHttpException('Document not found');
}
return $this->render(
'internal/cases/members/documents/sign-member-doc.html.twig', [
'doc' => $memDoc,
'caseWorkerSignature' => $user->getSignature(),
]
);
}
}

View File

@ -0,0 +1,59 @@
{% extends 'base.html.twig' %}
{% block title %}{% endblock %}
{% 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">Assigned Case Documents</h6>
</div>
</div>
</div>
<div class="card-body px-0 pb-2">
<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">Title</th>
<th class='text-center text-uppercase text-secondary text-xxs font-weight-bolder opacity-7'>Client Signed</th>
<th class='text-center text-uppercase text-secondary text-xxs font-weight-bolder opacity-7'>Case Worker Signed</th>
<th class="text-secondary opacity-7"></th>
</tr>
</thead>
<tbody>
{% for d in docs %}
<tr>
<td>{{ d.document.title }}</td>
<td class='text-center'>{% if d.clientSigned %}{{ d.clientSigned|date('M j, Y H:i:s', company_timezone) }}{% endif %}</td>
<td class='text-center'>{% if d.workerSigned %}{{ d.workerSigned|date('M j, Y H:i:s', company_timezone) }}{% endif %}</td>
<td>
<a href='{{ path('app_member_sign_case_doc', {memberId: d.client.id, docId: d.document.id}) }}' class='text-secondary' title='Edit Member'>
<i class="material-symbols-rounded opacity-5">edit</i>
</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
</main>
{% endblock %}
{% block page_js %}{% endblock %}
{% block page_css %}{% endblock %}

View File

@ -0,0 +1,119 @@
{% extends 'base.html.twig' %}
{% block title %}{% endblock %}
{% block body %}
<div class="container">
<h1>Sign Member Document</h1>
<h3>{{ doc.document.title }}</h3>
<p>{{ doc.document.updated|date('M j, Y h:i:s a', company_timezone) }}</p>
<form action="" method="post" enctype="multipart/form-data">
<div class='row'>
{{ doc.document.renderHtml()|
replace({'{{ doc.client.email }}': doc.client.email})|
replace({'{{ doc.client.phone }}': doc.client.phone})|
raw
}}
</div>
<div class='row'>
<div class='col'>
<div class='input-group input-group-outline mb-3'>
{% if doc.document.inExtras(enum('App\\Enums\\DocumentExtras').CLIENT_NAME) %}
<input type='text' name='clientName' id='clientName' value='{{ doc.client.name }}'/>
{% endif %}
{% if doc.document.inExtras(enum('App\\Enums\\DocumentExtras').CLIENT_SIGNATURE) %}
<button id='clearClientSignature'>Clear Signature</button><br />
<canvas id='clientSignatureCanvas'></canvas>
{% endif %}
</div>
</div>
</div>
{% if doc.document.inExtras(enum('App\\Enums\\DocumentExtras').PROVIDER_SIGNATURE) %}
<div class='row'>
<div class='col'>
<div class='input-group input-group-outline mb-3'>
{% if doc.document.inExtras(enum('App\\Enums\\DocumentExtras').PROVIDER_NAME) %}
<input type='text' name='providerName' id='providerName' value='{{ doc.caseWorker.name }}'/>
{% endif %}
<button id='clearProviderSignature'>Clear Signature</button><br/>
<canvas id='providerSignatureCanvas'></canvas>
</div>
</div>
</div>
{% endif %}
<button type="submit" class="btn btn-primary">Complete</button>
</form>
</div>
{% endblock %}
{% block page_js %}
<script src="https://cdn.jsdelivr.net/npm/signature_pad@5.0.4/dist/signature_pad.umd.min.js"></script>
<script type='text/javascript'>
window.onload = initializeSignatureBlocks;
var clientCanvas = document.getElementById('clientSignatureCanvas');
var providerCanvas = document.getElementById('providerSignatureCanvas');
var providerSaveSignature = JSON.parse('{{ caseWorkerSignature|raw }}');
var clientPad = null;
var providerPad = null
function initializeSignatureBlocks() {
if (providerCanvas) {
createProviderSignatureBlock();
}
if (clientCanvas) {
createClientSignatureBlock();
}
}
function createProviderSignatureBlock() {
providerPad = new SignaturePad(providerCanvas);
const ratio = Math.max(window.devicePixelRatio || 1, 1);
providerCanvas.width = providerCanvas.offsetWidth * ratio;
providerCanvas.height = providerCanvas.offsetHeight * ratio;
providerCanvas.getContext("2d").scale(ratio, ratio);
if (providerSaveSignature) {providerPad.fromData(providerSaveSignature);}
document.getElementById('clearProviderSignature').addEventListener('click', clearProviderSignature);
}
function createClientSignatureBlock() {
clientPad = new SignaturePad(clientCanvas);
const ratio = Math.max(window.devicePixelRatio || 1, 1);
clientCanvas.width = clientCanvas.offsetWidth * ratio;
clientCanvas.height = clientCanvas.offsetHeight * ratio;
clientCanvas.getContext("2d").scale(ratio, ratio);
document.getElementById('clearClientSignature').addEventListener('click', clearClientSignature);
}
function clearClientSignature(e) {
e.preventDefault();
clientPad.clear();
}
function clearProviderSignature(e) {
e.preventDefault();
providerPad.clear();
}
function saveSignatureBlock() {
document.getElementById('signature').value = JSON.stringify(pad.toData());
return true;
}
</script>
{% endblock %}
{% block page_css %}
<style rel='stylesheet'>
#clientSignatureCanvas, #providerSignatureCanvas {
width: 100%;
height: 300px;
background-color: white;
border: solid 1px black;
}
</style>
{% endblock %}

View File

@ -76,7 +76,7 @@
<a href='{{ path('app_case_edit_member', {caseId: case.id, memberId: member.id}) }}' class='text-secondary' title='Edit Member'>
<i class="material-symbols-rounded opacity-5">edit</i>
</a>
<a href='{{ path('app_assign_case_documents', {caseId: case.id, memberId: member.id}) }}' class='text-secondary' title='Assign Case Document'>
<a href='{{ path('app_display_case_doc', {memberId: member.id}) }}' class='text-secondary' title='Sign Case Document'>
<i class='material-symbols-rounded opacity-5'>content_copy</i>
</a>
</td>