feat: implement full project management suite with module/task CRUD, progress tracking, and admin view toggling
This commit is contained in:
@@ -5,7 +5,7 @@
|
||||
<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>
|
||||
<title>{{ $project->title }} - Canlı Proje Takip & Yönetim Portalı | Trunçgil Teknoloji</title>
|
||||
|
||||
<!-- Google Fonts: Inter & Outfit -->
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
@@ -40,21 +40,21 @@
|
||||
|
||||
<style type="text/css">
|
||||
.glass-header {
|
||||
background: rgba(255, 255, 255, 0.8);
|
||||
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.8);
|
||||
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.7);
|
||||
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.7);
|
||||
background: rgba(30, 41, 59, 0.75);
|
||||
border: 1px solid rgba(255, 255, 255, 0.06);
|
||||
}
|
||||
.mermaid-container {
|
||||
@@ -122,12 +122,67 @@
|
||||
.dark .mermaid svg .section1, .dark .mermaid svg .section3 {
|
||||
fill: rgba(239, 68, 68, 0.18) !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">
|
||||
|
||||
<!-- 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>
|
||||
|
||||
<!-- Admin / Manager Floating Top Control Bar -->
|
||||
<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>WEB YÖNETİCİ DÜZENLEME MODU (INTERACTIVE AJAX & DRAG-AND-DROP)</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>
|
||||
|
||||
<!-- Header Navigation -->
|
||||
<header class="sticky top-0 z-40 w-full glass-header">
|
||||
<header class="w-full glass-header sticky top-[41px] 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 -->
|
||||
@@ -138,7 +193,7 @@
|
||||
</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-xs font-bold uppercase tracking-wider text-orange-600 dark:text-orange-400">Canlı Proje Takip & Yönetim Portalı</span>
|
||||
<span class="text-sm font-semibold text-slate-700 dark:text-slate-200 line-clamp-1">{{ $project->client_name }}</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -164,6 +219,19 @@
|
||||
<!-- 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-gradient-to-br from-slate-900 via-slate-800 to-orange-950 text-white p-6 sm:p-10 shadow-2xl border border-slate-800">
|
||||
<div class="absolute -top-24 -right-24 w-96 h-96 bg-orange-600/20 rounded-full blur-3xl pointer-events-none"></div>
|
||||
@@ -173,7 +241,7 @@
|
||||
<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/20 border border-orange-500/30 text-orange-300 text-xs font-bold uppercase tracking-wider">
|
||||
<span class="w-2 h-2 rounded-full bg-orange-400 animate-ping"></span>
|
||||
<span>MÜŞTERİ CANLI TAKİP PANELİ</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-white">
|
||||
@@ -181,7 +249,7 @@
|
||||
</h1>
|
||||
|
||||
<p class="text-sm sm:text-base text-slate-300 font-medium">
|
||||
Bu ekran, <strong>{{ $project->client_name }}</strong> projesine ait iş takvimini, tamamlanan hizmet modüllerini ve canlı Kanban görev akışını şeffaf biçimde sunmaktadır.
|
||||
Bu ekran, <strong>{{ $project->client_name }}</strong> projesine ait iş takvimini, modül durumlarını ve sürükle-bırak destekli canlı Kanban panosunu yönetebileceğiniz portal ekranıdır.
|
||||
</p>
|
||||
|
||||
<!-- Meta Tags -->
|
||||
@@ -211,20 +279,19 @@
|
||||
<div class="lg:col-span-4 flex flex-col items-center justify-center p-6 bg-slate-800/60 backdrop-blur-md rounded-2xl border border-slate-700/60 shadow-inner">
|
||||
<span class="text-xs font-bold text-slate-400 uppercase tracking-widest mb-2">GENEL PROJE İLERLEMESİ</span>
|
||||
|
||||
<!-- Progress Donut/Bar -->
|
||||
<div class="text-5xl font-extrabold font-display text-transparent bg-clip-text bg-gradient-to-r from-orange-400 to-rose-400 mb-3">
|
||||
<div id="progress-percent-val" class="text-5xl font-extrabold font-display text-transparent bg-clip-text bg-gradient-to-r from-orange-400 to-rose-400 mb-3">
|
||||
%{{ $project->progress_percent }}
|
||||
</div>
|
||||
|
||||
<div class="w-full bg-slate-700 h-3 rounded-full overflow-hidden mb-3">
|
||||
<div class="bg-gradient-to-r from-orange-500 to-rose-500 h-full rounded-full transition-all duration-1000 shadow-lg shadow-orange-500/50" style="width: {{ $project->progress_percent }}%"></div>
|
||||
<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-lg shadow-orange-500/50" style="width: {{ $project->progress_percent }}%"></div>
|
||||
</div>
|
||||
|
||||
<span class="text-xs text-slate-300 font-medium text-center">
|
||||
<span id="progress-status-text" class="text-xs text-slate-300 font-medium text-center">
|
||||
@if($project->progress_percent >= 100)
|
||||
🎉 Proje %100 Başarıyla Tamamlandı!
|
||||
@elseif($project->progress_percent >= 50)
|
||||
⚡ Proje Geliştirmeleri Hızla Devam Ediyor
|
||||
⚡ Geliştirmeler Hızla Devam Ediyor
|
||||
@else
|
||||
🚀 Faz 1 Çalışmaları Başlatıldı
|
||||
@endif
|
||||
@@ -233,7 +300,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Interactive Section Tabs -->
|
||||
<!-- 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">
|
||||
@@ -251,7 +318,7 @@
|
||||
</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">{{ $project->modules->where('status', 'completed')->count() }} / {{ $project->modules->count() }} Modül Bitti</p>
|
||||
<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>
|
||||
|
||||
@@ -261,7 +328,7 @@
|
||||
</div>
|
||||
<div>
|
||||
<h4 class="text-sm font-bold text-slate-900 dark:text-white">Kanban Panosu</h4>
|
||||
<p class="text-xs text-slate-500">{{ $project->tasks->count() }} Adet Görev Kartı</p>
|
||||
<p class="text-xs text-slate-500"><span id="total-tasks-count">{{ $project->tasks->count() }}</span> Adet Sürükle-Bırak Kartı</p>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
@@ -271,7 +338,7 @@
|
||||
</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() }} İlerleme Duyurusu</p>
|
||||
<p class="text-xs text-slate-500">{{ $project->updates->count() }} Duyuru Yayınlandı</p>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
@@ -319,63 +386,77 @@ gantt
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Section 2: Modules Completion Matrix -->
|
||||
<!-- Section 2: Modules Completion Matrix (INTERACTIVE AJAX STATUS TOGGLES) -->
|
||||
<section id="modules-section" class="glass-card rounded-3xl p-6 sm:p-8 space-y-6">
|
||||
<div class="flex items-center justify-between border-b border-slate-200 dark:border-slate-800 pb-4">
|
||||
<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 tamamlanma ve gidişat durumları</p>
|
||||
<p class="text-xs text-slate-500">Sayfa yenilemeden tek tıkla modül durumlarını canlı değiştirin</p>
|
||||
</div>
|
||||
</div>
|
||||
<span class="text-xs font-bold px-3 py-1 rounded-full bg-slate-100 dark:bg-slate-800 text-slate-600 dark:text-slate-300">
|
||||
Toplam {{ $project->modules->count() }} Modül
|
||||
<span class="text-xs font-bold px-3 py-1.5 rounded-full bg-orange-500/10 text-orange-600 dark:text-orange-400 border border-orange-500/20 self-start sm:self-auto">
|
||||
⚡ Canlı AJAX Düzenleme
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<!-- Modules Grid -->
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
@forelse($project->modules as $module)
|
||||
<div 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 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">
|
||||
@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 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 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') }}">
|
||||
<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-3 pl-8">
|
||||
<p class="text-xs text-slate-600 dark:text-slate-400 leading-relaxed mb-4 pl-8">
|
||||
{{ $module->description }}
|
||||
</p>
|
||||
@endif
|
||||
|
||||
<div class="flex items-center justify-between text-xs font-semibold pt-2 border-t border-slate-200/60 dark:border-slate-800/60 pl-8">
|
||||
<span class="text-slate-500">Ağırlık: %{{ $module->weight_percent }}</span>
|
||||
@if($module->start_date && $module->end_date)
|
||||
<span class="text-slate-400">{{ $module->start_date->format('d.m') }} - {{ $module->end_date->format('d.m.Y') }}</span>
|
||||
@endif
|
||||
<!-- Admin Web Status Toggle Buttons (AJAX enabled) -->
|
||||
<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>
|
||||
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
@empty
|
||||
@@ -386,129 +467,162 @@ gantt
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Section 3: Live Kanban Board -->
|
||||
<!-- Section 3: Live Drag-and-Drop Kanban Board (Sortable.js + AJAX) -->
|
||||
<section id="kanban-section" class="glass-card rounded-3xl p-6 sm:p-8 space-y-6">
|
||||
<div class="flex items-center justify-between border-b border-slate-200 dark:border-slate-800 pb-4">
|
||||
<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 ve çalışma kartları</p>
|
||||
<h2 class="text-lg font-bold text-slate-900 dark:text-white font-display">3. Canlı Sürükle-Bırak Kanban Görev Panosu</h2>
|
||||
<p class="text-xs text-slate-500">Kartları kolonlar arasında sürükleyip bırakarak sayfa yenilemeden canlı güncelleyin</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<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>
|
||||
</div>
|
||||
|
||||
<!-- 4 Column Kanban Board -->
|
||||
<!-- 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">
|
||||
<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> Yapılacaklar
|
||||
<span class="w-2 h-2 rounded-full bg-slate-400"></span> YAPILACAKLAR
|
||||
</span>
|
||||
<span 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">
|
||||
<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>
|
||||
|
||||
@forelse($project->tasks->where('status', 'todo') as $task)
|
||||
<div class="p-3.5 bg-white dark:bg-slate-900 rounded-xl border border-slate-200 dark:border-slate-800 shadow-sm space-y-2">
|
||||
<h4 class="text-xs font-bold text-slate-900 dark:text-white">{{ $task->title }}</h4>
|
||||
@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">
|
||||
<span>{{ $task->assigned_person ?? 'Atanmadı' }}</span>
|
||||
<span class="px-2 py-0.5 rounded bg-slate-100 dark:bg-slate-800 uppercase">{{ $task->priority }}</span>
|
||||
<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 cursor-grab active:cursor-grabbing 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 hover:shadow-md" 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>
|
||||
<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>
|
||||
</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>
|
||||
</div>
|
||||
@empty
|
||||
<p class="text-xs text-slate-400 italic text-center py-4">Görev yok</p>
|
||||
@endforelse
|
||||
@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">
|
||||
<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 class="w-2 h-2 rounded-full bg-orange-500 animate-ping"></span> DEVAM EDENLER
|
||||
</span>
|
||||
<span 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">
|
||||
<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>
|
||||
|
||||
@forelse($project->tasks->where('status', 'in_progress') as $task)
|
||||
<div class="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">
|
||||
<h4 class="text-xs font-bold text-slate-900 dark:text-white">{{ $task->title }}</h4>
|
||||
@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">
|
||||
<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">{{ $task->priority }}</span>
|
||||
<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 cursor-grab active:cursor-grabbing 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 hover:shadow-md" 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>
|
||||
<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>
|
||||
</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>
|
||||
</div>
|
||||
@empty
|
||||
<p class="text-xs text-slate-400 italic text-center py-4">Görev yok</p>
|
||||
@endforelse
|
||||
@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">
|
||||
<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 class="w-2 h-2 rounded-full bg-amber-500"></span> KONTROL / TEST
|
||||
</span>
|
||||
<span 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">
|
||||
<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>
|
||||
|
||||
@forelse($project->tasks->where('status', 'review') as $task)
|
||||
<div class="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">
|
||||
<h4 class="text-xs font-bold text-slate-900 dark:text-white">{{ $task->title }}</h4>
|
||||
@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">
|
||||
<span>{{ $task->assigned_person ?? 'Test Ekibi' }}</span>
|
||||
<span class="px-2 py-0.5 rounded bg-amber-100 dark:bg-amber-950 uppercase">{{ $task->priority }}</span>
|
||||
<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 cursor-grab active:cursor-grabbing 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 hover:shadow-md" 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>
|
||||
<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>
|
||||
</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>
|
||||
</div>
|
||||
@empty
|
||||
<p class="text-xs text-slate-400 italic text-center py-4">Görev yok</p>
|
||||
@endforelse
|
||||
@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">
|
||||
<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 class="w-2 h-2 rounded-full bg-emerald-500"></span> TAMAMLANANLAR
|
||||
</span>
|
||||
<span 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">
|
||||
<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>
|
||||
|
||||
@forelse($project->tasks->where('status', 'done') as $task)
|
||||
<div class="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 opacity-90">
|
||||
<h4 class="text-xs font-bold text-slate-900 dark:text-white line-through decoration-emerald-500">{{ $task->title }}</h4>
|
||||
@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">
|
||||
<span>Tamamlandı</span>
|
||||
<i data-lucide="check-circle-2" class="w-3.5 h-3.5 text-emerald-500"></i>
|
||||
<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 cursor-grab active:cursor-grabbing 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 hover:shadow-md 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>
|
||||
<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>
|
||||
</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>
|
||||
</div>
|
||||
@empty
|
||||
<p class="text-xs text-slate-400 italic text-center py-4">Görev yok</p>
|
||||
@endforelse
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@@ -516,23 +630,45 @@ gantt
|
||||
|
||||
<!-- 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 items-center justify-between border-b border-slate-200 dark:border-slate-800 pb-4">
|
||||
<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">Yazılım yöneticisinden gelen canlı proje güncellemeleri</p>
|
||||
<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>
|
||||
|
||||
<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>
|
||||
</div>
|
||||
|
||||
<!-- Fast Add Update Box -->
|
||||
<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>
|
||||
|
||||
<!-- 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">
|
||||
<!-- Bullet Dot -->
|
||||
<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">
|
||||
@@ -567,6 +703,66 @@ gantt
|
||||
|
||||
</main>
|
||||
|
||||
<!-- Add Task Modal -->
|
||||
<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>
|
||||
|
||||
<!-- 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">
|
||||
@@ -579,9 +775,10 @@ gantt
|
||||
<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>
|
||||
<!-- Sortable JS for Drag and Drop -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/sortablejs@1.15.0/Sortable.min.js"></script>
|
||||
|
||||
<script>
|
||||
// Init Lucide Icons
|
||||
lucide.createIcons();
|
||||
|
||||
// Dark Mode Controller
|
||||
@@ -661,6 +858,153 @@ gantt
|
||||
if (type === 'in') ganttPanZoom.zoomIn();
|
||||
else ganttPanZoom.zoomOut();
|
||||
}
|
||||
|
||||
// Sortable.js Drag and Drop Setup for 4 Kanban Columns
|
||||
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('Görev taşınamadı.', 'error');
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
console.error('AJAX Error:', err);
|
||||
showToast('Bağlantı hatası.', 'error');
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// 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);
|
||||
});
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
||||
// 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';
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user