Site Details: WP/PHP/Overview tabs use template's toggle-row pattern + file tree folder toggle

WP tab now matches template verbatim:
- wp-config.php card with set-grid of fields, MB/sek suffix input-groups,
  5 template-exact toggle-rows (WP_DEBUG, WP_CACHE, DISALLOW_FILE_EDIT,
  AUTOMATIC_UPDATER_DISABLED, FORCE_SSL_ADMIN) with title + description.
- Datenbank card with DB_NAME/USER/HOST/PASSWORD fields + 'verbunden' pill.
- Sicherheits-Schlüssel (Salts) card with kv rows + warning regenerate btn.
- Allgemeine WordPress-Einstellungen card with 4 fields + 2 toggles.

PHP tab now matches template verbatim:
- php.ini card with 8 fields (4 with M/sek suffix groups), 4 toggle-rows
  (display_errors, opcache.enable, log_errors, allow_url_fopen).
- OPcache card with kv stats + leeren btn.
- Extensions card with pill list + '+ 16' accent pill.

Overview Schnellaktionen card converted to clu-toggle-row pattern with
title + description + clu-toggle switch (template's pattern, not the
checkbox-style).

File tree folders now toggle:
- New Volt openDirs array tracks expanded paths.
- toggleDir(path) Livewire action adds/removes path.
- clu_render_tree($items, $activeFile, $openDirs, $base) walks the tree,
  derives full path per node, emits wire:click=toggleDir on dirs.

CSS additions: clu-toggle-row, clu-tr-body/title/desc, clu-toggle (iOS-style
switch with .on state), clu-input-group with suffix, clu-kv, clu-field,
clu-form-actions, clu-hico.
master
Boban Blaskovic 2026-05-31 23:57:41 +02:00
parent 4d4e2cbc33
commit 933f641e58
2 changed files with 256 additions and 66 deletions

View File

