refactor: implement strict admin authentication for management actions and add PIN verification flow for client access
This commit is contained in:
@@ -25,10 +25,13 @@ class ProjectController extends Controller
|
||||
// Recalculate progress dynamically
|
||||
$project->recalculateProgress();
|
||||
|
||||
// Check if admin mode is active (logged in user OR session toggle)
|
||||
$isAdminMode = Auth::check() || session()->get('admin_mode_' . $project->id, true);
|
||||
// Strict Admin Check: ONLY logged-in admin users can edit/manage
|
||||
$isAdminMode = Auth::check();
|
||||
|
||||
return view('projects.show', compact('project', 'isAdminMode'));
|
||||
// Client PIN Verification Check: Admins are auto-verified, clients need PIN verification in session
|
||||
$isVerified = $isAdminMode || session()->get('project_access_' . $project->id, false);
|
||||
|
||||
return view('projects.show', compact('project', 'isAdminMode', 'isVerified'));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -55,6 +58,13 @@ class ProjectController extends Controller
|
||||
*/
|
||||
public function updateModuleStatus(Request $request, string $slug)
|
||||
{
|
||||
if (!Auth::check()) {
|
||||
if ($request->wantsJson() || $request->ajax()) {
|
||||
return response()->json(['success' => false, 'message' => 'Bu işlem için yönetici girişi yapmanız gerekmektedir.'], 403);
|
||||
}
|
||||
return back()->withErrors(['error' => 'Yetkisiz erişim.']);
|
||||
}
|
||||
|
||||
$project = Project::where('slug', $slug)->firstOrFail();
|
||||
|
||||
$request->validate([
|
||||
@@ -67,8 +77,6 @@ class ProjectController extends Controller
|
||||
->firstOrFail();
|
||||
|
||||
$module->update(['status' => $request->input('status')]);
|
||||
|
||||
// Recalculate overall progress %
|
||||
$newProgress = $project->recalculateProgress();
|
||||
|
||||
if ($request->wantsJson() || $request->ajax()) {
|
||||
@@ -87,6 +95,13 @@ class ProjectController extends Controller
|
||||
*/
|
||||
public function updateTaskStatus(Request $request, string $slug)
|
||||
{
|
||||
if (!Auth::check()) {
|
||||
if ($request->wantsJson() || $request->ajax()) {
|
||||
return response()->json(['success' => false, 'message' => 'Bu işlem için yönetici girişi yapmanız gerekmektedir.'], 403);
|
||||
}
|
||||
return back()->withErrors(['error' => 'Yetkisiz erişim.']);
|
||||
}
|
||||
|
||||
$project = Project::where('slug', $slug)->firstOrFail();
|
||||
|
||||
$request->validate([
|
||||
@@ -123,6 +138,10 @@ class ProjectController extends Controller
|
||||
*/
|
||||
public function addTask(Request $request, string $slug)
|
||||
{
|
||||
if (!Auth::check()) {
|
||||
return back()->withErrors(['error' => 'Yetkisiz erişim.']);
|
||||
}
|
||||
|
||||
$project = Project::where('slug', $slug)->firstOrFail();
|
||||
|
||||
$request->validate([
|
||||
@@ -154,6 +173,13 @@ class ProjectController extends Controller
|
||||
*/
|
||||
public function deleteTask(Request $request, string $slug)
|
||||
{
|
||||
if (!Auth::check()) {
|
||||
if ($request->wantsJson() || $request->ajax()) {
|
||||
return response()->json(['success' => false, 'message' => 'Yetkisiz erişim.'], 403);
|
||||
}
|
||||
return back()->withErrors(['error' => 'Yetkisiz erişim.']);
|
||||
}
|
||||
|
||||
$project = Project::where('slug', $slug)->firstOrFail();
|
||||
|
||||
$request->validate([
|
||||
@@ -176,6 +202,10 @@ class ProjectController extends Controller
|
||||
*/
|
||||
public function addUpdate(Request $request, string $slug)
|
||||
{
|
||||
if (!Auth::check()) {
|
||||
return back()->withErrors(['error' => 'Yetkisiz erişim.']);
|
||||
}
|
||||
|
||||
$project = Project::where('slug', $slug)->firstOrFail();
|
||||
|
||||
$request->validate([
|
||||
@@ -200,6 +230,13 @@ class ProjectController extends Controller
|
||||
*/
|
||||
public function deleteUpdate(Request $request, string $slug)
|
||||
{
|
||||
if (!Auth::check()) {
|
||||
if ($request->wantsJson() || $request->ajax()) {
|
||||
return response()->json(['success' => false, 'message' => 'Yetkisiz erişim.'], 403);
|
||||
}
|
||||
return back()->withErrors(['error' => 'Yetkisiz erişim.']);
|
||||
}
|
||||
|
||||
$project = Project::where('slug', $slug)->firstOrFail();
|
||||
|
||||
$request->validate([
|
||||
@@ -227,22 +264,13 @@ class ProjectController extends Controller
|
||||
*/
|
||||
public function recalculate(Request $request, string $slug)
|
||||
{
|
||||
if (!Auth::check()) {
|
||||
return back()->withErrors(['error' => 'Yetkisiz erişim.']);
|
||||
}
|
||||
|
||||
$project = Project::where('slug', $slug)->firstOrFail();
|
||||
$pct = $project->recalculateProgress();
|
||||
|
||||
return back()->with('success', "Proje ilerleme yüzdesi yeniden hesaplandı: %{$pct}");
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle Admin / Client View Mode in session
|
||||
*/
|
||||
public function toggleAdminMode(Request $request, string $slug)
|
||||
{
|
||||
$project = Project::where('slug', $slug)->firstOrFail();
|
||||
$key = 'admin_mode_' . $project->id;
|
||||
$current = session()->get($key, true);
|
||||
session()->put($key, !$current);
|
||||
|
||||
return back();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 & Yönetim Portalı | Trunçgil Teknoloji</title>
|
||||
<title>{{ $project->title }} - Canlı Proje Takip Portalı | Trunçgil Teknoloji</title>
|
||||
|
||||
<!-- Google Fonts: Inter & Outfit -->
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
@@ -16,7 +16,9 @@
|
||||
<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 = {
|
||||
@@ -140,6 +142,43 @@
|
||||
</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="text" name="access_code" required placeholder="Örn: {{ $project->client_access_code }}" 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">
|
||||
@@ -148,13 +187,14 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Admin / Manager Floating Top Control Bar -->
|
||||
<!-- 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>WEB YÖNETİCİ DÜZENLEME MODU (INTERACTIVE AJAX & DRAG-AND-DROP)</span>
|
||||
<span>YÖNETİCİ DÜZENLEME MODU (ADMIN GİRİŞİ YAPILDI)</span>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
@@ -183,9 +223,20 @@
|
||||
</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-[41px] z-40">
|
||||
<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 -->
|
||||
@@ -196,7 +247,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 & Yönetim Portalı</span>
|
||||
<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>
|
||||
@@ -252,7 +303,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, modül durumlarını ve sürükle-bırak destekli canlı Kanban panosunu yönetebileceğiniz portal ekranıdır.
|
||||
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 -->
|
||||
@@ -270,11 +321,6 @@
|
||||
<span>Hedef Bitiş: {{ $project->target_date->format('d.m.Y') }}</span>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div class="flex items-center gap-1.5 bg-orange-500/20 px-3 py-1.5 rounded-xl border border-orange-500/40 text-orange-300">
|
||||
<i data-lucide="key" class="w-3.5 h-3.5"></i>
|
||||
<span>Müşteri PIN: <strong>{{ $project->client_access_code }}</strong></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -331,7 +377,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"><span id="total-tasks-count">{{ $project->tasks->count() }}</span> Adet Sürükle-Bırak Kartı</p>
|
||||
<p class="text-xs text-slate-500"><span id="total-tasks-count">{{ $project->tasks->count() }}</span> Adet Görev Kartı</p>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
@@ -389,7 +435,7 @@ gantt
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Section 2: Modules Completion Matrix (INTERACTIVE AJAX STATUS TOGGLES) -->
|
||||
<!-- 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">
|
||||
@@ -398,12 +444,9 @@ gantt
|
||||
</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">Sayfa yenilemeden tek tıkla modül durumlarını canlı değiştirin</p>
|
||||
<p class="text-xs text-slate-500">Sözleşmedeki hizmet kalemlerinin tamamlama durumları</p>
|
||||
</div>
|
||||
</div>
|
||||
<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 -->
|
||||
@@ -443,10 +486,10 @@ gantt
|
||||
</p>
|
||||
@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>
|
||||
|
||||
@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
|
||||
@@ -460,6 +503,7 @@ gantt
|
||||
✓ Tamamlandı
|
||||
</button>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
@empty
|
||||
@@ -470,7 +514,7 @@ gantt
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Section 3: Live Drag-and-Drop Kanban Board (Sortable.js + AJAX) -->
|
||||
<!-- 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">
|
||||
@@ -478,15 +522,17 @@ gantt
|
||||
<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ı 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>
|
||||
<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 -->
|
||||
@@ -505,12 +551,14 @@ gantt
|
||||
|
||||
<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="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)
|
||||
@@ -539,12 +587,14 @@ gantt
|
||||
|
||||
<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="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)
|
||||
@@ -573,12 +623,14 @@ gantt
|
||||
|
||||
<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="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)
|
||||
@@ -607,12 +659,14 @@ gantt
|
||||
|
||||
<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="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)
|
||||
@@ -644,13 +698,16 @@ gantt
|
||||
</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>
|
||||
|
||||
<!-- Fast Add Update Box -->
|
||||
@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>
|
||||
@@ -667,6 +724,7 @@ gantt
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<!-- Timeline Stream -->
|
||||
<div class="relative pl-6 border-l-2 border-orange-500/30 space-y-8 my-4">
|
||||
@@ -683,10 +741,12 @@ gantt
|
||||
<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>
|
||||
|
||||
@@ -712,7 +772,8 @@ gantt
|
||||
|
||||
</main>
|
||||
|
||||
<!-- Add Task Modal -->
|
||||
@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">
|
||||
@@ -771,6 +832,7 @@ gantt
|
||||
</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">
|
||||
@@ -784,8 +846,6 @@ 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>
|
||||
lucide.createIcons();
|
||||
@@ -868,7 +928,8 @@ gantt
|
||||
else ganttPanZoom.zoomOut();
|
||||
}
|
||||
|
||||
// Sortable.js Drag and Drop Setup for 4 Kanban Columns
|
||||
@if($isAdminMode)
|
||||
// Sortable.js Drag and Drop Setup (Admin Only)
|
||||
function initKanbanSortable() {
|
||||
if (typeof Sortable === 'undefined') {
|
||||
setTimeout(initKanbanSortable, 100);
|
||||
@@ -911,7 +972,7 @@ gantt
|
||||
}
|
||||
showToast(data.message, 'success');
|
||||
} else {
|
||||
showToast('Görev taşınamadı.', 'error');
|
||||
showToast(data.message || 'Görev taşınamadı.', 'error');
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
@@ -1014,6 +1075,7 @@ gantt
|
||||
showToast('Bağlantı hatası.', 'error');
|
||||
});
|
||||
}
|
||||
@endif
|
||||
|
||||
// Dynamic UI Updates
|
||||
function updateProgressUI(pct) {
|
||||
@@ -1054,6 +1116,7 @@ gantt
|
||||
}
|
||||
}
|
||||
|
||||
@if($isAdminMode)
|
||||
// Modal Handlers
|
||||
function openAddTaskModal() {
|
||||
document.getElementById('add-task-modal').classList.remove('hidden');
|
||||
@@ -1064,6 +1127,8 @@ gantt
|
||||
function openAddUpdateModal() {
|
||||
window.location.hash = 'updates-section';
|
||||
}
|
||||
@endif
|
||||
</script>
|
||||
@endif
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user