Initial push

This commit is contained in:
2026-06-13 08:09:39 -04:00
parent d2d2a4d596
commit b8750e50c5
5 changed files with 587 additions and 21 deletions

View File

@@ -0,0 +1,201 @@
<?php
/**
* DSC-01 Renderer
* Renders the ten DSC category checklist. See DSC-RENDERER-SPEC.md.
*/
// ---------------------------------------------------------------------------
// SCHEMA LOADING
// ---------------------------------------------------------------------------
function dsc01_load_categories() {
$dir = 'addon/dsc01/diagnostic-categories';
$out = [];
$files = @glob($dir . '/DSC-*.json');
if (!$files) return $out;
sort($files);
foreach ($files as $file) {
$raw = @file_get_contents($file);
if ($raw === false) continue;
$data = json_decode($raw, true);
if (json_last_error() !== JSON_ERROR_NONE) continue;
$meta = $data['_meta'] ?? null;
if (!$meta || empty($meta['code'])) continue;
$out[$meta['code']] = $meta;
}
ksort($out);
return $out;
}
// ---------------------------------------------------------------------------
// RENDER — INDEX (category overview)
// ---------------------------------------------------------------------------
function dsc01_render_index() {
$categories = dsc01_load_categories();
$out = '<div class="dsc01-content">';
$out .= '<div class="dsc01-header mb-3">';
$out .= '<h2>Diagnostic Surface Categories</h2>';
$out .= '<p class="text-muted">The legal surfaces where HOA governance disputes manifest, organized from the homeowner\'s perspective. Select an association to apply this checklist to a case.</p>';
$out .= '</div>';
if (empty($categories)) {
$out .= '<div class="dsc01-placeholder text-muted fst-italic">No categories defined.</div>';
$out .= '</div>';
return $out;
}
$out .= '<nav class="dsc01-category-nav" aria-label="Diagnostic Surface Categories">';
$out .= '<ul class="list-group list-group-flush">';
foreach ($categories as $code => $meta) {
$title = dsc01_h($meta['title'] ?? $code);
$summary = dsc01_h($meta['summary'] ?? '');
$out .= '<li class="list-group-item">';
$out .= '<strong>' . dsc01_h($code) . '</strong> — ' . $title;
if ($summary) {
$out .= '<p class="small text-muted mb-0">' . $summary . '</p>';
}
$out .= '</li>';
}
$out .= '</ul></nav>';
// Association picker — list associations from vs01/config.json
$raw = @file_get_contents('addon/vs01/config.json');
$cfg = $raw ? json_decode($raw, true) : [];
$associations = $cfg['associations'] ?? [];
if (!empty($associations)) {
$out .= '<div class="dsc01-association-picker mt-4">';
$out .= '<h3>Apply this checklist</h3>';
$out .= '<ul class="list-group">';
foreach ($associations as $slug => $assoc) {
$name = dsc01_h($assoc['name'] ?? $slug);
$url = z_root() . '/dsc01/' . dsc01_h($slug);
$out .= '<li class="list-group-item"><a href="' . $url . '">' . $name . '</a></li>';
}
$out .= '</ul></div>';
}
$out .= '</div>';
return $out;
}
// ---------------------------------------------------------------------------
// RENDER — ASSOCIATION LANDING (checklist form)
// ---------------------------------------------------------------------------
function dsc01_render_landing($association_slug, $access, $submitted_codes = [], $submitted_narrative = '') {
$raw = @file_get_contents('addon/vs01/config.json');
$cfg = $raw ? json_decode($raw, true) : [];
$assoc = $cfg['associations'][$association_slug] ?? [];
$name = dsc01_h($assoc['name'] ?? $association_slug);
$categories = dsc01_load_categories();
$out = '<div class="dsc01-content">';
$out .= '<div class="dsc01-header mb-3">';
$out .= '<h2>' . $name . '</h2>';
$out .= '<p class="text-muted">Diagnostic Surface Categories checklist. Select every category that applies to your situation.</p>';
$out .= '</div>';
if ($access === 'public') {
$out .= dsc01_render_checklist_readonly($categories);
$out .= '<div class="alert alert-info mt-3">';
$out .= 'DSC Categories are public. ';
$out .= '<a href="https://directory.diagnostics.kane-il.us/channel/theron">Complete the SASE process</a> ';
$out .= 'to submit a checklist for this association.';
$out .= '</div>';
$out .= '</div>';
return $out;
}
$form_url = z_root() . '/dsc01/' . dsc01_h($association_slug);
$out .= '<form method="post" action="' . $form_url . '" class="dsc01-form" novalidate>';
$out .= dsc01_csrf_token();
foreach ($categories as $code => $meta) {
$checked = in_array($code, $submitted_codes, true) ? ' checked' : '';
$title = dsc01_h($meta['title'] ?? $code);
$out .= '<div class="dsc01-category">';
$out .= '<label class="dsc01-category-label">';
$out .= '<input type="checkbox" name="dsc_codes[]" value="' . dsc01_h($code) . '"' . $checked . '> ';
$out .= '<strong>' . dsc01_h($code) . '</strong> — ' . $title;
$out .= '</label>';
if (!empty($meta['diagnostic_questions'])) {
$out .= '<details class="dsc01-questions">';
$out .= '<summary>Diagnostic questions</summary>';
$out .= '<ul>';
foreach ($meta['diagnostic_questions'] as $q) {
$out .= '<li>' . dsc01_h($q) . '</li>';
}
$out .= '</ul>';
$out .= '</details>';
}
$out .= '</div>';
}
$out .= '<div class="dsc01-narrative mt-3">';
$out .= '<label for="dsc01_narrative"><strong>Describe your situation</strong></label>';
$out .= '<textarea id="dsc01_narrative" name="narrative" class="form-control" rows="6">'
. dsc01_h($submitted_narrative) . '</textarea>';
$out .= '</div>';
$out .= '<div class="mt-3">';
$out .= '<button type="submit" class="btn btn-primary">Submit</button>';
$out .= '</div>';
$out .= '</form>';
$out .= '</div>';
return $out;
}
// ---------------------------------------------------------------------------
// RENDER — READ-ONLY CHECKLIST (public access)
// ---------------------------------------------------------------------------
function dsc01_render_checklist_readonly($categories) {
$out = '<div class="dsc01-checklist-readonly">';
foreach ($categories as $code => $meta) {
$title = dsc01_h($meta['title'] ?? $code);
$out .= '<div class="dsc01-category">';
$out .= '<strong>' . dsc01_h($code) . '</strong> — ' . $title;
if (!empty($meta['diagnostic_questions'])) {
$out .= '<details class="dsc01-questions">';
$out .= '<summary>Diagnostic questions</summary>';
$out .= '<ul>';
foreach ($meta['diagnostic_questions'] as $q) {
$out .= '<li>' . dsc01_h($q) . '</li>';
}
$out .= '</ul>';
$out .= '</details>';
}
$out .= '</div>';
}
$out .= '</div>';
return $out;
}
// ---------------------------------------------------------------------------
// RENDER — MANAGE (stub, operator only)
// ---------------------------------------------------------------------------
function dsc01_render_manage($association_slug) {
$raw = @file_get_contents('addon/vs01/config.json');
$cfg = $raw ? json_decode($raw, true) : [];
$assoc = $cfg['associations'][$association_slug] ?? [];
$name = dsc01_h($assoc['name'] ?? $association_slug);
$out = '<div class="dsc01-content">';
$out .= '<h2>Manage — ' . $name . '</h2>';
$out .= '<p class="text-muted fst-italic">DSC record review forthcoming.</p>';
$out .= '</div>';
return $out;
}