feat(admin): add OPD Queue TV Settings panel for right-side ads upload, left-side bottom ad space, ticker text editing, and TTS pronunciation fixes

This commit is contained in:
DevSoftTech Server 2026-08-30 02:49:59 +05:30
parent ac5d2710fa
commit 31b2319419
6 changed files with 520 additions and 27 deletions

View File

@ -1387,6 +1387,10 @@ $routes->group('setting', static function($routes) {
$routes->post('admin/nursing-station/save', 'Setting\\NursingStation::save');
$routes->post('admin/nursing-station/delete', 'Setting\\NursingStation::delete');
$routes->get('admin/nursing-station/list_json', 'Setting\\NursingStation::list_json');
$routes->get('admin/opd-queue-tv', 'Setting\\OpdQueueSetting::index');
$routes->post('admin/opd-queue-tv/save', 'Setting\\OpdQueueSetting::save');
$routes->post('admin/opd-queue-tv/upload-banner', 'Setting\\OpdQueueSetting::uploadBanner');
$routes->post('admin/opd-queue-tv/delete-banner', 'Setting\\OpdQueueSetting::deleteBanner');
$routes->get('admin/bed-management', 'Setting\\BedManagement::index', ['filter' => $settingsBedFilter]);
$routes->get('admin/departments', 'Setting\\BedManagement::departments', ['filter' => $settingsBedFilter]);
$routes->post('admin/departments/save', 'Setting\\BedManagement::saveDepartment', ['filter' => $settingsBedFilter]);

View File

@ -823,6 +823,17 @@ class DoctorApi extends BaseController
$allTrackers = array_merge($docTokens, $deptTokens);
// 3. Hospital Advertisements & Doctor Showcase Slides
$adsJsonRow = $db->table('hospital_setting')->select('s_value')->where('s_name', 'TV_QUEUE_ADS_JSON')->get(1)->getRowArray();
$customAdsList = !empty($adsJsonRow['s_value']) ? json_decode($adsJsonRow['s_value'], true) : [];
$activeCustomAds = [];
if (is_array($customAdsList)) {
foreach ($customAdsList as $cad) {
if (!isset($cad['enabled']) || (int)$cad['enabled'] === 1) {
$activeCustomAds[] = $cad;
}
}
}
$ads = [];
$adId = 1;
@ -840,36 +851,64 @@ class DoctorApi extends BaseController
];
}
// Hospital Service Advertisements
$ads[] = [
'id' => $adId++,
'type' => 'hospital_ad',
'title' => 'In-House 24/7 Pharmacy',
'tagline' => '100% Genuine Medicines & Fastest Counter Dispense',
'image_url' => base_url('assets/img/slides-1.jpg'),
'description' => 'Discounts on prescription billing. Doorstep delivery available for senior citizens and admitted patients.'
];
$ads[] = [
'id' => $adId++,
'type' => 'hospital_ad',
'title' => 'Advanced Radiology & Imaging Center',
'tagline' => 'Digital X-Ray, 32-Slice CT Scan & 4D Ultrasound',
'image_url' => base_url('assets/img/slides-2.jpg'),
'description' => 'Accurate high-precision diagnostic imaging with immediate digital report generation.'
];
$ads[] = [
'id' => $adId++,
'type' => 'hospital_ad',
'title' => '24x7 Emergency & Critical Care ICU',
'tagline' => 'Fully Equipped Ventilators, Cardiac Monitors & Trauma Team',
'image_url' => base_url('assets/img/slides-3.jpg'),
'description' => 'Round-the-clock emergency medical services with dedicated ICU & Operation Theater.'
];
// Add Active Custom Admin Uploaded Ad Banners
if (!empty($activeCustomAds)) {
foreach ($activeCustomAds as $cad) {
$ads[] = [
'id' => $adId++,
'type' => $cad['type'] ?? 'hospital_ad',
'title' => $cad['title'] ?? 'Hospital Service',
'tagline' => $cad['tagline'] ?? '',
'image_url' => !empty($cad['relative_image_url']) ? base_url($cad['relative_image_url']) : ($cad['image_url'] ?? base_url('assets/img/slides-1.jpg')),
'description' => $cad['description'] ?? ''
];
}
} else {
// Default Service Banners
$ads[] = [
'id' => $adId++,
'type' => 'hospital_ad',
'title' => 'In-House 24/7 Pharmacy',
'tagline' => '100% Genuine Medicines & Fastest Counter Dispense',
'image_url' => base_url('assets/img/slides-1.jpg'),
'description' => 'Discounts on prescription billing. Doorstep delivery available for senior citizens and admitted patients.'
];
$ads[] = [
'id' => $adId++,
'type' => 'hospital_ad',
'title' => 'Advanced Radiology & Imaging Center',
'tagline' => 'Digital X-Ray, 32-Slice CT Scan & 4D Ultrasound',
'image_url' => base_url('assets/img/slides-2.jpg'),
'description' => 'Accurate high-precision diagnostic imaging with immediate digital report generation.'
];
$ads[] = [
'id' => $adId++,
'type' => 'hospital_ad',
'title' => '24x7 Emergency & Critical Care ICU',
'tagline' => 'Fully Equipped Ventilators, Cardiac Monitors & Trauma Team',
'image_url' => base_url('assets/img/slides-3.jpg'),
'description' => 'Round-the-clock emergency medical services with dedicated ICU & Operation Theater.'
];
}
// Fetch Ticker & Left Ad Settings
$tickerRow = $db->table('hospital_setting')->select('s_value')->where('s_name', 'TV_QUEUE_FOOTER_TICKER')->get(1)->getRowArray();
$leftAdTextRow = $db->table('hospital_setting')->select('s_value')->where('s_name', 'TV_QUEUE_LEFT_AD_TEXT')->get(1)->getRowArray();
$leftAdImgRow = $db->table('hospital_setting')->select('s_value')->where('s_name', 'TV_QUEUE_LEFT_AD_IMAGE')->get(1)->getRowArray();
$leftAdEnabledRow = $db->table('hospital_setting')->select('s_value')->where('s_name', 'TV_QUEUE_LEFT_AD_ENABLED')->get(1)->getRowArray();
$hospNameRow = $db->table('hospital_setting')->select('s_value')->where('s_name', 'H_Name')->get(1)->getRowArray();
return $this->response->setJSON([
'status' => 1,
'hospital_name' => 'E-Atria Multispeciality Hospital',
'hospital_name' => !empty($hospNameRow['s_value']) ? $hospNameRow['s_value'] : 'E-Atria Multispeciality Hospital',
'server_time' => date('d-m-Y h:i:s A'),
'footer_ticker' => !empty($tickerRow['s_value']) ? $tickerRow['s_value'] : 'Welcome to E-Atria Multispeciality Hospital. Please observe your token number on the screen. Voice announcements will call your turn. For assistance, contact the help desk. • Emergency Helpline: +91-9876543210 • 24/7 Pharmacy & Ambulance Services Available',
'left_ad' => [
'enabled' => ($leftAdEnabledRow['s_value'] ?? '1') === '1',
'text' => $leftAdTextRow['s_value'] ?? '24x7 Emergency ICU & Ambulance Available',
'image_url' => !empty($leftAdImgRow['s_value']) ? base_url($leftAdImgRow['s_value']) : ''
],
'trackers' => $allTrackers,
'ads' => $ads
]);

