182 lines
7.4 KiB
PHP

<?php
URL::$route['page-title'] = 'Workspace';
$section = trim((string)(URL::$route['param'] ?? 'overview'));
if ($section === '') $section = 'overview';
$sections = [
'overview' => [
'title' => 'Workspace overview',
'subtitle' => 'A generic app-shell pattern for tools, admin consoles, and internal products.',
'status' => ['label' => 'Ready', 'variant' => 'success'],
'description' => 'This slice comes from the uh-ai portal app: a reusable workspace shell with sidebar navigation, compact mobile controls, and semantic panel primitives.',
'highlights' => [
['title' => 'Shell layout', 'text' => 'Sidebar plus main panel, responsive overlay behavior, and a compact mobile header.'],
['title' => 'Semantic primitives', 'text' => 'Panels, section heads, status pills, list states, and empty-state placeholders.'],
['title' => 'Starter-friendly', 'text' => 'Backported against the starter theme variables instead of portal-specific branding tokens.'],
],
],
'projects' => [
'title' => 'Projects queue',
'subtitle' => 'Nested path fallback lets one controller serve multiple panes cleanly.',
'status' => ['label' => '3 active', 'variant' => 'info'],
'description' => 'This page is served from views/workspace/index.php, while the route remains /workspace/projects. That path fallback was backported from uh-ai as part of this slice.',
'highlights' => [
['title' => 'Content review', 'text' => 'Design a shared docs/workspace explorer for internal tools.'],
['title' => 'Component extraction', 'text' => 'Promote shell primitives into stable starter components.'],
['title' => 'Routing cleanup', 'text' => 'Use nested routes without multiplying single-file views.'],
],
],
'activity' => [
'title' => 'Recent activity',
'subtitle' => 'Sidebar-first tools often need a live or recent-events pane.',
'status' => ['label' => 'Monitoring', 'variant' => 'warn'],
'description' => 'The workspace shell is a better fit than the marketing-style home page when the app is navigation-heavy and stateful.',
'highlights' => [
['title' => 'Deploy preview', 'text' => 'Workspace layout validated on desktop and mobile widths.'],
['title' => 'Shell behavior', 'text' => 'Sidebar toggle is abstracted in js/u-workspace-shell.js.'],
['title' => 'Surface consistency', 'text' => 'All blocks inherit existing starter color and radius tokens.'],
],
],
];
if (!isset($sections[$section])) {
$section = 'overview';
}
$current = $sections[$section];
$navItems = [
['key' => 'overview', 'label' => 'Overview', 'icon' => 'fas fa-compass', 'meta' => 'Shell'],
['key' => 'projects', 'label' => 'Projects', 'icon' => 'fas fa-folder-tree', 'meta' => 'Routes'],
['key' => 'activity', 'label' => 'Activity', 'icon' => 'fas fa-wave-square', 'meta' => 'State'],
];
ob_start();
?>
<div class="ws-nav-group-label">Workspace areas</div>
<div class="ws-nav-list">
<?php foreach ($navItems as $item): ?>
<?php $active = $item['key'] === $section; ?>
<a class="ws-nav-item<?= $active ? ' is-active' : '' ?>" href="<?= URL::link('workspace/' . $item['key']) ?>">
<span class="ws-nav-item-inner">
<span class="ws-nav-icon"><i class="<?= asafe($item['icon']) ?>"></i></span>
<span class="ws-nav-item-text">
<span class="ws-nav-title"><?= safe($item['label']) ?></span>
<span class="ws-nav-meta"><?= safe($item['meta']) ?></span>
</span>
</span>
</a>
<?php endforeach; ?>
</div>
<div class="ws-demo-sidebar-copy">
<p>This sidebar and mobile shell pattern was extracted from the AI portal app and normalized against starter theme tokens.</p>
</div>
<?= component('components/workspace/list-state', [
'icon_class' => 'fas fa-circle-info',
'text' => 'Use /workspace/overview, /workspace/projects, or /workspace/activity',
]) ?>
<?php
$sidebarBody = ob_get_clean();
$sidebarTop = component('components/workspace/sidebar-toolbar', [
'action_html' => '<a class="ws-sidebar-action-btn" href="' . asafe(URL::link('workspace/overview')) . '"><i class="fas fa-grid-2"></i><span>Open shell</span></a>',
'search_input_id' => 'workspace-demo-search',
'search_input_name' => 'workspace_demo_search',
'search_placeholder' => 'Search workspace sections',
]);
$sidebar = component('components/workspace/sidebar-shell', [
'id' => 'workspace-demo-sidebar',
'top_html' => $sidebarTop,
'body_html' => $sidebarBody,
]);
$mobileBar = component('components/workspace/mobile-bar', [
'button_id' => 'workspace-demo-toggle',
'title' => 'Starter Workspace',
]);
$header = component('components/workspace/panel-header', [
'title' => $current['title'],
'subtitle' => $current['subtitle'],
'actions_html' => component('components/workspace/status-pill', [
'label' => $current['status']['label'],
'variant' => $current['status']['variant'],
]),
]);
$stats = [
['label' => 'Sidebar pattern', 'value' => 'Responsive', 'meta' => 'Overlay on mobile'],
['label' => 'Routing mode', 'value' => 'Nested', 'meta' => 'Parent-path fallback'],
['label' => 'Source app', 'value' => 'uh-ai', 'meta' => 'Portal shell'],
];
ob_start();
?>
<div class="ws-stat-grid">
<?php foreach ($stats as $stat): ?>
<div class="ws-stat-card">
<div class="ws-stat-card-label"><?= safe($stat['label']) ?></div>
<div class="ws-stat-card-value"><?= safe($stat['value']) ?></div>
<div class="ws-stat-card-meta"><?= safe($stat['meta']) ?></div>
</div>
<?php endforeach; ?>
</div>
<?php
$statsHtml = ob_get_clean();
ob_start();
?>
<div class="ws-detail-grid">
<?php foreach ($current['highlights'] as $highlight): ?>
<div class="ws-detail-card">
<h4><?= safe($highlight['title']) ?></h4>
<p><?= safe($highlight['text']) ?></p>
</div>
<?php endforeach; ?>
</div>
<?php
$detailHtml = ob_get_clean();
$mainBody =
component('components/workspace/section', [
'header_html' => component('components/workspace/section-head', ['title' => 'Why this belongs in the starter']),
'body_html' => '<p class="ws-section-copy">' . safe($current['description']) . '</p>' . $statsHtml,
]) .
component('components/workspace/section', [
'header_html' => component('components/workspace/section-head', ['title' => 'Starter demo content']),
'body_html' => $detailHtml . '<p class="ws-inline-note">Try visiting <strong>' . safe(URL::link('workspace/projects')) . '</strong> or <strong>' . safe(URL::link('workspace/activity')) . '</strong> to see the nested route fallback in action.</p>',
]);
if ($section === 'activity') {
$mainBody .= component('components/workspace/empty-state', [
'icon_class' => 'fas fa-clock-rotate-left',
'title' => 'No live stream wired yet',
'text' => 'The shell is generic. Add your own websocket, polling, or event-driven runtime behind it when a real product needs one.',
'action_html' => '<a class="ws-primary-btn" href="' . asafe(URL::link('dashboard')) . '">Open dashboard demo</a>',
]);
}
$main = $mobileBar . component('components/workspace/panel', [
'header_html' => $header,
'body_html' => $mainBody,
]);
echo component('components/workspace/app-frame', [
'id' => 'workspace-demo-shell',
'overlay_id' => 'workspace-demo-overlay',
'sidebar_html' => $sidebar,
'main_html' => $main,
]);
?>
<script>
(function () {
if (typeof UWorkspaceShell === 'undefined') return;
UWorkspaceShell.init({
sidebarId: 'workspace-demo-sidebar',
overlayId: 'workspace-demo-overlay',
toggleButtonId: 'workspace-demo-toggle'
});
}());
</script>