Files
citrus/resources/views/projects/show.blade.php
T

1182 lines
72 KiB
PHP
Raw 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="scroll-smooth">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="csrf-token" content="{{ csrf_token() }}">
<meta name="robots" content="noindex, nofollow">
<title>{{ $project->title }} - Canlı Proje Takip Portalı | Trunçgil Teknoloji</title>
<!-- Google Fonts: Inter & Outfit -->
<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=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>
<!-- Sortable JS for Drag and Drop -->
@if($isAdminMode)
<script src="https://cdn.jsdelivr.net/npm/sortablejs@1.15.0/Sortable.min.js"></script>
@endif
<script>
tailwind.config = {
darkMode: 'class',
theme: {
extend: {
fontFamily: {
sans: ['Inter', 'sans-serif'],
display: ['Outfit', 'sans-serif'],
},
colors: {
brand: {
50: '#fff7ed',
100: '#ffedd5',
500: '#f97316',
600: '#ea580c',
700: '#c2410c',
}
}
}
}
}
</script>
<style type="text/css">
.glass-header {
background: rgba(255, 255, 255, 0.85);
backdrop-filter: blur(12px);
border-bottom: 1px solid rgba(0, 0, 0, 0.06);
}
.dark .glass-header {
background: rgba(15, 23, 42, 0.85);
border-bottom: 1px solid rgba(255, 255, 255, 0.06);
}
.glass-card {
background: rgba(255, 255, 255, 0.75);
backdrop-filter: blur(8px);
border: 1px solid rgba(0, 0, 0, 0.06);
}
.dark .glass-card {
background: rgba(30, 41, 59, 0.75);
border: 1px solid rgba(255, 255, 255, 0.06);
}
.mermaid-container {
background: #ffffff;
border-radius: 16px;
margin: 1.5rem 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);
}
.mermaid-toolbar {
display: flex;
align-items: center;
padding: 10px 16px;
background: rgba(248, 250, 252, 0.9);
border-bottom: 1px solid rgba(0, 0, 0, 0.06);
}
.dark .mermaid-toolbar {
background: rgba(15, 23, 42, 0.9);
border-bottom: 1px solid rgba(255, 255, 255, 0.06);
}
.mermaid-canvas {
width: 100%;
height: 480px;
position: relative;
overflow: hidden;
cursor: grab;
}
.mermaid-canvas:active { cursor: grabbing; }
/* Mermaid Theme Customization - High Contrast Text */
.mermaid svg text {
fill: #0f172a !important;
font-family: 'Inter', sans-serif !important;
font-weight: 600 !important;
}
.dark .mermaid svg text {
fill: #f8fafc !important;
}
.mermaid svg .titleText {
fill: #0f172a !important;
font-size: 16px !important;
font-weight: 800 !important;
}
.dark .mermaid svg .titleText {
fill: #ffffff !important;
}
.mermaid svg .sectionTitle {
fill: #0f172a !important;
font-size: 13px !important;
font-weight: 700 !important;
}
.dark .mermaid svg .sectionTitle {
fill: #f1f5f9 !important;
}
.mermaid svg .taskText {
fill: #ffffff !important;
font-family: 'Inter', sans-serif !important;
font-weight: 700 !important;
}
.mermaid svg .taskTextOutsideRight, .mermaid svg .taskTextOutsideLeft {
fill: #0f172a !important;
font-weight: 700 !important;
}
.dark .mermaid svg .taskTextOutsideRight, .dark .mermaid svg .taskTextOutsideLeft {
fill: #ffffff !important;
}
.mermaid svg .task {
fill: #ea580c !important;
stroke: #c2410c !important;
rx: 6px !important;
ry: 6px !important;
}
.mermaid svg .task0, .mermaid svg .task2 {
fill: #ea580c !important;
stroke: #c2410c !important;
}
.mermaid svg .task1, .mermaid svg .task3 {
fill: #ef4444 !important;
stroke: #b91c1c !important;
}
.mermaid svg .section0, .mermaid svg .section2 {
fill: rgba(234, 88, 12, 0.08) !important;
}
.dark .mermaid svg .section0, .dark .mermaid svg .section2 {
fill: rgba(234, 88, 12, 0.22) !important;
}
.mermaid svg .section1, .mermaid svg .section3 {
fill: rgba(239, 68, 68, 0.08) !important;
}
.dark .mermaid svg .section1, .dark .mermaid svg .section3 {
fill: rgba(239, 68, 68, 0.22) !important;
}
.mermaid svg .grid .tick text {
fill: #334155 !important;
font-weight: 700 !important;
}
.dark .mermaid svg .grid .tick text {
fill: #94a3b8 !important;
}
.mermaid svg .grid .tick line {
stroke: #cbd5e1 !important;
stroke-width: 1.5px !important;
}
.dark .mermaid svg .grid .tick line {
stroke: #334155 !important;
}
/* Sortable Dragging Styling */
.sortable-ghost {
opacity: 0.3 !important;
background: rgba(234, 88, 12, 0.15) !important;
border: 2px dashed #ea580c !important;
}
.sortable-drag {
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.2), 0 10px 10px -5px rgba(0, 0, 0, 0.1) !important;
transform: rotate(2deg) scale(1.03) !important;
}
</style>
</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">
@if(!$isVerified && !$isAdminMode)
<!-- MÜŞTERİ PIN GİRİŞ EKRANI (CLIENT PIN LOGIN PORTAL) -->
<div class="min-h-screen flex items-center justify-center p-4 bg-gradient-to-br from-slate-900 via-slate-800 to-orange-950">
<div class="max-w-md w-full bg-slate-900/90 backdrop-blur-xl rounded-3xl p-8 shadow-2xl border border-slate-700/80 text-white space-y-6">
<!-- Branding -->
<div class="text-center space-y-3">
<img src="{{ asset('logos/truncgil-yatay-dark.svg') }}" alt="Trunçgil Teknoloji" class="h-10 w-auto mx-auto mb-2">
<span class="inline-block px-3 py-1 rounded-full bg-orange-500/20 text-orange-400 text-xs font-bold uppercase tracking-wider border border-orange-500/30">MÜŞTERİ CANLI TAKİP PORTALI</span>
<h2 class="text-xl font-extrabold font-display text-white">{{ $project->client_name }}</h2>
<p class="text-xs text-slate-300">{{ $project->title }}</p>
</div>
@if($errors->has('access_code'))
<div class="p-3 bg-red-500/20 border border-red-500/40 text-red-200 rounded-2xl text-xs font-bold text-center">
{{ $errors->first('access_code') }}
</div>
@endif
<form action="{{ route('projects.verify', $project->slug) }}" method="POST" class="space-y-4">
@csrf
<div>
<label class="block text-xs font-bold text-slate-300 uppercase tracking-wider mb-2 text-center">Müşteri Giriş Şifresi / PIN</label>
<input type="password" name="access_code" required placeholder="••••••••" class="w-full px-4 py-3 rounded-2xl bg-slate-800 border border-slate-700 text-center font-mono font-bold text-lg text-orange-400 focus:outline-none focus:border-orange-500 uppercase tracking-widest">
</div>
<button type="submit" class="w-full py-3.5 rounded-2xl bg-gradient-to-r from-orange-500 to-rose-600 hover:from-orange-600 hover:to-rose-700 text-white font-extrabold text-sm shadow-xl transition-all flex items-center justify-center gap-2">
<i data-lucide="lock" class="w-4 h-4"></i>
<span>Projeyi Canlı İzle →</span>
</button>
</form>
<p class="text-[11px] text-center text-slate-500">
Projenizin şifresi sözleşme belgelerinizde yer almaktadır. Destek için Trunçgil Teknoloji ile iletişime geçebilirsiniz.
</p>
</div>
</div>
@else
<!-- Floating Toast Notification -->
<div id="toast-notification" class="fixed bottom-6 right-6 z-50 transform translate-y-20 opacity-0 transition-all duration-300 pointer-events-none">
<div class="bg-slate-900 text-white px-5 py-3 rounded-2xl shadow-2xl border border-slate-700 flex items-center gap-3">
<div id="toast-icon" class="w-6 h-6 rounded-full bg-emerald-500 text-white flex items-center justify-center flex-shrink-0 font-bold text-xs">✓</div>
<span id="toast-message" class="text-xs font-bold">İşlem başarıyla gerçekleşti.</span>
</div>
</div>
<!-- Top Header Bar (Admin vs Client Mode) -->
@if($isAdminMode)
<div class="bg-gradient-to-r from-amber-600 via-orange-600 to-red-600 text-white px-4 py-2.5 shadow-lg border-b border-orange-500/40 sticky top-0 z-50">
<div class="max-w-7xl mx-auto flex flex-col sm:flex-row items-center justify-between gap-3 text-xs">
<div class="flex items-center gap-2 font-bold uppercase tracking-wider">
<span class="w-2.5 h-2.5 rounded-full bg-white animate-ping"></span>
<i data-lucide="shield-check" class="w-4 h-4"></i>
<span>YÖNETİCİ DÜZENLEME MODU (ADMIN GİRİŞİ YAPILDI)</span>
</div>
<div class="flex flex-wrap items-center gap-2">
<button onclick="openAddTaskModal()" class="px-3 py-1.5 rounded-lg bg-white/20 hover:bg-white/30 font-bold transition-all flex items-center gap-1">
<i data-lucide="plus-circle" class="w-3.5 h-3.5"></i>
<span>+ Yeni Görev Ekle</span>
</button>
<button onclick="openAddUpdateModal()" class="px-3 py-1.5 rounded-lg bg-white/20 hover:bg-white/30 font-bold transition-all flex items-center gap-1">
<i data-lucide="message-square-plus" class="w-3.5 h-3.5"></i>
<span>+ Duyuru Yayınla</span>
</button>
<form action="{{ route('projects.admin.recalculate', $project->slug) }}" method="POST" class="inline">
@csrf
<button type="submit" class="px-3 py-1.5 rounded-lg bg-white/20 hover:bg-white/30 font-bold transition-all flex items-center gap-1">
<i data-lucide="refresh-cw" class="w-3.5 h-3.5"></i>
<span>Yeniden Hesapla</span>
</button>
</form>
<a href="/admin/projects/{{ $project->id }}/edit" target="_blank" class="px-3 py-1.5 rounded-lg bg-white text-orange-700 hover:bg-orange-50 font-bold transition-all flex items-center gap-1 shadow-sm">
<i data-lucide="layout-dashboard" class="w-3.5 h-3.5"></i>
<span>Filament Paneli</span>
</a>
</div>
</div>
</div>
@else
<div class="bg-gradient-to-r from-blue-900 via-indigo-900 to-slate-900 text-white px-4 py-2 shadow-md border-b border-indigo-500/30 sticky top-0 z-50">
<div class="max-w-7xl mx-auto flex items-center justify-between text-xs">
<div class="flex items-center gap-2 font-bold uppercase tracking-wider text-blue-300">
<i data-lucide="eye" class="w-4 h-4 text-cyan-400"></i>
<span>🔒 MÜŞTERİ CANLI TAKİP PORTALI (SADECE OKUMA MODU)</span>
</div>
<span class="hidden sm:inline text-[11px] text-slate-300">Bu sayfa projenizi canlı izleyebilmeniz için tasarlanmıştır. Tüm değişiklikler Trunçgil Teknoloji ekibi tarafından yönetilmektedir.</span>
</div>
</div>
@endif
<!-- Header Navigation -->
<header class="w-full glass-header sticky top-[37px] z-40">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 h-18 flex items-center justify-between py-4">
<!-- Logos -->
<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>
<div class="h-6 w-px bg-slate-300 dark:bg-slate-700"></div>
<div class="flex flex-col">
<span class="text-xs font-bold uppercase tracking-wider text-orange-600 dark:text-orange-400">Canlı Proje Takip Portalı</span>
<span class="text-sm font-semibold text-slate-700 dark:text-slate-200 line-clamp-1">{{ $project->client_name }}</span>
</div>
</div>
<!-- Right Controls -->
<div class="flex items-center gap-3">
@if($project->proposal)
<a href="{{ route('proposals.show', $project->proposal->slug) }}" target="_blank" class="px-3.5 py-1.5 rounded-xl bg-orange-50 dark:bg-orange-950/40 text-orange-600 dark:text-orange-400 border border-orange-200 dark:border-orange-800/40 text-xs font-bold hover:bg-orange-100 transition-all flex items-center gap-1.5">
<i data-lucide="file-text" class="w-3.5 h-3.5"></i>
<span>Sözleşme / Teklif</span>
</a>
@endif
<button id="theme-toggle" class="p-2 rounded-xl border border-slate-200 dark:border-slate-800 hover:bg-slate-100 dark:hover:bg-slate-800 transition-all">
<i data-lucide="sun" id="theme-toggle-light-icon" class="w-4 h-4 text-amber-500 hidden"></i>
<i data-lucide="moon" id="theme-toggle-dark-icon" class="w-4 h-4 text-slate-600 hidden"></i>
</button>
</div>
</div>
</header>
<!-- Main Container -->
<main class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8 space-y-8">
<!-- Flash Session Notifications -->
@if(session('success'))
<div class="bg-emerald-50 dark:bg-emerald-950/50 border border-emerald-300 dark:border-emerald-800 text-emerald-800 dark:text-emerald-200 p-4 rounded-2xl flex items-center justify-between shadow-md">
<div class="flex items-center gap-2.5 font-bold text-sm">
<i data-lucide="check-circle-2" class="w-5 h-5 text-emerald-600"></i>
<span>{{ session('success') }}</span>
</div>
<button onclick="this.parentElement.remove()" class="text-emerald-500 hover:text-emerald-700">
<i data-lucide="x" class="w-4 h-4"></i>
</button>
</div>
@endif
<!-- Hero Section & Progress Banner -->
<div class="relative overflow-hidden rounded-3xl bg-white dark:bg-slate-900 text-slate-900 dark:text-white p-6 sm:p-10 shadow-xl border border-slate-200 dark:border-slate-800">
<div class="absolute -top-24 -right-24 w-96 h-96 bg-orange-500/10 dark:bg-orange-600/20 rounded-full blur-3xl pointer-events-none"></div>
<div class="relative z-10 grid grid-cols-1 lg:grid-cols-12 gap-8 items-center">
<!-- Left: Title & Info -->
<div class="lg:col-span-8 space-y-4">
<div class="inline-flex items-center gap-2 px-3 py-1 rounded-full bg-orange-500/10 dark:bg-orange-500/20 border border-orange-500/20 dark:border-orange-500/30 text-orange-600 dark:text-orange-300 text-xs font-bold uppercase tracking-wider">
<span class="w-2 h-2 rounded-full bg-orange-500 animate-ping"></span>
<span>CANLI PROJE YÖNETİM & TAKİP EKRANI</span>
</div>
<h1 class="text-2xl sm:text-4xl font-extrabold font-display leading-tight text-slate-900 dark:text-white">
{{ $project->title }}
</h1>
<p class="text-sm sm:text-base text-slate-600 dark:text-slate-300 font-medium">
Bu ekran, <strong>{{ $project->client_name }}</strong> projesine ait iş takvimini, modül durumlarını ve canlı Kanban panosunu izleyebileceğiniz portal ekranıdır.
</p>
<!-- Meta Tags -->
<div class="flex flex-wrap items-center gap-4 text-xs font-semibold pt-2 text-slate-600 dark:text-slate-300">
@if($project->start_date)
<div class="flex items-center gap-1.5 bg-slate-100 dark:bg-slate-800/80 px-3.5 py-2 rounded-xl border border-slate-200 dark:border-slate-700">
<i data-lucide="calendar" class="w-3.5 h-3.5 text-orange-600 dark:text-orange-400"></i>
<span>Başlangıç: {{ $project->start_date->format('d.m.Y') }}</span>
</div>
@endif
@if($project->target_date)
<div class="flex items-center gap-1.5 bg-slate-100 dark:bg-slate-800/80 px-3.5 py-2 rounded-xl border border-slate-200 dark:border-slate-700">
<i data-lucide="flag" class="w-3.5 h-3.5 text-rose-600 dark:text-rose-400"></i>
<span>Hedef Bitiş: {{ $project->target_date->format('d.m.Y') }}</span>
</div>
@endif
</div>
</div>
<!-- Right: Progress Meter -->
<div class="lg:col-span-4 flex flex-col items-center justify-center p-6 bg-slate-50 dark:bg-slate-800/60 backdrop-blur-md rounded-2xl border border-slate-200 dark:border-slate-700/60 shadow-sm">
<span class="text-xs font-bold text-slate-500 dark:text-slate-400 uppercase tracking-widest mb-2">GENEL PROJE İLERLEMESİ</span>
<div id="progress-percent-val" class="text-5xl font-extrabold font-display text-orange-600 dark:text-transparent dark:bg-clip-text dark:bg-gradient-to-r dark:from-orange-400 dark:to-rose-400 mb-3">
%{{ $project->progress_percent }}
</div>
<div class="w-full bg-slate-200 dark:bg-slate-700 h-3 rounded-full overflow-hidden mb-3">
<div id="progress-bar-fill" class="bg-gradient-to-r from-orange-500 to-rose-500 h-full rounded-full transition-all duration-700 shadow-md shadow-orange-500/40" style="width: {{ $project->progress_percent }}%"></div>
</div>
<span id="progress-status-text" class="text-xs text-slate-700 dark:text-slate-300 font-semibold text-center">
@if($project->progress_percent >= 100)
🎉 Proje %100 Başarıyla Tamamlandı!
@elseif($project->progress_percent >= 50)
⚡ Geliştirmeler Hızla Devam Ediyor
@else
🚀 Faz 1 Çalışmaları Başlatıldı
@endif
</span>
</div>
</div>
</div>
<!-- Section Navigation Cards -->
<div class="grid grid-cols-1 md:grid-cols-4 gap-4">
<a href="#gantt-section" class="glass-card p-4 rounded-2xl hover:border-orange-500/50 transition-all flex items-center gap-3">
<div class="w-10 h-10 rounded-xl bg-orange-500/10 text-orange-600 dark:text-orange-400 flex items-center justify-center font-bold">
<i data-lucide="gantt-chart-square" class="w-5 h-5"></i>
</div>
<div>
<h4 class="text-sm font-bold text-slate-900 dark:text-white">İş Takvimi (Gantt)</h4>
<p class="text-xs text-slate-500">12 Haftalık Zaman Çizelgesi</p>
</div>
</a>
<a href="#modules-section" class="glass-card p-4 rounded-2xl hover:border-orange-500/50 transition-all flex items-center gap-3">
<div class="w-10 h-10 rounded-xl bg-rose-500/10 text-rose-600 dark:text-rose-400 flex items-center justify-center font-bold">
<i data-lucide="layers" class="w-5 h-5"></i>
</div>
<div>
<h4 class="text-sm font-bold text-slate-900 dark:text-white">Hizmet Modülleri</h4>
<p class="text-xs text-slate-500"><span id="completed-modules-count">{{ $project->modules->where('status', 'completed')->count() }}</span> / {{ $project->modules->count() }} Modül Bitti</p>
</div>
</a>
<a href="#kanban-section" class="glass-card p-4 rounded-2xl hover:border-orange-500/50 transition-all flex items-center gap-3">
<div class="w-10 h-10 rounded-xl bg-amber-500/10 text-amber-600 dark:text-amber-400 flex items-center justify-center font-bold">
<i data-lucide="kanban-square" class="w-5 h-5"></i>
</div>
<div>
<h4 class="text-sm font-bold text-slate-900 dark:text-white">Kanban Panosu</h4>
<p class="text-xs text-slate-500"><span id="total-tasks-count">{{ $project->tasks->count() }}</span> Adet Görev Kartı</p>
</div>
</a>
<a href="#updates-section" class="glass-card p-4 rounded-2xl hover:border-orange-500/50 transition-all flex items-center gap-3">
<div class="w-10 h-10 rounded-xl bg-emerald-500/10 text-emerald-600 dark:text-emerald-400 flex items-center justify-center font-bold">
<i data-lucide="activity" class="w-5 h-5"></i>
</div>
<div>
<h4 class="text-sm font-bold text-slate-900 dark:text-white">Canlı Log Akışı</h4>
<p class="text-xs text-slate-500">{{ $project->updates->count() }} Duyuru Yayınlandı</p>
</div>
</a>
</div>
<!-- Section 1: Gantt Chart -->
<section id="gantt-section" class="glass-card rounded-3xl p-6 sm:p-8 space-y-4">
<div class="flex items-center justify-between border-b border-slate-200 dark:border-slate-800 pb-4">
<div class="flex items-center gap-3">
<div class="w-8 h-8 rounded-xl bg-orange-600 text-white flex items-center justify-center font-bold">
<i data-lucide="calendar" class="w-4 h-4"></i>
</div>
<div>
<h2 class="text-lg font-bold text-slate-900 dark:text-white font-display">1. İş Takvimi ve Gantt Çizelgesi</h2>
<p class="text-xs text-slate-700 dark:text-slate-300 font-medium">Proje aşamalarının ve geliştirme süreçlerinin zamansal planı</p>
</div>
</div>
</div>
<!-- Mermaid Gantt Container -->
<div class="mermaid-container no-print">
<div class="mermaid-toolbar">
<span class="text-xs font-bold uppercase tracking-wider text-slate-400">GANTT ZAMAN ÇİZELGESİ</span>
<div class="ml-auto flex items-center gap-2">
<button onclick="zoomGantt('out')" class="p-1.5 rounded-lg border border-slate-200 dark:border-slate-800 hover:bg-slate-100 dark:hover:bg-slate-800 text-xs font-bold">Zoom -</button>
<span id="gantt-zoom-pct" class="text-xs font-mono text-slate-500">100%</span>
<button onclick="zoomGantt('in')" class="p-1.5 rounded-lg border border-slate-200 dark:border-slate-800 hover:bg-slate-100 dark:hover:bg-slate-800 text-xs font-bold">Zoom +</button>
</div>
</div>
<div class="mermaid-canvas" id="gantt-canvas">
<div class="mermaid w-full h-full">
gantt
title {{ $project->title }} (İş Takvimi)
dateFormat YYYY-MM-DD
section Faz 1: İş Takip & WhatsApp Otomasyonu
Backend REST API & Veritabanı :a1, 2026-08-01, 21d
Flutter Responsive Kanban Board :a2, after a1, 21d
WhatsApp API & AI NLP Parser :a3, after a2, 14d
AI Görsel Analiz & PDF Raporlama :a4, after a3, 14d
section Faz 2: CMS Migrasyonu & Web Sync
Citrus CMS Migrasyonu & Formlar :b1, 2026-10-10, 7d
Portfolyo Web Sync & Nihai Testler :b2, after b1, 7d
</div>
</div>
</div>
</section>
<!-- Section 2: Modules Completion Matrix -->
<section id="modules-section" class="glass-card rounded-3xl p-6 sm:p-8 space-y-6">
<div class="flex flex-col sm:flex-row sm:items-center justify-between border-b border-slate-200 dark:border-slate-800 pb-4 gap-4">
<div class="flex items-center gap-3">
<div class="w-8 h-8 rounded-xl bg-rose-600 text-white flex items-center justify-center font-bold">
<i data-lucide="check-square" class="w-4 h-4"></i>
</div>
<div>
<h2 class="text-lg font-bold text-slate-900 dark:text-white font-display">2. Hizmet Modülleri İlerleme Matrisi</h2>
<p class="text-xs text-slate-500">Sözleşmedeki hizmet kalemlerinin tamamlama durumları</p>
</div>
</div>
</div>
<!-- Modules Grid -->
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
@forelse($project->modules as $module)
<div id="module-card-{{ $module->id }}" class="p-5 rounded-2xl border transition-all duration-300 {{ $module->status === 'completed' ? 'bg-emerald-50/40 dark:bg-emerald-950/20 border-emerald-500/30' : ($module->status === 'in_progress' ? 'bg-orange-50/40 dark:bg-orange-950/20 border-orange-500/30' : 'bg-slate-100/50 dark:bg-slate-800/40 border-slate-200 dark:border-slate-800') }}">
<div class="flex items-start justify-between gap-3 mb-3">
<div class="flex items-center gap-2.5">
<div id="module-icon-{{ $module->id }}">
@if($module->status === 'completed')
<div class="w-6 h-6 rounded-full bg-emerald-500 text-white flex items-center justify-center flex-shrink-0">
<i data-lucide="check" class="w-3.5 h-3.5"></i>
</div>
@elseif($module->status === 'in_progress')
<div class="w-6 h-6 rounded-full bg-orange-500 text-white flex items-center justify-center flex-shrink-0 animate-pulse">
<i data-lucide="play" class="w-3 h-3"></i>
</div>
@else
<div class="w-6 h-6 rounded-full bg-slate-300 dark:bg-slate-700 text-slate-600 dark:text-slate-400 flex items-center justify-center flex-shrink-0">
<i data-lucide="clock" class="w-3.5 h-3.5"></i>
</div>
@endif
</div>
<h3 class="font-bold text-slate-900 dark:text-white text-base">
{{ $module->title }}
</h3>
</div>
<span id="module-badge-{{ $module->id }}" class="text-xs font-extrabold px-2.5 py-1 rounded-lg {{ $module->status === 'completed' ? 'bg-emerald-500/10 text-emerald-600 dark:text-emerald-400' : ($module->status === 'in_progress' ? 'bg-orange-500/10 text-orange-600 dark:text-orange-400' : 'bg-slate-200 dark:bg-slate-700 text-slate-600 dark:text-slate-400') }}">
@if($module->status === 'completed') TAMAMLANDI @elseif($module->status === 'in_progress') DEVAM EDİYOR @else BEKLİYOR @endif
</span>
</div>
@if($module->description)
<p class="text-xs text-slate-600 dark:text-slate-400 leading-relaxed mb-4 pl-8">
{{ $module->description }}
</p>
@endif
<div class="pt-3 border-t border-slate-200/60 dark:border-slate-800/60 flex flex-wrap items-center justify-between gap-2">
<span class="text-xs font-semibold text-slate-500">Ağırlık: %{{ $module->weight_percent }}</span>
@if($isAdminMode)
<div class="flex items-center gap-1.5">
<button onclick="setModuleStatusAjax({{ $module->id }}, 'pending')" class="px-2.5 py-1 rounded-lg text-xs font-bold transition-all hover:scale-105 active:scale-95 {{ $module->status === 'pending' ? 'bg-slate-700 text-white shadow-sm' : 'bg-slate-200 dark:bg-slate-800 text-slate-600 dark:text-slate-400 hover:bg-slate-300' }}">
Bekliyor
</button>
<button onclick="setModuleStatusAjax({{ $module->id }}, 'in_progress')" class="px-2.5 py-1 rounded-lg text-xs font-bold transition-all hover:scale-105 active:scale-95 {{ $module->status === 'in_progress' ? 'bg-orange-600 text-white shadow-sm' : 'bg-slate-200 dark:bg-slate-800 text-slate-600 dark:text-slate-400 hover:bg-orange-100 dark:hover:bg-orange-950' }}">
Devam Ediyor
</button>
<button onclick="setModuleStatusAjax({{ $module->id }}, 'completed')" class="px-2.5 py-1 rounded-lg text-xs font-bold transition-all hover:scale-105 active:scale-95 {{ $module->status === 'completed' ? 'bg-emerald-600 text-white shadow-sm' : 'bg-slate-200 dark:bg-slate-800 text-slate-600 dark:text-slate-400 hover:bg-emerald-100 dark:hover:bg-emerald-950' }}">
✓ Tamamlandı
</button>
</div>
@endif
</div>
</div>
@empty
<div class="col-span-full text-center py-8 text-slate-400 text-sm">
Henüz tanımlanmış modül bulunmamaktadır.
</div>
@endforelse
</div>
</section>
<!-- Section 3: Live Kanban Board -->
<section id="kanban-section" class="glass-card rounded-3xl p-6 sm:p-8 space-y-6">
<div class="flex flex-col sm:flex-row sm:items-center justify-between border-b border-slate-200 dark:border-slate-800 pb-4 gap-4">
<div class="flex items-center gap-3">
<div class="w-8 h-8 rounded-xl bg-amber-600 text-white flex items-center justify-center font-bold">
<i data-lucide="kanban-square" class="w-4 h-4"></i>
</div>
<div>
<h2 class="text-lg font-bold text-slate-900 dark:text-white font-display">3. Canlı Kanban Görev Panosu</h2>
<p class="text-xs text-slate-500">Mühendislik ekibinin anlık görev statüleri</p>
</div>
</div>
@if($isAdminMode)
<button onclick="openAddTaskModal()" class="px-4 py-2 rounded-xl bg-orange-600 hover:bg-orange-700 text-white font-bold text-xs shadow-md transition-all flex items-center gap-1.5 self-start sm:self-auto">
<i data-lucide="plus" class="w-4 h-4"></i>
<span>Yeni Görev Ekle</span>
</button>
@endif
</div>
<!-- 4 Column Drag and Drop Board -->
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-6">
<!-- Column 1: To Do -->
<div class="bg-slate-100/70 dark:bg-slate-800/50 rounded-2xl p-4 space-y-3 border border-slate-200 dark:border-slate-700 flex flex-col">
<div class="flex items-center justify-between pb-2 border-b border-slate-200 dark:border-slate-700">
<span class="text-xs font-bold uppercase tracking-wider text-slate-600 dark:text-slate-400 flex items-center gap-1.5">
<span class="w-2 h-2 rounded-full bg-slate-400"></span> YAPILACAKLAR
</span>
<span id="count-todo" class="text-xs font-extrabold px-2 py-0.5 rounded-full bg-slate-200 dark:bg-slate-700 text-slate-600 dark:text-slate-300">
{{ $project->tasks->where('status', 'todo')->count() }}
</span>
</div>
<div class="kanban-drop-zone min-h-[220px] flex-1 space-y-3 pt-1" data-status="todo">
@foreach($project->tasks->where('status', 'todo') as $task)
<div class="kanban-card {{ $isAdminMode ? 'cursor-grab active:cursor-grabbing hover:shadow-md' : 'cursor-default' }} p-3.5 bg-white dark:bg-slate-900 rounded-xl border border-slate-200 dark:border-slate-800 shadow-sm space-y-2.5 transition-all" data-task-id="{{ $task->id }}">
<div class="flex items-start justify-between gap-2">
<h4 class="text-xs font-bold text-slate-900 dark:text-white leading-snug">{{ $task->title }}</h4>
@if($isAdminMode)
<button onclick="deleteTaskAjax({{ $task->id }}, this)" class="text-slate-400 hover:text-red-500 p-0.5 flex-shrink-0">
<i data-lucide="trash-2" class="w-3.5 h-3.5"></i>
</button>
@endif
</div>
@if($task->description)
<p class="text-[11px] text-slate-500 line-clamp-2">{{ $task->description }}</p>
@endif
<div class="flex items-center justify-between text-[10px] font-semibold text-slate-400 pt-1.5 border-t border-slate-100 dark:border-slate-800">
<span>{{ $task->assigned_person ?? 'Atanmadı' }}</span>
<span class="px-2 py-0.5 rounded bg-slate-100 dark:bg-slate-800 uppercase text-slate-600 dark:text-slate-300 font-bold">{{ $task->priority }}</span>
</div>
</div>
@endforeach
</div>
</div>
<!-- Column 2: In Progress -->
<div class="bg-orange-50/60 dark:bg-orange-950/20 rounded-2xl p-4 space-y-3 border border-orange-500/20 flex flex-col">
<div class="flex items-center justify-between pb-2 border-b border-orange-200 dark:border-orange-800/40">
<span class="text-xs font-bold uppercase tracking-wider text-orange-600 dark:text-orange-400 flex items-center gap-1.5">
<span class="w-2 h-2 rounded-full bg-orange-500 animate-ping"></span> DEVAM EDENLER
</span>
<span id="count-in_progress" class="text-xs font-extrabold px-2 py-0.5 rounded-full bg-orange-200 dark:bg-orange-900/60 text-orange-700 dark:text-orange-300">
{{ $project->tasks->where('status', 'in_progress')->count() }}
</span>
</div>
<div class="kanban-drop-zone min-h-[220px] flex-1 space-y-3 pt-1" data-status="in_progress">
@foreach($project->tasks->where('status', 'in_progress') as $task)
<div class="kanban-card {{ $isAdminMode ? 'cursor-grab active:cursor-grabbing hover:shadow-md' : 'cursor-default' }} p-3.5 bg-white dark:bg-slate-900 rounded-xl border border-orange-200 dark:border-orange-900/40 shadow-sm space-y-2.5 transition-all" data-task-id="{{ $task->id }}">
<div class="flex items-start justify-between gap-2">
<h4 class="text-xs font-bold text-slate-900 dark:text-white leading-snug">{{ $task->title }}</h4>
@if($isAdminMode)
<button onclick="deleteTaskAjax({{ $task->id }}, this)" class="text-slate-400 hover:text-red-500 p-0.5 flex-shrink-0">
<i data-lucide="trash-2" class="w-3.5 h-3.5"></i>
</button>
@endif
</div>
@if($task->description)
<p class="text-[11px] text-slate-500 line-clamp-2">{{ $task->description }}</p>
@endif
<div class="flex items-center justify-between text-[10px] font-semibold text-orange-600 dark:text-orange-400 pt-1.5 border-t border-slate-100 dark:border-slate-800">
<span>{{ $task->assigned_person ?? 'Yazılım Ekibi' }}</span>
<span class="px-2 py-0.5 rounded bg-orange-100 dark:bg-orange-950 uppercase font-bold">{{ $task->priority }}</span>
</div>
</div>
@endforeach
</div>
</div>
<!-- Column 3: Review -->
<div class="bg-amber-50/60 dark:bg-amber-950/20 rounded-2xl p-4 space-y-3 border border-amber-500/20 flex flex-col">
<div class="flex items-center justify-between pb-2 border-b border-amber-200 dark:border-amber-800/40">
<span class="text-xs font-bold uppercase tracking-wider text-amber-600 dark:text-amber-400 flex items-center gap-1.5">
<span class="w-2 h-2 rounded-full bg-amber-500"></span> KONTROL / TEST
</span>
<span id="count-review" class="text-xs font-extrabold px-2 py-0.5 rounded-full bg-amber-200 dark:bg-amber-900/60 text-amber-700 dark:text-amber-300">
{{ $project->tasks->where('status', 'review')->count() }}
</span>
</div>
<div class="kanban-drop-zone min-h-[220px] flex-1 space-y-3 pt-1" data-status="review">
@foreach($project->tasks->where('status', 'review') as $task)
<div class="kanban-card {{ $isAdminMode ? 'cursor-grab active:cursor-grabbing hover:shadow-md' : 'cursor-default' }} p-3.5 bg-white dark:bg-slate-900 rounded-xl border border-amber-200 dark:border-amber-900/40 shadow-sm space-y-2.5 transition-all" data-task-id="{{ $task->id }}">
<div class="flex items-start justify-between gap-2">
<h4 class="text-xs font-bold text-slate-900 dark:text-white leading-snug">{{ $task->title }}</h4>
@if($isAdminMode)
<button onclick="deleteTaskAjax({{ $task->id }}, this)" class="text-slate-400 hover:text-red-500 p-0.5 flex-shrink-0">
<i data-lucide="trash-2" class="w-3.5 h-3.5"></i>
</button>
@endif
</div>
@if($task->description)
<p class="text-[11px] text-slate-500 line-clamp-2">{{ $task->description }}</p>
@endif
<div class="flex items-center justify-between text-[10px] font-semibold text-amber-600 dark:text-amber-400 pt-1.5 border-t border-slate-100 dark:border-slate-800">
<span>{{ $task->assigned_person ?? 'Test Ekibi' }}</span>
<span class="px-2 py-0.5 rounded bg-amber-100 dark:bg-amber-950 uppercase font-bold">{{ $task->priority }}</span>
</div>
</div>
@endforeach
</div>
</div>
<!-- Column 4: Done -->
<div class="bg-emerald-50/60 dark:bg-emerald-950/20 rounded-2xl p-4 space-y-3 border border-emerald-500/20 flex flex-col">
<div class="flex items-center justify-between pb-2 border-b border-emerald-200 dark:border-emerald-800/40">
<span class="text-xs font-bold uppercase tracking-wider text-emerald-600 dark:text-emerald-400 flex items-center gap-1.5">
<span class="w-2 h-2 rounded-full bg-emerald-500"></span> TAMAMLANANLAR
</span>
<span id="count-done" class="text-xs font-extrabold px-2 py-0.5 rounded-full bg-emerald-200 dark:bg-emerald-900/60 text-emerald-700 dark:text-emerald-300">
{{ $project->tasks->where('status', 'done')->count() }}
</span>
</div>
<div class="kanban-drop-zone min-h-[220px] flex-1 space-y-3 pt-1" data-status="done">
@foreach($project->tasks->where('status', 'done') as $task)
<div class="kanban-card {{ $isAdminMode ? 'cursor-grab active:cursor-grabbing hover:shadow-md' : 'cursor-default' }} p-3.5 bg-white dark:bg-slate-900 rounded-xl border border-emerald-200 dark:border-emerald-900/40 shadow-sm space-y-2.5 transition-all opacity-95" data-task-id="{{ $task->id }}">
<div class="flex items-start justify-between gap-2">
<h4 class="text-xs font-bold text-slate-900 dark:text-white leading-snug line-through decoration-emerald-500">{{ $task->title }}</h4>
@if($isAdminMode)
<button onclick="deleteTaskAjax({{ $task->id }}, this)" class="text-slate-400 hover:text-red-500 p-0.5 flex-shrink-0">
<i data-lucide="trash-2" class="w-3.5 h-3.5"></i>
</button>
@endif
</div>
@if($task->description)
<p class="text-[11px] text-slate-500 line-clamp-2">{{ $task->description }}</p>
@endif
<div class="flex items-center justify-between text-[10px] font-semibold text-emerald-600 dark:text-emerald-400 pt-1.5 border-t border-slate-100 dark:border-slate-800">
<span>Tamamlandı</span>
<i data-lucide="check-circle-2" class="w-3.5 h-3.5 text-emerald-500"></i>
</div>
</div>
@endforeach
</div>
</div>
</div>
</section>
<!-- Section 4: Live Activity Log Stream -->
<section id="updates-section" class="glass-card rounded-3xl p-6 sm:p-8 space-y-6">
<div class="flex flex-col sm:flex-row sm:items-center justify-between border-b border-slate-200 dark:border-slate-800 pb-4 gap-4">
<div class="flex items-center gap-3">
<div class="w-8 h-8 rounded-xl bg-emerald-600 text-white flex items-center justify-center font-bold">
<i data-lucide="message-square" class="w-4 h-4"></i>
</div>
<div>
<h2 class="text-lg font-bold text-slate-900 dark:text-white font-display">4. Canlı İlerleme Duyuruları & Log Akışı</h2>
<p class="text-xs text-slate-500">Müşterinin takip ettiği anlık durum mesajları ve sürüm açıklamaları</p>
</div>
</div>
@if($isAdminMode)
<button onclick="openAddUpdateModal()" class="px-4 py-2 rounded-xl bg-emerald-600 hover:bg-emerald-700 text-white font-bold text-xs shadow-md transition-all flex items-center gap-1.5 self-start sm:self-auto">
<i data-lucide="plus" class="w-4 h-4"></i>
<span>Yeni Duyuru Yayınla</span>
</button>
@endif
</div>
@if($isAdminMode)
<!-- Fast Add Update Box (Admin Only) -->
<div class="bg-slate-100/70 dark:bg-slate-800/60 p-4 sm:p-5 rounded-2xl border border-slate-200 dark:border-slate-700">
<h3 class="text-xs font-bold uppercase tracking-wider text-slate-600 dark:text-slate-300 mb-3 flex items-center gap-1.5">
<i data-lucide="send" class="w-3.5 h-3.5 text-emerald-500"></i>
<span>Hızlı Canlı Duyuru / Log Ekle</span>
</h3>
<form action="{{ route('projects.admin.add-update', $project->slug) }}" method="POST" class="space-y-3">
@csrf
<input type="text" name="title" required placeholder="Duyuru Başlığı (Örn: WhatsApp Webhook Testleri Başlatıldı)" class="w-full px-3.5 py-2 rounded-xl bg-white dark:bg-slate-900 border border-slate-300 dark:border-slate-700 text-xs font-semibold text-slate-800 dark:text-white focus:outline-none focus:border-orange-500">
<textarea name="content" required rows="2" placeholder="Duyuru detayları ve açıklama notu..." class="w-full px-3.5 py-2 rounded-xl bg-white dark:bg-slate-900 border border-slate-300 dark:border-slate-700 text-xs text-slate-800 dark:text-white focus:outline-none focus:border-orange-500"></textarea>
<div class="flex justify-end">
<button type="submit" class="px-4 py-2 rounded-xl bg-emerald-600 hover:bg-emerald-700 text-white font-bold text-xs transition-all shadow-sm">
Duyuruyu Yayınla →
</button>
</div>
</form>
</div>
@endif
<!-- Timeline Stream -->
<div class="relative pl-6 border-l-2 border-orange-500/30 space-y-8 my-4">
@forelse($project->updates as $update)
<div class="relative group">
<div class="absolute -left-[31px] top-0 w-4 h-4 rounded-full bg-orange-600 border-4 border-slate-50 dark:border-slate-900 shadow-md"></div>
<div class="p-5 rounded-2xl bg-slate-100/60 dark:bg-slate-800/40 border border-slate-200 dark:border-slate-800 space-y-2">
<div class="flex items-center justify-between gap-4">
<h3 class="text-sm font-bold text-slate-900 dark:text-white">
{{ $update->title }}
</h3>
<div class="flex items-center gap-3">
<span class="text-[11px] font-semibold text-slate-400">
{{ $update->created_at->format('d.m.Y H:i') }}
</span>
@if($isAdminMode)
<button onclick="deleteUpdateAjax({{ $update->id }}, this)" title="Duyuruyu Sil" class="px-2 py-1 rounded-lg bg-red-50 hover:bg-red-100 text-red-600 dark:bg-red-950/40 dark:hover:bg-red-900/60 dark:text-red-400 font-bold text-xs transition-all flex items-center gap-1 border border-red-200 dark:border-red-800/40">
<i data-lucide="trash-2" class="w-3.5 h-3.5"></i>
<span>Sil</span>
</button>
@endif
</div>
</div>
<p class="text-xs text-slate-600 dark:text-slate-300 leading-relaxed whitespace-pre-line">
{{ $update->content }}
</p>
@if($update->progress_percent_at_update !== null)
<div class="pt-2 flex items-center gap-2 text-xs font-semibold text-orange-600 dark:text-orange-400">
<i data-lucide="trending-up" class="w-3.5 h-3.5"></i>
<span>Güncelleme Anındaki İlerleme: %{{ $update->progress_percent_at_update }}</span>
</div>
@endif
</div>
</div>
@empty
<div class="text-center py-6 text-slate-400 text-sm italic">
Henüz yayınlanmış bir duyuru bulunmamaktadır.
</div>
@endforelse
</div>
</section>
</main>
@if($isAdminMode)
<!-- Add Task Modal (Admin Only) -->
<div id="add-task-modal" class="fixed inset-0 z-50 bg-slate-900/80 backdrop-blur-sm hidden flex items-center justify-center p-4">
<div class="bg-white dark:bg-slate-900 rounded-3xl max-w-lg w-full p-6 space-y-4 shadow-2xl border border-slate-200 dark:border-slate-800">
<div class="flex items-center justify-between border-b border-slate-200 dark:border-slate-800 pb-3">
<h3 class="font-bold text-base text-slate-900 dark:text-white flex items-center gap-2">
<i data-lucide="plus-circle" class="w-4 h-4 text-orange-500"></i>
<span>Panoya Yeni Görev Kartı Ekle</span>
</h3>
<button onclick="closeAddTaskModal()" class="text-slate-400 hover:text-slate-600">
<i data-lucide="x" class="w-5 h-5"></i>
</button>
</div>
<form action="{{ route('projects.admin.add-task', $project->slug) }}" method="POST" class="space-y-4">
@csrf
<div>
<label class="block text-xs font-bold text-slate-700 dark:text-slate-300 mb-1">Görev Başlığı *</label>
<input type="text" name="title" required placeholder="Örn: Meta WhatsApp Webhook Testleri" class="w-full px-3.5 py-2.5 rounded-xl bg-slate-50 dark:bg-slate-800 border border-slate-300 dark:border-slate-700 text-xs font-semibold text-slate-900 dark:text-white focus:outline-none focus:border-orange-500">
</div>
<div>
<label class="block text-xs font-bold text-slate-700 dark:text-slate-300 mb-1">Açıklama / Detaylar</label>
<textarea name="description" rows="3" placeholder="Görev detayları..." class="w-full px-3.5 py-2.5 rounded-xl bg-slate-50 dark:bg-slate-800 border border-slate-300 dark:border-slate-700 text-xs text-slate-900 dark:text-white focus:outline-none focus:border-orange-500"></textarea>
</div>
<div class="grid grid-cols-2 gap-3">
<div>
<label class="block text-xs font-bold text-slate-700 dark:text-slate-300 mb-1">Başlangıç Durumu</label>
<select name="status" class="w-full px-3 py-2 rounded-xl bg-slate-50 dark:bg-slate-800 border border-slate-300 dark:border-slate-700 text-xs font-semibold text-slate-900 dark:text-white">
<option value="todo">Yapılacaklar</option>
<option value="in_progress">Devam Edenler</option>
<option value="review">Kontrol / Test</option>
<option value="done">Tamamlananlar</option>
</select>
</div>
<div>
<label class="block text-xs font-bold text-slate-700 dark:text-slate-300 mb-1">Öncelik</label>
<select name="priority" class="w-full px-3 py-2 rounded-xl bg-slate-50 dark:bg-slate-800 border border-slate-300 dark:border-slate-700 text-xs font-semibold text-slate-900 dark:text-white">
<option value="low">Düşük</option>
<option value="medium" selected>Orta</option>
<option value="high">Yüksek</option>
<option value="urgent">Acil</option>
</select>
</div>
</div>
<div>
<label class="block text-xs font-bold text-slate-700 dark:text-slate-300 mb-1">Sorumlu Kişi / Ekip</label>
<input type="text" name="assigned_person" placeholder="Örn: Backend Ekibi" class="w-full px-3.5 py-2 rounded-xl bg-slate-50 dark:bg-slate-800 border border-slate-300 dark:border-slate-700 text-xs font-semibold text-slate-900 dark:text-white focus:outline-none focus:border-orange-500">
</div>
<div class="pt-2 flex justify-end gap-2">
<button type="button" onclick="closeAddTaskModal()" class="px-4 py-2 rounded-xl bg-slate-200 dark:bg-slate-800 text-slate-700 dark:text-slate-300 font-bold text-xs">İptal</button>
<button type="submit" class="px-4 py-2 rounded-xl bg-orange-600 hover:bg-orange-700 text-white font-bold text-xs shadow-md">Görevi Ekle →</button>
</div>
</form>
</div>
</div>
@endif
<!-- Corporate Footer -->
<footer class="mt-16 border-t border-slate-200 dark:border-slate-800 py-8 text-center text-xs text-slate-500">
<div class="max-w-7xl mx-auto px-4 space-y-2">
<p>© {{ date('Y') }} <strong>Trunçgil Teknoloji Sanayi ve Ticaret Ltd. Şti.</strong> — Gaziantep Teknopark</p>
<p class="text-[11px] text-slate-400">Tüm geliştirmeler 4691 Sayılı Teknoloji Geliştirme Bölgeleri Kanunu Ar-Ge standartlarında yürütülmektedir.</p>
</div>
</footer>
<!-- JS Libraries -->
<script src="https://cdn.jsdelivr.net/npm/mermaid@10.8.0/dist/mermaid.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/svg-pan-zoom@3.6.1/dist/svg-pan-zoom.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/lucide@0.321.0/dist/umd/lucide.min.js"></script>
<script>
lucide.createIcons();
// Dark Mode Controller
var themeToggleBtn = document.getElementById('theme-toggle');
var themeToggleDarkIcon = document.getElementById('theme-toggle-dark-icon');
var themeToggleLightIcon = document.getElementById('theme-toggle-light-icon');
if (localStorage.getItem('color-theme') === 'dark' || (!('color-theme' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
document.documentElement.classList.add('dark');
themeToggleLightIcon.classList.remove('hidden');
} else {
document.documentElement.classList.remove('dark');
themeToggleDarkIcon.classList.remove('hidden');
}
themeToggleBtn.addEventListener('click', function() {
themeToggleDarkIcon.classList.toggle('hidden');
themeToggleLightIcon.classList.toggle('hidden');
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');
}
});
// Mermaid Init
const isDark = document.documentElement.classList.contains('dark');
mermaid.initialize({
startOnLoad: true,
theme: 'base',
securityLevel: 'loose',
themeVariables: {
fontFamily: 'Inter, sans-serif',
darkMode: isDark,
background: isDark ? '#0f172a' : '#ffffff',
primaryColor: '#ea580c',
primaryTextColor: '#ffffff',
primaryBorderColor: '#c2410c',
lineColor: '#ea580c',
secondaryColor: '#ef4444',
secondaryTextColor: '#ffffff',
tertiaryColor: isDark ? '#1e293b' : '#fff7ed',
textColor: isDark ? '#f8fafc' : '#0f172a',
titleColor: isDark ? '#ffffff' : '#0f172a',
sectionBkgColor: isDark ? 'rgba(234, 88, 12, 0.2)' : 'rgba(234, 88, 12, 0.08)',
sectionBkgColor2: isDark ? 'rgba(239, 68, 68, 0.2)' : 'rgba(239, 68, 68, 0.08)',
gridColor: isDark ? 'rgba(255, 255, 255, 0.15)' : '#cbd5e1',
todayLineColor: '#ef4444'
}
});
let ganttPanZoom = null;
setTimeout(() => {
const svg = document.querySelector('#gantt-canvas svg');
if (svg) {
svg.style.width = '100%';
svg.style.height = '100%';
if (!svg.id) svg.id = 'gantt-svg';
try {
ganttPanZoom = svgPanZoom('#' + svg.id, {
zoomEnabled: true,
panEnabled: true,
fit: true,
center: true,
minZoom: 0.3,
maxZoom: 8,
onZoom: function(z) {
document.getElementById('gantt-zoom-pct').textContent = Math.round(z * 100) + '%';
}
});
} catch(e) {}
}
}, 300);
function zoomGantt(type) {
if (!ganttPanZoom) return;
if (type === 'in') ganttPanZoom.zoomIn();
else ganttPanZoom.zoomOut();
}
@if($isAdminMode)
// Sortable.js Drag and Drop Setup (Admin Only)
function initKanbanSortable() {
if (typeof Sortable === 'undefined') {
setTimeout(initKanbanSortable, 100);
return;
}
document.querySelectorAll('.kanban-drop-zone').forEach(zone => {
new Sortable(zone, {
group: 'kanban-board',
animation: 200,
ghostClass: 'sortable-ghost',
dragClass: 'sortable-drag',
onEnd: function(evt) {
const itemEl = evt.item;
const targetZone = evt.to;
const taskId = itemEl.getAttribute('data-task-id');
const newStatus = targetZone.getAttribute('data-status');
if (!taskId || !newStatus) return;
fetch('{{ route("projects.admin.task-status", $project->slug) }}', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').getAttribute('content'),
'Accept': 'application/json',
'X-Requested-With': 'XMLHttpRequest'
},
body: JSON.stringify({
task_id: taskId,
status: newStatus
})
})
.then(res => res.json())
.then(data => {
if (data.success) {
updateProgressUI(data.progress_percent);
if (data.counts) {
updateCountsUI(data.counts);
}
showToast(data.message, 'success');
} else {
showToast(data.message || 'Görev taşınamadı.', 'error');
}
})
.catch(err => {
console.error('AJAX Error:', err);
showToast('Bağlantı hatası.', 'error');
});
}
});
});
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initKanbanSortable);
} else {
initKanbanSortable();
}
// AJAX Module Status Toggle Handler
function setModuleStatusAjax(moduleId, status) {
fetch('{{ route("projects.admin.module-status", $project->slug) }}', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').getAttribute('content'),
'Accept': 'application/json',
'X-Requested-With': 'XMLHttpRequest'
},
body: JSON.stringify({
module_id: moduleId,
status: status
})
})
.then(res => res.json())
.then(data => {
if (data.success) {
updateProgressUI(data.progress_percent);
showToast(data.message, 'success');
setTimeout(() => location.reload(), 400);
}
});
}
// AJAX Delete Task
function deleteTaskAjax(taskId, btnEl) {
if (!confirm('Bu görevi silmek istediğinize emin misiniz?')) return;
fetch('{{ route("projects.admin.delete-task", $project->slug) }}', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').getAttribute('content'),
'Accept': 'application/json',
'X-Requested-With': 'XMLHttpRequest'
},
body: JSON.stringify({
task_id: taskId
})
})
.then(res => {
const card = btnEl.closest('.kanban-card');
if (card) card.remove();
showToast('Görev silindi.', 'success');
setTimeout(() => location.reload(), 500);
});
}
// AJAX Delete Announcement / Update
function deleteUpdateAjax(updateId, btnEl) {
if (!confirm('Bu duyuruyu silmek istediğinize emin misiniz?')) return;
fetch('{{ route("projects.admin.delete-update", $project->slug) }}', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').getAttribute('content'),
'Accept': 'application/json',
'X-Requested-With': 'XMLHttpRequest'
},
body: JSON.stringify({
update_id: updateId
})
})
.then(res => res.json())
.then(data => {
if (data.success) {
const card = btnEl.closest('.relative.group');
if (card) {
card.style.transition = 'all 0.3s ease';
card.style.opacity = '0';
card.style.transform = 'scale(0.95)';
setTimeout(() => card.remove(), 300);
}
showToast(data.message, 'success');
} else {
showToast('Duyuru silinemedi.', 'error');
}
})
.catch(err => {
console.error('AJAX Error:', err);
showToast('Bağlantı hatası.', 'error');
});
}
@endif
// Dynamic UI Updates
function updateProgressUI(pct) {
const valEl = document.getElementById('progress-percent-val');
const barFill = document.getElementById('progress-bar-fill');
if (valEl) valEl.textContent = '%' + pct;
if (barFill) barFill.style.width = pct + '%';
}
function updateCountsUI(counts) {
if (counts.todo !== undefined) document.getElementById('count-todo').textContent = counts.todo;
if (counts.in_progress !== undefined) document.getElementById('count-in_progress').textContent = counts.in_progress;
if (counts.review !== undefined) document.getElementById('count-review').textContent = counts.review;
if (counts.done !== undefined) document.getElementById('count-done').textContent = counts.done;
}
// Floating Toast Notification
function showToast(msg, type = 'success') {
const toast = document.getElementById('toast-notification');
const toastMsg = document.getElementById('toast-message');
const toastIcon = document.getElementById('toast-icon');
if (toastMsg) toastMsg.textContent = msg;
if (toastIcon) {
toastIcon.className = 'w-6 h-6 rounded-full flex items-center justify-center flex-shrink-0 font-bold text-xs ' +
(type === 'success' ? 'bg-emerald-500 text-white' : 'bg-red-500 text-white');
toastIcon.textContent = type === 'success' ? '✓' : '!';
}
if (toast) {
toast.classList.remove('translate-y-20', 'opacity-0');
toast.classList.add('translate-y-0', 'opacity-100');
setTimeout(() => {
toast.classList.remove('translate-y-0', 'opacity-100');
toast.classList.add('translate-y-20', 'opacity-0');
}, 3000);
}
}
@if($isAdminMode)
// Modal Handlers
function openAddTaskModal() {
document.getElementById('add-task-modal').classList.remove('hidden');
}
function closeAddTaskModal() {
document.getElementById('add-task-modal').classList.add('hidden');
}
function openAddUpdateModal() {
window.location.hash = 'updates-section';
}
@endif
</script>
@endif
</body>
</html>