wip: Mobile-Theme-Testphase und PHP-8.2-Guards (Stand Juni 2026)
This commit is contained in:
@@ -0,0 +1,816 @@
|
||||
<?php
|
||||
/**
|
||||
* Krempl PostgreSQL Search Test Interface
|
||||
* Search for devices and show matching ersatzteile
|
||||
*/
|
||||
|
||||
require_once __DIR__ . '/core/postgres/KremplDB.php';
|
||||
|
||||
$search_term = isset($_GET['q']) ? trim($_GET['q']) : '';
|
||||
$results = [];
|
||||
$item_results = [];
|
||||
$search_time = 0;
|
||||
$item_search_time = 0;
|
||||
$error_message = '';
|
||||
$show_shop_items = isset($_GET['shop_items']) && $_GET['shop_items'] === '1';
|
||||
|
||||
try {
|
||||
// MySQL wird in beiden Fällen gebraucht (rechte Spalte = Artikel-Suche)
|
||||
$db = new KremplDB(true);
|
||||
|
||||
if ($search_term !== '') {
|
||||
// Links: Geräte-Suche (PostgreSQL)
|
||||
$start_time = microtime(true);
|
||||
$results = $db->searchGeraete($search_term, 20);
|
||||
if ($show_shop_items) {
|
||||
foreach ($results as &$device) {
|
||||
$device['shop_items'] = $db->getAvailableItemsForGeraet($device['geraet_id'], 200);
|
||||
}
|
||||
unset($device);
|
||||
}
|
||||
$search_time = (microtime(true) - $start_time) * 1000;
|
||||
|
||||
// Rechts: Artikel-/Ersatzteil-Suche (MySQL)
|
||||
$start_time2 = microtime(true);
|
||||
$item_results = $db->searchItems($search_term, 20);
|
||||
$item_search_time = (microtime(true) - $start_time2) * 1000;
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
$error_message = $e->getMessage();
|
||||
}
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Krempl PostgreSQL Search Test</title>
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
|
||||
background: #f5f5f5;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 1600px;
|
||||
margin: 0 auto;
|
||||
background: white;
|
||||
padding: 30px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
.split-results {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 24px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
.split-results > div {
|
||||
min-width: 0;
|
||||
}
|
||||
.split-results h2 {
|
||||
font-size: 16px;
|
||||
color: #444;
|
||||
background: #f4f4f4;
|
||||
padding: 10px 14px;
|
||||
border-left: 4px solid #4CAF50;
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
.split-results .right-pane h2 {
|
||||
border-left-color: #1976D2;
|
||||
}
|
||||
.item-card {
|
||||
border: 1px solid #e0e0e0;
|
||||
border-radius: 4px;
|
||||
margin-bottom: 8px;
|
||||
padding: 10px 12px;
|
||||
background: #fafafa;
|
||||
font-size: 13px;
|
||||
}
|
||||
.item-card .item-name {
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
.item-card .item-meta {
|
||||
color: #666;
|
||||
font-size: 12px;
|
||||
}
|
||||
.item-card .item-meta strong {color:#1976D2;}
|
||||
.inventory-positive {color:#2e7d32;font-weight:bold;}
|
||||
.inventory-zero {color:#aaa;}
|
||||
.inventory-null {color:#aaa;font-style:italic;}
|
||||
@media (max-width: 900px) {
|
||||
.split-results { grid-template-columns: 1fr; }
|
||||
}
|
||||
|
||||
h1 {
|
||||
color: #333;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.search-box {
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.search-input {
|
||||
width: 100%;
|
||||
padding: 12px 15px;
|
||||
font-size: 16px;
|
||||
border: 2px solid #ddd;
|
||||
border-radius: 4px;
|
||||
transition: border-color 0.3s;
|
||||
}
|
||||
|
||||
.search-input:focus {
|
||||
outline: none;
|
||||
border-color: #4CAF50;
|
||||
}
|
||||
|
||||
.search-btn {
|
||||
padding: 10px 20px;
|
||||
background: #4CAF50;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
font-size: 16px;
|
||||
cursor: pointer;
|
||||
transition: background 0.3s;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.search-btn:hover {
|
||||
background: #45a049;
|
||||
}
|
||||
|
||||
#shopItemsToggle {
|
||||
cursor: pointer;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
}
|
||||
|
||||
.search-info {
|
||||
color: #666;
|
||||
margin-bottom: 20px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.device-card {
|
||||
border: 1px solid #e0e0e0;
|
||||
border-radius: 4px;
|
||||
margin-bottom: 20px;
|
||||
background: #fafafa;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.device-card.collapsed .ersatzteile-section {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.device-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: start;
|
||||
padding: 15px;
|
||||
margin-bottom: 0;
|
||||
border-bottom: 2px solid #e0e0e0;
|
||||
}
|
||||
|
||||
.score-badge {
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.score-badge:hover {
|
||||
opacity: 0.9;
|
||||
transform: scale(1.02);
|
||||
}
|
||||
|
||||
.toggle-arrow {
|
||||
display: inline-block;
|
||||
margin-left: 8px;
|
||||
transition: transform 0.3s;
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
.device-card.collapsed .toggle-arrow {
|
||||
transform: rotate(-90deg);
|
||||
}
|
||||
|
||||
.device-info {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.device-title {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.device-title a {
|
||||
color: #333;
|
||||
text-decoration: none;
|
||||
transition: color 0.2s;
|
||||
}
|
||||
|
||||
.device-title a:hover {
|
||||
color: #4CAF50;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.device-details {
|
||||
color: #666;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.device-details span {
|
||||
display: inline-block;
|
||||
margin-right: 15px;
|
||||
}
|
||||
|
||||
.score-badge {
|
||||
display: inline-block;
|
||||
padding: 5px 12px;
|
||||
border-radius: 20px;
|
||||
font-weight: bold;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.score-100 { background: #4CAF50; color: white; }
|
||||
.score-95 { background: #66BB6A; color: white; }
|
||||
.score-90 { background: #81C784; color: white; }
|
||||
.score-80 { background: #FFC107; color: white; }
|
||||
.score-75 { background: #FFD54F; color: #333; }
|
||||
.score-60 { background: #FFE082; color: #333; }
|
||||
.score-default { background: #BDBDBD; color: white; }
|
||||
|
||||
.ersatzteile-section {
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.ersatzteile-title {
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
margin-bottom: 10px;
|
||||
margin-top: 10px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.ersatzteile-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
background: white;
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.ersatzteile-table th {
|
||||
background: #f0f0f0;
|
||||
padding: 8px;
|
||||
text-align: left;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: #555;
|
||||
}
|
||||
|
||||
.ersatzteile-table td {
|
||||
padding: 8px;
|
||||
border-top: 1px solid #e0e0e0;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.ersatzteile-table tr:hover {
|
||||
background: #f9f9f9;
|
||||
}
|
||||
|
||||
.navision-id {
|
||||
font-weight: bold;
|
||||
color: #1976D2;
|
||||
}
|
||||
|
||||
.no-results {
|
||||
text-align: center;
|
||||
padding: 40px;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.match-type {
|
||||
display: inline-block;
|
||||
font-size: 11px;
|
||||
padding: 2px 6px;
|
||||
background: #e0e0e0;
|
||||
border-radius: 3px;
|
||||
color: #666;
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.error-message {
|
||||
background: #ffebee;
|
||||
border: 1px solid #ef5350;
|
||||
color: #c62828;
|
||||
padding: 15px;
|
||||
border-radius: 4px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.error-message strong {
|
||||
display: block;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<h1>Krempl PostgreSQL Search Test</h1>
|
||||
|
||||
<div class="search-box">
|
||||
<form method="GET" action="" id="searchForm">
|
||||
<input
|
||||
type="text"
|
||||
name="q"
|
||||
class="search-input"
|
||||
placeholder="Gerätesuche (z.B. AWB 921 PH, ZCS 2100B, 2100B)"
|
||||
value="<?php echo htmlspecialchars($search_term); ?>"
|
||||
autofocus
|
||||
/>
|
||||
<div style="margin-top: 10px; display: flex; align-items: center; gap: 15px;">
|
||||
<button type="submit" class="search-btn">Suchen</button>
|
||||
<label style="font-size: 14px; cursor: pointer; display: flex; align-items: center; gap: 5px;">
|
||||
<input type="checkbox" name="shop_items" value="1" <?php echo $show_shop_items ? 'checked' : ''; ?> id="shopItemsToggle" onchange="document.getElementById('searchForm').submit();">
|
||||
<span>Nur verfügbare Shop-Artikel (MySQL)</span>
|
||||
</label>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<?php if ($error_message): ?>
|
||||
<div class="error-message">
|
||||
<strong>Fehler bei der Datenbankverbindung:</strong>
|
||||
<?php echo htmlspecialchars($error_message); ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($search_term !== '' && !$error_message): ?>
|
||||
<div class="split-results">
|
||||
|
||||
<!-- LINKS: Geräte-Suche (PostgreSQL) -->
|
||||
<div class="left-pane">
|
||||
<h2>Geräte (PostgreSQL)
|
||||
— <?php echo count($results); ?> Treffer
|
||||
(<?php echo number_format($search_time, 0); ?> ms)
|
||||
</h2>
|
||||
|
||||
<?php if (count($results) === 0): ?>
|
||||
<div class="no-results" style="padding:20px;">
|
||||
Keine Geräte gefunden.
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php foreach ($results as $device): ?>
|
||||
<?php
|
||||
$score = (int)$device['score'];
|
||||
$score_class = 'score-default';
|
||||
if ($score >= 100) $score_class = 'score-100';
|
||||
elseif ($score >= 95) $score_class = 'score-95';
|
||||
elseif ($score >= 90) $score_class = 'score-90';
|
||||
elseif ($score >= 80) $score_class = 'score-80';
|
||||
elseif ($score >= 75) $score_class = 'score-75';
|
||||
elseif ($score >= 60) $score_class = 'score-60';
|
||||
?>
|
||||
|
||||
<div class="device-card collapsed" id="device-<?php echo $device['geraet_id']; ?>">
|
||||
<div class="device-header">
|
||||
<div class="device-info">
|
||||
<div class="device-title">
|
||||
<a href="geraet.php?id=<?php echo $device['geraet_id']; ?>&q=<?php echo urlencode($search_term); ?><?php echo $show_shop_items ? '&shop_items=1' : ''; ?>" style="color: #333; text-decoration: none;">
|
||||
<?php echo htmlspecialchars($device['modell_bezeichnung']); ?>
|
||||
</a>
|
||||
<span class="match-type"><?php echo htmlspecialchars($device['match_type']); ?></span>
|
||||
</div>
|
||||
<div class="device-details">
|
||||
<span><strong>Marke:</strong> <?php echo htmlspecialchars($device['marke']); ?></span>
|
||||
<span><strong>Typ:</strong> <?php echo htmlspecialchars($device['typ']); ?></span>
|
||||
<span><strong>Nr:</strong> <?php echo htmlspecialchars($device['nr']); ?></span>
|
||||
<span><strong>ID:</strong> <?php echo htmlspecialchars($device['geraet_id']); ?></span>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<span class="score-badge <?php echo $score_class; ?>" onclick="toggleDevice(<?php echo $device['geraet_id']; ?>)">
|
||||
Score: <?php echo $score; ?>
|
||||
<span class="toggle-arrow">▼</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php if ($show_shop_items && isset($device['shop_items'])): ?>
|
||||
<!-- Show available shop items -->
|
||||
<?php if (count($device['shop_items']) > 0): ?>
|
||||
<div class="ersatzteile-section">
|
||||
<div class="ersatzteile-title">
|
||||
Verfügbare Shop-Artikel:
|
||||
<span style="background: #4CAF50; color: white; padding: 3px 10px; border-radius: 12px; font-weight: bold; margin-left: 5px;">
|
||||
<?php echo count($device['shop_items']); ?> verfügbar
|
||||
</span>
|
||||
</div>
|
||||
<table class="ersatzteile-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Artikel-Nr</th>
|
||||
<th>Artikelname</th>
|
||||
<th>Navision-ID</th>
|
||||
<th>Lagerbestand</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($device['shop_items'] as $item): ?>
|
||||
<tr>
|
||||
<td><?php echo htmlspecialchars($item['number']); ?></td>
|
||||
<td><?php echo htmlspecialchars($item['name']); ?></td>
|
||||
<td class="navision-id"><?php echo htmlspecialchars($item['matched_navision_id']); ?></td>
|
||||
<td><?php echo (int)$item['inventory']; ?> Stück</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<div class="ersatzteile-section">
|
||||
<div class="ersatzteile-title" style="color: #999;">
|
||||
Keine verfügbaren Shop-Artikel gefunden
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<?php else: ?>
|
||||
<!-- Show all Krempl ersatzteile -->
|
||||
<?php if (count($device['ersatzteile']) > 0): ?>
|
||||
<div class="ersatzteile-section">
|
||||
<div class="ersatzteile-title">
|
||||
Passende Ersatzteile (Krempl):
|
||||
<span style="background: #2196F3; color: white; padding: 3px 10px; border-radius: 12px; font-weight: bold; margin-left: 5px;">
|
||||
<?php echo count($device['ersatzteile']); ?> gesamt
|
||||
</span>
|
||||
</div>
|
||||
<table class="ersatzteile-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Navision ID</th>
|
||||
<th>Krempl ID</th>
|
||||
<th>Originalnummer</th>
|
||||
<th>Marke</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($device['ersatzteile'] as $ers): ?>
|
||||
<tr>
|
||||
<td class="navision-id"><?php echo htmlspecialchars($ers['navision_id']); ?></td>
|
||||
<td><?php echo htmlspecialchars($ers['krempl_id']); ?></td>
|
||||
<td><?php echo htmlspecialchars($ers['originalnummer']); ?></td>
|
||||
<td><?php echo htmlspecialchars($ers['marke']); ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<div class="ersatzteile-section">
|
||||
<div class="ersatzteile-title" style="color: #999;">
|
||||
Keine Ersatzteile gefunden
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div><!-- /left-pane -->
|
||||
|
||||
<!-- RECHTS: Artikel-/Ersatzteil-Suche (MySQL) -->
|
||||
<div class="right-pane">
|
||||
<h2>Ersatzteile / Artikel (MySQL items)
|
||||
— <?php echo count($item_results); ?> Treffer
|
||||
(<?php echo number_format($item_search_time, 0); ?> ms)
|
||||
</h2>
|
||||
|
||||
<?php if (count($item_results) === 0): ?>
|
||||
<div class="no-results" style="padding:20px;">
|
||||
Keine Artikel gefunden.
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<?php foreach ($item_results as $item): ?>
|
||||
<div class="item-card">
|
||||
<div class="item-name"><?= htmlspecialchars($item['name']) ?></div>
|
||||
<div class="item-meta">
|
||||
Art-Nr: <strong><?= htmlspecialchars($item['number']) ?></strong>
|
||||
· Navision: <strong><?= htmlspecialchars($item['matched_navision_id']) ?></strong>
|
||||
· Lager:
|
||||
<?php
|
||||
$inv = $item['inventory'];
|
||||
if ($inv === null) {
|
||||
echo '<span class="inventory-null">k.A.</span>';
|
||||
} elseif ((int)$inv > 0) {
|
||||
echo '<span class="inventory-positive">' . (int)$inv . ' Stk</span>';
|
||||
} else {
|
||||
echo '<span class="inventory-zero">0</span>';
|
||||
}
|
||||
?>
|
||||
</div>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
<?php endif; ?>
|
||||
</div><!-- /right-pane -->
|
||||
|
||||
</div><!-- /split-results -->
|
||||
|
||||
<?php
|
||||
// ==================================================================
|
||||
// A/B – NEUE DARSTELLUNG (Prototyp, geführte Ergebnis-Ansicht)
|
||||
// Nutzt dieselben Daten ($results / $item_results), nur menschlich.
|
||||
// ==================================================================
|
||||
if (!function_exists('v2_confidence')) {
|
||||
function v2_confidence($score) {
|
||||
$s = (int)$score;
|
||||
if ($s >= 100) return ['Exakter Treffer', '#2e7d32', '#e8f5e9', '✓'];
|
||||
if ($s >= 90) return ['Sehr wahrscheinlich dein Gerät','#388e3c', '#edf7ee', '✓'];
|
||||
if ($s >= 75) return ['Könnte passen', '#f9a825', '#fff8e1', '≈'];
|
||||
return ['Ähnlich – bitte prüfen', '#9e9e9e', '#f5f5f5', '?'];
|
||||
}
|
||||
// nml/Nachfolger steckt im Artikelnamen: "(nml)"/"(nmle)" + "Nachfolger <Nr>"
|
||||
function v2_nml_info($name) {
|
||||
$is_nml = (bool)preg_match('/\(nml[e]?\)/i', $name);
|
||||
$nachf = null;
|
||||
// Nachfolger-Nummer muss mind. eine Ziffer enthalten (schließt "Nachfolger Original" aus)
|
||||
if (preg_match('/Nachfolger\s+([A-Za-z0-9\-\/\.]*\d[A-Za-z0-9\-\/\.]*)/i', $name, $m)) {
|
||||
$nachf = $m[1];
|
||||
}
|
||||
$kein_ersatz = (stripos($name, 'kein Ersatz') !== false);
|
||||
// Anzeigename säubern: (nml)-Marker + "Nachfolger ... Original(teil)" raus
|
||||
$clean = preg_replace('/\s*\(nml[e]?\)\s*/i', ' ', $name);
|
||||
$clean = preg_replace('/\s*Nachfolger\s+[A-Za-z0-9\-\/\.]*\d[A-Za-z0-9\-\/\.]*\s*/i', ' ', $clean);
|
||||
$clean = preg_replace('/\s*(Originalteil|Original)\s*$/i', '', $clean);
|
||||
$clean = trim(preg_replace('/\s{2,}/', ' ', $clean));
|
||||
return ['is_nml'=>$is_nml, 'nachfolger'=>$nachf, 'kein_ersatz'=>$kein_ersatz, 'clean'=>($clean ?: $name)];
|
||||
}
|
||||
}
|
||||
// Treffer in "stark" (>=75) und "schwach" (<75) teilen
|
||||
$v2_strong = []; $v2_weak = [];
|
||||
foreach ($results as $d) { ((int)$d['score'] >= 75) ? $v2_strong[] = $d : $v2_weak[] = $d; }
|
||||
// Artikel klassifizieren: kaufbar / ausgelaufen-mit-Nachfolger / tot
|
||||
$g_buy = []; $g_succ = []; $g_dead = [];
|
||||
foreach ($item_results as $it) {
|
||||
$it['_nml'] = v2_nml_info($it['name']);
|
||||
if (!$it['_nml']['is_nml']) $g_buy[] = $it; // regulär
|
||||
elseif ($it['_nml']['nachfolger']) $g_succ[] = $it; // ausgelaufen, Nachfolger da
|
||||
else $g_dead[] = $it; // ausgelaufen, kein Ersatz
|
||||
}
|
||||
$v2_top_score = isset($results[0]) ? (int)$results[0]['score'] : 0;
|
||||
// Intent-Einordnung
|
||||
if ($v2_top_score >= 90) $v2_headline = 'Wir haben dein Gerät gefunden.';
|
||||
elseif (count($v2_strong) > 0) $v2_headline = 'Das könnte dein Gerät sein:';
|
||||
elseif (count($item_results)) $v2_headline = 'Passende Artikel gefunden:';
|
||||
else $v2_headline = 'Mehrere mögliche Treffer – grenz gern ein.';
|
||||
?>
|
||||
|
||||
<hr style="margin:40px 0 0; border:none; border-top:3px dashed #cfcfcf;">
|
||||
<style>
|
||||
.v2{margin-top:24px;font-size:15px}
|
||||
.v2 .v2-flag{display:inline-block;background:#111;color:#fff;font-size:11px;font-weight:700;
|
||||
letter-spacing:.5px;padding:3px 10px;border-radius:4px;text-transform:uppercase}
|
||||
.v2 .v2-head{font-size:22px;font-weight:700;color:#222;margin:14px 0 4px}
|
||||
.v2 .v2-sub{color:#777;font-size:13px;margin-bottom:22px}
|
||||
.v2-dev{border:1px solid #e3e3e3;border-radius:10px;margin-bottom:16px;overflow:hidden;
|
||||
box-shadow:0 1px 3px rgba(0,0,0,.05)}
|
||||
.v2-dev-top{display:flex;justify-content:space-between;align-items:flex-start;gap:16px;
|
||||
padding:16px 18px;background:#fff}
|
||||
.v2-dev-name{font-size:18px;font-weight:700;color:#1a1a1a;line-height:1.3}
|
||||
.v2-dev-meta{color:#666;font-size:13px;margin-top:5px}
|
||||
.v2-dev-meta b{color:#444}
|
||||
.v2-conf{white-space:nowrap;font-size:12px;font-weight:700;padding:6px 12px;border-radius:20px}
|
||||
.v2-parts{padding:14px 18px;border-top:1px solid #f0f0f0;background:#fcfcfc}
|
||||
.v2-parts h4{font-size:13px;color:#555;margin-bottom:9px;font-weight:600}
|
||||
.v2-chip{display:inline-block;background:#fff;border:1px solid #e0e0e0;border-radius:16px;
|
||||
padding:4px 11px;margin:0 5px 6px 0;font-size:12px;color:#444}
|
||||
.v2-chip b{color:#1565c0}
|
||||
.v2-buy{background:#e8f5e9;border-color:#c8e6c9}
|
||||
.v2-more{margin:6px 0 22px}
|
||||
.v2-more summary{cursor:pointer;color:#1565c0;font-size:14px;padding:8px 0}
|
||||
.v2-shop{margin-top:30px}
|
||||
.v2-shop h3{font-size:16px;color:#333;margin-bottom:12px;border-left:4px solid #1976D2;padding-left:10px}
|
||||
.v2-item{display:flex;justify-content:space-between;align-items:center;gap:14px;
|
||||
border:1px solid #eee;border-radius:8px;padding:12px 15px;margin-bottom:8px;background:#fff}
|
||||
.v2-item-name{font-weight:600;color:#222}
|
||||
.v2-item-meta{color:#888;font-size:12px;margin-top:3px}
|
||||
.v2-stock{white-space:nowrap;font-size:13px;font-weight:700}
|
||||
.v2-in{color:#2e7d32}.v2-out{color:#c62828}.v2-na{color:#aaa;font-style:italic;font-weight:400}
|
||||
.v2-cart{background:#4CAF50;color:#fff;border:none;border-radius:6px;padding:8px 14px;
|
||||
font-size:13px;font-weight:600;cursor:pointer;white-space:nowrap}
|
||||
.v2-cart:disabled{background:#ccc;cursor:not-allowed}
|
||||
.v2-hint{background:#fffdf3;border:1px dashed #e6d9a8;border-radius:8px;padding:10px 14px;
|
||||
font-size:12px;color:#8a7a3a;margin-top:20px}
|
||||
</style>
|
||||
|
||||
<div class="v2">
|
||||
<span class="v2-flag">◇ Variante B – geführte Ansicht (Prototyp)</span>
|
||||
<div class="v2-head"><?php echo htmlspecialchars($v2_headline); ?></div>
|
||||
<div class="v2-sub">
|
||||
<?php echo count($v2_strong); ?> passende Geräte
|
||||
· <?php echo count($g_buy); ?> kaufbare Artikel
|
||||
<?php if (count($g_succ)) echo ' · '.count($g_succ).' mit Nachfolger'; ?>
|
||||
<?php if (count($g_dead)) echo ' · '.count($g_dead).' ausgelaufen'; ?>
|
||||
<?php if (count($v2_weak)) echo ' · '.count($v2_weak).' weitere mögliche Geräte'; ?>
|
||||
</div>
|
||||
|
||||
<?php foreach ($v2_strong as $i => $d):
|
||||
list($clabel,$ccol,$cbg,$cicon) = v2_confidence($d['score']);
|
||||
$parts = $d['ersatzteile'] ?? [];
|
||||
$buy = $d['shop_items'] ?? null; // nur wenn shop_items-Toggle an
|
||||
?>
|
||||
<div class="v2-dev">
|
||||
<div class="v2-dev-top">
|
||||
<div>
|
||||
<div class="v2-dev-name"><?php echo htmlspecialchars($d['marke'].' '.$d['modell_bezeichnung']); ?></div>
|
||||
<div class="v2-dev-meta">
|
||||
<b>Typ:</b> <?php echo htmlspecialchars($d['typ']); ?>
|
||||
· <b>Nr:</b> <?php echo htmlspecialchars($d['nr']); ?>
|
||||
<?php if (!empty($d['produktionsstart']) || !empty($d['produktionsende'])): ?>
|
||||
· <b>Baujahr:</b> <?php echo htmlspecialchars(trim(($d['produktionsstart']??'').'–'.($d['produktionsende']??''),'–')); ?>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
<span class="v2-conf" style="color:<?php echo $ccol; ?>;background:<?php echo $cbg; ?>;">
|
||||
<?php echo $cicon.' '.htmlspecialchars($clabel); ?>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<?php if ($buy && count($buy)): ?>
|
||||
<div class="v2-parts">
|
||||
<h4>✓ Sofort erhältlich für dieses Gerät (<?php echo count($buy); ?>):</h4>
|
||||
<?php foreach (array_slice($buy,0,8) as $b): ?>
|
||||
<span class="v2-chip v2-buy"><b><?php echo htmlspecialchars($b['number']); ?></b>
|
||||
· <?php echo htmlspecialchars(mb_strimwidth($b['name'],0,42,'…')); ?>
|
||||
<?php if ((int)$b['inventory']>0) echo '· '.(int)$b['inventory'].' Stk'; ?></span>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<?php elseif (count($parts)): ?>
|
||||
<div class="v2-parts">
|
||||
<h4><?php echo count($parts); ?> passende Ersatzteile — Auswahl:</h4>
|
||||
<?php foreach (array_slice($parts,0,10) as $p): ?>
|
||||
<span class="v2-chip"><b><?php echo htmlspecialchars($p['originalnummer']); ?></b>
|
||||
<?php if (!empty($p['marke'])) echo '· '.htmlspecialchars($p['marke']); ?></span>
|
||||
<?php endforeach; ?>
|
||||
<?php if (count($parts) > 10): ?>
|
||||
<span class="v2-chip" style="border-style:dashed;color:#999;">+<?php echo count($parts)-10; ?> weitere</span>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
|
||||
<?php if (count($v2_weak)): ?>
|
||||
<details class="v2-more">
|
||||
<summary>Weitere mögliche Geräte anzeigen (<?php echo count($v2_weak); ?>) – schwächere Treffer</summary>
|
||||
<?php foreach ($v2_weak as $d): list($clabel,$ccol,$cbg,$cicon)=v2_confidence($d['score']); ?>
|
||||
<div class="v2-dev" style="opacity:.75">
|
||||
<div class="v2-dev-top">
|
||||
<div>
|
||||
<div class="v2-dev-name" style="font-size:15px"><?php echo htmlspecialchars($d['marke'].' '.$d['modell_bezeichnung']); ?></div>
|
||||
<div class="v2-dev-meta"><b>Typ:</b> <?php echo htmlspecialchars($d['typ']); ?> · <b>Nr:</b> <?php echo htmlspecialchars($d['nr']); ?></div>
|
||||
</div>
|
||||
<span class="v2-conf" style="color:<?php echo $ccol; ?>;background:<?php echo $cbg; ?>;"><?php echo $cicon.' '.htmlspecialchars($clabel); ?></span>
|
||||
</div>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</details>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php $keep_shop = $show_shop_items ? '&shop_items=1' : ''; ?>
|
||||
|
||||
<?php if (count($g_buy)): ?>
|
||||
<div class="v2-shop">
|
||||
<h3>🛒 Diese Artikel kannst du direkt kaufen</h3>
|
||||
<?php foreach ($g_buy as $it):
|
||||
$inv = $it['inventory'];
|
||||
$can = ($inv !== null && (int)$inv > 0);
|
||||
// Bilder liegen nur auf LIVE → absolute URL
|
||||
$img = !empty($it['default_image_file_name'])
|
||||
? 'https://intelectra.de/web/intelectra_shop/images/article/'.rawurlencode($it['default_image_file_name'])
|
||||
: '';
|
||||
$pb = isset($it['price_brutto']) ? (float)$it['price_brutto'] : 0;
|
||||
$bb = !empty($it['bargain_brutto']) ? (float)$it['bargain_brutto'] : 0;
|
||||
?>
|
||||
<div class="v2-item">
|
||||
<div style="display:flex;align-items:center;gap:14px;min-width:0">
|
||||
<?php if ($img): ?>
|
||||
<img src="<?php echo $img; ?>" alt="" loading="lazy"
|
||||
style="width:64px;height:64px;object-fit:contain;background:#fff;border:1px solid #eee;border-radius:6px;flex:0 0 64px"
|
||||
onerror="this.style.display='none'">
|
||||
<?php else: ?>
|
||||
<div style="width:64px;height:64px;flex:0 0 64px;background:#f5f5f5;border:1px dashed #ddd;border-radius:6px;
|
||||
display:flex;align-items:center;justify-content:center;color:#bbb;font-size:11px">kein Bild</div>
|
||||
<?php endif; ?>
|
||||
<div style="min-width:0">
|
||||
<div class="v2-item-name"><?php echo htmlspecialchars($it['_nml']['clean']); ?></div>
|
||||
<div class="v2-item-meta">Art-Nr <?php echo htmlspecialchars($it['number']); ?></div>
|
||||
</div>
|
||||
</div>
|
||||
<div style="text-align:right;flex:0 0 auto">
|
||||
<div style="font-size:17px;font-weight:700;color:#1a1a1a;margin-bottom:2px">
|
||||
<?php if ($bb > 0 && $pb > 0 && $bb < $pb): ?>
|
||||
<span style="color:#c62828"><?php echo number_format($bb, 2, ',', '.'); ?> €</span>
|
||||
<span style="font-size:12px;color:#999;text-decoration:line-through;font-weight:400"><?php echo number_format($pb, 2, ',', '.'); ?> €</span>
|
||||
<?php elseif ($pb > 0): ?>
|
||||
<?php echo number_format($pb, 2, ',', '.'); ?> €
|
||||
<?php else: ?>
|
||||
<span style="font-size:12px;color:#999;font-weight:400">Preis auf Anfrage</span>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<?php if ($pb > 0): ?><div style="font-size:10px;color:#aaa;margin-bottom:4px">inkl. MwSt.</div><?php endif; ?>
|
||||
<div class="v2-stock <?php echo $inv===null?'v2-na':($can?'v2-in':'v2-out'); ?>">
|
||||
<?php echo $inv===null ? 'Lager k.A.' : ($can ? '● '.(int)$inv.' ab Lager' : '○ nicht am Lager'); ?>
|
||||
</div>
|
||||
<button class="v2-cart" <?php echo $can?'':'disabled'; ?> style="margin-top:6px">
|
||||
<?php echo $can ? 'In den Warenkorb' : 'Bestellbar'; ?>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (count($g_succ)): ?>
|
||||
<div class="v2-shop">
|
||||
<h3 style="border-left-color:#fb8c00">🔄 Ausgelaufen – aber es gibt einen Nachfolger</h3>
|
||||
<?php foreach ($g_succ as $it): $nf = $it['_nml']['nachfolger']; ?>
|
||||
<div class="v2-item" style="background:#fff8f0;border-color:#ffe0b2">
|
||||
<div>
|
||||
<div class="v2-item-name" style="color:#8a6d3b;text-decoration:line-through;text-decoration-color:#d9b38c">
|
||||
<?php echo htmlspecialchars($it['_nml']['clean']); ?>
|
||||
</div>
|
||||
<div class="v2-item-meta">Art-Nr <?php echo htmlspecialchars($it['number']); ?> · nicht mehr lieferbar</div>
|
||||
</div>
|
||||
<div style="text-align:right">
|
||||
<div style="font-size:12px;color:#e65100;font-weight:600;margin-bottom:6px">Nachfolger: <?php echo htmlspecialchars($nf); ?></div>
|
||||
<a href="?q=<?php echo urlencode($nf).$keep_shop; ?>"
|
||||
class="v2-cart" style="background:#fb8c00;text-decoration:none;display:inline-block">
|
||||
→ Nachfolger anzeigen
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (count($g_dead)): ?>
|
||||
<details class="v2-more" style="margin-top:20px">
|
||||
<summary>Nicht mehr lieferbar, kein Ersatz (<?php echo count($g_dead); ?>) – ausgeblendet</summary>
|
||||
<?php foreach ($g_dead as $it): ?>
|
||||
<div class="v2-item" style="opacity:.55">
|
||||
<div>
|
||||
<div class="v2-item-name" style="text-decoration:line-through"><?php echo htmlspecialchars($it['_nml']['clean']); ?></div>
|
||||
<div class="v2-item-meta">Art-Nr <?php echo htmlspecialchars($it['number']); ?></div>
|
||||
</div>
|
||||
<div class="v2-stock v2-out" style="white-space:nowrap">✕ entfällt</div>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</details>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (!count($g_buy) && !count($g_succ) && !count($g_dead)): ?>
|
||||
<!-- keine Artikel -->
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="v2-hint">
|
||||
💡 <b>Prototyp-Notiz (nicht final):</b> Bild (aus <code>items.default_image_file_name</code>, geladen vom Live-Server)
|
||||
+ Preis (<code>item_prices</code> Kundengruppe 1, brutto inkl. Steuersatz, Angebotspreis rot) sind jetzt drin.
|
||||
<b>nml/Nachfolger ist jetzt live:</b> Der Marker <code>(nml)</code>/<code>(nmle)</code> im Artikelnamen wird erkannt —
|
||||
ausgelaufene Teile <b>mit</b> „Nachfolger <Nr>" landen im orangen Block und verlinken per Klick auf den kaufbaren Nachfolger;
|
||||
Teile <b>ohne</b> Ersatz werden ans Ende ausgeblendet. Score-Zahlen durch Vertrauens-Text ersetzt.
|
||||
Tipp: oben „Nur verfügbare Shop-Artikel" aktivieren zeigt pro Gerät die kaufbaren Teile grün.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php else: ?>
|
||||
<div class="no-results">
|
||||
Gib einen Suchbegriff ein (z.B. "AWB 921 PH", "ZCS 2100B", "Kohlefilter Bauknecht")
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function toggleDevice(deviceId) {
|
||||
const card = document.getElementById('device-' + deviceId);
|
||||
if (card) {
|
||||
card.classList.toggle('collapsed');
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user