185 lines
8.4 KiB
PHP
185 lines
8.4 KiB
PHP
<?php
|
|
|
|
/**
|
|
* KdxDirectoryWidget — shared institutional directory renderer.
|
|
*
|
|
* Used by: Vs01, Dsc01, Scn01, Cry01, G1wallet widget classes.
|
|
* CSS: /addon/vs01/view/css/vs01-directory.css (loaded by each widget)
|
|
* Images: /addon/vs01/view/img/directory-core-*.png
|
|
*
|
|
* Each addon widget calls KdxDirectoryWidget::render($listings, $addon_id)
|
|
* after loading its own listings.json. The $addon_id string scopes
|
|
* Bootstrap tab IDs to avoid collisions when multiple widgets appear on
|
|
* the same page.
|
|
*/
|
|
|
|
class KdxDirectoryWidget {
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// ENTRY POINT
|
|
// ---------------------------------------------------------------------------
|
|
|
|
public static function render($listings, $addon_id = 'kdx') {
|
|
$out = '<div class="kdx-directory">';
|
|
$out .= self::render_tabs($listings, $addon_id);
|
|
$out .= '</div>';
|
|
return $out;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// TABS
|
|
// ---------------------------------------------------------------------------
|
|
|
|
private static function render_tabs($listings, $addon_id) {
|
|
$tabs = [
|
|
['id' => 'core', 'label' => 'Core', 'active' => true],
|
|
['id' => 'tier1', 'label' => 'Tier-I', 'active' => false],
|
|
['id' => 'tier2', 'label' => 'Tier-II', 'active' => false],
|
|
['id' => 'other', 'label' => 'Other', 'active' => false],
|
|
];
|
|
|
|
$uid = $addon_id . '-dir';
|
|
|
|
$out = '<ul class="nav nav-tabs kdx-dir-tabs" id="' . $uid . '-tabs" role="tablist">';
|
|
foreach ($tabs as $tab) {
|
|
$active = $tab['active'] ? 'active' : '';
|
|
$selected = $tab['active'] ? 'true' : 'false';
|
|
$out .= '<li class="nav-item" role="presentation">';
|
|
$out .= '<button class="nav-link ' . $active . '"'
|
|
. ' id="' . $uid . '-' . $tab['id'] . '-tab"'
|
|
. ' data-bs-toggle="tab"'
|
|
. ' data-bs-target="#' . $uid . '-' . $tab['id'] . '"'
|
|
. ' type="button" role="tab"'
|
|
. ' aria-selected="' . $selected . '">'
|
|
. $tab['label']
|
|
. '</button>';
|
|
$out .= '</li>';
|
|
}
|
|
$out .= '</ul>';
|
|
|
|
$out .= '<div class="tab-content kdx-dir-content" id="' . $uid . '-content">';
|
|
foreach ($tabs as $tab) {
|
|
$show = $tab['active'] ? ' show active' : '';
|
|
$out .= '<div class="tab-pane fade' . $show . '"'
|
|
. ' id="' . $uid . '-' . $tab['id'] . '"'
|
|
. ' role="tabpanel">';
|
|
if ($tab['id'] === 'core') {
|
|
$out .= self::render_core_tab($listings['core'] ?? []);
|
|
} else {
|
|
$placeholders = [
|
|
'tier1' => 'Mediators, subject matter experts, advisors, and authors will be listed here.',
|
|
'tier2' => 'Attorneys who have concluded HOA homeowner cases in Illinois will be listed here.',
|
|
'other' => 'Civic organizations, consumer protection entities, and oversight agencies will be listed here.',
|
|
];
|
|
$out .= self::render_tier_tab(
|
|
$listings[$tab['id']] ?? [],
|
|
$placeholders[$tab['id']] ?? ''
|
|
);
|
|
}
|
|
$out .= '</div>';
|
|
}
|
|
$out .= '</div>';
|
|
|
|
return $out;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// CORE TAB — three fixed slots with pending-card images
|
|
// ---------------------------------------------------------------------------
|
|
|
|
private static function render_core_tab($core_entries) {
|
|
$slots = [
|
|
[
|
|
'slot' => 'taxonomy-authority',
|
|
'role' => 'Taxonomy Authority',
|
|
'description' => 'The institutional source of the case law categories this diagnostic record is organized by.',
|
|
'image' => '/addon/vs01/view/img/directory-core-taxonomy-authority.png',
|
|
],
|
|
[
|
|
'slot' => 'methodology-certifier',
|
|
'role' => 'Methodology Certifier',
|
|
'description' => 'The law firm whose professional association with this project certifies the diagnostic record meets a standard the legal community can use.',
|
|
'image' => '/addon/vs01/view/img/directory-core-methodology-certifier.png',
|
|
],
|
|
[
|
|
'slot' => 'peer-operator',
|
|
'role' => 'Peer Operator',
|
|
'description' => 'The cooperating Civic Infrastructure host who can independently verify, support, and if necessary continue this diagnostic record.',
|
|
'image' => '/addon/vs01/view/img/directory-core-peer-operator.png',
|
|
],
|
|
];
|
|
|
|
$populated = [];
|
|
foreach ($core_entries as $entry) {
|
|
if (!empty($entry['slot']) && ($entry['status'] ?? '') === 'active') {
|
|
$populated[$entry['slot']] = $entry;
|
|
}
|
|
}
|
|
|
|
$out = '';
|
|
foreach ($slots as $slot) {
|
|
if (isset($populated[$slot['slot']])) {
|
|
$e = $populated[$slot['slot']];
|
|
$name = self::h($e['name'] ?? '');
|
|
$url = self::h($e['url'] ?? '');
|
|
$image = self::h($e['image'] ?? $slot['image']);
|
|
$out .= '<div class="vs01-dir-card vs01-dir-card-core vs01-card-active">';
|
|
$out .= '<div class="vs01-card-image"><img src="' . $image . '" alt="" loading="lazy"></div>';
|
|
$out .= '<div class="vs01-card-body">';
|
|
$out .= '<div class="vs01-card-role">' . self::h($slot['role']) . '</div>';
|
|
if ($url) {
|
|
$out .= '<div class="vs01-card-name"><a href="' . $url . '" target="_blank" rel="noopener">' . $name . '</a></div>';
|
|
} else {
|
|
$out .= '<div class="vs01-card-name">' . $name . '</div>';
|
|
}
|
|
$out .= '</div></div>';
|
|
} else {
|
|
$out .= '<div class="vs01-dir-card vs01-dir-card-core vs01-card-pending">';
|
|
$out .= '<div class="vs01-card-image"><img src="' . self::h($slot['image']) . '" alt="' . self::h($slot['role']) . ' — reserved" loading="lazy"></div>';
|
|
$out .= '<div class="vs01-card-body">';
|
|
$out .= '<div class="vs01-card-description">' . self::h($slot['description']) . '</div>';
|
|
$out .= '</div></div>';
|
|
}
|
|
}
|
|
return $out;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// TIER TABS — Tier-I, Tier-II, Other
|
|
// ---------------------------------------------------------------------------
|
|
|
|
private static function render_tier_tab($entries, $placeholder) {
|
|
if (empty($entries)) {
|
|
return '<div class="vs01-dir-placeholder text-muted small">' . self::h($placeholder) . '</div>';
|
|
}
|
|
$out = '';
|
|
foreach ($entries as $entry) {
|
|
$name = self::h($entry['name'] ?? '');
|
|
$role = self::h($entry['role'] ?? '');
|
|
$desc = self::h($entry['description'] ?? '');
|
|
$url = self::h($entry['url'] ?? '');
|
|
$image = self::h($entry['image'] ?? '/addon/vs01/view/img/directory-tier-default.png');
|
|
$out .= '<div class="vs01-dir-card vs01-dir-card-tier">';
|
|
$out .= '<div class="vs01-card-image"><img src="' . $image . '" alt="" loading="lazy"></div>';
|
|
$out .= '<div class="vs01-card-body">';
|
|
if ($role) $out .= '<div class="vs01-card-role">' . $role . '</div>';
|
|
if ($url) {
|
|
$out .= '<div class="vs01-card-name"><a href="' . $url . '" target="_blank" rel="noopener">' . $name . '</a></div>';
|
|
} else {
|
|
$out .= '<div class="vs01-card-name">' . $name . '</div>';
|
|
}
|
|
if ($desc) $out .= '<div class="vs01-card-desc">' . $desc . '</div>';
|
|
$out .= '</div></div>';
|
|
}
|
|
return $out;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// HELPER
|
|
// ---------------------------------------------------------------------------
|
|
|
|
public static function h($value) {
|
|
return htmlspecialchars((string) $value, ENT_QUOTES, 'UTF-8');
|
|
}
|
|
}
|