View File

@ -0,0 +1,193 @@
<?php
namespace App\Controllers\Setting;
use App\Controllers\BaseController;
use CodeIgniter\Database\BaseConnection;
class OpdQueueSetting extends BaseController
{
private BaseConnection $db;
public function __construct()
{
$this->db = db_connect();
}
public function index()
{
$tickerText = $this->getSetting('TV_QUEUE_FOOTER_TICKER', 'Welcome to E-Atria Multispeciality Hospital. Please observe your token number on the screen. Voice announcements will call your turn. For assistance, contact the help desk. • Emergency Helpline: +91-9876543210 • 24/7 Pharmacy & Ambulance Services Available');
$leftAdText = $this->getSetting('TV_QUEUE_LEFT_AD_TEXT', '24x7 Emergency & Trauma Center • ICU & Ventilator Available');
$leftAdImage = $this->getSetting('TV_QUEUE_LEFT_AD_IMAGE', '');
$leftAdEnabled = $this->getSetting('TV_QUEUE_LEFT_AD_ENABLED', '1');
$adsJson = $this->getSetting('TV_QUEUE_ADS_JSON', '');
$customAds = [];
if ($adsJson !== '') {
$customAds = json_decode($adsJson, true) ?: [];
}
return view('Setting/Admin/opd_queue_setting', [
'footer_ticker' => $tickerText,
'left_ad_text' => $leftAdText,
'left_ad_image' => $leftAdImage,
'left_ad_enabled' => $leftAdEnabled,
'custom_ads' => $customAds
]);
}
public function save()
{
if (!$this->request->isAJAX()) {
return $this->response->setJSON(['status' => 0, 'message' => 'Invalid request']);
}
$post = $this->request->getPost();
$ticker = trim((string) ($post['footer_ticker'] ?? ''));
$leftAdText = trim((string) ($post['left_ad_text'] ?? ''));
$leftAdEnabled = isset($post['left_ad_enabled']) ? '1' : '0';
if ($ticker !== '') {
$this->setSetting('TV_QUEUE_FOOTER_TICKER', $ticker);
}
$this->setSetting('TV_QUEUE_LEFT_AD_TEXT', $leftAdText);
$this->setSetting('TV_QUEUE_LEFT_AD_ENABLED', $leftAdEnabled);
// Handle Left Bottom Ad Image Upload
$leftAdFile = $this->request->getFile('left_ad_image_file');
if ($leftAdFile && $leftAdFile->isValid() && !$leftAdFile->hasMoved()) {
$ext = strtolower($leftAdFile->getExtension());
if (in_array($ext, ['jpg', 'jpeg', 'png', 'webp'])) {
$targetDir = FCPATH . 'assets/images/tv_ads';
if (!is_dir($targetDir)) @mkdir($targetDir, 0775, true);
$newName = 'left_ad_' . date('Ymd_His') . '.' . $ext;
$leftAdFile->move($targetDir, $newName);
$this->setSetting('TV_QUEUE_LEFT_AD_IMAGE', 'assets/images/tv_ads/' . $newName);
}
}
return $this->response->setJSON([
'status' => 1,
'message' => 'OPD Queue TV Display settings updated successfully!'
]);
}
public function uploadBanner()
{
if (!$this->request->isAJAX()) {
return $this->response->setJSON(['status' => 0, 'message' => 'Invalid request']);
}
$post = $this->request->getPost();
$title = trim((string) ($post['title'] ?? ''));
$tagline = trim((string) ($post['tagline'] ?? ''));
$description = trim((string) ($post['description'] ?? ''));
$enabled = isset($post['enabled']) ? 1 : 0;
$slideType = trim((string) ($post['slide_type'] ?? 'hospital_ad'));
$file = $this->request->getFile('banner_image');
$imageUrl = '';
if ($file && $file->isValid() && !$file->hasMoved()) {
$ext = strtolower($file->getExtension());
if (in_array($ext, ['jpg', 'jpeg', 'png', 'webp'])) {
$targetDir = FCPATH . 'assets/images/tv_ads';
if (!is_dir($targetDir)) @mkdir($targetDir, 0775, true);
$newName = 'ad_' . date('Ymd_His') . '_' . mt_rand(1000, 9999) . '.' . $ext;
$file->move($targetDir, $newName);
$imageUrl = 'assets/images/tv_ads/' . $newName;
}
}
if ($imageUrl === '' && !empty($post['existing_image_url'])) {
$imageUrl = $post['existing_image_url'];
}
if ($title === '') {
return $this->response->setJSON(['status' => 0, 'message' => 'Banner Title is required']);
}
$adsJson = $this->getSetting('TV_QUEUE_ADS_JSON', '');
$customAds = json_decode($adsJson, true) ?: [];
$newSlide = [
'id' => time() . mt_rand(10, 99),
'type' => $slideType,
'title' => $title,
'tagline' => $tagline,
'description' => $description,
'image_url' => $imageUrl !== '' ? base_url($imageUrl) : base_url('assets/img/slides-1.jpg'),
'relative_image_url' => $imageUrl,
'enabled' => $enabled
];
$customAds[] = $newSlide;
$this->setSetting('TV_QUEUE_ADS_JSON', json_encode($customAds, JSON_UNESCAPED_SLASHES));
return $this->response->setJSON([
'status' => 1,
'message' => 'Right Side Advertisement Banner added successfully!'
]);
}
public function deleteBanner()
{
if (!$this->request->isAJAX()) {
return $this->response->setJSON(['status' => 0, 'message' => 'Invalid request']);
}
$post = $this->request->getPost();
$bannerId = (int) ($post['banner_id'] ?? 0);
$adsJson = $this->getSetting('TV_QUEUE_ADS_JSON', '');
$customAds = json_decode($adsJson, true) ?: [];
$updatedAds = [];
foreach ($customAds as $ad) {
if ((int) ($ad['id'] ?? 0) !== $bannerId) {
$updatedAds[] = $ad;
}
}
$this->setSetting('TV_QUEUE_ADS_JSON', json_encode($updatedAds, JSON_UNESCAPED_SLASHES));
return $this->response->setJSON([
'status' => 1,
'message' => 'Advertisement slide removed successfully!'
]);
}
private function getSetting(string $name, string $default = ''): string
{
$row = $this->db->table('hospital_setting')
->select('s_value')
->where('s_name', $name)
->get(1)
->getRowArray();
return trim((string) ($row['s_value'] ?? $default));
}
private function setSetting(string $name, string $value): bool
{
$existing = $this->db->table('hospital_setting')
->select('id')
->where('s_name', $name)
->get(1)
->getRowArray();
if ($existing) {
return $this->db->table('hospital_setting')
->where('id', (int) $existing['id'])
->update(['s_value' => $value]);
} else {
return $this->db->table('hospital_setting')->insert([
's_name' => $name,
's_value' => $value
]);
}
}
}

