Files
inter-hub/Web/View/WidgetOwnerships/Index.hs
Bernd Worsch 9265ca2d9c feat(P8): IHF Phase 8 complete — Federated Hub Maturity
Implements the final phase of the IHF v0.1 specification:

- WidgetOwnership: delegated ownership registry (local/delegated/global),
  append-only audit artefacts, ownership badge on widget show page
- HubRoutingRule + RoutingEngine: priority-ordered inter-hub routing engine;
  null-inclusive category/widget-type matching; RouteNowAction for manual
  re-evaluation; RoutedCandidates view per hub
- FederatedPolicyOverlay: draft → active → retired lifecycle; activated
  overlays are immutable (same pattern as Phase 6 contracts); policy
  compliance dashboard with decision coverage metrics
- StewardshipRole: named governance roles per hub; point-in-time revocation
  pattern; hub and ops-board integration
- ArchiveRecord + is_archived: soft-delete on widgets; lineage inspector
  traces full traceability chain (Widget → Events → Annotations → Candidates
  → Requirements → Decisions → Deployments → Signals + ArchiveRecord)
- FederatedGovernanceDashboard: 5-panel autoRefresh org-wide governance view
  (ownership coverage, routing activity, policy compliance, stewardship
  coverage, archive activity)

Schema: widget_ownerships, hub_routing_rules, federated_policy_overlays,
stewardship_roles, archive_records; ALTER widgets ADD is_archived;
ALTER requirement_candidates ADD routed_to_hub_id

Migration: 1743638400-ihf-phase8-federated-hub-maturity.sql
Tests: Phase 8 integration tests appended to Test/Integration.hs
Docs: docs/phase8-summary.md; SCOPE.md updated to Phase 8 complete

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-29 22:53:01 +00:00

77 lines
3.3 KiB
Haskell
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
module Web.View.WidgetOwnerships.Index where
import Web.Types
import Generated.Types
import IHP.Prelude
import IHP.ViewPrelude
data IndexView = IndexView
{ ownerships :: ![WidgetOwnership]
, widgets :: ![Widget]
, hubs :: ![Hub]
}
instance View IndexView where
html IndexView { .. } = [hsx|
<div class="flex items-center justify-between mb-6">
<h1 class="text-2xl font-semibold">Widget Ownerships</h1>
<a href={NewWidgetOwnershipAction}
class="text-sm bg-blue-600 text-white px-3 py-1.5 rounded hover:bg-blue-700">
Assign Ownership
</a>
</div>
{if null ownerships
then [hsx|<p class="text-sm text-gray-400">No ownership records yet.</p>|]
else [hsx|
<div class="bg-white rounded-lg border border-gray-200 overflow-hidden">
<table class="w-full text-sm">
<thead class="bg-gray-50 border-b border-gray-200">
<tr>
<th class="text-left px-4 py-3 font-medium text-gray-700">Widget</th>
<th class="text-left px-4 py-3 font-medium text-gray-700">Owner Hub</th>
<th class="text-left px-4 py-3 font-medium text-gray-700">Steward Hub</th>
<th class="text-left px-4 py-3 font-medium text-gray-700">Type</th>
<th class="text-left px-4 py-3 font-medium text-gray-700">Effective From</th>
<th class="px-4 py-3"></th>
</tr>
</thead>
<tbody class="divide-y divide-gray-100">
{forEach ownerships renderRow}
</tbody>
</table>
</div>
|]}
|]
where
widgetName wid = maybe (show wid) (.name) (find (\w -> w.id == wid) widgets)
hubName hid = maybe "" (.name) (find (\h -> h.id == hid) hubs)
renderRow :: WidgetOwnership -> Html
renderRow o = [hsx|
<tr class="hover:bg-gray-50">
<td class="px-4 py-3 font-medium text-gray-800">{widgetName o.widgetId}</td>
<td class="px-4 py-3 text-gray-600">{hubName o.ownerHubId}</td>
<td class="px-4 py-3 text-gray-500 text-xs">
{maybe "" hubName o.stewardHubId}
</td>
<td class="px-4 py-3">
<span class={typeBadge o.ownershipType <> " text-xs px-2 py-0.5 rounded font-medium"}>
{o.ownershipType}
</span>
</td>
<td class="px-4 py-3 text-xs text-gray-400">{show o.effectiveFrom}</td>
<td class="px-4 py-3 text-right">
<a href={ShowWidgetOwnershipAction { widgetOwnershipId = o.id }}
class="text-xs text-blue-600 hover:underline">View</a>
</td>
</tr>
|]
typeBadge :: Text -> Text
typeBadge t = case t of
"local" -> "bg-gray-100 text-gray-700"
"delegated" -> "bg-blue-100 text-blue-700"
"global" -> "bg-purple-100 text-purple-700"
_ -> "bg-gray-100 text-gray-600"