Updated
This commit is contained in:
184
hubzilla/addon/vs01/directory_widget.php
Normal file
184
hubzilla/addon/vs01/directory_widget.php
Normal file
@@ -0,0 +1,184 @@
|
||||
<?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');
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user