feat(help-center): add help panel to sidebar icon bar

New help-center module adds a question-mark icon to the sidebar with
an aside panel containing three sections: Documentation (permission-
gated links to docs viewer and API docs), Keyboard Shortcuts (inline
reference from HotkeyService with Mac/Win variants), and About
(application info). Visible to all logged-in users — individual
resource links respect existing permissions.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-26 14:19:08 +01:00
parent 02a7d2fe90
commit d068efa65c
7 changed files with 269 additions and 1 deletions

View File

@@ -12,5 +12,5 @@
* Each entry must match a directory name under modules/ containing a module.php manifest.
*/
return [
'enabled_modules' => ['audit', 'addressbook', 'bookmarks', 'notifications', 'api-docs'],
'enabled_modules' => ['audit', 'addressbook', 'bookmarks', 'notifications', 'api-docs', 'help-center'],
];

View File

@@ -0,0 +1,16 @@
{
"Help": "Hilfe",
"Documentation": "Dokumentation",
"Keyboard shortcuts": "Tastenkuerzel",
"Action": "Aktion",
"Shortcut": "Kuerzel",
"About": "Info",
"Application": "Anwendung",
"CoreCore Admin": "CoreCore Admin",
"Open search": "Suche oeffnen",
"Save in edit pages": "Speichern auf Bearbeitungsseiten",
"Toggle sidebar": "Seitenleiste umschalten",
"Switch sidebar section": "Seitenleisten-Bereich wechseln",
"Back": "Zurueck",
"Forward": "Vorwaerts"
}

View File

@@ -0,0 +1,16 @@
{
"Help": "Help",
"Documentation": "Documentation",
"Keyboard shortcuts": "Keyboard shortcuts",
"Action": "Action",
"Shortcut": "Shortcut",
"About": "About",
"Application": "Application",
"CoreCore Admin": "CoreCore Admin",
"Open search": "Open search",
"Save in edit pages": "Save in edit pages",
"Toggle sidebar": "Toggle sidebar",
"Switch sidebar section": "Switch sidebar section",
"Back": "Back",
"Forward": "Forward"
}

View File

@@ -0,0 +1,14 @@
<?php
namespace MintyPHP\Module\HelpCenter;
use MintyPHP\App\AppContainer;
use MintyPHP\App\Container\ContainerRegistrar;
final class HelpCenterContainerRegistrar implements ContainerRegistrar
{
public function register(AppContainer $container): void
{
// No services to register — help panel uses static utility classes only.
}
}

View File

@@ -0,0 +1,54 @@
<?php
/**
* Help Center module manifest.
*
* Adds a help icon to the sidebar with documentation links,
* keyboard shortcuts reference, and system information.
*/
return [
'id' => 'help-center',
'version' => '1.0.0',
'enabled_by_default' => true,
'load_order' => 30,
'requires' => [],
'routes' => [],
'public_paths' => [],
'container_registrars' => [
\MintyPHP\Module\HelpCenter\HelpCenterContainerRegistrar::class,
],
'ui_slots' => [
'aside.tab_panel' => [
[
'key' => 'help',
'label' => 'Help',
'icon' => 'bi-question-circle',
'href' => '',
'permission' => '',
'panel_template' => 'templates/aside-help-panel.phtml',
'order' => 900,
],
],
'layout.head_style' => [
[
'key' => 'help-center-style',
'path' => 'modules/help-center/css/components/app-help-panel.css',
'order' => 200,
],
],
],
'authorization_policies' => [],
'search_resources' => [],
'asset_groups' => [],
'scheduler_jobs' => [],
'layout_context_providers' => [],
'session_providers' => [],
'permissions' => [],
'event_listeners' => [],
'deactivation_handler' => null,
'migrations_path' => null,
'i18n_path' => 'i18n',
];

View File

