Files
citrus/resources/views/oem_b2b_demo.blade.php

1426 lines
50 KiB
PHP
Raw Permalink 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.
<!DOCTYPE html>
<html lang="tr" class="dark">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>Trunçgil OEM B2B - Akıllı Barkod & QR Canlı Arama Laboratuvarı</title>
<meta name="robots" content="noindex, nofollow">
<!-- Google Fonts -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@300;400;500;600;700;800&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet">
<!-- FontAwesome -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<!-- DUAL BARCODE ENGINES: ZXing (1D/2D) + HTML5-QRCode -->
<script src="https://unpkg.com/@zxing/library@0.20.0/umd/index.min.js"></script>
<script src="https://unpkg.com/html5-qrcode@2.3.8/html5-qrcode.min.js"></script>
<style>
:root {
--bg-main: #0B0F17;
--bg-card: #131B2E;
--bg-card-hover: #1A243F;
--bg-input: #1C2640;
--border-color: rgba(255, 255, 255, 0.08);
--border-focus: #3B82F6;
--primary: #3B82F6;
--primary-glow: rgba(59, 130, 246, 0.35);
--accent-cyan: #06B6D4;
--accent-green: #10B981;
--accent-amber: #F59E0B;
--text-main: #F1F5F9;
--text-muted: #94A3B8;
--text-sub: #64748B;
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
font-family: 'Plus Jakarta Sans', -apple-system, BlinkMacSystemFont, sans-serif;
-webkit-tap-highlight-color: transparent;
}
body {
background-color: var(--bg-main);
color: var(--text-main);
min-height: 100vh;
display: flex;
flex-direction: column;
padding-bottom: 90px;
background-image:
radial-gradient(circle at 10% 20%, rgba(59, 130, 246, 0.08) 0%, transparent 40%),
radial-gradient(circle at 90% 80%, rgba(6, 182, 212, 0.06) 0%, transparent 40%);
}
/* Top Header */
.app-header {
background: rgba(19, 27, 46, 0.85);
backdrop-filter: blur(12px);
-webkit-backdrop-filter: blur(12px);
border-bottom: 1px solid var(--border-color);
position: sticky;
top: 0;
z-index: 100;
padding: 12px 16px;
}
.header-container {
max-width: 800px;
margin: 0 auto;
display: flex;
align-items: center;
justify-content: space-between;
}
.brand-box {
display: flex;
align-items: center;
gap: 10px;
}
.brand-icon {
width: 38px;
height: 38px;
border-radius: 10px;
background: linear-gradient(135deg, #3B82F6, #06B6D4);
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: 18px;
box-shadow: 0 4px 12px var(--primary-glow);
}
.brand-text h1 {
font-size: 15px;
font-weight: 700;
color: #FFFFFF;
letter-spacing: -0.2px;
}
.brand-text p {
font-size: 11px;
color: var(--accent-cyan);
font-weight: 600;
display: flex;
align-items: center;
gap: 4px;
}
.live-badge {
background: rgba(16, 185, 129, 0.15);
border: 1px solid rgba(16, 185, 129, 0.3);
color: var(--accent-green);
font-size: 10px;
font-weight: 700;
padding: 4px 10px;
border-radius: 20px;
display: flex;
align-items: center;
gap: 5px;
}
.live-dot {
width: 6px;
height: 6px;
border-radius: 50%;
background: var(--accent-green);
animation: pulse-dot 1.5s infinite;
}
@keyframes pulse-dot {
0% { transform: scale(1); opacity: 1; }
50% { transform: scale(1.5); opacity: 0.4; }
100% { transform: scale(1); opacity: 1; }
}
/* Main Container */
.main-content {
max-width: 800px;
width: 100%;
margin: 0 auto;
padding: 16px;
display: flex;
flex-direction: column;
gap: 16px;
}
/* Cards */
.card {
background: var(--bg-card);
border: 1px solid var(--border-color);
border-radius: 18px;
padding: 18px;
box-shadow: 0 8px 24px rgba(0,0,0,0.25);
transition: border-color 0.2s ease;
}
.card-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 14px;
}
.card-title {
font-size: 14px;
font-weight: 700;
color: #FFFFFF;
display: flex;
align-items: center;
gap: 8px;
}
.card-title i {
color: var(--primary);
}
/* Diagnostic Status Alert */
.cam-status-box {
background: rgba(59, 130, 246, 0.1);
border: 1px solid rgba(59, 130, 246, 0.3);
border-radius: 12px;
padding: 10px 14px;
margin-bottom: 12px;
font-size: 12px;
color: #93C5FD;
display: flex;
align-items: center;
justify-content: space-between;
}
.cam-status-text {
display: flex;
align-items: center;
gap: 8px;
font-weight: 600;
}
/* Scanner Area */
.scanner-wrapper {
position: relative;
width: 100%;
border-radius: 14px;
overflow: hidden;
background: #000;
border: 2px dashed rgba(59, 130, 246, 0.5);
min-height: 280px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
#video-preview {
width: 100% !important;
height: 280px !important;
object-fit: cover !important;
border-radius: 12px;
display: none;
}
.viewfinder-target {
display: none;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 240px;
height: 150px;
border: 2px solid var(--accent-green);
box-shadow: 0 0 0 4000px rgba(0, 0, 0, 0.45);
border-radius: 12px;
pointer-events: none;
z-index: 10;
}
.viewfinder-laser {
position: absolute;
top: 0;
left: 0;
right: 0;
height: 2px;
background: #10B981;
box-shadow: 0 0 12px #10B981;
animation: scan-laser 1.8s infinite ease-in-out;
}
@keyframes scan-laser {
0% { top: 0; }
50% { top: 100%; }
100% { top: 0; }
}
.scanner-placeholder {
padding: 30px 20px;
text-align: center;
display: flex;
flex-direction: column;
align-items: center;
gap: 12px;
}
.scanner-placeholder i {
font-size: 48px;
color: var(--primary);
animation: pulse 2s infinite;
}
@keyframes pulse {
0% { transform: scale(1); opacity: 0.8; }
50% { transform: scale(1.1); opacity: 1; }
100% { transform: scale(1); opacity: 0.8; }
}
.cam-select {
width: 100%;
background: var(--bg-input);
border: 1px solid var(--border-color);
color: white;
padding: 10px 14px;
border-radius: 10px;
font-size: 12px;
margin-top: 10px;
outline: none;
}
.btn-group {
display: flex;
flex-direction: column;
gap: 8px;
margin-top: 12px;
width: 100%;
}
@media (min-width: 480px) {
.btn-group {
flex-direction: row;
}
}
.btn {
flex: 1;
padding: 12px 16px;
border-radius: 12px;
font-weight: 600;
font-size: 13px;
border: none;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
transition: all 0.2s ease;
}
.btn-primary {
background: linear-gradient(135deg, #3B82F6, #2563EB);
color: white;
box-shadow: 0 4px 12px var(--primary-glow);
}
.btn-green {
background: linear-gradient(135deg, #10B981, #059669);
color: white;
box-shadow: 0 4px 12px rgba(16, 185, 129, 0.3);
}
.btn-danger {
background: rgba(239, 68, 68, 0.2);
color: #EF4444;
border: 1px solid rgba(239, 68, 68, 0.4);
}
/* Search Input */
.search-box {
position: relative;
width: 100%;
}
.search-input {
width: 100%;
padding: 14px 16px 14px 44px;
background: var(--bg-input);
border: 1px solid var(--border-color);
border-radius: 14px;
color: white;
font-size: 14px;
font-weight: 500;
outline: none;
transition: all 0.2s ease;
}
.search-input:focus {
border-color: var(--border-focus);
box-shadow: 0 0 0 3px var(--primary-glow);
}
.search-icon {
position: absolute;
left: 16px;
top: 50%;
transform: translateY(-50%);
color: var(--text-muted);
font-size: 16px;
}
.clear-btn {
position: absolute;
right: 14px;
top: 50%;
transform: translateY(-50%);
background: none;
border: none;
color: var(--text-muted);
cursor: pointer;
font-size: 14px;
}
/* Sample Buttons */
.sample-section {
margin-top: 12px;
}
.sample-title {
font-size: 11px;
font-weight: 700;
color: var(--text-sub);
text-transform: uppercase;
letter-spacing: 0.5px;
margin-bottom: 8px;
display: flex;
align-items: center;
gap: 6px;
}
.sample-chips {
display: flex;
flex-wrap: wrap;
gap: 6px;
}
.chip {
background: var(--bg-input);
border: 1px solid var(--border-color);
padding: 6px 10px;
border-radius: 20px;
font-size: 11px;
font-weight: 600;
color: var(--text-muted);
cursor: pointer;
transition: all 0.2s ease;
display: flex;
align-items: center;
gap: 6px;
}
.chip:hover, .chip.active {
background: rgba(59, 130, 246, 0.15);
border-color: var(--primary);
color: #60A5FA;
}
.chip .tag {
font-size: 9px;
background: rgba(255,255,255,0.1);
padding: 2px 6px;
border-radius: 10px;
}
/* Loading Spinner */
.loading-box {
display: none;
padding: 24px;
text-align: center;
color: var(--accent-cyan);
font-size: 13px;
font-weight: 600;
}
.loading-spinner {
width: 32px;
height: 32px;
border: 3px solid rgba(6, 182, 212, 0.2);
border-top-color: var(--accent-cyan);
border-radius: 50%;
animation: spin 0.8s linear infinite;
margin: 0 auto 10px auto;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
/* Product Detail Result Card */
.product-card {
display: none;
animation: fadeIn 0.3s ease;
}
.product-card.active {
display: block;
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(8px); }
to { opacity: 1; transform: translateY(0); }
}
.product-top-bar {
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: 12px;
margin-bottom: 12px;
}
.brand-badge {
background: linear-gradient(135deg, #1E293B, #0F172A);
border: 1px solid rgba(255,255,255,0.15);
color: #F8FAFC;
font-size: 11px;
font-weight: 800;
padding: 4px 10px;
border-radius: 8px;
letter-spacing: 0.5px;
display: inline-flex;
align-items: center;
gap: 6px;
}
.stock-badge {
font-size: 11px;
font-weight: 700;
padding: 4px 10px;
border-radius: 20px;
display: flex;
align-items: center;
gap: 5px;
background: rgba(16, 185, 129, 0.15);
color: var(--accent-green);
border: 1px solid rgba(16, 185, 129, 0.3);
}
.product-grid {
display: grid;
grid-template-columns: 1fr;
gap: 16px;
}
@media (min-width: 600px) {
.product-grid {
grid-template-columns: 240px 1fr;
}
}
.product-image-box {
position: relative;
background: #FFFFFF;
border-radius: 14px;
padding: 12px;
display: flex;
align-items: center;
justify-content: center;
min-height: 180px;
border: 1px solid var(--border-color);
}
.product-image-box img {
max-width: 100%;
max-height: 180px;
object-fit: contain;
border-radius: 8px;
}
.source-pill {
display: inline-block;
background: rgba(6, 182, 212, 0.15);
border: 1px solid rgba(6, 182, 212, 0.3);
color: var(--accent-cyan);
font-size: 10px;
font-weight: 700;
padding: 3px 8px;
border-radius: 6px;
margin-bottom: 8px;
}
.product-info-col h2 {
font-size: 17px;
font-weight: 700;
color: white;
line-height: 1.3;
margin-bottom: 4px;
}
.product-info-col .subtitle {
font-size: 12px;
color: var(--text-muted);
margin-bottom: 12px;
}
/* Spec Table */
.spec-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 8px;
margin-bottom: 14px;
}
.spec-item {
background: var(--bg-input);
padding: 8px 12px;
border-radius: 10px;
border: 1px solid rgba(255,255,255,0.04);
}
.spec-label {
font-size: 10px;
color: var(--text-sub);
font-weight: 600;
text-transform: uppercase;
}
.spec-value {
font-size: 12px;
color: white;
font-weight: 700;
font-family: 'JetBrains Mono', monospace;
margin-top: 2px;
word-break: break-all;
}
/* OEM Numbers list */
.oem-box {
background: rgba(59, 130, 246, 0.08);
border: 1px dashed rgba(59, 130, 246, 0.3);
border-radius: 12px;
padding: 10px 12px;
margin-bottom: 14px;
}
.oem-header {
font-size: 11px;
font-weight: 700;
color: #60A5FA;
margin-bottom: 6px;
display: flex;
align-items: center;
gap: 6px;
}
.oem-tags {
display: flex;
flex-wrap: wrap;
gap: 4px;
}
.oem-tag {
background: rgba(59, 130, 246, 0.2);
color: #93C5FD;
font-family: 'JetBrains Mono', monospace;
font-size: 11px;
font-weight: 600;
padding: 3px 8px;
border-radius: 6px;
}
/* Compatible Vehicles */
.vehicle-box {
margin-bottom: 14px;
}
.vehicle-title {
font-size: 11px;
font-weight: 700;
color: var(--text-muted);
margin-bottom: 6px;
}
.vehicle-list {
display: flex;
flex-wrap: wrap;
gap: 6px;
}
.vehicle-badge {
background: var(--bg-input);
border: 1px solid var(--border-color);
color: var(--text-main);
font-size: 11px;
font-weight: 500;
padding: 4px 10px;
border-radius: 8px;
display: flex;
align-items: center;
gap: 5px;
}
/* Price & Action Section */
.price-row {
display: flex;
align-items: center;
justify-content: space-between;
background: rgba(16, 185, 129, 0.08);
border: 1px solid rgba(16, 185, 129, 0.2);
border-radius: 14px;
padding: 12px 16px;
margin-top: 14px;
}
.price-label {
font-size: 11px;
color: var(--text-muted);
}
.price-value {
font-size: 20px;
font-weight: 800;
color: var(--accent-green);
font-family: 'JetBrains Mono', monospace;
}
.price-vat {
font-size: 10px;
color: var(--text-sub);
}
.qty-picker {
display: flex;
align-items: center;
background: var(--bg-input);
border-radius: 10px;
border: 1px solid var(--border-color);
overflow: hidden;
}
.qty-btn {
background: none;
border: none;
color: white;
width: 32px;
height: 34px;
font-size: 14px;
cursor: pointer;
}
.qty-input {
width: 40px;
background: none;
border: none;
color: white;
text-align: center;
font-weight: 700;
font-size: 13px;
}
/* Floating Bottom Bar for B2B Cart Simulation */
.cart-bar {
position: fixed;
bottom: 0;
left: 0;
right: 0;
background: rgba(19, 27, 46, 0.95);
backdrop-filter: blur(16px);
-webkit-backdrop-filter: blur(16px);
border-top: 1px solid var(--border-color);
padding: 12px 16px;
z-index: 90;
}
.cart-container {
max-width: 800px;
margin: 0 auto;
display: flex;
align-items: center;
justify-content: space-between;
}
.cart-info {
display: flex;
align-items: center;
gap: 12px;
}
.cart-icon-box {
position: relative;
width: 40px;
height: 40px;
border-radius: 12px;
background: var(--bg-input);
display: flex;
align-items: center;
justify-content: center;
color: var(--text-main);
}
.cart-badge {
position: absolute;
top: -4px;
right: -4px;
background: var(--primary);
color: white;
font-size: 10px;
font-weight: 800;
width: 18px;
height: 18px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
}
.cart-total-label {
font-size: 10px;
color: var(--text-muted);
}
.cart-total-price {
font-size: 15px;
font-weight: 700;
color: white;
font-family: 'JetBrains Mono', monospace;
}
/* Notification Toast */
.toast {
position: fixed;
top: 70px;
left: 50%;
transform: translateX(-50%) translateY(-20px);
background: rgba(16, 185, 129, 0.95);
color: white;
padding: 10px 20px;
border-radius: 30px;
font-size: 12px;
font-weight: 700;
box-shadow: 0 8px 24px rgba(0,0,0,0.3);
opacity: 0;
pointer-events: none;
transition: all 0.3s ease;
z-index: 1000;
display: flex;
align-items: center;
gap: 8px;
}
.toast.show {
opacity: 1;
transform: translateX(-50%) translateY(0);
}
</style>
</head>
<body>
<!-- Notification Toast -->
<div id="toast" class="toast">
<i class="fa-solid fa-circle-check"></i>
<span id="toast-msg">Barkod Başarıyla Okundu!</span>
</div>
<!-- Hidden File Input for Mobile QR Photo Scan -->
<input type="file" id="qr-file-input" accept="image/*" capture="environment" style="display: none;" onchange="scanImageFile(this)">
<!-- Header -->
<header class="app-header">
<div class="header-container">
<div class="brand-box">
<div class="brand-icon">
<i class="fa-solid fa-gears"></i>
</div>
<div class="brand-text">
<h1>Trunçgil OEM B2B</h1>
<p><i class="fa-solid fa-bolt"></i> Akıllı QR / Barkod Canlı Okuyucu</p>
</div>
</div>
<div class="live-badge">
<div class="live-dot"></div> Kamera İzinleri Açık
</div>
</div>
</header>
<!-- Main Content Area -->
<main class="main-content">
<!-- Camera / Code Scanner Card -->
<section class="card">
<div class="card-header">
<div class="card-title">
<i class="fa-solid fa-camera"></i> Canlı Kamera & Çizgi Barkod Okuyucu
</div>
</div>
<!-- Diagnostic Status Box -->
<div class="cam-status-box" id="cam-status-box">
<div class="cam-status-text">
<i class="fa-solid fa-circle-info" id="cam-status-icon" style="color: var(--accent-cyan);"></i>
<span id="cam-status-text">Kamerayı başlatmak için aşağıdaki butona basın.</span>
</div>
</div>
<!-- Camera Viewfinder Box -->
<div class="scanner-wrapper" id="scanner-container">
<video id="video-preview" autoplay playsinline muted></video>
<div class="viewfinder-target" id="viewfinder-target">
<div class="viewfinder-laser"></div>
</div>
<div class="scanner-placeholder" id="scanner-placeholder">
<i class="fa-solid fa-barcode" style="font-size: 52px; color: var(--primary);"></i>
<p style="font-size: 14px; font-weight: 700; color: var(--text-main);">Kamera ile Barkod veya QR Okutun</p>
<p style="font-size: 11px; color: var(--text-muted); max-width: 320px;">
Kutulardaki çizgi barkodları (EAN-13, Code-128) ve QR kodları doğrudan okutabilirsiniz.
</p>
</div>
</div>
<!-- Camera Device Selector -->
<select id="camera-select" class="cam-select" onchange="changeCameraDevice(this.value)" style="display: none;">
<option value="">-- Kamera Seçin --</option>
</select>
<div class="btn-group">
<button class="btn btn-primary" id="start-cam-btn" onclick="startScannerEngine()">
<i class="fa-solid fa-video"></i> Kamerayı Başlat (Canlı)
</button>
<button class="btn btn-green" onclick="triggerFileScan()">
<i class="fa-solid fa-file-image"></i> Fotoğraf Çek / QR Yükle
</button>
<button class="btn btn-danger" id="stop-cam-btn" onclick="stopScannerEngine()" style="display: none;">
<i class="fa-solid fa-video-slash"></i> Kamerayı Durdur
</button>
</div>
</section>
<!-- Manual Search & Sample Chips Card -->
<section class="card">
<div class="card-header">
<div class="card-title">
<i class="fa-solid fa-magnifying-glass"></i> Manuel Kod veya Barkod Numarası Girin
</div>
</div>
<div class="search-box">
<i class="fa-solid fa-barcode search-icon"></i>
<input type="text" id="code-input" class="search-input" placeholder="Barkod veya OE No Girin (Örn: 8698110129623, 1K0412331B, 5WA412331A...)" oninput="handleSearchInput(this.value)">
<button class="clear-btn" onclick="clearSearch()" style="display: none;" id="clear-btn">
<i class="fa-solid fa-xmark"></i>
</button>
</div>
<!-- Sample Data Test Shortcuts -->
<div class="sample-section">
<div class="sample-title">
<i class="fa-solid fa-vial"></i> Etiket Örneklerinizle Tek Tıkla Sorgula:
</div>
<div class="sample-chips">
<button class="chip" onclick="loadSample('1K0412331B')">
<span>YTT Y11203</span>
<span class="tag">OE: 1K0412331B</span>
</button>
<button class="chip" onclick="loadSample('5WA412331A')">
<span>LEMFÖRDER</span>
<span class="tag">OE: 5WA 412 331 A</span>
</button>
<button class="chip" onclick="loadSample('8698110129623')">
<span>TEKNOROT (Barkod)</span>
<span class="tag">EAN: 8698110129623</span>
</button>
<button class="chip" onclick="loadSample('SUP928268')">
<span>VALEO Rulman</span>
<span class="tag">Code: SUP928268</span>
</button>
</div>
</div>
</section>
<!-- Loading Indicator -->
<div class="card loading-box" id="loading-box">
<div class="loading-spinner"></div>
<span>İnternetten Canlı Ürün Detayları ve Görseller Çekiliyor...</span>
</div>
<!-- Dynamic Product Result Display Card -->
<section class="card product-card" id="product-card">
<div class="product-top-bar">
<div>
<span class="brand-badge" id="res-brand">YTT AUTO PARTS</span>
<div style="font-size: 11px; color: var(--text-sub); margin-top: 4px;" id="res-scanned-code">
Sorgulanan Kod: 1K0412331B
</div>
</div>
<div class="stock-badge" id="res-stock-badge">
<i class="fa-solid fa-circle-check"></i> <span id="res-stock-text">Stokta Var (148 Adet)</span>
</div>
</div>
<div class="product-grid">
<div class="product-image-box">
<img id="res-image" src="/assets/oem/top_strut_mount.png" alt="Ürün Görseli">
</div>
<div class="product-info-col">
<div class="source-pill" id="res-source">🌐 Canlı İnternet Arama Servisi</div>
<h2 id="res-title">Amortisör Takozu (Top Strut Mounting)</h2>
<div class="subtitle" id="res-subtitle">Ön Süspansiyon Takozu - YTT Y11203</div>
<!-- OEM Cross Numbers -->
<div class="oem-box">
<div class="oem-header">
<i class="fa-solid fa-code-compare"></i> OE & Çapraz Referans Numaraları
</div>
<div class="oem-tags" id="res-oem-tags">
<span class="oem-tag">1K0412331B</span>
<span class="oem-tag">1K0 412 331 B</span>
</div>
</div>
<!-- Technical Specs Grid -->
<div class="spec-grid">
<div class="spec-item">
<div class="spec-label">Üretim Menşei</div>
<div class="spec-value" id="res-origin">Türkiye (Made in TR)</div>
</div>
<div class="spec-item">
<div class="spec-label">Montaj Tarafı</div>
<div class="spec-value" id="res-side">Ön Aks Sağ/Sol</div>
</div>
<div class="spec-item">
<div class="spec-label">Ambalaj İçi</div>
<div class="spec-value" id="res-packaging">1 Pcs / Kutulu</div>
</div>
<div class="spec-item">
<div class="spec-label">Sorgulama Kaynağı</div>
<div class="spec-value" style="color: var(--accent-green);">İnternet API / DB</div>
</div>
</div>
<!-- Compatible Vehicles -->
<div class="vehicle-box">
<div class="vehicle-title">
<i class="fa-solid fa-car"></i> Uyumlu Araç Modelleri
</div>
<div class="vehicle-list" id="res-vehicles">
<span class="vehicle-badge"><i class="fa-solid fa-check"></i> Audi A3 (8P / 8V)</span>
<span class="vehicle-badge"><i class="fa-solid fa-check"></i> VW Golf V / VI / VII</span>
</div>
</div>
<!-- B2B Price & Add to Cart -->
<div class="price-row">
<div>
<div class="price-label">B2B Toptan Fiyatı (KDV Hariç)</div>
<div class="price-value" id="res-price">₺245,00</div>
<div class="price-vat">%20 KDV Dahil: <span id="res-price-vat">₺294,00</span></div>
</div>
<div style="display: flex; align-items: center; gap: 8px;">
<div class="qty-picker">
<button class="qty-btn" onclick="changeQty(-1)">-</button>
<input type="text" id="qty-input" class="qty-input" value="1" readonly>
<button class="qty-btn" onclick="changeQty(1)">+</button>
</div>
<button class="btn btn-primary" onclick="addToCart()">
<i class="fa-solid fa-cart-plus"></i> Ekle
</button>
</div>
</div>
</div>
</div>
</section>
</main>
<!-- Floating Cart Simulation Bar -->
<div class="cart-bar">
<div class="cart-container">
<div class="cart-info">
<div class="cart-icon-box">
<i class="fa-solid fa-cart-shopping"></i>
<div class="cart-badge" id="cart-count">0</div>
</div>
<div>
<div class="cart-total-label">B2B Sepet Toplamı</div>
<div class="cart-total-price" id="cart-total">₺0,00</div>
</div>
</div>
<button class="btn btn-primary" style="flex: unset; padding: 10px 18px;" onclick="checkoutSim()">
<i class="fa-solid fa-paper-plane"></i> Sipariş Oluştur (Test)
</button>
</div>
</div>
<!-- Application Script Logic -->
<script>
let zxingControls = null;
let zxingReader = null;
let selectedDeviceId = null;
let cartItems = [];
let currentQty = 1;
let currentProduct = null;
let searchTimeout = null;
window.addEventListener('DOMContentLoaded', () => {
initCameraDevices();
loadSample('1K0412331B');
});
function updateCamStatus(msg, isSuccess = false, isError = false) {
const box = document.getElementById('cam-status-box');
const icon = document.getElementById('cam-status-icon');
const text = document.getElementById('cam-status-text');
text.innerText = msg;
if (isSuccess) {
box.style.borderColor = 'rgba(16, 185, 129, 0.4)';
icon.className = 'fa-solid fa-circle-check';
icon.style.color = 'var(--accent-green)';
} else if (isError) {
box.style.borderColor = 'rgba(239, 68, 68, 0.4)';
icon.className = 'fa-solid fa-triangle-exclamation';
icon.style.color = '#EF4444';
} else {
box.style.borderColor = 'rgba(59, 130, 246, 0.3)';
icon.className = 'fa-solid fa-circle-info';
icon.style.color = 'var(--accent-cyan)';
}
}
// Initialize Camera Devices
function initCameraDevices() {
if (typeof ZXing !== 'undefined') {
zxingReader = new ZXing.BrowserMultiFormatReader();
zxingReader.listVideoInputDevices()
.then((videoInputDevices) => {
const select = document.getElementById('camera-select');
select.innerHTML = '';
if (videoInputDevices.length > 0) {
select.style.display = 'block';
videoInputDevices.forEach((device) => {
const option = document.createElement('option');
option.value = device.deviceId;
option.text = device.label || `Kamera ${select.length + 1}`;
select.appendChild(option);
});
const backCamera = videoInputDevices.find(d =>
d.label.toLowerCase().includes('back') ||
d.label.toLowerCase().includes('arka') ||
d.label.toLowerCase().includes('environment')
);
if (backCamera) {
selectedDeviceId = backCamera.deviceId;
select.value = selectedDeviceId;
} else {
selectedDeviceId = videoInputDevices[0].deviceId;
}
updateCamStatus("Kamera hazır. 'Kamerayı Başlat' butonuna basın.");
} else {
updateCamStatus("Kamera cihazı algılanamadı. 'Fotoğraf Çek / QR Yükle' butonunu kullanabilirsiniz.");
}
})
.catch((err) => {
updateCamStatus("Kamera izinleri alınamadı. Lütfen tarayıcınızda kameraya izin verin.", false, true);
});
}
}
function changeCameraDevice(deviceId) {
selectedDeviceId = deviceId;
if (document.getElementById('video-preview').style.display === 'block') {
stopScannerEngine();
startScannerEngine();
}
}
// Start Scanner Engine
function startScannerEngine() {
updateCamStatus("Kamera açılıyor, lütfen bekleyin...");
document.getElementById('scanner-placeholder').style.display = 'none';
const videoElem = document.getElementById('video-preview');
videoElem.style.display = 'block';
document.getElementById('viewfinder-target').style.display = 'block';
document.getElementById('start-cam-btn').style.display = 'none';
document.getElementById('stop-cam-btn').style.display = 'flex';
if (!zxingReader) {
zxingReader = new ZXing.BrowserMultiFormatReader();
}
const constraints = selectedDeviceId
? { video: { deviceId: { exact: selectedDeviceId } } }
: { video: { facingMode: { ideal: "environment" } } };
navigator.mediaDevices.getUserMedia(constraints)
.then((stream) => {
videoElem.srcObject = stream;
videoElem.play();
updateCamStatus("Kamera Aktif. Barkod veya QR kodu kutunun ortasındaki yeşil çizgiye tutun.", true);
// Start continuous decoding from video stream
zxingReader.decodeFromVideoElement(videoElem, (result, err) => {
if (result) {
onScanSuccess(result.getText());
}
});
})
.catch((err) => {
// Fallback to basic video stream without constraints
navigator.mediaDevices.getUserMedia({ video: true })
.then((stream) => {
videoElem.srcObject = stream;
videoElem.play();
updateCamStatus("Kamera Aktif. Barkodu yeşil çizgiye tutun.", true);
zxingReader.decodeFromVideoElement(videoElem, (result, err) => {
if (result) onScanSuccess(result.getText());
});
})
.catch((err2) => {
updateCamStatus("Kamera erişimi reddedildi: " + err2.message + ". 'Fotoğraf Çek' butonunu deneyin.", false, true);
stopScannerEngine();
});
});
}
function stopScannerEngine() {
const videoElem = document.getElementById('video-preview');
if (videoElem.srcObject) {
videoElem.srcObject.getTracks().forEach(track => track.stop());
videoElem.srcObject = null;
}
if (zxingReader) {
try { zxingReader.reset(); } catch(e) {}
}
videoElem.style.display = 'none';
document.getElementById('viewfinder-target').style.display = 'none';
document.getElementById('scanner-placeholder').style.display = 'flex';
document.getElementById('start-cam-btn').style.display = 'flex';
document.getElementById('stop-cam-btn').style.display = 'none';
updateCamStatus("Kamera durduruldu.");
}
// Trigger File Upload Photo Barcode Scan
function triggerFileScan() {
document.getElementById('qr-file-input').click();
}
function scanImageFile(input) {
if (!input.files || input.files.length === 0) return;
const imageFile = input.files[0];
showToast("Fotoğraftaki barkod taranıyor...");
updateCamStatus("Fotoğraf taranıyor...");
if (!zxingReader) {
zxingReader = new ZXing.BrowserMultiFormatReader();
}
const imgUrl = URL.createObjectURL(imageFile);
const imgEl = document.createElement('img');
imgEl.src = imgUrl;
imgEl.onload = () => {
zxingReader.decodeFromImageElement(imgEl)
.then((result) => {
onScanSuccess(result.getText());
URL.revokeObjectURL(imgUrl);
})
.catch((err) => {
// Try HTML5 QR Code file scan fallback
if (typeof Html5Qrcode !== 'undefined') {
const html5Qr = new Html5Qrcode("scanner-container");
html5Qr.scanFile(imageFile, true)
.then(decodedText => onScanSuccess(decodedText))
.catch(() => {
updateCamStatus("Fotoğraftaki barkod okunamadı. Lütfen barkodu daha yakından çekin.", false, true);
alert("Fotoğraftaki barkod/QR okunamadı. Lütfen etiket üzerindeki barkoda yaklaşıp daha net fotoğraf çekin.");
});
} else {
updateCamStatus("Fotoğraftaki barkod okunamadı.", false, true);
}
});
};
}
function onScanSuccess(decodedText) {
playBeep();
showToast("Barkod Okundu: " + decodedText);
updateCamStatus("Barkod başarıyla okundu: " + decodedText, true);
document.getElementById('code-input').value = decodedText;
document.getElementById('clear-btn').style.display = 'block';
stopScannerEngine();
lookupCodeLive(decodedText);
}
// Audio Beep Feedback
function playBeep() {
try {
const ctx = new (window.AudioContext || window.webkitAudioContext)();
const osc = ctx.createOscillator();
const gain = ctx.createGain();
osc.type = 'sine';
osc.frequency.setValueAtTime(880, ctx.currentTime);
gain.gain.setValueAtTime(0.1, ctx.currentTime);
osc.connect(gain);
gain.connect(ctx.destination);
osc.start();
osc.stop(ctx.currentTime + 0.15);
} catch(e) {}
}
// Handle Search Input with debounce
function handleSearchInput(val) {
const clearBtn = document.getElementById('clear-btn');
clearTimeout(searchTimeout);
if (val.trim().length > 0) {
clearBtn.style.display = 'block';
searchTimeout = setTimeout(() => {
lookupCodeLive(val.trim());
}, 400);
} else {
clearBtn.style.display = 'none';
document.getElementById('product-card').classList.remove('active');
}
}
function clearSearch() {
document.getElementById('code-input').value = '';
document.getElementById('clear-btn').style.display = 'none';
document.getElementById('product-card').classList.remove('active');
}
function loadSample(code) {
document.getElementById('code-input').value = code;
document.getElementById('clear-btn').style.display = 'block';
lookupCodeLive(code);
}
// Live Internet & DB Lookup via Backend API (/api/oem-lookup)
function lookupCodeLive(code) {
document.getElementById('loading-box').style.display = 'block';
document.getElementById('product-card').classList.remove('active');
fetch(`/api/oem-lookup?code=${encodeURIComponent(code)}`)
.then(res => res.json())
.then(res => {
document.getElementById('loading-box').style.display = 'none';
if (res.success && res.data) {
renderProductCard(res.data);
} else {
alert("Ürün bulunamadı veya geçersiz kod.");
}
})
.catch(err => {
document.getElementById('loading-box').style.display = 'none';
alert("Arama servisine bağlanırken bir hata oluştu.");
});
}
// Render Product Card
function renderProductCard(p) {
currentProduct = p;
currentQty = 1;
document.getElementById('qty-input').value = currentQty;
document.getElementById('res-brand').innerText = p.brand || 'OEM BRAND';
document.getElementById('res-scanned-code').innerText = 'Sorgulanan Kod: ' + p.scanned;
document.getElementById('res-source').innerText = '🌐 ' + (p.source || 'Canlı İnternet Sorgusu');
document.getElementById('res-title').innerText = p.title;
document.getElementById('res-subtitle').innerText = p.subtitle || '';
document.getElementById('res-image').src = p.image || '/assets/oem/top_strut_mount.png';
document.getElementById('res-origin').innerText = p.origin || 'TR / İthal';
document.getElementById('res-side').innerText = p.side || 'Ön / Arka Aks';
document.getElementById('res-packaging').innerText = p.packaging || '1 Pcs Kutulu';
// Stock
document.getElementById('res-stock-text').innerText = `Stokta Var (${p.stock} Adet)`;
// OEM Tags
const oemContainer = document.getElementById('res-oem-tags');
oemContainer.innerHTML = '';
if (p.oems && p.oems.length > 0) {
p.oems.forEach(o => {
const span = document.createElement('span');
span.className = 'oem-tag';
span.innerText = o;
oemContainer.appendChild(span);
});
}
// Vehicles
const vehContainer = document.getElementById('res-vehicles');
vehContainer.innerHTML = '';
if (p.vehicles && p.vehicles.length > 0) {
p.vehicles.forEach(v => {
const span = document.createElement('span');
span.className = 'vehicle-badge';
span.innerHTML = `<i class="fa-solid fa-check" style="color:var(--accent-green)"></i> ${v}`;
vehContainer.appendChild(span);
});
}
// Price
const priceVal = p.price || 250;
const formattedPrice = priceVal.toLocaleString('tr-TR', { minimumFractionDigits: 2, maximumFractionDigits: 2 });
const vatPrice = (priceVal * 1.20).toLocaleString('tr-TR', { minimumFractionDigits: 2, maximumFractionDigits: 2 });
document.getElementById('res-price').innerText = '₺' + formattedPrice;
document.getElementById('res-price-vat').innerText = '₺' + vatPrice;
// Show Card
const card = document.getElementById('product-card');
card.classList.add('active');
card.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
}
// Quantity Picker
function changeQty(delta) {
currentQty = Math.max(1, currentQty + delta);
document.getElementById('qty-input').value = currentQty;
}
// Add To Cart Simulation
function addToCart() {
if (!currentProduct) return;
cartItems.push({
product: currentProduct,
qty: currentQty
});
updateCartUI();
showToast(`${currentQty} adet ${currentProduct.title} sepete eklendi!`);
}
function updateCartUI() {
const count = cartItems.reduce((acc, item) => acc + item.qty, 0);
const total = cartItems.reduce((acc, item) => acc + (item.product.price * item.qty), 0);
document.getElementById('cart-count').innerText = count;
document.getElementById('cart-total').innerText = '₺' + total.toLocaleString('tr-TR', { minimumFractionDigits: 2, maximumFractionDigits: 2 });
}
function checkoutSim() {
if (cartItems.length === 0) {
alert("Sepetiniz henüz boş! Lütfen önce bir ürün aratıp sepete ekleyin.");
return;
}
const total = cartItems.reduce((acc, item) => acc + (item.product.price * item.qty), 0);
alert(`🎉 Test Siparişi Başarıyla Başlatıldı!\nToplam Tutar: ₺${total.toLocaleString('tr-TR', {minimumFractionDigits:2})}\nÜrün Sayısı: ${cartItems.length}\n\nBu ekran Trunçgil OEM B2B sisteminin canlı arama ve sipariş fonksiyon testidir.`);
}
// Toast Helper
function showToast(msg) {
const toast = document.getElementById('toast');
document.getElementById('toast-msg').innerText = msg;
toast.classList.add('show');
setTimeout(() => {
toast.classList.remove('show');
}, 3000);
}
</script>
</body>
</html>