Files
kane-diagnostics/hubzilla/addon/scn01/Widget/Scn01.php
2026-06-05 15:40:09 -04:00

176 lines
7.6 KiB
PHP

<?php
namespace Zotlabs\Widget;
class Scn01 {
public function widget($arr) {
$listings = $this->load_listings();
return $this->render_directory($listings);
}
// ----------------------------------------------------------------------------
// DIRECTORY
// ----------------------------------------------------------------------------
private function render_directory($listings) {
$out = '<div class="scn01-directory">';
// Horizontal Bootstrap nav-tabs
$out .= '<ul class="nav nav-tabs scn01-dir-tabs" id="scn01DirectoryTabs" role="tablist">';
$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],
];
foreach ($tabs as $tab) {
$active = $tab['active'] ? 'active' : '';
$selected = $tab['active'] ? 'true' : 'false';
$out .= '
<li class="nav-item" role="presentation">
<button class="nav-link ' . $active . '"
id="scn01-tab-' . $tab['id'] . '"
data-bs-toggle="tab"
data-bs-target="#scn01-pane-' . $tab['id'] . '"
type="button" role="tab"
aria-selected="' . $selected . '">
' . $tab['label'] . '
</button>
</li>
';
}
$out .= '</ul>';
// Tab panes
$out .= '<div class="tab-content scn01-dir-content" id="scn01DirectoryTabContent">';
$out .= '<div class="tab-pane fade show active" id="scn01-pane-core" role="tabpanel">';
$out .= $this->render_core_tab($listings['core'] ?? []);
$out .= '</div>';
$out .= '<div class="tab-pane fade" id="scn01-pane-tier1" role="tabpanel">';
$out .= $this->render_tier_tab($listings['tier1'] ?? [], 'Mediators, subject matter experts, advisors, and authors will be listed here.');
$out .= '</div>';
$out .= '<div class="tab-pane fade" id="scn01-pane-tier2" role="tabpanel">';
$out .= $this->render_tier_tab($listings['tier2'] ?? [], 'Attorneys who have concluded HOA homeowner cases in Illinois will be listed here.');
$out .= '</div>';
$out .= '<div class="tab-pane fade" id="scn01-pane-other" role="tabpanel">';
$out .= $this->render_tier_tab($listings['other'] ?? [], 'Civic organizations, consumer protection entities, and oversight agencies will be listed here.');
$out .= '</div>';
$out .= '</div>'; // tab-content
$out .= '</div>'; // scn01-directory
return $out;
}
private 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.',
],
[
'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.',
],
[
'slot' => 'peer-operator',
'role' => 'Peer Operator',
'description' => 'The cooperating Civic Infrastructure host who can independently verify, support, and if necessary continue this diagnostic record.',
],
];
// Index populated entries by slot
$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 = $this->h($e['name'] ?? '');
$url = $this->h($e['url'] ?? '');
$out .= '<div class="scn01-dir-slot scn01-slot-active">';
$out .= '<div class="scn01-slot-role">' . $this->h($slot['role']) . '</div>';
if ($url) {
$out .= '<div class="scn01-slot-name"><a href="' . $url . '" target="_blank" rel="noopener">' . $name . '</a></div>';
} else {
$out .= '<div class="scn01-slot-name">' . $name . '</div>';
}
$out .= '</div>';
} else {
$out .= '<div class="scn01-dir-slot scn01-slot-pending">';
$out .= '<div class="scn01-slot-role">' . $this->h($slot['role']) . '</div>';
$out .= '<div class="scn01-slot-description">' . $this->h($slot['description']) . '</div>';
$out .= '<div class="scn01-slot-status">Invitation pending</div>';
$out .= '</div>';
}
}
return $out;
}
private function render_tier_tab($entries, $placeholder) {
if (empty($entries)) {
return '<div class="scn01-dir-placeholder text-muted small">' . $this->h($placeholder) . '</div>';
}
$out = '';
foreach ($entries as $entry) {
$name = $this->h($entry['name'] ?? '');
$role = $this->h($entry['role'] ?? '');
$desc = $this->h($entry['description'] ?? '');
$url = $this->h($entry['url'] ?? '');
$out .= '<div class="scn01-dir-entry">';
$out .= '<div class="scn01-entry-role">' . $role . '</div>';
if ($url) {
$out .= '<div class="scn01-entry-name"><a href="' . $url . '" target="_blank" rel="noopener">' . $name . '</a></div>';
} else {
$out .= '<div class="scn01-entry-name">' . $name . '</div>';
}
if ($desc) {
$out .= '<div class="scn01-entry-desc">' . $desc . '</div>';
}
$out .= '</div>';
}
return $out;
}
// ----------------------------------------------------------------------------
// LISTINGS LOADER
// ----------------------------------------------------------------------------
private function load_listings() {
$config = $this->load_config();
$path = $config['listings_file'] ?? 'addon/scn01/listings.json';
$raw = @file_get_contents($path);
if ($raw === false) {
// Fail visibly — return structure with empty tiers, Core slots will show pending
return ['core' => [], 'tier1' => [], 'tier2' => [], 'other' => []];
}
$data = json_decode($raw, true);
if (json_last_error() !== JSON_ERROR_NONE) {
return ['core' => [], 'tier1' => [], 'tier2' => [], 'other' => []];
}
return $data;
}
private function load_config() {
$raw = @file_get_contents('addon/scn01/config.json');
if ($raw === false) return [];
$data = json_decode($raw, true);
return (json_last_error() === JSON_ERROR_NONE) ? $data : [];
}
// ----------------------------------------------------------------------------
// HELPER
// ----------------------------------------------------------------------------
private function h($value) {
return htmlspecialchars((string) $value, ENT_QUOTES, 'UTF-8');
}
}