You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
569 lines
24 KiB
569 lines
24 KiB
<?php |
|
|
|
/** |
|
* @package OpenEMR |
|
* @link https://www.open-emr.org |
|
* @author Jerry Padgett <sjpadgett@gmail.com> |
|
* @copyright Copyright (c) 2022 Jerry Padgett <sjpadgett@gmail.com> |
|
* @license https://github.com/openemr/openemr/blob/master/LICENSE GNU General Public License 3 |
|
*/ |
|
|
|
require_once('./../../interface/globals.php'); |
|
require_once("$srcdir/patient.inc"); |
|
|
|
use OpenEMR\Core\Header; |
|
use OpenEMR\Services\DocumentTemplates\DocumentTemplateService; |
|
|
|
$templateService = new DocumentTemplateService(); |
|
$group_list = $templateService->fetchDefaultGroups(); |
|
$profile_list = $templateService->fetchDefaultProfiles(); |
|
$_POST['mode'] = $_POST['mode'] ?? null; |
|
|
|
if ($_POST['mode'] === 'save_profile_groups') { |
|
$groups = json_decode(($_POST['patient_groups'] ?? ''), true, 512, JSON_THROW_ON_ERROR); |
|
$work_ids = []; |
|
foreach ($groups as $group) { |
|
if (empty($group)) { |
|
continue; |
|
} |
|
foreach ($group as $item) { |
|
$work_ids[$item['profile']][] = $item; |
|
} |
|
} |
|
|
|
$rtn = $templateService->savePatientGroupsByProfile($work_ids); |
|
if ($rtn) { |
|
echo xlt('Groups successfully saved.'); |
|
} else { |
|
echo xlt('Error! Groups save failed. Check your Group lists.'); |
|
} |
|
exit; |
|
} |
|
|
|
if ($_POST['mode'] === 'save_groups') { |
|
$groups = json_decode(($_POST['groups'] ?? ''), true, 512, JSON_THROW_ON_ERROR); |
|
$master_ids = $work_ids = []; |
|
foreach ($groups as $group) { |
|
if (empty($group)) { |
|
continue; |
|
} |
|
foreach ($group as $item) { |
|
$work_ids[$item['id']][$item['group']][] = $item['group']; |
|
} |
|
} |
|
foreach ($work_ids as $id => $items) { |
|
$work_grps = []; |
|
foreach ($items as $grp => $item) { |
|
$work_grps[] = $grp; |
|
} |
|
$master_ids[$id] = implode('|', array_unique($work_grps)); |
|
} |
|
|
|
$rtn = $templateService->updateGroupsInPatients($master_ids); |
|
if ($rtn) { |
|
echo xlt('Groups successfully saved.'); |
|
} else { |
|
echo xlt('Error! Groups save failed. Check your Group lists.'); |
|
} |
|
exit; |
|
} |
|
|
|
if (!isset($_GET['render_group_assignments'])) { |
|
$info_msg = ''; |
|
$result = ''; |
|
if (!empty($_REQUEST['searchby']) && !empty($_REQUEST['searchparm'])) { |
|
$searchby = $_REQUEST['searchby']; |
|
$searchparm = trim($_REQUEST['searchparm'] ?? ''); |
|
|
|
if ($searchby == 'Last') { |
|
$result = getPatientLnames("$searchparm", 'pid, pubpid, lname, fname, mname, providerID, DOB'); |
|
} elseif ($searchby == 'Phone') { |
|
$result = getPatientPhone("$searchparm"); |
|
} elseif ($searchby == 'ID') { |
|
$result = getPatientId("$searchparm"); |
|
} elseif ($searchby == 'DOB') { |
|
$result = getPatientDOB(DateToYYYYMMDD($searchparm)); |
|
} elseif ($searchby == 'SSN') { |
|
$result = getPatientSSN("$searchparm"); |
|
} elseif ($searchby == 'Issues') { |
|
$result = $templateService->fetchPatientListByIssuesSearch("$searchparm"); |
|
} |
|
} else { |
|
$result = getPatientLnames("", 'pid, pubpid, lname, fname, mname, providerID, DOB'); |
|
} |
|
?> |
|
<!DOCTYPE html> |
|
<html> |
|
<head> |
|
<?php |
|
if (empty($GLOBALS['openemr_version'] ?? null)) { |
|
Header::setupHeader(['opener','datetime-picker', 'sortablejs']); |
|
} else { |
|
Header::setupHeader(['opener','datetime-picker']); ?> |
|
<script src="<?php echo $GLOBALS['web_root']; ?>/portal/public/assets/sortablejs/Sortable.min.js?v=<?php echo $GLOBALS['v_js_includes']; ?>"></script> |
|
<?php } ?> |
|
</head> |
|
<style> |
|
body { |
|
overflow:hidden; |
|
} |
|
.list-group-item { |
|
cursor: move; |
|
} |
|
strong { |
|
font-weight: 600; |
|
} |
|
.col-height { |
|
max-height: 95vh; |
|
overflow-y:auto; |
|
} |
|
</style> |
|
<script> |
|
const groups = <?php echo js_escape($group_list); ?>; |
|
const profiles = <?php echo js_escape($profile_list); ?>; |
|
const demoPatient = <?php echo js_escape($_REQUEST['from_demo_pid'] ?? '0'); ?>; |
|
document.addEventListener('DOMContentLoaded', function () { |
|
// init drag and drop |
|
let patientRepository = document.getElementById('searchResults'); |
|
Sortable.create(patientRepository, { |
|
group: { |
|
name: 'patientGroup', |
|
pull: 'clone', |
|
}, |
|
multiDrag: true, |
|
selectedClass: 'active', |
|
fallbackTolerance: 3, |
|
sort: false, |
|
swapThreshold: 0.25, |
|
animation: 150, |
|
revertClone: true, |
|
removeCloneOnHide: true, |
|
onAdd: function (evt) { |
|
if (evt.items.length > 0) { |
|
for (let i = 0; i < evt.items.length; i++) { |
|
let el = evt.items[i]; |
|
el.parentNode.removeChild(el); |
|
} |
|
} else { |
|
let el = evt.item; |
|
el.parentNode.removeChild(el); |
|
} |
|
} |
|
}); |
|
Object.keys(groups).forEach(key => { |
|
let groupEl = groups[key]['option_id'] |
|
let dropGroup = document.getElementById(groupEl); |
|
Sortable.create(dropGroup, { |
|
group: { |
|
name: 'patientGroup', |
|
delay: 1000, |
|
}, |
|
multiDrag: true, |
|
selectedClass: 'active', |
|
fallbackTolerance: 3, |
|
animation: 150, |
|
sort: true, |
|
swapThreshold: 0.25, |
|
removeCloneOnHide: false, |
|
onAdd: function (evt) { // make group unique |
|
let toList = evt.to.children; |
|
let dedup = {}; |
|
let list = [...toList]; |
|
list.forEach(function (toEl) { |
|
if (dedup[toEl.getAttribute('data-pid')]) { |
|
toEl.remove(); |
|
} else { |
|
dedup[toEl.getAttribute('data-pid')] = true; |
|
} |
|
}); |
|
} |
|
}); |
|
}); |
|
|
|
$('#searchparm').focus(); |
|
$('#theform').submit(function () { |
|
SubmitForm(this); |
|
}); |
|
|
|
$('select[name="searchby"]').on('change', function () { |
|
if ($(this).val() === 'DOB') { |
|
$('#searchparm').datetimepicker({ |
|
<?php $datetimepicker_timepicker = false; ?> |
|
<?php $datetimepicker_showseconds = false; ?> |
|
<?php $datetimepicker_formatInput = true; ?> |
|
<?php require($GLOBALS['srcdir'] . '/js/xl/jquery-datetimepicker-2-5-4.js.php'); ?> |
|
}); |
|
} else { |
|
$('#searchparm').datetimepicker("destroy"); |
|
} |
|
}); |
|
}); |
|
|
|
function submitGroups() { |
|
top.restoreSession(); |
|
document.getElementById('search_spinner').classList.toggle('d-none'); |
|
let target = document.getElementById('edit-groups'); |
|
let groupTarget = target.querySelectorAll('ul'); |
|
let groupArray = []; |
|
let listArray = []; |
|
let listData = {}; |
|
groupTarget.forEach((ulItem, index) => { |
|
let lists = ulItem.querySelectorAll('li'); |
|
lists.forEach((item, index) => { |
|
console.log({index, item}) |
|
listData = { |
|
'group': ulItem.dataset.group, |
|
'groups': item.dataset.groups, |
|
'id': item.dataset.pid |
|
} |
|
listArray.push(listData); |
|
listData = {}; |
|
}); |
|
groupArray.push(listArray); |
|
listArray = []; |
|
}); |
|
const data = new FormData(); |
|
data.append('groups', JSON.stringify(groupArray)); |
|
data.append('mode', 'save_groups'); |
|
fetch('./patient_groups.php', { |
|
method: 'POST', |
|
body: data, |
|
}).then(rtn => rtn.text()).then((rtn) => { |
|
document.getElementById('search_spinner').classList.toggle('d-none'); |
|
(async (time) => { |
|
await asyncAlertMsg(rtn, time, 'success', 'lg'); |
|
})(1500).then(rtn => { |
|
if (typeof opener.document.edit_form !== 'undefined') { |
|
opener.document.edit_form.submit(); |
|
} |
|
dlgclose(); |
|
}); |
|
}).catch((error) => { |
|
console.error('Error:', error); |
|
}); |
|
} |
|
|
|
const SubmitForm = function (eObj) { |
|
$("#submit_button").css("disabled", "true"); |
|
return true; |
|
} |
|
</script> |
|
<body> |
|
<div class='container-fluid'> |
|
<div class='row'> |
|
<div class='col-6 col-height p-0 pb-1'> |
|
<nav id='searchCriteria' class='navbar navbar-light bg-light sticky-top'> |
|
<form class="form-inline" method='post' name='theform' id='theform' action=''> |
|
<div class='form-row'> |
|
<select name='searchby' id='searchby' class="form-control form-control-sm ml-1"> |
|
<option value="Last"><?php echo xlt('Name'); ?></option> |
|
<option value='Issues'<?php if (!empty($searchby) && ($searchby === 'Issues')) { |
|
echo ' selected'; } ?>><?php echo xlt('Problems or Code'); ?></option> |
|
</select> |
|
<div class='input-group'> |
|
<input type='text' class="form-control form-control-sm" id='searchparm' name='searchparm' value='<?php echo attr($_REQUEST['searchparm'] ?? ''); ?>' title='<?php echo xla('If name, any part of lastname or lastname,firstname') ?>' placeholder='<?php echo xla('Search criteria.') ?>' /> |
|
<button class='btn btn-primary btn-sm btn-search' type='submit' id="submit_button" value='<?php echo xla('Search'); ?>'></button> |
|
</div> |
|
</div> |
|
</form> |
|
<div class='btn-group'> |
|
<button type='button' class='btn btn-secondary btn-cancel btn-sm' onclick='dlgclose();'><?php echo xlt('Quit'); ?></button> |
|
<span id='search_spinner' class="d-none"><i class='fa fa-spinner fa-spin fa-2x ml-1'></i></span> |
|
</div> |
|
</nav> |
|
<div class=""> |
|
<?php if (!is_countable($result)) : ?> |
|
<div id="searchstatus" class="alert alert-danger m-1 p-1 rounded-0"><?php echo xlt('No records found. Please expand your search criteria.'); ?><br /> |
|
</div> |
|
<?php elseif (count($result) >= 1000) : ?> |
|
<div id="searchstatus" class="alert alert-danger m-1 p-1 rounded-0"><?php echo xlt('More than 1000 records found. Please narrow your search criteria.'); ?></div> |
|
<?php else : ?> |
|
<div id="searchstatus" class="alert alert-success m-1 p-1 rounded-0"><?php echo text(count($result)) . ' '; ?><?php echo xlt('records found.'); ?></div> |
|
<?php endif; ?> |
|
<ul id='searchResults' class='list-group mx-1'> |
|
<?php |
|
if (isset($result) && is_countable($result)) { |
|
foreach ($result as $pt) { |
|
$name = $pt['lname'] . ', ' . $pt['fname'] . ' ' . $pt['mname']; |
|
$this_name = attr($name); |
|
$pt_pid = attr($pt['pid']); |
|
$groups_esc = attr($pt['patient_groups'] ?? ''); |
|
echo "<li class='list-group-item px-1 py-1 mb-1' data-pid='$pt_pid' data-groups='$groups_esc'>" . |
|
'<strong>' . text($name) . '</strong>' . ' ' . xlt('Dob') . ': ' . |
|
'<strong>' . text(oeFormatShortDate($pt['DOB'])) . '</strong>' . ' ' . xlt('ID') . ': ' . |
|
'<strong>' . text($pt['pubpid']) . '</strong>'; |
|
if (!empty($searchby) && ($searchby === 'Issues')) { |
|
echo ' ' . xlt('Result') . ': ' . text($pt['title']) . ' ' . text($pt['diagnosis']); |
|
} |
|
echo '</li>' . "\n"; |
|
} |
|
} |
|
?> |
|
</ul> |
|
</div> |
|
</div> |
|
<div class='col-6 col-height p-0 pb-1'> |
|
<nav id='dispose' class='navbar navbar-light bg-light sticky-top'> |
|
<div class='btn-group ml-auto'> |
|
<button type='button' class='btn btn-primary btn-save btn-sm' onclick='return submitGroups();'><?php echo xlt('Save'); ?></button> |
|
<button type='button' class='btn btn-secondary btn-cancel btn-sm' onclick='dlgclose();'><?php /*echo xlt('Quit'); */?></button> |
|
<span id='search_spinner' class="d-none"><i class='fa fa-spinner fa-spin fa-2x ml-1'></i></span> |
|
</div> |
|
</nav> |
|
<div id="edit-groups" class='control-group mx-1 border-left border-right'> |
|
<?php |
|
$result = $templateService->getPatientsByAllGroups(); |
|
foreach ($group_list as $group => $groups) { |
|
$group_esc = attr($groups['option_id']); |
|
$groups_esc = attr($groups['option_id']); |
|
echo "<h5 class='bg-dark text-light text-center' data-toggle='collapse' data-target='#$group_esc' role='button'><i class='fa fa-eye mr-1'></i>" . text($groups['title']) . "</h5>\n"; |
|
echo "<ul id='$group_esc' class='list-group mx-1 px-1 show' data-group='$group_esc'>\n"; |
|
if (!empty($result[$groups['option_id']] ?? '')) { |
|
foreach ($result[$groups['option_id']] as $pt) { |
|
$name = $pt['lname'] . ', ' . $pt['fname'] . ' ' . $pt['mname']; |
|
$this_name = attr($name); |
|
$pt_pid = attr($pt['pid']); |
|
$groups_esc = attr($pt['patient_groups']); |
|
echo "<li class='list-group-item px-1 py-1 mb-1' data-pid='$pt_pid' data-groups='$groups_esc'>" . |
|
'<strong>' . text($name) . '</strong>' . ' ' . xlt('Dob') . ': ' . |
|
'<strong>' . text(oeFormatShortDate($pt['DOB'])) . '</strong>' . ' ' . xlt('ID') . ': ' . |
|
'<strong>' . text($pt['pubpid']) . '</strong>' . '</li>' . "\n"; |
|
} |
|
} |
|
echo "</ul>\n"; |
|
} |
|
// so list is responsive. |
|
echo "<div class='py-1'></div>\n"; |
|
?> |
|
</div> |
|
</div> |
|
</div> |
|
</div> |
|
<hr /> |
|
</body> |
|
</html> |
|
<?php } elseif ($_GET['render_group_assignments'] === 'true') { ?> |
|
<!-- *** Groups *** --> |
|
<!DOCTYPE html> |
|
<html> |
|
<head> |
|
<?php |
|
if (empty($GLOBALS['openemr_version'] ?? null)) { |
|
Header::setupHeader(['opener','datetime-picker', 'sortablejs']); |
|
} else { |
|
Header::setupHeader(['opener','datetime-picker']); ?> |
|
<script src="<?php echo $GLOBALS['web_root']; ?>/portal/public/assets/sortablejs/Sortable.min.js?v=<?php echo $GLOBALS['v_js_includes']; ?>"></script> |
|
<?php } ?> |
|
</head> |
|
<style> |
|
body { |
|
overflow:hidden; |
|
} |
|
.move-handle { |
|
cursor: move; |
|
} |
|
strong { |
|
font-weight: 600; |
|
} |
|
.col-height { |
|
max-height: 95vh; |
|
overflow-y:auto; |
|
} |
|
</style> |
|
<script> |
|
const groups = <?php echo js_escape($group_list); ?>; |
|
const profiles = <?php echo js_escape($profile_list); ?>; |
|
const demoPatient = <?php echo js_escape($_REQUEST['from_demo_pid'] ?? '0'); ?>; |
|
|
|
function thisPopPatientDialog() { |
|
top.restoreSession(); |
|
let url = './patient_groups.php'; |
|
dlgopen(url, 'pop-profile', 'modal-lg', 850, '', '', { |
|
allowDrag: true, |
|
allowResize: true, |
|
sizeHeight: 'full', |
|
resolvePromiseOn: 'close', |
|
}).then(() => { |
|
location.reload(); |
|
}) |
|
} |
|
|
|
function submitPatientGroups() { |
|
top.restoreSession(); |
|
let target = document.getElementById('edit-profiles'); |
|
let groupTarget = target.querySelectorAll('ul'); |
|
let groupArray = []; |
|
let listArray = []; |
|
let listData = {}; |
|
groupTarget.forEach((ulItem, index) => { |
|
let lists = ulItem.querySelectorAll('li'); |
|
lists.forEach((item, index) => { |
|
console.log({index, item}) |
|
listData = { |
|
'profile': ulItem.dataset.profile, |
|
'active': ulItem.dataset.active, |
|
'group': item.dataset.group |
|
} |
|
listArray.push(listData); |
|
listData = {}; |
|
}); |
|
groupArray.push(listArray); |
|
listArray = []; |
|
}); |
|
const data = new FormData(); |
|
data.append('patient_groups', JSON.stringify(groupArray)); |
|
data.append('mode', 'save_profile_groups'); |
|
fetch('./patient_groups.php', { |
|
method: 'POST', |
|
body: data, |
|
}).then(rtn => rtn.text()).then((rtn) => { |
|
(async (time) => { |
|
await asyncAlertMsg(rtn, time, 'success', 'lg'); |
|
})(1500).then(rtn => { |
|
if (typeof opener.document.edit_form !== undefined) { |
|
opener.document.edit_form.submit(); |
|
} |
|
dlgclose(); |
|
}); |
|
}).catch((error) => { |
|
console.error('Error:', error); |
|
}); |
|
} |
|
|
|
document.addEventListener('DOMContentLoaded', function () { |
|
// init drag and drop |
|
let patientRepository = document.getElementById('edit-groups'); |
|
Sortable.create(patientRepository, { |
|
group: { |
|
name: 'patientGroup', |
|
pull: 'clone', |
|
}, |
|
fallbackTolerance: 3, |
|
sort: false, |
|
swapThreshold: 0.25, |
|
animation: 150, |
|
revertClone: true, |
|
removeCloneOnHide: true, |
|
filter: '.nested-item', |
|
onAdd: function (evt) { |
|
if (evt.items.length > 0) { |
|
for (let i = 0; i < evt.items.length; i++) { |
|
let el = evt.items[i]; |
|
el.parentNode.removeChild(el); |
|
} |
|
} else { |
|
let el = evt.item; |
|
el.parentNode.removeChild(el); |
|
} |
|
} |
|
}); |
|
Object.keys(profiles).forEach(key => { |
|
let profileEl = profiles[key]['option_id'] |
|
let id = document.getElementById(profileEl); |
|
Sortable.create(id, { |
|
group: { |
|
name: 'patientGroup', |
|
}, |
|
selectedClass: 'active', |
|
animation: 150, |
|
sort: true, |
|
filter: '.nested-item', |
|
onAdd: function (evt) { // make group unique |
|
let toList = evt.to.children; |
|
let dedup = {}; |
|
let list = [...toList]; |
|
list.forEach(function (toEl) { |
|
if (dedup[toEl.getAttribute('data-group')]) { |
|
toEl.remove(); |
|
} else { |
|
dedup[toEl.getAttribute('data-group')] = true; |
|
} |
|
}); |
|
} |
|
}); |
|
}); |
|
}); |
|
</script> |
|
<body> |
|
<div class='container-fluid'> |
|
<div class='row'> |
|
<div class='col-6 col-height p-0 pb-1'> |
|
<nav id='dispose' class='navbar navbar-light bg-light sticky-top'> |
|
<div class='navbar-brand'><?php echo xlt('Patient Groups'); ?></div> |
|
<form class='form-inline'id='groupsForm'> |
|
<div class='btn-group'> |
|
<button type='button' class='btn btn-sm btn-primary' onclick='return thisPopPatientDialog()'><?php echo xlt('Groups') ?></button> |
|
</div> |
|
</form> |
|
</nav> |
|
<div id="edit-groups" class='control-group mx-1 border-left border-right'> |
|
<?php |
|
$result = $templateService->getPatientsByAllGroups(); |
|
foreach ($group_list as $group => $groups) { |
|
$group_esc = attr($groups['option_id']); |
|
$groups_esc = attr($groups['option_id']); |
|
echo "<li class='list-group-item move-handle text-center bg-light text-dark font-weight-bolder p-1 mt-1 mb-0' data-group='$group_esc'>" . text($groups['title']) . "<i class='fa fa-eye float-right my-1 mr-2' data-toggle='collapse' data-target='#$group_esc' role='button'></i></li>\n"; |
|
echo "<ul id='$group_esc' class='list-group-flush m-1 p-1 collapse'>\n"; |
|
if (!empty($result[$groups['option_id']] ?? '')) { |
|
foreach ($result[$groups['option_id']] as $pt) { |
|
$name = $pt['lname'] . ', ' . $pt['fname'] . ' ' . $pt['mname']; |
|
$this_name = attr($name); |
|
$pt_pid = attr($pt['pid']); |
|
$groups_esc = attr($pt['patient_groups']); |
|
echo "<li class='list-group-item nested-item m-1 p-0' data-pid='$pt_pid' data-groups='$groups_esc'>" . |
|
'<strong>' . text($name) . '</strong>' . ' ' . xlt('Dob') . ': ' . |
|
'<strong>' . text(oeFormatShortDate($pt['DOB'])) . '</strong>' . ' ' . xlt('ID') . ': ' . |
|
'<strong>' . text($pt['pubpid']) . '</strong>' . '</li>' . "\n"; |
|
} |
|
} |
|
echo "</ul>\n"; |
|
} |
|
// so list is responsive. |
|
echo "<div class='py-1'></div>\n"; |
|
?> |
|
</div> |
|
</div> |
|
<div class='col-6 col-height p-0 pb-1'> |
|
<nav id='dispose' class='navbar navbar-light bg-light sticky-top'> |
|
<div class="navbar-brand"><?php echo xlt('Profiles'); ?></div> |
|
<form class='form-inline' id='profileForm'> |
|
<div class='btn-group'> |
|
<button type="button" class='btn btn-primary btn-save btn-sm' onclick='return submitPatientGroups();'><?php echo xlt('Save'); ?></button> |
|
<button type='button' class='btn btn-secondary btn-cancel btn-sm' onclick='dlgclose();'><?php echo xlt('Quit'); ?></button> |
|
<span id='search_spinner' class="d-none"><i class='fa fa-spinner fa-spin fa-2x ml-1'></i></span> |
|
</div> |
|
</form> |
|
</nav> |
|
<div id='edit-profiles' class='control-group mx-1 border-left border-right'> |
|
<?php |
|
foreach ($profile_list as $profile => $profiles) { |
|
$profile_items_list = $templateService->getPatientGroupsByProfile($profile); |
|
$active = $templateService->getProfileActiveStatus($profile); |
|
$active_esc = attr($active); |
|
if (!empty($active)) { |
|
$active_text = '<span class="small float-left">' . xlt('Active') . '</span>'; |
|
} else { |
|
$active_text = ''; |
|
} |
|
$profile_esc = attr($profile); |
|
echo "<h5 class='text-center bg-dark text-light p-1 mt-1 mb-0'>" . $active_text . text($profiles['title']) . |
|
"<i class='fa fa-eye float-right my-1 mr-2' data-toggle='collapse' data-target='#$profile_esc' role='button'></i></h5>\n"; |
|
echo "<ul id='$profile_esc' class='list-group-flush m-1 p-1 show' data-profile='$profile_esc' data-active='$active_esc'>\n"; |
|
foreach ($profile_items_list as $grp_profile => $groups) { |
|
foreach ($groups as $group) { |
|
$group_esc = attr($group['member_of']); |
|
$title = $group_list[$group['member_of']]['title'] ?: $group['member_of']; |
|
echo "<li class='list-group-item move-handle text-center bg-light text-dark font-weight-bolder p-1 mt-1 mb-0' data-group='$group_esc'>" . text($title) . "</li>\n"; |
|
} |
|
} |
|
echo "</ul>\n"; |
|
} |
|
?> |
|
</div> |
|
</div> |
|
</div> |
|
</div> |
|
<hr /> |
|
</body> |
|
</html> |
|
<?php }
|
|
|