@@ -0,0 +1,65 @@
<?php
use MintyPHP\Service\Docs\DocsCatalogService;
use MintyPHP\Service\Ui\HotkeyService;
$layoutAuth = is_array($viewAuth['layout'] ?? null) ? $viewAuth['layout'] : [];
$canViewDocs = !empty($layoutAuth['can_view_docs']);
$canViewApiDocs = !empty($layoutAuth['api_docs.view']);
$hasDocumentation = $canViewDocs || $canViewApiDocs;
$docsDefaultSlug = $canViewDocs ? DocsCatalogService::defaultSlug() : '';
$docsUrl = $docsDefaultSlug !== '' ? lurl('admin/docs/' . $docsDefaultSlug) : '';
$apiDocsUrl = $canViewApiDocs ? lurl('admin/api-docs') : '';
$isMac = str_contains(strtolower((string) ($_SERVER['HTTP_USER_AGENT'] ?? '')), 'mac');
$hotkeys = HotkeyService::list();
?>
<div class="app-help-panel">
<?php if ($hasDocumentation): ?>
<div class="app-sidebar-group">
<h3 class="app-sidebar-title"><?php e(t('Documentation')); ?></h3>
<ul>
<?php if ($canViewDocs && $docsUrl !== ''): ?>
<li><a href="<?php e($docsUrl); ?>"><i class="bi bi-book"></i> <?php e(t('Documentation')); ?></a></li>
<?php endif; ?>
<?php if ($canViewApiDocs): ?>
<li><a href="<?php e($apiDocsUrl); ?>"><i class="bi bi-braces"></i> <?php e(t('API docs')); ?></a></li>
<?php endif; ?>
</ul>
</div>
<?php endif; ?>
<div class="app-sidebar-group">
<h3 class="app-sidebar-title"><?php e(t('Keyboard shortcuts')); ?></h3>
<table class="app-help-shortcuts-table" role="grid">
<thead>
<tr>
<th scope="col"><?php e(t('Action')); ?></th>
<th scope="col"><?php e(t('Shortcut')); ?></th>
</tr>
</thead>
<tbody>
<?php foreach ($hotkeys as $hotkey):
$shortcut = $isMac ? ($hotkey['mac'] ?? '') : ($hotkey['win'] ?? '');
$action = t($hotkey['action_key'] ?? '');
?>
<tr>
<td><?php e($action); ?></td>
<td><kbd><?php e($shortcut); ?></kbd></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<div class="app-sidebar-group">
<h3 class="app-sidebar-title"><?php e(t('About')); ?></h3>
<ul>
<li class="app-help-about-item">
<span class="app-help-about-label"><?php e(t('Application')); ?></span>
<span class="app-help-about-value"><?php e(t('CoreCore Admin')); ?></span>
</li>
</ul>
</div>
</div>

View File

@@ -0,0 +1,103 @@
@layer components {
.app-help-panel {
padding: var(--app-spacing);
}
.app-help-panel .app-sidebar-title {
text-transform: uppercase;
font-size: var(--app-text-2xs);
font-weight: 600;
color: var(--app-muted-color);
margin: 0 0 0.5rem;
letter-spacing: 0.03em;
}
.app-help-panel .app-sidebar-group {
margin-bottom: calc(var(--app-spacing) * 1.5);
}
.app-help-panel .app-sidebar-group ul {
list-style: none;
margin: 0;
padding: 0;
}
.app-help-panel .app-sidebar-group ul li a {
display: flex;
align-items: center;
gap: 0.5rem;
padding: 0.35rem 0.5rem;
border-radius: var(--app-border-radius);
color: var(--app-color);
text-decoration: none;
font-size: var(--app-text-sm);
transition: background-color 0.15s;
}
.app-help-panel .app-sidebar-group ul li a:hover {
background-color: var(--app-accordion-border-color);
}
.app-help-panel .app-sidebar-group ul li a i {
font-size: 1rem;
opacity: 0.65;
}
.app-help-shortcuts-table {
width: 100%;
border-collapse: collapse;
font-size: var(--app-text-sm);
}
.app-help-shortcuts-table th,
.app-help-shortcuts-table td {
padding: 0.3rem 0.5rem;
text-align: left;
border: none;
background: transparent;
}
.app-help-shortcuts-table th {
font-weight: 600;
color: var(--app-muted-color);
font-size: var(--app-text-2xs);
text-transform: uppercase;
letter-spacing: 0.03em;
border-bottom: 1px solid var(--app-border);
}
.app-help-shortcuts-table td {
border-bottom: 1px solid var(--app-border);
}
.app-help-shortcuts-table tr:last-child td {
border-bottom: none;
}
.app-help-shortcuts-table kbd {
display: inline-block;
padding: 0.1rem 0.35rem;
font-size: var(--app-text-xs);
font-family: var(--app-font-family-mono, monospace);
background: var(--app-card-background-color);
border: 1px solid var(--app-border);
border-radius: 3px;
box-shadow: 0 1px 0 var(--app-border);
white-space: nowrap;
}
.app-help-about-item {
display: flex;
justify-content: space-between;
padding: 0.35rem 0.5rem;
font-size: var(--app-text-sm);
}
.app-help-about-label {
color: var(--app-muted-color);
}
.app-help-about-value {
font-weight: 500;
}
}