Compare commits
15 Commits
5b0ededa27
...
2e163d526c
Author | SHA1 | Date | |
---|---|---|---|
2e163d526c | |||
d99ef31fef | |||
e5bd7c4003 | |||
67c341c390 | |||
ef45c6cd28 | |||
642492411e | |||
1d16cadc7b | |||
e44b346788 | |||
6a5a5c2282 | |||
6c340b4229 | |||
1b5ca4bd34 | |||
7f2f6aa749 | |||
d0e48b4142 | |||
66100f0eaf | |||
e207668205 |
@ -4,32 +4,7 @@
|
|||||||
* This file will be included onto the page via the importmap() Twig function,
|
* This file will be included onto the page via the importmap() Twig function,
|
||||||
* which should already be in your base.html.twig.
|
* which should already be in your base.html.twig.
|
||||||
*/
|
*/
|
||||||
|
import './bootstrap.js';
|
||||||
|
import './vendor/bootstrap/bootstrap.index.js';
|
||||||
|
import './vendor/bootstrap/dist/css/bootstrap.min.css';
|
||||||
import './styles/app.css';
|
import './styles/app.css';
|
||||||
|
|
||||||
function filterCasesByUser(userId) {
|
|
||||||
fetch('/index.php/api/filter-cases-by-user', {
|
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json'
|
|
||||||
},
|
|
||||||
body: JSON.stringify({ userId: userId })
|
|
||||||
})
|
|
||||||
.then(response => response.json())
|
|
||||||
.then(data => {
|
|
||||||
const caseList = document.getElementById('case-list');
|
|
||||||
caseList.innerHTML = '';
|
|
||||||
data.forEach(c => {
|
|
||||||
caseList.innerHTML += `
|
|
||||||
<tr>
|
|
||||||
<td>${c.clientName}</td>
|
|
||||||
<td>${c.caseNumber}</td>
|
|
||||||
<td>${c.dcsCaseId}</td>
|
|
||||||
<td>${c.referralType}/${c.referralSource.name}<br/><a href='mailto:${c.referralSource.email}'>${c.referralSource.email}</a></td>
|
|
||||||
<td>${c.county.value}</td>
|
|
||||||
<td>${c.referrals.length}</td>
|
|
||||||
<td></td>
|
|
||||||
<td></td>
|
|
||||||
</tr>`;
|
|
||||||
})
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
5
assets/bootstrap.js
vendored
Normal file
5
assets/bootstrap.js
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import { startStimulusApp } from '@symfony/stimulus-bundle';
|
||||||
|
|
||||||
|
const app = startStimulusApp();
|
||||||
|
// register any custom, 3rd party controllers here
|
||||||
|
// app.register('some_controller_name', SomeImportedController);
|
11
assets/controllers.json
Normal file
11
assets/controllers.json
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"controllers": {
|
||||||
|
"@symfony/ux-leaflet-map": {
|
||||||
|
"map": {
|
||||||
|
"enabled": true,
|
||||||
|
"fetch": "lazy"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"entrypoints": []
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
2
assets/css/material-dashboard.min.css
vendored
2
assets/css/material-dashboard.min.css
vendored
File diff suppressed because one or more lines are too long
49
assets/js/app/filter.js
Normal file
49
assets/js/app/filter.js
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
export function filterAddressesByCase() {
|
||||||
|
if (!document.getElementById('case-filter').value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
fetch('/index.php/api/filter-address-by-case/' + document.getElementById('case-filter').value, {
|
||||||
|
method: 'POST',
|
||||||
|
header: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(result => {
|
||||||
|
const addressList = document.getElementById('addressList');
|
||||||
|
const origin = document.getElementById('origin');
|
||||||
|
const destination = document.getElementById('destination');
|
||||||
|
|
||||||
|
origin.innerHTML = '';
|
||||||
|
destination.innerHTML = '';
|
||||||
|
addressList.innerHTML = '';
|
||||||
|
|
||||||
|
origin.innerHTML += '<option value="">-- Origin --</option>';
|
||||||
|
destination.innerHTML += '<option value="0">-- Destination --</option>';
|
||||||
|
|
||||||
|
result.forEach(a => {
|
||||||
|
origin.innerHTML += `<option value='${a.id}'>${a.name}</option>`;
|
||||||
|
destination.innerHTML += `<option value='${a.id}'>${a.name}</option>`;
|
||||||
|
addressList.innerHTML += `
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<div class='d-flex px-2 py-1'>
|
||||||
|
<div class='d-flex flex-column justify-content-center'>
|
||||||
|
<h6 class='mb-0 text-small'>
|
||||||
|
${a.name}
|
||||||
|
</h6>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td>${a.formattedAddress}</td>
|
||||||
|
<td class='align-middle text-center text-xs'>${a.lat}/${a.lon}</td>
|
||||||
|
<td class='align-middle'>
|
||||||
|
<a href='/index.php/addresses/edit/${a.id}' title='Edit Address'>
|
||||||
|
<i class='material-symbols-rounded opacity-5'>edit</i>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>`;
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
56
assets/js/app/itinerary.js
Normal file
56
assets/js/app/itinerary.js
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
export function createItinerary() {
|
||||||
|
if (!document.getElementById('case-filter').value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let date = document.getElementById('date');
|
||||||
|
date.value = new Date().toLocaleDateString();
|
||||||
|
|
||||||
|
let btn = document.getElementById('create-itinerary');
|
||||||
|
btn.setAttribute('data-bs-toggle', 'modal');
|
||||||
|
btn.setAttribute('data-bs-target', '#exampleModalMessage');
|
||||||
|
btn.click();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function addLocationToItinerary() {
|
||||||
|
let date = document.getElementById('date').value;
|
||||||
|
let origin = document.getElementById('origin').value;
|
||||||
|
let destination = document.getElementById('destination').value;
|
||||||
|
let departure = document.getElementById('departure').value;
|
||||||
|
let arrival = document.getElementById('arrival').value;
|
||||||
|
let caseMileage = document.getElementById('case-mileage').checked;
|
||||||
|
let caseId = document.getElementById('case-filter').value;
|
||||||
|
|
||||||
|
fetch('/index.php/api/add-location-to-itinerary', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
date: date,
|
||||||
|
origin: origin,
|
||||||
|
destination: destination,
|
||||||
|
departure: departure,
|
||||||
|
arrival: arrival,
|
||||||
|
caseMileage: caseMileage,
|
||||||
|
caseId: caseId
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
if (data.success === true) {
|
||||||
|
$('#close-modal').click();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function openMap() {
|
||||||
|
if (!document.getElementById('case-filter').value || !document.getElementById('date-filter').value) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
document.getElementById('caseId').value = document.getElementById('case-filter').value;
|
||||||
|
document.getElementById('caseDate').value = document.getElementById('date-filter').value;
|
||||||
|
|
||||||
|
document.getElementById('map-form').submit();
|
||||||
|
}
|
7
assets/js/core/bootstrap.min.js
vendored
7
assets/js/core/bootstrap.min.js
vendored
File diff suppressed because one or more lines are too long
@ -16,12 +16,18 @@ return [
|
|||||||
'path' => './assets/app.js',
|
'path' => './assets/app.js',
|
||||||
'entrypoint' => true,
|
'entrypoint' => true,
|
||||||
],
|
],
|
||||||
'@hotwired/stimulus' => [
|
|
||||||
'version' => '3.2.2',
|
|
||||||
],
|
|
||||||
'@symfony/stimulus-bundle' => [
|
'@symfony/stimulus-bundle' => [
|
||||||
'path' => './vendor/symfony/stimulus-bundle/assets/dist/loader.js',
|
'path' => './vendor/symfony/stimulus-bundle/assets/dist/loader.js',
|
||||||
],
|
],
|
||||||
|
'@symfony/ux-leaflet-map' => [
|
||||||
|
'path' => './vendor/symfony/ux-leaflet-map/assets/dist/map_controller.js',
|
||||||
|
],
|
||||||
|
'jquery' => [
|
||||||
|
'version' => '3.7.1',
|
||||||
|
],
|
||||||
|
'@hotwired/stimulus' => [
|
||||||
|
'version' => '3.2.2',
|
||||||
|
],
|
||||||
'leaflet' => [
|
'leaflet' => [
|
||||||
'version' => '1.9.4',
|
'version' => '1.9.4',
|
||||||
],
|
],
|
||||||
@ -29,9 +35,6 @@ return [
|
|||||||
'version' => '1.9.4',
|
'version' => '1.9.4',
|
||||||
'type' => 'css',
|
'type' => 'css',
|
||||||
],
|
],
|
||||||
'@symfony/ux-leaflet-map' => [
|
|
||||||
'path' => './vendor/symfony/ux-leaflet-map/assets/dist/map_controller.js',
|
|
||||||
],
|
|
||||||
'bootstrap' => [
|
'bootstrap' => [
|
||||||
'version' => '5.3.3',
|
'version' => '5.3.3',
|
||||||
],
|
],
|
||||||
@ -53,21 +56,27 @@ return [
|
|||||||
'version' => '8.8.4',
|
'version' => '8.8.4',
|
||||||
],
|
],
|
||||||
'tslib' => [
|
'tslib' => [
|
||||||
'version' => '1.14.1',
|
'version' => '2.8.1',
|
||||||
],
|
],
|
||||||
'core-js/es/map' => [
|
'core-js/es/map' => [
|
||||||
'version' => '3.33.2',
|
'version' => '3.39.0',
|
||||||
],
|
],
|
||||||
'core-js/es/set' => [
|
'core-js/es/set' => [
|
||||||
'version' => '3.33.2',
|
'version' => '3.39.0',
|
||||||
],
|
],
|
||||||
'core-js/es/weak-map' => [
|
'core-js/es/weak-map' => [
|
||||||
'version' => '3.33.2',
|
'version' => '3.39.0',
|
||||||
],
|
],
|
||||||
'core-js/es/array/from' => [
|
'core-js/es/array/from' => [
|
||||||
'version' => '3.33.2',
|
'version' => '3.39.0',
|
||||||
],
|
],
|
||||||
'core-js/es/object/assign' => [
|
'core-js/es/object/assign' => [
|
||||||
'version' => '3.33.2',
|
'version' => '3.39.0',
|
||||||
|
],
|
||||||
|
'notify.js' => [
|
||||||
|
'version' => '0.0.3',
|
||||||
|
],
|
||||||
|
'underscore' => [
|
||||||
|
'version' => '1.5.2',
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
|
@ -2,15 +2,19 @@
|
|||||||
|
|
||||||
namespace App\Controller;
|
namespace App\Controller;
|
||||||
|
|
||||||
|
use App\Entity\CaseLocation;
|
||||||
|
use App\Entity\Location;
|
||||||
use App\Entity\MemberCase;
|
use App\Entity\MemberCase;
|
||||||
use App\Entity\Messages;
|
use App\Entity\Messages;
|
||||||
use App\Entity\ReferralSource;
|
use App\Entity\ReferralSource;
|
||||||
use App\Entity\User;
|
use App\Entity\User;
|
||||||
use App\Entity\UserCase;
|
use App\Entity\UserCase;
|
||||||
use App\Factory\MessageFactory;
|
use App\Factory\MessageFactory;
|
||||||
|
use App\Form\LocationFormType;
|
||||||
use App\Form\MemberCaseFormType;
|
use App\Form\MemberCaseFormType;
|
||||||
use App\Form\UserCaseFormType;
|
use App\Form\UserCaseFormType;
|
||||||
use App\Libs\Breadcrumb;
|
use App\Libs\Breadcrumb;
|
||||||
|
use App\Libs\Libs;
|
||||||
use App\Libs\NavList;
|
use App\Libs\NavList;
|
||||||
use Doctrine\ORM\EntityManagerInterface;
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||||
@ -39,10 +43,11 @@ class CaseController extends AbstractController
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[Route('/my-cases', name: 'app_my_cases')]
|
#[Route('/my-cases', name: 'app_my_cases')]
|
||||||
public function myCases(#[CurrentUser()] User $user): Response
|
public function myCases(#[CurrentUser()] User $user, Request $request): Response
|
||||||
{
|
{
|
||||||
$this->navLinks['my_cases'] = NavList::PRESENT_LINK;
|
$this->navLinks['my_cases'] = NavList::PRESENT_LINK;
|
||||||
$this->navLinks['case_list'] = 'nav-link text-dark';
|
$this->navLinks['case_list'] = 'nav-link text-dark';
|
||||||
|
|
||||||
$ucs = $this->entityManager->getRepository(UserCase::class)->findBy(['user' => $user]);
|
$ucs = $this->entityManager->getRepository(UserCase::class)->findBy(['user' => $user]);
|
||||||
$this->msgs = $this->entityManager->getRepository(Messages::class)->getUnreadMessages($user);
|
$this->msgs = $this->entityManager->getRepository(Messages::class)->getUnreadMessages($user);
|
||||||
$this->notificationCount = $this->entityManager->getRepository(Messages::class)->getUnreadMessageCount($user);
|
$this->notificationCount = $this->entityManager->getRepository(Messages::class)->getUnreadMessageCount($user);
|
||||||
@ -59,7 +64,11 @@ class CaseController extends AbstractController
|
|||||||
$this->navLinks,
|
$this->navLinks,
|
||||||
[
|
[
|
||||||
'breadcrumbs' => [
|
'breadcrumbs' => [
|
||||||
new Breadcrumb($this->generateUrl('app_my_cases'), 'List Cases')
|
(
|
||||||
|
strpos($request->server->get('HTTP_REFERER'), 'list-cases') !== false
|
||||||
|
? new Breadcrumb($this->generateUrl('app_list_cases'), 'List Cases')
|
||||||
|
: new Breadcrumb($this->generateUrl('app_my_cases'), 'My Cases')
|
||||||
|
),
|
||||||
],
|
],
|
||||||
'notifications' => $this->msgs,
|
'notifications' => $this->msgs,
|
||||||
'cases' => $cases,
|
'cases' => $cases,
|
||||||
@ -276,6 +285,176 @@ class CaseController extends AbstractController
|
|||||||
return new Response();
|
return new Response();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[Route('/addresses/list', name: 'app_list_case_addresses')]
|
||||||
|
public function listCaseAddresses(Request $request, #[CurrentUser()] User $user): Response
|
||||||
|
{
|
||||||
|
$this->navLinks['case_itinerary'] = NavList::PRESENT_LINK;
|
||||||
|
$this->navLinks['case_list'] = 'nav-link text-dark';
|
||||||
|
|
||||||
|
$addresses = $this->entityManager->getRepository(Location::class)->getUserLocations($user);
|
||||||
|
$this->msgs = $this->entityManager->getRepository(Messages::class)->getUnreadMessages($user);
|
||||||
|
$this->notificationCount = $this->entityManager->getRepository(Messages::class)->getUnreadMessageCount($user);
|
||||||
|
|
||||||
|
$ucs = $this->entityManager->getRepository(UserCase::class)->findBy(['user' => $user]);
|
||||||
|
$cases = [];
|
||||||
|
foreach ($ucs as $uc) {
|
||||||
|
/** @var UserCase $uc */
|
||||||
|
$cases[] = $uc->getMemberCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->render(
|
||||||
|
'internal/cases/addresses/list-case-addresses.html.twig',
|
||||||
|
array_merge(
|
||||||
|
$this->navLinks,
|
||||||
|
[
|
||||||
|
'title' => 'List Case Addresses',
|
||||||
|
'breadcrumbs' => [
|
||||||
|
new Breadcrumb($this->generateUrl('app_my_cases'), 'My Cases'),
|
||||||
|
new Breadcrumb($this->generateUrl('app_list_case_addresses'), 'List Case Addresses')
|
||||||
|
],
|
||||||
|
'notifications' => $this->msgs,
|
||||||
|
'notificationCount' => $this->notificationCount,
|
||||||
|
'addresses' => $addresses,
|
||||||
|
'cases' => $cases,
|
||||||
|
]
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[Route('/addresses/add', name: 'app_case_add_address')]
|
||||||
|
public function addCaseAddress(Request $request, #[CurrentUser()] User $user): Response
|
||||||
|
{
|
||||||
|
$this->navLinks['case_itinerary'] = NavList::PRESENT_LINK;
|
||||||
|
$this->navLinks['case_list'] = 'nav-link text-dark';
|
||||||
|
|
||||||
|
$this->msgs = $this->entityManager->getRepository(Messages::class)->getUnreadMessages($user);
|
||||||
|
$this->notificationCount = $this->entityManager->getRepository(Messages::class)->getUnreadMessageCount($user);
|
||||||
|
$ucs = $this->entityManager->getRepository(UserCase::class)->findBy(['user' => $user]);
|
||||||
|
|
||||||
|
$cases = [];
|
||||||
|
foreach ($ucs as $uc) {
|
||||||
|
/** @var UserCase $uc */
|
||||||
|
$cases[] = $uc->getMemberCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
$address = new Location();
|
||||||
|
$form = $this->createForm(LocationFormType::class, $address);
|
||||||
|
$form->handleRequest($request);
|
||||||
|
|
||||||
|
if ($form->isSubmitted() && $form->isValid()) {
|
||||||
|
/** @var Location $address */
|
||||||
|
$address = $form->getData();
|
||||||
|
|
||||||
|
foreach ($request->get('cases') as $caseId) {
|
||||||
|
$case = $this->entityManager->getRepository(MemberCase::class)->find($caseId);
|
||||||
|
|
||||||
|
$cl = new CaseLocation();
|
||||||
|
$cl->setMemberCase($case)
|
||||||
|
->setLocation($address);
|
||||||
|
|
||||||
|
$this->entityManager->persist($cl);
|
||||||
|
}
|
||||||
|
|
||||||
|
list($lat, $lon) = Libs::getLatLonFromGeoapify((string) $address);
|
||||||
|
$address->setLat($lat)
|
||||||
|
->setLon($lon);
|
||||||
|
|
||||||
|
//dd($address);
|
||||||
|
$this->entityManager->persist($address);
|
||||||
|
$this->entityManager->flush();
|
||||||
|
|
||||||
|
$this->addFlash('success', 'Address added successfully');
|
||||||
|
|
||||||
|
return $this->redirectToRoute('app_list_case_addresses');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->render(
|
||||||
|
'internal/cases/addresses/add-case-address.html.twig',
|
||||||
|
array_merge(
|
||||||
|
$this->navLinks,
|
||||||
|
[
|
||||||
|
'title' => 'Add Case Address',
|
||||||
|
'breadcrumbs' => [
|
||||||
|
new Breadcrumb($this->generateUrl('app_list_case_addresses'), 'List Addresses'),
|
||||||
|
new Breadcrumb($this->generateUrl('app_case_add_address'), 'Add Case Address')
|
||||||
|
],
|
||||||
|
'notifications' => $this->msgs,
|
||||||
|
'notificationCount' => $this->notificationCount,
|
||||||
|
'form' => $form,
|
||||||
|
'cases' => $cases,
|
||||||
|
]
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[Route('/addresses/edit/{id}', name: 'app_case_edit_address')]
|
||||||
|
public function editCaseAddress(Request $request, string $id): Response
|
||||||
|
{
|
||||||
|
$this->navLinks['case_itinerary'] = NavList::PRESENT_LINK;
|
||||||
|
$this->navLinks['case_list'] = 'nav-link text-dark';
|
||||||
|
|
||||||
|
$this->msgs = $this->entityManager->getRepository(Messages::class)->getUnreadMessages($this->getUser());
|
||||||
|
$this->notificationCount = $this->entityManager->getRepository(Messages::class)->getUnreadMessageCount($this->getUser());
|
||||||
|
|
||||||
|
$ucs = $this->entityManager->getRepository(UserCase::class)->findBy(['user' => $this->getUser()]);
|
||||||
|
$lcs = $this->entityManager->getRepository(CaseLocation::class)->findBy(['location' => $id]);
|
||||||
|
|
||||||
|
$inCases = [];
|
||||||
|
foreach ($lcs as $lc) {
|
||||||
|
$inCases[] = $lc->getMemberCase()->getId()->toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
$cases = [];
|
||||||
|
foreach ($ucs as $uc) {
|
||||||
|
/** @var UserCase $uc */
|
||||||
|
$cases[] = $uc->getMemberCase();
|
||||||
|
}
|
||||||
|
$location = $this->entityManager->getRepository(Location::class)->find($id);
|
||||||
|
$form = $this->createForm(LocationFormType::class, $location);
|
||||||
|
$form->handleRequest($request);
|
||||||
|
|
||||||
|
if ($form->isSubmitted() && $form->isValid()) {
|
||||||
|
list($lat, $lon) = Libs::getLatLonFromGeoapify((string) $location);
|
||||||
|
$location->setLat($lat)
|
||||||
|
->setLon($lon);
|
||||||
|
|
||||||
|
$this->entityManager->flush();
|
||||||
|
|
||||||
|
$this->addFlash('success', 'Address updated successfully');
|
||||||
|
|
||||||
|
return $this->redirectToRoute('app_list_case_addresses');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->render(
|
||||||
|
'internal/cases/addresses/edit-case-address.html.twig',
|
||||||
|
array_merge(
|
||||||
|
$this->navLinks,
|
||||||
|
[
|
||||||
|
'title' => 'Edit Case Address',
|
||||||
|
'breadcrumbs' => [
|
||||||
|
new Breadcrumb($this->generateUrl('app_list_case_addresses'), 'List Case Addresses'),
|
||||||
|
new Breadcrumb($this->generateUrl('app_case_edit_address', ['id' => $id]), 'Edit Case Address')
|
||||||
|
],
|
||||||
|
'location' => $location,
|
||||||
|
'cases' => $cases,
|
||||||
|
'inCases' => $inCases,
|
||||||
|
'form' => $form,
|
||||||
|
'notifications' => $this->msgs,
|
||||||
|
'notificationCount' => $this->notificationCount,
|
||||||
|
]
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[Route('/api/filter-address-by-case/{caseId}', name: 'ajax_filter_address_by_case')]
|
||||||
|
public function filterAddressByCase(string $caseId, Request $request): Response
|
||||||
|
{
|
||||||
|
$case = $this->entityManager->getRepository(MemberCase::class)->find($caseId);
|
||||||
|
$addresses = $this->entityManager->getRepository(Location::class)->getLocationsByCase($case);
|
||||||
|
|
||||||
|
return $this->json($addresses);
|
||||||
|
}
|
||||||
|
|
||||||
#[Route('/api/case/{caseId}/user/{userId}', name: 'ajax_case_user_level_check')]
|
#[Route('/api/case/{caseId}/user/{userId}', name: 'ajax_case_user_level_check')]
|
||||||
public function checkUserCaseLevel(string $caseId, string $userId) : Response
|
public function checkUserCaseLevel(string $caseId, string $userId) : Response
|
||||||
{
|
{
|
||||||
|
145
src/Controller/ItineraryController.php
Normal file
145
src/Controller/ItineraryController.php
Normal file
@ -0,0 +1,145 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Controller;
|
||||||
|
|
||||||
|
use App\Entity\CaseItinerary;
|
||||||
|
use App\Entity\CaseLocation;
|
||||||
|
use App\Entity\Location;
|
||||||
|
use App\Entity\MemberCase;
|
||||||
|
use App\Entity\Messages;
|
||||||
|
use App\Entity\User;
|
||||||
|
use App\Libs\Breadcrumb;
|
||||||
|
use App\Libs\Libs;
|
||||||
|
use App\Libs\NavList;
|
||||||
|
use DateTime;
|
||||||
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
|
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||||
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
|
use Symfony\Component\HttpFoundation\Session\Session;
|
||||||
|
use Symfony\Component\Routing\Attribute\Route;
|
||||||
|
use Symfony\Component\Security\Http\Attribute\CurrentUser;
|
||||||
|
use Symfony\UX\Map\InfoWindow;
|
||||||
|
use Symfony\UX\Map\Map;
|
||||||
|
use Symfony\UX\Map\Marker;
|
||||||
|
use Symfony\UX\Map\Point;
|
||||||
|
use Symfony\UX\Map\Polyline;
|
||||||
|
|
||||||
|
class ItineraryController extends AbstractController
|
||||||
|
{
|
||||||
|
private array $msgs = [];
|
||||||
|
|
||||||
|
private int $notificationCount = 0;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
private EntityManagerInterface $entityManager,
|
||||||
|
private array $navLinks = []
|
||||||
|
) {
|
||||||
|
$this->navLinks = NavList::LIST;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[Route('/itinerary/map', name: 'app_map_itinerary')]
|
||||||
|
public function mapItinerary(Request $request, #[CurrentUser()] ?User $user): Response
|
||||||
|
{
|
||||||
|
$this->msgs = $this->entityManager->getRepository(Messages::class)->getUnreadMessages($user);
|
||||||
|
$this->notificationCount = $this->entityManager->getRepository(Messages::class)->getUnreadMessageCount($user);
|
||||||
|
|
||||||
|
$itineraries = $this->entityManager->getRepository(CaseItinerary::class)->findBy([
|
||||||
|
'memberCase' => $request->getPayload()->get('caseId'),
|
||||||
|
'date' => new DateTime($request->getPayload()->get('caseDate'))
|
||||||
|
]);
|
||||||
|
|
||||||
|
$map = new Map('default');
|
||||||
|
$map->center(new Point(39.768502, -86.157918))
|
||||||
|
->zoom(9)
|
||||||
|
;
|
||||||
|
|
||||||
|
$total_distance = 0;
|
||||||
|
$total_duration = 0;
|
||||||
|
|
||||||
|
foreach ($itineraries as $itinerary) {
|
||||||
|
/** @var CaseItinerary $itinerary */
|
||||||
|
$map->addPolyline(new Polyline(
|
||||||
|
points: $itinerary->getGPSPolyLines(),
|
||||||
|
infoWindow: new InfoWindow(
|
||||||
|
content: $itinerary->getMemberCase()->getCaseName()
|
||||||
|
)
|
||||||
|
));
|
||||||
|
|
||||||
|
$total_distance += $itinerary->getDistance();
|
||||||
|
$total_duration += $itinerary->getDuration()->s;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->render(
|
||||||
|
'internal/cases/itinerary/map.html.twig',
|
||||||
|
array_merge(
|
||||||
|
$this->navLinks,
|
||||||
|
[
|
||||||
|
'breadcrumbs' => [
|
||||||
|
new Breadcrumb($this->generateUrl('app_my_cases'), 'My Cases'),
|
||||||
|
],
|
||||||
|
'notifications' => $this->msgs,
|
||||||
|
'notificationCount' => $this->notificationCount,
|
||||||
|
'map' => $map,
|
||||||
|
'total_distance' => $total_distance,
|
||||||
|
'total_duration' => $total_duration,
|
||||||
|
]
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[Route('/api/get-case-locations/{caseId}', name: 'get_case_locations')]
|
||||||
|
public function createItinerary(string $caseId): Response
|
||||||
|
{
|
||||||
|
$case = $this->entityManager->getRepository(MemberCase::class)->find($caseId);
|
||||||
|
$cls = $this->entityManager->getRepository(CaseLocation::class)->getCaseLocations($case);
|
||||||
|
|
||||||
|
$locations = [];
|
||||||
|
foreach ($cls as $cl) {
|
||||||
|
/** @var CaseLocation $cl */
|
||||||
|
$locations[] = $cl->getLocation();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->json($locations);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[Route('/api/add-location-to-itinerary', name: 'add_location_to_itinerary')]
|
||||||
|
public function addLocationToItinerary(Request $request, Session $session): Response
|
||||||
|
{
|
||||||
|
$case = $this->entityManager->getRepository(MemberCase::class)->find($request->getPayload()->get('caseId'));
|
||||||
|
$origin = $this->entityManager->getRepository(Location::class)->find($request->getPayload()->get('origin'));
|
||||||
|
$destination = $this->entityManager->getRepository(Location::class)->find($request->getPayload()->get('destination'));
|
||||||
|
$departure = $request->getPayload()->get('departure');
|
||||||
|
$arrival = $request->getPayload()->get('arrival');
|
||||||
|
$caseMileage = (bool) $request->getPayload()->get('caseMileage');
|
||||||
|
$date = new DateTime($request->getPayload()->get('date'));
|
||||||
|
|
||||||
|
$route = Libs::getRouteDistance($origin, $destination);
|
||||||
|
|
||||||
|
if (!$route) {
|
||||||
|
return $this->json(['success' => false, 'message' => 'No route found']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$ci = new CaseItinerary();
|
||||||
|
$ci->setMemberCase($case)
|
||||||
|
->setDate($date)
|
||||||
|
->setCaseMileage($caseMileage)
|
||||||
|
->setOriginLocation($origin)
|
||||||
|
->setDestLocation($destination)
|
||||||
|
->setDeparture(new \DateTimeImmutable($departure))
|
||||||
|
->setArrival(new \DateTimeImmutable($arrival))
|
||||||
|
->setDistance($route->getDistance())
|
||||||
|
->setDuration($route->getDuration())
|
||||||
|
->setGpsRoute($route->getGeometry())
|
||||||
|
;
|
||||||
|
|
||||||
|
$this->entityManager->persist($ci);
|
||||||
|
$this->entityManager->flush();
|
||||||
|
|
||||||
|
$session->getFlashBag()->add(
|
||||||
|
'success',
|
||||||
|
'Location added to itinerary'
|
||||||
|
);
|
||||||
|
return $this->json(['success' => true, 'message' => 'Location added to itinerary']);
|
||||||
|
}
|
||||||
|
}
|
194
src/Entity/CaseItinerary.php
Normal file
194
src/Entity/CaseItinerary.php
Normal file
@ -0,0 +1,194 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Entity;
|
||||||
|
|
||||||
|
use App\Repository\CaseItineraryRepository;
|
||||||
|
use DateInterval;
|
||||||
|
use Doctrine\DBAL\Types\Types;
|
||||||
|
use Doctrine\ORM\Mapping as ORM;
|
||||||
|
use Symfony\Bridge\Doctrine\Types\UuidType;
|
||||||
|
use Symfony\Component\Uid\Uuid;
|
||||||
|
use Symfony\UX\Map\Point;
|
||||||
|
|
||||||
|
#[ORM\Entity(repositoryClass: CaseItineraryRepository::class)]
|
||||||
|
class CaseItinerary
|
||||||
|
{
|
||||||
|
#[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::TIME_MUTABLE, nullable: true)]
|
||||||
|
private ?\DateTimeInterface $departure = null;
|
||||||
|
|
||||||
|
#[ORM\ManyToOne]
|
||||||
|
#[ORM\JoinColumn(nullable: false)]
|
||||||
|
private ?Location $originLocation = null;
|
||||||
|
|
||||||
|
#[ORM\Column(type: Types::TIME_MUTABLE, nullable: true)]
|
||||||
|
private ?\DateTimeInterface $arrival = null;
|
||||||
|
|
||||||
|
#[ORM\ManyToOne]
|
||||||
|
#[ORM\JoinColumn(nullable: false)]
|
||||||
|
private ?Location $destLocation = null;
|
||||||
|
|
||||||
|
#[ORM\Column]
|
||||||
|
private ?bool $caseMileage = null;
|
||||||
|
|
||||||
|
#[ORM\Column]
|
||||||
|
private ?DateInterval $duration = null;
|
||||||
|
|
||||||
|
#[ORM\ManyToOne]
|
||||||
|
#[ORM\JoinColumn(nullable: false)]
|
||||||
|
private ?MemberCase $memberCase = null;
|
||||||
|
|
||||||
|
#[ORM\Column(type: Types::DATE_MUTABLE)]
|
||||||
|
private ?\DateTimeInterface $date = null;
|
||||||
|
|
||||||
|
#[ORM\Column(nullable: true)]
|
||||||
|
private ?float $distance = null;
|
||||||
|
|
||||||
|
#[ORM\Column(nullable: true)]
|
||||||
|
private ?array $gpsRoute = null;
|
||||||
|
|
||||||
|
public function getId(): ?Uuid
|
||||||
|
{
|
||||||
|
return $this->id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDeparture(): ?\DateTimeInterface
|
||||||
|
{
|
||||||
|
return $this->departure;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setDeparture(?\DateTimeInterface $departure): static
|
||||||
|
{
|
||||||
|
$this->departure = $departure;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getOriginLocation(): ?Location
|
||||||
|
{
|
||||||
|
return $this->originLocation;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setOriginLocation(?Location $originLocation): static
|
||||||
|
{
|
||||||
|
$this->originLocation = $originLocation;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getArrival(): ?\DateTimeInterface
|
||||||
|
{
|
||||||
|
return $this->arrival;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setArrival(?\DateTimeInterface $arrival): static
|
||||||
|
{
|
||||||
|
$this->arrival = $arrival;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDestLocation(): ?Location
|
||||||
|
{
|
||||||
|
return $this->destLocation;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setDestLocation(?Location $destLocation): static
|
||||||
|
{
|
||||||
|
$this->destLocation = $destLocation;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isCaseMileage(): ?bool
|
||||||
|
{
|
||||||
|
return $this->caseMileage;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setCaseMileage(bool $caseMileage): static
|
||||||
|
{
|
||||||
|
$this->caseMileage = $caseMileage;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDuration(): ?DateInterval
|
||||||
|
{
|
||||||
|
return $this->duration;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setDuration(?DateInterval $duration): static
|
||||||
|
{
|
||||||
|
//$this->calcDuration();
|
||||||
|
$this->duration = $duration;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function calcDuration()
|
||||||
|
{
|
||||||
|
$this->duration = $this->departure - $this->arrival;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getMemberCase(): ?MemberCase
|
||||||
|
{
|
||||||
|
return $this->memberCase;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setMemberCase(?MemberCase $memberCase): static
|
||||||
|
{
|
||||||
|
$this->memberCase = $memberCase;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDate(): ?\DateTimeInterface
|
||||||
|
{
|
||||||
|
return $this->date;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setDate(\DateTimeInterface $date): static
|
||||||
|
{
|
||||||
|
$this->date = $date;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDistance(): ?float
|
||||||
|
{
|
||||||
|
return $this->distance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setDistance(?float $distance): static
|
||||||
|
{
|
||||||
|
$this->distance = $distance;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getGpsRoute(): ?array
|
||||||
|
{
|
||||||
|
return $this->gpsRoute;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setGpsRoute(?array $gpsRoute): static
|
||||||
|
{
|
||||||
|
$this->gpsRoute = $gpsRoute;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getGPSPolyLines(): array
|
||||||
|
{
|
||||||
|
$points = [];
|
||||||
|
foreach ($this->gpsRoute as $route) {
|
||||||
|
$points[] = new Point($route['lat'], $route['lon']);
|
||||||
|
}
|
||||||
|
return $points;
|
||||||
|
}
|
||||||
|
}
|
55
src/Entity/CaseLocation.php
Normal file
55
src/Entity/CaseLocation.php
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Entity;
|
||||||
|
|
||||||
|
use App\Repository\CaseLocationRepository;
|
||||||
|
use Doctrine\ORM\Mapping as ORM;
|
||||||
|
use Symfony\Bridge\Doctrine\Types\UuidType;
|
||||||
|
use Symfony\Component\Uid\Uuid;
|
||||||
|
|
||||||
|
#[ORM\Entity(repositoryClass: CaseLocationRepository::class)]
|
||||||
|
class CaseLocation
|
||||||
|
{
|
||||||
|
#[ORM\Id]
|
||||||
|
#[ORM\Column(type: UuidType::NAME, unique: true)]
|
||||||
|
#[ORM\GeneratedValue(strategy: 'CUSTOM')]
|
||||||
|
#[ORM\CustomIdGenerator(class: 'doctrine.uuid_generator')]
|
||||||
|
private ?Uuid $id = null;
|
||||||
|
|
||||||
|
#[ORM\ManyToOne]
|
||||||
|
#[ORM\JoinColumn(nullable: false)]
|
||||||
|
private ?MemberCase $memberCase = null;
|
||||||
|
|
||||||
|
#[ORM\ManyToOne]
|
||||||
|
#[ORM\JoinColumn(nullable: false)]
|
||||||
|
private ?Location $location = null;
|
||||||
|
|
||||||
|
public function getId(): ?int
|
||||||
|
{
|
||||||
|
return $this->id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getMemberCase(): ?MemberCase
|
||||||
|
{
|
||||||
|
return $this->memberCase;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setMemberCase(?MemberCase $memberCase): static
|
||||||
|
{
|
||||||
|
$this->memberCase = $memberCase;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getLocation(): ?Location
|
||||||
|
{
|
||||||
|
return $this->location;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setLocation(?Location $location): static
|
||||||
|
{
|
||||||
|
$this->location = $location;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
}
|
@ -110,7 +110,7 @@ class CommunityResource
|
|||||||
private ?float $lon = null;
|
private ?float $lon = null;
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private DateTime $today = new DateTime('now', new DateTimeZone('America/New_York'))
|
private DateTime $today = new DateTime('now', new DateTimeZone('America/Indiana/Indianapolis'))
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -309,7 +309,7 @@ class CommunityResource
|
|||||||
return 'C';
|
return 'C';
|
||||||
}
|
}
|
||||||
|
|
||||||
$closeAt = new DateTime($this->today->format('Y-m-d') . ' ' . $this->monClose->format('H:i:s'));
|
$closeAt = new DateTime($this->today->format('Y-m-d') . ' ' . $this->monClose->format('H:i:s'), new DateTimeZone('America/Indiana/Indianapolis'));
|
||||||
if ($closeAt <= new DateTime()) {
|
if ($closeAt <= new DateTime()) {
|
||||||
return 'C';
|
return 'C';
|
||||||
}
|
}
|
||||||
@ -347,7 +347,7 @@ class CommunityResource
|
|||||||
return 'C';
|
return 'C';
|
||||||
}
|
}
|
||||||
|
|
||||||
$closeAt = new DateTime($this->today->format('Y-m-d') . ' ' . $this->tueClose->format('H:i:s'));
|
$closeAt = new DateTime($this->today->format('Y-m-d') . ' ' . $this->tueClose->format('H:i:s'), new DateTimeZone('America/Indiana/Indianapolis'));
|
||||||
if ($closeAt <= new DateTime()) {
|
if ($closeAt <= new DateTime()) {
|
||||||
return 'C';
|
return 'C';
|
||||||
}
|
}
|
||||||
@ -385,8 +385,9 @@ class CommunityResource
|
|||||||
return 'C';
|
return 'C';
|
||||||
}
|
}
|
||||||
|
|
||||||
$closeAt = new DateTime($this->today->format('Y-m-d') . ' ' . $this->wedClose->format('H:i:s'));
|
$closeAt = new DateTime($this->today->format('Y-m-d') . ' ' . $this->wedClose->format('H:i:s'), new DateTimeZone('America/Indiana/Indianapolis'));
|
||||||
if ($closeAt <= new DateTime()) {
|
|
||||||
|
if ($closeAt <= new DateTime("now", new DateTimeZone('America/Indiana/Indianapolis'))) {
|
||||||
return 'C';
|
return 'C';
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -423,7 +424,7 @@ class CommunityResource
|
|||||||
return 'C';
|
return 'C';
|
||||||
}
|
}
|
||||||
|
|
||||||
$closeAt = new DateTime($this->today->format('Y-m-d') . ' ' . $this->thuClose->format('H:i:s'));
|
$closeAt = new DateTime($this->today->format('Y-m-d') . ' ' . $this->thuClose->format('H:i:s'), new DateTimeZone('America/Indiana/Indianapolis'));
|
||||||
if ($closeAt <= new DateTime()) {
|
if ($closeAt <= new DateTime()) {
|
||||||
return 'C';
|
return 'C';
|
||||||
}
|
}
|
||||||
@ -461,7 +462,7 @@ class CommunityResource
|
|||||||
return 'C';
|
return 'C';
|
||||||
}
|
}
|
||||||
|
|
||||||
$closeAt = new DateTime($this->today->format('Y-m-d') . ' ' . $this->friClose->format('H:i:s'));
|
$closeAt = new DateTime($this->today->format('Y-m-d') . ' ' . $this->friClose->format('H:i:s'), new DateTimeZone('America/Indiana/Indianapolis'));
|
||||||
if ($closeAt <= new DateTime()) {
|
if ($closeAt <= new DateTime()) {
|
||||||
return 'C';
|
return 'C';
|
||||||
}
|
}
|
||||||
@ -499,7 +500,7 @@ class CommunityResource
|
|||||||
return 'C';
|
return 'C';
|
||||||
}
|
}
|
||||||
|
|
||||||
$closeAt = new DateTime($this->today->format('Y-m-d') . ' ' . $this->satClose->format('H:i:s'));
|
$closeAt = new DateTime($this->today->format('Y-m-d') . ' ' . $this->satClose->format('H:i:s'), new DateTimeZone('America/Indiana/Indianapolis'));
|
||||||
if ($closeAt <= new DateTime()) {
|
if ($closeAt <= new DateTime()) {
|
||||||
return 'C';
|
return 'C';
|
||||||
}
|
}
|
||||||
@ -537,7 +538,7 @@ class CommunityResource
|
|||||||
return 'C';
|
return 'C';
|
||||||
}
|
}
|
||||||
|
|
||||||
$closeAt = new DateTime($this->today->format('Y-m-d') . ' ' . $this->sunClose->format('H:i:s'));
|
$closeAt = new DateTime($this->today->format('Y-m-d') . ' ' . $this->sunClose->format('H:i:s'), new DateTimeZone('America/Indiana/Indianapolis'));
|
||||||
if ($closeAt <= new DateTime()) {
|
if ($closeAt <= new DateTime()) {
|
||||||
return 'C';
|
return 'C';
|
||||||
}
|
}
|
||||||
@ -547,7 +548,7 @@ class CommunityResource
|
|||||||
|
|
||||||
public function getHours(): ?string
|
public function getHours(): ?string
|
||||||
{
|
{
|
||||||
$this->today = new DateTime('now', new DateTimeZone('America/New_York'));
|
$this->today = new DateTime('now', new DateTimeZone('America/Indiana/Indianapolis'));
|
||||||
switch ($this->today->format('w')) {
|
switch ($this->today->format('w')) {
|
||||||
case 0:
|
case 0:
|
||||||
return $this->sun();
|
return $this->sun();
|
||||||
|
140
src/Entity/Location.php
Normal file
140
src/Entity/Location.php
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Entity;
|
||||||
|
|
||||||
|
use App\Enums\State;
|
||||||
|
use App\Repository\LocationRepository;
|
||||||
|
use Doctrine\DBAL\Types\Types;
|
||||||
|
use Doctrine\ORM\Mapping as ORM;
|
||||||
|
use Symfony\Bridge\Doctrine\Types\UuidType;
|
||||||
|
use Symfony\Component\Uid\Uuid;
|
||||||
|
|
||||||
|
#[ORM\Entity(repositoryClass: LocationRepository::class)]
|
||||||
|
class Location
|
||||||
|
{
|
||||||
|
#[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(length: 45)]
|
||||||
|
private ?string $name = null;
|
||||||
|
|
||||||
|
#[ORM\Column(length: 255)]
|
||||||
|
private ?string $address = null;
|
||||||
|
|
||||||
|
#[ORM\Column(length: 100, nullable: true)]
|
||||||
|
private ?string $city = null;
|
||||||
|
|
||||||
|
#[ORM\Column(length: 10, nullable: true)]
|
||||||
|
private ?State $state = null;
|
||||||
|
|
||||||
|
#[ORM\Column(nullable: true)]
|
||||||
|
private ?int $zip = null;
|
||||||
|
|
||||||
|
#[ORM\Column(type: Types::DECIMAL, precision: 10, scale: 6, nullable: true)]
|
||||||
|
private ?string $lat = null;
|
||||||
|
|
||||||
|
#[ORM\Column(type: Types::DECIMAL, precision: 10, scale: 6, nullable: true)]
|
||||||
|
private ?string $lon = null;
|
||||||
|
|
||||||
|
public function getId(): ?Uuid
|
||||||
|
{
|
||||||
|
return $this->id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getName(): ?string
|
||||||
|
{
|
||||||
|
return $this->name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setName(string $name): static
|
||||||
|
{
|
||||||
|
$this->name = $name;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getAddress(): ?string
|
||||||
|
{
|
||||||
|
return $this->address;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setAddress(string $address): static
|
||||||
|
{
|
||||||
|
$this->address = $address;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCity(): ?string
|
||||||
|
{
|
||||||
|
return $this->city;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setCity(?string $city): static
|
||||||
|
{
|
||||||
|
$this->city = $city;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getState(): ?State
|
||||||
|
{
|
||||||
|
return $this->state;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setState(?State $state): static
|
||||||
|
{
|
||||||
|
$this->state = $state;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getZip(): ?int
|
||||||
|
{
|
||||||
|
return $this->zip;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setZip(?int $zip): static
|
||||||
|
{
|
||||||
|
$this->zip = $zip;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getFormattedAddress(): string
|
||||||
|
{
|
||||||
|
return "{$this->address}<br/>{$this->city}, {$this->state->value} {$this->zip}";
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getLat(): ?string
|
||||||
|
{
|
||||||
|
return $this->lat;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setLat(?string $lat): static
|
||||||
|
{
|
||||||
|
$this->lat = $lat;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getLon(): ?string
|
||||||
|
{
|
||||||
|
return $this->lon;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setLon(?string $lon): static
|
||||||
|
{
|
||||||
|
$this->lon = $lon;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __toString(): string
|
||||||
|
{
|
||||||
|
return "{$this->address} {$this->city}, {$this->state->value} {$this->zip}";
|
||||||
|
}
|
||||||
|
}
|
@ -23,7 +23,7 @@ class Member
|
|||||||
|
|
||||||
#[ORM\ManyToOne(inversedBy: 'members')]
|
#[ORM\ManyToOne(inversedBy: 'members')]
|
||||||
#[ORM\JoinColumn(nullable: false)]
|
#[ORM\JoinColumn(nullable: false)]
|
||||||
private ?MemberCase $caseId = null;
|
private ?MemberCase $memberCase = null;
|
||||||
|
|
||||||
#[ORM\Column(length: 45)]
|
#[ORM\Column(length: 45)]
|
||||||
private ?string $lastName = null;
|
private ?string $lastName = null;
|
||||||
@ -110,12 +110,12 @@ class Member
|
|||||||
|
|
||||||
public function getCaseId(): ?MemberCase
|
public function getCaseId(): ?MemberCase
|
||||||
{
|
{
|
||||||
return $this->caseId;
|
return $this->memberCase;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setCaseId(?MemberCase $caseId): static
|
public function setCaseId(?MemberCase $caseId): static
|
||||||
{
|
{
|
||||||
$this->caseId = $caseId;
|
$this->memberCase = $caseId;
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
@ -50,6 +50,9 @@ class CompanyFormType extends AbstractType
|
|||||||
{
|
{
|
||||||
$resolver->setDefaults([
|
$resolver->setDefaults([
|
||||||
'data_class' => CompanyDetailsDto::class,
|
'data_class' => CompanyDetailsDto::class,
|
||||||
|
'csrf_protection' => true,
|
||||||
|
'csrf_field_name' => '_token',
|
||||||
|
'csrf_token_id' => 'company',
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -54,6 +54,9 @@ class EditUserFormType extends AbstractType
|
|||||||
{
|
{
|
||||||
$resolver->setDefaults([
|
$resolver->setDefaults([
|
||||||
'data_class' => User::class,
|
'data_class' => User::class,
|
||||||
|
'csrf_protection' => true,
|
||||||
|
'csrf_field_name' => '_token',
|
||||||
|
'csrf_token_id' => 'edit_user',
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
38
src/Form/LocationFormType.php
Normal file
38
src/Form/LocationFormType.php
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Form;
|
||||||
|
|
||||||
|
use App\Entity\Location;
|
||||||
|
use App\Entity\MemberCase;
|
||||||
|
use App\Enums\State;
|
||||||
|
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 LocationFormType extends AbstractType
|
||||||
|
{
|
||||||
|
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||||
|
{
|
||||||
|
$builder
|
||||||
|
->add('name')
|
||||||
|
->add('address')
|
||||||
|
->add('city')
|
||||||
|
->add('state', EnumType::class, [
|
||||||
|
'class' => State::class
|
||||||
|
])
|
||||||
|
->add('zip')
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function configureOptions(OptionsResolver $resolver): void
|
||||||
|
{
|
||||||
|
$resolver->setDefaults([
|
||||||
|
'data_class' => Location::class,
|
||||||
|
'csrf_protection' => true,
|
||||||
|
'csrf_field_name' => '_token',
|
||||||
|
'csrf_token_id' => 'location',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
@ -67,6 +67,9 @@ class MemberCaseFormType extends AbstractType
|
|||||||
{
|
{
|
||||||
$resolver->setDefaults([
|
$resolver->setDefaults([
|
||||||
'data_class' => MemberCase::class,
|
'data_class' => MemberCase::class,
|
||||||
|
'csrf_protection' => true,
|
||||||
|
'csrf_field_name' => '_token',
|
||||||
|
'csrf_token_id' => 'member_case',
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -75,6 +75,9 @@ class MemberFormType extends AbstractType
|
|||||||
{
|
{
|
||||||
$resolver->setDefaults([
|
$resolver->setDefaults([
|
||||||
'data_class' => Member::class,
|
'data_class' => Member::class,
|
||||||
|
'csrf_protection' => true,
|
||||||
|
'csrf_field_name' => '_token',
|
||||||
|
'csrf_token_id' => 'member',
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -39,6 +39,9 @@ class ReferralFormType extends AbstractType
|
|||||||
{
|
{
|
||||||
$resolver->setDefaults([
|
$resolver->setDefaults([
|
||||||
'data_class' => Referral::class,
|
'data_class' => Referral::class,
|
||||||
|
'csrf_protection' => true,
|
||||||
|
'csrf_field_name' => '_token',
|
||||||
|
'csrf_token_id' => 'referral',
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,6 +24,9 @@ class ReferralSourceFormType extends AbstractType
|
|||||||
{
|
{
|
||||||
$resolver->setDefaults([
|
$resolver->setDefaults([
|
||||||
'data_class' => ReferralSource::class,
|
'data_class' => ReferralSource::class,
|
||||||
|
'csrf_protection' => true,
|
||||||
|
'csrf_field_name' => '_token',
|
||||||
|
'csrf_token_id' => 'referral_source',
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -98,6 +98,9 @@ class ResourceFormType extends AbstractType
|
|||||||
{
|
{
|
||||||
$resolver->setDefaults([
|
$resolver->setDefaults([
|
||||||
'data_class' => CommunityResource::class,
|
'data_class' => CommunityResource::class,
|
||||||
|
'csrf_protection' => true,
|
||||||
|
'csrf_field_name' => '_token',
|
||||||
|
'csrf_token_id' => 'community_resource',
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -32,6 +32,9 @@ class StaffNoteFormType extends AbstractType
|
|||||||
{
|
{
|
||||||
$resolver->setDefaults([
|
$resolver->setDefaults([
|
||||||
'data_class' => StaffNote::class,
|
'data_class' => StaffNote::class,
|
||||||
|
'csrf_protection' => true,
|
||||||
|
'csrf_field_name' => '_token',
|
||||||
|
'csrf_token_id' => 'staff_note',
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,6 +28,9 @@ class SupervisorFormType extends AbstractType
|
|||||||
{
|
{
|
||||||
$resolver->setDefaults([
|
$resolver->setDefaults([
|
||||||
'data_class' => Supervision::class,
|
'data_class' => Supervision::class,
|
||||||
|
'csrf_protection' => true,
|
||||||
|
'csrf_field_name' => '_token',
|
||||||
|
'csrf_token_id' => 'supervisor',
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -38,6 +38,9 @@ class SupervisorStaffNoteFormType extends AbstractType
|
|||||||
{
|
{
|
||||||
$resolver->setDefaults([
|
$resolver->setDefaults([
|
||||||
'data_class' => StaffNote::class,
|
'data_class' => StaffNote::class,
|
||||||
|
'csrf_protection' => true,
|
||||||
|
'csrf_field_name' => '_token',
|
||||||
|
'csrf_token_id' => 'supervisor_staff_note',
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -29,6 +29,9 @@ class UserCaseFormType extends AbstractType
|
|||||||
{
|
{
|
||||||
$resolver->setDefaults([
|
$resolver->setDefaults([
|
||||||
'data_class' => UserCase::class,
|
'data_class' => UserCase::class,
|
||||||
|
'csrf_protection' => true,
|
||||||
|
'csrf_field_name' => '_token',
|
||||||
|
'csrf_token_id' => 'user_case',
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,7 @@ use Symfony\Component\Form\AbstractType;
|
|||||||
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
|
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
|
||||||
use Symfony\Component\Form\Extension\Core\Type\EmailType;
|
use Symfony\Component\Form\Extension\Core\Type\EmailType;
|
||||||
use Symfony\Component\Form\Extension\Core\Type\EnumType;
|
use Symfony\Component\Form\Extension\Core\Type\EnumType;
|
||||||
|
use Symfony\Component\Form\Extension\Core\Type\FileType;
|
||||||
use Symfony\Component\Form\Extension\Core\Type\NumberType;
|
use Symfony\Component\Form\Extension\Core\Type\NumberType;
|
||||||
use Symfony\Component\Form\Extension\Core\Type\PasswordType;
|
use Symfony\Component\Form\Extension\Core\Type\PasswordType;
|
||||||
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
||||||
@ -71,6 +72,7 @@ class UserFormType extends AbstractType
|
|||||||
'class' => RateType::class,
|
'class' => RateType::class,
|
||||||
])
|
])
|
||||||
->add('rate', NumberType::class)
|
->add('rate', NumberType::class)
|
||||||
|
->add('imageName', FileType::class)
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -78,6 +80,9 @@ class UserFormType extends AbstractType
|
|||||||
{
|
{
|
||||||
$resolver->setDefaults([
|
$resolver->setDefaults([
|
||||||
'data_class' => User::class,
|
'data_class' => User::class,
|
||||||
|
'csrf_protection' => true,
|
||||||
|
'csrf_field_name' => '_token',
|
||||||
|
'csrf_token_id' => 'user',
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
namespace App\Libs;
|
namespace App\Libs;
|
||||||
|
|
||||||
|
use App\Entity\Location;
|
||||||
|
|
||||||
class Libs
|
class Libs
|
||||||
{
|
{
|
||||||
public static function getLatLonFromGeoapify($address): ?array
|
public static function getLatLonFromGeoapify($address): ?array
|
||||||
@ -24,7 +26,7 @@ class Libs
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function getRoute($lat1, $lon1, $lat2, $lon2): ?array
|
public static function getRoute($lat1, $lon1, $lat2, $lon2): ?Route
|
||||||
{
|
{
|
||||||
$params = [
|
$params = [
|
||||||
'waypoints' => "{$lat1},{$lon1}|{$lat2},{$lon2}",
|
'waypoints' => "{$lat1},{$lon1}|{$lat2},{$lon2}",
|
||||||
@ -34,7 +36,6 @@ class Libs
|
|||||||
'apiKey' => $_ENV['GEOAPIFY_API_KEY']
|
'apiKey' => $_ENV['GEOAPIFY_API_KEY']
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
||||||
$url = "https://api.geoapify.com/v1/routing?".http_build_query($params);
|
$url = "https://api.geoapify.com/v1/routing?".http_build_query($params);
|
||||||
$ch = curl_init();
|
$ch = curl_init();
|
||||||
curl_setopt($ch, CURLOPT_URL, $url);
|
curl_setopt($ch, CURLOPT_URL, $url);
|
||||||
@ -43,10 +44,18 @@ class Libs
|
|||||||
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
|
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
|
||||||
$result = curl_exec($ch);
|
$result = curl_exec($ch);
|
||||||
curl_close($ch);
|
curl_close($ch);
|
||||||
$result = json_decode($result, true);
|
$route = new Route(json_decode($result));
|
||||||
dd($result);
|
|
||||||
if (isset($result['features'][0]['geometry']['coordinates'])) {
|
if (is_a($route, Route::class)) {
|
||||||
$route = $result['features'][0]['geometry']['coordinates'];
|
return $route;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getRouteDistance(Location $origin, Location $destination): ?Route
|
||||||
|
{
|
||||||
|
$route = self::getRoute($origin->getLat(), $origin->getLon(), $destination->getLat(), $destination->getLon());
|
||||||
|
if ($route) {
|
||||||
return $route;
|
return $route;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
|
47
src/Libs/Route.php
Normal file
47
src/Libs/Route.php
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Libs;
|
||||||
|
|
||||||
|
use DateInterval;
|
||||||
|
use DateTime;
|
||||||
|
|
||||||
|
class Route
|
||||||
|
{
|
||||||
|
private float $distance;
|
||||||
|
|
||||||
|
private float $duration;
|
||||||
|
|
||||||
|
private array $geometry;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
object $route
|
||||||
|
) {
|
||||||
|
$this->distance = $route->results[0]->distance;
|
||||||
|
$this->duration = $route->results[0]->time;
|
||||||
|
$this->geometry = $route->results[0]->geometry[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDuration(): ?DateInterval
|
||||||
|
{
|
||||||
|
$startDate = new DateTime("@0");
|
||||||
|
$endDate = new DateTime("@{$this->duration}");
|
||||||
|
$dur = $startDate->diff($endDate);
|
||||||
|
|
||||||
|
return $dur;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDurationString(): string
|
||||||
|
{
|
||||||
|
return $this->getDuration()->format('%H:%I:%S');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDistance(): float
|
||||||
|
{
|
||||||
|
return $this->distance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getGeometry(): array
|
||||||
|
{
|
||||||
|
return $this->geometry;
|
||||||
|
}
|
||||||
|
}
|
43
src/Repository/CaseItineraryRepository.php
Normal file
43
src/Repository/CaseItineraryRepository.php
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Repository;
|
||||||
|
|
||||||
|
use App\Entity\CaseItinerary;
|
||||||
|
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
||||||
|
use Doctrine\Persistence\ManagerRegistry;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @extends ServiceEntityRepository<CaseItinerary>
|
||||||
|
*/
|
||||||
|
class CaseItineraryRepository extends ServiceEntityRepository
|
||||||
|
{
|
||||||
|
public function __construct(ManagerRegistry $registry)
|
||||||
|
{
|
||||||
|
parent::__construct($registry, CaseItinerary::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * @return CaseLocation[] Returns an array of CaseLocation objects
|
||||||
|
// */
|
||||||
|
// public function findByExampleField($value): array
|
||||||
|
// {
|
||||||
|
// return $this->createQueryBuilder('c')
|
||||||
|
// ->andWhere('c.exampleField = :val')
|
||||||
|
// ->setParameter('val', $value)
|
||||||
|
// ->orderBy('c.id', 'ASC')
|
||||||
|
// ->setMaxResults(10)
|
||||||
|
// ->getQuery()
|
||||||
|
// ->getResult()
|
||||||
|
// ;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// public function findOneBySomeField($value): ?CaseLocation
|
||||||
|
// {
|
||||||
|
// return $this->createQueryBuilder('c')
|
||||||
|
// ->andWhere('c.exampleField = :val')
|
||||||
|
// ->setParameter('val', $value)
|
||||||
|
// ->getQuery()
|
||||||
|
// ->getOneOrNullResult()
|
||||||
|
// ;
|
||||||
|
// }
|
||||||
|
}
|
57
src/Repository/CaseLocationRepository.php
Normal file
57
src/Repository/CaseLocationRepository.php
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Repository;
|
||||||
|
|
||||||
|
use App\Entity\CaseLocation;
|
||||||
|
use App\Entity\Location;
|
||||||
|
use App\Entity\MemberCase;
|
||||||
|
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
||||||
|
use Doctrine\Persistence\ManagerRegistry;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @extends ServiceEntityRepository<CaseLocation>
|
||||||
|
*/
|
||||||
|
class CaseLocationRepository extends ServiceEntityRepository
|
||||||
|
{
|
||||||
|
public function __construct(ManagerRegistry $registry)
|
||||||
|
{
|
||||||
|
parent::__construct($registry, CaseLocation::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCaseLocations(MemberCase $case): array
|
||||||
|
{
|
||||||
|
return $this->createQueryBuilder('cl')
|
||||||
|
->leftJoin(Location::class, 'l', 'WITH', 'l.id = cl.location')
|
||||||
|
->andWhere('cl.memberCase = :case')
|
||||||
|
->setParameter('case', $case->getId()->toBinary())
|
||||||
|
->orderBy('l.name', 'ASC')
|
||||||
|
->getQuery()
|
||||||
|
->getResult()
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * @return CaseLocation[] Returns an array of CaseLocation objects
|
||||||
|
// */
|
||||||
|
// public function findByExampleField($value): array
|
||||||
|
// {
|
||||||
|
// return $this->createQueryBuilder('c')
|
||||||
|
// ->andWhere('c.exampleField = :val')
|
||||||
|
// ->setParameter('val', $value)
|
||||||
|
// ->orderBy('c.id', 'ASC')
|
||||||
|
// ->setMaxResults(10)
|
||||||
|
// ->getQuery()
|
||||||
|
// ->getResult()
|
||||||
|
// ;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// public function findOneBySomeField($value): ?CaseLocation
|
||||||
|
// {
|
||||||
|
// return $this->createQueryBuilder('c')
|
||||||
|
// ->andWhere('c.exampleField = :val')
|
||||||
|
// ->setParameter('val', $value)
|
||||||
|
// ->getQuery()
|
||||||
|
// ->getOneOrNullResult()
|
||||||
|
// ;
|
||||||
|
// }
|
||||||
|
}
|
74
src/Repository/LocationRepository.php
Normal file
74
src/Repository/LocationRepository.php
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Repository;
|
||||||
|
|
||||||
|
use App\Entity\CaseLocation;
|
||||||
|
use App\Entity\Location;
|
||||||
|
use App\Entity\MemberCase;
|
||||||
|
use App\Entity\User;
|
||||||
|
use App\Entity\UserCase;
|
||||||
|
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
||||||
|
use Doctrine\Persistence\ManagerRegistry;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @extends ServiceEntityRepository<Locations>
|
||||||
|
*/
|
||||||
|
class LocationRepository extends ServiceEntityRepository
|
||||||
|
{
|
||||||
|
public function __construct(ManagerRegistry $registry)
|
||||||
|
{
|
||||||
|
parent::__construct($registry, Location::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getUserLocations(User $user): array
|
||||||
|
{
|
||||||
|
$query = $this->createQueryBuilder('location')
|
||||||
|
->leftJoin(CaseLocation::class, 'cl', 'WITH', 'cl.location = location.id')
|
||||||
|
->leftJoin(UserCase::class, 'uc', 'WITH', 'uc.memberCase = cl.memberCase')
|
||||||
|
->andWhere('uc.user = :user')
|
||||||
|
->setParameter('user', $user->getId()->toBinary())
|
||||||
|
->orderBy('location.name', 'ASC')
|
||||||
|
->getQuery()
|
||||||
|
->getResult()
|
||||||
|
;
|
||||||
|
|
||||||
|
return $query;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getLocationsByCase(MemberCase $case): array
|
||||||
|
{
|
||||||
|
return $this->createQueryBuilder('location')
|
||||||
|
->leftJoin(CaseLocation::class, 'cl', 'WITH', 'cl.location = location.id')
|
||||||
|
->andWhere('cl.memberCase = :case')
|
||||||
|
->setParameter('case', $case->getId()->toBinary())
|
||||||
|
->orderBy('location.name', 'ASC')
|
||||||
|
->getQuery()
|
||||||
|
->getResult()
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * @return Locations[] Returns an array of Locations objects
|
||||||
|
// */
|
||||||
|
// public function findByExampleField($value): array
|
||||||
|
// {
|
||||||
|
// return $this->createQueryBuilder('l')
|
||||||
|
// ->andWhere('l.exampleField = :val')
|
||||||
|
// ->setParameter('val', $value)
|
||||||
|
// ->orderBy('l.id', 'ASC')
|
||||||
|
// ->setMaxResults(10)
|
||||||
|
// ->getQuery()
|
||||||
|
// ->getResult()
|
||||||
|
// ;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// public function findOneBySomeField($value): ?Locations
|
||||||
|
// {
|
||||||
|
// return $this->createQueryBuilder('l')
|
||||||
|
// ->andWhere('l.exampleField = :val')
|
||||||
|
// ->setParameter('val', $value)
|
||||||
|
// ->getQuery()
|
||||||
|
// ->getOneOrNullResult()
|
||||||
|
// ;
|
||||||
|
// }
|
||||||
|
}
|
@ -11,8 +11,8 @@
|
|||||||
</title>
|
</title>
|
||||||
|
|
||||||
<!-- Simple CSS
|
<!-- Simple CSS
|
||||||
<link
|
<link
|
||||||
rel="stylesheet" href="https://cdn.simplecss.org/simple.min.css">-->
|
rel="stylesheet" href="https://cdn.simplecss.org/simple.min.css">-->
|
||||||
|
|
||||||
<!-- Fonts and icons -->
|
<!-- Fonts and icons -->
|
||||||
<link
|
<link
|
||||||
@ -32,7 +32,6 @@
|
|||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body class="bg-gray-200"> {% block body %}{% endblock %}
|
<body class="bg-gray-200"> {% block body %}{% endblock %}
|
||||||
|
|
||||||
{% block javascripts %}
|
{% block javascripts %}
|
||||||
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js" integrity="sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo=" crossorigin=""></script>
|
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js" integrity="sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo=" crossorigin=""></script>
|
||||||
<script>
|
<script>
|
||||||
@ -55,5 +54,7 @@ Scrollbar.init(document.querySelector('#sidenav-scrollbar'), options);
|
|||||||
{{ importmap('app') }}
|
{{ importmap('app') }}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block page_js %}{% endblock %}
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
@ -0,0 +1,76 @@
|
|||||||
|
{% 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 Address</h4>
|
||||||
|
<p class="mb-0"></p>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="container">
|
||||||
|
{{ form_errors(form) }}
|
||||||
|
|
||||||
|
{{ form_start(form) }}
|
||||||
|
<div class="row">
|
||||||
|
<div class='col'>
|
||||||
|
<div class='input-group input-group-outline mb-3'>
|
||||||
|
<label for='address_form_name' class='form-label'>Name</label>
|
||||||
|
<input type='text' name='{{ field_name(form.name) }}' id='address_form_nameame' class='form-control' required='required'/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class='input-group input-group-outline mb-3'>
|
||||||
|
{% for c in cases %}
|
||||||
|
<div>
|
||||||
|
<input type='checkbox' name='cases[]' id='{{ c.id }}' value='{{ c.id }}'/>
|
||||||
|
<label for='{{ c.id }}' style='margin:5px'>{{ c.caseName }}</label>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class='col'>
|
||||||
|
<div class='input-group input-group-outline mb-3'>
|
||||||
|
<label for='address_form_address' class='form-label'>Address</label>
|
||||||
|
<input type='text' name='{{ field_name(form.address) }}' id='address_form_address' class='form-control'/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class='input-group input-group-outline mb-3'>
|
||||||
|
<label for='address_form_city' class='form-label'>City</label>
|
||||||
|
<input type='text' name='{{ field_name(form.city) }}' id='address_form_city' class='form-control'/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class='input-group input-group-outline mb-3'>
|
||||||
|
<label for='address_form_state'></label>
|
||||||
|
<select name='{{ field_name(form.state) }}' id='address_form_state' class='form-control'>
|
||||||
|
<option value=''>-- Select State --</option>
|
||||||
|
|
||||||
|
{% for s in enum('App\\Enums\\State').cases() %}
|
||||||
|
<option value='{{ s.value }}'>{{ s.name }}</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class='input-group input-group-outline mb-3'>
|
||||||
|
<label for='address_form_zip' class='form-label'>Zip</label>
|
||||||
|
<input type='text' name='{{ field_name(form.zip) }}' id='address_form_zip' class='form-control'/>
|
||||||
|
</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 Address</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{ form_end(form) }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</main>
|
||||||
|
{% endblock %}
|
@ -0,0 +1,76 @@
|
|||||||
|
{% 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">Edit Address</h4>
|
||||||
|
<p class="mb-0">{{ location.name }}</p>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="container">
|
||||||
|
{{ form_start(form) }}
|
||||||
|
|
||||||
|
{{ form_errors(form) }}
|
||||||
|
<div class="row">
|
||||||
|
<div class='col'>
|
||||||
|
<div class='input-group input-group-outline mb-3 is-filled'>
|
||||||
|
<label for='address_form_name' class='form-label'>Name</label>
|
||||||
|
<input type='text' name='{{ field_name(form.name) }}' id='address_form_nameame' value='{{ field_value(form.name) }}' class='form-control' required='required'/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class='input-group input-group-outline mb-3'>
|
||||||
|
{% for c in cases %}
|
||||||
|
<div>
|
||||||
|
<input type='checkbox' name='cases[]' id='{{ c.id }}' value='{{ c.id }}' {% for id in inCases %} {% if c.id == id %} checked='checked' {% endif %} {% endfor %}/>
|
||||||
|
<label for='{{ c.id }}' style='margin:5px'>{{ c.caseName }}</label>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class='col'>
|
||||||
|
<div class='input-group input-group-outline mb-3 is-filled'>
|
||||||
|
<label for='address_form_address' class='form-label'>Address</label>
|
||||||
|
<input type='text' name='{{ field_name(form.address) }}' id='address_form_address' value='{{ field_value(form.address) }}' class='form-control'/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class='input-group input-group-outline mb-3 is-filled'>
|
||||||
|
<label for='address_form_city' class='form-label'>City</label>
|
||||||
|
<input type='text' name='{{ field_name(form.city) }}' id='address_form_city' value='{{ field_value(form.city) }}' class='form-control'/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class='input-group input-group-outline mb-3'>
|
||||||
|
<label for='address_form_state'></label>
|
||||||
|
<select name='{{ field_name(form.state) }}' id='address_form_state' class='form-control'>
|
||||||
|
<option value=''>-- Select State --</option>
|
||||||
|
|
||||||
|
{% for s in enum('App\\Enums\\State').cases() %}
|
||||||
|
<option value='{{ s.value }}' {% if s.value == location.state.value %} selected='selected' {% endif %}>{{ s.name }}</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class='input-group input-group-outline mb-3 is-filled'>
|
||||||
|
<label for='address_form_zip' class='form-label'>Zip</label>
|
||||||
|
<input type='text' name='{{ field_name(form.zip) }}' id='address_form_zip' value='{{ field_value(form.zip) }}' class='form-control'/>
|
||||||
|
</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 Address</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{ form_end(form) }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</main>
|
||||||
|
{% endblock %}
|
149
templates/internal/cases/addresses/list-case-addresses.html.twig
Normal file
149
templates/internal/cases/addresses/list-case-addresses.html.twig
Normal file
@ -0,0 +1,149 @@
|
|||||||
|
{% 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">Case Address List</h6>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<form action='{{ path('app_map_itinerary') }}' method='post' id='map-form' style='display:inline;'>
|
||||||
|
<input type='hidden' name='caseId' id='caseId'/>
|
||||||
|
<input type='hidden' name='caseDate' id='caseDate'/>
|
||||||
|
<button type='button' class='btn bg-gradient-info btn-block mb-3' id='map-itinerary'>Map Itinerary</button>
|
||||||
|
</form>
|
||||||
|
<button type='button' class='btn bg-gradient-success btn-block mb-3' id='create-itinerary'>Create Itinerary</button>
|
||||||
|
<button type="button" class="btn btn-block btn-light mb-3" onclick="window.open('{{ path('app_case_add_address') }}', '_self')">Add Address</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card-body px-0 pb-2">
|
||||||
|
<div class=''>
|
||||||
|
Filters:
|
||||||
|
<select id='case-filter'>
|
||||||
|
<option value=''>-- Select --</option>
|
||||||
|
|
||||||
|
{% for c in cases %}
|
||||||
|
<option value='{{ c.id }}'>{{ c.caseName }}</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
<input type='date' id='date-filter'/>
|
||||||
|
</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">Name</th>
|
||||||
|
<th class="text-uppercase text-secondary text-xxs font-weight-bolder opacity-7 ps-2">Address</th>
|
||||||
|
<th class='text-center text-uppercase text-secondary text-xxs font-weight-bolder opacity-7'>Lat/Lon</th>
|
||||||
|
<th class="text-secondary opacity-7"></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody id='addressList'>
|
||||||
|
{% for l in addresses %}
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<div class='d-flex px-2 py-1'>
|
||||||
|
<div class='d-flex flex-column justify-content-center'>
|
||||||
|
<h6 class='mb-0 text-small'>
|
||||||
|
{{ l.name }}
|
||||||
|
</h6>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td title=''>
|
||||||
|
{{ l.getFormattedAddress()|raw }}
|
||||||
|
</td>
|
||||||
|
<td class='align-middle text-center text-xs'>
|
||||||
|
{{ l.lat }}/{{ l.lon }}
|
||||||
|
</td>
|
||||||
|
<td class='align-middle'>
|
||||||
|
<a href='{{ path('app_case_edit_address', {id: l.id}) }}' title='Edit Location'>
|
||||||
|
<i class="material-symbols-rounded opacity-5">edit</i>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class='modal fade' id='exampleModalMessage' tabindex='-1' role='dialog' aria-labelledby='exampleModalMessageTitle'>
|
||||||
|
<div class='modal-dialog modal-dialog-centered' role='document'>
|
||||||
|
<div class='modal-content'>
|
||||||
|
<div class="modal-header">
|
||||||
|
<h6 class="modal-title font-weight-normal" id="exampleModalLabel">Add Itinerary</h6>
|
||||||
|
<button type="button" class="btn-close text-dark" data-bs-dismiss="modal" aria-label="Close">
|
||||||
|
<span aria-hidden="true">×</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class='modal-body'>
|
||||||
|
<form>
|
||||||
|
<div class='input-group input-group-outline my-3'>
|
||||||
|
<input type='date' id='date' class='form-control'/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class='input-group input-group-outline my-3'>
|
||||||
|
<select name="origin" id='origin' class='form-control'>
|
||||||
|
<option value=''>-- Origin --</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class='input-group input-group-outline my-3'>
|
||||||
|
<input type='time' name='departure' id='departure'/>
|
||||||
|
</div>
|
||||||
|
<div class='input-group input-group-outline my-3'>
|
||||||
|
<select name='destination' id='destination' class='form-control'>
|
||||||
|
<option value=''>-- Destination --</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class='input-group input-group-outline my-3'>
|
||||||
|
<input type='time' name='arrival' id='arrival'/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class='input-group input-group-outline my-3'>
|
||||||
|
<input type='checkbox' name='case-mileage' id='case-mileage' value='1'/>
|
||||||
|
<label for='case-mileage' style='padding:5px;'>Case Mileage</label>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div class='modal-footer'>
|
||||||
|
<button type='button' id='close-modal' class='btn bg-gradient-secondary' data-bs-dismiss='modal'>Close</button>
|
||||||
|
<button type='button' id='add-location-to-itinerary' class='btn bg-gradient-primary'>Add Location</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</main>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block page_js %}
|
||||||
|
<script type='module'>
|
||||||
|
import $ from "{{ asset('vendor/jquery/jquery.index.js') }}";
|
||||||
|
import * as notify from "{{ asset('vendor/notify.js/notify.js.index.js') }}";
|
||||||
|
|
||||||
|
import {filterAddressesByCase} from '{{ asset("js/app/filter.js") }}';
|
||||||
|
import {createItinerary, addLocationToItinerary, openMap} from '{{ asset("js/app/itinerary.js") }}';
|
||||||
|
window.$ = $;
|
||||||
|
|
||||||
|
$(function () {
|
||||||
|
document.getElementById('case-filter').addEventListener('change', filterAddressesByCase);
|
||||||
|
document.getElementById('create-itinerary').addEventListener('click', createItinerary);
|
||||||
|
document.getElementById('add-location-to-itinerary').addEventListener('click', addLocationToItinerary);
|
||||||
|
document.getElementById('map-itinerary').addEventListener('click', openMap);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
20
templates/internal/cases/itinerary/map.html.twig
Normal file
20
templates/internal/cases/itinerary/map.html.twig
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
{% 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>
|
||||||
|
Totals:
|
||||||
|
{{ total_distance }}
|
||||||
|
/
|
||||||
|
{{ total_duration }}
|
||||||
|
</div>
|
||||||
|
{{ ux_map(map, {style: 'height: 600px', 'data-controller': 'my-map'}) }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</main>
|
||||||
|
{% endblock %}
|
@ -3,7 +3,7 @@
|
|||||||
<div class="container-fluid py-1 px-3">
|
<div class="container-fluid py-1 px-3">
|
||||||
{{ block('breadcrumb', 'internal/libs/breadcrumb.html.twig') }}
|
{{ block('breadcrumb', 'internal/libs/breadcrumb.html.twig') }}
|
||||||
<div class="collapse navbar-collapse mt-sm-0 mt-2 me-md-0 me-sm-4" id="navbar">
|
<div class="collapse navbar-collapse mt-sm-0 mt-2 me-md-0 me-sm-4" id="navbar">
|
||||||
<div class='ms-md-auto pe-md-3 d-flex align-items-left'>
|
<div class='ms-md-auto pe-md-3 d-flex align-items-left' id='messages'>
|
||||||
{% for label, messages in app.flashes %}
|
{% for label, messages in app.flashes %}
|
||||||
{% for message in messages %}
|
{% for message in messages %}
|
||||||
<div class="flash-{{ label }} bg-gradient-info text-white">
|
<div class="flash-{{ label }} bg-gradient-info text-white">
|
||||||
|
Reference in New Issue
Block a user