@ -1350,6 +1350,100 @@
.clu-section-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; }
.clu-section-head h3 { margin: 0; font-size: 14px; font-weight: 600; letter-spacing: -0.005em; }
/* Toggle row (template's <div class="toggle-row">) */
.clu-toggle-row {
display: flex; align-items: center; justify-content: space-between; gap: 14px;
padding: 11px 0;
border-bottom: 1px solid var(--color-hairline);
}
.clu-toggle-row:last-child { border-bottom: 0; }
.clu-tr-body { flex: 1; min-width: 0; }
.clu-tr-title { font-size: 13px; font-weight: 600; color: var(--color-fg); }
.clu-tr-desc { font-size: 11.5px; color: var(--color-muted); margin-top: 2px; line-height: 1.4; }
/* Pure switch toggle (no checkbox) */
.clu-toggle {
position: relative; flex: none;
width: 36px; height: 20px;
background: rgba(30,35,60,0.20);
border-radius: 999px;
cursor: pointer;
transition: background .18s;
}
.clu-toggle::after {
content: "";
position: absolute;
top: 2px; left: 2px;
width: 16px; height: 16px;
border-radius: 50%;
background: #fff;
box-shadow: 0 1px 3px rgba(0,0,0,0.25);
transition: transform .18s;
}
.clu-toggle.on { background: var(--color-accent); }
.clu-toggle.on::after { transform: translateX(16px); }
/* Input-group with suffix (numeric unit) */
.clu-input-group {
display: flex; align-items: center;
border: 1px solid var(--color-hairline);
border-radius: var(--radius-xs);
background: rgba(255,255,255,0.5);
height: 42px;
overflow: hidden;
box-shadow: inset 0 1px 0 rgba(255,255,255,0.7);
}
.clu-input-group:focus-within { border-color: rgba(59,111,242,0.5); background: rgba(255,255,255,0.85); box-shadow: 0 0 0 3px rgba(59,111,242,0.12); }
.clu-input-group input {
flex: 1; border: 0; outline: 0;
background: none;
padding: 0 13px;
font-family: var(--font-mono);
font-size: 13px;
color: var(--color-fg);
height: 100%;
min-width: 0;
}
.clu-input-group .suffix {
padding: 0 13px;
border-left: 1px solid var(--color-hairline);
color: var(--color-muted-2);
font-family: var(--font-mono);
font-size: 11.5px;
background: rgba(255,255,255,0.35);
height: 100%;
display: flex; align-items: center;
}
/* Monospace input variant */
.clu-input.mono input { font-family: var(--font-mono); }
/* Key-value row */
.clu-kv {
display: flex; align-items: center; justify-content: space-between; gap: 12px;
padding: 10px 0;
border-bottom: 1px solid var(--color-hairline);
font-size: 12.5px;
}
.clu-kv:last-child { border-bottom: 0; }
.clu-kv .k { color: var(--color-muted); }
.clu-kv .v { color: var(--color-fg); font-family: var(--font-mono); }
/* Field wrapper */
.clu-field { display: flex; flex-direction: column; gap: 6px; margin-bottom: 14px; }
.clu-field label { font-size: 11.5px; font-weight: 600; color: var(--color-muted); text-transform: uppercase; letter-spacing: 0.04em; }
.clu-field:last-child { margin-bottom: 0; }
/* Form actions row */
.clu-form-actions {
display: flex; align-items: center; justify-content: flex-end; gap: 8px;
padding: 12px 18px;
border-top: 1px solid var(--color-hairline);
}
/* Hico (card-head icon) */
.clu-hico { color: var(--color-muted); flex: none; }
/* File tree */
.clu-file-tree {
font-family: var(--font-mono);

View File

@ -15,6 +15,9 @@ new #[Layout('layouts.app')] class extends Component {
public bool $logFollow = true;
public string $currentLog = 'access';
/** Set of dir paths currently expanded in file tree. */
public array $openDirs = ['htdocs', 'htdocs/wp-content', 'htdocs/wp-content/themes', 'htdocs/wp-content/themes/storefront-child', 'logs'];
public function mount(Site $site): void
{
$this->site = $site->load(['server', 'certificate', 'crons']);
@ -26,6 +29,15 @@ new #[Layout('layouts.app')] class extends Component {
public function setLog(string $type): void { $this->currentLog = $type; }
public function toggleDir(string $path): void
{
if (in_array($path, $this->openDirs, true)) {
$this->openDirs = array_values(array_diff($this->openDirs, [$path]));
} else {
$this->openDirs[] = $path;
}
}
public function toggleMaintenance(): void
{
$this->site->maintenance_mode = ! $this->site->maintenance_mode;
@ -245,16 +257,19 @@ new #[Layout('layouts.app')] class extends Component {
}; ?>
@php
/** Recursive file tree renderer. */
/** Recursive file tree renderer (folder toggle via Livewire). */
if (! function_exists('clu_render_tree')) {
function clu_render_tree(array $items, string $activeFile): string {
function clu_render_tree(array $items, string $activeFile, array $openDirs, string $base = ''): string {
$out = '';
foreach ($items as $node) {
$isDir = ($node['type'] ?? '') === 'dir';
$isOpen = $node['open'] ?? false;
$isDir = ($node['type'] ?? '') === 'dir';
$myPath = ltrim($base . '/' . $node['name'], '/');
$isOpen = $isDir && in_array($myPath, $openDirs, true);
$isActive = ! $isDir && ($node['path'] ?? null) === $activeFile;
$rowClass = 'clu-tree-item' . ($isDir ? ' dir' : '') . ($isOpen ? ' open' : '') . ($isActive ? ' active' : '');
$click = $isDir ? '' : 'wire:click="setActiveFile(\''.addslashes($node['path']).'\')" wire:navigate.hover';
$click = $isDir
? 'wire:click="toggleDir(\''.addslashes($myPath).'\')"'
: 'wire:click="setActiveFile(\''.addslashes($node['path']).'\')"';
$ico = $isDir
? '<svg class="ti-ico" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9"><path d="M3 7a2 2 0 0 1 2-2h4l2 2h8a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/></svg>'
: '<svg class="ti-ico" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><path d="M14 2v6h6"/></svg>';
@ -264,7 +279,7 @@ new #[Layout('layouts.app')] class extends Component {
$out .= '<div class="'.$rowClass.'" '.$click.'>'.$chev.$ico.'<span>'.htmlspecialchars($node['name']).'</span></div>';
if ($isDir && ! empty($node['children'])) {
$hidden = $isOpen ? '' : ' collapsed';
$out .= '<div class="clu-tree-children'.$hidden.'">'.clu_render_tree($node['children'], $activeFile).'</div>';
$out .= '<div class="clu-tree-children'.$hidden.'">'.clu_render_tree($node['children'], $activeFile, $openDirs, $myPath).'</div>';
}
}
return $out;
@ -343,27 +358,23 @@ new #[Layout('layouts.app')] class extends Component {
</div>
<div class="clu-card">
<div class="clu-card-head"><h3>Schnellaktionen</h3></div>
<div class="clu-card-body" style="display:flex;flex-direction:column;gap:12px;">
<label class="clu-checkbox" style="justify-content:space-between;width:100%;">
<span>Maintenance Mode</span>
<input type="checkbox" wire:click="toggleMaintenance" @checked($site->maintenance_mode) />
<span class="box"><svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3.5"><path d="M5 12l5 5L20 7"/></svg></span>
</label>
<label class="clu-checkbox" style="justify-content:space-between;width:100%;">
<span>Page Cache</span>
<input type="checkbox" wire:click="toggleCache" @checked($site->cache_enabled) />
<span class="box"><svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3.5"><path d="M5 12l5 5L20 7"/></svg></span>
</label>
<label class="clu-checkbox" style="justify-content:space-between;width:100%;">
<span>CDN</span>
<input type="checkbox" wire:click="toggleCdn" @checked($site->cdn_enabled) />
<span class="box"><svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3.5"><path d="M5 12l5 5L20 7"/></svg></span>
</label>
<label class="clu-checkbox" style="justify-content:space-between;width:100%;">
<span>HTTPS erzwingen</span>
<input type="checkbox" @checked($site->https_enabled) />
<span class="box"><svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3.5"><path d="M5 12l5 5L20 7"/></svg></span>
</label>
<div class="clu-card-body">
<div class="clu-toggle-row" wire:click="toggleMaintenance" style="cursor:pointer;">
<div class="clu-tr-body"><div class="clu-tr-title">Wartungsmodus</div><div class="clu-tr-desc">site für besucher sperren</div></div>
<span class="clu-toggle @if($site->maintenance_mode) on @endif"></span>
</div>
<div class="clu-toggle-row" wire:click="toggleCache" style="cursor:pointer;">
<div class="clu-tr-body"><div class="clu-tr-title">Objekt-Cache (Redis)</div><div class="clu-tr-desc">datenbankabfragen zwischenspeichern</div></div>
<span class="clu-toggle @if($site->cache_enabled) on @endif"></span>
</div>
<div class="clu-toggle-row" wire:click="toggleCdn" style="cursor:pointer;">
<div class="clu-tr-body"><div class="clu-tr-title">Edge-CDN</div><div class="clu-tr-desc">statische inhalte global cachen</div></div>
<span class="clu-toggle @if($site->cdn_enabled) on @endif"></span>
</div>
<div class="clu-toggle-row" style="cursor:pointer;">
<div class="clu-tr-body"><div class="clu-tr-title">HTTPS erzwingen</div><div class="clu-tr-desc">alle anfragen auf ssl umleiten</div></div>
<span class="clu-toggle @if($site->https_enabled) on @endif"></span>
</div>
</div>
</div>
</div>
@ -386,7 +397,7 @@ new #[Layout('layouts.app')] class extends Component {
<span class="path">/var/www/bergmann</span>
<button type="button" class="clu-icon-btn" aria-label="Refresh"><svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M3 12a9 9 0 1 0 9-9"/><path d="M3 4v5h5"/></svg></button>
</div>
{!! clu_render_tree($this->fileTree, $activeFile) !!}
{!! clu_render_tree($this->fileTree, $activeFile, $openDirs) !!}
</div>
<div class="clu-card clu-editor" wire:key="editor-{{ $activeFile }}">
<div class="clu-editor-head">
@ -523,60 +534,145 @@ new #[Layout('layouts.app')] class extends Component {
{{-- ────── WP + PHP (kompakte Hardcoded-Konfig nach Template) ────── --}}
@if ($tab === 'wp')
<div class="clu-pane">
{{-- wp-config.php card --}}
<div class="clu-card">
<div class="clu-card-head"><h3>wp-config.php</h3><span class="meta">WordPress core</span></div>
<div class="clu-card-body" style="display:flex;flex-direction:column;gap:14px;">
<div class="clu-set-grid" style="row-gap:14px;">
@foreach (['Tabellen-Präfix' => 'wp_bg_', 'WP_MEMORY_LIMIT' => '256M', 'AUTOSAVE_INTERVAL' => '60', 'WP_POST_REVISIONS' => '10', 'WP_DEBUG_LOG' => '/var/www/bergmann/logs/wp-debug.log', 'WP_HOME' => 'https://shop.bergmann-coffee.com'] as $label => $val)
<div class="flex flex-col gap-[6px]"><label class="text-[11.5px] text-(--color-muted) font-semibold">{{ $label }}</label><div class="clu-input"><input value="{{ $val }}" type="text" /></div></div>
@endforeach
<div class="clu-card-head">
<h3 class="flex items-center gap-[8px]">
<svg class="clu-hico" width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M4 7h16M4 12h16M4 17h10"/></svg>
wp-config.php
</h3>
<span class="meta">/htdocs/wp-config.php</span>
</div>
<div class="clu-card-body">
<div class="clu-set-grid">
<div class="clu-field"><label>Tabellen-Präfix</label><div class="clu-input"><input value="wp_bg_" style="font-family:var(--font-mono);" type="text" /></div></div>
<div class="clu-field"><label>WP_MEMORY_LIMIT</label><div class="clu-input-group"><input value="256" type="text" /><span class="suffix">MB</span></div></div>
<div class="clu-field"><label>WP_MAX_MEMORY_LIMIT</label><div class="clu-input-group"><input value="512" type="text" /><span class="suffix">MB</span></div></div>
<div class="clu-field"><label>AUTOSAVE_INTERVAL</label><div class="clu-input-group"><input value="60" type="text" /><span class="suffix">sek</span></div></div>
<div class="clu-field"><label>WP_POST_REVISIONS</label><div class="clu-input"><input value="10" style="font-family:var(--font-mono);" type="text" /></div></div>
<div class="clu-field"><label>Sprache (WPLANG)</label><div class="clu-input"><input value="de_DE" style="font-family:var(--font-mono);" type="text" /></div></div>
</div>
@foreach (['WP_DEBUG' => false, 'WP_CACHE' => true, 'DISALLOW_FILE_EDIT' => true, 'FORCE_SSL_ADMIN' => true, 'AUTOMATIC_UPDATER_DISABLED' => false] as $k => $v)
<label class="clu-checkbox" style="justify-content:space-between;width:100%;"><span>{{ $k }}</span><input type="checkbox" @checked($v) /><span class="box"><svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3.5"><path d="M5 12l5 5L20 7"/></svg></span></label>
@endforeach
<div style="margin-top:8px;">
<div class="clu-toggle-row"><div class="clu-tr-body"><div class="clu-tr-title">WP_DEBUG</div><div class="clu-tr-desc">debug-modus &amp; fehlerprotokoll</div></div><span class="clu-toggle"></span></div>
<div class="clu-toggle-row"><div class="clu-tr-body"><div class="clu-tr-title">WP_CACHE</div><div class="clu-tr-desc">persistentes caching aktiv</div></div><span class="clu-toggle on"></span></div>
<div class="clu-toggle-row"><div class="clu-tr-body"><div class="clu-tr-title">DISALLOW_FILE_EDIT</div><div class="clu-tr-desc">datei-editor im wp-admin sperren</div></div><span class="clu-toggle on"></span></div>
<div class="clu-toggle-row"><div class="clu-tr-body"><div class="clu-tr-title">AUTOMATIC_UPDATER_DISABLED</div><div class="clu-tr-desc">automatische core-updates deaktiviert</div></div><span class="clu-toggle"></span></div>
<div class="clu-toggle-row"><div class="clu-tr-body"><div class="clu-tr-title">FORCE_SSL_ADMIN</div><div class="clu-tr-desc">wp-admin nur über https</div></div><span class="clu-toggle on"></span></div>
</div>
</div>
<div class="clu-form-actions">
<button class="clu-ghost-btn" type="button">In Editor öffnen</button>
<button class="clu-btn-primary" type="button" style="height:36px;padding:0 14px;">wp-config speichern</button>
</div>
</div>
{{-- Datenbank + Salts side-by-side --}}
<div class="grid gap-[14px]" style="grid-template-columns:1fr 1fr;">
<div class="clu-card"><div class="clu-card-head"><h3>Datenbank</h3></div><div class="clu-card-body" style="display:flex;flex-direction:column;gap:10px;">
@foreach (['DB_NAME' => 'bergmann_prod', 'DB_USER' => 'bergmann_wp', 'DB_HOST' => '127.0.0.1:3306', 'DB_CHARSET' => 'utf8mb4'] as $k => $v)
<div class="flex flex-col gap-[6px]"><label class="text-[11.5px] text-(--color-muted) font-semibold">{{ $k }}</label><div class="clu-input"><input value="{{ $v }}" type="text" /></div></div>
@endforeach
</div></div>
<div class="clu-card"><div class="clu-card-head"><h3>Security Keys</h3></div><div class="clu-card-body" style="display:flex;flex-direction:column;gap:10px;">
<div class="flex justify-between text-[12.5px]"><span class="text-(--color-muted)">AUTH_KEY</span><span class="font-mono">env</span></div>
<div class="flex justify-between text-[12.5px]"><span class="text-(--color-muted)">SECURE_AUTH_KEY</span><span class="font-mono">env</span></div>
<div class="flex justify-between text-[12.5px]"><span class="text-(--color-muted)">Rotation</span><span class="font-mono">vor 11 Wochen</span></div>
<button class="clu-btn-primary" type="button" style="height:34px;padding:0 14px;align-self:flex-start;">Schlüssel rotieren</button>
</div></div>
<div class="clu-card">
<div class="clu-card-head"><h3>Datenbank</h3><span class="clu-pill success">verbunden</span></div>
<div class="clu-card-body">
<div class="clu-field"><label>DB_NAME</label><div class="clu-input"><input value="bergmann_prod" style="font-family:var(--font-mono);" type="text" /></div></div>
<div class="clu-field"><label>DB_USER</label><div class="clu-input"><input value="bergmann_wp" style="font-family:var(--font-mono);" type="text" /></div></div>
<div class="clu-field"><label>DB_HOST</label><div class="clu-input"><input value="127.0.0.1:3306" style="font-family:var(--font-mono);" type="text" /></div></div>
<div class="clu-field"><label>DB_PASSWORD</label><div class="clu-input-group"><input value="************" type="password" /><span class="suffix" style="cursor:pointer;">anzeigen</span></div></div>
</div>
</div>
<div class="clu-card">
<div class="clu-card-head"><h3>Sicherheits-Schlüssel (Salts)</h3></div>
<div class="clu-card-body">
<p class="clu-tr-desc" style="margin:0 0 14px;">AUTH_KEY, SECURE_AUTH_KEY, LOGGED_IN_KEY, NONCE_KEY &amp; zugehörige Salts. Neu generieren meldet alle Benutzer ab.</p>
<div class="clu-kv"><span class="k">Zuletzt rotiert</span><span class="v">14.03.2026</span></div>
<div class="clu-kv"><span class="k">Schlüssel-Länge</span><span class="v">64 zeichen</span></div>
<div class="clu-kv" style="border-bottom:0;"><span class="k">Status</span><span class="v" style="color:var(--color-success);">stark</span></div>
</div>
<div class="clu-form-actions">
<button class="clu-ghost-btn" type="button" style="color:var(--color-warning);border-color:rgba(194,130,15,0.35);">Salts neu generieren</button>
</div>
</div>
</div>
{{-- Allgemeine WP-Einstellungen --}}
<div class="clu-card">
<div class="clu-card-head"><h3>Allgemeine WordPress-Einstellungen</h3></div>
<div class="clu-card-body">
<div class="clu-set-grid">
<div class="clu-field"><label>Seitentitel</label><div class="clu-input"><input value="Bergmann Coffee Shop" type="text" /></div></div>
<div class="clu-field"><label>Untertitel</label><div class="clu-input"><input value="Rösterei &amp; Onlineshop" type="text" /></div></div>
<div class="clu-field"><label>WordPress-Adresse (URL)</label><div class="clu-input"><input value="https://{{ $site->domain }}" style="font-family:var(--font-mono);" type="text" /></div></div>
<div class="clu-field"><label>Permalink-Struktur</label><div class="clu-input"><input value="/%postname%/" style="font-family:var(--font-mono);" type="text" /></div></div>
</div>
<div style="margin-top:8px;">
<div class="clu-toggle-row"><div class="clu-tr-body"><div class="clu-tr-title">Suchmaschinen-Sichtbarkeit</div><div class="clu-tr-desc">von google &amp; co. indexieren lassen</div></div><span class="clu-toggle on"></span></div>
<div class="clu-toggle-row"><div class="clu-tr-body"><div class="clu-tr-title">Registrierung erlauben</div><div class="clu-tr-desc">neue benutzer können sich registrieren</div></div><span class="clu-toggle"></span></div>
</div>
</div>
<div class="clu-form-actions">
<button class="clu-btn-primary" type="button" style="height:36px;padding:0 14px;">Einstellungen speichern</button>
</div>
</div>
</div>
@endif
@if ($tab === 'php')
<div class="clu-pane">
{{-- php.ini main card --}}
<div class="clu-card">
<div class="clu-card-head"><h3>php.ini · PHP {{ $site->php_version }}</h3></div>
<div class="clu-card-body" style="display:flex;flex-direction:column;gap:14px;">
<div class="clu-set-grid" style="row-gap:14px;">
@foreach (['memory_limit' => '256M', 'max_execution_time' => '60', 'max_input_time' => '60', 'upload_max_filesize' => '64M', 'post_max_size' => '64M', 'date.timezone' => 'Europe/Berlin', 'opcache.memory_consumption' => '256', 'opcache.max_accelerated_files' => '20000'] as $k => $v)
<div class="flex flex-col gap-[6px]"><label class="text-[11.5px] text-(--color-muted) font-semibold">{{ $k }}</label><div class="clu-input"><input value="{{ $v }}" type="text" /></div></div>
@endforeach
<div class="clu-card-head">
<h3 class="flex items-center gap-[8px]">
<svg class="clu-hico" width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M4 7h16M4 12h16M4 17h10"/></svg>
php.ini
</h3>
<span class="meta">php {{ $site->php_version }} · fpm pool: bergmann</span>
</div>
<div class="clu-card-body">
<div class="clu-set-grid">
<div class="clu-field"><label>memory_limit</label><div class="clu-input-group"><input value="256" type="text" /><span class="suffix">M</span></div></div>
<div class="clu-field"><label>max_execution_time</label><div class="clu-input-group"><input value="120" type="text" /><span class="suffix">sek</span></div></div>
<div class="clu-field"><label>upload_max_filesize</label><div class="clu-input-group"><input value="128" type="text" /><span class="suffix">M</span></div></div>
<div class="clu-field"><label>post_max_size</label><div class="clu-input-group"><input value="128" type="text" /><span class="suffix">M</span></div></div>
<div class="clu-field"><label>max_input_vars</label><div class="clu-input"><input value="3000" style="font-family:var(--font-mono);" type="text" /></div></div>
<div class="clu-field"><label>max_input_time</label><div class="clu-input-group"><input value="120" type="text" /><span class="suffix">sek</span></div></div>
<div class="clu-field"><label>default_charset</label><div class="clu-input"><input value="UTF-8" style="font-family:var(--font-mono);" type="text" /></div></div>
<div class="clu-field"><label>date.timezone</label><div class="clu-input"><input value="Europe/Berlin" style="font-family:var(--font-mono);" type="text" /></div></div>
</div>
@foreach (['display_errors' => false, 'opcache.enable' => true, 'log_errors' => true, 'allow_url_fopen' => false] as $k => $v)
<label class="clu-checkbox" style="justify-content:space-between;width:100%;"><span>{{ $k }}</span><input type="checkbox" @checked($v) /><span class="box"><svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3.5"><path d="M5 12l5 5L20 7"/></svg></span></label>
@endforeach
<div style="margin-top:8px;">
<div class="clu-toggle-row"><div class="clu-tr-body"><div class="clu-tr-title">display_errors</div><div class="clu-tr-desc">php-fehler im browser anzeigen (nur dev)</div></div><span class="clu-toggle"></span></div>
<div class="clu-toggle-row"><div class="clu-tr-body"><div class="clu-tr-title">opcache.enable</div><div class="clu-tr-desc">bytecode-cache aktiv</div></div><span class="clu-toggle on"></span></div>
<div class="clu-toggle-row"><div class="clu-tr-body"><div class="clu-tr-title">log_errors</div><div class="clu-tr-desc">fehler in error.log schreiben</div></div><span class="clu-toggle on"></span></div>
<div class="clu-toggle-row"><div class="clu-tr-body"><div class="clu-tr-title">allow_url_fopen</div><div class="clu-tr-desc">externe urls als datei öffnen</div></div><span class="clu-toggle on"></span></div>
</div>
</div>
<div class="clu-form-actions">
<button class="clu-ghost-btn" type="button">PHP-FPM neu starten</button>
<button class="clu-btn-primary" type="button" style="height:36px;padding:0 14px;">php.ini speichern</button>
</div>
</div>
{{-- OPcache + Extensions --}}
<div class="grid gap-[14px]" style="grid-template-columns:1fr 1fr;">
<div class="clu-card"><div class="clu-card-head"><h3>OPcache</h3></div><div class="clu-card-body" style="display:flex;flex-direction:column;gap:9px;">
<div class="flex justify-between text-[12.5px]"><span class="text-(--color-muted)">Hit-Rate</span><span class="font-mono">99.2%</span></div>
<div class="flex justify-between text-[12.5px]"><span class="text-(--color-muted)">Scripts</span><span class="font-mono">4 218</span></div>
<div class="flex justify-between text-[12.5px]"><span class="text-(--color-muted)">Memory</span><span class="font-mono">187 / 256 MB</span></div>
<button class="clu-ghost-btn" type="button" style="align-self:flex-start;">Cache leeren</button>
</div></div>
<div class="clu-card"><div class="clu-card-head"><h3>Aktive Erweiterungen</h3></div><div class="clu-card-body"><div class="flex flex-wrap gap-[6px]">
@foreach (['curl', 'gd', 'imagick', 'mbstring', 'mysqli', 'opcache', 'redis', 'xml', 'zip', 'intl', 'json', 'soap', 'sodium', 'exif', 'fileinfo', 'iconv'] as $ext)<span class="clu-pill muted">{{ $ext }}</span>@endforeach
</div></div></div>
<div class="clu-card">
<div class="clu-card-head"><h3>OPcache</h3><span class="clu-pill success">aktiv</span></div>
<div class="clu-card-body">
<div class="clu-kv"><span class="k">Trefferquote</span><span class="v" style="color:var(--color-success);">99,2%</span></div>
<div class="clu-kv"><span class="k">Speicher</span><span class="v">142 / 256 MB</span></div>
<div class="clu-kv"><span class="k">Zwischengesp. Skripte</span><span class="v">4.218</span></div>
<div class="clu-kv" style="border-bottom:0;"><span class="k">opcache.revalidate_freq</span><span class="v">60 s</span></div>
</div>
<div class="clu-form-actions">
<button class="clu-ghost-btn" type="button">OPcache leeren</button>
</div>
</div>
<div class="clu-card">
<div class="clu-card-head"><h3>Aktive Erweiterungen</h3><span class="meta">28 module</span></div>
<div class="clu-card-body">
<div class="flex flex-wrap gap-[8px]">
@foreach (['curl', 'gd', 'imagick', 'intl', 'mbstring', 'mysqli', 'opcache', 'redis', 'soap', 'zip', 'bcmath', 'exif'] as $ext)
<span class="clu-pill muted">{{ $ext }}</span>
@endforeach
<span class="clu-pill accent">+ 16</span>
</div>
</div>
</div>
</div>
</div>
@endif