View File

@ -100,6 +100,12 @@
<span>ABDM Report Doctors</span>
</a>
</div>
<div class="col-6 col-md-2 col-lg-2">
<a class="admin-tile" href="javascript:openAdminSubPage('<?= base_url('setting/admin/opd-queue-tv') ?>','OPD Queue TV Settings');">
<i class="bi bi-tv" style="color:#0d6efd"></i>
<span>OPD Queue TV</span>
</a>
</div>
<div class="col-6 col-md-2 col-lg-2">
<a class="admin-tile" href="javascript:openAdminSubPage('<?= base_url('setting/admin/hospital-profile') ?>','Hospital Profile');">
<i class="bi bi-building"></i>

View File

@ -0,0 +1,210 @@
<section class="content">
<div class="pagetitle mb-3">
<h1 class="mb-0">OPD Queue TV Display Settings</h1>
<nav>
<ol class="breadcrumb mb-0">
<li class="breadcrumb-item"><a href="javascript:showAdminTiles()">Admin Panel</a></li>
<li class="breadcrumb-item active">OPD Queue TV Settings</li>
</ol>
</nav>
</div>
<div class="row g-3">
<!-- Left Side: Ticker & Left Bottom Ad Settings -->
<div class="col-md-6">
<div class="card shadow-sm border-0 mb-3">
<div class="card-header bg-primary text-white fw-bold">
<i class="bi bi-fonts me-2"></i> Footer Scrolling Ticker Text
</div>
<div class="card-body mt-3">
<form id="form_ticker_settings" enctype="multipart/form-data">
<div class="mb-3">
<label class="form-label fw-bold">Scrolling Ticker Message</label>
<textarea class="form-control" name="footer_ticker" rows="3" required><?= esc($footer_ticker) ?></textarea>
<small class="text-muted">This message scrolls continuously across the bottom of the TV screen.</small>
</div>
<hr>
<h6 class="fw-bold text-primary mb-3">
<i class="bi bi-layout-sidebar-inset me-1"></i> Left-Side Bottom Ad Space Settings
</h6>
<div class="form-check form-switch mb-3">
<input class="form-check-input" type="checkbox" name="left_ad_enabled" id="left_ad_enabled" value="1" <?= $left_ad_enabled === '1' ? 'checked' : '' ?>>
<label class="form-check-label fw-bold" for="left_ad_enabled">Enable Left-Side Bottom Ad Space</label>
</div>
<div class="mb-3">
<label class="form-label fw-semibold">Left-Side Ad Text / Announcement</label>
<input type="text" class="form-control" name="left_ad_text" value="<?= esc($left_ad_text) ?>" placeholder="e.g. 24x7 Emergency ICU & Ambulance Available">
</div>
<div class="mb-3">
<label class="form-label fw-semibold">Left-Side Custom Image Banner (Optional)</label>
<input type="file" class="form-control" name="left_ad_image_file" accept="image/*">
<?php if (!empty($left_ad_image)): ?>
<div class="mt-2">
<small class="d-block text-muted mb-1">Current Banner:</small>
<img src="<?= base_url($left_ad_image) ?>" style="max-height: 80px; border-radius: 8px;" class="border shadow-sm" />
</div>
<?php endif; ?>
</div>
<button type="submit" class="btn btn-primary fw-bold px-4">
<i class="bi bi-save me-1"></i> Save Ticker &amp; Left Ad Settings
</button>
</form>
</div>
</div>
</div>
<!-- Right Side: Showcase & Ad Banners Management -->
<div class="col-md-6">
<div class="card shadow-sm border-0 mb-3">
<div class="card-header bg-success text-white fw-bold">
<i class="bi bi-file-earmark-image me-2"></i> Right-Side Showcase Ad Banners
</div>
<div class="card-body mt-3">
<form id="form_add_banner" enctype="multipart/form-data">
<h6 class="fw-bold text-success mb-3">Add New Right-Side Showcase Slide</h6>
<div class="row g-2 mb-2">
<div class="col-md-6">
<label class="form-label fw-semibold small mb-1">Slide Type</label>
<select class="form-select form-select-sm" name="slide_type">
<option value="hospital_ad">Hospital Ad Banner</option>
<option value="doctor_profile">Doctor Profile Showcase</option>
</select>
</div>
<div class="col-md-6">
<label class="form-label fw-semibold small mb-1">Slide Title *</label>
<input type="text" class="form-control form-control-sm" name="title" required placeholder="e.g. In-House 24/7 Pharmacy">
</div>
</div>
<div class="mb-2">
<label class="form-label fw-semibold small mb-1">Tagline / Subtitle</label>
<input type="text" class="form-control form-control-sm" name="tagline" placeholder="e.g. 100% Genuine Medicines & Fast Dispense">
</div>
<div class="mb-2">
<label class="form-label fw-semibold small mb-1">Description / Details</label>
<textarea class="form-control form-control-sm" name="description" rows="2" placeholder="e.g. Available 24/7 for inpatient and outpatient needs..."></textarea>
</div>
<div class="mb-3">
<label class="form-label fw-semibold small mb-1">Upload Banner Image *</label>
<input type="file" class="form-control form-control-sm" name="banner_image" accept="image/*" required>
</div>
<div class="form-check form-switch mb-3">
<input class="form-check-input" type="checkbox" name="enabled" id="slide_enabled" value="1" checked>
<label class="form-check-label small fw-bold" for="slide_enabled">Active Slide in Carousel</label>
</div>
<button type="submit" class="btn btn-success btn-sm fw-bold px-3">
<i class="bi bi-plus-circle me-1"></i> Add Slide Banner
</button>
</form>
<hr class="my-4">
<h6 class="fw-bold text-dark mb-3">Configured Right-Side Slides</h6>
<div id="custom_ads_list_container">
<?php if (empty($custom_ads)): ?>
<div class="alert alert-info small py-2">
No custom ad slides uploaded yet. Default system slides are active.
</div>
<?php else: ?>
<div class="d-flex flex-column gap-2">
<?php foreach ($custom_ads as $ad): ?>
<div class="d-flex align-items-center justify-content-between p-2 border rounded bg-light">
<div class="d-flex align-items-center gap-2">
<?php if (!empty($ad['relative_image_url'])): ?>
<img src="<?= base_url($ad['relative_image_url']) ?>" style="width: 50px; height: 40px; object-fit: cover;" class="rounded border" />
<?php else: ?>
<div class="bg-secondary text-white rounded p-1 small" style="width: 50px; text-align: center;">No Img</div>
<?php endif; ?>
<div>
<div class="fw-bold small"><?= esc($ad['title']) ?></div>
<small class="text-muted d-block opacity-75" style="font-size: 11px;"><?= esc($ad['tagline'] ?? '') ?></small>
</div>
</div>
<div>
<button type="button" class="btn btn-outline-danger btn-sm p-1 px-2" onclick="deleteBanner(<?= (int) ($ad['id'] ?? 0) ?>)">
<i class="bi bi-trash"></i>
</button>
</div>
</div>
<?php endforeach; ?>
</div>
<?php endif; ?>
</div>
</div>
</div>
</div>
</div>
</section>
<script>
$('#form_ticker_settings').on('submit', function(e) {
e.preventDefault();
var formData = new FormData(this);
$.ajax({
url: '<?= base_url('setting/admin/opd-queue-tv/save') ?>',
type: 'POST',
data: formData,
contentType: false,
processData: false,
dataType: 'json',
success: function(res) {
if (res.status === 1) {
alert(res.message);
location.reload();
} else {
alert(res.message || 'Error saving settings');
}
}
});
});
$('#form_add_banner').on('submit', function(e) {
e.preventDefault();
var formData = new FormData(this);
$.ajax({
url: '<?= base_url('setting/admin/opd-queue-tv/upload-banner') ?>',
type: 'POST',
data: formData,
contentType: false,
processData: false,
dataType: 'json',
success: function(res) {
if (res.status === 1) {
alert(res.message);
location.reload();
} else {
alert(res.message || 'Error uploading banner');
}
}
});
});
function deleteBanner(bannerId) {
if (!confirm('Are you sure you want to delete this advertisement slide?')) return;
$.ajax({
url: '<?= base_url('setting/admin/opd-queue-tv/delete-banner') ?>',
type: 'POST',
data: { banner_id: bannerId },
dataType: 'json',
success: function(res) {
if (res.status === 1) {
alert(res.message);
location.reload();
} else {
alert(res.message || 'Error deleting banner');
}
}
});
}
</script>

