1474 lines
83 KiB
PHP
1474 lines
83 KiB
PHP
<!DOCTYPE html>
|
||
<html lang="tr" class="scroll-smooth">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=device-width, initial-scale=device-width, initial-scale=1.0">
|
||
<meta name="csrf-token" content="{{ csrf_token() }}">
|
||
<title>{{ $proposal->title }} - Trunçgil Teknoloji</title>
|
||
|
||
<!-- Google Fonts: Inter (Body), Outfit (Headings), Caveat (Signature Script) -->
|
||
<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=Caveat:wght@600&family=Inter:wght@300;400;500;600;700&family=Outfit:wght@400;500;600;700;800&display=swap" rel="stylesheet">
|
||
|
||
<!-- Tailwind CSS -->
|
||
<script src="https://cdn.tailwindcss.com"></script>
|
||
|
||
<script>
|
||
tailwind.config = {
|
||
darkMode: 'class',
|
||
theme: {
|
||
extend: {
|
||
fontFamily: {
|
||
sans: ['Inter', 'sans-serif'],
|
||
display: ['Outfit', 'sans-serif'],
|
||
signature: ['Caveat', 'cursive'],
|
||
},
|
||
colors: {
|
||
theme: {
|
||
indigo: '#4f46e5',
|
||
emerald: '#059669',
|
||
rose: '#f43f5e',
|
||
orange: '#ea580c',
|
||
amber: '#d97706',
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<!-- Styles for interactive elements -->
|
||
<style type="text/css">
|
||
/* Glassmorphism custom styles */
|
||
.glass-header {
|
||
background: rgba(255, 255, 255, 0.7);
|
||
backdrop-filter: blur(12px);
|
||
-webkit-backdrop-filter: blur(12px);
|
||
border-bottom: 1px solid rgba(0, 0, 0, 0.05);
|
||
}
|
||
.dark .glass-header {
|
||
background: rgba(15, 23, 42, 0.7);
|
||
border-bottom: 1px solid rgba(255, 255, 255, 0.05);
|
||
}
|
||
.glass-panel {
|
||
background: rgba(255, 255, 255, 0.4);
|
||
backdrop-filter: blur(8px);
|
||
-webkit-backdrop-filter: blur(8px);
|
||
border: 1px solid rgba(0, 0, 0, 0.05);
|
||
}
|
||
.dark .glass-panel {
|
||
background: rgba(30, 41, 59, 0.4);
|
||
border: 1px solid rgba(255, 255, 255, 0.05);
|
||
}
|
||
|
||
/* Active TOC indicators */
|
||
.toc-item.active {
|
||
color: var(--accent-color);
|
||
font-weight: 600;
|
||
}
|
||
.toc-item.active .toc-bullet {
|
||
transform: scale(1.3);
|
||
background-color: var(--accent-color);
|
||
box-shadow: 0 0 8px var(--accent-color-glow);
|
||
}
|
||
|
||
/* Rendered Markdown Styling */
|
||
.proposal-content h1 {
|
||
font-family: 'Outfit', sans-serif;
|
||
font-size: 2.25rem;
|
||
font-weight: 800;
|
||
color: #0f172a;
|
||
margin-top: 2rem;
|
||
margin-bottom: 1rem;
|
||
line-height: 1.2;
|
||
}
|
||
.dark .proposal-content h1 { color: #f8fafc; }
|
||
|
||
.proposal-content h2 {
|
||
font-family: 'Outfit', sans-serif;
|
||
font-size: 1.6rem;
|
||
font-weight: 700;
|
||
color: #1e293b;
|
||
margin-top: 2.5rem;
|
||
margin-bottom: 1rem;
|
||
padding-bottom: 0.5rem;
|
||
border-bottom: 1px solid rgba(0, 0, 0, 0.05);
|
||
line-height: 1.3;
|
||
}
|
||
.dark .proposal-content h2 {
|
||
color: #f1f5f9;
|
||
border-bottom: 1px solid rgba(255, 255, 255, 0.05);
|
||
}
|
||
|
||
.proposal-content h3 {
|
||
font-family: 'Outfit', sans-serif;
|
||
font-size: 1.25rem;
|
||
font-weight: 600;
|
||
color: #334155;
|
||
margin-top: 1.8rem;
|
||
margin-bottom: 0.75rem;
|
||
}
|
||
.dark .proposal-content h3 { color: #e2e8f0; }
|
||
|
||
.proposal-content p {
|
||
font-size: 1rem;
|
||
line-height: 1.7;
|
||
color: #475569;
|
||
margin-bottom: 1.25rem;
|
||
}
|
||
.dark .proposal-content p { color: #94a3b8; }
|
||
|
||
/* Tables inside Markdown styling */
|
||
.proposal-content table {
|
||
width: 100%;
|
||
margin: 2rem 0;
|
||
border-collapse: collapse;
|
||
font-size: 0.925rem;
|
||
border-radius: 12px;
|
||
overflow: hidden;
|
||
border: 1px solid rgba(0, 0, 0, 0.05);
|
||
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.05), 0 2px 4px -1px rgba(0, 0, 0, 0.03);
|
||
}
|
||
.dark .proposal-content table {
|
||
border: 1px solid rgba(255, 255, 255, 0.05);
|
||
box-shadow: none;
|
||
}
|
||
|
||
.proposal-content th {
|
||
background-color: #f8fafc;
|
||
color: #0f172a;
|
||
font-weight: 600;
|
||
padding: 12px 16px;
|
||
text-align: left;
|
||
border-bottom: 2px solid rgba(0, 0, 0, 0.05);
|
||
}
|
||
.dark .proposal-content th {
|
||
background-color: #1e293b;
|
||
color: #f8fafc;
|
||
border-bottom: 2px solid rgba(255, 255, 255, 0.05);
|
||
}
|
||
|
||
.proposal-content td {
|
||
padding: 14px 16px;
|
||
border-bottom: 1px solid rgba(0, 0, 0, 0.04);
|
||
color: #475569;
|
||
}
|
||
.dark .proposal-content td {
|
||
border-bottom: 1px solid rgba(255, 255, 255, 0.04);
|
||
color: #cbd5e1;
|
||
}
|
||
|
||
.proposal-content tr:last-child td {
|
||
border-bottom: none;
|
||
}
|
||
|
||
.proposal-content tr:nth-child(even) td {
|
||
background-color: rgba(248, 250, 252, 0.4);
|
||
}
|
||
.dark .proposal-content tr:nth-child(even) td {
|
||
background-color: rgba(30, 41, 59, 0.2);
|
||
}
|
||
|
||
/* Mermaid container custom styled */
|
||
.mermaid-container {
|
||
background: #ffffff;
|
||
border-radius: 16px;
|
||
margin: 2rem 0;
|
||
border: 1px solid rgba(0, 0, 0, 0.05);
|
||
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.05);
|
||
overflow: hidden;
|
||
position: relative;
|
||
}
|
||
.dark .mermaid-container {
|
||
background: #0f172a;
|
||
border: 1px solid rgba(255, 255, 255, 0.05);
|
||
box-shadow: none;
|
||
}
|
||
|
||
/* Mermaid zoom toolbar */
|
||
.mermaid-toolbar {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 4px;
|
||
padding: 8px 12px;
|
||
background: rgba(248, 250, 252, 0.9);
|
||
backdrop-filter: blur(8px);
|
||
border-bottom: 1px solid rgba(0, 0, 0, 0.06);
|
||
border-radius: 16px 16px 0 0;
|
||
}
|
||
.dark .mermaid-toolbar {
|
||
background: rgba(15, 23, 42, 0.9);
|
||
border-bottom: 1px solid rgba(255, 255, 255, 0.06);
|
||
}
|
||
.mermaid-toolbar-label {
|
||
font-size: 0.65rem;
|
||
font-weight: 700;
|
||
letter-spacing: 0.08em;
|
||
text-transform: uppercase;
|
||
color: #94a3b8;
|
||
margin-right: 4px;
|
||
flex: 1;
|
||
}
|
||
.mermaid-zoom-btn {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
width: 30px;
|
||
height: 30px;
|
||
border-radius: 8px;
|
||
border: 1px solid rgba(0, 0, 0, 0.08);
|
||
background: #fff;
|
||
color: #475569;
|
||
cursor: pointer;
|
||
font-size: 15px;
|
||
transition: all 0.15s ease;
|
||
user-select: none;
|
||
flex-shrink: 0;
|
||
}
|
||
.dark .mermaid-zoom-btn {
|
||
background: #1e293b;
|
||
border-color: rgba(255, 255, 255, 0.08);
|
||
color: #94a3b8;
|
||
}
|
||
.mermaid-zoom-btn:hover {
|
||
background: var(--accent-color, #4f46e5);
|
||
border-color: var(--accent-color, #4f46e5);
|
||
color: #fff;
|
||
transform: scale(1.05);
|
||
box-shadow: 0 2px 8px rgba(79, 70, 229, 0.3);
|
||
}
|
||
.mermaid-zoom-btn:active {
|
||
transform: scale(0.95);
|
||
}
|
||
.mermaid-zoom-pct {
|
||
font-size: 0.7rem;
|
||
font-weight: 700;
|
||
color: #64748b;
|
||
min-width: 38px;
|
||
text-align: center;
|
||
font-family: 'Inter', monospace;
|
||
}
|
||
.dark .mermaid-zoom-pct { color: #64748b; }
|
||
|
||
/* Mermaid svg canvas area */
|
||
.mermaid-canvas {
|
||
width: 100%;
|
||
height: 520px;
|
||
cursor: grab;
|
||
position: relative;
|
||
overflow: hidden;
|
||
}
|
||
.mermaid-canvas:active { cursor: grabbing; }
|
||
.mermaid-canvas .mermaid {
|
||
width: 100%;
|
||
height: 100%;
|
||
}
|
||
.mermaid-canvas svg {
|
||
display: block;
|
||
}
|
||
|
||
/* Fullscreen overlay */
|
||
.mermaid-fullscreen-overlay {
|
||
display: none;
|
||
position: fixed;
|
||
inset: 0;
|
||
z-index: 9999;
|
||
background: rgba(2, 6, 23, 0.92);
|
||
backdrop-filter: blur(12px);
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
.mermaid-fullscreen-overlay.active {
|
||
display: flex;
|
||
}
|
||
.mermaid-fullscreen-inner {
|
||
background: #fff;
|
||
border-radius: 20px;
|
||
overflow: hidden;
|
||
width: 92vw;
|
||
max-width: 1200px;
|
||
max-height: 90vh;
|
||
display: flex;
|
||
flex-direction: column;
|
||
box-shadow: 0 40px 80px rgba(0,0,0,0.5);
|
||
}
|
||
.dark .mermaid-fullscreen-inner {
|
||
background: #0f172a;
|
||
}
|
||
.mermaid-fullscreen-canvas {
|
||
flex: 1;
|
||
overflow: auto;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
padding: 2rem;
|
||
cursor: grab;
|
||
}
|
||
.mermaid-fullscreen-canvas:active { cursor: grabbing; }
|
||
.mermaid-fullscreen-canvas svg {
|
||
width: 100%;
|
||
height: auto;
|
||
}
|
||
|
||
/* Bullet lists */
|
||
.proposal-content ul, .proposal-content ol {
|
||
padding-left: 1.5rem;
|
||
margin-bottom: 1.5rem;
|
||
color: #475569;
|
||
}
|
||
.dark .proposal-content ul, .dark .proposal-content ol {
|
||
color: #94a3b8;
|
||
}
|
||
.proposal-content ul {
|
||
list-style-type: disc;
|
||
}
|
||
.proposal-content ol {
|
||
list-style-type: decimal;
|
||
}
|
||
.proposal-content li {
|
||
margin-bottom: 0.5rem;
|
||
line-height: 1.6;
|
||
}
|
||
|
||
/* Print styles */
|
||
@media print {
|
||
body {
|
||
background: white !important;
|
||
color: black !important;
|
||
font-size: 11pt !important;
|
||
}
|
||
.no-print {
|
||
display: none !important;
|
||
}
|
||
.print-container {
|
||
width: 100% !important;
|
||
max-width: 100% !important;
|
||
margin: 0 !important;
|
||
padding: 0 !important;
|
||
box-shadow: none !important;
|
||
}
|
||
.proposal-content h1, .proposal-content h2, .proposal-content h3 {
|
||
color: black !important;
|
||
page-break-after: avoid;
|
||
}
|
||
.proposal-content h2 {
|
||
margin-top: 30pt !important;
|
||
page-break-before: always;
|
||
}
|
||
.proposal-content table {
|
||
page-break-inside: avoid !important;
|
||
}
|
||
.alert-card {
|
||
border-color: #ccc !important;
|
||
background: #f9f9f9 !important;
|
||
color: black !important;
|
||
page-break-inside: avoid !important;
|
||
}
|
||
/* Add custom header/footer for print */
|
||
@page {
|
||
margin: 2cm;
|
||
@top-center {
|
||
content: "Trunçgil Teknoloji A.Ş. Resmi Teklif Dökümanı";
|
||
font-size: 8pt;
|
||
color: #666;
|
||
}
|
||
@bottom-right {
|
||
content: counter(page);
|
||
font-size: 8pt;
|
||
}
|
||
}
|
||
}
|
||
</style>
|
||
|
||
@php
|
||
$accent = $proposal->meta['accent_color'] ?? 'indigo';
|
||
$accentColor = match($accent) {
|
||
'emerald' => '#059669',
|
||
'cyberpunk' => '#ec4899',
|
||
'coral' => '#ea580c',
|
||
'amber' => '#d97706',
|
||
default => '#4f46e5',
|
||
};
|
||
$accentColorGlow = match($accent) {
|
||
'emerald' => 'rgba(5, 150, 105, 0.2)',
|
||
'cyberpunk' => 'rgba(236, 72, 153, 0.2)',
|
||
'coral' => 'rgba(234, 88, 12, 0.2)',
|
||
'amber' => 'rgba(217, 119, 6, 0.2)',
|
||
default => 'rgba(79, 70, 229, 0.2)',
|
||
};
|
||
$themeClass = match($accent) {
|
||
'emerald' => 'theme-emerald bg-emerald-600 hover:bg-emerald-700 focus:ring-emerald-500 text-emerald-600 dark:text-emerald-400 border-emerald-500/20',
|
||
'cyberpunk' => 'theme-rose bg-rose-500 hover:bg-rose-600 focus:ring-rose-400 text-rose-500 dark:text-rose-400 border-rose-500/20',
|
||
'coral' => 'theme-orange bg-orange-600 hover:bg-orange-700 focus:ring-orange-500 text-orange-600 dark:text-orange-400 border-orange-500/20',
|
||
'amber' => 'theme-amber bg-amber-600 hover:bg-amber-700 focus:ring-amber-500 text-amber-600 dark:text-amber-400 border-amber-500/20',
|
||
default => 'theme-indigo bg-indigo-600 hover:bg-indigo-700 focus:ring-indigo-500 text-indigo-600 dark:text-indigo-400 border-indigo-500/20',
|
||
};
|
||
$gradient = match($accent) {
|
||
'emerald' => 'from-emerald-600 to-teal-600 dark:from-emerald-500 dark:to-teal-500',
|
||
'cyberpunk' => 'from-rose-500 to-cyan-500 dark:from-rose-400 dark:to-cyan-400',
|
||
'coral' => 'from-orange-600 to-rose-600 dark:from-orange-500 dark:to-rose-500',
|
||
'amber' => 'from-amber-500 to-yellow-600 dark:from-amber-400 dark:to-yellow-500',
|
||
default => 'from-indigo-600 to-purple-600 dark:from-indigo-500 dark:to-purple-500',
|
||
};
|
||
$glowGradients = match($accent) {
|
||
'emerald' => 'shadow-emerald-500/20 dark:shadow-emerald-500/10',
|
||
'cyberpunk' => 'shadow-rose-500/20 dark:shadow-rose-500/10',
|
||
'coral' => 'shadow-orange-500/20 dark:shadow-orange-500/10',
|
||
'amber' => 'shadow-amber-500/20 dark:shadow-amber-500/10',
|
||
default => 'shadow-indigo-500/20 dark:shadow-indigo-500/10',
|
||
};
|
||
@endphp
|
||
|
||
<script>
|
||
// Set dynamic theme color variables
|
||
document.documentElement.style.setProperty('--accent-color', '{{ $accentColor }}');
|
||
document.documentElement.style.setProperty('--accent-color-glow', '{{ $accentColorGlow }}');
|
||
|
||
// Dark Mode Logic
|
||
if (localStorage.getItem('color-theme') === 'dark' || (!('color-theme' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
|
||
document.documentElement.classList.add('dark');
|
||
} else {
|
||
document.documentElement.classList.remove('dark');
|
||
}
|
||
</script>
|
||
</head>
|
||
<body class="bg-slate-50 dark:bg-slate-900 text-slate-800 dark:text-slate-200 min-h-screen font-sans transition-colors duration-300">
|
||
|
||
<!-- Standalone Premium Navbar -->
|
||
<header class="no-print sticky top-0 z-40 w-full glass-header transition-colors duration-300">
|
||
<div class="w-full px-4 sm:px-8 lg:px-12 h-18 flex items-center justify-between py-4">
|
||
|
||
<!-- Branding: Trunçgil & Client -->
|
||
<div class="flex items-center gap-3">
|
||
<a href="https://www.truncgil.com.tr" target="_blank" class="flex items-center gap-2">
|
||
<img src="{{ asset('logos/truncgil-yatay.svg') }}" alt="Trunçgil Teknoloji" class="h-8 w-auto dark:hidden">
|
||
<img src="{{ asset('logos/truncgil-yatay-dark.svg') }}" alt="Trunçgil Teknoloji" class="h-8 w-auto hidden dark:block">
|
||
</a>
|
||
|
||
<!-- Glowing Pulse Line Separator -->
|
||
<div class="h-6 w-px bg-slate-300 dark:bg-slate-700 relative flex items-center">
|
||
<span class="absolute w-2 h-2 rounded-full bg-theme-{{ $accent }} animate-ping opacity-75 -left-[3.5px]"></span>
|
||
</div>
|
||
|
||
<!-- Client Info -->
|
||
<div class="flex items-center gap-2">
|
||
<span class="text-xs bg-slate-100 dark:bg-slate-800 text-slate-600 dark:text-slate-400 font-semibold px-2.5 py-1 rounded-md border border-slate-200/50 dark:border-slate-700/50">
|
||
{{ $proposal->client_name }}
|
||
</span>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Header Actions -->
|
||
<div class="flex items-center gap-3">
|
||
<!-- Theme Toggle Button -->
|
||
<button id="theme-toggle" type="button" class="text-slate-500 dark:text-slate-400 hover:bg-slate-100 dark:hover:bg-slate-800 focus:outline-none focus:ring-4 focus:ring-slate-200 dark:focus:ring-slate-700 rounded-lg text-sm p-2.5">
|
||
<svg id="theme-toggle-dark-icon" class="hidden w-5 h-5" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path d="M17.293 13.293A8 8 0 016.707 2.707a8.001 8.001 0 1010.586 10.586z"></path></svg>
|
||
<svg id="theme-toggle-light-icon" class="hidden w-5 h-5" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path d="M10 2a1 1 0 011 1v1a1 1 0 11-2 0V3a1 1 0 011-1zm4 8a4 4 0 11-8 0 4 4 0 018 0zm-.464 4.95l.707.707a1 1 0 001.414-1.414l-.707-.707a1 1 0 00-1.414 1.414zm2.12-10.607a1 1 0 010 1.414l-.707.707a1 1 0 11-1.414-1.414l.707-.707a1 1 0 011.414 0zM17 11a1 1 0 100-2h-1a1 1 0 100 2h1zm-7 4a1 1 0 011 1v1a1 1 0 11-2 0v-1a1 1 0 011-1zM5.05 6.464a1 1 0 10-1.414 1.414l.707.707a1 1 0 001.414-1.414l-.707-.707zm1.414 8.486a1 1 0 01-1.414 0l-.707-.707a1 1 0 011.414-1.414l.707.707a1 1 0 010 1.414zM4 11a1 1 0 100-2H3a1 1 0 100 2h1z" fill-rule="evenodd" clip-rule="evenodd"></path></svg>
|
||
</button>
|
||
|
||
<!-- Print/PDF Button -->
|
||
<button onclick="window.print()" class="flex items-center gap-1.5 px-4 py-2 border border-slate-300 dark:border-slate-700 text-sm font-semibold rounded-lg hover:bg-slate-100 dark:hover:bg-slate-800 transition-colors">
|
||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2.2" stroke="currentColor" class="w-4 h-4">
|
||
<path stroke-linecap="round" stroke-linejoin="round" d="M6.72 13.82l-.24-2.073a.75.75 0 00-.73-.667H3.75a.75.75 0 00-.707.501l-1.121 3.364a.75.75 0 00.16.806l2.121 2.122a.75.75 0 00.805.159l3.364-1.121a.75.75 0 00.502-.707V13.82zm0 0h6.562m-6.562 0L8.4 18.06M17.28 13.82l.24-2.073a.75.75 0 01.73-.667h2.003a.75.75 0 01.707.501l1.121 3.364a.75.75 0 01-.16.806l-2.121 2.122a.75.75 0 01-.805.159l-3.364-1.121a.75.75 0 01-.502-.707V13.82zm0 0H10.72m6.56 0l-1.68 4.24" />
|
||
<path stroke-linecap="round" stroke-linejoin="round" d="M12 3v13.5m0-13.5L8.25 6.75M12 3l3.75 3.75" />
|
||
</svg>
|
||
<span>Yazdır / PDF</span>
|
||
</button>
|
||
</div>
|
||
|
||
</div>
|
||
</header>
|
||
|
||
<!-- Main Container -->
|
||
<main class="w-full px-4 sm:px-8 lg:px-12 py-8 md:py-12 print-container">
|
||
|
||
<!-- Executive Metadata & Cover Card -->
|
||
<section class="mb-10 p-6 sm:p-8 rounded-2xl bg-white dark:bg-slate-800 border border-slate-200/60 dark:border-slate-700/50 shadow-sm relative overflow-hidden transition-all duration-300">
|
||
<!-- Background Glow Accent -->
|
||
<div class="absolute top-0 right-0 w-80 h-80 bg-gradient-to-br {{ $gradient }} rounded-full blur-3xl opacity-[0.08] dark:opacity-[0.05] pointer-events-none"></div>
|
||
|
||
<div class="relative z-10">
|
||
<div class="flex flex-col md:flex-row md:items-center justify-between gap-4 border-b border-slate-100 dark:border-slate-700/50 pb-6 mb-6">
|
||
<div>
|
||
<span class="text-xs uppercase tracking-wider font-extrabold text-theme-{{ $accent }} dark:text-theme-{{ $accent }} inline-flex items-center gap-1.5 mb-2">
|
||
<span class="w-1.5 h-1.5 rounded-full bg-theme-{{ $accent }} animate-pulse"></span>
|
||
TEKNİK VE MALİ TEKLİF DOKÜMANI
|
||
</span>
|
||
<h1 class="text-2xl sm:text-3xl font-display font-extrabold text-slate-900 dark:text-white leading-tight">
|
||
{{ $proposal->title }}
|
||
</h1>
|
||
</div>
|
||
|
||
<!-- Dynamic Status Badge -->
|
||
<div class="flex items-center gap-2">
|
||
@php
|
||
$statusLabel = match($proposal->status) {
|
||
'draft' => 'Taslak',
|
||
'sent' => 'İletildi / Değerlendirmede',
|
||
'accepted' => 'Onaylandı',
|
||
'rejected' => 'Reddedildi',
|
||
'revised' => 'Revizyon Talep Edildi',
|
||
default => 'Taslak',
|
||
};
|
||
$statusColor = match($proposal->status) {
|
||
'draft' => 'bg-slate-100 text-slate-700 border-slate-200 dark:bg-slate-800 dark:text-slate-300 dark:border-slate-700',
|
||
'sent' => 'bg-blue-50 text-blue-700 border-blue-200 dark:bg-blue-950/20 dark:text-blue-400 dark:border-blue-800',
|
||
'accepted' => 'bg-emerald-50 text-emerald-700 border-emerald-200 dark:bg-emerald-950/20 dark:text-emerald-400 dark:border-emerald-800',
|
||
'rejected' => 'bg-rose-50 text-rose-700 border-rose-200 dark:bg-rose-950/20 dark:text-rose-400 dark:border-rose-800',
|
||
'revised' => 'bg-amber-50 text-amber-700 border-amber-200 dark:bg-amber-950/20 dark:text-amber-400 dark:border-amber-800',
|
||
default => 'bg-slate-100 text-slate-700 border-slate-200',
|
||
};
|
||
@endphp
|
||
<span class="px-3.5 py-1.5 text-xs font-bold rounded-full border {{ $statusColor }} shadow-sm">
|
||
{{ $statusLabel }}
|
||
</span>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Information Grid -->
|
||
<div class="grid grid-cols-2 sm:grid-cols-4 gap-6 text-sm">
|
||
<div>
|
||
<div class="text-slate-400 dark:text-slate-500 font-semibold mb-1">Referans Kodu</div>
|
||
<div class="font-bold text-slate-800 dark:text-slate-200 uppercase">{{ $proposal->slug }}</div>
|
||
</div>
|
||
<div>
|
||
<div class="text-slate-400 dark:text-slate-500 font-semibold mb-1">Teklif Tarihi</div>
|
||
<div class="font-bold text-slate-800 dark:text-slate-200">{{ $proposal->created_at->format('d.m.Y') }}</div>
|
||
</div>
|
||
<div>
|
||
<div class="text-slate-400 dark:text-slate-500 font-semibold mb-1">Son Geçerlilik</div>
|
||
<div class="font-bold text-slate-800 dark:text-slate-200">
|
||
{{ $proposal->valid_until ? $proposal->valid_until->format('d.m.Y') : 'Belirtilmedi' }}
|
||
</div>
|
||
</div>
|
||
<div>
|
||
<div class="text-slate-400 dark:text-slate-500 font-semibold mb-1">Okunma / İnceleme</div>
|
||
<div class="font-bold text-slate-800 dark:text-slate-200 inline-flex items-center gap-1">
|
||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" class="w-4 h-4 text-slate-400">
|
||
<path stroke-linecap="round" stroke-linejoin="round" d="M2.036 12.322a1.012 1.012 0 010-.639C3.423 7.51 7.36 4.5 12 4.5c4.638 0 8.573 3.007 9.963 7.178.07.207.07.431 0 .639C20.577 16.49 16.64 19.5 12 19.5c-4.638 0-8.573-3.007-9.963-7.178z" />
|
||
<path stroke-linecap="round" stroke-linejoin="round" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
|
||
</svg>
|
||
<span>{{ $proposal->views_count }} kez</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- Main Workspace: Sidebar & Content -->
|
||
<div class="flex flex-col lg:flex-row gap-8 items-start relative">
|
||
|
||
<!-- Dynamic Left Navigation (Sidebar) -->
|
||
<aside class="no-print w-full lg:w-72 shrink-0 lg:sticky lg:top-24 max-h-[calc(100vh-140px)] overflow-y-auto hidden lg:block scrollbar-thin">
|
||
<div class="p-5 rounded-2xl bg-white dark:bg-slate-800 border border-slate-200/60 dark:border-slate-700/50 shadow-sm flex flex-col gap-5">
|
||
|
||
<div class="font-display font-bold text-sm tracking-wider uppercase text-slate-400 dark:text-slate-500">
|
||
Teklif İçindekiler
|
||
</div>
|
||
|
||
<!-- Dynamic TOC List (Populated via JS) -->
|
||
<nav id="toc-nav" class="flex flex-col gap-1.5 text-sm">
|
||
<!-- Filled dynamically -->
|
||
<div class="text-slate-400 dark:text-slate-500 italic py-2">Başlıklar çıkarılıyor...</div>
|
||
</nav>
|
||
|
||
<!-- Interactive Accents Panel inside Sidebar -->
|
||
@if($proposal->total_price)
|
||
<div class="border-t border-slate-100 dark:border-slate-700/50 pt-4 mt-2">
|
||
<div class="font-display font-bold text-xs tracking-wider uppercase text-slate-400 dark:text-slate-500 mb-3">
|
||
Finansal Özet
|
||
</div>
|
||
|
||
<!-- Sidebar Price Box -->
|
||
<div class="p-3.5 rounded-xl bg-slate-50 dark:bg-slate-900 border border-slate-100 dark:border-slate-800/80">
|
||
<div class="text-xs text-slate-400 dark:text-slate-500 font-semibold mb-1">Toplam Bütçe (KDV Hariç)</div>
|
||
<div class="text-xl font-display font-extrabold text-slate-900 dark:text-white">
|
||
{{ number_format($proposal->total_price, 2, ',', '.') }} {{ $proposal->currency === 'USD' ? 'USD' : ($proposal->currency === 'EUR' ? 'EUR' : 'TL') }}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
@endif
|
||
|
||
<!-- Action Guide -->
|
||
<div class="text-xs text-slate-400 leading-relaxed border-t border-slate-100 dark:border-slate-700/50 pt-4">
|
||
Bu teklifi dijital olarak onaylamak veya revizyon talep etmek için sayfanın en altında bulunan **İş Birliği & Karar Terminalini** kullanabilirsiniz.
|
||
</div>
|
||
|
||
</div>
|
||
</aside>
|
||
|
||
<!-- Proposal Body (Main Area) -->
|
||
<div class="flex-1 w-full flex flex-col gap-8">
|
||
|
||
<!-- Main Content Card -->
|
||
<article class="p-6 sm:p-10 rounded-2xl bg-white dark:bg-slate-800 border border-slate-200/60 dark:border-slate-700/50 shadow-sm relative overflow-hidden transition-all duration-300">
|
||
|
||
<!-- Markdown Output Area -->
|
||
<div id="proposal-markdown" class="proposal-content leading-relaxed transition-colors duration-300">
|
||
<!-- Will be populated via Marked.js dynamically -->
|
||
<div class="flex flex-col items-center justify-center py-20 text-slate-400">
|
||
<div class="w-8 h-8 border-4 border-t-transparent border-theme-{{ $accent }} rounded-full animate-spin mb-4"></div>
|
||
<span class="font-medium text-sm">Teklif dökümanı güvenli olarak yükleniyor...</span>
|
||
</div>
|
||
</div>
|
||
|
||
</article>
|
||
|
||
<!-- Interactive pricing & Gaziantep Teknopark %0 KDV muafiyeti box -->
|
||
@if($proposal->total_price && ($proposal->meta['show_calculator'] ?? true))
|
||
<section class="p-6 sm:p-8 rounded-2xl bg-white dark:bg-slate-800 border border-slate-200/60 dark:border-slate-700/50 shadow-sm relative overflow-hidden transition-all duration-300">
|
||
<div class="absolute top-0 left-0 w-full h-1 bg-gradient-to-r {{ $gradient }}"></div>
|
||
|
||
<div class="flex flex-col md:flex-row md:items-center justify-between gap-6">
|
||
|
||
<!-- Copy Content -->
|
||
<div class="max-w-md">
|
||
<div class="flex items-center gap-2 mb-2 text-xs font-bold text-emerald-600 dark:text-emerald-400 uppercase tracking-widest">
|
||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2.5" stroke="currentColor" class="w-4 h-4">
|
||
<path stroke-linecap="round" stroke-linejoin="round" d="M9 12.75L11.25 15 15 9.75M21 12c0 1.268-.63 2.39-1.593 3.068a3.745 3.745 0 01-1.043 3.296 3.745 3.745 0 01-3.296 1.043A3.745 3.745 0 0112 21c-1.268 0-2.39-.63-3.068-1.593a3.746 3.746 0 01-3.296-1.043 3.745 3.745 0 01-1.043-3.296A3.745 3.745 0 013 12c0-1.268.63-2.39 1.593-3.068a3.745 3.745 0 011.043-3.296 3.746 3.746 0 013.296-1.043A3.746 3.746 0 0112 3c1.268 0 2.39.63 3.068 1.593a3.746 3.746 0 013.296 1.043 3.746 3.746 0 011.043 3.296A3.745 3.745 0 0121 12z" />
|
||
</svg>
|
||
<span>Teknopark KDV Muafiyeti Avantajı</span>
|
||
</div>
|
||
<h3 class="text-xl font-display font-bold text-slate-900 dark:text-white mb-2">
|
||
4691 Sayılı Kanun KDV Muafiyeti
|
||
</h3>
|
||
<p class="text-sm text-slate-500 dark:text-slate-400 leading-relaxed mb-0">
|
||
Trunçgil Teknoloji, Gaziantep Üniversitesi Teknoparkı bünyesinde AR-GE yürüten tescilli bir kuruluştur. Kanun kapsamında geliştirdiğimiz yazılım çözümleri KDV'den (%0 KDV) istisnadır. Bu sayede maliyetlerinizde doğrudan avantaj sağlarsınız.
|
||
</p>
|
||
</div>
|
||
|
||
<!-- Interactive Box with Toggle Switch -->
|
||
<div class="w-full md:w-80 shrink-0 p-5 rounded-2xl bg-slate-50 dark:bg-slate-900 border border-slate-100 dark:border-slate-800 flex flex-col gap-4">
|
||
<div class="flex items-center justify-between">
|
||
<span class="text-xs text-slate-400 dark:text-slate-500 font-bold uppercase">KDV Muafiyeti Göster</span>
|
||
|
||
<!-- Toggle Switch -->
|
||
<label class="relative inline-flex items-center cursor-pointer">
|
||
<input type="checkbox" id="kdv-toggle" class="sr-only peer" checked>
|
||
<div class="w-11 h-6 bg-slate-200 peer-focus:outline-none rounded-full peer dark:bg-slate-700 peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-slate-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all dark:border-slate-600 peer-checked:bg-emerald-500"></div>
|
||
</label>
|
||
</div>
|
||
|
||
<div class="border-t border-slate-200/50 dark:border-slate-800/80 pt-3">
|
||
<div class="text-xs text-slate-400 dark:text-slate-500 font-semibold mb-1" id="kdv-label">Teknopark Avantajlı Fiyat (%0 KDV)</div>
|
||
<div class="text-2xl font-display font-extrabold text-slate-900 dark:text-white" id="kdv-price">
|
||
{{ number_format($proposal->total_price, 2, ',', '.') }} {{ $proposal->currency === 'USD' ? 'USD' : ($proposal->currency === 'EUR' ? 'EUR' : 'TL') }}
|
||
</div>
|
||
</div>
|
||
|
||
<div class="p-2.5 rounded-lg bg-emerald-50 dark:bg-emerald-950/20 text-emerald-800 dark:text-emerald-400 border border-emerald-200/20 text-xs font-semibold leading-relaxed flex items-start gap-2" id="kdv-benefit-box">
|
||
<i data-lucide="sparkles" class="w-4 h-4 shrink-0 text-emerald-500"></i>
|
||
<span>
|
||
%20 standart KDV oranına kıyasla toplamda <strong id="kdv-benefit-amount">{{ number_format($proposal->total_price * 0.20, 2, ',', '.') }}</strong> kazanç sağladınız.
|
||
</span>
|
||
</div>
|
||
</div>
|
||
|
||
</div>
|
||
</section>
|
||
@endif
|
||
|
||
<!-- Client Interaction Terminal (Approve & Feedback Form) -->
|
||
<section id="terminal-card" class="p-6 sm:p-10 rounded-2xl bg-white dark:bg-slate-800 border border-slate-200/60 dark:border-slate-700/50 shadow-sm relative overflow-hidden transition-all duration-300">
|
||
<div class="absolute top-0 right-0 w-96 h-96 bg-gradient-to-br {{ $gradient }} rounded-full blur-3xl opacity-[0.06] dark:opacity-[0.03] pointer-events-none"></div>
|
||
|
||
<div class="relative z-10" id="terminal-content">
|
||
|
||
<!-- Case: Already Accepted -->
|
||
@if($proposal->status === 'accepted')
|
||
<div class="flex flex-col items-center justify-center text-center py-6">
|
||
<div class="w-16 h-16 rounded-full bg-emerald-100 dark:bg-emerald-950/30 text-emerald-500 flex items-center justify-center mb-4 border border-emerald-500/20 shadow-sm">
|
||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2.5" stroke="currentColor" class="w-8 h-8">
|
||
<path stroke-linecap="round" stroke-linejoin="round" d="M9 12.75L11.25 15 15 9.75M21 12c0 1.268-.63 2.39-1.593 3.068a3.745 3.745 0 01-1.043 3.296 3.745 3.745 0 01-3.296 1.043A3.745 3.745 0 0112 21c-1.268 0-2.39-.63-3.068-1.593a3.746 3.746 0 01-3.296-1.043 3.745 3.745 0 01-1.043-3.296A3.745 3.745 0 013 12c0-1.268.63-2.39 1.593-3.068a3.745 3.745 0 011.043-3.296 3.746 3.746 0 013.296-1.043A3.746 3.746 0 0112 3c1.268 0 2.39.63 3.068 1.593a3.746 3.746 0 013.296 1.043 3.746 3.746 0 011.043 3.296A3.745 3.745 0 0121 12z" />
|
||
</svg>
|
||
</div>
|
||
<h3 class="text-2xl font-display font-extrabold text-slate-900 dark:text-white mb-2">
|
||
Teklif Başarıyla Onaylanmıştır
|
||
</h3>
|
||
<p class="text-sm text-slate-500 dark:text-slate-400 max-w-lg mb-6 leading-relaxed">
|
||
Bu döküman, yetkiliniz <strong class="text-slate-800 dark:text-slate-200">{{ $proposal->accepted_name }}</strong> tarafından <strong class="text-slate-800 dark:text-slate-200">{{ $proposal->accepted_at->format('d.m.Y H:i') }}</strong> tarihinde resmi olarak onaylanmış ve dijital olarak imzalanmıştır.
|
||
</p>
|
||
|
||
<!-- Digital Signature Stamp -->
|
||
<div class="px-6 py-4 rounded-xl border border-slate-200 dark:border-slate-700 bg-slate-50 dark:bg-slate-900 max-w-sm w-full relative">
|
||
<div class="absolute -top-2.5 left-4 px-2 bg-white dark:bg-slate-800 text-[10px] font-bold text-slate-400 tracking-wider rounded uppercase">Dijital İmza Mührü</div>
|
||
<div class="font-signature text-3xl text-theme-{{ $accent }} dark:text-theme-{{ $accent }} py-2 select-none">
|
||
{{ $proposal->accepted_name }}
|
||
</div>
|
||
<div class="text-[9px] text-slate-400 dark:text-slate-500 font-semibold border-t border-slate-200/50 dark:border-slate-800/80 pt-2 flex justify-between">
|
||
<span>DOKÜMAN DOĞRULAMA KODU:</span>
|
||
<span class="font-mono text-slate-500">{{ substr(sha1($proposal->uuid . $proposal->accepted_at), 0, 16) }}</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
@else
|
||
|
||
<!-- Case: Not Accepted Yet -->
|
||
<div class="border-b border-slate-100 dark:border-slate-700 pb-5 mb-6">
|
||
<h3 class="text-xl font-display font-bold text-slate-900 dark:text-white mb-1">
|
||
İş Birliği & Karar Arayüzü
|
||
</h3>
|
||
<p class="text-sm text-slate-400 dark:text-slate-500 leading-relaxed mb-0">
|
||
Aşağıdaki butonları kullanarak teklifi doğrudan onaylayıp dijital olarak imzalayabilir ya da ekibimize revizyon notlarınızı bırakabilirsiniz.
|
||
</p>
|
||
</div>
|
||
|
||
<!-- Navigation tabs -->
|
||
<div class="flex gap-4 mb-6">
|
||
<button onclick="switchTab('approve')" id="tab-btn-approve" class="flex-1 sm:flex-initial flex items-center justify-center gap-2 px-5 py-3 rounded-xl font-semibold text-sm transition-all duration-200 border text-white bg-slate-900 dark:bg-white dark:text-slate-900 hover:shadow-lg focus:ring-4 focus:ring-slate-300">
|
||
<i data-lucide="check-circle" class="w-4 h-4"></i>
|
||
<span>Teklifi Onayla & İmzala</span>
|
||
</button>
|
||
<button onclick="switchTab('feedback')" id="tab-btn-feedback" class="flex-1 sm:flex-initial flex items-center justify-center gap-2 px-5 py-3 rounded-xl font-semibold text-sm transition-all duration-200 border border-slate-200 dark:border-slate-700 text-slate-700 dark:text-slate-300 hover:bg-slate-50 dark:hover:bg-slate-800">
|
||
<i data-lucide="message-square" class="w-4 h-4"></i>
|
||
<span>Revizyon / Görüş İlet</span>
|
||
</button>
|
||
</div>
|
||
|
||
<!-- Action Tab 1: Approval Form -->
|
||
<div id="tab-pane-approve" class="action-pane transition-opacity duration-300">
|
||
<form id="approve-form" class="space-y-4">
|
||
<div class="max-w-md">
|
||
<label for="approve-name" class="block text-sm font-semibold text-slate-600 dark:text-slate-300 mb-2">Ad Soyad / Unvan</label>
|
||
<input type="text" id="approve-name" name="name" required placeholder="Örn: Ümit Tunç (Kurucu)" class="w-full px-4 py-3 text-sm rounded-xl border border-slate-200 dark:border-slate-700 bg-transparent focus:ring-4 focus:ring-slate-200/50 dark:focus:ring-slate-700/50 focus:border-slate-500 outline-none transition-all">
|
||
</div>
|
||
|
||
<!-- Real-time Signature Preview -->
|
||
<div class="max-w-md p-4 rounded-xl border border-dashed border-slate-200 dark:border-slate-700 bg-slate-50/50 dark:bg-slate-900/30 flex flex-col justify-center min-h-[90px] relative">
|
||
<span class="absolute top-2 left-3 text-[10px] font-bold text-slate-400 uppercase tracking-wider">İmza Önizleme</span>
|
||
<div id="signature-preview" class="font-signature text-3xl text-theme-{{ $accent }} dark:text-theme-{{ $accent }} text-center pt-2 select-none italic text-slate-300 dark:text-slate-600">
|
||
İmzanız burada belirecektir
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Secure Checkbox -->
|
||
<div class="flex items-start gap-3 max-w-lg">
|
||
<input type="checkbox" id="approve-agree" required class="mt-1 w-4 h-4 rounded text-theme-{{ $accent }} focus:ring-theme-{{ $accent }} border-slate-300 dark:border-slate-700 dark:bg-slate-800">
|
||
<label for="approve-agree" class="text-xs text-slate-400 dark:text-slate-500 leading-normal">
|
||
Bu teklifi ve içeriğindeki mali/teknik şartları onaylayarak dijital imzamın döküman üzerinde tescil edilmesini, IP adresimin ve zaman damgamın doğruluk doğrulaması amacıyla kaydedilmesini kabul ediyorum.
|
||
</label>
|
||
</div>
|
||
|
||
<button type="submit" class="w-full sm:w-auto px-6 py-3.5 rounded-xl font-bold text-sm text-white bg-emerald-600 hover:bg-emerald-700 hover:shadow-lg hover:shadow-emerald-600/10 focus:ring-4 focus:ring-emerald-200 dark:focus:ring-emerald-950/50 transition-all flex items-center justify-center gap-2">
|
||
<i data-lucide="check" class="w-4 h-4"></i>
|
||
<span>Onay İşlemini Tamamla</span>
|
||
</button>
|
||
</form>
|
||
</div>
|
||
|
||
<!-- Action Tab 2: Feedback Form -->
|
||
<div id="tab-pane-feedback" class="action-pane hidden transition-opacity duration-300">
|
||
<form id="feedback-form" class="space-y-4">
|
||
<div>
|
||
<label for="feedback-text" class="block text-sm font-semibold text-slate-600 dark:text-slate-300 mb-2">Revizyon Notları veya Sorularınız</label>
|
||
<textarea id="feedback-text" name="feedback" rows="4" required placeholder="Teklif üzerinde talep ettiğiniz teknik veya mali güncellemeleri buraya yazabilirsiniz..." class="w-full px-4 py-3 text-sm rounded-xl border border-slate-200 dark:border-slate-700 bg-transparent focus:ring-4 focus:ring-slate-200/50 dark:focus:ring-slate-700/50 focus:border-slate-500 outline-none transition-all"></textarea>
|
||
</div>
|
||
|
||
<button type="submit" class="w-full sm:w-auto px-6 py-3.5 rounded-xl font-bold text-sm text-white bg-amber-600 hover:bg-amber-700 hover:shadow-lg hover:shadow-amber-600/10 focus:ring-4 focus:ring-amber-200 dark:focus:ring-amber-950/50 transition-all flex items-center justify-center gap-2">
|
||
<i data-lucide="send" class="w-4 h-4"></i>
|
||
<span>Revizyon Talebini İlet</span>
|
||
</button>
|
||
</form>
|
||
</div>
|
||
@endif
|
||
|
||
</div>
|
||
</section>
|
||
|
||
</div>
|
||
|
||
</div>
|
||
</main>
|
||
|
||
<!-- Standalone Premium Footer -->
|
||
<footer class="no-print border-t border-slate-200/60 dark:border-slate-800/80 mt-16 bg-white dark:bg-slate-900 transition-colors duration-300">
|
||
<div class="w-full px-4 sm:px-8 lg:px-12 py-8 flex flex-col sm:flex-row items-center justify-between gap-4 text-xs text-slate-400 dark:text-slate-500">
|
||
<div>
|
||
© {{ date('Y') }} Trunçgil Teknoloji San. ve Tic. Ltd. Şti. Tüm hakları saklıdır.
|
||
</div>
|
||
<div class="flex items-center gap-4">
|
||
<a href="https://www.truncgil.com.tr" class="hover:text-slate-600 dark:hover:text-slate-300 font-semibold transition-colors">www.truncgil.com.tr</a>
|
||
<span>•</span>
|
||
<a href="mailto:info@truncgil.com.tr" class="hover:text-slate-600 dark:hover:text-slate-300 font-semibold transition-colors">info@truncgil.com.tr</a>
|
||
<span>•</span>
|
||
<a href="tel:+905512343695" class="hover:text-slate-600 dark:hover:text-slate-300 font-semibold transition-colors">+90 551 234 36 95</a>
|
||
</div>
|
||
</div>
|
||
</footer>
|
||
|
||
<!-- Raw Markdown Data Store (Escape-Proof container) -->
|
||
<textarea id="markdown-source" class="hidden">{{ $proposal->content }}</textarea>
|
||
|
||
<!-- Libraries (CDN) -->
|
||
<!-- Marked.js for markdown parsing -->
|
||
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
|
||
<!-- Mermaid.js for drawing diagrams -->
|
||
<script src="https://cdn.jsdelivr.net/npm/mermaid@10.8.0/dist/mermaid.min.js"></script>
|
||
<!-- SVG Pan Zoom for interactive Mermaid diagrams -->
|
||
<script src="https://cdn.jsdelivr.net/npm/svg-pan-zoom@3.6.1/dist/svg-pan-zoom.min.js"></script>
|
||
<!-- Confetti for approval celebration -->
|
||
<script src="https://cdn.jsdelivr.net/npm/canvas-confetti@1.6.0/dist/confetti.browser.min.js"></script>
|
||
<!-- Lucide Premium Icons -->
|
||
<script src="https://cdn.jsdelivr.net/npm/lucide@0.321.0/dist/umd/lucide.min.js"></script>
|
||
|
||
<script>
|
||
// Init Lucide Icons
|
||
lucide.createIcons();
|
||
|
||
// 1. Theme Toggle Script
|
||
var themeToggleDarkIcon = document.getElementById('theme-toggle-dark-icon');
|
||
var themeToggleLightIcon = document.getElementById('theme-toggle-light-icon');
|
||
|
||
// Adjust visibility of icons based on initial setting
|
||
if (document.documentElement.classList.contains('dark')) {
|
||
themeToggleLightIcon.classList.remove('hidden');
|
||
} else {
|
||
themeToggleDarkIcon.classList.remove('hidden');
|
||
}
|
||
|
||
var themeToggleBtn = document.getElementById('theme-toggle');
|
||
|
||
themeToggleBtn.addEventListener('click', function() {
|
||
// Toggle icons inside button
|
||
themeToggleDarkIcon.classList.toggle('hidden');
|
||
themeToggleLightIcon.classList.toggle('hidden');
|
||
|
||
// If set via local storage previously
|
||
if (localStorage.getItem('color-theme')) {
|
||
if (localStorage.getItem('color-theme') === 'light') {
|
||
document.documentElement.classList.add('dark');
|
||
localStorage.setItem('color-theme', 'dark');
|
||
} else {
|
||
document.documentElement.classList.remove('dark');
|
||
localStorage.setItem('color-theme', 'light');
|
||
}
|
||
} else {
|
||
if (document.documentElement.classList.contains('dark')) {
|
||
document.documentElement.classList.remove('dark');
|
||
localStorage.setItem('color-theme', 'light');
|
||
} else {
|
||
document.documentElement.classList.add('dark');
|
||
localStorage.setItem('color-theme', 'dark');
|
||
}
|
||
}
|
||
|
||
// Re-render Mermaid charts dynamically on theme toggle to sync colors!
|
||
setTimeout(() => {
|
||
location.reload(); // Quick refresh is the most bulletproof way to re-render complex mermaid graphs
|
||
}, 100);
|
||
});
|
||
|
||
// 2. Markdown & Mermaid Parsing
|
||
const rawMarkdown = document.getElementById('markdown-source').value;
|
||
|
||
// Custom parser function for GitHub-Style Alerts
|
||
function parseGithubAlerts(text) {
|
||
const alertTypes = {
|
||
'NOTE': { class: 'bg-blue-50/50 dark:bg-blue-950/20 border-blue-500/30 text-blue-800 dark:text-blue-200', icon: 'info', title: 'Gizlilik Bildirimi / Not' },
|
||
'IMPORTANT': { class: 'bg-red-50/50 dark:bg-red-950/20 border-rose-500/30 text-rose-800 dark:text-rose-200', icon: 'alert-circle', title: 'Önemli Gereksinim' },
|
||
'TIP': { class: 'bg-emerald-50/50 dark:bg-emerald-950/20 border-emerald-500/30 text-emerald-800 dark:text-emerald-200', icon: 'sparkles', title: 'Faydalı Bilgi / Avantaj / İpucu' },
|
||
'WARNING': { class: 'bg-amber-50/50 dark:bg-amber-950/20 border-amber-500/30 text-amber-800 dark:text-amber-200', icon: 'alert-triangle', title: 'Uyarı / İstisna' },
|
||
'CAUTION': { class: 'bg-orange-50/50 dark:bg-orange-950/20 border-orange-500/30 text-orange-800 dark:text-orange-200', icon: 'zap', title: 'Dikkat' }
|
||
};
|
||
|
||
let lines = text.split('\n');
|
||
let inAlert = false;
|
||
let alertType = null;
|
||
let alertLines = [];
|
||
let result = [];
|
||
|
||
for (let i = 0; i < lines.length; i++) {
|
||
let line = lines[i];
|
||
let match = line.match(/^>\s*\[!(NOTE|IMPORTANT|TIP|WARNING|CAUTION)\]\s*(.*)/i);
|
||
|
||
if (match) {
|
||
inAlert = true;
|
||
alertType = match[1].toUpperCase();
|
||
let rest = match[2];
|
||
if (rest.trim()) alertLines.push(rest);
|
||
} else if (inAlert && line.trim().startsWith('>')) {
|
||
// Extract text after the '>' symbol safely
|
||
let content = line.trim().substring(1).trim();
|
||
alertLines.push(content);
|
||
} else {
|
||
if (inAlert) {
|
||
// Assemble the alert box HTML
|
||
let typeInfo = alertTypes[alertType] || alertTypes['NOTE'];
|
||
let alertHtml = `
|
||
<div class="alert-card border-l-4 p-4 rounded-r-xl my-6 ${typeInfo.class}">
|
||
<div class="flex items-center gap-2 font-bold mb-2 text-sm uppercase tracking-wider">
|
||
<i data-lucide="${typeInfo.icon}" class="w-4 h-4"></i>
|
||
<span>${typeInfo.title}</span>
|
||
</div>
|
||
<div class="text-sm leading-relaxed">${marked.parse(alertLines.join('\n'))}</div>
|
||
</div>`;
|
||
result.push(alertHtml);
|
||
inAlert = false;
|
||
alertLines = [];
|
||
alertType = null;
|
||
}
|
||
result.push(line);
|
||
}
|
||
}
|
||
|
||
if (inAlert) {
|
||
let typeInfo = alertTypes[alertType] || alertTypes['NOTE'];
|
||
let alertHtml = `
|
||
<div class="alert-card border-l-4 p-4 rounded-r-xl my-6 ${typeInfo.class}">
|
||
<div class="flex items-center gap-2 font-bold mb-2 text-sm uppercase tracking-wider">
|
||
<i data-lucide="${typeInfo.icon}" class="w-4 h-4"></i>
|
||
<span>${typeInfo.title}</span>
|
||
</div>
|
||
<div class="text-sm leading-relaxed">${marked.parse(alertLines.join('\n'))}</div>
|
||
</div>`;
|
||
result.push(alertHtml);
|
||
}
|
||
|
||
return result.join('\n');
|
||
}
|
||
|
||
// Render Markdown content
|
||
const contentDiv = document.getElementById('proposal-markdown');
|
||
const preprocessedText = parseGithubAlerts(rawMarkdown);
|
||
contentDiv.innerHTML = marked.parse(preprocessedText);
|
||
|
||
// Render Mermaid Diagrams
|
||
const isDark = document.documentElement.classList.contains('dark');
|
||
mermaid.initialize({
|
||
startOnLoad: false,
|
||
theme: isDark ? 'dark' : 'default',
|
||
securityLevel: 'loose',
|
||
themeVariables: {
|
||
fontFamily: 'Inter, sans-serif'
|
||
}
|
||
});
|
||
|
||
// Mermaid zoom state store
|
||
const mermaidZoomInstances = {};
|
||
|
||
// Scan code blocks for mermaid and convert them
|
||
const codeElements = contentDiv.querySelectorAll('pre code.language-mermaid');
|
||
codeElements.forEach((codeEl, index) => {
|
||
const rawMermaid = codeEl.textContent.trim();
|
||
const preContainer = codeEl.parentNode;
|
||
|
||
// Build glass wrapper for diagram
|
||
const wrapper = document.createElement('div');
|
||
wrapper.className = 'mermaid-container no-print transition-all duration-300';
|
||
wrapper.id = 'mermaid-wrapper-' + index;
|
||
|
||
// Zoom Toolbar
|
||
const toolbar = document.createElement('div');
|
||
toolbar.className = 'mermaid-toolbar';
|
||
toolbar.innerHTML = `
|
||
<span class="mermaid-toolbar-label">Diyagram Görünümü</span>
|
||
<button class="mermaid-zoom-btn" title="Yakınlaştır (Zoom In)" onclick="mermaidZoomIn(${index})">
|
||
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/><line x1="11" y1="8" x2="11" y2="14"/><line x1="8" y1="11" x2="14" y2="11"/></svg>
|
||
</button>
|
||
<span class="mermaid-zoom-pct" id="mermaid-pct-${index}">100%</span>
|
||
<button class="mermaid-zoom-btn" title="Uzaklaştır (Zoom Out)" onclick="mermaidZoomOut(${index})">
|
||
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/><line x1="8" y1="11" x2="14" y2="11"/></svg>
|
||
</button>
|
||
<button class="mermaid-zoom-btn" title="Tam Boyut (1:1)" onclick="mermaidZoomReset(${index})">
|
||
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M15 3h6v6M9 21H3v-6M21 3l-7 7M3 21l7-7"/></svg>
|
||
</button>
|
||
<button class="mermaid-zoom-btn" title="Tam Ekran" onclick="mermaidFullscreen(${index})">
|
||
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M8 3H5a2 2 0 0 0-2 2v3m18 0V5a2 2 0 0 0-2-2h-3m0 18h3a2 2 0 0 0 2-2v-3M3 16v3a2 2 0 0 0 2 2h3"/></svg>
|
||
</button>
|
||
`;
|
||
|
||
// Canvas wrapper
|
||
const canvas = document.createElement('div');
|
||
canvas.className = 'mermaid-canvas';
|
||
canvas.id = 'mermaid-canvas-' + index;
|
||
|
||
const graphDiv = document.createElement('div');
|
||
graphDiv.className = 'mermaid w-full';
|
||
graphDiv.id = 'mermaid-graph-' + index;
|
||
graphDiv.textContent = rawMermaid;
|
||
|
||
canvas.appendChild(graphDiv);
|
||
wrapper.appendChild(toolbar);
|
||
wrapper.appendChild(canvas);
|
||
preContainer.parentNode.replaceChild(wrapper, preContainer);
|
||
});
|
||
|
||
// Fullscreen overlay (singleton)
|
||
const fsOverlay = document.createElement('div');
|
||
fsOverlay.className = 'mermaid-fullscreen-overlay';
|
||
fsOverlay.id = 'mermaid-fs-overlay';
|
||
fsOverlay.innerHTML = `
|
||
<div class="mermaid-fullscreen-inner" id="mermaid-fs-inner">
|
||
<div class="mermaid-toolbar" style="border-radius:20px 20px 0 0;">
|
||
<span class="mermaid-toolbar-label">Tam Ekran Görünüm</span>
|
||
<button class="mermaid-zoom-btn" title="Yakınlaştır" onclick="mermaidFsZoom('in')">
|
||
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/><line x1="11" y1="8" x2="11" y2="14"/><line x1="8" y1="11" x2="14" y2="11"/></svg>
|
||
</button>
|
||
<span class="mermaid-zoom-pct" id="mermaid-fs-pct">100%</span>
|
||
<button class="mermaid-zoom-btn" title="Uzaklaştır" onclick="mermaidFsZoom('out')">
|
||
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/><line x1="8" y1="11" x2="14" y2="11"/></svg>
|
||
</button>
|
||
<button class="mermaid-zoom-btn" title="1:1 Sıfırla" onclick="mermaidFsZoom('reset')">
|
||
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M15 3h6v6M9 21H3v-6M21 3l-7 7M3 21l7-7"/></svg>
|
||
</button>
|
||
<button class="mermaid-zoom-btn" title="Kapat" onclick="closeMermaidFullscreen()" style="background:#ef4444;border-color:#ef4444;color:#fff;">
|
||
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>
|
||
</button>
|
||
</div>
|
||
<div class="mermaid-fullscreen-canvas" id="mermaid-fs-canvas"></div>
|
||
</div>
|
||
`;
|
||
document.body.appendChild(fsOverlay);
|
||
fsOverlay.addEventListener('click', function(e) { if (e.target === fsOverlay) closeMermaidFullscreen(); });
|
||
|
||
// Run mermaid parser
|
||
setTimeout(() => {
|
||
mermaid.run().then(() => {
|
||
// Re-init lucide icons inside alerts after marked rendering completes
|
||
lucide.createIcons();
|
||
|
||
// Initialize svg-pan-zoom for each mermaid diagram
|
||
codeElements.forEach((_, index) => {
|
||
initMermaidZoom(index);
|
||
});
|
||
}).catch(err => {
|
||
console.error("Mermaid drawing error:", err);
|
||
});
|
||
}, 100);
|
||
|
||
// Init svg-pan-zoom for a given mermaid index
|
||
function initMermaidZoom(index) {
|
||
const canvas = document.getElementById('mermaid-canvas-' + index);
|
||
if (!canvas) return;
|
||
const svg = canvas.querySelector('svg');
|
||
if (!svg) return;
|
||
|
||
// Ensure SVG has an ID for svg-pan-zoom
|
||
if (!svg.id) svg.id = 'mermaid-svg-' + index;
|
||
|
||
// Compute a good canvas height from SVG viewBox aspect ratio
|
||
const canvasW = canvas.clientWidth || canvas.offsetWidth || 800;
|
||
let canvasH = 520;
|
||
const vb = svg.getAttribute('viewBox');
|
||
if (vb) {
|
||
const parts = vb.split(/[\s,]+/);
|
||
if (parts.length >= 4) {
|
||
const svgW = parseFloat(parts[2]);
|
||
const svgH = parseFloat(parts[3]);
|
||
if (svgW > 0 && svgH > 0) {
|
||
canvasH = Math.max(400, Math.min(Math.round(svgH * (canvasW / svgW)), 900));
|
||
}
|
||
}
|
||
}
|
||
canvas.style.height = canvasH + 'px';
|
||
|
||
// svg-pan-zoom needs explicit width/height on the SVG element
|
||
svg.setAttribute('width', canvasW);
|
||
svg.setAttribute('height', canvasH);
|
||
svg.style.width = canvasW + 'px';
|
||
svg.style.height = canvasH + 'px';
|
||
|
||
try {
|
||
const panZoom = svgPanZoom('#' + svg.id, {
|
||
zoomEnabled: true,
|
||
controlIconsEnabled: false,
|
||
fit: true,
|
||
center: true,
|
||
minZoom: 0.3,
|
||
maxZoom: 8,
|
||
zoomScaleSensitivity: 0.3,
|
||
onZoom: (zoom) => {
|
||
const pctEl = document.getElementById('mermaid-pct-' + index);
|
||
if (pctEl) pctEl.textContent = Math.round(zoom * 100) + '%';
|
||
}
|
||
});
|
||
mermaidZoomInstances[index] = panZoom;
|
||
} catch(e) {
|
||
console.warn('svg-pan-zoom init failed for mermaid-' + index, e);
|
||
}
|
||
}
|
||
|
||
// Zoom controls
|
||
function mermaidZoomIn(index) {
|
||
const pz = mermaidZoomInstances[index];
|
||
if (pz) { pz.zoomIn(); updatePct(index, pz.getZoom()); }
|
||
}
|
||
function mermaidZoomOut(index) {
|
||
const pz = mermaidZoomInstances[index];
|
||
if (pz) { pz.zoomOut(); updatePct(index, pz.getZoom()); }
|
||
}
|
||
function mermaidZoomReset(index) {
|
||
const pz = mermaidZoomInstances[index];
|
||
if (pz) { pz.resetZoom(); pz.center(); updatePct(index, pz.getZoom()); }
|
||
}
|
||
function updatePct(index, zoom) {
|
||
const pctEl = document.getElementById('mermaid-pct-' + index);
|
||
if (pctEl) pctEl.textContent = Math.round(zoom * 100) + '%';
|
||
}
|
||
|
||
// Fullscreen
|
||
let fsActivePanZoom = null;
|
||
function mermaidFullscreen(index) {
|
||
const canvas = document.getElementById('mermaid-canvas-' + index);
|
||
if (!canvas) return;
|
||
const svg = canvas.querySelector('svg');
|
||
if (!svg) return;
|
||
|
||
const fsCanvas = document.getElementById('mermaid-fs-canvas');
|
||
const svgClone = svg.cloneNode(true);
|
||
svgClone.id = 'mermaid-fs-svg-' + index;
|
||
svgClone.style.width = '100%';
|
||
svgClone.style.height = 'auto';
|
||
svgClone.removeAttribute('height');
|
||
fsCanvas.innerHTML = '';
|
||
fsCanvas.appendChild(svgClone);
|
||
|
||
document.getElementById('mermaid-fs-overlay').classList.add('active');
|
||
document.body.style.overflow = 'hidden';
|
||
|
||
// Init pan-zoom on cloned SVG
|
||
setTimeout(() => {
|
||
try {
|
||
fsActivePanZoom = svgPanZoom('#mermaid-fs-svg-' + index, {
|
||
zoomEnabled: true,
|
||
controlIconsEnabled: false,
|
||
fit: true,
|
||
center: true,
|
||
minZoom: 0.2,
|
||
maxZoom: 10,
|
||
zoomScaleSensitivity: 0.3,
|
||
onZoom: (zoom) => {
|
||
const pctEl = document.getElementById('mermaid-fs-pct');
|
||
if (pctEl) pctEl.textContent = Math.round(zoom * 100) + '%';
|
||
}
|
||
});
|
||
document.getElementById('mermaid-fs-pct').textContent = Math.round(fsActivePanZoom.getZoom() * 100) + '%';
|
||
} catch(e) {}
|
||
}, 100);
|
||
}
|
||
function closeMermaidFullscreen() {
|
||
document.getElementById('mermaid-fs-overlay').classList.remove('active');
|
||
document.body.style.overflow = '';
|
||
if (fsActivePanZoom) { try { fsActivePanZoom.destroy(); } catch(e) {} fsActivePanZoom = null; }
|
||
}
|
||
function mermaidFsZoom(action) {
|
||
if (!fsActivePanZoom) return;
|
||
if (action === 'in') fsActivePanZoom.zoomIn();
|
||
else if (action === 'out') fsActivePanZoom.zoomOut();
|
||
else if (action === 'reset') { fsActivePanZoom.resetZoom(); fsActivePanZoom.center(); }
|
||
const pctEl = document.getElementById('mermaid-fs-pct');
|
||
if (pctEl) pctEl.textContent = Math.round(fsActivePanZoom.getZoom() * 100) + '%';
|
||
}
|
||
// Keyboard ESC closes fullscreen
|
||
document.addEventListener('keydown', function(e) {
|
||
if (e.key === 'Escape') closeMermaidFullscreen();
|
||
});
|
||
|
||
// 3. Dynamic Sidebar TOC Generator
|
||
const tocNav = document.getElementById('toc-nav');
|
||
const headings = contentDiv.querySelectorAll('h2, h3');
|
||
|
||
if (headings.length === 0) {
|
||
tocNav.innerHTML = `<span class="text-slate-400 italic">Başlık bulunamadı.</span>`;
|
||
} else {
|
||
tocNav.innerHTML = ''; // Clear loading indicator
|
||
headings.forEach((heading, idx) => {
|
||
const id = 'heading-' + idx;
|
||
heading.id = id; // Assign ID
|
||
|
||
const level = heading.tagName.toLowerCase();
|
||
const text = heading.textContent;
|
||
|
||
const a = document.createElement('a');
|
||
a.href = '#' + id;
|
||
a.className = `toc-item flex items-center gap-2 py-1.5 transition-all text-slate-500 hover:text-slate-800 dark:text-slate-400 dark:hover:text-slate-200 ${level === 'h3' ? 'pl-5 text-xs' : 'font-medium text-sm'}`;
|
||
|
||
// Bullet marker
|
||
const bullet = document.createElement('span');
|
||
bullet.className = 'toc-bullet w-1.5 h-1.5 rounded-full bg-slate-300 dark:bg-slate-600 transition-all shrink-0';
|
||
|
||
const label = document.createElement('span');
|
||
label.className = 'truncate';
|
||
label.textContent = text;
|
||
|
||
a.appendChild(bullet);
|
||
a.appendChild(label);
|
||
tocNav.appendChild(a);
|
||
});
|
||
}
|
||
|
||
// Active TOC tracking on scroll
|
||
const tocItems = tocNav.querySelectorAll('.toc-item');
|
||
window.addEventListener('scroll', () => {
|
||
let activeIdx = 0;
|
||
headings.forEach((heading, idx) => {
|
||
const rect = heading.getBoundingClientRect();
|
||
if (rect.top <= 140) {
|
||
activeIdx = idx;
|
||
}
|
||
});
|
||
|
||
tocItems.forEach((item, idx) => {
|
||
if (idx === activeIdx) {
|
||
item.classList.add('active');
|
||
} else {
|
||
item.classList.remove('active');
|
||
}
|
||
});
|
||
});
|
||
|
||
// 4. KDV Switch Calculator Logic
|
||
const kdvToggle = document.getElementById('kdv-toggle');
|
||
const kdvLabel = document.getElementById('kdv-label');
|
||
const kdvPrice = document.getElementById('kdv-price');
|
||
const kdvBenefitBox = document.getElementById('kdv-benefit-box');
|
||
|
||
if (kdvToggle) {
|
||
const totalPrice = parseFloat('{{ $proposal->total_price }}');
|
||
const currencySymbol = '{{ $proposal->currency === 'USD' ? 'USD' : ($proposal->currency === 'EUR' ? 'EUR' : 'TL') }}';
|
||
|
||
kdvToggle.addEventListener('change', function() {
|
||
if (this.checked) {
|
||
// %0 KDV selected (Teknopark exemption)
|
||
kdvLabel.textContent = 'Teknopark Avantajlı Fiyat (%0 KDV)';
|
||
kdvPrice.textContent = new Intl.NumberFormat('tr-TR', { minimumFractionDigits: 2 }).format(totalPrice) + ' ' + currencySymbol;
|
||
kdvBenefitBox.classList.remove('hidden');
|
||
} else {
|
||
// %20 standard KDV added
|
||
const priceWithVat = totalPrice * 1.20;
|
||
kdvLabel.textContent = 'Standart Fiyat (+%20 KDV)';
|
||
kdvPrice.textContent = new Intl.NumberFormat('tr-TR', { minimumFractionDigits: 2 }).format(priceWithVat) + ' ' + currencySymbol;
|
||
kdvBenefitBox.classList.add('hidden');
|
||
}
|
||
});
|
||
}
|
||
|
||
// 5. Client Interaction Actions & Tabs switching
|
||
function switchTab(tab) {
|
||
const btnApprove = document.getElementById('tab-btn-approve');
|
||
const btnFeedback = document.getElementById('tab-btn-feedback');
|
||
const paneApprove = document.getElementById('tab-pane-approve');
|
||
const paneFeedback = document.getElementById('tab-pane-feedback');
|
||
|
||
if (tab === 'approve') {
|
||
btnApprove.className = "flex-1 sm:flex-initial flex items-center justify-center gap-2 px-5 py-3 rounded-xl font-semibold text-sm transition-all duration-200 border text-white bg-slate-900 dark:bg-white dark:text-slate-900 hover:shadow-lg focus:ring-4 focus:ring-slate-300";
|
||
btnFeedback.className = "flex-1 sm:flex-initial flex items-center justify-center gap-2 px-5 py-3 rounded-xl font-semibold text-sm transition-all duration-200 border border-slate-200 dark:border-slate-700 text-slate-700 dark:text-slate-300 hover:bg-slate-50 dark:hover:bg-slate-800";
|
||
paneApprove.classList.remove('hidden');
|
||
paneFeedback.classList.add('hidden');
|
||
} else {
|
||
btnFeedback.className = "flex-1 sm:flex-initial flex items-center justify-center gap-2 px-5 py-3 rounded-xl font-semibold text-sm transition-all duration-200 border text-white bg-slate-900 dark:bg-white dark:text-slate-900 hover:shadow-lg focus:ring-4 focus:ring-slate-300";
|
||
btnApprove.className = "flex-1 sm:flex-initial flex items-center justify-center gap-2 px-5 py-3 rounded-xl font-semibold text-sm transition-all duration-200 border border-slate-200 dark:border-slate-700 text-slate-700 dark:text-slate-300 hover:bg-slate-50 dark:hover:bg-slate-800";
|
||
paneFeedback.classList.remove('hidden');
|
||
paneApprove.classList.add('hidden');
|
||
}
|
||
}
|
||
|
||
// Signature Real-time typing script preview
|
||
const approveNameInput = document.getElementById('approve-name');
|
||
const signaturePreview = document.getElementById('signature-preview');
|
||
|
||
if (approveNameInput && signaturePreview) {
|
||
approveNameInput.addEventListener('input', function() {
|
||
const text = this.value.trim();
|
||
if (text) {
|
||
signaturePreview.textContent = text;
|
||
signaturePreview.className = "font-signature text-4xl text-theme-{{ $accent }} dark:text-theme-{{ $accent }} text-center pt-2 select-none";
|
||
} else {
|
||
signaturePreview.textContent = "İmzanız burada belirecektir";
|
||
signaturePreview.className = "font-signature text-3xl text-theme-{{ $accent }} dark:text-theme-{{ $accent }} text-center pt-2 select-none italic text-slate-300 dark:text-slate-600";
|
||
}
|
||
});
|
||
}
|
||
|
||
// AJAX Action Post Handlers
|
||
const approveForm = document.getElementById('approve-form');
|
||
const feedbackForm = document.getElementById('feedback-form');
|
||
const terminalContent = document.getElementById('terminal-content');
|
||
|
||
if (approveForm) {
|
||
approveForm.addEventListener('submit', function(e) {
|
||
e.preventDefault();
|
||
|
||
const name = document.getElementById('approve-name').value.trim();
|
||
if (!name) return;
|
||
|
||
const csrfToken = document.querySelector('meta[name="csrf-token"]').getAttribute('content');
|
||
|
||
// Show loading state
|
||
const submitBtn = this.querySelector('button[type="submit"]');
|
||
const origHtml = submitBtn.innerHTML;
|
||
submitBtn.disabled = true;
|
||
submitBtn.innerHTML = `<span class="w-4 h-4 border-2 border-t-transparent border-white rounded-full animate-spin"></span> Onaylanıyor...`;
|
||
|
||
fetch('{{ route('proposals.action', $proposal->slug) }}', {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json',
|
||
'X-CSRF-TOKEN': csrfToken,
|
||
'Accept': 'application/json'
|
||
},
|
||
body: JSON.stringify({
|
||
action_type: 'approve',
|
||
name: name
|
||
})
|
||
})
|
||
.then(response => response.json())
|
||
.then(data => {
|
||
if (data.success) {
|
||
// Trigger Confetti Rain!
|
||
var duration = 4 * 1000;
|
||
var end = Date.now() + duration;
|
||
|
||
(function frame() {
|
||
confetti({
|
||
particleCount: 4,
|
||
angle: 60,
|
||
spread: 55,
|
||
origin: { x: 0 },
|
||
colors: ['{{ $accentColor }}', '#10b981', '#3b82f6']
|
||
});
|
||
confetti({
|
||
particleCount: 4,
|
||
angle: 120,
|
||
spread: 55,
|
||
origin: { x: 1 },
|
||
colors: ['{{ $accentColor }}', '#10b981', '#3b82f6']
|
||
});
|
||
|
||
if (Date.now() < end) {
|
||
requestAnimationFrame(frame);
|
||
}
|
||
}());
|
||
|
||
// Transform terminal HTML into secure signature verification layout dynamically
|
||
const verifyCode = Math.random().toString(36).substring(2, 10).toUpperCase() + Math.random().toString(36).substring(2, 10).toUpperCase();
|
||
const now = new Date();
|
||
const formattedDate = now.toLocaleDateString('tr-TR') + ' ' + now.toLocaleTimeString('tr-TR', { hour: '2-digit', minute: '2-digit' });
|
||
|
||
setTimeout(() => {
|
||
terminalContent.innerHTML = `
|
||
<div class="flex flex-col items-center justify-center text-center py-6 animate-fade-in">
|
||
<div class="w-16 h-16 rounded-full bg-emerald-100 dark:bg-emerald-950/30 text-emerald-500 flex items-center justify-center mb-4 border border-emerald-500/20 shadow-sm">
|
||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2.5" stroke="currentColor" class="w-8 h-8">
|
||
<path stroke-linecap="round" stroke-linejoin="round" d="M9 12.75L11.25 15 15 9.75M21 12c0 1.268-.63 2.39-1.593 3.068a3.745 3.745 0 01-1.043 3.296 3.745 3.745 0 01-3.296 1.043A3.745 3.745 0 0112 21c-1.268 0-2.39-.63-3.068-1.593a3.746 3.746 0 01-3.296-1.043 3.745 3.745 0 01-1.043-3.296A3.745 3.745 0 013 12c0-1.268.63-2.39 1.593-3.068a3.745 3.745 0 011.043-3.296 3.746 3.746 0 013.296-1.043A3.746 3.746 0 0112 3c1.268 0 2.39.63 3.068 1.593a3.746 3.746 0 013.296 1.043 3.746 3.746 0 011.043 3.296A3.745 3.745 0 0121 12z" />
|
||
</svg>
|
||
</div>
|
||
<h3 class="text-2xl font-display font-extrabold text-slate-900 dark:text-white mb-2">
|
||
Teklif Başarıyla Onaylanmıştır
|
||
</h3>
|
||
<p class="text-sm text-slate-500 dark:text-slate-400 max-w-lg mb-6 leading-relaxed">
|
||
Bu döküman, yetkiliniz <strong class="text-slate-800 dark:text-slate-200">${name}</strong> tarafından <strong class="text-slate-800 dark:text-slate-200">${formattedDate}</strong> tarihinde resmi olarak onaylanmış ve dijital olarak imzalanmıştır.
|
||
</p>
|
||
|
||
<div class="px-6 py-4 rounded-xl border border-slate-200 dark:border-slate-700 bg-slate-50 dark:bg-slate-900 max-w-sm w-full relative">
|
||
<div class="absolute -top-2.5 left-4 px-2 bg-white dark:bg-slate-800 text-[10px] font-bold text-slate-400 tracking-wider rounded uppercase">Dijital İmza Mührü</div>
|
||
<div class="font-signature text-3xl text-theme-{{ $accent }} dark:text-theme-{{ $accent }} py-2 select-none">
|
||
${name}
|
||
</div>
|
||
<div class="text-[9px] text-slate-400 dark:text-slate-500 font-semibold border-t border-slate-200/50 dark:border-slate-800/80 pt-2 flex justify-between">
|
||
<span>DOKÜMAN DOĞRULAMA KODU:</span>
|
||
<span class="font-mono text-slate-500">${verifyCode.substring(0, 16)}</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
`;
|
||
|
||
// Smooth scroll to the terminal card
|
||
document.getElementById('terminal-card').scrollIntoView({ behavior: 'smooth' });
|
||
|
||
// Re-update status badge in header
|
||
const headerBadge = document.querySelector('span.px-3.5.py-1.5');
|
||
if (headerBadge) {
|
||
headerBadge.className = 'px-3.5 py-1.5 text-xs font-bold rounded-full border bg-emerald-50 text-emerald-700 border-emerald-200 dark:bg-emerald-950/20 dark:text-emerald-400 dark:border-emerald-800 shadow-sm';
|
||
headerBadge.textContent = 'Onaylandı';
|
||
}
|
||
}, 800);
|
||
} else {
|
||
alert("Onay hatası: " + data.message);
|
||
submitBtn.disabled = false;
|
||
submitBtn.innerHTML = origHtml;
|
||
}
|
||
})
|
||
.catch(err => {
|
||
console.error("AJAX Error:", err);
|
||
alert("Onay işlemi gerçekleştirilemedi. Lütfen bağlantınızı kontrol edin.");
|
||
submitBtn.disabled = false;
|
||
submitBtn.innerHTML = origHtml;
|
||
});
|
||
});
|
||
}
|
||
|
||
if (feedbackForm) {
|
||
feedbackForm.addEventListener('submit', function(e) {
|
||
e.preventDefault();
|
||
|
||
const feedbackText = document.getElementById('feedback-text').value.trim();
|
||
if (!feedbackText) return;
|
||
|
||
const csrfToken = document.querySelector('meta[name="csrf-token"]').getAttribute('content');
|
||
|
||
// Show loading state
|
||
const submitBtn = this.querySelector('button[type="submit"]');
|
||
const origHtml = submitBtn.innerHTML;
|
||
submitBtn.disabled = true;
|
||
submitBtn.innerHTML = `<span class="w-4 h-4 border-2 border-t-transparent border-white rounded-full animate-spin"></span> İletiliyor...`;
|
||
|
||
fetch('{{ route('proposals.action', $proposal->slug) }}', {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json',
|
||
'X-CSRF-TOKEN': csrfToken,
|
||
'Accept': 'application/json'
|
||
},
|
||
body: JSON.stringify({
|
||
action_type: 'feedback',
|
||
feedback: feedbackText
|
||
})
|
||
})
|
||
.then(response => response.json())
|
||
.then(data => {
|
||
if (data.success) {
|
||
setTimeout(() => {
|
||
terminalContent.innerHTML = `
|
||
<div class="flex flex-col items-center justify-center text-center py-8 animate-fade-in">
|
||
<div class="w-16 h-16 rounded-full bg-amber-100 dark:bg-amber-950/30 text-amber-500 flex items-center justify-center mb-4 border border-amber-500/20 shadow-sm">
|
||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2.2" stroke="currentColor" class="w-8 h-8">
|
||
<path stroke-linecap="round" stroke-linejoin="round" d="M12 7.5h1.5m-1.5 3h1.5m-7.5 3h7.5m-7.5 3h7.5m3-9h3.375c.621 0 1.125.504 1.125 1.125V18a2.25 2.25 0 01-2.25 2.25H5.25A2.25 2.25 0 013 18V6.125c0-.621.504-1.125 1.125-1.125H9.75M8.25 21h8.25" />
|
||
</svg>
|
||
</div>
|
||
<h3 class="text-xl font-display font-extrabold text-slate-900 dark:text-white mb-2">
|
||
Geri Bildiriminiz Alındı
|
||
</h3>
|
||
<p class="text-sm text-slate-500 dark:text-slate-400 max-w-md leading-relaxed mb-0">
|
||
Revizyon talebiniz ve notlarınız teknik ekibimize iletilmiştir. Teklifimiz en kısa sürede güncellenerek sizinle yeniden paylaşılacaktır.
|
||
</p>
|
||
</div>
|
||
`;
|
||
|
||
// Re-update status badge in header
|
||
const headerBadge = document.querySelector('span.px-3.5.py-1.5');
|
||
if (headerBadge) {
|
||
headerBadge.className = 'px-3.5 py-1.5 text-xs font-bold rounded-full border bg-amber-50 text-amber-700 border-amber-200 dark:bg-amber-950/20 dark:text-amber-400 dark:border-amber-800 shadow-sm';
|
||
headerBadge.textContent = 'Revizyon Talep Edildi';
|
||
}
|
||
}, 800);
|
||
} else {
|
||
alert("Gönderim hatası: " + data.message);
|
||
submitBtn.disabled = false;
|
||
submitBtn.innerHTML = origHtml;
|
||
}
|
||
})
|
||
.catch(err => {
|
||
console.error("AJAX Error:", err);
|
||
alert("Geri bildirim iletilemedi. Lütfen bağlantınızı kontrol edin.");
|
||
submitBtn.disabled = false;
|
||
submitBtn.innerHTML = origHtml;
|
||
});
|
||
});
|
||
}
|
||
</script>
|
||
</body>
|
||
</html>
|