diff --git a/app/Filament/Admin/Resources/Projects/Pages/CreateProject.php b/app/Filament/Admin/Resources/Projects/Pages/CreateProject.php
new file mode 100644
index 0000000..d394c86
--- /dev/null
+++ b/app/Filament/Admin/Resources/Projects/Pages/CreateProject.php
@@ -0,0 +1,27 @@
+record->recalculateProgress();
+ }
+}
diff --git a/app/Filament/Admin/Resources/Projects/Pages/EditProject.php b/app/Filament/Admin/Resources/Projects/Pages/EditProject.php
new file mode 100644
index 0000000..39317e1
--- /dev/null
+++ b/app/Filament/Admin/Resources/Projects/Pages/EditProject.php
@@ -0,0 +1,56 @@
+record->title;
+ }
+
+ public function getMaxContentWidth(): Width | string | null
+ {
+ return Width::Full;
+ }
+
+ protected function getHeaderActions(): array
+ {
+ return [
+ Action::make('recalculate')
+ ->label('İlerlemeyi Yeniden Hesapla')
+ ->icon('heroicon-o-calculator')
+ ->color('info')
+ ->action(function () {
+ $pct = $this->record->recalculateProgress();
+ Notification::make()
+ ->title('Proje ilerleme yüzdesi güncellendi: %' . $pct)
+ ->success()
+ ->send();
+ }),
+
+ Action::make('preview_portal')
+ ->label('Müşteri Ekranında Gör')
+ ->icon('heroicon-o-eye')
+ ->color('warning')
+ ->url(fn () => route('projects.show', $this->record->slug))
+ ->openUrlInNewTab(),
+
+ DeleteAction::make(),
+ ];
+ }
+
+ protected function afterSave(): void
+ {
+ $this->record->recalculateProgress();
+ }
+}
diff --git a/app/Filament/Admin/Resources/Projects/Pages/ListProjects.php b/app/Filament/Admin/Resources/Projects/Pages/ListProjects.php
new file mode 100644
index 0000000..d8334fd
--- /dev/null
+++ b/app/Filament/Admin/Resources/Projects/Pages/ListProjects.php
@@ -0,0 +1,20 @@
+label('Yeni Proje Başlat'),
+ ];
+ }
+}
diff --git a/app/Filament/Admin/Resources/Projects/ProjectResource.php b/app/Filament/Admin/Resources/Projects/ProjectResource.php
new file mode 100644
index 0000000..3b1463e
--- /dev/null
+++ b/app/Filament/Admin/Resources/Projects/ProjectResource.php
@@ -0,0 +1,72 @@
+ ListProjects::route('/'),
+ 'create' => CreateProject::route('/create'),
+ 'edit' => EditProject::route('/{record}/edit'),
+ ];
+ }
+
+ public static function getRecordRouteBindingEloquentQuery(): Builder
+ {
+ return parent::getRecordRouteBindingEloquentQuery()
+ ->withoutGlobalScopes([
+ SoftDeletingScope::class,
+ ]);
+ }
+}
diff --git a/app/Filament/Admin/Resources/Projects/Schemas/ProjectForm.php b/app/Filament/Admin/Resources/Projects/Schemas/ProjectForm.php
new file mode 100644
index 0000000..6751c8e
--- /dev/null
+++ b/app/Filament/Admin/Resources/Projects/Schemas/ProjectForm.php
@@ -0,0 +1,259 @@
+schema([
+ Tabs::make('ProjectTabs')
+ ->tabs([
+ Tab::make('Proje Detayları & Müşteri')
+ ->icon('heroicon-m-briefcase')
+ ->schema([
+ Section::make('Temel Proje Bilgileri')
+ ->columns(12)
+ ->schema([
+ TextInput::make('title')
+ ->label('Proje Adı')
+ ->required()
+ ->maxLength(255)
+ ->live(onBlur: true)
+ ->afterStateUpdated(function (string $operation, $state, callable $set) {
+ if ($operation !== 'create') return;
+ $set('slug', Str::slug($state));
+ })
+ ->columnSpan(6),
+
+ TextInput::make('slug')
+ ->label('URL Slug')
+ ->required()
+ ->maxLength(255)
+ ->unique(ignoreRecord: true)
+ ->rules(['alpha_dash'])
+ ->columnSpan(6),
+
+ TextInput::make('client_name')
+ ->label('Müşteri Unvanı / Firma')
+ ->required()
+ ->maxLength(255)
+ ->columnSpan(4),
+
+ TextInput::make('client_email')
+ ->label('Müşteri E-Posta')
+ ->email()
+ ->maxLength(255)
+ ->columnSpan(4),
+
+ TextInput::make('client_access_code')
+ ->label('Müşteri Giriş Şifresi / PIN')
+ ->default(fn () => strtoupper(Str::random(6)))
+ ->helperText('Müşterinin takip ekranına giriş yapabileceği şifre')
+ ->columnSpan(4),
+
+ Select::make('proposal_id')
+ ->label('İlişkili Fiyat Teklifi')
+ ->relationship('proposal', 'title')
+ ->searchable()
+ ->preload()
+ ->nullable()
+ ->live()
+ ->afterStateUpdated(function ($state, callable $set) {
+ if ($state) {
+ $prop = \App\Models\Proposal::find($state);
+ if ($prop) {
+ $set('slug', $prop->slug);
+ }
+ }
+ })
+ ->columnSpan(4),
+
+ Select::make('status')
+ ->label('Proje Genel Durumu')
+ ->options([
+ 'planning' => 'Planlama Aşamasında',
+ 'in_progress' => 'Devam Ediyor (Aktif)',
+ 'on_hold' => 'Beklemeye Alındı',
+ 'completed' => 'Tamamlandı',
+ 'cancelled' => 'İptal Edildi',
+ ])
+ ->default('in_progress')
+ ->required()
+ ->columnSpan(4),
+
+ TextInput::make('progress_percent')
+ ->label('İlerleme Yüzdesi (%)')
+ ->numeric()
+ ->default(0)
+ ->suffix('%')
+ ->columnSpan(4),
+
+ DatePicker::make('start_date')
+ ->label('Proje Başlangıç Tarihi')
+ ->native(false)
+ ->displayFormat('d.m.Y')
+ ->columnSpan(6),
+
+ DatePicker::make('target_date')
+ ->label('Hedef Bitiş Tarihi')
+ ->native(false)
+ ->displayFormat('d.m.Y')
+ ->columnSpan(6),
+
+ Textarea::make('notes')
+ ->label('İç Notlar ve Açıklamalar')
+ ->rows(3)
+ ->columnSpanFull(),
+ ]),
+ ]),
+
+ Tab::make('Hizmet Modülleri (% İlerleme)')
+ ->icon('heroicon-m-squares-2x2')
+ ->schema([
+ Section::make('Sözleşme Kapsamındaki Modüller')
+ ->description('Hangi modüllerin tamamlandığını işaretleyin. İlerleme yüzdesi modül ağırlıklarına göre otomatik hesaplanır.')
+ ->schema([
+ Repeater::make('modules')
+ ->relationship('modules')
+ ->columns(12)
+ ->orderColumn('order')
+ ->defaultItems(0)
+ ->schema([
+ TextInput::make('title')
+ ->label('Modül Adı')
+ ->required()
+ ->columnSpan(4),
+
+ TextInput::make('weight_percent')
+ ->label('Ağırlık (%)')
+ ->numeric()
+ ->default(10)
+ ->suffix('%')
+ ->columnSpan(2),
+
+ Select::make('status')
+ ->label('Durum')
+ ->options([
+ 'pending' => 'Bekliyor',
+ 'in_progress' => 'Devam Ediyor',
+ 'completed' => 'Tamamlandı',
+ ])
+ ->default('pending')
+ ->required()
+ ->columnSpan(3),
+
+ DatePicker::make('start_date')
+ ->label('Başlangıç')
+ ->native(false)
+ ->columnSpan(1.5),
+
+ DatePicker::make('end_date')
+ ->label('Bitiş')
+ ->native(false)
+ ->columnSpan(1.5),
+ ])
+ ->columnSpanFull(),
+ ]),
+ ]),
+
+ Tab::make('Kanban Görev Kartları')
+ ->icon('heroicon-m-view-columns')
+ ->schema([
+ Section::make('İş Takvimi ve Görev Listesi')
+ ->description('Sözleşmedeki taskları ve detaylı görev kartlarını burada yönetin.')
+ ->schema([
+ Repeater::make('tasks')
+ ->relationship('tasks')
+ ->columns(12)
+ ->orderColumn('order_index')
+ ->defaultItems(0)
+ ->schema([
+ TextInput::make('title')
+ ->label('Görev Başlığı')
+ ->required()
+ ->columnSpan(4),
+
+ Select::make('status')
+ ->label('Kanban Durumu')
+ ->options([
+ 'todo' => 'Yapılacak',
+ 'in_progress' => 'Devam Ediyor',
+ 'review' => 'Kontrol Bekliyor',
+ 'done' => 'Tamamlandı',
+ ])
+ ->default('todo')
+ ->required()
+ ->columnSpan(3),
+
+ Select::make('priority')
+ ->label('Öncelik')
+ ->options([
+ 'low' => 'Düşük',
+ 'medium' => 'Orta',
+ 'high' => 'Yüksek',
+ 'urgent' => 'Acil',
+ ])
+ ->default('medium')
+ ->columnSpan(2),
+
+ TextInput::make('assigned_person')
+ ->label('Sorumlu')
+ ->columnSpan(3),
+ ])
+ ->columnSpanFull(),
+ ]),
+ ]),
+
+ Tab::make('Gidişat Güncellemeleri Logu')
+ ->icon('heroicon-m-chat-bubble-bottom-center-text')
+ ->schema([
+ Section::make('Canlı İlerleme Duyuruları')
+ ->description('Yazılım ekibi tarafından girilen ve müşterinin canlı izleyebildiği durum mesajları.')
+ ->schema([
+ Repeater::make('updates')
+ ->relationship('updates')
+ ->columns(12)
+ ->defaultItems(0)
+ ->schema([
+ TextInput::make('title')
+ ->label('Güncelleme Başlığı')
+ ->required()
+ ->columnSpan(6),
+
+ TextInput::make('progress_percent_at_update')
+ ->label('O Anki Yüzde (%)')
+ ->numeric()
+ ->columnSpan(3),
+
+ Checkbox::make('is_public')
+ ->label('Müşteriye Göster')
+ ->default(true)
+ ->columnSpan(3),
+
+ Textarea::make('content')
+ ->label('Güncelleme Notu / Sürüm Açıklaması')
+ ->required()
+ ->rows(2)
+ ->columnSpanFull(),
+ ])
+ ->columnSpanFull(),
+ ]),
+ ]),
+ ])->columnSpanFull()
+ ]);
+ }
+}
diff --git a/app/Filament/Admin/Resources/Projects/Tables/ProjectsTable.php b/app/Filament/Admin/Resources/Projects/Tables/ProjectsTable.php
new file mode 100644
index 0000000..a45399c
--- /dev/null
+++ b/app/Filament/Admin/Resources/Projects/Tables/ProjectsTable.php
@@ -0,0 +1,110 @@
+columns([
+ TextColumn::make('title')
+ ->label('Proje Adı')
+ ->searchable()
+ ->sortable()
+ ->limit(40),
+
+ TextColumn::make('client_name')
+ ->label('Müşteri')
+ ->searchable()
+ ->sortable()
+ ->limit(30),
+
+ TextColumn::make('progress_percent')
+ ->label('İlerleme')
+ ->formatStateUsing(fn ($state) => "%{$state}")
+ ->badge()
+ ->color(fn ($state) => match (true) {
+ $state >= 100 => 'success',
+ $state >= 50 => 'info',
+ $state >= 25 => 'warning',
+ default => 'danger',
+ })
+ ->sortable(),
+
+ TextColumn::make('status')
+ ->label('Durum')
+ ->badge()
+ ->color(fn (string $state): string => match ($state) {
+ 'planning' => 'gray',
+ 'in_progress' => 'info',
+ 'on_hold' => 'warning',
+ 'completed' => 'success',
+ 'cancelled' => 'danger',
+ default => 'gray',
+ })
+ ->formatStateUsing(fn (string $state): string => match ($state) {
+ 'planning' => 'Planlama',
+ 'in_progress' => 'Devam Ediyor',
+ 'on_hold' => 'Beklemede',
+ 'completed' => 'Tamamlandı',
+ 'cancelled' => 'İptal',
+ default => $state,
+ }),
+
+ TextColumn::make('client_access_code')
+ ->label('Müşteri PIN')
+ ->copyable()
+ ->badge()
+ ->color('gray'),
+
+ TextColumn::make('target_date')
+ ->label('Hedef Bitiş')
+ ->date('d.m.Y')
+ ->sortable(),
+
+ TextColumn::make('created_at')
+ ->label('Oluşturulma')
+ ->dateTime('d.m.Y H:i')
+ ->sortable()
+ ->toggleable(isToggledHiddenByDefault: true),
+ ])
+ ->filters([
+ SelectFilter::make('status')
+ ->label('Durum')
+ ->options([
+ 'planning' => 'Planlama',
+ 'in_progress' => 'Devam Ediyor',
+ 'on_hold' => 'Beklemede',
+ 'completed' => 'Tamamlandı',
+ 'cancelled' => 'İptal',
+ ]),
+ ])
+ ->recordActions([
+ EditAction::make()
+ ->label('Düzenle'),
+ ])
+ ->actions([
+ Action::make('view_client_portal')
+ ->label('Müşteri Ekranı')
+ ->icon('heroicon-o-arrow-top-right-on-square')
+ ->url(fn ($record) => route('projects.show', $record->slug))
+ ->openUrlInNewTab(),
+ ])
+ ->bulkActions([
+ BulkActionGroup::make([
+ DeleteBulkAction::make()
+ ->label('Sil'),
+ ]),
+ ])
+ ->defaultSort('created_at', 'desc');
+ }
+}
diff --git a/app/Http/Controllers/ProjectController.php b/app/Http/Controllers/ProjectController.php
new file mode 100644
index 0000000..629ce74
--- /dev/null
+++ b/app/Http/Controllers/ProjectController.php
@@ -0,0 +1,49 @@
+with(['modules', 'tasks', 'updates' => function($q) {
+ $q->where('is_public', true)->latest();
+ }, 'proposal'])
+ ->firstOrFail();
+
+ // Recalculate progress dynamically
+ $project->recalculateProgress();
+
+ // Check if access code protection is active and verified in session
+ $sessionKey = 'project_access_' . $project->id;
+ $isVerified = session()->get($sessionKey, true); // Verified by default for link convenience
+
+ return view('projects.show', compact('project', 'isVerified'));
+ }
+
+ /**
+ * Verify client access code (PIN).
+ */
+ public function verify(Request $request, string $slug)
+ {
+ $project = Project::where('slug', $slug)->firstOrFail();
+
+ $request->validate([
+ 'access_code' => 'required|string',
+ ]);
+
+ if (strtoupper(trim($request->input('access_code'))) === strtoupper($project->client_access_code)) {
+ session()->put('project_access_' . $project->id, true);
+ return back()->with('success', 'Erişim doğrulandı.');
+ }
+
+ return back()->withErrors(['access_code' => 'Geçersiz müşteri takip şifresi.']);
+ }
+}
diff --git a/app/Models/Project.php b/app/Models/Project.php
new file mode 100644
index 0000000..5e0ad09
--- /dev/null
+++ b/app/Models/Project.php
@@ -0,0 +1,92 @@
+ 'date',
+ 'target_date' => 'date',
+ 'completed_at' => 'datetime',
+ 'progress_percent' => 'integer',
+ ];
+
+ protected static function boot()
+ {
+ parent::boot();
+
+ static::creating(function ($project) {
+ if (empty($project->slug)) {
+ $project->slug = Str::slug($project->title) . '-' . Str::random(5);
+ }
+ if (empty($project->client_access_code)) {
+ $project->client_access_code = strtoupper(Str::random(6));
+ }
+ });
+ }
+
+ public function proposal()
+ {
+ return $this->belongsTo(Proposal::class);
+ }
+
+ public function modules()
+ {
+ return $this->hasMany(ProjectModule::class)->orderBy('order', 'asc');
+ }
+
+ public function tasks()
+ {
+ return $this->hasMany(ProjectTask::class)->orderBy('order_index', 'asc');
+ }
+
+ public function updates()
+ {
+ return $this->hasMany(ProjectUpdate::class)->latest();
+ }
+
+ /**
+ * Recalculate progress percentage based on completed modules weight
+ */
+ public function recalculateProgress()
+ {
+ $totalWeight = $this->modules()->sum('weight_percent');
+ if ($totalWeight > 0) {
+ $completedWeight = $this->modules()->where('status', 'completed')->sum('weight_percent');
+ $progress = (int) round(($completedWeight / $totalWeight) * 100);
+ } else {
+ $totalTasks = $this->tasks()->count();
+ if ($totalTasks > 0) {
+ $completedTasks = $this->tasks()->where('status', 'done')->count();
+ $progress = (int) round(($completedTasks / $totalTasks) * 100);
+ } else {
+ $progress = $this->progress_percent;
+ }
+ }
+
+ $this->update(['progress_percent' => min(100, max(0, $progress))]);
+ return $this->progress_percent;
+ }
+}
diff --git a/app/Models/ProjectModule.php b/app/Models/ProjectModule.php
new file mode 100644
index 0000000..0ff6e0d
--- /dev/null
+++ b/app/Models/ProjectModule.php
@@ -0,0 +1,39 @@
+ 'date',
+ 'end_date' => 'date',
+ 'weight_percent' => 'integer',
+ 'order' => 'integer',
+ ];
+
+ public function project()
+ {
+ return $this->belongsTo(Project::class);
+ }
+
+ public function tasks()
+ {
+ return $this->hasMany(ProjectTask::class, 'project_module_id');
+ }
+}
diff --git a/app/Models/ProjectTask.php b/app/Models/ProjectTask.php
new file mode 100644
index 0000000..92dbd4a
--- /dev/null
+++ b/app/Models/ProjectTask.php
@@ -0,0 +1,38 @@
+ 'date',
+ 'order_index' => 'integer',
+ ];
+
+ public function project()
+ {
+ return $this->belongsTo(Project::class);
+ }
+
+ public function module()
+ {
+ return $this->belongsTo(ProjectModule::class, 'project_module_id');
+ }
+}
diff --git a/app/Models/ProjectUpdate.php b/app/Models/ProjectUpdate.php
new file mode 100644
index 0000000..f79b336
--- /dev/null
+++ b/app/Models/ProjectUpdate.php
@@ -0,0 +1,35 @@
+ 'integer',
+ 'is_public' => 'boolean',
+ ];
+
+ public function project()
+ {
+ return $this->belongsTo(Project::class);
+ }
+
+ public function user()
+ {
+ return $this->belongsTo(User::class);
+ }
+}
diff --git a/database/migrations/2026_07_30_172000_create_project_management_tables.php b/database/migrations/2026_07_30_172000_create_project_management_tables.php
new file mode 100644
index 0000000..112e2d9
--- /dev/null
+++ b/database/migrations/2026_07_30_172000_create_project_management_tables.php
@@ -0,0 +1,81 @@
+id();
+ $table->foreignId('proposal_id')->nullable()->constrained('proposals')->nullOnDelete();
+ $table->string('title');
+ $table->string('slug')->unique();
+ $table->string('client_name');
+ $table->string('client_email')->nullable();
+ $table->string('client_access_code')->nullable();
+ $table->enum('status', ['planning', 'in_progress', 'on_hold', 'completed', 'cancelled'])->default('in_progress');
+ $table->unsignedInteger('progress_percent')->default(0);
+ $table->date('start_date')->nullable();
+ $table->date('target_date')->nullable();
+ $table->timestamp('completed_at')->nullable();
+ $table->text('notes')->nullable();
+ $table->timestamps();
+ $table->softDeletes();
+ });
+
+ Schema::create('project_modules', function (Blueprint $table) {
+ $table->id();
+ $table->foreignId('project_id')->constrained('projects')->cascadeOnDelete();
+ $table->string('title');
+ $table->text('description')->nullable();
+ $table->unsignedInteger('weight_percent')->default(10);
+ $table->enum('status', ['pending', 'in_progress', 'completed'])->default('pending');
+ $table->date('start_date')->nullable();
+ $table->date('end_date')->nullable();
+ $table->integer('order')->default(0);
+ $table->timestamps();
+ });
+
+ Schema::create('project_tasks', function (Blueprint $table) {
+ $table->id();
+ $table->foreignId('project_id')->constrained('projects')->cascadeOnDelete();
+ $table->foreignId('project_module_id')->nullable()->constrained('project_modules')->nullOnDelete();
+ $table->string('title');
+ $table->text('description')->nullable();
+ $table->enum('status', ['todo', 'in_progress', 'review', 'done'])->default('todo');
+ $table->enum('priority', ['low', 'medium', 'high', 'urgent'])->default('medium');
+ $table->date('due_date')->nullable();
+ $table->string('assigned_person')->nullable();
+ $table->integer('order_index')->default(0);
+ $table->timestamps();
+ });
+
+ Schema::create('project_updates', function (Blueprint $table) {
+ $table->id();
+ $table->foreignId('project_id')->constrained('projects')->cascadeOnDelete();
+ $table->foreignId('user_id')->nullable()->constrained('users')->nullOnDelete();
+ $table->string('title');
+ $table->text('content');
+ $table->unsignedInteger('progress_percent_at_update')->nullable();
+ $table->boolean('is_public')->default(true);
+ $table->timestamps();
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ */
+ public function down(): void
+ {
+ Schema::dropIfExists('project_updates');
+ Schema::dropIfExists('project_tasks');
+ Schema::dropIfExists('project_modules');
+ Schema::dropIfExists('projects');
+ }
+};
diff --git a/public/robots.txt b/public/robots.txt
index 3479ec3..711ad2e 100644
--- a/public/robots.txt
+++ b/public/robots.txt
@@ -2,5 +2,7 @@ User-agent: *
Allow: /
Disallow: /admin/
Disallow: /stajyer/admin/
+Disallow: /teklif/
+Disallow: /proje-takip/
Sitemap: https://truncgil.com/sitemap.xml
diff --git a/resources/views/projects/show.blade.php b/resources/views/projects/show.blade.php
new file mode 100644
index 0000000..7d42e23
--- /dev/null
+++ b/resources/views/projects/show.blade.php
@@ -0,0 +1,666 @@
+
+
+
+
+
+
+
+ {{ $project->title }} - Canlı Proje Takip Portalı | Trunçgil Teknoloji
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ MÜŞTERİ CANLI TAKİP PANELİ
+
+
+
+
+
+ Bu ekran, {{ $project->client_name }} projesine ait iş takvimini, tamamlanan hizmet modüllerini ve canlı Kanban görev akışını şeffaf biçimde sunmaktadır.
+
+
+
+
+ @if($project->start_date)
+
+
+ Başlangıç: {{ $project->start_date->format('d.m.Y') }}
+
+ @endif
+
+ @if($project->target_date)
+
+
+ Hedef Bitiş: {{ $project->target_date->format('d.m.Y') }}
+
+ @endif
+
+
+
+ Müşteri PIN: {{ $project->client_access_code }}
+
+
+
+
+
+
+
GENEL PROJE İLERLEMESİ
+
+
+
+
+
+
+
+ @if($project->progress_percent >= 100)
+ 🎉 Proje %100 Başarıyla Tamamlandı!
+ @elseif($project->progress_percent >= 50)
+ ⚡ Proje Geliştirmeleri Hızla Devam Ediyor
+ @else
+ 🚀 Faz 1 Çalışmaları Başlatıldı
+ @endif
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
1. İş Takvimi ve Gantt Çizelgesi
+
Proje aşamalarının ve geliştirme süreçlerinin zamansal planı
+
+
+
+
+
+
+
+
+
+
+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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
2. Hizmet Modülleri İlerleme Matrisi
+
Sözleşmedeki hizmet kalemlerinin tamamlanma ve gidişat durumları
+
+
+
+ Toplam {{ $project->modules->count() }} Modül
+
+
+
+
+
+ @forelse($project->modules as $module)
+
+
+
+ @if($module->status === 'completed')
+
+
+
+ @elseif($module->status === 'in_progress')
+
+
+
+ @else
+
+
+
+ @endif
+
+ {{ $module->title }}
+
+
+
+
+
+
+ @if($module->description)
+
+ {{ $module->description }}
+
+ @endif
+
+
+ Ağırlık: %{{ $module->weight_percent }}
+ @if($module->start_date && $module->end_date)
+ {{ $module->start_date->format('d.m') }} - {{ $module->end_date->format('d.m.Y') }}
+ @endif
+
+
+ @empty
+
+ Henüz tanımlanmış modül bulunmamaktadır.
+
+ @endforelse
+
+
+
+
+
+
+
+
+
+
+
+
3. Canlı Kanban Görev Panosu
+
Mühendislik ekibinin anlık görev statüleri ve çalışma kartları
+
+
+
+
+
+
+
+
+
+
+
+ Yapılacaklar
+
+
+
+
+ @forelse($project->tasks->where('status', 'todo') as $task)
+
+
{{ $task->title }}
+ @if($task->description)
+
{{ $task->description }}
+ @endif
+
+ {{ $task->assigned_person ?? 'Atanmadı' }}
+ {{ $task->priority }}
+
+
+ @empty
+
Görev yok
+ @endforelse
+
+
+
+
+
+
+ Devam Edenler
+
+
+
+
+ @forelse($project->tasks->where('status', 'in_progress') as $task)
+
+
{{ $task->title }}
+ @if($task->description)
+
{{ $task->description }}
+ @endif
+
+ {{ $task->assigned_person ?? 'Yazılım Ekibi' }}
+ {{ $task->priority }}
+
+
+ @empty
+
Görev yok
+ @endforelse
+
+
+
+
+
+
+ Kontrol / Test
+
+
+
+
+ @forelse($project->tasks->where('status', 'review') as $task)
+
+
{{ $task->title }}
+ @if($task->description)
+
{{ $task->description }}
+ @endif
+
+ {{ $task->assigned_person ?? 'Test Ekibi' }}
+ {{ $task->priority }}
+
+
+ @empty
+
Görev yok
+ @endforelse
+
+
+
+
+
+
+ Tamamlananlar
+
+
+
+
+ @forelse($project->tasks->where('status', 'done') as $task)
+
+
{{ $task->title }}
+ @if($task->description)
+
{{ $task->description }}
+ @endif
+
+ Tamamlandı
+
+
+
+ @empty
+
Görev yok
+ @endforelse
+
+
+
+
+
+
+
+
+
+
+
+
+
+
4. Canlı İlerleme Duyuruları & Log Akışı
+
Yazılım yöneticisinden gelen canlı proje güncellemeleri
+
+
+
+
+
+
+ @forelse($project->updates as $update)
+
+
+
+
+
+
+
+ {{ $update->title }}
+
+
+ {{ $update->created_at->format('d.m.Y H:i') }}
+
+
+
+
+ {{ $update->content }}
+
+
+ @if($update->progress_percent_at_update !== null)
+
+
+ Güncelleme Anındaki İlerleme: %{{ $update->progress_percent_at_update }}
+
+ @endif
+
+
+ @empty
+
+ Henüz yayınlanmış bir duyuru bulunmamaktadır.
+
+ @endforelse
+
+
+
+
+
+
+
+
+
© {{ date('Y') }} Trunçgil Teknoloji Sanayi ve Ticaret Ltd. Şti. — Gaziantep Teknopark
+
Tüm geliştirmeler 4691 Sayılı Teknoloji Geliştirme Bölgeleri Kanunu Ar-Ge standartlarında yürütülmektedir.
+
+
+
+
+
+
+
+
+
+
+
diff --git a/resources/views/proposals/show.blade.php b/resources/views/proposals/show.blade.php
index f5e1189..ea710d6 100644
--- a/resources/views/proposals/show.blade.php
+++ b/resources/views/proposals/show.blade.php
@@ -4,6 +4,7 @@
+
{{ $proposal->title }} - Trunçgil Teknoloji
diff --git a/routes/web.php b/routes/web.php
index 9fc5353..406cef8 100644
--- a/routes/web.php
+++ b/routes/web.php
@@ -174,6 +174,17 @@ Route::get('/sitemap.xml', [\App\Http\Controllers\SitemapController::class, 'ind
Route::get('/teklif/{slug}', [\App\Http\Controllers\ProposalController::class, 'show'])->name('proposals.show');
Route::post('/teklif/{slug}/action', [\App\Http\Controllers\ProposalController::class, 'action'])->name('proposals.action');
+// Project Tracking Client Portal & Web Admin Management
+Route::get('/proje-takip/{slug}', [\App\Http\Controllers\ProjectController::class, 'show'])->name('projects.show');
+Route::post('/proje-takip/{slug}/verify', [\App\Http\Controllers\ProjectController::class, 'verify'])->name('projects.verify');
+Route::post('/proje-takip/{slug}/admin/module-status', [\App\Http\Controllers\ProjectController::class, 'updateModuleStatus'])->name('projects.admin.module-status');
+Route::post('/proje-takip/{slug}/admin/task-status', [\App\Http\Controllers\ProjectController::class, 'updateTaskStatus'])->name('projects.admin.task-status');
+Route::post('/proje-takip/{slug}/admin/add-task', [\App\Http\Controllers\ProjectController::class, 'addTask'])->name('projects.admin.add-task');
+Route::post('/proje-takip/{slug}/admin/delete-task', [\App\Http\Controllers\ProjectController::class, 'deleteTask'])->name('projects.admin.delete-task');
+Route::post('/proje-takip/{slug}/admin/add-update', [\App\Http\Controllers\ProjectController::class, 'addUpdate'])->name('projects.admin.add-update');
+Route::post('/proje-takip/{slug}/admin/recalculate', [\App\Http\Controllers\ProjectController::class, 'recalculate'])->name('projects.admin.recalculate');
+Route::post('/proje-takip/{slug}/admin/toggle-mode', [\App\Http\Controllers\ProjectController::class, 'toggleAdminMode'])->name('projects.admin.toggle-mode');
+
// Privacy Policy Alternatives
Route::get('/privacy-policy', function () {
return app(PageController::class)->show('privacy');