View File

@ -289,6 +289,13 @@
</div>
</div>
<!-- Left Bottom Space Ad Banner -->
<div id="tracker_left_ad_container" class="px-2 py-1" style="display: none; background: #0f172a; color: #ffffff;">
<div id="tracker_left_ad_content" class="p-2 rounded d-flex align-items-center justify-content-between shadow-sm" style="background: linear-gradient(90deg, #1e293b 0%, #0f172a 100%); border: 1px solid rgba(255,255,255,0.15);">
<!-- Dynamic Left Bottom Ad Content -->
</div>
</div>
<!-- Page Dots Footer for Left Column -->
<div class="tracker-footer" id="tracker_dots_container">
<!-- Dynamically Populated Dots -->
@ -336,9 +343,23 @@
setInterval(updateClock, 1000);
updateClock();
// Speech Pronunciation Formatter (Expands abbreviations so TTS reads "Doctor", "Department", etc. cleanly)
function formatSpeechText(text) {
if (!text) return '';
var clean = text;
clean = clean.replace(/\bDr\.\s*/gi, 'Doctor ');
clean = clean.replace(/\bDr\s+/gi, 'Doctor ');
clean = clean.replace(/\bDept\.\s*/gi, 'Department ');
clean = clean.replace(/\bDept\s+/gi, 'Department ');
clean = clean.replace(/\bNo\.\s*/gi, 'Number ');
clean = clean.replace(/\bUSG\b/gi, 'Ultrasound');
return clean;
}
// Audio Announcement
function speakText(text) {
function speakText(rawText) {
if (!audioEnabled) return;
var text = formatSpeechText(rawText);
try {
if ('speechSynthesis' in window) {
window.speechSynthesis.cancel();
@ -498,6 +519,26 @@
var hospEl = document.getElementById('hdr_hosp_name');
if (hospEl) hospEl.textContent = json.hospital_name;
}
if (json.footer_ticker) {
var tickerEl = document.getElementById('ticker_text');
if (tickerEl) {
tickerEl.innerHTML = '<i class="bi bi-info-circle-fill text-warning me-2"></i> ' + json.footer_ticker;
}
}
if (json.left_ad) {
var leftAdContainer = document.getElementById('tracker_left_ad_container');
var leftAdContent = document.getElementById('tracker_left_ad_content');
if (leftAdContainer && leftAdContent && json.left_ad.enabled) {
leftAdContainer.style.display = 'block';
if (json.left_ad.image_url) {
leftAdContent.innerHTML = '<img src="' + json.left_ad.image_url + '" style="max-height: 48px; width: 100%; object-fit: contain; border-radius: 6px;" />';
} else {
leftAdContent.innerHTML = '<div class="d-flex align-items-center gap-2 text-warning fw-bold small"><i class="bi bi-megaphone-fill fs-5"></i> <span>' + (json.left_ad.text || '24x7 Emergency Services Available') + '</span></div>';
}
} else if (leftAdContainer) {
leftAdContainer.style.display = 'none';
}
}
renderTrackers();
renderShowcase();
}